Commit 7fbc6038 authored by Sean Christopherson's avatar Sean Christopherson
Browse files

KVM: selftests: Cache CPUID in struct kvm_vcpu



Cache a vCPU's CPUID information in "struct kvm_vcpu" to allow fixing the
mess where tests, often unknowingly, modify the global/static "cpuid"
allocated by kvm_get_supported_cpuid().

Add vcpu_init_cpuid() to handle stuffing an entirely different CPUID
model, e.g. during vCPU creation or when switching to the Hyper-V enabled
CPUID model.  Automatically refresh the cache on vcpu_set_cpuid() so that
any adjustments made by KVM are always reflected in the cache.  Drop
vcpu_get_cpuid() entirely to force tests to use the cache, and to allow
adding e.g. vcpu_get_cpuid_entry() in the future without creating a
conflicting set of APIs where vcpu_get_cpuid() does KVM_GET_CPUID2, but
vcpu_get_cpuid_entry() does not.

Opportunistically convert the VMX nested state test and KVM PV test to
manipulating the vCPU's CPUID (because it's easy), but use
vcpu_init_cpuid() for the Hyper-V features test and "emulator error" test
to effectively retain their current behavior as they're less trivial to
convert.

Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/r/20220614200707.3315957-19-seanjc@google.com
parent fc66963d
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@ struct kvm_vcpu {
	int fd;
	struct kvm_vm *vm;
	struct kvm_run *run;
#ifdef __x86_64__
	struct kvm_cpuid2 *cpuid;
#endif
	struct kvm_dirty_gfn *dirty_gfns;
	uint32_t fetch_index;
	uint32_t dirty_gfns_count;
@@ -748,6 +751,8 @@ static inline struct kvm_vcpu *vm_vcpu_recreate(struct kvm_vm *vm,
	return vm_arch_vcpu_recreate(vm, vcpu_id);
}

void vcpu_arch_free(struct kvm_vcpu *vcpu);

void virt_arch_pgd_alloc(struct kvm_vm *vm);

static inline void virt_pgd_alloc(struct kvm_vm *vm)
+18 −7
Original line number Diff line number Diff line
@@ -618,18 +618,29 @@ static inline struct kvm_cpuid2 *allocate_kvm_cpuid2(int nr_entries)
	return cpuid;
}

struct kvm_cpuid2 *vcpu_get_cpuid(struct kvm_vcpu *vcpu);
void vcpu_init_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid);

static inline int __vcpu_set_cpuid(struct kvm_vcpu *vcpu,
				   struct kvm_cpuid2 *cpuid)
static inline int __vcpu_set_cpuid(struct kvm_vcpu *vcpu)
{
	return __vcpu_ioctl(vcpu, KVM_SET_CPUID2, cpuid);
	int r;

	TEST_ASSERT(vcpu->cpuid, "Must do vcpu_init_cpuid() first");
	r = __vcpu_ioctl(vcpu, KVM_SET_CPUID2, vcpu->cpuid);
	if (r)
		return r;

	/* On success, refresh the cache to pick up adjustments made by KVM. */
	vcpu_ioctl(vcpu, KVM_GET_CPUID2, vcpu->cpuid);
	return 0;
}

static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu,
				  struct kvm_cpuid2 *cpuid)
static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu)
{
	vcpu_ioctl(vcpu, KVM_SET_CPUID2, cpuid);
	TEST_ASSERT(vcpu->cpuid, "Must do vcpu_init_cpuid() first");
	vcpu_ioctl(vcpu, KVM_SET_CPUID2, vcpu->cpuid);

	/* Refresh the cache to pick up adjustments made by KVM. */
	vcpu_ioctl(vcpu, KVM_GET_CPUID2, vcpu->cpuid);
}

struct kvm_cpuid_entry2 *
+7 −0
Original line number Diff line number Diff line
@@ -472,6 +472,11 @@ kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start,
	return &region->region;
}

__weak void vcpu_arch_free(struct kvm_vcpu *vcpu)
{

}

/*
 * VM VCPU Remove
 *
@@ -501,6 +506,8 @@ static void vm_vcpu_rm(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
	TEST_ASSERT(!ret,  __KVM_SYSCALL_ERROR("close()", ret));

	list_del(&vcpu->list);

	vcpu_arch_free(vcpu);
	free(vcpu);
}

+20 −22
Original line number Diff line number Diff line
@@ -648,7 +648,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
				     DEFAULT_GUEST_STACK_VADDR_MIN);

	vcpu = __vm_vcpu_add(vm, vcpu_id);
	vcpu_set_cpuid(vcpu, kvm_get_supported_cpuid());
	vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
	vcpu_setup(vm, vcpu);

	/* Setup guest general purpose registers */
