target/arm: implement MTE_PERM
Introduces a new stage 2 memory attribute, NoTagAccess, that raises a stage 2 data abort on a tag check, tag read, or tag write. Signed-off-by: Gabriel Brookman <brookmangabriel@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260529-feat-mte4-v7-1-ccbd3c14eb3c@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Gabriel Brookman committed
May 29, 2026 at 12:52 UTC
907f99861beb7d209695d114352c2e894c520cda
3 files changed
+63
-5
target/arm/cpu-features.h
+5
@@ -1191,6 +1191,11 @@ static inline bool isar_feature_aa64_mte3(const ARMISARegisters *id)
1191
return FIELD_EX64_IDREG(id, ID_AA64PFR1, MTE) >= 3;
1192
}
1193
1194
+static inline bool isar_feature_aa64_mteperm(const ARMISARegisters *id)
1195
+{
1196
+ return FIELD_EX64_IDREG(id, ID_AA64PFR2, MTEPERM) >= 1;
1197
+}
1198
+
1199
static inline bool isar_feature_aa64_sme(const ARMISARegisters *id)
1200
{
1201
return FIELD_EX64_IDREG(id, ID_AA64PFR1, SME) != 0;
target/arm/ptw.c
+22
-3
@@ -3415,7 +3415,7 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr,
3415
ARMCacheAttrs s1, ARMCacheAttrs s2)
3416
{
3417
ARMCacheAttrs ret;
3418
- bool tagged = false;
3418
+ bool tagged = false, notagaccess = false;
3419
3420
assert(!s1.is_s2_format);
3421
ret.is_s2_format = false;
@@ -3425,6 +3425,18 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr,
3425
s1.attrs = 0xff;
3426
}
3427
3428
+ if (hcr & HCR_FWB) {
3429
+ if (s2.attrs >= 0xe) {
3430
+ notagaccess = true;
3431
+ s2.attrs = 0x7;
3432
+ }
3433
+ } else {
3434
+ if (s2.attrs == 0x4) {
3435
+ notagaccess = true;
3436
+ s2.attrs = 0xf;
3437
+ }
3438
+ }
3439
+
3440
/* Combine shareability attributes (table D4-43) */
3441
if (s1.shareability == 2 || s2.shareability == 2) {
3442
/* if either are outer-shareable, the result is outer-shareable */
@@ -3456,9 +3468,16 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr,
3468
ret.shareability = 2;
3469
}
3470
3459
- /* TODO: CombineS1S2Desc does not consider transient, only WB, RWA. */
3471
+ /*
3472
+ * The attr encoding 0xe0 corresponds to Tagged NoTagAccess and is only
3473
+ * valid with FEAT_MTE_PERM (otherwise RESERVED, constrained
3474
+ * unpredictable)). The presence of this feature is checked in
3475
+ * allocation_tag_mem_probe, where Tagged NoTagAccess has its effect. See
3476
+ * J1.3.5.2 EncodePARAttrs.
3477
+ * TODO: CombineS1S2Desc does not consider transient, only WB, RWA.
3478
+ */
3479
if (tagged && ret.attrs == 0xff) {
3461
- ret.attrs = 0xf0;
3480
+ ret.attrs = notagaccess ? 0xe0 : 0xf0;
3481
}
3482
3483
return ret;
target/arm/tcg/mte_helper.c
+36
-2
@@ -58,6 +58,27 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude)
58
return tag;
59
}
60
61
+#ifndef CONFIG_USER_ONLY
62
+/*
63
+ * Constructs S2 Permission Fault as described in ARM ARM "Stage 2 Memory
64
+ * Tagging Attributes".
65
+ */
66
+static void mte_perm_check_fail(CPUARMState *env, uint64_t dirty_ptr,
67
+ uintptr_t ra, bool is_write)
68
+{
69
+ uint64_t syn;
70
+
71
+ env->exception.vaddress = dirty_ptr;
72
+
73
+ syn = syn_data_abort_no_iss(0, 0, 0, 0, 0, is_write, 0);
74
+
75
+ syn |= BIT_ULL(41); /* TagAccess is bit 41 */
76
+
77
+ raise_exception_ra(env, EXCP_DATA_ABORT, syn, 2, ra);
78
+ g_assert_not_reached();
79
+}
80
+#endif
81
+
82
uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx,
83
uint64_t ptr, MMUAccessType ptr_access,
84
int ptr_size, MMUAccessType tag_access,
@@ -117,8 +138,21 @@ uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx,
138
}
139
assert(!(flags & TLB_INVALID_MASK));
140
120
- /* If the virtual page MemAttr != Tagged, access unchecked. */
121
- if (full->extra.arm.pte_attrs != 0xf0) {
141
+ switch (full->extra.arm.pte_attrs) {
142
+ case 0xf0: /* Tagged */
143
+ break;
144
+
145
+ case 0xe0: /* NoTagAccess */
146
+ if (cpu_isar_feature(aa64_mteperm, env_archcpu(env))) {
147
+ if (probe) {
148
+ return NULL;
149
+ }
150
+ assert(ra);
151
+ mte_perm_check_fail(env, ptr, ra, tag_access == MMU_DATA_STORE);
152
+ }
153
+ /* fall through */
154
+
155
+ default: /* Not Tagged */
156
return NULL;
157
}
158