[patch 09/16] Add support for SuperH platforms to KGDB

From: Tom Rini
Date: Mon Aug 29 2005 - 11:14:16 EST



This adds basic support for KGDB on SuperH as well as adding some architecture
specific notes to the DocBook file and converting the 7751 to use this. I
have tested all combinations of 8250 and SCI(F) ports being used as KGDB and
console that I could (one of each usable to me).

Index: linux-2.6.13/arch/sh/boards/se/7751/setup.c
===================================================================
--- linux-2.6.13.orig/arch/sh/boards/se/7751/setup.c
+++ linux-2.6.13/arch/sh/boards/se/7751/setup.c
@@ -18,10 +18,6 @@
#include <asm/io.h>
#include <asm/se7751/se7751.h>

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#endif
-
/*
* Configure the Super I/O chip
*/
@@ -83,12 +79,6 @@ const char *get_system_type(void)
return "7751 SolutionEngine";
}

-#ifdef CONFIG_SH_KGDB
-static int kgdb_uart_setup(void);
-static struct kgdb_sermap kgdb_uart_sermap =
-{ "ttyS", 0, kgdb_uart_setup, NULL };
-#endif
-
/*
* Initialize the board
*/
@@ -96,133 +86,4 @@ void __init platform_setup(void)
{
/* Call init_smsc() replacement to set up SuperIO. */
/* XXX: RTC setting comes here */
-#ifdef CONFIG_SH_KGDB
- kgdb_register_sermap(&kgdb_uart_sermap);
-#endif
-}
-
-/*********************************************************************
- * Currently a hack (e.g. does not interact well w/serial.c, lots of *
- * hardcoded stuff) but may be useful if SCI/F needs debugging. *
- * Mostly copied from x86 code (see files asm-i386/kgdb_local.h and *
- * arch/i386/lib/kgdb_serial.c). *
- *********************************************************************/
-
-#ifdef CONFIG_SH_KGDB
-#include <linux/types.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/serial_reg.h>
-
-#define COM1_PORT 0x3f8 /* Base I/O address */
-#define COM1_IRQ 4 /* IRQ not used yet */
-#define COM2_PORT 0x2f8 /* Base I/O address */
-#define COM2_IRQ 3 /* IRQ not used yet */
-
-#define SB_CLOCK 1843200 /* Serial baud clock */
-#define SB_BASE (SB_CLOCK/16)
-#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS
-
-struct uart_port {
- int base;
-};
-#define UART_NPORTS 2
-struct uart_port uart_ports[] = {
- { COM1_PORT },
- { COM2_PORT },
-};
-struct uart_port *kgdb_uart_port;
-
-#define UART_IN(reg) inb_p(kgdb_uart_port->base + reg)
-#define UART_OUT(reg,v) outb_p((v), kgdb_uart_port->base + reg)
-
-/* Basic read/write functions for the UART */
-#define UART_LSR_RXCERR (UART_LSR_BI | UART_LSR_FE | UART_LSR_PE)
-static int kgdb_uart_getchar(void)
-{
- int lsr;
- int c = -1;
-
- while (c == -1) {
- lsr = UART_IN(UART_LSR);
- if (lsr & UART_LSR_DR)
- c = UART_IN(UART_RX);
- if ((lsr & UART_LSR_RXCERR))
- c = -1;
- }
- return c;
-}
-
-static void kgdb_uart_putchar(int c)
-{
- while ((UART_IN(UART_LSR) & UART_LSR_THRE) == 0)
- ;
- UART_OUT(UART_TX, c);
-}
-
-/*
- * Initialize UART to configured/requested values.
- * (But we don't interrupts yet, or interact w/serial.c)
- */
-static int kgdb_uart_setup(void)
-{
- int port;
- int lcr = 0;
- int bdiv = 0;
-
- if (kgdb_portnum >= UART_NPORTS) {
- KGDB_PRINTK("uart port %d invalid.\n", kgdb_portnum);
- return -1;
- }
-
- kgdb_uart_port = &uart_ports[kgdb_portnum];
-
- /* Init sequence from gdb_hook_interrupt */
- UART_IN(UART_RX);
- UART_OUT(UART_IER, 0);
-
- UART_IN(UART_RX); /* Serial driver comments say */
- UART_IN(UART_IIR); /* this clears interrupt regs */
- UART_IN(UART_MSR);
-
- /* Figure basic LCR values */
- switch (kgdb_bits) {
- case '7':
- lcr |= UART_LCR_WLEN7;
- break;
- default: case '8':
- lcr |= UART_LCR_WLEN8;
- break;
- }
- switch (kgdb_parity) {
- case 'O':
- lcr |= UART_LCR_PARITY;
- break;
- case 'E':
- lcr |= (UART_LCR_PARITY | UART_LCR_EPAR);
- break;
- default: break;
- }
-
- /* Figure the baud rate divisor */
- bdiv = (SB_BASE/kgdb_baud);
-
- /* Set the baud rate and LCR values */
- UART_OUT(UART_LCR, (lcr | UART_LCR_DLAB));
- UART_OUT(UART_DLL, (bdiv & 0xff));
- UART_OUT(UART_DLM, ((bdiv >> 8) & 0xff));
- UART_OUT(UART_LCR, lcr);
-
- /* Set the MCR */
- UART_OUT(UART_MCR, SB_MCR);
-
- /* Turn off FIFOs for now */
- UART_OUT(UART_FCR, 0);
-
- /* Setup complete: initialize function pointers */
- kgdb_getchar = kgdb_uart_getchar;
- kgdb_putchar = kgdb_uart_putchar;
-
- return 0;
}
-#endif /* CONFIG_SH_KGDB */
Index: linux-2.6.13/arch/sh/Kconfig.debug
===================================================================
--- linux-2.6.13.orig/arch/sh/Kconfig.debug
+++ linux-2.6.13/arch/sh/Kconfig.debug
@@ -29,96 +29,4 @@ config EARLY_PRINTK
This option is only useful porting the kernel to a new machine,
when the kernel may crash or hang before the serial console is
initialised. If unsure, say N.
-
-config KGDB
- bool "Include KGDB kernel debugger"
- help
- Include in-kernel hooks for kgdb, the Linux kernel source level
- debugger. See <http://kgdb.sourceforge.net/> for more information.
- Unless you are intending to debug the kernel, say N here.
-
-menu "KGDB configuration options"
- depends on KGDB
-
-config MORE_COMPILE_OPTIONS
- bool "Add any additional compile options"
- help
- If you want to add additional CFLAGS to the kernel build, enable this
- option and then enter what you would like to add in the next question.
- Note however that -g is already appended with the selection of KGDB.
-
-config COMPILE_OPTIONS
- string "Additional compile arguments"
- depends on MORE_COMPILE_OPTIONS
-
-config KGDB_NMI
- bool "Enter KGDB on NMI"
- default n
-
-config KGDB_THREAD
- bool "Include KGDB thread support"
- default y
-
-config SH_KGDB_CONSOLE
- bool "Console messages through GDB"
- default n
-
-config KGDB_SYSRQ
- bool "Allow SysRq 'G' to enter KGDB"
- default y
-
-config KGDB_KERNEL_ASSERTS
- bool "Include KGDB kernel assertions"
- default n
-
-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 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 KGDB
- default KGDB_DEFBITS_8
-
-config KGDB_DEFBITS_8
- bool "8"
-
-config KGDB_DEFBITS_7
- bool "7"
-
-endchoice
-
-endmenu
-
-config FRAME_POINTER
- bool "Compile the kernel with frame pointers"
- default y if KGDB
- help
- If you say Y here the resulting kernel image will be slightly larger
- and slower, but it will give very useful debugging information.
- If you don't debug the kernel, you can say N, but we may not be able
- to solve problems without frame pointers.
-
endmenu
Index: linux-2.6.13/arch/sh/kernel/cpu/sh3/ex.S
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/cpu/sh3/ex.S
+++ linux-2.6.13/arch/sh/kernel/cpu/sh3/ex.S
@@ -43,7 +43,7 @@ ENTRY(exception_handling_table)
.long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
.long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
ENTRY(nmi_slot)
-#if defined (CONFIG_KGDB_NMI)
+#if defined (CONFIG_KGDB)
.long debug_enter /* 1C0 */ ! Allow trap to debugger
#else
.long exception_none /* 1C0 */ ! Not implemented yet
Index: linux-2.6.13/arch/sh/kernel/cpu/sh4/ex.S
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/cpu/sh4/ex.S
+++ linux-2.6.13/arch/sh/kernel/cpu/sh4/ex.S
@@ -47,7 +47,7 @@ ENTRY(exception_handling_table)
.long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
.long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
ENTRY(nmi_slot)
-#if defined (CONFIG_KGDB_NMI)
+#if defined (CONFIG_KGDB)
.long debug_enter /* 1C0 */ ! Allow trap to debugger
#else
.long exception_none /* 1C0 */ ! Not implemented yet
Index: linux-2.6.13/arch/sh/kernel/entry.S
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/entry.S
+++ linux-2.6.13/arch/sh/kernel/entry.S
@@ -92,7 +92,7 @@ INTEVT = 0xff000028
MMU_TEA = 0xff00000c ! TLB Exception Address Register
#endif

-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
NMI_VEC = 0x1c0 ! Must catch early for debounce
#endif

@@ -244,31 +244,33 @@ call_dae:
2: .long do_address_error
#endif /* CONFIG_MMU */

-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_KGDB)
! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present.
! If both are configured, handle the debug traps (breakpoints) in SW,
! but still allow BIOS traps to FW.

.align 2
debug_kernel:
-#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB)
+#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_KGDB)
/* Force BIOS call to FW (debug_trap put TRA in r8) */
mov r8,r0
shlr2 r0
cmp/eq #0x3f,r0
bt debug_kernel_fw
-#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_KGDB */

-debug_enter:
-#if defined(CONFIG_SH_KGDB)
+ .align 2
+ .globl debug_enter
+debug_enter:
+#if defined(CONFIG_KGDB)
/* Jump to kgdb, pass stacked regs as arg */
debug_kernel_sw:
mov.l 3f, r0
jmp @r0
mov r15, r4
.align 2
-3: .long kgdb_handle_exception
-#endif /* CONFIG_SH_KGDB */
+3: .long kgdb_exception_handler
+#endif /* CONFIG_KGDB */

#if defined(CONFIG_SH_STANDARD_BIOS)
/* Unwind the stack and jmp to the debug entry */
@@ -310,12 +312,12 @@ debug_kernel_fw:
2: .long gdb_vbr_vector
#endif /* CONFIG_SH_STANDARD_BIOS */

-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_KGDB */


.align 2
-debug_trap:
-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+debug_trap:
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_KGDB)
mov #OFF_SR, r0
mov.l @(r0,r15), r0 ! get status register
shll r0
@@ -659,7 +661,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 4f, k0
mov #0, k1
@@ -711,7 +713,7 @@ tlb_miss:
interrupt:
mov.l 2f, k2
mov.l 3f, k3
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
! Debounce (filter nested NMI)
mov.l @k2, k0
mov.l 5f, k1
@@ -726,7 +728,7 @@ interrupt:
5: .long NMI_VEC
6: .long in_nmi
0:
-#endif /* defined(CONFIG_KGDB_NMI) */
+#endif /* defined(CONFIG_KGDB) */
bra handle_exception
mov.l @k2, k2

