Define pm interfaces between dom0 and Xen, with one to register sleep info and the other for triggering sleep state. "acpi_sleep=s3_mode/s3_bios" option is also supported by piggybacking video flag/mode to xen at trigger point. Signed-off-by Ke Yu Signed-off-by Kevin Tian diff -r fc995dc97a94 xen/arch/x86/acpi/power.c --- a/xen/arch/x86/acpi/power.c Mon May 14 18:27:46 2007 -0400 +++ b/xen/arch/x86/acpi/power.c Mon May 14 18:29:06 2007 -0400 @@ -28,6 +28,16 @@ u8 sleep_states[ACPI_S_STATE_COUNT]; u8 sleep_states[ACPI_S_STATE_COUNT]; DEFINE_SPINLOCK(pm_lock); +struct acpi_sleep_info { + uint16_t pm1a_cnt; + uint16_t pm1b_cnt; + uint16_t pm1a_evt; + uint16_t pm1b_evt; + uint16_t pm1a_cnt_val; + uint16_t pm1b_cnt_val; + uint32_t sleep_state; +} acpi_sinfo; + extern void do_suspend_lowlevel(void); static char *acpi_states[ACPI_S_STATE_COUNT] = @@ -95,7 +105,7 @@ void __init acpi_reserve_bootmem(void) acpi_wakeup_address = (unsigned long)__va(0x7000); } -/* Add suspend failure recover later */ +/* XXX: Add suspend failure recover later */ static int device_power_down(void) { console_suspend(); @@ -124,6 +134,7 @@ static void device_power_up(void) console_resume(); } +/* Main interface to do xen specific suspend/resume */ int enter_state(u32 state) { struct domain *d; @@ -152,7 +163,6 @@ int enter_state(u32 state) ACPI_FLUSH_CPU_CACHE(); - /* Do arch specific saving of state. */ if (state > ACPI_STATE_S1) { error = acpi_save_state_mem(); if (error) @@ -165,7 +175,7 @@ int enter_state(u32 state) break; default: error = -EINVAL; - goto Powerup; + break; } pmprintk(XENLOG_INFO, "Back to C!\n"); @@ -185,6 +195,97 @@ int enter_state(u32 state) spin_unlock(&pm_lock); return error; +} + +/* + * Xen just requires address of pm1x_cnt, and ACPI interpreter + * is still kept in dom0. Address of xen wakeup stub will be + * returned, and then dom0 writes that address to FACS. + */ +int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info) +{ + if (acpi_sinfo.pm1a_cnt) + pmprintk(XENLOG_WARNING, "Multiple setting on acpi sleep info\n"); + + acpi_sinfo.pm1a_cnt = info->pm1a_cnt_port; + acpi_sinfo.pm1b_cnt = info->pm1b_cnt_port; + acpi_sinfo.pm1a_evt = info->pm1a_evt_port; + acpi_sinfo.pm1b_evt = info->pm1b_evt_port; + info->xen_waking_vec = (uint64_t)__pa(acpi_wakeup_address); + + pmprintk(XENLOG_INFO, "pm1a[%x],pm1b[%x],pm1a_e[%x],pm1b_e[%x]" + "wake[%"PRIx64"]", + acpi_sinfo.pm1a_cnt, acpi_sinfo.pm1b_cnt, + acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_evt, + info->xen_waking_vec); + return 0; +} + +/* + * Dom0 issues this hypercall in place of writing pm1a_cnt. Xen then + * takes over the control and put the system into sleep state really. + * Also video flags and mode are passed here, in case user may use + * "acpi_sleep=***" for video resume. + * + * Guest may issue a two-phases write to PM1x_CNT, to work + * around poorly implemented hardware. It's better to keep + * this logic here. Two writes can be differentiated by + * enable bit setting. + */ +int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep) +{ + if (!acpi_sinfo.pm1a_cnt) + return -EPERM; + + /* Sanity check */ + if (acpi_sinfo.pm1b_cnt_val && + ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) & + ACPI_BITMASK_SLEEP_ENABLE)) { + pmprintk(XENLOG_ERR, "Mismatched pm1a/pm1b setting\n"); + return -EINVAL; + } + + /* Write #1 */ + if (!(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE)) { + outw((u16)sleep->pm1a_cnt_val, acpi_sinfo.pm1a_cnt); + if (acpi_sinfo.pm1b_cnt) + outw((u16)sleep->pm1b_cnt_val, acpi_sinfo.pm1b_cnt); + return 0; + } + + /* Write #2 */ + acpi_sinfo.pm1a_cnt_val = sleep->pm1a_cnt_val; + acpi_sinfo.pm1b_cnt_val = sleep->pm1b_cnt_val; + acpi_sinfo.sleep_state = sleep->sleep_state; + acpi_video_flags = sleep->video_flags; + saved_videomode = sleep->video_mode; + + return enter_state(acpi_sinfo.sleep_state); +} + +static int acpi_get_wake_status(void) +{ + uint16_t val; + + /* Wake status is the 15th bit of PM1 status register. (ACPI spec 3.0) */ + val = inw(acpi_sinfo.pm1a_evt) | inw(acpi_sinfo.pm1b_evt); + val &= ACPI_BITMASK_WAKE_STATUS; + val >>= ACPI_BITPOSITION_WAKE_STATUS; + return val; +} + +/* System is really put into sleep state by this stub */ +acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) +{ + ACPI_FLUSH_CPU_CACHE(); + + outw((u16)acpi_sinfo.pm1a_cnt_val, acpi_sinfo.pm1a_cnt); + if (acpi_sinfo.pm1b_cnt) + outw((u16)acpi_sinfo.pm1b_cnt_val, acpi_sinfo.pm1b_cnt); + + /* Wait until we enter sleep state, and spin until we wake */ + while (!acpi_get_wake_status()); + return_ACPI_STATUS(AE_OK); } static int __init acpi_sleep_init(void) diff -r fc995dc97a94 xen/arch/x86/platform_hypercall.c --- a/xen/arch/x86/platform_hypercall.c Mon May 14 18:27:46 2007 -0400 +++ b/xen/arch/x86/platform_hypercall.c Mon May 14 18:29:06 2007 -0400 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,20 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xe } break; + case XENPF_set_acpi_sleep: + { + ret = set_acpi_sleep_info(&op->u.set_acpi_sleep); + if (!ret && copy_to_guest(u_xenpf_op, op, 1)) + ret = -EFAULT; + } + break; + + case XENPF_enter_acpi_sleep: + { + ret = acpi_enter_sleep(&op->u.enter_acpi_sleep); + } + break; + default: ret = -ENOSYS; break; diff -r fc995dc97a94 xen/include/public/platform.h --- a/xen/include/public/platform.h Mon May 14 18:27:46 2007 -0400 +++ b/xen/include/public/platform.h Mon May 14 18:29:06 2007 -0400 @@ -114,6 +114,31 @@ typedef struct xenpf_platform_quirk xenp typedef struct xenpf_platform_quirk xenpf_platform_quirk_t; DEFINE_XEN_GUEST_HANDLE(xenpf_platform_quirk_t); +#define XENPF_set_acpi_sleep 40 +struct xenpf_set_acpi_sleep { + /* IN variables. */ + uint16_t pm1a_cnt_port; + uint16_t pm1b_cnt_port; + uint16_t pm1a_evt_port; + uint16_t pm1b_evt_port; + /* OUT variables */ + uint64_t xen_waking_vec; /* Tell dom0 to set FACS waking vector */ +}; +typedef struct xenpf_set_acpi_sleep xenpf_set_acpi_sleep_t; +DEFINE_XEN_GUEST_HANDLE(xenpf_set_acpi_sleep_t); + +#define XENPF_enter_acpi_sleep 41 +struct xenpf_enter_acpi_sleep { + /* IN variables */ + uint16_t pm1a_cnt_val; + uint16_t pm1b_cnt_val; + uint32_t sleep_state; /* Which state to enter */ + uint32_t video_flags; /* S3_bios or s3_mode */ + uint32_t video_mode; /* Mode setting for s3_mode */ +}; +typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t; +DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t); + struct xen_platform_op { uint32_t cmd; uint32_t interface_version; /* XENPF_INTERFACE_VERSION */ @@ -124,6 +149,8 @@ struct xen_platform_op { struct xenpf_read_memtype read_memtype; struct xenpf_microcode_update microcode; struct xenpf_platform_quirk platform_quirk; + struct xenpf_set_acpi_sleep set_acpi_sleep; + struct xenpf_enter_acpi_sleep enter_acpi_sleep; uint8_t pad[128]; } u; }; diff -r fc995dc97a94 xen/include/xen/acpi.h --- a/xen/include/xen/acpi.h Mon May 14 18:27:46 2007 -0400 +++ b/xen/include/xen/acpi.h Mon May 14 18:29:06 2007 -0400 @@ -535,4 +535,15 @@ static inline int acpi_get_pxm(acpi_hand extern int pnpacpi_disabled; +#include +#ifdef COMPAT +#define xenpf_set_acpi_sleep compat_pf_set_acpi_sleep +#define xenpf_enter_acpi_sleep compat_pf_enter_acpi_sleep +#endif /* COMPAT */ + +extern unsigned long acpi_video_flags; +extern unsigned long saved_videomode; +extern int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info); +extern int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep); +extern int acpi_enter_state(u32 state); #endif /*_LINUX_ACPI_H*/