Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v3 0/5] KVM: SVM: Add Bus Lock Detect support and refactor LBRV
@ 2026-07-09  8:29 Shivansh Dhiman
  2026-07-09  8:29 ` [PATCH v3 1/5] KVM: SVM: Refactor svm_update_lbrv() Shivansh Dhiman
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Shivansh Dhiman @ 2026-07-09  8:29 UTC (permalink / raw)
  To: seanjc, pbonzini, tglx, mingo
  Cc: kvm, x86, yosry.ahmed, jmattson, thomas.lendacky, nikunj.dadhania,
	ravi.bangoria, santosh.shukla, shivansh.dhiman

Bus Lock Detect (BLD) is a CPU feature that raises a #DB trap when an
instruction acquires a bus lock, allowing software to detect and act on bus
locks. It is enabled via MSR_IA32_DEBUGCTLMSR bit 2.

This series adds BLD support to AMD SVM and exposes the capability to the
guest. On AMD, MSR_IA32_DEBUGCTLMSR is virtualized only when LBR
Virtualization (LBRV) is enabled, so BLD depends on LBRV. Enabling that
dependency cleanly required first refactoring the LBRV handling, so the bulk
of the series is preparatory:

  1: Refactor svm_update_lbrv() so new LBRV dependencies are trivial to add.
  2: Sanitize V_LBR in the nested control cache and drop the now-redundant 
     X86_FEATURE_LBRV checks.
  3: Use kvm_dr6_fixed() for nested DR6 so DR6_BUS_LOCK (bit 11) is forced
     per guest CPUID instead of unconditionally.
  4: Compute DEBUGCTL reserved bits per-vCPU so DEBUGCTLMSR_BUS_LOCK_DETECT
     is only accepted when the guest supports BLD.
  5: Add Bus Lock Detect support, wiring BLD to LBRV and exposing the cap.

Regards,
Shivansh

---
Changelog:
v2 -> v3:
 * Reworked the single v2 patch into a prep series (patches 1-4) plus the
   feature.
 * Rewrite svm_update_lbrv() as 'if' statements so the BLD LBRV
   dependency can be added cleanly (Yosry Ahmed).
 * Sanitize V_LBR in the nested control cache like NP/GMET and drop the
   redundant X86_FEATURE_LBRV checks (Yosry Ahmed).
 * Use kvm_dr6_fixed() for nested DR6 instead of DR6_FIXED_1 | DR6_RTM.
 * Compute DEBUGCTL reserved bits per-vCPU, gating BUS_LOCK_DETECT on
   guest CPUID rather than a static macro.

v2 resend:
 * Rebased on kvm-x86-next-2026.06.24.

v1 -> v2:
 * 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

---
Shivansh Dhiman (5):
  KVM: SVM: Refactor svm_update_lbrv()
  KVM: nSVM: Disable LBRV in nested control cache when unsupported
  KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed()
  KVM: SVM: Compute DEBUGCTL reserved bits dynamically
  KVM: SVM: Add Bus Lock Detect support

 arch/x86/kvm/regs.c       |  3 ++-
 arch/x86/kvm/regs.h       |  1 +
 arch/x86/kvm/svm/nested.c | 21 ++++++++++++---------
 arch/x86/kvm/svm/svm.c    | 27 ++++++++++++++++++++-------
 arch/x86/kvm/svm/svm.h    | 13 +++++++++++--
 5 files changed, 46 insertions(+), 19 deletions(-)


base-commit: 50406d35f5635e1cc523e61409d57e851b5f5df8
-- 
2.43.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 1/5] KVM: SVM: Refactor svm_update_lbrv()
  2026-07-09  8:29 [PATCH v3 0/5] KVM: SVM: Add Bus Lock Detect support and refactor LBRV Shivansh Dhiman
@ 2026-07-09  8:29 ` Shivansh Dhiman
  2026-07-09 19:53   ` Yosry Ahmed
  2026-07-09  8:29 ` [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported Shivansh Dhiman
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Shivansh Dhiman @ 2026-07-09  8:29 UTC (permalink / raw)
  To: seanjc, pbonzini, tglx, mingo
  Cc: kvm, x86, yosry.ahmed, jmattson, thomas.lendacky, nikunj.dadhania,
	ravi.bangoria, santosh.shukla, shivansh.dhiman

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 <yosry@kernel.org>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported
  2026-07-09  8:29 [PATCH v3 0/5] KVM: SVM: Add Bus Lock Detect support and refactor LBRV Shivansh Dhiman
  2026-07-09  8:29 ` [PATCH v3 1/5] KVM: SVM: Refactor svm_update_lbrv() Shivansh Dhiman
@ 2026-07-09  8:29 ` Shivansh Dhiman
  2026-07-09  8:50   ` sashiko-bot
  2026-07-09 19:48   ` Yosry Ahmed
  2026-07-09  8:29 ` [PATCH v3 3/5] KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed() Shivansh Dhiman
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 13+ messages in thread
From: Shivansh Dhiman @ 2026-07-09  8:29 UTC (permalink / raw)
  To: seanjc, pbonzini, tglx, mingo
  Cc: kvm, x86, yosry.ahmed, jmattson, thomas.lendacky, nikunj.dadhania,
	ravi.bangoria, santosh.shukla, shivansh.dhiman

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 <yosry@kernel.org>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 3/5] KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed()
  2026-07-09  8:29 [PATCH v3 0/5] KVM: SVM: Add Bus Lock Detect support and refactor LBRV Shivansh Dhiman
  2026-07-09  8:29 ` [PATCH v3 1/5] KVM: SVM: Refactor svm_update_lbrv() Shivansh Dhiman
  2026-07-09  8:29 ` [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported Shivansh Dhiman
@ 2026-07-09  8:29 ` Shivansh Dhiman
  2026-07-09  8:51   ` sashiko-bot
  2026-07-09  8:29 ` [PATCH v3 4/5] KVM: SVM: Compute DEBUGCTL reserved bits dynamically Shivansh Dhiman
  2026-07-09  8:29 ` [PATCH v3 5/5] KVM: SVM: Add Bus Lock Detect support Shivansh Dhiman
  4 siblings, 1 reply; 13+ messages in thread
From: Shivansh Dhiman @ 2026-07-09  8:29 UTC (permalink / raw)
  To: seanjc, pbonzini, tglx, mingo
  Cc: kvm, x86, yosry.ahmed, jmattson, thomas.lendacky, nikunj.dadhania,
	ravi.bangoria, santosh.shukla, shivansh.dhiman

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 <shivansh.dhiman@amd.com>
---
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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 4/5] KVM: SVM: Compute DEBUGCTL reserved bits dynamically
  2026-07-09  8:29 [PATCH v3 0/5] KVM: SVM: Add Bus Lock Detect support and refactor LBRV Shivansh Dhiman
                   ` (2 preceding siblings ...)
  2026-07-09  8:29 ` [PATCH v3 3/5] KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed() Shivansh Dhiman
@ 2026-07-09  8:29 ` Shivansh Dhiman
  2026-07-09  8:44   ` sashiko-bot
  2026-07-09  8:29 ` [PATCH v3 5/5] KVM: SVM: Add Bus Lock Detect support Shivansh Dhiman
  4 siblings, 1 reply; 13+ messages in thread
From: Shivansh Dhiman @ 2026-07-09  8:29 UTC (permalink / raw)
  To: seanjc, pbonzini, tglx, mingo
  Cc: kvm, x86, yosry.ahmed, jmattson, thomas.lendacky, nikunj.dadhania,
	ravi.bangoria, santosh.shukla, shivansh.dhiman

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 <ravi.bangoria@amd.com>
Signed-off-by: Ravi Bangoria <ravi.bangoria@amd.com>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 5/5] KVM: SVM: Add Bus Lock Detect support
  2026-07-09  8:29 [PATCH v3 0/5] KVM: SVM: Add Bus Lock Detect support and refactor LBRV Shivansh Dhiman
                   ` (3 preceding siblings ...)
  2026-07-09  8:29 ` [PATCH v3 4/5] KVM: SVM: Compute DEBUGCTL reserved bits dynamically Shivansh Dhiman