Index: linux-2.6.13/arch/sh/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.13/arch/sh/kernel/kgdb.c
@@ -0,0 +1,363 @@
+/*
+ * arch/sh/kernel/kgdb.c
+ *
+ * Contains SH-specific low-level support for KGDB.
+ *
+ * Containes extracts from code by Glenn Engel, Jim Kingdon,
+ * David Grothe <dave@xxxxxxxx>, Tigran Aivazian <tigran@xxxxxxx>,
+ * Amit S. Kale <akale@xxxxxxxxxxx>, William Gatliff <bgat@xxxxxxxxxxxxxxxx>,
+ * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@xxxxxxxxx>,
+ * Henry Bell <henry.bell@xxxxxx> and Jeremy Siegel <jsiegel@xxxxxxxxxx>
+ *
+ * Maintainer: Tom Rini <trini@xxxxxxxxxxxxxxxxxxx>
+ *
+ * 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.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+
+#include <asm/system.h>
+#include <asm/current.h>
+#include <asm/signal.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+
+extern void per_cpu_trap_init(void);
+extern atomic_t cpu_doing_single_step;
+
+/* Function pointers for linkage */
+static struct kgdb_regs trap_registers;
+
+/* Globals. */
+char in_nmi; /* Set during NMI to prevent reentry */
+
+/* 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 SERIAL_BREAK_VEC 58
+
+/* Misc static */
+static int stepped_address;
+static short stepped_opcode;
+
+/* Translate SH-3/4 exception numbers to unix-like signal values */
+static int compute_signal(const int excep_code)
+{
+ switch (excep_code) {
+ case INVALID_INSN_VEC:
+ case INVALID_SLOT_VEC:
+ return SIGILL;
+ case ADDRESS_ERROR_LOAD_VEC:
+ case ADDRESS_ERROR_STORE_VEC:
+ return SIGSEGV;
+ case SERIAL_BREAK_VEC:
+ case NMI_VEC:
+ return SIGINT;
+ default:
+ /* Act like it was a break/trap. */
+ return SIGTRAP;
+ }
+}
+
+/*
+ * Translate the registers of the system into the format that GDB wants. Since
+ * we use a local structure to store things, instead of getting them out
+ * of pt_regs, we can just do a memcpy.
+ */
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *ign)
+{
+ memcpy(gdb_regs, &trap_registers, sizeof(trap_registers));
+}
+
+/*
+ * On SH we save: r1 (prev->thread.sp) r2 (prev->thread.pc) r4 (prev) r5 (next)
+ * r6 (next->thread.sp) r7 (next->thread.pc)
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ int count;
+
+ for (count = 0; count < 16; count++)
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = p->thread.pc;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+}
+
+/*
+ * Translate the registers values that GDB has given us back into the
+ * format of the system. See the comment above about memcpy.
+ */
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *ign)
+{
+ memcpy(&trap_registers, gdb_regs, sizeof(trap_registers));
+}
+
+/* Calculate the new address for after a step */
+static short *get_step_address(void)
+{
+ short op = *(short *)trap_registers.pc;
+ long addr;
+
+ /* BT */
+ if (OPCODE_BT(op)) {
+ if (trap_registers.sr & SR_T_BIT_MASK)
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 2;
+ }
+
+ /* BTS */
+ else if (OPCODE_BTS(op)) {
+ if (trap_registers.sr & SR_T_BIT_MASK)
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 4; /* Not in delay slot */
+ }
+
+ /* BF */
+ else if (OPCODE_BF(op)) {
+ if (!(trap_registers.sr & SR_T_BIT_MASK))
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 2;
+ }
+
+ /* BFS */
+ else if (OPCODE_BFS(op)) {
+ if (!(trap_registers.sr & SR_T_BIT_MASK))
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 4; /* Not in delay slot */
+ }
+
+ /* BRA */
+ else if (OPCODE_BRA(op))
+ addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
+
+ /* BRAF */
+ else if (OPCODE_BRAF(op))
+ addr = trap_registers.pc + 4
+ + trap_registers.regs[OPCODE_BRAF_REG(op)];
+
+ /* BSR */
+ else if (OPCODE_BSR(op))
+ addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
+
+ /* BSRF */
+ else if (OPCODE_BSRF(op))
+ addr = trap_registers.pc + 4
+ + trap_registers.regs[OPCODE_BSRF_REG(op)];
+
+ /* JMP */
+ else if (OPCODE_JMP(op))
+ addr = trap_registers.regs[OPCODE_JMP_REG(op)];
+
+ /* JSR */
+ else if (OPCODE_JSR(op))
+ addr = trap_registers.regs[OPCODE_JSR_REG(op)];
+
+ /* RTS */
+ else if (OPCODE_RTS(op))
+ addr = trap_registers.pr;
+
+ /* RTE */
+ else if (OPCODE_RTE(op))
+ addr = trap_registers.regs[15];
+
+ /* Other */
+ else
+ addr = trap_registers.pc + 2;
+
+ kgdb_flush_icache_range(addr, addr + 2);
+ return (short *)addr;
+}
+
+/* The command loop, read and act on requests */
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ struct pt_regs *ign)
+{
+ unsigned long addr;
+ char *ptr = &remcom_in_buffer[1];
+
+ /* Examine first char of buffer to see what we need to do */
+ switch (remcom_in_buffer[0]) {
+ case 'c': /* Continue at address AA..AA (optional) */
+ case 's': /* Step one instruction from AA..AA */
+ /* Try to read optional parameter, PC unchanged if none */
+ if (kgdb_hex2long(&ptr, &addr))
+ trap_registers.pc = addr;
+
+ atomic_set(&cpu_doing_single_step, -1);
+ if (remcom_in_buffer[0] == 's') {
+ /* 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.
+ */
+ /* Determine where the target instruction will send
+ * us to */
+ unsigned short *next_addr = get_step_address();
+ stepped_address = (int)next_addr;
+
+ /* Replace it */
+ stepped_opcode = *(short *)next_addr;
+ *next_addr = STEP_OPCODE;
+
+ /* Flush and return */
+ kgdb_flush_icache_range((long)next_addr,
+ (long)next_addr + 2);
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,
+ smp_processor_id());
+ }
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * When an exception has occured, we are called. We need to set things
+ * up so that we can call kgdb_handle_exception to handle requests from
+ * the remote GDB.
+ */
+void kgdb_exception_handler(struct pt_regs *regs)
+{
+ int excep_code, vbr_val;
+ int count;
+
+ /* 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__ __volatile__("stc vbr, %0":"=r"(vbr_val));
+ trap_registers.vbr = vbr_val;
+
+ /* Get the execption code. */
+ __asm__ __volatile__("stc r2_bank, %0":"=r"(excep_code));
+
+ excep_code >>= 5;
+
+ /* If we got an NMI, and KGDB is not yet initialized, call
+ * breakpoint() to try and initialize everything for us. */
+ if (excep_code == NMI_VEC && !kgdb_initialized) {
+ breakpoint();
+ return;
+ }
+
+ /* 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 &&
+ (*(volatile unsigned long *)TRA != (0xff << 2)))
+ trap_registers.pc -= 2;
+
+ /* If we have been single-stepping, put back the old instruction.
+ * We use stepped_address in case we have stopped more than one
+ * instruction away. */
+ if (stepped_opcode != 0) {
+ *(short *)stepped_address = stepped_opcode;
+ kgdb_flush_icache_range(stepped_address, stepped_address + 2);
+ }
+ stepped_opcode = 0;
+
+ /* Call the stub to do the processing. Note that not everything we
+ * need to send back and forth lives in pt_regs. */
+ kgdb_handle_exception(excep_code, compute_signal(excep_code), 0, regs);
+
+ /* 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__ __volatile__("ldc %0, vbr": :"r"(vbr_val));
+}
+
+int __init kgdb_arch_init(void)
+{
+ per_cpu_trap_init();
+
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ .gdb_bpt_instr = {0xff, 0xc3},
+#else
+ .gdb_bpt_instr = {0xc3, 0xff},
+#endif
+};
Index: linux-2.6.13/arch/sh/kernel/kgdb_jmp.S
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/kgdb_jmp.S
+++ /dev/null
@@ -1,33 +0,0 @@
-#include <linux/linkage.h>
-
-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
-
Index: linux-2.6.13/arch/sh/kernel/kgdb-jmp.S
===================================================================
--- /dev/null
+++ linux-2.6.13/arch/sh/kernel/kgdb-jmp.S
@@ -0,0 +1,32 @@
+#include <linux/linkage.h>
+
+ENTRY(kgdb_fault_setjmp)
+ add #(9*4), r4
+ sts.l pr, @-r4
+ mov.l r15, @-r4
+ mov.l r14, @-r4
+ mov.l r13, @-r4
+ mov.l r12, @-r4
+ mov.l r11, @-r4
+ mov.l r10, @-r4
+ mov.l r9, @-r4
+ mov.l r8, @-r4
+ rts
+ mov #0, r0
+
+ENTRY(kgdb_fault_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
+1: rts
+ nop
Index: linux-2.6.13/arch/sh/kernel/kgdb_stub.c
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/kgdb_stub.c
+++ /dev/null
@@ -1,1491 +0,0 @@
-/*
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Containes extracts from code by Glenn Engel, Jim Kingdon,
- * David Grothe <dave@xxxxxxxx>, Tigran Aivazian <tigran@xxxxxxx>,
- * Amit S. Kale <akale@xxxxxxxxxxx>, William Gatliff <bgat@xxxxxxxxxxxxxxxx>,
- * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@xxxxxxxxx>.
- *
- * This version by Henry Bell <henry.bell@xxxxxx>
- * Minor modifications by Jeremy Siegel <jsiegel@xxxxxxxxxx>
- *
- * 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<id>
- * 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 <data> is encapsulated for
- * transmission in the form:
- *
- * $ <data> # CSUM1 CSUM2
- *
- * <data> must be ASCII alphanumeric and cannot include characters
- * '$' or '#'. If <data> 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 <data>, the most significant nibble is sent first.
- * the hex digits 0-9,a-f are used.
- *
- * Receiver responds with:
- *
- * + - if CSUM is correct and ready for next packet
- * - - if CSUM is incorrect
- *
- * Responses can be run-length encoded to save space. A '*' means that
- * the next character is an ASCII encoding giving a repeat count which
- * stands for that many repititions of the character preceding the '*'.
- * The encoding is n+29, yielding a printable character where n >=3
- * (which is where RLE starts to win). Don't use an n > 126.
- *
- * So "0* " means the same as "0000".
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-#include <linux/linkage.h>
-#include <linux/init.h>
-
-#include <asm/system.h>
-#include <asm/current.h>
-#include <asm/signal.h>
-#include <asm/pgtable.h>
-#include <asm/ptrace.h>
-#include <asm/kgdb.h>
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-#include <linux/console.h>
-#endif
-
-/* Function pointers for linkage */
-kgdb_debug_hook_t *kgdb_debug_hook;
-kgdb_bus_error_hook_t *kgdb_bus_err_hook;
-
-int (*kgdb_getchar)(void);
-void (*kgdb_putchar)(int);
-
-static void put_debug_char(int c)
-{
- if (!kgdb_putchar)
- return;
- (*kgdb_putchar)(c);
-}
-static int get_debug_char(void)
-{
- if (!kgdb_getchar)
- return -1;
- return (*kgdb_getchar)();
-}
-
-/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */
-#define BUFMAX 1024
-#define NUMREGBYTES (MAXREG*4)
-#define OUTBUFMAX (NUMREGBYTES*2+512)
-
-enum regs {
- R0 = 0, R1, R2, R3, R4, R5, R6, R7,
- R8, R9, R10, R11, R12, R13, R14, R15,
- PC, PR, GBR, VBR, MACH, MACL, SR,
- /* */
- MAXREG
-};
-
-static unsigned int registers[MAXREG];
-struct kgdb_regs trap_registers;
-
-char kgdb_in_gdb_mode;
-char in_nmi; /* Set during NMI to prevent reentry */
-int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */
-int kgdb_enabled = 1; /* Default to enabled, cmdline can disable */
-int kgdb_halt;
-
-/* Exposed for user access */
-struct task_struct *kgdb_current;
-unsigned int kgdb_g_imask;
-int kgdb_trapa_val;
-int kgdb_excode;
-
-/* 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;
-int kgdb_baud = CONFIG_KGDB_DEFBAUD;
-char kgdb_parity = CONFIG_KGDB_DEFPARITY;
-char kgdb_bits = CONFIG_KGDB_DEFBITS;
-
-/* 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 const char hexchars[] = "0123456789abcdef";
-static char in_buffer[BUFMAX];
-static char out_buffer[OUTBUFMAX];
-
-static void kgdb_to_gdb(const char *s);
-
-#ifdef CONFIG_KGDB_THREAD
-static struct task_struct *trapped_thread;
-static struct task_struct *current_thread;
-typedef unsigned char threadref[8];
-#define BUF_THREAD_ID_SIZE 16
-#endif
-
-/* Return addr as a real volatile address */
-static inline unsigned int ctrl_inl(const unsigned long addr)
-{
- return *(volatile unsigned long *) addr;
-}
-
-/* Correctly set *addr using volatile */
-static inline void ctrl_outl(const unsigned int b, unsigned long addr)
-{
- *(volatile unsigned long *) addr = b;
-}
-
-/* Get high hex bits */
-static char highhex(const int x)
-{
- return hexchars[(x >> 4) & 0xf];
-}
-
-/* Get low hex bits */
-static char lowhex(const int x)
-{
- return hexchars[x & 0xf];
-}
-
-/* Convert ch to hex */
-static int hex(const char ch)
-{
- if ((ch >= 'a') && (ch <= 'f'))
- return (ch - 'a' + 10);
- if ((ch >= '0') && (ch <= '9'))
- return (ch - '0');
- if ((ch >= 'A') && (ch <= 'F'))
- return (ch - 'A' + 10);
- return (-1);
-}
-
-/* Convert the memory pointed to by mem into hex, placing result in buf.
- Returns a pointer to the last char put in buf (null) */
-static char *mem_to_hex(const char *mem, char *buf, const int count)
-{
- int i;
- int ch;
- unsigned short s_val;
- unsigned long l_val;
-
- /* Check for 16 or 32 */
- if (count == 2 && ((long) mem & 1) == 0) {
- s_val = *(unsigned short *) mem;
- mem = (char *) &s_val;
- } else if (count == 4 && ((long) mem & 3) == 0) {
- l_val = *(unsigned long *) mem;
- mem = (char *) &l_val;
- }
- for (i = 0; i < count; i++) {
- ch = *mem++;
- *buf++ = highhex(ch);
- *buf++ = lowhex(ch);
- }
- *buf = 0;
- return (buf);
-}
-
-/* Convert the hex array pointed to by buf into binary, to be placed in mem.
- Return a pointer to the character after the last byte written */
-static char *hex_to_mem(const char *buf, char *mem, const int count)
-{
- int i;
- unsigned char ch;
-
- for (i = 0; i < count; i++) {
- ch = hex(*buf++) << 4;
- ch = ch + hex(*buf++);
- *mem++ = ch;
- }
- return (mem);
-}
-
-/* While finding valid hex chars, convert to an integer, then return it */
-static int hex_to_int(char **ptr, int *int_value)
-{
- int num_chars = 0;
- int hex_value;
-
- *int_value = 0;
-
- while (**ptr) {
- hex_value = hex(**ptr);
- if (hex_value >= 0) {
- *int_value = (*int_value << 4) | hex_value;
- num_chars++;
- } else
- break;
- (*ptr)++;
- }
- return num_chars;
-}
-
-/* Copy the binary array pointed to by buf into mem. Fix $, #,
- and 0x7d escaped with 0x7d. Return a pointer to the character
- after the last byte written. */
-static char *ebin_to_mem(const char *buf, char *mem, int count)
-{
- for (; count > 0; count--, buf++) {
- if (*buf == 0x7d)
- *mem++ = *(++buf) ^ 0x20;
- else
- *mem++ = *buf;
- }
- return mem;
-}
-
-/* Pack a hex byte */
-static char *pack_hex_byte(char *pkt, int byte)
-{
- *pkt++ = hexchars[(byte >> 4) & 0xf];
- *pkt++ = hexchars[(byte & 0xf)];
- return pkt;
-}
-
-#ifdef CONFIG_KGDB_THREAD
-
-/* Pack a thread ID */
-static char *pack_threadid(char *pkt, threadref * id)
-{
- char *limit;
- unsigned char *altid;
-
- altid = (unsigned char *) id;
-
- limit = pkt + BUF_THREAD_ID_SIZE;
- while (pkt < limit)
- pkt = pack_hex_byte(pkt, *altid++);
- return pkt;
-}
-
-/* Convert an integer into our threadref */
-static void int_to_threadref(threadref * id, const int value)
-{
- unsigned char *scan = (unsigned char *) id;
- int i = 4;
-
- while (i--)
- *scan++ = 0;
-
- *scan++ = (value >> 24) & 0xff;
- *scan++ = (value >> 16) & 0xff;
- *scan++ = (value >> 8) & 0xff;
- *scan++ = (value & 0xff);
-}
-
-/* Return a task structure ptr for a particular pid */
-static struct task_struct *get_thread(int pid)
-{
- struct task_struct *thread;
-
- /* Use PID_MAX w/gdb for pid 0 */
- if (pid == PID_MAX) pid = 0;
-
- /* First check via PID */
- thread = find_task_by_pid(pid);
-
- if (thread)
- return thread;
-
- /* Start at the start */
- thread = init_tasks[0];
-
- /* Walk along the linked list of tasks */
- do {
- if (thread->pid == pid)
- return thread;
- thread = thread->next_task;
- } while (thread != init_tasks[0]);
-
- return NULL;
-}
-
-#endif /* CONFIG_KGDB_THREAD */
-
-/* Scan for the start char '$', read the packet and check the checksum */
-static void get_packet(char *buffer, int buflen)
-{
- unsigned char checksum;
- unsigned char xmitcsum;
- int i;
- int count;
- char ch;
-
- do {
- /* Ignore everything until the start character */
- while ((ch = get_debug_char()) != '$');
-
- checksum = 0;
- xmitcsum = -1;
- count = 0;
-
- /* Now, read until a # or end of buffer is found */
- while (count < (buflen - 1)) {
- ch = get_debug_char();
-
- if (ch == '#')
- break;
-
- checksum = checksum + ch;
- buffer[count] = ch;
- count = count + 1;
- }
-
- buffer[count] = 0;
-
- /* Continue to read checksum following # */
- if (ch == '#') {
- xmitcsum = hex(get_debug_char()) << 4;
- xmitcsum += hex(get_debug_char());
-
- /* Checksum */
- if (checksum != xmitcsum)
- put_debug_char('-'); /* Failed checksum */
- else {
- /* Ack successful transfer */
- put_debug_char('+');
-
- /* If a sequence char is present, reply
- the sequence ID */
- if (buffer[2] == ':') {
- put_debug_char(buffer[0]);
- put_debug_char(buffer[1]);
-
- /* Remove sequence chars from buffer */
- count = strlen(buffer);
- for (i = 3; i <= count; i++)
- buffer[i - 3] = buffer[i];
- }
- }
- }
- }
- while (checksum != xmitcsum); /* Keep trying while we fail */
-}
-
-/* Send the packet in the buffer with run-length encoding */
-static void put_packet(char *buffer)
-{
- int checksum;
- char *src;
- int runlen;
- int encode;
-
- do {
- src = buffer;
- put_debug_char('$');
- checksum = 0;
-
- /* Continue while we still have chars left */
- while (*src) {
- /* Check for runs up to 99 chars long */
- for (runlen = 1; runlen < 99; runlen++) {
- if (src[0] != src[runlen])
- break;
- }
-
- if (runlen > 3) {
- /* Got a useful amount, send encoding */
- encode = runlen + ' ' - 4;
- put_debug_char(*src); checksum += *src;
- put_debug_char('*'); checksum += '*';
- put_debug_char(encode); checksum += encode;
- src += runlen;
- } else {
- /* Otherwise just send the current char */
- put_debug_char(*src); checksum += *src;
- src += 1;
- }
- }
-
- /* '#' Separator, put high and low components of checksum */
- put_debug_char('#');
- put_debug_char(highhex(checksum));
- put_debug_char(lowhex(checksum));
- }
- while ((get_debug_char()) != '+'); /* While no ack */
-}
-
-/* A bus error has occurred - perform a longjmp to return execution and
- allow handling of the error */
-static void kgdb_handle_bus_error(void)
-{
- longjmp(rem_com_env, 1);
-}
-
-/* Translate SH-3/4 exception numbers to unix-like signal values */
-static int compute_signal(const int excep_code)
-{
- int sigval;
-
- switch (excep_code) {
-
- case INVALID_INSN_VEC:
- case INVALID_SLOT_VEC:
- sigval = SIGILL;
- break;
- case ADDRESS_ERROR_LOAD_VEC:
- case ADDRESS_ERROR_STORE_VEC:
- sigval = SIGSEGV;
- break;
-
- case SERIAL_BREAK_VEC:
- case NMI_VEC:
- sigval = SIGINT;
- break;
-
- case USER_BREAK_VEC:
- case TRAP_VEC:
- sigval = SIGTRAP;
- break;
-
- default:
- sigval = SIGBUS; /* "software generated" */
- break;
- }
-
- return (sigval);
-}
-
-/* Make a local copy of the registers passed into the handler (bletch) */
-static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs,
- int *gdb_regs)
-{
- gdb_regs[R0] = regs->regs[R0];
- gdb_regs[R1] = regs->regs[R1];
- gdb_regs[R2] = regs->regs[R2];
- gdb_regs[R3] = regs->regs[R3];
- gdb_regs[R4] = regs->regs[R4];
- gdb_regs[R5] = regs->regs[R5];
- gdb_regs[R6] = regs->regs[R6];
- gdb_regs[R7] = regs->regs[R7];
- gdb_regs[R8] = regs->regs[R8];
- gdb_regs[R9] = regs->regs[R9];
- gdb_regs[R10] = regs->regs[R10];
- gdb_regs[R11] = regs->regs[R11];
- gdb_regs[R12] = regs->regs[R12];
- gdb_regs[R13] = regs->regs[R13];
- gdb_regs[R14] = regs->regs[R14];
- gdb_regs[R15] = regs->regs[R15];
- gdb_regs[PC] = regs->pc;
- gdb_regs[PR] = regs->pr;
- gdb_regs[GBR] = regs->gbr;
- gdb_regs[MACH] = regs->mach;
- gdb_regs[MACL] = regs->macl;
- gdb_regs[SR] = regs->sr;
- gdb_regs[VBR] = regs->vbr;
-}
-
-/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
-static void gdb_regs_to_kgdb_regs(const int *gdb_regs,
- struct kgdb_regs *regs)
-{
- regs->regs[R0] = gdb_regs[R0];
- regs->regs[R1] = gdb_regs[R1];
- regs->regs[R2] = gdb_regs[R2];
- regs->regs[R3] = gdb_regs[R3];
- regs->regs[R4] = gdb_regs[R4];
- regs->regs[R5] = gdb_regs[R5];
- regs->regs[R6] = gdb_regs[R6];
- regs->regs[R7] = gdb_regs[R7];
- regs->regs[R8] = gdb_regs[R8];
- regs->regs[R9] = gdb_regs[R9];
- regs->regs[R10] = gdb_regs[R10];
- regs->regs[R11] = gdb_regs[R11];
- regs->regs[R12] = gdb_regs[R12];
- regs->regs[R13] = gdb_regs[R13];
- regs->regs[R14] = gdb_regs[R14];
- regs->regs[R15] = gdb_regs[R15];
- regs->pc = gdb_regs[PC];
- regs->pr = gdb_regs[PR];
- regs->gbr = gdb_regs[GBR];
- regs->mach = gdb_regs[MACH];
- regs->macl = gdb_regs[MACL];
- regs->sr = gdb_regs[SR];
- regs->vbr = gdb_regs[VBR];
-}
-
-#ifdef CONFIG_KGDB_THREAD
-/* Make a local copy of registers from the specified thread */
-asmlinkage void ret_from_fork(void);
-static void thread_regs_to_gdb_regs(const struct task_struct *thread,
- int *gdb_regs)
-{
- int regno;
- int *tregs;
-
- /* Initialize to zero */
- for (regno = 0; regno < MAXREG; regno++)
- gdb_regs[regno] = 0;
-
- /* Just making sure... */
- if (thread == NULL)
- return;
-
- /* A new fork has pt_regs on the stack from a fork() call */
- if (thread->thread.pc == (unsigned long)ret_from_fork) {
-
- int vbr_val;
- struct pt_regs *kregs;
- kregs = (struct pt_regs*)thread->thread.sp;
-
- gdb_regs[R0] = kregs->regs[R0];
- gdb_regs[R1] = kregs->regs[R1];
- gdb_regs[R2] = kregs->regs[R2];
- gdb_regs[R3] = kregs->regs[R3];
- gdb_regs[R4] = kregs->regs[R4];
- gdb_regs[R5] = kregs->regs[R5];
- gdb_regs[R6] = kregs->regs[R6];
- gdb_regs[R7] = kregs->regs[R7];
- gdb_regs[R8] = kregs->regs[R8];
- gdb_regs[R9] = kregs->regs[R9];
- gdb_regs[R10] = kregs->regs[R10];
- gdb_regs[R11] = kregs->regs[R11];
- gdb_regs[R12] = kregs->regs[R12];
- gdb_regs[R13] = kregs->regs[R13];
- gdb_regs[R14] = kregs->regs[R14];
- gdb_regs[R15] = kregs->regs[R15];
- gdb_regs[PC] = kregs->pc;
- gdb_regs[PR] = kregs->pr;
- gdb_regs[GBR] = kregs->gbr;
- gdb_regs[MACH] = kregs->mach;
- gdb_regs[MACL] = kregs->macl;
- gdb_regs[SR] = kregs->sr;
-
- asm("stc vbr, %0":"=r"(vbr_val));
- gdb_regs[VBR] = vbr_val;
- return;
- }
-
- /* Otherwise, we have only some registers from switch_to() */
- tregs = (int *)thread->thread.sp;
- gdb_regs[R15] = (int)tregs;
- gdb_regs[R14] = *tregs++;
- gdb_regs[R13] = *tregs++;
- gdb_regs[R12] = *tregs++;
- gdb_regs[R11] = *tregs++;
- gdb_regs[R10] = *tregs++;
- gdb_regs[R9] = *tregs++;
- gdb_regs[R8] = *tregs++;
- gdb_regs[PR] = *tregs++;
- gdb_regs[GBR] = *tregs++;
- gdb_regs[PC] = thread->thread.pc;
-}
-#endif /* CONFIG_KGDB_THREAD */
-
-/* Calculate the new address for after a step */
-static short *get_step_address(void)
-{
- short op = *(short *) trap_registers.pc;
- long addr;
-
- /* BT */
- if (OPCODE_BT(op)) {
- if (trap_registers.sr & SR_T_BIT_MASK)
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 2;
- }
-
- /* BTS */
- else if (OPCODE_BTS(op)) {
- if (trap_registers.sr & SR_T_BIT_MASK)
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 4; /* Not in delay slot */
- }
-
- /* BF */
- else if (OPCODE_BF(op)) {
- if (!(trap_registers.sr & SR_T_BIT_MASK))
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 2;
- }
-
- /* BFS */
- else if (OPCODE_BFS(op)) {
- if (!(trap_registers.sr & SR_T_BIT_MASK))
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 4; /* Not in delay slot */
- }
-
- /* BRA */
- else if (OPCODE_BRA(op))
- addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
-
- /* BRAF */
- else if (OPCODE_BRAF(op))
- addr = trap_registers.pc + 4
- + trap_registers.regs[OPCODE_BRAF_REG(op)];
-
- /* BSR */
- else if (OPCODE_BSR(op))
- addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
-
- /* BSRF */
- else if (OPCODE_BSRF(op))
- addr = trap_registers.pc + 4
- + trap_registers.regs[OPCODE_BSRF_REG(op)];
-
- /* JMP */
- else if (OPCODE_JMP(op))
- addr = trap_registers.regs[OPCODE_JMP_REG(op)];
-
- /* JSR */
- else if (OPCODE_JSR(op))
- addr = trap_registers.regs[OPCODE_JSR_REG(op)];
-
- /* RTS */
- else if (OPCODE_RTS(op))
- addr = trap_registers.pr;
-
- /* RTE */
- else if (OPCODE_RTE(op))
- addr = trap_registers.regs[15];
-
- /* Other */
- else
- addr = trap_registers.pc + 2;
-
- kgdb_flush_icache_range(addr, addr + 2);
- return (short *) addr;
-}
-
-/* Set up a single-step. Replace the instruction immediately after the
- current instruction (i.e. next in the expected flow of control) with a
- trap instruction, so that returning will cause only a single instruction
- to be executed. Note that this model is slightly broken for instructions
- with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch
- and the instruction in the delay slot will be executed. */
-static void do_single_step(void)
-{
- unsigned short *addr = 0;
-
- /* Determine where the target instruction will send us to */
- addr = get_step_address();
- stepped_address = (int)addr;
-
- /* Replace it */
- stepped_opcode = *(short *)addr;
- *addr = STEP_OPCODE;
-
- /* Flush and return */
- kgdb_flush_icache_range((long) addr, (long) addr + 2);
- return;
-}
-
-/* Undo a single step */
-static void undo_single_step(void)
-{
- /* If we have stepped, put back the old instruction */
- /* Use stepped_address in case we stopped elsewhere */
- if (stepped_opcode != 0) {
- *(short*)stepped_address = stepped_opcode;
- kgdb_flush_icache_range(stepped_address, stepped_address + 2);
- }
- stepped_opcode = 0;
-}
-
-/* Send a signal message */
-static void send_signal_msg(const int signum)
-{
-#ifndef CONFIG_KGDB_THREAD
- out_buffer[0] = 'S';
- out_buffer[1] = highhex(signum);
- out_buffer[2] = lowhex(signum);
- out_buffer[3] = 0;
- put_packet(out_buffer);
-#else /* CONFIG_KGDB_THREAD */
- int threadid;
- threadref thref;
- char *out = out_buffer;
- const char *tstring = "thread";
-
- *out++ = 'T';
- *out++ = highhex(signum);
- *out++ = lowhex(signum);
-
- while (*tstring) {
- *out++ = *tstring++;
- }
- *out++ = ':';
-
- threadid = trapped_thread->pid;
- if (threadid == 0) threadid = PID_MAX;
- int_to_threadref(&thref, threadid);
- pack_threadid(out, &thref);
- out += BUF_THREAD_ID_SIZE;
- *out++ = ';';
-
- *out = 0;
- put_packet(out_buffer);
-#endif /* CONFIG_KGDB_THREAD */
-}
-
-/* Reply that all was well */
-static void send_ok_msg(void)
-{
- strcpy(out_buffer, "OK");
- put_packet(out_buffer);
-}
-
-/* Reply that an error occurred */
-static void send_err_msg(void)
-{
- strcpy(out_buffer, "E01");
- put_packet(out_buffer);
-}
-
-/* Empty message indicates unrecognised command */
-static void send_empty_msg(void)
-{
- put_packet("");
-}
-
-/* Read memory due to 'm' message */
-static void read_mem_msg(void)
-{
- char *ptr;
- int addr;
- int length;
-
- /* Jmp, disable bus error handler */
- if (setjmp(rem_com_env) == 0) {
-
- kgdb_nofault = 1;
-
- /* Walk through, have m<addr>,<length> */
- 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<addr>,<length>:<data> */
- ptr = &in_buffer[1];
- if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
- if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) {
- if (binary)
- ebin_to_mem(ptr, (char*)addr, length);
- else
- hex_to_mem(ptr, (char*)addr, length);
- kgdb_flush_icache_range(addr, addr + length);
- ptr = 0;
- send_ok_msg();
- }
- if (ptr)
- send_err_msg();
- } else
- send_err_msg();
-
- /* Restore bus error handler */
- kgdb_nofault = 0;
-}
-
-/* Continue message */
-static void continue_msg(void)
-{
- /* Try to read optional parameter, PC unchanged if none */
- char *ptr = &in_buffer[1];
- int addr;
-
- if (hex_to_int(&ptr, &addr))
- trap_registers.pc = addr;
-}
-
-/* Continue message with signal */
-static void continue_with_sig_msg(void)
-{
- int signal;
- char *ptr = &in_buffer[1];
- int addr;
-
- /* Report limitation */
- kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n");
-
- /* Signal */
- hex_to_int(&ptr, &signal);
- if (*ptr == ';')
- ptr++;
-
- /* Optional address */
- if (hex_to_int(&ptr, &addr))
- trap_registers.pc = addr;
-}
-
-/* Step message */
-static void step_msg(void)
-{
- continue_msg();
- do_single_step();
-}
-
-/* Step message with signal */
-static void step_with_sig_msg(void)
-{
- continue_with_sig_msg();
- do_single_step();
-}
-
-/* Send register contents */
-static void send_regs_msg(void)
-{
-#ifdef CONFIG_KGDB_THREAD
- if (!current_thread)
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
- else
- thread_regs_to_gdb_regs(current_thread, registers);
-#else
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
-#endif
-
- mem_to_hex((char *) registers, out_buffer, NUMREGBYTES);
- put_packet(out_buffer);
-}
-
-/* Set register contents - currently can't set other thread's registers */
-static void set_regs_msg(void)
-{
-#ifdef CONFIG_KGDB_THREAD
- if (!current_thread) {
-#endif
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
- hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES);
- gdb_regs_to_kgdb_regs(registers, &trap_registers);
- send_ok_msg();
-#ifdef CONFIG_KGDB_THREAD
- } else
- send_err_msg();
-#endif
-}
-
-
-#ifdef CONFIG_KGDB_THREAD
-
-/* Set the status for a thread */
-void set_thread_msg(void)
-{
- int threadid;
- struct task_struct *thread = NULL;
- char *ptr;
-
- switch (in_buffer[1]) {
-
- /* To select which thread for gG etc messages, i.e. supported */
- case 'g':
-
- ptr = &in_buffer[2];
- hex_to_int(&ptr, &threadid);
- thread = get_thread(threadid);
-
- /* If we haven't found it */
- if (!thread) {
- send_err_msg();
- break;
- }
-
- /* Set current_thread (or not) */
- if (thread == trapped_thread)
- current_thread = NULL;
- else
- current_thread = thread;
- send_ok_msg();
- break;
-
- /* To select which thread for cCsS messages, i.e. unsupported */
- case 'c':
- send_ok_msg();
- break;
-
- default:
- send_empty_msg();
- break;
- }
-}
-
-/* Is a thread alive? */
-static void thread_status_msg(void)
-{
- char *ptr;
- int threadid;
- struct task_struct *thread = NULL;
-
- ptr = &in_buffer[1];
- hex_to_int(&ptr, &threadid);
- thread = get_thread(threadid);
- if (thread)
- send_ok_msg();
- else
- send_err_msg();
-}
-/* Send the current thread ID */
-static void thread_id_msg(void)
-{
- int threadid;
- threadref thref;
-
- out_buffer[0] = 'Q';
- out_buffer[1] = 'C';
-
- if (current_thread)
- threadid = current_thread->pid;
- else if (trapped_thread)
- threadid = trapped_thread->pid;
- else /* Impossible, but just in case! */
- {
- send_err_msg();
- return;
- }
-
- /* Translate pid 0 to PID_MAX for gdb */
- if (threadid == 0) threadid = PID_MAX;
-
- int_to_threadref(&thref, threadid);
- pack_threadid(out_buffer + 2, &thref);
- out_buffer[2 + BUF_THREAD_ID_SIZE] = '\0';
- put_packet(out_buffer);
-}
-
-/* Send thread info */
-static void thread_info_msg(void)
-{
- struct task_struct *thread = NULL;
- int threadid;
- char *pos;
- threadref thref;
-
- /* Start with 'm' */
- out_buffer[0] = 'm';
- pos = &out_buffer[1];
-
- /* For all possible thread IDs - this will overrun if > 44 threads! */
- /* Start at 1 and include PID_MAX (since GDB won't use pid 0...) */
- for (threadid = 1; threadid <= PID_MAX; threadid++) {
-
- read_lock(&tasklist_lock);
- thread = get_thread(threadid);
- read_unlock(&tasklist_lock);
-
- /* If it's a valid thread */
- if (thread) {
- int_to_threadref(&thref, threadid);
- pack_threadid(pos, &thref);
- pos += BUF_THREAD_ID_SIZE;
- *pos++ = ',';
- }
- }
- *--pos = 0; /* Lose final comma */
- put_packet(out_buffer);
-
-}
-
-/* Return printable info for gdb's 'info threads' command */
-static void thread_extra_info_msg(void)
-{
- int threadid;
- struct task_struct *thread = NULL;
- char buffer[20], *ptr;
- int i;
-
- /* Extract thread ID */
- ptr = &in_buffer[17];
- hex_to_int(&ptr, &threadid);
- thread = get_thread(threadid);
-
- /* If we don't recognise it, say so */
- if (thread == NULL)
- strcpy(buffer, "(unknown)");
- else
- strcpy(buffer, thread->comm);
-
- /* Construct packet */
- for (i = 0, ptr = out_buffer; buffer[i]; i++)
- ptr = pack_hex_byte(ptr, buffer[i]);
-
- if (thread->thread.pc == (unsigned long)ret_from_fork) {
- strcpy(buffer, "<new fork>");
- for (i = 0; buffer[i]; i++)
- ptr = pack_hex_byte(ptr, buffer[i]);
- }
-
- *ptr = '\0';
- put_packet(out_buffer);
-}
-
-/* Handle all qFooBarBaz messages - have to use an if statement as
- opposed to a switch because q messages can have > 1 char id. */
-static void query_msg(void)
-{
- const char *q_start = &in_buffer[1];
-
- /* qC = return current thread ID */
- if (strncmp(q_start, "C", 1) == 0)
- thread_id_msg();
-
- /* qfThreadInfo = query all threads (first) */
- else if (strncmp(q_start, "fThreadInfo", 11) == 0)
- thread_info_msg();
-
- /* qsThreadInfo = query all threads (subsequent). We know we have sent
- them all after the qfThreadInfo message, so there are no to send */
- else if (strncmp(q_start, "sThreadInfo", 11) == 0)
- put_packet("l"); /* el = last */
-
- /* qThreadExtraInfo = supply printable information per thread */
- else if (strncmp(q_start, "ThreadExtraInfo", 15) == 0)
- thread_extra_info_msg();
-
- /* Unsupported - empty message as per spec */
- else
- send_empty_msg();
-}
-#endif /* CONFIG_KGDB_THREAD */
-
-/*
- * Bring up the ports..
- */
-static int kgdb_serial_setup(void)
-{
- extern int kgdb_console_setup(struct console *co, char *options);
- struct console dummy;
-
- kgdb_console_setup(&dummy, 0);
-
- return 0;
-}
-
-/* The command loop, read and act on requests */
-static void kgdb_command_loop(const int excep_code, const int trapa_value)
-{
- int sigval;
-
- if (excep_code == NMI_VEC) {
-#ifndef CONFIG_KGDB_NMI
- KGDB_PRINTK("Ignoring unexpected NMI?\n");
- return;
-#else /* CONFIG_KGDB_NMI */
- if (!kgdb_enabled) {
- kgdb_enabled = 1;
- kgdb_init();
- }
-#endif /* CONFIG_KGDB_NMI */
- }
-
- /* Ignore if we're disabled */
- if (!kgdb_enabled)
- return;
-
-#ifdef CONFIG_KGDB_THREAD
- /* Until GDB specifies a thread */
- current_thread = NULL;
- trapped_thread = current;
-#endif
-
- /* Enter GDB mode (e.g. after detach) */
- if (!kgdb_in_gdb_mode) {
- /* Do serial setup, notify user, issue preemptive ack */
- kgdb_serial_setup();
- KGDB_PRINTK("Waiting for GDB (on %s%d at %d baud)\n",
- (kgdb_porttype ? kgdb_porttype->name : ""),
- kgdb_portnum, kgdb_baud);
- kgdb_in_gdb_mode = 1;
- put_debug_char('+');
- }
-
- /* Reply to host that an exception has occurred */
- sigval = compute_signal(excep_code);
- send_signal_msg(sigval);
-
- /* TRAP_VEC exception indicates a software trap inserted in place of
- code by GDB so back up PC by one instruction, as this instruction
- will later be replaced by its original one. Do NOT do this for
- trap 0xff, since that indicates a compiled-in breakpoint which
- will not be replaced (and we would retake the trap forever) */
- if ((excep_code == TRAP_VEC) && (trapa_value != (0xff << 2))) {
- trap_registers.pc -= 2;
- }
-
- /* Undo any stepping we may have done */
- undo_single_step();
-
- while (1) {
-
- out_buffer[0] = 0;
- get_packet(in_buffer, BUFMAX);
-
- /* Examine first char of buffer to see what we need to do */
- switch (in_buffer[0]) {
-
- case '?': /* Send which signal we've received */
- send_signal_msg(sigval);
- break;
-
- case 'g': /* Return the values of the CPU registers */
- send_regs_msg();
- break;
-
- case 'G': /* Set the value of the CPU registers */
- set_regs_msg();
- break;
-
- case 'm': /* Read LLLL bytes address AA..AA */
- read_mem_msg();
- break;
-
- case 'M': /* Write LLLL bytes address AA..AA, ret OK */
- write_mem_msg(0); /* 0 = data in hex */
- break;
-
- case 'X': /* Write LLLL bytes esc bin address AA..AA */
- if (kgdb_bits == '8')
- write_mem_msg(1); /* 1 = data in binary */
- else
- send_empty_msg();
- break;
-
- case 'C': /* Continue, signum included, we ignore it */
- continue_with_sig_msg();
- return;
-
- case 'c': /* Continue at address AA..AA (optional) */
- continue_msg();
- return;
-
- case 'S': /* Step, signum included, we ignore it */
- step_with_sig_msg();
- return;
-
- case 's': /* Step one instruction from AA..AA */
- step_msg();
- return;
-
-#ifdef CONFIG_KGDB_THREAD
-
- case 'H': /* Task related */
- set_thread_msg();
- break;
-
- case 'T': /* Query thread status */
- thread_status_msg();
- break;
-
- case 'q': /* Handle query - currently thread-related */
- query_msg();
- break;
-#endif
-
- case 'k': /* 'Kill the program' with a kernel ? */
- break;
-
- case 'D': /* Detach from program, send reply OK */
- kgdb_in_gdb_mode = 0;
- send_ok_msg();
- get_debug_char();
- return;
-
- default:
- send_empty_msg();
- break;
- }
- }
-}
-
-/* There has been an exception, most likely a breakpoint. */
-void kgdb_handle_exception(struct pt_regs *regs)
-{
- int excep_code, vbr_val;
- int count;
- int trapa_value = ctrl_inl(TRA);
-
- /* Copy kernel regs (from stack) */
- for (count = 0; count < 16; count++)
- trap_registers.regs[count] = regs->regs[count];
- trap_registers.pc = regs->pc;
- trap_registers.pr = regs->pr;
- trap_registers.sr = regs->sr;
- trap_registers.gbr = regs->gbr;
- trap_registers.mach = regs->mach;
- trap_registers.macl = regs->macl;
-
- asm("stc vbr, %0":"=r"(vbr_val));
- trap_registers.vbr = vbr_val;
-
- /* Get excode for command loop call, user access */
- asm("stc r2_bank, %0":"=r"(excep_code));
- kgdb_excode = excep_code;
-
- /* Other interesting environment items for reference */
- asm("stc r6_bank, %0":"=r"(kgdb_g_imask));
- kgdb_current = current;
- kgdb_trapa_val = trapa_value;
-
- /* Act on the exception */
- kgdb_command_loop(excep_code >> 5, trapa_value);
-
- kgdb_current = NULL;
-
- /* Copy back the (maybe modified) registers */
- for (count = 0; count < 16; count++)
- regs->regs[count] = trap_registers.regs[count];
- regs->pc = trap_registers.pc;
- regs->pr = trap_registers.pr;
- regs->sr = trap_registers.sr;
- regs->gbr = trap_registers.gbr;
- regs->mach = trap_registers.mach;
- regs->macl = trap_registers.macl;
-
- vbr_val = trap_registers.vbr;
- asm("ldc %0, vbr": :"r"(vbr_val));
-
- return;
-}
-
-/* Trigger a breakpoint by function */
-void breakpoint(void)
-{
- if (!kgdb_enabled) {
- kgdb_enabled = 1;
- kgdb_init();
- }
- BREAKPOINT();
-}
-
-/* Initialise the KGDB data structures and serial configuration */
-int kgdb_init(void)
-{
- if (!kgdb_enabled)
- return 1;
-
- in_nmi = 0;
- kgdb_nofault = 0;
- stepped_opcode = 0;
- kgdb_in_gdb_mode = 0;
-
- if (kgdb_serial_setup() != 0) {
- KGDB_PRINTK("serial setup error\n");
- return -1;
- }
-
- /* Init ptr to exception handler */
- kgdb_debug_hook = kgdb_handle_exception;
- kgdb_bus_err_hook = kgdb_handle_bus_error;
-
- /* Enter kgdb now if requested, or just report init done */
- if (kgdb_halt) {
- kgdb_in_gdb_mode = 1;
- put_debug_char('+');
- breakpoint();
- }
- else
- {
- KGDB_PRINTK("stub is initialized.\n");
- }
-
- return 0;
-}
-
-/* Make function available for "user messages"; console will use it too. */
-
-char gdbmsgbuf[BUFMAX];
-#define MAXOUT ((BUFMAX-2)/2)
-
-static void kgdb_msg_write(const char *s, unsigned count)
-{
- int i;
- int wcount;
- char *bufptr;
-
- /* 'O'utput */
- gdbmsgbuf[0] = 'O';
-
- /* Fill and send buffers... */
- while (count > 0) {
- bufptr = gdbmsgbuf + 1;
-
- /* Calculate how many this time */
- wcount = (count > MAXOUT) ? MAXOUT : count;
-
- /* Pack in hex chars */
- for (i = 0; i < wcount; i++)
- bufptr = pack_hex_byte(bufptr, s[i]);
- *bufptr = '\0';
-
- /* Move up */
- s += wcount;
- count -= wcount;
-
- /* Write packet */
- put_packet(gdbmsgbuf);
- }
-}
-
-static void kgdb_to_gdb(const char *s)
-{
- kgdb_msg_write(s, strlen(s));
-}
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-void kgdb_console_write(struct console *co, const char *s, unsigned count)
-{
- /* Bail if we're not talking to GDB */
- if (!kgdb_in_gdb_mode)
- return;
-
- kgdb_msg_write(s, count);
-}
-#endif
Index: linux-2.6.13/arch/sh/kernel/Makefile
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/Makefile
+++ linux-2.6.13/arch/sh/kernel/Makefile
@@ -13,7 +13,7 @@ obj-y += cpu/
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 kgdb-jmp.o
obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
Index: linux-2.6.13/arch/sh/kernel/setup.c
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/setup.c
+++ linux-2.6.13/arch/sh/kernel/setup.c
@@ -27,10 +27,6 @@
#include <asm/irq.h>
#include <asm/setup.h>

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-static int kgdb_parse_options(char *options);
-#endif
extern void * __rd_start, * __rd_end;
/*
* Machine setup..
@@ -557,93 +553,3 @@ struct seq_operations cpuinfo_op = {
.show = show_cpuinfo,
};
#endif /* CONFIG_PROC_FS */
-
-#ifdef CONFIG_SH_KGDB
-/*
- * Parse command-line kgdb options. By default KGDB is enabled,
- * entered on error (or other action) using default serial info.
- * The command-line option can include a serial port specification
- * and an action to override default or configured behavior.
- */
-struct kgdb_sermap kgdb_sci_sermap =
-{ "ttySC", 5, kgdb_sci_setup, NULL };
-
-struct kgdb_sermap *kgdb_serlist = &kgdb_sci_sermap;
-struct kgdb_sermap *kgdb_porttype = &kgdb_sci_sermap;
-
-void kgdb_register_sermap(struct kgdb_sermap *map)
-{
- struct kgdb_sermap *last;
-
- for (last = kgdb_serlist; last->next; last = last->next)
- ;
- last->next = map;
- if (!map->namelen) {
- map->namelen = strlen(map->name);
- }
-}
-
-static int __init kgdb_parse_options(char *options)
-{
- char c;
- int baud;
-
- /* Check for port spec (or use default) */
-
- /* Determine port type and instance */
- if (!memcmp(options, "tty", 3)) {
- struct kgdb_sermap *map = kgdb_serlist;
-
- while (map && memcmp(options, map->name, map->namelen))
- map = map->next;
-
- if (!map) {
- KGDB_PRINTK("unknown port spec in %s\n", options);
- return -1;
- }
-
- kgdb_porttype = map;
- kgdb_serial_setup = map->setup_fn;
- kgdb_portnum = options[map->namelen] - '0';
- options += map->namelen + 1;
-
- options = (*options == ',') ? options+1 : options;
-
- /* Read optional parameters (baud/parity/bits) */
- baud = simple_strtoul(options, &options, 10);
- if (baud != 0) {
- kgdb_baud = baud;
-
- c = toupper(*options);
- if (c == 'E' || c == 'O' || c == 'N') {
- kgdb_parity = c;
- options++;
- }
-
- c = *options;
- if (c == '7' || c == '8') {
- kgdb_bits = c;
- options++;
- }
- options = (*options == ',') ? options+1 : options;
- }
- }
-
- /* Check for action specification */
- if (!memcmp(options, "halt", 4)) {
- kgdb_halt = 1;
- options += 4;
- } else if (!memcmp(options, "disabled", 8)) {
- kgdb_enabled = 0;
- options += 8;
- }
-
- if (*options) {
- KGDB_PRINTK("ignored unknown options: %s\n", options);
- return 0;
- }
- return 1;
-}
-__setup("kgdb=", kgdb_parse_options);
-#endif /* CONFIG_SH_KGDB */
-
Index: linux-2.6.13/arch/sh/kernel/time.c
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/time.c
+++ linux-2.6.13/arch/sh/kernel/time.c
@@ -34,9 +34,6 @@
#include <asm/rtc.h>
#include <asm/freq.h>
#include <asm/cpu/timer.h>
-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#endif

