KVM: selftests: Cache CPUID in struct kvm_vcpu
authorSean Christopherson <seanjc@google.com>
Tue, 14 Jun 2022 20:06:43 +0000 (20:06 +0000)
committerSean Christopherson <seanjc@google.com>
Thu, 14 Jul 2022 01:14:15 +0000 (18:14 -0700)
Cache a vCPU's CPUID information in "struct kvm_vcpu" to allow fixing the
mess where tests, often unknowingly, modify the global/static "cpuid"
allocated by kvm_get_supported_cpuid().

Add vcpu_init_cpuid() to handle stuffing an entirely different CPUID
model, e.g. during vCPU creation or when switching to the Hyper-V enabled
CPUID model.  Automatically refresh the cache on vcpu_set_cpuid() so that
any adjustments made by KVM are always reflected in the cache.  Drop
vcpu_get_cpuid() entirely to force tests to use the cache, and to allow
adding e.g. vcpu_get_cpuid_entry() in the future without creating a
conflicting set of APIs where vcpu_get_cpuid() does KVM_GET_CPUID2, but
vcpu_get_cpuid_entry() does not.

Opportunistically convert the VMX nested state test and KVM PV test to
manipulating the vCPU's CPUID (because it's easy), but use
vcpu_init_cpuid() for the Hyper-V features test and "emulator error" test
to effectively retain their current behavior as they're less trivial to
convert.

Signed-off-by: Sean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/r/20220614200707.3315957-19-seanjc@google.com
tools/testing/selftests/kvm/include/kvm_util_base.h
tools/testing/selftests/kvm/include/x86_64/processor.h
tools/testing/selftests/kvm/lib/kvm_util.c
tools/testing/selftests/kvm/lib/x86_64/processor.c
tools/testing/selftests/kvm/x86_64/cpuid_test.c
tools/testing/selftests/kvm/x86_64/emulator_error_test.c
tools/testing/selftests/kvm/x86_64/hyperv_features.c
tools/testing/selftests/kvm/x86_64/kvm_pv_test.c
tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c
tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c
tools/testing/selftests/kvm/x86_64/xapic_state_test.c

index 19f7b33..24fde97 100644 (file)
@@ -50,6 +50,9 @@ struct kvm_vcpu {
        int fd;
        struct kvm_vm *vm;
        struct kvm_run *run;
+#ifdef __x86_64__
+       struct kvm_cpuid2 *cpuid;
+#endif
        struct kvm_dirty_gfn *dirty_gfns;
        uint32_t fetch_index;
        uint32_t dirty_gfns_count;
@@ -748,6 +751,8 @@ static inline struct kvm_vcpu *vm_vcpu_recreate(struct kvm_vm *vm,
        return vm_arch_vcpu_recreate(vm, vcpu_id);
 }
 
+void vcpu_arch_free(struct kvm_vcpu *vcpu);
+
 void virt_arch_pgd_alloc(struct kvm_vm *vm);
 
 static inline void virt_pgd_alloc(struct kvm_vm *vm)
index 1326377..c61b3aa 100644 (file)
@@ -618,18 +618,29 @@ static inline struct kvm_cpuid2 *allocate_kvm_cpuid2(int nr_entries)
        return cpuid;
 }
 
-struct kvm_cpuid2 *vcpu_get_cpuid(struct kvm_vcpu *vcpu);
+void vcpu_init_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid);
 
-static inline int __vcpu_set_cpuid(struct kvm_vcpu *vcpu,
-                                  struct kvm_cpuid2 *cpuid)
+static inline int __vcpu_set_cpuid(struct kvm_vcpu *vcpu)
 {
-       return __vcpu_ioctl(vcpu, KVM_SET_CPUID2, cpuid);
+       int r;
+
+       TEST_ASSERT(vcpu->cpuid, "Must do vcpu_init_cpuid() first");
+       r = __vcpu_ioctl(vcpu, KVM_SET_CPUID2, vcpu->cpuid);
+       if (r)
+               return r;
+
+       /* On success, refresh the cache to pick up adjustments made by KVM. */
+       vcpu_ioctl(vcpu, KVM_GET_CPUID2, vcpu->cpuid);
+       return 0;
 }
 
