KVM: x86: Virtualize AMD CPUID faulting

On AMD CPUs, CPUID faulting support is advertised via
CPUID.80000021H:EAX.CpuidUserDis[bit 17] and enabled by setting
HWCR.CpuidUserDis[bit 35].

Advertise the feature to userspace regardless of host CPU support. Allow
writes to HWCR to set bit 35 when the guest CPUID advertises
CpuidUserDis. Update cpuid_fault_enabled() to check HWCR.CpuidUserDis as
well as MSR_FEATURE_ENABLES.CPUID_GP_ON_CPL_GT_0.

Unlike VMX, SVM prioritizes the CPUID intercept over the #GP induced by
CPUID faulting.[1] This behavior has been confirmed on a Turin CPU (F/M/S
1AH/2/1).

Link: https://lore.kernel.org/r/DS7PR12MB82011943131DF5415365E19E940B2@DS7PR12MB8201.namprd12.prod.outlook.com [1]
Signed-off-by: Jim Mattson <jmattson@google.com>
Link: https://patch.msgid.link/20260527174347.2356165-5-jmattson@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
Jim Mattson 2026-05-27 10:43:46 -07:00 committed by Sean Christopherson
parent d1bc99885a
commit e93a93f114
4 changed files with 17 additions and 9 deletions

View File

@ -898,6 +898,7 @@
#define MSR_K7_HWCR_IRPERF_EN_BIT 30 #define MSR_K7_HWCR_IRPERF_EN_BIT 30
#define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT) #define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT)
#define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35 #define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35
#define MSR_K7_HWCR_CPUID_USER_DIS BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT)
#define MSR_K7_FID_VID_CTL 0xc0010041 #define MSR_K7_FID_VID_CTL 0xc0010041
#define MSR_K7_FID_VID_STATUS 0xc0010042 #define MSR_K7_FID_VID_STATUS 0xc0010042
#define MSR_K7_HWCR_CPB_DIS_BIT 25 #define MSR_K7_HWCR_CPB_DIS_BIT 25

View File

@ -1248,7 +1248,7 @@ void kvm_initialize_cpu_caps(void)
F(AUTOIBRS), F(AUTOIBRS),
EMULATED_F(NO_SMM_CTL_MSR), EMULATED_F(NO_SMM_CTL_MSR),
/* PrefetchCtlMsr */ /* PrefetchCtlMsr */
/* GpOnUserCpuid */ EMULATED_F(GP_ON_USER_CPUID),
/* EPSF */ /* EPSF */
F(PREFETCHI), F(PREFETCHI),
F(AVX512_BMM), F(AVX512_BMM),

View File

@ -185,8 +185,9 @@ static inline int guest_cpuid_stepping(struct kvm_vcpu *vcpu)
static inline bool cpuid_fault_enabled(struct kvm_vcpu *vcpu) static inline bool cpuid_fault_enabled(struct kvm_vcpu *vcpu)
{ {
return vcpu->arch.msr_misc_features_enables & return (vcpu->arch.msr_misc_features_enables &
MSR_MISC_FEATURES_ENABLES_CPUID_FAULT; MSR_MISC_FEATURES_ENABLES_CPUID_FAULT) ||
(vcpu->arch.msr_hwcr & MSR_K7_HWCR_CPUID_USER_DIS);
} }
static inline bool kvm_is_cpuid_allowed(struct kvm_vcpu *vcpu) static inline bool kvm_is_cpuid_allowed(struct kvm_vcpu *vcpu)

View File

@ -4002,22 +4002,28 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
break; break;
case MSR_EFER: case MSR_EFER:
return set_efer(vcpu, msr_info); return set_efer(vcpu, msr_info);
case MSR_K7_HWCR: case MSR_K7_HWCR: {
data &= ~(u64)0x40; /* ignore flush filter disable */
data &= ~(u64)0x100; /* ignore ignne emulation enable */
data &= ~(u64)0x8; /* ignore TLB cache disable */
/* /*
* Allow McStatusWrEn and TscFreqSel. (Linux guests from v3.2 * Allow McStatusWrEn and TscFreqSel. (Linux guests from v3.2
* through at least v6.6 whine if TscFreqSel is clear, * through at least v6.6 whine if TscFreqSel is clear,
* depending on F/M/S. * depending on F/M/S.
*/ */
if (data & ~(BIT_ULL(18) | BIT_ULL(24))) { u64 valid = BIT_ULL(18) | BIT_ULL(24);
data &= ~(u64)0x40; /* ignore flush filter disable */
data &= ~(u64)0x100; /* ignore ignne emulation enable */
data &= ~(u64)0x8; /* ignore TLB cache disable */
if (guest_cpu_cap_has(vcpu, X86_FEATURE_GP_ON_USER_CPUID))
valid |= MSR_K7_HWCR_CPUID_USER_DIS;
if (data & ~valid) {
kvm_pr_unimpl_wrmsr(vcpu, msr, data); kvm_pr_unimpl_wrmsr(vcpu, msr, data);
return 1; return 1;
} }
vcpu->arch.msr_hwcr = data; vcpu->arch.msr_hwcr = data;
break; break;
}
case MSR_FAM10H_MMIO_CONF_BASE: case MSR_FAM10H_MMIO_CONF_BASE:
if (data != 0) { if (data != 0) {
kvm_pr_unimpl_wrmsr(vcpu, msr, data); kvm_pr_unimpl_wrmsr(vcpu, msr, data);