Commit 8c01290b authored by Sean Christopherson's avatar Sean Christopherson
Browse files

KVM: x86: Swap incoming guest CPUID into vCPU before massaging in KVM_SET_CPUID2



When handling KVM_SET_CPUID{,2}, swap the old and new CPUID arrays and
lengths before processing the new CPUID, and simply undo the swap if
setting the new CPUID fails for whatever reason.

To keep the diff reasonable, continue passing the entry array and length
to most helpers, and defer the more complete cleanup to future commits.

For any sane VMM, setting "bad" CPUID state is not a hot path (or even
something that is surviable), and setting guest CPUID before it's known
good will allow removing all of KVM's infrastructure for processing CPUID
entries directly (as opposed to operating on vcpu->arch.cpuid_entries).

Reviewed-by: default avatarMaxim Levitsky <mlevitsk@redhat.com>
Link: https://lore.kernel.org/r/20241128013424.4096668-32-seanjc@google.com


Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
parent 6174004e
Loading
Loading
Loading
Loading
+32 −22
Original line number Diff line number Diff line
@@ -142,10 +142,10 @@ static inline struct kvm_cpuid_entry2 *cpuid_entry2_find(
	return NULL;
}

static int kvm_check_cpuid(struct kvm_vcpu *vcpu,
			   struct kvm_cpuid_entry2 *entries,
			   int nent)
static int kvm_check_cpuid(struct kvm_vcpu *vcpu)
{
	struct kvm_cpuid_entry2 *entries = vcpu->arch.cpuid_entries;
	int nent = vcpu->arch.cpuid_nent;
	struct kvm_cpuid_entry2 *best;
	u64 xfeatures;

@@ -178,9 +178,6 @@ static int kvm_check_cpuid(struct kvm_vcpu *vcpu,
	return fpu_enable_guest_xfd_features(&vcpu->arch.guest_fpu, xfeatures);
}

static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *entries,
				       int nent);

/* Check whether the supplied CPUID data is equal to what is already set for the vCPU. */
static int kvm_cpuid_check_equal(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *e2,
				 int nent)
@@ -196,8 +193,10 @@ static int kvm_cpuid_check_equal(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2
	 * CPUID processing is functionally correct only because any change in
	 * CPUID is disallowed, i.e. using stale data is ok because the below
	 * checks will reject the change.
	 *
	 * Note!  @e2 and @nent track the _old_ CPUID entries!
	 */
	__kvm_update_cpuid_runtime(vcpu, e2, nent);
	kvm_update_cpuid_runtime(vcpu);

	if (nent != vcpu->arch.cpuid_nent)
		return -EINVAL;
@@ -350,9 +349,11 @@ void kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu)
}
EXPORT_SYMBOL_GPL(kvm_update_cpuid_runtime);

static bool kvm_cpuid_has_hyperv(struct kvm_cpuid_entry2 *entries, int nent)
static bool kvm_cpuid_has_hyperv(struct kvm_vcpu *vcpu)
{
#ifdef CONFIG_KVM_HYPERV
	struct kvm_cpuid_entry2 *entries = vcpu->arch.cpuid_entries;
	int nent = vcpu->arch.cpuid_nent;
	struct kvm_cpuid_entry2 *entry;

	entry = cpuid_entry2_find(entries, nent, HYPERV_CPUID_INTERFACE,
@@ -429,8 +430,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
					 __cr4_reserved_bits(guest_cpuid_has, vcpu);
#undef __kvm_cpu_cap_has

	kvm_hv_set_cpuid(vcpu, kvm_cpuid_has_hyperv(vcpu->arch.cpuid_entries,
						    vcpu->arch.cpuid_nent));
	kvm_hv_set_cpuid(vcpu, kvm_cpuid_has_hyperv(vcpu));

	/* Invoke the vendor callback only after the above state is updated. */
	kvm_x86_call(vcpu_after_set_cpuid)(vcpu);
@@ -471,6 +471,15 @@ static int kvm_set_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *e2,
{
	int r;

	/*
	 * Swap the existing (old) entries with the incoming (new) entries in
	 * order to massage the new entries, e.g. to account for dynamic bits
	 * that KVM controls, without clobbering the current guest CPUID, which
	 * KVM needs to preserve in order to unwind on failure.
	 */
	swap(vcpu->arch.cpuid_entries, e2);
	swap(vcpu->arch.cpuid_nent, nent);

	/*
	 * KVM does not correctly handle changing guest CPUID after KVM_RUN, as
	 * MAXPHYADDR, GBPAGES support, AMD reserved bit behavior, etc.. aren't
@@ -485,27 +494,21 @@ static int kvm_set_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *e2,
	if (kvm_vcpu_has_run(vcpu)) {
		r = kvm_cpuid_check_equal(vcpu, e2, nent);
		if (r)
			return r;

		kvfree(e2);
		return 0;
			goto err;
		goto success;
	}

#ifdef CONFIG_KVM_HYPERV
	if (kvm_cpuid_has_hyperv(e2, nent)) {
	if (kvm_cpuid_has_hyperv(vcpu)) {
		r = kvm_hv_vcpu_init(vcpu);
		if (r)
			return r;
			goto err;
	}
#endif

	r = kvm_check_cpuid(vcpu, e2, nent);
	r = kvm_check_cpuid(vcpu);
	if (r)
		return r;

	kvfree(vcpu->arch.cpuid_entries);
	vcpu->arch.cpuid_entries = e2;
	vcpu->arch.cpuid_nent = nent;
		goto err;

	vcpu->arch.kvm_cpuid = kvm_get_hypervisor_cpuid(vcpu, KVM_SIGNATURE);
#ifdef CONFIG_KVM_XEN
@@ -513,7 +516,14 @@ static int kvm_set_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *e2,
#endif
	kvm_vcpu_after_set_cpuid(vcpu);

success:
	kvfree(e2);
	return 0;

err:
	swap(vcpu->arch.cpuid_entries, e2);
	swap(vcpu->arch.cpuid_nent, nent);
	return r;
}

/* when an old userspace process fills a new kernel module */