@ 2026-07-09  8:29 ` Shivansh Dhiman
  2026-07-09  8:55   ` sashiko-bot
  4 siblings, 1 reply; 13+ messages in thread
From: Shivansh Dhiman @ 2026-07-09  8:29 UTC (permalink / raw)
  To: seanjc, pbonzini, tglx, mingo
  Cc: kvm, x86, yosry.ahmed, jmattson, thomas.lendacky, nikunj.dadhania,
	ravi.bangoria, santosh.shukla, shivansh.dhiman

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 <ravi.bangoria@amd.com>
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
Co-developed-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 4/5] KVM: SVM: Compute DEBUGCTL reserved bits dynamically
  2026-07-09  8:29 ` [PATCH v3 4/5] KVM: SVM: Compute DEBUGCTL reserved bits dynamically Shivansh Dhiman
@ 2026-07-09  8:44   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-09  8:44 UTC (permalink / raw)
  To: Shivansh Dhiman; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The dynamic reserved bit check for DEBUGCTLMSR_BUS_LOCK_DETECT is dead code because KVM explicitly clears the feature from SVM's supported CPUID capabilities.
- [High] Hardware silently ignores the guest's Bus Lock Detect configuration on VMRUN unless the guest also enables LBRs, due to AMD SVM requirements for loading DEBUGCTLMSR.
- [High] Bus Lock Detect support is fundamentally broken because DEBUGCTLMSR state management is strictly gated by unrelated LBR Virtualization parameters and host capabilities.
--

--- Patch [4]: [PATCH v3 4/5] KVM: SVM: Compute DEBUGCTL reserved bits dynamically ---
commit 8b16c8234c21a39b440d6a7688ffcc624702cdf8
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>

KVM: SVM: Compute DEBUGCTL reserved bits dynamically

This patch replaces the static DEBUGCTL_RESERVED_BITS macro with a
per-vCPU helper that derives reserved bits from the guest's CPUID. This
allows DEBUGCTLMSR_BUS_LOCK_DETECT when the guest supports it.

> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c

[ ... ]

> @@ -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);

[Severity: High]
Does Bus Lock Detect incorrectly depend on LBR Virtualization being supported
and enabled?

Here, the guest's dbgctl is only copied if the host supports X86_FEATURE_LBRV.

Similarly, in svm_set_msr(), writes to MSR_IA32_DEBUGCTLMSR are unconditionally
rejected if the lbrv module parameter is disabled:

svm_set_msr() {
        ...
        case MSR_IA32_DEBUGCTLMSR:
                if (!lbrv) {
                        kvm_pr_unimpl_wrmsr(vcpu, ecx, data);
                        break;
                }
        ...
}

And earlier in nested_vmcb02_prepare_save(), L2's dbgctl is replaced with
L1's dbgctl if L1 does not explicitly enable LBRV for L2:

nested_vmcb02_prepare_save() {
        ...
        if (nested_vmcb12_has_lbrv(vcpu)) {
                ...
        } else {
                svm_copy_lbrs(&vmcb02->save, &vmcb01->save);
        }
        ...
}

Will this break Bus Lock Detect for guests when LBR features are unavailable
or disabled?

>  	}
>  }

> diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
> --- 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;

[Severity: High]
Is it possible for a guest to actually have this feature under SVM?

In svm_set_cpu_caps(), KVM unconditionally clears the feature:

svm_set_cpu_caps() {
        ...
        kvm_cpu_cap_clear(X86_FEATURE_BUS_LOCK_DETECT);
        ...
}

Because of this, guest_cpu_cap_has() will always evaluate to false,
rendering this new logic dead code. Guest writes to enable the feature
will inject a #GP.

[Severity: High]
If the dead-code issue above is fixed and KVM accepts the write to
DEBUGCTLMSR_BUS_LOCK_DETECT, will the hardware silently ignore the guest's
configuration on VMRUN unless the guest also enables LBRs?

AMD SVM only loads DEBUGCTLMSR from the VMCB save area during VMRUN if LBR
Virtualization is enabled. In svm_update_lbrv(), KVM disables V_LBR if the
LBR bit is not set:

svm_update_lbrv() {
        ...
        if (svm->vmcb->save.dbgctl & DEBUGCTLMSR_LBR)
                enable_lbrv = true;
        ...
        else if (!enable_lbrv && current_enable_lbrv)
                __svm_disable_lbrv(vcpu);
        ...
}

Could this create a state desynchronization where KVM accepts the feature
but the CPU ignores it?

> +
> +	return ~debugctl;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709082953.69434-1-shivansh.dhiman@amd.com?part=4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported
  2026-07-09  8:29 ` [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported Shivansh Dhiman
@ 2026-07-09  8:50   ` sashiko-bot
  2026-07-09 19:42     ` Yosry Ahmed
  2026-07-09 19:48   ` Yosry Ahmed
  1 sibling, 1 reply; 13+ messages in thread
From: sashiko-bot @ 2026-07-09  8:50 UTC (permalink / raw)
  To: Shivansh Dhiman; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The `SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE` bit is never cleared in `vmcb02->control.misc_ctl2` once set, allowing L2 to bypass L1 intercepts or causing host log spam.
--

--- Patch [2]: [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported ---
commit 54bf374923da671433d0174e1a9df561ac634275
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>

KVM: nSVM: Disable LBRV in nested control cache when unsupported

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.

> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index d3df6b22bfefe..84248e6665cfe 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;

While reviewing these updates to misc_ctl2, I noticed a related pre-existing
behavior in nested_vmcb02_prepare_control():

arch/x86/kvm/svm/nested.c:nested_vmcb02_prepare_control() {
...
	if (!nested_vmcb_needs_vls_intercept(svm))
		vmcb02->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE;
...
}

[Severity: High]
This is a pre-existing issue, but does this code leave the
SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE bit set in vmcb02->control.misc_ctl2 once it
has been enabled?

If L1 enables virtual VMLOAD/VMSAVE for L2, KVM sets this bit via bitwise OR.
On a subsequent nested VMRUN where L1 disables the feature or disables nested
paging, KVM correctly tries to set intercepts or clears NP_ENABLE. However,
it appears to leave V_VMLOAD_VMSAVE set to 1 since there is no else branch
to clear it, and the vmcb02 page is reused across runs.

Could this allow a malicious L2 guest to bypass L1's intercepts for VMLOAD and
VMSAVE, or cause an L1 guest to spam the host kernel log with SVM_EXIT_ERR by
toggling NP_ENABLE while V_VMLOAD_VMSAVE remains stuck at 1?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709082953.69434-1-shivansh.dhiman@amd.com?part=2

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 3/5] KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed()
  2026-07-09  8:29 ` [PATCH v3 3/5] KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed() Shivansh Dhiman
@ 2026-07-09  8:51   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-09  8:51 UTC (permalink / raw)
  To: Shivansh Dhiman; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The patch identifies that DR6_ACTIVE_LOW unconditionally sets DR6_BUS_LOCK (bit 11) to 1, and replaces it with kvm_dr6_fixed() in nested VMCB02 preparation. However, the patch fails to replace DR6_ACTIVE_LOW in L1 vCPU initialization and #DB payload processing paths, completely breaking Bus Lock Detect for L1 guests.
--

--- Patch [3]: [PATCH v3 3/5] KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed() ---
commit 0ee3ae36939ed695ccbd636580058ad35113add9
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>

KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed()

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.

> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index 84248e6665cfe..0a9e28a02692e 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);