#include <linux/timex.h>
#include <linux/irq.h>
@@ -646,12 +643,4 @@ void __init time_init(void)
ctrl_outl(interval, TMU0_TCOR);
ctrl_outl(interval, TMU0_TCNT);
ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
-
-#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
}
Index: linux-2.6.13/arch/sh/kernel/traps.c
===================================================================
--- linux-2.6.13.orig/arch/sh/kernel/traps.c
+++ linux-2.6.13/arch/sh/kernel/traps.c
@@ -27,6 +27,7 @@
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>

#include <asm/system.h>
#include <asm/uaccess.h>
@@ -35,17 +36,8 @@
#include <asm/processor.h>
#include <asm/sections.h>

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#define CHK_REMOTE_DEBUG(regs) \
-{ \
- if ((kgdb_debug_hook != (kgdb_debug_hook_t *) NULL) && (!user_mode(regs))) \
- { \
- (*kgdb_debug_hook)(regs); \
- } \
-}
-#else
-#define CHK_REMOTE_DEBUG(regs)
+#ifndef CONFIG_KGDB
+#define kgdb_handle_exception(t, s, e, r)
#endif

#define DO_ERROR(trapnr, signr, str, name, tsk) \
@@ -66,7 +58,7 @@ asmlinkage void do_##name(unsigned long
local_irq_enable(); \
tsk->thread.error_code = error_code; \
tsk->thread.trap_no = trapnr; \
- CHK_REMOTE_DEBUG(&regs); \
+ kgdb_handle_exception(trapnr, signr, error_code, &regs); \
force_sig(signr, tsk); \
die_if_no_fixup(str,&regs,error_code); \
}
@@ -93,10 +85,12 @@ void die(const char * str, struct pt_reg
{
static int die_counter;

+#ifdef CONFIG_KGDB
+ kgdb_handle_exception(1, SIGTRAP, err, regs);
+#endif
console_verbose();
spin_lock_irq(&die_lock);
printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
- CHK_REMOTE_DEBUG(regs);
show_regs(regs);
spin_unlock_irq(&die_lock);
do_exit(SIGSEGV);
Index: linux-2.6.13/arch/sh/Makefile
===================================================================
--- linux-2.6.13.orig/arch/sh/Makefile
+++ linux-2.6.13/arch/sh/Makefile
@@ -23,7 +23,6 @@ cflags-$(CONFIG_CPU_SH4) += -m4 \
$(call cc-option,-mno-implicit-fp,-m4-nofpu)

cflags-$(CONFIG_SH_DSP) += -Wa,-dsp
-cflags-$(CONFIG_SH_KGDB) += -g

cflags-$(CONFIG_MORE_COMPILE_OPTIONS) += \
$(shell echo $(CONFIG_COMPILE_OPTIONS) | sed -e 's/"//g')
Index: linux-2.6.13/arch/sh/mm/extable.c
===================================================================
--- linux-2.6.13.orig/arch/sh/mm/extable.c
+++ linux-2.6.13/arch/sh/mm/extable.c
@@ -6,6 +6,7 @@

#include <linux/config.h>
#include <linux/module.h>
+#include <linux/kgdb.h>
#include <asm/uaccess.h>

int fixup_exception(struct pt_regs *regs)
@@ -17,6 +18,12 @@ int fixup_exception(struct pt_regs *regs
regs->pc = fixup->fixup;
return 1;
}
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Never reached. */
+#endif

return 0;
}
Index: linux-2.6.13/arch/sh/mm/fault.c
===================================================================
--- linux-2.6.13.orig/arch/sh/mm/fault.c
+++ linux-2.6.13/arch/sh/mm/fault.c
@@ -28,7 +28,6 @@
#include <asm/pgalloc.h>
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
-#include <asm/kgdb.h>

extern void die(const char *,struct pt_regs *,long);

@@ -45,11 +44,6 @@ asmlinkage void do_page_fault(struct pt_
struct vm_area_struct * vma;
unsigned long page;

-#ifdef CONFIG_SH_KGDB
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
tsk = current;
mm = tsk->mm;

@@ -153,6 +147,7 @@ no_context:
}
die("Oops", regs, writeaccess);
do_exit(SIGKILL);
+ dump_stack();

/*
* We ran out of memory, or some other thing happened to us that made
@@ -199,11 +194,6 @@ asmlinkage int __do_page_fault(struct pt
pte_t *pte;
pte_t entry;

-#ifdef CONFIG_SH_KGDB
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
#ifdef CONFIG_SH_STORE_QUEUES
addrmax = P4SEG_STORE_QUE + 0x04000000;
#endif
Index: linux-2.6.13/arch/sh/mm/fault-nommu.c
===================================================================
--- linux-2.6.13.orig/arch/sh/mm/fault-nommu.c
+++ linux-2.6.13/arch/sh/mm/fault-nommu.c
@@ -29,10 +29,6 @@
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>

-#if defined(CONFIG_SH_KGDB)
-#include <asm/kgdb.h>
-#endif
-
extern void die(const char *,struct pt_regs *,long);

/*
@@ -43,11 +39,6 @@ extern void die(const char *,struct pt_r
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
unsigned long address)
{
-#if defined(CONFIG_SH_KGDB)
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
@@ -69,11 +60,6 @@ asmlinkage void do_page_fault(struct pt_
asmlinkage int __do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
unsigned long address)
{
-#if defined(CONFIG_SH_KGDB)
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
if (address >= TASK_SIZE)
return 1;

Index: linux-2.6.13/Documentation/DocBook/kgdb.tmpl
===================================================================
--- linux-2.6.13.orig/Documentation/DocBook/kgdb.tmpl
+++ linux-2.6.13/Documentation/DocBook/kgdb.tmpl
@@ -127,6 +127,10 @@
<constant>kgdbwait</constant> after this arguement.
</para>
<para>
+ To specify the values of the SH SCI(F) serial port at boot:
+ <constant>kgdbsci=0,115200</constant>.
+ </para>
+ <para>
To configure the <symbol>CONFIG_KGDB_ETH</symbol> driver, pass in
<constant>kgdboe=[src-port]@&lt;src-ip&gt;/[dev],[tgt-port]@&lt;tgt-ip&gt;/[tgt-macaddr]</constant>
where:
@@ -168,6 +172,18 @@
application program.
</para>
</chapter>
+ <chapter id="ArchitectureNotes">
+ <title>Architecture specific notes</title>
+ <para>
+ SuperH: The NMI switch found on some boards can be used to trigger an
+ initial breakpoint. Subsequent triggers do nothing. If console
+ is enabled on the SCI(F) serial port, and that is the port being used
+ for KGDB, then you must trigger a breakpoint via sysrq, NMI, or
+ some other method prior to connecting, or echo a control-c to the
+ serial port. Also, to use the SCI(F) port for KGDB, the
+ <symbol>CONFIG_SERIAL_SH_SCI</symbol> driver must be enabled.
+ </para>
+ </chapter>
<chapter id="CommonBackEndReq">
<title>The common backend (required)</title>
<para>
Index: linux-2.6.13/drivers/serial/sh-sci.c
===================================================================
--- linux-2.6.13.orig/drivers/serial/sh-sci.c
+++ linux-2.6.13/drivers/serial/sh-sci.c
@@ -42,6 +42,7 @@
#include <linux/delay.h>
#include <linux/console.h>
#include <linux/bitops.h>
+#include <linux/kgdb.h>

#ifdef CONFIG_CPU_FREQ
#include <linux/notifier.h>
@@ -65,14 +66,34 @@

#include "sh-sci.h"

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
+#ifdef CONFIG_KGDB_SH_SCI
+/* Speed of the UART. */
+#if defined(CONFIG_KGDB_9600BAUD)
+static int kgdbsci_baud = 9600;
+#elif defined(CONFIG_KGDB_19200BAUD)
+static int kgdbsci_baud = 19200;
+#elif defined(CONFIG_KGDB_38400BAUD)
+static int kgdbsci_baud = 38400;
+#elif defined(CONFIG_KGDB_57600BAUD)
+static int kgdbsci_baud = 57600;
+#else
+static int kgdbsci_baud = 115200; /* Start with this if not given */
+#endif

-static int kgdb_get_char(struct sci_port *port);
-static void kgdb_put_char(struct sci_port *port, char c);
-static void kgdb_handle_error(struct sci_port *port);
-static struct sci_port *kgdb_sci_port;
-#endif /* CONFIG_SH_KGDB */
+/* Index of the UART, matches ttySCX naming. */
+#if defined(CONFIG_KGDB_SERIAL_PORT_1)
+static int kgdbsci_ttySC = 1;
+#elif defined(CONFIG_KGDB_SERIAL_PORT_2)
+static int kgdbsci_ttySC = 2;
+#elif defined(CONFIG_KGDB_SERIAL_PORT_3)
+static int kgdbsci_ttySC = 3;
+#else
+static int kgdbsci_ttySC = 0; /* Start with this if not given */
+#endif
+
+/* Make life easier on us. */
+#define KGDBPORT sci_ports[kgdbsci_ttySC]
+#endif /* CONFIG_KGDB_SH_SCI */

#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static struct sci_port *serial_console_port = 0;
@@ -85,18 +106,15 @@ static void sci_start_rx(struct uart_por
static void sci_stop_rx(struct uart_port *port);
static int sci_request_irq(struct sci_port *port);
static void sci_free_irq(struct sci_port *port);
+static void sci_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old);
+static int kgdbsci_init(void);

static struct sci_port sci_ports[SCI_NPORTS];
static struct uart_driver sci_uart_driver;

-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
-
-static void handle_error(struct uart_port *port)
-{ /* Clear error flags */
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
-}
-
-static int get_char(struct uart_port *port)
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_KGDB_SH_SCI)
+static int get_char_for_gdb(struct uart_port *port)
{
unsigned long flags;
unsigned short status;
@@ -106,7 +124,8 @@ static int get_char(struct uart_port *po
do {
status = sci_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) {
- handle_error(port);
+ /* Clear error flags. */
+ sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
continue;
}
} while (!(status & SCxSR_RDxF(port)));
@@ -117,21 +136,7 @@ static int get_char(struct uart_port *po

return c;
}
-
-/* Taken from sh-stub.c of GDB 4.18 */
-static const char hexchars[] = "0123456789abcdef";
-
-static __inline__ char highhex(int x)
-{
- return hexchars[(x >> 4) & 0xf];
-}
-
-static __inline__ char lowhex(int x)
-{
- return hexchars[x & 0xf];
-}
-
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_KGDB_SH_SCI */

/*
* Send the packet in buffer. The host gets one chance to read it.
@@ -163,21 +168,14 @@ static void put_string(struct sci_port *
const unsigned char *p = buffer;
int i;

-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+#ifdef CONFIG_SH_STANDARD_BIOS
int checksum;
- int usegdb=0;
+ const char hexchars[] = "0123456789abcdef";

-#ifdef CONFIG_SH_STANDARD_BIOS
/* This call only does a trap the first time it is
* called, and so is safe to do here unconditionally
*/
- usegdb |= sh_bios_in_gdb_mode();
-#endif
-#ifdef CONFIG_SH_KGDB
- usegdb |= (kgdb_in_gdb_mode && (port == kgdb_sci_port));
-#endif
-
- if (usegdb) {
+ if (sh_bios_in_gdb_mode()) {
/* $<packet info>#<checksum>. */
do {
unsigned char c;
@@ -189,18 +187,18 @@ static void put_string(struct sci_port *
int h, l;

c = *p++;
- h = highhex(c);
- l = lowhex(c);
+ h = hexchars[c >> 4];
+ l = hexchars[c % 16];
put_char(port, h);
put_char(port, l);
checksum += h + l;
}
put_char(port, '#');
- put_char(port, highhex(checksum));
- put_char(port, lowhex(checksum));
+ put_char(port, hexchars[checksum >> 4]);
+ put_char(port, hexchars[checksum & 16]);
} while (get_char(port) != '+');
} else
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS */
for (i=0; i<count; i++) {
if (*p == 10)
put_char(port, '\r');
@@ -210,90 +208,163 @@ static void put_string(struct sci_port *
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */


-#ifdef CONFIG_SH_KGDB
-
-/* Is the SCI ready, ie is there a char waiting? */
-static int kgdb_is_char_ready(struct sci_port *port)
+#ifdef CONFIG_KGDB_SH_SCI
+static int kgdbsci_read_char(void)
{
- unsigned short status = sci_in(port, SCxSR);
-
- if (status & (SCxSR_ERRORS(port) | SCxSR_BRK(port)))
- kgdb_handle_error(port);
-
- return (status & SCxSR_RDxF(port));
+ return get_char_for_gdb(&KGDBPORT.port);
}

-/* Write a char */
-static void kgdb_put_char(struct sci_port *port, char c)
+/* Called from kgdbstub.c to put a character, just a wrapper */
+static void kgdbsci_write_char(int c)
{
- unsigned short status;
-
- do
- status = sci_in(port, SCxSR);
- while (!(status & SCxSR_TDxE(port)));
+ unsigned short status;

- sci_out(port, SCxTDR, c);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+ do
+ status = sci_in(&KGDBPORT.port, SCxSR);
+ while (!(status & SCxSR_TDxE(&KGDBPORT.port)));
+
+ sci_out(&KGDBPORT.port, SCxTDR, c);
+ sci_in(&KGDBPORT.port, SCxSR); /* Dummy read */
+ sci_out(&KGDBPORT.port, SCxSR, SCxSR_TDxE_CLEAR(&KGDBPORT.port));
}

-/* Get a char if there is one, else ret -1 */
-static int kgdb_get_char(struct sci_port *port)
+#ifndef CONFIG_SERIAL_SH_SCI_CONSOLE
+/* If we don't have console, we never hookup IRQs. But we need to
+ * hookup one so that we can interrupt the system.
+ */
+static irqreturn_t kgdbsci_rx_interrupt(int irq, void *ptr,
+ struct pt_regs *regs)
{
- int c;
-
- if (kgdb_is_char_ready(port) == 0)
- c = -1;
- else {
- c = sci_in(port, SCxRDR);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- }
+ struct uart_port *port = ptr;

- return c;
-}
+ if (!(sci_in(port, SCxSR) & SCxSR_RDxF(port)))
+ return IRQ_NONE;

-/* Called from kgdbstub.c to get a character, i.e. is blocking */
-static int kgdb_sci_getchar(void)
-{
- volatile int c;
+ if (kgdb_io_ops.init != kgdbsci_init) {
+ /* Throw away the data if another I/O routine is active */
+ get_char_for_gdb(&KGDBPORT.port);
+ } else
+ /* We've got an interrupt, so go ahead and call breakpoint() */
+ breakpoint();

- /* Keep trying to read a character, this could be neater */
- while ((c = kgdb_get_char(kgdb_sci_port)) < 0);
+ sci_in(port, SCxSR); /* dummy read */
+ sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));

- return c;
+ return IRQ_HANDLED;
}

-/* Called from kgdbstub.c to put a character, just a wrapper */
-static void kgdb_sci_putchar(int c)
+static irqreturn_t kgdbsci_mpxed_interrupt(int irq, void *ptr,
+ struct pt_regs *regs)
{
+ unsigned short ssr_status, scr_status;
+ struct uart_port *port = ptr;
+
+ ssr_status = sci_in(port,SCxSR);
+ scr_status = sci_in(port,SCSCR);

- kgdb_put_char(kgdb_sci_port, c);
+ /* Rx Interrupt */
+ if ((ssr_status&0x0002) && (scr_status&0x0040))
+ kgdbsci_rx_interrupt(irq, ptr, regs);
+
+ return IRQ_HANDLED;
}

-/* Clear any errors on the SCI */
-static void kgdb_handle_error(struct sci_port *port)
+static void __init kgdbsci_lateinit(void)
{
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); /* Clear error flags */
+ if (KGDBPORT.irqs[0] == KGDBPORT.irqs[1]) {
+ if (!KGDBPORT.irqs[0]) {
+ printk(KERN_ERR "kgdbsci: Cannot allocate irq.\n");
+ return;
+ }
+ if (request_irq(KGDBPORT.irqs[0], kgdbsci_mpxed_interrupt,
+ SA_INTERRUPT, "kgdbsci",
+ &KGDBPORT.port)) {
+ printk(KERN_ERR "kgdbsci: Cannot allocate irq.\n");
+ return;
+ }
+ } else {
+ if (KGDBPORT.irqs[1])
+ request_irq(KGDBPORT.irqs[1],
+ kgdbsci_rx_interrupt, SA_INTERRUPT,
+ "kgdbsci", &KGDBPORT.port);
+ }
}
+#endif

-/* Breakpoint if there's a break sent on the serial port */
-static void kgdb_break_interrupt(int irq, void *ptr, struct pt_regs *regs)
+/*
+ * We use the normal init routine to setup the port, so we can't be
+ * in here too early.
+ */
+static int kgdbsci_init(void)
{
- struct sci_port *port = ptr;
- unsigned short status = sci_in(port, SCxSR);
+ struct termios termios;

- if (status & SCxSR_BRK(port)) {
+ memset(&termios, 0, sizeof(struct termios));

- /* Break into the debugger if a break is detected */
- BREAKPOINT();
+ termios.c_cflag = CREAD | HUPCL | CLOCAL | CS8;
+ switch (kgdbsci_baud) {
+ case 9600:
+ termios.c_cflag |= B9600;
+ break;
+ case 19200:
+ termios.c_cflag |= B19200;
+ break;
+ case 38400:
+ termios.c_cflag |= B38400;
+ break;
+ case 57600:
+ termios.c_cflag |= B57600;
+ break;
+ case 115200:
+ termios.c_cflag |= B115200;
+ break;
+ }
+ sci_set_termios(&KGDBPORT.port, &termios, NULL);

- /* Clear */
- sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
- }
+ return 0;
}

-#endif /* CONFIG_SH_KGDB */
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdbsci_read_char,
+ .write_char = kgdbsci_write_char,
+ .init = kgdbsci_init,
+#ifndef CONFIG_SERIAL_SH_SCI_CONSOLE
+ .late_init = kgdbsci_lateinit,
+#else /* ! CONFIG_SERIAL_SH_SCI_CONSOLE */
+ .late_init = NULL,
+#endif /* ! CONFIG_SERIAL_SH_SCI_CONSOLE */
+ .pre_exception = NULL,
+ .post_exception = NULL
+};
+
+/*
+ * Syntax for this cmdline option is "kgdbsci=ttyno,baudrate".
+ */
+static int __init
+kgdbsci_opt(char *str)
+{
+ /* We might have anywhere from 1 to 3 ports. */
+ if (*str < '0' || *str > SCI_NPORTS + '0')
+ goto errout;
+ kgdbsci_ttySC = *str - '0';
+ str++;
+ if (*str != ',')
+ goto errout;
+ str++;
+ kgdbsci_baud = simple_strtoul(str, &str, 10);
+ if (kgdbsci_baud != 9600 && kgdbsci_baud != 19200 &&
+ kgdbsci_baud != 38400 && kgdbsci_baud != 57600 &&
+ kgdbsci_baud != 115200)
+ goto errout;
+
+ return 0;
+
+errout:
+ printk(KERN_ERR "Invalid syntax for option kgdbsci=\n");
+ return 1;
+}
+__setup("kgdbsci", kgdbsci_opt);
+#endif /* CONFIG_KGDB_SH_SCI */

