| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2021, Red Hat Inc.
- *
- * Generic tests for KVM CPUID set/get ioctls
- */
- #include <asm/kvm_para.h>
- #include <linux/kvm_para.h>
- #include <stdint.h>
- #include "test_util.h"
- #include "kvm_util.h"
- #include "processor.h"
- struct cpuid_mask {
- union {
- struct {
- u32 eax;
- u32 ebx;
- u32 ecx;
- u32 edx;
- };
- u32 regs[4];
- };
- };
- static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid)
- {
- int i;
- u32 eax, ebx, ecx, edx;
- for (i = 0; i < guest_cpuid->nent; i++) {
- __cpuid(guest_cpuid->entries[i].function,
- guest_cpuid->entries[i].index,
- &eax, &ebx, &ecx, &edx);
- GUEST_ASSERT_EQ(eax, guest_cpuid->entries[i].eax);
- GUEST_ASSERT_EQ(ebx, guest_cpuid->entries[i].ebx);
- GUEST_ASSERT_EQ(ecx, guest_cpuid->entries[i].ecx);
- GUEST_ASSERT_EQ(edx, guest_cpuid->entries[i].edx);
- }
- }
- static void guest_main(struct kvm_cpuid2 *guest_cpuid)
- {
- GUEST_SYNC(1);
- test_guest_cpuids(guest_cpuid);
- GUEST_SYNC(2);
- GUEST_ASSERT_EQ(this_cpu_property(X86_PROPERTY_MAX_KVM_LEAF), 0x40000001);
- GUEST_DONE();
- }
- static struct cpuid_mask get_const_cpuid_mask(const struct kvm_cpuid_entry2 *entry)
- {
- struct cpuid_mask mask;
- memset(&mask, 0xff, sizeof(mask));
- switch (entry->function) {
- case 0x1:
- mask.regs[X86_FEATURE_OSXSAVE.reg] &= ~BIT(X86_FEATURE_OSXSAVE.bit);
- break;
- case 0x7:
- mask.regs[X86_FEATURE_OSPKE.reg] &= ~BIT(X86_FEATURE_OSPKE.bit);
- break;
- case 0xd:
- /*
- * CPUID.0xD.{0,1}.EBX enumerate XSAVE size based on the current
- * XCR0 and IA32_XSS MSR values.
- */
- if (entry->index < 2)
- mask.ebx = 0;
- break;
- }
- return mask;
- }
- static void compare_cpuids(const struct kvm_cpuid2 *cpuid1,
- const struct kvm_cpuid2 *cpuid2)
- {
- const struct kvm_cpuid_entry2 *e1, *e2;
- int i;
- TEST_ASSERT(cpuid1->nent == cpuid2->nent,
- "CPUID nent mismatch: %d vs. %d", cpuid1->nent, cpuid2->nent);
- for (i = 0; i < cpuid1->nent; i++) {
- struct cpuid_mask mask;
- e1 = &cpuid1->entries[i];
- e2 = &cpuid2->entries[i];
- TEST_ASSERT(e1->function == e2->function &&
- e1->index == e2->index && e1->flags == e2->flags,
- "CPUID entries[%d] mismtach: 0x%x.%d.%x vs. 0x%x.%d.%x",
- i, e1->function, e1->index, e1->flags,
- e2->function, e2->index, e2->flags);
- /* Mask off dynamic bits, e.g. OSXSAVE, when comparing entries. */
- mask = get_const_cpuid_mask(e1);
- TEST_ASSERT((e1->eax & mask.eax) == (e2->eax & mask.eax) &&
- (e1->ebx & mask.ebx) == (e2->ebx & mask.ebx) &&
- (e1->ecx & mask.ecx) == (e2->ecx & mask.ecx) &&
- (e1->edx & mask.edx) == (e2->edx & mask.edx),
- "CPUID 0x%x.%x differ: 0x%x:0x%x:0x%x:0x%x vs 0x%x:0x%x:0x%x:0x%x",
- e1->function, e1->index,
- e1->eax & mask.eax, e1->ebx & mask.ebx,
- e1->ecx & mask.ecx, e1->edx & mask.edx,
- e2->eax & mask.eax, e2->ebx & mask.ebx,
- e2->ecx & mask.ecx, e2->edx & mask.edx);
- }
- }
- static void run_vcpu(struct kvm_vcpu *vcpu, int stage)
- {
- struct ucall uc;
- vcpu_run(vcpu);
- switch (get_ucall(vcpu, &uc)) {
- case UCALL_SYNC:
- TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
- uc.args[1] == stage + 1,
- "Stage %d: Unexpected register values vmexit, got %lx",
- stage + 1, (ulong)uc.args[1]);
- return;
- case UCALL_DONE:
- return;
- case UCALL_ABORT:
- REPORT_GUEST_ASSERT(uc);
- default:
- TEST_ASSERT(false, "Unexpected exit: %s",
- exit_reason_str(vcpu->run->exit_reason));
- }
- }
- struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct kvm_cpuid2 *cpuid)
- {
- int size = sizeof(*cpuid) + cpuid->nent * sizeof(cpuid->entries[0]);
- vm_vaddr_t gva = vm_vaddr_alloc(vm, size, KVM_UTIL_MIN_VADDR);
- struct kvm_cpuid2 *guest_cpuids = addr_gva2hva(vm, gva);
- memcpy(guest_cpuids, cpuid, size);
- *p_gva = gva;
- return guest_cpuids;
- }
- static void set_cpuid_after_run(struct kvm_vcpu *vcpu)
- {
- struct kvm_cpuid_entry2 *ent;
- struct kvm_sregs sregs;
- int rc;
- u32 eax, ebx, x;
- /* Setting unmodified CPUID is allowed */
- rc = __vcpu_set_cpuid(vcpu);
- TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
- /*
- * Toggle CR4 bits that affect dynamic CPUID feature flags to verify
- * setting unmodified CPUID succeeds with runtime CPUID updates.
- */
- vcpu_sregs_get(vcpu, &sregs);
- if (kvm_cpu_has(X86_FEATURE_XSAVE))
- sregs.cr4 ^= X86_CR4_OSXSAVE;
- if (kvm_cpu_has(X86_FEATURE_PKU))
- sregs.cr4 ^= X86_CR4_PKE;
- vcpu_sregs_set(vcpu, &sregs);
- rc = __vcpu_set_cpuid(vcpu);
- TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
- /* Changing CPU features is forbidden */
- ent = vcpu_get_cpuid_entry(vcpu, 0x7);
- ebx = ent->ebx;
- ent->ebx--;
- rc = __vcpu_set_cpuid(vcpu);
- TEST_ASSERT(rc, "Changing CPU features should fail");
- ent->ebx = ebx;
- /* Changing MAXPHYADDR is forbidden */
- ent = vcpu_get_cpuid_entry(vcpu, 0x80000008);
- eax = ent->eax;
- x = eax & 0xff;
- ent->eax = (eax & ~0xffu) | (x - 1);
- rc = __vcpu_set_cpuid(vcpu);
- TEST_ASSERT(rc, "Changing MAXPHYADDR should fail");
- ent->eax = eax;
- }
- static void test_get_cpuid2(struct kvm_vcpu *vcpu)
- {
- struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent + 1);
- int i, r;
- vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
- TEST_ASSERT(cpuid->nent == vcpu->cpuid->nent,
- "KVM didn't update nent on success, wanted %u, got %u",
- vcpu->cpuid->nent, cpuid->nent);
- for (i = 0; i < vcpu->cpuid->nent; i++) {
- cpuid->nent = i;
- r = __vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
- TEST_ASSERT(r && errno == E2BIG, KVM_IOCTL_ERROR(KVM_GET_CPUID2, r));
- TEST_ASSERT(cpuid->nent == i, "KVM modified nent on failure");
- }
- free(cpuid);
- }
- int main(void)
- {
- struct kvm_vcpu *vcpu;
- vm_vaddr_t cpuid_gva;
- struct kvm_vm *vm;
- int stage;
- vm = vm_create_with_one_vcpu(&vcpu, guest_main);
- compare_cpuids(kvm_get_supported_cpuid(), vcpu->cpuid);
- 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);
- test_get_cpuid2(vcpu);
- kvm_vm_free(vm);
- }
|