/* 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/gdb-stub.h> #include <asm/exceptions.h> #include <asm/debugger.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); } #ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP /* * 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) { /* vmalloc area */ if (((u8 *) VMALLOC_START <= addr) && (addr < (u8 *) VMALLOC_END)) goto okay; /* SRAM, SDRAM */ if (((u8 *) 0x80000000UL <= addr) && (addr < (u8 *) 0xa0000000UL)) 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; } #endif /* CONFIG_GDBSTUB_ALLOW_SINGLE_STEP */ #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 = hex_byte_pack(buf, ch[0]); mem++; count--; } if ((u32) mem & 3 && count >= 2) { if (gdbstub_read_word(mem, ch) != 0) return 0; buf = hex_byte_pack(buf, ch[0]); buf = hex_byte_pack(buf, ch[1]); mem += 2; count -= 2; } while (count >= 4) { if (gdbstub_read_dword(mem, ch) != 0) return 0; buf = hex_byte_pack(buf, ch[0]); buf = hex_byte_pack(buf, ch[1]); buf = hex_byte_pack(buf, ch[2]); buf = hex_byte_pack(buf, ch[3]); mem += 4; count -= 4; } if (count >= 2) { if (gdbstub_read_word(mem, ch) != 0) return 0; buf = hex_byte_pack(buf, ch[0]); buf = hex_byte_pack(buf, ch[1]); mem += 2; count -= 2; } if (count >= 1) { if (gdbstub_read_byte(mem, ch) != 0) return 0; buf = hex_byte_pack(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 0 if the exception should be skipped, -ERROR 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 -ENOTSUPP; gdbstub_flush_caches = 0; mn10300_set_gdbleds(1); asm volatile("mov mdr,%0" : "=d"(mdr)); local_save_flags(epsw); arch_local_change_intr_mask_level( NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 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; #ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP 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(); #endif 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 = hex_byte_pack(ptr, hx); hx = hex_asc_lo(excep >> 8); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(excep); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(excep); ptr = hex_byte_pack(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 = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr >> 24); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(bcberr >> 16); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr >> 16); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(bcberr >> 8); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr >> 8); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(bcberr); ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr); ptr = hex_byte_pack(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 = hex_byte_pack(ptr, sigval); /* * Send Error PC */ ptr = hex_byte_pack(ptr, GDB_REGID_PC); *ptr++ = ':'; ptr = mem2hex(®s->pc, ptr, 4, 0); *ptr++ = ';'; /* * Send frame pointer */ ptr = hex_byte_pack(ptr, GDB_REGID_FP); *ptr++ = ':'; ptr = mem2hex(®s->a3, ptr, 4, 0); *ptr++ = ';'; /* * Send stack pointer */ ssp = (unsigned long) (regs + 1); ptr = hex_byte_pack(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. */ #ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP if (gdbstub_single_step(regs) < 0) /* ignore any fault error for now */ gdbstub_printk("unable to set single-step" " bp\n"); goto done; #else gdbstub_strcpy(output_buffer, "E01"); break; #endif /* * 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) debugger_local_cache_flushinv(); gdbstub_load_fpu(); mn10300_set_gdbleds(0); if (excep == EXCEP_NMI) NMICR = NMICR_NMIF; touch_softlockup_watchdog(); local_irq_restore(epsw); return 0; } /* * Determine if we hit a debugger special breakpoint that needs skipping over * automatically. */ int at_debugger_breakpoint(struct pt_regs *regs) { return 0; } /* * handle event interception */ asmlinkage int debugger_intercept(enum exception_code excep, int signo, int si_code, struct pt_regs *regs) { 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( "--> debugger_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("<-- debugger_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"); }