Skip to content

Commit e1c2e55

Browse files
yosrym93opsiff
authored andcommitted
KVM: nSVM: Always recalculate LBR MSR intercepts in svm_update_lbrv()
commit fbe5e5f upstream. svm_update_lbrv() is called when MSR_IA32_DEBUGCTLMSR is updated, and on nested transitions where LBRV is used. It checks whether LBRV enablement needs to be changed in the current VMCB, and if it does, it also recalculate intercepts to LBR MSRs. However, there are cases where intercepts need to be updated even when LBRV enablement doesn't. Example scenario: - L1 has MSR_IA32_DEBUGCTLMSR cleared. - L1 runs L2 without LBR_CTL_ENABLE (no LBRV). - L2 sets DEBUGCTLMSR_LBR in MSR_IA32_DEBUGCTLMSR, svm_update_lbrv() sets LBR_CTL_ENABLE in VMCB02 and disables intercepts to LBR MSRs. - L2 exits to L1, svm_update_lbrv() is not called on this transition. - L1 clears MSR_IA32_DEBUGCTLMSR, svm_update_lbrv() finds that LBR_CTL_ENABLE is already cleared in VMCB01 and does nothing. - Intercepts remain disabled, L1 reads to LBR MSRs read the host MSRs. Fix it by always recalculating intercepts in svm_update_lbrv(). Fixes: 1d5a1b5 ("KVM: x86: nSVM: correctly virtualize LBR msrs when L2 is running") Cc: stable@vger.kernel.org Signed-off-by: Yosry Ahmed <yosry.ahmed@linux.dev> Link: https://patch.msgid.link/20251108004524.1600006-3-yosry.ahmed@linux.dev Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Yosry Ahmed <yosry.ahmed@linux.dev> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> (cherry picked from commit e6ff197547a911ad6b9970740fb436cf5713e52d) Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
1 parent d8c7456 commit e1c2e55

1 file changed

Lines changed: 19 additions & 10 deletions

File tree

arch/x86/kvm/svm/svm.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,26 +1047,30 @@ static void svm_recalc_lbr_msr_intercepts(struct kvm_vcpu *vcpu)
10471047
!intercept, !intercept);
10481048
}
10491049

1050-
void svm_enable_lbrv(struct kvm_vcpu *vcpu)
1050+
static void __svm_enable_lbrv(struct kvm_vcpu *vcpu)
10511051
{
10521052
struct vcpu_svm *svm = to_svm(vcpu);
10531053

10541054
svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK;
1055-
svm_recalc_lbr_msr_intercepts(vcpu);
10561055

10571056
/* Move the LBR msrs to the vmcb02 so that the guest can see them. */
10581057
if (is_guest_mode(vcpu))
10591058
svm_copy_lbrs(svm->vmcb, svm->vmcb01.ptr);
10601059
}
10611060

1062-
static void svm_disable_lbrv(struct kvm_vcpu *vcpu)
1061+
void svm_enable_lbrv(struct kvm_vcpu *vcpu)
1062+
{
1063+
__svm_enable_lbrv(vcpu);
1064+
svm_recalc_lbr_msr_intercepts(vcpu);
1065+
}
1066+
1067+
static void __svm_disable_lbrv(struct kvm_vcpu *vcpu)
10631068
{
10641069
struct vcpu_svm *svm = to_svm(vcpu);
10651070

10661071
KVM_BUG_ON(sev_es_guest(vcpu->kvm), vcpu->kvm);
10671072

10681073
svm->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK;
1069-
svm_recalc_lbr_msr_intercepts(vcpu);
10701074

10711075
/*
10721076
* Move the LBR msrs back to the vmcb01 to avoid copying them
@@ -1095,13 +1099,18 @@ void svm_update_lbrv(struct kvm_vcpu *vcpu)
10951099
(is_guest_mode(vcpu) && guest_can_use(vcpu, X86_FEATURE_LBRV) &&
10961100
(svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK));
10971101

1098-
if (enable_lbrv == current_enable_lbrv)
1099-
return;
1102+
if (enable_lbrv && !current_enable_lbrv)
1103+
__svm_enable_lbrv(vcpu);
1104+
else if (!enable_lbrv && current_enable_lbrv)
1105+
__svm_disable_lbrv(vcpu);
11001106

1101-
if (enable_lbrv)
1102-
svm_enable_lbrv(vcpu);
1103-
else
1104-
svm_disable_lbrv(vcpu);
1107+
/*
1108+
* During nested transitions, it is possible that the current VMCB has
1109+
* LBR_CTL set, but the previous LBR_CTL had it cleared (or vice versa).
1110+
* In this case, even though LBR_CTL does not need an update, intercepts
1111+
* do, so always recalculate the intercepts here.
1112+
*/
1113+
svm_recalc_lbr_msr_intercepts(vcpu);
11051114
}
11061115

11071116
void disable_nmi_singlestep(struct vcpu_svm *svm)

0 commit comments

Comments
 (0)