Add a LASS selftest for KVM's handling of CPUID advertisement and CR4.LASS. Verify that a guest write to CR4.LASS generates #GP, and leaves CR4 unchanged, when LASS isn't enumerated in the guest's CPUID.
Extend set_sregs_test to cover CR4.LASS and verify that KVM_SET_SREGS accepts CR4.LASS when LASS is supported, and rejects it, without modifying sregs, when it isn't. Don't try running with CR4.LASS enabled in the guest or attempt to trigger LASS violations. Selftests run guest code in the lower half of the address space at CPL0, so enabling CR4.LASS would make the next instruction fetch a violation and triple-fault the guest. Assisted-by: Claude:claude-opus-5 Signed-off-by: Sohil Mehta <[email protected]> --- v4: - New patch lass_test.c doesn't really test LASS enforcement or any other LASS functionality. It just tests that a guest write to CR4.LASS generates a #GP when LASS isn't enumerated. There could be a generic test which covers such CPUID/CR4 interactions. For now, I added something for LASS because I couldn't find one. --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/include/x86/processor.h | 2 + tools/testing/selftests/kvm/x86/lass_test.c | 56 +++++++++++++++++++ .../selftests/kvm/x86/set_sregs_test.c | 3 + 4 files changed, 62 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/lass_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 00123169a190..86fa93e0f6c0 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -91,6 +91,7 @@ TEST_GEN_PROGS_x86 += x86/hyperv_tlb_flush TEST_GEN_PROGS_x86 += x86/kvm_clock_test TEST_GEN_PROGS_x86 += x86/kvm_pv_test TEST_GEN_PROGS_x86 += x86/kvm_buslock_test +TEST_GEN_PROGS_x86 += x86/lass_test TEST_GEN_PROGS_x86 += x86/monitor_mwait_test TEST_GEN_PROGS_x86 += x86/msrs_test TEST_GEN_PROGS_x86 += x86/nested_close_kvm_test diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 6e6f70035508..f425166174f9 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -79,6 +79,7 @@ const char *ex_str(int vector); #define X86_CR4_SMEP (1ul << 20) #define X86_CR4_SMAP (1ul << 21) #define X86_CR4_PKE (1ul << 22) +#define X86_CR4_LASS (1ul << 27) struct xstate_header { u64 xstate_bv; @@ -195,6 +196,7 @@ struct kvm_x86_cpu_feature { #define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26) #define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29) #define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31) +#define X86_FEATURE_LASS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 6) #define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17) #define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18) #define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3) diff --git a/tools/testing/selftests/kvm/x86/lass_test.c b/tools/testing/selftests/kvm/x86/lass_test.c new file mode 100644 index 000000000000..7c5cfa24c2bd --- /dev/null +++ b/tools/testing/selftests/kvm/x86/lass_test.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Linear Address Space Separation (LASS) test + * + * Copyright (C) 2026, Intel Corporation. + * + * Only test that the guest can't set CR4.LASS when LASS isn't + * enumerated in the guest's CPUID. KVM's handling of CR4.LASS via + * KVM_SET_SREGS is covered by set_sregs_test. + * + * Testing LASS enforcement requires running supervisor code in the + * upper half of the address space, which the KVM selftests framework + * doesn't support. Enabling CR4.LASS in the current framework would + * make the next instruction fetch a violation and triple-fault the + * guest. + */ +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" + +/* + * Without LASS in CPUID, a guest write must generate #GP without + * changing CR4. Reserved bits are owned by KVM so the write is + * guaranteed to exit to KVM. + */ +static void guest_code(void) +{ + u8 vector; + + GUEST_ASSERT(!this_cpu_has(X86_FEATURE_LASS)); + + vector = kvm_asm_safe("mov %[cr4], %%cr4", + [cr4] "r"(get_cr4() | X86_CR4_LASS)); + __GUEST_ASSERT(vector == GP_VECTOR, + "Wanted #GP on CR4.LASS, got %s", ex_str(vector)); + GUEST_ASSERT(!(get_cr4() & X86_CR4_LASS)); + + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_LASS)); + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_LASS); + + vcpu_run(vcpu); + TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); + + kvm_vm_free(vm); + return 0; +} diff --git a/tools/testing/selftests/kvm/x86/set_sregs_test.c b/tools/testing/selftests/kvm/x86/set_sregs_test.c index 603226ffe437..8b375fea84d0 100644 --- a/tools/testing/selftests/kvm/x86/set_sregs_test.c +++ b/tools/testing/selftests/kvm/x86/set_sregs_test.c @@ -72,6 +72,8 @@ static u64 calc_supported_cr4_feature_bits(void) cr4 |= X86_CR4_SMAP; if (kvm_cpu_has(X86_FEATURE_PKU)) cr4 |= X86_CR4_PKE; + if (kvm_cpu_has(X86_FEATURE_LASS)) + cr4 |= X86_CR4_LASS; return cr4; } @@ -128,6 +130,7 @@ static void test_cr_bits(struct kvm_vcpu *vcpu, u64 cr4) TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMEP); TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMAP); TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_PKE); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_LASS); for (i = 32; i < 64; i++) TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, BIT(i)); -- 2.43.0

