All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes
@ 2026-09-01 17:33 Brian Cain
  2026-09-01 17:33 ` [PATCH v2 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
                   ` (10 more replies)
  0 siblings, 11 replies; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

Changes to address some test cases:
- user/system mode exception consolidation
- Direct-to-Guest interrupt delivery
- interrupt mask writes, tlb probe exception
- l2vic edge-trigger clear on deassert
- map architectural timer regs to clock

v2:
- for `guard writes to unimplemented guest registers`: updated
the logic to early-return and clarified the commit message.
- `implement direct-to-guest interrupt delivery`: added a comment
to explain "dtg".

v1: https://lore.kernel.org/qemu-devel/20260819013144.3264096-1-brian.cain@oss.qualcomm.com/

Brian Cain (10):
  target/hexagon: align exceptions for user/sysemu
  tests/tcg/hexagon: check priv instructions raise SIGILL
  target/hexagon: guard writes to unimplemented guest registers
  target/hexagon: take BQL when reading the system pcycle count
  target/hexagon: gate GPCYCLE guest register reads on SSR:CE
  target/hexagon: read UTIMERLO/UTIMERHI from the global timer
  target/hexagon: raise imprecise exception on multi-TLB match
  target/hexagon: implement direct-to-guest interrupt delivery
  target/hexagon: fix iassign{r,w} to cover all threads
  tests/functional/hexagon: update to v0.2.12,
    +test_{interrupts,sys_regs}

