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, ®);
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, ®);
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;