Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH v2 0/9] KVM: x86: Improve the handling of debug exceptions during instruction emulation
@ 2025-12-18 14:00 Hou Wenlong
  2025-12-18 14:00 ` [PATCH v2 8/9] KVM: selftests: Verify guest debug DR7.GD checking " Hou Wenlong
  2025-12-18 14:00 ` [PATCH v2 9/9] KVM: selftests: Verify 'BS' bit checking in pending debug exception state during VM-Entry Hou Wenlong
  0 siblings, 2 replies; 3+ messages in thread
From: Hou Wenlong @ 2025-12-18 14:00 UTC (permalink / raw)
  To: kvm
  Cc: Lai Jiangshan, Sean Christopherson, Paolo Bonzini,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Shuah Khan, linux-kernel, linux-kselftest

During my testing, I found that guest debugging with 'DR6.BD' does not
work in instruction emulation, as the current code only considers the
guest's DR7. Upon reviewing the code, I also observed that the checks
for the userspace guest debugging feature and the guest's own debugging
feature are repeated in different places during instruction
emulation, but the overall logic is the same. If guest debug
is enabled, it needs to exit to userspace; otherwise, a #DB
exception needs to be injected into the guest. Therefore, as
suggested by Jiangshan Lai, some cleanup has been done for #DB
handling in instruction emulation in this patchset. A new
function named 'kvm_inject_emulated_db()' is introduced to
consolidate all the checking logic. Moreover, I hope we can make
the #DB interception path use the same function as well.

Additionally, when I looked into the single-step #DB handling in
instruction emulation, I noticed that the interrupt shadow is toggled,
but it is not considered in the single-step #DB injection. This
oversight causes VM entry to fail on VMX (due to pending debug
exceptions state checking).

As pointed out by Sean, fault-like code #DBs can be coincident with
trap-like single-step #DBs at the instruction boundary on the hardware.
However it is difficult to emulate this in the emulator, as
kvm_vcpu_check_code_breakpoint() is called at the start of the next
instruction while the single-step #DB for the previous instruction has
already been injected.

v1->v2:
  - cleanup in inject_emulated_exception().
  - rename 'set_pending_dbg' callback as 'refresh_pending_dbg_exceptions'.
  - fold refresh_pending_dbg_exceptions() call into
    kvm_vcpu_do_singlestep().
  - Split the change to move up kvm_set_rflags() into a single patch.
  - Move the #DB and IRQ handler registration after guest debug testcases.

Hou Wenlong (9):
  KVM: x86: Capture "struct x86_exception" in
    inject_emulated_exception()
  KVM: x86: Set guest DR6 by kvm_queue_exception_p() in instruction
    emulation
  KVM: x86: Check guest debug in DR access instruction emulation
  KVM: x86: Only check effective code breakpoint in emulation
  KVM: x86: Consolidate KVM_GUESTDBG_SINGLESTEP check into the
    kvm_inject_emulated_db()
  KVM: x86: Move kvm_set_rflags() up before kvm_vcpu_do_singlestep()
  KVM: VMX: Refresh 'PENDING_DBG_EXCEPTIONS.BS' bit during instruction
    emulation
  KVM: selftests: Verify guest debug DR7.GD checking during instruction
    emulation
  KVM: selftests: Verify 'BS' bit checking in pending debug exception
    during VM entry

 arch/x86/include/asm/kvm-x86-ops.h            |   1 +
 arch/x86/include/asm/kvm_host.h               |   1 +
 arch/x86/kvm/emulate.c                        |  14 +--
 arch/x86/kvm/kvm_emulate.h                    |   7 +-
 arch/x86/kvm/vmx/main.c                       |   9 ++
 arch/x86/kvm/vmx/vmx.c                        |  15 ++-
 arch/x86/kvm/vmx/x86_ops.h                    |   1 +
 arch/x86/kvm/x86.c                            | 116 ++++++++++--------
 arch/x86/kvm/x86.h                            |   7 ++
 .../selftests/kvm/include/x86/processor.h     |   3 +-
 tools/testing/selftests/kvm/x86/debug_regs.c  |  72 ++++++++++-
 11 files changed, 178 insertions(+), 68 deletions(-)


base-commit: 5d3e2d9ba9ed68576c70c127e4f7446d896f2af2
--
2.31.1


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

