* [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2
@ 2026-10-19 21:05 Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 01/12] KVM: SVM: Add VMCB fields for LBRv2 virtualization Shivansh Dhiman
` (11 more replies)
0 siblings, 12 replies; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
This series adds SVM support for virtualizing AMD's Last Branch Record
Extension Version 2 (LbrExtV2) in KVM.
Background
----------
LbrExtV2, introduced on Zen 4, is a branch sampling facility. It records
the last taken branches in a 16-entry stack of dedicated MSRs:
* MSR 0xC000010E - Last Branch Stack Select (LBR_SELECT)
* MSR 0xC000010F - Debug Extension Control (DBG_EXTN_CFG)
* MSR 0xC0010300..0xC001031F - 16 LBR FROM/TO MSR pairs
AMD hardware extends the VMCB save area with fields for these MSRs. When
V_LBR (VMCB.MISC_CTL2.LBR_VIRTUALIZATION_ENABLE) is set, the CPU saves
and restores the guest's LbrExtV2 state on VMRUN/#VMEXIT. Thus, the
guest can use LBRv2 without interception.
V_LBR is enabled lazily. KVM enables LBR virtualization only when the
guest sets DBG_EXTN_CFG.LBRV2EN (bit 6) to record branches. Thus, there
is no save/restore cost on VMRUN/#VMEXIT while the guest does not use
LBRv2. The series also adds the dependent LBR and PMC freeze-on-PMI
feature.
Major changes in v2
-------------------
* Added LBRv2 support for SEV-ES/SNP guests.
* Added LBRv2 support for nested SVM.
* Added live migration support for the LBRv2 MSRs.
Testing
-------
Disable the NMI watchdog on the host and enable the mediated PMU before
the test:
$ echo 0 | sudo tee /proc/sys/kernel/nmi_watchdog
$ sudo modprobe kvm_amd enable_mediated_pmu=1
Testing has been done by running perf LBR test inside the guest:
$ ./tools/perf/perf test -vvvv "LBR"
Links
-----
v1: https://lore.kernel.org/kvm/20260724195040.630468-1-shivansh.dhiman@amd.com/
Related QEMU patchset:
https://lore.kernel.org/kvm/20261019201314.269798-1-shivansh.dhiman@amd.com/
Regards,
Shivansh
---
Changelog:
v1 -> v2:
* Rebased on kvm-x86-next-2026.10.13. Use newly added functions
svm_get_supported_debugctl() and svm_need_lbr_virtualization().
* New patch to report the LBRv2 MSRs for live migration (patch 5).
* New patch to support LBRv2 for SEV-ES/SNP guests (patch 8).
* New patches to support LBRv2 for nested SVM (patches 9 and 10).
* Split v1 patch 2 into the CPUID feature bit (patch 2) and the perf
change (patch 6). Fix authorship.
* Advertise LbrExtV2 only with a 16-entry host LBR stack, and removed
the per-vCPU clear of the feature.
* Use KVM_BUG_ON() for an out-of-range LBR stack index.
* Do not mark the VMCB dirty on LBRv2 MSR writes.
* Updated commit messages.
---
Sandipan Das (3):
KVM: SVM: Add VMCB fields for LBRv2 virtualization
KVM: SVM: Add the LbrExtV2 CPUID feature bit
perf/x86: Expose the LBR stack depth via x86_pmu_capability
Shivansh Dhiman (9):
KVM: SVM: Emulate guest accesses to LBRv2 MSRs
KVM: SVM: Enable hardware-assisted LBRv2 virtualization
KVM: x86: Add support for Live Migration
KVM: SVM: Advertise LbrExtV2 to userspace
KVM: SVM: Support LBRv2 virtualization for SEV-ES guests
KVM: nSVM: Propagate LBRv2 state across nested transitions
KVM: nSVM: Pass through LBRv2 MSRs to L2
KVM: SVM: Add LBR/PMC freeze support
KVM: x86: Use SCATTERED_F() for PERFMON_V2
arch/x86/events/core.c | 1 +
arch/x86/include/asm/perf_event.h | 1 +
arch/x86/include/asm/svm.h | 28 ++++--
arch/x86/kvm/cpuid.c | 6 +-
arch/x86/kvm/msrs.c | 26 ++++++
arch/x86/kvm/reverse_cpuid.h | 6 +-
arch/x86/kvm/svm/nested.c | 51 ++++++++++-
arch/x86/kvm/svm/svm.c | 137 +++++++++++++++++++++++++++++-
arch/x86/kvm/svm/svm.h | 26 ++++++
9 files changed, 271 insertions(+), 11 deletions(-)
base-commit: dea4f6380f4ec96e8f3b0aaf2343764bd0d25ef4
--
2.43.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 01/12] KVM: SVM: Add VMCB fields for LBRv2 virtualization
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 22:09 ` Jim Mattson
2026-10-19 21:05 ` [PATCH v2 02/12] KVM: SVM: Add the LbrExtV2 CPUID feature bit Shivansh Dhiman
` (10 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
From: Sandipan Das <sandipan.das@amd.com>
Define the new VMCB fields that will be used to save and restore the
state of the following Last Branch Record Extension Version 2 (LbrExtV2)
related MSRs.
* Last Branch Stack Select (MSR 0xc000010e)
* Debug Extension Control (MSR 0xc000010f)
* LBRv2 Branch FROM (MSRs 0xc0010300..0xc001031e)
* LBRv2 Branch To (MSRs 0xc0010301..0xc001031f)
Signed-off-by: Sandipan Das <sandipan.das@amd.com>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
arch/x86/include/asm/svm.h | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 83fd49b26a4b..d76844224745 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -372,8 +372,15 @@ struct vmcb_save_area {
u64 br_to;
u64 last_excp_from;
u64 last_excp_to;
- u8 reserved_0x298[72];
+ u64 dbg_extn_cfg;
+ u8 reserved_0x2a0[64];
u64 spec_ctrl; /* Guest version of SPEC_CTRL at 0x2E0 */
+ u8 reserved_0x2e8[904];
+ struct {
+ u64 lbr_stack_from;
+ u64 lbr_stack_to;
+ } __packed lbr[16];
+ u64 lbr_select;
} __packed;
/* Save area definition for SEV-ES and SEV-SNP guests */
@@ -556,7 +563,7 @@ struct vmcb {
};
} __packed;
-#define EXPECTED_VMCB_SAVE_AREA_SIZE 744
+#define EXPECTED_VMCB_SAVE_AREA_SIZE 1912
#define EXPECTED_GHCB_SAVE_AREA_SIZE 1032
#define EXPECTED_SEV_ES_SAVE_AREA_SIZE 1648
#define EXPECTED_VMCB_CONTROL_AREA_SIZE 1024
@@ -581,7 +588,8 @@ static inline void __unused_size_checks(void)
BUILD_BUG_RESERVED_OFFSET(vmcb_save_area, 0xd8);
BUILD_BUG_RESERVED_OFFSET(vmcb_save_area, 0x180);
BUILD_BUG_RESERVED_OFFSET(vmcb_save_area, 0x248);
- BUILD_BUG_RESERVED_OFFSET(vmcb_save_area, 0x298);
+ BUILD_BUG_RESERVED_OFFSET(vmcb_save_area, 0x2a0);
+ BUILD_BUG_RESERVED_OFFSET(vmcb_save_area, 0x2e8);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0xc8);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0xcc);
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 02/12] KVM: SVM: Add the LbrExtV2 CPUID feature bit
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 01/12] KVM: SVM: Add VMCB fields for LBRv2 virtualization Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 22:15 ` Jim Mattson
2026-10-19 21:05 ` [PATCH v2 03/12] KVM: SVM: Emulate guest accesses to LBRv2 MSRs Shivansh Dhiman
` (9 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
From: Sandipan Das <sandipan.das@amd.com>
Define the LbrExtV2 KVM feature (CPUID 0x80000022.EAX[1]), so that KVM
can track it in guest CPUID and advertise it to userspace.
Signed-off-by: Sandipan Das <sandipan.das@amd.com>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* New patch. Split from v1 patch 2, "KVM: SVM: Add capability for
LbrExtV2". The perf change is now patch 6.
* Set Sandipan Das as the author.
---
arch/x86/kvm/reverse_cpuid.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
index 89345af0f7ad..8cb8af2d8c70 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -73,6 +73,7 @@
/* CPUID level 0x80000022 (EAX) */
#define KVM_X86_FEATURE_PERFMON_V2 KVM_X86_FEATURE(CPUID_8000_0022_EAX, 0)
+#define KVM_X86_FEATURE_AMD_LBR_V2 KVM_X86_FEATURE(CPUID_8000_0022_EAX, 1)
/* CPUID level 0x80000021 (ECX) */
#define KVM_X86_FEATURE_TSA_SQ_NO KVM_X86_FEATURE(CPUID_8000_0021_ECX, 1)
@@ -148,6 +149,7 @@ static __always_inline u32 __feature_translate(int x86_feature)
KVM_X86_TRANSLATE_FEATURE(SGX_EDECCSSA);
KVM_X86_TRANSLATE_FEATURE(CONSTANT_TSC);
KVM_X86_TRANSLATE_FEATURE(PERFMON_V2);
+ KVM_X86_TRANSLATE_FEATURE(AMD_LBR_V2);
KVM_X86_TRANSLATE_FEATURE(RRSBA_CTRL);
KVM_X86_TRANSLATE_FEATURE(BHI_CTRL);
KVM_X86_TRANSLATE_FEATURE(TSA_SQ_NO);
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 03/12] KVM: SVM: Emulate guest accesses to LBRv2 MSRs
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 01/12] KVM: SVM: Add VMCB fields for LBRv2 virtualization Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 02/12] KVM: SVM: Add the LbrExtV2 CPUID feature bit Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 23:26 ` Jim Mattson
2026-10-19 21:05 ` [PATCH v2 04/12] KVM: SVM: Enable hardware-assisted LBRv2 virtualization Shivansh Dhiman
` (8 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
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 <shivansh.dhiman@amd.com>
---
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
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 04/12] KVM: SVM: Enable hardware-assisted LBRv2 virtualization
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (2 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 03/12] KVM: SVM: Emulate guest accesses to LBRv2 MSRs Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:33 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 05/12] KVM: x86: Add support for Live Migration Shivansh Dhiman
` (7 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
Enable V_LBR so hardware saves and restores the guest's LBRv2 state in
the VMCB, and pass the LBRv2 MSRs through (by disabling interception)
to the guest while it is set. V_LBR is enabled lazily, only once the
guest turns on branch recording, just like legacy LBR keys off DEBUGCTL.
Co-developed-by: Sandipan Das <sandipan.das@amd.com>
Signed-off-by: Sandipan Das <sandipan.das@amd.com>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* Check svm_lbrv2_active() in svm_need_lbr_virtualization(), which is
a newly added function.
---
arch/x86/kvm/svm/svm.c | 45 +++++++++++++++++++++++++++++++++++++++++-
arch/x86/kvm/svm/svm.h | 1 +
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 428c95d7ef21..69f9ea746bb6 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -758,6 +758,41 @@ static bool svm_lbrv2_supported(struct kvm_vcpu *vcpu)
kvm_vcpu_has_mediated_pmu(vcpu);
}
+static bool svm_lbrv2_active(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ return svm_lbrv2_supported(vcpu) &&
+ (svm->vmcb->save.dbg_extn_cfg & DBG_EXTN_CFG_LBRV2EN);
+}
+
+static void svm_recalc_lbrv2_msr_intercepts(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ bool intercept = !svm_lbrv2_active(vcpu);
+ int i;
+
+ if (intercept == svm->lbrv2_msrs_intercepted)
+ return;
+
+ for (i = 0; i < 32; i++)
+ svm_set_intercept_for_msr(vcpu, MSR_AMD_SAMP_BR_FROM + i, MSR_TYPE_RW, intercept);
+
+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_LBR_SELECT, MSR_TYPE_RW, intercept);
+
+ /*
+ * DBG_EXTN_CFG stays permanently intercepted for non-SEV-ES guests so
+ * KVM can observe LBRV2EN and lazily toggle V_LBR. SEV-ES+ guests must
+ * delegate LBR virtualization to the processor (per the APM), where
+ * intercepting LBR MSRs can be fatal, so toggle it with the rest. See
+ * commit b7e4be0a224f ("KVM: SEV-ES: Delegate LBR virtualization to the
+ * processor").
+ */
+ if (is_sev_es_guest(vcpu))
+ svm_set_intercept_for_msr(vcpu, MSR_AMD_DBG_EXTN_CFG, MSR_TYPE_RW, intercept);
+
+ svm->lbrv2_msrs_intercepted = intercept;
+}
+
void svm_vcpu_free_msrpm(void *msrpm)
{
__free_pages(virt_to_page(msrpm), get_order(MSRPM_SIZE));
@@ -813,8 +848,10 @@ static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu)
svm_disable_intercept_for_msr(vcpu, MSR_SYSCALL_MASK, MSR_TYPE_RW);
#endif
- if (lbrv)
+ if (lbrv) {
svm_recalc_lbr_msr_intercepts(vcpu);
+ svm_recalc_lbrv2_msr_intercepts(vcpu);
+ }
if (cpu_feature_enabled(X86_FEATURE_IBPB))
svm_set_intercept_for_msr(vcpu, MSR_IA32_PRED_CMD, MSR_TYPE_W,
@@ -882,6 +919,7 @@ void svm_enable_lbrv(struct kvm_vcpu *vcpu)
{
__svm_enable_lbrv(vcpu);
svm_recalc_lbr_msr_intercepts(vcpu);
+ svm_recalc_lbrv2_msr_intercepts(vcpu);
}
static void __svm_disable_lbrv(struct kvm_vcpu *vcpu)
@@ -897,6 +935,9 @@ static bool svm_need_lbr_virtualization(struct kvm_vcpu *vcpu)
if (svm->vmcb->save.dbgctl & (DEBUGCTLMSR_LBR | DEBUGCTLMSR_BUS_LOCK_DETECT))
return true;
+ if (svm_lbrv2_active(vcpu))
+ return true;
+
return is_guest_mode(vcpu) &&
(svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR);
}
@@ -919,6 +960,7 @@ void svm_update_lbrv(struct kvm_vcpu *vcpu)
* do, so always recalculate the intercepts here.
*/
svm_recalc_lbr_msr_intercepts(vcpu);
+ svm_recalc_lbrv2_msr_intercepts(vcpu);
}
void disable_nmi_singlestep(struct vcpu_svm *svm)
@@ -1383,6 +1425,7 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
svm->x2avic_msrs_intercepted = true;
svm->lbr_msrs_intercepted = true;
+ svm->lbrv2_msrs_intercepted = true;
svm->vmcb01.ptr = page_address(vmcb01_page);
svm->vmcb01.pa = __sme_set(page_to_pfn(vmcb01_page) << PAGE_SHIFT);
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index cde6bef61dcd..72f19053fb4c 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -366,6 +366,7 @@ struct vcpu_svm {
bool avic_irq_window;
bool x2avic_msrs_intercepted;
bool lbr_msrs_intercepted;
+ bool lbrv2_msrs_intercepted;
/* Guest GIF value, used when vGIF is not enabled */
bool guest_gif;
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 05/12] KVM: x86: Add support for Live Migration
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (3 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 04/12] KVM: SVM: Enable hardware-assisted LBRv2 virtualization Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 06/12] perf/x86: Expose the LBR stack depth via x86_pmu_capability Shivansh Dhiman
` (6 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
Report the LBRv2 MSRs through KVM_GET_MSR_INDEX_LIST so that userspace
can save and restore them across live migration.
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* New patch.
* Lets userspace save/restore the LBRv2 MSRs through
KVM_{GET,SET}_MSRS.
---
arch/x86/kvm/msrs.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index e91650af44c2..e392e3ac928f 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -245,6 +245,26 @@ static const u32 msrs_to_save_pmu[] = {
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS,
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR,
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET,
+
+ MSR_AMD_DBG_EXTN_CFG, MSR_AMD64_LBR_SELECT,
+
+ /* This part of MSRs should match SVM_LBR_V2_STACK_SIZE * 2. */
+ MSR_AMD_SAMP_BR_FROM + 0, MSR_AMD_SAMP_BR_FROM + 1,
+ MSR_AMD_SAMP_BR_FROM + 2, MSR_AMD_SAMP_BR_FROM + 3,
+ MSR_AMD_SAMP_BR_FROM + 4, MSR_AMD_SAMP_BR_FROM + 5,
+ MSR_AMD_SAMP_BR_FROM + 6, MSR_AMD_SAMP_BR_FROM + 7,
+ MSR_AMD_SAMP_BR_FROM + 8, MSR_AMD_SAMP_BR_FROM + 9,
+ MSR_AMD_SAMP_BR_FROM + 10, MSR_AMD_SAMP_BR_FROM + 11,
+ MSR_AMD_SAMP_BR_FROM + 12, MSR_AMD_SAMP_BR_FROM + 13,
+ MSR_AMD_SAMP_BR_FROM + 14, MSR_AMD_SAMP_BR_FROM + 15,
+ MSR_AMD_SAMP_BR_FROM + 16, MSR_AMD_SAMP_BR_FROM + 17,
+ MSR_AMD_SAMP_BR_FROM + 18, MSR_AMD_SAMP_BR_FROM + 19,
+ MSR_AMD_SAMP_BR_FROM + 20, MSR_AMD_SAMP_BR_FROM + 21,
+ MSR_AMD_SAMP_BR_FROM + 22, MSR_AMD_SAMP_BR_FROM + 23,
+ MSR_AMD_SAMP_BR_FROM + 24, MSR_AMD_SAMP_BR_FROM + 25,
+ MSR_AMD_SAMP_BR_FROM + 26, MSR_AMD_SAMP_BR_FROM + 27,
+ MSR_AMD_SAMP_BR_FROM + 28, MSR_AMD_SAMP_BR_FROM + 29,
+ MSR_AMD_SAMP_BR_FROM + 30, MSR_AMD_SAMP_BR_FROM + 31,
};
static u32 msrs_to_save[ARRAY_SIZE(msrs_to_save_base) +
@@ -2644,6 +2664,12 @@ static void kvm_probe_msr_to_save(u32 msr_index)
if (!kvm_cpu_cap_has(X86_FEATURE_PERFMON_V2))
return;
break;
+ case MSR_AMD_DBG_EXTN_CFG:
+ case MSR_AMD64_LBR_SELECT:
+ case MSR_AMD_SAMP_BR_FROM ... MSR_AMD_SAMP_BR_FROM + 31:
+ if (!kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2))
+ return;
+ break;
case MSR_IA32_XFD:
case MSR_IA32_XFD_ERR:
if (!kvm_cpu_cap_has(X86_FEATURE_XFD))
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 06/12] perf/x86: Expose the LBR stack depth via x86_pmu_capability
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (4 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 05/12] KVM: x86: Add support for Live Migration Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 07/12] KVM: SVM: Advertise LbrExtV2 to userspace Shivansh Dhiman
` (5 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
From: Sandipan Das <sandipan.das@amd.com>
Report the host's LBR stack depth in x86_pmu_capability, so that KVM can
advertise the depth the hardware actually supports rather than assuming
a value.
Signed-off-by: Sandipan Das <sandipan.das@amd.com>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* New patch. Split from v1 patch 2, "KVM: SVM: Add capability for
LbrExtV2", and moved right before its first user, patch 7.
* Set Sandipan Das as the author.
---
arch/x86/events/core.c | 1 +
arch/x86/include/asm/perf_event.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 8b3ea0adb965..85dc065406e9 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -3162,6 +3162,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
cap->bit_width_fixed = cap->num_counters_fixed ? x86_pmu.cntval_bits : 0;
cap->events_mask = (unsigned int)x86_pmu.events_maskl;
cap->events_mask_len = x86_pmu.events_mask_len;
+ cap->num_branches_lbr = x86_pmu.lbr_nr;
cap->pebs_ept = x86_pmu.pebs_ept;
cap->mediated = !!(pmu.capabilities & PERF_PMU_CAP_MEDIATED_VPMU);
}
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 1eb13673e889..f18cf0986e12 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -306,6 +306,7 @@ struct x86_pmu_capability {
int bit_width_fixed;
unsigned int events_mask;
int events_mask_len;
+ int num_branches_lbr;
unsigned int pebs_ept :1;
unsigned int mediated :1;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 07/12] KVM: SVM: Advertise LbrExtV2 to userspace
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (5 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 06/12] perf/x86: Expose the LBR stack depth via x86_pmu_capability Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:27 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 08/12] KVM: SVM: Support LBRv2 virtualization for SEV-ES guests Shivansh Dhiman
` (4 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
Now that LBRv2 is fully virtualized, advertise it and report the LBR
stack depth in CPUID. Gate the advertisement on LBR virtualization, the
mediated PMU, PerfMonV2 and a 16-entry host stack, so KVM never exposes a
configuration it cannot faithfully virtualize.
Co-developed-by: Sandipan Das <sandipan.das@amd.com>
Signed-off-by: Sandipan Das <sandipan.das@amd.com>
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* Clear AMD_LBR_V2 in svm_set_cpu_caps() if the host LBR stack does not
have 16 entries.
* Removed the per-vCPU clear of AMD_LBR_V2 in svm_vcpu_after_set_cpuid().
* Report the host LBR stack depth in CPUID 0x80000022.EBX directly.
* Added a comment.
---
arch/x86/kvm/cpuid.c | 3 +++
arch/x86/kvm/svm/svm.c | 12 ++++++++++++
2 files changed, 15 insertions(+)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index adafd6fa2c54..bb24d8362132 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1258,6 +1258,7 @@ void kvm_initialize_cpu_caps(void)
kvm_cpu_cap_init(CPUID_8000_0022_EAX,
F(PERFMON_V2),
+ SCATTERED_F(AMD_LBR_V2),
);
if (!static_cpu_has_bug(X86_BUG_NULL_SEG))
@@ -1904,6 +1905,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
cpuid_entry_override(entry, CPUID_8000_0022_EAX);
ebx.split.num_core_pmc = kvm_pmu_cap.num_counters_gp;
+ ebx.split.lbr_v2_stack_sz = kvm_pmu_cap.num_branches_lbr;
+
entry->ebx = ebx.full;
break;
}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 69f9ea746bb6..ea7535ccc3fe 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5843,6 +5843,18 @@ static __init void svm_set_cpu_caps(void)
if (kvm_pmu_cap.version != 2 ||
!kvm_cpu_cap_has(X86_FEATURE_PERFCTR_CORE))
kvm_cpu_cap_clear(X86_FEATURE_PERFMON_V2);
+
+ /*
+ * LBRv2 requires LBR virtualization and the mediated PMU to
+ * let the guest own the LBR stack, and is enumerated through
+ * PerfMonV2's CPUID leaf. KVM also can't faithfully virtualize
+ * a stack depth other than the architectural 16 entries, as the
+ * VMCB save area provides exactly that many entries.
+ */
+ if (!lbrv || !enable_mediated_pmu ||
+ !kvm_cpu_cap_has(X86_FEATURE_PERFMON_V2) ||
+ kvm_pmu_cap.num_branches_lbr != SVM_LBR_V2_STACK_SIZE)
+ kvm_cpu_cap_clear(X86_FEATURE_AMD_LBR_V2);
}
/* CPUID 0x8000001F (SME/SEV features) */
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 08/12] KVM: SVM: Support LBRv2 virtualization for SEV-ES guests
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (6 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 07/12] KVM: SVM: Advertise LbrExtV2 to userspace Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 09/12] KVM: nSVM: Propagate LBRv2 state across nested transitions Shivansh Dhiman
` (3 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
Add the LBRv2 fields to the SEV-ES VMSA save area and treat LBRv2 as
always active for SEV-ES+ guests, which run with LBR virtualization
mandatorily enabled and keep DBG_EXTN_CFG in the encrypted VMSA.
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* New patch.
---
arch/x86/include/asm/svm.h | 12 +++++++++---
arch/x86/kvm/svm/svm.c | 15 +++++++++++++--
2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index fb42c6806655..0473e38e33bc 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -446,7 +446,8 @@ struct sev_es_save_area {
u64 br_to;
u64 last_excp_from;
u64 last_excp_to;
- u8 reserved_0x298[80];
+ u64 dbg_extn_cfg;
+ u8 reserved_0x2a0[72];
u32 pkru;
u32 tsc_aux;
u64 tsc_scale;
@@ -495,6 +496,11 @@ struct sev_es_save_area {
u8 fpreg_x87[80];
u8 fpreg_xmm[256];
u8 fpreg_ymm[256];
+ struct {
+ u64 lbr_stack_from;
+ u64 lbr_stack_to;
+ } __packed lbr[16];
+ u64 lbr_select;
} __packed;
struct ghcb_save_area {
@@ -567,7 +573,7 @@ struct vmcb {
#define EXPECTED_VMCB_SAVE_AREA_SIZE 1912
#define EXPECTED_GHCB_SAVE_AREA_SIZE 1032
-#define EXPECTED_SEV_ES_SAVE_AREA_SIZE 1648
+#define EXPECTED_SEV_ES_SAVE_AREA_SIZE 1912
#define EXPECTED_VMCB_CONTROL_AREA_SIZE 1024
#define EXPECTED_GHCB_SIZE PAGE_SIZE
@@ -598,7 +604,7 @@ static inline void __unused_size_checks(void)
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0xd8);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x1c0);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x248);
- BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x298);
+ BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x2a0);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x300);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index ea7535ccc3fe..d5656e047dad 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -761,8 +761,19 @@ static bool svm_lbrv2_supported(struct kvm_vcpu *vcpu)
static bool svm_lbrv2_active(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
- return svm_lbrv2_supported(vcpu) &&
- (svm->vmcb->save.dbg_extn_cfg & DBG_EXTN_CFG_LBRV2EN);
+
+ if (!svm_lbrv2_supported(vcpu))
+ return false;
+
+ /*
+ * SEV-ES+ guests run with LBR virtualization mandatorily enabled and
+ * keep DBG_EXTN_CFG in the encrypted VMSA, so KVM can't observe
+ * LBRV2EN; treat LBRv2 as always active.
+ */
+ if (is_sev_es_guest(vcpu))
+ return true;
+
+ return svm->vmcb->save.dbg_extn_cfg & DBG_EXTN_CFG_LBRV2EN;
}
static void svm_recalc_lbrv2_msr_intercepts(struct kvm_vcpu *vcpu)
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 09/12] KVM: nSVM: Propagate LBRv2 state across nested transitions
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (7 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 08/12] KVM: SVM: Support LBRv2 virtualization for SEV-ES guests Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:31 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 10/12] KVM: nSVM: Pass through LBRv2 MSRs to L2 Shivansh Dhiman
` (2 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
Copy the LBRv2 state on nested VMRUN and #VMEXIT just like the legacy
LBR state. If L1 enables V_LBR for L2, L2 has its own LBRv2 state in
vmcb12. If L1 does not enable V_LBR, L2 uses the LBRv2 state of L1.
Add svm_copy_lbrs_v2() macro to copy this state. Keep it separate from
svm_copy_lbrs(), because the LBR stack is 256 bytes and KVM copies it
only when LBRv2 is supported.
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* New patch.
---
arch/x86/kvm/svm/nested.c | 28 +++++++++++++++++++++++++++-
arch/x86/kvm/svm/svm.h | 18 ++++++++++++++++++
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index af0ef678433d..b356a091e320 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -608,6 +608,9 @@ static void __nested_copy_vmcb_save_to_cache(struct vmcb_save_area_cached *to,
to->g_pat = from->g_pat;
svm_copy_lbrs(to, from);
+
+ if (kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2))
+ svm_copy_lbrs_v2(to, from);
}
void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm,
@@ -819,8 +822,17 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm)
*/
svm_copy_lbrs(&vmcb02->save, save);
vmcb02->save.dbgctl &= svm_get_supported_debugctl(vcpu);
+
+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2)) {
+ svm_copy_lbrs_v2(&vmcb02->save, save);
+ vmcb02->save.dbg_extn_cfg &= ~DBG_EXTN_CFG_RESERVED_BITS;
+ vmcb02->save.lbr_select &= ~LBR_SELECT_RESERVED_BITS;
+ }
} else {
svm_copy_lbrs(&vmcb02->save, &vmcb01->save);
+
+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2))
+ svm_copy_lbrs_v2(&vmcb02->save, &vmcb01->save);
}
vmcb_mark_dirty(vmcb02, VMCB_LBR);
svm_update_lbrv(&svm->vcpu);
@@ -1236,6 +1248,12 @@ void svm_copy_vmrun_state(struct kvm_vcpu *vcpu, struct vmcb_save_area *to_save,
svm_copy_lbrs(to_save, from_save);
to_save->dbgctl &= svm_get_supported_debugctl(vcpu);
}
+
+ if (kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2)) {
+ svm_copy_lbrs_v2(to_save, from_save);
+ to_save->dbg_extn_cfg &= ~DBG_EXTN_CFG_RESERVED_BITS;
+ to_save->lbr_select &= ~LBR_SELECT_RESERVED_BITS;
+ }
}
void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
@@ -1305,9 +1323,13 @@ static int nested_svm_vmexit_update_vmcb12(struct kvm_vcpu *vcpu)
if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS))
vmcb12->control.next_rip = vmcb02->control.next_rip;
- if (svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR)
+ if (svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR) {
svm_copy_lbrs(&vmcb12->save, &vmcb02->save);
+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2))
+ svm_copy_lbrs_v2(&vmcb12->save, &vmcb02->save);
+ }
+
vmcb12->control.event_inj = 0;
vmcb12->control.event_inj_err = 0;
vmcb12->control.int_ctl = svm->nested.ctl.int_ctl;
@@ -1384,6 +1406,10 @@ void nested_svm_vmexit(struct vcpu_svm *svm)
if (!(svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR)) {
svm_copy_lbrs(&vmcb01->save, &vmcb02->save);
+
+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2))
+ svm_copy_lbrs_v2(&vmcb01->save, &vmcb02->save);
+
vmcb_mark_dirty(vmcb01, VMCB_LBR);
}
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 72f19053fb4c..01be2fc05d70 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -176,6 +176,12 @@ struct vmcb_save_area_cached {
u64 br_to;
u64 last_excp_from;
u64 last_excp_to;
+ u64 dbg_extn_cfg;
+ struct {
+ u64 lbr_stack_from;
+ u64 lbr_stack_to;
+ } lbr[SVM_LBR_V2_STACK_SIZE];
+ u64 lbr_select;
};
struct vmcb_ctrl_area_cached {
@@ -820,6 +826,18 @@ do { \
(to)->last_excp_to = (from)->last_excp_to; \
} while (0)
+/*
+ * Kept separate from svm_copy_lbrs() as the branch record stack is 256 bytes,
+ * i.e. is only worth copying when LBR v2 is exposed to the guest.
+ */
+#define svm_copy_lbrs_v2(to, from) \
+do { \
+ BUILD_BUG_ON(sizeof((to)->lbr) != sizeof((from)->lbr)); \
+ (to)->dbg_extn_cfg = (from)->dbg_extn_cfg; \
+ (to)->lbr_select = (from)->lbr_select; \
+ memcpy((to)->lbr, (from)->lbr, sizeof((to)->lbr)); \
+} while (0)
+
void svm_vcpu_free_msrpm(void *msrpm);
void svm_enable_lbrv(struct kvm_vcpu *vcpu);
void svm_update_lbrv(struct kvm_vcpu *vcpu);
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 10/12] KVM: nSVM: Pass through LBRv2 MSRs to L2
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (8 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 09/12] KVM: nSVM: Propagate LBRv2 state across nested transitions Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:36 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 11/12] KVM: SVM: Add LBR/PMC freeze support Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 12/12] KVM: x86: Use SCATTERED_F() for PERFMON_V2 Shivansh Dhiman
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
Add the LBRv2 MSRs to merge_msrs[] so that their MSRPM bits are merged
into msrpm02, letting L2 access them without taking a VM-Exit. Grow the
merge offsets array to 12, as needed by 32-bit kernels where each offset
covers 16 MSRs rather than 32.
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* New patch.
---
arch/x86/kvm/svm/nested.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index b356a091e320..84154f353935 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -209,7 +209,7 @@ void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
* Hardcode the capacity of the array based on the maximum number of _offsets_.
* MSRs are batched together, so there are fewer offsets than MSRs.
*/
-static int nested_svm_msrpm_merge_offsets[10] __ro_after_init;
+static int nested_svm_msrpm_merge_offsets[12] __ro_after_init;
static int nested_svm_nr_msrpm_merge_offsets __ro_after_init;
typedef unsigned long nsvm_msrpm_merge_t;
@@ -253,6 +253,27 @@ int __init nested_svm_init_msrpm_merge_offsets(void)
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS,
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR,
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET,
+
+ MSR_AMD_DBG_EXTN_CFG,
+ MSR_AMD64_LBR_SELECT,
+
+ /* This part of MSRs should match SVM_LBR_V2_STACK_SIZE * 2. */
+ MSR_AMD_SAMP_BR_FROM + 0, MSR_AMD_SAMP_BR_FROM + 1,
+ MSR_AMD_SAMP_BR_FROM + 2, MSR_AMD_SAMP_BR_FROM + 3,
+ MSR_AMD_SAMP_BR_FROM + 4, MSR_AMD_SAMP_BR_FROM + 5,
+ MSR_AMD_SAMP_BR_FROM + 6, MSR_AMD_SAMP_BR_FROM + 7,
+ MSR_AMD_SAMP_BR_FROM + 8, MSR_AMD_SAMP_BR_FROM + 9,
+ MSR_AMD_SAMP_BR_FROM + 10, MSR_AMD_SAMP_BR_FROM + 11,
+ MSR_AMD_SAMP_BR_FROM + 12, MSR_AMD_SAMP_BR_FROM + 13,
+ MSR_AMD_SAMP_BR_FROM + 14, MSR_AMD_SAMP_BR_FROM + 15,
+ MSR_AMD_SAMP_BR_FROM + 16, MSR_AMD_SAMP_BR_FROM + 17,
+ MSR_AMD_SAMP_BR_FROM + 18, MSR_AMD_SAMP_BR_FROM + 19,
+ MSR_AMD_SAMP_BR_FROM + 20, MSR_AMD_SAMP_BR_FROM + 21,
+ MSR_AMD_SAMP_BR_FROM + 22, MSR_AMD_SAMP_BR_FROM + 23,
+ MSR_AMD_SAMP_BR_FROM + 24, MSR_AMD_SAMP_BR_FROM + 25,
+ MSR_AMD_SAMP_BR_FROM + 26, MSR_AMD_SAMP_BR_FROM + 27,
+ MSR_AMD_SAMP_BR_FROM + 28, MSR_AMD_SAMP_BR_FROM + 29,
+ MSR_AMD_SAMP_BR_FROM + 30, MSR_AMD_SAMP_BR_FROM + 31,
};
int i, j;
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 11/12] KVM: SVM: Add LBR/PMC freeze support
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (9 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 10/12] KVM: nSVM: Pass through LBRv2 MSRs to L2 Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
2026-10-19 21:27 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 12/12] KVM: x86: Use SCATTERED_F() for PERFMON_V2 Shivansh Dhiman
11 siblings, 1 reply; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
Add support for the LBR and PMC freeze-on-PMI feature (CPUID
0x80000022.EAX[2]). This feature is very often used with LBRv2. Permit
the guest to enable freezing via DEBUGCTL only when the feature is
advertised to it, and advertise it accordingly.
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* Add the freeze bits to svm_get_supported_debugctl() when the guest has
AMD_LBR_PMC_FREEZE, in place of a separate check in svm_set_msr().
* Removed DEBUGCTL_LBR_PMC_FREEZE_BITS.
---
arch/x86/kvm/cpuid.c | 1 +
arch/x86/kvm/reverse_cpuid.h | 6 ++++--
arch/x86/kvm/svm/svm.h | 4 ++++
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index bb24d8362132..37da587ede71 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1259,6 +1259,7 @@ void kvm_initialize_cpu_caps(void)
kvm_cpu_cap_init(CPUID_8000_0022_EAX,
F(PERFMON_V2),
SCATTERED_F(AMD_LBR_V2),
+ SCATTERED_F(AMD_LBR_PMC_FREEZE),
);
if (!static_cpu_has_bug(X86_BUG_NULL_SEG))
diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
index 8cb8af2d8c70..418d6f59d7d7 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -72,8 +72,9 @@
#define KVM_X86_FEATURE_CONSTANT_TSC KVM_X86_FEATURE(CPUID_8000_0007_EDX, 8)
/* CPUID level 0x80000022 (EAX) */
-#define KVM_X86_FEATURE_PERFMON_V2 KVM_X86_FEATURE(CPUID_8000_0022_EAX, 0)
-#define KVM_X86_FEATURE_AMD_LBR_V2 KVM_X86_FEATURE(CPUID_8000_0022_EAX, 1)
+#define KVM_X86_FEATURE_PERFMON_V2 KVM_X86_FEATURE(CPUID_8000_0022_EAX, 0)
+#define KVM_X86_FEATURE_AMD_LBR_V2 KVM_X86_FEATURE(CPUID_8000_0022_EAX, 1)
+#define KVM_X86_FEATURE_AMD_LBR_PMC_FREEZE KVM_X86_FEATURE(CPUID_8000_0022_EAX, 2)
/* CPUID level 0x80000021 (ECX) */
#define KVM_X86_FEATURE_TSA_SQ_NO KVM_X86_FEATURE(CPUID_8000_0021_ECX, 1)
@@ -150,6 +151,7 @@ static __always_inline u32 __feature_translate(int x86_feature)
KVM_X86_TRANSLATE_FEATURE(CONSTANT_TSC);
KVM_X86_TRANSLATE_FEATURE(PERFMON_V2);
KVM_X86_TRANSLATE_FEATURE(AMD_LBR_V2);
+ KVM_X86_TRANSLATE_FEATURE(AMD_LBR_PMC_FREEZE);
KVM_X86_TRANSLATE_FEATURE(RRSBA_CTRL);
KVM_X86_TRANSLATE_FEATURE(BHI_CTRL);
KVM_X86_TRANSLATE_FEATURE(TSA_SQ_NO);
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 01be2fc05d70..9b65728ee0d2 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -801,6 +801,10 @@ static inline u64 svm_get_supported_debugctl(struct kvm_vcpu *vcpu)
if (guest_cpu_cap_has(vcpu, X86_FEATURE_BUS_LOCK_DETECT))
debugctl |= DEBUGCTLMSR_BUS_LOCK_DETECT;
+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_PMC_FREEZE))
+ debugctl |= DEBUGCTLMSR_FREEZE_LBRS_ON_PMI |
+ DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI;
+
return debugctl;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 12/12] KVM: x86: Use SCATTERED_F() for PERFMON_V2
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
` (10 preceding siblings ...)
2026-10-19 21:05 ` [PATCH v2 11/12] KVM: SVM: Add LBR/PMC freeze support Shivansh Dhiman
@ 2026-10-19 21:05 ` Shivansh Dhiman
11 siblings, 0 replies; 21+ messages in thread
From: Shivansh Dhiman @ 2026-10-19 21:05 UTC (permalink / raw)
To: seanjc, pbonzini, tglx, mingo
Cc: peterz, acme, kvm, x86, yosry, jmattson, thomas.lendacky,
nikunj.dadhania, sandipan.das, shivansh.dhiman
PERFMON_V2 is a scattered feature, so enumerate it with SCATTERED_F()
instead of F(). No functional change intended.
Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
---
Changes in v2:
* Rebased, no changes.
---
arch/x86/kvm/cpuid.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 37da587ede71..3d00a4eaf1a4 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1257,7 +1257,7 @@ void kvm_initialize_cpu_caps(void)
);
kvm_cpu_cap_init(CPUID_8000_0022_EAX,
- F(PERFMON_V2),
+ SCATTERED_F(PERFMON_V2),
SCATTERED_F(AMD_LBR_V2),
SCATTERED_F(AMD_LBR_PMC_FREEZE),
);
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v2 07/12] KVM: SVM: Advertise LbrExtV2 to userspace
2026-10-19 21:05 ` [PATCH v2 07/12] KVM: SVM: Advertise LbrExtV2 to userspace Shivansh Dhiman
@ 2026-10-19 21:27 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-19 21:27 UTC (permalink / raw)
To: Shivansh Dhiman; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] KVM: x86: __do_cpuid_func() reports lbr_v2_stack_sz when LBRv2 is disabled
- [Medium] KVM: SVM: AMD_LBR_V2 not cleared in svm_set_cpu_caps() when !enable_pmu
--
commit 7e80a77e67ad5de914e6e6583e6c7b559aacbce2
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>
KVM: SVM: Advertise LbrExtV2 to userspace
This commit advertises X86_FEATURE_AMD_LBR_V2 and reports the LBRv2 stack
depth in CPUID leaf 0x80000022 when LBR virtualization, the mediated PMU,
PerfMonV2, and a 16-entry host LBR stack are available.
> diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
> index adafd6fa2c542..bb24d83621328 100644
> --- a/arch/x86/kvm/cpuid.c
> +++ b/arch/x86/kvm/cpuid.c
> @@ -1258,6 +1258,7 @@ void kvm_initialize_cpu_caps(void)
>
> kvm_cpu_cap_init(CPUID_8000_0022_EAX,
> F(PERFMON_V2),
> + SCATTERED_F(AMD_LBR_V2),
> );
>
> if (!static_cpu_has_bug(X86_BUG_NULL_SEG))
> @@ -1904,6 +1905,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
> cpuid_entry_override(entry, CPUID_8000_0022_EAX);
>
> ebx.split.num_core_pmc = kvm_pmu_cap.num_counters_gp;
> + ebx.split.lbr_v2_stack_sz = kvm_pmu_cap.num_branches_lbr;
[Severity: Medium]
Should __do_cpuid_func() in arch/x86/kvm/cpuid.c check
kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2) before populating
ebx.split.lbr_v2_stack_sz?
When enable_pmu is true on an LBRv2-capable host, kvm_init_pmu_capability()
copies kvm_host_pmu into kvm_pmu_cap:
arch/x86/kvm/pmu.c:kvm_init_pmu_capability() {
...
memcpy(&kvm_pmu_cap, &kvm_host_pmu, sizeof(kvm_host_pmu));
...
}
In svm_set_cpu_caps(), if lbrv is false, enable_mediated_pmu is false, or
kvm_pmu_cap.num_branches_lbr is not SVM_LBR_V2_STACK_SIZE, KVM clears
X86_FEATURE_AMD_LBR_V2 while X86_FEATURE_PERFMON_V2 remains set and
kvm_pmu_cap.num_branches_lbr remains non-zero.
When userspace invokes KVM_GET_SUPPORTED_CPUID, __do_cpuid_func() for leaf
0x80000022 only checks enable_pmu and
kvm_cpu_cap_has(X86_FEATURE_PERFMON_V2). Wouldn't this cause
KVM_GET_SUPPORTED_CPUID to report a non-zero lbr_v2_stack_sz in EBX even
when X86_FEATURE_AMD_LBR_V2 is cleared in EAX?
> +
> entry->ebx = ebx.full;
> break;
> }
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index 69f9ea746bb6f..ea7535ccc3fe6 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -5843,6 +5843,18 @@ static __init void svm_set_cpu_caps(void)
> if (kvm_pmu_cap.version != 2 ||
> !kvm_cpu_cap_has(X86_FEATURE_PERFCTR_CORE))
> kvm_cpu_cap_clear(X86_FEATURE_PERFMON_V2);
> +
> + /*
> + * LBRv2 requires LBR virtualization and the mediated PMU to
> + * let the guest own the LBR stack, and is enumerated through
> + * PerfMonV2's CPUID leaf. KVM also can't faithfully virtualize
> + * a stack depth other than the architectural 16 entries, as the
> + * VMCB save area provides exactly that many entries.
> + */
> + if (!lbrv || !enable_mediated_pmu ||
> + !kvm_cpu_cap_has(X86_FEATURE_PERFMON_V2) ||
> + kvm_pmu_cap.num_branches_lbr != SVM_LBR_V2_STACK_SIZE)
> + kvm_cpu_cap_clear(X86_FEATURE_AMD_LBR_V2);
> }
[Severity: Medium]
In svm_set_cpu_caps() in arch/x86/kvm/svm/svm.c, both
kvm_cpu_cap_clear(X86_FEATURE_PERFMON_V2) and
kvm_cpu_cap_clear(X86_FEATURE_AMD_LBR_V2) are inside the if (enable_pmu)
block:
arch/x86/kvm/svm/svm.c:svm_set_cpu_caps() {
...
if (enable_pmu) {
...
if (kvm_pmu_cap.version != 2 ||
!kvm_cpu_cap_has(X86_FEATURE_PERFCTR_CORE))
kvm_cpu_cap_clear(X86_FEATURE_PERFMON_V2);
if (!lbrv || !enable_mediated_pmu ||
!kvm_cpu_cap_has(X86_FEATURE_PERFMON_V2) ||
kvm_pmu_cap.num_branches_lbr != SVM_LBR_V2_STACK_SIZE)
kvm_cpu_cap_clear(X86_FEATURE_AMD_LBR_V2);
}
...
}
When enable_pmu is false on an LBRv2-capable host, kvm_initialize_cpu_caps()
still initializes CPUID_8000_0022_EAX with F(PERFMON_V2) and
SCATTERED_F(AMD_LBR_V2), and svm_set_cpu_caps() skips clearing them.
Because kvm_vcpu_after_set_cpuid() populates vcpu->arch.cpu_caps from
kvm_cpu_caps, doesn't this leave kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2)
true globally and allow guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2) to
be set if userspace passes leaf 0x80000022 in KVM_SET_CPUID2 when enable_pmu
is false?
With the earlier commit ("KVM: nSVM: Propagate LBRv2 state across nested
transitions") in this series, svm_copy_vmrun_state() and
__nested_copy_vmcb_save_to_cache() check
kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2), while nested_vmcb02_prepare_save(),
nested_svm_vmexit_update_vmcb12(), and nested_svm_vmexit() check
guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2). Wouldn't leaving
X86_FEATURE_AMD_LBR_V2 set when enable_pmu is false cause KVM to copy LBRv2
state into the L1 MSR_VM_HSAVE_PA area during SMM transitions and propagate
DBG_EXTN_CFG_LBRV2EN across nested transitions without mediated PMU support?
>
> /* CPUID 0x8000001F (SME/SEV features) */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019210532.378628-1-shivansh.dhiman@amd.com?part=7
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 11/12] KVM: SVM: Add LBR/PMC freeze support
2026-10-19 21:05 ` [PATCH v2 11/12] KVM: SVM: Add LBR/PMC freeze support Shivansh Dhiman
@ 2026-10-19 21:27 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-19 21:27 UTC (permalink / raw)
To: Shivansh Dhiman; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] KVM: SVM: AMD_LBR_PMC_FREEZE advertised without gating in svm_set_cpu_caps()
- [High] KVM: SVM: amd_pmu_refresh() leaves freeze bits reserved in global_status_rsvd
- [High] KVM: SVM: svm_vcpu_run() loses hardware PMC freeze when GLOBAL_CTL intercepted
- [High] KVM: SVM: svm_need_lbr_virtualization() omits DEBUGCTL freeze-on-PMI bits
--
commit e9a3e4432aa85c33133e5cfe1c272c751022082d
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>
KVM: SVM: Add LBR/PMC freeze support
This commit advertises X86_FEATURE_AMD_LBR_PMC_FREEZE (CPUID
0x80000022.EAX[2]) to guests and allows guests with the feature to set
DEBUGCTLMSR_FREEZE_LBRS_ON_PMI and DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI in
MSR_IA32_DEBUGCTLMSR.
> diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
> index bb24d83621328..37da587ede716 100644
> --- a/arch/x86/kvm/cpuid.c
> +++ b/arch/x86/kvm/cpuid.c
> @@ -1259,6 +1259,7 @@ void kvm_initialize_cpu_caps(void)
> kvm_cpu_cap_init(CPUID_8000_0022_EAX,
> F(PERFMON_V2),
> SCATTERED_F(AMD_LBR_V2),
> + SCATTERED_F(AMD_LBR_PMC_FREEZE),
> );
[Severity: High]
Should X86_FEATURE_AMD_LBR_PMC_FREEZE also be gated in svm_set_cpu_caps()
(and in svm_get_supported_debugctl()) on lbrv, enable_mediated_pmu, and
X86_FEATURE_PERFMON_V2?
In arch/x86/kvm/svm/svm.c, svm_set_cpu_caps() clears X86_FEATURE_AMD_LBR_V2
and X86_FEATURE_BUS_LOCK_DETECT when LBR virtualization or the mediated PMU
is unavailable:
arch/x86/kvm/svm/svm.c:svm_set_cpu_caps() {
...
if (!lbrv || !enable_mediated_pmu ||
!kvm_cpu_cap_has(X86_FEATURE_PERFMON_V2) ||
kvm_pmu_cap.num_branches_lbr != SVM_LBR_V2_STACK_SIZE)
kvm_cpu_cap_clear(X86_FEATURE_AMD_LBR_V2);
}
...
if (!lbrv)
kvm_cpu_cap_clear(X86_FEATURE_BUS_LOCK_DETECT);
}
Without similar gating for X86_FEATURE_AMD_LBR_PMC_FREEZE:
1. When !lbrv, MSR_IA32_DEBUGCTLMSR is not virtualized in svm_set_msr(),
which ignores writes via kvm_pr_unimpl_wrmsr(), leaving the advertised
feature non-functional:
arch/x86/kvm/svm/svm.c:svm_set_msr() {
case MSR_IA32_DEBUGCTLMSR:
if (!lbrv) {
kvm_pr_unimpl_wrmsr(vcpu, ecx, data);
break;
}
}
2. When lbrv is enabled but !enable_mediated_pmu (or when
!kvm_vcpu_has_mediated_pmu(vcpu)), the perf_event-backed vPMU does not
emulate freeze-on-PMI in __kvm_perf_overflow(). Because
svm_get_supported_debugctl() still permits setting
DEBUGCTLMSR_FREEZE_LBRS_ON_PMI and DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI in
svm->vmcb->save.dbgctl, those bits are loaded into the physical
MSR_IA32_DEBUGCTLMSR while the hardware PMCs and LBRv2 remain owned by
host perf, causing host PMIs during guest execution to freeze host PMCs
and LBRs (and trigger the reserved PerfCntrGlobalStatus warning in
amd_pmu_v2_handle_irq() if bit 59 is set).
[ ... ]
> diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
> index 01be2fc05d701..9b65728ee0d2f 100644
> --- a/arch/x86/kvm/svm/svm.h
> +++ b/arch/x86/kvm/svm/svm.h
> @@ -801,6 +801,10 @@ static inline u64 svm_get_supported_debugctl(struct kvm_vcpu *vcpu)
> if (guest_cpu_cap_has(vcpu, X86_FEATURE_BUS_LOCK_DETECT))
> debugctl |= DEBUGCTLMSR_BUS_LOCK_DETECT;
>
> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_PMC_FREEZE))
> + debugctl |= DEBUGCTLMSR_FREEZE_LBRS_ON_PMI |
> + DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI;
> +
> return debugctl;
> }
[Severity: High]
When X86_FEATURE_AMD_LBR_PMC_FREEZE is exposed to the guest, does
amd_pmu_refresh() also need to clear the freeze status bits in
pmu->global_status_rsvd?
When DEBUGCTLMSR_FREEZE_LBRS_ON_PMI is enabled and a PMC overflows in the
guest, hardware sets GLOBAL_STATUS_LBRS_FROZEN (bit 58) in
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, which amd_mediated_pmu_put() saves into
pmu->global_status:
arch/x86/kvm/svm/pmu.c:amd_mediated_pmu_put() {
...
wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0);
rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, pmu->global_status);
}
However, amd_pmu_refresh() leaves all bits above nr_arch_gp_counters marked
as reserved in pmu->global_status_rsvd:
arch/x86/kvm/svm/pmu.c:amd_pmu_refresh() {
...
if (pmu->version > 1) {
pmu->global_ctrl_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1);
pmu->global_status_rsvd = pmu->global_ctrl_rsvd;
}
}
Won't kvm_pmu_set_msr() then reject host-initiated KVM_SET_MSRS writes to
MSR_AMD64_PERF_CNTR_GLOBAL_STATUS during live migration (and strip the freeze
bits on intercepted writes to MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET) whenever
GLOBAL_STATUS_LBRS_FROZEN (or GLOBAL_STATUS_COUNTERS_FROZEN) is set?
arch/x86/kvm/pmu.c:kvm_pmu_set_msr() {
case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS:
/* Per PPR, Read-only MSR. Writes are ignored. */
if (!msr_info->host_initiated)
break;
if (data & pmu->global_status_rsvd)
return 1;
pmu->global_status = data;
break;
}
[Severity: High]
Can the hardware-cleared MSR_AMD64_PERF_CNTR_GLOBAL_CTL state on PMC
freeze-on-PMI be lost across a VM-Exit when MSR_AMD64_PERF_CNTR_GLOBAL_CTL
is write-intercepted?
When DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI is set and a counter overflows,
hardware clears the PerfCntrEn bits in MSR_AMD64_PERF_CNTR_GLOBAL_CTL
directly without a guest WRMSR.
On VM-Exit, svm_vcpu_run() only saves MSR_AMD64_PERF_CNTR_GLOBAL_CTL when
writes to the MSR are not intercepted, and amd_mediated_pmu_put() then zeroes
the hardware MSR:
arch/x86/kvm/svm/svm.c:svm_vcpu_run() {
...
if (kvm_vcpu_has_mediated_pmu(vcpu)) {
if (!msr_write_intercepted(svm, MSR_AMD64_PERF_CNTR_GLOBAL_CTL))
rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL,
vcpu_to_pmu(vcpu)->global_ctrl);
...
}
}
If MSR_AMD64_PERF_CNTR_GLOBAL_CTL is write-intercepted (for example when
kvm_need_perf_global_ctrl_intercept(vcpu) is true because guest
nr_arch_gp_counters differs from host num_counters_gp, or when L1 intercepts
the MSR for L2), won't KVM miss the hardware-cleared value and reload the
stale pre-overflow pmu->global_ctrl on the next amd_mediated_pmu_load(),
unfreezing the guest's counters?
[Severity: High]
Does svm_need_lbr_virtualization() also need to check for
DEBUGCTLMSR_FREEZE_LBRS_ON_PMI and DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI?
Currently svm_need_lbr_virtualization() only checks for DEBUGCTLMSR_LBR,
DEBUGCTLMSR_BUS_LOCK_DETECT, and svm_lbrv2_active():
arch/x86/kvm/svm/svm.c:svm_need_lbr_virtualization() {
struct vcpu_svm *svm = to_svm(vcpu);
if (svm->vmcb->save.dbgctl & (DEBUGCTLMSR_LBR | DEBUGCTLMSR_BUS_LOCK_DETECT))
return true;
if (svm_lbrv2_active(vcpu))
return true;
return false;
}
If a guest sets DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI without enabling LBRs or
Bus Lock Detect (or sets DEBUGCTLMSR_FREEZE_LBRS_ON_PMI before setting
DBG_EXTN_CFG_LBRV2EN), SVM_MISC2_ENABLE_V_LBR will remain clear in
svm->vmcb->control.misc_ctl2.
When SVM_MISC2_ENABLE_V_LBR is clear, svm_vcpu_run() only updates the
hardware MSR_IA32_DEBUGCTLMSR when vcpu->arch.host_debugctl differs from
svm->vmcb->save.dbgctl:
arch/x86/kvm/svm/svm.c:svm_vcpu_run() {
if (!(svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR) &&
vcpu->arch.host_debugctl != svm->vmcb->save.dbgctl)
update_debugctlmsr(svm->vmcb->save.dbgctl);
}
Because vcpu_enter_guest() captures vcpu->arch.host_debugctl =
get_debugctlmsr() before kvm_mediated_pmu_load() calls
perf_load_guest_context() -> amd_pmu_lbr_disable_all() (which clears
DEBUGCTLMSR_FREEZE_LBRS_ON_PMI in hardware MSR_IA32_DEBUGCTLMSR on the host),
if the guest's svm->vmcb->save.dbgctl matches the host's pre-disable
DEBUGCTLMSR_FREEZE_LBRS_ON_PMI value, won't svm_vcpu_run() skip
update_debugctlmsr() and leave the hardware MSR_IA32_DEBUGCTLMSR cleared?
титься
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019210532.378628-1-shivansh.dhiman@amd.com?part=11
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 09/12] KVM: nSVM: Propagate LBRv2 state across nested transitions
2026-10-19 21:05 ` [PATCH v2 09/12] KVM: nSVM: Propagate LBRv2 state across nested transitions Shivansh Dhiman
@ 2026-10-19 21:31 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-19 21:31 UTC (permalink / raw)
To: Shivansh Dhiman; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] KVM: nSVM: wrong LBRv2 capability checks in svm_copy_vmrun_state() and nested.c
- [High] KVM: nSVM: missing mediated PMU check for LBRv2 in nested transitions
- [High] KVM: nSVM: missing svm_update_lbrv() in svm_set_nested_state()
--
commit a61dcc1b0377ffbac74e374ce3dc16b1683a68e0
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>
KVM: nSVM: Propagate LBRv2 state across nested transitions
This commit adds the svm_copy_lbrs_v2() macro and updates nested SVM
transitions (VMRUN, #VMEXIT, and VMCB state caching/copying) to propagate
LBRv2 state between vmcb01, vmcb02, and vmcb12.
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index af0ef678433d7..b356a091e320c 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -608,6 +608,9 @@ static void __nested_copy_vmcb_save_to_cache(struct vmcb_save_area_cached *to,
> to->g_pat = from->g_pat;
>
> svm_copy_lbrs(to, from);
> +
> + if (kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2))
> + svm_copy_lbrs_v2(to, from);
> }
>
> void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm,
> @@ -819,8 +822,17 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm)
> */
> svm_copy_lbrs(&vmcb02->save, save);
> vmcb02->save.dbgctl &= svm_get_supported_debugctl(vcpu);
> +
> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2)) {
> + svm_copy_lbrs_v2(&vmcb02->save, save);
> + vmcb02->save.dbg_extn_cfg &= ~DBG_EXTN_CFG_RESERVED_BITS;
> + vmcb02->save.lbr_select &= ~LBR_SELECT_RESERVED_BITS;
> + }
> } else {
> svm_copy_lbrs(&vmcb02->save, &vmcb01->save);
> +
> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2))
> + svm_copy_lbrs_v2(&vmcb02->save, &vmcb01->save);
> }
> vmcb_mark_dirty(vmcb02, VMCB_LBR);
> svm_update_lbrv(&svm->vcpu);
[Severity: High]
Should the checks in nested_vmcb02_prepare_save() (as well as
nested_svm_vmexit_update_vmcb12() and nested_svm_vmexit() below) use
svm_lbrv2_supported(vcpu) instead of only checking
guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2)?
In arch/x86/kvm/svm/svm.c, per-vCPU LBRv2 support is gated by
svm_lbrv2_supported(vcpu), which requires both X86_FEATURE_AMD_LBR_V2 and
kvm_vcpu_has_mediated_pmu(vcpu):
arch/x86/kvm/svm/svm.c:svm_lbrv2_supported() {
return guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2) &&
kvm_vcpu_has_mediated_pmu(vcpu);
}
All L1 MSR accesses (svm_get_msr(), svm_set_msr()) and intercept
calculations (svm_lbrv2_active(), svm_recalc_lbrv2_msr_intercepts()) check
svm_lbrv2_supported(vcpu) because hardware LBRv2 requires the mediated PMU
to schedule out host perf contexts via perf_load_guest_context().
If a VM is configured where guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2)
is true in guest CPUID, but kvm_vcpu_has_mediated_pmu(vcpu) is false (for
example, if the PMU is disabled via KVM_CAP_PMU_CAPABILITY with
KVM_PMU_CAP_DISABLE, or when running without an in-kernel irqchip), L1 can
set SVM_MISC2_ENABLE_V_LBR in vmcb12->control.misc_ctl2 and
DBG_EXTN_CFG_LBRV2EN in vmcb12->save.dbg_extn_cfg and execute VMRUN.
nested_vmcb02_prepare_save() will copy save->dbg_extn_cfg, save->lbr_select,
and save->lbr[] into vmcb02->save, and svm_update_lbrv() will enable
SVM_MISC2_ENABLE_V_LBR in vmcb02->control.misc_ctl2. On VMRUN of vmcb02,
hardware will load vmcb02->save.dbg_extn_cfg with DBG_EXTN_CFG_LBRV2EN set
and record branches into the hardware LBR stack while L2 executes, even
though kvm_mediated_pmu_load() did not call perf_load_guest_context() to
switch out host perf LBRv2 state.
> @@ -1236,6 +1248,12 @@ void svm_copy_vmrun_state(struct kvm_vcpu *vcpu, struct vmcb_save_area *to_save,
> svm_copy_lbrs(to_save, from_save);
> to_save->dbgctl &= svm_get_supported_debugctl(vcpu);
> }
> +
> + if (kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2)) {
> + svm_copy_lbrs_v2(to_save, from_save);
> + to_save->dbg_extn_cfg &= ~DBG_EXTN_CFG_RESERVED_BITS;
> + to_save->lbr_select &= ~LBR_SELECT_RESERVED_BITS;
> + }
> }
[Severity: High]
Can gating svm_copy_vmrun_state() (and __nested_copy_vmcb_save_to_cache()
above) on the host capability kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2)
instead of svm_lbrv2_supported(vcpu) corrupt L1's HSAVE page on SMM entry
and allow L1 to activate hardware LBRv2 when LBRv2 is not enabled for the
vCPU?
When an SMI arrives while a vCPU without LBRv2 enabled is running L2,
svm_enter_smm() calls svm_copy_vmrun_state():
arch/x86/kvm/svm/svm.c:svm_enter_smm() {
...
svm_copy_vmrun_state(vcpu, m_save.map.hva + 0x400, &svm->vmcb01.ptr->save);
...
}
Because svm_copy_vmrun_state() checks
kvm_cpu_cap_has(X86_FEATURE_AMD_LBR_V2), KVM writes 272 bytes (dbg_extn_cfg
at offset 0x698, lbr[16] at 0xa70..0xb6f, and lbr_select at 0xb70) into L1's
MSR_VM_HSAVE_PA page even when LBRv2 is not exposed to the guest.
Conversely, on RSM in svm_leave_smm() or during KVM_SET_NESTED_STATE in
svm_set_nested_state():
arch/x86/kvm/svm/svm.c:svm_leave_smm() {
...
svm_copy_vmrun_state(vcpu, &svm->vmcb01.ptr->save, m_save.map.hva + 0x400);
...
}
svm_copy_vmrun_state() copies dbg_extn_cfg (including DBG_EXTN_CFG_LBRV2EN),
lbr_select, and lbr[16] from the guest-controlled HSAVE page (or userspace
buffer) into vmcb01->save even when !svm_lbrv2_supported(vcpu). On a
subsequent nested #VMEXIT from L2 to L1, nested_svm_vmexit() skips
svm_copy_lbrs_v2(&vmcb01->save, &vmcb02->save) because
guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2) is false, leaving
DBG_EXTN_CFG_LBRV2EN set in vmcb01->save.dbg_extn_cfg. If L1 then enables
SVM_MISC2_ENABLE_V_LBR (for example, by setting DEBUGCTLMSR_LBR in
MSR_IA32_DEBUGCTLMSR), hardware VMRUN will load vmcb01->save.dbg_extn_cfg
into physical hardware.
[Severity: High]
Also, when svm_set_nested_state() calls svm_copy_vmrun_state() to restore
nested state while in guest mode, does svm_set_nested_state() also need a
call to svm_update_lbrv(vcpu)?
During nested state restore in svm_set_nested_state():
arch/x86/kvm/svm/nested.c:svm_set_nested_state() {
...
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);
if (use_separate_l2_pat)
vmcb_set_gpat(svm->vmcb, kvm_state->hdr.svm.gpat);
nested_vmcb02_prepare_control(svm);
...
}
nested_vmcb02_prepare_control() leaves SVM_MISC2_ENABLE_V_LBR to
svm_update_lbrv(), which is normally called at the end of
nested_vmcb02_prepare_save() during VMRUN, but svm_set_nested_state() never
calls nested_vmcb02_prepare_save() or svm_update_lbrv(vcpu).
If userspace restores MSRs via KVM_SET_MSRS before KVM_SET_NESTED_STATE,
svm_set_msr() for MSR_AMD_DBG_EXTN_CFG (with DBG_EXTN_CFG_LBRV2EN) or
MSR_IA32_DEBUGCTLMSR (with DEBUGCTLMSR_LBR) runs while svm->vmcb points to
vmcb01. svm_update_lbrv() sets SVM_MISC2_ENABLE_V_LBR in
vmcb01->control.misc_ctl2 and disables interception of the LBR/LBRv2 MSRs in
svm->msrpm. When svm_set_nested_state() then moves vmcb01->save to
vmcb02->save and switches to vmcb02 without calling svm_update_lbrv(vcpu),
vmcb02->control.misc_ctl2 is left with SVM_MISC2_ENABLE_V_LBR cleared while
the LBR/LBRv2 MSRs remain unintercepted in msrpm01/msrpm02.
Even if KVM_SET_NESTED_STATE is called before KVM_SET_MSRS, if L1 enabled
SVM_MISC2_ENABLE_V_LBR in vmcb12 while L2's dbgctl and dbg_extn_cfg are 0,
svm_set_msr() breaks out early because the save area values are already 0,
leaving SVM_MISC2_ENABLE_V_LBR cleared in vmcb02->control.misc_ctl2.
[ ... ]
> @@ -1305,9 +1323,13 @@ static int nested_svm_vmexit_update_vmcb12(struct kvm_vcpu *vcpu)
> if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS))
> vmcb12->control.next_rip = vmcb02->control.next_rip;
>
> - if (svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR)
> + if (svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR) {
> svm_copy_lbrs(&vmcb12->save, &vmcb02->save);
>
> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2))
> + svm_copy_lbrs_v2(&vmcb12->save, &vmcb02->save);
> + }
> +
> vmcb12->control.event_inj = 0;
> vmcb12->control.event_inj_err = 0;
> vmcb12->control.int_ctl = svm->nested.ctl.int_ctl;
> @@ -1384,6 +1406,10 @@ void nested_svm_vmexit(struct vcpu_svm *svm)
>
> if (!(svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR)) {
> svm_copy_lbrs(&vmcb01->save, &vmcb02->save);
> +
> + if (guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_LBR_V2))
> + svm_copy_lbrs_v2(&vmcb01->save, &vmcb02->save);
> +
> vmcb_mark_dirty(vmcb01, VMCB_LBR);
> }
>
[ ... ]
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019210532.378628-1-shivansh.dhiman@amd.com?part=9
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 04/12] KVM: SVM: Enable hardware-assisted LBRv2 virtualization
2026-10-19 21:05 ` [PATCH v2 04/12] KVM: SVM: Enable hardware-assisted LBRv2 virtualization Shivansh Dhiman
@ 2026-10-19 21:33 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-19 21:33 UTC (permalink / raw)
To: Shivansh Dhiman; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] KVM: SVM: Missing VMCB_LBR dirty mark when toggling V_LBR in svm_update_lbrv()
--
commit 46b5fce40fe2fd2edafbeb3ed6f81f7158c27cba
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>
KVM: SVM: Enable hardware-assisted LBRv2 virtualization
This commit enables V_LBR when LBRv2 is active in the guest and passes
the LBRv2 MSRs through to the guest by disabling their intercepts while
branch recording is enabled.
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index 428c95d7ef21b..69f9ea746bb6f 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
[ ... ]
> @@ -897,6 +935,9 @@ static bool svm_need_lbr_virtualization(struct kvm_vcpu *vcpu)
> if (svm->vmcb->save.dbgctl & (DEBUGCTLMSR_LBR | DEBUGCTLMSR_BUS_LOCK_DETECT))
> return true;
>
> + if (svm_lbrv2_active(vcpu))
> + return true;
> +
> return is_guest_mode(vcpu) &&
> (svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR);
> }
> @@ -919,6 +960,7 @@ void svm_update_lbrv(struct kvm_vcpu *vcpu)
> * do, so always recalculate the intercepts here.
> */
> svm_recalc_lbr_msr_intercepts(vcpu);
> + svm_recalc_lbrv2_msr_intercepts(vcpu);
> }
[Severity: High]
When MSR_AMD_DBG_EXTN_CFG is written and toggles DBG_EXTN_CFG_LBRV2EN,
svm_set_msr() calls svm_update_lbrv():
arch/x86/kvm/svm/svm.c:svm_set_msr() {
...
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;
...
}
In svm_update_lbrv(), __svm_enable_lbrv() or __svm_disable_lbrv() toggles
SVM_MISC2_ENABLE_V_LBR in svm->vmcb->control.misc_ctl2, and
svm_recalc_lbr_msr_intercepts() updates the legacy LBR MSR intercepts:
arch/x86/kvm/svm/svm.c:svm_update_lbrv() {
...
if (enable_lbrv && !current_enable_lbrv)
__svm_enable_lbrv(vcpu);
else if (!enable_lbrv && current_enable_lbrv)
__svm_disable_lbrv(vcpu);
...
svm_recalc_lbr_msr_intercepts(vcpu);
svm_recalc_lbrv2_msr_intercepts(vcpu);
}
Per the AMD APM, VMCB clean bit 10 (VMCB_LBR) covers the LBR virtualization
enable bit (SVM_MISC2_ENABLE_V_LBR) in addition to DBGCTL and the legacy LBR
MSRs. Unlike the MSR_IA32_DEBUGCTLMSR path in svm_set_msr(), neither the
MSR_AMD_DBG_EXTN_CFG handler nor __svm_enable_lbrv() / __svm_disable_lbrv()
marks VMCB_LBR dirty.
After a previous VMRUN has marked VMCB_LBR clean via vmcb_mark_all_clean()
in svm_vcpu_run(), can toggling DBG_EXTN_CFG_LBRV2EN leave VMCB_LBR clean in
svm->vmcb->control.clean, causing the CPU to use a stale cached value for
SVM_MISC2_ENABLE_V_LBR and DBGCTL on VMRUN while KVM stops manually
switching DEBUGCTL and disables the LBR MSR intercepts?
Should __svm_enable_lbrv() and __svm_disable_lbrv() call
vmcb_mark_dirty(svm->vmcb, VMCB_LBR)?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019210532.378628-1-shivansh.dhiman@amd.com?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 10/12] KVM: nSVM: Pass through LBRv2 MSRs to L2
2026-10-19 21:05 ` [PATCH v2 10/12] KVM: nSVM: Pass through LBRv2 MSRs to L2 Shivansh Dhiman
@ 2026-10-19 21:36 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-19 21:36 UTC (permalink / raw)
To: Shivansh Dhiman; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] KVM: nSVM: missing msrpm02 recalc in svm_update_lbrv() for LBRv2 MSRs
--
commit 97c65be26df9b6afb7c41700b0bfb2f3401bc714
Author: Shivansh Dhiman <shivansh.dhiman@amd.com>
KVM: nSVM: Pass through LBRv2 MSRs to L2
This commit adds the LBRv2 MSRs to merge_msrs[] in
nested_svm_init_msrpm_merge_offsets() so their MSRPM bits are merged into
msrpm02, allowing L2 to access them directly without taking a VM-Exit.
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index b356a091e320c..84154f353935c 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -209,7 +209,7 @@ void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
> * Hardcode the capacity of the array based on the maximum number of _offsets_.
> * MSRs are batched together, so there are fewer offsets than MSRs.
> */
> -static int nested_svm_msrpm_merge_offsets[10] __ro_after_init;
> +static int nested_svm_msrpm_merge_offsets[12] __ro_after_init;
> static int nested_svm_nr_msrpm_merge_offsets __ro_after_init;
> typedef unsigned long nsvm_msrpm_merge_t;
>
> @@ -253,6 +253,27 @@ int __init nested_svm_init_msrpm_merge_offsets(void)
> MSR_AMD64_PERF_CNTR_GLOBAL_STATUS,
> MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR,
> MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET,
> +
> + MSR_AMD_DBG_EXTN_CFG,
> + MSR_AMD64_LBR_SELECT,
> +
> + /* This part of MSRs should match SVM_LBR_V2_STACK_SIZE * 2. */
> + MSR_AMD_SAMP_BR_FROM + 0, MSR_AMD_SAMP_BR_FROM + 1,
> + MSR_AMD_SAMP_BR_FROM + 2, MSR_AMD_SAMP_BR_FROM + 3,
> + MSR_AMD_SAMP_BR_FROM + 4, MSR_AMD_SAMP_BR_FROM + 5,
> + MSR_AMD_SAMP_BR_FROM + 6, MSR_AMD_SAMP_BR_FROM + 7,
> + MSR_AMD_SAMP_BR_FROM + 8, MSR_AMD_SAMP_BR_FROM + 9,
> + MSR_AMD_SAMP_BR_FROM + 10, MSR_AMD_SAMP_BR_FROM + 11,
> + MSR_AMD_SAMP_BR_FROM + 12, MSR_AMD_SAMP_BR_FROM + 13,
> + MSR_AMD_SAMP_BR_FROM + 14, MSR_AMD_SAMP_BR_FROM + 15,
> + MSR_AMD_SAMP_BR_FROM + 16, MSR_AMD_SAMP_BR_FROM + 17,
> + MSR_AMD_SAMP_BR_FROM + 18, MSR_AMD_SAMP_BR_FROM + 19,
> + MSR_AMD_SAMP_BR_FROM + 20, MSR_AMD_SAMP_BR_FROM + 21,
> + MSR_AMD_SAMP_BR_FROM + 22, MSR_AMD_SAMP_BR_FROM + 23,
> + MSR_AMD_SAMP_BR_FROM + 24, MSR_AMD_SAMP_BR_FROM + 25,
> + MSR_AMD_SAMP_BR_FROM + 26, MSR_AMD_SAMP_BR_FROM + 27,
> + MSR_AMD_SAMP_BR_FROM + 28, MSR_AMD_SAMP_BR_FROM + 29,
> + MSR_AMD_SAMP_BR_FROM + 30, MSR_AMD_SAMP_BR_FROM + 31,
[Severity: High]
Can this leave stale LBRv2 intercept bits in msrpm02 when L2 toggles
DBG_EXTN_CFG_LBRV2EN without exiting to L1, exposing the host's hardware
LBRv2 registers to L2?
Suppose L1 runs L2 with INTERCEPT_MSR_PROT enabled in vmcb12,
SVM_MISC2_ENABLE_V_LBR disabled in vmcb12->control.misc_ctl2, and the LBRv2
MSRs un-intercepted in vmcb12's MSRPM, while DBG_EXTN_CFG_LBRV2EN is set in
dbg_extn_cfg at nested VMRUN.
During nested_svm_vmrun(), nested_vmcb02_prepare_save() calls
svm_update_lbrv(), which sees svm_lbrv2_active() as true, sets
SVM_MISC2_ENABLE_V_LBR in vmcb02->control.misc_ctl2, and clears the
intercept bits for MSR_AMD64_LBR_SELECT and MSR_AMD_SAMP_BR_FROM + 0..31 in
svm->msrpm (msrpm01) via svm_recalc_lbrv2_msr_intercepts(). Then
nested_svm_merge_msrpm() merges msrpm01 and vmcb12's MSRPM into
svm->nested.msrpm (msrpm02), clearing those intercept bits in msrpm02.
When L2 subsequently writes 0 to MSR_AMD_DBG_EXTN_CFG (which remains
intercepted in msrpm01 for non-SEV-ES guests), hardware exits to L0 and
restores L0's host LBRv2 registers. Because L1 does not intercept
MSR_AMD_DBG_EXTN_CFG in vmcb12, nested_svm_exit_handled_msr() returns
NESTED_EXIT_HOST and L0 handles the write in svm_set_msr():
arch/x86/kvm/svm/svm.c:svm_set_msr() {
...
case MSR_AMD_DBG_EXTN_CFG:
...
svm->vmcb->save.dbg_extn_cfg = data;
svm_update_lbrv(vcpu);
break;
}
svm_update_lbrv() disables LBR virtualization in vmcb02 (clearing
SVM_MISC2_ENABLE_V_LBR) and calls svm_recalc_lbrv2_msr_intercepts():
arch/x86/kvm/svm/svm.c:svm_recalc_lbrv2_msr_intercepts() {
...
for (i = 0; i < 32; i++)
svm_set_intercept_for_msr(vcpu, MSR_AMD_SAMP_BR_FROM + i, MSR_TYPE_RW, intercept);
svm_set_intercept_for_msr(vcpu, MSR_AMD64_LBR_SELECT, MSR_TYPE_RW, intercept);
}
This re-enables the intercepts in svm->msrpm (msrpm01) and sets
svm->nested.force_msr_bitmap_recalc = true, but nested_svm_merge_msrpm() is
only invoked during nested_svm_vmrun() and svm_get_nested_state_pages()
(via KVM_REQ_GET_NESTED_STATE_PAGES).
Because msrpm01 is not re-merged into msrpm02 before re-entering L2, L0
resumes L2 with SVM_MISC2_ENABLE_V_LBR cleared in vmcb02 while msrpm02 still
has the intercept bits cleared for MSR_AMD64_LBR_SELECT and
MSR_AMD_SAMP_BR_FROM + 0..31, allowing L2 to directly read and write the L0
host's hardware LBRv2 MSRs.
Conversely, if L1 enters L2 with dbg_extn_cfg == 0 and L2 later sets
DBG_EXTN_CFG_LBRV2EN without a nested exit to L1, clearing the intercepts in
msrpm01 is not propagated to msrpm02 until the next nested #VMEXIT and VMRUN.
Should svm_recalc_lbrv2_msr_intercepts() (or svm_set_intercept_for_msr() when
is_guest_mode(vcpu) is true) ensure msrpm02 is updated before re-entering L2?
> };
> int i, j;
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019210532.378628-1-shivansh.dhiman@amd.com?part=10
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 01/12] KVM: SVM: Add VMCB fields for LBRv2 virtualization
2026-10-19 21:05 ` [PATCH v2 01/12] KVM: SVM: Add VMCB fields for LBRv2 virtualization Shivansh Dhiman
@ 2026-10-19 22:09 ` Jim Mattson
0 siblings, 0 replies; 21+ messages in thread
From: Jim Mattson @ 2026-10-19 22:09 UTC (permalink / raw)
To: Shivansh Dhiman
Cc: seanjc, pbonzini, tglx, mingo, peterz, acme, kvm, x86, yosry,
thomas.lendacky, nikunj.dadhania, sandipan.das
On Mon, Oct 19, 2026 at 2:06 PM Shivansh Dhiman <shivansh.dhiman@amd.com> wrote:
>
> From: Sandipan Das <sandipan.das@amd.com>
>
> Define the new VMCB fields that will be used to save and restore the
> state of the following Last Branch Record Extension Version 2 (LbrExtV2)
> related MSRs.
> * Last Branch Stack Select (MSR 0xc000010e)
> * Debug Extension Control (MSR 0xc000010f)
> * LBRv2 Branch FROM (MSRs 0xc0010300..0xc001031e)
> * LBRv2 Branch To (MSRs 0xc0010301..0xc001031f)
>
> Signed-off-by: Sandipan Das <sandipan.das@amd.com>
> Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
> ---
> arch/x86/include/asm/svm.h | 14 +++++++++++---
> 1 file changed, 11 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
> index 83fd49b26a4b..d76844224745 100644
> --- a/arch/x86/include/asm/svm.h
> +++ b/arch/x86/include/asm/svm.h
> @@ -372,8 +372,15 @@ struct vmcb_save_area {
> u64 br_to;
> u64 last_excp_from;
> u64 last_excp_to;
> - u8 reserved_0x298[72];
> + u64 dbg_extn_cfg;
> + u8 reserved_0x2a0[64];
> u64 spec_ctrl; /* Guest version of SPEC_CTRL at 0x2E0 */
> + u8 reserved_0x2e8[904];
> + struct {
> + u64 lbr_stack_from;
> + u64 lbr_stack_to;
> + } __packed lbr[16];
Nit: instead of '16', can we hoist the definition of
SVM_LBR_V2_STACK_SIZE from patch 3 and use that here?
Reviewed-by: Jim Mattson <jmattson@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 02/12] KVM: SVM: Add the LbrExtV2 CPUID feature bit
2026-10-19 21:05 ` [PATCH v2 02/12] KVM: SVM: Add the LbrExtV2 CPUID feature bit Shivansh Dhiman
@ 2026-10-19 22:15 ` Jim Mattson
0 siblings, 0 replies; 21+ messages in thread
From: Jim Mattson @ 2026-10-19 22:15 UTC (permalink / raw)
To: Shivansh Dhiman
Cc: seanjc, pbonzini, tglx, mingo, peterz, acme, kvm, x86, yosry,
thomas.lendacky, nikunj.dadhania, sandipan.das
On Mon, Oct 19, 2026 at 2:06 PM Shivansh Dhiman <shivansh.dhiman@amd.com> wrote:
>
> From: Sandipan Das <sandipan.das@amd.com>
>
> Define the LbrExtV2 KVM feature (CPUID 0x80000022.EAX[1]), so that KVM
> can track it in guest CPUID and advertise it to userspace.
>
> Signed-off-by: Sandipan Das <sandipan.das@amd.com>
> Signed-off-by: Shivansh Dhiman <shivansh.dhiman@amd.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 03/12] KVM: SVM: Emulate guest accesses to LBRv2 MSRs
2026-10-19 21:05 ` [PATCH v2 03/12] KVM: SVM: Emulate guest accesses to LBRv2 MSRs Shivansh Dhiman
@ 2026-10-19 23:26 ` Jim Mattson
0 siblings, 0 replies; 21+ messages in thread
From: Jim Mattson @ 2026-10-19 23:26 UTC (permalink / raw)
To: Shivansh Dhiman
Cc: seanjc, pbonzini, tglx, mingo, peterz, acme, kvm, x86, yosry,
thomas.lendacky, nikunj.dadhania, sandipan.das
On Mon, Oct 19, 2026 at 2:06 PM Shivansh Dhiman <shivansh.dhiman@amd.com> wrote:
>
> 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 <shivansh.dhiman@amd.com>
> ---
>
> 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(+)
>
[ ... ]
> +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;
This guard isn't Spectre V1 safe. Use array_index_nospec().
> +
> + if (offset & 1)
> + return &svm->vmcb->save.lbr[idx].lbr_stack_to;
> + return &svm->vmcb->save.lbr[idx].lbr_stack_from;
> +}
> +
[ ... ]
> *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;
Hardware doesn't raise #GP. It masks the reserved bits off.
> +
> + 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:
What if the guest CPUID.0x80000022:EBX[9:4] declares fewer than 16 LBRs?
> + if (!svm_lbrv2_supported(vcpu))
> + return KVM_MSR_RET_UNSUPPORTED;
> +
> + *svm_vmcb_lbrv2(svm, ecx) = data;
Hardware does some funky things with high bits that should (a) be
documented, and (b) be modeled here.
On a Genoa host, I found:
- For FROM MSRs, bits 62:58 always read back as sign-extension of bit
57, but bit 63 reads back as stored.
- For TO MSRs, bits 60:58 always read back as sign-extension of bit
57, but bits 63:61 read back as stored.
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-10-19 23:26 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 21:05 [PATCH v2 00/12] KVM: SVM: Add support for AMD LBR Extension v2 Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 01/12] KVM: SVM: Add VMCB fields for LBRv2 virtualization Shivansh Dhiman
2026-10-19 22:09 ` Jim Mattson
2026-10-19 21:05 ` [PATCH v2 02/12] KVM: SVM: Add the LbrExtV2 CPUID feature bit Shivansh Dhiman
2026-10-19 22:15 ` Jim Mattson
2026-10-19 21:05 ` [PATCH v2 03/12] KVM: SVM: Emulate guest accesses to LBRv2 MSRs Shivansh Dhiman
2026-10-19 23:26 ` Jim Mattson
2026-10-19 21:05 ` [PATCH v2 04/12] KVM: SVM: Enable hardware-assisted LBRv2 virtualization Shivansh Dhiman
2026-10-19 21:33 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 05/12] KVM: x86: Add support for Live Migration Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 06/12] perf/x86: Expose the LBR stack depth via x86_pmu_capability Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 07/12] KVM: SVM: Advertise LbrExtV2 to userspace Shivansh Dhiman
2026-10-19 21:27 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 08/12] KVM: SVM: Support LBRv2 virtualization for SEV-ES guests Shivansh Dhiman
2026-10-19 21:05 ` [PATCH v2 09/12] KVM: nSVM: Propagate LBRv2 state across nested transitions Shivansh Dhiman
2026-10-19 21:31 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 10/12] KVM: nSVM: Pass through LBRv2 MSRs to L2 Shivansh Dhiman
2026-10-19 21:36 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 11/12] KVM: SVM: Add LBR/PMC freeze support Shivansh Dhiman
2026-10-19 21:27 ` sashiko-bot
2026-10-19 21:05 ` [PATCH v2 12/12] KVM: x86: Use SCATTERED_F() for PERFMON_V2 Shivansh Dhiman
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.