[Severity: High]
This is a pre-existing issue, but since this patch correctly identifies that
DR6_ACTIVE_LOW unconditionally forces bit 11 (DR6_BUS_LOCK) to 1, should
DR6_ACTIVE_LOW also be replaced in the L1 vCPU initialization and #DB payload
processing paths?

Currently, DR6_ACTIVE_LOW is still used to initialize L1's dr6:

arch/x86/kvm/x86.c:kvm_vcpu_reset() {
    ...
    vcpu->arch.dr6 = DR6_ACTIVE_LOW;
    ...
}

It is also used as a flip mask when KVM translates the exception payload to
guest DR6. Because Bus Lock Detect is actually active-high, XORing it with
DR6_ACTIVE_LOW incorrectly inverts the bus lock bit the guest sees:

arch/x86/kvm/x86.c:kvm_deliver_exception_payload() {
    ...
    vcpu->arch.dr6 ^= ex->payload & DR6_ACTIVE_LOW;
    ...
}

Similarly, SVM constructs the #DB payload by XORing the hardware exit DR6
with DR6_ACTIVE_LOW:

arch/x86/kvm/svm/svm.c:db_interception() {
    ...
    u32 payload = svm->vmcb->save.dr6 ^ DR6_ACTIVE_LOW;
    ...
}

