#include #include #include #include "xcpt.h" #include "libsys.h" #include "mips_dis.h" #define DBG_PRINT_REG(tag_str, reg) \ dbg_puts(tag_str); \ dbg_print_word(reg); \ dbg_puts(" "); #define DBG_PRINT_REG_CHG(tag_str, reg, chg_tag) \ dbg_puts(tag_str); \ dbg_print_word(reg); \ dbg_puts(chg_tag); \ dbg_puts(" "); #define MAX_USER_BP 16 #define BP_MASK_ID 0x00FFF #define BP_MASK_TYPE 0xFF000 #define BP_GET_ID(bp) ((bp >> 6) & 0xFFF) #define BP_USER_BASE 0x10000 #define BP_USER(id) (((BP_USER_BASE + id) << 6) | 0x0D) #define BP_MGMT_BASE_SS 0x20000 #define BP_MGMT_SS(id) (((BP_MGMT_BASE_SS + id) << 6) | 0x0D) #define BP_MGMT_BASE_RU 0x30000 #define BP_MGMT_RU(id) (((BP_MGMT_BASE_RU + id) << 6) | 0x0D) static char g_buf[80]; enum { STATE_INIT = 0, STATE_RUN, STATE_STEP_SINGLE, STATE_STEP_PROC, STATE_NUM_ENTRIES }; static int g_state; static char *g_state_names[STATE_NUM_ENTRIES] = {"Init", "Run", "Single step", "Precedure step"}; typedef struct _sbp_t { uint32_t *pAddr; uint32_t instr; } bp_t; static EXCEPTION_CONTEXT xcp_last; static bp_t user_bp[MAX_USER_BP]; static bp_t mgmt_bp_ss[2]; static bp_t mgmt_bp_ru[2]; static int g_is_ss; static int g_bp_count; #if 1 extern void dbg_putchar(char c); extern char dbg_getchar(); void dbg_puts(const char *buffer) { while(*buffer) { dbg_putchar(*buffer); buffer++; } } void dbg_print_byte(char byte) { int i; unsigned char c, nibble; for (i=0; i < 2; i++) { nibble = (char)((byte >> 4) & 0xF); byte <<= 4; if (nibble < 10) c = nibble + '0'; else c = nibble + 'A' - 10; dbg_putchar(c); } } void dbg_print_word(int word) { int i; unsigned char c; for (i=0; i < 4; i++) { c = (char) (word >> 24); dbg_print_byte(c); word <<= 8; } } int dbg_gets(char *buffer, size_t size) { size_t len = 0; while(size) { char c = dbg_getchar(); if (c == 0x0A || c == 0x0D) { if (len) { break; } } else { buffer[len++] = c; dbg_putchar(c); if (len == size) { break; } } } buffer[len] = 0; dbg_putchar(0x0D); dbg_putchar(0x0A); return len; } #endif int IsBreak(uint32_t instr) { return ((instr & 0xFC00003F) == 0x0000000D); } uint32_t BreakGetType(uint32_t instr) { return (instr >> 6) & BP_MASK_TYPE; } char* reg_changed(uint32_t reg1, uint32_t reg2) { static char chg_str[2] = {0}; if (reg1 != reg2) chg_str[0] = '*'; else chg_str[0] = ' '; return chg_str; } void set_mgmt_break(uint32_t *pAddr_break) { mgmt_bp_ss[0].pAddr = NULL; mgmt_bp_ss[0].pAddr = 0; // Don't overwrite user breaks if (IsBreak(*pAddr_break) && (BreakGetType(*pAddr_break) == BP_USER_BASE)) { // dbg_puts("Instruction at address ");dbg_print_word((long)pAddr_break);dbg_puts(" is user break\n"); } else { // Save original instruction mgmt_bp_ss[0].pAddr = pAddr_break; mgmt_bp_ss[0].instr = *(mgmt_bp_ss[0].pAddr); // Replace instruction with managment breakpoint *(mgmt_bp_ss[0].pAddr) = BP_MGMT_SS(1); ICACHE_invalidate_at(mgmt_bp_ss[0].pAddr); } } void singlestep(struct xcptcontext * xcp, uint32_t *pAddr_curr, uint32_t is_branch_shadow) { int junk; uint32_t branch_addr, reg, result, bp_type; uint32_t *pInstr, *pAddr_prev, *pAddr_next; pAddr_prev = pAddr_curr - 1; pAddr_next = pAddr_curr + 1; set_mgmt_break(pAddr_next); mgmt_bp_ss[1].pAddr = NULL; mgmt_bp_ss[1].pAddr = 0; // We have two possible management breaks, if previous instruction was branch or jump if (is_branch_shadow) { result = tdisasm(g_buf, (long)pAddr_prev, *pAddr_prev, 0, &junk, &branch_addr, ®); // Is jump target stored in register if (result == 3) { branch_addr = xcp->regs[reg&0x1F]; } // Don't overwrite user breaks if (IsBreak(branch_addr) && (BreakGetType(branch_addr) == BP_USER_BASE)) { // dbg_puts("Instruction at branch address ");dbg_print_word((long)branch_addr);dbg_puts(" is user break\n"); } else { // Save original instruction mgmt_bp_ss[1].pAddr = (uint32_t*)branch_addr; mgmt_bp_ss[1].instr = *(mgmt_bp_ss[1].pAddr); // Replace instruction with managment breakpoint *(mgmt_bp_ss[1].pAddr) = BP_MGMT_SS(2); ICACHE_invalidate_at(mgmt_bp_ss[1].pAddr); } } } void dbg_handler(struct xcptcontext * xcp) { uint32_t bp_addr, bp_index = 0, branch_addr, reg, result, bp_type; int junk, i, leave_isr, is_branch_shadow, is_user_bp; uint32_t *pInstr, *pAddr_curr, *pAddr_prev, *pAddr_next; instr_info_t info; is_user_bp = 0; pAddr_curr = (uint32_t*)xcp->epc; is_branch_shadow = ((xcp->cr & 0x80000000) == 0x80000000); if (is_branch_shadow) pAddr_curr++; pAddr_prev = pAddr_curr - 1; pAddr_next = pAddr_curr + 1; if (IsBreak(*pAddr_curr)) { bp_type = BreakGetType(*pAddr_curr); switch (bp_type) { case BP_MGMT_BASE_SS: // Restore original instructions after single-step // dbg_puts("1. Restore after single-step at ");dbg_print_word((long)mgmt_bp_ss[0].pAddr);dbg_puts("\n\r"); *(mgmt_bp_ss[0].pAddr) = mgmt_bp_ss[0].instr; ICACHE_invalidate_at(mgmt_bp_ss[0].pAddr); if (mgmt_bp_ss[1].pAddr) { // dbg_puts("2. Restore after single-step at ");dbg_print_word((long)mgmt_bp_ss[1].pAddr);dbg_puts("\n\r"); *(mgmt_bp_ss[1].pAddr) = mgmt_bp_ss[1].instr; ICACHE_invalidate_at(mgmt_bp_ss[1].pAddr); } break; case BP_MGMT_BASE_RU: bp_index = BP_GET_ID(*pAddr_curr); // dbg_puts("Restore user break at ");dbg_print_word((long)user_bp[bp_index].pAddr);dbg_puts("\n\r"); *(user_bp[bp_index].pAddr) = BP_USER(bp_index); ICACHE_invalidate_at(user_bp[bp_index].pAddr); *(mgmt_bp_ru[0].pAddr) = mgmt_bp_ru[0].instr; ICACHE_invalidate_at(mgmt_bp_ru[0].pAddr); // dbg_puts("1. Restore after RU at ");dbg_print_word((long)mgmt_bp_ru[0].pAddr);dbg_puts("\n\r"); if (mgmt_bp_ru[1].pAddr) { *(mgmt_bp_ru[1].pAddr) = mgmt_bp_ru[1].instr; ICACHE_invalidate_at(mgmt_bp_ru[1].pAddr); // dbg_puts("2. Restore after RU at ");dbg_print_word((long)mgmt_bp_ru[1].pAddr);dbg_puts("\n\r"); } if (!g_is_ss) return; break; case BP_USER_BASE: is_user_bp = 1; bp_index = BP_GET_ID(*pAddr_curr); // dbg_puts("Restore instruction at ");dbg_print_word((long)user_bp[bp_index].pAddr);dbg_puts("\n\r"); *(user_bp[bp_index].pAddr) = user_bp[bp_index].instr; ICACHE_invalidate_at(user_bp[bp_index].pAddr); break; default: // dbg_puts("Found ordinary break at ");dbg_print_word((long)pAddr_curr);dbg_puts("\n\r"); break; } } dbg_puts("\n\r"); DBG_PRINT_REG_CHG(" Status : ", xcp->sr, reg_changed(xcp->sr, xcp_last.sr)); DBG_PRINT_REG_CHG(" Cause : ", xcp->cr, reg_changed(xcp->cr, xcp_last.cr)); DBG_PRINT_REG_CHG(" EPC : ", xcp->epc, reg_changed(xcp->epc, xcp_last.epc)); DBG_PRINT_REG_CHG("BadAddr : ", xcp->baddr, reg_changed(xcp->baddr, xcp_last.baddr)); dbg_puts("\n\r"); DBG_PRINT_REG_CHG(" MDLO : ", xcp->mdlo, reg_changed(xcp->mdlo, xcp_last.mdlo)); DBG_PRINT_REG_CHG(" MDHI : ", xcp->mdhi, reg_changed(xcp->mdhi, xcp_last.mdhi)); dbg_puts("\n\r"); dbg_puts("Registers:\n\r"); DBG_PRINT_REG_CHG(" 0 (ze) : ", xcp->regs[0], reg_changed(xcp->regs[0], xcp_last.regs[0])); DBG_PRINT_REG_CHG(" 1 (at) : ", xcp->regs[1], reg_changed(xcp->regs[1], xcp_last.regs[1])); DBG_PRINT_REG_CHG(" 2 (v0) : ", xcp->regs[2], reg_changed(xcp->regs[2], xcp_last.regs[2])); DBG_PRINT_REG_CHG(" 3 (v1) : ", xcp->regs[3], reg_changed(xcp->regs[3], xcp_last.regs[3])); dbg_puts("\n\r"); DBG_PRINT_REG_CHG(" 4 (a0) : ", xcp->regs[4], reg_changed(xcp->regs[4], xcp_last.regs[4])); DBG_PRINT_REG_CHG(" 5 (a1) : ", xcp->regs[5], reg_changed(xcp->regs[5], xcp_last.regs[5])); DBG_PRINT_REG_CHG(" 6 (a2) : ", xcp->regs[6], reg_changed(xcp->regs[6], xcp_last.regs[6])); DBG_PRINT_REG_CHG(" 7 (a3) : ", xcp->regs[7], reg_changed(xcp->regs[7], xcp_last.regs[7])); dbg_puts("\n\r"); DBG_PRINT_REG_CHG(" 8 (t0) : ", xcp->regs[8], reg_changed(xcp->regs[8], xcp_last.regs[8])); DBG_PRINT_REG_CHG(" 9 (t1) : ", xcp->regs[9], reg_changed(xcp->regs[9], xcp_last.regs[9])); DBG_PRINT_REG_CHG("10 (t2) : ", xcp->regs[10], reg_changed(xcp->regs[10], xcp_last.regs[10])); DBG_PRINT_REG_CHG("11 (t3) : ", xcp->regs[11], reg_changed(xcp->regs[11], xcp_last.regs[11])); dbg_puts("\n\r"); DBG_PRINT_REG_CHG("12 (t4) : ", xcp->regs[12], reg_changed(xcp->regs[12], xcp_last.regs[12])); DBG_PRINT_REG_CHG("13 (t5) : ", xcp->regs[13], reg_changed(xcp->regs[13], xcp_last.regs[13])); DBG_PRINT_REG_CHG("14 (t6) : ", xcp->regs[14], reg_changed(xcp->regs[14], xcp_last.regs[14])); DBG_PRINT_REG_CHG("15 (t7) : ", xcp->regs[15], reg_changed(xcp->regs[15], xcp_last.regs[15])); dbg_puts("\n\r"); DBG_PRINT_REG_CHG("16 (s0) : ", xcp->regs[16], reg_changed(xcp->regs[16], xcp_last.regs[16])); DBG_PRINT_REG_CHG("17 (s1) : ", xcp->regs[17], reg_changed(xcp->regs[17], xcp_last.regs[17])); DBG_PRINT_REG_CHG("18 (s2) : ", xcp->regs[18], reg_changed(xcp->regs[18], xcp_last.regs[18])); DBG_PRINT_REG_CHG("19 (s3) : ", xcp->regs[19], reg_changed(xcp->regs[19], xcp_last.regs[19])); dbg_puts("\n\r"); DBG_PRINT_REG_CHG("20 (s4) : ", xcp->regs[20], reg_changed(xcp->regs[20], xcp_last.regs[20])); DBG_PRINT_REG_CHG("21 (s5) : ", xcp->regs[21], reg_changed(xcp->regs[21], xcp_last.regs[21])); DBG_PRINT_REG_CHG("22 (s6) : ", xcp->regs[22], reg_changed(xcp->regs[22], xcp_last.regs[22])); DBG_PRINT_REG_CHG("23 (s7) : ", xcp->regs[23], reg_changed(xcp->regs[23], xcp_last.regs[23])); dbg_puts("\n\r"); DBG_PRINT_REG_CHG("24 (s8) : ", xcp->regs[24], reg_changed(xcp->regs[24], xcp_last.regs[24])); DBG_PRINT_REG_CHG("25 (s9) : ", xcp->regs[25], reg_changed(xcp->regs[25], xcp_last.regs[25])); DBG_PRINT_REG_CHG("26 (k0) : ", xcp->regs[26], reg_changed(xcp->regs[26], xcp_last.regs[26])); DBG_PRINT_REG_CHG("27 (k1) : ", xcp->regs[27], reg_changed(xcp->regs[27], xcp_last.regs[27])); dbg_puts("\n\r"); DBG_PRINT_REG_CHG("28 (gp) : ", xcp->regs[28], reg_changed(xcp->regs[28], xcp_last.regs[28])); DBG_PRINT_REG_CHG("29 (sp) : ", xcp->regs[29], reg_changed(xcp->regs[29], xcp_last.regs[29])); DBG_PRINT_REG_CHG("30 (fp) : ", xcp->regs[30], reg_changed(xcp->regs[30], xcp_last.regs[30])); DBG_PRINT_REG_CHG("31 (ra) : ", xcp->regs[31], reg_changed(xcp->regs[31], xcp_last.regs[31])); dbg_puts("\n\r"); dbg_puts("\n\r"); // dbg_puts(g_state_names[g_state]);dbg_puts("\n\r"); // dbg_puts("\n\r"); pInstr = pAddr_prev; // Disassemble instructions for (i=0; i < 3; i++) { dbg_print_word((long)pInstr); if (pInstr != pAddr_curr) { if (IsBreak(*pInstr) && (BreakGetType(*pInstr) == BP_USER_BASE)) { tdisasm(g_buf, (long)user_bp[BP_GET_ID(*pInstr)].pAddr, user_bp[BP_GET_ID(*pInstr)].instr, 0, &junk, &junk, &junk); dbg_puts(": b("); dbg_print_byte((char)BP_GET_ID(*pInstr)); dbg_puts(") "); } else { tdisasm(g_buf, (long)pInstr, *pInstr, 0, &junk, &junk, &junk); dbg_puts(": "); } } else { tdisasm(g_buf, (long)pInstr, *pInstr, 0, &junk, &junk, &junk); if (is_user_bp) { dbg_puts(": b("); dbg_print_byte((char)bp_index); dbg_puts(")-> "); } else { dbg_puts(": -> "); } } dbg_puts(g_buf); dbg_puts("\n\r"); pInstr++; } if (is_user_bp) { // Save original instruction mgmt_bp_ru[0].pAddr = pAddr_next; mgmt_bp_ru[0].instr = *(mgmt_bp_ru[0].pAddr); // Replace instruction with managment breakpoint *(mgmt_bp_ru[0].pAddr) = BP_MGMT_RU(bp_index); ICACHE_invalidate_at(mgmt_bp_ru[0].pAddr); mgmt_bp_ru[1].pAddr = NULL; mgmt_bp_ru[1].pAddr = 0; // dbg_puts("1. Insert RU-break at ");dbg_print_word((long)mgmt_bp_ru[0].pAddr);dbg_puts("\n\r"); // We have two possible management breaks, if previous instruction was branch or jump if (is_branch_shadow) { result = tdisasm(g_buf, (long)pAddr_prev, *pAddr_prev, 0, &junk, &branch_addr, ®); // Is jump target stored in register if (result == 3) { branch_addr = xcp->regs[reg&0x1F]; } // Save original instruction mgmt_bp_ru[1].pAddr = (uint32_t*)branch_addr; mgmt_bp_ru[1].instr = *(mgmt_bp_ru[1].pAddr); // Replace instruction with managment breakpoint *(mgmt_bp_ru[1].pAddr) = BP_MGMT_RU(bp_index); ICACHE_invalidate_at(mgmt_bp_ru[1].pAddr); // dbg_puts("2. Insert RU-break at ");dbg_print_word((long)mgmt_bp_ru[1].pAddr);dbg_puts("\n\r"); } } do { char buffer[33]; leave_isr = 0; switch(dbg_getchar()) { case 'g': g_state = STATE_RUN; leave_isr = 1; g_is_ss = 0; if (IsBreak(*pAddr_curr)) { if (BreakGetType(*pAddr_curr) != BP_USER_BASE) { xcp->epc += 4; // Skip ordinary program breaks } } break; case 'b': dbg_puts("Enter breakpoint: "); dbg_gets(buffer, sizeof(buffer)); bp_addr = strtol(buffer, NULL, 16); dbg_puts("Insert breakpoint at "); dbg_print_word(bp_addr); dbg_puts("\n\r"); user_bp[g_bp_count].pAddr = (uint32_t*)bp_addr; user_bp[g_bp_count].instr = *(user_bp[g_bp_count].pAddr); *(user_bp[g_bp_count].pAddr) = BP_USER(g_bp_count); tdisasm(g_buf, (long)user_bp[g_bp_count].pAddr, user_bp[g_bp_count].instr, 0, &junk, &junk, &junk); dbg_print_word((long)user_bp[g_bp_count].pAddr);dbg_puts(": b ");dbg_puts(g_buf);dbg_puts("\n\r"); ICACHE_invalidate_at(user_bp[g_bp_count].pAddr); g_bp_count++; break; case 's': g_state = STATE_STEP_SINGLE; g_is_ss = 1; leave_isr = 1; if (is_user_bp) break; if (IsBreak(*pAddr_curr)) { if (BreakGetType(*pAddr_curr) == BP_USER_BASE) { break; } else { xcp->epc += 4; // Skip ordinary program breaks } } singlestep(xcp, pAddr_curr, is_branch_shadow); break; case 'e': g_state = STATE_STEP_SINGLE; g_is_ss = 1; leave_isr = 1; if (is_user_bp) break; if (IsBreak(*pAddr_curr)) { if (BreakGetType(*pAddr_curr) == BP_USER_BASE) { break; } else { xcp->epc += 4; // Skip ordinary program breaks } } pInstr = pAddr_curr; do { result = instr_info(*pInstr, &info); if (result == INSTR_TYPE_JUMP) { if (info.rs == 0x1F) break; } else if (result == INSTR_TYPE_LOAD) { if (info.rs == 0x1F) break; } pInstr++; } while(1); // dbg_puts("Return found at "); dbg_print_word((long)pInstr);dbg_puts("\n\r"); set_mgmt_break(pInstr); break; case 'p': g_state = STATE_STEP_PROC; g_is_ss = 1; leave_isr = 1; if (is_user_bp) break; if (IsBreak(*pAddr_curr)) { if (BreakGetType(*pAddr_curr) == BP_USER_BASE) { break; } else { xcp->epc += 4; // Skip ordinary program breaks } } result = instr_info(*pAddr_curr, &info); if ((result == INSTR_TYPE_JUMP) && (info.flags & INSTR_FLAGS_JUMP_LINK)) { singlestep(xcp, pAddr_next, 0); // printf("Skip sub-routine\n"); } else { singlestep(xcp, pAddr_curr, is_branch_shadow); } break; default: break; } } while (!leave_isr); memcpy(&xcp_last, xcp, sizeof(EXCEPTION_CONTEXT)); } void debug_int(struct xcptcontext * xcp) { dbg_handler(xcp); } int debug_break(struct xcptcontext * xcp) { dbg_handler(xcp); return 0; } void dbg_init(void) { memset(&xcp_last, 0, sizeof(EXCEPTION_CONTEXT)); g_state = STATE_INIT; xcpt_register(EXC_BP, debug_break); }