Skip to content

Commit 8f2a277

Browse files
committed
KVM: x86: Replace (almost) all guest CPUID feature queries with cpu_caps
Switch all queries (except XSAVES) of guest features from guest CPUID to guest capabilities, i.e. replace all calls to guest_cpuid_has() with calls to guest_cpu_cap_has(). Keep guest_cpuid_has() around for XSAVES, but subsume its helper guest_cpuid_get_register() and add a compile-time assertion to prevent using guest_cpuid_has() for any other feature. Add yet another comment for XSAVE to explain why KVM is allowed to query its raw guest CPUID. Opportunistically drop the unused guest_cpuid_clear(), as there should be no circumstance in which KVM needs to _clear_ a guest CPUID feature now that everything is tracked via cpu_caps. E.g. KVM may need to _change_ a feature to emulate dynamic CPUID flags, but KVM should never need to clear a feature in guest CPUID to prevent it from being used by the guest. Delete the last remnants of the governed features framework, as the lone holdout was vmx_adjust_secondary_exec_control()'s divergent behavior for governed vs. ungoverned features. Note, replacing guest_cpuid_has() checks with guest_cpu_cap_has() when computing reserved CR4 bits is a nop when viewed as a whole, as KVM's capabilities are already incorporated into the calculation, i.e. if a feature is present in guest CPUID but unsupported by KVM, its CR4 bit was already being marked as reserved, checking guest_cpu_cap_has() simply double-stamps that it's a reserved bit. Reviewed-by: Maxim Levitsky <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]>
1 parent 820545b commit 8f2a277

File tree

15 files changed

+124
-171
lines changed

15 files changed

+124
-171
lines changed

arch/x86/kvm/cpuid.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
437437
* and can install smaller shadow pages if the host lacks 1GiB support.
438438
*/
439439
allow_gbpages = tdp_enabled ? boot_cpu_has(X86_FEATURE_GBPAGES) :
440-
guest_cpuid_has(vcpu, X86_FEATURE_GBPAGES);
440+
guest_cpu_cap_has(vcpu, X86_FEATURE_GBPAGES);
441441
guest_cpu_cap_change(vcpu, X86_FEATURE_GBPAGES, allow_gbpages);
442442

443443
best = kvm_find_cpuid_entry(vcpu, 1);
@@ -462,7 +462,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
462462

463463
#define __kvm_cpu_cap_has(UNUSED_, f) kvm_cpu_cap_has(f)
464464
vcpu->arch.cr4_guest_rsvd_bits = __cr4_reserved_bits(__kvm_cpu_cap_has, UNUSED_) |
465-
__cr4_reserved_bits(guest_cpuid_has, vcpu);
465+
__cr4_reserved_bits(guest_cpu_cap_has, vcpu);
466466
#undef __kvm_cpu_cap_has
467467

468468
kvm_hv_set_cpuid(vcpu, kvm_cpuid_has_hyperv(vcpu));

arch/x86/kvm/cpuid.h

Lines changed: 27 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -67,41 +67,40 @@ static __always_inline void cpuid_entry_override(struct kvm_cpuid_entry2 *entry,
6767
*reg = kvm_cpu_caps[leaf];
6868
}
6969

70-
static __always_inline u32 *guest_cpuid_get_register(struct kvm_vcpu *vcpu,
71-
unsigned int x86_feature)
70+
static __always_inline bool guest_cpuid_has(struct kvm_vcpu *vcpu,
71+
unsigned int x86_feature)
7272
{
7373
const struct cpuid_reg cpuid = x86_feature_cpuid(x86_feature);
7474
struct kvm_cpuid_entry2 *entry;
75+
u32 *reg;
76+
77+
/*
78+
* XSAVES is a special snowflake. Due to lack of a dedicated intercept
79+
* on SVM, KVM must assume that XSAVES (and thus XRSTORS) is usable by
80+
* the guest if the host supports XSAVES and *XSAVE* is exposed to the
81+
* guest. Because the guest can execute XSAVES and XRSTORS, i.e. can
82+
* indirectly consume XSS, KVM must ensure XSS is zeroed when running
83+
* the guest, i.e. must set XSAVES in vCPU capabilities. But to reject
84+
* direct XSS reads and writes (to minimize the virtualization hole and
85+
* honor userspace's CPUID), KVM needs to check the raw guest CPUID,
86+
* not KVM's view of guest capabilities.
87+
*
88+
* For all other features, guest capabilities are accurate. Expand
89+
* this allowlist with extreme vigilance.
90+
*/
91+
BUILD_BUG_ON(x86_feature != X86_FEATURE_XSAVES);
7592

7693
entry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index);
7794
if (!entry)
7895
return NULL;
7996

