/*
* 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.
*/
/* $Id$ */
#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();
}