2011-04-04 17:55:25

by Matthew Garrett

[permalink] [raw]
Subject: [PATCH] X86: Reorder reboot method preferences

Windows reboots by hitting the ACPI reboot vector (if available), trying
the keyboard controller, hitting the ACPI reboot vector again and then
giving the keyboard controller one last go. This is important for modern
hardware, which often lacks a keyboard controller and may even wedge if the
legacy io ports are hit. Rework our reboot process a little to default to
matching this behaviour, although we'll fall through to attempting a triple
fault if nothing else works.

Signed-off-by: Matthew Garrett <[email protected]>
---
arch/x86/kernel/reboot.c | 23 ++++++++++++++++++++++-
1 files changed, 22 insertions(+), 1 deletions(-)

diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index d3ce37e..9dd4330 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -35,7 +35,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)
@@ -477,9 +477,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) If 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();
@@ -501,6 +515,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);
--
1.7.4.1


2011-04-04 18:16:55

by Alan

[permalink] [raw]
Subject: Re: [PATCH] X86: Reorder reboot method preferences

On Mon, 4 Apr 2011 13:55:05 -0400
Matthew Garrett <[email protected]> wrote:

> Windows reboots by hitting the ACPI reboot vector (if available), trying
> the keyboard controller, hitting the ACPI reboot vector again and then
> giving the keyboard controller one last go. This is important for modern
> hardware, which often lacks a keyboard controller and may even wedge if the
> legacy io ports are hit. Rework our reboot process a little to default to
> matching this behaviour, although we'll fall through to attempting a triple
> fault if nothing else works.

Acked-by: Alan Cox <[email protected]>

and will also save special casing Oaktrail boards

2011-04-06 11:07:22

by Matthew Garrett

[permalink] [raw]
Subject: [tip:x86/reboot] x86: Reorder reboot method preferences

Commit-ID: 660e34cebf0a11d54f2d5dd8838607452355f321
Gitweb: http://git.kernel.org/tip/660e34cebf0a11d54f2d5dd8838607452355f321
Author: Matthew Garrett <[email protected]>
AuthorDate: Mon, 4 Apr 2011 13:55:05 -0400
Committer: Ingo Molnar <[email protected]>
CommitDate: Wed, 6 Apr 2011 10:36:50 +0200

x86: Reorder reboot method preferences

We have a never ending stream of 'reboot quirks' for new boxes
that will not reboot properly under Linux (they will hang on
reboot).

The reason is widespread 'Windows compatible' assumption of modern
x86 hardware, which expects the following reboot sequence:

- hitting the ACPI reboot vector (if available)
- trying the keyboard controller
- hitting the ACPI reboot vector again
- then giving the keyboard controller one last go

This sequence expectation gets more and more embedded in modern
hardware, which often lacks a keyboard controller and may even
lock up if the legacy io ports are hit - and which hardware is
often not tested with Linux during development.

The end result is that reboot works under Windows-alike OSs but not
under Linux.

Rework our reboot process to meet this hardware externality a little
better and match this assumption of newer x86 hardware.

In addition to the ACPI,kbd,ACPI,kbd sequence we'll still fall
through to attempting a legacy triple fault if nothing else
works - and keep trying that and the kbd reset.

Signed-off-by: Matthew Garrett <[email protected]>
[ this commit will also save special casing Oaktrail boards ]
Acked-by: Alan Cox <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Leann Ogasawara <[email protected]>
Cc: Dave Jones <[email protected]>
Cc: Len Brown <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
---
arch/x86/kernel/reboot.c | 24 +++++++++++++++++++++++-
1 files changed, 23 insertions(+), 1 deletions(-)

diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 08c44b0..0c016f7 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -36,7 +36,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)
@@ -478,9 +478,24 @@ void __attribute__((weak)) mach_reboot_fixups(void)
{
}

+/*
+ * Windows compatible x86 hardware expects 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) If 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();
@@ -502,6 +517,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);