/* * Low-level support for LM32 remote debuging with GDB. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include // #include #include "gdb.h" #include "gdb_uart.h" /* Enable system call support */ #undef GDB_SYSCALLS_ENABLED 1 /* Enable open/close system calls */ #undef GDB_OPEN_CLOSE_SYSCALLS_ENABLED /* Enable support for z packets */ #undef GDB_HARDWARE_BREAKPOINTS_ENABLED /* Enable support for q packets */ #undef GDB_ECLIPSE_SUPPORT /* Enable support for X packets */ #define GDB_BINARY_DOWNLOAD_ENABLED /* Enable support for P packets */ #define GDB_P_ENABLED /* Enable support for remote stub debugging */ #undef GDB_REMOTE_DEBUG_ENABLED /* Enable support for run-length encoding */ #undef GDB_RLE_ENABLED /* Enable support for restart packets */ #undef GDB_RESTART_ENABLED /* Exception IDs */ #define LM32_EXCEPTION_RESET 0x0 #define LM32_EXCEPTION_INST_BREAKPOINT 0x1 #define LM32_EXCEPTION_INST_BUS_ERROR 0x2 #define LM32_EXCEPTION_DATA_BREAKPOINT 0x3 #define LM32_EXCEPTION_DATA_BUS_ERROR 0x4 #define LM32_EXCEPTION_DIVIDE_BY_ZERO 0x5 #define LM32_EXCEPTION_INTERRUPT 0x6 #define LM32_EXCEPTION_SYSTEM_CALL 0x7 /* Breakpoint instruction */ #define LM32_BREAK 0xac000002UL /* BUFMAX defines the maximum number of characters in inbound/outbound buffers */ #define BUFMAX 400 /* Function prototypes */ #ifdef GDB_REMOTE_DEBUG_ENABLED static int gdb_write (char *data, int len); static int gdb_puts (char *str); static void gdb_putint (int num); #endif static unsigned char *getpacket (void); static unsigned char *getpacket (void); /* For integer to ASCII conversion */ static const char hexchars[]="0123456789abcdef"; /* This numbering must be consistant with GDBs numbering in gdb/lm32-tdep.c */ enum regnames { R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, GP, FP, SP, RA, EA, BE, PC, EID, NUM_REGS }; /* I/O packet buffers */ static unsigned char remcomInBuffer[BUFMAX]; static unsigned char remcomOutBuffer[BUFMAX]; /* Set by debugger to indicate that when handling memory faults (bus errors), the handler should set the mem_err flag and skip over the faulting instruction */ static volatile int may_fault; /* Set by bus error exception handler, this indicates to caller of mem2hex, * hex2mem or bin2mem that there has been an error. */ static volatile int mem_err; /* Indicates if we're single stepping */ static unsigned char stepping; static unsigned *seq_ptr; static unsigned seq_insn; static unsigned *branch_ptr; static unsigned branch_insn; static char branch_step; #ifdef GDB_REMOTE_DEBUG_ENABLED /* debug > 0 prints ill-formed commands in valid packets & checksum errors */ static int remote_debug; #endif /* interrupt handler */ static void (*intr_handler)(void); /* Convert ch from a hex digit to an int */ static int hex (unsigned char ch) { if (ch >= 'a' && ch <= 'f') return ch-'a'+10; if (ch >= '0' && ch <= '9') return ch-'0'; if (ch >= 'A' && ch <= 'F') return ch-'A'+10; return -1; } /* Scan for the sequence $# */ static unsigned char * getpacket (void) { unsigned char *buffer = &remcomInBuffer[0]; unsigned char checksum; unsigned char xmitcsum; int count; char ch; while (1) { /* wait around for the start character, ignore all other characters */ while ((ch = _gdb_read_char ()) != '$') ; retry: checksum = 0; xmitcsum = -1; count = 0; /* now, read until a # or end of buffer is found */ while (count < BUFMAX) { ch = _gdb_read_char (); if (ch == '$') goto retry; if (ch == '#') break; checksum = checksum + ch; buffer[count] = ch; count = count + 1; } buffer[count] = 0; if (ch == '#') { ch = _gdb_read_char (); xmitcsum = hex (ch) << 4; ch = _gdb_read_char (); xmitcsum += hex (ch); if (checksum != xmitcsum) { #ifdef GDB_REMOTE_DEBUG_ENABLED if (remote_debug) { gdb_puts ("Bad checksum: "); gdb_putint (checksum); gdb_puts (" != "); gdb_putint (xmitcsum); gdb_puts ("\n"); } #endif _gdb_write_char ('-'); /* failed checksum */ } else { _gdb_write_char ('+'); /* successful transfer */ /* if a sequence char is present, reply the sequence ID */ if (buffer[2] == ':') { _gdb_write_char (buffer[0]); _gdb_write_char (buffer[1]); return &buffer[3]; } return &buffer[0]; } } } } /* Send the packet in buffer. */ #ifdef GDB_RLE_ENABLED static void putpacket (unsigned char *buffer) { unsigned char checksum; int count; unsigned char ch; int run_length; int run_idx; char run_length_char; /* $#. */ do { _gdb_write_char ('$'); checksum = 0; count = 0; while (ch = buffer[count]) { /* Transmit character */ _gdb_write_char (ch); checksum += ch; count += 1; /* Determine how many consecutive characters there are that are the * same as the character we just transmitted */ run_length = 0; run_idx = count; while ((buffer[run_idx++] == ch) && (run_length < 97)) run_length++; /* Encode run length as an ASCII character */ run_length_char = (char)(run_length + 29); if ( (run_length >= 3) && (run_length_char != '$') && (run_length_char != '#') && (run_length_char != '+') && (run_length_char != '-') ) { /* Transmit run-length */ _gdb_write_char ('*'); checksum += '*'; _gdb_write_char (run_length_char); checksum += run_length_char; count += run_length; } } _gdb_write_char ('#'); _gdb_write_char (hexchars[(checksum >> 4) & 0xf]); _gdb_write_char (hexchars[checksum & 0xf]); } while (_gdb_read_char () != '+'); } #else static void putpacket (unsigned char *buffer) { unsigned char checksum; int count; unsigned char ch; /* $#. */ do { _gdb_write_char ('$'); checksum = 0; count = 0; while (ch = buffer[count]) { _gdb_write_char (ch); checksum += ch; count += 1; } _gdb_write_char ('#'); _gdb_write_char (hexchars[checksum >> 4]); _gdb_write_char (hexchars[checksum % 16]); } while (_gdb_read_char () != '+'); } #endif #ifdef GDB_REMOTE_DEBUG_ENABLED /* Make gdb write n bytes to stdout (not assumed to be null-terminated). Returns: number of bytes written */ static int gdb_write (char *data, int len) { char *buf, *cpy; int i; char temp[100]; buf = temp; buf[0] = 'O'; i = 0; while (i < len) { for (cpy = buf + 1; i < len && cpy < buf + sizeof (temp) - 3; i++) { *cpy++ = hexchars[data[i] >> 4]; *cpy++ = hexchars[data[i] & 0x0F]; } *cpy = 0; putpacket (buf); } return len; } /* Make gdb write a null-terminated string to stdout. Returns: the length of the string */ static int gdb_puts (char *str) { return gdb_write (str, strlen (str)); } /* Make gdb write an integer to stdout. */ static void gdb_putint (int num) { char buf[9]; int cnt; char *ptr; int digit; ptr = buf; for (cnt = 7 ; cnt >= 0 ; cnt--) { digit = (num >> (cnt * 4)) & 0xf; if (digit <= 9) *ptr++ = (char) ('0' + digit); else *ptr++ = (char) ('a' - 10 + digit); } *ptr = (char) 0; gdb_puts (buf); } #endif static void allow_nested_exception () { mem_err = 0; may_fault = 1; } static void disallow_nested_exception () { mem_err = 0; may_fault = 0; } /* Convert the memory pointed to by mem into hex, placing result in buf. * Return a pointer to the last char put in buf ('\0'), in case of mem fault, * return NULL. */ static unsigned char * mem2hex (unsigned char *mem, unsigned char *buf, int count) { unsigned char ch; #if 0 /* Some h/w registers require word/half-word access, so treat them as a special case */ if ((count == 4) && (((unsigned)mem & 3) == 0)) { unsigned long val; int i; /* Read 32-bit value from memory */ val = *(unsigned long *)mem; /* Return NULL if the memory access caused an exception */ if (mem_err) return NULL; /* Convert 32-bit value to a hex string */ for (i = 28; i >= 0; i -= 4) *buf++ = hexchars[(val >> i) & 0xf]; } else if ((count == 2) && (((unsigned)mem & 1) == 0)) { unsigned short val; int i; /* Read 16-bit value from memory */ val = *(unsigned short *)mem; /* Return NULL if the memory access caused an exception */ if (mem_err) return NULL; /* Convert 16-bit value to a hex string */ for (i = 12; i >= 0; i -= 4) *buf++ = hexchars[(val >> i) & 0xf]; } else #endif { while (count-- > 0) { ch = *mem++; if (mem_err) return NULL; *buf++ = hexchars[(ch >> 4) & 0xf]; *buf++ = hexchars[ch & 0xf]; } } *buf = '\0'; return buf; } /* convert the hex array pointed to by buf into binary to be placed in mem * return a pointer to the character AFTER the last byte written */ static char * hex2mem (unsigned char *buf, unsigned char *mem, int count) { int i; unsigned char ch; #if 0 /* Some h/w registers require word/half-word access, so treat them as a special case */ if ((count == 4) && (((unsigned)mem & 3) == 0)) { unsigned long val; int i; /* Convert hex data to 32-bit value */ val = 0; for (i = 24; i >= 0; i -= 4) val |= hex (*buf++) << i; /* Attempt to write data to memory */ *(unsigned long *)mem = val; /* Return NULL if write caused an exception */ if (mem_err) return NULL; mem += 4; } else if ((count == 2) && (((unsigned)mem & 1) == 0)) { unsigned short val; /* Convert hex data to 16-bit value */ val = 0; for (i = 12; i >= 0; i -= 4) val |= hex (*buf++) << i; /* Attempt to write data to memory */ *(unsigned short *)mem = val; /* Return NULL if write caused an exception */ if (mem_err) return NULL; mem += 2; } else #endif { for (i = 0; i < count; i++) { /* Convert hex data to 8-bit value */ ch = hex (*buf++) << 4; ch |= hex (*buf++); /* Attempt to write data to memory */ *mem++ = ch; /* Return NULL if write caused an exception */ if (mem_err) return NULL; } } return mem; } #ifdef GDB_BINARY_DOWNLOAD_ENABLED /* Copy the binary data pointed to by buf to mem and * return a pointer to the character AFTER the last byte written * $, # and 0x7d are escaped with 0x7d */ static char * bin2mem (unsigned char *buf, unsigned char *mem, int count) { int i; unsigned char c; #if 0 /* Some h/w registers require word/half-word access, so treat them as a special case */ if ((count == 4) && (((unsigned)mem & 3) == 0)) { unsigned long val; int i; /* Convert binary data to 32-bit value */ val = 0; for (i = 24; i >= 0; i -= 8) { c = *buf++; if (c == 0x7d) c = *buf++ ^ 0x20; val |= c << i; } /* Attempt to write value to memory */ *(unsigned long *)mem = val; /* Return NULL if write caused an exception */ if (mem_err) return NULL; mem += 4; } else if ((count == 2) && (((unsigned)mem & 1) == 0)) { unsigned short val; int i; /* Convert binary data to 16-bit */ val = 0; for (i = 8; i >= 0; i -= 8) { c = *buf++; if (c == 0x7d) c = *buf++ ^ 0x20; val |= c << i; } /* Attempt to write value to memory */ *(unsigned short *)mem = val; /* Return NULL if write caused an exception */ if (mem_err) return NULL; mem += 2; } else #endif { for (i = 0; i < count; i++) { /* Convert binary data to unsigned byte */ c = *buf++; if (c == 0x7d) c = *buf++ ^ 0x20; /* Attempt to write value to memory */ *mem++ = c; /* Return NULL if write caused an exception */ if (mem_err) return NULL; } } return mem; } #endif /* Convert the exception identifier to a signal number. */ static int computeSignal (int eid) { switch (eid) { case LM32_EXCEPTION_RESET: return 0; case LM32_EXCEPTION_INTERRUPT: return SIGINT; case LM32_EXCEPTION_DATA_BREAKPOINT: case LM32_EXCEPTION_INST_BREAKPOINT: return SIGTRAP; case LM32_EXCEPTION_INST_BUS_ERROR: case LM32_EXCEPTION_DATA_BUS_ERROR: return SIGSEGV; case LM32_EXCEPTION_DIVIDE_BY_ZERO: return SIGFPE; } return SIGHUP; /* default for things we don't know about */ } /* Flush the instruction cache */ static void flush_i_cache (void) { /* Executing this does no harm on CPUs without a cache */ /* We flush DCache as well incase debugger has accessed memory directly */ __asm__ __volatile__ ("wcsr ICC, %0\n" "nop\n" "nop\n" "nop\n" "wcsr DCC, %0\n" "nop\n" "nop\n" "nop" : : "r" (1) ); } /* * While we find nice hex chars, build an int. * Return number of chars processed. */ static int hexToInt (char **ptr, int *intValue) { int numChars = 0; int hexValue; *intValue = 0; while (**ptr) { hexValue = hex(**ptr); if (hexValue < 0) break; *intValue = (*intValue << 4) | hexValue; numChars ++; (*ptr)++; } return (numChars); } /* Convert a register to a hex string */ static unsigned char * reg2hex (unsigned val, unsigned char *buf) { *buf++ = hexchars[(val >> 28) & 0xf]; *buf++ = hexchars[(val >> 24) & 0xf]; *buf++ = hexchars[(val >> 20) & 0xf]; *buf++ = hexchars[(val >> 16) & 0xf]; *buf++ = hexchars[(val >> 12) & 0xf]; *buf++ = hexchars[(val >> 8) & 0xf]; *buf++ = hexchars[(val >> 4) & 0xf]; *buf++ = hexchars[val & 0xf]; return buf; } #ifdef GDB_HARDWARE_BREAKPOINTS_ENABLED /* Set a h/w breakpoint at the given address */ static int set_hw_breakpoint(int address, int length) { int bp; /* Find a free break point register and then set it */ __asm__ ("rcsr %0, BP0" : "=d" (bp)); if ((bp & 0x01) == 0) { __asm__ ("wcsr BP0, %0" : : "d" (address | 1)); return 1; } __asm__ ("rcsr %0, BP1" : "=d" (bp)); if ((bp & 0x01) == 0) { __asm__ ("wcsr BP1, %0" : : "d" (address | 1)); return 1; } __asm__ ("rcsr %0, BP2" : "=d" (bp)); if ((bp & 0x01) == 0) { __asm__ ("wcsr BP2, %0" : : "d" (address | 1)); return 1; } __asm__ ("rcsr %0, BP3" : "=d" (bp)); if ((bp & 0x01) == 0) { __asm__ ("wcsr BP3, %0" : : "d" (address | 1)); return 1; } /* No free breakpoint registers */ return -1; } /* Remove a h/w breakpoint which should be set at the given address */ static int disable_hw_breakpoint(int address, int length) { int bp; /* Try to find matching breakpoint register */ __asm__ ("rcsr %0, BP0" : "=d" (bp)); if ((bp & 0xfffffffc) == (address & 0xfffffffc)) { __asm__ ("wcsr BP0, %0" : : "d" (0)); return 1; } __asm__ ("rcsr %0, BP1" : "=d" (bp)); if ((bp & 0xfffffffc) == (address & 0xfffffffc)) { __asm__ ("wcsr BP1, %0" : : "d" (0)); return 1; } __asm__ ("rcsr %0, BP2" : "=d" (bp)); if ((bp & 0xfffffffc) == (address & 0xfffffffc)) { __asm__ ("wcsr BP2, %0" : : "d" (0)); return 1; } __asm__ ("rcsr %0, BP3" : "=d" (bp)); if ((bp & 0xfffffffc) == (address & 0xfffffffc)) { __asm__ ("wcsr BP3, %0" : : "d" (0)); return 1; } /* Breakpoint not found */ return -1; } #endif /* This function does all command procesing for interfacing to gdb. * The error codes we return are errno numbers */ void _handle_exception (unsigned int *registers) { int tt; /* Trap type */ int sigval; int addr; int length; char *ptr; unsigned int *sp; int err; unsigned int dc; int pathlen; int retcode; int reterrno; int reg; unsigned char status; unsigned insn; unsigned opcode; unsigned branch_target; /* Check for bus error caused by this code (rather than the program being debugged) */ if (may_fault && (registers[EID] == LM32_EXCEPTION_DATA_BUS_ERROR)) { #ifdef GDB_REMOTE_DEBUG_ENABLED if (remote_debug) gdb_puts ("Bus error in monitor\n"); #endif /* Indicate that a fault occured */ mem_err = 1; /* Skip over faulting instruction */ registers[PC] += 4; /* Resume execution */ return; } if (stepping) { /* Remove breakpoints */ *seq_ptr = seq_insn; if (branch_step) *branch_ptr = branch_insn; stepping = 0; } /* Convert exception ID to a signal number */ sigval = computeSignal(registers[EID]); if (sigval == SIGINT) { if (intr_handler != NULL) { (*intr_handler)(); return; } else { _gdb_ack_interrupt (); } } /* Set pointer to start of output buffer */ ptr = remcomOutBuffer; #ifdef GDB_SYSCALLS_ENABLED if (registers[EID] == LM32_EXCEPTION_SYSTEM_CALL) { /*_gpio.OutData = 0x82;*/ /* Calls to strlen in the following code may cause bus errors */ allow_nested_exception (); /* Pass system calls to the debugger */ switch (registers[R8]) { case SYS_exit: *ptr++ = 'W'; *ptr++ = hexchars[(registers[R1] >> 4) & 0xf]; *ptr++ = hexchars[registers[R1] & 0xf]; *ptr++ = 0; intr_handler = NULL; break; #ifdef GDB_OPEN_CLOSE_SYSCALLS_ENABLED case SYS_open: memcpy (ptr, "Fopen,", 6); ptr += 6; ptr = reg2hex(registers[R1], ptr); *ptr++ = '/'; pathlen = strlen((unsigned char *)registers[R1]) + 1; ptr = reg2hex(pathlen, ptr); *ptr++ = ','; ptr = reg2hex(registers[R2], ptr); *ptr++ = ','; ptr = reg2hex(registers[R3], ptr); *ptr++ = 0; break; case SYS_close: memcpy (ptr, "Fclose,", 7); ptr += 7; ptr = reg2hex(registers[R1], ptr); *ptr++ = 0; break; #endif case SYS_read: memcpy (ptr, "Fread,", 6); ptr += 6; ptr = reg2hex(registers[R1], ptr); *ptr++ = ','; ptr = reg2hex(registers[R2], ptr); *ptr++ = ','; ptr = reg2hex(registers[R3], ptr); *ptr++ = 0; break; case SYS_write: memcpy (ptr, "Fwrite,", 7); ptr += 7; ptr = reg2hex(registers[R1], ptr); *ptr++ = ','; ptr = reg2hex(registers[R2], ptr); *ptr++ = ','; ptr = reg2hex(registers[R3], ptr); *ptr++ = 0; break; case 231: /*_gpio.OutData = 0x90;*/ intr_handler = registers[R1]; /* Skip over instruction */ registers[PC] += 4; return; case 232: intr_handler = NULL; /* Skip over instruction */ registers[PC] += 4; return; default: /* Unknown or unsupported system call */ /* Indicate to calling program that its not supported */ registers[R1] = -1; registers[R2] = 0; registers[R3] = ENOSYS; /* Skip over instruction */ registers[PC] += 4; return; } /* Check to see if a bus error occured */ if (mem_err) { disallow_nested_exception (); /* Indicate error to calling program */ registers[R1] = -1; registers[R2] = 0; registers[R3] = ENOSYS; /* Skip over scall instruction */ registers[PC] += 4; return; } disallow_nested_exception (); } else #endif /* GDB_SYSCALLS_ENABLED */ { /* reply to host that an exception has occurred */ *ptr++ = 'T'; *ptr++ = hexchars[(sigval >> 4) & 0xf]; *ptr++ = hexchars[sigval & 0xf]; *ptr++ = hexchars[(PC >> 4) & 0xf]; *ptr++ = hexchars[PC & 0xf]; *ptr++ = ':'; ptr = mem2hex ((unsigned char *)®isters[PC], ptr, 4); *ptr++ = ';'; *ptr++ = hexchars[(SP >> 4) & 0xf]; *ptr++ = hexchars[SP & 0xf]; *ptr++ = ':'; ptr = mem2hex ((unsigned char *)®isters[SP], ptr, 4); *ptr++ = ';'; *ptr++ = 0; } if (registers[EID]) putpacket (remcomOutBuffer); while (1) { remcomOutBuffer[0] = 0; ptr = getpacket(); switch (*ptr++) { case '?': /* return last signal */ remcomOutBuffer[0] = 'S'; remcomOutBuffer[1] = hexchars[sigval >> 4]; remcomOutBuffer[2] = hexchars[sigval & 0xf]; remcomOutBuffer[3] = 0; break; #ifdef GDB_REMOTE_DEBUG_ENABLED case 'd': /* toggle debug flag */ remote_debug = !(remote_debug); break; #endif case 'g': /* return the value of the CPU registers */ ptr = remcomOutBuffer; ptr = mem2hex ((unsigned char *)registers, ptr, NUM_REGS * 4); break; case 'G': /* set the value of the CPU registers */ hex2mem (ptr, (unsigned char *)registers, NUM_REGS * 4); strcpy (remcomOutBuffer, "OK"); break; #ifdef GDB_P_ENABLED case 'p': /* Return the value of the specified register */ if (hexToInt (&ptr, ®)) { ptr = remcomOutBuffer; ptr = mem2hex ((unsigned char *)®isters[reg], ptr, 4); } else strcpy (remcomOutBuffer, "E22"); break; case 'P': /* Set the specified register to the given value */ if (hexToInt (&ptr, ®) && *ptr++ == '=') { hex2mem (ptr, (unsigned char *)®isters[reg], 4); strcpy (remcomOutBuffer, "OK"); } else strcpy (remcomOutBuffer, "E22"); break; #endif case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ /* Try to read %x,%x. */ if (hexToInt (&ptr, &addr) && *ptr++ == ',' && hexToInt (&ptr, &length) && length < (sizeof(remcomOutBuffer)/2)) { allow_nested_exception (); if (NULL == mem2hex((unsigned char *)addr, remcomOutBuffer, length)) strcpy (remcomOutBuffer, "E14"); disallow_nested_exception (); } else strcpy (remcomOutBuffer,"E22"); break; case 'M': /* MAA.AA,LLLL: Write LLLL bytes at address AA.AA */ /* Try to read '%x,%x:'. */ if (hexToInt (&ptr, &addr) && *ptr++ == ',' && hexToInt (&ptr, &length) && *ptr++ == ':') { allow_nested_exception (); if (hex2mem(ptr, (char *)addr, length)) strcpy (remcomOutBuffer, "OK"); else strcpy (remcomOutBuffer, "E14"); disallow_nested_exception (); } else strcpy (remcomOutBuffer, "E22"); break; #ifdef GDB_BINARY_DOWNLOAD_ENABLED case 'X': /* XAA.AA,LLLL: Write LLLL bytes at address AA.AA */ /* Try to read '%x,%x:'. */ if (hexToInt (&ptr, &addr) && *ptr++ == ',' && hexToInt (&ptr, &length) && *ptr++ == ':') { allow_nested_exception (); if (bin2mem (ptr, (unsigned char *)addr, length)) strcpy (remcomOutBuffer, "OK"); else strcpy (remcomOutBuffer, "E14"); disallow_nested_exception (); } else strcpy (remcomOutBuffer, "E22"); break; #endif #if 0 case 'C': /* CSS;AA..AA Continue with signal SS at address AA..AA(optional) */ /* Set signal number */ if (hexToInt (&ptr, &sigval)) registers[EID] = sigval; /* try to read optional parameter, pc unchanged if no parm */ if (*ptr == ';') { ptr++; if (hexToInt (&ptr, &addr)) registers[PC] = addr; } flush_i_cache (); return; #endif case 'c': /* cAA..AA Continue at address AA..AA(optional) */ /* try to read optional parameter, pc unchanged if no parm */ if (hexToInt (&ptr, &addr)) registers[PC] = addr; flush_i_cache (); return; case 's': /* step at address AA (optional) */ /* try to read optional parameter, pc unchanged if no parm */ if (hexToInt (&ptr, &addr)) registers[PC] = addr; stepping = 1; /* Is instruction a branch? */ insn = *(unsigned *)registers[PC]; opcode = insn & 0xfc000000; if ( (opcode == 0xe0000000) || (opcode == 0xf8000000) ) { branch_step = 1; branch_target = registers[PC] + (((signed)insn << 6) >> 4); } else if ( (opcode == 0x44000000) || (opcode == 0x48000000) || (opcode == 0x4c000000) || (opcode == 0x50000000) || (opcode == 0x54000000) || (opcode == 0x5c000000) ) { branch_step = 1; branch_target = registers[PC] + (((signed)insn << 16) >> 14); } else if ( (opcode == 0xd8000000) || (opcode == 0xc0000000) ) { branch_step = 1; branch_target = registers[(insn >> 21) & 0x1f]; } else branch_step = 0; /* Set breakpoint after instruction we're stepping */ seq_ptr = (unsigned *)registers[PC]; seq_ptr++; seq_insn = *seq_ptr; *seq_ptr = LM32_BREAK; if (branch_step) { /* Set breakpoint on branch target */ branch_ptr = (unsigned *)branch_target; branch_insn = *branch_ptr; *branch_ptr = LM32_BREAK; } flush_i_cache (); return; #ifdef GDB_HARDWARE_BREAKPOINTS_ENABLED case 'Z': switch (*ptr++) { case '1': /* Insert h/w breakpoint */ if (*ptr++ == ',' && hexToInt (&ptr, &addr) && *ptr++ == ',' && hexToInt (&ptr, &length)) { err = set_hw_breakpoint(addr, length); if (err > 0) strcpy (remcomOutBuffer, "OK"); else if (err < 0) strcpy (remcomOutBuffer, "E28"); } else strcpy (remcomOutBuffer, "E22"); break; } break; case 'z': switch (*ptr++) { case '1': /* Remove h/w breakpoint */ if (*ptr++ == ',' && hexToInt (&ptr, &addr) && *ptr++ == ',' && hexToInt (&ptr, &length)) { err = disable_hw_breakpoint(addr, length); if (err > 0) strcpy (remcomOutBuffer, "OK"); else if (err < 0) strcpy (remcomOutBuffer, "E28"); } else strcpy (remcomOutBuffer, "E22"); break; } break; #endif #ifdef GDB_SYSCALLS_ENABLED case 'F': /* system call result */ if ( (ptr[0] == '-') && (ptr[1] == '1') && (ptr[2] == ',') ) { /* System call failed */ ptr += 3; hexToInt (&ptr, &reterrno); retcode = -1; } else { /* System call was successful */ hexToInt (&ptr, &retcode); allow_nested_exception (); /* Check if a bus error occured when mapping data structures */ if (mem_err) { reterrno = EFAULT; retcode = -1; } disallow_nested_exception (); } /* Skip over scall instruction */ registers[PC] += 4; /* Set return value */ registers[R1] = retcode; registers[R2] = 0; registers[R3] = reterrno; return; #endif /* GDB_SYSCALLS_ENABLED */ #ifdef GDB_ECLIPSE_SUPPORT case 'q': /* Query */ if (ptr[0] == 'C') { /* Return current thread ID. We only support 1. */ strcpy (remcomOutBuffer, "qC1"); } else if (!strncmp (&ptr[0], "fThreadInfo", 11)) { /* Return all thread IDs. We only support 1. */ strcpy (remcomOutBuffer, "m1"); } else if (!strncmp (&ptr[0], "sThreadInfo", 11)) { /* Indicate there are no more threads. */ strcpy (remcomOutBuffer, "l"); } break; #endif #ifdef GDB_RESTART_ENABLED case 'r': /* Reset */ case 'R': /* We reset by branching to the reset exception handler. */ registers[PC] = 0; return; #endif } /* reply to the request */ putpacket (remcomOutBuffer); } }