[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 3/12] Provide basic Xen PM infrastructure



Add basic infrastructure for xen power management. Now
only S3 (suspend to ram) is supported.

Signed-off-by Ke Yu <ke.yu@xxxxxxxxx>
Signed-off-by Kevin Tian <kevin.tian@xxxxxxxxx>

diff -r 13e258a58044 xen/arch/x86/acpi/Makefile
--- a/xen/arch/x86/acpi/Makefile        Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/acpi/Makefile        Wed Feb 14 11:13:40 2007 +0800
@@ -1,1 +1,2 @@ obj-y += boot.o
 obj-y += boot.o
+obj-y += power.o
diff -r 13e258a58044 xen/arch/x86/boot/x86_32.S
--- a/xen/arch/x86/boot/x86_32.S        Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/boot/x86_32.S        Wed Feb 14 11:13:40 2007 +0800
@@ -146,6 +146,8 @@ start_paging:
         rdmsr
         bts     $_EFER_NX,%eax
         wrmsr
+        mov     $1,%eax
+        mov     %eax, nx_enabled-__PAGE_OFFSET
 no_execute_disable:
         pop     %ebx
 #endif
diff -r 13e258a58044 xen/arch/x86/smp.c
--- a/xen/arch/x86/smp.c        Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/smp.c        Wed Feb 14 14:59:49 2007 +0800
@@ -276,8 +276,9 @@ int on_selected_cpus(
 {
     struct call_data_struct data;
     unsigned int nr_cpus = cpus_weight(selected);
-
-    ASSERT(local_irq_is_enabled());
+    unsigned int self = cpu_isset(smp_processor_id(), selected);
+
+    ASSERT(!self || local_irq_is_enabled());
 
     if ( nr_cpus == 0 )
         return 0;
diff -r 13e258a58044 xen/arch/x86/x86_32/Makefile
--- a/xen/arch/x86/x86_32/Makefile      Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/x86_32/Makefile      Wed Feb 14 11:13:40 2007 +0800
@@ -6,3 +6,5 @@ obj-y += traps.o
 obj-y += traps.o
 
 obj-$(supervisor_mode_kernel) += supervisor_mode_kernel.o
+subdir-y += acpi
+subdir-y += power
diff -r 13e258a58044 xen/arch/x86/x86_32/acpi/sleep.c
--- a/xen/arch/x86/x86_32/acpi/sleep.c  Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/x86_32/acpi/sleep.c  Wed Feb 14 11:13:40 2007 +0800
@@ -5,16 +5,29 @@
  *  Copyright (C) 2001-2003 Pavel Machek <pavel@xxxxxxx>
  */
 
+#ifndef __XEN__
 #include <linux/acpi.h>
 #include <linux/bootmem.h>
 #include <linux/dmi.h>
 #include <linux/cpumask.h>
 
 #include <asm/smp.h>
+#else
+#include <asm/config.h>
+#include <xen/string.h>
+#include <xen/domain_page.h>
+#include <asm/init.h>
+#include <asm/page.h>
+#include <asm/flushtlb.h>
+#include <xen/init.h>
+#endif
 
 /* address in low memory of the wakeup routine. */
 unsigned long acpi_wakeup_address = 0;
 unsigned long acpi_video_flags;
+#ifdef __XEN__
+unsigned long saved_videomode = 0;
+#endif
 extern char wakeup_start, wakeup_end;
 
 extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long));
@@ -29,6 +42,9 @@ int acpi_save_state_mem(void)
 {
        if (!acpi_wakeup_address)
                return 1;
+#ifdef __XEN__
+       init_low_mappings();
+#endif
        memcpy((void *)acpi_wakeup_address, &wakeup_start,
               &wakeup_end - &wakeup_start);
        acpi_copy_wakeup_routine(acpi_wakeup_address);
@@ -59,11 +75,20 @@ void __init acpi_reserve_bootmem(void)
                return;
        }
 
+#ifndef __XEN__
        acpi_wakeup_address = (unsigned
long)alloc_bootmem_low(PAGE_SIZE);
+#else
+       /*  0~640K is not used by anyone, except 0x9000 is used by smp
+        *  trampoline code, so choose 0x7000 for XEN acpi wake up code
+        */
+
+       acpi_wakeup_address = (unsigned long)__va(0x7000);
+#endif
        if (!acpi_wakeup_address)
                printk(KERN_ERR "ACPI: Cannot allocate lowmem, S3
disabled.\n");
 }
 
