diff -ruN linux/arch/arm/config.in kgdb_linux/arch/arm/config.in --- linux/arch/arm/config.in 2004-09-17 20:09:25.000000000 +0530 +++ kgdb_linux/arch/arm/config.in 2004-09-17 20:40:36.000000000 +0530 @@ -662,4 +662,28 @@ dep_bool ' Kernel low-level debugging functions' CONFIG_DEBUG_LL $CONFIG_DEBUG_KERNEL dep_bool ' Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT $CONFIG_DEBUG_LL $CONFIG_FOOTBRIDGE dep_bool ' Kernel low-level debugging messages via UART2' CONFIG_DEBUG_CLPS711X_UART2 $CONFIG_DEBUG_LL $CONFIG_ARCH_CLPS711X + +dep_bool ' KGDB support' CONFIG_KGDB $CONFIG_DEBUG_KERNEL +dep_bool ' KGB over serial port' CONFIG_KGDB_SERIAL $CONFIG_KGDB +dep_bool ' Console messages over KGDB' CONFIG_KGDB_CONSOLE $CONFIG_KGDB +dep_bool ' Enable SysRq-G command to invoke kgdb via breakpoint?' CONFIG_KGDB_SYSRQ $CONFIG_MAGIC_SYSRQ $CONFIG_KGDB +dep_bool ' Add additional compile options' CONFIG_KGDB_MORE $CONFIG_KGDB +if [ "$CONFIG_KGDB_MORE" = "y" ]; then + string ' Additional compile options' CONFIG_KGDB_OPTIONS "-O1" +fi +if [ "$CONFIG_KGDB_SERIAL" = "y" ]; then + if [ "$CONFIG_ARCH_MX1ADS" = "y" ]; then + choice 'KGDB serial port UART selection' \ + "1 CONFIG_KGDB_UART1 \ + 2 CONFIG_KGDB_UART2" 1 + fi + if [ "$CONFIG_ARCH_MX1ADS" = "y" ]; then + choice 'KGDB serial port BAUD rate selection' \ + "9600 CONFIG_KGDB_9600BAUD \ + 19200 CONFIG_KGDB_19200BAUD \ + 38400 CONFIG_KGDB_38400BAUD \ + 57600 CONFIG_KGDB_57600BAUD \ + 115200 CONFIG_KGDB_115200BAUD" 9600 + fi +fi endmenu diff -ruN linux/arch/arm/kernel/debug-armv.S kgdb_linux/arch/arm/kernel/debug-armv.S --- linux/arch/arm/kernel/debug-armv.S 2004-09-17 20:08:55.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/debug-armv.S 2004-10-06 11:59:20.000000000 +0530 @@ -9,6 +9,7 @@ * * 32-bit debugging code */ + #include #include #include @@ -385,6 +386,25 @@ bne 1001b .endm +#elif defined(CONFIG_ARCH_MX1ADS) + .macro addruart,rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, #0x00000000 @ physical + movne \rx, #0xf0000000 @ virtual + orr \rx, \rx, #0x00200000 + orr \rx, \rx, #0x00006000 @ UART1 offset + .endm + + .macro senduart,rd,rx + str \rd, [\rx, #0x40] @ TXDATA + .endm + + .macro busyuart,rd,rx +1002: ldr \rd, [\rx, #0x98] @ SR2 + tst \rd, #1 << 3 @ TXDC + beq 1002b @ wait until transmit done + .endm #else #error Unknown architecture #endif diff -ruN linux/arch/arm/kernel/entry-armv.S kgdb_linux/arch/arm/kernel/entry-armv.S --- linux/arch/arm/kernel/entry-armv.S 2004-09-17 20:09:25.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/entry-armv.S 2004-10-06 12:00:41.000000000 +0530 @@ -14,6 +14,7 @@ * it to save wrong values... Be aware! */ #include +#include #include "entry-header.S" @@ -739,9 +740,26 @@ add r4, sp, #S_SP stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro +#ifdef CONFIG_KGDB + adr r10, kgdb_brk_instr + ldr r10, [r10] + ldr r9, [sp, #S_PC] @ Load PC + sub r9, r9, #4 + mask_pc r9, r9 + ldr r9, [r9] @ get instruction + teq r10, r9 @ Is it GDB breakpoint? + beq 2f + adr r10, kgdb_trap_instr @ Compiled in breakpoint? + ldr r10, [r10] + teq r10, r9 + beq 2f +#endif adrsvc al, r9, 1f @ r9 = normal FP return bl call_fpe @ lr = undefined instr return +#ifdef CONFIG_KGDB +2: +#endif mov r0, r5 @ unsigned long pc mov r1, sp @ struct pt_regs *regs bl SYMBOL_NAME(do_undefinstr) @@ -752,6 +770,10 @@ msr spsr, lr ldmia sp, {r0 - pc}^ @ Restore SVC registers +#ifdef CONFIG_KGDB +kgdb_brk_instr: .word KGDB_BREAKINST +kgdb_trap_instr: .word KGDB_COMPILED_BREAK +#endif .align 5 __pabt_svc: sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ save r0 - r12 diff -ruN linux/arch/arm/kernel/head-armv.S kgdb_linux/arch/arm/kernel/head-armv.S --- linux/arch/arm/kernel/head-armv.S 2004-09-17 20:09:25.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/head-armv.S 2004-09-17 20:40:36.000000000 +0530 @@ -283,7 +283,7 @@ bic r8, r8, #0x0c @ turn off cacheable @ and bufferable bits -#ifdef CONFIG_DEBUG_LL +#if defined(CONFIG_DEBUG_LL) || defined(CONFIG_KGDB) /* * Map in IO space for serial debugging. * This allows debug messages to be output diff -ruN linux/arch/arm/kernel/kgdb-jmp.S kgdb_linux/arch/arm/kernel/kgdb-jmp.S --- linux/arch/arm/kernel/kgdb-jmp.S 1970-01-01 05:30:00.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/kgdb-jmp.S 2004-10-06 12:01:25.000000000 +0530 @@ -0,0 +1,46 @@ +/* + * FILE NAME: arch/arm/kernel/kgdb-jmp.S + * + * MODULE DESCRIPTION: + * Trivial setjmp and longjmp procedures to support bus error recovery + * which may occur during kgdb memory read/write operations. + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +ENTRY (kgdb_setjmp) + /* Save registers */ + stmia r0, {r0-r14} + str lr,[r0, #60] + mrs r1,cpsr + str r1,[r0,#64] + ldr r1,[r0,#4] + mov r0, #0 + mov pc,lr +ENTRY (kgdb_longjmp) + /* Restore registers */ + str r1,[r0] + ldmia r0,{r0-pc}^ diff -ruN linux/arch/arm/kernel/kgdb-serial.c kgdb_linux/arch/arm/kernel/kgdb-serial.c --- linux/arch/arm/kernel/kgdb-serial.c 1970-01-01 05:30:00.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/kgdb-serial.c 2004-10-01 10:48:53.000000000 +0530 @@ -0,0 +1,277 @@ +/* + * arch/arm/kernel/kgdb-serial.c + * + * Generic serial access routines for use by kgdb. + * + * Author: Deepak Saxena + * + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Based on original KGDB code developed by various people: + * + * This is a simple middle layer that allows KGDB to run on + * top of any serial device. This layer expects the board + * port to provide the following functions: + * + * void kgdb_serial_init(void) + * initialize serial interface if needed + * void kgdb_serial_putchar(unsigned char) + * send a character over the serial connection + * kgdb_serial_getchar() + * get a character from the serial connection + * + * Note that this is not meant for debugging over sercons, but for + * when you have a _dedicated_ serial port for kgdb. To send debug + * and console messages over the same port (ICK), you need to + * turn on CONFIG_KGDB_CONSOLE, which uses kgb-console.c instead and + * hooks into the console system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_KGDB_CONSOLE +#include +#endif + +static int kgdb_io_initialized = 0; + +static const char hexchars[]="0123456789abcdef"; + +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 the serial stream for the sequence $# + * + */ + +void kgdb_get_packet(unsigned char *buffer, int buflen) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + /* Ignore everything until the start character */ + while ((ch = kgdb_serial_getchar()) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* Now, read until a # or end of buffer is found */ + while (count < (buflen - 1)) { + ch = kgdb_serial_getchar(); + + if (ch == '#') + break; + + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + buffer[count] = 0; + + /* Continue to read checksum following # */ + if (ch == '#') { + xmitcsum = hex(kgdb_serial_getchar()) << 4; + xmitcsum += hex(kgdb_serial_getchar()); + + /* Checksum */ + if (checksum != xmitcsum) + kgdb_serial_putchar('-'); /* Failed checksum */ + else { + /* Ack successful transfer */ + kgdb_serial_putchar('+'); + + /* If a sequence char is present, reply + the sequence ID */ + if (buffer[2] == ':') { + kgdb_serial_putchar(buffer[0]); + kgdb_serial_putchar(buffer[1]); + + /* Remove sequence chars from buffer */ + count = strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); /* Keep trying while we fail */ +} + + +/* + * + * Send packet in the buffer with run-length encoding as follows: + * + * $#. + * + */ +void kgdb_put_packet(const unsigned char *buffer) +{ + unsigned char checksum; + const unsigned char *src; + int runlen; + int encode; + + do { + src = buffer; + kgdb_serial_putchar('$'); + checksum = 0; + + /* Continue while we still have chars left */ + while (*src) { + /* Check for runs up to 99 chars long */ + for (runlen = 1; runlen < 99; runlen++) { + if (src[0] != src[runlen]) + break; + } + + if (runlen > 3) { + /* Got a useful amount, send encoding */ + encode = runlen + ' ' - 4; + kgdb_serial_putchar(*src); + checksum += *src; + kgdb_serial_putchar('*'); + checksum += '*'; + kgdb_serial_putchar(encode); + checksum += encode; + src += runlen; + } else { + /* Otherwise just send the current char */ + kgdb_serial_putchar(*src); + checksum += *src; + src += 1; + } + } + + /* '#' Separator, put high and low components of checksum */ + kgdb_serial_putchar('#'); + kgdb_serial_putchar(hexchars[(checksum >> 4) & 0xf]); + kgdb_serial_putchar(hexchars[checksum & 0xf]); + } while ((kgdb_serial_getchar()) != '+'); /* While no ack */ +} + +int kgdb_io_init(void) +{ + if (kgdb_io_initialized) + return 0; + + kgdb_serial_init(); + + kgdb_io_initialized = 1; + + return 0; +} + + +/* + * If only one serial port is available on the board or if you want + * to send console messages over KGDB, this is a nice and easy way + * to accomplish this. Just add "console=ttyKGDB" to the command line + * and all console output will be piped to the GDB client. + */ +#ifdef CONFIG_KGDB_CONSOLE + +static char kgdb_console_buf[4096]; + +static void kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + int i = 0; + static int j = 0; + + if(!kgdb_active()) + return; + + kgdb_console_buf[0] = 'O'; + j = 1; + + for(i = 0; i < count; i++, s++) + { + kgdb_console_buf[j++] = hexchars[*s >> 4]; + kgdb_console_buf[j++] = hexchars[*s & 0xf]; + + if(*s == '\n') + { + kgdb_console_buf[j] = 0; + kgdb_put_packet(kgdb_console_buf); + + kgdb_console_buf[0]='O'; + j = 1; + } + } + + if(j != 1) + { + kgdb_console_buf[j] = 0; + kgdb_put_packet(kgdb_console_buf); + } +} + +static int kgdb_console_wait_key(struct console *co) +{ + return -1; +} + +static kdev_t kgdb_console_device(struct console *c) +{ + return 0; +} + +static int kgdb_console_setup(struct console *co, char *options) +{ + kgdb_io_init(); + + return 0; +} + +static struct console kgdb_console = { + name: "ttyKGDB", + write: kgdb_console_write, + device: kgdb_console_device, + wait_key: kgdb_console_wait_key, + setup: kgdb_console_setup, + flags: CON_PRINTBUFFER, + index: -1 +}; + +void __init kgdb_console_init(void) +{ + register_console(&kgdb_console); +} + +#endif // CONFIG_KGDB_CONSOLE diff -ruN linux/arch/arm/kernel/kgdb-stub.c kgdb_linux/arch/arm/kernel/kgdb-stub.c --- linux/arch/arm/kernel/kgdb-stub.c 1970-01-01 05:30:00.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/kgdb-stub.c 2004-10-06 12:03:17.000000000 +0530 @@ -0,0 +1,1522 @@ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * + * 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 trap #1. + * + * The external function exceptionHandler() is + * used to attach a specific handler to a specific 386 vector number. + * It should use the same privilege level it runs at. It should + * install it as an interrupt gate so that interrupts are masked + * while the handler runs. + * + * Because gdb will sometimes write to the stack area to execute function + * calls, this program cannot rely on using the supervisor stack so it + * uses it's own stack area reserved in the int array remcomStack. + * + ************* + * + * 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 + * XAA..AA,LLLL: Same, but data is binary (not hex) OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * CNN; Resume at current address with signal SNN + * CNN;AA..AA Resume at address AA..AA with signal SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * SNN; Step one instruction with signal SNN + * SNNAA..AA Step one instruction from AA..AA w/NN SNN + * + * k kill (Detach GDB) + * + * d Toggle debug flag + * D Detach GDB + * + * Hct Set thread t for operations, OK or ENN + * c = 'c' (step, cont), c = 'g' (other + * operations) + * + * qC Query current thread ID QCpid + * qfThreadInfo Get list of current threads (first) m + * qsThreadInfo " " " " " (subsequent) + * qOffsets Get section offsets Text=x;Data=y;Bss=z + * + * TXX Find if thread XX is alive OK or ENN + * ? What was the last sigval ? SNN (signal NN) + * O Output to GDB console + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * 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 + * + ****************************************************************************/ +/* + * + * Remote communication protocol. + * + * A debug packet whose contents are is encapsulated for + * transmission in the form: + * + * $ # CSUM1 CSUM2 + * + * must be ASCII alphanumeric and cannot include characters + * '$' or '#'. If starts with two characters followed by + * ':', then the existing stubs interpret this as a sequence number. + * + * CSUM1 and CSUM2 are ascii hex representation of an 8-bit + * checksum of , the most significant nibble is sent first. + * the hex digits 0-9,a-f are used. + * + * Receiver responds with: + * + * + - if CSUM is correct and ready for next packet + * - - if CSUM is incorrect + * + * Responses can be run-length encoded to save space. A '*' means that + * the next character is an ASCII encoding giving a repeat count which + * stands for that many repititions of the character preceding the '*'. + * The encoding is n+29, yielding a printable character where n >=3 + * (which is where RLE starts to win). Don't use an n > 126. + * + * So "0* " means the same as "0000". + */ + +/* + * ARM port Copyright (c) 2002 MontaVista Software, Inc + * + * Authors: George Davis + * Deepak Saxena + * Few Additions are done by Bhupesh Kumar Pandey + * + * See Documentation/ARM/kgdb for information on porting to a new board + * + * tabstop=3 to make this readable + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif + +#ifdef CONFIG_DEBUG_LL +// #define printascii(x, args...) printk(x, ## args) +// #undef printascii +// #define printascii(x, args...) +extern void printascii(const char *); +extern void printhex8(unsigned int); +#else +#define printascii(s) +#define printhex8(i) +#endif + + +#define GDB_MAXREGS (16 + 8 * 3 + 1 + 1) +#define GDB_REGBYTES (GDB_MAXREGS << 2) + +#define BUFMAX 2048 + +#define PC_REGNUM 0x0f +#define LR_REGNUM 0x0e +#define SP_REGNUM 0x0d + + +/* External functions */ + +extern struct pt_regs *get_task_registers(const struct task_struct *); + + +/* Forward declarations */ + +static unsigned long get_next_pc(struct pt_regs *); + + +/* Globals */ + +int kgdb_fault_expected = 0; /* Boolean to ignore bus errs (i.e. in GDB) */ +int kgdb_enabled = 0; + +int kgdb_enter = 0; /* when set, wait for connection from gdb */ +int kgdb_ttyS = 1; /* which serial port to use */ +unsigned long kgdb_baud = 38400; /* baud rate to use */ + +/* Locals */ + +static char remote_debug = 0; +static const char hexchars[]="0123456789abcdef"; +static int fault_jmp_buf[32]; /* Jump buffer for kgdb_setjmp/longjmp */ +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static int kgdb_initialized = 0; +static volatile unsigned int *step_addr = NULL; +static unsigned int step_instr = 0; + +static struct pt_regs kgdb_regs; +static unsigned int gdb_regs[GDB_MAXREGS]; + +#ifdef CONFIG_KGDB_THREAD +static struct task_struct *trapped_thread; +static struct task_struct *current_thread; +typedef unsigned char threadref[8]; +#define BUF_THREAD_ID_SIZE 16 +#endif + + +/* + * Various conversion functions + */ +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); +} + +static unsigned char * mem2hex(const char *mem, char *buf, int count) +{ + /* Accessing 16-bit and 32-bit objects in a single + * load instruction is required to avoid bad side + * effects for some IO registers. + */ + + if ((count == 2) && (((long)mem & 1) == 0)) { + unsigned short tmp_s = + cpu_to_be16(*(unsigned short *)mem); + mem += 2; + *buf++ = hexchars[(tmp_s >> 12) & 0xf]; + *buf++ = hexchars[(tmp_s >> 8) & 0xf]; + *buf++ = hexchars[(tmp_s >> 4) & 0xf]; + *buf++ = hexchars[tmp_s & 0xf]; + } else if ((count == 4) && (((long)mem & 3) == 0)) { + unsigned int tmp_l = + cpu_to_be32(*(unsigned int *)mem); + mem += 4; + *buf++ = hexchars[(tmp_l >> 28) & 0xf]; + *buf++ = hexchars[(tmp_l >> 24) & 0xf]; + *buf++ = hexchars[(tmp_l >> 20) & 0xf]; + *buf++ = hexchars[(tmp_l >> 16) & 0xf]; + *buf++ = hexchars[(tmp_l >> 12) & 0xf]; + *buf++ = hexchars[(tmp_l >> 8) & 0xf]; + *buf++ = hexchars[(tmp_l >> 4) & 0xf]; + *buf++ = hexchars[tmp_l & 0xf]; + } else { + unsigned char ch; + while (count-- > 0) { + ch = *mem++; + *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. + */ +static char * hex2mem(char *buf, char *mem, int count) +{ + /* Accessing 16-bit and 32-bit objects in a single + * store instruction is required to avoid bad side + * effects for some IO registers. + */ + + if ((count == 2) && (((long)mem & 1) == 0)) { + unsigned short tmp_s = + hex(*buf++) << 12; + tmp_s |= hex(*buf++) << 8; + tmp_s |= hex(*buf++) << 4; + tmp_s |= hex(*buf++); + *(unsigned short *)mem = be16_to_cpu(tmp_s); + mem += 2; + } else if ((count == 4) && (((long)mem & 3) == 0)) { + unsigned int tmp_l = + hex(*buf++) << 28; + tmp_l |= hex(*buf++) << 24; + tmp_l |= hex(*buf++) << 20; + tmp_l |= hex(*buf++) << 16; + tmp_l |= hex(*buf++) << 12; + tmp_l |= hex(*buf++) << 8; + tmp_l |= hex(*buf++) << 4; + tmp_l |= hex(*buf++); + *(unsigned int *)mem = be32_to_cpu(tmp_l); + mem += 4; + } else { + int i; + unsigned char ch; + for (i=0; i 0; count--, buf++) { + if (*buf == 0x7d) + *mem++ = *(++buf) ^ 0x20; + else + *mem++ = *buf; + } + return mem; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hex2int(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); +} + +/* Make a local copy of the registers passed into the handler (bletch) */ +static void kregs2gregs(const struct pt_regs *kregs, int *gregs) +{ + int regno; + + /* Initialize to zero */ + for (regno = 0; regno < GDB_MAXREGS; regno++) + gregs[regno] = 0; + + gregs[0] = kregs->ARM_r0; + gregs[1] = kregs->ARM_r1; + gregs[2] = kregs->ARM_r2; + gregs[3] = kregs->ARM_r3; + gregs[4] = kregs->ARM_r4; + gregs[5] = kregs->ARM_r5; + gregs[6] = kregs->ARM_r6; + gregs[7] = kregs->ARM_r7; + gregs[8] = kregs->ARM_r8; + gregs[9] = kregs->ARM_r9; + gregs[10] = kregs->ARM_r10; + gregs[11] = kregs->ARM_fp; + gregs[12] = kregs->ARM_ip; + gregs[13] = kregs->ARM_sp; + gregs[14] = kregs->ARM_lr; + gregs[15] = kregs->ARM_pc; + gregs[GDB_MAXREGS - 1] = kregs->ARM_cpsr; +} + +/* Copy local gdb registers back to kgdb regs, for later copy to kernel */ +static void gregs2kregs(const int *gregs, struct pt_regs *kregs) +{ + kregs->ARM_r0 = gregs[0]; + kregs->ARM_r1 = gregs[1]; + kregs->ARM_r2 = gregs[2]; + kregs->ARM_r3 = gregs[3]; + kregs->ARM_r4 = gregs[4]; + kregs->ARM_r5 = gregs[5]; + kregs->ARM_r6 = gregs[6]; + kregs->ARM_r7 = gregs[7]; + kregs->ARM_r8 = gregs[8]; + kregs->ARM_r9 = gregs[9]; + kregs->ARM_r10 = gregs[10]; + kregs->ARM_fp = gregs[11]; + kregs->ARM_ip = gregs[12]; + kregs->ARM_sp = gregs[13]; + kregs->ARM_lr = gregs[14]; + kregs->ARM_pc = gregs[15]; + kregs->ARM_cpsr = gregs[GDB_MAXREGS - 1]; +} + +#ifdef CONFIG_KGDB_THREAD +/* Make a local copy of registers from the specified thread */ +static void tregs2gregs(const struct task_struct *task, int *gregs) +{ + int regno; + struct pt_regs *tregs; + + /* Initialize to zero */ + for (regno = 0; regno < GDB_MAXREGS; regno++) + gregs[regno] = 0; + + /* Just making sure... */ + if (task == NULL) + return; + +#if 0 /* REVISIT */ + /* A new fork has pt_regs on the stack from a fork() call */ + if (task->thread->save.pc == (unsigned long)ret_from_fork) { + struct pt_regs *tregs; + kregs = (struct pt_regs*)task->thread->save; + + gregs[0] = tregs->ARM_r0; + gregs[1] = tregs->ARM_r1; + gregs[2] = tregs->ARM_r2; + gregs[3] = tregs->ARM_r3; + gregs[4] = tregs->ARM_r4; + gregs[5] = tregs->ARM_r5; + gregs[6] = tregs->ARM_r6; + gregs[7] = tregs->ARM_r7; + gregs[8] = tregs->ARM_r8; + gregs[9] = tregs->ARM_r9; + gregs[10] = tregs->ARM_r10; + gregs[11] = tregs->ARM_fp; + gregs[12] = tregs->ARM_ip; + gregs[13] = tregs->ARM_sp; + gregs[14] = tregs->ARM_lr; + gregs[15] = tregs->ARM_pc; + gregs[GDB_MAXREGS - 1] = tregs->ARM_cpsr; + } +#endif + + /* Otherwise, we have only some registers from switch_to() */ + tregs = get_task_registers(task); + gregs[0] = tregs->ARM_r0; /* Not really valid? */ + gregs[1] = tregs->ARM_r1; /* " " */ + gregs[2] = tregs->ARM_r2; /* " " */ + gregs[3] = tregs->ARM_r3; /* " " */ + gregs[4] = tregs->ARM_r4; + gregs[5] = tregs->ARM_r5; + gregs[6] = tregs->ARM_r6; + gregs[7] = tregs->ARM_r7; + gregs[8] = tregs->ARM_r8; + gregs[9] = tregs->ARM_r9; + gregs[10] = tregs->ARM_r10; + gregs[11] = tregs->ARM_fp; + gregs[12] = tregs->ARM_ip; + gregs[13] = tregs->ARM_sp; + gregs[14] = tregs->ARM_lr; + gregs[15] = tregs->ARM_pc; + gregs[GDB_MAXREGS - 1] = tregs->ARM_cpsr; +} + +static char * pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +static char * pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +static void int_to_threadref(threadref * id, const int value) +{ + unsigned char *scan = (unsigned char *) id; + int i = 4; + + while (i--) + *scan++ = 0; + + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} + +/* Return a task structure ptr for a particular pid */ +static struct task_struct * get_thread(int pid) +{ + struct task_struct *thread; + + /* Use PID_MAX w/gdb for pid 0 */ + if (pid == PID_MAX) pid = 0; + + /* First check via PID */ + thread = find_task_by_pid(pid); + + if (thread) + return thread; + + /* Start at the start */ + thread = init_tasks[0]; + + /* Walk along the linked list of tasks */ + do { + if (thread->pid == pid) + return thread; + thread = thread->next_task; + } while (thread != init_tasks[0]); + + return NULL; +} +#endif /* CONFIG_KGDB_THREAD */ + +void kgdb_msg(const char * msg) +{ + const char *emsg; + + for (emsg = msg; *emsg; emsg++) ; + + remcomOutBuffer[0] = 'O'; + + mem2hex(msg, remcomOutBuffer+1, emsg-msg); + + if (kgdb_active()) kgdb_put_packet(remcomOutBuffer); +} + +static void send_ok_msg(void) +{ + kgdb_put_packet("OK"); +} + +static void send_err_msg(void) +{ + kgdb_put_packet("E01"); +} + +static void send_empty_msg(void) +{ + kgdb_put_packet(""); +} + +static void send_signal_msg(const int signum) +{ +#ifndef CONFIG_KGDB_THREAD + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[(signum >> 4) & 0xf]; + remcomOutBuffer[2] = hexchars[signum % 16]; + remcomOutBuffer[3] = 0; +#else /* CONFIG_KGDB_THREAD */ + int threadid; + threadref thref; + char *out = remcomOutBuffer; + const char *tstring = "thread"; + + *out++ = 'T'; + *out++ = hexchars[(signum >> 4) & 0xf]; + *out++ = hexchars[signum % 16]; + + while (*tstring) { + *out++ = *tstring++; + } + *out++ = ':'; + + threadid = trapped_thread->pid; + if (threadid == 0) threadid = PID_MAX; + int_to_threadref(&thref, threadid); + pack_threadid(out, &thref); + out += BUF_THREAD_ID_SIZE; + *out++ = ';'; + + *out = 0; +#endif /* CONFIG_KGDB_THREAD */ + kgdb_put_packet(remcomOutBuffer); +} + +static void send_regs_msg(void) +{ +#ifdef CONFIG_KGDB_THREAD + if (!current_thread) + kregs2gregs(&kgdb_regs, gdb_regs); + else + tregs2gregs(current_thread, gdb_regs); +#else + kregs2gregs(&kgdb_regs, gdb_regs); +#endif + mem2hex((const char *)gdb_regs, remcomOutBuffer, GDB_REGBYTES); + kgdb_put_packet(remcomOutBuffer); +} + +/* Set register contents - currently can't set other thread's registers */ +static void set_regs_msg(void) +{ +#ifdef CONFIG_KGDB_THREAD + if (!current_thread) { +#endif + kregs2gregs(&kgdb_regs, gdb_regs); + hex2mem(&remcomInBuffer[1], (char *) gdb_regs, GDB_REGBYTES); + gregs2kregs(gdb_regs, &kgdb_regs); + send_ok_msg(); +#ifdef CONFIG_KGDB_THREAD + } else + send_err_msg(); +#endif +} + +/* The following provides support for recovery from bus + * errors during kgdb memory read/write operations. + */ +void kgdb_handle_bus_error(void) +{ + kgdb_longjmp(fault_jmp_buf, 1); +} + +/* Read memory due to 'm' message */ +static void read_mem_msg(void) +{ + char *ptr; + int addr; + int length; + + /* Jmp, disable bus error handler */ + if (kgdb_setjmp(fault_jmp_buf) == 0) { + + kgdb_fault_expected = 1; + + /* Walk through, have m, */ + ptr = &remcomInBuffer[1]; + if (hex2int(&ptr, &addr) && (*ptr++ == ',')) + if (hex2int(&ptr, &length)) { + ptr = 0; + if (length * 2 > BUFMAX) + length = BUFMAX / 2; + mem2hex((char *) addr, remcomOutBuffer, length); + } + if (ptr) + send_err_msg(); + else + kgdb_put_packet(remcomOutBuffer); + } else + send_err_msg(); + + /* Restore bus error handler */ + kgdb_fault_expected = 0; +} + +/* Write memory due to 'M' or 'X' message */ +static void write_mem_msg(int binary) +{ + char *ptr; + int addr; + int length; + + if (kgdb_setjmp(fault_jmp_buf) == 0) { + + kgdb_fault_expected = 1; + + /* Walk through, have M,: */ + ptr = &remcomInBuffer[1]; + if (hex2int(&ptr, &addr) && (*ptr++ == ',')) + if (hex2int(&ptr, &length) && (*ptr++ == ':')) { + if (binary) + ebin2mem(ptr, (char*)addr, length); + else + hex2mem(ptr, (char*)addr, length); + ptr = 0; + + /* + * Trap breakpoints + */ + if(length == 4 && !(addr & 0x3) && + *((unsigned *)addr) == GDB_BREAKINST) + *((unsigned *)addr) = KGDB_BREAKINST; + + send_ok_msg(); + } + if (ptr) + send_err_msg(); + } else + send_err_msg(); + + /* Restore bus error handler */ + kgdb_fault_expected = 0; +} + +static void continue_msg(void) +{ + /* Try to read optional parameter, PC unchanged if none */ + char *ptr = &remcomInBuffer[1]; + int addr; + + if (hex2int(&ptr, &addr)) + kgdb_regs.ARM_pc = addr; + + strcpy(remcomOutBuffer, "OK"); + kgdb_put_packet(remcomOutBuffer); +} + +static void continue_with_sig_msg(void) +{ + int signal; + char *ptr = &remcomInBuffer[1]; + int addr; + + /* Report limitation */ + kgdb_msg("Cannot force signal in kgdb, continuing anyway.\n"); + + /* Signal */ + hex2int(&ptr, &signal); + if (*ptr == ';') + ptr++; + + /* Optional address */ + if (hex2int(&ptr, &addr)) + kgdb_regs.ARM_pc = addr; + + strcpy(remcomOutBuffer, "OK"); + kgdb_put_packet(remcomOutBuffer); +} + +void do_single_step(void) +{ + step_addr = (unsigned int *) get_next_pc(&kgdb_regs); + step_instr = *step_addr; + *step_addr = KGDB_BREAKINST; +} + +static void undo_single_step(void) +{ + if (step_addr != NULL) { + *step_addr = step_instr; + step_addr = NULL; + step_instr = 0; + } +} + +static void step_msg(void) +{ + continue_msg(); + do_single_step(); +} + +static void step_with_sig_msg(void) +{ + continue_with_sig_msg(); + do_single_step(); +} + +/* Set the status for a thread */ +void set_thread_msg(void) +{ +#ifndef CONFIG_KGDB_THREAD + strcpy(remcomOutBuffer, "OK"); + kgdb_put_packet(remcomOutBuffer); +#else + int threadid; + struct task_struct *thread = NULL; + char *ptr; + + switch (remcomInBuffer[1]) { + + /* To select which thread for gG etc messages, i.e. supported */ + case 'g': + ptr = &remcomInBuffer[2]; + hex2int(&ptr, &threadid); + thread = get_thread(threadid); + + /* If we haven't found it */ + if (!thread) { + send_err_msg(); + break; + } + + /* Set current_thread (or not) */ + if (thread == trapped_thread) + current_thread = NULL; + else + current_thread = thread; + send_ok_msg(); + break; + + /* To select which thread for cCsS messages, i.e. unsupported */ + case 'c': + send_ok_msg(); + break; + + default: + send_empty_msg(); + break; + } +#endif +} + +#ifdef CONFIG_KGDB_THREAD +/* Is a thread alive? */ +static void thread_status_msg(void) +{ + char *ptr; + int threadid; + struct task_struct *thread = NULL; + + ptr = &remcomInBuffer[1]; + hex2int(&ptr, &threadid); + thread = get_thread(threadid); + if (thread) + send_ok_msg(); + else + send_err_msg(); +} + +/* Send the current thread ID */ +static void thread_id_msg(void) +{ + int threadid; + threadref thref; + + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + + if (current_thread) + threadid = current_thread->pid; + else if (trapped_thread) + threadid = trapped_thread->pid; + else /* Impossible, but just in case! */ + { + send_err_msg(); + return; + } + + /* Translate pid 0 to PID_MAX for gdb */ + if (threadid == 0) threadid = PID_MAX; + + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[2 + BUF_THREAD_ID_SIZE] = '\0'; + kgdb_put_packet(remcomOutBuffer); +} + +/* Send thread info */ +static void thread_info_msg(void) +{ + struct task_struct *thread = NULL; + int threadid; + char *pos; + threadref thref; + + /* Start with 'm' */ + remcomOutBuffer[0] = 'm'; + pos = &remcomOutBuffer[1]; + + /* For all possible thread IDs - this will overrun if > 44 threads! */ + /* Start at 1 and include PID_MAX (since GDB won't use pid 0...) */ + for (threadid = 1; threadid <= PID_MAX; threadid++) { + + read_lock(&tasklist_lock); + thread = get_thread(threadid); + read_unlock(&tasklist_lock); + + /* If it's a valid thread */ + if (thread) { + int_to_threadref(&thref, threadid); + pack_threadid(pos, &thref); + pos += BUF_THREAD_ID_SIZE; + *pos++ = ','; + } + } + *--pos = 0; /* Lose final comma */ + kgdb_put_packet(remcomOutBuffer); + +} + +/* Return printable info for gdb's 'info threads' command */ +static void thread_extra_info_msg(void) +{ + int threadid; + struct task_struct *thread = NULL; + char buffer[20], *ptr; + int i; + + /* Extract thread ID */ + ptr = &remcomInBuffer[17]; + hex2int(&ptr, &threadid); + thread = get_thread(threadid); + + /* If we don't recognise it, say so */ + if (thread == NULL) + strcpy(buffer, "(unknown)"); + else + strcpy(buffer, thread->comm); + + /* Construct packet */ + for (i = 0, ptr = remcomOutBuffer; buffer[i]; i++) + ptr = pack_hex_byte(ptr, buffer[i]); + +#if 0 /* REVISIT! */ + if (thread->thread.pc == (unsigned long)ret_from_fork) { + strcpy(buffer, ""); + for (i = 0; buffer[i]; i++) + ptr = pack_hex_byte(ptr, buffer[i]); + } +#endif + + *ptr = '\0'; + kgdb_put_packet(remcomOutBuffer); +} + +/* Handle all qFooBarBaz messages - have to use an if statement as + opposed to a switch because q messages can have > 1 char id. */ +static void query_msg(void) +{ + const char *q_start = &remcomInBuffer[1]; + + /* qC = return current thread ID */ + if (strncmp(q_start, "C", 1) == 0) + thread_id_msg(); + + /* qfThreadInfo = query all threads (first) */ + else if (strncmp(q_start, "fThreadInfo", 11) == 0) + thread_info_msg(); + + /* qsThreadInfo = query all threads (subsequent). We know we have sent + them all after the qfThreadInfo message, so there are no to send */ + else if (strncmp(q_start, "sThreadInfo", 11) == 0) + kgdb_put_packet("l"); /* el = last */ + + /* qThreadExtraInfo = supply printable information per thread */ + else if (strncmp(q_start, "ThreadExtraInfo", 15) == 0) + thread_extra_info_msg(); + + /* Unsupported - empty message as per spec */ + else + send_empty_msg(); +} +#endif /* CONFIG_KGDB_THREAD */ + +/* + * This function does all command procesing for interfacing to gdb. + */ +void do_kgdb(struct pt_regs *trap_regs, unsigned char sigval) +{ + unsigned long flags; + + save_flags_cli(flags); + + kgdb_regs = *trap_regs; /* Quick and dirty clone of pt_regs */ + + /* + * reply to host that an exception has occurred + * + * We don't do this on the first call as it would cause a sync problem. + */ + if (kgdb_initialized) { + send_signal_msg(sigval); + } else { + /* + * This is the first breakpoint, called from + * start_kernel or elsewhere. We need to + * (re-)initialize the I/O subsystem. + */ + + printk("Breaking into KGDB\n"); + + if(kgdb_io_init()) + { + printk("KGB I/O INIT FAILED...HALTING!"); + while(1){ }; + } + } + + kgdb_initialized = 1; + +#ifdef CONFIG_KGDB_THREAD + /* Until GDB specifies a thread */ + current_thread = NULL; + trapped_thread = current; +#endif + + /* + * Check to see if this is a compiled in breakpoint + * (sysrq-G or initial breakpoint). If so, we + * need to increment the PC to the next instruction + * so that we don't infinite loop. + * + * NOTE: THIS COULD BE BAD. We're reading the PC and + * if the cause of the fault is a bad PC, we're going + * to suffer massive death. Need to find some way to + * validate the PC address or use our setjmp/longjmp. + */ + if (sigval == SIGTRAP) { + /* Only do this check for the SIGTRAP case! */ + if (*(unsigned int *)(kgdb_regs.ARM_pc) == KGDB_COMPILED_BREAK) + kgdb_regs.ARM_pc += 4; + } + + + undo_single_step(); /* handles cleanup upon step/stepi return */ + + while (1) { + remcomOutBuffer[0] = 0; + + kgdb_get_packet(remcomInBuffer, BUFMAX); + + switch (remcomInBuffer[0]) + { + case '?': /* Report most recent signal */ + send_signal_msg(sigval); + break; + + case 'g': /* return the values of the CPU registers */ + send_regs_msg(); + break; + + case 'G': /* set the values of the CPU registers */ + set_regs_msg(); + break; + + case 'm': /* Read LLLL bytes address AA..AA */ + read_mem_msg(); + break; + + case 'M': /* Write LLLL bytes address AA.AA ret OK */ + write_mem_msg(0); + break; + + case 'X': /* Write LLLL bytes esc bin address AA..AA */ + /* WARNING: UART must be configured for 8-bit chars */ + write_mem_msg(1); /* 1 = data in binary */ + break; + + case 'C': /* Continue, signum included, we ignore it */ + continue_with_sig_msg(); + goto exit_kgdb; + + case 'c': /* Continue at address AA..AA (optional) */ + continue_msg(); + goto exit_kgdb; + + case 'S': /* Step, signum included, we ignore it */ + step_with_sig_msg(); + goto exit_kgdb; + + case 's': /* Step one instruction from AA..AA */ + step_msg(); + goto exit_kgdb; + + case 'H': /* Task related */ + set_thread_msg(); + break; + +#ifdef CONFIG_KGDB_THREAD + case 'T': /* Query thread status */ + thread_status_msg(); + break; + + case 'q': /* Handle query - currently thread-related */ + query_msg(); + break; +#endif + + case 'k': /* kill the program - do nothing */ + break; + + case 'D': /* Detach from program, send reply OK */ + kgdb_enabled = 0; + send_ok_msg(); + kgdb_serial_getchar(); + goto exit_kgdb; + + case 'd': /* toggle debug flag */ + remote_debug = !(remote_debug); + break; + + default: + send_empty_msg(); + break; + } + } + +exit_kgdb: + *trap_regs = kgdb_regs; /* Copy back any register updates */ + cpu_cache_clean_invalidate_all(); + restore_flags(flags); +} + +/* + * TODO: If remote GDB disconnects from us, we need to return + * 0 as we're no longer active. Does GDB send us a disconnect + * message?? + */ +int kgdb_active(void) +{ + return kgdb_enabled; +} + + +/* Trigger a breakpoint by function */ +void breakpoint(void) +{ + if (!kgdb_enabled) { + kgdb_enabled = 1; + } + BREAKPOINT(); +} + +static int __init kgdb_opt_gdb(char *str) { + kgdb_enter = 1; + return 1; +} + +static int __init kgdb_opt_gdbttyS(char *str) { +#ifdef CONFIG_KGDB_UART1 + kgdb_ttyS=1; +#endif +#ifdef CONFIG_KGDB_UART2 + kgdb_ttyS=2; +#endif + //kgdb_ttyS = simple_strtoul(str,NULL,10); + return 1; +} + +static int __init kgdb_opt_gdbbaud(char *str) { + +#ifdef CONFIG_KGDB_9600BAUD + kgdb_baud=9600; +#endif + +#ifdef CONFIG_KGDB_19200BAUD + kgdb_baud=19200; +#endif + +#ifdef CONFIG_KGDB_38400BAUD + kgdb_baud=38400; +#endif + +#ifdef CONFIG_KGDB_57600BAUD + kgdb_baud=57600; +#endif + +#ifdef CONFIG_KGDB_115200BAUD + kgdb_baud=115200; +#endif + //kgdb_baud = simple_strtoul(str,NULL,10); + return 1; +} + +__setup("gdbttyS=", kgdb_opt_gdbttyS); +__setup("gdbbaud=", kgdb_opt_gdbbaud); +__setup("gdb", kgdb_opt_gdb); + +/* + * Code to determine next PC based on current PC address. + * Taken from GDB source code. Open Source is good. :) + */ +#define read_register(x) regs->uregs[x] + +#define addr_bits_remove(x) (x & 0xfffffffc) + +#define submask(x) ((1L << ((x) + 1)) - 1) +#define bit(obj,st) (((obj) >> (st)) & 1) +#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st))) +#define sbits(obj,st,fn) ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~submask(fn-st)))) +#define BranchDest(addr,instr) ((unsigned) (((long) (addr)) + 8 + (sbits (instr, 0, 23) <<2))) + +/* Instruction condition field values. */ +#define INST_EQ 0x0 +#define INST_NE 0x1 +#define INST_CS 0x2 +#define INST_CC 0x3 +#define INST_MI 0x4 +#define INST_PL 0x5 +#define INST_VS 0x6 +#define INST_VC 0x7 +#define INST_HI 0x8 +#define INST_LS 0x9 +#define INST_GE 0xa +#define INST_LT 0xb +#define INST_GT 0xc +#define INST_LE 0xd +#define INST_AL 0xe +#define INST_NV 0xf + +#define FLAG_N 0x80000000 +#define FLAG_Z 0x40000000 +#define FLAG_C 0x20000000 +#define FLAG_V 0x10000000 + +#define error(x) + +static unsigned long +shifted_reg_val (unsigned long inst, int carry, unsigned long pc_val, + unsigned long status_reg, struct pt_regs* regs) +{ + unsigned long res = 0, shift = 0; + int rm = bits (inst, 0, 3); + unsigned long shifttype = bits (inst, 5, 6); + + if (bit (inst, 4)) + { + int rs = bits (inst, 8, 11); + shift = (rs == 15 ? pc_val + 8 : read_register (rs)) & 0xFF; + } + else + shift = bits (inst, 7, 11); + + res = (rm == 15 + ? ((pc_val | (1 ? 0 : status_reg)) + + (bit (inst, 4) ? 12 : 8)) : read_register (rm)); + + switch (shifttype) + { + case 0: /* LSL */ + res = shift >= 32 ? 0 : res << shift; + break; + + case 1: /* LSR */ + res = shift >= 32 ? 0 : res >> shift; + break; + + case 2: /* ASR */ + if (shift >= 32) + shift = 31; + res = ((res & 0x80000000L) + ? ~((~res) >> shift) : res >> shift); + break; + + case 3: /* ROR/RRX */ + shift &= 31; + if (shift == 0) + res = (res >> 1) | (carry ? 0x80000000L : 0); + else + res = (res >> shift) | (res << (32 - shift)); + break; + } + + return res & 0xffffffff; +} + +/* Return number of 1-bits in VAL. */ + +static int +bitcount (unsigned long val) +{ + int nbits; + for (nbits = 0; val != 0; nbits++) + val &= val - 1; /* delete rightmost 1-bit in val */ + return nbits; +} + +static int +condition_true (unsigned long cond, unsigned long status_reg) +{ + if (cond == INST_AL || cond == INST_NV) + return 1; + + switch (cond) + { + case INST_EQ: + return ((status_reg & FLAG_Z) != 0); + case INST_NE: + return ((status_reg & FLAG_Z) == 0); + case INST_CS: + return ((status_reg & FLAG_C) != 0); + case INST_CC: + return ((status_reg & FLAG_C) == 0); + case INST_MI: + return ((status_reg & FLAG_N) != 0); + case INST_PL: + return ((status_reg & FLAG_N) == 0); + case INST_VS: + return ((status_reg & FLAG_V) != 0); + case INST_VC: + return ((status_reg & FLAG_V) == 0); + case INST_HI: + return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C); + case INST_LS: + return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C); + case INST_GE: + return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0)); + case INST_LT: + return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0)); + case INST_GT: + return (((status_reg & FLAG_Z) == 0) && (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0))); + case INST_LE: + return (((status_reg & FLAG_Z) != 0) || (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0))); + } + return 1; +} + +unsigned long get_next_pc(struct pt_regs *regs) +{ + unsigned long pc_val; + unsigned long this_instr; + unsigned long status; + unsigned long nextpc; + + pc_val = regs->ARM_pc; + this_instr = *((unsigned long *)regs->ARM_pc); + status = regs->ARM_cpsr; + nextpc = pc_val + 4; /* Default case */ + + if (condition_true (bits (this_instr, 28, 31), status)) + { + switch (bits (this_instr, 24, 27)) + { + case 0x0: + case 0x1: /* data processing */ + case 0x2: + case 0x3: + { + unsigned long operand1, operand2, result = 0; + unsigned long rn; + int c; + + if (bits (this_instr, 12, 15) != 15) + break; + + if (bits (this_instr, 22, 25) == 0 + && bits (this_instr, 4, 7) == 9) + /* multiply */ + error ("Illegal update to pc in instruction"); + + /* Multiply into PC */ + c = (status & FLAG_C) ? 1 : 0; + rn = bits (this_instr, 16, 19); + operand1 = (rn == 15) ? pc_val + 8 : read_register (rn); + + if (bit (this_instr, 25)) + { + unsigned long immval = bits (this_instr, 0, 7); + unsigned long rotate = 2 * bits (this_instr, 8, 11); + operand2 = ((immval >> rotate) | (immval << (32 - rotate))) + & 0xffffffff; + } + else /* operand 2 is a shifted register */ + operand2 = shifted_reg_val (this_instr, c, pc_val, status, regs); + switch (bits (this_instr, 21, 24)) + { + case 0x0: /*and */ + result = operand1 & operand2; + break; + + case 0x1: /*eor */ + result = operand1 ^ operand2; + break; + + case 0x2: /*sub */ + result = operand1 - operand2; + break; + + case 0x3: /*rsb */ + result = operand2 - operand1; + break; + + case 0x4: /*add */ + result = operand1 + operand2; + break; + + case 0x5: /*adc */ + result = operand1 + operand2 + c; + break; + + case 0x6: /*sbc */ + result = operand1 - operand2 + c; + break; + + case 0x7: /*rsc */ + result = operand2 - operand1 + c; + break; + + case 0x8: + case 0x9: + case 0xa: + case 0xb: /* tst, teq, cmp, cmn */ + result = (unsigned long) nextpc; + break; + + case 0xc: /*orr */ + result = operand1 | operand2; + break; + + case 0xd: /*mov */ + /* Always step into a function. */ + result = operand2; + break; + + case 0xe: /*bic */ + result = operand1 & ~operand2; + break; + + case 0xf: /*mvn */ + result = ~operand2; + break; + } + nextpc = addr_bits_remove(result); + + break; + } + + case 0x4: + case 0x5: /* data transfer */ + case 0x6: + case 0x7: + if (bit (this_instr, 20)) + { + /* load */ + if (bits (this_instr, 12, 15) == 15) + { + /* rd == pc */ + unsigned long rn; + unsigned long base; + + if (bit (this_instr, 22)) + error ("Illegal update to pc in instruction"); + + /* byte write to PC */ + rn = bits (this_instr, 16, 19); + base = (rn == 15) ? pc_val + 8 : read_register (rn); + if (bit (this_instr, 24)) + { + /* pre-indexed */ + int c = (status & FLAG_C) ? 1 : 0; + unsigned long offset = + (bit (this_instr, 25) + ? shifted_reg_val (this_instr, c, pc_val, status, regs) + : bits (this_instr, 0, 11)); + + if (bit (this_instr, 23)) + base += offset; + else + base -= offset; + } + nextpc = *((unsigned long *) base); + + nextpc = addr_bits_remove (nextpc); + + if (nextpc == regs->ARM_pc) + error ("Infinite loop detected"); + } + } + break; + + case 0x8: + case 0x9: /* block transfer */ + if (bit (this_instr, 20)) + { + /* LDM */ + if (bit (this_instr, 15)) + { + /* loading pc */ + int offset = 0; + + if (bit (this_instr, 23)) + { + /* up */ + unsigned long reglist = bits (this_instr, 0, 14); + offset = bitcount (reglist) * 4; + if (bit (this_instr, 24)) /* pre */ + offset += 4; + } + else if (bit (this_instr, 24)) + offset = - 4; + + { + unsigned long rn_val = + read_register (bits (this_instr, 16, 19)); + nextpc = *((unsigned int *) (rn_val + offset)); + } + + nextpc = addr_bits_remove (nextpc); + if (nextpc == regs->ARM_pc) + error ("Infinite loop detected"); + } + } + break; + + case 0xb: /* branch & link */ + case 0xa: /* branch */ + { + nextpc = BranchDest (regs->ARM_pc, this_instr); + nextpc = addr_bits_remove (nextpc); + + if (nextpc == regs->ARM_pc) + error ("Infinite loop detected"); + + break; + } + + case 0xc: + case 0xd: + case 0xe: /* coproc ops */ + case 0xf: /* SWI */ + break; + + default: + error("Bad bit-field extraction"); + return (regs->ARM_pc); + } + } + + return nextpc; +} diff -ruN linux/arch/arm/kernel/Makefile kgdb_linux/arch/arm/kernel/Makefile --- linux/arch/arm/kernel/Makefile 2004-09-17 20:08:55.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/Makefile 2004-10-06 12:03:49.000000000 +0530 @@ -49,6 +49,9 @@ obj-y += irq-arch.o endif +obj-$(CONFIG_KGDB) += kgdb-stub.o kgdb-jmp.o +obj-$(CONFIG_KGDB_SERIAL) += kgdb-serial.o +obj-$(CONFIG_KGDB_UDP) += kgdb-udp.o obj-$(CONFIG_ARCH_ACORN) += ecard.o fiq.o time-acorn.o obj-$(CONFIG_ARCH_CLPS7500) += time-acorn.o obj-$(CONFIG_DEBUG_LL) += debug-$(PROCESSOR).o diff -ruN linux/arch/arm/kernel/traps.c kgdb_linux/arch/arm/kernel/traps.c --- linux/arch/arm/kernel/traps.c 2004-09-17 20:08:55.000000000 +0530 +++ kgdb_linux/arch/arm/kernel/traps.c 2004-10-01 10:51:33.000000000 +0530 @@ -12,6 +12,7 @@ * 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably * kill the offending process. */ + #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include "ptrace.h" @@ -185,6 +187,12 @@ set_fs(fs); } +#ifdef CONFIG_KGDB + if (kgdb_active()) { + do_kgdb(regs, SIGKILL); /* SIGKILL sure seems appropriate here. : ) */ + } +#endif + spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } @@ -209,6 +217,13 @@ regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; pc = (unsigned long *)instruction_pointer(regs); +#ifdef CONFIG_KGDB + if (!user_mode(regs) && kgdb_active()) { + do_kgdb(regs, SIGTRAP); + return; + } +#endif + #ifdef CONFIG_DEBUG_USER printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n", current->comm, current->pid, pc); @@ -239,6 +254,12 @@ dump_instr(regs); #endif +#ifdef CONFIG_KGDB + if (!user_mode(regs) && kgdb_active()) { + do_kgdb(regs, SIGBUS); + } +#endif + current->thread.error_code = 0; current->thread.trap_no = 11; @@ -294,6 +315,12 @@ set_fs(fs); +#ifdef CONFIG_KGDB + if (!user_mode(regs) && kgdb_active()) { + do_kgdb(regs, -1); + } +#endif + die("Oops", regs, 0); cli(); panic("bad mode"); @@ -345,6 +372,11 @@ switch (no & 0xffff) { case 0: /* branch through 0 */ +#ifdef CONFIG_KGDB + if (!user_mode(regs) && kgdb_active()) { + do_kgdb(regs, -1); + } +#endif info.si_signo = SIGSEGV; info.si_errno = 0; info.si_code = SEGV_MAPERR; @@ -432,6 +464,12 @@ c_backtrace(regs->ARM_fp, processor_mode(regs)); } #endif +#ifdef CONFIG_KGDB + if (!user_mode(regs) && kgdb_active()) { + do_kgdb(regs, SIGILL); + } +#endif + info.si_signo = SIGILL; info.si_errno = 0; info.si_code = ILL_ILLTRP; @@ -466,6 +504,11 @@ dump_instr(regs); show_pte(current->mm, addr); #endif +#ifdef CONFIG_KGDB + if (!user_mode(regs) && kgdb_active()) { + do_kgdb(regs, SIGILL); + } +#endif info.si_signo = SIGILL; info.si_errno = 0; @@ -520,6 +563,17 @@ panic("Oops failed to kill thread"); } +#ifdef CONFIG_KGDB +static int kgdb_halt = 1; + +void __init nohalt_setup(char *line) +{ + kgdb_halt = 0; +} + +__setup("nohalt", nohalt_setup); +#endif + void __init trap_init(void) { extern void __trap_init(void *); @@ -531,4 +585,13 @@ #ifdef CONFIG_CPU_32 modify_domain(DOMAIN_USER, DOMAIN_CLIENT); #endif + /* + * This is the earliest we can bkpt for now as we rely on + * undefined instruction trap to hook into KGDB. + */ +#ifdef CONFIG_KGDB + if(kgdb_halt) + breakpoint(); +#endif + } diff -ruN linux/arch/arm/mach-mx1ads/kgdb-serial.c kgdb_linux/arch/arm/mach-mx1ads/kgdb-serial.c --- linux/arch/arm/mach-mx1ads/kgdb-serial.c 1970-01-01 05:30:00.000000000 +0530 +++ kgdb_linux/arch/arm/mach-mx1ads/kgdb-serial.c 2004-10-06 12:04:46.000000000 +0530 @@ -0,0 +1,91 @@ +/* + * FILE NAME: arch/arm/mach-mx1ads/kgdb-serial.c + * + * BRIEF MODULE DESCRIPTION: + * Provides low level kgdb serial support hooks for MX1. + * + * Copyright 2001, 2003 MontaVista Software Inc. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#define UART_GET_CHAR(p) inl((p) + UART_RXDATA) +#define UART_PUT_CHAR(p, c) outl((c), (p) + UART_TXDATA) +#define UART_PUT_UBIR(p,c) outl((c), (p) + UART_BIR) +#define UART_PUT_UBMR(p,c) outl((c), (p) + UART_BMR) +#define UART_PUT_UCR1(p,c) outl((c), (p) + UART_CR1) +#define UART_PUT_UCR2(p,c) outl((c), (p) + UART_CR2) +#define UART_PUT_UCR3(p,c) outl((c), (p) + UART_CR3) +#define UART_PUT_UCR4(p,c) outl((c), (p) + UART_CR4) +#define UART_PUT_UFCR(p,c) outl((c), (p) + UART_UFCR) +#define UART_GET_USR2(p) inl((p) + UART_SR2) +#define _reg_PCDR (*((volatile unsigned long *)(IO_ADDRESS(PLL_BASE)+PLL_PCDR))) + +typedef unsigned char U8; /* unsigned 8 bit data */ +typedef unsigned short U16; /* unsigned 16 bit data */ +typedef unsigned int U32; /* unsigned 32 bit data */ + +#define TXFE_MASK 0x4000 // Tx buffer empty +#define RDR_MASK 0x0001 // receive data ready + +static volatile unsigned char *port = NULL; + +void kgdb_serial_init(void) +{ + _reg_PCDR &= 0xFFFFFFF0; + _reg_PCDR |= 0x5; // 16 MHz + switch (kgdb_ttyS) { + case 2: + port =(unsigned char*)UART2_BASE; + break; + case 1: + default: + port =(unsigned char*)UART1_BASE; + } + UART_PUT_UCR1(port, 5); + UART_PUT_UCR2(port, 0x4027); + UART_PUT_UCR4(port, 1); + UART_PUT_UFCR(port, 0xA81); + UART_PUT_UBIR(port, kgdb_baud / 100 - 1); + UART_PUT_UBMR(port, 10000 - 1); + return; +} +void kgdb_serial_putchar(unsigned char ch) +{ + while (!(UART_GET_USR2(port) & TXFE_MASK)) ; // wait until TXFE bit set + UART_PUT_CHAR(port, ch); +} + +unsigned char kgdb_serial_getchar(void) +{ + while (!(UART_GET_USR2(port) & RDR_MASK)) ; // wait until RDR bit set + return (unsigned char) UART_GET_CHAR(port); +} + diff -ruN linux/arch/arm/mach-mx1ads/Makefile kgdb_linux/arch/arm/mach-mx1ads/Makefile --- linux/arch/arm/mach-mx1ads/Makefile 2004-09-17 20:08:55.000000000 +0530 +++ kgdb_linux/arch/arm/mach-mx1ads/Makefile 2004-09-17 20:40:36.000000000 +0530 @@ -15,6 +15,6 @@ obj-m := obj-n := obj- := - +obj-$(CONFIG_KGDB_SERIAL) += kgdb-serial.o include $(TOPDIR)/Rules.make diff -ruN linux/arch/arm/Makefile kgdb_linux/arch/arm/Makefile --- linux/arch/arm/Makefile 2004-09-17 20:08:55.000000000 +0530 +++ kgdb_linux/arch/arm/Makefile 2004-10-06 12:05:18.000000000 +0530 @@ -19,6 +19,13 @@ CFLAGS +=-g endif +ifeq ($(CONFIG_KGDB),y) +CFLAGS :=$(CFLAGS:-fomit-frame-pointer=) +CFLAGS +=-g +ifdef CONFIG_KGDB_MORE +CFLAGS += $(shell echo $(CONFIG_KGDB_OPTIONS) | sed -e 's/"//g') +endif +endif # Select CPU dependent flags. Note that order of declaration is important; # the options further down the list override previous items. # diff -ruN linux/arch/arm/mm/fault-armv.c kgdb_linux/arch/arm/mm/fault-armv.c --- linux/arch/arm/mm/fault-armv.c 2004-09-17 20:08:55.000000000 +0530 +++ kgdb_linux/arch/arm/mm/fault-armv.c 2004-09-17 20:40:36.000000000 +0530 @@ -27,6 +27,7 @@ #include #include #include +#include extern void die_if_kernel(const char *str, struct pt_regs *regs, int err); extern void show_pte(struct mm_struct *mm, unsigned long addr); @@ -112,13 +113,22 @@ { const struct fsr_info *inf = fsr_info + (fsr & 15); +#ifdef CONFIG_KGDB + if(kgdb_active() && kgdb_fault_expected) + kgdb_handle_bus_error(); +#endif + if (!inf->fn(addr, error_code, regs)) return; - printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", - inf->name, fsr, addr); + printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",inf->name, fsr, addr); force_sig(inf->sig, current); show_pte(current->mm, addr); +#ifdef CONFIG_KGDB + if (!user_mode(regs) && kgdb_active()) { + do_kgdb(regs, inf->sig); + } +#endif die_if_kernel("Oops", regs, 0); } diff -ruN linux/arch/arm/mm/fault-common.c kgdb_linux/arch/arm/mm/fault-common.c --- linux/arch/arm/mm/fault-common.c 2004-09-17 20:08:55.000000000 +0530 +++ kgdb_linux/arch/arm/mm/fault-common.c 2004-09-17 20:40:36.000000000 +0530 @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef CONFIG_CPU_26 #define FAULT_CODE_WRITE 0x02 @@ -123,6 +124,12 @@ "paging request", addr); show_pte(mm, addr); + +#ifdef CONFIG_KGDB + if (kgdb_active()) { + do_kgdb(regs, SIGSEGV); + } +#endif die("Oops", regs, error_code); do_exit(SIGKILL); } @@ -175,6 +182,11 @@ struct vm_area_struct *vma; int fault, mask; +#if defined(CONFIG_KGDB) + if (kgdb_fault_expected) + kgdb_handle_bus_error(); +#endif + vma = find_vma(mm, addr); fault = -2; /* bad map area */ if (!vma) @@ -242,6 +254,11 @@ struct mm_struct *mm; int fault; +#if defined(CONFIG_KGDB) + if (kgdb_fault_expected) + kgdb_handle_bus_error(); +#endif + tsk = current; mm = tsk->mm; diff -ruN linux/drivers/char/tty_io.c kgdb_linux/drivers/char/tty_io.c --- linux/drivers/char/tty_io.c 2004-09-17 20:09:26.000000000 +0530 +++ kgdb_linux/drivers/char/tty_io.c 2004-09-17 20:40:36.000000000 +0530 @@ -162,6 +162,10 @@ extern void ambauart_console_init(void); extern void mx1uart_console_init(void); +#ifdef CONFIG_KGDB_CONSOLE +extern void kgdb_console_init(void); +#endif + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -2244,6 +2248,9 @@ #ifdef CONFIG_SERIAL_TX3912_CONSOLE tx3912_console_init(); #endif +#ifdef CONFIG_KGDB_CONSOLE + kgdb_console_init(); +#endif #ifdef CONFIG_MX1_EXT_UART ambauart_console_init(); #endif diff -ruN linux/include/asm-arm/kgdb.h kgdb_linux/include/asm-arm/kgdb.h --- linux/include/asm-arm/kgdb.h 1970-01-01 05:30:00.000000000 +0530 +++ kgdb_linux/include/asm-arm/kgdb.h 2004-10-06 12:06:56.000000000 +0530 @@ -0,0 +1,96 @@ +/* + * include/asm-arm/kgdb.h + * + * ARM KGDB support + * + * Author: Deepak Saxena + * + * MX1ADS Support added by Bhupesh Kumar Pandey + * + * Copyright (C) 2002 MontaVista Software Inc. + * + */ + +#ifndef __ASM_KGDB_H__ +#define __ASM_KGDB_H__ + +#include +#include + +#ifdef CONFIG_KGDB + +#define GDB_BREAKINST 0xef9f0001 +#define KGDB_BREAKINST 0xe7ffdefe +#define KGDB_COMPILED_BREAK 0xe7ffdeff + +#define UART1_BASE IO_ADDRESS(0x00206000) +#define UART2_BASE IO_ADDRESS(0x00207000) +#define UART_RXDATA 0x00 +#define UART_TXDATA 0x40 +#define UART_CR1 0x80 +#define UART_CR2 0x84 +#define UART_CR3 0x88 +#define UART_CR4 0x8C +#define UART_UFCR 0x90 +#define UART_SR2 0x98 +#define UART_BIR 0xA4 +#define UART_BMR 0xA8 +#define PLL_BASE 0x0021B000 +#define PLL_PCDR 0x20 + +#if !defined(__ASSEMBLY__) + +void do_kgdb(struct pt_regs *, unsigned char); + +int kgdb_active(void); + +extern void kgdb_handle_bus_error(void); +extern int kgdb_setjmp(int *machine_context); +extern int kgdb_longjmp(int *machine_context, int flag); +extern int kgdb_fault_expected; + +extern int kgdb_enter; +extern int kgdb_ttyS; +extern unsigned long kgdb_baud; + +extern void breakpoint(void); + +#define BREAKPOINT() asm (".word 0xe7ffdeff") + +extern void kgdb_get_packet(unsigned char *, int); +extern void kgdb_put_packet(const unsigned char *); + +extern int kgdb_io_init(void); + +#ifdef CONFIG_KGDB_SERIAL + +extern unsigned char kgdb_serial_getchar(void); +extern void kgdb_serial_putchar(unsigned char); + +extern void kgdb_serial_init(void); + +#endif + +#ifdef CONFIG_KGDB_UDP + +extern unsigned kgdb_net_rx(unsigned char *); +extern unsigned kgdb_net_tx(unsigned char *); + +#endif + +#ifdef CONFIG_KGDB_CONSOLE + +void kgdb_console_init(void); + +#endif + +#else // NO KGDB + +#define kgdb_active() 0 + +#define breakpoint() + +#endif + +#endif // !__ASSEMBLY__ +#endif // __ASM_KGDB_H__