xref: /openbmc/linux/tools/testing/selftests/kvm/x86_64/cpuid_test.c (revision 06b651d250e5eea0a1c1fd31fbbc0e0b27f66592)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2021, Red Hat Inc.
4  *
5  * Generic tests for KVM CPUID set/get ioctls
6  */
7 #define USE_GUEST_ASSERT_PRINTF 1
8 
9 #include <asm/kvm_para.h>
10 #include <linux/kvm_para.h>
11 #include <stdint.h>
12 
13 #include "test_util.h"
14 #include "kvm_util.h"
15 #include "processor.h"
16 
17 /* CPUIDs known to differ */
18 struct {
19 	u32 function;
20 	u32 index;
21 } mangled_cpuids[] = {
22 	/*
23 	 * These entries depend on the vCPU's XCR0 register and IA32_XSS MSR,
24 	 * which are not controlled for by this test.
25 	 */
26 	{.function = 0xd, .index = 0},
27 	{.function = 0xd, .index = 1},
28 };
29 
30 static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid)
31 {
32 	int i;
33 	u32 eax, ebx, ecx, edx;
34 
35 	for (i = 0; i < guest_cpuid->nent; i++) {
36 		__cpuid(guest_cpuid->entries[i].function,
37 			guest_cpuid->entries[i].index,
38 			&eax, &ebx, &ecx, &edx);
39 
40 		GUEST_ASSERT_EQ(eax, guest_cpuid->entries[i].eax);
41 		GUEST_ASSERT_EQ(ebx, guest_cpuid->entries[i].ebx);
42 		GUEST_ASSERT_EQ(ecx, guest_cpuid->entries[i].ecx);
43 		GUEST_ASSERT_EQ(edx, guest_cpuid->entries[i].edx);
44 	}
45 
46 }
47 
48 static void guest_main(struct kvm_cpuid2 *guest_cpuid)
49 {
50 	GUEST_SYNC(1);
51 
52 	test_guest_cpuids(guest_cpuid);
53 
54 	GUEST_SYNC(2);
55 
56 	GUEST_ASSERT_EQ(this_cpu_property(X86_PROPERTY_MAX_KVM_LEAF), 0x40000001);
57 
58 	GUEST_DONE();
59 }
60 
61 static bool is_cpuid_mangled(const struct kvm_cpuid_entry2 *entrie)
62 {
63 	int i;
64 
65 	for (i = 0; i < sizeof(mangled_cpuids); i++) {
66 		if (mangled_cpuids[i].function == entrie->function &&
67 		    mangled_cpuids[i].index == entrie->index)
68 			return true;
69 	}
70 
71 	return false;
72 }
73 
74 static void compare_cpuids(const struct kvm_cpuid2 *cpuid1,
75 			   const struct kvm_cpuid2 *cpuid2)
76 {
77 	const struct kvm_cpuid_entry2 *e1, *e2;
78 	int i;
79 
80 	TEST_ASSERT(cpuid1->nent == cpuid2->nent,
81 		    "CPUID nent mismatch: %d vs. %d", cpuid1->nent, cpuid2->nent);
82 
83 	for (i = 0; i < cpuid1->nent; i++) {
84 		e1 = &cpuid1->entries[i];
85 		e2 = &cpuid2->entries[i];
86 
87 		TEST_ASSERT(e1->function == e2->function &&
88 			    e1->index == e2->index && e1->flags == e2->flags,
89 			    "CPUID entries[%d] mismtach: 0x%x.%d.%x vs. 0x%x.%d.%x\n",
90 			    i, e1->function, e1->index, e1->flags,
91 			    e2->function, e2->index, e2->flags);
92 
93 		if (is_cpuid_mangled(e1))
94 			continue;
95 
96 		TEST_ASSERT(e1->eax == e2->eax && e1->ebx == e2->ebx &&
97 			    e1->ecx == e2->ecx && e1->edx == e2->edx,
98 			    "CPUID 0x%x.%x differ: 0x%x:0x%x:0x%x:0x%x vs 0x%x:0x%x:0x%x:0x%x",
99 			    e1->function, e1->index,
100 			    e1->eax, e1->ebx, e1->ecx, e1->edx,
101 			    e2->eax, e2->ebx, e2->ecx, e2->edx);
102 	}
103 }
104 
105 static void run_vcpu(struct kvm_vcpu *vcpu, int stage)
106 {
107 	struct ucall uc;
108 
109 	vcpu_run(vcpu);
110 
111 	switch (get_ucall(vcpu, &uc)) {
112 	case UCALL_SYNC:
113 		TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
114 			    uc.args[1] == stage + 1,
115 			    "Stage %d: Unexpected register values vmexit, got %lx",
116 			    stage + 1, (ulong)uc.args[1]);
117 		return;
118 	case UCALL_DONE:
119 		return;
120 	case UCALL_ABORT:
121 		REPORT_GUEST_ASSERT(uc);
122 	default:
123 		TEST_ASSERT(false, "Unexpected exit: %s",
124 			    exit_reason_str(vcpu->run->exit_reason));
125 	}
126 }
127 
128 struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct kvm_cpuid2 *cpuid)
129 {
130 	int size = sizeof(*cpuid) + cpuid->nent * sizeof(cpuid->entries[0]);
131 	vm_vaddr_t gva = vm_vaddr_alloc(vm, size, KVM_UTIL_MIN_VADDR);
132 	struct kvm_cpuid2 *guest_cpuids = addr_gva2hva(vm, gva);
133 
134 	memcpy(guest_cpuids, cpuid, size);
135 
136 	*p_gva = gva;
137 	return guest_cpuids;
138 }
139 
140 static void set_cpuid_after_run(struct kvm_vcpu *vcpu)
141 {
142 	struct kvm_cpuid_entry2 *ent;
143 	int rc;
144 	u32 eax, ebx, x;
145 
146 	/* Setting unmodified CPUID is allowed */
147 	rc = __vcpu_set_cpuid(vcpu);
148 	TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
149 
150 	/* Changing CPU features is forbidden */
151 	ent = vcpu_get_cpuid_entry(vcpu, 0x7);
152 	ebx = ent->ebx;
153 	ent->ebx--;
154 	rc = __vcpu_set_cpuid(vcpu);
155 	TEST_ASSERT(rc, "Changing CPU features should fail");
156 	ent->ebx = ebx;
157 
158 	/* Changing MAXPHYADDR is forbidden */
159 	ent = vcpu_get_cpuid_entry(vcpu, 0x80000008);
160 	eax = ent->eax;
161 	x = eax & 0xff;
162 	ent->eax = (eax & ~0xffu) | (x - 1);
163 	rc = __vcpu_set_cpuid(vcpu);
164 	TEST_ASSERT(rc, "Changing MAXPHYADDR should fail");
165 	ent->eax = eax;
166 }
167 
168 static void test_get_cpuid2(struct kvm_vcpu *vcpu)
169 {
170 	struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent + 1);
171 	int i, r;
172 
173 	vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
174 	TEST_ASSERT(cpuid->nent == vcpu->cpuid->nent,
175 		    "KVM didn't update nent on success, wanted %u, got %u\n",
176 		    vcpu->cpuid->nent, cpuid->nent);
177 
178 	for (i = 0; i < vcpu->cpuid->nent; i++) {
179 		cpuid->nent = i;
180 		r = __vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
181 		TEST_ASSERT(r && errno == E2BIG, KVM_IOCTL_ERROR(KVM_GET_CPUID2, r));
182 		TEST_ASSERT(cpuid->nent == i, "KVM modified nent on failure");
183 	}
184 	free(cpuid);
185 }
186 
187 int main(void)
188 {
189 	struct kvm_vcpu *vcpu;
190 	vm_vaddr_t cpuid_gva;
191 	struct kvm_vm *vm;
192 	int stage;
193 
194 	vm = vm_create_with_one_vcpu(&vcpu, guest_main);
195 
196 	compare_cpuids(kvm_get_supported_cpuid(), vcpu->cpuid);
197 
198 	vcpu_alloc_cpuid(vm, &cpuid_gva, vcpu->cpuid);
199 
200 	vcpu_args_set(vcpu, 1, cpuid_gva);
201 
202 	for (stage = 0; stage < 3; stage++)
203 		run_vcpu(vcpu, stage);
204 
205 	set_cpuid_after_run(vcpu);
206 
207 	test_get_cpuid2(vcpu);
208 
209 	kvm_vm_free(vm);
210 }
211