.. | .. |
---|
10 | 10 | #include <linux/sched.h> |
---|
11 | 11 | #include <linux/tboot.h> |
---|
12 | 12 | #include <linux/delay.h> |
---|
13 | | -#include <linux/frame.h> |
---|
| 13 | +#include <linux/objtool.h> |
---|
| 14 | +#include <linux/pgtable.h> |
---|
14 | 15 | #include <acpi/reboot.h> |
---|
15 | 16 | #include <asm/io.h> |
---|
16 | 17 | #include <asm/apic.h> |
---|
17 | 18 | #include <asm/io_apic.h> |
---|
18 | 19 | #include <asm/desc.h> |
---|
19 | 20 | #include <asm/hpet.h> |
---|
20 | | -#include <asm/pgtable.h> |
---|
21 | 21 | #include <asm/proto.h> |
---|
22 | 22 | #include <asm/reboot_fixups.h> |
---|
23 | 23 | #include <asm/reboot.h> |
---|
.. | .. |
---|
113 | 113 | spin_unlock(&rtc_lock); |
---|
114 | 114 | |
---|
115 | 115 | /* |
---|
116 | | - * Switch back to the initial page table. |
---|
| 116 | + * Switch to the trampoline page table. |
---|
117 | 117 | */ |
---|
118 | | -#ifdef CONFIG_X86_32 |
---|
119 | | - load_cr3(initial_page_table); |
---|
120 | | -#else |
---|
121 | | - write_cr3(real_mode_header->trampoline_pgd); |
---|
122 | | - |
---|
123 | | - /* Exiting long mode will fail if CR4.PCIDE is set. */ |
---|
124 | | - if (static_cpu_has(X86_FEATURE_PCID)) |
---|
125 | | - cr4_clear_bits(X86_CR4_PCIDE); |
---|
126 | | -#endif |
---|
| 118 | + load_trampoline_pgtable(); |
---|
127 | 119 | |
---|
128 | 120 | /* Jump to the identity-mapped low memory code */ |
---|
129 | 121 | #ifdef CONFIG_X86_32 |
---|
.. | .. |
---|
536 | 528 | } |
---|
537 | 529 | } |
---|
538 | 530 | |
---|
539 | | -static void vmxoff_nmi(int cpu, struct pt_regs *regs) |
---|
540 | | -{ |
---|
541 | | - cpu_emergency_vmxoff(); |
---|
542 | | -} |
---|
| 531 | +static inline void nmi_shootdown_cpus_on_restart(void); |
---|
543 | 532 | |
---|
544 | | -/* Use NMIs as IPIs to tell all CPUs to disable virtualization */ |
---|
545 | | -static void emergency_vmx_disable_all(void) |
---|
| 533 | +static void emergency_reboot_disable_virtualization(void) |
---|
546 | 534 | { |
---|
547 | 535 | /* Just make sure we won't change CPUs while doing this */ |
---|
548 | 536 | local_irq_disable(); |
---|
549 | 537 | |
---|
550 | 538 | /* |
---|
551 | | - * Disable VMX on all CPUs before rebooting, otherwise we risk hanging |
---|
552 | | - * the machine, because the CPU blocks INIT when it's in VMX root. |
---|
| 539 | + * Disable virtualization on all CPUs before rebooting to avoid hanging |
---|
| 540 | + * the system, as VMX and SVM block INIT when running in the host. |
---|
553 | 541 | * |
---|
554 | 542 | * We can't take any locks and we may be on an inconsistent state, so |
---|
555 | | - * use NMIs as IPIs to tell the other CPUs to exit VMX root and halt. |
---|
| 543 | + * use NMIs as IPIs to tell the other CPUs to disable VMX/SVM and halt. |
---|
556 | 544 | * |
---|
557 | | - * Do the NMI shootdown even if VMX if off on _this_ CPU, as that |
---|
558 | | - * doesn't prevent a different CPU from being in VMX root operation. |
---|
| 545 | + * Do the NMI shootdown even if virtualization is off on _this_ CPU, as |
---|
| 546 | + * other CPUs may have virtualization enabled. |
---|
559 | 547 | */ |
---|
560 | | - if (cpu_has_vmx()) { |
---|
561 | | - /* Safely force _this_ CPU out of VMX root operation. */ |
---|
562 | | - __cpu_emergency_vmxoff(); |
---|
| 548 | + if (cpu_has_vmx() || cpu_has_svm(NULL)) { |
---|
| 549 | + /* Safely force _this_ CPU out of VMX/SVM operation. */ |
---|
| 550 | + cpu_emergency_disable_virtualization(); |
---|
563 | 551 | |
---|
564 | | - /* Halt and exit VMX root operation on the other CPUs. */ |
---|
565 | | - nmi_shootdown_cpus(vmxoff_nmi); |
---|
566 | | - |
---|
| 552 | + /* Disable VMX/SVM and halt on other CPUs. */ |
---|
| 553 | + nmi_shootdown_cpus_on_restart(); |
---|
567 | 554 | } |
---|
568 | 555 | } |
---|
569 | 556 | |
---|
.. | .. |
---|
599 | 586 | unsigned short mode; |
---|
600 | 587 | |
---|
601 | 588 | if (reboot_emergency) |
---|
602 | | - emergency_vmx_disable_all(); |
---|
| 589 | + emergency_reboot_disable_virtualization(); |
---|
603 | 590 | |
---|
604 | 591 | tboot_shutdown(TB_SHUTDOWN_REBOOT); |
---|
605 | 592 | |
---|
.. | .. |
---|
655 | 642 | |
---|
656 | 643 | case BOOT_CF9_FORCE: |
---|
657 | 644 | port_cf9_safe = true; |
---|
658 | | - /* Fall through */ |
---|
| 645 | + fallthrough; |
---|
659 | 646 | |
---|
660 | 647 | case BOOT_CF9_SAFE: |
---|
661 | 648 | if (port_cf9_safe) { |
---|
.. | .. |
---|
804 | 791 | /* This is the CPU performing the emergency shutdown work. */ |
---|
805 | 792 | int crashing_cpu = -1; |
---|
806 | 793 | |
---|
| 794 | +/* |
---|
| 795 | + * Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized during |
---|
| 796 | + * reboot. VMX blocks INIT if the CPU is post-VMXON, and SVM blocks INIT if |
---|
| 797 | + * GIF=0, i.e. if the crash occurred between CLGI and STGI. |
---|
| 798 | + */ |
---|
| 799 | +void cpu_emergency_disable_virtualization(void) |
---|
| 800 | +{ |
---|
| 801 | + cpu_emergency_vmxoff(); |
---|
| 802 | + cpu_emergency_svm_disable(); |
---|
| 803 | +} |
---|
| 804 | + |
---|
807 | 805 | #if defined(CONFIG_SMP) |
---|
808 | 806 | |
---|
809 | 807 | static nmi_shootdown_cb shootdown_callback; |
---|
.. | .. |
---|
826 | 824 | return NMI_HANDLED; |
---|
827 | 825 | local_irq_disable(); |
---|
828 | 826 | |
---|
829 | | - shootdown_callback(cpu, regs); |
---|
| 827 | + if (shootdown_callback) |
---|
| 828 | + shootdown_callback(cpu, regs); |
---|
| 829 | + |
---|
| 830 | + /* |
---|
| 831 | + * Prepare the CPU for reboot _after_ invoking the callback so that the |
---|
| 832 | + * callback can safely use virtualization instructions, e.g. VMCLEAR. |
---|
| 833 | + */ |
---|
| 834 | + cpu_emergency_disable_virtualization(); |
---|
830 | 835 | |
---|
831 | 836 | atomic_dec(&waiting_for_crash_ipi); |
---|
832 | 837 | /* Assume hlt works */ |
---|
.. | .. |
---|
837 | 842 | return NMI_HANDLED; |
---|
838 | 843 | } |
---|
839 | 844 | |
---|
840 | | -static void smp_send_nmi_allbutself(void) |
---|
841 | | -{ |
---|
842 | | - apic->send_IPI_allbutself(NMI_VECTOR); |
---|
843 | | -} |
---|
844 | | - |
---|
845 | | -/* |
---|
846 | | - * Halt all other CPUs, calling the specified function on each of them |
---|
| 845 | +/** |
---|
| 846 | + * nmi_shootdown_cpus - Stop other CPUs via NMI |
---|
| 847 | + * @callback: Optional callback to be invoked from the NMI handler |
---|
847 | 848 | * |
---|
848 | | - * This function can be used to halt all other CPUs on crash |
---|
849 | | - * or emergency reboot time. The function passed as parameter |
---|
850 | | - * will be called inside a NMI handler on all CPUs. |
---|
| 849 | + * The NMI handler on the remote CPUs invokes @callback, if not |
---|
| 850 | + * NULL, first and then disables virtualization to ensure that |
---|
| 851 | + * INIT is recognized during reboot. |
---|
| 852 | + * |
---|
| 853 | + * nmi_shootdown_cpus() can only be invoked once. After the first |
---|
| 854 | + * invocation all other CPUs are stuck in crash_nmi_callback() and |
---|
| 855 | + * cannot respond to a second NMI. |
---|
851 | 856 | */ |
---|
852 | 857 | void nmi_shootdown_cpus(nmi_shootdown_cb callback) |
---|
853 | 858 | { |
---|
854 | 859 | unsigned long msecs; |
---|
| 860 | + |
---|
855 | 861 | local_irq_disable(); |
---|
| 862 | + |
---|
| 863 | + /* |
---|
| 864 | + * Avoid certain doom if a shootdown already occurred; re-registering |
---|
| 865 | + * the NMI handler will cause list corruption, modifying the callback |
---|
| 866 | + * will do who knows what, etc... |
---|
| 867 | + */ |
---|
| 868 | + if (WARN_ON_ONCE(crash_ipi_issued)) |
---|
| 869 | + return; |
---|
856 | 870 | |
---|
857 | 871 | /* Make a note of crashing cpu. Will be used in NMI callback. */ |
---|
858 | 872 | crashing_cpu = safe_smp_processor_id(); |
---|
.. | .. |
---|
870 | 884 | */ |
---|
871 | 885 | wmb(); |
---|
872 | 886 | |
---|
873 | | - smp_send_nmi_allbutself(); |
---|
| 887 | + apic_send_IPI_allbutself(NMI_VECTOR); |
---|
874 | 888 | |
---|
875 | 889 | /* Kick CPUs looping in NMI context. */ |
---|
876 | 890 | WRITE_ONCE(crash_ipi_issued, 1); |
---|
.. | .. |
---|
881 | 895 | msecs--; |
---|
882 | 896 | } |
---|
883 | 897 | |
---|
884 | | - /* Leave the nmi callback set */ |
---|
| 898 | + /* |
---|
| 899 | + * Leave the nmi callback set, shootdown is a one-time thing. Clearing |
---|
| 900 | + * the callback could result in a NULL pointer dereference if a CPU |
---|
| 901 | + * (finally) responds after the timeout expires. |
---|
| 902 | + */ |
---|
| 903 | +} |
---|
| 904 | + |
---|
| 905 | +static inline void nmi_shootdown_cpus_on_restart(void) |
---|
| 906 | +{ |
---|
| 907 | + if (!crash_ipi_issued) |
---|
| 908 | + nmi_shootdown_cpus(NULL); |
---|
885 | 909 | } |
---|
886 | 910 | |
---|
887 | 911 | /* |
---|
.. | .. |
---|
911 | 935 | /* No other CPUs to shoot down */ |
---|
912 | 936 | } |
---|
913 | 937 | |
---|
| 938 | +static inline void nmi_shootdown_cpus_on_restart(void) { } |
---|
| 939 | + |
---|
914 | 940 | void run_crash_ipi_callback(struct pt_regs *regs) |
---|
915 | 941 | { |
---|
916 | 942 | } |
---|