From: "Anirudh Rayabharam (Microsoft)" <[email protected]>
Add the main vCPU run loop for MSHV using the MSHV_RUN_VP_IOCTL. Handle MMIO exits by emulating the instruction using the syndrome information from ESR_EL2. Signed-off-by: Anirudh Rayabharam (Microsoft) <[email protected]> --- include/hw/hyperv/hvgdk_mini.h | 44 +++++++++++++++ target/arm/mshv/mshv-all.c | 120 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h index 7dc9511692..192d464d22 100644 --- a/include/hw/hyperv/hvgdk_mini.h +++ b/include/hw/hyperv/hvgdk_mini.h @@ -759,6 +759,50 @@ struct hv_x64_memory_intercept_message { uint8_t instruction_bytes[16]; } QEMU_PACKED; +union hv_arm64_vp_execution_state { + uint16_t as_uint16; + struct { + uint16_t cpl:2; + uint16_t debug_active:1; + uint16_t interruption_pending:1; + uint16_t vtl:4; + uint16_t virtualization_fault_active:1; + uint16_t reserved:7; + }; +}; + +struct hv_arm64_intercept_message_header { + uint32_t vp_index; + uint8_t instruction_length; + uint8_t intercept_access_type; + union hv_arm64_vp_execution_state execution_state; + uint64_t pc; + uint64_t cpsr; +}; + +union hv_arm64_memory_access_info { + uint8_t as_uint8; + struct { + uint8_t gva_valid:1; + uint8_t gva_gpa_valid:1; + uint8_t hypercall_output_pending:1; + uint8_t reserved:5; + }; +}; + +struct hv_arm64_memory_intercept_message { + struct hv_arm64_intercept_message_header header; + uint32_t cache_type; /* enum hv_cache_type */ + uint8_t instruction_byte_count; + union hv_arm64_memory_access_info memory_access_info; + uint16_t reserved1; + uint8_t instruction_bytes[4]; + uint32_t reserved2; + uint64_t guest_virtual_address; + uint64_t guest_physical_address; + uint64_t syndrome; +}; + union hv_message_flags { uint8_t asu8; struct { diff --git a/target/arm/mshv/mshv-all.c b/target/arm/mshv/mshv-all.c index 96a04df38a..22653eb366 100644 --- a/target/arm/mshv/mshv-all.c +++ b/target/arm/mshv/mshv-all.c @@ -20,6 +20,7 @@ #include "target/arm/cpu.h" #include "target/arm/internals.h" #include "target/arm/mshv_arm.h" +#include "target/arm/helper.h" #include "system/mshv.h" #include "system/mshv_int.h" @@ -147,8 +148,127 @@ int mshv_arch_put_registers(const CPUState *cpu) return 0; } +static int set_memory_info(const struct hyperv_message *msg, + struct hv_arm64_memory_intercept_message *info) +{ + if (msg->header.message_type != HVMSG_GPA_INTERCEPT + && msg->header.message_type != HVMSG_UNMAPPED_GPA + && msg->header.message_type != HVMSG_UNACCEPTED_GPA) { + error_report("invalid message type"); + return -1; + } + memcpy(info, msg->payload, sizeof(*info)); + + return 0; +} + +int mshv_store_regs(CPUState *cpu) +{ + int ret; + + ret = set_standard_regs(cpu); + if (ret < 0) { + error_report("Failed to store standard registers"); + return -1; + } + + return 0; +} + +static uint64_t mshv_mmio_get_reg(CPUState *cpu, int reg_index) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + return reg_index < 31 ? env->xregs[reg_index] : 0ULL; +} + +static void mshv_mmio_set_reg(CPUState *cpu, int reg_index, uint64_t val) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + if (reg_index < 31) { + env->xregs[reg_index] = val; + } +} + +static int handle_unmapped_mem(int vm_fd, CPUState *cpu, + const struct hyperv_message *msg, + MshvVmExit *exit_reason) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + struct hv_arm64_memory_intercept_message info = { 0 }; + int ret; + EsrEl2 syndrome; + + ret = set_memory_info(msg, &info); + if (ret < 0) { + error_report("failed to convert message to memory info"); + return -1; + } + + syndrome.raw = info.syndrome; + + ret = mshv_load_regs(cpu); + if (ret < 0) { + error_report("Failed to load registers"); + return -1; + } + + static const struct arm_emul_ops mshv_arm_emul_ops = { + .get_reg = mshv_mmio_get_reg, + .set_reg = mshv_mmio_set_reg, + }; + + ret = arm_emulate_mmio(cpu, syndrome, info.guest_physical_address, + &mshv_arm_emul_ops); + if (ret < 0) { + error_report("Failed to emulate with syndrome"); + return -1; + } + + /* Advance PC past the faulting instruction */ + env->pc += (syndrome.il == 1) ? 4 : 2; + + ret = mshv_store_regs(cpu); + if (ret < 0) { + error_report("Failed to store registers"); + return -1; + } + + *exit_reason = MshvVmExitIgnore; + + return 0; +} + int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit) { + int ret; + int cpu_fd = mshv_vcpufd(cpu); + + ret = ioctl(cpu_fd, MSHV_RUN_VP, msg); + if (ret < 0) { + *exit = MshvVmExitShutdown; + return ret; + } + + switch (msg->header.message_type) { + case HVMSG_UNRECOVERABLE_EXCEPTION: + *exit = MshvVmExitShutdown; + break; + case HVMSG_GPA_INTERCEPT: + case HVMSG_UNMAPPED_GPA: + ret = handle_unmapped_mem(vm_fd, cpu, msg, exit); + if (ret < 0) { + error_report("failed to handle mmio"); + return -1; + } + break; + default: + error_report("Unhandled message type: 0x%x", msg->header.message_type); + return -1; + } + return 0; } -- 2.45.4
