KVM: VMX: Unconditionally clear CPUID.INVPCID if !CPUID.PCID
authorSean Christopherson <sean.j.christopherson@intel.com>
Wed, 23 Sep 2020 16:50:46 +0000 (09:50 -0700)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 28 Sep 2020 11:57:29 +0000 (07:57 -0400)
If PCID is not exposed to the guest, clear INVPCID in the guest's CPUID
even if the VMCS INVPCID enable is not supported.  This will allow
consolidating the secondary execution control adjustment code without
having to special case INVPCID.

Technically, this fixes a bug where !CPUID.PCID && CPUID.INVCPID would
result in unexpected guest behavior (#UD instead of #GP/#PF), but KVM
doesn't support exposing INVPCID if it's not supported in the VMCS, i.e.
such a config is broken/bogus no matter what.

Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Message-Id: <20200923165048.20486-3-sean.j.christopherson@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/vmx/vmx.c

index 96adc964afed3bf9049e61f375fccb686f839260..641e5ef513aea4010ae948143385e47f93de83f9 100644 (file)
@@ -4158,16 +4158,22 @@ static void vmx_compute_secondary_exec_control(struct vcpu_vmx *vmx)
                }
        }
 
+       /*
+        * Expose INVPCID if and only if PCID is also exposed to the guest.
+        * INVPCID takes a #UD when it's disabled in the VMCS, but a #GP or #PF
+        * if CR4.PCIDE=0.  Enumerating CPUID.INVPCID=1 would lead to incorrect
+        * behavior from the guest perspective (it would expect #GP or #PF).
+        */
+       if (!guest_cpuid_has(vcpu, X86_FEATURE_PCID))
+               guest_cpuid_clear(vcpu, X86_FEATURE_INVPCID);
+
        if (cpu_has_vmx_invpcid()) {
                /* Exposing INVPCID only when PCID is exposed */
                bool invpcid_enabled =
-                       guest_cpuid_has(vcpu, X86_FEATURE_INVPCID) &&
-                       guest_cpuid_has(vcpu, X86_FEATURE_PCID);
+                       guest_cpuid_has(vcpu, X86_FEATURE_INVPCID);
 
-               if (!invpcid_enabled) {
+               if (!invpcid_enabled)
                        exec_control &= ~SECONDARY_EXEC_ENABLE_INVPCID;
-                       guest_cpuid_clear(vcpu, X86_FEATURE_INVPCID);
-               }
 
                if (nested) {
                        if (invpcid_enabled)