aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/reboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/reboot.c')
-rw-r--r--arch/x86/kernel/reboot.c189
1 files changed, 182 insertions, 7 deletions
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index cc5a2545dd41..2b46eb41643b 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -12,6 +12,8 @@
12#include <asm/proto.h> 12#include <asm/proto.h>
13#include <asm/reboot_fixups.h> 13#include <asm/reboot_fixups.h>
14#include <asm/reboot.h> 14#include <asm/reboot.h>
15#include <asm/pci_x86.h>
16#include <asm/virtext.h>
15 17
16#ifdef CONFIG_X86_32 18#ifdef CONFIG_X86_32
17# include <linux/dmi.h> 19# include <linux/dmi.h>
@@ -21,6 +23,8 @@
21# include <asm/iommu.h> 23# include <asm/iommu.h>
22#endif 24#endif
23 25
26#include <mach_ipi.h>
27
24/* 28/*
25 * Power off function, if any 29 * Power off function, if any
26 */ 30 */
@@ -36,7 +40,16 @@ int reboot_force;
36static int reboot_cpu = -1; 40static int reboot_cpu = -1;
37#endif 41#endif
38 42
39/* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] 43/* This is set if we need to go through the 'emergency' path.
44 * When machine_emergency_restart() is called, we may be on
45 * an inconsistent state and won't be able to do a clean cleanup
46 */
47static int reboot_emergency;
48
49/* This is set by the PCI code if either type 1 or type 2 PCI is detected */
50bool port_cf9_safe = false;
51
52/* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci]
40 warm Don't set the cold reboot flag 53 warm Don't set the cold reboot flag
41 cold Set the cold reboot flag 54 cold Set the cold reboot flag
42 bios Reboot by jumping through the BIOS (only for X86_32) 55 bios Reboot by jumping through the BIOS (only for X86_32)
@@ -45,6 +58,7 @@ static int reboot_cpu = -1;
45 kbd Use the keyboard controller. cold reset (default) 58 kbd Use the keyboard controller. cold reset (default)
46 acpi Use the RESET_REG in the FADT 59 acpi Use the RESET_REG in the FADT
47 efi Use efi reset_system runtime service 60 efi Use efi reset_system runtime service
61 pci Use the so-called "PCI reset register", CF9
48 force Avoid anything that could hang. 62 force Avoid anything that could hang.
49 */ 63 */
50static int __init reboot_setup(char *str) 64static int __init reboot_setup(char *str)
@@ -79,6 +93,7 @@ static int __init reboot_setup(char *str)
79 case 'k': 93 case 'k':
80 case 't': 94 case 't':
81 case 'e': 95 case 'e':
96 case 'p':
82 reboot_type = *str; 97 reboot_type = *str;
83 break; 98 break;
84 99
@@ -360,6 +375,48 @@ static inline void kb_wait(void)
360 } 375 }
361} 376}
362 377
378static void vmxoff_nmi(int cpu, struct die_args *args)
379{
380 cpu_emergency_vmxoff();
381}
382
383/* Use NMIs as IPIs to tell all CPUs to disable virtualization
384 */
385static void emergency_vmx_disable_all(void)
386{
387 /* Just make sure we won't change CPUs while doing this */
388 local_irq_disable();
389
390 /* We need to disable VMX on all CPUs before rebooting, otherwise
391 * we risk hanging up the machine, because the CPU ignore INIT
392 * signals when VMX is enabled.
393 *
394 * We can't take any locks and we may be on an inconsistent
395 * state, so we use NMIs as IPIs to tell the other CPUs to disable
396 * VMX and halt.
397 *
398 * For safety, we will avoid running the nmi_shootdown_cpus()
399 * stuff unnecessarily, but we don't have a way to check
400 * if other CPUs have VMX enabled. So we will call it only if the
401 * CPU we are running on has VMX enabled.
402 *
403 * We will miss cases where VMX is not enabled on all CPUs. This
404 * shouldn't do much harm because KVM always enable VMX on all
405 * CPUs anyway. But we can miss it on the small window where KVM
406 * is still enabling VMX.
407 */
408 if (cpu_has_vmx() && cpu_vmx_enabled()) {
409 /* Disable VMX on this CPU.
410 */
411 cpu_vmxoff();
412
413 /* Halt and disable VMX on the other CPUs */
414 nmi_shootdown_cpus(vmxoff_nmi);
415
416 }
417}
418
419
363void __attribute__((weak)) mach_reboot_fixups(void) 420void __attribute__((weak)) mach_reboot_fixups(void)
364{ 421{
365} 422}
@@ -368,6 +425,9 @@ static void native_machine_emergency_restart(void)
368{ 425{
369 int i; 426 int i;
370 427
428 if (reboot_emergency)
429 emergency_vmx_disable_all();
430
371 /* Tell the BIOS if we want cold or warm reboot */ 431 /* Tell the BIOS if we want cold or warm reboot */
372 *((unsigned short *)__va(0x472)) = reboot_mode; 432 *((unsigned short *)__va(0x472)) = reboot_mode;
373 433
@@ -404,12 +464,27 @@ static void native_machine_emergency_restart(void)
404 reboot_type = BOOT_KBD; 464 reboot_type = BOOT_KBD;
405 break; 465 break;
406 466
407
408 case BOOT_EFI: 467 case BOOT_EFI:
409 if (efi_enabled) 468 if (efi_enabled)
410 efi.reset_system(reboot_mode ? EFI_RESET_WARM : EFI_RESET_COLD, 469 efi.reset_system(reboot_mode ?
470 EFI_RESET_WARM :
471 EFI_RESET_COLD,
411 EFI_SUCCESS, 0, NULL); 472 EFI_SUCCESS, 0, NULL);
473 reboot_type = BOOT_KBD;
474 break;
475
476 case BOOT_CF9:
477 port_cf9_safe = true;
478 /* fall through */
412 479
480 case BOOT_CF9_COND:
481 if (port_cf9_safe) {
482 u8 cf9 = inb(0xcf9) & ~6;
483 outb(cf9|2, 0xcf9); /* Request hard reset */
484 udelay(50);
485 outb(cf9|6, 0xcf9); /* Actually do the reset */
486 udelay(50);
487 }
413 reboot_type = BOOT_KBD; 488 reboot_type = BOOT_KBD;
414 break; 489 break;
415 } 490 }
@@ -426,7 +501,7 @@ void native_machine_shutdown(void)
426 501
427#ifdef CONFIG_X86_32 502#ifdef CONFIG_X86_32
428 /* See if there has been given a command line override */ 503 /* See if there has been given a command line override */
429 if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) && 504 if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) &&
430 cpu_online(reboot_cpu)) 505 cpu_online(reboot_cpu))
431 reboot_cpu_id = reboot_cpu; 506 reboot_cpu_id = reboot_cpu;
432#endif 507#endif
@@ -436,7 +511,7 @@ void native_machine_shutdown(void)
436 reboot_cpu_id = smp_processor_id(); 511 reboot_cpu_id = smp_processor_id();
437 512
438 /* Make certain I only run on the appropriate processor */ 513 /* Make certain I only run on the appropriate processor */
439 set_cpus_allowed_ptr(current, &cpumask_of_cpu(reboot_cpu_id)); 514 set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id));
440 515
441 /* O.K Now that I'm on the appropriate processor, 516 /* O.K Now that I'm on the appropriate processor,
442 * stop all of the others. 517 * stop all of the others.
@@ -459,17 +534,28 @@ void native_machine_shutdown(void)
459#endif 534#endif
460} 535}
461 536
537static void __machine_emergency_restart(int emergency)
538{
539 reboot_emergency = emergency;
540 machine_ops.emergency_restart();
541}
542
462static void native_machine_restart(char *__unused) 543static void native_machine_restart(char *__unused)
463{ 544{
464 printk("machine restart\n"); 545 printk("machine restart\n");
465 546
466 if (!reboot_force) 547 if (!reboot_force)
467 machine_shutdown(); 548 machine_shutdown();
468 machine_emergency_restart(); 549 __machine_emergency_restart(0);
469} 550}
470 551
471static void native_machine_halt(void) 552static void native_machine_halt(void)
472{ 553{
554 /* stop other cpus and apics */
555 machine_shutdown();
556
557 /* stop this cpu */
558 stop_this_cpu(NULL);
473} 559}
474 560
475static void native_machine_power_off(void) 561static void native_machine_power_off(void)
@@ -504,7 +590,7 @@ void machine_shutdown(void)
504 590
505void machine_emergency_restart(void) 591void machine_emergency_restart(void)
506{ 592{
507 machine_ops.emergency_restart(); 593 __machine_emergency_restart(1);
508} 594}
509 595
510void machine_restart(char *cmd) 596void machine_restart(char *cmd)
@@ -523,3 +609,92 @@ void machine_crash_shutdown(struct pt_regs *regs)
523 machine_ops.crash_shutdown(regs); 609 machine_ops.crash_shutdown(regs);
524} 610}
525#endif 611#endif
612
613
614#if defined(CONFIG_SMP)
615
616/* This keeps a track of which one is crashing cpu. */
617static int crashing_cpu;
618static nmi_shootdown_cb shootdown_callback;
619
620static atomic_t waiting_for_crash_ipi;
621
622static int crash_nmi_callback(struct notifier_block *self,
623 unsigned long val, void *data)
624{
625 int cpu;
626
627 if (val != DIE_NMI_IPI)
628 return NOTIFY_OK;
629
630 cpu = raw_smp_processor_id();
631
632 /* Don't do anything if this handler is invoked on crashing cpu.
633 * Otherwise, system will completely hang. Crashing cpu can get
634 * an NMI if system was initially booted with nmi_watchdog parameter.
635 */
636 if (cpu == crashing_cpu)
637 return NOTIFY_STOP;
638 local_irq_disable();
639
640 shootdown_callback(cpu, (struct die_args *)data);
641
642 atomic_dec(&waiting_for_crash_ipi);
643 /* Assume hlt works */
644 halt();
645 for (;;)
646 cpu_relax();
647
648 return 1;
649}
650
651static void smp_send_nmi_allbutself(void)
652{
653 send_IPI_allbutself(NMI_VECTOR);
654}
655
656static struct notifier_block crash_nmi_nb = {
657 .notifier_call = crash_nmi_callback,
658};
659
660/* Halt all other CPUs, calling the specified function on each of them
661 *
662 * This function can be used to halt all other CPUs on crash
663 * or emergency reboot time. The function passed as parameter
664 * will be called inside a NMI handler on all CPUs.
665 */
666void nmi_shootdown_cpus(nmi_shootdown_cb callback)
667{
668 unsigned long msecs;
669 local_irq_disable();
670
671 /* Make a note of crashing cpu. Will be used in NMI callback.*/
672 crashing_cpu = safe_smp_processor_id();
673
674 shootdown_callback = callback;
675
676 atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
677 /* Would it be better to replace the trap vector here? */
678 if (register_die_notifier(&crash_nmi_nb))
679 return; /* return what? */
680 /* Ensure the new callback function is set before sending
681 * out the NMI
682 */
683 wmb();
684
685 smp_send_nmi_allbutself();
686
687 msecs = 1000; /* Wait at most a second for the other cpus to stop */
688 while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
689 mdelay(1);
690 msecs--;
691 }
692
693 /* Leave the nmi callback set */
694}
695#else /* !CONFIG_SMP */
696void nmi_shootdown_cpus(nmi_shootdown_cb callback)
697{
698 /* No other CPUs to shoot down */
699}
700#endif