#if defined(__H8300S__)
enum { sci_disable, sci_enable };
@@ -541,6 +612,16 @@ static inline void sci_receive_chars(str
continue;
}

+#ifdef CONFIG_KGDB_SH_SCI
+ /* We assume that a ^C on the port KGDB
+ * is using means that KGDB wants to
+ * interrupt the running system.
+ */
+ if (port->line == KGDBPORT.port.line &&
+ c == 3)
+ breakpoint();
+#endif
+
/* Store data and status */
tty->flip.char_buf_ptr[i] = c;
if (status&SCxSR_FER(port)) {
@@ -1552,6 +1633,7 @@ static int __init sci_console_init(void)
console_initcall(sci_console_init);
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */

+#if 0
#ifdef CONFIG_SH_KGDB
/*
* FIXME: Most of this can go away.. at the moment, we rely on
@@ -1597,30 +1679,9 @@ int __init kgdb_console_setup(struct con
return uart_set_options(port, co, baud, parity, bits, flow);
}
#endif /* CONFIG_SH_KGDB */
+#endif /* 0 */

-#ifdef CONFIG_SH_KGDB_CONSOLE
-static struct console kgdb_console = {
- .name = "ttySC",
- .write = kgdb_console_write,
- .setup = kgdb_console_setup,
- .flags = CON_PRINTBUFFER | CON_ENABLED,
- .index = -1,
- .data = &sci_uart_driver,
-};
-
-/* Register the KGDB console so we get messages (d'oh!) */
-static int __init kgdb_console_init(void)
-{
- register_console(&kgdb_console);
- return 0;
-}
-
-console_initcall(kgdb_console_init);
-#endif /* CONFIG_SH_KGDB_CONSOLE */
-
-#if defined(CONFIG_SH_KGDB_CONSOLE)
-#define SCI_CONSOLE &kgdb_console
-#elif defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
+#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
#define SCI_CONSOLE &serial_console
#else
#define SCI_CONSOLE 0
@@ -1689,4 +1750,3 @@ static void __exit sci_exit(void)

module_init(sci_init);
module_exit(sci_exit);
-
Index: linux-2.6.13/include/asm-sh/kgdb.h
===================================================================
--- linux-2.6.13.orig/include/asm-sh/kgdb.h
+++ linux-2.6.13/include/asm-sh/kgdb.h
@@ -2,94 +2,41 @@
* 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 <dave@xxxxxxxx>, Tigran Aivazian, <tigran@xxxxxxx> and
- * Amit S. Kale <akale@xxxxxxxxxxx>
- *
- * Super-H port based on sh-stub.c (Ben Lee and Steve Chamberlain) by
- * Henry Bell <henry.bell@xxxxxx>
- *
- * Header file for low-level support for remote debug using GDB.
+ * Based on a file that was modified or based on files by: Glenn Engel,
+ * Jim Kingdon, David Grothe <dave@xxxxxxxx>, Tigran Aivazian <tigran@xxxxxxx>,
+ * Amit S. Kale <akale@xxxxxxxxxxx>, sh-stub.c from Ben Lee and
+ * Steve Chamberlain, Henry Bell <henry.bell@xxxxxx>
+ *
+ * Maintainer: Tom Rini <trini@xxxxxxxxxxxxxxxxxxx>
*
*/

#ifndef __KGDB_H
#define __KGDB_H

-#include <asm/ptrace.h>
-
-struct console;
+#include <asm-generic/kgdb.h>
+/* Based on sh-gdb.c from gdb-6.1, Glenn
+ Engel at HP Ben Lee and Steve Chamberlain */
+#define NUMREGBYTES 112 /* 92 */
+#define NUMCRITREGBYTES (9 << 2)
+#define BUFMAX 400

-/* Same as pt_regs but has vbr in place of syscall_nr */
+#ifndef __ASSEMBLY__
struct kgdb_regs {
unsigned long regs[16];
unsigned long pc;
unsigned long pr;
- unsigned long sr;
unsigned long gbr;
+ unsigned long vbr;
unsigned long mach;
unsigned long macl;
- unsigned long vbr;
-};
-
-/* State info */
-extern char kgdb_in_gdb_mode;
-extern int kgdb_done_init;
-extern int kgdb_enabled;
-extern int kgdb_nofault; /* Ignore bus errors (in gdb mem access) */
-extern int kgdb_halt; /* Execute initial breakpoint at startup */
-extern char in_nmi; /* Debounce flag to prevent NMI reentry*/
-
-/* SCI */
-extern int kgdb_portnum;
-extern int kgdb_baud;
-extern char kgdb_parity;
-extern char kgdb_bits;
-extern int kgdb_console_setup(struct console *, char *);
-
-/* Init and interface stuff */
-extern int kgdb_init(void);
-extern int (*kgdb_serial_setup)(void);
-extern int (*kgdb_getchar)(void);
-extern void (*kgdb_putchar)(int);
-
-struct kgdb_sermap {
- char *name;
- int namelen;
- int (*setup_fn)(struct console *, char *);
- struct kgdb_sermap *next;
+ unsigned long sr;
};
-extern void kgdb_register_sermap(struct kgdb_sermap *map);
-extern struct kgdb_sermap *kgdb_porttype;

-/* Trap functions */
-typedef void (kgdb_debug_hook_t)(struct pt_regs *regs);
-typedef void (kgdb_bus_error_hook_t)(void);
-extern kgdb_debug_hook_t *kgdb_debug_hook;
-extern kgdb_bus_error_hook_t *kgdb_bus_err_hook;
-
-extern void breakpoint(void);
-
-/* Console */
-struct console;
-void kgdb_console_write(struct console *co, const char *s, unsigned count);
-void kgdb_console_init(void);
-
-/* Prototypes for jmp fns */
-#define _JBLEN 9
-typedef int jmp_buf[_JBLEN];
-extern void longjmp(jmp_buf __jmpb, int __retval);
-extern int setjmp(jmp_buf __jmpb);
-
-/* Variadic macro to print our own message to the console */
-#define KGDB_PRINTK(...) printk("KGDB: " __VA_ARGS__)
-
-/* Forced breakpoint */
-#define BREAKPOINT() do { \
- if (kgdb_enabled) { \
- asm volatile("trapa #0xff"); \
- } \
-} while (0)
+#define BREAKPOINT() asm("trapa #0xff");
+#define BREAK_INSTR_SIZE 2
+#define CHECK_EXCEPTION_STACK() 1
+#define CACHE_FLUSH_IS_SAFE 1