Sid Manning (1):
  hw/intc: clear pending bit on l2vic de-assertion

 include/hw/hexagon/hexagon_tlb.h            |  3 +-
 target/hexagon/cpu.h                        |  1 +
 target/hexagon/helper.h                     |  4 +
 target/hexagon/translate.h                  |  6 +-
 target/hexagon/reg_fields_def.h.inc         |  7 ++
 hw/hexagon/hexagon_tlb.c                    |  5 +-
 hw/intc/hex-l2vic.c                         |  2 +
 linux-user/hexagon/cpu_loop.c               | 30 +++----
 target/hexagon/cpu.c                        | 15 +++-
 target/hexagon/cpu_helper.c                 | 12 ++-
 target/hexagon/genptr.c                     | 29 +++++++
 target/hexagon/hex_interrupts.c             | 91 +++++++++++++++++++--
 target/hexagon/hex_mmu.c                    |  5 +-
 target/hexagon/hexswi.c                     | 37 ++++++++-
 target/hexagon/machine.c                    |  5 +-
 target/hexagon/op_helper.c                  | 72 ++++++++--------
 target/hexagon/translate.c                  | 56 +++++++++----
 tests/tcg/hexagon/privileged-insn.c         | 79 ++++++++++++++++++
 tests/tcg/hexagon/reg_mut.c                 | 11 ++-
 target/hexagon/hex_common.py                | 18 +++-
 tests/functional/hexagon/test_arch_tests.py | 12 ++-
 tests/tcg/hexagon/Makefile.target           |  1 +
 22 files changed, 395 insertions(+), 106 deletions(-)
 create mode 100644 tests/tcg/hexagon/privileged-insn.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 01/11] target/hexagon: align exceptions for user/sysemu
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-01 17:33 ` [PATCH v2 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL Brian Cain
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

System mode reports an exception as cs->exception_index = HEX_EVENT_* plus
env->cause_code = HEX_CAUSE_*, but translated code in user mode put the cause
code straight into exception_index, so cpu_loop() was decoding both forms.
gen_exception_decode_fail() and the misaligned-PC check used the raw form
unconditionally, so in system mode the cause code was misread as an event
number.

Use the {event, cause} everywhere and drop the duplicated cases
from cpu_loop(), which fixes HEX_CAUSE_PRIV_USER_NO_SINSN and
HEX_CAUSE_PRIV_USER_NO_GINSN.  The misaligned PC is no longer zeroed
on its way out either, so it reaches the signal frame as si_addr instead
of whatever r31 held.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/translate.h    |  2 +-
 linux-user/hexagon/cpu_loop.c | 30 +++++++++++-------------------
 target/hexagon/cpu.c          |  3 ++-
 target/hexagon/translate.c    | 27 +++++++++------------------
 4 files changed, 23 insertions(+), 39 deletions(-)

diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
index 3c5773e2c73..00de2b0d2ec 100644
--- a/target/hexagon/translate.h
+++ b/target/hexagon/translate.h
@@ -330,7 +330,7 @@ extern TCGv_i32 hex_t_sreg[NUM_SREGS];
 #endif
 
 
-void hex_gen_exception_end_tb(DisasContext *ctx, int excp);
+void hex_gen_exception_end_tb(DisasContext *ctx, int cause);
 
 void process_store(DisasContext *ctx, int slot_num);
 
diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c
index d7f73439dbc..e4ef97a1184 100644
--- a/linux-user/hexagon/cpu_loop.c
+++ b/linux-user/hexagon/cpu_loop.c
@@ -66,21 +66,22 @@ void cpu_loop(CPUHexagonState *env)
             case HEX_CAUSE_FETCH_NO_UPAGE:
             case HEX_CAUSE_PRIV_NO_UREAD:
             case HEX_CAUSE_PRIV_NO_UWRITE:
-            force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR,
-                    env->gpr[HEX_REG_PC]);
-
-            break;
+                force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR,
+                                env->gpr[HEX_REG_PC]);
+                break;
             case HEX_CAUSE_PRIV_USER_NO_GINSN:
             case HEX_CAUSE_PRIV_USER_NO_SINSN:
             case HEX_CAUSE_INVALID_PACKET:
-            force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC,
-                    env->gpr[HEX_REG_PC]);
-            break;
+            case HEX_CAUSE_REG_WRITE_CONFLICT:
+                force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC,
+                                env->gpr[HEX_REG_PC]);
+                break;
             case HEX_CAUSE_MISALIGNED_LOAD:
             case HEX_CAUSE_MISALIGNED_STORE:
-            force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN,
-                    env->gpr[HEX_REG_PC]);
-            break;
+            case HEX_CAUSE_PC_NOT_ALIGNED:
+                force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN,
+                                env->gpr[HEX_REG_PC]);
+                break;
             default:
                 EXCP_DUMP(env, "\nqemu: unhandled CPU precise exception "
                     "cause code 0x%x - aborting\n",
@@ -88,15 +89,6 @@ void cpu_loop(CPUHexagonState *env)
                 exit(EXIT_FAILURE);
             }
             break;
-        case HEX_CAUSE_PC_NOT_ALIGNED:
-            force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN,
-                            env->gpr[HEX_REG_R31]);
-            break;
-        case HEX_CAUSE_INVALID_PACKET:
-        case HEX_CAUSE_REG_WRITE_CONFLICT:
-            force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC,
-                            env->gpr[HEX_REG_PC]);
-            break;
         case EXCP_ATOMIC:
             cpu_exec_step_atomic(cs);
             break;
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 7067e5b70f7..0bbefc2fb87 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -323,7 +323,8 @@ static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs)
         hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1);
     }
     if (pc & PCALIGN_MASK) {
-        hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0);
+        env->cause_code = HEX_CAUSE_PC_NOT_ALIGNED;
+        hexagon_raise_exception_err(env, HEX_EVENT_PRECISE, pc);
     }
 
 #ifndef CONFIG_USER_ONLY
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 06a8159d283..5cfa60ca302 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -73,8 +73,8 @@ TCGv hex_vstore_pending[VSTORES_MAX];
 #ifndef CONFIG_USER_ONLY
 TCGv_i32 hex_greg[NUM_GREGS];
 TCGv_i32 hex_t_sreg[NUM_SREGS];
-TCGv_i32 hex_cause_code;
 #endif
+static TCGv_i32 hex_cause_code;
 
 static const char * const hexagon_prednames[] = {
   "p0", "p1", "p2", "p3"
@@ -128,19 +128,14 @@ intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum,
     return offset;
 }
 
-static void gen_exception(int excp, uint32_t PC)
+static void gen_precise_exception(int cause, uint32_t PC)
 {
-    gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp),
+    tcg_gen_movi_i32(hex_cause_code, cause);
+    gen_helper_raise_exception(tcg_env, tcg_constant_i32(HEX_EVENT_PRECISE),
                                tcg_constant_i32(PC));
 }
 
 #ifndef CONFIG_USER_ONLY
-static inline void gen_precise_exception(int excp, uint32_t PC)
-{
-    tcg_gen_movi_i32(hex_cause_code, excp);
-    gen_exception(HEX_EVENT_PRECISE, PC);
-}
-
 static void gen_pcycle_counters(DisasContext *ctx)
 {
     if (ctx->pcycle_enabled) {
@@ -224,14 +219,10 @@ static void gen_end_tb(DisasContext *ctx)
     ctx->base.is_jmp = DISAS_NORETURN;
 }
 
-void hex_gen_exception_end_tb(DisasContext *ctx, int excp)
+void hex_gen_exception_end_tb(DisasContext *ctx, int cause)
 {
     gen_exec_counters(ctx);
-#ifdef CONFIG_USER_ONLY
-    gen_exception(excp, ctx->pkt.pc);
-#else
-    gen_precise_exception(excp, ctx->pkt.pc);
-#endif
+    gen_precise_exception(cause, ctx->pkt.pc);
     ctx->base.is_jmp = DISAS_NORETURN;
 }
 
@@ -239,13 +230,13 @@ void hex_gen_exception_end_tb(DisasContext *ctx, int excp)
  * Generate exception for decode failures. Unlike gen_exception_end_tb,
  * this is used when decode fails before ctx->next_PC is initialized.
  */
-static void gen_exception_decode_fail(DisasContext *ctx, int nwords, int excp)
+static void gen_exception_decode_fail(DisasContext *ctx, int nwords, int cause)
 {
     target_ulong fail_pc = ctx->base.pc_next + nwords * sizeof(uint32_t);
 
     gen_exec_counters(ctx);
     tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], fail_pc);
-    gen_exception(excp, fail_pc);
+    gen_precise_exception(cause, fail_pc);
     ctx->base.is_jmp = DISAS_NORETURN;
     ctx->base.pc_next = fail_pc;
 }
@@ -1361,9 +1352,9 @@ void hexagon_translate_init(void)
         offsetof(CPUHexagonState, llsc_val), "llsc_val");
     hex_llsc_val_i64 = tcg_global_mem_new_i64(tcg_env,
         offsetof(CPUHexagonState, llsc_val_i64), "llsc_val_i64");
-#ifndef CONFIG_USER_ONLY
     hex_cause_code = tcg_global_mem_new_i32(tcg_env,
         offsetof(CPUHexagonState, cause_code), "cause_code");
+#ifndef CONFIG_USER_ONLY
     hex_cycle_count = tcg_global_mem_new_i64(tcg_env,
         offsetof(CPUHexagonState, t_cycle_count), "t_cycle_count");
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
  2026-09-01 17:33 ` [PATCH v2 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-01 17:33 ` [PATCH v2 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

A_PRIV and A_GUEST instructions executed from user mode raise
HEX_CAUSE_PRIV_USER_NO_SINSN and HEX_CAUSE_PRIV_USER_NO_GINSN, which aborted
qemu-hexagon until the preceding patch.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 tests/tcg/hexagon/privileged-insn.c | 79 +++++++++++++++++++++++++++++
 tests/tcg/hexagon/Makefile.target   |  1 +
 2 files changed, 80 insertions(+)
 create mode 100644 tests/tcg/hexagon/privileged-insn.c

diff --git a/tests/tcg/hexagon/privileged-insn.c b/tests/tcg/hexagon/privileged-insn.c
new file mode 100644
index 00000000000..306d4542ea4
--- /dev/null
+++ b/tests/tcg/hexagon/privileged-insn.c
@@ -0,0 +1,79 @@
+/*
+ * Test that privileged and guest-mode instructions raise SIGILL in user mode.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void *resume_pc;
+
+static void handle_sigill(int sig, siginfo_t *info, void *puc)
+{
+    ucontext_t *uc = (ucontext_t *)puc;
+
+    if (sig != SIGILL) {
+        _exit(EXIT_FAILURE);
+    }
+
+    uc->uc_mcontext.r0 = SIGILL;
+    uc->uc_mcontext.pc = (unsigned long)resume_pc;
+}
+
+static int test_priv_insn(void)
+{
+    int sig;
+
+    asm volatile(
+        "r0 = #0\n"
+        "r1 = ##1f\n"
+        "memw(%[pc]) = r1\n"
+        "stop(r0)\n"
+        "1:\n"
+        "%[sig] = r0\n"
+        : [sig] "=r"(sig)
+        : [pc] "r"(&resume_pc)
+        : "r0", "r1", "memory");
+
+    return sig;
+}
+
+static int test_guest_insn(void)
+{
+    int sig;
+
+    asm volatile(
+        "r0 = #0\n"
+        "r1 = ##1f\n"
+        "memw(%[pc]) = r1\n"
+        "r0 = g0\n"
+        "1:\n"
+        "%[sig] = r0\n"
+        : [sig] "=r"(sig)
+        : [pc] "r"(&resume_pc)
+        : "r0", "r1", "memory");
+
+    return sig;
+}
+
+int main()
+{
+    struct sigaction act;
+
+    memset(&act, 0, sizeof(act));
+    act.sa_sigaction = handle_sigill;
+    act.sa_flags = SA_SIGINFO;
+    assert(sigaction(SIGILL, &act, NULL) == 0);
+
+    assert(test_priv_insn() == SIGILL);
+    assert(test_guest_insn() == SIGILL);
+
+    puts("PASS");
+    return EXIT_SUCCESS;
+}
diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target
index 61adf6356e4..641f6bc10e7 100644
--- a/tests/tcg/hexagon/Makefile.target
+++ b/tests/tcg/hexagon/Makefile.target
@@ -58,6 +58,7 @@ HEX_TESTS += invalid-slots
 HEX_TESTS += valid-slots
 HEX_TESTS += invalid-encoding
 HEX_TESTS += multiple-writes
+HEX_TESTS += privileged-insn
 HEX_TESTS += unaligned_pc
 HEX_TESTS += unaligned_data
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 03/11] hw/intc: clear pending bit on l2vic de-assertion
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
  2026-09-01 17:33 ` [PATCH v2 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
  2026-09-01 17:33 ` [PATCH v2 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-02 21:30   ` Philippe Mathieu-Daudé
  2026-09-01 17:33 ` [PATCH v2 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller,
	Sid Manning

From: Sid Manning <sidneym@quicinc.com>

l2vic_set_irq() clears a level-triggered source's pending bit on
de-assertion, so a source that drops before it is latched into
int_status is not spuriously re-delivered on the next ciad.
Edge-triggered sources (int_type set) keep the pulse semantics they
already have via SOFT_INT/set_irq(level=1).

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 hw/intc/hex-l2vic.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/hw/intc/hex-l2vic.c b/hw/intc/hex-l2vic.c
index f07ec850d49..a986f0bdf35 100644
--- a/hw/intc/hex-l2vic.c
+++ b/hw/intc/hex-l2vic.c
@@ -299,6 +299,8 @@ static void l2vic_set_irq(void *opaque, int irq, int level)
 
     if (level) {
         set_bit32(irq, s->int_pending);
+    } else if (!test_bit32(irq, s->int_type)) {
+        clear_bit32(irq, s->int_pending);
     }
     l2vic_update(s, irq);
 }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 04/11] target/hexagon: guard writes to unimplemented guest registers
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (2 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-01 18:25   ` Pierrick Bouvier
  2026-09-01 17:33 ` [PATCH v2 05/11] target/hexagon: take BQL when reading the system pcycle count Brian Cain
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

Guest register G4 and above have PMU-ish features that are yet
to be implemented.  We'll skip these when we encounter this code
rather than fault on an assertion.

Note that the generated call to greg_writable() has the side-effect
of doing a LOG_UNIMP for these register accesses.

Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/translate.h   |  4 +++-
 target/hexagon/hex_common.py | 18 +++++++++++++++++-
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
index 00de2b0d2ec..d8628383f1d 100644
--- a/target/hexagon/translate.h
+++ b/target/hexagon/translate.h
@@ -97,7 +97,9 @@ bool is_gather_store_insn(DisasContext *ctx);
 #ifndef CONFIG_USER_ONLY
 static inline void ctx_log_greg_write(DisasContext *ctx, int rnum)
 {
-    assert(rnum <= HEX_GREG_G3);
+    if (rnum > HEX_GREG_G3) {
+        return;
+    }
     ctx->greg_log[ctx->greg_log_idx] = rnum;
     ctx->greg_log_idx++;
 }
diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py
index e33d43e3ce0..c180c19b092 100755
--- a/target/hexagon/hex_common.py
+++ b/target/hexagon/hex_common.py
@@ -1097,11 +1097,24 @@ def analyze_write(self, f, tag, regno):
         """))
 
 class GuestRegister(Register):
-    pass
+    def gen_check_impl(self, f, regno):
+        if self.is_written():
+            f.write(code_fmt(f"""\
+                if (!greg_writable(insn->regno[{regno}],
+                    {str(self.is_pair()).lower()})) {{
+                    return;
+                }}
+            """))
+        else:
+            f.write(code_fmt(f"""\
+                check_greg_impl(insn->regno[{regno}],
+                                {str(self.is_pair()).lower()});
+            """))
 
 class GuestDest(GuestRegister, Single, Dest):
     def decl_tcg(self, f, tag, regno):
         self.decl_reg_num(f, regno)
+        self.gen_check_impl(f, regno)
         f.write(code_fmt(f"""\
             TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32();
         """))
@@ -1121,6 +1134,7 @@ def decl_reg_num(self, f, regno):
         """))
     def decl_tcg(self, f, tag, regno):
         self.decl_reg_num(f, regno)
+        self.gen_check_impl(f, regno)
         f.write(code_fmt(f"""\
             TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32();
             gen_read_greg({self.reg_tcg()}, {self.reg_num});
@@ -1131,6 +1145,7 @@ def analyze_read(self, f, regno):
 class GuestPairDest(GuestRegister, Pair, Dest):
     def decl_tcg(self, f, tag, regno):
         self.decl_reg_num(f, regno)
+        self.gen_check_impl(f, regno)
         f.write(code_fmt(f"""\
             TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64();
         """))
@@ -1150,6 +1165,7 @@ def decl_reg_num(self, f, regno):
         """))
     def decl_tcg(self, f, tag, regno):
         self.decl_reg_num(f, regno)
+        self.gen_check_impl(f, regno)
         f.write(code_fmt(f"""\
             TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64();
             gen_read_greg_pair({self.reg_tcg()}, {self.reg_num});
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 05/11] target/hexagon: take BQL when reading the system pcycle count
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (3 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-01 17:33 ` [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

hexagon_get_sys_pcycle_count() iterates all CPUs, so take the BQL with
BQL_LOCK_GUARD() instead of asserting the caller already holds it.
Convert the matching setters the same way, which keeps the locking
contract symmetric and makes the read-modify-write in the _low/_high
setters atomic.  BQL_LOCK_GUARD() is a no-op when the lock is already
held, so the nested guards on the guest-register read path cost nothing.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/cpu_helper.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
index 64c5746c6d9..649c583c0fa 100644
--- a/target/hexagon/cpu_helper.c
+++ b/target/hexagon/cpu_helper.c
@@ -36,7 +36,7 @@ uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env)
     uint64_t total = 0;
     CPUState *cs;
 
-    g_assert(bql_locked());
+    BQL_LOCK_GUARD();
     CPU_FOREACH(cs) {
         CPUHexagonState *thread_env = cpu_env(cs);
         total += thread_env->t_cycle_count;
@@ -54,11 +54,15 @@ uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env)
     return (uint32_t)(hexagon_get_sys_pcycle_count(env));
 }
 
+/*
+ * Every function in this family takes the BQL itself, so the guard below
+ * holds it across the read-modify-write.  Nested guards are no-ops.
+ */
 void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t val)
 {
     uint64_t old;
 
-    g_assert(bql_locked());
+    BQL_LOCK_GUARD();
     old = hexagon_get_sys_pcycle_count(env);
     old = deposit64(old, 32, 32, val);
     hexagon_set_sys_pcycle_count(env, old);
@@ -68,7 +72,7 @@ void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t val)
 {
     uint64_t old;
 
-    g_assert(bql_locked());
+    BQL_LOCK_GUARD();
     old = hexagon_get_sys_pcycle_count(env);
     old = deposit64(old, 0, 32, val);
     hexagon_set_sys_pcycle_count(env, old);
@@ -81,7 +85,7 @@ void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t val)
     int num_threads;
     int64_t delta, per_thread, remainder;
 