80-
return __cpuid_entry_get_reg(entry, cpuid.reg);
81-
}
82-
83-
static __always_inline bool guest_cpuid_has(struct kvm_vcpu *vcpu,
84-
unsigned int x86_feature)
85-
{
86-
u32 *reg;
87-
88-
reg = guest_cpuid_get_register(vcpu, x86_feature);
97+
reg = __cpuid_entry_get_reg(entry, cpuid.reg);
8998
if (!reg)
9099
return false;
91100

92101
return *reg & __feature_bit(x86_feature);
93102
}
94103

95-
static __always_inline void guest_cpuid_clear(struct kvm_vcpu *vcpu,
96-
unsigned int x86_feature)
97-
{
98-
u32 *reg;
99-
100-
reg = guest_cpuid_get_register(vcpu, x86_feature);
101-
if (reg)
102-
*reg &= ~__feature_bit(x86_feature);
103-
}
104-
105104
static inline bool guest_cpuid_is_amd_compatible(struct kvm_vcpu *vcpu)
106105
{
107106
return vcpu->arch.is_amd_compatible;
@@ -202,27 +201,6 @@ static __always_inline bool guest_pv_has(struct kvm_vcpu *vcpu,
202201
return vcpu->arch.pv_cpuid.features & (1u << kvm_feature);
203202
}
204203

205-
enum kvm_governed_features {
206-
#define KVM_GOVERNED_FEATURE(x) KVM_GOVERNED_##x,
207-
#include "governed_features.h"
208-
KVM_NR_GOVERNED_FEATURES
209-
};
210-
211-
static __always_inline int kvm_governed_feature_index(unsigned int x86_feature)
212-
{
213-
switch (x86_feature) {
214-
#define KVM_GOVERNED_FEATURE(x) case x: return KVM_GOVERNED_##x;
215-
#include "governed_features.h"
216-
default:
217-
return -1;
218-
}
219-
}
220-
221-
static __always_inline bool kvm_is_governed_feature(unsigned int x86_feature)
222-
{
223-
return kvm_governed_feature_index(x86_feature) >= 0;
224-
}
225-
226204
static __always_inline void guest_cpu_cap_set(struct kvm_vcpu *vcpu,
227205
unsigned int x86_feature)
228206
{
@@ -267,17 +245,17 @@ static inline bool kvm_vcpu_is_legal_cr3(struct kvm_vcpu *vcpu, unsigned long cr
267245

268246
static inline bool guest_has_spec_ctrl_msr(struct kvm_vcpu *vcpu)
269247
{
270-
return (guest_cpuid_has(vcpu, X86_FEATURE_SPEC_CTRL) ||
271-
guest_cpuid_has(vcpu, X86_FEATURE_AMD_STIBP) ||
272-
guest_cpuid_has(vcpu, X86_FEATURE_AMD_IBRS) ||
273-
guest_cpuid_has(vcpu, X86_FEATURE_AMD_SSBD));
248+
return (guest_cpu_cap_has(vcpu, X86_FEATURE_SPEC_CTRL) ||
249+
guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_STIBP) ||
250+
guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_IBRS) ||
251+
guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_SSBD));
274252
}
275253

