Initial revision
diff --git a/common/kgdb.c b/common/kgdb.c
new file mode 100644
index 0000000..b563094
--- /dev/null
+++ b/common/kgdb.c
@@ -0,0 +1,580 @@
+/* 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 (CONFIG_COMMANDS & CFG_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 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 = mem2hex((char *)&rp->val, ptr, 4);
+		*ptr++ = ';';
+	}
+
+	*ptr = 0;
+
+#ifdef KGDB_DEBUG
+	if (kdebug)
+		printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer);
+#endif
+
+	putpacket(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(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(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;
+}
+
+#else
+
+int kgdb_not_configured = 1;
+
+#endif /* CFG_CMD_KGDB */