Could this leave Bus Lock Detect functionally broken for L1 guests by causing
them to observe corrupted hardware state (false positives or false negatives
for bus locks)?

>  		vmcb_mark_dirty(vmcb02, VMCB_DR);
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709082953.69434-1-shivansh.dhiman@amd.com?part=3

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 5/5] KVM: SVM: Add Bus Lock Detect support
  2026-07-09  8:29 ` [PATCH v3 5/5] KVM: SVM: Add Bus Lock Detect support Shivansh Dhiman
@ 2026-07-09  8:55   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-09  8:55 UTC (permalink / raw)
  To: Shivansh Dhiman; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [High] Unprivileged host userspace can trigger a KVM_BUG_ON (resulting in a WARN_ON_ONCE and potential host panic) by dynamically toggling MSR_IA32_DEBUGCTLMSR for a SEV-ES guest.
- [High] Enabling Bus Lock Detect inadvertently un-intercepts LBR MSRs, granting the guest unauthorized hardware access to LBRs.

Pre-existing issues:
- [High] L2 guest can bypass MSR intercepts and overwrite L1's MSR_IA32_DEBUGCTLMSR and LBR state when L1 enables Bus Lock Detect or LBR.
- [High] Bus Lock Trap #DB causes are lost during reinjection, rendering the feature non-functional in the guest.
--

--- Patch [5]: [PATCH v3 5/5] KVM: SVM: Add Bus Lock Detect support ---
commit d2f71feda9dbdbb51e59bc110b5518d4f10bcf93
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>

KVM: SVM: Add Bus Lock Detect support

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.

> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index a59389c322da1..bd204aa33e47f 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;

[Severity: High]
Could this change allow unprivileged host userspace to trigger a kernel panic
on systems with panic_on_warn=1?

If userspace calls KVM_SET_MSRS for a SEV-ES vCPU before the guest is
finalized and sets MSR_IA32_DEBUGCTLMSR with DEBUGCTLMSR_LBR or
DEBUGCTLMSR_BUS_LOCK_DETECT, svm_update_lbrv() will set enable_lbrv = true.

If userspace subsequently writes 0 to clear the bit, enable_lbrv becomes
false, causing KVM to call __svm_disable_lbrv().

This would then hit the following assertion:

arch/x86/kvm/svm/svm.c:__svm_disable_lbrv() {
    ...
    KVM_BUG_ON(is_sev_es_guest(vcpu), vcpu->kvm);
    ...
}


[Severity: High]
Does enabling Bus Lock Detect inadvertently grant the guest unauthorized
hardware access to LBRs?

When a guest with Bus Lock Detect sets DEBUGCTLMSR_BUS_LOCK_DETECT,
enable_lbrv becomes true. This leads to a call to
svm_recalc_lbr_msr_intercepts():

arch/x86/kvm/svm/svm.c:svm_recalc_lbr_msr_intercepts() {
    ...
    bool intercept = !(svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR);
    ...
}

Because intercepts are disabled entirely based on SVM_MISC2_ENABLE_V_LBR,
could this un-intercept all LBR MSRs (e.g., MSR_IA32_LASTBRANCHFROMIP) even
if the guest does not possess the LBRV CPUID feature?


[Severity: High]
This is a pre-existing issue, but can an L2 guest bypass MSR intercepts and
overwrite L1's state if L1 enables Bus Lock Detect or LBRs?

If L1 enables these features but does not expose LBRV to L2, KVM populates
vmcb02->save.dbgctl with L1's state. When svm_update_lbrv() sees these bits,
it appears to activate hardware LBRV (SVM_MISC2_ENABLE_V_LBR) for L2.

This would un-intercept MSRs for L2, allowing L2 to modify DEBUGCTLMSR
natively. Upon nested VMEXIT, KVM copies this modified state back to L1:

arch/x86/kvm/svm/nested.c:nested_svm_vmexit() {
    ...
    if (!nested_vmcb12_has_lbrv(vcpu)) {
        svm_copy_lbrs(&vmcb01->save, &vmcb02->save);
        vmcb_mark_dirty(vmcb01, VMCB_LBR);
    }
    ...
}

Would this allow a malicious L2 guest to corrupt L1's state?

[ ... ]

> @@ -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);

[Severity: High]
This isn't a bug introduced by this patch, but are Bus Lock Trap exceptions
actually reaching the guest?

According to the AMD APM, when a #DB is intercepted, the CPU provides the
new DR6 value (containing the DR6_BUS_LOCK bit) in EXITINFO1 and does not
update the guest's DR6.

However, when KVM reinjects the exception, it reads the stale save.dr6:

arch/x86/kvm/svm/svm.c:db_interception() {
    ...
    u32 payload = svm->vmcb->save.dr6 ^ DR6_ACTIVE_LOW;
    kvm_queue_exception_p(vcpu, DB_VECTOR, payload);
    ...
}

Does this cause the DR6_BUS_LOCK bit to be lost during reinjection, rendering
the feature non-functional in the guest?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709082953.69434-1-shivansh.dhiman@amd.com?part=5

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported
  2026-07-09  8:50   ` sashiko-bot