276254
static inline bool guest_has_pred_cmd_msr(struct kvm_vcpu *vcpu)
277255
{
278-
return (guest_cpuid_has(vcpu, X86_FEATURE_SPEC_CTRL) ||
279-
guest_cpuid_has(vcpu, X86_FEATURE_AMD_IBPB) ||
280-
guest_cpuid_has(vcpu, X86_FEATURE_SBPB));
256+
return (guest_cpu_cap_has(vcpu, X86_FEATURE_SPEC_CTRL) ||
257+
guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_IBPB) ||
258+
guest_cpu_cap_has(vcpu, X86_FEATURE_SBPB));
281259
}
282260

283261
#endif

arch/x86/kvm/governed_features.h

Lines changed: 0 additions & 22 deletions
This file was deleted.

arch/x86/kvm/hyperv.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1352,7 +1352,7 @@ static void __kvm_hv_xsaves_xsavec_maybe_warn(struct kvm_vcpu *vcpu)
13521352
return;
13531353

13541354
if (guest_cpuid_has(vcpu, X86_FEATURE_XSAVES) ||
1355-
!guest_cpuid_has(vcpu, X86_FEATURE_XSAVEC))
1355+
!guest_cpu_cap_has(vcpu, X86_FEATURE_XSAVEC))
13561356
return;
13571357

13581358
pr_notice_ratelimited("Booting SMP Windows KVM VM with !XSAVES && XSAVEC. "

arch/x86/kvm/lapic.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu)
598598
* version first and level-triggered interrupts never get EOIed in
599599
* IOAPIC.
600600
*/
601-
if (guest_cpuid_has(vcpu, X86_FEATURE_X2APIC) &&
601+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_X2APIC) &&
602602
!ioapic_in_kernel(vcpu->kvm))
603603
v |= APIC_LVR_DIRECTED_EOI;
604604
kvm_lapic_set_reg(apic, APIC_LVR, v);
@@ -2634,7 +2634,7 @@ int kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value, bool host_initiated)
26342634
return 0;
26352635

26362636
u64 reserved_bits = kvm_vcpu_reserved_gpa_bits_raw(vcpu) | 0x2ff |
2637-
(guest_cpuid_has(vcpu, X86_FEATURE_X2APIC) ? 0 : X2APIC_ENABLE);
2637+
(guest_cpu_cap_has(vcpu, X86_FEATURE_X2APIC) ? 0 : X2APIC_ENABLE);
26382638

26392639
if ((value & reserved_bits) != 0 || new_mode == LAPIC_MODE_INVALID)
26402640
return 1;

arch/x86/kvm/smm.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ void enter_smm(struct kvm_vcpu *vcpu)
283283
memset(smram.bytes, 0, sizeof(smram.bytes));
284284

285285
#ifdef CONFIG_X86_64
286-
if (guest_cpuid_has(vcpu, X86_FEATURE_LM))
286+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_LM))
287287
enter_smm_save_state_64(vcpu, &smram.smram64);
288288
else
289289
#endif
@@ -353,7 +353,7 @@ void enter_smm(struct kvm_vcpu *vcpu)
353353
kvm_set_segment(vcpu, &ds, VCPU_SREG_SS);
354354

355355
#ifdef CONFIG_X86_64
356-
if (guest_cpuid_has(vcpu, X86_FEATURE_LM))
356+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_LM))
357357
if (kvm_x86_call(set_efer)(vcpu, 0))
358358
goto error;
359359
#endif
@@ -586,7 +586,7 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
586586
* supports long mode.
587587
*/
588588
#ifdef CONFIG_X86_64
589-
if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) {
589+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_LM)) {
590590
struct kvm_segment cs_desc;
591591
unsigned long cr4;
592592

@@ -609,7 +609,7 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
609609
kvm_set_cr0(vcpu, cr0 & ~(X86_CR0_PG | X86_CR0_PE));
610610

611611
#ifdef CONFIG_X86_64
612-
if (guest_cpuid_has(vcpu, X86_FEATURE_LM)) {
612+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_LM)) {
613613
unsigned long cr4, efer;
614614

615615
/* Clear CR4.PAE before clearing EFER.LME. */
@@ -634,7 +634,7 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
634634
return X86EMUL_UNHANDLEABLE;
635635

636636
#ifdef CONFIG_X86_64
637-
if (guest_cpuid_has(vcpu, X86_FEATURE_LM))
637+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_LM))
638638
ret = rsm_load_state_64(ctxt, &smram.smram64);
639639
else
640640
#endif

