* [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator
@ 2026-05-15 22:26 Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 01/10] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs Sean Christopherson
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
Hou's series of fixes and cleanups related to #DB handling in the emulator.
v3:
- Fix the GUEST_PENDING_DBG_EXCEPTIONS.BS consistency check issue by
stuffing the VMCS during injection, not during emulation.
- Drop a prep that is no longer necessary.
- Massage a few shortlogs/changelogs.
- Fix the selftest that takes an IRQ in the STI-shadow after IRET.
- Fix an intermediate bug where kvm_queue_exception_e() was being used
instead of kvm_queue_exception_p().
v2:
- https://lore.kernel.org/all/cover.1766066076.git.houwenlong.hwl@antgroup.com
- 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.
v1: https://lore.kernel.org/all/cover.1757416809.git.houwenlong.hwl@antgroup.com
Hou Wenlong (7):
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: Honor KVM_GUESTDBG_USE_HW_BP when emulating MOV DR (in
emulator)
KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when checking for code
breakpoints in emulation
KVM: x86: Move KVM_GUESTDBG_SINGLESTEP handling into
kvm_inject_emulated_db()
KVM: selftests: Verify guest debug DR7.GD checking during instruction
emulation
KVM: selftests: Verify VMX's GUEST_PENDING_DBG_EXCEPTIONS.BS
Consistency Check
Sean Christopherson (3):
KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs
KVM: x86: Drop kvm_vcpu_do_singlestep() now that it's been gutted
KVM: selftests: Add all (known) EFLAGS bit definitions
arch/x86/kvm/emulate.c | 14 +--
arch/x86/kvm/kvm_emulate.h | 7 +-
arch/x86/kvm/vmx/vmx.c | 35 +++---
arch/x86/kvm/x86.c | 111 +++++++++---------
.../selftests/kvm/include/x86/processor.h | 19 ++-
.../testing/selftests/kvm/lib/x86/processor.c | 2 +-
tools/testing/selftests/kvm/lib/x86/vmx.c | 2 +-
tools/testing/selftests/kvm/x86/debug_regs.c | 83 ++++++++++++-
8 files changed, 184 insertions(+), 89 deletions(-)
base-commit: b7fbe9a1bf9ee6c967ef77d366ca58c35fcf1887
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 01/10] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 02/10] KVM: x86: Capture "struct x86_exception" in inject_emulated_exception() Sean Christopherson
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
Move KVM's stuffing of GUEST_PENDING_DBG_EXCEPTIONS.BS when RFLAGS.TF=1 and
MOV/POP SS or STI blocking is active into the exception injection code so
that KVM fixes up the VMCS for all injected #DBs, not only those that are
reflected back into the guest after #DB interception. E.g. if KVM queues
a #DB in the emulator, or more importantly if userspace does save/restore
exactly on the #DB+shadow boundary, then KVM needs to massage the VMCS to
avoid the VM-Entry consistency check.
Opportunistically update the wording of the comment to describe the
behavior as a workaround of flawed CPU behavior/architecture, to make it
clear that the *only* thing KVM is doing is fudging around a consistency
check. Per the SDM:
There are no pending debug exceptions after VM entry if any of the
following are true:
* The VM entry is vectoring with one of the following interruption
types: external interrupt, non-maskable interrupt (NMI), hardware
exception, or privileged software exception.
I.e. forcing GUEST_PENDING_DBG_EXCEPTIONS.BS does *not* impact guest-
visible behavior.
Fixes: b9bed78e2fa9 ("KVM: VMX: Set vmcs.PENDING_DBG.BS on #DB in STI/MOVSS blocking shadow")
Cc: stable@vger.kernel.org
Reported-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
Closes: https://lore.kernel.org/all/b1a294bc9ed4dae532474a5dc6c8cb6e5962de7c.1757416809.git.houwenlong.hwl@antgroup.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/vmx/vmx.c | 35 ++++++++++++++++++-----------------
1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 1701db1b2e18..a0a0ccf342d3 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1909,6 +1909,24 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu)
u32 intr_info = ex->vector | INTR_INFO_VALID_MASK;
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ /*
+ * When injecting a #DB, single-stepping is enabled in RFLAGS, and STI
+ * or MOV-SS blocking is active, set vmcs.PENDING_DBG_EXCEPTIONS.BS to
+ * prevent a false positive from VM-Entry consistency check. VM-Entry
+ * asserts that a single-step #DB _must_ be pending in this scenario,
+ * as the previous instruction cannot have toggled RFLAGS.TF 0=>1
+ * (because STI and POP/MOV don't modify RFLAGS), therefore the one
+ * instruction delay when activating single-step breakpoints must have
+ * already expired. However, the CPU isn't smart enough to peek at
+ * vmcs.VM_ENTRY_INTR_INFO_FIELD and so doesn't realize that yes, there
+ * is indeed a #DB pending/imminent.
+ */
+ if (ex->vector == DB_VECTOR &&
+ (vmx_get_rflags(vcpu) & X86_EFLAGS_TF) &&
+ vmx_get_interrupt_shadow(vcpu))
+ vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS,
+ vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS);
+
kvm_deliver_exception_payload(vcpu, ex);
if (ex->has_error_code) {
@@ -5485,26 +5503,9 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
* avoid single-step #DB and MTF updates, as ICEBP is
* higher priority. Note, skipping ICEBP still clears
* STI and MOVSS blocking.
- *
- * For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS
- * if single-step is enabled in RFLAGS and STI or MOVSS
- * blocking is active, as the CPU doesn't set the bit
- * on VM-Exit due to #DB interception. VM-Entry has a
- * consistency check that a single-step #DB is pending
- * in this scenario as the previous instruction cannot
- * have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV
- * don't modify RFLAGS), therefore the one instruction
- * delay when activating single-step breakpoints must
- * have already expired. Note, the CPU sets/clears BS
- * as appropriate for all other VM-Exits types.
*/
if (is_icebp(intr_info))
WARN_ON(!skip_emulated_instruction(vcpu));
- else if ((vmx_get_rflags(vcpu) & X86_EFLAGS_TF) &&
- (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
- (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS)))
- vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS,
- vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS);
kvm_queue_exception_p(vcpu, DB_VECTOR, dr6);
return 1;
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 02/10] KVM: x86: Capture "struct x86_exception" in inject_emulated_exception()
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 01/10] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 03/10] KVM: x86: Set guest DR6 by kvm_queue_exception_p() in instruction emulation Sean Christopherson
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
From: Hou Wenlong <houwenlong.hwl@antgroup.com>
As all callers in inject_emulated_exception() use "struct x86_exception"
directly, capture it locally instead of using the context.
No functional change intended.
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/x86.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 48f259015ce4..8ddb878934ed 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8978,15 +8978,14 @@ static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
static void inject_emulated_exception(struct kvm_vcpu *vcpu)
{
- struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt;
+ struct x86_exception *ex = &vcpu->arch.emulate_ctxt->exception;
- if (ctxt->exception.vector == PF_VECTOR)
- kvm_inject_emulated_page_fault(vcpu, &ctxt->exception);
- else if (ctxt->exception.error_code_valid)
- kvm_queue_exception_e(vcpu, ctxt->exception.vector,
- ctxt->exception.error_code);
+ if (ex->vector == PF_VECTOR)
+ kvm_inject_emulated_page_fault(vcpu, ex);
+ else if (ex->error_code_valid)
+ kvm_queue_exception_e(vcpu, ex->vector, ex->error_code);
else
- kvm_queue_exception(vcpu, ctxt->exception.vector);
+ kvm_queue_exception(vcpu, ex->vector);
}
static struct x86_emulate_ctxt *alloc_emulate_ctxt(struct kvm_vcpu *vcpu)
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 03/10] KVM: x86: Set guest DR6 by kvm_queue_exception_p() in instruction emulation
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 01/10] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 02/10] KVM: x86: Capture "struct x86_exception" in inject_emulated_exception() Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 04/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when emulating MOV DR (in emulator) Sean Christopherson
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
From: Hou Wenlong <houwenlong.hwl@antgroup.com>
Record DR6 in emulate_db() and use kvm_queue_exception_p() to set DR6
instead of directly using kvm_set_dr6() in emulation, which keeps the
handling of DR6 during #DB injection consistent with other code paths.
No functional change intended.
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
[sean: fix e vs. p goof, add kvm_inject_emulated_db() right away]
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/emulate.c | 14 ++++----------
arch/x86/kvm/kvm_emulate.h | 6 +++++-
arch/x86/kvm/x86.c | 10 +++++++++-
3 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index c8c6cc0406d6..510244555a74 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -540,8 +540,9 @@ static int emulate_exception(struct x86_emulate_ctxt *ctxt, int vec,
return X86EMUL_PROPAGATE_FAULT;
}
-static int emulate_db(struct x86_emulate_ctxt *ctxt)
+static int emulate_db(struct x86_emulate_ctxt *ctxt, unsigned long dr6)
{
+ ctxt->exception.dr6 = dr6;
return emulate_exception(ctxt, DB_VECTOR, 0, false);
}
@@ -3847,15 +3848,8 @@ static int check_dr_read(struct x86_emulate_ctxt *ctxt)
if ((cr4 & X86_CR4_DE) && (dr == 4 || dr == 5))
return emulate_ud(ctxt);
- if (ctxt->ops->get_dr(ctxt, 7) & DR7_GD) {
- ulong dr6;
-
- dr6 = ctxt->ops->get_dr(ctxt, 6);
- dr6 &= ~DR_TRAP_BITS;
- dr6 |= DR6_BD | DR6_ACTIVE_LOW;
- ctxt->ops->set_dr(ctxt, 6, dr6);
- return emulate_db(ctxt);
- }
+ if (ctxt->ops->get_dr(ctxt, 7) & DR7_GD)
+ return emulate_db(ctxt, DR6_BD);
return X86EMUL_CONTINUE;
}
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index 0abff36d0994..bb2a2aee0e13 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -24,7 +24,11 @@ struct x86_exception {
bool error_code_valid;
u16 error_code;
bool nested_page_fault;
- u64 address; /* cr2 or nested page fault gpa */
+ union {
+ u64 address; /* cr2 or nested page fault gpa */
+ unsigned long dr6;
+ u64 payload;
+ };
u8 async_page_fault;
unsigned long exit_qualification;
};
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8ddb878934ed..8a862d39302c 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8976,11 +8976,18 @@ static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
}
}
+static void kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6)
+{
+ kvm_queue_exception_p(vcpu, DB_VECTOR, dr6);
+}
+
static void inject_emulated_exception(struct kvm_vcpu *vcpu)
{
struct x86_exception *ex = &vcpu->arch.emulate_ctxt->exception;
- if (ex->vector == PF_VECTOR)
+ if (ex->vector == DB_VECTOR)
+ kvm_inject_emulated_db(vcpu, ex->dr6);
+ else if (ex->vector == PF_VECTOR)
kvm_inject_emulated_page_fault(vcpu, ex);
else if (ex->error_code_valid)
kvm_queue_exception_e(vcpu, ex->vector, ex->error_code);
@@ -9025,6 +9032,7 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
ctxt->interruptibility = 0;
ctxt->have_exception = false;
ctxt->exception.vector = -1;
+ ctxt->exception.payload = 0;
ctxt->perm_ok = false;
init_decode_cache(ctxt);
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 04/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when emulating MOV DR (in emulator)
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
` (2 preceding siblings ...)
2026-05-15 22:26 ` [PATCH v3 03/10] KVM: x86: Set guest DR6 by kvm_queue_exception_p() in instruction emulation Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 05/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when checking for code breakpoints in emulation Sean Christopherson
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
From: Hou Wenlong <houwenlong.hwl@antgroup.com>
When emulating a MOV DR instruction, honor KVM_GUESTDBG_USE_HW_BP when
checking DR7.GD, and if there is a general-detect #DB, route it to host
userspace as appropriate. Consulting only the guest's actual DR7 causes
KVM to fail to report a DR access to userspace (assuming the guest itself
doesn't have DR7.GD=1).
Fixes: ae675ef01cd8 ("KVM: x86: Wire-up hardware breakpoints for guest debugging")
Suggested-by: Lai Jiangshan <jiangshan.ljs@antgroup.com>
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
[sean: only expose effective DR7 to emulator, massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/emulate.c | 2 +-
arch/x86/kvm/kvm_emulate.h | 1 +
arch/x86/kvm/x86.c | 41 ++++++++++++++++++++++++++++++--------
3 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 510244555a74..917a521c299f 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -3848,7 +3848,7 @@ static int check_dr_read(struct x86_emulate_ctxt *ctxt)
if ((cr4 & X86_CR4_DE) && (dr == 4 || dr == 5))
return emulate_ud(ctxt);
- if (ctxt->ops->get_dr(ctxt, 7) & DR7_GD)
+ if (ctxt->ops->get_eff_dr7(ctxt) & DR7_GD)
return emulate_db(ctxt, DR6_BD);
return X86EMUL_CONTINUE;
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index bb2a2aee0e13..33bfc9aa948e 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -215,6 +215,7 @@ struct x86_emulate_ops {
ulong (*get_cr)(struct x86_emulate_ctxt *ctxt, int cr);
int (*set_cr)(struct x86_emulate_ctxt *ctxt, int cr, ulong val);
int (*cpl)(struct x86_emulate_ctxt *ctxt);
+ ulong (*get_eff_dr7)(struct x86_emulate_ctxt *ctxt);
ulong (*get_dr)(struct x86_emulate_ctxt *ctxt, int dr);
int (*set_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong value);
int (*set_msr_with_filter)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 data);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8a862d39302c..8b07bd2f8310 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1601,6 +1601,14 @@ unsigned long kvm_get_dr(struct kvm_vcpu *vcpu, int dr)
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_get_dr);
+static unsigned long kvm_get_eff_dr7(struct kvm_vcpu *vcpu)
+{
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
+ return vcpu->arch.guest_debug_dr7;
+
+ return vcpu->arch.dr7;
+}
+
int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu)
{
u32 pmc = kvm_rcx_read(vcpu);
@@ -8548,6 +8556,11 @@ static void emulator_wbinvd(struct x86_emulate_ctxt *ctxt)
kvm_emulate_wbinvd_noskip(emul_to_vcpu(ctxt));
}
+static unsigned long emulator_get_eff_dr7(struct x86_emulate_ctxt *ctxt)
+{
+ return kvm_get_eff_dr7(emul_to_vcpu(ctxt));
+}
+
static unsigned long emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr)
{
return kvm_get_dr(emul_to_vcpu(ctxt), dr);
@@ -8930,6 +8943,7 @@ static const struct x86_emulate_ops emulate_ops = {
.get_cr = emulator_get_cr,
.set_cr = emulator_set_cr,
.cpl = emulator_get_cpl,
+ .get_eff_dr7 = emulator_get_eff_dr7,
.get_dr = emulator_get_dr,
.set_dr = emulator_set_dr,
.set_msr_with_filter = emulator_set_msr_with_filter,
@@ -8976,23 +8990,36 @@ static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
}
}
-static void kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6)
+static int kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6)
{
+ struct kvm_run *kvm_run = vcpu->run;
+
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) {
+ kvm_run->debug.arch.dr6 = dr6 | DR6_ACTIVE_LOW;
+ kvm_run->debug.arch.pc = kvm_get_linear_rip(vcpu);
+ kvm_run->debug.arch.exception = DB_VECTOR;
+ kvm_run->exit_reason = KVM_EXIT_DEBUG;
+ return 0;
+ }
+
kvm_queue_exception_p(vcpu, DB_VECTOR, dr6);
+ return 1;
}
-static void inject_emulated_exception(struct kvm_vcpu *vcpu)
+static int inject_emulated_exception(struct kvm_vcpu *vcpu)
{
struct x86_exception *ex = &vcpu->arch.emulate_ctxt->exception;
if (ex->vector == DB_VECTOR)
- kvm_inject_emulated_db(vcpu, ex->dr6);
- else if (ex->vector == PF_VECTOR)
+ return kvm_inject_emulated_db(vcpu, ex->dr6);
+
+ if (ex->vector == PF_VECTOR)
kvm_inject_emulated_page_fault(vcpu, ex);
else if (ex->error_code_valid)
kvm_queue_exception_e(vcpu, ex->vector, ex->error_code);
else
kvm_queue_exception(vcpu, ex->vector);
+ return 1;
}
static struct x86_emulate_ctxt *alloc_emulate_ctxt(struct kvm_vcpu *vcpu)
@@ -9501,8 +9528,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
*/
WARN_ON_ONCE(ctxt->exception.vector == UD_VECTOR ||
exception_type(ctxt->exception.vector) == EXCPT_TRAP);
- inject_emulated_exception(vcpu);
- return 1;
+ return inject_emulated_exception(vcpu);
}
return handle_emulation_failure(vcpu, emulation_type);
}
@@ -9597,8 +9623,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
if (ctxt->have_exception) {
WARN_ON_ONCE(vcpu->mmio_needed && !vcpu->mmio_is_write);
vcpu->mmio_needed = false;
- r = 1;
- inject_emulated_exception(vcpu);
+ r = inject_emulated_exception(vcpu);
} else if (vcpu->arch.pio.count) {
if (!vcpu->arch.pio.in) {
/* FIXME: return into emulator if single-stepping. */
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 05/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when checking for code breakpoints in emulation
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
` (3 preceding siblings ...)
2026-05-15 22:26 ` [PATCH v3 04/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when emulating MOV DR (in emulator) Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 06/10] KVM: x86: Move KVM_GUESTDBG_SINGLESTEP handling into kvm_inject_emulated_db() Sean Christopherson
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
From: Hou Wenlong <houwenlong.hwl@antgroup.com>
When KVM_GUESTDBG_USE_HW_BP is enabled, i.e. userspace is usurping the
guest's hardware debug registers, the guest's effective breakpoints are
controlled by userspace rather than by the guest itself. Honor the
KVM_GUESTDBG_USE_HW_BP behavior when handling code #DBs in the emulator so
that userspace (and the guest) gets consistent behavior for code #DBs
regardless of whether an instruction is executed natively or emulated by
KVM.
To aid in userspace debug, don't treat code breakpoints as inhibited if
KVM_GUESTDBG_USE_HW_BP is enabled as accurately emulating x86 architecture
is obviously a non-goal of guest-debug.
Fixes: 4a1e10d5b5d8 ("KVM: x86: handle hardware breakpoints during emulation")
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
[sean: massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/x86.c | 35 ++++++++++-------------------------
1 file changed, 10 insertions(+), 25 deletions(-)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8b07bd2f8310..279e2734e088 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -9319,6 +9319,9 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_skip_emulated_instruction);
static bool kvm_is_code_breakpoint_inhibited(struct kvm_vcpu *vcpu)
{
+ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
+ return false;
+
if (kvm_get_rflags(vcpu) & X86_EFLAGS_RF)
return true;
@@ -9335,6 +9338,8 @@ static bool kvm_is_code_breakpoint_inhibited(struct kvm_vcpu *vcpu)
static bool kvm_vcpu_check_code_breakpoint(struct kvm_vcpu *vcpu,
int emulation_type, int *r)
{
+ unsigned long dr7 = kvm_get_eff_dr7(vcpu);
+
WARN_ON_ONCE(emulation_type & EMULTYPE_NO_DECODE);
/*
@@ -9355,34 +9360,14 @@ static bool kvm_vcpu_check_code_breakpoint(struct kvm_vcpu *vcpu,
EMULTYPE_TRAP_UD | EMULTYPE_VMWARE_GP | EMULTYPE_PF))
return false;
- if (unlikely(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) &&
- (vcpu->arch.guest_debug_dr7 & DR7_BP_EN_MASK)) {
- struct kvm_run *kvm_run = vcpu->run;
- unsigned long eip = kvm_get_linear_rip(vcpu);
- u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0,
- vcpu->arch.guest_debug_dr7,
- vcpu->arch.eff_db);
-
- if (dr6 != 0) {
- kvm_run->debug.arch.dr6 = dr6 | DR6_ACTIVE_LOW;
- kvm_run->debug.arch.pc = eip;
- kvm_run->debug.arch.exception = DB_VECTOR;
- kvm_run->exit_reason = KVM_EXIT_DEBUG;
- *r = 0;
- return true;
- }
- }
-
- if (unlikely(vcpu->arch.dr7 & DR7_BP_EN_MASK) &&
+ if (unlikely(dr7 & DR7_BP_EN_MASK) &&
!kvm_is_code_breakpoint_inhibited(vcpu)) {
unsigned long eip = kvm_get_linear_rip(vcpu);
- u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0,
- vcpu->arch.dr7,
- vcpu->arch.db);
+ u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0, dr7,
+ vcpu->arch.eff_db);
- if (dr6 != 0) {
- kvm_queue_exception_p(vcpu, DB_VECTOR, dr6);
- *r = 1;
+ if (dr6) {
+ *r = kvm_inject_emulated_db(vcpu, dr6);
return true;
}
}
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 06/10] KVM: x86: Move KVM_GUESTDBG_SINGLESTEP handling into kvm_inject_emulated_db()
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
` (4 preceding siblings ...)
2026-05-15 22:26 ` [PATCH v3 05/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when checking for code breakpoints in emulation Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 07/10] KVM: x86: Drop kvm_vcpu_do_singlestep() now that it's been gutted Sean Christopherson
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
From: Hou Wenlong <houwenlong.hwl@antgroup.com>
Move KVM_GUESTDBG_SINGLESTEP handling from kvm_vcpu_do_singlestep() into
kvm_inject_emulated_db() to dedup the USE_HW_BP vs. SINGLESTEP logic, and
to allow for removing kvm_vcpu_do_singlestep() entirely.
No functional change intended.
Suggested-by: Lai Jiangshan <jiangshan.ljs@antgroup.com>
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
[sean: massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/x86.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 279e2734e088..ca30a8987f2f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8994,7 +8994,7 @@ static int kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6)
{
struct kvm_run *kvm_run = vcpu->run;
- if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) {
+ if (vcpu->guest_debug & (KVM_GUESTDBG_USE_HW_BP | KVM_GUESTDBG_SINGLESTEP)) {
kvm_run->debug.arch.dr6 = dr6 | DR6_ACTIVE_LOW;
kvm_run->debug.arch.pc = kvm_get_linear_rip(vcpu);
kvm_run->debug.arch.exception = DB_VECTOR;
@@ -9279,17 +9279,7 @@ static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 dr7,
static int kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu)
{
- struct kvm_run *kvm_run = vcpu->run;
-
- if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
- kvm_run->debug.arch.dr6 = DR6_BS | DR6_ACTIVE_LOW;
- kvm_run->debug.arch.pc = kvm_get_linear_rip(vcpu);
- kvm_run->debug.arch.exception = DB_VECTOR;
- kvm_run->exit_reason = KVM_EXIT_DEBUG;
- return 0;
- }
- kvm_queue_exception_p(vcpu, DB_VECTOR, DR6_BS);
- return 1;
+ return kvm_inject_emulated_db(vcpu, DR6_BS);
}
int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu)
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 07/10] KVM: x86: Drop kvm_vcpu_do_singlestep() now that it's been gutted
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
` (5 preceding siblings ...)
2026-05-15 22:26 ` [PATCH v3 06/10] KVM: x86: Move KVM_GUESTDBG_SINGLESTEP handling into kvm_inject_emulated_db() Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 08/10] KVM: selftests: Add all (known) EFLAGS bit definitions Sean Christopherson
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
Now that all of kvm_vcpu_do_singlestep()'s previously-unique functionality
has been moved into kvm_inject_emulated_db(), drop the one-line wrapper.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/x86.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ca30a8987f2f..758b99b2fa7f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -133,7 +133,6 @@ static void process_nmi(struct kvm_vcpu *vcpu);
static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags);
static void store_regs(struct kvm_vcpu *vcpu);
static int sync_regs(struct kvm_vcpu *vcpu);
-static int kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu);
static int __set_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2);
static void __get_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2);
@@ -9277,11 +9276,6 @@ static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 dr7,
return dr6;
}
-static int kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu)
-{
- return kvm_inject_emulated_db(vcpu, DR6_BS);
-}
-
int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu)
{
unsigned long rflags = kvm_x86_call(get_rflags)(vcpu);
@@ -9302,7 +9296,7 @@ int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu)
* that sets the TF flag".
*/
if (unlikely(rflags & X86_EFLAGS_TF))
- r = kvm_vcpu_do_singlestep(vcpu);
+ r = kvm_inject_emulated_db(vcpu, DR6_BS);
return r;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_skip_emulated_instruction);
@@ -9641,7 +9635,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
kvm_pmu_branch_retired(vcpu);
kvm_rip_write(vcpu, ctxt->eip);
if (r && (ctxt->tf || (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)))
- r = kvm_vcpu_do_singlestep(vcpu);
+ r = kvm_inject_emulated_db(vcpu, DR6_BS);
kvm_x86_call(update_emulated_instruction)(vcpu);
__kvm_set_rflags(vcpu, ctxt->eflags);
}
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 08/10] KVM: selftests: Add all (known) EFLAGS bit definitions
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
` (6 preceding siblings ...)
2026-05-15 22:26 ` [PATCH v3 07/10] KVM: x86: Drop kvm_vcpu_do_singlestep() now that it's been gutted Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 09/10] KVM: selftests: Verify guest debug DR7.GD checking during instruction emulation Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 10/10] KVM: selftests: Verify VMX's GUEST_PENDING_DBG_EXCEPTIONS.BS Consistency Check Sean Christopherson
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
Add #defines for all known EFLAGS bit, e.g. so that tests can use things
like EFLAGS.TF to validate single-stepping behavior. Opportunistically
use X86_EFLAGS_FIXED instead of an open-coded equivalent when stuffing
initial vCPU state.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
.../selftests/kvm/include/x86/processor.h | 19 ++++++++++++++++++-
.../testing/selftests/kvm/lib/x86/processor.c | 2 +-
tools/testing/selftests/kvm/lib/x86/vmx.c | 2 +-
3 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 77f576ee7789..851ffcd3340c 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -38,7 +38,24 @@ extern u64 guest_tsc_khz;
const char *ex_str(int vector);
-#define X86_EFLAGS_FIXED (1u << 1)
+#define X86_EFLAGS_CF BIT(0) /* Carry Flag */
+#define X86_EFLAGS_FIXED BIT(1) /* Bit 1 - always on */
+#define X86_EFLAGS_PF BIT(2) /* Parity Flag */
+#define X86_EFLAGS_AF BIT(4) /* Auxiliary carry Flag */
+#define X86_EFLAGS_ZF BIT(6) /* Zero Flag */
+#define X86_EFLAGS_SF BIT(7) /* Sign Flag */
+#define X86_EFLAGS_TF BIT(8) /* Trap Flag */
+#define X86_EFLAGS_IF BIT(9) /* Interrupt Flag */
+#define X86_EFLAGS_DF BIT(10) /* Direction Flag */
+#define X86_EFLAGS_OF BIT(11) /* Overflow Flag */
+#define X86_EFLAGS_IOPL BIT(12) /* I/O Privilege Level (2 bits) */
+#define X86_EFLAGS_NT BIT(14) /* Nested Task */
+#define X86_EFLAGS_RF BIT(16) /* Resume Flag */
+#define X86_EFLAGS_VM BIT(17) /* Virtual Mode */
+#define X86_EFLAGS_AC BIT(18) /* Alignment Check/Access Control */
+#define X86_EFLAGS_VIF BIT(19) /* Virtual Interrupt Flag */
+#define X86_EFLAGS_VIP BIT(20) /* Virtual Interrupt Pending */
+#define X86_EFLAGS_ID BIT(21) /* CPUID detection */
#define X86_CR4_VME (1ul << 0)
#define X86_CR4_PVI (1ul << 1)
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index b51467d70f6e..4ca48de7a926 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -848,7 +848,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
/* Setup guest general purpose registers */
vcpu_regs_get(vcpu, ®s);
- regs.rflags = regs.rflags | 0x2;
+ regs.rflags = regs.rflags | X86_EFLAGS_FIXED;
regs.rsp = stack_gva;
vcpu_regs_set(vcpu, ®s);
diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c
index 67642759e4a0..7c10ba6e6fb4 100644
--- a/tools/testing/selftests/kvm/lib/x86/vmx.c
+++ b/tools/testing/selftests/kvm/lib/x86/vmx.c
@@ -360,7 +360,7 @@ static inline void init_vmcs_guest_state(void *rip, void *rsp)
vmwrite(GUEST_DR7, 0x400);
vmwrite(GUEST_RSP, (u64)rsp);
vmwrite(GUEST_RIP, (u64)rip);
- vmwrite(GUEST_RFLAGS, 2);
+ vmwrite(GUEST_RFLAGS, X86_EFLAGS_FIXED);
vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, 0);
vmwrite(GUEST_SYSENTER_ESP, vmreadz(HOST_IA32_SYSENTER_ESP));
vmwrite(GUEST_SYSENTER_EIP, vmreadz(HOST_IA32_SYSENTER_EIP));
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 09/10] KVM: selftests: Verify guest debug DR7.GD checking during instruction emulation
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
` (7 preceding siblings ...)
2026-05-15 22:26 ` [PATCH v3 08/10] KVM: selftests: Add all (known) EFLAGS bit definitions Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 10/10] KVM: selftests: Verify VMX's GUEST_PENDING_DBG_EXCEPTIONS.BS Consistency Check Sean Christopherson
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
From: Hou Wenlong <houwenlong.hwl@antgroup.com>
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>
Signed-off-by: Sean Christopherson <seanjc@google.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 0dfaf03cd0a0..ee9d0f3a5807 100644
--- a/tools/testing/selftests/kvm/x86/debug_regs.c
+++ b/tools/testing/selftests/kvm/x86/debug_regs.c
@@ -19,6 +19,7 @@
u32 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.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 10/10] KVM: selftests: Verify VMX's GUEST_PENDING_DBG_EXCEPTIONS.BS Consistency Check
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
` (8 preceding siblings ...)
2026-05-15 22:26 ` [PATCH v3 09/10] KVM: selftests: Verify guest debug DR7.GD checking during instruction emulation Sean Christopherson
@ 2026-05-15 22:26 ` Sean Christopherson
9 siblings, 0 replies; 11+ messages in thread
From: Sean Christopherson @ 2026-05-15 22:26 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Hou Wenlong, Lai Jiangshan
From: Hou Wenlong <houwenlong.hwl@antgroup.com>
In x86's debug_regs test, add a test case to cover the scenario where a
single-step #DB occurs in an STI-shadow, in which case KVM needs to stuff
vmcs.GUEST_PENDING_DBG_EXCEPTIONS.BS in order to satisfy a flawed VM-Entry
Consistency Check.
Wire up an IRQ handler to gain a bit of bonus coverage, as the subsequent
IRET from the #DB sets RFLAGS.IF, but *without* STI-blocking, and so the
pending IRQ is expected on the instruction immediately following STI.
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
[sean: expect the IRQ on the CLI, and explain why]
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
tools/testing/selftests/kvm/x86/debug_regs.c | 64 ++++++++++++++++++--
1 file changed, 60 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c
index ee9d0f3a5807..6299e921dc27 100644
--- a/tools/testing/selftests/kvm/x86/debug_regs.c
+++ b/tools/testing/selftests/kvm/x86/debug_regs.c
@@ -15,11 +15,46 @@
#define IRQ_VECTOR 0xAA
+#define CAST_TO_RIP(v) ((unsigned long long)&(v))
+
/* For testing data access debug BP */
u32 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[%u]: unexpected rip 0x%lx (should be 0x%lx)",
+ count, regs->rip, target_rips[count]);
+ regs->rflags &= ~X86_EFLAGS_TF;
+ count++;
+}
+
+static void guest_irq_handler(struct ex_regs *regs)
+{
+ /*
+ * The pending IRQ should finally be take when KVM_GUESTDBG_BLOCKIRQ is
+ * cleared and IRQs are enabled. Note, the IRQ is expected to arrive
+ * on the instruction immediately after STI, even though its in an STI
+ * shadow. Because the next instruction has a coincident #DB, and #DBs
+ * are not subject to STI-blocking, the #DB will push RFLAGS.IF=1 on
+ * the stack, and the eventual IRET will unmask IRQs and obliterate the
+ * STI shadow in the process.
+ */
+ unsigned long target_rip = CAST_TO_RIP(fep_sti_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 +101,32 @@ static void guest_code(void)
/* DR6.BD test */
asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax");
- if (is_forced_emulation_enabled)
+ /*
+ * Note, the IRET from the #DB that occurs in the below STI-shadow will
+ * unmask IRQs, i.e. the pending interrupt will be delivered after #DB
+ * handling, on the CLI!
+ */
+ 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 +280,9 @@ int main(void)
memset(&debug, 0, sizeof(debug));
vcpu_guest_debug_set(vcpu, &debug);
+ vm_install_exception_handler(vm, DB_VECTOR, guest_db_handler);
+ 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.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-05-15 22:26 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 22:26 [PATCH v3 00/10] KVM: x86: Improve #DB handling in the emulator Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 01/10] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 02/10] KVM: x86: Capture "struct x86_exception" in inject_emulated_exception() Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 03/10] KVM: x86: Set guest DR6 by kvm_queue_exception_p() in instruction emulation Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 04/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when emulating MOV DR (in emulator) Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 05/10] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when checking for code breakpoints in emulation Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 06/10] KVM: x86: Move KVM_GUESTDBG_SINGLESTEP handling into kvm_inject_emulated_db() Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 07/10] KVM: x86: Drop kvm_vcpu_do_singlestep() now that it's been gutted Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 08/10] KVM: selftests: Add all (known) EFLAGS bit definitions Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 09/10] KVM: selftests: Verify guest debug DR7.GD checking during instruction emulation Sean Christopherson
2026-05-15 22:26 ` [PATCH v3 10/10] KVM: selftests: Verify VMX's GUEST_PENDING_DBG_EXCEPTIONS.BS Consistency Check Sean Christopherson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox