target/arm: emit tag check when MTX without TBI
Previously, the TBI bit was used to mediate whether tag checks happened. With MTE4, if the MTX bits are enabled, then tag checking happens even if TBI is disabled. See AccessIsTagChecked. Signed-off-by: Gabriel Brookman <brookmangabriel@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260529-feat-mte4-v7-5-ccbd3c14eb3c@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Gabriel Brookman committed
May 29, 2026 at 12:52 UTC
6f7a2e2d467b4dce923581098e1d48e756fc9cb2
8 files changed
+45
-20
target/arm/cpu-features.h
+5
@@ -1201,6 +1201,11 @@ static inline bool isar_feature_aa64_mte_store_only(const ARMISARegisters *id)
1201
return FIELD_EX64_IDREG(id, ID_AA64PFR2, MTESTOREONLY) == 1;
1202
}
1203
1204
+static inline bool isar_feature_aa64_mte_mtx(const ARMISARegisters *id)
1205
+{
1206
+ return FIELD_EX64_IDREG(id, ID_AA64PFR1, MTEX) != 0;
1207
+}
1208
+
1209
static inline bool isar_feature_aa64_sme(const ARMISARegisters *id)
1210
{
1211
return FIELD_EX64_IDREG(id, ID_AA64PFR1, SME) != 0;
target/arm/helper.c
+10
@@ -9661,6 +9661,16 @@ uint64_t arm_sctlr(CPUARMState *env, int el)
9661
return env->cp15.sctlr_el[el];
9662
}
9663
9664
+int aa64_va_parameter_mtx(uint64_t tcr, ARMMMUIdx mmu_idx)
9665
+{
9666
+ if (regime_has_2_ranges(mmu_idx)) {
9667
+ return extract64(tcr, 60, 2);
9668
+ } else {
9669
+ /* Replicate the single MTX bit so we always have 2 bits. */
9670
+ return extract64(tcr, 33, 1) * 3;
9671
+ }
9672
+}
9673
+
9674
int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx)
9675
{
9676
if (regime_has_2_ranges(mmu_idx)) {
target/arm/internals.h
+8
-5
@@ -1445,6 +1445,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
1445
ARMMMUIdx mmu_idx, bool data,
1446
bool el1_is_aa32);
1447
1448
+int aa64_va_parameter_mtx(uint64_t tcr, ARMMMUIdx mmu_idx);
1449
int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx);
1450
int aa64_va_parameter_tbid(uint64_t tcr, ARMMMUIdx mmu_idx);
1451
int aa64_va_parameter_tcma(uint64_t tcr, ARMMMUIdx mmu_idx);
@@ -1586,7 +1587,8 @@ FIELD(MTEDESC, TBI, 4, 2)
1587
FIELD(MTEDESC, TCMA, 6, 2)
1588
FIELD(MTEDESC, WRITE, 8, 1)
1589
FIELD(MTEDESC, ALIGN, 9, 3)
1589
-FIELD(MTEDESC, SIZEM1, 12, 32 - 12) /* size - 1 */
1590
+FIELD(MTEDESC, MTX, 12, 2)
1591
+FIELD(MTEDESC, SIZEM1, 14, 32 - 14) /* size - 1 */
1592
1593
bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr);
1594
uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra);
@@ -1656,10 +1658,11 @@ static inline uint64_t address_with_allocation_tag(uint64_t ptr, int rtag)
1658
return deposit64(ptr, 56, 4, rtag);
1659
}
1660
1659
-/* Return true if tbi bits mean that the access is checked. */
1660
-static inline bool tbi_check(uint32_t desc, int bit55)
1661
+/* Return true if tbi or mtx bits mean that the access is tag checked. */
1662
+static inline bool tbi_or_mtx_check(uint32_t desc, int bit55)
1663
{
1662
- return (desc >> (R_MTEDESC_TBI_SHIFT + bit55)) & 1;
1664
+ uint32_t mask = (1u << R_MTEDESC_TBI_SHIFT) | (1u << R_MTEDESC_MTX_SHIFT);
1665
+ return desc & (mask << bit55);
1666
}
1667
1668
/* Return true if tcma bits mean that the access is unchecked. */
@@ -1693,7 +1696,7 @@ static inline uint64_t useronly_maybe_clean_ptr(uint32_t desc, uint64_t ptr)
1696
{
1697
#ifdef CONFIG_USER_ONLY
1698
int64_t clean_ptr = sextract64(ptr, 0, 56);
1696
- if (tbi_check(desc, clean_ptr < 0)) {
1699
+ if (tbi_or_mtx_check(desc, clean_ptr < 0)) {
1700
ptr = clean_ptr;
1701
}
1702
#endif
target/arm/tcg/helper-a64.c
+4
-3
@@ -1054,7 +1054,7 @@ static int mops_sizereg(uint32_t syndrome)
1054
}
1055
1056
/*
1057
- * Return true if TCMA and TBI bits mean we need to do MTE checks.
1057
+ * Return true if the TCMA, TBI, and MTX bits mean we need to do MTE checks.
1058
* We only need to do this once per MOPS insn, not for every page.
1059
*/
1060
static bool mte_checks_needed(uint64_t ptr, uint32_t desc)
@@ -1062,12 +1062,13 @@ static bool mte_checks_needed(uint64_t ptr, uint32_t desc)
1062
int bit55 = extract64(ptr, 55, 1);
1063
1064
/*
1065
- * Note that tbi_check() returns true for "access checked" but
1065
+ * Note that tbi_or_mtx_check() return true for "access checked", but
1066
* tcma_check() returns true for "access unchecked".
1067
*/
1068
- if (!tbi_check(desc, bit55)) {
1068
+ if (!tbi_or_mtx_check(desc, bit55)) {
1069
return false;
1070
}
1071
+
1072
return !tcma_check(desc, bit55, allocation_tag_from_addr(ptr));
1073
}
1074
target/arm/tcg/hflags.c
+7
-4
@@ -283,13 +283,16 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
283
uint64_t tcr = regime_tcr(env, mmu_idx);
284
uint64_t hcr = arm_hcr_el2_eff(env);
285
uint64_t sctlr;
286
- int tbii, tbid;
286
+ int tbii, tbid, mtx;
287
288
DP_TBFLAG_ANY(flags, AARCH64_STATE, 1);
289
290
/* Get control bits for tagged addresses. */
291
tbid = aa64_va_parameter_tbi(tcr, mmu_idx);
292
tbii = tbid & ~aa64_va_parameter_tbid(tcr, mmu_idx);
293
+ mtx = cpu_isar_feature(aa64_mte_mtx, env_archcpu(env)) ?
294
+ aa64_va_parameter_mtx(tcr, mmu_idx) :
295
+ 0;
296
297
DP_TBFLAG_A64(flags, TBII, tbii);
298
DP_TBFLAG_A64(flags, TBID, tbid);
@@ -441,14 +444,14 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
444
/*
445
* Set MTE_ACTIVE if any access may be Checked, and leave clear
446
* if all accesses must be Unchecked:
444
- * 1) If no TBI, then there are no tags in the address to check,
447
+ * 1) If TBI and MTX are both unset, accesses are Unchecked.
448
* 2) If Tag Check Override, then all accesses are Unchecked,
449
* 3) If Tag Check Fail == 0, then Checked access have no effect,
450
* 4) If no Allocation Tag Access, then all accesses are Unchecked.
451
*/
452
if (allocation_tag_access_enabled(env, el, sctlr)) {
453
DP_TBFLAG_A64(flags, ATA, 1);
451
- if (tbid
454
+ if ((tbid || mtx)
455
&& !(env->pstate & PSTATE_TCO)
456
&& (sctlr & (el == 0 ? SCTLR_TCF0 : SCTLR_TCF))) {
457
DP_TBFLAG_A64(flags, MTE_ACTIVE, 1);
@@ -474,7 +477,7 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
477
}
478
/* And again for unprivileged accesses, if required. */
479
if (EX_TBFLAG_A64(flags, UNPRIV)
477
- && tbid
480
+ && (tbid || mtx)
481
&& !(env->pstate & PSTATE_TCO)
482
&& (sctlr & SCTLR_TCF0)
483
&& allocation_tag_access_enabled(env, 0, sctlr)) {
target/arm/tcg/mte_helper.c
+6
-3
@@ -823,8 +823,11 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr,
823
bit55 = extract64(ptr, 55, 1);
824
*fault = ptr;
825
826
- /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */
827
- if (unlikely(!tbi_check(desc, bit55))) {
826
+ /*
827
+ * If TBI and MTX are disabled, the access is unchecked, and ptr is not
828
+ * dirty.
829
+ */
830
+ if (unlikely(!tbi_or_mtx_check(desc, bit55))) {
831
return -1;
832
}
833
@@ -965,7 +968,7 @@ uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr)
968
bit55 = extract64(ptr, 55, 1);
969
970
/* If TBI is disabled, the access is unchecked, and ptr is not dirty. */
968
- if (unlikely(!tbi_check(desc, bit55))) {
971
+ if (unlikely(!tbi_or_mtx_check(desc, bit55))) {
972
return ptr;
973
}
974
target/arm/tcg/sme_helper.c
+2
-2
@@ -674,7 +674,7 @@ void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg,
674
int bit55 = extract64(addr, 55, 1);
675
676
/* Perform gross MTE suppression early. */
677
- if (!tbi_check(mtedesc, bit55) ||
677
+ if (!tbi_or_mtx_check(mtedesc, bit55) ||
678
tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) {
679
mtedesc = 0;
680
}
@@ -856,7 +856,7 @@ void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr,
856
int bit55 = extract64(addr, 55, 1);
857
858
/* Perform gross MTE suppression early. */
859
- if (!tbi_check(mtedesc, bit55) ||
859
+ if (!tbi_or_mtx_check(mtedesc, bit55) ||
860
tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) {
861
mtedesc = 0;
862
}
target/arm/tcg/sve_helper.c
+3
-3
@@ -6388,7 +6388,7 @@ void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr,
6388
int bit55 = extract64(addr, 55, 1);
6389
6390
/* Perform gross MTE suppression early. */
6391
- if (!tbi_check(mtedesc, bit55) ||
6391
+ if (!tbi_or_mtx_check(mtedesc, bit55) ||
6392
tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) {
6393
mtedesc = 0;
6394
}
@@ -6750,7 +6750,7 @@ void sve_ldnfff1_r_mte(CPUARMState *env, void *vg, target_ulong addr,
6750
int bit55 = extract64(addr, 55, 1);
6751
6752
/* Perform gross MTE suppression early. */
6753
- if (!tbi_check(mtedesc, bit55) ||
6753
+ if (!tbi_or_mtx_check(mtedesc, bit55) ||
6754
tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) {
6755
mtedesc = 0;
6756
}
@@ -7005,7 +7005,7 @@ void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr,
7005
int bit55 = extract64(addr, 55, 1);
7006
7007
/* Perform gross MTE suppression early. */
7008
- if (!tbi_check(mtedesc, bit55) ||
7008
+ if (!tbi_or_mtx_check(mtedesc, bit55) ||
7009
tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) {
7010
mtedesc = 0;
7011
}