Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/kvm/x86/cpuid_test.c
38245 views
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
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
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
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
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
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
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
155
static void set_cpuid_after_run(struct kvm_vcpu *vcpu)
156
{
157
struct kvm_cpuid_entry2 *ent;
158
int rc;
159
u32 eax, ebx, x;
160
161
/* Setting unmodified CPUID is allowed */
162
rc = __vcpu_set_cpuid(vcpu);
163
TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);
164
165
/* Changing CPU features is forbidden */
166
ent = vcpu_get_cpuid_entry(vcpu, 0x7);
167
ebx = ent->ebx;
168
ent->ebx--;
169
rc = __vcpu_set_cpuid(vcpu);
170
TEST_ASSERT(rc, "Changing CPU features should fail");
171
ent->ebx = ebx;
172
173
/* Changing MAXPHYADDR is forbidden */
174
ent = vcpu_get_cpuid_entry(vcpu, 0x80000008);
175
eax = ent->eax;
176
x = eax & 0xff;
177
ent->eax = (eax & ~0xffu) | (x - 1);
178
rc = __vcpu_set_cpuid(vcpu);
179
TEST_ASSERT(rc, "Changing MAXPHYADDR should fail");
180
ent->eax = eax;
181
}
182
183
static void test_get_cpuid2(struct kvm_vcpu *vcpu)
184
{
185
struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent + 1);
186
int i, r;
187
188
vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
189
TEST_ASSERT(cpuid->nent == vcpu->cpuid->nent,
190
"KVM didn't update nent on success, wanted %u, got %u",
191
vcpu->cpuid->nent, cpuid->nent);
192
193
for (i = 0; i < vcpu->cpuid->nent; i++) {
194
cpuid->nent = i;
195
r = __vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
196
TEST_ASSERT(r && errno == E2BIG, KVM_IOCTL_ERROR(KVM_GET_CPUID2, r));
197
TEST_ASSERT(cpuid->nent == i, "KVM modified nent on failure");
198
}
199
free(cpuid);
200
}
201
202
int main(void)
203
{
204
struct kvm_vcpu *vcpu;
205
vm_vaddr_t cpuid_gva;
206
struct kvm_vm *vm;
207
int stage;
208
209
vm = vm_create_with_one_vcpu(&vcpu, guest_main);
210
211
compare_cpuids(kvm_get_supported_cpuid(), vcpu->cpuid);
212
213
vcpu_alloc_cpuid(vm, &cpuid_gva, vcpu->cpuid);
214
215
vcpu_args_set(vcpu, 1, cpuid_gva);
216
217
for (stage = 0; stage < 3; stage++)
218
run_vcpu(vcpu, stage);
219
220
set_cpuid_after_run(vcpu);
221
222
test_get_cpuid2(vcpu);
223
224
kvm_vm_free(vm);
225
}
226
227