-    g_assert(bql_locked());
+    BQL_LOCK_GUARD();
     total = hexagon_get_sys_pcycle_count(env);
 
     /* Count active threads */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (4 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 05/11] target/hexagon: take BQL when reading the system pcycle count Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-02 21:16   ` Philippe Mathieu-Daudé
  2026-09-01 17:33 ` [PATCH v2 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

GPCYCLELO/GPCYCLEHI read as zero unless the cycle counter is enabled
(SSR:CE), matching the hardware.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/cpu.c       | 7 +++++--
 target/hexagon/op_helper.c | 9 ++-------
 2 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 0bbefc2fb87..0a677840bcb 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -809,14 +809,17 @@ static void hexagon_cpu_class_init(ObjectClass *c, const void *data)
 #ifndef CONFIG_USER_ONLY
 uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg)
 {
+    uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
+    int ssr_ce = GET_SSR_FIELD(SSR_CE, ssr);
+
     if (reg <= HEX_GREG_G3) {
         return env->greg[reg];
     }
     switch (reg) {
     case HEX_GREG_GPCYCLELO:
-        return hexagon_get_sys_pcycle_count_low(env);
+        return ssr_ce ? hexagon_get_sys_pcycle_count_low(env) : 0;
     case HEX_GREG_GPCYCLEHI:
-        return hexagon_get_sys_pcycle_count_high(env);
+        return ssr_ce ? hexagon_get_sys_pcycle_count_high(env) : 0;
     default:
         qemu_log_mask(LOG_UNIMP, "reading greg %" PRId32
                 " not yet supported.\n", reg);
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 23894ff3d28..2cea1927263 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1912,13 +1912,8 @@ uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg)
         return (uint64_t)(env->greg[reg]) |
                (((uint64_t)(env->greg[reg + 1])) << 32);
     }