+#ifndef __XEN__
 static int __init acpi_sleep_setup(char *str)
 {
        while ((str != NULL) && (*str != '\0')) {
@@ -104,3 +129,4 @@ static int __init acpisleep_dmi_init(voi
 }
 
 core_initcall(acpisleep_dmi_init);
+#endif
diff -r 13e258a58044 xen/arch/x86/x86_32/acpi/wakeup.S
--- a/xen/arch/x86/x86_32/acpi/wakeup.S Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/x86_32/acpi/wakeup.S Wed Feb 14 11:13:40 2007 +0800
@@ -1,6 +1,11 @@
 .text
+#ifndef __XEN__
 #include <linux/linkage.h>
 #include <asm/segment.h>
+#else
+#include <xen/config.h>
+#include <asm/asm_defns.h>
+#endif
 #include <asm/page.h>
 
 #
@@ -56,7 +61,11 @@ 1:
 1:
 
        # set up page table
+#ifndef __XEN__
        movl    $swsusp_pg_dir-__PAGE_OFFSET, %eax
+#else
+       movl    $idle_pg_table-__PAGE_OFFSET, %eax
+#endif
        movl    %eax, %cr3
 
        testl   $1, real_efer_save_restore - wakeup_code
@@ -88,7 +97,11 @@ 1:
        cmpl    $0x12345678, %eax
        jne     bogus_real_magic
 
+#ifndef __XEN__
        ljmpl   $__KERNEL_CS,$wakeup_pmode_return
+#else
+       ljmpl   $(__HYPERVISOR_CS),$wakeup_pmode_return
+#endif
 
 real_save_gdt: .word 0
                .long 0
@@ -184,7 +197,11 @@ ENTRY(wakeup_end)
 .org   0x1000
 
 wakeup_pmode_return:
+#ifndef __XEN__
        movw    $__KERNEL_DS, %ax
+#else
+       movw    $__HYPERVISOR_DS, %ax
+#endif
        movw    %ax, %ss
        movw    %ax, %ds
        movw    %ax, %es
@@ -196,7 +213,11 @@ wakeup_pmode_return:
        lgdt    saved_gdt
        lidt    saved_idt
        lldt    saved_ldt
+#ifndef __XEN__
        ljmp    $(__KERNEL_CS),$1f
+#else
+       ljmp    $(__HYPERVISOR_CS),$1f
+#endif
 1:
        movl    %cr3, %eax
        movl    %eax, %cr3
diff -r 13e258a58044 xen/arch/x86/x86_32/mm.c
--- a/xen/arch/x86/x86_32/mm.c  Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/x86_32/mm.c  Wed Feb 14 11:13:40 2007 +0800
@@ -34,6 +34,7 @@ unsigned int PAGE_HYPERVISOR_NOCACHE = _
 unsigned int PAGE_HYPERVISOR_NOCACHE = __PAGE_HYPERVISOR_NOCACHE;
 
 static unsigned long mpt_size;
+int nx_enabled = 0;
 
 struct page_info *alloc_xen_pagetable(void)
 {
@@ -132,7 +133,7 @@ void __init setup_idle_pagetable(void)
                                 __PAGE_HYPERVISOR));
 }
 
-void __init zap_low_mappings(l2_pgentry_t *base)
+void zap_low_mappings(l2_pgentry_t *base)
 {
     int i;
     u32 addr;
@@ -146,6 +147,15 @@ void __init zap_low_mappings(l2_pgentry_
             continue;
         l2e_write(&base[i], l2e_empty());
     }
+
+    flush_tlb_all_pge();
+}
+
+void init_low_mappings(void)
+{
+    memcpy(idle_pg_table_l2,
+           idle_pg_table_l2 + (DIRECTMAP_VIRT_START >>
L2_PAGETABLE_SHIFT),
+           (DIRECTMAP_MBYTES << 20) >> L2_PAGETABLE_SHIFT);
 
     flush_tlb_all_pge();
 }
diff -r 13e258a58044 xen/arch/x86/x86_32/power/cpu.c
--- a/xen/arch/x86/x86_32/power/cpu.c   Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/x86_32/power/cpu.c   Wed Feb 14 14:59:56 2007 +0800
@@ -7,10 +7,91 @@
  * Copyright (c) 2001 Patrick Mochel <mochel@xxxxxxxx>
  */
 
+#ifndef __XEN__
 #include <linux/module.h>
 #include <linux/suspend.h>
 #include <asm/mtrr.h>
 #include <asm/mce.h>
