xref: /linux/tools/testing/selftests/kvm/x86_64/cpuid_test.c (revision e301aea030d60da760f85f854a82ce788d5cf6e7)
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