
From: Mikael Pettersson <mikpe@csd.uu.se>

Here is a patch to disable the local APIC before reboot.  This fixes BIOS
reboot problems reported by a few people.

disable_local_APIC() now checks if detect_init_APIC() enabled the local
APIC via the APIC_BASE MSR, and if so it now disables APIC_BASE. 
Previously we would leave APIC_BASE enabled, and that made some BIOSen
unhappy.

The SMP reboot code calls disable_local_APIC().  On SMP HW there is no
change since detect_init_APIC() isn't called and APIC_BASE isn't enabled by
us.  An SMP kernel on UP HW behaves just like an UP_APIC kernel, so it
disables APIC_BASE if we enabled it at boot.

The UP_APIC disable-before-suspend code is simplified since the existing
code to disable APIC_BASE is moved into disable_local_APIC().

(Felix Kühling originally reported the BIOS reboot problem.  This is a
fixed-up version of his preliminary patch.)



 25-akpm/arch/i386/kernel/apic.c   |   14 ++++++++++----
 25-akpm/arch/i386/kernel/reboot.c |    9 +++++++++
 2 files changed, 19 insertions(+), 4 deletions(-)

diff -puN arch/i386/kernel/apic.c~reboot-disable-local-apic arch/i386/kernel/apic.c
--- 25/arch/i386/kernel/apic.c~reboot-disable-local-apic	Mon Aug 11 16:39:02 2003
+++ 25-akpm/arch/i386/kernel/apic.c	Mon Aug 11 16:39:02 2003
@@ -66,6 +66,8 @@ static DEFINE_PER_CPU(int, prof_multipli
 static DEFINE_PER_CPU(int, prof_old_multiplier) = 1;
 static DEFINE_PER_CPU(int, prof_counter) = 1;
 
+static int enabled_via_apicbase;
+
 void enable_NMI_through_LVT0 (void * dummy)
 {
 	unsigned int v, ver;
@@ -195,6 +197,13 @@ void disable_local_APIC(void)
 	value = apic_read(APIC_SPIV);
 	value &= ~APIC_SPIV_APIC_ENABLED;
 	apic_write_around(APIC_SPIV, value);
+
+	if (enabled_via_apicbase) {
+		unsigned int l, h;
+		rdmsr(MSR_IA32_APICBASE, l, h);
+		l &= ~MSR_IA32_APICBASE_ENABLE;
+		wrmsr(MSR_IA32_APICBASE, l, h);
+	}
 }
 
 /*
@@ -490,7 +499,6 @@ static struct {
 
 static int lapic_suspend(struct sys_device *dev, u32 state)
 {
-	unsigned int l, h;
 	unsigned long flags;
 
 	if (!apic_pm_state.active)
@@ -512,9 +520,6 @@ static int lapic_suspend(struct sys_devi
 	
 	local_irq_save(flags);
 	disable_local_APIC();
-	rdmsr(MSR_IA32_APICBASE, l, h);
-	l &= ~MSR_IA32_APICBASE_ENABLE;
-	wrmsr(MSR_IA32_APICBASE, l, h);
 	local_irq_restore(flags);
 	return 0;
 }
@@ -660,6 +665,7 @@ static int __init detect_init_APIC (void
 			l &= ~MSR_IA32_APICBASE_BASE;
 			l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
 			wrmsr(MSR_IA32_APICBASE, l, h);
+			enabled_via_apicbase = 1;
 		}
 	}
 	/*
diff -puN arch/i386/kernel/reboot.c~reboot-disable-local-apic arch/i386/kernel/reboot.c
--- 25/arch/i386/kernel/reboot.c~reboot-disable-local-apic	Mon Aug 11 16:39:02 2003
+++ 25-akpm/arch/i386/kernel/reboot.c	Mon Aug 11 16:39:02 2003
@@ -8,6 +8,7 @@
 #include <linux/interrupt.h>
 #include <linux/mc146818rtc.h>
 #include <asm/uaccess.h>
+#include <asm/apic.h>
 #include "mach_reboot.h"
 
 /*
@@ -248,6 +249,14 @@ void machine_restart(char * __unused)
 	 * other OSs see a clean IRQ state.
 	 */
 	smp_send_stop();
+#elif CONFIG_X86_LOCAL_APIC
+	if (cpu_has_apic) {
+		local_irq_disable();
+		disable_local_APIC();
+		local_irq_enable();
+	}
+#endif
+#ifdef CONFIG_X86_IO_APIC
 	disable_IO_APIC();
 #endif
 

_
