@@ -515,6 +515,73 @@ FIELD(HDFGWTR_EL2, NBRBCTL, 60, 1)
FIELD(HDFGWTR_EL2, NBRBDATA, 61, 1)
FIELD(HDFGWTR_EL2, NPMSNEVFR_EL1, 62, 1)
+/* Which fine-grained trap bit register to check, if any */
+FIELD(FGT, TYPE, 10, 3)
+FIELD(FGT, REV, 9, 1) /* Is bit sense reversed? */
+FIELD(FGT, IDX, 6, 3) /* Index within a uint64_t[] array */
+FIELD(FGT, BITPOS, 0, 6) /* Bit position within the uint64_t */
+
+/*
+ * Macros to define FGT_##bitname enum constants to use in ARMCPRegInfo::fgt
+ * fields. We assume for brevity's sake that there are no duplicated
+ * bit names across the various FGT registers.
+ */
+#define DO_BIT(REG, BITNAME) \
+ FGT_##BITNAME = FGT_##REG | R_##REG##_EL2_##BITNAME##_SHIFT
+
+/* Some bits have reversed sense, so 0 means trap and 1 means not */
+#define DO_REV_BIT(REG, BITNAME) \
+ FGT_##BITNAME = FGT_##REG | FGT_REV | R_##REG##_EL2_##BITNAME##_SHIFT
+
+typedef enum FGTBit {
+ /*
+ * These bits tell us which register arrays to use:
+ * if FGT_R is set then reads are checked against fgt_read[];
+ * if FGT_W is set then writes are checked against fgt_write[];
+ * if FGT_EXEC is set then all accesses are checked against fgt_exec[].
+ *
+ * For almost all bits in the R/W register pairs, the bit exists in
+ * both registers for a RW register, in HFGRTR/HDFGRTR for a RO register
+ * with the corresponding HFGWTR/HDFGTWTR bit being RES0, and vice-versa
+ * for a WO register. There are unfortunately a couple of exceptions
+ * (PMCR_EL0, TRFCR_EL1) where the register being trapped is RW but
+ * the FGT system only allows trapping of writes, not reads.
+ *
+ * Note that we arrange these bits so that a 0 FGTBit means "no trap".
+ */
+ FGT_R = 1 << R_FGT_TYPE_SHIFT,
+ FGT_W = 2 << R_FGT_TYPE_SHIFT,
+ FGT_EXEC = 4 << R_FGT_TYPE_SHIFT,
+ FGT_RW = FGT_R | FGT_W,
+ /* Bit to identify whether trap bit is reversed sense */
+ FGT_REV = R_FGT_REV_MASK,
+
+ /*
+ * If a bit exists in HFGRTR/HDFGRTR then either the register being
+ * trapped is RO or the bit also exists in HFGWTR/HDFGWTR, so we either
+ * want to trap for both reads and writes or else it's harmless to mark
+ * it as trap-on-writes.
+ * If a bit exists only in HFGWTR/HDFGWTR then either the register being
+ * trapped is WO, or else it is one of the two oddball special cases
+ * which are RW but have only a write trap. We mark these as only
+ * FGT_W so we get the right behaviour for those special cases.
+ * (If a bit was added in future that provided only a read trap for an
+ * RW register we'd need to do something special to get the FGT_R bit
+ * only. But this seems unlikely to happen.)
+ *
+ * So for the DO_BIT/DO_REV_BIT macros: use FGT_HFGRTR/FGT_HDFGRTR if
+ * the bit exists in that register. Otherwise use FGT_HFGWTR/FGT_HDFGWTR.
+ */
+ FGT_HFGRTR = FGT_RW | (FGTREG_HFGRTR << R_FGT_IDX_SHIFT),
+ FGT_HFGWTR = FGT_W | (FGTREG_HFGWTR << R_FGT_IDX_SHIFT),
+ FGT_HDFGRTR = FGT_RW | (FGTREG_HDFGRTR << R_FGT_IDX_SHIFT),
+ FGT_HDFGWTR = FGT_W | (FGTREG_HDFGWTR << R_FGT_IDX_SHIFT),
+ FGT_HFGITR = FGT_EXEC | (FGTREG_HFGITR << R_FGT_IDX_SHIFT),
+} FGTBit;
+
+#undef DO_BIT
+#undef DO_REV_BIT
+
typedef struct ARMCPRegInfo ARMCPRegInfo;
/*
@@ -569,6 +636,11 @@ struct ARMCPRegInfo {
CPAccessRights access;
/* Security state: ARM_CP_SECSTATE_* bits/values */
CPSecureState secure;
+ /*
+ * Which fine-grained trap register bit to check, if any. This
+ * value encodes both the trap register and bit within it.
+ */
+ FGTBit fgt;
/*
* The opaque pointer passed to define_arm_cp_regs_with_opaque() when
* this register was defined: can be used to hand data through to the
@@ -3170,6 +3170,7 @@ FIELD(TBFLAG_ANY, FPEXC_EL, 8, 2)
/* Memory operations require alignment: SCTLR_ELx.A or CCR.UNALIGN_TRP */
FIELD(TBFLAG_ANY, ALIGN_MEM, 10, 1)
FIELD(TBFLAG_ANY, PSTATE__IL, 11, 1)
+FIELD(TBFLAG_ANY, FGT_ACTIVE, 12, 1)
/*
* Bit usage when in AArch32 state, both A- and M-profile.
@@ -1377,4 +1377,24 @@ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env)
((1 << (1 - 1)) | (1 << (2 - 1)) | \
(1 << (4 - 1)) | (1 << (8 - 1)) | (1 << (16 - 1)))
+/*
+ * Return true if it is possible to take a fine-grained-trap to EL2.
+ */
+static inline bool arm_fgt_active(CPUARMState *env, int el)
+{
+ /*
+ * The Arm ARM only requires the "{E2H,TGE} != {1,1}" test for traps
+ * that can affect EL0, but it is harmless to do the test also for
+ * traps on registers that are only accessible at EL1 because if the test
+ * returns true then we can't be executing at EL1 anyway.
+ * FGT traps only happen when EL2 is enabled and EL1 is AArch64;
+ * traps from AArch32 only happen for the EL0 is AArch32 case.
+ */
+ return cpu_isar_feature(aa64_fgt, env_archcpu(env)) &&
+ el < 2 && arm_is_el2_enabled(env) &&
+ arm_el_is_aa64(env, 1) &&
+ (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE) &&
+ (!arm_feature(env, ARM_FEATURE_EL3) || (env->cp15.scr_el3 & SCR_FGTEN));
+}
+
#endif
@@ -130,6 +130,8 @@ typedef struct DisasContext {
bool is_nonstreaming;
/* True if MVE insns are definitely not predicated by VPR or LTPSIZE */
bool mve_no_pred;
+ /* True if fine-grained traps are active */
+ bool fgt_active;
/*
* >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI.
* < 0, set by the current instruction.
@@ -11689,6 +11689,7 @@ static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el,
if (arm_singlestep_active(env)) {
DP_TBFLAG_ANY(flags, SS_ACTIVE, 1);
}
+
return flags;
}
@@ -11761,6 +11762,10 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el,
DP_TBFLAG_A32(flags, HSTR_ACTIVE, 1);
}
+ if (arm_fgt_active(env, el)) {
+ DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1);
+ }
+
if (env->uncached_cpsr & CPSR_IL) {
DP_TBFLAG_ANY(flags, PSTATE__IL, 1);
}
@@ -11895,6 +11900,10 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
DP_TBFLAG_ANY(flags, PSTATE__IL, 1);
}
+ if (arm_fgt_active(env, el)) {
+ DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1);
+ }
+
if (cpu_isar_feature(aa64_mte, env_archcpu(env))) {
/*
* Set MTE_ACTIVE if any access may be Checked, and leave clear
@@ -680,6 +680,36 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
}
}
+ /*
+ * Fine-grained traps also are lower priority than undef-to-EL1,
+ * higher priority than trap-to-EL3, and we don't care about priority
+ * order with other EL2 traps because the syndrome value is the same.
+ */
+ if (arm_fgt_active(env, arm_current_el(env))) {
+ uint64_t trapword = 0;
+ unsigned int idx = FIELD_EX32(ri->fgt, FGT, IDX);
+ unsigned int bitpos = FIELD_EX32(ri->fgt, FGT, BITPOS);
+ bool rev = FIELD_EX32(ri->fgt, FGT, REV);
+ bool trapbit;
+
+ if (ri->fgt & FGT_EXEC) {
+ assert(idx < ARRAY_SIZE(env->cp15.fgt_exec));
+ trapword = env->cp15.fgt_exec[idx];
+ } else if (isread && (ri->fgt & FGT_R)) {
+ assert(idx < ARRAY_SIZE(env->cp15.fgt_read));
+ trapword = env->cp15.fgt_read[idx];
+ } else if (!isread && (ri->fgt & FGT_W)) {
+ assert(idx < ARRAY_SIZE(env->cp15.fgt_write));
+ trapword = env->cp15.fgt_write[idx];
+ }
+
+ trapbit = extract64(trapword, bitpos, 1);
+ if (trapbit != rev) {
+ res = CP_ACCESS_TRAP_EL2;
+ goto fail;
+ }
+ }
+
if (likely(res == CP_ACCESS_OK)) {
return ri;
}
@@ -1962,7 +1962,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
return;
}
- if (ri->accessfn) {
+ if (ri->accessfn || (ri->fgt && s->fgt_active)) {
/* Emit code to perform further access permissions checks at
* runtime; this may result in an exception.
*/
@@ -14741,6 +14741,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL);
dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM);
dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL);
+ dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE);
dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL);
dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL);
dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16;
@@ -4815,6 +4815,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64,
}
if ((s->hstr_active && s->current_el == 0) || ri->accessfn ||
+ (ri->fgt && s->fgt_active) ||
(arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) {
/*
* Emit code to perform further access permissions checks at
@@ -9415,6 +9416,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL);
dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM);
dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL);
+ dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE);
if (arm_feature(env, ARM_FEATURE_M)) {
dc->vfp_enabled = 1;