+#else
+#include <xen/config.h>
+#include <xen/acpi.h>
+#include <xen/smp.h>
+#include <asm/processor.h>
+#include <asm/msr.h>
+#include <asm/flushtlb.h>
+
+/* image of the saved processor state */
+struct saved_context {
+       u16 es, fs, gs, ss;
+       unsigned long cr0, cr2, cr3, cr4;
+       u16 gdt_pad;
+       u16 gdt_limit;
+       unsigned long gdt_base;
+       u16 idt_pad;
+       u16 idt_limit;
+       unsigned long idt_base;
+       u16 ldt;
+       u16 tss;
+       unsigned long tr;
+       unsigned long safety;
+       unsigned long return_address;
+} __attribute__((packed));
+
+#define load_TR_desc() __asm__ __volatile__("ltr %w0"::"q"
(GDT_ENTRY_TSS*8))
+#define load_LDT_desc() __asm__ __volatile__("lldt %w0"::"q"
(GDT_ENTRY_LDT*8))
+
+#define load_gdt(dtr) __asm__ __volatile("lgdt %0"::"m" (*dtr))
+#define load_idt(dtr) __asm__ __volatile("lidt %0"::"m" (*dtr))
+#define load_tr(tr) __asm__ __volatile("ltr %0"::"mr" (tr))
+#define load_ldt(ldt) __asm__ __volatile("lldt %0"::"mr" (ldt))
+
+#define store_gdt(dtr) __asm__ ("sgdt %0":"=m" (*dtr))
+#define store_idt(dtr) __asm__ ("sidt %0":"=m" (*dtr))
+#define store_tr(tr) __asm__ ("str %0":"=mr" (tr))
+#define store_ldt(ldt) __asm__ ("sldt %0":"=mr" (ldt))
+
+/*
+ * Load a segment. Fall back on loading the zero
+ * segment if something goes wrong..
+ */
+#define loadsegment(seg,value)          \
+    asm volatile("\n"           \
+        "1:\t"              \
+        "mov %0,%%" #seg "\n"       \
+        "2:\n"              \
+        ".section .fixup,\"ax\"\n"  \
+        "3:\t"              \
+        "pushl $0\n\t"          \
+        "popl %%" #seg "\n\t"       \
+        "jmp 2b\n"          \
+        ".previous\n"           \
+        ".section __ex_table,\"a\"\n\t" \
+        ".align 4\n\t"          \
+        ".long 1b,3b\n"         \
+        ".previous"         \
+        : :"rm" (value))
+
+/*
+ * Save a segment register away
+ */
+#define savesegment(seg, value) \
+       asm volatile("mov %%" #seg ",%0":"=rm" (value))
+
+#define set_debugreg(value, register)           \
+        __asm__("movl %0,%%db" #register        \
+            : /* no output */           \
+            :"r" (value))
+
+void kernel_fpu_begin(void)
+{
+       clts();
+}
+
+void kernel_fpu_end(void)
+{
+       stts();
+}
+#endif
 
 static struct saved_context saved_context;
 
@@ -34,8 +115,10 @@ void __save_processor_state(struct saved
         * segment registers
         */
        savesegment(es, ctxt->es);
+#ifndef __XEN__
        savesegment(fs, ctxt->fs);
        savesegment(gs, ctxt->gs);
+#endif
        savesegment(ss, ctxt->ss);
 
        /*
@@ -60,6 +143,7 @@ static void do_fpu_end(void)
        kernel_fpu_end();
 }
 
+#ifndef __XEN__
 static void fix_processor_context(void)
 {
        int cpu = smp_processor_id();
@@ -84,6 +168,32 @@ static void fix_processor_context(void)
        }
 
 }
+#else
+static void fix_processor_context(void)
+{
+       int cpu = smp_processor_id();
+       struct tss_struct * t = &init_tss[cpu];;
+
+       if ( supervisor_mode_kernel && cpu_has_sep )
+               wrmsr(MSR_IA32_SYSENTER_ESP, &t->esp1, 0);
+
+       set_tss_desc(cpu,t);    /* This just modifies memory; should not
be necessary. But... This is necessary, because 386 hardware has concept
of busy TSS or some similar stupidity. */
+
+       load_TR(cpu);          /* This does ltr */
+       __asm__ __volatile__ ( "lldt %%ax" : : "a" (0) );/* This does
lldt */
+
+       /*
+        * Now maybe reset the debug registers
+        */
+       set_debugreg(0UL, 0);
+       set_debugreg(0UL, 1);
+       set_debugreg(0UL, 2);
+       set_debugreg(0UL, 3);
+       /* no 4 and 5 */
+       set_debugreg(0UL, 6);
+       set_debugreg(0UL, 7);
+}
+#endif
 
 void __restore_processor_state(struct saved_context *ctxt)
 {
@@ -106,15 +216,19 @@ void __restore_processor_state(struct sa
         * segment registers
         */
        loadsegment(es, ctxt->es);
+#ifndef __XEN__
        loadsegment(fs, ctxt->fs);
        loadsegment(gs, ctxt->gs);
+#endif
        loadsegment(ss, ctxt->ss);
 
+#ifndef __XEN__
        /*
         * sysenter MSRs
         */
        if (boot_cpu_has(X86_FEATURE_SEP))
                enable_sep_cpu();
+#endif
 
        fix_processor_context();
        do_fpu_end();
@@ -127,6 +241,8 @@ void restore_processor_state(void)
        __restore_processor_state(&saved_context);
 }
 
+#ifndef __XEN__
 /* Needed by apm.c */
 EXPORT_SYMBOL(save_processor_state);
 EXPORT_SYMBOL(restore_processor_state);
+#endif
diff -r 13e258a58044 xen/arch/x86/x86_64/mm.c
--- a/xen/arch/x86/x86_64/mm.c  Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/arch/x86/x86_64/mm.c  Wed Feb 14 11:13:40 2007 +0800
@@ -185,9 +185,16 @@ void __init setup_idle_pagetable(void)
                   __PAGE_HYPERVISOR));
 }
 
