@@ -621,6 +621,7 @@ FIELD(HDFGWTR_EL2, NBRBCTL, 60, 1)
FIELD(HDFGWTR_EL2, NBRBDATA, 61, 1)
FIELD(HDFGWTR_EL2, NPMSNEVFR_EL1, 62, 1)
+FIELD(FGT, NXS, 13, 1) /* Honour HCR_EL2.FGTnXS to suppress FGT */
/* Which fine-grained trap bit register to check, if any */
FIELD(FGT, TYPE, 10, 3)
FIELD(FGT, REV, 9, 1) /* Is bit sense reversed? */
@@ -639,6 +640,17 @@ FIELD(FGT, BITPOS, 0, 6) /* Bit position within the uint64_t */
#define DO_REV_BIT(REG, BITNAME) \
FGT_##BITNAME = FGT_##REG | FGT_REV | R_##REG##_EL2_##BITNAME##_SHIFT
+/*
+ * The FGT bits for TLBI maintenance instructions accessible at EL1 always
+ * affect the "normal" TLBI insns; they affect the corresponding TLBI insns
+ * with the nXS qualifier only if HCRX_EL2.FGTnXS is 0. We define e.g.
+ * FGT_TLBIVAE1 to use for the normal insn, and FGT_TLBIVAE1NXS to use
+ * for the nXS qualified insn.
+ */
+#define DO_TLBINXS_BIT(REG, BITNAME) \
+ FGT_##BITNAME = FGT_##REG | R_##REG##_EL2_##BITNAME##_SHIFT, \
+ FGT_##BITNAME##NXS = FGT_##BITNAME | R_FGT_NXS_MASK
+
typedef enum FGTBit {
/*
* These bits tell us which register arrays to use:
@@ -772,36 +784,36 @@ typedef enum FGTBit {
DO_BIT(HFGITR, ATS1E0W),
DO_BIT(HFGITR, ATS1E1RP),
DO_BIT(HFGITR, ATS1E1WP),
- DO_BIT(HFGITR, TLBIVMALLE1OS),
- DO_BIT(HFGITR, TLBIVAE1OS),
- DO_BIT(HFGITR, TLBIASIDE1OS),
- DO_BIT(HFGITR, TLBIVAAE1OS),
- DO_BIT(HFGITR, TLBIVALE1OS),
- DO_BIT(HFGITR, TLBIVAALE1OS),
- DO_BIT(HFGITR, TLBIRVAE1OS),
- DO_BIT(HFGITR, TLBIRVAAE1OS),
- DO_BIT(HFGITR, TLBIRVALE1OS),
- DO_BIT(HFGITR, TLBIRVAALE1OS),
- DO_BIT(HFGITR, TLBIVMALLE1IS),
- DO_BIT(HFGITR, TLBIVAE1IS),
- DO_BIT(HFGITR, TLBIASIDE1IS),
- DO_BIT(HFGITR, TLBIVAAE1IS),
- DO_BIT(HFGITR, TLBIVALE1IS),
- DO_BIT(HFGITR, TLBIVAALE1IS),
- DO_BIT(HFGITR, TLBIRVAE1IS),
- DO_BIT(HFGITR, TLBIRVAAE1IS),
- DO_BIT(HFGITR, TLBIRVALE1IS),
- DO_BIT(HFGITR, TLBIRVAALE1IS),
- DO_BIT(HFGITR, TLBIRVAE1),
- DO_BIT(HFGITR, TLBIRVAAE1),
- DO_BIT(HFGITR, TLBIRVALE1),
- DO_BIT(HFGITR, TLBIRVAALE1),
- DO_BIT(HFGITR, TLBIVMALLE1),
- DO_BIT(HFGITR, TLBIVAE1),
- DO_BIT(HFGITR, TLBIASIDE1),
- DO_BIT(HFGITR, TLBIVAAE1),
- DO_BIT(HFGITR, TLBIVALE1),
- DO_BIT(HFGITR, TLBIVAALE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIASIDE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAAE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVALE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAALE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVALE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1OS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIASIDE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAAE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVALE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAALE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVALE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1IS),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVALE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIASIDE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAAE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIVALE1),
+ DO_TLBINXS_BIT(HFGITR, TLBIVAALE1),
DO_BIT(HFGITR, CFPRCTX),
DO_BIT(HFGITR, DVPRCTX),
DO_BIT(HFGITR, CPPRCTX),
@@ -474,6 +474,11 @@ static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id)
return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0;
}
+static inline bool isar_feature_aa64_xs(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, XS) != 0;
+}
+
/*
* These are the values from APA/API/APA3.
* In general these must be compared '>=', per the normal Arm ARM
@@ -5346,10 +5346,13 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri,
valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI;
}
/* FEAT_CMOW adds CMOW */
-
if (cpu_isar_feature(aa64_cmow, cpu)) {
valid_mask |= HCRX_CMOW;
}
+ /* FEAT_XS adds FGTnXS, FnXS */
+ if (cpu_isar_feature(aa64_xs, cpu)) {
+ valid_mask |= HCRX_FGTNXS | HCRX_FNXS;
+ }
/* Clear RES0 bits. */
env->cp15.hcrx_el2 = value & valid_mask;
@@ -817,6 +817,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
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 nxs = FIELD_EX32(ri->fgt, FGT, NXS);
bool trapbit;
if (ri->fgt & FGT_EXEC) {
@@ -830,7 +831,15 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
trapword = env->cp15.fgt_write[idx];
}
- trapbit = extract64(trapword, bitpos, 1);
+ if (nxs && (arm_hcrx_el2_eff(env) & HCRX_FGTNXS)) {
+ /*
+ * If HCRX_EL2.FGTnXS is 1 then the fine-grained trap for
+ * TLBI maintenance insns does *not* apply to the nXS variant.
+ */
+ trapbit = 0;
+ } else {
+ trapbit = extract64(trapword, bitpos, 1);
+ }
if (trapbit != rev) {
res = CP_ACCESS_TRAP_EL2;
goto fail;