Implement the ICH_APR_EL2 register.  This is part of the hypervisor
control register interface, and is the hypervisor-facing access to
the state which holds the guest's its active priority register.

The only slightly non-straightforward part of this is that because
the GICv5 allows a hypervisor to give its guest a GICv3-compatible
virtual CPU interface, this register has to UNDEF to the inner guest
when legacy-vcpu operation is enabled.  We don't implement
GCIE_LEGACY in QEMU yet, but since we expect to do so in future, the
handling for this is implemented here.

Signed-off-by: Peter Maydell <[email protected]>
---
 target/arm/cpu.h             |  2 ++
 target/arm/tcg/gicv5-cpuif.c | 44 ++++++++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 82c13186b68..a93fd362417 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -594,6 +594,8 @@ typedef struct CPUArchState {
 
         /* Cached highest-priority pending PPI for each domain */
         GICv5PendingIrq ppi_hppi[NUM_GICV5_DOMAINS];
+
+        uint64_t ich_apr_el2;
     } gicv5_cpuif;
 
     struct {
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index 0b1033c1ef0..83c9be2b0e6 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -90,6 +90,15 @@ static GICv5Common *gicv5_get_gic(CPUARMState *env)
     return env->gicv5.gicv5state;
 }
 
+/*
+ * Return true if GIC virtual interface legacy operation is enabled.
+ * We don't currently implement FEAT_GCIE_LEGACY.
+ */
+static bool gicv5_legacy_enabled(CPUARMState *env)
+{
+    return false;
+}
+
 static GICv5Domain gicv5_logical_domain(CPUARMState *env)
 {
     /*
@@ -696,6 +705,34 @@ static void gic_cddi_write(CPUARMState *env, const 
ARMCPRegInfo *ri,
     }
 }
 
+static CPAccessResult access_gicv5_ich_reg(CPUARMState *env,
+                                           const ARMCPRegInfo *ri,
+                                           bool isread)
+{
+    /*
+     * accessfn for GICv5 ICH regs. The NV behaviour here is a little
+     * different from usual because of the optional legacy GIC feature.
+     *
+     * At EL1, if GCIE_LEGACY is disabled or not implemented, these
+     * behave like normal EL2 registers, which is that they redirect
+     * to memory if NV2 is enabled and trap to EL2 if not.  If legacy
+     * operation is enabled, these registers must UNDEF to EL1
+     * (because from the inner guest's point of view it has a GICv3
+     * and this sysreg doesn't exist).
+     *
+     * The UNDEF at EL1 when NV==0 is handled already because of the
+     * .access = PL2_RW setting on the cpreg.
+     */
+    if (arm_current_el(env) == 1 && gicv5_legacy_enabled(env)) {
+        return CP_ACCESS_UNDEFINED;
+    }
+    /*
+     * continue to either redirect-to-memory, trap-to-EL2 or (if
+     * current EL is 2 or 3) access the register
+     */
+    return CP_ACCESS_OK;
+}
+
 static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
     /*
      * Barrier: wait until the effects of a cpuif system register
@@ -893,6 +930,13 @@ static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
         .writefn = gic_icc_pcr_el1_write,
         .resetfn = gic_icc_pcr_el1_reset,
     },
+    {   .name = "ICH_APR_EL2", .state = ARM_CP_STATE_AA64,
+        .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 4,
+        .access = PL2_RW, .type = ARM_CP_NO_RAW,
+        .nv2_redirect_offset = 0xb00,
+        .accessfn = access_gicv5_ich_reg,
+        .fieldoffset = offsetof(CPUARMState, gicv5_cpuif.ich_apr_el2),
+    },
 };
 
 void define_gicv5_cpuif_regs(ARMCPU *cpu)
-- 
2.43.0


Reply via email to