gdb.c

来自「umon bootloader source code, support mip」· C语言 代码 · 共 676 行 · 第 1/2 页

C
676
字号
/* gdb.c:
 *	The code in this file allows a gdb debugger on a host to connect to the
 *	monitor.  It supports both serial and network (udp) connections. 
 *	Note that this is only the cpu-independent portion of the GDB debug
 *	protocol.  The following commands in gdb have been verified to work
 *	with this code:
 *
 *		- target remote com1
 *		- target remote udp:135.222.140.68:1234
 *		- load and c 
 *		- info registers
 *		- x/16x 0xff800000
 *		- print varname
 *
 *	I'm sure other commands work, but these are the ones I've tested.
 *
 *	This interface was written from scratch.
 *	References used were:
 *	Bill Gatliff's ESP article and a description of the GDB Remote
 *	Serial Protocol I found at:
 *	http://developer/apple.com/documentation/DeveloperTools/gdb/gdb/gdb_32.htm
 *
 *	General notice:
 *	This code is part of a boot-monitor package developed as a generic base
 *	platform for embedded system designs.  As such, it is likely to be
 *	distributed to various projects beyond the control of the original
 *	author.  Please notify the author of any enhancements made or bugs found
 *	so that all may benefit from the changes.  In addition, notification back
 *	to the author will allow the new user to pick up changes that may have
 *	been made by other users after this version of the code was distributed.
 *
 *	Note1: the majority of this code was edited with 4-space tabs.
 *	Note2: as more and more contributions are accepted, the term "author"
 *		   is becoming a mis-representation of credit.
 *
 *	Original author:	Ed Sutter
 *	Email:				esutter@lucent.com
 *	Phone:				908-582-2351
 */
#include "config.h"
#include "genlib.h"
#include "ether.h"
#include "stddefs.h"
#include "endian.h"
#include "cli.h"

#if INCLUDE_GDB

#include "gdbregs.c"	/* CPU-specific register name table */

#define REGTBL_SIZE			(sizeof(gdb_regtbl)/sizeof(char *))

#define GDBERR_NOHASH		1	/* no '#' in command */
#define GDBERR_BADCSUM		2	/* bad checksum */
#define GDBERR_GENERR		3	/* general confusion */
#define GDBERR_BADXFMT		4	/* unexpected 'X' command format */
#define GDBERR_RNUMOOR		5	/* register number out of range */
#define GDBERR_NOSPACE		6	/* buffer not big enough for response */

/* gdbIbuf[]:
 * Input buffer used for storage of the incoming command from
 * the gdb debugger.
 */
static	uchar gdbIbuf[512];

/* gdbRbuf[]:
 * Response buffer used for the complete response destined
 * for the gdb host.
 */
static	uchar	gdbRbuf[1024];
static	int		gdbRlen;
static	void	(*gdbContinueFptr)();

/* gdbUdp:
 * Set if the gdb interaction is via UDP, else zero.
 * Obviously this code assumes there is no reentrancy to deal with.
 */
static int gdbUdp;

/* gdbTrace():
 * This is a function pointer that is loaded with either printf
 * or Mtrace... Use printf if gdb is running via UDP; else use
 * printf.
 */
static int	(*gdbTrace)(char *, ...);

/* gdb_response():
 * Utility function used by the other response functions to format
 * the complete response back to the gdb host.  All interaction with
 * the host is in ASCII.  The response message is preceded by '+$'
 * and and terminated with '#CC' where 'CC' is a checksum.
 */
int
gdb_response(char *line)
{
	uchar	csum, *resp;
	
	csum = 0;
	resp = gdbRbuf;
	*resp++ = '$';
	while(*line) {
		csum += *line;
		*resp++ = *line++;
	}
	resp += sprintf(resp,"#%02x",csum);
	*resp = 0;

	/* If gdbUdp is clear, then we assume that the gdb host is tied
	 * to the target's serial port, so just use printf to send the
	 * response. 
	 * If gdbUdp is set, then we assume the calling function will
	 * send the response (via ethernet).
	 */
	if (!gdbUdp)
		printf(gdbRbuf);

	gdbRlen = strlen(gdbRbuf);

	if (gdbRlen < 128)
		gdbTrace("GDB_RSP: %s\n",gdbRbuf);
	else
		gdbTrace("GDB_RSP: BIG\n");

	return(gdbRlen);
}

int
gdb_ok(void)
{
	return(gdb_response("OK"));
}

int
gdb_sig(int signal)
{
	char buf[8];

	sprintf(buf,"S%02d",signal);
	return(gdb_response(buf));
}

int
gdb_err(int errno)
{
	char buf[8];

	sprintf(buf,"E%02d",errno);
	return(gdb_response(buf));
}

