target/arm: Implement FGWTE3 traps
Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260806172709.333300-5-richard.henderson@linaro.org [PMM: fixed comment indent] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Richard Henderson committed
Aug 6, 2026 at 10:27 UTC
56f329b54a5782781aecb97c16d27d4e3eb1a702
2 files changed
+42
-14
target/arm/tcg/op_helper.c
+27
-13
@@ -1061,13 +1061,15 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
1061
* Fine-grained traps also are lower priority than undef-to-EL1,
1062
* higher priority than trap-to-EL3, and we don't care about priority
1063
* order with other EL2 traps because the syndrome value is the same.
1064
+ *
1065
+ * FGWTE3 traps are exclusively traps to EL3 on registers that are
1066
+ * only accessible to EL3, so there's no possibility of a trap to EL2.
1067
+ * So we can handle these checks here too.
1068
*/
1065
- if (arm_fgt_active(env, arm_current_el(env))) {
1069
+ if (ri->fgt) {
1070
uint64_t trapword = 0;
1071
unsigned int idx = FIELD_EX32(ri->fgt, FGT, IDX);
1072
unsigned int bitpos = FIELD_EX32(ri->fgt, FGT, BITPOS);
1069
- bool rev = FIELD_EX32(ri->fgt, FGT, REV);
1070
- bool nxs = FIELD_EX32(ri->fgt, FGT, NXS);
1073
bool trapbit;
1074
1075
if (ri->fgt & FGT_EXEC) {
@@ -1080,19 +1082,31 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
1082
assert(idx < ARRAY_SIZE(env->cp15.fgt_write));
1083
trapword = env->cp15.fgt_write[idx];
1084
}
1085
+ trapbit = extract64(trapword, bitpos, 1);
1086
1084
- if (nxs && (arm_hcrx_el2_eff(env) & HCRX_FGTNXS)) {
1087
+ if ((ri->access & ~PL3_RW) == 0) {
1088
/*
1086
- * If HCRX_EL2.FGTnXS is 1 then the fine-grained trap for
1087
- * TLBI maintenance insns does *not* apply to the nXS variant.
1089
+ * EL3 cpreg -- must be FGWTE3, and FGWTE3_EL3 can only be
1090
+ * set from AArch64, and if the feature is enabled.
1091
*/
1089
- trapbit = 0;
1090
- } else {
1091
- trapbit = extract64(trapword, bitpos, 1);
1092
- }
1093
- if (trapbit != rev) {
1094
- res = CP_ACCESS_TRAP_EL2;
1095
- goto fail;
1092
+ if (trapbit) {
1093
+ res = CP_ACCESS_TRAP_EL3;
1094
+ goto fail;
1095
+ }
1096
+ } else if (arm_fgt_active(env, arm_current_el(env))) {
1097
+ bool nxs = FIELD_EX32(ri->fgt, FGT, NXS);
1098
+ bool rev = FIELD_EX32(ri->fgt, FGT, REV);
1099
+ if (nxs && (arm_hcrx_el2_eff(env) & HCRX_FGTNXS)) {
1100
+ /*
1101
+ * If HCRX_EL2.FGTnXS is 1 then the fine-grained trap for
1102
+ * TLBI maintenance insns does *not* apply to the nXS variant.
1103
+ */
1104
+ trapbit = 0;
1105
+ }
1106
+ if (trapbit != rev) {
1107
+ res = CP_ACCESS_TRAP_EL2;
1108
+ goto fail;
1109
+ }
1110
}
1111
}
1112
target/arm/tcg/translate-a64.c
+15
-1
@@ -2886,6 +2886,7 @@ static void handle_sys(DisasContext *s, bool isread,
2886
{
2887
uint32_t key = ENCODE_AA64_CP_REG(op0, op1, crn, crm, op2);
2888
const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key);
2889
+ bool need_helper = false;
2890
bool need_exit_tb = false;
2891
bool nv_trap_to_el2 = false;
2892
bool nv_redirect_reg = false;
@@ -2999,7 +3000,20 @@ static void handle_sys(DisasContext *s, bool isread,
3000
ri = redirect_cpreg(s, key, isread);
3001
}
3002
3002
- if (ri->accessfn || (ri->fgt && s->fgt_active)) {
3003
+ if (ri->accessfn) {
3004
+ need_helper = true;
3005
+ } else if (ri->fgt) {
3006
+ /*
3007
+ * EL3-only access means this must be an FGWTE3 trap (which are
3008
+ * always active); otherwise it's an FGT trap to EL2.
3009
+ */
3010
+ if ((ri->access & ~PL3_RW) == 0) {
3011
+ need_helper = dc_isar_feature(aa64_fgwte3, s);
3012
+ } else {
3013
+ need_helper = s->fgt_active;
3014
+ }
3015
+ }
3016
+ if (need_helper) {
3017
/* Emit code to perform further access permissions checks at
3018
* runtime; this may result in an exception.
3019
*/