Merge tag 'kvm-x86-pvunhalt-6.9' of https://github.com/kvm-x86/linux into HEAD
authorPaolo Bonzini <pbonzini@redhat.com>
Mon, 18 Mar 2024 23:19:08 +0000 (19:19 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 18 Mar 2024 23:19:08 +0000 (19:19 -0400)
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.

arch/x86/kvm/cpuid.c
tools/testing/selftests/kvm/include/x86_64/processor.h
tools/testing/selftests/kvm/x86_64/kvm_pv_test.c

index adba49afb5fe63b1de9345579615284593e00468..bfc0bfcb2bc60dd2860fa4617f3dc92dea1f3a97 100644 (file)
@@ -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)))
-               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);
index 3bd03b088dda605348c7f85fc8d190ef63cf9e5e..81ce37ec407dd18b04abab3ff0a40c8deca9cd65 100644 (file)
@@ -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);
index 9e2879af7c201fb749120cee94a61e03dfaf162b..40cc59f4e6501316695485a10532479787899d81 100644 (file)
@@ -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();
 }