-    switch (reg) {
-    case HEX_GREG_GPCYCLELO:
-        return hexagon_get_sys_pcycle_count(env);
-    default:
-        return (uint64_t)hexagon_greg_read(env, reg) |
-               ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
-    }
+    return (uint64_t)hexagon_greg_read(env, reg) |
+           ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
 }
 
 /*
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (5 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-02 21:21   ` Philippe Mathieu-Daudé
  2026-09-01 17:33 ` [PATCH v2 08/11] target/hexagon: raise imprecise exception on multi-TLB match Brian Cain
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

In system mode, route accesses to the qtimer-backed global
TIMERLO/TIMERHI so guest reads see a live, monotonically increasing
timer.

In user mode we derive it from QEMU_CLOCK_VIRTUAL.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/helper.h     |  4 ++++
 target/hexagon/genptr.c     | 29 +++++++++++++++++++++++++++++
 target/hexagon/op_helper.c  | 21 +++++++++++++++++++++
 tests/tcg/hexagon/reg_mut.c | 11 ++++++++---
 4 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h
index 78dc28ca9e5..e39afd623b5 100644
--- a/target/hexagon/helper.h
+++ b/target/hexagon/helper.h
@@ -113,6 +113,10 @@ DEF_HELPER_FLAGS_4(gvec_sabsdiff_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 DEF_HELPER_FLAGS_4(gvec_uabsdiff_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 DEF_HELPER_FLAGS_4(gvec_uabsdiff_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 
+#if defined(CONFIG_USER_ONLY)
+DEF_HELPER_FLAGS_0(utimer, TCG_CALL_NO_RWG, i64)
+#endif
+
 #if !defined(CONFIG_USER_ONLY)
 DEF_HELPER_3(raise_stack_overflow, void, env, i32, i32)
 DEF_HELPER_2(swi, void, env, i32)
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
index 2a98b13b714..282a697d190 100644
--- a/target/hexagon/genptr.c
+++ b/target/hexagon/genptr.c
@@ -411,6 +411,23 @@ static inline void gen_read_ctrl_reg(DisasContext *ctx, const int reg_num,
     } else if (reg_num == HEX_REG_QEMU_HVX_CNT) {
         tcg_gen_addi_tl(dest, hex_gpr[HEX_REG_QEMU_HVX_CNT],
                         ctx->num_hvx_insns);
+#ifndef CONFIG_USER_ONLY
+    } else if (reg_num == HEX_REG_UTIMERLO) {
+        gen_helper_sreg_read(dest, tcg_env,
+                             tcg_constant_i32(HEX_SREG_TIMERLO));
+    } else if (reg_num == HEX_REG_UTIMERHI) {
+        gen_helper_sreg_read(dest, tcg_env,
+                             tcg_constant_i32(HEX_SREG_TIMERHI));
+#else
+    } else if (reg_num == HEX_REG_UTIMERLO) {
+        TCGv_i64 utimer = tcg_temp_new_i64();
+        gen_helper_utimer(utimer);
+        tcg_gen_extrl_i64_i32(dest, utimer);
+    } else if (reg_num == HEX_REG_UTIMERHI) {
+        TCGv_i64 utimer = tcg_temp_new_i64();
+        gen_helper_utimer(utimer);
+        tcg_gen_extrh_i64_i32(dest, utimer);
+#endif
     } else {
         tcg_gen_mov_tl(dest, hex_gpr[reg_num]);
     }
@@ -439,6 +456,18 @@ static inline void gen_read_ctrl_reg_pair(DisasContext *ctx, const int reg_num,
         tcg_gen_addi_tl(hvx_cnt, hex_gpr[HEX_REG_QEMU_HVX_CNT],
                         ctx->num_hvx_insns);
         tcg_gen_concat_i32_i64(dest, hvx_cnt, hex_gpr[reg_num + 1]);
+#ifndef CONFIG_USER_ONLY
+    } else if (reg_num == HEX_REG_UTIMERLO) {
+        TCGv lo = tcg_temp_new();
+        TCGv hi = tcg_temp_new();
+        gen_helper_sreg_read(lo, tcg_env, tcg_constant_i32(HEX_SREG_TIMERLO));
+        gen_helper_sreg_read(hi, tcg_env, tcg_constant_i32(HEX_SREG_TIMERHI));
+        tcg_gen_concat_i32_i64(dest, lo, hi);
+#else
+    } else if (reg_num == HEX_REG_UTIMERLO) {
+        /* One helper call, so the pair is a coherent 64-bit snapshot. */
+        gen_helper_utimer(dest);
+#endif
     } else {
         tcg_gen_concat_i32_i64(dest,
             hex_gpr[reg_num],
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 2cea1927263..df31ed2488a 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -37,6 +37,9 @@
 #include "cpu_helper.h"
 #include "tcg/tcg-gvec-desc.h"
 #include "translate.h"
+#ifdef CONFIG_USER_ONLY
+#include "qemu/timer.h"
+#endif
 #ifndef CONFIG_USER_ONLY
 #include "hw/hexagon/hexagon_globalreg.h"
 #include "hex_mmu.h"
@@ -46,6 +49,24 @@
 #include "hexswi.h"
 #endif
 
+#ifdef CONFIG_USER_ONLY
+/*
+ * User mode has no qtimer device backing TIMERLO/TIMERHI, so derive the
+ * user timer directly from the virtual clock -- the same clock the qtimer
+ * counts -- at the qtimer's default 19.2MHz tick rate, masked to the
+ * qtimer's counter width.
+ */
+#define HEX_UTIMER_FREQ_HZ  19200000ULL
+#define HEX_UTIMER_CNT_MASK 0x00ffffffffffffffULL
+
+uint64_t HELPER(utimer)(void)
+{
+    return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+                    HEX_UTIMER_FREQ_HZ, NANOSECONDS_PER_SECOND) &
+           HEX_UTIMER_CNT_MASK;
+}
+#endif
+
 #define SF_BIAS        127
 #define SF_MANTBITS    23
 
diff --git a/tests/tcg/hexagon/reg_mut.c b/tests/tcg/hexagon/reg_mut.c
index c5a39e55100..9ce18f3ebe5 100644
--- a/tests/tcg/hexagon/reg_mut.c
+++ b/tests/tcg/hexagon/reg_mut.c
@@ -77,10 +77,10 @@ static inline void write_control_registers(void)
     check32(result, 0x00000000);
 
     WRITE_REG_NOCLOBBER(result, "utimerlo", 0xffffffff);
-    check32(result, 0x00000000);
+    check32_ne(result, 0xffffffff);
 
     WRITE_REG_NOCLOBBER(result, "utimerhi", 0xffffffff);
-    check32(result, 0x00000000);
+    check32_ne(result, 0xffffffff);
 
     /*
      * PC is special.  Setting it to these values
@@ -106,8 +106,13 @@ static inline void write_control_register_pairs(void)
     WRITE_REG_NOCLOBBER(result, "c15:14", 0xffffffffffffffff);
     check64(result, 0x0000000000000000);
 
+    /*
+     * c31:30 is UTIMERHI:UTIMERLO, a read-only free-running counter.  The
+     * write must be discarded; the read-back is whatever the timer says,
+     * so only check that the written value did not stick.
+     */
     WRITE_REG_NOCLOBBER(result, "c31:30", 0xffffffffffffffff);
-    check64(result, 0x0000000000000000);
+    check64_ne(result, 0xffffffffffffffff);
 
     WRITE_REG_PAIR_ENCODED(result, "c9:8", (uint64_t) 0x0000000000000000,
                            C9_8_EQ_R1_0);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 08/11] target/hexagon: raise imprecise exception on multi-TLB match
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (6 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-01 17:33 ` [PATCH v2 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

tlbp now records a pending imprecise exception (via env->imprecise_exception)
when the lookup matches multiple entries, and the translator raises it after
the tlbp packet.  Implement the HEX_EVENT_IMPRECISE delivery path so the
guest sees HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH instead of the exception
being silently dropped.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 include/hw/hexagon/hexagon_tlb.h |  3 ++-
 target/hexagon/cpu.h             |  1 +
 hw/hexagon/hexagon_tlb.c         |  5 ++++-
 target/hexagon/hex_mmu.c         |  5 ++++-
 target/hexagon/hexswi.c          | 37 +++++++++++++++++++++++++++++---
 target/hexagon/machine.c         |  5 +++--
 target/hexagon/translate.c       | 29 +++++++++++++++++++++++++
 7 files changed, 77 insertions(+), 8 deletions(-)

diff --git a/include/hw/hexagon/hexagon_tlb.h b/include/hw/hexagon/hexagon_tlb.h
index 760dc1ea811..67c0a56b79e 100644
--- a/include/hw/hexagon/hexagon_tlb.h
+++ b/include/hw/hexagon/hexagon_tlb.h
@@ -32,7 +32,8 @@ bool hexagon_tlb_find_match(HexagonTLBState *tlb, uint32_t asid,
                             int32_t *excp, int *cause_code, int mmu_idx);
 
 uint32_t hexagon_tlb_lookup(HexagonTLBState *tlb, uint32_t asid,
-                            uint32_t VA, int *cause_code);
+                            uint32_t VA, uint32_t *imprecise_exception,
+                            int *cause_code);
 
 int hexagon_tlb_check_overlap(HexagonTLBState *tlb, uint64_t entry,
                               uint64_t index);
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index c50fbb3f72a..ed5671abe5c 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -147,6 +147,7 @@ typedef struct CPUArchState {
     uint64_t t_cycle_count;
 #endif
     uint32_t next_PC;
+    uint32_t imprecise_exception;
     target_ulong new_value_usr;
 
     MemLog mem_log_stores[STORES_MAX];
diff --git a/hw/hexagon/hexagon_tlb.c b/hw/hexagon/hexagon_tlb.c
index b6d4aff389e..6539458f25f 100644
--- a/hw/hexagon/hexagon_tlb.c
+++ b/hw/hexagon/hexagon_tlb.c
@@ -319,15 +319,18 @@ bool hexagon_tlb_find_match(HexagonTLBState *tlb, uint32_t asid,
 }
 
 uint32_t hexagon_tlb_lookup(HexagonTLBState *tlb, uint32_t asid,
-                            uint32_t VA, int *cause_code)
+                            uint32_t VA, uint32_t *imprecise_exception,
+                            int *cause_code)
 {
     uint32_t not_found = 0x80000000;
     uint32_t idx = not_found;
 
+    *imprecise_exception = 0;
     for (uint32_t i = 0; i < tlb->num_entries; i++) {
         uint64_t entry = tlb->entries[i];
         if (hex_tlb_entry_match_noperm(entry, asid, VA)) {
             if (idx != not_found) {
+                *imprecise_exception = HEX_EVENT_IMPRECISE;
                 *cause_code = HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH;
                 break;
             }
diff --git a/target/hexagon/hex_mmu.c b/target/hexagon/hex_mmu.c
index 81d64a6146c..de108f709c0 100644
--- a/target/hexagon/hex_mmu.c
+++ b/target/hexagon/hex_mmu.c
@@ -86,9 +86,12 @@ uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA)
 {
     HexagonCPU *cpu = env_archcpu(env);
     uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr);
+    uint32_t imprecise_exception = 0;
     int cause_code = 0;
 
-    uint32_t result = hexagon_tlb_lookup(cpu->tlb, asid, VA, &cause_code);
+    uint32_t result = hexagon_tlb_lookup(cpu->tlb, asid, VA,
+                                         &imprecise_exception, &cause_code);
+    env->imprecise_exception = imprecise_exception;
     if (cause_code) {
         env->cause_code = cause_code;
     }
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
index 43c373ea2ee..d29c12e6481 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -69,6 +69,7 @@ void hexagon_cpu_do_interrupt(CPUState *cs)
 
 {
     CPUHexagonState *env = cpu_env(cs);
+    HexagonCPU *cpu = HEXAGON_CPU(cs);
     uint32_t ssr;
 
     BQL_LOCK_GUARD();
@@ -83,7 +84,6 @@ void hexagon_cpu_do_interrupt(CPUState *cs)
 
     ssr = env->t_sreg[HEX_SREG_SSR];
     if (GET_SSR_FIELD(SSR_EX, ssr) == 1) {
-        HexagonCPU *cpu = env_archcpu(env);
         if (cpu->globalregs) {
             hexagon_globalreg_write(cpu->globalregs, HEX_SREG_DIAG,
                                     env->cause_code, env->threadId);
@@ -243,8 +243,39 @@ void hexagon_cpu_do_interrupt(CPUState *cs)
         break;
 
     case HEX_EVENT_IMPRECISE:
-        qemu_log_mask(LOG_UNIMP,
-                "Imprecise exception: this case is not yet handled");
+        if (get_exe_mode(env) == HEX_EXE_MODE_WAIT) {
+            env->gpr[HEX_REG_PC] = env->wait_next_pc - 4;
+            clear_wait_mode(env);
+        }
+        switch (env->cause_code) {
+        case HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH:
+            hexagon_ssr_set_cause(env, env->cause_code);
+            set_addresses(env, 4, cs->exception_index);
+            if (cpu->globalregs) {
+                hexagon_globalreg_write(cpu->globalregs, HEX_SREG_DIAG,
+                    (0x4 << 4) | (env->t_sreg[HEX_SREG_HTID] & 0xF),
+                    env->threadId);
+            }
+            break;
+
+        case HEX_CAUSE_IMPRECISE_NMI:
+            hexagon_ssr_set_cause(env, env->cause_code);
+            set_addresses(env, 4, cs->exception_index);
+            if (cpu->globalregs) {
+                hexagon_globalreg_write(cpu->globalregs, HEX_SREG_DIAG,
+                    (0x3 << 4) | (env->t_sreg[HEX_SREG_HTID] & 0xF),
+                    env->threadId);
+            }
+            break;
+
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                    "Imprecise exception with unhandled cause 0x%x\n",
+                    env->cause_code);
+            hexagon_ssr_set_cause(env, env->cause_code);
+            set_addresses(env, 4, cs->exception_index);
+            break;
+        }
         break;
 
     default:
diff --git a/target/hexagon/machine.c b/target/hexagon/machine.c
index 2dd95466e7d..bf4646f4a8b 100644
--- a/target/hexagon/machine.c
+++ b/target/hexagon/machine.c
@@ -10,8 +10,8 @@
 
 const VMStateDescription vmstate_hexagon_cpu = {
     .name = "cpu",
-    .version_id = 1,
-    .minimum_version_id = 1,
+    .version_id = 2,
+    .minimum_version_id = 2,
     .fields = (const VMStateField[]) {
         VMSTATE_UINT32_ARRAY(env.gpr, HexagonCPU, TOTAL_PER_THREAD_REGS),
         VMSTATE_UINT32_ARRAY(env.pred, HexagonCPU, NUM_PREGS),
@@ -26,6 +26,7 @@ const VMStateDescription vmstate_hexagon_cpu = {
         VMSTATE_UINT32(env.cause_code, HexagonCPU),
         VMSTATE_UINT32(env.wait_next_pc, HexagonCPU),
         VMSTATE_UINT64(env.t_cycle_count, HexagonCPU),
+        VMSTATE_UINT32(env.imprecise_exception, HexagonCPU),
 
         VMSTATE_END_OF_LIST()
     },
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 5cfa60ca302..1d28f2db0fc 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -65,6 +65,7 @@ TCGv hex_llsc_val;
 TCGv_i64 hex_llsc_val_i64;
 #ifndef CONFIG_USER_ONLY
 TCGv_i64 hex_cycle_count;
+TCGv hex_imprecise_exception;
 #endif
 TCGv hex_vstore_addr[VSTORES_MAX];
 TCGv hex_vstore_size[VSTORES_MAX];
@@ -1051,6 +1052,28 @@ static void update_exec_counters(DisasContext *ctx)
     ctx->num_cycles += PCYCLES_PER_PACKET;
 }
 
+#ifndef CONFIG_USER_ONLY
+/*
+ * A tlbp instruction may detect multiple TLB matches and set a pending
+ * imprecise exception.  Raise it after the packet that ran the tlbp.
+ */
+static void check_imprecise_exception(Packet *pkt)
+{
+    for (int i = 0; i < pkt->num_insns; i++) {
+        if (pkt->insn[i].opcode == Y2_tlbp) {
+            TCGv PC = tcg_constant_tl(pkt->pc);
+            TCGLabel *label = gen_new_label();
+            tcg_gen_brcondi_tl(TCG_COND_EQ, hex_imprecise_exception,
+                               0, label);
+            gen_helper_raise_exception(tcg_env,
+                                       hex_imprecise_exception, PC);
+            gen_set_label(label);
+            return;
+        }
+    }
+}
+#endif
+
 static void gen_commit_packet(DisasContext *ctx)
 {
     /*
@@ -1150,6 +1173,10 @@ static void gen_commit_packet(DisasContext *ctx)
         ctx->pkt.vhist_insn->generate(ctx);
     }
 
+#ifndef CONFIG_USER_ONLY
+    check_imprecise_exception(&ctx->pkt);
+#endif
+
     if (ctx->pkt_ends_tb || ctx->base.is_jmp == DISAS_NORETURN) {
         gen_end_tb(ctx);
     }
@@ -1357,6 +1384,8 @@ void hexagon_translate_init(void)
 #ifndef CONFIG_USER_ONLY
     hex_cycle_count = tcg_global_mem_new_i64(tcg_env,
         offsetof(CPUHexagonState, t_cycle_count), "t_cycle_count");
+    hex_imprecise_exception = tcg_global_mem_new(tcg_env,
+        offsetof(CPUHexagonState, imprecise_exception), "imprecise_exception");
 #endif
     for (i = 0; i < STORES_MAX; i++) {
         snprintf(store_addr_names[i], NAME_LEN, "store_addr_%d", i);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 09/11] target/hexagon: implement direct-to-guest interrupt delivery
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (7 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 08/11] target/hexagon: raise imprecise exception on multi-TLB match Brian Cain
@ 2026-09-01 17:33 ` Brian Cain
  2026-09-01 18:26   ` Pierrick Bouvier
  2026-09-01 17:33 ` [PATCH v2 10/11] target/hexagon: fix iassign{r, w} to cover all threads Brian Cain via qemu development
  2026-09-01 17:33 ` [PATCH v2 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs} Brian Cain via qemu development
  10 siblings, 1 reply; 20+ messages in thread
From: Brian Cain @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

Interrupts 3 through 5 are routed to guest mode when CCR:GIE and the
matching CCR:VV bit are set.  Enter through GEVB rather than EVB,
record the pre-entry state in GSR and the return address in GELR, and
read the vector ID from the l2vic.

Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/reg_fields_def.h.inc |  7 +++
 target/hexagon/cpu.c                |  5 ++
 target/hexagon/hex_interrupts.c     | 91 ++++++++++++++++++++++++++---
 3 files changed, 96 insertions(+), 7 deletions(-)

diff --git a/target/hexagon/reg_fields_def.h.inc b/target/hexagon/reg_fields_def.h.inc
index d2c706d56b5..29497fbcc4d 100644
--- a/target/hexagon/reg_fields_def.h.inc
+++ b/target/hexagon/reg_fields_def.h.inc
@@ -136,6 +136,13 @@ DEF_REG_FIELD(CCR_VV1, 29, 1)
 DEF_REG_FIELD(CCR_VV2, 30, 1)
 DEF_REG_FIELD(CCR_VV3, 31, 1)
 
+/* GSR fields */
+DEF_REG_FIELD(GSR_CAUSE, 0, 16)
+DEF_REG_FIELD(GSR_CFI, 28, 1)
+DEF_REG_FIELD(GSR_SS, 29, 1)
+DEF_REG_FIELD(GSR_IE, 30, 1)
+DEF_REG_FIELD(GSR_UM, 31, 1)
+
 /* ISDB ST fields */
 DEF_REG_FIELD(ISDBST_WAITRUN, 24, 8)
 DEF_REG_FIELD(ISDBST_ONOFF, 16, 8)
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 0a677840bcb..efaf569c6f8 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -475,6 +475,11 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp)
         error_setg(errp, "hexagon cpu requires 'tlb' link property to be set");
         return;
     }
+    if (!HEXAGON_CPU(dev)->l2vic) {
+        error_setg(errp,
+                   "hexagon cpu requires 'l2vic' link property to be set");
+        return;
+    }
 #endif
 
     qemu_init_vcpu(cs);
diff --git a/target/hexagon/hex_interrupts.c b/target/hexagon/hex_interrupts.c
index 3534481da24..7dde1294b2e 100644
--- a/target/hexagon/hex_interrupts.c
+++ b/target/hexagon/hex_interrupts.c
@@ -11,6 +11,7 @@
 #include "cpu_helper.h"
 #include "exec/cpu-interrupt.h"
 #include "hex_interrupts.h"
+#include "hw/intc/hex-l2vic.h"
 #include "macros.h"
 #include "sys_macros.h"
 #include "system/cpus.h"
@@ -215,19 +216,79 @@ static void restore_state(CPUHexagonState *env, bool int_accepted)
     }
 }
 
+/*
+ * Direct-to-guest interrupts bypass the need for monitor-mode
+ * forwarding of interrupts into the guest OS.
+ */
+static bool int_should_dtg(CPUHexagonState *env, int int_num)
+{
+    uint32_t ccr = env->t_sreg[HEX_SREG_CCR];
+
+    switch (int_num) {
+    case 3:
+        if (!GET_FIELD(CCR_VV1, ccr)) {
+            return false;
+        }
+        break;
+    case 4:
+        if (!GET_FIELD(CCR_VV2, ccr)) {
+            return false;
+        }
+        break;
+    case 5:
+        if (!GET_FIELD(CCR_VV3, ccr)) {
+            return false;
+        }
+        break;
+    default:
+        return false;
+    }
+
+    return GET_FIELD(CCR_GIE, ccr);
+}
+
+static void guest_interrupt_entry(CPUHexagonState *env, uint32_t cause,
+                                  uint32_t event_pc)
+{
+    uint32_t old_ssr = env->t_sreg[HEX_SREG_SSR];
+    uint32_t new_ssr = old_ssr;
+    uint32_t ccr = env->t_sreg[HEX_SREG_CCR];
+    uint32_t gsr = 0;
+
+    gsr = deposit32(gsr, reg_field_info[GSR_CAUSE].offset,
+                    reg_field_info[GSR_CAUSE].width, cause);
+    gsr = deposit32(gsr, reg_field_info[GSR_SS].offset,
+                    reg_field_info[GSR_SS].width,
+                    GET_SSR_FIELD(SSR_SS, old_ssr));
+    gsr = deposit32(gsr, reg_field_info[GSR_UM].offset,
+                    reg_field_info[GSR_UM].width,
+                    !GET_SSR_FIELD(SSR_GM, old_ssr));
+    gsr = deposit32(gsr, reg_field_info[GSR_IE].offset,
+                    reg_field_info[GSR_IE].width,
+                    GET_FIELD(CCR_GIE, ccr));
+    env->greg[HEX_GREG_GSR] = gsr;
+
+    fSET_FIELD(new_ssr, SSR_SS, 0);
+    fSET_FIELD(new_ssr, SSR_GM, 1);
+    env->t_sreg[HEX_SREG_SSR] = new_ssr;
+    hexagon_modify_ssr(env, new_ssr, old_ssr);
+
+    SET_SYSTEM_FIELD(env, HEX_SREG_CCR, CCR_GIE, 0);
+    env->greg[HEX_GREG_GELR] = event_pc;
+    env->gpr[HEX_REG_PC] = env->t_sreg[HEX_SREG_GEVB] |
+                           (HEX_EVENT_INT0 << 2);
+}
+
 static void hex_accept_int(CPUHexagonState *env, int int_num)
 {
     CPUState *cs = env_cpu(env);
     HexagonCPU *cpu = env_archcpu(env);
-    uint32_t evb =
-        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB,
-                               env->threadId);
     const int exe_mode = get_exe_mode(env);
     const bool in_wait_mode = exe_mode == HEX_EXE_MODE_WAIT;
+    uint32_t elr;
 
     set_ipend_bit(env, int_num, 0);
     set_iad_bit(env, int_num, 1);
-    set_ssr_ex_cause(env, 1, HEX_CAUSE_INT0 | int_num);
     cs->exception_index = HEX_EVENT_INT0 + int_num;
     env->cause_code = HEX_EVENT_INT0 + int_num;
     clear_pending_locks(env);
@@ -235,15 +296,31 @@ static void hex_accept_int(CPUHexagonState *env, int int_num)
         qemu_log_mask(CPU_LOG_INT,
             "%s: thread " TARGET_FMT_ld " resuming, exiting WAIT mode\n",
             __func__, env->threadId);
-        set_elr(env, env->wait_next_pc);
+        elr = env->wait_next_pc;
         clear_wait_mode(env);
         cs->halted = false;
     } else if (env->k0_lock_state == HEX_LOCK_WAITING) {
         g_assert_not_reached();
     } else {
-        set_elr(env, env->gpr[HEX_REG_PC]);
+        elr = env->gpr[HEX_REG_PC];
+    }
+
+    if (int_should_dtg(env, int_num)) {
+        int vic_group = int_num - 2;
+        uint32_t vid_packed = l2vic_read_vid(cpu->l2vic, vic_group / 2);
+        uint32_t vid = extract32(vid_packed,
+                                 (vic_group & 1) ? 16 : 0, 16);
+
+        guest_interrupt_entry(env, vid, elr);
+    } else {
+        uint32_t evb =
+            hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB,
+                                   env->threadId);
+
+        set_ssr_ex_cause(env, 1, HEX_CAUSE_INT0 | int_num);
+        set_elr(env, elr);
+        env->gpr[HEX_REG_PC] = evb | (cs->exception_index << 2);
     }
-    env->gpr[HEX_REG_PC] = evb | (cs->exception_index << 2);
     if (get_ipend(env) == 0) {
         restore_state(env, true);
     }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 10/11] target/hexagon: fix iassign{r, w} to cover all threads
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (8 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
@ 2026-09-01 17:33 ` Brian Cain via qemu development
  2026-09-01 17:33 ` [PATCH v2 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs} Brian Cain via qemu development
  10 siblings, 0 replies; 20+ messages in thread
From: Brian Cain via qemu development @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

iassignw/iassignr were filtering CPU_FOREACH by MODECTL_E, the
thread-enabled mask. That mask reflects whether a thread has been
started, not whether it exists, so IMASK writes/reads never reached
threads that hadn't been started yet, leaving their IMASK stale or
unreadable. Drop the MODECTL_E filtering and operate on every thread.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/op_helper.c | 42 +++++++++-----------------------------
 1 file changed, 10 insertions(+), 32 deletions(-)

diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index df31ed2488a..e19c3c91085 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1637,61 +1637,39 @@ void HELPER(cswi)(CPUHexagonState *env, uint32_t mask)
 
 void HELPER(iassignw)(CPUHexagonState *env, uint32_t src)
 {
-    uint32_t modectl;
-    uint32_t thread_enabled_mask;
     CPUState *cpu;
-    HexagonCPU *hex_cpu;
 
     BQL_LOCK_GUARD();
-    hex_cpu = env_archcpu(env);
-    modectl = hex_cpu->globalregs ?
-        hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL,
-                               env->threadId) : 0;
-    thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
 
     CPU_FOREACH(cpu) {
         CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env);
-        uint32_t thread_id_mask = 0x1 << thread_env->threadId;
-        if (thread_enabled_mask & thread_id_mask) {
-            uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK];
-            uint32_t intbitpos = (src >> 16) & 0xF;
-            uint32_t val = (src >> thread_env->threadId) & 0x1;
-            imask = deposit32(imask, intbitpos, 1, val);
-            thread_env->t_sreg[HEX_SREG_IMASK] = imask;
+        uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK];
+        uint32_t intbitpos = (src >> 16) & 0xF;
+        uint32_t val = (src >> thread_env->threadId) & 0x1;
+        imask = deposit32(imask, intbitpos, 1, val);
+        thread_env->t_sreg[HEX_SREG_IMASK] = imask;
 
-            qemu_log_mask(CPU_LOG_INT, "%s: thread " TARGET_FMT_ld
-               ", new imask 0x%" PRIx32 "\n", __func__,
-               thread_env->threadId, imask);
-        }
+        qemu_log_mask(CPU_LOG_INT, "%s: thread " TARGET_FMT_ld
+           ", new imask 0x%" PRIx32 "\n", __func__,
+           thread_env->threadId, imask);
     }
     hex_interrupt_update(env);
 }
 
 uint32_t HELPER(iassignr)(CPUHexagonState *env, uint32_t src)
 {
-    uint32_t modectl;
-    uint32_t thread_enabled_mask;
     uint32_t intbitpos;
     uint32_t dest_reg;
     CPUState *cpu;
-    HexagonCPU *hex_cpu;
 
     BQL_LOCK_GUARD();
-    hex_cpu = env_archcpu(env);
-    modectl = hex_cpu->globalregs ?
-        hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL,
-                               env->threadId) : 0;
-    thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
     /* src fields are in same position as modectl, but mean different things */
     intbitpos = GET_FIELD(MODECTL_W, src);
     dest_reg = 0;
     CPU_FOREACH(cpu) {
         CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env);
-        uint32_t thread_id_mask = 0x1 << thread_env->threadId;
-        if (thread_enabled_mask & thread_id_mask) {
-            uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK];
-            dest_reg |= ((imask >> intbitpos) & 0x1) << thread_env->threadId;
-        }
+        uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK];
+        dest_reg |= ((imask >> intbitpos) & 0x1) << thread_env->threadId;
     }
 
     return dest_reg;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs}
  2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
                   ` (9 preceding siblings ...)
  2026-09-01 17:33 ` [PATCH v2 10/11] target/hexagon: fix iassign{r, w} to cover all threads Brian Cain via qemu development
@ 2026-09-01 17:33 ` Brian Cain via qemu development
  10 siblings, 0 replies; 20+ messages in thread
From: Brian Cain via qemu development @ 2026-09-01 17:33 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pierrick Bouvier, Brian Cain, Laurent Vivier, Helge Deller

test_interrupts also now passes in full following the iassign{r,w}
fix, so enable it alongside the other arch tests.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 tests/functional/hexagon/test_arch_tests.py | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/tests/functional/hexagon/test_arch_tests.py b/tests/functional/hexagon/test_arch_tests.py
index 0834398c3b1..8e71386c186 100755
--- a/tests/functional/hexagon/test_arch_tests.py
+++ b/tests/functional/hexagon/test_arch_tests.py
@@ -21,8 +21,8 @@ class ArchTestsUart(QemuSystemTest):
 
     ASSET_TARBALL = Asset(
         "https://github.com/qualcomm/qemu-hexagon-testing/releases/"
-        "download/v0.2.5/arch_tests_uart.tar.gz",
-        "edb4f37b877a3a72a0e10920477458a43b40045d34398fee8cb763fefd342f4f",
+        "download/v0.2.12/arch_tests_uart.tar.gz",
+        "871a339bf78cac0ebaf1b2509bfcd5b249ad8190be33e0cf848283b2f6915323",
     )
 
     def run_uart_test(self, test_name: str,
@@ -58,6 +58,10 @@ def test_int_steering(self) -> None:
         """
         self.run_uart_test("test_int_steering")
 
+    def test_interrupts(self) -> None:
+        """Tests interrupt delivery."""
+        self.run_uart_test("test_interrupts")
+
     def test_cache(self) -> None:
         """Tests cache operations: dckill/ickill, l2kill, dczeroa,
         dccleaninva, cache disable/enable, barriers, and dcinva/dccleana.
@@ -70,6 +74,10 @@ def test_l2vic(self) -> None:
         """
         self.run_uart_test("test_l2vic")
 
+    def test_sys_regs(self) -> None:
+        """Tests system registers."""
+        self.run_uart_test("test_sys_regs")
+
     def test_threads(self) -> None:
         """Tests hardware thread management: start/stop, MODECTL state,
         per-thread HTID, shared memory, wait/resume, STID priority, and
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 04/11] target/hexagon: guard writes to unimplemented guest registers
  2026-09-01 17:33 ` [PATCH v2 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
@ 2026-09-01 18:25   ` Pierrick Bouvier
  0 siblings, 0 replies; 20+ messages in thread
From: Pierrick Bouvier @ 2026-09-01 18:25 UTC (permalink / raw)
  To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller

On 9/1/2026 10:33 AM, Brian Cain wrote:
> Guest register G4 and above have PMU-ish features that are yet
> to be implemented.  We'll skip these when we encounter this code
> rather than fault on an assertion.
> 
> Note that the generated call to greg_writable() has the side-effect
> of doing a LOG_UNIMP for these register accesses.
> 
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>  target/hexagon/translate.h   |  4 +++-
>  target/hexagon/hex_common.py | 18 +++++++++++++++++-
>  2 files changed, 20 insertions(+), 2 deletions(-)
> 

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 09/11] target/hexagon: implement direct-to-guest interrupt delivery
  2026-09-01 17:33 ` [PATCH v2 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
@ 2026-09-01 18:26   ` Pierrick Bouvier
  0 siblings, 0 replies; 20+ messages in thread
