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 <lucaaamaral@gmail.com>
---
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
© 2016 - 2026 Red Hat, Inc.