mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
[host s3] Retrieve necessary sleep information from plain-text ACPI
tables (FADT/FACS), and keep one hypercall remained for sleep notification. Signed-off-by: Ke Yu <ke.yu@intel.com> Signed-off-by: Kevin Tian <kevin.tian@intel.com> Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
d17ac42f37
commit
d3faf9badf
8 files changed
+174
-93
No files matched your search
@@ -369,6 +369,95 @@ static int __init acpi_parse_hpet(unsigned long phys, unsigned long size)
|
||||
extern u32 pmtmr_ioport;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI_SLEEP
|
||||
/* Get pm1x_cnt and pm1x_evt information for ACPI sleep */
|
||||
static int __init
|
||||
acpi_fadt_parse_sleep_info(struct fadt_descriptor_rev2 *fadt)
|
||||
{
|
||||
struct facs_descriptor_rev2 *facs = NULL;
|
||||
uint64_t facs_pa;
|
||||
|
||||
if (fadt->revision >= FADT2_REVISION_ID) {
|
||||
/* Sanity check on FADT Rev. 2 */
|
||||
if ((fadt->xpm1a_cnt_blk.address_space_id !=
|
||||
ACPI_ADR_SPACE_SYSTEM_IO) ||
|
||||
(fadt->xpm1b_cnt_blk.address_space_id !=
|
||||
ACPI_ADR_SPACE_SYSTEM_IO) ||
|
||||
(fadt->xpm1a_evt_blk.address_space_id !=
|
||||
ACPI_ADR_SPACE_SYSTEM_IO) ||
|
||||
(fadt->xpm1b_evt_blk.address_space_id !=
|
||||
ACPI_ADR_SPACE_SYSTEM_IO))
|
||||
goto bad;
|
||||
|
||||
acpi_sinfo.pm1a_cnt = (uint16_t)fadt->xpm1a_cnt_blk.address;
|
||||
acpi_sinfo.pm1b_cnt = (uint16_t)fadt->xpm1b_cnt_blk.address;
|
||||
acpi_sinfo.pm1a_evt = (uint16_t)fadt->xpm1a_evt_blk.address;
|
||||
acpi_sinfo.pm1b_evt = (uint16_t)fadt->xpm1b_evt_blk.address;
|
||||
}
|
||||
|
||||
if (!acpi_sinfo.pm1a_cnt)
|
||||
acpi_sinfo.pm1a_cnt = (uint16_t)fadt->V1_pm1a_cnt_blk;
|
||||
if (!acpi_sinfo.pm1b_cnt)
|
||||
acpi_sinfo.pm1b_cnt = (uint16_t)fadt->V1_pm1b_cnt_blk;
|
||||
if (!acpi_sinfo.pm1a_evt)
|
||||
acpi_sinfo.pm1a_evt = (uint16_t)fadt->V1_pm1a_evt_blk;
|
||||
if (!acpi_sinfo.pm1b_evt)
|
||||
acpi_sinfo.pm1b_evt = (uint16_t)fadt->V1_pm1b_evt_blk;
|
||||
|
||||
/* Now FACS... */
|
||||
if (fadt->revision >= FADT2_REVISION_ID)
|
||||
facs_pa = fadt->xfirmware_ctrl;
|
||||
else
|
||||
facs_pa = (uint64_t)fadt->V1_firmware_ctrl;
|
||||
|
||||
facs = (struct facs_descriptor_rev2 *)
|
||||
__acpi_map_table(facs_pa, sizeof(struct facs_descriptor_rev2));
|
||||
if (!facs)
|
||||
goto bad;
|
||||
|
||||
if (strncmp(facs->signature, "FACS", 4)) {
|
||||
printk(KERN_ERR PREFIX "Invalid FACS signature %s\n",
|
||||
facs->signature);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (facs->length < 24) {
|
||||
printk(KERN_ERR PREFIX "Invalid FACS table length: 0x%x",
|
||||
facs->length);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (facs->length < 64)
|
||||
printk(KERN_WARNING PREFIX
|
||||
"FACS is shorter than ACPI spec allow: 0x%x",
|
||||
facs->length);
|
||||
|
||||
if ((acpi_rsdp_rev < 2) ||
|
||||
(facs->length < 32)) {
|
||||
acpi_sinfo.wakeup_vector = facs_pa +
|
||||
offsetof(struct facs_descriptor_rev2,
|
||||
firmware_waking_vector);
|
||||
acpi_sinfo.vector_width = 32;
|
||||
} else {
|
||||
acpi_sinfo.wakeup_vector = facs_pa +
|
||||
offsetof(struct facs_descriptor_rev2,
|
||||
xfirmware_waking_vector);
|
||||
acpi_sinfo.vector_width = 64;
|
||||
}
|
||||
|
||||
printk (KERN_INFO PREFIX
|
||||
"ACPI SLEEP INFO: pm1x_cnt[%x,%x], pm1x_evt[%x,%x]\n"
|
||||
" wakeup_vec[%"PRIx64"], vec_size[%x]\n",
|
||||
acpi_sinfo.pm1a_cnt, acpi_sinfo.pm1b_cnt,
|
||||
acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_cnt,
|
||||
acpi_sinfo.wakeup_vector, acpi_sinfo.vector_width);
|
||||
return 0;
|
||||
bad:
|
||||
memset(&acpi_sinfo, 0, sizeof(acpi_sinfo));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init acpi_parse_fadt(unsigned long phys, unsigned long size)
|
||||
{
|
||||
struct fadt_descriptor_rev2 *fadt = NULL;
|
||||
@@ -413,6 +502,10 @@ static int __init acpi_parse_fadt(unsigned long phys, unsigned long size)
|
||||
printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n",
|
||||
pmtmr_ioport);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI_SLEEP
|
||||
acpi_fadt_parse_sleep_info(fadt);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+56
-52
@@ -28,20 +28,15 @@
|
||||
|
||||
#define pmprintk(_l, _f, _a...) printk(_l "<PM>" _f, ## _a )
|
||||
|
||||
u8 sleep_states[ACPI_S_STATE_COUNT];
|
||||
DEFINE_SPINLOCK(pm_lock);
|
||||
static char opt_acpi_sleep[20];
|
||||
string_param("acpi_sleep", opt_acpi_sleep);
|
||||
|
||||
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;
|
||||
static u8 sleep_states[ACPI_S_STATE_COUNT];
|
||||
static DEFINE_SPINLOCK(pm_lock);
|
||||
|
||||
extern void do_suspend_lowlevel(void);
|
||||
struct acpi_sleep_info acpi_sinfo;
|
||||
|
||||
void do_suspend_lowlevel(void);
|
||||
|
||||
static char *acpi_states[ACPI_S_STATE_COUNT] =
|
||||
{
|
||||
@@ -50,10 +45,6 @@ static char *acpi_states[ACPI_S_STATE_COUNT] =
|
||||
[ACPI_STATE_S4] = "disk",
|
||||
};
|
||||
|
||||
unsigned long acpi_video_flags;
|
||||
unsigned long saved_videomode;
|
||||
|
||||
/* XXX: Add suspend failure recover later */
|
||||
static int device_power_down(void)
|
||||
{
|
||||
console_suspend();
|
||||
@@ -100,8 +91,27 @@ static void thaw_domains(void)
|
||||
domain_unpause(d);
|
||||
}
|
||||
|
||||
static void acpi_sleep_prepare(u32 state)
|
||||
{
|
||||
void *wakeup_vector_va;
|
||||
|
||||
if ( state != ACPI_STATE_S3 )
|
||||
return;
|
||||
|
||||
wakeup_vector_va = __acpi_map_table(
|
||||
acpi_sinfo.wakeup_vector, sizeof(uint64_t));
|
||||
if (acpi_sinfo.vector_width == 32)
|
||||
*(uint32_t *)wakeup_vector_va =
|
||||
(uint32_t)bootsym_phys(wakeup_start);
|
||||
else
|
||||
*(uint64_t *)wakeup_vector_va =
|
||||
(uint64_t)bootsym_phys(wakeup_start);
|
||||
}
|
||||
|
||||
static void acpi_sleep_post(u32 state) {}
|
||||
|
||||
/* Main interface to do xen specific suspend/resume */
|
||||
int enter_state(u32 state)
|
||||
static int enter_state(u32 state)
|
||||
{
|
||||
unsigned long flags;
|
||||
int error;
|
||||
@@ -123,6 +133,8 @@ int enter_state(u32 state)
|
||||
pmprintk(XENLOG_INFO, "PM: Preparing system for %s sleep\n",
|
||||
acpi_states[state]);
|
||||
|
||||
acpi_sleep_prepare(state);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if ((error = device_power_down()))
|
||||
@@ -152,6 +164,8 @@ int enter_state(u32 state)
|
||||
Done:
|
||||
local_irq_restore(flags);
|
||||
|
||||
acpi_sleep_post(state);
|
||||
|
||||
if ( !hvm_cpu_up() )
|
||||
BUG();
|
||||
|
||||
@@ -160,30 +174,6 @@ int enter_state(u32 state)
|
||||
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)bootsym_phys(wakeup_start);
|
||||
|
||||
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.
|
||||
@@ -197,20 +187,23 @@ int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info)
|
||||
*/
|
||||
int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep)
|
||||
{
|
||||
if (!IS_PRIV(current->domain) || !acpi_sinfo.pm1a_cnt)
|
||||
if ( !IS_PRIV(current->domain) || !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))
|
||||
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;
|
||||
}
|
||||
|
||||
if ( sleep->flags )
|
||||
return -EINVAL;
|
||||
|
||||
/* Write #1 */
|
||||
if (!(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE))
|
||||
if ( !(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE) )
|
||||
{
|
||||
outw((u16)sleep->pm1a_cnt_val, acpi_sinfo.pm1a_cnt);
|
||||
if (acpi_sinfo.pm1b_cnt)
|
||||
@@ -222,8 +215,6 @@ int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep)
|
||||
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);
|
||||
}
|
||||
@@ -247,7 +238,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
|
||||
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);
|
||||
@@ -255,12 +246,24 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
|
||||
|
||||
static int __init acpi_sleep_init(void)
|
||||
{
|
||||
int i = 0;
|
||||
int i;
|
||||
char *p = opt_acpi_sleep;
|
||||
|
||||
while ( (p != NULL) && (*p != '\0') )
|
||||
{
|
||||
if ( !strncmp(p, "s3_bios", 7) )
|
||||
acpi_video_flags |= 1;
|
||||
if ( !strncmp(p, "s3_mode", 7) )
|
||||
acpi_video_flags |= 2;
|
||||
p = strchr(p, ',');
|
||||
if ( p != NULL )
|
||||
p += strspn(p, ", \t");
|
||||
}
|
||||
|
||||
pmprintk(XENLOG_INFO, "ACPI (supports");
|
||||
for (i = 0; i < ACPI_S_STATE_COUNT; i++)
|
||||
for ( i = 0; i < ACPI_S_STATE_COUNT; i++ )
|
||||
{
|
||||
if (i == ACPI_STATE_S3)
|
||||
if ( i == ACPI_STATE_S3 )
|
||||
{
|
||||
sleep_states[i] = 1;
|
||||
printk(" S%d", i);
|
||||
@@ -269,6 +272,7 @@ static int __init acpi_sleep_init(void)
|
||||
sleep_states[i] = 0;
|
||||
}
|
||||
printk(")\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
__initcall(acpi_sleep_init);
|
||||
@@ -35,9 +35,6 @@ void save_rest_processor_state(void)
|
||||
rdmsrl(MSR_CSTAR, saved_cstar);
|
||||
rdmsrl(MSR_LSTAR, saved_lstar);
|
||||
#endif
|
||||
|
||||
bootsym(video_flags) = acpi_video_flags;
|
||||
bootsym(video_mode) = saved_videomode;
|
||||
}
|
||||
|
||||
#define loaddebug(_v,_reg) \
|
||||
|
||||
@@ -248,21 +248,9 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op)
|
||||
}
|
||||
break;
|
||||
|
||||
#if 0
|
||||
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;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -ENOSYS;
|
||||
|
||||
@@ -73,6 +73,7 @@ struct acpi_table_sdt {
|
||||
|
||||
static unsigned long sdt_pa; /* Physical Address */
|
||||
static unsigned long sdt_count; /* Table count */
|
||||
unsigned char acpi_rsdp_rev;
|
||||
|
||||
static struct acpi_table_sdt sdt_entry[ACPI_MAX_TABLES] __initdata;
|
||||
|
||||
@@ -598,6 +599,8 @@ int __init acpi_table_init(void)
|
||||
"RSDP (v%3.3d %6.6s ) @ 0x%p\n",
|
||||
rsdp->revision, rsdp->oem_id, (void *)rsdp_phys);
|
||||
|
||||
acpi_rsdp_rev = rsdp->revision;
|
||||
|
||||
if (rsdp->revision < 2)
|
||||
result =
|
||||
acpi_table_compute_checksum(rsdp,
|
||||
|
||||
@@ -173,15 +173,25 @@ extern unsigned long acpi_wakeup_address;
|
||||
/* early initialization routine */
|
||||
extern void acpi_reserve_bootmem(void);
|
||||
|
||||
extern unsigned long acpi_video_flags;
|
||||
extern unsigned long saved_videomode;
|
||||
struct xenpf_set_acpi_sleep;
|
||||
extern struct acpi_sleep_info acpi_sinfo;
|
||||
#define acpi_video_flags bootsym(video_flags)
|
||||
struct xenpf_enter_acpi_sleep;
|
||||
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 /*CONFIG_ACPI_SLEEP*/
|
||||
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;
|
||||
uint64_t wakeup_vector;
|
||||
uint32_t vector_width;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_ACPI_SLEEP */
|
||||
|
||||
extern u8 x86_acpiid_to_apicid[];
|
||||
#define MAX_LOCAL_APIC 256
|
||||
|
||||
@@ -153,27 +153,13 @@ struct xenpf_firmware_info {
|
||||
typedef struct xenpf_firmware_info xenpf_firmware_info_t;
|
||||
DEFINE_XEN_GUEST_HANDLE(xenpf_firmware_info_t);
|
||||
|
||||
#define XENPF_set_acpi_sleep 51
|
||||
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 52
|
||||
#define XENPF_enter_acpi_sleep 51
|
||||
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 */
|
||||
uint16_t pm1a_cnt_val; /* PM1a control value. */
|
||||
uint16_t pm1b_cnt_val; /* PM1b control value. */
|
||||
uint32_t sleep_state; /* Which state to enter (Sn). */
|
||||
uint32_t flags; /* Must be zero. */
|
||||
};
|
||||
typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t;
|
||||
DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t);
|
||||
@@ -189,7 +175,6 @@ struct xen_platform_op {
|
||||
struct xenpf_microcode_update microcode;
|
||||
struct xenpf_platform_quirk platform_quirk;
|
||||
struct xenpf_firmware_info firmware_info;
|
||||
struct xenpf_set_acpi_sleep set_acpi_sleep;
|
||||
struct xenpf_enter_acpi_sleep enter_acpi_sleep;
|
||||
uint8_t pad[128];
|
||||
} u;
|
||||
|
||||
@@ -534,5 +534,6 @@ static inline int acpi_get_pxm(acpi_handle handle)
|
||||
#endif
|
||||
|
||||
extern int pnpacpi_disabled;
|
||||
extern unsigned char acpi_rsdp_rev;
|
||||
|
||||
#endif /*_LINUX_ACPI_H*/
|
||||