Commit 7d42b0a4f5 for qemu.org
commit 7d42b0a4f52d57cd2d9e0a9ceb8ad5102219a801
Author: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Date: Fri Jul 31 21:46:41 2026 +0300
target/i386/mshv: support single-stepping
Single-step by toggling RFLAGS.TF around the vCPU run, as WHPX does.
TF is set only on the live register, never in env->eflags, so it is not read
back and re-applied by a later register store.
The resulting #DB is reported to gdb by mshv_handle_debug() whenever the vCPU
is single-stepping.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 0670e7a5ae..4df73bb497 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -2091,17 +2091,67 @@ static int handle_exception_interrupt(CPUState *cpu,
return 0;
}
+/*
+ * Flip RFLAGS.TF like WHPX. Set it only on the live register, not env->eflags,
+ * so a later store won't put it back.
+ */
+static int arch_set_single_step(CPUState *cpu, bool enable)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ hv_register_assoc assoc = { .name = HV_X64_REGISTER_RFLAGS };
+ uint64_t rflags;
+ int ret;
+
+ if (env->regs_page && env->regs_page->isvalid != 0) {
+ rflags = env->regs_page->rflags;
+ rflags = enable ? (rflags | TF_MASK) : (rflags & ~TF_MASK);
+ env->regs_page->rflags = rflags;
+ env->regs_page->dirty |= (1u << HV_X64_REGISTER_CLASS_FLAGS);
+ return 0;
+ }
+
+ ret = mshv_get_generic_regs(cpu, &assoc, 1);
+ if (ret < 0) {
+ return ret;
+ }
+ rflags = assoc.value.reg64;
+ rflags = enable ? (rflags | TF_MASK) : (rflags & ~TF_MASK);
+ assoc.value.reg64 = rflags;
+ return mshv_set_generic_regs(cpu, &assoc, 1);
+}
+
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
{
int ret;
enum MshvVmExit exit_reason;
int cpu_fd = mshv_vcpufd(cpu);
+ bool single_step;
+
+ /* enable single stepping by flipping RFLAGS.TF */
+ single_step = cpu_single_stepping(cpu);
+ if (single_step) {
+ ret = arch_set_single_step(cpu, true);
+ if (ret < 0) {
+ error_report("Failed to arm single-step (TF) on vcpu %d: %s",
+ cpu->cpu_index, strerror(-ret));
+ *exit = MshvVmExitShutdown;
+ return -1;
+ }
+ }
ret = ioctl(cpu_fd, MSHV_RUN_VP, msg);
if (ret < 0) {
return MshvVmExitShutdown;
}
+ /* disable single stepping by flipping RFLAGS.TF */
+ if (single_step && arch_set_single_step(cpu, false) < 0) {
+ error_report("Failed to clear single-step (TF) on vcpu %d",
+ cpu->cpu_index);
+ return -1;
+ }
+
switch (msg->header.message_type) {
case HVMSG_UNRECOVERABLE_EXCEPTION:
return MshvVmExitShutdown;