UBUNTU: SAUCE: x86: reboot: Make Dell Latitude E6520 use reboot=pci
[linux-flexiantxendom0-natty.git] / arch / x86 / kernel / reboot.c
index 5b32f0b..95d5371 100644 (file)
@@ -1,24 +1,30 @@
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/pm.h>
 #include <linux/efi.h>
+#include <linux/dmi.h>
+#include <linux/sched.h>
+#include <linux/tboot.h>
 #include <acpi/reboot.h>
 #include <asm/io.h>
 #include <asm/apic.h>
 #include <asm/desc.h>
 #include <asm/hpet.h>
+#include <asm/pgtable.h>
+#include <asm/proto.h>
 #include <asm/reboot_fixups.h>
 #include <asm/reboot.h>
+#include <asm/pci_x86.h>
+#include <asm/virtext.h>
+#include <asm/cpu.h>
+#include <asm/nmi.h>
 
 #ifdef CONFIG_X86_32
-# include <linux/dmi.h>
 # include <linux/ctype.h>
 # include <linux/mc146818rtc.h>
-# include <asm/pgtable.h>
 #else
-# include <asm/iommu.h>
+# include <asm/x86_init.h>
 #endif
 
 /*
@@ -27,7 +33,7 @@
 void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
 
-static long no_idt[3];
+static const struct desc_ptr no_idt = {};
 static int reboot_mode;
 enum reboot_type reboot_type = BOOT_KBD;
 int reboot_force;
@@ -36,7 +42,16 @@ int reboot_force;
 static int reboot_cpu = -1;
 #endif
 
-/* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old]
+/* This is set if we need to go through the 'emergency' path.
+ * When machine_emergency_restart() is called, we may be on
+ * an inconsistent state and won't be able to do a clean cleanup
+ */
+static int reboot_emergency;
+
+/* This is set by the PCI code if either type 1 or type 2 PCI is detected */
+bool port_cf9_safe = false;
+
+/* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci]
    warm   Don't set the cold reboot flag
    cold   Set the cold reboot flag
    bios   Reboot by jumping through the BIOS (only for X86_32)
@@ -45,6 +60,7 @@ static int reboot_cpu = -1;
    kbd    Use the keyboard controller. cold reset (default)
    acpi   Use the RESET_REG in the FADT
    efi    Use efi reset_system runtime service
+   pci    Use the so-called "PCI reset register", CF9
    force  Avoid anything that could hang.
  */
 static int __init reboot_setup(char *str)
@@ -69,7 +85,7 @@ static int __init reboot_setup(char *str)
                        }
                                /* we will leave sorting out the final value
                                   when we are ready to reboot, since we might not
-                                  have set up boot_cpu_id or smp_num_cpu */
+                                  have detected BSP APIC ID or smp_num_cpu */
                        break;
 #endif /* CONFIG_SMP */
 
@@ -79,6 +95,7 @@ static int __init reboot_setup(char *str)
                case 'k':
                case 't':
                case 'e':
+               case 'p':
                        reboot_type = *str;
                        break;
 
@@ -149,7 +166,51 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
-                       DMI_MATCH(DMI_BOARD_NAME, "0WF810"),
+               },
+       },
+       {       /* Handle problems with rebooting on Dell Optiplex 745's DFF*/
+               .callback = set_bios_reboot,
+               .ident = "Dell OptiPlex 745",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
+                       DMI_MATCH(DMI_BOARD_NAME, "0MM599"),
+               },
+       },
+       {       /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */
+               .callback = set_bios_reboot,
+               .ident = "Dell OptiPlex 745",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
+                       DMI_MATCH(DMI_BOARD_NAME, "0KW626"),
+               },
+       },
+       {   /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */
+               .callback = set_bios_reboot,
+               .ident = "Dell OptiPlex 330",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"),
+                       DMI_MATCH(DMI_BOARD_NAME, "0KP561"),
+               },
+       },
+       {   /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */
+               .callback = set_bios_reboot,
+               .ident = "Dell OptiPlex 360",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 360"),
+                       DMI_MATCH(DMI_BOARD_NAME, "0T656F"),
+               },
+       },
+       {       /* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G*/
+               .callback = set_bios_reboot,
+               .ident = "Dell OptiPlex 760",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 760"),
+                       DMI_MATCH(DMI_BOARD_NAME, "0G919G"),
                },
        },
        {       /* Handle problems with rebooting on Dell 2400's */
@@ -160,6 +221,22 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
                },
        },
