- changed address of debugger entry - mips_dbg: no use om memset and memcpy git-svn-id: http://moon:8086/svn/mips@120 a8ebac50-d88d-4704-bea3-6648445a41b3
643 lines
14 KiB
C
643 lines
14 KiB
C
/****************************************************************************
|
|
|
|
THIS SOFTWARE IS NOT COPYRIGHTED
|
|
|
|
HP offers the following for use in the public domain. HP makes no
|
|
warranty with regard to the software or it's performance and the
|
|
user accepts the software "AS IS" with all faults.
|
|
|
|
HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
|
|
TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
|
|
*
|
|
* Module name: remcom.c $
|
|
* Revision: 1.34 $
|
|
* Date: 91/03/09 12:29:49 $
|
|
* Contributor: Lake Stevens Instrument Division$
|
|
*
|
|
* Description: low level support for gdb debugger. $
|
|
*
|
|
* Considerations: only works on target hardware $
|
|
*
|
|
* Written by: Glenn Engel $
|
|
* ModuleState: Experimental $
|
|
*
|
|
* NOTES: See Below $
|
|
*
|
|
* Modified for SPARC by Stu Grossman, Cygnus Support.
|
|
*
|
|
* This code has been extensively tested on the Fujitsu SPARClite demo board.
|
|
*
|
|
* To enable debugger support, two things need to happen. One, a
|
|
* call to set_debug_traps() is necessary in order to allow any breakpoints
|
|
* or error conditions to be properly intercepted and reported to gdb.
|
|
* Two, a breakpoint needs to be generated to begin communication. This
|
|
* is most easily accomplished by a call to breakpoint(). Breakpoint()
|
|
* simulates a breakpoint by executing a trap #1.
|
|
*
|
|
*************
|
|
*
|
|
* The following gdb commands are supported:
|
|
*
|
|
* command function Return value
|
|
*
|
|
* g return the value of the CPU registers hex data or ENN
|
|
* G set the value of the CPU registers OK or ENN
|
|
*
|
|
* mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
|
|
* MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
|
|
*
|
|
* c Resume at current address SNN ( signal NN)
|
|
* cAA..AA Continue at address AA..AA SNN
|
|
*
|
|
* s Step one instruction SNN
|
|
* sAA..AA Step one instruction from AA..AA SNN
|
|
*
|
|
* k kill
|
|
*
|
|
* ? What was the last sigval ? SNN (signal NN)
|
|
*
|
|
* All commands and responses are sent with a packet which includes a
|
|
* checksum. A packet consists of
|
|
*
|
|
* $<packet info>#<checksum>.
|
|
*
|
|
* where
|
|
* <packet info> :: <characters representing the command or response>
|
|
* <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
|
|
*
|
|
* When a packet is received, it is first acknowledged with either '+' or '-'.
|
|
* '+' indicates a successful transfer. '-' indicates a failed transfer.
|
|
*
|
|
* Example:
|
|
*
|
|
* Host: Reply:
|
|
* $m0,10#2a +$00010203040506070809101112131415#42
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
|
|
/************************************************************************
|
|
*
|
|
* external low-level support routines
|
|
*/
|
|
|
|
extern void dbg_putchar(char c); /* write a single character */
|
|
extern char dbg_getchar(); /* read and return a single char */
|
|
extern void ICACHE_invalidate_all();
|
|
extern int sputs(char const *pStr);
|
|
extern void print_byte(char byte);
|
|
extern void print_word(int word);
|
|
|
|
void putDebugChar(char c)
|
|
{
|
|
dbg_putchar(c);
|
|
}
|
|
|
|
int getDebugChar()
|
|
{
|
|
return (int)dbg_getchar();
|
|
}
|
|
|
|
void flush_i_cache()
|
|
{
|
|
ICACHE_invalidate_all();
|
|
}
|
|
|
|
void dbg_strcpy(char *pDst, const char *pSrc)
|
|
{
|
|
while(*pSrc)
|
|
{
|
|
*(pDst++) = *(pSrc++);
|
|
}
|
|
*pDst = *pSrc;
|
|
}
|
|
|
|
void* dbg_memset(void *pDst, int value, size_t size)
|
|
{
|
|
char *dst = (char*)pDst;
|
|
|
|
while(size)
|
|
{
|
|
*(dst++) = (char)value;
|
|
size--;
|
|
}
|
|
return (void*)dst;
|
|
}
|
|
|
|
void* dbg_memcpy(void *pDst, const void *pSrc, size_t size )
|
|
{
|
|
char *dst = (char*)pDst;
|
|
char *src = (char*)pSrc;
|
|
|
|
while(size)
|
|
{
|
|
*(dst++) = *(src++);
|
|
size--;
|
|
}
|
|
return (void*)dst;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
|
|
/* at least NUMREGBYTES*2 are needed for register packets */
|
|
#define BUFMAX 2048
|
|
|
|
const char hexchars[]="0123456789abcdef";
|
|
|
|
#define NUMREGS 72
|
|
|
|
/* Number of bytes of registers. */
|
|
#define NUMREGBYTES (NUMREGS * 4)
|
|
|
|
enum regnames
|
|
{
|
|
START_CPU_REGS = 0,
|
|
SR = START_CPU_REGS,
|
|
CR,
|
|
PC,
|
|
BV,
|
|
R00,
|
|
R01,
|
|
R02,
|
|
R03,
|
|
R04,
|
|
R05,
|
|
R06,
|
|
R07,
|
|
R08,
|
|
R09,
|
|
R10,
|
|
R11,
|
|
R12,
|
|
R13,
|
|
R14,
|
|
R15,
|
|
R16,
|
|
R17,
|
|
R18,
|
|
R19,
|
|
R20,
|
|
R21,
|
|
R22,
|
|
R23,
|
|
R24,
|
|
R25,
|
|
R26,
|
|
R27,
|
|
R28,
|
|
R29,
|
|
R30,
|
|
R31,
|
|
LO,
|
|
HI,
|
|
NUM_REGS
|
|
};
|
|
|
|
/* 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 $<data>#<checksum> */
|
|
|
|
unsigned char *
|
|
getpacket (unsigned char *buffer)
|
|
{
|
|
unsigned char checksum;
|
|
unsigned char xmitcsum;
|
|
int count;
|
|
char ch;
|
|
|
|
while (1)
|
|
{
|
|
/* wait around for the start character, ignore all other characters */
|
|
while ((ch = getDebugChar ()) != '$')
|
|
;
|
|
|
|
retry:
|
|
checksum = 0;
|
|
xmitcsum = -1;
|
|
count = 0;
|
|
|
|
/* now, read until a # or end of buffer is found */
|
|
while (count < BUFMAX - 1)
|
|
{
|
|
ch = getDebugChar ();
|
|
if (ch == '$')
|
|
goto retry;
|
|
if (ch == '#')
|
|
break;
|
|
checksum = checksum + ch;
|
|
buffer[count] = ch;
|
|
count = count + 1;
|
|
}
|
|
buffer[count] = 0;
|
|
|
|
if (ch == '#')
|
|
{
|
|
ch = getDebugChar ();
|
|
xmitcsum = hex (ch) << 4;
|
|
ch = getDebugChar ();
|
|
xmitcsum += hex (ch);
|
|
|
|
#ifdef WITH_CHECKSUM_CHECK
|
|
if (checksum != xmitcsum)
|
|
{
|
|
putDebugChar ('-'); /* failed checksum */
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
putDebugChar ('+'); /* successful transfer */
|
|
|
|
/* if a sequence char is present, reply the sequence ID */
|
|
if (buffer[2] == ':')
|
|
{
|
|
putDebugChar (buffer[0]);
|
|
putDebugChar (buffer[1]);
|
|
|
|
return &buffer[3];
|
|
}
|
|
return &buffer[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* send the packet in buffer. */
|
|
|
|
static void
|
|
putpacket (unsigned char *buffer)
|
|
{
|
|
unsigned char checksum;
|
|
int count;
|
|
unsigned char ch;
|
|
|
|
/* $<packet info>#<checksum>. */
|
|
do
|
|
{
|
|
putDebugChar('$');
|
|
checksum = 0;
|
|
count = 0;
|
|
|
|
while (ch = buffer[count])
|
|
{
|
|
putDebugChar(ch);
|
|
checksum += ch;
|
|
count += 1;
|
|
}
|
|
|
|
putDebugChar('#');
|
|
putDebugChar(hexchars[checksum >> 4]);
|
|
putDebugChar(hexchars[checksum & 0xf]);
|
|
|
|
} while (getDebugChar() != '+');
|
|
}
|
|
|
|
/* Indicate to caller of mem2hex or hex2mem that there has been an
|
|
error. */
|
|
const int mem_err = 0;
|
|
|
|
/* Convert the memory pointed to by mem into hex, placing result in buf.
|
|
* Return a pointer to the last char put in buf (null), in case of mem fault,
|
|
* return 0.
|
|
* If MAY_FAULT is non-zero, then we will handle memory faults by returning
|
|
* a 0, else treat a fault like any other fault in the stub.
|
|
*/
|
|
|
|
static unsigned char *
|
|
mem2hex (unsigned char *mem, unsigned char *buf, int count, int may_fault)
|
|
{
|
|
unsigned char ch;
|
|
|
|
while (count-- > 0)
|
|
{
|
|
ch = *mem++;
|
|
if (mem_err)
|
|
return 0;
|
|
*buf++ = hexchars[ch >> 4];
|
|
*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 may_fault)
|
|
{
|
|
int i;
|
|
unsigned char ch;
|
|
|
|
for (i=0; i<count; i++)
|
|
{
|
|
ch = hex(*buf++) << 4;
|
|
ch |= hex(*buf++);
|
|
*mem++ = ch;
|
|
if (mem_err)
|
|
return 0;
|
|
}
|
|
return mem;
|
|
}
|
|
|
|
/* This table contains the mapping between SPARC hardware trap types, and
|
|
signals, which are primarily what GDB understands. It also indicates
|
|
which hardware traps we need to commandeer when initializing the stub. */
|
|
|
|
struct hard_trap_info_t
|
|
{
|
|
unsigned long tt; /* Trap type code for SPARClite */
|
|
unsigned long signo; /* Signal that we map this trap into */
|
|
};
|
|
|
|
const struct hard_trap_info_t hard_trap_info[] =
|
|
{
|
|
{0, SIGINT}, /* Interrupt (Int) */
|
|
{1, SIGSEGV}, /* TLB modification (Mod) */
|
|
{2, SIGSEGV}, /* TLB load (TLBL) */
|
|
{3, SIGSEGV}, /* TLB store (TLBS) */
|
|
{4, SIGSEGV}, /* Address error load (ADEL) */
|
|
{5, SIGSEGV}, /* Address error store (ADES) */
|
|
{6, SIGBUS}, /* Bus error on instruction (IBE) */
|
|
{7, SIGBUS}, /* Bus error on data (DBE) */
|
|
{8, SIGUSR1}, /* Syscall instruction called (Syscall) */
|
|
{9, SIGTRAP}, /* Breakpoint instruction called (Breakpoint) */
|
|
{10, SIGILL}, /* Reserved instruction (RI) */
|
|
{11, SIGXCPU}, /* Coprocessor unusable (CpU) */
|
|
{12, SIGFPE}, /* Arithmetic overflow (Ov) */
|
|
{0, 0} /* Must be last */
|
|
};
|
|
|
|
/* Convert the SPARC hardware trap type code to a unix signal number. */
|
|
static unsigned long
|
|
computeSignal (unsigned long tt)
|
|
{
|
|
// Determine linux signal from CPU specific exception code
|
|
struct hard_trap_info_t *ht;
|
|
|
|
for (ht = (struct hard_trap_info_t *)hard_trap_info; ht->signo != 0; ht++)
|
|
{
|
|
if (ht->tt == tt)
|
|
{
|
|
return ht->signo;
|
|
}
|
|
}
|
|
return SIGHUP; /* default for things we don't know about */
|
|
}
|
|
|
|
/*
|
|
* While we find nice hex chars, build an int.
|
|
* Return number of chars processed.
|
|
*/
|
|
|
|
static int
|
|
hexToInt(char **ptr, unsigned long *intValue)
|
|
{
|
|
unsigned long numChars = 0;
|
|
int hexValue;
|
|
|
|
*intValue = 0;
|
|
|
|
while (**ptr)
|
|
{
|
|
hexValue = hex(**ptr);
|
|
if (hexValue < 0)
|
|
break;
|
|
|
|
*intValue = (*intValue << 4) | hexValue;
|
|
numChars ++;
|
|
|
|
(*ptr)++;
|
|
}
|
|
return (numChars);
|
|
}
|
|
|
|
int isBreak(unsigned long instr)
|
|
{
|
|
return ((instr & 0xFC00003F) == 0x0000000D);
|
|
}
|
|
|
|
/*
|
|
* This function does all command procesing for interfacing to gdb. It
|
|
* returns 1 if you should skip the instruction at the trap address, 0
|
|
* otherwise.
|
|
*/
|
|
|
|
void
|
|
handle_exception (unsigned long *registers)
|
|
{
|
|
char remcomInBuffer[BUFMAX];
|
|
char remcomOutBuffer[BUFMAX];
|
|
|
|
sputs("Entry\n");
|
|
unsigned long tt; /* Trap type */
|
|
unsigned long sigval;
|
|
unsigned long addr;
|
|
unsigned long length;
|
|
char *ptr;
|
|
unsigned long *sp;
|
|
|
|
addr = *((unsigned long *)registers[PC]);
|
|
|
|
// is break
|
|
if (isBreak(addr))
|
|
{
|
|
registers[PC] += 4;
|
|
}
|
|
|
|
sp = (unsigned long *)®isters[R29];
|
|
|
|
tt = (registers[CR] >> 2) & 0x1f;
|
|
|
|
/* reply to host that an exception has occurred */
|
|
sigval = computeSignal(tt);
|
|
ptr = remcomOutBuffer;
|
|
|
|
*ptr++ = 'T';
|
|
*ptr++ = hexchars[sigval >> 4];
|
|
*ptr++ = hexchars[sigval & 0xf];
|
|
|
|
*ptr++ = hexchars[37 >> 4];
|
|
*ptr++ = hexchars[37 & 0xf];
|
|
*ptr++ = ':';
|
|
ptr = mem2hex((char *)®isters[PC], ptr, 4, 0);
|
|
*ptr++ = ';';
|
|
|
|
*ptr++ = hexchars[29 >> 4];
|
|
*ptr++ = hexchars[29 & 0xf];
|
|
*ptr++ = ':';
|
|
ptr = mem2hex((char *)sp, ptr, 4, 0);
|
|
*ptr++ = ';';
|
|
|
|
*ptr++ = 0;
|
|
|
|
putpacket(remcomOutBuffer);
|
|
|
|
while (1)
|
|
{
|
|
remcomOutBuffer[0] = 0;
|
|
|
|
ptr = getpacket(remcomInBuffer);
|
|
switch (*ptr++)
|
|
{
|
|
case '?':
|
|
sputs("?\n");
|
|
remcomOutBuffer[0] = 'S';
|
|
remcomOutBuffer[1] = hexchars[sigval >> 4];
|
|
remcomOutBuffer[2] = hexchars[sigval & 0xf];
|
|
remcomOutBuffer[3] = 0;
|
|
break;
|
|
|
|
case 'd': /* toggle debug flag */
|
|
sputs("d\n");
|
|
break;
|
|
|
|
case 'g': /* return the value of the CPU registers */
|
|
{
|
|
sputs("g\n");
|
|
ptr = remcomOutBuffer;
|
|
/* R00 .. R31 */
|
|
ptr = mem2hex((char *)®isters[R00], ptr, 4*32, 0);
|
|
/* SR, HI, LO, BADVADDR, CR, PC */
|
|
ptr = mem2hex((char *)®isters[SR], ptr, 4, 0);
|
|
ptr = mem2hex((char *)®isters[HI], ptr, 4, 0);
|
|
ptr = mem2hex((char *)®isters[LO], ptr, 4, 0);
|
|
ptr = mem2hex((char *)®isters[BV], ptr, 4, 0);
|
|
ptr = mem2hex((char *)®isters[CR], ptr, 4, 0);
|
|
ptr = mem2hex((char *)®isters[PC], ptr, 4, 0);
|
|
/* Floating point F00 .. F31*/
|
|
dbg_memset(ptr, '0', 8 * 32);
|
|
ptr += 8*32;
|
|
/* FCSR, FIR, RESTART*/
|
|
dbg_memset(ptr, '0', 8 * 3);
|
|
ptr += 8*3;
|
|
*ptr = 0;
|
|
}
|
|
break;
|
|
|
|
case 'G': /* set the value of the CPU registers - return OK */
|
|
{
|
|
sputs("G\n");
|
|
unsigned long *newsp, sr;
|
|
sr = registers[SR];
|
|
|
|
hex2mem(ptr, (char *)®isters[R00], 4*32, 0); /* R00 .. R31 */
|
|
ptr += 4*32 * 2;
|
|
/* SR, HI, LO, BADVADDR, CR, PC */
|
|
hex2mem(ptr, (char *)®isters[SR], 4, 0);
|
|
ptr += 4 * 2;
|
|
hex2mem(ptr, (char *)®isters[HI], 4, 0);
|
|
ptr += 4 * 2;
|
|
hex2mem(ptr, (char *)®isters[LO], 4, 0);
|
|
ptr += 4 * 2;
|
|
hex2mem(ptr, (char *)®isters[BV], 4, 0);
|
|
ptr += 4 * 2;
|
|
hex2mem(ptr, (char *)®isters[CR], 4, 0);
|
|
ptr += 4 * 2;
|
|
hex2mem(ptr, (char *)®isters[PC], 4, 0);
|
|
ptr += 4 * 2;
|
|
*ptr = 0;
|
|
|
|
dbg_strcpy(remcomOutBuffer,"OK");
|
|
}
|
|
break;
|
|
|
|
case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
|
|
sputs("m\n");
|
|
/* Try to read %x,%x. */
|
|
|
|
if (hexToInt(&ptr, &addr) && *ptr++ == ',' && hexToInt(&ptr, &length))
|
|
{
|
|
if (mem2hex((char *)addr, remcomOutBuffer, length, 1))
|
|
{
|
|
break;
|
|
}
|
|
dbg_strcpy (remcomOutBuffer, "E03");
|
|
}
|
|
else
|
|
{
|
|
dbg_strcpy(remcomOutBuffer,"E01");
|
|
}
|
|
sputs("m:"); print_word(addr); sputs(",");print_word(length); sputs("\n");
|
|
break;
|
|
|
|
case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
|
|
/* Try to read '%x,%x:'. */
|
|
|
|
if (hexToInt(&ptr, &addr) && *ptr++ == ',' && hexToInt(&ptr, &length) && *ptr++ == ':')
|
|
{
|
|
if (hex2mem(ptr, (char *)addr, length, 1))
|
|
{
|
|
dbg_strcpy(remcomOutBuffer, "OK");
|
|
}
|
|
else
|
|
{
|
|
dbg_strcpy(remcomOutBuffer, "E03");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dbg_strcpy(remcomOutBuffer, "E02");
|
|
}
|
|
sputs("M:"); print_word(addr); sputs(",");print_word(length); sputs("\n");
|
|
break;
|
|
|
|
case 'c': /* cAA..AA Continue at address AA..AA(optional) */
|
|
/* try to read optional parameter, pc unchanged if no parm */
|
|
|
|
sputs("c: PC="); print_word(registers[PC]);sputs("\n");
|
|
if (hexToInt(&ptr, &addr))
|
|
{
|
|
registers[PC] = addr;
|
|
sputs("c: addr="); print_word(addr);sputs("\n");
|
|
}
|
|
|
|
/* Need to flush the instruction cache here, as we may have deposited a
|
|
breakpoint, and the icache probably has no way of knowing that a data ref to
|
|
some location may have changed something that is in the instruction cache.
|
|
*/
|
|
|
|
flush_i_cache();
|
|
return;
|
|
|
|
/* kill the program */
|
|
case 'k' : /* do nothing */
|
|
sputs("Kill\n");
|
|
break;
|
|
#if 0
|
|
case 't': /* Test feature */
|
|
break;
|
|
#endif
|
|
case 'r': /* Reset */
|
|
// Perform reset
|
|
sputs("Reset\n");
|
|
break;
|
|
} /* switch */
|
|
/* reply to the request */
|
|
putpacket(remcomOutBuffer);
|
|
}
|
|
sputs("Exit\n");
|
|
|
|
}
|