Commit d372cbc1c8 for qemu.org

commit d372cbc1c824c4346d1b822928d888fc04952af3
Author: Marco Liebel <marco.liebel@oss.qualcomm.com>
Date:   Mon Nov 9 15:21:41 2026 -0800

    target/hexagon: serialize TLB invalidation and IMASK access

    Hold the shared TLB mutex during ASID invalidation so it cannot
    overwrite a concurrent entry replacement.

    Route local IMASK reads and writes through BQL-protected helpers
    to serialize them with cross-thread interrupt assignment. Apply
    the pre-V81 writable-bit mask under the same lock.

    Signed-off-by: Marco Liebel <marco.liebel@oss.qualcomm.com>
    Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

diff --git a/hw/hexagon/hexagon_tlb.c b/hw/hexagon/hexagon_tlb.c
index 3e72026d62..07406cea35 100644
--- a/hw/hexagon/hexagon_tlb.c
+++ b/hw/hexagon/hexagon_tlb.c
@@ -325,6 +325,7 @@ uint32_t hexagon_tlb_invalidate_asid(HexagonTLBState *tlb, uint32_t asid)
 {
     uint32_t invalidated = 0;

+    QEMU_LOCK_GUARD(&tlb->lock);
     for (uint32_t i = 0; i < tlb->num_entries; i++) {
         uint64_t entry = tlb->entries[i];

diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
index addb5d23d9..52615628e3 100644
--- a/target/hexagon/genptr.c
+++ b/target/hexagon/genptr.c
@@ -302,13 +302,11 @@ static void gen_log_sreg_write(DisasContext *ctx, int rnum, TCGv_i32 val)
     if (rnum == HEX_SREG_MODECTL || rnum == HEX_SREG_SYSCFG) {
         translator_io_start(&ctx->base);
     }
-    if (rnum == HEX_SREG_IMASK &&
-        ctx->hex_def->hex_version < HEX_VER_V81) {
-        reg_mask = 0xffff0000;
-    }
-
     if (reg_mask != IMMUTABLE) {
-        if (rnum < HEX_SREG_GLB_START) {
+        if (rnum == HEX_SREG_IMASK) {
+            /* Commit through a helper to serialize with iassignw/setimask. */
+            tcg_gen_mov_i32(ctx->t_sreg_new_value[rnum], val);
+        } else if (rnum < HEX_SREG_GLB_START) {
             gen_masked_reg_write(val, hex_t_sreg[rnum], reg_mask);
             if (ctx->need_commit || rnum == HEX_SREG_SSR ||
                 rnum == HEX_SREG_CCR) {
@@ -343,7 +341,8 @@ static void gen_read_sreg(DisasContext *ctx, TCGv_i32 dst, int reg_num)
         reg_num == HEX_SREG_PCYCLELO || reg_num == HEX_SREG_PCYCLEHI) {
         translator_io_start(&ctx->base);
     }
-    if (reg_num >= HEX_SREG_GLB_START || reg_num == HEX_SREG_BADVA) {
+    if (reg_num >= HEX_SREG_GLB_START || reg_num == HEX_SREG_BADVA ||
+        reg_num == HEX_SREG_IMASK) {
         gen_helper_sreg_read(dst, tcg_env, tcg_constant_i32(reg_num));
     } else {
         tcg_gen_mov_i32(dst, hex_t_sreg[reg_num]);
@@ -356,7 +355,9 @@ static void gen_read_sreg_pair(DisasContext *ctx, TCGv_i64 dst, int reg_num)
     if (reg_num == HEX_SREG_TIMERLO || reg_num == HEX_SREG_PCYCLELO) {
         translator_io_start(&ctx->base);
     }
-    if (reg_num < HEX_SREG_GLB_START) {
+    if (reg_num == HEX_SREG_IMASK) {
+        gen_helper_sreg_read_pair(dst, tcg_env, tcg_constant_i32(reg_num));
+    } else if (reg_num < HEX_SREG_GLB_START) {
         if (reg_num + 1 == HEX_SREG_BADVA) {
             TCGv_i32 badva = tcg_temp_new_i32();
             gen_helper_sreg_read(badva, tcg_env,
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 0a16778e5e..71b27ef07f 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1994,6 +1994,10 @@ void HELPER(sreg_write_masked)(CPUHexagonState *env, uint32_t reg, uint32_t val)
 {
     BQL_LOCK_GUARD();
     if (reg < HEX_SREG_GLB_START) {
+        if (reg == HEX_SREG_IMASK &&
+            env_archcpu(env)->cfg.hex_def->hex_version < HEX_VER_V81) {
+            val = deposit32(env->t_sreg[reg], 0, 16, val);
+        }
         env->t_sreg[reg] = val;
     } else {
         HexagonCPU *cpu = env_archcpu(env);
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 8ab28d40d3..ec1d79e31f 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -804,8 +804,13 @@ static void gen_start_packet(DisasContext *ctx)
         int reg_num = ctx->sreg_log[i];
         if (reg_num < HEX_SREG_GLB_START) {
             ctx->t_sreg_new_value[reg_num] = tcg_temp_new();
-            tcg_gen_mov_tl(ctx->t_sreg_new_value[reg_num],
-                          hex_t_sreg[reg_num]);
+            if (reg_num == HEX_SREG_IMASK) {
+                gen_helper_sreg_read(ctx->t_sreg_new_value[reg_num], tcg_env,
+                                     tcg_constant_i32(reg_num));
+            } else {
+                tcg_gen_mov_tl(ctx->t_sreg_new_value[reg_num],
+                              hex_t_sreg[reg_num]);
+            }
         }
     }
     for (i = 0; i < NUM_GREGS; i++) {
@@ -1071,8 +1076,11 @@ static void gen_sreg_writes(DisasContext *ctx)
                                   old_reg);
             /* A CCR write can change context, so end the TB. */
             ctx->base.is_jmp = DISAS_NORETURN;
+        } else if (reg_num == HEX_SREG_IMASK) {
+            gen_helper_sreg_write_masked(tcg_env, tcg_constant_i32(reg_num),
+                                         ctx->t_sreg_new_value[reg_num]);
+            gen_helper_pending_interrupt(tcg_env);
         } else if ((reg_num == HEX_SREG_STID) ||
-                    (reg_num == HEX_SREG_IMASK) ||
                    (reg_num == HEX_SREG_IPENDAD) ||
                    (reg_num == HEX_SREG_IPEND) ||
                    (reg_num == HEX_SREG_IAD)) {
@@ -1715,7 +1723,7 @@ void hexagon_translate_init(void)
                 hexagon_gregnames[i]);
     }
     for (i = 0; i < NUM_SREGS; i++) {
-        if (i < HEX_SREG_GLB_START) {
+        if (i < HEX_SREG_GLB_START && i != HEX_SREG_IMASK) {
             hex_t_sreg[i] = tcg_global_mem_new_i32(tcg_env,
                 offsetof(CPUHexagonState, t_sreg[i]),
                 hexagon_sregnames[i]);