summaryrefslogtreecommitdiff
path: root/tools/testing
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2026-04-13 12:53:46 +0300
committerPaolo Bonzini <pbonzini@redhat.com>2026-04-13 12:53:46 +0300
commitc13008ed3d76142a001ebc56d8e391431cac2411 (patch)
tree7f1afe4676127e6b2d0e3d6ca4bdb38bcf6ec396 /tools/testing
parent276f81a4912157a018141dfe71ab0f40e575a796 (diff)
parent9830209b4ae8c8eecae7e6af271cebf1e1285142 (diff)
downloadlinux-c13008ed3d76142a001ebc56d8e391431cac2411.tar.xz
Merge tag 'kvm-x86-selftests-7.1' of https://github.com/kvm-x86/linux into HEAD
KVM selftests changes for 7.1 - Add support for Hygon CPUs in KVM selftests. - Fix a bug in the MSR test where it would get false failures on AMD/Hygon CPUs with exactly one of RDPID or RDTSCP. - Add an MADV_COLLAPSE testcase for guest_memfd as a regression test for a bug where the kernel would attempt to collapse guest_memfd folios against KVM's will.
Diffstat (limited to 'tools/testing')
-rw-r--r--tools/testing/selftests/kvm/guest_memfd_test.c70
-rw-r--r--tools/testing/selftests/kvm/include/kvm_syscalls.h1
-rw-r--r--tools/testing/selftests/kvm/include/x86/processor.h7
-rw-r--r--tools/testing/selftests/kvm/lib/x86/processor.c15
-rw-r--r--tools/testing/selftests/kvm/x86/fix_hypercall_test.c2
-rw-r--r--tools/testing/selftests/kvm/x86/msrs_test.c4
-rw-r--r--tools/testing/selftests/kvm/x86/pmu_event_filter_test.c3
-rw-r--r--tools/testing/selftests/kvm/x86/xapic_state_test.c2
8 files changed, 92 insertions, 12 deletions
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index cc329b57ce2e..ec7644aae999 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -171,6 +171,64 @@ static void test_numa_allocation(int fd, size_t total_size)
kvm_munmap(mem, total_size);
}
+static void test_collapse(int fd, uint64_t flags)
+{
+ const size_t pmd_size = get_trans_hugepagesz();
+ void *reserved_addr;
+ void *aligned_addr;
+ char *mem;
+ off_t i;
+
+ /*
+ * To even reach the point where the guest_memfd folios will
+ * get collapsed, both the userspace address and the offset
+ * within the guest_memfd have to be aligned to pmd_size.
+ *
+ * To achieve that alignment, reserve virtual address space
+ * with regular mmap, then use MAP_FIXED to allocate memory
+ * from a pmd_size-aligned offset (0) at a known, available
+ * virtual address.
+ */
+ reserved_addr = kvm_mmap(pmd_size * 2, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1);
+ aligned_addr = align_ptr_up(reserved_addr, pmd_size);
+
+ mem = mmap(aligned_addr, pmd_size, PROT_READ | PROT_WRITE,
+ MAP_FIXED | MAP_SHARED, fd, 0);
+ TEST_ASSERT(IS_ALIGNED((u64)mem, pmd_size),
+ "Userspace address must be aligned to PMD size.");
+
+ /*
+ * Use reads to populate page table to avoid setting dirty
+ * flag on page.
+ */
+ for (i = 0; i < pmd_size; i += getpagesize())
+ READ_ONCE(mem[i]);
+
+ /*
+ * Advising the use of huge pages in guest_memfd should be
+ * fine...
+ */
+ kvm_madvise(mem, pmd_size, MADV_HUGEPAGE);
+
+ /*
+ * ... but collapsing folios must not be supported to avoid
+ * mapping beyond shared ranges into host userspace page
+ * tables.
+ */
+ TEST_ASSERT_EQ(madvise(mem, pmd_size, MADV_COLLAPSE), -1);
+ TEST_ASSERT_EQ(errno, EINVAL);
+
+ /*
+ * Removing from host page tables and re-faulting should be
+ * fine; should not end up faulting in a collapsed/huge folio.
+ */
+ kvm_madvise(mem, pmd_size, MADV_DONTNEED);
+ READ_ONCE(mem[0]);
+
+ kvm_munmap(reserved_addr, pmd_size * 2);
+}
+
static void test_fault_sigbus(int fd, size_t accessible_size, size_t map_size)
{
const char val = 0xaa;
@@ -350,14 +408,17 @@ static void test_guest_memfd_flags(struct kvm_vm *vm)
}
}
-#define gmem_test(__test, __vm, __flags) \
+#define __gmem_test(__test, __vm, __flags, __gmem_size) \
do { \
- int fd = vm_create_guest_memfd(__vm, page_size * 4, __flags); \
+ int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
\
- test_##__test(fd, page_size * 4); \
+ test_##__test(fd, __gmem_size); \
close(fd); \
} while (0)
+#define gmem_test(__test, __vm, __flags) \
+ __gmem_test(__test, __vm, __flags, page_size * 4)
+
static void __test_guest_memfd(struct kvm_vm *vm, uint64_t flags)
{
test_create_guest_memfd_multiple(vm);
@@ -367,9 +428,12 @@ static void __test_guest_memfd(struct kvm_vm *vm, uint64_t flags)
if (flags & GUEST_MEMFD_FLAG_MMAP) {
if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) {
+ size_t pmd_size = get_trans_hugepagesz();
+
gmem_test(mmap_supported, vm, flags);
gmem_test(fault_overflow, vm, flags);
gmem_test(numa_allocation, vm, flags);
+ __gmem_test(collapse, vm, flags, pmd_size);
} else {
gmem_test(fault_private, vm, flags);
}
diff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h
index d4e613162bba..843c9904c46f 100644
--- a/tools/testing/selftests/kvm/include/kvm_syscalls.h
+++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h
@@ -77,5 +77,6 @@ __KVM_SYSCALL_DEFINE(munmap, 2, void *, mem, size_t, size);
__KVM_SYSCALL_DEFINE(close, 1, int, fd);
__KVM_SYSCALL_DEFINE(fallocate, 4, int, fd, int, mode, loff_t, offset, loff_t, len);
__KVM_SYSCALL_DEFINE(ftruncate, 2, unsigned int, fd, off_t, length);
+__KVM_SYSCALL_DEFINE(madvise, 3, void *, addr, size_t, length, int, advice);
#endif /* SELFTEST_KVM_SYSCALLS_H */
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 469a22122157..732a9d816a70 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -21,6 +21,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 uint64_t guest_tsc_khz;
#ifndef MAX_NR_CPUID_ENTRIES
@@ -717,6 +719,11 @@ static inline bool this_cpu_is_amd(void)
return this_cpu_vendor_string_is("AuthenticAMD");
}
+static inline bool this_cpu_is_hygon(void)
+{
+ return this_cpu_vendor_string_is("HygonGenuine");
+}
+
static inline uint32_t __this_cpu_has(uint32_t function, uint32_t index,
uint8_t reg, uint8_t lo, uint8_t hi)
{
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index 23a44941e283..01f0f97d4430 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -24,6 +24,8 @@
vm_vaddr_t exception_handlers;
bool host_cpu_is_amd;
bool host_cpu_is_intel;
+bool host_cpu_is_hygon;
+bool host_cpu_is_amd_compatible;
bool is_forced_emulation_enabled;
uint64_t guest_tsc_khz;
@@ -793,6 +795,8 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
sync_global_to_guest(vm, host_cpu_is_intel);
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, is_forced_emulation_enabled);
sync_global_to_guest(vm, pmu_errata_mask);
@@ -1349,7 +1353,8 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,
"1: vmmcall\n\t" \
"2:" \
: "=a"(r) \
- : [use_vmmcall] "r" (host_cpu_is_amd), inputs); \
+ : [use_vmmcall] "r" (host_cpu_is_amd_compatible), \
+ inputs); \
\
r; \
})
@@ -1389,8 +1394,8 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm)
max_gfn = (1ULL << (guest_maxphyaddr - vm->page_shift)) - 1;
- /* Avoid reserved HyperTransport region on AMD processors. */
- if (!host_cpu_is_amd)
+ /* Avoid reserved HyperTransport region on AMD or Hygon processors. */
+ if (!host_cpu_is_amd_compatible)
return max_gfn;
/* On parts with <40 physical address bits, the area is fully hidden */
@@ -1404,7 +1409,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm)
/*
* Otherwise it's at the top of the physical address space, possibly
- * reduced due to SME by bits 11:6 of CPUID[0x8000001f].EBX. Use
+ * reduced due to SME or CSV by bits 11:6 of CPUID[0x8000001f].EBX. Use
* the old conservative value if MAXPHYADDR is not enumerated.
*/
if (!this_cpu_has_p(X86_PROPERTY_MAX_PHY_ADDR))
@@ -1425,6 +1430,8 @@ void kvm_selftest_arch_init(void)
{
host_cpu_is_intel = this_cpu_is_intel();
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;
is_forced_emulation_enabled = kvm_is_forced_emulation_enabled();
kvm_init_pmu_errata();
diff --git a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
index 762628f7d4ba..00b6e85735dd 100644
--- a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
+++ b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
@@ -52,7 +52,7 @@ static void guest_main(void)
if (host_cpu_is_intel) {
native_hypercall_insn = vmx_vmcall;
other_hypercall_insn = svm_vmmcall;
- } else if (host_cpu_is_amd) {
+ } else if (host_cpu_is_amd_compatible) {
native_hypercall_insn = svm_vmmcall;
other_hypercall_insn = vmx_vmcall;
} else {
diff --git a/tools/testing/selftests/kvm/x86/msrs_test.c b/tools/testing/selftests/kvm/x86/msrs_test.c
index 40d918aedce6..f7e39bf887ad 100644
--- a/tools/testing/selftests/kvm/x86/msrs_test.c
+++ b/tools/testing/selftests/kvm/x86/msrs_test.c
@@ -81,7 +81,7 @@ static u64 fixup_rdmsr_val(u32 msr, u64 want)
* is supposed to emulate that behavior based on guest vendor model
* (which is the same as the host vendor model for this test).
*/
- if (!host_cpu_is_amd)
+ if (!host_cpu_is_amd_compatible)
return want;
switch (msr) {
@@ -175,7 +175,7 @@ void guest_test_reserved_val(const struct kvm_msr *msr)
* If the CPU will truncate the written value (e.g. SYSENTER on AMD),
* expect success and a truncated value, not #GP.
*/
- if (!this_cpu_has(msr->feature) ||
+ if ((!this_cpu_has(msr->feature) && !this_cpu_has(msr->feature2)) ||
msr->rsvd_val == fixup_rdmsr_val(msr->index, msr->rsvd_val)) {
u8 vec = wrmsr_safe(msr->index, msr->rsvd_val);
diff --git a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c
index 1c5b7611db24..93b61c077991 100644
--- a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c
+++ b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c
@@ -361,7 +361,8 @@ static bool use_intel_pmu(void)
*/
static bool use_amd_pmu(void)
{
- return host_cpu_is_amd && kvm_cpu_family() >= 0x17;
+ return (host_cpu_is_amd && kvm_cpu_family() >= 0x17) ||
+ host_cpu_is_hygon;
}
/*
diff --git a/tools/testing/selftests/kvm/x86/xapic_state_test.c b/tools/testing/selftests/kvm/x86/xapic_state_test.c
index 3b4814c55722..0c5e12f5f14e 100644
--- a/tools/testing/selftests/kvm/x86/xapic_state_test.c
+++ b/tools/testing/selftests/kvm/x86/xapic_state_test.c
@@ -248,7 +248,7 @@ int main(int argc, char *argv[])
* drops writes, AMD does not). Account for the errata when checking
* that KVM reads back what was written.
*/
- x.has_xavic_errata = host_cpu_is_amd &&
+ x.has_xavic_errata = host_cpu_is_amd_compatible &&
get_kvm_amd_param_bool("avic");
vcpu_clear_cpuid_feature(x.vcpu, X86_FEATURE_X2APIC);