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 #include <asm/kvm_para.h>
8 #include <linux/kvm_para.h>
9 #include <stdint.h>
10
11 #include "test_util.h"
12 #include "kvm_util.h"
13 #include "processor.h"
14
15 struct cpuid_mask {
16 union {
17 struct {
18 u32 eax;
19 u32 ebx;
20 u32 ecx;
21 u32 edx;
22 };
23 u32 regs[4];
24 };
25 };
26
test_guest_cpuids(struct kvm_cpuid2 * guest_cpuid)27 static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid)
28 {
29 int i;
30 u32 eax, ebx, ecx, edx;
31
32 for (i = 0; i < guest_cpuid->nent; i++) {
33 __cpuid(guest_cpuid->entries[i].function,
34 guest_cpuid->entries[i].index,
35 &eax, &ebx, &ecx, &edx);
36
37 GUEST_ASSERT_EQ(eax, guest_cpuid->entries[i].eax);
38 GUEST_ASSERT_EQ(ebx, guest_cpuid->entries[i].ebx);
39 GUEST_ASSERT_EQ(ecx, guest_cpuid->entries[i].ecx);
40 GUEST_ASSERT_EQ(edx, guest_cpuid->entries[i].edx);
41 }
42
43 }
44
guest_main(struct kvm_cpuid2 * guest_cpuid)45 static void guest_main(struct kvm_cpuid2 *guest_cpuid)
46 {
47 GUEST_SYNC(1);
48
49 test_guest_cpuids(guest_cpuid);
50
51 GUEST_SYNC(2);
52
53 GUEST_ASSERT_EQ(this_cpu_property(X86_PROPERTY_MAX_KVM_LEAF), 0x40000001);
54
55 GUEST_DONE();
56 }
57
get_const_cpuid_mask(const struct kvm_cpuid_entry2 * entry)58 static struct cpuid_mask get_const_cpuid_mask(const struct kvm_cpuid_entry2 *entry)
59 {
60 struct cpuid_mask mask;
61
62 memset(&mask, 0xff, sizeof(mask));
63
64 switch (entry->function) {
65 case 0x1:
66 mask.regs[X86_FEATURE_OSXSAVE.reg] &= ~BIT(X86_FEATURE_OSXSAVE.bit);
67 break;
68 case 0x7:
69 mask.regs[X86_FEATURE_OSPKE.reg] &= ~BIT(X86_FEATURE_OSPKE.bit);
70 break;
71 case 0xd:
72 /*
73 * CPUID.0xD.{0,1}.EBX enumerate XSAVE size based on the current
74 * XCR0 and IA32_XSS MSR values.
75 */
76 if (entry->index < 2)
77 mask.ebx = 0;
78 break;
79 }
80 return mask;
81 }
82
compare_cpuids(const struct kvm_cpuid2 * cpuid1,const struct kvm_cpuid2 * cpuid2)83 static void compare_cpuids(const struct kvm_cpuid2 *cpuid1,
84 const struct kvm_cpuid2 *cpuid2)
85 {
86 const struct kvm_cpuid_entry2 *e1, *e2;
87 int i;
88
89 TEST_ASSERT(cpuid1->nent == cpuid2->nent,
90 "CPUID nent mismatch: %d vs. %d", cpuid1->nent, cpuid2->nent);
91
92 for (i = 0; i < cpuid1->nent; i++) {
93 struct cpuid_mask mask;
94
95 e1 = &cpuid1->entries[i];
96 e2 = &cpuid2->entries[i];
97
98 TEST_ASSERT(e1->function == e2->function &&
99 e1->index == e2->index && e1->flags == e2->flags,
100 "CPUID entries[%d] mismtach: 0x%x.%d.%x vs. 0x%x.%d.%x",
101 i, e1->function, e1->index, e1->flags,
102 e2->function, e2->index, e2->flags);
103
104 /* Mask off dynamic bits, e.g. OSXSAVE, when comparing entries. */
105 mask = get_const_cpuid_mask(e1);
106
107 TEST_ASSERT((e1->eax & mask.eax) == (e2->eax & mask.eax) &&
108 (e1->ebx & mask.ebx) == (e2->ebx & mask.ebx) &&
109 (e1->ecx & mask.ecx) == (e2->ecx & mask.ecx) &&
110 (e1->edx & mask.edx) == (e2->edx & mask.edx),
111 "CPUID 0x%x.%x differ: 0x%x:0x%x:0x%x:0x%x vs 0x%x:0x%x:0x%x:0x%x",
112 e1->function, e1->index,
113 e1->eax & mask.eax, e1->ebx & mask.ebx,
114 e1->ecx & mask.ecx, e1->edx & mask.edx,
115 e2->eax & mask.eax, e2->ebx & mask.ebx,
116 e2->ecx & mask.ecx, e2->edx & mask.edx);
117 }
118 }
119
run_vcpu(struct kvm_vcpu * vcpu,int stage)120 static void run_vcpu(struct kvm_vcpu *vcpu, int stage)
121 {
122 struct ucall uc;
123
124 vcpu_run(vcpu);
125
126 switch (get_ucall(vcpu, &uc)) {
127 case UCALL_SYNC:
128 TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
129 uc.args[1] == stage + 1,
130 "Stage %d: Unexpected register values vmexit, got %lx",
131 stage + 1, (ulong)uc.args[1]);
132 return;
133 case UCALL_DONE:
134 return;
135 case UCALL_ABORT:
136 REPORT_GUEST_ASSERT(uc);
137 default:
138 TEST_ASSERT(false, "Unexpected exit: %s",
139 exit_reason_str(vcpu->run->exit_reason));
140 }
141 }
142
vcpu_alloc_cpuid(struct kvm_vm * vm,vm_vaddr_t * p_gva,struct kvm_cpuid2 * cpuid)143 struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct kvm_cpuid2 *cpuid)
144 {
145 int size = sizeof(*cpuid) + cpuid->nent * sizeof(cpuid->entries[0]);
146 vm_vaddr_t gva = vm_vaddr_alloc(vm, size, KVM_UTIL_MIN_VADDR);
147 struct kvm_cpuid2 *guest_cpuids = addr_gva2hva(vm, gva);
148
149 memcpy(guest_cpuids, cpuid, size);
150
151 *p_gva = gva;
152 return guest_cpuids;
153 }
154
set_cpuid_after_run(struct kvm_vcpu * vcpu)155 static void set_cpuid_after_run(struct kvm_vcpu *vcpu)
156 {
157 struct kvm_cpuid_entry2 *ent;
158 struct kvm_sregs sregs;
159 int rc;
160 u32 eax, ebx, x;
161
162 /* Setting unmodified CPUID is allowed */
163 rc = __vcpu_set_cpuid(vcpu);
164 TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
165
166 /*
167 * Toggle CR4 bits that affect dynamic CPUID feature flags to verify
168 * setting unmodified CPUID succeeds with runtime CPUID updates.
169 */
170 vcpu_sregs_get(vcpu, &sregs);
171 if (kvm_cpu_has(X86_FEATURE_XSAVE))
172 sregs.cr4 ^= X86_CR4_OSXSAVE;
173 if (kvm_cpu_has(X86_FEATURE_PKU))
174 sregs.cr4 ^= X86_CR4_PKE;
175 vcpu_sregs_set(vcpu, &sregs);
176
177 rc = __vcpu_set_cpuid(vcpu);
178 TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
179
180 /* Changing CPU features is forbidden */
181 ent = vcpu_get_cpuid_entry(vcpu, 0x7);
182 ebx = ent->ebx;
183 ent->ebx--;
184 rc = __vcpu_set_cpuid(vcpu);
185 TEST_ASSERT(rc, "Changing CPU features should fail");
186 ent->ebx = ebx;
187
188 /* Changing MAXPHYADDR is forbidden */
189 ent = vcpu_get_cpuid_entry(vcpu, 0x80000008);
190 eax = ent->eax;
191 x = eax & 0xff;
192 ent->eax = (eax & ~0xffu) | (x - 1);
193 rc = __vcpu_set_cpuid(vcpu);
194 TEST_ASSERT(rc, "Changing MAXPHYADDR should fail");
195 ent->eax = eax;
196 }
197
test_get_cpuid2(struct kvm_vcpu * vcpu)198 static void test_get_cpuid2(struct kvm_vcpu *vcpu)
199 {
200 struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent + 1);
201 int i, r;
202
203 vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
204 TEST_ASSERT(cpuid->nent == vcpu->cpuid->nent,
205 "KVM didn't update nent on success, wanted %u, got %u",
206 vcpu->cpuid->nent, cpuid->nent);
207
208 for (i = 0; i < vcpu->cpuid->nent; i++) {
209 cpuid->nent = i;
210 r = __vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
211 TEST_ASSERT(r && errno == E2BIG, KVM_IOCTL_ERROR(KVM_GET_CPUID2, r));
212 TEST_ASSERT(cpuid->nent == i, "KVM modified nent on failure");
213 }
214 free(cpuid);
215 }
216
main(void)217 int main(void)
218 {
219 struct kvm_vcpu *vcpu;
220 vm_vaddr_t cpuid_gva;
221 struct kvm_vm *vm;
222 int stage;
223
224 vm = vm_create_with_one_vcpu(&vcpu, guest_main);
225
226 compare_cpuids(kvm_get_supported_cpuid(), vcpu->cpuid);
227
228 vcpu_alloc_cpuid(vm, &cpuid_gva, vcpu->cpuid);
229
230 vcpu_args_set(vcpu, 1, cpuid_gva);
231
232 for (stage = 0; stage < 3; stage++)
233 run_vcpu(vcpu, stage);
234
235 set_cpuid_after_run(vcpu);
236
237 test_get_cpuid2(vcpu);
238
239 kvm_vm_free(vm);
240 }
241