@@ -669,11 +669,17 @@ struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, uint32_t vcpu_id)
{
	struct kvm_vcpu *vcpu = __vm_vcpu_add(vm, vcpu_id);

	vcpu_set_cpuid(vcpu, kvm_get_supported_cpuid());
	vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());

	return vcpu;
}

void vcpu_arch_free(struct kvm_vcpu *vcpu)
{
	if (vcpu->cpuid)
		free(vcpu->cpuid);
}

/*
 * KVM Supported CPUID Get
 *
@@ -743,30 +749,22 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index)
	return buffer.entry.data;
}

struct kvm_cpuid2 *vcpu_get_cpuid(struct kvm_vcpu *vcpu)
void vcpu_init_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid)
{
	struct kvm_cpuid2 *cpuid;
	int max_ent;
	int rc = -1;

	cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
	max_ent = cpuid->nent;
	TEST_ASSERT(cpuid != vcpu->cpuid, "@cpuid can't be the vCPU's CPUID");

	for (cpuid->nent = 1; cpuid->nent <= max_ent; cpuid->nent++) {
		rc = __vcpu_ioctl(vcpu, KVM_GET_CPUID2, cpuid);
		if (!rc)
			break;

		TEST_ASSERT(rc == -1 && errno == E2BIG,
			    "KVM_GET_CPUID2 should either succeed or give E2BIG: %d %d",
			    rc, errno);
	}

	TEST_ASSERT(!rc, KVM_IOCTL_ERROR(KVM_GET_CPUID2, rc));
	return cpuid;
	/* Allow overriding the default CPUID. */
	if (vcpu->cpuid && vcpu->cpuid->nent < cpuid->nent) {
		free(vcpu->cpuid);
		vcpu->cpuid = NULL;
	}

	if (!vcpu->cpuid)
		vcpu->cpuid = allocate_kvm_cpuid2(cpuid->nent);

	memcpy(vcpu->cpuid, cpuid, kvm_cpuid2_size(cpuid->nent));
	vcpu_set_cpuid(vcpu);
}

/*
 * Locate a cpuid entry.
@@ -1302,7 +1300,7 @@ void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu)
		cpuid_full->nent = nent + cpuid_hv->nent;
	}

	vcpu_set_cpuid(vcpu, cpuid_full);
	vcpu_init_cpuid(vcpu, cpuid_full);
}

struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu)
+9 −9
Original line number Diff line number Diff line
@@ -144,21 +144,22 @@ struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct
	return guest_cpuids;
}

static void set_cpuid_after_run(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid)
static void set_cpuid_after_run(struct kvm_vcpu *vcpu)
{
	struct kvm_cpuid2 *cpuid = vcpu->cpuid;
	struct kvm_cpuid_entry2 *ent;
	int rc;
	u32 eax, ebx, x;

	/* Setting unmodified CPUID is allowed */
	rc = __vcpu_set_cpuid(vcpu, cpuid);
	rc = __vcpu_set_cpuid(vcpu);
	TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc);

	/* Changing CPU features is forbidden */
	ent = get_cpuid(cpuid, 0x7, 0);
	ebx = ent->ebx;
	ent->ebx--;
	rc = __vcpu_set_cpuid(vcpu, cpuid);
	rc = __vcpu_set_cpuid(vcpu);
	TEST_ASSERT(rc, "Changing CPU features should fail");
	ent->ebx = ebx;

@@ -167,14 +168,14 @@ static void set_cpuid_after_run(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid)
	eax = ent->eax;
	x = eax & 0xff;
	ent->eax = (eax & ~0xffu) | (x - 1);
	rc = __vcpu_set_cpuid(vcpu, cpuid);
	rc = __vcpu_set_cpuid(vcpu);
	TEST_ASSERT(rc, "Changing MAXPHYADDR should fail");
	ent->eax = eax;
}

int main(void)
{
	struct kvm_cpuid2 *supp_cpuid, *cpuid2;
	struct kvm_cpuid2 *supp_cpuid;
	struct kvm_vcpu *vcpu;
	vm_vaddr_t cpuid_gva;
	struct kvm_vm *vm;
@@ -183,18 +184,17 @@ int main(void)
	vm = vm_create_with_one_vcpu(&vcpu, guest_main);

	supp_cpuid = kvm_get_supported_cpuid();
	cpuid2 = vcpu_get_cpuid(vcpu);

	compare_cpuids(supp_cpuid, cpuid2);
	compare_cpuids(supp_cpuid, vcpu->cpuid);

	vcpu_alloc_cpuid(vm, &cpuid_gva, cpuid2);
	vcpu_alloc_cpuid(vm, &cpuid_gva, vcpu->cpuid);

	vcpu_args_set(vcpu, 1, cpuid_gva);

	for (stage = 0; stage < 3; stage++)
		run_vcpu(vcpu, stage);

	set_cpuid_after_run(vcpu, cpuid2);
	set_cpuid_after_run(vcpu);

	kvm_vm_free(vm);
}
Loading