Commit 196f037029 for qemu.org
commit 196f037029b89a39e63c0296f9ffcf688f0e225d
Author: Brian Cain <brian.cain@oss.qualcomm.com>
Date: Tue Sep 1 10:33:50 2026 -0700
target/hexagon: raise imprecise exception on multi-TLB match
tlbp now records a pending imprecise exception (via env->imprecise_exception)
when the lookup matches multiple entries, and the translator raises it after
the tlbp packet. Implement the HEX_EVENT_IMPRECISE delivery path so the
guest sees HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH instead of the exception
being silently dropped.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
diff --git a/hw/hexagon/hexagon_tlb.c b/hw/hexagon/hexagon_tlb.c
index b6d4aff389..6539458f25 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/include/hw/hexagon/hexagon_tlb.h b/include/hw/hexagon/hexagon_tlb.h
index 760dc1ea81..67c0a56b79 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 c50fbb3f72..ed5671abe5 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/target/hexagon/hex_mmu.c b/target/hexagon/hex_mmu.c
index 81d64a6146..de108f709c 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 2ab4aaf39d..75f0a9cc52 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -761,6 +761,7 @@ void hexagon_cpu_do_interrupt(CPUState *cs)
{
CPUHexagonState *env = cpu_env(cs);
+ HexagonCPU *cpu = HEXAGON_CPU(cs);
uint32_t ssr;
BQL_LOCK_GUARD();
@@ -775,7 +776,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);
@@ -934,8 +934,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 2dd95466e7..bf4646f4a8 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 9d5402a982..bbc2732e99 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];
@@ -1056,6 +1057,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)
{
/*
@@ -1155,6 +1178,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);
}
@@ -1362,6 +1389,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);