@ 2026-07-09 19:42     ` Yosry Ahmed
  0 siblings, 0 replies; 13+ messages in thread
From: Yosry Ahmed @ 2026-07-09 19:42 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Shivansh Dhiman, kvm

On Thu, Jul 9, 2026 at 1:50 AM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The `SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE` bit is never cleared in `vmcb02->control.misc_ctl2` once set, allowing L2 to bypass L1 intercepts or causing host log spam.
> --
>
> --- Patch [2]: [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported ---
> commit 54bf374923da671433d0174e1a9df561ac634275
> Author: Shivansh Dhiman <shivansh.dhiman@amd.com>
>
> KVM: nSVM: Disable LBRV in nested control cache when unsupported
>
> 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.
>
> > diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> > index d3df6b22bfefe..84248e6665cfe 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;
>
> While reviewing these updates to misc_ctl2, I noticed a related pre-existing
> behavior in nested_vmcb02_prepare_control():
>
> arch/x86/kvm/svm/nested.c:nested_vmcb02_prepare_control() {
> ...
>         if (!nested_vmcb_needs_vls_intercept(svm))
>                 vmcb02->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE;
> ...
> }
>
> [Severity: High]
> This is a pre-existing issue, but does this code leave the
> SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE bit set in vmcb02->control.misc_ctl2 once it
> has been enabled?
>
> If L1 enables virtual VMLOAD/VMSAVE for L2, KVM sets this bit via bitwise OR.
> On a subsequent nested VMRUN where L1 disables the feature or disables nested
> paging, KVM correctly tries to set intercepts or clears NP_ENABLE. However,
> it appears to leave V_VMLOAD_VMSAVE set to 1 since there is no else branch
> to clear it, and the vmcb02 page is reused across runs.

It would probably be better code hygiene to clean it, but I don't
think it will actually cause problems.

>
> Could this allow a malicious L2 guest to bypass L1's intercepts for VMLOAD and
> VMSAVE,

No it shouldn't, because nested_vmcb02_recalc_intercepts() will
correctly set the intercepts() if nested_vmcb_needs_vls_intercept()
returns true, and the intercepts being set makes V_VMLOAD_VMSAVE a
noop (see the comment in svm_recalc_instruction_intercepts()).

> or cause an L1 guest to spam the host kernel log with SVM_EXIT_ERR by
> toggling NP_ENABLE while V_VMLOAD_VMSAVE remains stuck at 1?

No, IIUC having NP_ENABLED=0 also just makes V_VMLOAD_VMSAVE a noop,
and a VMLOAD/VMSAVE will result in a proper VMLOAD/VMSAVE exit, not
SVM_EXIT_ERR, from the APM:

"""
Support for virtualized VMSAVE and VMLOAD is indicated by CPUID
Fn8000_000A_EDX[15]=1. When this feature is available, it must be
explicitly enabled by setting bit 1 of VMCB offset 0B8h to 1. This
enable bit is only recognized when the hypervisor is in 64 bit mode,
nested paging is enabled and Secure Encrypted Virtualization is
disabled, otherwise attempted execution of a VMLOAD or VMSAVE in the
guest will result in a #VMEXIT with a VMSAVE/VMLOAD exit code.
"""

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported
  2026-07-09  8:29 ` [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported Shivansh Dhiman
  2026-07-09  8:50   ` sashiko-bot
@ 2026-07-09 19:48   ` Yosry Ahmed
  1 sibling, 0 replies; 13+ messages in thread
From: Yosry Ahmed @ 2026-07-09 19:48 UTC (permalink / raw)
  To: Shivansh Dhiman
  Cc: seanjc, pbonzini, tglx, mingo, kvm, x86, yosry.ahmed, jmattson,
	thomas.lendacky, nikunj.dadhania, ravi.bangoria, santosh.shukla

On Thu, Jul 9, 2026 at 1:32 AM Shivansh Dhiman <shivansh.dhiman@amd.com> wrote:
>
> 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 <yosry@kernel.org>
> Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
> ---
> 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 */

I think the comment here is unnecessary, the comment above for
misc_ctl should already make this clear.

> +       to->misc_ctl2 = from->misc_ctl2;
> +       if (!lbrv || !guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV))

