[PATCH v2 3/3] target/arm: wire ISV=0 emulation into HVF and WHPX

Lucas Amaral posted 3 patches 3 weeks, 4 days ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Alexander Graf <agraf@csgraf.de>, Pedro Barbuda <pbarbuda@microsoft.com>, Mohamed Mediouni <mohamed@unpredictable.fr>
There is a newer version of this series
[PATCH v2 3/3] target/arm: wire ISV=0 emulation into HVF and WHPX
Posted by Lucas Amaral 3 weeks, 4 days ago
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