--- /dev/null
+ Documentation/Configure.help | 23
+ Documentation/i386/gdb-serial.txt | 386 ++++++++++
+ Makefile | 4
+ arch/i386/Makefile | 6
+ arch/i386/config.in | 39 -
+ arch/i386/kernel/Makefile | 18
+ arch/i386/kernel/entry.S | 27
+ arch/i386/kernel/gdbstart.c | 148 ++++
+ arch/i386/kernel/gdbstub.c | 1402 ++++++++++++++++++++++++++++++++++++++
+ arch/i386/kernel/nmi.c | 30
+ arch/i386/kernel/signal.c | 3
+ arch/i386/kernel/traps.c | 44 +
+ arch/i386/mm/fault.c | 31
+ drivers/char/Makefile | 1
+ drivers/char/gdbserial.c | 280 +++++++
+ drivers/char/serial.c | 147 +++
+ drivers/char/tty_io.c | 6
+ include/asm-i386/ioctls.h | 1
+ include/asm-i386/page.h | 4
+ include/asm-i386/processor.h | 3
+ include/linux/dcache.h | 1
+ include/linux/gdb.h | 63 +
+ include/linux/sched.h | 13
+ init/main.c | 9
+ kernel/ksyms.c | 5
+ kernel/sched.c | 18
+ 26 files changed, 2679 insertions(+), 33 deletions(-)
+
+--- linux-2.4.18-p4smp/arch/i386/config.in~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/config.in 2003-07-09 13:18:12.000000000 -0600
+@@ -456,25 +456,26 @@ mainmenu_option next_comment
+ comment 'Kernel hacking'
+
+ bool 'Kernel debugging' CONFIG_DEBUG_KERNEL
+-if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then
+- bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER
+- bool ' Use Scan technique for doing stack trace' CONFIG_STACK_TRACE_SCAN
+- if [ "$CONFIG_FRAME_POINTER" = "y" ]; then
+- bool ' Use Frame Pointer Walk technique for doing stack trace' CONFIG_STACK_TRACE_FPTR
+- fi
+- int 'Number of parameters to show in stack trace' CONFIG_STACK_TRACE_PARAM_COUNT 4
+- 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 ' Kernel Core Dump Facility' CONFIG_MCL_COREDUMP
+- if [ "$CONFIG_MCL_COREDUMP" = "y" ]; then
+- bool ' Reboot using bootimg' CONFIG_BOOTIMG
+- fi
+- bool ' OPROFILE support' CONFIG_OPROFILE
+-fi
+-
++bool 'KGDB: Remote (serial) kernel debugging with gdb' CONFIG_X86_REMOTE_DEBUG
++ if [ "$CONFIG_X86_REMOTE_DEBUG" != "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 ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER
++ 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 ' Kernel Core Dump Facility' CONFIG_MCL_COREDUMP
++ if [ "$CONFIG_MCL_COREDUMP" = "y" ]; then
++ bool ' Reboot using bootimg' CONFIG_BOOTIMG
++ fi
++ bool ' OPROFILE support' CONFIG_OPROFILE
++ fi
++ fi
+ endmenu
+
+ source lib/Config.in
+--- linux-2.4.18-p4smp/arch/i386/kernel/entry.S~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/entry.S 2003-07-09 13:15:23.000000000 -0600
+@@ -266,7 +266,7 @@ ret_from_exception:
+
+ ALIGN
+ reschedule:
+- call SYMBOL_NAME(schedule) # test
++ call SYMBOL_NAME(user_schedule) # test
+ jmp ret_from_sys_call
+
+ ENTRY(divide_error)
+@@ -404,6 +404,31 @@ ENTRY(spurious_interrupt_bug)
+ 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*/
+--- /dev/null 2003-01-30 03:24:37.000000000 -0700
++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/gdbstart.c 2003-07-09 13:15:23.000000000 -0600
+@@ -0,0 +1,148 @@
++/*
++ * This program opens a tty file and issues the GDB stub activating
++ * ioctl on it.
++ */
++
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <asm/ioctls.h>
++#include <stdio.h>
++#include <string.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <stdlib.h>
++#include <termios.h>
++#include <unistd.h>
++
++#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 */
+--- /dev/null 2003-01-30 03:24:37.000000000 -0700
++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/gdbstub.c 2003-07-09 13:15:23.000000000 -0600
+@@ -0,0 +1,1402 @@
++/*
++ *
++ * 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<akale@veritas.com>
++ * ModuleState: Experimental $
++ *
++ * NOTES: See Below $
++ *
++ * Modified for 386 by Jim Kingdon, Cygnus Support.
++ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <dave@gcom.com>
++ * Integrated into 2.2.5 kernel by Tigran Aivazian <tigran@sco.com>
++ * thread support,
++ * support for multiple processors,
++ * support for ia-32(x86) hardware debugging,
++ * Console support,
++ * handling nmi watchdog
++ * Amit S. Kale ( akale@veritas.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
++ *
++ * $<packet info>#<checksum>.
++ *
++ * where
++ * <packet info> :: <characters representing the command or response>
++ * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
++ *
++ * 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 <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/smp.h>
++#include <linux/spinlock.h>
++#include <linux/delay.h>
++#include <asm/vm86.h>
++#include <asm/system.h>
++#include <asm/ptrace.h> /* for linux pt_regs struct */
++#include <linux/gdb.h>
++#ifdef CONFIG_GDB_CONSOLE
++#include <linux/console.h>
++#endif
++#include <linux/init.h>
++
++/************************************************************************
++ *
++ * external low-level support routines
++ */
++typedef void (*Function)(void); /* pointer to a function */
++
++/* Thread reference */
++typedef unsigned char threadref[8];
++
++extern int putDebugChar(int); /* write a single character */
++extern int getDebugChar(void); /* read and return a single char */
++
++/************************************************************************/
++/* 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
++
++static char initialized; /* boolean flag. != 0 means we've been initialized */
++
++static const char hexchars[]="0123456789abcdef";
++
++/* 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 */
++
++
++
++/*************************** ASSEMBLY CODE MACROS *************************/
++/* */
++
++#define BREAKPOINT() asm(" int $3");
++
++/* 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 spinlock_t slavecpulocks[KGDB_MAX_NO_CPUS] = { SPIN_LOCK_UNLOCKED,
++ SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED,
++ SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED,
++ SPIN_LOCK_UNLOCKED };
++volatile int procindebug[KGDB_MAX_NO_CPUS];
++
++volatile unsigned kgdb_step = 0;
++
++volatile unsigned kgdb_lock = 0;
++
++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
++};
++
++#define BREAK_INSTR_SIZE 1
++
++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;
++
++#define MAX_BREAKPOINTS 16
++
++gdb_breakpoint_t kgdb_break[MAX_BREAKPOINTS];
++static char gdb_bpt_instr[BREAK_INSTR_SIZE] = {0xcc};
++
++static void kgdb_usercode (void)
++{
++}
++
++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 $<data>#<checksum> */
++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;
++
++ /* $<packet info>#<checksum>. */
++ do {
++ putDebugChar('$');
++ checksum = 0;
++ count = 0;
++
++ while ((ch=buffer[count])) {
++ if (! putDebugChar(ch)) return;
++ checksum += ch;
++ count += 1;
++ }
++
++ putDebugChar('#');
++ putDebugChar(hexchars[checksum >> 4]);
++ putDebugChar(hexchars[checksum % 16]);
++
++ } while ((getDebugChar() & 0x7f) != '+');
++
++}
++
++static char remcomInBuffer[BUFMAX];
++static char remcomOutBuffer[BUFMAX];
++static short error;
++
++static void 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 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 */
++
++/* Indicate to caller of mem2hex or hex2mem that there has been an
++ error. */
++static volatile int kgdb_memerr = 0;
++volatile int kgdb_memerr_expected = 0;
++static volatile int kgdb_memerr_cnt = 0;
++static int garbage_loc = -1 ;
++
++int
++get_char (char *addr)
++{
++ return *addr;
++}
++
++void
++set_char ( char *addr, int val)
++{
++ *addr = val;
++}
++
++static void
++get_mem (char *addr,
++ unsigned char *buf,
++ int count)
++{
++ while (count) {
++ *buf++ = get_char(addr++);
++ if (kgdb_memerr)
++ return;
++ count--;
++ }
++}
++
++static void
++set_mem (char *addr,
++ char *buf,
++ int count)
++{
++ while (count) {
++ set_char(addr++,*buf++);
++ if (kgdb_memerr)
++ return;
++ count--;
++ }
++}
++
++/* 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 may_fault)
++{
++ int i;
++ unsigned char ch;
++
++ if (may_fault)
++ {
++ kgdb_memerr_expected = 1 ;
++ kgdb_memerr = 0 ;
++ }
++ for (i=0;i<count;i++) {
++
++ ch = get_char (mem++);
++
++ if (may_fault && kgdb_memerr)
++ {
++ *buf = 0 ; /* truncate buffer */
++ return (buf);
++ }
++ *buf++ = hexchars[ch >> 4];
++ *buf++ = hexchars[ch % 16];
++ }
++ *buf = 0;
++ if (may_fault)
++ kgdb_memerr_expected = 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 may_fault)
++{
++ int i;
++ unsigned char ch;
++
++ if (may_fault)
++ {
++ kgdb_memerr_expected = 1 ;
++ kgdb_memerr = 0 ;
++ }
++ for (i=0;i<count;i++) {
++ ch = hex(*buf++) << 4;
++ ch = ch + hex(*buf++);
++ set_char (mem++, ch);
++
++ if (may_fault && kgdb_memerr)
++ {
++ return (mem);
++ }
++ }
++ if (may_fault)
++ kgdb_memerr_expected = 0 ;
++ 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);
++}
++
++#ifdef CONFIG_KGDB_THREAD
++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;
++}
++#endif
++
++static 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
++
++#ifdef CONFIG_KGDB_THREAD
++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;
++}
++#endif
++
++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);
++}
++
++#ifdef CONFIG_KGDB_THREAD
++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 pid)
++{
++ task_t *thread;
++ thread = find_task_by_pid(pid);
++ if (thread) {
++ return thread;
++ }
++ thread = &init_task;
++ do {
++ if (thread->pid == pid) {
++ return thread;
++ }
++ thread = thread->next_task;
++ } while (thread != &init_task);
++ return NULL;
++}
++#endif
++
++struct hw_breakpoint {
++ unsigned enabled;
++ unsigned type;
++ unsigned len;
++ unsigned addr;
++} breakinfo[4] = { { enabled:0 }, { enabled:0 }, { enabled:0 }, { enabled:0 }};
++
++void 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 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 int
++set_break (
++ unsigned 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,gdb_bpt_instr,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);
++ if (kgdb_memerr)
++ return -1;
++ kgdb_break[i].state = bp_disabled;
++ return 0;
++ }
++ }
++ return -1;
++}
++
++static void
++clear_disabled_break()
++{
++ int i;
++ for (i=0; i<MAX_BREAKPOINTS; i++) {
++ if (kgdb_break[i].state == bp_disabled)
++ kgdb_break[i].bpt_addr = 0;
++ }
++}
++
++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[kgdb_lock - 1]) {
++ int i = 10; /* an arbitrary number */
++
++ while (--i)
++ asm volatile ("nop": : : "memory");
++
++ }
++
++ /* Wait till master processor is done with debugging */
++ spin_lock(slavecpulocks + processor);
++ correct_hw_break();
++
++ /* Signal the master processor that we are done */
++ procindebug[processor] = 0;
++ spin_unlock(slavecpulocks + processor);
++ local_irq_restore(flags);
++}
++
++void
++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;
++}
++
++/*
++ * This function does all command procesing for interfacing to gdb.
++ *
++ * NOTE: The INT nn instruction leaves the state of the interrupt
++ * enable flag UNCHANGED. That means that when this routine
++ * is entered via a breakpoint (INT 3) instruction from code
++ * that has interrupts enabled, then interrupts will STILL BE
++ * enabled when this routine is entered. The first thing that
++ * we do here is disable interrupts so as to prevent recursive
++ * entries and bothersome serial interrupts while we are
++ * trying to run the serial port in polled mode.
++ *
++ * For kernel version 2.1.xx the cli() actually gets a spin lock so
++ * it is always necessary to do a restore_flags before returning
++ * so as to let go of that lock.
++ */
++int handle_exception(int exceptionVector,
++ int signo,
++ int err_code,
++ struct pt_regs *linux_regs)
++{
++ struct task_struct *usethread = NULL;
++ int addr, length;
++ int breakno, breaktype;
++ char *ptr;
++ int newPC;
++ unsigned long flags;
++ int gdb_regs[NUMREGBYTES/4];
++ int i;
++ int dr6;
++ int ret;
++ int maxbpt, bpt_done;
++ unsigned long bptaddr;
++ struct pt_regs tempregs;
++#ifdef CONFIG_KGDB_THREAD
++ int nothreads;
++ int maxthreads;
++ int threadid;
++ threadref thref;
++ struct task_struct *thread = NULL;
++#endif
++ unsigned procid;
++
++#define regs (*linux_regs)
++
++ /*
++ * If the entry is not from the kernel then return to the Linux
++ * trap handler and let it process the interrupt normally.
++ */
++ if ((linux_regs->eflags & VM_MASK) || (3 & linux_regs->xcs)) {
++ return(0);
++ }
++
++ if (kgdb_memerr_expected)
++ {
++ /*
++ * This fault occured because of the get_char or set_char
++ * routines. These two routines use either eax of edx to
++ * indirectly reference the location in memory that they
++ * are working with. For a page fault, when we return
++ * the instruction will be retried, so we have to make
++ * sure that these registers point to valid memory.
++ */
++ kgdb_memerr = 1 ; /* set mem error flag */
++ kgdb_memerr_expected = 0 ;
++ kgdb_memerr_cnt++ ; /* helps in debugging */
++ regs.eax = (long) &garbage_loc ; /* make valid address */
++ regs.edx = (long) &garbage_loc ; /* make valid address */
++ return(0) ;
++ }
++
++ /* Hold kgdb_lock */
++ procid = smp_processor_id();
++ while (cmpxchg(&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);
++
++ /* Disable hardware debugging while we are in kgdb */
++ __asm__("movl %0,%%db7"
++ : /* no output */
++ : "r" (0));
++
++ 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 */
++
++ gdb_i386vector = exceptionVector;
++ gdb_i386errcode = err_code ;
++
++ /* reply to host that an exception has occurred */
++ remcomOutBuffer[0] = 'S';
++ remcomOutBuffer[1] = hexchars[signo >> 4];
++ remcomOutBuffer[2] = hexchars[signo % 16];
++ remcomOutBuffer[3] = 0;
++
++ putpacket(remcomOutBuffer);
++
++ while (1==1) {
++ error = 0;
++ remcomOutBuffer[0] = 0;
++ getpacket(remcomInBuffer);
++ 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 */
++ if (!usethread || usethread == current) {
++ regs_to_gdb_regs(gdb_regs, ®s) ;
++ } else {
++ memset(gdb_regs, 0, NUMREGBYTES);
++ if (usethread->thread.kgdbregs) {
++ kgdb_memerr_expected = 1 ;
++ kgdb_memerr = 0;
++ get_char((char *)usethread->thread.kgdbregs);
++ kgdb_memerr_expected = 0;
++ if (kgdb_memerr) {
++ *(((char *)&tempregs) + i) = '\0';
++ gdb_regs[_PC] = kgdb_usercode;
++ } else {
++ regs_to_gdb_regs(gdb_regs,
++ usethread->thread.kgdbregs);
++ }
++ } else {
++ gdb_regs[_PC] = kgdb_usercode;
++ }
++ }
++ 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 (!usethread || usethread == current) {
++ gdb_regs_to_regs(gdb_regs, ®s) ;
++ strcpy(remcomOutBuffer,"OK");
++ } else {
++ strcpy(remcomOutBuffer,"E00");
++ }
++ 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))
++ if (*(ptr++) == ',')
++ if (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))
++ if (*(ptr++) == ',')
++ if (hexToInt(&ptr,&length))
++ if (*(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;
++
++ /* cAA..AA Continue at address AA..AA(optional) */
++ /* sAA..AA Step one instruction from AA..AA(optional) */
++ /* D Detach from debugger */
++ case 'c' :
++ case 's' :
++ case 'D' :
++
++ /* try to read optional parameter, pc unchanged if no parm */
++ ptr = &remcomInBuffer[1];
++ if (hexToInt(&ptr,&addr))
++ {
++ regs.eip = addr;
++ }
++
++ newPC = regs.eip ;
++
++ /* clear the trace bit */
++ regs.eflags &= 0xfffffeff;
++
++ /* set the trace bit if we're stepping */
++ if (remcomInBuffer[0] == 's') {
++ regs.eflags |= 0x100;
++ kgdb_step = 1;
++ }
++ if (remcomInBuffer[0] == 'D') {
++ clear_disabled_break();
++ strcpy(remcomOutBuffer,"+");
++ putpacket(remcomOutBuffer);
++ }
++
++ 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 */
++ regs.eflags |= 0x10000;
++ break;
++ }
++ }
++ }
++ }
++ correct_hw_break();
++ asm volatile (
++ "movl %0, %%db6\n"
++ :
++ : "r" (0) );
++
++ procindebug[smp_processor_id()] = 0;
++
++ /* Signal the slave processors to quit from
++ * the debugger */
++ 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");
++ }
++ }
++ }
++
++ /* Release kgdb_lock */
++ asm volatile (
++#ifndef CONFIG_X86_OOSTORE
++ "movb $0,%0"
++#else
++ "lock movb $0,%0"
++#endif
++ : "=m" (kgdb_lock) : : "memory");
++
++ local_irq_restore(flags) ;
++ return(0) ;
++
++ /* kill the program */
++ case 'k' : /* do nothing */
++ break;
++
++ /* query */
++ case 'q' :
++ switch (remcomInBuffer[1]) {
++#ifdef CONFIG_KGDB_THREAD
++ 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;
++ threadid++ )
++ {
++ read_lock(&tasklist_lock);
++ thread = getthread(threadid);
++ read_unlock(&tasklist_lock);
++ if (thread) {
++ int_to_threadref(&thref, threadid);
++ pack_threadid(remcomOutBuffer+21+nothreads*16,
++ &thref);
++ nothreads++;
++ }
++ }
++ if (threadid == PID_MAX) {
++ 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;
++ int_to_threadref(&thref, threadid);
++ pack_threadid(remcomOutBuffer+2, &thref);
++ remcomOutBuffer[18] = '\0';
++ break;
++#endif
++
++ case 'E':
++ /* Print exception info */
++ printexceptioninfo(exceptionVector, err_code, remcomOutBuffer);
++ break;
++ }
++ break;
++
++#ifdef CONFIG_KGDB_THREAD
++ /* task related */
++ case 'H' :
++ switch (remcomInBuffer[1]) {
++ case 'g':
++ ptr = &remcomInBuffer[2];
++ hexToInt(&ptr, &threadid);
++ thread = getthread(threadid);
++ if (!thread) {
++ remcomOutBuffer[0] = 'E';
++ remcomOutBuffer[1] = '\0';
++ break;
++ }
++ usethread = thread;
++ /* follow through */
++ case 'c':
++ 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;
++#endif
++
++ 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;
++ case 'Z':
++ case 'z':
++ ptr = &remcomInBuffer[1];
++ if (*ptr++ != bp_breakpoint)
++ break;
++ if (*(ptr++) != ',') {
++ strcpy(remcomOutBuffer, "ERROR");
++ break;
++ }
++ hexToInt(&ptr, &addr);
++ if (remcomInBuffer[0] == 'Z')
++ ret = set_break(addr);
++ else
++ ret = remove_break(addr);
++
++ if (ret == 0) {
++ strcpy(remcomOutBuffer, "OK");
++ } else {
++ strcpy(remcomOutBuffer, "ERROR");
++ }
++ break;
++
++ } /* switch */
++
++ /* reply to the request */
++ putpacket(remcomOutBuffer);
++ }
++}
++
++/* this function is used to set up exception handlers for tracing and
++ breakpoints */
++void set_debug_traps(void)
++{
++ int i = 0;
++ /*
++ * linux_debug_hook is defined in traps.c. We store a pointer
++ * to our own exception handler into it.
++ */
++ linux_debug_hook = handle_exception ;
++
++ for (i=0; i<MAX_BREAKPOINTS; i++) {
++ kgdb_break[i].bpt_addr = 0;
++ kgdb_break[i].state = bp_disabled;
++ }
++
++ /*
++ * In case GDB is started before us, ack any packets (presumably
++ * "$?#xx") sitting there. */
++ putDebugChar ('+');
++
++ initialized = 1;
++}
++
++/* 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)
++ BREAKPOINT();
++}
++
++#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;
++
++ if (!gdb_initialized) {
++ return;
++ }
++ 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);
++
++ }
++}
++#endif
++static int __init kgdb_opt_gdb(void)
++{
++ 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);
+--- linux-2.4.18-p4smp/arch/i386/kernel/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/Makefile 2003-07-09 13:15:23.000000000 -0600
+@@ -10,7 +10,15 @@
+ .S.o:
+ $(CC) $(AFLAGS) -traditional -c $< -o $*.o
+
+-all: kernel.o head.o init_task.o
++ifeq ($(CONFIG_X86_REMOTE_DEBUG),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
+
+@@ -33,6 +41,7 @@ endif
+ obj-$(CONFIG_MCA) += mca.o
+ obj-$(CONFIG_E820_PROC) += e820.o rpmhelper.o
+ obj-$(CONFIG_MTRR) += mtrr.o
++obj-$(CONFIG_X86_REMOTE_DEBUG) += gdbstub.o
+ obj-$(CONFIG_X86_MSR) += msr.o
+ obj-$(CONFIG_X86_CPUID) += cpuid.o
+ obj-$(CONFIG_MICROCODE) += microcode.o
+@@ -51,4 +60,11 @@ obj-$(CONFIG_ABI) += lcall7.o
+ obj-$(CONFIG_MCL_COREDUMP) += crash.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
+--- linux-2.4.18-p4smp/arch/i386/kernel/nmi.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/nmi.c 2003-07-09 13:15:23.000000000 -0600
+@@ -25,6 +25,20 @@
+ #include <asm/mtrr.h>
+ #include <asm/mpspec.h>
+
++#ifdef CONFIG_X86_REMOTE_DEBUG
++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 @@ void nmi_watchdog_tick (struct pt_regs *
+ x86_do_profile(regs->eip);
+
+ sum = irq_stat[cpu].apic_timer_irqs;
++#ifdef CONFIG_X86_REMOTE_DEBUG
++ if (kgdb_lock) {
++
++ /*
++ * The machine is in kgdb, hold this cpu if already
++ * not held.
++ */
+
++ if (!procindebug[cpu] && 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 @@ void nmi_watchdog_tick (struct pt_regs *
+ */
+ 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
+--- linux-2.4.18-p4smp/arch/i386/kernel/signal.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/signal.c 2003-07-09 13:15:23.000000000 -0600
+@@ -701,7 +701,8 @@ int do_signal(struct pt_regs *regs, sigs
+ * 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);
+--- linux-2.4.18-p4smp/arch/i386/kernel/traps.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/traps.c 2003-07-09 13:19:30.000000000 -0600
+@@ -49,12 +49,33 @@
+ #endif
+
+ #include <linux/irq.h>
++#ifdef CONFIG_X86_REMOTE_DEBUG
++#include <linux/gdb.h>
++#endif
+ #include <linux/module.h>
+
+ #ifdef CONFIG_MCL_COREDUMP
+ #include <linux/crash.h>
+ #endif
+
++#ifdef CONFIG_X86_REMOTE_DEBUG
++#include <linux/gdb.h>
++#endif
++
++#ifdef CONFIG_X86_REMOTE_DEBUG
++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
++
+ asmlinkage int system_call(void);
+ asmlinkage void lcall7(void);
+ asmlinkage void lcall27(void);
+@@ -409,6 +430,7 @@ void die(const char * str, struct pt_reg
+ bust_spinlocks(1);
+ handle_BUG(regs);
+ printk("%s: %04lx\n", str, err & 0xffff);
++ CHK_REMOTE_DEBUG(1,SIGTRAP,err,regs,)
+ show_registers(regs);
+ if (netdump_func)
+ netdump_func(regs);
+@@ -479,6 +501,7 @@ static void inline do_trap(int trapnr, i
+ #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); \
+ }
+
+@@ -496,7 +519,10 @@ asmlinkage void do_##name(struct pt_regs
+ #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) \
+@@ -548,6 +574,7 @@ gp_in_kernel:
+ regs->eip = fixup;
+ return;
+ }
++ CHK_REMOTE_DEBUG(13,SIGSEGV,error_code,regs,)
+ die("general protection fault", regs, error_code);
+ }
+ }
+@@ -659,7 +686,7 @@ asmlinkage void do_debug(struct pt_regs
+ __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 */
+@@ -675,7 +702,7 @@ asmlinkage void do_debug(struct pt_regs
+ 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
+@@ -698,11 +725,13 @@ asmlinkage void do_debug(struct pt_regs
+ 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
+@@ -712,6 +741,7 @@ clear_dr7:
+ __asm__("movl %0,%%db7"
+ : /* no output */
+ : "r" (0));
++ CHK_REMOTE_DEBUG(1,SIGTRAP,error_code,regs,)
+ return;
+
+ debug_vm86:
+--- linux-2.4.18-p4smp/arch/i386/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/Makefile 2003-07-09 13:15:23.000000000 -0600
+@@ -105,6 +105,11 @@ arch/i386/mm: dummy
+ $(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
+
+@@ -140,6 +145,7 @@ install: vmlinux
+
+ archclean:
+ @$(MAKEBOOT) clean
++ @$(CLEANKERNEL)
+
+ archmrproper:
+
+--- linux-2.4.18-p4smp/arch/i386/mm/fault.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/arch/i386/mm/fault.c 2003-07-09 13:15:23.000000000 -0600
+@@ -2,6 +2,11 @@
+ * linux/arch/i386/mm/fault.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
++ *
++ * Change History
++ *
++ * Tigran Aivazian <tigran@sco.com> Remote debugging support.
++ *
+ */
+
+ #include <linux/signal.h>
+@@ -19,6 +24,9 @@
+ #include <linux/init.h>
+ #include <linux/tty.h>
+ #include <linux/vt_kern.h> /* For unblank_screen() */
++#ifdef CONFIG_X86_REMOTE_DEBUG
++#include <linux/gdb.h>
++#endif
+
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
+@@ -183,6 +191,15 @@ asmlinkage void do_page_fault(struct pt_
+ if (in_interrupt() || !mm)
+ goto no_context;
+
++#ifdef CONFIG_X86_REMOTE_DEBUG
++ if (kgdb_memerr_expected) {
++ if (linux_debug_hook != (gdb_debug_hook *) NULL) {
++ (*linux_debug_hook)(14, SIGSEGV, error_code, regs) ;
++ return; /* return w/modified regs */
++ }
++ }
++#endif
++
+ down_read(&mm->mmap_sem);
+
+ vma = find_vma(mm, address);
+@@ -301,6 +318,19 @@ no_context:
+ return;
+ }
+
++#ifdef CONFIG_X86_REMOTE_DEBUG
++ if (kgdb_memerr_expected) {
++ if (linux_debug_hook != (gdb_debug_hook *) NULL) {
++ (*linux_debug_hook)(14, SIGSEGV, error_code, regs);
++ return; /* Return with modified registers */
++ }
++ } else {
++ 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 +338,7 @@ no_context:
+
+ bust_spinlocks(1);
+
++
+ if (address < PAGE_SIZE)
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ else
+--- linux-2.4.18-p4smp/Documentation/Configure.help~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/Documentation/Configure.help 2003-07-09 13:15:23.000000000 -0600
+@@ -20274,6 +20274,29 @@ CONFIG_SGIWD93_SCSI
+ 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_X86_REMOTE_DEBUG
++ If you say Y here, it will be possible to remotely debug the x86
++ kernel using gdb. This enlarges your kernel image disk size by
++ several megabytes and requires a machine with more than 16 MB,
++ better 32 MB RAM to avoid excessive linking time.
++ To use this feature you need to perform some basic setup described
++ briefly in Documentation/i386/gdb-serial.txt.
++ 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
+--- /dev/null 2003-01-30 03:24:37.000000000 -0700
++++ linux-2.4.18-p4smp-braam/Documentation/i386/gdb-serial.txt 2003-07-09 13:15:23.000000000 -0600
+@@ -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
++ <blank line>
++ 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 <sfoehner@engr.sgi.com> 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=<port number>" and
++"gdbbaud=<baud rate>" 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
++<Edouard.Parmelan@quadratec.fr>
++
++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 <module-address> <module-name>
++
++mod-print-symbols <module-address>
++ Prints all the symbols in the indicated module.
++
++mod-add-symbols <module-address> <object-file-path-name>
++ 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 <module-address> 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
++<akale@veritas.com>
++
++(First kgdb by David Grothe <dave@gcom.com>)
++
++(modified by Tigran Aivazian <tigran@sco.com>)
++ Putting gdbstub into the kernel config menu.
++
++(modified by Scott Foehner <sfoehner@engr.sgi.com>)
++ Hooks for entering gdbstub at boot time.
++
++(modified by Amit S. Kale <akale@veritas.com>)
++ Threads, ia-32 hw debugging, mp support, console support,
++ nmi watchdog handling.
+--- /dev/null 2003-01-30 03:24:37.000000000 -0700
++++ linux-2.4.18-p4smp-braam/drivers/char/gdbserial.c 2003-07-09 13:15:23.000000000 -0600
+@@ -0,0 +1,280 @@
++/*
++ * 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 <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/serial.h>
++#include <linux/serial_reg.h>
++#include <linux/serialP.h>
++#include <linux/config.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/termios.h>
++#include <linux/gdb.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/segment.h>
++#include <asm/bitops.h>
++#include <asm/system.h>
++#include <asm/irq.h>
++#include <asm/atomic.h>
++
++#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 ;
++
++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;
++int gdb_ttyS = 1; /* Default: ttyS1 */
++int gdb_baud = 38400;
++int gdb_enter = 0; /* Default: do not do gdb_hook on boot */
++int gdb_initialized = 0;
++
++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(&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);
++
++} /* 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.
++ */
++int 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(int chr)
++{
++#ifdef PRNT
++ printk("putDebugChar: chr=%02x '%c'\n", chr,
++ chr > ' ' && chr < 0x7F ? chr : ' ') ;
++#endif
++
++ write_char(chr) ; /* this routine will wait */
++
++} /* putDebugChar */
++
+--- linux-2.4.18-p4smp/drivers/char/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/drivers/char/Makefile 2003-07-09 13:15:23.000000000 -0600
+@@ -142,6 +142,7 @@ ifeq ($(CONFIG_DUMMY_KEYB),y)
+ KEYBD = dummy_keyb.o
+ endif
+
++obj-$(CONFIG_X86_REMOTE_DEBUG) += 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
+--- linux-2.4.18-p4smp/drivers/char/serial.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/drivers/char/serial.c 2003-07-09 13:15:23.000000000 -0600
+@@ -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_X86_REMOTE_DEBUG
++ * Tigran Aivazian
++ *
+ * 5/99: Updated to include support for the XR16C850 and ST16C654
+ * uarts. Stuart MacDonald <stuartm@connecttech.com>
+ *
+@@ -66,6 +71,8 @@
+ * to waiting processes
+ * Sapan Bhatia <sapan@corewars.org>
+ *
++ * 10/00: added console support for kgdb. Amit Kale <akale@veritas.com>
++ *
+ */
+
+ static char *serial_version = "5.05c";
+@@ -215,7 +222,7 @@ static char *serial_revdate = "2001-07-0
+ #include <asm/uaccess.h>
+ #endif
+ #include <linux/delay.h>
+-#ifdef CONFIG_SERIAL_CONSOLE
++#if defined(CONFIG_SERIAL_CONSOLE) || defined (CONFIG_GDB_CONSOLE)
+ #include <linux/console.h>
+ #endif
+ #ifdef ENABLE_SERIAL_PCI
+@@ -235,6 +242,9 @@ static char *serial_revdate = "2001-07-0
+ #if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */
+ #include "serial_compat.h"
+ #endif
++#ifdef CONFIG_X86_REMOTE_DEBUG
++#include <linux/gdb.h>
++#endif
+
+ #include <asm/system.h>
+ #include <asm/io.h>
+@@ -1586,6 +1596,12 @@ static void shutdown(struct async_struct
+
+ spin_unlock_irqrestore( &info->irq_spinlock, flags);
+ }
++#ifdef CONFIG_X86_REMOTE_DEBUG
++void shutdown_for_gdb(struct async_struct * info)
++{
++ shutdown(info) ;
++}
++#endif
+
+ #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
+ static int baud_table[] = {
+@@ -2698,6 +2714,12 @@ static int rs_ioctl(struct tty_struct *t
+ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+ return 0;
+
++#ifdef CONFIG_X86_REMOTE_DEBUG
++ case TIOCGDB:
++ gdb_ttyS = MINOR(tty->device) & 0x03F ;
++ gdb_baud = tty_get_baud_rate(tty) ;
++ return gdb_hook();
++#endif
+ default:
+ return -ENOIOCTLCMD;
+ }
+@@ -6043,6 +6065,129 @@ void __init serial_console_init(void)
+ #endif
+
+ /*
++ * ------------------------------------------------------------
++ * Serial GDB driver (most in gdbserial.c)
++ * ------------------------------------------------------------
++ */
++
++#ifdef CONFIG_X86_REMOTE_DEBUG
++#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_X86_REMOTE_DEBUG */
++
++/*
+ 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:
+--- linux-2.4.18-p4smp/drivers/char/tty_io.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/drivers/char/tty_io.c 2003-07-09 13:15:23.000000000 -0600
+@@ -90,6 +90,9 @@
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/smp_lock.h>
++#ifdef CONFIG_GDB_CONSOLE
++#include <linux/gdb.h>
++#endif
+
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+@@ -2233,6 +2236,9 @@ void __init console_init(void)
+ #ifdef CONFIG_AU1000_SERIAL_CONSOLE
+ au1000_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();
+--- linux-2.4.18-p4smp/include/asm-i386/ioctls.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/include/asm-i386/ioctls.h 2003-07-09 13:15:23.000000000 -0600
+@@ -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
+--- linux-2.4.18-p4smp/include/asm-i386/page.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/include/asm-i386/page.h 2003-07-09 13:15:23.000000000 -0600
+@@ -95,6 +95,9 @@ typedef struct { unsigned long pgprot; }
+ * undefined" opcode for parsing in the trap handler.
+ */
+
++#ifdef CONFIG_X86_REMOTE_DEBUG
++#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 +107,7 @@ typedef struct { unsigned long pgprot; }
+ #else
+ #define BUG() __asm__ __volatile__("ud2\n")
+ #endif
++#endif
+
+ #define PAGE_BUG(page) do { \
+ BUG(); \
+--- linux-2.4.18-p4smp/include/asm-i386/processor.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/include/asm-i386/processor.h 2003-07-09 13:20:27.000000000 -0600
+@@ -390,6 +390,9 @@ struct thread_struct {
+ /* IO permissions */
+ int ioperm;
+ unsigned long io_bitmap[IO_BITMAP_SIZE+1];
++#ifdef CONFIG_X86_REMOTE_DEBUG
++ struct pt_regs *kgdbregs;
++#endif
+ /* performance counters */
+ struct vperfctr *perfctr;
+ };
+--- linux-2.4.18-p4smp/include/linux/dcache.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/include/linux/dcache.h 2003-07-09 13:15:23.000000000 -0600
+@@ -4,6 +4,7 @@
+ #ifdef __KERNEL__
+
+ #include <asm/atomic.h>
++#include <linux/gdb.h>
+ #include <linux/mount.h>
+
+ /*
+--- /dev/null 2003-01-30 03:24:37.000000000 -0700
++++ linux-2.4.18-p4smp-braam/include/linux/gdb.h 2003-07-09 13:15:23.000000000 -0600
+@@ -0,0 +1,63 @@
++#ifndef _GDB_H_
++#define _GDB_H_
++
++/*
++ * Copyright (C) 2001 Amit S. Kale
++ */
++
++/* 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_hook(void);
++extern void breakpoint(void);
++
++typedef int gdb_debug_hook(int trapno,
++ int signo,
++ int err_code,
++ struct pt_regs *regs);
++extern gdb_debug_hook *linux_debug_hook;
++
++extern volatile unsigned kgdb_lock;
++
++extern volatile int kgdb_memerr_expected;
++
++extern volatile unsigned kgdb_step;
++
++struct console;
++void gdb_console_write(struct console *co, const char *s,
++ unsigned count);
++void gdb_console_init(void);
++
++extern volatile int procindebug[KGDB_MAX_NO_CPUS];
++
++void gdb_wait(struct pt_regs *regs);
++
++#define KGDB_ASSERT(message, condition) do { \
++ if (!(condition)) { \
++ printk("kgdb assertion failed: %s\n", message); \
++ asm ("int $0x3"); \
++ } \
++} 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_H_ */
+--- linux-2.4.18-p4smp/include/linux/sched.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/include/linux/sched.h 2003-07-09 13:15:23.000000000 -0600
+@@ -159,7 +159,9 @@ extern int set_user(uid_t new_ruid, int
+
+ #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);
+@@ -1025,6 +1027,15 @@ static inline int need_resched(void)
+ return unlikely(current->need_resched);
+ }
+
++static inline void schedule(void)
++{
++#ifdef CONFIG_KGDB_THREAD
++ kern_schedule();
++#else
++ do_schedule();
++#endif
++}
++
+ #endif /* __KERNEL__ */
+
+ #endif
+--- linux-2.4.18-p4smp/init/main.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/init/main.c 2003-07-09 13:15:23.000000000 -0600
+@@ -57,6 +57,10 @@
+ #include <linux/nubus.h>
+ #endif
+
++#ifdef CONFIG_X86_REMOTE_DEBUG
++#include <linux/gdb.h>
++#endif
++
+ #ifdef CONFIG_ISAPNP
+ #include <linux/isapnp.h>
+ #endif
+@@ -476,6 +480,11 @@ asmlinkage void __init start_kernel(void
+ * make syscalls (and thus be locked).
+ */
+ smp_init();
++#ifdef CONFIG_X86_REMOTE_DEBUG
++ if (gdb_enter) {
++ gdb_hook(); /* right at boot time */
++ }
++#endif
+
+ /* Do the rest non-__init'ed, we're now alive */
+ rest_init();
+--- linux-2.4.18-p4smp/kernel/ksyms.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/kernel/ksyms.c 2003-07-09 13:15:23.000000000 -0600
+@@ -478,7 +478,10 @@ EXPORT_SYMBOL(sleep_on);
+ 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);
+ EXPORT_SYMBOL(sys_sched_yield);
+ EXPORT_SYMBOL(set_user_nice);
+--- linux-2.4.18-p4smp/kernel/sched.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/kernel/sched.c 2003-07-09 13:15:23.000000000 -0600
+@@ -797,7 +797,7 @@ void scheduling_functions_start_here(voi
+ /*
+ * 'schedule()' is the main scheduler function.
+ */
+-asmlinkage void schedule(void)
++asmlinkage void do_schedule(void)
+ {
+ task_t *prev, *next;
+ runqueue_t *rq;
+@@ -879,6 +879,22 @@ switch_tasks:
+ goto need_resched;
+ }
+
++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
+--- linux-2.4.18-p4smp/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600
++++ linux-2.4.18-p4smp-braam/Makefile 2003-07-09 13:15:23.000000000 -0600
+@@ -101,9 +101,13 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH)
+
+ CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \
+ -fno-strict-aliasing -fno-common
++ifeq ($(CONFIG_X86_REMOTE_DEBUG),y)
++CFLAGS += -g
++else
+ ifndef CONFIG_FRAME_POINTER
+ CFLAGS += -fomit-frame-pointer
+ endif
++endif
+ AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
+
+ ifeq ($(CONFIG_MCL_COREDUMP),y)
+
+_