+       {       /* Handle problems with rebooting on Dell T5400's */
+               .callback = set_bios_reboot,
+               .ident = "Dell Precision T5400",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T5400"),
+               },
+       },
+       {       /* Handle problems with rebooting on Dell T7400's */
+               .callback = set_bios_reboot,
+               .ident = "Dell Precision T7400",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T7400"),
+               },
+       },
        {       /* Handle problems with rebooting on HP laptops */
                .callback = set_bios_reboot,
                .ident = "HP Compaq Laptop",
@@ -168,6 +245,54 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"),
                },
        },
+       {       /* Handle problems with rebooting on Dell XPS710 */
+               .callback = set_bios_reboot,
+               .ident = "Dell XPS710",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell XPS710"),
+               },
+       },
+       {       /* Handle problems with rebooting on Dell DXP061 */
+               .callback = set_bios_reboot,
+               .ident = "Dell DXP061",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell DXP061"),
+               },
+       },
+       {       /* Handle problems with rebooting on Sony VGN-Z540N */
+               .callback = set_bios_reboot,
+               .ident = "Sony VGN-Z540N",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"),
+               },
+       },
+       {       /* Handle problems with rebooting on CompuLab SBC-FITPC2 */
+               .callback = set_bios_reboot,
+               .ident = "CompuLab SBC-FITPC2",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "CompuLab"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "SBC-FITPC2"),
+               },
+       },
+       {       /* Handle problems with rebooting on ASUS P4S800 */
+               .callback = set_bios_reboot,
+               .ident = "ASUS P4S800",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+                       DMI_MATCH(DMI_BOARD_NAME, "P4S800"),
+               },
+       },
+       {       /* Handle problems with rebooting on VersaLogic Menlow boards */
+               .callback = set_bios_reboot,
+               .ident = "VersaLogic Menlow based board",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "VersaLogic Corporation"),
+                       DMI_MATCH(DMI_BOARD_NAME, "VersaLogic Menlow board"),
+               },
+       },
        { }
 };
 
@@ -184,15 +309,15 @@ core_initcall(reboot_init);
    controller to pulse the CPU reset line, which is more thorough, but
    doesn't work with at least one type of 486 motherboard.  It is easy
    to stop this code working; hence the copious comments. */
-static unsigned long long
+static const unsigned long long
 real_mode_gdt_entries [3] =
 {
        0x0000000000000000ULL,  /* Null descriptor */
-       0x00009a000000ffffULL,  /* 16-bit real-mode 64k code at 0x00000000 */
-       0x000092000100ffffULL   /* 16-bit real-mode 64k data at 0x00000100 */
+       0x00009b000000ffffULL,  /* 16-bit real-mode 64k code at 0x00000000 */
+       0x000093000100ffffULL   /* 16-bit real-mode 64k data at 0x00000100 */
 };
 
-static struct desc_ptr
+static const struct desc_ptr
 real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
 real_mode_idt = { 0x3ff, 0 };
 
@@ -214,7 +339,7 @@ real_mode_idt = { 0x3ff, 0 };
 
    More could be done here to set up the registers as if a CPU reset had
    occurred; hopefully real BIOSs don't assume much. */
-static unsigned char real_mode_switch [] =
+static const unsigned char real_mode_switch [] =
 {
        0x66, 0x0f, 0x20, 0xc0,                 /*    movl  %cr0,%eax        */
        0x66, 0x83, 0xe0, 0x11,                 /*    andl  $0x00000011,%eax */
@@ -228,7 +353,7 @@ static unsigned char real_mode_switch [] =
        0x24, 0x10,                             /* f: andb  $0x10,al         */
        0x66, 0x0f, 0x22, 0xc0                  /*    movl  %eax,%cr0        */
 };
-static unsigned char jump_to_bios [] =
+static const unsigned char jump_to_bios [] =
 {
        0xea, 0x00, 0x00, 0xff, 0xff            /*    ljmp  $0xffff,$0x0000  */
 };
@@ -238,7 +363,7 @@ static unsigned char jump_to_bios [] =
  * specified by the code and length parameters.
  * We assume that length will aways be less that 100!
  */
