[PATCH v3 20/20] KVM: x86: selftests: Add APX state handling and XCR0 sanity checks

Chang S. Bae
2026-04-28T05:26:39Z

Now that KVM exposes the APX feature to guests, extend some existing
selftests to validate XCR0 configuration and state management.

Since APX repurposes the XSAVE area previously used by MPX in the
non-compacted format, add a check to ensure that MPX states are not set
when APX is enabled.

Also, load non-init APX state data in the guest so that XSTATE_BV[APX] is
set, allowing validation of APX state testing.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
V2 -> V3: Use EGPR helper
---
tools/testing/selftests/kvm/x86/state_test.c | 3 +++
.../selftests/kvm/x86/xcr0_cpuid_test.c | 19 +++++++++++++++++++
2 files changed, 22 insertions(+)

diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c
index 992a52504a4a..24a8166d2a4f 100644
--- a/tools/testing/selftests/kvm/x86/state_test.c
+++ b/tools/testing/selftests/kvm/x86/state_test.c
@@ -171,6 +171,9 @@ static void __attribute__((__flatten__)) guest_code(void *arg)
asm volatile ("vmovupd %0, %%zmm16" :: "m" (buffer));
}

+ if (supported_xcr0 & XFEATURE_MASK_APX)
+ write_egpr(16, 0xcccccccc);
+
if (this_cpu_has(X86_FEATURE_MPX)) {
uint64_t bounds[2] = { 10, 0xffffffffull };
uint64_t output[2] = { };
diff --git a/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c b/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c
index d038c1571729..e3d3af5ab6f2 100644
--- a/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c
+++ b/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c
@@ -46,6 +46,20 @@ do { \
__supported, (xfeatures)); \
} while (0)

+/*
+ * Verify that mutually exclusive architectural features do not overlap.
+ * For example, APX and MPX must never be reported as supported together.
+ */
+#define ASSERT_XFEATURE_CONFLICT(supported_xcr0, xfeatures, conflicts) \
+do { \
+ uint64_t __supported = (supported_xcr0) & ((xfeatures) | (conflicts)); \
+ \
+ __GUEST_ASSERT((__supported & (xfeatures)) != (xfeatures) || \
+ !(__supported & (conflicts)), \
+ "supported = 0x%lx, xfeatures = 0x%llx, conflicts = 0x%llx", \
+ __supported, (xfeatures), (conflicts)); \
+} while (0)
+
static void guest_code(void)
{
uint64_t initial_xcr0;
@@ -79,6 +93,11 @@ static void guest_code(void)
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
XFEATURE_MASK_XTILE);

+ /* Check APX by ensuring MPX is not exposed concurrently */
+ ASSERT_XFEATURE_CONFLICT(supported_xcr0,
+ XFEATURE_MASK_APX,
+ XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
+
vector = xsetbv_safe(0, XFEATURE_MASK_FP);
__GUEST_ASSERT(!vector,
"Expected success on XSETBV(FP), got %s",
--
2.51.0