[PATCH 08/11] target/hexagon: raise imprecise exception on multi-TLB match

Brian Cain <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
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 <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.