From: Pierrick Bouvier @ 2026-09-01 18:26 UTC (permalink / raw)
  To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller

On 9/1/2026 10:33 AM, Brian Cain wrote:
> Interrupts 3 through 5 are routed to guest mode when CCR:GIE and the
> matching CCR:VV bit are set.  Enter through GEVB rather than EVB,
> record the pre-entry state in GSR and the return address in GELR, and
> read the vector ID from the l2vic.
> 
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>  target/hexagon/reg_fields_def.h.inc |  7 +++
>  target/hexagon/cpu.c                |  5 ++
>  target/hexagon/hex_interrupts.c     | 91 ++++++++++++++++++++++++++---
>  3 files changed, 96 insertions(+), 7 deletions(-)
> 

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE
  2026-09-01 17:33 ` [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
@ 2026-09-02 21:16   ` Philippe Mathieu-Daudé
  2026-09-03 19:36     ` Brian Cain
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-02 21:16 UTC (permalink / raw)
  To: Brian Cain, qemu-devel; +Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller

Hi Brian,

On 1/9/26 19:33, Brian Cain wrote:
> GPCYCLELO/GPCYCLEHI read as zero unless the cycle counter is enabled
> (SSR:CE), matching the hardware.
> 
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>   target/hexagon/cpu.c       | 7 +++++--
>   target/hexagon/op_helper.c | 9 ++-------
>   2 files changed, 7 insertions(+), 9 deletions(-)


> diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
> index 23894ff3d28..2cea1927263 100644
> --- a/target/hexagon/op_helper.c
> +++ b/target/hexagon/op_helper.c
> @@ -1912,13 +1912,8 @@ uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg)
>           return (uint64_t)(env->greg[reg]) |
>                  (((uint64_t)(env->greg[reg + 1])) << 32);
>       }
> -    switch (reg) {
> -    case HEX_GREG_GPCYCLELO:
> -        return hexagon_get_sys_pcycle_count(env);
> -    default:
> -        return (uint64_t)hexagon_greg_read(env, reg) |
> -               ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
> -    }
> +    return (uint64_t)hexagon_greg_read(env, reg) |
> +           ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
>   }

Just curious, does greg_read_pair() work with impair %reg?


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer
  2026-09-01 17:33 ` [PATCH v2 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
@ 2026-09-02 21:21   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-02 21:21 UTC (permalink / raw)
  To: Brian Cain, qemu-devel; +Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller

On 1/9/26 19:33, Brian Cain wrote:
> In system mode, route accesses to the qtimer-backed global
> TIMERLO/TIMERHI so guest reads see a live, monotonically increasing
> timer.
> 
> In user mode we derive it from QEMU_CLOCK_VIRTUAL.
> 
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>   target/hexagon/helper.h     |  4 ++++
>   target/hexagon/genptr.c     | 29 +++++++++++++++++++++++++++++
>   target/hexagon/op_helper.c  | 21 +++++++++++++++++++++
>   tests/tcg/hexagon/reg_mut.c | 11 ++++++++---
>   4 files changed, 62 insertions(+), 3 deletions(-)


> diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
> index 2cea1927263..df31ed2488a 100644
> --- a/target/hexagon/op_helper.c
> +++ b/target/hexagon/op_helper.c
> @@ -37,6 +37,9 @@
>   #include "cpu_helper.h"
>   #include "tcg/tcg-gvec-desc.h"
>   #include "translate.h"
> +#ifdef CONFIG_USER_ONLY
> +#include "qemu/timer.h"
> +#endif
>   #ifndef CONFIG_USER_ONLY
>   #include "hw/hexagon/hexagon_globalreg.h"
>   #include "hex_mmu.h"
> @@ -46,6 +49,24 @@
>   #include "hexswi.h"
>   #endif
>   
> +#ifdef CONFIG_USER_ONLY
> +/*
> + * User mode has no qtimer device backing TIMERLO/TIMERHI, so derive the
> + * user timer directly from the virtual clock -- the same clock the qtimer
> + * counts -- at the qtimer's default 19.2MHz tick rate, masked to the
> + * qtimer's counter width.
> + */
> +#define HEX_UTIMER_FREQ_HZ  19200000ULL
> +#define HEX_UTIMER_CNT_MASK 0x00ffffffffffffffULL
> +
> +uint64_t HELPER(utimer)(void)
> +{
> +    return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
> +                    HEX_UTIMER_FREQ_HZ, NANOSECONDS_PER_SECOND) &
> +           HEX_UTIMER_CNT_MASK;
> +}
> +#endif