* [PATCH v2 8/9] KVM: selftests: Verify guest debug DR7.GD checking during instruction emulation
  2025-12-18 14:00 [PATCH v2 0/9] KVM: x86: Improve the handling of debug exceptions during instruction emulation Hou Wenlong
@ 2025-12-18 14:00 ` Hou Wenlong
  2025-12-18 14:00 ` [PATCH v2 9/9] KVM: selftests: Verify 'BS' bit checking in pending debug exception state during VM-Entry Hou Wenlong
  1 sibling, 0 replies; 3+ messages in thread
From: Hou Wenlong @ 2025-12-18 14:00 UTC (permalink / raw)
  To: kvm
  Cc: Lai Jiangshan, Sean Christopherson, Paolo Bonzini, Shuah Khan,
	linux-kselftest, linux-kernel

Similar to the global disable test case in x86's debug_regs test, use
'KVM_FEP' to trigger instruction emulation in order to verify the guest
debug DR7.GD checking during instruction emulation.

Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 tools/testing/selftests/kvm/x86/debug_regs.c | 23 +++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c
index 2d814c1d1dc4..bd34cf2a96b7 100644
--- a/tools/testing/selftests/kvm/x86/debug_regs.c
+++ b/tools/testing/selftests/kvm/x86/debug_regs.c
@@ -19,6 +19,7 @@
 uint32_t guest_value;
 
 extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start;
+extern unsigned char fep_bd_start;
 
 static void guest_code(void)
 {
@@ -64,6 +65,10 @@ static void guest_code(void)
 
 	/* DR6.BD test */
 	asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax");
+
+	if (is_forced_emulation_enabled)
+		asm volatile(KVM_FEP "fep_bd_start: mov %%dr0, %%rax" : : : "rax");
+
 	GUEST_DONE();
 }
 
@@ -185,7 +190,7 @@ int main(void)
 			    target_dr6);
 	}
 
-	/* Finally test global disable */
+	/* test global disable */
 	memset(&debug, 0, sizeof(debug));
 	debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
 	debug.arch.debugreg[7] = 0x400 | DR7_GD;
@@ -202,6 +207,22 @@ int main(void)
 			    run->debug.arch.pc, target_rip, run->debug.arch.dr6,
 			    target_dr6);
 
+	/* test global disable in emulation */
+	if (is_forced_emulation_enabled) {
+		/* Skip the 3-bytes "mov dr0" */
+		vcpu_skip_insn(vcpu, 3);
+		vcpu_run(vcpu);
+		TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
+			    run->debug.arch.exception == DB_VECTOR &&
+			    run->debug.arch.pc == CAST_TO_RIP(fep_bd_start) &&
+			    run->debug.arch.dr6 == target_dr6,
+			    "DR7.GD: exit %d exception %d rip 0x%llx "
+			    "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
+			    run->exit_reason, run->debug.arch.exception,
+			    run->debug.arch.pc, target_rip, run->debug.arch.dr6,
+			    target_dr6);
+	}
+
 	/* Disable all debug controls, run to the end */
 	memset(&debug, 0, sizeof(debug));
 	vcpu_guest_debug_set(vcpu, &debug);
-- 
2.31.1


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

* [PATCH v2 9/9] KVM: selftests: Verify 'BS' bit checking in pending debug exception state during VM-Entry
  2025-12-18 14:00 [PATCH v2 0/9] KVM: x86: Improve the handling of debug exceptions during instruction emulation Hou Wenlong
  2025-12-18 14:00 ` [PATCH v2 8/9] KVM: selftests: Verify guest debug DR7.GD checking " Hou Wenlong
@ 2025-12-18 14:00 ` Hou Wenlong
  1 sibling, 0 replies; 3+ messages in thread
From: Hou Wenlong @ 2025-12-18 14:00 UTC (permalink / raw)
  To: kvm
  Cc: Lai Jiangshan, Sean Christopherson, Paolo Bonzini, Shuah Khan,
	linux-kselftest, linux-kernel

In the x86's debug_regs test, add a test case to cover the scenario
where single-step with STI in VMX sets the 'BS' bit in pending debug
exceptions state for #DB interception and instruction emulation in both
cases.

Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 .../selftests/kvm/include/x86/processor.h     |  3 +-
 tools/testing/selftests/kvm/x86/debug_regs.c  | 53 +++++++++++++++++--
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 57d62a425109..1992d98016d4 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -36,7 +36,8 @@ extern uint64_t guest_tsc_khz;

 const char *ex_str(int vector);

