pm: acpi hibernation: utilize hardware signature
authorShaohua Li <shaohua.li@intel.com>
Thu, 24 Jul 2008 04:28:41 +0000 (21:28 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Jul 2008 17:47:24 +0000 (10:47 -0700)
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: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Len Brown <lenb@kernel.org>
Acked-by: Pavel Machek <pavel@ucw.cz>
Cc: <Valdis.Kletnieks@vt.edu>
Cc: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/kernel-parameters.txt
arch/x86/kernel/acpi/sleep.c
drivers/acpi/sleep/main.c
include/linux/acpi.h

index 4d705713cabcdf3c8f38a3d523da39cb1973bf2b..497a98dafdaa0157520fa9fdc61a7ed81cdb1e22 100644 (file)
@@ -148,10 +148,12 @@ and is between 256 and 4096 characters. It is defined in the file
                        default: 0
 
        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.
                        s3_beep is for debugging; it makes the PC's speaker beep
                        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
                        control method, wrt putting devices into low power
                        states, to be enforced (the ACPI 2.0 ordering of _PTS is
index a3ddad18aaa35b37c5344c4080b59d3a46c99021..fa2161d5003b78ad6e7b021523efd7b761894602 100644 (file)
@@ -150,6 +150,10 @@ static int __init acpi_sleep_setup(char *str)
                        acpi_realmode_flags |= 2;
                if (strncmp(str, "s3_beep", 7) == 0)
                        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)
                        acpi_old_suspend_ordering();
                str = strchr(str, ',');
index 0489a7d1d42c91dcf404c50c56e07c722a41ea1f..313507accf1874a188f612ec17ef9d8fa3e958c7 100644 (file)
@@ -283,6 +283,15 @@ static struct platform_suspend_ops acpi_suspend_ops_old = {
 #endif /* CONFIG_SUSPEND */
 
 #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)
 {
        acpi_target_sleep_state = ACPI_STATE_S4;
@@ -316,6 +325,12 @@ static void acpi_hibernation_leave(void)
        acpi_enable();
        /* Reprogram control registers and execute _BFS */
        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)
@@ -544,6 +559,13 @@ int __init acpi_sleep_init(void)
                        &acpi_hibernation_ops_old : &acpi_hibernation_ops);
                sleep_states[ACPI_STATE_S4] = 1;
                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
        status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
index a171776393767ab0e23411c4ff2d8a89f15676e2..702f79dad16a98a3638957bf64423c5b128423ce 100644 (file)
@@ -236,6 +236,7 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n,
                      const char *name);
 
 #ifdef CONFIG_PM_SLEEP
+void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
 #endif /* CONFIG_PM_SLEEP */
 #else  /* CONFIG_ACPI */