Re: [PATCH v2 12/15] KVM: x86/vmx: Disable Arch LBREn bit in #DB and warm reset

From: Yang, Weijiang
Date: Mon Jan 30 2023 - 08:10:14 EST



On 1/28/2023 6:09 AM, Sean Christopherson wrote:
On Thu, Nov 24, 2022, Yang Weijiang wrote:
Per SDM 3B, Chapter 18:
“On a debug breakpoint event (#DB), IA32_LBR_CTL.LBREn is cleared.”
and "On a warm reset, all LBR MSRs, including IA32_LBR_DEPTH, have their
values preserved. However, IA32_LBR_CTL.LBREn is cleared to 0, disabling
LBRs.", clear the bit manually before inject #DB or when vCPU is in warm
reset.

Signed-off-by: Yang Weijiang <weijiang.yang@xxxxxxxxx>
---
arch/x86/kvm/vmx/vmx.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 3bc892e8cf7a..6ad765ea4059 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1695,6 +1695,20 @@ static void vmx_clear_hlt(struct kvm_vcpu *vcpu)
vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE);
}
+static void disable_arch_lbr_ctl(struct kvm_vcpu *vcpu)
+{
+ struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+ if (kvm_cpu_cap_has(X86_FEATURE_ARCH_LBR) &&
+ test_bit(INTEL_PMC_IDX_FIXED_VLBR, pmu->pmc_in_use) &&
+ lbr_desc->event) {
I don't see any reason to check that an event is actually assigned. The behavior
is architectural, whether or not KVM is actively exposing LBRs to the guest is
irrelevant

Agree, will remove them.


+ u64 ctl = vmcs_read64(GUEST_IA32_LBR_CTL);
+
+ vmcs_write64(GUEST_IA32_LBR_CTL, ctl & ~ARCH_LBR_CTL_LBREN);
+ }
+}
+
static void vmx_inject_exception(struct kvm_vcpu *vcpu)
{
struct kvm_queued_exception *ex = &vcpu->arch.exception;
@@ -1738,6 +1752,9 @@ static void vmx_inject_exception(struct kvm_vcpu *vcpu)
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
vmx_clear_hlt(vcpu);
+
+ if (ex->vector == DB_VECTOR)
+ disable_arch_lbr_ctl(vcpu);
}
static void vmx_setup_uret_msr(struct vcpu_vmx *vmx, unsigned int msr,
@@ -4796,7 +4813,9 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
vmx_update_fb_clear_dis(vcpu, vmx);
- if (!init_event && cpu_has_vmx_arch_lbr())
+ if (init_event)
INIT and warm RESET are not the same thing, i.e. this is flat out wrong.

I was confused a bit. Is there's a way to intercept guest warm RESET so as to disable the bit?

Or what should be followed per spec.?


+ disable_arch_lbr_ctl(vcpu);
+ else if (cpu_has_vmx_arch_lbr())
vmcs_write64(GUEST_IA32_LBR_CTL, 0);
}
@@ -4873,6 +4892,9 @@ static void vmx_inject_nmi(struct kvm_vcpu *vcpu)
INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR);
vmx_clear_hlt(vcpu);
+
+ if (vcpu->arch.exception.vector == DB_VECTOR)
Huh? This is _very_ obviously injecting NMIs, not #DBs.

My fault, will remove it.


+ disable_arch_lbr_ctl(vcpu);
}
bool vmx_get_nmi_mask(struct kvm_vcpu *vcpu)
--
2.27.0