On 11/17/25 02:40, Gabriel Brookman wrote:
This feature causes tag checks to compare logical address tags against
their canonical form rather than against allocation tags. Described in
the ARM ARM section "Logical Address Tagging".
Signed-off-by: Gabriel Brookman <[email protected]>
---
target/arm/internals.h | 40 ++++++++++++++++++++++++++++++++++++++++
target/arm/tcg/mte_helper.c | 12 ++++++++++++
2 files changed, 52 insertions(+)
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 75677945af..5f0bcdaaac 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -1633,6 +1633,46 @@ static inline bool tcma_check(uint32_t desc, int bit55,
int ptr_tag)
return tcma && match;
}
+/* Return whether or not the second nibble of a VA matches bit 55. */
+static inline bool tag_is_canonical(int ptr_tag, int bit55)
+{
+ return ((ptr_tag + bit55) & 0xf) == 0;
+}
+
+/* Return true if mtx bits mean that the access is canonically checked. */
+static inline bool mtx_check(CPUARMState *env, bool bit55)
+{
+ /*
+ * the MTX bits used in EL0 are those used in whichever EL is used
+ * for the supervisor. The EL that contains the supervisor uses
+ * bits 60 and 61 (MTX0 and MTX1), while the other ELs that aren't
+ * used by the supervisor.
+ */
+ int el = arm_current_el(env);
+ if (el == 0) {
+ if (HCR_E2H & env->cp15.hcr_el2) {
+ return (1l << (60 + bit55)) & env->cp15.tcr_el[2];
+ } else {
+ return (1l << (60 + bit55)) & env->cp15.tcr_el[1];
+ }
+ } else if (el == 1) {
+ if (HCR_E2H & env->cp15.hcr_el2) {
+ g_assert_not_reached();
+ } else {
+ return (1l << (60 + bit55)) & env->cp15.tcr_el[1];
+ }
+ } else if (el == 2) {
+ if (HCR_E2H & env->cp15.hcr_el2) {
+ return (1l << (60 + bit55)) & env->cp15.tcr_el[2];
+ } else {
+ return (1l << 33) & env->cp15.tcr_el[2];
+ }
+ } else if (el == 3) {
+ return (1l << 33) & env->cp15.tcr_el[3];
+ }
+ return false;
+}
Again, you can't touch hcr_el2 directly.
In this case, you want to use regime_tcr, using the mmu_idx value from
mte_probe_int.
Use arm_mmu_idx_to_el() instead of arm_current_el(), so that UNPRIV acceses are handled
correctly.
Using "l" is always wrong; always ll or preferably ull.
Though really defines for
bit55 ? TCR_MTX1 : TCR_MTX0
wouldn't go amiss.
You seam to be missing a check for FEAT_MTE_NO_ADDRESS_TAGS or
FEAT_MTE_CANONICAL_TAGS.
+
/*
* For TBI, ideally, we would do nothing. Proper behaviour on fault is
* for the tag to be present in the FAR_ELx register. But for user-only
diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c
index f9fd6fd408..513ee8d6a1 100644
--- a/target/arm/tcg/mte_helper.c
+++ b/target/arm/tcg/mte_helper.c
@@ -799,6 +799,10 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc,
uint64_t ptr,
return 1;
}
+ if (mtx_check(env, bit55)) {
+ return tag_is_canonical(ptr_tag, bit55);
+ }
This could use a comment like
If mtx is enabled, then the access is MemTag_CanonicallyTagged,
otherwise it is MemTag_AllocationTagged. See AArch64.CheckTag.
+ if (mtx_check(env, bit55)) {
+ if (tag_is_canonical(ptr_tag, bit55)) {
+ goto done;
+ }
+ mte_check_fail(env, desc, ptr, ra);
+ }
Likewise.
In the pseudocode, walkparams.mtx is used in AArch64.VAIsOutOfRange, which is part of
get_phys_addr_lpae() here:
/*
* We determined the region when collecting the parameters, but we
* have not yet validated that the address is valid for the region.
* Extract the top bits and verify that they all match select.
*
* For aa32, if inputsize == addrsize, then we have selected the
* region by exclusion in aa32_va_parameters and there is no more
* validation to do here.
*/
if (inputsize < addrsize) {
uint64_t top_bits = sextract64(address, inputsize,
addrsize - inputsize);
if (-top_bits != param.select) {
/* The gap between the two regions is a Translation fault */
goto do_translation_fault;
}
}
There is also a use in AArch64.S1DisabledOutput, which would correspond to
get_phys_addr_disabled(), which is relevant before
if (extract64(address, pamax, addrtop - pamax + 1) != 0) {
fi->type = ARMFault_AddressSize;
r~