* [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes
@ 2026-08-19 1:31 Brian Cain
2026-08-19 1:31 ` [PATCH 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
` (10 more replies)
0 siblings, 11 replies; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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
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 | 9 ++-
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 | 87 +++++++++++++++++++--
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, 392 insertions(+), 108 deletions(-)
create mode 100644 tests/tcg/hexagon/privileged-insn.c
--
2.34.1
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 01/11] target/hexagon: align exceptions for user/sysemu
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:40 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL Brian Cain
` (9 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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.
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] 26+ messages in thread
* [PATCH 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
2026-08-19 1:31 ` [PATCH 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:41 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
` (8 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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.
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] 26+ messages in thread
* [PATCH 03/11] hw/intc: clear pending bit on l2vic de-assertion
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
2026-08-19 1:31 ` [PATCH 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
2026-08-19 1:31 ` [PATCH 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:41 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
` (7 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel
Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier,
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).
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] 26+ messages in thread
* [PATCH 04/11] target/hexagon: guard writes to unimplemented guest registers
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (2 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:44 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 05/11] target/hexagon: take BQL when reading the system pcycle count Brian Cain
` (6 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
Gate guest-register writes on greg_writable() in the generated code so
writes to gregs above G3 are dropped instead of dereferencing an
unallocated TCG temp.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
target/hexagon/translate.h | 7 ++++---
target/hexagon/hex_common.py | 18 +++++++++++++++++-
2 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
index 00de2b0d2ec..05425d92b29 100644
--- a/target/hexagon/translate.h
+++ b/target/hexagon/translate.h
@@ -97,9 +97,10 @@ 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);
- ctx->greg_log[ctx->greg_log_idx] = rnum;
- ctx->greg_log_idx++;
+ if (rnum <= HEX_GREG_G3) {
+ ctx->greg_log[ctx->greg_log_idx] = rnum;
+ ctx->greg_log_idx++;
+ }
}
static inline void ctx_log_greg_write_pair(DisasContext *ctx, int rnum)
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] 26+ messages in thread
* [PATCH 05/11] target/hexagon: take BQL when reading the system pcycle count
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (3 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:45 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
` (5 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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.
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] 26+ messages in thread
* [PATCH 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (4 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 05/11] target/hexagon: take BQL when reading the system pcycle count Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:46 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
` (4 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
GPCYCLELO/GPCYCLEHI read as zero unless the cycle counter is enabled
(SSR:CE), matching the hardware.
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] 26+ messages in thread
* [PATCH 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (5 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:48 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 08/11] target/hexagon: raise imprecise exception on multi-TLB match Brian Cain
` (3 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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.
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] 26+ messages in thread
* [PATCH 08/11] target/hexagon: raise imprecise exception on multi-TLB match
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (6 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:54 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
` (2 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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.
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] 26+ messages in thread
* [PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (7 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 08/11] target/hexagon: raise imprecise exception on multi-TLB match Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:57 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 10/11] target/hexagon: fix iassign{r,w} to cover all threads Brian Cain
2026-08-19 1:31 ` [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs} Brian Cain via qemu development
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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 | 87 ++++++++++++++++++++++++++---
3 files changed, 92 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..ea1ba0903dd 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,75 @@ static void restore_state(CPUHexagonState *env, bool int_accepted)
}
}
+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 +292,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] 26+ messages in thread
* [PATCH 10/11] target/hexagon: fix iassign{r,w} to cover all threads
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (8 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
@ 2026-08-19 1:31 ` Brian Cain
2026-08-20 18:59 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs} Brian Cain via qemu development
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
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.
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] 26+ messages in thread
* [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs}
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
` (9 preceding siblings ...)
2026-08-19 1:31 ` [PATCH 10/11] target/hexagon: fix iassign{r,w} to cover all threads Brian Cain
@ 2026-08-19 1:31 ` Brian Cain via qemu development
2026-08-20 18:59 ` [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts,sys_regs} Pierrick Bouvier
10 siblings, 1 reply; 26+ messages in thread
From: Brian Cain via qemu development @ 2026-08-19 1:31 UTC (permalink / raw)
To: qemu-devel; +Cc: Laurent Vivier, Brian Cain, Helge Deller, Pierrick Bouvier
test_interrupts also now passes in full following the iassign{r,w}
fix, so enable it alongside the other arch tests.
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] 26+ messages in thread
* Re: [PATCH 01/11] target/hexagon: align exceptions for user/sysemu
2026-08-19 1:31 ` [PATCH 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
@ 2026-08-20 18:40 ` Pierrick Bouvier
2026-08-20 19:02 ` Brian Cain
0 siblings, 1 reply; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:40 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> 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.
>
> 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;
>
Shouldn't this be part of CPUState?
What if multiple cpus trigger an exception at the same time?
> 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
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL
2026-08-19 1:31 ` [PATCH 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL Brian Cain
@ 2026-08-20 18:41 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:41 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> 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.
>
> 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
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 03/11] hw/intc: clear pending bit on l2vic de-assertion
2026-08-19 1:31 ` [PATCH 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
@ 2026-08-20 18:41 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:41 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller, Sid Manning
On 8/18/2026 6:31 PM, 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).
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> hw/intc/hex-l2vic.c | 2 ++
> 1 file changed, 2 insertions(+)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 04/11] target/hexagon: guard writes to unimplemented guest registers
2026-08-19 1:31 ` [PATCH 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
@ 2026-08-20 18:44 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:44 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> Gate guest-register writes on greg_writable() in the generated code so
> writes to gregs above G3 are dropped instead of dereferencing an
> unallocated TCG temp.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> target/hexagon/translate.h | 7 ++++---
> target/hexagon/hex_common.py | 18 +++++++++++++++++-
> 2 files changed, 21 insertions(+), 4 deletions(-)
>
> diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
> index 00de2b0d2ec..05425d92b29 100644
> --- a/target/hexagon/translate.h
> +++ b/target/hexagon/translate.h
> @@ -97,9 +97,10 @@ 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);
> - ctx->greg_log[ctx->greg_log_idx] = rnum;
> - ctx->greg_log_idx++;
> + if (rnum <= HEX_GREG_G3) {
> + ctx->greg_log[ctx->greg_log_idx] = rnum;
> + ctx->greg_log_idx++;
> + }
> }
>
I'm not sure how this change is related to what is given in written
message. The functional change here is that we'll ignore write, instead
of asserting. Is that still expected to have a write on a wrong register
anyway?
If not, maybe we should at least log a guest_error instead of silently
ignoring it.
> static inline void ctx_log_greg_write_pair(DisasContext *ctx, int rnum)
> 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});
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 05/11] target/hexagon: take BQL when reading the system pcycle count
2026-08-19 1:31 ` [PATCH 05/11] target/hexagon: take BQL when reading the system pcycle count Brian Cain
@ 2026-08-20 18:45 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:45 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> 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.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> target/hexagon/cpu_helper.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE
2026-08-19 1:31 ` [PATCH 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
@ 2026-08-20 18:46 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:46 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> GPCYCLELO/GPCYCLEHI read as zero unless the cycle counter is enabled
> (SSR:CE), matching the hardware.
>
> 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(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer
2026-08-19 1:31 ` [PATCH 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
@ 2026-08-20 18:48 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:48 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, 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.
>
> 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],
Out of the scope of this patch, but seems like we could use a switch
instead of if/else ladder.
> 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);
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 08/11] target/hexagon: raise imprecise exception on multi-TLB match
2026-08-19 1:31 ` [PATCH 08/11] target/hexagon: raise imprecise exception on multi-TLB match Brian Cain
@ 2026-08-20 18:54 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:54 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> 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.
>
> 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(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery
2026-08-19 1:31 ` [PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
@ 2026-08-20 18:57 ` Pierrick Bouvier
2026-08-20 21:59 ` Brian Cain
0 siblings, 1 reply; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:57 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, 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 | 87 ++++++++++++++++++++++++++---
> 3 files changed, 92 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..ea1ba0903dd 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,75 @@ static void restore_state(CPUHexagonState *env, bool int_accepted)
> }
> }
>
> +static bool int_should_dtg(CPUHexagonState *env, int int_num)
> +{
What does dtg means?
> + 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 +292,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);
> }
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 10/11] target/hexagon: fix iassign{r,w} to cover all threads
2026-08-19 1:31 ` [PATCH 10/11] target/hexagon: fix iassign{r,w} to cover all threads Brian Cain
@ 2026-08-20 18:59 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:59 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> 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.
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
> target/hexagon/op_helper.c | 42 +++++++++-----------------------------
> 1 file changed, 10 insertions(+), 32 deletions(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts,sys_regs}
2026-08-19 1:31 ` [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs} Brian Cain via qemu development
@ 2026-08-20 18:59 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 18:59 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/18/2026 6:31 PM, Brian Cain wrote:
> test_interrupts also now passes in full following the iassign{r,w}
> fix, so enable it alongside the other arch tests.
>
> 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(-)
>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 01/11] target/hexagon: align exceptions for user/sysemu
2026-08-20 18:40 ` Pierrick Bouvier
@ 2026-08-20 19:02 ` Brian Cain
2026-08-20 19:32 ` Pierrick Bouvier
0 siblings, 1 reply; 26+ messages in thread
From: Brian Cain @ 2026-08-20 19:02 UTC (permalink / raw)
To: Pierrick Bouvier, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/20/2026 1:40 PM, Pierrick Bouvier wrote:
> On 8/18/2026 6:31 PM, Brian Cain wrote:
>> 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.
>>
>> 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;
>>
> Shouldn't this be part of CPUState?
> What if multiple cpus trigger an exception at the same time?
The cause_code is part of CPUState. This TCGv is a reference to that
state member for use with translation. We take advantage of the
single-threaded nature of translation with all of these file-global TCGv
values.
>
>> 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
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 01/11] target/hexagon: align exceptions for user/sysemu
2026-08-20 19:02 ` Brian Cain
@ 2026-08-20 19:32 ` Pierrick Bouvier
0 siblings, 0 replies; 26+ messages in thread
From: Pierrick Bouvier @ 2026-08-20 19:32 UTC (permalink / raw)
To: Brian Cain, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/20/2026 12:02 PM, Brian Cain wrote:
>
> On 8/20/2026 1:40 PM, Pierrick Bouvier wrote:
>> On 8/18/2026 6:31 PM, Brian Cain wrote:
>>> 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.
>>>
>>> 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;
>>>
>> Shouldn't this be part of CPUState?
>> What if multiple cpus trigger an exception at the same time?
>
> The cause_code is part of CPUState. This TCGv is a reference to that
> state member for use with translation. We take advantage of the single-
> threaded nature of translation with all of these file-global TCGv values.
>
>
>>
>>> 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
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery
2026-08-20 18:57 ` Pierrick Bouvier
@ 2026-08-20 21:59 ` Brian Cain
0 siblings, 0 replies; 26+ messages in thread
From: Brian Cain @ 2026-08-20 21:59 UTC (permalink / raw)
To: Pierrick Bouvier, qemu-devel; +Cc: Laurent Vivier, Helge Deller
On 8/20/2026 1:57 PM, Pierrick Bouvier wrote:
> On 8/18/2026 6:31 PM, 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 | 87 ++++++++++++++++++++++++++---
>> 3 files changed, 92 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..ea1ba0903dd 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,75 @@ static void restore_state(CPUHexagonState *env, bool int_accepted)
>> }
>> }
>>
>> +static bool int_should_dtg(CPUHexagonState *env, int int_num)
>> +{
> What does dtg means?
"dtg" is "direct-to-guest" interrupts. This is an architectural feature
to raise interrupts directly in the guest instead of the monitor/VMM,
saving the latency of having to manually propagate interrupts to the guest.
Maybe this is a good case for a clarifying comment on `int_should_dtg()`?
>
>> + 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 +292,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);
>> }
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2026-08-20 22:00 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 1:31 [PATCH 00/11] Hexagon: exception, interrupt, system reg fixes Brian Cain
2026-08-19 1:31 ` [PATCH 01/11] target/hexagon: align exceptions for user/sysemu Brian Cain
2026-08-20 18:40 ` Pierrick Bouvier
2026-08-20 19:02 ` Brian Cain
2026-08-20 19:32 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 02/11] tests/tcg/hexagon: check priv instructions raise SIGILL Brian Cain
2026-08-20 18:41 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 03/11] hw/intc: clear pending bit on l2vic de-assertion Brian Cain
2026-08-20 18:41 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 04/11] target/hexagon: guard writes to unimplemented guest registers Brian Cain
2026-08-20 18:44 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 05/11] target/hexagon: take BQL when reading the system pcycle count Brian Cain
2026-08-20 18:45 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 06/11] target/hexagon: gate GPCYCLE guest register reads on SSR:CE Brian Cain
2026-08-20 18:46 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 07/11] target/hexagon: read UTIMERLO/UTIMERHI from the global timer Brian Cain
2026-08-20 18:48 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 08/11] target/hexagon: raise imprecise exception on multi-TLB match Brian Cain
2026-08-20 18:54 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery Brian Cain
2026-08-20 18:57 ` Pierrick Bouvier
2026-08-20 21:59 ` Brian Cain
2026-08-19 1:31 ` [PATCH 10/11] target/hexagon: fix iassign{r,w} to cover all threads Brian Cain
2026-08-20 18:59 ` Pierrick Bouvier
2026-08-19 1:31 ` [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts, sys_regs} Brian Cain via qemu development
2026-08-20 18:59 ` [PATCH 11/11] tests/functional/hexagon: update to v0.2.12, +test_{interrupts,sys_regs} Pierrick Bouvier
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.