/* gdb_m():
 * GDB memory read command...
 * Incoming command format is...
 *
 *		mADDR,LEN#CC
 *
 * where:
 *	'm'		is the "memory read" request
 *	'ADDR'	is the address from which the data is to be read
 *	'LEN'	is the number of bytes to be read
 *
 */
int
gdb_m(char *line)
{
	int		len, i;
	char 	*lp;
	uchar	*addr, *resp, buf[128];
	
	addr = (uchar *)strtol(line+1,&lp,16);
	len = (int)strtol(lp+1,0,16);
	if (len) {
		if (len*2 >= sizeof(buf)) {
			gdb_err(GDBERR_NOSPACE);
		}
		else {
			resp = buf;
			for(i=0;i<len;i++,addr++)
				resp += sprintf(resp,"%02x",*addr);
			gdb_response(buf);
		}
	}
	else
		gdb_ok();
	return(0);
}

/* gdb_M():
 * GDB memory write command...
 * Incoming command format is...
 *
 *		MADDR,LEN:DATA#CC
 *
 * where:
 *	'M'		is the "memory read" request
 *	'ADDR'	is the address from which the data is to be read
 *	'LEN'	is the number of bytes to be read
 *	'DATA'	is the ascii data
 *
 * STATUS: This function has been tested with m68k-elf-gdb (xtools)
 * and appears to work ok.
 */
int
gdb_M(char *line)
{
	int		len, i;
	char	*lp;
	uchar	*addr, buf[3];
	
	addr = (uchar *)strtol(line+1,&lp,16);
	len = (int)strtol(lp+1,&lp,16);
	lp++;

	buf[2] = 0;
	for(i=0;i<len;i++) {
		buf[0] = *lp++;
		buf[1] = *lp++;
		*addr++ = (uchar)strtol(buf,0,16);
	}
	gdb_ok();
	return(0);
}

/* gdb_X():
 * Similar to gdb_M() except that the data is in binary.
 * The characters '$', '#' and 0x7d are escaped using 0x7d.
 */
int
gdb_X(char *line)
{
	int		len, i;
	char	*lp;
	uchar	*addr;
	
	addr = (uchar *)strtol(line+1,&lp,16);
	len = (int)strtol(lp+1,&lp,16);
	lp++;

	for(i=0;i<len;i++) {
		if ((*lp == 0x7d) && 
			((*(lp+1) == 0x03) || (*(lp+1) == 0x04) || (*(lp+1) == 0x5d))) {
			*addr++ = *(lp+1) | 0x20;
			lp += 2;
		}
		else
			*addr++ = *lp++;
	}

	gdb_ok();
	return(0);
}

/* gdb_q():
 * Query.  Not sure what this is for, but according to Gatliff's
 * article, just do it...
 */
int
gdb_q(char *line)
{
	line++;

	if (strncmp(line,"Offsets",7) == 0)
		return(gdb_response("Text=0;Data=0;Bss=0"));
	else
		return(gdb_ok());
}

/* gdb_g():
 * Get all registers. 
 * GDB at the host expects to see a buffer of ASCII-coded hex values
 * with each register being 8-bytes (forming one 32-bit value).
 * The order of these registers is defined by the table included
 * in the file gdbregs.c above.
 */

int
gdb_g(char *line)
{
	int		i;
	ulong	reg;
	char	*resp, buf[(REGTBL_SIZE * 8) + 1];

	resp = buf;
	for(i=0;i<REGTBL_SIZE;i++) {
		if (gdb_regtbl[i] != 0)
			getreg(gdb_regtbl[i],&reg);
		else
			reg = 0;
		self_ecl(reg);
		resp += sprintf(resp,"%08lx",reg);
	}
	return(gdb_response(buf));
}

/* gdb_P():
 * Store to a register.
 */
int
gdb_P(char *line)
{
	char *lp;
	int rnum;
	ulong rval;

	line++;
	rnum = strtol(line,&lp,16);
	if (rnum >= REGTBL_SIZE) {
		gdb_err(GDBERR_RNUMOOR);	
		return(-1);
	}
	lp++;
	rval = strtol(lp,0,16);
	self_ecl(rval);
	putreg(gdb_regtbl[rnum],rval);

	gdb_ok();
	return(0);
}

/* gdb_c():
 * This is the function that is called as a result of the 'c' (continue)
 * command. 
 */ 
int
gdb_c(char *line)
{
	ulong	addr;
	void	(*func)();

	line++;
	if (*line == '#')
		getreg(CPU_PC_REG,&addr);
	else
		addr = strtol(line,0,16);

	func = (void(*)())addr;
	func();
	return(0);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?