-void __init zap_low_mappings(void)
+void zap_low_mappings(void)
 {
     l4e_write(&idle_pg_table[0], l4e_empty());
+    flush_tlb_all_pge();
+}
+
+void init_low_mappings(void)
+{
+    l4e_write(&idle_pg_table[0],
+               l4e_from_paddr(__pa(idle_pg_table_l3),
__PAGE_HYPERVISOR));
     flush_tlb_all_pge();
 }
 
diff -r 13e258a58044 xen/include/asm-x86/config.h
--- a/xen/include/asm-x86/config.h      Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/include/asm-x86/config.h      Wed Feb 14 14:59:48 2007 +0800
@@ -251,6 +251,7 @@
 #define CONFIG_DOMAIN_PAGE 1
 
 #define asmlinkage __attribute__((regparm(0)))
+#define FASTCALL(x) fastcall(x)
 
 /*
  * Memory layout (high to low):                          SIZE
PAE-SIZE
diff -r 13e258a58044 xen/include/asm-x86/processor.h
--- a/xen/include/asm-x86/processor.h   Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/include/asm-x86/processor.h   Wed Feb 14 11:13:40 2007 +0800
@@ -295,6 +295,11 @@ static inline unsigned long read_cr2(voi
     unsigned long __cr2;
     __asm__("mov %%cr2,%0\n\t" :"=r" (__cr2));
     return __cr2;
+}
+
+static inline void write_cr2(unsigned long val)
+{
+       __asm__("mov %0,%%cr2": :"r" ((unsigned long)val));
 }
 
 static inline unsigned long read_cr4(void)
diff -r 13e258a58044 xen/include/asm-x86/smp.h
--- a/xen/include/asm-x86/smp.h Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/include/asm-x86/smp.h Wed Feb 14 14:59:48 2007 +0800
@@ -45,6 +45,7 @@ extern void zap_low_mappings(l2_pgentry_
 extern void zap_low_mappings(l2_pgentry_t *base);
 #endif
 
+extern void init_low_mappings(void);
 #define MAX_APICID 256
 extern u8 x86_cpu_to_apicid[];
 
diff -r 13e258a58044 xen/include/asm-x86/page.h
--- a/xen/include/asm-x86/page.h        Wed Feb 14 11:13:40 2007 +0800
+++ b/xen/include/asm-x86/page.h        Wed Feb 14 15:00:49 2007 +0800
@@ -288,6 +288,9 @@ extern l2_pgentry_t   idle_pg_table_l2[R
 #else
 extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES];
 extern l2_pgentry_t   idle_pg_table_l2[ROOT_PAGETABLE_ENTRIES];
+#if CONFIG_PAGING_LEVELS == 4
+extern l3_pgentry_t   idle_pg_table_l3[L3_PAGETABLE_ENTRIES];
+#endif
 #ifdef CONFIG_COMPAT
 extern l2_pgentry_t  *compat_idle_pg_table_l2;
 extern unsigned int   m2p_compat_vstart;
diff -r 13e258a58044 xen/arch/x86/acpi/power.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/acpi/power.c Wed Feb 14 14:59:58 2007 +0800
@@ -0,0 +1,149 @@
+/* drivers/acpi/sleep/power.c - PM core functionality for Xen
+ *
+ * Copyrights from Linux side:
+ * Copyright (c) 2000-2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ * Copyright (c) 2004 David Shaohua Li <shaohua.li@xxxxxxxxx>
+ * Copyright (c) 2005 Alexey Starikovskiy
<alexey.y.starikovskiy@xxxxxxxxx>
+ *
+ * Slimmed with Xen specific support.
+ */
+
+#include <asm/io.h>
+#define CONFIG_ACPI_SLEEP
+#include <asm/acpi.h>
+#include <xen/acpi.h>
+#include <xen/errno.h>
+#include <xen/iocap.h>
+#include <xen/sched.h>
+#include <asm/acpi.h>
+#include <asm/irq.h>
+#include <asm/init.h>
+#include <xen/spinlock.h>
+#include <xen/sched.h>
+#include <xen/domain.h>
+#include <xen/console.h>
+
+u8 sleep_states[ACPI_S_STATE_COUNT];
+DEFINE_SPINLOCK(pm_lock);
+
+extern void do_suspend_lowlevel(void);
+
+static char *acpi_states[ACPI_S_STATE_COUNT] =
+{
+    [ACPI_STATE_S1] = "standby",
+    [ACPI_STATE_S3] = "mem",
+    [ACPI_STATE_S4] = "disk",
+};
+
+/* Add suspend failure recover later */
+static int device_power_down(void)
+{
+    console_suspend();
+
+    time_suspend();
+
+    i8259A_suspend();
+    
+    ioapic_suspend();
+    
+    lapic_suspend();
+
+    return 0;
+}
+
+static void device_power_up(void)
+{
+    lapic_resume();
+    
+    ioapic_resume();
+
+    i8259A_resume();
+    
+    time_resume();
+
+    console_resume();
+}
+
+int enter_state(u32 state)
+{
+    struct domain *d;
+    unsigned long flags;
+    int error;
+
+    if (state <= ACPI_STATE_S0 || state > ACPI_S_STATES_MAX)
+        return -EINVAL;
+
+    if (!spin_trylock(&pm_lock))
+        return -EBUSY;
+    
+    for_each_domain(d)
+       if (d->domain_id != 0)
+           domain_pause(d);
+
+    printk("PM: Preparing system for %s sleep\n", acpi_states[state]);
+
+    local_irq_save(flags);
+
+    if ((error = device_power_down())) {
+        printk(KERN_ERR "Some devices failed to power down\n");
+        goto Done;
+    }
+
+    ACPI_FLUSH_CPU_CACHE();
+
+    /* Do arch specific saving of state. */
+    if (state > ACPI_STATE_S1) {
+        error = acpi_save_state_mem();
+        if (error)
+            goto Powerup;
+    }
+
+    switch (state) {
+        case ACPI_STATE_S3:
+            do_suspend_lowlevel();
+            break;
+        default:
+            error = -EINVAL;
+            goto Powerup;
+    }
+
+    printk("Back to C!\n");
+    if (state > ACPI_STATE_S1)
+        acpi_restore_state_mem();
+
+ Powerup:
+    device_power_up();
+
+    printk("PM: Finishing wakeup.\n");
+    for_each_domain(d)
+       if (d->domain_id!=0)
+           domain_unpause(d);
+
+ Done:
+    local_irq_restore(flags);
+    spin_unlock(&pm_lock);
+    return error;
+
+}
+
+static int __init acpi_sleep_init(void)
+{
+    int i = 0; 
+
+    printk("ACPI (supports");
+    for (i = 0; i < ACPI_S_STATE_COUNT; i++) {
+        if (i == ACPI_STATE_S3){
+            sleep_states[i] = 1;
+            printk(" S%d", i);
+        }
+        else{
+            sleep_states[i] = 0;
+        }
+    }
+    printk(")\n");
+
+    acpi_reserve_bootmem();
+    return 0;
+}
+__initcall(acpi_sleep_init);
diff -r 13e258a58044 xen/arch/x86/x86_32/acpi/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/x86_32/acpi/Makefile Wed Feb 14 11:13:40 2007 +0800
@@ -0,0 +1,2 @@
+obj-y += wakeup.o
+obj-y += sleep.o
diff -r 13e258a58044 xen/arch/x86/x86_32/power/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/x86_32/power/Makefile        Wed Feb 14 11:13:40 2007
+0800
@@ -0,0 +1,1 @@
+obj-y += cpu.o

Attachment: xen_pm_arch.patch
Description: xen_pm_arch.patch

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.