]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - arch/x86/kvm/cpuid.c
kvm: x86: Improve emulation of CPUID leaves 0BH and 1FH
[linux.git] / arch / x86 / kvm / cpuid.c
index 22c2720cd948e321d3f0021670c1dce1e0b5799f..a26a1ae03145a2a3bcad4163cfc9615621c10997 100644 (file)
@@ -304,7 +304,13 @@ static void do_host_cpuid(struct kvm_cpuid_entry2 *entry, u32 function,
        case 7:
        case 0xb:
        case 0xd:
+       case 0xf:
+       case 0x10:
+       case 0x12:
        case 0x14:
+       case 0x17:
+       case 0x18:
+       case 0x1f:
        case 0x8000001d:
                entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
                break;
@@ -360,7 +366,7 @@ static inline void do_cpuid_7_mask(struct kvm_cpuid_entry2 *entry, int index)
                F(AVX512VBMI) | F(LA57) | F(PKU) | 0 /*OSPKE*/ |
                F(AVX512_VPOPCNTDQ) | F(UMIP) | F(AVX512_VBMI2) | F(GFNI) |
                F(VAES) | F(VPCLMULQDQ) | F(AVX512_VNNI) | F(AVX512_BITALG) |
-               F(CLDEMOTE) | F(MOVDIRI) | F(MOVDIR64B);
+               F(CLDEMOTE) | F(MOVDIRI) | F(MOVDIR64B) | 0 /*WAITPKG*/;
 
        /* cpuid 7.0.edx*/
        const u32 kvm_cpuid_7_0_edx_x86_features =
@@ -392,6 +398,12 @@ static inline void do_cpuid_7_mask(struct kvm_cpuid_entry2 *entry, int index)
 
                entry->edx &= kvm_cpuid_7_0_edx_x86_features;
                cpuid_mask(&entry->edx, CPUID_7_EDX);
+               if (boot_cpu_has(X86_FEATURE_IBPB) && boot_cpu_has(X86_FEATURE_IBRS))
+                       entry->edx |= F(SPEC_CTRL);
+               if (boot_cpu_has(X86_FEATURE_STIBP))
+                       entry->edx |= F(INTEL_STIBP);
+               if (boot_cpu_has(X86_FEATURE_SSBD))
+                       entry->edx |= F(SPEC_CTRL_SSBD);
                /*
                 * We emulate ARCH_CAPABILITIES in software even
                 * if the host doesn't support it.
@@ -606,16 +618,20 @@ static inline int __do_cpuid_func(struct kvm_cpuid_entry2 *entry, u32 function,
         */
        case 0x1f:
        case 0xb: {
-               int i, level_type;
+               int i;
 
-               /* read more entries until level_type is zero */
-               for (i = 1; ; ++i) {
+               /*
+                * We filled in entry[0] for CPUID(EAX=<function>,
+                * ECX=00H) above.  If its level type (ECX[15:8]) is
+                * zero, then the leaf is unimplemented, and we're
+                * done.  Otherwise, continue to populate entries
+                * until the level type (ECX[15:8]) of the previously
+                * added entry is zero.
+                */
+               for (i = 1; entry[i - 1].ecx & 0xff00; ++i) {
                        if (*nent >= maxnent)
                                goto out;
 
-                       level_type = entry[i - 1].ecx & 0xff00;
-                       if (!level_type)
-                               break;
                        do_host_cpuid(&entry[i], function, i);
                        ++*nent;
                }
@@ -729,18 +745,23 @@ static inline int __do_cpuid_func(struct kvm_cpuid_entry2 *entry, u32 function,
                        g_phys_as = phys_as;
                entry->eax = g_phys_as | (virt_as << 8);
                entry->edx = 0;
+               entry->ebx &= kvm_cpuid_8000_0008_ebx_x86_features;
+               cpuid_mask(&entry->ebx, CPUID_8000_0008_EBX);
                /*
-                * IBRS, IBPB and VIRT_SSBD aren't necessarily present in
-                * hardware cpuid
+                * AMD has separate bits for each SPEC_CTRL bit.
+                * arch/x86/kernel/cpu/bugs.c is kind enough to
+                * record that in cpufeatures so use them.
                 */
-               if (boot_cpu_has(X86_FEATURE_AMD_IBPB))
+               if (boot_cpu_has(X86_FEATURE_IBPB))
                        entry->ebx |= F(AMD_IBPB);
-               if (boot_cpu_has(X86_FEATURE_AMD_IBRS))
+               if (boot_cpu_has(X86_FEATURE_IBRS))
                        entry->ebx |= F(AMD_IBRS);
-               if (boot_cpu_has(X86_FEATURE_VIRT_SSBD))
-                       entry->ebx |= F(VIRT_SSBD);
-               entry->ebx &= kvm_cpuid_8000_0008_ebx_x86_features;
-               cpuid_mask(&entry->ebx, CPUID_8000_0008_EBX);
+               if (boot_cpu_has(X86_FEATURE_STIBP))
+                       entry->ebx |= F(AMD_STIBP);
+               if (boot_cpu_has(X86_FEATURE_SSBD))
+                       entry->ebx |= F(AMD_SSBD);
+               if (!boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS))
+                       entry->ebx |= F(AMD_SSB_NO);
                /*
                 * The preference is to use SPEC CTRL MSR instead of the
                 * VIRT_SPEC MSR.
@@ -952,53 +973,64 @@ struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
 EXPORT_SYMBOL_GPL(kvm_find_cpuid_entry);
 
 /*
- * If no match is found, check whether we exceed the vCPU's limit
- * and return the content of the highest valid _standard_ leaf instead.
- * This is to satisfy the CPUID specification.
+ * If the basic or extended CPUID leaf requested is higher than the
+ * maximum supported basic or extended leaf, respectively, then it is
+ * out of range.
  */
-static struct kvm_cpuid_entry2* check_cpuid_limit(struct kvm_vcpu *vcpu,
-                                                  u32 function, u32 index)
+static bool cpuid_function_in_range(struct kvm_vcpu *vcpu, u32 function)
 {
-       struct kvm_cpuid_entry2 *maxlevel;
-
-       maxlevel = kvm_find_cpuid_entry(vcpu, function & 0x80000000, 0);
-       if (!maxlevel || maxlevel->eax >= function)
-               return NULL;
-       if (function & 0x80000000) {
-               maxlevel = kvm_find_cpuid_entry(vcpu, 0, 0);
-               if (!maxlevel)
-                       return NULL;
-       }
-       return kvm_find_cpuid_entry(vcpu, maxlevel->eax, index);
+       struct kvm_cpuid_entry2 *max;
+
+       max = kvm_find_cpuid_entry(vcpu, function & 0x80000000, 0);
+       return max && function <= max->eax;
 }
 
 bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,
               u32 *ecx, u32 *edx, bool check_limit)
 {
        u32 function = *eax, index = *ecx;
-       struct kvm_cpuid_entry2 *best;
-       bool entry_found = true;
-
-       best = kvm_find_cpuid_entry(vcpu, function, index);
+       struct kvm_cpuid_entry2 *entry;
+       struct kvm_cpuid_entry2 *max;
+       bool found;
 
-       if (!best) {
-               entry_found = false;
-               if (!check_limit)
-                       goto out;
-
-               best = check_cpuid_limit(vcpu, function, index);
+       entry = kvm_find_cpuid_entry(vcpu, function, index);
+       found = entry;
+       /*
+        * Intel CPUID semantics treats any query for an out-of-range
+        * leaf as if the highest basic leaf (i.e. CPUID.0H:EAX) were
+        * requested.
+        */
+       if (!entry && check_limit && !cpuid_function_in_range(vcpu, function)) {
+               max = kvm_find_cpuid_entry(vcpu, 0, 0);
+               if (max) {
+                       function = max->eax;
+                       entry = kvm_find_cpuid_entry(vcpu, function, index);
+               }
        }
-
-out:
-       if (best) {
-               *eax = best->eax;
-               *ebx = best->ebx;
-               *ecx = best->ecx;
-               *edx = best->edx;
-       } else
+       if (entry) {
+               *eax = entry->eax;
+               *ebx = entry->ebx;
+               *ecx = entry->ecx;
+               *edx = entry->edx;
+       } else {
                *eax = *ebx = *ecx = *edx = 0;
-       trace_kvm_cpuid(function, *eax, *ebx, *ecx, *edx, entry_found);
-       return entry_found;
+               /*
+                * When leaf 0BH or 1FH is defined, CL is pass-through
+                * and EDX is always the x2APIC ID, even for undefined
+                * subleaves. Index 1 will exist iff the leaf is
+                * implemented, so we pass through CL iff leaf 1
+                * exists. EDX can be copied from any existing index.
+                */
+               if (function == 0xb || function == 0x1f) {
+                       entry = kvm_find_cpuid_entry(vcpu, function, 1);
+                       if (entry) {
+                               *ecx = index & 0xff;
+                               *edx = entry->edx;
+                       }
+               }
+       }
+       trace_kvm_cpuid(function, *eax, *ebx, *ecx, *edx, found);
+       return found;
 }
 EXPORT_SYMBOL_GPL(kvm_cpuid);