-void machine_real_restart(unsigned char *code, int length)
+void machine_real_restart(const unsigned char *code, int length)
 {
        local_irq_disable();
 
@@ -255,16 +380,10 @@ void machine_real_restart(unsigned char *code, int length)
        CMOS_WRITE(0x00, 0x8f);
        spin_unlock(&rtc_lock);
 
-       /* Remap the kernel at virtual address zero, as well as offset zero
-          from the kernel segment.  This assumes the kernel segment starts at
-          virtual address PAGE_OFFSET. */
-       memcpy(swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
-               sizeof(swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
-
        /*
-        * Use `swapper_pg_dir' as our page directory.
+        * Switch back to the initial page table.
         */
-       load_cr3(swapper_pg_dir);
+       load_cr3(initial_page_table);
 
        /* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
           this on booting to tell it to "Bypass memory test (also warm
@@ -315,19 +434,144 @@ EXPORT_SYMBOL(machine_real_restart);
 
 #endif /* CONFIG_X86_32 */
 
+/*
+ * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
+ */
+static int __init set_pci_reboot(const struct dmi_system_id *d)
+{
+       if (reboot_type != BOOT_CF9) {
+               reboot_type = BOOT_CF9;
+               printk(KERN_INFO "%s series board detected. "
+                      "Selecting PCI-method for reboots.\n", d->ident);
+       }
+       return 0;
+}
+
+static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
+       {       /* Handle problems with rebooting on Apple MacBook5 */
+               .callback = set_pci_reboot,
+               .ident = "Apple MacBook5",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"),
+               },
+       },
+       {       /* Handle problems with rebooting on Apple MacBookPro5 */
+               .callback = set_pci_reboot,
+               .ident = "Apple MacBookPro5",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"),
+               },
+       },
+       {       /* Handle problems with rebooting on Apple Macmini3,1 */
+               .callback = set_pci_reboot,
+               .ident = "Apple Macmini3,1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Macmini3,1"),
+               },
+       },
+       {       /* Handle problems with rebooting on the iMac9,1. */
+               .callback = set_pci_reboot,
+               .ident = "Apple iMac9,1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"),
+               },
+       },
+       {       /* Handle problems with rebooting on the Latitude E6220. */
+               .callback = set_pci_reboot,
+               .ident = "Dell Latitude E6220",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6220"),
+               },
+       },
+       {       /* Handle problems with rebooting on the Latitude E6520. */
+               .callback = set_pci_reboot,
+               .ident = "Dell Latitude E6520",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6520"),
+               },
+       },
+       { }
+};
+
+static int __init pci_reboot_init(void)
+{
+       dmi_check_system(pci_reboot_dmi_table);
+       return 0;
+}
+core_initcall(pci_reboot_init);
+
 static inline void kb_wait(void)
 {
        int i;
 
-       for (i = 0; i < 0x10000; i++)
-               if ((inb_p(0x64) & 0x02) == 0)
+       for (i = 0; i < 0x10000; i++) {
+               if ((inb(0x64) & 0x02) == 0)
                        break;
+               udelay(2);
+       }
 }
 
