|  | /* 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 <asm/unit/leds.h> | 
|  | #include <asm/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: | 
|  | case 0xc1: | 
|  | case 0xc2: | 
|  | case 0xc3: | 
|  | case 0xc4: | 
|  | case 0xc5: | 
|  | case 0xc6: | 
|  | case 0xc7: | 
|  | case 0xc8: | 
|  | case 0xc9: | 
|  | case 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: | 
|  | case 0xd1: | 
|  | case 0xd2: | 
|  | case 0xd3: | 
|  | case 0xd4: | 
|  | case 0xd5: | 
|  | case 0xd6: | 
|  | case 0xd7: | 
|  | case 0xd8: | 
|  | case 0xd9: | 
|  | case 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"); | 
|  | } |