* [PATCH 1/6] target/i386/mshv: remove duplicate function for reading vcpu registers
2026-04-28 13:50 [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
@ 2026-04-28 13:50 ` Doru Blânzeanu
2026-04-29 10:19 ` Magnus Kulke
2026-04-28 13:50 ` [PATCH 2/6] accel/mshv: move vcpu arch specific initialization after vcpu creation Doru Blânzeanu
` (5 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Doru Blânzeanu @ 2026-04-28 13:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
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>
---
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] 18+ messages in thread* Re: [PATCH 1/6] target/i386/mshv: remove duplicate function for reading vcpu registers
2026-04-28 13:50 ` [PATCH 1/6] target/i386/mshv: remove duplicate function for reading vcpu registers Doru Blânzeanu
@ 2026-04-29 10:19 ` Magnus Kulke
0 siblings, 0 replies; 18+ messages in thread
From: Magnus Kulke @ 2026-04-29 10:19 UTC (permalink / raw)
To: Doru Blânzeanu; +Cc: qemu-devel, Wei Liu, Paolo Bonzini, Zhao Liu
On Tue, Apr 28, 2026 at 04:50:48PM +0300, Doru Blânzeanu wrote:
> 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>
> ---
> 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
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 2/6] accel/mshv: move vcpu arch specific initialization after vcpu creation
2026-04-28 13:50 [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
2026-04-28 13:50 ` [PATCH 1/6] target/i386/mshv: remove duplicate function for reading vcpu registers Doru Blânzeanu
@ 2026-04-28 13:50 ` Doru Blânzeanu
2026-04-29 10:21 ` Magnus Kulke
2026-04-28 13:50 ` [PATCH 3/6] include/hw/hyperv: add hv_vp_register_page struct definition Doru Blânzeanu
` (4 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Doru Blânzeanu @ 2026-04-28 13:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
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>
---
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] 18+ messages in thread* Re: [PATCH 2/6] accel/mshv: move vcpu arch specific initialization after vcpu creation
2026-04-28 13:50 ` [PATCH 2/6] accel/mshv: move vcpu arch specific initialization after vcpu creation Doru Blânzeanu
@ 2026-04-29 10:21 ` Magnus Kulke
0 siblings, 0 replies; 18+ messages in thread
From: Magnus Kulke @ 2026-04-29 10:21 UTC (permalink / raw)
To: Doru Blânzeanu; +Cc: qemu-devel, Wei Liu, Paolo Bonzini, Zhao Liu
On Tue, Apr 28, 2026 at 04:50:49PM +0300, Doru Blânzeanu wrote:
> 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>
> ---
> 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
Reviewed-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/6] include/hw/hyperv: add hv_vp_register_page struct definition
2026-04-28 13:50 [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
2026-04-28 13:50 ` [PATCH 1/6] target/i386/mshv: remove duplicate function for reading vcpu registers Doru Blânzeanu
2026-04-28 13:50 ` [PATCH 2/6] accel/mshv: move vcpu arch specific initialization after vcpu creation Doru Blânzeanu
@ 2026-04-28 13:50 ` Doru Blânzeanu
2026-04-28 17:44 ` Mohamed Mediouni
2026-04-29 10:28 ` Magnus Kulke
2026-04-28 13:50 ` [PATCH 4/6] target/i386/mshv: hv_vp_register_page setup for the vcpu Doru Blânzeanu
` (3 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Doru Blânzeanu @ 2026-04-28 13:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
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_mini.h | 103 +++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index c3a8f33280..07e322865a 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -474,6 +474,109 @@ 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)
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH 3/6] include/hw/hyperv: add hv_vp_register_page struct definition
2026-04-28 13:50 ` [PATCH 3/6] include/hw/hyperv: add hv_vp_register_page struct definition Doru Blânzeanu
@ 2026-04-28 17:44 ` Mohamed Mediouni
2026-04-29 10:28 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Mohamed Mediouni @ 2026-04-28 17:44 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
> On 28. Apr 2026, at 15:50, Doru Blânzeanu <dblanzeanu@linux.microsoft.com> 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>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> include/hw/hyperv/hvgdk_mini.h | 103 +++++++++++++++++++++++++++++++++
> 1 file changed, 103 insertions(+)
>
> diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
> index c3a8f33280..07e322865a 100644
> --- a/include/hw/hyperv/hvgdk_mini.h
> +++ b/include/hw/hyperv/hvgdk_mini.h
> @@ -474,6 +474,109 @@ 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)
> --
> 2.53.0
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH 3/6] include/hw/hyperv: add hv_vp_register_page struct definition
2026-04-28 13:50 ` [PATCH 3/6] include/hw/hyperv: add hv_vp_register_page struct definition Doru Blânzeanu
2026-04-28 17:44 ` Mohamed Mediouni
@ 2026-04-29 10:28 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Magnus Kulke @ 2026-04-29 10:28 UTC (permalink / raw)
To: Doru Blânzeanu; +Cc: qemu-devel, Wei Liu, Paolo Bonzini, Zhao Liu
On Tue, Apr 28, 2026 at 04:50:50PM +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_mini.h | 103 +++++++++++++++++++++++++++++++++
> 1 file changed, 103 insertions(+)
>
> diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
In the kernel and the mshv crates the definitions are in hvhdk.h. We
probably want to put it into the same header in QEMU to avoid confusion.
> index c3a8f33280..07e322865a 100644
> --- a/include/hw/hyperv/hvgdk_mini.h
> +++ b/include/hw/hyperv/hvgdk_mini.h
> @@ -474,6 +474,109 @@ 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)
> --
> 2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/6] target/i386/mshv: hv_vp_register_page setup for the vcpu
2026-04-28 13:50 [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (2 preceding siblings ...)
2026-04-28 13:50 ` [PATCH 3/6] include/hw/hyperv: add hv_vp_register_page struct definition Doru Blânzeanu
@ 2026-04-28 13:50 ` Doru Blânzeanu
2026-04-28 17:49 ` Mohamed Mediouni
2026-04-29 11:21 ` Magnus Kulke
2026-04-28 13:50 ` [PATCH 5/6] target/i386/mshv: use the register page to get registers Doru Blânzeanu
` (2 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Doru Blânzeanu @ 2026-04-28 13:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
When the vcpu is created, call mmap to configure access to the register page.
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 | 3 +++
target/i386/mshv/mshv-cpu.c | 18 ++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 0b539155c4..0108e2157b 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2016,6 +2016,9 @@ typedef struct CPUArchState {
uint64_t msr_bndcfgs;
uint64_t efer;
+ /* Shared register page */
+ struct hv_vp_register_page *regs_page;
+
/* Beginning of state preserved by INIT (dummy marker). */
struct {} start_init_save;
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 9defd05db6..42b6fb1912 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1595,6 +1595,19 @@ void mshv_arch_init_vcpu(CPUState *cpu)
+ sizeof(hv_input_get_vp_registers)
> HV_HYP_PAGE_SIZE));
+
+ /* mmap the registers page */
+ void *rp = 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;
+ }
+
state->hvcall_args.base = mem;
state->hvcall_args.input_page = mem;
state->hvcall_args.output_page = (uint8_t *)mem + page;
@@ -1608,6 +1621,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] 18+ messages in thread* Re: [PATCH 4/6] target/i386/mshv: hv_vp_register_page setup for the vcpu
2026-04-28 13:50 ` [PATCH 4/6] target/i386/mshv: hv_vp_register_page setup for the vcpu Doru Blânzeanu
@ 2026-04-28 17:49 ` Mohamed Mediouni
2026-04-29 11:21 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Mohamed Mediouni @ 2026-04-28 17:49 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
> On 28. Apr 2026, at 15:50, Doru Blânzeanu <dblanzeanu@linux.microsoft.com> wrote:
>
> When the vcpu is created, call mmap to configure access to the register page.
>
> 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 | 3 +++
> target/i386/mshv/mshv-cpu.c | 18 ++++++++++++++++++
> 2 files changed, 21 insertions(+)
>
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index 0b539155c4..0108e2157b 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -2016,6 +2016,9 @@ typedef struct CPUArchState {
> uint64_t msr_bndcfgs;
> uint64_t efer;
>
> + /* Shared register page */
> + struct hv_vp_register_page *regs_page;
> +
> /* Beginning of state preserved by INIT (dummy marker). */
> struct {} start_init_save;
>
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 9defd05db6..42b6fb1912 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -1595,6 +1595,19 @@ void mshv_arch_init_vcpu(CPUState *cpu)
> + sizeof(hv_input_get_vp_registers)
>> HV_HYP_PAGE_SIZE));
>
> +
> + /* mmap the registers page */
> + void *rp = 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));
Hi,
I think it’s better to make this fatal tbh, the perf hit is just way
too big imo.
Otherwise,
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> + env->regs_page = NULL;
> + } else {
> + env->regs_page = (struct hv_vp_register_page *) rp;
> + }
> +
> state->hvcall_args.base = mem;
> state->hvcall_args.input_page = mem;
> state->hvcall_args.output_page = (uint8_t *)mem + page;
> @@ -1608,6 +1621,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 [flat|nested] 18+ messages in thread* Re: [PATCH 4/6] target/i386/mshv: hv_vp_register_page setup for the vcpu
2026-04-28 13:50 ` [PATCH 4/6] target/i386/mshv: hv_vp_register_page setup for the vcpu Doru Blânzeanu
2026-04-28 17:49 ` Mohamed Mediouni
@ 2026-04-29 11:21 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Magnus Kulke @ 2026-04-29 11:21 UTC (permalink / raw)
To: Doru Blânzeanu; +Cc: qemu-devel, Wei Liu, Paolo Bonzini, Zhao Liu
On Tue, Apr 28, 2026 at 04:50:51PM +0300, Doru Blânzeanu wrote:
> When the vcpu is created, call mmap to configure access to the register page.
>
> 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 | 3 +++
> target/i386/mshv/mshv-cpu.c | 18 ++++++++++++++++++
> 2 files changed, 21 insertions(+)
>
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index 0b539155c4..0108e2157b 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -2016,6 +2016,9 @@ typedef struct CPUArchState {
> uint64_t msr_bndcfgs;
> uint64_t efer;
>
> + /* Shared register page */
> + struct hv_vp_register_page *regs_page;
> +
We probably want to put this behind a compile-time guard:
#if defined(CONFIG_MSHV)
...
#endif
> /* Beginning of state preserved by INIT (dummy marker). */
> struct {} start_init_save;
>
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 9defd05db6..42b6fb1912 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -1595,6 +1595,19 @@ void mshv_arch_init_vcpu(CPUState *cpu)
> + sizeof(hv_input_get_vp_registers)
> > HV_HYP_PAGE_SIZE));
>
> +
> + /* mmap the registers page */
> + void *rp = mmap(NULL, page, PROT_READ | PROT_WRITE,
> + MAP_SHARED, mshv_vcpufd(cpu),
> + MSHV_VP_MMAP_OFFSET_REGISTERS * page);
nit: convention is to declare variables at the top of a fn:
int cpu_fd = mshv_vcpufd(cpu);
void *regs_page;
...
regs_page = mmap(..., cpu_fd, ...);
> + if (rp == MAP_FAILED) {
> + warn_report("register page mmap failed, falling back to hypercalls: %s",
We're not falling back at this specific place. so it probably makes
sense to just say register page mmap failed and leave it at that here.
> + strerror(errno));
> + env->regs_page = NULL;
> + } else {
> + env->regs_page = (struct hv_vp_register_page *) rp;
> + }
> +
> state->hvcall_args.base = mem;
> state->hvcall_args.input_page = mem;
> state->hvcall_args.output_page = (uint8_t *)mem + page;
> @@ -1608,6 +1621,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 [flat|nested] 18+ messages in thread
* [PATCH 5/6] target/i386/mshv: use the register page to get registers
2026-04-28 13:50 [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (3 preceding siblings ...)
2026-04-28 13:50 ` [PATCH 4/6] target/i386/mshv: hv_vp_register_page setup for the vcpu Doru Blânzeanu
@ 2026-04-28 13:50 ` Doru Blânzeanu
2026-04-28 18:04 ` Mohamed Mediouni
2026-04-29 11:31 ` Magnus Kulke
2026-04-28 13:50 ` [PATCH 6/6] target/i386/mshv: use the register page to set registers Doru Blânzeanu
2026-04-28 18:36 ` [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Mohamed Mediouni
6 siblings, 2 replies; 18+ messages in thread
From: Doru Blânzeanu @ 2026-04-28 13:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
Change the mshv_load_regs to use the register page when it is mmapped
and is valid.
Otherwise use the existing logic that uses ioctls to fetch registers.
When retrieving the special registers, there are some registers that are
not present in the register page: TR, LDTR, GDTR, IDTR, CR2, APIC_BASE.
For this ones we still need to use ioctls to correctly fetch.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
target/i386/mshv/mshv-cpu.c | 137 +++++++++++++++++++++++++++++++++---
1 file changed, 128 insertions(+), 9 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 42b6fb1912..7949493e97 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -107,6 +107,15 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
HV_X64_REGISTER_XMM_CONTROL_STATUS,
};
+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)
{
@@ -401,6 +410,105 @@ 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);
+}
+
+static int mshv_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 */
+ 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]);
+
+ /* 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 = 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)
+{
+ int ret;
+
+ /* General Purpose Registers */
+ mshv_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;
+}
+
int mshv_get_special_regs(CPUState *cpu)
{
@@ -424,18 +532,29 @@ int mshv_get_special_regs(CPUState *cpu)
int mshv_load_regs(CPUState *cpu)
{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
int ret;
- ret = mshv_get_standard_regs(cpu);
- if (ret < 0) {
- error_report("Failed to load standard registers");
- return -1;
- }
+ /* Use register vp page to optimize registers access */
+ if (env->regs_page && env->regs_page->isvalid != 0) {
+ ret = mshv_get_registers_vp_page(cpu);
+ if (ret < 0) {
+ error_report("Failed to load registers from vp page");
+ return -1;
+ }
+ } else {
+ ret = mshv_get_standard_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to load standard registers");
+ return -1;
+ }
- ret = mshv_get_special_regs(cpu);
- if (ret < 0) {
- error_report("Failed to load special registers");
- return -1;
+ ret = mshv_get_special_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to load special registers");
+ return -1;
+ }
}
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH 5/6] target/i386/mshv: use the register page to get registers
2026-04-28 13:50 ` [PATCH 5/6] target/i386/mshv: use the register page to get registers Doru Blânzeanu
@ 2026-04-28 18:04 ` Mohamed Mediouni
2026-04-29 11:31 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Mohamed Mediouni @ 2026-04-28 18:04 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
> On 28. Apr 2026, at 15:50, Doru Blânzeanu <dblanzeanu@linux.microsoft.com> wrote:
>
> Change the mshv_load_regs to use the register page when it is mmapped
> and is valid.
> Otherwise use the existing logic that uses ioctls to fetch registers.
>
> When retrieving the special registers, there are some registers that are
> not present in the register page: TR, LDTR, GDTR, IDTR, CR2, APIC_BASE.
> For this ones we still need to use ioctls to correctly fetch.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
But with comments below...
> ---
> target/i386/mshv/mshv-cpu.c | 137 +++++++++++++++++++++++++++++++++---
> 1 file changed, 128 insertions(+), 9 deletions(-)
>
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 42b6fb1912..7949493e97 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -107,6 +107,15 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
> HV_X64_REGISTER_XMM_CONTROL_STATUS,
> };
>
> +static enum hv_register_name NON_VP_PAGE_REGISTER_NAMES[6] = {
> + HV_X64_REGISTER_TR,
> + HV_X64_REGISTER_LDTR,
> + HV_X64_REGISTER_GDTR,
Those two _not_ being in a shared register page does cause quite a bit of
pain for perf if not dealt with carefully which leads to…
> + 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)
> {
> @@ -401,6 +410,105 @@ 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);
> +}
> +
> +static int mshv_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 */
> + 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]);
It’s probably best to split this function.
A “regular” MMIO access is going to need segment info, but not GDTR/LDTR.
Otherwise you’d effectively defeat the point of the shared register page. Ideally
0 register fetch/writes outside of the shared register page would happen on an MMIO
access.
> +
> + /* 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 = 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;
For CRs, there’s a read_cr x86 emulation callback now, although it never
needs to read CR2.
> +
> + cpu_set_apic_base(x86cpu->apic_state, assocs[5].value.reg64);
For APIC base, what I ended up doing on WHPX (upcoming 11.1 series) is
to just trap APIC base writes so that there’s never a need to read it.
> +
> + return ret;
> +}
> +
> +static int mshv_get_registers_vp_page(CPUState *cpu)
> +{
> + int ret;
> +
> + /* General Purpose Registers */
> + mshv_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;
> +}
> +
>
> int mshv_get_special_regs(CPUState *cpu)
> {
> @@ -424,18 +532,29 @@ int mshv_get_special_regs(CPUState *cpu)
>
> int mshv_load_regs(CPUState *cpu)
> {
> + X86CPU *x86_cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86_cpu->env;
> int ret;
>
> - ret = mshv_get_standard_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to load standard registers");
> - return -1;
> - }
> + /* Use register vp page to optimize registers access */
> + if (env->regs_page && env->regs_page->isvalid != 0) {
> + ret = mshv_get_registers_vp_page(cpu);
> + if (ret < 0) {
> + error_report("Failed to load registers from vp page");
> + return -1;
> + }
> + } else {
> + ret = mshv_get_standard_regs(cpu);
> + if (ret < 0) {
> + error_report("Failed to load standard registers");
> + return -1;
> + }
>
> - ret = mshv_get_special_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to load special registers");
> - return -1;
> + ret = mshv_get_special_regs(cpu);
> + if (ret < 0) {
> + error_report("Failed to load special registers");
> + return -1;
> + }
> }
>
> return 0;
> --
> 2.53.0
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH 5/6] target/i386/mshv: use the register page to get registers
2026-04-28 13:50 ` [PATCH 5/6] target/i386/mshv: use the register page to get registers Doru Blânzeanu
2026-04-28 18:04 ` Mohamed Mediouni
@ 2026-04-29 11:31 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Magnus Kulke @ 2026-04-29 11:31 UTC (permalink / raw)
To: Doru Blânzeanu; +Cc: qemu-devel, Wei Liu, Paolo Bonzini, Zhao Liu
On Tue, Apr 28, 2026 at 04:50:52PM +0300, Doru Blânzeanu wrote:
> Change the mshv_load_regs to use the register page when it is mmapped
> and is valid.
> Otherwise use the existing logic that uses ioctls to fetch registers.
>
> When retrieving the special registers, there are some registers that are
> not present in the register page: TR, LDTR, GDTR, IDTR, CR2, APIC_BASE.
> For this ones we still need to use ioctls to correctly fetch.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
> ---
> target/i386/mshv/mshv-cpu.c | 137 +++++++++++++++++++++++++++++++++---
> 1 file changed, 128 insertions(+), 9 deletions(-)
>
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 42b6fb1912..7949493e97 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -107,6 +107,15 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
> HV_X64_REGISTER_XMM_CONTROL_STATUS,
> };
>
> +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)
> {
> @@ -401,6 +410,105 @@ 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);
> +}
> +
> +static int mshv_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 */
> + 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]);
> +
> + /* 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 = 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);
Do we know whether MMIO emulation requires the
NON_VP_PAGE_REGISTER_NAMES to be roundtripped? I understand this is how
it is done in the mshv-ioctls crate, but I understand this was motivated
mostly by maitaining parity for sregs between KVM and MSHV
implementations.
Did you test what happens if we don't perform an ioctl and just ignore
those registers?
> +
> + return ret;
> +}
> +
> +static int mshv_get_registers_vp_page(CPUState *cpu)
> +{
> + int ret;
> +
> + /* General Purpose Registers */
> + mshv_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;
> +}
> +
>
> int mshv_get_special_regs(CPUState *cpu)
> {
> @@ -424,18 +532,29 @@ int mshv_get_special_regs(CPUState *cpu)
>
> int mshv_load_regs(CPUState *cpu)
> {
> + X86CPU *x86_cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86_cpu->env;
> int ret;
>
> - ret = mshv_get_standard_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to load standard registers");
> - return -1;
> - }
> + /* Use register vp page to optimize registers access */
> + if (env->regs_page && env->regs_page->isvalid != 0) {
> + ret = mshv_get_registers_vp_page(cpu);
> + if (ret < 0) {
> + error_report("Failed to load registers from vp page");
nit: slightly inaccurate, since loading registers is infallible. it's
the ioctl op that fails, no?
> + return -1;
> + }
> + } else {
> + ret = mshv_get_standard_regs(cpu);
> + if (ret < 0) {
> + error_report("Failed to load standard registers");
> + return -1;
> + }
>
> - ret = mshv_get_special_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to load special registers");
> - return -1;
> + ret = mshv_get_special_regs(cpu);
> + if (ret < 0) {
> + error_report("Failed to load special registers");
> + return -1;
> + }
> }
>
> return 0;
> --
> 2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 6/6] target/i386/mshv: use the register page to set registers
2026-04-28 13:50 [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (4 preceding siblings ...)
2026-04-28 13:50 ` [PATCH 5/6] target/i386/mshv: use the register page to get registers Doru Blânzeanu
@ 2026-04-28 13:50 ` Doru Blânzeanu
2026-04-28 18:05 ` Mohamed Mediouni
2026-04-29 11:38 ` Magnus Kulke
2026-04-28 18:36 ` [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Mohamed Mediouni
6 siblings, 2 replies; 18+ messages in thread
From: Doru Blânzeanu @ 2026-04-28 13:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
Update mshv_store_regs to use the register page when it is mmapped and
valid to set registers.
Otherwise use the ioctls to set the registers.
Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
---
target/i386/mshv/mshv-cpu.c | 45 +++++++++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 4 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 7949493e97..6bb3e6d4a9 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -294,14 +294,51 @@ static int set_standard_regs(const CPUState *cpu)
return 0;
}
+static void mshv_set_standard_regs_vp_page(CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+
+ 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);
+}
+
int mshv_store_regs(CPUState *cpu)
{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
int ret;
- ret = set_standard_regs(cpu);
- if (ret < 0) {
- error_report("Failed to store standard registers");
- return -1;
+ /* 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) {
+ error_report("Failed to store standard registers");
+ return -1;
+ }
}
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH 6/6] target/i386/mshv: use the register page to set registers
2026-04-28 13:50 ` [PATCH 6/6] target/i386/mshv: use the register page to set registers Doru Blânzeanu
@ 2026-04-28 18:05 ` Mohamed Mediouni
2026-04-29 11:38 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Mohamed Mediouni @ 2026-04-28 18:05 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
> On 28. Apr 2026, at 15:50, Doru Blânzeanu <dblanzeanu@linux.microsoft.com> wrote:
>
> Update mshv_store_regs to use the register page when it is mmapped and
> valid to set registers.
> Otherwise use the ioctls to set the registers.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> target/i386/mshv/mshv-cpu.c | 45 +++++++++++++++++++++++++++++++++----
> 1 file changed, 41 insertions(+), 4 deletions(-)
>
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 7949493e97..6bb3e6d4a9 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -294,14 +294,51 @@ static int set_standard_regs(const CPUState *cpu)
> return 0;
> }
>
> +static void mshv_set_standard_regs_vp_page(CPUState *cpu)
> +{
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
> +
> + 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);
> +}
> +
> int mshv_store_regs(CPUState *cpu)
> {
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
> int ret;
>
> - ret = set_standard_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to store standard registers");
> - return -1;
> + /* 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) {
> + error_report("Failed to store standard registers");
> + return -1;
> + }
> }
>
> return 0;
> --
> 2.53.0
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH 6/6] target/i386/mshv: use the register page to set registers
2026-04-28 13:50 ` [PATCH 6/6] target/i386/mshv: use the register page to set registers Doru Blânzeanu
2026-04-28 18:05 ` Mohamed Mediouni
@ 2026-04-29 11:38 ` Magnus Kulke
1 sibling, 0 replies; 18+ messages in thread
From: Magnus Kulke @ 2026-04-29 11:38 UTC (permalink / raw)
To: Doru Blânzeanu; +Cc: qemu-devel, Wei Liu, Paolo Bonzini, Zhao Liu
On Tue, Apr 28, 2026 at 04:50:53PM +0300, Doru Blânzeanu wrote:
> Update mshv_store_regs to use the register page when it is mmapped and
> valid to set registers.
> Otherwise use the ioctls to set the registers.
>
> Signed-off-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
> ---
> target/i386/mshv/mshv-cpu.c | 45 +++++++++++++++++++++++++++++++++----
> 1 file changed, 41 insertions(+), 4 deletions(-)
>
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 7949493e97..6bb3e6d4a9 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -294,14 +294,51 @@ static int set_standard_regs(const CPUState *cpu)
> return 0;
> }
>
> +static void mshv_set_standard_regs_vp_page(CPUState *cpu)
> +{
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
> +
> + 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);
> +}
> +
> int mshv_store_regs(CPUState *cpu)
> {
> + X86CPU *x86cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86cpu->env;
> int ret;
>
> - ret = set_standard_regs(cpu);
> - if (ret < 0) {
> - error_report("Failed to store standard registers");
> - return -1;
> + /* 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) {
> + error_report("Failed to store standard registers");
> + return -1;
> + }
> }
I think for consistency sake it would be better to also write back
sregs, but this is reworked in the migration patch series anyway, so we
can leave it as is here.
>
> return 0;
> --
> 2.53.0
Reviewed-By: Magnus Kulke <magnuskulke@linux.microsoft.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access
2026-04-28 13:50 [PATCH 0/6] target/i386/mshv: use hv_vp_register_page for fast register access Doru Blânzeanu
` (5 preceding siblings ...)
2026-04-28 13:50 ` [PATCH 6/6] target/i386/mshv: use the register page to set registers Doru Blânzeanu
@ 2026-04-28 18:36 ` Mohamed Mediouni
6 siblings, 0 replies; 18+ messages in thread
From: Mohamed Mediouni @ 2026-04-28 18:36 UTC (permalink / raw)
To: Doru Blânzeanu
Cc: qemu-devel, Magnus Kulke, Wei Liu, Paolo Bonzini, Zhao Liu
> On 28. Apr 2026, at 15:50, Doru Blânzeanu <dblanzeanu@linux.microsoft.com> wrote:
>
> This series adds support for using the hypervisor's vp register page
> in the mshv accelerator to optimize vcpu register access on mmio and pio
> exits.
>
> Currently, all register reads and write go through hypercalls (ioctls),
> which adds overhead on every VM exit. The VP register page is a shared
> memory page that the hypervisor populates with vcpu register state,
> allowing Qemu to read and write registers directly without hypercalls.
>
> The series is structured as follows:
> 1. Remove the duplicate `fetch_guest_state` function, consolidating
> register loading into `mshv_load_regs`.
> 2. Move `mshv_arch_init_vcpu` after vcpu creation so the vcpu fd is
> valid when we need it for mmap.
> 3. Define the `hv_vp_register_page` structure in `hvgdk_mini.h`, matching
> the layout used by the Linux kernel's mshv driver.
> 4. Set up the register page by mmapping the vcpu fd at init time. If the
> mmap fails, we fall back gracefully to the existing hypercall path.
> 5. Use the register page to read registers on VM exit. General purpose
> registers, RIP, RFLAGS, segment registers, and control registers
> (CR0, CR4, CR4, CR8, EFER) are read directly from the page. Registers
> not present on the page (TR, LDTR, GDTR, IDTR, CR2, APIC_BASE) are still
> fetched via hypercall.
> 6. Use register page to write registers on vmentry. GP registers,
> RIP, and RFLAGS are written to the page with the appropriate dirty
> bits set, avoiding the hypercall for the standard register store.
>
> The register page is only used when it has been successfully mmapped and
> the hypervisor has marked it as valid (`isvalid != 0`). Otherwise, the
> existing hypercall-based path is used as a fallback.
Hello,
Some overall comments after reading this series:
- bifurcated state sync is inevitable with not syncing the full
state for most MMIO/I/O port accesses.
It’s wanted to skip some state. LDTR/GDTR are among the painful
ones and you’ll very much want to avoid syncing those if you can.
In the typical WHP emulation case, there’s 0 state used outside
of the shared register page and getting there made things so much
smoother than otherwise.
- but there’s an exception:
A pattern that causes problems:
/* 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;
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;
When put together with hw/i386/vmport.c, which calls cpu_synchronize_state
on an I/O port read (thankfully not very frequent), you’ll get issues with
this as vmport does both read and set register values on its own.
In WHPX, was dealt with in: https://patchew.org/QEMU/20260422214225.2242-1-mohamed@unpredictable.fr/20260422214225.2242-12-mohamed@unpredictable.fr/
and you’ll probably want to do something similar.
I imagine that “cpu->accel->dirty = false;” was probably an attempt to
get things to boot when faced with that.
This adds a small constraint that if additional state will be fetched in
the future, then writes have to happen either before that fetch, or on the
now synced state instead of the partial view.
^ permalink raw reply [flat|nested] 18+ messages in thread