* [PATCH v3 1/7] target/i386/mshv: remove duplicate function for reading vcpu registers
2026-05-21 16:50 [PATCH v3 0/7] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
@ 2026-05-21 16:50 ` Doru Blânzeanu
2026-05-21 16:50 ` [PATCH v3 2/7] accel/mshv: move vcpu arch specific initialization after vcpu creation Doru Blânzeanu
` (5 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Doru Blânzeanu @ 2026-05-21 16:50 UTC (permalink / raw)
To: qemu-devel
Cc: Doru Blânzeanu, Paolo Bonzini, Zhao Liu, Wei Liu,
Magnus Kulke, Wei Liu, Magnus Kulke
Remove function `fetch_guest_state` because it is a duplicate function
of `mshv_load_regs` function.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
---
target/i386/mshv/mshv-cpu.c | 21 +--------------------
1 file changed, 1 insertion(+), 20 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 4ed6e7548f..9defd05db6 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1291,25 +1291,6 @@ static int handle_pio_non_str(const CPUState *cpu,
return 0;
}
-static int fetch_guest_state(CPUState *cpu)
-{
- int ret;
-
- ret = mshv_get_standard_regs(cpu);
- if (ret < 0) {
- error_report("Failed to get standard registers");
- return -1;
- }
-
- ret = mshv_get_special_regs(cpu);
- if (ret < 0) {
- error_report("Failed to get special registers");
- return -1;
- }
-
- return 0;
-}
-
static int read_memory(const CPUState *cpu, uint64_t initial_gva,
uint64_t initial_gpa, uint64_t gva, uint8_t *data,
size_t len)
@@ -1429,7 +1410,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 = fetch_guest_state(cpu);
+ ret = mshv_load_regs(cpu);
if (ret < 0) {
error_report("Failed to fetch guest state");
return -1;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v3 2/7] accel/mshv: move vcpu arch specific initialization after vcpu creation
2026-05-21 16:50 [PATCH v3 0/7] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
2026-05-21 16:50 ` [PATCH v3 1/7] target/i386/mshv: remove duplicate function for reading vcpu registers Doru Blânzeanu
@ 2026-05-21 16:50 ` Doru Blânzeanu
2026-05-21 16:50 ` [PATCH v3 3/7] include/hw/hyperv: add hv_vp_register_page struct definition Doru Blânzeanu
` (4 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Doru Blânzeanu @ 2026-05-21 16:50 UTC (permalink / raw)
To: qemu-devel
Cc: Doru Blânzeanu, Paolo Bonzini, Zhao Liu, Wei Liu,
Magnus Kulke, Wei Liu, Magnus Kulke
Call mshv_arch_init_vcpu after the vcpu is created to ensure a valid
vcpu fd.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
---
accel/mshv/mshv-all.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 58af674bd9..e3da583f21 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -415,13 +415,14 @@ static int mshv_init_vcpu(CPUState *cpu)
int ret;
cpu->accel = g_new0(AccelCPUState, 1);
- mshv_arch_init_vcpu(cpu);
ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
if (ret < 0) {
return -1;
}
+ mshv_arch_init_vcpu(cpu);
+
cpu->accel->dirty = true;
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v3 3/7] include/hw/hyperv: add hv_vp_register_page struct definition
2026-05-21 16:50 [PATCH v3 0/7] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
2026-05-21 16:50 ` [PATCH v3 1/7] target/i386/mshv: remove duplicate function for reading vcpu registers Doru Blânzeanu
2026-05-21 16:50 ` [PATCH v3 2/7] accel/mshv: move vcpu arch specific initialization after vcpu creation Doru Blânzeanu
@ 2026-05-21 16:50 ` Doru Blânzeanu
2026-05-22 12:45 ` Magnus Kulke
2026-05-21 16:50 ` [PATCH v3 4/7] target/i386/mshv: hv_vp_register_page setup for the vcpu Doru Blânzeanu
` (3 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Doru Blânzeanu @ 2026-05-21 16:50 UTC (permalink / raw)
To: qemu-devel
Cc: Doru Blânzeanu, Paolo Bonzini, Zhao Liu, Wei Liu,
Magnus Kulke, Wei Liu, Magnus Kulke
Define the `hv_vp_register_page` structure that the linux kernel uses
to allow access to vcpu registers.
This structure is going to be used in later patches to access vcpu
registers.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
include/hw/hyperv/hvgdk.h | 2 +
include/hw/hyperv/hvhdk.h | 106 ++++++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+)
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/hvhdk.h b/include/hw/hyperv/hvhdk.h
index 41af743847..39b1e2497b 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_set_partition_property {
uint64_t partition_id;
@@ -246,4 +251,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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 3/7] include/hw/hyperv: add hv_vp_register_page struct definition
2026-05-21 16:50 ` [PATCH v3 3/7] include/hw/hyperv: add hv_vp_register_page struct definition Doru Blânzeanu
@ 2026-05-22 12:45 ` Magnus Kulke
0 siblings, 0 replies; 12+ messages in thread
From: Magnus Kulke @ 2026-05-22 12:45 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Paolo Bonzini, Zhao Liu, Wei Liu, Magnus Kulke,
Wei Liu
On Thu, May 21, 2026 at 07:50:37PM +0300, Doru Blânzeanu wrote:
> Define the `hv_vp_register_page` structure that the linux kernel uses
> to allow access to vcpu registers.
>
> This structure is going to be used in later patches to access vcpu
> registers.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
> ---
> include/hw/hyperv/hvgdk.h | 2 +
> include/hw/hyperv/hvhdk.h | 106 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 108 insertions(+)
>
> 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/hvhdk.h b/include/hw/hyperv/hvhdk.h
> index 41af743847..39b1e2497b 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_set_partition_property {
> uint64_t partition_id;
> @@ -246,4 +251,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.53.0
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 4/7] target/i386/mshv: hv_vp_register_page setup for the vcpu
2026-05-21 16:50 [PATCH v3 0/7] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (2 preceding siblings ...)
2026-05-21 16:50 ` [PATCH v3 3/7] include/hw/hyperv: add hv_vp_register_page struct definition Doru Blânzeanu
@ 2026-05-21 16:50 ` Doru Blânzeanu
2026-05-22 12:48 ` Magnus Kulke
2026-05-21 16:50 ` [PATCH v3 5/7] target/i386/mshv: use the register page to get registers Doru Blânzeanu
` (2 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Doru Blânzeanu @ 2026-05-21 16:50 UTC (permalink / raw)
To: qemu-devel
Cc: Doru Blânzeanu, Paolo Bonzini, Zhao Liu, Wei Liu,
Magnus Kulke, Wei Liu, Magnus Kulke
When the vcpu is created, call mmap to configure access to the register page.
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.
Update CPUArchState to store a pointer to the mmapped hv_vp_register_page.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
target/i386/cpu.h | 4 ++++
target/i386/mshv/mshv-cpu.c | 24 ++++++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index bdd4fff89d..d772b4c4cc 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2214,6 +2214,10 @@ typedef struct CPUArchState {
struct {} end_reset_fields;
/* Fields after this point are preserved across CPU reset. */
+#ifdef CONFIG_MSHV
+ /* Shared register page */
+ struct hv_vp_register_page *regs_page;
+#endif
/* processor features (e.g. for CPUID insn) */
/* Minimum cpuid leaf 7 value */
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 9defd05db6..45dc6e6331 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1587,6 +1587,7 @@ void mshv_arch_init_vcpu(CPUState *cpu)
CPUX86State *env = &x86_cpu->env;
AccelCPUState *state = cpu->accel;
size_t page = HV_HYP_PAGE_SIZE;
+ void *regs_page;
void *mem = qemu_memalign(page, 2 * page);
/* sanity check, to make sure we don't overflow the page */
@@ -1595,6 +1596,24 @@ void mshv_arch_init_vcpu(CPUState *cpu)
+ sizeof(hv_input_get_vp_registers)
> HV_HYP_PAGE_SIZE));
+
+ /* mmap the registers page */
+ regs_page = mmap(NULL, page, PROT_READ | PROT_WRITE,
+ MAP_SHARED, mshv_vcpufd(cpu),
+ MSHV_VP_MMAP_OFFSET_REGISTERS * page);
+ 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;
state->hvcall_args.input_page = mem;
state->hvcall_args.output_page = (uint8_t *)mem + page;
@@ -1608,6 +1627,11 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
CPUX86State *env = &x86_cpu->env;
AccelCPUState *state = cpu->accel;
+ /* Unmap the register page */
+ if (env->regs_page) {
+ munmap(env->regs_page, HV_HYP_PAGE_SIZE);
+ env->regs_page = NULL;
+ }
g_free(state->hvcall_args.base);
state->hvcall_args = (MshvHvCallArgs){0};
g_clear_pointer(&env->emu_mmio_buf, g_free);
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 4/7] target/i386/mshv: hv_vp_register_page setup for the vcpu
2026-05-21 16:50 ` [PATCH v3 4/7] target/i386/mshv: hv_vp_register_page setup for the vcpu Doru Blânzeanu
@ 2026-05-22 12:48 ` Magnus Kulke
0 siblings, 0 replies; 12+ messages in thread
From: Magnus Kulke @ 2026-05-22 12:48 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Paolo Bonzini, Zhao Liu, Wei Liu, Magnus Kulke,
Wei Liu
On Thu, May 21, 2026 at 07:50:38PM +0300, Doru Blânzeanu wrote:
> When the vcpu is created, call mmap to configure access to the register page.
> 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.
>
> Update CPUArchState to store a pointer to the mmapped hv_vp_register_page.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
> ---
> target/i386/cpu.h | 4 ++++
> target/i386/mshv/mshv-cpu.c | 24 ++++++++++++++++++++++++
> 2 files changed, 28 insertions(+)
>
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index bdd4fff89d..d772b4c4cc 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -2214,6 +2214,10 @@ typedef struct CPUArchState {
> struct {} end_reset_fields;
>
> /* Fields after this point are preserved across CPU reset. */
> +#ifdef CONFIG_MSHV
> + /* Shared register page */
> + struct hv_vp_register_page *regs_page;
> +#endif
>
> /* processor features (e.g. for CPUID insn) */
> /* Minimum cpuid leaf 7 value */
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 9defd05db6..45dc6e6331 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -1587,6 +1587,7 @@ void mshv_arch_init_vcpu(CPUState *cpu)
> CPUX86State *env = &x86_cpu->env;
> AccelCPUState *state = cpu->accel;
> size_t page = HV_HYP_PAGE_SIZE;
> + void *regs_page;
> void *mem = qemu_memalign(page, 2 * page);
>
> /* sanity check, to make sure we don't overflow the page */
> @@ -1595,6 +1596,24 @@ void mshv_arch_init_vcpu(CPUState *cpu)
> + sizeof(hv_input_get_vp_registers)
> > HV_HYP_PAGE_SIZE));
>
> +
> + /* mmap the registers page */
> + regs_page = mmap(NULL, page, PROT_READ | PROT_WRITE,
> + MAP_SHARED, mshv_vcpufd(cpu),
> + MSHV_VP_MMAP_OFFSET_REGISTERS * page);
> + 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;
> state->hvcall_args.input_page = mem;
> state->hvcall_args.output_page = (uint8_t *)mem + page;
> @@ -1608,6 +1627,11 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
> CPUX86State *env = &x86_cpu->env;
> AccelCPUState *state = cpu->accel;
>
> + /* Unmap the register page */
> + if (env->regs_page) {
> + munmap(env->regs_page, HV_HYP_PAGE_SIZE);
> + env->regs_page = NULL;
> + }
> g_free(state->hvcall_args.base);
> state->hvcall_args = (MshvHvCallArgs){0};
> g_clear_pointer(&env->emu_mmio_buf, g_free);
> --
> 2.53.0
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 5/7] target/i386/mshv: use the register page to get registers
2026-05-21 16:50 [PATCH v3 0/7] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (3 preceding siblings ...)
2026-05-21 16:50 ` [PATCH v3 4/7] target/i386/mshv: hv_vp_register_page setup for the vcpu Doru Blânzeanu
@ 2026-05-21 16:50 ` Doru Blânzeanu
2026-05-22 13:09 ` Magnus Kulke
2026-05-21 16:50 ` [PATCH v3 6/7] target/i386/mshv: use the register page to set registers Doru Blânzeanu
2026-05-21 16:50 ` [PATCH v3 7/7] target/i386/mshv: fix pio handlers clobbering device-modified registers Doru Blânzeanu
6 siblings, 1 reply; 12+ messages in thread
From: Doru Blânzeanu @ 2026-05-21 16:50 UTC (permalink / raw)
To: qemu-devel
Cc: Doru Blânzeanu, Paolo Bonzini, Zhao Liu, Wei Liu,
Magnus Kulke, Wei Liu, Magnus Kulke
Change the mshv_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.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
accel/mshv/mshv-all.c | 9 +--
include/system/mshv_int.h | 2 +-
target/i386/mshv/mshv-cpu.c | 113 +++++++++++++++++++++++++++++-------
3 files changed, 93 insertions(+), 31 deletions(-)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index e3da583f21..bd3bf557ec 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -666,14 +666,7 @@ static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->accel->dirty) {
- int ret = mshv_load_regs(cpu);
- if (ret < 0) {
- error_report("Failed to load registers for vcpu %d",
- cpu->cpu_index);
-
- cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
- vm_stop(RUN_STATE_INTERNAL_ERROR);
- }
+ mshv_load_regs(cpu);
cpu->accel->dirty = true;
}
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 35386c422f..a8a59ebf16 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -85,7 +85,7 @@ int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0);
int mshv_get_standard_regs(CPUState *cpu);
int mshv_get_special_regs(CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
-int mshv_load_regs(CPUState *cpu);
+void mshv_load_regs(CPUState *cpu);
int mshv_store_regs(CPUState *cpu);
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 45dc6e6331..500967b53e 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -401,6 +401,80 @@ 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)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+
+ /* General Purpose Registers */
+ env->regs[R_EAX] = env->regs_page->rax;
+ env->regs[R_EBX] = env->regs_page->rbx;
+ env->regs[R_ECX] = env->regs_page->rcx;
+ env->regs[R_EDX] = env->regs_page->rdx;
+ env->regs[R_ESI] = env->regs_page->rsi;
+ env->regs[R_EDI] = env->regs_page->rdi;
+ env->regs[R_ESP] = env->regs_page->rsp;
+ env->regs[R_EBP] = env->regs_page->rbp;
+ env->regs[R_R8] = env->regs_page->r8;
+ env->regs[R_R9] = env->regs_page->r9;
+ env->regs[R_R10] = env->regs_page->r10;
+ env->regs[R_R11] = env->regs_page->r11;
+ env->regs[R_R12] = env->regs_page->r12;
+ env->regs[R_R13] = env->regs_page->r13;
+ env->regs[R_R14] = env->regs_page->r14;
+ env->regs[R_R15] = env->regs_page->r15;
+
+ env->eip = env->regs_page->rip;
+ env->eflags = env->regs_page->rflags;
+ rflags_to_lflags(env);
+}
+
+/*
+ * 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 mshv_get_special_regs_vp_page(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ hv_x64_segment_register seg;
+
+ /* Populate special registers that are in the VP register page */
+ env->cr[0] = env->regs_page->cr0;
+ env->cr[3] = env->regs_page->cr3;
+ env->cr[4] = env->regs_page->cr4;
+ env->efer = env->regs_page->efer;
+ cpu_set_apic_tpr(x86cpu->apic_state, env->regs_page->cr8);
+
+ /* Segment Registers - copy from packed struct to avoid unaligned access */
+ memcpy(&seg, &env->regs_page->es, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_ES]);
+ memcpy(&seg, &env->regs_page->cs, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_CS]);
+ memcpy(&seg, &env->regs_page->ss, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_SS]);
+ memcpy(&seg, &env->regs_page->ds, sizeof(hv_x64_segment_register));
+ populate_segment_reg(&seg, &env->segs[R_DS]);
+ memcpy(&seg, &env->regs_page->fs, sizeof(hv_x64_segment_register));
+ 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]);
+}
+
+static void mshv_get_registers_vp_page(CPUState *cpu)
+{
+ /* General Purpose Registers */
+ mshv_get_standard_regs_vp_page(cpu);
+
+ /* Special Registers */
+ mshv_get_special_regs_vp_page(cpu);
+}
+
int mshv_get_special_regs(CPUState *cpu)
{
@@ -422,23 +496,26 @@ int mshv_get_special_regs(CPUState *cpu)
return 0;
}
-int mshv_load_regs(CPUState *cpu)
+void mshv_load_regs(CPUState *cpu)
{
- int ret;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
- ret = mshv_get_standard_regs(cpu);
- if (ret < 0) {
- error_report("Failed to load standard registers");
- return -1;
+ /* 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 = mshv_get_special_regs(cpu);
- if (ret < 0) {
- error_report("Failed to load special registers");
- return -1;
+ if (env->regs_page->isvalid == 0) {
+ error_report(
+ "load regs: register page invalid for vcpu %d",
+ cpu->cpu_index);
+ abort();
}
- return 0;
+ mshv_get_registers_vp_page(cpu);
}
static void add_cpuid_entry(GList *cpuid_entries,
@@ -1103,11 +1180,7 @@ static int emulate_instruction(CPUState *cpu,
int ret;
x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
- ret = mshv_load_regs(cpu);
- if (ret < 0) {
- error_report("failed to load registers");
- return -1;
- }
+ mshv_load_regs(cpu);
decode_instruction_stream(env, &decode, &stream);
exec_instruction(env, &decode);
@@ -1410,11 +1483,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 = mshv_load_regs(cpu);
- if (ret < 0) {
- error_report("Failed to fetch guest state");
- return -1;
- }
+ mshv_load_regs(cpu);
direction_flag = (env->eflags & DESC_E_MASK) != 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 5/7] target/i386/mshv: use the register page to get registers
2026-05-21 16:50 ` [PATCH v3 5/7] target/i386/mshv: use the register page to get registers Doru Blânzeanu
@ 2026-05-22 13:09 ` Magnus Kulke
0 siblings, 0 replies; 12+ messages in thread
From: Magnus Kulke @ 2026-05-22 13:09 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Paolo Bonzini, Zhao Liu, Wei Liu, Magnus Kulke,
Wei Liu
On Thu, May 21, 2026 at 07:50:39PM +0300, Doru Blânzeanu wrote:
> Change the mshv_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.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
> ---
> accel/mshv/mshv-all.c | 9 +--
> include/system/mshv_int.h | 2 +-
> target/i386/mshv/mshv-cpu.c | 113 +++++++++++++++++++++++++++++-------
> 3 files changed, 93 insertions(+), 31 deletions(-)
>
> diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
> index e3da583f21..bd3bf557ec 100644
> --- a/accel/mshv/mshv-all.c
> +++ b/accel/mshv/mshv-all.c
> @@ -666,14 +666,7 @@ static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
> static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
> {
> if (!cpu->accel->dirty) {
> - int ret = mshv_load_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to load registers for vcpu %d",
> - cpu->cpu_index);
> -
> - cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
> - vm_stop(RUN_STATE_INTERNAL_ERROR);
> - }
> + mshv_load_regs(cpu);
>
> cpu->accel->dirty = true;
> }
> diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
> index 35386c422f..a8a59ebf16 100644
> --- a/include/system/mshv_int.h
> +++ b/include/system/mshv_int.h
> @@ -85,7 +85,7 @@ int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0);
> int mshv_get_standard_regs(CPUState *cpu);
> int mshv_get_special_regs(CPUState *cpu);
> int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
> -int mshv_load_regs(CPUState *cpu);
> +void mshv_load_regs(CPUState *cpu);
> int mshv_store_regs(CPUState *cpu);
> int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
> size_t n_regs);
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 45dc6e6331..500967b53e 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -401,6 +401,80 @@ 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)
> +{
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
> +
> + /* General Purpose Registers */
> + env->regs[R_EAX] = env->regs_page->rax;
> + env->regs[R_EBX] = env->regs_page->rbx;
> + env->regs[R_ECX] = env->regs_page->rcx;
> + env->regs[R_EDX] = env->regs_page->rdx;
> + env->regs[R_ESI] = env->regs_page->rsi;
> + env->regs[R_EDI] = env->regs_page->rdi;
> + env->regs[R_ESP] = env->regs_page->rsp;
> + env->regs[R_EBP] = env->regs_page->rbp;
> + env->regs[R_R8] = env->regs_page->r8;
> + env->regs[R_R9] = env->regs_page->r9;
> + env->regs[R_R10] = env->regs_page->r10;
> + env->regs[R_R11] = env->regs_page->r11;
> + env->regs[R_R12] = env->regs_page->r12;
> + env->regs[R_R13] = env->regs_page->r13;
> + env->regs[R_R14] = env->regs_page->r14;
> + env->regs[R_R15] = env->regs_page->r15;
> +
> + env->eip = env->regs_page->rip;
> + env->eflags = env->regs_page->rflags;
> + rflags_to_lflags(env);
> +}
> +
> +/*
> + * 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 mshv_get_special_regs_vp_page(CPUState *cpu)
> +{
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
> + hv_x64_segment_register seg;
> +
> + /* Populate special registers that are in the VP register page */
> + env->cr[0] = env->regs_page->cr0;
> + env->cr[3] = env->regs_page->cr3;
> + env->cr[4] = env->regs_page->cr4;
> + env->efer = env->regs_page->efer;
> + cpu_set_apic_tpr(x86cpu->apic_state, env->regs_page->cr8);
> +
> + /* Segment Registers - copy from packed struct to avoid unaligned access */
> + memcpy(&seg, &env->regs_page->es, sizeof(hv_x64_segment_register));
> + populate_segment_reg(&seg, &env->segs[R_ES]);
> + memcpy(&seg, &env->regs_page->cs, sizeof(hv_x64_segment_register));
> + populate_segment_reg(&seg, &env->segs[R_CS]);
> + memcpy(&seg, &env->regs_page->ss, sizeof(hv_x64_segment_register));
> + populate_segment_reg(&seg, &env->segs[R_SS]);
> + memcpy(&seg, &env->regs_page->ds, sizeof(hv_x64_segment_register));
> + populate_segment_reg(&seg, &env->segs[R_DS]);
> + memcpy(&seg, &env->regs_page->fs, sizeof(hv_x64_segment_register));
> + 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]);
> +}
> +
> +static void mshv_get_registers_vp_page(CPUState *cpu)
> +{
> + /* General Purpose Registers */
> + mshv_get_standard_regs_vp_page(cpu);
> +
> + /* Special Registers */
> + mshv_get_special_regs_vp_page(cpu);
> +}
> +
>
> int mshv_get_special_regs(CPUState *cpu)
> {
> @@ -422,23 +496,26 @@ int mshv_get_special_regs(CPUState *cpu)
> return 0;
> }
>
> -int mshv_load_regs(CPUState *cpu)
> +void mshv_load_regs(CPUState *cpu)
> {
> - int ret;
> + X86CPU *x86_cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86_cpu->env;
>
> - ret = mshv_get_standard_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to load standard registers");
> - return -1;
> + /* Check register page pointer and abort if in unexpected state */
nit: I would generally just assert() those invariants, since this really
represents an internal error that the user cannot do anything about.
> + if (!env->regs_page) {
> + error_report(
> + "load regs: register page not set for vcpu %d",
> + cpu->cpu_index);
> + abort();
> }
> -
> - ret = mshv_get_special_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to load special registers");
> - return -1;
> + if (env->regs_page->isvalid == 0) {
> + error_report(
> + "load regs: register page invalid for vcpu %d",
> + cpu->cpu_index);
> + abort();
> }
>
> - return 0;
> + mshv_get_registers_vp_page(cpu);
> }
>
> static void add_cpuid_entry(GList *cpuid_entries,
> @@ -1103,11 +1180,7 @@ static int emulate_instruction(CPUState *cpu,
> int ret;
> x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
>
> - ret = mshv_load_regs(cpu);
> - if (ret < 0) {
> - error_report("failed to load registers");
> - return -1;
> - }
> + mshv_load_regs(cpu);
>
> decode_instruction_stream(env, &decode, &stream);
> exec_instruction(env, &decode);
> @@ -1410,11 +1483,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 = mshv_load_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to fetch guest state");
> - return -1;
> - }
> + mshv_load_regs(cpu);
>
> direction_flag = (env->eflags & DESC_E_MASK) != 0;
>
> --
> 2.53.0
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 6/7] target/i386/mshv: use the register page to set registers
2026-05-21 16:50 [PATCH v3 0/7] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (4 preceding siblings ...)
2026-05-21 16:50 ` [PATCH v3 5/7] target/i386/mshv: use the register page to get registers Doru Blânzeanu
@ 2026-05-21 16:50 ` Doru Blânzeanu
2026-05-22 13:18 ` Magnus Kulke
2026-05-21 16:50 ` [PATCH v3 7/7] target/i386/mshv: fix pio handlers clobbering device-modified registers Doru Blânzeanu
6 siblings, 1 reply; 12+ messages in thread
From: Doru Blânzeanu @ 2026-05-21 16:50 UTC (permalink / raw)
To: qemu-devel
Cc: Doru Blânzeanu, Paolo Bonzini, Zhao Liu, Wei Liu,
Magnus Kulke, Wei Liu, Magnus Kulke
Update mshv_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.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
include/system/mshv_int.h | 2 +-
target/i386/mshv/mshv-cpu.c | 70 ++++++++++++++++++++++++++-----------
2 files changed, 50 insertions(+), 22 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index a8a59ebf16..c2bc36ec60 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -86,7 +86,7 @@ int mshv_get_standard_regs(CPUState *cpu);
int mshv_get_special_regs(CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
void mshv_load_regs(CPUState *cpu);
-int mshv_store_regs(CPUState *cpu);
+void mshv_store_regs(CPUState *cpu);
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs);
int mshv_arch_put_registers(const CPUState *cpu);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 500967b53e..a2bc29abd4 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -285,17 +285,56 @@ static int set_standard_regs(const CPUState *cpu)
return 0;
}
-int mshv_store_regs(CPUState *cpu)
+static void mshv_set_standard_regs_vp_page(CPUState *cpu)
{
- int ret;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
- ret = set_standard_regs(cpu);
- if (ret < 0) {
- error_report("Failed to store standard registers");
- return -1;
+ env->regs_page->rax = env->regs[R_EAX];
+ env->regs_page->rbx = env->regs[R_EBX];
+ env->regs_page->rcx = env->regs[R_ECX];
+ env->regs_page->rdx = env->regs[R_EDX];
+ env->regs_page->rsi = env->regs[R_ESI];
+ env->regs_page->rdi = env->regs[R_EDI];
+ env->regs_page->rsp = env->regs[R_ESP];
+ env->regs_page->rbp = env->regs[R_EBP];
+ env->regs_page->r8 = env->regs[R_R8];
+ env->regs_page->r9 = env->regs[R_R9];
+ env->regs_page->r10 = env->regs[R_R10];
+ env->regs_page->r11 = env->regs[R_R11];
+ env->regs_page->r12 = env->regs[R_R12];
+ env->regs_page->r13 = env->regs[R_R13];
+ env->regs_page->r14 = env->regs[R_R14];
+ env->regs_page->r15 = env->regs[R_R15];
+ env->regs_page->rip = env->eip;
+ lflags_to_rflags(env);
+ env->regs_page->rflags = env->eflags;
+
+ env->regs_page->dirty |= (1u << HV_X64_REGISTER_CLASS_GENERAL)
+ | (1u << HV_X64_REGISTER_CLASS_IP)
+ | (1u << HV_X64_REGISTER_CLASS_FLAGS);
+}
+
+void mshv_store_regs(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+
+ /* 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();
+ }
+ if (env->regs_page->isvalid == 0) {
+ error_report(
+ "store regs: register page invalid for vcpu %d",
+ cpu->cpu_index);
+ abort();
}
- return 0;
+ mshv_set_standard_regs_vp_page(cpu);
}
static void populate_standard_regs(const hv_register_assoc *assocs,
@@ -1170,14 +1209,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 };
mshv_load_regs(cpu);
@@ -1185,13 +1223,7 @@ static int emulate_instruction(CPUState *cpu,
decode_instruction_stream(env, &decode, &stream);
exec_instruction(env, &decode);
- ret = mshv_store_regs(cpu);
- if (ret < 0) {
- error_report("failed to store registers");
- return -1;
- }
-
- return 0;
+ mshv_store_regs(cpu);
}
static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg,
@@ -1227,13 +1259,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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3 6/7] target/i386/mshv: use the register page to set registers
2026-05-21 16:50 ` [PATCH v3 6/7] target/i386/mshv: use the register page to set registers Doru Blânzeanu
@ 2026-05-22 13:18 ` Magnus Kulke
0 siblings, 0 replies; 12+ messages in thread
From: Magnus Kulke @ 2026-05-22 13:18 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Paolo Bonzini, Zhao Liu, Wei Liu, Magnus Kulke,
Wei Liu
On Thu, May 21, 2026 at 07:50:40PM +0300, Doru Blânzeanu wrote:
> Update mshv_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.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
> ---
> include/system/mshv_int.h | 2 +-
> target/i386/mshv/mshv-cpu.c | 70 ++++++++++++++++++++++++++-----------
> 2 files changed, 50 insertions(+), 22 deletions(-)
>
> diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
> index a8a59ebf16..c2bc36ec60 100644
> --- a/include/system/mshv_int.h
> +++ b/include/system/mshv_int.h
> @@ -86,7 +86,7 @@ int mshv_get_standard_regs(CPUState *cpu);
> int mshv_get_special_regs(CPUState *cpu);
> int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
> void mshv_load_regs(CPUState *cpu);
> -int mshv_store_regs(CPUState *cpu);
> +void mshv_store_regs(CPUState *cpu);
> int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
> size_t n_regs);
> int mshv_arch_put_registers(const CPUState *cpu);
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 500967b53e..a2bc29abd4 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -285,17 +285,56 @@ static int set_standard_regs(const CPUState *cpu)
> return 0;
> }
>
> -int mshv_store_regs(CPUState *cpu)
> +static void mshv_set_standard_regs_vp_page(CPUState *cpu)
nit: per convention in the mshv files we have static fn's not prefixed
with mshv_, only the exported ones. It's probably not consistently used,
admitteldy.
> {
> - int ret;
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
>
> - ret = set_standard_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to store standard registers");
> - return -1;
> + env->regs_page->rax = env->regs[R_EAX];
> + env->regs_page->rbx = env->regs[R_EBX];
> + env->regs_page->rcx = env->regs[R_ECX];
> + env->regs_page->rdx = env->regs[R_EDX];
> + env->regs_page->rsi = env->regs[R_ESI];
> + env->regs_page->rdi = env->regs[R_EDI];
> + env->regs_page->rsp = env->regs[R_ESP];
> + env->regs_page->rbp = env->regs[R_EBP];
> + env->regs_page->r8 = env->regs[R_R8];
> + env->regs_page->r9 = env->regs[R_R9];
> + env->regs_page->r10 = env->regs[R_R10];
> + env->regs_page->r11 = env->regs[R_R11];
> + env->regs_page->r12 = env->regs[R_R12];
> + env->regs_page->r13 = env->regs[R_R13];
> + env->regs_page->r14 = env->regs[R_R14];
> + env->regs_page->r15 = env->regs[R_R15];
> + env->regs_page->rip = env->eip;
> + lflags_to_rflags(env);
> + env->regs_page->rflags = env->eflags;
> +
> + env->regs_page->dirty |= (1u << HV_X64_REGISTER_CLASS_GENERAL)
> + | (1u << HV_X64_REGISTER_CLASS_IP)
> + | (1u << HV_X64_REGISTER_CLASS_FLAGS);
> +}
> +
> +void mshv_store_regs(CPUState *cpu)
> +{
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
> +
> + /* 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();
> + }
> + if (env->regs_page->isvalid == 0) {
> + error_report(
> + "store regs: register page invalid for vcpu %d",
> + cpu->cpu_index);
> + abort();
> }
>
> - return 0;
> + mshv_set_standard_regs_vp_page(cpu);
> }
>
> static void populate_standard_regs(const hv_register_assoc *assocs,
> @@ -1170,14 +1209,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 };
>
> mshv_load_regs(cpu);
> @@ -1185,13 +1223,7 @@ static int emulate_instruction(CPUState *cpu,
> decode_instruction_stream(env, &decode, &stream);
> exec_instruction(env, &decode);
>
> - ret = mshv_store_regs(cpu);
> - if (ret < 0) {
> - error_report("failed to store registers");
> - return -1;
> - }
> -
> - return 0;
> + mshv_store_regs(cpu);
> }
>
> static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg,
> @@ -1227,13 +1259,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.53.0
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 7/7] target/i386/mshv: fix pio handlers clobbering device-modified registers
2026-05-21 16:50 [PATCH v3 0/7] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (5 preceding siblings ...)
2026-05-21 16:50 ` [PATCH v3 6/7] target/i386/mshv: use the register page to set registers Doru Blânzeanu
@ 2026-05-21 16:50 ` Doru Blânzeanu
6 siblings, 0 replies; 12+ messages in thread
From: Doru Blânzeanu @ 2026-05-21 16:50 UTC (permalink / raw)
To: qemu-devel
Cc: Doru Blânzeanu, Paolo Bonzini, Zhao Liu, Wei Liu,
Magnus Kulke, Wei Liu, Magnus Kulke
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>
---
target/i386/mshv/mshv-cpu.c | 74 +++++++++++++++++++++++++------------
1 file changed, 51 insertions(+), 23 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index a2bc29abd4..60d21cedbb 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1331,7 +1331,7 @@ static int pio_write(uint64_t port, const uint8_t *data, uintptr_t size,
return ret;
}
-static int handle_pio_non_str(const CPUState *cpu,
+static int handle_pio_non_str(CPUState *cpu,
hv_x64_io_port_intercept_message *info)
{
size_t len = info->access_info.access_size;
@@ -1340,10 +1340,12 @@ static int handle_pio_non_str(const 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 {
@@ -1374,21 +1376,36 @@ static int handle_pio_non_str(const 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->accel->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. */
+ mshv_store_regs(cpu);
+ cpu->accel->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->accel->dirty = false;
-
return 0;
}
@@ -1504,6 +1521,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];
@@ -1533,18 +1551,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 x64 registers");
- return -1;
- }
+ if (cpu->accel->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. */
+ mshv_store_regs(cpu);
+ cpu->accel->dirty = false;
+ } else {
+ reg_names[1] = HV_X64_REGISTER_RIP;
+ reg_values[1] = rip;
- cpu->accel->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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread