Improve our reboot handling for compatibility with Windows. Upstream in .38? diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index c495aa8..c770e66 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -34,7 +34,7 @@ EXPORT_SYMBOL(pm_power_off); static const struct desc_ptr no_idt = {}; static int reboot_mode; -enum reboot_type reboot_type = BOOT_KBD; +enum reboot_type reboot_type = BOOT_ACPI; int reboot_force; #if defined(CONFIG_X86_32) && defined(CONFIG_SMP) @@ -538,9 +538,23 @@ void __attribute__((weak)) mach_reboot_fixups(void) { } +/* + * Windows does the following on reboot: + * 1) If the FADT has the ACPI reboot register flag set, try it + * 2) If still alive, write to the keyboard controller + * 3) If still alive, write to the ACPI reboot register again + * 4) Ig still alive, write to the keyboard controller again + * + * If the machine is still alive at this stage, it gives up. We default to + * following the same pattern, except that if we're still alive after (4) we'll + * try to force a triple fault and then cycle between hitting the keyboard + * controller and doing that + */ static void native_machine_emergency_restart(void) { int i; + int attempt = 0; + int orig_reboot_type = reboot_type; if (reboot_emergency) emergency_vmx_disable_all(); @@ -562,6 +576,13 @@ static void native_machine_emergency_restart(void) outb(0xfe, 0x64); /* pulse reset low */ udelay(50); } + if (attempt == 0 && orig_reboot_type == BOOT_ACPI) { + attempt = 1; + reboot_type = BOOT_ACPI; + } else { + reboot_type = BOOT_TRIPLE; + } + break; case BOOT_TRIPLE: load_idt(&no_idt); diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 50cc3be..c6a4e63 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -82,12 +82,11 @@ acpi_status acpi_reset(void) /* * For I/O space, write directly to the OSL. This bypasses the port * validation mechanism, which may block a valid write to the reset - * register. + * register. Spec section 4.7.3.6 requires register width to be 8. */ status = acpi_os_write_port((acpi_io_address) reset_reg->address, - acpi_gbl_FADT.reset_value, - reset_reg->bit_width); + acpi_gbl_FADT.reset_value, 8); } else { /* Write the reset value to the reset register */ diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c index 93f9114..a6c77e8b 100644 --- a/drivers/acpi/reboot.c +++ b/drivers/acpi/reboot.c @@ -15,9 +15,15 @@ void acpi_reboot(void) rr = &acpi_gbl_FADT.reset_register; - /* Is the reset register supported? */ - if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) || - rr->bit_width != 8 || rr->bit_offset != 0) + /* ACPI reset register was only introduced with v2 of the FADT */ + + if (acpi_gbl_FADT.header.revision < 2) + return; + + /* Is the reset register supported? The spec says we should be + * checking the bit width and bit offset, but Windows ignores + * these fields */ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER)) return; reset_value = acpi_gbl_FADT.reset_value; @@ -45,6 +51,4 @@ void acpi_reboot(void) acpi_reset(); break; } - /* Wait ten seconds */ - acpi_os_stall(10000000); }