mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 12:39:23 +00:00 
			
		
		
		
	 2f2a2132ff
			
		
	
	
		2f2a2132ff
		
	
	
	
	
		
			
			MN10300 arch headers and place them instead in the same directories as contain the .c files for the processor and unit implementations. This permits the symlinks include/asm/proc and include/asm/unit to be dispensed with. This does, however, require that #include <asm/proc/xxx.h> be converted to #include <proc/xxx.h> and similarly for asm/unit -> unit. Signed-off-by: David Howells <dhowells@redhat.com>
		
			
				
	
	
		
			1912 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1912 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* MN10300 GDB stub
 | |
|  *
 | |
|  * 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
 | |
|  *
 | |
|  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 | |
|  * Modified for Linux/mn10300 by David Howells <dhowells@redhat.com>
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  *  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
 | |
|  *
 | |
|  *
 | |
|  *  ==============
 | |
|  *  MORE EXAMPLES:
 | |
|  *  ==============
 | |
|  *
 | |
|  *  For reference -- the following are the steps that one
 | |
|  *  company took (RidgeRun Inc) to get remote gdb debugging
 | |
|  *  going. In this scenario the host machine was a PC and the
 | |
|  *  target platform was a Galileo EVB64120A MIPS evaluation
 | |
|  *  board.
 | |
|  *
 | |
|  *  Step 1:
 | |
|  *  First download gdb-5.0.tar.gz from the internet.
 | |
|  *  and then build/install the package.
 | |
|  *
 | |
|  *  Example:
 | |
|  *    $ tar zxf gdb-5.0.tar.gz
 | |
|  *    $ cd gdb-5.0
 | |
|  *    $ ./configure --target=am33_2.0-linux-gnu
 | |
|  *    $ make
 | |
|  *    $ install
 | |
|  *    am33_2.0-linux-gnu-gdb
 | |
|  *
 | |
|  *  Step 2:
 | |
|  *  Configure linux for remote debugging and build it.
 | |
|  *
 | |
|  *  Example:
 | |
|  *    $ cd ~/linux
 | |
|  *    $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging>
 | |
|  *    $ make dep; make vmlinux
 | |
|  *
 | |
|  *  Step 3:
 | |
|  *  Download the kernel to the remote target and start
 | |
|  *  the kernel running. It will promptly halt and wait
 | |
|  *  for the host gdb session to connect. It does this
 | |
|  *  since the "Kernel Hacking" option has defined
 | |
|  *  CONFIG_REMOTE_DEBUG which in turn enables your calls
 | |
|  *  to:
 | |
|  *     set_debug_traps();
 | |
|  *     breakpoint();
 | |
|  *
 | |
|  *  Step 4:
 | |
|  *  Start the gdb session on the host.
 | |
|  *
 | |
|  *  Example:
 | |
|  *    $ am33_2.0-linux-gnu-gdb vmlinux
 | |
|  *    (gdb) set remotebaud 115200
 | |
|  *    (gdb) target remote /dev/ttyS1
 | |
|  *    ...at this point you are connected to
 | |
|  *       the remote target and can use gdb
 | |
|  *       in the normal fasion. Setting
 | |
|  *       breakpoints, single stepping,
 | |
|  *       printing variables, etc.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/string.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/signal.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/mm.h>
 | |
| #include <linux/console.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/bug.h>
 | |
| 
 | |
| #include <asm/pgtable.h>
 | |
| #include <asm/system.h>
 | |
| #include <asm/gdb-stub.h>
 | |
| #include <asm/exceptions.h>
 | |
| #include <asm/cacheflush.h>
 | |
| #include <asm/serial-regs.h>
 | |
| #include <asm/busctl-regs.h>
 | |
| #include <unit/leds.h>
 | |
| #include <unit/serial.h>
 | |
| 
 | |
| /* define to use F7F7 rather than FF which is subverted by JTAG debugger */
 | |
| #undef GDBSTUB_USE_F7F7_AS_BREAKPOINT
 | |
| 
 | |
| /*
 | |
|  * BUFMAX defines the maximum number of characters in inbound/outbound buffers
 | |
|  * at least NUMREGBYTES*2 are needed for register packets
 | |
|  */
 | |
| #define BUFMAX 2048
 | |
| 
 | |
| static const char gdbstub_banner[] =
 | |
| 	"Linux/MN10300 GDB Stub (c) RedHat 2007\n";
 | |
| 
 | |
| u8	gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
 | |
| u32	gdbstub_rx_inp;
 | |
| u32	gdbstub_rx_outp;
 | |
| u8	gdbstub_busy;
 | |
| u8	gdbstub_rx_overflow;
 | |
| u8	gdbstub_rx_unget;
 | |
| 
 | |
| static u8	gdbstub_flush_caches;
 | |
| static char	input_buffer[BUFMAX];
 | |
| static char	output_buffer[BUFMAX];
 | |
| static char	trans_buffer[BUFMAX];
 | |
| 
 | |
| struct gdbstub_bkpt {
 | |
| 	u8	*addr;		/* address of breakpoint */
 | |
| 	u8	len;		/* size of breakpoint */
 | |
| 	u8	origbytes[7];	/* original bytes */
 | |
| };
 | |
| 
 | |
| static struct gdbstub_bkpt gdbstub_bkpts[256];
 | |
| 
 | |
| /*
 | |
|  * local prototypes
 | |
|  */
 | |
| static void getpacket(char *buffer);
 | |
| static int putpacket(char *buffer);
 | |
| static int computeSignal(enum exception_code excep);
 | |
| static int hex(unsigned char ch);
 | |
| static int hexToInt(char **ptr, int *intValue);
 | |
| static unsigned char *mem2hex(const void *mem, char *buf, int count,
 | |
| 			      int may_fault);
 | |
| static const char *hex2mem(const char *buf, void *_mem, int count,
 | |
| 			   int may_fault);
 | |
| 
 | |
| /*
 | |
|  * 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;
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_GDBSTUB_DEBUGGING
 | |
| 
 | |
| void debug_to_serial(const char *p, int n)
 | |
| {
 | |
| 	__debug_to_serial(p, n);
 | |
| 	/* gdbstub_console_write(NULL, p, n); */
 | |
| }
 | |
| 
 | |
| void gdbstub_printk(const char *fmt, ...)
 | |
| {
 | |
| 	va_list args;
 | |
| 	int len;
 | |
| 
 | |
| 	/* Emit the output into the temporary buffer */
 | |
| 	va_start(args, fmt);
 | |
| 	len = vsnprintf(trans_buffer, sizeof(trans_buffer), fmt, args);
 | |
| 	va_end(args);
 | |
| 	debug_to_serial(trans_buffer, len);
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| static inline char *gdbstub_strcpy(char *dst, const char *src)
 | |
| {
 | |
| 	int loop = 0;
 | |
| 	while ((dst[loop] = src[loop]))
 | |
| 	       loop++;
 | |
| 	return dst;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * scan for the sequence $<data>#<checksum>
 | |
|  */
 | |
| static void getpacket(char *buffer)
 | |
| {
 | |
| 	unsigned char checksum;
 | |
| 	unsigned char xmitcsum;
 | |
| 	unsigned char ch;
 | |
| 	int count, i, ret, error;
 | |
| 
 | |
| 	for (;;) {
 | |
| 		/*
 | |
| 		 * wait around for the start character,
 | |
| 		 * ignore all other characters
 | |
| 		 */
 | |
| 		do {
 | |
| 			gdbstub_io_rx_char(&ch, 0);
 | |
| 		} while (ch != '$');
 | |
| 
 | |
| 		checksum = 0;
 | |
| 		xmitcsum = -1;
 | |
| 		count = 0;
 | |
| 		error = 0;
 | |
| 
 | |
| 		/*
 | |
| 		 * now, read until a # or end of buffer is found
 | |
| 		 */
 | |
| 		while (count < BUFMAX) {
 | |
| 			ret = gdbstub_io_rx_char(&ch, 0);
 | |
| 			if (ret < 0)
 | |
| 				error = ret;
 | |
| 
 | |
| 			if (ch == '#')
 | |
| 				break;
 | |
| 			checksum += ch;
 | |
| 			buffer[count] = ch;
 | |
| 			count++;
 | |
| 		}
 | |
| 
 | |
| 		if (error == -EIO) {
 | |
| 			gdbstub_proto("### GDB Rx Error - Skipping packet"
 | |
| 				      " ###\n");
 | |
| 			gdbstub_proto("### GDB Tx NAK\n");
 | |
| 			gdbstub_io_tx_char('-');
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		if (count >= BUFMAX || error)
 | |
| 			continue;
 | |
| 
 | |
| 		buffer[count] = 0;
 | |
| 
 | |
| 		/* read the checksum */
 | |
| 		ret = gdbstub_io_rx_char(&ch, 0);
 | |
| 		if (ret < 0)
 | |
| 			error = ret;
 | |
| 		xmitcsum = hex(ch) << 4;
 | |
| 
 | |
| 		ret = gdbstub_io_rx_char(&ch, 0);
 | |
| 		if (ret < 0)
 | |
| 			error = ret;
 | |
| 		xmitcsum |= hex(ch);
 | |
| 
 | |
| 		if (error) {
 | |
| 			if (error == -EIO)
 | |
| 				gdbstub_io("### GDB Rx Error -"
 | |
| 					   " Skipping packet\n");
 | |
| 			gdbstub_io("### GDB Tx NAK\n");
 | |
| 			gdbstub_io_tx_char('-');
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* check the checksum */
 | |
| 		if (checksum != xmitcsum) {
 | |
| 			gdbstub_io("### GDB Tx NAK\n");
 | |
| 			gdbstub_io_tx_char('-');	/* failed checksum */
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum);
 | |
| 		gdbstub_io("### GDB Tx ACK\n");
 | |
| 		gdbstub_io_tx_char('+'); /* successful transfer */
 | |
| 
 | |
| 		/*
 | |
| 		 * if a sequence char is present,
 | |
| 		 * reply the sequence ID
 | |
| 		 */
 | |
| 		if (buffer[2] == ':') {
 | |
| 			gdbstub_io_tx_char(buffer[0]);
 | |
| 			gdbstub_io_tx_char(buffer[1]);
 | |
| 
 | |
| 			/*
 | |
| 			 * remove sequence chars from buffer
 | |
| 			 */
 | |
| 			count = 0;
 | |
| 			while (buffer[count])
 | |
| 				count++;
 | |
| 			for (i = 3; i <= count; i++)
 | |
| 				buffer[i - 3] = buffer[i];
 | |
| 		}
 | |
| 
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * send the packet in buffer.
 | |
|  * - return 0 if successfully ACK'd
 | |
|  * - return 1 if abandoned due to new incoming packet
 | |
|  */
 | |
| static int putpacket(char *buffer)
 | |
| {
 | |
| 	unsigned char checksum;
 | |
| 	unsigned char ch;
 | |
| 	int count;
 | |
| 
 | |
| 	/*
 | |
| 	 * $<packet info>#<checksum>.
 | |
| 	 */
 | |
| 	gdbstub_proto("### GDB Tx $'%s'#?? ###\n", buffer);
 | |
| 
 | |
| 	do {
 | |
| 		gdbstub_io_tx_char('$');
 | |
| 		checksum = 0;
 | |
| 		count = 0;
 | |
| 
 | |
| 		while ((ch = buffer[count]) != 0) {
 | |
| 			gdbstub_io_tx_char(ch);
 | |
| 			checksum += ch;
 | |
| 			count += 1;
 | |
| 		}
 | |
| 
 | |
| 		gdbstub_io_tx_char('#');
 | |
| 		gdbstub_io_tx_char(hex_asc_hi(checksum));
 | |
| 		gdbstub_io_tx_char(hex_asc_lo(checksum));
 | |
| 
 | |
| 	} while (gdbstub_io_rx_char(&ch, 0),
 | |
| 		 ch == '-' && (gdbstub_io("### GDB Rx NAK\n"), 0),
 | |
| 		 ch != '-' && ch != '+' &&
 | |
| 		 (gdbstub_io("### GDB Rx ??? %02x\n", ch), 0),
 | |
| 		 ch != '+' && ch != '$');
 | |
| 
 | |
| 	if (ch == '+') {
 | |
| 		gdbstub_io("### GDB Rx ACK\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	gdbstub_io("### GDB Tx Abandoned\n");
 | |
| 	gdbstub_rx_unget = ch;
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * We single-step by setting breakpoints. When an exception
 | |
|  * is handled, we need to restore the instructions hoisted
 | |
|  * when the breakpoints were set.
 | |
|  *
 | |
|  * This is where we save the original instructions.
 | |
|  */
 | |
| static struct gdb_bp_save {
 | |
| 	u8	*addr;
 | |
| 	u8	opcode[2];
 | |
| } step_bp[2];
 | |
| 
 | |
| static const unsigned char gdbstub_insn_sizes[256] =
 | |
| {
 | |
| 	/* 1  2  3  4  5  6  7  8  9  a  b  c  d  e  f */
 | |
| 	1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3,	/* 0 */
 | |
| 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */
 | |
| 	2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */
 | |
| 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */
 | |
| 	1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */
 | |
| 	1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */
 | |
| 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
 | |
| 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
 | |
| 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */
 | |
| 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */
 | |
| 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */
 | |
| 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */
 | |
| 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */
 | |
| 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
 | |
| 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
 | |
| 	0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1  /* f */
 | |
| };
 | |
| 
 | |
| static int __gdbstub_mark_bp(u8 *addr, int ix)
 | |
| {
 | |
| 	if (addr < (u8 *) 0x70000000UL)
 | |
| 		return 0;
 | |
| 	/* 70000000-7fffffff: vmalloc area */
 | |
| 	if (addr < (u8 *) 0x80000000UL)
 | |
| 		goto okay;
 | |
| 	if (addr < (u8 *) 0x8c000000UL)
 | |
| 		return 0;
 | |
| 	/* 8c000000-93ffffff: SRAM, SDRAM */
 | |
| 	if (addr < (u8 *) 0x94000000UL)
 | |
| 		goto okay;
 | |
| 	return 0;
 | |
| 
 | |
| okay:
 | |
| 	if (gdbstub_read_byte(addr + 0, &step_bp[ix].opcode[0]) < 0 ||
 | |
| 	    gdbstub_read_byte(addr + 1, &step_bp[ix].opcode[1]) < 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	step_bp[ix].addr = addr;
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| static inline void __gdbstub_restore_bp(void)
 | |
| {
 | |
| #ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
 | |
| 	if (step_bp[0].addr) {
 | |
| 		gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0);
 | |
| 		gdbstub_write_byte(step_bp[0].opcode[1], step_bp[0].addr + 1);
 | |
| 	}
 | |
| 	if (step_bp[1].addr) {
 | |
| 		gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0);
 | |
| 		gdbstub_write_byte(step_bp[1].opcode[1], step_bp[1].addr + 1);
 | |
| 	}
 | |
| #else
 | |
| 	if (step_bp[0].addr)
 | |
| 		gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0);
 | |
| 	if (step_bp[1].addr)
 | |
| 		gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0);
 | |
| #endif
 | |
| 
 | |
| 	gdbstub_flush_caches = 1;
 | |
| 
 | |
| 	step_bp[0].addr		= NULL;
 | |
| 	step_bp[0].opcode[0]	= 0;
 | |
| 	step_bp[0].opcode[1]	= 0;
 | |
| 	step_bp[1].addr		= NULL;
 | |
| 	step_bp[1].opcode[0]	= 0;
 | |
| 	step_bp[1].opcode[1]	= 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * emulate single stepping by means of breakpoint instructions
 | |
|  */
 | |
| static int gdbstub_single_step(struct pt_regs *regs)
 | |
| {
 | |
| 	unsigned size;
 | |
| 	uint32_t x;
 | |
| 	uint8_t cur, *pc, *sp;
 | |
| 
 | |
| 	step_bp[0].addr		= NULL;
 | |
| 	step_bp[0].opcode[0]	= 0;
 | |
| 	step_bp[0].opcode[1]	= 0;
 | |
| 	step_bp[1].addr		= NULL;
 | |
| 	step_bp[1].opcode[0]	= 0;
 | |
| 	step_bp[1].opcode[1]	= 0;
 | |
| 	x = 0;
 | |
| 
 | |
| 	pc = (u8 *) regs->pc;
 | |
| 	sp = (u8 *) (regs + 1);
 | |
| 	if (gdbstub_read_byte(pc, &cur) < 0)
 | |
| 		return -EFAULT;
 | |
| 
 | |
| 	gdbstub_bkpt("Single Step from %p { %02x }\n", pc, cur);
 | |
| 
 | |
| 	gdbstub_flush_caches = 1;
 | |
| 
 | |
| 	size = gdbstub_insn_sizes[cur];
 | |
| 	if (size > 0) {
 | |
| 		if (!__gdbstub_mark_bp(pc + size, 0))
 | |
| 			goto fault;
 | |
| 	} else {
 | |
| 		switch (cur) {
 | |
| 			/* Bxx (d8,PC) */
 | |
| 		case 0xc0 ... 0xca:
 | |
| 			if (gdbstub_read_byte(pc + 1, (u8 *) &x) < 0)
 | |
| 				goto fault;
 | |
| 			if (!__gdbstub_mark_bp(pc + 2, 0))
 | |
| 				goto fault;
 | |
| 			if ((x < 0 || x > 2) &&
 | |
| 			    !__gdbstub_mark_bp(pc + (s8) x, 1))
 | |
| 				goto fault;
 | |
| 			break;
 | |
| 
 | |
| 			/* LXX (d8,PC) */
 | |
| 		case 0xd0 ... 0xda:
 | |
| 			if (!__gdbstub_mark_bp(pc + 1, 0))
 | |
| 				goto fault;
 | |
| 			if (regs->pc != regs->lar &&
 | |
| 			    !__gdbstub_mark_bp((u8 *) regs->lar, 1))
 | |
| 				goto fault;
 | |
| 			break;
 | |
| 
 | |
| 			/* SETLB - loads the next for bytes into the LIR
 | |
| 			 * register */
 | |
| 		case 0xdb:
 | |
| 			if (!__gdbstub_mark_bp(pc + 1, 0))
 | |
| 				goto fault;
 | |
| 			break;
 | |
| 
 | |
| 			/* JMP (d16,PC) or CALL (d16,PC) */
 | |
| 		case 0xcc:
 | |
| 		case 0xcd:
 | |
| 			if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 ||
 | |
| 			    gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0)
 | |
| 				goto fault;
 | |
| 			if (!__gdbstub_mark_bp(pc + (s16) x, 0))
 | |
| 				goto fault;
 | |
| 			break;
 | |
| 
 | |
| 			/* JMP (d32,PC) or CALL (d32,PC) */
 | |
| 		case 0xdc:
 | |
| 		case 0xdd:
 | |
| 			if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 ||
 | |
| 			    gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0 ||
 | |
| 			    gdbstub_read_byte(pc + 3, ((u8 *) &x) + 2) < 0 ||
 | |
| 			    gdbstub_read_byte(pc + 4, ((u8 *) &x) + 3) < 0)
 | |
| 				goto fault;
 | |
| 			if (!__gdbstub_mark_bp(pc + (s32) x, 0))
 | |
| 				goto fault;
 | |
| 			break;
 | |
| 
 | |
| 			/* RETF */
 | |
| 		case 0xde:
 | |
| 			if (!__gdbstub_mark_bp((u8 *) regs->mdr, 0))
 | |
| 				goto fault;
 | |
| 			break;
 | |
| 
 | |
| 			/* RET */
 | |
| 		case 0xdf:
 | |
| 			if (gdbstub_read_byte(pc + 2, (u8 *) &x) < 0)
 | |
| 				goto fault;
 | |
| 			sp += (s8)x;
 | |
| 			if (gdbstub_read_byte(sp + 0, ((u8 *) &x) + 0) < 0 ||
 | |
| 			    gdbstub_read_byte(sp + 1, ((u8 *) &x) + 1) < 0 ||
 | |
| 			    gdbstub_read_byte(sp + 2, ((u8 *) &x) + 2) < 0 ||
 | |
| 			    gdbstub_read_byte(sp + 3, ((u8 *) &x) + 3) < 0)
 | |
| 				goto fault;
 | |
| 			if (!__gdbstub_mark_bp((u8 *) x, 0))
 | |
| 				goto fault;
 | |
| 			break;
 | |
| 
 | |
| 		case 0xf0:
 | |
| 			if (gdbstub_read_byte(pc + 1, &cur) < 0)
 | |
| 				goto fault;
 | |
| 
 | |
| 			if (cur >= 0xf0 && cur <= 0xf7) {
 | |
| 				/* JMP (An) / CALLS (An) */
 | |
| 				switch (cur & 3) {
 | |
| 				case 0: x = regs->a0; break;
 | |
| 				case 1: x = regs->a1; break;
 | |
| 				case 2: x = regs->a2; break;
 | |
| 				case 3: x = regs->a3; break;
 | |
| 				}
 | |
| 				if (!__gdbstub_mark_bp((u8 *) x, 0))
 | |
| 					goto fault;
 | |
| 			} else if (cur == 0xfc) {
 | |
| 				/* RETS */
 | |
| 				if (gdbstub_read_byte(
 | |
| 					    sp + 0, ((u8 *) &x) + 0) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    sp + 1, ((u8 *) &x) + 1) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    sp + 2, ((u8 *) &x) + 2) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    sp + 3, ((u8 *) &x) + 3) < 0)
 | |
| 					goto fault;
 | |
| 				if (!__gdbstub_mark_bp((u8 *) x, 0))
 | |
| 					goto fault;
 | |
| 			} else if (cur == 0xfd) {
 | |
| 				/* RTI */
 | |
| 				if (gdbstub_read_byte(
 | |
| 					    sp + 4, ((u8 *) &x) + 0) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    sp + 5, ((u8 *) &x) + 1) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    sp + 6, ((u8 *) &x) + 2) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    sp + 7, ((u8 *) &x) + 3) < 0)
 | |
| 					goto fault;
 | |
| 				if (!__gdbstub_mark_bp((u8 *) x, 0))
 | |
| 					goto fault;
 | |
| 			} else {
 | |
| 				if (!__gdbstub_mark_bp(pc + 2, 0))
 | |
| 					goto fault;
 | |
| 			}
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 			/* potential 3-byte conditional branches */
 | |
| 		case 0xf8:
 | |
| 			if (gdbstub_read_byte(pc + 1, &cur) < 0)
 | |
| 				goto fault;
 | |
| 			if (!__gdbstub_mark_bp(pc + 3, 0))
 | |
| 				goto fault;
 | |
| 
 | |
| 			if (cur >= 0xe8 && cur <= 0xeb) {
 | |
| 				if (gdbstub_read_byte(
 | |
| 					    pc + 2, ((u8 *) &x) + 0) < 0)
 | |
| 					goto fault;
 | |
| 				if ((x < 0 || x > 3) &&
 | |
| 				    !__gdbstub_mark_bp(pc + (s8) x, 1))
 | |
| 					goto fault;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case 0xfa:
 | |
| 			if (gdbstub_read_byte(pc + 1, &cur) < 0)
 | |
| 				goto fault;
 | |
| 
 | |
| 			if (cur == 0xff) {
 | |
| 				/* CALLS (d16,PC) */
 | |
| 				if (gdbstub_read_byte(
 | |
| 					    pc + 2, ((u8 *) &x) + 0) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    pc + 3, ((u8 *) &x) + 1) < 0)
 | |
| 					goto fault;
 | |
| 				if (!__gdbstub_mark_bp(pc + (s16) x, 0))
 | |
| 					goto fault;
 | |
| 			} else {
 | |
| 				if (!__gdbstub_mark_bp(pc + 4, 0))
 | |
| 					goto fault;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case 0xfc:
 | |
| 			if (gdbstub_read_byte(pc + 1, &cur) < 0)
 | |
| 				goto fault;
 | |
| 			if (cur == 0xff) {
 | |
| 				/* CALLS (d32,PC) */
 | |
| 				if (gdbstub_read_byte(
 | |
| 					    pc + 2, ((u8 *) &x) + 0) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    pc + 3, ((u8 *) &x) + 1) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    pc + 4, ((u8 *) &x) + 2) < 0 ||
 | |
| 				    gdbstub_read_byte(
 | |
| 					    pc + 5, ((u8 *) &x) + 3) < 0)
 | |
| 					goto fault;
 | |
| 				if (!__gdbstub_mark_bp(
 | |
| 					    pc + (s32) x, 0))
 | |
| 					goto fault;
 | |
| 			} else {
 | |
| 				if (!__gdbstub_mark_bp(
 | |
| 					    pc + 6, 0))
 | |
| 					goto fault;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	gdbstub_bkpt("Step: %02x at %p; %02x at %p\n",
 | |
| 		     step_bp[0].opcode[0], step_bp[0].addr,
 | |
| 		     step_bp[1].opcode[0], step_bp[1].addr);
 | |
| 
 | |
| 	if (step_bp[0].addr) {
 | |
| #ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
 | |
| 		if (gdbstub_write_byte(0xF7, step_bp[0].addr + 0) < 0 ||
 | |
| 		    gdbstub_write_byte(0xF7, step_bp[0].addr + 1) < 0)
 | |
| 			goto fault;
 | |
| #else
 | |
| 		if (gdbstub_write_byte(0xFF, step_bp[0].addr + 0) < 0)
 | |
| 			goto fault;
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	if (step_bp[1].addr) {
 | |
| #ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
 | |
| 		if (gdbstub_write_byte(0xF7, step_bp[1].addr + 0) < 0 ||
 | |
| 		    gdbstub_write_byte(0xF7, step_bp[1].addr + 1) < 0)
 | |
| 			goto fault;
 | |
| #else
 | |
| 		if (gdbstub_write_byte(0xFF, step_bp[1].addr + 0) < 0)
 | |
| 			goto fault;
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
|  fault:
 | |
| 	/* uh-oh - silly address alert, try and restore things */
 | |
| 	__gdbstub_restore_bp();
 | |
| 	return -EFAULT;
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_GDBSTUB_CONSOLE
 | |
| 
 | |
| void gdbstub_console_write(struct console *con, const char *p, unsigned n)
 | |
| {
 | |
| 	static const char gdbstub_cr[] = { 0x0d };
 | |
| 	char outbuf[26];
 | |
| 	int qty;
 | |
| 	u8 busy;
 | |
| 
 | |
| 	busy = gdbstub_busy;
 | |
| 	gdbstub_busy = 1;
 | |
| 
 | |
| 	outbuf[0] = 'O';
 | |
| 
 | |
| 	while (n > 0) {
 | |
| 		qty = 1;
 | |
| 
 | |
| 		while (n > 0 && qty < 20) {
 | |
| 			mem2hex(p, outbuf + qty, 2, 0);
 | |
| 			qty += 2;
 | |
| 			if (*p == 0x0a) {
 | |
| 				mem2hex(gdbstub_cr, outbuf + qty, 2, 0);
 | |
| 				qty += 2;
 | |
| 			}
 | |
| 			p++;
 | |
| 			n--;
 | |
| 		}
 | |
| 
 | |
| 		outbuf[qty] = 0;
 | |
| 		putpacket(outbuf);
 | |
| 	}
 | |
| 
 | |
| 	gdbstub_busy = busy;
 | |
| }
 | |
| 
 | |
| static kdev_t gdbstub_console_dev(struct console *con)
 | |
| {
 | |
| 	return MKDEV(1, 3); /* /dev/null */
 | |
| }
 | |
| 
 | |
| static struct console gdbstub_console = {
 | |
| 	.name	= "gdb",
 | |
| 	.write	= gdbstub_console_write,
 | |
| 	.device	= gdbstub_console_dev,
 | |
| 	.flags	= CON_PRINTBUFFER,
 | |
| 	.index	= -1,
 | |
| };
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Convert the memory pointed to by mem into hex, placing result in buf.
 | |
|  * - if successful, return a pointer to the last char put in buf (NUL)
 | |
|  * - in case of mem fault, return NULL
 | |
|  * may_fault is non-zero if we are reading from arbitrary memory, but is
 | |
|  * currently not used.
 | |
|  */
 | |
| static
 | |
| unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault)
 | |
| {
 | |
| 	const u8 *mem = _mem;
 | |
| 	u8 ch[4];
 | |
| 
 | |
| 	if ((u32) mem & 1 && count >= 1) {
 | |
| 		if (gdbstub_read_byte(mem, ch) != 0)
 | |
| 			return 0;
 | |
| 		buf = pack_hex_byte(buf, ch[0]);
 | |
| 		mem++;
 | |
| 		count--;
 | |
| 	}
 | |
| 
 | |
| 	if ((u32) mem & 3 && count >= 2) {
 | |
| 		if (gdbstub_read_word(mem, ch) != 0)
 | |
| 			return 0;
 | |
| 		buf = pack_hex_byte(buf, ch[0]);
 | |
| 		buf = pack_hex_byte(buf, ch[1]);
 | |
| 		mem += 2;
 | |
| 		count -= 2;
 | |
| 	}
 | |
| 
 | |
| 	while (count >= 4) {
 | |
| 		if (gdbstub_read_dword(mem, ch) != 0)
 | |
| 			return 0;
 | |
| 		buf = pack_hex_byte(buf, ch[0]);
 | |
| 		buf = pack_hex_byte(buf, ch[1]);
 | |
| 		buf = pack_hex_byte(buf, ch[2]);
 | |
| 		buf = pack_hex_byte(buf, ch[3]);
 | |
| 		mem += 4;
 | |
| 		count -= 4;
 | |
| 	}
 | |
| 
 | |
| 	if (count >= 2) {
 | |
| 		if (gdbstub_read_word(mem, ch) != 0)
 | |
| 			return 0;
 | |
| 		buf = pack_hex_byte(buf, ch[0]);
 | |
| 		buf = pack_hex_byte(buf, ch[1]);
 | |
| 		mem += 2;
 | |
| 		count -= 2;
 | |
| 	}
 | |
| 
 | |
| 	if (count >= 1) {
 | |
| 		if (gdbstub_read_byte(mem, ch) != 0)
 | |
| 			return 0;
 | |
| 		buf = pack_hex_byte(buf, ch[0]);
 | |
| 	}
 | |
| 
 | |
| 	*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
 | |
|  * may_fault is non-zero if we are reading from arbitrary memory, but is
 | |
|  * currently not used.
 | |
|  */
 | |
| static
 | |
| const char *hex2mem(const char *buf, void *_mem, int count, int may_fault)
 | |
| {
 | |
| 	u8 *mem = _mem;
 | |
| 	union {
 | |
| 		u32 val;
 | |
| 		u8 b[4];
 | |
| 	} ch;
 | |
| 
 | |
| 	if ((u32) mem & 1 && count >= 1) {
 | |
| 		ch.b[0]  = hex(*buf++) << 4;
 | |
| 		ch.b[0] |= hex(*buf++);
 | |
| 		if (gdbstub_write_byte(ch.val, mem) != 0)
 | |
| 			return 0;
 | |
| 		mem++;
 | |
| 		count--;
 | |
| 	}
 | |
| 
 | |
| 	if ((u32) mem & 3 && count >= 2) {
 | |
| 		ch.b[0]  = hex(*buf++) << 4;
 | |
| 		ch.b[0] |= hex(*buf++);
 | |
| 		ch.b[1]  = hex(*buf++) << 4;
 | |
| 		ch.b[1] |= hex(*buf++);
 | |
| 		if (gdbstub_write_word(ch.val, mem) != 0)
 | |
| 			return 0;
 | |
| 		mem += 2;
 | |
| 		count -= 2;
 | |
| 	}
 | |
| 
 | |
| 	while (count >= 4) {
 | |
| 		ch.b[0]  = hex(*buf++) << 4;
 | |
| 		ch.b[0] |= hex(*buf++);
 | |
| 		ch.b[1]  = hex(*buf++) << 4;
 | |
| 		ch.b[1] |= hex(*buf++);
 | |
| 		ch.b[2]  = hex(*buf++) << 4;
 | |
| 		ch.b[2] |= hex(*buf++);
 | |
| 		ch.b[3]  = hex(*buf++) << 4;
 | |
| 		ch.b[3] |= hex(*buf++);
 | |
| 		if (gdbstub_write_dword(ch.val, mem) != 0)
 | |
| 			return 0;
 | |
| 		mem += 4;
 | |
| 		count -= 4;
 | |
| 	}
 | |
| 
 | |
| 	if (count >= 2) {
 | |
| 		ch.b[0]  = hex(*buf++) << 4;
 | |
| 		ch.b[0] |= hex(*buf++);
 | |
| 		ch.b[1]  = hex(*buf++) << 4;
 | |
| 		ch.b[1] |= hex(*buf++);
 | |
| 		if (gdbstub_write_word(ch.val, mem) != 0)
 | |
| 			return 0;
 | |
| 		mem += 2;
 | |
| 		count -= 2;
 | |
| 	}
 | |
| 
 | |
| 	if (count >= 1) {
 | |
| 		ch.b[0]  = hex(*buf++) << 4;
 | |
| 		ch.b[0] |= hex(*buf++);
 | |
| 		if (gdbstub_write_byte(ch.val, mem) != 0)
 | |
| 			return 0;
 | |
| 	}
 | |
| 
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This table contains the mapping between MN10300 exception codes, and
 | |
|  * signals, which are primarily what GDB understands.  It also indicates
 | |
|  * which hardware traps we need to commandeer when initializing the stub.
 | |
|  */
 | |
| static const struct excep_to_sig_map {
 | |
| 	enum exception_code	excep;	/* MN10300 exception code */
 | |
| 	unsigned char		signo;	/* Signal that we map this into */
 | |
| } excep_to_sig_map[] = {
 | |
| 	{ EXCEP_ITLBMISS,	SIGSEGV		},
 | |
| 	{ EXCEP_DTLBMISS,	SIGSEGV		},
 | |
| 	{ EXCEP_TRAP,		SIGTRAP		},
 | |
| 	{ EXCEP_ISTEP,		SIGTRAP		},
 | |
| 	{ EXCEP_IBREAK,		SIGTRAP		},
 | |
| 	{ EXCEP_OBREAK,		SIGTRAP		},
 | |
| 	{ EXCEP_UNIMPINS,	SIGILL		},
 | |
| 	{ EXCEP_UNIMPEXINS,	SIGILL		},
 | |
| 	{ EXCEP_MEMERR,		SIGSEGV		},
 | |
| 	{ EXCEP_MISALIGN,	SIGSEGV		},
 | |
| 	{ EXCEP_BUSERROR,	SIGBUS		},
 | |
| 	{ EXCEP_ILLINSACC,	SIGSEGV		},
 | |
| 	{ EXCEP_ILLDATACC,	SIGSEGV		},
 | |
| 	{ EXCEP_IOINSACC,	SIGSEGV		},
 | |
| 	{ EXCEP_PRIVINSACC,	SIGSEGV		},
 | |
| 	{ EXCEP_PRIVDATACC,	SIGSEGV		},
 | |
| 	{ EXCEP_FPU_DISABLED,	SIGFPE		},
 | |
| 	{ EXCEP_FPU_UNIMPINS,	SIGFPE		},
 | |
| 	{ EXCEP_FPU_OPERATION,	SIGFPE		},
 | |
| 	{ EXCEP_WDT,		SIGALRM		},
 | |
| 	{ EXCEP_NMI,		SIGQUIT		},
 | |
| 	{ EXCEP_IRQ_LEVEL0,	SIGINT		},
 | |
| 	{ EXCEP_IRQ_LEVEL1,	SIGINT		},
 | |
| 	{ EXCEP_IRQ_LEVEL2,	SIGINT		},
 | |
| 	{ EXCEP_IRQ_LEVEL3,	SIGINT		},
 | |
| 	{ EXCEP_IRQ_LEVEL4,	SIGINT		},
 | |
| 	{ EXCEP_IRQ_LEVEL5,	SIGINT		},
 | |
| 	{ EXCEP_IRQ_LEVEL6,	SIGINT		},
 | |
| 	{ 0, 0}
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * convert the MN10300 exception code into a UNIX signal number
 | |
|  */
 | |
| static int computeSignal(enum exception_code excep)
 | |
| {
 | |
| 	const struct excep_to_sig_map *map;
 | |
| 
 | |
| 	for (map = excep_to_sig_map; map->signo; map++)
 | |
| 		if (map->excep == excep)
 | |
| 			return map->signo;
 | |
| 
 | |
| 	return SIGHUP; /* default for things we don't know about */
 | |
| }
 | |
| 
 | |
| static u32 gdbstub_fpcr, gdbstub_fpufs_array[32];
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| static void gdbstub_store_fpu(void)
 | |
| {
 | |
| #ifdef CONFIG_FPU
 | |
| 
 | |
| 	asm volatile(
 | |
| 		"or %2,epsw\n"
 | |
| #ifdef CONFIG_MN10300_PROC_MN103E010
 | |
| 		"nop\n"
 | |
| 		"nop\n"
 | |
| #endif
 | |
| 		"mov %1, a1\n"
 | |
| 		"fmov fs0,  (a1+)\n"
 | |
| 		"fmov fs1,  (a1+)\n"
 | |
| 		"fmov fs2,  (a1+)\n"
 | |
| 		"fmov fs3,  (a1+)\n"
 | |
| 		"fmov fs4,  (a1+)\n"
 | |
| 		"fmov fs5,  (a1+)\n"
 | |
| 		"fmov fs6,  (a1+)\n"
 | |
| 		"fmov fs7,  (a1+)\n"
 | |
| 		"fmov fs8,  (a1+)\n"
 | |
| 		"fmov fs9,  (a1+)\n"
 | |
| 		"fmov fs10, (a1+)\n"
 | |
| 		"fmov fs11, (a1+)\n"
 | |
| 		"fmov fs12, (a1+)\n"
 | |
| 		"fmov fs13, (a1+)\n"
 | |
| 		"fmov fs14, (a1+)\n"
 | |
| 		"fmov fs15, (a1+)\n"
 | |
| 		"fmov fs16, (a1+)\n"
 | |
| 		"fmov fs17, (a1+)\n"
 | |
| 		"fmov fs18, (a1+)\n"
 | |
| 		"fmov fs19, (a1+)\n"
 | |
| 		"fmov fs20, (a1+)\n"
 | |
| 		"fmov fs21, (a1+)\n"
 | |
| 		"fmov fs22, (a1+)\n"
 | |
| 		"fmov fs23, (a1+)\n"
 | |
| 		"fmov fs24, (a1+)\n"
 | |
| 		"fmov fs25, (a1+)\n"
 | |
| 		"fmov fs26, (a1+)\n"
 | |
| 		"fmov fs27, (a1+)\n"
 | |
| 		"fmov fs28, (a1+)\n"
 | |
| 		"fmov fs29, (a1+)\n"
 | |
| 		"fmov fs30, (a1+)\n"
 | |
| 		"fmov fs31, (a1+)\n"
 | |
| 		"fmov fpcr, %0\n"
 | |
| 		: "=d"(gdbstub_fpcr)
 | |
| 		: "g" (&gdbstub_fpufs_array), "i"(EPSW_FE)
 | |
| 		: "a1"
 | |
| 		);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| static void gdbstub_load_fpu(void)
 | |
| {
 | |
| #ifdef CONFIG_FPU
 | |
| 
 | |
| 	asm volatile(
 | |
| 		"or %1,epsw\n"
 | |
| #ifdef CONFIG_MN10300_PROC_MN103E010
 | |
| 		"nop\n"
 | |
| 		"nop\n"
 | |
| #endif
 | |
| 		"mov %0, a1\n"
 | |
| 		"fmov (a1+), fs0\n"
 | |
| 		"fmov (a1+), fs1\n"
 | |
| 		"fmov (a1+), fs2\n"
 | |
| 		"fmov (a1+), fs3\n"
 | |
| 		"fmov (a1+), fs4\n"
 | |
| 		"fmov (a1+), fs5\n"
 | |
| 		"fmov (a1+), fs6\n"
 | |
| 		"fmov (a1+), fs7\n"
 | |
| 		"fmov (a1+), fs8\n"
 | |
| 		"fmov (a1+), fs9\n"
 | |
| 		"fmov (a1+), fs10\n"
 | |
| 		"fmov (a1+), fs11\n"
 | |
| 		"fmov (a1+), fs12\n"
 | |
| 		"fmov (a1+), fs13\n"
 | |
| 		"fmov (a1+), fs14\n"
 | |
| 		"fmov (a1+), fs15\n"
 | |
| 		"fmov (a1+), fs16\n"
 | |
| 		"fmov (a1+), fs17\n"
 | |
| 		"fmov (a1+), fs18\n"
 | |
| 		"fmov (a1+), fs19\n"
 | |
| 		"fmov (a1+), fs20\n"
 | |
| 		"fmov (a1+), fs21\n"
 | |
| 		"fmov (a1+), fs22\n"
 | |
| 		"fmov (a1+), fs23\n"
 | |
| 		"fmov (a1+), fs24\n"
 | |
| 		"fmov (a1+), fs25\n"
 | |
| 		"fmov (a1+), fs26\n"
 | |
| 		"fmov (a1+), fs27\n"
 | |
| 		"fmov (a1+), fs28\n"
 | |
| 		"fmov (a1+), fs29\n"
 | |
| 		"fmov (a1+), fs30\n"
 | |
| 		"fmov (a1+), fs31\n"
 | |
| 		"fmov %2, fpcr\n"
 | |
| 		:
 | |
| 		: "g" (&gdbstub_fpufs_array), "i"(EPSW_FE), "d"(gdbstub_fpcr)
 | |
| 		: "a1"
 | |
| 	);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * set a software breakpoint
 | |
|  */
 | |
| int gdbstub_set_breakpoint(u8 *addr, int len)
 | |
| {
 | |
| 	int bkpt, loop, xloop;
 | |
| 
 | |
| #ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
 | |
| 	len = (len + 1) & ~1;
 | |
| #endif
 | |
| 
 | |
| 	gdbstub_bkpt("setbkpt(%p,%d)\n", addr, len);
 | |
| 
 | |
| 	for (bkpt = 255; bkpt >= 0; bkpt--)
 | |
| 		if (!gdbstub_bkpts[bkpt].addr)
 | |
| 			break;
 | |
| 	if (bkpt < 0)
 | |
| 		return -ENOSPC;
 | |
| 
 | |
| 	for (loop = 0; loop < len; loop++)
 | |
| 		if (gdbstub_read_byte(&addr[loop],
 | |
| 				      &gdbstub_bkpts[bkpt].origbytes[loop]
 | |
| 				      ) < 0)
 | |
| 			return -EFAULT;
 | |
| 
 | |
| 	gdbstub_flush_caches = 1;
 | |
| 
 | |
| #ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
 | |
| 	for (loop = 0; loop < len; loop++)
 | |
| 		if (gdbstub_write_byte(0xF7, &addr[loop]) < 0)
 | |
| 			goto restore;
 | |
| #else
 | |
| 	for (loop = 0; loop < len; loop++)
 | |
| 		if (gdbstub_write_byte(0xFF, &addr[loop]) < 0)
 | |
| 			goto restore;
 | |
| #endif
 | |
| 
 | |
| 	gdbstub_bkpts[bkpt].addr = addr;
 | |
| 	gdbstub_bkpts[bkpt].len = len;
 | |
| 
 | |
| 	gdbstub_bkpt("Set BKPT[%02x]: %p-%p {%02x%02x%02x%02x%02x%02x%02x}\n",
 | |
| 		     bkpt,
 | |
| 		     gdbstub_bkpts[bkpt].addr,
 | |
| 		     gdbstub_bkpts[bkpt].addr + gdbstub_bkpts[bkpt].len - 1,
 | |
| 		     gdbstub_bkpts[bkpt].origbytes[0],
 | |
| 		     gdbstub_bkpts[bkpt].origbytes[1],
 | |
| 		     gdbstub_bkpts[bkpt].origbytes[2],
 | |
| 		     gdbstub_bkpts[bkpt].origbytes[3],
 | |
| 		     gdbstub_bkpts[bkpt].origbytes[4],
 | |
| 		     gdbstub_bkpts[bkpt].origbytes[5],
 | |
| 		     gdbstub_bkpts[bkpt].origbytes[6]
 | |
| 		     );
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| restore:
 | |
| 	for (xloop = 0; xloop < loop; xloop++)
 | |
| 		gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[xloop],
 | |
| 				   addr + xloop);
 | |
| 	return -EFAULT;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * clear a software breakpoint
 | |
|  */
 | |
| int gdbstub_clear_breakpoint(u8 *addr, int len)
 | |
| {
 | |
| 	int bkpt, loop;
 | |
| 
 | |
| #ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
 | |
| 	len = (len + 1) & ~1;
 | |
| #endif
 | |
| 
 | |
| 	gdbstub_bkpt("clearbkpt(%p,%d)\n", addr, len);
 | |
| 
 | |
| 	for (bkpt = 255; bkpt >= 0; bkpt--)
 | |
| 		if (gdbstub_bkpts[bkpt].addr == addr &&
 | |
| 		    gdbstub_bkpts[bkpt].len == len)
 | |
| 			break;
 | |
| 	if (bkpt < 0)
 | |
| 		return -ENOENT;
 | |
| 
 | |
| 	gdbstub_bkpts[bkpt].addr = NULL;
 | |
| 
 | |
| 	gdbstub_flush_caches = 1;
 | |
| 
 | |
| 	for (loop = 0; loop < len; loop++)
 | |
| 		if (gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[loop],
 | |
| 				       addr + loop) < 0)
 | |
| 			return -EFAULT;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function does all command processing for interfacing to gdb
 | |
|  * - returns 1 if the exception should be skipped, 0 otherwise.
 | |
|  */
 | |
| static int gdbstub(struct pt_regs *regs, enum exception_code excep)
 | |
| {
 | |
| 	unsigned long *stack;
 | |
| 	unsigned long epsw, mdr;
 | |
| 	uint32_t zero, ssp;
 | |
| 	uint8_t broke;
 | |
| 	char *ptr;
 | |
| 	int sigval;
 | |
| 	int addr;
 | |
| 	int length;
 | |
| 	int loop;
 | |
| 
 | |
| 	if (excep == EXCEP_FPU_DISABLED)
 | |
| 		return 0;
 | |
| 
 | |
| 	gdbstub_flush_caches = 0;
 | |
| 
 | |
| 	mn10300_set_gdbleds(1);
 | |
| 
 | |
| 	asm volatile("mov mdr,%0" : "=d"(mdr));
 | |
| 	asm volatile("mov epsw,%0" : "=d"(epsw));
 | |
| 	asm volatile("mov %0,epsw"
 | |
| 		     :: "d"((epsw & ~EPSW_IM) | EPSW_IE | EPSW_IM_1));
 | |
| 
 | |
| 	gdbstub_store_fpu();
 | |
| 
 | |
| #ifdef CONFIG_GDBSTUB_IMMEDIATE
 | |
| 	/* skip the initial pause loop */
 | |
| 	if (regs->pc == (unsigned long) __gdbstub_pause)
 | |
| 		regs->pc = (unsigned long) start_kernel;
 | |
| #endif
 | |
| 
 | |
| 	/* if we were single stepping, restore the opcodes hoisted for the
 | |
| 	 * breakpoint[s] */
 | |
| 	broke = 0;
 | |
| 	if ((step_bp[0].addr && step_bp[0].addr == (u8 *) regs->pc) ||
 | |
| 	    (step_bp[1].addr && step_bp[1].addr == (u8 *) regs->pc))
 | |
| 		broke = 1;
 | |
| 
 | |
| 	__gdbstub_restore_bp();
 | |
| 
 | |
| 	if (gdbstub_rx_unget) {
 | |
| 		sigval = SIGINT;
 | |
| 		if (gdbstub_rx_unget != 3)
 | |
| 			goto packet_waiting;
 | |
| 		gdbstub_rx_unget = 0;
 | |
| 	}
 | |
| 
 | |
| 	stack = (unsigned long *) regs->sp;
 | |
| 	sigval = broke ? SIGTRAP : computeSignal(excep);
 | |
| 
 | |
| 	/* send information about a BUG() */
 | |
| 	if (!user_mode(regs) && excep == EXCEP_SYSCALL15) {
 | |
| 		const struct bug_entry *bug;
 | |
| 
 | |
| 		bug = find_bug(regs->pc);
 | |
| 		if (bug)
 | |
| 			goto found_bug;
 | |
| 		length = snprintf(trans_buffer, sizeof(trans_buffer),
 | |
| 				  "BUG() at address %lx\n", regs->pc);
 | |
| 		goto send_bug_pkt;
 | |
| 
 | |
| 	found_bug:
 | |
| 		length = snprintf(trans_buffer, sizeof(trans_buffer),
 | |
| 				  "BUG() at address %lx (%s:%d)\n",
 | |
| 				  regs->pc, bug->file, bug->line);
 | |
| 
 | |
| 	send_bug_pkt:
 | |
| 		ptr = output_buffer;
 | |
| 		*ptr++ = 'O';
 | |
| 		ptr = mem2hex(trans_buffer, ptr, length, 0);
 | |
| 		*ptr = 0;
 | |
| 		putpacket(output_buffer);
 | |
| 
 | |
| 		regs->pc -= 2;
 | |
| 		sigval = SIGABRT;
 | |
| 	} else if (regs->pc == (unsigned long) __gdbstub_bug_trap) {
 | |
| 		regs->pc = regs->mdr;
 | |
| 		sigval = SIGABRT;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * send a message to the debugger's user saying what happened if it may
 | |
| 	 * not be clear cut (we can't map exceptions onto signals properly)
 | |
| 	 */
 | |
| 	if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) {
 | |
| 		static const char title[] = "Excep ", tbcberr[] = "BCBERR ";
 | |
| 		static const char crlf[] = "\r\n";
 | |
| 		char hx;
 | |
| 		u32 bcberr = BCBERR;
 | |
| 
 | |
| 		ptr = output_buffer;
 | |
| 		*ptr++ = 'O';
 | |
| 		ptr = mem2hex(title, ptr, sizeof(title) - 1, 0);
 | |
| 
 | |
| 		hx = hex_asc_hi(excep >> 8);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_lo(excep >> 8);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_hi(excep);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_lo(excep);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 
 | |
| 		ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0);
 | |
| 		*ptr = 0;
 | |
| 		putpacket(output_buffer);	/* send it off... */
 | |
| 
 | |
| 		/* BCBERR */
 | |
| 		ptr = output_buffer;
 | |
| 		*ptr++ = 'O';
 | |
| 		ptr = mem2hex(tbcberr, ptr, sizeof(tbcberr) - 1, 0);
 | |
| 
 | |
| 		hx = hex_asc_hi(bcberr >> 24);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_lo(bcberr >> 24);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_hi(bcberr >> 16);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_lo(bcberr >> 16);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_hi(bcberr >> 8);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_lo(bcberr >> 8);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_hi(bcberr);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 		hx = hex_asc_lo(bcberr);
 | |
| 		ptr = pack_hex_byte(ptr, hx);
 | |
| 
 | |
| 		ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0);
 | |
| 		*ptr = 0;
 | |
| 		putpacket(output_buffer);	/* send it off... */
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * tell the debugger that an exception has occurred
 | |
| 	 */
 | |
| 	ptr = output_buffer;
 | |
| 
 | |
| 	/*
 | |
| 	 * Send trap type (converted to signal)
 | |
| 	 */
 | |
| 	*ptr++ = 'T';
 | |
| 	ptr = pack_hex_byte(ptr, sigval);
 | |
| 
 | |
| 	/*
 | |
| 	 * Send Error PC
 | |
| 	 */
 | |
| 	ptr = pack_hex_byte(ptr, GDB_REGID_PC);
 | |
| 	*ptr++ = ':';
 | |
| 	ptr = mem2hex(®s->pc, ptr, 4, 0);
 | |
| 	*ptr++ = ';';
 | |
| 
 | |
| 	/*
 | |
| 	 * Send frame pointer
 | |
| 	 */
 | |
| 	ptr = pack_hex_byte(ptr, GDB_REGID_FP);
 | |
| 	*ptr++ = ':';
 | |
| 	ptr = mem2hex(®s->a3, ptr, 4, 0);
 | |
| 	*ptr++ = ';';
 | |
| 
 | |
| 	/*
 | |
| 	 * Send stack pointer
 | |
| 	 */
 | |
| 	ssp = (unsigned long) (regs + 1);
 | |
| 	ptr = pack_hex_byte(ptr, GDB_REGID_SP);
 | |
| 	*ptr++ = ':';
 | |
| 	ptr = mem2hex(&ssp, ptr, 4, 0);
 | |
| 	*ptr++ = ';';
 | |
| 
 | |
| 	*ptr++ = 0;
 | |
| 	putpacket(output_buffer);	/* send it off... */
 | |
| 
 | |
| packet_waiting:
 | |
| 	/*
 | |
| 	 * Wait for input from remote GDB
 | |
| 	 */
 | |
| 	while (1) {
 | |
| 		output_buffer[0] = 0;
 | |
| 		getpacket(input_buffer);
 | |
| 
 | |
| 		switch (input_buffer[0]) {
 | |
| 			/* request repeat of last signal number */
 | |
| 		case '?':
 | |
| 			output_buffer[0] = 'S';
 | |
| 			output_buffer[1] = hex_asc_hi(sigval);
 | |
| 			output_buffer[2] = hex_asc_lo(sigval);
 | |
| 			output_buffer[3] = 0;
 | |
| 			break;
 | |
| 
 | |
| 		case 'd':
 | |
| 			/* toggle debug flag */
 | |
| 			break;
 | |
| 
 | |
| 			/*
 | |
| 			 * Return the value of the CPU registers
 | |
| 			 */
 | |
| 		case 'g':
 | |
| 			zero = 0;
 | |
| 			ssp = (u32) (regs + 1);
 | |
| 			ptr = output_buffer;
 | |
| 			ptr = mem2hex(®s->d0, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->d1, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->d2, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->d3, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->a0, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->a1, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->a2, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->a3, ptr, 4, 0);
 | |
| 
 | |
| 			ptr = mem2hex(&ssp, ptr, 4, 0);		/* 8 */
 | |
| 			ptr = mem2hex(®s->pc, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->mdr, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->epsw, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->lir, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->lar, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->mdrq, ptr, 4, 0);
 | |
| 
 | |
| 			ptr = mem2hex(®s->e0, ptr, 4, 0);	/* 15 */
 | |
| 			ptr = mem2hex(®s->e1, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->e2, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->e3, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->e4, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->e5, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->e6, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->e7, ptr, 4, 0);
 | |
| 
 | |
| 			ptr = mem2hex(&ssp, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->sp, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->mcrh, ptr, 4, 0);	/* 26 */
 | |
| 			ptr = mem2hex(®s->mcrl, ptr, 4, 0);
 | |
| 			ptr = mem2hex(®s->mcvf, ptr, 4, 0);
 | |
| 
 | |
| 			ptr = mem2hex(&gdbstub_fpcr, ptr, 4, 0); /* 29 - FPCR */
 | |
| 			ptr = mem2hex(&zero, ptr, 4, 0);
 | |
| 			ptr = mem2hex(&zero, ptr, 4, 0);
 | |
| 			for (loop = 0; loop < 32; loop++)
 | |
| 				ptr = mem2hex(&gdbstub_fpufs_array[loop],
 | |
| 					      ptr, 4, 0); /* 32 - FS0-31 */
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 			/*
 | |
| 			 * set the value of the CPU registers - return OK
 | |
| 			 */
 | |
| 		case 'G':
 | |
| 		{
 | |
| 			const char *ptr;
 | |
| 
 | |
| 			ptr = &input_buffer[1];
 | |
| 			ptr = hex2mem(ptr, ®s->d0, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->d1, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->d2, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->d3, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->a0, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->a1, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->a2, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->a3, 4, 0);
 | |
| 
 | |
| 			ptr = hex2mem(ptr, &ssp, 4, 0);		/* 8 */
 | |
| 			ptr = hex2mem(ptr, ®s->pc, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->mdr, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->epsw, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->lir, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->lar, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->mdrq, 4, 0);
 | |
| 
 | |
| 			ptr = hex2mem(ptr, ®s->e0, 4, 0);	/* 15 */
 | |
| 			ptr = hex2mem(ptr, ®s->e1, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->e2, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->e3, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->e4, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->e5, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->e6, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->e7, 4, 0);
 | |
| 
 | |
| 			ptr = hex2mem(ptr, &ssp, 4, 0);
 | |
| 			ptr = hex2mem(ptr, &zero, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->sp, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->mcrh, 4, 0);	/* 26 */
 | |
| 			ptr = hex2mem(ptr, ®s->mcrl, 4, 0);
 | |
| 			ptr = hex2mem(ptr, ®s->mcvf, 4, 0);
 | |
| 
 | |
| 			ptr = hex2mem(ptr, &zero, 4, 0);	/* 29 - FPCR */
 | |
| 			ptr = hex2mem(ptr, &zero, 4, 0);
 | |
| 			ptr = hex2mem(ptr, &zero, 4, 0);
 | |
| 			for (loop = 0; loop < 32; loop++)     /* 32 - FS0-31 */
 | |
| 				ptr = hex2mem(ptr, &zero, 4, 0);
 | |
| 
 | |
| #if 0
 | |
| 			/*
 | |
| 			 * See if the stack pointer has moved. If so, then copy
 | |
| 			 * the saved locals and ins to the new location.
 | |
| 			 */
 | |
| 			unsigned long *newsp = (unsigned long *) registers[SP];
 | |
| 			if (sp != newsp)
 | |
| 				sp = memcpy(newsp, sp, 16 * 4);
 | |
| #endif
 | |
| 
 | |
| 			gdbstub_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;
 | |
| 				gdbstub_strcpy(output_buffer, "E03");
 | |
| 			} else {
 | |
| 				gdbstub_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))
 | |
| 					gdbstub_strcpy(output_buffer, "OK");
 | |
| 				else
 | |
| 					gdbstub_strcpy(output_buffer, "E03");
 | |
| 
 | |
| 				gdbstub_flush_caches = 1;
 | |
| 			} else {
 | |
| 				gdbstub_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->pc = addr;
 | |
| 			goto done;
 | |
| 
 | |
| 			/*
 | |
| 			 * kill the program
 | |
| 			 */
 | |
| 		case 'k' :
 | |
| 			goto done;	/* just continue */
 | |
| 
 | |
| 			/*
 | |
| 			 * Reset the whole machine (FIXME: system dependent)
 | |
| 			 */
 | |
| 		case 'r':
 | |
| 			break;
 | |
| 
 | |
| 			/*
 | |
| 			 * Step to next instruction
 | |
| 			 */
 | |
| 		case 's':
 | |
| 			/*
 | |
| 			 * using the T flag doesn't seem to perform single
 | |
| 			 * stepping (it seems to wind up being caught by the
 | |
| 			 * JTAG unit), so we have to use breakpoints and
 | |
| 			 * continue instead.
 | |
| 			 */
 | |
| 			if (gdbstub_single_step(regs) < 0)
 | |
| 				/* ignore any fault error for now */
 | |
| 				gdbstub_printk("unable to set single-step"
 | |
| 					       " bp\n");
 | |
| 			goto done;
 | |
| 
 | |
| 			/*
 | |
| 			 * Set baud rate (bBB)
 | |
| 			 */
 | |
| 		case 'b':
 | |
| 			do {
 | |
| 				int baudrate;
 | |
| 
 | |
| 				ptr = &input_buffer[1];
 | |
| 				if (!hexToInt(&ptr, &baudrate)) {
 | |
| 					gdbstub_strcpy(output_buffer, "B01");
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				if (baudrate) {
 | |
| 					/* ACK before changing speed */
 | |
| 					putpacket("OK");
 | |
| 					gdbstub_io_set_baud(baudrate);
 | |
| 				}
 | |
| 			} while (0);
 | |
| 			break;
 | |
| 
 | |
| 			/*
 | |
| 			 * Set breakpoint
 | |
| 			 */
 | |
| 		case 'Z':
 | |
| 			ptr = &input_buffer[1];
 | |
| 
 | |
| 			if (!hexToInt(&ptr, &loop) || *ptr++ != ',' ||
 | |
| 			    !hexToInt(&ptr, &addr) || *ptr++ != ',' ||
 | |
| 			    !hexToInt(&ptr, &length)
 | |
| 			    ) {
 | |
| 				gdbstub_strcpy(output_buffer, "E01");
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/* only support software breakpoints */
 | |
| 			gdbstub_strcpy(output_buffer, "E03");
 | |
| 			if (loop != 0 ||
 | |
| 			    length < 1 ||
 | |
| 			    length > 7 ||
 | |
| 			    (unsigned long) addr < 4096)
 | |
| 				break;
 | |
| 
 | |
| 			if (gdbstub_set_breakpoint((u8 *) addr, length) < 0)
 | |
| 				break;
 | |
| 
 | |
| 			gdbstub_strcpy(output_buffer, "OK");
 | |
| 			break;
 | |
| 
 | |
| 			/*
 | |
| 			 * Clear breakpoint
 | |
| 			 */
 | |
| 		case 'z':
 | |
| 			ptr = &input_buffer[1];
 | |
| 
 | |
| 			if (!hexToInt(&ptr, &loop) || *ptr++ != ',' ||
 | |
| 			    !hexToInt(&ptr, &addr) || *ptr++ != ',' ||
 | |
| 			    !hexToInt(&ptr, &length)
 | |
| 			    ) {
 | |
| 				gdbstub_strcpy(output_buffer, "E01");
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/* only support software breakpoints */
 | |
| 			gdbstub_strcpy(output_buffer, "E03");
 | |
| 			if (loop != 0 ||
 | |
| 			    length < 1 ||
 | |
| 			    length > 7 ||
 | |
| 			    (unsigned long) addr < 4096)
 | |
| 				break;
 | |
| 
 | |
| 			if (gdbstub_clear_breakpoint((u8 *) addr, length) < 0)
 | |
| 				break;
 | |
| 
 | |
| 			gdbstub_strcpy(output_buffer, "OK");
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 			gdbstub_proto("### GDB Unsupported Cmd '%s'\n",
 | |
| 				      input_buffer);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		/* reply to the request */
 | |
| 		putpacket(output_buffer);
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	/*
 | |
| 	 * 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...
 | |
| 	 */
 | |
| 	if (gdbstub_flush_caches)
 | |
| 		gdbstub_purge_cache();
 | |
| 
 | |
| 	gdbstub_load_fpu();
 | |
| 	mn10300_set_gdbleds(0);
 | |
| 	if (excep == EXCEP_NMI)
 | |
| 		NMICR = NMICR_NMIF;
 | |
| 
 | |
| 	touch_softlockup_watchdog();
 | |
| 
 | |
| 	local_irq_restore(epsw);
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * handle event interception
 | |
|  */
 | |
| asmlinkage int gdbstub_intercept(struct pt_regs *regs,
 | |
| 				 enum exception_code excep)
 | |
| {
 | |
| 	static u8 notfirst = 1;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (gdbstub_busy)
 | |
| 		gdbstub_printk("--> gdbstub reentered itself\n");
 | |
| 	gdbstub_busy = 1;
 | |
| 
 | |
| 	if (notfirst) {
 | |
| 		unsigned long mdr;
 | |
| 		asm("mov mdr,%0" : "=d"(mdr));
 | |
| 
 | |
| 		gdbstub_entry(
 | |
| 			"--> gdbstub_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
 | |
| 			regs, excep, mdr, regs->pc);
 | |
| 
 | |
| 		gdbstub_entry(
 | |
| 			"PC:  %08lx EPSW:  %08lx  SSP: %08lx mode: %s\n",
 | |
| 			regs->pc, regs->epsw, (unsigned long) &ret,
 | |
| 			user_mode(regs) ? "User" : "Super");
 | |
| 		gdbstub_entry(
 | |
| 			"d0:  %08lx   d1:  %08lx   d2: %08lx   d3: %08lx\n",
 | |
| 			regs->d0, regs->d1, regs->d2, regs->d3);
 | |
| 		gdbstub_entry(
 | |
| 			"a0:  %08lx   a1:  %08lx   a2: %08lx   a3: %08lx\n",
 | |
| 			regs->a0, regs->a1, regs->a2, regs->a3);
 | |
| 		gdbstub_entry(
 | |
| 			"e0:  %08lx   e1:  %08lx   e2: %08lx   e3: %08lx\n",
 | |
| 			regs->e0, regs->e1, regs->e2, regs->e3);
 | |
| 		gdbstub_entry(
 | |
| 			"e4:  %08lx   e5:  %08lx   e6: %08lx   e7: %08lx\n",
 | |
| 			regs->e4, regs->e5, regs->e6, regs->e7);
 | |
| 		gdbstub_entry(
 | |
| 			"lar: %08lx   lir: %08lx  mdr: %08lx  usp: %08lx\n",
 | |
| 			regs->lar, regs->lir, regs->mdr, regs->sp);
 | |
| 		gdbstub_entry(
 | |
| 			"cvf: %08lx   crl: %08lx  crh: %08lx  drq: %08lx\n",
 | |
| 			regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq);
 | |
| 		gdbstub_entry(
 | |
| 			"threadinfo=%p task=%p)\n",
 | |
| 			current_thread_info(), current);
 | |
| 	} else {
 | |
| 		notfirst = 1;
 | |
| 	}
 | |
| 
 | |
| 	ret = gdbstub(regs, excep);
 | |
| 
 | |
| 	gdbstub_entry("<-- gdbstub_intercept()\n");
 | |
| 	gdbstub_busy = 0;
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * handle the GDB stub itself causing an exception
 | |
|  */
 | |
| asmlinkage void gdbstub_exception(struct pt_regs *regs,
 | |
| 				  enum exception_code excep)
 | |
| {
 | |
| 	unsigned long mdr;
 | |
| 
 | |
| 	asm("mov mdr,%0" : "=d"(mdr));
 | |
| 	gdbstub_entry("--> gdbstub exception({%p},%04x) [MDR=%lx]\n",
 | |
| 		      regs, excep, mdr);
 | |
| 
 | |
| 	while ((unsigned long) regs == 0xffffffff) {}
 | |
| 
 | |
| 	/* handle guarded memory accesses where we know it might fault */
 | |
| 	if (regs->pc == (unsigned) gdbstub_read_byte_guard) {
 | |
| 		regs->pc = (unsigned) gdbstub_read_byte_cont;
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	if (regs->pc == (unsigned) gdbstub_read_word_guard) {
 | |
| 		regs->pc = (unsigned) gdbstub_read_word_cont;
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	if (regs->pc == (unsigned) gdbstub_read_dword_guard) {
 | |
| 		regs->pc = (unsigned) gdbstub_read_dword_cont;
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	if (regs->pc == (unsigned) gdbstub_write_byte_guard) {
 | |
| 		regs->pc = (unsigned) gdbstub_write_byte_cont;
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	if (regs->pc == (unsigned) gdbstub_write_word_guard) {
 | |
| 		regs->pc = (unsigned) gdbstub_write_word_cont;
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	if (regs->pc == (unsigned) gdbstub_write_dword_guard) {
 | |
| 		regs->pc = (unsigned) gdbstub_write_dword_cont;
 | |
| 		goto fault;
 | |
| 	}
 | |
| 
 | |
| 	gdbstub_printk("\n### GDB stub caused an exception ###\n");
 | |
| 
 | |
| 	/* something went horribly wrong */
 | |
| 	console_verbose();
 | |
| 	show_registers(regs);
 | |
| 
 | |
| 	panic("GDB Stub caused an unexpected exception - can't continue\n");
 | |
| 
 | |
| 	/* we caught an attempt by the stub to access silly memory */
 | |
| fault:
 | |
| 	gdbstub_entry("<-- gdbstub exception() = EFAULT\n");
 | |
| 	regs->d0 = -EFAULT;
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * send an exit message to GDB
 | |
|  */
 | |
| void gdbstub_exit(int status)
 | |
| {
 | |
| 	unsigned char checksum;
 | |
| 	unsigned char ch;
 | |
| 	int count;
 | |
| 
 | |
| 	gdbstub_busy = 1;
 | |
| 	output_buffer[0] = 'W';
 | |
| 	output_buffer[1] = hex_asc_hi(status);
 | |
| 	output_buffer[2] = hex_asc_lo(status);
 | |
| 	output_buffer[3] = 0;
 | |
| 
 | |
| 	gdbstub_io_tx_char('$');
 | |
| 	checksum = 0;
 | |
| 	count = 0;
 | |
| 
 | |
| 	while ((ch = output_buffer[count]) != 0) {
 | |
| 		gdbstub_io_tx_char(ch);
 | |
| 		checksum += ch;
 | |
| 		count += 1;
 | |
| 	}
 | |
| 
 | |
| 	gdbstub_io_tx_char('#');
 | |
| 	gdbstub_io_tx_char(hex_asc_hi(checksum));
 | |
| 	gdbstub_io_tx_char(hex_asc_lo(checksum));
 | |
| 
 | |
| 	/* make sure the output is flushed, or else RedBoot might clobber it */
 | |
| 	gdbstub_io_tx_flush();
 | |
| 
 | |
| 	gdbstub_busy = 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * initialise the GDB stub
 | |
|  */
 | |
| asmlinkage void __init gdbstub_init(void)
 | |
| {
 | |
| #ifdef CONFIG_GDBSTUB_IMMEDIATE
 | |
| 	unsigned char ch;
 | |
| 	int ret;
 | |
| #endif
 | |
| 
 | |
| 	gdbstub_busy = 1;
 | |
| 
 | |
| 	printk(KERN_INFO "%s", gdbstub_banner);
 | |
| 
 | |
| 	gdbstub_io_init();
 | |
| 
 | |
| 	gdbstub_entry("--> gdbstub_init\n");
 | |
| 
 | |
| 	/* try to talk to GDB (or anyone insane enough to want to type GDB
 | |
| 	 * protocol by hand) */
 | |
| 	gdbstub_io("### GDB Tx ACK\n");
 | |
| 	gdbstub_io_tx_char('+'); /* 'hello world' */
 | |
| 
 | |
| #ifdef CONFIG_GDBSTUB_IMMEDIATE
 | |
| 	gdbstub_printk("GDB Stub waiting for packet\n");
 | |
| 
 | |
| 	/* in case GDB is started before us, ACK any packets that are already
 | |
| 	 * sitting there (presumably "$?#xx")
 | |
| 	 */
 | |
| 	do { gdbstub_io_rx_char(&ch, 0); } while (ch != '$');
 | |
| 	do { gdbstub_io_rx_char(&ch, 0); } while (ch != '#');
 | |
| 	/* eat first csum byte */
 | |
| 	do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0);
 | |
| 	/* eat second csum byte */
 | |
| 	do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0);
 | |
| 
 | |
| 	gdbstub_io("### GDB Tx NAK\n");
 | |
| 	gdbstub_io_tx_char('-'); /* NAK it */
 | |
| 
 | |
| #else
 | |
| 	printk("GDB Stub ready\n");
 | |
| #endif
 | |
| 
 | |
| 	gdbstub_busy = 0;
 | |
| 	gdbstub_entry("<-- gdbstub_init\n");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * register the console at a more appropriate time
 | |
|  */
 | |
| #ifdef CONFIG_GDBSTUB_CONSOLE
 | |
| static int __init gdbstub_postinit(void)
 | |
| {
 | |
| 	printk(KERN_NOTICE "registering console\n");
 | |
| 	register_console(&gdbstub_console);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| __initcall(gdbstub_postinit);
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * handle character reception on GDB serial port
 | |
|  * - jump into the GDB stub if BREAK is detected on the serial line
 | |
|  */
 | |
| asmlinkage void gdbstub_rx_irq(struct pt_regs *regs, enum exception_code excep)
 | |
| {
 | |
| 	char ch;
 | |
| 	int ret;
 | |
| 
 | |
| 	gdbstub_entry("--> gdbstub_rx_irq\n");
 | |
| 
 | |
| 	do {
 | |
| 		ret = gdbstub_io_rx_char(&ch, 1);
 | |
| 		if (ret != -EIO && ret != -EAGAIN) {
 | |
| 			if (ret != -EINTR)
 | |
| 				gdbstub_rx_unget = ch;
 | |
| 			gdbstub(regs, excep);
 | |
| 		}
 | |
| 	} while (ret != -EAGAIN);
 | |
| 
 | |
| 	gdbstub_entry("<-- gdbstub_rx_irq\n");
 | |
| }
 |