Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / arch / i386 / kernel / reboot.c
index c8936c4..54cfeab 100644 (file)
@@ -9,23 +9,26 @@
 #include <linux/interrupt.h>
 #include <linux/mc146818rtc.h>
 #include <linux/efi.h>
+#include <linux/dmi.h>
+#include <linux/ctype.h>
+#include <linux/pm.h>
 #include <asm/uaccess.h>
 #include <asm/apic.h>
+#include <asm/desc.h>
 #include "mach_reboot.h"
+#include <linux/reboot_fixups.h>
 
 /*
  * Power off function, if any
  */
 void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
 
 static int reboot_mode;
-int reboot_thru_bios;
+static int reboot_thru_bios;
 
 #ifdef CONFIG_SMP
-int reboot_smp = 0;
 static int reboot_cpu = -1;
-/* shamelessly grabbed from lib/vsprintf.c for readability */
-#define is_digit(c)    ((c) >= '0' && (c) <= '9')
 #endif
 static int __init reboot_setup(char *str)
 {
@@ -45,10 +48,9 @@ static int __init reboot_setup(char *str)
                        break;
 #ifdef CONFIG_SMP
                case 's': /* "smp" reboot by executing reset on BSP or other CPU*/
-                       reboot_smp = 1;
-                       if (is_digit(*(str+1))) {
+                       if (isdigit(*(str+1))) {
                                reboot_cpu = (int) (*(str+1) - '0');
-                               if (is_digit(*(str+2))) 
+                               if (isdigit(*(str+2)))
                                        reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
                        }
                                /* we will leave sorting out the final value 
@@ -67,6 +69,67 @@ static int __init reboot_setup(char *str)
 
 __setup("reboot=", reboot_setup);
 
+/*
+ * Reboot options and system auto-detection code provided by
+ * Dell Inc. so their systems "just work". :-)
+ */
+
+/*
+ * Some machines require the "reboot=b"  commandline option, this quirk makes that automatic.
+ */
+static int __init set_bios_reboot(struct dmi_system_id *d)
+{
+       if (!reboot_thru_bios) {
+               reboot_thru_bios = 1;
+               printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
+       }
+       return 0;
+}
+
+static struct dmi_system_id __initdata reboot_dmi_table[] = {
+       {       /* Handle problems with rebooting on Dell 1300's */
+               .callback = set_bios_reboot,
+               .ident = "Dell PowerEdge 1300",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
+               },
+       },
+       {       /* Handle problems with rebooting on Dell 300's */
+               .callback = set_bios_reboot,
+               .ident = "Dell PowerEdge 300",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
+               },
+       },
+       {       /* Handle problems with rebooting on Dell 2400's */
+               .callback = set_bios_reboot,
+               .ident = "Dell PowerEdge 2400",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
+               },
+       },
+       {       /* Handle problems with rebooting on HP laptops */
+               .callback = set_bios_reboot,
+               .ident = "HP Compaq Laptop",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"),
+               },
+       },
+       { }
+};
+
+static int __init reboot_init(void)
+{
+       dmi_check_system(reboot_dmi_table);
+       return 0;
+}
+
+core_initcall(reboot_init);
+
 /* The following code and data reboots the machine by switching to real
    mode and jumping to the BIOS reset entry point, as if the CPU has
    really been reset.  The previous version asked the keyboard
@@ -88,8 +151,8 @@ static struct
        unsigned long long * base __attribute__ ((packed));
 }
 real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
-real_mode_idt = { 0x3ff, 0 },
-no_idt = { 0, 0 };
+real_mode_idt = { 0x3ff, NULL },
+no_idt = { 0, NULL };
 
 
 /* This is 16-bit protected mode code to disable paging and the cache,
@@ -187,13 +250,13 @@ void machine_real_restart(unsigned char *code, int length)
 
        /* Set up the IDT for real mode. */
 
-       __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
+       load_idt(&real_mode_idt);
 
        /* Set up a GDT from which we can load segment descriptors for real
           mode.  The GDT is not used in real mode; it is just needed here to
           prepare the descriptors. */
 
