diff options
Diffstat (limited to 'bsps/lm32/shared/gdbstub/lm32-stub.c')
-rw-r--r-- | bsps/lm32/shared/gdbstub/lm32-stub.c | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/bsps/lm32/shared/gdbstub/lm32-stub.c b/bsps/lm32/shared/gdbstub/lm32-stub.c new file mode 100644 index 0000000000..8ca1a91054 --- /dev/null +++ b/bsps/lm32/shared/gdbstub/lm32-stub.c @@ -0,0 +1,977 @@ +/* + * Low-level support for LM32 remote debuging with GDB. + * Contributed by Jon Beniston <jon@beniston.com> + * Modified for RTEMS with thread support by Michael Walle <michael@walle.cc> + * + * 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 <bsp.h> +#include <string.h> +#include <signal.h> +#include <rtems.h> +#include <rtems/score/cpu.h> +#include "gdb_if.h" + +/* Enable support for run-length encoding */ +#undef GDB_RLE_ENABLED +/* Enable support for restart packets */ +#undef GDB_RESTART_ENABLED + +#define GDB_STUB_ENABLE_THREAD_SUPPORT + +/* + * The following external functions provide character input and output. + */ +extern char gdb_get_debug_char(void); +extern void gdb_put_debug_char(char); +extern void gdb_console_init(void); +extern void gdb_ack_irq(void); +extern void *_deba; + +/* Function prototypes */ +static void allow_nested_exception(void); +static void disallow_nested_exception(void); +static char *mem2hex(unsigned char *mem, char *buf, int count); +static unsigned char *hex2mem(char *buf, unsigned char *mem, int count); +static unsigned char *bin2mem(char *buf, unsigned char *mem, int count); +static int compute_signal(int eid); +static void flush_cache(void); +static int hex2int(char **ptr, int *int_value); +static char *getpacket(void); +static void putpacket(char *buffer); + +unsigned int registers[NUM_REGS]; + +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers */ +#define BUFMAX 1500 + +/* I/O packet buffers */ +static char remcomInBuffer[BUFMAX]; +static 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 char branch_step; + +/* Saved instructions */ +static unsigned int *seq_ptr; +static unsigned int seq_insn; +static unsigned int *branch_ptr; +static unsigned int branch_insn; + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) +static char do_threads; +int current_thread_registers[NUM_REGS]; +#endif + +/* this mapping is used to copy the registers from a debug interrupt frame + * see gdb_handle_break() */ +static unsigned char reg_map[] = { + 0, LM32_INT_REG_R1, LM32_INT_REG_R2, LM32_INT_REG_R3, LM32_INT_REG_R4, + LM32_INT_REG_R5, LM32_INT_REG_R6, LM32_INT_REG_R7, LM32_INT_REG_R8, + LM32_INT_REG_R9, LM32_INT_REG_R10, LM32_INT_REG_R11, LM32_INT_REG_R12, + LM32_INT_REG_R13, LM32_INT_REG_R14, LM32_INT_REG_R15, LM32_INT_REG_R16, + LM32_INT_REG_R17, LM32_INT_REG_R18, LM32_INT_REG_R19, LM32_INT_REG_R20, + LM32_INT_REG_R21, LM32_INT_REG_R22, LM32_INT_REG_R23, LM32_INT_REG_R24, + LM32_INT_REG_R25, LM32_INT_REG_GP, LM32_INT_REG_FP, LM32_INT_REG_SP, + LM32_INT_REG_RA, LM32_INT_REG_EA, LM32_INT_REG_BA, LM32_INT_REG_PC, + LM32_INT_REG_EID, LM32_INT_REG_EBA, LM32_INT_REG_DEBA, LM32_INT_REG_IE +}; + +/* + * Conversion helper functions + */ + +/* For integer to ASCII conversion */ +#define highhex(x) gdb_hexchars [(x >> 4) & 0xf] +#define lowhex(x) gdb_hexchars [x & 0xf] +const char gdb_hexchars[]="0123456789abcdef"; + +/* 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; +} + +/* + * 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 char *mem2hex( + unsigned char *mem, + char *buf, int count +) +{ + unsigned char ch; + + while (count-- > 0) + { + ch = *mem++; + if (mem_err) + return NULL; + *buf++ = highhex(ch); + *buf++ = lowhex(ch); + } + + *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 unsigned char *hex2mem( + char *buf, + unsigned char *mem, + int count +) +{ + int i; + unsigned char ch; + + 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; +} + +/* + * 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 unsigned char *bin2mem( + char *buf, + unsigned char *mem, + int count +) +{ + int i; + unsigned char c; + + 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; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hex2int( + char **ptr, + int *int_value +) +{ + int num_chars = 0; + int hex_value; + + *int_value = 0; + + while(**ptr) + { + hex_value = hex(**ptr); + if (hex_value < 0) + break; + + *int_value = (*int_value << 4) | hex_value; + num_chars ++; + + (*ptr)++; + } + + return (num_chars); +} + +/* Convert the exception identifier to a signal number. */ +static int compute_signal( + 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 */ +} + +/* Scan for the sequence $<data>#<checksum> */ +static char *getpacket(void) +{ + 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_get_debug_char()) != '$'); + +retry: + checksum = 0; + xmitcsum = -1; + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) + { + ch = gdb_get_debug_char(); + if (ch == '$') + goto retry; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') + { + ch = gdb_get_debug_char(); + xmitcsum = hex(ch) << 4; + ch = gdb_get_debug_char(); + xmitcsum += hex(ch); + + if (checksum != xmitcsum) + { + /* failed checksum */ + gdb_put_debug_char('-'); + } + else + { + /* successful transfer */ + gdb_put_debug_char('+'); + + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') + { + gdb_put_debug_char(buffer[0]); + gdb_put_debug_char(buffer[1]); + + return &buffer[3]; + } + + return &buffer[0]; + } + } + } +} + +/* Send the packet in buffer. */ +static void putpacket( + char *buffer +) +{ + unsigned char checksum; + int count; + unsigned char ch; + +#ifdef GDB_RLE_ENABLED + int run_length; + int run_idx; + char run_length_char; +#endif + + /* $<packet info>#<checksum>. */ + do { + gdb_put_debug_char('$'); + checksum = 0; + count = 0; + +#ifdef GDB_RLE_ENABLED + while (ch = buffer[count]) + { + /* Transmit character */ + gdb_put_debug_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_put_debug_char('*'); + checksum += '*'; + gdb_put_debug_char(run_length_char); + checksum += run_length_char; + count += run_length; + } + } +#else + while ((ch = buffer[count])) + { + gdb_put_debug_char(ch); + checksum += ch; + count += 1; + } +#endif + + gdb_put_debug_char('#'); + gdb_put_debug_char(highhex(checksum)); + gdb_put_debug_char(lowhex(checksum)); + } while (gdb_get_debug_char() != '+'); +} + +static void allow_nested_exception(void) +{ + mem_err = 0; + may_fault = 1; +} + +static void disallow_nested_exception(void) +{ + mem_err = 0; + may_fault = 0; +} + +/* Flush the instruction cache */ +static void flush_cache(void) +{ + /* + * Executing this does no harm on CPUs without a cache. We flush data cache as + * well as instruction cache in case the debugger has accessed memory + * directly. + */ + __asm__ __volatile__ ("wcsr ICC, r0\n" + "nop\n" + "nop\n" + "nop\n" + "wcsr DCC, r0\n" + "nop\n" + "nop\n" + "nop" + ); +} + +/* 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" : "=r" (bp)); + if ((bp & 0x01) == 0) + { + __asm__ ("wcsr BP0, %0" : : "r" (address | 1)); + return 1; + } + __asm__ ("rcsr %0, BP1" : "=r" (bp)); + if ((bp & 0x01) == 0) + { + __asm__ ("wcsr BP1, %0" : : "r" (address | 1)); + return 1; + } + __asm__ ("rcsr %0, BP2" : "=r" (bp)); + if ((bp & 0x01) == 0) + { + __asm__ ("wcsr BP2, %0" : : "r" (address | 1)); + return 1; + } + __asm__ ("rcsr %0, BP3" : "=r" (bp)); + if ((bp & 0x01) == 0) + { + __asm__ ("wcsr BP3, %0" : : "r" (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" : "=r" (bp)); + if ((bp & 0xfffffffc) == (address & 0xfffffffc)) + { + __asm__ ("wcsr BP0, %0" : : "r" (0)); + return 1; + } + __asm__ ("rcsr %0, BP1" : "=r" (bp)); + if ((bp & 0xfffffffc) == (address & 0xfffffffc)) + { + __asm__ ("wcsr BP1, %0" : : "r" (0)); + return 1; + } + __asm__ ("rcsr %0, BP2" : "=r" (bp)); + if ((bp & 0xfffffffc) == (address & 0xfffffffc)) + { + __asm__ ("wcsr BP2, %0" : : "r" (0)); + return 1; + } + __asm__ ("rcsr %0, BP3" : "=r" (bp)); + if ((bp & 0xfffffffc) == (address & 0xfffffffc)) + { + __asm__ ("wcsr BP3, %0" : : "r" (0)); + return 1; + } + + /* Breakpoint not found */ + return -1; +} + +/* + * This support function prepares and sends the message containing the + * basic information about this exception. + */ +static void gdb_stub_report_exception_info( + int thread +) +{ + char *ptr; + int sigval; + + /* Convert exception ID to a signal number */ + sigval = compute_signal(registers[LM32_REG_EID]); + + /* Set pointer to start of output buffer */ + ptr = remcomOutBuffer; + + *ptr++ = 'T'; + *ptr++ = highhex(sigval); + *ptr++ = lowhex(sigval); + + *ptr++ = highhex(LM32_REG_PC); + *ptr++ = lowhex(LM32_REG_PC); + *ptr++ = ':'; + ptr = mem2hex((unsigned char *)&(registers[LM32_REG_PC]), ptr, 4); + *ptr++ = ';'; + + *ptr++ = highhex(LM32_REG_SP); + *ptr++ = lowhex(LM32_REG_SP); + *ptr++ = ':'; + ptr = mem2hex((unsigned char *)&(registers[LM32_REG_SP]), ptr, 4); + *ptr++ = ';'; + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads) + { + *ptr++ = 't'; + *ptr++ = 'h'; + *ptr++ = 'r'; + *ptr++ = 'e'; + *ptr++ = 'a'; + *ptr++ = 'd'; + *ptr++ = ':'; + ptr = thread2vhstr(ptr, thread); + *ptr++ = ';'; + } +#endif + + *ptr++ = '\0'; +} + +/* + * This function does all command procesing for interfacing to gdb. The error + * codes we return are errno numbers. + */ +void handle_exception(void) +{ + int addr; + int length; + char *ptr; + int err; + int reg; + unsigned insn; + unsigned opcode; + unsigned branch_target = 0; + int current_thread; + int thread; + void *regptr; + int host_has_detached = 0; + int binary; + + thread = 0; +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads) + thread = rtems_gdb_stub_get_current_thread(); +#endif + current_thread = thread; + + /* + * Check for bus error caused by this code (rather than the program being + * debugged) + */ + if (may_fault && (registers[LM32_REG_EID] == LM32_EXCEPTION_DATA_BUS_ERROR)) + { + /* Indicate that a fault occured */ + mem_err = 1; + /* Skip over faulting instruction */ + registers[LM32_REG_PC] += 4; + /* Resume execution */ + return; + } + + if (stepping) + { + /* Remove breakpoints */ + *seq_ptr = seq_insn; + if (branch_step) + *branch_ptr = branch_insn; + stepping = 0; + } + + /* Reply to host that an exception has occured with some basic info */ + gdb_stub_report_exception_info(thread); + putpacket(remcomOutBuffer); + + while (!host_has_detached) + { + remcomOutBuffer[0] = '\0'; + ptr = getpacket(); + binary = 0; + + switch (*ptr++) + { + /* Return last signal */ + case '?': + gdb_stub_report_exception_info(thread); + break; + + /* Detach - exit from debugger */ + case 'D': + strcpy(remcomOutBuffer, "OK"); + host_has_detached = 1; + break; + + /* Return the value of the CPU registers */ + case 'g': + regptr = registers; +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads && current_thread != thread ) + regptr = ¤t_thread_registers; +#endif + ptr = mem2hex((unsigned char*)regptr, remcomOutBuffer, NUM_REGS * 4); + break; + + /* Set the value of the CPU registers */ + case 'G': + regptr = registers; +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (do_threads && current_thread != thread ) + regptr = ¤t_thread_registers; +#endif + hex2mem(ptr, (unsigned char*)regptr, NUM_REGS * 4); + strcpy(remcomOutBuffer, "OK"); + break; + + /* Return the value of the specified register */ + case 'p': + if (hex2int(&ptr, ®)) + { + ptr = remcomOutBuffer; + ptr = mem2hex((unsigned char *)®isters[reg], ptr, 4); + } else + strcpy(remcomOutBuffer, "E22"); + break; + + /* Set the specified register to the given value */ + case 'P': + if (hex2int(&ptr, ®) + && *ptr++ == '=') + { + hex2mem(ptr, (unsigned char *)®isters[reg], 4); + strcpy(remcomOutBuffer, "OK"); + } + else + strcpy(remcomOutBuffer, "E22"); + break; + + /* Read memory */ + case 'm': + /* Try to read %x,%x. */ + if (hex2int(&ptr, &addr) + && *ptr++ == ',' + && hex2int(&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; + + /* Write memory */ + case 'X': + binary = 1; + case 'M': + /* Try to read '%x,%x:'. */ + if (hex2int(&ptr, &addr) + && *ptr++ == ',' + && hex2int(&ptr, &length) + && *ptr++ == ':') + { + allow_nested_exception(); + if (binary) + err = (int)bin2mem(ptr, (unsigned char *)addr, length); + else + err = (int)hex2mem(ptr, (unsigned char *)addr, length); + if (err) + strcpy(remcomOutBuffer, "OK"); + else + strcpy(remcomOutBuffer, "E14"); + disallow_nested_exception(); + } + else + strcpy(remcomOutBuffer, "E22"); + break; + + /* Continue */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + if (hex2int(&ptr, &addr)) + registers[LM32_REG_PC] = addr; + flush_cache(); + return; + + /* Step */ + case 's': + /* try to read optional parameter, pc unchanged if no parm */ + if (hex2int(&ptr, &addr)) + registers[LM32_REG_PC] = addr; + stepping = 1; + /* Is instruction a branch? */ + insn = *(unsigned int*)registers[LM32_REG_PC]; + opcode = insn & 0xfc000000; + if ( (opcode == 0xe0000000) + || (opcode == 0xf8000000) + ) + { + branch_step = 1; + branch_target = registers[LM32_REG_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[LM32_REG_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 int *)registers[LM32_REG_PC]; + seq_ptr++; + seq_insn = *seq_ptr; + *seq_ptr = LM32_BREAK; + + /* Make sure one insn doesn't get replaced twice */ + if (seq_ptr == (unsigned int*)branch_target) + branch_step = 0; + + if (branch_step) + { + /* Set breakpoint on branch target */ + branch_ptr = (unsigned int*)branch_target; + branch_insn = *branch_ptr; + *branch_ptr = LM32_BREAK; + } + flush_cache(); + return; + + case 'Z': + switch (*ptr++) + { + /* Insert h/w breakpoint */ + case '1': + if (*ptr++ == ',' + && hex2int(&ptr, &addr) + && *ptr++ == ',' + && hex2int(&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++) + { + /* Remove h/w breakpoint */ + case '1': + if (*ptr++ == ',' + && hex2int(&ptr, &addr) + && *ptr++ == ',' + && hex2int(&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; + + /* Query */ + case 'q': +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + rtems_gdb_process_query( + remcomInBuffer, + remcomOutBuffer, + do_threads, + thread ); +#endif + break; + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + /* Thread alive */ + case 'T': + { + int testThread; + + if (vhstr2thread(&remcomInBuffer[1], &testThread) == NULL) + { + strcpy(remcomOutBuffer, "E01"); + break; + } + + if (rtems_gdb_index_to_stub_id(testThread) == NULL) + strcpy(remcomOutBuffer, "E02"); + else + strcpy(remcomOutBuffer, "OK"); + } + break; +#endif + + /* Set thread */ + case 'H': +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if (remcomInBuffer[1] != 'g') + break; + + if (!do_threads) + break; + + { + int tmp, ret; + + /* Set new generic thread */ + if (vhstr2thread(&remcomInBuffer[2], &tmp) == NULL) + { + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* 0 means `thread' */ + if (tmp == 0) + tmp = thread; + + if (tmp == current_thread) + { + /* No changes */ + strcpy(remcomOutBuffer, "OK"); + break; + } + + /* Save current thread registers if necessary */ + if (current_thread != thread) + { + ret = rtems_gdb_stub_set_thread_regs( + current_thread, (unsigned int *) ¤t_thread_registers); + } + + /* Read new registers if necessary */ + if (tmp != thread) + { + ret = rtems_gdb_stub_get_thread_regs( + tmp, (unsigned int *) ¤t_thread_registers); + + if (!ret) + { + /* Thread does not exist */ + strcpy(remcomOutBuffer, "E02"); + break; + } + } + + current_thread = tmp; + strcpy(remcomOutBuffer, "OK"); + } +#endif + break; + +#ifdef GDB_RESTART_ENABLED + /* Reset */ + case 'r': + case 'R': + /* We reset by branching to the reset exception handler. */ + registers[LM32_REG_PC] = 0; + return; +#endif + } + + /* reply to the request */ + putpacket(remcomOutBuffer); + } +} + +void gdb_handle_break(rtems_vector_number vector, CPU_Interrupt_frame *frame) +{ + int i; + unsigned int *int_regs = (unsigned int*)frame; + + /* copy extended frame to registers */ + registers[LM32_REG_R0] = 0; + for (i = 1; i < NUM_REGS; i++) + { + registers[i] = int_regs[reg_map[i]]; + } + + /* now call the real handler */ + handle_exception(); + gdb_ack_irq(); + + /* copy registers back to extended frame */ + for (i = 1; i < NUM_REGS; i++) + { + int_regs[reg_map[i]] = registers[i]; + } +} + +void lm32_gdb_stub_install(int enable_threads) +{ + unsigned int dc; + + /* set DEBA and remap all exception */ + __asm__("wcsr DEBA, %0" : : "r" (&_deba)); + __asm__("rcsr %0, DC" : "=r" (dc)); + dc |= 0x2; + __asm__("wcsr DC, %0" : : "r" (dc)); + +#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) + if( enable_threads ) + do_threads = 1; + else + do_threads = 0; +#endif + + gdb_console_init(); +} + |