Skip to content

Commit a7a308f

Browse files
committed
KVM: x86: Initialize guest cpu_caps based on guest CPUID
Initialize a vCPU's capabilities based on the guest CPUID provided by userspace instead of simply zeroing the entire array. This is the first step toward using cpu_caps to query *all* CPUID-based guest capabilities, i.e. will allow converting all usage of guest_cpuid_has() to guest_cpu_cap_has(). Zeroing the array was the logical choice when using cpu_caps was opt-in, e.g. "unsupported" was generally a safer default, and the whole point of governed features is that KVM would need to check host and guest support, i.e. making everything unsupported by default didn't require more code. But requiring KVM to manually "enable" every CPUID-based feature in cpu_caps would require an absurd amount of boilerplate code. Follow existing CPUID/kvm_cpu_caps nomenclature where possible, e.g. for the change() and clear() APIs. Replace check_and_set() with constrain() to try and capture that KVM is constraining userspace's desired guest feature set based on KVM's capabilities. This is intended to be gigantic nop, i.e. should not have any impact on guest or KVM functionality. This is also an intermediate step; a future commit will also incorporate KVM support into the vCPU's cpu_caps before converting guest_cpuid_has() to guest_cpu_cap_has(). Reviewed-by: Maxim Levitsky <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]>
1 parent 7ea3457 commit a7a308f

File tree

4 files changed

+85
-21
lines changed

4 files changed

+85
-21
lines changed

arch/x86/kvm/cpuid.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,13 +354,56 @@ static bool guest_cpuid_is_amd_or_hygon(struct kvm_vcpu *vcpu)
354354
is_guest_vendor_hygon(entry->ebx, entry->ecx, entry->edx);
355355
}
356356

357+
/*
358+
* This isn't truly "unsafe", but except for the cpu_caps initialization code,
359+
* all register lookups should use __cpuid_entry_get_reg(), which provides
360+
* compile-time validation of the input.
361+
*/
362+
static u32 cpuid_get_reg_unsafe(struct kvm_cpuid_entry2 *entry, u32 reg)
363+
{
364+
switch (reg) {
365+
case CPUID_EAX:
366+
return entry->eax;
367+
case CPUID_EBX:
368+
return entry->ebx;
369+
case CPUID_ECX:
370+
return entry->ecx;
371+
case CPUID_EDX:
372+
return entry->edx;
373+
default:
374+
WARN_ON_ONCE(1);
375+
return 0;
376+
}
377+
}
378+
357379
void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
358380
{
359381
struct kvm_lapic *apic = vcpu->arch.apic;
360382
struct kvm_cpuid_entry2 *best;
383+
struct kvm_cpuid_entry2 *entry;
361384
bool allow_gbpages;
385+
int i;
362386

363387
memset(vcpu->arch.cpu_caps, 0, sizeof(vcpu->arch.cpu_caps));
388+
BUILD_BUG_ON(ARRAY_SIZE(reverse_cpuid) != NR_KVM_CPU_CAPS);
389+
390+
/*
391+
* Reset guest capabilities to userspace's guest CPUID definition, i.e.
392+
* honor userspace's definition for features that don't require KVM or
393+
* hardware management/support (or that KVM simply doesn't care about).
394+
*/
395+
for (i = 0; i < NR_KVM_CPU_CAPS; i++) {
396+
const struct cpuid_reg cpuid = reverse_cpuid[i];
397+
398+
if (!cpuid.function)
399+
continue;
400+
401+
entry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index);
402+
if (!entry)
403+
continue;
404+
405+
vcpu->arch.cpu_caps[i] = cpuid_get_reg_unsafe(entry, cpuid.reg);
406+
}
364407

365408
kvm_update_cpuid_runtime(vcpu);
366409

@@ -377,8 +420,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
377420
*/
378421
allow_gbpages = tdp_enabled ? boot_cpu_has(X86_FEATURE_GBPAGES) :
379422
guest_cpuid_has(vcpu, X86_FEATURE_GBPAGES);
380-
if (allow_gbpages)
381-
guest_cpu_cap_set(vcpu, X86_FEATURE_GBPAGES);
423+
guest_cpu_cap_change(vcpu, X86_FEATURE_GBPAGES, allow_gbpages);
382424

