diff options
Diffstat (limited to 'arch/x86/kernel/reboot.c')
-rw-r--r-- | arch/x86/kernel/reboot.c | 116 |
1 files changed, 103 insertions, 13 deletions
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index c3cd512484e5..32e8f0af292c 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c | |||
@@ -12,6 +12,9 @@ | |||
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> | ||
17 | #include <asm/cpu.h> | ||
15 | 18 | ||
16 | #ifdef CONFIG_X86_32 | 19 | #ifdef CONFIG_X86_32 |
17 | # include <linux/dmi.h> | 20 | # include <linux/dmi.h> |
@@ -21,8 +24,7 @@ | |||
21 | # include <asm/iommu.h> | 24 | # include <asm/iommu.h> |
22 | #endif | 25 | #endif |
23 | 26 | ||
24 | #include <mach_ipi.h> | 27 | #include <asm/genapic.h> |
25 | |||
26 | 28 | ||
27 | /* | 29 | /* |
28 | * Power off function, if any | 30 | * Power off function, if any |
@@ -39,7 +41,16 @@ int reboot_force; | |||
39 | static int reboot_cpu = -1; | 41 | static int reboot_cpu = -1; |
40 | #endif | 42 | #endif |
41 | 43 | ||
42 | /* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | 44 | /* This is set if we need to go through the 'emergency' path. |
45 | * When machine_emergency_restart() is called, we may be on | ||
46 | * an inconsistent state and won't be able to do a clean cleanup | ||
47 | */ | ||
48 | static int reboot_emergency; | ||
49 | |||
50 | /* This is set by the PCI code if either type 1 or type 2 PCI is detected */ | ||
51 | bool port_cf9_safe = false; | ||
52 | |||
53 | /* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci] | ||
43 | warm Don't set the cold reboot flag | 54 | warm Don't set the cold reboot flag |
44 | cold Set the cold reboot flag | 55 | cold Set the cold reboot flag |
45 | bios Reboot by jumping through the BIOS (only for X86_32) | 56 | bios Reboot by jumping through the BIOS (only for X86_32) |
@@ -48,6 +59,7 @@ static int reboot_cpu = -1; | |||
48 | kbd Use the keyboard controller. cold reset (default) | 59 | kbd Use the keyboard controller. cold reset (default) |
49 | acpi Use the RESET_REG in the FADT | 60 | acpi Use the RESET_REG in the FADT |
50 | efi Use efi reset_system runtime service | 61 | efi Use efi reset_system runtime service |
62 | pci Use the so-called "PCI reset register", CF9 | ||
51 | force Avoid anything that could hang. | 63 | force Avoid anything that could hang. |
52 | */ | 64 | */ |
53 | static int __init reboot_setup(char *str) | 65 | static int __init reboot_setup(char *str) |
@@ -82,6 +94,7 @@ static int __init reboot_setup(char *str) | |||
82 | case 'k': | 94 | case 'k': |
83 | case 't': | 95 | case 't': |
84 | case 'e': | 96 | case 'e': |
97 | case 'p': | ||
85 | reboot_type = *str; | 98 | reboot_type = *str; |
86 | break; | 99 | break; |
87 | 100 | ||
@@ -172,6 +185,15 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { | |||
172 | DMI_MATCH(DMI_BOARD_NAME, "0KW626"), | 185 | DMI_MATCH(DMI_BOARD_NAME, "0KW626"), |
173 | }, | 186 | }, |
174 | }, | 187 | }, |
188 | { /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */ | ||
189 | .callback = set_bios_reboot, | ||
190 | .ident = "Dell OptiPlex 330", | ||
191 | .matches = { | ||
192 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
193 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"), | ||
194 | DMI_MATCH(DMI_BOARD_NAME, "0KP561"), | ||
195 | }, | ||
196 | }, | ||
175 | { /* Handle problems with rebooting on Dell 2400's */ | 197 | { /* Handle problems with rebooting on Dell 2400's */ |
176 | .callback = set_bios_reboot, | 198 | .callback = set_bios_reboot, |
177 | .ident = "Dell PowerEdge 2400", | 199 | .ident = "Dell PowerEdge 2400", |
@@ -354,6 +376,48 @@ static inline void kb_wait(void) | |||
354 | } | 376 | } |
355 | } | 377 | } |
356 | 378 | ||
379 | static void vmxoff_nmi(int cpu, struct die_args *args) | ||
380 | { | ||
381 | cpu_emergency_vmxoff(); | ||
382 | } | ||
383 | |||
384 | /* Use NMIs as IPIs to tell all CPUs to disable virtualization | ||
385 | */ | ||
386 | static void emergency_vmx_disable_all(void) | ||
387 | { | ||
388 | /* Just make sure we won't change CPUs while doing this */ | ||
389 | local_irq_disable(); | ||
390 | |||
391 | /* We need to disable VMX on all CPUs before rebooting, otherwise | ||
392 | * we risk hanging up the machine, because the CPU ignore INIT | ||
393 | * signals when VMX is enabled. | ||
394 | * | ||
395 | * We can't take any locks and we may be on an inconsistent | ||
396 | * state, so we use NMIs as IPIs to tell the other CPUs to disable | ||
397 | * VMX and halt. | ||
398 | * | ||
399 | * For safety, we will avoid running the nmi_shootdown_cpus() | ||
400 | * stuff unnecessarily, but we don't have a way to check | ||
401 | * if other CPUs have VMX enabled. So we will call it only if the | ||
402 | * CPU we are running on has VMX enabled. | ||
403 | * | ||
404 | * We will miss cases where VMX is not enabled on all CPUs. This | ||
405 | * shouldn't do much harm because KVM always enable VMX on all | ||
406 | * CPUs anyway. But we can miss it on the small window where KVM | ||
407 | * is still enabling VMX. | ||
408 | */ | ||
409 | if (cpu_has_vmx() && cpu_vmx_enabled()) { | ||
410 | /* Disable VMX on this CPU. | ||
411 | */ | ||
412 | cpu_vmxoff(); | ||
413 | |||
414 | /* Halt and disable VMX on the other CPUs */ | ||
415 | nmi_shootdown_cpus(vmxoff_nmi); | ||
416 | |||
417 | } | ||
418 | } | ||
419 | |||
420 | |||
357 | void __attribute__((weak)) mach_reboot_fixups(void) | 421 | void __attribute__((weak)) mach_reboot_fixups(void) |
358 | { | 422 | { |
359 | } | 423 | } |
@@ -362,6 +426,9 @@ static void native_machine_emergency_restart(void) | |||
362 | { | 426 | { |
363 | int i; | 427 | int i; |
364 | 428 | ||
429 | if (reboot_emergency) | ||
430 | emergency_vmx_disable_all(); | ||
431 | |||
365 | /* Tell the BIOS if we want cold or warm reboot */ | 432 | /* Tell the BIOS if we want cold or warm reboot */ |
366 | *((unsigned short *)__va(0x472)) = reboot_mode; | 433 | *((unsigned short *)__va(0x472)) = reboot_mode; |
367 | 434 | ||
@@ -398,12 +465,27 @@ static void native_machine_emergency_restart(void) | |||
398 | reboot_type = BOOT_KBD; | 465 | reboot_type = BOOT_KBD; |
399 | break; | 466 | break; |
400 | 467 | ||
401 | |||
402 | case BOOT_EFI: | 468 | case BOOT_EFI: |
403 | if (efi_enabled) | 469 | if (efi_enabled) |
404 | efi.reset_system(reboot_mode ? EFI_RESET_WARM : EFI_RESET_COLD, | 470 | efi.reset_system(reboot_mode ? |
471 | EFI_RESET_WARM : | ||
472 | EFI_RESET_COLD, | ||
405 | EFI_SUCCESS, 0, NULL); | 473 | EFI_SUCCESS, 0, NULL); |
474 | reboot_type = BOOT_KBD; | ||
475 | break; | ||
476 | |||
477 | case BOOT_CF9: | ||
478 | port_cf9_safe = true; | ||
479 | /* fall through */ | ||
406 | 480 | ||
481 | case BOOT_CF9_COND: | ||
482 | if (port_cf9_safe) { | ||
483 | u8 cf9 = inb(0xcf9) & ~6; | ||
484 | outb(cf9|2, 0xcf9); /* Request hard reset */ | ||
485 | udelay(50); | ||
486 | outb(cf9|6, 0xcf9); /* Actually do the reset */ | ||
487 | udelay(50); | ||
488 | } | ||
407 | reboot_type = BOOT_KBD; | 489 | reboot_type = BOOT_KBD; |
408 | break; | 490 | break; |
409 | } | 491 | } |
@@ -420,7 +502,7 @@ void native_machine_shutdown(void) | |||
420 | 502 | ||
421 | #ifdef CONFIG_X86_32 | 503 | #ifdef CONFIG_X86_32 |
422 | /* See if there has been given a command line override */ | 504 | /* See if there has been given a command line override */ |
423 | if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) && | 505 | if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) && |
424 | cpu_online(reboot_cpu)) | 506 | cpu_online(reboot_cpu)) |
425 | reboot_cpu_id = reboot_cpu; | 507 | reboot_cpu_id = reboot_cpu; |
426 | #endif | 508 | #endif |
@@ -430,7 +512,7 @@ void native_machine_shutdown(void) | |||
430 | reboot_cpu_id = smp_processor_id(); | 512 | reboot_cpu_id = smp_processor_id(); |
431 | 513 | ||
432 | /* Make certain I only run on the appropriate processor */ | 514 | /* Make certain I only run on the appropriate processor */ |
433 | set_cpus_allowed_ptr(current, &cpumask_of_cpu(reboot_cpu_id)); | 515 | set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id)); |
434 | 516 | ||
435 | /* O.K Now that I'm on the appropriate processor, | 517 | /* O.K Now that I'm on the appropriate processor, |
436 | * stop all of the others. | 518 | * stop all of the others. |
@@ -453,17 +535,28 @@ void native_machine_shutdown(void) | |||
453 | #endif | 535 | #endif |
454 | } | 536 | } |
455 | 537 | ||
538 | static void __machine_emergency_restart(int emergency) | ||
539 | { | ||
540 | reboot_emergency = emergency; | ||
541 | machine_ops.emergency_restart(); | ||
542 | } | ||
543 | |||
456 | static void native_machine_restart(char *__unused) | 544 | static void native_machine_restart(char *__unused) |
457 | { | 545 | { |
458 | printk("machine restart\n"); | 546 | printk("machine restart\n"); |
459 | 547 | ||
460 | if (!reboot_force) | 548 | if (!reboot_force) |
461 | machine_shutdown(); | 549 | machine_shutdown(); |
462 | machine_emergency_restart(); | 550 | __machine_emergency_restart(0); |
463 | } | 551 | } |
464 | 552 | ||
465 | static void native_machine_halt(void) | 553 | static void native_machine_halt(void) |
466 | { | 554 | { |
555 | /* stop other cpus and apics */ | ||
556 | machine_shutdown(); | ||
557 | |||
558 | /* stop this cpu */ | ||
559 | stop_this_cpu(NULL); | ||
467 | } | 560 | } |
468 | 561 | ||
469 | static void native_machine_power_off(void) | 562 | static void native_machine_power_off(void) |
@@ -498,7 +591,7 @@ void machine_shutdown(void) | |||
498 | 591 | ||
499 | void machine_emergency_restart(void) | 592 | void machine_emergency_restart(void) |
500 | { | 593 | { |
501 | machine_ops.emergency_restart(); | 594 | __machine_emergency_restart(1); |
502 | } | 595 | } |
503 | 596 | ||
504 | void machine_restart(char *cmd) | 597 | void machine_restart(char *cmd) |
@@ -558,10 +651,7 @@ static int crash_nmi_callback(struct notifier_block *self, | |||
558 | 651 | ||
559 | static void smp_send_nmi_allbutself(void) | 652 | static void smp_send_nmi_allbutself(void) |
560 | { | 653 | { |
561 | cpumask_t mask = cpu_online_map; | 654 | apic->send_IPI_allbutself(NMI_VECTOR); |
562 | cpu_clear(safe_smp_processor_id(), mask); | ||
563 | if (!cpus_empty(mask)) | ||
564 | send_IPI_mask(mask, NMI_VECTOR); | ||
565 | } | 655 | } |
566 | 656 | ||
567 | static struct notifier_block crash_nmi_nb = { | 657 | static struct notifier_block crash_nmi_nb = { |