/* KGDB should be able to flush all kernel text space */
#if defined(CONFIG_CPU_SH4)
@@ -102,30 +49,5 @@ extern int setjmp(jmp_buf __jmpb);
#else
#define kgdb_flush_icache_range(start, end) do { } while (0)
#endif
-
-/* Kernel assert macros */
-#ifdef CONFIG_KGDB_KERNEL_ASSERTS
-
-/* Predefined conditions */
-#define KA_VALID_ERRNO(errno) ((errno) > 0 && (errno) <= EMEDIUMTYPE)
-#define KA_VALID_PTR_ERR(ptr) KA_VALID_ERRNO(-PTR_ERR(ptr))
-#define KA_VALID_KPTR(ptr) (!(ptr) || \
- ((void *)(ptr) >= (void *)PAGE_OFFSET && \
- (void *)(ptr) < ERR_PTR(-EMEDIUMTYPE)))
-#define KA_VALID_PTRORERR(errptr) \
- (KA_VALID_KPTR(errptr) || KA_VALID_PTR_ERR(errptr))
-#define KA_HELD_GKL() (current->lock_depth >= 0)
-
-/* The actual assert */
-#define KGDB_ASSERT(condition, message) do { \
- if (!(condition) && (kgdb_enabled)) { \
- KGDB_PRINTK("Assertion failed at %s:%d: %s\n", \
- __FILE__, __LINE__, message);\
- BREAKPOINT(); \
- } \
-} while (0)
-#else
-#define KGDB_ASSERT(condition, message)
-#endif
-
+#endif /* !__ASSEMBLY__ */
#endif
Index: linux-2.6.13/include/asm-sh/system.h
===================================================================
--- linux-2.6.13.orig/include/asm-sh/system.h
+++ linux-2.6.13/include/asm-sh/system.h
@@ -7,6 +7,7 @@
*/

