xref: /linux/arch/x86/kernel/reboot.c (revision 660e34cebf0a11d54f2d5dd8838607452355f321)
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;
39*660e34ceSMatthew 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 	},
2974d022e35SMiguel Boton 	{ }
2984d022e35SMiguel Boton };
2994d022e35SMiguel Boton 
3004d022e35SMiguel Boton static int __init reboot_init(void)
3014d022e35SMiguel Boton {
3024d022e35SMiguel Boton 	dmi_check_system(reboot_dmi_table);
3034d022e35SMiguel Boton 	return 0;
3044d022e35SMiguel Boton }
3054d022e35SMiguel Boton core_initcall(reboot_init);
3064d022e35SMiguel Boton 
3073d35ac34SH. Peter Anvin extern const unsigned char machine_real_restart_asm[];
3083d35ac34SH. Peter Anvin extern const u64 machine_real_restart_gdt[3];
3093d35ac34SH. Peter Anvin 
3103d35ac34SH. Peter Anvin void machine_real_restart(unsigned int type)
3114d022e35SMiguel Boton {
3123d35ac34SH. Peter Anvin 	void *restart_va;
3133d35ac34SH. Peter Anvin 	unsigned long restart_pa;
3143d35ac34SH. Peter Anvin 	void (*restart_lowmem)(unsigned int);
3153d35ac34SH. Peter Anvin 	u64 *lowmem_gdt;
3164d022e35SMiguel Boton 
3174d022e35SMiguel Boton 	local_irq_disable();
3184d022e35SMiguel Boton 
3194d022e35SMiguel Boton 	/* Write zero to CMOS register number 0x0f, which the BIOS POST
3204d022e35SMiguel Boton 	   routine will recognize as telling it to do a proper reboot.  (Well
3214d022e35SMiguel Boton 	   that's what this book in front of me says -- it may only apply to
3224d022e35SMiguel Boton 	   the Phoenix BIOS though, it's not clear).  At the same time,
3234d022e35SMiguel Boton 	   disable NMIs by setting the top bit in the CMOS address register,
3244d022e35SMiguel Boton 	   as we're about to do peculiar things to the CPU.  I'm not sure if
3254d022e35SMiguel Boton 	   `outb_p' is needed instead of just `outb'.  Use it to be on the
3264d022e35SMiguel Boton 	   safe side.  (Yes, CMOS_WRITE does outb_p's. -  Paul G.)
3274d022e35SMiguel Boton 	 */
3284d022e35SMiguel Boton 	spin_lock(&rtc_lock);
3294d022e35SMiguel Boton 	CMOS_WRITE(0x00, 0x8f);
3304d022e35SMiguel Boton 	spin_unlock(&rtc_lock);
3314d022e35SMiguel Boton 
3324d022e35SMiguel Boton 	/*
333b40827faSBorislav Petkov 	 * Switch back to the initial page table.
3344d022e35SMiguel Boton 	 */
335b40827faSBorislav Petkov 	load_cr3(initial_page_table);
3364d022e35SMiguel Boton 
3374d022e35SMiguel Boton 	/* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
3384d022e35SMiguel Boton 	   this on booting to tell it to "Bypass memory test (also warm
3394d022e35SMiguel Boton 	   boot)".  This seems like a fairly standard thing that gets set by
3404d022e35SMiguel Boton 	   REBOOT.COM programs, and the previous reset routine did this
3414d022e35SMiguel Boton 	   too. */
3424d022e35SMiguel Boton 	*((unsigned short *)0x472) = reboot_mode;
3434d022e35SMiguel Boton 
3443d35ac34SH. Peter Anvin 	/* Patch the GDT in the low memory trampoline */
3453d35ac34SH. Peter Anvin 	lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt);
3464d022e35SMiguel Boton 
3473d35ac34SH. Peter Anvin 	restart_va = TRAMPOLINE_SYM(machine_real_restart_asm);
3483d35ac34SH. Peter Anvin 	restart_pa = virt_to_phys(restart_va);
3493d35ac34SH. Peter Anvin 	restart_lowmem = (void (*)(unsigned int))restart_pa;
3504d022e35SMiguel Boton 
3513d35ac34SH. Peter Anvin 	/* GDT[0]: GDT self-pointer */
3523d35ac34SH. Peter Anvin 	lowmem_gdt[0] =
3533d35ac34SH. Peter Anvin 		(u64)(sizeof(machine_real_restart_gdt) - 1) +
3543d35ac34SH. Peter Anvin 		((u64)virt_to_phys(lowmem_gdt) << 16);
3553d35ac34SH. Peter Anvin 	/* GDT[1]: 64K real mode code segment */
3563d35ac34SH. Peter Anvin 	lowmem_gdt[1] =
3573d35ac34SH. Peter Anvin 		GDT_ENTRY(0x009b, restart_pa, 0xffff);
3584d022e35SMiguel Boton 
3593d35ac34SH. Peter Anvin 	/* Jump to the identity-mapped low memory code */
3603d35ac34SH. Peter Anvin 	restart_lowmem(type);
3614d022e35SMiguel Boton }
3624d022e35SMiguel Boton #ifdef CONFIG_APM_MODULE
3634d022e35SMiguel Boton EXPORT_SYMBOL(machine_real_restart);
3644d022e35SMiguel Boton #endif
3654d022e35SMiguel Boton 
3664d022e35SMiguel Boton #endif /* CONFIG_X86_32 */
3674d022e35SMiguel Boton 
3686c6c51e4SPaul Mackerras /*
369498cdbfbSOzan Çağlayan  * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
3706c6c51e4SPaul Mackerras  */
3716c6c51e4SPaul Mackerras static int __init set_pci_reboot(const struct dmi_system_id *d)
3726c6c51e4SPaul Mackerras {
3736c6c51e4SPaul Mackerras 	if (reboot_type != BOOT_CF9) {
3746c6c51e4SPaul Mackerras 		reboot_type = BOOT_CF9;
3756c6c51e4SPaul Mackerras 		printk(KERN_INFO "%s series board detected. "
3766c6c51e4SPaul Mackerras 		       "Selecting PCI-method for reboots.\n", d->ident);
3776c6c51e4SPaul Mackerras 	}
3786c6c51e4SPaul Mackerras 	return 0;
3796c6c51e4SPaul Mackerras }
3806c6c51e4SPaul Mackerras 
3816c6c51e4SPaul Mackerras static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
3823e03bbeaSShunichi Fuji 	{	/* Handle problems with rebooting on Apple MacBook5 */
3836c6c51e4SPaul Mackerras 		.callback = set_pci_reboot,
3843e03bbeaSShunichi Fuji 		.ident = "Apple MacBook5",
3856c6c51e4SPaul Mackerras 		.matches = {
3866c6c51e4SPaul Mackerras 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
3873e03bbeaSShunichi Fuji 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"),
3886c6c51e4SPaul Mackerras 		},
3896c6c51e4SPaul Mackerras 	},
3903e03bbeaSShunichi Fuji 	{	/* Handle problems with rebooting on Apple MacBookPro5 */
391498cdbfbSOzan Çağlayan 		.callback = set_pci_reboot,
3923e03bbeaSShunichi Fuji 		.ident = "Apple MacBookPro5",
393498cdbfbSOzan Çağlayan 		.matches = {
394498cdbfbSOzan Çağlayan 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
3953e03bbeaSShunichi Fuji 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"),
396498cdbfbSOzan Çağlayan 		},
397498cdbfbSOzan Çağlayan 	},
39805154752SGottfried Haider 	{	/* Handle problems with rebooting on Apple Macmini3,1 */
39905154752SGottfried Haider 		.callback = set_pci_reboot,
40005154752SGottfried Haider 		.ident = "Apple Macmini3,1",
40105154752SGottfried Haider 		.matches = {
40205154752SGottfried Haider 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
40305154752SGottfried Haider 			DMI_MATCH(DMI_PRODUCT_NAME, "Macmini3,1"),
40405154752SGottfried Haider 		},
40505154752SGottfried Haider 	},
4060a832320SJustin P. Mattock 	{	/* Handle problems with rebooting on the iMac9,1. */
4070a832320SJustin P. Mattock 		.callback = set_pci_reboot,
4080a832320SJustin P. Mattock 		.ident = "Apple iMac9,1",
4090a832320SJustin P. Mattock 		.matches = {
4100a832320SJustin P. Mattock 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
4110a832320SJustin P. Mattock 			DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"),
4120a832320SJustin P. Mattock 		},
4130a832320SJustin P. Mattock 	},
4146c6c51e4SPaul Mackerras 	{ }
4156c6c51e4SPaul Mackerras };
4166c6c51e4SPaul Mackerras 
4176c6c51e4SPaul Mackerras static int __init pci_reboot_init(void)
4186c6c51e4SPaul Mackerras {
4196c6c51e4SPaul Mackerras 	dmi_check_system(pci_reboot_dmi_table);
4206c6c51e4SPaul Mackerras 	return 0;
4216c6c51e4SPaul Mackerras }
4226c6c51e4SPaul Mackerras core_initcall(pci_reboot_init);
4236c6c51e4SPaul Mackerras 
4244d022e35SMiguel Boton static inline void kb_wait(void)
4254d022e35SMiguel Boton {
4264d022e35SMiguel Boton 	int i;
4274d022e35SMiguel Boton 
428c84d6af8SAlan Cox 	for (i = 0; i < 0x10000; i++) {
429c84d6af8SAlan Cox 		if ((inb(0x64) & 0x02) == 0)
4304d022e35SMiguel Boton 			break;
431c84d6af8SAlan Cox 		udelay(2);
432c84d6af8SAlan Cox 	}
4334d022e35SMiguel Boton }
4344d022e35SMiguel Boton 
435d176720dSEduardo Habkost static void vmxoff_nmi(int cpu, struct die_args *args)
436d176720dSEduardo Habkost {
437d176720dSEduardo Habkost 	cpu_emergency_vmxoff();
438d176720dSEduardo Habkost }
439d176720dSEduardo Habkost 
440d176720dSEduardo Habkost /* Use NMIs as IPIs to tell all CPUs to disable virtualization
441d176720dSEduardo Habkost  */
442d176720dSEduardo Habkost static void emergency_vmx_disable_all(void)
443d176720dSEduardo Habkost {
444d176720dSEduardo Habkost 	/* Just make sure we won't change CPUs while doing this */
445d176720dSEduardo Habkost 	local_irq_disable();
446d176720dSEduardo Habkost 
447d176720dSEduardo Habkost 	/* We need to disable VMX on all CPUs before rebooting, otherwise
448d176720dSEduardo Habkost 	 * we risk hanging up the machine, because the CPU ignore INIT
449d176720dSEduardo Habkost 	 * signals when VMX is enabled.
450d176720dSEduardo Habkost 	 *
451d176720dSEduardo Habkost 	 * We can't take any locks and we may be on an inconsistent
452d176720dSEduardo Habkost 	 * state, so we use NMIs as IPIs to tell the other CPUs to disable
453d176720dSEduardo Habkost 	 * VMX and halt.
454d176720dSEduardo Habkost 	 *
455d176720dSEduardo Habkost 	 * For safety, we will avoid running the nmi_shootdown_cpus()
456d176720dSEduardo Habkost 	 * stuff unnecessarily, but we don't have a way to check
457d176720dSEduardo Habkost 	 * if other CPUs have VMX enabled. So we will call it only if the
458d176720dSEduardo Habkost 	 * CPU we are running on has VMX enabled.
459d176720dSEduardo Habkost 	 *
460d176720dSEduardo Habkost 	 * We will miss cases where VMX is not enabled on all CPUs. This
461d176720dSEduardo Habkost 	 * shouldn't do much harm because KVM always enable VMX on all
462d176720dSEduardo Habkost 	 * CPUs anyway. But we can miss it on the small window where KVM
463d176720dSEduardo Habkost 	 * is still enabling VMX.
464d176720dSEduardo Habkost 	 */
465d176720dSEduardo Habkost 	if (cpu_has_vmx() && cpu_vmx_enabled()) {
466d176720dSEduardo Habkost 		/* Disable VMX on this CPU.
467d176720dSEduardo Habkost 		 */
468d176720dSEduardo Habkost 		cpu_vmxoff();
469d176720dSEduardo Habkost 
470d176720dSEduardo Habkost 		/* Halt and disable VMX on the other CPUs */
471d176720dSEduardo Habkost 		nmi_shootdown_cpus(vmxoff_nmi);
472d176720dSEduardo Habkost 
473d176720dSEduardo Habkost 	}
474d176720dSEduardo Habkost }
475d176720dSEduardo Habkost 
476d176720dSEduardo Habkost 
4777432d149SIngo Molnar void __attribute__((weak)) mach_reboot_fixups(void)
4787432d149SIngo Molnar {
4797432d149SIngo Molnar }
4807432d149SIngo Molnar 
481*660e34ceSMatthew Garrett /*
482*660e34ceSMatthew Garrett  * Windows compatible x86 hardware expects the following on reboot:
483*660e34ceSMatthew Garrett  *
484*660e34ceSMatthew Garrett  * 1) If the FADT has the ACPI reboot register flag set, try it
485*660e34ceSMatthew Garrett  * 2) If still alive, write to the keyboard controller
486*660e34ceSMatthew Garrett  * 3) If still alive, write to the ACPI reboot register again
487*660e34ceSMatthew Garrett  * 4) If still alive, write to the keyboard controller again
488*660e34ceSMatthew Garrett  *
489*660e34ceSMatthew Garrett  * If the machine is still alive at this stage, it gives up. We default to
490*660e34ceSMatthew Garrett  * following the same pattern, except that if we're still alive after (4) we'll
491*660e34ceSMatthew Garrett  * try to force a triple fault and then cycle between hitting the keyboard
492*660e34ceSMatthew Garrett  * controller and doing that
493*660e34ceSMatthew Garrett  */
494416e2d63SJody Belka static void native_machine_emergency_restart(void)
4954d022e35SMiguel Boton {
4964d022e35SMiguel Boton 	int i;
497*660e34ceSMatthew Garrett 	int attempt = 0;
498*660e34ceSMatthew Garrett 	int orig_reboot_type = reboot_type;
4994d022e35SMiguel Boton 
500d176720dSEduardo Habkost 	if (reboot_emergency)
501d176720dSEduardo Habkost 		emergency_vmx_disable_all();
502d176720dSEduardo Habkost 
503840c2bafSJoseph Cihula 	tboot_shutdown(TB_SHUTDOWN_REBOOT);
504840c2bafSJoseph Cihula 
5054d022e35SMiguel Boton 	/* Tell the BIOS if we want cold or warm reboot */
5064d022e35SMiguel Boton 	*((unsigned short *)__va(0x472)) = reboot_mode;
5074d022e35SMiguel Boton 
5084d022e35SMiguel Boton 	for (;;) {
5094d022e35SMiguel Boton 		/* Could also try the reset bit in the Hammer NB */
5104d022e35SMiguel Boton 		switch (reboot_type) {
5114d022e35SMiguel Boton 		case BOOT_KBD:
5127432d149SIngo Molnar 			mach_reboot_fixups(); /* for board specific fixups */
5137432d149SIngo Molnar 
5144d022e35SMiguel Boton 			for (i = 0; i < 10; i++) {
5154d022e35SMiguel Boton 				kb_wait();
5164d022e35SMiguel Boton 				udelay(50);
5174d022e35SMiguel Boton 				outb(0xfe, 0x64); /* pulse reset low */
5184d022e35SMiguel Boton 				udelay(50);
5194d022e35SMiguel Boton 			}
520*660e34ceSMatthew Garrett 			if (attempt == 0 && orig_reboot_type == BOOT_ACPI) {
521*660e34ceSMatthew Garrett 				attempt = 1;
522*660e34ceSMatthew Garrett 				reboot_type = BOOT_ACPI;
523*660e34ceSMatthew Garrett 			} else {
524*660e34ceSMatthew Garrett 				reboot_type = BOOT_TRIPLE;
525*660e34ceSMatthew Garrett 			}
526*660e34ceSMatthew Garrett 			break;
5274d022e35SMiguel Boton 
5284d022e35SMiguel Boton 		case BOOT_TRIPLE:
529ebdd561aSJan Beulich 			load_idt(&no_idt);
5304d022e35SMiguel Boton 			__asm__ __volatile__("int3");
5314d022e35SMiguel Boton 
5324d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5334d022e35SMiguel Boton 			break;
5344d022e35SMiguel Boton 
5354d022e35SMiguel Boton #ifdef CONFIG_X86_32
5364d022e35SMiguel Boton 		case BOOT_BIOS:
5373d35ac34SH. Peter Anvin 			machine_real_restart(MRR_BIOS);
5384d022e35SMiguel Boton 
5394d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5404d022e35SMiguel Boton 			break;
5414d022e35SMiguel Boton #endif
5424d022e35SMiguel Boton 
5434d022e35SMiguel Boton 		case BOOT_ACPI:
5444d022e35SMiguel Boton 			acpi_reboot();
5454d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5464d022e35SMiguel Boton 			break;
5474d022e35SMiguel Boton 
5484d022e35SMiguel Boton 		case BOOT_EFI:
5494d022e35SMiguel Boton 			if (efi_enabled)
55014d7ca5cSH. Peter Anvin 				efi.reset_system(reboot_mode ?
55114d7ca5cSH. Peter Anvin 						 EFI_RESET_WARM :
55214d7ca5cSH. Peter Anvin 						 EFI_RESET_COLD,
5534d022e35SMiguel Boton 						 EFI_SUCCESS, 0, NULL);
554b47b9288SH. Peter Anvin 			reboot_type = BOOT_KBD;
55514d7ca5cSH. Peter Anvin 			break;
5564d022e35SMiguel Boton 
55714d7ca5cSH. Peter Anvin 		case BOOT_CF9:
55814d7ca5cSH. Peter Anvin 			port_cf9_safe = true;
55914d7ca5cSH. Peter Anvin 			/* fall through */
56014d7ca5cSH. Peter Anvin 
56114d7ca5cSH. Peter Anvin 		case BOOT_CF9_COND:
56214d7ca5cSH. Peter Anvin 			if (port_cf9_safe) {
56314d7ca5cSH. Peter Anvin 				u8 cf9 = inb(0xcf9) & ~6;
56414d7ca5cSH. Peter Anvin 				outb(cf9|2, 0xcf9); /* Request hard reset */
56514d7ca5cSH. Peter Anvin 				udelay(50);
56614d7ca5cSH. Peter Anvin 				outb(cf9|6, 0xcf9); /* Actually do the reset */
56714d7ca5cSH. Peter Anvin 				udelay(50);
56814d7ca5cSH. Peter Anvin 			}
5694d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5704d022e35SMiguel Boton 			break;
5714d022e35SMiguel Boton 		}
5724d022e35SMiguel Boton 	}
5734d022e35SMiguel Boton }
5744d022e35SMiguel Boton 
5753c62c625SGlauber Costa void native_machine_shutdown(void)
5764d022e35SMiguel Boton {
5774d022e35SMiguel Boton 	/* Stop the cpus and apics */
5784d022e35SMiguel Boton #ifdef CONFIG_SMP
5794d022e35SMiguel Boton 
5804d022e35SMiguel Boton 	/* The boot cpu is always logical cpu 0 */
58165c01184SMike Travis 	int reboot_cpu_id = 0;
5824d022e35SMiguel Boton 
5834d022e35SMiguel Boton #ifdef CONFIG_X86_32
5844d022e35SMiguel Boton 	/* See if there has been given a command line override */
5859628937dSMike Travis 	if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) &&
5860bc3cc03SMike Travis 		cpu_online(reboot_cpu))
5874d022e35SMiguel Boton 		reboot_cpu_id = reboot_cpu;
5884d022e35SMiguel Boton #endif
5894d022e35SMiguel Boton 
5904d022e35SMiguel Boton 	/* Make certain the cpu I'm about to reboot on is online */
5910bc3cc03SMike Travis 	if (!cpu_online(reboot_cpu_id))
5924d022e35SMiguel Boton 		reboot_cpu_id = smp_processor_id();
5934d022e35SMiguel Boton 
5944d022e35SMiguel Boton 	/* Make certain I only run on the appropriate processor */
5959628937dSMike Travis 	set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id));
5964d022e35SMiguel Boton 
5974d022e35SMiguel Boton 	/* O.K Now that I'm on the appropriate processor,
5984d022e35SMiguel Boton 	 * stop all of the others.
5994d022e35SMiguel Boton 	 */
60076fac077SAlok Kataria 	stop_other_cpus();
6014d022e35SMiguel Boton #endif
6024d022e35SMiguel Boton 
6034d022e35SMiguel Boton 	lapic_shutdown();
6044d022e35SMiguel Boton 
6054d022e35SMiguel Boton #ifdef CONFIG_X86_IO_APIC
6064d022e35SMiguel Boton 	disable_IO_APIC();
6074d022e35SMiguel Boton #endif
6084d022e35SMiguel Boton 
6094d022e35SMiguel Boton #ifdef CONFIG_HPET_TIMER
6104d022e35SMiguel Boton 	hpet_disable();
6114d022e35SMiguel Boton #endif
6124d022e35SMiguel Boton 
6134d022e35SMiguel Boton #ifdef CONFIG_X86_64
614338bac52SFUJITA Tomonori 	x86_platform.iommu_shutdown();
6154d022e35SMiguel Boton #endif
6164d022e35SMiguel Boton }
6174d022e35SMiguel Boton 
618d176720dSEduardo Habkost static void __machine_emergency_restart(int emergency)
619d176720dSEduardo Habkost {
620d176720dSEduardo Habkost 	reboot_emergency = emergency;
621d176720dSEduardo Habkost 	machine_ops.emergency_restart();
622d176720dSEduardo Habkost }
623d176720dSEduardo Habkost 
624416e2d63SJody Belka static void native_machine_restart(char *__unused)
6254d022e35SMiguel Boton {
6264d022e35SMiguel Boton 	printk("machine restart\n");
6274d022e35SMiguel Boton 
6284d022e35SMiguel Boton 	if (!reboot_force)
6294d022e35SMiguel Boton 		machine_shutdown();
630d176720dSEduardo Habkost 	__machine_emergency_restart(0);
6314d022e35SMiguel Boton }
6324d022e35SMiguel Boton 
633416e2d63SJody Belka static void native_machine_halt(void)
6344d022e35SMiguel Boton {
635d3ec5caeSIvan Vecera 	/* stop other cpus and apics */
636d3ec5caeSIvan Vecera 	machine_shutdown();
637d3ec5caeSIvan Vecera 
638840c2bafSJoseph Cihula 	tboot_shutdown(TB_SHUTDOWN_HALT);
639840c2bafSJoseph Cihula 
640d3ec5caeSIvan Vecera 	/* stop this cpu */
641d3ec5caeSIvan Vecera 	stop_this_cpu(NULL);
6424d022e35SMiguel Boton }
6434d022e35SMiguel Boton 
644416e2d63SJody Belka static void native_machine_power_off(void)
6454d022e35SMiguel Boton {
6464d022e35SMiguel Boton 	if (pm_power_off) {
6474d022e35SMiguel Boton 		if (!reboot_force)
6484d022e35SMiguel Boton 			machine_shutdown();
6494d022e35SMiguel Boton 		pm_power_off();
6504d022e35SMiguel Boton 	}
651840c2bafSJoseph Cihula 	/* a fallback in case there is no PM info available */
652840c2bafSJoseph Cihula 	tboot_shutdown(TB_SHUTDOWN_HALT);
6534d022e35SMiguel Boton }
6544d022e35SMiguel Boton 
6554d022e35SMiguel Boton struct machine_ops machine_ops = {
656416e2d63SJody Belka 	.power_off = native_machine_power_off,
657416e2d63SJody Belka 	.shutdown = native_machine_shutdown,
658416e2d63SJody Belka 	.emergency_restart = native_machine_emergency_restart,
659416e2d63SJody Belka 	.restart = native_machine_restart,
660ed23dc6fSGlauber Costa 	.halt = native_machine_halt,
661ed23dc6fSGlauber Costa #ifdef CONFIG_KEXEC
662ed23dc6fSGlauber Costa 	.crash_shutdown = native_machine_crash_shutdown,
663ed23dc6fSGlauber Costa #endif
6644d022e35SMiguel Boton };
665416e2d63SJody Belka 
666416e2d63SJody Belka void machine_power_off(void)
667416e2d63SJody Belka {
668416e2d63SJody Belka 	machine_ops.power_off();
669416e2d63SJody Belka }
670416e2d63SJody Belka 
671416e2d63SJody Belka void machine_shutdown(void)
672416e2d63SJody Belka {
673416e2d63SJody Belka 	machine_ops.shutdown();
674416e2d63SJody Belka }
675416e2d63SJody Belka 
676416e2d63SJody Belka void machine_emergency_restart(void)
677416e2d63SJody Belka {
678d176720dSEduardo Habkost 	__machine_emergency_restart(1);
679416e2d63SJody Belka }
680416e2d63SJody Belka 
681416e2d63SJody Belka void machine_restart(char *cmd)
682416e2d63SJody Belka {
683416e2d63SJody Belka 	machine_ops.restart(cmd);
684416e2d63SJody Belka }
685416e2d63SJody Belka 
686416e2d63SJody Belka void machine_halt(void)
687416e2d63SJody Belka {
688416e2d63SJody Belka 	machine_ops.halt();
689416e2d63SJody Belka }
690416e2d63SJody Belka 
691ed23dc6fSGlauber Costa #ifdef CONFIG_KEXEC
692ed23dc6fSGlauber Costa void machine_crash_shutdown(struct pt_regs *regs)
693ed23dc6fSGlauber Costa {
694ed23dc6fSGlauber Costa 	machine_ops.crash_shutdown(regs);
695ed23dc6fSGlauber Costa }
696ed23dc6fSGlauber Costa #endif
6972ddded21SEduardo Habkost 
6982ddded21SEduardo Habkost 
699bb8dd270SEduardo Habkost #if defined(CONFIG_SMP)
7002ddded21SEduardo Habkost 
7012ddded21SEduardo Habkost /* This keeps a track of which one is crashing cpu. */
7022ddded21SEduardo Habkost static int crashing_cpu;
7032ddded21SEduardo Habkost static nmi_shootdown_cb shootdown_callback;
7042ddded21SEduardo Habkost 
7052ddded21SEduardo Habkost static atomic_t waiting_for_crash_ipi;
7062ddded21SEduardo Habkost 
7072ddded21SEduardo Habkost static int crash_nmi_callback(struct notifier_block *self,
7082ddded21SEduardo Habkost 			unsigned long val, void *data)
7092ddded21SEduardo Habkost {
7102ddded21SEduardo Habkost 	int cpu;
7112ddded21SEduardo Habkost 
712c410b830SDon Zickus 	if (val != DIE_NMI)
7132ddded21SEduardo Habkost 		return NOTIFY_OK;
7142ddded21SEduardo Habkost 
7152ddded21SEduardo Habkost 	cpu = raw_smp_processor_id();
7162ddded21SEduardo Habkost 
7172ddded21SEduardo Habkost 	/* Don't do anything if this handler is invoked on crashing cpu.
7182ddded21SEduardo Habkost 	 * Otherwise, system will completely hang. Crashing cpu can get
7192ddded21SEduardo Habkost 	 * an NMI if system was initially booted with nmi_watchdog parameter.
7202ddded21SEduardo Habkost 	 */
7212ddded21SEduardo Habkost 	if (cpu == crashing_cpu)
7222ddded21SEduardo Habkost 		return NOTIFY_STOP;
7232ddded21SEduardo Habkost 	local_irq_disable();
7242ddded21SEduardo Habkost 
7252ddded21SEduardo Habkost 	shootdown_callback(cpu, (struct die_args *)data);
7262ddded21SEduardo Habkost 
7272ddded21SEduardo Habkost 	atomic_dec(&waiting_for_crash_ipi);
7282ddded21SEduardo Habkost 	/* Assume hlt works */
7292ddded21SEduardo Habkost 	halt();
7302ddded21SEduardo Habkost 	for (;;)
7312ddded21SEduardo Habkost 		cpu_relax();
7322ddded21SEduardo Habkost 
7332ddded21SEduardo Habkost 	return 1;
7342ddded21SEduardo Habkost }
7352ddded21SEduardo Habkost 
7362ddded21SEduardo Habkost static void smp_send_nmi_allbutself(void)
7372ddded21SEduardo Habkost {
738dac5f412SIngo Molnar 	apic->send_IPI_allbutself(NMI_VECTOR);
7392ddded21SEduardo Habkost }
7402ddded21SEduardo Habkost 
7412ddded21SEduardo Habkost static struct notifier_block crash_nmi_nb = {
7422ddded21SEduardo Habkost 	.notifier_call = crash_nmi_callback,
743166d7514SDon Zickus 	/* we want to be the first one called */
744166d7514SDon Zickus 	.priority = NMI_LOCAL_HIGH_PRIOR+1,
7452ddded21SEduardo Habkost };
7462ddded21SEduardo Habkost 
747bb8dd270SEduardo Habkost /* Halt all other CPUs, calling the specified function on each of them
748bb8dd270SEduardo Habkost  *
749bb8dd270SEduardo Habkost  * This function can be used to halt all other CPUs on crash
750bb8dd270SEduardo Habkost  * or emergency reboot time. The function passed as parameter
751bb8dd270SEduardo Habkost  * will be called inside a NMI handler on all CPUs.
752bb8dd270SEduardo Habkost  */
7532ddded21SEduardo Habkost void nmi_shootdown_cpus(nmi_shootdown_cb callback)
7542ddded21SEduardo Habkost {
7552ddded21SEduardo Habkost 	unsigned long msecs;
756c415b3dcSEduardo Habkost 	local_irq_disable();
7572ddded21SEduardo Habkost 
7582ddded21SEduardo Habkost 	/* Make a note of crashing cpu. Will be used in NMI callback.*/
7592ddded21SEduardo Habkost 	crashing_cpu = safe_smp_processor_id();
7602ddded21SEduardo Habkost 
7612ddded21SEduardo Habkost 	shootdown_callback = callback;
7622ddded21SEduardo Habkost 
7632ddded21SEduardo Habkost 	atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
7642ddded21SEduardo Habkost 	/* Would it be better to replace the trap vector here? */
7652ddded21SEduardo Habkost 	if (register_die_notifier(&crash_nmi_nb))
7662ddded21SEduardo Habkost 		return;		/* return what? */
7672ddded21SEduardo Habkost 	/* Ensure the new callback function is set before sending
7682ddded21SEduardo Habkost 	 * out the NMI
7692ddded21SEduardo Habkost 	 */
7702ddded21SEduardo Habkost 	wmb();
7712ddded21SEduardo Habkost 
7722ddded21SEduardo Habkost 	smp_send_nmi_allbutself();
7732ddded21SEduardo Habkost 
7742ddded21SEduardo Habkost 	msecs = 1000; /* Wait at most a second for the other cpus to stop */
7752ddded21SEduardo Habkost 	while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
7762ddded21SEduardo Habkost 		mdelay(1);
7772ddded21SEduardo Habkost 		msecs--;
7782ddded21SEduardo Habkost 	}
7792ddded21SEduardo Habkost 
7802ddded21SEduardo Habkost 	/* Leave the nmi callback set */
7812ddded21SEduardo Habkost }
782bb8dd270SEduardo Habkost #else /* !CONFIG_SMP */
783bb8dd270SEduardo Habkost void nmi_shootdown_cpus(nmi_shootdown_cb callback)
784bb8dd270SEduardo Habkost {
785bb8dd270SEduardo Habkost 	/* No other CPUs to shoot down */
786bb8dd270SEduardo Habkost }
7872ddded21SEduardo Habkost #endif
788