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 <[email protected]> --- target/i386/whpx/whpx-all.c | 299 ++++++++++++++++++++++-------------- 1 file changed, 180 insertions(+), 119 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index cdcaebbe16..68ac9dd6ff 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,11 @@ #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 <winhvplatform.h> -#include <winhvemulation.h> #define HYPERV_APIC_BUS_FREQUENCY (200000000ULL) @@ -756,160 +760,224 @@ 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) { - 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 }; + + whpx_get_registers(cpu); + decode_instruction(env, &decode); + 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_MEMORY_ACCESS_CONTEXT *ctx) { - CPUState *cs = (CPUState *)ctx; - AddressSpace *as = cpu_addressspace(cs, MEMTXATTRS_UNSPECIFIED); + int ret; - address_space_rw(as, ma->GpaAddress, MEMTXATTRS_UNSPECIFIED, - ma->Data, ma->AccessSize, ma->Direction); - return S_OK; + ret = emulate_instruction(cpu); + if (ret < 0) { + error_report("failed to emulate mmio"); + return -1; + } + + return 0; } -static HRESULT CALLBACK whpx_emu_getreg_callback( - void *ctx, - const WHV_REGISTER_NAME *RegisterNames, - UINT32 RegisterCount, - WHV_REGISTER_VALUE *RegisterValues) +static void handle_io(CPUState *env, uint16_t port, void *buffer, + int direction, int size, int count) { - HRESULT hr; - struct whpx_state *whpx = &whpx_global; - CPUState *cpu = (CPUState *)ctx; + int i; + uint8_t *ptr = buffer; - 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); + for (i = 0; i < count; i++) { + address_space_rw(&address_space_io, port, MEMTXATTRS_UNSPECIFIED, + ptr, size, + direction); + ptr += size; } +} - return hr; +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.InstructionLength; + whpx_set_reg(cpu, WHvX64RegisterRip, reg); } -static HRESULT CALLBACK whpx_emu_setreg_callback( - void *ctx, - const WHV_REGISTER_NAME *RegisterNames, - UINT32 RegisterCount, - const WHV_REGISTER_VALUE *RegisterValues) +static int whpx_handle_portio(CPUState *cpu, + WHV_RUN_VP_EXIT_CONTEXT *exit_ctx) { - HRESULT hr; - struct whpx_state *whpx = &whpx_global; - CPUState *cpu = (CPUState *)ctx; + 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.WHvSetVirtualProcessorRegisters( - whpx->partition, cpu->cpu_index, - RegisterNames, RegisterCount, - RegisterValues); - if (FAILED(hr)) { - error_report("WHPX: Failed to set virtual processor registers," - " hr=%08lx", hr); + if (!ctx->AccessInfo.StringOp && !ctx->AccessInfo.IsWrite) { + uint64_t val = 0; + WHV_REGISTER_VALUE reg; + + whpx_get_registers(cpu); + 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; + } + + ret = emulate_instruction(cpu); + if (ret < 0) { + error_report("failed to emulate I/O port access"); + return -1; } - /* - * 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; + return 0; } -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) +static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa, + WHV_TRANSLATE_GVA_FLAGS flags) { HRESULT hr; struct whpx_state *whpx = &whpx_global; - CPUState *cpu = (CPUState *)ctx; - WHV_TRANSLATE_GVA_RESULT res; + WHV_TRANSLATE_GVA_RESULT translation_result; + + /* perform the call */ + hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index, gva, flags, &translation_result, gpa); - 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; + error_report("Failed to translate gva (0x%llx) to gpa", gva); + return -1; } - return hr; -} + if (translation_result.ResultCode != WHvTranslateGvaResultSuccess) { + error_report("Failed to translate gva (0x%llx) to gpa", gva); + return -1; + } -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, -}; + return 0; +} -static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) +static int guest_mem_read_with_gva(CPUState *cpu, uint64_t gva, + uint8_t *data, uintptr_t size, + bool fetch_instruction) { - HRESULT hr; - AccelCPUState *vcpu = cpu->accel; - WHV_EMULATOR_STATUS emu_status; + int ret; + uint64_t gpa; + AddressSpace *as = cpu_addressspace(cpu, MEMTXATTRS_UNSPECIFIED); - 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); + ret = translate_gva(cpu, gva, &gpa, WHvTranslateGvaFlagValidateRead); + if (ret < 0) { + error_report("failed to translate gva to gpa"); return -1; } - if (!emu_status.EmulationSuccessful) { - error_report("WHPX: Failed to emulate MMIO access with" - " EmulatorReturnStatus: %u", emu_status.AsUINT32); + ret = address_space_rw(as, gpa, MEMTXATTRS_UNSPECIFIED, data, size, false); + if (ret < 0) { + error_report("failed to read from guest memory"); return -1; } return 0; } -static int whpx_handle_portio(CPUState *cpu, - WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx) +static int guest_mem_write_with_gva(CPUState *cpu, uint64_t gva, + const uint8_t *data, uintptr_t size) { - HRESULT hr; - AccelCPUState *vcpu = cpu->accel; - WHV_EMULATOR_STATUS emu_status; + int ret; + uint64_t gpa; + AddressSpace *as = cpu_addressspace(cpu, MEMTXATTRS_UNSPECIFIED); - 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); + ret = translate_gva(cpu, gva, &gpa, WHvTranslateGvaFlagValidateWrite); + if (ret < 0) { + error_report("failed to translate gva to gpa"); return -1; } - if (!emu_status.EmulationSuccessful) { - error_report("WHPX: Failed to emulate PortIO access with" - " EmulatorReturnStatus: %u", emu_status.AsUINT32); + ret = address_space_rw(as, gpa, MEMTXATTRS_UNSPECIFIED, (void *)data, size, true); + if (ret < 0) { + error_report("failed to write to guest memory"); return -1; } return 0; } +static void write_mem(CPUState *cpu, void *data, target_ulong addr, int bytes) +{ + if (guest_mem_write_with_gva(cpu, addr, data, bytes) < 0) { + error_report("failed to write memory"); + abort(); + } +} + + +static void fetch_instruction(CPUState *cpu, void *data, + target_ulong addr, int bytes) +{ + if (guest_mem_read_with_gva(cpu, addr, data, bytes, true) < 0) { + error_report("failed to fetch instruction"); + abort(); + } +} + +static void read_mem(CPUState *cpu, void *data, target_ulong addr, int bytes) +{ + if (guest_mem_read_with_gva(cpu, addr, data, bytes, false) < 0) { + error_report("failed to read memory"); + abort(); + } +} + +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 = { + .fetch_instruction = fetch_instruction, + .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 +1347,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. */ @@ -1643,7 +1712,7 @@ int whpx_vcpu_run(CPUState *cpu) 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 +2059,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 +2125,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 +2316,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.50.1 (Apple Git-155)
