diff options
Diffstat (limited to 'arch/mips/kernel/gdb-stub.c')
-rw-r--r-- | arch/mips/kernel/gdb-stub.c | 1091 |
1 files changed, 1091 insertions, 0 deletions
diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c new file mode 100644 index 00000000000..269889302a2 --- /dev/null +++ b/arch/mips/kernel/gdb-stub.c @@ -0,0 +1,1091 @@ +/* + * arch/mips/kernel/gdb-stub.c + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + */ + +/* + * 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=mips-linux-elf + * $ make + * $ install + * $ which mips-linux-elf-gdb + * /usr/local/bin/mips-linux-elf-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 + * + * 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_KGDB which in turn enables your calls + * to: + * set_debug_traps(); + * breakpoint(); + * + * Step 4: + * Start the gdb session on the host. + * + * Example: + * $ mips-linux-elf-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/config.h> +#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/smp.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/reboot.h> + +#include <asm/asm.h> +#include <asm/cacheflush.h> +#include <asm/mipsregs.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/inst.h> + +/* + * external low-level support routines + */ + +extern int putDebugChar(char c); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ +extern void trap_low(void); + +/* + * breakpoint and test functions + */ +extern void breakpoint(void); +extern void breakinst(void); +extern void async_breakpoint(void); +extern void async_breakinst(void); +extern void adel(void); + +/* + * local prototypes + */ + +static void getpacket(char *buffer); +static void putpacket(char *buffer); +static int computeSignal(int tt); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static int hexToLong(char **ptr, long *longValue); +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); +void handle_exception(struct gdb_regs *regs); + +int kgdb_enabled; + +/* + * spin locks for smp case + */ +static spinlock_t kgdb_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t kgdb_cpulock[NR_CPUS] = { [0 ... NR_CPUS-1] = SPIN_LOCK_UNLOCKED}; + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static int initialized; /* !0 means we've been initialized */ +static int kgdb_started; +static const char hexchars[]="0123456789abcdef"; + +/* Used to prevent crashes in memory access. Note that they'll crash anyway if + we haven't set up fault handlers yet... */ +int kgdb_read_byte(unsigned char *address, unsigned char *dest); +int kgdb_write_byte(unsigned char val, unsigned char *dest); + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar(); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $<packet info>#<checksum>. + */ + + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * may_fault is non-zero if we are reading from arbitrary memory, but is currently + * not used. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + unsigned char ch; + + while (count-- > 0) { + if (kgdb_read_byte(mem++, &ch) != 0) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *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 char *hex2mem(char *buf, char *mem, int count, int binary, int may_fault) +{ + int i; + unsigned char ch; + + for (i=0; i<count; i++) + { + if (binary) { + ch = *buf++; + if (ch == 0x7d) + ch = 0x20 ^ *buf++; + } + else { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + } + if (kgdb_write_byte(ch, mem++) != 0) + return 0; + } + + return mem; +} + +/* + * This table contains the mapping between SPARC hardware trap types, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static struct hard_trap_info { + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ + { 10, SIGILL }, /* reserved instruction */ +/* { 11, SIGILL }, */ /* CPU unusable */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + +/* Save the normal trap handlers for user-mode traps. */ +void *saved_vectors[32]; + +/* + * Set up exception handlers for tracing and breakpoints + */ +void set_debug_traps(void) +{ + struct hard_trap_info *ht; + unsigned long flags; + unsigned char c; + + local_irq_save(flags); + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low); + + putDebugChar('+'); /* 'hello world' */ + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ + + initialized = 1; + local_irq_restore(flags); +} + +void restore_debug_traps(void) +{ + struct hard_trap_info *ht; + unsigned long flags; + + local_irq_save(flags); + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + set_except_vector(ht->tt, saved_vectors[ht->tt]); + local_irq_restore(flags); +} + +/* + * Convert the MIPS hardware trap type code to a Unix signal number. + */ +static int computeSignal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + +static int hexToLong(char **ptr, long *longValue) +{ + int numChars = 0; + int hexValue; + + *longValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *longValue = (*longValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return numChars; +} + + +#if 0 +/* + * Print registers (on target console) + * Used only to debug the stub... + */ +void show_gdbregs(struct gdb_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg0, regs->reg1, regs->reg2, regs->reg3, + regs->reg4, regs->reg5, regs->reg6, regs->reg7); + printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg8, regs->reg9, regs->reg10, regs->reg11, + regs->reg12, regs->reg13, regs->reg14, regs->reg15); + printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg16, regs->reg17, regs->reg18, regs->reg19, + regs->reg20, regs->reg21, regs->reg22, regs->reg23); + printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg24, regs->reg25, regs->reg26, regs->reg27, + regs->reg28, regs->reg29, regs->reg30, regs->reg31); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n", + regs->cp0_epc, regs->cp0_status, regs->cp0_cause); +} +#endif /* dead code */ + +/* + * 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 { + unsigned long addr; + unsigned int val; +} step_bp[2]; + +#define BP 0x0000000d /* break opcode */ + +/* + * Set breakpoint instructions for single stepping. + */ +static void single_step(struct gdb_regs *regs) +{ + union mips_instruction insn; + unsigned long targ; + int is_branch, is_cond, i; + + targ = regs->cp0_epc; + insn.word = *(unsigned int *)targ; + is_branch = is_cond = 0; + + switch (insn.i_format.opcode) { + /* + * jr and jalr are in r_format format. + */ + case spec_op: + switch (insn.r_format.func) { + case jalr_op: + case jr_op: + targ = *(®s->reg0 + insn.r_format.rs); + is_branch = 1; + break; + } + break; + + /* + * This group contains: + * bltz_op, bgez_op, bltzl_op, bgezl_op, + * bltzal_op, bgezal_op, bltzall_op, bgezall_op. + */ + case bcond_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + + /* + * These are unconditional and in j_format. + */ + case jal_op: + case j_op: + is_branch = 1; + targ += 4; + targ >>= 28; + targ <<= 28; + targ |= (insn.j_format.target << 2); + break; + + /* + * These are conditional. + */ + case beq_op: + case beql_op: + case bne_op: + case bnel_op: + case blez_op: + case blezl_op: + case bgtz_op: + case bgtzl_op: + case cop0_op: + case cop1_op: + case cop2_op: + case cop1x_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + } + + if (is_branch) { + i = 0; + if (is_cond && targ != (regs->cp0_epc + 8)) { + step_bp[i].addr = regs->cp0_epc + 8; + step_bp[i++].val = *(unsigned *)(regs->cp0_epc + 8); + *(unsigned *)(regs->cp0_epc + 8) = BP; + } + step_bp[i].addr = targ; + step_bp[i].val = *(unsigned *)targ; + *(unsigned *)targ = BP; + } else { + step_bp[0].addr = regs->cp0_epc + 4; + step_bp[0].val = *(unsigned *)(regs->cp0_epc + 4); + *(unsigned *)(regs->cp0_epc + 4) = BP; + } +} + +/* + * If asynchronously interrupted by gdb, then we need to set a breakpoint + * at the interrupted instruction so that we wind up stopped with a + * reasonable stack frame. + */ +static struct gdb_bp_save async_bp; + +/* + * Swap the interrupted EPC with our asynchronous breakpoint routine. + * This is safer than stuffing the breakpoint in-place, since no cache + * flushes (or resulting smp_call_functions) are required. The + * assumption is that only one CPU will be handling asynchronous bp's, + * and only one can be active at a time. + */ +extern spinlock_t smp_call_lock; +void set_async_breakpoint(unsigned long *epc) +{ + /* skip breaking into userland */ + if ((*epc & 0x80000000) == 0) + return; + + /* avoid deadlock if someone is make IPC */ + if (spin_is_locked(&smp_call_lock)) + return; + + async_bp.addr = *epc; + *epc = (unsigned long)async_breakpoint; +} + +void kgdb_wait(void *arg) +{ + unsigned flags; + int cpu = smp_processor_id(); + + local_irq_save(flags); + + spin_lock(&kgdb_cpulock[cpu]); + spin_unlock(&kgdb_cpulock[cpu]); + + local_irq_restore(flags); +} + + +/* + * This function does all command processing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ +void handle_exception (struct gdb_regs *regs) +{ + int trap; /* Trap type */ + int sigval; + long addr; + int length; + char *ptr; + unsigned long *stack; + int i; + int bflag = 0; + + kgdb_started = 1; + + /* + * acquire the big kgdb spinlock + */ + if (!spin_trylock(&kgdb_lock)) { + /* + * some other CPU has the lock, we should go back to + * receive the gdb_wait IPC + */ + return; + } + + /* + * If we're in async_breakpoint(), restore the real EPC from + * the breakpoint. + */ + if (regs->cp0_epc == (unsigned long)async_breakinst) { + regs->cp0_epc = async_bp.addr; + async_bp.addr = 0; + } + + /* + * acquire the CPU spinlocks + */ + for (i = num_online_cpus()-1; i >= 0; i--) + if (spin_trylock(&kgdb_cpulock[i]) == 0) + panic("kgdb: couldn't get cpulock %d\n", i); + + /* + * force other cpus to enter kgdb + */ + smp_call_function(kgdb_wait, NULL, 0, 0); + + /* + * If we're in breakpoint() increment the PC + */ + trap = (regs->cp0_cause & 0x7c) >> 2; + if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) + regs->cp0_epc += 4; + + /* + * If we were single_stepping, restore the opcodes hoisted + * for the breakpoint[s]. + */ + if (step_bp[0].addr) { + *(unsigned *)step_bp[0].addr = step_bp[0].val; + step_bp[0].addr = 0; + + if (step_bp[1].addr) { + *(unsigned *)step_bp[1].addr = step_bp[1].val; + step_bp[1].addr = 0; + } + } + + stack = (long *)regs->reg29; /* stack ptr */ + sigval = computeSignal(trap); + + /* + * reply to host that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[REG_EPC >> 4]; + *ptr++ = hexchars[REG_EPC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->cp0_epc, ptr, sizeof(long), 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[REG_FP >> 4]; + *ptr++ = hexchars[REG_FP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg30, ptr, sizeof(long), 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + *ptr++ = hexchars[REG_SP >> 4]; + *ptr++ = hexchars[REG_SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg29, ptr, sizeof(long), 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ + + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) + { + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hexchars[sigval >> 4]; + output_buffer[2] = hexchars[sigval & 0xf]; + output_buffer[3] = 0; + break; + + /* + * Detach debugger; let CPU run + */ + case 'D': + putpacket(output_buffer); + goto finish_kgdb; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + ptr = output_buffer; + ptr = mem2hex((char *)®s->reg0, ptr, 32*sizeof(long), 0); /* r0...r31 */ + ptr = mem2hex((char *)®s->cp0_status, ptr, 6*sizeof(long), 0); /* cp0 */ + ptr = mem2hex((char *)®s->fpr0, ptr, 32*sizeof(long), 0); /* f0...31 */ + ptr = mem2hex((char *)®s->cp1_fsr, ptr, 2*sizeof(long), 0); /* cp1 */ + ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*sizeof(long), 0); /* frp */ + ptr = mem2hex((char *)®s->cp0_index, ptr, 16*sizeof(long), 0); /* cp0 */ + break; + + /* + * set the value of the CPU registers - return OK + */ + case 'G': + { + ptr = &input_buffer[1]; + hex2mem(ptr, (char *)®s->reg0, 32*sizeof(long), 0, 0); + ptr += 32*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->cp0_status, 6*sizeof(long), 0, 0); + ptr += 6*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->fpr0, 32*sizeof(long), 0, 0); + ptr += 32*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->cp1_fsr, 2*sizeof(long), 0, 0); + ptr += 2*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->frame_ptr, 2*sizeof(long), 0, 0); + ptr += 2*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->cp0_index, 16*sizeof(long), 0, 0); + strcpy(output_buffer,"OK"); + } + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToLong(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * XAA..AA,LLLL: Write LLLL escaped binary bytes at address AA.AA + */ + case 'X': + bflag = 1; + /* fall through */ + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToLong(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, bflag, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToLong(&ptr, &addr)) + regs->cp0_epc = addr; + + goto exit_kgdb_exception; + break; + + /* + * kill the program; let us try to restart the machine + * Reset the whole machine. + */ + case 'k': + case 'r': + machine_restart("kgdb restarts machine"); + break; + + /* + * Step to next instruction + */ + case 's': + /* + * There is no single step insn in the MIPS ISA, so we + * use breakpoints and continue, instead. + */ + single_step(regs); + goto exit_kgdb_exception; + /* NOTREACHED */ + break; + + /* + * Set baud rate (bBB) + * FIXME: Needs to be written + */ + case 'b': + { +#if 0 + int baudrate; + extern void set_timer_3(); + + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &baudrate)) + { + strcpy(output_buffer,"B01"); + break; + } + + /* Convert baud rate to uart clock divider */ + + switch (baudrate) + { + case 38400: + baudrate = 16; + break; + case 19200: + baudrate = 33; + break; + case 9600: + baudrate = 65; + break; + default: + baudrate = 0; + strcpy(output_buffer,"B02"); + goto x1; + } + + if (baudrate) { + putpacket("OK"); /* Ack before changing speed */ + set_timer_3(baudrate); /* Set it */ + } +#endif + } + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer); + + } /* while */ + + return; + +finish_kgdb: + restore_debug_traps(); + +exit_kgdb_exception: + /* release locks so other CPUs can go */ + for (i = num_online_cpus()-1; i >= 0; i--) + spin_unlock(&kgdb_cpulock[i]); + spin_unlock(&kgdb_lock); + + __flush_cache_all(); + return; +} + +/* + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void breakpoint(void) +{ + if (!initialized) + return; + + __asm__ __volatile__( + ".globl breakinst\n\t" + ".set\tnoreorder\n\t" + "nop\n" + "breakinst:\tbreak\n\t" + "nop\n\t" + ".set\treorder" + ); +} + +/* Nothing but the break; don't pollute any registers */ +void async_breakpoint(void) +{ + __asm__ __volatile__( + ".globl async_breakinst\n\t" + ".set\tnoreorder\n\t" + "nop\n" + "async_breakinst:\tbreak\n\t" + "nop\n\t" + ".set\treorder" + ); +} + +void adel(void) +{ + __asm__ __volatile__( + ".globl\tadel\n\t" + "lui\t$8,0x8000\n\t" + "lw\t$9,1($8)\n\t" + ); +} + +/* + * malloc is needed by gdb client in "call func()", even a private one + * will make gdb happy + */ +static void *malloc(size_t size) +{ + return kmalloc(size, GFP_ATOMIC); +} + +static void free(void *where) +{ + kfree(where); +} + +#ifdef CONFIG_GDB_CONSOLE + +void gdb_putsn(const char *str, int l) +{ + char outbuf[18]; + + if (!kgdb_started) + return; + + outbuf[0]='O'; + + while(l) { + int i = (l>8)?8:l; + mem2hex((char *)str, &outbuf[1], i, 0); + outbuf[(i*2)+1]=0; + putpacket(outbuf); + str += i; + l -= i; + } +} + +static void gdb_console_write(struct console *con, const char *s, unsigned n) +{ + gdb_putsn(s, n); +} + +static struct console gdb_console = { + .name = "gdb", + .write = gdb_console_write, + .flags = CON_PRINTBUFFER, + .index = -1 +}; + +static int __init register_gdb_console(void) +{ + register_console(&gdb_console); + + return 0; +} + +console_initcall(register_gdb_console); + +#endif |