diff mbox series

[kvm-unit-tests,2/2] x86: nSVM: Ensure APIC MMIO tests run with APIC in xAPIC mode

Message ID 20250304211223.124321-3-seanjc@google.com (mailing list archive)
State New
Headers show
Series x86: nSVM: Fix a bug with nNPT+x2AVIC | expand

Commit Message

Sean Christopherson March 4, 2025, 9:12 p.m. UTC
Implement prepare/restore logic for the nSVM/nNPT APIC MMIO passthrough
tests to ensure the CPU is actually running with xAPIC enabled.  As is,
the test is effectively validating KVM's KVM_X86_QUIRK_LAPIC_MMIO_HOLE,
or if x2AVIC is support, CPU behavior.

The latter (x2AVIC enabled) is especially problematic, as AMD CPUs appear
to return '0' for xAPIC reads when x2AVIC is enabled.  And because KVM
disables/inhibits AVIC and x2AVIC when running L2, the divergence in
behavior (KVM provies 0xffs, CPU provides 0s) results in test failures.

Opportunistically make the hardcoded APIC base pointer (eww) an unsigned
long literal.

Note, svm_test.finished() is invoked *before* svm_test.succeeded(), i.e.
restoring x2APIC (if it was enabled) must be done in the "check" code.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 x86/svm_npt.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
diff mbox series

Patch

diff --git a/x86/svm_npt.c b/x86/svm_npt.c
index b791f1ac..bd5e8f35 100644
--- a/x86/svm_npt.c
+++ b/x86/svm_npt.c
@@ -1,3 +1,4 @@ 
+#include "apic.h"
 #include "svm.h"
 #include "vm.h"
 #include "alloc_page.h"
@@ -134,8 +135,27 @@  static bool npt_rw_pfwalk_check(struct svm_test *test)
 	    && (vmcb->control.exit_info_2 == read_cr3());
 }
 
+static bool was_x2apic;
+
+static void npt_apic_prepare(void)
+{
+	was_x2apic = is_x2apic_enabled();
+
+	if (was_x2apic)
+		reset_apic();
+}
+
+static void npt_apic_restore(void)
+{
+	if (was_x2apic)
+		enable_x2apic();
+
+	was_x2apic = false;
+}
+
 static void npt_l1mmio_prepare(struct svm_test *test)
 {
+	npt_apic_prepare();
 }
 
 u32 nested_apic_version1;
@@ -154,6 +174,9 @@  static bool npt_l1mmio_check(struct svm_test *test)
 	volatile u32 *data = (volatile void *)(0xfee00030);
 	u32 lvr = *data;
 
+	/* Restore APIC state *after* reading LVR. */
+	npt_apic_restore();
+
 	return nested_apic_version1 == lvr && nested_apic_version2 == lvr;
 }
 
@@ -162,6 +185,8 @@  static void npt_rw_l1mmio_prepare(struct svm_test *test)
 
 	u64 *pte;
 
+	npt_apic_prepare();
+
 	pte = npt_get_pte(0xfee00080);
 
 	*pte &= ~(1ULL << 1);
@@ -180,6 +205,8 @@  static bool npt_rw_l1mmio_check(struct svm_test *test)
 
 	*pte |= (1ULL << 1);
 
+	npt_apic_restore();
+
 	return (vmcb->control.exit_code == SVM_EXIT_NPF)
 	    && (vmcb->control.exit_info_1 == 0x100000007ULL);
 }