* [kvm-unit-tests PATCH v2 0/4] nVMX: Improve IA32_DEBUGCTLMSR test on debug controls
@ 2026-05-26 3:16 Chenyi Qiang
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test Chenyi Qiang
` (3 more replies)
0 siblings, 4 replies; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-26 3:16 UTC (permalink / raw)
To: kvm; +Cc: Chenyi Qiang, Paolo Bonzini, Sean Christopherson, Xiaoyao Li
Current nested debugctls test focuses on DR7 value and fakes the result of
IA32_DEBUGCTLMSR in comment. Although the test can pass because DR7 is the
only criterion to determine the result, there are some error messages
appearing in dmesg due to accessing to BTF | LBR bits in IA32_DEBUGCTLMSR
with report_ignored_msrs KVM parameter enabled.
Clean up the old test so that it focuses on DR7, then add separate tests for
DEBUGCTLMSR bits that are actually supported and can be validated during VMX
transitions.
Tested on:
Emerald Rapids:
vmx_bus_lock_detect_test PASS
vmx_rtm_test PASS
vmx_legacy_lbr_test SKIP
Skylake:
vmx_bus_lock_detect_test SKIP
vmx_rtm_test PASS
vmx_legacy_lbr_test PASS
Changes since v1:
- Modify the commit message in patch 1 (Xiaoyao)
- Add dedicated tests (for BUS_LOCK_DETECT and LBR) covering the DEBUGCTL
reads and writes during VMX transitions (Sean)
- Add one additional test for RTM_DEBUG bit that was not mentioned in v1.
- https://lore.kernel.org/kvm/20250811063035.12626-1-chenyi.qiang@intel.com/
Chenyi Qiang (4):
nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test
nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX
transitions
nVMX: Validate DEBUGCTLMSR_RTM_DEBUG states during VMX transitions
nVMX: Validate DEBUGCTLMSR_LBR states during VMX transitions
lib/x86/msr.h | 2 +
x86/unittests.cfg | 25 +++++++
x86/vmx_tests.c | 175 +++++++++++++++++++++++++++++++++++++---------
3 files changed, 170 insertions(+), 32 deletions(-)
--
2.43.5
^ permalink raw reply [flat|nested] 15+ messages in thread
* [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test
2026-05-26 3:16 [kvm-unit-tests PATCH v2 0/4] nVMX: Improve IA32_DEBUGCTLMSR test on debug controls Chenyi Qiang
@ 2026-05-26 3:16 ` Chenyi Qiang
2026-05-26 4:39 ` Xiaoyao Li
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions Chenyi Qiang
` (2 subsequent siblings)
3 siblings, 1 reply; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-26 3:16 UTC (permalink / raw)
To: kvm; +Cc: Chenyi Qiang, Paolo Bonzini, Sean Christopherson, Xiaoyao Li
"debug controls" test was added by commit dc5c01f17b1a ("VMX: Test
behavior on set and cleared save/load debug controls") to verify that
"VM-Entry load debug controls" and "VM-Exit save debug controls" are
correctly emulated by KVM for nested VMX. But due to the limitation that
KVM didn't support MSR_IA32_DEBUGCTL for guest at that time, the test
commented out all the value comparison of MSR_IA32_DEBUGCTL and leave
it to future when KVM supports the MSR.
The test doesn't check the functionality of save/restore guest
MSR_IA32_DEBUGCTL on vm-exit/entry, but it keeps the write of
MSR_IA32_DEBUGCTL. It leads to
kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x407de7 Unhandled WRMSR(0x1d9) = 0x1
kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x0 Unhandled WRMSR(0x1d9) = 0x2
kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40936f Unhandled WRMSR(0x1d9) = 0x3
kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40cf09 Unhandled WRMSR(0x1d9) = 0x1
kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40940d Unhandled WRMSR(0x1d9) = 0x3
to the kernel log. Though it doesn't break the test, the log confuses
people to think something wrong happened in the test case.
Current KVM doesn't support some bits of MSR_IA32_DEBUGCTL but they
depend on the vcpu model exposed. To simplify the case and eliminate
the confusing "Unhandled WRMSR(0x1d9)" log, remove the MSR_IA32_DEBUGCTL
logic in the test and make it concentrate only on DR7. Following patch
will bring back MSR_IA32_DEBUGCTL separately.
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
---
Changes in v2:
- Rewrite of commit message by Xiaoyao.
- Add RB from Xiaoyao.
---
x86/vmx_tests.c | 46 ++++++++++++++--------------------------------
1 file changed, 14 insertions(+), 32 deletions(-)
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index ff387ded..91f417c6 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -1856,21 +1856,18 @@ static int nmi_hlt_exit_handler(union exit_reason exit_reason)
}
-static int dbgctls_init(struct vmcs *vmcs)
+static int dbgctls_dr7_init(struct vmcs *vmcs)
{
u64 dr7 = 0x402;
u64 zero = 0;
- msr_bmp_init();
asm volatile(
"mov %0,%%dr0\n\t"
"mov %0,%%dr1\n\t"
"mov %0,%%dr2\n\t"
"mov %1,%%dr7\n\t"
: : "r" (zero), "r" (dr7));
- wrmsr(MSR_IA32_DEBUGCTLMSR, 0x1);
vmcs_write(GUEST_DR7, 0x404);
- vmcs_write(GUEST_DEBUGCTL, 0x2);
vmcs_write(ENT_CONTROLS, vmcs_read(ENT_CONTROLS) | ENT_LOAD_DBGCTLS);
vmcs_write(EXI_CONTROLS, vmcs_read(EXI_CONTROLS) | EXI_SAVE_DBGCTLS);
@@ -1878,23 +1875,19 @@ static int dbgctls_init(struct vmcs *vmcs)
return VMX_TEST_START;
}
-static void dbgctls_main(void)
+static void dbgctls_dr7_main(void)
{
- u64 dr7, debugctl;
+ u64 dr7;
asm volatile("mov %%dr7,%0" : "=r" (dr7));
- debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
- /* Commented out: KVM does not support DEBUGCTL so far */
- (void)debugctl;
- report(dr7 == 0x404, "Load debug controls" /* && debugctl == 0x2 */);
+ report(dr7 == 0x404, "DR7: Load debug controls");
dr7 = 0x408;
asm volatile("mov %0,%%dr7" : : "r" (dr7));
- wrmsr(MSR_IA32_DEBUGCTLMSR, 0x3);
vmx_set_test_stage(0);
vmcall();
- report(vmx_get_test_stage() == 1, "Save debug controls");
+ report(vmx_get_test_stage() == 1, "DR7: Save debug controls");
if (ctrl_enter_rev.set & ENT_LOAD_DBGCTLS ||
ctrl_exit_rev.set & EXI_SAVE_DBGCTLS) {
@@ -1905,46 +1898,37 @@ static void dbgctls_main(void)
vmcall();
asm volatile("mov %%dr7,%0" : "=r" (dr7));
- debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
- /* Commented out: KVM does not support DEBUGCTL so far */
- (void)debugctl;
report(dr7 == 0x402,
- "Guest=host debug controls" /* && debugctl == 0x1 */);
+ "DR7: Guest=host debug controls");
dr7 = 0x408;
asm volatile("mov %0,%%dr7" : : "r" (dr7));
- wrmsr(MSR_IA32_DEBUGCTLMSR, 0x3);
vmx_set_test_stage(3);
vmcall();
- report(vmx_get_test_stage() == 4, "Don't save debug controls");
+ report(vmx_get_test_stage() == 4, "DR7: Don't save debug controls");
}
-static int dbgctls_exit_handler(union exit_reason exit_reason)
+static int dbgctls_dr7_exit_handler(union exit_reason exit_reason)
{
u32 insn_len = vmcs_read(EXI_INST_LEN);
u64 guest_rip = vmcs_read(GUEST_RIP);
- u64 dr7, debugctl;
+ u64 dr7;
asm volatile("mov %%dr7,%0" : "=r" (dr7));
- debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
switch (exit_reason.basic) {
case VMX_VMCALL:
switch (vmx_get_test_stage()) {
case 0:
- if (dr7 == 0x400 && debugctl == 0 &&
- vmcs_read(GUEST_DR7) == 0x408 /* &&
- Commented out: KVM does not support DEBUGCTL so far
- vmcs_read(GUEST_DEBUGCTL) == 0x3 */)
+ if (dr7 == 0x400 &&
+ vmcs_read(GUEST_DR7) == 0x408)
vmx_inc_test_stage();
break;
case 2:
dr7 = 0x402;
asm volatile("mov %0,%%dr7" : : "r" (dr7));
- wrmsr(MSR_IA32_DEBUGCTLMSR, 0x1);
vmcs_write(GUEST_DR7, 0x404);
- vmcs_write(GUEST_DEBUGCTL, 0x2);
vmcs_write(ENT_CONTROLS,
vmcs_read(ENT_CONTROLS) & ~ENT_LOAD_DBGCTLS);
@@ -1952,10 +1936,8 @@ static int dbgctls_exit_handler(union exit_reason exit_reason)
vmcs_read(EXI_CONTROLS) & ~EXI_SAVE_DBGCTLS);
break;
case 3:
- if (dr7 == 0x400 && debugctl == 0 &&
- vmcs_read(GUEST_DR7) == 0x404 /* &&
- Commented out: KVM does not support DEBUGCTL so far
- vmcs_read(GUEST_DEBUGCTL) == 0x2 */)
+ if (dr7 == 0x400 &&
+ vmcs_read(GUEST_DR7) == 0x404)
vmx_inc_test_stage();
break;
}
@@ -11780,7 +11762,7 @@ struct vmx_test vmx_tests[] = {
interrupt_exit_handler, NULL, {0} },
{ "nmi_hlt", nmi_hlt_init, nmi_hlt_main,
nmi_hlt_exit_handler, NULL, {0} },
- { "debug controls", dbgctls_init, dbgctls_main, dbgctls_exit_handler,
+ { "debug controls dr7", dbgctls_dr7_init, dbgctls_dr7_main, dbgctls_dr7_exit_handler,
NULL, {0} },
{ "MSR switch", msr_switch_init, msr_switch_main,
msr_switch_exit_handler, NULL, {0}, msr_switch_entry_failure },
--
2.43.5
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions
2026-05-26 3:16 [kvm-unit-tests PATCH v2 0/4] nVMX: Improve IA32_DEBUGCTLMSR test on debug controls Chenyi Qiang
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test Chenyi Qiang
@ 2026-05-26 3:16 ` Chenyi Qiang
2026-05-26 12:32 ` Xiaoyao Li
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 3/4] nVMX: Validate DEBUGCTLMSR_RTM_DEBUG " Chenyi Qiang
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 4/4] nVMX: Validate DEBUGCTLMSR_LBR " Chenyi Qiang
3 siblings, 1 reply; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-26 3:16 UTC (permalink / raw)
To: kvm; +Cc: Chenyi Qiang, Paolo Bonzini, Sean Christopherson, Xiaoyao Li
Add tests to verify that DEBUGCTLMSR_BUS_LOCK_DETECT state is correctly
handled during VMX transitions.
Wrap the VM-entry/VM-exit DEBUGCTL transition flow into a shared helper so
follow-up DEBUGCTL feature tests can reuse the same checks.
The following behaviors are verified in the helper:
1. Guest states are loaded from VMCS iff "Load debug controls" VM-Entry control is set.
2. Guest states are saved to VMCS iff "Save debug controls" VM-Exit control is set.
3. DEBUGCTL MSR is cleared after VM exits.
Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
---
lib/x86/msr.h | 1 +
x86/unittests.cfg | 8 ++++
x86/vmx_tests.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 105 insertions(+)
diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index 7397809c..da75740d 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -103,6 +103,7 @@
/* DEBUGCTLMSR bits (others vary by model): */
#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */
#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */
+#define DEBUGCTLMSR_BUS_LOCK_DETECT (1UL << 2)
#define DEBUGCTLMSR_TR (1UL << 6)
#define DEBUGCTLMSR_BTS (1UL << 7)
#define DEBUGCTLMSR_BTINT (1UL << 8)
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 48835eba..c8426770 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -471,6 +471,14 @@ arch = x86_64
groups = vmx
timeout = 240
+[vmx_bus_lock_detect_test]
+file = vmx.flat
+test_args = "vmx_bus_lock_detect_test"
+qemu_params = -cpu max,+vmx
+arch = x86_64
+groups = vmx
+timeout = 240
+
[debug]
file = debug.flat
arch = x86_64
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 91f417c6..a3e52d3b 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -11737,6 +11737,100 @@ static void vmx_cet_test(void)
test_set_guest_finished();
}
+static struct vmx_debugctl_test_data {
+ u64 mask;
+ u64 guest_debugctl;
+} vmx_debugctl_test_data;
+
+static void vmx_debugctl_test_guest(void)
+{
+ vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+ vmcall();
+
+ wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
+ vmcall();
+
+ vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+ vmcall();
+
+ wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
+ vmcall();
+}
+
+static void vmx_debugctl_transition_test(const char *label, u64 mask)
+{
+ u64 debugctl;
+
+ vmx_debugctl_test_data.mask = mask;
+
+ msr_bmp_init();
+ wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
+ vmcs_write(GUEST_DEBUGCTL, 0x0);
+
+ /* Validate when save/load debug controls are set */
+ vmcs_set_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
+ vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
+
+ test_set_guest(vmx_debugctl_test_guest);
+
+ enter_guest();
+ skip_exit_vmcall();
+ /* Validate VM-entry loads the guest DEBUGCTL value from GUEST_DEBUGCTL. */
+ report(vmx_debugctl_test_data.guest_debugctl == 0x0,
+ "%s: Load debug controls", label);
+
+ enter_guest();
+ skip_exit_vmcall();
+ debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+ /* Validate VM-exit clears host DEBUGCTL and saves guest DEBUGCTL into the VMCS. */
+ report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == mask,
+ "%s: Save debug controls", label);
+
+ if (ctrl_enter_rev.set & ENT_LOAD_DBGCTLS ||
+ ctrl_exit_rev.set & EXI_SAVE_DBGCTLS) {
+ printf("\tDebug controls are always loaded/saved\n");
+ test_set_guest_finished();
+ return;
+ }
+
+ wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
+ vmcs_write(GUEST_DEBUGCTL, 0x0);
+ /* Validate when save/load debug controls are clear */
+ vmcs_clear_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
+ vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
+
+ enter_guest();
+ skip_exit_vmcall();
+ /* Validate guest observes the host's DEBUGCTL value. */
+ report(vmx_debugctl_test_data.guest_debugctl == mask,
+ "%s: Guest=host debug controls", label);
+
+ wrmsr(MSR_IA32_DEBUGCTLMSR, 0x0);
+ vmcs_write(GUEST_DEBUGCTL, 0x0);
+
+ enter_guest();
+ skip_exit_vmcall();
+ debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+ /* Validate VM-exit clears host DEBUGCTL and does not save guest DEBUGCTL into the VMCS. */
+ report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == 0x0,
+ "%s: Don't save debug controls", label);
+
+ enter_guest();
+}
+
+static void vmx_bus_lock_detect_test(void)
+{
+
+ if (!this_cpu_has(X86_FEATURE_BUS_LOCK_DETECT)) {
+ report_skip("%s : \"Bus Lock Detect\" not supported", __func__);
+ return;
+ }
+
+ vmx_debugctl_transition_test("DEBUGCTLMSR.BUS_LOCK_DETECT",
+ DEBUGCTLMSR_BUS_LOCK_DETECT);
+ report_pass(__func__);
+}
+
#define TEST(name) { #name, .v2 = name }
/* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
@@ -11858,5 +11952,7 @@ struct vmx_test vmx_tests[] = {
TEST(vmx_canonical_test),
/* "Load CET" VM-entry/exit controls tests. */
TEST(vmx_cet_test),
+ /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_BUS_LOCK_DETECT */
+ TEST(vmx_bus_lock_detect_test),
{ NULL, NULL, NULL, NULL, NULL, {0} },
};
--
2.43.5
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [kvm-unit-tests PATCH v2 3/4] nVMX: Validate DEBUGCTLMSR_RTM_DEBUG states during VMX transitions
2026-05-26 3:16 [kvm-unit-tests PATCH v2 0/4] nVMX: Improve IA32_DEBUGCTLMSR test on debug controls Chenyi Qiang
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test Chenyi Qiang
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions Chenyi Qiang
@ 2026-05-26 3:16 ` Chenyi Qiang
2026-05-26 12:33 ` Xiaoyao Li
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 4/4] nVMX: Validate DEBUGCTLMSR_LBR " Chenyi Qiang
3 siblings, 1 reply; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-26 3:16 UTC (permalink / raw)
To: kvm; +Cc: Chenyi Qiang, Paolo Bonzini, Sean Christopherson, Xiaoyao Li
Add tests to verify that DEBUGCTLMSR_RTM_DEBUG state is correctly
handled during VMX transitions on top of the shared helper.
Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
---
lib/x86/msr.h | 1 +
x86/unittests.cfg | 8 ++++++++
x86/vmx_tests.c | 14 ++++++++++++++
3 files changed, 23 insertions(+)
diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index da75740d..8b019f7e 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -110,6 +110,7 @@
#define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9)
#define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10)
#define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11)
+#define DEBUGCTLMSR_RTM_DEBUG (1UL << 15)
#define MSR_LBR_NHM_FROM 0x00000680
#define MSR_LBR_NHM_TO 0x000006c0
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index c8426770..78c15e2b 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -479,6 +479,14 @@ arch = x86_64
groups = vmx
timeout = 240
+[vmx_rtm_test]
+file = vmx.flat
+test_args = "vmx_rtm_test"
+qemu_params = -cpu max,+vmx
+arch = x86_64
+groups = vmx
+timeout = 240
+
[debug]
file = debug.flat
arch = x86_64
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index a3e52d3b..934d6021 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -11831,6 +11831,18 @@ static void vmx_bus_lock_detect_test(void)
report_pass(__func__);
}
+static void vmx_rtm_test(void)
+{
+ if (!this_cpu_has(X86_FEATURE_RTM)) {
+ report_skip("%s : \"RTM\" not supported", __func__);
+ return;
+ }
+
+ vmx_debugctl_transition_test("DEBUGCTLMSR.RTM_DEBUG",
+ DEBUGCTLMSR_RTM_DEBUG);
+ report_pass(__func__);
+}
+
#define TEST(name) { #name, .v2 = name }
/* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
@@ -11954,5 +11966,7 @@ struct vmx_test vmx_tests[] = {
TEST(vmx_cet_test),
/* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_BUS_LOCK_DETECT */
TEST(vmx_bus_lock_detect_test),
+ /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_RTM_DEBUG */
+ TEST(vmx_rtm_test),
{ NULL, NULL, NULL, NULL, NULL, {0} },
};
--
2.43.5
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [kvm-unit-tests PATCH v2 4/4] nVMX: Validate DEBUGCTLMSR_LBR states during VMX transitions
2026-05-26 3:16 [kvm-unit-tests PATCH v2 0/4] nVMX: Improve IA32_DEBUGCTLMSR test on debug controls Chenyi Qiang
` (2 preceding siblings ...)
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 3/4] nVMX: Validate DEBUGCTLMSR_RTM_DEBUG " Chenyi Qiang
@ 2026-05-26 3:16 ` Chenyi Qiang
2026-05-26 12:34 ` Xiaoyao Li
3 siblings, 1 reply; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-26 3:16 UTC (permalink / raw)
To: kvm; +Cc: Chenyi Qiang, Paolo Bonzini, Sean Christopherson, Xiaoyao Li
Add tests to verify that DEBUGCTLMSR_LBR state is correctly
handled during VMX transitions on top of the shared helper.
Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
---
x86/unittests.cfg | 9 +++++++++
x86/vmx_tests.c | 19 +++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 78c15e2b..d089c86d 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -487,6 +487,15 @@ arch = x86_64
groups = vmx
timeout = 240
+[vmx_legacy_lbr_test]
+file = vmx.flat
+test_args = "vmx_legacy_lbr_test"
+qemu_params = -cpu host,migratable=no,+vmx
+check = /sys/module/kvm/parameters/enable_pmu=Y /sys/module/kvm/parameters/ignore_msrs=N
+arch = x86_64
+groups = vmx
+timeout = 240
+
[debug]
file = debug.flat
arch = x86_64
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 934d6021..80ed541a 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -11843,6 +11843,23 @@ static void vmx_rtm_test(void)
report_pass(__func__);
}
+static void vmx_legacy_lbr_test(void)
+{
+ if (!this_cpu_has(X86_FEATURE_PDCM)) {
+ report_skip("%s : Perfmon/Debug Capabilities MSR not supported",
+ __func__);
+ return;
+ }
+
+ if (!pmu_lbr_version()) {
+ report_skip("%s : LBR not supported", __func__);
+ return;
+ }
+
+ vmx_debugctl_transition_test("DEBUGCTLMSR.LBR", DEBUGCTLMSR_LBR);
+ report_pass(__func__);
+}
+
#define TEST(name) { #name, .v2 = name }
/* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
@@ -11968,5 +11985,7 @@ struct vmx_test vmx_tests[] = {
TEST(vmx_bus_lock_detect_test),
/* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_RTM_DEBUG */
TEST(vmx_rtm_test),
+ /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_LBR */
+ TEST(vmx_legacy_lbr_test),
{ NULL, NULL, NULL, NULL, NULL, {0} },
};
--
2.43.5
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test Chenyi Qiang
@ 2026-05-26 4:39 ` Xiaoyao Li
2026-05-26 4:52 ` Chenyi Qiang
0 siblings, 1 reply; 15+ messages in thread
From: Xiaoyao Li @ 2026-05-26 4:39 UTC (permalink / raw)
To: Chenyi Qiang, kvm; +Cc: Paolo Bonzini, Sean Christopherson
On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
> "debug controls" test was added by commit dc5c01f17b1a ("VMX: Test
> behavior on set and cleared save/load debug controls") to verify that
> "VM-Entry load debug controls" and "VM-Exit save debug controls" are
> correctly emulated by KVM for nested VMX. But due to the limitation that
> KVM didn't support MSR_IA32_DEBUGCTL for guest at that time, the test
> commented out all the value comparison of MSR_IA32_DEBUGCTL and leave
> it to future when KVM supports the MSR.
>
> The test doesn't check the functionality of save/restore guest
> MSR_IA32_DEBUGCTL on vm-exit/entry, but it keeps the write of
> MSR_IA32_DEBUGCTL. It leads to
>
> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x407de7 Unhandled WRMSR(0x1d9) = 0x1
> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x0 Unhandled WRMSR(0x1d9) = 0x2
> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40936f Unhandled WRMSR(0x1d9) = 0x3
> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40cf09 Unhandled WRMSR(0x1d9) = 0x1
> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40940d Unhandled WRMSR(0x1d9) = 0x3
>
> to the kernel log. Though it doesn't break the test, the log confuses
> people to think something wrong happened in the test case.
>
> Current KVM doesn't support some bits of MSR_IA32_DEBUGCTL but they
This sentence doesn't read right because my version used "does support",
not "doesn't support".
> depend on the vcpu model exposed. To simplify the case and eliminate
> the confusing "Unhandled WRMSR(0x1d9)" log, remove the MSR_IA32_DEBUGCTL
> logic in the test and make it concentrate only on DR7. Following patch
> will bring back MSR_IA32_DEBUGCTL separately.
>
> Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
>
> ---
> Changes in v2:
> - Rewrite of commit message by Xiaoyao.
> - Add RB from Xiaoyao.
> ---
> x86/vmx_tests.c | 46 ++++++++++++++--------------------------------
> 1 file changed, 14 insertions(+), 32 deletions(-)
>
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index ff387ded..91f417c6 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -1856,21 +1856,18 @@ static int nmi_hlt_exit_handler(union exit_reason exit_reason)
> }
>
>
> -static int dbgctls_init(struct vmcs *vmcs)
> +static int dbgctls_dr7_init(struct vmcs *vmcs)
> {
> u64 dr7 = 0x402;
> u64 zero = 0;
>
> - msr_bmp_init();
> asm volatile(
> "mov %0,%%dr0\n\t"
> "mov %0,%%dr1\n\t"
> "mov %0,%%dr2\n\t"
> "mov %1,%%dr7\n\t"
> : : "r" (zero), "r" (dr7));
> - wrmsr(MSR_IA32_DEBUGCTLMSR, 0x1);
> vmcs_write(GUEST_DR7, 0x404);
> - vmcs_write(GUEST_DEBUGCTL, 0x2);
>
> vmcs_write(ENT_CONTROLS, vmcs_read(ENT_CONTROLS) | ENT_LOAD_DBGCTLS);
> vmcs_write(EXI_CONTROLS, vmcs_read(EXI_CONTROLS) | EXI_SAVE_DBGCTLS);
> @@ -1878,23 +1875,19 @@ static int dbgctls_init(struct vmcs *vmcs)
> return VMX_TEST_START;
> }
>
> -static void dbgctls_main(void)
> +static void dbgctls_dr7_main(void)
> {
> - u64 dr7, debugctl;
> + u64 dr7;
>
> asm volatile("mov %%dr7,%0" : "=r" (dr7));
> - debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> - /* Commented out: KVM does not support DEBUGCTL so far */
> - (void)debugctl;
> - report(dr7 == 0x404, "Load debug controls" /* && debugctl == 0x2 */);
> + report(dr7 == 0x404, "DR7: Load debug controls");
>
> dr7 = 0x408;
> asm volatile("mov %0,%%dr7" : : "r" (dr7));
> - wrmsr(MSR_IA32_DEBUGCTLMSR, 0x3);
>
> vmx_set_test_stage(0);
> vmcall();
> - report(vmx_get_test_stage() == 1, "Save debug controls");
> + report(vmx_get_test_stage() == 1, "DR7: Save debug controls");
>
> if (ctrl_enter_rev.set & ENT_LOAD_DBGCTLS ||
> ctrl_exit_rev.set & EXI_SAVE_DBGCTLS) {
> @@ -1905,46 +1898,37 @@ static void dbgctls_main(void)
> vmcall();
>
> asm volatile("mov %%dr7,%0" : "=r" (dr7));
> - debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> - /* Commented out: KVM does not support DEBUGCTL so far */
> - (void)debugctl;
> report(dr7 == 0x402,
> - "Guest=host debug controls" /* && debugctl == 0x1 */);
> + "DR7: Guest=host debug controls");
>
> dr7 = 0x408;
> asm volatile("mov %0,%%dr7" : : "r" (dr7));
> - wrmsr(MSR_IA32_DEBUGCTLMSR, 0x3);
>
> vmx_set_test_stage(3);
> vmcall();
> - report(vmx_get_test_stage() == 4, "Don't save debug controls");
> + report(vmx_get_test_stage() == 4, "DR7: Don't save debug controls");
> }
>
> -static int dbgctls_exit_handler(union exit_reason exit_reason)
> +static int dbgctls_dr7_exit_handler(union exit_reason exit_reason)
> {
> u32 insn_len = vmcs_read(EXI_INST_LEN);
> u64 guest_rip = vmcs_read(GUEST_RIP);
> - u64 dr7, debugctl;
> + u64 dr7;
>
> asm volatile("mov %%dr7,%0" : "=r" (dr7));
> - debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
>
> switch (exit_reason.basic) {
> case VMX_VMCALL:
> switch (vmx_get_test_stage()) {
> case 0:
> - if (dr7 == 0x400 && debugctl == 0 &&
> - vmcs_read(GUEST_DR7) == 0x408 /* &&
> - Commented out: KVM does not support DEBUGCTL so far
> - vmcs_read(GUEST_DEBUGCTL) == 0x3 */)
> + if (dr7 == 0x400 &&
> + vmcs_read(GUEST_DR7) == 0x408)
> vmx_inc_test_stage();
> break;
> case 2:
> dr7 = 0x402;
> asm volatile("mov %0,%%dr7" : : "r" (dr7));
> - wrmsr(MSR_IA32_DEBUGCTLMSR, 0x1);
> vmcs_write(GUEST_DR7, 0x404);
> - vmcs_write(GUEST_DEBUGCTL, 0x2);
>
> vmcs_write(ENT_CONTROLS,
> vmcs_read(ENT_CONTROLS) & ~ENT_LOAD_DBGCTLS);
> @@ -1952,10 +1936,8 @@ static int dbgctls_exit_handler(union exit_reason exit_reason)
> vmcs_read(EXI_CONTROLS) & ~EXI_SAVE_DBGCTLS);
> break;
> case 3:
> - if (dr7 == 0x400 && debugctl == 0 &&
> - vmcs_read(GUEST_DR7) == 0x404 /* &&
> - Commented out: KVM does not support DEBUGCTL so far
> - vmcs_read(GUEST_DEBUGCTL) == 0x2 */)
> + if (dr7 == 0x400 &&
> + vmcs_read(GUEST_DR7) == 0x404)
> vmx_inc_test_stage();
> break;
> }
> @@ -11780,7 +11762,7 @@ struct vmx_test vmx_tests[] = {
> interrupt_exit_handler, NULL, {0} },
> { "nmi_hlt", nmi_hlt_init, nmi_hlt_main,
> nmi_hlt_exit_handler, NULL, {0} },
> - { "debug controls", dbgctls_init, dbgctls_main, dbgctls_exit_handler,
> + { "debug controls dr7", dbgctls_dr7_init, dbgctls_dr7_main, dbgctls_dr7_exit_handler,
> NULL, {0} },
> { "MSR switch", msr_switch_init, msr_switch_main,
> msr_switch_exit_handler, NULL, {0}, msr_switch_entry_failure },
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test
2026-05-26 4:39 ` Xiaoyao Li
@ 2026-05-26 4:52 ` Chenyi Qiang
0 siblings, 0 replies; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-26 4:52 UTC (permalink / raw)
To: Xiaoyao Li, kvm; +Cc: Paolo Bonzini, Sean Christopherson
On 5/26/2026 12:39 PM, Xiaoyao Li wrote:
> On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
>> "debug controls" test was added by commit dc5c01f17b1a ("VMX: Test
>> behavior on set and cleared save/load debug controls") to verify that
>> "VM-Entry load debug controls" and "VM-Exit save debug controls" are
>> correctly emulated by KVM for nested VMX. But due to the limitation that
>> KVM didn't support MSR_IA32_DEBUGCTL for guest at that time, the test
>> commented out all the value comparison of MSR_IA32_DEBUGCTL and leave
>> it to future when KVM supports the MSR.
>>
>> The test doesn't check the functionality of save/restore guest
>> MSR_IA32_DEBUGCTL on vm-exit/entry, but it keeps the write of
>> MSR_IA32_DEBUGCTL. It leads to
>>
>> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x407de7 Unhandled WRMSR(0x1d9) = 0x1
>> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x0 Unhandled WRMSR(0x1d9) = 0x2
>> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40936f Unhandled WRMSR(0x1d9) = 0x3
>> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40cf09 Unhandled WRMSR(0x1d9) = 0x1
>> kvm_intel: kvm [18663]: vcpu0, guest rIP: 0x40940d Unhandled WRMSR(0x1d9) = 0x3
>>
>> to the kernel log. Though it doesn't break the test, the log confuses
>> people to think something wrong happened in the test case.
>>
>> Current KVM doesn't support some bits of MSR_IA32_DEBUGCTL but they
>
> This sentence doesn't read right because my version used "does support", not "doesn't support".
Oops, thanks for catching it.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions Chenyi Qiang
@ 2026-05-26 12:32 ` Xiaoyao Li
2026-05-27 1:42 ` Chenyi Qiang
0 siblings, 1 reply; 15+ messages in thread
From: Xiaoyao Li @ 2026-05-26 12:32 UTC (permalink / raw)
To: Chenyi Qiang, kvm; +Cc: Paolo Bonzini, Sean Christopherson
On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
> Add tests to verify that DEBUGCTLMSR_BUS_LOCK_DETECT state is correctly
> handled during VMX transitions.
>
> Wrap the VM-entry/VM-exit DEBUGCTL transition flow into a shared helper so
> follow-up DEBUGCTL feature tests can reuse the same checks.
>
> The following behaviors are verified in the helper:
>
> 1. Guest states are loaded from VMCS iff "Load debug controls" VM-Entry control is set.
> 2. Guest states are saved to VMCS iff "Save debug controls" VM-Exit control is set.
> 3. DEBUGCTL MSR is cleared after VM exits.
>
> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
Though some nits below, the functionality looks good to me.
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
> ---
> lib/x86/msr.h | 1 +
> x86/unittests.cfg | 8 ++++
> x86/vmx_tests.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 105 insertions(+)
>
> diff --git a/lib/x86/msr.h b/lib/x86/msr.h
> index 7397809c..da75740d 100644
> --- a/lib/x86/msr.h
> +++ b/lib/x86/msr.h
> @@ -103,6 +103,7 @@
> /* DEBUGCTLMSR bits (others vary by model): */
> #define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */
> #define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */
> +#define DEBUGCTLMSR_BUS_LOCK_DETECT (1UL << 2)
> #define DEBUGCTLMSR_TR (1UL << 6)
> #define DEBUGCTLMSR_BTS (1UL << 7)
> #define DEBUGCTLMSR_BTINT (1UL << 8)
> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
> index 48835eba..c8426770 100644
> --- a/x86/unittests.cfg
> +++ b/x86/unittests.cfg
> @@ -471,6 +471,14 @@ arch = x86_64
> groups = vmx
> timeout = 240
>
> +[vmx_bus_lock_detect_test]
> +file = vmx.flat
> +test_args = "vmx_bus_lock_detect_test"
> +qemu_params = -cpu max,+vmx
> +arch = x86_64
> +groups = vmx
> +timeout = 240
> +
> [debug]
> file = debug.flat
> arch = x86_64
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index 91f417c6..a3e52d3b 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -11737,6 +11737,100 @@ static void vmx_cet_test(void)
> test_set_guest_finished();
> }
>
> +static struct vmx_debugctl_test_data {
> + u64 mask;
> + u64 guest_debugctl;
> +} vmx_debugctl_test_data;
> +
> +static void vmx_debugctl_test_guest(void)
> +{
> + vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> + vmcall();
> +
> + wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
> + vmcall();
> +
> + vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> + vmcall();
> +
> + wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
> + vmcall();
> +}
> +
> +static void vmx_debugctl_transition_test(const char *label, u64 mask)
> +{
> + u64 debugctl;
> +
> + vmx_debugctl_test_data.mask = mask;
> +
> + msr_bmp_init();
> + wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
> + vmcs_write(GUEST_DEBUGCTL, 0x0);
> +
> + /* Validate when save/load debug controls are set */
> + vmcs_set_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
> + vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
> +
> + test_set_guest(vmx_debugctl_test_guest);
> +
> + enter_guest();
> + skip_exit_vmcall();
> + /* Validate VM-entry loads the guest DEBUGCTL value from GUEST_DEBUGCTL. */
> + report(vmx_debugctl_test_data.guest_debugctl == 0x0,
> + "%s: Load debug controls", label);
> +
> + enter_guest();
> + skip_exit_vmcall();
> + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> + /* Validate VM-exit clears host DEBUGCTL and saves guest DEBUGCTL into the VMCS. */
> + report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == mask,
> + "%s: Save debug controls", label);
> +
> + if (ctrl_enter_rev.set & ENT_LOAD_DBGCTLS ||
> + ctrl_exit_rev.set & EXI_SAVE_DBGCTLS) {
> + printf("\tDebug controls are always loaded/saved\n");
> + test_set_guest_finished();
> + return;
> + }
> +
> + wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
> + vmcs_write(GUEST_DEBUGCTL, 0x0);
> + /* Validate when save/load debug controls are clear */
> + vmcs_clear_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
> + vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
> +
> + enter_guest();
> + skip_exit_vmcall();
> + /* Validate guest observes the host's DEBUGCTL value. */
> + report(vmx_debugctl_test_data.guest_debugctl == mask,
> + "%s: Guest=host debug controls", label);
> +
> + wrmsr(MSR_IA32_DEBUGCTLMSR, 0x0);
> + vmcs_write(GUEST_DEBUGCTL, 0x0);
> +
> + enter_guest();
> + skip_exit_vmcall();
> + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> + /* Validate VM-exit clears host DEBUGCTL and does not save guest DEBUGCTL into the VMCS. */
> + report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == 0x0,
> + "%s: Don't save debug controls", label);
> +
> + enter_guest();
> +}
> +
> +static void vmx_bus_lock_detect_test(void)
the name reads ambiguous to me.
how about debug_controls_debugctrlmsr_bus_lock_detect_test? hmm, seems
too lengthy
> +{
> +
unnecessary new line.
> + if (!this_cpu_has(X86_FEATURE_BUS_LOCK_DETECT)) {
> + report_skip("%s : \"Bus Lock Detect\" not supported", __func__);
> + return;
> + }
> +
> + vmx_debugctl_transition_test("DEBUGCTLMSR.BUS_LOCK_DETECT",
> + DEBUGCTLMSR_BUS_LOCK_DETECT);
> + report_pass(__func__);
> +}
> +
> #define TEST(name) { #name, .v2 = name }
>
> /* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
> @@ -11858,5 +11952,7 @@ struct vmx_test vmx_tests[] = {
> TEST(vmx_canonical_test),
> /* "Load CET" VM-entry/exit controls tests. */
> TEST(vmx_cet_test),
> + /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_BUS_LOCK_DETECT */
> + TEST(vmx_bus_lock_detect_test),
> { NULL, NULL, NULL, NULL, NULL, {0} },
> };
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 3/4] nVMX: Validate DEBUGCTLMSR_RTM_DEBUG states during VMX transitions
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 3/4] nVMX: Validate DEBUGCTLMSR_RTM_DEBUG " Chenyi Qiang
@ 2026-05-26 12:33 ` Xiaoyao Li
0 siblings, 0 replies; 15+ messages in thread
From: Xiaoyao Li @ 2026-05-26 12:33 UTC (permalink / raw)
To: Chenyi Qiang, kvm; +Cc: Paolo Bonzini, Sean Christopherson
On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
> Add tests to verify that DEBUGCTLMSR_RTM_DEBUG state is correctly
> handled during VMX transitions on top of the shared helper.
>
> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
> ---
> lib/x86/msr.h | 1 +
> x86/unittests.cfg | 8 ++++++++
> x86/vmx_tests.c | 14 ++++++++++++++
> 3 files changed, 23 insertions(+)
>
> diff --git a/lib/x86/msr.h b/lib/x86/msr.h
> index da75740d..8b019f7e 100644
> --- a/lib/x86/msr.h
> +++ b/lib/x86/msr.h
> @@ -110,6 +110,7 @@
> #define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9)
> #define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10)
> #define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11)
> +#define DEBUGCTLMSR_RTM_DEBUG (1UL << 15)
>
> #define MSR_LBR_NHM_FROM 0x00000680
> #define MSR_LBR_NHM_TO 0x000006c0
> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
> index c8426770..78c15e2b 100644
> --- a/x86/unittests.cfg
> +++ b/x86/unittests.cfg
> @@ -479,6 +479,14 @@ arch = x86_64
> groups = vmx
> timeout = 240
>
> +[vmx_rtm_test]
> +file = vmx.flat
> +test_args = "vmx_rtm_test"
> +qemu_params = -cpu max,+vmx
> +arch = x86_64
> +groups = vmx
> +timeout = 240
> +
> [debug]
> file = debug.flat
> arch = x86_64
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index a3e52d3b..934d6021 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -11831,6 +11831,18 @@ static void vmx_bus_lock_detect_test(void)
> report_pass(__func__);
> }
>
> +static void vmx_rtm_test(void)
still the name is not perfect to me, otherwise
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
> +{
> + if (!this_cpu_has(X86_FEATURE_RTM)) {
> + report_skip("%s : \"RTM\" not supported", __func__);
> + return;
> + }
> +
> + vmx_debugctl_transition_test("DEBUGCTLMSR.RTM_DEBUG",
> + DEBUGCTLMSR_RTM_DEBUG);
> + report_pass(__func__);
> +}
> +
> #define TEST(name) { #name, .v2 = name }
>
> /* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
> @@ -11954,5 +11966,7 @@ struct vmx_test vmx_tests[] = {
> TEST(vmx_cet_test),
> /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_BUS_LOCK_DETECT */
> TEST(vmx_bus_lock_detect_test),
> + /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_RTM_DEBUG */
> + TEST(vmx_rtm_test),
> { NULL, NULL, NULL, NULL, NULL, {0} },
> };
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 4/4] nVMX: Validate DEBUGCTLMSR_LBR states during VMX transitions
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 4/4] nVMX: Validate DEBUGCTLMSR_LBR " Chenyi Qiang
@ 2026-05-26 12:34 ` Xiaoyao Li
2026-05-27 2:11 ` Chenyi Qiang
0 siblings, 1 reply; 15+ messages in thread
From: Xiaoyao Li @ 2026-05-26 12:34 UTC (permalink / raw)
To: Chenyi Qiang, kvm; +Cc: Paolo Bonzini, Sean Christopherson
On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
> Add tests to verify that DEBUGCTLMSR_LBR state is correctly
> handled during VMX transitions on top of the shared helper.
>
> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
> ---
> x86/unittests.cfg | 9 +++++++++
> x86/vmx_tests.c | 19 +++++++++++++++++++
> 2 files changed, 28 insertions(+)
>
> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
> index 78c15e2b..d089c86d 100644
> --- a/x86/unittests.cfg
> +++ b/x86/unittests.cfg
> @@ -487,6 +487,15 @@ arch = x86_64
> groups = vmx
> timeout = 240
>
> +[vmx_legacy_lbr_test]
> +file = vmx.flat
> +test_args = "vmx_legacy_lbr_test"
> +qemu_params = -cpu host,migratable=no,+vmx
> +check = /sys/module/kvm/parameters/enable_pmu=Y /sys/module/kvm/parameters/ignore_msrs=N
What's the purpose of checking ignore_msrs here? can you elaborate a bit?
> +arch = x86_64
> +groups = vmx
> +timeout = 240
> +
> [debug]
> file = debug.flat
> arch = x86_64
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index 934d6021..80ed541a 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -11843,6 +11843,23 @@ static void vmx_rtm_test(void)
> report_pass(__func__);
> }
>
> +static void vmx_legacy_lbr_test(void)
> +{
> + if (!this_cpu_has(X86_FEATURE_PDCM)) {
> + report_skip("%s : Perfmon/Debug Capabilities MSR not supported",
> + __func__);
> + return;
> + }
> +
> + if (!pmu_lbr_version()) {
> + report_skip("%s : LBR not supported", __func__);
> + return;
> + }
> +
> + vmx_debugctl_transition_test("DEBUGCTLMSR.LBR", DEBUGCTLMSR_LBR);
> + report_pass(__func__);
> +}
> +
> #define TEST(name) { #name, .v2 = name }
>
> /* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
> @@ -11968,5 +11985,7 @@ struct vmx_test vmx_tests[] = {
> TEST(vmx_bus_lock_detect_test),
> /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_RTM_DEBUG */
> TEST(vmx_rtm_test),
> + /* "debug controls VM-entry/exit controls tests" for DEBUGCTLMSR_LBR */
> + TEST(vmx_legacy_lbr_test),
> { NULL, NULL, NULL, NULL, NULL, {0} },
> };
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions
2026-05-26 12:32 ` Xiaoyao Li
@ 2026-05-27 1:42 ` Chenyi Qiang
2026-05-27 14:58 ` Sean Christopherson
0 siblings, 1 reply; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-27 1:42 UTC (permalink / raw)
To: Xiaoyao Li, kvm; +Cc: Paolo Bonzini, Sean Christopherson
On 5/26/2026 8:32 PM, Xiaoyao Li wrote:
> On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
>> Add tests to verify that DEBUGCTLMSR_BUS_LOCK_DETECT state is correctly
>> handled during VMX transitions.
>>
>> Wrap the VM-entry/VM-exit DEBUGCTL transition flow into a shared helper so
>> follow-up DEBUGCTL feature tests can reuse the same checks.
>>
>> The following behaviors are verified in the helper:
>>
>> 1. Guest states are loaded from VMCS iff "Load debug controls" VM-Entry control is set.
>> 2. Guest states are saved to VMCS iff "Save debug controls" VM-Exit control is set.
>> 3. DEBUGCTL MSR is cleared after VM exits.
>>
>> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
>
> Though some nits below, the functionality looks good to me.
>
> Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
>
>> ---
>> lib/x86/msr.h | 1 +
>> x86/unittests.cfg | 8 ++++
>> x86/vmx_tests.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 105 insertions(+)
>>
>> diff --git a/lib/x86/msr.h b/lib/x86/msr.h
>> index 7397809c..da75740d 100644
>> --- a/lib/x86/msr.h
>> +++ b/lib/x86/msr.h
>> @@ -103,6 +103,7 @@
>> /* DEBUGCTLMSR bits (others vary by model): */
>> #define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */
>> #define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */
>> +#define DEBUGCTLMSR_BUS_LOCK_DETECT (1UL << 2)
>> #define DEBUGCTLMSR_TR (1UL << 6)
>> #define DEBUGCTLMSR_BTS (1UL << 7)
>> #define DEBUGCTLMSR_BTINT (1UL << 8)
>> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
>> index 48835eba..c8426770 100644
>> --- a/x86/unittests.cfg
>> +++ b/x86/unittests.cfg
>> @@ -471,6 +471,14 @@ arch = x86_64
>> groups = vmx
>> timeout = 240
>> +[vmx_bus_lock_detect_test]
>> +file = vmx.flat
>> +test_args = "vmx_bus_lock_detect_test"
>> +qemu_params = -cpu max,+vmx
>> +arch = x86_64
>> +groups = vmx
>> +timeout = 240
>> +
>> [debug]
>> file = debug.flat
>> arch = x86_64
>> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
>> index 91f417c6..a3e52d3b 100644
>> --- a/x86/vmx_tests.c
>> +++ b/x86/vmx_tests.c
>> @@ -11737,6 +11737,100 @@ static void vmx_cet_test(void)
>> test_set_guest_finished();
>> }
>> +static struct vmx_debugctl_test_data {
>> + u64 mask;
>> + u64 guest_debugctl;
>> +} vmx_debugctl_test_data;
>> +
>> +static void vmx_debugctl_test_guest(void)
>> +{
>> + vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
>> + vmcall();
>> +
>> + wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
>> + vmcall();
>> +
>> + vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
>> + vmcall();
>> +
>> + wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
>> + vmcall();
>> +}
>> +
>> +static void vmx_debugctl_transition_test(const char *label, u64 mask)
>> +{
>> + u64 debugctl;
>> +
>> + vmx_debugctl_test_data.mask = mask;
>> +
>> + msr_bmp_init();
>> + wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
>> + vmcs_write(GUEST_DEBUGCTL, 0x0);
>> +
>> + /* Validate when save/load debug controls are set */
>> + vmcs_set_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
>> + vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
>> +
>> + test_set_guest(vmx_debugctl_test_guest);
>> +
>> + enter_guest();
>> + skip_exit_vmcall();
>> + /* Validate VM-entry loads the guest DEBUGCTL value from GUEST_DEBUGCTL. */
>> + report(vmx_debugctl_test_data.guest_debugctl == 0x0,
>> + "%s: Load debug controls", label);
>> +
>> + enter_guest();
>> + skip_exit_vmcall();
>> + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
>> + /* Validate VM-exit clears host DEBUGCTL and saves guest DEBUGCTL into the VMCS. */
>> + report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == mask,
>> + "%s: Save debug controls", label);
>> +
>> + if (ctrl_enter_rev.set & ENT_LOAD_DBGCTLS ||
>> + ctrl_exit_rev.set & EXI_SAVE_DBGCTLS) {
>> + printf("\tDebug controls are always loaded/saved\n");
>> + test_set_guest_finished();
>> + return;
>> + }
>> +
>> + wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
>> + vmcs_write(GUEST_DEBUGCTL, 0x0);
>> + /* Validate when save/load debug controls are clear */
>> + vmcs_clear_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
>> + vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
>> +
>> + enter_guest();
>> + skip_exit_vmcall();
>> + /* Validate guest observes the host's DEBUGCTL value. */
>> + report(vmx_debugctl_test_data.guest_debugctl == mask,
>> + "%s: Guest=host debug controls", label);
>> +
>> + wrmsr(MSR_IA32_DEBUGCTLMSR, 0x0);
>> + vmcs_write(GUEST_DEBUGCTL, 0x0);
>> +
>> + enter_guest();
>> + skip_exit_vmcall();
>> + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
>> + /* Validate VM-exit clears host DEBUGCTL and does not save guest DEBUGCTL into the VMCS. */
>> + report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == 0x0,
>> + "%s: Don't save debug controls", label);
>> +
>> + enter_guest();
>> +}
>> +
>> +static void vmx_bus_lock_detect_test(void)
>
> the name reads ambiguous to me.
>
> how about debug_controls_debugctrlmsr_bus_lock_detect_test? hmm, seems too lengthy
I'm also hesitant about what kind of function name to use and whether it needs to be
specific to the debug controls.
Sean's suggestion was to "add dedicated tests (for BUS_LOCK_DETECT and LBR), and get
any coverage for KVM's handling of DEBUGCTL reads and writes as a side effect."
That makes me think the case should focus on the feature, with DEBUGCTL handling as
a subcase for that feature. Perhaps I'm overthinking this a bit.
>
>> +{
>> +
>
> unnecessary new line.
Fixed, thanks!
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 4/4] nVMX: Validate DEBUGCTLMSR_LBR states during VMX transitions
2026-05-26 12:34 ` Xiaoyao Li
@ 2026-05-27 2:11 ` Chenyi Qiang
0 siblings, 0 replies; 15+ messages in thread
From: Chenyi Qiang @ 2026-05-27 2:11 UTC (permalink / raw)
To: Xiaoyao Li, kvm; +Cc: Paolo Bonzini, Sean Christopherson
On 5/26/2026 8:34 PM, Xiaoyao Li wrote:
> On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
>> Add tests to verify that DEBUGCTLMSR_LBR state is correctly
>> handled during VMX transitions on top of the shared helper.
>>
>> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
>> ---
>> x86/unittests.cfg | 9 +++++++++
>> x86/vmx_tests.c | 19 +++++++++++++++++++
>> 2 files changed, 28 insertions(+)
>>
>> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
>> index 78c15e2b..d089c86d 100644
>> --- a/x86/unittests.cfg
>> +++ b/x86/unittests.cfg
>> @@ -487,6 +487,15 @@ arch = x86_64
>> groups = vmx
>> timeout = 240
>> +[vmx_legacy_lbr_test]
>> +file = vmx.flat
>> +test_args = "vmx_legacy_lbr_test"
>> +qemu_params = -cpu host,migratable=no,+vmx
>> +check = /sys/module/kvm/parameters/enable_pmu=Y /sys/module/kvm/parameters/ignore_msrs=N
>
> What's the purpose of checking ignore_msrs here? can you elaborate a bit?
This check is mentioned in pmu_lbr, and I followed it without giving it much thought at the time.
After reconsideration, this check is not needed in our case. The pmu_lbr requires it to ensure that
accessing model-unsupported LBR MSRs generates a #GP, but DEBUGCTLMSR does not need it.
Will drop it in next version.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions
2026-05-27 1:42 ` Chenyi Qiang
@ 2026-05-27 14:58 ` Sean Christopherson
2026-05-27 15:16 ` Xiaoyao Li
0 siblings, 1 reply; 15+ messages in thread
From: Sean Christopherson @ 2026-05-27 14:58 UTC (permalink / raw)
To: Chenyi Qiang; +Cc: Xiaoyao Li, kvm, Paolo Bonzini
On Wed, May 27, 2026, Chenyi Qiang wrote:
> On 5/26/2026 8:32 PM, Xiaoyao Li wrote:
> > On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
> >> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
> >> index 48835eba..c8426770 100644
> >> --- a/x86/unittests.cfg
> >> +++ b/x86/unittests.cfg
> >> @@ -471,6 +471,14 @@ arch = x86_64
> >> groups = vmx
> >> timeout = 240
> >> +[vmx_bus_lock_detect_test]
> >> +file = vmx.flat
> >> +test_args = "vmx_bus_lock_detect_test"
> >> +qemu_params = -cpu max,+vmx
> >> +arch = x86_64
> >> +groups = vmx
> >> +timeout = 240
Why does this need a separate config, and with a massive timeout? This should
be nearly instantaneous, no?
Ugh, because you copy+pasted vmx_cet. That thing doesn't need to exist, the
testcase is run by the common "vmx" entry. I'll send a patch to drop vmx_cet.
> >> +
> >> [debug]
> >> file = debug.flat
> >> arch = x86_64
> >> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> >> index 91f417c6..a3e52d3b 100644
> >> --- a/x86/vmx_tests.c
> >> +++ b/x86/vmx_tests.c
> >> @@ -11737,6 +11737,100 @@ static void vmx_cet_test(void)
> >> test_set_guest_finished();
> >> }
> >> +static struct vmx_debugctl_test_data {
> >> + u64 mask;
It's not a mask, it's an explicit value.
> >> + u64 guest_debugctl;
> >> +} vmx_debugctl_test_data;
There's no need for a struct, only a single u64 is needed to pass information
back and forth since there's a VMCALL after every step.
> >> +static void vmx_debugctl_test_guest(void)
> >> +{
> >> + vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> >> + vmcall();
> >> +
> >> + wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
> >> + vmcall();
> >> +
> >> + vmx_debugctl_test_data.guest_debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> >> + vmcall();
> >> +
> >> + wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_data.mask);
> >> + vmcall();
> >> +}
> >> +
> >> +static void vmx_debugctl_transition_test(const char *label, u64 mask)
> >> +{
> >> + u64 debugctl;
> >> +
> >> + vmx_debugctl_test_data.mask = mask;
> >> +
> >> + msr_bmp_init();
> >> + wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
> >> + vmcs_write(GUEST_DEBUGCTL, 0x0);
> >> +
> >> + /* Validate when save/load debug controls are set */
> >> + vmcs_set_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
> >> + vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
> >> +
> >> + test_set_guest(vmx_debugctl_test_guest);
> >> +
> >> + enter_guest();
> >> + skip_exit_vmcall();
> >> + /* Validate VM-entry loads the guest DEBUGCTL value from GUEST_DEBUGCTL. */
> >> + report(vmx_debugctl_test_data.guest_debugctl == 0x0,
> >> + "%s: Load debug controls", label);
> >> +
> >> + enter_guest();
> >> + skip_exit_vmcall();
> >> + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> >> + /* Validate VM-exit clears host DEBUGCTL and saves guest DEBUGCTL into the VMCS. */
> >> + report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == mask,
> >> + "%s: Save debug controls", label);
> >> +
> >> + if (ctrl_enter_rev.set & ENT_LOAD_DBGCTLS ||
> >> + ctrl_exit_rev.set & EXI_SAVE_DBGCTLS) {
> >> + printf("\tDebug controls are always loaded/saved\n");
> >> + test_set_guest_finished();
> >> + return;
> >> + }
> >> +
> >> + wrmsr(MSR_IA32_DEBUGCTLMSR, mask);
> >> + vmcs_write(GUEST_DEBUGCTL, 0x0);
> >> + /* Validate when save/load debug controls are clear */
We should also test with only of the controls set.
> >> + vmcs_clear_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
> >> + vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
> >> +
> >> + enter_guest();
> >> + skip_exit_vmcall();
> >> + /* Validate guest observes the host's DEBUGCTL value. */
> >> + report(vmx_debugctl_test_data.guest_debugctl == mask,
> >> + "%s: Guest=host debug controls", label);
> >> +
> >> + wrmsr(MSR_IA32_DEBUGCTLMSR, 0x0);
> >> + vmcs_write(GUEST_DEBUGCTL, 0x0);
> >> +
> >> + enter_guest();
> >> + skip_exit_vmcall();
> >> + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
> >> + /* Validate VM-exit clears host DEBUGCTL and does not save guest DEBUGCTL into the VMCS. */
This can/should be done on *every* VM-Exit, using report_fail() to avoid polluting
the PASS results with a relatively uninteresting testcase.
> >> + report(debugctl == 0x0 && vmcs_read(GUEST_DEBUGCTL) == 0x0,
> >> + "%s: Don't save debug controls", label);
> >> +
> >> + enter_guest();
> >> +}
> >> +
> >> +static void vmx_bus_lock_detect_test(void)
> >
> > the name reads ambiguous to me.
The name would be great, _if_ the test were actually testing BUS_LOCK_DETECT,
i.e. needs to actually generate a bus lock in L2 and verify L1 gets the VM-Exit.
That's what I was suggesting in my earlier feedback: actually test BUS_LOCK_DETECT,
and verify vmcs.GUEST_DEBUGCTL handling as a side effect.
But since you've already gone thorugh most of the effort... :-)
Rather than split this into three tests, bundle it all into one test. Splitting
makes sense _if_ the guest is actually validating functionality, but if the goal
is purely to validate the values and not the semantics, then bundling is much
more interesting as it allows testing combinations.
And since only three bits are set, just validate all combinations, for both the
host and the guest. It's a total of 8 values, so at most 64 total testcases to
cover both host and guest.
This works for me, I'll post a v3.
---
lib/x86/msr.h | 2 +
x86/vmx_tests.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 152 insertions(+)
diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index 7397809c..8b019f7e 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -103,12 +103,14 @@
/* DEBUGCTLMSR bits (others vary by model): */
#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */
#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */
+#define DEBUGCTLMSR_BUS_LOCK_DETECT (1UL << 2)
#define DEBUGCTLMSR_TR (1UL << 6)
#define DEBUGCTLMSR_BTS (1UL << 7)
#define DEBUGCTLMSR_BTINT (1UL << 8)
#define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9)
#define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10)
#define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11)
+#define DEBUGCTLMSR_RTM_DEBUG (1UL << 15)
#define MSR_LBR_NHM_FROM 0x00000680
#define MSR_LBR_NHM_TO 0x000006c0
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index aca93af9..2d6eb451 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -11774,6 +11774,155 @@ static void vmx_cet_test(void)
test_set_guest_finished();
}
+static u64 vmx_debugctl_test_val;
+
+static void vmx_debugctl_test_guest(void)
+{
+ for (;;) {
+ vmx_debugctl_test_val = rdmsr(MSR_IA32_DEBUGCTLMSR);
+ vmcall();
+
+ wrmsr(MSR_IA32_DEBUGCTLMSR, vmx_debugctl_test_val);
+ vmcall();
+ }
+}
+
+static void __run_vmx_debugctl_guest(void)
+{
+ u64 val;
+
+ enter_guest();
+ skip_exit_vmcall();
+
+ /* Verify DEBUGCTL is unconditionally zeroed on VM-Exit. */
+ val = rdmsr(MSR_IA32_DEBUGCTLMSR);
+ if (val)
+ report_fail("DEBUGCTL = 0x%lx (not zeroed on VM-Exit)", val);
+}
+
+static u64 run_vmx_debugctl_guest(u64 msr_val, u64 vmcs_val, u64 write_val)
+{
+ u64 read_val;
+
+ wrmsr(MSR_IA32_DEBUGCTLMSR, msr_val);
+ vmcs_write(GUEST_DEBUGCTL, vmcs_val);
+
+ __run_vmx_debugctl_guest();
+ read_val = vmx_debugctl_test_val;
+
+ vmx_debugctl_test_val = write_val;
+ __run_vmx_debugctl_guest();
+
+ return read_val;
+}
+
+static void __vmx_debugctl_test(u64 host_val, u64 guest_val)
+{
+ u64 val, rand;
+
+ /* Validate VM-Entrywhen save/load debug controls are set */
+ vmcs_set_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
+ vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
+
+ /* Verify guest DEBUGCTL is loaded/saved when the controls are set. */
+ val = run_vmx_debugctl_guest(host_val, guest_val, 0);
+ report(val == guest_val, "Load DEBUGCTL = 0x%lx, guest RDMSR = 0x%lx", guest_val, val);
+ val = vmcs_read(GUEST_DEBUGCTL);
+ report(!val, "Save DEBUGCTL = 0x0, guest WRMSR = 0x%lx", val);
+
+ /* Rerun the test with the guest values flipped. */
+ val = run_vmx_debugctl_guest(host_val, 0, guest_val);
+ report(!val, "Load DEBUGCTL = 0x0, guest RDMSR = 0x%lx", val);
+ val = vmcs_read(GUEST_DEBUGCTL);
+ report(val == guest_val, "Save DEBUGCTL = 0x0%lx, guest WRMSR = 0x%lx", val, guest_val);
+
+ /*
+ * If running without the LOAD control set is supported, validate that
+ * the guest sees the host's value. Scribble vmcs.GUEST_DEBUGCTL with
+ * a random value to verify it's ignored.
+ */
+ if (!(ctrl_enter_rev.set & ENT_LOAD_DBGCTLS)) {
+ vmcs_clear_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
+
+ val = run_vmx_debugctl_guest(host_val, rdtsc(), guest_val);
+ report(val == host_val, "Host DEBUGCTL = 0x%lx, guest RDMSR = 0x%lx", host_val, val);
+
+ /* The guest value should still be saved on exit! */
+ val = vmcs_read(GUEST_DEBUGCTL);
+ report(val == guest_val, "Save DEBUGCTL = 0x%lx, guest WRMSR = 0x%lx", val, guest_val);
+
+ vmcs_set_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
+ }
+
+ if (!(ctrl_exit_rev.set & EXI_SAVE_DBGCTLS)) {
+ vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
+
+ /* Verify the guest's value is NOT saved on exit.*/
+ val = run_vmx_debugctl_guest(host_val, guest_val, 0);
+ report(val == guest_val, "Load DEBUGCTL = 0x%lx, guest RDMSR = 0x%lx", guest_val, val);
+ val = vmcs_read(GUEST_DEBUGCTL);
+ report(val == guest_val, "VMREAD DEBUGCTL = 0x%lx, VMWRITE = 0x%lx", val, guest_val);
+
+ /* And again with the values flipped. */
+ val = run_vmx_debugctl_guest(host_val, 0, guest_val);
+ report(!val, "Load DEBUGCTL = 0x0, guest RDMSR = 0x%lx", val);
+ val = vmcs_read(GUEST_DEBUGCTL);
+ report(!val, "VMREAD DEBUGCTL = 0x%lx, VMWRITE = 0x0", val);
+
+ vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
+ }
+
+ if ((ctrl_enter_rev.set & ENT_LOAD_DBGCTLS) ||
+ (ctrl_exit_rev.set & EXI_SAVE_DBGCTLS))
+ return;
+
+ vmcs_clear_bits(ENT_CONTROLS, ENT_LOAD_DBGCTLS);
+ vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_DBGCTLS);
+
+ rand = rdtsc();
+ val = run_vmx_debugctl_guest(host_val, rand, guest_val);
+ report(val == host_val, "Host DEBUGCTL = 0x%lx, guest RDMSR = 0x%lx", host_val, val);
+
+ val = vmcs_read(GUEST_DEBUGCTL);
+ report(val == rand, "VMWRITE DEBUGCTL = 0x%lx, VMREAD = 0x%lx", rand, val);
+}
+
+static void vmx_debugctl_test(void)
+{
+ u64 supported_bits = 0;
+ u64 i, j;
+
+ if (this_cpu_has(X86_FEATURE_BUS_LOCK_DETECT))
+ supported_bits |= DEBUGCTLMSR_BUS_LOCK_DETECT;
+
+ if (this_cpu_has(X86_FEATURE_RTM))
+ supported_bits |= DEBUGCTLMSR_RTM_DEBUG;
+
+ if (this_cpu_has(X86_FEATURE_PDCM) && pmu_lbr_version())
+ supported_bits |= DEBUGCTLMSR_LBR;
+
+ if (!supported_bits) {
+ report_skip("%s : No DEBUGCTL features supported", __func__);
+ return;
+ }
+
+ test_set_guest(vmx_debugctl_test_guest);
+ msr_bmp_init();
+
+ for (i = 1; i <= supported_bits; i++) {
+ if ((i & supported_bits) != i)
+ continue;
+
+ for (j = 1; j <= supported_bits; j++) {
+ if ((j & supported_bits) != j)
+ continue;
+
+ __vmx_debugctl_test(i, j);
+ }
+ }
+ test_set_guest_finished();
+}
+
#define TEST(name) { #name, .v2 = name }
/* name/init/guest_main/exit_handler/vmfail_handler */
@@ -11890,5 +12039,6 @@ struct vmx_test vmx_tests[] = {
TEST(vmx_canonical_test),
/* "Load CET" VM-entry/exit controls tests. */
TEST(vmx_cet_test),
+ TEST(vmx_debugctl_test),
{ NULL, NULL, NULL, NULL },
};
base-commit: da6e732372e64a31010b0c8cbfc550a9d83195ba
--
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions
2026-05-27 14:58 ` Sean Christopherson
@ 2026-05-27 15:16 ` Xiaoyao Li
2026-05-27 15:20 ` Sean Christopherson
0 siblings, 1 reply; 15+ messages in thread
From: Xiaoyao Li @ 2026-05-27 15:16 UTC (permalink / raw)
To: Sean Christopherson, Chenyi Qiang; +Cc: kvm, Paolo Bonzini
On 5/27/2026 10:58 PM, Sean Christopherson wrote:
> On Wed, May 27, 2026, Chenyi Qiang wrote:
>> On 5/26/2026 8:32 PM, Xiaoyao Li wrote:
>>> On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
>>>> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
>>>> index 48835eba..c8426770 100644
>>>> --- a/x86/unittests.cfg
>>>> +++ b/x86/unittests.cfg
>>>> @@ -471,6 +471,14 @@ arch = x86_64
>>>> groups = vmx
>>>> timeout = 240
>>>> +[vmx_bus_lock_detect_test]
>>>> +file = vmx.flat
>>>> +test_args = "vmx_bus_lock_detect_test"
>>>> +qemu_params = -cpu max,+vmx
>>>> +arch = x86_64
>>>> +groups = vmx
>>>> +timeout = 240
>
> Why does this need a separate config, and with a massive timeout? This should
> be nearly instantaneous, no?
>
> Ugh, because you copy+pasted vmx_cet. That thing doesn't need to exist, the
> testcase is run by the common "vmx" entry. I'll send a patch to drop vmx_cet.
I told Chenyi that to actually be able to test LBR bit, we might need
some specific QEMU configuration to enable PMU and legacy LBR and it
will need a specific config.
I missed that he even added the config for BUS_LOCK_DETECT and RTM tests.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions
2026-05-27 15:16 ` Xiaoyao Li
@ 2026-05-27 15:20 ` Sean Christopherson
0 siblings, 0 replies; 15+ messages in thread
From: Sean Christopherson @ 2026-05-27 15:20 UTC (permalink / raw)
To: Xiaoyao Li; +Cc: Chenyi Qiang, kvm, Paolo Bonzini
On Wed, May 27, 2026, Xiaoyao Li wrote:
> On 5/27/2026 10:58 PM, Sean Christopherson wrote:
> > On Wed, May 27, 2026, Chenyi Qiang wrote:
> > > On 5/26/2026 8:32 PM, Xiaoyao Li wrote:
> > > > On 5/26/2026 11:16 AM, Chenyi Qiang wrote:
> > > > > diff --git a/x86/unittests.cfg b/x86/unittests.cfg
> > > > > index 48835eba..c8426770 100644
> > > > > --- a/x86/unittests.cfg
> > > > > +++ b/x86/unittests.cfg
> > > > > @@ -471,6 +471,14 @@ arch = x86_64
> > > > > groups = vmx
> > > > > timeout = 240
> > > > > +[vmx_bus_lock_detect_test]
> > > > > +file = vmx.flat
> > > > > +test_args = "vmx_bus_lock_detect_test"
> > > > > +qemu_params = -cpu max,+vmx
> > > > > +arch = x86_64
> > > > > +groups = vmx
> > > > > +timeout = 240
> >
> > Why does this need a separate config, and with a massive timeout? This should
> > be nearly instantaneous, no?
> >
> > Ugh, because you copy+pasted vmx_cet. That thing doesn't need to exist, the
> > testcase is run by the common "vmx" entry. I'll send a patch to drop vmx_cet.
>
> I told Chenyi that to actually be able to test LBR bit, we might need some
> specific QEMU configuration to enable PMU and legacy LBR and it will need a
> specific config.
Ah. AFAICT, it's just the migratable=no thing? If so, any reason we can't do
that for the main "vmx" testcase?
diff --git x86/unittests.cfg x86/unittests.cfg
index 48835eba..fad58255 100644
--- x86/unittests.cfg
+++ x86/unittests.cfg
@@ -320,7 +320,7 @@ qemu_params = -cpu max,host-phys-bits
[vmx]
file = vmx.flat
test_args = "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test -atomic_switch_overflow_msrs_test -vmx_init_signal_test -vmx_apic_passthrough_tpr_threshold_test -apic_reg_virt_test -virt_x2apic_mode_test -vmx_pf_exception_test -vmx_pf_exception_forced_emulation_test -vmx_pf_no_vpid_test -vmx_pf_invvpid_test -vmx_pf_vpid_test -vmx_basic_vid_test -vmx_eoi_virt_test -vmx_posted_interrupts_test"
-qemu_params = -cpu max,+vmx
+qemu_params = -cpu max,migratable=no,+vmx
arch = x86_64
groups = vmx
^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-05-27 15:20 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-26 3:16 [kvm-unit-tests PATCH v2 0/4] nVMX: Improve IA32_DEBUGCTLMSR test on debug controls Chenyi Qiang
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 1/4] nVMX: Remove the IA32_DEBUGCTLMSR access in debugctls test Chenyi Qiang
2026-05-26 4:39 ` Xiaoyao Li
2026-05-26 4:52 ` Chenyi Qiang
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 2/4] nVMX: Validate DEBUGCTLMSR_BUS_LOCK_DETECT states during VMX transitions Chenyi Qiang
2026-05-26 12:32 ` Xiaoyao Li
2026-05-27 1:42 ` Chenyi Qiang
2026-05-27 14:58 ` Sean Christopherson
2026-05-27 15:16 ` Xiaoyao Li
2026-05-27 15:20 ` Sean Christopherson
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 3/4] nVMX: Validate DEBUGCTLMSR_RTM_DEBUG " Chenyi Qiang
2026-05-26 12:33 ` Xiaoyao Li
2026-05-26 3:16 ` [kvm-unit-tests PATCH v2 4/4] nVMX: Validate DEBUGCTLMSR_LBR " Chenyi Qiang
2026-05-26 12:34 ` Xiaoyao Li
2026-05-27 2:11 ` Chenyi Qiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox