diff -ruN uClinux-2.4.org/arch/sh/config.in uClinux-2.4.new/arch/sh/config.in --- uClinux-2.4.org/arch/sh/config.in 2004-10-24 15:33:46.000000000 +0530 +++ uClinux-2.4.new/arch/sh/config.in 2004-04-08 05:46:04.000000000 +0530 @@ -478,6 +478,34 @@ string 'Command Line' CONFIG_COMMAND_LINE_VALUE "" fi +bool 'Include KGDB kernel debugger' CONFIG_SH_KGDB +if [ "$CONFIG_SH_KGDB" = "y" ]; then + bool ' Enter KGDB on NMI' CONFIG_KGDB_NMI + bool ' Include KGDB thread support' CONFIG_KGDB_THREAD + bool ' Console messages through GDB' CONFIG_SH_KGDB_CONSOLE + bool ' Allow SysRq "G" to enter KGDB?' CONFIG_KGDB_SYSRQ + if [ "$CONFIG_KGDB_SYSRQ" != "n" ]; then + define_bool CONFIG_MAGIC_SYSRQ y + fi + bool ' Include KGDB kernel asserts' CONFIG_KGDB_KERNEL_ASSERTS + bool ' Add additional compile options' CONFIG_KGDB_MORE + if [ "$CONFIG_KGDB_MORE" = "y" ]; then + string ' Additional compile options' CONFIG_KGDB_OPTIONS "-O1" + fi + mainmenu_option next_comment + comment ' Default KGDB port setup' + int 'Port number (ttySCn)' CONFIG_KGDB_DEFPORT 1 + int 'Baud rate' CONFIG_KGDB_DEFBAUD 115200 + choice 'Parity' \ + "None CONFIG_KGDB_DEFPARITY_N \ + Even CONFIG_KGDB_DEFPARITY_E \ + Odd CONFIG_KGDB_DEFPARITY_O" None + choice 'DataBits' \ + "7 CONFIG_KGDB_DEFBITS_7 \ + 8 CONFIG_KGDB_DEFBITS_8" 8 + endmenu +fi + int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 endmenu diff -ruN uClinux-2.4.org/arch/sh/kernel/entry.S uClinux-2.4.new/arch/sh/kernel/entry.S --- uClinux-2.4.org/arch/sh/kernel/entry.S 2004-10-24 15:15:56.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/entry.S 2004-04-08 05:46:04.000000000 +0530 @@ -88,6 +88,9 @@ MMU_TEA = 0xff00000c ! TLB Exception Address Register #endif +#if defined(CONFIG_KGDB_NMI) +NMI_VEC = 0x1c0 ! Must catch early for debounce +#endif /* Offsets to the stack */ OFF_R0 = 0 /* Return value. New ABI also arg4 */ @@ -222,21 +225,31 @@ 2: .long SYMBOL_NAME(do_address_error) -#if defined(CONFIG_SH_STANDARD_BIOS) +#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) ! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present. ! If both are configured, handle the debug traps (breakpoints) in SW, ! but still allow BIOS traps to FW. .align 2 debug_kernel: -#if defined(CONFIG_SH_STANDARD_BIOS) +#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB) /* Force BIOS call to FW (debug_trap put TRA in r8) */ mov r8,r0 shlr2 r0 cmp/eq #0x3f,r0 bt debug_kernel_fw -#endif /* CONFIG_SH_STANDARD_BIOS */ +#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */ +debug_enter: +#if defined(CONFIG_SH_KGDB) + /* Jump to kgdb, pass stacked regs as arg */ +debug_kernel_sw: + mov.l 3f, r0 + jmp @r0 + mov r15, r4 + .align 2 +3: .long SYMBOL_NAME(kgdb_handle_exception) +#endif /* CONFIG_SH_KGDB */ #if defined(CONFIG_SH_STANDARD_BIOS) /* Unwind the stack and jmp to the debug entry */ @@ -278,12 +291,12 @@ 2: .long SYMBOL_NAME(gdb_vbr_vector) #endif /* CONFIG_SH_STANDARD_BIOS */ -#endif /* CONFIG_SH_STANDARD_BIOS */ +#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ .align 2 debug_trap: -#if defined(CONFIG_SH_STANDARD_BIOS) +#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register shll r0 @@ -626,7 +639,12 @@ 7: or k0, k2 ! Set the IMASK-bits ldc k2, ssr ! - +#if defined(CONFIG_KGDB_NMI) + ! Clear in_nmi + mov.l 4f, k0 + mov #0, k1 + mov.b k1, @k0 +#endif mov.l @r15+, k2 ! restore EXPEVT mov k4, r15 rte @@ -638,6 +656,9 @@ 2: .long 0xffff7f0f ! ~(IMASK+FD) 3: .long 0x00080000 ! SZ=0, PR=1 31: .long 0x00001000 ! DSP bit set +#if defined(CONFIG_KGDB_NMI) +4: .long SYMBOL_NAME(in_nmi) +#endif 5: .long 0x00001000 ! DSP @@ -671,7 +692,22 @@ interrupt: mov.l 2f, k2 mov.l 3f, k3 - +#if defined(CONFIG_KGDB_NMI) + ! Debounce (filter nested NMI) + mov.l @k2, k0 + mov.l 5f, k1 + cmp/eq k1, k0 + bf 0f + mov.l 6f, k1 + tas.b @k1 + bt 0f + rte + nop + .align 2 +5: .long NMI_VEC +6: .long SYMBOL_NAME(in_nmi) +0: +#endif /* defined(CONFIG_KGDB_NMI) */ bra handle_exception mov.l @k2, k2 @@ -815,7 +851,11 @@ .long error ! reserved_instruction (filled by trap_init) .long error ! illegal_slot_instruction (filled by trap_init) ENTRY(nmi_slot) - +#if defined (CONFIG_KGDB_NMI) + .long debug_enter ! Allow trap to debugger +#else + .long none ! Not implemented yet +#endif ENTRY(user_break_point_trap) .long break_point_trap ENTRY(interrupt_table) diff -ruN uClinux-2.4.org/arch/sh/kernel/kgdb_jmp.S uClinux-2.4.new/arch/sh/kernel/kgdb_jmp.S --- uClinux-2.4.org/arch/sh/kernel/kgdb_jmp.S 1970-01-01 05:30:00.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/kgdb_jmp.S 2004-04-08 05:46:04.000000000 +0530 @@ -0,0 +1,33 @@ +#include + +ENTRY(kgdb_setjmp) + add #(9*4), r4 + sts.l pr, @-r4 + mov.l r15, @-r4 + mov.l r14, @-r4 + mov.l r13, @-r4 + mov.l r12, @-r4 + mov.l r11, @-r4 + mov.l r10, @-r4 + mov.l r9, @-r4 + mov.l r8, @-r4 + rts + mov #0, r0 + +ENTRY(kgdb_longjmp) + mov.l @r4+, r8 + mov.l @r4+, r9 + mov.l @r4+, r10 + mov.l @r4+, r11 + mov.l @r4+, r12 + mov.l @r4+, r13 + mov.l @r4+, r14 + mov.l @r4+, r15 + lds.l @r4+, pr + mov r5, r0 + tst r0, r0 + bf 1f + mov #1, r0 ! in case val==0 +1: rts + nop + diff -ruN uClinux-2.4.org/arch/sh/kernel/kgdb_stub.c uClinux-2.4.new/arch/sh/kernel/kgdb_stub.c --- uClinux-2.4.org/arch/sh/kernel/kgdb_stub.c 1970-01-01 05:30:00.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/kgdb_stub.c 2004-10-24 09:40:33.000000000 +0530 @@ -0,0 +1,1488 @@ +/* + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Contains extracts from code by Glenn Engel, Jim Kingdon, + * David Grothe , Tigran Aivazian , + * Amit S. Kale , William Gatliff , + * Ben Lee, Steve Chamberlain and Benoit Miller . + * + * This version by Henry Bell + * Minor modifications by Jeremy Siegel + * + * Contains low-level support for remote debug using GDB. + * + * To enable debugger support, two things need to happen. 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. + * A breakpoint also needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint() which does + * a trapa if the initialisation phase has been successfully completed. + * + * In this case, set_debug_traps() is not used to "take over" exceptions; + * other kernel code is modified instead to enter the kgdb functions here + * when appropriate (see entry.S for breakpoint traps and NMI interrupts, + * see traps.c for kernel error exceptions). + * + * 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 + * + * 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". + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SH_KGDB_CONSOLE +#include +#endif + +/* Function pointers for linkage */ +kgdb_debug_hook_t *kgdb_debug_hook; +kgdb_bus_error_hook_t *kgdb_bus_err_hook; + +int (*kgdb_getchar)(void); +void (*kgdb_putchar)(int); + +static void put_debug_char(int c) +{ + if (!kgdb_putchar) + return; + (*kgdb_putchar)(c); +} +static int get_debug_char(void) +{ + if (!kgdb_getchar) + return -1; + return (*kgdb_getchar)(); +} + +/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */ +#define BUFMAX 1024 +#define NUMREGBYTES (MAXREG*4) +#define OUTBUFMAX (NUMREGBYTES*2+512) + +enum regs { + R0 = 0, R1, R2, R3, R4, R5, R6, R7, + R8, R9, R10, R11, R12, R13, R14, R15, + PC, PR, GBR, VBR, MACH, MACL, SR, + /* */ + MAXREG +}; + +static unsigned int registers[MAXREG]; +struct kgdb_regs trap_registers; + +char kgdb_in_gdb_mode; +char in_nmi; /* Set during NMI to prevent reentry */ +int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */ +int kgdb_enabled = 1; /* Default to enabled, cmdline can disable */ +int kgdb_halt=1; + +/* Exposed for user access */ +struct task_struct *kgdb_current; +unsigned int kgdb_g_imask; +int kgdb_trapa_val; +int kgdb_excode; +struct pt_regs *kgdb_tregs; + +/* Default values for SCI (can override via kernel args in setup.c) */ +#ifndef CONFIG_KGDB_DEFPORT +#define CONFIG_KGDB_DEFPORT 1 +#endif + +#ifndef CONFIG_KGDB_DEFBAUD +#define CONFIG_KGDB_DEFBAUD 115200 +#endif + +#if defined(CONFIG_KGDB_DEFPARITY_E) +#define CONFIG_KGDB_DEFPARITY 'E' +#elif defined(CONFIG_KGDB_DEFPARITY_O) +#define CONFIG_KGDB_DEFPARITY 'O' +#else /* CONFIG_KGDB_DEFPARITY_N */ +#define CONFIG_KGDB_DEFPARITY 'N' +#endif + +#ifdef CONFIG_KGDB_DEFBITS_7 +#define CONFIG_KGDB_DEFBITS '7' +#else /* CONFIG_KGDB_DEFBITS_8 */ +#define CONFIG_KGDB_DEFBITS '8' +#endif + +/* SCI/UART settings, used in kgdb_serial_setup() */ +int (*kgdb_serial_setup)(void) = kgdb_sci_setup; +int kgdb_portnum = CONFIG_KGDB_DEFPORT; +int kgdb_baud = CONFIG_KGDB_DEFBAUD; +char kgdb_parity = CONFIG_KGDB_DEFPARITY; +char kgdb_bits = CONFIG_KGDB_DEFBITS; +int kgdb_cflag; + +/* Jump buffer for setjmp/longjmp */ +static jmp_buf rem_com_env; + +/* TRA differs sh3/4 */ +#if defined(__sh3__) +#define TRA 0xffffffd0 +#elif defined(__SH4__) +#define TRA 0xff000020 +#endif + +/* Macros for single step instruction identification */ +#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900) +#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00) +#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \ + (((op) & 0x7f ) << 1)) +#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00) +#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00) +#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000) +#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \ + (((op) & 0x7ff) << 1)) +#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023) +#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8) +#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000) +#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \ + (((op) & 0x7ff) << 1)) +#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003) +#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf) +#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b) +#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf) +#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b) +#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf) +#define OPCODE_RTS(op) ((op) == 0xb) +#define OPCODE_RTE(op) ((op) == 0x2b) + +#define SR_T_BIT_MASK 0x1 +#define STEP_OPCODE 0xc320 +#define BIOS_CALL_TRAP 0x3f + +/* Exception codes as per SH-4 core manual */ +#define ADDRESS_ERROR_LOAD_VEC 7 +#define ADDRESS_ERROR_STORE_VEC 8 +#define TRAP_VEC 11 +#define INVALID_INSN_VEC 12 +#define INVALID_SLOT_VEC 13 +#define NMI_VEC 14 +#define USER_BREAK_VEC 15 +#define SERIAL_BREAK_VEC 58 + +/* Misc static */ +static int stepped_address; +static short stepped_opcode; +static const char hexchars[] = "0123456789abcdef"; +static char in_buffer[BUFMAX]; +static char out_buffer[OUTBUFMAX]; + +static void kgdb_to_gdb(const char *s); + +#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 + +/* Return addr as a real volatile address */ +static inline unsigned int ctrl_inl(const unsigned long addr) +{ + return *(volatile unsigned long *) addr; +} + +/* Correctly set *addr using volatile */ +static inline void ctrl_outl(const unsigned int b, unsigned long addr) +{ + *(volatile unsigned long *) addr = b; +} + +/* Get high hex bits */ +static char highhex(const int x) +{ + return hexchars[(x >> 4) & 0xf]; +} + +/* Get low hex bits */ +static char lowhex(const int x) +{ + return hexchars[x & 0xf]; +} + +/* Convert ch to hex */ +static int hex(const 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); +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + Returns a pointer to the last char put in buf (null) */ +static char *mem_to_hex(const char *mem, char *buf, const int count) +{ + int i; + int ch; + unsigned short s_val; + unsigned long l_val; + + /* Check for 16 or 32 */ + if (count == 2 && ((long) mem & 1) == 0) { + s_val = *(unsigned short *) mem; + mem = (char *) &s_val; + } else if (count == 4 && ((long) mem & 3) == 0) { + l_val = *(unsigned long *) mem; + mem = (char *) &l_val; + } + for (i = 0; i < count; i++) { + ch = *mem++; + *buf++ = highhex(ch); + *buf++ = lowhex(ch); + } + *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 *hex_to_mem(const char *buf, char *mem, const int count) +{ + int i; + unsigned char ch; + + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + *mem++ = ch; + } + return (mem); +} + +/* While finding valid hex chars, convert to an integer, then return it */ +static int hex_to_int(char **ptr, int *int_value) +{ + int num_chars = 0; + int hex_value; + + *int_value = 0; + + while (**ptr) { + hex_value = hex(**ptr); + if (hex_value >= 0) { + *int_value = (*int_value << 4) | hex_value; + num_chars++; + } else + break; + (*ptr)++; + } + return num_chars; +} + +/* Copy the binary array pointed to by buf into mem. Fix $, #, + and 0x7d escaped with 0x7d. Return a pointer to the character + after the last byte written. */ +static char *ebin_to_mem(const char *buf, char *mem, int count) +{ + for (; count > 0; count--, buf++) { + if (*buf == 0x7d) + *mem++ = *(++buf) ^ 0x20; + else + *mem++ = *buf; + } + return mem; +} + +/* Pack a hex byte */ +static char *pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#ifdef CONFIG_KGDB_THREAD + +/* Pack a thread ID */ +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; +} + +/* Convert an integer into our threadref */ +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 */ + +/* Scan for the start char '$', read the packet and check the checksum */ +static void get_packet(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 = get_debug_char()) != '$'); + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* Now, read until a # or end of buffer is found */ + while (count < (buflen - 1)) { + ch = get_debug_char(); + + if (ch == '#') + break; + + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + buffer[count] = 0; + + /* Continue to read checksum following # */ + if (ch == '#') { + xmitcsum = hex(get_debug_char()) << 4; + xmitcsum += hex(get_debug_char()); + + /* Checksum */ + if (checksum != xmitcsum) + put_debug_char('-'); /* Failed checksum */ + else { + /* Ack successful transfer */ + put_debug_char('+'); + + /* If a sequence char is present, reply + the sequence ID */ + if (buffer[2] == ':') { + put_debug_char(buffer[0]); + put_debug_char(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 the packet in the buffer with run-length encoding */ +static void put_packet(char *buffer) +{ + int checksum; + char *src; + int runlen; + int encode; + + do { + src = buffer; + put_debug_char('$'); + 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; + put_debug_char(*src); checksum += *src; + put_debug_char('*'); checksum += '*'; + put_debug_char(encode); checksum += encode; + src += runlen; + } else { + /* Otherwise just send the current char */ + put_debug_char(*src); checksum += *src; + src += 1; + } + } + + /* '#' Separator, put high and low components of checksum */ + put_debug_char('#'); + put_debug_char(highhex(checksum)); + put_debug_char(lowhex(checksum)); + } + while ((get_debug_char()) != '+'); /* While no ack */ +} + +/* A bus error has occurred - perform a longjmp to return execution and + allow handling of the error */ +static void kgdb_handle_bus_error(void) +{ + kgdb_longjmp(rem_com_env, 1); +} + +/* Translate SH-3/4 exception numbers to unix-like signal values */ +static int compute_signal(const int excep_code) +{ + int sigval; + + switch (excep_code) { + + case INVALID_INSN_VEC: + case INVALID_SLOT_VEC: + sigval = SIGILL; + break; + case ADDRESS_ERROR_LOAD_VEC: + case ADDRESS_ERROR_STORE_VEC: + sigval = SIGSEGV; + break; + + case SERIAL_BREAK_VEC: + case NMI_VEC: + sigval = SIGINT; + break; + + case USER_BREAK_VEC: + case TRAP_VEC: + sigval = SIGTRAP; + break; + + default: + sigval = SIGBUS; /* "software generated" */ + break; + } + + return (sigval); +} + +/* Make a local copy of the registers passed into the handler (bletch) */ +static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs, + int *gdb_regs) +{ + gdb_regs[R0] = regs->regs[R0]; + gdb_regs[R1] = regs->regs[R1]; + gdb_regs[R2] = regs->regs[R2]; + gdb_regs[R3] = regs->regs[R3]; + gdb_regs[R4] = regs->regs[R4]; + gdb_regs[R5] = regs->regs[R5]; + gdb_regs[R6] = regs->regs[R6]; + gdb_regs[R7] = regs->regs[R7]; + gdb_regs[R8] = regs->regs[R8]; + gdb_regs[R9] = regs->regs[R9]; + gdb_regs[R10] = regs->regs[R10]; + gdb_regs[R11] = regs->regs[R11]; + gdb_regs[R12] = regs->regs[R12]; + gdb_regs[R13] = regs->regs[R13]; + gdb_regs[R14] = regs->regs[R14]; + gdb_regs[R15] = regs->regs[R15]; + gdb_regs[PC] = regs->pc; + gdb_regs[PR] = regs->pr; + gdb_regs[GBR] = regs->gbr; + gdb_regs[MACH] = regs->mach; + gdb_regs[MACL] = regs->macl; + gdb_regs[SR] = regs->sr; + gdb_regs[VBR] = regs->vbr; +} + +/* Copy local gdb registers back to kgdb regs, for later copy to kernel */ +static void gdb_regs_to_kgdb_regs(const int *gdb_regs, + struct kgdb_regs *regs) +{ + regs->regs[R0] = gdb_regs[R0]; + regs->regs[R1] = gdb_regs[R1]; + regs->regs[R2] = gdb_regs[R2]; + regs->regs[R3] = gdb_regs[R3]; + regs->regs[R4] = gdb_regs[R4]; + regs->regs[R5] = gdb_regs[R5]; + regs->regs[R6] = gdb_regs[R6]; + regs->regs[R7] = gdb_regs[R7]; + regs->regs[R8] = gdb_regs[R8]; + regs->regs[R9] = gdb_regs[R9]; + regs->regs[R10] = gdb_regs[R10]; + regs->regs[R11] = gdb_regs[R11]; + regs->regs[R12] = gdb_regs[R12]; + regs->regs[R13] = gdb_regs[R13]; + regs->regs[R14] = gdb_regs[R14]; + regs->regs[R15] = gdb_regs[R15]; + regs->pc = gdb_regs[PC]; + regs->pr = gdb_regs[PR]; + regs->gbr = gdb_regs[GBR]; + regs->mach = gdb_regs[MACH]; + regs->macl = gdb_regs[MACL]; + regs->sr = gdb_regs[SR]; + regs->vbr = gdb_regs[VBR]; +} + +#ifdef CONFIG_KGDB_THREAD +/* Make a local copy of registers from the specified thread */ +asmlinkage void ret_from_fork(void); +static void thread_regs_to_gdb_regs(const struct task_struct *thread, + int *gdb_regs) +{ + int regno; + int *tregs; + + /* Initialize to zero */ + for (regno = 0; regno < MAXREG; regno++) + gdb_regs[regno] = 0; + + /* Just making sure... */ + if (thread == NULL) + return; + + /* A new fork has pt_regs on the stack from a fork() call */ + if (thread->thread.pc == (unsigned long)ret_from_fork) { + + int vbr_val; + struct pt_regs *kregs; + kregs = (struct pt_regs*)thread->thread.sp; + + gdb_regs[R0] = kregs->regs[R0]; + gdb_regs[R1] = kregs->regs[R1]; + gdb_regs[R2] = kregs->regs[R2]; + gdb_regs[R3] = kregs->regs[R3]; + gdb_regs[R4] = kregs->regs[R4]; + gdb_regs[R5] = kregs->regs[R5]; + gdb_regs[R6] = kregs->regs[R6]; + gdb_regs[R7] = kregs->regs[R7]; + gdb_regs[R8] = kregs->regs[R8]; + gdb_regs[R9] = kregs->regs[R9]; + gdb_regs[R10] = kregs->regs[R10]; + gdb_regs[R11] = kregs->regs[R11]; + gdb_regs[R12] = kregs->regs[R12]; + gdb_regs[R13] = kregs->regs[R13]; + gdb_regs[R14] = kregs->regs[R14]; + gdb_regs[R15] = kregs->regs[R15]; + gdb_regs[PC] = kregs->pc; + gdb_regs[PR] = kregs->pr; + gdb_regs[GBR] = kregs->gbr; + gdb_regs[MACH] = kregs->mach; + gdb_regs[MACL] = kregs->macl; + gdb_regs[SR] = kregs->sr; + + asm("stc vbr, %0":"=r"(vbr_val)); + gdb_regs[VBR] = vbr_val; + return; + } + + /* Otherwise, we have only some registers from switch_to() */ + tregs = (int *)thread->thread.sp; + gdb_regs[R15] = (int)tregs; + gdb_regs[R14] = *tregs++; + gdb_regs[R13] = *tregs++; + gdb_regs[R12] = *tregs++; + gdb_regs[R11] = *tregs++; + gdb_regs[R10] = *tregs++; + gdb_regs[R9] = *tregs++; + gdb_regs[R8] = *tregs++; + gdb_regs[PR] = *tregs++; + gdb_regs[GBR] = *tregs++; + gdb_regs[PC] = thread->thread.pc; +} +#endif /* CONFIG_KGDB_THREAD */ + +/* Calculate the new address for after a step */ +static short *get_step_address(void) +{ + short op = *(short *) trap_registers.pc; + long addr; + + /* BT */ + if (OPCODE_BT(op)) { + if (trap_registers.sr & SR_T_BIT_MASK) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 2; + } + + /* BTS */ + else if (OPCODE_BTS(op)) { + if (trap_registers.sr & SR_T_BIT_MASK) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 4; /* Not in delay slot */ + } + + /* BF */ + else if (OPCODE_BF(op)) { + if (!(trap_registers.sr & SR_T_BIT_MASK)) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 2; + } + + /* BFS */ + else if (OPCODE_BFS(op)) { + if (!(trap_registers.sr & SR_T_BIT_MASK)) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 4; /* Not in delay slot */ + } + + /* BRA */ + else if (OPCODE_BRA(op)) + addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op); + + /* BRAF */ + else if (OPCODE_BRAF(op)) + addr = trap_registers.pc + 4 + + trap_registers.regs[OPCODE_BRAF_REG(op)]; + + /* BSR */ + else if (OPCODE_BSR(op)) + addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op); + + /* BSRF */ + else if (OPCODE_BSRF(op)) + addr = trap_registers.pc + 4 + + trap_registers.regs[OPCODE_BSRF_REG(op)]; + + /* JMP */ + else if (OPCODE_JMP(op)) + addr = trap_registers.regs[OPCODE_JMP_REG(op)]; + + /* JSR */ + else if (OPCODE_JSR(op)) + addr = trap_registers.regs[OPCODE_JSR_REG(op)]; + + /* RTS */ + else if (OPCODE_RTS(op)) + addr = trap_registers.pr; + + /* RTE */ + else if (OPCODE_RTE(op)) + addr = trap_registers.regs[15]; + + /* Other */ + else + addr = trap_registers.pc + 2; + + kgdb_flush_icache_range(addr, addr + 2); + return (short *) addr; +} + +/* Set up a single-step. Replace the instruction immediately after the + current instruction (i.e. next in the expected flow of control) with a + trap instruction, so that returning will cause only a single instruction + to be executed. Note that this model is slightly broken for instructions + with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch + and the instruction in the delay slot will be executed. */ +static void do_single_step(void) +{ + unsigned short *addr = 0; + + /* Determine where the target instruction will send us to */ + addr = get_step_address(); + stepped_address = (int)addr; + + /* Replace it */ + stepped_opcode = *(short *)addr; + *addr = STEP_OPCODE; + + /* Flush and return */ + kgdb_flush_icache_range((long) addr, (long) addr + 2); + return; +} + +/* Undo a single step */ +static void undo_single_step(void) +{ + /* If we have stepped, put back the old instruction */ + /* Use stepped_address in case we stopped elsewhere */ + if (stepped_opcode != 0) { + *(short*)stepped_address = stepped_opcode; + kgdb_flush_icache_range(stepped_address, stepped_address + 2); + } + stepped_opcode = 0; +} + +/* Send a signal message */ +static void send_signal_msg(const int signum) +{ +#ifndef CONFIG_KGDB_THREAD + out_buffer[0] = 'S'; + out_buffer[1] = highhex(signum); + out_buffer[2] = lowhex(signum); + out_buffer[3] = 0; + put_packet(out_buffer); +#else /* CONFIG_KGDB_THREAD */ + int threadid; + threadref thref; + char *out = out_buffer; + const char *tstring = "thread"; + + *out++ = 'T'; + *out++ = highhex(signum); + *out++ = lowhex(signum); + + 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; + put_packet(out_buffer); +#endif /* CONFIG_KGDB_THREAD */ +} + +/* Reply that all was well */ +static void send_ok_msg(void) +{ + strcpy(out_buffer, "OK"); + put_packet(out_buffer); +} + +/* Reply that an error occurred */ +static void send_err_msg(void) +{ + strcpy(out_buffer, "E01"); + put_packet(out_buffer); +} + +/* Empty message indicates unrecognised command */ +static void send_empty_msg(void) +{ + put_packet(""); +} + +/* 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(rem_com_env) == 0) { + + kgdb_nofault = 1; + + /* Walk through, have m, */ + ptr = &in_buffer[1]; + if (hex_to_int(&ptr, &addr) && (*ptr++ == ',')) + if (hex_to_int(&ptr, &length)) { + ptr = 0; + if (length * 2 > OUTBUFMAX) + length = OUTBUFMAX / 2; + mem_to_hex((char *) addr, out_buffer, length); + } + if (ptr) + send_err_msg(); + else + put_packet(out_buffer); + } else + send_err_msg(); + + /* Restore bus error handler */ + kgdb_nofault = 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(rem_com_env) == 0) { + + kgdb_nofault = 1; + + /* Walk through, have M,: */ + ptr = &in_buffer[1]; + if (hex_to_int(&ptr, &addr) && (*ptr++ == ',')) + if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) { + if (binary) + ebin_to_mem(ptr, (char*)addr, length); + else + hex_to_mem(ptr, (char*)addr, length); + kgdb_flush_icache_range(addr, addr + length); + ptr = 0; + send_ok_msg(); + } + if (ptr) + send_err_msg(); + } else + send_err_msg(); + + /* Restore bus error handler */ + kgdb_nofault = 0; +} + +/* Continue message */ +static void continue_msg(void) +{ + /* Try to read optional parameter, PC unchanged if none */ + char *ptr = &in_buffer[1]; + int addr; + + if (hex_to_int(&ptr, &addr)) + trap_registers.pc = addr; +} + +/* Continue message with signal */ +static void continue_with_sig_msg(void) +{ + int signal; + char *ptr = &in_buffer[1]; + int addr; + + /* Report limitation */ + kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n"); + + /* Signal */ + hex_to_int(&ptr, &signal); + if (*ptr == ';') + ptr++; + + /* Optional address */ + if (hex_to_int(&ptr, &addr)) + trap_registers.pc = addr; +} + +/* Step message */ +static void step_msg(void) +{ + continue_msg(); + do_single_step(); +} + +/* Step message with signal */ +static void step_with_sig_msg(void) +{ + continue_with_sig_msg(); + do_single_step(); +} + +/* Send register contents */ +static void send_regs_msg(void) +{ +#ifdef CONFIG_KGDB_THREAD + if (!current_thread) + kgdb_regs_to_gdb_regs(&trap_registers, registers); + else + thread_regs_to_gdb_regs(current_thread, registers); +#else + kgdb_regs_to_gdb_regs(&trap_registers, registers); +#endif + + mem_to_hex((char *) registers, out_buffer, NUMREGBYTES); + put_packet(out_buffer); +} + +/* 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 + kgdb_regs_to_gdb_regs(&trap_registers, registers); + hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES); + gdb_regs_to_kgdb_regs(registers, &trap_registers); + send_ok_msg(); +#ifdef CONFIG_KGDB_THREAD + } else + send_err_msg(); +#endif +} + + +#ifdef CONFIG_KGDB_THREAD + +/* Set the status for a thread */ +void set_thread_msg(void) +{ + int threadid; + struct task_struct *thread = NULL; + char *ptr; + + switch (in_buffer[1]) { + + /* To select which thread for gG etc messages, i.e. supported */ + case 'g': + + ptr = &in_buffer[2]; + hex_to_int(&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; + } +} + +/* Is a thread alive? */ +static void thread_status_msg(void) +{ + char *ptr; + int threadid; + struct task_struct *thread = NULL; + + ptr = &in_buffer[1]; + hex_to_int(&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; + + out_buffer[0] = 'Q'; + out_buffer[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(out_buffer + 2, &thref); + out_buffer[2 + BUF_THREAD_ID_SIZE] = '\0'; + put_packet(out_buffer); +} + +/* Send thread info */ +static void thread_info_msg(void) +{ + struct task_struct *thread = NULL; + int threadid; + char *pos; + threadref thref; + + /* Start with 'm' */ + out_buffer[0] = 'm'; + pos = &out_buffer[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 */ + put_packet(out_buffer); + +} + +/* 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 = &in_buffer[17]; + hex_to_int(&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 = out_buffer; buffer[i]; i++) + ptr = pack_hex_byte(ptr, buffer[i]); + + if (thread->thread.pc == (unsigned long)ret_from_fork) { + strcpy(buffer, ""); + for (i = 0; buffer[i]; i++) + ptr = pack_hex_byte(ptr, buffer[i]); + } + + *ptr = '\0'; + put_packet(out_buffer); +} + +/* 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 = &in_buffer[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) + 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 */ + +/* The command loop, read and act on requests */ +static void kgdb_command_loop(const int excep_code, const int trapa_value) +{ + int sigval; + + if (excep_code == NMI_VEC) { +#ifndef CONFIG_KGDB_NMI + KGDB_PRINTK("Ignoring unexpected NMI?\n"); + return; +#else /* CONFIG_KGDB_NMI */ + if (!kgdb_enabled) { + kgdb_enabled = 1; + kgdb_init(); + } +#endif /* CONFIG_KGDB_NMI */ + } + + /* Ignore if we're disabled */ + if (!kgdb_enabled) + return; + +#ifdef CONFIG_KGDB_THREAD + /* Until GDB specifies a thread */ + current_thread = NULL; + trapped_thread = current; +#endif + + /* Enter GDB mode (e.g. after detach) */ + if (!kgdb_in_gdb_mode) { + /* Do serial setup, notify user, issue preemptive ack */ + (void)kgdb_serial_setup(); + KGDB_PRINTK("Waiting for GDB (on %s%d at %d baud)\n", + (kgdb_porttype ? kgdb_porttype->name : ""), + kgdb_portnum, kgdb_baud); + kgdb_in_gdb_mode = 1; + put_debug_char('+'); + } + + /* Reply to host that an exception has occurred */ + sigval = compute_signal(excep_code); + send_signal_msg(sigval); + + /* TRAP_VEC exception indicates a software trap inserted in place of + code by GDB so back up PC by one instruction, as this instruction + will later be replaced by its original one. Do NOT do this for + trap 0xff, since that indicates a compiled-in breakpoint which + will not be replaced (and we would retake the trap forever) */ + if ((excep_code == TRAP_VEC) && (trapa_value != (0xff << 2))) { + trap_registers.pc -= 2; + } + + /* Undo any stepping we may have done */ + undo_single_step(); + + while (1) { + + out_buffer[0] = 0; + get_packet(in_buffer, BUFMAX); + + /* Examine first char of buffer to see what we need to do */ + switch (in_buffer[0]) { + + case '?': /* Send which signal we've received */ + send_signal_msg(sigval); + break; + + case 'g': /* Return the values of the CPU registers */ + send_regs_msg(); + break; + + case 'G': /* Set the value 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); /* 0 = data in hex */ + break; + + case 'X': /* Write LLLL bytes esc bin address AA..AA */ + if (kgdb_bits == '8') + write_mem_msg(1); /* 1 = data in binary */ + else + send_empty_msg(); + break; + + case 'C': /* Continue, signum included, we ignore it */ + continue_with_sig_msg(); + return; + + case 'c': /* Continue at address AA..AA (optional) */ + continue_msg(); + return; + + case 'S': /* Step, signum included, we ignore it */ + step_with_sig_msg(); + return; + + case 's': /* Step one instruction from AA..AA */ + step_msg(); + return; + +#ifdef CONFIG_KGDB_THREAD + + case 'H': /* Task related */ + set_thread_msg(); + break; + + 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' with a kernel ? */ + break; + + case 'D': /* Detach from program, send reply OK */ + kgdb_in_gdb_mode = 0; + send_ok_msg(); + get_debug_char(); + return; + + default: + send_empty_msg(); + break; + } + } +} + +/* There has been an exception, most likely a breakpoint. */ +void kgdb_handle_exception(struct pt_regs *regs) +{ + int excep_code, vbr_val; + int count; + int trapa_value = ctrl_inl(TRA); + + /* Copy kernel regs (from stack) */ + for (count = 0; count < 16; count++) + trap_registers.regs[count] = regs->regs[count]; + trap_registers.pc = regs->pc; + trap_registers.pr = regs->pr; + trap_registers.sr = regs->sr; + trap_registers.gbr = regs->gbr; + trap_registers.mach = regs->mach; + trap_registers.macl = regs->macl; + + asm("stc vbr, %0":"=r"(vbr_val)); + trap_registers.vbr = vbr_val; + + /* Get excode for command loop call, user access */ + asm("stc r2_bank, %0":"=r"(excep_code)); + kgdb_excode = excep_code; + + /* Other interesting environment items for reference */ + asm("stc r6_bank, %0":"=r"(kgdb_g_imask)); + kgdb_current = current; + kgdb_trapa_val = trapa_value; + kgdb_tregs = regs; + + /* Act on the exception */ + kgdb_command_loop(excep_code >> 5, trapa_value); + + kgdb_current = NULL; + + /* Copy back the (maybe modified) registers */ + for (count = 0; count < 16; count++) + regs->regs[count] = trap_registers.regs[count]; + regs->pc = trap_registers.pc; + regs->pr = trap_registers.pr; + regs->sr = trap_registers.sr; + regs->gbr = trap_registers.gbr; + regs->mach = trap_registers.mach; + regs->macl = trap_registers.macl; + + vbr_val = trap_registers.vbr; + asm("ldc %0, vbr": :"r"(vbr_val)); + + return; +} + +/* Trigger a breakpoint by function */ +void breakpoint(void) +{ + if (!kgdb_enabled) { + kgdb_enabled = 1; + kgdb_init(); + } + BREAKPOINT(); +} + +/* Initialise the KGDB data structures and serial configuration */ +int kgdb_init(void) +{ + if (!kgdb_enabled) + return 1; + + in_nmi = 0; + kgdb_nofault = 0; + stepped_opcode = 0; + kgdb_in_gdb_mode = 0; + + /* Set up for serial comms */ + if (kgdb_serial_setup == NULL) { + KGDB_PRINTK("no serial init function!!\n"); + return -1; + } + + if (kgdb_serial_setup() != 0) { + KGDB_PRINTK("serial setup error\n"); + return -1; + } + + /* Init ptr to exception handler */ + kgdb_debug_hook = kgdb_handle_exception; + kgdb_bus_err_hook = kgdb_handle_bus_error; + + /* Enter kgdb now if requested, or just report init done */ + if (kgdb_halt) { + kgdb_in_gdb_mode = 1; + put_debug_char('+'); + breakpoint(); + } + else + { + KGDB_PRINTK("stub is initialized.\n"); + } + + return 0; +} + +/* Make function available for "user messages"; console will use it too. */ + +char gdbmsgbuf[BUFMAX]; +#define MAXOUT ((BUFMAX-2)/2) + +static void kgdb_msg_write(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + + /* 'O'utput */ + gdbmsgbuf[0] = 'O'; + + /* Fill and send buffers... */ + while (count > 0) { + bufptr = gdbmsgbuf + 1; + + /* Calculate how many this time */ + wcount = (count > MAXOUT) ? MAXOUT : count; + + /* Pack in hex chars */ + for (i = 0; i < wcount; i++) + bufptr = pack_hex_byte(bufptr, s[i]); + *bufptr = '\0'; + + /* Move up */ + s += wcount; + count -= wcount; + + /* Write packet */ + put_packet(gdbmsgbuf); + } +} + +static void kgdb_to_gdb(const char *s) +{ + kgdb_msg_write(s, strlen(s)); +} + +#ifdef CONFIG_SH_KGDB_CONSOLE +void kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + /* Bail if we're not talking to GDB */ + if (!kgdb_in_gdb_mode) + return; + + kgdb_msg_write(s, count); +} +#endif diff -ruN uClinux-2.4.org/arch/sh/kernel/Makefile uClinux-2.4.new/arch/sh/kernel/Makefile --- uClinux-2.4.org/arch/sh/kernel/Makefile 2004-10-24 15:16:34.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/Makefile 2004-10-14 19:11:45.000000000 +0530 @@ -27,7 +27,7 @@ obj-$(CONFIG_SH_RTC) += rtc.o obj-$(CONFIG_SH_DMA) += dma.o obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o - +obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o ifeq ($(CONFIG_PCI),y) ifeq ($(CONFIG_SH_DREAMCAST),y) diff -ruN uClinux-2.4.org/arch/sh/kernel/setup.c uClinux-2.4.new/arch/sh/kernel/setup.c --- uClinux-2.4.org/arch/sh/kernel/setup.c 2004-10-24 15:46:29.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/setup.c 2004-04-08 05:46:04.000000000 +0530 @@ -45,6 +45,10 @@ #endif #include +#ifdef CONFIG_SH_KGDB +#include +static int kgdb_parse_options(char *options); +#endif /* * Machine setup.. @@ -678,4 +682,92 @@ }; #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_SH_KGDB +/* + * Parse command-line kgdb options. By default KGDB is enabled, + * entered on error (or other action) using default serial info. + * The command-line option can include a serial port specification + * and an action to override default or configured behavior. + */ +struct kgdb_sermap kgdb_sci_sermap = +{ "ttySC", 5, kgdb_sci_setup, NULL }; + +struct kgdb_sermap *kgdb_serlist = &kgdb_sci_sermap; +struct kgdb_sermap *kgdb_porttype = &kgdb_sci_sermap; + +void kgdb_register_sermap(struct kgdb_sermap *map) +{ + struct kgdb_sermap *last; + + for (last = kgdb_serlist; last->next; last = last->next) + ; + last->next = map; + if (!map->namelen) { + map->namelen = strlen(map->name); + } +} + +static int __init kgdb_parse_options(char *options) +{ + char c; + int baud; + + /* Check for port spec (or use default) */ + + /* Determine port type and instance */ + if (!memcmp(options, "tty", 3)) { + struct kgdb_sermap *map = kgdb_serlist; + + while (map && memcmp(options, map->name, map->namelen)) + map = map->next; + + if (!map) { + KGDB_PRINTK("unknown port spec in %s\n", options); + return -1; + } + + kgdb_porttype = map; + kgdb_serial_setup = map->setup_fn; + kgdb_portnum = options[map->namelen] - '0'; + options += map->namelen + 1; + + options = (*options == ',') ? options+1 : options; + + /* Read optional parameters (baud/parity/bits) */ + baud = simple_strtoul(options, &options, 10); + if (baud != 0) { + kgdb_baud = baud; + + c = toupper(*options); + if (c == 'E' || c == 'O' || c == 'N') { + kgdb_parity = c; + options++; + } + + c = *options; + if (c == '7' || c == '8') { + kgdb_bits = c; + options++; + } + options = (*options == ',') ? options+1 : options; + } + } + + /* Check for action specification */ + if (!memcmp(options, "halt", 4)) { + kgdb_halt = 1; + options += 4; + } else if (!memcmp(options, "disabled", 8)) { + kgdb_enabled = 0; + options += 8; + } + + if (*options) { + KGDB_PRINTK("ignored unknown options: %s\n", options); + return 0; + } + return 1; +} +__setup("kgdb=", kgdb_parse_options); +#endif /* CONFIG_SH_KGDB */ diff -ruN uClinux-2.4.org/arch/sh/kernel/setup_se.c uClinux-2.4.new/arch/sh/kernel/setup_se.c --- uClinux-2.4.org/arch/sh/kernel/setup_se.c 2004-10-24 15:19:39.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/setup_se.c 2004-04-08 05:46:04.000000000 +0530 @@ -18,6 +18,9 @@ #include #include +#ifdef CONFIG_SH_KGDB +#include +#endif /* * Configure the Super I/O chip @@ -109,6 +112,11 @@ make_ipr_irq( 2, BCR_ILCRG, 0, 0x0f- 2); /* SLOTIRQ1 */ } +#ifdef CONFIG_SH_KGDB +static int kgdb_uart_setup(void); +static struct kgdb_sermap kgdb_uart_sermap = +{ "ttyS", 0, kgdb_uart_setup, NULL }; +#endif /* * Initialize the board @@ -117,6 +125,9 @@ { init_smsc(); /* XXX: RTC setting comes here */ +#ifdef CONFIG_SH_KGDB + kgdb_register_sermap(&kgdb_uart_sermap); +#endif } /********************************************************************* @@ -126,6 +137,7 @@ * arch/i386/lib/kgdb_serial.c). * *********************************************************************/ +#ifdef CONFIG_SH_KGDB #include #include #include @@ -148,11 +160,98 @@ { COM1_PORT }, { COM2_PORT }, }; - +struct uart_port *kgdb_uart_port; #define UART_IN(reg) inb_p(kgdb_uart_port->base + reg) #define UART_OUT(reg,v) outb_p((v), kgdb_uart_port->base + reg) /* Basic read/write functions for the UART */ #define UART_LSR_RXCERR (UART_LSR_BI | UART_LSR_FE | UART_LSR_PE) +static int kgdb_uart_getchar(void) +{ + int lsr; + int c = -1; + + while (c == -1) { + lsr = UART_IN(UART_LSR); + if (lsr & UART_LSR_DR) + c = UART_IN(UART_RX); + if ((lsr & UART_LSR_RXCERR)) + c = -1; + } + return c; +} + +static void kgdb_uart_putchar(int c) +{ + while ((UART_IN(UART_LSR) & UART_LSR_THRE) == 0) + ; + UART_OUT(UART_TX, c); +} + +/* + * Initialize UART to configured/requested values. + * (But we don't interrupts yet, or interact w/serial.c) + */ +static int kgdb_uart_setup(void) +{ + int port; + int lcr = 0; + int bdiv = 0; + + if (kgdb_portnum >= UART_NPORTS) { + KGDB_PRINTK("uart port %d invalid.\n", kgdb_portnum); + return -1; + } + + kgdb_uart_port = &uart_ports[kgdb_portnum]; + + /* Init sequence from gdb_hook_interrupt */ + UART_IN(UART_RX); + UART_OUT(UART_IER, 0); + + UART_IN(UART_RX); /* Serial driver comments say */ + UART_IN(UART_IIR); /* this clears interrupt regs */ + UART_IN(UART_MSR); + + /* Figure basic LCR values */ + switch (kgdb_bits) { + case '7': + lcr |= UART_LCR_WLEN7; + break; + default: case '8': + lcr |= UART_LCR_WLEN8; + break; + } + switch (kgdb_parity) { + case 'O': + lcr |= UART_LCR_PARITY; + break; + case 'E': + lcr |= (UART_LCR_PARITY | UART_LCR_EPAR); + break; + default: break; + } + + /* Figure the baud rate divisor */ + bdiv = (SB_BASE/kgdb_baud); + + /* Set the baud rate and LCR values */ + UART_OUT(UART_LCR, (lcr | UART_LCR_DLAB)); + UART_OUT(UART_DLL, (bdiv & 0xff)); + UART_OUT(UART_DLM, ((bdiv >> 8) & 0xff)); + UART_OUT(UART_LCR, lcr); + + /* Set the MCR */ + UART_OUT(UART_MCR, SB_MCR); + + /* Turn off FIFOs for now */ + UART_OUT(UART_FCR, 0); + + /* Setup complete: initialize function pointers */ + kgdb_getchar = kgdb_uart_getchar; + kgdb_putchar = kgdb_uart_putchar; + return 0; +} +#endif /* CONFIG_SH_KGDB */ diff -ruN uClinux-2.4.org/arch/sh/kernel/time.c uClinux-2.4.new/arch/sh/kernel/time.c --- uClinux-2.4.org/arch/sh/kernel/time.c 2004-10-24 15:20:36.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/time.c 2004-04-08 05:46:04.000000000 +0530 @@ -30,6 +30,9 @@ #include #include #include +#ifdef CONFIG_SH_KGDB +#include +#endif #if defined(CONFIG_SH_SECUREEDGE5410) #include @@ -748,4 +751,12 @@ ctrl_outb((ctrl_inb(TMU_TSTR) | TMU1_TSTR_INIT), TMU_TSTR); #endif +#if defined(CONFIG_SH_KGDB) + /* + * Set up kgdb as requested. We do it here because the serial + * init uses the timer vars we just set up for figuring baud. + */ + kgdb_init(); +#endif + } diff -ruN uClinux-2.4.org/arch/sh/kernel/traps.c uClinux-2.4.new/arch/sh/kernel/traps.c --- uClinux-2.4.org/arch/sh/kernel/traps.c 2004-10-24 15:49:43.000000000 +0530 +++ uClinux-2.4.new/arch/sh/kernel/traps.c 2004-04-08 05:46:04.000000000 +0530 @@ -32,6 +32,19 @@ #include #include +#ifdef CONFIG_SH_KGDB +#include +#define CHK_REMOTE_DEBUG(regs) \ +{ \ + if ((kgdb_debug_hook != (kgdb_debug_hook_t *) NULL) && (!user_mode(regs))) \ + { \ + (*kgdb_debug_hook)(regs); \ + } \ +} +#else +#define CHK_REMOTE_DEBUG(regs) +#endif + #define DO_ERROR(trapnr, signr, str, name, tsk) \ asmlinkage void do_##name(unsigned long r4, unsigned long r5, \ unsigned long r6, unsigned long r7, \ diff -ruN uClinux-2.4.org/arch/sh/Makefile uClinux-2.4.new/arch/sh/Makefile --- uClinux-2.4.org/arch/sh/Makefile 2004-10-24 15:33:02.000000000 +0530 +++ uClinux-2.4.new/arch/sh/Makefile 2004-04-08 05:46:04.000000000 +0530 @@ -28,6 +28,14 @@ AFLAGS += -Wa,-dsp endif +ifdef CONFIG_SH_KGDB +CFLAGS :=$(CFLAGS:-fomit-frame-pointer=) -g +AFLAGS += -g +ifdef CONFIG_KGDB_MORE +CFLAGS += $(shell echo $(CONFIG_KGDB_OPTIONS) | sed -e 's/"//g') +endif +endif + LD =$(CROSS_COMPILE)ld $(LDFLAGS) OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -R .stab -R .stabstr -S diff -ruN uClinux-2.4.org/arch/sh/mm/fault.c uClinux-2.4.new/arch/sh/mm/fault.c --- uClinux-2.4.org/arch/sh/mm/fault.c 2004-10-24 15:23:45.000000000 +0530 +++ uClinux-2.4.new/arch/sh/mm/fault.c 2004-04-08 05:46:04.000000000 +0530 @@ -27,6 +27,9 @@ #include #include +#if defined(CONFIG_SH_KGDB) +#include +#endif extern void die(const char *,struct pt_regs *,long); @@ -96,6 +99,11 @@ unsigned long page; unsigned long fixup; +#if defined(CONFIG_SH_KGDB) + if (kgdb_nofault && kgdb_bus_err_hook) + kgdb_bus_err_hook(); +#endif + tsk = current; mm = tsk->mm; @@ -244,6 +252,10 @@ pte_t *pte; pte_t entry; +#if defined(CONFIG_SH_KGDB) + if (kgdb_nofault && kgdb_bus_err_hook) + kgdb_bus_err_hook(); +#endif if (address >= P3SEG && address < P4SEG) dir = pgd_offset_k(address); else if (address >= TASK_SIZE) diff -ruN uClinux-2.4.org/drivers/char/sh-sci.c uClinux-2.4.new/drivers/char/sh-sci.c --- uClinux-2.4.org/drivers/char/sh-sci.c 2004-10-24 15:29:13.000000000 +0530 +++ uClinux-2.4.new/drivers/char/sh-sci.c 2004-10-14 19:21:34.000000000 +0530 @@ -35,7 +35,7 @@ #include #include #include -#if defined(CONFIG_SERIAL_CONSOLE) +#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SH_KGDB_CONSOLE) #include #endif @@ -65,6 +65,22 @@ #include "sh-sci.h" +#ifdef CONFIG_SH_KGDB +#include + +int kgdb_sci_setup(void); +static int kgdb_get_char(struct sci_port *port); +static void kgdb_put_char(struct sci_port *port, char c); +static void kgdb_handle_error(struct sci_port *port); +static struct sci_port *kgdb_sci_port; + +#ifdef CONFIG_SH_KGDB_CONSOLE +static struct console kgdbcons; +void __init kgdb_console_init(void); +#endif /* CONFIG_SH_KGDB_CONSOLE */ + +#endif /* CONFIG_SH_KGDB */ + #ifdef CONFIG_SERIAL_CONSOLE static struct console sercons; static struct sci_port* sercons_port=0; @@ -141,7 +157,7 @@ } #endif -#if defined(CONFIG_SH_STANDARD_BIOS) +#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) static void handle_error(struct sci_port *port) { /* Clear error flags */ @@ -183,7 +199,7 @@ return hexchars[x & 0xf]; } -#endif /* CONFIG_SH_STANDARD_BIOS */ +#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ /* * Send the packet in buffer. The host gets one chance to read it. @@ -196,7 +212,7 @@ int i; const unsigned char *p = buffer; -#if defined(CONFIG_SH_STANDARD_BIOS) +#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) int checksum; int usegdb=0; @@ -206,6 +222,9 @@ */ usegdb |= sh_bios_in_gdb_mode(); #endif +#ifdef CONFIG_SH_KGDB + usegdb |= (kgdb_in_gdb_mode && (port == kgdb_sci_port)); +#endif if (usegdb) { /* $#. */ @@ -230,7 +249,7 @@ put_char(port, lowhex(checksum)); } while (get_char(port) != '+'); } else -#endif /* CONFIG_SH_STANDARD_BIOS */ +#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ for (i=0; itermios->c_cflag = kgdbcons.cflag; + port->gs.baud = kgdb_baud; + sercons.cflag = 0; + sci_set_real_termios(port); + } +#elif CONFIG_SH_KGDB + /* Even for non-console, may defer to kgdb */ + if (port == kgdb_sci_port && kgdb_in_gdb_mode) { + tty->termios->c_cflag = kgdb_cflag; + port->gs.baud = kgdb_baud; + sercons.cflag = 0; + sci_set_real_termios(port); + } +#endif /* CONFIG_SH_KGDB */ + sci_enable_rx_interrupts(port); port->gs.session = current->session; @@ -1661,6 +1787,21 @@ break; } +#ifdef CONFIG_SH_KGDB + if (kgdb_in_gdb_mode && sercons_port == kgdb_sci_port) { + co->cflag = kgdb_cflag; + sercons_baud = kgdb_baud; + sercons_port->old_cflag = cflag; + } + else +#endif /* CONFIG_SH_KGDB */ + { + co->cflag = cflag; + sercons_baud = baud; + +#if defined(__H8300S__) + sci_enable(sercons_port,1); +#endif sci_set_termios_cflag(sercons_port, cflag, baud); sercons_port->old_cflag = cflag; } @@ -1697,3 +1838,111 @@ } #endif /* CONFIG_SERIAL_CONSOLE */ + +#ifdef CONFIG_SH_KGDB + +/* Initialise the KGDB serial port */ +int kgdb_sci_setup(void) +{ + int cflag = CREAD | HUPCL | CLOCAL; + + if ((kgdb_portnum < 0) || (kgdb_portnum >= SCI_NPORTS)) + return -1; + + kgdb_sci_port = &sci_ports[kgdb_portnum]; + + switch (kgdb_baud) { + case 115200: + cflag |= B115200; + break; + case 57600: + cflag |= B57600; + break; + case 38400: + cflag |= B38400; + break; + case 19200: + cflag |= B19200; + break; + case 9600: + default: + cflag |= B9600; + kgdb_baud = 9600; + break; + } + + switch (kgdb_bits) { + case '7': + cflag |= CS7; + break; + default: + case '8': + cflag |= CS8; + break; + } + + switch (kgdb_parity) { + case 'O': + cflag |= PARODD; + break; + case 'E': + cflag |= PARENB; + break; + } + + kgdb_cflag = cflag; + sci_set_termios_cflag(kgdb_sci_port, kgdb_cflag, kgdb_baud); + + /* Set up the interrupt for BREAK from GDB */ + /* Commented out for now since it may not be possible yet... + request_irq(kgdb_sci_port->irqs[0], kgdb_break_interrupt, + SA_INTERRUPT, "sci", kgdb_sci_port); + sci_enable_rx_interrupts(kgdb_sci_port); + */ + + /* Setup complete: initialize function pointers */ + kgdb_getchar = kgdb_sci_getchar; + kgdb_putchar = kgdb_sci_putchar; + + return 0; +} + +#ifdef CONFIG_SH_KGDB_CONSOLE + +/* Create a console device */ +static kdev_t kgdb_console_device(struct console *c) +{ + return MKDEV(SCI_MAJOR, SCI_MINOR_START + c->index); +} + +/* Set up the KGDB console */ +static int __init kgdb_console_setup(struct console *co, char *options) +{ + /* NB we ignore 'options' because we've already done the setup */ + co->cflag = kgdb_cflag; + + return 0; +} + +/* Register the KGDB console so we get messages (d'oh!) */ +void __init kgdb_console_init(void) +{ + register_console(&kgdbcons); +} + +/* The console structure for KGDB */ +//extern int serial_console_wait_key(struct console *); + +static struct console kgdbcons = { + name:"ttySC", + write:kgdb_console_write, + device:kgdb_console_device, + // wait_key:serial_console_wait_key, + setup:kgdb_console_setup, + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +#endif /* CONFIG_SH_KGDB_CONSOLE */ + +#endif /* CONFIG_SH_KGDB */ diff -ruN uClinux-2.4.org/include/asm-sh/kgdb.h uClinux-2.4.new/include/asm-sh/kgdb.h --- uClinux-2.4.org/include/asm-sh/kgdb.h 1970-01-01 05:30:00.000000000 +0530 +++ uClinux-2.4.new/include/asm-sh/kgdb.h 2004-10-15 12:01:17.000000000 +0530 @@ -0,0 +1,131 @@ +/* + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Based on original code by Glenn Engel, Jim Kingdon, + * David Grothe , Tigran Aivazian, and + * Amit S. Kale + * + * Super-H port based on sh-stub.c (Ben Lee and Steve Chamberlain) by + * Henry Bell + * + * Header file for low-level support for remote debug using GDB. + * + */ + +#ifndef __KGDB_H +#define __KGDB_H + +#include + +/* Same as pt_regs but has vbr in place of syscall_nr */ +struct kgdb_regs { + unsigned long regs[16]; + unsigned long pc; + unsigned long pr; + unsigned long sr; + unsigned long gbr; + unsigned long mach; + unsigned long macl; + unsigned long vbr; +}; + +/* State info */ +extern char kgdb_in_gdb_mode; +extern int kgdb_done_init; +extern int kgdb_enabled; +extern int kgdb_nofault; /* Ignore bus errors (in gdb mem access) */ +extern int kgdb_halt; /* Execute initial breakpoint at startup */ +extern char in_nmi; /* Debounce flag to prevent NMI reentry*/ + +/* SCI */ +extern int kgdb_portnum; +extern int kgdb_baud; +extern char kgdb_parity; +extern char kgdb_bits; +extern int kgdb_cflag; +extern int kgdb_sci_setup(void); + +/* Init and interface stuff */ +extern int kgdb_init(void); +extern int (*kgdb_serial_setup)(void); +extern int (*kgdb_getchar)(void); +extern void (*kgdb_putchar)(int); + +struct kgdb_sermap { + char *name; + int namelen; + int (*setup_fn)(void); + struct kgdb_sermap *next; +}; +extern void kgdb_register_sermap(struct kgdb_sermap *map); +extern struct kgdb_sermap *kgdb_porttype; + +/* Trap functions */ +typedef void (kgdb_debug_hook_t)(struct pt_regs *regs); +typedef void (kgdb_bus_error_hook_t)(void); +extern kgdb_debug_hook_t *kgdb_debug_hook; +extern kgdb_bus_error_hook_t *kgdb_bus_err_hook; + +extern void breakpoint(void); + +/* Console */ +struct console; +void kgdb_console_write(struct console *co, const char *s, unsigned count); +void kgdb_console_init(void); + +/* Prototypes for jmp fns */ +#define _JBLEN 9 +typedef int jmp_buf[_JBLEN]; +extern void kgdb_longjmp(jmp_buf __jmpb, int __retval); +extern int kgdb_setjmp(jmp_buf __jmpb); + +/* Variadic macro to print our own message to the console */ +#define KGDB_PRINTK(...) printk("KGDB: " __VA_ARGS__) + +/* Forced breakpoint */ +#define BREAKPOINT() do { \ + if (kgdb_enabled) { \ + asm volatile("trapa #0xff"); \ + } \ +} while (0) + +/* KGDB should be able to flush all kernel text space */ +#if defined(__sh3__) +#define kgdb_flush_icache_range(start, end) do { } while (0) +#elif defined(__SH4__) +#define kgdb_flush_icache_range(start, end) \ +{ \ + extern void __flush_purge_region(void *, int); \ + __flush_purge_region((void*)(start), (int)(end) - (int)(start)); \ + flush_icache_range((start), (end)); \ +} +#endif + + +/* Kernel assert macros */ +#ifdef CONFIG_KGDB_KERNEL_ASSERTS + +/* Predefined conditions */ +#define KA_VALID_ERRNO(errno) ((errno) > 0 && (errno) <= EMEDIUMTYPE) +#define KA_VALID_PTR_ERR(ptr) KA_VALID_ERRNO(-PTR_ERR(ptr)) +#define KA_VALID_KPTR(ptr) (!(ptr) || \ + ((void *)(ptr) >= (void *)PAGE_OFFSET && \ + (void *)(ptr) < ERR_PTR(-EMEDIUMTYPE))) +#define KA_VALID_PTRORERR(errptr) \ + (KA_VALID_KPTR(errptr) || KA_VALID_PTR_ERR(errptr)) +#define KA_HELD_GKL() (current->lock_depth >= 0) + +/* The actual assert */ +#define KGDB_ASSERT(condition, message) do { \ + if (!(condition) && (kgdb_enabled)) { \ + KGDB_PRINTK("Assertion failed at %s:%d: %s\n", \ + __FILE__, __LINE__, message);\ + BREAKPOINT(); \ + } \ +} while (0) +#else +#define KGDB_ASSERT(condition, message) +#endif + +#endif