-#define X86_EFLAGS_FIXED	 (1u << 1)
+#define X86_EFLAGS_FIXED	(1u << 1)
+#define X86_EFLAGS_TF		(1u << 8)

 #define X86_CR4_VME		(1ul << 0)
 #define X86_CR4_PVI		(1ul << 1)
diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c
index bd34cf2a96b7..84d3103a05fc 100644
--- a/tools/testing/selftests/kvm/x86/debug_regs.c
+++ b/tools/testing/selftests/kvm/x86/debug_regs.c
@@ -15,11 +15,35 @@

 #define IRQ_VECTOR 0xAA

+#define  CAST_TO_RIP(v)  ((unsigned long long)&(v))
+
 /* For testing data access debug BP */
 uint32_t guest_value;

 extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start;
-extern unsigned char fep_bd_start;
+extern unsigned char fep_bd_start, fep_sti_start, fep_sti_end;
+
+static void guest_db_handler(struct ex_regs *regs)
+{
+	static int count;
+	unsigned long target_rips[2] = {
+		CAST_TO_RIP(fep_sti_start),
+		CAST_TO_RIP(fep_sti_end),
+	};
+
+	__GUEST_ASSERT(regs->rip == target_rips[count], "STI: unexpected rip 0x%lx (should be 0x%lx)",
+		       regs->rip, target_rips[count]);
+	regs->rflags &= ~X86_EFLAGS_TF;
+	count++;
+}
+
+static void guest_irq_handler(struct ex_regs *regs)
+{
+	unsigned long target_rip = CAST_TO_RIP(fep_bd_start);
+
+	__GUEST_ASSERT(regs->rip == target_rip, "IRQ: unexpected rip 0x%lx (should be 0x%lx)",
+		       regs->rip, target_rip);
+}

 static void guest_code(void)
 {
@@ -66,14 +90,27 @@ static void guest_code(void)
 	/* DR6.BD test */
 	asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax");

-	if (is_forced_emulation_enabled)
+	if (is_forced_emulation_enabled) {
 		asm volatile(KVM_FEP "fep_bd_start: mov %%dr0, %%rax" : : : "rax");

+		/* pending debug exceptions for emulation */
+		asm volatile("pushf\n\t"
+			     "orq $" __stringify(X86_EFLAGS_TF) ", (%rsp)\n\t"
+			     "popf\n\t"
+			     "sti\n\t"
+			     "fep_sti_start:"
+			     "cli\n\t"
+			     "pushf\n\t"
+			     "orq $" __stringify(X86_EFLAGS_TF) ", (%rsp)\n\t"
+			     "popf\n\t"
+			     KVM_FEP "sti\n\t"
+			     "fep_sti_end:"
+			     "cli\n\t");
+	}
+
 	GUEST_DONE();
 }

-#define  CAST_TO_RIP(v)  ((unsigned long long)&(v))
-
 static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len)
 {
 	struct kvm_regs regs;
@@ -227,6 +264,14 @@ int main(void)
 	memset(&debug, 0, sizeof(debug));
 	vcpu_guest_debug_set(vcpu, &debug);

+	vm_install_exception_handler(vm, DB_VECTOR, guest_db_handler);
+	/*
+	 * Install a dummy IRQ handler, as the single-step #DB delivery after
+	 * STI removes the interrupt shadow, so the pending interrupt will be
+	 * delivered after #DB handling.
+	 */
+	vm_install_exception_handler(vm, IRQ_VECTOR, guest_irq_handler);
+
 	vcpu_run(vcpu);
 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
 	cmd = get_ucall(vcpu, &uc);
--
2.31.1


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

end of thread, other threads:[~2025-12-18 14:06 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-18 14:00 [PATCH v2 0/9] KVM: x86: Improve the handling of debug exceptions during instruction emulation Hou Wenlong
2025-12-18 14:00 ` [PATCH v2 8/9] KVM: selftests: Verify guest debug DR7.GD checking " Hou Wenlong
2025-12-18 14:00 ` [PATCH v2 9/9] KVM: selftests: Verify 'BS' bit checking in pending debug exception state during VM-Entry Hou Wenlong

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