From: David Woodhouse <[email protected]> Cover KVM_CAP_PLANES argument validation and ordering, plane creation bounds, plane vCPU creation, and per-plane isolation of vCPU register state.
The tools copy of linux/kvm.h is synced to pick up the VM planes definitions the test builds against. Signed-off-by: David Woodhouse <[email protected]> Assisted-by: LLM --- tools/include/uapi/linux/kvm.h | 40 +++++- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/x86/planes_test.c | 120 ++++++++++++++++++ 3 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 tools/testing/selftests/kvm/x86/planes_test.c diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index fe2f500c932d..dcd12f58186e 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -11,6 +11,7 @@ #include <linux/const.h> #include <linux/types.h> #include <linux/compiler.h> +#include <linux/stddef.h> #include <linux/ioctl.h> #include <asm/kvm.h> @@ -145,6 +146,13 @@ struct kvm_exit_snp_req_certs { __u64 ret; }; +struct kvm_plane_event_exit { +#define KVM_PLANE_EVENT_CREATE_VCPU 1 + __u32 cause; + __u32 plane; + __u64 extra[8]; +}; + #define KVM_S390_GET_SKEYS_NONE 1 #define KVM_S390_SKEYS_MAX 1048576 @@ -192,6 +200,7 @@ struct kvm_exit_snp_req_certs { #define KVM_EXIT_ARM_SEA 41 #define KVM_EXIT_ARM_LDST64B 42 #define KVM_EXIT_SNP_REQ_CERTS 43 +#define KVM_EXIT_PLANE_EVENT 44 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -456,6 +465,9 @@ struct kvm_run { } notify; /* KVM_EXIT_MEMORY_FAULT */ struct { +#define KVM_MEMORY_EXIT_FLAG_READ (1ULL << 0) +#define KVM_MEMORY_EXIT_FLAG_WRITE (1ULL << 1) +#define KVM_MEMORY_EXIT_FLAG_EXEC (1ULL << 2) #define KVM_MEMORY_EXIT_FLAG_PRIVATE (1ULL << 3) __u64 flags; __u64 gpa; @@ -496,6 +508,8 @@ struct kvm_run { } arm_sea; /* KVM_EXIT_SNP_REQ_CERTS */ struct kvm_exit_snp_req_certs snp_req_certs; + /* KVM_EXIT_PLANE_EVENT */ + struct kvm_plane_event_exit plane_event; /* Fix the size of the union. */ char padding[256]; }; @@ -542,7 +556,7 @@ struct kvm_coalesced_mmio { struct kvm_coalesced_mmio_ring { __u32 first, last; - struct kvm_coalesced_mmio coalesced_mmio[]; + __DECLARE_FLEX_ARRAY(struct kvm_coalesced_mmio, coalesced_mmio); }; #define KVM_COALESCED_MMIO_MAX \ @@ -592,7 +606,7 @@ struct kvm_clear_dirty_log { /* for KVM_SET_SIGNAL_MASK */ struct kvm_signal_mask { __u32 len; - __u8 sigset[]; + __DECLARE_FLEX_ARRAY(__u8, sigset); }; /* for KVM_TPR_ACCESS_REPORTING */ @@ -703,6 +717,11 @@ struct kvm_enable_cap { #define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL #define KVM_VM_TYPE_ARM_IPA_SIZE(x) \ ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK) + +#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31) +#define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \ + KVM_VM_TYPE_ARM_PROTECTED) + /* * ioctls for /dev/kvm fds: */ @@ -724,6 +743,11 @@ struct kvm_enable_cap { #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2) #define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list) +/* + * Maximum number of supported planes + */ +#define KVM_MAX_PLANES 16 + /* * Extension capability list. */ @@ -989,6 +1013,8 @@ struct kvm_enable_cap { #define KVM_CAP_ARM_SEA_TO_USER 245 #define KVM_CAP_S390_USER_OPEREXEC 246 #define KVM_CAP_S390_KEYOP 247 +#define KVM_CAP_S390_VSIE_ESAMODE 248 +#define KVM_CAP_PLANES 249 struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1051,7 +1077,7 @@ struct kvm_irq_routing_entry { struct kvm_irq_routing { __u32 nr; __u32 flags; - struct kvm_irq_routing_entry entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_irq_routing_entry, entries); }; #define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) @@ -1142,7 +1168,7 @@ struct kvm_dirty_tlb { struct kvm_reg_list { __u64 n; /* number of regs */ - __u64 reg[]; + __DECLARE_FLEX_ARRAY(__u64, reg); }; struct kvm_one_reg { @@ -1224,6 +1250,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_LOONGARCH_DMSINTC, +#define KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_ARM_VGIC_V5, #define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 @@ -1340,6 +1368,8 @@ struct kvm_s390_keyop { #define KVM_GET_DEVICE_ATTR _IOW(KVMIO, 0xe2, struct kvm_device_attr) #define KVM_HAS_DEVICE_ATTR _IOW(KVMIO, 0xe3, struct kvm_device_attr) +#define KVM_CREATE_PLANE _IO(KVMIO, 0xe4) + /* * ioctls for vcpu fds */ @@ -1610,7 +1640,7 @@ struct kvm_stats_desc { #ifdef __KERNEL__ char name[KVM_STATS_NAME_SIZE]; #else - char name[]; + __DECLARE_FLEX_ARRAY(char, name); #endif }; diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 61e767a1e31b..5883359c1a5d 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -104,6 +104,7 @@ TEST_GEN_PROGS_x86 += x86/nested_tsc_scaling_test TEST_GEN_PROGS_x86 += x86/nested_vmsave_vmload_test TEST_GEN_PROGS_x86 += x86/platform_info_test TEST_GEN_PROGS_x86 += x86/plane_x86_test +TEST_GEN_PROGS_x86 += x86/planes_test TEST_GEN_PROGS_x86 += x86/pmu_counters_test TEST_GEN_PROGS_x86 += x86/pmu_event_filter_test TEST_GEN_PROGS_x86 += x86/private_mem_conversions_test diff --git a/tools/testing/selftests/kvm/x86/planes_test.c b/tools/testing/selftests/kvm/x86/planes_test.c new file mode 100644 index 000000000000..3517175580d3 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/planes_test.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test the KVM_CAP_PLANES opt-in and the plane lifecycle uAPI: plane + * creation bounds, plane vCPU creation, and the independence of + * per-plane vCPU register state. + */ +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include "kvm_util.h" + +static int plane_vcpu_fd(int plane_fd, int vcpu_id) +{ + return __kvm_ioctl(plane_fd, KVM_CREATE_VCPU, (void *)(unsigned long)vcpu_id); +} + +static void test_enable_cap_bounds(void) +{ + struct kvm_vm *vm = vm_create_barebones(); + + /* Planes require a split irqchip; the cap itself does not. */ + TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_PLANES, 0), -1); + TEST_ASSERT_EQ(errno, EINVAL); + + TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_PLANES, KVM_MAX_PLANES + 1), -1); + TEST_ASSERT_EQ(errno, EINVAL); + + vm_enable_cap(vm, KVM_CAP_PLANES, KVM_MAX_PLANES); + + kvm_vm_free(vm); +} + +static void test_enable_cap_too_late(void) +{ + struct kvm_vm *vm = vm_create_barebones(); + int vcpu_fd; + + vcpu_fd = __vm_ioctl(vm, KVM_CREATE_VCPU, NULL); + TEST_ASSERT(vcpu_fd >= 0, "KVM_CREATE_VCPU failed: errno %d", errno); + + TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_PLANES, 2), -1); + TEST_ASSERT_EQ(errno, EEXIST); + + close(vcpu_fd); + kvm_vm_free(vm); +} + +static void test_plane_lifecycle(void) +{ + struct kvm_vm *vm = vm_create_barebones(); + struct kvm_regs regs0, regs1; + int plane_fd, vcpu0_fd, vcpu1_fd; + int r; + + vm_enable_cap(vm, KVM_CAP_SPLIT_IRQCHIP, 24); + + /* Without the opt-in a VM has a single plane. */ + TEST_ASSERT_EQ(vm_check_cap(vm, KVM_CAP_PLANES), 1); + TEST_ASSERT_EQ(__vm_ioctl(vm, KVM_CREATE_PLANE, (void *)1UL), -1); + TEST_ASSERT_EQ(errno, EINVAL); + + vm_enable_cap(vm, KVM_CAP_PLANES, 2); + TEST_ASSERT_EQ(vm_check_cap(vm, KVM_CAP_PLANES), 2); + + /* The configured count is a hard bound, and reconfiguration a hard no. */ + TEST_ASSERT_EQ(__vm_ioctl(vm, KVM_CREATE_PLANE, (void *)2UL), -1); + TEST_ASSERT_EQ(errno, EINVAL); + + plane_fd = __vm_ioctl(vm, KVM_CREATE_PLANE, (void *)1UL); + TEST_ASSERT(plane_fd >= 0, "KVM_CREATE_PLANE failed: errno %d", errno); + + TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_PLANES, 3), -1); + TEST_ASSERT_EQ(errno, EEXIST); + + /* + * A plane cannot host planes of its own. Plane fds that do not + * implement KVM_CHECK_EXTENSION at all satisfy that trivially. + */ + r = __kvm_ioctl(plane_fd, KVM_CHECK_EXTENSION, (void *)KVM_CAP_PLANES); + TEST_ASSERT(r == 0 || (r == -1 && errno == ENOTTY), + "plane fd reports nested plane support: %d", r); + + vcpu0_fd = __vm_ioctl(vm, KVM_CREATE_VCPU, NULL); + TEST_ASSERT(vcpu0_fd >= 0, "KVM_CREATE_VCPU failed: errno %d", errno); + + vcpu1_fd = plane_vcpu_fd(plane_fd, 0); + TEST_ASSERT(vcpu1_fd >= 0, + "KVM_CREATE_VCPU on plane 1 failed: errno %d", errno); + + /* Register state is per plane: write plane 1, plane 0 unaffected. */ + kvm_ioctl(vcpu1_fd, KVM_GET_REGS, ®s1); + regs1.rbx = 0x5157ca11; /* arbitrary marker */ + kvm_ioctl(vcpu1_fd, KVM_SET_REGS, ®s1); + + kvm_ioctl(vcpu0_fd, KVM_GET_REGS, ®s0); + TEST_ASSERT(regs0.rbx != 0x5157ca11, + "plane 0 RBX changed by plane 1 KVM_SET_REGS"); + + memset(®s1, 0, sizeof(regs1)); + kvm_ioctl(vcpu1_fd, KVM_GET_REGS, ®s1); + TEST_ASSERT_EQ(regs1.rbx, 0x5157ca11); + + close(vcpu1_fd); + close(vcpu0_fd); + close(plane_fd); + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_has_cap(KVM_CAP_PLANES)); + + test_enable_cap_bounds(); + test_enable_cap_too_late(); + test_plane_lifecycle(); + + return 0; +} -- 2.50.1

