For broken nForce2 BIOS resulting in XT-PIC timer.
 
        acpi_sleep=     [HW,ACPI] Sleep options
-                       Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
-                                 old_ordering, nonvs, sci_force_enable, nobl }
+                       Format: { s3_bios, s3_mode, s3_beep, s4_hwsig,
+                                 s4_nohwsig, old_ordering, nonvs,
+                                 sci_force_enable, nobl }
                        See Documentation/power/video.rst for information on
                        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_hwsig causes the kernel to check the ACPI hardware
+                       signature during resume from hibernation, and gracefully
+                       refuse to resume if it has changed. This complies with
+                       the ACPI specification but not with reality, since
+                       Windows does not do this and many laptops do change it
+                       on docking. So the default behaviour is to allow resume
+                       and simply warn when the signature changes, unless the
+                       s4_hwsig option is enabled.
                        s4_nohwsig prevents ACPI hardware signature from being
-                       used during resume from hibernation.
+                       used (or even warned about) during resume.
                        old_ordering causes the ACPI 1.0 ordering of the _PTS
                        control method, with respect to putting devices into
                        low power states, to be enforced (the ACPI 2.0 ordering
 
                if (strncmp(str, "s3_beep", 7) == 0)
                        acpi_realmode_flags |= 4;
 #ifdef CONFIG_HIBERNATION
+               if (strncmp(str, "s4_hwsig", 8) == 0)
+                       acpi_check_s4_hw_signature(1);
                if (strncmp(str, "s4_nohwsig", 10) == 0)
-                       acpi_no_s4_hw_signature();
+                       acpi_check_s4_hw_signature(0);
 #endif
                if (strncmp(str, "nonvs", 5) == 0)
                        acpi_nvs_nosave();
 
 #ifdef CONFIG_HIBERNATION
 static unsigned long s4_hardware_signature;
 static struct acpi_table_facs *facs;
-static bool nosigcheck;
+static int sigcheck = -1; /* Default behaviour is just to warn */
 
-void __init acpi_no_s4_hw_signature(void)
+void __init acpi_check_s4_hw_signature(int check)
 {
-       nosigcheck = true;
+       sigcheck = check;
 }
 
 static int acpi_hibernation_begin(pm_message_t stage)
        hibernation_set_ops(old_suspend_ordering ?
                        &acpi_hibernation_ops_old : &acpi_hibernation_ops);
        sleep_states[ACPI_STATE_S4] = 1;
-       if (nosigcheck)
+       if (!sigcheck)
                return;
 
        acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs);
-       if (facs)
+       if (facs) {
+               /*
+                * s4_hardware_signature is the local variable which is just
+                * used to warn about mismatch after we're attempting to
+                * resume (in violation of the ACPI specification.)
+                */
                s4_hardware_signature = facs->hardware_signature;
+
+               if (sigcheck > 0) {
+                       /*
+                        * If we're actually obeying the ACPI specification
+                        * then the signature is written out as part of the
+                        * swsusp header, in order to allow the boot kernel
+                        * to gracefully decline to resume.
+                        */
+                       swsusp_hardware_signature = facs->hardware_signature;
+               }
+       }
 }
 #else /* !CONFIG_HIBERNATION */
 static inline void acpi_sleep_hibernate_setup(void) {}
 
 int acpi_resources_are_enforced(void);
 
 #ifdef CONFIG_HIBERNATION
-void __init acpi_no_s4_hw_signature(void);
+void __init acpi_check_s4_hw_signature(int check);
 #endif
 
 #ifdef CONFIG_PM_SLEEP
 
 extern asmlinkage int swsusp_arch_suspend(void);
 extern asmlinkage int swsusp_arch_resume(void);
 
+extern u32 swsusp_hardware_signature;
 extern void hibernation_set_ops(const struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
 
 #define SF_PLATFORM_MODE       1
 #define SF_NOCOMPRESS_MODE     2
 #define SF_CRC32_MODE          4
+#define SF_HW_SIG              8
 
 /* kernel/power/hibernate.c */
 extern int swsusp_check(void);
 
 
 #define HIBERNATE_SIG  "S1SUSPEND"
 
+u32 swsusp_hardware_signature;
+
 /*
  * When reading an {un,}compressed image, we may restore pages in place,
  * in which case some architectures need these pages cleaning before they
 
 struct swsusp_header {
        char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int) -
-                     sizeof(u32)];
+                     sizeof(u32) - sizeof(u32)];
+       u32     hw_sig;
        u32     crc32;
        sector_t image;
        unsigned int flags;     /* Flags to pass to the "boot" kernel */
 /*
  * Saving part
  */
-
 static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags)
 {
        int error;
                memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10);
                memcpy(swsusp_header->sig, HIBERNATE_SIG, 10);
                swsusp_header->image = handle->first_sector;
+               if (swsusp_hardware_signature) {
+                       swsusp_header->hw_sig = swsusp_hardware_signature;
+                       flags |= SF_HW_SIG;
+               }
                swsusp_header->flags = flags;
                if (flags & SF_CRC32_MODE)
                        swsusp_header->crc32 = handle->crc32;
                } else {
                        error = -EINVAL;
                }
+               if (!error && swsusp_header->flags & SF_HW_SIG &&
+                   swsusp_header->hw_sig != swsusp_hardware_signature) {
+                       pr_info("Suspend image hardware signature mismatch (%08x now %08x); aborting resume.\n",
+                               swsusp_header->hw_sig, swsusp_hardware_signature);
+                       error = -EINVAL;
+               }
 
 put:
                if (error)