Commit 43561123 authored by Jim Mattson's avatar Jim Mattson Committed by Paolo Bonzini

kvm: x86: Improve emulation of CPUID leaves 0BH and 1FH

For these CPUID leaves, the EDX output is not dependent on the ECX
input (i.e. the SIGNIFCANT_INDEX flag doesn't apply to
EDX). Furthermore, the low byte of the ECX output is always identical
to the low byte of the ECX input. KVM does not produce the correct ECX
and EDX outputs for any undefined subleaves beyond the first.

Special-case these CPUID leaves in kvm_cpuid, so that the ECX and EDX
outputs are properly generated for all undefined subleaves.

Fixes: 07716717 ("KVM: Enhance guest cpuid management")
Fixes: a87f2d3a ("KVM: x86: Add Intel CPUID.1F cpuid emulation support")
Signed-off-by: default avatarJim Mattson <jmattson@google.com>
Reviewed-by: default avatarMarc Orr <marcorr@google.com>
Reviewed-by: default avatarPeter Shier <pshier@google.com>
Reviewed-by: default avatarJacob Xu <jacobhxu@google.com>
Cc: Sean Christopherson <sean.j.christopherson@intel.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 3ca94192
...@@ -973,53 +973,64 @@ struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu, ...@@ -973,53 +973,64 @@ struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
EXPORT_SYMBOL_GPL(kvm_find_cpuid_entry); EXPORT_SYMBOL_GPL(kvm_find_cpuid_entry);
/* /*
* If no match is found, check whether we exceed the vCPU's limit * If the basic or extended CPUID leaf requested is higher than the
* and return the content of the highest valid _standard_ leaf instead. * maximum supported basic or extended leaf, respectively, then it is
* This is to satisfy the CPUID specification. * out of range.
*/ */
static struct kvm_cpuid_entry2* check_cpuid_limit(struct kvm_vcpu *vcpu, static bool cpuid_function_in_range(struct kvm_vcpu *vcpu, u32 function)
u32 function, u32 index)
{ {
struct kvm_cpuid_entry2 *maxlevel; struct kvm_cpuid_entry2 *max;
maxlevel = kvm_find_cpuid_entry(vcpu, function & 0x80000000, 0); max = kvm_find_cpuid_entry(vcpu, function & 0x80000000, 0);
if (!maxlevel || maxlevel->eax >= function) return max && function <= max->eax;
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);
} }
bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,
u32 *ecx, u32 *edx, bool check_limit) u32 *ecx, u32 *edx, bool check_limit)
{ {
u32 function = *eax, index = *ecx; u32 function = *eax, index = *ecx;
struct kvm_cpuid_entry2 *best; struct kvm_cpuid_entry2 *entry;
bool entry_found = true; struct kvm_cpuid_entry2 *max;
bool found;
best = kvm_find_cpuid_entry(vcpu, function, index);
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);
}
} }
if (entry) {
out: *eax = entry->eax;
if (best) { *ebx = entry->ebx;
*eax = best->eax; *ecx = entry->ecx;
*ebx = best->ebx; *edx = entry->edx;
*ecx = best->ecx; } else {
*edx = best->edx;
} else
*eax = *ebx = *ecx = *edx = 0; *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); EXPORT_SYMBOL_GPL(kvm_cpuid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment