/************************************************************** ** ** Broadcom Corp. Confidential ** Copyright 1999 - 2002 Broadcom Corp. All Rights Reserved. ** ** THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE ** AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR ** EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. ** ** File: gdb_nub.c ** Description: simple mips debug nub ** Created: Jeff Fisher, July 7, 2004 ** ** ** REVISION: ** ** $Log: $ ** **************************************************************/ #include "ministd.h" #include "bcm_mips.h" #include "serial.h" #include "cache_util.h" #undef gdb_handler /* external functions to read/write from UARTB in serial.c */ #define dbg_putc(x) serial_putc(CONSOLE_UART,x) #define dbg_getc() serial_getc(CONSOLE_UART,1) static const char g_reg_names[ROFF_NUM_REG][7] = /* "STATUS" takes 7 characters */ { "zero", "AT", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", "STATUS", "LO", "HI", "BADVA", "CAUSE", "EPC", "START", "ErrPC", "40", "41", "DEPC", "42" }; #define RS(a) (((a) >> 21) & 0x1f) /* extract the RS field */ #define RT(a) (((a) >> 16) & 0x1f) /* extract the RT field */ #define NOT_KSEG0_OR_KSEG1(x) ((0x80000000 > (unsigned int)(x)) || (0xc0000000 < (unsigned int)(x))) #define KSEG0_TO_KSEG1(x) (0xa0000000 | (unsigned int)(x)) /* Locals */ #define EX_MASK (0x1f << 2) #define TLBL_EX (0x02 << 2) #define IBE_EX (0x06 << 2) #define BP_EX (0x09 << 2) #define IN_BUF_SIZE 0x800 #define OUT_BUF_SIZE 0x800 /* PR28549, don't run dbg mode if not DEBUG is not enabled */ #ifdef BCM_DEBUG static unsigned char s_inbuf[IN_BUF_SIZE]; static unsigned char s_outbuf[OUT_BUF_SIZE]; #endif static const char s_hex[]="0123456789abcdef"; /* used for conversion to hex */ /* * The following is a mapping from our exception codes to the * signals in gdb. Definitions found in stdc headers. */ #define SIGHUP 1 /* Hangup (POSIX). */ #define SIGINT 2 /* Interrupt (ANSI). */ #define SIGQUIT 3 /* Quit (POSIX). */ #define SIGILL 4 /* Illegal instruction (ANSI). */ #define SIGTRAP 5 /* Trace trap (POSIX). */ #define SIGIOT 6 /* IOT trap (4.2 BSD). */ #define SIGABRT SIGIOT /* Abort (ANSI). */ #define SIGEMT 7 #define SIGFPE 8 /* Floating-point exception (ANSI). */ #define SIGKILL 9 /* Kill, unblockable (POSIX). */ #define SIGBUS 10 /* BUS error (4.2 BSD). */ #define SIGSEGV 11 /* Segmentation violation (ANSI). */ #define SIGSYS 12 #define SIGPIPE 13 /* Broken pipe (POSIX). */ #define SIGALRM 14 /* Alarm clock (POSIX). */ #define SIGTERM 15 /* Termination (ANSI). */ #define SIGUSR1 16 /* User-defined signal 1 (POSIX). */ #define SIGUSR2 17 /* User-defined signal 2 (POSIX). */ #define SIGCHLD 18 /* Child status has changed (POSIX). */ #define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ #define SIGPWR 19 /* Power failure restart (System V). */ #define SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */ #define SIGURG 21 /* Urgent condition on socket (4.2 BSD). */ #define SIGIO 22 /* I/O now possible (4.2 BSD). */ #define SIGPOLL SIGIO /* Pollable event occurred (System V). */ #define SIGSTOP 23 /* Stop, unblockable (POSIX). */ #define SIGTSTP 24 /* Keyboard stop (POSIX). */ #define SIGCONT 25 /* Continue (POSIX). */ #define SIGTTIN 26 /* Background read from tty (POSIX). */ #define SIGTTOU 27 /* Background write to tty (POSIX). */ #define SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */ #define SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */ #define SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */ #define SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */ static const unsigned char sigequ[16] = { SIGINT, SIGSEGV, SIGSEGV, SIGSEGV, SIGSYS, SIGSYS, SIGBUS, SIGBUS, SIGSYS, SIGTRAP, SIGILL, SIGILL, SIGFPE, SIGHUP, SIGHUP, SIGFPE }; struct break_info_t { unsigned int ref; unsigned int * addr; unsigned int instruction; }; #define BREAK ((unsigned int)0x0007000d) #define MAX_BP 50 static struct break_info_t brps[MAX_BP]; static int gdb_active = 0; void gdb_console_output (const char *buf, int len); static void skip_user_break(unsigned int regs[]); static void power_off(void); static inline char nibble_to_hex (char i); static int gdb_insert_break(unsigned int * addr, unsigned int size); static int gdb_remove_break(unsigned int * addr, unsigned int size); static char * gdb_strcpy(char * dest, const char * src); static unsigned gdb_where(void); static void gdb_halt_ejtag(void); /* PR28549, don't run dbg mode if not DEBUG is not enabled */ #ifdef BCM_DEBUG /*#define nested_jump (*(unsigned*)(0x80002004))*/ unsigned nested_jump; static volatile int nested_exception; #ifndef CONFIG_SYMBOLS const char * bsymtable_lookup(uint32_t addr, unsigned *offset) { *offset = (unsigned)-1; return ""; } const char * bsymtable_get_name(uint32_t addr, char *buf, size_t buf_len) { snprintf(buf, buf_len, "0x%08x", addr); return buf; } #endif /**************************************************************** * INPUTS: none * OUTPUTS: none * RETURNS: none * FUNCTION: Flush the instruction cache. * ****************************************************************/ static void gdb_invalidate_icache(void) { invalidate_icache_all(); } static void gdb_flush_dcache(unsigned int start, unsigned int end) { flush_dcache(start, end); } /**************************************************************** * INPUTS: inst_val - instruction value * OUTPUTS: none * RETURNS: return the offset from the branch instruction * FUNCTION: Determine the relative offset from the branch instruction. * ****************************************************************/ static int gdb_offset(unsigned int inst_val) { int val; val = inst_val & 0xffff; if (val & 0x8000) val |= 0xffff0000; return(val << 2); } /**************************************************************** * INPUTS: regs - register array * OUTPUTS: none * RETURNS: return the address of the next instruction. * FUNCTION: Set breakpoint for next single step operation. * ****************************************************************/ static unsigned int gdb_next_pc(unsigned int regs[]) { unsigned int inst, pc, op; pc = regs[ROFF_PC]; inst = *((unsigned int *)pc); if ((inst & 0xe0000000) != 0) { /* not a special,regimm,jump,branch */ if ((inst >> 28) == 5) { op = ((inst >> 26) & 0x03); switch (op) { case 0:/* BEQL */ if (regs[RS(inst)] == regs[RT(inst)]) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; case 1:/* BNEL */ if (regs[RS(inst)] != regs[RT(inst)]) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; case 2:/* BLEZL */ if (regs[RS(inst)] <= 0) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; case 3:/* BGTZL */ if (regs[RS(inst)] > 0) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; } } else { pc += 4; /* go to the next one */ } } else /* is a special,regimm,jump,branch */ { op = ((inst >> 26) & 0x07); switch (op)/* then handle each case separately */ { case 0: /* SPECIAL format */ if (((inst & 0x3f) == 8) || /* JR */ ((inst & 0x3f) == 9)) /* JALR */ { pc = regs[RS(inst)]; /* take the jump */ } else /* all other specials */ pc += 4; /* just go to the next instruction */ break; case 1: /* REGIMM */ if (!(inst & 0x140000)) /* if one of the branches */ { if (inst & 0x10000) /* BGEZ,BGEZAL,BGEZL,BGEZALL */ { if (((int)regs[RS(inst)]) >= 0) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ } else /* BLTZ,BLTZAL,BLTZL,BLTZALL */ { if (((int)regs[RS(inst)]) < 0) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ } } else pc += 4; /* go to the next instruction */ break; case 2: /* J */ case 3: /* JAL */ pc = ((inst << 2) & 0x0fffffff) | ((pc + 4) & 0xf0000000); break; case 4: /* BEQ */ if (regs[RS(inst)] == regs[RT(inst)]) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; case 5: /* BNE */ if (regs[RS(inst)] != regs[RT(inst)]) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; case 6: /* BLEZ */ if (regs[RS(inst)] <= 0) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; case 7: /* BGTZ */ if (regs[RS(inst)] > 0) /* taken */ pc += gdb_offset(inst) + 4; else pc += 8; /* not taken */ break; } } return(pc); } /**************************************************************** * INPUTS: regs - register array * set - when non-zero set the breakpoint else remove a * previously set breakpoint * OUTPUTS: none * RETURNS: none. * FUNCTION: Set breakpoint at next instruction or clear previously * set breakpoint. * ****************************************************************/ static void gdb_step(unsigned int regs[], unsigned int set) { static unsigned int stepping = 0; /* flag indicating break set */ static unsigned int inst; /* overwritten instruction */ static unsigned int *loc; /* where it was put */ if (stepping) { *loc = inst; /* put the instruction back */ stepping = 0; /* mark it cleared */ } if (set) { loc = (unsigned int *)gdb_next_pc(regs); nested_exception = 0; nested_jump = gdb_where(); if (0 == nested_exception) { nested_exception = 1; inst = *loc; /* get old instruction */ *loc = 0x0000000d; /* put in a BREAK 0 instruction */ stepping = 1; /* keep track of the change */ gdb_flush_dcache((unsigned int)loc, (unsigned int)loc + 4); } nested_jump = 0; nested_exception = 0; } } /**************************************************************** * INPUTS: c - hex character to convert * OUTPUTS: none * RETURNS: integer value or -1 if not a valid hex value. * FUNCTION: Convert hex character to integer. * ****************************************************************/ static int gdb_hex(unsigned char c) { if (c >= 'a' && c <= 'f') return(c-'a'+10); if (c >= '0' && c <= '9') return(c-'0'); if (c >= 'A' && c <= 'F') return(c-'A'+10); return(-1); } /**************************************************************** * INPUTS: none * OUTPUTS: buffer - gdb packet * RETURNS: integer value or -1 if not a valid hex value. * FUNCTION: Scan for valid gdb packet of the form $# * and acknowledge the receiption. * ****************************************************************/ static void gdb_get_packet(unsigned char *buffer) { unsigned char ch, checksum, rcksum; unsigned int count, i; do { /* scan for start char */ while ((ch = (dbg_getc() & 0x7f)) != '$') ; /* printf("Found start char\n");*/ restart: checksum = 0; rcksum = 1; /* and set this to something different */ count = 0; /* clear the count */ while (count < IN_BUF_SIZE) { ch = dbg_getc() & 0x7f; if (ch == '$') goto restart; if (ch == '#') break; checksum = checksum + ch; buffer[count++] = ch; } if (count >= IN_BUF_SIZE) continue; /* Buffer full so start over */ buffer[count++] = 0; /* terminate the input string */ rcksum = gdb_hex(dbg_getc() & 0x7f) << 4; rcksum |= gdb_hex(dbg_getc() & 0x7f); #if 0 dbg_putc('+'); return; printf("Checksum = 0x%08x\n",checksum); printf("rcksum = 0x%08x\n",rcksum); #endif if (checksum != rcksum) /* checksum bad */ { dbg_putc('-'); /* nack */ } else { /* printf("Ack \n");*/ dbg_putc('+'); /* ack */ if (buffer[2] == ':') /* is there a sequence number? */ { dbg_putc(buffer[0]); /* if so, send it back too */ dbg_putc(buffer[1]); for (i = 3; i < count; i++) /* remove the sequence number */ buffer[i-3] = buffer[i]; } } } while (checksum != rcksum); /* while we don't have a good one */ } /**************************************************************** * INPUTS: buffer - buffer to output * OUTPUTS: none * RETURNS: check sum of the buffer * FUNCTION: Send a buffer to gdb. * ****************************************************************/ static unsigned char gdb_put_buffer(const unsigned char * buf) { unsigned char sum; sum = 0; while (0 != *buf) { sum += *buf; dbg_putc(*buf); ++buf; } return sum; } /**************************************************************** * INPUTS: buffer - gdb packet * OUTPUTS: none * RETURNS: integer value or -1 if not a valid hex value. * FUNCTION: Send a gdb packet of the form $#. * ****************************************************************/ static void gdb_put_packet(unsigned char *buffer) { unsigned char checksum; do { dbg_putc('$'); /* send the start of the packet */ checksum = gdb_put_buffer(buffer); dbg_putc('#'); /* send the separator */ dbg_putc(nibble_to_hex(checksum >> 4)); /* send the checksum */ dbg_putc(nibble_to_hex(checksum)); } while ((dbg_getc() & 0x7f) != '+'); /* wait for ack */ } /**************************************************************** * INPUTS: pptr - pointer to a buffer pointer containing hex chars, * null terminated * OUTPUTS: pptr - updated buffer pointer to end of value * vptr - pointer to integer value * RETURNS: number of characters used. * FUNCTION: Find hex chars and convert to integer. * ****************************************************************/ static unsigned int gdb_hex_to_int(unsigned char **pptr, unsigned int *vptr) { unsigned int count = 0, value = 0; unsigned char *ptr = *pptr; int x; while (*ptr) { if ((x = gdb_hex(*ptr)) < 0) break; /* stop when an invalid character is found */ value = (value << 4) + x; ptr += 1; count += 1; } *pptr = ptr; *vptr = value; return(count); } /**************************************************************** * INPUTS: mem - data to convert * count - size of mem. * may_fault - protect against exceptions when non-zero * OUTPUTS: buf - hex buffer converted from chars in mem * RETURNS: End of buffer. * FUNCTION: Convert the data in mem to hex and store in buf. * ****************************************************************/ static unsigned char *gdb_mem_to_hex(unsigned char *mem, unsigned char *buf, unsigned int count) { unsigned char ch; nested_exception = 0; nested_jump = gdb_where(); if (0 == nested_exception) { nested_exception = 1; while (count--) { ch = *mem; mem += 1; *buf++ = nibble_to_hex(ch >> 4); *buf++ = nibble_to_hex(ch); } } else { buf = NULL; } nested_jump = 0; nested_exception = 0; return(buf); } /**************************************************************** * INPUTS: buf - buffer containing hex characters * count - size of buf. * OUTPUTS: mem - binary characters converted from hex in buf * RETURNS: End of mem values. * FUNCTION: Convert the hex in buf to binary values in mem. * ****************************************************************/ static unsigned char *gdb_hex_to_mem(unsigned char *buf, unsigned char *mem, unsigned int count) { unsigned char ch; unsigned int addr, len; addr = (unsigned int)mem; len = count; nested_exception = 0; nested_jump = gdb_where(); if (0 == nested_exception) { nested_exception = 1; while (count--) { ch = gdb_hex(*buf++) << 4; ch |= gdb_hex(*buf++); *mem = ch; mem += 1; } gdb_flush_dcache(addr, addr+len); } else { mem = NULL; } nested_exception = 0; nested_jump = 0; return(mem); } /**************************************************************** * INPUTS: regs - register array * OUTPUTS: none * RETURNS: Signal value. * FUNCTION: Send an exception report to gdb. * ****************************************************************/ static inline unsigned int gdb_send_exception(unsigned int regs[]) { unsigned char *ptr; unsigned int sig; sig = sigequ[(regs[ROFF_CAUSE] >> 2) & 0x0f]; ptr = s_outbuf; *ptr++ = 'T';/* EPC */ *ptr++ = nibble_to_hex(sig >> 4); *ptr++ = nibble_to_hex(sig); *ptr++ = nibble_to_hex(ROFF_PC >> 4); *ptr++ = nibble_to_hex(ROFF_PC); *ptr++ = ':'; /* separator */ ptr = gdb_mem_to_hex((unsigned char *)®s[ROFF_PC], ptr, 4); *ptr++ = ';'; /* final separator */ *ptr++ = nibble_to_hex(ROFF_CAUSE >> 4); /* CAUSE */ *ptr++ = nibble_to_hex(ROFF_CAUSE); *ptr++ = ':'; ptr = gdb_mem_to_hex((unsigned char *)®s[ROFF_CAUSE], ptr, 4); *ptr++ = ';'; *ptr++ = nibble_to_hex(ROFF_BADVA >> 4);/* BAD */ *ptr++ = nibble_to_hex(ROFF_BADVA); *ptr++ = ':'; ptr = gdb_mem_to_hex((unsigned char *)®s[ROFF_BADVA], ptr, 4); *ptr++ = ';'; *ptr++ = nibble_to_hex(ROFF_SP >> 4);/* SP */ *ptr++ = nibble_to_hex(ROFF_SP); *ptr++ = ':'; ptr = gdb_mem_to_hex((unsigned char *)®s[ROFF_SP], ptr, 4); *ptr++ = ';'; *ptr++ = nibble_to_hex(ROFF_RA >> 4);/* SP */ *ptr++ = nibble_to_hex(ROFF_RA); *ptr++ = ':'; ptr = gdb_mem_to_hex((unsigned char *)®s[ROFF_RA], ptr, 4); *ptr++ = ';'; /* send rs and rs registers to preempt gdb request at breakpoint */ if (BP_EX == (regs[ROFF_CAUSE] & EX_MASK)) { int i; i = 0; while ((i < MAX_BP) && (regs[ROFF_PC] != (unsigned int)brps[i].addr)) { ++i; } if (MAX_BP != i) { /* found breakpoint */ unsigned char rs, rt; rs = RS(brps[i].instruction); rt = RT(brps[i].instruction); if (ROFF_SP != rs) { *ptr++ = nibble_to_hex(rs >> 4); *ptr++ = nibble_to_hex(rs); *ptr++ = ':'; ptr = gdb_mem_to_hex((unsigned char *)®s[rs], ptr, 4); *ptr++ = ';'; } if ((ROFF_SP != rt) && (rt != rs)) { *ptr++ = nibble_to_hex(rt >> 4); *ptr++ = nibble_to_hex(rt); *ptr++ = ':'; ptr = gdb_mem_to_hex((unsigned char *)®s[rt], ptr, 4); *ptr++ = ';'; } } } *ptr++ = 0; /* NULL terminate */ gdb_put_packet(s_outbuf); return(sig); } #endif /**************************************************************** * INPUTS: none * OUTPUTS: none * RETURNS: none. * FUNCTION: Set up for debugging. * ****************************************************************/ void set_debug_traps(void) { dbg_putc('+'); /* ack everything */ } /* Summary: Returns bits 'e'..'b' from word 'w', Example: B_GET_BITS(0xDE,7,4)==0x0D B_GET_BITS(0xDE,3,0)=0x0E */ #define B_GET_BITS(w,e,b) (((w)>>(b))&(((unsigned)(-1))>>((sizeof(unsigned))*8-(e+1-b)))) /**************************************************************** * INPUTS: MIPS32 opcode * OUTPUTS: none * RETURNS: true if this opcode is a function calll * FUNCTION: test whether opcode is a function call * ****************************************************************/ static inline bool b_is_call(uint32_t opcode) { return(B_GET_BITS(opcode, 31, (31-5)) == 0x03) /* opcode jal */ || (B_GET_BITS(opcode, 31, (31-5)) == 0 /* opcode reg-op */ && B_GET_BITS(opcode, 5, 0) == 0x09); /* function jalr */ } /**************************************************************** * INPUTS: cause - value of CP0_CAUSE * regs - register array containing gp registers and select * CP0 registers. * OUTPUTS: none * RETURNS: none. * FUNCTION: main gdb exception handler. * ****************************************************************/ void bcm_test_regs( unsigned int *regs) { unsigned int i; const unsigned int *base; char buf[32]; unsigned cause; for (i = 0; i < ROFF_NUM_REG; ++i) { if (i % 4 == 0) { printf("\n"); } #ifndef BCM_DEBUG printf("%6s = 0x%08x",g_reg_names[i], regs[i]); } #else #if 0 { /* balance { so editor stops complaining. Never enable this conditional */ #endif printf("%6s = %-10s",g_reg_names[i],bsymtable_get_name(regs[i], buf, sizeof(buf))); } cause = regs[ROFF_CAUSE] & EX_MASK; if ((TLBL_EX != cause) && (IBE_EX != cause)) { base = (unsigned int*)regs[ROFF_SP]; printf("\nBacktrace PC(%s) ", bsymtable_get_name(regs[ROFF_PC], buf, sizeof(buf))); printf("ra(%s):\n", bsymtable_get_name(regs[ROFF_RA], buf, sizeof(buf))); for (i=0;i<256;i++,base++) { unsigned offset; uint32_t addr; const char *symbol; addr = *base; if ((addr&(0x3))!=0) { /* address shall be 32 bit alligned */ continue; } symbol = bsymtable_lookup(addr, &offset); if (offset>2048) { /* test whether the symbol is good */ continue; } if (!b_is_call( ((uint32_t *)addr)[-2])) { /* test whether address points to call instruction */ continue; } printf("\t%s+0x%x\n", symbol, offset); } base = (unsigned int*)regs[ROFF_PC]; base -= 4; for (i = 0; i < 24; ++i) { if (i % 4 == 0) { printf("\nCode @ %08x: ",&base[i]); } printf("%08x, ",base[i]); } } printf("\n"); #endif } /**************************************************************** * INPUTS: cause - value of CP0_CAUSE * regs - register array containing gp registers and select * CP0 registers. * OUTPUTS: none * RETURNS: none. * FUNCTION: main gdb exception handler. * ****************************************************************/ extern void bprint_flush(void); void gdb_handler(unsigned int regs[], unsigned int cause) { #ifdef BCM_DEBUG unsigned char *ptr; unsigned int sigval, addr, length; unsigned int do_power_off; sigval = 0; do_power_off = 0; regs[0] = 0; if (0 == gdb_active) { #endif console_shutdown(); bcm_test_regs(regs); #ifdef BCM_DEBUG gdb_active = 1; #ifndef ENABLE_GDB_SERIAL bprint_flush(); regs[ROFF_RA] = regs[ROFF_PC]; regs[ROFF_PC] = (unsigned int)&gdb_halt_ejtag; return; #endif printf("Waiting for gdb to connect ...\n"); } gdb_step(regs, 0); /* clear any previous stepping action */ cause &= EX_MASK; if ((TLBL_EX != cause) && (IBE_EX != cause)) { skip_user_break(regs); } else { /* we called function by invalid ptr causing this exception */ if (NOT_KSEG0_OR_KSEG1(regs[ROFF_PC])) { regs[ROFF_PC] = regs[ROFF_RA] - 8; /* adjust pc for gdb */ } } sigval = gdb_send_exception(regs); /* process input commands */ while (1) { s_outbuf[0] = 0; gdb_get_packet(s_inbuf); switch (s_inbuf[0]) { case '?': /* report last signal */ s_outbuf[0] = 'S'; s_outbuf[1] = nibble_to_hex(sigval >> 4); s_outbuf[2] = nibble_to_hex(sigval); s_outbuf[3] = 0; break; case 'd': /* toggle debug flag*/ case 'b': /* change baud rate */ break; case 'g': /* send the registers */ ptr = s_outbuf; /* start at the beginning */ ptr = gdb_mem_to_hex((unsigned char *)regs, ptr, ROFF_NUM_REG*4); *ptr = 0; break; case 'G': /* set register values */ ptr = s_inbuf+1; /* starting position */ if (gdb_hex_to_mem(ptr, (unsigned char *)regs, ROFF_NUM_REG*4)) { gdb_strcpy(s_outbuf, "OK"); } else { gdb_strcpy(s_outbuf, "E01"); } break; case 'm': /* read memory */ ptr = s_inbuf+1; if (gdb_hex_to_int(&ptr, &addr) && *ptr++ == ',' && gdb_hex_to_int(&ptr, &length)) { ptr = gdb_mem_to_hex((unsigned char *)addr, s_outbuf, length); if (ptr != NULL) { *ptr = 0; } else /* if some error */ { gdb_strcpy(s_outbuf, "E02"); } } else /* command no good */ { gdb_strcpy(s_outbuf, "E01"); } break; case 'M': /* update memory */ ptr = s_inbuf+1; /* start of parm data */ if (gdb_hex_to_int(&ptr, &addr) && *ptr++ == ',' && gdb_hex_to_int(&ptr, &length) && *ptr++ == ':') { if (gdb_hex_to_mem(ptr, (unsigned char *)addr, length)) { gdb_strcpy(s_outbuf, "OK"); } else { gdb_strcpy(s_outbuf, "E02"); } } else { gdb_strcpy(s_outbuf, "E01"); } break; case 's': /* handle step as continue */ ptr = s_inbuf+1; if (gdb_hex_to_int(&ptr, &addr)) regs[ROFF_PC] = addr; gdb_step(regs, 1); /* set a step breakpoint */ gdb_invalidate_icache(); return; case 'c': /* continue */ ptr = s_inbuf+1; if (gdb_hex_to_int(&ptr, &addr)) regs[ROFF_PC] = addr; gdb_invalidate_icache(); return; case 'D': case 'k': /* kill the target program */ gdb_strcpy(s_outbuf, "OK"); do_power_off = 1; break; case 'H': gdb_strcpy(s_outbuf, "OK"); break; case 'Z': ptr = s_inbuf + 1; if (('0' == ptr[0]) && (',' == ptr[1])) { /* sw breakpoint */ ptr += 2; if (gdb_hex_to_int(&ptr, &addr) && *ptr++ == ',' && gdb_hex_to_int(&ptr, &length) && (0 ==gdb_insert_break((unsigned int *)addr, length))) { gdb_strcpy(s_outbuf, "OK"); gdb_invalidate_icache(); } else { gdb_strcpy(s_outbuf, "E02"); } } else { gdb_strcpy(s_outbuf, "E02"); } break; case 'z': ptr = s_inbuf + 1; if (('0' == ptr[0]) && (',' == ptr[1])) { /* sw breakpoint */ ptr += 2; if (gdb_hex_to_int(&ptr, &addr) && *ptr++ == ',' && gdb_hex_to_int(&ptr, &length) && (0 == gdb_remove_break((unsigned int *)addr, length))) { gdb_strcpy(s_outbuf, "OK"); gdb_invalidate_icache(); } else { gdb_strcpy(s_outbuf, "E02"); } } else { gdb_strcpy(s_outbuf, "E01"); } break; default: break; } /* switch */ gdb_put_packet(s_outbuf); if (0 != do_power_off) { power_off(); } } /* while (1) */ #else while (1) { __asm__("wait"); } #endif } char nibble_to_hex (char i) { return s_hex[i & 0xf]; } #ifdef BCM_DEBUG void gdb_console_output (const char *buf, int len) { unsigned char checksum; unsigned char hi_nibble; unsigned char lo_nibble; dbg_putc('$'); /* send the start of the packet */ dbg_putc('O'); checksum = 'O'; while (len--) { hi_nibble = nibble_to_hex(*buf >> 4); lo_nibble = nibble_to_hex(*buf++); checksum += hi_nibble; checksum += lo_nibble; dbg_putc(hi_nibble); dbg_putc(lo_nibble); } dbg_putc('#'); /* send the separator */ dbg_putc(nibble_to_hex(checksum >> 4)); /* send the checksum */ dbg_putc(nibble_to_hex(checksum)); return; } #define SUN_TOP_CTRL_RESET_CTRL ((unsigned int *)0xB0404008) #define SUN_TOP_CTRL_SW_RESET ((unsigned int *)0xB0404014) void power_off(void) { *SUN_TOP_CTRL_RESET_CTRL = 0x0; *SUN_TOP_CTRL_RESET_CTRL = 0x8; *SUN_TOP_CTRL_SW_RESET = 0x80000000; } void skip_user_break(unsigned int regs[]) { unsigned int inst; inst = *((unsigned int *)regs[ROFF_PC]); if (0xd == (0x3f & inst)) { /* breakpoint */ if (0x2 == (inst >> 16)) { /* user breakpoint */ regs[ROFF_PC] += 4; /* skip to the next instruction */ } } } int gdb_insert_break(unsigned int * addr, unsigned int size) { int i; if (4 != size) { return 1; } i = MAX_BP; if (BREAK == *addr) { i = 0; while ((i < MAX_BP) && (addr != brps[i].addr)) { ++i; } } if (MAX_BP == i) { i = 0; while ((i < MAX_BP) && (NULL != brps[i].addr)) { ++i; } } if (MAX_BP != i) { if (0 == brps[i].ref) { ++brps[i].ref; brps[i].addr = addr; brps[i].instruction = *addr; *(unsigned int *)(KSEG0_TO_KSEG1(addr)) = BREAK; } else { brps[i].ref++; } } else { /* no free slots */ return 1; } return 0; } int gdb_remove_break(unsigned int * addr, unsigned int size) { int i; if (4 != size) { return 1; } i = 0; while ((i < MAX_BP) && (addr != brps[i].addr)) { ++i; } if (MAX_BP != i) { if (0 == (--brps[i].ref)) { *(unsigned int *)(KSEG0_TO_KSEG1(addr)) = brps[i].instruction; brps[i].addr = 0; brps[i].instruction = 0; brps[i].ref = 0; } } else { return 1; } return 0; } char * gdb_strcpy(char * dest, const char * src) { char * old_dest; old_dest = dest; while (0 != *src) { *dest = *src; ++dest; ++src; } *dest = 0; return old_dest; } unsigned gdb_where(void) { unsigned val; __asm("or %0, $0, $31" : "=r"(val)); return val; } void gdb_control_breakpoints(unsigned int enable) { int i; i = 0; if (0 != gdb_active) { while ((i < MAX_BP) && (NULL != brps[i].addr)) { *(unsigned int *)(KSEG0_TO_KSEG1(brps[i].addr)) = (0 == enable) ? brps[i].instruction : BREAK ; ++i; } gdb_invalidate_icache(); } } #endif #ifdef CONFIG_WATCHDOG #include "os_cpu.h" #endif void gdb_halt_ejtag(void) { #ifdef CONFIG_WATCHDOG unsigned int cpu_sr; /* __asm__("sdbbp") will put the cpu in debug mode that will freeze watchdog timer */ /* we also need to disable interrupt */ OS_ENTER_CRITICAL(); while (1); /* if we are here, it can't recover */ //OS_EXIT_CRITICAL(); #else __asm__("sdbbp ; 1: b 1b; nop"); #endif }