diff options
Diffstat (limited to 'arch/x86/kernel/reboot.c')
-rw-r--r-- | arch/x86/kernel/reboot.c | 189 |
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; | |||
36 | static int reboot_cpu = -1; | 40 | static 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 | */ | ||
47 | static int reboot_emergency; | ||
48 | |||
49 | /* This is set by the PCI code if either type 1 or type 2 PCI is detected */ | ||
50 | bool 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 | */ |
50 | static int __init reboot_setup(char *str) | 64 | static 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 | ||
378 | static 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 | */ | ||
385 | static 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 | |||
363 | void __attribute__((weak)) mach_reboot_fixups(void) | 420 | void __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 | ||
537 | static void __machine_emergency_restart(int emergency) | ||
538 | { | ||
539 | reboot_emergency = emergency; | ||
540 | machine_ops.emergency_restart(); | ||
541 | } | ||
542 | |||
462 | static void native_machine_restart(char *__unused) | 543 | static 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 | ||
471 | static void native_machine_halt(void) | 552 | static 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 | ||
475 | static void native_machine_power_off(void) | 561 | static void native_machine_power_off(void) |
@@ -504,7 +590,7 @@ void machine_shutdown(void) | |||
504 | 590 | ||
505 | void machine_emergency_restart(void) | 591 | void machine_emergency_restart(void) |
506 | { | 592 | { |
507 | machine_ops.emergency_restart(); | 593 | __machine_emergency_restart(1); |
508 | } | 594 | } |
509 | 595 | ||
510 | void machine_restart(char *cmd) | 596 | void 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. */ | ||
617 | static int crashing_cpu; | ||
618 | static nmi_shootdown_cb shootdown_callback; | ||
619 | |||
620 | static atomic_t waiting_for_crash_ipi; | ||
621 | |||
622 | static 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 | |||
651 | static void smp_send_nmi_allbutself(void) | ||
652 | { | ||
653 | send_IPI_allbutself(NMI_VECTOR); | ||
654 | } | ||
655 | |||
656 | static 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 | */ | ||
666 | void 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 */ | ||
696 | void nmi_shootdown_cpus(nmi_shootdown_cb callback) | ||
697 | { | ||
698 | /* No other CPUs to shoot down */ | ||
699 | } | ||
700 | #endif | ||