xref: /linux/arch/x86/kernel/reboot.c (revision d43c36dc6b357fa1806800f18aa30123c747a6d1)
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>
7*d43c36dcSAlexey Dobriyan #include <linux/sched.h>
869575d38SShane Wang #include <linux/tboot.h>
94d022e35SMiguel Boton #include <acpi/reboot.h>
104d022e35SMiguel Boton #include <asm/io.h>
114d022e35SMiguel Boton #include <asm/apic.h>
124d022e35SMiguel Boton #include <asm/desc.h>
134d022e35SMiguel Boton #include <asm/hpet.h>
1468db065cSJeremy Fitzhardinge #include <asm/pgtable.h>
154412620fSDmitri Vorobiev #include <asm/proto.h>
164d022e35SMiguel Boton #include <asm/reboot_fixups.h>
174d022e35SMiguel Boton #include <asm/reboot.h>
1882487711SJaswinder Singh Rajput #include <asm/pci_x86.h>
19d176720dSEduardo Habkost #include <asm/virtext.h>
2096b89dc6SJaswinder Singh Rajput #include <asm/cpu.h>
214d022e35SMiguel Boton 
224d022e35SMiguel Boton #ifdef CONFIG_X86_32
234d022e35SMiguel Boton # include <linux/ctype.h>
244d022e35SMiguel Boton # include <linux/mc146818rtc.h>
254d022e35SMiguel Boton #else
264d022e35SMiguel Boton # include <asm/iommu.h>
274d022e35SMiguel Boton #endif
284d022e35SMiguel Boton 
294d022e35SMiguel Boton /*
304d022e35SMiguel Boton  * Power off function, if any
314d022e35SMiguel Boton  */
324d022e35SMiguel Boton void (*pm_power_off)(void);
334d022e35SMiguel Boton EXPORT_SYMBOL(pm_power_off);
344d022e35SMiguel Boton 
35ebdd561aSJan Beulich static const struct desc_ptr no_idt = {};
364d022e35SMiguel Boton static int reboot_mode;
378d00450dSEduardo Habkost enum reboot_type reboot_type = BOOT_KBD;
384d022e35SMiguel Boton int reboot_force;
394d022e35SMiguel Boton 
404d022e35SMiguel Boton #if defined(CONFIG_X86_32) && defined(CONFIG_SMP)
414d022e35SMiguel Boton static int reboot_cpu = -1;
424d022e35SMiguel Boton #endif
434d022e35SMiguel Boton 
44d176720dSEduardo Habkost /* This is set if we need to go through the 'emergency' path.
45d176720dSEduardo Habkost  * When machine_emergency_restart() is called, we may be on
46d176720dSEduardo Habkost  * an inconsistent state and won't be able to do a clean cleanup
47d176720dSEduardo Habkost  */
48d176720dSEduardo Habkost static int reboot_emergency;
49d176720dSEduardo Habkost 
5014d7ca5cSH. Peter Anvin /* This is set by the PCI code if either type 1 or type 2 PCI is detected */
5114d7ca5cSH. Peter Anvin bool port_cf9_safe = false;
5214d7ca5cSH. Peter Anvin 
5314d7ca5cSH. Peter Anvin /* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci]
544d022e35SMiguel Boton    warm   Don't set the cold reboot flag
554d022e35SMiguel Boton    cold   Set the cold reboot flag
564d022e35SMiguel Boton    bios   Reboot by jumping through the BIOS (only for X86_32)
574d022e35SMiguel Boton    smp    Reboot by executing reset on BSP or other CPU (only for X86_32)
584d022e35SMiguel Boton    triple Force a triple fault (init)
594d022e35SMiguel Boton    kbd    Use the keyboard controller. cold reset (default)
604d022e35SMiguel Boton    acpi   Use the RESET_REG in the FADT
614d022e35SMiguel Boton    efi    Use efi reset_system runtime service
6214d7ca5cSH. Peter Anvin    pci    Use the so-called "PCI reset register", CF9
634d022e35SMiguel Boton    force  Avoid anything that could hang.
644d022e35SMiguel Boton  */
654d022e35SMiguel Boton static int __init reboot_setup(char *str)
664d022e35SMiguel Boton {
674d022e35SMiguel Boton 	for (;;) {
684d022e35SMiguel Boton 		switch (*str) {
694d022e35SMiguel Boton 		case 'w':
704d022e35SMiguel Boton 			reboot_mode = 0x1234;
714d022e35SMiguel Boton 			break;
724d022e35SMiguel Boton 
734d022e35SMiguel Boton 		case 'c':
744d022e35SMiguel Boton 			reboot_mode = 0;
754d022e35SMiguel Boton 			break;
764d022e35SMiguel Boton 
774d022e35SMiguel Boton #ifdef CONFIG_X86_32
784d022e35SMiguel Boton #ifdef CONFIG_SMP
794d022e35SMiguel Boton 		case 's':
804d022e35SMiguel Boton 			if (isdigit(*(str+1))) {
814d022e35SMiguel Boton 				reboot_cpu = (int) (*(str+1) - '0');
824d022e35SMiguel Boton 				if (isdigit(*(str+2)))
834d022e35SMiguel Boton 					reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
844d022e35SMiguel Boton 			}
854d022e35SMiguel Boton 				/* we will leave sorting out the final value
864d022e35SMiguel Boton 				   when we are ready to reboot, since we might not
874d022e35SMiguel Boton 				   have set up boot_cpu_id or smp_num_cpu */
884d022e35SMiguel Boton 			break;
894d022e35SMiguel Boton #endif /* CONFIG_SMP */
904d022e35SMiguel Boton 
914d022e35SMiguel Boton 		case 'b':
924d022e35SMiguel Boton #endif
934d022e35SMiguel Boton 		case 'a':
944d022e35SMiguel Boton 		case 'k':
954d022e35SMiguel Boton 		case 't':
964d022e35SMiguel Boton 		case 'e':
9714d7ca5cSH. Peter Anvin 		case 'p':
984d022e35SMiguel Boton 			reboot_type = *str;
994d022e35SMiguel Boton 			break;
1004d022e35SMiguel Boton 
1014d022e35SMiguel Boton 		case 'f':
1024d022e35SMiguel Boton 			reboot_force = 1;
1034d022e35SMiguel Boton 			break;
1044d022e35SMiguel Boton 		}
1054d022e35SMiguel Boton 
1064d022e35SMiguel Boton 		str = strchr(str, ',');
1074d022e35SMiguel Boton 		if (str)
1084d022e35SMiguel Boton 			str++;
1094d022e35SMiguel Boton 		else
1104d022e35SMiguel Boton 			break;
1114d022e35SMiguel Boton 	}
1124d022e35SMiguel Boton 	return 1;
1134d022e35SMiguel Boton }
1144d022e35SMiguel Boton 
1154d022e35SMiguel Boton __setup("reboot=", reboot_setup);
1164d022e35SMiguel Boton 
1174d022e35SMiguel Boton 
1184d022e35SMiguel Boton #ifdef CONFIG_X86_32
1194d022e35SMiguel Boton /*
1204d022e35SMiguel Boton  * Reboot options and system auto-detection code provided by
1214d022e35SMiguel Boton  * Dell Inc. so their systems "just work". :-)
1224d022e35SMiguel Boton  */
1234d022e35SMiguel Boton 
1244d022e35SMiguel Boton /*
1254d022e35SMiguel Boton  * Some machines require the "reboot=b"  commandline option,
1264d022e35SMiguel Boton  * this quirk makes that automatic.
1274d022e35SMiguel Boton  */
1284d022e35SMiguel Boton static int __init set_bios_reboot(const struct dmi_system_id *d)
1294d022e35SMiguel Boton {
1304d022e35SMiguel Boton 	if (reboot_type != BOOT_BIOS) {
1314d022e35SMiguel Boton 		reboot_type = BOOT_BIOS;
1324d022e35SMiguel Boton 		printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
1334d022e35SMiguel Boton 	}
1344d022e35SMiguel Boton 	return 0;
1354d022e35SMiguel Boton }
1364d022e35SMiguel Boton 
1374d022e35SMiguel Boton static struct dmi_system_id __initdata reboot_dmi_table[] = {
1384d022e35SMiguel Boton 	{	/* Handle problems with rebooting on Dell E520's */
1394d022e35SMiguel Boton 		.callback = set_bios_reboot,
1404d022e35SMiguel Boton 		.ident = "Dell E520",
1414d022e35SMiguel Boton 		.matches = {
1424d022e35SMiguel Boton 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1434d022e35SMiguel Boton 			DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"),
1444d022e35SMiguel Boton 		},
1454d022e35SMiguel Boton 	},
1464d022e35SMiguel Boton 	{	/* Handle problems with rebooting on Dell 1300's */
1474d022e35SMiguel Boton 		.callback = set_bios_reboot,
1484d022e35SMiguel Boton 		.ident = "Dell PowerEdge 1300",
1494d022e35SMiguel Boton 		.matches = {
1504d022e35SMiguel Boton 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
1514d022e35SMiguel Boton 			DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
1524d022e35SMiguel Boton 		},
1534d022e35SMiguel Boton 	},
1544d022e35SMiguel Boton 	{	/* Handle problems with rebooting on Dell 300's */
1554d022e35SMiguel Boton 		.callback = set_bios_reboot,
1564d022e35SMiguel Boton 		.ident = "Dell PowerEdge 300",
1574d022e35SMiguel Boton 		.matches = {
1584d022e35SMiguel Boton 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
1594d022e35SMiguel Boton 			DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
1604d022e35SMiguel Boton 		},
1614d022e35SMiguel Boton 	},
1624d022e35SMiguel Boton 	{       /* Handle problems with rebooting on Dell Optiplex 745's SFF*/
1634d022e35SMiguel Boton 		.callback = set_bios_reboot,
1644d022e35SMiguel Boton 		.ident = "Dell OptiPlex 745",
1654d022e35SMiguel Boton 		.matches = {
1664d022e35SMiguel Boton 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1674d022e35SMiguel Boton 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
1684d022e35SMiguel Boton 		},
1694d022e35SMiguel Boton 	},
170fc115bf1SColeman Kane 	{       /* Handle problems with rebooting on Dell Optiplex 745's DFF*/
171fc115bf1SColeman Kane 		.callback = set_bios_reboot,
172fc115bf1SColeman Kane 		.ident = "Dell OptiPlex 745",
173fc115bf1SColeman Kane 		.matches = {
174fc115bf1SColeman Kane 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
175fc115bf1SColeman Kane 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
176fc115bf1SColeman Kane 			DMI_MATCH(DMI_BOARD_NAME, "0MM599"),
177fc115bf1SColeman Kane 		},
178fc115bf1SColeman Kane 	},
179fc1c8925SHeinz-Ado Arnolds 	{       /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */
180fc1c8925SHeinz-Ado Arnolds 		.callback = set_bios_reboot,
181fc1c8925SHeinz-Ado Arnolds 		.ident = "Dell OptiPlex 745",
182fc1c8925SHeinz-Ado Arnolds 		.matches = {
183fc1c8925SHeinz-Ado Arnolds 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
184fc1c8925SHeinz-Ado Arnolds 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
185fc1c8925SHeinz-Ado Arnolds 			DMI_MATCH(DMI_BOARD_NAME, "0KW626"),
186fc1c8925SHeinz-Ado Arnolds 		},
187fc1c8925SHeinz-Ado Arnolds 	},
188093bac15SSteve Conklin 	{   /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */
189093bac15SSteve Conklin 		.callback = set_bios_reboot,
190093bac15SSteve Conklin 		.ident = "Dell OptiPlex 330",
191093bac15SSteve Conklin 		.matches = {
192093bac15SSteve Conklin 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
193093bac15SSteve Conklin 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"),
194093bac15SSteve Conklin 			DMI_MATCH(DMI_BOARD_NAME, "0KP561"),
195093bac15SSteve Conklin 		},
196093bac15SSteve Conklin 	},
1974a4aca64SJean Delvare 	{   /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */
1984a4aca64SJean Delvare 		.callback = set_bios_reboot,
1994a4aca64SJean Delvare 		.ident = "Dell OptiPlex 360",
2004a4aca64SJean Delvare 		.matches = {
2014a4aca64SJean Delvare 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
2024a4aca64SJean Delvare 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 360"),
2034a4aca64SJean Delvare 			DMI_MATCH(DMI_BOARD_NAME, "0T656F"),
2044a4aca64SJean Delvare 		},
2054a4aca64SJean Delvare 	},
2064d022e35SMiguel Boton 	{	/* Handle problems with rebooting on Dell 2400's */
2074d022e35SMiguel Boton 		.callback = set_bios_reboot,
2084d022e35SMiguel Boton 		.ident = "Dell PowerEdge 2400",
2094d022e35SMiguel Boton 		.matches = {
2104d022e35SMiguel Boton 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2114d022e35SMiguel Boton 			DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
2124d022e35SMiguel Boton 		},
2134d022e35SMiguel Boton 	},
214fab3b58dSIngo Molnar 	{	/* Handle problems with rebooting on Dell T5400's */
215fab3b58dSIngo Molnar 		.callback = set_bios_reboot,
216fab3b58dSIngo Molnar 		.ident = "Dell Precision T5400",
217fab3b58dSIngo Molnar 		.matches = {
218fab3b58dSIngo Molnar 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
219fab3b58dSIngo Molnar 			DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T5400"),
220fab3b58dSIngo Molnar 		},
221fab3b58dSIngo Molnar 	},
2224d022e35SMiguel Boton 	{	/* Handle problems with rebooting on HP laptops */
2234d022e35SMiguel Boton 		.callback = set_bios_reboot,
2244d022e35SMiguel Boton 		.ident = "HP Compaq Laptop",
2254d022e35SMiguel Boton 		.matches = {
2264d022e35SMiguel Boton 			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
2274d022e35SMiguel Boton 			DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"),
2284d022e35SMiguel Boton 		},
2294d022e35SMiguel Boton 	},
230dd4124a8SLeann Ogasawara 	{	/* Handle problems with rebooting on Dell XPS710 */
231dd4124a8SLeann Ogasawara 		.callback = set_bios_reboot,
232dd4124a8SLeann Ogasawara 		.ident = "Dell XPS710",
233dd4124a8SLeann Ogasawara 		.matches = {
234dd4124a8SLeann Ogasawara 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
235dd4124a8SLeann Ogasawara 			DMI_MATCH(DMI_PRODUCT_NAME, "Dell XPS710"),
236dd4124a8SLeann Ogasawara 		},
237dd4124a8SLeann Ogasawara 	},
238c5da9a2bSAlan Cox 	{	/* Handle problems with rebooting on Dell DXP061 */
239c5da9a2bSAlan Cox 		.callback = set_bios_reboot,
240c5da9a2bSAlan Cox 		.ident = "Dell DXP061",
241c5da9a2bSAlan Cox 		.matches = {
242c5da9a2bSAlan Cox 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
243c5da9a2bSAlan Cox 			DMI_MATCH(DMI_PRODUCT_NAME, "Dell DXP061"),
244c5da9a2bSAlan Cox 		},
245c5da9a2bSAlan Cox 	},
24688dff493SZhang Rui 	{	/* Handle problems with rebooting on Sony VGN-Z540N */
24788dff493SZhang Rui 		.callback = set_bios_reboot,
24888dff493SZhang Rui 		.ident = "Sony VGN-Z540N",
24988dff493SZhang Rui 		.matches = {
25088dff493SZhang Rui 			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
25188dff493SZhang Rui 			DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"),
25288dff493SZhang Rui 		},
25388dff493SZhang Rui 	},
25477f32dfdSDenis Turischev 	{	/* Handle problems with rebooting on CompuLab SBC-FITPC2 */
25577f32dfdSDenis Turischev 		.callback = set_bios_reboot,
25677f32dfdSDenis Turischev 		.ident = "CompuLab SBC-FITPC2",
25777f32dfdSDenis Turischev 		.matches = {
25877f32dfdSDenis Turischev 			DMI_MATCH(DMI_SYS_VENDOR, "CompuLab"),
25977f32dfdSDenis Turischev 			DMI_MATCH(DMI_PRODUCT_NAME, "SBC-FITPC2"),
26077f32dfdSDenis Turischev 		},
26177f32dfdSDenis Turischev 	},
2624d022e35SMiguel Boton 	{ }
2634d022e35SMiguel Boton };
2644d022e35SMiguel Boton 
2654d022e35SMiguel Boton static int __init reboot_init(void)
2664d022e35SMiguel Boton {
2674d022e35SMiguel Boton 	dmi_check_system(reboot_dmi_table);
2684d022e35SMiguel Boton 	return 0;
2694d022e35SMiguel Boton }
2704d022e35SMiguel Boton core_initcall(reboot_init);
2714d022e35SMiguel Boton 
2724d022e35SMiguel Boton /* The following code and data reboots the machine by switching to real
2734d022e35SMiguel Boton    mode and jumping to the BIOS reset entry point, as if the CPU has
2744d022e35SMiguel Boton    really been reset.  The previous version asked the keyboard
2754d022e35SMiguel Boton    controller to pulse the CPU reset line, which is more thorough, but
2764d022e35SMiguel Boton    doesn't work with at least one type of 486 motherboard.  It is easy
2774d022e35SMiguel Boton    to stop this code working; hence the copious comments. */
278ebdd561aSJan Beulich static const unsigned long long
2794d022e35SMiguel Boton real_mode_gdt_entries [3] =
2804d022e35SMiguel Boton {
2814d022e35SMiguel Boton 	0x0000000000000000ULL,	/* Null descriptor */
282ebdd561aSJan Beulich 	0x00009b000000ffffULL,	/* 16-bit real-mode 64k code at 0x00000000 */
283ebdd561aSJan Beulich 	0x000093000100ffffULL	/* 16-bit real-mode 64k data at 0x00000100 */
2844d022e35SMiguel Boton };
2854d022e35SMiguel Boton 
286ebdd561aSJan Beulich static const struct desc_ptr
2874d022e35SMiguel Boton real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
2884d022e35SMiguel Boton real_mode_idt = { 0x3ff, 0 };
2894d022e35SMiguel Boton 
2904d022e35SMiguel Boton /* This is 16-bit protected mode code to disable paging and the cache,
2914d022e35SMiguel Boton    switch to real mode and jump to the BIOS reset code.
2924d022e35SMiguel Boton 
2934d022e35SMiguel Boton    The instruction that switches to real mode by writing to CR0 must be
2944d022e35SMiguel Boton    followed immediately by a far jump instruction, which set CS to a
2954d022e35SMiguel Boton    valid value for real mode, and flushes the prefetch queue to avoid
2964d022e35SMiguel Boton    running instructions that have already been decoded in protected
2974d022e35SMiguel Boton    mode.
2984d022e35SMiguel Boton 
2994d022e35SMiguel Boton    Clears all the flags except ET, especially PG (paging), PE
3004d022e35SMiguel Boton    (protected-mode enable) and TS (task switch for coprocessor state
3014d022e35SMiguel Boton    save).  Flushes the TLB after paging has been disabled.  Sets CD and
3024d022e35SMiguel Boton    NW, to disable the cache on a 486, and invalidates the cache.  This
3034d022e35SMiguel Boton    is more like the state of a 486 after reset.  I don't know if
3044d022e35SMiguel Boton    something else should be done for other chips.
3054d022e35SMiguel Boton 
3064d022e35SMiguel Boton    More could be done here to set up the registers as if a CPU reset had
3074d022e35SMiguel Boton    occurred; hopefully real BIOSs don't assume much. */
308ebdd561aSJan Beulich static const unsigned char real_mode_switch [] =
3094d022e35SMiguel Boton {
3104d022e35SMiguel Boton 	0x66, 0x0f, 0x20, 0xc0,			/*    movl  %cr0,%eax        */
3114d022e35SMiguel Boton 	0x66, 0x83, 0xe0, 0x11,			/*    andl  $0x00000011,%eax */
3124d022e35SMiguel Boton 	0x66, 0x0d, 0x00, 0x00, 0x00, 0x60,	/*    orl   $0x60000000,%eax */
3134d022e35SMiguel Boton 	0x66, 0x0f, 0x22, 0xc0,			/*    movl  %eax,%cr0        */
3144d022e35SMiguel Boton 	0x66, 0x0f, 0x22, 0xd8,			/*    movl  %eax,%cr3        */
3154d022e35SMiguel Boton 	0x66, 0x0f, 0x20, 0xc3,			/*    movl  %cr0,%ebx        */
3164d022e35SMiguel Boton 	0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60,	/*    andl  $0x60000000,%ebx */
3174d022e35SMiguel Boton 	0x74, 0x02,				/*    jz    f                */
3184d022e35SMiguel Boton 	0x0f, 0x09,				/*    wbinvd                 */
3194d022e35SMiguel Boton 	0x24, 0x10,				/* f: andb  $0x10,al         */
3204d022e35SMiguel Boton 	0x66, 0x0f, 0x22, 0xc0			/*    movl  %eax,%cr0        */
3214d022e35SMiguel Boton };
322ebdd561aSJan Beulich static const unsigned char jump_to_bios [] =
3234d022e35SMiguel Boton {
3244d022e35SMiguel Boton 	0xea, 0x00, 0x00, 0xff, 0xff		/*    ljmp  $0xffff,$0x0000  */
3254d022e35SMiguel Boton };
3264d022e35SMiguel Boton 
3274d022e35SMiguel Boton /*
3284d022e35SMiguel Boton  * Switch to real mode and then execute the code
3294d022e35SMiguel Boton  * specified by the code and length parameters.
3304d022e35SMiguel Boton  * We assume that length will aways be less that 100!
3314d022e35SMiguel Boton  */
332ebdd561aSJan Beulich void machine_real_restart(const unsigned char *code, int length)
3334d022e35SMiguel Boton {
3344d022e35SMiguel Boton 	local_irq_disable();
3354d022e35SMiguel Boton 
3364d022e35SMiguel Boton 	/* Write zero to CMOS register number 0x0f, which the BIOS POST
3374d022e35SMiguel Boton 	   routine will recognize as telling it to do a proper reboot.  (Well
3384d022e35SMiguel Boton 	   that's what this book in front of me says -- it may only apply to
3394d022e35SMiguel Boton 	   the Phoenix BIOS though, it's not clear).  At the same time,
3404d022e35SMiguel Boton 	   disable NMIs by setting the top bit in the CMOS address register,
3414d022e35SMiguel Boton 	   as we're about to do peculiar things to the CPU.  I'm not sure if
3424d022e35SMiguel Boton 	   `outb_p' is needed instead of just `outb'.  Use it to be on the
3434d022e35SMiguel Boton 	   safe side.  (Yes, CMOS_WRITE does outb_p's. -  Paul G.)
3444d022e35SMiguel Boton 	 */
3454d022e35SMiguel Boton 	spin_lock(&rtc_lock);
3464d022e35SMiguel Boton 	CMOS_WRITE(0x00, 0x8f);
3474d022e35SMiguel Boton 	spin_unlock(&rtc_lock);
3484d022e35SMiguel Boton 
3494d022e35SMiguel Boton 	/* Remap the kernel at virtual address zero, as well as offset zero
3504d022e35SMiguel Boton 	   from the kernel segment.  This assumes the kernel segment starts at
3514d022e35SMiguel Boton 	   virtual address PAGE_OFFSET. */
35268db065cSJeremy Fitzhardinge 	memcpy(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY,
3534d022e35SMiguel Boton 		sizeof(swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
3544d022e35SMiguel Boton 
3554d022e35SMiguel Boton 	/*
3564d022e35SMiguel Boton 	 * Use `swapper_pg_dir' as our page directory.
3574d022e35SMiguel Boton 	 */
3584d022e35SMiguel Boton 	load_cr3(swapper_pg_dir);
3594d022e35SMiguel Boton 
3604d022e35SMiguel Boton 	/* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
3614d022e35SMiguel Boton 	   this on booting to tell it to "Bypass memory test (also warm
3624d022e35SMiguel Boton 	   boot)".  This seems like a fairly standard thing that gets set by
3634d022e35SMiguel Boton 	   REBOOT.COM programs, and the previous reset routine did this
3644d022e35SMiguel Boton 	   too. */
3654d022e35SMiguel Boton 	*((unsigned short *)0x472) = reboot_mode;
3664d022e35SMiguel Boton 
3674d022e35SMiguel Boton 	/* For the switch to real mode, copy some code to low memory.  It has
3684d022e35SMiguel Boton 	   to be in the first 64k because it is running in 16-bit mode, and it
3694d022e35SMiguel Boton 	   has to have the same physical and virtual address, because it turns
3704d022e35SMiguel Boton 	   off paging.  Copy it near the end of the first page, out of the way
3714d022e35SMiguel Boton 	   of BIOS variables. */
3724d022e35SMiguel Boton 	memcpy((void *)(0x1000 - sizeof(real_mode_switch) - 100),
3734d022e35SMiguel Boton 		real_mode_switch, sizeof (real_mode_switch));
3744d022e35SMiguel Boton 	memcpy((void *)(0x1000 - 100), code, length);
3754d022e35SMiguel Boton 
3764d022e35SMiguel Boton 	/* Set up the IDT for real mode. */
3774d022e35SMiguel Boton 	load_idt(&real_mode_idt);
3784d022e35SMiguel Boton 
3794d022e35SMiguel Boton 	/* Set up a GDT from which we can load segment descriptors for real
3804d022e35SMiguel Boton 	   mode.  The GDT is not used in real mode; it is just needed here to
3814d022e35SMiguel Boton 	   prepare the descriptors. */
3824d022e35SMiguel Boton 	load_gdt(&real_mode_gdt);
3834d022e35SMiguel Boton 
3844d022e35SMiguel Boton 	/* Load the data segment registers, and thus the descriptors ready for
3854d022e35SMiguel Boton 	   real mode.  The base address of each segment is 0x100, 16 times the
3864d022e35SMiguel Boton 	   selector value being loaded here.  This is so that the segment
3874d022e35SMiguel Boton 	   registers don't have to be reloaded after switching to real mode:
3884d022e35SMiguel Boton 	   the values are consistent for real mode operation already. */
3894d022e35SMiguel Boton 	__asm__ __volatile__ ("movl $0x0010,%%eax\n"
3904d022e35SMiguel Boton 				"\tmovl %%eax,%%ds\n"
3914d022e35SMiguel Boton 				"\tmovl %%eax,%%es\n"
3924d022e35SMiguel Boton 				"\tmovl %%eax,%%fs\n"
3934d022e35SMiguel Boton 				"\tmovl %%eax,%%gs\n"
3944d022e35SMiguel Boton 				"\tmovl %%eax,%%ss" : : : "eax");
3954d022e35SMiguel Boton 
3964d022e35SMiguel Boton 	/* Jump to the 16-bit code that we copied earlier.  It disables paging
3974d022e35SMiguel Boton 	   and the cache, switches to real mode, and jumps to the BIOS reset
3984d022e35SMiguel Boton 	   entry point. */
3994d022e35SMiguel Boton 	__asm__ __volatile__ ("ljmp $0x0008,%0"
4004d022e35SMiguel Boton 				:
4014d022e35SMiguel Boton 				: "i" ((void *)(0x1000 - sizeof (real_mode_switch) - 100)));
4024d022e35SMiguel Boton }
4034d022e35SMiguel Boton #ifdef CONFIG_APM_MODULE
4044d022e35SMiguel Boton EXPORT_SYMBOL(machine_real_restart);
4054d022e35SMiguel Boton #endif
4064d022e35SMiguel Boton 
4074d022e35SMiguel Boton #endif /* CONFIG_X86_32 */
4084d022e35SMiguel Boton 
4096c6c51e4SPaul Mackerras /*
410498cdbfbSOzan Çağlayan  * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
4116c6c51e4SPaul Mackerras  */
4126c6c51e4SPaul Mackerras static int __init set_pci_reboot(const struct dmi_system_id *d)
4136c6c51e4SPaul Mackerras {
4146c6c51e4SPaul Mackerras 	if (reboot_type != BOOT_CF9) {
4156c6c51e4SPaul Mackerras 		reboot_type = BOOT_CF9;
4166c6c51e4SPaul Mackerras 		printk(KERN_INFO "%s series board detected. "
4176c6c51e4SPaul Mackerras 		       "Selecting PCI-method for reboots.\n", d->ident);
4186c6c51e4SPaul Mackerras 	}
4196c6c51e4SPaul Mackerras 	return 0;
4206c6c51e4SPaul Mackerras }
4216c6c51e4SPaul Mackerras 
4226c6c51e4SPaul Mackerras static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
4233e03bbeaSShunichi Fuji 	{	/* Handle problems with rebooting on Apple MacBook5 */
4246c6c51e4SPaul Mackerras 		.callback = set_pci_reboot,
4253e03bbeaSShunichi Fuji 		.ident = "Apple MacBook5",
4266c6c51e4SPaul Mackerras 		.matches = {
4276c6c51e4SPaul Mackerras 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
4283e03bbeaSShunichi Fuji 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"),
4296c6c51e4SPaul Mackerras 		},
4306c6c51e4SPaul Mackerras 	},
4313e03bbeaSShunichi Fuji 	{	/* Handle problems with rebooting on Apple MacBookPro5 */
432498cdbfbSOzan Çağlayan 		.callback = set_pci_reboot,
4333e03bbeaSShunichi Fuji 		.ident = "Apple MacBookPro5",
434498cdbfbSOzan Çağlayan 		.matches = {
435498cdbfbSOzan Çağlayan 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
4363e03bbeaSShunichi Fuji 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"),
437498cdbfbSOzan Çağlayan 		},
438498cdbfbSOzan Çağlayan 	},
4396c6c51e4SPaul Mackerras 	{ }
4406c6c51e4SPaul Mackerras };
4416c6c51e4SPaul Mackerras 
4426c6c51e4SPaul Mackerras static int __init pci_reboot_init(void)
4436c6c51e4SPaul Mackerras {
4446c6c51e4SPaul Mackerras 	dmi_check_system(pci_reboot_dmi_table);
4456c6c51e4SPaul Mackerras 	return 0;
4466c6c51e4SPaul Mackerras }
4476c6c51e4SPaul Mackerras core_initcall(pci_reboot_init);
4486c6c51e4SPaul Mackerras 
4494d022e35SMiguel Boton static inline void kb_wait(void)
4504d022e35SMiguel Boton {
4514d022e35SMiguel Boton 	int i;
4524d022e35SMiguel Boton 
453c84d6af8SAlan Cox 	for (i = 0; i < 0x10000; i++) {
454c84d6af8SAlan Cox 		if ((inb(0x64) & 0x02) == 0)
4554d022e35SMiguel Boton 			break;
456c84d6af8SAlan Cox 		udelay(2);
457c84d6af8SAlan Cox 	}
4584d022e35SMiguel Boton }
4594d022e35SMiguel Boton 
460d176720dSEduardo Habkost static void vmxoff_nmi(int cpu, struct die_args *args)
461d176720dSEduardo Habkost {
462d176720dSEduardo Habkost 	cpu_emergency_vmxoff();
463d176720dSEduardo Habkost }
464d176720dSEduardo Habkost 
465d176720dSEduardo Habkost /* Use NMIs as IPIs to tell all CPUs to disable virtualization
466d176720dSEduardo Habkost  */
467d176720dSEduardo Habkost static void emergency_vmx_disable_all(void)
468d176720dSEduardo Habkost {
469d176720dSEduardo Habkost 	/* Just make sure we won't change CPUs while doing this */
470d176720dSEduardo Habkost 	local_irq_disable();
471d176720dSEduardo Habkost 
472d176720dSEduardo Habkost 	/* We need to disable VMX on all CPUs before rebooting, otherwise
473d176720dSEduardo Habkost 	 * we risk hanging up the machine, because the CPU ignore INIT
474d176720dSEduardo Habkost 	 * signals when VMX is enabled.
475d176720dSEduardo Habkost 	 *
476d176720dSEduardo Habkost 	 * We can't take any locks and we may be on an inconsistent
477d176720dSEduardo Habkost 	 * state, so we use NMIs as IPIs to tell the other CPUs to disable
478d176720dSEduardo Habkost 	 * VMX and halt.
479d176720dSEduardo Habkost 	 *
480d176720dSEduardo Habkost 	 * For safety, we will avoid running the nmi_shootdown_cpus()
481d176720dSEduardo Habkost 	 * stuff unnecessarily, but we don't have a way to check
482d176720dSEduardo Habkost 	 * if other CPUs have VMX enabled. So we will call it only if the
483d176720dSEduardo Habkost 	 * CPU we are running on has VMX enabled.
484d176720dSEduardo Habkost 	 *
485d176720dSEduardo Habkost 	 * We will miss cases where VMX is not enabled on all CPUs. This
486d176720dSEduardo Habkost 	 * shouldn't do much harm because KVM always enable VMX on all
487d176720dSEduardo Habkost 	 * CPUs anyway. But we can miss it on the small window where KVM
488d176720dSEduardo Habkost 	 * is still enabling VMX.
489d176720dSEduardo Habkost 	 */
490d176720dSEduardo Habkost 	if (cpu_has_vmx() && cpu_vmx_enabled()) {
491d176720dSEduardo Habkost 		/* Disable VMX on this CPU.
492d176720dSEduardo Habkost 		 */
493d176720dSEduardo Habkost 		cpu_vmxoff();
494d176720dSEduardo Habkost 
495d176720dSEduardo Habkost 		/* Halt and disable VMX on the other CPUs */
496d176720dSEduardo Habkost 		nmi_shootdown_cpus(vmxoff_nmi);
497d176720dSEduardo Habkost 
498d176720dSEduardo Habkost 	}
499d176720dSEduardo Habkost }
500d176720dSEduardo Habkost 
501d176720dSEduardo Habkost 
5027432d149SIngo Molnar void __attribute__((weak)) mach_reboot_fixups(void)
5037432d149SIngo Molnar {
5047432d149SIngo Molnar }
5057432d149SIngo Molnar 
506416e2d63SJody Belka static void native_machine_emergency_restart(void)
5074d022e35SMiguel Boton {
5084d022e35SMiguel Boton 	int i;
5094d022e35SMiguel Boton 
510d176720dSEduardo Habkost 	if (reboot_emergency)
511d176720dSEduardo Habkost 		emergency_vmx_disable_all();
512d176720dSEduardo Habkost 
513840c2bafSJoseph Cihula 	tboot_shutdown(TB_SHUTDOWN_REBOOT);
514840c2bafSJoseph Cihula 
5154d022e35SMiguel Boton 	/* Tell the BIOS if we want cold or warm reboot */
5164d022e35SMiguel Boton 	*((unsigned short *)__va(0x472)) = reboot_mode;
5174d022e35SMiguel Boton 
5184d022e35SMiguel Boton 	for (;;) {
5194d022e35SMiguel Boton 		/* Could also try the reset bit in the Hammer NB */
5204d022e35SMiguel Boton 		switch (reboot_type) {
5214d022e35SMiguel Boton 		case BOOT_KBD:
5227432d149SIngo Molnar 			mach_reboot_fixups(); /* for board specific fixups */
5237432d149SIngo Molnar 
5244d022e35SMiguel Boton 			for (i = 0; i < 10; i++) {
5254d022e35SMiguel Boton 				kb_wait();
5264d022e35SMiguel Boton 				udelay(50);
5274d022e35SMiguel Boton 				outb(0xfe, 0x64); /* pulse reset low */
5284d022e35SMiguel Boton 				udelay(50);
5294d022e35SMiguel Boton 			}
5304d022e35SMiguel Boton 
5314d022e35SMiguel Boton 		case BOOT_TRIPLE:
532ebdd561aSJan Beulich 			load_idt(&no_idt);
5334d022e35SMiguel Boton 			__asm__ __volatile__("int3");
5344d022e35SMiguel Boton 
5354d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5364d022e35SMiguel Boton 			break;
5374d022e35SMiguel Boton 
5384d022e35SMiguel Boton #ifdef CONFIG_X86_32
5394d022e35SMiguel Boton 		case BOOT_BIOS:
5404d022e35SMiguel Boton 			machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
5414d022e35SMiguel Boton 
5424d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5434d022e35SMiguel Boton 			break;
5444d022e35SMiguel Boton #endif
5454d022e35SMiguel Boton 
5464d022e35SMiguel Boton 		case BOOT_ACPI:
5474d022e35SMiguel Boton 			acpi_reboot();
5484d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5494d022e35SMiguel Boton 			break;
5504d022e35SMiguel Boton 
5514d022e35SMiguel Boton 		case BOOT_EFI:
5524d022e35SMiguel Boton 			if (efi_enabled)
55314d7ca5cSH. Peter Anvin 				efi.reset_system(reboot_mode ?
55414d7ca5cSH. Peter Anvin 						 EFI_RESET_WARM :
55514d7ca5cSH. Peter Anvin 						 EFI_RESET_COLD,
5564d022e35SMiguel Boton 						 EFI_SUCCESS, 0, NULL);
557b47b9288SH. Peter Anvin 			reboot_type = BOOT_KBD;
55814d7ca5cSH. Peter Anvin 			break;
5594d022e35SMiguel Boton 
56014d7ca5cSH. Peter Anvin 		case BOOT_CF9:
56114d7ca5cSH. Peter Anvin 			port_cf9_safe = true;
56214d7ca5cSH. Peter Anvin 			/* fall through */
56314d7ca5cSH. Peter Anvin 
56414d7ca5cSH. Peter Anvin 		case BOOT_CF9_COND:
56514d7ca5cSH. Peter Anvin 			if (port_cf9_safe) {
56614d7ca5cSH. Peter Anvin 				u8 cf9 = inb(0xcf9) & ~6;
56714d7ca5cSH. Peter Anvin 				outb(cf9|2, 0xcf9); /* Request hard reset */
56814d7ca5cSH. Peter Anvin 				udelay(50);
56914d7ca5cSH. Peter Anvin 				outb(cf9|6, 0xcf9); /* Actually do the reset */
57014d7ca5cSH. Peter Anvin 				udelay(50);
57114d7ca5cSH. Peter Anvin 			}
5724d022e35SMiguel Boton 			reboot_type = BOOT_KBD;
5734d022e35SMiguel Boton 			break;
5744d022e35SMiguel Boton 		}
5754d022e35SMiguel Boton 	}
5764d022e35SMiguel Boton }
5774d022e35SMiguel Boton 
5783c62c625SGlauber Costa void native_machine_shutdown(void)
5794d022e35SMiguel Boton {
5804d022e35SMiguel Boton 	/* Stop the cpus and apics */
5814d022e35SMiguel Boton #ifdef CONFIG_SMP
5824d022e35SMiguel Boton 
5834d022e35SMiguel Boton 	/* The boot cpu is always logical cpu 0 */
58465c01184SMike Travis 	int reboot_cpu_id = 0;
5854d022e35SMiguel Boton 
5864d022e35SMiguel Boton #ifdef CONFIG_X86_32
5874d022e35SMiguel Boton 	/* See if there has been given a command line override */
5889628937dSMike Travis 	if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) &&
5890bc3cc03SMike Travis 		cpu_online(reboot_cpu))
5904d022e35SMiguel Boton 		reboot_cpu_id = reboot_cpu;
5914d022e35SMiguel Boton #endif
5924d022e35SMiguel Boton 
5934d022e35SMiguel Boton 	/* Make certain the cpu I'm about to reboot on is online */
5940bc3cc03SMike Travis 	if (!cpu_online(reboot_cpu_id))
5954d022e35SMiguel Boton 		reboot_cpu_id = smp_processor_id();
5964d022e35SMiguel Boton 
5974d022e35SMiguel Boton 	/* Make certain I only run on the appropriate processor */
5989628937dSMike Travis 	set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id));
5994d022e35SMiguel Boton 
6004d022e35SMiguel Boton 	/* O.K Now that I'm on the appropriate processor,
6014d022e35SMiguel Boton 	 * stop all of the others.
6024d022e35SMiguel Boton 	 */
6034d022e35SMiguel Boton 	smp_send_stop();
6044d022e35SMiguel Boton #endif
6054d022e35SMiguel Boton 
6064d022e35SMiguel Boton 	lapic_shutdown();
6074d022e35SMiguel Boton 
6084d022e35SMiguel Boton #ifdef CONFIG_X86_IO_APIC
6094d022e35SMiguel Boton 	disable_IO_APIC();
6104d022e35SMiguel Boton #endif
6114d022e35SMiguel Boton 
6124d022e35SMiguel Boton #ifdef CONFIG_HPET_TIMER
6134d022e35SMiguel Boton 	hpet_disable();
6144d022e35SMiguel Boton #endif
6154d022e35SMiguel Boton 
6164d022e35SMiguel Boton #ifdef CONFIG_X86_64
6174d022e35SMiguel Boton 	pci_iommu_shutdown();
6184d022e35SMiguel Boton #endif
6194d022e35SMiguel Boton }
6204d022e35SMiguel Boton 
621d176720dSEduardo Habkost static void __machine_emergency_restart(int emergency)
622d176720dSEduardo Habkost {
623d176720dSEduardo Habkost 	reboot_emergency = emergency;
624d176720dSEduardo Habkost 	machine_ops.emergency_restart();
625d176720dSEduardo Habkost }
626d176720dSEduardo Habkost 
627416e2d63SJody Belka static void native_machine_restart(char *__unused)
6284d022e35SMiguel Boton {
6294d022e35SMiguel Boton 	printk("machine restart\n");
6304d022e35SMiguel Boton 
6314d022e35SMiguel Boton 	if (!reboot_force)
6324d022e35SMiguel Boton 		machine_shutdown();
633d176720dSEduardo Habkost 	__machine_emergency_restart(0);
6344d022e35SMiguel Boton }
6354d022e35SMiguel Boton 
636416e2d63SJody Belka static void native_machine_halt(void)
6374d022e35SMiguel Boton {
638d3ec5caeSIvan Vecera 	/* stop other cpus and apics */
639d3ec5caeSIvan Vecera 	machine_shutdown();
640d3ec5caeSIvan Vecera 
641840c2bafSJoseph Cihula 	tboot_shutdown(TB_SHUTDOWN_HALT);
642840c2bafSJoseph Cihula 
643d3ec5caeSIvan Vecera 	/* stop this cpu */
644d3ec5caeSIvan Vecera 	stop_this_cpu(NULL);
6454d022e35SMiguel Boton }
6464d022e35SMiguel Boton 
647416e2d63SJody Belka static void native_machine_power_off(void)
6484d022e35SMiguel Boton {
6494d022e35SMiguel Boton 	if (pm_power_off) {
6504d022e35SMiguel Boton 		if (!reboot_force)
6514d022e35SMiguel Boton 			machine_shutdown();
6524d022e35SMiguel Boton 		pm_power_off();
6534d022e35SMiguel Boton 	}
654840c2bafSJoseph Cihula 	/* a fallback in case there is no PM info available */
655840c2bafSJoseph Cihula 	tboot_shutdown(TB_SHUTDOWN_HALT);
6564d022e35SMiguel Boton }
6574d022e35SMiguel Boton 
6584d022e35SMiguel Boton struct machine_ops machine_ops = {
659416e2d63SJody Belka 	.power_off = native_machine_power_off,
660416e2d63SJody Belka 	.shutdown = native_machine_shutdown,
661416e2d63SJody Belka 	.emergency_restart = native_machine_emergency_restart,
662416e2d63SJody Belka 	.restart = native_machine_restart,
663ed23dc6fSGlauber Costa 	.halt = native_machine_halt,
664ed23dc6fSGlauber Costa #ifdef CONFIG_KEXEC
665ed23dc6fSGlauber Costa 	.crash_shutdown = native_machine_crash_shutdown,
666ed23dc6fSGlauber Costa #endif
6674d022e35SMiguel Boton };
668416e2d63SJody Belka 
669416e2d63SJody Belka void machine_power_off(void)
670416e2d63SJody Belka {
671416e2d63SJody Belka 	machine_ops.power_off();
672416e2d63SJody Belka }
673416e2d63SJody Belka 
674416e2d63SJody Belka void machine_shutdown(void)
675416e2d63SJody Belka {
676416e2d63SJody Belka 	machine_ops.shutdown();
677416e2d63SJody Belka }
678416e2d63SJody Belka 
679416e2d63SJody Belka void machine_emergency_restart(void)
680416e2d63SJody Belka {
681d176720dSEduardo Habkost 	__machine_emergency_restart(1);
682416e2d63SJody Belka }
683416e2d63SJody Belka 
684416e2d63SJody Belka void machine_restart(char *cmd)
685416e2d63SJody Belka {
686416e2d63SJody Belka 	machine_ops.restart(cmd);
687416e2d63SJody Belka }
688416e2d63SJody Belka 
689416e2d63SJody Belka void machine_halt(void)
690416e2d63SJody Belka {
691416e2d63SJody Belka 	machine_ops.halt();
692416e2d63SJody Belka }
693416e2d63SJody Belka 
694ed23dc6fSGlauber Costa #ifdef CONFIG_KEXEC
695ed23dc6fSGlauber Costa void machine_crash_shutdown(struct pt_regs *regs)
696ed23dc6fSGlauber Costa {
697ed23dc6fSGlauber Costa 	machine_ops.crash_shutdown(regs);
698ed23dc6fSGlauber Costa }
699ed23dc6fSGlauber Costa #endif
7002ddded21SEduardo Habkost 
7012ddded21SEduardo Habkost 
702bb8dd270SEduardo Habkost #if defined(CONFIG_SMP)
7032ddded21SEduardo Habkost 
7042ddded21SEduardo Habkost /* This keeps a track of which one is crashing cpu. */
7052ddded21SEduardo Habkost static int crashing_cpu;
7062ddded21SEduardo Habkost static nmi_shootdown_cb shootdown_callback;
7072ddded21SEduardo Habkost 
7082ddded21SEduardo Habkost static atomic_t waiting_for_crash_ipi;
7092ddded21SEduardo Habkost 
7102ddded21SEduardo Habkost static int crash_nmi_callback(struct notifier_block *self,
7112ddded21SEduardo Habkost 			unsigned long val, void *data)
7122ddded21SEduardo Habkost {
7132ddded21SEduardo Habkost 	int cpu;
7142ddded21SEduardo Habkost 
7152ddded21SEduardo Habkost 	if (val != DIE_NMI_IPI)
7162ddded21SEduardo Habkost 		return NOTIFY_OK;
7172ddded21SEduardo Habkost 
7182ddded21SEduardo Habkost 	cpu = raw_smp_processor_id();
7192ddded21SEduardo Habkost 
7202ddded21SEduardo Habkost 	/* Don't do anything if this handler is invoked on crashing cpu.
7212ddded21SEduardo Habkost 	 * Otherwise, system will completely hang. Crashing cpu can get
7222ddded21SEduardo Habkost 	 * an NMI if system was initially booted with nmi_watchdog parameter.
7232ddded21SEduardo Habkost 	 */
7242ddded21SEduardo Habkost 	if (cpu == crashing_cpu)
7252ddded21SEduardo Habkost 		return NOTIFY_STOP;
7262ddded21SEduardo Habkost 	local_irq_disable();
7272ddded21SEduardo Habkost 
7282ddded21SEduardo Habkost 	shootdown_callback(cpu, (struct die_args *)data);
7292ddded21SEduardo Habkost 
7302ddded21SEduardo Habkost 	atomic_dec(&waiting_for_crash_ipi);
7312ddded21SEduardo Habkost 	/* Assume hlt works */
7322ddded21SEduardo Habkost 	halt();
7332ddded21SEduardo Habkost 	for (;;)
7342ddded21SEduardo Habkost 		cpu_relax();
7352ddded21SEduardo Habkost 
7362ddded21SEduardo Habkost 	return 1;
7372ddded21SEduardo Habkost }
7382ddded21SEduardo Habkost 
7392ddded21SEduardo Habkost static void smp_send_nmi_allbutself(void)
7402ddded21SEduardo Habkost {
741dac5f412SIngo Molnar 	apic->send_IPI_allbutself(NMI_VECTOR);
7422ddded21SEduardo Habkost }
7432ddded21SEduardo Habkost 
7442ddded21SEduardo Habkost static struct notifier_block crash_nmi_nb = {
7452ddded21SEduardo Habkost 	.notifier_call = crash_nmi_callback,
7462ddded21SEduardo Habkost };
7472ddded21SEduardo Habkost 
748bb8dd270SEduardo Habkost /* Halt all other CPUs, calling the specified function on each of them
749bb8dd270SEduardo Habkost  *
750bb8dd270SEduardo Habkost  * This function can be used to halt all other CPUs on crash
751bb8dd270SEduardo Habkost  * or emergency reboot time. The function passed as parameter
752bb8dd270SEduardo Habkost  * will be called inside a NMI handler on all CPUs.
753bb8dd270SEduardo Habkost  */
7542ddded21SEduardo Habkost void nmi_shootdown_cpus(nmi_shootdown_cb callback)
7552ddded21SEduardo Habkost {
7562ddded21SEduardo Habkost 	unsigned long msecs;
757c415b3dcSEduardo Habkost 	local_irq_disable();
7582ddded21SEduardo Habkost 
7592ddded21SEduardo Habkost 	/* Make a note of crashing cpu. Will be used in NMI callback.*/
7602ddded21SEduardo Habkost 	crashing_cpu = safe_smp_processor_id();
7612ddded21SEduardo Habkost 
7622ddded21SEduardo Habkost 	shootdown_callback = callback;
7632ddded21SEduardo Habkost 
7642ddded21SEduardo Habkost 	atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
7652ddded21SEduardo Habkost 	/* Would it be better to replace the trap vector here? */
7662ddded21SEduardo Habkost 	if (register_die_notifier(&crash_nmi_nb))
7672ddded21SEduardo Habkost 		return;		/* return what? */
7682ddded21SEduardo Habkost 	/* Ensure the new callback function is set before sending
7692ddded21SEduardo Habkost 	 * out the NMI
7702ddded21SEduardo Habkost 	 */
7712ddded21SEduardo Habkost 	wmb();
7722ddded21SEduardo Habkost 
7732ddded21SEduardo Habkost 	smp_send_nmi_allbutself();
7742ddded21SEduardo Habkost 
7752ddded21SEduardo Habkost 	msecs = 1000; /* Wait at most a second for the other cpus to stop */
7762ddded21SEduardo Habkost 	while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
7772ddded21SEduardo Habkost 		mdelay(1);
7782ddded21SEduardo Habkost 		msecs--;
7792ddded21SEduardo Habkost 	}
7802ddded21SEduardo Habkost 
7812ddded21SEduardo Habkost 	/* Leave the nmi callback set */
7822ddded21SEduardo Habkost }
783bb8dd270SEduardo Habkost #else /* !CONFIG_SMP */
784bb8dd270SEduardo Habkost void nmi_shootdown_cpus(nmi_shootdown_cb callback)
785bb8dd270SEduardo Habkost {
786bb8dd270SEduardo Habkost 	/* No other CPUs to shoot down */
787bb8dd270SEduardo Habkost }
7882ddded21SEduardo Habkost #endif
789