Long term it could be easier to maintain a new user-specific file,
adding it to hexagon_user_ss[] then target_user_arch (less #ifdefs).


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 03/11] hw/intc: clear pending bit on l2vic de-assertion
  2026-09-01 17:33 ` [PATCH v2 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
@ 2026-09-02 21:30   ` Philippe Mathieu-Daudé
  2026-09-03 19:33     ` Brian Cain
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-02 21:30 UTC (permalink / raw)
  To: Brian Cain, qemu-devel
  Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller, Sid Manning

On 1/9/26 19:33, Brian Cain wrote:
> From: Sid Manning <sidneym@quicinc.com>
> 
> l2vic_set_irq() clears a level-triggered source's pending bit on
> de-assertion, so a source that drops before it is latched into
> int_status is not spuriously re-delivered on the next ciad.
> Edge-triggered sources (int_type set) keep the pulse semantics they
> already have via SOFT_INT/set_irq(level=1).
> 
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>   hw/intc/hex-l2vic.c | 2 ++
>   1 file changed, 2 insertions(+)
> 
> diff --git a/hw/intc/hex-l2vic.c b/hw/intc/hex-l2vic.c
> index f07ec850d49..a986f0bdf35 100644
> --- a/hw/intc/hex-l2vic.c
> +++ b/hw/intc/hex-l2vic.c
> @@ -299,6 +299,8 @@ static void l2vic_set_irq(void *opaque, int irq, int level)
>   
>       if (level) {
>           set_bit32(irq, s->int_pending);
> +    } else if (!test_bit32(irq, s->int_type)) {
> +        clear_bit32(irq, s->int_pending);
>       }
>       l2vic_update(s, irq);

I'm confused with l2vic_update() logic which seems to
duplicate a bit.

>   }

hw/intc/hex-l2vic.c-95-    /* Edge or Level interrupt */
hw/intc/hex-l2vic.c:96:    DECLARE_BITMAP32(int_type, L2VIC_INTERRUPT_MAX);

Not obvious, so 0:edge and 1:level. Ah no...

hw/intc/hex-l2vic.c-268-         * Only auto-disable for edge-triggered 
interrupts (type=1).

So 1:edge and 0:level?

Tiny helpers can help to make the code more readable:

static bool edge_triggered_irq(HexL2VICState *s, int irq)
{
     return test_bit32(irq, s->int_type) == 1;
}


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 03/11] hw/intc: clear pending bit on l2vic de-assertion
  2026-09-02 21:30   ` Philippe Mathieu-Daudé
