arm-lite.patch From: Jason Wessel CC: linux-arm-kernel@lists.arm.linux.org.uk Subject: [PATCH] KGDB arch support for ARM This adds a backend, written by Deepak Saxena and George Davis as well as support for the TI OMAP boards, ADI Coyote, PXA2xx, ARM Versatile and PNX4008. Geoff Levand , Nicolas Pitre, and Manish Lachwani have contributed various fixups here as well. This should only require (on boards that don't have a custom uart) registering the uart with KGDB to add any other boards, or using kgdboe it should Just Work. Signed-off-by: Milind Dumbare Signed-off-by: Dmitry Antipov Signed-off-by: Tom Rini Signed-off-by: Deepak Saxena Signed-off-by: Vitaly Wool Signed-off-by: Jason Wessel --- arch/arm/kernel/Makefile | 1 arch/arm/kernel/kgdb-jmp.S | 32 +++++ arch/arm/kernel/kgdb.c | 207 +++++++++++++++++++++++++++++++++++ arch/arm/kernel/setup.c | 5 arch/arm/kernel/traps.c | 11 + arch/arm/mach-ixp2000/core.c | 4 arch/arm/mach-ixp2000/ixdp2x01.c | 6 + arch/arm/mach-ixp4xx/coyote-setup.c | 4 arch/arm/mach-ixp4xx/ixdp425-setup.c | 21 ++- arch/arm/mach-omap1/serial.c | 4 arch/arm/mach-pnx4008/core.c | 4 arch/arm/mach-pxa/Makefile | 1 arch/arm/mach-pxa/kgdb-serial.c | 97 ++++++++++++++++ arch/arm/mach-versatile/core.c | 8 + arch/arm/mm/extable.c | 7 + drivers/serial/Makefile | 1 drivers/serial/amba-pl011.c | 3 drivers/serial/pl011_kgdb.c | 117 +++++++++++++++++++ drivers/serial/pxa.c | 5 include/asm-arm/kgdb.h | 103 +++++++++++++++++ lib/Kconfig.kgdb | 55 ++++++++- 21 files changed, 689 insertions(+), 7 deletions(-) create mode 100644 arch/arm/kernel/kgdb-jmp.S create mode 100644 arch/arm/kernel/kgdb.c create mode 100644 arch/arm/mach-pxa/kgdb-serial.c create mode 100644 drivers/serial/pl011_kgdb.c create mode 100644 include/asm-arm/kgdb.h --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 --- /dev/null +++ b/arch/arm/kernel/kgdb-jmp.S @@ -0,0 +1,32 @@ +/* + * arch/arm/kernel/kgdb-jmp.S + * + * Trivial setjmp and longjmp procedures to support bus error recovery + * which may occur during kgdb memory read/write operations. + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2005 (c) MontaVista Software, Inc. 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 + +ENTRY (kgdb_fault_setjmp) + /* Save registers */ + stmia r0, {r0-r14} + str lr,[r0, #60] + mrs r1,cpsr + str r1,[r0,#64] + ldr r1,[r0,#4] + mov r0, #0 + mov pc,lr + +ENTRY (kgdb_fault_longjmp) + /* Restore registers */ + mov r1,#1 + str r1,[r0] + ldr r1,[r0, #64] + msr spsr,r1 + ldmia r0,{r0-pc}^ --- /dev/null +++ b/arch/arm/kernel/kgdb.c @@ -0,0 +1,207 @@ +/* + * arch/arm/kernel/kgdb.c + * + * ARM KGDB support + * + * Copyright (c) 2002-2004 MontaVista Software, Inc + * + * Authors: George Davis + * Deepak Saxena + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Make a local copy of the registers passed into the handler (bletch) */ +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs) +{ + int regno; + + /* Initialize all to zero (??) */ + for (regno = 0; regno < GDB_MAX_REGS; regno++) + gdb_regs[regno] = 0; + + gdb_regs[_R0] = kernel_regs->ARM_r0; + gdb_regs[_R1] = kernel_regs->ARM_r1; + gdb_regs[_R2] = kernel_regs->ARM_r2; + gdb_regs[_R3] = kernel_regs->ARM_r3; + gdb_regs[_R4] = kernel_regs->ARM_r4; + gdb_regs[_R5] = kernel_regs->ARM_r5; + gdb_regs[_R6] = kernel_regs->ARM_r6; + gdb_regs[_R7] = kernel_regs->ARM_r7; + gdb_regs[_R8] = kernel_regs->ARM_r8; + gdb_regs[_R9] = kernel_regs->ARM_r9; + gdb_regs[_R10] = kernel_regs->ARM_r10; + gdb_regs[_FP] = kernel_regs->ARM_fp; + gdb_regs[_IP] = kernel_regs->ARM_ip; + gdb_regs[_SP] = kernel_regs->ARM_sp; + gdb_regs[_LR] = kernel_regs->ARM_lr; + gdb_regs[_PC] = kernel_regs->ARM_pc; + gdb_regs[_CPSR] = kernel_regs->ARM_cpsr; +} + +/* Copy local gdb registers back to kgdb regs, for later copy to kernel */ +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs) +{ + kernel_regs->ARM_r0 = gdb_regs[_R0]; + kernel_regs->ARM_r1 = gdb_regs[_R1]; + kernel_regs->ARM_r2 = gdb_regs[_R2]; + kernel_regs->ARM_r3 = gdb_regs[_R3]; + kernel_regs->ARM_r4 = gdb_regs[_R4]; + kernel_regs->ARM_r5 = gdb_regs[_R5]; + kernel_regs->ARM_r6 = gdb_regs[_R6]; + kernel_regs->ARM_r7 = gdb_regs[_R7]; + kernel_regs->ARM_r8 = gdb_regs[_R8]; + kernel_regs->ARM_r9 = gdb_regs[_R9]; + kernel_regs->ARM_r10 = gdb_regs[_R10]; + kernel_regs->ARM_fp = gdb_regs[_FP]; + kernel_regs->ARM_ip = gdb_regs[_IP]; + kernel_regs->ARM_sp = gdb_regs[_SP]; + kernel_regs->ARM_lr = gdb_regs[_LR]; + kernel_regs->ARM_pc = gdb_regs[_PC]; + kernel_regs->ARM_cpsr = gdb_regs[_CPSR]; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, + struct task_struct *task) +{ + int regno; + struct pt_regs *thread_regs; + + /* Just making sure... */ + if (task == NULL) + return; + + /* Initialize to zero */ + for (regno = 0; regno < GDB_MAX_REGS; regno++) + gdb_regs[regno] = 0; + + /* Otherwise, we have only some registers from switch_to() */ + thread_regs = task_pt_regs(task); + gdb_regs[_R0] = thread_regs->ARM_r0; /* Not really valid? */ + gdb_regs[_R1] = thread_regs->ARM_r1; /* " " */ + gdb_regs[_R2] = thread_regs->ARM_r2; /* " " */ + gdb_regs[_R3] = thread_regs->ARM_r3; /* " " */ + gdb_regs[_R4] = thread_regs->ARM_r4; + gdb_regs[_R5] = thread_regs->ARM_r5; + gdb_regs[_R6] = thread_regs->ARM_r6; + gdb_regs[_R7] = thread_regs->ARM_r7; + gdb_regs[_R8] = thread_regs->ARM_r8; + gdb_regs[_R9] = thread_regs->ARM_r9; + gdb_regs[_R10] = thread_regs->ARM_r10; + gdb_regs[_FP] = thread_regs->ARM_fp; + gdb_regs[_IP] = thread_regs->ARM_ip; + gdb_regs[_SP] = thread_regs->ARM_sp; + gdb_regs[_LR] = thread_regs->ARM_lr; + gdb_regs[_PC] = thread_regs->ARM_pc; + gdb_regs[_CPSR] = thread_regs->ARM_cpsr; +} + +static int compiled_break; + +int kgdb_arch_handle_exception(int exception_vector, int signo, + int err_code, char *remcom_in_buffer, + char *remcom_out_buffer, + struct pt_regs *linux_regs) +{ + long addr; + char *ptr; + + switch (remcom_in_buffer[0]) { + case 'D': + case 'k': + case 'c': + kgdb_contthread = NULL; + + /* + * Try to read optional parameter, pc unchanged if no parm. + * If this was a compiled breakpoint, we need to move + * to the next instruction or we will just breakpoint + * over and over again. + */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) { + linux_regs->ARM_pc = addr; + } else if (compiled_break == 1) { + linux_regs->ARM_pc += 4; + } + + compiled_break = 0; + + return 0; + } + + return -1; +} + +static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) +{ + if (!kgdb_io_ops.read_char) + return 1; + + kgdb_handle_exception(1, SIGTRAP, 0, regs); + + return 0; +} + +static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr) +{ + if (!kgdb_io_ops.read_char) + return 1; + + compiled_break = 1; + kgdb_handle_exception(1, SIGTRAP, 0, regs); + + return 0; +} + +static struct undef_hook kgdb_brkpt_hook = { + .instr_mask = 0xffffffff, + .instr_val = KGDB_BREAKINST, + .fn = kgdb_brk_fn +}; + +static struct undef_hook kgdb_compiled_brkpt_hook = { + .instr_mask = 0xffffffff, + .instr_val = KGDB_COMPILED_BREAK, + .fn = kgdb_compiled_brk_fn +}; + +/* + * Register our undef instruction hooks with ARM undef core. + * We regsiter a hook specifically looking for the KGB break inst + * and we handle the normal undef case within the do_undefinstr + * handler. + */ +int kgdb_arch_init(void) +{ + register_undef_hook(&kgdb_brkpt_hook); + register_undef_hook(&kgdb_compiled_brkpt_hook); + + return 0; +} + +struct kgdb_arch arch_kgdb_ops = { +#ifndef __ARMEB__ + .gdb_bpt_instr = {0xfe, 0xde, 0xff, 0xe7} +#else /* ! __ARMEB__ */ + .gdb_bpt_instr = {0xe7, 0xff, 0xde, 0xfe} +#endif +}; --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -837,6 +837,11 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con; #endif #endif + +#if defined(CONFIG_KGDB) + extern void __init early_trap_init(void); + early_trap_init(); +#endif } --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -302,6 +302,7 @@ asmlinkage void __exception do_undefinst unsigned int instr; struct undef_hook *hook; siginfo_t info; + mm_segment_t fs; void __user *pc; unsigned long flags; @@ -312,6 +313,8 @@ asmlinkage void __exception do_undefinst */ regs->ARM_pc -= correction; + fs = get_fs(); + set_fs(KERNEL_DS); pc = (void __user *)instruction_pointer(regs); if (processor_mode(regs) == SVC_MODE) { @@ -321,6 +324,7 @@ asmlinkage void __exception do_undefinst } else { get_user(instr, (u32 __user *)pc); } + set_fs(fs); spin_lock_irqsave(&undef_lock, flags); list_for_each_entry(hook, &undef_hook, node) { @@ -706,6 +710,13 @@ EXPORT_SYMBOL(abort); void __init trap_init(void) { +#if defined(CONFIG_KGDB) + return; +} + +void __init early_trap_init(void) +{ +#endif unsigned long vectors = CONFIG_VECTORS_BASE; extern char __stubs_start[], __stubs_end[]; extern char __vectors_start[], __vectors_end[]; --- a/arch/arm/mach-ixp2000/core.c +++ b/arch/arm/mach-ixp2000/core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -184,6 +185,9 @@ static struct platform_device ixp2000_se void __init ixp2000_uart_init(void) { platform_device_register(&ixp2000_serial_device); +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(0, &ixp2000_serial_port); +#endif } --- a/arch/arm/mach-ixp2000/ixdp2x01.c +++ b/arch/arm/mach-ixp2000/ixdp2x01.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -413,6 +414,11 @@ static void __init ixdp2x01_init_machine platform_add_devices(ixdp2x01_devices, ARRAY_SIZE(ixdp2x01_devices)); ixp2000_uart_init(); ixdp2x01_uart_init(); + +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(0, ixdp2x01_serial_port1); + kgdb8250_add_platform_port(1, ixdp2x01_serial_port1); +#endif } --- a/arch/arm/mach-ixp4xx/coyote-setup.c +++ b/arch/arm/mach-ixp4xx/coyote-setup.c @@ -96,6 +96,10 @@ static void __init coyote_init(void) } platform_add_devices(coyote_devices, ARRAY_SIZE(coyote_devices)); + +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(0, &coyote_uart_data); +#endif } #ifdef CONFIG_ARCH_ADI_COYOTE --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -152,7 +153,8 @@ static struct plat_serial8250_port ixdp4 .mapbase = IXP4XX_UART1_BASE_PHYS, .membase = (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET, .irq = IRQ_IXP4XX_UART1, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | + UPF_SHARE_IRQ, .iotype = UPIO_MEM, .regshift = 2, .uartclk = IXP4XX_UART_XTAL, @@ -161,7 +163,8 @@ static struct plat_serial8250_port ixdp4 .mapbase = IXP4XX_UART2_BASE_PHYS, .membase = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET, .irq = IRQ_IXP4XX_UART2, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | + UPF_SHARE_IRQ, .iotype = UPIO_MEM, .regshift = 2, .uartclk = IXP4XX_UART_XTAL, @@ -219,12 +222,22 @@ static void __init ixdp425_init(void) platform_add_devices(ixdp425_devices, ARRAY_SIZE(ixdp425_devices)); } +static void __init ixdp425_map_io(void) +{ + ixp4xx_map_io(); + +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(0, &ixdp425_uart_data[0]); + kgdb8250_add_platform_port(1, &ixdp425_uart_data[1]); +#endif +} + #ifdef CONFIG_ARCH_IXDP425 MACHINE_START(IXDP425, "Intel IXDP425 Development Platform") /* Maintainer: MontaVista Software, Inc. */ .phys_io = IXP4XX_PERIPHERAL_BASE_PHYS, .io_pg_offst = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc, - .map_io = ixp4xx_map_io, + .map_io = ixdp425_map_io, .init_irq = ixp4xx_init_irq, .timer = &ixp4xx_timer, .boot_params = 0x0100, @@ -237,7 +250,7 @@ MACHINE_START(IXDP465, "Intel IXDP465 De /* Maintainer: MontaVista Software, Inc. */ .phys_io = IXP4XX_PERIPHERAL_BASE_PHYS, .io_pg_offst = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc, - .map_io = ixp4xx_map_io, + .map_io = ixdp425_map_io, .init_irq = ixp4xx_init_irq, .timer = &ixp4xx_timer, .boot_params = 0x0100, --- a/arch/arm/mach-omap1/serial.c +++ b/arch/arm/mach-omap1/serial.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -199,6 +200,9 @@ void __init omap_serial_init(void) break; } omap_serial_reset(&serial_platform_data[i]); +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(i, &serial_platform_data[i]); +#endif } } --- a/arch/arm/mach-pnx4008/core.c +++ b/arch/arm/mach-pnx4008/core.c @@ -224,6 +224,10 @@ static void __init pnx4008_init(void) spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); /* Switch on the UART clocks */ pnx4008_uart_init(); +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(0, &platform_serial_ports[0]); + kgdb8250_add_platform_port(1, &platform_serial_ports[1]); +#endif } static struct map_desc pnx4008_io_desc[] __initdata = { --- a/arch/arm/mach-pxa/Makefile +++ b/arch/arm/mach-pxa/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_LEDS) += $(led-y) # Misc features obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_PXA_SSP) += ssp.o +obj-$(CONFIG_KGDB_PXA_SERIAL) += kgdb-serial.o ifeq ($(CONFIG_PXA27x),y) obj-$(CONFIG_PM) += standby.o --- /dev/null +++ b/arch/arm/mach-pxa/kgdb-serial.c @@ -0,0 +1,97 @@ +/* + * linux/arch/arm/mach-pxa/kgdb-serial.c + * + * Provides low level kgdb serial support hooks for PXA2xx boards + * + * Author: Nicolas Pitre + * Copyright: (C) 2002-2005 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_KGDB_PXA_FFUART) + +#define UART FFUART +#define CKEN_UART CKEN6_FFUART +#define GPIO_RX_MD GPIO34_FFRXD_MD +#define GPIO_TX_MD GPIO39_FFTXD_MD + +#elif defined(CONFIG_KGDB_PXA_BTUART) + +#define UART BTUART +#define CKEN_UART CKEN7_BTUART +#define GPIO_RX_MD GPIO42_BTRXD_MD +#define GPIO_TX_MD GPIO43_BTTXD_MD + +#elif defined(CONFIG_KGDB_PXA_STUART) + +#define UART STUART +#define CKEN_UART CKEN5_STUART +#define GPIO_RX_MD GPIO46_STRXD_MD +#define GPIO_TX_MD GPIO47_STTXD_MD + +#endif + +#define UART_BAUDRATE (CONFIG_KGDB_BAUDRATE) + +static volatile unsigned long *port = (unsigned long *)&UART; + +static int kgdb_serial_init(void) +{ + pxa_set_cken(CKEN_UART, 1); + pxa_gpio_mode(GPIO_RX_MD); + pxa_gpio_mode(GPIO_TX_MD); + + port[UART_IER] = 0; + port[UART_LCR] = LCR_DLAB; + port[UART_DLL] = ((921600 / UART_BAUDRATE) & 0xff); + port[UART_DLM] = ((921600 / UART_BAUDRATE) >> 8); + port[UART_LCR] = LCR_WLS1 | LCR_WLS0; + port[UART_MCR] = 0; + port[UART_IER] = IER_UUE; + port[UART_FCR] = FCR_ITL_16; + + return 0; +} + +static void kgdb_serial_putchar(u8 c) +{ + if (!(CKEN & CKEN_UART) || port[UART_IER] != IER_UUE) + kgdb_serial_init(); + while (!(port[UART_LSR] & LSR_TDRQ)) + cpu_relax(); + port[UART_TX] = c; +} + +static void kgdb_serial_flush(void) +{ + if ((CKEN & CKEN_UART) && (port[UART_IER] & IER_UUE)) + while (!(port[UART_LSR] & LSR_TEMT)) + cpu_relax(); +} + +static int kgdb_serial_getchar(void) +{ + unsigned char c; + if (!(CKEN & CKEN_UART) || port[UART_IER] != IER_UUE) + kgdb_serial_init(); + while (!(port[UART_LSR] & UART_LSR_DR)) + cpu_relax(); + c = port[UART_RX]; + return c; +} + +struct kgdb_io kgdb_io_ops = { + .init = kgdb_serial_init, + .write_char = kgdb_serial_putchar, + .flush = kgdb_serial_flush, + .read_char = kgdb_serial_getchar, +}; --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -184,6 +184,14 @@ static struct map_desc versatile_io_desc .type = MT_DEVICE }, #endif +#ifdef CONFIG_KGDB_AMBA_PL011 + { + .virtual = IO_ADDRESS(CONFIG_KGDB_AMBA_BASE), + .pfn = __phys_to_pfn(CONFIG_KGDB_AMBA_BASE), + .length = SZ_4K, + .type = MT_DEVICE + }, +#endif #ifdef CONFIG_PCI { .virtual = IO_ADDRESS(VERSATILE_PCI_CORE_BASE), --- a/arch/arm/mm/extable.c +++ b/arch/arm/mm/extable.c @@ -2,6 +2,7 @@ * linux/arch/arm/mm/extable.c */ #include +#include #include int fixup_exception(struct pt_regs *regs) @@ -11,6 +12,12 @@ int fixup_exception(struct pt_regs *regs fixup = search_exception_tables(instruction_pointer(regs)); if (fixup) regs->ARM_pc = fixup->fixup; +#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 fixup != NULL; } --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mc obj-$(CONFIG_SERIAL_8250_AU1X00) += 8250_au1x00.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o +obj-$(CONFIG_KGDB_AMBA_PL011) += pl011_kgdb.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_PXA) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -332,7 +332,8 @@ static int pl011_startup(struct uart_por /* * Allocate the IRQ */ - retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); + retval = request_irq(uap->port.irq, pl011_int, IRQF_SHARED, + "uart-pl011", uap); if (retval) goto clk_dis; --- /dev/null +++ b/drivers/serial/pl011_kgdb.c @@ -0,0 +1,117 @@ +/* + * driver/serial/pl011_kgdb.c + * + * Support for KGDB on ARM AMBA PL011 UARTs + * + * Authors: Manish Lachwani + * Deepak Saxena + * + * Copyright (c) 2005-2007 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 expressor implied. + * + */ +#include +#include +#include +#include + +#include +#include + +static int kgdb_irq = CONFIG_KGDB_AMBA_IRQ; + +#define UART_DIVISOR (CONFIG_KGDB_AMBA_UARTCLK * 4 / CONFIG_KGDB_BAUDRATE) +/* + * Todo: IO_ADDRESS is not very generic across ARM... + */ +static volatile unsigned char *kgdb_port = + (unsigned char *)IO_ADDRESS(CONFIG_KGDB_AMBA_BASE); + +/* + * Init code taken from amba-pl011.c. + */ +static int kgdb_serial_init(void) +{ + writew(0, kgdb_port + UART010_CR); + + /* Set baud rate */ + writew(UART_DIVISOR & 0x3f, kgdb_port + UART011_FBRD); + writew(UART_DIVISOR >> 6, kgdb_port + UART011_IBRD); + + writew(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, kgdb_port + + UART010_LCRH); + writew(UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE, + kgdb_port + UART010_CR); + + writew(UART011_RXIM, kgdb_port + UART011_IMSC); + + return 0; +} + +static void kgdb_serial_putchar(u8 ch) +{ + unsigned int status; + + do { + status = readw(kgdb_port + UART01x_FR); + } while (status & UART01x_FR_TXFF); + + writew(ch, kgdb_port + UART01x_DR); +} + +static int kgdb_serial_getchar(void) +{ + unsigned int status; + int ch; + +#ifdef CONFIG_DEBUG_LL_OLD + printascii("Entering serial_getchar loop"); +#endif + do { + status = readw(kgdb_port + UART01x_FR); + } while (status & UART01x_FR_RXFE); + ch = readw(kgdb_port + UART01x_DR); +#ifdef CONFIG_DEBUG_LL_OLD + printascii("Exited serial_getchar loop"); + printascii("Read char: "); + printch(ch); + printascii("\n"); +#endif + return ch; +} + +static irqreturn_t kgdb_interrupt(int irq, void *dev_id) +{ + int status = readw(kgdb_port + UART011_MIS); + +#ifdef CONFIG_DEBUG_LL_OLD + printascii("KGDB irq\n"); +#endif + if (irq != kgdb_irq) + return IRQ_NONE; + + if (status & 0x40) + breakpoint(); + + return IRQ_HANDLED; +} + +static void __init kgdb_hookup_irq(void) +{ + int retval; + retval = request_irq(kgdb_irq, kgdb_interrupt, IRQF_SHARED, + "KGDB-serial", (void *)kgdb_port); + if (retval) { + printk(KERN_ERR "pl011_kgdb: ERROR requesting irq\n"); + } +} + +struct kgdb_io kgdb_io_ops = { + .init = kgdb_serial_init, + .write_char = kgdb_serial_putchar, + .read_char = kgdb_serial_getchar, + .late_init = kgdb_hookup_irq, +}; --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -42,6 +42,9 @@ #include #include #include +#ifdef CONFIG_KGDB_CONSOLE +#include +#endif #include #include @@ -690,6 +693,8 @@ serial_pxa_console_init(void) console_initcall(serial_pxa_console_init); #define PXA_CONSOLE &serial_pxa_console +#elif defined(CONFIG_KGDB_CONSOLE) +#define PXA_CONSOLE &kgdbcons #else #define PXA_CONSOLE NULL #endif --- /dev/null +++ b/include/asm-arm/kgdb.h @@ -0,0 +1,103 @@ +/* + * include/asm-arm/kgdb.h + * + * ARM KGDB support + * + * Author: Deepak Saxena + * + * Copyright (C) 2002 MontaVista Software Inc. + * + */ + +#ifndef __ASM_KGDB_H__ +#define __ASM_KGDB_H__ + +#include +#include + + +/* + * GDB assumes that we're a user process being debugged, so + * it will send us an SWI command to write into memory as the + * debug trap. When an SWI occurs, the next instruction addr is + * placed into R14_svc before jumping to the vector trap. + * This doesn't work for kernel debugging as we are already in SVC + * we would loose the kernel's LR, which is a bad thing. This + * is bad thing. + * + * By doing this as an undefined instruction trap, we force a mode + * switch from SVC to UND mode, allowing us to save full kernel state. + * + * We also define a KGDB_COMPILED_BREAK which can be used to compile + * in breakpoints. This is important for things like sysrq-G and for + * the initial breakpoint from trap_init(). + * + * Note to ARM HW designers: Add real trap support like SH && PPC to + * make our lives much much simpler. :) + */ +#define BREAK_INSTR_SIZE 4 +#define GDB_BREAKINST 0xef9f0001 +#define KGDB_BREAKINST 0xe7ffdefe +#define KGDB_COMPILED_BREAK 0xe7ffdeff +#define CACHE_FLUSH_IS_SAFE 1 + +#ifndef __ASSEMBLY__ + +#define BREAKPOINT() asm(".word 0xe7ffdeff") + + +extern void kgdb_handle_bus_error(void); +extern int kgdb_fault_expected; +#endif /* !__ASSEMBLY__ */ + +/* + * From Kevin Hilman: + * + * gdb is expecting the following registers layout. + * + * r0-r15: 1 long word each + * f0-f7: unused, 3 long words each !! + * fps: unused, 1 long word + * cpsr: 1 long word + * + * Even though f0-f7 and fps are not used, they need to be + * present in the registers sent for correct processing in + * the host-side gdb. + * + * In particular, it is crucial that CPSR is in the right place, + * otherwise gdb will not be able to correctly interpret stepping over + * conditional branches. + */ +#define _GP_REGS 16 +#define _FP_REGS 8 +#define _EXTRA_REGS 2 +#define GDB_MAX_REGS (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS) + +#define KGDB_MAX_NO_CPUS 1 +#define BUFMAX 400 +#define NUMREGBYTES (GDB_MAX_REGS << 2) +#define NUMCRITREGBYTES (32 << 2) + +#define _R0 0 +#define _R1 1 +#define _R2 2 +#define _R3 3 +#define _R4 4 +#define _R5 5 +#define _R6 6 +#define _R7 7 +#define _R8 8 +#define _R9 9 +#define _R10 10 +#define _FP 11 +#define _IP 12 +#define _SP 13 +#define _LR 14 +#define _PC 15 +#define _CPSR (GDB_MAX_REGS - 1) + +/* So that we can denote the end of a frame for tracing, in the simple + * case. */ +#define CFI_END_FRAME(func) __CFI_END_FRAME(_PC, _SP, func) + +#endif /* __ASM_KGDB_H__ */ --- a/lib/Kconfig.kgdb +++ b/lib/Kconfig.kgdb @@ -14,7 +14,7 @@ config KGDB bool "KGDB: kernel debugging with remote gdb" select WANT_EXTRA_DEBUG_INFORMATION select KGDB_ARCH_HAS_SHADOW_INFO if X86_64 - depends on DEBUG_KERNEL && (X86 || MIPS || (SUPERH && !SUPERH64) || IA64 || PPC) + depends on DEBUG_KERNEL && (ARM || X86 || MIPS || (SUPERH && !SUPERH64) || IA64 || PPC) help If you say Y here, it will be possible to remotely debug the kernel using gdb. Documentation of kernel debugger is available @@ -45,6 +45,8 @@ choice default KGDB_SIBYTE if SIBYTE_SB1xxx_SOC default KGDB_TXX9 if CPU_TX49XX default KGDB_SH_SCI if SERIAL_SH_SCI + default KGDB_PXA_SERIAL if ARCH_PXA + default KGDB_AMBA_PL011 if ARM_AMBA default KGDB_8250_NOMODULE help There are a number of different ways in which you can communicate @@ -111,6 +113,34 @@ config KGDB_SH_SCI depends on SUPERH && SERIAL_SH_SCI help Uses the SH SCI(F) serial port to communicate with the host GDB. + +config KGDB_AMBA_PL011 + bool "KGDB: On ARM AMBA PL011 Serial Port" + depends on ARM && ARCH_VERSATILE + help + Enables the KGDB serial driver for the AMBA bus PL011 serial + devices from ARM. + +config KGDB_PXA_SERIAL + bool "KGDB: On the PXA2xx serial port" + depends on ARCH_PXA + help + Enables the KGDB serial driver for Intel PXA SOC +endchoice + +choice + prompt "PXA UART to use for KGDB" + depends on KGDB_PXA_SERIAL + default KGDB_PXA_FFUART + +config KGDB_PXA_FFUART + bool "FFUART" + +config KGDB_PXA_BTUART + bool "BTUART" + +config KGDB_PXA_STUART + bool "STUART" endchoice choice @@ -179,7 +209,7 @@ config KGDB_BAUDRATE int "Debug serial port baud rate" depends on (KGDB_8250 && KGDB_SIMPLE_SERIAL) || \ KGDB_MPSC || KGDB_CPM_UART || \ - KGDB_TXX9 + KGDB_TXX9 || KGDB_PXA_SERIAL || KGDB_AMBA_PL011 default "115200" help gdb and the kernel stub need to agree on the baud rate to be @@ -195,6 +225,27 @@ config KGDB_PORT_NUM help Pick the port number (0 based) for KGDB to use. +config KGDB_AMBA_BASE + hex "AMBA PL011 Serial Port Base Address" + default 0x101f2000 if ARCH_VERSATILE + depends on KGDB_AMBA_PL011 + help + Base address of the AMBA port that KGDB will use. + +config KGDB_AMBA_UARTCLK + int "AMBAPL011 Serial UART Clock Frequency" + default 24000000 if ARCH_VERSATILE + depends on KGDB_AMBA_PL011 + help + Frequency (in HZ) of the ARM AMBA UART clock + +config KGDB_AMBA_IRQ + int "AMBA PL011 Serial Port IRQ" + default 13 if ARCH_VERSATILE + depends on KGDB_AMBA_PL011 + help + Pick the IRQ of the AMBA port that KGDB will use. + config KGDB_8250_CONF_STRING string "Configuration string for KGDB" depends on KGDB_8250_NOMODULE && !KGDB_SIMPLE_SERIAL