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 <ke.yu@xxxxxxxxx>
Signed-off-by Kevin Tian <kevin.tian@xxxxxxxxx>
diff -r 169fcd328fd9 xen/arch/x86/acpi/power.c
--- a/xen/arch/x86/acpi/power.c Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/arch/x86/acpi/power.c Tue Feb 13 17:30:34 2007 +0800
@@ -27,6 +27,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] =
@@ -36,7 +46,7 @@ static char *acpi_states[ACPI_S_STATE_CO
[ACPI_STATE_S4] = "disk",
};
-/* Add suspend failure recover later */
+/* XXX: Add suspend failure recover later */
static int device_power_down(void)
{
console_suspend();
@@ -65,6 +75,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;
@@ -92,7 +103,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)
@@ -105,7 +115,7 @@ int enter_state(u32 state)
break;
default:
error = -EINVAL;
- goto Powerup;
+ break;
}
printk("Back to C!\n");
@@ -125,6 +135,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)
+ printk(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);
+
+ printk(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)) {
+ printk(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 169fcd328fd9 xen/arch/x86/platform_hypercall.c
--- a/xen/arch/x86/platform_hypercall.c Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/arch/x86/platform_hypercall.c Tue Feb 13 17:30:21 2007 +0800
@@ -18,6 +18,7 @@
#include <xen/console.h>
#include <xen/iocap.h>
#include <xen/guest_access.h>
+#include <xen/acpi.h>
#include <asm/current.h>
#include <public/platform.h>
#include <asm/mtrr.h>
@@ -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 169fcd328fd9 xen/include/public/platform.h
--- a/xen/include/public/platform.h Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/include/public/platform.h Tue Feb 13 17:30:21 2007 +0800
@@ -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 169fcd328fd9 xen/include/xen/acpi.h
--- a/xen/include/xen/acpi.h Tue Feb 13 17:30:14 2007 +0800
+++ b/xen/include/xen/acpi.h Tue Feb 13 17:30:21 2007 +0800
@@ -535,4 +535,10 @@ static inline int acpi_get_pxm(acpi_hand
extern int pnpacpi_disabled;
+#include <public/platform.h>
+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*/
acpi_sleep_interface.patch
Description: acpi_sleep_interface.patch
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|