diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-26 22:47:47 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-26 22:47:47 +0100 |
| commit | c7e79cf653a2a4b398d2af8be20d54d0b3c392cd (patch) | |
| tree | 00692d1d57f595182b3dae5650d4acf1c3400ccc /tools/testing/selftests | |
| parent | f660e5812a96dc8f780b9db8823a2432f5e1a4b3 (diff) | |
| parent | 567329869b9c75c9d3df74b35fe10af9b51c479d (diff) | |
| download | linux-next-c7e79cf653a2a4b398d2af8be20d54d0b3c392cd.tar.gz linux-next-c7e79cf653a2a4b398d2af8be20d54d0b3c392cd.zip | |
Merge branch 'next' of https://github.com/kvm-x86/linux.git
Diffstat (limited to 'tools/testing/selftests')
28 files changed, 639 insertions, 120 deletions
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 665f0be0a97e..eb8af7b8a29a 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -11,6 +11,7 @@ LIBKVM += lib/kvm_util.c LIBKVM += lib/lru_gen_util.c LIBKVM += lib/memstress.c LIBKVM += lib/guest_sprintf.c +LIBKVM += lib/proc_util.c LIBKVM += lib/rbtree.c LIBKVM += lib/sparsebit.c LIBKVM += lib/test_util.c @@ -157,6 +158,7 @@ TEST_GEN_PROGS_x86 += coalesced_io_test TEST_GEN_PROGS_x86 += dirty_log_perf_test TEST_GEN_PROGS_x86 += guest_memfd_test TEST_GEN_PROGS_x86 += hardware_disable_test +TEST_GEN_PROGS_x86 += irq_test TEST_GEN_PROGS_x86 += mmu_stress_test TEST_GEN_PROGS_x86 += rseq_test TEST_GEN_PROGS_x86 += steal_time @@ -260,6 +262,7 @@ OVERRIDE_TARGETS = 1 # which causes the environment variable to override the makefile). include ../lib.mk include ../cgroup/lib/libcgroup.mk +include ../vfio/lib/libvfio.mk INSTALL_HDR_PATH = $(top_srcdir)/usr LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/ @@ -314,7 +317,9 @@ LIBKVM_S := $(filter %.S,$(LIBKVM)) LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C)) LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S)) LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING)) -LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) $(LIBCGROUP_O) +LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) +LIBKVM_OBJS += $(LIBCGROUP_O) +LIBKVM_OBJS += $(LIBVFIO_O) SPLIT_TEST_GEN_PROGS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS)) SPLIT_TEST_GEN_OBJ := $(patsubst %, $(OUTPUT)/$(ARCH)/%.o, $(SPLIT_TESTS)) diff --git a/tools/testing/selftests/kvm/arch_timer.c b/tools/testing/selftests/kvm/arch_timer.c index 90c475a61b22..f8b02597897b 100644 --- a/tools/testing/selftests/kvm/arch_timer.c +++ b/tools/testing/selftests/kvm/arch_timer.c @@ -85,7 +85,7 @@ static u32 test_get_pcpu(void) cpu_set_t online_cpuset; nproc_conf = get_nprocs_conf(); - sched_getaffinity(0, sizeof(cpu_set_t), &online_cpuset); + kvm_sched_getaffinity(0, sizeof(cpu_set_t), &online_cpuset); /* Randomly find an available pCPU to place a vCPU on */ do { diff --git a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c index f7625eb711d6..d9c9377a6325 100644 --- a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c +++ b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c @@ -1039,7 +1039,7 @@ int main(int argc, char *argv[]) if (!parse_args(argc, argv)) exit(KSFT_SKIP); - sched_getaffinity(0, sizeof(default_cpuset), &default_cpuset); + kvm_sched_getaffinity(0, sizeof(default_cpuset), &default_cpuset); set_counter_defaults(); if (test_args.test_virtual) { diff --git a/tools/testing/selftests/kvm/demand_paging_test.c b/tools/testing/selftests/kvm/demand_paging_test.c index 302c4923d093..f8b3d0b68830 100644 --- a/tools/testing/selftests/kvm/demand_paging_test.c +++ b/tools/testing/selftests/kvm/demand_paging_test.c @@ -57,7 +57,7 @@ static void vcpu_worker(struct memstress_vcpu_args *vcpu_args) static int handle_uffd_page_request(int uffd_mode, int uffd, struct uffd_msg *msg) { - pid_t tid = syscall(__NR_gettid); + pid_t tid = kvm_gettid(); u64 addr = msg->arg.pagefault.address; struct timespec start; struct timespec ts_diff; diff --git a/tools/testing/selftests/kvm/dirty_log_perf_test.c b/tools/testing/selftests/kvm/dirty_log_perf_test.c index ef779fa91827..7c5abe1ae9e0 100644 --- a/tools/testing/selftests/kvm/dirty_log_perf_test.c +++ b/tools/testing/selftests/kvm/dirty_log_perf_test.c @@ -311,7 +311,7 @@ int main(int argc, char *argv[]) int opt; /* Override the seed to be deterministic by default. */ - guest_random_seed = 1; + kvm_random_seed = 1; dirty_log_manual_caps = kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); @@ -357,7 +357,7 @@ int main(int argc, char *argv[]) p.phys_offset = strtoull(optarg, NULL, 0); break; case 'r': - guest_random_seed = atoi_positive("Random seed", optarg); + kvm_random_seed = atoi_positive("Random seed", optarg); break; case 's': p.backing_src = parse_backing_src_type(optarg); diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c index 74ca096bf976..e8419d7da1ea 100644 --- a/tools/testing/selftests/kvm/dirty_log_test.c +++ b/tools/testing/selftests/kvm/dirty_log_test.c @@ -121,7 +121,7 @@ static void guest_code(void) while (true) { while (!READ_ONCE(vcpu_stop)) { addr = guest_test_virt_mem; - addr += (guest_random_u64(&guest_rng) % guest_num_pages) + addr += (kvm_random_u64(&kvm_rng) % guest_num_pages) * guest_page_size; addr = align_down(addr, host_page_size); @@ -708,8 +708,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) sync_global_to_guest(vm, iteration); - WRITE_ONCE(nr_writes, 0); - sync_global_to_guest(vm, nr_writes); + WRITE_AND_SYNC_TO_GUEST(vm, nr_writes, 0); dirty_ring_prev_iteration_last_page = dirty_ring_last_page; WRITE_ONCE(dirty_ring_vcpu_ring_full, false); @@ -775,16 +774,14 @@ static void run_test(enum vm_guest_mode mode, void *arg) * writing memory during verification, pages that this thread * sees as clean may be written with this iteration's value. */ - WRITE_ONCE(vcpu_stop, true); - sync_global_to_guest(vm, vcpu_stop); + WRITE_AND_SYNC_TO_GUEST(vm, vcpu_stop, true); sem_wait(&sem_vcpu_stop); /* * Clear vcpu_stop after the vCPU thread has acknowledge the * stop request and is waiting, i.e. is definitely not running! */ - WRITE_ONCE(vcpu_stop, false); - sync_global_to_guest(vm, vcpu_stop); + WRITE_AND_SYNC_TO_GUEST(vm, vcpu_stop, false); /* * Sync the number of writes performed before verification, the diff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h index 6cb3bed29b81..5dae6143ddb0 100644 --- a/tools/testing/selftests/kvm/include/kvm_syscalls.h +++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h @@ -12,6 +12,7 @@ #include <sys/mman.h> #include <sys/syscall.h> +#include <sched.h> #include <test_util.h> #define MAP_ARGS0(m,...) @@ -83,11 +84,17 @@ static inline int kvm_dup(int fd) return new_fd; } +static inline pid_t kvm_gettid(void) +{ + return syscall(__NR_gettid); +} + __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); +__KVM_SYSCALL_DEFINE(sched_getaffinity, 3, pid_t, pid, size_t, cpusetsize, cpu_set_t *, mask); #define kvm_free_fd(fd) \ do { \ diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 04a910164a29..b39e713c30a4 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -1094,6 +1094,8 @@ static inline void pin_task_to_cpu(pthread_t task, int cpu) TEST_ASSERT(!r, "Failed to set thread affinity to pCPU '%u'", cpu); } +void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus); + static inline int pin_task_to_any_cpu(pthread_t task) { int cpu = sched_getcpu(); @@ -1138,6 +1140,16 @@ vm_adjust_num_guest_pages(enum vm_guest_mode mode, unsigned int num_guest_pages) memcpy(&(g), _p, sizeof(g)); \ }) +#define SYNC_FROM_GUEST_AND_READ(_vm, _variable) ({ \ + sync_global_from_guest(_vm, _variable); \ + READ_ONCE(_variable); \ +}) + +#define WRITE_AND_SYNC_TO_GUEST(_vm, _variable, _value) do { \ + WRITE_ONCE(_variable, _value); \ + sync_global_to_guest(_vm, _variable); \ +} while (0) + /* * Write a global value, but only in the VM's (guest's) domain. Primarily used * for "globals" that hold per-VM values (VMs always duplicate code and global diff --git a/tools/testing/selftests/kvm/include/proc_util.h b/tools/testing/selftests/kvm/include/proc_util.h new file mode 100644 index 000000000000..d1ddc967d11d --- /dev/null +++ b/tools/testing/selftests/kvm/include/proc_util.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_PROC_UTIL_H +#define SELFTEST_KVM_PROC_UTIL_H + +#include <stdint.h> + +unsigned int vfio_msix_to_host_irq(const char *vfio_device_bdf, int msix); + +void proc_irq_set_smp_affinity(unsigned int irq, int cpu); + +#endif /* SELFTEST_KVM_PROC_UTIL_H */ diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h index a56271c237ae..d64c8a228207 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -108,32 +108,35 @@ struct timespec timespec_sub(struct timespec ts1, struct timespec ts2); struct timespec timespec_elapsed(struct timespec start); struct timespec timespec_div(struct timespec ts, int divisor); -struct guest_random_state { +struct kvm_random_state { u32 seed; }; -extern u32 guest_random_seed; -extern struct guest_random_state guest_rng; +extern u32 kvm_random_seed; +extern struct kvm_random_state kvm_rng; -struct guest_random_state new_guest_random_state(u32 seed); -u32 guest_random_u32(struct guest_random_state *state); +struct kvm_random_state new_kvm_random_state(u32 seed); +u32 kvm_random_u32(struct kvm_random_state *state); -static inline bool __guest_random_bool(struct guest_random_state *state, +static inline bool __kvm_random_bool(struct kvm_random_state *state, u8 percent) { - return (guest_random_u32(state) % 100) < percent; + return (kvm_random_u32(state) % 100) < percent; } -static inline bool guest_random_bool(struct guest_random_state *state) +static inline bool kvm_random_bool(struct kvm_random_state *state) { - return __guest_random_bool(state, 50); + return __kvm_random_bool(state, 50); } -static inline u64 guest_random_u64(struct guest_random_state *state) +static inline u64 kvm_random_u64(struct kvm_random_state *state) { - return ((u64)guest_random_u32(state) << 32) | guest_random_u32(state); + return ((u64)kvm_random_u32(state) << 32) | kvm_random_u32(state); } +u64 kvm_random_u64_in_range(struct kvm_random_state *state, u64 min, + u64 max); + enum vm_mem_backing_src_type { VM_MEM_SRC_ANONYMOUS, VM_MEM_SRC_ANONYMOUS_THP, diff --git a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h index c33ab6e04171..6904dbda79f9 100644 --- a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h +++ b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h @@ -55,9 +55,9 @@ static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch) do { \ const typeof(mem) val = (__val); \ \ - if (!is_forced_emulation_enabled || guest_random_bool(&guest_rng)) { \ + if (!is_forced_emulation_enabled || kvm_random_bool(&kvm_rng)) { \ (mem) = val; \ - } else if (guest_random_bool(&guest_rng)) { \ + } else if (kvm_random_bool(&kvm_rng)) { \ __asm__ __volatile__(KVM_FEP "mov %1, %0" \ : "+m" (mem) \ : "r" (val) : "memory"); \ diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 7d3a27bc0d84..b161174ece45 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -208,6 +208,7 @@ struct kvm_x86_cpu_feature { #define X86_FEATURE_PERFCTR_NB KVM_X86_CPU_FEATURE(0x80000001, 0, ECX, 24) #define X86_FEATURE_PERFCTR_LLC KVM_X86_CPU_FEATURE(0x80000001, 0, ECX, 28) #define X86_FEATURE_NX KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 20) +#define X86_FEATURE_FXSR_OPT KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 25) #define X86_FEATURE_GBPAGES KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 26) #define X86_FEATURE_RDTSCP KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 27) #define X86_FEATURE_LM KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 29) @@ -226,6 +227,7 @@ struct kvm_x86_cpu_feature { #define X86_FEATURE_SEV KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 1) #define X86_FEATURE_SEV_ES KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 3) #define X86_FEATURE_SEV_SNP KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 4) +#define X86_FEATURE_AUTOIBRS KVM_X86_CPU_FEATURE(0x80000021, 0, EAX, 8) #define X86_FEATURE_GP_ON_USER_CPUID KVM_X86_CPU_FEATURE(0x80000021, 0, EAX, 17) #define X86_FEATURE_PERFMON_V2 KVM_X86_CPU_FEATURE(0x80000022, 0, EAX, 0) #define X86_FEATURE_LBR_PMC_FREEZE KVM_X86_CPU_FEATURE(0x80000022, 0, EAX, 2) diff --git a/tools/testing/selftests/kvm/irq_test.c b/tools/testing/selftests/kvm/irq_test.c new file mode 100644 index 000000000000..240f6f0fdbe4 --- /dev/null +++ b/tools/testing/selftests/kvm/irq_test.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "kvm_util.h" +#include "test_util.h" +#include "apic.h" +#include "processor.h" +#include "proc_util.h" + +#include <libvfio.h> +#include <linux/sizes.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> +#include <sys/eventfd.h> +#include <sys/sysinfo.h> + +static u64 timeout_ns = 2ULL * 1000 * 1000 * 1000; +static bool guest_ready_for_irqs[KVM_MAX_VCPUS]; +static bool guest_received_irq[KVM_MAX_VCPUS]; +static bool guest_received_nmi[KVM_MAX_VCPUS]; +static bool x2apic = true; +static bool irq_affinity; +static bool done; + +#define GUEST_RECEIVED_IRQ(__vcpu) \ + SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_irq[(__vcpu)->id]) +#define GUEST_RECEIVED_NMI(__vcpu) \ + SYNC_FROM_GUEST_AND_READ((__vcpu)->vm, guest_received_nmi[(__vcpu)->id]) + +#define GUEST_RECEIVED_INTERRUPT(__vcpu, __nmi) \ + ((__nmi) ? GUEST_RECEIVED_NMI(__vcpu) : GUEST_RECEIVED_IRQ(__vcpu)) + +static u32 guest_get_vcpu_id(void) +{ + if (x2apic) + return x2apic_read_reg(APIC_ID); + else + return xapic_read_reg(APIC_ID) >> 24; +} + +static void guest_irq_handler(struct ex_regs *regs) +{ + WRITE_ONCE(guest_received_irq[guest_get_vcpu_id()], true); + + if (x2apic) + x2apic_write_reg(APIC_EOI, 0); + else + xapic_write_reg(APIC_EOI, 0); +} + +static void guest_nmi_handler(struct ex_regs *regs) +{ + WRITE_ONCE(guest_received_nmi[guest_get_vcpu_id()], true); +} + +static void guest_code(void) +{ + if (x2apic) + x2apic_enable(); + else + xapic_enable(); + + sti_nop(); + + WRITE_ONCE(guest_ready_for_irqs[guest_get_vcpu_id()], true); + + while (!READ_ONCE(done)) + cpu_relax(); + + GUEST_DONE(); +} + +static void *vcpu_thread_main(void *arg) +{ + struct kvm_vcpu *vcpu = arg; + struct ucall uc; + + vcpu_run(vcpu); + TEST_ASSERT_EQ(UCALL_DONE, get_ucall(vcpu, &uc)); + + return NULL; +} + +static int vfio_setup_msi(struct vfio_pci_device *device) +{ + const int flags = MAP_SHARED | MAP_ANONYMOUS; + const int prot = PROT_READ | PROT_WRITE; + struct iova_allocator *allocator; + struct dma_region *region; + + /* Sanity check that the device+driver can actually send MSIs. */ + TEST_REQUIRE(device->driver.ops); + TEST_REQUIRE(device->driver.ops->send_msi); + + /* + * Set up a DMA-able region for the driver to use. Very few devices + * provide a way to arbitrarily send interrupts (MSIs), e.g. by writing + * an MMIO register. Instead, most devices send MSIs when an action is + * completed, and practically all actions involve DMA of some form. + */ + allocator = iova_allocator_init(device->iommu); + + region = &device->driver.region; + region->size = SZ_2M; + region->iova = iova_allocator_alloc(allocator, region->size); + region->vaddr = kvm_mmap(region->size, prot, flags, -1); + TEST_ASSERT(region->vaddr != MAP_FAILED, "mmap() failed\n"); + iommu_map(device->iommu, region); + + iova_allocator_cleanup(allocator); + + vfio_pci_driver_init(device); + + return device->driver.msi; +} + +static void trigger_interrupt(struct vfio_pci_device *device, int eventfd) +{ + if (device) + vfio_pci_driver_send_msi(device); + else + eventfd_write(eventfd, 1); +} + + +static void kvm_route_msi(struct kvm_vm *vm, u32 gsi, struct kvm_vcpu *vcpu, + u8 vector, bool use_nmi) +{ + struct { + struct kvm_irq_routing header; + struct kvm_irq_routing_entry entry; + } routing = { + .header.nr = 1, + .entry = { + .gsi = gsi, + .type = KVM_IRQ_ROUTING_MSI, + .u.msi.address_lo = 0xFEE00000 | (vcpu->id & GENMASK(7, 0)) << 12, + .u.msi.address_hi = vcpu->id & GENMASK(31, 8), + .u.msi.data = use_nmi ? NMI_VECTOR | (4 << 8) : vector, + }, + }; + + vm_ioctl(vm, KVM_SET_GSI_ROUTING, &routing.header); +} + +static void kvm_set_empty_gsi_routing(struct kvm_vm *vm) +{ + struct kvm_irq_routing routing = {}; + + vm_ioctl(vm, KVM_SET_GSI_ROUTING, &routing); +} + +static const char *probe_iommu_type(void) +{ + int io_fd; + + io_fd = open("/dev/iommu", O_RDONLY); + if (io_fd >= 0) { + close(io_fd); + return MODE_IOMMUFD; + } + + io_fd = __open_path_or_exit("/dev/vfio/vfio", O_RDONLY, + "Is VFIO (or IOMMUFD) loaded and enabled?"); + close(io_fd); + return MODE_VFIO_TYPE1_IOMMU; +} + +static void help(const char *name) +{ + printf("Usage: %s [-a] [-d <segment:bus:device.function>] [-e] [-h] [-i nr_irqs] [-m] [-n] [-t iommu_type] [-v nr_vcpus] [-x]\n", name); + printf("\n"); + printf("Tests KVM interrupt routing and delivery via irqfd.\n"); + printf("-a Affine the device's host IRQ to a random physical CPU\n"); + printf("-d Use a VFIO device to send MSI-X interrupts instead of manually signaling the eventfd\n"); + printf("-e Set empty GSI routing in-between some interrupts\n"); + printf("-i The number of IRQs to generate during the test\n"); + printf("-m Pin target vCPU to random physical CPU before triggering interrupt\n"); + printf("-n Deliver 50 percent of IRQs as non-maskable interrupts\n"); + printf("-t Override the IOMMU type to use (vfio_type1_iommu or iommufd)\n"); + printf("-v Number of vCPUS to run\n"); + printf("-x Use xAPIC mode instead of x2APIC mode in the guest\n"); + printf("\n"); + exit(KSFT_FAIL); +} + +int main(int argc, char **argv) +{ + /* + * Pick a random vector and a random GSI to use for device IRQ. + * + * Pick an IRQ vector in range [32, UINT8_MAX]. Min value is 32 because + * Linux/x86 reserves vectors 0-31 for exceptions and architecture + * defined NMIs and interrupts. + * + * Pick a GSI in range [24, KVM_MAX_IRQ_ROUTES - 1]. The min value is 24 + * because KVM reserves GSIs 0-15 for legacy ISA IRQs and 16-23 only go + * to the IOAPIC. The max is KVM_MAX_IRQ_ROUTES - 1, because + * KVM_MAX_IRQ_ROUTES is exclusive. + */ + u32 gsi = kvm_random_u64_in_range(&kvm_rng, 24, KVM_MAX_IRQ_ROUTES - 1); + u8 vector = kvm_random_u64_in_range(&kvm_rng, 32, UINT8_MAX); + + pthread_t vcpu_threads[KVM_MAX_VCPUS]; + struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; + struct vfio_pci_device *device = NULL; + int nr_irqs = 1000, nr_vcpus = 1; + bool set_empty_routing = false; + const char *device_bdf = NULL; + const char *iommu_type = NULL; + int i, j, c, msix, eventfd; + bool migrate_vcpus = false; + cpu_set_t available_cpus; + bool use_nmi = false; + struct iommu *iommu; + struct kvm_vm *vm; + int irq, irq_cpu; + + while ((c = getopt(argc, argv, "ad:ehi:mnt:v:x")) != -1) { + switch (c) { + case 'a': + irq_affinity = true; + break; + case 'd': + device_bdf = optarg; + break; + case 'e': + set_empty_routing = true; + break; + case 'i': + nr_irqs = atoi_positive("Number of IRQs", optarg); + break; + case 'm': + migrate_vcpus = true; + break; + case 'n': + use_nmi = true; + break; + case 't': + iommu_type = optarg; + break; + case 'v': + nr_vcpus = atoi_positive("Number of vCPUS", optarg); + TEST_ASSERT(nr_vcpus <= KVM_MAX_VCPUS, + "KVM selftests support at most %u vCPUs", KVM_MAX_VCPUS); + break; + case 'x': + x2apic = false; + break; + case 'h': + default: + help(argv[0]); + } + } + + TEST_REQUIRE(kvm_arch_has_default_irqchip()); + + vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); + vm_enable_cap(vm, KVM_CAP_X2APIC_API, KVM_X2APIC_API_USE_32BIT_IDS | + KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK); + + vm_install_exception_handler(vm, vector, guest_irq_handler); + vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler); + + if (!x2apic) { + TEST_ASSERT(nr_vcpus < 256, "xAPIC can only target IDs [0-254] (255 vCPUs)"); + virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); + } + + if (device_bdf) { + if (!iommu_type) + iommu_type = probe_iommu_type(); + iommu = iommu_init(iommu_type); + device = vfio_pci_device_init(device_bdf, iommu); + msix = vfio_setup_msi(device); + irq = vfio_msix_to_host_irq(device_bdf, msix); + eventfd = device->msi_eventfds[msix]; + printf("Using device %s MSI-X[%d] (IRQ-%u)\n", device_bdf, msix, + irq); + } else { + TEST_ASSERT(!irq_affinity, + "Setting IRQ affinity (-a) requires a backing device (-d)"); + + eventfd = kvm_new_eventfd(); + irq = -1; + } + + pr_info("Injecting interrupts for GSI %d (guest vector 0x%x) %d times\n", + gsi, vector, nr_irqs); + + kvm_assign_irqfd(vm, gsi, eventfd); + + sync_global_to_guest(vm, x2apic); + + if (migrate_vcpus) + kvm_sched_getaffinity(0, sizeof(available_cpus), &available_cpus); + + for (i = 0; i < nr_vcpus; i++) + pthread_create(&vcpu_threads[i], NULL, vcpu_thread_main, vcpus[i]); + + for (i = 0; i < nr_vcpus; i++) { + struct kvm_vcpu *vcpu = vcpus[i]; + + while (!SYNC_FROM_GUEST_AND_READ(vm, guest_ready_for_irqs[vcpu->id])) + continue; + } + + irq_cpu = -1; + + for (i = 0; i < nr_irqs; i++) { + const bool do_set_empty_routing = set_empty_routing && (i & BIT(3)); + const bool do_use_nmi = use_nmi && (i & BIT(2)); + struct kvm_vcpu *vcpu = vcpus[i % nr_vcpus]; + struct timespec start; + + if (do_set_empty_routing) + kvm_set_empty_gsi_routing(vm); + + kvm_route_msi(vm, gsi, vcpu, vector, do_use_nmi); + + if (irq_affinity) { + irq_cpu = kvm_random_u64(&kvm_rng) % get_nprocs(); + proc_irq_set_smp_affinity(irq, irq_cpu); + } + + if (migrate_vcpus) + pin_task_to_random_cpu(vcpu_threads[i % nr_vcpus], &available_cpus); + + for (j = 0; j < nr_vcpus; j++) { + TEST_ASSERT(!GUEST_RECEIVED_IRQ(vcpus[j]), + "IRQ flag for vCPU %d not clear prior to test", + vcpus[j]->id); + TEST_ASSERT(!GUEST_RECEIVED_NMI(vcpus[j]), + "NMI flag for vCPU %d not clear prior to test", + vcpus[j]->id); + } + + trigger_interrupt(device, eventfd); + + clock_gettime(CLOCK_MONOTONIC, &start); + while (!GUEST_RECEIVED_INTERRUPT(vcpu, do_use_nmi) && + timespec_to_ns(timespec_elapsed(start)) <= timeout_ns) + cpu_relax(); + + TEST_ASSERT(GUEST_RECEIVED_INTERRUPT(vcpu, do_use_nmi), + "vCPU %d timed out waiting for %s (vector 0x%x) from GSI %d (via CPU %d)\n", + vcpu->id, do_use_nmi ? "NMI" : "IRQ", + do_use_nmi ? NMI_VECTOR : vector, gsi, irq_cpu); + + if (do_use_nmi) + WRITE_AND_SYNC_TO_GUEST(vm, guest_received_nmi[vcpu->id], false); + else + WRITE_AND_SYNC_TO_GUEST(vm, guest_received_irq[vcpu->id], false); + } + + WRITE_AND_SYNC_TO_GUEST(vm, done, true); + + for (i = 0; i < nr_vcpus; i++) + pthread_join(vcpu_threads[i], NULL); + + return 0; +} diff --git a/tools/testing/selftests/kvm/lib/assert.c b/tools/testing/selftests/kvm/lib/assert.c index 8be0d09ecf0f..1d72dcdfce3b 100644 --- a/tools/testing/selftests/kvm/lib/assert.c +++ b/tools/testing/selftests/kvm/lib/assert.c @@ -10,6 +10,7 @@ #include <sys/syscall.h> #include "kselftest.h" +#include "kvm_syscalls.h" #ifdef __GLIBC__ #include <execinfo.h> @@ -64,11 +65,6 @@ static void test_dump_stack(void) static void test_dump_stack(void) {} #endif -static pid_t _gettid(void) -{ - return syscall(SYS_gettid); -} - void __attribute__((noinline)) test_assert(bool exp, const char *exp_str, const char *file, unsigned int line, const char *fmt, ...) @@ -81,7 +77,7 @@ test_assert(bool exp, const char *exp_str, fprintf(stderr, "==== Test Assertion Failure ====\n" " %s:%u: %s\n" " pid=%d tid=%d errno=%d - %s\n", - file, line, exp_str, getpid(), _gettid(), + file, line, exp_str, getpid(), kvm_gettid(), errno, strerror(errno)); test_dump_stack(); if (fmt) { diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 195f3fdae1e3..3794575d2ca0 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -15,14 +15,22 @@ #include <sys/resource.h> #include <sys/types.h> #include <sys/stat.h> +#include <time.h> #include <unistd.h> #include <linux/kernel.h> #define KVM_UTIL_MIN_PFN 2 -u32 guest_random_seed; -struct guest_random_state guest_rng; -static u32 last_guest_seed; +u32 kvm_random_seed; +struct kvm_random_state kvm_rng; +static u32 last_kvm_seed; + +static void kvm_seed_rng(u32 seed) +{ + kvm_random_seed = last_kvm_seed = seed; + pr_info("Random seed: 0x%x\n", kvm_random_seed); + kvm_rng = new_kvm_random_state(kvm_random_seed); +} static size_t vcpu_mmap_sz(void); @@ -515,12 +523,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, slot0 = memslot2region(vm, 0); ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size); - if (guest_random_seed != last_guest_seed) { - pr_info("Random seed: 0x%x\n", guest_random_seed); - last_guest_seed = guest_random_seed; - } - guest_rng = new_guest_random_state(guest_random_seed); - sync_global_to_guest(vm, guest_rng); + if (kvm_random_seed != last_kvm_seed) + kvm_seed_rng(kvm_random_seed); + + sync_global_to_guest(vm, kvm_rng); kvm_arch_vm_post_create(vm, nr_runnable_vcpus); @@ -662,19 +668,39 @@ void kvm_print_vcpu_pinning_help(void) " (default: no pinning)\n", name, name); } +void pin_task_to_random_cpu(pthread_t task, cpu_set_t *possible_cpus) +{ + int target_idx; + int nr_cpus; + int cpu; + + nr_cpus = CPU_COUNT(possible_cpus); + TEST_ASSERT(nr_cpus > 0, "No CPUs available in possible_cpus"); + + target_idx = kvm_random_u64(&kvm_rng) % nr_cpus; + + for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { + if (CPU_ISSET(cpu, possible_cpus) && target_idx-- == 0) { + pin_task_to_cpu(task, cpu); + return; + } + } + + TEST_FAIL("Failed to find random CPU in possible_cpus"); +} + void kvm_parse_vcpu_pinning(const char *pcpus_string, u32 vcpu_to_pcpu[], int nr_vcpus) { cpu_set_t allowed_mask; char *cpu, *cpu_list; char delim[2] = ","; - int i, r; + int i; cpu_list = strdup(pcpus_string); TEST_ASSERT(cpu_list, "strdup() allocation failed."); - r = sched_getaffinity(0, sizeof(allowed_mask), &allowed_mask); - TEST_ASSERT(!r, "sched_getaffinity() failed"); + kvm_sched_getaffinity(0, sizeof(allowed_mask), &allowed_mask); cpu = strtok(cpu_list, delim); @@ -2279,8 +2305,8 @@ void __attribute((constructor)) kvm_selftest_init(void) sigaction(SIGILL, &sig_sa, NULL); sigaction(SIGFPE, &sig_sa, NULL); - guest_random_seed = last_guest_seed = random(); - pr_info("Random seed: 0x%x\n", guest_random_seed); + srandom(time(0)); + kvm_seed_rng(random()); kvm_selftest_arch_init(); } diff --git a/tools/testing/selftests/kvm/lib/memstress.c b/tools/testing/selftests/kvm/lib/memstress.c index 6dcd15910a06..3599b75d97c9 100644 --- a/tools/testing/selftests/kvm/lib/memstress.c +++ b/tools/testing/selftests/kvm/lib/memstress.c @@ -48,14 +48,14 @@ void memstress_guest_code(u32 vcpu_idx) { struct memstress_args *args = &memstress_args; struct memstress_vcpu_args *vcpu_args = &args->vcpu_args[vcpu_idx]; - struct guest_random_state rand_state; + struct kvm_random_state rand_state; gva_t gva; u64 pages; u64 addr; u64 page; int i; - rand_state = new_guest_random_state(guest_random_seed + vcpu_idx); + rand_state = new_kvm_random_state(kvm_random_seed + vcpu_idx); gva = vcpu_args->gva; pages = vcpu_args->pages; @@ -69,13 +69,13 @@ void memstress_guest_code(u32 vcpu_idx) for (i = 0; i < pages; i++) { if (args->random_access) - page = guest_random_u32(&rand_state) % pages; + page = kvm_random_u32(&rand_state) % pages; else page = i; addr = gva + (page * args->guest_page_size); - if (__guest_random_bool(&rand_state, args->write_percent)) + if (__kvm_random_bool(&rand_state, args->write_percent)) *(u64 *)addr = 0x0123456789ABCDEF; else READ_ONCE(*(u64 *)addr); diff --git a/tools/testing/selftests/kvm/lib/proc_util.c b/tools/testing/selftests/kvm/lib/proc_util.c new file mode 100644 index 000000000000..3960b3841d63 --- /dev/null +++ b/tools/testing/selftests/kvm/lib/proc_util.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "kvm_util.h" +#include "test_util.h" +#include "proc_util.h" + +static FILE *open_proc_interrupts(void) +{ + FILE *fp; + + fp = fopen("/proc/interrupts", "r"); + TEST_ASSERT(fp, "fopen(/proc/interrupts) failed"); + + return fp; +} + +unsigned int vfio_msix_to_host_irq(const char *device_bdf, int msix) +{ + char search_string[64]; + char line[4096]; + int irq = -1; + FILE *fp; + + fp = open_proc_interrupts(); + + snprintf(search_string, sizeof(search_string), "vfio-msix[%d]", msix); + + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, device_bdf) && strstr(line, search_string)) { + TEST_ASSERT_EQ(1, sscanf(line, "%d:", &irq)); + break; + } + } + + fclose(fp); + + TEST_ASSERT(irq != -1, "Failed to locate IRQ for %s %s", device_bdf, + search_string); + return (unsigned int)irq; +} + +void proc_irq_set_smp_affinity(unsigned int irq, int cpu) +{ + char path[PATH_MAX]; + int r, fd; + + snprintf(path, sizeof(path), "/proc/irq/%u/smp_affinity_list", irq); + fd = open(path, O_RDWR); + TEST_ASSERT(fd >= 0, "Failed to open %s", path); + + r = dprintf(fd, "%d\n", cpu); + TEST_ASSERT(r > 0, "Failed to affinitize IRQ-%u to CPU %d", irq, cpu); + + kvm_close(fd); +} diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c index bab1bd2b775b..6b00ab11f3c0 100644 --- a/tools/testing/selftests/kvm/lib/test_util.c +++ b/tools/testing/selftests/kvm/lib/test_util.c @@ -17,6 +17,7 @@ #include "linux/kernel.h" #include "test_util.h" +#include "kvm_syscalls.h" sigjmp_buf expect_sigbus_jmpbuf; @@ -30,18 +31,36 @@ void __attribute__((used)) expect_sigbus_handler(int signum) * Park-Miller LCG using standard constants. */ -struct guest_random_state new_guest_random_state(u32 seed) +struct kvm_random_state new_kvm_random_state(u32 seed) { - struct guest_random_state s = {.seed = seed}; + struct kvm_random_state s = {.seed = seed}; return s; } -u32 guest_random_u32(struct guest_random_state *state) +u32 kvm_random_u32(struct kvm_random_state *state) { state->seed = (u64)state->seed * 48271 % ((u32)(1 << 31) - 1); return state->seed; } +/* Returns a random u64 in the inclusive range [min, max] */ +u64 kvm_random_u64_in_range(struct kvm_random_state *state, u64 min, + u64 max) +{ + u64 value; + u64 range; + + TEST_ASSERT(min <= max, "PEBKAC, min = 0x%lx, max = 0x%lx", min, max); + + value = kvm_random_u64(state); + + range = max - min; + if (range == ULLONG_MAX) + return value; + + return min + (value % (range + 1)); +} + /* * Parses "[0-9]+[kmgt]?". */ @@ -377,7 +396,7 @@ long get_run_delay(void) long val[2]; FILE *fp; - sprintf(path, "/proc/%ld/schedstat", syscall(SYS_gettid)); + sprintf(path, "/proc/%ld/schedstat", (long)kvm_gettid()); fp = fopen(path, "r"); /* Return MIN_RUN_DELAY_NS upon failure just to be safe */ if (fscanf(fp, "%ld %ld ", &val[0], &val[1]) < 2) diff --git a/tools/testing/selftests/kvm/mmu_stress_test.c b/tools/testing/selftests/kvm/mmu_stress_test.c index 54d281419d31..3d5f33a63b2b 100644 --- a/tools/testing/selftests/kvm/mmu_stress_test.c +++ b/tools/testing/selftests/kvm/mmu_stress_test.c @@ -155,10 +155,8 @@ static void *vcpu_worker(void *data) "Expected EFAULT on write to RO memory, got r = %d, errno = %d", r, errno); atomic_inc(&nr_ro_faults); - if (atomic_read(&nr_ro_faults) == nr_vcpus) { - WRITE_ONCE(all_vcpus_hit_ro_fault, true); - sync_global_to_guest(vm, all_vcpus_hit_ro_fault); - } + if (atomic_read(&nr_ro_faults) == nr_vcpus) + WRITE_AND_SYNC_TO_GUEST(vm, all_vcpus_hit_ro_fault, true); #if defined(__x86_64__) || defined(__aarch64__) /* @@ -257,11 +255,7 @@ static void rendezvous_with_vcpus(struct timespec *time, const char *name) static void calc_default_nr_vcpus(void) { cpu_set_t possible_mask; - int r; - - r = sched_getaffinity(0, sizeof(possible_mask), &possible_mask); - TEST_ASSERT(!r, "sched_getaffinity failed, errno = %d (%s)", - errno, strerror(errno)); + kvm_sched_getaffinity(0, sizeof(possible_mask), &possible_mask); nr_vcpus = CPU_COUNT(&possible_mask); TEST_ASSERT(nr_vcpus > 0, "Uh, no CPUs?"); @@ -383,8 +377,7 @@ int main(int argc, char *argv[]) rendezvous_with_vcpus(&time_run2, "run 2"); mprotect(mem, slot_size, PROT_READ); - mprotect_ro_done = true; - sync_global_to_guest(vm, mprotect_ro_done); + WRITE_AND_SYNC_TO_GUEST(vm, mprotect_ro_done, true); rendezvous_with_vcpus(&time_ro, "mprotect RO"); mprotect(mem, slot_size, PROT_READ | PROT_WRITE); diff --git a/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c b/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c index e56a3dd6a51e..20388f0b959d 100644 --- a/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c +++ b/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c @@ -492,7 +492,7 @@ static void test_pmu_events_snaphost(void) struct riscv_pmu_snapshot_data *snapshot_data = snapshot_gva; int i; - /* Verify presence of SBI PMU and minimum requrired SBI version */ + /* Verify presence of SBI PMU and minimum required SBI version */ verify_sbi_requirement_assert(); snapshot_set_shmem(snapshot_gpa, 0); @@ -518,7 +518,7 @@ static void test_pmu_events_overflow(void) { int num_counters = 0, i = 0; - /* Verify presence of SBI PMU and minimum requrired SBI version */ + /* Verify presence of SBI PMU and minimum required SBI version */ verify_sbi_requirement_assert(); snapshot_set_shmem(snapshot_gpa, 0); diff --git a/tools/testing/selftests/kvm/rseq_test.c b/tools/testing/selftests/kvm/rseq_test.c index f80ad6b47d16..557e393c223b 100644 --- a/tools/testing/selftests/kvm/rseq_test.c +++ b/tools/testing/selftests/kvm/rseq_test.c @@ -226,9 +226,7 @@ int main(int argc, char *argv[]) } } - r = sched_getaffinity(0, sizeof(possible_mask), &possible_mask); - TEST_ASSERT(!r, "sched_getaffinity failed, errno = %d (%s)", errno, - strerror(errno)); + kvm_sched_getaffinity(0, sizeof(possible_mask), &possible_mask); calc_min_max_cpu(); @@ -244,7 +242,7 @@ int main(int argc, char *argv[]) vm = vm_create_with_one_vcpu(&vcpu, guest_code); pthread_create(&migration_thread, NULL, migration_worker, - (void *)(unsigned long)syscall(SYS_gettid)); + (void *)(unsigned long)kvm_gettid()); if (latency >= 0) { /* diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c index 76fcdd1fd3cb..0b88c743ee7d 100644 --- a/tools/testing/selftests/kvm/steal_time.c +++ b/tools/testing/selftests/kvm/steal_time.c @@ -70,8 +70,8 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu) static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) { /* ST_GPA_BASE is identity mapped */ - st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); - sync_global_to_guest(vcpu->vm, st_gva[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); } @@ -187,8 +187,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) }; /* ST_GPA_BASE is identity mapped */ - st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); - sync_global_to_guest(vm, st_gva[i]); + WRITE_AND_SYNC_TO_GUEST(vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE)); st_ipa = (ulong)st_gva[i]; vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); @@ -310,10 +309,8 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu) static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) { /* ST_GPA_BASE is identity mapped */ - st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); - st_gpa[i] = addr_gva2gpa(vcpu->vm, (gva_t)st_gva[i]); - sync_global_to_guest(vcpu->vm, st_gva[i]); - sync_global_to_guest(vcpu->vm, st_gpa[i]); + WRITE_AND_SYNC_TO_GUEST(vcpu->vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE)); + WRITE_AND_SYNC_TO_GUEST(vcpu->vm, st_gpa[i], addr_gva2gpa(vcpu->vm, (gva_t)st_gva[i])); } static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) @@ -442,8 +439,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) }; /* ST_GPA_BASE is identity mapped */ - st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); - sync_global_to_guest(vm, st_gva[i]); + WRITE_AND_SYNC_TO_GUEST(vm, st_gva[i], (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE)); err = __vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &attr); TEST_ASSERT(err == 0, "No PV stealtime Feature"); @@ -508,23 +504,18 @@ int main(int ac, char **av) { struct kvm_vcpu *vcpus[NR_VCPUS]; struct kvm_vm *vm; - pthread_attr_t attr; pthread_t thread; cpu_set_t cpuset; unsigned int gpages; long stolen_time; long run_delay; bool verbose; - int i; + int i, cpu; verbose = ac > 1 && (!strncmp(av[1], "-v", 3) || !strncmp(av[1], "--verbose", 10)); /* Set CPU affinity so we can force preemption of the VCPU */ - CPU_ZERO(&cpuset); - CPU_SET(0, &cpuset); - pthread_attr_init(&attr); - pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); - pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); + cpu = pin_self_to_any_cpu(); /* Create a VM and an identity mapped memslot for the steal time structure */ vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus); @@ -549,8 +540,7 @@ int main(int ac, char **av) /* Second VCPU run, expect guest stolen time to be <= run_delay */ run_vcpu(vcpus[i]); - sync_global_from_guest(vm, guest_stolen_time[i]); - stolen_time = guest_stolen_time[i]; + stolen_time = SYNC_FROM_GUEST_AND_READ(vm, guest_stolen_time[i]); run_delay = get_run_delay(); TEST_ASSERT(stolen_time <= run_delay, "Expected stolen time <= %ld, got %ld", @@ -558,7 +548,11 @@ int main(int ac, char **av) /* Steal time from the VCPU. The steal time thread has the same CPU affinity as the VCPUs. */ run_delay = get_run_delay(); - pthread_create(&thread, &attr, do_steal_time, NULL); + pthread_create(&thread, NULL, do_steal_time, NULL); + pthread_getaffinity_np(thread, sizeof(cpuset), &cpuset); + TEST_ASSERT(CPU_COUNT(&cpuset) == 1 && CPU_ISSET(cpu, &cpuset), + "Worker failed to inherit parent's CPU affinity"); + do sched_yield(); while (get_run_delay() - run_delay < MIN_RUN_DELAY_NS); @@ -570,8 +564,7 @@ int main(int ac, char **av) /* Run VCPU again to confirm stolen time is consistent with run_delay */ run_vcpu(vcpus[i]); - sync_global_from_guest(vm, guest_stolen_time[i]); - stolen_time = guest_stolen_time[i] - stolen_time; + stolen_time = SYNC_FROM_GUEST_AND_READ(vm, guest_stolen_time[i]) - stolen_time; TEST_ASSERT(stolen_time >= run_delay, "Expected stolen time >= %ld, got %ld", run_delay, stolen_time); diff --git a/tools/testing/selftests/kvm/x86/hyperv_clock.c b/tools/testing/selftests/kvm/x86/hyperv_clock.c index c083cea546dc..d5d779623cc6 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_clock.c +++ b/tools/testing/selftests/kvm/x86/hyperv_clock.c @@ -56,7 +56,7 @@ static inline void check_tsc_msr_rdtsc(void) tsc_freq = rdmsr(HV_X64_MSR_TSC_FREQUENCY); GUEST_ASSERT(tsc_freq > 0); - /* For increased accuracy, take mean rdtsc() before and afrer rdmsr() */ + /* For increased accuracy, take mean rdtsc() before and after rdmsr() */ r1 = rdtsc(); t1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT); r1 = (r1 + rdtsc()) / 2; @@ -181,7 +181,7 @@ static void host_check_tsc_msr_rdtsc(struct kvm_vcpu *vcpu) tsc_freq = vcpu_get_msr(vcpu, HV_X64_MSR_TSC_FREQUENCY); TEST_ASSERT(tsc_freq > 0, "TSC frequency must be nonzero"); - /* For increased accuracy, take mean rdtsc() before and afrer ioctl */ + /* For increased accuracy, take mean rdtsc() before and after ioctl */ r1 = rdtsc(); t1 = vcpu_get_msr(vcpu, HV_X64_MSR_TIME_REF_COUNT); r1 = (r1 + rdtsc()) / 2; diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 1bda2cd3f739..63ea1533e4ea 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -125,7 +125,7 @@ void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, /* * NMI forces L2->L1 exit, resuming L2 and hope that EVMCS is * up-to-date (RIP points where it should and not at the beginning - * of l2_guest_code(). GUEST_SYNC(9) checkes that. + * of l2_guest_code(). GUEST_SYNC(9) checks that. */ GUEST_ASSERT(!vmresume()); diff --git a/tools/testing/selftests/kvm/x86/set_sregs_test.c b/tools/testing/selftests/kvm/x86/set_sregs_test.c index 8e654cc9ab16..162692b3e663 100644 --- a/tools/testing/selftests/kvm/x86/set_sregs_test.c +++ b/tools/testing/selftests/kvm/x86/set_sregs_test.c @@ -21,20 +21,20 @@ #include "kvm_util.h" #include "processor.h" -#define TEST_INVALID_CR_BIT(vcpu, cr, orig, bit) \ +#define TEST_INVALID_SREG_BIT(vcpu, reg, orig, bit) \ do { \ struct kvm_sregs new; \ int rc; \ \ /* Skip the sub-test, the feature/bit is supported. */ \ - if (orig.cr & bit) \ + if (orig.reg & bit) \ break; \ \ - memcpy(&new, &orig, sizeof(sregs)); \ - new.cr |= bit; \ + memcpy(&new, &orig, sizeof(new)); \ + new.reg |= bit; \ \ rc = _vcpu_sregs_set(vcpu, &new); \ - TEST_ASSERT(rc, "KVM allowed invalid " #cr " bit (0x%lx)", bit); \ + TEST_ASSERT(rc, "KVM allowed invalid " #reg " bit (0x%llx)", (u64)bit); \ \ /* Sanity check that KVM didn't change anything. */ \ vcpu_sregs_get(vcpu, &new); \ @@ -46,6 +46,8 @@ do { \ X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | \ X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT) +#define KVM_ALWAYS_ALLOWED_EFER EFER_SCE + static u64 calc_supported_cr4_feature_bits(void) { u64 cr4 = KVM_ALWAYS_ALLOWED_CR4; @@ -74,6 +76,24 @@ static u64 calc_supported_cr4_feature_bits(void) return cr4; } +static u64 calc_supported_efer_feature_bits(void) +{ + u64 efer = KVM_ALWAYS_ALLOWED_EFER; + + if (kvm_cpu_has(X86_FEATURE_LM)) + efer |= (EFER_LME | EFER_LMA); + if (kvm_cpu_has(X86_FEATURE_NX)) + efer |= EFER_NX; + if (kvm_cpu_has(X86_FEATURE_SVM)) + efer |= EFER_SVME; + if (kvm_cpu_has(X86_FEATURE_FXSR_OPT)) + efer |= EFER_FFXSR; + if (kvm_cpu_has(X86_FEATURE_AUTOIBRS)) + efer |= EFER_AUTOIBRS; + + return efer; +} + static void test_cr_bits(struct kvm_vcpu *vcpu, u64 cr4) { struct kvm_sregs sregs; @@ -96,26 +116,45 @@ static void test_cr_bits(struct kvm_vcpu *vcpu, u64 cr4) (sregs.cr4 & X86_CR4_PKE) ? "set" : "clear"); vcpu_sregs_get(vcpu, &sregs); - TEST_ASSERT(sregs.cr4 == cr4, "sregs.CR4 (0x%llx) != CR4 (0x%lx)", - sregs.cr4, cr4); - - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_UMIP); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_LA57); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_VMXE); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMXE); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_FSGSBASE); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_PCIDE); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_OSXSAVE); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMEP); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_SMAP); - TEST_INVALID_CR_BIT(vcpu, cr4, sregs, X86_CR4_PKE); + TEST_ASSERT_EQ(sregs.cr4, cr4); + + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_UMIP); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_LA57); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_VMXE); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMXE); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_FSGSBASE); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_PCIDE); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_OSXSAVE); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMEP); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_SMAP); + TEST_INVALID_SREG_BIT(vcpu, cr4, sregs, X86_CR4_PKE); for (i = 32; i < 64; i++) - TEST_INVALID_CR_BIT(vcpu, cr0, sregs, BIT(i)); + TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, BIT(i)); /* NW without CD is illegal, as is PG without PE. */ - TEST_INVALID_CR_BIT(vcpu, cr0, sregs, X86_CR0_NW); - TEST_INVALID_CR_BIT(vcpu, cr0, sregs, X86_CR0_PG); + TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, X86_CR0_NW); + TEST_INVALID_SREG_BIT(vcpu, cr0, sregs, X86_CR0_PG); +} + +static void test_efer_bits(struct kvm_vcpu *vcpu, u64 efer) +{ + struct kvm_sregs sregs; + int rc; + + vcpu_sregs_get(vcpu, &sregs); + sregs.efer |= efer; + rc = _vcpu_sregs_set(vcpu, &sregs); + TEST_ASSERT(!rc, "Failed to set supported EFER bits (0x%llx)", sregs.efer); + + vcpu_sregs_get(vcpu, &sregs); + TEST_ASSERT_EQ(sregs.efer, efer); + + TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_LME); + TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_NX); + TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_SVME); + TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_FFXSR); + TEST_INVALID_SREG_BIT(vcpu, efer, sregs, EFER_AUTOIBRS); } int main(int argc, char *argv[]) @@ -132,6 +171,7 @@ int main(int argc, char *argv[]) */ vm = vm_create_barebones(); vcpu = __vm_vcpu_add(vm, 0); + test_efer_bits(vcpu, KVM_ALWAYS_ALLOWED_EFER); test_cr_bits(vcpu, KVM_ALWAYS_ALLOWED_CR4); kvm_vm_free(vm); @@ -151,6 +191,7 @@ int main(int argc, char *argv[]) sregs.apic_base); test_cr_bits(vcpu, calc_supported_cr4_feature_bits()); + test_efer_bits(vcpu, calc_supported_efer_feature_bits()); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c index a9d8e4c059f9..eaa8201b937d 100644 --- a/tools/testing/selftests/kvm/x86/sev_dbg_test.c +++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c @@ -34,7 +34,7 @@ static void validate_buffers(void) static void ____test_sev_dbg(struct kvm_vm *vm, int i, int j, int nr_bytes) { - u8 pattern = guest_random_u32(&guest_rng); + u8 pattern = kvm_random_u32(&kvm_rng); if (i + nr_bytes > BUFFER_SIZE || j + nr_bytes > BUFFER_SIZE) return; 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 6d88c54f69fa..578283893ab3 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 @@ -77,7 +77,7 @@ int main(int argc, char *argv[]) ARBITRARY_IO_PORT, run->io.port); /* - * Stuff invalid guest state for L2 by making TR unusuable. The next + * Stuff invalid guest state for L2 by making TR unusable. The next * KVM_RUN should induce a TRIPLE_FAULT in L2 as KVM doesn't support * emulating invalid guest state for L2. */ diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index 39ce9a9369f5..3a326c5e74ca 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -17,7 +17,7 @@ * amongst the available numa nodes on the machine. * * Migration is a command line option. When used on non-numa machines will - * exit with error. Test is still usefull on non-numa for testing IPIs. + * exit with error. Test is still useful on non-numa for testing IPIs. */ #include <getopt.h> #include <pthread.h> |
