Back the LBRv2 MSRs with their VMCB save-area fields in svm_{get,set}_msr(). This is required when guest accesses to these MSRs are intercepted. This allows the guest to retain the values of these MSRs on VMRUN/#VMEXIT cycle when vLBR is disabled. Signed-off-by: Shivansh Dhiman --- Changes in v2: * Use KVM_BUG_ON() in svm_vmcb_lbrv2() for an out-of-range LBR stack index, and then use entry 0. * Do not mark the VMCB dirty on writes to the LBRv2 MSRs since VMCB clean bit for LBR doesn't cover the LBRv2 state. --- arch/x86/include/asm/svm.h | 2 ++ arch/x86/kvm/svm/svm.c | 69 ++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.h | 3 ++ 3 files changed, 74 insertions(+) diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h index d76844224745..fb42c6806655 100644 --- a/arch/x86/include/asm/svm.h +++ b/arch/x86/include/asm/svm.h @@ -257,6 +257,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define SVM_TSC_RATIO_MAX 0x000000ffffffffffULL #define SVM_TSC_RATIO_DEFAULT 0x0100000000ULL +#define SVM_LBR_V2_STACK_SIZE 16 + /* AVIC */ #define AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK (0xFFULL) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 136069dfcd44..428c95d7ef21 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -752,6 +752,12 @@ static void svm_recalc_lbr_msr_intercepts(struct kvm_vcpu *vcpu) svm->lbr_msrs_intercepted = intercept; } +static bool svm_lbrv2_supported(struct kvm_vcpu *vcpu) +{ + return guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2) && + kvm_vcpu_has_mediated_pmu(vcpu); +} + void svm_vcpu_free_msrpm(void *msrpm) { __free_pages(virt_to_page(msrpm), get_order(MSRPM_SIZE)); @@ -2855,6 +2861,19 @@ static u64 *svm_vmcb_lbr(struct vcpu_svm *svm, u32 msr) return &svm->vmcb->save.br_from; } +static u64 *svm_vmcb_lbrv2(struct vcpu_svm *svm, u32 msr) +{ + u32 offset = msr - MSR_AMD_SAMP_BR_FROM; + u32 idx = offset >> 1; + + if (KVM_BUG_ON(idx >= SVM_LBR_V2_STACK_SIZE, svm->vcpu.kvm)) + idx = 0; + + if (offset & 1) + return &svm->vmcb->save.lbr[idx].lbr_stack_to; + return &svm->vmcb->save.lbr[idx].lbr_stack_from; +} + static bool sev_es_prevent_msr_access(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { @@ -2950,6 +2969,21 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_IA32_LASTINTTOIP: msr_info->data = lbrv ? *svm_vmcb_lbr(svm, msr_info->index) : 0; break; + case MSR_AMD_DBG_EXTN_CFG: + if (!svm_lbrv2_supported(vcpu)) + return KVM_MSR_RET_UNSUPPORTED; + msr_info->data = svm->vmcb->save.dbg_extn_cfg; + break; + case MSR_AMD64_LBR_SELECT: + if (!svm_lbrv2_supported(vcpu)) + return KVM_MSR_RET_UNSUPPORTED; + msr_info->data = svm->vmcb->save.lbr_select; + break; + case MSR_AMD_SAMP_BR_FROM ... MSR_AMD_SAMP_BR_FROM + 31: + if (!svm_lbrv2_supported(vcpu)) + return KVM_MSR_RET_UNSUPPORTED; + msr_info->data = *svm_vmcb_lbrv2(svm, msr_info->index); + break; case MSR_VM_HSAVE_PA: msr_info->data = svm->nested.hsave_msr; break; @@ -3249,6 +3283,41 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) *svm_vmcb_lbr(svm, ecx) = data; vmcb_mark_dirty(svm->vmcb, VMCB_LBR); break; + /* + * No VMCB clean bit covers the LBRv2 state, i.e. the CPU never skips + * loading it on VMRUN, so there's no need to mark the VMCB dirty. + */ + case MSR_AMD_DBG_EXTN_CFG: + if (!svm_lbrv2_supported(vcpu)) + return KVM_MSR_RET_UNSUPPORTED; + + if (data & DBG_EXTN_CFG_RESERVED_BITS) + return 1; + + if (svm->vmcb->save.dbg_extn_cfg == data) + break; + + svm->vmcb->save.dbg_extn_cfg = data; + svm_update_lbrv(vcpu); + break; + case MSR_AMD64_LBR_SELECT: + if (!svm_lbrv2_supported(vcpu)) + return KVM_MSR_RET_UNSUPPORTED; + + if (data & LBR_SELECT_RESERVED_BITS) + return 1; + + if (svm->vmcb->save.lbr_select == data) + break; + + svm->vmcb->save.lbr_select = data; + break; + case MSR_AMD_SAMP_BR_FROM ... MSR_AMD_SAMP_BR_FROM + 31: + if (!svm_lbrv2_supported(vcpu)) + return KVM_MSR_RET_UNSUPPORTED; + + *svm_vmcb_lbrv2(svm, ecx) = data; + break; case MSR_VM_HSAVE_PA: /* * Old kernels did not validate the value written to diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 6d0242966dc8..cde6bef61dcd 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -797,6 +797,9 @@ static inline u64 svm_get_supported_debugctl(struct kvm_vcpu *vcpu) return debugctl; } +#define DBG_EXTN_CFG_RESERVED_BITS (~DBG_EXTN_CFG_LBRV2EN) +#define LBR_SELECT_RESERVED_BITS (~GENMASK_ULL(8, 0)) + /* svm.c */ extern bool dump_invalid_vmcb; -- 2.43.0