This adds basic support to the MIPS architecture as well as support specifically for the MIPS Malta (via 8250) and SiByte 1250-SWARM (32 and 64bit, custom uart), all from Manish Lachwani. This looks like it should work on anything with an 8250-compatible uart, and be fairly easy to convert other boards with custom uarts. This also adds the base functions for debugging into the serial_txx9 driver, from Ralf. Signed-off-by: Geoff Levand Signed-off-by: Tom Rini arch/mips/Kconfig.debug | 19 arch/mips/kernel/Makefile | 3 arch/mips/kernel/gdb-low.S | 393 ---------- arch/mips/kernel/gdb-stub.c | 1155 ------------------------------ arch/mips/kernel/irq.c | 32 arch/mips/kernel/kgdb-jmp.c | 116 +++ arch/mips/kernel/kgdb-setjmp.S | 28 arch/mips/kernel/kgdb.c | 297 +++++++ arch/mips/kernel/kgdb_handler.S | 57 + arch/mips/kernel/traps.c | 24 arch/mips/mips-boards/generic/Makefile | 1 arch/mips/mips-boards/generic/init.c | 61 - arch/mips/mips-boards/malta/malta_setup.c | 8 arch/mips/mm/extable.c | 7 arch/mips/sibyte/cfe/setup.c | 14 arch/mips/sibyte/sb1250/Makefile | 1 arch/mips/sibyte/sb1250/irq.c | 62 - arch/mips/sibyte/sb1250/kgdb_sibyte.c | 164 ++++ arch/mips/sibyte/swarm/Makefile | 2 arch/mips/sibyte/swarm/dbg_io.c | 76 - arch/mips/tx4938/common/Makefile | 2 drivers/serial/serial_txx9.c | 90 ++ include/asm-mips/kdebug.h | 47 + include/asm-mips/kgdb.h | 30 lib/Kconfig.debug | 7 25 files changed, 890 insertions(+), 1806 deletions(-) Index: linux-2.6.18-rc7/drivers/serial/serial_txx9.c =================================================================== --- linux-2.6.18-rc7.orig/drivers/serial/serial_txx9.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/drivers/serial/serial_txx9.c 2006-10-13 17:07:03.000000000 +0100 @@ -1164,6 +1164,96 @@ static struct pci_driver serial_txx9_pci MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl); #endif /* ENABLE_SERIAL_TXX9_PCI */ +/******************************************************************************/ +/* BEG: KDBG Routines */ +/******************************************************************************/ + +#ifdef CONFIG_KGDB +int kgdb_init_count = 0; + +void txx9_sio_kgdb_hook(unsigned int port, unsigned int baud_rate) +{ + static struct resource kgdb_resource; + int ret; + struct uart_txx9_port *up = &serial_txx9_ports[port]; + + /* prevent initialization by driver */ + kgdb_resource.name = "serial_txx9(debug)"; + kgdb_resource.start = (unsigned long)up->port.membase; + kgdb_resource.end = (unsigned long)(up->port.membase + 36 - 1); + kgdb_resource.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + + ret = request_resource(&iomem_resource, &kgdb_resource); + if(ret == -EBUSY) + printk(" serial_txx9(debug): request_resource failed\n"); + + return; +} +void +txx9_sio_kdbg_init( unsigned int port_number ) +{ + if (port_number == 1) { + txx9_sio_kgdb_hook(port_number, 38400); + } else { + printk("Bad Port Number [%u] != [1]\n",port_number); + } + return; +} + +u8 +txx9_sio_kdbg_rd( void ) +{ + unsigned int status,ch; + struct uart_txx9_port *up = &serial_txx9_ports[1]; + + if (kgdb_init_count == 0) { + txx9_sio_kdbg_init(1); + kgdb_init_count = 1; + } + + while (1) { + status = sio_in(up, TXX9_SIDISR); + if ( status & 0x1f ) { + ch = sio_in(up, TXX9_SIRFIFO ); + break; + } + } + + return (ch); +} + +int +txx9_sio_kdbg_wr( u8 ch ) +{ + unsigned int status; + struct uart_txx9_port *up = &serial_txx9_ports[1]; + + if (kgdb_init_count == 0) { + txx9_sio_kdbg_init(1); + kgdb_init_count = 1; + } + + while (1) { + status = sio_in(up, TXX9_SICISR); + if (status & TXX9_SICISR_TRDY) { + if ( ch == '\n' ) { + txx9_sio_kdbg_wr( '\r' ); + } + sio_out(up, TXX9_SITFIFO, (u32)ch ); + + break; + } + } + + return (1); +} +#endif /* CONFIG_KGDB */ + + +/******************************************************************************/ +/* END: KDBG Routines */ +/******************************************************************************/ + static int __init serial_txx9_init(void) { int ret; Index: linux-2.6.18-rc7/arch/mips/sibyte/swarm/Makefile =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/sibyte/swarm/Makefile 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/sibyte/swarm/Makefile 2006-10-13 17:07:03.000000000 +0100 @@ -1,3 +1 @@ lib-y = setup.o rtc_xicor1241.o rtc_m41t81.o - -lib-$(CONFIG_KGDB) += dbg_io.o Index: linux-2.6.18-rc7/arch/mips/sibyte/swarm/dbg_io.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/sibyte/swarm/dbg_io.c 2006-10-13 16:56:46.000000000 +0100 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -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); -} - Index: linux-2.6.18-rc7/arch/mips/sibyte/sb1250/irq.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/sibyte/sb1250/irq.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/sibyte/sb1250/irq.c 2006-10-13 17:07:03.000000000 +0100 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -59,16 +60,6 @@ static void sb1250_set_affinity(unsigned extern unsigned long ldt_eoi_space; #endif -#ifdef CONFIG_KGDB -static int kgdb_irq; - -/* Default to UART1 */ -int kgdb_port = 1; -#ifdef CONFIG_SIBYTE_SB1250_DUART -extern char sb1250_duart_present[]; -#endif -#endif - static struct irq_chip sb1250_irq_type = { .typename = "SB1250-IMR", .startup = startup_sb1250_irq, @@ -324,6 +315,11 @@ void __init arch_init_irq(void) unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 | STATUSF_IP1 | STATUSF_IP0; +#ifdef CONFIG_KGDB + if (kgdb_early_setup) + return; +#endif + /* Default everything to IP2 */ for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */ __raw_writeq(IMR_IP2_VAL, @@ -375,50 +371,6 @@ void __init arch_init_irq(void) /* Enable necessary IPs, disable the rest */ change_c0_status(ST0_IM, imask); -#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); - } -#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))) - -static 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 */ - static inline int dclz(unsigned long long x) { int lz; @@ -473,7 +425,7 @@ asmlinkage void plat_irq_dispatch(struct sb1250_mailbox_interrupt(regs); #endif -#ifdef CONFIG_KGDB +#ifdef CONFIG_KGDB_SIBYTE else if (pending & CAUSEF_IP6) /* KGDB (uart 1) */ sb1250_kgdb_interrupt(regs); #endif Index: linux-2.6.18-rc7/arch/mips/sibyte/sb1250/kgdb_sibyte.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/arch/mips/sibyte/sb1250/kgdb_sibyte.c 2006-10-13 17:07:03.000000000 +0100 @@ -0,0 +1,164 @@ +/* + * 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); + +/* Forward declarations. */ +static void kgdbsibyte_init_duart(void); +static int kgdb_init_io(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) +{ + int ret_char; + unsigned int status; + + 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); + + 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); + if (kgdb_io_ops.init != kgdb_init_io) { + /* Throw away the data if another I/O routine is + * active. + */ + unsigned int status; + + 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(); + } + duart_in(R_DUART_RX_HOLD); + } else + 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; +} + +/* + * 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); +} + +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, + .pre_exception = NULL, + .post_exception = NULL +}; Index: linux-2.6.18-rc7/arch/mips/sibyte/sb1250/Makefile =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/sibyte/sb1250/Makefile 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/sibyte/sb1250/Makefile 2006-10-13 17:07:03.000000000 +0100 @@ -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) Index: linux-2.6.18-rc7/arch/mips/sibyte/cfe/setup.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/sibyte/cfe/setup.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/sibyte/cfe/setup.c 2006-10-13 17:07:03.000000000 +0100 @@ -58,10 +58,6 @@ int cfe_cons_handle; extern unsigned long initrd_start, initrd_end; #endif -#ifdef CONFIG_KGDB -extern int kgdb_port; -#endif - static void ATTRIB_NORET cfe_linux_exit(void *arg) { int warm = *(int *)arg; @@ -242,9 +238,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 = cfe_linux_restart; _machine_halt = cfe_linux_halt; @@ -308,13 +301,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; Index: linux-2.6.18-rc7/arch/mips/mips-boards/malta/malta_setup.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/mips-boards/malta/malta_setup.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/mips-boards/malta/malta_setup.c 2006-10-13 17:07:03.000000000 +0100 @@ -46,10 +46,6 @@ extern void mips_reboot_setup(void); extern void mips_time_init(void); extern unsigned long mips_rtc_get_time(void); -#ifdef CONFIG_KGDB -extern void kgdb_config(void); -#endif - struct resource standard_io_resources[] = { { .name = "dma1", .start = 0x00, .end = 0x1f, .flags = IORESOURCE_BUSY }, { .name = "timer", .start = 0x40, .end = 0x5f, .flags = IORESOURCE_BUSY }, @@ -124,10 +120,6 @@ void __init plat_mem_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)) { Index: linux-2.6.18-rc7/arch/mips/mips-boards/generic/Makefile =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/mips-boards/generic/Makefile 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/mips-boards/generic/Makefile 2006-10-13 17:07:03.000000000 +0100 @@ -21,6 +21,5 @@ obj-y := 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) Index: linux-2.6.18-rc7/arch/mips/mips-boards/generic/init.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/mips-boards/generic/init.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/mips-boards/generic/init.c 2006-10-13 17:07:03.000000000 +0100 @@ -37,15 +37,6 @@ #include -#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); -#endif - int prom_argc; int *_prom_argv, *_prom_envp; @@ -172,58 +163,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++); - } - - /* Breakpoint is invoked after interrupts are initialised */ - } -} -#endif - void __init mips_nmi_setup (void) { void *base; Index: linux-2.6.18-rc7/arch/mips/Kconfig.debug =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/Kconfig.debug 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/Kconfig.debug 2006-10-13 17:07:03.000000000 +0100 @@ -37,25 +37,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 Index: linux-2.6.18-rc7/arch/mips/mm/extable.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/mm/extable.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/mm/extable.c 2006-10-13 17:07:03.000000000 +0100 @@ -3,6 +3,7 @@ */ #include #include +#include #include #include @@ -16,6 +17,12 @@ int fixup_exception(struct pt_regs *regs return 1; } +#ifdef CONFIG_KGDB + if (atomic_read(&debugger_active) && kgdb_may_fault) + /* Restore our previous state. */ + kgdb_fault_longjmp(kgdb_fault_jmp_regs); + /* Not reached. */ +#endif return 0; } Index: linux-2.6.18-rc7/arch/mips/kernel/irq.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/kernel/irq.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/kernel/irq.c 2006-10-13 17:07:03.000000000 +0100 @@ -25,6 +25,10 @@ #include #include #include +#include + +/* Keep track of if we've done certain initialization already or not. */ +int kgdb_early_setup; /* * 'what should we do if we get a hw irq event on an illegal vector'. @@ -115,23 +119,13 @@ asmlinkage void spurious_interrupt(struc atomic_inc(&irq_err_count); } -#ifdef CONFIG_KGDB -extern void breakpoint(void); -extern void set_debug_traps(void); - -static int kgdb_flag = 1; -static int __init nokgdb(char *str) -{ - kgdb_flag = 0; - return 1; -} -__setup("nokgdb", nokgdb); -#endif - void __init init_IRQ(void) { int i; + if (kgdb_early_setup) + return; + for (i = 0; i < NR_IRQS; i++) { irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = NULL; @@ -144,12 +138,12 @@ void __init init_IRQ(void) } arch_init_irq(); - #ifdef CONFIG_KGDB - if (kgdb_flag) { - printk("Wait for gdb client connection ...\n"); - set_debug_traps(); - breakpoint(); - } + /* + * We have been called before kgdb_arch_init(). Hence, + * we dont want the traps to be reinitialized + */ + if (kgdb_early_setup == 0) + kgdb_early_setup = 1; #endif } Index: linux-2.6.18-rc7/arch/mips/kernel/kgdb-jmp.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/arch/mips/kernel/kgdb-jmp.c 2006-10-13 17:07:03.000000000 +0100 @@ -0,0 +1,116 @@ +/* + * arch/mips/kernel/kgdb-jmp.c + * + * Save and restore system registers so that within a limited frame we + * may have a fault and "jump back" to a known safe location. + * + * Author: Tom Rini + * Author: Manish Lachwani + * + * Cribbed from glibc, which carries the following: + * Copyright (C) 1996, 1997, 2000, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2005 by MontaVista Software. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program as licensed "as is" without any warranty of + * any kind, whether express or implied. + */ + +#include +#include + +#ifdef CONFIG_MIPS64 +/* + * MIPS 64-bit + */ + +int kgdb_fault_setjmp_aux(unsigned long *curr_context, int sp, int fp) +{ + __asm__ __volatile__ ("sd $gp, %0" : : "m" (curr_context[0])); + __asm__ __volatile__ ("sd $16, %0" : : "m" (curr_context[1])); + __asm__ __volatile__ ("sd $17, %0" : : "m" (curr_context[2])); + __asm__ __volatile__ ("sd $18, %0" : : "m" (curr_context[3])); + __asm__ __volatile__ ("sd $19, %0" : : "m" (curr_context[4])); + __asm__ __volatile__ ("sd $20, %0" : : "m" (curr_context[5])); + __asm__ __volatile__ ("sd $21, %0" : : "m" (curr_context[6])); + __asm__ __volatile__ ("sd $22, %0" : : "m" (curr_context[7])); + __asm__ __volatile__ ("sd $23, %0" : : "m" (curr_context[8])); + __asm__ __volatile__ ("sd $31, %0" : : "m" (curr_context[9])); + curr_context[10] = (long *)sp; + curr_context[11] = (long *)fp; + + return 0; +} + +void kgdb_fault_longjmp(unsigned long *curr_context) +{ + unsigned long sp_val, fp_val; + + __asm__ __volatile__ ("ld $gp, %0" : : "m" (curr_context[0])); + __asm__ __volatile__ ("ld $16, %0" : : "m" (curr_context[1])); + __asm__ __volatile__ ("ld $17, %0" : : "m" (curr_context[2])); + __asm__ __volatile__ ("ld $18, %0" : : "m" (curr_context[3])); + __asm__ __volatile__ ("ld $19, %0" : : "m" (curr_context[4])); + __asm__ __volatile__ ("ld $20, %0" : : "m" (curr_context[5])); + __asm__ __volatile__ ("ld $21, %0" : : "m" (curr_context[6])); + __asm__ __volatile__ ("ld $22, %0" : : "m" (curr_context[7])); + __asm__ __volatile__ ("ld $23, %0" : : "m" (curr_context[8])); + __asm__ __volatile__ ("ld $25, %0" : : "m" (curr_context[9])); + sp_val = curr_context[10]; + fp_val = curr_context[11]; + __asm__ __volatile__ ("ld $29, %0\n\t" + "ld $30, %1\n\t" : : "m" (sp_val), "m" (fp_val)); + + __asm__ __volatile__ ("dli $2, 1"); + __asm__ __volatile__ ("j $25"); + + for (;;); +} +#else +/* + * MIPS 32-bit + */ + +int kgdb_fault_setjmp_aux(unsigned long *curr_context, int sp, int fp) +{ + __asm__ __volatile__("sw $gp, %0" : : "m" (curr_context[0])); + __asm__ __volatile__("sw $16, %0" : : "m" (curr_context[1])); + __asm__ __volatile__("sw $17, %0" : : "m" (curr_context[2])); + __asm__ __volatile__("sw $18, %0" : : "m" (curr_context[3])); + __asm__ __volatile__("sw $19, %0" : : "m" (curr_context[4])); + __asm__ __volatile__("sw $20, %0" : : "m" (curr_context[5])); + __asm__ __volatile__("sw $21, %0" : : "m" (curr_context[6])); + __asm__ __volatile__("sw $22, %0" : : "m" (curr_context[7])); + __asm__ __volatile__("sw $23, %0" : : "m" (curr_context[8])); + __asm__ __volatile__("sw $31, %0" : : "m" (curr_context[9])); + curr_context[10] = (long *)sp; + curr_context[11] = (long *)fp; + + return 0; +} + +void kgdb_fault_longjmp(unsigned long *curr_context) +{ + unsigned long sp_val, fp_val; + + __asm__ __volatile__("lw $gp, %0" : : "m" (curr_context[0])); + __asm__ __volatile__("lw $16, %0" : : "m" (curr_context[1])); + __asm__ __volatile__("lw $17, %0" : : "m" (curr_context[2])); + __asm__ __volatile__("lw $18, %0" : : "m" (curr_context[3])); + __asm__ __volatile__("lw $19, %0" : : "m" (curr_context[4])); + __asm__ __volatile__("lw $20, %0" : : "m" (curr_context[5])); + __asm__ __volatile__("lw $21, %0" : : "m" (curr_context[6])); + __asm__ __volatile__("lw $22, %0" : : "m" (curr_context[7])); + __asm__ __volatile__("lw $23, %0" : : "m" (curr_context[8])); + __asm__ __volatile__("lw $25, %0" : : "m" (curr_context[9])); + sp_val = curr_context[10]; + fp_val = curr_context[11]; + __asm__ __volatile__("lw $29, %0\n\t" + "lw $30, %1\n\t" : : "m" (sp_val), "m" (fp_val)); + + __asm__ __volatile__("li $2, 1"); + __asm__ __volatile__("jr $25"); + + for (;;); +} +#endif Index: linux-2.6.18-rc7/arch/mips/kernel/kgdb.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/arch/mips/kernel/kgdb.c 2006-10-13 17:07:03.000000000 +0100 @@ -0,0 +1,297 @@ +/* + * 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-2005 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 /* for linux pt_regs struct */ +#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 */ +/* { 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 */ +}; + +/* Save the normal trap handlers for user-mode traps. */ +void *saved_vectors[32]; + +extern void trap_low(void); +extern void breakinst(void); +extern void init_IRQ(void); + +void kgdb_call_nmi_hook(void *ignored) +{ + kgdb_nmihook(smp_processor_id(), (void *)0); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + local_irq_restore(flags); + smp_call_function(kgdb_call_nmi_hook, 0, 0, 0); + local_irq_save(flags); +} + +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 (fixup_exception(regs)) { + return; + } + + if (atomic_read(&debugger_active)) + kgdb_nmihook(smp_processor_id(), regs); + + if (atomic_read(&kgdb_setting_breakpoint)) + if ((trap == 9) && (regs->cp0_epc == (unsigned long)breakinst)) + regs->cp0_epc += 4; + + kgdb_handle_exception(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; + gdb_reg_t *ptr = (gdb_reg_t*)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; + const gdb_reg_t *ptr = (gdb_reg_t*)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; + 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; + gdb_reg_t *ptr = (gdb_reg_t*)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; + + /* See if KGDB is interested. */ + if (user_mode(regs)) + /* Userpace events, ignore. */ + return NOTIFY_DONE; + + kgdb_handle_exception(trap, compute_signal(trap), 0, regs); + 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 *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long address; + int cpu = smp_processor_id(); + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameter */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &address)) + regs->cp0_epc = address; + + atomic_set(&cpu_doing_single_step, -1); + if (remcom_in_buffer[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 +}; + +/* + * We use kgdb_early_setup so that functions we need to call now don't + * cause trouble when called again later. + */ +int kgdb_arch_init(void) +{ + /* Board-specifics. */ + /* Force some calls to happen earlier. */ + if (kgdb_early_setup == 0) { + trap_init(); + init_IRQ(); + kgdb_early_setup = 1; + } + + /* 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; +} Index: linux-2.6.18-rc7/arch/mips/kernel/gdb-low.S =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/kernel/gdb-low.S 2006-10-13 16:56:46.000000000 +0100 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -1,394 +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_32BIT -#define DMFC0 mfc0 -#define DMTC0 mtc0 -#define LDC1 lwc1 -#define SDC1 lwc1 -#endif -#ifdef CONFIG_64BIT -#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. - */ - mfc0 k0, CP0_CAUSE - andi k0, k0, 0x7c -#ifdef CONFIG_64BIT - dsll k0, k0, 1 -#endif - PTR_L k1, saved_vectors(k0) - jr k1 - nop -1: - move k0, sp - PTR_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 */ - TRACE_IRQS_OFF - -/* - * 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: -#ifdef CONFIG_MIPS_MT_SMTC - /* Read-modify write of Status must be atomic */ - mfc0 t2, CP0_TCSTATUS - ori t1, t2, TCSTATUS_IXMT - mtc0 t1, CP0_TCSTATUS - andi t2, t2, TCSTATUS_IXMT - _ehb - DMT 9 # dmt t1 - jal mips_ihb - nop -#endif /* CONFIG_MIPS_MT_SMTC */ - mfc0 t0, CP0_STATUS - ori t0, 0x1f - xori t0, 0x1f - mtc0 t0, CP0_STATUS -#ifdef CONFIG_MIPS_MT_SMTC - andi t1, t1, VPECONTROL_TE - beqz t1, 9f - nop - EMT # emt -9: - mfc0 t1, CP0_TCSTATUS - xori t1, t1, TCSTATUS_IXMT - or t1, t1, t2 - mtc0 t1, CP0_TCSTATUS - _ehb -#endif /* CONFIG_MIPS_MT_SMTC */ - 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 Index: linux-2.6.18-rc7/arch/mips/kernel/traps.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/kernel/traps.c 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/kernel/traps.c 2006-10-13 17:07:03.000000000 +0100 @@ -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, 2005 Maciej W. Rozycki + * + * KGDB specific changes - Manish Lachwani (mlachwani@mvista.com) */ #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #include #include @@ -40,6 +43,7 @@ #include #include #include +#include extern asmlinkage void handle_int(void); extern asmlinkage void handle_tlbm(void); @@ -78,6 +82,21 @@ void (*board_bind_eic_interrupt)(int irq */ #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 ... @@ -1387,6 +1406,11 @@ void __init trap_init(void) extern char except_vec4; unsigned long i; +#if defined(CONFIG_KGDB) + if (kgdb_early_setup) + return; /* Already done */ +#endif + if (cpu_has_veic || cpu_has_vint) ebase = (unsigned long) alloc_bootmem_low_pages (0x200 + VECTORSPACING*64); else Index: linux-2.6.18-rc7/arch/mips/kernel/Makefile =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/kernel/Makefile 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/kernel/Makefile 2006-10-13 17:07:03.000000000 +0100 @@ -59,7 +59,8 @@ obj-$(CONFIG_MIPS32_COMPAT) += linux32.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 kgdb-jmp.o \ + kgdb-setjmp.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_64BIT) += cpu-bugs64.o Index: linux-2.6.18-rc7/arch/mips/kernel/kgdb_handler.S =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/arch/mips/kernel/kgdb_handler.S 2006-10-13 17:07:03.000000000 +0100 @@ -0,0 +1,57 @@ +/* + * arch/mips/kernel/kgdb_handler.S + * + * Copyright (C) 2004-2005 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 + + /* + * 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 + */ + mfc0 k1, CP0_CAUSE + andi k1, k1, 0x7c + PTR_L k0, saved_vectors(k1) + jr k0 + nop +1: + SAVE_ALL + .set at + .set reorder + move a0, sp + jal handle_exception + j ret_from_exception + END(trap_low) Index: linux-2.6.18-rc7/arch/mips/kernel/gdb-stub.c =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/kernel/gdb-stub.c 2006-10-13 16:56:46.000000000 +0100 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -1,1154 +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); - -int kgdb_enabled; - -/* - * spin locks for smp case - */ -static DEFINE_SPINLOCK(kgdb_lock); -static raw_spinlock_t kgdb_cpulock[NR_CPUS] = { - [0 ... NR_CPUS-1] = __RAW_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; - -#ifdef CONFIG_SMP - /* avoid deadlock if someone is make IPC */ - if (spin_is_locked(&smp_call_lock)) - return; -#endif - - async_bp.addr = *epc; - *epc = (unsigned long)async_breakpoint; -} - -static void kgdb_wait(void *arg) -{ - unsigned flags; - int cpu = smp_processor_id(); - - local_irq_save(flags); - - __raw_spin_lock(&kgdb_cpulock[cpu]); - __raw_spin_unlock(&kgdb_cpulock[cpu]); - - local_irq_restore(flags); -} - -/* - * GDB stub needs to call kgdb_wait on all processor with interrupts - * disabled, so it uses it's own special variant. - */ -static int kgdb_smp_call_kgdb_wait(void) -{ -#ifdef CONFIG_SMP - struct call_data_struct data; - int i, cpus = num_online_cpus() - 1; - int cpu = smp_processor_id(); - - /* - * Can die spectacularly if this CPU isn't yet marked online - */ - BUG_ON(!cpu_online(cpu)); - - if (!cpus) - return 0; - - if (spin_is_locked(&smp_call_lock)) { - /* - * Some other processor is trying to make us do something - * but we're not going to respond... give up - */ - return -1; - } - - /* - * We will continue here, accepting the fact that - * the kernel may deadlock if another CPU attempts - * to call smp_call_function now... - */ - - data.func = kgdb_wait; - data.info = NULL; - atomic_set(&data.started, 0); - data.wait = 0; - - spin_lock(&smp_call_lock); - call_data = &data; - mb(); - - /* Send a message to all other CPUs and wait for them to respond */ - for (i = 0; i < NR_CPUS; i++) - if (cpu_online(i) && i != cpu) - core_send_ipi(i, SMP_CALL_FUNCTION); - - /* Wait for response */ - /* FIXME: lock-up detection, backtrace on lock-up */ - while (atomic_read(&data.started) != cpus) - barrier(); - - call_data = NULL; - spin_unlock(&smp_call_lock); -#endif - - return 0; -} - -/* - * 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 (__raw_spin_trylock(&kgdb_cpulock[i]) == 0) - panic("kgdb: couldn't get cpulock %d\n", i); - - /* - * force other cpus to enter kgdb - */ - kgdb_smp_call_kgdb_wait(); - - /* - * 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--) - __raw_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 * __attribute_used__ malloc(size_t size) -{ - return kmalloc(size, GFP_ATOMIC); -} - -static void __attribute_used__ 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 Index: linux-2.6.18-rc7/arch/mips/kernel/kgdb-setjmp.S =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/arch/mips/kernel/kgdb-setjmp.S 2006-10-13 17:07:03.000000000 +0100 @@ -0,0 +1,28 @@ +/* + * arch/mips/kernel/kgdb-jmp.c + * + * Save and restore system registers so that within a limited frame we + * may have a fault and "jump back" to a known safe location. + * + * Copyright (C) 2005 by MontaVista Software. + * Author: Manish Lachwani (mlachwani@mvista.com) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program as licensed "as is" without any warranty of + * any kind, whether express or implied. + */ + +#include +#include +#include +#include + + .ent kgdb_fault_setjmp,0 +ENTRY (kgdb_fault_setjmp) + move a1, sp + move a2, fp +#ifdef CONFIG_MIPS64 + nop +#endif + j kgdb_fault_setjmp_aux + .end kgdb_fault_setjmp Index: linux-2.6.18-rc7/arch/mips/tx4938/common/Makefile =================================================================== --- linux-2.6.18-rc7.orig/arch/mips/tx4938/common/Makefile 2006-10-13 16:56:46.000000000 +0100 +++ linux-2.6.18-rc7/arch/mips/tx4938/common/Makefile 2006-10-13 17:07:03.000000000 +0100 @@ -7,5 +7,5 @@ # obj-y += prom.o setup.o irq.o rtc_rx5c348.o -obj-$(CONFIG_KGDB) += dbgio.o +obj-$(CONFIG_KGDB_8250) += dbgio.o Index: linux-2.6.18-rc7/lib/Kconfig.debug =================================================================== --- linux-2.6.18-rc7.orig/lib/Kconfig.debug 2006-10-13 17:06:25.000000000 +0100 +++ linux-2.6.18-rc7/lib/Kconfig.debug 2006-10-13 17:07:03.000000000 +0100 @@ -378,7 +378,7 @@ config WANT_EXTRA_DEBUG_INFORMATION config KGDB bool "KGDB: kernel debugging with remote gdb" select WANT_EXTRA_DEBUG_INFORMATION - depends on DEBUG_KERNEL && (X86 || PPC) + depends on DEBUG_KERNEL && (X86 || MIPS || PPC) help If you say Y here, it will be possible to remotely debug the kernel using gdb. It is strongly suggested that you enable @@ -406,6 +406,7 @@ choice default KGDB_8250_NOMODULE default KGDB_MPSC if SERIAL_MPSC default KGDB_CPM_UART if (8xx || 8260) + default KGDB_SIBYTE if SIBYTE_SB1xxx_SOC help There are a number of different ways in which you can communicate with KGDB. The most common is via serial, with the 8250 driver @@ -456,6 +457,10 @@ config KGDB_CPM_UART depends on PPC && (CPM2 || 8xx) help Uses CPM UART to communicate with the host GDB. + +config KGDB_SIBYTE + bool "KGDB: On the Broadcom SWARM serial port" + depends on MIPS && SIBYTE_SB1xxx_SOC endchoice config KGDBOE Index: linux-2.6.18-rc7/include/asm-mips/kgdb.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/include/asm-mips/kgdb.h 2006-10-13 17:07:03.000000000 +0100 @@ -0,0 +1,30 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +#ifndef __ASSEMBLY__ +#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) +typedef u32 gdb_reg_t; +#elif (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) +typedef u64 gdb_reg_t; +#else +#error need to do +#endif /* _MIPS_ISA */ + +#define BUFMAX 2048 +#define NUMREGBYTES (90*sizeof(gdb_reg_t)) +#define NUMCRITREGBYTES (12*sizeof(gdb_reg_t)) +#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 CACHE_FLUSH_IS_SAFE 0 + +extern int kgdb_early_setup; + +#endif /* _ASM_KGDB_H_ */ +#endif /* __KERNEL__ */ Index: linux-2.6.18-rc7/include/asm-mips/kdebug.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/include/asm-mips/kdebug.h 2006-10-13 17:07:03.000000000 +0100 @@ -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 */