diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/Documentation/Configure.help linux-2.4.22-pa12/Documentation/Configure.help --- linux-2.4.22-pa12.orig/Documentation/Configure.help 2003-10-03 05:01:34.000000000 +0200 +++ linux-2.4.22-pa12/Documentation/Configure.help 2003-10-04 15:56:58.000000000 +0200 @@ -21160,6 +21160,40 @@ 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_PARISC_REMOTE_DEBUG + If you say Y here, it will be possible to remotely debug the parisc + kernel using gdb. This enlarges your kernel image disk size by + several megabytes and requires a machine with more than 32 MB. + To use this feature you need to perform some basic setup described + briefly in Documentation/parisc/kgdb-serial.txt. + Currently only PA32 platforms are supported. + 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 messages 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. + NEVER use this option if you plan to debug gdb remote serial protocol + +KGDB: Enable kernel asserts +CONFIG_KERNEL_ASSERTS + This option enables kernel asserts. A kernel assert is a condition, + which if turnes out to be false during an execution path, then kgdb + is called. Kernel assertions help in modifying the kernel or writing + drivers. Bugs can be traced sooner with kernel asserts because invalid + conditions caused by bugs are checked at various places. Kernel asserts + add the overhead of checking asserted conditions. You can disable this + option if you do not want the overhead. + Magic System Request Key support CONFIG_MAGIC_SYSRQ If you say Y here, you will have some control over the system even diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/Documentation/parisc/gdbrc linux-2.4.22-pa12/Documentation/parisc/gdbrc --- linux-2.4.22-pa12.orig/Documentation/parisc/gdbrc 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-pa12/Documentation/parisc/gdbrc 2003-10-04 15:56:58.000000000 +0200 @@ -0,0 +1,9 @@ +set remotelogfile kgdb.log +set remotebaud 38400 +set debug remote 1 +set debug serial 1 +set debug arch 1 +set prompt (kgdb)\ +set remote Z-packet 0 +target remote /dev/ttyS0 + diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/Documentation/parisc/kgdb-serial.txt linux-2.4.22-pa12/Documentation/parisc/kgdb-serial.txt --- linux-2.4.22-pa12.orig/Documentation/parisc/kgdb-serial.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-pa12/Documentation/parisc/kgdb-serial.txt 2003-10-04 15:56:58.000000000 +0200 @@ -0,0 +1,296 @@ +About the PA/RISC stub of kgdb +============================== +The PA/RISC stub of kgdb was written by Laurent Canet +and Thibaut Varene . +Currently it only works on 32 bits machines, or in 32 bits kernel mode +(there is no 64bits gdb available). +Most of the original KGDB features are available, excepted hardware +assisted debugging and single stepping, which are not (yet) supported +or available. + + +Version +======= + +This version of the gdbstub package was developed and tested on +kernel version 2.4.20. It will not install on a 2.2 kernel. It may +not work on earlier versions of 2.3 kernels. + + +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 IPL command line. The patch defaults to use ttyS0 at a baud rate of +38400. These parameters can be changed by using "gdbttyS=" and +"gdbbaud=" on the command line. + +Example: + +IPL boot command line: 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. + +If you use network booting, be sure to change palo.conf. + + +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/parisc/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. + +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. +More cleanly, you can use the "detach" command + +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/parisc/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. + + +Threads +======= + +Each process in a target machine is seen as a gdb thread. gdb thread related +commands (info threads, thread n) can be used. + +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. + +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. + +(modified by Laurent Canet ) + Modified i386 doc to reflect PA-RISC modifications + diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/Makefile linux-2.4.22-pa12/Makefile --- linux-2.4.22-pa12.orig/Makefile 2003-10-03 05:01:32.000000000 +0200 +++ linux-2.4.22-pa12/Makefile 2003-10-04 15:56:58.000000000 +0200 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 22 -EXTRAVERSION = -pa12 +EXTRAVERSION = -pa12-kgdb KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -112,6 +112,9 @@ CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ -fno-strict-aliasing -fno-common +ifeq ($(CONFIG_PARISC_REMOTE_DEBUG),y) +CFLAGS += -g +endif ifndef CONFIG_FRAME_POINTER CFLAGS += -fomit-frame-pointer endif diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/arch/parisc/config.in linux-2.4.22-pa12/arch/parisc/config.in --- linux-2.4.22-pa12.orig/arch/parisc/config.in 2003-10-03 05:01:48.000000000 +0200 +++ linux-2.4.22-pa12/arch/parisc/config.in 2003-10-04 15:56:58.000000000 +0200 @@ -38,6 +38,7 @@ dep_bool '32-bit PDC' CONFIG_PDC_NARROW $CONFIG_PARISC64 else define_bool CONFIG_PA11 y + define_bool CONFIG_PARISC64 n fi endmenu @@ -205,6 +206,15 @@ bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ bool 'Debug spinlocks' CONFIG_DEBUG_SPINLOCK bool 'Compile kernel with frame pointers' CONFIG_FRAME_POINTER +if [ "$CONFIG_PARISC64" = "n" -a "$CONFIG_GSC" = "y" -a "$CONFIG_SUPERIO" = "n" ]; then + bool 'KGDB: Remote (serial) kernel debugging with gdb' CONFIG_PARISC_REMOTE_DEBUG + if [ "$CONFIG_PARISC_REMOTE_DEBUG" != "n" ]; then + bool ' KGDB: Console messages through gdb' CONFIG_GDB_CONSOLE + bool ' KGDB: Thread analysis' CONFIG_KGDB_THREAD + bool ' KGDB: Enable kernel asserts' CONFIG_KERNEL_ASSERTS + bool ' KGDB: Add a /proc/bugme for SEGV handling presentation' CONFIG_PROC_BUGME + fi +fi endmenu source crypto/Config.in diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/arch/parisc/kernel/Makefile linux-2.4.22-pa12/arch/parisc/kernel/Makefile --- linux-2.4.22-pa12.orig/arch/parisc/kernel/Makefile 2002-10-03 17:33:29.000000000 +0200 +++ linux-2.4.22-pa12/arch/parisc/kernel/Makefile 2003-10-04 15:56:58.000000000 +0200 @@ -7,10 +7,20 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... +ifeq ($(CONFIG_PARISC_REMOTE_DEBUG),y) +#GDBSTART=gdbstart +# KGDB XXX: gdbstart is not yet on plans +GDBSTART= +GDBCLEAN= -rm -f gdbstart /sbin/gdbstart +else +GDBSTART= +GDBCLEAN= +endif + ifdef CONFIG_PARISC64 all: kernel.o init_task.o pdc_cons.o process.o head64.o unaligned.o perf.o perf_asm.o else -all: kernel.o init_task.o pdc_cons.o process.o head.o unaligned.o +all: kernel.o init_task.o pdc_cons.o process.o head.o unaligned.o $(GDBSTART) endif O_TARGET = kernel.o @@ -47,5 +57,6 @@ ioctl32.o signal32.o # only supported for PCX-W/U in 64-bit mode at the moment obj-$(CONFIG_PARISC64) += perf.o perf_asm.o +obj-$(CONFIG_PARISC_REMOTE_DEBUG) += gdbstub.o include $(TOPDIR)/Rules.make diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/arch/parisc/kernel/entry.S linux-2.4.22-pa12/arch/parisc/kernel/entry.S --- linux-2.4.22-pa12.orig/arch/parisc/kernel/entry.S 2003-09-25 05:01:45.000000000 +0200 +++ linux-2.4.22-pa12/arch/parisc/kernel/entry.S 2003-10-04 15:56:58.000000000 +0200 @@ -766,7 +766,7 @@ b do_softirq ldo R%intr_check_resched(%r2), %r2 - .import schedule,code + .import user_schedule,code intr_do_resched: /* Only do reschedule if we are returning to user space */ LDREG PT_IASQ0(%r16), %r20 @@ -781,7 +781,7 @@ #endif ldil L%intr_check_sig, %r2 - b schedule + b user_schedule ldo R%intr_check_sig(%r2), %r2 @@ -2401,9 +2401,9 @@ b syscall_check_resched ssm PSW_SM_I, %r0 /* do_softirq returns with I bit off */ - .import schedule,code + .import user_schedule,code syscall_do_resched: - bl schedule,%r2 + bl user_schedule,%r2 #ifdef __LP64__ ldo -16(%r30),%r29 /* Reference param save area */ #else diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/arch/parisc/kernel/gdbstart.c linux-2.4.22-pa12/arch/parisc/kernel/gdbstart.c --- linux-2.4.22-pa12.orig/arch/parisc/kernel/gdbstart.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-pa12/arch/parisc/kernel/gdbstart.c 2003-10-04 15:56:59.000000000 +0200 @@ -0,0 +1,148 @@ +/* + * This program opens a tty file and issues the GDB stub activating + * ioctl on it. + * + * KGDB XXX: testing needed, add a build command in the makefile + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 -EbBNaur -x CVS linux-2.4.22-pa12.orig/arch/parisc/kernel/gdbstub.c linux-2.4.22-pa12/arch/parisc/kernel/gdbstub.c --- linux-2.4.22-pa12.orig/arch/parisc/kernel/gdbstub.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-pa12/arch/parisc/kernel/gdbstub.c 2003-10-04 15:56:59.000000000 +0200 @@ -0,0 +1,1237 @@ +/* + * KGDB stubs for hppa. + * + * Copyright (c) 2003 Laurent Canet + * Copyright (c) 2003 Thibaut Varene + * + * 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. + * + * KGDB XXX: + * - hardware breakpoints (at least see if we can have the debug sfu + * working) + * - test SMP locks + * - see if we can use single stepping + * - a 64 bit KGDB ! :) + */ + +/* + * Copyright (C) 2000-2001 VERITAS Software Corporation. + */ +/* + * To enable debugger support, two things need to happen. + * One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a break instruction. + * + ************* + * command function Return value + * + * The following gdb commands are supported: + * + * 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 + * + * ? What was the last sigval ? SNN (signal NN) + * + * O Used by GDB console (reports printk + * to the remote gdb) + * + * The following gdb commands are not yet supported because we don't have + * threads supports: + * + * k kill + * Hct switch to thread t OK or eNN + * qL list threads + * + * The following gdb commands are NOT supported due to lack of hardware + * support or because it won't work + * + * z/Z set/remove hardware {break,watch}points + * F call system call + * + * 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 + +#include +#include /* for linux pt_regs struct */ +#include + +#ifdef CONFIG_GDB_CONSOLE +#include +#endif + + +/* asm macros */ +#define MEMB(no) { int __mi = no; while (--__mi) __asm__ volatile (""::: "memory"); } + +#define BREAKPOINT() asm("break 5,8"); + +/* + * 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 4096 + +/* Number of bytes of registers. */ +#define NUMREGS 128 +#define NUMREGBYTES NUMREGS*4 +/* masks for 64 bit registers */ +#define MASK_32LOW 0x00000000ffffffff +#define MASK_32HIGH 0xffffffff00000000 + +#undef KGDB_HPPA_HAVE_HWBREAKPOINT + +/* Thread reference */ +typedef unsigned char threadref[8]; + +static const char hexchars[] = "0123456789abcdef"; + +/* + * 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. + * + * FIXME: This image is from gdb521, check for gdb5.3 + */ +enum gdb_regpacket { + flags, r1, rp, r3, r4, r5, r6, r7, + r8, r9, r10, r11, r12, r13, r14, r15, + r16, r17, r18, r19, r20, r21, r22, r23, + r24, r25, r26, dp, ret0, ret1, sp, r31, + sar, pcoqh, pcsqh, pcoqt, pcsqt, eiem, iir, isr, + ior, ipsw, gotoreg, sr4, sr0, sr1, sr2, sr3, + sr5, sr6, sr7, cr0, cr8, cr9, ccr, cr12, + cr13, cr24, cr25, cr26, mpsfu_high,mpsfu_low,mpsfu_ovflo,pad, + fpsr, fpe1, fpe2, fpe3, fpe4, fpe5, fpe6, fpe7, + fr4, fr4R, fr5, fr5R, fr6, fr6R, fr7, fr7R, + fr8, fr8R, fr9, fr9R, fr10, fr10R, fr11, fr11R, + fr12, fr12R, fr13, fr13R, fr14, fr14R, fr15, fr15R, + fr16, fr16R, fr17, fr17R, fr18, fr18R, fr19, fr19R, + fr20, fr20R, fr21, fr21R, fr22, fr22R, fr23, fr23R, + fr24, fr24R, fr25, fr25R, fr26, fr26R, fr27, fr27R, + fr28, fr28R, fr29, fr29R, fr30, fr30R, fr31, fr31R +}; + + +/* ***************** + * Global variables + * *****************/ +/* Put the error code here just in case the user cares. */ + +int gdb_arg_enter = 0; /* do not enter by default */ +int gdb_arg_baud = 38400; +int gdb_arg_ttyS = 0; +int gdb_arg_gdbcons = 0; /* do not use the gdbconsole */ + +#ifdef CONFIG_SMP +#error kgdb does not yet support SMP systems +#endif + +#if KGDB_MAX_NO_CPUS != 8 +#error change the initialisation of slavecpulocks +#endif + +static spinlock_t slavecpulocks[KGDB_MAX_NO_CPUS]; +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_lock = 0; + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; +static char initialized = 0; /* boolean flag. != 0 means we've been initialized */ + +/* 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; + +/* We don't have hardware breakpoints on the currently supported hw. + * FIXME: find a better way to initialize them eventually. + */ +#ifdef KGDB_HPPA_HAVE_HWBREAKPOINT +static struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned addr; +} +breakinfo[8] = { +{enabled:0},{enabled:0},{enabled:0},{enabled:0},{enabled:0},{enabled:0},{enabled:0},{enabled:0} +}; +#endif + + +/* Those two are in driver/char/gdbserial.c */ +extern int putDebugChar (int); /* write a single character */ +extern int getDebugChar (void); /* read and return a single char */ + + +/* Returns the integer equivalent of a hexadecimal character. */ +static 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); +} + +/* Awaits the sequence $# and stores in the array buffer returned */ +static 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. */ + +/* Send $# from the in the array buffer. */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + 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 void regs_to_gdb_regs (int *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[flags] = regs->gr[0]; + gdb_regs[r1] = regs->gr[1]; + gdb_regs[rp] = regs->gr[2]; + gdb_regs[r3] = regs->gr[3]; + gdb_regs[r4] = regs->gr[4]; + gdb_regs[r5] = regs->gr[5]; + gdb_regs[r6] = regs->gr[6]; + gdb_regs[r7] = regs->gr[7]; + gdb_regs[r8] = regs->gr[8]; + gdb_regs[r9] = regs->gr[9]; + gdb_regs[r10] = regs->gr[10]; + gdb_regs[r11] = regs->gr[11]; + gdb_regs[r12] = regs->gr[12]; + gdb_regs[r13] = regs->gr[13]; + gdb_regs[r14] = regs->gr[14]; + gdb_regs[r15] = regs->gr[15]; + gdb_regs[r16] = regs->gr[16]; + gdb_regs[r17] = regs->gr[17]; + gdb_regs[r18] = regs->gr[18]; + gdb_regs[r19] = regs->gr[19]; + gdb_regs[r20] = regs->gr[20]; + gdb_regs[r21] = regs->gr[21]; + gdb_regs[r22] = regs->gr[22]; + gdb_regs[r23] = regs->gr[23]; + gdb_regs[r24] = regs->gr[24]; + gdb_regs[r25] = regs->gr[25]; + gdb_regs[r26] = regs->gr[26]; + gdb_regs[dp] = regs->gr[27]; + gdb_regs[ret0] = regs->gr[28]; + gdb_regs[ret1] = regs->gr[29]; + gdb_regs[sp] = regs->gr[30]; + gdb_regs[r31] = regs->gr[31]; + gdb_regs[sar] = regs->sar; + gdb_regs[pcoqh] = regs->iaoq[0]; + gdb_regs[pcsqh] = regs->iasq[0]; + gdb_regs[pcoqt] = regs->iaoq[1]; + gdb_regs[pcsqh] = regs->iasq[1]; + gdb_regs[eiem] = 0; /* eiem is cr15, not in pt_regs */ + gdb_regs[iir] = regs->iir; + gdb_regs[isr] = regs->isr; + gdb_regs[ior] = regs->ior; + gdb_regs[ipsw] = regs->ipsw; + gdb_regs[gotoreg] = 0; /* wtf is goto register in gdb ? */ + gdb_regs[sr4] = regs->sr[4]; + gdb_regs[sr0] = regs->sr[0]; + gdb_regs[sr1] = regs->sr[1]; + gdb_regs[sr2] = regs->sr[2]; + gdb_regs[sr3] = regs->sr[3]; + gdb_regs[sr5] = regs->sr[5]; + gdb_regs[sr6] = regs->sr[6]; + gdb_regs[sr7] = regs->sr[7]; + gdb_regs[cr0] = 0; /* not available in pt_regs */ + gdb_regs[cr8] = 0; /* not available in pt_regs */ + gdb_regs[cr9] = 0; /* not available in pt_regs */ + gdb_regs[ccr] = 0; /* not available in pt_regs */ + gdb_regs[cr12] = 0; /* not available in pt_regs */ + gdb_regs[cr13] = 0; /* not available in pt_regs */ + gdb_regs[cr24] = 0; /* not available in pt_regs */ + gdb_regs[cr26] = 0; /* not available in pt_regs */ + gdb_regs[mpsfu_high] = 0; /* not available in pt_regs */ + gdb_regs[mpsfu_low] = 0; /* not available in pt_regs */ + gdb_regs[mpsfu_ovflo] = 0; /* not available in pt_regs */ + gdb_regs[pad] = regs->pad0; + gdb_regs[fpsr] = (__u32) (regs->fr[0] & MASK_32LOW); + gdb_regs[fpe1] = 0; /* not used according to PA1.1doc */ + gdb_regs[fpe2] = 0; /* not used according to PA1.1doc */ + gdb_regs[fpe3] = 0; /* not used according to PA1.1doc */ + gdb_regs[fpe4] = 0; /* not used according to PA1.1doc */ + gdb_regs[fpe5] = 0; /* not used according to PA1.1doc */ + gdb_regs[fpe6] = 0; /* not used according to PA1.1doc */ + gdb_regs[fpe7] = 0; /* not used according to PA1.1doc */ + + /* floating point registers are in 64bit format */ + gdb_regs[fr4] = (__u32) (regs->fr[4] & MASK_32LOW); + gdb_regs[fr4R] = (__u32) (regs->fr[4] & MASK_32HIGH); + gdb_regs[fr5] = (__u32) (regs->fr[5] & MASK_32LOW); + gdb_regs[fr5R] = (__u32) (regs->fr[5] & MASK_32HIGH); + gdb_regs[fr6] = (__u32) (regs->fr[6] & MASK_32LOW); + gdb_regs[fr6R] = (__u32) (regs->fr[6] & MASK_32HIGH); + gdb_regs[fr7] = (__u32) (regs->fr[7] & MASK_32LOW); + gdb_regs[fr7R] = (__u32) (regs->fr[7] & MASK_32HIGH); + gdb_regs[fr8] = (__u32) (regs->fr[8] & MASK_32LOW); + gdb_regs[fr8R] = (__u32) (regs->fr[8] & MASK_32HIGH); + gdb_regs[fr9] = (__u32) (regs->fr[9] & MASK_32LOW); + gdb_regs[fr9R] = (__u32) (regs->fr[9] & MASK_32HIGH); + gdb_regs[fr10] = (__u32) (regs->fr[10] & MASK_32LOW); + gdb_regs[fr10R] = (__u32) (regs->fr[10] & MASK_32HIGH); + gdb_regs[fr11] = (__u32) (regs->fr[11] & MASK_32LOW); + gdb_regs[fr11R] = (__u32) (regs->fr[11] & MASK_32HIGH); + gdb_regs[fr12] = (__u32) (regs->fr[12] & MASK_32LOW); + gdb_regs[fr12R] = (__u32) (regs->fr[12] & MASK_32HIGH); + gdb_regs[fr13] = (__u32) (regs->fr[13] & MASK_32LOW); + gdb_regs[fr13R] = (__u32) (regs->fr[13] & MASK_32HIGH); + gdb_regs[fr14] = (__u32) (regs->fr[14] & MASK_32LOW); + gdb_regs[fr14R] = (__u32) (regs->fr[14] & MASK_32HIGH); + gdb_regs[fr15] = (__u32) (regs->fr[15] & MASK_32LOW); + gdb_regs[fr15R] = (__u32) (regs->fr[15] & MASK_32HIGH); + gdb_regs[fr16] = (__u32) (regs->fr[16] & MASK_32LOW); + gdb_regs[fr16R] = (__u32) (regs->fr[16] & MASK_32HIGH); + gdb_regs[fr17] = (__u32) (regs->fr[17] & MASK_32LOW); + gdb_regs[fr17R] = (__u32) (regs->fr[17] & MASK_32HIGH); + gdb_regs[fr18] = (__u32) (regs->fr[18] & MASK_32LOW); + gdb_regs[fr18R] = (__u32) (regs->fr[18] & MASK_32HIGH); + gdb_regs[fr19] = (__u32) (regs->fr[19] & MASK_32LOW); + gdb_regs[fr19R] = (__u32) (regs->fr[19] & MASK_32HIGH); + gdb_regs[fr20] = (__u32) (regs->fr[20] & MASK_32LOW); + gdb_regs[fr20R] = (__u32) (regs->fr[20] & MASK_32HIGH); + gdb_regs[fr21] = (__u32) (regs->fr[21] & MASK_32LOW); + gdb_regs[fr21R] = (__u32) (regs->fr[21] & MASK_32HIGH); + gdb_regs[fr22] = (__u32) (regs->fr[22] & MASK_32LOW); + gdb_regs[fr22R] = (__u32) (regs->fr[22] & MASK_32HIGH); + gdb_regs[fr23] = (__u32) (regs->fr[23] & MASK_32LOW); + gdb_regs[fr23R] = (__u32) (regs->fr[23] & MASK_32HIGH); + gdb_regs[fr24] = (__u32) (regs->fr[24] & MASK_32LOW); + gdb_regs[fr24R] = (__u32) (regs->fr[24] & MASK_32HIGH); + gdb_regs[fr25] = (__u32) (regs->fr[25] & MASK_32LOW); + gdb_regs[fr25R] = (__u32) (regs->fr[25] & MASK_32HIGH); + gdb_regs[fr26] = (__u32) (regs->fr[26] & MASK_32LOW); + gdb_regs[fr26R] = (__u32) (regs->fr[26] & MASK_32HIGH); + gdb_regs[fr27] = (__u32) (regs->fr[27] & MASK_32LOW); + gdb_regs[fr27R] = (__u32) (regs->fr[27] & MASK_32HIGH); + gdb_regs[fr28] = (__u32) (regs->fr[28] & MASK_32LOW); + gdb_regs[fr28R] = (__u32) (regs->fr[28] & MASK_32HIGH); + gdb_regs[fr29] = (__u32) (regs->fr[29] & MASK_32LOW); + gdb_regs[fr29R] = (__u32) (regs->fr[29] & MASK_32HIGH); + gdb_regs[fr30] = (__u32) (regs->fr[30] & MASK_32LOW); + gdb_regs[fr30R] = (__u32) (regs->fr[30] & MASK_32HIGH); + gdb_regs[fr31] = (__u32) (regs->fr[31] & MASK_32LOW); + gdb_regs[fr31R] = (__u32) (regs->fr[31] & MASK_32HIGH); +} /* regs_to_gdb_regs */ + +static void gdb_regs_to_regs (int *gdb_regs, struct pt_regs *regs) +{ + regs->gr[0] = gdb_regs[flags]; + regs->gr[1] = gdb_regs[r1]; + regs->gr[2] = gdb_regs[rp]; + regs->gr[3] = gdb_regs[r3]; + regs->gr[4] = gdb_regs[r4]; + regs->gr[5] = gdb_regs[r5]; + regs->gr[6] = gdb_regs[r6]; + regs->gr[7] = gdb_regs[r7]; + regs->gr[8] = gdb_regs[r8]; + regs->gr[9] = gdb_regs[r9]; + regs->gr[10] = gdb_regs[r10]; + regs->gr[11] = gdb_regs[r11]; + regs->gr[12] = gdb_regs[r12]; + regs->gr[13] = gdb_regs[r13]; + regs->gr[14] = gdb_regs[r14]; + regs->gr[15] = gdb_regs[r15]; + regs->gr[16] = gdb_regs[r16]; + regs->gr[17] = gdb_regs[r17]; + regs->gr[18] = gdb_regs[r18]; + regs->gr[19] = gdb_regs[r19]; + regs->gr[20] = gdb_regs[r20]; + regs->gr[21] = gdb_regs[r21]; + regs->gr[22] = gdb_regs[r22]; + regs->gr[23] = gdb_regs[r23]; + regs->gr[24] = gdb_regs[r24]; + regs->gr[25] = gdb_regs[r25]; + regs->gr[26] = gdb_regs[r26]; + regs->gr[27] = gdb_regs[dp]; + regs->gr[28] = gdb_regs[ret0]; + regs->gr[29] = gdb_regs[ret1]; + regs->gr[30] = gdb_regs[sp]; + regs->gr[31] = gdb_regs[r31]; + regs->sar = gdb_regs[sar]; + regs->iaoq[0] = gdb_regs[pcoqh]; + regs->iasq[0] = gdb_regs[pcsqh]; + regs->iaoq[1] = gdb_regs[pcoqt]; + regs->iasq[1] = gdb_regs[pcsqt]; + regs->iir = gdb_regs[iir]; + regs->isr = gdb_regs[isr]; + regs->ior = gdb_regs[ior]; + regs->ipsw = gdb_regs[ipsw]; + regs->sr[4] = gdb_regs[sr4]; + regs->sr[0] = gdb_regs[sr0]; + regs->sr[1] = gdb_regs[sr1]; + regs->sr[2] = gdb_regs[sr2]; + regs->sr[3] = gdb_regs[sr3]; + regs->sr[5] = gdb_regs[sr5]; + regs->sr[6] = gdb_regs[sr6]; + regs->sr[7] = gdb_regs[sr7]; + regs->pad0 = gdb_regs[pad]; + regs->fr[0] |= gdb_regs[fpsr]; + + /* floating point registers are in 64bit format */ + regs->fr[4] = (__u64) gdb_regs[fr4]; + regs->fr[4] |= ((__u64) gdb_regs[fr4R]) << 32; + regs->fr[5] = (__u64) gdb_regs[fr5]; + regs->fr[5] |= ((__u64) gdb_regs[fr5R]) << 32; + regs->fr[6] = (__u64) gdb_regs[fr6]; + regs->fr[6] |= ((__u64) gdb_regs[fr6R]) << 32; + regs->fr[7] = (__u64) gdb_regs[fr7]; + regs->fr[7] |= ((__u64) gdb_regs[fr7R]) << 32; + regs->fr[8] = (__u64) gdb_regs[fr8]; + regs->fr[8] |= ((__u64) gdb_regs[fr8R]) << 32; + regs->fr[9] = (__u64) gdb_regs[fr9]; + regs->fr[9] |= ((__u64) gdb_regs[fr9R]) << 32; + regs->fr[10] = (__u64) gdb_regs[fr10]; + regs->fr[10] |= ((__u64) gdb_regs[fr10R]) << 32; + regs->fr[11] = (__u64) gdb_regs[fr11]; + regs->fr[11] |= ((__u64) gdb_regs[fr11R]) << 32; + regs->fr[12] = (__u64) gdb_regs[fr12]; + regs->fr[12] |= ((__u64) gdb_regs[fr12R]) << 32; + regs->fr[13] = (__u64) gdb_regs[fr13]; + regs->fr[13] |= ((__u64) gdb_regs[fr13R]) << 32; + regs->fr[14] = (__u64) gdb_regs[fr14]; + regs->fr[14] |= ((__u64) gdb_regs[fr14R]) << 32; + regs->fr[15] = (__u64) gdb_regs[fr15]; + regs->fr[15] |= ((__u64) gdb_regs[fr15R]) << 32; + regs->fr[16] = (__u64) gdb_regs[fr16]; + regs->fr[16] |= ((__u64) gdb_regs[fr16R]) << 32; + regs->fr[17] = (__u64) gdb_regs[fr17]; + regs->fr[17] |= ((__u64) gdb_regs[fr17R]) << 32; + regs->fr[18] = (__u64) gdb_regs[fr18]; + regs->fr[18] |= ((__u64) gdb_regs[fr18R]) << 32; + regs->fr[19] = (__u64) gdb_regs[fr19]; + regs->fr[19] |= ((__u64) gdb_regs[fr19R]) << 32; + regs->fr[20] = (__u64) gdb_regs[fr20]; + regs->fr[20] |= ((__u64) gdb_regs[fr20R]) << 32; + regs->fr[21] = (__u64) gdb_regs[fr21]; + regs->fr[21] |= ((__u64) gdb_regs[fr21R]) << 32; + regs->fr[22] = (__u64) gdb_regs[fr22]; + regs->fr[22] |= ((__u64) gdb_regs[fr22R]) << 32; + regs->fr[23] = (__u64) gdb_regs[fr23]; + regs->fr[23] |= ((__u64) gdb_regs[fr23R]) << 32; + regs->fr[24] = (__u64) gdb_regs[fr24]; + regs->fr[24] |= ((__u64) gdb_regs[fr24R]) << 32; + regs->fr[25] = (__u64) gdb_regs[fr25]; + regs->fr[25] |= ((__u64) gdb_regs[fr25R]) << 32; + regs->fr[26] = (__u64) gdb_regs[fr26]; + regs->fr[26] |= ((__u64) gdb_regs[fr26R]) << 32; + regs->fr[27] = (__u64) gdb_regs[fr27]; + regs->fr[27] |= ((__u64) gdb_regs[fr27R]) << 32; + regs->fr[28] = (__u64) gdb_regs[fr28]; + regs->fr[28] |= ((__u64) gdb_regs[fr28R]) << 32; + regs->fr[29] = (__u64) gdb_regs[fr29]; + regs->fr[29] |= ((__u64) gdb_regs[fr29R]) << 32; + regs->fr[30] = (__u64) gdb_regs[fr30]; + regs->fr[30] |= ((__u64) gdb_regs[fr30R]) << 32; + regs->fr[31] = (__u64) gdb_regs[fr31]; + regs->fr[31] |= ((__u64) gdb_regs[fr31R]) << 32; +} /* gdb_regs_to_regs */ + + +static int get_char (char *addr) +{ + return *addr; +} + +static void set_char (char *addr, int val) +{ + *addr = val; +} + +/* 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. + */ +static 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; + } + + /* don't enter in mem trapping if we know it's bad */ + if (mem == NULL) { + *buf = 0; + return buf; + } + + 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 + */ +static 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; +} + +/* build an int from hex chars */ +static 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); +} + +#if defined(CONFIG_KGDB_THREAD) || defined(CONFIG_GDB_CONSOLE) +static char * pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} +#endif + +#ifdef CONFIG_KGDB_THREAD +/* needed to get a valid code adress */ +static void kgdb_usercode(void) +{ +} + +static int stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = hex (*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} + +#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 = hex(*inbuf++); + y = hex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} + +static 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 pid) +{ + struct task_struct *thread; + thread = find_task_by_pid(pid); + if (thread) + return thread; + + thread = init_tasks[0]; + do { + if (thread->pid == pid) { + return thread; + } + thread = thread->next_task; + } while (thread != init_tasks[0]); + + kgdb_dprintk(KERN_ERR "Didn't found thread id %d\n", pid); + + return NULL; +} +#endif /* CONFIG_KGDB_THREAD */ + +/* these functions will be only useful with hardware breakpoints + */ +#ifdef KGDB_HPPA_HAVE_HWBREAKPOINT +static void correct_hw_break (void) +{ + /* should be long asm code */ + kgdb_dprintk(KERN_ERR "Correct HW break\n"); +} + +static int remove_hw_break (unsigned breakno) +{ + if (!breakinfo[breakno].enabled) + return -1; + + breakinfo[breakno].enabled = 0; + return 0; +} + +static 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; + + kgdb_dprintk(KERN_INFO "Set hw break %u: %u %u %u\n", breakno, type, + len, addr); + return 0; +} +#endif /* KGDB_HAVE_HWBREAKPOINT */ + +static void printexceptioninfo (int exceptionNo, int errorcode, char *buffer) +{ + switch (exceptionNo) { + case KGDB_TRAP_BKPT: /* gdb break insn (break 4,8) */ + sprintf(buffer, "GDB Breakpoint"); + break; + case KGDB_TRAP_DIE: /* die_if_kernel(); */ + sprintf(buffer, "die_if_kernel() err=%d", errorcode); + break; + case KGDB_TRAP_BREAK: /* breakpoint (break 5,8) */ + sprintf(buffer, "forced break or kernel assertion failed"); + break; + case KGDB_TRAP_PAGEFAULT: /* TLB page errors in kernel space */ + sprintf(buffer, "page fault"); + break; + default: + sprintf(buffer, "break/trap unknown"); + break; + } + + return; +} + +/* This function does all command procesing for interfacing to gdb. */ +static void handle_exception (int exception, int signo, int err_code, + struct pt_regs *linux_regs) +{ + struct task_struct *usethread = NULL; + int addr, length; + char *ptr; + unsigned long flags; + int gdb_regs[NUMREGBYTES / 4]; + int i; +#ifdef CONFIG_KGDB_THREAD + struct pt_regs *tempregs; + int nothreads; + int maxthreads; + int threadid; + threadref thref; + struct task_struct *thread = NULL; +#endif + unsigned procid; + + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if (user_mode(linux_regs)) + return; + + kgdb_dprintk(KERN_ERR "Entering kgdb because of excep=%d signo=%d err=%d\n", + exception, signo, err_code); + + if (kgdb_memerr_expected) { + kgdb_dprintk(KERN_ERR "kgdb: memerr!\n"); + /* + * 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 */ + return; + } + /* Hold kgdb_lock */ + procid = smp_processor_id(); + while (cmpxchg (&kgdb_lock, 0, (procid + 1)) != 0) + MEMB(50); + + local_irq_save(flags); + + /* Disable hardware debugging while we are in kgdb */ + /* KGDB TODO */ + + 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 */ + + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + printk("...(%s)...", remcomOutBuffer); + putpacket (remcomOutBuffer); + + while (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, linux_regs); +#ifdef CONFIG_KGDB_THREAD + else { + memset(gdb_regs, 0, NUMREGBYTES); + if (usethread->thread.gotregs) { + printk(KERN_ERR "Gotregs!\n"); + kgdb_memerr = 0; + kgdb_memerr_expected = 1; + get_char((char *) &usethread->thread.regs); + kgdb_memerr_expected = 0; + if (kgdb_memerr) { + *(((char *)&tempregs) + i) = '\0'; + gdb_regs[pcoqt] = (unsigned long) &kgdb_usercode; + gdb_regs[pcoqh] = gdb_regs[pcoqt] + 4; + } else { + regs_to_gdb_regs(gdb_regs, &usethread->thread.regs); + } + } else { + gdb_regs[pcoqt] = (unsigned long) &kgdb_usercode; + gdb_regs[pcoqh] = gdb_regs[pcoqt] + 4; + } + } +#endif /* CONFIG_KGDB_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 (!usethread || usethread == current) { + gdb_regs_to_regs (gdb_regs, linux_regs); + 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) + && (*(ptr++) == ',') + && hexToInt(&ptr, &length)) { + kgdb_dprintk (KERN_ERR "transferring mem add=%lx - %lx\n", + (unsigned long) addr, (unsigned long) addr + 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++) == ':')) { + kgdb_dprintk (KERN_ERR "Writing to memory %lx - %lx\n", + (unsigned long) ptr, (unsigned long) ptr + length); + 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)) + linux_regs->iaoq[0] = addr; + + /* goodbye' */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer,"+"); + putpacket(remcomOutBuffer); + } + + procindebug[smp_processor_id()] = 0; + + /* Wait till all the processors have quit + * from the debugger */ + for (i = 0; i < smp_num_cpus; i++) + while (procindebug[i]) + MEMB(10); + + /* Release kgdb_lock */ + while (cmpxchg (&kgdb_lock, 1, 0) != 1) + MEMB(50); + + + local_irq_restore (flags); + return; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* General querys */ + 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 /* CONFIG_KGDB_THREAD */ + case 'E': + /* Print exception info */ + printexceptioninfo (exception, err_code, remcomOutBuffer); + break; + case 'R': + /* commands */ + if (strncmp(&remcomInBuffer[2], "cmd,7265626f6f74", 17) == 0) { + printk(KERN_ERR "Remote debugger asked reboot!\n"); + + /* reply before rebooting */ + strcpy (remcomOutBuffer, "OK"); + putpacket (remcomOutBuffer); + + machine_restart(NULL); + } + 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 /* CONFIG_KGDB_THREAD */ + + } /* switch */ + + /* reply to the request */ + putpacket (remcomOutBuffer); + } /* while */ +} + +/* this function is used to set up exception handlers for tracing and + breakpoints */ +void set_debug_traps (void) +{ + /* + * linux_debug_hook is defined in traps.c. We store a pointer + * to our own exception handler into it. + */ + linux_debug_hook = handle_exception; + + /* + * 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. Called by init/main.c */ + +void breakpoint () +{ + if (initialized) + BREAKPOINT (); +} + +#ifdef CONFIG_GDB_CONSOLE +/* KGDB XXX : check if the buffer is large enough to contain console printk's */ +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); + } +} + +static int __init kgdb_opt_gdbconsole (char *str) +{ + gdb_arg_gdbcons = 1; + return 1; +} +#endif /* CONFIG_GDB_CONSOLE */ + +static int __init kgdb_opt_gdb (char *str) +{ + gdb_arg_enter = 1; + return 1; +} + +static int __init kgdb_opt_gdbttyS (char *str) +{ + gdb_arg_ttyS = simple_strtoul (str, NULL, 10); + return 1; +} + +static int __init kgdb_opt_gdbbaud (char *str) +{ + gdb_arg_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); +#ifdef CONFIG_GDB_CONSOLE +__setup ("gdbcons", kgdb_opt_gdbconsole); +#endif +__setup ("gdbbaud=", kgdb_opt_gdbbaud); +__setup ("gdb", kgdb_opt_gdb); + + + diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/arch/parisc/kernel/traps.c linux-2.4.22-pa12/arch/parisc/kernel/traps.c --- linux-2.4.22-pa12.orig/arch/parisc/kernel/traps.c 2002-12-18 09:19:14.000000000 +0100 +++ linux-2.4.22-pa12/arch/parisc/kernel/traps.c 2003-10-04 15:56:59.000000000 +0200 @@ -10,6 +10,11 @@ * state in 'asm.s'. */ +/* KGDB XXX: + * - check if kGDB could possibly do unaligned access. If so, put CHK_REMOTE + * _DEBUG where it happens. + */ + #include #include #include @@ -26,6 +31,10 @@ #include #include +#ifdef CONFIG_PARISC_REMOTE_DEBUG +#include +#endif + #include #include #include @@ -39,6 +48,20 @@ #include "../math-emu/math-emu.h" /* for handle_fpe() */ +#ifdef CONFIG_PARISC_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 /* CONFIG_PARISC_REMOTE_DEBUG */ + #define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */ /* dumped to the console via printk) */ @@ -253,8 +276,13 @@ return syscall(regs); } -/* gdb uses break 4,8 */ +/* gdb uses break 4,8 + * kgdb uses break 5,8 + */ + #define GDB_BREAK_INSN 0x10004 +#define KGDB_BREAK_INSN 0x10005 + void handle_gdb_break(struct pt_regs *regs, int wot) { struct siginfo si; @@ -266,6 +294,27 @@ force_sig_info(SIGTRAP, &si, current); } +static void handle_kgdb_break(struct pt_regs *regs, int why) +{ +#ifdef CONFIG_PARISC_REMOTE_DEBUG + /* userland, stay off! */ + if (user_mode(regs)) + return; + + switch (why) { + case GDB_BREAK_INSN: + CHK_REMOTE_DEBUG(KGDB_TRAP_BKPT, SIGTRAP, 0, regs, INCREASE_IAOQ(regs)); + break; + case KGDB_BREAK_INSN: + CHK_REMOTE_DEBUG(KGDB_TRAP_BREAK, SIGTRAP, 0, regs, INCREASE_IAOQ(regs)); + break; + } +#else + printk(KERN_ERR "Attempt to handle breakpoint without KGDB stubs!\n"); + die_if_kernel("Breakpoint", regs, 0); +#endif /* CONFIG_PARISC_REMOTE_DEBUG */ +} + void handle_break(unsigned iir, struct pt_regs *regs) { struct siginfo si; @@ -286,9 +335,23 @@ force_sig_info(SIGTRAP, &si, current); break; + /* this break was caused by a break 5, 8 instruction : this + * is the code used by kGDB to recognize fixed source-breakpoints + * (in kernel faults or init). In this case, we jump into the debugger + * handle_exception routine and increment IAOQ after. + */ + case KGDB_BREAK_INSN: + if (user_mode(regs)) + printk(KERN_DEBUG "KGDB Hook in user mode\n"); + else + handle_kgdb_break(regs, iir); + break; + case GDB_BREAK_INSN: - die_if_kernel("Breakpoint", regs, 0); + if (user_mode(regs)) handle_gdb_break(regs, TRAP_BRKPT); + else + handle_kgdb_break(regs, iir); break; default: @@ -712,16 +775,21 @@ /* * The kernel should never fault on its own address space. */ - if (fault_space == 0) { + CHK_REMOTE_DEBUG(KGDB_TRAP_PAGEFAULT,SIGSEGV,0,regs,goto skip_trap); pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC); parisc_terminate("Kernel Fault", regs, code, fault_address); - /** NOT REACHED **/ } } local_irq_enable(); do_page_fault(regs, code, fault_address); + return; + + /* skip page fault messages & terminare when kGDB is active */ +skip_trap: + INCREASE_IAOQ(regs); + local_irq_enable(); } diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/arch/parisc/mm/fault.c linux-2.4.22-pa12/arch/parisc/mm/fault.c --- linux-2.4.22-pa12.orig/arch/parisc/mm/fault.c 2002-11-04 00:18:19.000000000 +0100 +++ linux-2.4.22-pa12/arch/parisc/mm/fault.c 2003-10-04 15:56:59.000000000 +0200 @@ -16,6 +16,10 @@ #include #include +#ifdef CONFIG_PARISC_REMOTE_DEBUG +#include +#endif + #include #include @@ -155,6 +159,18 @@ vma = find_vma_prev(mm, address, &prev_vma); if (!vma || address < vma->vm_start) goto check_expansion; + +#ifdef CONFIG_PARISC_REMOTE_DEBUG + if (kgdb_memerr_expected) { + printk(KERN_WARNING "memerr in do_page_fault\n"); + if (linux_debug_hook != (gdb_debug_hook *) NULL) { + (*linux_debug_hook)(KGDB_TRAP_PAGEFAULT, SIGSEGV, code, regs) ; + INCREASE_IAOQ(regs); + return; /* return w/modified regs */ + } + } +#endif + /* * Ok, we have a good vm_area for this memory access. We still need to * check the access permissions. diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/drivers/char/Makefile linux-2.4.22-pa12/drivers/char/Makefile --- linux-2.4.22-pa12.orig/drivers/char/Makefile 2003-08-25 20:06:03.000000000 +0200 +++ linux-2.4.22-pa12/drivers/char/Makefile 2003-10-04 15:56:59.000000000 +0200 @@ -165,6 +165,7 @@ KEYBD = dummy_keyb.o endif +obj-$(CONFIG_PARISC_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 diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/drivers/char/gdbserial.c linux-2.4.22-pa12/drivers/char/gdbserial.c --- linux-2.4.22-pa12.orig/drivers/char/gdbserial.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-pa12/drivers/char/gdbserial.c 2003-10-04 15:56:59.000000000 +0200 @@ -0,0 +1,477 @@ +/* + * Serial interface GDB stub for hppa + * + * Copyright (c) 2003 Laurent Canet + * Copyright (c) 2003 Thibaut Varene + * + * KGDB originally Written by David Grothe (dave@gcom.com) + * Modified by Scott Foehner (sfoehner@engr.sgi.com) to allow connect + * on boot-up + * GSC Serial device initialisation by Helge Deller (deller@gmx.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * KGDB TODO: SuperIO. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_GSC + #include + #define GDB_OUTB(x,y) gsc_writeb(x,y) + #define GDB_INB(x) gsc_readb(x) + static void gdb_probe_gsc(void); +#else /* We can deal with only one serial subsystem at the same time */ + #ifdef CONFIG_SUPERIO + #include /* for superio_serial_init() proto */ + #include + #define GDB_OUTB(x,y) outb(x,y) + #define GDB_INB(x) inb(x) + #endif /* CONFIG_SUPERIO */ +#endif /* CONFIG_GSC */ + +#define GDB_BUF_SIZE 512 /* power of 2, please */ +#define MAX_SER_PORTS 4 + +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; + +/* Those two are in arch/parisc/kernel/gdbstub.c */ +extern void set_debug_traps(void); /* GDB routine */ +extern void shutdown_for_gdb(struct async_struct * info); + +struct serial_state *ser_table[MAX_SER_PORTS]; +struct serial_state *ser; + +int gdb_enter = 0; /* Default: do not do gdb_hook on boot */ +int gdb_initialized = 0; + +int serports_initialized = 0; + + + +/* + * Takes: + * ttyS - integer specifying which serial port to use for debugging + * baud - baud rate of specified serial port + */ + +int gdb_serial_setup(int ttyS, int baud) +{ + unsigned cval; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + + ser = ser_table[ttyS]; + if (ser == NULL) { + kgdb_dprintk(KERN_ERR "No ser_table entry for %d\n", ttyS); + return -ENODEV; + } + + if (ser->port == 0) + return -ENODEV; + + /* 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->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; + + GDB_OUTB(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ + GDB_OUTB(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ + GDB_OUTB(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ + GDB_OUTB(cval, ser->port + UART_LCR); /* reset DLAB */ + GDB_OUTB(UART_IER_RDI, ser->port + UART_IER); /* turn on interrupts*/ + GDB_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 (GDB_INB(ser->port + UART_LSR) == 0xff) + return -EIO; + return 0; +} + + + +/* + * Get a byte from the hardware data buffer and return it + */ +static int read_data_bfr(void) +{ + unsigned long gdb_port = ser->port; + + if (GDB_INB(gdb_port + UART_LSR) & UART_LSR_DR) + return(GDB_INB(gdb_port + UART_RX)); + + return( -1 ) ; +} + +/* + * 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 */ +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void write_char(int chr) +{ + unsigned long gdb_port = ser->port; + + while ( !(GDB_INB(gdb_port + UART_LSR) & UART_LSR_THRE) ); + + GDB_OUTB(chr, gdb_port+UART_TX); +} + +/* + * 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, iir; + unsigned long gdb_port = ser->port; + + do { + chr = read_data_bfr() ; + iir = GDB_INB(gdb_port + UART_IIR) ; + + 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 */ + kgdb_dprintk(KERN_ERR "GDB Received buffer overflow\n"); + 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); +} + +/* + * Just a NULL routine for testing. + */ +void gdb_null(void) +{ +} + +/* main kGDB hook function + * + * This is called from init/main.c when "gdb" arg is passed + * on command line. It probes serial ports and initialize + * one to use for remote debugging. + */ +int gdb_hook(void) +{ + int retval; + +#ifdef CONFIG_SMP + if (smp_num_cpus > KGDB_MAX_NO_CPUS) { + printk("kgdb: too manu cpus. Cannot enable debugger with more than %u cpus\n", + KGDB_MAX_NO_CPUS); + return (-1); + } +#endif + + /* Here we call probe() functions that probe serial ports (dino/lasi + * or superio) and fill the ser_table structure with informations + * related to all ports + */ +#ifdef CONFIG_GSC + gdb_probe_gsc(); +#else + #ifdef CONFIG_SUPERIO + superio_serial_init(); + #endif /* CONFIG_SUPERIO */ +#endif /*CONFIG_GSC */ + + + /* then we initialize only the serial port associated with kGDB */ + if (gdb_serial_setup(gdb_arg_ttyS, gdb_arg_baud) != 0) { + printk ("gdb_serial_setup() error"); + return(-1); + } + + retval = request_irq(ser->irq, gdb_interrupt, SA_INTERRUPT, "GDB-stub", NULL); + if (retval != 0) + printk("gdb_hook: request_irq(irq=%d) failed: %d\n", ser->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) ; +} + +/* + * 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 DEBUG_KGDB_SERIAL + printk("getDebugChar: "); +#endif + while ( (chr = read_char()) < 0 ); + + +#ifdef DEBUG_KGDB_SERIAL + printk("%c\n", chr > ' ' && chr < 0x7F ? chr : ' '); +#endif + + return(chr); +} + +/* + * 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 DEBUG_KGDB_SERIAL + printk("putDebugChar: chr=%02x '%c'\n", chr, + chr > ' ' && chr < 0x7F ? chr : ' '); +#endif + + write_char(chr) ; /* this routine will wait */ +} + +/* + * GSC specific initialisation stuff. + * This function is called by register_paris_driver. It + * fills the ser_table structure with info related to GSC + * serial ports. Later gdb_serial_setup will use these. + */ +#ifdef CONFIG_GSC +static int serial_init_chip(struct parisc_device *dev) +{ + static int serial_line_nr; + unsigned long address; + + if (!dev->irq) { + if (dev->parent->id.hw_type != HPHW_IOA) { + printk(KERN_INFO "Serial: device 0x%lx not configured.\n", dev->hpa); + } + return -ENODEV; + } + + if (serports_initialized > MAX_SER_PORTS) + return -ENOMEM; + + ser = kmalloc(sizeof(*ser), GFP_KERNEL); + if (!ser) + return -ENOMEM; + memset(ser, 0, sizeof(*ser)); + + address = dev->hpa; + if (dev->id.sversion != 0x8d) + address += 0x800; + + ser->type = PORT_16550A; + + ser->line = serial_line_nr++; + ser->port = ioremap(address, 0x8); + ser->irq = dev->irq; + ser->type = SERIAL_IO_MEM; /* define access method */ + ser->flags = 0; + ser->xmit_fifo_size = 16; + ser->custom_divisor = 0; + ser->baud_base = LASI_BASE_BAUD; + + ser_table[serports_initialized] = ser; + serports_initialized++; + + return 0; +} + +static struct parisc_device_id serial_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, + { 0 } +}; + +/* Hack. Dino's serial port will get listed first on some machines. + * So we register this driver first which knows about Lasi's serial port. + * This needs to get fixed properly somehow. + */ +static struct parisc_device_id serial1_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */ + { 0 } +}; + +static struct parisc_driver serial1_driver = { + name: "KGDB Serial RS232", + id_table: serial1_tbl, + probe: serial_init_chip, +}; + +static struct parisc_driver serial_driver = { + name: "KGDB Serial RS232", + id_table: serial_tbl, + probe: serial_init_chip, +}; + +static void gdb_probe_gsc(void) +{ + register_parisc_driver(&serial1_driver); + register_parisc_driver(&serial_driver); +} + +#endif /* CONFIG_GSC */ diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/drivers/char/serial.c linux-2.4.22-pa12/drivers/char/serial.c --- linux-2.4.22-pa12.orig/drivers/char/serial.c 2003-08-25 20:06:03.000000000 +0200 +++ linux-2.4.22-pa12/drivers/char/serial.c 2003-10-04 15:56:59.000000000 +0200 @@ -214,7 +214,7 @@ #include #endif #include -#ifdef CONFIG_SERIAL_CONSOLE +#if defined(CONFIG_SERIAL_CONSOLE) || defined (CONFIG_GDB_CONSOLE) #include #endif #ifdef ENABLE_SERIAL_PCI @@ -235,6 +235,10 @@ #include "serial_compat.h" #endif +#ifdef CONFIG_PARISC_REMOTE_DEBUG +#include +#endif + #include #include #include @@ -1680,6 +1684,12 @@ info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags); } +#ifdef CONFIG_PARISC_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[] = { @@ -2791,7 +2801,13 @@ /* "setserial -W" is called in Debian boot */ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); return 0; - +#ifdef CONFIG_PARISC_REMOTE_DEBUG + case TIOCGDB: + printk(KERN_ERR "TIOGDB in rs_ioctl\n"); + gdb_arg_ttyS = MINOR(tty->device) & 0x03F ; + gdb_arg_baud = tty_get_baud_rate(tty) ; + return gdb_hook(); +#endif default: return -ENOIOCTLCMD; } @@ -6144,6 +6160,35 @@ #endif /* + * ------------------------------------------------------------ + * Serial GDB driver (most in gdbserial.c) + * ------------------------------------------------------------ + */ + +#ifdef CONFIG_PARISC_REMOTE_DEBUG +#ifdef CONFIG_GDB_CONSOLE +static struct console gdbcons = { + name: "gdb", /* name */ + write: gdb_console_write, /* write */ + flags: CON_PRINTBUFFER | CON_ENABLED, /* flags */ + index: -1, /* cflags */ +}; + +/* la console GDB n'est la que pour renvoyer + * les printks vers le remote gdb + */ + +void __init gdb_console_init(void) +{ + if (gdb_arg_gdbcons) { + printk(KERN_INFO "GDB Console Init\n"); + register_console(&gdbcons); + } +} +#endif +#endif /* CONFIG_PARISC_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: diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/drivers/gsc/serial.c linux-2.4.22-pa12/drivers/gsc/serial.c --- linux-2.4.22-pa12.orig/drivers/gsc/serial.c 2002-03-26 06:19:53.000000000 +0100 +++ linux-2.4.22-pa12/drivers/gsc/serial.c 2003-10-04 15:56:59.000000000 +0200 @@ -29,6 +29,7 @@ #include #include #include +#include #include "busdevice.h" @@ -76,8 +77,12 @@ address += 0x800; } - setup_parisc_serial(serial, address, dev->irq, serial_line_nr++); + if (serial_line_nr == gdb_arg_ttyS) { + printk(KERN_WARNING "Not registering serial port already allocated to KGDB\n"); + return -ENODEV; + } + setup_parisc_serial(serial, address, dev->irq, serial_line_nr++); if (register_serial(serial) < 0) { printk(KERN_WARNING "register_serial returned error\n"); kfree(serial); diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/fs/proc/proc_misc.c linux-2.4.22-pa12/fs/proc/proc_misc.c --- linux-2.4.22-pa12.orig/fs/proc/proc_misc.c 2003-08-25 20:06:37.000000000 +0200 +++ linux-2.4.22-pa12/fs/proc/proc_misc.c 2003-10-04 15:56:59.000000000 +0200 @@ -150,6 +150,17 @@ return proc_calc_metrics(page, start, off, count, eof, len); } +static int bugme_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *a = NULL; + + *a = '0'; + + return 0; +} + + static int meminfo_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -575,6 +586,7 @@ } *p, simple_ones[] = { {"loadavg", loadavg_read_proc}, {"uptime", uptime_read_proc}, + {"bugme", bugme_read_proc}, {"meminfo", meminfo_read_proc}, {"version", version_read_proc}, #ifdef CONFIG_PROC_HARDWARE diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/include/asm-parisc/gdb.h linux-2.4.22-pa12/include/asm-parisc/gdb.h --- linux-2.4.22-pa12.orig/include/asm-parisc/gdb.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-pa12/include/asm-parisc/gdb.h 2003-10-04 15:56:59.000000000 +0200 @@ -0,0 +1,94 @@ +#ifndef _GDB_H_ +#define _GDB_H_ + +/* + * Copyright (C) 2001 Amit S. Kale + * + * hppa bits are + * Copyright (c) 2003 Laurent Canet + * Copyright (c) 2003 Thibaut Varene + * + * 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. + */ + +#include /* struct pt_regs */ +#include /* PSW_B */ +#include + +/* define to debug KGDB itself */ +#undef KGDB_DEBUG + +/* gdb locks */ +#define KGDB_MAX_NO_CPUS 8 + +#define KGDB_TRAP_DIE 1 /* kernel dies */ +#define KGDB_TRAP_BREAK 2 /* break 5,8 */ +#define KGDB_TRAP_PAGEFAULT 3 /* kernel page faults : mostly kgdb mem erri */ +#define KGDB_TRAP_BKPT 4 /* GDB breakpoint */ + +/* this increases the IAOQ registers (pc) for next, continue and mem err */ +#define INCREASE_IAOQ(regs) { \ + regs->iaoq[0] = regs->iaoq[1]; \ + regs->iaoq[1] = regs->iaoq[0] + 4; \ + regs->gr[0] &= ~PSW_B; } + +#define KGDB_ASSERT(message, condition) do { \ + if (!(condition)) { \ + printk("kgdb assertion failed: %s\n", message); \ + asm ("break 5,8"); \ + } \ +} while (0) + +#ifdef CONFIG_KERNEL_ASSERTS + #define KERNEL_ASSERT(message, condition) KGDB_ASSERT(message, condition) +#else + #define KERNEL_ASSERT(message, condition) +#endif + +#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 + +#ifdef KGDB_DEBUG + #define kgdb_dprintk(fmt, args...) printk(fmt, ##args) +#else + #define kgdb_dprintk(x,...) +#endif + +extern int gdb_arg_enter; /* 1 = enter debugger on boot */ +extern int gdb_arg_ttyS; +extern int gdb_arg_baud; +extern int gdb_arg_gdbcons; +extern int gdb_initialized; + +extern int gdb_hook(void); +extern void breakpoint(void); + +typedef void 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; + +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 /* _GDB_H_ */ diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/include/asm-parisc/ioctls.h linux-2.4.22-pa12/include/asm-parisc/ioctls.h --- linux-2.4.22-pa12.orig/include/asm-parisc/ioctls.h 2001-10-27 01:24:55.000000000 +0200 +++ linux-2.4.22-pa12/include/asm-parisc/ioctls.h 2003-10-04 15:56:59.000000000 +0200 @@ -67,6 +67,7 @@ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ +#define TIOCGDB 0x547F /* enable GDB stub mode on this terminal */ #define FIOQSIZE 0x5460 /* Get exact space used by quota */ /* Used for packet mode */ diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/include/asm-parisc/page.h linux-2.4.22-pa12/include/asm-parisc/page.h --- linux-2.4.22-pa12.orig/include/asm-parisc/page.h 2002-08-05 00:59:52.000000000 +0200 +++ linux-2.4.22-pa12/include/asm-parisc/page.h 2003-10-04 15:56:59.000000000 +0200 @@ -10,6 +10,7 @@ #ifndef __ASSEMBLY__ #include +#include #define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) #define copy_page(to,from) copy_user_page_asm((void *)(to), (void *)(from)) @@ -88,10 +89,16 @@ * see^H^H^Hhear bugs in early bootup as well! * * We don't beep yet. prumpf + * Run KGDB if compiled. */ + +#ifdef CONFIG_PARISC_REMOTE_DEBUG +#define BUG() KGDB_ASSERT("BUG", 0) +#else #define BUG() do { \ printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ } while (0) +#endif #define PAGE_BUG(page) do { \ BUG(); \ diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/include/asm-parisc/processor.h linux-2.4.22-pa12/include/asm-parisc/processor.h --- linux-2.4.22-pa12.orig/include/asm-parisc/processor.h 2003-03-20 16:04:36.000000000 +0100 +++ linux-2.4.22-pa12/include/asm-parisc/processor.h 2003-10-04 15:56:59.000000000 +0200 @@ -120,6 +120,9 @@ unsigned long task_size; unsigned long map_base; unsigned long flags; +#ifdef CONFIG_KGDB_THREAD + int gotregs; +#endif }; /* Thread struct flags. */ diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/include/linux/sched.h linux-2.4.22-pa12/include/linux/sched.h --- linux-2.4.22-pa12.orig/include/linux/sched.h 2003-06-26 17:08:08.000000000 +0200 +++ linux-2.4.22-pa12/include/linux/sched.h 2003-10-04 15:56:59.000000000 +0200 @@ -146,7 +146,18 @@ #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_do_schedule(void); + +static inline void schedule(void) +{ +#ifdef CONFIG_KGDB_THREAD + kern_do_schedule(); +#else + do_schedule(); +#endif +} extern int schedule_task(struct tq_struct *task); extern void flush_scheduled_tasks(void); diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/init/main.c linux-2.4.22-pa12/init/main.c --- linux-2.4.22-pa12.orig/init/main.c 2003-08-25 20:07:12.000000000 +0200 +++ linux-2.4.22-pa12/init/main.c 2003-10-04 15:56:59.000000000 +0200 @@ -73,6 +73,10 @@ #include #endif +#ifdef CONFIG_PARISC_REMOTE_DEBUG +#include +#endif + /* * Versions of gcc older than that listed below may actually compile * and link okay, but the end product can have subtle run time bugs. @@ -443,6 +447,11 @@ * make syscalls (and thus be locked). */ smp_init(); +#ifdef CONFIG_PARISC_REMOTE_DEBUG + if (gdb_arg_enter) { + gdb_hook(); /* right at boot time */ + } +#endif rest_init(); } diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/kernel/ksyms.c linux-2.4.22-pa12/kernel/ksyms.c --- linux-2.4.22-pa12.orig/kernel/ksyms.c 2003-08-25 20:07:12.000000000 +0200 +++ linux-2.4.22-pa12/kernel/ksyms.c 2003-10-04 15:56:59.000000000 +0200 @@ -455,7 +455,8 @@ EXPORT_SYMBOL(sleep_on_timeout); EXPORT_SYMBOL(interruptible_sleep_on); EXPORT_SYMBOL(interruptible_sleep_on_timeout); -EXPORT_SYMBOL(schedule); +EXPORT_SYMBOL(do_schedule); +EXPORT_SYMBOL(kern_do_schedule); EXPORT_SYMBOL(schedule_timeout); #if CONFIG_SMP EXPORT_SYMBOL(set_cpus_allowed); diff -EbBNaur -x CVS linux-2.4.22-pa12.orig/kernel/sched.c linux-2.4.22-pa12/kernel/sched.c --- linux-2.4.22-pa12.orig/kernel/sched.c 2003-08-25 20:07:12.000000000 +0200 +++ linux-2.4.22-pa12/kernel/sched.c 2003-10-04 15:57:00.000000000 +0200 @@ -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.gotregs = 0; +#endif + do_schedule(); +} + +#ifdef CONFIG_KGDB_THREAD +asmlinkage void kern_do_schedule(void) +{ + current->thread.gotregs = 1; + 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