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
xen_pm_arch.patch
Description: xen_pm_arch.patch
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|