- added
git-svn-id: http://moon:8086/svn/mips@88 a8ebac50-d88d-4704-bea3-6648445a41b3
This commit is contained in:
@@ -0,0 +1,601 @@
|
||||
/****************************************************************************
|
||||
|
||||
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 void flush_i_cache();
|
||||
|
||||
void putDebugChar(char c)
|
||||
{
|
||||
dbg_putchar(c);
|
||||
}
|
||||
|
||||
int getDebugChar()
|
||||
{
|
||||
return (int)dbg_getchar();
|
||||
}
|
||||
|
||||
void flush_i_cache()
|
||||
{
|
||||
ICACHE_invalidate_all();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
|
||||
/* at least NUMREGBYTES*2 are needed for register packets */
|
||||
#define BUFMAX 2048
|
||||
|
||||
static int initialized = 0; /* !0 means we've been initialized */
|
||||
static 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;
|
||||
}
|
||||
|
||||
static char remcomInBuffer[BUFMAX];
|
||||
static char remcomOutBuffer[BUFMAX];
|
||||
|
||||
/* scan for the sequence $<data>#<checksum> */
|
||||
|
||||
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 = 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. */
|
||||
static volatile 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. */
|
||||
|
||||
static struct hard_trap_info
|
||||
{
|
||||
unsigned char tt; /* Trap type code for SPARClite */
|
||||
unsigned char signo; /* Signal that we map this trap into */
|
||||
} 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 */
|
||||
};
|
||||
|
||||
/* Set up exception handlers for tracing and breakpoints */
|
||||
|
||||
void
|
||||
set_debug_traps (void)
|
||||
{
|
||||
// Register exception handler
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
/* 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
|
||||
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;
|
||||
unsigned long hexValue;
|
||||
|
||||
*intValue = 0;
|
||||
|
||||
while (**ptr)
|
||||
{
|
||||
hexValue = hex(**ptr);
|
||||
if (hexValue < 0)
|
||||
break;
|
||||
|
||||
*intValue = (*intValue << 4) | hexValue;
|
||||
numChars ++;
|
||||
|
||||
(*ptr)++;
|
||||
}
|
||||
return (numChars);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
unsigned long tt; /* Trap type */
|
||||
unsigned long sigval;
|
||||
unsigned long addr;
|
||||
unsigned long length;
|
||||
char *ptr;
|
||||
unsigned long *sp;
|
||||
|
||||
// branch shadow
|
||||
if (registers[CR] & 0x80000000)
|
||||
{
|
||||
registers[PC] += 4;
|
||||
}
|
||||
|
||||
sp = (unsigned long *)registers[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[PC >> 4];
|
||||
*ptr++ = hexchars[PC & 0xf];
|
||||
*ptr++ = ':';
|
||||
ptr = mem2hex((char *)®isters[PC], ptr, 4, 0);
|
||||
*ptr++ = ';';
|
||||
|
||||
*ptr++ = hexchars[R29 >> 4];
|
||||
*ptr++ = hexchars[R29 & 0xf];
|
||||
*ptr++ = ':';
|
||||
ptr = mem2hex((char *)sp, ptr, 4, 0);
|
||||
*ptr++ = ';';
|
||||
|
||||
*ptr++ = 0;
|
||||
|
||||
putpacket(remcomOutBuffer);
|
||||
|
||||
while (1)
|
||||
{
|
||||
remcomOutBuffer[0] = 0;
|
||||
|
||||
ptr = getpacket();
|
||||
switch (*ptr++)
|
||||
{
|
||||
case '?':
|
||||
remcomOutBuffer[0] = 'S';
|
||||
remcomOutBuffer[1] = hexchars[sigval >> 4];
|
||||
remcomOutBuffer[2] = hexchars[sigval & 0xf];
|
||||
remcomOutBuffer[3] = 0;
|
||||
break;
|
||||
|
||||
case 'd': /* toggle debug flag */
|
||||
break;
|
||||
|
||||
case 'g': /* return the value of the CPU registers */
|
||||
{
|
||||
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*/
|
||||
memset(ptr, '0', 8 * 32);
|
||||
ptr += 8*32;
|
||||
/* FCSR, FIR, RESTART*/
|
||||
memset(ptr, '0', 8 * 3);
|
||||
ptr += 8*3;
|
||||
*ptr = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'G': /* set the value of the CPU registers - return OK */
|
||||
{
|
||||
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;
|
||||
/* See if the stack pointer has moved. If so, then copy the saved
|
||||
locals and ins to the new location. This keeps the window
|
||||
overflow and underflow routines happy. */
|
||||
|
||||
newsp = (unsigned long *)registers[R29];
|
||||
if (sp != newsp)
|
||||
{
|
||||
sp = memcpy(newsp, sp, 16 * 4);
|
||||
}
|
||||
|
||||
/* Don't allow SR:iec, SR:kuc to be modified. */
|
||||
if (sr != registers[SR])
|
||||
{
|
||||
registers[SR] = (sr & ~0x3);
|
||||
}
|
||||
strcpy(remcomOutBuffer,"OK");
|
||||
}
|
||||
break;
|
||||
|
||||
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))
|
||||
{
|
||||
if (mem2hex((char *)addr, remcomOutBuffer, length, 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
strcpy (remcomOutBuffer, "E03");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(remcomOutBuffer,"E01");
|
||||
}
|
||||
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))
|
||||
{
|
||||
strcpy(remcomOutBuffer, "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(remcomOutBuffer, "E03");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(remcomOutBuffer, "E02");
|
||||
}
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
break;
|
||||
#if 0
|
||||
case 't': /* Test feature */
|
||||
break;
|
||||
#endif
|
||||
case 'r': /* Reset */
|
||||
// Perform reset
|
||||
break;
|
||||
} /* switch */
|
||||
/* reply to the request */
|
||||
putpacket(remcomOutBuffer);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user