-static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu,
-                                 struct kvm_cpuid2 *cpuid)
+static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu)
 {
-       vcpu_ioctl(vcpu, KVM_SET_CPUID2, cpuid);
+       TEST_ASSERT(vcpu->cpuid, "Must do vcpu_init_cpuid() first");
+       vcpu_ioctl(vcpu, KVM_SET_CPUID2, vcpu->cpuid);
+
+       /* Refresh the cache to pick up adjustments made by KVM. */
+       vcpu_ioctl(vcpu, KVM_GET_CPUID2, vcpu->cpuid);
 }
 
 struct kvm_cpuid_entry2 *
index 49c1898..9889fe0 100644 (file)
@@ -472,6 +472,11 @@ kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start,
        return &region->region;
 }
 
+__weak void vcpu_arch_free(struct kvm_vcpu *vcpu)
+{
+
+}
+
 /*
  * VM VCPU Remove
  *
@@ -501,6 +506,8 @@ static void vm_vcpu_rm(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
        TEST_ASSERT(!ret,  __KVM_SYSCALL_ERROR("close()", ret));
 
        list_del(&vcpu->list);
+
+       vcpu_arch_free(vcpu);
        free(vcpu);
 }
 
index 4d383de..4cbcc33 100644 (file)
@@ -648,7 +648,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
                                     DEFAULT_GUEST_STACK_VADDR_MIN);
 
        vcpu = __vm_vcpu_add(vm, vcpu_id);
-       vcpu_set_cpuid(vcpu, kvm_get_supported_cpuid());
+       vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
        vcpu_setup(vm, vcpu);
 
        /* Setup guest general purpose registers */
@@ -669,11 +669,17 @@ struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, uint32_t vcpu_id)
 {
        struct kvm_vcpu *vcpu = __vm_vcpu_add(vm, vcpu_id);
 
-       vcpu_set_cpuid(vcpu, kvm_get_supported_cpuid());
+       vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
 
        return vcpu;
 }
 
+void vcpu_arch_free(struct kvm_vcpu *vcpu)
+{
+       if (vcpu->cpuid)
+               free(vcpu->cpuid);
+}
+
 /*
  * KVM Supported CPUID Get
  *
@@ -743,30 +749,22 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index)
        return buffer.entry.data;
 }
 
-struct kvm_cpuid2 *vcpu_get_cpuid(struct kvm_vcpu *vcpu)
+void vcpu_init_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid)
 {
-       struct kvm_cpuid2 *cpuid;
-       int max_ent;
-       int rc = -1;
-
-       cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
-       max_ent = cpuid->nent;
+       TEST_ASSERT(cpuid != vcpu->cpuid, "@cpuid can't be the vCPU's CPUID");
 
-       for (cpuid->nent = 1; cpuid->nent <= max_ent; cpuid->nent++) {
-               rc = __vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
-               if (!rc)
-                       break;
-
-               TEST_ASSERT(rc == -1 && errno == E2BIG,
-                           "KVM_GET_CPUID2 should either succeed or give E2BIG: %d %d",
-                           rc, errno);
+       /* Allow overriding the default CPUID. */
+       if (vcpu->cpuid && vcpu->cpuid->nent < cpuid->nent) {
+               free(vcpu->cpuid);
+               vcpu->cpuid = NULL;
        }
 
-       TEST_ASSERT(!rc, KVM_IOCTL_ERROR(KVM_GET_CPUID2, rc));
-       return cpuid;
-}
-
+       if (!vcpu->cpuid)
+               vcpu->cpuid = allocate_kvm_cpuid2(cpuid->nent);
 
