[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