Connect the ISV=0 emulation library to the HVF and WHPX backends. Each implements arm_emul_ops callbacks over CPUARMState and cpu_memory_rw_debug(). Replaces the assert(isv) with instruction fetch, decode, and emulation via arm_emul_insn().
Signed-off-by: Lucas Amaral <[email protected]> --- target/arm/hvf/hvf.c | 94 ++++++++++++++++++++++++++++++++++++-- target/arm/whpx/whpx-all.c | 86 +++++++++++++++++++++++++++++++++- 2 files changed, 176 insertions(+), 4 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index d79469c..2a57b97 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -30,6 +30,7 @@ #include "qemu/main-loop.h" #include "system/cpus.h" #include "arm-powerctl.h" +#include "emulate/arm_emulate.h" #include "target/arm/cpu.h" #include "target/arm/internals.h" #include "target/arm/multiprocessing.h" @@ -797,6 +798,59 @@ static uint64_t hvf_get_reg(CPUState *cpu, int rt) return val; } +/* + * arm_emul_ops callbacks for HVF + * + * State must already be synchronized (cpu_synchronize_state) before + * calling arm_emul_insn(). Reads/writes env->xregs[] directly to + * correctly handle register 31 as SP and avoid redundant HVF API calls. + */ + +static uint64_t hvf_emul_read_gpr(CPUState *cpu, int reg) +{ + return ARM_CPU(cpu)->env.xregs[reg]; +} + +static void hvf_emul_write_gpr(CPUState *cpu, int reg, uint64_t val) +{ + ARM_CPU(cpu)->env.xregs[reg] = val; + cpu->vcpu_dirty = true; +} + +static void hvf_emul_read_fpreg(CPUState *cpu, int reg, void *buf, int size) +{ + memcpy(buf, &ARM_CPU(cpu)->env.vfp.zregs[reg], size); +} + +static void hvf_emul_write_fpreg(CPUState *cpu, int reg, + const void *buf, int size) +{ + CPUARMState *env = &ARM_CPU(cpu)->env; + memset(&env->vfp.zregs[reg], 0, sizeof(env->vfp.zregs[reg])); + memcpy(&env->vfp.zregs[reg], buf, size); + cpu->vcpu_dirty = true; +} + +static int hvf_emul_read_mem(CPUState *cpu, uint64_t va, void *buf, int size) +{ + return cpu_memory_rw_debug(cpu, va, buf, size, false); +} + +static int hvf_emul_write_mem(CPUState *cpu, uint64_t va, + const void *buf, int size) +{ + return cpu_memory_rw_debug(cpu, va, (void *)buf, size, true); +} + +static const struct arm_emul_ops hvf_arm_emul_ops = { + .read_gpr = hvf_emul_read_gpr, + .write_gpr = hvf_emul_write_gpr, + .read_fpreg = hvf_emul_read_fpreg, + .write_fpreg = hvf_emul_write_fpreg, + .read_mem = hvf_emul_read_mem, + .write_mem = hvf_emul_write_mem, +}; + static void clamp_id_aa64mmfr0_parange_to_ipa_size(ARMISARegisters *isar) { uint32_t ipa_size = chosen_ipa_bit_size ? @@ -1871,10 +1925,44 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp) assert(!s1ptw); /* - * TODO: ISV will be 0 for SIMD or SVE accesses. - * Inject the exception into the guest. + * ISV=0: syndrome doesn't carry access size/register info. + * Fetch and emulate via target/arm/emulate/. + * Unhandled instructions log an error and advance PC. */ - assert(isv); + if (!isv) { + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + uint32_t insn; + ArmEmulResult r; + + cpu_synchronize_state(cpu); + + if (cpu_memory_rw_debug(cpu, env->pc, + (uint8_t *)&insn, 4, false) != 0) { + error_report("HVF: cannot read insn at pc=0x%" PRIx64, + (uint64_t)env->pc); + advance_pc = true; + break; + } + + r = arm_emul_insn(cpu, &hvf_arm_emul_ops, insn); + if (r == ARM_EMUL_UNHANDLED) { + /* + * TODO: Inject data abort into guest instead of + * advancing PC. Requires setting ESR_EL1/FAR_EL1/ + * ELR_EL1/SPSR_EL1 and redirecting to VBAR_EL1. + */ + error_report("HVF: ISV=0 unhandled insn 0x%08x at " + "pc=0x%" PRIx64, insn, (uint64_t)env->pc); + } else if (r == ARM_EMUL_ERR_MEM) { + error_report("HVF: ISV=0 memory error emulating " + "insn 0x%08x at pc=0x%" PRIx64, + insn, (uint64_t)env->pc); + } + + advance_pc = true; + break; + } /* * Emulate MMIO. diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c index 40ada2d..c57abef 100644 --- a/target/arm/whpx/whpx-all.c +++ b/target/arm/whpx/whpx-all.c @@ -37,6 +37,7 @@ #include "whpx_arm.h" #include "hw/arm/bsa.h" #include "arm-powerctl.h" +#include "emulate/arm_emulate.h" #include <winhvplatform.h> #include <winhvplatformdefs.h> @@ -377,6 +378,53 @@ static void whpx_set_gp_reg(CPUState *cpu, int rt, uint64_t val) whpx_set_reg(cpu, reg, reg_val); } +/* arm_emul_ops callbacks for WHPX */ + +static uint64_t whpx_emul_read_gpr(CPUState *cpu, int reg) +{ + return ARM_CPU(cpu)->env.xregs[reg]; +} + +static void whpx_emul_write_gpr(CPUState *cpu, int reg, uint64_t val) +{ + ARM_CPU(cpu)->env.xregs[reg] = val; + cpu->vcpu_dirty = true; +} + +static void whpx_emul_read_fpreg(CPUState *cpu, int reg, void *buf, int size) +{ + memcpy(buf, &ARM_CPU(cpu)->env.vfp.zregs[reg], size); +} + +static void whpx_emul_write_fpreg(CPUState *cpu, int reg, + const void *buf, int size) +{ + CPUARMState *env = &ARM_CPU(cpu)->env; + memset(&env->vfp.zregs[reg], 0, sizeof(env->vfp.zregs[reg])); + memcpy(&env->vfp.zregs[reg], buf, size); + cpu->vcpu_dirty = true; +} + +static int whpx_emul_read_mem(CPUState *cpu, uint64_t va, void *buf, int size) +{ + return cpu_memory_rw_debug(cpu, va, buf, size, false); +} + +static int whpx_emul_write_mem(CPUState *cpu, uint64_t va, + const void *buf, int size) +{ + return cpu_memory_rw_debug(cpu, va, (void *)buf, size, true); +} + +static const struct arm_emul_ops whpx_arm_emul_ops = { + .read_gpr = whpx_emul_read_gpr, + .write_gpr = whpx_emul_write_gpr, + .read_fpreg = whpx_emul_read_fpreg, + .write_fpreg = whpx_emul_write_fpreg, + .read_mem = whpx_emul_read_mem, + .write_mem = whpx_emul_write_mem, +}; + static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) { uint64_t syndrome = ctx->Syndrome; @@ -391,7 +439,43 @@ static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) uint64_t val = 0; assert(!cm); - assert(isv); + + /* + * ISV=0: syndrome doesn't carry access size/register info. + * Fetch and decode the faulting instruction via the emulation library. + */ + if (!isv) { + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + uint32_t insn; + ArmEmulResult r; + + cpu_synchronize_state(cpu); + + if (cpu_memory_rw_debug(cpu, env->pc, + (uint8_t *)&insn, 4, false) != 0) { + error_report("WHPX: cannot read insn at pc=0x%" PRIx64, + (uint64_t)env->pc); + return 0; + } + + r = arm_emul_insn(cpu, &whpx_arm_emul_ops, insn); + if (r == ARM_EMUL_UNHANDLED) { + /* + * TODO: Inject data abort into guest instead of + * advancing PC. Requires setting ESR_EL1/FAR_EL1/ + * ELR_EL1/SPSR_EL1 and redirecting to VBAR_EL1. + */ + error_report("WHPX: ISV=0 unhandled insn 0x%08x at " + "pc=0x%" PRIx64, insn, (uint64_t)env->pc); + } else if (r == ARM_EMUL_ERR_MEM) { + error_report("WHPX: ISV=0 memory error emulating " + "insn 0x%08x at pc=0x%" PRIx64, + insn, (uint64_t)env->pc); + } + + return 0; + } if (iswrite) { val = whpx_get_gp_reg(cpu, srt); -- 2.52.0