-void machine_emergency_restart(void)
+static void vmxoff_nmi(int cpu, struct die_args *args)
+{
+       cpu_emergency_vmxoff();
+}
+
+/* Use NMIs as IPIs to tell all CPUs to disable virtualization
+ */
+static void emergency_vmx_disable_all(void)
+{
+       /* Just make sure we won't change CPUs while doing this */
+       local_irq_disable();
+
+       /* We need to disable VMX on all CPUs before rebooting, otherwise
+        * we risk hanging up the machine, because the CPU ignore INIT
+        * signals when VMX is enabled.
+        *
+        * We can't take any locks and we may be on an inconsistent
+        * state, so we use NMIs as IPIs to tell the other CPUs to disable
+        * VMX and halt.
+        *
+        * For safety, we will avoid running the nmi_shootdown_cpus()
+        * stuff unnecessarily, but we don't have a way to check
+        * if other CPUs have VMX enabled. So we will call it only if the
+        * CPU we are running on has VMX enabled.
+        *
+        * We will miss cases where VMX is not enabled on all CPUs. This
+        * shouldn't do much harm because KVM always enable VMX on all
+        * CPUs anyway. But we can miss it on the small window where KVM
+        * is still enabling VMX.
+        */
+       if (cpu_has_vmx() && cpu_vmx_enabled()) {
+               /* Disable VMX on this CPU.
+                */
+               cpu_vmxoff();
+
+               /* Halt and disable VMX on the other CPUs */
+               nmi_shootdown_cpus(vmxoff_nmi);
+
+       }
+}
+
+
+void __attribute__((weak)) mach_reboot_fixups(void)
+{
+}
+
+static void native_machine_emergency_restart(void)
 {
        int i;
 
+       if (reboot_emergency)
+               emergency_vmx_disable_all();
+
+       tboot_shutdown(TB_SHUTDOWN_REBOOT);
+
        /* Tell the BIOS if we want cold or warm reboot */
        *((unsigned short *)__va(0x472)) = reboot_mode;
 
@@ -335,6 +579,8 @@ void machine_emergency_restart(void)
                /* Could also try the reset bit in the Hammer NB */
                switch (reboot_type) {
                case BOOT_KBD:
+                       mach_reboot_fixups(); /* for board specific fixups */
+
                        for (i = 0; i < 10; i++) {
                                kb_wait();
                                udelay(50);
@@ -343,7 +589,7 @@ void machine_emergency_restart(void)
                        }
 
                case BOOT_TRIPLE:
-                       load_idt((const struct desc_ptr *)&no_idt);
+                       load_idt(&no_idt);
                        __asm__ __volatile__("int3");
 
                        reboot_type = BOOT_KBD;
@@ -362,45 +608,59 @@ void machine_emergency_restart(void)
                        reboot_type = BOOT_KBD;
                        break;
 
-
                case BOOT_EFI:
                        if (efi_enabled)
-                               efi.reset_system(reboot_mode ? EFI_RESET_WARM : EFI_RESET_COLD,
+                               efi.reset_system(reboot_mode ?
+                                                EFI_RESET_WARM :
+                                                EFI_RESET_COLD,
                                                 EFI_SUCCESS, 0, NULL);
+                       reboot_type = BOOT_KBD;
+                       break;
 
+               case BOOT_CF9:
+                       port_cf9_safe = true;
+                       /* fall through */
+
+               case BOOT_CF9_COND:
+                       if (port_cf9_safe) {
+                               u8 cf9 = inb(0xcf9) & ~6;
+                               outb(cf9|2, 0xcf9); /* Request hard reset */
+                               udelay(50);
+                               outb(cf9|6, 0xcf9); /* Actually do the reset */
+                               udelay(50);
+                       }
                        reboot_type = BOOT_KBD;
                        break;
                }
        }
 }
 
-void machine_shutdown(void)
+void native_machine_shutdown(void)
 {
        /* Stop the cpus and apics */
 #ifdef CONFIG_SMP
-       int reboot_cpu_id;
 
        /* The boot cpu is always logical cpu 0 */
-       reboot_cpu_id = 0;
+       int reboot_cpu_id = 0;
 
 #ifdef CONFIG_X86_32
        /* 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))
+       if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) &&
+               cpu_online(reboot_cpu))
                reboot_cpu_id = reboot_cpu;
 #endif
 
        /* Make certain the cpu I'm about to reboot on is online */
-       if (!cpu_isset(reboot_cpu_id, cpu_online_map))
+       if (!cpu_online(reboot_cpu_id))
                reboot_cpu_id = smp_processor_id();
 
        /* Make certain I only run on the appropriate processor */
-       set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id));
+       set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id));
 
        /* O.K Now that I'm on the appropriate processor,
         * stop all of the others.
         */
-       smp_send_stop();
+       stop_other_cpus();
 #endif
 
        lapic_shutdown();
@@ -414,36 +674,177 @@ void machine_shutdown(void)
 #endif
 
 #ifdef CONFIG_X86_64
-       pci_iommu_shutdown();
+       x86_platform.iommu_shutdown();
 #endif
 }
 
-void machine_restart(char *__unused)
+static void __machine_emergency_restart(int emergency)
+{
+       reboot_emergency = emergency;
+       machine_ops.emergency_restart();
+}
+
+static void native_machine_restart(char *__unused)
 {
        printk("machine restart\n");
 
        if (!reboot_force)
                machine_shutdown();
-       machine_emergency_restart();
+       __machine_emergency_restart(0);
 }
 
-void machine_halt(void)
+static void native_machine_halt(void)
 {
+       /* stop other cpus and apics */
+       machine_shutdown();
+
+       tboot_shutdown(TB_SHUTDOWN_HALT);
+
+       /* stop this cpu */
+       stop_this_cpu(NULL);
 }
 
