14d022e35SMiguel Boton #include <linux/module.h> 24d022e35SMiguel Boton #include <linux/reboot.h> 34d022e35SMiguel Boton #include <linux/init.h> 44d022e35SMiguel Boton #include <linux/pm.h> 54d022e35SMiguel Boton #include <linux/efi.h> 66c6c51e4SPaul Mackerras #include <linux/dmi.h> 7d43c36dcSAlexey Dobriyan #include <linux/sched.h> 869575d38SShane Wang #include <linux/tboot.h> 9ca444564SJean Delvare #include <linux/delay.h> 104d022e35SMiguel Boton #include <acpi/reboot.h> 114d022e35SMiguel Boton #include <asm/io.h> 124d022e35SMiguel Boton #include <asm/apic.h> 134d022e35SMiguel Boton #include <asm/desc.h> 144d022e35SMiguel Boton #include <asm/hpet.h> 1568db065cSJeremy Fitzhardinge #include <asm/pgtable.h> 164412620fSDmitri Vorobiev #include <asm/proto.h> 174d022e35SMiguel Boton #include <asm/reboot_fixups.h> 184d022e35SMiguel Boton #include <asm/reboot.h> 1982487711SJaswinder Singh Rajput #include <asm/pci_x86.h> 20d176720dSEduardo Habkost #include <asm/virtext.h> 2196b89dc6SJaswinder Singh Rajput #include <asm/cpu.h> 22c410b830SDon Zickus #include <asm/nmi.h> 234d022e35SMiguel Boton 244d022e35SMiguel Boton #ifdef CONFIG_X86_32 254d022e35SMiguel Boton # include <linux/ctype.h> 264d022e35SMiguel Boton # include <linux/mc146818rtc.h> 274d022e35SMiguel Boton #else 28338bac52SFUJITA Tomonori # include <asm/x86_init.h> 294d022e35SMiguel Boton #endif 304d022e35SMiguel Boton 314d022e35SMiguel Boton /* 324d022e35SMiguel Boton * Power off function, if any 334d022e35SMiguel Boton */ 344d022e35SMiguel Boton void (*pm_power_off)(void); 354d022e35SMiguel Boton EXPORT_SYMBOL(pm_power_off); 364d022e35SMiguel Boton 37ebdd561aSJan Beulich static const struct desc_ptr no_idt = {}; 384d022e35SMiguel Boton static int reboot_mode; 39660e34ceSMatthew Garrett enum reboot_type reboot_type = BOOT_ACPI; 404d022e35SMiguel Boton int reboot_force; 414d022e35SMiguel Boton 424d022e35SMiguel Boton #if defined(CONFIG_X86_32) && defined(CONFIG_SMP) 434d022e35SMiguel Boton static int reboot_cpu = -1; 444d022e35SMiguel Boton #endif 454d022e35SMiguel Boton 46d176720dSEduardo Habkost /* This is set if we need to go through the 'emergency' path. 47d176720dSEduardo Habkost * When machine_emergency_restart() is called, we may be on 48d176720dSEduardo Habkost * an inconsistent state and won't be able to do a clean cleanup 49d176720dSEduardo Habkost */ 50d176720dSEduardo Habkost static int reboot_emergency; 51d176720dSEduardo Habkost 5214d7ca5cSH. Peter Anvin /* This is set by the PCI code if either type 1 or type 2 PCI is detected */ 5314d7ca5cSH. Peter Anvin bool port_cf9_safe = false; 5414d7ca5cSH. Peter Anvin 5514d7ca5cSH. Peter Anvin /* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci] 564d022e35SMiguel Boton warm Don't set the cold reboot flag 574d022e35SMiguel Boton cold Set the cold reboot flag 584d022e35SMiguel Boton bios Reboot by jumping through the BIOS (only for X86_32) 594d022e35SMiguel Boton smp Reboot by executing reset on BSP or other CPU (only for X86_32) 604d022e35SMiguel Boton triple Force a triple fault (init) 614d022e35SMiguel Boton kbd Use the keyboard controller. cold reset (default) 624d022e35SMiguel Boton acpi Use the RESET_REG in the FADT 634d022e35SMiguel Boton efi Use efi reset_system runtime service 6414d7ca5cSH. Peter Anvin pci Use the so-called "PCI reset register", CF9 654d022e35SMiguel Boton force Avoid anything that could hang. 664d022e35SMiguel Boton */ 674d022e35SMiguel Boton static int __init reboot_setup(char *str) 684d022e35SMiguel Boton { 694d022e35SMiguel Boton for (;;) { 704d022e35SMiguel Boton switch (*str) { 714d022e35SMiguel Boton case 'w': 724d022e35SMiguel Boton reboot_mode = 0x1234; 734d022e35SMiguel Boton break; 744d022e35SMiguel Boton 754d022e35SMiguel Boton case 'c': 764d022e35SMiguel Boton reboot_mode = 0; 774d022e35SMiguel Boton break; 784d022e35SMiguel Boton 794d022e35SMiguel Boton #ifdef CONFIG_X86_32 804d022e35SMiguel Boton #ifdef CONFIG_SMP 814d022e35SMiguel Boton case 's': 824d022e35SMiguel Boton if (isdigit(*(str+1))) { 834d022e35SMiguel Boton reboot_cpu = (int) (*(str+1) - '0'); 844d022e35SMiguel Boton if (isdigit(*(str+2))) 854d022e35SMiguel Boton reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0'); 864d022e35SMiguel Boton } 874d022e35SMiguel Boton /* we will leave sorting out the final value 884d022e35SMiguel Boton when we are ready to reboot, since we might not 89f6e9456cSRobert Richter have detected BSP APIC ID or smp_num_cpu */ 904d022e35SMiguel Boton break; 914d022e35SMiguel Boton #endif /* CONFIG_SMP */ 924d022e35SMiguel Boton 934d022e35SMiguel Boton case 'b': 944d022e35SMiguel Boton #endif 954d022e35SMiguel Boton case 'a': 964d022e35SMiguel Boton case 'k': 974d022e35SMiguel Boton case 't': 984d022e35SMiguel Boton case 'e': 9914d7ca5cSH. Peter Anvin case 'p': 1004d022e35SMiguel Boton reboot_type = *str; 1014d022e35SMiguel Boton break; 1024d022e35SMiguel Boton 1034d022e35SMiguel Boton case 'f': 1044d022e35SMiguel Boton reboot_force = 1; 1054d022e35SMiguel Boton break; 1064d022e35SMiguel Boton } 1074d022e35SMiguel Boton 1084d022e35SMiguel Boton str = strchr(str, ','); 1094d022e35SMiguel Boton if (str) 1104d022e35SMiguel Boton str++; 1114d022e35SMiguel Boton else 1124d022e35SMiguel Boton break; 1134d022e35SMiguel Boton } 1144d022e35SMiguel Boton return 1; 1154d022e35SMiguel Boton } 1164d022e35SMiguel Boton 1174d022e35SMiguel Boton __setup("reboot=", reboot_setup); 1184d022e35SMiguel Boton 1194d022e35SMiguel Boton 1204d022e35SMiguel Boton #ifdef CONFIG_X86_32 1214d022e35SMiguel Boton /* 1224d022e35SMiguel Boton * Reboot options and system auto-detection code provided by 1234d022e35SMiguel Boton * Dell Inc. so their systems "just work". :-) 1244d022e35SMiguel Boton */ 1254d022e35SMiguel Boton 1264d022e35SMiguel Boton /* 1274d022e35SMiguel Boton * Some machines require the "reboot=b" commandline option, 1284d022e35SMiguel Boton * this quirk makes that automatic. 1294d022e35SMiguel Boton */ 1304d022e35SMiguel Boton static int __init set_bios_reboot(const struct dmi_system_id *d) 1314d022e35SMiguel Boton { 1324d022e35SMiguel Boton if (reboot_type != BOOT_BIOS) { 1334d022e35SMiguel Boton reboot_type = BOOT_BIOS; 1344d022e35SMiguel Boton printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident); 1354d022e35SMiguel Boton } 1364d022e35SMiguel Boton return 0; 1374d022e35SMiguel Boton } 1384d022e35SMiguel Boton 1394d022e35SMiguel Boton static struct dmi_system_id __initdata reboot_dmi_table[] = { 1404d022e35SMiguel Boton { /* Handle problems with rebooting on Dell E520's */ 1414d022e35SMiguel Boton .callback = set_bios_reboot, 1424d022e35SMiguel Boton .ident = "Dell E520", 1434d022e35SMiguel Boton .matches = { 1444d022e35SMiguel Boton DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1454d022e35SMiguel Boton DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"), 1464d022e35SMiguel Boton }, 1474d022e35SMiguel Boton }, 1484d022e35SMiguel Boton { /* Handle problems with rebooting on Dell 1300's */ 1494d022e35SMiguel Boton .callback = set_bios_reboot, 1504d022e35SMiguel Boton .ident = "Dell PowerEdge 1300", 1514d022e35SMiguel Boton .matches = { 1524d022e35SMiguel Boton DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 1534d022e35SMiguel Boton DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"), 1544d022e35SMiguel Boton }, 1554d022e35SMiguel Boton }, 1564d022e35SMiguel Boton { /* Handle problems with rebooting on Dell 300's */ 1574d022e35SMiguel Boton .callback = set_bios_reboot, 1584d022e35SMiguel Boton .ident = "Dell PowerEdge 300", 1594d022e35SMiguel Boton .matches = { 1604d022e35SMiguel Boton DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 1614d022e35SMiguel Boton DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"), 1624d022e35SMiguel Boton }, 1634d022e35SMiguel Boton }, 1644d022e35SMiguel Boton { /* Handle problems with rebooting on Dell Optiplex 745's SFF*/ 1654d022e35SMiguel Boton .callback = set_bios_reboot, 1664d022e35SMiguel Boton .ident = "Dell OptiPlex 745", 1674d022e35SMiguel Boton .matches = { 1684d022e35SMiguel Boton DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 1694d022e35SMiguel Boton DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), 1704d022e35SMiguel Boton }, 1714d022e35SMiguel Boton }, 172fc115bf1SColeman Kane { /* Handle problems with rebooting on Dell Optiplex 745's DFF*/ 173fc115bf1SColeman Kane .callback = set_bios_reboot, 174fc115bf1SColeman Kane .ident = "Dell OptiPlex 745", 175fc115bf1SColeman Kane .matches = { 176fc115bf1SColeman Kane DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 177fc115bf1SColeman Kane DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), 178fc115bf1SColeman Kane DMI_MATCH(DMI_BOARD_NAME, "0MM599"), 179fc115bf1SColeman Kane }, 180fc115bf1SColeman Kane }, 181fc1c8925SHeinz-Ado Arnolds { /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */ 182fc1c8925SHeinz-Ado Arnolds .callback = set_bios_reboot, 183fc1c8925SHeinz-Ado Arnolds .ident = "Dell OptiPlex 745", 184fc1c8925SHeinz-Ado Arnolds .matches = { 185fc1c8925SHeinz-Ado Arnolds DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 186fc1c8925SHeinz-Ado Arnolds DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), 187fc1c8925SHeinz-Ado Arnolds DMI_MATCH(DMI_BOARD_NAME, "0KW626"), 188fc1c8925SHeinz-Ado Arnolds }, 189fc1c8925SHeinz-Ado Arnolds }, 190093bac15SSteve Conklin { /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */ 191093bac15SSteve Conklin .callback = set_bios_reboot, 192093bac15SSteve Conklin .ident = "Dell OptiPlex 330", 193093bac15SSteve Conklin .matches = { 194093bac15SSteve Conklin DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 195093bac15SSteve Conklin DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"), 196093bac15SSteve Conklin DMI_MATCH(DMI_BOARD_NAME, "0KP561"), 197093bac15SSteve Conklin }, 198093bac15SSteve Conklin }, 1994a4aca64SJean Delvare { /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */ 2004a4aca64SJean Delvare .callback = set_bios_reboot, 2014a4aca64SJean Delvare .ident = "Dell OptiPlex 360", 2024a4aca64SJean Delvare .matches = { 2034a4aca64SJean Delvare DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 2044a4aca64SJean Delvare DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 360"), 2054a4aca64SJean Delvare DMI_MATCH(DMI_BOARD_NAME, "0T656F"), 2064a4aca64SJean Delvare }, 2074a4aca64SJean Delvare }, 20835ea63d7SLeann Ogasawara { /* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G*/ 20935ea63d7SLeann Ogasawara .callback = set_bios_reboot, 21035ea63d7SLeann Ogasawara .ident = "Dell OptiPlex 760", 21135ea63d7SLeann Ogasawara .matches = { 21235ea63d7SLeann Ogasawara DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 21335ea63d7SLeann Ogasawara DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 760"), 21435ea63d7SLeann Ogasawara DMI_MATCH(DMI_BOARD_NAME, "0G919G"), 21535ea63d7SLeann Ogasawara }, 21635ea63d7SLeann Ogasawara }, 2174d022e35SMiguel Boton { /* Handle problems with rebooting on Dell 2400's */ 2184d022e35SMiguel Boton .callback = set_bios_reboot, 2194d022e35SMiguel Boton .ident = "Dell PowerEdge 2400", 2204d022e35SMiguel Boton .matches = { 2214d022e35SMiguel Boton DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 2224d022e35SMiguel Boton DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"), 2234d022e35SMiguel Boton }, 2244d022e35SMiguel Boton }, 225fab3b58dSIngo Molnar { /* Handle problems with rebooting on Dell T5400's */ 226fab3b58dSIngo Molnar .callback = set_bios_reboot, 227fab3b58dSIngo Molnar .ident = "Dell Precision T5400", 228fab3b58dSIngo Molnar .matches = { 229fab3b58dSIngo Molnar DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 230fab3b58dSIngo Molnar DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T5400"), 231fab3b58dSIngo Molnar }, 232fab3b58dSIngo Molnar }, 233890ffedcSThomas Backlund { /* Handle problems with rebooting on Dell T7400's */ 234890ffedcSThomas Backlund .callback = set_bios_reboot, 235890ffedcSThomas Backlund .ident = "Dell Precision T7400", 236890ffedcSThomas Backlund .matches = { 237890ffedcSThomas Backlund DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 238890ffedcSThomas Backlund DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T7400"), 239890ffedcSThomas Backlund }, 240890ffedcSThomas Backlund }, 2414d022e35SMiguel Boton { /* Handle problems with rebooting on HP laptops */ 2424d022e35SMiguel Boton .callback = set_bios_reboot, 2434d022e35SMiguel Boton .ident = "HP Compaq Laptop", 2444d022e35SMiguel Boton .matches = { 2454d022e35SMiguel Boton DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 2464d022e35SMiguel Boton DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"), 2474d022e35SMiguel Boton }, 2484d022e35SMiguel Boton }, 249dd4124a8SLeann Ogasawara { /* Handle problems with rebooting on Dell XPS710 */ 250dd4124a8SLeann Ogasawara .callback = set_bios_reboot, 251dd4124a8SLeann Ogasawara .ident = "Dell XPS710", 252dd4124a8SLeann Ogasawara .matches = { 253dd4124a8SLeann Ogasawara DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 254dd4124a8SLeann Ogasawara DMI_MATCH(DMI_PRODUCT_NAME, "Dell XPS710"), 255dd4124a8SLeann Ogasawara }, 256dd4124a8SLeann Ogasawara }, 257c5da9a2bSAlan Cox { /* Handle problems with rebooting on Dell DXP061 */ 258c5da9a2bSAlan Cox .callback = set_bios_reboot, 259c5da9a2bSAlan Cox .ident = "Dell DXP061", 260c5da9a2bSAlan Cox .matches = { 261c5da9a2bSAlan Cox DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 262c5da9a2bSAlan Cox DMI_MATCH(DMI_PRODUCT_NAME, "Dell DXP061"), 263c5da9a2bSAlan Cox }, 264c5da9a2bSAlan Cox }, 26588dff493SZhang Rui { /* Handle problems with rebooting on Sony VGN-Z540N */ 26688dff493SZhang Rui .callback = set_bios_reboot, 26788dff493SZhang Rui .ident = "Sony VGN-Z540N", 26888dff493SZhang Rui .matches = { 26988dff493SZhang Rui DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), 27088dff493SZhang Rui DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"), 27188dff493SZhang Rui }, 27288dff493SZhang Rui }, 27377f32dfdSDenis Turischev { /* Handle problems with rebooting on CompuLab SBC-FITPC2 */ 27477f32dfdSDenis Turischev .callback = set_bios_reboot, 27577f32dfdSDenis Turischev .ident = "CompuLab SBC-FITPC2", 27677f32dfdSDenis Turischev .matches = { 27777f32dfdSDenis Turischev DMI_MATCH(DMI_SYS_VENDOR, "CompuLab"), 27877f32dfdSDenis Turischev DMI_MATCH(DMI_PRODUCT_NAME, "SBC-FITPC2"), 27977f32dfdSDenis Turischev }, 28077f32dfdSDenis Turischev }, 2814832dddaSLeann Ogasawara { /* Handle problems with rebooting on ASUS P4S800 */ 2824832dddaSLeann Ogasawara .callback = set_bios_reboot, 2834832dddaSLeann Ogasawara .ident = "ASUS P4S800", 2844832dddaSLeann Ogasawara .matches = { 2854832dddaSLeann Ogasawara DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), 2864832dddaSLeann Ogasawara DMI_MATCH(DMI_BOARD_NAME, "P4S800"), 2874832dddaSLeann Ogasawara }, 2884832dddaSLeann Ogasawara }, 289e19e074bSKushal Koolwal { /* Handle problems with rebooting on VersaLogic Menlow boards */ 290e19e074bSKushal Koolwal .callback = set_bios_reboot, 291e19e074bSKushal Koolwal .ident = "VersaLogic Menlow based board", 292e19e074bSKushal Koolwal .matches = { 293e19e074bSKushal Koolwal DMI_MATCH(DMI_BOARD_VENDOR, "VersaLogic Corporation"), 294e19e074bSKushal Koolwal DMI_MATCH(DMI_BOARD_NAME, "VersaLogic Menlow board"), 295e19e074bSKushal Koolwal }, 296e19e074bSKushal Koolwal }, 297b49c78d4SPeter Chubb { /* Handle reboot issue on Acer Aspire one */ 298b49c78d4SPeter Chubb .callback = set_bios_reboot, 299b49c78d4SPeter Chubb .ident = "Acer Aspire One A110", 300b49c78d4SPeter Chubb .matches = { 301b49c78d4SPeter Chubb DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 302b49c78d4SPeter Chubb DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"), 303b49c78d4SPeter Chubb }, 304b49c78d4SPeter Chubb }, 3054d022e35SMiguel Boton { } 3064d022e35SMiguel Boton }; 3074d022e35SMiguel Boton 3084d022e35SMiguel Boton static int __init reboot_init(void) 3094d022e35SMiguel Boton { 3104d022e35SMiguel Boton dmi_check_system(reboot_dmi_table); 3114d022e35SMiguel Boton return 0; 3124d022e35SMiguel Boton } 3134d022e35SMiguel Boton core_initcall(reboot_init); 3144d022e35SMiguel Boton 3153d35ac34SH. Peter Anvin extern const unsigned char machine_real_restart_asm[]; 3163d35ac34SH. Peter Anvin extern const u64 machine_real_restart_gdt[3]; 3173d35ac34SH. Peter Anvin 3183d35ac34SH. Peter Anvin void machine_real_restart(unsigned int type) 3194d022e35SMiguel Boton { 3203d35ac34SH. Peter Anvin void *restart_va; 3213d35ac34SH. Peter Anvin unsigned long restart_pa; 3223d35ac34SH. Peter Anvin void (*restart_lowmem)(unsigned int); 3233d35ac34SH. Peter Anvin u64 *lowmem_gdt; 3244d022e35SMiguel Boton 3254d022e35SMiguel Boton local_irq_disable(); 3264d022e35SMiguel Boton 3274d022e35SMiguel Boton /* Write zero to CMOS register number 0x0f, which the BIOS POST 3284d022e35SMiguel Boton routine will recognize as telling it to do a proper reboot. (Well 3294d022e35SMiguel Boton that's what this book in front of me says -- it may only apply to 3304d022e35SMiguel Boton the Phoenix BIOS though, it's not clear). At the same time, 3314d022e35SMiguel Boton disable NMIs by setting the top bit in the CMOS address register, 3324d022e35SMiguel Boton as we're about to do peculiar things to the CPU. I'm not sure if 3334d022e35SMiguel Boton `outb_p' is needed instead of just `outb'. Use it to be on the 3344d022e35SMiguel Boton safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.) 3354d022e35SMiguel Boton */ 3364d022e35SMiguel Boton spin_lock(&rtc_lock); 3374d022e35SMiguel Boton CMOS_WRITE(0x00, 0x8f); 3384d022e35SMiguel Boton spin_unlock(&rtc_lock); 3394d022e35SMiguel Boton 3404d022e35SMiguel Boton /* 341b40827faSBorislav Petkov * Switch back to the initial page table. 3424d022e35SMiguel Boton */ 343b40827faSBorislav Petkov load_cr3(initial_page_table); 3444d022e35SMiguel Boton 3454d022e35SMiguel Boton /* Write 0x1234 to absolute memory location 0x472. The BIOS reads 3464d022e35SMiguel Boton this on booting to tell it to "Bypass memory test (also warm 3474d022e35SMiguel Boton boot)". This seems like a fairly standard thing that gets set by 3484d022e35SMiguel Boton REBOOT.COM programs, and the previous reset routine did this 3494d022e35SMiguel Boton too. */ 3504d022e35SMiguel Boton *((unsigned short *)0x472) = reboot_mode; 3514d022e35SMiguel Boton 3523d35ac34SH. Peter Anvin /* Patch the GDT in the low memory trampoline */ 3533d35ac34SH. Peter Anvin lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt); 3544d022e35SMiguel Boton 3553d35ac34SH. Peter Anvin restart_va = TRAMPOLINE_SYM(machine_real_restart_asm); 3563d35ac34SH. Peter Anvin restart_pa = virt_to_phys(restart_va); 3573d35ac34SH. Peter Anvin restart_lowmem = (void (*)(unsigned int))restart_pa; 3584d022e35SMiguel Boton 3593d35ac34SH. Peter Anvin /* GDT[0]: GDT self-pointer */ 3603d35ac34SH. Peter Anvin lowmem_gdt[0] = 3613d35ac34SH. Peter Anvin (u64)(sizeof(machine_real_restart_gdt) - 1) + 3623d35ac34SH. Peter Anvin ((u64)virt_to_phys(lowmem_gdt) << 16); 3633d35ac34SH. Peter Anvin /* GDT[1]: 64K real mode code segment */ 3643d35ac34SH. Peter Anvin lowmem_gdt[1] = 3653d35ac34SH. Peter Anvin GDT_ENTRY(0x009b, restart_pa, 0xffff); 3664d022e35SMiguel Boton 3673d35ac34SH. Peter Anvin /* Jump to the identity-mapped low memory code */ 3683d35ac34SH. Peter Anvin restart_lowmem(type); 3694d022e35SMiguel Boton } 3704d022e35SMiguel Boton #ifdef CONFIG_APM_MODULE 3714d022e35SMiguel Boton EXPORT_SYMBOL(machine_real_restart); 3724d022e35SMiguel Boton #endif 3734d022e35SMiguel Boton 3744d022e35SMiguel Boton #endif /* CONFIG_X86_32 */ 3754d022e35SMiguel Boton 3766c6c51e4SPaul Mackerras /* 377498cdbfbSOzan Çağlayan * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot 3786c6c51e4SPaul Mackerras */ 3796c6c51e4SPaul Mackerras static int __init set_pci_reboot(const struct dmi_system_id *d) 3806c6c51e4SPaul Mackerras { 3816c6c51e4SPaul Mackerras if (reboot_type != BOOT_CF9) { 3826c6c51e4SPaul Mackerras reboot_type = BOOT_CF9; 3836c6c51e4SPaul Mackerras printk(KERN_INFO "%s series board detected. " 3846c6c51e4SPaul Mackerras "Selecting PCI-method for reboots.\n", d->ident); 3856c6c51e4SPaul Mackerras } 3866c6c51e4SPaul Mackerras return 0; 3876c6c51e4SPaul Mackerras } 3886c6c51e4SPaul Mackerras 3896c6c51e4SPaul Mackerras static struct dmi_system_id __initdata pci_reboot_dmi_table[] = { 3903e03bbeaSShunichi Fuji { /* Handle problems with rebooting on Apple MacBook5 */ 3916c6c51e4SPaul Mackerras .callback = set_pci_reboot, 3923e03bbeaSShunichi Fuji .ident = "Apple MacBook5", 3936c6c51e4SPaul Mackerras .matches = { 3946c6c51e4SPaul Mackerras DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), 3953e03bbeaSShunichi Fuji DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"), 3966c6c51e4SPaul Mackerras }, 3976c6c51e4SPaul Mackerras }, 3983e03bbeaSShunichi Fuji { /* Handle problems with rebooting on Apple MacBookPro5 */ 399498cdbfbSOzan Çağlayan .callback = set_pci_reboot, 4003e03bbeaSShunichi Fuji .ident = "Apple MacBookPro5", 401498cdbfbSOzan Çağlayan .matches = { 402498cdbfbSOzan Çağlayan DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), 4033e03bbeaSShunichi Fuji DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"), 404498cdbfbSOzan Çağlayan }, 405498cdbfbSOzan Çağlayan }, 40605154752SGottfried Haider { /* Handle problems with rebooting on Apple Macmini3,1 */ 40705154752SGottfried Haider .callback = set_pci_reboot, 40805154752SGottfried Haider .ident = "Apple Macmini3,1", 40905154752SGottfried Haider .matches = { 41005154752SGottfried Haider DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), 41105154752SGottfried Haider DMI_MATCH(DMI_PRODUCT_NAME, "Macmini3,1"), 41205154752SGottfried Haider }, 41305154752SGottfried Haider }, 4140a832320SJustin P. Mattock { /* Handle problems with rebooting on the iMac9,1. */ 4150a832320SJustin P. Mattock .callback = set_pci_reboot, 4160a832320SJustin P. Mattock .ident = "Apple iMac9,1", 4170a832320SJustin P. Mattock .matches = { 4180a832320SJustin P. Mattock DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), 4190a832320SJustin P. Mattock DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"), 4200a832320SJustin P. Mattock }, 4210a832320SJustin P. Mattock }, 4223628c3f5SMaxime Ripard { /* Handle problems with rebooting on the Latitude E6320. */ 4233628c3f5SMaxime Ripard .callback = set_pci_reboot, 4243628c3f5SMaxime Ripard .ident = "Dell Latitude E6320", 4253628c3f5SMaxime Ripard .matches = { 4263628c3f5SMaxime Ripard DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 4273628c3f5SMaxime Ripard DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6320"), 4283628c3f5SMaxime Ripard }, 4293628c3f5SMaxime Ripard }, 430b7798d28SDaniel J Blueman { /* Handle problems with rebooting on the Latitude E5420. */ 431b7798d28SDaniel J Blueman .callback = set_pci_reboot, 432b7798d28SDaniel J Blueman .ident = "Dell Latitude E5420", 433b7798d28SDaniel J Blueman .matches = { 434b7798d28SDaniel J Blueman DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 435b7798d28SDaniel J Blueman DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E5420"), 436b7798d28SDaniel J Blueman }, 437b7798d28SDaniel J Blueman }, 438a536877eSH. Peter Anvin { /* Handle problems with rebooting on the Latitude E6420. */ 439a536877eSH. Peter Anvin .callback = set_pci_reboot, 440a536877eSH. Peter Anvin .ident = "Dell Latitude E6420", 441a536877eSH. Peter Anvin .matches = { 442a536877eSH. Peter Anvin DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 443a536877eSH. Peter Anvin DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6420"), 444a536877eSH. Peter Anvin }, 445a536877eSH. Peter Anvin }, 446*6be30bb7SRafael J. Wysocki { /* Handle problems with rebooting on the OptiPlex 990. */ 447*6be30bb7SRafael J. Wysocki .callback = set_pci_reboot, 448*6be30bb7SRafael J. Wysocki .ident = "Dell OptiPlex 990", 449*6be30bb7SRafael J. Wysocki .matches = { 450*6be30bb7SRafael J. Wysocki DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 451*6be30bb7SRafael J. Wysocki DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"), 452*6be30bb7SRafael J. Wysocki }, 453*6be30bb7SRafael J. Wysocki }, 4546c6c51e4SPaul Mackerras { } 4556c6c51e4SPaul Mackerras }; 4566c6c51e4SPaul Mackerras 4576c6c51e4SPaul Mackerras static int __init pci_reboot_init(void) 4586c6c51e4SPaul Mackerras { 4596c6c51e4SPaul Mackerras dmi_check_system(pci_reboot_dmi_table); 4606c6c51e4SPaul Mackerras return 0; 4616c6c51e4SPaul Mackerras } 4626c6c51e4SPaul Mackerras core_initcall(pci_reboot_init); 4636c6c51e4SPaul Mackerras 4644d022e35SMiguel Boton static inline void kb_wait(void) 4654d022e35SMiguel Boton { 4664d022e35SMiguel Boton int i; 4674d022e35SMiguel Boton 468c84d6af8SAlan Cox for (i = 0; i < 0x10000; i++) { 469c84d6af8SAlan Cox if ((inb(0x64) & 0x02) == 0) 4704d022e35SMiguel Boton break; 471c84d6af8SAlan Cox udelay(2); 472c84d6af8SAlan Cox } 4734d022e35SMiguel Boton } 4744d022e35SMiguel Boton 4759c48f1c6SDon Zickus static void vmxoff_nmi(int cpu, struct pt_regs *regs) 476d176720dSEduardo Habkost { 477d176720dSEduardo Habkost cpu_emergency_vmxoff(); 478d176720dSEduardo Habkost } 479d176720dSEduardo Habkost 480d176720dSEduardo Habkost /* Use NMIs as IPIs to tell all CPUs to disable virtualization 481d176720dSEduardo Habkost */ 482d176720dSEduardo Habkost static void emergency_vmx_disable_all(void) 483d176720dSEduardo Habkost { 484d176720dSEduardo Habkost /* Just make sure we won't change CPUs while doing this */ 485d176720dSEduardo Habkost local_irq_disable(); 486d176720dSEduardo Habkost 487d176720dSEduardo Habkost /* We need to disable VMX on all CPUs before rebooting, otherwise 488d176720dSEduardo Habkost * we risk hanging up the machine, because the CPU ignore INIT 489d176720dSEduardo Habkost * signals when VMX is enabled. 490d176720dSEduardo Habkost * 491d176720dSEduardo Habkost * We can't take any locks and we may be on an inconsistent 492d176720dSEduardo Habkost * state, so we use NMIs as IPIs to tell the other CPUs to disable 493d176720dSEduardo Habkost * VMX and halt. 494d176720dSEduardo Habkost * 495d176720dSEduardo Habkost * For safety, we will avoid running the nmi_shootdown_cpus() 496d176720dSEduardo Habkost * stuff unnecessarily, but we don't have a way to check 497d176720dSEduardo Habkost * if other CPUs have VMX enabled. So we will call it only if the 498d176720dSEduardo Habkost * CPU we are running on has VMX enabled. 499d176720dSEduardo Habkost * 500d176720dSEduardo Habkost * We will miss cases where VMX is not enabled on all CPUs. This 501d176720dSEduardo Habkost * shouldn't do much harm because KVM always enable VMX on all 502d176720dSEduardo Habkost * CPUs anyway. But we can miss it on the small window where KVM 503d176720dSEduardo Habkost * is still enabling VMX. 504d176720dSEduardo Habkost */ 505d176720dSEduardo Habkost if (cpu_has_vmx() && cpu_vmx_enabled()) { 506d176720dSEduardo Habkost /* Disable VMX on this CPU. 507d176720dSEduardo Habkost */ 508d176720dSEduardo Habkost cpu_vmxoff(); 509d176720dSEduardo Habkost 510d176720dSEduardo Habkost /* Halt and disable VMX on the other CPUs */ 511d176720dSEduardo Habkost nmi_shootdown_cpus(vmxoff_nmi); 512d176720dSEduardo Habkost 513d176720dSEduardo Habkost } 514d176720dSEduardo Habkost } 515d176720dSEduardo Habkost 516d176720dSEduardo Habkost 5177432d149SIngo Molnar void __attribute__((weak)) mach_reboot_fixups(void) 5187432d149SIngo Molnar { 5197432d149SIngo Molnar } 5207432d149SIngo Molnar 521660e34ceSMatthew Garrett /* 522660e34ceSMatthew Garrett * Windows compatible x86 hardware expects the following on reboot: 523660e34ceSMatthew Garrett * 524660e34ceSMatthew Garrett * 1) If the FADT has the ACPI reboot register flag set, try it 525660e34ceSMatthew Garrett * 2) If still alive, write to the keyboard controller 526660e34ceSMatthew Garrett * 3) If still alive, write to the ACPI reboot register again 527660e34ceSMatthew Garrett * 4) If still alive, write to the keyboard controller again 528660e34ceSMatthew Garrett * 529660e34ceSMatthew Garrett * If the machine is still alive at this stage, it gives up. We default to 530660e34ceSMatthew Garrett * following the same pattern, except that if we're still alive after (4) we'll 531660e34ceSMatthew Garrett * try to force a triple fault and then cycle between hitting the keyboard 532660e34ceSMatthew Garrett * controller and doing that 533660e34ceSMatthew Garrett */ 534416e2d63SJody Belka static void native_machine_emergency_restart(void) 5354d022e35SMiguel Boton { 5364d022e35SMiguel Boton int i; 537660e34ceSMatthew Garrett int attempt = 0; 538660e34ceSMatthew Garrett int orig_reboot_type = reboot_type; 5394d022e35SMiguel Boton 540d176720dSEduardo Habkost if (reboot_emergency) 541d176720dSEduardo Habkost emergency_vmx_disable_all(); 542d176720dSEduardo Habkost 543840c2bafSJoseph Cihula tboot_shutdown(TB_SHUTDOWN_REBOOT); 544840c2bafSJoseph Cihula 5454d022e35SMiguel Boton /* Tell the BIOS if we want cold or warm reboot */ 5464d022e35SMiguel Boton *((unsigned short *)__va(0x472)) = reboot_mode; 5474d022e35SMiguel Boton 5484d022e35SMiguel Boton for (;;) { 5494d022e35SMiguel Boton /* Could also try the reset bit in the Hammer NB */ 5504d022e35SMiguel Boton switch (reboot_type) { 5514d022e35SMiguel Boton case BOOT_KBD: 5527432d149SIngo Molnar mach_reboot_fixups(); /* for board specific fixups */ 5537432d149SIngo Molnar 5544d022e35SMiguel Boton for (i = 0; i < 10; i++) { 5554d022e35SMiguel Boton kb_wait(); 5564d022e35SMiguel Boton udelay(50); 5574d022e35SMiguel Boton outb(0xfe, 0x64); /* pulse reset low */ 5584d022e35SMiguel Boton udelay(50); 5594d022e35SMiguel Boton } 560660e34ceSMatthew Garrett if (attempt == 0 && orig_reboot_type == BOOT_ACPI) { 561660e34ceSMatthew Garrett attempt = 1; 562660e34ceSMatthew Garrett reboot_type = BOOT_ACPI; 563660e34ceSMatthew Garrett } else { 564660e34ceSMatthew Garrett reboot_type = BOOT_TRIPLE; 565660e34ceSMatthew Garrett } 566660e34ceSMatthew Garrett break; 5674d022e35SMiguel Boton 5684d022e35SMiguel Boton case BOOT_TRIPLE: 569ebdd561aSJan Beulich load_idt(&no_idt); 5704d022e35SMiguel Boton __asm__ __volatile__("int3"); 5714d022e35SMiguel Boton 5724d022e35SMiguel Boton reboot_type = BOOT_KBD; 5734d022e35SMiguel Boton break; 5744d022e35SMiguel Boton 5754d022e35SMiguel Boton #ifdef CONFIG_X86_32 5764d022e35SMiguel Boton case BOOT_BIOS: 5773d35ac34SH. Peter Anvin machine_real_restart(MRR_BIOS); 5784d022e35SMiguel Boton 5794d022e35SMiguel Boton reboot_type = BOOT_KBD; 5804d022e35SMiguel Boton break; 5814d022e35SMiguel Boton #endif 5824d022e35SMiguel Boton 5834d022e35SMiguel Boton case BOOT_ACPI: 5844d022e35SMiguel Boton acpi_reboot(); 5854d022e35SMiguel Boton reboot_type = BOOT_KBD; 5864d022e35SMiguel Boton break; 5874d022e35SMiguel Boton 5884d022e35SMiguel Boton case BOOT_EFI: 5894d022e35SMiguel Boton if (efi_enabled) 59014d7ca5cSH. Peter Anvin efi.reset_system(reboot_mode ? 59114d7ca5cSH. Peter Anvin EFI_RESET_WARM : 59214d7ca5cSH. Peter Anvin EFI_RESET_COLD, 5934d022e35SMiguel Boton EFI_SUCCESS, 0, NULL); 594b47b9288SH. Peter Anvin reboot_type = BOOT_KBD; 59514d7ca5cSH. Peter Anvin break; 5964d022e35SMiguel Boton 59714d7ca5cSH. Peter Anvin case BOOT_CF9: 59814d7ca5cSH. Peter Anvin port_cf9_safe = true; 59914d7ca5cSH. Peter Anvin /* fall through */ 60014d7ca5cSH. Peter Anvin 60114d7ca5cSH. Peter Anvin case BOOT_CF9_COND: 60214d7ca5cSH. Peter Anvin if (port_cf9_safe) { 60314d7ca5cSH. Peter Anvin u8 cf9 = inb(0xcf9) & ~6; 60414d7ca5cSH. Peter Anvin outb(cf9|2, 0xcf9); /* Request hard reset */ 60514d7ca5cSH. Peter Anvin udelay(50); 60614d7ca5cSH. Peter Anvin outb(cf9|6, 0xcf9); /* Actually do the reset */ 60714d7ca5cSH. Peter Anvin udelay(50); 60814d7ca5cSH. Peter Anvin } 6094d022e35SMiguel Boton reboot_type = BOOT_KBD; 6104d022e35SMiguel Boton break; 6114d022e35SMiguel Boton } 6124d022e35SMiguel Boton } 6134d022e35SMiguel Boton } 6144d022e35SMiguel Boton 6153c62c625SGlauber Costa void native_machine_shutdown(void) 6164d022e35SMiguel Boton { 6174d022e35SMiguel Boton /* Stop the cpus and apics */ 6184d022e35SMiguel Boton #ifdef CONFIG_SMP 6194d022e35SMiguel Boton 6204d022e35SMiguel Boton /* The boot cpu is always logical cpu 0 */ 62165c01184SMike Travis int reboot_cpu_id = 0; 6224d022e35SMiguel Boton 6234d022e35SMiguel Boton #ifdef CONFIG_X86_32 6244d022e35SMiguel Boton /* See if there has been given a command line override */ 6259628937dSMike Travis if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) && 6260bc3cc03SMike Travis cpu_online(reboot_cpu)) 6274d022e35SMiguel Boton reboot_cpu_id = reboot_cpu; 6284d022e35SMiguel Boton #endif 6294d022e35SMiguel Boton 6304d022e35SMiguel Boton /* Make certain the cpu I'm about to reboot on is online */ 6310bc3cc03SMike Travis if (!cpu_online(reboot_cpu_id)) 6324d022e35SMiguel Boton reboot_cpu_id = smp_processor_id(); 6334d022e35SMiguel Boton 6344d022e35SMiguel Boton /* Make certain I only run on the appropriate processor */ 6359628937dSMike Travis set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id)); 6364d022e35SMiguel Boton 6374d022e35SMiguel Boton /* O.K Now that I'm on the appropriate processor, 6384d022e35SMiguel Boton * stop all of the others. 6394d022e35SMiguel Boton */ 64076fac077SAlok Kataria stop_other_cpus(); 6414d022e35SMiguel Boton #endif 6424d022e35SMiguel Boton 6434d022e35SMiguel Boton lapic_shutdown(); 6444d022e35SMiguel Boton 6454d022e35SMiguel Boton #ifdef CONFIG_X86_IO_APIC 6464d022e35SMiguel Boton disable_IO_APIC(); 6474d022e35SMiguel Boton #endif 6484d022e35SMiguel Boton 6494d022e35SMiguel Boton #ifdef CONFIG_HPET_TIMER 6504d022e35SMiguel Boton hpet_disable(); 6514d022e35SMiguel Boton #endif 6524d022e35SMiguel Boton 6534d022e35SMiguel Boton #ifdef CONFIG_X86_64 654338bac52SFUJITA Tomonori x86_platform.iommu_shutdown(); 6554d022e35SMiguel Boton #endif 6564d022e35SMiguel Boton } 6574d022e35SMiguel Boton 658d176720dSEduardo Habkost static void __machine_emergency_restart(int emergency) 659d176720dSEduardo Habkost { 660d176720dSEduardo Habkost reboot_emergency = emergency; 661d176720dSEduardo Habkost machine_ops.emergency_restart(); 662d176720dSEduardo Habkost } 663d176720dSEduardo Habkost 664416e2d63SJody Belka static void native_machine_restart(char *__unused) 6654d022e35SMiguel Boton { 6664d022e35SMiguel Boton printk("machine restart\n"); 6674d022e35SMiguel Boton 6684d022e35SMiguel Boton if (!reboot_force) 6694d022e35SMiguel Boton machine_shutdown(); 670d176720dSEduardo Habkost __machine_emergency_restart(0); 6714d022e35SMiguel Boton } 6724d022e35SMiguel Boton 673416e2d63SJody Belka static void native_machine_halt(void) 6744d022e35SMiguel Boton { 675d3ec5caeSIvan Vecera /* stop other cpus and apics */ 676d3ec5caeSIvan Vecera machine_shutdown(); 677d3ec5caeSIvan Vecera 678840c2bafSJoseph Cihula tboot_shutdown(TB_SHUTDOWN_HALT); 679840c2bafSJoseph Cihula 680d3ec5caeSIvan Vecera /* stop this cpu */ 681d3ec5caeSIvan Vecera stop_this_cpu(NULL); 6824d022e35SMiguel Boton } 6834d022e35SMiguel Boton 684416e2d63SJody Belka static void native_machine_power_off(void) 6854d022e35SMiguel Boton { 6864d022e35SMiguel Boton if (pm_power_off) { 6874d022e35SMiguel Boton if (!reboot_force) 6884d022e35SMiguel Boton machine_shutdown(); 6894d022e35SMiguel Boton pm_power_off(); 6904d022e35SMiguel Boton } 691840c2bafSJoseph Cihula /* a fallback in case there is no PM info available */ 692840c2bafSJoseph Cihula tboot_shutdown(TB_SHUTDOWN_HALT); 6934d022e35SMiguel Boton } 6944d022e35SMiguel Boton 6954d022e35SMiguel Boton struct machine_ops machine_ops = { 696416e2d63SJody Belka .power_off = native_machine_power_off, 697416e2d63SJody Belka .shutdown = native_machine_shutdown, 698416e2d63SJody Belka .emergency_restart = native_machine_emergency_restart, 699416e2d63SJody Belka .restart = native_machine_restart, 700ed23dc6fSGlauber Costa .halt = native_machine_halt, 701ed23dc6fSGlauber Costa #ifdef CONFIG_KEXEC 702ed23dc6fSGlauber Costa .crash_shutdown = native_machine_crash_shutdown, 703ed23dc6fSGlauber Costa #endif 7044d022e35SMiguel Boton }; 705416e2d63SJody Belka 706416e2d63SJody Belka void machine_power_off(void) 707416e2d63SJody Belka { 708416e2d63SJody Belka machine_ops.power_off(); 709416e2d63SJody Belka } 710416e2d63SJody Belka 711416e2d63SJody Belka void machine_shutdown(void) 712416e2d63SJody Belka { 713416e2d63SJody Belka machine_ops.shutdown(); 714416e2d63SJody Belka } 715416e2d63SJody Belka 716416e2d63SJody Belka void machine_emergency_restart(void) 717416e2d63SJody Belka { 718d176720dSEduardo Habkost __machine_emergency_restart(1); 719416e2d63SJody Belka } 720416e2d63SJody Belka 721416e2d63SJody Belka void machine_restart(char *cmd) 722416e2d63SJody Belka { 723416e2d63SJody Belka machine_ops.restart(cmd); 724416e2d63SJody Belka } 725416e2d63SJody Belka 726416e2d63SJody Belka void machine_halt(void) 727416e2d63SJody Belka { 728416e2d63SJody Belka machine_ops.halt(); 729416e2d63SJody Belka } 730416e2d63SJody Belka 731ed23dc6fSGlauber Costa #ifdef CONFIG_KEXEC 732ed23dc6fSGlauber Costa void machine_crash_shutdown(struct pt_regs *regs) 733ed23dc6fSGlauber Costa { 734ed23dc6fSGlauber Costa machine_ops.crash_shutdown(regs); 735ed23dc6fSGlauber Costa } 736ed23dc6fSGlauber Costa #endif 7372ddded21SEduardo Habkost 7382ddded21SEduardo Habkost 739bb8dd270SEduardo Habkost #if defined(CONFIG_SMP) 7402ddded21SEduardo Habkost 7412ddded21SEduardo Habkost /* This keeps a track of which one is crashing cpu. */ 7422ddded21SEduardo Habkost static int crashing_cpu; 7432ddded21SEduardo Habkost static nmi_shootdown_cb shootdown_callback; 7442ddded21SEduardo Habkost 7452ddded21SEduardo Habkost static atomic_t waiting_for_crash_ipi; 7462ddded21SEduardo Habkost 7479c48f1c6SDon Zickus static int crash_nmi_callback(unsigned int val, struct pt_regs *regs) 7482ddded21SEduardo Habkost { 7492ddded21SEduardo Habkost int cpu; 7502ddded21SEduardo Habkost 7512ddded21SEduardo Habkost cpu = raw_smp_processor_id(); 7522ddded21SEduardo Habkost 7532ddded21SEduardo Habkost /* Don't do anything if this handler is invoked on crashing cpu. 7542ddded21SEduardo Habkost * Otherwise, system will completely hang. Crashing cpu can get 7552ddded21SEduardo Habkost * an NMI if system was initially booted with nmi_watchdog parameter. 7562ddded21SEduardo Habkost */ 7572ddded21SEduardo Habkost if (cpu == crashing_cpu) 7589c48f1c6SDon Zickus return NMI_HANDLED; 7592ddded21SEduardo Habkost local_irq_disable(); 7602ddded21SEduardo Habkost 7619c48f1c6SDon Zickus shootdown_callback(cpu, regs); 7622ddded21SEduardo Habkost 7632ddded21SEduardo Habkost atomic_dec(&waiting_for_crash_ipi); 7642ddded21SEduardo Habkost /* Assume hlt works */ 7652ddded21SEduardo Habkost halt(); 7662ddded21SEduardo Habkost for (;;) 7672ddded21SEduardo Habkost cpu_relax(); 7682ddded21SEduardo Habkost 7699c48f1c6SDon Zickus return NMI_HANDLED; 7702ddded21SEduardo Habkost } 7712ddded21SEduardo Habkost 7722ddded21SEduardo Habkost static void smp_send_nmi_allbutself(void) 7732ddded21SEduardo Habkost { 774dac5f412SIngo Molnar apic->send_IPI_allbutself(NMI_VECTOR); 7752ddded21SEduardo Habkost } 7762ddded21SEduardo Habkost 777bb8dd270SEduardo Habkost /* Halt all other CPUs, calling the specified function on each of them 778bb8dd270SEduardo Habkost * 779bb8dd270SEduardo Habkost * This function can be used to halt all other CPUs on crash 780bb8dd270SEduardo Habkost * or emergency reboot time. The function passed as parameter 781bb8dd270SEduardo Habkost * will be called inside a NMI handler on all CPUs. 782bb8dd270SEduardo Habkost */ 7832ddded21SEduardo Habkost void nmi_shootdown_cpus(nmi_shootdown_cb callback) 7842ddded21SEduardo Habkost { 7852ddded21SEduardo Habkost unsigned long msecs; 786c415b3dcSEduardo Habkost local_irq_disable(); 7872ddded21SEduardo Habkost 7882ddded21SEduardo Habkost /* Make a note of crashing cpu. Will be used in NMI callback.*/ 7892ddded21SEduardo Habkost crashing_cpu = safe_smp_processor_id(); 7902ddded21SEduardo Habkost 7912ddded21SEduardo Habkost shootdown_callback = callback; 7922ddded21SEduardo Habkost 7932ddded21SEduardo Habkost atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); 7942ddded21SEduardo Habkost /* Would it be better to replace the trap vector here? */ 7959c48f1c6SDon Zickus if (register_nmi_handler(NMI_LOCAL, crash_nmi_callback, 7969c48f1c6SDon Zickus NMI_FLAG_FIRST, "crash")) 7972ddded21SEduardo Habkost return; /* return what? */ 7982ddded21SEduardo Habkost /* Ensure the new callback function is set before sending 7992ddded21SEduardo Habkost * out the NMI 8002ddded21SEduardo Habkost */ 8012ddded21SEduardo Habkost wmb(); 8022ddded21SEduardo Habkost 8032ddded21SEduardo Habkost smp_send_nmi_allbutself(); 8042ddded21SEduardo Habkost 8052ddded21SEduardo Habkost msecs = 1000; /* Wait at most a second for the other cpus to stop */ 8062ddded21SEduardo Habkost while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) { 8072ddded21SEduardo Habkost mdelay(1); 8082ddded21SEduardo Habkost msecs--; 8092ddded21SEduardo Habkost } 8102ddded21SEduardo Habkost 8112ddded21SEduardo Habkost /* Leave the nmi callback set */ 8122ddded21SEduardo Habkost } 813bb8dd270SEduardo Habkost #else /* !CONFIG_SMP */ 814bb8dd270SEduardo Habkost void nmi_shootdown_cpus(nmi_shootdown_cb callback) 815bb8dd270SEduardo Habkost { 816bb8dd270SEduardo Habkost /* No other CPUs to shoot down */ 817bb8dd270SEduardo Habkost } 8182ddded21SEduardo Habkost #endif 819