+       memcpy(vcpu->cpuid, cpuid, kvm_cpuid2_size(cpuid->nent));
+       vcpu_set_cpuid(vcpu);
+}
 
 /*
  * Locate a cpuid entry.
@@ -1302,7 +1300,7 @@ void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu)
                cpuid_full->nent = nent + cpuid_hv->nent;
        }
 
-       vcpu_set_cpuid(vcpu, cpuid_full);
+       vcpu_init_cpuid(vcpu, cpuid_full);
 }
 
 struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu)
index 59dcdf5..96f141a 100644 (file)
@@ -144,21 +144,22 @@ struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct
        return guest_cpuids;
 }
 
-static void set_cpuid_after_run(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid)
+static void set_cpuid_after_run(struct kvm_vcpu *vcpu)
 {
+       struct kvm_cpuid2 *cpuid = vcpu->cpuid;
        struct kvm_cpuid_entry2 *ent;
        int rc;
        u32 eax, ebx, x;
 
        /* Setting unmodified CPUID is allowed */
-       rc = __vcpu_set_cpuid(vcpu, cpuid);
+       rc = __vcpu_set_cpuid(vcpu);
        TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
 
        /* Changing CPU features is forbidden */
        ent = get_cpuid(cpuid, 0x7, 0);
        ebx = ent->ebx;
        ent->ebx--;
-       rc = __vcpu_set_cpuid(vcpu, cpuid);
+       rc = __vcpu_set_cpuid(vcpu);
        TEST_ASSERT(rc, "Changing CPU features should fail");
        ent->ebx = ebx;
 
@@ -167,14 +168,14 @@ static void set_cpuid_after_run(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid)
        eax = ent->eax;
        x = eax & 0xff;
        ent->eax = (eax & ~0xffu) | (x - 1);
-       rc = __vcpu_set_cpuid(vcpu, cpuid);
+       rc = __vcpu_set_cpuid(vcpu);
        TEST_ASSERT(rc, "Changing MAXPHYADDR should fail");
        ent->eax = eax;
 }
 
 int main(void)
 {
-       struct kvm_cpuid2 *supp_cpuid, *cpuid2;
+       struct kvm_cpuid2 *supp_cpuid;
        struct kvm_vcpu *vcpu;
        vm_vaddr_t cpuid_gva;
        struct kvm_vm *vm;
@@ -183,18 +184,17 @@ int main(void)
        vm = vm_create_with_one_vcpu(&vcpu, guest_main);
 
        supp_cpuid = kvm_get_supported_cpuid();
-       cpuid2 = vcpu_get_cpuid(vcpu);
 
-       compare_cpuids(supp_cpuid, cpuid2);
+       compare_cpuids(supp_cpuid, vcpu->cpuid);
 
-       vcpu_alloc_cpuid(vm, &cpuid_gva, cpuid2);
+       vcpu_alloc_cpuid(vm, &cpuid_gva, vcpu->cpuid);
 
        vcpu_args_set(vcpu, 1, cpuid_gva);
 
        for (stage = 0; stage < 3; stage++)
                run_vcpu(vcpu, stage);
 
-       set_cpuid_after_run(vcpu, cpuid2);
+       set_cpuid_after_run(vcpu);
 
        kvm_vm_free(vm);
 }
index 3aa3d17..d8dbed4 100644 (file)
@@ -171,7 +171,7 @@ int main(int argc, char *argv[])
        entry->eax = (entry->eax & 0xffffff00) | MAXPHYADDR;
        set_cpuid(cpuid, entry);
 
-       vcpu_set_cpuid(vcpu, cpuid);
+       vcpu_init_cpuid(vcpu, cpuid);
 
        rc = kvm_check_cap(KVM_CAP_EXIT_ON_EMULATION_FAILURE);
        TEST_ASSERT(rc, "KVM_CAP_EXIT_ON_EMULATION_FAILURE is unavailable");
