]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: x86: Initialize guest cpu_caps based on KVM support
authorSean Christopherson <seanjc@google.com>
Thu, 28 Nov 2024 01:34:11 +0000 (17:34 -0800)
committerSean Christopherson <seanjc@google.com>
Wed, 18 Dec 2024 22:20:09 +0000 (14:20 -0800)
Constrain all guest cpu_caps based on KVM support instead of constraining
only the few features that KVM _currently_ needs to verify are actually
supported by KVM.  The intent of cpu_caps is to track what the guest is
actually capable of using, not the raw, unfiltered CPUID values that the
guest sees.

I.e. KVM should always consult it's only support when making decisions
based on guest CPUID, and the only reason KVM has historically made the
checks opt-in was due to lack of centralized tracking.

Suggested-by: Maxim Levitsky <mlevitsk@redhat.com>
Link: https://lore.kernel.org/r/20241128013424.4096668-45-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/cpuid.c
arch/x86/kvm/cpuid.h
arch/x86/kvm/svm/svm.c
arch/x86/kvm/vmx/vmx.c

index 17e372de7ee840f85720183ad04b3f975ea51921..b02f5098108abaaddd2b7650df6f5580e0ec623f 100644 (file)
@@ -376,6 +376,9 @@ static u32 cpuid_get_reg_unsafe(struct kvm_cpuid_entry2 *entry, u32 reg)
        }
 }
 
+static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func,
+                              bool include_partially_emulated);
+
 void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 {
        struct kvm_lapic *apic = vcpu->arch.apic;
@@ -394,6 +397,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
         */
        for (i = 0; i < NR_KVM_CPU_CAPS; i++) {
                const struct cpuid_reg cpuid = reverse_cpuid[i];
+               struct kvm_cpuid_entry2 emulated;
 
                if (!cpuid.function)
                        continue;
@@ -402,7 +406,16 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                if (!entry)
                        continue;
 
-               vcpu->arch.cpu_caps[i] = cpuid_get_reg_unsafe(entry, cpuid.reg);
+               cpuid_func_emulated(&emulated, cpuid.function, true);
+
+               /*
+                * A vCPU has a feature if it's supported by KVM and is enabled
+                * in guest CPUID.  Note, this includes features that are
+                * supported by KVM but aren't advertised to userspace!
+                */
+               vcpu->arch.cpu_caps[i] = kvm_cpu_caps[i] |
+                                        cpuid_get_reg_unsafe(&emulated, cpuid.reg);
+               vcpu->arch.cpu_caps[i] &= cpuid_get_reg_unsafe(entry, cpuid.reg);
        }
 
        kvm_update_cpuid_runtime(vcpu);
index 0a3b27547d2fe6e23fd808b9fcd580f0f053be15..8f978f9a40f80e3f312fcc126bb0842a593b0514 100644 (file)
@@ -264,13 +264,6 @@ static __always_inline void guest_cpu_cap_change(struct kvm_vcpu *vcpu,
                guest_cpu_cap_clear(vcpu, x86_feature);
 }
 
-static __always_inline void guest_cpu_cap_constrain(struct kvm_vcpu *vcpu,
-                                                   unsigned int x86_feature)
-{
-       if (!kvm_cpu_cap_has(x86_feature))
-               guest_cpu_cap_clear(vcpu, x86_feature);
-}
-
 static __always_inline bool guest_cpu_cap_has(struct kvm_vcpu *vcpu,
                                              unsigned int x86_feature)
 {
index 3b94cb6c2b7a5e0fb87c76ae1c14fda5f42ae624..0045fe474023875ba5d4c9f91f17a5fcde1b5382 100644 (file)
@@ -4406,10 +4406,6 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                             boot_cpu_has(X86_FEATURE_XSAVES) &&
                             guest_cpuid_has(vcpu, X86_FEATURE_XSAVE));
 
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_NRIPS);
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_TSCRATEMSR);
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_LBRV);
-
        /*
         * Intercept VMLOAD if the vCPU model is Intel in order to emulate that
         * VMLOAD drops bits 63:32 of SYSENTER (ignoring the fact that exposing
@@ -4417,13 +4413,6 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
         */
        if (guest_cpuid_is_intel_compatible(vcpu))
                guest_cpu_cap_clear(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
-       else
-               guest_cpu_cap_constrain(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
-
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_PAUSEFILTER);
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_PFTHRESHOLD);
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_VGIF);
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_VNMI);
 
        svm_recalc_instruction_intercepts(vcpu, svm);
 
index 8b95ba323a17c54be44d7c74b56061bc707a4646..a7c2c36f2a4fb75286fd4e02dbb44aa53edc21af 100644 (file)
@@ -7828,15 +7828,10 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
         * to the guest.  XSAVES depends on CR4.OSXSAVE, and CR4.OSXSAVE can be
         * set if and only if XSAVE is supported.
         */
-       if (boot_cpu_has(X86_FEATURE_XSAVE) &&
-           guest_cpuid_has(vcpu, X86_FEATURE_XSAVE))
-               guest_cpu_cap_constrain(vcpu, X86_FEATURE_XSAVES);
-       else
+       if (!boot_cpu_has(X86_FEATURE_XSAVE) ||
+           !guest_cpuid_has(vcpu, X86_FEATURE_XSAVE))
                guest_cpu_cap_clear(vcpu, X86_FEATURE_XSAVES);
 
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_VMX);
-       guest_cpu_cap_constrain(vcpu, X86_FEATURE_LAM);
-
        vmx_setup_uret_msrs(vmx);
 
        if (cpu_has_secondary_exec_ctrls())