File Index
Files and directories
Sign in
FILE

cpuid_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/x86/cpuid_test.c C
  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
 15struct 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
 27static 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
 45static 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
 58static 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
 83static 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
120static 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
143struct 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
155static 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
198static 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
217int 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}
~/ Read-only file preview.