diff options
-rw-r--r-- | exec.c | 18 | ||||
-rw-r--r-- | hmp-commands.hx | 2 | ||||
-rw-r--r-- | monitor.c | 4 | ||||
-rw-r--r-- | target-xtensa/core-dc232b.c | 1 | ||||
-rw-r--r-- | target-xtensa/core-fsf.c | 1 | ||||
-rw-r--r-- | target-xtensa/cpu.h | 43 | ||||
-rw-r--r-- | target-xtensa/helper.c | 110 | ||||
-rw-r--r-- | target-xtensa/helpers.h | 7 | ||||
-rw-r--r-- | target-xtensa/op_helper.c | 100 | ||||
-rw-r--r-- | target-xtensa/overlay_tool.h | 23 | ||||
-rw-r--r-- | target-xtensa/translate.c | 156 | ||||
-rw-r--r-- | tests/tcg/xtensa/Makefile | 1 | ||||
-rw-r--r-- | tests/tcg/xtensa/test_break.S | 223 |
13 files changed, 674 insertions, 15 deletions
@@ -1504,7 +1504,8 @@ int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, CPUWatchpoint *wp; /* sanity checks: allow power-of-2 lengths, deny unaligned watchpoints */ - if ((len != 1 && len != 2 && len != 4 && len != 8) || (addr & ~len_mask)) { + if ((len & (len - 1)) || (addr & ~len_mask) || + len == 0 || len > TARGET_PAGE_SIZE) { fprintf(stderr, "qemu: tried to set invalid watchpoint at " TARGET_FMT_lx ", len=" TARGET_FMT_lu "\n", addr, len); return -EINVAL; @@ -3331,11 +3332,12 @@ static void check_watchpoint(int offset, int len_mask, int flags) tb_phys_invalidate(tb, -1); if (wp->flags & BP_STOP_BEFORE_ACCESS) { env->exception_index = EXCP_DEBUG; + cpu_loop_exit(env); } else { cpu_get_tb_cpu_state(env, &pc, &cs_base, &cpu_flags); tb_gen_code(env, pc, cs_base, cpu_flags, 1); + cpu_resume_from_signal(env, NULL); } - cpu_resume_from_signal(env, NULL); } } else { wp->flags &= ~BP_WATCHPOINT_HIT; @@ -3363,9 +3365,15 @@ static void watch_mem_write(void *opaque, target_phys_addr_t addr, { check_watchpoint(addr & ~TARGET_PAGE_MASK, ~(size - 1), BP_MEM_WRITE); switch (size) { - case 1: stb_phys(addr, val); - case 2: stw_phys(addr, val); - case 4: stl_phys(addr, val); + case 1: + stb_phys(addr, val); + break; + case 2: + stw_phys(addr, val); + break; + case 4: + stl_phys(addr, val); + break; default: abort(); } } diff --git a/hmp-commands.hx b/hmp-commands.hx index 64b3656d8e..ed88877230 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1355,7 +1355,7 @@ show i8259 (PIC) state @item info pci show emulated PCI device info @item info tlb -show virtual to physical memory mappings (i386, SH4, SPARC, and PPC only) +show virtual to physical memory mappings (i386, SH4, SPARC, PPC, and Xtensa only) @item info mem show the active virtual memory mappings (i386 only) @item info jit @@ -1949,7 +1949,7 @@ static void tlb_info(Monitor *mon) #endif -#if defined(TARGET_SPARC) || defined(TARGET_PPC) +#if defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_XTENSA) static void tlb_info(Monitor *mon) { CPUState *env1 = mon_get_cpu(); @@ -2396,7 +2396,7 @@ static mon_cmd_t info_cmds[] = { .mhandler.info = hmp_info_pci, }, #if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \ - defined(TARGET_PPC) + defined(TARGET_PPC) || defined(TARGET_XTENSA) { .name = "tlb", .args_type = "", diff --git a/target-xtensa/core-dc232b.c b/target-xtensa/core-dc232b.c index 4d9bd559d4..b723c4ca7b 100644 --- a/target-xtensa/core-dc232b.c +++ b/target-xtensa/core-dc232b.c @@ -22,6 +22,7 @@ static const XtensaConfig dc232b = { EXCEPTIONS_SECTION, INTERRUPTS_SECTION, TLB_SECTION, + DEBUG_SECTION, .clock_freq_khz = 10000, }; diff --git a/target-xtensa/core-fsf.c b/target-xtensa/core-fsf.c index 7650462f2f..88de4dd4ba 100644 --- a/target-xtensa/core-fsf.c +++ b/target-xtensa/core-fsf.c @@ -16,6 +16,7 @@ static const XtensaConfig fsf = { EXCEPTIONS_SECTION, INTERRUPTS_SECTION, TLB_SECTION, + DEBUG_SECTION, .clock_freq_khz = 10000, }; diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 0db83a6fd7..fb8a727c66 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -126,6 +126,10 @@ enum { RASID = 90, ITLBCFG = 91, DTLBCFG = 92, + IBREAKENABLE = 96, + IBREAKA = 128, + DBREAKA = 144, + DBREAKC = 160, EPC1 = 177, DEPC = 192, EPS2 = 194, @@ -137,8 +141,11 @@ enum { PS = 230, VECBASE = 231, EXCCAUSE = 232, + DEBUGCAUSE = 233, CCOUNT = 234, PRID = 235, + ICOUNT = 236, + ICOUNTLEVEL = 237, EXCVADDR = 238, CCOMPARE = 240, }; @@ -161,12 +168,27 @@ enum { #define PS_WOE 0x40000 +#define DEBUGCAUSE_IC 0x1 +#define DEBUGCAUSE_IB 0x2 +#define DEBUGCAUSE_DB 0x4 +#define DEBUGCAUSE_BI 0x8 +#define DEBUGCAUSE_BN 0x10 +#define DEBUGCAUSE_DI 0x20 +#define DEBUGCAUSE_DBNUM 0xf00 +#define DEBUGCAUSE_DBNUM_SHIFT 8 + +#define DBREAKC_SB 0x80000000 +#define DBREAKC_LB 0x40000000 +#define DBREAKC_SB_LB (DBREAKC_SB | DBREAKC_LB) +#define DBREAKC_MASK 0x3f + #define MAX_NAREG 64 #define MAX_NINTERRUPT 32 #define MAX_NLEVEL 6 #define MAX_NNMI 1 #define MAX_NCCOMPARE 3 #define MAX_TLB_WAY_SIZE 8 +#define MAX_NDBREAK 2 #define REGION_PAGE_MASK 0xe0000000 @@ -186,6 +208,7 @@ enum { EXC_KERNEL, EXC_USER, EXC_DOUBLE, + EXC_DEBUG, EXC_MAX }; @@ -279,6 +302,11 @@ typedef struct XtensaConfig { uint32_t timerint[MAX_NCCOMPARE]; unsigned nextint; unsigned extint[MAX_NINTERRUPT]; + + unsigned debug_level; + unsigned nibreak; + unsigned ndbreak; + uint32_t clock_freq_khz; xtensa_tlb itlb; @@ -310,6 +338,9 @@ typedef struct CPUXtensaState { int exception_taken; + /* Watchpoints for DBREAK registers */ + CPUWatchpoint *cpu_watchpoint[MAX_NDBREAK]; + CPU_COMMON } CPUXtensaState; @@ -344,6 +375,8 @@ void xtensa_tlb_set_entry(CPUState *env, bool dtlb, int xtensa_get_physical_addr(CPUState *env, uint32_t vaddr, int is_write, int mmu_idx, uint32_t *paddr, uint32_t *page_size, unsigned *access); +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env); +void debug_exception_env(CPUState *new_env, uint32_t cause); #define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt)) @@ -409,6 +442,8 @@ 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 +#define XTENSA_TBFLAG_ICOUNT 0x20 static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -424,6 +459,14 @@ 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; + } + if (xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) { + *flags |= XTENSA_TBFLAG_ICOUNT; + } + } } #include "cpu-all.h" diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index 2a0cb1a562..2ef50d656e 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); @@ -57,9 +58,44 @@ void xtensa_register_core(XtensaConfigList *node) xtensa_cores = node; } +static uint32_t check_hw_breakpoints(CPUState *env) +{ + unsigned i; + + for (i = 0; i < env->config->ndbreak; ++i) { + if (env->cpu_watchpoint[i] && + env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) { + return DEBUGCAUSE_DB | (i << DEBUGCAUSE_DBNUM_SHIFT); + } + } + return 0; +} + +static CPUDebugExcpHandler *prev_debug_excp_handler; + +static void breakpoint_handler(CPUState *env) +{ + if (env->watchpoint_hit) { + if (env->watchpoint_hit->flags & BP_CPU) { + uint32_t cause; + + env->watchpoint_hit = NULL; + cause = check_hw_breakpoints(env); + if (cause) { + debug_exception_env(env, cause); + } + cpu_resume_from_signal(env, NULL); + } + } + if (prev_debug_excp_handler) { + prev_debug_excp_handler(env); + } +} + CPUXtensaState *cpu_xtensa_init(const char *cpu_model) { static int tcg_inited; + static int debug_handler_inited; CPUXtensaState *env; const XtensaConfig *config = NULL; XtensaConfigList *core = xtensa_cores; @@ -83,6 +119,12 @@ CPUXtensaState *cpu_xtensa_init(const char *cpu_model) xtensa_translate_init(); } + if (!debug_handler_inited && tcg_enabled()) { + debug_handler_inited = 1; + prev_debug_excp_handler = + cpu_set_debug_excp_handler(breakpoint_handler); + } + xtensa_irq_init(env); qemu_init_vcpu(env); return env; @@ -193,6 +235,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, @@ -540,3 +583,70 @@ int xtensa_get_physical_addr(CPUState *env, return 0; } } + +static void dump_tlb(FILE *f, fprintf_function cpu_fprintf, + CPUState *env, bool dtlb) +{ + unsigned wi, ei; + const xtensa_tlb *conf = + dtlb ? &env->config->dtlb : &env->config->itlb; + unsigned (*attr_to_access)(uint32_t) = + xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) ? + mmu_attr_to_access : region_attr_to_access; + + for (wi = 0; wi < conf->nways; ++wi) { + uint32_t sz = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1; + const char *sz_text; + bool print_header = true; + + if (sz >= 0x100000) { + sz >>= 20; + sz_text = "MB"; + } else { + sz >>= 10; + sz_text = "KB"; + } + + for (ei = 0; ei < conf->way_size[wi]; ++ei) { + const xtensa_tlb_entry *entry = + xtensa_tlb_get_entry(env, dtlb, wi, ei); + + if (entry->asid) { + unsigned access = attr_to_access(entry->attr); + + if (print_header) { + print_header = false; + cpu_fprintf(f, "Way %u (%d %s)\n", wi, sz, sz_text); + cpu_fprintf(f, + "\tVaddr Paddr ASID Attr RWX\n" + "\t---------- ---------- ---- ---- ---\n"); + } + cpu_fprintf(f, + "\t0x%08x 0x%08x 0x%02x 0x%02x %c%c%c\n", + entry->vaddr, + entry->paddr, + entry->asid, + entry->attr, + (access & PAGE_READ) ? 'R' : '-', + (access & PAGE_WRITE) ? 'W' : '-', + (access & PAGE_EXEC) ? 'X' : '-'); + } + } + } +} + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) +{ + if (xtensa_option_bits_enabled(env->config, + XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | + XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION) | + XTENSA_OPTION_BIT(XTENSA_OPTION_MMU))) { + + cpu_fprintf(f, "ITLB:\n"); + dump_tlb(f, cpu_fprintf, env, false); + cpu_fprintf(f, "\nDTLB:\n"); + dump_tlb(f, cpu_fprintf, env, true); + } else { + cpu_fprintf(f, "No TLB for this CPU core\n"); + } +} diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h index 09ab3325c9..48a741e46d 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,9 @@ 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) +DEF_HELPER_2(wsr_dbreaka, void, i32, i32) +DEF_HELPER_2(wsr_dbreakc, void, i32, i32) + #include "def-helper.h" diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 0605611031..e184cf64f0 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -134,6 +134,27 @@ void HELPER(exception_cause_vaddr)(uint32_t pc, uint32_t cause, uint32_t vaddr) HELPER(exception_cause)(pc, cause); } +void debug_exception_env(CPUState *new_env, uint32_t cause) +{ + if (xtensa_get_cintlevel(new_env) < new_env->config->debug_level) { + env = new_env; + HELPER(debug_exception)(env->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 +683,82 @@ 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; +} + +static void set_dbreak(unsigned i, uint32_t dbreaka, uint32_t dbreakc) +{ + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + uint32_t mask = dbreakc | ~DBREAKC_MASK; + + if (env->cpu_watchpoint[i]) { + cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[i]); + } + if (dbreakc & DBREAKC_SB) { + flags |= BP_MEM_WRITE; + } + if (dbreakc & DBREAKC_LB) { + flags |= BP_MEM_READ; + } + /* contiguous mask after inversion is one less than some power of 2 */ + if ((~mask + 1) & ~mask) { + qemu_log("DBREAKC mask is not contiguous: 0x%08x\n", dbreakc); + /* cut mask after the first zero bit */ + mask = 0xffffffff << (32 - clo32(mask)); + } + if (cpu_watchpoint_insert(env, dbreaka & mask, ~mask + 1, + flags, &env->cpu_watchpoint[i])) { + env->cpu_watchpoint[i] = NULL; + qemu_log("Failed to set data breakpoint at 0x%08x/%d\n", + dbreaka & mask, ~mask + 1); + } +} + +void HELPER(wsr_dbreaka)(uint32_t i, uint32_t v) +{ + uint32_t dbreakc = env->sregs[DBREAKC + i]; + + if ((dbreakc & DBREAKC_SB_LB) && + env->sregs[DBREAKA + i] != v) { + set_dbreak(i, v, dbreakc); + } + env->sregs[DBREAKA + i] = v; +} + +void HELPER(wsr_dbreakc)(uint32_t i, uint32_t v) +{ + if ((env->sregs[DBREAKC + i] ^ v) & (DBREAKC_SB_LB | DBREAKC_MASK)) { + if (v & DBREAKC_SB_LB) { + set_dbreak(i, env->sregs[DBREAKA + i], v); + } else { + if (env->cpu_watchpoint[i]) { + cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[i]); + env->cpu_watchpoint[i] = NULL; + } + } + } + env->sregs[DBREAKC + i] = v; +} diff --git a/target-xtensa/overlay_tool.h b/target-xtensa/overlay_tool.h index df19cc96ea..a3a5650fb0 100644 --- a/target-xtensa/overlay_tool.h +++ b/target-xtensa/overlay_tool.h @@ -114,6 +114,7 @@ [EXC_KERNEL] = XCHAL_KERNEL_VECTOR_VADDR, \ [EXC_USER] = XCHAL_USER_VECTOR_VADDR, \ [EXC_DOUBLE] = XCHAL_DOUBLEEXC_VECTOR_VADDR, \ + [EXC_DEBUG] = XCHAL_DEBUG_VECTOR_VADDR, \ } #define INTERRUPT_VECTORS { \ @@ -251,6 +252,8 @@ .nextint = XCHAL_NUM_EXTINTERRUPTS, \ .extint = EXTINTS +#if XCHAL_HAVE_PTP_MMU + #define TLB_TEMPLATE(ways, refill_way_size, way56) { \ .nways = ways, \ .way_size = { \ @@ -268,11 +271,23 @@ #define DTLB(varway56) \ TLB_TEMPLATE(10, 1 << XCHAL_DTLB_ARF_ENTRIES_LOG2, varway56) -#if XCHAL_HAVE_PTP_MMU #define TLB_SECTION \ .itlb = ITLB(XCHAL_HAVE_SPANNING_WAY), \ .dtlb = DTLB(XCHAL_HAVE_SPANNING_WAY) -#else + +#elif XCHAL_HAVE_XLT_CACHEATTR || XCHAL_HAVE_MIMIC_CACHEATTR + +#define TLB_TEMPLATE { \ + .nways = 1, \ + .way_size = { \ + 8, \ + } \ + } + +#define TLB_SECTION \ + .itlb = TLB_TEMPLATE, \ + .dtlb = TLB_TEMPLATE + #endif #if (defined(TARGET_WORDS_BIGENDIAN) != 0) == (XCHAL_HAVE_BE != 0) @@ -288,6 +303,10 @@ #define REGISTER_CORE(core) #endif +#define DEBUG_SECTION \ + .debug_level = XCHAL_DEBUGLEVEL, \ + .nibreak = XCHAL_NUM_IBREAK, \ + .ndbreak = XCHAL_NUM_DBREAK #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI + 1 <= 2 #define XCHAL_INTLEVEL2_VECTOR_VADDR 0 diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index c81450d1a5..9e8e20a904 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -61,6 +61,10 @@ typedef struct DisasContext { uint32_t ccount_delta; unsigned used_window; + + bool debug; + bool icount; + TCGv_i32 next_icount; } DisasContext; static TCGv_ptr cpu_env; @@ -91,6 +95,13 @@ static const char * const sregnames[256] = { [RASID] = "RASID", [ITLBCFG] = "ITLBCFG", [DTLBCFG] = "DTLBCFG", + [IBREAKENABLE] = "IBREAKENABLE", + [IBREAKA] = "IBREAKA0", + [IBREAKA + 1] = "IBREAKA1", + [DBREAKA] = "DBREAKA0", + [DBREAKA + 1] = "DBREAKA1", + [DBREAKC] = "DBREAKC0", + [DBREAKC + 1] = "DBREAKC1", [EPC1] = "EPC1", [EPC1 + 1] = "EPC2", [EPC1 + 2] = "EPC3", @@ -119,8 +130,11 @@ static const char * const sregnames[256] = { [PS] = "PS", [VECBASE] = "VECBASE", [EXCCAUSE] = "EXCCAUSE", + [DEBUGCAUSE] = "DEBUGCAUSE", [CCOUNT] = "CCOUNT", [PRID] = "PRID", + [ICOUNT] = "ICOUNT", + [ICOUNTLEVEL] = "ICOUNTLEVEL", [EXCVADDR] = "EXCVADDR", [CCOMPARE] = "CCOMPARE0", [CCOMPARE + 1] = "CCOMPARE1", @@ -283,6 +297,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) { @@ -294,10 +321,13 @@ static void gen_check_privilege(DisasContext *dc) static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) { tcg_gen_mov_i32(cpu_pc, dest); + gen_advance_ccount(dc); + if (dc->icount) { + tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); + } if (dc->singlestep_enabled) { gen_exception(dc, EXCP_DEBUG); } else { - gen_advance_ccount(dc); if (slot >= 0) { tcg_gen_goto_tb(slot); tcg_gen_exit_tb((tcg_target_long)dc->tb + slot); @@ -492,6 +522,46 @@ 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_dbreaka(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + unsigned id = sr - DBREAKA; + + if (id < dc->config->ndbreak) { + TCGv_i32 tmp = tcg_const_i32(id); + gen_helper_wsr_dbreaka(tmp, v); + tcg_temp_free(tmp); + } +} + +static void gen_wsr_dbreakc(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + unsigned id = sr - DBREAKC; + + if (id < dc->config->ndbreak) { + TCGv_i32 tmp = tcg_const_i32(id); + gen_helper_wsr_dbreakc(tmp, v); + tcg_temp_free(tmp); + } +} + static void gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, @@ -535,10 +605,30 @@ static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) gen_jumpi_check_loop_end(dc, -1); } +static void gen_wsr_debugcause(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ +} + static void gen_wsr_prid(DisasContext *dc, uint32_t sr, TCGv_i32 v) { } +static void gen_wsr_icount(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + if (dc->icount) { + tcg_gen_mov_i32(dc->next_icount, v); + } else { + tcg_gen_mov_i32(cpu_SR[sr], v); + } +} + +static void gen_wsr_icountlevel(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + tcg_gen_andi_i32(cpu_SR[sr], v, 0xf); + /* This can change tb->flags, so exit tb */ + gen_jumpi_check_loop_end(dc, -1); +} + static void gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v) { uint32_t id = sr - CCOMPARE; @@ -567,11 +657,21 @@ 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, + [DBREAKA] = gen_wsr_dbreaka, + [DBREAKA + 1] = gen_wsr_dbreaka, + [DBREAKC] = gen_wsr_dbreakc, + [DBREAKC + 1] = gen_wsr_dbreakc, [INTSET] = gen_wsr_intset, [INTCLEAR] = gen_wsr_intclear, [INTENABLE] = gen_wsr_intenable, [PS] = gen_wsr_ps, + [DEBUGCAUSE] = gen_wsr_debugcause, [PRID] = gen_wsr_prid, + [ICOUNT] = gen_wsr_icount, + [ICOUNTLEVEL] = gen_wsr_icountlevel, [CCOMPARE] = gen_wsr_ccompare, [CCOMPARE + 1] = gen_wsr_ccompare, [CCOMPARE + 2] = gen_wsr_ccompare, @@ -749,7 +849,7 @@ static void disas_xtensa_insn(DisasContext *dc) uint8_t b0 = ldub_code(dc->pc); uint8_t b1 = ldub_code(dc->pc + 1); - uint8_t b2 = ldub_code(dc->pc + 2); + uint8_t b2 = 0; static const uint32_t B4CONST[] = { 0xffffffff, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 32, 64, 128, 256 @@ -764,6 +864,7 @@ static void disas_xtensa_insn(DisasContext *dc) HAS_OPTION(XTENSA_OPTION_CODE_DENSITY); } else { dc->next_pc = dc->pc + 3; + b2 = ldub_code(dc->pc + 2); } switch (OP0) { @@ -968,8 +1069,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*/ @@ -2349,7 +2452,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*/ @@ -2402,6 +2508,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) { @@ -2428,10 +2547,15 @@ 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; + dc.icount = tb->flags & XTENSA_TBFLAG_ICOUNT; init_litbase(&dc); init_sar_tracker(&dc); reset_used_window(&dc); + if (dc.icount) { + dc.next_icount = tcg_temp_local_new_i32(); + } gen_icount_start(); @@ -2467,8 +2591,27 @@ static void gen_intermediate_code_internal( gen_io_start(); } + if (dc.icount) { + int label = gen_new_label(); + + tcg_gen_addi_i32(dc.next_icount, cpu_SR[ICOUNT], 1); + tcg_gen_brcondi_i32(TCG_COND_NE, dc.next_icount, 0, label); + tcg_gen_mov_i32(dc.next_icount, cpu_SR[ICOUNT]); + if (dc.debug) { + gen_debug_exception(&dc, DEBUGCAUSE_IC); + } + gen_set_label(label); + } + + if (dc.debug) { + gen_ibreak_check(env, &dc); + } + disas_xtensa_insn(&dc); ++insn_count; + if (dc.icount) { + tcg_gen_mov_i32(cpu_SR[ICOUNT], dc.next_icount); + } if (env->singlestep_enabled) { tcg_gen_movi_i32(cpu_pc, dc.pc); gen_exception(&dc, EXCP_DEBUG); @@ -2481,6 +2624,9 @@ static void gen_intermediate_code_internal( reset_litbase(&dc); reset_sar_tracker(&dc); + if (dc.icount) { + tcg_temp_free(dc.next_icount); + } if (tb->cflags & CF_LAST_IO) { gen_io_end(); diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile index 8713af16eb..7e1e619d23 100644 --- a/tests/tcg/xtensa/Makefile +++ b/tests/tcg/xtensa/Makefile @@ -23,6 +23,7 @@ CRT = crt.o vectors.o TESTCASES += test_b.tst TESTCASES += test_bi.tst #TESTCASES += test_boolean.tst +TESTCASES += test_break.tst TESTCASES += test_bz.tst TESTCASES += test_clamps.tst TESTCASES += test_fail.tst diff --git a/tests/tcg/xtensa/test_break.S b/tests/tcg/xtensa/test_break.S new file mode 100644 index 0000000000..8a8db8033b --- /dev/null +++ b/tests/tcg/xtensa/test_break.S @@ -0,0 +1,223 @@ +.include "macros.inc" + +#define debug_level 6 +#define debug_vector level6 + +test_suite break + +test break + set_vector debug_vector, 0 + rsil a2, debug_level + _break 0, 0 + + set_vector debug_vector, 2f + rsil a2, debug_level - 1 +1: + _break 0, 0 + test_fail +2: + rsr a2, ps + movi a3, 0x1f + and a2, a2, a3 + movi a3, 0x10 | debug_level + assert eq, a2, a3 + rsr a2, epc6 + movi a3, 1b + assert eq, a2, a3 + rsr a2, debugcause + movi a3, 0x8 + assert eq, a2, a3 +test_end + +test breakn + set_vector debug_vector, 0 + rsil a2, debug_level + _break.n 0 + + set_vector debug_vector, 2f + rsil a2, debug_level - 1 +1: + _break.n 0 + test_fail +2: + rsr a2, ps + movi a3, 0x1f + and a2, a2, a3 + movi a3, 0x10 | debug_level + assert eq, a2, a3 + rsr a2, epc6 + movi a3, 1b + assert eq, a2, a3 + rsr a2, debugcause + movi a3, 0x10 + assert eq, a2, a3 +test_end + +test ibreak + set_vector debug_vector, 0 + rsil a2, debug_level + movi a2, 1f + wsr a2, ibreaka0 + movi a2, 1 + wsr a2, ibreakenable + isync +1: + rsil a2, debug_level - 1 + movi a2, 1f + wsr a2, ibreaka0 + movi a2, 0 + wsr a2, ibreakenable + isync +1: + set_vector debug_vector, 2f + movi a2, 1f + wsr a2, ibreaka0 + movi a2, 1 + wsr a2, ibreakenable + isync +1: + test_fail +2: + rsr a2, ps + movi a3, 0x1f + and a2, a2, a3 + movi a3, 0x10 | debug_level + assert eq, a2, a3 + rsr a2, epc6 + movi a3, 1b + assert eq, a2, a3 + rsr a2, debugcause + movi a3, 0x2 + assert eq, a2, a3 +test_end + +test ibreak_priority + set_vector debug_vector, 2f + rsil a2, debug_level - 1 + movi a2, 1f + wsr a2, ibreaka0 + movi a2, 1 + wsr a2, ibreakenable + isync +1: + break 0, 0 + test_fail +2: + rsr a2, debugcause + movi a3, 0x2 + assert eq, a2, a3 +test_end + +test icount + set_vector debug_vector, 2f + rsil a2, debug_level - 1 + movi a2, -2 + wsr a2, icount + movi a2, 1 + wsr a2, icountlevel + isync + rsil a2, 0 + nop +1: + break 0, 0 + test_fail +2: + movi a2, 0 + wsr a2, icountlevel + rsr a2, epc6 + movi a3, 1b + assert eq, a2, a3 + rsr a2, debugcause + movi a3, 0x1 + assert eq, a2, a3 +test_end + +.macro check_dbreak dr + rsr a2, epc6 + movi a3, 1b + assert eq, a2, a3 + rsr a2, debugcause + movi a3, 0x4 | (\dr << 8) + assert eq, a2, a3 + movi a2, 0 + wsr a2, dbreakc\dr +.endm + +.macro dbreak_test dr, ctl, break, access, op + set_vector debug_vector, 2f + rsil a2, debug_level - 1 + movi a2, \ctl + wsr a2, dbreakc\dr + movi a2, \break + wsr a2, dbreaka\dr + movi a2, \access + isync +1: + \op a3, a2, 0 + test_fail +2: + check_dbreak \dr + reset_ps +.endm + +test dbreak_exact + dbreak_test 0, 0x4000003f, 0xd000007f, 0xd000007f, l8ui + dbreak_test 1, 0x4000003e, 0xd000007e, 0xd000007e, l16ui + dbreak_test 0, 0x4000003c, 0xd000007c, 0xd000007c, l32i + + dbreak_test 1, 0x8000003f, 0xd000007f, 0xd000007f, s8i + dbreak_test 0, 0x8000003e, 0xd000007e, 0xd000007e, s16i + dbreak_test 1, 0x8000003c, 0xd000007c, 0xd000007c, s32i +test_end + +test dbreak_overlap + dbreak_test 0, 0x4000003f, 0xd000007d, 0xd000007c, l16ui + dbreak_test 1, 0x4000003f, 0xd000007d, 0xd000007c, l32i + + dbreak_test 0, 0x4000003e, 0xd000007e, 0xd000007f, l8ui + dbreak_test 1, 0x4000003e, 0xd000007e, 0xd000007c, l32i + + dbreak_test 0, 0x4000003c, 0xd000007c, 0xd000007d, l8ui + dbreak_test 1, 0x4000003c, 0xd000007c, 0xd000007c, l16ui + + dbreak_test 0, 0x40000038, 0xd0000078, 0xd000007b, l8ui + dbreak_test 1, 0x40000038, 0xd0000078, 0xd000007a, l16ui + dbreak_test 0, 0x40000038, 0xd0000078, 0xd000007c, l32i + + dbreak_test 1, 0x40000030, 0xd0000070, 0xd0000075, l8ui + dbreak_test 0, 0x40000030, 0xd0000070, 0xd0000076, l16ui + dbreak_test 1, 0x40000030, 0xd0000070, 0xd0000078, l32i + + dbreak_test 0, 0x40000020, 0xd0000060, 0xd000006f, l8ui + dbreak_test 1, 0x40000020, 0xd0000060, 0xd0000070, l16ui + dbreak_test 0, 0x40000020, 0xd0000060, 0xd0000074, l32i + + + dbreak_test 0, 0x8000003f, 0xd000007d, 0xd000007c, s16i + dbreak_test 1, 0x8000003f, 0xd000007d, 0xd000007c, s32i + + dbreak_test 0, 0x8000003e, 0xd000007e, 0xd000007f, s8i + dbreak_test 1, 0x8000003e, 0xd000007e, 0xd000007c, s32i + + dbreak_test 0, 0x8000003c, 0xd000007c, 0xd000007d, s8i + dbreak_test 1, 0x8000003c, 0xd000007c, 0xd000007c, s16i + + dbreak_test 0, 0x80000038, 0xd0000078, 0xd000007b, s8i + dbreak_test 1, 0x80000038, 0xd0000078, 0xd000007a, s16i + dbreak_test 0, 0x80000038, 0xd0000078, 0xd000007c, s32i + + dbreak_test 1, 0x80000030, 0xd0000070, 0xd0000075, s8i + dbreak_test 0, 0x80000030, 0xd0000070, 0xd0000076, s16i + dbreak_test 1, 0x80000030, 0xd0000070, 0xd0000078, s32i + + dbreak_test 0, 0x80000020, 0xd0000060, 0xd000006f, s8i + dbreak_test 1, 0x80000020, 0xd0000060, 0xd0000070, s16i + dbreak_test 0, 0x80000020, 0xd0000060, 0xd0000074, s32i +test_end + +test dbreak_invalid + dbreak_test 0, 0x40000030, 0xd0000071, 0xd0000070, l16ui + dbreak_test 1, 0x40000035, 0xd0000072, 0xd0000070, l32i +test_end + +test_suite_end |