-       __asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt));
+       load_gdt(&real_mode_gdt);
 
        /* Load the data segment registers, and thus the descriptors ready for
           real mode.  The base address of each segment is 0x100, 16 times the
@@ -216,90 +279,86 @@ void machine_real_restart(unsigned char *code, int length)
                                :
                                : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100)));
 }
+#ifdef CONFIG_APM_MODULE
+EXPORT_SYMBOL(machine_real_restart);
+#endif
 
-void machine_restart(char * __unused)
+void machine_shutdown(void)
 {
 #ifdef CONFIG_SMP
-       int cpuid;
-       
-       cpuid = GET_APIC_ID(apic_read(APIC_ID));
-
-       if (reboot_smp) {
-
-               /* check to see if reboot_cpu is valid 
-                  if its not, default to the BSP */
-               if ((reboot_cpu == -1) ||  
-                     (reboot_cpu > (NR_CPUS -1))  || 
-                     !physid_isset(cpuid, phys_cpu_present_map))
-                       reboot_cpu = boot_cpu_physical_apicid;
-
-               reboot_smp = 0;  /* use this as a flag to only go through this once*/
-               /* re-run this function on the other CPUs
-                  it will fall though this section since we have 
-                  cleared reboot_smp, and do the reboot if it is the
-                  correct CPU, otherwise it halts. */
-               if (reboot_cpu != cpuid)
-                       smp_call_function((void *)machine_restart , NULL, 1, 0);
+       int reboot_cpu_id;
+
+       /* The boot cpu is always logical cpu 0 */
+       reboot_cpu_id = 0;
+
+       /* See if there has been given a command line override */
+       if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) &&
+               cpu_isset(reboot_cpu, cpu_online_map)) {
+               reboot_cpu_id = reboot_cpu;
        }
 
-       /* if reboot_cpu is still -1, then we want a tradional reboot, 
-          and if we are not running on the reboot_cpu,, halt */
-       if ((reboot_cpu != -1) && (cpuid != reboot_cpu)) {
-               for (;;)
-               __asm__ __volatile__ ("hlt");
+       /* Make certain the cpu I'm rebooting on is online */
+       if (!cpu_isset(reboot_cpu_id, cpu_online_map)) {
+               reboot_cpu_id = smp_processor_id();
        }
-       /*
-        * Stop all CPUs and turn off local APICs and the IO-APIC, so
-        * other OSs see a clean IRQ state.
+
+       /* Make certain I only run on the appropriate processor */
+       set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id));
+
+       /* O.K. Now that I'm on the appropriate processor, stop
+        * all of the others, and disable their local APICs.
         */
+
        smp_send_stop();
-#elif defined(CONFIG_X86_LOCAL_APIC)
-       if (cpu_has_apic) {
-               local_irq_disable();
-               disable_local_APIC();
-               local_irq_enable();
-       }
-#endif
+#endif /* CONFIG_SMP */
+
+       lapic_shutdown();
+
 #ifdef CONFIG_X86_IO_APIC
        disable_IO_APIC();
 #endif
+}
 
+void machine_emergency_restart(void)
+{
        if (!reboot_thru_bios) {
                if (efi_enabled) {
-                       efi.reset_system(EFI_RESET_COLD, EFI_SUCCESS, 0, 0);
-                       __asm__ __volatile__("lidt %0": :"m" (no_idt));
+                       efi.reset_system(EFI_RESET_COLD, EFI_SUCCESS, 0, NULL);
+                       load_idt(&no_idt);
                        __asm__ __volatile__("int3");
                }
                /* rebooting needs to touch the page at absolute addr 0 */
                *((unsigned short *)__va(0x472)) = reboot_mode;
                for (;;) {
+                       mach_reboot_fixups(); /* for board specific fixups */
                        mach_reboot();
                        /* That didn't work - force a triple fault.. */
-                       __asm__ __volatile__("lidt %0": :"m" (no_idt));
+                       load_idt(&no_idt);
                        __asm__ __volatile__("int3");
                }
        }
        if (efi_enabled)
-               efi.reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, 0);
+               efi.reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL);
 
        machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
 }
 
-EXPORT_SYMBOL(machine_restart);
+void machine_restart(char * __unused)
+{
+       machine_shutdown();
+       machine_emergency_restart();
+}
 
 void machine_halt(void)
 {
 }
 
-EXPORT_SYMBOL(machine_halt);
-
 void machine_power_off(void)
 {
-       if (efi_enabled)
-               efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, 0);
-       if (pm_power_off)
+       if (pm_power_off) {
+               machine_shutdown();
                pm_power_off();
+       }
 }
 
-EXPORT_SYMBOL(machine_power_off);