hc
2024-05-10 9999e48639b3cecb08ffb37358bcba3b48161b29
kernel/arch/x86/kernel/reboot.c
....@@ -10,14 +10,14 @@
1010 #include <linux/sched.h>
1111 #include <linux/tboot.h>
1212 #include <linux/delay.h>
13
-#include <linux/frame.h>
13
+#include <linux/objtool.h>
14
+#include <linux/pgtable.h>
1415 #include <acpi/reboot.h>
1516 #include <asm/io.h>
1617 #include <asm/apic.h>
1718 #include <asm/io_apic.h>
1819 #include <asm/desc.h>
1920 #include <asm/hpet.h>
20
-#include <asm/pgtable.h>
2121 #include <asm/proto.h>
2222 #include <asm/reboot_fixups.h>
2323 #include <asm/reboot.h>
....@@ -113,17 +113,9 @@
113113 spin_unlock(&rtc_lock);
114114
115115 /*
116
- * Switch back to the initial page table.
116
+ * Switch to the trampoline page table.
117117 */
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();
127119
128120 /* Jump to the identity-mapped low memory code */
129121 #ifdef CONFIG_X86_32
....@@ -536,34 +528,29 @@
536528 }
537529 }
538530
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);
543532
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)
546534 {
547535 /* Just make sure we won't change CPUs while doing this */
548536 local_irq_disable();
549537
550538 /*
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.
553541 *
554542 * 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.
556544 *
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.
559547 */
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();
563551
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();
567554 }
568555 }
569556
....@@ -599,7 +586,7 @@
599586 unsigned short mode;
600587
601588 if (reboot_emergency)
602
- emergency_vmx_disable_all();
589
+ emergency_reboot_disable_virtualization();
603590
604591 tboot_shutdown(TB_SHUTDOWN_REBOOT);
605592
....@@ -655,7 +642,7 @@
655642
656643 case BOOT_CF9_FORCE:
657644 port_cf9_safe = true;
658
- /* Fall through */
645
+ fallthrough;
659646
660647 case BOOT_CF9_SAFE:
661648 if (port_cf9_safe) {
....@@ -804,6 +791,17 @@
804791 /* This is the CPU performing the emergency shutdown work. */
805792 int crashing_cpu = -1;
806793
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
+
807805 #if defined(CONFIG_SMP)
808806
809807 static nmi_shootdown_cb shootdown_callback;
....@@ -826,7 +824,14 @@
826824 return NMI_HANDLED;
827825 local_irq_disable();
828826
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();
830835
831836 atomic_dec(&waiting_for_crash_ipi);
832837 /* Assume hlt works */
....@@ -837,22 +842,31 @@
837842 return NMI_HANDLED;
838843 }
839844
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
847848 *
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.
851856 */
852857 void nmi_shootdown_cpus(nmi_shootdown_cb callback)
853858 {
854859 unsigned long msecs;
860
+
855861 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;
856870
857871 /* Make a note of crashing cpu. Will be used in NMI callback. */
858872 crashing_cpu = safe_smp_processor_id();
....@@ -870,7 +884,7 @@
870884 */
871885 wmb();
872886
873
- smp_send_nmi_allbutself();
887
+ apic_send_IPI_allbutself(NMI_VECTOR);
874888
875889 /* Kick CPUs looping in NMI context. */
876890 WRITE_ONCE(crash_ipi_issued, 1);
....@@ -881,7 +895,17 @@
881895 msecs--;
882896 }
883897
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);
885909 }
886910
887911 /*
....@@ -911,6 +935,8 @@
911935 /* No other CPUs to shoot down */
912936 }
913937
938
+static inline void nmi_shootdown_cpus_on_restart(void) { }
939
+
914940 void run_crash_ipi_callback(struct pt_regs *regs)
915941 {
916942 }