diff options
Diffstat (limited to 'arch/x86/kernel/reboot.c')
-rw-r--r-- | arch/x86/kernel/reboot.c | 141 |
1 files changed, 133 insertions, 8 deletions
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index f4c93f1cfc19..61f718df6eec 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c | |||
@@ -21,6 +21,9 @@ | |||
21 | # include <asm/iommu.h> | 21 | # include <asm/iommu.h> |
22 | #endif | 22 | #endif |
23 | 23 | ||
24 | #include <mach_ipi.h> | ||
25 | |||
26 | |||
24 | /* | 27 | /* |
25 | * Power off function, if any | 28 | * Power off function, if any |
26 | */ | 29 | */ |
@@ -29,18 +32,17 @@ EXPORT_SYMBOL(pm_power_off); | |||
29 | 32 | ||
30 | static const struct desc_ptr no_idt = {}; | 33 | static const struct desc_ptr no_idt = {}; |
31 | static int reboot_mode; | 34 | static int reboot_mode; |
32 | /* | 35 | enum reboot_type reboot_type = BOOT_KBD; |
33 | * Keyboard reset and triple fault may result in INIT, not RESET, which | ||
34 | * doesn't work when we're in vmx root mode. Try ACPI first. | ||
35 | */ | ||
36 | enum reboot_type reboot_type = BOOT_ACPI; | ||
37 | int reboot_force; | 36 | int reboot_force; |
38 | 37 | ||
39 | #if defined(CONFIG_X86_32) && defined(CONFIG_SMP) | 38 | #if defined(CONFIG_X86_32) && defined(CONFIG_SMP) |
40 | static int reboot_cpu = -1; | 39 | static int reboot_cpu = -1; |
41 | #endif | 40 | #endif |
42 | 41 | ||
43 | /* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | 42 | /* This is set by the PCI code if either type 1 or type 2 PCI is detected */ |
43 | bool port_cf9_safe = false; | ||
44 | |||
45 | /* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci] | ||
44 | warm Don't set the cold reboot flag | 46 | warm Don't set the cold reboot flag |
45 | cold Set the cold reboot flag | 47 | cold Set the cold reboot flag |
46 | bios Reboot by jumping through the BIOS (only for X86_32) | 48 | bios Reboot by jumping through the BIOS (only for X86_32) |
@@ -49,6 +51,7 @@ static int reboot_cpu = -1; | |||
49 | kbd Use the keyboard controller. cold reset (default) | 51 | kbd Use the keyboard controller. cold reset (default) |
50 | acpi Use the RESET_REG in the FADT | 52 | acpi Use the RESET_REG in the FADT |
51 | efi Use efi reset_system runtime service | 53 | efi Use efi reset_system runtime service |
54 | pci Use the so-called "PCI reset register", CF9 | ||
52 | force Avoid anything that could hang. | 55 | force Avoid anything that could hang. |
53 | */ | 56 | */ |
54 | static int __init reboot_setup(char *str) | 57 | static int __init reboot_setup(char *str) |
@@ -83,6 +86,7 @@ static int __init reboot_setup(char *str) | |||
83 | case 'k': | 86 | case 'k': |
84 | case 't': | 87 | case 't': |
85 | case 'e': | 88 | case 'e': |
89 | case 'p': | ||
86 | reboot_type = *str; | 90 | reboot_type = *str; |
87 | break; | 91 | break; |
88 | 92 | ||
@@ -173,6 +177,15 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { | |||
173 | DMI_MATCH(DMI_BOARD_NAME, "0KW626"), | 177 | DMI_MATCH(DMI_BOARD_NAME, "0KW626"), |
174 | }, | 178 | }, |
175 | }, | 179 | }, |
180 | { /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */ | ||
181 | .callback = set_bios_reboot, | ||
182 | .ident = "Dell OptiPlex 330", | ||
183 | .matches = { | ||
184 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
185 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"), | ||
186 | DMI_MATCH(DMI_BOARD_NAME, "0KP561"), | ||
187 | }, | ||
188 | }, | ||
176 | { /* Handle problems with rebooting on Dell 2400's */ | 189 | { /* Handle problems with rebooting on Dell 2400's */ |
177 | .callback = set_bios_reboot, | 190 | .callback = set_bios_reboot, |
178 | .ident = "Dell PowerEdge 2400", | 191 | .ident = "Dell PowerEdge 2400", |
@@ -399,12 +412,27 @@ static void native_machine_emergency_restart(void) | |||
399 | reboot_type = BOOT_KBD; | 412 | reboot_type = BOOT_KBD; |
400 | break; | 413 | break; |
401 | 414 | ||
402 | |||
403 | case BOOT_EFI: | 415 | case BOOT_EFI: |
404 | if (efi_enabled) | 416 | if (efi_enabled) |
405 | efi.reset_system(reboot_mode ? EFI_RESET_WARM : EFI_RESET_COLD, | 417 | efi.reset_system(reboot_mode ? |
418 | EFI_RESET_WARM : | ||
419 | EFI_RESET_COLD, | ||
406 | EFI_SUCCESS, 0, NULL); | 420 | EFI_SUCCESS, 0, NULL); |
421 | reboot_type = BOOT_KBD; | ||
422 | break; | ||
423 | |||
424 | case BOOT_CF9: | ||
425 | port_cf9_safe = true; | ||
426 | /* fall through */ | ||
407 | 427 | ||
428 | case BOOT_CF9_COND: | ||
429 | if (port_cf9_safe) { | ||
430 | u8 cf9 = inb(0xcf9) & ~6; | ||
431 | outb(cf9|2, 0xcf9); /* Request hard reset */ | ||
432 | udelay(50); | ||
433 | outb(cf9|6, 0xcf9); /* Actually do the reset */ | ||
434 | udelay(50); | ||
435 | } | ||
408 | reboot_type = BOOT_KBD; | 436 | reboot_type = BOOT_KBD; |
409 | break; | 437 | break; |
410 | } | 438 | } |
@@ -465,6 +493,11 @@ static void native_machine_restart(char *__unused) | |||
465 | 493 | ||
466 | static void native_machine_halt(void) | 494 | static void native_machine_halt(void) |
467 | { | 495 | { |
496 | /* stop other cpus and apics */ | ||
497 | machine_shutdown(); | ||
498 | |||
499 | /* stop this cpu */ | ||
500 | stop_this_cpu(NULL); | ||
468 | } | 501 | } |
469 | 502 | ||
470 | static void native_machine_power_off(void) | 503 | static void native_machine_power_off(void) |
@@ -518,3 +551,95 @@ void machine_crash_shutdown(struct pt_regs *regs) | |||
518 | machine_ops.crash_shutdown(regs); | 551 | machine_ops.crash_shutdown(regs); |
519 | } | 552 | } |
520 | #endif | 553 | #endif |
554 | |||
555 | |||
556 | #if defined(CONFIG_SMP) | ||
557 | |||
558 | /* This keeps a track of which one is crashing cpu. */ | ||
559 | static int crashing_cpu; | ||
560 | static nmi_shootdown_cb shootdown_callback; | ||
561 | |||
562 | static atomic_t waiting_for_crash_ipi; | ||
563 | |||
564 | static int crash_nmi_callback(struct notifier_block *self, | ||
565 | unsigned long val, void *data) | ||
566 | { | ||
567 | int cpu; | ||
568 | |||
569 | if (val != DIE_NMI_IPI) | ||
570 | return NOTIFY_OK; | ||
571 | |||
572 | cpu = raw_smp_processor_id(); | ||
573 | |||
574 | /* Don't do anything if this handler is invoked on crashing cpu. | ||
575 | * Otherwise, system will completely hang. Crashing cpu can get | ||
576 | * an NMI if system was initially booted with nmi_watchdog parameter. | ||
577 | */ | ||
578 | if (cpu == crashing_cpu) | ||
579 | return NOTIFY_STOP; | ||
580 | local_irq_disable(); | ||
581 | |||
582 | shootdown_callback(cpu, (struct die_args *)data); | ||
583 | |||
584 | atomic_dec(&waiting_for_crash_ipi); | ||
585 | /* Assume hlt works */ | ||
586 | halt(); | ||
587 | for (;;) | ||
588 | cpu_relax(); | ||
589 | |||
590 | return 1; | ||
591 | } | ||
592 | |||
593 | static void smp_send_nmi_allbutself(void) | ||
594 | { | ||
595 | cpumask_t mask = cpu_online_map; | ||
596 | cpu_clear(safe_smp_processor_id(), mask); | ||
597 | if (!cpus_empty(mask)) | ||
598 | send_IPI_mask(mask, NMI_VECTOR); | ||
599 | } | ||
600 | |||
601 | static struct notifier_block crash_nmi_nb = { | ||
602 | .notifier_call = crash_nmi_callback, | ||
603 | }; | ||
604 | |||
605 | /* Halt all other CPUs, calling the specified function on each of them | ||
606 | * | ||
607 | * This function can be used to halt all other CPUs on crash | ||
608 | * or emergency reboot time. The function passed as parameter | ||
609 | * will be called inside a NMI handler on all CPUs. | ||
610 | */ | ||
611 | void nmi_shootdown_cpus(nmi_shootdown_cb callback) | ||
612 | { | ||
613 | unsigned long msecs; | ||
614 | local_irq_disable(); | ||
615 | |||
616 | /* Make a note of crashing cpu. Will be used in NMI callback.*/ | ||
617 | crashing_cpu = safe_smp_processor_id(); | ||
618 | |||
619 | shootdown_callback = callback; | ||
620 | |||
621 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); | ||
622 | /* Would it be better to replace the trap vector here? */ | ||
623 | if (register_die_notifier(&crash_nmi_nb)) | ||
624 | return; /* return what? */ | ||
625 | /* Ensure the new callback function is set before sending | ||
626 | * out the NMI | ||
627 | */ | ||
628 | wmb(); | ||
629 | |||
630 | smp_send_nmi_allbutself(); | ||
631 | |||
632 | msecs = 1000; /* Wait at most a second for the other cpus to stop */ | ||
633 | while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) { | ||
634 | mdelay(1); | ||
635 | msecs--; | ||
636 | } | ||
637 | |||
638 | /* Leave the nmi callback set */ | ||
639 | } | ||
640 | #else /* !CONFIG_SMP */ | ||
641 | void nmi_shootdown_cpus(nmi_shootdown_cb callback) | ||
642 | { | ||
643 | /* No other CPUs to shoot down */ | ||
644 | } | ||
645 | #endif | ||