@samitouri / QOSamiQemu / commits / 6f7a2e2d46

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 }