2001-02-27 Joel Sherrill <joel@OARcorp.com>

* Significant modifications including adding thread support, the 'X'
	command, and reorganizing so that target CPU independent routines
	could be reused.
	* gdb_if.h: Added numerous prototypes.
	* mips-stub.c: Added thread support as well as 'X' command.
	Also noticed that the 'P' command was from the mips protocol.
	* rtems-stub-glue.c: New file.  This file contains all generic
	support which should be able to be reused on another target CPU.
This commit is contained in:
Joel Sherrill
2002-02-27 22:32:15 +00:00
parent 3f1c12435f
commit 1bbe2e1dbb
5 changed files with 3115 additions and 45 deletions

View File

@@ -1,3 +1,14 @@
2001-02-27 Joel Sherrill <joel@OARcorp.com>
* Significant modifications including adding thread support, the 'X'
command, and reorganizing so that target CPU independent routines
could be reused.
* gdb_if.h: Added numerous prototypes.
* mips-stub.c: Added thread support as well as 'X' command.
Also noticed that the 'P' command was from the mips protocol.
* rtems-stub-glue.c: New file. This file contains all generic
support which should be able to be reused on another target CPU.
2002-02-08 Joel Sherrill <joel@OARcorp.com> 2002-02-08 Joel Sherrill <joel@OARcorp.com>
* mips-stub.c (handle_exception): Prototype changed to be an RTEMS * mips-stub.c (handle_exception): Prototype changed to be an RTEMS

View File

@@ -17,6 +17,61 @@
#ifndef _GDB_IF_H #ifndef _GDB_IF_H
#define _GDB_IF_H #define _GDB_IF_H
/* Max number of threads in qM response */
#define QM_MAX_THREADS (20)
struct rtems_gdb_stub_thread_info {
char display[256];
char name[256];
char more_display[256];
};
/*
* Prototypes
*/
int parse_zbreak(const char *in, int *type, unsigned char **addr, int *len);
char* mem2hstr(char *buf, const unsigned char *mem, int count);
int hstr2mem(unsigned char *mem, const char *buf, int count);
void set_mem_err(void);
unsigned char get_byte(const unsigned char *ptr);
void set_byte(unsigned char *ptr, int val);
char* thread2vhstr(char *buf, int thread);
char* thread2fhstr(char *buf, int thread);
const char* fhstr2thread(const char *buf, int *thread);
const char* vhstr2thread(const char *buf, int *thread);
char* int2fhstr(char *buf, int val);
char* int2vhstr(char *buf, int vali);
const char* fhstr2int(const char *buf, int *ival);
const char* vhstr2int(const char *buf, int *ival);
int hstr2byte(const char *buf, int *bval);
int hstr2nibble(const char *buf, int *nibble);
int rtems_gdb_stub_thread_support_ok(void);
int rtems_gdb_stub_get_current_thread(void);
int rtems_gdb_stub_get_next_thread(int);
int rtems_gdb_stub_get_offsets(
unsigned char **text_addr,
unsigned char **data_addr,
unsigned char **bss_addr
);
int rtems_gdb_stub_get_thread_regs(
int thread,
unsigned int *registers
);
int rtems_gdb_stub_set_thread_regs(
int thread,
unsigned int *registers
);
void rtems_gdb_process_query(
char *inbuffer,
char *outbuffer,
int do_threads,
int thread
);
/* /*
* MIPS registers, numbered in the order in which gdb expects to see them. * MIPS registers, numbered in the order in which gdb expects to see them.
*/ */

View File

