@samitouri / QOSamiQemu / commits / ad17d24d18

whpx: i386: switch over from winhvemulation to target/i386/emulate

Using the mshv backend as a base, move away from winhvemulation to using common QEMU code used by the HVF and mshv backends. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Link: https://lore.kernel.org/r/20260223233950.96076-11-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Mohamed Mediouni committed Feb 24, 2026 at 00:39 UTC ad17d24d189661715bb35334085aa640db2decd8
1 file changed +114 -136
target/i386/whpx/whpx-all.c
+114 -136
@@ -15,6 +15,7 @@
15 #include "gdbstub/helpers.h"
16 #include "qemu/accel.h"
17 #include "accel/accel-ops.h"
18 +#include "system/memory.h"
19 #include "system/whpx.h"
20 #include "system/cpus.h"
21 #include "system/runstate.h"
@@ -36,8 +37,12 @@
37 #include "system/whpx-all.h"
38 #include "system/whpx-common.h"
39
40 +#include "emulate/x86_decode.h"
41 +#include "emulate/x86_emu.h"
42 +#include "emulate/x86_flags.h"
43 +#include "emulate/x86_mmu.h"
44 +
45 #include <winhvplatform.h>
40 -#include <winhvemulation.h>
46
47 #define HYPERV_APIC_BUS_FREQUENCY (200000000ULL)
48
@@ -756,158 +761,138 @@ void whpx_get_registers(CPUState *cpu)
761 x86_update_hflags(env);
762 }
763
759 -static HRESULT CALLBACK whpx_emu_ioport_callback(
760 - void *ctx,
761 - WHV_EMULATOR_IO_ACCESS_INFO *IoAccess)
764 +static int emulate_instruction(CPUState *cpu, const uint8_t *insn_bytes, size_t insn_len)
765 {
763 - MemTxAttrs attrs = { 0 };
764 - address_space_rw(&address_space_io, IoAccess->Port, attrs,
765 - &IoAccess->Data, IoAccess->AccessSize,
766 - IoAccess->Direction);
767 - return S_OK;
768 -}
766 + X86CPU *x86_cpu = X86_CPU(cpu);
767 + CPUX86State *env = &x86_cpu->env;
768 + struct x86_decode decode = { 0 };
769 + x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
770
770 -static HRESULT CALLBACK whpx_emu_mmio_callback(
771 - void *ctx,
772 - WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
773 -{
774 - CPUState *cs = (CPUState *)ctx;
775 - AddressSpace *as = cpu_addressspace(cs, MEMTXATTRS_UNSPECIFIED);
771 + whpx_get_registers(cpu);
772 + decode_instruction_stream(env, &decode, &stream);
773 + exec_instruction(env, &decode);
774 + whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE);
775
777 - address_space_rw(as, ma->GpaAddress, MEMTXATTRS_UNSPECIFIED,
778 - ma->Data, ma->AccessSize, ma->Direction);
779 - return S_OK;
776 + return 0;
777 }
778
782 -static HRESULT CALLBACK whpx_emu_getreg_callback(
783 - void *ctx,
784 - const WHV_REGISTER_NAME *RegisterNames,
785 - UINT32 RegisterCount,
786 - WHV_REGISTER_VALUE *RegisterValues)
779 +static int whpx_handle_mmio(CPUState *cpu, WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
780 {
788 - HRESULT hr;
789 - struct whpx_state *whpx = &whpx_global;
790 - CPUState *cpu = (CPUState *)ctx;
781 + WHV_MEMORY_ACCESS_CONTEXT *ctx = &exit_ctx->MemoryAccess;
782 + int ret;
783
792 - hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
793 - whpx->partition, cpu->cpu_index,
794 - RegisterNames, RegisterCount,
795 - RegisterValues);
796 - if (FAILED(hr)) {
797 - error_report("WHPX: Failed to get virtual processor registers,"
798 - " hr=%08lx", hr);
784 + ret = emulate_instruction(cpu, ctx->InstructionBytes, ctx->InstructionByteCount);
785 + if (ret < 0) {
786 + error_report("failed to emulate mmio");
787 + return -1;
788 }
789
801 - return hr;
790 + return 0;
791 }
792
804 -static HRESULT CALLBACK whpx_emu_setreg_callback(
805 - void *ctx,
806 - const WHV_REGISTER_NAME *RegisterNames,
807 - UINT32 RegisterCount,
808 - const WHV_REGISTER_VALUE *RegisterValues)
793 +static void handle_io(CPUState *env, uint16_t port, void *buffer,
794 + int direction, int size, int count)
795 {
810 - HRESULT hr;
811 - struct whpx_state *whpx = &whpx_global;
812 - CPUState *cpu = (CPUState *)ctx;
796 + int i;
797 + uint8_t *ptr = buffer;
798
814 - hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
815 - whpx->partition, cpu->cpu_index,
816 - RegisterNames, RegisterCount,
817 - RegisterValues);
818 - if (FAILED(hr)) {
819 - error_report("WHPX: Failed to set virtual processor registers,"
820 - " hr=%08lx", hr);
799 + for (i = 0; i < count; i++) {
800 + address_space_rw(&address_space_io, port, MEMTXATTRS_UNSPECIFIED,
801 + ptr, size,
802 + direction);
803 + ptr += size;
804 }
822 -
823 - /*
824 - * The emulator just successfully wrote the register state. We clear the
825 - * dirty state so we avoid the double write on resume of the VP.
826 - */
827 - cpu->vcpu_dirty = false;
828 -
829 - return hr;
805 }
806
832 -static HRESULT CALLBACK whpx_emu_translate_callback(
833 - void *ctx,
834 - WHV_GUEST_VIRTUAL_ADDRESS Gva,
835 - WHV_TRANSLATE_GVA_FLAGS TranslateFlags,
836 - WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult,
837 - WHV_GUEST_PHYSICAL_ADDRESS *Gpa)
807 +static void whpx_bump_rip(CPUState *cpu, WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
808 {
839 - HRESULT hr;
840 - struct whpx_state *whpx = &whpx_global;
841 - CPUState *cpu = (CPUState *)ctx;
842 - WHV_TRANSLATE_GVA_RESULT res;
843 -
844 - hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index,
845 - Gva, TranslateFlags, &res, Gpa);
846 - if (FAILED(hr)) {
847 - error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
848 - } else {
849 - *TranslationResult = res.ResultCode;
850 - }
851 -
852 - return hr;
809 + WHV_REGISTER_VALUE reg;
810 + whpx_get_reg(cpu, WHvX64RegisterRip, &reg);
811 + reg.Reg64 = exit_ctx->VpContext.Rip + exit_ctx->VpContext.InstructionLength;
812 + whpx_set_reg(cpu, WHvX64RegisterRip, reg);
813 }
814
855 -static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
856 - .Size = sizeof(WHV_EMULATOR_CALLBACKS),
857 - .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
858 - .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
859 - .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
860 - .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
861 - .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
862 -};
863 -
864 -static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
815 +static int whpx_handle_portio(CPUState *cpu,
816 + WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
817 {
866 - HRESULT hr;
867 - AccelCPUState *vcpu = cpu->accel;
868 - WHV_EMULATOR_STATUS emu_status;
869 -
870 - hr = whp_dispatch.WHvEmulatorTryMmioEmulation(
871 - vcpu->emulator, cpu,
872 - &vcpu->exit_ctx.VpContext, ctx,
873 - &emu_status);
874 - if (FAILED(hr)) {
875 - error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
876 - return -1;
877 - }
818 + WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx = &exit_ctx->IoPortAccess;
819 + X86CPU *x86_cpu = X86_CPU(cpu);
820 + CPUX86State *env = &x86_cpu->env;
821 + int ret;
822
879 - if (!emu_status.EmulationSuccessful) {
880 - error_report("WHPX: Failed to emulate MMIO access with"
881 - " EmulatorReturnStatus: %u", emu_status.AsUINT32);
823 + if (!ctx->AccessInfo.StringOp && !ctx->AccessInfo.IsWrite) {
824 + uint64_t val = 0;
825 + WHV_REGISTER_VALUE reg;
826 +
827 + whpx_get_reg(cpu, WHvX64RegisterRax, &reg);
828 + handle_io(cpu, ctx->PortNumber, &val, 0, ctx->AccessInfo.AccessSize, 1);
829 + if (ctx->AccessInfo.AccessSize == 1) {
830 + reg.Reg8 = val;
831 + } else if (ctx->AccessInfo.AccessSize == 2) {
832 + reg.Reg16 = val;
833 + } else if (ctx->AccessInfo.AccessSize == 4) {
834 + reg.Reg64 = (uint32_t)val;
835 + } else {
836 + reg.Reg64 = (uint64_t)val;
837 + }
838 + whpx_bump_rip(cpu, exit_ctx);
839 + whpx_set_reg(cpu, WHvX64RegisterRax, reg);
840 + return 0;
841 + } else if (!ctx->AccessInfo.StringOp && ctx->AccessInfo.IsWrite) {
842 + RAX(env) = ctx->Rax;
843 + handle_io(cpu, ctx->PortNumber, &RAX(env), 1, ctx->AccessInfo.AccessSize, 1);
844 + whpx_bump_rip(cpu, exit_ctx);
845 + return 0;
846 + }
847 +
848 + ret = emulate_instruction(cpu, ctx->InstructionBytes, exit_ctx->VpContext.InstructionLength);
849 + if (ret < 0) {
850 + error_report("failed to emulate I/O port access");
851 return -1;
852 }
853
854 return 0;
855 }
856
888 -static int whpx_handle_portio(CPUState *cpu,
889 - WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
857 +static void write_mem(CPUState *cpu, void *data, target_ulong addr, int bytes)
858 {
891 - HRESULT hr;
892 - AccelCPUState *vcpu = cpu->accel;
893 - WHV_EMULATOR_STATUS emu_status;
859 + vmx_write_mem(cpu, addr, data, bytes);
860 +}
861
895 - hr = whp_dispatch.WHvEmulatorTryIoEmulation(
896 - vcpu->emulator, cpu,
897 - &vcpu->exit_ctx.VpContext, ctx,
898 - &emu_status);
899 - if (FAILED(hr)) {
900 - error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
901 - return -1;
902 - }
862 +static void read_mem(CPUState *cpu, void *data, target_ulong addr, int bytes)
863 +{
864 + vmx_read_mem(cpu, data, addr, bytes);
865 +}
866
904 - if (!emu_status.EmulationSuccessful) {
905 - error_report("WHPX: Failed to emulate PortIO access with"
906 - " EmulatorReturnStatus: %u", emu_status.AsUINT32);
907 - return -1;
867 +static void read_segment_descriptor(CPUState *cpu,
868 + struct x86_segment_descriptor *desc,
869 + enum X86Seg seg_idx)
870 +{
871 + bool ret;
872 + X86CPU *x86_cpu = X86_CPU(cpu);
873 + CPUX86State *env = &x86_cpu->env;
874 + SegmentCache *seg = &env->segs[seg_idx];
875 + x86_segment_selector sel = { .sel = seg->selector & 0xFFFF };
876 +
877 + ret = x86_read_segment_descriptor(cpu, desc, sel);
878 + if (ret == false) {
879 + error_report("failed to read segment descriptor");
880 + abort();
881 }
882 +}
883
910 - return 0;
884 +
885 +static const struct x86_emul_ops whpx_x86_emul_ops = {
886 + .read_mem = read_mem,
887 + .write_mem = write_mem,
888 + .read_segment_descriptor = read_segment_descriptor,
889 + .handle_io = handle_io
890 +};
891 +
892 +static void whpx_init_emu(void)
893 +{
894 + init_decoder();
895 + init_emu(&whpx_x86_emul_ops);
896 }
897
898 /*
@@ -1279,8 +1264,9 @@ bool whpx_arch_supports_guest_debug(void)
1264
1265 void whpx_arch_destroy_vcpu(CPUState *cpu)
1266 {
1282 - AccelCPUState *vcpu = cpu->accel;
1283 - whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1267 + X86CPU *x86cpu = X86_CPU(cpu);
1268 + CPUX86State *env = &x86cpu->env;
1269 + g_free(env->emu_mmio_buf);
1270 }
1271
1272 /* Returns the address of the next instruction that is about to be executed. */
@@ -1639,11 +1625,11 @@ int whpx_vcpu_run(CPUState *cpu)
1625
1626 switch (vcpu->exit_ctx.ExitReason) {
1627 case WHvRunVpExitReasonMemoryAccess:
1642 - ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
1628 + ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx);
1629 break;
1630
1631 case WHvRunVpExitReasonX64IoPortAccess:
1646 - ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
1632 + ret = whpx_handle_portio(cpu, &vcpu->exit_ctx);
1633 break;
1634
1635 case WHvRunVpExitReasonX64InterruptWindow:
@@ -1990,22 +1976,11 @@ int whpx_init_vcpu(CPUState *cpu)
1976
1977 vcpu = g_new0(AccelCPUState, 1);
1978
1993 - hr = whp_dispatch.WHvEmulatorCreateEmulator(
1994 - &whpx_emu_callbacks,
1995 - &vcpu->emulator);
1996 - if (FAILED(hr)) {
1997 - error_report("WHPX: Failed to setup instruction completion support,"
1998 - " hr=%08lx", hr);
1999 - ret = -EINVAL;
2000 - goto error;
2001 - }
2002 -
1979 hr = whp_dispatch.WHvCreateVirtualProcessor(
1980 whpx->partition, cpu->cpu_index, 0);
1981 if (FAILED(hr)) {
1982 error_report("WHPX: Failed to create a virtual processor,"
1983 " hr=%08lx", hr);
2008 - whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1984 ret = -EINVAL;
1985 goto error;
1986 }
@@ -2067,6 +2042,8 @@ int whpx_init_vcpu(CPUState *cpu)
2042 max_vcpu_index = max(max_vcpu_index, cpu->cpu_index);
2043 qemu_add_vm_change_state_handler(whpx_cpu_update_state, env);
2044
2045 + env->emu_mmio_buf = g_new(char, 4096);
2046 +
2047 return 0;
2048
2049 error:
@@ -2256,6 +2233,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
2233 }
2234
2235 whpx_memory_init();
2236 + whpx_init_emu();
2237
2238 printf("Windows Hypervisor Platform accelerator is operational\n");
2239 return 0;