diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index ff7a1f358a36..ff3c137deac3 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -27,6 +27,7 @@ config SUPERH32 select HAVE_FUNCTION_TRACER select HAVE_FTRACE_MCOUNT_RECORD select HAVE_DYNAMIC_FTRACE + select HAVE_ARCH_KGDB config SUPERH64 def_bool y if CPU_SH5 diff --git a/arch/sh/Kconfig.debug b/arch/sh/Kconfig.debug index 8f0c1fbd51a6..834a2d24342b 100644 --- a/arch/sh/Kconfig.debug +++ b/arch/sh/Kconfig.debug @@ -98,19 +98,6 @@ config IRQSTACKS for handling hard and soft interrupts. This can help avoid overflowing the process kernel stacks. -config SH_KGDB - bool "Include KGDB kernel debugger" - select FRAME_POINTER - select DEBUG_INFO - depends on CPU_SH3 || CPU_SH4 - help - Include in-kernel hooks for kgdb, the Linux kernel source level - debugger. See for more information. - Unless you are intending to debug the kernel, say N here. - -menu "KGDB configuration options" - depends on SH_KGDB - config MORE_COMPILE_OPTIONS bool "Add any additional compile options" help @@ -122,62 +109,6 @@ config COMPILE_OPTIONS string "Additional compile arguments" depends on MORE_COMPILE_OPTIONS -config KGDB_NMI - def_bool n - prompt "Enter KGDB on NMI" - -config SH_KGDB_CONSOLE - def_bool n - prompt "Console messages through GDB" - depends on !SERIAL_SH_SCI_CONSOLE && SERIAL_SH_SCI=y - select SERIAL_CORE_CONSOLE - -config KGDB_SYSRQ - def_bool y - prompt "Allow SysRq 'G' to enter KGDB" - depends on MAGIC_SYSRQ - -comment "Serial port setup" - -config KGDB_DEFPORT - int "Port number (ttySCn)" - default "1" - -config KGDB_DEFBAUD - int "Baud rate" - default "115200" - -choice - prompt "Parity" - depends on SH_KGDB - default KGDB_DEFPARITY_N - -config KGDB_DEFPARITY_N - bool "None" - -config KGDB_DEFPARITY_E - bool "Even" - -config KGDB_DEFPARITY_O - bool "Odd" - -endchoice - -choice - prompt "Data bits" - depends on SH_KGDB - default KGDB_DEFBITS_8 - -config KGDB_DEFBITS_8 - bool "8" - -config KGDB_DEFBITS_7 - bool "7" - -endchoice - -endmenu - if SUPERH64 config SH64_SR_WATCH diff --git a/arch/sh/include/asm/kgdb.h b/arch/sh/include/asm/kgdb.h index 24e42078f36f..72704ed725e5 100644 --- a/arch/sh/include/asm/kgdb.h +++ b/arch/sh/include/asm/kgdb.h @@ -1,21 +1,7 @@ -/* - * 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 +#ifndef __ASM_SH_KGDB_H +#define __ASM_SH_KGDB_H +#include #include /* Same as pt_regs but has vbr in place of syscall_nr */ @@ -30,40 +16,26 @@ struct kgdb_regs { unsigned long vbr; }; +enum regnames { + GDB_R0, GDB_R1, GDB_R2, GDB_R3, GDB_R4, GDB_R5, GDB_R6, GDB_R7, + GDB_R8, GDB_R9, GDB_R10, GDB_R11, GDB_R12, GDB_R13, GDB_R14, GDB_R15, + + GDB_PC, GDB_PR, GDB_SR, GDB_GBR, GDB_MACH, GDB_MACL, GDB_VBR, +}; + +#define NUMREGBYTES ((GDB_VBR + 1) * 4) + +static inline void arch_kgdb_breakpoint(void) +{ + __asm__ __volatile__ ("trapa #0x3c\n"); +} + /* State info */ -extern char kgdb_in_gdb_mode; -extern int kgdb_nofault; /* Ignore bus errors (in gdb mem access) */ 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; +#define BUFMAX 2048 -/* Init and interface stuff */ -extern int kgdb_init(void); -extern int (*kgdb_getchar)(void); -extern void (*kgdb_putchar)(int); +#define CACHE_FLUSH_IS_SAFE 1 +#define BREAK_INSTR_SIZE 2 -/* 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; - -/* Console */ -struct console; -void kgdb_console_write(struct console *co, const char *s, unsigned count); -extern int kgdb_console_setup(struct console *, char *); - -/* Prototypes for jmp fns */ -#define _JBLEN 9 -typedef int jmp_buf[_JBLEN]; -extern void longjmp(jmp_buf __jmpb, int __retval); -extern int setjmp(jmp_buf __jmpb); - -/* Forced breakpoint */ -#define breakpoint() __asm__ __volatile__("trapa #0x3c") - -#endif +#endif /* __ASM_SH_KGDB_H */ diff --git a/arch/sh/include/asm/system.h b/arch/sh/include/asm/system.h index 6160fe445161..c9ec6af8e745 100644 --- a/arch/sh/include/asm/system.h +++ b/arch/sh/include/asm/system.h @@ -175,6 +175,8 @@ asmlinkage void name##_trap_handler(unsigned int vec, struct pt_regs *regs) BUILD_TRAP_HANDLER(address_error); BUILD_TRAP_HANDLER(debug); BUILD_TRAP_HANDLER(bug); +BUILD_TRAP_HANDLER(breakpoint); +BUILD_TRAP_HANDLER(singlestep); BUILD_TRAP_HANDLER(fpu_error); BUILD_TRAP_HANDLER(fpu_state_restore); diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32 index 10a34c3ae647..df25304ad780 100644 --- a/arch/sh/kernel/Makefile_32 +++ b/arch/sh/kernel/Makefile_32 @@ -19,7 +19,7 @@ obj-$(CONFIG_VSYSCALL) += vsyscall/ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_CF_ENABLER) += cf-enabler.o obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o -obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o +obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MODULES) += sh_ksyms_32.o module.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index f112faa219c3..b4106d0c68ec 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S @@ -52,7 +52,7 @@ * syscall # * */ -#if defined(CONFIG_KGDB_NMI) +#if defined(CONFIG_KGDB) NMI_VEC = 0x1c0 ! Must catch early for debounce #endif @@ -307,7 +307,7 @@ skip_restore: 6: or k0, k2 ! Set the IMASK-bits ldc k2, ssr ! -#if defined(CONFIG_KGDB_NMI) +#if defined(CONFIG_KGDB) ! Clear in_nmi mov.l 6f, k0 mov #0, k1 @@ -320,7 +320,7 @@ skip_restore: .align 2 5: .long 0x00001000 ! DSP -#ifdef CONFIG_KGDB_NMI +#ifdef CONFIG_KGDB 6: .long in_nmi #endif 7: .long 0x30000000 @@ -377,7 +377,7 @@ tlb_miss: .balign 512,0,512 interrupt: mov.l 3f, k3 -#if defined(CONFIG_KGDB_NMI) +#if defined(CONFIG_KGDB) mov.l 2f, k2 ! Debounce (filter nested NMI) mov.l @k2, k0 @@ -394,7 +394,7 @@ interrupt: 5: .long NMI_VEC 6: .long in_nmi 0: -#endif /* defined(CONFIG_KGDB_NMI) */ +#endif /* defined(CONFIG_KGDB) */ bra handle_exception mov #-1, k2 ! interrupt exception marker diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S index dac429726899..e5a0de39a2db 100644 --- a/arch/sh/kernel/cpu/sh3/ex.S +++ b/arch/sh/kernel/cpu/sh3/ex.S @@ -26,7 +26,7 @@ #define fpu_error_trap_handler exception_error #endif -#if !defined(CONFIG_KGDB_NMI) +#if !defined(CONFIG_KGDB) #define kgdb_handle_exception exception_error #endif diff --git a/arch/sh/kernel/debugtraps.S b/arch/sh/kernel/debugtraps.S index 13b66746410a..591741383ee6 100644 --- a/arch/sh/kernel/debugtraps.S +++ b/arch/sh/kernel/debugtraps.S @@ -3,7 +3,7 @@ * * Debug trap jump tables for SuperH * - * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2006 - 2008 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -12,12 +12,13 @@ #include #include -#if !defined(CONFIG_SH_KGDB) -#define kgdb_handle_exception debug_trap_handler +#if !defined(CONFIG_KGDB) +#define breakpoint_trap_handler debug_trap_handler +#define singlestep_trap_handler debug_trap_handler #endif #if !defined(CONFIG_SH_STANDARD_BIOS) -#define sh_bios_handler debug_trap_handler +#define sh_bios_handler debug_trap_handler #endif .data @@ -35,7 +36,7 @@ ENTRY(debug_trap_table) .long debug_trap_handler /* 0x39 */ .long debug_trap_handler /* 0x3a */ .long debug_trap_handler /* 0x3b */ - .long kgdb_handle_exception /* 0x3c */ - .long debug_trap_handler /* 0x3d */ + .long breakpoint_trap_handler /* 0x3c */ + .long singlestep_trap_handler /* 0x3d */ .long bug_trap_handler /* 0x3e */ .long sh_bios_handler /* 0x3f */ diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S index efbb4268875e..d62359cfbbe2 100644 --- a/arch/sh/kernel/entry-common.S +++ b/arch/sh/kernel/entry-common.S @@ -308,15 +308,19 @@ ENTRY(system_call) mov.l 1f, r9 mov.l @r9, r8 ! Read from TRA (Trap Address) Register #endif + + mov #OFF_TRA, r10 + add r15, r10 + mov.l r8, @r10 ! set TRA value to tra + /* * Check the trap type */ mov #((0x20 << 2) - 1), r9 cmp/hi r9, r8 bt/s debug_trap ! it's a debug trap.. - mov #OFF_TRA, r9 - add r15, r9 - mov.l r8, @r9 ! set TRA value to tra + nop + #ifdef CONFIG_TRACE_IRQFLAGS mov.l 5f, r10 jsr @r10 diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c new file mode 100644 index 000000000000..7c747e7d71b8 --- /dev/null +++ b/arch/sh/kernel/kgdb.c @@ -0,0 +1,285 @@ +/* + * SuperH KGDB support + * + * Copyright (C) 2008 Paul Mundt + * + * Single stepping taken from the old stub by Henry Bell and Jeremy Siegel. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +char in_nmi = 0; /* Set during NMI to prevent re-entry */ + +/* 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 0xc33d + +/* Calculate the new address for after a step */ +static short *get_step_address(struct pt_regs *linux_regs) +{ + opcode_t op = __raw_readw(linux_regs->pc); + long addr; + + /* BT */ + if (OPCODE_BT(op)) { + if (linux_regs->sr & SR_T_BIT_MASK) + addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op); + else + addr = linux_regs->pc + 2; + } + + /* BTS */ + else if (OPCODE_BTS(op)) { + if (linux_regs->sr & SR_T_BIT_MASK) + addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op); + else + addr = linux_regs->pc + 4; /* Not in delay slot */ + } + + /* BF */ + else if (OPCODE_BF(op)) { + if (!(linux_regs->sr & SR_T_BIT_MASK)) + addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op); + else + addr = linux_regs->pc + 2; + } + + /* BFS */ + else if (OPCODE_BFS(op)) { + if (!(linux_regs->sr & SR_T_BIT_MASK)) + addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op); + else + addr = linux_regs->pc + 4; /* Not in delay slot */ + } + + /* BRA */ + else if (OPCODE_BRA(op)) + addr = linux_regs->pc + 4 + OPCODE_BRA_DISP(op); + + /* BRAF */ + else if (OPCODE_BRAF(op)) + addr = linux_regs->pc + 4 + + linux_regs->regs[OPCODE_BRAF_REG(op)]; + + /* BSR */ + else if (OPCODE_BSR(op)) + addr = linux_regs->pc + 4 + OPCODE_BSR_DISP(op); + + /* BSRF */ + else if (OPCODE_BSRF(op)) + addr = linux_regs->pc + 4 + + linux_regs->regs[OPCODE_BSRF_REG(op)]; + + /* JMP */ + else if (OPCODE_JMP(op)) + addr = linux_regs->regs[OPCODE_JMP_REG(op)]; + + /* JSR */ + else if (OPCODE_JSR(op)) + addr = linux_regs->regs[OPCODE_JSR_REG(op)]; + + /* RTS */ + else if (OPCODE_RTS(op)) + addr = linux_regs->pr; + + /* RTE */ + else if (OPCODE_RTE(op)) + addr = linux_regs->regs[15]; + + /* Other */ + else + addr = linux_regs->pc + instruction_size(op); + + flush_icache_range(addr, addr + instruction_size(op)); + return (short *)addr; +} + +/* + * 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 unsigned long stepped_address; +static opcode_t stepped_opcode; + +static void do_single_step(struct pt_regs *linux_regs) +{ + /* Determine where the target instruction will send us to */ + unsigned short *addr = get_step_address(linux_regs); + + stepped_address = (int)addr; + + /* Replace it */ + stepped_opcode = __raw_readw((long)addr); + *addr = STEP_OPCODE; + + /* Flush and return */ + flush_icache_range((long)addr, (long)addr + + instruction_size(stepped_opcode)); +} + +/* Undo a single step */ +static void undo_single_step(struct pt_regs *linux_regs) +{ + /* If we have stepped, put back the old instruction */ + /* Use stepped_address in case we stopped elsewhere */ + if (stepped_opcode != 0) { + __raw_writew(stepped_opcode, stepped_address); + flush_icache_range(stepped_address, stepped_address + 2); + } + + stepped_opcode = 0; +} + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int i; + + for (i = 0; i < 16; i++) + gdb_regs[GDB_R0 + i] = regs->regs[i]; + + gdb_regs[GDB_PC] = regs->pc; + gdb_regs[GDB_PR] = regs->pr; + gdb_regs[GDB_SR] = regs->sr; + gdb_regs[GDB_GBR] = regs->gbr; + gdb_regs[GDB_MACH] = regs->mach; + gdb_regs[GDB_MACL] = regs->macl; + + __asm__ __volatile__ ("stc vbr, %0" : "=r" (gdb_regs[GDB_VBR])); +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int i; + + for (i = 0; i < 16; i++) + regs->regs[GDB_R0 + i] = gdb_regs[GDB_R0 + i]; + + regs->pc = gdb_regs[GDB_PC]; + regs->pr = gdb_regs[GDB_PR]; + regs->sr = gdb_regs[GDB_SR]; + regs->gbr = gdb_regs[GDB_GBR]; + regs->mach = gdb_regs[GDB_MACH]; + regs->macl = gdb_regs[GDB_MACL]; + + __asm__ __volatile__ ("ldc %0, vbr" : : "r" (gdb_regs[GDB_VBR])); +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + gdb_regs[GDB_R15] = p->thread.sp; + gdb_regs[GDB_PC] = p->thread.pc; +} + +int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, + char *remcomInBuffer, char *remcomOutBuffer, + struct pt_regs *linux_regs) +{ + unsigned long addr; + char *ptr; + + /* Undo any stepping we may have done */ + undo_single_step(linux_regs); + + switch (remcomInBuffer[0]) { + case 'c': + case 's': + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + linux_regs->pc = addr; + case 'D': + case 'k': + atomic_set(&kgdb_cpu_doing_single_step, -1); + + if (remcomInBuffer[0] == 's') { + do_single_step(linux_regs); + kgdb_single_step = 1; + + atomic_set(&kgdb_cpu_doing_single_step, + raw_smp_processor_id()); + } + + return 0; + } + + /* this means that we do not want to exit from the handler: */ + return -1; +} + +/* + * The primary entry points for the kgdb debug trap table entries. + */ +BUILD_TRAP_HANDLER(singlestep) +{ + unsigned long flags; + TRAP_HANDLER_DECL; + + local_irq_save(flags); + regs->pc -= instruction_size(__raw_readw(regs->pc - 4)); + kgdb_handle_exception(vec >> 2, SIGTRAP, 0, regs); + local_irq_restore(flags); +} + + +BUILD_TRAP_HANDLER(breakpoint) +{ + unsigned long flags; + TRAP_HANDLER_DECL; + + local_irq_save(flags); + kgdb_handle_exception(vec >> 2, SIGTRAP, 0, regs); + local_irq_restore(flags); +} + +int kgdb_arch_init(void) +{ + return 0; +} + +void kgdb_arch_exit(void) +{ +} + +struct kgdb_arch arch_kgdb_ops = { + /* Breakpoint instruction: trapa #0x3c */ +#ifdef CONFIG_CPU_LITTLE_ENDIAN + .gdb_bpt_instr = { 0x3c, 0xc3 }, +#else + .gdb_bpt_instr = { 0xc3, 0x3c }, +#endif +}; diff --git a/arch/sh/kernel/kgdb_jmp.S b/arch/sh/kernel/kgdb_jmp.S deleted file mode 100644 index 339bb1d7ff0b..000000000000 --- a/arch/sh/kernel/kgdb_jmp.S +++ /dev/null @@ -1,33 +0,0 @@ -#include - -ENTRY(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(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 --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c deleted file mode 100644 index bf8ac4c71640..000000000000 --- a/arch/sh/kernel/kgdb_stub.c +++ /dev/null @@ -1,1052 +0,0 @@ -/* - * 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 repetitions 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 -#include -#include -#include -#include -#include - -/* Function pointers for linkage */ -kgdb_debug_hook_t *kgdb_debug_hook; -kgdb_bus_error_hook_t *kgdb_bus_err_hook; - -int (*kgdb_getchar)(void); -EXPORT_SYMBOL_GPL(kgdb_getchar); -void (*kgdb_putchar)(int); -EXPORT_SYMBOL_GPL(kgdb_putchar); - -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 { - 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) */ - -/* 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_console_setup() */ -int kgdb_portnum = CONFIG_KGDB_DEFPORT; -EXPORT_SYMBOL_GPL(kgdb_portnum); -int kgdb_baud = CONFIG_KGDB_DEFBAUD; -EXPORT_SYMBOL_GPL(kgdb_baud); -char kgdb_parity = CONFIG_KGDB_DEFPARITY; -EXPORT_SYMBOL_GPL(kgdb_parity); -char kgdb_bits = CONFIG_KGDB_DEFBITS; -EXPORT_SYMBOL_GPL(kgdb_bits); - -/* Jump buffer for setjmp/longjmp */ -static jmp_buf rem_com_env; - -/* TRA differs sh3/4 */ -#if defined(CONFIG_CPU_SH3) -#define TRA 0xffffffd0 -#elif defined(CONFIG_CPU_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 char in_buffer[BUFMAX]; -static char out_buffer[OUTBUFMAX]; - -static void kgdb_to_gdb(const char *s); - -/* 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 = pack_hex_byte(buf, 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; -} - -/* 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(hex_asc_hi(checksum)); - put_debug_char(hex_asc_lo(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) -{ - 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]; -} - -/* 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; - - 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 */ - flush_icache_range((long) addr, (long) addr + 2); -} - -/* 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; - flush_icache_range(stepped_address, stepped_address + 2); - } - stepped_opcode = 0; -} - -/* Send a signal message */ -static void send_signal_msg(const int signum) -{ - out_buffer[0] = 'S'; - out_buffer[1] = hex_asc_hi(signum); - out_buffer[2] = hex_asc_lo(signum); - out_buffer[3] = 0; - put_packet(out_buffer); -} - -/* 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 (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 (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); - 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) -{ - kgdb_regs_to_gdb_regs(&trap_registers, registers); - 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) -{ - 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_SH_KGDB_CONSOLE -/* - * Bring up the ports.. - */ -static int __init kgdb_serial_setup(void) -{ - struct console dummy; - return kgdb_console_setup(&dummy, 0); -} -#else -#define kgdb_serial_setup() 0 -#endif - -/* The command loop, read and act on requests */ -static void kgdb_command_loop(const int excep_code, const int trapa_value) -{ - int sigval; - - /* Enter GDB mode (e.g. after detach) */ - if (!kgdb_in_gdb_mode) { - /* Do serial setup, notify user, issue preemptive ack */ - printk(KERN_NOTICE "KGDB: Waiting for GDB\n"); - 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 != (0x3c << 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; - - 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. */ -static void 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)); - - /* Act on the exception */ - kgdb_command_loop(excep_code, trapa_value); - - /* 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)); -} - -asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) -{ - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); - handle_exception(regs); -} - -/* Initialise the KGDB data structures and serial configuration */ -int __init kgdb_init(void) -{ - in_nmi = 0; - kgdb_nofault = 0; - stepped_opcode = 0; - kgdb_in_gdb_mode = 0; - - if (kgdb_serial_setup() != 0) { - printk(KERN_NOTICE "KGDB: serial setup error\n"); - return -1; - } - - /* Init ptr to exception handler */ - kgdb_debug_hook = handle_exception; - kgdb_bus_err_hook = kgdb_handle_bus_error; - - /* Enter kgdb now if requested, or just report init done */ - printk(KERN_NOTICE "KGDB: 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 - -#ifdef CONFIG_KGDB_SYSRQ -static void sysrq_handle_gdb(int key, struct tty_struct *tty) -{ - printk("Entering GDB stub\n"); - breakpoint(); -} - -static struct sysrq_key_op sysrq_gdb_op = { - .handler = sysrq_handle_gdb, - .help_msg = "Gdb", - .action_msg = "GDB", -}; - -static int gdb_register_sysrq(void) -{ - printk("Registering GDB sysrq handler\n"); - register_sysrq_key('g', &sysrq_gdb_op); - return 0; -} -module_init(gdb_register_sysrq); -#endif diff --git a/arch/sh/kernel/time_32.c b/arch/sh/kernel/time_32.c index 23ca711c27d2..1336f275326c 100644 --- a/arch/sh/kernel/time_32.c +++ b/arch/sh/kernel/time_32.c @@ -277,11 +277,4 @@ void __init time_init(void) ((sh_hpt_frequency + 500) / 1000) / 1000, ((sh_hpt_frequency + 500) / 1000) % 1000); -#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 --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 6094fc13beea..88807a2aacc3 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -28,17 +28,6 @@ #include #include -#ifdef CONFIG_SH_KGDB -#include -#define CHK_REMOTE_DEBUG(regs) \ -{ \ - if (kgdb_debug_hook && !user_mode(regs))\ - (*kgdb_debug_hook)(regs); \ -} -#else -#define CHK_REMOTE_DEBUG(regs) -#endif - #ifdef CONFIG_CPU_SH2 # define TRAP_RESERVED_INST 4 # define TRAP_ILLEGAL_SLOT_INST 6 @@ -94,7 +83,6 @@ void die(const char * str, struct pt_regs * regs, long err) printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); - CHK_REMOTE_DEBUG(regs); print_modules(); show_regs(regs); @@ -683,7 +671,6 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, error_code = lookup_exception_vector(); local_irq_enable(); - CHK_REMOTE_DEBUG(regs); force_sig(SIGILL, tsk); die_if_no_fixup("reserved instruction", regs, error_code); } @@ -761,7 +748,6 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, inst = lookup_exception_vector(); local_irq_enable(); - CHK_REMOTE_DEBUG(regs); force_sig(SIGILL, tsk); die_if_no_fixup("illegal slot instruction", regs, inst); } diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index e58726892b5f..31a33ebdef6f 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -20,7 +20,6 @@ #include #include #include -#include /* * This routine handles page faults. It determines the address, @@ -282,11 +281,6 @@ asmlinkage int __kprobes __do_page_fault(struct pt_regs *regs, if (notify_page_fault(regs, lookup_exception_vector())) goto out; -#ifdef CONFIG_SH_KGDB - if (kgdb_nofault && kgdb_bus_err_hook) - kgdb_bus_err_hook(); -#endif - ret = 1; /*