| /* taken from arch/ppc/kernel/ppc-stub.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 its 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 | 
 |  *    qOffsets      Get section offsets.  Reply is Text=xxx;Data=yyy;Bss=zzz | 
 |  * | 
 |  *    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) | 
 |  * | 
 |  *    bBB..BB	    Set baud rate to BB..BB		   OK or BNN, then sets | 
 |  *							   baud rate | 
 |  * | 
 |  * 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 <common.h> | 
 |  | 
 | #include <kgdb.h> | 
 | #include <command.h> | 
 |  | 
 | #if defined(CONFIG_CMD_KGDB) | 
 |  | 
 | #undef KGDB_DEBUG | 
 |  | 
 | /* | 
 |  * BUFMAX defines the maximum number of characters in inbound/outbound buffers | 
 |  */ | 
 | #define BUFMAX 1024 | 
 | static char remcomInBuffer[BUFMAX]; | 
 | static char remcomOutBuffer[BUFMAX]; | 
 | static char remcomRegBuffer[BUFMAX]; | 
 |  | 
 | static int initialized = 0; | 
 | static int kgdb_active = 0, first_entry = 1; | 
 | static struct pt_regs entry_regs; | 
 | static u_int error_jmp_buf[BUFMAX/2]; | 
 | static int longjmp_on_fault = 0; | 
 | #ifdef KGDB_DEBUG | 
 | static int kdebug = 1; | 
 | #endif | 
 |  | 
 | static const char 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 (null). | 
 |  */ | 
 | static unsigned char * | 
 | mem2hex(char *mem, char *buf, int count) | 
 | { | 
 | 	unsigned char ch; | 
 |  | 
 | 	longjmp_on_fault = 1; | 
 | 	while (count-- > 0) { | 
 | 		ch = *mem++; | 
 | 		*buf++ = hexchars[ch >> 4]; | 
 | 		*buf++ = hexchars[ch & 0xf]; | 
 | 	} | 
 | 	*buf = 0; | 
 | 	longjmp_on_fault = 0; | 
 | 	return (unsigned char *)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 fetched from buf. | 
 | */ | 
 | static char * | 
 | hex2mem(char *buf, char *mem, int count) | 
 | { | 
 | 	int i, hexValue; | 
 | 	unsigned char ch; | 
 | 	char *mem_start = mem; | 
 |  | 
 | 	longjmp_on_fault = 1; | 
 | 	for (i=0; i<count; i++) { | 
 | 		if ((hexValue = hex(*buf++)) < 0) | 
 | 			kgdb_error(KGDBERR_NOTHEXDIG); | 
 | 		ch = hexValue << 4; | 
 | 		if ((hexValue = hex(*buf++)) < 0) | 
 | 			kgdb_error(KGDBERR_NOTHEXDIG); | 
 | 		ch |= hexValue; | 
 | 		*mem++ = ch; | 
 | 	} | 
 | 	kgdb_flush_cache_range((void *)mem_start, (void *)(mem - 1)); | 
 | 	longjmp_on_fault = 0; | 
 |  | 
 | 	return buf; | 
 | } | 
 |  | 
 | /* | 
 |  * While we find nice hex chars, build an int. | 
 |  * Return number of chars processed. | 
 |  */ | 
 | static int | 
 | hexToInt(char **ptr, int *intValue) | 
 | { | 
 | 	int numChars = 0; | 
 | 	int hexValue; | 
 |  | 
 | 	*intValue = 0; | 
 |  | 
 | 	longjmp_on_fault = 1; | 
 | 	while (**ptr) { | 
 | 		hexValue = hex(**ptr); | 
 | 		if (hexValue < 0) | 
 | 			break; | 
 |  | 
 | 		*intValue = (*intValue << 4) | hexValue; | 
 | 		numChars ++; | 
 |  | 
 | 		(*ptr)++; | 
 | 	} | 
 | 	longjmp_on_fault = 0; | 
 |  | 
 | 	return (numChars); | 
 | } | 
 |  | 
 | /* scan for the sequence $<data>#<checksum>     */ | 
 | static void | 
 | getpacket(char *buffer) | 
 | { | 
 | 	unsigned char checksum; | 
 | 	unsigned char xmitcsum; | 
 | 	int i; | 
 | 	int count; | 
 | 	unsigned char ch; | 
 |  | 
 | 	do { | 
 | 		/* wait around for the start character, ignore all other | 
 | 		 * characters */ | 
 | 		while ((ch = (getDebugChar() & 0x7f)) != '$') { | 
 | #ifdef KGDB_DEBUG | 
 | 			if (kdebug) | 
 | 				putc(ch); | 
 | #endif | 
 | 			; | 
 | 		} | 
 |  | 
 | 		checksum = 0; | 
 | 		xmitcsum = -1; | 
 |  | 
 | 		count = 0; | 
 |  | 
 | 		/* now, read until a # or end of buffer is found */ | 
 | 		while (count < BUFMAX) { | 
 | 			ch = getDebugChar() & 0x7f; | 
 | 			if (ch == '#') | 
 | 				break; | 
 | 			checksum = checksum + ch; | 
 | 			buffer[count] = ch; | 
 | 			count = count + 1; | 
 | 		} | 
 |  | 
 | 		if (count >= BUFMAX) | 
 | 			continue; | 
 |  | 
 | 		buffer[count] = 0; | 
 |  | 
 | 		if (ch == '#') { | 
 | 			xmitcsum = hex(getDebugChar() & 0x7f) << 4; | 
 | 			xmitcsum |= hex(getDebugChar() & 0x7f); | 
 | 			if (checksum != xmitcsum) | 
 | 				putDebugChar('-');	/* failed checksum */ | 
 | 			else { | 
 | 				putDebugChar('+'); /* successful transfer */ | 
 | 				/* if a sequence char is present, reply the ID */ | 
 | 				if (buffer[2] == ':') { | 
 | 					putDebugChar(buffer[0]); | 
 | 					putDebugChar(buffer[1]); | 
 | 					/* remove sequence chars from buffer */ | 
 | 					count = strlen(buffer); | 
 | 					for (i=3; i <= count; i++) | 
 | 						buffer[i-3] = buffer[i]; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} while (checksum != xmitcsum); | 
 | } | 
 |  | 
 | /* send the packet in buffer.  */ | 
 | static void | 
 | putpacket(unsigned char *buffer) | 
 | { | 
 | 	unsigned char checksum; | 
 | 	int count; | 
 | 	unsigned char ch, recv; | 
 |  | 
 | 	/*  $<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]); | 
 | 		recv = getDebugChar(); | 
 | 	} while ((recv & 0x7f) != '+'); | 
 | } | 
 |  | 
 | /* | 
 |  * This function does all command processing for interfacing to gdb. | 
 |  */ | 
 | static int | 
 | handle_exception (struct pt_regs *regs) | 
 | { | 
 | 	int addr; | 
 | 	int length; | 
 | 	char *ptr; | 
 | 	kgdb_data kd; | 
 | 	int i; | 
 |  | 
 | 	if (!initialized) { | 
 | 		printf("kgdb: exception before kgdb is initialized! huh?\n"); | 
 | 		return (0); | 
 | 	} | 
 |  | 
 | 	/* probably should check which exception occured as well */ | 
 | 	if (longjmp_on_fault) { | 
 | 		longjmp_on_fault = 0; | 
 | 		kgdb_longjmp((long*)error_jmp_buf, KGDBERR_MEMFAULT); | 
 | 		panic("kgdb longjump failed!\n"); | 
 | 	} | 
 |  | 
 | 	if (kgdb_active) { | 
 | 		printf("kgdb: unexpected exception from within kgdb\n"); | 
 | 		return (0); | 
 | 	} | 
 | 	kgdb_active = 1; | 
 |  | 
 | 	kgdb_interruptible(0); | 
 |  | 
 | 	printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs)); | 
 |  | 
 | 	if (kgdb_setjmp((long*)error_jmp_buf) != 0) | 
 | 		panic("kgdb: error or fault in entry init!\n"); | 
 |  | 
 | 	kgdb_enter(regs, &kd); | 
 |  | 
 | 	if (first_entry) { | 
 | 		/* | 
 | 		 * the first time we enter kgdb, we save the processor | 
 | 		 * state so that we can return to the monitor if the | 
 | 		 * remote end quits gdb (or at least, tells us to quit | 
 | 		 * with the 'k' packet) | 
 | 		 */ | 
 | 		entry_regs = *regs; | 
 | 		first_entry = 0; | 
 | 	} | 
 |  | 
 | 	ptr = remcomOutBuffer; | 
 |  | 
 | 	*ptr++ = 'T'; | 
 |  | 
 | 	*ptr++ = hexchars[kd.sigval >> 4]; | 
 | 	*ptr++ = hexchars[kd.sigval & 0xf]; | 
 |  | 
 | 	for (i = 0; i < kd.nregs; i++) { | 
 | 		kgdb_reg *rp = &kd.regs[i]; | 
 |  | 
 | 		*ptr++ = hexchars[rp->num >> 4]; | 
 | 		*ptr++ = hexchars[rp->num & 0xf]; | 
 | 		*ptr++ = ':'; | 
 | 		ptr = (char *)mem2hex((char *)&rp->val, ptr, 4); | 
 | 		*ptr++ = ';'; | 
 | 	} | 
 |  | 
 | 	*ptr = 0; | 
 |  | 
 | #ifdef KGDB_DEBUG | 
 | 	if (kdebug) | 
 | 		printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); | 
 | #endif | 
 |  | 
 | 	putpacket((unsigned char *)&remcomOutBuffer); | 
 |  | 
 | 	while (1) { | 
 | 		volatile int errnum; | 
 |  | 
 | 		remcomOutBuffer[0] = 0; | 
 |  | 
 | 		getpacket(remcomInBuffer); | 
 | 		ptr = &remcomInBuffer[1]; | 
 |  | 
 | #ifdef KGDB_DEBUG | 
 | 		if (kdebug) | 
 | 			printf("kgdb:  remcomInBuffer: %s\n", remcomInBuffer); | 
 | #endif | 
 |  | 
 | 		errnum = kgdb_setjmp((long*)error_jmp_buf); | 
 |  | 
 | 		if (errnum == 0) switch (remcomInBuffer[0]) { | 
 |  | 
 | 		case '?':               /* report most recent signal */ | 
 | 			remcomOutBuffer[0] = 'S'; | 
 | 			remcomOutBuffer[1] = hexchars[kd.sigval >> 4]; | 
 | 			remcomOutBuffer[2] = hexchars[kd.sigval & 0xf]; | 
 | 			remcomOutBuffer[3] = 0; | 
 | 			break; | 
 |  | 
 | #ifdef KGDB_DEBUG | 
 | 		case 'd': | 
 | 			/* toggle debug flag */ | 
 | 			kdebug ^= 1; | 
 | 			break; | 
 | #endif | 
 |  | 
 | 		case 'g':	/* return the value of the CPU registers. */ | 
 | 			length = kgdb_getregs(regs, remcomRegBuffer, BUFMAX); | 
 | 			mem2hex(remcomRegBuffer, remcomOutBuffer, length); | 
 | 			break; | 
 |  | 
 | 		case 'G':   /* set the value of the CPU registers */ | 
 | 			length = strlen(ptr); | 
 | 			if ((length & 1) != 0) kgdb_error(KGDBERR_BADPARAMS); | 
 | 			hex2mem(ptr, remcomRegBuffer, length/2); | 
 | 			kgdb_putregs(regs, remcomRegBuffer, length/2); | 
 | 			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))	{ | 
 | 				mem2hex((char *)addr, remcomOutBuffer, length); | 
 | 			} else { | 
 | 				kgdb_error(KGDBERR_BADPARAMS); | 
 | 			} | 
 | 			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++ == ':') { | 
 | 				hex2mem(ptr, (char *)addr, length); | 
 | 				strcpy(remcomOutBuffer, "OK"); | 
 | 			} else { | 
 | 				kgdb_error(KGDBERR_BADPARAMS); | 
 | 			} | 
 | 			break; | 
 |  | 
 |  | 
 | 		case 'k':    /* kill the program, actually return to monitor */ | 
 | 			kd.extype = KGDBEXIT_KILL; | 
 | 			*regs = entry_regs; | 
 | 			first_entry = 1; | 
 | 			goto doexit; | 
 |  | 
 | 		case 'C':    /* CSS  continue with signal SS */ | 
 | 			*ptr = '\0';	/* ignore the signal number for now */ | 
 | 			/* fall through */ | 
 |  | 
 | 		case 'c':    /* cAA..AA  Continue; address AA..AA optional */ | 
 | 			/* try to read optional parameter, pc unchanged if no parm */ | 
 | 			kd.extype = KGDBEXIT_CONTINUE; | 
 |  | 
 | 			if (hexToInt(&ptr, &addr)) { | 
 | 				kd.exaddr = addr; | 
 | 				kd.extype |= KGDBEXIT_WITHADDR; | 
 | 			} | 
 |  | 
 | 			goto doexit; | 
 |  | 
 | 		case 'S':    /* SSS  single step with signal SS */ | 
 | 			*ptr = '\0';	/* ignore the signal number for now */ | 
 | 			/* fall through */ | 
 |  | 
 | 		case 's': | 
 | 			kd.extype = KGDBEXIT_SINGLE; | 
 |  | 
 | 			if (hexToInt(&ptr, &addr)) { | 
 | 				kd.exaddr = addr; | 
 | 				kd.extype |= KGDBEXIT_WITHADDR; | 
 | 			} | 
 |  | 
 | 		doexit: | 
 | /* 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. | 
 |  */ | 
 | 			kgdb_flush_cache_all(); | 
 | 			kgdb_exit(regs, &kd); | 
 | 			kgdb_active = 0; | 
 | 			kgdb_interruptible(1); | 
 | 			return (1); | 
 |  | 
 | 		case 'r':		/* Reset (if user process..exit ???)*/ | 
 | 			panic("kgdb reset."); | 
 | 			break; | 
 |  | 
 | 		case 'P':    /* Pr=v  set reg r to value v (r and v are hex) */ | 
 | 			if (hexToInt(&ptr, &addr) | 
 | 			    && *ptr++ == '=' | 
 | 			    && ((length = strlen(ptr)) & 1) == 0) { | 
 | 				hex2mem(ptr, remcomRegBuffer, length/2); | 
 | 				kgdb_putreg(regs, addr, | 
 | 					remcomRegBuffer, length/2); | 
 | 				strcpy(remcomOutBuffer,"OK"); | 
 | 			} else { | 
 | 				kgdb_error(KGDBERR_BADPARAMS); | 
 | 			} | 
 | 			break; | 
 | 		}			/* switch */ | 
 |  | 
 | 		if (errnum != 0) | 
 | 			sprintf(remcomOutBuffer, "E%02d", errnum); | 
 |  | 
 | #ifdef KGDB_DEBUG | 
 | 		if (kdebug) | 
 | 			printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); | 
 | #endif | 
 |  | 
 | 		/* reply to the request */ | 
 | 		putpacket((unsigned char *)&remcomOutBuffer); | 
 |  | 
 | 	} /* while(1) */ | 
 | } | 
 |  | 
 | /* | 
 |  * kgdb_init must be called *after* the | 
 |  * monitor is relocated into ram | 
 |  */ | 
 | void | 
 | kgdb_init(void) | 
 | { | 
 | 	kgdb_serial_init(); | 
 | 	debugger_exception_handler = handle_exception; | 
 | 	initialized = 1; | 
 |  | 
 | 	putDebugStr("kgdb ready\n"); | 
 | 	puts("ready\n"); | 
 | } | 
 |  | 
 | void | 
 | kgdb_error(int errnum) | 
 | { | 
 | 	longjmp_on_fault = 0; | 
 | 	kgdb_longjmp((long*)error_jmp_buf, errnum); | 
 | 	panic("kgdb_error: longjmp failed!\n"); | 
 | } | 
 |  | 
 | /* Output string in GDB O-packet format if GDB has connected. If nothing | 
 |    output, returns 0 (caller must then handle output). */ | 
 | int | 
 | kgdb_output_string (const char* s, unsigned int count) | 
 | { | 
 | 	char buffer[512]; | 
 |  | 
 | 	count = (count <= (sizeof(buffer) / 2 - 2)) | 
 | 		? count : (sizeof(buffer) / 2 - 2); | 
 |  | 
 | 	buffer[0] = 'O'; | 
 | 	mem2hex ((char *)s, &buffer[1], count); | 
 | 	putpacket((unsigned char *)&buffer); | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | void | 
 | breakpoint(void) | 
 | { | 
 | 	if (!initialized) { | 
 | 		printf("breakpoint() called b4 kgdb init\n"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	kgdb_breakpoint(0, 0); | 
 | } | 
 |  | 
 | int | 
 | do_kgdb(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | 
 | { | 
 |     printf("Entering KGDB mode via exception handler...\n\n"); | 
 |     kgdb_breakpoint(argc - 1, argv + 1); | 
 |     printf("\nReturned from KGDB mode\n"); | 
 |     return 0; | 
 | } | 
 |  | 
 | U_BOOT_CMD( | 
 | 	kgdb, CFG_MAXARGS, 1,	do_kgdb, | 
 | 	"kgdb    - enter gdb remote debug mode\n", | 
 | 	"[arg0 arg1 .. argN]\n" | 
 | 	"    - executes a breakpoint so that kgdb mode is\n" | 
 | 	"      entered via the exception handler. To return\n" | 
 | 	"      to the monitor, the remote gdb debugger must\n" | 
 | 	"      execute a \"continue\" or \"quit\" command.\n" | 
 | 	"\n" | 
 | 	"      if a program is loaded by the remote gdb, any args\n" | 
 | 	"      passed to the kgdb command are given to the loaded\n" | 
 | 	"      program if it is executed (see the \"hello_world\"\n" | 
 | 	"      example program in the U-Boot examples directory)." | 
 | ); | 
 | #else | 
 |  | 
 | int kgdb_not_configured = 1; | 
 |  | 
 | #endif |