diff -Naur linux-2.4.23/arch/i386/config.in linux-2.4.23-kgdb/arch/i386/config.in --- linux-2.4.23/arch/i386/config.in 2003-12-17 16:15:30.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/config.in 2003-12-17 15:28:19.000000000 +0530 @@ -470,15 +470,21 @@ mainmenu_option next_comment comment 'Kernel hacking' -bool 'Kernel debugging' CONFIG_DEBUG_KERNEL -if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then - bool ' Check for stack overflows' CONFIG_DEBUG_STACKOVERFLOW - bool ' Debug high memory support' CONFIG_DEBUG_HIGHMEM - bool ' Debug memory allocations' CONFIG_DEBUG_SLAB - bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT - bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ - bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK - bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER +bool 'KGDB: Remote (serial) kernel debugging with gdb' CONFIG_KGDB +if [ "$CONFIG_KGDB" != "n" ]; then + bool 'KGDB: Thread analysis' CONFIG_KGDB_THREAD + bool 'KGDB: Console messages through gdb' CONFIG_GDB_CONSOLE +else + bool 'Kernel debugging' CONFIG_DEBUG_KERNEL + if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then + bool ' Check for stack overflows' CONFIG_DEBUG_STACKOVERFLOW + bool ' Debug high memory support' CONFIG_DEBUG_HIGHMEM + bool ' Debug memory allocations' CONFIG_DEBUG_SLAB + bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT + bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ + bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK + bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER + fi fi int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 diff -Naur linux-2.4.23/arch/i386/kernel/entry.S linux-2.4.23-kgdb/arch/i386/kernel/entry.S --- linux-2.4.23/arch/i386/kernel/entry.S 2003-12-17 16:12:58.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/kernel/entry.S 2003-12-17 15:28:19.000000000 +0530 @@ -264,7 +264,7 @@ ALIGN reschedule: - call SYMBOL_NAME(schedule) # test + call SYMBOL_NAME(user_schedule) # test jmp ret_from_sys_call ENTRY(divide_error) @@ -402,6 +402,31 @@ pushl $ SYMBOL_NAME(do_spurious_interrupt_bug) jmp error_code +#ifdef CONFIG_KGDB_THREAD +ENTRY(kern_schedule) + pushl %ebp + movl %esp, %ebp + pushl %ss + pushl %ebp + pushfl + pushl %cs + pushl 4(%ebp) + pushl %eax + pushl %es + pushl %ds + pushl %eax + pushl (%ebp) + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + call kern_do_schedule + movl %ebp, %esp + pop %ebp + ret +#endif + .data ENTRY(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ diff -Naur linux-2.4.23/arch/i386/kernel/gdbstart.c linux-2.4.23-kgdb/arch/i386/kernel/gdbstart.c --- linux-2.4.23/arch/i386/kernel/gdbstart.c 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/kernel/gdbstart.c 2003-12-17 15:28:19.000000000 +0530 @@ -0,0 +1,148 @@ +/* + * This program opens a tty file and issues the GDB stub activating + * ioctl on it. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIOCGDB 0x547F + +char *tty_name = "/dev/ttyS0" ; /* COM1 port */ +int speed = 9600 ; /* default speed */ +struct termios save_ts ; /* original term struct */ + +void print_usage(void) +{ + printf("gdbstub [-s speed] [-t tty-dev]\n") ; + printf(" defaults: /dev/ttyS0 with speed unmodified by this program\n"); + +} /* print_usage */ + +void tty_err(char *msg) +{ + char buf[100] ; + + strcpy(buf, msg) ; + strcat(buf, ": ") ; + strcat(buf, tty_name) ; + perror(buf) ; + exit(1) ; + +} /* tty_err */ + + +void setup_term(int fd) +{ + struct termios ts ; + int speed_code ; + + if (tcgetattr(fd, &ts) < 0) tty_err("tcgetattr") ; + + save_ts = ts ; + switch (speed) + { + case 4800: + speed_code = B4800 ; + break ; + case 9600: + speed_code = B9600 ; + break ; + case 19200: + speed_code = B19200 ; + break ; + case 38400: + speed_code = B38400 ; + break ; + case 57600: + speed_code = B57600 ; + break ; + case 115200: + speed_code = B115200 ; + break ; + case 230400: + speed_code = B230400 ; + break ; + default: + printf("Invalid speed: %d\n", speed) ; + exit(1) ; + } + + ts.c_cflag = CS8 | CREAD | CLOCAL ; + if (cfsetospeed(&ts, speed_code) < 0) tty_err("cfsetospeed") ; + if (cfsetispeed(&ts, speed_code) < 0) tty_err("cfsetispeed") ; + + if (tcsetattr(fd, TCSANOW, &ts) < 0) tty_err("tcsetattr") ; + +} /* setup_term */ + +void main(int argc, char **argv) +{ + int opt ; + int fil ; + int rslt ; + + while ((opt = getopt(argc, argv, "hs:t:")) > 0) + { + switch (opt) + { + case 's': + speed = atol(optarg) ; + break ; + case 't': + tty_name = optarg ; + break ; + case ':': + printf("Invalid option\n") ; + break ; + case '?': + case 'h': + default: + print_usage() ; + return ; + } + } + + fil = open(tty_name, O_RDWR) ; + if (fil < 0) + { + perror(tty_name) ; + return ; + } + + + setup_term(fil) ; + + /* + * When we issue this ioctl, control will not return until + * the debugger running on the remote host machine says "go". + */ + printf("\nAbout to activate GDB stub in the kernel on %s\n", tty_name) ; + printf("Hit CR to continue, kill program to abort -- ") ; + getchar() ; + sync() ; + rslt = ioctl(fil, TIOCGDB, 0) ; + if (rslt < 0) + { + perror("TIOCGDB ioctl") ; + return ; + } + + printf("\nGDB stub successfully activated\n") ; + + for (;;) + { + pause() ; + } + + if (tcsetattr(fil, TCSANOW, &save_ts) < 0) tty_err("tcsetattr") ; + +} /* main */ diff -Naur linux-2.4.23/arch/i386/kernel/i386-stub.c linux-2.4.23-kgdb/arch/i386/kernel/i386-stub.c --- linux-2.4.23/arch/i386/kernel/i386-stub.c 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/kernel/i386-stub.c 2003-12-17 15:28:19.000000000 +0530 @@ -0,0 +1,457 @@ +/* + * + * 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, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +/* + * Copyright (C) 2000-2001 VERITAS Software Corporation. + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: Amit Kale + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe + * Integrated into 2.2.5 kernel by Tigran Aivazian + * thread support, + * support for multiple processors, + * support for ia-32(x86) hardware debugging, + * Console support, + * handling nmi watchdog + * Amit S. Kale ( amitkale@emsyssoft.com ) + * + * + * 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 an int 3. + * + ************* + * + * 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) + * + * 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 + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#ifdef CONFIG_GDB_CONSOLE +#include +#endif +#include + +/* Put the error code here just in case the user cares. */ +int gdb_i386errcode; +/* Likewise, the vector number here (since GDB only gets the signal + number through the usual means, and that's not very specific). */ +int gdb_i386vector = -1; + +#if KGDB_MAX_NO_CPUS != 8 +#error change the definition of slavecpulocks +#endif + +static void i386_regs_to_gdb_regs(int *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_EAX] = regs->eax; + gdb_regs[_EBX] = regs->ebx; + gdb_regs[_ECX] = regs->ecx; + gdb_regs[_EDX] = regs->edx; + gdb_regs[_ESI] = regs->esi; + gdb_regs[_EDI] = regs->edi; + gdb_regs[_EBP] = regs->ebp; + gdb_regs[ _DS] = regs->xds; + gdb_regs[ _ES] = regs->xes; + gdb_regs[ _PS] = regs->eflags; + gdb_regs[ _CS] = regs->xcs; + gdb_regs[ _PC] = regs->eip; + gdb_regs[_ESP] = (int) (®s->esp) ; + gdb_regs[ _SS] = __KERNEL_DS; + gdb_regs[ _FS] = 0xFFFF; + gdb_regs[ _GS] = 0xFFFF; +} /* regs_to_gdb_regs */ + +static void i386_sleeping_thread_to_gdb_regs(int *gdb_regs, struct task_struct *p) +{ + gdb_regs[_EAX] = 0; + gdb_regs[_EBX] = 0; + gdb_regs[_ECX] = 0; + gdb_regs[_EDX] = 0; + gdb_regs[_ESI] = 0; + gdb_regs[_EDI] = 0; + gdb_regs[_EBP] = *(int *)p->thread.esp; + gdb_regs[_DS] = __KERNEL_DS; + gdb_regs[_ES] = __KERNEL_DS; + gdb_regs[_PS] = 0; + gdb_regs[_CS] = __KERNEL_CS; + gdb_regs[_PC] = p->thread.eip; + gdb_regs[_ESP] = p->thread.esp; + gdb_regs[_SS] = __KERNEL_DS; + gdb_regs[_FS] = 0xFFFF; + gdb_regs[_GS] = 0xFFFF; +} + +static void i386_gdb_regs_to_regs(int *gdb_regs, struct pt_regs *regs) +{ + regs->eax = gdb_regs[_EAX] ; + regs->ebx = gdb_regs[_EBX] ; + regs->ecx = gdb_regs[_ECX] ; + regs->edx = gdb_regs[_EDX] ; + regs->esi = gdb_regs[_ESI] ; + regs->edi = gdb_regs[_EDI] ; + regs->ebp = gdb_regs[_EBP] ; + regs->xds = gdb_regs[ _DS] ; + regs->xes = gdb_regs[ _ES] ; + regs->eflags= gdb_regs[ _PS] ; + regs->xcs = gdb_regs[ _CS] ; + regs->eip = gdb_regs[ _PC] ; +#if 0 /* can't change these */ + regs->esp = gdb_regs[_ESP] ; + regs->xss = gdb_regs[ _SS] ; + regs->fs = gdb_regs[ _FS] ; + regs->gs = gdb_regs[ _GS] ; +#endif + +} /* gdb_regs_to_regs */ + +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned addr; +} breakinfo[4] = { { +enabled:0}, { +enabled:0}, { +enabled:0}, { +enabled:0}}; + +void i386_correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned dr7; + + asm volatile ("movl %%db7, %0\n":"=r" (dr7) + :); + do { + unsigned addr0, addr1, addr2, addr3; + asm volatile ("movl %%db0, %0\n" + "movl %%db1, %1\n" + "movl %%db2, %2\n" + "movl %%db3, %3\n":"=r" (addr0), "=r"(addr1), + "=r"(addr2), "=r"(addr3):); + } while (0); + correctit = 0; + for (breakno = 0; breakno < 3; breakno++) { + breakbit = 2 << (breakno << 1); + if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { + correctit = 1; + dr7 |= breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + dr7 |= (((breakinfo[breakno].len << 2) | + breakinfo[breakno].type) << 16) << + (breakno << 2); + switch (breakno) { + case 0: + asm volatile ("movl %0, %%dr0\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 1: + asm volatile ("movl %0, %%dr1\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 2: + asm volatile ("movl %0, %%dr2\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 3: + asm volatile ("movl %0, %%dr3\n"::"r" + (breakinfo[breakno].addr)); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) { + asm volatile ("movl %0, %%db7\n"::"r" (dr7)); + } +} + +int i386_remove_hw_break(unsigned long addr, int type) +{ + int i, idx = -1; + for (i = 0; i < 4; i ++) { + if (breakinfo[i].addr == addr && breakinfo[i].enabled) { + idx = i; + break; + } + } + if (idx == -1) + return -1; + + breakinfo[idx].enabled = 0; + return 0; +} + +int i386_set_hw_break(unsigned long addr, int type) +{ + int i, idx = -1; + for (i = 0; i < 4; i ++) { + if (!breakinfo[i].enabled) { + idx = i; + break; + } + } + if (idx == -1) + return -1; + + breakinfo[idx].enabled = 1; + breakinfo[idx].type = type; + breakinfo[idx].len = 1; + breakinfo[idx].addr = addr; + return 0; +} + +int remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 0; + return 0; +} + +int set_hw_break(unsigned breakno, + unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].len = len; + breakinfo[breakno].addr = addr; + return 0; +} + +static void i386_printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + unsigned dr6; + int i; + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + asm volatile ("movl %%db6, %0\n":"=r" (dr6) + :); + if (dr6 & 0x4000) { + sprintf(buffer, "Single step"); + return; + } + for (i = 0; i < 4; ++i) { + if (dr6 & (1 << i)) { + sprintf(buffer, "Hardware breakpoint %d", i); + return; + } + } + sprintf(buffer, "Unknown trap"); + return; +} + +static void i386_disable_hw_debug(struct pt_regs *regs) +{ + /* Disable hardware debugging while we are in kgdb */ + asm volatile("movl %0,%%db7": /* no output */ : "r"(0)); +} + +static void i386_post_master_code(struct pt_regs *regs, int eVector, int err_code) +{ + /* Master processor is completely in the debugger */ + gdb_i386vector = eVector; + gdb_i386errcode = err_code; +} +static int i386_handle_exception(int exceptionVector, int signo, int err_code, + char *remcomInBuffer, char *remcomOutBuffer, + struct pt_regs *linux_regs) +{ + int addr, length; + int breakno, breaktype; + char *ptr; + int newPC; + int dr6; + + switch (remcomInBuffer[0]) { + case 'c': + case 's': + if (kgdb_contthread && kgdb_contthread != current) { + strcpy(remcomOutBuffer, "E00"); + break; + } + + kgdb_contthread = NULL; + + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + linux_regs->eip = addr; + } + newPC = linux_regs->eip; + + /* clear the trace bit */ + linux_regs->eflags &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') { + linux_regs->eflags |= 0x100; + kgdb_step = 1; + } + + asm volatile ("movl %%db6, %0\n" : "=r" (dr6)); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno)) { + if (breakinfo[breakno].type == 0) { + /* Set restore flag */ + linux_regs->eflags |= 0x10000; + break; + } + } + } + } + i386_correct_hw_break(); + asm volatile ("movl %0, %%db6\n"::"r" (0)); + + return (0); + + case 'Y': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + ptr++; + hexToInt(&ptr, &breaktype); + ptr++; + hexToInt(&ptr, &length); + ptr++; + hexToInt(&ptr, &addr); + if (set_hw_break(breakno & 0x3, breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + } /* switch */ + return -1; /* this means that we do not want to exit from the handler */ +} + +int i386_kgdb_init(void) +{ + return 0; +} + +struct kgdb_arch arch_kgdb_ops = { + {0xcc}, + KGDB_HW_BREAKPOINT, + i386_kgdb_init, + i386_regs_to_gdb_regs, + i386_sleeping_thread_to_gdb_regs, + i386_gdb_regs_to_regs, + i386_printexceptioninfo, + i386_disable_hw_debug, + i386_post_master_code, + i386_handle_exception, + i386_set_hw_break, + i386_remove_hw_break, + i386_correct_hw_break, + NULL, +}; diff -Naur linux-2.4.23/arch/i386/kernel/Makefile linux-2.4.23-kgdb/arch/i386/kernel/Makefile --- linux-2.4.23/arch/i386/kernel/Makefile 2003-12-17 16:15:30.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/kernel/Makefile 2003-12-17 15:28:19.000000000 +0530 @@ -10,7 +10,15 @@ .S.o: $(CC) $(AFLAGS) -traditional -c $< -o $*.o -all: kernel.o head.o init_task.o +ifeq ($(CONFIG_KGDB),y) +GDBSTART=gdbstart +GDBCLEAN= -rm -f gdbstart /sbin/gdbstart +else +GDBSTART= +GDBCLEAN= +endif + +all: kernel.o head.o init_task.o $(GDBSTART) O_TARGET := kernel.o @@ -32,6 +40,7 @@ obj-$(CONFIG_MCA) += mca.o obj-$(CONFIG_MTRR) += mtrr.o +obj-$(CONFIG_KGDB) += i386-stub.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_MICROCODE) += microcode.o @@ -44,4 +53,11 @@ obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o obj-$(CONFIG_EDD) += edd.o +gdbstart: gdbstart.o + ${HOSTCC} -o gdbstart gdbstart.o +gdbstart.o: gdbstart.c + ${HOSTCC} -c -o gdbstart.o gdbstart.c + +kernelclean: dummy + include $(TOPDIR)/Rules.make diff -Naur linux-2.4.23/arch/i386/kernel/nmi.c linux-2.4.23-kgdb/arch/i386/kernel/nmi.c --- linux-2.4.23/arch/i386/kernel/nmi.c 2003-12-17 16:12:58.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/kernel/nmi.c 2003-12-17 16:24:26.000000000 +0530 @@ -25,6 +25,20 @@ #include #include +#ifdef CONFIG_KGDB +extern gdb_debug_hook * linux_debug_hook; +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ + { \ + if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs)) \ + { \ + (*linux_debug_hook)(trapnr, signr, error_code, regs) ; \ + after; \ + } \ + } +#else +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) +#endif + unsigned int nmi_watchdog = NMI_NONE; static unsigned int nmi_hz = HZ; unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ @@ -347,7 +361,20 @@ int sum, cpu = smp_processor_id(); sum = apic_timer_irqs[cpu]; +#if defined(CONFIG_KGDB) && defined(CONFIG_SMP) + if (atomic_read(&kgdb_lock)) { + + /* + * The machine is in kgdb, hold this cpu if already + * not held. + */ + if (!procindebug[cpu] && atomic_read(&kgdb_lock) != (cpu + 1)) { + gdb_wait(regs); + } + alert_counter[cpu] = 0; + } else +#endif if (last_irq_sums[cpu] == sum) { /* * Ayiee, looks like this CPU is stuck ... @@ -355,6 +382,9 @@ */ alert_counter[cpu]++; if (alert_counter[cpu] == 5*nmi_hz) { + + CHK_REMOTE_DEBUG(2,SIGSEGV,0,regs,) + spin_lock(&nmi_print_lock); /* * We are in trouble anyway, lets at least try diff -Naur linux-2.4.23/arch/i386/kernel/signal.c linux-2.4.23-kgdb/arch/i386/kernel/signal.c --- linux-2.4.23/arch/i386/kernel/signal.c 2003-10-22 02:59:00.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/kernel/signal.c 2003-12-17 15:28:19.000000000 +0530 @@ -695,7 +695,8 @@ * have been cleared if the watchpoint triggered * inside the kernel. */ - __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7])); + if (current->thread.debugreg[7]) + __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7])); /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs); diff -Naur linux-2.4.23/arch/i386/kernel/traps.c linux-2.4.23-kgdb/arch/i386/kernel/traps.c --- linux-2.4.23/arch/i386/kernel/traps.c 2003-10-22 02:59:00.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/kernel/traps.c 2003-12-17 15:28:19.000000000 +0530 @@ -48,8 +48,24 @@ #endif #include +#ifdef CONFIG_KGDB +#include +#endif #include +#ifdef CONFIG_KGDB +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ + { \ + if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs)) \ + { \ + (*linux_debug_hook)(trapnr, signr, error_code, regs) ; \ + after; \ + } \ + } +#else +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) +#endif + asmlinkage int system_call(void); asmlinkage void lcall7(void); asmlinkage void lcall27(void); @@ -289,6 +305,7 @@ bust_spinlocks(1); handle_BUG(regs); printk("%s: %04lx\n", str, err & 0xffff); + CHK_REMOTE_DEBUG(1,SIGTRAP,err,regs,) show_registers(regs); bust_spinlocks(0); spin_unlock_irq(&die_lock); @@ -353,6 +370,7 @@ #define DO_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,)\ do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ } @@ -370,7 +388,10 @@ #define DO_VM86_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,goto skip_trap)\ do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ +skip_trap: \ + return; \ } #define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ @@ -422,6 +443,7 @@ regs->eip = fixup; return; } + CHK_REMOTE_DEBUG(13,SIGSEGV,error_code,regs,) die("general protection fault", regs, error_code); } } @@ -533,7 +555,7 @@ __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); /* If the user set TF, it's simplest to clear it right away. */ - if ((eip >=PAGE_OFFSET) && (regs->eflags & TF_MASK)) + if ((eip >=PAGE_OFFSET) && (regs->eflags & TF_MASK) && !kgdb_step) goto clear_TF; /* Mask out spurious debug traps due to lazy DR7 setting */ @@ -549,7 +571,7 @@ tsk->thread.debugreg[6] = condition; /* Mask out spurious TF errors due to lazy TF clearing */ - if (condition & DR_STEP) { + if (condition & DR_STEP && !kgdb_step) { /* * The TF error should be masked out only if the current * process is not traced and if the TRAP flag has been set @@ -572,11 +594,13 @@ info.si_errno = 0; info.si_code = TRAP_BRKPT; - /* If this is a kernel mode trap, save the user PC on entry to - * the kernel, that's what the debugger can make sense of. - */ - info.si_addr = ((regs->xcs & 3) == 0) ? (void *)tsk->thread.eip : - (void *)regs->eip; + + /* If this is a kernel mode trap, we need to reset db7 to allow us + * to continue sanely */ + if ((regs->xcs & 3) == 0) + goto clear_dr7; + + info.si_addr = (void *)regs->eip; force_sig_info(SIGTRAP, &info, tsk); /* Disable additional traps. They'll be re-enabled when @@ -586,6 +610,7 @@ __asm__("movl %0,%%db7" : /* no output */ : "r" (0)); + CHK_REMOTE_DEBUG(1,SIGTRAP,error_code,regs,) return; debug_vm86: diff -Naur linux-2.4.23/arch/i386/Makefile linux-2.4.23-kgdb/arch/i386/Makefile --- linux-2.4.23/arch/i386/Makefile 2003-12-17 16:12:58.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/Makefile 2003-12-17 15:28:19.000000000 +0530 @@ -113,6 +113,11 @@ $(MAKE) linuxsubdirs SUBDIRS=arch/i386/mm MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot +ifeq ($(CONFIG_X86_REMOTE_DEBUG),y) +CLEANKERNEL = $(MAKE) -C arch/$(ARCH)/kernel kernelclean +else +CLEANKERNEL = +endif vmlinux: arch/i386/vmlinux.lds @@ -148,6 +153,7 @@ archclean: @$(MAKEBOOT) clean + @$(CLEANKERNEL) archmrproper: diff -Naur linux-2.4.23/arch/i386/mm/fault.c linux-2.4.23-kgdb/arch/i386/mm/fault.c --- linux-2.4.23/arch/i386/mm/fault.c 2003-10-22 02:58:59.000000000 +0530 +++ linux-2.4.23-kgdb/arch/i386/mm/fault.c 2003-12-17 15:28:19.000000000 +0530 @@ -2,6 +2,11 @@ * linux/arch/i386/mm/fault.c * * Copyright (C) 1995 Linus Torvalds + * + * Change History + * + * Tigran Aivazian Remote debugging support. + * */ #include @@ -19,6 +24,9 @@ #include #include #include /* For unblank_screen() */ +#ifdef CONFIG_KGDB +#include +#endif #include #include @@ -183,6 +191,13 @@ if (in_interrupt() || !mm) goto no_context; +#ifdef CONFIG_KGDB + if (kgdb_memerr_expected) { + /* We are in kgdb. Can't handle the fault */ + goto no_context; + } +#endif + down_read(&mm->mmap_sem); vma = find_vma(mm, address); @@ -301,6 +316,12 @@ return; } +#ifdef CONFIG_KGDB + if (linux_debug_hook != (gdb_debug_hook *) NULL) { + (*linux_debug_hook)(14, SIGSEGV, error_code, regs); + } +#endif + /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. @@ -308,6 +329,7 @@ bust_spinlocks(1); + if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); else diff -Naur linux-2.4.23/arch/ppc/config.in linux-2.4.23-kgdb/arch/ppc/config.in --- linux-2.4.23/arch/ppc/config.in 2003-12-17 16:15:31.000000000 +0530 +++ linux-2.4.23-kgdb/arch/ppc/config.in 2003-12-17 16:28:16.000000000 +0530 @@ -573,6 +573,8 @@ bool ' Wait queue debugging' CONFIG_DEBUG_WAITQ bool ' Include kgdb kernel debugger' CONFIG_KGDB if [ "$CONFIG_KGDB" = "y" ]; then + bool 'KGDB: Thread analysis' CONFIG_KGDB_THREAD + bool 'KGDB: Console messages through gdb' CONFIG_GDB_CONSOLE choice ' Serial Port' \ "ttyS0 CONFIG_KGDB_TTYS0 \ ttyS1 CONFIG_KGDB_TTYS1 \ diff -Naur linux-2.4.23/arch/ppc/kernel/entry.S linux-2.4.23-kgdb/arch/ppc/kernel/entry.S --- linux-2.4.23/arch/ppc/kernel/entry.S 2003-12-17 16:15:31.000000000 +0530 +++ linux-2.4.23-kgdb/arch/ppc/kernel/entry.S 2003-12-19 18:24:51.000000000 +0530 @@ -291,7 +291,7 @@ lwz r3,NEED_RESCHED(r2) cmpi 0,r3,0 /* check need_resched flag */ beq+ 7f - bl schedule + bl user_schedule 7: lwz r5,SIGPENDING(r2) /* Check for pending unblocked signals */ cmpwi 0,r5,0 beq+ do_signal_ret @@ -446,3 +446,53 @@ mtspr SRR1,r9 RFI /* return to caller */ #endif /* CONFIG_ALL_PPC */ + +#ifdef CONFIG_KGDB +/* + * These are hooks used by KGDB because switch_to does not save registers + * in pt_regs. The registers are saved on the stack on behalf of the caller + * of these funtions. + */ + +_GLOBAL(kern_schedule) + stwu r1,-INT_FRAME_SIZE(r1) /* Allocate exception frame */ + SAVE_8GPRS( 0, r1) + SAVE_8GPRS( 8, r1) + SAVE_8GPRS(16, r1) + SAVE_8GPRS(24, r1) + addi r3,r1,INT_FRAME_SIZE + stw r3,GPR1(r1) + mfcr r3 + stw r3,_CCR(r1) + mflr r3 + stw r3,_NIP(r1) + mfctr r3 + stw r3,_CTR(r1) + mfxer r3 + stw r3,_XER(r1) + mfmsr r3 + stw r3,_MSR(r1) + lwz r3,INT_FRAME_SIZE+4(r1) + stw r3,_LINK(r1) + + bl kern_do_schedule + + lwz r3,_CCR(r1) + mtcr r3 + lwz r3,_XER(r1) + mtxer r3 + lwz r3,_NIP(r1) + mtlr r3 + lwz r3,_CTR(r1) + mtctr r3 + lwz r3,_MSR(r1) + mtmsr r3 + lwz r0,GPR0(r1) + REST_2GPRS ( 2, r1) + REST_4GPRS ( 4, r1) + REST_8GPRS ( 8, r1) + REST_8GPRS (16, r1) + REST_8GPRS (24, r1) + addi r1,r1,INT_FRAME_SIZE + blr +#endif diff -Naur linux-2.4.23/arch/ppc/kernel/ppc-stub.c linux-2.4.23-kgdb/arch/ppc/kernel/ppc-stub.c --- linux-2.4.23/arch/ppc/kernel/ppc-stub.c 2003-12-17 16:15:31.000000000 +0530 +++ linux-2.4.23-kgdb/arch/ppc/kernel/ppc-stub.c 2003-12-17 17:26:08.000000000 +0530 @@ -1,742 +1,261 @@ /* - * ppc-stub.c: KGDB support for the Linux kernel. * - * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC - * some stuff borrowed from Paul Mackerras' xmon - * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) + * 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, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. * - * Modifications to run under Linux - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * - * This file originally came from the gdb sources, and the - * copyright notices have been retained below. */ -/**************************************************************************** - - THIS SOFTWARE IS NOT COPYRIGHTED - - HP offers the following for use in the public domain. HP makes no - warranty with regard to the software or its performance and the - user accepts the software "AS IS" with all faults. - - HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD - TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - -****************************************************************************/ - -/**************************************************************************** - * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ - * - * Module name: remcom.c $ - * Revision: 1.34 $ - * Date: 91/03/09 12:29:49 $ - * Contributor: Lake Stevens Instrument Division$ - * - * Description: low level support for gdb debugger. $ - * - * Considerations: only works on target hardware $ - * - * Written by: Glenn Engel $ - * ModuleState: Experimental $ - * - * NOTES: See Below $ - * - * Modified for SPARC by Stu Grossman, Cygnus Support. - * - * This code has been extensively tested on the Fujitsu SPARClite demo board. - * - * 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 trap #1. - * - ************* - * - * 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 - * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz - * - * 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 - * :: - * :: > - * - * 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 - * - ****************************************************************************/ +/* + * Copyright (C) 2003 Timesys Corporation. + * KGDB for the PowerPC processor + */ -#include -#include #include -#include -#include -#include - -#include -#include -#include -#include +#include +#include +#include +#include +#include #include - -void breakinst(void); +#include +#include /* - * BUFMAX defines the maximum number of characters in inbound/outbound buffers - * at least NUMREGBYTES*2 are needed for register packets + * Forward prototypes */ -#define BUFMAX 2048 -static char remcomInBuffer[BUFMAX]; -static char remcomOutBuffer[BUFMAX]; - -static int initialized; -static int kgdb_active; -static int kgdb_started; -static u_int fault_jmp_buf[100]; -static int kdebug; - -static const char hexchars[]="0123456789abcdef"; - -/* Place where we save old trap entries for restoration - sparc*/ -/* struct tt_entry kgdb_savettable[256]; */ -/* typedef void (*trapfunc_t)(void); */ - -static void kgdb_fault_handler(struct pt_regs *regs); -static int handle_exception (struct pt_regs *regs); - -#if 0 -/* Install an exception handler for kgdb */ -static void exceptionHandler(int tnum, unsigned int *tfunc) -{ - /* We are dorking with a live trap table, all irqs off */ -} -#endif +static void kgdb_debugger (struct pt_regs *regs); +static int kgdb_breakpoint (struct pt_regs *regs); +static int kgdb_singlestep (struct pt_regs *regs); +static int kgdb_iabr_match(struct pt_regs *regs); +static int kgdb_dabr_match(struct pt_regs *regs); +static int ppc_kgdb_init (void); +static void ppc_regs_to_gdb_regs(int *gdb_regs, struct pt_regs *regs); +static void ppc_sleeping_thread_to_gdb_regs(int *gdb_regs, struct task_struct *p); +static void ppc_gdb_regs_to_regs(int *gdb_regs, struct pt_regs *regs); +void ppc_exit_handler (void); +int ppc_handle_exception (int vector, + int signo, + int err_code, + char *remcomInBuffer, + char *remcomOutBuffer, + struct pt_regs *linux_regs); -int -kgdb_setjmp(long *buf) -{ - asm ("mflr 0; stw 0,0(%0);" - "stw 1,4(%0); stw 2,8(%0);" - "mfcr 0; stw 0,12(%0);" - "stmw 13,16(%0)" - : : "r" (buf)); - /* XXX should save fp regs as well */ - return 0; -} -void -kgdb_longjmp(long *buf, int val) -{ - if (val == 0) - val = 1; - asm ("lmw 13,16(%0);" - "lwz 0,12(%0); mtcrf 0x38,0;" - "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" - "mtlr 0; mr 3,%1" - : : "r" (buf), "r" (val)); -} -/* 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; -} - -/* 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. +/* + * Global data */ -static unsigned char * -mem2hex(const char *mem, char *buf, int count) +struct kgdb_arch arch_kgdb_ops = { - unsigned char ch; - - if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { - debugger_fault_handler = kgdb_fault_handler; - while (count-- > 0) { - ch = *mem++; - *buf++ = hexchars[ch >> 4]; - *buf++ = hexchars[ch & 0xf]; - } - } else { - /* error condition */ - } - debugger_fault_handler = 0; - *buf = 0; - return buf; -} - -/* convert the hex array pointed to by buf into binary to be placed in mem - * return a pointer to the character AFTER the last byte written. -*/ -static char * -hex2mem(char *buf, char *mem, int count) -{ - int i; - unsigned char ch; - - if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { - debugger_fault_handler = kgdb_fault_handler; - for (i=0; i# */ -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() & 0x7f; - 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 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); + kgdb_handle_exception (0, 0, 0, regs); + return; } -/* send the packet in buffer. */ -static void putpacket(unsigned char *buffer) +static int kgdb_breakpoint (struct pt_regs *regs) { - unsigned char checksum; - int count; - unsigned char ch, recv; - - /* $#. */ - do { - putDebugChar('$'); - checksum = 0; - count = 0; - - while ((ch = buffer[count])) { - putDebugChar(ch); - checksum += ch; - count += 1; - } - - putDebugChar('#'); - putDebugChar(hexchars[checksum >> 4]); - putDebugChar(hexchars[checksum & 0xf]); - recv = getDebugChar(); - } while ((recv & 0x7f) != '+'); -} + extern atomic_t kgdb_setting_breakpoint; -static void kgdb_flush_cache_all(void) -{ - flush_instruction_cache(); -} + kgdb_handle_exception (0, SIGTRAP, 0, regs); -/* Set up exception handlers for tracing and breakpoints - * [could be called kgdb_init()] - */ -void set_debug_traps(void) -{ -#if 0 - unsigned char c; - - save_and_cli(flags); - - /* In case GDB is started before us, ack any packets (presumably - * "$?#xx") sitting there. - * - * I've found this code causes more problems than it solves, - * so that's why it's commented out. GDB seems to work fine - * now starting either before or after the kernel -bwb - */ - - while((c = getDebugChar()) != '$'); - while((c = getDebugChar()) != '#'); - c = getDebugChar(); /* eat first csum byte */ - c = getDebugChar(); /* eat second csum byte */ - putDebugChar('+'); /* ack it */ -#endif - debugger = kgdb; - debugger_bpt = kgdb_bpt; - debugger_sstep = kgdb_sstep; - debugger_iabr_match = kgdb_iabr_match; - debugger_dabr_match = kgdb_dabr_match; + if (atomic_read (&kgdb_setting_breakpoint)) + regs->nip += 4; - initialized = 1; + return 1; } -static void kgdb_fault_handler(struct pt_regs *regs) +static int kgdb_singlestep (struct pt_regs *regs) { - kgdb_longjmp((long*)fault_jmp_buf, 1); + kgdb_handle_exception (0, SIGTRAP, 0, regs); + return 1; } -int kgdb_bpt(struct pt_regs *regs) +static int kgdb_iabr_match(struct pt_regs *regs) { - return handle_exception(regs); + kgdb_handle_exception (0, 0, 0, regs); + return 1; } -int kgdb_sstep(struct pt_regs *regs) +static int kgdb_dabr_match(struct pt_regs *regs) { - return handle_exception(regs); + kgdb_handle_exception (0, 0, 0, regs); + return 1; } -void kgdb(struct pt_regs *regs) +static int ppc_kgdb_init (void) { - handle_exception(regs); -} + debugger = kgdb_debugger; + debugger_bpt = kgdb_breakpoint; + debugger_sstep = kgdb_singlestep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; -int kgdb_iabr_match(struct pt_regs *regs) -{ - printk(KERN_ERR "kgdb doesn't support iabr, what?!?\n"); - return handle_exception(regs); + return 0; + } -int kgdb_dabr_match(struct pt_regs *regs) +static void ppc_regs_to_gdb_regs(int *gdb_regs, struct pt_regs *regs) { - printk(KERN_ERR "kgdb doesn't support dabr, what?!?\n"); - return handle_exception(regs); -} + int reg; + int *ptr = gdb_regs; -/* Convert the SPARC hardware trap type code to a unix signal number. */ -/* - * This table contains the mapping between PowerPC hardware trap types, and - * signals, which are primarily what GDB understands. - */ -static struct hard_trap_info -{ - unsigned int tt; /* Trap type code for powerpc */ - unsigned char signo; /* Signal that we map this trap into */ -} hard_trap_info[] = { - { 0x200, SIGSEGV }, /* machine check */ - { 0x300, SIGSEGV }, /* address error (store) */ - { 0x400, SIGBUS }, /* instruction bus error */ - { 0x500, SIGINT }, /* interrupt */ - { 0x600, SIGBUS }, /* alingment */ - { 0x700, SIGTRAP }, /* breakpoint trap */ - { 0x800, SIGFPE }, /* fpu unavail */ - { 0x900, SIGALRM }, /* decrementer */ - { 0xa00, SIGILL }, /* reserved */ - { 0xb00, SIGILL }, /* reserved */ - { 0xc00, SIGCHLD }, /* syscall */ - { 0xd00, SIGTRAP }, /* single-step/watch */ - { 0xe00, SIGFPE }, /* fp assist */ - { 0, 0} /* Must be last */ -}; + memset(gdb_regs, 0, MAXREG*4); -static int computeSignal(unsigned int tt) -{ - struct hard_trap_info *ht; + for (reg = 0; reg < 32; reg++) + *(ptr++) = regs->gpr[reg]; - for (ht = hard_trap_info; ht->tt && ht->signo; ht++) - if (ht->tt == tt) - return ht->signo; + for (reg = 0; reg < 64; reg++) + *(ptr++) = 0; - return SIGHUP; /* default for things we don't know about */ -} + *(ptr++) = regs->nip; + *(ptr++) = regs->msr; + *(ptr++) = regs->ccr; + *(ptr++) = regs->link; + *(ptr++) = regs->ctr; + *(ptr++) = regs->xer; -#define PC_REGNUM 64 -#define SP_REGNUM 1 + return; +} /* regs_to_gdb_regs */ -/* - * This function does all command processing for interfacing to gdb. - */ -static int -handle_exception (struct pt_regs *regs) +static void ppc_sleeping_thread_to_gdb_regs(int *gdb_regs, struct task_struct *p) { - int sigval; - int addr; - int length; - char *ptr; - unsigned int msr; - - /* We don't handle user-mode breakpoints. */ - if (user_mode(regs)) - return 0; - - if (debugger_fault_handler) { - debugger_fault_handler(regs); - panic("kgdb longjump failed!\n"); - } - if (kgdb_active) { - printk(KERN_ERR "interrupt while in kgdb, returning\n"); - return 0; - } - - kgdb_active = 1; - kgdb_started = 1; - -#ifdef KGDB_DEBUG - printk("kgdb: entering handle_exception; trap [0x%x]\n", - (unsigned int)regs->trap); -#endif - - kgdb_interruptible(0); - lock_kernel(); - msr = mfmsr(); - mtmsr(msr & ~MSR_EE); /* disable interrupts */ - - if (regs->nip == (unsigned long)breakinst) { - /* Skip over breakpoint trap insn */ - regs->nip += 4; - } - - /* reply to host that an exception has occurred */ - sigval = computeSignal(regs->trap); - ptr = remcomOutBuffer; - -#if 0 - *ptr++ = 'S'; - *ptr++ = hexchars[sigval >> 4]; - *ptr++ = hexchars[sigval & 0xf]; -#else - *ptr++ = 'T'; - *ptr++ = hexchars[sigval >> 4]; - *ptr++ = hexchars[sigval & 0xf]; - *ptr++ = hexchars[PC_REGNUM >> 4]; - *ptr++ = hexchars[PC_REGNUM & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®s->nip, ptr, 4); - *ptr++ = ';'; - *ptr++ = hexchars[SP_REGNUM >> 4]; - *ptr++ = hexchars[SP_REGNUM & 0xf]; - *ptr++ = ':'; - ptr = mem2hex(((char *)regs) + SP_REGNUM*4, ptr, 4); - *ptr++ = ';'; -#endif - - *ptr++ = 0; + struct pt_regs *regs = (struct pt_regs *) (p->thread.ksp + + STACK_FRAME_OVERHEAD); + int reg; + int *ptr = gdb_regs; - putpacket(remcomOutBuffer); + memset(gdb_regs, 0, MAXREG*4); - /* XXX We may want to add some features dealing with poking the - * XXX page tables, ... (look at sparc-stub.c for more info) - * XXX also required hacking to the gdb sources directly... - */ - - while (1) { - remcomOutBuffer[0] = 0; - - getpacket(remcomInBuffer); - switch (remcomInBuffer[0]) { - case '?': /* report most recent signal */ - remcomOutBuffer[0] = 'S'; - remcomOutBuffer[1] = hexchars[sigval >> 4]; - remcomOutBuffer[2] = hexchars[sigval & 0xf]; - remcomOutBuffer[3] = 0; - break; -#if 0 - case 'q': /* this screws up gdb for some reason...*/ - { - extern long _start, sdata, __bss_start; + /* Regs GPR0-2 */ + for (reg = 0; reg < 3; reg++) + *(ptr++) = regs->gpr[reg]; - ptr = &remcomInBuffer[1]; - if (strncmp(ptr, "Offsets", 7) != 0) - break; - - ptr = remcomOutBuffer; - sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", - &_start, &sdata, &__bss_start); - break; - } -#endif - case 'd': - /* toggle debug flag */ - kdebug ^= 1; - break; - - case 'g': /* return the value of the CPU registers. - * some of them are non-PowerPC names :( - * they are stored in gdb like: - * struct { - * u32 gpr[32]; - * f64 fpr[32]; - * u32 pc, ps, cnd, lr; (ps=msr) - * u32 cnt, xer, mq; - * } - */ - { - int i; - ptr = remcomOutBuffer; - /* General Purpose Regs */ - ptr = mem2hex((char *)regs, ptr, 32 * 4); - /* Floating Point Regs - FIXME */ - /*ptr = mem2hex((char *), ptr, 32 * 8);*/ - for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ - ptr[i] = '0'; - } - ptr += 32*8*2; - /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ - ptr = mem2hex((char *)®s->nip, ptr, 4); - ptr = mem2hex((char *)®s->msr, ptr, 4); - ptr = mem2hex((char *)®s->ccr, ptr, 4); - ptr = mem2hex((char *)®s->link, ptr, 4); - ptr = mem2hex((char *)®s->ctr, ptr, 4); - ptr = mem2hex((char *)®s->xer, ptr, 4); - } - break; + /* Regs GPR3-13 are not saved */ + for (reg = 3; reg < 14; reg++) + *(ptr++) = 0; - case 'G': /* set the value of the CPU registers */ - { - ptr = &remcomInBuffer[1]; + /* Regs GPR14-31 */ + for (reg = 14; reg < 32; reg++) + *(ptr++) = regs->gpr[reg]; - /* - * If the stack pointer has moved, you should pray. - * (cause only god can help you). - */ - - /* General Purpose Regs */ - hex2mem(ptr, (char *)regs, 32 * 4); - - /* Floating Point Regs - FIXME?? */ - /*ptr = hex2mem(ptr, ??, 32 * 8);*/ - ptr += 32*8*2; - - /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ - ptr = hex2mem(ptr, (char *)®s->nip, 4); - ptr = hex2mem(ptr, (char *)®s->msr, 4); - ptr = hex2mem(ptr, (char *)®s->ccr, 4); - ptr = hex2mem(ptr, (char *)®s->link, 4); - ptr = hex2mem(ptr, (char *)®s->ctr, 4); - ptr = hex2mem(ptr, (char *)®s->xer, 4); - - strcpy(remcomOutBuffer,"OK"); - } - break; - case 'H': - /* don't do anything, yet, just acknowledge */ - hexToInt(&ptr, &addr); - strcpy(remcomOutBuffer,"OK"); - break; + for (reg = 0; reg < 64; reg++) + *(ptr++) = 0; - case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ - /* Try to read %x,%x. */ + *(ptr++) = regs->nip; + *(ptr++) = regs->msr; + *(ptr++) = regs->ccr; + *(ptr++) = regs->link; + *(ptr++) = regs->ctr; + *(ptr++) = regs->xer; - ptr = &remcomInBuffer[1]; + return; +} - if (hexToInt(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length)) { - if (mem2hex((char *)addr, remcomOutBuffer,length)) - break; - strcpy (remcomOutBuffer, "E03"); - } else { - strcpy(remcomOutBuffer,"E01"); - } - break; +static void ppc_gdb_regs_to_regs(int *gdb_regs, struct pt_regs *regs) +{ + int reg; + int *ptr = gdb_regs; - case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ - /* Try to read '%x,%x:'. */ + for (reg = 0; reg < 32; reg++) + regs->gpr[reg] = *(ptr++); - ptr = &remcomInBuffer[1]; + for (reg = 0; reg < 64; reg++) + ptr++; - if (hexToInt(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length) - && *ptr++ == ':') { - if (hex2mem(ptr, (char *)addr, length)) { - strcpy(remcomOutBuffer, "OK"); - } else { - strcpy(remcomOutBuffer, "E03"); - } - flush_icache_range(addr, addr+length); - } else { - strcpy(remcomOutBuffer, "E02"); - } - break; + regs->nip = *(ptr++); + regs->msr = *(ptr++); + regs->ccr = *(ptr++); + regs->link = *(ptr++); + regs->ctr = *(ptr++); + regs->xer = *(ptr++); + return; +} /* gdb_regs_to_regs */ - case 'k': /* kill the program, actually just continue */ - case 'c': /* cAA..AA Continue; address AA..AA optional */ - /* try to read optional parameter, pc unchanged if no parm */ - ptr = &remcomInBuffer[1]; - if (hexToInt(&ptr, &addr)) { - regs->nip = addr; - } - -/* Need to flush the instruction cache here, as we may have deposited a - * breakpoint, and the icache probably has no way of knowing that a data ref to - * some location may have changed something that is in the instruction cache. +/* exit_handler: + * + * This is called by the generic layer when it is about to return from + * the exception handler */ - kgdb_flush_cache_all(); - mtmsr(msr); - kgdb_interruptible(1); - unlock_kernel(); - kgdb_active = 0; - return 1; - - case 's': - kgdb_flush_cache_all(); - regs->msr |= MSR_SE; - unlock_kernel(); - kgdb_active = 0; - return 1; - - case 'r': /* Reset (if user process..exit ???)*/ - panic("kgdb reset."); - break; - } /* switch */ - if (remcomOutBuffer[0] && kdebug) { - printk("remcomInBuffer: %s\n", remcomInBuffer); - printk("remcomOutBuffer: %s\n", remcomOutBuffer); - } - /* reply to the request */ - putpacket(remcomOutBuffer); - } /* while(1) */ +void ppc_exit_handler (void) +{ +// flush_instruction_cache (); + 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) { - printk("breakpoint() called b4 kgdb init\n"); - return; - } - - asm(" .globl breakinst \n\ - breakinst: .long 0x7d821008"); -} -#ifdef CONFIG_KGDB_CONSOLE /* - * Output string in GDB O-packet format if GDB has connected. If nothing - * output, returns 0 (caller must then handle output) + * This function does PoerPC specific procesing for interfacing to gdb. */ -int -kgdb_output_string (const char* s, unsigned int count) +int ppc_handle_exception (int vector, + int signo, + int err_code, + char *remcomInBuffer, + char *remcomOutBuffer, + struct pt_regs *linux_regs) { - char buffer[512]; + char *ptr; + int addr; + + switch (remcomInBuffer[0]) + { + /* + * sAA..AA Step one instruction from AA..AA + * This will return an error to gdb .. + */ + case 's': + case 'c': + if (kgdb_contthread && kgdb_contthread != current) + { + strcpy(remcomOutBuffer, "E00"); + break; + } - if (!kgdb_started) - return 0; + kgdb_contthread = NULL; - count = (count <= (sizeof(buffer) / 2 - 2)) - ? count : (sizeof(buffer) / 2 - 2); + /* handle the optional parameter */ + ptr = &remcomInBuffer[1]; + if (hexToInt (&ptr, &addr)) + linux_regs->nip = addr; - buffer[0] = 'O'; - mem2hex (s, &buffer[1], count); - putpacket(buffer); + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + { +#if defined (CONFIG_4xx) + linux_regs->msr |= MSR_DE; + current->thread.dbcr0 |= (DBCR_IDM | DBCR_IC); +#else + linux_regs->msr |= MSR_SE; +#endif + } + return 0; + } - return 1; + return -1; } -#endif diff -Naur linux-2.4.23/Documentation/Configure.help linux-2.4.23-kgdb/Documentation/Configure.help --- linux-2.4.23/Documentation/Configure.help 2003-12-17 16:15:29.000000000 +0530 +++ linux-2.4.23-kgdb/Documentation/Configure.help 2003-12-19 18:36:21.000000000 +0530 @@ -21820,6 +21820,31 @@ If you have a Western Digital WD93 SCSI controller on an SGI MIPS system, say Y. Otherwise, say N. +KGDB: Remote (serial) kernel debugging with gdb +CONFIG_KGDB + If you say Y here, it will be possible to remotely debug the + kernel using gdb. This enlarges your kernel image disk size by + several megabytes and requires a machine with more than 128 MB + RAM to avoid excessive linking time. + To use this feature you need to perform some basic setup described + briefly in Documentation/i386/gdb-serial.txt. + More documentation of kernel debugger available at + http://kgdb.sourceforge.net + This is only useful for kernel hackers. If unsure, say N. + +KGDB: Thread analysis +CONFIG_KGDB_THREAD + With thread analysis enabled, gdb can talk to kgdb stub to list + threads and to get stack trace for a thread. This option also enables + some code which helps gdb get exact status of thread. Thread analysis + adds some overhead to schedule and down functions. You can disable this + option if you do not want to compromise on speed. + +KGDB: Console messagegs through gdb +CONFIG_GDB_CONSOLE + If you say Y here, console messages will appear through gdb. + Other consoles such as tty or ttyS will continue to work as usual. + Magic System Request Key support CONFIG_MAGIC_SYSRQ If you say Y here, you will have some control over the system even diff -Naur linux-2.4.23/Documentation/i386/gdb-serial.txt linux-2.4.23-kgdb/Documentation/i386/gdb-serial.txt --- linux-2.4.23/Documentation/i386/gdb-serial.txt 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/Documentation/i386/gdb-serial.txt 2003-12-17 15:28:19.000000000 +0530 @@ -0,0 +1,386 @@ +Version +======= + +This version of the gdbstub package was developed and tested on +kernel version 2.3.48. It will not install on a 2.2 kernel. It may +not work on earlier versions of 2.3 kernels. It is possible that +it will continue to work on later versions of 2.3 and then +versions of 2.4 (I hope). + + +Debugging Setup +=============== + +Designate one machine as the "development" machine. This is the +machine on which you run your compiles and which has your source +code for the kernel. Designate a second machine as the "target" +machine. This is the machine that will run your experimental +kernel. + +The two machines will be connected together via a serial line out +one or the other of the COM ports of the PC. You will need a modem +eliminator and the appropriate cables. + +On the DEVELOPMENT machine you need to apply the patch for the gdb +hooks. You have probably already done that if you are reading this +file. + +On your DEVELOPMENT machine, go to your kernel source directory and +do "make menuconfig". Go down to the kernel hacking menu item and +open it up. Enable the kernel gdb stub code by selecting that item. + +Save and exit the menuconfig program. Then do "make clean" and +"make bzImage" (or whatever target you want to make). This gets +the kernel compiled with the "-g" option set -- necessary for +debugging. + +You have just built the kernel on your DEVELOPMENT machine that you +intend to run on our TARGET machine. + +To install this new kernel, use the following installation procedure. +Remember, you are on the DEVELOPMENT machine patching the kernel source +for the kernel that you intend to run on the TARGET machine. + +Copy this kernel to your target machine using your usual procedures. +I usually arrange to copy development:/usr/src/linux/arch/i386/boot/zImage +to /vmlinuz on the TARGET machine via a LAN based NFS access. That is, +I run the cp command on the target and copy from the development machine +via the LAN. Run Lilo on the new kernel on the target machine so that it +will boot! Then boot the kernel on the target machine. + +There is an utility program named "gdbstart" in the +development:/usr/src/linux/arch/i386/kernel directory. +You should copy this program over to your target machine, probably into +/sbin. This utility program is run on the target machine to +activate the kernel hooks for the debugger. It is invoked as follows: + + gdbstart [-s speed] [-t tty-dev] + defaults: /dev/ttyS0 with speed unmodified by gdbstart + +Don't run the program just yet. We'll get to that in a bit. + +Decide on which tty port you want the machines to communicate, then +cable them up back-to-back using the null modem. COM1 is /dev/ttyS0 +and COM2 is /dev/ttyS1. + +On the DEVELOPMENT machine, create a file called .gdbinit in the +directory /usr/src/linux. An example .gdbinit file looks like this: + +define rmt +set remotebaud 38400 +target remote /dev/ttyS0 +end + +Assuming that you added my gdbinit stuff to your .gdbinit, edit .gdbinit +and find the section that looks like this: + + define rmt + set remotebaud 38400 + target remote /dev/ttyS0 + end + +Change the "target" definition so that it specifies the tty port that +you intend to use. Change the "remotebaud" definition to match the +data rate that you are going to use for the com line. + +On the TARGET machine I find it helpful to create shell script file +named "debug" in the root home directory with the following contents: + + gdbstart -s 38400 -t /dev/ttyS0 < + EOF + +This runs the gdbstart program and gives it the carriage return that +it prompts for. This sets the data rate from the target machine's side. + +You are now ready to try it out. + +On your TARGET machine, freshly rebooted with your gdbstub-equipped +kernel, type "debug" in the root home directory. The system will appear +to hang with some messages on the screen from the debug stub. What +it is doing is waiting for contact from the development machine. + +On your DEVELOPMENT machine, cd /usr/src/linux and enter "gdb vmlinux". +When gdb gets the symbols loaded and prompts you, enter "rmt" (that's +the macro from the .gdbinit file that you just edited). If everything +is working correctly you should see gdb print out a few lines indicating +that a breakpoint has been taken. It will actually show a line of +code in the target kernel inside the gdbstub activation code. + +The gdb interaction should look something like this: + + linux-dev:/usr/src/linux# gdb vmlinux + GDB is free software and you are welcome to distribute copies of it + under certain conditions; type "show copying" to see the conditions. + There is absolutely no warranty for GDB; type "show warranty" for details. + GDB 4.15.1 (i486-slackware-linux), + Copyright 1995 Free Software Foundation, Inc... + (gdb) rmt + breakpoint () at i386-stub.c:750 + 750 } + (gdb) + + +You can now use whatever gdb commands you like to set breakpoints. +Enter "continue" to start your target machine executing again. At this +point the target system will run at full speed until it encounters +your breakpoint or gets a segment violation in the kernel, or whatever. + + +Triggering gdbstub at Kernel Boot Time +====================================== + +The gdbstub patch now has the ability for gdb to connect to the kernel during +bootup (as opposed to waiting for the system to come all the way up and then +running the gdbstart program on the target machine). This new functionality was +added by Scott Foehner at SGI. + +To force a kernel that has been compiled with gdbstub to pause during the boot +process and wait for a connection from gdb, the paramter "gdb" should be passed +to the kernel. This can be done by typing "gdb" after the name of the kernel +on the LILO command line. The patch defaults to use ttyS1 at a baud rate of +38400. These parameters can be changed by using "gdbttyS=" and +"gdbbaud=" on the command line. + +Example: + +LILO boot: linux gdb gdbttyS=1 gdbbaud=38400 + +Note that this command is entered on the TARGET machine as it is booting +the kernel that was compiled on the DEVELOPMENT machine. + +An alternate approach is to place a line in the /etc/lilo.conf file on +your TARGET machine. Under the heading for the kernel that you intend +to boot, place a line that looks like this: + + append = "gdb gdbttyS=1 gdbbaud=38400" + +This will cause the kernel to enter the gdbstub automatically at boot +time. + +BE SURE to run "lilo" after changing the /etc/lilo.conf file. + + +The "gdbstart" Program +===================== + +This utility program is used to set up the com port and data rate +for the connection from the target system to the development system. +Its usage has been described above. + +This version of the patch uses the same tty ioctl for kernel versions +2.0.30 onwards. Thus, the gdbstart utility does not need to be re-compiled +to install the patch in a later version of the kernel. The ioctl added +to the kernel for this purpose is far enough "off the end" of existing +ioctls (as of 2.1.120) that it should not interfere with any new kernel +tty ioctls for quite some time (famous last words). + +The source for the gdbstart program resides in the arch/i386/kernel directory. + + +Debugging hints +=============== + +You can break into the target machine at any time from the development +machine by typing ^C. If the target machine has interrupts enabled +this will stop it in the kernel and enter the debugger. + +There is unfortunately no way of breaking into the kernel if it is +in a loop with interrupts disabled, so if this happens to you then +you need to place exploratory breakpoints or printk's into the kernel +to find out where it is looping. + +There is a copy of an e-mail in the kgdb distribution directory which +describes how to create an NMI on an ISA bus machine using a paper +clip. I have a sophisticated version of this made by wiring a push +button switch into a PC104/ISA bus adapter card. The adapter card +nicely furnishes wire wrap pins for all the ISA bus signals. + +When you are done debugging the kernel on the target machine it is +a good idea to leave it in a running state. This makes reboots +faster, bypassing the fsck. So do a gdb "continue" as the last gdb +command if this is possible. To terminate gdb itself on the development +machine and leave the target machine running, type ^Z to suspend gdb +and then kill it with "kill %1" or something similar. + +If gdbstub Does Not Work +======================== + +If it doesn't work, you will have to troubleshoot it. Do the easy things +first like double checking your cabling and data rates. You might +try some non-kernel based programs to see if the back-to-back connection +works properly. Just something simple like cat /etc/hosts >/dev/ttyS0 +on one machine and cat /dev/ttyS0 on the other will tell you if you +can send data from one machine to the other. There is no point in tearing +out your hair in the kernel if the line doesn't work. + +All of the real action takes place in the file +/usr/src/linux/arch/i386/kernel/gdbstub.c. That is the code on the target +machine that interacts with gdb on the development machine. In gdb you can +turn on a debug switch with the following command: + + set remotedebug + +This will print out the protocol messages that gdb is exchanging with +the target machine. + +Another place to look is /usr/src/linux/drivers/char/gdbserial.c +That is the code that talks to the serial port on the target side. +There might be a problem there. + +If you are really desperate you can use printk debugging in the +gdbstub code in the target kernel until you get it working. In particular, +there is a global variable in /usr/src/linux/arch/i386/kernel/gdbstub.c +named "remote_debug". Compile your kernel with this set to 1, rather +than 0 and the debug stub will print out lots of stuff as it does +what it does. + + +Debugging Loadable Modules +========================== + +This technique comes courtesy of Edouard Parmelan + + +When you run gdb, enter the command + +source gdbinit-modules + +This will read in a file of gdb macros that was installed in your +kernel source directory with kgdb was installed. This file implements +the following commands: + +mod-list + Lists the loaded modules in the form + +mod-print-symbols + Prints all the symbols in the indicated module. + +mod-add-symbols + Loads the symbols from the object file and associates them + with the indicated module. + +After you have loaded the module that you want to debug, use the command +mod-list to find the of your module. Then use that +address in the mod-add-symbols command to load your module's symbols. +From that point onward you can debug your module as if it were a part +of the kernel. + +The file gdbinit-modules also contains a command named mod-add-lis as +an example of how to construct a command of your own to load your +favorite module. The idea is to "can" the pathname of the module +in the command so you don't have to type so much. + +Threads +======= + +Each process in a target machine is seen as a gdb thread. gdb thread related +commands (info threads, thread n) can be used. + +ia-32 hardware breakpoints +========================== + +gdb stub contains support for hardware breakpoints using debugging features +of ia-32(x86) processors. These breakpoints do not need code modification. +They use debugging registers. 4 hardware breakpoints are available in ia-32 +processors. + +Each hardware breakpoint can be of one of the following three types. +1. Execution breakpoint - An Execution breakpoint is triggered when code at the + breakpoint address is executed. + + As limited number of hardware breakpoints are available, it is advisable + to use software breakpoints ( break command ) instead of execution + hardware breakpoints, unless modification of code is to be avoided. + +2. Write breakpoint - A write breakpoint is triggered when memory location at the + breakpoint address is written. + + A write or can be placed for data of variable length. Length of a write + breakpoint indicates length of the datatype to be watched. Length is 1 + for 1 byte data , 2 for 2 byte data, 3 for 4 byte data. + +3. Access breakpoint - An access breakpoint is triggered when memory location at + the breakpoint address is either read or written. + + Access breakpoints also have lengths similar to write breakpoints. + +IO breakpoints in ia-32 are not supported. + +Since gdb stub at present does not use the protocol used by gdb for hardware +breakpoints, hardware breakpoints are accessed through gdb macros. gdb macros +for hardware breakpoints are described below. + +hwebrk - Places an execution breakpoint + hwebrk breakpointno address +hwwbrk - Places a write breakpoint + hwwbrk breakpointno length address +hwabrk - Places an access breakpoint + hwabrk breakpointno length address +hwrmbrk - Removes a breakpoint + hwrmbrk breakpointno +exinfo - Tells whether a software or hardware breakpoint has occured. + Prints number of the hardware breakpoint if a hardware breakpoint has + occured. + +Arguments required by these commands are as follows +breakpointno - 0 to 3 +length - 1 to 3 +address - Memory location in hex digits ( without 0x ) e.g c015e9bc + +MP support +========== + +When a breakpoint occurs or user issues a break ( Ctrl + C ) to gdb client, +all the processors are forced to enter the debugger. Current thread +corresponds to the thread running on the processor where breakpoint occured. +Threads running on other processor(s) appear similar to other non running +threads in the 'info threads' output. + +ia-32 hardware debugging registers on all processors are set to same values. +Hence any hardware breakpoints may occur on any processor. + +gdb troubleshooting +=================== + +1. gdb hangs +Kill it. restart gdb. Connect to target machine. + +2. gdb cannot connect to target machine (after killing a gdb and restarting +another) +If the target machine was not inside debugger when you killed gdb, gdb cannot +connect because the target machine won't respond. +In this case echo "Ctrl+C"(ascii 3) in the serial line. +e.g. echo -e "\003" > /dev/ttyS1 +This forces that target machine into debugger after which you can connect. + +3. gdb cannot connect even after echoing Ctrl+C into serial line +Try changing serial line settings min to 1 and time to 0 +e.g. stty min 1 time 0 < /dev/ttyS1 +Try echoing again + +check serial line speed and set it to correct value if required +e.g. stty ispeed 115200 ospeed 115200 < /dev/ttyS1 + +Final Items +=========== + +I picked up this code from Dave Grothe and enhanced it. + +If you make some really cool modification to this stuff, or if you +fix a bug, please let me know. + +Amit S. Kale + + +(First kgdb by David Grothe ) + +(modified by Tigran Aivazian ) + Putting gdbstub into the kernel config menu. + +(modified by Scott Foehner ) + Hooks for entering gdbstub at boot time. + +(modified by Amit S. Kale ) + Threads, ia-32 hw debugging, mp support, console support, + nmi watchdog handling. diff -Naur linux-2.4.23/drivers/char/gdbserial.c linux-2.4.23-kgdb/drivers/char/gdbserial.c --- linux-2.4.23/drivers/char/gdbserial.c 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/drivers/char/gdbserial.c 2003-12-17 15:28:19.000000000 +0530 @@ -0,0 +1,304 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * + * Modified by Scott Foehner (sfoehner@engr.sgi.com) to allow connect + * on boot-up + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#undef PRNT /* define for debug printing */ + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE] ; +static int gdb_buf_in_inx ; +static atomic_t gdb_buf_in_cnt ; +static int gdb_buf_out_inx ; + + +static int gdb_got_dollar = -3, gdb_got_H = -3, gdb_interrupt_iteration = 0; + +extern void set_debug_traps(void) ; /* GDB routine */ +extern struct serial_state * gdb_serial_setup(int ttyS, int baud); +extern void shutdown_for_gdb(struct async_struct * info) ; + /* in serial.c */ + +int gdb_irq; +int gdb_port; + +static int initialized = -1; + +/* + * Get a byte from the hardware data buffer and return it + */ +static int read_data_bfr(void) +{ + if (inb(gdb_port + UART_LSR) & UART_LSR_DR) + return(inb(gdb_port + UART_RX)); + + return( -1 ) ; + +} /* read_data_bfr */ + + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + */ +static int read_char(void) +{ + if (atomic_read(&gdb_buf_in_cnt) != 0) /* intr routine has q'd chars */ + { + int chr ; + + chr = gdb_buf[gdb_buf_out_inx++] ; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1) ; + atomic_dec(&gdb_buf_in_cnt) ; + return(chr) ; + } + + return(read_data_bfr()) ; /* read from hardware */ + +} /* read_char */ + +/* + * Wait until the interface can accept a char, then write it. + */ +static void write_char(int chr) +{ + while ( !(inb(gdb_port + UART_LSR) & UART_LSR_THRE) ) ; + + outb(chr, gdb_port+UART_TX); + +} /* write_char */ + +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static void gdb_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + int chr ; + int iir ; + + do + { + chr = read_data_bfr() ; + iir = inb(gdb_port + UART_IIR) ; +#ifdef PRNT + printk("gdb_interrupt: chr=%02x '%c' after read iir=%02x\n", chr, + chr > ' ' && chr < 0x7F ? chr : ' ', iir) ; +#endif + if (chr < 0) continue ; + + if (chr == 3) /* Ctrl-C means remote interrupt */ + { + breakpoint(); + continue ; + } + + if(atomic_read(&kgdb_killed_or_detached)) { + if (chr == '$') + gdb_got_dollar = gdb_interrupt_iteration; + else if (gdb_interrupt_iteration == gdb_got_dollar + 1 && + chr == 'H') + gdb_got_H = gdb_interrupt_iteration; + else if (gdb_interrupt_iteration == gdb_got_H + 1 && + chr == 'c') { + gdb_buf[gdb_buf_in_inx++] = chr; + atomic_inc(&gdb_buf_in_cnt); + atomic_set(&kgdb_might_be_resumed, 1); + wmb(); + breakpoint(); + atomic_set(&kgdb_might_be_resumed, 0); + gdb_interrupt_iteration = 0; + gdb_got_dollar = -3; + gdb_got_H = -3; + continue; + } + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) + { /* buffer overflow, clear it */ + gdb_buf_in_inx = 0 ; + atomic_set(&gdb_buf_in_cnt, 0) ; + gdb_buf_out_inx = 0 ; + break ; + } + + gdb_buf[gdb_buf_in_inx++] = chr ; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1) ; + atomic_inc(&gdb_buf_in_cnt) ; + } + while (iir & UART_IIR_RDI); + + if (atomic_read(&kgdb_killed_or_detached)) + gdb_interrupt_iteration ++; + + +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void gdb_null(void) +{ +} /* gdb_null */ + + +int gdb_hook(void) +{ + int retval ; + struct serial_state *ser; + +#ifdef CONFIG_SMP + if (smp_num_cpus > KGDB_MAX_NO_CPUS) { + printk("kgdb: too manu cpus. Cannot enable debugger with more than 8 cpus\n"); + return (-1); + } +#endif + + /* + * Call first time just to get the ser ptr + */ + if((ser = gdb_serial_setup(gdb_ttyS, gdb_baud)) == 0) { + printk ("gdb_serial_setup() error"); + return(-1); + } + + gdb_port = ser->port; + gdb_irq = ser->irq; + + if (ser->info != NULL) + { + shutdown_for_gdb(ser->info) ; + /* + * Call second time to do the setup now that we have + * shut down the previous user of the interface. + */ + gdb_serial_setup(gdb_ttyS, gdb_baud) ; + } + + retval = request_irq(gdb_irq, + gdb_interrupt, + SA_INTERRUPT, + "GDB-stub", NULL); + if (retval == 0) + initialized = 1; + else + { + initialized = 0; + printk("gdb_hook: request_irq(irq=%d) failed: %d\n", gdb_irq, retval); + } + + /* + * Call GDB routine to setup the exception vectors for the debugger + */ + set_debug_traps() ; + + /* + * Call the breakpoint() routine in GDB to start the debugging + * session. + */ + printk("Waiting for connection from remote gdb... ") ; + breakpoint() ; + gdb_null() ; + + printk("Connected.\n"); + + gdb_initialized = 1; + return(0) ; + +} /* gdb_hook_interrupt2 */ + +/* + * getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. + */ +char getDebugChar(void) +{ + volatile int chr ; + +#ifdef PRNT + printk("getDebugChar: ") ; +#endif + + while ( (chr = read_char()) < 0 ) ; + + +#ifdef PRNT + printk("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ') ; +#endif + return(chr) ; + +} /* getDebugChar */ + +/* + * putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. + */ +void putDebugChar(char chr) +{ +#ifdef PRNT + printk("putDebugChar: chr=%02x '%c'\n", chr, + chr > ' ' && chr < 0x7F ? chr : ' ') ; +#endif + + write_char(chr) ; /* this routine will wait */ + +} /* putDebugChar */ + diff -Naur linux-2.4.23/drivers/char/Makefile linux-2.4.23-kgdb/drivers/char/Makefile --- linux-2.4.23/drivers/char/Makefile 2003-12-17 16:15:36.000000000 +0530 +++ linux-2.4.23-kgdb/drivers/char/Makefile 2003-12-17 15:28:19.000000000 +0530 @@ -165,6 +165,7 @@ KEYBD = dummy_keyb.o endif +obj-$(CONFIG_KGDB) += gdbserial.o obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o obj-$(CONFIG_SERIAL) += $(SERIAL) obj-$(CONFIG_SERIAL_HCDP) += hcdp_serial.o diff -Naur linux-2.4.23/drivers/char/serial.c linux-2.4.23-kgdb/drivers/char/serial.c --- linux-2.4.23/drivers/char/serial.c 2003-12-17 16:15:37.000000000 +0530 +++ linux-2.4.23-kgdb/drivers/char/serial.c 2003-12-17 15:28:19.000000000 +0530 @@ -4,6 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, * 1998, 1999 Theodore Ts'o + * Copyright (C) 2000 VERITAS Software Corporation. * * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now * much more extensible to support other serial cards based on the @@ -34,6 +35,10 @@ * 4/98: Added changes to support the ARM architecture proposed by * Russell King * + * 3/99: Added TIOCGDB for remote debugging with gdb if compiled with + * CONFIG_KGDB + * Tigran Aivazian + * * 5/99: Updated to include support for the XR16C850 and ST16C654 * uarts. Stuart MacDonald * @@ -210,7 +215,7 @@ #include #endif #include -#ifdef CONFIG_SERIAL_CONSOLE +#if defined(CONFIG_SERIAL_CONSOLE) || defined (CONFIG_GDB_CONSOLE) #include #endif #ifdef ENABLE_SERIAL_PCI @@ -1591,6 +1596,13 @@ restore_flags(flags); } +#ifdef CONFIG_KGDB +void shutdown_for_gdb(struct async_struct * info) +{ + shutdown(info) ; +} +#endif + #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, @@ -2701,7 +2713,12 @@ /* "setserial -W" is called in Debian boot */ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); return 0; - +#ifdef CONFIG_KGDB + case TIOCGDB: + gdb_ttyS = MINOR(tty->device) & 0x03F ; + gdb_baud = tty_get_baud_rate(tty) ; + return gdb_hook(); +#endif default: return -ENOIOCTLCMD; } @@ -4949,12 +4966,17 @@ * * Accept a maximum of eight boards * + * 10/00: added console support for kgdb. Amit Kale + * */ static void __devinit probe_serial_pci(void) { #ifdef SERIAL_DEBUG_PCI printk(KERN_DEBUG "Entered probe_serial_pci()\n"); #endif +#ifdef CONFIG_KGDB +#include +#endif /* Register call PCI serial devices. Null out * the driver name upon failure, as a signal @@ -6023,6 +6045,129 @@ #endif /* + * ------------------------------------------------------------ + * Serial GDB driver (most in gdbserial.c) + * ------------------------------------------------------------ + */ + +#ifdef CONFIG_KGDB +#ifdef CONFIG_GDB_CONSOLE +static struct console gdbcons = { + name: "gdb", + write: gdb_console_write, + flags: CON_PRINTBUFFER | CON_ENABLED, + index: -1, +}; +#endif + + +/* + * Takes: + * ttyS - integer specifying which serial port to use for debugging + * baud - baud rate of specified serial port + * Returns: + * port for use by the gdb serial driver + */ +struct serial_state * +gdb_serial_setup(int ttyS, int baud) +{ + struct serial_state *ser; + unsigned cval; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + + /* + * Divisor, bytesize and parity + */ + + ser = rs_table + ttyS; + ser->flags &= ~ASYNC_BOOT_AUTOCONF; + quot = ser->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); + cval >>= 4; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + cval = 0x3; + outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ + outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ + outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ + outb(cval, ser->port + UART_LCR); /* reset DLAB */ + outb(UART_IER_RDI, ser->port + UART_IER); /* turn on interrupts*/ + outb(UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); + + /* + * If we read 0xff from the LSR, there is no UART here. + */ + if (inb(ser->port + UART_LSR) == 0xff) + return 0; + return ser; +} +#ifdef CONFIG_GDB_CONSOLE +void __init gdb_console_init(void) +{ + register_console(&gdbcons); +} +#endif +#endif /* CONFIG_KGDB */ + +/* Local variables: compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" End: diff -Naur linux-2.4.23/drivers/char/tty_io.c linux-2.4.23-kgdb/drivers/char/tty_io.c --- linux-2.4.23/drivers/char/tty_io.c 2003-12-17 16:15:37.000000000 +0530 +++ linux-2.4.23-kgdb/drivers/char/tty_io.c 2003-12-17 15:28:19.000000000 +0530 @@ -90,6 +90,9 @@ #include #include #include +#ifdef CONFIG_GDB_CONSOLE +#include +#endif #include #include @@ -2244,6 +2247,9 @@ #ifdef CONFIG_AU1X00_SERIAL_CONSOLE au1x00_serial_console_init(); #endif +#ifdef CONFIG_GDB_CONSOLE + gdb_console_init(); +#endif #ifdef CONFIG_SERIAL_CONSOLE #if (defined(CONFIG_8xx) || defined(CONFIG_8260)) console_8xx_init(); diff -Naur linux-2.4.23/include/asm-i386/ioctls.h linux-2.4.23-kgdb/include/asm-i386/ioctls.h --- linux-2.4.23/include/asm-i386/ioctls.h 2003-12-17 16:14:56.000000000 +0530 +++ linux-2.4.23-kgdb/include/asm-i386/ioctls.h 2003-12-17 15:28:19.000000000 +0530 @@ -68,6 +68,7 @@ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ #define FIOQSIZE 0x5460 +#define TIOCGDB 0x547F /* enable GDB stub mode on this tty */ /* Used for packet mode */ #define TIOCPKT_DATA 0 diff -Naur linux-2.4.23/include/asm-i386/kgdb.h linux-2.4.23-kgdb/include/asm-i386/kgdb.h --- linux-2.4.23/include/asm-i386/kgdb.h 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/include/asm-i386/kgdb.h 2003-12-19 17:49:40.000000000 +0530 @@ -0,0 +1,62 @@ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +/* + * Copyright (C) 2001 Amit S. Kale + */ + +#include + +/* gdb locks */ +#define KGDB_MAX_NO_CPUS 8 + +extern int gdb_enter; /* 1 = enter debugger on boot */ +extern int gdb_ttyS; +extern int gdb_baud; +extern int gdb_initialized; +extern int gdb_irq; + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 1024 + +/* Number of bytes of registers. */ +#define NUMREGBYTES 64 +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + */ +enum regnames { _EAX, /* 0 */ + _ECX, /* 1 */ + _EDX, /* 2 */ + _EBX, /* 3 */ + _ESP, /* 4 */ + _EBP, /* 5 */ + _ESI, /* 6 */ + _EDI, /* 7 */ + _PC, /* 8 also known as eip */ + _PS, /* 9 also known as eflags */ + _CS, /* 10 */ + _SS, /* 11 */ + _DS, /* 12 */ + _ES, /* 13 */ + _FS, /* 14 */ + _GS /* 15 */ +}; + +#define BREAKPOINT() asm(" int $3"); +#define BREAK_INSTR_SIZE 1 +struct console; +void gdb_console_write(struct console *co, const char *s, + unsigned count); +void gdb_console_init(void); + +void gdb_wait(struct pt_regs *regs); + + +#endif /* _ASM_KGDB_H_ */ diff -Naur linux-2.4.23/include/asm-i386/page.h linux-2.4.23-kgdb/include/asm-i386/page.h --- linux-2.4.23/include/asm-i386/page.h 2003-10-22 02:57:29.000000000 +0530 +++ linux-2.4.23-kgdb/include/asm-i386/page.h 2003-12-19 17:49:44.000000000 +0530 @@ -95,6 +95,10 @@ * undefined" opcode for parsing in the trap handler. */ +#ifdef CONFIG_KGDB +#include +#define BUG() KGDB_ASSERT("BUG", 0) +#else #if 1 /* Set to zero for a slightly smaller kernel */ #define BUG() \ __asm__ __volatile__( "ud2\n" \ @@ -104,6 +108,7 @@ #else #define BUG() __asm__ __volatile__("ud2\n") #endif +#endif #define PAGE_BUG(page) do { \ BUG(); \ diff -Naur linux-2.4.23/include/asm-i386/processor.h linux-2.4.23-kgdb/include/asm-i386/processor.h --- linux-2.4.23/include/asm-i386/processor.h 2003-12-17 16:14:56.000000000 +0530 +++ linux-2.4.23-kgdb/include/asm-i386/processor.h 2003-12-19 17:49:44.000000000 +0530 @@ -376,6 +376,9 @@ /* IO permissions */ int ioperm; unsigned long io_bitmap[IO_BITMAP_SIZE+1]; +#ifdef CONFIG_KGDB + struct pt_regs *kgdbregs; +#endif }; #define INIT_THREAD { \ diff -Naur linux-2.4.23/include/asm-ppc/ioctls.h linux-2.4.23-kgdb/include/asm-ppc/ioctls.h --- linux-2.4.23/include/asm-ppc/ioctls.h 2003-12-17 16:13:34.000000000 +0530 +++ linux-2.4.23-kgdb/include/asm-ppc/ioctls.h 2003-12-19 16:35:10.000000000 +0530 @@ -105,4 +105,6 @@ #define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define TIOCGDB 0x547F /* enable GDB stub mode on this tty */ + #endif /* _ASM_PPC_IOCTLS_H */ diff -Naur linux-2.4.23/include/asm-ppc/kgdb.h linux-2.4.23-kgdb/include/asm-ppc/kgdb.h --- linux-2.4.23/include/asm-ppc/kgdb.h 2003-12-17 16:13:34.000000000 +0530 +++ linux-2.4.23-kgdb/include/asm-ppc/kgdb.h 2003-12-17 16:22:57.000000000 +0530 @@ -2,47 +2,40 @@ * kgdb.h: Defines and declarations for serial line source level * remote debugging of the Linux kernel using gdb. * + * PPC Mods (C) 2003 John Whitney (john.whitney@timesys.com) + * * PPC Mods (C) 1998 Michael Tesch (tesch@cs.wisc.edu) * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) */ #ifdef __KERNEL__ -#ifndef _PPC_KGDB_H -#define _PPC_KGDB_H - -#ifndef __ASSEMBLY__ -/* To initialize the serial, first thing called */ -extern void kgdb_map_scc(void); -/* To init the kgdb engine. (called by serial hook)*/ -extern void set_debug_traps(void); - -/* To enter the debugger explicitly. */ -extern void breakpoint(void); +#ifndef _ASMPPC_KGDB_H +#define _ASMPPC_KGDB_H -/* For taking exceptions - * these are defined in traps.c +/* + * For taking exceptions these are defined in traps.c */ -extern void (*debugger)(struct pt_regs *regs); -extern int (*debugger_bpt)(struct pt_regs *regs); -extern int (*debugger_sstep)(struct pt_regs *regs); -extern int (*debugger_iabr_match)(struct pt_regs *regs); -extern int (*debugger_dabr_match)(struct pt_regs *regs); -extern void (*debugger_fault_handler)(struct pt_regs *regs); - -/* What we bring to the party */ -int kgdb_bpt(struct pt_regs *regs); -int kgdb_sstep(struct pt_regs *regs); -void kgdb(struct pt_regs *regs); -int kgdb_iabr_match(struct pt_regs *regs); -int kgdb_dabr_match(struct pt_regs *regs); +extern void (*debugger) (struct pt_regs *regs); +extern int (*debugger_bpt) (struct pt_regs *regs); +extern int (*debugger_sstep) (struct pt_regs *regs); +extern int (*debugger_iabr_match) (struct pt_regs *regs); +extern int (*debugger_dabr_match) (struct pt_regs *regs); +extern void (*debugger_fault_handler) (struct pt_regs *regs); /* * external low-level support routines (ie macserial.c) */ -extern void kgdb_interruptible(int); /* control interrupts from serial */ -extern void putDebugChar(char); /* write a single character */ -extern char getDebugChar(void); /* read and return a single char */ +extern void kgdb_interruptible (int); /* control interrupts from serial */ +extern void putDebugChar (char); /* write a single character */ +extern char getDebugChar (void); /* read and return a single char */ + +#define BREAK_INSTR_SIZE 4 +#define MAXREG (PT_FPSCR+1) +#define NUMREGBYTES (MAXREG * sizeof (int)) +#define BUFMAX ((NUMREGBYTES * 2) + 512) +#define OUTBUFMAX ((NUMREGBYTES * 2) + 512) + +#define BREAKPOINT() asm (".long 0x7d821008"); -#endif /* !(__ASSEMBLY__) */ -#endif /* !(_PPC_KGDB_H) */ +#endif /* !(_ASMPPC_KGDB_H) */ #endif /* __KERNEL__ */ diff -Naur linux-2.4.23/include/asm-ppc/processor.h linux-2.4.23-kgdb/include/asm-ppc/processor.h --- linux-2.4.23/include/asm-ppc/processor.h 2003-12-17 16:44:40.000000000 +0530 +++ linux-2.4.23-kgdb/include/asm-ppc/processor.h 2003-12-19 18:29:17.000000000 +0530 @@ -828,6 +828,9 @@ /* Saved 4xx debug registers */ unsigned long dbcr0; #endif +#ifdef CONFIG_KGDB + struct pt_regs *kgdbregs; +#endif }; #define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack) diff -Naur linux-2.4.23/include/linux/dcache.h linux-2.4.23-kgdb/include/linux/dcache.h --- linux-2.4.23/include/linux/dcache.h 2003-10-22 02:57:24.000000000 +0530 +++ linux-2.4.23-kgdb/include/linux/dcache.h 2003-12-19 18:29:17.000000000 +0530 @@ -4,6 +4,7 @@ #ifdef __KERNEL__ #include +#include #include #include diff -Naur linux-2.4.23/include/linux/kgdb-asserts.h linux-2.4.23-kgdb/include/linux/kgdb-asserts.h --- linux-2.4.23/include/linux/kgdb-asserts.h 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/include/linux/kgdb-asserts.h 2003-12-19 17:49:44.000000000 +0530 @@ -0,0 +1,31 @@ +#ifndef _GDB_ASSERTS_H_ +#define _GDB_ASSERTS_H_ + +/* + * Copyright (C) 2001 Amit S. Kale + */ + +#define KGDB_ASSERT(message, condition) do { \ + if (!(condition)) { \ + printk("kgdb assertion failed: %s\n", message); \ + breakpoint(); \ + } \ +} while (0) + +#define KA_VALID_ERRNO(errno) ((errno) > 0 && (errno) <= EMEDIUMTYPE) + +#define KA_VALID_PTR_ERR(ptr) KA_VALID_ERRNO(-PTR_ERR(ptr)) + +#define KA_VALID_KPTR(ptr) (!(ptr) || \ + ((void *)(ptr) >= (void *)PAGE_OFFSET && \ + (void *)(ptr) < ERR_PTR(-EMEDIUMTYPE))) + +#define KA_VALID_PTRORERR(errptr) (KA_VALID_KPTR(errptr) || KA_VALID_PTR_ERR(errptr)) + +#ifndef CONFIG_SMP +#define KA_HELD_GKL() 1 +#else +#define KA_HELD_GKL() (current->lock_depth >= 0) +#endif + +#endif /* _GDB_ASSERTS_H_ */ diff -Naur linux-2.4.23/include/linux/kgdb-defs.h linux-2.4.23-kgdb/include/linux/kgdb-defs.h --- linux-2.4.23/include/linux/kgdb-defs.h 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/include/linux/kgdb-defs.h 2003-12-19 18:29:17.000000000 +0530 @@ -0,0 +1,58 @@ +#ifndef _GDB_DEFS_H_ +#define _GDB_DEFS_H_ + +#include + +/* Wait for GDB to connect. */ +int gdb_hook(void); + +/* To init the kgdb engine. (called by serial hook) */ +void set_debug_traps(void); + +/* To enter the debugger explicitly. */ +void breakpoint(void); + +/* Output a string via the GDB console. Returns non-zero on success. */ +int kgdb_output_string(const char *s, unsigned int count); + +extern int gdb_enter; /* 1 = enter debugger on boot */ +extern int gdb_ttyS; +extern int gdb_baud; +extern int gdb_initialized; + +void putDebugChar(char); /* write a single character */ +char getDebugChar(void); /* read and return a single char */ +int hexToInt(char **ptr, int *intValue); +int kgdb_handle_exception(int exVector, int signo, int err_code, + struct pt_regs *linux_regs); +char *hex2mem(char *buf, char *mem, int count, int can_fault); +char *mem2hex(char *mem, char *buf, int count, int can_fault); +void putpacket(char *buffer); + +#ifdef CONFIG_KGDB +extern volatile int kgdb_memerr_expected; +#else +static const int kgdb_memerr_expected = 0; +#endif + +typedef int gdb_debug_hook(int exVector, int signo, int err_code, + struct pt_regs *regs); + +#ifndef KGDB_MAX_NO_CPUS +#define KGDB_MAX_NO_CPUS 8 +#endif + +extern gdb_debug_hook *linux_debug_hook; +extern atomic_t kgdb_lock; +extern spinlock_t slavecpulocks[KGDB_MAX_NO_CPUS]; +extern volatile int procindebug[KGDB_MAX_NO_CPUS]; +extern int kgdb_initialized; +extern struct kgdb_arch arch_kgdb_ops; +extern struct task_struct *kgdb_usethread, *kgdb_contthread; +extern volatile int kgdb_memerr; +extern atomic_t kgdb_setting_breakpoint; +extern atomic_t kgdb_killed_or_detached; +extern atomic_t kgdb_might_be_resumed; +extern volatile unsigned kgdb_step; + +#endif /* _GDB_DEFS_H_ */ diff -Naur linux-2.4.23/include/linux/kgdb.h linux-2.4.23-kgdb/include/linux/kgdb.h --- linux-2.4.23/include/linux/kgdb.h 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/include/linux/kgdb.h 2003-12-19 18:29:17.000000000 +0530 @@ -0,0 +1,80 @@ +#ifndef _GDB_H_ +#define _GDB_H_ + +/* + * Copyright (C) 2001 Amit S. Kale + */ + +#include +#include +#include +#include + +enum gdb_bptype +{ + bp_breakpoint = '0', + bp_hardware_breakpoint, + bp_write_watchpoint, + bp_read_watchpoint, + bp_access_watchpoint +}; + +enum gdb_bpstate +{ + bp_disabled, + bp_enabled +}; + +#ifndef BREAK_INSTR_SIZE +#error BREAK_INSTR_SIZE needed by kgdb +#endif + +struct gdb_breakpoint +{ + unsigned int bpt_addr; + unsigned char saved_instr[BREAK_INSTR_SIZE]; + enum gdb_bptype type; + enum gdb_bpstate state; +}; + +typedef struct gdb_breakpoint gdb_breakpoint_t; + +#ifndef MAX_BREAKPOINTS +#define MAX_BREAKPOINTS 16 +#endif + +#define KGDB_HW_BREAKPOINT 1 + +struct kgdb_arch { + unsigned char gdb_bpt_instr[BREAK_INSTR_SIZE]; + unsigned long flags; + + int (*kgdb_init) (void); + void (*regs_to_gdb_regs)(int *gdb_regs, struct pt_regs *regs); + void (*sleeping_thread_to_gdb_regs)(int *gdb_regs,struct task_struct *p); + void (*gdb_regs_to_regs)(int *gdb_regs, struct pt_regs *regs); + void (*printexpinfo)(int exceptionNo, int errorcode, char *buffer); + void (*disable_hw_debug) (struct pt_regs *regs); + void (*post_master_code) (struct pt_regs *regs, int eVector, int err_code); + int (*handle_buffer) (int vector, int signo, int err_code, + char *InBuffer, char *outBuffer, + struct pt_regs *regs); + int (*set_break) (unsigned long addr, int type); + int (*remove_break) (unsigned long addr, int type); + void (*correct_hw_break) (void); + void (*handler_exit) (void); +}; + + +/* Thread reference */ +typedef unsigned char threadref[8]; + +/* Routine prototypes */ +struct console; +extern void gdb_console_write(struct console *co, const char *s, unsigned count); + +#ifdef CONFIG_GDB_CONSOLE +extern void gdb_console_init(void); +#endif /* CONFIG_GDB_CONSOLE */ + +#endif /* _GDB_H_ */ diff -Naur linux-2.4.23/include/linux/sched.h linux-2.4.23-kgdb/include/linux/sched.h --- linux-2.4.23/include/linux/sched.h 2003-12-17 16:15:52.000000000 +0530 +++ linux-2.4.23-kgdb/include/linux/sched.h 2003-12-19 18:29:17.000000000 +0530 @@ -146,7 +146,9 @@ #define MAX_SCHEDULE_TIMEOUT LONG_MAX extern signed long FASTCALL(schedule_timeout(signed long timeout)); -asmlinkage void schedule(void); +asmlinkage void do_schedule(void); +asmlinkage void kern_schedule(void); +asmlinkage void kern_do_schedule(struct pt_regs); extern int schedule_task(struct tq_struct *task); extern void flush_scheduled_tasks(void); @@ -956,5 +958,14 @@ __cond_resched(); } +static inline void schedule(void) +{ +#ifdef CONFIG_KGDB_THREAD + kern_schedule(); +#else + do_schedule(); +#endif +} + #endif /* __KERNEL__ */ #endif diff -Naur linux-2.4.23/init/main.c linux-2.4.23-kgdb/init/main.c --- linux-2.4.23/init/main.c 2003-12-17 16:15:52.000000000 +0530 +++ linux-2.4.23-kgdb/init/main.c 2003-12-17 15:28:19.000000000 +0530 @@ -61,6 +61,10 @@ #include #endif +#ifdef CONFIG_KGDB +#include +#endif + #ifdef CONFIG_ISAPNP #include #endif @@ -434,6 +438,11 @@ * make syscalls (and thus be locked). */ smp_init(); +#ifdef CONFIG_KGDB + if (gdb_enter) { + gdb_hook(); /* right at boot time */ + } +#endif #if defined(CONFIG_SYSVIPC) ipc_init(); #endif diff -Naur linux-2.4.23/kernel/kgdbstub.c linux-2.4.23-kgdb/kernel/kgdbstub.c --- linux-2.4.23/kernel/kgdbstub.c 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.4.23-kgdb/kernel/kgdbstub.c 2003-12-17 15:28:19.000000000 +0530 @@ -0,0 +1,1074 @@ +/* + * 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, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +/* + * Generic KGDB Support + * Copyright (C) 2002-2003 Timesys Corporation + * Implemented by Anurekh Saxena (anurekh.saxena@timesys.com) + * + * Copyright (C) 2000-2001 VERITAS Software Corporation. + * Contributor: Lake Stevens Instrument Division + * Written by: Glenn Engel + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe + * Integrated into 2.2.5 kernel by Tigran Aivazian + * + * thread support, support for multiple processors,support for ia-32(x86) + * hardware debugging, Console support, handling nmi watchdog + * - Amit S. Kale ( amitkale@emsyssoft.com ) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include + +#ifdef CONFIG_GDB_CONSOLE +#include +#endif + +#include + + + +/* DEBUGGING THE DEBUGGER */ +#undef KGDB_DEG +/**************************/ + +struct kgdb_arch *kgdb_ops = &arch_kgdb_ops; +gdb_breakpoint_t kgdb_break[MAX_BREAKPOINTS]; + +int kgdb_initialized = 0; +static const char hexchars[] = "0123456789abcdef"; + +int get_char(char *addr, unsigned char *data, int can_fault); +int set_char(char *addr, int data, int can_fault); + +spinlock_t slavecpulocks[KGDB_MAX_NO_CPUS]; +volatile int procindebug[KGDB_MAX_NO_CPUS]; +volatile unsigned kgdb_step; +atomic_t kgdb_lock; +atomic_t kgdb_setting_breakpoint; +atomic_t kgdb_killed_or_detached; +atomic_t kgdb_might_be_resumed; +struct task_struct *kgdb_usethread, *kgdb_contthread; + +/* + * Indicate to caller of mem2hex or hex2mem that there has been an + * error. + */ +volatile int kgdb_memerr = 0; +volatile int kgdb_memerr_expected = 0; + +/* This will point to kgdb_handle_exception by default. + * The architecture code can override this in its init function + */ +gdb_debug_hook *linux_debug_hook; + + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; + +int hex(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 $# */ + +void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + 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() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + 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.*/ +void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + + } 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) */ +/* If MAY_FAULT is non-zero, then we should set kgdb_memerr in response to + a fault; if zero treat a fault like any other fault in the stub. */ + +char *mem2hex(char *mem, char *buf, int count, int can_fault) +{ + int i; + unsigned char ch; + + for (i = 0; i < count; i++) { + + if (get_char(mem++, &ch, can_fault) < 0) + break; + + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *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 */ + +char *hex2mem(char *buf, char *mem, int count, int can_fault) +{ + int i; + unsigned char ch; + + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + if (set_char(mem++, ch, can_fault) < 0) + break; + } + return (mem); +} + + +/* + * WHILE WE FIND NICE HEX CHARS, BUILD AN INT + * RETURN NUMBER OF CHARS PROCESSED + */ +int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +static int stubhex(int 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; +} + + +static int stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} + +static inline char *pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char *pack_threadid(char *pkt, threadref *id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + + return pkt; +} + +static char *unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char *unpack_threadid(char *inbuf, threadref *id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} + +void int_to_threadref(threadref *id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} + +static int threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} + + +struct task_struct *getthread(int tid) +{ +#if CONFIG_SMP + if (tid >= PID_MAX && tid < PID_MAX + smp_num_cpus) + return init_tasks[tid - PID_MAX]; +#else + if (tid == PID_MAX) + tid = 0; +#endif + return find_task_by_pid(tid); +} + +#ifdef CONFIG_SMP +void gdb_wait(struct pt_regs *regs) +{ + unsigned flags; + int processor; + + local_irq_save(flags); + processor = smp_processor_id(); + procindebug[processor] = 1; + current->thread.kgdbregs = regs; + + /* Wait till master processor goes completely into the debugger */ + while (!procindebug[atomic_read(&kgdb_lock) - 1]) { + int i = 10; /* an arbitrary number */ + + while (--i) + asm volatile ("nop": : : "memory"); + barrier(); + } + + /* Wait till master processor is done with debugging */ + spin_lock(slavecpulocks + processor); + + /* This has been taken from x86 kgdb implementation and + * will be needed by architectures that have SMP support + */ + if (kgdb_ops->correct_hw_break) + kgdb_ops->correct_hw_break(); + + /* Signal the master processor that we are done */ + procindebug[processor] = 0; + spin_unlock(slavecpulocks + processor); + local_irq_restore(flags); +} + +#endif + +static void get_mem (char *addr, unsigned char *buf, int count) +{ + while (count) { + if(get_char(addr++, buf, 1) < 0) + return; + buf++; + count--; + } +} + +static void set_mem (char *addr,unsigned char *buf, int count) +{ + while (count) { + if (set_char(addr++,*buf++, 1) < 0) + return; + count--; + } +} + + + +static int set_break (unsigned long addr) +{ + int i, breakno = -1; + + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if ((kgdb_break[i].state == bp_enabled) && + (kgdb_break[i].bpt_addr == addr)) { + breakno = -1; + break; + } + + if (kgdb_break[i].state == bp_disabled) { + if ((breakno == -1) || (kgdb_break[i].bpt_addr == addr)) + breakno = i; + } + } + if (breakno == -1) + return -1; + + get_mem((char *)addr, kgdb_break[breakno].saved_instr, BREAK_INSTR_SIZE); + if (kgdb_memerr) + return -1; + + set_mem((char *)addr, kgdb_ops->gdb_bpt_instr, BREAK_INSTR_SIZE); + flush_cache_range (current->mm, addr, addr + BREAK_INSTR_SIZE); + flush_icache_range (addr, addr + BREAK_INSTR_SIZE); + if (kgdb_memerr) + return -1; + + kgdb_break[breakno].state = bp_enabled; + kgdb_break[breakno].type = bp_breakpoint; + kgdb_break[breakno].bpt_addr = addr; + + return 0; +} + +static int remove_break (unsigned addr) +{ + int i; + + for (i=0; i < MAX_BREAKPOINTS; i++) { + if ((kgdb_break[i].state == bp_enabled) && + (kgdb_break[i].bpt_addr == addr)) { + set_mem((char *)addr, kgdb_break[i].saved_instr, + BREAK_INSTR_SIZE); + flush_cache_range (current->mm, addr, addr + BREAK_INSTR_SIZE); + flush_icache_range (addr, addr + BREAK_INSTR_SIZE); + if (kgdb_memerr) + return -1; + kgdb_break[i].state = bp_disabled; + return 0; + } + } + return -1; +} + +int remove_all_break(void) +{ + int i; + for (i=0; i < MAX_BREAKPOINTS; i++) { + if(kgdb_break[i].state == bp_enabled) { + unsigned long addr = kgdb_break[i].bpt_addr; + set_mem((char *)addr, kgdb_break[i].saved_instr, + BREAK_INSTR_SIZE); + flush_cache_range (current->mm, addr, addr + BREAK_INSTR_SIZE); + flush_icache_range (addr, addr + BREAK_INSTR_SIZE); + } + kgdb_break[i].state = bp_disabled; + } + return 0; +} + +int get_char(char *addr, unsigned char *data, int can_fault) +{ + mm_segment_t fs; + int ret = 0; + + kgdb_memerr = 0; + + if (can_fault) + kgdb_memerr_expected = 1; + wmb(); + fs = get_fs(); + set_fs(KERNEL_DS); + + if (get_user(*data, addr) != 0) { + ret = -EFAULT; + kgdb_memerr = 1; + } + + kgdb_memerr_expected = 0; + set_fs(fs); + return ret; +} + +int set_char(char *addr, int data, int can_fault) +{ + mm_segment_t fs; + int ret = 0; + + kgdb_memerr = 0; + + if (can_fault) + kgdb_memerr_expected = 1; + wmb(); + fs = get_fs(); + set_fs(KERNEL_DS); + + if (put_user(data, addr) != 0) { + ret = -EFAULT; + kgdb_memerr = 1; + } + + kgdb_memerr_expected = 0; + set_fs(fs); + return ret; +} + +/* + * This function does all command procesing for interfacing to gdb. + */ + +int kgdb_handle_exception(int exVector, int signo, int err_code, + struct pt_regs *linux_regs) +{ + int length, addr; + char *ptr; + unsigned long flags; + int gdb_regs[NUMREGBYTES / 4]; + int i; + int threadid; +#ifdef CONFIG_KGDB_THREAD + threadref thref; + struct task_struct *thread = NULL; + int nothreads; + int maxthreads; +#endif + unsigned procid; + int ret = 0; + struct pt_regs *oldregs = current->thread.kgdbregs; + + /* + * Interrupts will be restored by the 'trap return' code, except when + * single stepping. + */ + local_irq_save(flags); + + /* Hold kgdb_lock */ + procid = smp_processor_id(); + while (cmpxchg(&atomic_read(&kgdb_lock), 0, (procid + 1)) != 0) { + int i = 25; /* an arbitrary number */ + + while (--i) + asm volatile ("nop": : : "memory"); + } + + kgdb_step = 0; + + + local_irq_save(flags); + + current->thread.kgdbregs = linux_regs; + + if (kgdb_ops->disable_hw_debug) + kgdb_ops->disable_hw_debug(linux_regs); + + + for (i = 0; i < smp_num_cpus; i++) { + spin_lock(&slavecpulocks[i]); + } + + /* spin_lock code is good enough as a barrier so we don't + * need one here */ + procindebug[smp_processor_id()] = 1; + + /* Master processor is completely in the debugger */ + + if (kgdb_ops->post_master_code) + kgdb_ops->post_master_code(linux_regs, exVector, err_code); + + if (atomic_read(&kgdb_killed_or_detached) && + atomic_read(&kgdb_might_be_resumed)) { + getpacket(remcomInBuffer); + if(remcomInBuffer[0] == 'H' && remcomInBuffer[1] =='c') { + remove_all_break(); + atomic_set(&kgdb_killed_or_detached, 0); + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = 0; + } + else + return 1; + } + else { + + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 'p'; + + int_to_threadref(&thref, current->pid ? + current->pid : + PID_MAX + cpu_number_map(smp_processor_id())); + + *pack_threadid(remcomOutBuffer + 4, &thref) = 0; + } + putpacket(remcomOutBuffer); + + kgdb_usethread = current; + + while (1) { + int bpt_type = 0; + error = 0; + remcomOutBuffer[0] = 0; + remcomOutBuffer[1] = 0; + getpacket(remcomInBuffer); + +#if KGDB_DEBUG + bust_spinlocks(1); + printk("CPU%d pid%d GDB packet: %s\n", + smp_processor_id(), current->pid, remcomInBuffer); + bust_spinlocks(0); +#endif + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + + case 'g': /* return the value of the CPU registers */ + thread = kgdb_usethread; + + if (!thread) + thread = current; + + /* All threads that don't have kgdbregs should be + in __schedule() sleeping, since all other CPUs + are in gdbwait, and thus have kgdbregs. */ + + if (thread->thread.kgdbregs) + kgdb_ops->regs_to_gdb_regs(gdb_regs, thread->thread.kgdbregs); + else { + /* Pull stuff saved during + * switch_to; nothing else is + accessible (or even particularly relevant). + This should be enough for a stack trace. */ + kgdb_ops->sleeping_thread_to_gdb_regs(gdb_regs, thread); + } + + mem2hex((char *) gdb_regs, remcomOutBuffer, NUMREGBYTES, 0); + break; + + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], (char *) gdb_regs, + NUMREGBYTES, 0); + + if (kgdb_usethread && kgdb_usethread != current) + strcpy(remcomOutBuffer, "E00"); + else { + kgdb_ops->gdb_regs_to_regs(gdb_regs, current->thread.kgdbregs); + strcpy(remcomOutBuffer, "OK"); + } + + break; + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && *ptr++ == ',' && + hexToInt(&ptr, &length)) { + ptr = 0; + mem2hex((char *) addr, remcomOutBuffer, length, 1); + if (kgdb_memerr) + strcpy(remcomOutBuffer, "E03"); + + } + + if (ptr) + strcpy(remcomOutBuffer, "E01"); + break; + + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && *(ptr++) == ',' && + hexToInt(&ptr, &length) && *(ptr++) == ':') { + hex2mem(ptr, (char *)addr, length, 1); + if (kgdb_memerr) + strcpy(remcomOutBuffer, "E03"); + else + strcpy(remcomOutBuffer, "OK"); + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + } + break; + + + /* Continue and Single Step are Architecture specific + * and will not be handled by the generic code. + */ + + + /* kill the program. KGDB should treat this like a + * continue. + */ + case 'D': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + remove_all_break(); + putpacket(remcomOutBuffer); + goto default_handle; + + case 'k': + remove_all_break(); + goto default_handle; + + /* query */ + case 'q': + switch (remcomInBuffer[1]) { + case 'L': + /* List threads */ + unpack_byte(remcomInBuffer + 3, + &maxthreads); + unpack_threadid(remcomInBuffer + 5, + &thref); + + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads + && threadid < PID_MAX + smp_num_cpus; threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid + (remcomOutBuffer + 21 + + nothreads * 16, + &thref); + nothreads++; + } + } + + + if (threadid == PID_MAX + smp_num_cpus) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, + nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; + + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + + if (threadid == 0) + threadid = cpu_number_map(smp_processor_id()) + PID_MAX; + + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + if (kgdb_ops->printexpinfo) + kgdb_ops->printexpinfo(exVector, + err_code, + remcomOutBuffer); + break; + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (!thread && threadid > 0) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + kgdb_usethread = thread; + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + + case 'c': + atomic_set(&kgdb_killed_or_detached, 0); + ptr = &remcomInBuffer[2]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (!thread && threadid > 0) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + kgdb_contthread = thread; + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + case 'z': + case 'Z': + ptr = &remcomInBuffer[2]; + if (*(ptr++) != ',') { + strcpy(remcomOutBuffer, "ERROR"); + break; + } + hexToInt(&ptr, &addr); + + bpt_type = remcomInBuffer[1]; + if (bpt_type != bp_breakpoint) { + if (bpt_type == bp_hardware_breakpoint && + !(kgdb_ops->flags & KGDB_HW_BREAKPOINT)) + break; + + /* if set_break is not defined, then + * remove_break does not matter + */ + if(!kgdb_ops->set_break) + break; + } + + if (remcomInBuffer[0] == 'Z') { + if (bpt_type == bp_breakpoint) + ret = set_break(addr); + else + ret = kgdb_ops->set_break(addr, bpt_type); + } + else { + if (bpt_type == bp_breakpoint) + ret = remove_break(addr); + else + ret = kgdb_ops->remove_break(addr, bpt_type); + } + + if (ret == 0) + strcpy(remcomOutBuffer, "OK"); + else + strcpy(remcomOutBuffer, "ERROR"); + + break; + + default: + default_handle: + ret = 0; + if (kgdb_ops->handle_buffer) + ret= kgdb_ops->handle_buffer(exVector, signo, + err_code, + remcomInBuffer, + remcomOutBuffer, + linux_regs); + if(ret >= 0 || remcomInBuffer[0] == 'D' || + remcomInBuffer[0] == 'k') + goto kgdb_exit; + + + } /* switch */ +#if KGDB_DEBUG + bust_spinlocks(1); + printk("Response to GDB: %s\n", remcomOutBuffer); + bust_spinlocks(0); +#endif + + /* reply to the request */ + putpacket(remcomOutBuffer); + } +kgdb_exit: + + if(kgdb_ops->handler_exit) + kgdb_ops->handler_exit(); + + procindebug[smp_processor_id()] = 0; + + for (i = 0; i < smp_num_cpus; i++) { + spin_unlock(&slavecpulocks[i]); + } + /* Wait till all the processors have quit + * from the debugger + */ + for (i = 0; i < smp_num_cpus; i++) { + while (procindebug[i]) { + int j = 10; /* an arbitrary number */ + + while (--j) { + asm volatile ("nop" : : : "memory"); + } + barrier(); + } + } + + /* Free kgdb_lock */ + atomic_set(&kgdb_lock, 0); + current->thread.kgdbregs = oldregs; + atomic_set(&kgdb_killed_or_detached, 1); + local_irq_restore(flags); + return ret; +} + +/* this function is used to set up exception handlers for tracing and + breakpoints */ +void set_debug_traps(void) +{ + int i; + + for (i = 0; i < KGDB_MAX_NO_CPUS; i++) + spin_lock_init(&slavecpulocks[i]); + + /* Free kgdb_lock */ + atomic_set(&kgdb_lock, 0); + + /* This flag is used, if gdb has detached and wants to start + * another session + */ + atomic_set(&kgdb_killed_or_detached, 0); + atomic_set(&kgdb_might_be_resumed, 0); + + for (i = 0; i < MAX_BREAKPOINTS; i++) + kgdb_break[i].state = bp_disabled; + + + /* + * In case GDB is started before us, ack any packets (presumably + * "$?#xx") sitting there. */ + putDebugChar('+'); + + linux_debug_hook = kgdb_handle_exception; + + if (kgdb_ops->kgdb_init) + kgdb_ops->kgdb_init(); + + kgdb_initialized = 1; + atomic_set(&kgdb_setting_breakpoint, 0); +} + +/* 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 (kgdb_initialized) { + atomic_set(&kgdb_setting_breakpoint, 1); + wmb(); + BREAKPOINT(); + wmb(); + atomic_set(&kgdb_setting_breakpoint, 0); + } +} + +#ifdef CONFIG_GDB_CONSOLE +char gdbconbuf[BUFMAX]; + +void gdb_console_write(struct console *co, const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + int flags; + + if (!gdb_initialized || atomic_read(&kgdb_killed_or_detached)) { + return; + } + local_irq_save(flags); + + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + local_irq_restore(flags); +} +#endif + +int gdb_enter; +int gdb_baud = 115200; +int gdb_ttyS; +int gdb_initialized; + +static int __init kgdb_opt_gdb(char *str) +{ + gdb_enter = 1; + return 1; +} +static int __init kgdb_opt_gdbttyS(char *str) +{ + gdb_ttyS = simple_strtoul(str, NULL, 10); + return 1; +} +static int __init kgdb_opt_gdbbaud(char *str) +{ + gdb_baud = simple_strtoul(str, NULL, 10); + return 1; +} + +/* + * Sequence of these lines has to be maintained because gdb option is a prefix + * of the other two options + */ + +__setup("gdbttyS=", kgdb_opt_gdbttyS); +__setup("gdbbaud=", kgdb_opt_gdbbaud); +__setup("gdb", kgdb_opt_gdb); diff -Naur linux-2.4.23/kernel/ksyms.c linux-2.4.23-kgdb/kernel/ksyms.c --- linux-2.4.23/kernel/ksyms.c 2003-12-17 16:15:52.000000000 +0530 +++ linux-2.4.23-kgdb/kernel/ksyms.c 2003-12-17 15:28:19.000000000 +0530 @@ -457,7 +457,10 @@ EXPORT_SYMBOL(sleep_on_timeout); EXPORT_SYMBOL(interruptible_sleep_on); EXPORT_SYMBOL(interruptible_sleep_on_timeout); -EXPORT_SYMBOL(schedule); +EXPORT_SYMBOL(do_schedule); +#ifdef CONFIG_KGDB_THREAD +EXPORT_SYMBOL(kern_schedule); +#endif EXPORT_SYMBOL(schedule_timeout); #if CONFIG_SMP EXPORT_SYMBOL(set_cpus_allowed); diff -Naur linux-2.4.23/kernel/Makefile linux-2.4.23-kgdb/kernel/Makefile --- linux-2.4.23/kernel/Makefile 2003-10-22 02:57:18.000000000 +0530 +++ linux-2.4.23-kgdb/kernel/Makefile 2003-12-17 15:28:19.000000000 +0530 @@ -19,6 +19,7 @@ obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_KGDB) += kgdbstub.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra , the -fno-omit-frame-pointer is diff -Naur linux-2.4.23/kernel/sched.c linux-2.4.23-kgdb/kernel/sched.c --- linux-2.4.23/kernel/sched.c 2003-12-17 16:15:52.000000000 +0530 +++ linux-2.4.23-kgdb/kernel/sched.c 2003-12-17 15:28:19.000000000 +0530 @@ -544,7 +544,7 @@ * tasks can run. It can not be killed, and it cannot sleep. The 'state' * information in task[0] is never used. */ -asmlinkage void schedule(void) +asmlinkage void do_schedule(void) { struct schedule_data * sched_data; struct task_struct *prev, *next, *p; @@ -702,6 +702,22 @@ return; } +asmlinkage void user_schedule(void) +{ +#ifdef CONFIG_KGDB_THREAD + current->thread.kgdbregs = NULL; +#endif + do_schedule(); +} + +#ifdef CONFIG_KGDB_THREAD +asmlinkage void kern_do_schedule(struct pt_regs regs) +{ + current->thread.kgdbregs = ®s; + do_schedule(); +} +#endif + /* * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just wake everything * up. If it's an exclusive wakeup (nr_exclusive == small +ve number) then we wake all the diff -Naur linux-2.4.23/Makefile linux-2.4.23-kgdb/Makefile --- linux-2.4.23/Makefile 2003-12-17 16:15:29.000000000 +0530 +++ linux-2.4.23-kgdb/Makefile 2003-12-17 15:28:19.000000000 +0530 @@ -93,9 +93,13 @@ CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ -fno-strict-aliasing -fno-common +ifeq ($(CONFIG_KGDB),y) +CFLAGS += -g +else ifndef CONFIG_FRAME_POINTER CFLAGS += -fomit-frame-pointer endif +endif AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) #