index 2070ba0..86ce1f1 100644 (file)
@@ -102,7 +102,7 @@ static void hv_set_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
                    "failed to set HYPERV_CPUID_ENLIGHTMENT_INFO leaf");
        TEST_ASSERT(set_cpuid(cpuid, dbg),
                    "failed to set HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES leaf");
-       vcpu_set_cpuid(vcpu, cpuid);
+       vcpu_init_cpuid(vcpu, cpuid);
 }
 
 static void guest_test_msrs_access(void)
index ea45244..986ca72 100644 (file)
@@ -147,7 +147,6 @@ static void enter_guest(struct kvm_vcpu *vcpu)
 
 int main(void)
 {
-       struct kvm_cpuid2 *best;
        struct kvm_vcpu *vcpu;
        struct kvm_vm *vm;
 
@@ -157,9 +156,8 @@ int main(void)
 
        vcpu_enable_cap(vcpu, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 1);
 
-       best = kvm_get_supported_cpuid();
-       clear_kvm_cpuid_features(best);
-       vcpu_set_cpuid(vcpu, best);
+       clear_kvm_cpuid_features(vcpu->cpuid);
+       vcpu_set_cpuid(vcpu);
 
        vm_init_descriptor_tables(vm);
        vcpu_init_descriptor_tables(vcpu);
index c804f6f..0ac5bec 100644 (file)
@@ -80,7 +80,7 @@ int main(int argc, char *argv[])
        set_cpuid(cpuid, entry);
 
        vm = vm_create_with_one_vcpu(&vcpu, guest_code);
-       vcpu_set_cpuid(vcpu, cpuid);
+       vcpu_set_cpuid(vcpu);
 
        run = vcpu->run;
 
index 66cb2d0..1cf78ec 100644 (file)
@@ -121,7 +121,7 @@ void test_vmx_nested_state(struct kvm_vcpu *vcpu)
        test_nested_state(vcpu, state);
 
        /* Enable VMX in the guest CPUID. */
-       vcpu_set_cpuid(vcpu, kvm_get_supported_cpuid());
+       vcpu_set_cpuid(vcpu);
 
        /*
         * Setting vmxon_pa == -1ull and vmcs_pa == -1ull exits early without
@@ -245,7 +245,7 @@ void test_vmx_nested_state(struct kvm_vcpu *vcpu)
 
 void disable_vmx(struct kvm_vcpu *vcpu)
 {
-       struct kvm_cpuid2 *cpuid = kvm_get_supported_cpuid();
+       struct kvm_cpuid2 *cpuid = vcpu->cpuid;
        int i;
 
        for (i = 0; i < cpuid->nent; ++i)
@@ -255,7 +255,7 @@ void disable_vmx(struct kvm_vcpu *vcpu)
        TEST_ASSERT(i != cpuid->nent, "CPUID function 1 not found");
 
        cpuid->entries[i].ecx &= ~CPUID_VMX;
-       vcpu_set_cpuid(vcpu, cpuid);
+       vcpu_set_cpuid(vcpu);
        cpuid->entries[i].ecx |= CPUID_VMX;
 }
 
index 8753162..52133bb 100644 (file)
@@ -152,13 +152,13 @@ int main(int argc, char *argv[])
        vm = vm_create_with_one_vcpu(&x.vcpu, xapic_guest_code);
        x.is_x2apic = false;
 
-       cpuid = vcpu_get_cpuid(x.vcpu);
+       cpuid = x.vcpu->cpuid;
        for (i = 0; i < cpuid->nent; i++) {
                if (cpuid->entries[i].function == 1)
                        break;
        }
        cpuid->entries[i].ecx &= ~BIT(21);
-       vcpu_set_cpuid(x.vcpu, cpuid);
+       vcpu_set_cpuid(x.vcpu);
 
        virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
        test_icr(&x);