* [PATCH 1/3] include/hw/hyperv: add ABI for exception intercepts and pending events
2026-07-27 14:28 [PATCH 0/3] accel/mshv: add gdbstub guest debugging support Doru Blânzeanu
@ 2026-07-27 14:28 ` Doru Blânzeanu
2026-07-27 14:28 ` [PATCH 2/3] accel/mshv: add gdbstub software breakpoint support Doru Blânzeanu
2026-07-27 14:28 ` [PATCH 3/3] target/i386/mshv: support single-stepping Doru Blânzeanu
2 siblings, 0 replies; 4+ messages in thread
From: Doru Blânzeanu @ 2026-07-27 14:28 UTC (permalink / raw)
To: qemu-devel
Cc: Magnus Kulke, Doru Blânzeanu, Doru Blânzeanu, Wei Liu,
Wei Liu, Magnus Kulke
Import the hypervisor definitions needed to intercept guest exceptions
and to inject events back into a guest, in preparation for gdbstub
debugging support in the MSHV accelerator.
Add:
- union hv_intercept_parameters, struct hv_input_install_intercept and
HVCALL_INSTALL_INTERCEPT, used to install an exception intercept on
the partition;
- struct hv_x64_exception_intercept_message, the message delivered on
an intercepted exception, carrying the faulting RIP, the exception
vector and the instruction bytes;
- HV_X64_PENDING_EVENT_EXCEPTION, the pending-event type for an
exception.
These definitions mirror the hypervisor headers and have no functional
effect on their own.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
include/hw/hyperv/hvgdk_mini.h | 56 ++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index f8838a31bb..c81928d0c2 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -22,6 +22,9 @@
#define HV_X64_MSR_TSC_FREQUENCY 0x40000022
#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
+/* event_type values for hv_x64_pending_exception_event */
+#define HV_X64_PENDING_EVENT_EXCEPTION 0
+
typedef enum hv_register_name {
/* Pending Interruption Register */
HV_REGISTER_PENDING_INTERRUPTION = 0x00010002,
@@ -236,6 +239,29 @@ enum hv_intercept_type {
HV_INTERCEPT_TYPE_INVALID = 0XFFFFFFFF,
};
+union hv_intercept_parameters {
+ /* HV_INTERCEPT_PARAMETERS is defined to be an 8-byte field. */
+ uint64_t as_uint64;
+ /* HV_INTERCEPT_TYPE_X64_IO_PORT */
+ uint16_t io_port;
+ /* HV_INTERCEPT_TYPE_X64_CPUID */
+ uint32_t cpuid_index;
+ /* HV_INTERCEPT_TYPE_X64_APIC_WRITE */
+ uint32_t apic_write_mask;
+ /* HV_INTERCEPT_TYPE_EXCEPTION */
+ uint16_t exception_vector;
+ /* HV_INTERCEPT_TYPE_X64_MSR_INDEX */
+ uint32_t msr_index;
+ /* N.B. Other intercept types do not have any parameters. */
+};
+
+struct hv_input_install_intercept {
+ uint64_t partition_id;
+ uint32_t access_type; /* mask */
+ uint32_t intercept_type; /* enum hv_intercept_type */
+ union hv_intercept_parameters intercept_parameter;
+} QEMU_PACKED;
+
struct hv_u128 {
uint64_t low_part;
uint64_t high_part;
@@ -836,6 +862,35 @@ struct hv_x64_memory_intercept_message {
uint8_t instruction_bytes[16];
} QEMU_PACKED;
+struct hv_x64_exception_intercept_message {
+ struct hv_x64_intercept_message_header header;
+ uint16_t exception_vector;
+ uint8_t exception_info;
+ uint8_t instruction_byte_count;
+ uint32_t error_code;
+ uint64_t exception_parameter; /* DR6 for #DB, CR2 for #PF */
+ uint64_t reserved;
+ uint8_t instruction_bytes[16];
+ struct hv_x64_segment_register ds_segment;
+ struct hv_x64_segment_register ss_segment;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rbx;
+ uint64_t rsp;
+ uint64_t rbp;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+} QEMU_PACKED;
+
union hv_message_flags {
uint8_t asu8;
struct {
@@ -943,6 +998,7 @@ struct hv_cpuid {
#define HVCALL_GET_PARTITION_PROPERTY 0x0044
#define HVCALL_SET_PARTITION_PROPERTY 0x0045
+#define HVCALL_INSTALL_INTERCEPT 0x004d
#define HVCALL_GET_VP_REGISTERS 0x0050
#define HVCALL_SET_VP_REGISTERS 0x0051
#define HVCALL_TRANSLATE_VIRTUAL_ADDRESS 0x0052
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 2/3] accel/mshv: add gdbstub software breakpoint support
2026-07-27 14:28 [PATCH 0/3] accel/mshv: add gdbstub guest debugging support Doru Blânzeanu
2026-07-27 14:28 ` [PATCH 1/3] include/hw/hyperv: add ABI for exception intercepts and pending events Doru Blânzeanu
@ 2026-07-27 14:28 ` Doru Blânzeanu
2026-07-27 14:28 ` [PATCH 3/3] target/i386/mshv: support single-stepping Doru Blânzeanu
2 siblings, 0 replies; 4+ messages in thread
From: Doru Blânzeanu @ 2026-07-27 14:28 UTC (permalink / raw)
To: qemu-devel
Cc: Magnus Kulke, Doru Blânzeanu, Doru Blânzeanu, Wei Liu,
Wei Liu, Magnus Kulke
Implement software breakpoint based guest debugging for the MSHV
accelerator, modeled on the WHPX backend. The guest-visible debug
registers are not used: a breakpoint is an INT1 (opcode 0xf1)
patched into guest memory, which raises a #DB when executed.
A partition-wide intercept on #DB is installed the first time a
breakpoint (or single step) is requested. The intercept is not uninstalled,
so it stays for the lifetime of the VM and every vmexit mshv_handle_debug()
decides whether the #DB belongs to gdb (it hit one of our INT1 breakpoints) or
to the guest; a guest-owned #DB is handed back through a pending exception
event so the guest takes it through its own IDT.
Guest INT3 (#BP) is deliberately left to the guest's own IDT and is never
intercepted.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
accel/mshv/mshv-all.c | 165 +++++++++++++++++++++++++++++++++++-
include/system/mshv_int.h | 14 +++
target/i386/mshv/mshv-cpu.c | 86 +++++++++++++++++++
3 files changed, 264 insertions(+), 1 deletion(-)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 72721d0f0d..afcb87b3b8 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -29,6 +29,7 @@
#include "qemu/accel.h"
#include "qemu/guest-random.h"
+#include "gdbstub/enums.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "exec/cpu-common.h"
@@ -583,6 +584,10 @@ static int mshv_init(AccelState *as, MachineState *ms)
s->nr_as = 1;
s->as = g_new0(MshvAddressSpace, s->nr_as);
+ QTAILQ_INIT(&s->sw_breakpoints);
+
+ as->gdbstub.sstep_flags = SSTEP_ENABLE;
+
mshv_state = s;
mshv_init_irq_routing(s);
@@ -607,6 +612,153 @@ static int mshv_destroy_vcpu(CPUState *cpu)
return 0;
}
+struct MshvSwBreakpoint *mshv_find_sw_breakpoint(CPUState *cpu, vaddr pc)
+{
+ struct MshvSwBreakpoint *bp;
+
+ QTAILQ_FOREACH(bp, &mshv_state->sw_breakpoints, entry) {
+ if (bp->pc == pc) {
+ return bp;
+ }
+ }
+ return NULL;
+}
+
+static int mshv_install_exception_intercept(int vm_fd, uint16_t vector)
+{
+ struct hv_input_install_intercept in = {0};
+ struct mshv_root_hvcall args = {0};
+ int ret;
+
+ in.access_type = 1 << HV_X64_INTERCEPT_ACCESS_TYPE_EXECUTE;
+ in.intercept_type = HV_INTERCEPT_TYPE_EXCEPTION;
+ in.intercept_parameter.as_uint64 = vector;
+
+ args.code = HVCALL_INSTALL_INTERCEPT;
+ args.in_sz = sizeof(in);
+ args.in_ptr = (uint64_t)∈
+
+ ret = mshv_hvcall(vm_fd, &args);
+ if (ret < 0) {
+ error_report("Failed to install exception intercept for vector %u",
+ vector);
+ }
+ return ret;
+}
+
+/*
+ * Install the #DB intercept. This is a permanent, partition-wide latch: it is
+ * installed once on the first debug use and stays for the lifetime of the VM.
+ */
+static int mshv_init_exception_intercept(void)
+{
+ int ret;
+
+ if (mshv_state->exception_intercepts_installed) {
+ return 0;
+ }
+
+ /* Only #DB is needed: gdb breakpoints are INT1 (0xf1) */
+ ret = mshv_install_exception_intercept(mshv_state->vm, 1); /* #DB */
+ if (ret < 0) {
+ return ret;
+ }
+
+ mshv_state->exception_intercepts_installed = true;
+ return 0;
+}
+
+static int mshv_update_guest_debug(CPUState *cpu)
+{
+ if (cpu_single_stepping(cpu)) {
+ /* Installs exception intercept only on the first call */
+ return mshv_init_exception_intercept();
+ }
+ return 0;
+}
+
+static int mshv_insert_gdbstub_breakpoint(CPUState *cpu, GdbBreakpointType type,
+ vaddr addr, vaddr len)
+{
+ struct MshvSwBreakpoint *bp;
+ int err;
+
+ /* We don't use the guest's debug registers. */
+ if (type != GDB_BREAKPOINT_SW) {
+ return -ENOSYS;
+ }
+
+ bp = mshv_find_sw_breakpoint(cpu, addr);
+ if (bp) {
+ bp->use_count++;
+ return 0;
+ }
+
+ bp = g_new(struct MshvSwBreakpoint, 1);
+ bp->pc = addr;
+ bp->use_count = 1;
+ err = mshv_arch_insert_sw_breakpoint(cpu, bp);
+ if (err) {
+ g_free(bp);
+ return err;
+ }
+
+ QTAILQ_INSERT_HEAD(&mshv_state->sw_breakpoints, bp, entry);
+
+ /* Installs exception intercept only on the first call */
+ return mshv_init_exception_intercept();
+}
+
+static int mshv_remove_gdbstub_breakpoint(CPUState *cpu, GdbBreakpointType type,
+ vaddr addr, vaddr len)
+{
+ struct MshvSwBreakpoint *bp;
+ int err;
+
+ if (type != GDB_BREAKPOINT_SW) {
+ return -ENOSYS;
+ }
+
+ bp = mshv_find_sw_breakpoint(cpu, addr);
+ if (!bp) {
+ return -ENOENT;
+ }
+
+ if (bp->use_count > 1) {
+ bp->use_count--;
+ return 0;
+ }
+
+ err = mshv_arch_remove_sw_breakpoint(cpu, bp);
+ if (err) {
+ return err;
+ }
+
+ QTAILQ_REMOVE(&mshv_state->sw_breakpoints, bp, entry);
+ g_free(bp);
+
+ return 0;
+}
+
+static void mshv_remove_all_gdbstub_breakpoints(CPUState *cpu)
+{
+ struct MshvSwBreakpoint *bp, *next;
+ CPUState *tmpcpu;
+
+ QTAILQ_FOREACH_SAFE(bp, &mshv_state->sw_breakpoints, entry, next) {
+ if (mshv_arch_remove_sw_breakpoint(cpu, bp) != 0) {
+ /* Fall back to whichever CPU still has it mapped. */
+ CPU_FOREACH(tmpcpu) {
+ if (mshv_arch_remove_sw_breakpoint(tmpcpu, bp) == 0) {
+ break;
+ }
+ }
+ }
+ QTAILQ_REMOVE(&mshv_state->sw_breakpoints, bp, entry);
+ g_free(bp);
+ }
+}
+
static int mshv_cpu_exec(CPUState *cpu)
{
hv_message mshv_msg;
@@ -637,6 +789,9 @@ static int mshv_cpu_exec(CPUState *cpu)
switch (exit_reason) {
case MshvVmExitIgnore:
break;
+ case MshvVmExitDebug:
+ ret = EXCP_DEBUG;
+ break;
default:
ret = EXCP_INTERRUPT;
break;
@@ -708,7 +863,10 @@ static void *mshv_vcpu_thread(void *arg)
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
- mshv_cpu_exec(cpu);
+ ret = mshv_cpu_exec(cpu);
+ if (ret == EXCP_DEBUG) {
+ cpu_handle_guest_debug(cpu);
+ }
}
} while (!cpu->unplug || cpu_can_run(cpu));
@@ -852,6 +1010,11 @@ static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm;
ops->cpus_are_resettable = mshv_cpus_are_resettable;
ops->handle_interrupt = generic_handle_interrupt;
+
+ ops->update_guest_debug = mshv_update_guest_debug;
+ ops->insert_gdbstub_breakpoint = mshv_insert_gdbstub_breakpoint;
+ ops->remove_gdbstub_breakpoint = mshv_remove_gdbstub_breakpoint;
+ ops->remove_all_gdbstub_breakpoints = mshv_remove_all_gdbstub_breakpoints;
}
static const TypeInfo mshv_accel_ops_type = {
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index b91c4d661a..9244915eb6 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -15,6 +15,7 @@
#define QEMU_MSHV_INT_H
#include "hw/hyperv/hvhdk.h"
+#include "exec/vaddr.h"
#define MSHV_MSR_ENTRIES_COUNT 64
@@ -56,6 +57,13 @@ typedef struct MshvAddressSpace {
AddressSpace *as;
} MshvAddressSpace;
+struct MshvSwBreakpoint {
+ vaddr pc;
+ vaddr saved_insn;
+ int use_count;
+ QTAILQ_ENTRY(MshvSwBreakpoint) entry;
+};
+
struct MshvState {
AccelState parent_obj;
int vm;
@@ -70,6 +78,8 @@ struct MshvState {
unsigned long *used_gsi_bitmap;
unsigned int gsi_count;
union hv_partition_processor_features processor_features;
+ QTAILQ_HEAD(, MshvSwBreakpoint) sw_breakpoints;
+ bool exception_intercepts_installed;
};
typedef struct MshvMsiControl {
@@ -85,6 +95,7 @@ typedef enum MshvVmExit {
MshvVmExitIgnore = 0,
MshvVmExitShutdown = 1,
MshvVmExitSpecial = 2,
+ MshvVmExitDebug = 3,
} MshvVmExit;
void mshv_init_mmio_emu(void);
@@ -98,6 +109,9 @@ int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
size_t n_regs);
int mshv_arch_store_vcpu_state(const CPUState *cpu);
int mshv_arch_load_vcpu_state(CPUState *cpu);
+int mshv_arch_insert_sw_breakpoint(CPUState *cpu, struct MshvSwBreakpoint *bp);
+int mshv_arch_remove_sw_breakpoint(CPUState *cpu, struct MshvSwBreakpoint *bp);
+struct MshvSwBreakpoint *mshv_find_sw_breakpoint(CPUState *cpu, vaddr pc);
void mshv_arch_init_vcpu(CPUState *cpu);
void mshv_arch_destroy_vcpu(CPUState *cpu);
void mshv_arch_amend_proc_features(
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 1c433c408c..2333d3304a 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -12,6 +12,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
#include "qemu/memalign.h"
#include "system/mshv.h"
@@ -28,6 +29,7 @@
#include "emulate/x86_decode.h"
#include "emulate/x86_emu.h"
#include "emulate/x86_flags.h"
+#include "gdbstub/enums.h"
#include "accel/accel-cpu-target.h"
@@ -1932,6 +1934,46 @@ static int handle_pio(CPUState *cpu, const struct hyperv_message *msg)
return handle_pio_non_str(cpu, &info);
}
+/* Re-inject a guest-owned #DB via a pending event */
+static int reinject_exception(CPUState *cpu,
+ struct hv_x64_exception_intercept_message *info)
+{
+ hv_register_assoc assoc = { .name = HV_REGISTER_PENDING_EVENT0 };
+
+ assoc.value.pending_exception_event.event_pending = 1;
+ assoc.value.pending_exception_event.event_type =
+ HV_X64_PENDING_EVENT_EXCEPTION;
+ assoc.value.pending_exception_event.exception_parameter =
+ info->exception_parameter;
+ assoc.value.pending_exception_event.vector = info->exception_vector;
+
+ return mshv_set_generic_regs(cpu, &assoc, 1);
+}
+
+/* Check if the intercepted #DB has been set by gdb. */
+static int handle_debug(CPUState *cpu, hv_message *msg)
+{
+ struct hv_x64_exception_intercept_message *info = (void *)msg->payload;
+
+ /* Only #DB is intercepted */
+ if (info->exception_vector != EXCP01_DB) {
+ return 0;
+ }
+
+ /* INT1 hit: RIP points at the breakpoint */
+ if (mshv_find_sw_breakpoint(cpu, info->header.rip) != NULL) {
+ return EXCP_DEBUG;
+ }
+
+ /* Check if single stepping */
+ if (cpu_single_stepping(cpu)) {
+ return EXCP_DEBUG;
+ }
+
+ /* The guest's own #DB; caller re-injects it. */
+ return 0;
+}
+
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
{
int ret;
@@ -1960,6 +2002,24 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
return MshvVmExitSpecial;
}
return MshvVmExitIgnore;
+ case HVMSG_X64_EXCEPTION_INTERCEPT:
+ bql_lock();
+ if (handle_debug(cpu, msg) == EXCP_DEBUG) {
+ *exit = MshvVmExitDebug;
+ bql_unlock();
+ return 0;
+ }
+ /* Not ours - hand the #DB back to the guest and keep running. */
+ ret = reinject_exception(cpu,
+ (struct hv_x64_exception_intercept_message *)msg->payload);
+ bql_unlock();
+ if (ret < 0) {
+ error_report("failed to reinject exception on vcpu %d",
+ cpu->cpu_index);
+ return -1;
+ }
+ *exit = MshvVmExitIgnore;
+ return 0;
default:
break;
}
@@ -1973,6 +2033,32 @@ void mshv_remove_vcpu(int vm_fd, int cpu_fd)
close(cpu_fd);
}
+int mshv_arch_insert_sw_breakpoint(CPUState *cpu, struct MshvSwBreakpoint *bp)
+{
+ static const uint8_t int1 = 0xf1;
+
+ if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) ||
+ cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&int1, 1, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int mshv_arch_remove_sw_breakpoint(CPUState *cpu, struct MshvSwBreakpoint *bp)
+{
+ uint8_t int1;
+
+ if (cpu_memory_rw_debug(cpu, bp->pc, &int1, 1, 0)) {
+ return -EINVAL;
+ }
+ if (int1 != 0xf1) {
+ return 0;
+ }
+ if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+}
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd)
{
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 3/3] target/i386/mshv: support single-stepping
2026-07-27 14:28 [PATCH 0/3] accel/mshv: add gdbstub guest debugging support Doru Blânzeanu
2026-07-27 14:28 ` [PATCH 1/3] include/hw/hyperv: add ABI for exception intercepts and pending events Doru Blânzeanu
2026-07-27 14:28 ` [PATCH 2/3] accel/mshv: add gdbstub software breakpoint support Doru Blânzeanu
@ 2026-07-27 14:28 ` Doru Blânzeanu
2 siblings, 0 replies; 4+ messages in thread
From: Doru Blânzeanu @ 2026-07-27 14:28 UTC (permalink / raw)
To: qemu-devel
Cc: Magnus Kulke, Doru Blânzeanu, Doru Blânzeanu, Wei Liu,
Wei Liu, Magnus Kulke
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>
---
target/i386/mshv/mshv-cpu.c | 50 +++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 2333d3304a..036b142113 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1974,17 +1974,67 @@ static int handle_debug(CPUState *cpu, hv_message *msg)
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;
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread