patch-1.3.48 linux/arch/mips/kernel/gdb-stub.c

Next file: linux/arch/mips/kernel/head.S
Previous file: linux/arch/mips/kernel/gdb-low.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.47/linux/arch/mips/kernel/gdb-stub.c linux/arch/mips/kernel/gdb-stub.c
@@ -0,0 +1,748 @@
+/*
+ *  arch/mips/kernel/gdb-stub.c
+ *
+ *  Originally written by Glenn Engel, Lake Stevens Instrument Division
+ *
+ *  Contributed by HP Systems
+ *
+ *  Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ *  Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
+ *  Send complaints, suggestions etc. to <andy@waldorf-gmbh.de>
+ *
+ *  Copyright (C) 1995 Andreas Busse
+ */
+
+/*
+ *  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 BREAK instruction.
+ *
+ *
+ *    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)
+ *
+ *    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 <linux/string.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/segment.h>
+#include <asm/cachectl.h>
+#include <asm/system.h>
+#include <asm/gdb-stub.h>
+
+/*
+ * external low-level support routines
+ */
+
+extern int putDebugChar(char c);    /* write a single character      */
+extern char getDebugChar(void);     /* read and return a single char */
+extern void fltr_set_mem_err(void);
+extern void trap_low(void);
+
+/*
+ * breakpoint and test functions
+ */
+extern void breakpoint(void);
+extern void breakinst(void);
+extern void adel(void);
+
+/*
+ * local prototypes
+ */
+
+static void getpacket(char *buffer);
+static void putpacket(char *buffer);
+static void set_mem_fault_trap(int enable);
+static int computeSignal(int tt);
+static int hex(unsigned char ch);
+static int hexToInt(char **ptr, int *intValue);
+static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault);
+void handle_exception(struct gdb_regs *regs);
+static void show_gdbregs(struct gdb_regs *regs);
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+
+static char input_buffer[BUFMAX];
+static char output_buffer[BUFMAX];
+static int initialized = 0;	/* !0 means we've been initialized */
+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;
+}
+
+/*
+ * 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)) != '$') ;
+
+		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 sequence 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(char *buffer)
+{
+	unsigned char checksum;
+	int count;
+	unsigned char ch;
+
+	/*
+	 * $<packet info>#<checksum>.
+	 */
+
+	do {
+		putDebugChar('$');
+		checksum = 0;
+		count = 0;
+
+		while ((ch = buffer[count]) != 0) {
+			if (!(putDebugChar(ch)))
+				return;
+			checksum += ch;
+			count += 1;
+		}
+
+		putDebugChar('#');
+		putDebugChar(hexchars[checksum >> 4]);
+		putDebugChar(hexchars[checksum & 0xf]);
+
+	}
+	while ((getDebugChar() & 0x7f) != '+');
+}
+
+
+/*
+ * 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(char *mem, char *buf, int count, int may_fault)
+{
+	unsigned char ch;
+
+/*	set_mem_fault_trap(may_fault); */
+
+	while (count-- > 0) {
+		ch = *(mem++);
+		if (mem_err)
+			return 0;
+		*buf++ = hexchars[ch >> 4];
+		*buf++ = hexchars[ch & 0xf];
+	}
+
+	*buf = 0;
+
+/*	set_mem_fault_trap(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(char *buf, char *mem, int count, int may_fault)
+{
+	int i;
+	unsigned char ch;
+
+/*	set_mem_fault_trap(may_fault); */
+
+	for (i=0; i<count; i++)
+	{
+		ch = hex(*buf++) << 4;
+		ch |= hex(*buf++);
+		*(mem++) = ch;
+		if (mem_err)
+			return 0;
+	}
+
+/*	set_mem_fault_trap(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 MIPS R3xxx and R4xxx */
+	unsigned char signo;		/* Signal that we map this trap into */
+} hard_trap_info[] = {
+	{ 4, SIGBUS },			/* address error (load) */
+	{ 5, SIGBUS },			/* address error (store) */
+	{ 6, SIGBUS },			/* instruction bus error */
+	{ 7, SIGBUS },			/* data bus error */
+	{ 9, SIGTRAP },			/* break */
+	{ 10, SIGILL },			/* reserved instruction */
+/*	{ 11, SIGILL },		*/	/* cpu unusable */
+	{ 12, SIGFPE },			/* overflow */
+	{ 13, SIGTRAP },		/* trap */
+	{ 14, SIGSEGV },		/* virtual instruction cache coherency */
+	{ 15, SIGFPE },			/* floating point exception */
+	{ 23, SIGSEGV },		/* watch */
+	{ 31, SIGSEGV },		/* virtual data cache coherency */
+	{ 0, 0}				/* Must be last */
+};
+
+
+/*
+ * Set up exception handlers for tracing and breakpoints
+ */
+void set_debug_traps(void)
+{
+	struct hard_trap_info *ht;
+
+	for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+		set_except_vector(ht->tt, trap_low);
+  
+	/*
+	 * In case GDB is started before us, ack any packets
+	 * (presumably "$?#xx") sitting there.
+	 */
+
+	putDebugChar ('+');
+	initialized = 1;
+
+	breakpoint();
+}
+
+
+/*
+ * Trap handler for memory errors.  This just sets mem_err to be non-zero.  It
+ * assumes that %l1 is non-zero.  This should be safe, as it is doubtful that
+ * 0 would ever contain code that could mem fault.  This routine will skip
+ * past the faulting instruction after setting mem_err.
+ */
+extern void fltr_set_mem_err(void)
+{
+  /* FIXME: Needs to be written... */
+}
+
+
+static void set_mem_fault_trap(int enable)
+{
+  mem_err = 0;
+
+#if 0
+  if (enable)
+    exceptionHandler(9, fltr_set_mem_err);
+  else
+    exceptionHandler(9, trap_low);
+#endif  
+}
+
+/*
+ * Convert the MIPS hardware trap type code to a unix signal number.
+ */
+static int computeSignal(int tt)
+{
+	struct hard_trap_info *ht;
+
+	for (ht = hard_trap_info; ht->tt && ht->signo; 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, int *intValue)
+{
+	int numChars = 0;
+	int 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 (struct gdb_regs *regs)
+{
+	int trap;			/* Trap type */
+	int sigval;
+	int addr;
+	int length;
+	char *ptr;
+	unsigned long *stack;
+
+#if 0	
+	printk("in handle_exception()\n");
+	show_gdbregs(regs);
+#endif
+	
+	/*
+	 * First check trap type. If this is CPU_UNUSABLE and CPU_ID is 1,
+	 * the simply switch the FPU on and return since this is no error
+	 * condition. kernel/traps.c does the same.
+	 * FIXME: This doesn't work yet, so we don't catch CPU_UNUSABLE
+	 * traps for now.
+	 */
+	trap = (regs->cp0_cause & 0x7c) >> 2;
+/*	printk("trap=%d\n",trap); */
+	if (trap == 11) {
+		if (((regs->cp0_cause >> CAUSEB_CE) & 3) == 1) {
+			regs->cp0_status |= ST0_CU1;
+			return;
+		}
+	}
+
+	/*
+	 * If we're in breakpoint() increment the PC
+	 */
+	if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst)		
+		regs->cp0_epc += 4;
+
+	stack = (long *)regs->reg29;			/* stack ptr */
+	sigval = computeSignal(trap);
+
+	/*
+	 * reply to host that an exception has occurred
+	 */
+	ptr = output_buffer;
+
+	/*
+	 * Send trap type (converted to signal)
+	 */
+	*ptr++ = 'T';
+	*ptr++ = hexchars[sigval >> 4];
+	*ptr++ = hexchars[sigval & 0xf];
+
+	/*
+	 * Send Error PC
+	 */
+	*ptr++ = hexchars[REG_EPC >> 4];
+	*ptr++ = hexchars[REG_EPC & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *)&regs->cp0_epc, ptr, 4, 0);
+	*ptr++ = ';';
+
+	/*
+	 * Send frame pointer
+	 */
+	*ptr++ = hexchars[REG_FP >> 4];
+	*ptr++ = hexchars[REG_FP & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *)&regs->reg30, ptr, 4, 0);
+	*ptr++ = ';';
+
+	/*
+	 * Send stack pointer
+	 */
+	*ptr++ = hexchars[REG_SP >> 4];
+	*ptr++ = hexchars[REG_SP & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *)&regs->reg29, ptr, 4, 0);
+	*ptr++ = ';';
+
+	*ptr++ = 0;
+	putpacket(output_buffer);	/* send it off... */
+
+	/*
+	 * Wait for input from remote GDB
+	 */
+	while (1) {
+		output_buffer[0] = 0;
+		getpacket(input_buffer);
+
+		switch (input_buffer[0])
+		{
+		case '?':
+			output_buffer[0] = 'S';
+			output_buffer[1] = hexchars[sigval >> 4];
+			output_buffer[2] = hexchars[sigval & 0xf];
+			output_buffer[3] = 0;
+			break;
+
+		case 'd':
+			/* toggle debug flag */
+			break;
+
+		/*
+		 * Return the value of the CPU registers
+		 */
+		case 'g':
+			ptr = output_buffer;
+			ptr = mem2hex((char *)&regs->reg0, ptr, 32*4, 0); /* r0...r31 */
+			ptr = mem2hex((char *)&regs->cp0_status, ptr, 6*4, 0); /* cp0 */
+			ptr = mem2hex((char *)&regs->fpr0, ptr, 32*4, 0); /* f0...31 */
+			ptr = mem2hex((char *)&regs->cp1_fsr, ptr, 2*4, 0); /* cp1 */
+			ptr = mem2hex((char *)&regs->frame_ptr, ptr, 2*4, 0); /* frp */
+			ptr = mem2hex((char *)&regs->cp0_index, ptr, 16*4, 0); /* cp0 */
+			break;
+	  
+		/*
+		 * set the value of the CPU registers - return OK
+		 * FIXME: Needs to be written
+		 */
+		case 'G':
+		{
+#if 0
+			unsigned long *newsp, psr;
+
+			ptr = &input_buffer[1];
+			hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */
+
+			/*
+			 * See if the stack pointer has moved. If so, then copy the
+			 * saved locals and ins to the new location.
+			 */
+
+			newsp = (unsigned long *)registers[SP];
+			if (sp != newsp)
+				sp = memcpy(newsp, sp, 16 * 4);
+
+#endif
+			strcpy(output_buffer,"OK");
+		 }
+		break;
+
+		/*
+		 * mAA..AA,LLLL  Read LLLL bytes at address AA..AA
+		 */
+		case 'm':
+			ptr = &input_buffer[1];
+
+			if (hexToInt(&ptr, &addr)
+				&& *ptr++ == ','
+				&& hexToInt(&ptr, &length)) {
+				if (mem2hex((char *)addr, output_buffer, length, 1))
+					break;
+				strcpy (output_buffer, "E03");
+			} else
+				strcpy(output_buffer,"E01");
+			break;
+
+		/*
+		 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK
+		 */
+		case 'M': 
+			ptr = &input_buffer[1];
+
+			if (hexToInt(&ptr, &addr)
+				&& *ptr++ == ','
+				&& hexToInt(&ptr, &length)
+				&& *ptr++ == ':') {
+				if (hex2mem(ptr, (char *)addr, length, 1))
+					strcpy(output_buffer, "OK");
+				else
+					strcpy(output_buffer, "E03");
+			}
+			else
+				strcpy(output_buffer, "E02");
+			break;
+
+		/*
+		 * cAA..AA    Continue at address AA..AA(optional)
+		 */
+		case 'c':    
+			/* try to read optional parameter, pc unchanged if no parm */
+
+			ptr = &input_buffer[1];
+			if (hexToInt(&ptr, &addr))
+				regs->cp0_epc = 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.
+			 * NB: We flush both caches, just to be sure...
+			 */
+
+			sys_cacheflush((void *)KSEG0,KSEG1-KSEG0,BCACHE);
+			return;
+			/* NOTREACHED */
+			break;
+
+
+		/*
+		 * kill the program
+		 */
+		case 'k' :
+			break;		/* do nothing */
+
+
+		/*
+		 * Reset the whole machine (FIXME: system dependent)
+		 */
+		case 'r':
+			break;
+
+
+		/*
+		 * Step to next instruction
+		 * FIXME: Needs to be written
+		 */
+		case 's':
+			strcpy (output_buffer, "S01");
+			break;
+
+		/*
+		 * Set baud rate (bBB)
+		 * FIXME: Needs to be written
+		 */
+		case 'b':
+		{
+#if 0				
+			int baudrate;
+			extern void set_timer_3();
+
+			ptr = &input_buffer[1];
+			if (!hexToInt(&ptr, &baudrate))
+			{
+				strcpy(output_buffer,"B01");
+				break;
+			}
+
+			/* Convert baud rate to uart clock divider */
+
+			switch (baudrate)
+			{
+				case 38400:
+					baudrate = 16;
+					break;
+				case 19200:
+					baudrate = 33;
+					break;
+				case 9600:
+					baudrate = 65;
+					break;
+				default:
+					baudrate = 0;
+					strcpy(output_buffer,"B02");
+					goto x1;
+			}
+
+			if (baudrate) {
+				putpacket("OK");	/* Ack before changing speed */
+				set_timer_3(baudrate); /* Set it */
+			}
+#endif
+		}
+		break;
+
+		}			/* switch */
+
+		/*
+		 * reply to the request
+		 */
+
+		putpacket(output_buffer);
+
+	} /* while */
+}
+
+/*
+ * This function will generate a breakpoint exception.  It is used at the
+ * beginning of a program to sync up with a debugger and can be used
+ * otherwise as a quick means to stop program execution and "break" into
+ * the debugger.
+ */
+void breakpoint(void)
+{
+	if (!initialized)
+		return;
+
+	__asm__ __volatile__("
+			.globl	breakinst
+			.set	noreorder
+			nop
+breakinst:		break
+			nop
+			.set	reorder
+	");
+}
+
+void adel(void)
+{
+	__asm__ __volatile__("
+			.globl	adel
+			la	$8,0x80000001
+			lw	$9,0($8)
+	");
+}
+
+/*
+ * Print registers (on target console)
+ * Used only to debug the stub...
+ */
+void show_gdbregs(struct gdb_regs * regs)
+{
+	/*
+	 * Saved main processor registers
+	 */
+	printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+	       regs->reg0, regs->reg1, regs->reg2, regs->reg3,
+               regs->reg4, regs->reg5, regs->reg6, regs->reg7);
+	printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+	       regs->reg8, regs->reg9, regs->reg10, regs->reg11,
+               regs->reg12, regs->reg13, regs->reg14, regs->reg15);
+	printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+	       regs->reg16, regs->reg17, regs->reg18, regs->reg19,
+               regs->reg20, regs->reg21, regs->reg22, regs->reg23);
+	printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+	       regs->reg24, regs->reg25, regs->reg26, regs->reg27,
+	       regs->reg28, regs->reg29, regs->reg30, regs->reg31);
+
+	/*
+	 * Saved cp0 registers
+	 */
+	printk("epc  : %08lx\nStatus: %08lx\nCause : %08lx\n",
+	       regs->cp0_epc, regs->cp0_status, regs->cp0_cause);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this