From 5be2d20f981843faa8b754479102ac8e9e707ead Mon Sep 17 00:00:00 2001 From: Hisam Mehboob Date: Mon, 3 Aug 2026 22:08:28 +0500 Subject: [PATCH 01/83] KVM: selftests: Replace ulong with unsigned long KVM selftests use the non-standard ulong alias in several files. glibc exposes the alias transitively through its headers, whereas musl does not, causing musl builds to fail with ulong undeclared. Use unsigned long directly instead of depending on a libc-specific typedef. This also avoids adding a duplicate typedef to a shared tools header. Reported-by: Aqib Faruqui Closes: https://lore.kernel.org/r/20250829142556.72577-10-aqibaf@amazon.com Signed-off-by: Hisam Mehboob Link: https://patch.msgid.link/20260803170825.925561-4-hisamshar@gmail.com Signed-off-by: Sean Christopherson --- .../testing/selftests/kvm/arm64/hypercalls.c | 2 +- .../testing/selftests/kvm/memslot_perf_test.c | 2 +- tools/testing/selftests/kvm/steal_time.c | 20 +++++++++---------- tools/testing/selftests/kvm/x86/amx_test.c | 2 +- tools/testing/selftests/kvm/x86/cpuid_test.c | 2 +- .../testing/selftests/kvm/x86/hyperv_clock.c | 2 +- .../testing/selftests/kvm/x86/hyperv_evmcs.c | 4 ++-- .../selftests/kvm/x86/hyperv_svm_test.c | 2 +- .../selftests/kvm/x86/set_boot_cpu_id.c | 2 +- tools/testing/selftests/kvm/x86/state_test.c | 4 ++-- .../testing/selftests/kvm/x86/tsc_msrs_test.c | 2 +- .../kvm/x86/vmx_nested_la57_state_test.c | 2 +- .../kvm/x86/vmx_preemption_timer_test.c | 4 ++-- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/hypercalls.c b/tools/testing/selftests/kvm/arm64/hypercalls.c index 5d96cdf382c4..90d22b2cb6cc 100644 --- a/tools/testing/selftests/kvm/arm64/hypercalls.c +++ b/tools/testing/selftests/kvm/arm64/hypercalls.c @@ -162,7 +162,7 @@ struct st_time { static void steal_time_init(struct kvm_vcpu *vcpu) { - u64 st_ipa = (ulong)ST_GPA_BASE; + u64 st_ipa = (unsigned long)ST_GPA_BASE; unsigned int gpages; gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE); diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c index e1299611be32..bc184d4e1656 100644 --- a/tools/testing/selftests/kvm/memslot_perf_test.c +++ b/tools/testing/selftests/kvm/memslot_perf_test.c @@ -152,7 +152,7 @@ static void *vcpu_worker(void *__data) case UCALL_SYNC: TEST_ASSERT(uc.args[1] == 0, "Unexpected sync ucall, got %lx", - (ulong)uc.args[1]); + (unsigned long)uc.args[1]); sem_post(&vcpu_ready); continue; case UCALL_NONE: diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c index bc3c62b72c58..e85e0f1e7fdb 100644 --- a/tools/testing/selftests/kvm/steal_time.c +++ b/tools/testing/selftests/kvm/steal_time.c @@ -73,12 +73,12 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) WRITE_AND_SYNC_TO_GUEST(vcpu->vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE)); - vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_MSR_ENABLED); + vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, (unsigned long)st_gva[i] | KVM_MSR_ENABLED); } static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { - struct kvm_steal_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); + struct kvm_steal_time *st = addr_gva2hva(vm, (unsigned long)st_gva[vcpu_idx]); ksft_print_msg("VCPU%d:\n", vcpu_idx); ksft_print_msg(" steal: %lld\n", st->steal); @@ -102,7 +102,7 @@ static void check_steal_time_uapi(void) vm = vm_create_with_one_vcpu(&vcpu, NULL); ret = _vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, - (ulong)ST_GPA_BASE | KVM_STEAL_RESERVED_MASK); + (unsigned long)ST_GPA_BASE | KVM_STEAL_RESERVED_MASK); TEST_ASSERT(ret == 0, "Bad GPA didn't fail"); kvm_vm_free(vm); @@ -151,7 +151,7 @@ static void guest_code(int cpu) status = smccc(PV_TIME_ST, 0); GUEST_ASSERT_NE(status, -1); - GUEST_ASSERT_EQ(status, (ulong)st_gva[cpu]); + GUEST_ASSERT_EQ(status, (unsigned long)st_gva[cpu]); st = (struct st_time *)status; GUEST_SYNC(0); @@ -189,13 +189,13 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) /* ST_GPA_BASE is identity mapped */ WRITE_AND_SYNC_TO_GUEST(vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE)); - st_ipa = (ulong)st_gva[i]; + st_ipa = (unsigned long)st_gva[i]; vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); } static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { - struct st_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); + struct st_time *st = addr_gva2hva(vm, (unsigned long)st_gva[vcpu_idx]); ksft_print_msg("VCPU%d:\n", vcpu_idx); ksft_print_msg(" rev: %d\n", st->rev); @@ -222,11 +222,11 @@ static void check_steal_time_uapi(void) vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, 1, 0); virt_map(vm, ST_GPA_BASE, ST_GPA_BASE, 1); - st_ipa = (ulong)ST_GPA_BASE | 1; + st_ipa = (unsigned long)ST_GPA_BASE | 1; ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); TEST_ASSERT(ret == -1 && errno == EINVAL, "Bad IPA didn't report EINVAL"); - st_ipa = (ulong)ST_GPA_BASE; + st_ipa = (unsigned long)ST_GPA_BASE; vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); @@ -315,7 +315,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { - struct sta_struct *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); + struct sta_struct *st = addr_gva2hva(vm, (unsigned long)st_gva[vcpu_idx]); int i; pr_info("VCPU%d:\n", vcpu_idx); @@ -451,7 +451,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { - struct kvm_steal_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); + struct kvm_steal_time *st = addr_gva2hva(vm, (unsigned long)st_gva[vcpu_idx]); ksft_print_msg("VCPU%d:\n", vcpu_idx); ksft_print_msg(" steal: %lld\n", st->steal); diff --git a/tools/testing/selftests/kvm/x86/amx_test.c b/tools/testing/selftests/kvm/x86/amx_test.c index 4e63da2b1889..66b6e442c41c 100644 --- a/tools/testing/selftests/kvm/x86/amx_test.c +++ b/tools/testing/selftests/kvm/x86/amx_test.c @@ -325,7 +325,7 @@ int main(int argc, char *argv[]) vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", - (ulong) regs2.rdi, (ulong) regs2.rsi); + (unsigned long)regs2.rdi, (unsigned long)regs2.rsi); } break; case UCALL_DONE: diff --git a/tools/testing/selftests/kvm/x86/cpuid_test.c b/tools/testing/selftests/kvm/x86/cpuid_test.c index ef0ddd240887..dcd99a0501ee 100644 --- a/tools/testing/selftests/kvm/x86/cpuid_test.c +++ b/tools/testing/selftests/kvm/x86/cpuid_test.c @@ -128,7 +128,7 @@ static void run_vcpu(struct kvm_vcpu *vcpu, int stage) TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx", - stage + 1, (ulong)uc.args[1]); + stage + 1, (unsigned long)uc.args[1]); return; case UCALL_DONE: return; diff --git a/tools/testing/selftests/kvm/x86/hyperv_clock.c b/tools/testing/selftests/kvm/x86/hyperv_clock.c index d5d779623cc6..7a1b3b5d1e62 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_clock.c +++ b/tools/testing/selftests/kvm/x86/hyperv_clock.c @@ -248,7 +248,7 @@ int main(void) TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx", - stage, (ulong)uc.args[1]); + stage, (unsigned long)uc.args[1]); /* Reset kvmclock triggering TSC page update */ if (stage == 7 || stage == 8 || stage == 10) { diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 63ea1533e4ea..981a3a6d72fe 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -221,7 +221,7 @@ static struct kvm_vcpu *save_restore_vm(struct kvm_vm *vm, vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", - (ulong) regs2.rdi, (ulong) regs2.rsi); + (unsigned long)regs2.rdi, (unsigned long)regs2.rsi); return vcpu; } @@ -277,7 +277,7 @@ int main(int argc, char *argv[]) /* UCALL_SYNC is handled here. */ TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx", - stage, (ulong)uc.args[1]); + stage, (unsigned long)uc.args[1]); vcpu = save_restore_vm(vm, vcpu); diff --git a/tools/testing/selftests/kvm/x86/hyperv_svm_test.c b/tools/testing/selftests/kvm/x86/hyperv_svm_test.c index 1f74b0fa9b83..3a3dc31072e8 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_svm_test.c +++ b/tools/testing/selftests/kvm/x86/hyperv_svm_test.c @@ -186,7 +186,7 @@ int main(int argc, char *argv[]) /* UCALL_SYNC is handled here. */ TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx", - stage, (ulong)uc.args[1]); + stage, (unsigned long)uc.args[1]); } diff --git a/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c b/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c index 8e3898646c69..909cd9e83fda 100644 --- a/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c +++ b/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c @@ -69,7 +69,7 @@ static void run_vcpu(struct kvm_vcpu *vcpu) TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx", - stage + 1, (ulong)uc.args[1]); + stage + 1, (unsigned long)uc.args[1]); test_set_bsp_busy(vcpu, "while running vm"); break; case UCALL_DONE: diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index 4a1056a6cb8d..e6f521a6e737 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -296,7 +296,7 @@ int main(int argc, char *argv[]) /* UCALL_SYNC is handled here. */ TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx", - stage, (ulong)uc.args[1]); + stage, (unsigned long)uc.args[1]); state = vcpu_save_state(vcpu); memset(®s1, 0, sizeof(regs1)); @@ -343,7 +343,7 @@ int main(int argc, char *argv[]) vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", - (ulong) regs2.rdi, (ulong) regs2.rsi); + (unsigned long)regs2.rdi, (unsigned long)regs2.rsi); } done: diff --git a/tools/testing/selftests/kvm/x86/tsc_msrs_test.c b/tools/testing/selftests/kvm/x86/tsc_msrs_test.c index 91583969a14f..55779773614b 100644 --- a/tools/testing/selftests/kvm/x86/tsc_msrs_test.c +++ b/tools/testing/selftests/kvm/x86/tsc_msrs_test.c @@ -78,7 +78,7 @@ static void run_vcpu(struct kvm_vcpu *vcpu, int stage) else ksft_test_result_fail( "stage %d: Unexpected register values vmexit, got %lx", - stage + 1, (ulong)uc.args[1]); + stage + 1, (unsigned long)uc.args[1]); return; case UCALL_DONE: ksft_test_result_pass("stage %d passed\n", stage + 1); diff --git a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c index 75073efa926d..448721b54181 100644 --- a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c @@ -109,7 +109,7 @@ int main(int argc, char *argv[]) } TEST_ASSERT(uc.args[1] == stage, - "Expected stage %d, got stage %lu", stage, (ulong)uc.args[1]); + "Expected stage %d, got stage %lu", stage, (unsigned long)uc.args[1]); if (stage == 1) { pr_info("L2 is active; performing save/restore.\n"); state = vcpu_save_state(vcpu); diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index eb8021c33cd4..72fbf1b70e9f 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -193,7 +193,7 @@ int main(int argc, char *argv[]) /* UCALL_SYNC is handled here. */ TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx", - stage, (ulong)uc.args[1]); + stage, (unsigned long)uc.args[1]); /* * If this stage 2 then we should verify the vmx pt expiry * is as expected. @@ -234,7 +234,7 @@ int main(int argc, char *argv[]) vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", - (ulong) regs2.rdi, (ulong) regs2.rsi); + (unsigned long)regs2.rdi, (unsigned long)regs2.rsi); } done: From 73ac07325851f50b602bb2d009c9d0eb16762c42 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:04 -0700 Subject: [PATCH 02/83] KVM: selftests: Use MEM_REGION_PT memslot instead of '0' for s390 regions/segments Use MEM_REGION_PT to indirectly specify memslot '0' instead of hardcoding the memslot in s390's virtual memory allocator, as a first step towards taking the allocation type instead of the exact, raw memslot in the physical memory allocator. Note, the only tests that set MEM_REGION_PT to a memslot other than '0' are arm64-only, i.e. s390 always uses memslot '0' for page tables. No functional change intended. Tested-by: Itaru Kitayama Acked-by: Claudio Imbrenda Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-2-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/s390/processor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/lib/s390/processor.c b/tools/testing/selftests/kvm/lib/s390/processor.c index a9adb3782b35..fb9dab92d4d1 100644 --- a/tools/testing/selftests/kvm/lib/s390/processor.c +++ b/tools/testing/selftests/kvm/lib/s390/processor.c @@ -39,7 +39,8 @@ static u64 virt_alloc_region(struct kvm_vm *vm, int ri) u64 taddr; taddr = vm_phy_pages_alloc(vm, ri < 4 ? PAGES_PER_REGION : 1, - KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0); + KVM_GUEST_PAGE_TABLE_MIN_PADDR, + vm->memslots[MEM_REGION_PT]); memset(addr_gpa2hva(vm, taddr), 0xff, PAGES_PER_REGION * vm->page_size); return (taddr & REGION_ENTRY_ORIGIN) From 9886413e04667924c9b42ab8833712e2445302ad Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:05 -0700 Subject: [PATCH 03/83] KVM: selftests: Use the common minimum GPA for page tables on LoongArch Use the common minimum GPA for page tables (0x180000) on LoongArch instead of LoongArch's custom minimum of 0x200000, so that LoongArch can use vm_alloc_page_table() instead of open coding a rough equivalent. There are no known reasons why LoongArch can't use a lower, less-aligned minimum. The more-aligned value was originally selected to support huge pages for the guest, but that only requires the address of the final allocation to be aligned, the addresses of page table pages is irrelevant on that front. Reviewed-by: Bibo Mao Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-3-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/loongarch/processor.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/loongarch/processor.c b/tools/testing/selftests/kvm/lib/loongarch/processor.c index 47bbde3e205a..057a77713542 100644 --- a/tools/testing/selftests/kvm/lib/loongarch/processor.c +++ b/tools/testing/selftests/kvm/lib/loongarch/processor.c @@ -9,7 +9,6 @@ #include "processor.h" #include "ucall_common.h" -#define LOONGARCH_PAGE_TABLE_PHYS_MIN 0x200000 #define LOONGARCH_GUEST_STACK_VADDR_MIN 0x200000 static gpa_t invalid_pgtable[4]; @@ -57,7 +56,7 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) child = table = 0; for (i = 0; i < vm->mmu.pgtable_levels; i++) { invalid_pgtable[i] = child; - table = vm_phy_page_alloc(vm, LOONGARCH_PAGE_TABLE_PHYS_MIN, + table = vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, vm->memslots[MEM_REGION_PT]); TEST_ASSERT(table, "Fail to allocate page tale at level %d\n", i); virt_set_pgtable(vm, table, child); From befd94805906f986a564c16bab17467a114cfb81 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:06 -0700 Subject: [PATCH 04/83] KVM: selftests: Use vm_alloc_page_table() to allocate LoongArch page tables Now that LoongArch uses KVM_GUEST_PAGE_TABLE_MIN_PADDR for its page table page allocations, use the common vm_alloc_page_table() instead of open coding the same. Opportunistically drop the assert that the allocation succeeded, as the allocator itself guarantees success. No functional change intended. Reviewed-by: Bibo Mao Tested-by: Itaru Kitayama Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-4-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/loongarch/processor.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/loongarch/processor.c b/tools/testing/selftests/kvm/lib/loongarch/processor.c index 057a77713542..fcd41bc6fbda 100644 --- a/tools/testing/selftests/kvm/lib/loongarch/processor.c +++ b/tools/testing/selftests/kvm/lib/loongarch/processor.c @@ -56,9 +56,7 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) child = table = 0; for (i = 0; i < vm->mmu.pgtable_levels; i++) { invalid_pgtable[i] = child; - table = vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); - TEST_ASSERT(table, "Fail to allocate page tale at level %d\n", i); + table = vm_alloc_page_table(vm); virt_set_pgtable(vm, table, child); child = table; } From d220e33516fa3faa458b814d08dfa680f93ea2ae Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:07 -0700 Subject: [PATCH 05/83] KVM: selftests: Rename "num" param to "nr_pages" for physical page allocators Rename the "num" param in the physical page allocator APIs to "nr_pages" to make the unit and role of the param more obvious. Opportunistically rework the function comment to drop the superfluous kerneldoc style, and to very explicitly call out that success is guaranteed. No functional change intended. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-5-seanjc@google.com Signed-off-by: Sean Christopherson --- .../testing/selftests/kvm/include/kvm_util.h | 6 ++-- tools/testing/selftests/kvm/lib/kvm_util.c | 35 ++++++------------- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 5a7a455b5387..95bc0a78d2bc 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -991,11 +991,11 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); const char *exit_reason_str(unsigned int exit_reason); gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot); -gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, gpa_t min_gpa, +gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot, bool protected); gpa_t vm_alloc_page_table(struct kvm_vm *vm); -static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, +static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot) { /* @@ -1003,7 +1003,7 @@ static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, * protected memory, as the majority of memory for such VMs is * protected, i.e. using shared memory is effectively opt-in. */ - return __vm_phy_pages_alloc(vm, num, min_gpa, memslot, + return __vm_phy_pages_alloc(vm, nr_pages, min_gpa, memslot, vm_arch_has_protected_memory(vm)); } diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 9ddc047d5c27..76d2598a9f90 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -2025,33 +2025,20 @@ const char *exit_reason_str(unsigned int exit_reason) } /* - * Physical Contiguous Page Allocator + * Allocate contiguous (guest) physical pages in a given memory region, at or + * above the minimum specified GPA. If the memory is protected/private, also + * add the allocated pages to the region's set of protected pages, e.g. so that + * arch code knows which pages need to be encrypted when launching the VM. * - * Input Args: - * vm - Virtual Machine - * num - number of pages - * min_gpa - Physical address minimum - * memslot - Memory region to allocate page from - * protected - True if the pages will be used as protected/private memory - * - * Output Args: None - * - * Return: - * Starting physical address - * - * Within the VM specified by vm, locates a range of available physical - * pages at or above min_gpa. If found, the pages are marked as in use - * and their base address is returned. A TEST_ASSERT failure occurs if - * not enough pages are available at or above min_gpa. + * Note, success is guaranteed! */ -gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, - gpa_t min_gpa, u32 memslot, - bool protected) +gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, + u32 memslot, bool protected) { struct userspace_mem_region *region; sparsebit_idx_t pg, base; - TEST_ASSERT(num > 0, "Must allocate at least one page"); + TEST_ASSERT(nr_pages, "Must allocate at least one page"); TEST_ASSERT((min_gpa % vm->page_size) == 0, "Min physical address " "not divisible by page size.\n" @@ -2064,13 +2051,13 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, base = pg = min_gpa >> vm->page_shift; do { - for (; pg < base + num; ++pg) { + for (; pg < base + nr_pages; ++pg) { if (!sparsebit_is_set(region->unused_phy_pages, pg)) { base = pg = sparsebit_next_set(region->unused_phy_pages, pg); break; } } - } while (pg && pg != base + num); + } while (pg && pg != base + nr_pages); if (pg == 0) { fprintf(stderr, "No guest physical page available, " @@ -2081,7 +2068,7 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, abort(); } - for (pg = base; pg < base + num; ++pg) { + for (pg = base; pg < base + nr_pages; ++pg) { sparsebit_clear(region->unused_phy_pages, pg); if (protected) sparsebit_set(region->protected_phy_pages, pg); From 2c0555c51e7ca615c2bba7a99efd720876239b05 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 2 Sep 2026 09:41:08 -0700 Subject: [PATCH 06/83] KVM: selftests: Use goto instead of do-while to retry finding unused physical pages Rework the core loop of the physical page allocator to use a goto instead of do-while loop when retrying with a new base address. The do-while loop obviously works, but it obfuscates the success vs. failure paths, and in general is convoluted. For all intents and purposes, no functional change intended. Signed-off-by: Nicholas Piggin [Rebased to latest mainline tree] Signed-off-by: Ritesh Harjani (IBM) [sean: separate from alignment change, use enomem label, write changelog] Tested-by: Itaru Kitayama Acked-by: Gautam Menghani Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-6-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/kvm_util.c | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 76d2598a9f90..87e747bf3e3e 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -2049,23 +2049,15 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, TEST_ASSERT(!protected || region->protected_phy_pages, "Region doesn't support protected memory"); - base = pg = min_gpa >> vm->page_shift; - do { - for (; pg < base + nr_pages; ++pg) { - if (!sparsebit_is_set(region->unused_phy_pages, pg)) { - base = pg = sparsebit_next_set(region->unused_phy_pages, pg); - break; - } + base = min_gpa >> vm->page_shift; +again: + for (pg = base; pg < base + nr_pages; ++pg) { + if (!sparsebit_is_set(region->unused_phy_pages, pg)) { + base = sparsebit_next_set(region->unused_phy_pages, pg); + if (!base) + goto enomem; + goto again; } - } while (pg && pg != base + nr_pages); - - if (pg == 0) { - fprintf(stderr, "No guest physical page available, " - "min_gpa: 0x%lx page_size: 0x%x memslot: %u\n", - min_gpa, vm->page_size, memslot); - fputs("---- vm dump ----\n", stderr); - vm_dump(stderr, vm, 2); - abort(); } for (pg = base; pg < base + nr_pages; ++pg) { @@ -2075,6 +2067,14 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, } return base * vm->page_size; + +enomem: + fprintf(stderr, "No guest physical page available, min_gpa: 0x%lx page_size: 0x%x memslot: %u\n", + min_gpa, vm->page_size, memslot); + fputs("---- vm dump ----\n", stderr); + vm_dump(stderr, vm, 2); + abort(); + __builtin_unreachable(); } gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot) From 2429ecf7ccd52cac5d49adba5e880a25f16314ea Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:09 -0700 Subject: [PATCH 07/83] KVM: selftests: Extend page allocator to support naturally aligned allocations Extend the page allocator to support naturally aligned allocations, as some flavors of PowerPC page tables require multiple guest pages per table, and those tables need to be naturally aligned. Take care to align the potential base address *inside* the retry loop, i.e. the base needs to be (re)aligned on every attempt. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-7-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/kvm_util.h | 2 ++ tools/testing/selftests/kvm/lib/kvm_util.c | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 95bc0a78d2bc..4732b9528812 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -991,6 +991,8 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); const char *exit_reason_str(unsigned int exit_reason); gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot); +gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, + u32 memslot, bool protected, bool naturally_aligned); gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot, bool protected); gpa_t vm_alloc_page_table(struct kvm_vm *vm); diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 87e747bf3e3e..4c14b1c73239 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #define KVM_UTIL_MIN_PFN 2 @@ -2032,9 +2033,10 @@ const char *exit_reason_str(unsigned int exit_reason) * * Note, success is guaranteed! */ -gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, - u32 memslot, bool protected) +gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, + u32 memslot, bool protected, bool naturally_aligned) { + size_t alignment = naturally_aligned ? nr_pages : 1; struct userspace_mem_region *region; sparsebit_idx_t pg, base; @@ -2051,6 +2053,7 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, base = min_gpa >> vm->page_shift; again: + base = ALIGN(base, alignment); for (pg = base; pg < base + nr_pages; ++pg) { if (!sparsebit_is_set(region->unused_phy_pages, pg)) { base = sparsebit_next_set(region->unused_phy_pages, pg); @@ -2077,6 +2080,12 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, __builtin_unreachable(); } +gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, + u32 memslot, bool protected) +{ + return ____vm_phy_pages_alloc(vm, nr_pages, min_gpa, memslot, protected, false); +} + gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot) { return vm_phy_pages_alloc(vm, 1, min_gpa, memslot); From 54e83f729385f918a4a50259a5f32c63b87b99fd Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:10 -0700 Subject: [PATCH 08/83] KVM: selftests: Make the single-page allocator APIs static inline Move the single-page allocator APIs to kvm_util.h as inline functions, as the are trivial wrappers, and unlike __vm_phy_pages_alloc(), are expected to remain that way for the foreseeable future. No functional change intended. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-8-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/kvm_util.h | 14 ++++++++++++-- tools/testing/selftests/kvm/lib/kvm_util.c | 11 ----------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 4732b9528812..d9d39a8894e2 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -990,12 +990,10 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); const char *exit_reason_str(unsigned int exit_reason); -gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot); gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot, bool protected, bool naturally_aligned); gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot, bool protected); -gpa_t vm_alloc_page_table(struct kvm_vm *vm); static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot) @@ -1009,6 +1007,18 @@ static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, vm_arch_has_protected_memory(vm)); } +static inline gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, + u32 memslot) +{ + return vm_phy_pages_alloc(vm, 1, min_gpa, memslot); +} + +static inline gpa_t vm_alloc_page_table(struct kvm_vm *vm) +{ + return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, + vm->memslots[MEM_REGION_PT]); +} + /* * ____vm_create() does KVM_CREATE_VM and little else. __vm_create() also * loads the test binary into guest memory and creates an IRQ chip (x86 only). diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 4c14b1c73239..bb73671770de 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -2086,17 +2086,6 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, return ____vm_phy_pages_alloc(vm, nr_pages, min_gpa, memslot, protected, false); } -gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot) -{ - return vm_phy_pages_alloc(vm, 1, min_gpa, memslot); -} - -gpa_t vm_alloc_page_table(struct kvm_vm *vm) -{ - return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); -} - /* * Address Guest Virtual to Host Virtual * From b8d9e6ec747a7bb7c11dc6614db7bdd5c9e260f6 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:11 -0700 Subject: [PATCH 09/83] KVM: selftests: Use the innermost page allocator API in the memslot perf test Use the quad-underscores physical page allocator API in the memslot perf test in anticipation of converting the outer versions to take the memory region type instead of the raw memslot. Stating the obvious, the memslot perf test creates and allocates from multiple memslots, and so can't use MEM_REGION_TEST_DATA to communicate which region/slot to allocate from. No functional change intended. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-9-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/memslot_perf_test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c index bc184d4e1656..1e241390e11e 100644 --- a/tools/testing/selftests/kvm/memslot_perf_test.c +++ b/tools/testing/selftests/kvm/memslot_perf_test.c @@ -339,7 +339,8 @@ static bool prepare_vm(struct vm_data *data, int nslots, u64 *maxslots, if (slot == data->nslots) npages += rempages; - gpa = vm_phy_pages_alloc(data->vm, npages, guest_addr, slot); + gpa = ____vm_phy_pages_alloc(data->vm, npages, guest_addr, slot, + false, false); TEST_ASSERT(gpa == guest_addr, "vm_phy_pages_alloc() failed"); From 9c0061d2e7cd25bea2adcf3aecf1aa4027e83343 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:12 -0700 Subject: [PATCH 10/83] KVM: selftests: Use the innermost page allocator API in s390's IRQ routing test Use the quad-underscores page allocator in s390's IRQ routing test in anticipation of dropping the @min_gpa param from the primary APIs. Note, it's not entirely clear that the test *needs* to use a very magical @min_gpa, i.e. it's possible a more elegant solution exists. But that's a problem for a different day. No functional change intended. Tested-by: Itaru Kitayama Acked-by: Claudio Imbrenda Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-10-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/s390/irq_routing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/s390/irq_routing.c b/tools/testing/selftests/kvm/s390/irq_routing.c index f3839284ac08..cd26cc1aed80 100644 --- a/tools/testing/selftests/kvm/s390/irq_routing.c +++ b/tools/testing/selftests/kvm/s390/irq_routing.c @@ -36,7 +36,7 @@ static void test(void) }; vm = vm_create_with_one_vcpu(&vcpu, guest_code); - mem = vm_phy_pages_alloc(vm, 2, 4096 * 42, 0); + mem = ____vm_phy_pages_alloc(vm, 2, 4096 * 42, 0, false, false); routing = kvm_gsi_routing_create(); routing->nr = 1; From 7f477ba0878e9db2cfe5da8b6d058f3a677fad91 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:13 -0700 Subject: [PATCH 11/83] KVM: selftests: Add a wrapper API to allocate multiple page table pages Add vm_alloc_page_table_pages() and use it throughout arch code to replace open coded equivalents. No functional change intended. Reviewed-by: Itaru Kitayama Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Acked-by: Claudio Imbrenda Acked-by: Anup Patel Acked-by: Gautam Menghani Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-11-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/kvm_util.h | 9 +++++++-- tools/testing/selftests/kvm/lib/arm64/processor.c | 4 +--- tools/testing/selftests/kvm/lib/riscv/processor.c | 4 +--- tools/testing/selftests/kvm/lib/s390/processor.c | 8 ++------ 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index d9d39a8894e2..c56f342b16fd 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -1013,10 +1013,15 @@ static inline gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, return vm_phy_pages_alloc(vm, 1, min_gpa, memslot); } +static inline gpa_t vm_alloc_page_table_pages(struct kvm_vm *vm, size_t nr_pages) +{ + return vm_phy_pages_alloc(vm, nr_pages, KVM_GUEST_PAGE_TABLE_MIN_PADDR, + vm->memslots[MEM_REGION_PT]); +} + static inline gpa_t vm_alloc_page_table(struct kvm_vm *vm) { - return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); + return vm_alloc_page_table_pages(vm, 1); } /* diff --git a/tools/testing/selftests/kvm/lib/arm64/processor.c b/tools/testing/selftests/kvm/lib/arm64/processor.c index 01325bf4d36f..2dcaa2c4a038 100644 --- a/tools/testing/selftests/kvm/lib/arm64/processor.c +++ b/tools/testing/selftests/kvm/lib/arm64/processor.c @@ -115,9 +115,7 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) if (vm->mmu.pgd_created) return; - vm->mmu.pgd = vm_phy_pages_alloc(vm, nr_pages, - KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); + vm->mmu.pgd = vm_alloc_page_table_pages(vm, nr_pages); vm->mmu.pgd_created = true; } diff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c index ded5429f3448..c175dcd14114 100644 --- a/tools/testing/selftests/kvm/lib/riscv/processor.c +++ b/tools/testing/selftests/kvm/lib/riscv/processor.c @@ -69,9 +69,7 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) if (vm->mmu.pgd_created) return; - vm->mmu.pgd = vm_phy_pages_alloc(vm, nr_pages, - KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); + vm->mmu.pgd = vm_alloc_page_table_pages(vm, nr_pages); vm->mmu.pgd_created = true; } diff --git a/tools/testing/selftests/kvm/lib/s390/processor.c b/tools/testing/selftests/kvm/lib/s390/processor.c index fb9dab92d4d1..33f49d743b5f 100644 --- a/tools/testing/selftests/kvm/lib/s390/processor.c +++ b/tools/testing/selftests/kvm/lib/s390/processor.c @@ -20,9 +20,7 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) if (vm->mmu.pgd_created) return; - gpa = vm_phy_pages_alloc(vm, PAGES_PER_REGION, - KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); + gpa = vm_alloc_page_table_pages(vm, PAGES_PER_REGION); memset(addr_gpa2hva(vm, gpa), 0xff, PAGES_PER_REGION * vm->page_size); vm->mmu.pgd = gpa; @@ -38,9 +36,7 @@ static u64 virt_alloc_region(struct kvm_vm *vm, int ri) { u64 taddr; - taddr = vm_phy_pages_alloc(vm, ri < 4 ? PAGES_PER_REGION : 1, - KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); + taddr = vm_alloc_page_table_pages(vm, ri < 4 ? PAGES_PER_REGION : 1); memset(addr_gpa2hva(vm, taddr), 0xff, PAGES_PER_REGION * vm->page_size); return (taddr & REGION_ENTRY_ORIGIN) From 9e4e2281df12260e4b3419101d7d4dc027ab5d2f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:14 -0700 Subject: [PATCH 12/83] KVM: selftests: Initialize vm->memslots[] with invalid memslots during creation Initialize vm->memslots[] with an invalid memslots value during very initial VM creation so that failure to configure the core memory regions generates an immediate assert instead of potential weirdness, e.g. if slot0 is coincidentally valid. This will also allow hardening the region APIs to guard overriding a region's memslot multiple times. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Acked-by: Gautam Menghani Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-12-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/kvm_util.h | 2 ++ tools/testing/selftests/kvm/lib/kvm_util.c | 5 +++++ tools/testing/selftests/kvm/lib/x86/processor.c | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index c56f342b16fd..304ef6af3268 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -33,6 +33,8 @@ #define NSEC_PER_SEC 1000000000L +#define KVM_INVALID_MEMSLOT UINT_MAX + struct userspace_mem_region { struct kvm_userspace_memory_region2 region; struct sparsebit *unused_phy_pages; diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index bb73671770de..ca900ed111d6 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -288,6 +288,7 @@ __weak void vm_populate_gva_bitmap(struct kvm_vm *vm) struct kvm_vm *____vm_create(struct vm_shape shape) { struct kvm_vm *vm; + int i; vm = calloc(1, sizeof(*vm)); TEST_ASSERT(vm != NULL, "Insufficient Memory"); @@ -296,6 +297,8 @@ struct kvm_vm *____vm_create(struct vm_shape shape) vm->regions.gpa_tree = RB_ROOT; vm->regions.hva_tree = RB_ROOT; hash_init(vm->regions.slot_hash); + for (i = 0; i < NR_MEM_REGIONS; i++) + vm->memslots[i] = KVM_INVALID_MEMSLOT; vm->mode = shape.mode; vm->type = shape.type; @@ -1190,6 +1193,8 @@ memslot2region(struct kvm_vm *vm, u32 memslot) { struct userspace_mem_region *region; + TEST_ASSERT(memslot != KVM_INVALID_MEMSLOT, "vm->memslots[] unpopulated?"); + hash_for_each_possible(vm->regions.slot_hash, region, slot_node, memslot) if (region->region.slot == memslot) diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index d31fa81ea075..ea5fa59888af 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -529,7 +529,10 @@ void tdp_identity_map_default_memslots(struct kvm_vm *vm) /* Only memslot 0 is mapped here, ensure it's the only one being used */ for (s = 0; s < NR_MEM_REGIONS; s++) - TEST_ASSERT_EQ(vm->memslots[s], 0); + TEST_ASSERT(!vm->memslots[s] || + vm->memslots[s] == KVM_INVALID_MEMSLOT, + "Unhandled memslot '%u' for type '%u'", + vm->memslots[s], s); i = (region->region.guest_phys_addr >> vm->page_shift) - 1; last = i + (region->region.memory_size >> vm->page_shift); From d69b3e714265068778a4f0832897f85daa5b90d8 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:15 -0700 Subject: [PATCH 13/83] KVM: selftests: Add APIs to override memory region types with custom memslots Add and use APIs to override memory region types instead of open coding the logic in tests, partly to dedup test code, but mostly to add some amount of protection against conflicting overrides. This will allow converting more tests to override memory regions without taking on too much risk of bugs in the long term, e.g. due to silently overriding a region multiple times. Deliberately "unroll" the loops in __vm_create() and the CMMA test's create_main_memslot() to avoid filling all memslots in anticipation of adding an "extra" memslot that is for test use and *only* test use, i.e. shouldn't be configured by core library code. Converting tests to overriding memory regions will allow taking the region type instead of the raw memslot in the physical page allocator APIs. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Acked-by: Claudio Imbrenda Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-13-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/arm64/page_fault_test.c | 18 +++++------ .../testing/selftests/kvm/include/kvm_util.h | 30 +++++++++++++++++++ tools/testing/selftests/kvm/lib/kvm_util.c | 8 +++-- tools/testing/selftests/kvm/s390/cmma_test.c | 19 ++++-------- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/page_fault_test.c b/tools/testing/selftests/kvm/arm64/page_fault_test.c index 6bb3d82906b2..b0e3fc8cd76a 100644 --- a/tools/testing/selftests/kvm/arm64/page_fault_test.c +++ b/tools/testing/selftests/kvm/arm64/page_fault_test.c @@ -597,18 +597,16 @@ static void setup_memslots(struct kvm_vm *vm, struct test_params *p) vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, CODE_AND_DATA_MEMSLOT, code_npages, 0); - vm->memslots[MEM_REGION_CODE] = CODE_AND_DATA_MEMSLOT; - vm->memslots[MEM_REGION_DATA] = CODE_AND_DATA_MEMSLOT; + ____vm_override_mem_region(vm, MEM_REGION_CODE, CODE_AND_DATA_MEMSLOT); + ____vm_override_mem_region(vm, MEM_REGION_DATA, CODE_AND_DATA_MEMSLOT); - vm_userspace_mem_region_add(vm, p->src_type, data_gpa - pt_size, - PAGE_TABLE_MEMSLOT, pt_size / guest_page_size, - p->test_desc->pt_memslot_flags); - vm->memslots[MEM_REGION_PT] = PAGE_TABLE_MEMSLOT; + __vm_override_mem_region(vm, MEM_REGION_PT, p->src_type, data_gpa - pt_size, + PAGE_TABLE_MEMSLOT, pt_size / guest_page_size, + p->test_desc->pt_memslot_flags); - vm_userspace_mem_region_add(vm, p->src_type, data_gpa, TEST_DATA_MEMSLOT, - data_size / guest_page_size, - p->test_desc->data_memslot_flags); - vm->memslots[MEM_REGION_TEST_DATA] = TEST_DATA_MEMSLOT; + __vm_override_mem_region(vm, MEM_REGION_TEST_DATA, p->src_type, data_gpa, + TEST_DATA_MEMSLOT, data_size / guest_page_size, + p->test_desc->data_memslot_flags); } static void setup_ucall(struct kvm_vm *vm) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 304ef6af3268..c8faa8a02b93 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -704,6 +704,36 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, gpa_t gpa, u32 slot, u64 npages, u32 flags, int guest_memfd_fd, u64 guest_memfd_offset); + +static inline void ____vm_override_mem_region(struct kvm_vm *vm, + enum kvm_mem_region_type type, + u32 slot) +{ + TEST_ASSERT(vm->memslots[type] == KVM_INVALID_MEMSLOT, + "Memory region type '%u' was already overridden with slot=%u", + type, vm->memslots[type]); + + vm->memslots[type] = slot; +} + +static inline void __vm_override_mem_region(struct kvm_vm *vm, + enum kvm_mem_region_type type, + enum vm_mem_backing_src_type src_type, + gpa_t gpa, u32 slot, u64 npages, + u32 flags) +{ + ____vm_override_mem_region(vm, type, slot); + vm_userspace_mem_region_add(vm, src_type, gpa, slot, npages, flags); +} + +static inline void vm_override_mem_region(struct kvm_vm *vm, + enum kvm_mem_region_type type, + enum vm_mem_backing_src_type src_type, + gpa_t gpa, u32 slot, u64 npages) +{ + __vm_override_mem_region(vm, type, src_type, gpa, slot, npages, 0); +} + #ifndef vm_arch_has_protected_memory static inline bool vm_arch_has_protected_memory(struct kvm_vm *vm) { diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index ca900ed111d6..00cdc5154ed4 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -495,7 +495,7 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, nr_extra_pages); struct userspace_mem_region *slot0; struct kvm_vm *vm; - int i, flags; + int flags; kvm_set_files_rlimit(nr_runnable_vcpus); @@ -513,8 +513,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, flags |= KVM_MEM_GUEST_MEMFD; vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags); - for (i = 0; i < NR_MEM_REGIONS; i++) - vm->memslots[i] = 0; + ____vm_override_mem_region(vm, MEM_REGION_CODE, 0); + ____vm_override_mem_region(vm, MEM_REGION_PT, 0); + ____vm_override_mem_region(vm, MEM_REGION_DATA, 0); + ____vm_override_mem_region(vm, MEM_REGION_TEST_DATA, 0); kvm_vm_elf_load(vm, program_invocation_name); diff --git a/tools/testing/selftests/kvm/s390/cmma_test.c b/tools/testing/selftests/kvm/s390/cmma_test.c index 15d81b2ed7ad..73387cc3bca7 100644 --- a/tools/testing/selftests/kvm/s390/cmma_test.c +++ b/tools/testing/selftests/kvm/s390/cmma_test.c @@ -102,24 +102,17 @@ static void guest_dirty_test_data(void) static void create_main_memslot(struct kvm_vm *vm) { - int i; - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, MAIN_PAGE_COUNT, 0); - /* set the array of memslots to zero like __vm_create does */ - for (i = 0; i < NR_MEM_REGIONS; i++) - vm->memslots[i] = 0; + ____vm_override_mem_region(vm, MEM_REGION_CODE, 0); + ____vm_override_mem_region(vm, MEM_REGION_PT, 0); + ____vm_override_mem_region(vm, MEM_REGION_DATA, 0); } static void create_test_memslot(struct kvm_vm *vm) { - vm_userspace_mem_region_add(vm, - VM_MEM_SRC_ANONYMOUS, - TEST_DATA_START_GFN << vm->page_shift, - TEST_DATA_MEMSLOT, - TEST_DATA_PAGE_COUNT, - 0 - ); - vm->memslots[MEM_REGION_TEST_DATA] = TEST_DATA_MEMSLOT; + vm_override_mem_region(vm, MEM_REGION_TEST_DATA, VM_MEM_SRC_ANONYMOUS, + TEST_DATA_START_GFN << vm->page_shift, + TEST_DATA_MEMSLOT, TEST_DATA_PAGE_COUNT); } static void create_memslots(struct kvm_vm *vm) From 9f035aca8765d11f0f10a20422d5763835110096 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:16 -0700 Subject: [PATCH 14/83] KVM: selftests: Add TEST_EXTRA memory region type for "special" memslots And another memory region type to deal with extra, one-off memory regions, and use the new type to manage x86's SMRAM memslot, as another step towards taking the region type instead of the raw memslot in the physical page allocator APIs. Alternatively, SMRAM setup could simply use the quad-underscore API to continue passing in the memslot, but a surprising number of tests use an "extra" memslot for a variety of reasons. I.e. allocating memory from one (and exactly one) extra memslot isn't all that rare, and so should be treated as normal behavior, not as something extraordinary, as quad-underscore functions typically suggest. Opportunistically add comments to document the intended usage of the types, as the difference between DATA, TEST_DATA, and TEST_EXTRA in particular isn't exactly obvious. Tested-by: Itaru Kitayama Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-14-seanjc@google.com Signed-off-by: Sean Christopherson --- .../testing/selftests/kvm/include/kvm_util.h | 39 ++++++++++++++++--- tools/testing/selftests/kvm/include/x86/smm.h | 2 +- .../testing/selftests/kvm/lib/x86/processor.c | 7 ++-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index c8faa8a02b93..9b808bcf7c18 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -82,11 +82,45 @@ struct userspace_mem_regions { DECLARE_HASHTABLE(slot_hash, 9); }; +/* + * Memory region types are passed to various page allocators to communicate + * various properties and metadata related to the allocation. Note, the + * descriptions below described the primary usage of each type. Individual + * tests may allocate memory for other purposes. + * + * By default, all regions except TEST_EXTRA are mapped to memslot '0'. The + * TEST_EXTRA region is left unmapped as it's intended to be used only for test + * specific allocations, i.e. should never be used by core/infrastructure code. + * Tests can override the memslot for any or all types, e.g. so that all test + * data is allocated from a curated memslot. + */ enum kvm_mem_region_type { + /* + * The CODE region is used by lib/elf when loading the test's code into + * guest memory. + */ MEM_REGION_CODE, + /* + * The DATA region is used to allocate core data structures, e.g. vCPU + * stacks, VM exception tables, x86's TSS, etc. + */ MEM_REGION_DATA, + /* + * The PT region, a.k.a. Page Table region, is used to allocate page + * table pages. + */ MEM_REGION_PT, + /* + * The TEST_DATA region is used for allocating test data that is either + * test specific, and/or isn't considered a "core" data structure. + */ MEM_REGION_TEST_DATA, + /* + * The TEST_EXTRA region is for special snowflakes, where a test wants + * to create and use a one-off memslot, without impacting "normal" test + * data allocations. + */ + MEM_REGION_TEST_EXTRA, NR_MEM_REGIONS, }; @@ -129,11 +163,6 @@ struct kvm_vm { struct kvm_binary_stats stats; - /* - * KVM region slots. These are the default memslots used by page - * allocators, e.g., lib/elf uses the memslots[MEM_REGION_CODE] - * memslot. - */ u32 memslots[NR_MEM_REGIONS]; }; diff --git a/tools/testing/selftests/kvm/include/x86/smm.h b/tools/testing/selftests/kvm/include/x86/smm.h index 2d1afa09819b..15faaa060126 100644 --- a/tools/testing/selftests/kvm/include/x86/smm.h +++ b/tools/testing/selftests/kvm/include/x86/smm.h @@ -8,7 +8,7 @@ #define SMRAM_MEMSLOT ((1 << 16) | 1) #define SMRAM_PAGES (SMRAM_SIZE / PAGE_SIZE) -void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, u64 smram_gpa, +void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, gpa_t smram_gpa, const void *smi_handler, size_t handler_size); void inject_smi(struct kvm_vcpu *vcpu); diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index ea5fa59888af..b988eea373ad 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -1468,11 +1468,12 @@ bool kvm_arch_has_default_irqchip(void) return true; } -void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, u64 smram_gpa, +void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, gpa_t smram_gpa, const void *smi_handler, size_t handler_size) { - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, smram_gpa, - SMRAM_MEMSLOT, SMRAM_PAGES, 0); + vm_override_mem_region(vm, MEM_REGION_TEST_EXTRA, VM_MEM_SRC_ANONYMOUS, + smram_gpa, SMRAM_MEMSLOT, SMRAM_PAGES); + TEST_ASSERT(vm_phy_pages_alloc(vm, SMRAM_PAGES, smram_gpa, SMRAM_MEMSLOT) == smram_gpa, "Could not allocate guest physical addresses for SMRAM"); From 5d81014d38684068f2cbe50c33d32896fa1df30d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:17 -0700 Subject: [PATCH 15/83] KVM: selftests: Use TEST_EXTRA region in arm64's vGIC LPI stress test Use the TEST_EXTRA region/memslot for arm64's vGIC LPI stress test instead of a custom memslot, so that the primary vm_phy_pages_alloc() APIs can be reworked to take a region type instead of a raw memslot. For all intents and purposes, no functional change intended, as vm_override_mem_region() is barely more than a wrapper for vm_userspace_mem_region_add(). Tested-by: Itaru Kitayama Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-15-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c index 9a69c439a0f8..9865b204fdeb 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c +++ b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c @@ -164,8 +164,8 @@ static void setup_memslot(void) pages = sz / vm->page_size; gpa_base = ((vm_compute_max_gfn(vm) + 1) * vm->page_size) - sz; - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, gpa_base, - TEST_MEMSLOT_INDEX, pages, 0); + vm_override_mem_region(vm, MEM_REGION_TEST_EXTRA, VM_MEM_SRC_ANONYMOUS, + gpa_base, TEST_MEMSLOT_INDEX, pages); } #define LPI_PROP_DEFAULT_PRIO 0xa0 From 00c37bfc5b4e81f3b1a1a95749a17f933803d324 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:18 -0700 Subject: [PATCH 16/83] KVM: selftests: Use TEST_EXTRA region in x86's smaller MAXPHYADDR test Use the TEST_EXTRA region/memslot for x86's smaller MAXPHYADDR test instead of a custom memslot, so that the primary vm_phy_pages_alloc() APIs can be reworked to take a region type instead of a raw memslot. For all intents and purposes, no functional change intended, as vm_add_extra_mem_region() is barely more than a wrapper for vm_userspace_mem_region_add(). Tested-by: Itaru Kitayama Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-16-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c index 3dca85e95478..b21798a385ca 100644 --- a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c @@ -62,9 +62,10 @@ int main(int argc, char *argv[]) TEST_ASSERT(rc, "KVM_CAP_EXIT_ON_EMULATION_FAILURE is unavailable"); vm_enable_cap(vm, KVM_CAP_EXIT_ON_EMULATION_FAILURE, 1); - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, - MEM_REGION_GPA, MEM_REGION_SLOT, - MEM_REGION_SIZE / PAGE_SIZE, 0); + vm_override_mem_region(vm, MEM_REGION_TEST_EXTRA, VM_MEM_SRC_ANONYMOUS, + MEM_REGION_GPA, MEM_REGION_SLOT, + MEM_REGION_SIZE / PAGE_SIZE); + gpa = vm_phy_pages_alloc(vm, MEM_REGION_SIZE / PAGE_SIZE, MEM_REGION_GPA, MEM_REGION_SLOT); TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc"); From 5ed16822bc4525974a48e580098cab5a8ce4d7e1 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:19 -0700 Subject: [PATCH 17/83] KVM: selftests: Use TEST_EXTRA region in set memory region test Use the TEST_EXTRA region/memslot in the "set memory region" test instead of a custom memslot, so that the primary vm_phy_pages_alloc() APIs can be reworked to take a region type instead of a raw memslot. For all intents and purposes, no functional change intended, as vm_override_mem_region() is barely more than a wrapper for vm_userspace_mem_region_add(). Tested-by: Itaru Kitayama Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-17-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/set_memory_region_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index dffe9654f45b..9d21594e2f81 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -116,9 +116,9 @@ static struct kvm_vm *spawn_vm(struct kvm_vcpu **vcpu, pthread_t *vcpu_thread, vm = vm_create_with_one_vcpu(vcpu, guest_code); - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS_THP, - MEM_REGION_GPA, MEM_REGION_SLOT, - MEM_REGION_SIZE / getpagesize(), 0); + vm_override_mem_region(vm, MEM_REGION_TEST_EXTRA, VM_MEM_SRC_ANONYMOUS_THP, + MEM_REGION_GPA, MEM_REGION_SLOT, + MEM_REGION_SIZE / getpagesize()); /* * Allocate and map two pages so that the GPA accessed by guest_code() From 36a062568ea80fd8714b1c4efa58d48a798c4b9f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:20 -0700 Subject: [PATCH 18/83] KVM: selftests: Take the memory region type, not memslot, in page allocators Take the memory region type instead of the memslot in all page allocator APIs, except for the innermost quad-underscores function. This will allow automatically selecting the minimum GPA based on the allocation type, which can't be done using the memslot due to all regions sharing a single memslot by default. PowerPC support will also leverage the type to ensure that page table allocations are naturally aligned. No functional change intended. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-18-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c | 12 ++++++------ tools/testing/selftests/kvm/include/kvm_util.h | 13 +++++++------ tools/testing/selftests/kvm/lib/kvm_util.c | 10 +++++++--- tools/testing/selftests/kvm/lib/x86/processor.c | 2 +- .../testing/selftests/kvm/set_memory_region_test.c | 2 +- .../kvm/x86/smaller_maxphyaddr_emulation_test.c | 2 +- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c index 9865b204fdeb..a45c0849a47a 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c +++ b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c @@ -192,27 +192,27 @@ static void setup_test_data(void) test_data.device_table = vm_phy_pages_alloc(vm, pages_per_64k, gpa_base, - TEST_MEMSLOT_INDEX); + MEM_REGION_TEST_EXTRA); test_data.collection_table = vm_phy_pages_alloc(vm, pages_per_64k, gpa_base, - TEST_MEMSLOT_INDEX); + MEM_REGION_TEST_EXTRA); cmdq_base = vm_phy_pages_alloc(vm, pages_per_64k, gpa_base, - TEST_MEMSLOT_INDEX); + MEM_REGION_TEST_EXTRA); virt_map(vm, cmdq_base, cmdq_base, pages_per_64k); test_data.cmdq_base = cmdq_base; test_data.cmdq_base_va = (void *)cmdq_base; test_data.itt_tables = vm_phy_pages_alloc(vm, pages_per_64k * nr_devices, - gpa_base, TEST_MEMSLOT_INDEX); + gpa_base, MEM_REGION_TEST_EXTRA); test_data.lpi_prop_table = vm_phy_pages_alloc(vm, pages_per_64k, - gpa_base, TEST_MEMSLOT_INDEX); + gpa_base, MEM_REGION_TEST_EXTRA); configure_lpis(); test_data.lpi_pend_tables = vm_phy_pages_alloc(vm, pages_per_64k * nr_cpus, - gpa_base, TEST_MEMSLOT_INDEX); + gpa_base, MEM_REGION_TEST_EXTRA); sync_global_to_guest(vm, test_data); } diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 9b808bcf7c18..92ab2f5001c9 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -1054,30 +1054,31 @@ const char *exit_reason_str(unsigned int exit_reason); gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot, bool protected, bool naturally_aligned); gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, - u32 memslot, bool protected); + enum kvm_mem_region_type type, bool protected); static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, - gpa_t min_gpa, u32 memslot) + gpa_t min_gpa, + enum kvm_mem_region_type type) { /* * By default, allocate memory as protected for VMs that support * protected memory, as the majority of memory for such VMs is * protected, i.e. using shared memory is effectively opt-in. */ - return __vm_phy_pages_alloc(vm, nr_pages, min_gpa, memslot, + return __vm_phy_pages_alloc(vm, nr_pages, min_gpa, type, vm_arch_has_protected_memory(vm)); } static inline gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, - u32 memslot) + enum kvm_mem_region_type type) { - return vm_phy_pages_alloc(vm, 1, min_gpa, memslot); + return vm_phy_pages_alloc(vm, 1, min_gpa, type); } static inline gpa_t vm_alloc_page_table_pages(struct kvm_vm *vm, size_t nr_pages) { return vm_phy_pages_alloc(vm, nr_pages, KVM_GUEST_PAGE_TABLE_MIN_PADDR, - vm->memslots[MEM_REGION_PT]); + MEM_REGION_PT); } static inline gpa_t vm_alloc_page_table(struct kvm_vm *vm) diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 00cdc5154ed4..aae881c8b927 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -1475,7 +1475,7 @@ static gva_t ____vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva, virt_pgd_alloc(vm); gpa_t gpa = __vm_phy_pages_alloc(vm, pages, KVM_UTIL_MIN_PFN * vm->page_size, - vm->memslots[type], protected); + type, protected); /* * Find an unused range of virtual page addresses of at least @@ -2088,9 +2088,13 @@ gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, } gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, - u32 memslot, bool protected) + enum kvm_mem_region_type type, bool protected) { - return ____vm_phy_pages_alloc(vm, nr_pages, min_gpa, memslot, protected, false); + TEST_ASSERT(type < NR_MEM_REGIONS, + "Invalid memory region type '%u'", type); + + return ____vm_phy_pages_alloc(vm, nr_pages, min_gpa, vm->memslots[type], + protected, false); } /* diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index b988eea373ad..39d9ca6ceb1d 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -1475,7 +1475,7 @@ void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, gpa_t smram_gpa, smram_gpa, SMRAM_MEMSLOT, SMRAM_PAGES); TEST_ASSERT(vm_phy_pages_alloc(vm, SMRAM_PAGES, smram_gpa, - SMRAM_MEMSLOT) == smram_gpa, + MEM_REGION_TEST_EXTRA) == smram_gpa, "Could not allocate guest physical addresses for SMRAM"); memset(addr_gpa2hva(vm, smram_gpa), 0x0, SMRAM_SIZE); diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index 9d21594e2f81..160bbe3d7203 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -124,7 +124,7 @@ static struct kvm_vm *spawn_vm(struct kvm_vcpu **vcpu, pthread_t *vcpu_thread, * Allocate and map two pages so that the GPA accessed by guest_code() * stays valid across the memslot move. */ - gpa = vm_phy_pages_alloc(vm, 2, MEM_REGION_GPA, MEM_REGION_SLOT); + gpa = vm_phy_pages_alloc(vm, 2, MEM_REGION_GPA, MEM_REGION_TEST_EXTRA); TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc\n"); virt_map(vm, MEM_REGION_GPA, MEM_REGION_GPA, 2); diff --git a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c index b21798a385ca..4e125eb5e0cf 100644 --- a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) MEM_REGION_SIZE / PAGE_SIZE); gpa = vm_phy_pages_alloc(vm, MEM_REGION_SIZE / PAGE_SIZE, - MEM_REGION_GPA, MEM_REGION_SLOT); + MEM_REGION_GPA, MEM_REGION_TEST_EXTRA); TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc"); virt_map(vm, MEM_REGION_GVA, MEM_REGION_GPA, 1); hva = addr_gpa2hva(vm, MEM_REGION_GPA); From 1856064afc9db977209d8e9150613e5cb1c3906c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:21 -0700 Subject: [PATCH 19/83] KVM: selftests: Use TEST_ASSERT(), not assert(), in vm_get_mem_region() Use a proper selftests TEST_ASSERT() in vm_get_mem_region() so that bad input is guaranteed to fail the test, and with the standard error message format. Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-19-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/kvm_util.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 92ab2f5001c9..de5ebd85b606 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -200,7 +200,8 @@ memslot2region(struct kvm_vm *vm, u32 memslot); static inline struct userspace_mem_region *vm_get_mem_region(struct kvm_vm *vm, enum kvm_mem_region_type type) { - assert(type < NR_MEM_REGIONS); + TEST_ASSERT(type < NR_MEM_REGIONS, + "Invalid memory region type '%u'", type); return memslot2region(vm, vm->memslots[type]); } From 6c28a0d7cf1254d0916fba5c387b4b56ed49e124 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:22 -0700 Subject: [PATCH 20/83] KVM: selftests: Automatically pick min_gpa for allocations based on region type Automatically choose the minimum GPA for physical page allocations based on the region type instead of sprinkling the logic over various wrappers and tests. All usage falls into three categories: (a) don't care, just use the bare minimum GPA, (b) page tables, use a slightly higher min to keep low memory available, (c) custom memslot, use the base of the memslot. I.e. there isn't a strong need to allow completely custom minimums. Opportunistically move the vGIC LPI stress test's "gpa_base" variable into setup_memslot() as the only reason it was a global variable was so that it could be passed as the minimum GPA for allocations from the extra memslot. Tested-by: Itaru Kitayama Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-20-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/arm64/vgic_lpi_stress.c | 13 +++---- .../testing/selftests/kvm/include/kvm_util.h | 12 +++--- tools/testing/selftests/kvm/lib/kvm_util.c | 38 ++++++++++++++++--- .../testing/selftests/kvm/lib/x86/processor.c | 3 +- .../selftests/kvm/set_memory_region_test.c | 2 +- .../x86/smaller_maxphyaddr_emulation_test.c | 2 +- 6 files changed, 45 insertions(+), 25 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c index a45c0849a47a..45a87ba5a737 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c +++ b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c @@ -23,7 +23,6 @@ #define GIC_LPI_OFFSET 8192 static size_t nr_iterations = 1000; -static gpa_t gpa_base; static struct kvm_vm *vm; static struct kvm_vcpu **vcpus; @@ -143,6 +142,7 @@ static void guest_code(size_t nr_lpis) static void setup_memslot(void) { + gpa_t gpa_base; size_t pages; size_t sz; @@ -191,28 +191,25 @@ static void setup_test_data(void) gpa_t cmdq_base; test_data.device_table = vm_phy_pages_alloc(vm, pages_per_64k, - gpa_base, MEM_REGION_TEST_EXTRA); test_data.collection_table = vm_phy_pages_alloc(vm, pages_per_64k, - gpa_base, MEM_REGION_TEST_EXTRA); - cmdq_base = vm_phy_pages_alloc(vm, pages_per_64k, gpa_base, - MEM_REGION_TEST_EXTRA); + cmdq_base = vm_phy_pages_alloc(vm, pages_per_64k, MEM_REGION_TEST_EXTRA); virt_map(vm, cmdq_base, cmdq_base, pages_per_64k); test_data.cmdq_base = cmdq_base; test_data.cmdq_base_va = (void *)cmdq_base; test_data.itt_tables = vm_phy_pages_alloc(vm, pages_per_64k * nr_devices, - gpa_base, MEM_REGION_TEST_EXTRA); + MEM_REGION_TEST_EXTRA); test_data.lpi_prop_table = vm_phy_pages_alloc(vm, pages_per_64k, - gpa_base, MEM_REGION_TEST_EXTRA); + MEM_REGION_TEST_EXTRA); configure_lpis(); test_data.lpi_pend_tables = vm_phy_pages_alloc(vm, pages_per_64k * nr_cpus, - gpa_base, MEM_REGION_TEST_EXTRA); + MEM_REGION_TEST_EXTRA); sync_global_to_guest(vm, test_data); } diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index de5ebd85b606..e77842311919 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -1054,11 +1054,10 @@ const char *exit_reason_str(unsigned int exit_reason); gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot, bool protected, bool naturally_aligned); -gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, +gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, enum kvm_mem_region_type type, bool protected); static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, - gpa_t min_gpa, enum kvm_mem_region_type type) { /* @@ -1066,20 +1065,19 @@ static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, * protected memory, as the majority of memory for such VMs is * protected, i.e. using shared memory is effectively opt-in. */ - return __vm_phy_pages_alloc(vm, nr_pages, min_gpa, type, + return __vm_phy_pages_alloc(vm, nr_pages, type, vm_arch_has_protected_memory(vm)); } -static inline gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, +static inline gpa_t vm_phy_page_alloc(struct kvm_vm *vm, enum kvm_mem_region_type type) { - return vm_phy_pages_alloc(vm, 1, min_gpa, type); + return vm_phy_pages_alloc(vm, 1, type); } static inline gpa_t vm_alloc_page_table_pages(struct kvm_vm *vm, size_t nr_pages) { - return vm_phy_pages_alloc(vm, nr_pages, KVM_GUEST_PAGE_TABLE_MIN_PADDR, - MEM_REGION_PT); + return vm_phy_pages_alloc(vm, nr_pages, MEM_REGION_PT); } static inline gpa_t vm_alloc_page_table(struct kvm_vm *vm) diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index aae881c8b927..ae17642ebf7f 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -1473,9 +1473,7 @@ static gva_t ____vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva, u64 pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0); virt_pgd_alloc(vm); - gpa_t gpa = __vm_phy_pages_alloc(vm, pages, - KVM_UTIL_MIN_PFN * vm->page_size, - type, protected); + gpa_t gpa = __vm_phy_pages_alloc(vm, pages, type, protected); /* * Find an unused range of virtual page addresses of at least @@ -2087,11 +2085,39 @@ gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, __builtin_unreachable(); } -gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, +gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, enum kvm_mem_region_type type, bool protected) { - TEST_ASSERT(type < NR_MEM_REGIONS, - "Invalid memory region type '%u'", type); + struct userspace_mem_region *region = vm_get_mem_region(vm, type); + gpa_t min_gpa; + + TEST_ASSERT(region, "No region for type '%u', memslot '%u'", + type, vm->memslots[type]); + + switch (type) { + case MEM_REGION_CODE: + case MEM_REGION_DATA: + case MEM_REGION_TEST_DATA: + /* + * If the region is backed by the default memslot (id=0), use + * selftests' hardcoded minimum PFN, otherwise use the base of + * the custom memory slot that backs the region. + */ + if (!vm->memslots[type]) + min_gpa = KVM_UTIL_MIN_PFN * vm->page_size; + else + min_gpa = region->region.guest_phys_addr; + break; + case MEM_REGION_PT: + min_gpa = KVM_GUEST_PAGE_TABLE_MIN_PADDR; + break; + case MEM_REGION_TEST_EXTRA: + min_gpa = region->region.guest_phys_addr; + break; + default: + TEST_FAIL("Invalid memory region type '%u'", type); + break; + } return ____vm_phy_pages_alloc(vm, nr_pages, min_gpa, vm->memslots[type], protected, false); diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index 39d9ca6ceb1d..479e9a481687 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -1474,8 +1474,7 @@ void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, gpa_t smram_gpa, vm_override_mem_region(vm, MEM_REGION_TEST_EXTRA, VM_MEM_SRC_ANONYMOUS, smram_gpa, SMRAM_MEMSLOT, SMRAM_PAGES); - TEST_ASSERT(vm_phy_pages_alloc(vm, SMRAM_PAGES, smram_gpa, - MEM_REGION_TEST_EXTRA) == smram_gpa, + TEST_ASSERT(vm_phy_pages_alloc(vm, SMRAM_PAGES, MEM_REGION_TEST_EXTRA) == smram_gpa, "Could not allocate guest physical addresses for SMRAM"); memset(addr_gpa2hva(vm, smram_gpa), 0x0, SMRAM_SIZE); diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index 160bbe3d7203..bfa5ac8ce029 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -124,7 +124,7 @@ static struct kvm_vm *spawn_vm(struct kvm_vcpu **vcpu, pthread_t *vcpu_thread, * Allocate and map two pages so that the GPA accessed by guest_code() * stays valid across the memslot move. */ - gpa = vm_phy_pages_alloc(vm, 2, MEM_REGION_GPA, MEM_REGION_TEST_EXTRA); + gpa = vm_phy_pages_alloc(vm, 2, MEM_REGION_TEST_EXTRA); TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc\n"); virt_map(vm, MEM_REGION_GPA, MEM_REGION_GPA, 2); diff --git a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c index 4e125eb5e0cf..8d1822717abd 100644 --- a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) MEM_REGION_SIZE / PAGE_SIZE); gpa = vm_phy_pages_alloc(vm, MEM_REGION_SIZE / PAGE_SIZE, - MEM_REGION_GPA, MEM_REGION_TEST_EXTRA); + MEM_REGION_TEST_EXTRA); TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc"); virt_map(vm, MEM_REGION_GVA, MEM_REGION_GPA, 1); hva = addr_gpa2hva(vm, MEM_REGION_GPA); From 1e59e5a3bdbed560bb2acec3a117f193a646332a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 09:41:23 -0700 Subject: [PATCH 21/83] KVM: selftests: Add arch hook to force page tables to be naturally aligned Add an arch hook to communicate to the core physical page allocator that page table allocations need to be naturally aligned. The hook will be used for upcoming PowerPC support, which has MMU tables that are larger than the guest base page size and need to be naturally aligned. Bury the hook in the physical page allocator to avoid having to plumb a rarely used param into the main APIs. Cc: Ritesh Harjani (IBM) Tested-by: Itaru Kitayama Reviewed-by: Ritesh Harjani (IBM) Tested-by: Itaru Kitayam Link: https://patch.msgid.link/20260902164123.2546762-21-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/kvm_util.h | 2 ++ tools/testing/selftests/kvm/lib/kvm_util.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index e77842311919..8fff5b40a592 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -1052,6 +1052,8 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); const char *exit_reason_str(unsigned int exit_reason); +bool kvm_arch_needs_naturally_aligned_page_tables(void); + gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, u32 memslot, bool protected, bool naturally_aligned); gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index ae17642ebf7f..cb2e7a98820a 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -2085,10 +2085,16 @@ gpa_t ____vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, gpa_t min_gpa, __builtin_unreachable(); } +__weak bool kvm_arch_needs_naturally_aligned_page_tables(void) +{ + return false; +} + gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, enum kvm_mem_region_type type, bool protected) { struct userspace_mem_region *region = vm_get_mem_region(vm, type); + bool naturally_aligned = false; gpa_t min_gpa; TEST_ASSERT(region, "No region for type '%u', memslot '%u'", @@ -2110,6 +2116,7 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, break; case MEM_REGION_PT: min_gpa = KVM_GUEST_PAGE_TABLE_MIN_PADDR; + naturally_aligned = kvm_arch_needs_naturally_aligned_page_tables(); break; case MEM_REGION_TEST_EXTRA: min_gpa = region->region.guest_phys_addr; @@ -2120,7 +2127,7 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t nr_pages, } return ____vm_phy_pages_alloc(vm, nr_pages, min_gpa, vm->memslots[type], - protected, false); + protected, naturally_aligned); } /* From 35097cefedafcb6e9d1c8a9434844c487092ac07 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 3 Sep 2026 17:24:07 -0700 Subject: [PATCH 22/83] KVM: nVMX: Make VMPTRLD result in #UD when eVMCS is used VMPTRLD with active eVMCS is already forbidden, however, returning 1 without skipping the instruction or queuing an exception will likely result in L1 getting stuck. Genuine Hyper-V seems to inject #UD under similar circumstances, mimic the behavior in KVM. Reported-by: f734222792@gmail.com Link: https://bugzilla.kernel.org/show_bug.cgi?id=221841 Suggested-by: Sean Christopherson Signed-off-by: Vitaly Kuznetsov Link: https://patch.msgid.link/20260904002423.3154436-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/nested.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 151873407abd..ad3726fd4fda 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -5897,6 +5897,12 @@ static int handle_vmptrld(struct kvm_vcpu *vcpu) if (!nested_vmx_check_permission(vcpu)) return 1; + /* Forbid normal VMPTRLD if Enlightened version was used */ + if (nested_vmx_is_evmptr12_valid(vmx)) { + kvm_queue_exception(vcpu, UD_VECTOR); + return 1; + } + if (nested_vmx_get_vmptr(vcpu, &vmptr, &r)) return r; @@ -5906,10 +5912,6 @@ static int handle_vmptrld(struct kvm_vcpu *vcpu) if (vmptr == vmx->nested.vmxon_ptr) return nested_vmx_fail(vcpu, VMXERR_VMPTRLD_VMXON_POINTER); - /* Forbid normal VMPTRLD if Enlightened version was used */ - if (nested_vmx_is_evmptr12_valid(vmx)) - return 1; - if (vmx->nested.current_vmptr != vmptr) { struct gfn_to_hva_cache *ghc = &vmx->nested.vmcs12_cache; struct vmcs_hdr hdr; From 5b1acaadcdcbedee1ead455639df06f11ce33e6a Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 3 Sep 2026 17:24:08 -0700 Subject: [PATCH 23/83] KVM: nVMX: Make VMPTRST return eVMCS GPA when it is used VMPTRST with active eVMCS is currently forbidden, however, returning 1 without skipping the instruction will likely result in L1 getting stuck. While TLFS does not specify the expected behavior, genuine Hyper-V seems to be returning eVMCS GPA. Implement the same behavior in KVM. Reported-by: f734222792@gmail.com Link: https://bugzilla.kernel.org/show_bug.cgi?id=221841 Signed-off-by: Vitaly Kuznetsov Link: https://patch.msgid.link/20260904002423.3154436-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/nested.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index ad3726fd4fda..3058efa65c82 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -5964,7 +5964,7 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu) { unsigned long exit_qual = vmx_get_exit_qual(vcpu); u32 instr_info = vmcs_read32(VMX_INSTRUCTION_INFO); - gpa_t current_vmptr = to_vmx(vcpu)->nested.current_vmptr; + gpa_t current_vmptr; struct x86_exception e; gva_t gva; int r; @@ -5972,8 +5972,16 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu) if (!nested_vmx_check_permission(vcpu)) return 1; - if (unlikely(nested_vmx_is_evmptr12_valid(to_vmx(vcpu)))) - return 1; + /* + * Hyper-V TLFS does not specify the behavior of VMPTRST when eVMCS is used + * but genuine Hyper-V seems to be returning eVMCS GPA. + */ +#ifdef CONFIG_KVM_HYPERV + if (nested_vmx_is_evmptr12_valid(to_vmx(vcpu))) + current_vmptr = to_vmx(vcpu)->nested.hv_evmcs_vmptr; + else +#endif + current_vmptr = to_vmx(vcpu)->nested.current_vmptr; if (get_vmx_mem_address(vcpu, exit_qual, instr_info, true, sizeof(gpa_t), &gva)) From be7875808fcdec9e76eaf611b8fd1f1cb5121939 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:09 -0700 Subject: [PATCH 24/83] KVM: selftests: Don't clobber RFLAGS in happy path of __KVM_ASM_SAFE() Use a straight MOV instead of an XOR to zero R9 in the non-faulting path of __KVM_ASM_SAFE() so as not to clobber RFLAGS. XOR generates smaller code, but optimizing performance by minimizing the code footprint is just about the last thing that KVM selftests care about. The bug is relatively benign in the current code base, e.g. it marginally degrades the coverage in the fastops test, but otherwise does no harm. But the bug will become actively problematic if/when the "safe assembly" macros are used for instructions that communicate results via RFLAGS, e.g. for VMX instructions, which signal failure via RFLAGS.CF and RFLAGS.ZF. Opportunistically use R9D instead of the full R9, as bits 63:32 of GPRs are zeroed on writes to bit 31:0. Reported-by: Sashiko Bot Closes: https://lore.kernel.org/all/20260818165258.2613603-5-vkuznets@redhat.com Cc: Vitaly Kuznetsov Link: https://patch.msgid.link/20260904002423.3154436-4-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 6e6f70035508..282b8aaff799 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1318,7 +1318,7 @@ gva_t vm_alloc_stack(struct kvm_vm *vm, int nr_pages); "lea 1f(%%rip), %%r10\n\t" \ "lea 2f(%%rip), %%r11\n\t" \ fep "1: " insn "\n\t" \ - "xor %%r9, %%r9\n\t" \ + "mov $0, %%r9d\n\t" \ "2:\n\t" \ "mov %%r9b, %[vector]\n\t" \ "mov %%r10, %[error_code]\n\t" From 455919e7694500a6cf8884b65e7460d362c7663d Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 3 Sep 2026 17:24:10 -0700 Subject: [PATCH 25/83] KVM: selftests: Adapt to the updated VMPTRST behavior when eVMCS is used Previously, VMPTRST was forbidden with eVMCS and selftests were mocking the correct behavior in vmptrst() by returning enlightened vmptr directly. Since KVM's behavior has changed to match genuine Hyper-V, adjust evmcs test accordingly. Signed-off-by: Vitaly Kuznetsov Link: https://patch.msgid.link/20260904002423.3154436-5-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/evmcs.h | 8 -------- tools/testing/selftests/kvm/include/x86/vmx.h | 3 --- tools/testing/selftests/kvm/x86/hyperv_evmcs.c | 5 ++--- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h index 82a8ea6b661f..c5b9d26f6cdb 100644 --- a/tools/testing/selftests/kvm/include/x86/evmcs.h +++ b/tools/testing/selftests/kvm/include/x86/evmcs.h @@ -265,14 +265,6 @@ static inline bool load_evmcs(struct hyperv_test_pages *hv) return true; } -static inline int evmcs_vmptrst(u64 *value) -{ - *value = current_vp_assist->current_nested_vmcs & - ~HV_X64_MSR_VP_ASSIST_PAGE_ENABLE; - - return 0; -} - static inline int evmcs_vmread(u64 encoding, u64 *value) { switch (encoding) { diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 04f5e34dea3a..ac7d48d1387e 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -358,9 +358,6 @@ static inline int vmptrst(u64 *value) u64 tmp; u8 ret; - if (enable_evmcs) - return evmcs_vmptrst(value); - __asm__ __volatile__("vmptrst %[value]; setna %[ret]" : [value]"=m"(tmp), [ret]"=rm"(ret) : : "cc", "memory"); diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 981a3a6d72fe..eecb06e120c2 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -92,15 +92,14 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); GUEST_SYNC(3); GUEST_ASSERT(load_evmcs(hv_pages)); - GUEST_ASSERT(vmptrstz() == hv_pages->enlightened_vmcs_gpa); + /* VMPTRST returns -1 until VMLAUNCH with eVMCS ptr set */ + GUEST_ASSERT(vmptrstz() == -1); GUEST_SYNC(4); - GUEST_ASSERT(vmptrstz() == hv_pages->enlightened_vmcs_gpa); prepare_vmcs(vmx_pages, l2_guest_code); GUEST_SYNC(5); - GUEST_ASSERT(vmptrstz() == hv_pages->enlightened_vmcs_gpa); current_evmcs->revision_id = -1u; GUEST_ASSERT(vmlaunch()); current_evmcs->revision_id = EVMCS_VERSION; From 0d6e35f6ab7d6c8b9afa6882447793aad58701e4 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Thu, 3 Sep 2026 17:24:11 -0700 Subject: [PATCH 26/83] KVM: selftests: Check VMPTRLD with active eVMCS Check that VMPTRLD when eVMCS is active results in #UD. This matches genuine Hyper-V's behavior. Use KVM_ASM_SAFE framework to handle #UD from VMPTRLD. Unfortunately, the same trick cannot be applied to the existing #UD check on VMLAUNCH as VMLAUNCH clobbers all registers which KVM_ASM_SAFE depends on. Keep VMLAUNCH handling separately. Deliberately use a double-underscores prefix instead of a "safe" postfix for the helper, to communicate that the inner/outer helpers handle more than just exceptions, they also handle VM-Fail conditions. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Vitaly Kuznetsov [sean: use __ instead of _safe] Link: https://patch.msgid.link/20260904002423.3154436-6-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 19 +++++++++++--- .../testing/selftests/kvm/x86/hyperv_evmcs.c | 26 +++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index ac7d48d1387e..a161dbee7e04 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -342,9 +342,6 @@ static inline int vmptrld(u64 vmcs_pa) { u8 ret; - if (enable_evmcs) - return -1; - __asm__ __volatile__ ("vmptrld %[pa]; setna %[ret]" : [ret]"=rm"(ret) : [pa]"m"(vmcs_pa) @@ -353,6 +350,22 @@ static inline int vmptrld(u64 vmcs_pa) return ret; } +static inline int __vmptrld(u64 vmcs_pa) +{ + u64 error_code; + u8 vector; + u8 failed; + + asm volatile(KVM_ASM_SAFE("vmptrld %[pa]") + "\n\tsetna %[failed]" + : KVM_ASM_SAFE_OUTPUTS(vector, error_code), + [failed]"=qm"(failed) + : [pa]"m"(vmcs_pa) + : "cc", "memory", KVM_ASM_SAFE_CLOBBERS); + + return vector ? vector : failed ? -EINVAL : 0; +} + static inline int vmptrst(u64 *value) { u64 tmp; diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index eecb06e120c2..9cb823e20a68 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -23,7 +23,11 @@ static int ud_count; static void guest_ud_handler(struct ex_regs *regs) { ud_count++; - regs->rip += 3; /* VMLAUNCH */ + /* + * VMLAUNCH insn can't be easily covered by KVM_ASM_SAFE framework but + * luckily the instruction is always three bytes. + */ + regs->rip += 3; } static void guest_nmi_handler(struct ex_regs *regs) @@ -178,7 +182,15 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_SYNC(11); - /* Try enlightened vmptrld with an incorrect GPA */ + /* VMPTRLD instruction causes #UD after enlightened VMLAUNCH */ + GUEST_ASSERT(__vmptrld(hv_pages->enlightened_vmcs_gpa) == UD_VECTOR); + + /* + * Try enlightened vmptrld with an incorrect GPA. GUEST_SYNC(12) signals + * the host to enable guest_ud_handler() which cannot be enabled beforehand + * to not override the default fixup handler from KVM_ASM_SAFE(). + */ + GUEST_SYNC(12); evmcs_vmptrld(0xdeadbeef, hv_pages->enlightened_vmcs); GUEST_ASSERT(vmlaunch()); GUEST_ASSERT(ud_count == 1); @@ -252,7 +264,6 @@ int main(int argc, char *argv[]) vcpu_args_set(vcpu, 3, vmx_pages_gva, hv_pages_gva, addr_gva2gpa(vm, hcall_page)); vcpu_set_msr(vcpu, HV_X64_MSR_VP_INDEX, vcpu->id); - vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler); pr_info("Running L1 which uses EVMCS to run L2\n"); @@ -282,7 +293,7 @@ int main(int argc, char *argv[]) /* Force immediate L2->L1 exit before resuming */ if (stage == 8) { - pr_info("Injecting NMI into L1 before L2 had a chance to run after restore\n"); + pr_debug("Injecting NMI into L1 before L2 had a chance to run after restore\n"); inject_nmi(vcpu); } @@ -292,9 +303,14 @@ int main(int argc, char *argv[]) * KVM_STATE_NESTED_EVMCS is not lost. */ if (stage == 9) { - pr_info("Trying extra KVM_GET_NESTED_STATE/KVM_SET_NESTED_STATE cycle\n"); + pr_debug("Trying extra KVM_GET_NESTED_STATE/KVM_SET_NESTED_STATE cycle\n"); vcpu = save_restore_vm(vm, vcpu); } + + if (stage == 12) { + pr_debug("Trying enlightened VMLAUNCH with an invalid PTR\n"); + vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); + } } done: From f1d4e357a69559e47e7f34fe8dc9d156d0de7588 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:12 -0700 Subject: [PATCH 27/83] KVM: selftests: Assert success in vmptrst(), kill off vmptrstz() Assert success in vmptrst() instead of punting to the caller, as literally every user expects VMPTRST to succeed (and because '0' is a valid physical address). Link: https://patch.msgid.link/20260904002423.3154436-7-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 19 ++++--------------- .../testing/selftests/kvm/x86/hyperv_evmcs.c | 4 ++-- tools/testing/selftests/kvm/x86/state_test.c | 8 ++++---- .../kvm/x86/vmx_preemption_timer_test.c | 2 +- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index a161dbee7e04..04f638fcd3ba 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -366,27 +366,16 @@ static inline int __vmptrld(u64 vmcs_pa) return vector ? vector : failed ? -EINVAL : 0; } -static inline int vmptrst(u64 *value) +static inline u64 vmptrst(void) { - u64 tmp; + u64 value = 0; u8 ret; __asm__ __volatile__("vmptrst %[value]; setna %[ret]" - : [value]"=m"(tmp), [ret]"=rm"(ret) + : [value]"=m"(value), [ret]"=rm"(ret) : : "cc", "memory"); - *value = tmp; - return ret; -} - -/* - * A wrapper around vmptrst that ignores errors and returns zero if the - * vmptrst instruction fails. - */ -static inline u64 vmptrstz(void) -{ - u64 value = 0; - vmptrst(&value); + __GUEST_ASSERT(!ret, "vmptrst failed"); return value; } diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 9cb823e20a68..7923229c2dfc 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -97,7 +97,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, GUEST_SYNC(3); GUEST_ASSERT(load_evmcs(hv_pages)); /* VMPTRST returns -1 until VMLAUNCH with eVMCS ptr set */ - GUEST_ASSERT(vmptrstz() == -1); + GUEST_ASSERT(vmptrst() == -1); GUEST_SYNC(4); @@ -123,7 +123,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, GUEST_ASSERT(!vmlaunch()); GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); GUEST_ASSERT_EQ((vmreadz(VM_EXIT_INTR_INFO) & 0xff), NMI_VECTOR); - GUEST_ASSERT(vmptrstz() == hv_pages->enlightened_vmcs_gpa); + GUEST_ASSERT(vmptrst() == hv_pages->enlightened_vmcs_gpa); /* * NMI forces L2->L1 exit, resuming L2 and hope that EVMCS is diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index e6f521a6e737..354ff3cda597 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -78,17 +78,17 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); GUEST_SYNC(3); GUEST_ASSERT(load_vmcs(vmx_pages)); - GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); + GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); GUEST_SYNC(4); - GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); + GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); prepare_vmcs(vmx_pages, vmx_l2_guest_code); GUEST_SYNC(5); - GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); + GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); GUEST_ASSERT(!vmlaunch()); - GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); + GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); /* Check that the launched state is preserved. */ diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index 72fbf1b70e9f..3f77afeaad1b 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -73,7 +73,7 @@ void l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(vmx_pages->vmcs_gpa); GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); GUEST_ASSERT(load_vmcs(vmx_pages)); - GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); + GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); prepare_vmcs(vmx_pages, l2_guest_code); From 154b098cada9d673058c3911cb1a3864de1ae05b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:13 -0700 Subject: [PATCH 28/83] KVM: selftests: Always assert that vmxon() and prepare_for_vmx_operation() succeed Assert that vmxon() succeeds, and by extension prepare_for_vmx_operation(). Forcing every caller to assert that a core preparation helper succeeds is absurd and generates hard-to-read code. For all intents and purposes, no functional change intended. Link: https://patch.msgid.link/20260904002423.3154436-8-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 6 +++--- tools/testing/selftests/kvm/lib/x86/memstress.c | 2 +- tools/testing/selftests/kvm/lib/x86/vmx.c | 7 ++----- tools/testing/selftests/kvm/x86/aperfmperf_test.c | 2 +- tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c | 2 +- tools/testing/selftests/kvm/x86/hyperv_evmcs.c | 2 +- tools/testing/selftests/kvm/x86/kvm_buslock_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_close_kvm_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_dirty_log_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_emulation_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_exceptions_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 2 +- .../selftests/kvm/x86/save_restore_pf_stress_test.c | 2 +- tools/testing/selftests/kvm/x86/smm_test.c | 2 +- tools/testing/selftests/kvm/x86/state_test.c | 2 +- tools/testing/selftests/kvm/x86/triple_fault_event_test.c | 2 +- tools/testing/selftests/kvm/x86/vmx_apic_access_test.c | 2 +- tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c | 2 +- .../selftests/kvm/x86/vmx_invalid_nested_guest_state.c | 2 +- .../testing/selftests/kvm/x86/vmx_nested_la57_state_test.c | 2 +- .../testing/selftests/kvm/x86/vmx_preemption_timer_test.c | 2 +- 24 files changed, 27 insertions(+), 30 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 04f638fcd3ba..f7ca6646dfbe 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -309,7 +309,7 @@ struct vmx_msr_entry { #include "evmcs.h" -static inline int vmxon(u64 phys) +static inline void vmxon(u64 phys) { u8 ret; @@ -318,7 +318,7 @@ static inline int vmxon(u64 phys) : [pa]"m"(phys) : "cc", "memory"); - return ret; + __GUEST_ASSERT(!ret, "vmxon [0x%lx] failed", phys); } static inline void vmxoff(void) @@ -549,7 +549,7 @@ union vmx_ctrl_msr { }; struct vmx_pages *vcpu_alloc_vmx(struct kvm_vm *vm, gva_t *p_vmx_gva); -bool prepare_for_vmx_operation(struct vmx_pages *vmx); +void prepare_for_vmx_operation(struct vmx_pages *vmx); void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip); bool load_vmcs(struct vmx_pages *vmx); diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testing/selftests/kvm/lib/x86/memstress.c index e19e8b5a09c5..2fa39470c1b3 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -33,7 +33,7 @@ __asm__( static void l1_vmx_code(struct vmx_pages *vmx, u64 vcpu_id) { GUEST_ASSERT(vmx->vmcs_gpa); - GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + prepare_for_vmx_operation(vmx); GUEST_ASSERT(load_vmcs(vmx)); GUEST_ASSERT(ept_1g_pages_supported()); diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 089e1a8af53f..03d61b1159f8 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -125,7 +125,7 @@ vcpu_alloc_vmx(struct kvm_vm *vm, gva_t *p_vmx_gva) return vmx; } -bool prepare_for_vmx_operation(struct vmx_pages *vmx) +void prepare_for_vmx_operation(struct vmx_pages *vmx) { u64 feature_control; u64 required; @@ -163,10 +163,7 @@ bool prepare_for_vmx_operation(struct vmx_pages *vmx) /* Enter VMX root operation. */ *(u32 *)(vmx->vmxon) = vmcs_revision(); - if (vmxon(vmx->vmxon_gpa)) - return false; - - return true; + vmxon(vmx->vmxon_gpa); } bool load_vmcs(struct vmx_pages *vmx) diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/testing/selftests/kvm/x86/aperfmperf_test.c index 845cb685f174..e32131cd6ed1 100644 --- a/tools/testing/selftests/kvm/x86/aperfmperf_test.c +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -70,7 +70,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void l1_vmx_code(struct vmx_pages *vmx) { - GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); + prepare_for_vmx_operation(vmx); GUEST_ASSERT_EQ(load_vmcs(vmx), true); prepare_vmcs(vmx, NULL); diff --git a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c index aa7f3b405fd3..cafb58a37023 100644 --- a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c +++ b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c @@ -55,7 +55,7 @@ static void guest_code(struct vmx_pages *vmx_pages, enable_vp_assist(hv_pages->vp_assist_gpa, hv_pages->vp_assist); evmcs_enable(); - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_evmcs(hv_pages)); prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 7923229c2dfc..849d66c8470d 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -93,7 +93,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, enable_vp_assist(hv_pages->vp_assist_gpa, hv_pages->vp_assist); evmcs_enable(); - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_SYNC(3); GUEST_ASSERT(load_evmcs(hv_pages)); /* VMPTRST returns -1 until VMLAUNCH with eVMCS ptr set */ diff --git a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c index 25a182be00a9..2e006ce95896 100644 --- a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c +++ b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c @@ -42,7 +42,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void l1_vmx_code(struct vmx_pages *vmx) { - GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); + prepare_for_vmx_operation(vmx); GUEST_ASSERT_EQ(load_vmcs(vmx), true); prepare_vmcs(vmx, NULL); diff --git a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c index b974cfb347d6..479e9adcc20b 100644 --- a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c +++ b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c @@ -30,7 +30,7 @@ static void l2_guest_code(void) static void l1_vmx_code(struct vmx_pages *vmx_pages) { - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); /* Prepare the VMCS for L2 execution. */ diff --git a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c index 26b474bf1353..57f49471f4aa 100644 --- a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c @@ -93,7 +93,7 @@ void l1_vmx_code(struct vmx_pages *vmx) void *l2_rip; GUEST_ASSERT(vmx->vmcs_gpa); - GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + prepare_for_vmx_operation(vmx); GUEST_ASSERT(load_vmcs(vmx)); if (vmx->eptp_gpa) diff --git a/tools/testing/selftests/kvm/x86/nested_emulation_test.c b/tools/testing/selftests/kvm/x86/nested_emulation_test.c index e08c6b0697e5..6f27930bb6b5 100644 --- a/tools/testing/selftests/kvm/x86/nested_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/nested_emulation_test.c @@ -66,7 +66,7 @@ static void guest_code(void *test_data) BIT_ULL(INTERCEPT_HLT); vmcb->control.intercept_exceptions = 0; } else { - GUEST_ASSERT(prepare_for_vmx_operation(test_data)); + prepare_for_vmx_operation(test_data); GUEST_ASSERT(load_vmcs(test_data)); prepare_vmcs(test_data, NULL); diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index aeec3121c8e8..6a36cf4d1eab 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -125,7 +125,7 @@ static void vmx_run_l2(void *l2_code, int vector, u32 error_code) static void l1_vmx_code(struct vmx_pages *vmx) { - GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); + prepare_for_vmx_operation(vmx); GUEST_ASSERT_EQ(load_vmcs(vmx), true); diff --git a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c index 8c2ba9674558..749a93c318fe 100644 --- a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c +++ b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c @@ -40,7 +40,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) { uintptr_t save_cr3; - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c index 2e04563790ff..b143ebe710e7 100644 --- a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c @@ -55,7 +55,7 @@ static void l1_vmx_code(struct vmx_pages *vmx, u64 expected_fault_gpa, u64 exit_qual; GUEST_ASSERT(vmx->vmcs_gpa); - GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + prepare_for_vmx_operation(vmx); GUEST_ASSERT(load_vmcs(vmx)); prepare_vmcs(vmx, l2_entry); diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c index cb79d7b9619c..3d2eae848ba6 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c @@ -86,7 +86,7 @@ static void l1_guest_code(void *data) struct vmx_pages *vmx_pages = data; u32 control; - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 18f765835bf4..584071146b69 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -105,7 +105,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) /* check that L1's frequency looks alright before launching L2 */ check_tsc_freq(UCHECK_L1); - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); /* prepare the VMCS for L2 execution */ diff --git a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c index 507391ab2c93..6ab73a0bd34a 100644 --- a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c +++ b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c @@ -98,7 +98,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void l1_vmx_code(struct vmx_pages *vmx) { - GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + prepare_for_vmx_operation(vmx); GUEST_ASSERT(load_vmcs(vmx)); prepare_vmcs(vmx, guest_access_memory); diff --git a/tools/testing/selftests/kvm/x86/smm_test.c b/tools/testing/selftests/kvm/x86/smm_test.c index d1edafd5af75..be5662792ec8 100644 --- a/tools/testing/selftests/kvm/x86/smm_test.c +++ b/tools/testing/selftests/kvm/x86/smm_test.c @@ -78,7 +78,7 @@ static void guest_code(void *arg) if (this_cpu_has(X86_FEATURE_SVM)) { generic_svm_setup(svm, l2_guest_code); } else { - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); prepare_vmcs(vmx_pages, l2_guest_code); } diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index 354ff3cda597..d29a3e113460 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -75,7 +75,7 @@ void vmx_l2_guest_code(void) static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) { GUEST_ASSERT(vmx_pages->vmcs_gpa); - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_SYNC(3); GUEST_ASSERT(load_vmcs(vmx_pages)); GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); diff --git a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c index 0d83516f4bd0..57e79922b16b 100644 --- a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c +++ b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c @@ -25,7 +25,7 @@ void l1_guest_code_vmx(struct vmx_pages *vmx) { GUEST_ASSERT(vmx->vmcs_gpa); - GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + prepare_for_vmx_operation(vmx); GUEST_ASSERT(load_vmcs(vmx)); prepare_vmcs(vmx, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c index 463f73aa9159..7df9ab833cb2 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c @@ -38,7 +38,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa) { u32 control; - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); /* Prepare the VMCS for L2 execution. */ diff --git a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c index f9b88a6f6113..8e4dc304533a 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c @@ -33,7 +33,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) { u32 control; - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); /* Prepare the VMCS for L2 execution. */ diff --git a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c index c8379124b317..8999f0458aa1 100644 --- a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c +++ b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c @@ -41,7 +41,7 @@ static void l2_guest_code(void) static void l1_guest_code(struct vmx_pages *vmx_pages) { - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); /* Prepare the VMCS for L2 execution. */ diff --git a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c index 448721b54181..ab9f38daef17 100644 --- a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c @@ -37,7 +37,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(rdmsr(MSR_GS_BASE) == LA57_GS_BASE); GUEST_ASSERT(vmx_pages->vmcs_gpa); - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index 3f77afeaad1b..9d2b2ce11a65 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -71,7 +71,7 @@ void l1_guest_code(struct vmx_pages *vmx_pages) u64 l1_tsc_deadline, l2_tsc_deadline; GUEST_ASSERT(vmx_pages->vmcs_gpa); - GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + prepare_for_vmx_operation(vmx_pages); GUEST_ASSERT(load_vmcs(vmx_pages)); GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); From 5f73777a27b16f5778a9da12fc82f60d9e54a0f9 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:14 -0700 Subject: [PATCH 29/83] KVM: selftests: Always assert that vmclear() succeeds Continue removing dead error "handling", and assert that VMCLEAR succeeds instead of forcing callers of load_vmcs() to assert success. For all intents and purposes, no functional change intended. Link: https://patch.msgid.link/20260904002423.3154436-9-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 4 ++-- tools/testing/selftests/kvm/lib/x86/vmx.c | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index f7ca6646dfbe..cf37a03a93b9 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -326,7 +326,7 @@ static inline void vmxoff(void) __asm__ __volatile__("vmxoff"); } -static inline int vmclear(u64 vmcs_pa) +static inline void vmclear(u64 vmcs_pa) { u8 ret; @@ -335,7 +335,7 @@ static inline int vmclear(u64 vmcs_pa) : [pa]"m"(vmcs_pa) : "cc", "memory"); - return ret; + __GUEST_ASSERT(!ret, "vmclear [0x%lx] failed\n", vmcs_pa); } static inline int vmptrld(u64 vmcs_pa) diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 03d61b1159f8..57a8287de0d2 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -170,16 +170,14 @@ bool load_vmcs(struct vmx_pages *vmx) { /* Load a VMCS. */ *(u32 *)(vmx->vmcs) = vmcs_revision(); - if (vmclear(vmx->vmcs_gpa)) - return false; + vmclear(vmx->vmcs_gpa); if (vmptrld(vmx->vmcs_gpa)) return false; /* Setup shadow VMCS, do not load it yet. */ *(u32 *)(vmx->shadow_vmcs) = vmcs_revision() | 0x80000000ul; - if (vmclear(vmx->shadow_vmcs_gpa)) - return false; + vmclear(vmx->shadow_vmcs_gpa); return true; } From 9007b746aeb5073d03c722990080e4a8ec691478 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:15 -0700 Subject: [PATCH 30/83] KVM: selftests: Always assert that vmptrld() succeeds Assert that vmptrld() succeeds instead of forcing all callers to do the same, and opportunistically provide a macro to print (hopefully) useful information on VMX instruction failures. Leave load_vmcs() as-is for the moment to minimize the amount of churn since the vmptrld() changes aren't completely trivial. Link: https://patch.msgid.link/20260904002423.3154436-10-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 23 +++++++++---------- tools/testing/selftests/kvm/lib/x86/vmx.c | 3 +-- tools/testing/selftests/kvm/x86/state_test.c | 6 ++--- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index cf37a03a93b9..8f004b946826 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -309,6 +309,10 @@ struct vmx_msr_entry { #include "evmcs.h" +#define GUEST_ASSERT_VMX_INSN_SUCCEEDED(insn, __r, __pa) \ + __GUEST_ASSERT(!__r, __stringify(insn) "[0x%lx] hit %s", \ + __pa, __r < 0 ? "VM-Fail" : ex_str(__r)) + static inline void vmxon(u64 phys) { u8 ret; @@ -338,18 +342,6 @@ static inline void vmclear(u64 vmcs_pa) __GUEST_ASSERT(!ret, "vmclear [0x%lx] failed\n", vmcs_pa); } -static inline int vmptrld(u64 vmcs_pa) -{ - u8 ret; - - __asm__ __volatile__ ("vmptrld %[pa]; setna %[ret]" - : [ret]"=rm"(ret) - : [pa]"m"(vmcs_pa) - : "cc", "memory"); - - return ret; -} - static inline int __vmptrld(u64 vmcs_pa) { u64 error_code; @@ -366,6 +358,13 @@ static inline int __vmptrld(u64 vmcs_pa) return vector ? vector : failed ? -EINVAL : 0; } +static inline void vmptrld(u64 vmcs_pa) +{ + int ret = __vmptrld(vmcs_pa); + + GUEST_ASSERT_VMX_INSN_SUCCEEDED(vmptrld, ret, vmcs_pa); +} + static inline u64 vmptrst(void) { u64 value = 0; diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 57a8287de0d2..e65024b9ea0d 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -172,8 +172,7 @@ bool load_vmcs(struct vmx_pages *vmx) *(u32 *)(vmx->vmcs) = vmcs_revision(); vmclear(vmx->vmcs_gpa); - if (vmptrld(vmx->vmcs_gpa)) - return false; + vmptrld(vmx->vmcs_gpa); /* Setup shadow VMCS, do not load it yet. */ *(u32 *)(vmx->shadow_vmcs) = vmcs_revision() | 0x80000000ul; diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index d29a3e113460..2d5fc603929b 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -108,7 +108,7 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) vmwrite(SECONDARY_VM_EXEC_CONTROL, SECONDARY_EXEC_SHADOW_VMCS); vmwrite(VMCS_LINK_POINTER, vmx_pages->shadow_vmcs_gpa); - GUEST_ASSERT(!vmptrld(vmx_pages->shadow_vmcs_gpa)); + vmptrld(vmx_pages->shadow_vmcs_gpa); GUEST_ASSERT(vmlaunch()); GUEST_SYNC(8); GUEST_ASSERT(vmlaunch()); @@ -118,11 +118,11 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) GUEST_SYNC(9); GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); - GUEST_ASSERT(!vmptrld(vmx_pages->vmcs_gpa)); + vmptrld(vmx_pages->vmcs_gpa); GUEST_ASSERT(!vmresume()); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); - GUEST_ASSERT(!vmptrld(vmx_pages->shadow_vmcs_gpa)); + vmptrld(vmx_pages->shadow_vmcs_gpa); GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffffee); GUEST_ASSERT(vmlaunch()); GUEST_ASSERT(vmresume()); From f2bad303f4033e9cb516ce456c98cd8b2495c24b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:16 -0700 Subject: [PATCH 31/83] KVM: selftests: Drop useless return code from load_vmcs() Now that load_vmcs() either asserts or returns success, drop the return code and the many asserts in its callers. Link: https://patch.msgid.link/20260904002423.3154436-11-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 2 +- tools/testing/selftests/kvm/lib/x86/memstress.c | 2 +- tools/testing/selftests/kvm/lib/x86/vmx.c | 4 +--- tools/testing/selftests/kvm/x86/aperfmperf_test.c | 2 +- tools/testing/selftests/kvm/x86/kvm_buslock_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_close_kvm_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_dirty_log_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_emulation_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_exceptions_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c | 2 +- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 2 +- tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c | 2 +- tools/testing/selftests/kvm/x86/smm_test.c | 2 +- tools/testing/selftests/kvm/x86/state_test.c | 2 +- tools/testing/selftests/kvm/x86/triple_fault_event_test.c | 2 +- tools/testing/selftests/kvm/x86/vmx_apic_access_test.c | 2 +- tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c | 2 +- .../selftests/kvm/x86/vmx_invalid_nested_guest_state.c | 2 +- tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c | 2 +- tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c | 2 +- 22 files changed, 22 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 8f004b946826..5c46ae95c4c3 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -550,7 +550,7 @@ union vmx_ctrl_msr { struct vmx_pages *vcpu_alloc_vmx(struct kvm_vm *vm, gva_t *p_vmx_gva); void prepare_for_vmx_operation(struct vmx_pages *vmx); void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip); -bool load_vmcs(struct vmx_pages *vmx); +void load_vmcs(struct vmx_pages *vmx); bool ept_1g_pages_supported(void); diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testing/selftests/kvm/lib/x86/memstress.c index 2fa39470c1b3..207350ab6114 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -34,7 +34,7 @@ static void l1_vmx_code(struct vmx_pages *vmx, u64 vcpu_id) { GUEST_ASSERT(vmx->vmcs_gpa); prepare_for_vmx_operation(vmx); - GUEST_ASSERT(load_vmcs(vmx)); + load_vmcs(vmx); GUEST_ASSERT(ept_1g_pages_supported()); *(u64 *)vmx->stack = vcpu_id; diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index e65024b9ea0d..5c6cadc08405 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -166,7 +166,7 @@ void prepare_for_vmx_operation(struct vmx_pages *vmx) vmxon(vmx->vmxon_gpa); } -bool load_vmcs(struct vmx_pages *vmx) +void load_vmcs(struct vmx_pages *vmx) { /* Load a VMCS. */ *(u32 *)(vmx->vmcs) = vmcs_revision(); @@ -177,8 +177,6 @@ bool load_vmcs(struct vmx_pages *vmx) /* Setup shadow VMCS, do not load it yet. */ *(u32 *)(vmx->shadow_vmcs) = vmcs_revision() | 0x80000000ul; vmclear(vmx->shadow_vmcs_gpa); - - return true; } static bool ept_vpid_cap_supported(u64 mask) diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/testing/selftests/kvm/x86/aperfmperf_test.c index e32131cd6ed1..11f5894d8ac7 100644 --- a/tools/testing/selftests/kvm/x86/aperfmperf_test.c +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -71,7 +71,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void l1_vmx_code(struct vmx_pages *vmx) { prepare_for_vmx_operation(vmx); - GUEST_ASSERT_EQ(load_vmcs(vmx), true); + load_vmcs(vmx); prepare_vmcs(vmx, NULL); diff --git a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c index 2e006ce95896..22993ec697fa 100644 --- a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c +++ b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c @@ -43,7 +43,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void l1_vmx_code(struct vmx_pages *vmx) { prepare_for_vmx_operation(vmx); - GUEST_ASSERT_EQ(load_vmcs(vmx), true); + load_vmcs(vmx); prepare_vmcs(vmx, NULL); diff --git a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c index 479e9adcc20b..309cab009929 100644 --- a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c +++ b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c @@ -31,7 +31,7 @@ static void l2_guest_code(void) static void l1_vmx_code(struct vmx_pages *vmx_pages) { prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); /* Prepare the VMCS for L2 execution. */ prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c index 57f49471f4aa..dca2bfc8b158 100644 --- a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c @@ -94,7 +94,7 @@ void l1_vmx_code(struct vmx_pages *vmx) GUEST_ASSERT(vmx->vmcs_gpa); prepare_for_vmx_operation(vmx); - GUEST_ASSERT(load_vmcs(vmx)); + load_vmcs(vmx); if (vmx->eptp_gpa) l2_rip = l2_guest_code_tdp_enabled; diff --git a/tools/testing/selftests/kvm/x86/nested_emulation_test.c b/tools/testing/selftests/kvm/x86/nested_emulation_test.c index 6f27930bb6b5..2b38eff4f516 100644 --- a/tools/testing/selftests/kvm/x86/nested_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/nested_emulation_test.c @@ -67,7 +67,7 @@ static void guest_code(void *test_data) vmcb->control.intercept_exceptions = 0; } else { prepare_for_vmx_operation(test_data); - GUEST_ASSERT(load_vmcs(test_data)); + load_vmcs(test_data); prepare_vmcs(test_data, NULL); GUEST_ASSERT(!vmwrite(GUEST_IDTR_LIMIT, 0)); diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index 6a36cf4d1eab..50c271a03692 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -127,7 +127,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) { prepare_for_vmx_operation(vmx); - GUEST_ASSERT_EQ(load_vmcs(vmx), true); + load_vmcs(vmx); prepare_vmcs(vmx, NULL); GUEST_ASSERT_EQ(vmwrite(GUEST_IDTR_LIMIT, 0), 0); diff --git a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c index 749a93c318fe..43deb3b358ea 100644 --- a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c +++ b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c @@ -41,7 +41,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) uintptr_t save_cr3; prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c index b143ebe710e7..52df467e9c44 100644 --- a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c @@ -56,7 +56,7 @@ static void l1_vmx_code(struct vmx_pages *vmx, u64 expected_fault_gpa, GUEST_ASSERT(vmx->vmcs_gpa); prepare_for_vmx_operation(vmx); - GUEST_ASSERT(load_vmcs(vmx)); + load_vmcs(vmx); prepare_vmcs(vmx, l2_entry); diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c index 3d2eae848ba6..05194762ed33 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c @@ -87,7 +87,7 @@ static void l1_guest_code(void *data) u32 control; prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); prepare_vmcs(vmx_pages, l2_guest_code); control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 584071146b69..b9fae268a9ec 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -106,7 +106,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) check_tsc_freq(UCHECK_L1); prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); /* prepare the VMCS for L2 execution */ prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c index 6ab73a0bd34a..c0e4d6e156ad 100644 --- a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c +++ b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c @@ -99,7 +99,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void l1_vmx_code(struct vmx_pages *vmx) { prepare_for_vmx_operation(vmx); - GUEST_ASSERT(load_vmcs(vmx)); + load_vmcs(vmx); prepare_vmcs(vmx, guest_access_memory); GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR))); diff --git a/tools/testing/selftests/kvm/x86/smm_test.c b/tools/testing/selftests/kvm/x86/smm_test.c index be5662792ec8..377df09e582a 100644 --- a/tools/testing/selftests/kvm/x86/smm_test.c +++ b/tools/testing/selftests/kvm/x86/smm_test.c @@ -79,7 +79,7 @@ static void guest_code(void *arg) generic_svm_setup(svm, l2_guest_code); } else { prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); prepare_vmcs(vmx_pages, l2_guest_code); } diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index 2d5fc603929b..6880db3442e5 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -77,7 +77,7 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(vmx_pages->vmcs_gpa); prepare_for_vmx_operation(vmx_pages); GUEST_SYNC(3); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); GUEST_SYNC(4); diff --git a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c index 57e79922b16b..a9fb7046ea4f 100644 --- a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c +++ b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c @@ -26,7 +26,7 @@ void l1_guest_code_vmx(struct vmx_pages *vmx) GUEST_ASSERT(vmx->vmcs_gpa); prepare_for_vmx_operation(vmx); - GUEST_ASSERT(load_vmcs(vmx)); + load_vmcs(vmx); prepare_vmcs(vmx, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c index 7df9ab833cb2..1793f7b02219 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c @@ -39,7 +39,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa) u32 control; prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); /* Prepare the VMCS for L2 execution. */ prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c index 8e4dc304533a..f7ebc9b15629 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c @@ -34,7 +34,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) u32 control; prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); /* Prepare the VMCS for L2 execution. */ prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c index 8999f0458aa1..4a6445958cf4 100644 --- a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c +++ b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c @@ -42,7 +42,7 @@ static void l2_guest_code(void) static void l1_guest_code(struct vmx_pages *vmx_pages) { prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); /* Prepare the VMCS for L2 execution. */ prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c index ab9f38daef17..608bc0ed27db 100644 --- a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c @@ -38,7 +38,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(vmx_pages->vmcs_gpa); prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); prepare_vmcs(vmx_pages, l2_guest_code); diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index 9d2b2ce11a65..abbe5aedd35f 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -72,7 +72,7 @@ void l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(vmx_pages->vmcs_gpa); prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_vmcs(vmx_pages)); + load_vmcs(vmx_pages); GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); prepare_vmcs(vmx_pages, l2_guest_code); From b2f44e713c6d1c27bac17e376490e6ed9c334191 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:17 -0700 Subject: [PATCH 32/83] KVM: selftests: Add macros to handle simple VMX instructions Add macros to build helpers for VMXON, VMPTRLD, and VMCLEAR, i.e. the VMX instructions that take a VMCS address as input, and don't have outputs. Getting the params right is annoying, especially when using KVM_ASM_SAFE(). As a bonus, this adds non-asserting double-underscores versions of VMXON and VMCLEAR for "free". Link: https://patch.msgid.link/20260904002423.3154436-12-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 71 +++++++------------ 1 file changed, 26 insertions(+), 45 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 5c46ae95c4c3..3def8df902a2 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -313,58 +313,39 @@ struct vmx_msr_entry { __GUEST_ASSERT(!__r, __stringify(insn) "[0x%lx] hit %s", \ __pa, __r < 0 ? "VM-Fail" : ex_str(__r)) -static inline void vmxon(u64 phys) -{ - u8 ret; - - __asm__ __volatile__ ("vmxon %[pa]; setna %[ret]" - : [ret]"=rm"(ret) - : [pa]"m"(phys) - : "cc", "memory"); - - __GUEST_ASSERT(!ret, "vmxon [0x%lx] failed", phys); +#define BUILD_VMCS_ASM_HELPERS(insn) \ +static inline int __##insn(u64 vmcs_pa) \ +{ \ + u64 error_code; \ + u8 vector; \ + u8 failed; \ + \ + asm volatile(KVM_ASM_SAFE(__stringify(insn) " %[pa]") \ + "\n\tsetna %[failed]" \ + : KVM_ASM_SAFE_OUTPUTS(vector, error_code), \ + [failed]"=qm"(failed) \ + : [pa]"m"(vmcs_pa) \ + : "cc", "memory", KVM_ASM_SAFE_CLOBBERS); \ + \ + return vector ? vector : failed ? -EINVAL : 0; \ +} \ + \ +static inline void insn(u64 vmcs_pa) \ +{ \ + int ret = __##insn(vmcs_pa); \ + \ + GUEST_ASSERT_VMX_INSN_SUCCEEDED(insn, ret, vmcs_pa); \ } +BUILD_VMCS_ASM_HELPERS(vmxon) +BUILD_VMCS_ASM_HELPERS(vmptrld) +BUILD_VMCS_ASM_HELPERS(vmclear) + static inline void vmxoff(void) { __asm__ __volatile__("vmxoff"); } -static inline void vmclear(u64 vmcs_pa) -{ - u8 ret; - - __asm__ __volatile__ ("vmclear %[pa]; setna %[ret]" - : [ret]"=rm"(ret) - : [pa]"m"(vmcs_pa) - : "cc", "memory"); - - __GUEST_ASSERT(!ret, "vmclear [0x%lx] failed\n", vmcs_pa); -} - -static inline int __vmptrld(u64 vmcs_pa) -{ - u64 error_code; - u8 vector; - u8 failed; - - asm volatile(KVM_ASM_SAFE("vmptrld %[pa]") - "\n\tsetna %[failed]" - : KVM_ASM_SAFE_OUTPUTS(vector, error_code), - [failed]"=qm"(failed) - : [pa]"m"(vmcs_pa) - : "cc", "memory", KVM_ASM_SAFE_CLOBBERS); - - return vector ? vector : failed ? -EINVAL : 0; -} - -static inline void vmptrld(u64 vmcs_pa) -{ - int ret = __vmptrld(vmcs_pa); - - GUEST_ASSERT_VMX_INSN_SUCCEEDED(vmptrld, ret, vmcs_pa); -} - static inline u64 vmptrst(void) { u64 value = 0; From 7763344704509dfe89a9394550ac85ba6f7f76f3 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:18 -0700 Subject: [PATCH 33/83] KVM: selftests: Drop dead return code from evmcs_vmptrld() and load_evmcs() Drop the return codes from evmcs_vmptrld() and load_evmcs() as they are hardcoded to return '0' and "true" respectively. No functional change intended. Link: https://patch.msgid.link/20260904002423.3154436-13-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/evmcs.h | 12 +++--------- .../selftests/kvm/x86/evmcs_smm_controls_test.c | 2 +- tools/testing/selftests/kvm/x86/hyperv_evmcs.c | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h index c5b9d26f6cdb..f18ebfb10ee0 100644 --- a/tools/testing/selftests/kvm/include/x86/evmcs.h +++ b/tools/testing/selftests/kvm/include/x86/evmcs.h @@ -245,24 +245,18 @@ static inline void evmcs_enable(void) enable_evmcs = true; } -static inline int evmcs_vmptrld(u64 vmcs_pa, void *vmcs) +static inline void evmcs_vmptrld(u64 vmcs_pa, void *vmcs) { current_vp_assist->current_nested_vmcs = vmcs_pa; current_vp_assist->enlighten_vmentry = 1; current_evmcs = vmcs; - - return 0; } -static inline bool load_evmcs(struct hyperv_test_pages *hv) +static inline void load_evmcs(struct hyperv_test_pages *hv) { - if (evmcs_vmptrld(hv->enlightened_vmcs_gpa, hv->enlightened_vmcs)) - return false; - + evmcs_vmptrld(hv->enlightened_vmcs_gpa, hv->enlightened_vmcs); current_evmcs->revision_id = EVMCS_VERSION; - - return true; } static inline int evmcs_vmread(u64 encoding, u64 *value) diff --git a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c index cafb58a37023..5c0391f8a38d 100644 --- a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c +++ b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c @@ -56,7 +56,7 @@ static void guest_code(struct vmx_pages *vmx_pages, evmcs_enable(); prepare_for_vmx_operation(vmx_pages); - GUEST_ASSERT(load_evmcs(hv_pages)); + load_evmcs(hv_pages); prepare_vmcs(vmx_pages, l2_guest_code); GUEST_ASSERT(!vmlaunch()); diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 849d66c8470d..4c0e76d64c83 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -95,7 +95,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, prepare_for_vmx_operation(vmx_pages); GUEST_SYNC(3); - GUEST_ASSERT(load_evmcs(hv_pages)); + load_evmcs(hv_pages); /* VMPTRST returns -1 until VMLAUNCH with eVMCS ptr set */ GUEST_ASSERT(vmptrst() == -1); From 978c4df4b2458ec5a26bc6fe8c8ac57bfaf6c9db Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:19 -0700 Subject: [PATCH 34/83] KVM: selftests: Dedup assembly code for VMLAUNCH and VMRESUME Dedup the assembly code for VMLAUNCH vs. VMRESUME, for both architectural VMCS and eVMCS, as the difference is literally only the actual VM-Enter instruction for VMLAUNCH vs. VMRESUME, and VMWRITE vs. MOV for VMCS vs. eVMCS. Opportunistically place the implementations in vmx.c so that the lengthy sequence (~580 bytes of code) aren't inlined for every single VM-Enter. 000000000041b910 <__vmlaunch>: 41b910: 80 3d 61 5e 10 00 00 cmpb $0x0,0x105e61(%rip) # 521778 41b917: 0f 85 13 01 00 00 jne 41ba30 <__vmlaunch+0x120> 41b91d: ba 14 6c 00 00 mov $0x6c14,%edx 41b922: b9 16 6c 00 00 mov $0x6c16,%ecx 41b927: 6a 00 push $0x0 41b929: 0f 79 d4 vmwrite %rsp,%rdx 41b92c: 48 8d 05 82 00 00 00 lea 0x82(%rip),%rax # 41b9b5 <__vmlaunch+0xa5> 41b933: 0f 79 c8 vmwrite %rax,%rcx 41b9ae: 0f 01 c2 vmlaunch 41b9b1: 48 ff 04 24 incq (%rsp) 41ba2d: 58 pop %rax 41ba2e: c3 ret 41ba2f: 90 nop 41ba30: 48 8b 35 39 5d 10 00 mov 0x105d39(%rip),%rsi # 521770 41ba37: c7 86 38 03 00 00 00 00 00 00 movl $0x0,0x338(%rsi) 41ba41: 6a 00 push $0x0 41ba43: 48 89 a6 68 02 00 00 mov %rsp,0x268(%rsi) 41ba4a: 48 8d 05 83 00 00 00 lea 0x83(%rip),%rax # 41bad4 <__vmlaunch+0x1c4> 41ba51: 48 89 46 50 mov %rax,0x50(%rsi) 41bacd: 0f 01 c2 vmlaunch 41bad0: 48 ff 04 24 incq (%rsp) 41bb4c: 58 pop %rax 41bb4d: c3 ret No functional change intended (though the code generation will be quite different). Cc: Yosry Ahmed Link: https://patch.msgid.link/20260904002423.3154436-14-seanjc@google.com Signed-off-by: Sean Christopherson --- .../testing/selftests/kvm/include/x86/evmcs.h | 58 ------------------- tools/testing/selftests/kvm/include/x86/vmx.h | 47 ++------------- tools/testing/selftests/kvm/lib/x86/vmx.c | 55 ++++++++++++++++++ 3 files changed, 60 insertions(+), 100 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h index f18ebfb10ee0..491fb8107473 100644 --- a/tools/testing/selftests/kvm/include/x86/evmcs.h +++ b/tools/testing/selftests/kvm/include/x86/evmcs.h @@ -1187,62 +1187,4 @@ static inline int evmcs_vmwrite(u64 encoding, u64 value) return 0; } -static inline int evmcs_vmlaunch(void) -{ - int ret; - - current_evmcs->hv_clean_fields = 0; - - __asm__ __volatile__("push $0;" - "mov %%rsp, (%[host_rsp]);" - "lea 1f(%%rip), %%rax;" - "mov %%rax, (%[host_rip]);" - VMX_SWITCH_GPRS_ASM - "vmlaunch;" - "incq (%%rsp);" - "1: ;" - VMX_SWITCH_GPRS_ASM - "pop %%rax;" - : [ret]"=&a"(ret) - : [host_rsp]"r" - ((u64)¤t_evmcs->host_rsp), - [host_rip]"r" - ((u64)¤t_evmcs->host_rip), - GUEST_REGS_OFFSETS - : "memory", "cc"); - return ret; -} - -/* - * No guest state (e.g. GPRs) is established by this vmresume. - */ -static inline int evmcs_vmresume(void) -{ - int ret; - - /* HOST_RIP */ - current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_HOST_GRP1; - /* HOST_RSP */ - current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_HOST_POINTER; - - __asm__ __volatile__("push $0;" - "mov %%rsp, (%[host_rsp]);" - "lea 1f(%%rip), %%rax;" - "mov %%rax, (%[host_rip]);" - VMX_SWITCH_GPRS_ASM - "vmresume;" - "incq (%%rsp);" - "1: ;" - VMX_SWITCH_GPRS_ASM - "pop %%rax;" - : [ret]"=&a"(ret) - : [host_rsp]"r" - ((u64)¤t_evmcs->host_rsp), - [host_rip]"r" - ((u64)¤t_evmcs->host_rip), - GUEST_REGS_OFFSETS - : "memory", "cc"); - return ret; -} - #endif /* !SELFTEST_KVM_EVMCS_H */ diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 3def8df902a2..d0ac8e128d98 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -359,54 +359,17 @@ static inline u64 vmptrst(void) return value; } +int __vmlaunch(void); +int __vmresume(void); + static inline int vmlaunch(void) { - int ret; - - if (enable_evmcs) - return evmcs_vmlaunch(); - - __asm__ __volatile__("push $0;" - "vmwrite %%rsp, %[host_rsp];" - "lea 1f(%%rip), %%rax;" - "vmwrite %%rax, %[host_rip];" - VMX_SWITCH_GPRS_ASM - "vmlaunch;" - "incq (%%rsp);" - "1: ;" - VMX_SWITCH_GPRS_ASM - "pop %%rax;" - : [ret]"=&a"(ret) - : [host_rsp]"r"((u64)HOST_RSP), - [host_rip]"r"((u64)HOST_RIP), - GUEST_REGS_OFFSETS - : "memory", "cc"); - return ret; + return __vmlaunch(); } static inline int vmresume(void) { - int ret; - - if (enable_evmcs) - return evmcs_vmresume(); - - __asm__ __volatile__("push $0;" - "vmwrite %%rsp, %[host_rsp];" - "lea 1f(%%rip), %%rax;" - "vmwrite %%rax, %[host_rip];" - VMX_SWITCH_GPRS_ASM - "vmresume;" - "incq (%%rsp);" - "1: ;" - VMX_SWITCH_GPRS_ASM - "pop %%rax;" - : [ret]"=&a"(ret) - : [host_rsp]"r"((u64)HOST_RSP), - [host_rip]"r"((u64)HOST_RIP), - GUEST_REGS_OFFSETS - : "memory", "cc"); - return ret; + return __vmresume(); } static inline void vmcall(void) diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 5c6cadc08405..1a8515de42b0 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -179,6 +179,61 @@ void load_vmcs(struct vmx_pages *vmx) vmclear(vmx->shadow_vmcs_gpa); } +#define __BUILD_VMX_VM_ENTRY_HELPER(insn, prefix, vmwrite_insn, vmwrite_operand, \ + __host_rsp, __host_rip) \ +static int __##prefix##_##insn(void) \ +{ \ + int ret; \ + \ + __asm__ __volatile__("push $0;" \ + __stringify(vmwrite_insn) " %%rsp, %[host_rsp];" \ + "lea 1f(%%rip), %%rax;" \ + __stringify(vmwrite_insn) " %%rax, %[host_rip];" \ + VMX_SWITCH_GPRS_ASM \ + __stringify(insn)";" \ + "incq (%%rsp);" \ + "1: ;" \ + VMX_SWITCH_GPRS_ASM \ + "pop %%rax;" \ + : [ret]"=&a"(ret) \ + : [host_rsp]__stringify(vmwrite_operand)(__host_rsp), \ + [host_rip]__stringify(vmwrite_operand)(__host_rip), \ + GUEST_REGS_OFFSETS \ + : "memory", "cc"); \ + return ret; \ +} + +#define BUILD_VMX_VM_ENTRY_HELPER(insn) \ + __BUILD_VMX_VM_ENTRY_HELPER(insn, _, vmwrite, r, (u64)HOST_RSP, (u64)HOST_RIP) \ + __BUILD_VMX_VM_ENTRY_HELPER(insn, __evmcs, mov, m, \ + current_evmcs->host_rsp, current_evmcs->host_rip) + +BUILD_VMX_VM_ENTRY_HELPER(vmlaunch) +BUILD_VMX_VM_ENTRY_HELPER(vmresume) + +int __vmlaunch(void) +{ + if (enable_evmcs) { + current_evmcs->hv_clean_fields = 0; + return ____evmcs_vmlaunch(); + } + + return ____vmlaunch(); +} + +int __vmresume(void) +{ + if (enable_evmcs) { + /* HOST_RIP */ + current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_HOST_GRP1; + /* HOST_RSP */ + current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_HOST_POINTER; + return ____evmcs_vmresume(); + } + + return ____vmresume(); +} + static bool ept_vpid_cap_supported(u64 mask) { return rdmsr(MSR_IA32_VMX_EPT_VPID_CAP) & mask; From 5adf7087cfd280970beeb2b8b46393787003ea51 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:20 -0700 Subject: [PATCH 35/83] KVM: selftests: Assert success in vmlaunch() and vmresume() Assert success in the "outer" APIs for VMLAUNCH and VMRESUME to dedup a pile of code, and switch to the double-underscores versions as necessary, to make it more obvious which paths expect VM-Enter *failure*. Link: https://patch.msgid.link/20260904002423.3154436-15-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 8 +++---- .../testing/selftests/kvm/lib/x86/memstress.c | 2 +- .../selftests/kvm/x86/aperfmperf_test.c | 2 +- .../kvm/x86/evmcs_smm_controls_test.c | 2 +- .../testing/selftests/kvm/x86/hyperv_evmcs.c | 22 ++++++++--------- .../selftests/kvm/x86/kvm_buslock_test.c | 2 +- .../selftests/kvm/x86/nested_close_kvm_test.c | 2 +- .../selftests/kvm/x86/nested_dirty_log_test.c | 2 +- .../selftests/kvm/x86/nested_emulation_test.c | 5 +++- .../kvm/x86/nested_exceptions_test.c | 5 +++- .../kvm/x86/nested_invalid_cr3_test.c | 4 ++-- .../selftests/kvm/x86/nested_tdp_fault_test.c | 2 +- .../kvm/x86/nested_tsc_adjust_test.c | 2 +- .../kvm/x86/nested_tsc_scaling_test.c | 2 +- .../kvm/x86/save_restore_pf_stress_test.c | 4 ++-- tools/testing/selftests/kvm/x86/state_test.c | 24 +++++++++---------- .../kvm/x86/triple_fault_event_test.c | 2 +- .../selftests/kvm/x86/vmx_apic_access_test.c | 4 ++-- .../kvm/x86/vmx_apicv_updates_test.c | 4 ++-- .../kvm/x86/vmx_invalid_nested_guest_state.c | 2 +- .../kvm/x86/vmx_nested_la57_state_test.c | 2 +- .../kvm/x86/vmx_preemption_timer_test.c | 4 ++-- 22 files changed, 57 insertions(+), 51 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index d0ac8e128d98..1419043b41dd 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -362,14 +362,14 @@ static inline u64 vmptrst(void) int __vmlaunch(void); int __vmresume(void); -static inline int vmlaunch(void) +static inline void vmlaunch(void) { - return __vmlaunch(); + __GUEST_ASSERT(!__vmlaunch(), "vmlaunch hit VM-Fail"); } -static inline int vmresume(void) +static inline void vmresume(void) { - return __vmresume(); + __GUEST_ASSERT(!__vmresume(), "vmresume hit VM-Fail"); } static inline void vmcall(void) diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testing/selftests/kvm/lib/x86/memstress.c index 207350ab6114..6a1815fb97e9 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -40,7 +40,7 @@ static void l1_vmx_code(struct vmx_pages *vmx, u64 vcpu_id) *(u64 *)vmx->stack = vcpu_id; prepare_vmcs(vmx, memstress_l2_guest_entry); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/testing/selftests/kvm/x86/aperfmperf_test.c index 11f5894d8ac7..a082658c1e48 100644 --- a/tools/testing/selftests/kvm/x86/aperfmperf_test.c +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -83,7 +83,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); } static void guest_code(void *nested_test_data) diff --git a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c index 5c0391f8a38d..4a65eb02349c 100644 --- a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c +++ b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c @@ -59,7 +59,7 @@ static void guest_code(struct vmx_pages *vmx_pages, load_evmcs(hv_pages); prepare_vmcs(vmx_pages, l2_guest_code); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); /* L2 exits via vmcall if test fails */ sync_with_host(2); diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 4c0e76d64c83..cbacb114db1b 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -105,7 +105,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, GUEST_SYNC(5); current_evmcs->revision_id = -1u; - GUEST_ASSERT(vmlaunch()); + GUEST_ASSERT(__vmlaunch()); current_evmcs->revision_id = EVMCS_VERSION; GUEST_SYNC(6); @@ -120,7 +120,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, current_vp_assist->nested_control.features.directhypercall = 1; *(u32 *)(hv_pages->partition_assist) = 0; - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); GUEST_ASSERT_EQ((vmreadz(VM_EXIT_INTR_INFO) & 0xff), NMI_VECTOR); GUEST_ASSERT(vmptrst() == hv_pages->enlightened_vmcs_gpa); @@ -130,7 +130,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, * up-to-date (RIP points where it should and not at the beginning * of l2_guest_code(). GUEST_SYNC(9) checks that. */ - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_SYNC(10); @@ -141,13 +141,13 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); __set_bit(MSR_FS_BASE & 0x1fff, vmx_pages->msr + 0x400); - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ /* Enable enlightened MSR bitmap */ current_evmcs->hv_enlightenments_control.msr_bitmap = 1; - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ @@ -155,14 +155,14 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, __set_bit(MSR_GS_BASE & 0x1fff, vmx_pages->msr + 0x400); /* Make sure HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP is set */ current_evmcs->hv_clean_fields |= HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP; - GUEST_ASSERT(!vmresume()); + vmresume(); /* Make sure we don't see EXIT_REASON_MSR_READ here so eMSR bitmap works */ GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); current_evmcs->guest_rip += 3; /* vmcall */ /* Now tell KVM we've changed MSR-Bitmap */ current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP; - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ @@ -170,15 +170,15 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, * L2 TLB flush test. First VMCALL should be handled directly by L0, * no VMCALL exit expected. */ - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ /* Enable synthetic vmexit */ *(u32 *)(hv_pages->partition_assist) = 1; - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == HV_VMX_SYNTHETIC_EXIT_REASON_TRAP_AFTER_FLUSH); - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_SYNC(11); @@ -192,7 +192,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, */ GUEST_SYNC(12); evmcs_vmptrld(0xdeadbeef, hv_pages->enlightened_vmcs); - GUEST_ASSERT(vmlaunch()); + GUEST_ASSERT(__vmlaunch()); GUEST_ASSERT(ud_count == 1); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c index 22993ec697fa..337d3c28bc6d 100644 --- a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c +++ b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c @@ -48,7 +48,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) prepare_vmcs(vmx, NULL); GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); } static void guest_code(void *test_data) diff --git a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c index 309cab009929..1f4a5e44a6ef 100644 --- a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c +++ b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c @@ -36,7 +36,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) /* Prepare the VMCS for L2 execution. */ prepare_vmcs(vmx_pages, l2_guest_code); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(0); } diff --git a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c index dca2bfc8b158..7987ce50c382 100644 --- a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c @@ -104,7 +104,7 @@ void l1_vmx_code(struct vmx_pages *vmx) prepare_vmcs(vmx, l2_rip); GUEST_SYNC(TEST_SYNC_NO_FAULT); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_SYNC(TEST_SYNC_NO_FAULT); GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL); GUEST_DONE(); diff --git a/tools/testing/selftests/kvm/x86/nested_emulation_test.c b/tools/testing/selftests/kvm/x86/nested_emulation_test.c index 2b38eff4f516..51e2f7e386c1 100644 --- a/tools/testing/selftests/kvm/x86/nested_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/nested_emulation_test.c @@ -102,7 +102,10 @@ static void guest_code(void *test_data) exit_insn_len = vmcb->control.next_rip - vmcb->save.rip; GUEST_ASSERT_EQ(vmcb->save.rip, (u64)l2_instruction); } else { - GUEST_ASSERT_EQ(i ? vmresume() : vmlaunch(), 0); + if (!i) + vmlaunch(); + else + vmresume(); exit_reason = vmreadz(VM_EXIT_REASON); exit_insn_len = vmreadz(VM_EXIT_INSTRUCTION_LEN); GUEST_ASSERT_EQ(vmreadz(GUEST_RIP), (u64)l2_instruction); diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index 50c271a03692..031eb84229dc 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -112,7 +112,10 @@ static void vmx_run_l2(void *l2_code, int vector, u32 error_code) { GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_code)); - GUEST_ASSERT_EQ(vector == SS_VECTOR ? vmlaunch() : vmresume(), 0); + if (vector == SS_VECTOR) + vmlaunch(); + else + vmresume(); if (vector == FAKE_TRIPLE_FAULT_VECTOR) return; diff --git a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c index 43deb3b358ea..101c1fe79eeb 100644 --- a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c +++ b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c @@ -48,13 +48,13 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) /* Try to run L2 with invalid CR3 and make sure it fails */ save_cr3 = vmreadz(GUEST_CR3); vmwrite(GUEST_CR3, -1ull); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == (EXIT_REASON_FAILED_VMENTRY | EXIT_REASON_INVALID_STATE)); /* Now restore CR3 and make sure L2 runs successfully */ vmwrite(GUEST_CR3, save_cr3); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_DONE(); diff --git a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c index 52df467e9c44..d6c443e3d147 100644 --- a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c @@ -60,7 +60,7 @@ static void l1_vmx_code(struct vmx_pages *vmx, u64 expected_fault_gpa, prepare_vmcs(vmx, l2_entry); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); /* Verify we got an EPT violation exit */ __GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_EPT_VIOLATION, diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c index 05194762ed33..c60dea899624 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c @@ -95,7 +95,7 @@ static void l1_guest_code(void *data) vmwrite(CPU_BASED_VM_EXEC_CONTROL, control); vmwrite(TSC_OFFSET, TSC_OFFSET_VALUE); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); } else { struct svm_test_data *svm = data; diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index b9fae268a9ec..b2fc7f0b5b3c 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -125,7 +125,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) vmwrite(TSC_MULTIPLIER_HIGH, TSC_MULTIPLIER_L2 >> 32); /* launch L2 */ - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); /* check that L1's frequency still looks good */ diff --git a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c index c0e4d6e156ad..f88427e925fc 100644 --- a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c +++ b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c @@ -104,11 +104,11 @@ static void l1_vmx_code(struct vmx_pages *vmx) GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR))); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); while (1) { GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); GUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_INFO) & 0xff, UD_VECTOR); - GUEST_ASSERT(!vmresume()); + vmresume(); } } diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index 6880db3442e5..4dadae350dca 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -87,20 +87,20 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) GUEST_SYNC(5); GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); /* Check that the launched state is preserved. */ - GUEST_ASSERT(vmlaunch()); + GUEST_ASSERT(__vmlaunch()); - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_SYNC(7); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmwrite(GUEST_RIP, vmreadz(GUEST_RIP) + 3); @@ -109,27 +109,27 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) vmwrite(VMCS_LINK_POINTER, vmx_pages->shadow_vmcs_gpa); vmptrld(vmx_pages->shadow_vmcs_gpa); - GUEST_ASSERT(vmlaunch()); + GUEST_ASSERT(__vmlaunch()); GUEST_SYNC(8); - GUEST_ASSERT(vmlaunch()); - GUEST_ASSERT(vmresume()); + GUEST_ASSERT(__vmlaunch()); + GUEST_ASSERT(__vmresume()); vmwrite(GUEST_RIP, 0xc0ffee); GUEST_SYNC(9); GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); vmptrld(vmx_pages->vmcs_gpa); - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmptrld(vmx_pages->shadow_vmcs_gpa); GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffffee); - GUEST_ASSERT(vmlaunch()); - GUEST_ASSERT(vmresume()); + GUEST_ASSERT(__vmlaunch()); + GUEST_ASSERT(__vmresume()); GUEST_SYNC(13); GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffffee); - GUEST_ASSERT(vmlaunch()); - GUEST_ASSERT(vmresume()); + GUEST_ASSERT(__vmlaunch()); + GUEST_ASSERT(__vmresume()); } static void __attribute__((__flatten__)) guest_code(void *arg) diff --git a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c index a9fb7046ea4f..bb31a1ba8932 100644 --- a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c +++ b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c @@ -30,7 +30,7 @@ void l1_guest_code_vmx(struct vmx_pages *vmx) prepare_vmcs(vmx, l2_guest_code); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); /* L2 should triple fault after a triple fault event injected. */ GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT); GUEST_DONE(); diff --git a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c index 1793f7b02219..fe29c869a235 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c @@ -53,14 +53,14 @@ static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa) /* Try to launch L2 with the memory-backed APIC-access address. */ GUEST_SYNC(vmreadz(APIC_ACCESS_ADDR)); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmwrite(APIC_ACCESS_ADDR, high_gpa); /* Try to resume L2 with the unbacked APIC-access address. */ GUEST_SYNC(vmreadz(APIC_ACCESS_ADDR)); - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_DONE(); diff --git a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c index f7ebc9b15629..e578221da247 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c @@ -62,7 +62,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) * Run L2 to switch to x2APIC mode, which in turn will uninhibit APICv, * as KVM should force the APIC ID back to its default. */ - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmwrite(GUEST_RIP, vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)); GUEST_ASSERT(rdmsr(MSR_IA32_APICBASE) & MSR_IA32_APICBASE_EXTD); @@ -90,7 +90,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) * handles the x2APIC => xAPIC transition and inhibits APICv while L2 * is active. */ - GUEST_ASSERT(!vmresume()); + vmresume(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_ASSERT(!(rdmsr(MSR_IA32_APICBASE) & MSR_IA32_APICBASE_EXTD)); diff --git a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c index 4a6445958cf4..3017938fa993 100644 --- a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c +++ b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c @@ -55,7 +55,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(!(vmreadz(CPU_BASED_VM_EXEC_CONTROL) & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS) || !(vmreadz(SECONDARY_VM_EXEC_CONTROL) & SECONDARY_EXEC_UNRESTRICTED_GUEST)); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); /* L2 should triple fault after main() stuffs invalid guest state. */ GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT); diff --git a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c index 608bc0ed27db..46e5ca997e74 100644 --- a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c @@ -56,7 +56,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) guest_cr4 &= ~X86_CR4_LA57; vmwrite(GUEST_CR4, guest_cr4); - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); exit_reason = vmreadz(VM_EXIT_REASON); GUEST_ASSERT(exit_reason == EXIT_REASON_VMCALL); diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index abbe5aedd35f..97c84849f83e 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -90,7 +90,7 @@ void l1_guest_code(struct vmx_pages *vmx_pages) !(ctrl_exit_rev.clr & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER)) return; - GUEST_ASSERT(!vmlaunch()); + vmlaunch(); GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmwrite(GUEST_RIP, vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)); @@ -110,7 +110,7 @@ void l1_guest_code(struct vmx_pages *vmx_pages) l1_vmx_pt_start = (rdtsc() >> vmx_pt_rate) << vmx_pt_rate; - GUEST_ASSERT(!vmresume()); + vmresume(); l1_vmx_pt_finish = rdtsc(); From 68b94d08f9dcff3954c4f5baa7f2c5faa828b625 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:21 -0700 Subject: [PATCH 36/83] KVM: selftests: Add __vmwrite() and use it when initializing optional fields. Add __vmwrite() in anticipation of having vmwrite() assert success, and use the "failure allowed" version in the common VMCS setup code, which is the only code that wants to write fields that may or may not exist (although arguably the code should check if the field exists instead of blindly writing). E.g. When doing initial configuration of a VMCS, the core library doesn't want to assume secondary controls are supported, and so blindly does a VMWRITE and pivots on success/failure. All usage of vmwrite() outside of common setup already asserts success, or 100% relies on VMWRITE succeeding in order for the test to function correctly. No functional change intended. Link: https://patch.msgid.link/20260904002423.3154436-16-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 7 ++++- tools/testing/selftests/kvm/lib/x86/vmx.c | 28 +++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 1419043b41dd..40111db580a8 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -414,7 +414,7 @@ static inline u64 vmreadz(u64 encoding) return value; } -static inline int vmwrite(u64 encoding, u64 value) +static __always_inline int __vmwrite(u64 encoding, u64 value) { u8 ret; @@ -429,6 +429,11 @@ static inline int vmwrite(u64 encoding, u64 value) return ret; } +static inline int vmwrite(u64 encoding, u64 value) +{ + return __vmwrite(encoding, value); +} + static inline u32 vmcs_revision(void) { return rdmsr(MSR_IA32_VMX_BASIC); diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 1a8515de42b0..14ccfbd9c32d 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -251,8 +251,8 @@ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) { u32 sec_exec_ctl = 0; - vmwrite(VIRTUAL_PROCESSOR_ID, 0); - vmwrite(POSTED_INTR_NV, 0); + __vmwrite(VIRTUAL_PROCESSOR_ID, 0); + __vmwrite(POSTED_INTR_NV, 0); vmwrite(PIN_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS)); @@ -269,7 +269,7 @@ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) sec_exec_ctl |= SECONDARY_EXEC_ENABLE_EPT; } - if (!vmwrite(SECONDARY_VM_EXEC_CONTROL, sec_exec_ctl)) + if (!__vmwrite(SECONDARY_VM_EXEC_CONTROL, sec_exec_ctl)) vmwrite(CPU_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS); else { @@ -289,16 +289,16 @@ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) VM_ENTRY_IA32E_MODE); /* 64-bit guest */ vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0); vmwrite(VM_ENTRY_INTR_INFO_FIELD, 0); - vmwrite(TPR_THRESHOLD, 0); + __vmwrite(TPR_THRESHOLD, 0); vmwrite(CR0_GUEST_HOST_MASK, 0); vmwrite(CR4_GUEST_HOST_MASK, 0); vmwrite(CR0_READ_SHADOW, get_cr0()); vmwrite(CR4_READ_SHADOW, get_cr4()); - vmwrite(MSR_BITMAP, vmx->msr_gpa); - vmwrite(VMREAD_BITMAP, vmx->vmread_gpa); - vmwrite(VMWRITE_BITMAP, vmx->vmwrite_gpa); + __vmwrite(MSR_BITMAP, vmx->msr_gpa); + __vmwrite(VMREAD_BITMAP, vmx->vmread_gpa); + __vmwrite(VMWRITE_BITMAP, vmx->vmwrite_gpa); } /* @@ -356,15 +356,15 @@ static inline void init_vmcs_guest_state(void *rip, void *rsp) vmwrite(GUEST_GS_SELECTOR, vmreadz(HOST_GS_SELECTOR)); vmwrite(GUEST_LDTR_SELECTOR, 0); vmwrite(GUEST_TR_SELECTOR, vmreadz(HOST_TR_SELECTOR)); - vmwrite(GUEST_INTR_STATUS, 0); - vmwrite(GUEST_PML_INDEX, 0); + __vmwrite(GUEST_INTR_STATUS, 0); + __vmwrite(GUEST_PML_INDEX, 0); vmwrite(VMCS_LINK_POINTER, -1ll); vmwrite(GUEST_IA32_DEBUGCTL, 0); - vmwrite(GUEST_IA32_PAT, vmreadz(HOST_IA32_PAT)); - vmwrite(GUEST_IA32_EFER, vmreadz(HOST_IA32_EFER)); - vmwrite(GUEST_IA32_PERF_GLOBAL_CTRL, - vmreadz(HOST_IA32_PERF_GLOBAL_CTRL)); + __vmwrite(GUEST_IA32_PAT, vmreadz(HOST_IA32_PAT)); + __vmwrite(GUEST_IA32_EFER, vmreadz(HOST_IA32_EFER)); + __vmwrite(GUEST_IA32_PERF_GLOBAL_CTRL, + vmreadz(HOST_IA32_PERF_GLOBAL_CTRL)); vmwrite(GUEST_ES_LIMIT, -1); vmwrite(GUEST_CS_LIMIT, -1); @@ -391,7 +391,7 @@ static inline void init_vmcs_guest_state(void *rip, void *rsp) vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0); vmwrite(GUEST_ACTIVITY_STATE, 0); vmwrite(GUEST_SYSENTER_CS, vmreadz(HOST_IA32_SYSENTER_CS)); - vmwrite(VMX_PREEMPTION_TIMER_VALUE, 0); + __vmwrite(VMX_PREEMPTION_TIMER_VALUE, 0); vmwrite(GUEST_CR0, vmreadz(HOST_CR0)); vmwrite(GUEST_CR3, vmreadz(HOST_CR3)); From 6ecd351628f3493060fdd02d8044712ba7af094a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:22 -0700 Subject: [PATCH 37/83] KVM: selftests: Always assert that vmwrite() succeeds Assert that vmwrite() succeeds instead of forcing all callers to do the same (or as is much more common, ignoring VM-Fail and likely letting the test fail in weird ways), opportunistically provide a more verbose error message on failure. All usage of vmwrite() "guarantees" success, either because the field in question is unconditionally emulated by KVM, by virtue of earlier support checks, or by falling over (later in the test) if the VMWRITE fails. Cc: Yosry Ahmed Link: https://patch.msgid.link/20260904002423.3154436-17-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 5 +++-- tools/testing/selftests/kvm/x86/aperfmperf_test.c | 2 +- tools/testing/selftests/kvm/x86/kvm_buslock_test.c | 2 +- .../testing/selftests/kvm/x86/nested_emulation_test.c | 6 +++--- .../testing/selftests/kvm/x86/nested_exceptions_test.c | 10 +++++----- .../selftests/kvm/x86/save_restore_pf_stress_test.c | 2 +- tools/testing/selftests/kvm/x86/state_test.c | 4 ++-- .../selftests/kvm/x86/vmx_preemption_timer_test.c | 8 +++----- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 40111db580a8..be10028c51b9 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -429,9 +429,10 @@ static __always_inline int __vmwrite(u64 encoding, u64 value) return ret; } -static inline int vmwrite(u64 encoding, u64 value) +static inline void vmwrite(u64 encoding, u64 value) { - return __vmwrite(encoding, value); + __GUEST_ASSERT(!__vmwrite(encoding, value), + "vmwrite[0x%lx] = 0x%lx hit VM-Fail", encoding, value); } static inline u32 vmcs_revision(void) diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/testing/selftests/kvm/x86/aperfmperf_test.c index a082658c1e48..7761a8bdf5f6 100644 --- a/tools/testing/selftests/kvm/x86/aperfmperf_test.c +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -82,7 +82,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); - GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); + vmwrite(GUEST_RIP, (u64)l2_guest_code); vmlaunch(); } diff --git a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c index 337d3c28bc6d..e1d2730b16ba 100644 --- a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c +++ b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c @@ -47,7 +47,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) prepare_vmcs(vmx, NULL); - GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); + vmwrite(GUEST_RIP, (u64)l2_guest_code); vmlaunch(); } diff --git a/tools/testing/selftests/kvm/x86/nested_emulation_test.c b/tools/testing/selftests/kvm/x86/nested_emulation_test.c index 51e2f7e386c1..009932b1604e 100644 --- a/tools/testing/selftests/kvm/x86/nested_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/nested_emulation_test.c @@ -70,9 +70,9 @@ static void guest_code(void *test_data) load_vmcs(test_data); prepare_vmcs(test_data, NULL); - GUEST_ASSERT(!vmwrite(GUEST_IDTR_LIMIT, 0)); - GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); - GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, 0)); + vmwrite(GUEST_IDTR_LIMIT, 0); + vmwrite(GUEST_RIP, (u64)l2_guest_code); + vmwrite(EXCEPTION_BITMAP, 0); vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_PAUSE_EXITING | diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index 031eb84229dc..d0beaa7b280b 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -110,7 +110,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void vmx_run_l2(void *l2_code, int vector, u32 error_code) { - GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_code)); + vmwrite(GUEST_RIP, (u64)l2_code); if (vector == SS_VECTOR) vmlaunch(); @@ -133,21 +133,21 @@ static void l1_vmx_code(struct vmx_pages *vmx) load_vmcs(vmx); prepare_vmcs(vmx, NULL); - GUEST_ASSERT_EQ(vmwrite(GUEST_IDTR_LIMIT, 0), 0); + vmwrite(GUEST_IDTR_LIMIT, 0); /* * VMX disallows injecting an exception with error_code[31:16] != 0, * and hardware will never generate a VM-Exit with bits 31:16 set. * KVM should likewise truncate the "bad" userspace value. */ - GUEST_ASSERT_EQ(vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS_GP_DF), 0); + vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS_GP_DF); vmx_run_l2(l2_ss_pending_test, SS_VECTOR, (u16)SS_ERROR_CODE); vmx_run_l2(l2_ss_injected_gp_test, GP_VECTOR, GP_ERROR_CODE_INTEL); - GUEST_ASSERT_EQ(vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS_DF), 0); + vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS_DF); vmx_run_l2(l2_ss_injected_df_test, DF_VECTOR, DF_ERROR_CODE); - GUEST_ASSERT_EQ(vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS), 0); + vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS); vmx_run_l2(l2_ss_injected_tf_test, FAKE_TRIPLE_FAULT_VECTOR, 0); GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_TRIPLE_FAULT); diff --git a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c index f88427e925fc..e728f040916f 100644 --- a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c +++ b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c @@ -102,7 +102,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) load_vmcs(vmx); prepare_vmcs(vmx, guest_access_memory); - GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR))); + vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR)); vmlaunch(); while (1) { diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index 4dadae350dca..8de6eb183670 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -62,10 +62,10 @@ void vmx_l2_guest_code(void) GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); GUEST_SYNC(10); GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); - GUEST_ASSERT(!vmwrite(GUEST_RIP, 0xc0fffee)); + vmwrite(GUEST_RIP, 0xc0fffee); GUEST_SYNC(11); GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0fffee); - GUEST_ASSERT(!vmwrite(GUEST_RIP, 0xc0ffffee)); + vmwrite(GUEST_RIP, 0xc0ffffee); GUEST_SYNC(12); /* Done, exit to L1 and never come back. */ diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index 97c84849f83e..06e74c72fe65 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -97,12 +97,10 @@ void l1_guest_code(struct vmx_pages *vmx_pages) /* * Turn on PIN control and resume the guest */ - GUEST_ASSERT(!vmwrite(PIN_BASED_VM_EXEC_CONTROL, - vmreadz(PIN_BASED_VM_EXEC_CONTROL) | - PIN_BASED_VMX_PREEMPTION_TIMER)); + vmwrite(PIN_BASED_VM_EXEC_CONTROL, + vmreadz(PIN_BASED_VM_EXEC_CONTROL) | PIN_BASED_VMX_PREEMPTION_TIMER); - GUEST_ASSERT(!vmwrite(VMX_PREEMPTION_TIMER_VALUE, - PREEMPTION_TIMER_VALUE)); + vmwrite(VMX_PREEMPTION_TIMER_VALUE, PREEMPTION_TIMER_VALUE); vmx_pt_rate = rdmsr(MSR_IA32_VMX_MISC) & 0x1F; From 4e824a04d4fad3f2754d0089fb150c043daab732 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 3 Sep 2026 17:24:23 -0700 Subject: [PATCH 38/83] KVM: selftests: Always assert that vmreadz() succeeds Rename vmreadz() => vmread() (and vmread() => __vmread()) and have it assert success instead of returning garbage on failure. While the eVMCS path avoids clobbering *value on failure, the VMREAD path unconditionally stores tmp to *value, without ever zeroing tmp. I.e. if VMREAD did fail, *value would be loaded with random stack data, not '0'. An audit of the code confirms that nothing is actually relying on vmreadz() to zero the result on failure, as all paths either check for a non-zero value, propagate the result into vmwrite(), or guard the vmread() with a pre-check that the field exists. Opportunistically fix the bug in what is now __vmread(), so that the code isn't consuming uninitialized stack data. Begrudgingly keep zeroing value in vmread(), as gcc-15 at least isn't smart enough to figure out that value won't be used uninitialized (though to be fair, strictly speaking it isn't actually unreachable code). Link: https://patch.msgid.link/20260904002423.3154436-18-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 15 +++--- .../testing/selftests/kvm/lib/x86/memstress.c | 2 +- tools/testing/selftests/kvm/lib/x86/vmx.c | 52 +++++++++---------- .../selftests/kvm/x86/aperfmperf_test.c | 2 +- .../testing/selftests/kvm/x86/hyperv_evmcs.c | 24 ++++----- .../selftests/kvm/x86/nested_dirty_log_test.c | 2 +- .../selftests/kvm/x86/nested_emulation_test.c | 8 +-- .../kvm/x86/nested_exceptions_test.c | 10 ++-- .../kvm/x86/nested_invalid_cr3_test.c | 6 +-- .../selftests/kvm/x86/nested_tdp_fault_test.c | 10 ++-- .../kvm/x86/nested_tsc_adjust_test.c | 4 +- .../kvm/x86/nested_tsc_scaling_test.c | 6 +-- .../kvm/x86/save_restore_pf_stress_test.c | 4 +- tools/testing/selftests/kvm/x86/state_test.c | 24 ++++----- .../kvm/x86/triple_fault_event_test.c | 2 +- .../selftests/kvm/x86/vmx_apic_access_test.c | 12 ++--- .../kvm/x86/vmx_apicv_updates_test.c | 8 +-- .../kvm/x86/vmx_invalid_nested_guest_state.c | 6 +-- .../kvm/x86/vmx_nested_la57_state_test.c | 4 +- .../kvm/x86/vmx_preemption_timer_test.c | 8 +-- 20 files changed, 104 insertions(+), 105 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index be10028c51b9..ca67a618b68f 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -386,7 +386,7 @@ static inline void vmcall(void) "r10", "r11", "r12", "r13", "r14", "r15"); } -static inline int vmread(u64 encoding, u64 *value) +static inline int __vmread(u64 encoding, u64 *value) { u64 tmp; u8 ret; @@ -399,18 +399,17 @@ static inline int vmread(u64 encoding, u64 *value) : [encoding]"r"(encoding) : "cc", "memory"); - *value = tmp; + if (!ret) + *value = tmp; return ret; } -/* - * A wrapper around vmread that ignores errors and returns zero if the - * vmread instruction fails. - */ -static inline u64 vmreadz(u64 encoding) +static inline u64 vmread(u64 encoding) { u64 value = 0; - vmread(encoding, &value); + + __GUEST_ASSERT(!__vmread(encoding, &value), + "vmread[0x%lx] hit VM-Fail", encoding); return value; } diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testing/selftests/kvm/lib/x86/memstress.c index 6a1815fb97e9..9bfea36144a7 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -41,7 +41,7 @@ static void l1_vmx_code(struct vmx_pages *vmx, u64 vcpu_id) prepare_vmcs(vmx, memstress_l2_guest_entry); vmlaunch(); - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL); + GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_VMCALL); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 14ccfbd9c32d..6a53c8ce3b3c 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -308,7 +308,7 @@ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) */ static inline void init_vmcs_host_state(void) { - u32 exit_controls = vmreadz(VM_EXIT_CONTROLS); + u32 exit_controls = vmread(VM_EXIT_CONTROLS); vmwrite(HOST_ES_SELECTOR, get_es()); vmwrite(HOST_CS_SELECTOR, get_cs()); @@ -348,23 +348,23 @@ static inline void init_vmcs_host_state(void) */ static inline void init_vmcs_guest_state(void *rip, void *rsp) { - vmwrite(GUEST_ES_SELECTOR, vmreadz(HOST_ES_SELECTOR)); - vmwrite(GUEST_CS_SELECTOR, vmreadz(HOST_CS_SELECTOR)); - vmwrite(GUEST_SS_SELECTOR, vmreadz(HOST_SS_SELECTOR)); - vmwrite(GUEST_DS_SELECTOR, vmreadz(HOST_DS_SELECTOR)); - vmwrite(GUEST_FS_SELECTOR, vmreadz(HOST_FS_SELECTOR)); - vmwrite(GUEST_GS_SELECTOR, vmreadz(HOST_GS_SELECTOR)); + vmwrite(GUEST_ES_SELECTOR, vmread(HOST_ES_SELECTOR)); + vmwrite(GUEST_CS_SELECTOR, vmread(HOST_CS_SELECTOR)); + vmwrite(GUEST_SS_SELECTOR, vmread(HOST_SS_SELECTOR)); + vmwrite(GUEST_DS_SELECTOR, vmread(HOST_DS_SELECTOR)); + vmwrite(GUEST_FS_SELECTOR, vmread(HOST_FS_SELECTOR)); + vmwrite(GUEST_GS_SELECTOR, vmread(HOST_GS_SELECTOR)); vmwrite(GUEST_LDTR_SELECTOR, 0); - vmwrite(GUEST_TR_SELECTOR, vmreadz(HOST_TR_SELECTOR)); + vmwrite(GUEST_TR_SELECTOR, vmread(HOST_TR_SELECTOR)); __vmwrite(GUEST_INTR_STATUS, 0); __vmwrite(GUEST_PML_INDEX, 0); vmwrite(VMCS_LINK_POINTER, -1ll); vmwrite(GUEST_IA32_DEBUGCTL, 0); - __vmwrite(GUEST_IA32_PAT, vmreadz(HOST_IA32_PAT)); - __vmwrite(GUEST_IA32_EFER, vmreadz(HOST_IA32_EFER)); + __vmwrite(GUEST_IA32_PAT, vmread(HOST_IA32_PAT)); + __vmwrite(GUEST_IA32_EFER, vmread(HOST_IA32_EFER)); __vmwrite(GUEST_IA32_PERF_GLOBAL_CTRL, - vmreadz(HOST_IA32_PERF_GLOBAL_CTRL)); + vmread(HOST_IA32_PERF_GLOBAL_CTRL)); vmwrite(GUEST_ES_LIMIT, -1); vmwrite(GUEST_CS_LIMIT, -1); @@ -377,42 +377,42 @@ static inline void init_vmcs_guest_state(void *rip, void *rsp) vmwrite(GUEST_GDTR_LIMIT, 0xffff); vmwrite(GUEST_IDTR_LIMIT, 0xffff); vmwrite(GUEST_ES_AR_BYTES, - vmreadz(GUEST_ES_SELECTOR) == 0 ? 0x10000 : 0xc093); + vmread(GUEST_ES_SELECTOR) == 0 ? 0x10000 : 0xc093); vmwrite(GUEST_CS_AR_BYTES, 0xa09b); vmwrite(GUEST_SS_AR_BYTES, 0xc093); vmwrite(GUEST_DS_AR_BYTES, - vmreadz(GUEST_DS_SELECTOR) == 0 ? 0x10000 : 0xc093); + vmread(GUEST_DS_SELECTOR) == 0 ? 0x10000 : 0xc093); vmwrite(GUEST_FS_AR_BYTES, - vmreadz(GUEST_FS_SELECTOR) == 0 ? 0x10000 : 0xc093); + vmread(GUEST_FS_SELECTOR) == 0 ? 0x10000 : 0xc093); vmwrite(GUEST_GS_AR_BYTES, - vmreadz(GUEST_GS_SELECTOR) == 0 ? 0x10000 : 0xc093); + vmread(GUEST_GS_SELECTOR) == 0 ? 0x10000 : 0xc093); vmwrite(GUEST_LDTR_AR_BYTES, 0x10000); vmwrite(GUEST_TR_AR_BYTES, 0x8b); vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0); vmwrite(GUEST_ACTIVITY_STATE, 0); - vmwrite(GUEST_SYSENTER_CS, vmreadz(HOST_IA32_SYSENTER_CS)); + vmwrite(GUEST_SYSENTER_CS, vmread(HOST_IA32_SYSENTER_CS)); __vmwrite(VMX_PREEMPTION_TIMER_VALUE, 0); - vmwrite(GUEST_CR0, vmreadz(HOST_CR0)); - vmwrite(GUEST_CR3, vmreadz(HOST_CR3)); - vmwrite(GUEST_CR4, vmreadz(HOST_CR4)); + vmwrite(GUEST_CR0, vmread(HOST_CR0)); + vmwrite(GUEST_CR3, vmread(HOST_CR3)); + vmwrite(GUEST_CR4, vmread(HOST_CR4)); vmwrite(GUEST_ES_BASE, 0); vmwrite(GUEST_CS_BASE, 0); vmwrite(GUEST_SS_BASE, 0); vmwrite(GUEST_DS_BASE, 0); - vmwrite(GUEST_FS_BASE, vmreadz(HOST_FS_BASE)); - vmwrite(GUEST_GS_BASE, vmreadz(HOST_GS_BASE)); + vmwrite(GUEST_FS_BASE, vmread(HOST_FS_BASE)); + vmwrite(GUEST_GS_BASE, vmread(HOST_GS_BASE)); vmwrite(GUEST_LDTR_BASE, 0); - vmwrite(GUEST_TR_BASE, vmreadz(HOST_TR_BASE)); - vmwrite(GUEST_GDTR_BASE, vmreadz(HOST_GDTR_BASE)); - vmwrite(GUEST_IDTR_BASE, vmreadz(HOST_IDTR_BASE)); + vmwrite(GUEST_TR_BASE, vmread(HOST_TR_BASE)); + vmwrite(GUEST_GDTR_BASE, vmread(HOST_GDTR_BASE)); + vmwrite(GUEST_IDTR_BASE, vmread(HOST_IDTR_BASE)); vmwrite(GUEST_DR7, 0x400); vmwrite(GUEST_RSP, (u64)rsp); vmwrite(GUEST_RIP, (u64)rip); vmwrite(GUEST_RFLAGS, X86_EFLAGS_FIXED); vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, 0); - vmwrite(GUEST_SYSENTER_ESP, vmreadz(HOST_IA32_SYSENTER_ESP)); - vmwrite(GUEST_SYSENTER_EIP, vmreadz(HOST_IA32_SYSENTER_EIP)); + vmwrite(GUEST_SYSENTER_ESP, vmread(HOST_IA32_SYSENTER_ESP)); + vmwrite(GUEST_SYSENTER_EIP, vmread(HOST_IA32_SYSENTER_EIP)); } void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip) diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/testing/selftests/kvm/x86/aperfmperf_test.c index 7761a8bdf5f6..1ca6495a446f 100644 --- a/tools/testing/selftests/kvm/x86/aperfmperf_test.c +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -80,7 +80,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) * in the VMCS by prepare_vmcs()), as MSR exiting mandatory on Intel. */ vmwrite(CPU_BASED_VM_EXEC_CONTROL, - vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); + vmread(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); vmwrite(GUEST_RIP, (u64)l2_guest_code); vmlaunch(); diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index cbacb114db1b..c81f2dd4f2a1 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -109,7 +109,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, current_evmcs->revision_id = EVMCS_VERSION; GUEST_SYNC(6); - vmwrite(PIN_BASED_VM_EXEC_CONTROL, vmreadz(PIN_BASED_VM_EXEC_CONTROL) | + vmwrite(PIN_BASED_VM_EXEC_CONTROL, vmread(PIN_BASED_VM_EXEC_CONTROL) | PIN_BASED_NMI_EXITING); /* L2 TLB flush setup */ @@ -121,8 +121,8 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, *(u32 *)(hv_pages->partition_assist) = 0; vmlaunch(); - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); - GUEST_ASSERT_EQ((vmreadz(VM_EXIT_INTR_INFO) & 0xff), NMI_VECTOR); + GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); + GUEST_ASSERT_EQ((vmread(VM_EXIT_INTR_INFO) & 0xff), NMI_VECTOR); GUEST_ASSERT(vmptrst() == hv_pages->enlightened_vmcs_gpa); /* @@ -134,21 +134,21 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, GUEST_SYNC(10); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); current_evmcs->guest_rip += 3; /* vmcall */ /* Intercept RDMSR 0xc0000100 */ - vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmreadz(CPU_BASED_VM_EXEC_CONTROL) | + vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmread(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); __set_bit(MSR_FS_BASE & 0x1fff, vmx_pages->msr + 0x400); vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ /* Enable enlightened MSR bitmap */ current_evmcs->hv_enlightenments_control.msr_bitmap = 1; vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ /* Intercept RDMSR 0xc0000101 without telling KVM about it */ @@ -157,13 +157,13 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, current_evmcs->hv_clean_fields |= HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP; vmresume(); /* Make sure we don't see EXIT_REASON_MSR_READ here so eMSR bitmap works */ - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); current_evmcs->guest_rip += 3; /* vmcall */ /* Now tell KVM we've changed MSR-Bitmap */ current_evmcs->hv_clean_fields &= ~HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP; vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ /* @@ -171,15 +171,15 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, * no VMCALL exit expected. */ vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_MSR_READ); current_evmcs->guest_rip += 2; /* rdmsr */ /* Enable synthetic vmexit */ *(u32 *)(hv_pages->partition_assist) = 1; vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == HV_VMX_SYNTHETIC_EXIT_REASON_TRAP_AFTER_FLUSH); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == HV_VMX_SYNTHETIC_EXIT_REASON_TRAP_AFTER_FLUSH); vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_SYNC(11); /* VMPTRLD instruction causes #UD after enlightened VMLAUNCH */ diff --git a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c index 7987ce50c382..7e5f9ae1cb00 100644 --- a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c @@ -106,7 +106,7 @@ void l1_vmx_code(struct vmx_pages *vmx) GUEST_SYNC(TEST_SYNC_NO_FAULT); vmlaunch(); GUEST_SYNC(TEST_SYNC_NO_FAULT); - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL); + GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_VMCALL); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/nested_emulation_test.c b/tools/testing/selftests/kvm/x86/nested_emulation_test.c index 009932b1604e..68d9ce9dc6ba 100644 --- a/tools/testing/selftests/kvm/x86/nested_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/nested_emulation_test.c @@ -74,7 +74,7 @@ static void guest_code(void *test_data) vmwrite(GUEST_RIP, (u64)l2_guest_code); vmwrite(EXCEPTION_BITMAP, 0); - vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmreadz(CPU_BASED_VM_EXEC_CONTROL) | + vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmread(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_PAUSE_EXITING | CPU_BASED_HLT_EXITING); } @@ -106,9 +106,9 @@ static void guest_code(void *test_data) vmlaunch(); else vmresume(); - exit_reason = vmreadz(VM_EXIT_REASON); - exit_insn_len = vmreadz(VM_EXIT_INSTRUCTION_LEN); - GUEST_ASSERT_EQ(vmreadz(GUEST_RIP), (u64)l2_instruction); + exit_reason = vmread(VM_EXIT_REASON); + exit_insn_len = vmread(VM_EXIT_INSTRUCTION_LEN); + GUEST_ASSERT_EQ(vmread(GUEST_RIP), (u64)l2_instruction); } __GUEST_ASSERT(exit_reason == insn->exit_reason[f], diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index d0beaa7b280b..d23e24f35790 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -120,10 +120,10 @@ static void vmx_run_l2(void *l2_code, int vector, u32 error_code) if (vector == FAKE_TRIPLE_FAULT_VECTOR) return; - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); - GUEST_ASSERT_EQ((vmreadz(VM_EXIT_INTR_INFO) & 0xff), vector); - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_ERROR_CODE), error_code); - GUEST_ASSERT(!vmreadz(GUEST_INTERRUPTIBILITY_INFO)); + GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); + GUEST_ASSERT_EQ((vmread(VM_EXIT_INTR_INFO) & 0xff), vector); + GUEST_ASSERT_EQ(vmread(VM_EXIT_INTR_ERROR_CODE), error_code); + GUEST_ASSERT(!vmread(GUEST_INTERRUPTIBILITY_INFO)); } static void l1_vmx_code(struct vmx_pages *vmx) @@ -149,7 +149,7 @@ static void l1_vmx_code(struct vmx_pages *vmx) vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS); vmx_run_l2(l2_ss_injected_tf_test, FAKE_TRIPLE_FAULT_VECTOR, 0); - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_TRIPLE_FAULT); + GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_TRIPLE_FAULT); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c index 101c1fe79eeb..6e147826bc90 100644 --- a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c +++ b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c @@ -46,16 +46,16 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) prepare_vmcs(vmx_pages, l2_guest_code); /* Try to run L2 with invalid CR3 and make sure it fails */ - save_cr3 = vmreadz(GUEST_CR3); + save_cr3 = vmread(GUEST_CR3); vmwrite(GUEST_CR3, -1ull); vmlaunch(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == + GUEST_ASSERT(vmread(VM_EXIT_REASON) == (EXIT_REASON_FAILED_VMENTRY | EXIT_REASON_INVALID_STATE)); /* Now restore CR3 and make sure L2 runs successfully */ vmwrite(GUEST_CR3, save_cr3); vmlaunch(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c index d6c443e3d147..fae77d843b13 100644 --- a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c @@ -63,17 +63,17 @@ static void l1_vmx_code(struct vmx_pages *vmx, u64 expected_fault_gpa, vmlaunch(); /* Verify we got an EPT violation exit */ - __GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_EPT_VIOLATION, + __GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_EPT_VIOLATION, "Expected EPT violation (0x%x), got 0x%lx", EXIT_REASON_EPT_VIOLATION, - vmreadz(VM_EXIT_REASON)); + vmread(VM_EXIT_REASON)); - __GUEST_ASSERT(vmreadz(GUEST_PHYSICAL_ADDRESS) == expected_fault_gpa, + __GUEST_ASSERT(vmread(GUEST_PHYSICAL_ADDRESS) == expected_fault_gpa, "Expected guest_physical_address = 0x%lx, got 0x%lx", expected_fault_gpa, - vmreadz(GUEST_PHYSICAL_ADDRESS)); + vmread(GUEST_PHYSICAL_ADDRESS)); - exit_qual = vmreadz(EXIT_QUALIFICATION); + exit_qual = vmread(EXIT_QUALIFICATION); /* * Note, EPT page table accesses are always read+write, e.g. so that diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c index c60dea899624..6a1a88e926f8 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c @@ -90,13 +90,13 @@ static void l1_guest_code(void *data) load_vmcs(vmx_pages); prepare_vmcs(vmx_pages, l2_guest_code); - control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); + control = vmread(CPU_BASED_VM_EXEC_CONTROL); control |= CPU_BASED_USE_MSR_BITMAPS | CPU_BASED_USE_TSC_OFFSETTING; vmwrite(CPU_BASED_VM_EXEC_CONTROL, control); vmwrite(TSC_OFFSET, TSC_OFFSET_VALUE); vmlaunch(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); } else { struct svm_test_data *svm = data; diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index b2fc7f0b5b3c..c763f4cf0f62 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -112,11 +112,11 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) prepare_vmcs(vmx_pages, l2_guest_code); /* enable TSC offsetting and TSC scaling for L2 */ - control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); + control = vmread(CPU_BASED_VM_EXEC_CONTROL); control |= CPU_BASED_USE_MSR_BITMAPS | CPU_BASED_USE_TSC_OFFSETTING; vmwrite(CPU_BASED_VM_EXEC_CONTROL, control); - control = vmreadz(SECONDARY_VM_EXEC_CONTROL); + control = vmread(SECONDARY_VM_EXEC_CONTROL); control |= SECONDARY_EXEC_TSC_SCALING; vmwrite(SECONDARY_VM_EXEC_CONTROL, control); @@ -126,7 +126,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) /* launch L2 */ vmlaunch(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); /* check that L1's frequency still looks good */ check_tsc_freq(UCHECK_L1); diff --git a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c index e728f040916f..24056921110c 100644 --- a/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c +++ b/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c @@ -106,8 +106,8 @@ static void l1_vmx_code(struct vmx_pages *vmx) vmlaunch(); while (1) { - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); - GUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_INFO) & 0xff, UD_VECTOR); + GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); + GUEST_ASSERT_EQ(vmread(VM_EXIT_INTR_INFO) & 0xff, UD_VECTOR); vmresume(); } } diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index 8de6eb183670..500b09762555 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -59,12 +59,12 @@ void vmx_l2_guest_code(void) vmcall(); /* L1 has now set up a shadow VMCS for us. */ - GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); + GUEST_ASSERT(vmread(GUEST_RIP) == 0xc0ffee); GUEST_SYNC(10); - GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); + GUEST_ASSERT(vmread(GUEST_RIP) == 0xc0ffee); vmwrite(GUEST_RIP, 0xc0fffee); GUEST_SYNC(11); - GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0fffee); + GUEST_ASSERT(vmread(GUEST_RIP) == 0xc0fffee); vmwrite(GUEST_RIP, 0xc0ffffee); GUEST_SYNC(12); @@ -89,21 +89,21 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); vmlaunch(); GUEST_ASSERT(vmptrst() == vmx_pages->vmcs_gpa); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); /* Check that the launched state is preserved. */ GUEST_ASSERT(__vmlaunch()); vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_SYNC(7); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); - vmwrite(GUEST_RIP, vmreadz(GUEST_RIP) + 3); + vmwrite(GUEST_RIP, vmread(GUEST_RIP) + 3); vmwrite(SECONDARY_VM_EXEC_CONTROL, SECONDARY_EXEC_SHADOW_VMCS); vmwrite(VMCS_LINK_POINTER, vmx_pages->shadow_vmcs_gpa); @@ -116,18 +116,18 @@ static void vmx_l1_guest_code(struct vmx_pages *vmx_pages) vmwrite(GUEST_RIP, 0xc0ffee); GUEST_SYNC(9); - GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); + GUEST_ASSERT(vmread(GUEST_RIP) == 0xc0ffee); vmptrld(vmx_pages->vmcs_gpa); vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmptrld(vmx_pages->shadow_vmcs_gpa); - GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffffee); + GUEST_ASSERT(vmread(GUEST_RIP) == 0xc0ffffee); GUEST_ASSERT(__vmlaunch()); GUEST_ASSERT(__vmresume()); GUEST_SYNC(13); - GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffffee); + GUEST_ASSERT(vmread(GUEST_RIP) == 0xc0ffffee); GUEST_ASSERT(__vmlaunch()); GUEST_ASSERT(__vmresume()); } diff --git a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c index bb31a1ba8932..b2b796fd9ffb 100644 --- a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c +++ b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c @@ -32,7 +32,7 @@ void l1_guest_code_vmx(struct vmx_pages *vmx) vmlaunch(); /* L2 should triple fault after a triple fault event injected. */ - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c index fe29c869a235..d6373c414d18 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c @@ -43,25 +43,25 @@ static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa) /* Prepare the VMCS for L2 execution. */ prepare_vmcs(vmx_pages, l2_guest_code); - control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); + control = vmread(CPU_BASED_VM_EXEC_CONTROL); control |= CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; vmwrite(CPU_BASED_VM_EXEC_CONTROL, control); - control = vmreadz(SECONDARY_VM_EXEC_CONTROL); + control = vmread(SECONDARY_VM_EXEC_CONTROL); control |= SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES; vmwrite(SECONDARY_VM_EXEC_CONTROL, control); vmwrite(APIC_ACCESS_ADDR, vmx_pages->apic_access_gpa); /* Try to launch L2 with the memory-backed APIC-access address. */ - GUEST_SYNC(vmreadz(APIC_ACCESS_ADDR)); + GUEST_SYNC(vmread(APIC_ACCESS_ADDR)); vmlaunch(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); vmwrite(APIC_ACCESS_ADDR, high_gpa); /* Try to resume L2 with the unbacked APIC-access address. */ - GUEST_SYNC(vmreadz(APIC_ACCESS_ADDR)); + GUEST_SYNC(vmread(APIC_ACCESS_ADDR)); vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c index e578221da247..cda9e43e8599 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c @@ -38,7 +38,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) /* Prepare the VMCS for L2 execution. */ prepare_vmcs(vmx_pages, l2_guest_code); - control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); + control = vmread(CPU_BASED_VM_EXEC_CONTROL); control |= CPU_BASED_USE_MSR_BITMAPS; vmwrite(CPU_BASED_VM_EXEC_CONTROL, control); @@ -63,8 +63,8 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) * as KVM should force the APIC ID back to its default. */ vmlaunch(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); - vmwrite(GUEST_RIP, vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + vmwrite(GUEST_RIP, vmread(GUEST_RIP) + vmread(VM_EXIT_INSTRUCTION_LEN)); GUEST_ASSERT(rdmsr(MSR_IA32_APICBASE) & MSR_IA32_APICBASE_EXTD); /* @@ -91,7 +91,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) * is active. */ vmresume(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); GUEST_ASSERT(!(rdmsr(MSR_IA32_APICBASE) & MSR_IA32_APICBASE_EXTD)); xapic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_DM_FIXED | GOOD_IPI_VECTOR); diff --git a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c index 3017938fa993..bbd188949412 100644 --- a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c +++ b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c @@ -52,13 +52,13 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) * library hasn't enabled it. Because KVM selftests jump directly to * 64-bit mode, unrestricted guest support isn't required. */ - GUEST_ASSERT(!(vmreadz(CPU_BASED_VM_EXEC_CONTROL) & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS) || - !(vmreadz(SECONDARY_VM_EXEC_CONTROL) & SECONDARY_EXEC_UNRESTRICTED_GUEST)); + GUEST_ASSERT(!(vmread(CPU_BASED_VM_EXEC_CONTROL) & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS) || + !(vmread(SECONDARY_VM_EXEC_CONTROL) & SECONDARY_EXEC_UNRESTRICTED_GUEST)); vmlaunch(); /* L2 should triple fault after main() stuffs invalid guest state. */ - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c index 46e5ca997e74..7dcee11877ea 100644 --- a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c @@ -52,13 +52,13 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) pml4_pa = pml5[0] & PHYSICAL_PAGE_MASK; vmwrite(GUEST_CR3, pml4_pa); - guest_cr4 = vmreadz(GUEST_CR4); + guest_cr4 = vmread(GUEST_CR4); guest_cr4 &= ~X86_CR4_LA57; vmwrite(GUEST_CR4, guest_cr4); vmlaunch(); - exit_reason = vmreadz(VM_EXIT_REASON); + exit_reason = vmread(VM_EXIT_REASON); GUEST_ASSERT(exit_reason == EXIT_REASON_VMCALL); } diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index 06e74c72fe65..b9ee9d3c1da4 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -91,14 +91,14 @@ void l1_guest_code(struct vmx_pages *vmx_pages) return; vmlaunch(); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); - vmwrite(GUEST_RIP, vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); + vmwrite(GUEST_RIP, vmread(GUEST_RIP) + vmread(VM_EXIT_INSTRUCTION_LEN)); /* * Turn on PIN control and resume the guest */ vmwrite(PIN_BASED_VM_EXEC_CONTROL, - vmreadz(PIN_BASED_VM_EXEC_CONTROL) | PIN_BASED_VMX_PREEMPTION_TIMER); + vmread(PIN_BASED_VM_EXEC_CONTROL) | PIN_BASED_VMX_PREEMPTION_TIMER); vmwrite(VMX_PREEMPTION_TIMER_VALUE, PREEMPTION_TIMER_VALUE); @@ -121,7 +121,7 @@ void l1_guest_code(struct vmx_pages *vmx_pages) /* * Ensure the exit from L2 is due to preemption timer expiry */ - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_PREEMPTION_TIMER); + GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_PREEMPTION_TIMER); l1_tsc_deadline = l1_vmx_pt_start + (PREEMPTION_TIMER_VALUE << vmx_pt_rate); From 25158cac2d15ae2a3ac1f8791094df9c6522efeb Mon Sep 17 00:00:00 2001 From: Chaithanya Lagisetty Date: Mon, 7 Sep 2026 04:52:46 +0000 Subject: [PATCH 39/83] KVM: selftests: Fix the never-true negative UFFD delay check demand_paging_test parses the -d option with strtoul() and then asserts that the result is not negative: p.uffd_delay = strtoul(optarg, NULL, 0); TEST_ASSERT(p.uffd_delay >= 0, "A negative UFFD delay is not supported."); p.uffd_delay is a useconds_t, which is an unsigned type, so the comparison is always true and the assertion can never fire. GCC points this out with -Wtype-limits, which is enabled by -Wextra. As a result, "-d -1" is accepted and converted to a very large unsigned delay, causing each demand paging fault to sleep for an unexpectedly long time in usleep() instead of rejecting the argument up front. The return value of strtoul() is not validated either, so a non-numeric argument such as "-d abc" is silently treated as a zero delay. Use atoi_non_negative() instead. It rejects negative values, unparsable input, and trailing garbage. It is already used a few lines below for -v, and hexadecimal input keeps working because atoi_paranoid() also passes a base of 0 to strtol(). Fixes: 0119cb365c93 ("KVM: selftests: Add configurable demand paging delay") Signed-off-by: Chaithanya Lagisetty Tested-by: Gautam Menghani Reviewed-by: Gautam Menghani Link: https://patch.msgid.link/20260907045246.3783304-1-nagachaithanya9911@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/demand_paging_test.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/demand_paging_test.c b/tools/testing/selftests/kvm/demand_paging_test.c index f8b3d0b68830..619a0b2be45c 100644 --- a/tools/testing/selftests/kvm/demand_paging_test.c +++ b/tools/testing/selftests/kvm/demand_paging_test.c @@ -297,8 +297,7 @@ int main(int argc, char *argv[]) p.single_uffd = true; break; case 'd': - p.uffd_delay = strtoul(optarg, NULL, 0); - TEST_ASSERT(p.uffd_delay >= 0, "A negative UFFD delay is not supported."); + p.uffd_delay = atoi_non_negative("UFFD delay", optarg); break; case 'b': guest_percpu_mem_size = parse_size(optarg); From 2023782431927239c61330792904eff96ab9c1dc Mon Sep 17 00:00:00 2001 From: Chaithanya Lagisetty Date: Fri, 4 Sep 2026 05:20:33 +0000 Subject: [PATCH 40/83] KVM: selftests: Fix transposed calloc() arguments in the uffd helper uffd_setup_demand_paging() passes the element size as the first argument to calloc() and the element count as the second: uffd_desc->pipefds = calloc(sizeof(int), num_readers); calloc() expects the number of elements first and the size of each element second. The product, and therefore the allocation size, is the same either way, so this is not a functional change, but the arguments are misleading and GCC 14 diagnoses them with -Wcalloc-transposed-args, which is enabled by -Wextra. Swap the arguments to match the calloc() prototype. Signed-off-by: Chaithanya Lagisetty Reviewed-by: Gautam Menghani Link: https://patch.msgid.link/20260904052033.3648259-1-nagachaithanya9911@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/userfaultfd_util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/userfaultfd_util.c b/tools/testing/selftests/kvm/lib/userfaultfd_util.c index f7ce5a6ddcc2..49eff05b7d39 100644 --- a/tools/testing/selftests/kvm/lib/userfaultfd_util.c +++ b/tools/testing/selftests/kvm/lib/userfaultfd_util.c @@ -119,13 +119,13 @@ struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay, uffd_desc = malloc(sizeof(struct uffd_desc)); TEST_ASSERT(uffd_desc, "Failed to malloc uffd descriptor"); - uffd_desc->pipefds = calloc(sizeof(int), num_readers); + uffd_desc->pipefds = calloc(num_readers, sizeof(int)); TEST_ASSERT(uffd_desc->pipefds, "Failed to alloc pipes"); - uffd_desc->readers = calloc(sizeof(pthread_t), num_readers); + uffd_desc->readers = calloc(num_readers, sizeof(pthread_t)); TEST_ASSERT(uffd_desc->readers, "Failed to alloc reader threads"); - uffd_desc->reader_args = calloc(sizeof(struct uffd_reader_args), num_readers); + uffd_desc->reader_args = calloc(num_readers, sizeof(struct uffd_reader_args)); TEST_ASSERT(uffd_desc->reader_args, "Failed to alloc reader_args"); uffd_desc->num_readers = num_readers; From b57917fc2b26561a409894188ee55e7b6568941a Mon Sep 17 00:00:00 2001 From: Chaithanya Lagisetty Date: Tue, 1 Sep 2026 09:08:38 +0000 Subject: [PATCH 41/83] KVM: selftests: Check calloc() return in vcpu_get_reg_list() vcpu_get_reg_list() allocates reg_list with calloc() and immediately dereferences it via reg_list->n without checking for allocation failure, unlike every other allocation in this file which is guarded by TEST_ASSERT(). If calloc() returns NULL the test crashes with a NULL pointer dereference instead of a clean failure message. Add the missing TEST_ASSERT() check. Fixes: fd02029a9e01 ("KVM: selftests: Add aarch64 get-reg-list test") Signed-off-by: Chaithanya Lagisetty Link: https://patch.msgid.link/20260901090839.3338955-1-nagachaithanya9911@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/kvm_util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index cb2e7a98820a..980764e34208 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -1733,6 +1733,7 @@ struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vcpu *vcpu) TEST_ASSERT(ret == -1 && errno == E2BIG, "KVM_GET_REG_LIST n=0"); reg_list = calloc(1, sizeof(*reg_list) + reg_list_n.n * sizeof(__u64)); + TEST_ASSERT(reg_list, "Failed to allocate reg_list"); reg_list->n = reg_list_n.n; vcpu_ioctl(vcpu, KVM_GET_REG_LIST, reg_list); return reg_list; From 622a09f6deb53f8dd95d6b6482cf080997a0836a Mon Sep 17 00:00:00 2001 From: Gokul K Date: Sat, 8 Aug 2026 11:49:34 +0530 Subject: [PATCH 42/83] KVM: selftests: Fix the bounds of the SEV debug dst buffer check validate_dst() is passed a starting offset and a byte count, but bounds its loop with "i < nr_bytes", comparing the offset against the count rather than against offset + nr_bytes. Only calls that pass offset 0 check the region they were asked to check. Every other call checks too few bytes, and any call whose offset is >= the count returns without checking anything at all. Of the 1990 validate_dst() calls a full run makes per VM type, 740 (37%) check zero bytes, only the 29 zero-offset calls are correct, and 62% of the intended bytes are checked overall. The unaligned offsets the sweep deliberately picks to exercise partial chunks are precisely the ones that end up checking nothing. The bug is latent today: validate_buffers() runs first and compares all of src[] against dst[], so a genuine {en,de}crypt corruption is still caught. That is presumably why this went unnoticed. Bound the loop with offset + nr_bytes, and rename the parameter to offset so it cannot be mistaken for a length again. The caller already guarantees offset + nr_bytes <= BUFFER_SIZE, so the corrected loop stays in bounds. Fixes: 6edd35e77a42 ("KVM: selftests: Add a test to verify SEV {en,de}crypt debug ioctls") Signed-off-by: Gokul K Link: https://patch.msgid.link/20260808061934.400892-1-gokul02k@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/sev_dbg_test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c index eaa8201b937d..0277e38e3556 100644 --- a/tools/testing/selftests/kvm/x86/sev_dbg_test.c +++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c @@ -14,9 +14,11 @@ static u8 *data; static u8 src[BUFFER_SIZE] __aligned(PAGE_SIZE); static u8 dst[BUFFER_SIZE] __aligned(PAGE_SIZE); -static void validate_dst(int i, int nr_bytes, u8 pattern) +static void validate_dst(int offset, int nr_bytes, u8 pattern) { - for ( ; i < nr_bytes; i++) + int i; + + for (i = offset; i < offset + nr_bytes; i++) TEST_ASSERT(dst[i] == pattern, "Expected 0x%x at byte %u, got 0x%x", pattern, i, dst[i]); From 434c50e703c5aa24b0409a06f2ec1fc4040788b0 Mon Sep 17 00:00:00 2001 From: Gokul K Date: Tue, 11 Aug 2026 10:30:28 +0530 Subject: [PATCH 43/83] KVM: selftests: Free the VM when the SEV smoke test's guest completes test_sev() returns directly out of the UCALL_DONE case rather than leaving the loop, so the plain-SEV path skips the kvm_vm_free() at the end of the function. The SEV-ES path is unaffected; it breaks out of the loop and frees the VM correctly. main() invokes test_sev() once per supported SEV VM type, so a full run leaks a VM and its file descriptors. Nothing fails today because the process exits shortly afterwards, which is presumably why this was not noticed, but the leak also means the plain-SEV path never exercises VM teardown. Use a goto so UCALL_DONE joins the existing exit path. A plain break would only leave the switch statement and spin the loop again. Fixes: be250ff437fa ("KVM: selftests: Add a basic SEV smoke test") Signed-off-by: Gokul K Link: https://patch.msgid.link/20260811050029.530698-2-gokul02k@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/sev_smoke_test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c index bf27b6187afa..19f76f7d8ca6 100644 --- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c +++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c @@ -178,7 +178,7 @@ static void test_sev(void *guest_code, u32 type, u64 policy) case UCALL_SYNC: continue; case UCALL_DONE: - return; + goto done; case UCALL_ABORT: REPORT_GUEST_ASSERT(uc); default: @@ -187,6 +187,7 @@ static void test_sev(void *guest_code, u32 type, u64 policy) } } +done: kvm_vm_free(vm); } From ebb7027078965527d920360e7835a14eac6f835e Mon Sep 17 00:00:00 2001 From: Gokul K Date: Tue, 11 Aug 2026 10:30:29 +0530 Subject: [PATCH 44/83] KVM: selftests: Free the VM when NX hugepage disabling is denied run_test() returns as soon as it has confirmed that disabling NX huge pages fails with -EPERM, without freeing the VM created a few lines earlier. Jump to the kvm_vm_free() at the end of the function instead, matching the pattern used for the SEV smoke test in the previous patch. Fixes: b774da3f2e57 ("KVM: selftests: Test disabling NX hugepages on a VM") Signed-off-by: Gokul K Link: https://patch.msgid.link/20260811050029.530698-3-gokul02k@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nx_huge_pages_test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c b/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c index 70950067b989..e3b20033a884 100644 --- a/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c +++ b/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c @@ -120,7 +120,7 @@ void run_test(int reclaim_period_ms, bool disable_nx_huge_pages, } else { TEST_ASSERT(r == -1 && errno == EPERM, "This process should not have permission to disable NX huge pages"); - return; + goto done; } } @@ -213,6 +213,7 @@ void run_test(int reclaim_period_ms, bool disable_nx_huge_pages, check_2m_page_count(vm, disable_nx_huge_pages ? 3 : 2); check_split_count(vm, 0); +done: kvm_vm_free(vm); } From be3c6888e1ab29d1d2a36af224eaf7739985c414 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 17:16:19 -0700 Subject: [PATCH 45/83] KVM: selftests: Account for kernel's off-by-one bug in NUMA node syscalls Add and use MAXNODE_FOR_MASK() to compute the "correct" maxnode value that is passed to various NUMA-related syscalls, e.g. get_mempolicy(), mbind(), migrate_pages(), etc. In quotes, because the kernel has an undocumented, longstanding off-by-one bug that requires userspace to specify the number of bits plus one, i.e. the max node plus two. The kernel bug has been known since 2007[*]: : And this is I think the reason why we can't change this now. I assume : numactl allocates 1024 bits (0 to 1023) and passes 1025 to make sure all : 1024 bits are processed. If we change it now, kernel will process 1025 : bits (0 to 1024) and overflow the allocated bitmask. If it happens to be : at the border of mmaped vma, it's a segfault... But unfortunately the manpages haven't yet been updated, e.g. : The maxnode argument is the maximum node number in the bit mask plus one Link: https://lore.kernel.org/all/63ccc890-fd57-118b-5997-e0259f507d28@suse.cz[*] Reviewed-by: Shivank Garg Link: https://patch.msgid.link/20260903001625.2792367-2-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/guest_memfd_test.c | 2 +- tools/testing/selftests/kvm/include/numaif.h | 11 +++++++++++ tools/testing/selftests/kvm/x86/xapic_ipi_test.c | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index 2233d871a38f..cd5df88bc642 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -80,7 +80,7 @@ static void test_mbind(int fd, size_t total_size) { const unsigned long nodemask_0 = 1; /* nid: 0 */ unsigned long nodemask = 0; - unsigned long maxnode = BITS_PER_TYPE(nodemask); + unsigned long maxnode = MAXNODE_FOR_MASK(nodemask); int policy; char *mem; int ret; diff --git a/tools/testing/selftests/kvm/include/numaif.h b/tools/testing/selftests/kvm/include/numaif.h index 29572a6d789c..71f261eafc90 100644 --- a/tools/testing/selftests/kvm/include/numaif.h +++ b/tools/testing/selftests/kvm/include/numaif.h @@ -6,6 +6,7 @@ #include +#include #include #include "kvm_syscalls.h" @@ -30,6 +31,16 @@ KVM_SYSCALL_DEFINE(mbind, 6, void *, addr, unsigned long, size, int, mode, const unsigned long *, nodemask, unsigned long, maxnode, unsigned int, flags); +/* + * Calculate the @maxnode param for the above syscalls given the mask that will + * be passed to the kernel, to account for a longstanding off-by-one bug in the + * kernel that isn't properly documented in the manpages. The manpages say + * that @maxnode is "the maximum node ID plus one", but the kernel's actual + * behavior is "the number of bits in the mask plus one", i.e. "the maximum + * node ID plus two". + */ +#define MAXNODE_FOR_MASK(mask) (BITS_PER_TYPE(mask) + 1) + static inline int get_max_numa_node(void) { struct dirent *de; diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index 469e3ab16460..1ddcf95d7fe4 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -248,7 +248,7 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, delay_usecs); /* Get set of first 64 numa nodes available */ - kvm_get_mempolicy(NULL, &nodemask, sizeof(nodemask) * 8, + kvm_get_mempolicy(NULL, &nodemask, MAXNODE_FOR_MASK(nodemask), 0, MPOL_F_MEMS_ALLOWED); fprintf(stderr, "Numa nodes found amongst first %lu possible nodes " From 8883d2410c45bb2882b7ba44b38a70cfef0b84f9 Mon Sep 17 00:00:00 2001 From: Shivank Garg Date: Wed, 2 Sep 2026 17:16:20 -0700 Subject: [PATCH 46/83] KVM: selftests: Fix maxnode argument to migrate_pages() in xapic_ipi_test Use MAXNODE_FOR_MASK() to compute the @maxnode argument when migrating pages in the xAPIC IPI test, as the current code incorrectly passes the number of bytes, not the number of bits, in the mask, and also fails to account for the kernel's off-by-one bug. I.e. the test sets maxnode to 8, and so the kernel only checks node IDs 0-6 even though the nodemask covers node IDs 0-63. Fixes: 678e90a349a4 ("KVM: selftests: Test IPI to halted vCPU in xAPIC while backing page moves") Signed-off-by: Shivank Garg [sean: use MAXNODE_FOR_MASK(), rewrite changelog] Link: https://patch.msgid.link/20260903001625.2792367-3-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/xapic_ipi_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index 1ddcf95d7fe4..6be11c1c725c 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -291,7 +291,7 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, * KVM_CREATE_VCPU ioctl. If that assumption ever changes this * test may break or give a false positive signal. */ - pages_not_moved = migrate_pages(0, sizeof(nodemasks[from]), + pages_not_moved = migrate_pages(0, MAXNODE_FOR_MASK(nodemasks[from]), &nodemasks[from], &nodemasks[to]); if (pages_not_moved < 0) From d2dde5c387fc9ae1b9364c1bc096bac5d419756f Mon Sep 17 00:00:00 2001 From: Shivank Garg Date: Wed, 2 Sep 2026 17:16:21 -0700 Subject: [PATCH 47/83] KVM: selftests: use BITS_PER_TYPE() for NUMA masks Replace the open-coded sizeof() * 8 calculations in do_migrations() with BITS_PER_TYPE(). No functional change intended. Signed-off-by: Shivank Garg Link: https://patch.msgid.link/20260903001625.2792367-4-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/xapic_ipi_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index 6be11c1c725c..9d1dfad4efa6 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -233,7 +233,7 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, { long pages_not_moved; unsigned long nodemask = 0; - unsigned long nodemasks[sizeof(nodemask) * 8]; + unsigned long nodemasks[BITS_PER_TYPE(nodemask)]; int nodes = 0; time_t start_time, last_update, now; time_t interval_secs = 1; @@ -253,13 +253,13 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, fprintf(stderr, "Numa nodes found amongst first %lu possible nodes " "(each 1-bit indicates node is present): %#lx\n", - sizeof(nodemask) * 8, nodemask); + BITS_PER_TYPE(nodemask), nodemask); /* Init array of masks containing a single-bit in each, one for each * available node. migrate_pages called below requires specifying nodes * as bit masks. */ - for (i = 0, bit = 1; i < sizeof(nodemask) * 8; i++, bit <<= 1) { + for (i = 0, bit = 1; i < BITS_PER_TYPE(nodemask); i++, bit <<= 1) { if (nodemask & bit) { nodemasks[nodes] = nodemask & bit; nodes++; From 3a2a37d2d48e6844dde61032faf130400fad5312 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 17:16:22 -0700 Subject: [PATCH 48/83] KVM: selftests: Compute node masks on-demand in xAPIC IPI test Compute the node masks for the source (from) and destination (to) NUMA nodes in the xAPIC IPI test instead of pre-filling an array of masks with node per mask. Computing the mask on-demand is technically slower, but doesn't require a large-ish on-stack array, and more importantly allows the test to use a generic "get next NUMA node" API without having to commit all of KVM selftests to using a large array of single-bit nodemasks. Implement said API as a common KVM NUMA API so that it can be used by other tests, e.g. in guest_memfd tests. Deliberately make @from "exclusive" as the anticipated usage in KVM selftests is to select the next, *different* node, i.e. so that users don't have to copy+paste code to assert that the found node is different than the starting node. The obvious downside is that implementing the exclusive logic forces callers to pass -1 instead of 0 when the goal is to find the first node in the mask, but that arguably yields more intuitive code anyways. From an overall test functionality/coverage perspective, no functional change intended (the walking pattern of node migration should be unchanged). Reviewed-by: Shivank Garg Link: https://patch.msgid.link/20260903001625.2792367-5-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/numaif.h | 21 +++++++++++- .../selftests/kvm/x86/xapic_ipi_test.c | 32 ++++++------------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/kvm/include/numaif.h b/tools/testing/selftests/kvm/include/numaif.h index 71f261eafc90..299dddff2729 100644 --- a/tools/testing/selftests/kvm/include/numaif.h +++ b/tools/testing/selftests/kvm/include/numaif.h @@ -6,7 +6,7 @@ #include -#include +#include #include #include "kvm_syscalls.h" @@ -41,6 +41,25 @@ KVM_SYSCALL_DEFINE(mbind, 6, void *, addr, unsigned long, size, int, mode, */ #define MAXNODE_FOR_MASK(mask) (BITS_PER_TYPE(mask) + 1) +/* + * Return the node ID of the next NUMA node in the mask, starting at @from+1. + * Guarantees a node is found, and that the found node is not @from. Pass -1 + * to find the first node in the mask. + */ +static inline int kvm_get_next_numa_node(unsigned long nodemask, int from) +{ + const unsigned long nr_bits = BITS_PER_TYPE(nodemask); + int to; + + to = find_next_bit(&nodemask, nr_bits, from + 1); + if (to == nr_bits) + to = find_next_bit(&nodemask, nr_bits, 0); + + TEST_ASSERT(to != nr_bits && to != from, + "Unabled to find second NUMA node (from = %d, to = %d)", from, to); + return to; +} + static inline int get_max_numa_node(void) { struct dirent *de; diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index 9d1dfad4efa6..7144ad833ae0 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -233,13 +233,10 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, { long pages_not_moved; unsigned long nodemask = 0; - unsigned long nodemasks[BITS_PER_TYPE(nodemask)]; int nodes = 0; time_t start_time, last_update, now; time_t interval_secs = 1; - int i; int from, to; - unsigned long bit; u64 hlt_count; u64 wake_count; u64 ipis_sent; @@ -255,24 +252,15 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, "(each 1-bit indicates node is present): %#lx\n", BITS_PER_TYPE(nodemask), nodemask); - /* Init array of masks containing a single-bit in each, one for each - * available node. migrate_pages called below requires specifying nodes - * as bit masks. - */ - for (i = 0, bit = 1; i < BITS_PER_TYPE(nodemask); i++, bit <<= 1) { - if (nodemask & bit) { - nodemasks[nodes] = nodemask & bit; - nodes++; - } - } - + nodes = __builtin_popcountl(nodemask); TEST_ASSERT(nodes > 1, "Did not find at least 2 numa nodes. Can't do migration"); fprintf(stderr, "Migrating amongst %d nodes found\n", nodes); - from = 0; - to = 1; + from = kvm_get_next_numa_node(nodemask, -1); + to = kvm_get_next_numa_node(nodemask, from); + start_time = time(NULL); last_update = start_time; @@ -281,6 +269,9 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, wake_count = data->wake_count; while ((int)(time(NULL) - start_time) < run_secs) { + unsigned long from_mask = BIT(from); + unsigned long to_mask = BIT(to); + data->migrations_attempted++; /* @@ -291,9 +282,8 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, * KVM_CREATE_VCPU ioctl. If that assumption ever changes this * test may break or give a false positive signal. */ - pages_not_moved = migrate_pages(0, MAXNODE_FOR_MASK(nodemasks[from]), - &nodemasks[from], - &nodemasks[to]); + pages_not_moved = migrate_pages(0, MAXNODE_FOR_MASK(from_mask), + &from_mask, &to_mask); if (pages_not_moved < 0) fprintf(stderr, "migrate_pages failed, errno=%d\n", errno); @@ -305,9 +295,7 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, data->migrations_completed++; from = to; - to++; - if (to == nodes) - to = 0; + to = kvm_get_next_numa_node(nodemask, from); now = time(NULL); if (((now - start_time) % interval_secs == 0) && From 0be096bbc9cc7734b9aa82f62b4a5b44bd884540 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 17:16:23 -0700 Subject: [PATCH 49/83] KVM: selftests: Add common helper to get mask+number of usable memory NUMA nodes Extract and slightly adopt the xAPIC IPI test's logic for getting the mask and number of usable memory NUMA nodes into a common helper. To allow for friendlier behavior when the underlying kernel doesn't support NUMA, or the test was run without sufficient permissions, zero out the mask and return "zero nodes" if get_mempolicy() fails with ENOSYS or EPERM respectively. I.e. allow tests to skip (sub)tests instead of throwing an assert. Suggested-by: Shivank Garg Reviewed-by: Shivank Garg Link: https://patch.msgid.link/20260903001625.2792367-6-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/numaif.h | 13 +++++++++++++ tools/testing/selftests/kvm/x86/xapic_ipi_test.c | 5 +---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/kvm/include/numaif.h b/tools/testing/selftests/kvm/include/numaif.h index 299dddff2729..0945500c6322 100644 --- a/tools/testing/selftests/kvm/include/numaif.h +++ b/tools/testing/selftests/kvm/include/numaif.h @@ -41,6 +41,19 @@ KVM_SYSCALL_DEFINE(mbind, 6, void *, addr, unsigned long, size, int, mode, */ #define MAXNODE_FOR_MASK(mask) (BITS_PER_TYPE(mask) + 1) +static inline int kvm_get_numa_memory_nodes(unsigned long *nodemask) +{ + int r; + + *nodemask = 0; + + r = get_mempolicy(NULL, nodemask, MAXNODE_FOR_MASK(*nodemask), 0, + MPOL_F_MEMS_ALLOWED); + TEST_ASSERT(!r || errno == ENOSYS || errno == EPERM, + "Unexpected get_mempolicy() failure"); + return __builtin_popcountl(*nodemask); +} + /* * Return the node ID of the next NUMA node in the mask, starting at @from+1. * Guarantees a node is found, and that the found node is not @from. Pass -1 diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index 7144ad833ae0..42c601617f5a 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -244,15 +244,12 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, fprintf(stderr, "Calling migrate_pages every %d microseconds\n", delay_usecs); - /* Get set of first 64 numa nodes available */ - kvm_get_mempolicy(NULL, &nodemask, MAXNODE_FOR_MASK(nodemask), - 0, MPOL_F_MEMS_ALLOWED); + nodes = kvm_get_numa_memory_nodes(&nodemask); fprintf(stderr, "Numa nodes found amongst first %lu possible nodes " "(each 1-bit indicates node is present): %#lx\n", BITS_PER_TYPE(nodemask), nodemask); - nodes = __builtin_popcountl(nodemask); TEST_ASSERT(nodes > 1, "Did not find at least 2 numa nodes. Can't do migration"); From 92ed4d5c78fdfcbec3f6b64b194b5202f868ef4c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 17:16:24 -0700 Subject: [PATCH 50/83] KVM: selftests: Automatically run xAPIC IPI migration test when possible Rework the interface to the xAPIC IPI test to automatically run the migration testcase if at least two NUMA nodes are found. The test exists specifically to validate KVM's handling of migration of the APIC backing page, i.e. forcing end users to opt-in to running the test in migration mode largely defeats the purpose of the test. Run both the "sleeping" and "migration" testcases by default, e.g. so that the more basic testcase will fail if KVM completely breaks IPI delivery. Keep the -m / migration parameter so that infrastructure that wants to specifically test the migration case can do so and not get false passes. Reviewed-by: Shivank Garg Link: https://patch.msgid.link/20260903001625.2792367-7-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/x86/xapic_ipi_test.c | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index 42c601617f5a..c5b506eb5e7f 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -245,14 +245,13 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, delay_usecs); nodes = kvm_get_numa_memory_nodes(&nodemask); + TEST_ASSERT(nodes > 1, + "NUMA nodes disappeared? nodemask = 0x%lx", nodemask); fprintf(stderr, "Numa nodes found amongst first %lu possible nodes " "(each 1-bit indicates node is present): %#lx\n", BITS_PER_TYPE(nodemask), nodemask); - TEST_ASSERT(nodes > 1, - "Did not find at least 2 numa nodes. Can't do migration"); - fprintf(stderr, "Migrating amongst %d nodes found\n", nodes); from = kvm_get_next_numa_node(nodemask, -1); @@ -351,26 +350,17 @@ void get_cmdline_args(int argc, char *argv[], int *run_secs, } } -int main(int argc, char *argv[]) +static void test_xapic_ipi(int run_secs, int delay_usecs, bool migrate) { int wait_secs; const int max_halter_wait = 10; - int run_secs = 0; - int delay_usecs = 0; struct test_data_page *data; gva_t test_data_page_gva; - bool migrate = false; pthread_t threads[2]; struct thread_params params[2]; struct kvm_vm *vm; u64 *pipis_rcvd; - get_cmdline_args(argc, argv, &run_secs, &migrate, &delay_usecs); - if (run_secs <= 0) - run_secs = DEFAULT_RUN_SECS; - if (delay_usecs <= 0) - delay_usecs = DEFAULT_DELAY_USECS; - vm = vm_create_with_one_vcpu(¶ms[0].vcpu, halter_guest_code); vm_install_exception_handler(vm, IPI_VECTOR, guest_ipi_handler); @@ -458,5 +448,29 @@ int main(int argc, char *argv[]) kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + bool force_migrate = false; + unsigned long nodemask; + int run_secs = 0; + int delay_usecs = 0; + + get_cmdline_args(argc, argv, &run_secs, &force_migrate, &delay_usecs); + if (run_secs <= 0) + run_secs = DEFAULT_RUN_SECS; + if (delay_usecs <= 0) + delay_usecs = DEFAULT_DELAY_USECS; + + if (!force_migrate) + test_xapic_ipi(run_secs, delay_usecs, false); + + if (kvm_get_numa_memory_nodes(&nodemask) > 1) + test_xapic_ipi(run_secs, delay_usecs, true); + else + TEST_ASSERT(!force_migrate, + "Did not find at least 2 numa nodes. Can't do migration"); + return 0; } From d5aa06fbfec28a2acbb03bbba361ad9c1a90eb73 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 2 Sep 2026 17:16:25 -0700 Subject: [PATCH 51/83] KVM: selftests: Skip xAPIC IPI migration test when forced but unsupported Skip the xAPIC IPI migration test instead of failing outright if migration is forced by the user, but is unsupported due to lack of NUMA support. Failing the test is arguably "fine" for opt-in behavior, but in practice doesn't allow running the test in forced-migration mode in any kind of automated test environment. Reviewed-by: Shivank Garg Link: https://patch.msgid.link/20260903001625.2792367-8-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/xapic_ipi_test.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index c5b506eb5e7f..2362bf272e80 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -469,8 +469,9 @@ int main(int argc, char *argv[]) if (kvm_get_numa_memory_nodes(&nodemask) > 1) test_xapic_ipi(run_secs, delay_usecs, true); else - TEST_ASSERT(!force_migrate, - "Did not find at least 2 numa nodes. Can't do migration"); + __TEST_REQUIRE(!force_migrate, + "Need at least 2 NUMA nodes to do migration (nodemask = 0x%lx)", + nodemask); return 0; } From d80ddf303f3a31440de93572407c68d0d7493283 Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:06 +0800 Subject: [PATCH 52/83] KVM: selftests: Add Zhaoxin CPU detection support Add helpers to detect Zhaoxin CPUs using the "CentaurHauls" and " Shanghai " vendor strings. KVM handles Zhaoxin CPUs with its VMX backend. Add host_cpu_is_intel_compatible for selftests that exercise behavior shared by Intel and Zhaoxin CPUs, while retaining host_cpu_is_zhaoxin for Zhaoxin-specific behavior. Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-2-ewanhai-oc@zhaoxin.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/processor.h | 8 ++++++++ tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 282b8aaff799..a86bde201ed8 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -23,6 +23,8 @@ extern bool host_cpu_is_intel; extern bool host_cpu_is_amd; extern bool host_cpu_is_hygon; extern bool host_cpu_is_amd_compatible; +extern bool host_cpu_is_zhaoxin; +extern bool host_cpu_is_intel_compatible; extern u64 guest_tsc_khz; extern struct kvm_mmu guest_mmu; @@ -782,6 +784,12 @@ static inline bool this_cpu_is_hygon(void) return this_cpu_vendor_string_is("HygonGenuine"); } +static inline bool this_cpu_is_zhaoxin(void) +{ + return this_cpu_vendor_string_is("CentaurHauls") || + this_cpu_vendor_string_is(" Shanghai "); +} + static inline u32 __this_cpu_has(u32 function, u32 index, u8 reg, u8 lo, u8 hi) { u32 gprs[4]; diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index 479e9a481687..51d891131f3e 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -26,6 +26,8 @@ bool host_cpu_is_amd; bool host_cpu_is_intel; bool host_cpu_is_hygon; bool host_cpu_is_amd_compatible; +bool host_cpu_is_zhaoxin; +bool host_cpu_is_intel_compatible; bool is_forced_emulation_enabled; u64 guest_tsc_khz; struct kvm_mmu guest_mmu; @@ -822,6 +824,8 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus) sync_global_to_guest(vm, host_cpu_is_amd); sync_global_to_guest(vm, host_cpu_is_hygon); sync_global_to_guest(vm, host_cpu_is_amd_compatible); + sync_global_to_guest(vm, host_cpu_is_zhaoxin); + sync_global_to_guest(vm, host_cpu_is_intel_compatible); sync_global_to_guest(vm, is_forced_emulation_enabled); sync_global_to_guest(vm, pmu_errata_mask); @@ -1447,6 +1451,8 @@ void kvm_selftest_arch_init(void) host_cpu_is_amd = this_cpu_is_amd(); host_cpu_is_hygon = this_cpu_is_hygon(); host_cpu_is_amd_compatible = host_cpu_is_amd || host_cpu_is_hygon; + host_cpu_is_zhaoxin = this_cpu_is_zhaoxin(); + host_cpu_is_intel_compatible = host_cpu_is_intel || host_cpu_is_zhaoxin; is_forced_emulation_enabled = kvm_is_forced_emulation_enabled(); kvm_init_pmu_errata(); From 9bb23b9e85ea7e8f8ef65d31cee15aa44173cb97 Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:07 +0800 Subject: [PATCH 53/83] KVM: selftests: Use host_cpu_is_intel_compatible for mediated PMU kvm_is_mediated_pmu_enabled() returns kvm_amd's enable_mediated_pmu module parameter unless the host CPU is Intel. Zhaoxin CPUs implement VMX-compatible virtualization and are handled by KVM's VMX backend, so on Zhaoxin hosts the helper wrongly reports kvm_amd's parameter. Gate the check on host_cpu_is_intel_compatible so that Zhaoxin CPUs, like Intel CPUs, query kvm_intel's enable_mediated_pmu instead. This does not change behavior on Intel, and AMD and other non-Intel- compatible CPUs still fall back to kvm_amd's parameter. Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-3-ewanhai-oc@zhaoxin.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index a86bde201ed8..7579e692bc2b 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1454,7 +1454,7 @@ static inline bool kvm_is_pmu_enabled(void) static inline bool kvm_is_mediated_pmu_enabled(void) { - if (host_cpu_is_intel) + if (host_cpu_is_intel_compatible) return get_kvm_intel_param_bool("enable_mediated_pmu"); return get_kvm_amd_param_bool("enable_mediated_pmu"); From 54edcc375a46613bc6b14fa5c310be68ea3446b4 Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:08 +0800 Subject: [PATCH 54/83] KVM: selftests: Add TDP detection for Zhaoxin CPUs Update the kvm_is_tdp_enabled() function to include Zhaoxin CPUs by querying checking "Intel compatible" when checking for EPT (Extended Page Tables) support. This ensures that Zhaoxin CPUs use the appropriate TDP (Two-Dimensional Paging) parameter check, similar to Intel CPUs. Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-4-ewanhai-oc@zhaoxin.com [sean: massage changelog] Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/x86/processor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index 51d891131f3e..8374afd08baf 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -160,7 +160,7 @@ static void sregs_dump(FILE *stream, struct kvm_sregs *sregs, u8 indent) bool kvm_is_tdp_enabled(void) { - if (host_cpu_is_intel) + if (host_cpu_is_intel_compatible) return get_kvm_intel_param_bool("ept"); else return get_kvm_amd_param_bool("npt"); From 7610139e1ab7cc143491fb4b176a477e0001d134 Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:09 +0800 Subject: [PATCH 55/83] KVM: selftests: Allow running invalid guest state test on Zhaoxin CPUs The vmx_exception_with_invalid_guest_state test currently requires an Intel CPU. However, Zhaoxin CPUs also support VMX and the relevant invalid guest state handling under KVM. Update the test requirement to allow execution on Zhaoxin platforms in addition to Intel, enabling broader coverage of VMX-related error paths. Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-5-ewanhai-oc@zhaoxin.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c index 2cae86d9d5e2..08a0caa9c52d 100644 --- a/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c +++ b/tools/testing/selftests/kvm/x86/vmx_exception_with_invalid_guest_state.c @@ -109,7 +109,7 @@ int main(int argc, char *argv[]) struct kvm_vcpu *vcpu; struct kvm_vm *vm; - TEST_REQUIRE(host_cpu_is_intel); + TEST_REQUIRE(host_cpu_is_intel_compatible); TEST_REQUIRE(!kvm_is_unrestricted_guest_enabled()); vm = vm_create_with_one_vcpu(&vcpu, guest_code); From 2625a21954c46d0a9c8a926127c95a8178c80a54 Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:10 +0800 Subject: [PATCH 56/83] KVM: selftests: Add Zhaoxin CPU support in x86's "fix" hypercall test Zhaoxin CPUs use VMCALL as the native hypercall instruction. Use host_cpu_is_intel_compatible so that fix_hypercall_test selects the VMX hypercall path on Zhaoxin CPUs. Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-6-ewanhai-oc@zhaoxin.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/fix_hypercall_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c index 4931ec22768e..f11fcc520f24 100644 --- a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c +++ b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c @@ -48,7 +48,7 @@ static void guest_main(void) const u8 *other_hypercall_insn; u64 ret; - if (host_cpu_is_intel) { + if (host_cpu_is_intel_compatible) { native_hypercall_insn = vmx_vmcall; other_hypercall_insn = svm_vmmcall; } else if (host_cpu_is_amd_compatible) { From 658d82c07e78eaa153991ca72326637a6cc6ce92 Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:12 +0800 Subject: [PATCH 57/83] KVM: selftests: Extend Intel MSR_IA32_UCODE_REV quirk to Zhaoxin Zhaoxin CPUs use KVM's VMX backend and therefore observe the same KVM-defined initial value as Intel vCPUs. Update feature_msrs_test to expect 0x100000000ULL for Zhaoxin vCPUs. Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-8-ewanhai-oc@zhaoxin.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/feature_msrs_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/feature_msrs_test.c b/tools/testing/selftests/kvm/x86/feature_msrs_test.c index 158550701771..d56d0ddb3a57 100644 --- a/tools/testing/selftests/kvm/x86/feature_msrs_test.c +++ b/tools/testing/selftests/kvm/x86/feature_msrs_test.c @@ -59,7 +59,7 @@ static void test_feature_msr(u32 msr) * but initializes the vCPU's revision ID to an arbitrary value. */ if (msr == MSR_IA32_UCODE_REV) - reset_value = host_cpu_is_intel ? 0x100000000ULL : 0x01000065; + reset_value = host_cpu_is_intel_compatible ? 0x100000000ULL : 0x01000065; /* * For quirked MSRs, KVM's ABI is to initialize the vCPU's value to the From 1a0abfad6c8096dfb00aa5a764d92819ff749103 Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:13 +0800 Subject: [PATCH 58/83] KVM: selftests: Add Zhaoxin CPU support to nested exceptions test When vectoring an injected #SS through the empty L2 IDT, Zhaoxin CPUs report the resulting #GP with the IDT bit set and EXT clear. Zhaoxin sets EXT only for interrupt delivery and selected guest-mode events; #SS delivery is not one of those cases. Expect error code 0x62 when running the test on Zhaoxin CPUs. Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-9-ewanhai-oc@zhaoxin.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nested_exceptions_test.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index d23e24f35790..f47432526ea1 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -38,6 +38,7 @@ */ #define GP_ERROR_CODE_AMD ((SS_VECTOR * 8) | ERROR_CODE_IDT_FLAG) #define GP_ERROR_CODE_INTEL ((SS_VECTOR * 8) | ERROR_CODE_IDT_FLAG | ERROR_CODE_EXT_FLAG) +#define GP_ERROR_CODE_ZHAOXIN ((SS_VECTOR * 8) | ERROR_CODE_IDT_FLAG) /* * Intel and AMD both shove '0' into the error code on #DF, regardless of what @@ -142,7 +143,8 @@ static void l1_vmx_code(struct vmx_pages *vmx) */ vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS_GP_DF); vmx_run_l2(l2_ss_pending_test, SS_VECTOR, (u16)SS_ERROR_CODE); - vmx_run_l2(l2_ss_injected_gp_test, GP_VECTOR, GP_ERROR_CODE_INTEL); + vmx_run_l2(l2_ss_injected_gp_test, GP_VECTOR, + host_cpu_is_zhaoxin ? GP_ERROR_CODE_ZHAOXIN : GP_ERROR_CODE_INTEL); vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS_DF); vmx_run_l2(l2_ss_injected_df_test, DF_VECTOR, DF_ERROR_CODE); From 0d5c14800de007ee45fd76008e4a115f170fa88d Mon Sep 17 00:00:00 2001 From: Frank Zhu Date: Thu, 10 Sep 2026 19:54:14 +0800 Subject: [PATCH 59/83] KVM: selftests: Check for APIC access virtualization support in said test Not all CPUs that support VMX necessarily support the "virtualize APIC accesses" secondary processor-based VM-execution control. For example, some Zhaoxin CPUs lack this feature. Add a capability check before running the test to properly skip on unsupported hardware instead of failing. Carve out the guts of kvm_cpu_has_ept() into a generic helper and use said helper to provide kvm_cpu_has_vmx_apic_access_virt(). Signed-off-by: Frank Zhu Link: https://patch.msgid.link/20260910115414.3015260-10-ewanhai-oc@zhaoxin.com [sean: add a helper instead of copy+pasting kvm_cpu_has_ept()] Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/vmx.h | 13 ++++++++++++- tools/testing/selftests/kvm/lib/x86/vmx.c | 12 ++++++------ .../selftests/kvm/x86/vmx_apic_access_test.c | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index ca67a618b68f..8fd97434eee5 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -503,7 +503,18 @@ void load_vmcs(struct vmx_pages *vmx); bool ept_1g_pages_supported(void); -bool kvm_cpu_has_ept(void); +bool kvm_cpu_has_secondary_exec_control(u32 ctrl); + +static inline bool kvm_cpu_has_ept(void) +{ + return kvm_cpu_has_secondary_exec_control(SECONDARY_EXEC_ENABLE_EPT); +} + +static inline bool kvm_cpu_has_vmx_virtualize_apic_accesses(void) +{ + return kvm_cpu_has_secondary_exec_control(SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES); +} + void vm_enable_ept(struct kvm_vm *vm); void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm *vm); diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 6a53c8ce3b3c..5a06746ebea4 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -422,19 +422,19 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip) init_vmcs_guest_state(guest_rip, vmx->stack); } -bool kvm_cpu_has_ept(void) +bool kvm_cpu_has_secondary_exec_control(u32 ctrl) { - u64 ctrl; + u64 ctrl_msr; if (!kvm_cpu_has(X86_FEATURE_VMX)) return false; - ctrl = kvm_get_feature_msr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32; - if (!(ctrl & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS)) + ctrl_msr = kvm_get_feature_msr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32; + if (!(ctrl_msr & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS)) return false; - ctrl = kvm_get_feature_msr(MSR_IA32_VMX_PROCBASED_CTLS2) >> 32; - return ctrl & SECONDARY_EXEC_ENABLE_EPT; + ctrl_msr = kvm_get_feature_msr(MSR_IA32_VMX_PROCBASED_CTLS2) >> 32; + return ctrl_msr & ctrl; } void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm *vm) diff --git a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c index d6373c414d18..1d7a529c8297 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c @@ -77,7 +77,7 @@ int main(int argc, char *argv[]) struct kvm_vcpu *vcpu; struct kvm_vm *vm; - TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); + TEST_REQUIRE(kvm_cpu_has_vmx_virtualize_apic_accesses()); vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); From dd572ec94640b07e495237d2a6afd3d29a4c2a7c Mon Sep 17 00:00:00 2001 From: Gokul K Date: Fri, 18 Sep 2026 17:06:30 +0530 Subject: [PATCH 60/83] KVM: selftests: Delete the declaration of vcpu_set_cpuid_maxphyaddr() vcpu_set_cpuid_maxphyaddr() is declared in include/x86/processor.h and defined nowhere. The declaration is the only mention of the name in tools/testing/selftests, so nothing has ever been able to call it. No Fixes: tag: a prototype for a function that does not exist cannot have regressed anything, and there is nothing to backport. No functional change intended. Signed-off-by: Gokul K Link: https://patch.msgid.link/20260918113631.634233-3-gokul02k@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/processor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 7579e692bc2b..ffe95ce1ad49 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1180,7 +1180,6 @@ static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu) void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu, struct kvm_x86_cpu_property property, u32 value); -void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, u8 maxphyaddr); void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, u32 function); From 3069f4036dff6a36936c9f43f38c408db58bc846 Mon Sep 17 00:00:00 2001 From: Gokul K Date: Fri, 18 Sep 2026 11:14:20 +0530 Subject: [PATCH 61/83] KVM: selftests: Move test_read() into elf.c and delete lib/io.c lib/io.c has outlived its purpose. test_read() has exactly one caller, lib/elf.c; test_write() has none at all; and test_seq_read() is a declaration in test_util.h with no definition anywhere in the tree. Both surviving helpers open with TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count); which can never fire, because @count is a size_t. Building with -Wextra points this out: lib/io.c:51:27: warning: comparison of unsigned expression in '>= 0' is always true [-Wtype-limits] lib/io.c:128:27: warning: comparison of unsigned expression in '>= 0' is always true [-Wtype-limits] The comment above each assertion is worse than the assertion itself: it cites the read(2) and write(2) manpages to claim that a count of zero is allowed, but a zero count lands in "case 0" and fails the test outright. The helper has never accepted what the comment promises. Move test_read() into elf.c as a static elf_read(), named for what it does rather than reading as "test the read() syscall", and assert that @count is non-zero, which is what the code has always required and what every caller already passes. Drop the commentary describing retry behaviour that the code makes obvious. Nothing is then left in lib/io.c, so remove the file along with test_write() and the stale test_seq_read() declaration. No functional change intended. Suggested-by: Sean Christopherson Link: https://lore.kernel.org/all/aqxNbR9u5F1sqB3p@google.com Signed-off-by: Gokul K Link: https://patch.msgid.link/20260918054420.338842-1-gokul02k@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/Makefile.kvm | 1 - .../testing/selftests/kvm/include/test_util.h | 4 - tools/testing/selftests/kvm/lib/elf.c | 48 +++++- tools/testing/selftests/kvm/lib/io.c | 157 ------------------ 4 files changed, 43 insertions(+), 167 deletions(-) delete mode 100644 tools/testing/selftests/kvm/lib/io.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 96bab7002d39..d12c1c738956 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -6,7 +6,6 @@ all: LIBKVM += lib/assert.c LIBKVM += lib/elf.c LIBKVM += lib/guest_modes.c -LIBKVM += lib/io.c LIBKVM += lib/kvm_util.c LIBKVM += lib/lru_gen_util.c LIBKVM += lib/memstress.c diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h index a6a3e1657895..e558346c3b69 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -49,10 +49,6 @@ do { \ #define TEST_REQUIRE(f) __TEST_REQUIRE(f, "Requirement not met: %s", #f) -ssize_t test_write(int fd, const void *buf, size_t count); -ssize_t test_read(int fd, void *buf, size_t count); -int test_seq_read(const char *path, char **bufp, size_t *sizep); - void __printf(5, 6) test_assert(bool exp, const char *exp_str, const char *file, unsigned int line, const char *fmt, ...); diff --git a/tools/testing/selftests/kvm/lib/elf.c b/tools/testing/selftests/kvm/lib/elf.c index 1924a9895834..d5adb68fd7c0 100644 --- a/tools/testing/selftests/kvm/lib/elf.c +++ b/tools/testing/selftests/kvm/lib/elf.c @@ -12,6 +12,44 @@ #include "kvm_util.h" +static ssize_t elf_read(int fd, void *buf, size_t count) +{ + ssize_t rc; + ssize_t num_read = 0; + size_t num_left = count; + char *ptr = buf; + + TEST_ASSERT(count, "Count must be non-zero"); + + do { + rc = read(fd, ptr, num_left); + + switch (rc) { + case -1: + TEST_ASSERT(errno == EAGAIN || errno == EINTR, + "Unexpected read failure,\n" + " rc: %zi errno: %i", rc, errno); + break; + + case 0: + TEST_FAIL("Unexpected EOF,\n" + " rc: %zi num_read: %zi num_left: %zu", + rc, num_read, num_left); + break; + + default: + TEST_ASSERT(rc > 0, "Unexpected ret from read,\n" + " rc: %zi errno: %i", rc, errno); + num_read += rc; + num_left -= rc; + ptr += rc; + break; + } + } while (num_read < count); + + return num_read; +} + static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp) { off_t offset_rv; @@ -31,7 +69,7 @@ static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp) * the real size of the ELF header. */ unsigned char ident[EI_NIDENT]; - test_read(fd, ident, sizeof(ident)); + elf_read(fd, ident, sizeof(ident)); TEST_ASSERT((ident[EI_MAG0] == ELFMAG0) && (ident[EI_MAG1] == ELFMAG1) && (ident[EI_MAG2] == ELFMAG2) && (ident[EI_MAG3] == ELFMAG3), "ELF MAGIC Mismatch,\n" @@ -79,7 +117,7 @@ static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp) offset_rv = lseek(fd, 0, SEEK_SET); TEST_ASSERT(offset_rv == 0, "Seek to ELF header failed,\n" " rv: %zi expected: %i", offset_rv, 0); - test_read(fd, hdrp, sizeof(*hdrp)); + elf_read(fd, hdrp, sizeof(*hdrp)); TEST_ASSERT(hdrp->e_phentsize == sizeof(Elf64_Phdr), "Unexpected physical header size,\n" " hdrp->e_phentsize: %x\n" @@ -146,7 +184,7 @@ void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename) /* Read in the program header. */ Elf64_Phdr phdr; - test_read(fd, &phdr, sizeof(phdr)); + elf_read(fd, &phdr, sizeof(phdr)); /* Skip if this header doesn't describe a loadable segment. */ if (phdr.p_type != PT_LOAD) @@ -186,8 +224,8 @@ void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename) " expected: 0x%jx", n1, errno, (intmax_t) offset_rv, (intmax_t) phdr.p_offset); - test_read(fd, addr_gva2hva(vm, phdr.p_vaddr), - phdr.p_filesz); + elf_read(fd, addr_gva2hva(vm, phdr.p_vaddr), + phdr.p_filesz); } } close(fd); diff --git a/tools/testing/selftests/kvm/lib/io.c b/tools/testing/selftests/kvm/lib/io.c deleted file mode 100644 index fedb2a741f0b..000000000000 --- a/tools/testing/selftests/kvm/lib/io.c +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * tools/testing/selftests/kvm/lib/io.c - * - * Copyright (C) 2018, Google LLC. - */ - -#include "test_util.h" - -/* Test Write - * - * A wrapper for write(2), that automatically handles the following - * special conditions: - * - * + Interrupted system call (EINTR) - * + Write of less than requested amount - * + Non-block return (EAGAIN) - * - * For each of the above, an additional write is performed to automatically - * continue writing the requested data. - * There are also many cases where write(2) can return an unexpected - * error (e.g. EIO). Such errors cause a TEST_ASSERT failure. - * - * Note, for function signature compatibility with write(2), this function - * returns the number of bytes written, but that value will always be equal - * to the number of requested bytes. All other conditions in this and - * future enhancements to this function either automatically issue another - * write(2) or cause a TEST_ASSERT failure. - * - * Args: - * fd - Opened file descriptor to file to be written. - * count - Number of bytes to write. - * - * Output: - * buf - Starting address of data to be written. - * - * Return: - * On success, number of bytes written. - * On failure, a TEST_ASSERT failure is caused. - */ -ssize_t test_write(int fd, const void *buf, size_t count) -{ - ssize_t rc; - ssize_t num_written = 0; - size_t num_left = count; - const char *ptr = buf; - - /* Note: Count of zero is allowed (see "RETURN VALUE" portion of - * write(2) manpage for details. - */ - TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count); - - do { - rc = write(fd, ptr, num_left); - - switch (rc) { - case -1: - TEST_ASSERT(errno == EAGAIN || errno == EINTR, - "Unexpected write failure,\n" - " rc: %zi errno: %i", rc, errno); - continue; - - case 0: - TEST_FAIL("Unexpected EOF,\n" - " rc: %zi num_written: %zi num_left: %zu", - rc, num_written, num_left); - break; - - default: - TEST_ASSERT(rc >= 0, "Unexpected ret from write,\n" - " rc: %zi errno: %i", rc, errno); - num_written += rc; - num_left -= rc; - ptr += rc; - break; - } - } while (num_written < count); - - return num_written; -} - -/* Test Read - * - * A wrapper for read(2), that automatically handles the following - * special conditions: - * - * + Interrupted system call (EINTR) - * + Read of less than requested amount - * + Non-block return (EAGAIN) - * - * For each of the above, an additional read is performed to automatically - * continue reading the requested data. - * There are also many cases where read(2) can return an unexpected - * error (e.g. EIO). Such errors cause a TEST_ASSERT failure. Note, - * it is expected that the file opened by fd at the current file position - * contains at least the number of requested bytes to be read. A TEST_ASSERT - * failure is produced if an End-Of-File condition occurs, before all the - * data is read. It is the callers responsibility to assure that sufficient - * data exists. - * - * Note, for function signature compatibility with read(2), this function - * returns the number of bytes read, but that value will always be equal - * to the number of requested bytes. All other conditions in this and - * future enhancements to this function either automatically issue another - * read(2) or cause a TEST_ASSERT failure. - * - * Args: - * fd - Opened file descriptor to file to be read. - * count - Number of bytes to read. - * - * Output: - * buf - Starting address of where to write the bytes read. - * - * Return: - * On success, number of bytes read. - * On failure, a TEST_ASSERT failure is caused. - */ -ssize_t test_read(int fd, void *buf, size_t count) -{ - ssize_t rc; - ssize_t num_read = 0; - size_t num_left = count; - char *ptr = buf; - - /* Note: Count of zero is allowed (see "If count is zero" portion of - * read(2) manpage for details. - */ - TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count); - - do { - rc = read(fd, ptr, num_left); - - switch (rc) { - case -1: - TEST_ASSERT(errno == EAGAIN || errno == EINTR, - "Unexpected read failure,\n" - " rc: %zi errno: %i", rc, errno); - break; - - case 0: - TEST_FAIL("Unexpected EOF,\n" - " rc: %zi num_read: %zi num_left: %zu", - rc, num_read, num_left); - break; - - default: - TEST_ASSERT(rc > 0, "Unexpected ret from read,\n" - " rc: %zi errno: %i", rc, errno); - num_read += rc; - num_left -= rc; - ptr += rc; - break; - } - } while (num_read < count); - - return num_read; -} From 3bac3b77c5e6b4403701cab9df43030daf984a2e Mon Sep 17 00:00:00 2001 From: Gokul K Date: Fri, 18 Sep 2026 17:06:31 +0530 Subject: [PATCH 62/83] KVM: selftests: Delete the unused guest_snprintf() Delete guest_snprintf() as it isn't used, has never been used, and in all likelihood will never be used. Because variadic functions can't pass along va_args verbatim, guest_snprintf() can't be used as-is by things like ucall_fmt(). guest_snprintf() could be used to dedup the ucall code by turning it into a macro, but that would still require implicitly consuming the caller's variadic arguments and would risk creating variable shadows, i.e. introduces fragility for very little benefit. No functional change intended. Link: https://lore.kernel.org/all/20260930002238.649421F000FF@smtp.kernel.org Signed-off-by: Gokul K Closes: https://lore.kernel.org/all/20260918113631.634233-4-gokul02k@gmail.com Link: https://patch.msgid.link/20260918113631.634233-4-gokul02k@gmail.com [sean: massage changelog] Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/test_util.h | 1 - tools/testing/selftests/kvm/lib/guest_sprintf.c | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h index e558346c3b69..610d910e64af 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -234,7 +234,6 @@ static inline u32 atoi_non_negative(const char *name, const char *num_str) } int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args); -__printf(3, 4) int guest_snprintf(char *buf, int n, const char *fmt, ...); char *strdup_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2), nonnull(1))); diff --git a/tools/testing/selftests/kvm/lib/guest_sprintf.c b/tools/testing/selftests/kvm/lib/guest_sprintf.c index 7a33965349a7..6caebb456f3d 100644 --- a/tools/testing/selftests/kvm/lib/guest_sprintf.c +++ b/tools/testing/selftests/kvm/lib/guest_sprintf.c @@ -300,15 +300,3 @@ int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args) *str = '\0'; return str - buf; } - -int guest_snprintf(char *buf, int n, const char *fmt, ...) -{ - va_list va; - int len; - - va_start(va, fmt); - len = guest_vsnprintf(buf, n, fmt, va); - va_end(va); - - return len; -} From b701a8139fd6d51462a5dcac79c92c3ff6524f6c Mon Sep 17 00:00:00 2001 From: Amirmohammad Eftekhar Date: Wed, 30 Sep 2026 10:36:15 -0700 Subject: [PATCH 63/83] KVM: x86: Saturate L2's TSC frequency if it exceeds hardware supports When computing the TSC multiplier for L2, use the minimum/maximum frequency supported by hardware if the resulting multiplier can't be programmed into hardware, i.e. saturate L2's frequency on both sides. This can happen if userspace is running L1 at a low/high frequency relative to L0, and L1 is doing the same for L2 (relative to L1), because although each of the L1 and L2 inputs are constrained and validated, the end result can exceed SVM's and VMX's architectural limits. Don't bother trying to log an error or do something more sophisticated, because in practice only a misbehaving L1 (and/or host userspace) will run afoul of the flaw. On SVM, which has the smallest "range" by far (8 bits for the integer multiplier, versus 16 bits on VMX), KVM can still run L2 with a frequency 255x that of L0. Even assuming a pessimistic L0 TSC frequency of 1GHz (modern CPUs run TSC at 2GHz+), L2 would need to be running at a whopping ~255GHz for the limitation to be a problem. Not to mention that if L2 is running at a legitimate frequency, then running that VM on existing hardware is already doomed irrespective of the nested angle. Open code the mul_u64_u64_shr() if the compiler natively supports 128-bit integers, mostly to avoid having to do the multiply twice for what should be a very rare scenario, but also so that there's a slightly more scrutable sequence that can be read to understand what all is going on. Signed-off-by: Amirmohammad Eftekhar [sean: optimize for INT128, rewrite comment+changelog to be less AI] Link: https://patch.msgid.link/20260930173635.3362655-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 55 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 79468ddfe473..b7eb410c423c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1164,11 +1164,58 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_calc_nested_tsc_offset); u64 kvm_calc_nested_tsc_multiplier(u64 l1_multiplier, u64 l2_multiplier) { - if (l2_multiplier != kvm_caps.default_tsc_scaling_ratio) - return mul_u64_u64_shr(l1_multiplier, l2_multiplier, - kvm_caps.tsc_scaling_ratio_frac_bits); + u8 frac_bits = kvm_caps.tsc_scaling_ratio_frac_bits; + u64 nested_multiplier; - return l1_multiplier; + if (l2_multiplier == kvm_caps.default_tsc_scaling_ratio) + return l1_multiplier; + + /* + * The shift is fixed on both AMD and Intel, and operates on a 64-bit + * value. I.e. a shift greater than 63 is completely nonsensical. + */ + if (WARN_ON_ONCE(frac_bits > 63)) + return l1_multiplier; + + /* + * If the resulting multiplier can't be programmed into hardware, run + * L2 at the minimum/maximum frequency supported by hardware, i.e. + * saturate L2's frequency on both sides. Because L2's frequency needs + * to be distilled down to a single multiplier to get from: + * + * L2 = (((L0 * L1_mult) >> frac) * L2_mult) >> frac) + * + * to: + * + * L2 = (L0 * mult) >> frac + * + * very small/large L1 and L2 multipliers can underflow/overflow the + * minimum/maximum multiplier supported by hardware when combined into + * a single value. + * + * Manually check for the case where the result would overflow a u64, + * i.e. if the multiplier would be silently truncated before the "too + * large" check. Avoid doing the multiply twice in the common case + * where the compiler natively supports 128-bit values. + */ +#ifdef CONFIG_ARCH_SUPPORTS_INT128 + unsigned __int128 m = (unsigned __int128)l1_multiplier * l2_multiplier; + + if (m >> (64 + frac_bits)) + return kvm_caps.max_tsc_scaling_ratio; + + nested_multiplier = m >> frac_bits; +#else + if (mul_u64_u64_shr(l1_multiplier, l2_multiplier, 64 + frac_bits)) + return kvm_caps.max_tsc_scaling_ratio; + + nested_multiplier = mul_u64_u64_shr(l1_multiplier, l2_multiplier, frac_bits); +#endif + if (nested_multiplier > kvm_caps.max_tsc_scaling_ratio) + return kvm_caps.max_tsc_scaling_ratio; + + /* The minimum multiplier is '1' on both AMD and Intel. */ + return nested_multiplier ?: 1; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_calc_nested_tsc_multiplier); From 967e40fb1b08110e5528d19fc96b4b5410e53109 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:16 -0700 Subject: [PATCH 64/83] KVM: SVM: Fallback to the default TSC ratio if KVM tries to use a bad multiplier Explicitly WARN and fallback to the default TSC ratio, i.e. run the guest at L0's TSC frequency, if KVM tries to program a bad multiplier value. As pointed out by Sashiko, leaving the MSR as-is bleeds state from the vCPU that last ran on the pCPU into the likely-misbehaving current vCPU. Leaking a vCPU's frequency isn't very interesting, and corrupting KVM's cache isn't a big deal either since KVM would only refuse to try to write the same bad value in the future, but falling back to the default value is trivial, and explicitly WARNing ensures a KVM bug won't slip by silently. E.g. because ex_handler_msr() only WARNs once for *all* WRMSRs, it's possible for the potentially-fatal-to-the-guest issue to not exhibit any visible symptoms in the host. Reported-by: Sashiko Bot Closes: https://lore.kernel.org/all/20260722091414.E842B1F000E9@smtp.kernel.org Link: https://patch.msgid.link/20260930173635.3362655-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/svm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 7d59d301e1e5..26d458e9af54 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -530,6 +530,15 @@ static int svm_check_processor_compat(void) static void __svm_write_tsc_multiplier(u64 multiplier) { + /* + * Fallback to the default ratio if KVM is buggy and tries to program + * an unsupported scaling ratio, e.g. so that the guest has a chance of + * surviving, so that the cache isn't stale/corrupted, and so that KVM + * doesn't leak state across VMs. + */ + if (WARN_ON_ONCE(multiplier & SVM_TSC_RATIO_RSVD)) + multiplier = SVM_TSC_RATIO_DEFAULT; + if (multiplier == __this_cpu_read(current_tsc_ratio)) return; From 95eaeb7f202baddbe81ab832d8523acbb158edfe Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:17 -0700 Subject: [PATCH 65/83] KVM: x86: Allow userspace to set KVM's max supported guest TSC frequency Reject KVM_SET_TSC_KHZ if the incoming frequency is strictly greater than KVM's max supported frequency, not if the frequency is greater than *or* equal to the max frequency. The effective off-by-one bug came about via (dubious) review feedback, which also subtly collided with a functional change in later versions of the original TSC scaling series. In v2 of the original TSC scaling series[1], KVM set its absolute min/max to [1, UINT_MAX], i.e. allowed any value that would fit in the u32 passed to KVM_SET_TSC_KHZ[2]. min = max(1ULL, __scale_tsc(tsc_khz, TSC_RATIO_MIN)); max = min(0xffffffffULL, __scale_tsc(tsc_khz, TSC_RATIO_MAX)); That prompted Avi to suggest rejecting the "equals" case, presumably because the check would always succeed given an absolute max of UINT_MAX? But even that doesn't hold up to scrutiny, as allowing the actual min/max isn't inherently unsafe. Regardless, that feedback was taken and applied to future versions, but only for the maximum, not the minimum. > + > + r = -EINVAL; > + if (user_tsc_khz< kvm_min_guest_tsc_khz || > + user_tsc_khz> kvm_max_guest_tsc_khz) <= and >= are probably safer. v3 of the series[3] then also realized that allowing UINT_MAX would lead to undesirable interactions with KVM_GET_TSC_KHZ, which returns a *signed* 32-bit integer that is implicitly converted into a signed 64-bit value on 64-bit kernels. I.e. allowing a value greater than INT_MAX would result in userspace observing a negative value when doing KVM_GET_TSC_KHZ after KVM_SET_TSC_KHZ. /* * Make sure the user can only configure tsc_khz values that * fit into a signed integer. * A min value is not calculated needed because it will always * be 1 on all machines and a value of 0 is used to disable * tsc-scaling for the vcpu. */ max = min(0x7fffffffULL, __scale_tsc(tsc_khz, TSC_RATIO_MAX)); kvm_max_guest_tsc_khz = max; v3 also dropped the explicit minimum tracking, as both AMD and Intel support a minimum *fractional* ratio of 1, i.e. AMD and Intel support a minimum frequency of "host / 2^32" and "host / 2^48" respectively. And because KVM_GET_TSC_KHZ (and KVM itself) only supports frequencies that fit in a signed 32-bit integer, even AMD's more coarse-grained ratio can scale down any host frequency to '1', i.e. KVM can always support a minimum frequency of 1KHz. Rather than explicitly reject a frequency of 1KHz, v3 simply dropped the minimum check, i.e. ignored the "<=" suggestion, but kept the ">=" side of things. As a result, KVM now has a bizarre uABI where KVM_SET_TSC_KHZ tops out at INT_MAX-1 for no discernible reason. Fix the off-by-one flaw to provide a less weird uABI, and so that KVM_SET_TSC_KHZ accepts the maximum possible value that can be returned by KVM_GET_TSC_KHZ (without running afoul of casting issues; KVM doesn't actually sanity check that tsc_khz fits in a signed 32-bit value, which is a non-issue in practice because the units are KHz, not Hz, i.e. KVM_GET_TSC_KHZ is fine until CPUs with a TSC frequency greater than ~2.147PHz come along). Note, in practice, no real world VMM is likely to care. As above, running afoul of the off-by-one issue would mean trying to configure a virtual TSC frequency that is three orders of magnitude greater than what current CPUs support. Opportunistically explain *why* KVM restricts KVM_SET_TSC_KHZ to values that fit in a signed integer, as it requires far too much spelunking to piece together the connection to KVM_GET_TSC_KHZ. Link: https://lore.kernel.org/all/1300952424-32014-1-git-send-email-joerg.roedel@amd.com [1] Link: https://lore.kernel.org/all/1300952424-32014-7-git-send-email-joerg.roedel@amd.com [2] Link: https://lore.kernel.org/all/1301042691-22929-7-git-send-email-joerg.roedel@amd.com [3] Link: https://patch.msgid.link/20260930173635.3362655-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b7eb410c423c..baf171804748 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3764,7 +3764,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, user_tsc_khz = (u32)arg; if (kvm_caps.has_tsc_control && - user_tsc_khz >= kvm_caps.max_guest_tsc_khz) + user_tsc_khz > kvm_caps.max_guest_tsc_khz) goto out; if (user_tsc_khz == 0) @@ -4700,7 +4700,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) user_tsc_khz = (u32)arg; if (kvm_caps.has_tsc_control && - user_tsc_khz >= kvm_caps.max_guest_tsc_khz) + user_tsc_khz > kvm_caps.max_guest_tsc_khz) goto out; if (user_tsc_khz == 0) @@ -7178,7 +7178,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) if (kvm_caps.has_tsc_control) { /* * Make sure the user can only configure tsc_khz values that - * fit into a signed integer. + * fit into a signed integer, otherwise KVM_GET_TSC_KHZ would + * return a negative value and confuse userspace. * A min value is not calculated because it will always * be 1 on all machines. */ From ed95a2725719f41a7580480543eda9892164fe82 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:18 -0700 Subject: [PATCH 66/83] KVM: selftests: Use KVM's pRNG to randomize L1's TSC ratio in TSC scaling test Use KVM's pRNG to randomize L1's TSC ratio in the nested TSC scaling test, so that reproducing a failure with a specific seed is slightly easier. Link: https://patch.msgid.link/20260930173635.3362655-5-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index c763f4cf0f62..65de46f80a3e 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -166,8 +166,7 @@ int main(int argc, char *argv[]) * referenced by both main() and l1_guest_code() and using a global * variable does not work. */ - srand(time(NULL)); - l1_scale_factor = (rand() % 9) + 2; + l1_scale_factor = (kvm_random_u32(&kvm_rng) % 9) + 2; printf("L1's scale down factor is: %"PRIu64"\n", l1_scale_factor); printf("L2's scale up factor is: %llu\n", L2_SCALE_FACTOR); From b955d8fdabfe13d6ee6a52b1c0048cb9e5931912 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:19 -0700 Subject: [PATCH 67/83] KVM: selftests: Drop redundant VMWRITE of TSC_MULTIPLIER_HIGH Drop a redundant VMWRITE of TSC_MULTIPLIER_HIGH in the nested TSC scaling test. KVM selftests are 64-bit only, i.e. the full 64-bit multiplier is always written by setting TSC_MULTIPLIER. Link: https://patch.msgid.link/20260930173635.3362655-6-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 65de46f80a3e..052ddd61482d 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -122,7 +122,6 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) vmwrite(TSC_OFFSET, TSC_OFFSET_L2); vmwrite(TSC_MULTIPLIER, TSC_MULTIPLIER_L2); - vmwrite(TSC_MULTIPLIER_HIGH, TSC_MULTIPLIER_L2 >> 32); /* launch L2 */ vmlaunch(); From 17919fe61684235f9e66da14933ec09c3081df2a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:20 -0700 Subject: [PATCH 68/83] KVM: selftests: Drop unnecessary use of PRIu64 in nested TSC scaling test Use a plain "lu" instead of "PRIu64" to print u64s in the nested TSC scaling test, as KVM selftests are 64-bit only (and supporting 32-bit x86 selftest would be an absurdly large lift, for something absolutely no one cares about). Link: https://patch.msgid.link/20260930173635.3362655-7-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 052ddd61482d..b8b2217b9efa 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -41,14 +41,9 @@ static void compare_tsc_freq(u64 actual, u64 expected) thresh_low = expected - tolerance; thresh_high = expected + tolerance; - TEST_ASSERT(thresh_low < actual, - "TSC freq is expected to be between %"PRIu64" and %"PRIu64 - " but it actually is %"PRIu64, - thresh_low, thresh_high, actual); - TEST_ASSERT(thresh_high > actual, - "TSC freq is expected to be between %"PRIu64" and %"PRIu64 - " but it actually is %"PRIu64, - thresh_low, thresh_high, actual); + TEST_ASSERT(thresh_low < actual && thresh_high > actual, + "TSC freq is '%lu', expected to be between %lu and %lu", + actual, thresh_low, thresh_high); } static void check_tsc_freq(int level) @@ -166,7 +161,7 @@ int main(int argc, char *argv[]) * variable does not work. */ l1_scale_factor = (kvm_random_u32(&kvm_rng) % 9) + 2; - printf("L1's scale down factor is: %"PRIu64"\n", l1_scale_factor); + printf("L1's scale down factor is: %lu\n", l1_scale_factor); printf("L2's scale up factor is: %llu\n", L2_SCALE_FACTOR); tsc_start = rdtsc(); @@ -174,7 +169,7 @@ int main(int argc, char *argv[]) tsc_end = rdtsc(); l0_tsc_freq = tsc_end - tsc_start; - printf("real TSC frequency is around: %"PRIu64"\n", l0_tsc_freq); + printf("real TSC frequency is around: %lu\n", l0_tsc_freq); vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); @@ -207,16 +202,14 @@ int main(int argc, char *argv[]) break; case UCHECK_L1: l1_tsc_freq = uc.args[1]; - printf("L1's TSC frequency is around: %"PRIu64 - "\n", l1_tsc_freq); + printf("L1's TSC frequency is around: %lu\n", l1_tsc_freq); compare_tsc_freq(l1_tsc_freq, l0_tsc_freq / l1_scale_factor); break; case UCHECK_L2: l2_tsc_freq = uc.args[1]; - printf("L2's TSC frequency is around: %"PRIu64 - "\n", l2_tsc_freq); + printf("L2's TSC frequency is around: %lu\n", l2_tsc_freq); compare_tsc_freq(l2_tsc_freq, l1_tsc_freq * L2_SCALE_FACTOR); From 19d963ed9a4072fba776673619c44fbee45fc9d2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:21 -0700 Subject: [PATCH 69/83] KVM: selftests: Randomize L2's scale factor in nested TSC scaling test Randomize L2's scale factor in the nested TSC scaling test; the test's comment that "using a global variable does not work" just means the author didn't know how to use sync_global_to_guest(). Link: https://patch.msgid.link/20260930173635.3362655-8-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index b8b2217b9efa..84312146e93f 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -17,10 +17,9 @@ #include "kselftest.h" /* L2 is scaled up (from L1's perspective) by this factor */ -#define L2_SCALE_FACTOR 4ULL +static u64 l2_scale_factor; #define TSC_OFFSET_L2 ((u64)-33125236320908) -#define TSC_MULTIPLIER_L2 (L2_SCALE_FACTOR << 48) enum { USLEEP, UCHECK_L1, UCHECK_L2 }; #define GUEST_SLEEP(sec) ucall(UCALL_SYNC, 2, USLEEP, sec) @@ -81,7 +80,7 @@ static void l1_svm_code(struct svm_test_data *svm) generic_svm_setup(svm, l2_guest_code); /* enable TSC scaling for L2 */ - wrmsr(MSR_AMD64_TSC_RATIO, L2_SCALE_FACTOR << 32); + wrmsr(MSR_AMD64_TSC_RATIO, l2_scale_factor << 32); /* launch L2 */ run_guest(svm->vmcb, svm->vmcb_gpa); @@ -116,7 +115,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) vmwrite(SECONDARY_VM_EXEC_CONTROL, control); vmwrite(TSC_OFFSET, TSC_OFFSET_L2); - vmwrite(TSC_MULTIPLIER, TSC_MULTIPLIER_L2); + vmwrite(TSC_MULTIPLIER, l2_scale_factor << 48); /* launch L2 */ vmlaunch(); @@ -154,15 +153,12 @@ int main(int argc, char *argv[]) TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL)); TEST_REQUIRE(sys_clocksource_is_based_on_tsc()); - /* - * We set L1's scale factor to be a random number from 2 to 10. - * Ideally we would do the same for L2's factor but that one is - * referenced by both main() and l1_guest_code() and using a global - * variable does not work. - */ + /* Scale L1 "down" and L2 "up" at a random factor from 2 to 10. */ l1_scale_factor = (kvm_random_u32(&kvm_rng) % 9) + 2; printf("L1's scale down factor is: %lu\n", l1_scale_factor); - printf("L2's scale up factor is: %llu\n", L2_SCALE_FACTOR); + + l2_scale_factor = (kvm_random_u32(&kvm_rng) % 9) + 2; + printf("L2's scale up factor is: %lu\n", l2_scale_factor); tsc_start = rdtsc(); sleep(1); @@ -172,6 +168,7 @@ int main(int argc, char *argv[]) printf("real TSC frequency is around: %lu\n", l0_tsc_freq); vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + sync_global_to_guest(vm, l2_scale_factor); if (kvm_cpu_has(X86_FEATURE_VMX)) vcpu_alloc_vmx(vm, &guest_gva); @@ -212,7 +209,7 @@ int main(int argc, char *argv[]) printf("L2's TSC frequency is around: %lu\n", l2_tsc_freq); compare_tsc_freq(l2_tsc_freq, - l1_tsc_freq * L2_SCALE_FACTOR); + l1_tsc_freq * l2_scale_factor); break; } break; From 771902aecbb46e0908dec93fa75fba544fbba39d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:22 -0700 Subject: [PATCH 70/83] KVM: selftests: Rename TSC freq checkers in nested TSC scaling test Rename the nested TSC scaling test's helpers to clarify which one is used by guest code and which one is used by host code, and to make it more obvious that the host version *checks* the result and asserts on errors. Contrary to what "compare" suggests, the helper doesn't return whether or not the actual vs. expected values match. Link: https://patch.msgid.link/20260930173635.3362655-9-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 84312146e93f..e5fc4c9bb9f9 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -32,7 +32,7 @@ enum { USLEEP, UCHECK_L1, UCHECK_L2 }; * measurements, a difference of 1% between the actual and the expected value * is tolerated. */ -static void compare_tsc_freq(u64 actual, u64 expected) +static void host_check_tsc_freq(u64 actual, u64 expected) { u64 tolerance, thresh_low, thresh_high; @@ -45,7 +45,7 @@ static void compare_tsc_freq(u64 actual, u64 expected) actual, thresh_low, thresh_high); } -static void check_tsc_freq(int level) +static void guest_check_tsc_freq(int level) { u64 tsc_start, tsc_end, tsc_freq; @@ -66,7 +66,7 @@ static void check_tsc_freq(int level) static void l2_guest_code(void) { - check_tsc_freq(UCHECK_L2); + guest_check_tsc_freq(UCHECK_L2); /* exit to L1 */ __asm__ __volatile__("vmcall"); @@ -75,7 +75,7 @@ static void l2_guest_code(void) static void l1_svm_code(struct svm_test_data *svm) { /* check that L1's frequency looks alright before launching L2 */ - check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCHECK_L1); generic_svm_setup(svm, l2_guest_code); @@ -87,7 +87,7 @@ static void l1_svm_code(struct svm_test_data *svm) GUEST_ASSERT(svm->vmcb->control.exit_code == SVM_EXIT_VMMCALL); /* check that L1's frequency still looks good */ - check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCHECK_L1); GUEST_DONE(); } @@ -97,7 +97,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) u32 control; /* check that L1's frequency looks alright before launching L2 */ - check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCHECK_L1); prepare_for_vmx_operation(vmx_pages); load_vmcs(vmx_pages); @@ -122,7 +122,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); /* check that L1's frequency still looks good */ - check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCHECK_L1); GUEST_DONE(); } @@ -201,14 +201,14 @@ int main(int argc, char *argv[]) l1_tsc_freq = uc.args[1]; printf("L1's TSC frequency is around: %lu\n", l1_tsc_freq); - compare_tsc_freq(l1_tsc_freq, + host_check_tsc_freq(l1_tsc_freq, l0_tsc_freq / l1_scale_factor); break; case UCHECK_L2: l2_tsc_freq = uc.args[1]; printf("L2's TSC frequency is around: %lu\n", l2_tsc_freq); - compare_tsc_freq(l2_tsc_freq, + host_check_tsc_freq(l2_tsc_freq, l1_tsc_freq * l2_scale_factor); break; } From 6ab38c295d062121bd246403afb667662e5186f9 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:23 -0700 Subject: [PATCH 71/83] KVM: selftests: Extract guts of nested TSC scaling test to helper function Extract the guts of the nested TSC scaling test to a separate helper in anticipation of extending the test beyond its single "scale L1 up, L2 down" testcase. No functional change intended. Link: https://patch.msgid.link/20260930173635.3362655-10-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index e5fc4c9bb9f9..d3208b57eafc 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -135,38 +135,18 @@ static void l1_guest_code(void *data) l1_svm_code(data); } -int main(int argc, char *argv[]) +static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale) { + u64 tsc_khz, l1_tsc_freq, l2_tsc_freq; struct kvm_vcpu *vcpu; struct kvm_vm *vm; - gva_t guest_gva = 0; + gva_t guest_gva; - u64 tsc_start, tsc_end; - u64 tsc_khz; - u64 l1_scale_factor; - u64 l0_tsc_freq = 0; - u64 l1_tsc_freq = 0; - u64 l2_tsc_freq = 0; + l2_scale_factor = l2_scale; - TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || - kvm_cpu_has(X86_FEATURE_SVM)); - TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL)); - TEST_REQUIRE(sys_clocksource_is_based_on_tsc()); - - /* Scale L1 "down" and L2 "up" at a random factor from 2 to 10. */ - l1_scale_factor = (kvm_random_u32(&kvm_rng) % 9) + 2; printf("L1's scale down factor is: %lu\n", l1_scale_factor); - - l2_scale_factor = (kvm_random_u32(&kvm_rng) % 9) + 2; printf("L2's scale up factor is: %lu\n", l2_scale_factor); - tsc_start = rdtsc(); - sleep(1); - tsc_end = rdtsc(); - - l0_tsc_freq = tsc_end - tsc_start; - printf("real TSC frequency is around: %lu\n", l0_tsc_freq); - vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); sync_global_to_guest(vm, l2_scale_factor); @@ -183,6 +163,9 @@ int main(int argc, char *argv[]) /* scale down L1's TSC frequency */ vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *) (tsc_khz / l1_scale_factor)); + /* L1 will communicate its frequency before the L2 check.*/ + l1_tsc_freq = 0; + for (;;) { struct ucall uc; @@ -222,5 +205,28 @@ int main(int argc, char *argv[]) done: kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + u64 l0_tsc_freq, tsc_start, tsc_end, l1_scale, l2_scale; + + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || + kvm_cpu_has(X86_FEATURE_SVM)); + TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL)); + TEST_REQUIRE(sys_clocksource_is_based_on_tsc()); + + tsc_start = rdtsc(); + sleep(1); + tsc_end = rdtsc(); + + l0_tsc_freq = tsc_end - tsc_start; + printf("real TSC frequency is around: %lu\n", l0_tsc_freq); + + /* Scale L1 "down" and L2 "up" at a random factor from 2 to 10. */ + l1_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; + l2_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; + test_tsc_scaling(l0_tsc_freq, l1_scale, l2_scale); + return 0; } From ff18d56a08b2b1671d89a5be6d1b2bdd857de1ef Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:24 -0700 Subject: [PATCH 72/83] KVM: selftests: Track L2 multiplier, not scale-up factor, in nested TSC test Track L2's multiplier, not just the non-fractional scale-up factor, so that the nested TSC scaling test can also validate scaling L2's frequency *down*. No functional change intended. Link: https://patch.msgid.link/20260930173635.3362655-11-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index d3208b57eafc..af85fd469ef4 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -16,8 +16,8 @@ #include "svm_util.h" #include "kselftest.h" -/* L2 is scaled up (from L1's perspective) by this factor */ -static u64 l2_scale_factor; +/* L2's TSC multiplier, relative to L1. */ +static u64 l2_multiplier; #define TSC_OFFSET_L2 ((u64)-33125236320908) @@ -80,7 +80,7 @@ static void l1_svm_code(struct svm_test_data *svm) generic_svm_setup(svm, l2_guest_code); /* enable TSC scaling for L2 */ - wrmsr(MSR_AMD64_TSC_RATIO, l2_scale_factor << 32); + wrmsr(MSR_AMD64_TSC_RATIO, l2_multiplier); /* launch L2 */ run_guest(svm->vmcb, svm->vmcb_gpa); @@ -115,7 +115,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) vmwrite(SECONDARY_VM_EXEC_CONTROL, control); vmwrite(TSC_OFFSET, TSC_OFFSET_L2); - vmwrite(TSC_MULTIPLIER, l2_scale_factor << 48); + vmwrite(TSC_MULTIPLIER, l2_multiplier); /* launch L2 */ vmlaunch(); @@ -135,20 +135,22 @@ static void l1_guest_code(void *data) l1_svm_code(data); } -static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale) +static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_factor) { u64 tsc_khz, l1_tsc_freq, l2_tsc_freq; struct kvm_vcpu *vcpu; struct kvm_vm *vm; gva_t guest_gva; - - l2_scale_factor = l2_scale; + u8 frac_bits; printf("L1's scale down factor is: %lu\n", l1_scale_factor); printf("L2's scale up factor is: %lu\n", l2_scale_factor); + frac_bits = kvm_cpu_has(X86_FEATURE_VMX) ? 48 : 32; + l2_multiplier = l2_scale_factor << frac_bits; + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); - sync_global_to_guest(vm, l2_scale_factor); + sync_global_to_guest(vm, l2_multiplier); if (kvm_cpu_has(X86_FEATURE_VMX)) vcpu_alloc_vmx(vm, &guest_gva); From df1613249f6f65cebd6fff8782852013ca76b936 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:25 -0700 Subject: [PATCH 73/83] KVM: selftests: Print out the failing L{0,1,2} level in nested TSC scaling test Print out which one of L0, L1, or L2 TSC frequency checks failed if the nested TSC scaling test detects an out-of-bounds frequency. Triaging test failures without the level information is unnecessarily painful. Link: https://patch.msgid.link/20260930173635.3362655-12-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/x86/nested_tsc_scaling_test.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index af85fd469ef4..8c96b8e501f9 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -32,7 +32,7 @@ enum { USLEEP, UCHECK_L1, UCHECK_L2 }; * measurements, a difference of 1% between the actual and the expected value * is tolerated. */ -static void host_check_tsc_freq(u64 actual, u64 expected) +static void host_check_tsc_freq(int level, u64 actual, u64 expected) { u64 tolerance, thresh_low, thresh_high; @@ -41,8 +41,8 @@ static void host_check_tsc_freq(u64 actual, u64 expected) thresh_high = expected + tolerance; TEST_ASSERT(thresh_low < actual && thresh_high > actual, - "TSC freq is '%lu', expected to be between %lu and %lu", - actual, thresh_low, thresh_high); + "L%u TSC freq is '%lu', expected to be between %lu and %lu", + level, actual, thresh_low, thresh_high); } static void guest_check_tsc_freq(int level) @@ -186,14 +186,14 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_ l1_tsc_freq = uc.args[1]; printf("L1's TSC frequency is around: %lu\n", l1_tsc_freq); - host_check_tsc_freq(l1_tsc_freq, + host_check_tsc_freq(1, l1_tsc_freq, l0_tsc_freq / l1_scale_factor); break; case UCHECK_L2: l2_tsc_freq = uc.args[1]; printf("L2's TSC frequency is around: %lu\n", l2_tsc_freq); - host_check_tsc_freq(l2_tsc_freq, + host_check_tsc_freq(2, l2_tsc_freq, l1_tsc_freq * l2_scale_factor); break; } From faa4f010187c5d4cc4ddb3adecb9943ff87472f5 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:26 -0700 Subject: [PATCH 74/83] KVM: selftests: Allow +/- 1 tolerance if expected TSC frequency is <100 Massage the nested TSC scaling test's checker to allow for validating an expected TSC frequency of 1Hz, which is achievable for L2 since L1 can use a completely arbitrary fractional ratio. To do so, set the minimum tolerance to 1, and make the thresholds inclusive, i.e. for an expected frequency of 1, allow the actual frequency to be in the range of [0,2]. Because scaling L2's frequency down to a single Hz makes the virtual TSC run soooo comically slow, and because all of the math is done with basic integer arithmetic, it's very easy for the test to observe what appears to be a frequency of 0. Link: https://patch.msgid.link/20260930173635.3362655-13-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 8c96b8e501f9..d790ab36a198 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -36,11 +36,11 @@ static void host_check_tsc_freq(int level, u64 actual, u64 expected) { u64 tolerance, thresh_low, thresh_high; - tolerance = expected / 100; + tolerance = max(expected / 100, (u64)1); thresh_low = expected - tolerance; thresh_high = expected + tolerance; - TEST_ASSERT(thresh_low < actual && thresh_high > actual, + TEST_ASSERT(thresh_low <= actual && thresh_high >= actual, "L%u TSC freq is '%lu', expected to be between %lu and %lu", level, actual, thresh_low, thresh_high); } From 43f58b1ee6fe030d0e425cab0382d1e07841f897 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:27 -0700 Subject: [PATCH 75/83] KVM: selftests: Use KVM's reported TSC KHz as L0's frequency (sanity checked) Use KVM's reported default TSC frequency as L0's frequency instead of calibrating L0's frequency using RDTSC. Trying to calibrate the frequency in userspace is all but guaranteed to be wildly inaccurate, e.g. due to interrupts, preemption, etc, and no known CPU supports TSC scaling without also having a constant TSC. I.e. there's no need to calibrate the current frequency, as it should also be the same as KVM's default frequency. Opportunistically sanity check KVM's reported frequency against what is observed via RDTSC, e.g. so that KVM_GET_TSC_KHZ or platform issues show up much earlier in the test, before VMs get involved. Link: https://patch.msgid.link/20260930173635.3362655-14-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index d790ab36a198..a78585af0ffd 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -137,7 +137,7 @@ static void l1_guest_code(void *data) static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_factor) { - u64 tsc_khz, l1_tsc_freq, l2_tsc_freq; + u64 l1_tsc_freq, l2_tsc_freq; struct kvm_vcpu *vcpu; struct kvm_vm *vm; gva_t guest_gva; @@ -159,11 +159,8 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_ vcpu_args_set(vcpu, 1, guest_gva); - tsc_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL); - TEST_ASSERT(tsc_khz != -1, "vcpu ioctl KVM_GET_TSC_KHZ failed"); - /* scale down L1's TSC frequency */ - vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *) (tsc_khz / l1_scale_factor)); + vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *) (l0_tsc_freq / l1_scale_factor)); /* L1 will communicate its frequency before the L2 check.*/ l1_tsc_freq = 0; @@ -212,18 +209,30 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_ int main(int argc, char *argv[]) { u64 l0_tsc_freq, tsc_start, tsc_end, l1_scale, l2_scale; + struct kvm_vm *vm; TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || kvm_cpu_has(X86_FEATURE_SVM)); TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL)); TEST_REQUIRE(sys_clocksource_is_based_on_tsc()); + /* + * Create a dummy VM to get KVM's default TSC frequency. All CPUs that + * support TSC scaling should have a constant TSC, i.e. there's no need + * to calibrate the "real" TSC. But do sanity check that the observed + * TSC is within range of KVM's reported TSC frequency. + */ + vm = vm_create_barebones(); + l0_tsc_freq = (u64)__vm_ioctl(vm, KVM_GET_TSC_KHZ, NULL) * 1000; + TEST_ASSERT(l0_tsc_freq, "vcpu ioctl KVM_GET_TSC_KHZ failed"); + kvm_vm_free(vm); + + printf("L0 TSC frequency is: %lu\n", l0_tsc_freq); + tsc_start = rdtsc(); sleep(1); tsc_end = rdtsc(); - - l0_tsc_freq = tsc_end - tsc_start; - printf("real TSC frequency is around: %lu\n", l0_tsc_freq); + host_check_tsc_freq(0, tsc_end - tsc_start, l0_tsc_freq); /* Scale L1 "down" and L2 "up" at a random factor from 2 to 10. */ l1_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; From b77a34ac9d8fd19623dbe744bbde5a3f8b051add Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:28 -0700 Subject: [PATCH 76/83] KVM: selftests: Explicitly pass TSC frequencies to guts of TSC scaling test Explicitly pass the L1 and L2 TSC frequencies, along with the L2 multiplier, to the main test routine in the nested TSC scaling test. This will allow testing both "scale up" and "scale down" scenarios, and that KVM saturates L2's frequency if it's scale up/down beyond what hardware can support, in which case the expected frequency will not exactly match the L2 multiplier. Link: https://patch.msgid.link/20260930173635.3362655-15-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index a78585af0ffd..a8f9307ed2a4 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -8,7 +8,7 @@ * both L1 and L2 are scaled using different ratios. For this test we scale * L1 down and scale L2 up. */ - +#include #include #include "kvm_util.h" @@ -135,21 +135,19 @@ static void l1_guest_code(void *data) l1_svm_code(data); } -static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_factor) +static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_tsc_freq, u64 l2_tsc_freq, + u64 __l2_multiplier) { - u64 l1_tsc_freq, l2_tsc_freq; struct kvm_vcpu *vcpu; struct kvm_vm *vm; gva_t guest_gva; - u8 frac_bits; - printf("L1's scale down factor is: %lu\n", l1_scale_factor); - printf("L2's scale up factor is: %lu\n", l2_scale_factor); - - frac_bits = kvm_cpu_has(X86_FEATURE_VMX) ? 48 : 32; - l2_multiplier = l2_scale_factor << frac_bits; + printf("Testing L0 freq = %lu, L1 freq = %lu, L2 freq = %lu, L2 mult = 0x%lx\n", + l0_tsc_freq, l1_tsc_freq, l2_tsc_freq, __l2_multiplier); vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + + l2_multiplier = __l2_multiplier; sync_global_to_guest(vm, l2_multiplier); if (kvm_cpu_has(X86_FEATURE_VMX)) @@ -159,11 +157,7 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_ vcpu_args_set(vcpu, 1, guest_gva); - /* scale down L1's TSC frequency */ - vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *) (l0_tsc_freq / l1_scale_factor)); - - /* L1 will communicate its frequency before the L2 check.*/ - l1_tsc_freq = 0; + vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(l1_tsc_freq / 1000)); for (;;) { struct ucall uc; @@ -180,18 +174,12 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_ sleep(uc.args[1]); break; case UCHECK_L1: - l1_tsc_freq = uc.args[1]; - printf("L1's TSC frequency is around: %lu\n", l1_tsc_freq); - - host_check_tsc_freq(1, l1_tsc_freq, - l0_tsc_freq / l1_scale_factor); + printf("L1's observed TSC frequency: %lu\n", uc.args[1]); + host_check_tsc_freq(1, uc.args[1], l1_tsc_freq); break; case UCHECK_L2: - l2_tsc_freq = uc.args[1]; - printf("L2's TSC frequency is around: %lu\n", l2_tsc_freq); - - host_check_tsc_freq(2, l2_tsc_freq, - l1_tsc_freq * l2_scale_factor); + printf("L2's observed TSC frequency: %lu\n", uc.args[1]); + host_check_tsc_freq(2, uc.args[1], l2_tsc_freq); break; } break; @@ -209,6 +197,7 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_scale_factor, u64 l2_scale_ int main(int argc, char *argv[]) { u64 l0_tsc_freq, tsc_start, tsc_end, l1_scale, l2_scale; + u8 frac_bits = kvm_cpu_has(X86_FEATURE_VMX) ? 48 : 32; struct kvm_vm *vm; TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || @@ -237,7 +226,10 @@ int main(int argc, char *argv[]) /* Scale L1 "down" and L2 "up" at a random factor from 2 to 10. */ l1_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; l2_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; - test_tsc_scaling(l0_tsc_freq, l1_scale, l2_scale); + + test_tsc_scaling(l0_tsc_freq, l0_tsc_freq / l1_scale, + mul_u64_u64_div64(l0_tsc_freq, l2_scale, l1_scale), + (l2_scale << frac_bits)); return 0; } From 6760d12380a4e03aa4b1f2a4f769a3000d2543c1 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:29 -0700 Subject: [PATCH 77/83] KVM: selftests: Sanity check KVM's default TSC freq in nested TSC scaling test Explicitly verify that both L1 and L2 can run at KVM's default TSC frequency reported via KVM_GET_TSC_KHZ, e.g. that KVM_SET_TSC_KHZ can take in what KVM_GET_TSC_KHZ spits out. Link: https://patch.msgid.link/20260930173635.3362655-16-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index a8f9307ed2a4..ee8fda310e33 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -223,6 +223,9 @@ int main(int argc, char *argv[]) tsc_end = rdtsc(); host_check_tsc_freq(0, tsc_end - tsc_start, l0_tsc_freq); + /* Sanity check the frequency reported by KVM_GET_TSC_KHZ. */ + test_tsc_scaling(l0_tsc_freq, l0_tsc_freq, l0_tsc_freq, BIT_ULL(frac_bits)); + /* Scale L1 "down" and L2 "up" at a random factor from 2 to 10. */ l1_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; l2_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; From 245932d3880ca22382c9c727a9cdab54c3441d6a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:30 -0700 Subject: [PATCH 78/83] KVM: selftests: Test L1 "up" and L2 "down" in nested TSC scaling test Now that the guts of the nested TSC scaling test support scaling L2 down, validate all four combinations of scaling up/down L1 and L2. Ideally, the test would also validate integer+fractional scale up scenarios, but prioritize keeping the test and math somewhat understandable. Opportunistically drop the stale file comment about the test's name. Link: https://patch.msgid.link/20260930173635.3362655-17-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index ee8fda310e33..1d578af393bd 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -1,12 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * vmx_nested_tsc_scaling_test - * * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * This test case verifies that nested TSC scaling behaves as expected when - * both L1 and L2 are scaled using different ratios. For this test we scale - * L1 down and scale L2 up. + * both L1 and L2 are scaled using different ratios. */ #include #include @@ -226,7 +223,21 @@ int main(int argc, char *argv[]) /* Sanity check the frequency reported by KVM_GET_TSC_KHZ. */ test_tsc_scaling(l0_tsc_freq, l0_tsc_freq, l0_tsc_freq, BIT_ULL(frac_bits)); - /* Scale L1 "down" and L2 "up" at a random factor from 2 to 10. */ + /* + * Scale L1 and L2 up and down at random factors from 2 to 10. Current + * CPUs have two full orders of magnitude of "breathing room" before an + * ultrafast L0 TSC frequency multiplied by 10x will encounter KVM's + * signed 32-bit limit on L1's frequency, and it's highly unlikely a + * CPU that supports TSC scaling will show up running at 100MHz, i.e. + * underflowing KVM's minimum 1KHz frequency is extremely unlikely. + * + * For L2, the frequency is limited only by what hardware can support, + * not by KVM's limits. SVM provides 8 bits of integer scale up, and + * 32 bits of fractional scale down, i.e. can scale up 255x and down a + * comical amount, so scaling up 100x and down 1/100 is well within + * hardware's capabilities (VMX provides 16 bits of "up" and 48 bits of + * "down"). + */ l1_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; l2_scale = (kvm_random_u32(&kvm_rng) % 9) + 2; @@ -234,5 +245,17 @@ int main(int argc, char *argv[]) mul_u64_u64_div64(l0_tsc_freq, l2_scale, l1_scale), (l2_scale << frac_bits)); + test_tsc_scaling(l0_tsc_freq, l0_tsc_freq * l1_scale, + mul_u64_u64_div64(l0_tsc_freq, l1_scale, l2_scale), + (1ull << frac_bits) / l2_scale); + + test_tsc_scaling(l0_tsc_freq, l0_tsc_freq / l1_scale, + l0_tsc_freq / l1_scale / l2_scale, + (1ull << frac_bits) / l2_scale); + + test_tsc_scaling(l0_tsc_freq, l0_tsc_freq * l1_scale, + l0_tsc_freq * l1_scale * l2_scale, + (l2_scale << frac_bits)); + return 0; } From c2d3a10b90b13434aa5b945f661cf06a1b1ba56c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:31 -0700 Subject: [PATCH 79/83] KVM: selftests: Verify that KVM saturates L2 TSC freq on {under,over}flow Extend the nested TSC scaling test to validate the extreme ends of what hardware supports, i.e. validate that KVM allows L1 to scale L2's virtual TSC frequency up/down to the architectural maximum/minimum, from L1's perspective, and that KVM saturates L2's effective frequency when L2's frequency can't be virtualized by hardware (which is possible when stacking multipliers/ratios across L1 and L2). Link: https://patch.msgid.link/20260930173635.3362655-18-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 1d578af393bd..4cacd87f129b 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -193,6 +193,7 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_tsc_freq, u64 l2_tsc_freq, int main(int argc, char *argv[]) { + u64 min_freq, min_multiplier, max_freq, max_multiplier, l1_max_freq, l1_min_freq; u64 l0_tsc_freq, tsc_start, tsc_end, l1_scale, l2_scale; u8 frac_bits = kvm_cpu_has(X86_FEATURE_VMX) ? 48 : 32; struct kvm_vm *vm; @@ -257,5 +258,39 @@ int main(int argc, char *argv[]) l0_tsc_freq * l1_scale * l2_scale, (l2_scale << frac_bits)); + /* + * Test that KVM saturates L2's frequency on both ends if the resulting + * L2 TSC frequency would be below or above what hardware can support. + * Because L2 = L0 * (L1_mult >> frac) * (L2_mult >> frac) needs to be + * distilled down to a single multiplier, very small/large multipliers + * will underflow/overflow the minimum/maximum multiplier supported by + * hardware when L1 and L2 multipliers are combined. KVM's behavior is + * saturate on {under,over}flow, i.e. to run at the min/max frequency. + * + * Note, userspace can only program L1's frequency in KHz, i.e. can't + * specify an exact multiplier. As a result, the minimum and maximum + * frequencies are different for L1 vs L2, because L1 is constrained by + * hardware *and* KVM, whereas L2 is constrained only by hardware. + */ + min_multiplier = 1; + min_freq = mul_u64_u64_div64(l0_tsc_freq, min_multiplier, BIT_ULL(frac_bits)); + min_freq = max(min_freq, (u64)1); + l1_min_freq = max(min_freq, (u64)1 * 1000); + test_tsc_scaling(l0_tsc_freq, l1_min_freq, min_freq, 1); + + /* + * SVM takes a 40-bit value (right shifted by 32), while VMX takes a + * 64-bit value (right shifted by 48). mul_u64_u64_shr() isn't (yet) + * available in selftests, but mul_u64_u64_div64() does nicely since, + * albeit more slowly (performance is obviously not a concern). Note, + * because KVM_GET_TSC_KHZ returns a signed 32-bit integer, KVM limits + * KVM_SET_TSC_KHZ to INT_MAX, even though hardware (both SVM and VMX) + * supports much higher frequencies. + */ + max_multiplier = kvm_cpu_has(X86_FEATURE_VMX) ? -1ull : GENMASK_U64(39, 0); + max_freq = mul_u64_u64_div64(l0_tsc_freq, max_multiplier, BIT_ULL(frac_bits)); + l1_max_freq = min(max_freq, (u64)INT32_MAX * 1000); + test_tsc_scaling(l0_tsc_freq, l1_max_freq, max_freq, max_multiplier); + return 0; } From baada72b9c013cc6363a01b0e9af6263e07d696c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:32 -0700 Subject: [PATCH 80/83] KVM: selftests: Test non-zero TSC offset on SVM in nested TSC scaling test Set L2's TSC offset for SVM as well as VMX in the nested TSC scaling test. Odds are good skipping SVM was pure oversight. Fixes: e6bcdd212238 ("KVM: selftests: Extend vmx_nested_tsc_scaling_test to cover SVM") Link: https://patch.msgid.link/20260930173635.3362655-19-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 4cacd87f129b..571fc214c925 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -75,6 +75,7 @@ static void l1_svm_code(struct svm_test_data *svm) guest_check_tsc_freq(UCHECK_L1); generic_svm_setup(svm, l2_guest_code); + svm->vmcb->control.tsc_offset = TSC_OFFSET_L2; /* enable TSC scaling for L2 */ wrmsr(MSR_AMD64_TSC_RATIO, l2_multiplier); From 5f13a511c78215817fc603202b98293d4598f33d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:33 -0700 Subject: [PATCH 81/83] KVM: selftests: Randomize L2's TSC offset in the nested TSC scaling test Randomize L2's TSC offset in the nested TSC scaling test, not because it's all that interesting, but because hardcoding a copmletely arbitrary value is all kinds of silly. Link: https://patch.msgid.link/20260930173635.3362655-20-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/x86/nested_tsc_scaling_test.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 571fc214c925..be6246c20d7b 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -13,10 +13,9 @@ #include "svm_util.h" #include "kselftest.h" -/* L2's TSC multiplier, relative to L1. */ +/* L2's TSC multiplier and offset, relative to L1. */ static u64 l2_multiplier; - -#define TSC_OFFSET_L2 ((u64)-33125236320908) +static u64 l2_offset; enum { USLEEP, UCHECK_L1, UCHECK_L2 }; #define GUEST_SLEEP(sec) ucall(UCALL_SYNC, 2, USLEEP, sec) @@ -75,7 +74,7 @@ static void l1_svm_code(struct svm_test_data *svm) guest_check_tsc_freq(UCHECK_L1); generic_svm_setup(svm, l2_guest_code); - svm->vmcb->control.tsc_offset = TSC_OFFSET_L2; + svm->vmcb->control.tsc_offset = l2_offset; /* enable TSC scaling for L2 */ wrmsr(MSR_AMD64_TSC_RATIO, l2_multiplier); @@ -112,7 +111,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) control |= SECONDARY_EXEC_TSC_SCALING; vmwrite(SECONDARY_VM_EXEC_CONTROL, control); - vmwrite(TSC_OFFSET, TSC_OFFSET_L2); + vmwrite(TSC_OFFSET, l2_offset); vmwrite(TSC_MULTIPLIER, l2_multiplier); /* launch L2 */ @@ -145,6 +144,9 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_tsc_freq, u64 l2_tsc_freq, vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + l2_offset = kvm_random_u64(&kvm_rng); + sync_global_to_guest(vm, l2_offset); + l2_multiplier = __l2_multiplier; sync_global_to_guest(vm, l2_multiplier); From a744d1c1655d4c18769acf78a98119cb88bc3ada Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:34 -0700 Subject: [PATCH 82/83] KVM: selftests: Use GUEST_SYNC2() in nested TSC scaling test Use GUEST_SYNC2() instead of open coding equivalent functionality using raw ucall() invocations in the nested TSC scaling test. No functional change intended. Link: https://patch.msgid.link/20260930173635.3362655-21-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index be6246c20d7b..b21f79e9ec71 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -18,9 +18,6 @@ static u64 l2_multiplier; static u64 l2_offset; enum { USLEEP, UCHECK_L1, UCHECK_L2 }; -#define GUEST_SLEEP(sec) ucall(UCALL_SYNC, 2, USLEEP, sec) -#define GUEST_CHECK(level, freq) ucall(UCALL_SYNC, 2, level, freq) - /* * This function checks whether the "actual" TSC frequency of a guest matches @@ -52,12 +49,12 @@ static void guest_check_tsc_freq(int level) * be good enough for the purposes of this test. */ tsc_start = rdmsr(MSR_IA32_TSC); - GUEST_SLEEP(1); + GUEST_SYNC2(USLEEP, 1); tsc_end = rdmsr(MSR_IA32_TSC); tsc_freq = tsc_end - tsc_start; - GUEST_CHECK(level, tsc_freq); + GUEST_SYNC2(level, tsc_freq); } static void l2_guest_code(void) From bef953c069806148ae2f10714b39a08726c9a076 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 30 Sep 2026 10:36:35 -0700 Subject: [PATCH 83/83] KVM: selftests: Spell out UCALL in nested TSC scaling test's enums Explicitly spell out UCALL instead of prefixing only U in the nested TSC scaling test's ucall commands, as it's all too easy to think USLEEP means "do usleep()", when it actually means "do sleep() in userspace". Opportunistically name the enum and take an enum instead of a bare integer for the to-be-checked level, to make it more obvious that the fact that the level values happen to correspond to '1' and '2' for the nesting level is largely coincidental (or perhaps it was intentional, but it's not a hard requirement for the test). Link: https://patch.msgid.link/20260930173635.3362655-22-seanjc@google.com Signed-off-by: Sean Christopherson --- .../kvm/x86/nested_tsc_scaling_test.c | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index b21f79e9ec71..bfb1ff441c78 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -17,7 +17,7 @@ static u64 l2_multiplier; static u64 l2_offset; -enum { USLEEP, UCHECK_L1, UCHECK_L2 }; +enum ucall_cmd { UCALL_SLEEP, UCALL_CHECK_L1, UCALL_CHECK_L2 }; /* * This function checks whether the "actual" TSC frequency of a guest matches @@ -38,7 +38,7 @@ static void host_check_tsc_freq(int level, u64 actual, u64 expected) level, actual, thresh_low, thresh_high); } -static void guest_check_tsc_freq(int level) +static void guest_check_tsc_freq(enum ucall_cmd check_level) { u64 tsc_start, tsc_end, tsc_freq; @@ -49,17 +49,17 @@ static void guest_check_tsc_freq(int level) * be good enough for the purposes of this test. */ tsc_start = rdmsr(MSR_IA32_TSC); - GUEST_SYNC2(USLEEP, 1); + GUEST_SYNC2(UCALL_SLEEP, 1); tsc_end = rdmsr(MSR_IA32_TSC); tsc_freq = tsc_end - tsc_start; - GUEST_SYNC2(level, tsc_freq); + GUEST_SYNC2(check_level, tsc_freq); } static void l2_guest_code(void) { - guest_check_tsc_freq(UCHECK_L2); + guest_check_tsc_freq(UCALL_CHECK_L2); /* exit to L1 */ __asm__ __volatile__("vmcall"); @@ -68,7 +68,7 @@ static void l2_guest_code(void) static void l1_svm_code(struct svm_test_data *svm) { /* check that L1's frequency looks alright before launching L2 */ - guest_check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCALL_CHECK_L1); generic_svm_setup(svm, l2_guest_code); svm->vmcb->control.tsc_offset = l2_offset; @@ -81,7 +81,7 @@ static void l1_svm_code(struct svm_test_data *svm) GUEST_ASSERT(svm->vmcb->control.exit_code == SVM_EXIT_VMMCALL); /* check that L1's frequency still looks good */ - guest_check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCALL_CHECK_L1); GUEST_DONE(); } @@ -91,7 +91,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) u32 control; /* check that L1's frequency looks alright before launching L2 */ - guest_check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCALL_CHECK_L1); prepare_for_vmx_operation(vmx_pages); load_vmcs(vmx_pages); @@ -116,7 +116,7 @@ static void l1_vmx_code(struct vmx_pages *vmx_pages) GUEST_ASSERT(vmread(VM_EXIT_REASON) == EXIT_REASON_VMCALL); /* check that L1's frequency still looks good */ - guest_check_tsc_freq(UCHECK_L1); + guest_check_tsc_freq(UCALL_CHECK_L1); GUEST_DONE(); } @@ -167,14 +167,14 @@ static void test_tsc_scaling(u64 l0_tsc_freq, u64 l1_tsc_freq, u64 l2_tsc_freq, REPORT_GUEST_ASSERT(uc); case UCALL_SYNC: switch (uc.args[0]) { - case USLEEP: + case UCALL_SLEEP: sleep(uc.args[1]); break; - case UCHECK_L1: + case UCALL_CHECK_L1: printf("L1's observed TSC frequency: %lu\n", uc.args[1]); host_check_tsc_freq(1, uc.args[1], l1_tsc_freq); break; - case UCHECK_L2: + case UCALL_CHECK_L2: printf("L2's observed TSC frequency: %lu\n", uc.args[1]); host_check_tsc_freq(2, uc.args[1], l2_tsc_freq); break;