#include <linux/config.h>
+#include <asm/types.h>

/*
* switch_to() should switch tasks to task nr n, first
@@ -252,6 +253,45 @@ static __inline__ unsigned long __xchg(u
return x;
}

+static inline unsigned long __cmpxchg_u32(volatile int * m, unsigned long old,
+ unsigned long new)
+{
+ __u32 retval;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ retval = *m;
+ if (retval == old)
+ *m = new;
+ local_irq_restore(flags); /* implies memory barrier */
+ return retval;
+}
+
+/* This function doesn't exist, so you'll get a linker error
+ * if something tries to do an invalid cmpxchg(). */
+extern void __cmpxchg_called_with_bad_pointer(void);
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
+ unsigned long new, int size)
+{
+ switch (size) {
+ case 4:
+ return __cmpxchg_u32(ptr, old, new);
+ }
+ __cmpxchg_called_with_bad_pointer();
+ return old;
+}
+
+#define cmpxchg(ptr,o,n) \
+ ({ \
+ __typeof__(*(ptr)) _o_ = (o); \
+ __typeof__(*(ptr)) _n_ = (n); \
+ (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
+ (unsigned long)_n_, sizeof(*(ptr))); \
+ })
+
/* XXX
* disable hlt during certain critical i/o operations
*/
Index: linux-2.6.13/lib/Kconfig.debug
===================================================================
--- linux-2.6.13.orig/lib/Kconfig.debug
+++ linux-2.6.13/lib/Kconfig.debug
@@ -151,7 +151,7 @@ config DEBUG_FS