@@ -1,3 +1,4 @@
#define GDB_STUB_ENABLE_THREAD_SUPPORT 1
/******************************************************************************* /*******************************************************************************
THIS SOFTWARE IS NOT COPYRIGHTED THIS SOFTWARE IS NOT COPYRIGHTED
@@ -126,6 +127,12 @@
#include <rtems.h> #include <rtems.h>
#include "gdb_if.h" #include "gdb_if.h"
extern int printk(const char *fmt, ...);
/* Change it to something meaningful when debugging */
#undef ASSERT
#define ASSERT(x) if(!(x)) printk("ASSERT: stub: %d\n", __LINE__)
/***************/ /***************/
/* Exception Codes */ /* Exception Codes */
#define EXC_INT 0 /* External interrupt */ #define EXC_INT 0 /* External interrupt */
@@ -184,13 +191,18 @@
*/ */
#if (__mips == 3) #if (__mips == 3)
typedef long long mips_register_t; typedef long long mips_register_t;
#define R_SZ 8
#elif (__mips == 1) #elif (__mips == 1)
typedef unsigned int mips_register_t; typedef unsigned int mips_register_t;
#define R_SZ 4
#else #else
#error "unknown MIPS ISA" #error "unknown MIPS ISA"
#endif #endif
static mips_register_t *registers; static mips_register_t *registers;
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
static char do_threads; /* != 0 means we are supporting threads */
#endif
/* /*
* The following external functions provide character input and output. * The following external functions provide character input and output.
@@ -210,14 +222,31 @@ extern void putDebugChar (char);
static char inBuffer[BUFMAX]; static char inBuffer[BUFMAX];
static char outBuffer[BUFMAX]; static char outBuffer[BUFMAX];
/* Structure to keep info on a z-breaks */
#define BREAKNUM 32
struct z0break
{
/* List support */
struct z0break *next;
struct z0break *prev;
/* Location, preserved data */
unsigned char *address;
char buf[2];
};
static struct z0break z0break_arr[BREAKNUM];
static struct z0break *z0break_avail = NULL;
static struct z0break *z0break_list = NULL;
/* /*
* Convert an int to hex. * Convert an int to hex.
*/ */
static const char hexchars[] = "0123456789abcdef"; const char gdb_hexchars[] = "0123456789abcdef";
#define highhex(x) hexchars [(x >> 4) & 0xf] #define highhex(x) gdb_hexchars [(x >> 4) & 0xf]
#define lowhex(x) hexchars [x & 0xf] #define lowhex(x) gdb_hexchars [x & 0xf]
/* /*
@@ -225,8 +254,10 @@ static const char hexchars[] = "0123456789abcdef";
* result in buf. Return a pointer to the last (null) char in buf. * result in buf. Return a pointer to the last (null) char in buf.
*/ */
static char * static char *
mem2hex (int addr, int length, char *buf) mem2hex (void *_addr, int length, char *buf)
{ {
unsigned int addr = (unsigned int) _addr;
if (((addr & 0x7) == 0) && ((length & 0x7) == 0)) /* dword aligned */ if (((addr & 0x7) == 0) && ((length & 0x7) == 0)) /* dword aligned */
{ {
long long *source = (long long *) (addr); long long *source = (long long *) (addr);
@@ -238,7 +269,7 @@ mem2hex (int addr, int length, char *buf)
long long k = *source++; long long k = *source++;
for (i = 15; i >= 0; i--) for (i = 15; i >= 0; i--)
*buf++ = hexchars [(k >> (i*4)) & 0xf]; *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
} }
} }
else if (((addr & 0x3) == 0) && ((length & 0x3) == 0)) /* word aligned */ else if (((addr & 0x3) == 0) && ((length & 0x3) == 0)) /* word aligned */
@@ -252,7 +283,7 @@ mem2hex (int addr, int length, char *buf)
int k = *source++; int k = *source++;
for (i = 7; i >= 0; i--) for (i = 7; i >= 0; i--)
*buf++ = hexchars [(k >> (i*4)) & 0xf]; *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
} }
} }
else if (((addr & 0x1) == 0) && ((length & 0x1) == 0)) /* halfword aligned */ else if (((addr & 0x1) == 0) && ((length & 0x1) == 0)) /* halfword aligned */
@@ -266,7 +297,7 @@ mem2hex (int addr, int length, char *buf)
short k = *source++; short k = *source++;
for (i = 3; i >= 0; i--) for (i = 3; i >= 0; i--)
*buf++ = hexchars [(k >> (i*4)) & 0xf]; *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
} }
} }
else /* byte aligned */ else /* byte aligned */
@@ -280,7 +311,7 @@ mem2hex (int addr, int length, char *buf)
char k = *source++; char k = *source++;
for (i = 1; i >= 0; i--) for (i = 1; i >= 0; i--)
*buf++ = hexchars [(k >> (i*4)) & 0xf]; *buf++ = gdb_hexchars [(k >> (i*4)) & 0xf];
} }
} }
@@ -372,8 +403,9 @@ hexToLongLong (char **ptr, long long *intValue)
* then return 0; otherwise return 1. * then return 0; otherwise return 1.
*/ */
static int static int
hex2mem (char *buf, int addr, int length) hex2mem (char *buf, void *_addr, int length)
{ {
unsigned int addr = (unsigned int) _addr;
if (((addr & 0x7) == 0) && ((length & 0x7) == 0)) /* dword aligned */ if (((addr & 0x7) == 0) && ((length & 0x7) == 0)) /* dword aligned */
{ {
long long *target = (long long *) (addr); long long *target = (long long *) (addr);
@@ -450,6 +482,45 @@ hex2mem (char *buf, int addr, int length)
return 1; return 1;
} }
/* Convert the binary stream in BUF to memory.
Gdb will escape $, #, and the escape char (0x7d).
COUNT is the total number of bytes to write into
memory. */
static unsigned char *
bin2mem (
unsigned char *buf,
unsigned char *mem,
int count
)
{
int i;
for (i = 0; i < count; i++) {
/* Check for any escaped characters. Be paranoid and
only unescape chars that should be escaped. */
if (*buf == 0x7d) {
switch (*(buf+1)) {
case 0x3: /* # */
case 0x4: /* $ */
case 0x5d: /* escape char */
buf++;
*buf |= 0x20;
break;
default:
/* nothing */
break;
}
}
*mem++ = *buf++;
}
return mem;
}
/* /*
* Scan the input stream for a sequence for the form $<data>#<checksum>. * Scan the input stream for a sequence for the form $<data>#<checksum>.
@@ -773,30 +844,88 @@ computeSignal (void)
} }
} }
/*
* This support function prepares and sends the message containing the
* basic information about this exception.
*/
void gdb_stub_report_exception_info(
rtems_vector_number vector,
CPU_Interrupt_frame *frame,
int thread
)
{
char *optr;
int sigval;
optr = outBuffer;
*optr++ = 'T';
sigval = computeSignal ();
*optr++ = highhex (sigval);
*optr++ = lowhex (sigval);
*optr++ = gdb_hexchars[SP];
*optr++ = ':';
optr = mem2hstr(optr, (unsigned char *)&frame->sp, R_SZ );
*optr++ = ';';
*optr++ = gdb_hexchars[PC];
*optr++ = ':';
optr = mem2hstr(optr, (unsigned char *)&frame->sp, R_SZ );
*optr++ = ';';
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
if (do_threads) {
*optr++ = 't';
*optr++ = 'h';
*optr++ = 'r';
*optr++ = 'e';
*optr++ = 'a';
*optr++ = 'd';
*optr++ = ':';
optr = thread2vhstr(optr, thread);
*optr++ = ';';
}
#endif
putpacket (outBuffer);
*optr++ = '\0';
}
/* /*
* This function handles all exceptions. It only does two things: * This function handles all exceptions. It only does two things:
* it figures out why it was activated and tells gdb, and then it * it figures out why it was activated and tells gdb, and then it
* reacts to gdb's requests. * reacts to gdb's requests.
*/ */
CPU_Interrupt_frame current_thread_registers;
void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame) void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame)
{ {
int host_has_detached = 0; int host_has_detached = 0;
int sigval;
int regno, addr, length; int regno, addr, length;
long long regval; long long regval;
char *ptr; char *ptr;
int current_thread; /* current generic thread */
int thread; /* stopped thread: context exception happened in */
void *regptr;
int binary;
registers = (mips_register_t *)frame; registers = (mips_register_t *)frame;
/* reply to host that an exception has occurred */ thread = 0;
sigval = computeSignal (); #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
outBuffer[0] = 'S'; if (do_threads) {
outBuffer[1] = highhex (sigval); thread = rtems_gdb_stub_get_current_thread();
outBuffer[2] = lowhex (sigval); }
outBuffer[3] = '\0'; #endif
current_thread = thread;
/* reply to host that an exception has occurred with some basic info */
gdb_stub_report_exception_info(vector, frame, thread);
putpacket (outBuffer);
/* /*
* Restore the saved instruction at * Restore the saved instruction at
@@ -804,32 +933,43 @@ void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame)
*/ */
undoSStep (); undoSStep ();
while (!(host_has_detached)) while (!(host_has_detached)) {
{
outBuffer[0] = '\0'; outBuffer[0] = '\0';
getpacket (inBuffer); getpacket (inBuffer);
binary = 0;
switch (inBuffer[0]) switch (inBuffer[0]) {
{
case '?': case '?':
outBuffer[0] = 'S'; gdb_stub_report_exception_info(vector, frame, thread);
outBuffer[1] = highhex (sigval);
outBuffer[2] = lowhex (sigval);
outBuffer[3] = '\0';
break; break;
case 'd': case 'd': /* toggle debug flag */
/* toggle debug flag */ /* can print ill-formed commands in valid packets & checksum errors */
break; break;
case 'g': case 'D':
/* return the values of the CPU registers */ /* remote system is detaching - return OK and exit from debugger */
mem2hex ((int) registers, sizeof registers, outBuffer); strcpy (outBuffer, "OK");
host_has_detached = 1;
break; break;
case 'G': case 'g': /* return the values of the CPU registers */
/* set the values of the CPU registers - return OK */ regptr = registers;
if (hex2mem (&inBuffer[1], (int) registers, sizeof registers)) #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
if (do_threads && current_thread != thread )
regptr = &current_thread_registers;
#endif
mem2hex (regptr, NUM_REGS * (sizeof registers), outBuffer);
break;
case 'G': /* set the values of the CPU registers - return OK */
regptr = registers;
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
if (do_threads && current_thread != thread )
regptr = &current_thread_registers;
#endif
if (hex2mem (&inBuffer[1], regptr, NUM_REGS * (sizeof registers)))
strcpy (outBuffer, "OK"); strcpy (outBuffer, "OK");
else else
strcpy (outBuffer, "E00"); /* E00 = bad "set register" command */ strcpy (outBuffer, "E00"); /* E00 = bad "set register" command */
@@ -857,11 +997,13 @@ void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame)
&& hexToInt (&ptr, &length) && hexToInt (&ptr, &length)
&& is_readable (addr, length) && is_readable (addr, length)
&& (length < (BUFMAX - 4)/2)) && (length < (BUFMAX - 4)/2))
mem2hex (addr, length, outBuffer); mem2hex ((void *)addr, length, outBuffer);
else else
strcpy (outBuffer, "E01"); /* E01 = bad 'm' command */ strcpy (outBuffer, "E01"); /* E01 = bad 'm' command */
break; break;
case 'X': /* XAA..AA,LLLL:<binary data>#cs */
binary = 1;
case 'M': case 'M':
/* MAA..AA,LLLL: Write LLLL bytes at address AA..AA - return OK */ /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA - return OK */
ptr = &inBuffer[1]; ptr = &inBuffer[1];
@@ -869,9 +1011,13 @@ void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame)
&& *ptr++ == ',' && *ptr++ == ','
&& hexToInt (&ptr, &length) && hexToInt (&ptr, &length)
&& *ptr++ == ':' && *ptr++ == ':'
&& is_writeable (addr, length) && is_writeable (addr, length) ) {
&& hex2mem (ptr, addr, length)) if ( binary )
strcpy (outBuffer, "OK"); hex2mem (ptr, (void *)addr, length);
else
bin2mem (ptr, (void *)addr, length);
strcpy (outBuffer, "OK");
}
else else
strcpy (outBuffer, "E02"); /* E02 = bad 'M' command */ strcpy (outBuffer, "E02"); /* E02 = bad 'M' command */
break; break;
@@ -891,16 +1037,256 @@ void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame)
} }
return; return;
case 'D': case 'k': /* remove all zbreaks if any */
/* remote system is detaching - return OK and exit from debugger */ {
strcpy (outBuffer, "OK"); int ret;
host_has_detached = 1; struct z0break *z0, *z0last;
ret = 1;
z0last = NULL;
for (z0=z0break_list; z0!=NULL; z0=z0->next) {
if (!hstr2mem(z0->address, z0->buf, 1)) {
ret = 0;
}
z0last = z0;
}
/* Free entries if any */
if (z0last != NULL) {
z0last->next = z0break_avail;
z0break_avail = z0break_list;
z0break_list = NULL;
}
}
break; break;
default: case 'q': /* queries */
/* do nothing */ #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
rtems_gdb_process_query( inBuffer, outBuffer, do_threads, thread );
#endif
break; break;
} /* switch */
case 'H': /* set new thread */
#if defined(GDB_STUB_ENABLE_THREAD_SUPPORT)
if (inBuffer[1] != 'g') {
break;
}
if (!do_threads) {
break;
}
{
int tmp, ret;
/* Set new generic thread */
if (vhstr2thread(&inBuffer[2], &tmp) == NULL) {
strcpy(outBuffer, "E01");
break;
}
/* 0 means `thread' */
if (tmp == 0) {
tmp = thread;
}
if (tmp == current_thread) {
/* No changes */
strcpy(outBuffer, "OK");
break;
}
/* Save current thread registers if necessary */
if (current_thread != thread) {
ret = rtems_gdb_stub_set_thread_regs(
current_thread, (unsigned int *) &current_thread_registers);
ASSERT(ret);
}
/* Read new registers if necessary */
if (tmp != thread) {
ret = rtems_gdb_stub_get_thread_regs(
tmp, (unsigned int *) &current_thread_registers);
if (!ret) {
/* Thread does not exist */
strcpy(outBuffer, "E02");
break;
}
}
current_thread = tmp;
strcpy(outBuffer, "OK");
}
#endif
break;
case 'Z': /* Add breakpoint */
{
int ret, type, len;
unsigned char *address;
struct z0break *z0;
ret = parse_zbreak(inBuffer, &type, &address, &len);
if (!ret) {
strcpy(outBuffer, "E01");
break;
}
if (type != 0) {
/* We support only software break points so far */
break;
}
if (len != 1) {
strcpy(outBuffer, "E02");
break;
}
/* Let us check whether this break point already set */
for (z0=z0break_list; z0!=NULL; z0=z0->next) {
if (z0->address == address) {
break;
}
}
if (z0 != NULL) {
/* Repeated packet */
strcpy(outBuffer, "OK");
break;
}
/* Let us allocate new break point */
if (z0break_avail == NULL) {
strcpy(outBuffer, "E03");
break;
}
/* Get entry */
z0 = z0break_avail;
z0break_avail = z0break_avail->next;
/* Let us copy memory from address add stuff the break point in */
if (mem2hstr(z0->buf, address, 1) == NULL ||
!hstr2mem(address, "cc" , 1)) {
/* Memory error */
z0->next = z0break_avail;
z0break_avail = z0;
strcpy(outBuffer, "E03");
break;
}
/* Fill it */
z0->address = address;
/* Add to the list */
z0->next = z0break_list;
z0->prev = NULL;
if (z0break_list != NULL) {
z0break_list->prev = z0;
}
z0break_list = z0;
strcpy(outBuffer, "OK");
}
case 'z': /* remove breakpoint */
{
int ret, type, len;
unsigned char *address;
struct z0break *z0, *z0last;
if (inBuffer[1] == 'z') {
/* zz packet - remove all breaks */
ret = 1;
z0last = NULL;
for (z0=z0break_list; z0!=NULL; z0=z0->next) {
if(!hstr2mem(z0->address, z0->buf, 1)) {
ret = 0;
}
z0last = z0;
}
/* Free entries if any */
if (z0last != NULL) {
z0last->next = z0break_avail;
z0break_avail = z0break_list;
z0break_list = NULL;
}
if (ret) {
strcpy(outBuffer, "OK");
} else {
strcpy(outBuffer, "E04");
}
break;
}
ret = parse_zbreak(inBuffer, &type, &address, &len);
if (!ret) {
strcpy(outBuffer, "E01");
break;
}
if (type != 0) {
/* We support only software break points so far */
break;
}
if (len != 1) {
strcpy(outBuffer, "E02");
break;
}
/* Let us check whether this break point set */
for (z0=z0break_list; z0!=NULL; z0=z0->next) {
if (z0->address == address) {
break;
}
}
if (z0 == NULL) {
/* Unknown breakpoint */
strcpy(outBuffer, "E03");
break;
}
if (!hstr2mem(z0->address, z0->buf, 1)) {
strcpy(outBuffer, "E04");
break;
}
/* Unlink entry */
if (z0->prev == NULL) {
z0break_list = z0->next;
if (z0break_list != NULL) {
z0break_list->prev = NULL;
}
} else if (z0->next == NULL) {
z0->prev->next = NULL;
} else {
z0->prev->next = z0->next;
z0->next->prev = z0->prev;
}
z0->next = z0break_avail;
z0break_avail = z0;
strcpy(outBuffer, "OK");
}
break;
default: /* do nothing */
break;
}
/* reply to the request */ /* reply to the request */
putpacket (outBuffer); putpacket (outBuffer);
@@ -919,13 +1305,33 @@ void handle_exception (rtems_vector_number vector, CPU_Interrupt_frame *frame)
return; return;
} }
static char initialized; /* 0 means we are not initialized */
void mips_gdb_stub_install(void) void mips_gdb_stub_install(void)
{ {
rtems_isr_entry old; rtems_isr_entry old;
int i;
if (initialized) {
ASSERT(0);
return;
}
/* z0breaks */
for (i=0; i<(sizeof(z0break_arr)/sizeof(z0break_arr[0]))-1; i++) {
z0break_arr[i].next = &z0break_arr[i+1];
}
z0break_arr[i].next = NULL;
z0break_avail = &z0break_arr[0];
z0break_list = NULL;
rtems_interrupt_catch( (rtems_isr_entry) handle_exception, MIPS_EXCEPTION_SYSCALL, &old ); rtems_interrupt_catch( (rtems_isr_entry) handle_exception, MIPS_EXCEPTION_SYSCALL, &old );
/* rtems_interrupt_catch( handle_exception, MIPS_EXCEPTION_BREAK, &old ); */ /* rtems_interrupt_catch( handle_exception, MIPS_EXCEPTION_BREAK, &old ); */
initialized = 1;
/* get the attention of gdb */ /* get the attention of gdb */
mips_break(); mips_break(1);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff