* [PULL 01/11] target/i386/mshv: fix read/write memory across the page boundary
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
@ 2026-10-06 16:48 ` Magnus Kulke
2026-10-06 16:48 ` [PULL 02/11] include/hw/hyperv: move hv_vp_register_page struct definition Magnus Kulke
` (11 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:48 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Previously, read_memory and write_memory performed a single GVA-to-GPA
translation for the entire buffer. If the buffer spanned a page
boundary, the translated GPA was only valid for the first page, causing
incorrect reads/writes for the remainder.
Fix both functions to loop over pages, translating and accessing each
page-aligned chunk separately.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Fixes: 6dec60528c (target/i386/mshv: Implement mshv_vcpu_run())
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
---
target/i386/mshv/mshv-cpu.c | 69 +++++++++++++++++++++++++++----------
1 file changed, 51 insertions(+), 18 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index f528dd2b9a..b8a6924088 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1831,21 +1831,38 @@ static int read_memory(const CPUState *cpu, uint64_t initial_gva,
{
int ret;
uint64_t gpa, flags;
+ uint64_t cur_gva = gva;
+ size_t page_left, chunk;
+ uint8_t *cur_data = data;
- if (gva == initial_gva) {
- gpa = initial_gpa;
- } else {
- flags = HV_TRANSLATE_GVA_VALIDATE_READ;
- ret = translate_gva(cpu, gva, &gpa, flags);
- if (ret < 0) {
- return -1;
+ /*
+ * If the read spans multiple pages,
+ * we need to translate and read each page separately
+ */
+ while (len > 0) {
+ page_left = HV_HYP_PAGE_SIZE - (cur_gva & (HV_HYP_PAGE_SIZE - 1));
+ chunk = MIN(len, page_left);
+
+ if (cur_gva == initial_gva) {
+ gpa = initial_gpa;
+ } else {
+ flags = HV_TRANSLATE_GVA_VALIDATE_READ;
+ ret = translate_gva(cpu, cur_gva, &gpa, flags);
+ if (ret < 0) {
+ return -1;
+ }
}
- ret = mshv_guest_mem_read(gpa, data, len, false, false);
+ ret = mshv_guest_mem_read(gpa, cur_data, chunk,
+ false, false);
if (ret < 0) {
error_report("failed to read guest mem");
return -1;
}
+
+ cur_gva += chunk;
+ cur_data += chunk;
+ len -= chunk;
}
return 0;
@@ -1856,18 +1873,34 @@ static int write_memory(const CPUState *cpu, uint64_t gva, const uint8_t *data,
{
int ret;
uint64_t gpa, flags;
+ uint64_t cur_gva = gva;
+ size_t page_left, chunk;
+ const uint8_t *cur_data = data;
- flags = HV_TRANSLATE_GVA_VALIDATE_WRITE;
- ret = translate_gva(cpu, gva, &gpa, flags);
- if (ret < 0) {
- error_report("failed to translate gva to gpa");
- return -1;
- }
+ /*
+ * If the write spans multiple pages,
+ * we need to translate and write each page separately
+ */
+ while (len > 0) {
+ page_left = HV_HYP_PAGE_SIZE - (cur_gva & (HV_HYP_PAGE_SIZE - 1));
+ chunk = MIN(len, page_left);
- ret = mshv_guest_mem_write(gpa, data, len, false);
- if (ret != MEMTX_OK) {
- error_report("failed to write to mmio");
- return -1;
+ flags = HV_TRANSLATE_GVA_VALIDATE_WRITE;
+ ret = translate_gva(cpu, cur_gva, &gpa, flags);
+ if (ret < 0) {
+ error_report("failed to translate gva to gpa");
+ return -1;
+ }
+
+ ret = mshv_guest_mem_write(gpa, cur_data, chunk, false);
+ if (ret != MEMTX_OK) {
+ error_report("failed to write to mmio");
+ return -1;
+ }
+
+ cur_gva += chunk;
+ cur_data += chunk;
+ len -= chunk;
}
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 02/11] include/hw/hyperv: move hv_vp_register_page struct definition
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
2026-10-06 16:48 ` [PULL 01/11] target/i386/mshv: fix read/write memory across the page boundary Magnus Kulke
@ 2026-10-06 16:48 ` Magnus Kulke
2026-10-06 16:48 ` [PULL 03/11] target/i386/mshv: abort when hv_vp_register_page setup fails Magnus Kulke
` (10 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:48 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Move the `hv_vp_register_page` structure definition from `hvgdk_mini.h` to
`hvhdk.h`.
Fixes: 8afb40b7fe
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>
---
include/hw/hyperv/hvgdk.h | 2 +
include/hw/hyperv/hvgdk_mini.h | 103 --------------------------------
include/hw/hyperv/hvhdk.h | 106 +++++++++++++++++++++++++++++++++
3 files changed, 108 insertions(+), 103 deletions(-)
diff --git a/include/hw/hyperv/hvgdk.h b/include/hw/hyperv/hvgdk.h
index 71161f477c..e4be861716 100644
--- a/include/hw/hyperv/hvgdk.h
+++ b/include/hw/hyperv/hvgdk.h
@@ -9,6 +9,8 @@
#ifndef HW_HYPERV_HVGDK_H
#define HW_HYPERV_HVGDK_H
+#include "hvgdk_mini.h"
+
#define HVGDK_H_VERSION (25125)
enum hv_unimplemented_msr_action {
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index a5527d49cf..4ae20d2ac1 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -508,109 +508,6 @@ struct hv_input_assert_virtual_interrupt {
uint16_t rsvd_z1;
} QEMU_PACKED;
-/* Flags for dirty mask of hv_vp_register_page */
-enum hv_x64_register_class_type {
- HV_X64_REGISTER_CLASS_GENERAL = 0,
- HV_X64_REGISTER_CLASS_IP = 1,
- HV_X64_REGISTER_CLASS_XMM = 2,
- HV_X64_REGISTER_CLASS_SEGMENT = 3,
- HV_X64_REGISTER_CLASS_FLAGS = 4,
-};
-
-#define HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT 7
-
-union hv_vp_register_page_interrupt_vectors {
- uint64_t as_uint64;
- struct {
- uint8_t vector_count;
- uint8_t vector[HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT];
- };
-};
-
-struct hv_vp_register_page {
- uint16_t version;
- uint8_t isvalid;
- uint8_t rsvdz;
- uint32_t dirty;
-
- union {
- struct {
- /* General purpose registers (HV_X64_REGISTER_CLASS_GENERAL) */
- union {
- struct {
- 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;
-
- uint64_t gp_registers[16];
- };
- /* Instruction pointer (HV_X64_REGISTER_CLASS_IP) */
- uint64_t rip;
- /* Flags (HV_X64_REGISTER_CLASS_FLAGS) */
- uint64_t rflags;
- } QEMU_PACKED;
-
- uint64_t registers[18];
- };
- uint8_t reserved[8];
- /* Volatile XMM registers (HV_X64_REGISTER_CLASS_XMM) */
- union {
- struct {
- struct hv_u128 xmm0;
- struct hv_u128 xmm1;
- struct hv_u128 xmm2;
- struct hv_u128 xmm3;
- struct hv_u128 xmm4;
- struct hv_u128 xmm5;
- } QEMU_PACKED;
-
- struct hv_u128 xmm_registers[6];
- };
- /* Segment registers (HV_X64_REGISTER_CLASS_SEGMENT) */
- union {
- struct {
- struct hv_x64_segment_register es;
- struct hv_x64_segment_register cs;
- struct hv_x64_segment_register ss;
- struct hv_x64_segment_register ds;
- struct hv_x64_segment_register fs;
- struct hv_x64_segment_register gs;
- } QEMU_PACKED;
-
- struct hv_x64_segment_register segment_registers[6];
- };
- /* Misc. control registers (cannot be set via this interface) */
- uint64_t cr0;
- uint64_t cr3;
- uint64_t cr4;
- uint64_t cr8;
- uint64_t efer;
- uint64_t dr7;
- union hv_x64_pending_interruption_register pending_interruption;
- union hv_x64_interrupt_state_register interrupt_state;
- uint64_t instruction_emulation_hints;
- uint64_t xfem;
-
- uint8_t reserved1[0x100];
-
- /* Interrupts injected as part of HvCallDispatchVp. */
- union hv_vp_register_page_interrupt_vectors interrupt_vectors;
-} QEMU_PACKED;
-
/* /dev/mshv */
#define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition)
#define MSHV_CREATE_VP _IOW(MSHV_IOCTL, 0x01, struct mshv_create_vp)
diff --git a/include/hw/hyperv/hvhdk.h b/include/hw/hyperv/hvhdk.h
index 9e6dcb22f6..b9b76cf93c 100644
--- a/include/hw/hyperv/hvhdk.h
+++ b/include/hw/hyperv/hvhdk.h
@@ -9,7 +9,12 @@
#ifndef HW_HYPERV_HVHDK_H
#define HW_HYPERV_HVHDK_H
+#include "hvgdk.h"
+#include "hvhdk_mini.h"
+
#define HV_PARTITION_SYNTHETIC_PROCESSOR_FEATURES_BANKS 1
+#define HV_VP_REGISTER_PAGE_VERSION_1 1u
+#define HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT 7
struct hv_input_get_partition_property {
uint64_t partition_id;
@@ -441,4 +446,105 @@ typedef struct hv_input_register_intercept_result {
union hv_register_intercept_result_parameters parameters;
} QEMU_PACKED hv_input_register_intercept_result;
+/* Flags for dirty mask of hv_vp_register_page */
+enum hv_x64_register_class_type {
+ HV_X64_REGISTER_CLASS_GENERAL = 0,
+ HV_X64_REGISTER_CLASS_IP = 1,
+ HV_X64_REGISTER_CLASS_XMM = 2,
+ HV_X64_REGISTER_CLASS_SEGMENT = 3,
+ HV_X64_REGISTER_CLASS_FLAGS = 4,
+};
+
+union hv_vp_register_page_interrupt_vectors {
+ uint64_t as_uint64;
+ struct {
+ uint8_t vector_count;
+ uint8_t vector[HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT];
+ };
+};
+
+struct hv_vp_register_page {
+ uint16_t version;
+ uint8_t isvalid;
+ uint8_t rsvdz;
+ uint32_t dirty;
+
+ union {
+ struct {
+ /* General purpose registers (HV_X64_REGISTER_CLASS_GENERAL) */
+ union {
+ struct {
+ 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;
+
+ uint64_t gp_registers[16];
+ };
+ /* Instruction pointer (HV_X64_REGISTER_CLASS_IP) */
+ uint64_t rip;
+ /* Flags (HV_X64_REGISTER_CLASS_FLAGS) */
+ uint64_t rflags;
+ } QEMU_PACKED;
+
+ uint64_t registers[18];
+ };
+ uint8_t reserved[8];
+ /* Volatile XMM registers (HV_X64_REGISTER_CLASS_XMM) */
+ union {
+ struct {
+ struct hv_u128 xmm0;
+ struct hv_u128 xmm1;
+ struct hv_u128 xmm2;
+ struct hv_u128 xmm3;
+ struct hv_u128 xmm4;
+ struct hv_u128 xmm5;
+ } QEMU_PACKED;
+
+ struct hv_u128 xmm_registers[6];
+ };
+ /* Segment registers (HV_X64_REGISTER_CLASS_SEGMENT) */
+ union {
+ struct {
+ struct hv_x64_segment_register es;
+ struct hv_x64_segment_register cs;
+ struct hv_x64_segment_register ss;
+ struct hv_x64_segment_register ds;
+ struct hv_x64_segment_register fs;
+ struct hv_x64_segment_register gs;
+ } QEMU_PACKED;
+
+ struct hv_x64_segment_register segment_registers[6];
+ };
+ /* Misc. control registers (cannot be set via this interface) */
+ uint64_t cr0;
+ uint64_t cr3;
+ uint64_t cr4;
+ uint64_t cr8;
+ uint64_t efer;
+ uint64_t dr7;
+ union hv_x64_pending_interruption_register pending_interruption;
+ union hv_x64_interrupt_state_register interrupt_state;
+ uint64_t instruction_emulation_hints;
+ uint64_t xfem;
+
+ uint8_t reserved1[0x100];
+
+ /* Interrupts injected as part of HvCallDispatchVp. */
+ union hv_vp_register_page_interrupt_vectors interrupt_vectors;
+} QEMU_PACKED;
+
#endif /* HW_HYPERV_HVHDK_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 03/11] target/i386/mshv: abort when hv_vp_register_page setup fails
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
2026-10-06 16:48 ` [PULL 01/11] target/i386/mshv: fix read/write memory across the page boundary Magnus Kulke
2026-10-06 16:48 ` [PULL 02/11] include/hw/hyperv: move hv_vp_register_page struct definition Magnus Kulke
@ 2026-10-06 16:48 ` Magnus Kulke
2026-10-06 16:48 ` [PULL 04/11] target/i386/mshv: remove fallback for register page get registers Magnus Kulke
` (9 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:48 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
In case the call to mmap fails, we log an error and abort to signal
there is something wrong with the system.
Check the register page version and compare with the expected version and
abort in case of a mismatch.
Fixes: a173f8f170
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>
---
target/i386/mshv/mshv-cpu.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index b8a6924088..d236b9461f 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -2154,6 +2154,7 @@ void mshv_arch_init_vcpu(CPUState *cpu)
CPUX86State *env = &x86_cpu->env;
AccelCPUState *state = cpu->accel;
size_t page = HV_HYP_PAGE_SIZE, xsave_len;
+ void *regs_page;
void *mem = qemu_memalign(page, 2 * page);
int ret;
X86XSaveHeader *header;
@@ -2165,15 +2166,20 @@ void mshv_arch_init_vcpu(CPUState *cpu)
> HV_HYP_PAGE_SIZE));
/* mmap the registers page */
- void *rp = mmap(NULL, page, PROT_READ | PROT_WRITE,
+ regs_page = mmap(NULL, page, PROT_READ | PROT_WRITE,
MAP_SHARED, mshv_vcpufd(cpu),
MSHV_VP_MMAP_OFFSET_REGISTERS * page);
- if (rp == MAP_FAILED) {
- warn_report("register page mmap failed, falling back to hypercalls: %s",
- strerror(errno));
- env->regs_page = NULL;
- } else {
- env->regs_page = (struct hv_vp_register_page *) rp;
+ if (regs_page == MAP_FAILED) {
+ /* This shouldn't fail, so we treat it as a fatal error */
+ error_report("register page mmap failed: %s", strerror(errno));
+ abort();
+ }
+ env->regs_page = (struct hv_vp_register_page *) regs_page;
+
+ if (env->regs_page->version != HV_VP_REGISTER_PAGE_VERSION_1) {
+ error_report("register page version mismatch: got %u, expected %u",
+ env->regs_page->version, HV_VP_REGISTER_PAGE_VERSION_1);
+ abort();
}
state->hvcall_args.base = mem;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 04/11] target/i386/mshv: remove fallback for register page get registers
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (2 preceding siblings ...)
2026-10-06 16:48 ` [PULL 03/11] target/i386/mshv: abort when hv_vp_register_page setup fails Magnus Kulke
@ 2026-10-06 16:48 ` Magnus Kulke
2026-10-06 16:48 ` [PULL 05/11] target/i386/mshv: remove fallback for register page set registers Magnus Kulke
` (8 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:48 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Change `load_regs` to use the register page when it is mmapped
and is valid.
Eliminate the hypercall based logic and fail in case the register page
is found in an unexpected state.
When retrieving the special registers, there are some registers that are
not present in the register page: TR, LDTR, GDTR, IDTR, CR2, APIC_BASE.
As this registers are not likely to be used in an MMIO/PIO operation,
and to avoid a hypercall overhead we do not retrieve them.
Local testing showed no regression when using this logic. To properly
retrieve all the necessary registers for each decoded operation implies
having a mechanism that tracks the state of each register, which is
beyond the scope of this patch series.
Fixes: 40072a7391
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>
---
target/i386/mshv/mshv-cpu.c | 107 +++++++++++-------------------------
1 file changed, 31 insertions(+), 76 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index d236b9461f..fe1645f87d 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -307,15 +307,6 @@ static int get_xc_reg(CPUState *cpu)
return 0;
}
-static enum hv_register_name NON_VP_PAGE_REGISTER_NAMES[6] = {
- HV_X64_REGISTER_TR,
- HV_X64_REGISTER_LDTR,
- HV_X64_REGISTER_GDTR,
- HV_X64_REGISTER_IDTR,
- HV_X64_REGISTER_CR2,
- HV_X64_REGISTER_APIC_BASE,
-};
-
static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa,
uint64_t flags)
{
@@ -652,7 +643,7 @@ static void populate_special_regs(const hv_register_assoc *assocs,
cpu_set_apic_base(x86cpu->apic_state, assocs[16].value.reg64);
}
-static void mshv_get_standard_regs_vp_page(CPUState *cpu)
+static void get_standard_regs_vp_page(CPUState *cpu)
{
X86CPU *x86cpu = X86_CPU(cpu);
CPUX86State *env = &x86cpu->env;
@@ -680,13 +671,19 @@ static void mshv_get_standard_regs_vp_page(CPUState *cpu)
rflags_to_lflags(env);
}
-static int mshv_get_special_regs_vp_page(CPUState *cpu)
+/*
+ * This function synchronizes the special registers present in the
+ * register vp page, which are not all the special registers.
+ * The rest of the special registers (LD, TR, GDT, IDT, CR2, APIC_BASE)
+ * are not synchronized to avoid the overhead of a hypercall.
+ *
+ * These special registers are not normally used by the guest,
+ * and are only used in some specific cases.
+ */
+static void get_special_regs_vp_page(CPUState *cpu)
{
X86CPU *x86cpu = X86_CPU(cpu);
CPUX86State *env = &x86cpu->env;
- struct hv_register_assoc assocs[ARRAY_SIZE(NON_VP_PAGE_REGISTER_NAMES)];
- int ret;
- size_t n_regs = ARRAY_SIZE(NON_VP_PAGE_REGISTER_NAMES);
hv_x64_segment_register seg;
/* Populate special registers that are in the VP register page */
@@ -709,46 +706,15 @@ static int mshv_get_special_regs_vp_page(CPUState *cpu)
populate_segment_reg(&seg, &env->segs[R_FS]);
memcpy(&seg, &env->regs_page->gs, sizeof(hv_x64_segment_register));
populate_segment_reg(&seg, &env->segs[R_GS]);
-
- /* The rest of the special registers that are not in the VP register page */
- for (size_t i = 0; i < n_regs; i++) {
- assocs[i].name = NON_VP_PAGE_REGISTER_NAMES[i];
- }
-
- ret = mshv_get_generic_regs(cpu, assocs, n_regs);
- if (ret < 0) {
- error_report("failed to get non-vp-page special registers");
- return -1;
- }
-
- /* Non-VP page registers - TR, LDTR, GDTR, IDTR, CR2, APIC_BASE */
- populate_segment_reg(&assocs[0].value.segment, &env->tr);
- populate_segment_reg(&assocs[1].value.segment, &env->ldt);
-
- populate_table_reg(&assocs[2].value.table, &env->gdt);
- populate_table_reg(&assocs[3].value.table, &env->idt);
- env->cr[2] = assocs[4].value.reg64;
-
- cpu_set_apic_base(x86cpu->apic_state, assocs[5].value.reg64);
-
- return ret;
}
-static int mshv_get_registers_vp_page(CPUState *cpu)
+static void get_registers_vp_page(CPUState *cpu)
{
- int ret;
-
/* General Purpose Registers */
- mshv_get_standard_regs_vp_page(cpu);
+ get_standard_regs_vp_page(cpu);
- /* Special Registers - makes a hypercall */
- ret = mshv_get_special_regs_vp_page(cpu);
- if (ret < 0) {
- error_report("failed to get special registers for vp page");
- return -1;
- }
-
- return 0;
+ /* Special Registers */
+ get_special_regs_vp_page(cpu);
}
@@ -772,29 +738,26 @@ static int get_special_regs(CPUState *cpu)
return 0;
}
-static int load_regs(CPUState *cpu)
+static void load_regs(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
- int ret;
-
- /* Use register vp page to optimize registers access */
- if (env->regs_page && env->regs_page->isvalid != 0) {
- ret = mshv_get_registers_vp_page(cpu);
- return ret;
- }
- ret = get_standard_regs(cpu);
- if (ret < 0) {
- return ret;
+ /* Check register page pointer and abort if in unexpected state */
+ if (!env->regs_page) {
+ error_report(
+ "load regs: register page not set for vcpu %d",
+ cpu->cpu_index);
+ abort();
}
-
- ret = get_special_regs(cpu);
- if (ret < 0) {
- return ret;
+ if (env->regs_page->isvalid == 0) {
+ error_report(
+ "load regs: register page invalid for vcpu %d",
+ cpu->cpu_index);
+ abort();
}
- return 0;
+ get_registers_vp_page(cpu);
}
static int get_vcpu_events(CPUState *cpu)
@@ -1637,11 +1600,7 @@ static int emulate_instruction(CPUState *cpu,
int ret;
x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
- ret = load_regs(cpu);
- if (ret < 0) {
- error_report("Failed to load registers");
- return -1;
- }
+ load_regs(cpu);
decode_instruction_stream(env, &decode, &stream);
exec_instruction(env, &decode);
@@ -1977,11 +1936,7 @@ static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
- ret = load_regs(cpu);
- if (ret < 0) {
- error_report("Failed to load registers");
- return -1;
- }
+ load_regs(cpu);
direction_flag = (env->eflags & DESC_E_MASK) != 0;
@@ -2118,7 +2073,7 @@ static void read_segment_descriptor(CPUState *cpu,
/*
* SegmentCache stores the hypervisor-provided value verbatim (populated by
- * mshv_load_regs). We need to convert it to format expected by the
+ * load_regs). We need to convert it to format expected by the
* instruction emulator. We can have a limit value > 0xfffff with
* granularity of 0 (byte granularity), which is not representable
* in real x86_segment_descriptor. In this case we set granularity to 1
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 05/11] target/i386/mshv: remove fallback for register page set registers
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (3 preceding siblings ...)
2026-10-06 16:48 ` [PULL 04/11] target/i386/mshv: remove fallback for register page get registers Magnus Kulke
@ 2026-10-06 16:48 ` Magnus Kulke
2026-10-06 16:49 ` [PULL 06/11] target/i386/mshv: fix pio handlers clobbering device-modified registers Magnus Kulke
` (7 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:48 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Update `store_regs` to use the register page when it is mmapped and
valid to set registers.
Remove the ioctl based register retrieval and fail in case the register
page is not correctly set or valid.
Fixes: 80c7f8e9cd
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>
---
target/i386/mshv/mshv-cpu.c | 48 +++++++++++++------------------------
1 file changed, 17 insertions(+), 31 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index fe1645f87d..5a34357693 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -485,7 +485,7 @@ static int set_standard_regs(const CPUState *cpu)
return 0;
}
-static void mshv_set_standard_regs_vp_page(CPUState *cpu)
+static void set_standard_regs_vp_page(CPUState *cpu)
{
X86CPU *x86cpu = X86_CPU(cpu);
CPUX86State *env = &x86cpu->env;
@@ -515,29 +515,26 @@ static void mshv_set_standard_regs_vp_page(CPUState *cpu)
| (1u << HV_X64_REGISTER_CLASS_FLAGS);
}
-static int store_regs(CPUState *cpu)
+static void store_regs(CPUState *cpu)
{
X86CPU *x86cpu = X86_CPU(cpu);
CPUX86State *env = &x86cpu->env;
- int ret;
- /* Use register vp page to optimize registers access */
- if (env->regs_page && env->regs_page->isvalid != 0) {
- mshv_set_standard_regs_vp_page(cpu);
- } else {
- ret = set_standard_regs(cpu);
- if (ret < 0) {
- return ret;
- }
+ /* Check register page pointer and abort if in unexpected state */
+ if (!env->regs_page) {
+ error_report(
+ "store regs: register page not set for vcpu %d",
+ cpu->cpu_index);
+ abort();
}
-
- ret = set_special_regs(cpu);
- if (ret < 0) {
- error_report("Failed to store speical registers");
- return ret;
+ if (env->regs_page->isvalid == 0) {
+ error_report(
+ "store regs: register page invalid for vcpu %d",
+ cpu->cpu_index);
+ abort();
}
- return 0;
+ set_standard_regs_vp_page(cpu);
}
static void populate_standard_regs(const hv_register_assoc *assocs,
@@ -1590,14 +1587,13 @@ static int set_memory_info(const struct hyperv_message *msg,
return 0;
}
-static int emulate_instruction(CPUState *cpu,
+static void emulate_instruction(CPUState *cpu,
const uint8_t *insn_bytes, size_t insn_len,
uint64_t gva, uint64_t gpa)
{
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
struct x86_decode decode = { 0 };
- int ret;
x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
load_regs(cpu);
@@ -1605,13 +1601,7 @@ static int emulate_instruction(CPUState *cpu,
decode_instruction_stream(env, &decode, &stream);
exec_instruction(env, &decode);
- ret = store_regs(cpu);
- if (ret < 0) {
- error_report("failed to store registers");
- return -1;
- }
-
- return 0;
+ store_regs(cpu);
}
static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg,
@@ -1647,13 +1637,9 @@ static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg,
instruction_bytes = info.instruction_bytes;
- ret = emulate_instruction(cpu, instruction_bytes, insn_len,
+ emulate_instruction(cpu, instruction_bytes, insn_len,
info.guest_virtual_address,
info.guest_physical_address);
- if (ret < 0) {
- error_report("failed to emulate mmio");
- return -1;
- }
*exit_reason = MshvVmExitIgnore;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 06/11] target/i386/mshv: fix pio handlers clobbering device-modified registers
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (4 preceding siblings ...)
2026-10-06 16:48 ` [PULL 05/11] target/i386/mshv: remove fallback for register page set registers Magnus Kulke
@ 2026-10-06 16:49 ` Magnus Kulke
2026-10-06 16:49 ` [PULL 07/11] target/i386/mshv: zero-init msr hvcall args Magnus Kulke
` (6 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:49 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
When a device handler (e.g. vmport) calls cpu_synchronize_state() during
I/O port dispatch, it sets cpu->accel->dirty = true and may modify
registers directly in env. The old PIO code ignored this: it
unconditionally wrote the stale info->rax from the VM-exit intercept
message back to the hypervisor and then cleared dirty, discarding any
register changes made by the device.
Bifurcate both handlers on cpu->accel->dirty:
handle_pio_non_str:
- dirty path: update env->eip directly. For reads (IN), merge the I/O
result into env->regs[R_EAX] (which may have been modified by the
device) rather than info->rax. For writes (OUT), leave RAX untouched.
Flush all registers via mshv_store_regs() and clear dirty.
- non-dirty path: write RIP and RAX via set_x64_registers hypercall as
before.
handle_pio_str:
- dirty path: update env->eip and the appropriate index register
(RSI for OUTS, RDI for INS) directly. Flush via mshv_store_regs()
and clear dirty.
- non-dirty path: write the index register and RIP via
set_x64_registers. Drop the RAX assignment that was here before;
string I/O does not modify RAX, and set_x64_registers is hardcoded
to write only 2 registers so the third slot was silently ignored
anyway.
Remove the unconditional "cpu->accel->dirty = false" at the end of both
handlers. In the non-dirty fast path it was redundant (already false).
In the dirty path it was actively harmful: it told the vcpu run loop
that env was clean when it was not, losing the device's modifications.
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>
---
target/i386/mshv/mshv-cpu.c | 72 +++++++++++++++++++++++++------------
1 file changed, 50 insertions(+), 22 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 5a34357693..4f113da02d 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1718,10 +1718,12 @@ static int handle_pio_non_str(CPUState *cpu,
uint32_t val, eax;
const uint32_t eax_mask = 0xffffffffu >> (32 - len * 8);
size_t insn_len;
- uint64_t rip, rax;
+ uint64_t rip;
uint32_t reg_names[2];
uint64_t reg_values[2];
uint16_t port = info->port_number;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_WRITE) {
union {
@@ -1752,21 +1754,36 @@ static int handle_pio_non_str(CPUState *cpu,
/* Advance RIP and update RAX */
rip = info->header.rip + insn_len;
- rax = info->rax;
- reg_names[0] = HV_X64_REGISTER_RIP;
- reg_values[0] = rip;
- reg_names[1] = HV_X64_REGISTER_RAX;
- reg_values[1] = rax;
+ if (cpu->vcpu_dirty) {
+ env->eip = rip;
+ if (access_type != HV_X64_INTERCEPT_ACCESS_TYPE_WRITE) {
+ /*
+ * For reads, merge the I/O result into the current RAX.
+ * Use env->regs[R_EAX] as the base since a device handler
+ * (e.g. vmport) may have called cpu_synchronize_state()
+ * and modified registers.
+ */
+ eax = (((uint32_t)env->regs[R_EAX]) & ~eax_mask)
+ | (val & eax_mask);
+ env->regs[R_EAX] = (uint64_t)eax;
+ }
+ /* Sync modified standard registers back and clear dirty. */
+ store_regs(cpu);
+ cpu->vcpu_dirty = false;
+ } else {
+ reg_names[0] = HV_X64_REGISTER_RIP;
+ reg_values[0] = rip;
+ reg_names[1] = HV_X64_REGISTER_RAX;
+ reg_values[1] = info->rax;
- ret = set_x64_registers(cpu, reg_names, reg_values);
- if (ret < 0) {
- error_report("Failed to set x64 registers");
- return -1;
+ ret = set_x64_registers(cpu, reg_names, reg_values);
+ if (ret < 0) {
+ error_report("Failed to set x64 registers");
+ return -1;
+ }
}
- cpu->vcpu_dirty = false;
-
return 0;
}
@@ -1915,6 +1932,7 @@ static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
bool repop = info->access_info.rep_prefix == 1;
size_t repeat = repop ? info->rcx : 1;
size_t insn_len = info->header.instruction_length;
+ uint64_t rip;
bool direction_flag;
uint32_t reg_names[3];
uint64_t reg_values[3];
@@ -1944,18 +1962,28 @@ static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
reg_values[0] = info->rdi;
}
- reg_names[1] = HV_X64_REGISTER_RIP;
- reg_values[1] = info->header.rip + insn_len;
- reg_names[2] = HV_X64_REGISTER_RAX;
- reg_values[2] = info->rax;
+ rip = info->header.rip + insn_len;
- ret = set_x64_registers(cpu, reg_names, reg_values);
- if (ret < 0) {
- error_report("Failed to set RIP and RAX registers");
- return -1;
- }
+ if (cpu->vcpu_dirty) {
+ env->eip = rip;
+ if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_WRITE) {
+ env->regs[R_ESI] = info->rsi;
+ } else {
+ env->regs[R_EDI] = info->rdi;
+ }
+ /* Sync modified standard registers back and clear dirty. */
+ store_regs(cpu);
+ cpu->vcpu_dirty = false;
+ } else {
+ reg_names[1] = HV_X64_REGISTER_RIP;
+ reg_values[1] = rip;
- cpu->vcpu_dirty = false;
+ ret = set_x64_registers(cpu, reg_names, reg_values);
+ if (ret < 0) {
+ error_report("Failed to set x64 registers");
+ return -1;
+ }
+ }
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 07/11] target/i386/mshv: zero-init msr hvcall args
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (5 preceding siblings ...)
2026-10-06 16:49 ` [PULL 06/11] target/i386/mshv: fix pio handlers clobbering device-modified registers Magnus Kulke
@ 2026-10-06 16:49 ` Magnus Kulke
2026-10-06 16:49 ` [PULL 08/11] target/i386/mshv: Fix cpu feature-leak from host Magnus Kulke
` (5 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:49 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
In the current code we leave the argument arrays for the get/set hvcall
unitialized, setting only individual fields. some fields will be
populated during the hvcall, but in any case the arg arrays will contain
unitialized data (e.g. in reserved fields), to which static analysis
objects to.
Fixes: c38eb12c85
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
target/i386/mshv/msr.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c
index d3788d7715..96bd552916 100644
--- a/target/i386/mshv/msr.c
+++ b/target/i386/mshv/msr.c
@@ -328,7 +328,7 @@ int mshv_get_msrs(CPUState *cpu)
{
int ret = 0;
size_t n_assocs = MSHV_MSR_TOTAL_COUNT;
- struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT];
+ struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT] = { 0 };
size_t i, j;
uint32_t name;
X86CPU *x86cpu = X86_CPU(cpu);
@@ -409,7 +409,7 @@ static void load_from_env(const CPUState *cpu, struct hv_register_assoc *assocs,
int mshv_set_msrs(const CPUState *cpu)
{
size_t n_assocs = MSHV_MSR_TOTAL_COUNT;
- struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT];
+ struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT] = { 0 };
int ret;
size_t i, j;
X86CPU *x86cpu = X86_CPU(cpu);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 08/11] target/i386/mshv: Fix cpu feature-leak from host
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (6 preceding siblings ...)
2026-10-06 16:49 ` [PULL 07/11] target/i386/mshv: zero-init msr hvcall args Magnus Kulke
@ 2026-10-06 16:49 ` Magnus Kulke
2026-10-06 16:49 ` [PULL 09/11] include/hw/hyperv: add ABI for exception intercepts and pending events Magnus Kulke
` (4 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:49 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
In the current implementation there are subtle bugs that have to do with
the termination of subleaves, resulting in features reported on the
guest that it should not have according to the model.
If a guest model has fewer valid subleaves stopping too early leaves
unspecified gaps that are filled by the host, resulting in potentially
invalid feature combinations, for example:
The host reports features:
CPUID[EAX=01H].ECX.AVX [bit 28] and
CPUID[EAX=07H,ECX=01H].EAX.AVX_VNNI [bit 4]
In the current implementation we would skip over 7.1 completely, not
registering CPUID responses for the subleaf, resulting in a guest with
qemu64 model (which does not feature AVX), reporting AVX_VNNI as
available (passthrough from host).
We need to walk through the max subleaves of leaf 7 and register them
explicitly (also if they're 0).
A second bug was about the propagation of "terminator" subleaves,
all-zero responses that signal the end. We also want to register this
terminator response , so the guest stops enumerating. In the current
implementation we weren't doing that.
Fixes: 4fa04dd1621
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
target/i386/mshv/mshv-cpu.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 4f113da02d..f2b5641231 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1079,7 +1079,7 @@ static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
CPUX86State *env = &x86_cpu->env;
uint32_t eax, ebx, ecx, edx;
uint32_t leaf, subleaf;
- uint32_t max_basic_leaf, max_extended_leaf;
+ uint32_t max_basic_leaf, max_extended_leaf, max_subleaf_7;
uint32_t max_subleaf = 0x20;
uint32_t leaves_with_subleaves[] = {0x04, 0x07, 0x0d, 0x0f, 0x10};
int n_subleaf_leaves = ARRAY_SIZE(leaves_with_subleaves);
@@ -1117,14 +1117,31 @@ static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
continue;
}
+ /*
+ * Valid subleaves for are reported in 7.0:EAX. We need to register all
+ * subleaves to the maximum subleaf, even if they are 0. Otherwise the
+ * host will supply its own values for a unregistered subleaf, which
+ * can result in an inconsistent feature set.
+ */
+ if (leaf == 0x07) {
+ cpu_x86_cpuid(env, leaf, 0, &max_subleaf_7, &ebx, &ecx, &edx);
+ for (subleaf = 0; subleaf <= max_subleaf_7; subleaf++) {
+ cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx);
+ add_cpuid_entry(cpuid_entries, leaf, subleaf,
+ eax, ebx, ecx, edx);
+ }
+ continue;
+ }
+
subleaf = 0;
while (subleaf < max_subleaf) {
cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx);
+ /* register the "terminator" to the guest, before breaking */
+ add_cpuid_entry(cpuid_entries, leaf, subleaf, eax, ebx, ecx, edx);
if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) {
break;
}
- add_cpuid_entry(cpuid_entries, leaf, subleaf, eax, ebx, ecx, edx);
subleaf++;
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 09/11] include/hw/hyperv: add ABI for exception intercepts and pending events
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (7 preceding siblings ...)
2026-10-06 16:49 ` [PULL 08/11] target/i386/mshv: Fix cpu feature-leak from host Magnus Kulke
@ 2026-10-06 16:49 ` Magnus Kulke
2026-10-06 16:49 ` [PULL 10/11] accel/mshv: add gdbstub software breakpoint support Magnus Kulke
` (3 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:49 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
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>
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Signed-off-by: Magnus Kulke <magnuskulke@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 4ae20d2ac1..0983e3a58d 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 {
/* VP Management Registers */
HV_REGISTER_INTERNAL_ACTIVITY_STATE = 0x00000004,
@@ -240,6 +243,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;
@@ -737,6 +763,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 {
@@ -845,6 +900,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.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 10/11] accel/mshv: add gdbstub software breakpoint support
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (8 preceding siblings ...)
2026-10-06 16:49 ` [PULL 09/11] include/hw/hyperv: add ABI for exception intercepts and pending events Magnus Kulke
@ 2026-10-06 16:49 ` Magnus Kulke
2026-10-06 16:49 ` [PULL 11/11] target/i386/mshv: support single-stepping Magnus Kulke
` (2 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:49 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
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.
(adapted to the signature change of accel_ops->update_guest_debug)
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>
---
accel/mshv/mshv-all.c | 170 +++++++++++++++++++++++++++++++++++-
include/system/mshv_int.h | 14 +++
target/i386/mshv/mshv-cpu.c | 104 ++++++++++++++++++++++
3 files changed, 287 insertions(+), 1 deletion(-)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 5921ce693e..d820a67b95 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"
@@ -563,6 +564,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);
@@ -591,6 +596,158 @@ 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 void mshv_update_guest_debug(CPUState *cpu)
+{
+ int ret;
+
+ if (cpu_single_stepping(cpu)) {
+ /* Installs exception intercept only on the first call */
+ ret = mshv_init_exception_intercept();
+ if (ret < 0) {
+ error_report("Failed to install exception intercept for #DB");
+ abort();
+ }
+ }
+}
+
+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;
@@ -628,6 +785,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;
@@ -695,7 +855,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));
@@ -840,6 +1003,11 @@ static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
ops->cpus_are_resettable = mshv_cpus_are_resettable;
ops->cpu_thread_is_idle = mshv_vcpu_thread_is_idle;
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 3dffe3c5fb..1f77ffd152 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);
@@ -100,6 +111,9 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu);
int mshv_arch_load_vcpu_state(CPUState *cpu);
int mshv_arch_set_partition_msrs(const CPUState *cpu);
int mshv_arch_set_mp_state(const 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 f2b5641231..0670e7a5ae 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"
@@ -27,6 +28,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"
@@ -2023,6 +2025,72 @@ 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,
+ const 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 bool is_debug_exception(CPUState *cpu, const hv_message *msg)
+{
+ struct hv_x64_exception_intercept_message *info = (void *)msg->payload;
+
+ /* Only #DB is intercepted */
+ if (info->exception_vector != EXCP01_DB) {
+ return false;
+ }
+
+ /* INT1 hit: RIP points at the breakpoint */
+ if (mshv_find_sw_breakpoint(cpu, info->header.rip) != NULL) {
+ return true;
+ }
+
+ /* Check if single stepping */
+ if (cpu_single_stepping(cpu)) {
+ return true;
+ }
+
+ /* The guest's own #DB; caller re-injects it. */
+ return false;
+}
+
+static int handle_exception_interrupt(CPUState *cpu,
+ const struct hyperv_message *msg,
+ MshvVmExit *exit_reason)
+{
+ int ret;
+
+ if (is_debug_exception(cpu, msg)) {
+ /* Exit early reporting debug exception */
+ *exit_reason = MshvVmExitDebug;
+ 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);
+ if (ret < 0) {
+ error_report("failed to reinject exception on vcpu %d",
+ cpu->cpu_index);
+ return -1;
+ }
+
+ *exit_reason = MshvVmExitIgnore;
+
+ return 0;
+}
+
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
{
int ret;
@@ -2051,6 +2119,16 @@ 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();
+ ret = handle_exception_interrupt(cpu, msg, &exit_reason);
+ bql_unlock();
+ if (ret < 0) {
+ error_report("failed to handle exception intercept");
+ return -1;
+ }
+ *exit = exit_reason;
+ return exit_reason;
default:
break;
}
@@ -2064,6 +2142,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.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PULL 11/11] target/i386/mshv: support single-stepping
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (9 preceding siblings ...)
2026-10-06 16:49 ` [PULL 10/11] accel/mshv: add gdbstub software breakpoint support Magnus Kulke
@ 2026-10-06 16:49 ` Magnus Kulke
2026-10-09 9:32 ` [PULL 00/11] MSHV changes for 2026-10-06 Richard Henderson
2026-10-11 7:24 ` Michael Tokarev
12 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-06 16:49 UTC (permalink / raw)
To: qemu-devel; +Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
From: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
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>
---
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 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;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PULL 00/11] MSHV changes for 2026-10-06
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (10 preceding siblings ...)
2026-10-06 16:49 ` [PULL 11/11] target/i386/mshv: support single-stepping Magnus Kulke
@ 2026-10-09 9:32 ` Richard Henderson
2026-10-11 7:24 ` Michael Tokarev
12 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-10-09 9:32 UTC (permalink / raw)
To: Magnus Kulke, qemu-devel
Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini
On 10/6/26 18:48, Magnus Kulke wrote:
> Various minor fixes and a feature (gdb debugger support) for the Microsoft
> Hypervisor (mshv) accelerator. The changes have been rebased and re-tested
> individually.
>
> My public certificate is available at:
> https://mkulke.github.io/magnuskulke.asc
>
> Primary key fingerprint:
> 5255 B617 7982 9168 7022 31CA 68D9 BB56 DD17 8417
>
> The following changes since commit 3377670ee96b5a68361016680a95b9013709d4b7:
>
> Merge tag 'linux-user-next-pull-request' ofhttps://github.com/hdeller/qemu-hppa into staging (2026-10-05 07:14:51 +0200)
>
> are available in the Git repository at:
>
> https://github.com/mkulke/qemu.git tags/mkulke-pull-20261006
>
> for you to fetch changes up to 7d42b0a4f52d57cd2d9e0a9ceb8ad5102219a801:
>
> target/i386/mshv: support single-stepping (2026-10-06 18:14:35 +0200)
>
> ----------------------------------------------------------------
> mshv changes for 2026-10-06
Applied, thanks.
r~
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PULL 00/11] MSHV changes for 2026-10-06
2026-10-06 16:48 [PULL 00/11] MSHV changes for 2026-10-06 Magnus Kulke
` (11 preceding siblings ...)
2026-10-09 9:32 ` [PULL 00/11] MSHV changes for 2026-10-06 Richard Henderson
@ 2026-10-11 7:24 ` Michael Tokarev
2026-10-12 10:40 ` Magnus Kulke
12 siblings, 1 reply; 15+ messages in thread
From: Michael Tokarev @ 2026-10-11 7:24 UTC (permalink / raw)
To: Magnus Kulke, qemu-devel
Cc: Wei Liu, Doru Blânzeanu, Magnus Kulke, Paolo Bonzini,
qemu-stable
On 06.10.2026 19:48, Magnus Kulke wrote:
> Various minor fixes and a feature (gdb debugger support) for the Microsoft
> Hypervisor (mshv) accelerator. The changes have been rebased and re-tested
> individually.
>
> My public certificate is available at:
> https://mkulke.github.io/magnuskulke.asc
>
> Primary key fingerprint:
> 5255 B617 7982 9168 7022 31CA 68D9 BB56 DD17 8417
>
> The following changes since commit 3377670ee96b5a68361016680a95b9013709d4b7:
>
> Merge tag 'linux-user-next-pull-request' of https://github.com/hdeller/qemu-hppa into staging (2026-10-05 07:14:51 +0200)
>
> are available in the Git repository at:
>
> https://github.com/mkulke/qemu.git tags/mkulke-pull-20261006
>
> for you to fetch changes up to 7d42b0a4f52d57cd2d9e0a9ceb8ad5102219a801:
>
> target/i386/mshv: support single-stepping (2026-10-06 18:14:35 +0200)
>
> ----------------------------------------------------------------
> mshv changes for 2026-10-06
>
> ----------------------------------------------------------------
> Doru Blânzeanu (9):
> target/i386/mshv: fix read/write memory across the page boundary
> include/hw/hyperv: move hv_vp_register_page struct definition
> target/i386/mshv: abort when hv_vp_register_page setup fails
> target/i386/mshv: remove fallback for register page get registers
> target/i386/mshv: remove fallback for register page set registers
> target/i386/mshv: fix pio handlers clobbering device-modified registers
> include/hw/hyperv: add ABI for exception intercepts and pending events
> accel/mshv: add gdbstub software breakpoint support
> target/i386/mshv: support single-stepping
>
> Magnus Kulke (2):
> target/i386/mshv: zero-init msr hvcall args
> target/i386/mshv: Fix cpu feature-leak from host
Is there anything in this patch set to be picked up for the currently
active stable qemu series (10.0.x lts and 11.1.x)?
Or do we skip mshv updates in there still?
Thanks,
/mjt
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PULL 00/11] MSHV changes for 2026-10-06
2026-10-11 7:24 ` Michael Tokarev
@ 2026-10-12 10:40 ` Magnus Kulke
0 siblings, 0 replies; 15+ messages in thread
From: Magnus Kulke @ 2026-10-12 10:40 UTC (permalink / raw)
To: Michael Tokarev
Cc: qemu-devel, Wei Liu, Doru Blânzeanu, Magnus Kulke,
Paolo Bonzini, qemu-stable
On Sun, Oct 11, 2026 at 10:24:58AM +0300, Michael Tokarev wrote:
> On 06.10.2026 19:48, Magnus Kulke wrote:
> > Various minor fixes and a feature (gdb debugger support) for the Microsoft
> > Hypervisor (mshv) accelerator. The changes have been rebased and re-tested
> > individually.
> >
> > My public certificate is available at:
> > https://mkulke.github.io/magnuskulke.asc
> >
> > Primary key fingerprint:
> > 5255 B617 7982 9168 7022 31CA 68D9 BB56 DD17 8417
> >
> > The following changes since commit 3377670ee96b5a68361016680a95b9013709d4b7:
> >
> > Merge tag 'linux-user-next-pull-request' of https://github.com/hdeller/qemu-hppa into staging (2026-10-05 07:14:51 +0200)
> >
> > are available in the Git repository at:
> >
> > https://github.com/mkulke/qemu.git tags/mkulke-pull-20261006
> >
> > for you to fetch changes up to 7d42b0a4f52d57cd2d9e0a9ceb8ad5102219a801:
> >
> > target/i386/mshv: support single-stepping (2026-10-06 18:14:35 +0200)
> >
> > ----------------------------------------------------------------
> > mshv changes for 2026-10-06
> >
> > ----------------------------------------------------------------
> > Doru Blânzeanu (9):
> > target/i386/mshv: fix read/write memory across the page boundary
> > include/hw/hyperv: move hv_vp_register_page struct definition
> > target/i386/mshv: abort when hv_vp_register_page setup fails
> > target/i386/mshv: remove fallback for register page get registers
> > target/i386/mshv: remove fallback for register page set registers
> > target/i386/mshv: fix pio handlers clobbering device-modified registers
> > include/hw/hyperv: add ABI for exception intercepts and pending events
> > accel/mshv: add gdbstub software breakpoint support
> > target/i386/mshv: support single-stepping
> >
> > Magnus Kulke (2):
> > target/i386/mshv: zero-init msr hvcall args
> > target/i386/mshv: Fix cpu feature-leak from host
>
> Is there anything in this patch set to be picked up for the currently
> active stable qemu series (10.0.x lts and 11.1.x)?
>
> Or do we skip mshv updates in there still?
>
> Thanks,
>
> /mjt
Yes, I would still skip backports of mshv code for stable qemu at this
point. access to l1vh-enabled compute is still limited and there are no
critical fixes in this set.
best,
magnus
^ permalink raw reply [flat|nested] 15+ messages in thread