config FRAME_POINTER
bool "Compile the kernel with frame pointers"
- depends on DEBUG_KERNEL && ((X86 && !X86_64) || CRIS || M68K || M68KNOMMU || FRV || UML)
+ depends on DEBUG_KERNEL && ((X86 && !X86_64) || CRIS || M68K || M68KNOMMU || FRV || UML || SUPERH)
default y if DEBUG_INFO && UML
help
If you say Y here the resulting kernel image will be slightly larger
@@ -162,13 +162,13 @@ config FRAME_POINTER
config WANT_EXTRA_DEBUG_INFORMATION
bool
select DEBUG_INFO
- select FRAME_POINTER if X86 && !X86_64
+ select FRAME_POINTER if (X86 && !X86_64) || SUPERH
default n

config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- depends on DEBUG_KERNEL && (X86 || MIPS32 || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS32 || (SUPERH && !SUPERH64) || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. It is strongly suggested that you enable
@@ -232,6 +232,12 @@ config KGDB_SIBYTE
bool "KGDB: On the Broadcom SWARM serial port"
depends on MIPS && SIBYTE_SB1xxx_SOC

+config KGDB_SH_SCI
+ bool "KGDB: On SH SCI(F) serial port"
+ depends on SUPERH && SERIAL_SH_SCI
+ help
+ Uses the SCI(F) serial port found on the board.
+
endchoice

config KGDB_8250
@@ -255,7 +261,7 @@ config KGDB_SIMPLE_SERIAL

choice
prompt "Debug serial port BAUD"
- depends on KGDB_8250
+ depends on KGDB_8250 || KGDB_SH_SCI
default KGDB_115200BAUD
help
gdb and the kernel stub need to agree on the baud rate to be
@@ -280,7 +286,7 @@ endchoice

choice
prompt "Serial port for KGDB"
- depends on KGDB_SIMPLE_SERIAL
+ depends on KGDB_8250 || KGDB_SH_SCI
default KGDB_PORT_0

config KGDB_PORT_0
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/