@ 2026-09-03 19:33     ` Brian Cain
  0 siblings, 0 replies; 20+ messages in thread
From: Brian Cain @ 2026-09-03 19:33 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, qemu-devel
  Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller, Sid Manning


On 9/2/2026 4:30 PM, Philippe Mathieu-Daudé wrote:
> On 1/9/26 19:33, Brian Cain wrote:
>> From: Sid Manning <sidneym@quicinc.com>
>>
>> l2vic_set_irq() clears a level-triggered source's pending bit on
>> de-assertion, so a source that drops before it is latched into
>> int_status is not spuriously re-delivered on the next ciad.
>> Edge-triggered sources (int_type set) keep the pulse semantics they
>> already have via SOFT_INT/set_irq(level=1).
>>
>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
>> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
>> ---
>>   hw/intc/hex-l2vic.c | 2 ++
>>   1 file changed, 2 insertions(+)
>>
>> diff --git a/hw/intc/hex-l2vic.c b/hw/intc/hex-l2vic.c
>> index f07ec850d49..a986f0bdf35 100644
>> --- a/hw/intc/hex-l2vic.c
>> +++ b/hw/intc/hex-l2vic.c
>> @@ -299,6 +299,8 @@ static void l2vic_set_irq(void *opaque, int irq, 
>> int level)
>>         if (level) {
>>           set_bit32(irq, s->int_pending);
>> +    } else if (!test_bit32(irq, s->int_type)) {
>> +        clear_bit32(irq, s->int_pending);
>>       }
>>       l2vic_update(s, irq);
>
> I'm confused with l2vic_update() logic which seems to
> duplicate a bit.
>
>>   }
>
> hw/intc/hex-l2vic.c-95-    /* Edge or Level interrupt */
> hw/intc/hex-l2vic.c:96:    DECLARE_BITMAP32(int_type, 
> L2VIC_INTERRUPT_MAX);
>
> Not obvious, so 0:edge and 1:level. Ah no...
>
> hw/intc/hex-l2vic.c-268-         * Only auto-disable for 
> edge-triggered interrupts (type=1).
>
> So 1:edge and 0:level?
>
> Tiny helpers can help to make the code more readable:


Agreed! I like this change.  Sent a follow up.


>
> static bool edge_triggered_irq(HexL2VICState *s, int irq)
> {
>     return test_bit32(irq, s->int_type) == 1;
> }


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE
  2026-09-02 21:16   ` Philippe Mathieu-Daudé
