From: Mohamed Mediouni <mohamed@unpredictable.fr>
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>
---
target/i386/whpx/whpx-all.c | 274 +++++++++++++++++-------------------
1 file changed, 126 insertions(+), 148 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index cdcaebbe167..eb6076d2f49 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -15,6 +15,7 @@
#include "gdbstub/helpers.h"
#include "qemu/accel.h"
#include "accel/accel-ops.h"
+#include "system/memory.h"
#include "system/whpx.h"
#include "system/cpus.h"
#include "system/runstate.h"
@@ -36,8 +37,12 @@
#include "system/whpx-all.h"
#include "system/whpx-common.h"
+#include "emulate/x86_decode.h"
+#include "emulate/x86_emu.h"
+#include "emulate/x86_flags.h"
+#include "emulate/x86_mmu.h"
+
#include <winhvplatform.h>
-#include <winhvemulation.h>
#define HYPERV_APIC_BUS_FREQUENCY (200000000ULL)
@@ -756,160 +761,140 @@ void whpx_get_registers(CPUState *cpu)
x86_update_hflags(env);
}
-static HRESULT CALLBACK whpx_emu_ioport_callback(
- void *ctx,
- WHV_EMULATOR_IO_ACCESS_INFO *IoAccess)
+static int emulate_instruction(CPUState *cpu, const uint8_t *insn_bytes, size_t insn_len)
{
- MemTxAttrs attrs = { 0 };
- address_space_rw(&address_space_io, IoAccess->Port, attrs,
- &IoAccess->Data, IoAccess->AccessSize,
- IoAccess->Direction);
- return S_OK;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ struct x86_decode decode = { 0 };
+ x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
+
+ whpx_get_registers(cpu);
+ decode_instruction_stream(env, &decode, &stream);
+ exec_instruction(env, &decode);
+ whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE);
+
+ return 0;
}
-static HRESULT CALLBACK whpx_emu_mmio_callback(
- void *ctx,
- WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
+static int whpx_handle_mmio(CPUState *cpu, WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
{
- CPUState *cs = (CPUState *)ctx;
- AddressSpace *as = cpu_addressspace(cs, MEMTXATTRS_UNSPECIFIED);
+ WHV_MEMORY_ACCESS_CONTEXT *ctx = &exit_ctx->MemoryAccess;
+ int ret;
- address_space_rw(as, ma->GpaAddress, MEMTXATTRS_UNSPECIFIED,
- ma->Data, ma->AccessSize, ma->Direction);
- return S_OK;
-}
-
-static HRESULT CALLBACK whpx_emu_getreg_callback(
- void *ctx,
- const WHV_REGISTER_NAME *RegisterNames,
- UINT32 RegisterCount,
- WHV_REGISTER_VALUE *RegisterValues)
-{
- HRESULT hr;
- struct whpx_state *whpx = &whpx_global;
- CPUState *cpu = (CPUState *)ctx;
-
- hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
- whpx->partition, cpu->cpu_index,
- RegisterNames, RegisterCount,
- RegisterValues);
- if (FAILED(hr)) {
- error_report("WHPX: Failed to get virtual processor registers,"
- " hr=%08lx", hr);
- }
-
- return hr;
-}
-
-static HRESULT CALLBACK whpx_emu_setreg_callback(
- void *ctx,
- const WHV_REGISTER_NAME *RegisterNames,
- UINT32 RegisterCount,
- const WHV_REGISTER_VALUE *RegisterValues)
-{
- HRESULT hr;
- struct whpx_state *whpx = &whpx_global;
- CPUState *cpu = (CPUState *)ctx;
-
- hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
- whpx->partition, cpu->cpu_index,
- RegisterNames, RegisterCount,
- RegisterValues);
- if (FAILED(hr)) {
- error_report("WHPX: Failed to set virtual processor registers,"
- " hr=%08lx", hr);
- }
-
- /*
- * The emulator just successfully wrote the register state. We clear the
- * dirty state so we avoid the double write on resume of the VP.
- */
- cpu->vcpu_dirty = false;
-
- return hr;
-}
-
-static HRESULT CALLBACK whpx_emu_translate_callback(
- void *ctx,
- WHV_GUEST_VIRTUAL_ADDRESS Gva,
- WHV_TRANSLATE_GVA_FLAGS TranslateFlags,
- WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult,
- WHV_GUEST_PHYSICAL_ADDRESS *Gpa)
-{
- HRESULT hr;
- struct whpx_state *whpx = &whpx_global;
- CPUState *cpu = (CPUState *)ctx;
- WHV_TRANSLATE_GVA_RESULT res;
-
- hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index,
- Gva, TranslateFlags, &res, Gpa);
- if (FAILED(hr)) {
- error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
- } else {
- *TranslationResult = res.ResultCode;
- }
-
- return hr;
-}
-
-static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
- .Size = sizeof(WHV_EMULATOR_CALLBACKS),
- .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
- .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
- .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
- .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
- .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
-};
-
-static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
-{
- HRESULT hr;
- AccelCPUState *vcpu = cpu->accel;
- WHV_EMULATOR_STATUS emu_status;
-
- hr = whp_dispatch.WHvEmulatorTryMmioEmulation(
- vcpu->emulator, cpu,
- &vcpu->exit_ctx.VpContext, ctx,
- &emu_status);
- if (FAILED(hr)) {
- error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
- return -1;
- }
-
- if (!emu_status.EmulationSuccessful) {
- error_report("WHPX: Failed to emulate MMIO access with"
- " EmulatorReturnStatus: %u", emu_status.AsUINT32);
+ ret = emulate_instruction(cpu, ctx->InstructionBytes, ctx->InstructionByteCount);
+ if (ret < 0) {
+ error_report("failed to emulate mmio");
return -1;
}
return 0;
}
+static void handle_io(CPUState *env, uint16_t port, void *buffer,
+ int direction, int size, int count)
+{
+ int i;
+ uint8_t *ptr = buffer;
+
+ for (i = 0; i < count; i++) {
+ address_space_rw(&address_space_io, port, MEMTXATTRS_UNSPECIFIED,
+ ptr, size,
+ direction);
+ ptr += size;
+ }
+}
+
+static void whpx_bump_rip(CPUState *cpu, WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
+{
+ WHV_REGISTER_VALUE reg;
+ whpx_get_reg(cpu, WHvX64RegisterRip, ®);
+ reg.Reg64 = exit_ctx->VpContext.Rip + exit_ctx->VpContext.InstructionLength;
+ whpx_set_reg(cpu, WHvX64RegisterRip, reg);
+}
+
static int whpx_handle_portio(CPUState *cpu,
- WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
+ WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
{
- HRESULT hr;
- AccelCPUState *vcpu = cpu->accel;
- WHV_EMULATOR_STATUS emu_status;
+ WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx = &exit_ctx->IoPortAccess;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ int ret;
- hr = whp_dispatch.WHvEmulatorTryIoEmulation(
- vcpu->emulator, cpu,
- &vcpu->exit_ctx.VpContext, ctx,
- &emu_status);
- if (FAILED(hr)) {
- error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
- return -1;
+ if (!ctx->AccessInfo.StringOp && !ctx->AccessInfo.IsWrite) {
+ uint64_t val = 0;
+ WHV_REGISTER_VALUE reg;
+
+ whpx_get_reg(cpu, WHvX64RegisterRax, ®);
+ handle_io(cpu, ctx->PortNumber, &val, 0, ctx->AccessInfo.AccessSize, 1);
+ if (ctx->AccessInfo.AccessSize == 1) {
+ reg.Reg8 = val;
+ } else if (ctx->AccessInfo.AccessSize == 2) {
+ reg.Reg16 = val;
+ } else if (ctx->AccessInfo.AccessSize == 4) {
+ reg.Reg64 = (uint32_t)val;
+ } else {
+ reg.Reg64 = (uint64_t)val;
+ }
+ whpx_bump_rip(cpu, exit_ctx);
+ whpx_set_reg(cpu, WHvX64RegisterRax, reg);
+ return 0;
+ } else if (!ctx->AccessInfo.StringOp && ctx->AccessInfo.IsWrite) {
+ RAX(env) = ctx->Rax;
+ handle_io(cpu, ctx->PortNumber, &RAX(env), 1, ctx->AccessInfo.AccessSize, 1);
+ whpx_bump_rip(cpu, exit_ctx);
+ return 0;
}
- if (!emu_status.EmulationSuccessful) {
- error_report("WHPX: Failed to emulate PortIO access with"
- " EmulatorReturnStatus: %u", emu_status.AsUINT32);
+ ret = emulate_instruction(cpu, ctx->InstructionBytes, exit_ctx->VpContext.InstructionLength);
+ if (ret < 0) {
+ error_report("failed to emulate I/O port access");
return -1;
}
return 0;
}
+static void write_mem(CPUState *cpu, void *data, target_ulong addr, int bytes)
+{
+ vmx_write_mem(cpu, addr, data, bytes);
+}
+
+static void read_mem(CPUState *cpu, void *data, target_ulong addr, int bytes)
+{
+ vmx_read_mem(cpu, data, addr, bytes);
+}
+
+static void read_segment_descriptor(CPUState *cpu,
+ struct x86_segment_descriptor *desc,
+ enum X86Seg seg_idx)
+{
+ bool ret;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ SegmentCache *seg = &env->segs[seg_idx];
+ x86_segment_selector sel = { .sel = seg->selector & 0xFFFF };
+
+ ret = x86_read_segment_descriptor(cpu, desc, sel);
+ if (ret == false) {
+ error_report("failed to read segment descriptor");
+ abort();
+ }
+}
+
+
+static const struct x86_emul_ops whpx_x86_emul_ops = {
+ .read_mem = read_mem,
+ .write_mem = write_mem,
+ .read_segment_descriptor = read_segment_descriptor,
+ .handle_io = handle_io
+};
+
+static void whpx_init_emu(void)
+{
+ init_decoder();
+ init_emu(&whpx_x86_emul_ops);
+}
+
/*
* Controls whether we should intercept various exceptions on the guest,
* namely breakpoint/single-step events.
@@ -1279,8 +1264,9 @@ bool whpx_arch_supports_guest_debug(void)
void whpx_arch_destroy_vcpu(CPUState *cpu)
{
- AccelCPUState *vcpu = cpu->accel;
- whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ g_free(env->emu_mmio_buf);
}
/* Returns the address of the next instruction that is about to be executed. */
@@ -1639,11 +1625,11 @@ int whpx_vcpu_run(CPUState *cpu)
switch (vcpu->exit_ctx.ExitReason) {
case WHvRunVpExitReasonMemoryAccess:
- ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
+ ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx);
break;
case WHvRunVpExitReasonX64IoPortAccess:
- ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
+ ret = whpx_handle_portio(cpu, &vcpu->exit_ctx);
break;
case WHvRunVpExitReasonX64InterruptWindow:
@@ -1990,22 +1976,11 @@ int whpx_init_vcpu(CPUState *cpu)
vcpu = g_new0(AccelCPUState, 1);
- hr = whp_dispatch.WHvEmulatorCreateEmulator(
- &whpx_emu_callbacks,
- &vcpu->emulator);
- if (FAILED(hr)) {
- error_report("WHPX: Failed to setup instruction completion support,"
- " hr=%08lx", hr);
- ret = -EINVAL;
- goto error;
- }
-
hr = whp_dispatch.WHvCreateVirtualProcessor(
whpx->partition, cpu->cpu_index, 0);
if (FAILED(hr)) {
error_report("WHPX: Failed to create a virtual processor,"
" hr=%08lx", hr);
- whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
ret = -EINVAL;
goto error;
}
@@ -2067,6 +2042,8 @@ int whpx_init_vcpu(CPUState *cpu)
max_vcpu_index = max(max_vcpu_index, cpu->cpu_index);
qemu_add_vm_change_state_handler(whpx_cpu_update_state, env);
+ env->emu_mmio_buf = g_new(char, 4096);
+
return 0;
error:
@@ -2256,6 +2233,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
}
whpx_memory_init();
+ whpx_init_emu();
printf("Windows Hypervisor Platform accelerator is operational\n");
return 0;
--
2.53.0