Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S261737AbUJ2TO1 (ORCPT ); Fri, 29 Oct 2004 15:14:27 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S261730AbUJ2TO1 (ORCPT ); Fri, 29 Oct 2004 15:14:27 -0400 Received: from fed1rmmtao07.cox.net ([68.230.241.32]:28582 "EHLO fed1rmmtao07.cox.net") by vger.kernel.org with ESMTP id S263492AbUJ2SeB (ORCPT ); Fri, 29 Oct 2004 14:34:01 -0400 Subject: [patch 5/8] KGDB support for MIPS To: akpm@osdl.org Cc: linux-kernel@vger.kernel.org, trini@kernel.crashing.org, ralf@linux-mips.org, mlachwani@mvista.com From: Tom Rini Message-Id: <5.29102004.trini@kernel.crashing.org> In-Reply-To: <4.29102004.trini@kernel.crashing.org> References: <4.29102004.trini@kernel.crashing.org> <1.29102004.trini@kernel.crashing.org> Date: Fri, 29 Oct 2004 11:33:54 -0700 Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 70329 Lines: 2671 Cc: Ralf Baechle , Manish Lachwani This adds KGDB support for MIPS and was done by Manish Lachwani. This has been tested on MIPS Malta and Sibyte 1250 SWARM (32 & 64). --- linux-2.6.10-rc1-trini/arch/mips/Kconfig.debug | 19 linux-2.6.10-rc1-trini/arch/mips/kernel/Makefile | 2 linux-2.6.10-rc1-trini/arch/mips/kernel/irq.c | 3 linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb.c | 292 ++ linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb_handler.S | 58 linux-2.6.10-rc1-trini/arch/mips/kernel/traps.c | 29 linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/Makefile | 1 linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/init.c | 64 linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_int.c | 25 linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_setup.c | 8 linux-2.6.10-rc1-trini/arch/mips/sibyte/cfe/setup.c | 14 linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/Makefile | 1 linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/irq.c | 72 linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/kgdb_sibyte.c | 158 + linux-2.6.10-rc1-trini/arch/mips/sibyte/swarm/Makefile | 2 linux-2.6.10-rc1-trini/include/asm-mips/kdebug.h | 47 linux-2.6.10-rc1-trini/include/asm-mips/kgdb.h | 20 linux-2.6.10-rc1-trini/lib/Kconfig.debug | 9 linux-2.6.10-rc1/arch/mips/kernel/gdb-low.S | 370 --- linux-2.6.10-rc1/arch/mips/kernel/gdb-stub.c | 1089 ---------- linux-2.6.10-rc1/arch/mips/sibyte/swarm/dbg_io.c | 76 21 files changed, 633 insertions(+), 1726 deletions(-) diff -puN arch/mips/Kconfig.debug~mips-lite arch/mips/Kconfig.debug --- linux-2.6.10-rc1/arch/mips/Kconfig.debug~mips-lite 2004-10-29 11:26:45.172281931 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/Kconfig.debug 2004-10-29 11:26:45.204274419 -0700 @@ -27,25 +27,6 @@ config DEBUG_STACK_USAGE This option will slow down process creation somewhat. -config KGDB - bool "Remote GDB kernel debugging" - depends on DEBUG_KERNEL - select DEBUG_INFO - help - If you say Y here, it will be possible to remotely debug the MIPS - kernel using gdb. This enlarges your kernel image disk size by - several megabytes and requires a machine with more than 16 MB, - better 32 MB RAM to avoid excessive linking time. This is only - useful for kernel hackers. If unsure, say N. - -config GDB_CONSOLE - bool "Console output to GDB" - depends on KGDB - help - If you are using GDB for remote debugging over a serial port and - would like kernel messages to be formatted into GDB $O packets so - that GDB prints them as program output, say 'Y'. - config SB1XXX_CORELIS bool "Corelis Debugger" depends on SIBYTE_SB1xxx_SOC diff -puN arch/mips/kernel/Makefile~mips-lite arch/mips/kernel/Makefile --- linux-2.6.10-rc1/arch/mips/kernel/Makefile~mips-lite 2004-10-29 11:26:45.173281696 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/kernel/Makefile 2004-10-29 11:26:45.204274419 -0700 @@ -48,7 +48,7 @@ obj-$(CONFIG_MIPS32_COMPAT) += ioctl32.o obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o ptrace32.o -obj-$(CONFIG_KGDB) += gdb-low.o gdb-stub.o +obj-$(CONFIG_KGDB) += kgdb_handler.o kgdb.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_MIPS64) += cpu-bugs64.o diff -L arch/mips/kernel/gdb-low.S -puN arch/mips/kernel/gdb-low.S~mips-lite /dev/null --- linux-2.6.10-rc1/arch/mips/kernel/gdb-low.S +++ /dev/null 2004-10-25 00:35:20.587727328 -0700 @@ -1,370 +0,0 @@ -/* - * gdb-low.S contains the low-level trap handler for the GDB stub. - * - * Copyright (C) 1995 Andreas Busse - */ -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_MIPS32 -#define DMFC0 mfc0 -#define DMTC0 mtc0 -#define LDC1 lwc1 -#define SDC1 lwc1 -#endif -#ifdef CONFIG_MIPS64 -#define DMFC0 dmfc0 -#define DMTC0 dmtc0 -#define LDC1 ldc1 -#define SDC1 ldc1 -#endif - -/* - * [jsun] We reserves about 2x GDB_FR_SIZE in stack. The lower (addressed) - * part is used to store registers and passed to exception handler. - * The upper part is reserved for "call func" feature where gdb client - * saves some of the regs, setups call frame and passes args. - * - * A trace shows about 200 bytes are used to store about half of all regs. - * The rest should be big enough for frame setup and passing args. - */ - -/* - * The low level trap handler - */ - .align 5 - NESTED(trap_low, GDB_FR_SIZE, sp) - .set noat - .set noreorder - - mfc0 k0, CP0_STATUS - sll k0, 3 /* extract cu0 bit */ - bltz k0, 1f - move k1, sp - - /* - * Called from user mode, go somewhere else. - */ - lui k1, %hi(saved_vectors) - mfc0 k0, CP0_CAUSE - andi k0, k0, 0x7c - add k1, k1, k0 - lw k0, %lo(saved_vectors)(k1) - jr k0 - nop -1: - move k0, sp - subu sp, k1, GDB_FR_SIZE*2 # see comment above - LONG_S k0, GDB_FR_REG29(sp) - LONG_S $2, GDB_FR_REG2(sp) - -/* - * First save the CP0 and special registers - */ - - mfc0 v0, CP0_STATUS - LONG_S v0, GDB_FR_STATUS(sp) - mfc0 v0, CP0_CAUSE - LONG_S v0, GDB_FR_CAUSE(sp) - DMFC0 v0, CP0_EPC - LONG_S v0, GDB_FR_EPC(sp) - DMFC0 v0, CP0_BADVADDR - LONG_S v0, GDB_FR_BADVADDR(sp) - mfhi v0 - LONG_S v0, GDB_FR_HI(sp) - mflo v0 - LONG_S v0, GDB_FR_LO(sp) - -/* - * Now the integer registers - */ - - LONG_S zero, GDB_FR_REG0(sp) /* I know... */ - LONG_S $1, GDB_FR_REG1(sp) - /* v0 already saved */ - LONG_S $3, GDB_FR_REG3(sp) - LONG_S $4, GDB_FR_REG4(sp) - LONG_S $5, GDB_FR_REG5(sp) - LONG_S $6, GDB_FR_REG6(sp) - LONG_S $7, GDB_FR_REG7(sp) - LONG_S $8, GDB_FR_REG8(sp) - LONG_S $9, GDB_FR_REG9(sp) - LONG_S $10, GDB_FR_REG10(sp) - LONG_S $11, GDB_FR_REG11(sp) - LONG_S $12, GDB_FR_REG12(sp) - LONG_S $13, GDB_FR_REG13(sp) - LONG_S $14, GDB_FR_REG14(sp) - LONG_S $15, GDB_FR_REG15(sp) - LONG_S $16, GDB_FR_REG16(sp) - LONG_S $17, GDB_FR_REG17(sp) - LONG_S $18, GDB_FR_REG18(sp) - LONG_S $19, GDB_FR_REG19(sp) - LONG_S $20, GDB_FR_REG20(sp) - LONG_S $21, GDB_FR_REG21(sp) - LONG_S $22, GDB_FR_REG22(sp) - LONG_S $23, GDB_FR_REG23(sp) - LONG_S $24, GDB_FR_REG24(sp) - LONG_S $25, GDB_FR_REG25(sp) - LONG_S $26, GDB_FR_REG26(sp) - LONG_S $27, GDB_FR_REG27(sp) - LONG_S $28, GDB_FR_REG28(sp) - /* sp already saved */ - LONG_S $30, GDB_FR_REG30(sp) - LONG_S $31, GDB_FR_REG31(sp) - - CLI /* disable interrupts */ - -/* - * Followed by the floating point registers - */ - mfc0 v0, CP0_STATUS /* FPU enabled? */ - srl v0, v0, 16 - andi v0, v0, (ST0_CU1 >> 16) - - beqz v0,2f /* disabled, skip */ - nop - - SDC1 $0, GDB_FR_FPR0(sp) - SDC1 $1, GDB_FR_FPR1(sp) - SDC1 $2, GDB_FR_FPR2(sp) - SDC1 $3, GDB_FR_FPR3(sp) - SDC1 $4, GDB_FR_FPR4(sp) - SDC1 $5, GDB_FR_FPR5(sp) - SDC1 $6, GDB_FR_FPR6(sp) - SDC1 $7, GDB_FR_FPR7(sp) - SDC1 $8, GDB_FR_FPR8(sp) - SDC1 $9, GDB_FR_FPR9(sp) - SDC1 $10, GDB_FR_FPR10(sp) - SDC1 $11, GDB_FR_FPR11(sp) - SDC1 $12, GDB_FR_FPR12(sp) - SDC1 $13, GDB_FR_FPR13(sp) - SDC1 $14, GDB_FR_FPR14(sp) - SDC1 $15, GDB_FR_FPR15(sp) - SDC1 $16, GDB_FR_FPR16(sp) - SDC1 $17, GDB_FR_FPR17(sp) - SDC1 $18, GDB_FR_FPR18(sp) - SDC1 $19, GDB_FR_FPR19(sp) - SDC1 $20, GDB_FR_FPR20(sp) - SDC1 $21, GDB_FR_FPR21(sp) - SDC1 $22, GDB_FR_FPR22(sp) - SDC1 $23, GDB_FR_FPR23(sp) - SDC1 $24, GDB_FR_FPR24(sp) - SDC1 $25, GDB_FR_FPR25(sp) - SDC1 $26, GDB_FR_FPR26(sp) - SDC1 $27, GDB_FR_FPR27(sp) - SDC1 $28, GDB_FR_FPR28(sp) - SDC1 $29, GDB_FR_FPR29(sp) - SDC1 $30, GDB_FR_FPR30(sp) - SDC1 $31, GDB_FR_FPR31(sp) - -/* - * FPU control registers - */ - - cfc1 v0, CP1_STATUS - LONG_S v0, GDB_FR_FSR(sp) - cfc1 v0, CP1_REVISION - LONG_S v0, GDB_FR_FIR(sp) - -/* - * Current stack frame ptr - */ - -2: - LONG_S sp, GDB_FR_FRP(sp) - -/* - * CP0 registers (R4000/R4400 unused registers skipped) - */ - - mfc0 v0, CP0_INDEX - LONG_S v0, GDB_FR_CP0_INDEX(sp) - mfc0 v0, CP0_RANDOM - LONG_S v0, GDB_FR_CP0_RANDOM(sp) - DMFC0 v0, CP0_ENTRYLO0 - LONG_S v0, GDB_FR_CP0_ENTRYLO0(sp) - DMFC0 v0, CP0_ENTRYLO1 - LONG_S v0, GDB_FR_CP0_ENTRYLO1(sp) - DMFC0 v0, CP0_CONTEXT - LONG_S v0, GDB_FR_CP0_CONTEXT(sp) - mfc0 v0, CP0_PAGEMASK - LONG_S v0, GDB_FR_CP0_PAGEMASK(sp) - mfc0 v0, CP0_WIRED - LONG_S v0, GDB_FR_CP0_WIRED(sp) - DMFC0 v0, CP0_ENTRYHI - LONG_S v0, GDB_FR_CP0_ENTRYHI(sp) - mfc0 v0, CP0_PRID - LONG_S v0, GDB_FR_CP0_PRID(sp) - - .set at - -/* - * Continue with the higher level handler - */ - - move a0,sp - - jal handle_exception - nop - -/* - * Restore all writable registers, in reverse order - */ - - .set noat - - LONG_L v0, GDB_FR_CP0_ENTRYHI(sp) - LONG_L v1, GDB_FR_CP0_WIRED(sp) - DMTC0 v0, CP0_ENTRYHI - mtc0 v1, CP0_WIRED - LONG_L v0, GDB_FR_CP0_PAGEMASK(sp) - LONG_L v1, GDB_FR_CP0_ENTRYLO1(sp) - mtc0 v0, CP0_PAGEMASK - DMTC0 v1, CP0_ENTRYLO1 - LONG_L v0, GDB_FR_CP0_ENTRYLO0(sp) - LONG_L v1, GDB_FR_CP0_INDEX(sp) - DMTC0 v0, CP0_ENTRYLO0 - LONG_L v0, GDB_FR_CP0_CONTEXT(sp) - mtc0 v1, CP0_INDEX - DMTC0 v0, CP0_CONTEXT - - -/* - * Next, the floating point registers - */ - mfc0 v0, CP0_STATUS /* check if the FPU is enabled */ - srl v0, v0, 16 - andi v0, v0, (ST0_CU1 >> 16) - - beqz v0, 3f /* disabled, skip */ - nop - - LDC1 $31, GDB_FR_FPR31(sp) - LDC1 $30, GDB_FR_FPR30(sp) - LDC1 $29, GDB_FR_FPR29(sp) - LDC1 $28, GDB_FR_FPR28(sp) - LDC1 $27, GDB_FR_FPR27(sp) - LDC1 $26, GDB_FR_FPR26(sp) - LDC1 $25, GDB_FR_FPR25(sp) - LDC1 $24, GDB_FR_FPR24(sp) - LDC1 $23, GDB_FR_FPR23(sp) - LDC1 $22, GDB_FR_FPR22(sp) - LDC1 $21, GDB_FR_FPR21(sp) - LDC1 $20, GDB_FR_FPR20(sp) - LDC1 $19, GDB_FR_FPR19(sp) - LDC1 $18, GDB_FR_FPR18(sp) - LDC1 $17, GDB_FR_FPR17(sp) - LDC1 $16, GDB_FR_FPR16(sp) - LDC1 $15, GDB_FR_FPR15(sp) - LDC1 $14, GDB_FR_FPR14(sp) - LDC1 $13, GDB_FR_FPR13(sp) - LDC1 $12, GDB_FR_FPR12(sp) - LDC1 $11, GDB_FR_FPR11(sp) - LDC1 $10, GDB_FR_FPR10(sp) - LDC1 $9, GDB_FR_FPR9(sp) - LDC1 $8, GDB_FR_FPR8(sp) - LDC1 $7, GDB_FR_FPR7(sp) - LDC1 $6, GDB_FR_FPR6(sp) - LDC1 $5, GDB_FR_FPR5(sp) - LDC1 $4, GDB_FR_FPR4(sp) - LDC1 $3, GDB_FR_FPR3(sp) - LDC1 $2, GDB_FR_FPR2(sp) - LDC1 $1, GDB_FR_FPR1(sp) - LDC1 $0, GDB_FR_FPR0(sp) - -/* - * Now the CP0 and integer registers - */ - -3: - mfc0 t0, CP0_STATUS - ori t0, 0x1f - xori t0, 0x1f - mtc0 t0, CP0_STATUS - - LONG_L v0, GDB_FR_STATUS(sp) - LONG_L v1, GDB_FR_EPC(sp) - mtc0 v0, CP0_STATUS - DMTC0 v1, CP0_EPC - LONG_L v0, GDB_FR_HI(sp) - LONG_L v1, GDB_FR_LO(sp) - mthi v0 - mtlo v1 - LONG_L $31, GDB_FR_REG31(sp) - LONG_L $30, GDB_FR_REG30(sp) - LONG_L $28, GDB_FR_REG28(sp) - LONG_L $27, GDB_FR_REG27(sp) - LONG_L $26, GDB_FR_REG26(sp) - LONG_L $25, GDB_FR_REG25(sp) - LONG_L $24, GDB_FR_REG24(sp) - LONG_L $23, GDB_FR_REG23(sp) - LONG_L $22, GDB_FR_REG22(sp) - LONG_L $21, GDB_FR_REG21(sp) - LONG_L $20, GDB_FR_REG20(sp) - LONG_L $19, GDB_FR_REG19(sp) - LONG_L $18, GDB_FR_REG18(sp) - LONG_L $17, GDB_FR_REG17(sp) - LONG_L $16, GDB_FR_REG16(sp) - LONG_L $15, GDB_FR_REG15(sp) - LONG_L $14, GDB_FR_REG14(sp) - LONG_L $13, GDB_FR_REG13(sp) - LONG_L $12, GDB_FR_REG12(sp) - LONG_L $11, GDB_FR_REG11(sp) - LONG_L $10, GDB_FR_REG10(sp) - LONG_L $9, GDB_FR_REG9(sp) - LONG_L $8, GDB_FR_REG8(sp) - LONG_L $7, GDB_FR_REG7(sp) - LONG_L $6, GDB_FR_REG6(sp) - LONG_L $5, GDB_FR_REG5(sp) - LONG_L $4, GDB_FR_REG4(sp) - LONG_L $3, GDB_FR_REG3(sp) - LONG_L $2, GDB_FR_REG2(sp) - LONG_L $1, GDB_FR_REG1(sp) -#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) - LONG_L k0, GDB_FR_EPC(sp) - LONG_L $29, GDB_FR_REG29(sp) /* Deallocate stack */ - jr k0 - rfe -#else - LONG_L sp, GDB_FR_REG29(sp) /* Deallocate stack */ - - .set mips3 - eret - .set mips0 -#endif - .set at - .set reorder - END(trap_low) - -LEAF(kgdb_read_byte) -4: lb t0, (a0) - sb t0, (a1) - li v0, 0 - jr ra - .section __ex_table,"a" - PTR 4b, kgdbfault - .previous - END(kgdb_read_byte) - -LEAF(kgdb_write_byte) -5: sb a0, (a1) - li v0, 0 - jr ra - .section __ex_table,"a" - PTR 5b, kgdbfault - .previous - END(kgdb_write_byte) - - .type kgdbfault@function - .ent kgdbfault - -kgdbfault: li v0, -EFAULT - jr ra - .end kgdbfault diff -L arch/mips/kernel/gdb-stub.c -puN arch/mips/kernel/gdb-stub.c~mips-lite /dev/null --- linux-2.6.10-rc1/arch/mips/kernel/gdb-stub.c +++ /dev/null 2004-10-25 00:35:20.587727328 -0700 @@ -1,1089 +0,0 @@ -/* - * arch/mips/kernel/gdb-stub.c - * - * Originally written by Glenn Engel, Lake Stevens Instrument Division - * - * Contributed by HP Systems - * - * Modified for SPARC by Stu Grossman, Cygnus Support. - * - * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse - * Send complaints, suggestions etc. to - * - * Copyright (C) 1995 Andreas Busse - * - * Copyright (C) 2003 MontaVista Software Inc. - * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net - */ - -/* - * To enable debugger support, two things need to happen. One, a - * call to set_debug_traps() is necessary in order to allow any breakpoints - * or error conditions to be properly intercepted and reported to gdb. - * Two, a breakpoint needs to be generated to begin communication. This - * is most easily accomplished by a call to breakpoint(). Breakpoint() - * simulates a breakpoint by executing a BREAK instruction. - * - * - * The following gdb commands are supported: - * - * command function Return value - * - * g return the value of the CPU registers hex data or ENN - * G set the value of the CPU registers OK or ENN - * - * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN - * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN - * - * c Resume at current address SNN ( signal NN) - * cAA..AA Continue at address AA..AA SNN - * - * s Step one instruction SNN - * sAA..AA Step one instruction from AA..AA SNN - * - * k kill - * - * ? What was the last sigval ? SNN (signal NN) - * - * bBB..BB Set baud rate to BB..BB OK or BNN, then sets - * baud rate - * - * All commands and responses are sent with a packet which includes a - * checksum. A packet consists of - * - * $#. - * - * where - * :: - * :: < two hex digits computed as modulo 256 sum of > - * - * When a packet is received, it is first acknowledged with either '+' or '-'. - * '+' indicates a successful transfer. '-' indicates a failed transfer. - * - * Example: - * - * Host: Reply: - * $m0,10#2a +$00010203040506070809101112131415#42 - * - * - * ============== - * MORE EXAMPLES: - * ============== - * - * For reference -- the following are the steps that one - * company took (RidgeRun Inc) to get remote gdb debugging - * going. In this scenario the host machine was a PC and the - * target platform was a Galileo EVB64120A MIPS evaluation - * board. - * - * Step 1: - * First download gdb-5.0.tar.gz from the internet. - * and then build/install the package. - * - * Example: - * $ tar zxf gdb-5.0.tar.gz - * $ cd gdb-5.0 - * $ ./configure --target=mips-linux-elf - * $ make - * $ install - * $ which mips-linux-elf-gdb - * /usr/local/bin/mips-linux-elf-gdb - * - * Step 2: - * Configure linux for remote debugging and build it. - * - * Example: - * $ cd ~/linux - * $ make menuconfig - * $ make - * - * Step 3: - * Download the kernel to the remote target and start - * the kernel running. It will promptly halt and wait - * for the host gdb session to connect. It does this - * since the "Kernel Hacking" option has defined - * CONFIG_KGDB which in turn enables your calls - * to: - * set_debug_traps(); - * breakpoint(); - * - * Step 4: - * Start the gdb session on the host. - * - * Example: - * $ mips-linux-elf-gdb vmlinux - * (gdb) set remotebaud 115200 - * (gdb) target remote /dev/ttyS1 - * ...at this point you are connected to - * the remote target and can use gdb - * in the normal fasion. Setting - * breakpoints, single stepping, - * printing variables, etc. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/* - * external low-level support routines - */ - -extern int putDebugChar(char c); /* write a single character */ -extern char getDebugChar(void); /* read and return a single char */ -extern void trap_low(void); - -/* - * breakpoint and test functions - */ -extern void breakpoint(void); -extern void breakinst(void); -extern void async_breakpoint(void); -extern void async_breakinst(void); -extern void adel(void); - -/* - * local prototypes - */ - -static void getpacket(char *buffer); -static void putpacket(char *buffer); -static int computeSignal(int tt); -static int hex(unsigned char ch); -static int hexToInt(char **ptr, int *intValue); -static int hexToLong(char **ptr, long *longValue); -static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); -void handle_exception(struct gdb_regs *regs); - -/* - * spin locks for smp case - */ -static spinlock_t kgdb_lock = SPIN_LOCK_UNLOCKED; -static spinlock_t kgdb_cpulock[NR_CPUS] = { [0 ... NR_CPUS-1] = SPIN_LOCK_UNLOCKED}; - -/* - * BUFMAX defines the maximum number of characters in inbound/outbound buffers - * at least NUMREGBYTES*2 are needed for register packets - */ -#define BUFMAX 2048 - -static char input_buffer[BUFMAX]; -static char output_buffer[BUFMAX]; -static int initialized; /* !0 means we've been initialized */ -static int kgdb_started; -static const char hexchars[]="0123456789abcdef"; - -/* Used to prevent crashes in memory access. Note that they'll crash anyway if - we haven't set up fault handlers yet... */ -int kgdb_read_byte(unsigned char *address, unsigned char *dest); -int kgdb_write_byte(unsigned char val, unsigned char *dest); - -/* - * Convert ch from a hex digit to an int - */ -static int hex(unsigned char ch) -{ - if (ch >= 'a' && ch <= 'f') - return ch-'a'+10; - if (ch >= '0' && ch <= '9') - return ch-'0'; - if (ch >= 'A' && ch <= 'F') - return ch-'A'+10; - return -1; -} - -/* - * scan for the sequence $# - */ -static void getpacket(char *buffer) -{ - unsigned char checksum; - unsigned char xmitcsum; - int i; - int count; - unsigned char ch; - - do { - /* - * wait around for the start character, - * ignore all other characters - */ - while ((ch = (getDebugChar() & 0x7f)) != '$') ; - - checksum = 0; - xmitcsum = -1; - count = 0; - - /* - * now, read until a # or end of buffer is found - */ - while (count < BUFMAX) { - ch = getDebugChar(); - if (ch == '#') - break; - checksum = checksum + ch; - buffer[count] = ch; - count = count + 1; - } - - if (count >= BUFMAX) - continue; - - buffer[count] = 0; - - if (ch == '#') { - xmitcsum = hex(getDebugChar() & 0x7f) << 4; - xmitcsum |= hex(getDebugChar() & 0x7f); - - if (checksum != xmitcsum) - putDebugChar('-'); /* failed checksum */ - else { - putDebugChar('+'); /* successful transfer */ - - /* - * if a sequence char is present, - * reply the sequence ID - */ - if (buffer[2] == ':') { - putDebugChar(buffer[0]); - putDebugChar(buffer[1]); - - /* - * remove sequence chars from buffer - */ - count = strlen(buffer); - for (i=3; i <= count; i++) - buffer[i-3] = buffer[i]; - } - } - } - } - while (checksum != xmitcsum); -} - -/* - * send the packet in buffer. - */ -static void putpacket(char *buffer) -{ - unsigned char checksum; - int count; - unsigned char ch; - - /* - * $#. - */ - - do { - putDebugChar('$'); - checksum = 0; - count = 0; - - while ((ch = buffer[count]) != 0) { - if (!(putDebugChar(ch))) - return; - checksum += ch; - count += 1; - } - - putDebugChar('#'); - putDebugChar(hexchars[checksum >> 4]); - putDebugChar(hexchars[checksum & 0xf]); - - } - while ((getDebugChar() & 0x7f) != '+'); -} - - -/* - * Convert the memory pointed to by mem into hex, placing result in buf. - * Return a pointer to the last char put in buf (null), in case of mem fault, - * return 0. - * may_fault is non-zero if we are reading from arbitrary memory, but is currently - * not used. - */ -static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) -{ - unsigned char ch; - - while (count-- > 0) { - if (kgdb_read_byte(mem++, &ch) != 0) - return 0; - *buf++ = hexchars[ch >> 4]; - *buf++ = hexchars[ch & 0xf]; - } - - *buf = 0; - - return buf; -} - -/* - * convert the hex array pointed to by buf into binary to be placed in mem - * return a pointer to the character AFTER the last byte written - * may_fault is non-zero if we are reading from arbitrary memory, but is currently - * not used. - */ -static char *hex2mem(char *buf, char *mem, int count, int binary, int may_fault) -{ - int i; - unsigned char ch; - - for (i=0; itt && ht->signo; ht++) - saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low); - - putDebugChar('+'); /* 'hello world' */ - /* - * In case GDB is started before us, ack any packets - * (presumably "$?#xx") sitting there. - */ - while((c = getDebugChar()) != '$'); - while((c = getDebugChar()) != '#'); - c = getDebugChar(); /* eat first csum byte */ - c = getDebugChar(); /* eat second csum byte */ - putDebugChar('+'); /* ack it */ - - initialized = 1; - local_irq_restore(flags); -} - -void restore_debug_traps(void) -{ - struct hard_trap_info *ht; - unsigned long flags; - - local_irq_save(flags); - for (ht = hard_trap_info; ht->tt && ht->signo; ht++) - set_except_vector(ht->tt, saved_vectors[ht->tt]); - local_irq_restore(flags); -} - -/* - * Convert the MIPS hardware trap type code to a Unix signal number. - */ -static int computeSignal(int tt) -{ - struct hard_trap_info *ht; - - for (ht = hard_trap_info; ht->tt && ht->signo; ht++) - if (ht->tt == tt) - return ht->signo; - - return SIGHUP; /* default for things we don't know about */ -} - -/* - * While we find nice hex chars, build an int. - * Return number of chars processed. - */ -static int hexToInt(char **ptr, int *intValue) -{ - int numChars = 0; - int hexValue; - - *intValue = 0; - - while (**ptr) { - hexValue = hex(**ptr); - if (hexValue < 0) - break; - - *intValue = (*intValue << 4) | hexValue; - numChars ++; - - (*ptr)++; - } - - return (numChars); -} - -static int hexToLong(char **ptr, long *longValue) -{ - int numChars = 0; - int hexValue; - - *longValue = 0; - - while (**ptr) { - hexValue = hex(**ptr); - if (hexValue < 0) - break; - - *longValue = (*longValue << 4) | hexValue; - numChars ++; - - (*ptr)++; - } - - return numChars; -} - - -#if 0 -/* - * Print registers (on target console) - * Used only to debug the stub... - */ -void show_gdbregs(struct gdb_regs * regs) -{ - /* - * Saved main processor registers - */ - printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", - regs->reg0, regs->reg1, regs->reg2, regs->reg3, - regs->reg4, regs->reg5, regs->reg6, regs->reg7); - printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", - regs->reg8, regs->reg9, regs->reg10, regs->reg11, - regs->reg12, regs->reg13, regs->reg14, regs->reg15); - printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", - regs->reg16, regs->reg17, regs->reg18, regs->reg19, - regs->reg20, regs->reg21, regs->reg22, regs->reg23); - printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", - regs->reg24, regs->reg25, regs->reg26, regs->reg27, - regs->reg28, regs->reg29, regs->reg30, regs->reg31); - - /* - * Saved cp0 registers - */ - printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n", - regs->cp0_epc, regs->cp0_status, regs->cp0_cause); -} -#endif /* dead code */ - -/* - * We single-step by setting breakpoints. When an exception - * is handled, we need to restore the instructions hoisted - * when the breakpoints were set. - * - * This is where we save the original instructions. - */ -static struct gdb_bp_save { - unsigned long addr; - unsigned int val; -} step_bp[2]; - -#define BP 0x0000000d /* break opcode */ - -/* - * Set breakpoint instructions for single stepping. - */ -static void single_step(struct gdb_regs *regs) -{ - union mips_instruction insn; - unsigned long targ; - int is_branch, is_cond, i; - - targ = regs->cp0_epc; - insn.word = *(unsigned int *)targ; - is_branch = is_cond = 0; - - switch (insn.i_format.opcode) { - /* - * jr and jalr are in r_format format. - */ - case spec_op: - switch (insn.r_format.func) { - case jalr_op: - case jr_op: - targ = *(®s->reg0 + insn.r_format.rs); - is_branch = 1; - break; - } - break; - - /* - * This group contains: - * bltz_op, bgez_op, bltzl_op, bgezl_op, - * bltzal_op, bgezal_op, bltzall_op, bgezall_op. - */ - case bcond_op: - is_branch = is_cond = 1; - targ += 4 + (insn.i_format.simmediate << 2); - break; - - /* - * These are unconditional and in j_format. - */ - case jal_op: - case j_op: - is_branch = 1; - targ += 4; - targ >>= 28; - targ <<= 28; - targ |= (insn.j_format.target << 2); - break; - - /* - * These are conditional. - */ - case beq_op: - case beql_op: - case bne_op: - case bnel_op: - case blez_op: - case blezl_op: - case bgtz_op: - case bgtzl_op: - case cop0_op: - case cop1_op: - case cop2_op: - case cop1x_op: - is_branch = is_cond = 1; - targ += 4 + (insn.i_format.simmediate << 2); - break; - } - - if (is_branch) { - i = 0; - if (is_cond && targ != (regs->cp0_epc + 8)) { - step_bp[i].addr = regs->cp0_epc + 8; - step_bp[i++].val = *(unsigned *)(regs->cp0_epc + 8); - *(unsigned *)(regs->cp0_epc + 8) = BP; - } - step_bp[i].addr = targ; - step_bp[i].val = *(unsigned *)targ; - *(unsigned *)targ = BP; - } else { - step_bp[0].addr = regs->cp0_epc + 4; - step_bp[0].val = *(unsigned *)(regs->cp0_epc + 4); - *(unsigned *)(regs->cp0_epc + 4) = BP; - } -} - -/* - * If asynchronously interrupted by gdb, then we need to set a breakpoint - * at the interrupted instruction so that we wind up stopped with a - * reasonable stack frame. - */ -static struct gdb_bp_save async_bp; - -/* - * Swap the interrupted EPC with our asynchronous breakpoint routine. - * This is safer than stuffing the breakpoint in-place, since no cache - * flushes (or resulting smp_call_functions) are required. The - * assumption is that only one CPU will be handling asynchronous bp's, - * and only one can be active at a time. - */ -extern spinlock_t smp_call_lock; -void set_async_breakpoint(unsigned long *epc) -{ - /* skip breaking into userland */ - if ((*epc & 0x80000000) == 0) - return; - - /* avoid deadlock if someone is make IPC */ - if (spin_is_locked(&smp_call_lock)) - return; - - async_bp.addr = *epc; - *epc = (unsigned long)async_breakpoint; -} - -void kgdb_wait(void *arg) -{ - unsigned flags; - int cpu = smp_processor_id(); - - local_irq_save(flags); - - spin_lock(&kgdb_cpulock[cpu]); - spin_unlock(&kgdb_cpulock[cpu]); - - local_irq_restore(flags); -} - - -/* - * This function does all command processing for interfacing to gdb. It - * returns 1 if you should skip the instruction at the trap address, 0 - * otherwise. - */ -void handle_exception (struct gdb_regs *regs) -{ - int trap; /* Trap type */ - int sigval; - long addr; - int length; - char *ptr; - unsigned long *stack; - int i; - int bflag = 0; - - kgdb_started = 1; - - /* - * acquire the big kgdb spinlock - */ - if (!spin_trylock(&kgdb_lock)) { - /* - * some other CPU has the lock, we should go back to - * receive the gdb_wait IPC - */ - return; - } - - /* - * If we're in async_breakpoint(), restore the real EPC from - * the breakpoint. - */ - if (regs->cp0_epc == (unsigned long)async_breakinst) { - regs->cp0_epc = async_bp.addr; - async_bp.addr = 0; - } - - /* - * acquire the CPU spinlocks - */ - for (i = num_online_cpus()-1; i >= 0; i--) - if (spin_trylock(&kgdb_cpulock[i]) == 0) - panic("kgdb: couldn't get cpulock %d\n", i); - - /* - * force other cpus to enter kgdb - */ - smp_call_function(kgdb_wait, NULL, 0, 0); - - /* - * If we're in breakpoint() increment the PC - */ - trap = (regs->cp0_cause & 0x7c) >> 2; - if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) - regs->cp0_epc += 4; - - /* - * If we were single_stepping, restore the opcodes hoisted - * for the breakpoint[s]. - */ - if (step_bp[0].addr) { - *(unsigned *)step_bp[0].addr = step_bp[0].val; - step_bp[0].addr = 0; - - if (step_bp[1].addr) { - *(unsigned *)step_bp[1].addr = step_bp[1].val; - step_bp[1].addr = 0; - } - } - - stack = (long *)regs->reg29; /* stack ptr */ - sigval = computeSignal(trap); - - /* - * reply to host that an exception has occurred - */ - ptr = output_buffer; - - /* - * Send trap type (converted to signal) - */ - *ptr++ = 'T'; - *ptr++ = hexchars[sigval >> 4]; - *ptr++ = hexchars[sigval & 0xf]; - - /* - * Send Error PC - */ - *ptr++ = hexchars[REG_EPC >> 4]; - *ptr++ = hexchars[REG_EPC & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®s->cp0_epc, ptr, sizeof(long), 0); - *ptr++ = ';'; - - /* - * Send frame pointer - */ - *ptr++ = hexchars[REG_FP >> 4]; - *ptr++ = hexchars[REG_FP & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®s->reg30, ptr, sizeof(long), 0); - *ptr++ = ';'; - - /* - * Send stack pointer - */ - *ptr++ = hexchars[REG_SP >> 4]; - *ptr++ = hexchars[REG_SP & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®s->reg29, ptr, sizeof(long), 0); - *ptr++ = ';'; - - *ptr++ = 0; - putpacket(output_buffer); /* send it off... */ - - /* - * Wait for input from remote GDB - */ - while (1) { - output_buffer[0] = 0; - getpacket(input_buffer); - - switch (input_buffer[0]) - { - case '?': - output_buffer[0] = 'S'; - output_buffer[1] = hexchars[sigval >> 4]; - output_buffer[2] = hexchars[sigval & 0xf]; - output_buffer[3] = 0; - break; - - /* - * Detach debugger; let CPU run - */ - case 'D': - putpacket(output_buffer); - goto finish_kgdb; - break; - - case 'd': - /* toggle debug flag */ - break; - - /* - * Return the value of the CPU registers - */ - case 'g': - ptr = output_buffer; - ptr = mem2hex((char *)®s->reg0, ptr, 32*sizeof(long), 0); /* r0...r31 */ - ptr = mem2hex((char *)®s->cp0_status, ptr, 6*sizeof(long), 0); /* cp0 */ - ptr = mem2hex((char *)®s->fpr0, ptr, 32*sizeof(long), 0); /* f0...31 */ - ptr = mem2hex((char *)®s->cp1_fsr, ptr, 2*sizeof(long), 0); /* cp1 */ - ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*sizeof(long), 0); /* frp */ - ptr = mem2hex((char *)®s->cp0_index, ptr, 16*sizeof(long), 0); /* cp0 */ - break; - - /* - * set the value of the CPU registers - return OK - */ - case 'G': - { - ptr = &input_buffer[1]; - hex2mem(ptr, (char *)®s->reg0, 32*sizeof(long), 0, 0); - ptr += 32*(2*sizeof(long)); - hex2mem(ptr, (char *)®s->cp0_status, 6*sizeof(long), 0, 0); - ptr += 6*(2*sizeof(long)); - hex2mem(ptr, (char *)®s->fpr0, 32*sizeof(long), 0, 0); - ptr += 32*(2*sizeof(long)); - hex2mem(ptr, (char *)®s->cp1_fsr, 2*sizeof(long), 0, 0); - ptr += 2*(2*sizeof(long)); - hex2mem(ptr, (char *)®s->frame_ptr, 2*sizeof(long), 0, 0); - ptr += 2*(2*sizeof(long)); - hex2mem(ptr, (char *)®s->cp0_index, 16*sizeof(long), 0, 0); - strcpy(output_buffer,"OK"); - } - break; - - /* - * mAA..AA,LLLL Read LLLL bytes at address AA..AA - */ - case 'm': - ptr = &input_buffer[1]; - - if (hexToLong(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length)) { - if (mem2hex((char *)addr, output_buffer, length, 1)) - break; - strcpy (output_buffer, "E03"); - } else - strcpy(output_buffer,"E01"); - break; - - /* - * XAA..AA,LLLL: Write LLLL escaped binary bytes at address AA.AA - */ - case 'X': - bflag = 1; - /* fall through */ - - /* - * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK - */ - case 'M': - ptr = &input_buffer[1]; - - if (hexToLong(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length) - && *ptr++ == ':') { - if (hex2mem(ptr, (char *)addr, length, bflag, 1)) - strcpy(output_buffer, "OK"); - else - strcpy(output_buffer, "E03"); - } - else - strcpy(output_buffer, "E02"); - break; - - /* - * cAA..AA Continue at address AA..AA(optional) - */ - case 'c': - /* try to read optional parameter, pc unchanged if no parm */ - - ptr = &input_buffer[1]; - if (hexToLong(&ptr, &addr)) - regs->cp0_epc = addr; - - goto exit_kgdb_exception; - break; - - /* - * kill the program; let us try to restart the machine - * Reset the whole machine. - */ - case 'k': - case 'r': - machine_restart("kgdb restarts machine"); - break; - - /* - * Step to next instruction - */ - case 's': - /* - * There is no single step insn in the MIPS ISA, so we - * use breakpoints and continue, instead. - */ - single_step(regs); - goto exit_kgdb_exception; - /* NOTREACHED */ - break; - - /* - * Set baud rate (bBB) - * FIXME: Needs to be written - */ - case 'b': - { -#if 0 - int baudrate; - extern void set_timer_3(); - - ptr = &input_buffer[1]; - if (!hexToInt(&ptr, &baudrate)) - { - strcpy(output_buffer,"B01"); - break; - } - - /* Convert baud rate to uart clock divider */ - - switch (baudrate) - { - case 38400: - baudrate = 16; - break; - case 19200: - baudrate = 33; - break; - case 9600: - baudrate = 65; - break; - default: - baudrate = 0; - strcpy(output_buffer,"B02"); - goto x1; - } - - if (baudrate) { - putpacket("OK"); /* Ack before changing speed */ - set_timer_3(baudrate); /* Set it */ - } -#endif - } - break; - - } /* switch */ - - /* - * reply to the request - */ - - putpacket(output_buffer); - - } /* while */ - - return; - -finish_kgdb: - restore_debug_traps(); - -exit_kgdb_exception: - /* release locks so other CPUs can go */ - for (i = num_online_cpus()-1; i >= 0; i--) - spin_unlock(&kgdb_cpulock[i]); - spin_unlock(&kgdb_lock); - - __flush_cache_all(); - return; -} - -/* - * This function will generate a breakpoint exception. It is used at the - * beginning of a program to sync up with a debugger and can be used - * otherwise as a quick means to stop program execution and "break" into - * the debugger. - */ -void breakpoint(void) -{ - if (!initialized) - return; - - __asm__ __volatile__( - ".globl breakinst\n\t" - ".set\tnoreorder\n\t" - "nop\n" - "breakinst:\tbreak\n\t" - "nop\n\t" - ".set\treorder" - ); -} - -/* Nothing but the break; don't pollute any registers */ -void async_breakpoint(void) -{ - __asm__ __volatile__( - ".globl async_breakinst\n\t" - ".set\tnoreorder\n\t" - "nop\n" - "async_breakinst:\tbreak\n\t" - "nop\n\t" - ".set\treorder" - ); -} - -void adel(void) -{ - __asm__ __volatile__( - ".globl\tadel\n\t" - "lui\t$8,0x8000\n\t" - "lw\t$9,1($8)\n\t" - ); -} - -/* - * malloc is needed by gdb client in "call func()", even a private one - * will make gdb happy - */ -static void *malloc(size_t size) -{ - return kmalloc(size, GFP_ATOMIC); -} - -static void free(void *where) -{ - kfree(where); -} - -#ifdef CONFIG_GDB_CONSOLE - -void gdb_putsn(const char *str, int l) -{ - char outbuf[18]; - - if (!kgdb_started) - return; - - outbuf[0]='O'; - - while(l) { - int i = (l>8)?8:l; - mem2hex((char *)str, &outbuf[1], i, 0); - outbuf[(i*2)+1]=0; - putpacket(outbuf); - str += i; - l -= i; - } -} - -static void gdb_console_write(struct console *con, const char *s, unsigned n) -{ - gdb_putsn(s, n); -} - -static struct console gdb_console = { - .name = "gdb", - .write = gdb_console_write, - .flags = CON_PRINTBUFFER, - .index = -1 -}; - -static int __init register_gdb_console(void) -{ - register_console(&gdb_console); - - return 0; -} - -console_initcall(register_gdb_console); - -#endif diff -puN arch/mips/kernel/irq.c~mips-lite arch/mips/kernel/irq.c --- linux-2.6.10-rc1/arch/mips/kernel/irq.c~mips-lite 2004-10-29 11:26:45.179280287 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/kernel/irq.c 2004-10-29 11:26:45.207273714 -0700 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -420,6 +421,8 @@ out: irq_exit(); + kgdb_process_breakpoint(); + return 1; } diff -puN /dev/null arch/mips/kernel/kgdb.c --- /dev/null 2004-10-25 00:35:20.587727328 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb.c 2004-10-29 11:26:45.208273480 -0700 @@ -0,0 +1,292 @@ +/* + * arch/mips/kernel/kgdb.c + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2004 MontaVista Software Inc. + * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include + +static struct hard_trap_info { + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ + { 10, SIGILL }, /* reserved instruction */ +/* { 11, SIGILL }, */ /* CPU unusable */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + +extern atomic_t cpu_doing_single_step; + +/* Save the normal trap handlers for user-mode traps. */ +void *saved_vectors[32]; +extern void trap_low(void); +extern void breakinst(void); + +static int compute_signal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * Set up exception handlers for tracing and breakpoints + */ +void handle_exception(struct pt_regs *regs) +{ + int trap = (regs->cp0_cause & 0x7c) >> 2; + + if (atomic_read(&debugger_active)) + debugger_nmihook(smp_processor_id(), regs); + + if (atomic_read (&kgdb_setting_breakpoint)) + if ( (trap == 9) && (regs->cp0_epc == (unsigned long)breakinst) ) + regs->cp0_epc += 4; + + (*linux_debug_hook) (0, compute_signal(trap), 0, regs); + + /* In SMP mode, __flush_cache_all does IPI */ + __flush_cache_all(); +} + +void set_debug_traps(void) +{ + struct hard_trap_info *ht; + unsigned long flags; + + local_irq_save(flags); + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low); + + local_irq_restore(flags); +} + +#if 0 +/* This should be called before we exit kgdb_handle_exception() I believe. + * -- Tom + */ +void restore_debug_traps(void) +{ + struct hard_trap_info *ht; + unsigned long flags; + + local_irq_save(flags); + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + set_except_vector(ht->tt, saved_vectors[ht->tt]); + local_irq_restore(flags); +} +#endif + +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int reg = 0; + unsigned long *ptr = gdb_regs; + + for (reg = 0; reg < 32; reg++) + *(ptr++) = regs->regs[reg]; + + *(ptr++) = regs->cp0_status; + *(ptr++) = regs->lo; + *(ptr++) = regs->hi; + *(ptr++) = regs->cp0_badvaddr; + *(ptr++) = regs->cp0_cause; + *(ptr++) = regs->cp0_epc; + + return; +} + +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int reg = 0; + unsigned long *ptr = gdb_regs; + + for (reg = 0; reg < 32; reg++) + regs->regs[reg] = *(ptr++); + + regs->cp0_status = *(ptr++); + regs->lo = *(ptr++); + regs->hi = *(ptr++); + regs->cp0_badvaddr = *(ptr++); + regs->cp0_cause = *(ptr++); + regs->cp0_epc = *(ptr++); + + return; +} + +/* + * Similar to regs_to_gdb_regs() except that process is sleeping and so + * we may not be able to get all the info. + */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + int reg = 0; + struct thread_info *ti = p->thread_info; + unsigned long ksp = (unsigned long)ti + THREAD_SIZE -32; + struct pt_regs *regs = (struct pt_regs *)ksp - 1; + unsigned long *ptr = gdb_regs; + + for (reg = 0; reg < 16; reg++) + *(ptr++) = regs->regs[reg]; + + /* S0 - S7 */ + for (reg = 16; reg < 24; reg++) + *(ptr++) = regs->regs[reg]; + + for (reg = 24; reg < 28; reg++) + *(ptr++) = 0; + + /* GP, SP, FP, RA */ + for (reg = 28; reg < 32; reg++) + *(ptr++) = regs->regs[reg]; + + *(ptr++) = regs->cp0_status; + *(ptr++) = regs->lo; + *(ptr++) = regs->hi; + *(ptr++) = regs->cp0_badvaddr; + *(ptr++) = regs->cp0_cause; + *(ptr++) = regs->cp0_epc; + + return; +} + +/* + * Calls linux_debug_hook before the kernel dies. If KGDB is enabled, + * then try to fall into the debugger + */ +static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd, + void *ptr) +{ + struct die_args *args = (struct die_args *)ptr; + struct pt_regs *regs = args->regs; + int trap = (regs->cp0_cause & 0x7c) >> 2; + + if (cmd == DIE_PAGE_FAULT) + return NOTIFY_DONE; + + if (user_mode(regs)) { + if (cmd == DIE_OOPS) + CHK_DEBUGGER(trap, compute_signal(trap), 0, regs, 0); + return NOTIFY_DONE; + } + + CHK_DEBUGGER(trap, compute_signal(trap), 0, regs, 0); + return NOTIFY_OK; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_mips_notify, +}; + +/* + * Handle the 's' and 'c' commands + */ +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *InBuffer, char *outBuffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long address; + int cpu = smp_processor_id (); + + switch (InBuffer[0]) { + case 's': + case 'c': + if (kgdb_contthread && kgdb_contthread != current) { + strcpy(outBuffer, "E00"); + break; + } + + /* handle the optional parameter */ + ptr = &InBuffer[1]; + if (kgdb_hex2long (&ptr, &address)) + regs->cp0_epc = address; + + atomic_set(&cpu_doing_single_step,-1); + if (InBuffer[0] == 's') + if (kgdb_contthread) + atomic_set(&cpu_doing_single_step, cpu); + + return 0; + } + + return -1; +} + +struct kgdb_arch arch_kgdb_ops = { +#ifdef CONFIG_CPU_LITTLE_ENDIAN + .gdb_bpt_instr = {0xd}, +#else + .gdb_bpt_instr = {0x00, 0x00, 0x00, 0x0d}, +#endif +}; + +/* + * Note that on some boards (such as MIPS Malta) we need to do additional + * bringup. We do this via kgdb_mips_init(). We provide a weak version + * here as others (such as BCM91250A-SWARM) need no extra bringup. + */ +void __attribute__ ((weak)) kgdb_mips_init(void) +{ +} + +int kgdb_arch_init(void) +{ + /* Board-specifics. */ + kgdb_mips_init(); + + /* Set our traps. */ + /* This needs to be done more finely grained again, paired in + * a before/after in kgdb_handle_exception(...) -- Tom */ + set_debug_traps(); + notifier_chain_register(&mips_die_chain, &kgdb_notifier); + + return 0; +} diff -puN /dev/null arch/mips/kernel/kgdb_handler.S --- /dev/null 2004-10-25 00:35:20.587727328 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb_handler.S 2004-10-29 11:26:45.208273480 -0700 @@ -0,0 +1,58 @@ +/* + * arch/mips/kernel/kgdb_handler.S + * + * Copyright (C) 2004 MontaVista Software Inc. + * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com + * + * This file is licensed under the terms of the GNU General Public + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/* + * Trap Handler for the new KGDB framework. The main KGDB handler is + * handle_exception that will be called from here + * + */ + +#include +#include + +#include +#include +#include +#include +#include + + .align 5 + NESTED(trap_low, PT_SIZE, sp) + .set noat + .set noreorder + SAVE_ALL + /* + * Check for privileged instructions in user mode. For + * this, check the cu0 bit in the CPU status register. + */ + mfc0 k0, CP0_STATUS + sll k0, 3 + bltz k0, 1f + move k1, sp + + /* + * GDB userland from within KGDB. If a user mode address + * then jump to the saved exception handler + */ + lui k1, %hi(saved_vectors) + mfc0 k0, CP0_CAUSE + andi k0, k0, 0x7c + add k1, k1, k0 + lw k0, %lo(saved_vectors)(k1) + jr k0 + nop +1: + .set at + .set reorder + move a0, sp + jal handle_exception + j ret_from_exception + END(trap_low) diff -puN arch/mips/kernel/traps.c~mips-lite arch/mips/kernel/traps.c --- linux-2.6.10-rc1/arch/mips/kernel/traps.c~mips-lite 2004-10-29 11:26:45.181279818 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/kernel/traps.c 2004-10-29 11:26:45.210273010 -0700 @@ -10,6 +10,8 @@ * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000, 01 MIPS Technologies, Inc. * Copyright (C) 2002, 2003, 2004 Maciej W. Rozycki + * + * KGDB specific changes - Manish Lachwani (mlachwani@mvista.com) */ #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #include #include @@ -37,6 +40,7 @@ #include #include #include +#include extern asmlinkage void handle_mod(void); extern asmlinkage void handle_tlbl(void); @@ -72,6 +76,21 @@ int (*board_be_handler)(struct pt_regs * */ #define MODULE_RANGE (8*1024*1024) +struct notifier_block *mips_die_chain; +static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED; + +int register_die_notifier(struct notifier_block *nb) +{ + int err = 0; + unsigned long flags; + + spin_lock_irqsave(&die_notifier_lock, flags); + err = notifier_chain_register(&mips_die_chain, nb); + spin_unlock_irqrestore(&die_notifier_lock, flags); + + return err; +} + /* * This routine abuses get_user()/put_user() to reference pointers * with at least a bit of error checking ... @@ -265,8 +284,10 @@ NORET_TYPE void __die(const char * str, void __die_if_kernel(const char * str, struct pt_regs * regs, const char * file, const char * func, unsigned long line) { - if (!user_mode(regs)) + if (!user_mode(regs)) { + notify_die(DIE_TRAP, (char *)str, regs, 0); __die(str, regs, file, func, line); + } } extern const struct exception_table_entry __start___dbe_table[]; @@ -921,6 +942,9 @@ void __init per_cpu_trap_init(void) tlb_init(); } +/* Keep track of if we've done certain initialization already or not. */ +int kgdb_early_setup; + void __init trap_init(void) { extern char except_vec3_generic, except_vec3_r4000; @@ -928,6 +952,9 @@ void __init trap_init(void) extern char except_vec4; unsigned long i; + if (kgdb_early_setup) + return; /* Already done */ + per_cpu_trap_init(); /* diff -puN arch/mips/mips-boards/generic/Makefile~mips-lite arch/mips/mips-boards/generic/Makefile --- linux-2.6.10-rc1/arch/mips/mips-boards/generic/Makefile~mips-lite 2004-10-29 11:26:45.183279348 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/Makefile 2004-10-29 11:26:45.210273010 -0700 @@ -21,6 +21,5 @@ obj-y := mipsIRQ.o reset.o display.o init.o memory.o \ printf.o cmdline.o time.o obj-$(CONFIG_PCI) += pci.o -obj-$(CONFIG_KGDB) += gdb_hook.o EXTRA_AFLAGS := $(CFLAGS) diff -puN arch/mips/mips-boards/generic/init.c~mips-lite arch/mips/mips-boards/generic/init.c --- linux-2.6.10-rc1/arch/mips/mips-boards/generic/init.c~mips-lite 2004-10-29 11:26:45.185278879 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/init.c 2004-10-29 11:26:45.210273010 -0700 @@ -35,17 +35,6 @@ #include #endif -#ifdef CONFIG_KGDB -extern int rs_kgdb_hook(int, int); -extern int rs_putDebugChar(char); -extern char rs_getDebugChar(void); -extern int saa9730_kgdb_hook(int); -extern int saa9730_putDebugChar(char); -extern char saa9730_getDebugChar(void); - -int remote_debug = 0; -#endif - int prom_argc; int *_prom_argv, *_prom_envp; @@ -172,59 +161,6 @@ static void __init console_config(void) } #endif -#ifdef CONFIG_KGDB -void __init kgdb_config (void) -{ - extern int (*generic_putDebugChar)(char); - extern char (*generic_getDebugChar)(void); - char *argptr; - int line, speed; - - argptr = prom_getcmdline(); - if ((argptr = strstr(argptr, "kgdb=ttyS")) != NULL) { - argptr += strlen("kgdb=ttyS"); - if (*argptr != '0' && *argptr != '1') - printk("KGDB: Unknown serial line /dev/ttyS%c, " - "falling back to /dev/ttyS1\n", *argptr); - line = *argptr == '0' ? 0 : 1; - printk("KGDB: Using serial line /dev/ttyS%d for session\n", line); - - speed = 0; - if (*++argptr == ',') - { - int c; - while ((c = *++argptr) && ('0' <= c && c <= '9')) - speed = speed * 10 + c - '0'; - } -#ifdef CONFIG_MIPS_ATLAS - if (line == 1) { - speed = saa9730_kgdb_hook(speed); - generic_putDebugChar = saa9730_putDebugChar; - generic_getDebugChar = saa9730_getDebugChar; - } - else -#endif - { - speed = rs_kgdb_hook(line, speed); - generic_putDebugChar = rs_putDebugChar; - generic_getDebugChar = rs_getDebugChar; - } - - prom_printf("KGDB: Using serial line /dev/ttyS%d at %d for session, " - "please connect your debugger\n", line ? 1 : 0, speed); - - { - char *s; - for (s = "Please connect GDB to this port\r\n"; *s; ) - generic_putDebugChar (*s++); - } - - remote_debug = 1; - /* Breakpoint is invoked after interrupts are initialised */ - } -} -#endif - void __init prom_init(void) { prom_argc = fw_arg0; diff -puN arch/mips/mips-boards/malta/malta_int.c~mips-lite arch/mips/mips-boards/malta/malta_int.c --- linux-2.6.10-rc1/arch/mips/mips-boards/malta/malta_int.c~mips-lite 2004-10-29 11:26:45.187278409 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_int.c 2004-10-29 11:26:45.211272776 -0700 @@ -40,12 +40,7 @@ #include extern asmlinkage void mipsIRQ(void); - -#ifdef CONFIG_KGDB -extern void breakpoint(void); -extern void set_debug_traps(void); -extern int remote_debug; -#endif +extern int kgdb_early_setup; static spinlock_t mips_irq_lock = SPIN_LOCK_UNLOCKED; @@ -189,16 +184,20 @@ void corehi_irqdispatch(struct pt_regs * void __init init_IRQ(void) { + if (kgdb_early_setup) + return; /* Already done. */ set_except_vector(0, mipsIRQ); init_generic_irq(); init_i8259_irqs(); +} #ifdef CONFIG_KGDB - if (remote_debug) { - set_debug_traps(); - breakpoint(); - } -#endif +void kgdb_mips_init(void) +{ + trap_init(); + set_except_vector(0, mipsIRQ); + init_generic_irq(); + init_i8259_irqs(); + kgdb_early_setup = 1; } - - +#endif diff -puN arch/mips/mips-boards/malta/malta_setup.c~mips-lite arch/mips/mips-boards/malta/malta_setup.c --- linux-2.6.10-rc1/arch/mips/mips-boards/malta/malta_setup.c~mips-lite 2004-10-29 11:26:45.188278175 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_setup.c 2004-10-29 11:26:45.211272776 -0700 @@ -41,10 +41,6 @@ extern void mips_time_init(void); extern void mips_timer_setup(struct irqaction *irq); extern unsigned long mips_rtc_get_time(void); -#ifdef CONFIG_KGDB -extern void kgdb_config(void); -#endif - struct resource standard_io_resources[] = { { "dma1", 0x00, 0x1f, IORESOURCE_BUSY }, { "timer", 0x40, 0x5f, IORESOURCE_BUSY }, @@ -93,10 +89,6 @@ static int __init malta_setup(void) */ enable_dma(4); -#ifdef CONFIG_KGDB - kgdb_config (); -#endif - if ((mips_revision_corid == MIPS_REVISION_CORID_BONITO64) || (mips_revision_corid == MIPS_REVISION_CORID_CORE_20K) || (mips_revision_corid == MIPS_REVISION_CORID_CORE_EMUL_BON)) { diff -puN arch/mips/sibyte/cfe/setup.c~mips-lite arch/mips/sibyte/cfe/setup.c --- linux-2.6.10-rc1/arch/mips/sibyte/cfe/setup.c~mips-lite 2004-10-29 11:26:45.190277705 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/sibyte/cfe/setup.c 2004-10-29 11:26:45.212272541 -0700 @@ -62,10 +62,6 @@ extern void * __rd_start, * __rd_end; static int reboot_smp = 0; #endif -#ifdef CONFIG_KGDB -extern int kgdb_port; -#endif - static void cfe_linux_exit(void) { #ifdef CONFIG_SMP @@ -243,9 +239,6 @@ void __init prom_init(void) int argc = fw_arg0; char **envp = (char **) fw_arg2; int *prom_vec = (int *) fw_arg3; -#ifdef CONFIG_KGDB - char *arg; -#endif _machine_restart = (void (*)(char *))cfe_linux_exit; _machine_halt = cfe_linux_exit; @@ -309,13 +302,6 @@ void __init prom_init(void) } } -#ifdef CONFIG_KGDB - if ((arg = strstr(arcs_cmdline,"kgdb=duart")) != NULL) - kgdb_port = (arg[10] == '0') ? 0 : 1; - else - kgdb_port = 1; -#endif - #ifdef CONFIG_BLK_DEV_INITRD { char *ptr; diff -puN arch/mips/sibyte/sb1250/Makefile~mips-lite arch/mips/sibyte/sb1250/Makefile --- linux-2.6.10-rc1/arch/mips/sibyte/sb1250/Makefile~mips-lite 2004-10-29 11:26:45.192277236 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/Makefile 2004-10-29 11:26:45.212272541 -0700 @@ -4,5 +4,6 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SIBYTE_TBPROF) += bcm1250_tbprof.o obj-$(CONFIG_SIBYTE_STANDALONE) += prom.o obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o +obj-$(CONFIG_KGDB_SIBYTE) += kgdb_sibyte.o EXTRA_AFLAGS := $(CFLAGS) diff -puN arch/mips/sibyte/sb1250/irq.c~mips-lite arch/mips/sibyte/sb1250/irq.c --- linux-2.6.10-rc1/arch/mips/sibyte/sb1250/irq.c~mips-lite 2004-10-29 11:26:45.194276766 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/irq.c 2004-10-29 11:26:45.212272541 -0700 @@ -59,27 +59,7 @@ static void sb1250_set_affinity(unsigned #ifdef CONFIG_SIBYTE_HAS_LDT extern unsigned long ldt_eoi_space; #endif - -#ifdef CONFIG_KGDB -#include -extern void breakpoint(void); -static int kgdb_irq; - -/* kgdb is on when configured. Pass "nokgdb" kernel arg to turn it off */ -static int kgdb_flag = 1; -static int __init nokgdb(char *str) -{ - kgdb_flag = 0; - return 1; -} -__setup("nokgdb", nokgdb); - -/* Default to UART1 */ -int kgdb_port = 1; -#ifdef CONFIG_SIBYTE_SB1250_DUART -extern char sb1250_duart_present[]; -#endif -#endif +extern int kgdb_early_setup; static struct hw_interrupt_type sb1250_irq_type = { "SB1250-IMR", @@ -340,6 +320,11 @@ void __init init_IRQ(void) unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 | STATUSF_IP1 | STATUSF_IP0; +#ifdef CONFIG_KGDB + if (kgdb_early_setup == 1) + return; +#endif + /* Default everything to IP2 */ for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */ __raw_writeq(IMR_IP2_VAL, @@ -389,49 +374,4 @@ void __init init_IRQ(void) /* Enable necessary IPs, disable the rest */ change_c0_status(ST0_IM, imask); set_except_vector(0, sb1250_irq_handler); - -#ifdef CONFIG_KGDB - if (kgdb_flag) { - kgdb_irq = K_INT_UART_0 + kgdb_port; - -#ifdef CONFIG_SIBYTE_SB1250_DUART - sb1250_duart_present[kgdb_port] = 0; -#endif - /* Setup uart 1 settings, mapper */ - __raw_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port))); - - sb1250_steal_irq(kgdb_irq); - __raw_writeq(IMR_IP6_VAL, - IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + - (kgdb_irq<<3))); - sb1250_unmask_irq(0, kgdb_irq); - - prom_printf("Waiting for GDB on UART port %d\n", kgdb_port); - set_debug_traps(); - breakpoint(); - } -#endif -} - -#ifdef CONFIG_KGDB - -#include - -#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg))) -#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg))) - -void sb1250_kgdb_interrupt(struct pt_regs *regs) -{ - /* - * Clear break-change status (allow some time for the remote - * host to stop the break, since we would see another - * interrupt on the end-of-break too) - */ - kstat_this_cpu.irqs[kgdb_irq]++; - mdelay(500); - duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT | - M_DUART_RX_EN | M_DUART_TX_EN); - set_async_breakpoint(®s->cp0_epc); } - -#endif /* CONFIG_KGDB */ diff -puN /dev/null arch/mips/sibyte/sb1250/kgdb_sibyte.c --- /dev/null 2004-10-25 00:35:20.587727328 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/kgdb_sibyte.c 2004-10-29 11:26:45.213272306 -0700 @@ -0,0 +1,158 @@ +/* + * arch/mips/sibyte/sb1250/kgdb_sibyte.c + * + * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com + * + * 2004 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * Support for KGDB on the Broadcom Sibyte. The SWARM board + * for example does not have a 8250/16550 compatible serial + * port. Hence, we need to have a driver for the serial + * ports to handle KGDB. This board needs nothing in addition + * to what is normally provided by the gdb portion of the stub. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int kgdb_port = 1; +static int kgdb_irq; + +extern char sb1250_duart_present[]; +extern int sb1250_steal_irq(int irq); +extern int kgdb_early_setup; + +/* Forward declarations. */ +static void kgdbsibyte_init_duart(void); + +#define IMR_IP6_VAL K_INT_MAP_I4 +#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg))) +#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg))) + +static void kgdb_swarm_write_char(int c) +{ + while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0); + duart_out(R_DUART_TX_HOLD, c); +} + +static int kgdb_swarm_read_char(void) +{ + unsigned long flags; + int ret_char; + unsigned int status; + + local_irq_save(flags); + + status = duart_in(R_DUART_STATUS); + while ((status & M_DUART_RX_RDY) == 0) { + status = duart_in(R_DUART_STATUS); + } + + /* + * Check for framing error + */ + if (status & M_DUART_FRM_ERR) { + kgdbsibyte_init_duart(); + kgdb_swarm_write_char('-'); + return '-'; + } + + ret_char = duart_in(R_DUART_RX_HOLD); + local_irq_restore(flags); + + return ret_char; +} + +void sb1250_kgdb_interrupt(struct pt_regs *regs) +{ + int kgdb_irq = K_INT_UART_0 + kgdb_port; + /* + * Clear break-change status (allow some time for the remote + * host to stop the break, since we would see another + * interrupt on the end-of-break too) + */ + kstat_this_cpu.irqs[kgdb_irq]++; + mdelay(500); + duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT | + M_DUART_RX_EN | M_DUART_TX_EN); + kgdb_schedule_breakpoint(); +} + +/* + * We use port #1 and we set it for 115200 BAUD, 8n1. + */ +static void +kgdbsibyte_init_duart(void) +{ + /* Set 8n1. */ + duart_out(R_DUART_MODE_REG_1, + V_DUART_BITS_PER_CHAR_8 | V_DUART_PARITY_MODE_NONE); + duart_out(R_DUART_MODE_REG_2, M_DUART_STOP_BIT_LEN_1); + /* Set baud rate of 115200. */ + duart_out(R_DUART_CLK_SEL, V_DUART_BAUD_RATE(115200)); + /* Enable rx and tx */ + duart_out(R_DUART_CMD, M_DUART_RX_EN | M_DUART_TX_EN); +} + +static int +kgdb_init_io(void) +{ +#ifdef CONFIG_SIBYTE_SB1250_DUART + sb1250_duart_present[kgdb_port] = 0; +#endif + + kgdbsibyte_init_duart(); + + return 0; +} + +struct kgdb_io kgdb_io_ops = { + .read_char = kgdb_swarm_read_char, + .write_char = kgdb_swarm_write_char, + .init = kgdb_init_io, + .late_init = kgdbsibyte_hookup_irq, +}; + +void kgdb_mips_init(void) +{ + if (kgdb_early_setup == 0) { + trap_init(); + init_IRQ(); + kgdb_early_setup = 1; + } +} + +/* + * Hookup our IRQ line. We will already have been initialized a + * this point. + */ +static void __init kgdbsibyte_hookup_irq(void) +{ + /* Steal the IRQ. */ + kgdb_irq = K_INT_UART_0 + kgdb_port; + + /* Setup uart 1 settings, mapper */ + __raw_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port))); + + sb1250_steal_irq(kgdb_irq); + + __raw_writeq(IMR_IP6_VAL, + IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + + (kgdb_irq<<3))); + + sb1250_unmask_irq(0, kgdb_irq); +} diff -puN arch/mips/sibyte/swarm/Makefile~mips-lite arch/mips/sibyte/swarm/Makefile --- linux-2.6.10-rc1/arch/mips/sibyte/swarm/Makefile~mips-lite 2004-10-29 11:26:45.196276297 -0700 +++ linux-2.6.10-rc1-trini/arch/mips/sibyte/swarm/Makefile 2004-10-29 11:26:45.213272306 -0700 @@ -1,3 +1 @@ lib-y = setup.o rtc_xicor1241.o rtc_m41t81.o - -lib-$(CONFIG_KGDB) += dbg_io.o diff -L arch/mips/sibyte/swarm/dbg_io.c -puN arch/mips/sibyte/swarm/dbg_io.c~mips-lite /dev/null --- linux-2.6.10-rc1/arch/mips/sibyte/swarm/dbg_io.c +++ /dev/null 2004-10-25 00:35:20.587727328 -0700 @@ -1,76 +0,0 @@ -/* - * kgdb debug routines for SiByte boards. - * - * Copyright (C) 2001 MontaVista Software Inc. - * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -/* -------------------- BEGINNING OF CONFIG --------------------- */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * We use the second serial port for kgdb traffic. - * 115200, 8, N, 1. - */ - -#define BAUD_RATE 115200 -#define CLK_DIVISOR V_DUART_BAUD_RATE(BAUD_RATE) -#define DATA_BITS V_DUART_BITS_PER_CHAR_8 /* or 7 */ -#define PARITY V_DUART_PARITY_MODE_NONE /* or even */ -#define STOP_BITS M_DUART_STOP_BIT_LEN_1 /* or 2 */ - -static int duart_initialized = 0; /* 0: need to be init'ed by kgdb */ - -/* -------------------- END OF CONFIG --------------------- */ -extern int kgdb_port; - -#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg))) -#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg))) - -void putDebugChar(unsigned char c); -unsigned char getDebugChar(void); -static void -duart_init(int clk_divisor, int data, int parity, int stop) -{ - duart_out(R_DUART_MODE_REG_1, data | parity); - duart_out(R_DUART_MODE_REG_2, stop); - duart_out(R_DUART_CLK_SEL, clk_divisor); - - duart_out(R_DUART_CMD, M_DUART_RX_EN | M_DUART_TX_EN); /* enable rx and tx */ -} - -void -putDebugChar(unsigned char c) -{ - if (!duart_initialized) { - duart_initialized = 1; - duart_init(CLK_DIVISOR, DATA_BITS, PARITY, STOP_BITS); - } - while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0); - duart_out(R_DUART_TX_HOLD, c); -} - -unsigned char -getDebugChar(void) -{ - if (!duart_initialized) { - duart_initialized = 1; - duart_init(CLK_DIVISOR, DATA_BITS, PARITY, STOP_BITS); - } - while ((duart_in(R_DUART_STATUS) & M_DUART_RX_RDY) == 0) ; - return duart_in(R_DUART_RX_HOLD); -} - diff -puN /dev/null include/asm-mips/kdebug.h --- /dev/null 2004-10-25 00:35:20.587727328 -0700 +++ linux-2.6.10-rc1-trini/include/asm-mips/kdebug.h 2004-10-29 11:26:45.214272071 -0700 @@ -0,0 +1,47 @@ +/* + * + * Copyright (C) 2004 MontaVista Software Inc. + * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifndef _MIPS_KDEBUG_H +#define _MIPS_KDEBUG_H + +#include + +struct pt_regs; + +struct die_args { + struct pt_regs *regs; + const char *str; + long err; +}; + +int register_die_notifier(struct notifier_block *nb); +extern struct notifier_block *mips_die_chain; + +enum die_val { + DIE_OOPS = 1, + DIE_PANIC, + DIE_DIE, + DIE_KERNELDEBUG, + DIE_TRAP, + DIE_PAGE_FAULT, +}; + +/* + * trap number can be computed from regs and signr can be computed using + * compute_signal() + */ +static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err) +{ + struct die_args args = { .regs=regs, .str=str, .err=err }; + return notifier_call_chain(&mips_die_chain, val, &args); +} + +#endif /* _MIPS_KDEBUG_H */ diff -puN /dev/null include/asm-mips/kgdb.h --- /dev/null 2004-10-25 00:35:20.587727328 -0700 +++ linux-2.6.10-rc1-trini/include/asm-mips/kgdb.h 2004-10-29 11:26:45.214272071 -0700 @@ -0,0 +1,20 @@ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +#include + +#define BUFMAX 2048 +#define NUMREGBYTES (90*sizeof(long)) +#define BREAK_INSTR_SIZE 4 + +#define BREAKPOINT() __asm__ __volatile__ ( \ + ".globl breakinst\n\t" \ + ".set\tnoreorder\n\t" \ + "nop\n" \ + "breakinst:\tbreak\n\t" \ + "nop\n\t" \ + ".set\treorder") + +#define CHECK_EXCEPTION_STACK() 1 + +#endif /* _ASM_KGDB_H_ */ diff -puN lib/Kconfig.debug~mips-lite lib/Kconfig.debug --- linux-2.6.10-rc1/lib/Kconfig.debug~mips-lite 2004-10-29 11:26:45.200275358 -0700 +++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:56.134154345 -0700 @@ -115,7 +115,7 @@ endif config KGDB bool "KGDB: kernel debugging with remote gdb" - depends on DEBUG_KERNEL && (X86 || ((!SMP || BROKEN) && (PPC32))) + depends on DEBUG_KERNEL && (X86 || MIPS32 || ((!SMP || BROKEN) && PPC32)) help If you say Y here, it will be possible to remotely debug the kernel using gdb. This enlarges your kernel image disk size by @@ -128,6 +128,7 @@ config KGDB choice prompt "Method for KGDB communication" depends on KGDB + default KGDB_SIBYTE if SIBYTE_SB1xxx_SOC default KGDB_8250 help There are a number of different ways in which you can communicate @@ -140,6 +141,10 @@ config KGDB_8250 Uses generic serial port (8250) for kgdb. This is independent of the option 9250/16550 and compatible serial port. +config KGDB_SIBYTE + bool "KGDB: On the Broadcom SWARM serial port" + depends on MIPS && SIBYTE_SB1xxx_SOC + endchoice config KGDB_SIMPLE_SERIAL @@ -153,7 +158,7 @@ config KGDB_SIMPLE_SERIAL If in doubt, say Y. choice - depends on KGDB_8250 + depends on KGDB_8250 || KGDB_SIBYTE prompt "Debug serial port BAUD" default KGDB_115200BAUD help _ - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/