@ 2026-09-03 19:36     ` Brian Cain
  2026-09-04 13:19       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Brian Cain @ 2026-09-03 19:36 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, qemu-devel
  Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller


On 9/2/2026 4:16 PM, Philippe Mathieu-Daudé wrote:
> Hi Brian,
>
> On 1/9/26 19:33, Brian Cain wrote:
>> GPCYCLELO/GPCYCLEHI read as zero unless the cycle counter is enabled
>> (SSR:CE), matching the hardware.
>>
>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
>> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
>> ---
>>   target/hexagon/cpu.c       | 7 +++++--
>>   target/hexagon/op_helper.c | 9 ++-------
>>   2 files changed, 7 insertions(+), 9 deletions(-)
>
>
>> diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
>> index 23894ff3d28..2cea1927263 100644
>> --- a/target/hexagon/op_helper.c
>> +++ b/target/hexagon/op_helper.c
>> @@ -1912,13 +1912,8 @@ uint64_t 
>> HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg)
>>           return (uint64_t)(env->greg[reg]) |
>>                  (((uint64_t)(env->greg[reg + 1])) << 32);
>>       }
>> -    switch (reg) {
>> -    case HEX_GREG_GPCYCLELO:
>> -        return hexagon_get_sys_pcycle_count(env);
>> -    default:
>> -        return (uint64_t)hexagon_greg_read(env, reg) |
>> -               ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
>> -    }
>> +    return (uint64_t)hexagon_greg_read(env, reg) |
>> +           ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
>>   }
>
> Just curious, does greg_read_pair() work with impair %reg?


Not sure if I understand "impair %reg"?

But - if this refers to the odd-value pairs, then no - they don't.  The 
guest reg encoding only has the "g{odd}:{even}" designated by the even 
reg's value.  Sent a follow up patch to assert this in greg_read_pair().

-Brian



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE
  2026-09-03 19:36     ` Brian Cain
@ 2026-09-04 13:19       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-04 13:19 UTC (permalink / raw)
  To: Brian Cain, qemu-devel; +Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller

On 3/9/26 21:36, Brian Cain wrote:
> 
> On 9/2/2026 4:16 PM, Philippe Mathieu-Daudé wrote:
>> Hi Brian,
>>
>> On 1/9/26 19:33, Brian Cain wrote:
>>> GPCYCLELO/GPCYCLEHI read as zero unless the cycle counter is enabled
>>> (SSR:CE), matching the hardware.
>>>
>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
>>> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
>>> ---
>>>   target/hexagon/cpu.c       | 7 +++++--
>>>   target/hexagon/op_helper.c | 9 ++-------
>>>   2 files changed, 7 insertions(+), 9 deletions(-)
>>
>>
>>> diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
>>> index 23894ff3d28..2cea1927263 100644
>>> --- a/target/hexagon/op_helper.c
>>> +++ b/target/hexagon/op_helper.c
>>> @@ -1912,13 +1912,8 @@ uint64_t HELPER(greg_read_pair) 
>>> (CPUHexagonState *env, uint32_t reg)
>>>           return (uint64_t)(env->greg[reg]) |
>>>                  (((uint64_t)(env->greg[reg + 1])) << 32);
>>>       }
>>> -    switch (reg) {
>>> -    case HEX_GREG_GPCYCLELO:
>>> -        return hexagon_get_sys_pcycle_count(env);
>>> -    default:
>>> -        return (uint64_t)hexagon_greg_read(env, reg) |
>>> -               ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
>>> -    }
>>> +    return (uint64_t)hexagon_greg_read(env, reg) |
>>> +           ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
>>>   }
>>
>> Just curious, does greg_read_pair() work with impair %reg?
> 
> 
> Not sure if I understand "impair %reg"?
> 
> But - if this refers to the odd-value pairs,

Yes I meant 'odd' ;)

> then no - they don't.  The 
> guest reg encoding only has the "g{odd}:{even}" designated by the even 
> reg's value.  Sent a follow up patch to assert this in greg_read_pair().
> 
> -Brian
> 
> 



^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2026-09-04 13:20 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-01 17:33 [PATCH v2 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
2026-09-01 17:33 ` [PATCH v2 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
2026-09-01 17:33 ` [PATCH v2 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL Brian Cain
2026-09-01 17:33 ` [PATCH v2 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
2026-09-02 21:30   ` Philippe Mathieu-Daudé
2026-09-03 19:33     ` Brian Cain
2026-09-01 17:33 ` [PATCH v2 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
2026-09-01 18:25   ` Pierrick Bouvier
2026-09-01 17:33 ` [PATCH v2 05/11] target/hexagon: take BQL when reading the system pcycle count Brian Cain
2026-09-01 17:33 ` [PATCH v2 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
2026-09-02 21:16   ` Philippe Mathieu-Daudé
2026-09-03 19:36     ` Brian Cain
2026-09-04 13:19       ` Philippe Mathieu-Daudé
2026-09-01 17:33 ` [PATCH v2 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
2026-09-02 21:21   ` Philippe Mathieu-Daudé
2026-09-01 17:33 ` [PATCH v2 08/11] target/hexagon: raise imprecise exception on multi-TLB match Brian Cain
2026-09-01 17:33 ` [PATCH v2 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
2026-09-01 18:26   ` Pierrick Bouvier
2026-09-01 17:33 ` [PATCH v2 10/11] target/hexagon: fix iassign{r, w} to cover all threads Brian Cain via qemu development
2026-09-01 17:33 ` [PATCH v2 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs} Brian Cain via qemu development

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.