Do we need to check lbrv here? KVM should not set X86_FEATURE_LBRV if
lbrv=0 anyway, right?

> +               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
>
>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 1/5] KVM: SVM: Refactor svm_update_lbrv()
  2026-07-09  8:29 ` [PATCH v3 1/5] KVM: SVM: Refactor svm_update_lbrv() Shivansh Dhiman
@ 2026-07-09 19:53   ` Yosry Ahmed
  0 siblings, 0 replies; 13+ messages in thread
From: Yosry Ahmed @ 2026-07-09 19:53 UTC (permalink / raw)
  To: Shivansh Dhiman
  Cc: seanjc, pbonzini, tglx, mingo, kvm, x86, yosry.ahmed, jmattson,
	thomas.lendacky, nikunj.dadhania, ravi.bangoria, santosh.shukla

On Thu, Jul 9, 2026 at 1:31 AM Shivansh Dhiman <shivansh.dhiman@amd.com> wrote:
>
> 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 <yosry@kernel.org>
> Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
> ---
> 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)

We might wanna rename this function now that we are exposing it
outside of nested.c, but otherwise this looks good to me:

Reviewed-by: Yosry Ahmed <yosry@kernel.org>

>  {
>         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
>
>

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-07-09 19:54 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09  8:29 [PATCH v3 0/5] KVM: SVM: Add Bus Lock Detect support and refactor LBRV Shivansh Dhiman
2026-07-09  8:29 ` [PATCH v3 1/5] KVM: SVM: Refactor svm_update_lbrv() Shivansh Dhiman
2026-07-09 19:53   ` Yosry Ahmed
2026-07-09  8:29 ` [PATCH v3 2/5] KVM: nSVM: Disable LBRV in nested control cache when unsupported Shivansh Dhiman
2026-07-09  8:50   ` sashiko-bot
2026-07-09 19:42     ` Yosry Ahmed
2026-07-09 19:48   ` Yosry Ahmed
2026-07-09  8:29 ` [PATCH v3 3/5] KVM: nSVM: Sanitize nested DR6 using kvm_dr6_fixed() Shivansh Dhiman
2026-07-09  8:51   ` sashiko-bot
2026-07-09  8:29 ` [PATCH v3 4/5] KVM: SVM: Compute DEBUGCTL reserved bits dynamically Shivansh Dhiman
2026-07-09  8:44   ` sashiko-bot
2026-07-09  8:29 ` [PATCH v3 5/5] KVM: SVM: Add Bus Lock Detect support Shivansh Dhiman
2026-07-09  8:55   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox