@samitouri / QOSamiQemu / commits / 692a389737

target/arm: fault on tag store to canonical tag

According to ARM ARM, section "Memory region tagging types", tag-store instructions targeting canonically tagged regions cause a stage 1 permission fault with MTX enabled. Signed-off-by: Gabriel Brookman <brookmangabriel@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260529-feat-mte4-v7-10-ccbd3c14eb3c@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Gabriel Brookman committed May 29, 2026 at 12:52 UTC 692a389737f118416af42c30d5785fcd8dbba578
3 files changed +65 -28
target/arm/tcg/helper-a64-defs.h
+6 -6
@@ -103,15 +103,15 @@ DEF_HELPER_FLAGS_3(mte_check_zva, TCG_CALL_NO_WG, i64, env, i32, i64)
103 DEF_HELPER_FLAGS_3(irg, TCG_CALL_NO_RWG, i64, env, i64, i64)
104 DEF_HELPER_FLAGS_4(addsubg, TCG_CALL_NO_RWG_SE, i64, env, i64, s32, i32)
105 DEF_HELPER_FLAGS_4(ldg, TCG_CALL_NO_WG, i64, env, i64, i64, i32)
106 -DEF_HELPER_FLAGS_3(stg, TCG_CALL_NO_WG, void, env, i64, i64)
107 -DEF_HELPER_FLAGS_3(stg_parallel, TCG_CALL_NO_WG, void, env, i64, i64)
106 +DEF_HELPER_FLAGS_4(stg, TCG_CALL_NO_WG, void, env, i64, i64, i32)
107 +DEF_HELPER_FLAGS_4(stg_parallel, TCG_CALL_NO_WG, void, env, i64, i64, i32)
108 DEF_HELPER_FLAGS_2(stg_stub, TCG_CALL_NO_WG, void, env, i64)
109 -DEF_HELPER_FLAGS_3(st2g, TCG_CALL_NO_WG, void, env, i64, i64)
110 -DEF_HELPER_FLAGS_3(st2g_parallel, TCG_CALL_NO_WG, void, env, i64, i64)
109 +DEF_HELPER_FLAGS_4(st2g, TCG_CALL_NO_WG, void, env, i64, i64, i32)
110 +DEF_HELPER_FLAGS_4(st2g_parallel, TCG_CALL_NO_WG, void, env, i64, i64, i32)
111 DEF_HELPER_FLAGS_2(st2g_stub, TCG_CALL_NO_WG, void, env, i64)
112 DEF_HELPER_FLAGS_3(ldgm, TCG_CALL_NO_WG, i64, env, i64, i32)
113 -DEF_HELPER_FLAGS_3(stgm, TCG_CALL_NO_WG, void, env, i64, i64)
114 -DEF_HELPER_FLAGS_3(stzgm_tags, TCG_CALL_NO_WG, void, env, i64, i64)
113 +DEF_HELPER_FLAGS_4(stgm, TCG_CALL_NO_WG, void, env, i64, i64, i32)
114 +DEF_HELPER_FLAGS_4(stzgm_tags, TCG_CALL_NO_WG, void, env, i64, i64, i32)
115
116 DEF_HELPER_FLAGS_4(arm_unaligned_access, TCG_CALL_NO_WG,
117 noreturn, env, i64, i32, i32)
target/arm/tcg/mte_helper.c
+43 -12
@@ -231,6 +231,19 @@ uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx,
231 #endif
232 }
233
234 +static G_NORETURN void canonical_tag_write_fail(CPUARMState *env,
235 + uint64_t dirty_ptr, uintptr_t ra)
236 +{
237 + uint64_t syn;
238 +
239 + env->exception.vaddress = dirty_ptr;
240 +
241 + syn = syn_data_abort_no_iss(arm_current_el(env) != 0, 0, 0, 0, 0, 1, 0);
242 + syn |= BIT_ULL(42); /* TnD is bit 42 */
243 +
244 + raise_exception_ra(env, EXCP_DATA_ABORT, syn, exception_target_el(env), ra);
245 +}
246 +
247 static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
248 uint64_t ptr, MMUAccessType ptr_access,
249 int ptr_size, MMUAccessType tag_access,
@@ -360,7 +373,7 @@ static void store_tag1_parallel(uint64_t ptr, uint8_t *mem, int tag)
373 typedef void stg_store1(uint64_t, uint8_t *, int);
374
375 static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt,
363 - uintptr_t ra, stg_store1 store1)
376 + uint32_t mtx, uintptr_t ra, stg_store1 store1)
377 {
378 int mmu_idx = arm_env_mmu_index(env);
379 uint8_t *mem;
@@ -374,17 +387,20 @@ static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt,
387 /* Store if page supports tags. */
388 if (mem) {
389 store1(ptr, mem, allocation_tag_from_addr(xt));
390 + } else if (mtx) {
391 + canonical_tag_write_fail(env, ptr, ra);
392 }
393 }
394
380 -void HELPER(stg)(CPUARMState *env, uint64_t ptr, uint64_t xt)
395 +void HELPER(stg)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx)
396 {
382 - do_stg(env, ptr, xt, GETPC(), store_tag1);
397 + do_stg(env, ptr, xt, mtx, GETPC(), store_tag1);
398 }
399
385 -void HELPER(stg_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt)
400 +void HELPER(stg_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt,
401 + uint32_t mtx)
402 {
387 - do_stg(env, ptr, xt, GETPC(), store_tag1_parallel);
403 + do_stg(env, ptr, xt, mtx, GETPC(), store_tag1_parallel);
404 }
405
406 void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr)
@@ -397,7 +413,7 @@ void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr)
413 }
414
415 static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt,
400 - uintptr_t ra, stg_store1 store1)
416 + uint32_t mtx, uintptr_t ra, stg_store1 store1)
417 {
418 int mmu_idx = arm_env_mmu_index(env);
419 int tag = allocation_tag_from_addr(xt);
@@ -420,9 +436,13 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt,
436 /* Store if page(s) support tags. */
437 if (mem1) {
438 store1(TAG_GRANULE, mem1, tag);
439 + } else if (mtx) {
440 + canonical_tag_write_fail(env, ptr, ra);
441 }
442 if (mem2) {
443 store1(0, mem2, tag);
444 + } else if (mtx) {
445 + canonical_tag_write_fail(env, ptr + TAG_GRANULE, ra);
446 }
447 } else {
448 /* Two stores aligned mod TAG_GRANULE*2 -- modify one byte. */
@@ -431,18 +451,22 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt,
451 if (mem1) {
452 tag |= tag << 4;
453 qatomic_set(mem1, tag);
454 + } else if (mtx) {
455 + /* Writing tags to canonically tagged memory region: faults */
456 + canonical_tag_write_fail(env, ptr, ra);
457 }
458 }
459 }
460
438 -void HELPER(st2g)(CPUARMState *env, uint64_t ptr, uint64_t xt)
461 +void HELPER(st2g)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx)
462 {
440 - do_st2g(env, ptr, xt, GETPC(), store_tag1);
463 + do_st2g(env, ptr, xt, mtx, GETPC(), store_tag1);
464 }
465
443 -void HELPER(st2g_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt)
466 +void HELPER(st2g_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt,
467 + uint32_t mtx)
468 {
445 - do_st2g(env, ptr, xt, GETPC(), store_tag1_parallel);
469 + do_st2g(env, ptr, xt, mtx, GETPC(), store_tag1_parallel);
470 }
471
472 void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr)
@@ -528,7 +552,7 @@ uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr, uint32_t mtx)
552 return ret << shift;
553 }
554
531 -void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val)
555 +void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val, uint32_t mtx)
556 {
557 int mmu_idx = arm_env_mmu_index(env);
558 uintptr_t ra = GETPC();
@@ -548,6 +572,10 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val)
572 * and if the OS has enabled access to the tags.
573 */
574 if (!tag_mem) {
575 + /* Storing tags to canonically tagged region: fault. */
576 + if (mtx) {
577 + canonical_tag_write_fail(env, ptr, ra);
578 + }
579 return;
580 }
581
@@ -577,7 +605,8 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val)
605 }
606 }
607
580 -void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val)
608 +void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val,
609 + uint32_t mtx)
610 {
611 uintptr_t ra = GETPC();
612 int mmu_idx = arm_env_mmu_index(env);
@@ -601,6 +630,8 @@ void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val)
630 if (mem) {
631 int tag_pair = (val & 0xf) * 0x11;
632 memset(mem, tag_pair, tag_bytes);
633 + } else if (mtx) {
634 + canonical_tag_write_fail(env, ptr, ra);
635 }
636 }
637
target/arm/tcg/translate-a64.c
+16 -10
@@ -3121,7 +3121,8 @@ static void handle_sys(DisasContext *s, bool isread,
3121 /* Extract the tag from the register to match STZGM. */
3122 tag = tcg_temp_new_i64();
3123 tcg_gen_shri_i64(tag, tcg_rt, 56);
3124 - gen_helper_stzgm_tags(tcg_env, clean_addr, tag);
3124 + gen_helper_stzgm_tags(tcg_env, clean_addr, tag,
3125 + tcg_constant_i32(s->mtx));
3126 }
3127 }
3128 return;
@@ -3138,7 +3139,8 @@ static void handle_sys(DisasContext *s, bool isread,
3139 /* Extract the tag from the register to match STZGM. */
3140 tag = tcg_temp_new_i64();
3141 tcg_gen_shri_i64(tag, tcg_rt, 56);
3141 - gen_helper_stzgm_tags(tcg_env, clean_addr, tag);
3142 + gen_helper_stzgm_tags(tcg_env, clean_addr, tag,
3143 + tcg_constant_i32(s->mtx));
3144 }
3145 }
3146 return;
@@ -3955,9 +3957,11 @@ static bool trans_STGP(DisasContext *s, arg_ldstpair *a)
3957 /* Perform the tag store, if tag access enabled. */
3958 if (s->ata[0]) {
3959 if (tb_cflags(s->base.tb) & CF_PARALLEL) {
3958 - gen_helper_stg_parallel(tcg_env, dirty_addr, dirty_addr);
3960 + gen_helper_stg_parallel(tcg_env, dirty_addr, dirty_addr,
3961 + tcg_constant_i32(s->mtx));
3962 } else {
3960 - gen_helper_stg(tcg_env, dirty_addr, dirty_addr);
3963 + gen_helper_stg(tcg_env, dirty_addr, dirty_addr,
3964 + tcg_constant_i32(s->mtx));
3965 }
3966 }
3967
@@ -4737,7 +4741,7 @@ static bool trans_STZGM(DisasContext *s, arg_ldst_tag *a)
4741 tcg_rt = cpu_reg(s, a->rt);
4742
4743 if (s->ata[0]) {
4740 - gen_helper_stzgm_tags(tcg_env, addr, tcg_rt);
4744 + gen_helper_stzgm_tags(tcg_env, addr, tcg_rt, tcg_constant_i32(s->mtx));
4745 }
4746 /*
4747 * The non-tags portion of STZGM is mostly like DC_ZVA,
@@ -4769,7 +4773,7 @@ static bool trans_STGM(DisasContext *s, arg_ldst_tag *a)
4773 tcg_rt = cpu_reg(s, a->rt);
4774
4775 if (s->ata[0]) {
4772 - gen_helper_stgm(tcg_env, addr, tcg_rt);
4776 + gen_helper_stgm(tcg_env, addr, tcg_rt, tcg_constant_i32(s->mtx));
4777 } else {
4778 MMUAccessType acc = MMU_DATA_STORE;
4779 int size = 4 << s->gm_blocksize;
@@ -4885,15 +4889,17 @@ static bool do_STG(DisasContext *s, arg_ldst_tag *a, bool is_zero, bool is_pair)
4889 }
4890 } else if (tb_cflags(s->base.tb) & CF_PARALLEL) {
4891 if (is_pair) {
4888 - gen_helper_st2g_parallel(tcg_env, addr, tcg_rt);
4892 + gen_helper_st2g_parallel(tcg_env, addr, tcg_rt,
4893 + tcg_constant_i32(s->mtx));
4894 } else {
4890 - gen_helper_stg_parallel(tcg_env, addr, tcg_rt);
4895 + gen_helper_stg_parallel(tcg_env, addr, tcg_rt,
4896 + tcg_constant_i32(s->mtx));
4897 }
4898 } else {
4899 if (is_pair) {
4894 - gen_helper_st2g(tcg_env, addr, tcg_rt);
4900 + gen_helper_st2g(tcg_env, addr, tcg_rt, tcg_constant_i32(s->mtx));
4901 } else {
4896 - gen_helper_stg(tcg_env, addr, tcg_rt);
4902 + gen_helper_stg(tcg_env, addr, tcg_rt, tcg_constant_i32(s->mtx));
4903 }
4904 }
4905