383425
best = kvm_find_cpuid_entry(vcpu, 1);
384426
if (best && apic) {

arch/x86/kvm/cpuid.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,29 @@ static __always_inline void guest_cpu_cap_set(struct kvm_vcpu *vcpu,
246246
vcpu->arch.cpu_caps[x86_leaf] |= __feature_bit(x86_feature);
247247
}
248248

249-
static __always_inline void guest_cpu_cap_check_and_set(struct kvm_vcpu *vcpu,
250-
unsigned int x86_feature)
249+
static __always_inline void guest_cpu_cap_clear(struct kvm_vcpu *vcpu,
250+
unsigned int x86_feature)
251251
{
252-
if (kvm_cpu_cap_has(x86_feature) && guest_cpuid_has(vcpu, x86_feature))
252+
unsigned int x86_leaf = __feature_leaf(x86_feature);
253+
254+
vcpu->arch.cpu_caps[x86_leaf] &= ~__feature_bit(x86_feature);
255+
}
256+
257+
static __always_inline void guest_cpu_cap_change(struct kvm_vcpu *vcpu,
258+
unsigned int x86_feature,
259+
bool guest_has_cap)
260+
{
261+
if (guest_has_cap)
253262
guest_cpu_cap_set(vcpu, x86_feature);
263+
else
264+
guest_cpu_cap_clear(vcpu, x86_feature);
265+
}
266+
267+
static __always_inline void guest_cpu_cap_constrain(struct kvm_vcpu *vcpu,
268+
unsigned int x86_feature)
269+
{
270+
if (!kvm_cpu_cap_has(x86_feature))
271+
guest_cpu_cap_clear(vcpu, x86_feature);
254272
}
255273

256274
static __always_inline bool guest_cpu_cap_has(struct kvm_vcpu *vcpu,

arch/x86/kvm/svm/svm.c

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,27 +4401,29 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
44014401
* XSS on VM-Enter/VM-Exit. Failure to do so would effectively give
44024402
* the guest read/write access to the host's XSS.
44034403
*/
4404-
if (boot_cpu_has(X86_FEATURE_XSAVE) &&
4405-
boot_cpu_has(X86_FEATURE_XSAVES) &&
4406-
guest_cpuid_has(vcpu, X86_FEATURE_XSAVE))
4407-
guest_cpu_cap_set(vcpu, X86_FEATURE_XSAVES);
4404+
guest_cpu_cap_change(vcpu, X86_FEATURE_XSAVES,
4405+
boot_cpu_has(X86_FEATURE_XSAVE) &&
4406+
boot_cpu_has(X86_FEATURE_XSAVES) &&
4407+
guest_cpuid_has(vcpu, X86_FEATURE_XSAVE));
44084408

4409-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_NRIPS);
4410-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_TSCRATEMSR);
4411-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_LBRV);
4409+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_NRIPS);
4410+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_TSCRATEMSR);
4411+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_LBRV);
44124412

44134413
/*
44144414
* Intercept VMLOAD if the vCPU model is Intel in order to emulate that
44154415
* VMLOAD drops bits 63:32 of SYSENTER (ignoring the fact that exposing
44164416
* SVM on Intel is bonkers and extremely unlikely to work).
44174417
*/
4418-
if (!guest_cpuid_is_intel_compatible(vcpu))
4419-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
4418+
if (guest_cpuid_is_intel_compatible(vcpu))
4419+
guest_cpu_cap_clear(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
4420+
else
4421+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
44204422

4421-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_PAUSEFILTER);
4422-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_PFTHRESHOLD);
4423-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_VGIF);
4424-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_VNMI);
4423+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_PAUSEFILTER);
4424+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_PFTHRESHOLD);
4425+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_VGIF);
4426+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_VNMI);
44254427

44264428
svm_recalc_instruction_intercepts(vcpu, svm);
44274429

arch/x86/kvm/vmx/vmx.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7830,10 +7830,12 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
78307830
*/
78317831
if (boot_cpu_has(X86_FEATURE_XSAVE) &&
78327832
guest_cpuid_has(vcpu, X86_FEATURE_XSAVE))
7833-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_XSAVES);
7833+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_XSAVES);
7834+
else
7835+
guest_cpu_cap_clear(vcpu, X86_FEATURE_XSAVES);
78347836

7835-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_VMX);
7836-
guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_LAM);
7837+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_VMX);
7838+
guest_cpu_cap_constrain(vcpu, X86_FEATURE_LAM);
78377839

78387840
vmx_setup_uret_msrs(vmx);
78397841

0 commit comments

Comments
 (0)