arch/x86/kvm/svm/pmu.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr,
4646

4747
switch (msr) {
4848
case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5:
49-
if (!guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE))
49+
if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE))
5050
return NULL;
5151
/*
5252
* Each PMU counter has a pair of CTL and CTR MSRs. CTLn
@@ -109,7 +109,7 @@ static bool amd_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
109109
case MSR_K7_EVNTSEL0 ... MSR_K7_PERFCTR3:
110110
return pmu->version > 0;
111111
case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5:
112-
return guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE);
112+
return guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE);
113113
case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS:
114114
case MSR_AMD64_PERF_CNTR_GLOBAL_CTL:
115115
case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR:
@@ -179,7 +179,7 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
179179
union cpuid_0x80000022_ebx ebx;
180180

181181
pmu->version = 1;
182-
if (guest_cpuid_has(vcpu, X86_FEATURE_PERFMON_V2)) {
182+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_PERFMON_V2)) {
183183
pmu->version = 2;
184184
/*
185185
* Note, PERFMON_V2 is also in 0x80000022.0x0, i.e. the guest
@@ -189,7 +189,7 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
189189
x86_feature_cpuid(X86_FEATURE_PERFMON_V2).index);
190190
ebx.full = kvm_find_cpuid_entry_index(vcpu, 0x80000022, 0)->ebx;
191191
pmu->nr_arch_gp_counters = ebx.split.num_core_pmc;
192-
} else if (guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE)) {
192+
} else if (guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE)) {
193193
pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS_CORE;
194194
} else {
195195
pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS;

arch/x86/kvm/svm/sev.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4435,8 +4435,8 @@ static void sev_es_vcpu_after_set_cpuid(struct vcpu_svm *svm)
44354435
struct kvm_vcpu *vcpu = &svm->vcpu;
44364436

44374437
if (boot_cpu_has(X86_FEATURE_V_TSC_AUX)) {
4438-
bool v_tsc_aux = guest_cpuid_has(vcpu, X86_FEATURE_RDTSCP) ||
4439-
guest_cpuid_has(vcpu, X86_FEATURE_RDPID);
4438+
bool v_tsc_aux = guest_cpu_cap_has(vcpu, X86_FEATURE_RDTSCP) ||
4439+
guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID);
44404440

44414441
set_msr_interception(vcpu, svm->msrpm, MSR_TSC_AUX, v_tsc_aux, v_tsc_aux);
44424442
}

arch/x86/kvm/svm/svm.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,14 +1187,14 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu,
11871187
*/
11881188
if (kvm_cpu_cap_has(X86_FEATURE_INVPCID)) {
11891189
if (!npt_enabled ||
1190-
!guest_cpuid_has(&svm->vcpu, X86_FEATURE_INVPCID))
1190+
!guest_cpu_cap_has(&svm->vcpu, X86_FEATURE_INVPCID))
11911191
svm_set_intercept(svm, INTERCEPT_INVPCID);
11921192
else
11931193
svm_clr_intercept(svm, INTERCEPT_INVPCID);
11941194
}
11951195

11961196
if (kvm_cpu_cap_has(X86_FEATURE_RDTSCP)) {
1197-
if (guest_cpuid_has(vcpu, X86_FEATURE_RDTSCP))
1197+
if (guest_cpu_cap_has(vcpu, X86_FEATURE_RDTSCP))
11981198
svm_clr_intercept(svm, INTERCEPT_RDTSCP);
11991199
else
12001200
svm_set_intercept(svm, INTERCEPT_RDTSCP);
@@ -2940,7 +2940,7 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
29402940
break;
29412941
case MSR_AMD64_VIRT_SPEC_CTRL:
29422942
if (!msr_info->host_initiated &&
2943-
!guest_cpuid_has(vcpu, X86_FEATURE_VIRT_SSBD))
2943+
!guest_cpu_cap_has(vcpu, X86_FEATURE_VIRT_SSBD))
29442944
return 1;
29452945

