> On 28. Apr 2026, at 15:50, Doru Blânzeanu <[email protected]>
> 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 <[email protected]>
Reviewed-by: Mohamed Mediouni <[email protected]>
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
>
>