Rewrite the enable_lbrv computation in svm_update_lbrv() as a series of 'if' statements, and use nested_vmcb12_has_lbrv() instead of open-coding the nested LBRV check. No functional change intended. Suggested-by: Yosry Ahmed Signed-off-by: Shivansh Dhiman --- Changelog: v3: * New patch. * Refactor the enable_lbrv computation into 'if' statements (Yosry Ahmed). --- arch/x86/kvm/svm/nested.c | 2 +- arch/x86/kvm/svm/svm.c | 10 +++++++--- arch/x86/kvm/svm/svm.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index c1485c3e691c..d3df6b22bfef 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -736,7 +736,7 @@ static int nested_svm_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, return 0; } -static bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu) +bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu) { return guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && (to_svm(vcpu)->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index ef69a51ab27f..cead8f99d856 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -880,9 +880,13 @@ void svm_update_lbrv(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); bool current_enable_lbrv = svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR; - bool enable_lbrv = (svm->vmcb->save.dbgctl & DEBUGCTLMSR_LBR) || - (is_guest_mode(vcpu) && guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && - (svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR)); + bool enable_lbrv = false; + + if (svm->vmcb->save.dbgctl & DEBUGCTLMSR_LBR) + enable_lbrv = true; + + if (is_guest_mode(vcpu) && nested_vmcb12_has_lbrv(vcpu)) + enable_lbrv = true; if (enable_lbrv && !current_enable_lbrv) __svm_enable_lbrv(vcpu); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 716be21fba33..8d77c10cf4f6 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -899,6 +899,7 @@ void nested_copy_vmcb_control_to_cache(struct vcpu_svm *svm, void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm, struct vmcb_save_area *save); void nested_sync_control_from_vmcb02(struct vcpu_svm *svm); +bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu); void svm_switch_vmcb(struct vcpu_svm *svm, struct kvm_vmcb_info *target_vmcb); -- 2.43.0 Clear SVM_MISC2_ENABLE_V_LBR in __nested_copy_vmcb_control_to_cache() when the vCPU does not support LBR Virtualization. This lets the cached value be consumed directly instead of re-checking X86_FEATURE_LBRV on every access. Suggested-by: Yosry Ahmed Signed-off-by: Shivansh Dhiman --- Changelog: v3: * New patch. * Sanitize V_LBR in the cache and drop the redundant X86_FEATURE_LBRV checks (Yosry Ahmed). --- arch/x86/kvm/svm/nested.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index d3df6b22bfef..84248e6665cf 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -534,6 +534,11 @@ void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu, if (!gmet_enabled || !guest_cpu_cap_has(vcpu, X86_FEATURE_GMET)) to->misc_ctl &= ~SVM_MISC_ENABLE_GMET; + /* Always clear misc_ctl2 bits that the guest cannot use */ + to->misc_ctl2 = from->misc_ctl2; + if (!lbrv || !guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV)) + to->misc_ctl2 &= ~SVM_MISC2_ENABLE_V_LBR; + to->iopm_base_pa = from->iopm_base_pa & PAGE_MASK; to->msrpm_base_pa = from->msrpm_base_pa & PAGE_MASK; to->tsc_offset = from->tsc_offset; @@ -551,7 +556,6 @@ void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu, to->event_inj_err = from->event_inj_err; to->next_rip = from->next_rip; to->nested_cr3 = from->nested_cr3; - to->misc_ctl2 = from->misc_ctl2; to->pause_filter_count = from->pause_filter_count; to->pause_filter_thresh = from->pause_filter_thresh; @@ -738,8 +742,7 @@ static int nested_svm_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu) { - return guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && - (to_svm(vcpu)->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR); + return to_svm(vcpu)->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR; } static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) -- 2.43.0 When preparing vmcb02 for nested VMRUN, KVM ORs DR6_ACTIVE_LOW into the guest DR6 to force the fixed bits to 1. DR6_ACTIVE_LOW forces bit 11 (DR6_BUS_LOCK) to 1 unconditionally. Use kvm_dr6_fixed() instead, which forces DR6_RTM and DR6_BUS_LOCK based on the guest's CPUID. DR6_RTM is a reserved bit on AMD and is thus always set to 1. DR6_BUS_LOCK is left writable once the guest supports Bus Lock Detect. Signed-off-by: Shivansh Dhiman --- Changelog: v3: * New patch. * Use kvm_dr6_fixed() instead of open-coding DR6_FIXED_1 | DR6_RTM. --- arch/x86/kvm/regs.c | 3 ++- arch/x86/kvm/regs.h | 1 + arch/x86/kvm/svm/nested.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/regs.c b/arch/x86/kvm/regs.c index d2caf5a67dba..1b5dfa765140 100644 --- a/arch/x86/kvm/regs.c +++ b/arch/x86/kvm/regs.c @@ -764,7 +764,7 @@ void kvm_update_dr7(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_update_dr7); -static u64 kvm_dr6_fixed(struct kvm_vcpu *vcpu) +u64 kvm_dr6_fixed(struct kvm_vcpu *vcpu) { u64 fixed = DR6_FIXED_1; @@ -775,6 +775,7 @@ static u64 kvm_dr6_fixed(struct kvm_vcpu *vcpu) fixed |= DR6_BUS_LOCK; return fixed; } +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_dr6_fixed); int kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val) { diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h index 94fd86728fed..e85c3ff3e120 100644 --- a/arch/x86/kvm/regs.h +++ b/arch/x86/kvm/regs.h @@ -23,6 +23,7 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3); int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4); int kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8); int kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val); +u64 kvm_dr6_fixed(struct kvm_vcpu *vcpu); unsigned long kvm_get_dr(struct kvm_vcpu *vcpu, int dr); unsigned long kvm_get_cr8(struct kvm_vcpu *vcpu); void kvm_lmsw(struct kvm_vcpu *vcpu, unsigned long msw); diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 84248e6665cf..0a9e28a02692 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -811,7 +811,7 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) if (unlikely(new_vmcb12 || vmcb12_is_dirty(control, VMCB_DR))) { vmcb02->save.dr7 = svm->nested.save.dr7 | DR7_FIXED_1; - svm->vcpu.arch.dr6 = svm->nested.save.dr6 | DR6_ACTIVE_LOW; + svm->vcpu.arch.dr6 = svm->nested.save.dr6 | kvm_dr6_fixed(vcpu); vmcb_mark_dirty(vmcb02, VMCB_DR); } -- 2.43.0 Replace the static DEBUGCTL_RESERVED_BITS macro with a helper, svm_get_debugctl_reserved_bits(), that derives the reserved bits from the guest's CPUID. This will allow DEBUGCTLMSR_BUS_LOCK_DETECT only when the guest supports Bus Lock Detect, on both the MSR write and nested load paths. Plumb the vCPU into svm_copy_vmrun_state() so it can pass it to the helper. Co-developed-by: Ravi Bangoria Signed-off-by: Ravi Bangoria Signed-off-by: Shivansh Dhiman --- Changelog: v3: * New patch. * Replaced the static DEBUGCTL_RESERVED_BITS macro with a per-vCPU helper that gates the bit on guest CPUID. --- arch/x86/kvm/svm/nested.c | 8 ++++---- arch/x86/kvm/svm/svm.c | 6 +++--- arch/x86/kvm/svm/svm.h | 12 ++++++++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 0a9e28a02692..4dfccc2deda8 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -821,7 +821,7 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) * svm_set_msr's definition of reserved bits. */ svm_copy_lbrs(&vmcb02->save, save); - vmcb02->save.dbgctl &= ~DEBUGCTL_RESERVED_BITS; + vmcb02->save.dbgctl &= ~svm_get_debugctl_reserved_bits(vcpu); } else { svm_copy_lbrs(&vmcb02->save, &vmcb01->save); } @@ -1204,7 +1204,7 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) } /* Copy state save area fields which are handled by VMRUN */ -void svm_copy_vmrun_state(struct vmcb_save_area *to_save, +void svm_copy_vmrun_state(struct kvm_vcpu *vcpu, struct vmcb_save_area *to_save, struct vmcb_save_area *from_save) { to_save->es = from_save->es; @@ -1231,7 +1231,7 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save, if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) { svm_copy_lbrs(to_save, from_save); - to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS; + to_save->dbgctl &= ~svm_get_debugctl_reserved_bits(vcpu); } } @@ -2072,7 +2072,7 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, svm->nested.vmcb12_gpa = kvm_state->hdr.svm.vmcb_pa; - svm_copy_vmrun_state(&svm->vmcb01.ptr->save, save); + svm_copy_vmrun_state(vcpu, &svm->vmcb01.ptr->save, save); nested_copy_vmcb_control_to_cache(svm, ctl); svm_switch_vmcb(svm, &svm->nested.vmcb02); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index cead8f99d856..a59389c322da 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3164,7 +3164,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) data &= ~DEBUGCTLMSR_BTF; } - if (data & DEBUGCTL_RESERVED_BITS) + if (data & svm_get_debugctl_reserved_bits(vcpu)) return 1; if (svm->vmcb->save.dbgctl == data) @@ -5037,7 +5037,7 @@ static int svm_enter_smm(struct kvm_vcpu *vcpu, union kvm_smram *smram) BUILD_BUG_ON(offsetof(struct vmcb, save) != 0x400); - svm_copy_vmrun_state(map_save.hva + 0x400, + svm_copy_vmrun_state(vcpu, map_save.hva + 0x400, &svm->vmcb01.ptr->save); kvm_vcpu_unmap(vcpu, &map_save); @@ -5081,7 +5081,7 @@ static int svm_leave_smm(struct kvm_vcpu *vcpu, const union kvm_smram *smram) * used during SMM (see svm_enter_smm()) */ - svm_copy_vmrun_state(&svm->vmcb01.ptr->save, map_save.hva + 0x400); + svm_copy_vmrun_state(vcpu, &svm->vmcb01.ptr->save, map_save.hva + 0x400); /* * Enter the nested guest now diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 8d77c10cf4f6..402a827f622d 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -783,7 +783,15 @@ BUILD_SVM_MSR_BITMAP_HELPERS(bool, test, test) BUILD_SVM_MSR_BITMAP_HELPERS(void, clear, __clear) BUILD_SVM_MSR_BITMAP_HELPERS(void, set, __set) -#define DEBUGCTL_RESERVED_BITS (~DEBUGCTLMSR_LBR) +static inline u64 svm_get_debugctl_reserved_bits(struct kvm_vcpu *vcpu) +{ + u64 debugctl = DEBUGCTLMSR_LBR; + + if (guest_cpu_cap_has(vcpu, X86_FEATURE_BUS_LOCK_DETECT)) + debugctl |= DEBUGCTLMSR_BUS_LOCK_DETECT; + + return ~debugctl; +} /* svm.c */ extern bool dump_invalid_vmcb; @@ -873,7 +881,7 @@ void svm_leave_nested(struct kvm_vcpu *vcpu); void svm_free_nested(struct vcpu_svm *svm); int svm_allocate_nested(struct vcpu_svm *svm); int nested_svm_vmrun(struct kvm_vcpu *vcpu); -void svm_copy_vmrun_state(struct vmcb_save_area *to_save, +void svm_copy_vmrun_state(struct kvm_vcpu *vcpu, struct vmcb_save_area *to_save, struct vmcb_save_area *from_save); void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb); void nested_svm_vmexit(struct vcpu_svm *svm); -- 2.43.0 Add Bus Lock Detect support in AMD SVM. Bus Lock Detect is enabled through MSR_IA32_DEBUGCTLMSR and MSR_IA32_DEBUGCTLMSR is virtualized only if LBR Virtualization is enabled. Add this dependency in the SVM. Signed-off-by: Ravi Bangoria Reviewed-by: Tom Lendacky Co-developed-by: Shivansh Dhiman Signed-off-by: Shivansh Dhiman --- Changelog: v2 -> v3: * Split refactor and prep changes out into patches 1-4; this patch now only wires up the LBRV dependency and exposes the capability. v2 Resend: * Rebased on top of tag: kvm-x86-next-2026.06.24. v1 -> v2: * Rebased and used guest_cpu_cap_has() instead of guest_cpuid_has(). v2 Resend: https://lore.kernel.org/kvm/20260629081018.60618-1-shivansh.dhiman@amd.com/ v2: https://lore.kernel.org/kvm/20251121081228.426974-1-shivansh.dhiman@amd.com/ v1: https://lore.kernel.org/all/20240808062937.1149-5-ravi.bangoria@amd.com --- arch/x86/kvm/svm/svm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index a59389c322da..bd204aa33e47 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -882,7 +882,8 @@ void svm_update_lbrv(struct kvm_vcpu *vcpu) bool current_enable_lbrv = svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR; bool enable_lbrv = false; - if (svm->vmcb->save.dbgctl & DEBUGCTLMSR_LBR) + /* Bus Lock Detect in guest depends on LBR Virtualization */ + if (svm->vmcb->save.dbgctl & (DEBUGCTLMSR_LBR | DEBUGCTLMSR_BUS_LOCK_DETECT)) enable_lbrv = true; if (is_guest_mode(vcpu) && nested_vmcb12_has_lbrv(vcpu)) @@ -5595,9 +5596,17 @@ static __init void svm_set_cpu_caps(void) * Clear capabilities that are automatically configured by common code, * but that require explicit SVM support (that isn't yet implemented). */ - kvm_cpu_cap_clear(X86_FEATURE_BUS_LOCK_DETECT); kvm_cpu_cap_clear(X86_FEATURE_MSR_IMM); + /* + * LBR Virtualization must be enabled to support BusLockTrap inside the + * guest, since BusLockTrap is enabled through MSR_IA32_DEBUGCTLMSR and + * MSR_IA32_DEBUGCTLMSR is virtualized only if LBR Virtualization is + * enabled. + */ + if (!lbrv) + kvm_cpu_cap_clear(X86_FEATURE_BUS_LOCK_DETECT); + kvm_setup_xss_caps(); kvm_finalize_cpu_caps(); } -- 2.43.0