Now that LBR v2 is fully virtualized, advertise it and report the LBR stack depth in CPUID. Gate the advertisement on LBR virtualization, the mediated PMU, PerfMonV2 and a 16-entry host stack, so KVM never exposes a configuration it cannot faithfully virtualize, and drop the feature for a vCPU whose CPUID is set with an unsupported stack size. Co-developed-by: Sandipan Das Signed-off-by: Sandipan Das Signed-off-by: Shivansh Dhiman --- arch/x86/kvm/cpuid.c | 6 ++++++ arch/x86/kvm/svm/svm.c | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 2698fa42cd97..d450a5b2499d 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1267,6 +1267,7 @@ void kvm_initialize_cpu_caps(void) kvm_cpu_cap_init(CPUID_8000_0022_EAX, F(PERFMON_V2), + SCATTERED_F(AMD_LBR_V2), ); if (!static_cpu_has_bug(X86_BUG_NULL_SEG)) @@ -1883,6 +1884,11 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) cpuid_entry_override(entry, CPUID_8000_0022_EAX); ebx.split.num_core_pmc = kvm_pmu_cap.num_counters_gp; + + if (kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2)) + ebx.split.lbr_v2_stack_sz = + min(kvm_pmu_cap.num_branches_lbr, SVM_LBR_V2_STACK_SIZE); + entry->ebx = ebx.full; break; } diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 04e386b838d3..4b84498e93be 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4820,6 +4820,20 @@ static bool svm_has_emulated_msr(struct kvm *kvm, u32 index) return true; } +static int cpuid_query_lbrv2_stack_size(struct kvm_vcpu *vcpu) +{ + struct kvm_cpuid_entry2 *entry; + + entry = kvm_find_cpuid_entry(vcpu, 0x80000000); + if (!entry || entry->eax < 0x80000022) + goto not_found; + entry = kvm_find_cpuid_entry(vcpu, 0x80000022); + if (entry) + return (entry->ebx >> 4) & 0x3f; +not_found: + return 0; +} + static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -4846,6 +4860,10 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) if (guest_cpuid_is_intel_compatible(vcpu)) guest_cpu_cap_clear(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD); + if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2) && + cpuid_query_lbrv2_stack_size(vcpu) != SVM_LBR_V2_STACK_SIZE) + guest_cpu_cap_clear(vcpu, X86_FEATURE_AMD_LBR_V2); + if (is_sev_guest(vcpu)) sev_vcpu_after_set_cpuid(svm); } @@ -5692,6 +5710,10 @@ static __init void svm_set_cpu_caps(void) if (kvm_pmu_cap.version != 2 || !kvm_cpu_cap_has(X86_FEATURE_PERFCTR_CORE)) kvm_cpu_cap_clear(X86_FEATURE_PERFMON_V2); + + if (!lbrv || !enable_mediated_pmu || + !kvm_cpu_cap_has(X86_FEATURE_PERFMON_V2)) + kvm_cpu_cap_clear(X86_FEATURE_AMD_LBR_V2); } /* CPUID 0x8000001F (SME/SEV features) */ -- 2.43.0