Commit 0d175648 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge tag 'kvm-x86-pvunhalt-6.9' of https://github.com/kvm-x86/linux into HEAD

Fix a bug in KVM_SET_CPUID{2,} where KVM looks at the wrong CPUID entries (old
vs. new) and ultimately neglects to clear PV_UNHALT from vCPUs with HLT-exiting
disabled.
parents c20722c4 c2585047
Loading
Loading
Loading
Loading
+26 −16
Original line number Diff line number Diff line
@@ -189,15 +189,15 @@ static int kvm_cpuid_check_equal(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2
	return 0;
}

static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcpu,
							    const char *sig)
static struct kvm_hypervisor_cpuid __kvm_get_hypervisor_cpuid(struct kvm_cpuid_entry2 *entries,
							      int nent, const char *sig)
{
	struct kvm_hypervisor_cpuid cpuid = {};
	struct kvm_cpuid_entry2 *entry;
	u32 base;

	for_each_possible_hypervisor_cpuid_base(base) {
		entry = kvm_find_cpuid_entry(vcpu, base);
		entry = cpuid_entry2_find(entries, nent, base, KVM_CPUID_INDEX_NOT_SIGNIFICANT);

		if (entry) {
			u32 signature[3];
@@ -217,22 +217,29 @@ static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcp
	return cpuid;
}

static struct kvm_cpuid_entry2 *__kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu,
					      struct kvm_cpuid_entry2 *entries, int nent)
static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcpu,
							    const char *sig)
{
	u32 base = vcpu->arch.kvm_cpuid.base;

	if (!base)
		return NULL;
	return __kvm_get_hypervisor_cpuid(vcpu->arch.cpuid_entries,
					  vcpu->arch.cpuid_nent, sig);
}

	return cpuid_entry2_find(entries, nent, base | KVM_CPUID_FEATURES,
static struct kvm_cpuid_entry2 *__kvm_find_kvm_cpuid_features(struct kvm_cpuid_entry2 *entries,
							      int nent, u32 kvm_cpuid_base)
{
	return cpuid_entry2_find(entries, nent, kvm_cpuid_base | KVM_CPUID_FEATURES,
				 KVM_CPUID_INDEX_NOT_SIGNIFICANT);
}

static struct kvm_cpuid_entry2 *kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu)
{
	return __kvm_find_kvm_cpuid_features(vcpu, vcpu->arch.cpuid_entries,
					     vcpu->arch.cpuid_nent);
	u32 base = vcpu->arch.kvm_cpuid.base;

	if (!base)
		return NULL;

	return __kvm_find_kvm_cpuid_features(vcpu->arch.cpuid_entries,
					     vcpu->arch.cpuid_nent, base);
}

void kvm_update_pv_runtime(struct kvm_vcpu *vcpu)
@@ -266,6 +273,7 @@ static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_e
				       int nent)
{
	struct kvm_cpuid_entry2 *best;
	struct kvm_hypervisor_cpuid kvm_cpuid;

	best = cpuid_entry2_find(entries, nent, 1, KVM_CPUID_INDEX_NOT_SIGNIFICANT);
	if (best) {
@@ -292,10 +300,12 @@ static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_e
		     cpuid_entry_has(best, X86_FEATURE_XSAVEC)))
		best->ebx = xstate_required_size(vcpu->arch.xcr0, true);

	best = __kvm_find_kvm_cpuid_features(vcpu, entries, nent);
	if (kvm_hlt_in_guest(vcpu->kvm) && best &&
		(best->eax & (1 << KVM_FEATURE_PV_UNHALT)))
	kvm_cpuid = __kvm_get_hypervisor_cpuid(entries, nent, KVM_SIGNATURE);
	if (kvm_cpuid.base) {
		best = __kvm_find_kvm_cpuid_features(entries, nent, kvm_cpuid.base);
		if (kvm_hlt_in_guest(vcpu->kvm) && best)
			best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT);
	}

	if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT)) {
		best = cpuid_entry2_find(entries, nent, 0x1, KVM_CPUID_INDEX_NOT_SIGNIFICANT);
+11 −0
Original line number Diff line number Diff line
@@ -1037,8 +1037,19 @@ static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu)
void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu,
			     struct kvm_x86_cpu_property property,
			     uint32_t value);
void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, uint8_t maxphyaddr);

void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function);

static inline bool vcpu_cpuid_has(struct kvm_vcpu *vcpu,
				  struct kvm_x86_cpu_feature feature)
{
	struct kvm_cpuid_entry2 *entry;

	entry = __vcpu_get_cpuid_entry(vcpu, feature.function, feature.index);
	return *((&entry->eax) + feature.reg) & BIT(feature.bit);
}

void vcpu_set_or_clear_cpuid_feature(struct kvm_vcpu *vcpu,
				     struct kvm_x86_cpu_feature feature,
				     bool set);
+39 −0
Original line number Diff line number Diff line
@@ -133,6 +133,43 @@ static void enter_guest(struct kvm_vcpu *vcpu)
	}
}

static void test_pv_unhalt(void)
{
	struct kvm_vcpu *vcpu;
	struct kvm_vm *vm;
	struct kvm_cpuid_entry2 *ent;
	u32 kvm_sig_old;

	pr_info("testing KVM_FEATURE_PV_UNHALT\n");

	TEST_REQUIRE(KVM_CAP_X86_DISABLE_EXITS);

	/* KVM_PV_UNHALT test */
	vm = vm_create_with_one_vcpu(&vcpu, guest_main);
	vcpu_set_cpuid_feature(vcpu, X86_FEATURE_KVM_PV_UNHALT);

	TEST_ASSERT(vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),
		    "Enabling X86_FEATURE_KVM_PV_UNHALT had no effect");

	/* Make sure KVM clears vcpu->arch.kvm_cpuid */
	ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE);
	kvm_sig_old = ent->ebx;
	ent->ebx = 0xdeadbeef;
	vcpu_set_cpuid(vcpu);

	vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, KVM_X86_DISABLE_EXITS_HLT);
	ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE);
	ent->ebx = kvm_sig_old;
	vcpu_set_cpuid(vcpu);

	TEST_ASSERT(!vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),
		    "KVM_FEATURE_PV_UNHALT is set with KVM_CAP_X86_DISABLE_EXITS");

	/* FIXME: actually test KVM_FEATURE_PV_UNHALT feature */

	kvm_vm_free(vm);
}

int main(void)
{
	struct kvm_vcpu *vcpu;
@@ -151,4 +188,6 @@ int main(void)

	enter_guest(vcpu);
	kvm_vm_free(vm);

	test_pv_unhalt();
}