29462946
msr_info->data = svm->virt_spec_ctrl;
@@ -3091,7 +3091,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
30913091
break;
30923092
case MSR_AMD64_VIRT_SPEC_CTRL:
30933093
if (!msr->host_initiated &&
3094-
!guest_cpuid_has(vcpu, X86_FEATURE_VIRT_SSBD))
3094+
!guest_cpu_cap_has(vcpu, X86_FEATURE_VIRT_SSBD))
30953095
return 1;
30963096

30973097
if (data & ~SPEC_CTRL_SSBD)
@@ -3272,7 +3272,7 @@ static int invpcid_interception(struct kvm_vcpu *vcpu)
32723272
unsigned long type;
32733273
gva_t gva;
32743274

3275-
if (!guest_cpuid_has(vcpu, X86_FEATURE_INVPCID)) {
3275+
if (!guest_cpu_cap_has(vcpu, X86_FEATURE_INVPCID)) {
32763276
kvm_queue_exception(vcpu, UD_VECTOR);
32773277
return 1;
32783278
}
@@ -4404,7 +4404,7 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
44044404
guest_cpu_cap_change(vcpu, X86_FEATURE_XSAVES,
44054405
boot_cpu_has(X86_FEATURE_XSAVE) &&
44064406
boot_cpu_has(X86_FEATURE_XSAVES) &&
4407-
guest_cpuid_has(vcpu, X86_FEATURE_XSAVE));
4407+
guest_cpu_cap_has(vcpu, X86_FEATURE_XSAVE));
44084408

44094409
/*
44104410
* Intercept VMLOAD if the vCPU model is Intel in order to emulate that
@@ -4422,7 +4422,7 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
44224422

44234423
if (boot_cpu_has(X86_FEATURE_FLUSH_L1D))
44244424
set_msr_interception(vcpu, svm->msrpm, MSR_IA32_FLUSH_CMD, 0,
4425-
!!guest_cpuid_has(vcpu, X86_FEATURE_FLUSH_L1D));
4425+
!!guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D));
44264426

44274427
if (sev_guest(vcpu->kvm))
44284428
sev_vcpu_after_set_cpuid(svm);
@@ -4673,7 +4673,7 @@ static int svm_enter_smm(struct kvm_vcpu *vcpu, union kvm_smram *smram)
46734673
* responsible for ensuring nested SVM and SMIs are mutually exclusive.
46744674
*/
46754675

4676-
if (!guest_cpuid_has(vcpu, X86_FEATURE_LM))
4676+
if (!guest_cpu_cap_has(vcpu, X86_FEATURE_LM))
46774677
return 1;
46784678

46794679
smram->smram64.svm_guest_flag = 1;
@@ -4720,14 +4720,14 @@ static int svm_leave_smm(struct kvm_vcpu *vcpu, const union kvm_smram *smram)
47204720

47214721
const struct kvm_smram_state_64 *smram64 = &smram->smram64;
47224722

4723-
if (!guest_cpuid_has(vcpu, X86_FEATURE_LM))
4723+
if (!guest_cpu_cap_has(vcpu, X86_FEATURE_LM))
47244724
return 0;
47254725

47264726
/* Non-zero if SMI arrived while vCPU was in guest mode. */
47274727
if (!smram64->svm_guest_flag)
47284728
return 0;
47294729

4730-
if (!guest_cpuid_has(vcpu, X86_FEATURE_SVM))
4730+
if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SVM))
47314731
return 1;
47324732

47334733
if (!(smram64->efer & EFER_SVME))

arch/x86/kvm/vmx/hyperv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static inline struct hv_enlightened_vmcs *nested_vmx_evmcs(struct vcpu_vmx *vmx)
4242
return vmx->nested.hv_evmcs;
4343
}
4444

45-
static inline bool guest_cpuid_has_evmcs(struct kvm_vcpu *vcpu)
45+
static inline bool guest_cpu_cap_has_evmcs(struct kvm_vcpu *vcpu)
4646
{
4747
/*
4848
* eVMCS is exposed to the guest if Hyper-V is enabled in CPUID and

0 commit comments

Comments
 (0)