Commit bdfe6b7c authored by Shaohua Li's avatar Shaohua Li Committed by Linus Torvalds

pm: acpi hibernation: utilize hardware signature

ACPI defines a hardware signature.  BIOS calculates the signature according to
hardware configure and if hardware changes while hibernated, the signature
will change.  In that case, S4 resume should fail.

Still, there may be systems on which this mechanism does not work correctly,
so it is better to provide a workaround for them.  For this reason, add a new
switch to the acpi_sleep= command line argument allowing one to disable
hardware signature checking.

[shaohua.li@intel.com: build fix]
Signed-off-by: default avatarShaohua Li <shaohua.li@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Len Brown <lenb@kernel.org>
Acked-by: default avatarPavel Machek <pavel@ucw.cz>
Cc: <Valdis.Kletnieks@vt.edu>
Cc: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 2f15fc4b
...@@ -148,10 +148,12 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -148,10 +148,12 @@ and is between 256 and 4096 characters. It is defined in the file
default: 0 default: 0
acpi_sleep= [HW,ACPI] Sleep options acpi_sleep= [HW,ACPI] Sleep options
Format: { s3_bios, s3_mode, s3_beep, old_ordering } Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
See Documentation/power/video.txt for s3_bios and s3_mode. See Documentation/power/video.txt for s3_bios and s3_mode.
s3_beep is for debugging; it makes the PC's speaker beep s3_beep is for debugging; it makes the PC's speaker beep
as soon as the kernel's real-mode entry point is called. as soon as the kernel's real-mode entry point is called.
s4_nohwsig prevents ACPI hardware signature from being
used during resume from hibernation.
old_ordering causes the ACPI 1.0 ordering of the _PTS old_ordering causes the ACPI 1.0 ordering of the _PTS
control method, wrt putting devices into low power control method, wrt putting devices into low power
states, to be enforced (the ACPI 2.0 ordering of _PTS is states, to be enforced (the ACPI 2.0 ordering of _PTS is
......
...@@ -150,6 +150,10 @@ static int __init acpi_sleep_setup(char *str) ...@@ -150,6 +150,10 @@ static int __init acpi_sleep_setup(char *str)
acpi_realmode_flags |= 2; acpi_realmode_flags |= 2;
if (strncmp(str, "s3_beep", 7) == 0) if (strncmp(str, "s3_beep", 7) == 0)
acpi_realmode_flags |= 4; acpi_realmode_flags |= 4;
#ifdef CONFIG_HIBERNATION
if (strncmp(str, "s4_nohwsig", 10) == 0)
acpi_no_s4_hw_signature();
#endif
if (strncmp(str, "old_ordering", 12) == 0) if (strncmp(str, "old_ordering", 12) == 0)
acpi_old_suspend_ordering(); acpi_old_suspend_ordering();
str = strchr(str, ','); str = strchr(str, ',');
......
...@@ -283,6 +283,15 @@ static struct platform_suspend_ops acpi_suspend_ops_old = { ...@@ -283,6 +283,15 @@ static struct platform_suspend_ops acpi_suspend_ops_old = {
#endif /* CONFIG_SUSPEND */ #endif /* CONFIG_SUSPEND */
#ifdef CONFIG_HIBERNATION #ifdef CONFIG_HIBERNATION
static unsigned long s4_hardware_signature;
static struct acpi_table_facs *facs;
static bool nosigcheck;
void __init acpi_no_s4_hw_signature(void)
{
nosigcheck = true;
}
static int acpi_hibernation_begin(void) static int acpi_hibernation_begin(void)
{ {
acpi_target_sleep_state = ACPI_STATE_S4; acpi_target_sleep_state = ACPI_STATE_S4;
...@@ -316,6 +325,12 @@ static void acpi_hibernation_leave(void) ...@@ -316,6 +325,12 @@ static void acpi_hibernation_leave(void)
acpi_enable(); acpi_enable();
/* Reprogram control registers and execute _BFS */ /* Reprogram control registers and execute _BFS */
acpi_leave_sleep_state_prep(ACPI_STATE_S4); acpi_leave_sleep_state_prep(ACPI_STATE_S4);
/* Check the hardware signature */
if (facs && s4_hardware_signature != facs->hardware_signature) {
printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
"cannot resume!\n");
panic("ACPI S4 hardware signature mismatch");
}
} }
static void acpi_pm_enable_gpes(void) static void acpi_pm_enable_gpes(void)
...@@ -544,6 +559,13 @@ int __init acpi_sleep_init(void) ...@@ -544,6 +559,13 @@ int __init acpi_sleep_init(void)
&acpi_hibernation_ops_old : &acpi_hibernation_ops); &acpi_hibernation_ops_old : &acpi_hibernation_ops);
sleep_states[ACPI_STATE_S4] = 1; sleep_states[ACPI_STATE_S4] = 1;
printk(" S4"); printk(" S4");
if (!nosigcheck) {
acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS,
(struct acpi_table_header **)&facs);
if (facs)
s4_hardware_signature =
facs->hardware_signature;
}
} }
#endif #endif
status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
......
...@@ -236,6 +236,7 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n, ...@@ -236,6 +236,7 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n,
const char *name); const char *name);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
void __init acpi_no_s4_hw_signature(void);
void __init acpi_old_suspend_ordering(void); void __init acpi_old_suspend_ordering(void);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
#else /* CONFIG_ACPI */ #else /* CONFIG_ACPI */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment