19e6d484fSVitaly Kuznetsov // SPDX-License-Identifier: GPL-2.0-only 29e6d484fSVitaly Kuznetsov /* 39e6d484fSVitaly Kuznetsov * Copyright (C) 2021, Red Hat Inc. 49e6d484fSVitaly Kuznetsov * 59e6d484fSVitaly Kuznetsov * Generic tests for KVM CPUID set/get ioctls 69e6d484fSVitaly Kuznetsov */ 79e6d484fSVitaly Kuznetsov #include <asm/kvm_para.h> 89e6d484fSVitaly Kuznetsov #include <linux/kvm_para.h> 99e6d484fSVitaly Kuznetsov #include <stdint.h> 109e6d484fSVitaly Kuznetsov 119e6d484fSVitaly Kuznetsov #include "test_util.h" 129e6d484fSVitaly Kuznetsov #include "kvm_util.h" 139e6d484fSVitaly Kuznetsov #include "processor.h" 149e6d484fSVitaly Kuznetsov 159e6d484fSVitaly Kuznetsov /* CPUIDs known to differ */ 169e6d484fSVitaly Kuznetsov struct { 179e6d484fSVitaly Kuznetsov u32 function; 189e6d484fSVitaly Kuznetsov u32 index; 199e6d484fSVitaly Kuznetsov } mangled_cpuids[] = { 209e6d484fSVitaly Kuznetsov /* 219e6d484fSVitaly Kuznetsov * These entries depend on the vCPU's XCR0 register and IA32_XSS MSR, 229e6d484fSVitaly Kuznetsov * which are not controlled for by this test. 239e6d484fSVitaly Kuznetsov */ 249e6d484fSVitaly Kuznetsov {.function = 0xd, .index = 0}, 259e6d484fSVitaly Kuznetsov {.function = 0xd, .index = 1}, 269e6d484fSVitaly Kuznetsov }; 279e6d484fSVitaly Kuznetsov 289e6d484fSVitaly Kuznetsov static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid) 299e6d484fSVitaly Kuznetsov { 309e6d484fSVitaly Kuznetsov int i; 319e6d484fSVitaly Kuznetsov u32 eax, ebx, ecx, edx; 329e6d484fSVitaly Kuznetsov 339e6d484fSVitaly Kuznetsov for (i = 0; i < guest_cpuid->nent; i++) { 348fe09d6aSSean Christopherson __cpuid(guest_cpuid->entries[i].function, 358fe09d6aSSean Christopherson guest_cpuid->entries[i].index, 368fe09d6aSSean Christopherson &eax, &ebx, &ecx, &edx); 379e6d484fSVitaly Kuznetsov 3806b651d2SSean Christopherson GUEST_ASSERT_EQ(eax, guest_cpuid->entries[i].eax); 3906b651d2SSean Christopherson GUEST_ASSERT_EQ(ebx, guest_cpuid->entries[i].ebx); 4006b651d2SSean Christopherson GUEST_ASSERT_EQ(ecx, guest_cpuid->entries[i].ecx); 4106b651d2SSean Christopherson GUEST_ASSERT_EQ(edx, guest_cpuid->entries[i].edx); 429e6d484fSVitaly Kuznetsov } 439e6d484fSVitaly Kuznetsov 449e6d484fSVitaly Kuznetsov } 459e6d484fSVitaly Kuznetsov 469e6d484fSVitaly Kuznetsov static void guest_main(struct kvm_cpuid2 *guest_cpuid) 479e6d484fSVitaly Kuznetsov { 489e6d484fSVitaly Kuznetsov GUEST_SYNC(1); 499e6d484fSVitaly Kuznetsov 509e6d484fSVitaly Kuznetsov test_guest_cpuids(guest_cpuid); 519e6d484fSVitaly Kuznetsov 529e6d484fSVitaly Kuznetsov GUEST_SYNC(2); 539e6d484fSVitaly Kuznetsov 5406b651d2SSean Christopherson GUEST_ASSERT_EQ(this_cpu_property(X86_PROPERTY_MAX_KVM_LEAF), 0x40000001); 559e6d484fSVitaly Kuznetsov 569e6d484fSVitaly Kuznetsov GUEST_DONE(); 579e6d484fSVitaly Kuznetsov } 589e6d484fSVitaly Kuznetsov 59813e38cdSSean Christopherson static bool is_cpuid_mangled(const struct kvm_cpuid_entry2 *entrie) 609e6d484fSVitaly Kuznetsov { 619e6d484fSVitaly Kuznetsov int i; 629e6d484fSVitaly Kuznetsov 63*773cca18SSean Christopherson for (i = 0; i < ARRAY_SIZE(mangled_cpuids); i++) { 649e6d484fSVitaly Kuznetsov if (mangled_cpuids[i].function == entrie->function && 659e6d484fSVitaly Kuznetsov mangled_cpuids[i].index == entrie->index) 669e6d484fSVitaly Kuznetsov return true; 679e6d484fSVitaly Kuznetsov } 689e6d484fSVitaly Kuznetsov 699e6d484fSVitaly Kuznetsov return false; 709e6d484fSVitaly Kuznetsov } 719e6d484fSVitaly Kuznetsov 72813e38cdSSean Christopherson static void compare_cpuids(const struct kvm_cpuid2 *cpuid1, 73813e38cdSSean Christopherson const struct kvm_cpuid2 *cpuid2) 749e6d484fSVitaly Kuznetsov { 75813e38cdSSean Christopherson const struct kvm_cpuid_entry2 *e1, *e2; 769e6d484fSVitaly Kuznetsov int i; 779e6d484fSVitaly Kuznetsov 7871bcb951SSean Christopherson TEST_ASSERT(cpuid1->nent == cpuid2->nent, 7971bcb951SSean Christopherson "CPUID nent mismatch: %d vs. %d", cpuid1->nent, cpuid2->nent); 809e6d484fSVitaly Kuznetsov 8171bcb951SSean Christopherson for (i = 0; i < cpuid1->nent; i++) { 8271bcb951SSean Christopherson e1 = &cpuid1->entries[i]; 8371bcb951SSean Christopherson e2 = &cpuid2->entries[i]; 8471bcb951SSean Christopherson 8571bcb951SSean Christopherson TEST_ASSERT(e1->function == e2->function && 8671bcb951SSean Christopherson e1->index == e2->index && e1->flags == e2->flags, 8765612e99SAndrew Jones "CPUID entries[%d] mismtach: 0x%x.%d.%x vs. 0x%x.%d.%x", 8871bcb951SSean Christopherson i, e1->function, e1->index, e1->flags, 8971bcb951SSean Christopherson e2->function, e2->index, e2->flags); 9071bcb951SSean Christopherson 9171bcb951SSean Christopherson if (is_cpuid_mangled(e1)) 9271bcb951SSean Christopherson continue; 9371bcb951SSean Christopherson 9471bcb951SSean Christopherson TEST_ASSERT(e1->eax == e2->eax && e1->ebx == e2->ebx && 9571bcb951SSean Christopherson e1->ecx == e2->ecx && e1->edx == e2->edx, 9671bcb951SSean Christopherson "CPUID 0x%x.%x differ: 0x%x:0x%x:0x%x:0x%x vs 0x%x:0x%x:0x%x:0x%x", 9771bcb951SSean Christopherson e1->function, e1->index, 9871bcb951SSean Christopherson e1->eax, e1->ebx, e1->ecx, e1->edx, 9971bcb951SSean Christopherson e2->eax, e2->ebx, e2->ecx, e2->edx); 10071bcb951SSean Christopherson } 1019e6d484fSVitaly Kuznetsov } 1029e6d484fSVitaly Kuznetsov 10387f1b5b3SSean Christopherson static void run_vcpu(struct kvm_vcpu *vcpu, int stage) 1049e6d484fSVitaly Kuznetsov { 1059e6d484fSVitaly Kuznetsov struct ucall uc; 1069e6d484fSVitaly Kuznetsov 107768e9a61SSean Christopherson vcpu_run(vcpu); 1089e6d484fSVitaly Kuznetsov 109768e9a61SSean Christopherson switch (get_ucall(vcpu, &uc)) { 1109e6d484fSVitaly Kuznetsov case UCALL_SYNC: 1119e6d484fSVitaly Kuznetsov TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && 1129e6d484fSVitaly Kuznetsov uc.args[1] == stage + 1, 1139e6d484fSVitaly Kuznetsov "Stage %d: Unexpected register values vmexit, got %lx", 1149e6d484fSVitaly Kuznetsov stage + 1, (ulong)uc.args[1]); 1159e6d484fSVitaly Kuznetsov return; 1169e6d484fSVitaly Kuznetsov case UCALL_DONE: 1179e6d484fSVitaly Kuznetsov return; 1189e6d484fSVitaly Kuznetsov case UCALL_ABORT: 11906b651d2SSean Christopherson REPORT_GUEST_ASSERT(uc); 1209e6d484fSVitaly Kuznetsov default: 1219e6d484fSVitaly Kuznetsov TEST_ASSERT(false, "Unexpected exit: %s", 12287f1b5b3SSean Christopherson exit_reason_str(vcpu->run->exit_reason)); 1239e6d484fSVitaly Kuznetsov } 1249e6d484fSVitaly Kuznetsov } 1259e6d484fSVitaly Kuznetsov 1269e6d484fSVitaly Kuznetsov struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct kvm_cpuid2 *cpuid) 1279e6d484fSVitaly Kuznetsov { 1289e6d484fSVitaly Kuznetsov int size = sizeof(*cpuid) + cpuid->nent * sizeof(cpuid->entries[0]); 1299e6d484fSVitaly Kuznetsov vm_vaddr_t gva = vm_vaddr_alloc(vm, size, KVM_UTIL_MIN_VADDR); 1309e6d484fSVitaly Kuznetsov struct kvm_cpuid2 *guest_cpuids = addr_gva2hva(vm, gva); 1319e6d484fSVitaly Kuznetsov 1329e6d484fSVitaly Kuznetsov memcpy(guest_cpuids, cpuid, size); 1339e6d484fSVitaly Kuznetsov 1349e6d484fSVitaly Kuznetsov *p_gva = gva; 1359e6d484fSVitaly Kuznetsov return guest_cpuids; 1369e6d484fSVitaly Kuznetsov } 1379e6d484fSVitaly Kuznetsov 1387fbc6038SSean Christopherson static void set_cpuid_after_run(struct kvm_vcpu *vcpu) 139ecebb966SVitaly Kuznetsov { 140ecebb966SVitaly Kuznetsov struct kvm_cpuid_entry2 *ent; 141ecebb966SVitaly Kuznetsov int rc; 142ecebb966SVitaly Kuznetsov u32 eax, ebx, x; 143ecebb966SVitaly Kuznetsov 144ecebb966SVitaly Kuznetsov /* Setting unmodified CPUID is allowed */ 1457fbc6038SSean Christopherson rc = __vcpu_set_cpuid(vcpu); 146ecebb966SVitaly Kuznetsov TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc); 147ecebb966SVitaly Kuznetsov 148ecebb966SVitaly Kuznetsov /* Changing CPU features is forbidden */ 14949f6876aSSean Christopherson ent = vcpu_get_cpuid_entry(vcpu, 0x7); 150ecebb966SVitaly Kuznetsov ebx = ent->ebx; 151ecebb966SVitaly Kuznetsov ent->ebx--; 1527fbc6038SSean Christopherson rc = __vcpu_set_cpuid(vcpu); 153ecebb966SVitaly Kuznetsov TEST_ASSERT(rc, "Changing CPU features should fail"); 154ecebb966SVitaly Kuznetsov ent->ebx = ebx; 155ecebb966SVitaly Kuznetsov 156ecebb966SVitaly Kuznetsov /* Changing MAXPHYADDR is forbidden */ 15749f6876aSSean Christopherson ent = vcpu_get_cpuid_entry(vcpu, 0x80000008); 158ecebb966SVitaly Kuznetsov eax = ent->eax; 159ecebb966SVitaly Kuznetsov x = eax & 0xff; 160ecebb966SVitaly Kuznetsov ent->eax = (eax & ~0xffu) | (x - 1); 1617fbc6038SSean Christopherson rc = __vcpu_set_cpuid(vcpu); 162ecebb966SVitaly Kuznetsov TEST_ASSERT(rc, "Changing MAXPHYADDR should fail"); 163ecebb966SVitaly Kuznetsov ent->eax = eax; 164ecebb966SVitaly Kuznetsov } 165ecebb966SVitaly Kuznetsov 1662c761313SSean Christopherson static void test_get_cpuid2(struct kvm_vcpu *vcpu) 1672c761313SSean Christopherson { 1682c761313SSean Christopherson struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent + 1); 1692c761313SSean Christopherson int i, r; 1702c761313SSean Christopherson 1712c761313SSean Christopherson vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid); 1722c761313SSean Christopherson TEST_ASSERT(cpuid->nent == vcpu->cpuid->nent, 17365612e99SAndrew Jones "KVM didn't update nent on success, wanted %u, got %u", 1742c761313SSean Christopherson vcpu->cpuid->nent, cpuid->nent); 1752c761313SSean Christopherson 1762c761313SSean Christopherson for (i = 0; i < vcpu->cpuid->nent; i++) { 1772c761313SSean Christopherson cpuid->nent = i; 1782c761313SSean Christopherson r = __vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid); 1792c761313SSean Christopherson TEST_ASSERT(r && errno == E2BIG, KVM_IOCTL_ERROR(KVM_GET_CPUID2, r)); 1802c761313SSean Christopherson TEST_ASSERT(cpuid->nent == i, "KVM modified nent on failure"); 1812c761313SSean Christopherson } 1822c761313SSean Christopherson free(cpuid); 1832c761313SSean Christopherson } 1842c761313SSean Christopherson 1859e6d484fSVitaly Kuznetsov int main(void) 1869e6d484fSVitaly Kuznetsov { 18787f1b5b3SSean Christopherson struct kvm_vcpu *vcpu; 1889e6d484fSVitaly Kuznetsov vm_vaddr_t cpuid_gva; 1899e6d484fSVitaly Kuznetsov struct kvm_vm *vm; 1909e6d484fSVitaly Kuznetsov int stage; 1919e6d484fSVitaly Kuznetsov 19287f1b5b3SSean Christopherson vm = vm_create_with_one_vcpu(&vcpu, guest_main); 1939e6d484fSVitaly Kuznetsov 194813e38cdSSean Christopherson compare_cpuids(kvm_get_supported_cpuid(), vcpu->cpuid); 1959e6d484fSVitaly Kuznetsov 1967fbc6038SSean Christopherson vcpu_alloc_cpuid(vm, &cpuid_gva, vcpu->cpuid); 1979e6d484fSVitaly Kuznetsov 198768e9a61SSean Christopherson vcpu_args_set(vcpu, 1, cpuid_gva); 1999e6d484fSVitaly Kuznetsov 2009e6d484fSVitaly Kuznetsov for (stage = 0; stage < 3; stage++) 20187f1b5b3SSean Christopherson run_vcpu(vcpu, stage); 2029e6d484fSVitaly Kuznetsov 2037fbc6038SSean Christopherson set_cpuid_after_run(vcpu); 204ecebb966SVitaly Kuznetsov 2052c761313SSean Christopherson test_get_cpuid2(vcpu); 2062c761313SSean Christopherson 2079e6d484fSVitaly Kuznetsov kvm_vm_free(vm); 2089e6d484fSVitaly Kuznetsov } 209