-void machine_power_off(void)
+static void native_machine_power_off(void)
 {
        if (pm_power_off) {
                if (!reboot_force)
                        machine_shutdown();
                pm_power_off();
        }
+       /* a fallback in case there is no PM info available */
+       tboot_shutdown(TB_SHUTDOWN_HALT);
 }
 
 struct machine_ops machine_ops = {
-       .power_off = machine_power_off,
-       .shutdown = machine_shutdown,
-       .emergency_restart = machine_emergency_restart,
-       .restart = machine_restart,
-       .halt = machine_halt
+       .power_off = native_machine_power_off,
+       .shutdown = native_machine_shutdown,
+       .emergency_restart = native_machine_emergency_restart,
+       .restart = native_machine_restart,
+       .halt = native_machine_halt,
+#ifdef CONFIG_KEXEC
+       .crash_shutdown = native_machine_crash_shutdown,
+#endif
 };
+
+void machine_power_off(void)
+{
+       machine_ops.power_off();
+}
+
+void machine_shutdown(void)
+{
+       machine_ops.shutdown();
+}
+
+void machine_emergency_restart(void)
+{
+       __machine_emergency_restart(1);
+}
+
+void machine_restart(char *cmd)
+{
+       machine_ops.restart(cmd);
+}
+
+void machine_halt(void)
+{
+       machine_ops.halt();
+}
+
+#ifdef CONFIG_KEXEC
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+       machine_ops.crash_shutdown(regs);
+}
+#endif
+
+
+#if defined(CONFIG_SMP)
+
+/* This keeps a track of which one is crashing cpu. */
+static int crashing_cpu;
+static nmi_shootdown_cb shootdown_callback;
+
+static atomic_t waiting_for_crash_ipi;
+
+static int crash_nmi_callback(struct notifier_block *self,
+                       unsigned long val, void *data)
+{
+       int cpu;
+
+       if (val != DIE_NMI)
+               return NOTIFY_OK;
+
+       cpu = raw_smp_processor_id();
+
+       /* Don't do anything if this handler is invoked on crashing cpu.
+        * Otherwise, system will completely hang. Crashing cpu can get
+        * an NMI if system was initially booted with nmi_watchdog parameter.
+        */
+       if (cpu == crashing_cpu)
+               return NOTIFY_STOP;
+       local_irq_disable();
+
+       shootdown_callback(cpu, (struct die_args *)data);
+
+       atomic_dec(&waiting_for_crash_ipi);
+       /* Assume hlt works */
+       halt();
+       for (;;)
+               cpu_relax();
+
+       return 1;
+}
+
+static void smp_send_nmi_allbutself(void)
+{
+       apic->send_IPI_allbutself(NMI_VECTOR);
+}
+
+static struct notifier_block crash_nmi_nb = {
+       .notifier_call = crash_nmi_callback,
+       /* we want to be the first one called */
+       .priority = NMI_LOCAL_HIGH_PRIOR+1,
+};
+
+/* Halt all other CPUs, calling the specified function on each of them
+ *
+ * This function can be used to halt all other CPUs on crash
+ * or emergency reboot time. The function passed as parameter
+ * will be called inside a NMI handler on all CPUs.
+ */
+void nmi_shootdown_cpus(nmi_shootdown_cb callback)
+{
+       unsigned long msecs;
+       local_irq_disable();
+
+       /* Make a note of crashing cpu. Will be used in NMI callback.*/
+       crashing_cpu = safe_smp_processor_id();
+
+       shootdown_callback = callback;
+
+       atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
+       /* Would it be better to replace the trap vector here? */
+       if (register_die_notifier(&crash_nmi_nb))
+               return;         /* return what? */
+       /* Ensure the new callback function is set before sending
+        * out the NMI
+        */
+       wmb();
+
+       smp_send_nmi_allbutself();
+
+       msecs = 1000; /* Wait at most a second for the other cpus to stop */
+       while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
+               mdelay(1);
+               msecs--;
+       }
+
+       /* Leave the nmi callback set */
+}
+#else /* !CONFIG_SMP */
+void nmi_shootdown_cpus(nmi_shootdown_cb callback)
+{
+       /* No other CPUs to shoot down */
+}
+#endif