From e61dc8f72c096e084106d5e97101d9d88f642d0e Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 13 Jan 2012 09:21:32 +0400 Subject: [PATCH] target-xtensa: implement instruction breakpoints Add IBREAKA/IBREAKENABLE SRs and implement debug exception, BREAK and BREAK.N instructions and IBREAK breakpoints. IBREAK breakpoint address is considered constant for TB lifetime. On IBREAKA/IBREAKENABLE change corresponding TBs are invalidated. Signed-off-by: Max Filippov --- target-xtensa/cpu.h | 9 ++++++ target-xtensa/helper.c | 2 ++ target-xtensa/helpers.h | 5 +++ target-xtensa/op_helper.c | 38 ++++++++++++++++++++++ target-xtensa/translate.c | 68 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index c77fe13dd..a18072b7b 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -126,6 +126,8 @@ enum { RASID = 90, ITLBCFG = 91, DTLBCFG = 92, + IBREAKENABLE = 96, + IBREAKA = 128, EPC1 = 177, DEPC = 192, EPS2 = 194, @@ -196,6 +198,7 @@ enum { EXC_KERNEL, EXC_USER, EXC_DOUBLE, + EXC_DEBUG, EXC_MAX }; @@ -425,6 +428,7 @@ static inline int cpu_mmu_index(CPUState *env) #define XTENSA_TBFLAG_RING_MASK 0x3 #define XTENSA_TBFLAG_EXCM 0x4 #define XTENSA_TBFLAG_LITBASE 0x8 +#define XTENSA_TBFLAG_DEBUG 0x10 static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -440,6 +444,11 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, (env->sregs[LITBASE] & 1)) { *flags |= XTENSA_TBFLAG_LITBASE; } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) { + if (xtensa_get_cintlevel(env) < env->config->debug_level) { + *flags |= XTENSA_TBFLAG_DEBUG; + } + } } #include "cpu-all.h" diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index 973c268db..0a26f8dd3 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -44,6 +44,7 @@ void cpu_reset(CPUXtensaState *env) env->sregs[PS] = xtensa_option_enabled(env->config, XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10; env->sregs[VECBASE] = env->config->vecbase; + env->sregs[IBREAKENABLE] = 0; env->pending_irq_level = 0; reset_mmu(env); @@ -193,6 +194,7 @@ void do_interrupt(CPUState *env) case EXC_KERNEL: case EXC_USER: case EXC_DOUBLE: + case EXC_DEBUG: qemu_log_mask(CPU_LOG_INT, "%s(%d) " "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n", __func__, env->exception_index, diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h index 09ab3325c..afe39d4fc 100644 --- a/target-xtensa/helpers.h +++ b/target-xtensa/helpers.h @@ -3,6 +3,8 @@ DEF_HELPER_1(exception, void, i32) DEF_HELPER_2(exception_cause, void, i32, i32) DEF_HELPER_3(exception_cause_vaddr, void, i32, i32, i32) +DEF_HELPER_2(debug_exception, void, i32, i32) + DEF_HELPER_1(nsa, i32, i32) DEF_HELPER_1(nsau, i32, i32) DEF_HELPER_1(wsr_windowbase, void, i32) @@ -29,4 +31,7 @@ DEF_HELPER_2(itlb, void, i32, i32) DEF_HELPER_2(ptlb, i32, i32, i32) DEF_HELPER_3(wtlb, void, i32, i32, i32) +DEF_HELPER_1(wsr_ibreakenable, void, i32) +DEF_HELPER_2(wsr_ibreaka, void, i32, i32) + #include "def-helper.h" diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 060561103..1feaaee7f 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -134,6 +134,19 @@ void HELPER(exception_cause_vaddr)(uint32_t pc, uint32_t cause, uint32_t vaddr) HELPER(exception_cause)(pc, cause); } +void HELPER(debug_exception)(uint32_t pc, uint32_t cause) +{ + unsigned level = env->config->debug_level; + + env->pc = pc; + env->sregs[DEBUGCAUSE] = cause; + env->sregs[EPC1 + level - 1] = pc; + env->sregs[EPS2 + level - 2] = env->sregs[PS]; + env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | + (level << PS_INTLEVEL_SHIFT); + HELPER(exception)(EXC_DEBUG); +} + uint32_t HELPER(nsa)(uint32_t v) { if (v & 0x80000000) { @@ -662,3 +675,28 @@ void HELPER(wtlb)(uint32_t p, uint32_t v, uint32_t dtlb) split_tlb_entry_spec(v, dtlb, &vpn, &wi, &ei); xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); } + + +void HELPER(wsr_ibreakenable)(uint32_t v) +{ + uint32_t change = v ^ env->sregs[IBREAKENABLE]; + unsigned i; + + for (i = 0; i < env->config->nibreak; ++i) { + if (change & (1 << i)) { + tb_invalidate_phys_page_range( + env->sregs[IBREAKA + i], env->sregs[IBREAKA + i] + 1, 0); + } + } + env->sregs[IBREAKENABLE] = v & ((1 << env->config->nibreak) - 1); +} + +void HELPER(wsr_ibreaka)(uint32_t i, uint32_t v) +{ + if (env->sregs[IBREAKENABLE] & (1 << i) && env->sregs[IBREAKA + i] != v) { + tb_invalidate_phys_page_range( + env->sregs[IBREAKA + i], env->sregs[IBREAKA + i] + 1, 0); + tb_invalidate_phys_page_range(v, v + 1, 0); + } + env->sregs[IBREAKA + i] = v; +} diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index da5fdb581..a438474b4 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -61,6 +61,8 @@ typedef struct DisasContext { uint32_t ccount_delta; unsigned used_window; + + bool debug; } DisasContext; static TCGv_ptr cpu_env; @@ -91,6 +93,9 @@ static const char * const sregnames[256] = { [RASID] = "RASID", [ITLBCFG] = "ITLBCFG", [DTLBCFG] = "DTLBCFG", + [IBREAKENABLE] = "IBREAKENABLE", + [IBREAKA] = "IBREAKA0", + [IBREAKA + 1] = "IBREAKA1", [EPC1] = "EPC1", [EPC1 + 1] = "EPC2", [EPC1 + 2] = "EPC3", @@ -284,6 +289,19 @@ static void gen_exception_cause_vaddr(DisasContext *dc, uint32_t cause, tcg_temp_free(tcause); } +static void gen_debug_exception(DisasContext *dc, uint32_t cause) +{ + TCGv_i32 tpc = tcg_const_i32(dc->pc); + TCGv_i32 tcause = tcg_const_i32(cause); + gen_advance_ccount(dc); + gen_helper_debug_exception(tpc, tcause); + tcg_temp_free(tpc); + tcg_temp_free(tcause); + if (cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BI | DEBUGCAUSE_BN)) { + dc->is_jmp = DISAS_UPDATE; + } +} + static void gen_check_privilege(DisasContext *dc) { if (dc->cring) { @@ -493,6 +511,24 @@ static void gen_wsr_tlbcfg(DisasContext *dc, uint32_t sr, TCGv_i32 v) tcg_gen_andi_i32(cpu_SR[sr], v, 0x01130000); } +static void gen_wsr_ibreakenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + gen_helper_wsr_ibreakenable(v); + gen_jumpi_check_loop_end(dc, 0); +} + +static void gen_wsr_ibreaka(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + unsigned id = sr - IBREAKA; + + if (id < dc->config->nibreak) { + TCGv_i32 tmp = tcg_const_i32(id); + gen_helper_wsr_ibreaka(tmp, v); + tcg_temp_free(tmp); + gen_jumpi_check_loop_end(dc, 0); + } +} + static void gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, @@ -572,6 +608,9 @@ static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) [RASID] = gen_wsr_rasid, [ITLBCFG] = gen_wsr_tlbcfg, [DTLBCFG] = gen_wsr_tlbcfg, + [IBREAKENABLE] = gen_wsr_ibreakenable, + [IBREAKA] = gen_wsr_ibreaka, + [IBREAKA + 1] = gen_wsr_ibreaka, [INTSET] = gen_wsr_intset, [INTCLEAR] = gen_wsr_intclear, [INTENABLE] = gen_wsr_intenable, @@ -975,8 +1014,10 @@ static void disas_xtensa_insn(DisasContext *dc) break; case 4: /*BREAKx*/ - HAS_OPTION(XTENSA_OPTION_EXCEPTION); - TBD(); + HAS_OPTION(XTENSA_OPTION_DEBUG); + if (dc->debug) { + gen_debug_exception(dc, DEBUGCAUSE_BI); + } break; case 5: /*SYSCALLx*/ @@ -2356,7 +2397,10 @@ static void disas_xtensa_insn(DisasContext *dc) break; case 2: /*BREAK.Nn*/ - TBD(); + HAS_OPTION(XTENSA_OPTION_DEBUG); + if (dc->debug) { + gen_debug_exception(dc, DEBUGCAUSE_BN); + } break; case 3: /*NOP.Nn*/ @@ -2409,6 +2453,19 @@ static void check_breakpoint(CPUState *env, DisasContext *dc) } } +static void gen_ibreak_check(CPUState *env, DisasContext *dc) +{ + unsigned i; + + for (i = 0; i < dc->config->nibreak; ++i) { + if ((env->sregs[IBREAKENABLE] & (1 << i)) && + env->sregs[IBREAKA + i] == dc->pc) { + gen_debug_exception(dc, DEBUGCAUSE_IB); + break; + } + } +} + static void gen_intermediate_code_internal( CPUState *env, TranslationBlock *tb, int search_pc) { @@ -2435,6 +2492,7 @@ static void gen_intermediate_code_internal( dc.lend = env->sregs[LEND]; dc.is_jmp = DISAS_NEXT; dc.ccount_delta = 0; + dc.debug = tb->flags & XTENSA_TBFLAG_DEBUG; init_litbase(&dc); init_sar_tracker(&dc); @@ -2474,6 +2532,10 @@ static void gen_intermediate_code_internal( gen_io_start(); } + if (dc.debug) { + gen_ibreak_check(env, &dc); + } + disas_xtensa_insn(&dc); ++insn_count; if (env->singlestep_enabled) {