[cgl_discussion] Re: [CarrierLinux Developer] kgdb patch for cgle1.0
george anzinger
george at mvista.com
Mon Sep 9 14:44:39 PDT 2002
"Zhu, Yi" wrote:
>
> Anyone has kgdb patch for CGLE? Because the kgdb patch from
> http://kgdb.sourceforge.net/linux-2.4.18-kgdb-1.5.patch seems conflict with
> the O(1) schedule in CGLE.
That is the LEAST of its problems. The attached is what I
will be putting into the tree. After patching your kernel
see: .../Documentation/i386/kgdb/* for help on using it and
a list of all of its wonder filled features.
>
> ----------------------------------------------------------------------------
> ----------------------------------------------------
> I speak for my own, not for Intel Corp.
>
> Zhu Yi (Chuyee)
> Intel China Software Lab (ICSL)
> 22nd Floor, ShanghaiMart Tower No. 2299 Yan'an Road(West) Shanghai 200336,
> PRC
> Tel: 8621-52574545-1261 Fax: 8621-62366119
>
> GnuPG v1.0.6 (GNU/Linux)
> http://cn.geocities.com/chewie_chuyee/gpg.txt
> 1024D/71C34820 C939 2B0B FBCE 1D51 109A 55E5 8650 DB90 71C3 4820
>
> _______________________________________________
> Developer mailing list
> Developer at carrierlinux.org
> http://www.carrierlinux.org/mailman/listinfo/developer
--
George Anzinger george at mvista.com
High-res-timers:
http://sourceforge.net/projects/high-res-timers/
Preemption patch:
http://www.kernel.org/pub/linux/kernel/people/rml
-------------- next part --------------
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/Configure.help linux/Documentation/Configure.help
--- linux_test/Documentation/Configure.help Tue Aug 20 17:42:46 2002
+++ linux/Documentation/Configure.help Thu Aug 22 13:35:52 2002
@@ -19936,6 +19936,97 @@
this option. See "man kallsyms" for the data format, it adds 10-20%
to the size of the kernel and the loaded modules. If unsure, say N.
+Include kgdb kernel debugger
+CONFIG_KGDB
+ If you say Y here, the system will be compiled with the debug option
+ (-g) and a debugging stub will be included in the kernel. This stub
+ communicates with gdb on another (host) computer via a serial port.
+ The host computer should have access to the kernel binary file
+ (vmlinux) and a serial port that is connected to the target machine.
+ Gdb can be made to configure the serial port or you can use stty and
+ setserial to do this. See the 'target' command in gdb. This option also
+ configures in the ability to request a breakpoint early in
+ the boot process. To request the breakpoint just include 'kgdb' as a
+ boot option when booting the target machine. The system will then
+ break as soon as it looks at the boot options. This option also
+ installs a breakpoint in panic and sends any kernel faults to the
+ debugger. For more information see the Documentation/i386/kgdb.txt file.
+
+
+Debug serial port BAUD
+CONFIG_KGDB_9600BAUD
+ Gdb and the kernel stub need to agree on the baud rate to be used.
+ Some systems (x86 family at this writing) allow this to be configured.
+
+hex I/O port address of the debug serial port
+CONFIG_KGDB_PORT
+ Some systems (x86 family at this writing) allow the port address to be
+ configured. The number entered is assumed to be hex, don't put 0x in
+ front of it. The standard address are: COM1 3f8 , irq 4 and
+ COM2 2f8 irq 3. Setserial /dev/ttySx will tell you what you have.
+ It is good to test the serial connection with a live system before trying
+ to debug.
+
+Kgdb event macros
+CONFIG_KGDB_TS
+ Kgdb event macros allow you to instrument your code with calls to the
+ kgdb event recording function. The event log may be examined with gdb
+ at a break point. Turning on this capability also allows you to choose
+ how many events to keep. Kgdb always keeps the lastest events.
+
+Stack overflow test (part of kgdb option)
+CONFIG_STACK_OVERFLOW_TEST
+ This option enables code in the front line interrupt handlers to check
+ for kernel stack overflow on interrupts and system calls. This is part
+ of the kgdb code on x86 systems.
+
+
+IRQ of the debug serial port
+CONFIG_KGDB_IRQ
+ This is the irq for the debug port. If everything is working correctly
+ and the kernel has interrupts on a control C to the port should cause a
+ break into the kernel debug stub.
+
+Enable compile options
+CONFIG_KGDB_MORE
+ Saying yes here turns on the ability to enter additional compile options.
+
+Additional compile options
+CONFIG_KGDB_OPTIONS
+ This option allows you enter additional compile options for the whole
+ kernel compile. Each platform will have a default that seems right
+ for it. For example on PPC "-ggdb -O1", and for i386 "-O1". Note that
+ by configuring KGDB "-g" is already turned on. In addition, on i386
+ platforms "-fomit-frame-pointer" is deleted from the standard compile
+ options.
+
+Number of CPUs
+CONFIG_NO_KGDB_CPUS
+ This option sets the number of cpus for kgdb ONLY. It is used to prune
+ some internal structures so they look "nice" when displayed with gdb.
+ This is to overcome the kernel default of 32 for this number. Enter the
+ real number to get nice clean kgdb_info displays.
+
+SysRq Gdb command
+CONFIG_KGDB_SYSRQ
+ This option includes an option in the SysRq code that allows you to enter
+ SysRq G which generates a breakpoint to the KGDB stub. This will work if
+ the keyboard is alive and can interrupt the system. Because of constraints
+ on when the serial port interrupt can be enabled, this code may allow you
+ to interrupt the system before the serial port control C is available.
+ Just say yes here.
+
+KGDB console
+CONFIG_KGDB_CONSOLE
+ This option enables the command line "console=kgdb" option. When the
+ system is booted with this option in the command line all kernel printk
+ output is sent to gdb (as well as to other consoles). For this to work
+ gdb must be connected. For this reason, this command line option will
+ generate a breakpoint if gdb has not yet connected. After the gdb
+ continue command is given all pent up console output will be printed
+ by gdb on the host machine. Neither this option, nor KGDB require the
+ serial driver to be configured.
+
ISDN support
CONFIG_ISDN
ISDN ("Integrated Services Digital Networks", called RNIS in France)
@@ -26325,7 +26416,8 @@
# LocalWords: CramFs Cramfs uid cramfs AVM's kernelcapi PCIV cdrdao Cdparanoia
# LocalWords: DMX Domex dmx wellington ftdi sio Accton Billington Corega FEter
# LocalWords: MELCO LUA PNA Linksys SNC chkdsk AWACS Webcam RAMFS Ramfs ramfs
-# LocalWords: ramfiles MAKEDEV pty WDTPCI APA apa
+# LocalWords: ramfiles MAKEDEV pty WDTPCI APA apa keypress kgdb stty ggdb
+# LocalWords: fomit printk
#
# The following sets edit modes for GNU EMACS
# Local Variables:
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/andthen linux/Documentation/i386/kgdb/andthen
--- linux_test/Documentation/i386/kgdb/andthen Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/andthen Thu Aug 22 13:35:52 2002
@@ -0,0 +1,100 @@
+
+define set_andthen
+ set var $thp=0
+ set var $thp=(struct kgdb_and_then_struct *)&kgdb_data[0]
+ set var $at_size = (sizeof kgdb_data)/(sizeof *$thp)
+ set var $at_oc=kgdb_and_then_count
+ set var $at_cc=$at_oc
+end
+
+define andthen_next
+ set var $at_cc=$arg0
+end
+
+define andthen
+ andthen_set_edge
+ if ($at_cc >= $at_oc)
+ printf "Outside window. Window size is %d\n",($at_oc-$at_low)
+ else
+ printf "%d: ",$at_cc
+ output *($thp+($at_cc++ % $at_size ))
+ printf "\n"
+ end
+end
+define andthen_set_edge
+ set var $at_oc=kgdb_and_then_count
+ set var $at_low = $at_oc - $at_size
+ if ($at_low < 0 )
+ set var $at_low = 0
+ end
+ if (( $at_cc > $at_oc) || ($at_cc < $at_low))
+ printf "Count outside of window, setting count to "
+ if ($at_cc >= $at_oc)
+ set var $at_cc = $at_oc
+ else
+ set var $at_cc = $at_low
+ end
+ printf "%d\n",$at_cc
+ end
+end
+
+define beforethat
+ andthen_set_edge
+ if ($at_cc <= $at_low)
+ printf "Outside window. Window size is %d\n",($at_oc-$at_low)
+ else
+ printf "%d: ",$at_cc-1
+ output *($thp+(--$at_cc % $at_size ))
+ printf "\n"
+ end
+end
+
+document andthen_next
+ andthen_next <count>
+ . sets the number of the event to display next. If this event
+ . is not in the event pool, either andthen or beforethat will
+ . correct it to the nearest event pool edge. The event pool
+ . ends at the last event recorded and begins <number of events>
+ . prior to that. If beforethat is used next, it will display
+ . event <count> -1.
+.
+ andthen commands are: set_andthen, andthen_next, andthen and beforethat
+end
+
+
+document andthen
+ andthen
+. displays the next event in the list. <set_andthen> sets up to display
+. the oldest saved event first.
+. <count> (optional) count of the event to display.
+. note the number of events saved is specified at configure time.
+. if events are saved between calls to andthen the index will change
+. but the displayed event will be the next one (unless the event buffer
+. is overrun).
+.
+. andthen commands are: set_andthen, andthen_next, andthen and beforethat
+end
+
+document set_andthen
+ set_andthen
+. sets up to use the <andthen> and <beforethat> commands.
+. if you have defined your own struct, use the above and
+. then enter the following:
+. p $thp=(struct kgdb_and_then_structX *)&kgdb_data[0]
+. where <kgdb_and_then_structX> is the name of your structure.
+.
+. andthen commands are: set_andthen, andthen_next, andthen and beforethat
+end
+
+document beforethat
+ beforethat
+. displays the next prior event in the list. <set_andthen> sets up to
+. display the last occuring event first.
+.
+. note the number of events saved is specified at configure time.
+. if events are saved between calls to beforethat the index will change
+. but the displayed event will be the next one (unless the event buffer
+. is overrun).
+.
+. andthen commands are: set_andthen, andthen_next, andthen and beforethat
+end
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/debug-nmi.txt linux/Documentation/i386/kgdb/debug-nmi.txt
--- linux_test/Documentation/i386/kgdb/debug-nmi.txt Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/debug-nmi.txt Thu Aug 22 13:35:52 2002
@@ -0,0 +1,37 @@
+Subject: Debugging with NMI
+Date: Mon, 12 Jul 1999 11:28:31 -0500
+From: David Grothe <dave at gcom.com>
+Organization: Gcom, Inc
+To: David Grothe <dave at gcom.com>
+
+Kernel hackers:
+
+Maybe this is old hat, but it is new to me --
+
+On an ISA bus machine, if you short out the A1 and B1 pins of an ISA
+slot you will generate an NMI to the CPU. This interrupts even a
+machine that is hung in a loop with interrupts disabled. Used in
+conjunction with kgdb <
+ftp://ftp.gcom.com/pub/linux/src/kgdb-2.3.35/kgdb-2.3.35.tgz > you can
+gain debugger control of a machine that is hung in the kernel! Even
+without kgdb the kernel will print a stack trace so you can find out
+where it was hung.
+
+The A1/B1 pins are directly opposite one another and the farthest pins
+towards the bracket end of the ISA bus socket. You can stick a paper
+clip or multi-meter probe between them to short them out.
+
+I had a spare ISA bus to PC104 bus adapter around. The PC104 end of the
+board consists of two rows of wire wrap pins. So I wired a push button
+between the A1/B1 pins and now have an ISA board that I can stick into
+any ISA bus slot for debugger entry.
+
+Microsoft has a circuit diagram of a PCI card at
+http://www.microsoft.com/hwdev/DEBUGGING/DMPSW.HTM. If you want to
+build one you will have to mail them and ask for the PAL equations.
+Nobody makes one comercially.
+
+[THIS TIP COMES WITH NO WARRANTY WHATSOEVER. It works for me, but if
+your machine catches fire, it is your problem, not mine.]
+
+-- Dave (the kgdb guy)
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/gdb-globals.txt linux/Documentation/i386/kgdb/gdb-globals.txt
--- linux_test/Documentation/i386/kgdb/gdb-globals.txt Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/gdb-globals.txt Thu Aug 22 13:35:52 2002
@@ -0,0 +1,71 @@
+Sender: akale at veritas.com
+Date: Fri, 23 Jun 2000 19:26:35 +0530
+From: "Amit S. Kale" <akale at veritas.com>
+Organization: Veritas Software (India)
+To: Dave Grothe <dave at gcom.com>, linux-kernel at vger.rutgers.edu
+CC: David Milburn <dmilburn at wirespeed.com>,
+ "Edouard G. Parmelan" <Edouard.Parmelan at quadratec.fr>,
+ ezannoni at cygnus.com, Keith Owens <kaos at ocs.com.au>
+Subject: Re: Module debugging using kgdb
+
+Dave Grothe wrote:
+>
+> Amit:
+>
+> There is a 2.4.0 version of kgdb on our ftp site:
+> ftp://ftp.gcom.com/pub/linux/src/kgdb. I mirrored your version of gdb
+> and loadmodule.sh there.
+>
+> Have a look at the README file and see if I go it right. If not, send
+> me some corrections and I will update it.
+>
+> Does your version of gdb solve the global variable problem?
+
+Yes.
+Thanks to Elena Zanoni, gdb (developement version) can now calculate
+correctly addresses of dynamically loaded object files. I have not been
+following gdb developement for sometime and am not sure when symbol
+address calculation fix is going to appear in a gdb stable version.
+
+Elena, any idea when the fix will make it to a prebuilt gdb from a
+redhat release?
+
+For the time being I have built a gdb developement version. It can be
+used for module debugging with loadmodule.sh script.
+
+The problem with calculating of module addresses with previous versions
+of gdb was as follows:
+gdb did not use base address of a section while calculating address of
+a symbol in the section in an object file loaded via 'add-symbol-file'.
+It used address of .text segment instead. Due to this addresses of
+symbols in .data, .bss etc. (e.g. global variables) were calculated incorrectly.
+
+Above mentioned fix allow gdb to use base address of a segment while
+calculating address of a symbol in it. It adds a parameter '-s' to
+'add-symbol-file' command for specifying base address of a segment.
+
+loadmodule.sh script works as follows.
+
+1. Copy a module file to target machine.
+2. Load the module on the target machine using insmod with -m parameter.
+insmod produces a module load map which contains base addresses of all
+sections in the module and addresses of symbols in the module file.
+3. Find all sections and their base addresses in the module from
+the module map.
+4. Generate a script that loads the module file. The script uses
+'add-symbol-file' and specifies address of text segment followed by
+addresses of all segments in the module.
+
+Here is an example gdb script produced by loadmodule.sh script.
+
+add-symbol-file foo 0xd082c060 -s .text.lock 0xd08cbfb5
+-s .fixup 0xd08cfbdf -s .rodata 0xd08cfde0 -s __ex_table 0xd08e3b38
+-s .data 0xd08e3d00 -s .bss 0xd08ec8c0 -s __ksymtab 0xd08ee838
+
+With this command gdb can calculate addresses of symbols in ANY segment
+in a module file.
+
+Regards.
+--
+Amit Kale
+Veritas Software ( http://www.veritas.com )
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/gdbinit linux/Documentation/i386/kgdb/gdbinit
--- linux_test/Documentation/i386/kgdb/gdbinit Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/gdbinit Thu Aug 22 13:35:52 2002
@@ -0,0 +1,14 @@
+shell echo -e "\003" >/dev/ttyS0
+set remotebaud 38400
+target remote /dev/ttyS0
+define si
+stepi
+printf "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", $eax, $ebx, $ecx, $edx
+printf "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n", $esi, $edi, $ebp, $esp
+x/i $eip
+end
+define ni
+nexti
+printf "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", $eax, $ebx, $ecx, $edx
+printf "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n", $esi, $edi, $ebp, $esp
+x/i $eip
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/gdbinit-modules linux/Documentation/i386/kgdb/gdbinit-modules
--- linux_test/Documentation/i386/kgdb/gdbinit-modules Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/gdbinit-modules Thu Aug 22 13:35:52 2002
@@ -0,0 +1,146 @@
+#
+# Usefull GDB user-command to debug Linux Kernel Modules with gdbstub.
+#
+# This don't work for Linux-2.0 or older.
+#
+# Author Edouard G. Parmelan <Edouard.Parmelan at quadratec.fr>
+#
+#
+# Fri Apr 30 20:33:29 CEST 1999
+# First public release.
+#
+# Major cleanup after experiment Linux-2.0 kernel without success.
+# Symbols of a module are not in the correct order, I can't explain
+# why :(
+#
+# Fri Mar 19 15:41:40 CET 1999
+# Initial version.
+#
+# Thu Jan 6 16:29:03 CST 2000
+# A little fixing by Dave Grothe <dave at gcom.com>
+#
+# Mon Jun 19 09:33:13 CDT 2000
+# Alignment changes from Edouard Parmelan
+#
+# The basic idea is to find where insmod load the module and inform
+# GDB to load the symbol table of the module with the GDB command
+# ``add-symbol-file <object> <address>''.
+#
+# The Linux kernel holds the list of all loaded modules in module_list,
+# this list end with &kernel_module (exactly with module->next == NULL,
+# but the last module is not a real module).
+#
+# Insmod allocates the struct module before the object file. Since
+# Linux-2.1, this structure contain his size. The real address of
+# the object file is then (char*)module + module->size_of_struct.
+#
+# You can use three user functions ``mod-list'', ``mod-print-symbols''
+# and ``add-module-symbols''.
+#
+# mod-list list all loaded modules with the format:
+# <module-address> <module-name>
+#
+# As soon as you have found the address of your module, you can
+# print its exported symbols (mod-print-symbols) or inform GDB to add
+# symbols from your module file (mod-add-symbols).
+#
+# The argument that you give to mod-print-symbols or mod-add-symbols
+# is the <module-address> from the mod-list command.
+#
+# When using the mod-add-symbols command you must also give the full
+# pathname of the modules object code file.
+#
+# The command mod-add-lis is an example of how to make this easier.
+# You can edit this macro to contain the path name of your own
+# favorite module and then use it as a shorthand to load it. You
+# still need the module-address, however.
+#
+# The internal function ``mod-validate'' set the GDB variable $mod
+# as a ``struct module*'' if the kernel known the module otherwise
+# $mod is set to NULL. This ensure to not add symbols for a wrong
+# address.
+#
+# Have a nice hacking day !
+#
+#
+define mod-list
+ set $mod = (struct module*)module_list
+ # the last module is the kernel, ignore it
+ while $mod != &kernel_module
+ printf "%p\t%s\n", (long)$mod, ($mod)->name
+ set $mod = $mod->next
+ end
+end
+document mod-list
+List all modules in the form: <module-address> <module-name>
+Use the <module-address> as the argument for the other
+mod-commands: mod-print-symbols, mod-add-symbols.
+end
+
+define mod-validate
+ set $mod = (struct module*)module_list
+ while ($mod != $arg0) && ($mod != &kernel_module)
+ set $mod = $mod->next
+ end
+ if $mod == &kernel_module
+ set $mod = 0
+ printf "%p is not a module\n", $arg0
+ end
+end
+document mod-validate
+mod-validate <module-address>
+Internal user-command used to validate the module parameter.
+If <module> is a real loaded module, set $mod to it otherwise set $mod to 0.
+end
+
+
+define mod-print-symbols
+ mod-validate $arg0
+ if $mod != 0
+ set $i = 0
+ while $i < $mod->nsyms
+ set $sym = $mod->syms[$i]
+ printf "%p\t%s\n", $sym->value, $sym->name
+ set $i = $i + 1
+ end
+ end
+end
+document mod-print-symbols
+mod-print-symbols <module-address>
+Print all exported symbols of the module. see mod-list
+end
+
+
+define mod-add-symbols-align
+ mod-validate $arg0
+ if $mod != 0
+ set $mod_base = ($mod->size_of_struct + (long)$mod)
+ if ($arg2 != 0) && (($mod_base & ($arg2 - 1)) != 0)
+ set $mod_base = ($mod_base | ($arg2 - 1)) + 1
+ end
+ add-symbol-file $arg1 $mod_base
+ end
+end
+document mod-add-symbols-align
+mod-add-symbols-align <module-address> <object file path name> <align>
+Load the symbols table of the module from the object file where
+first section aligment is <align>.
+To retreive alignment, use `objdump -h <object file path name>'.
+end
+
+define mod-add-symbols
+ mod-add-symbols-align $arg0 $arg1 sizeof(long)
+end
+document mod-add-symbols
+mod-add-symbols <module-address> <object file path name>
+Load the symbols table of the module from the object file.
+Default alignment is 4. See mod-add-symbols-align.
+end
+
+define mod-add-lis
+ mod-add-symbols-align $arg0 /usr/src/LiS/streams.o 16
+end
+document mod-add-lis
+mod-add-lis <module-address>
+Does mod-add-symbols <module-address> /usr/src/LiS/streams.o
+end
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/gdbinit.hw linux/Documentation/i386/kgdb/gdbinit.hw
--- linux_test/Documentation/i386/kgdb/gdbinit.hw Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/gdbinit.hw Thu Aug 22 13:35:52 2002
@@ -0,0 +1,107 @@
+
+#Using ia-32 hardware breakpoints.
+#
+#4 hardware breakpoints are available in ia-32 processors. These breakpoints
+#do not need code modification. They are set using debug registers.
+#
+#Each hardware breakpoint can be of one of the
+#three types: execution, write, access.
+#1. An Execution breakpoint is triggered when code at the breakpoint address is
+#executed.
+#2. A write breakpoint ( aka watchpoints ) is triggered when memory location
+#at the breakpoint address is written.
+#3. An access breakpoint is triggered when memory location at the breakpoint
+#address is either read or written.
+#
+#As hardware breakpoints are available in limited number, use software
+#breakpoints ( br command in gdb ) instead of execution hardware breakpoints.
+#
+#Length of an access or a write breakpoint defines length of the datatype to
+#be watched. Length is 1 for char, 2 short , 3 int.
+#
+#For placing execution, write and access breakpoints, use commands
+#hwebrk, hwwbrk, hwabrk
+#To remove a breakpoint use hwrmbrk command.
+#
+#These commands take following types of arguments. For arguments associated
+#with each command, use help command.
+#1. breakpointno: 0 to 3
+#2. length: 1 to 3
+#3. address: Memory location in hex ( without 0x ) e.g c015e9bc
+#
+#Use the command exinfo to find which hardware breakpoint occured.
+
+#hwebrk breakpointno address
+define hwebrk
+ maintenance packet Y$arg0,0,0,$arg1
+end
+document hwebrk
+ hwebrk <breakpointno> <address>
+ Places a hardware execution breakpoint
+ <breakpointno> = 0 - 3
+ <address> = Hex digits without leading "0x".
+end
+
+#hwwbrk breakpointno length address
+define hwwbrk
+ maintenance packet Y$arg0,1,$arg1,$arg2
+end
+document hwwbrk
+ hwwbrk <breakpointno> <length> <address>
+ Places a hardware write breakpoint
+ <breakpointno> = 0 - 3
+ <length> = 1 (1 byte), 2 (2 byte), 3 (4 byte)
+ <address> = Hex digits without leading "0x".
+end
+
+#hwabrk breakpointno length address
+define hwabrk
+ maintenance packet Y$arg0,1,$arg1,$arg2
+end
+document hwabrk
+ hwabrk <breakpointno> <length> <address>
+ Places a hardware access breakpoint
+ <breakpointno> = 0 - 3
+ <length> = 1 (1 byte), 2 (2 byte), 3 (4 byte)
+ <address> = Hex digits without leading "0x".
+end
+
+#hwrmbrk breakpointno
+define hwrmbrk
+ maintenance packet y$arg0
+end
+document hwrmbrk
+ hwrmbrk <breakpointno>
+ <breakpointno> = 0 - 3
+ Removes a hardware breakpoint
+end
+
+#exinfo
+define exinfo
+ maintenance packet qE
+end
+document exinfo
+ exinfo
+ Gives information about a breakpoint.
+end
+define get_cu
+ p $cu=(struct task_struct *)((int)$esp & ~8191)
+end
+document get_cu
+ get_cu
+ Gets and print the "current" value. Defines $cu to be it.
+end
+define int_off
+ set var $flags=$eflags
+ set $eflags=$eflags&~0x200
+ end
+define int_on
+ set var $eflags|=$flags&0x200
+ end
+document int_off
+ saves the current interrupt state and clears the processor interrupt
+ flag. Use int_on to restore the saved flag.
+end
+document int_on
+ Restores the interrupt flag saved by int_off.
+end
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/kgdb.txt linux/Documentation/i386/kgdb/kgdb.txt
--- linux_test/Documentation/i386/kgdb/kgdb.txt Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/kgdb.txt Thu Aug 22 13:35:52 2002
@@ -0,0 +1,697 @@
+Last edit: <20020801.1635.47>
+This file has information specific to the i386 kgdb option. Other
+platforms with the kgdb option may behave in a similar fashion.
+
+New features:
+============
+20020801.1129.03
+This is currently the version for the 2.4.18 (and beyond?) kernel.
+
+We have several new "features" beginning with this version:
+
+1.) Kgdb now syncs the "other" cpus with a cross cpu NMI. No more
+ waiting and it will pull that guy out of an irq off spin lock :)
+
+2.) We doctored up the code that tells where a task is waiting and
+ included it so that the "info thread" command will show a bit more
+ than "schedule()". Try it...
+
+3.) Added the ability to call a function from gdb. All the standard gdb
+ issues apply, i.e. if you hit a break point in the function you are
+ not allowed to call another (gdb limitation, not kgdb). T0 help
+ this capability we added a memory allocation function. Gdb does not
+ return this memory (it is used for stings you pass to that function
+ you are calling from gdb) so we fixed up a way to allow you to
+ manually return the memory (see below).
+
+4.) Kgdb time stamps (kgdb_ts()) are enhanced to expand what was the
+ interrupt flag to now also include the preemption count and the
+ "in_interrupt" info. The flag is now called "with_pif" to indicate
+ the order, preempt_count, in_interrupt, flag. The preempt_count is
+ shifted left by 4 bits so you can read the count in hex by dropping
+ the low order digit. In_interrupt is in bit 1, and the flag is in
+ bit 0.
+
+5.) The command: "p kgdb_info" is now expanded and prints something
+ like:
+(gdb) p kgdb_info
+$2 = {used_malloc = 0, called_from = 0xc0107506, entry_tsc = 67468627259,
+ errcode = 0, vector = 3, print_debug_info = 0, hold_on_sstep = 1,
+ cpus_waiting = {{task = 0xc027a000, pid = 32768, hold = 0,
+ regs = 0xc027bf84}, {task = 0x0, pid = 0, hold = 0, regs = 0x0}}}
+
+ Things to note here: a.) used_malloc is the amount of memory that
+ has been malloc'ed to do calls from gdb. You can reclaim this
+ memory like this: "p kgdb_info.used_malloc=0" Cool, huh? b.)
+ cpus_waiting is now "sized" by the number of cpus you enter at
+ configure time in the kgdb configure section. This is NOT used any
+ where else in the system, but it is "nice" here. c.) The tasks
+ "pid" is now in the structure. This is the pid you will need to use
+ to decode to the thread id to get gdb to look at that thread.
+ Remember that the "info thread" command prints a list of threads
+ where in it numbers each thread with its reference number followed
+ by the threads pid. Note that the per cpu idle threads actually
+ have pids of 0 (yes there is more than one pid 0 in an SMP system).
+ To avoid confusion, kgdb numbers these threads with numbers beyond
+ the MAX_PID. That is why you see 32768 above.
+
+6.) A subtle change, we now provide the complete register set for tasks
+ that are active on the other cpus. This allows better trace back on
+ those tasks.
+
+ And, lets mention what we could not fix. Back-trace from all but the
+ thread that we trapped will, most likely, have a bogus entry in it.
+ The problem is that gdb does not recognize the entry code for
+ functions that use "current" near (at all?) the entry. The compiler
+ is putting the "current" decode as the first two instructions of the
+ function where gdb expects to find %ebp changing code. Back trace
+ also has trouble with interrupt frames. I am talking with Daniel
+ Jacobowitz about some way to fix this, but don't hold your breath.
+
+20011220.0050.35
+Major enhancement with this version is the ability to hold one or more
+cpus in an SMP system while allowing the others to continue. Also, by
+default only the current cpu is enabled on single step commands (please
+note that gdb issues single step commands at times other than when you
+use the si command).
+
+Another change is to collect some useful information in
+a global structure called "kgdb_info". You should be able to just:
+
+p kgdb_info
+
+although I have seen cases where the first time this is done gdb just
+prints the first member but prints the whole structure if you then enter
+CR (carriage return or enter). This also works:
+
+p *&kgdb_info
+
+Here is a sample:
+(gdb) p kgdb_info
+$4 = {called_from = 0xc010732c, entry_tsc = 32804123790856, errcode = 0,
+ vector = 3, print_debug_info = 0}
+
+"Called_from" is the return address from the current entry into kgdb.
+Sometimes it is useful to know why you are in kgdb, for example, was
+it an NMI or a real break point? The simple way to interrogate this
+return address is:
+
+l *0xc010732c
+
+which will print the surrounding few lines of source code.
+
+"Entry_tsc" is the cpu TSC on entry to kgdb (useful to compare to the
+kgdb_ts entries).
+
+"errcode" and "vector" are other entry parameters which may be helpful on
+some traps.
+
+"print_debug_info" is the internal debugging kgdb print enable flag. Yes,
+you can modify it.
+
+In SMP systems kgdb_info also includes the "cpus_waiting" structure and
+"hold_on_step":
+
+(gdb) p kgdb_info
+$7 = {called_from = 0xc0112739, entry_tsc = 1034936624074, errcode = 0,
+ vector = 2, print_debug_info = 0, hold_on_sstep = 1, cpus_waiting = {{
+ task = 0x0, hold = 0, regs = 0x0}, {task = 0xc71b8000, hold = 0,
+ regs = 0xc71b9f70}, {task = 0x0, hold = 0, regs = 0x0}, {task = 0x0,
+ hold = 0, regs = 0x0}, {task = 0x0, hold = 0, regs = 0x0}, {task = 0x0,
+ hold = 0, regs = 0x0}, {task = 0x0, hold = 0, regs = 0x0}, {task = 0x0,
+ hold = 0, regs = 0x0}}}
+
+"Cpus_waiting" has an entry for each cpu other than the current one that
+has been stopped. Each entry contains the task_struct address for that
+cpu, the address of the regs for that task and a hold flag. All these
+have the proper typing so that, for example:
+
+p *kgdb_info.cpus_waiting[1].regs
+
+will print the registers for cpu 1.
+
+"Hold_on_sstep" is a new feature with this version and comes up set or
+true. What is means is that whenever kgdb is asked to single step all
+other cpus are held (i.e. not allowed to execute). The flag applies to
+all but the current cpu and, again, can be changed:
+
+p kgdb_info.hold_on_sstep=0
+
+restores the old behavior of letting all cpus run during single stepping.
+
+Likewise, each cpu has a "hold" flag, which if set, locks that cpu out
+of execution. Note that this has some risk in cases where the cpus need
+to communicate with each other. If kgdb finds no cpu available on exit,
+it will push a message thru gdb and stay in kgdb. Note that it is legal
+to hold the current cpu as long as at least one cpu can execute.
+
+20010621.1117.09
+This version implements an event queue. Events are signaled by calling
+a function in the kgdb stub and may be examined from gdb. See EVENTS
+below for details. This version also tighten up the interrupt and SMP
+handling to not allow interrupts on the way to kgdb from a breakpoint
+trap. It is fine to allow these interrupts for user code, but not
+system debugging.
+
+Version
+=======
+
+This version of the kgdb package was developed and tested on
+kernel version 2.4.16. It will not install on any earlier kernels.
+It is possible that it will continue to work on later versions
+of 2.4 and then versions of 2.5 (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.
+
+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. You should test this connection with the two
+machines prior to trying to debug a kernel. Once you have it working,
+on the TARGET machine, enter:
+
+setserial /dev/ttyS0 (or what ever tty you are using)
+
+and record the port and the irq addresses.
+
+On the DEVELOPMENT machine you need to apply the patch for the kgdb
+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 Xconfig" where X is one of "x", "menu", or "". If you are
+configuring in the standard serial driver, it must not be a module.
+Either yes or no is ok, but making the serial driver a module means it
+will initialize after kgdb has set up the UART interrupt code and may
+cause a failure of the control C option discussed below. The configure
+question for the serial driver is under the "Character devices" heading
+and is:
+
+"Standard/generic (8250/16550 and compatible UARTs) serial support"
+
+Go down to the kernel debugging menu item and open it up. Enable the
+kernel kgdb stub code by selecting that item. You can also choose to
+turn on the "-ggdb -O1" compile options. The -ggdb causes the compiler
+to put more debug info (like local symbols) in the object file. On the
+i386 -g and -ggdb are the same so this option just reduces to "O1". The
+-O1 reduces the optimization level. This may be helpful in some cases,
+be aware, however, that this may also mask the problem you are looking
+for.
+
+The baud rate. Default is 115200. What ever you choose be sure that
+the host machine is set to the same speed. I recommend the default.
+
+The port. This is the I/O address of the serial UART that you should
+have gotten using setserial as described above. The standard com1 port
+(3f8) using irq 4 is default . Com2 is 2f8 which by convention uses irq
+3.
+
+The port irq (see above).
+
+Stack overflow test. This option makes a minor change in the trap,
+system call and interrupt code to detect stack overflow and transfer
+control to kgdb if it happens. (Some platforms have this in the base
+line code, but the i386 does not.)
+
+You can also configure the system to recognize the boot option
+"console=kgdb" which if given will cause all console output during
+booting to be put thru gdb as well as other consoles. This option
+requires that gdb and kgdb be connected prior to sending console output
+so, if they are not, a breakpoint is executed to force the connection.
+This will happen before any kernel output (it is going thru gdb, right),
+and will stall the boot until the connection is made.
+
+You can also configure in a patch to SysRq to enable the kGdb SysRq.
+This request generates a breakpoint. Since the serial port irq line is
+set up after any serial drivers, it is possible that this command will
+work when the control C will not.
+
+Save and exit the Xconfig program. Then do "make clean" , "make dep"
+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 your 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/bzImage 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 (see "man
+lilo" for details on how to set this up) on the new kernel on the target
+machine so that it will boot! Then boot the kernel on the target
+machine.
+
+On the DEVELOPMENT machine, create a file called .gdbinit in the
+directory /usr/src/linux. An example .gdbinit file looks like this:
+
+shell echo -e "\003" >/dev/ttyS0
+set remotebaud 38400 (or what ever speed you have chosen)
+target remote /dev/ttyS0
+
+
+Change the "echo" and "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.
+
+You are now ready to try it out.
+
+Boot your target machine with "kgdb" in the boot command i.e. something
+like:
+
+lilo> test kgdb
+
+or if you also want console output thru gdb:
+
+lilo> test kgdb console=kgdb
+
+You should see the lilo message saying it has loaded the kernel and then
+all output stops. The kgdb stub is trying to connect with gdb. Start
+gdb something like this:
+
+
+On your DEVELOPMENT machine, cd /usr/src/linux and enter "gdb vmlinux".
+When gdb gets the symbols loaded it will read your .gdbinit file and, 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 kgdb 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...
+ 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.
+
+If you have the kgdb console enabled when you continue, gdb will print
+out all the console messages.
+
+The above example caused a breakpoint relatively early in the boot
+process. For the i386 kgdb it is possible to code a break instruction
+as the first C-language point in init/main.c, i.e. as the first instruction
+in start_kernel(). This could be done as follows:
+
+#include <asm/kgdb.h>
+ breakpoint();
+
+This breakpoint() is really a function that sets up the breakpoint and
+single-step hardware trap cells and then executes a breakpoint. Any
+early hard coded breakpoint will need to use this function. Once the
+trap cells are set up they need not be set again, but doing it again
+does not hurt anything, so you don't need to be concerned about which
+breakpoint is hit first. Once the trap cells are set up (and the kernel
+sets them up in due course even if breakpoint() is never called) the
+macro:
+
+BREAKPOINT;
+
+will generate an inline breakpoint. This may be more useful as it stops
+the processor at the instruction instead of in a function a step removed
+from the location of interest. In either case <asm/kgdb.h> must be
+included to define both breakpoint() and BREAKPOINT.
+
+Triggering kgdbstub at other times
+==================================
+
+Often you don't need to enter the debugger until much later in the boot
+or even after the machine has been running for some time. Once the
+kernel is booted and interrupts are on, you can force the system to
+enter the debugger by sending a control C to the debug port. This is
+what the first line of the recommended .gdbinit file does. This allows
+you to start gdb any time after the system is up as well as when the
+system is already at a break point. (In the case where the system is
+already at a break point the control C is not needed, however, it will
+be ignored by the target so no harm is done. Also note the the echo
+command assumes that the port speed is already set. This will be true
+once gdb has connected, but it is best to set the port speed before you
+run gdb.)
+
+Another simple way to do this is to put the following file in you ~/bin
+directory:
+
+#!/bin/bash
+echo -e "\003" > /dev/ttyS0
+
+Here, the ttyS0 should be replaced with what ever port you are using.
+The "\003" is control-C. Once you are connected with gdb, you can enter
+control-C at the command prompt.
+
+An alternative way to get control to the debugger is to enable the kGdb
+SysRq command. Then you would enter Alt-SysRq-g (all three keys at the
+same time, but push them down in the order given). To refresh your
+memory of the available SysRq commands try Alt-SysRq-=. Actually any
+undefined command could replace the "=", but I like to KNOW that what I
+am pushing will never be defined.
+
+Debugging hints
+===============
+
+You can break into the target machine at any time from the development
+machine by typing ^C (see above paragraph). 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. The exploratory breakpoints can be
+entered either thru gdb or hard coded into the source. This is very
+handy if you do something like:
+
+if (<it hurts>) BREAKPOINT;
+
+
+There is a copy of an e-mail in the Documentation/i386/kgdb/ directory
+(debug-nmi.txt) 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, first clear all breakpoints and
+continue, then 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. Make sure it works
+in both directions. 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/kgdb_stub.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/arch/i386/lib/kgdb_serial.c This is
+the code that talks to the serial port on the target side. There might
+be a problem there. In particular there is a section of this code that
+tests the UART which will tell you what UART you have if you define
+"PRNT" (just remove "_off" from the #define PRNT_off). To view this
+report you will need to boot the system without any beakpoints. This
+allows the kernel to run to the point where it calls kgdb to set up
+interrupts. At this time kgdb will test the UART and print out the type
+it finds. (You need to wait so that the printks are actually being
+printed. Early in the boot they are cached, waiting for the console to
+be enabled. Also, if kgdb is entered thru a breakpoint it is possible
+to cause a dead lock by calling printk when the console is locked. The
+stub, thus avoids doing printks from break points especially in the
+serial code.) At this time, if the UART fails to do the expected thing,
+kgdb will print out (using printk) information on what failed. (These
+messages will be buried in all the other boot up messages. Look for
+lines that start with "gdb_hook_interrupt:". You may want to use dmesg
+once the system is up to view the log. If this fails or if you still
+don't connect, review your answers for the port address. Use:
+
+setserial /dev/ttyS0
+
+to get the current port and irq information. This command will also
+tell you what the system found for the UART type. The stub recognizes
+the following UART types:
+
+16450, 16550, and 16550A
+
+If you are really desperate you can use printk debugging in the
+kgdbstub code in the target kernel until you get it working. In particular,
+there is a global variable in /usr/src/linux/arch/i386/kernel/kgdb_stub.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. Likewise there are debug printks in the kgdb_serial.c
+code that can be turned on with simple changes in the macro defines.
+
+
+Debugging Loadable Modules
+==========================
+
+This technique comes courtesy of Edouard Parmelan
+<Edouard.Parmelan at 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 when 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
+==========================
+
+kgdb 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 occurred.
+ Prints number of the hardware breakpoint if a hardware breakpoint has
+ occurred.
+
+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
+
+SMP 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 occurred. Threads running on other processor(s) appear
+similar to other non running threads in the 'info threads' output. With
+in the kgdb stub there is a structure "waiting_cpus" in which kgdb
+records the values of "current" and "regs" for each cpu other than the
+one that hit the breakpoint. "current" is a pointer to the task
+structure for the task that cpu is running, while "regs" points to the
+saved registers for the task. This structure can be examined with the
+gdb "p" command.
+
+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
+
+EVENTS
+======
+
+Ever want to know the order of things happening? Which cpu did what and
+when? How did the spinlock get the way it is? Then events are for
+you. Events are defined by calls to an event collection interface and
+saved for later examination. In this case, kgdb events are saved by a
+very fast bit of code in kgdb which is fully SMP and interrupt protected
+and they are examined by using gdb to display them. Kgdb keeps only
+the last N events, where N must be a power of two and is defined at
+configure time.
+
+
+Events are signaled to kgdb by calling:
+
+kgdb_ts(data0,data1)
+
+For each call kgdb records each call in an array along with other info.
+Here is the array def:
+
+struct kgdb_and_then_struct {
+#ifdef CONFIG_SMP
+ int on_cpu;
+#endif
+ long long at_time;
+ int from_ln;
+ char * in_src;
+ void *from;
+ int with_if;
+ int data0;
+ int data1;
+};
+
+For SMP machines the cpu is recorded, for all machines the TSC is
+recorded (gets a time stamp) as well as the line number and source file
+the call was made from. The address of the (from), the "if" (interrupt
+flag) and the two data items are also recorded. The macro kgdb_ts casts
+the types to int, so you can put any 32-bit values here. There is a
+configure option to select the number of events you want to keep. A
+nice number might be 128, but you can keep up to 1024 if you want. The
+number must be a power of two. An "andthen" macro library is provided
+for gdb to help you look at these events. It is also possible to define
+a different structure for the event storage and cast the data to this
+structure. For example the following structure is defined in kgdb:
+
+struct kgdb_and_then_struct2 {
+#ifdef CONFIG_SMP
+ int on_cpu;
+#endif
+ long long at_time;
+ int from_ln;
+ char * in_src;
+ void *from;
+ int with_if;
+ struct task_struct *t1;
+ struct task_struct *t2;
+};
+
+If you use this for display, the data elements will be displayed as
+pointers to task_struct entries. You may want to define your own
+structure to use in casting. You should only change the last two items
+and you must keep the structure size the same. Kgdb will handle these
+as 32-bit ints, but within that constraint you can define a structure to
+cast to any 32-bit quantity. This need only be available to gdb and is
+only used for casting in the display code.
+
+Final Items
+===========
+
+I picked up this code from Amit S. Kale and enhanced it.
+
+If you make some really cool modification to this stuff, or if you
+fix a bug, please let me know.
+
+George Anzinger
+<george at mvista.com>
+
+Amit S. Kale
+<akale at veritas.com>
+
+(First kgdb by David Grothe <dave at gcom.com>)
+
+(modified by Tigran Aivazian <tigran at sco.com>)
+ Putting gdbstub into the kernel config menu.
+
+(modified by Scott Foehner <sfoehner at engr.sgi.com>)
+ Hooks for entering gdbstub at boot time.
+
+(modified by Amit S. Kale <akale at veritas.com>)
+ Threads, ia-32 hw debugging, mp support, console support,
+ nmi watchdog handling.
+
+(modified by George Anzinger <george at mvista.com>)
+ Extended threads to include the idle threads.
+ Enhancements to allow breakpoint() at first C code.
+ Use of module_init() and __setup() to automate the configure.
+ Enhanced the cpu "collection" code to work in early bring up.
+ Added ability to call functions from gdb
+ Print info thread stuff without going back to schedule()
+ Now collect the "other" cpus with a IPI/ NMI.
\ No newline at end of file
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/Documentation/i386/kgdb/loadmodule.sh linux/Documentation/i386/kgdb/loadmodule.sh
--- linux_test/Documentation/i386/kgdb/loadmodule.sh Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/kgdb/loadmodule.sh Thu Aug 22 13:35:52 2002
@@ -0,0 +1,78 @@
+#/bin/sh
+# This script loads a module on a target machine and generates a gdb script.
+# source generated gdb script to load the module file at appropriate addresses
+# in gdb.
+#
+# Usage:
+# Loading the module on target machine and generating gdb script)
+# [foo]$ loadmodule.sh <modulename>
+#
+# Loading the module file into gdb
+# (gdb) source <gdbscriptpath>
+#
+# Modify following variables according to your setup.
+# TESTMACHINE - Name of the target machine
+# GDBSCRIPTS - The directory where a gdb script will be generated
+#
+# Author: Amit S. Kale (akale at veritas.com).
+#
+# If you run into problems, please check files pointed to by following
+# variables.
+# ERRFILE - /tmp/<modulename>.errs contains stderr output of insmod
+# MAPFILE - /tmp/<modulename>.map contains stdout output of insmod
+# GDBSCRIPT - $GDBSCRIPTS/load<modulename> gdb script.
+
+TESTMACHINE=foo
+GDBSCRIPTS=/home/bar
+
+if [ $# -lt 1 ] ; then {
+ echo Usage: $0 modulefile
+ exit
+} ; fi
+
+MODULEFILE=$1
+MODULEFILEBASENAME=`basename $1`
+
+if [ $MODULEFILE = $MODULEFILEBASENAME ] ; then {
+ MODULEFILE=`pwd`/$MODULEFILE
+} fi
+
+ERRFILE=/tmp/$MODULEFILEBASENAME.errs
+MAPFILE=/tmp/$MODULEFILEBASENAME.map
+GDBSCRIPT=$GDBSCRIPTS/load$MODULEFILEBASENAME
+
+function findaddr() {
+ local ADDR=0x$(echo "$SEGMENTS" | \
+ grep "$1" | sed 's/^[^ ]*[ ]*[^ ]*[ ]*//' | \
+ sed 's/[ ]*[^ ]*$//')
+ echo $ADDR
+}
+
+function checkerrs() {
+ if [ "`cat $ERRFILE`" != "" ] ; then {
+ cat $ERRFILE
+ exit
+ } fi
+}
+
+#load the module
+echo Copying $MODULEFILE to $TESTMACHINE
+rcp $MODULEFILE root@${TESTMACHINE}:
+
+echo Loading module $MODULEFILE
+rsh -l root $TESTMACHINE /sbin/insmod -m ./`basename $MODULEFILE` \
+ > $MAPFILE 2> $ERRFILE
+checkerrs
+
+SEGMENTS=`head -n 11 $MAPFILE | tail -n 10`
+TEXTADDR=$(findaddr "\\.text[^.]")
+LOADSTRING="add-symbol-file $MODULEFILE $TEXTADDR"
+SEGADDRS=`echo "$SEGMENTS" | awk '//{
+ if ($1 != ".text" && $1 != ".this" &&
+ $1 != ".kstrtab" && $1 != ".kmodtab") {
+ print " -s " $1 " 0x" $3 " "
+ }
+}'`
+LOADSTRING="$LOADSTRING $SEGADDRS"
+echo Generating script $GDBSCRIPT
+echo $LOADSTRING > $GDBSCRIPT
Binary files linux_test/Kerntypes and linux/Kerntypes differ
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/MAINTAINERS linux/MAINTAINERS
--- linux_test/MAINTAINERS Wed Aug 14 11:37:11 2002
+++ linux/MAINTAINERS Thu Aug 22 13:35:53 2002
@@ -886,6 +886,12 @@
W: http://kbuild.sourceforge.net
S: Maintained
+KGDB FOR I386 PLATFORM
+P: George Anzinger
+M: george at mvista.com
+L: linux-net at vger.kernel.org
+S: Supported
+
KERNEL NFSD
P: Neil Brown
M: neilb at cse.unsw.edu.au
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/Makefile linux/arch/i386/Makefile
--- linux_test/arch/i386/Makefile Wed Aug 14 11:37:45 2002
+++ linux/arch/i386/Makefile Thu Aug 22 13:35:53 2002
@@ -86,6 +86,14 @@
CFLAGS += -march=i586
endif
+ifdef CONFIG_KGDB
+CFLAGS :=$(CFLAGS:-fomit-frame-pointer=)
+CFLAGS += -g
+ifdef CONFIG_KGDB_MORE
+ CFLAGS += $(shell echo $(CONFIG_KGDB_OPTIONS) | sed -e 's/"//g')
+endif
+endif
+
HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
SUBDIRS += arch/i386/kernel arch/i386/mm arch/i386/lib
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/config.in linux/arch/i386/config.in
--- linux_test/arch/i386/config.in Wed Aug 21 18:14:45 2002
+++ linux/arch/i386/config.in Thu Aug 22 13:39:32 2002
@@ -540,6 +540,39 @@
bool ' Load all symbols for debugging' CONFIG_KALLSYMS
fi
bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER
+ bool 'Include kgdb kernel debugger' CONFIG_KGDB
+ if [ "$CONFIG_KGDB" != "n" ]; then
+ choice 'Debug serial port BAUD' \
+ "9600 CONFIG_KGDB_9600BAUD \
+ 19200 CONFIG_KGDB_19200BAUD \
+ 38400 CONFIG_KGDB_38400BAUD \
+ 57600 CONFIG_KGDB_57600BAUD \
+ 115200 CONFIG_KGDB_115200BAUD" 115200
+ hex 'hex I/O port address of the debug serial port' CONFIG_KGDB_PORT 3f8
+ int 'IRQ of the debug serial port' CONFIG_KGDB_IRQ 4
+ define_bool CONFIG_DEBUG_INFO y
+ bool 'Add any additional compile options' CONFIG_KGDB_MORE
+ if [ "$CONFIG_KGDB_MORE" = "y" ] ; then
+ string 'Additional compile arguments' CONFIG_KGDB_OPTIONS "-O1"
+ fi
+ if [ "$CONFIG_SMP" = "y" ] ; then
+ int 'Number of CPUs' CONFIG_NO_KGDB_CPUS 2
+ fi
+ bool 'Enable kgdb time stamp macros?' CONFIG_KGDB_TS
+ if [ "$CONFIG_KGDB_TS" = "y" ] ; then
+ choice 'Max number of time stamps to save?' \
+ "64 CONFIG_KGDB_TS_64 \
+ 128 CONFIG_KGDB_TS_128 \
+ 256 CONFIG_KGDB_TS_256 \
+ 512 CONFIG_KGDB_TS_512 \
+ 1024 CONFIG_KGDB_TS_1024" 128
+ fi
+ bool 'Turn on kernel stack overflow testing?' CONFIG_STACK_OVERFLOW_TEST
+ bool 'Enable serial console thru kgdb port' CONFIG_KGDB_CONSOLE
+ bool 'Turn on SysRq "G" command to do a break?' CONFIG_KGDB_SYSRQ
+ if [ "$CONFIG_KGDB_SYSRQ" != "n" ] ; then
+ define_bool CONFIG_MAGIC_SYSRQ y
+ fi
fi
endmenu
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- linux_test/arch/i386/kernel/Makefile Tue May 21 13:18:35 2002
+++ linux/arch/i386/kernel/Makefile Thu Aug 22 13:35:53 2002
@@ -33,6 +33,7 @@
obj-$(CONFIG_MCA) += mca.o
obj-$(CONFIG_MTRR) += mtrr.o
+obj-$(CONFIG_KGDB) += kgdb_stub.o
obj-$(CONFIG_X86_MSR) += msr.o
obj-$(CONFIG_X86_CPUID) += cpuid.o
obj-$(CONFIG_MICROCODE) += microcode.o
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- linux_test/arch/i386/kernel/entry.S Wed Aug 14 11:37:52 2002
+++ linux/arch/i386/kernel/entry.S Thu Aug 22 13:35:53 2002
@@ -45,6 +45,18 @@
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/smp.h>
+ /* We do not recover from a stack overflow, but at least
+ * we know it happened and should be able to track it down.
+ */
+#ifdef CONFIG_STACK_OVERFLOW_TEST
+#define STACK_OVERFLOW_TEST \
+ testl $7680,%esp; \
+ jnz 10f; \
+ call SYMBOL_NAME(stack_overflow); \
+10:
+#else
+#define STACK_OVERFLOW_TEST
+#endif
EBX = 0x00
ECX = 0x04
@@ -115,7 +127,8 @@
pushl %ebx; \
movl $(__KERNEL_DS),%edx; \
movl %edx,%ds; \
- movl %edx,%es;
+ movl %edx,%es; \
+ STACK_OVERFLOW_TEST
#define RESTORE_ALL \
popl %ebx; \
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/kernel/kgdb_stub.c linux/arch/i386/kernel/kgdb_stub.c
--- linux_test/arch/i386/kernel/kgdb_stub.c Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/kernel/kgdb_stub.c Mon Aug 5 16:50:06 2002
@@ -0,0 +1,2222 @@
+/*
+ *
+ * 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 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: David Grothe <dave at gcom.com>
+ * ModuleState: Experimental $
+ *
+ * NOTES: See Below $
+ *
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Compatibility with 2.1.xx kernel by David Grothe <dave at gcom.com>
+ *
+ * Changes to allow auto initilization. All that is needed is that it
+ * be linked with the kernel and a break point (int 3) be executed.
+ * The header file <asm/kgdb.h> defines BREAKPOINT to allow one to do
+ * this. It should also be possible, once the interrupt system is up, to
+ * call putDebugChar("+"). Once this is done, the remote debugger should
+ * get our attention by sending a ^C in a packet. George Anzinger
+ * <george at mvista.com>
+ * Integrated into 2.2.5 kernel by Tigran Aivazian <tigran at sco.com>
+ * Added thread support, support for multiple processors,
+ * support for ia-32(x86) hardware debugging.
+ * Amit S. Kale ( akale at 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
+ *
+ ****************************************************************************/
+#define KGDB_VERSION "<20020805.1650.06>"
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/string.h> /* for strcpy */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/vm86.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <asm/kgdb_local.h>
+#include <linux/list.h>
+#include <asm/atomic.h>
+#include <asm/processor.h>
+#include <linux/irq.h>
+#include <asm/desc.h>
+/* RTAI support needs us to really stop/start interrupts */
+#define RTAI
+#ifdef hard_save_flags
+#undef cli()
+#undef sti()
+#undef save_flags(x)
+#undef restore_flags(x)
+#define sti() hard_sti()
+#define cli() hard_cli()
+#define save_flags(x) hard_save_flags(x)
+#define restore_flags(x) hard_restore_flags(x)
+#endif
+/************************************************************************
+ *
+ * external low-level support routines
+ */
+typedef void (*Function)(void); /* pointer to a function */
+
+/* Thread reference */
+typedef unsigned char threadref[8];
+
+extern void 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
+
+char * kgdb_version=KGDB_VERSION;
+
+/* debug > 0 prints ill-formed commands in valid packets & checksum errors */
+int debug_regs = 0; /* set to non-zero to print registers */
+
+/* filled in by an external module */
+char *gdb_module_offsets;
+
+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 *************************/
+/*
+ * Put the error code here just in case the user cares.
+ * Likewise, the vector number here (since GDB only gets the signal
+ * number through the usual means, and that's not very specific).
+ * The called_from is the return address so he can tell how we entered kgdb.
+ * This will allow him to seperate out the various possible entries.
+ */
+#define REMOTE_DEBUG 0 // set != to turn on printing (also available in info)
+
+
+#ifdef CONFIG_SMP
+void smp_send_nmi_allbutself(void);
+#define IF_SMP(x) x
+#undef MAX_NO_CPUS
+#ifndef CONFIG_NO_KGDB_CPUS
+#define CONFIG_NO_KGDB_CPUS 2
+#endif
+#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS
+#define hold_init hold_on_sstep: 1,
+#else
+#define IF_SMP(x)
+#define hold_init
+#undef MAX_NO_CPUS
+#define MAX_NO_CPUS 1
+#endif
+struct kgdb_info {
+ int used_malloc;
+ void * called_from;
+ long long entry_tsc;
+ int errcode;
+ int vector;
+ int print_debug_info;
+#ifdef CONFIG_SMP
+ int hold_on_sstep;
+ struct {
+ volatile struct task_struct *task;
+ int pid;
+ int hold;
+ struct pt_regs *regs;
+ } cpus_waiting[MAX_NO_CPUS];
+#endif
+} kgdb_info = {hold_init print_debug_info: REMOTE_DEBUG};
+
+#define used_m kgdb_info.used_malloc
+/*
+ * This is little area we set aside to contain the stack we
+ * need to build to allow gdb to call functions. We use one
+ * per cpu to avoid locking issues. We will do all this work
+ * with interrupts off so that should take care of the protection
+ * issues.
+ */
+#define LOOKASIDE_SIZE 200 // should be more than enough
+#define MALLOC_MAX 200 // Max malloc size
+struct {
+ unsigned int esp;
+ int array[LOOKASIDE_SIZE];
+} fn_call_lookaside[MAX_NO_CPUS];
+
+static int trap_cpu;
+static unsigned int OLD_esp;
+
+#define END_OF_LOOKASIDE &fn_call_lookaside[trap_cpu].array[LOOKASIDE_SIZE]
+#define IF_BIT 0x200
+#define TF_BIT 0x100
+
+#define MALLOC_ROUND 8-1
+
+static char malloc_array[MALLOC_MAX];
+
+void * malloc(int size)
+{
+
+ if( size <= (MALLOC_MAX - used_m)){
+ int old_used = used_m;
+ used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND));
+ return &malloc_array[old_used];
+ }else{
+ return NULL;
+ }
+}
+/*
+ * Gdb calls functions by pushing agruments, including a return address
+ * on the stack and the adjusting EIP to point to the function. The
+ * whole assumption in GDB is that we are on a different stack than the
+ * one the "user" i.e. code that hit the break point, is on. This, of
+ * course is not true in the kernel. Thus various dodges are needed to
+ * do the call without directly messing with EIP (which we can not change
+ * as it is just a location and not a register. To adjust it would then
+ * require that we move every thing below EIP up or down as needed. This
+ * will not work as we may well have stack relative pointer on the stack
+ * (such as the pointer to regs, for example).
+
+ * So here is what we do:
+ * We detect gdb attempting to store into the stack area and instead, store
+ * into the fn_call_lookaside.array at the same relative location as if it
+ * were the area ESP pointed at. We also trap ESP modifications
+ * and uses these to adjust fn_call_lookaside.esp. On entry
+ * fn_call_lookaside.esp will be set to point at the last entry in
+ * fn_call_lookaside.array. This allows us to check if it has changed, and
+ * if so, on exit, we add the registers we will use to do the move and a
+ * trap/ interrupt return exit sequence. We then adjust the eflags in the
+ * regs array (remember we now have a copy in the fn_call_lookaside.array) to
+ * kill the interrupt bit, AND we change EIP to point at our set up stub.
+ * As part of the register set up we preset the registers to point at the
+ * begining and end of the fn_call_lookaside.array, so all the stub needs to
+ * do is move words from the array to the stack until ESP= the desired value
+ * then do the rti. This will then transfer to the desired function with
+ * all the correct registers. Nifty huh?
+ */
+extern asmlinkage void fn_call_stub(void);
+extern asmlinkage void fn_rtn_stub(void);
+__asm__(
+ "fn_rtn_stub:\n\t"
+ "movl %eax,%esp\n\t"
+ "fn_call_stub:\n\t"
+ "1:\n\t"
+ "addl $-4,%ebx\n\t"
+ "movl (%ebx), %eax\n\t"
+ "pushl %eax\n\t"
+ "cmpl %esp,%ecx\n\t"
+ "jne 1b\n\t"
+ "popl %eax\n\t"
+ "popl %ebx\n\t"
+ "popl %ecx\n\t"
+ "iret \n\t"
+ );
+
+#define gdb_i386vector kgdb_info.vector
+#define gdb_i386errcode kgdb_info.errcode
+#define waiting_cpus kgdb_info.cpus_waiting
+#define remote_debug kgdb_info.print_debug_info
+#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold
+/* gdb locks */
+
+
+#ifdef _raw_read_unlock // must use a name that is "define"ed, not an inline
+//#error "renaming spin"
+#undef spin_lock
+#undef spin_trylock
+#undef spin_unlock
+#define spin_lock _raw_spin_lock
+#define spin_trylock _raw_spin_trylock
+#define spin_unlock _raw_spin_unlock
+#else
+//#error "not renaming spin"
+#endif
+
+#ifdef _SPIN_LOCK_UNLOCKED
+#undef SPIN_LOCK_UNLOCKED
+#undef spin_lock_init
+#undef spin_lock
+#undef spin_is_locked
+#undef spin_trylock
+#undef spin_unlock_wait
+#undef spin_unlock
+
+#define SPIN_LOCK_UNLOCKED _SPIN_LOCK_UNLOCKED
+#define spin_lock_init _spin_lock_init
+#define spin_lock _spin_lock
+#define spin_is_locked _spin_is_locked
+#define spin_trylock _spin_trylock
+#define spin_unlock_wait _spin_unlock_wait
+#define spin_unlock _spin_unlock
+
+#endif
+
+#ifdef CONFIG_SMP
+static int in_kgdb_called;
+static spinlock_t waitlocks[MAX_NO_CPUS] =
+ {[0 ... MAX_NO_CPUS-1]= SPIN_LOCK_UNLOCKED};
+/*
+ * The following array has the thread pointer of each of the "other"
+ * cpus. We make it global so it can be seen by gdb.
+ */
+volatile int in_kgdb_entry_log[MAX_NO_CPUS];
+volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS];
+/*
+static spinlock_t continuelocks[MAX_NO_CPUS];
+*/
+spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED;
+/* waiters on our spinlock plus us */
+static atomic_t spinlock_waiters = ATOMIC_INIT(1);
+static int spinlock_count = 0;
+static int spinlock_cpu = 0;
+/*
+ * Note we use nested spin locks to account for the case where a break
+ * point is encountered when calling a function by user direction from
+ * kgdb. Also there is the memory exception recursion to account for.
+ * Well, yes, but this lets other cpus thru too. Lets add a
+ * cpu id to the lock.
+ */
+#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \
+ spinlock_cpu != smp_processor_id()){\
+ atomic_inc(&spinlock_waiters); \
+ while (! spin_trylock(x)) {\
+ in_kgdb(®s);\
+ }\
+ atomic_dec(&spinlock_waiters); \
+ spinlock_count = 1; \
+ spinlock_cpu = smp_processor_id(); \
+ }else{ \
+ spinlock_count++; \
+ }
+#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x)
+#define NUM_CPUS smp_num_cpus
+#else
+unsigned kgdb_spinlock = 0;
+#define KGDB_SPIN_LOCK(x) --*x
+#define KGDB_SPIN_UNLOCK(x) ++*x
+#define NUM_CPUS 1
+#endif
+
+
+
+
+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);
+}
+
+IF_SMP(static void to_gdb(const char * mess));
+
+/* 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 ((remote_debug ) && (checksum != xmitcsum)) {
+ printk ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n",
+ checksum,xmitcsum,buffer);
+ }
+
+ 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);
+
+ if (remote_debug)
+ printk("R:%s\n", buffer) ;
+}
+
+/* send the packet in buffer. */
+
+
+void putpacket(char * buffer)
+{
+ unsigned char checksum;
+ int count;
+ char ch;
+
+ /* $<packet info>#<checksum>. */
+ do {
+ if (remote_debug)
+ printk("T:%s\n", buffer) ;
+ putDebugChar('$');
+ checksum = 0;
+ count = 0;
+
+ while ((ch=buffer[count])) {
+ putDebugChar(ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ putDebugChar('#');
+ putDebugChar(hexchars[checksum >> 4]);
+ putDebugChar(hexchars[checksum % 16]);
+
+ } while ((getDebugChar() & 0x7f) != '+');
+
+}
+
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+static short error;
+
+
+void debug_error( char * format, char * parm)
+{
+ if (remote_debug) printk (format,parm);
+}
+
+static void print_regs(struct pt_regs *regs)
+{
+ printk("EAX=%08lx ", regs->eax);
+ printk("EBX=%08lx ", regs->ebx);
+ printk("ECX=%08lx ", regs->ecx);
+ printk("EDX=%08lx ", regs->edx);
+ printk("\n");
+ printk("ESI=%08lx ", regs->esi);
+ printk("EDI=%08lx ", regs->edi);
+ printk("EBP=%08lx ", regs->ebp);
+ printk("ESP=%08lx ", (long) ®s->esp);
+ printk("\n");
+ printk(" DS=%08x ", regs->xds);
+ printk(" ES=%08x ", regs->xes);
+ printk(" SS=%08x ", __KERNEL_DS);
+ printk(" FL=%08lx ", regs->eflags);
+ printk("\n");
+ printk(" CS=%08x ", regs->xcs);
+ printk(" IP=%08lx ", regs->eip);
+#if 0
+ printk(" FS=%08x ", regs->fs);
+ printk(" GS=%08x ", regs->gs);
+#endif
+ printk("\n");
+
+
+} /* print_regs */
+
+#define NEW_esp fn_call_lookaside[trap_cpu].esp
+
+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;
+ /* Note, as we are a debugging the kernel, we will always
+ * trap in kernel code, this means no priviledge change,
+ * and so the pt_regs structure is not completely valid. In a non
+ * privilege change trap, only EFLAGS, CS and EIP are put on the stack,
+ * SS and ESP are not stacked, this means that the last 2 elements of
+ * pt_regs is not valid (they would normally refer to the user stack)
+ * also, using regs+1 is no good because you end up will a value that is
+ * 2 longs (8) too high. This used to cause stepping over functions
+ * to fail, so my fix is to use the address of regs->esp, which
+ * should point at the end of the stack frame. Note I have ignored
+ * completely exceptions that cause an error code to be stacked, such
+ * as double fault. Stuart Hughes, Zentropix.
+ * original code: gdb_regs[_ESP] = (int) (regs + 1) ;
+
+ * this is now done on entry and moved to OLD_esp (as well as NEW_esp).
+ */
+ gdb_regs[_ESP] = NEW_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] ;
+ NEW_esp = gdb_regs[_ESP] ; // keep the value
+#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 */
+extern void scheduling_functions_start_here(void);
+extern void scheduling_functions_end_here(void);
+#define first_sched ((unsigned long) scheduling_functions_start_here)
+#define last_sched ((unsigned long) scheduling_functions_end_here)
+
+int thread_list = 0;
+
+void get_gdb_regs(struct task_struct *p,struct pt_regs *regs, int *gdb_regs)
+{
+ unsigned long stack_page;
+ int count = 0;
+ int i;
+ if (!p || p == current) {
+ regs_to_gdb_regs(gdb_regs, regs) ;
+ return;
+ }
+#ifdef CONFIG_SMP
+ for (i = 0; i < MAX_NO_CPUS; i++){
+ if(p == kgdb_info.cpus_waiting[i].task ){
+ regs_to_gdb_regs(gdb_regs,
+ kgdb_info.cpus_waiting[i].regs);
+ gdb_regs[_ESP] = (int)&kgdb_info.cpus_waiting[i].regs->esp;
+
+ return;
+ }
+ }
+#endif
+ memset(gdb_regs, 0, NUMREGBYTES);
+ gdb_regs[_ESP] = p->thread.esp;
+ gdb_regs[_PC] = p->thread.eip;
+ gdb_regs[_EBP] = *(int *)gdb_regs[_ESP];
+ gdb_regs[_EDI] = *(int *)(gdb_regs[_ESP] + 4);
+ gdb_regs[_ESI] = *(int *)(gdb_regs[_ESP] + 8);
+
+/*
+ * This code is to give a more informative notion of where a process
+ * is waiting. It is used only when the user asks for a thread info
+ * list. If he then switches to the thread, s/he will find the task
+ * is in schedule, but a back trace should show the same info we come
+ * up with. This code was shamelessly purloined from process.c. It was
+ * then enhanced to provide more registers than simply the program
+ * counter.
+ */
+
+ if(! thread_list) {
+ return;
+ }
+
+ if ( p->state == TASK_RUNNING)
+ return;
+ stack_page = (unsigned long)p;
+ if (gdb_regs[_ESP] < stack_page || gdb_regs[_ESP] > 8188+stack_page)
+ return;
+ /* include/asm-i386/system.h:switch_to() pushes ebp last. */
+ do {
+ if (gdb_regs[_EBP] < stack_page ||
+ gdb_regs[_EBP] > 8184+stack_page)
+ return;
+ gdb_regs[_PC] = *(unsigned long *) (gdb_regs[_EBP]+4);
+ gdb_regs[_ESP] = gdb_regs[_EBP] + 8;
+ gdb_regs[_EBP] = *(unsigned long *) gdb_regs[_EBP];
+ if (gdb_regs[_PC] < first_sched || gdb_regs[_PC] >= last_sched)
+ return;
+ } while (count++ < 16);
+ return;
+}
+
+
+
+/* Indicate to caller of mem2hex or hex2mem that there has been an
+ error. */
+static volatile int mem_err = 0;
+static volatile int mem_err_expected = 0;
+static volatile int mem_err_cnt = 0;
+static int garbage_loc = -1 ;
+
+int
+get_char (char *addr)
+{
+ return *addr;
+}
+
+void
+set_char ( char *addr, int val, int may_fault)
+{
+ /*
+ * This code traps references to the area mapped to the kernel
+ * stack as given by the regs and, instead, stores to the
+ * fn_call_lookaside[cpu].array
+ */
+ if ( may_fault &&
+ (unsigned int)addr < OLD_esp &&
+ ((unsigned int)addr > (OLD_esp -
+ (unsigned int)LOOKASIDE_SIZE))){
+ addr = (char *)END_OF_LOOKASIDE - ((char *)OLD_esp - addr);
+ }
+ *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 mem_err 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)
+ {
+ mem_err_expected = 1 ;
+ mem_err = 0 ;
+ }
+ for (i=0;i<count;i++) {
+ /* printk("%lx = ", mem) ; */
+
+ ch = get_char (mem++);
+
+ /* printk("%02x\n", ch & 0xFF) ; */
+ if (may_fault && mem_err)
+ {
+ if (remote_debug)
+ printk("Mem fault fetching from addr %lx\n", (long)(mem-1));
+ *buf = 0 ; /* truncate buffer */
+ return (buf);
+ }
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch % 16];
+ }
+ *buf = 0;
+ if (may_fault)
+ mem_err_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 */
+/* NOTE: We use the may fault flag to also indicate if the write is to
+ * the registers (0) or "other" memory (!=0)
+ */
+char* hex2mem( char* buf,
+ char* mem,
+ int count,
+ int may_fault)
+{
+ int i;
+ unsigned char ch;
+
+ if (may_fault)
+ {
+ mem_err_expected = 1 ;
+ mem_err = 0 ;
+ }
+ for (i=0;i<count;i++) {
+ ch = hex(*buf++) << 4;
+ ch = ch + hex(*buf++);
+ set_char (mem++, ch, may_fault);
+
+ if (may_fault && mem_err)
+ {
+ if (remote_debug)
+ printk("Mem fault storing to addr %lx\n", (long)(mem-1));
+ return (mem);
+ }
+ }
+ if (may_fault)
+ mem_err_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);
+}
+#define stubhex(h) hex(h)
+
+static int
+stub_unpack_int (
+ char *buff,
+ int fieldlength)
+{
+ int nibble;
+ int retval = 0;
+
+ while (fieldlength)
+ {
+ nibble = stubhex (*buff++);
+ retval |= nibble;
+ fieldlength--;
+ if (fieldlength)
+ retval = retval << 4;
+ }
+ return retval;
+}
+
+
+static char *
+pack_hex_byte (
+ char *pkt,
+ int byte)
+{
+ *pkt++ = hexchars[(byte >> 4) & 0xf];
+ *pkt++ = hexchars[(byte & 0xf)];
+ return pkt;
+}
+
+#define BUF_THREAD_ID_SIZE 16
+
+static char *
+pack_threadid (
+ char *pkt,
+ threadref *id)
+{
+ char *limit;
+ unsigned char *altid;
+
+ altid = (unsigned char *) id;
+ limit = pkt + BUF_THREAD_ID_SIZE;
+ while (pkt < limit)
+ pkt = pack_hex_byte (pkt, *altid++);
+ return pkt;
+}
+
+static char *
+unpack_byte (
+ char *buf,
+ int *value)
+{
+ *value = stub_unpack_int (buf, 2);
+ return buf + 2;
+}
+
+static char *
+unpack_threadid (
+ char *inbuf,
+ threadref *id)
+{
+ char *altref;
+ char *limit = inbuf + BUF_THREAD_ID_SIZE;
+ int x, y;
+
+ altref = (char *) id;
+
+ while (inbuf < limit)
+ {
+ x = stubhex (*inbuf++);
+ y = stubhex (*inbuf++);
+ *altref++ = (x << 4) | y;
+ }
+ return inbuf;
+}
+
+void
+int_to_threadref (
+ threadref *id,
+ int value)
+{
+ unsigned char *scan;
+
+ scan = (unsigned char *) id;
+ {
+ int i = 4;
+ while (i--)
+ *scan++ = 0;
+ }
+ *scan++ = (value >> 24) & 0xff;
+ *scan++ = (value >> 16) & 0xff;
+ *scan++ = (value >> 8) & 0xff;
+ *scan++ = (value & 0xff);
+}
+
+static int
+threadref_to_int (
+ threadref *ref)
+{
+ int i, value = 0;
+ unsigned char *scan;
+
+ scan = (char *) ref;
+ scan += 4;
+ i = 4;
+ while (i-- > 0)
+ value = (value << 8) | ((*scan++) & 0xff);
+ return value;
+}
+#if defined(sched_find_first_bit)
+extern struct task_struct *kgdb_get_idle(int cpu);
+#define idle_task(cpu) kgdb_get_idle(cpu)
+#else
+#define idle_task(cpu) init_tasks[cpu]
+#endif
+
+
+struct task_struct *
+getthread (
+ int pid)
+{
+ struct task_struct *thread;
+ if ( pid >= PID_MAX &&
+ pid <= (PID_MAX + NUM_CPUS)){
+ return idle_task(pid - PID_MAX);
+ }else{
+ thread = find_task_by_pid(pid);
+ if (thread) {
+ return thread;
+ }
+ for_each_task(thread) {
+ if (thread->pid == pid) {
+ return thread;
+ }
+ }
+ }
+ return NULL;
+}
+
+struct hw_breakpoint {
+ unsigned enabled;
+ unsigned type;
+ unsigned len;
+ unsigned addr;
+} breakinfo[4] = { { enabled:0 }, { enabled:0 }, { enabled:0 }, { enabled:0 }};
+unsigned hw_breakpoint_status;
+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;
+}
+
+#ifdef CONFIG_SMP
+static int in_kgdb_console=0;
+
+int in_kgdb(struct pt_regs *regs)
+{
+ unsigned flags;
+ int cpu = smp_processor_id();
+ in_kgdb_called = 1;
+ if (in_kgdb_console) return 1;
+ if (!spin_is_locked(&kgdb_spinlock)){
+ if ( in_kgdb_here_log[cpu] ){ // we are holding this cpu
+ return 1;
+ }
+ return 0;
+ }
+
+ /* As I see it the only reason not to let all cpus spin on
+ * the same spin_lock is to allow selected ones to proceed.
+ * This would be a good thing, so we leave it this way.
+ * Maybe someday.... Done !
+
+ * in_kgdb() is called from an NMI so we don't pretend
+ * to have any resources, like printk() for example.
+ */
+
+ local_irq_save(flags); /* only local here, to avoid hanging */
+ /*
+ * log arival of this cpu
+ * The NMI keeps on ticking. Protect against recurring more
+ * than once, and ignor the cpu that has the kgdb lock
+ */
+ in_kgdb_entry_log[cpu]++;
+ in_kgdb_here_log[cpu] = regs;
+ if ( cpu == spinlock_cpu || waiting_cpus[cpu].task ){
+ goto exit_in_kgdb;
+ }
+ /*
+ * For protection of the initilization of the spin locks by kgdb
+ * it locks the kgdb spinlock before it gets the wait locks set
+ * up. We wait here for the wait lock to be taken. If the
+ * kgdb lock goes away first?? Well, it could be a slow exit
+ * sequence where the wait lock is removed prior to the kgdb lock
+ * so if kgdb gets unlocked, we just exit.
+ */
+ while (spin_is_locked(&kgdb_spinlock) &&
+ ! spin_is_locked(waitlocks + cpu));
+ if ( !spin_is_locked(&kgdb_spinlock)){
+ goto exit_in_kgdb;
+ }
+ waiting_cpus[cpu].task = current;
+ waiting_cpus[cpu].pid = (current->pid) ?:(PID_MAX + cpu);
+ waiting_cpus[cpu].regs = regs;
+
+ spin_unlock_wait(waitlocks + cpu);
+ /*
+ * log departure of this cpu
+ */
+ waiting_cpus[cpu].task = 0;
+ waiting_cpus[cpu].pid = 0;
+ waiting_cpus[cpu].regs = 0;
+ correct_hw_break();
+ exit_in_kgdb:
+ in_kgdb_here_log[cpu] = 0;
+ local_irq_restore(flags);
+ return 1;
+ /*
+ spin_unlock(continuelocks + smp_processor_id());
+ */
+}
+void smp__in_kgdb(struct pt_regs regs)
+{
+ ack_APIC_irq();
+ in_kgdb(®s);
+}
+#else
+int in_kgdb(struct pt_regs *regs)
+{
+ return (kgdb_spinlock);
+}
+#endif
+
+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 kgdb_handle_exception(int exceptionVector,
+ int signo,
+ int err_code,
+ struct pt_regs *linux_regs)
+{
+ struct task_struct *usethread = NULL;
+ struct task_struct *thread_list_start=0, *thread = NULL;
+ int addr, length;
+ int breakno, breaktype;
+ char *ptr;
+ int newPC;
+ threadref thref;
+ int threadid;
+ int thread_min = PID_MAX+NUM_CPUS;
+ int maxthreads;
+ int nothreads;
+ unsigned long flags;
+ int gdb_regs[NUMREGBYTES/4];
+ int dr6;
+ IF_SMP(int entry_state = 0;) /* 0, ok, 1, no nmi, 2 sync failed */
+#define NO_NMI 1
+#define NO_SYNC 2
+#define regs (*linux_regs)
+#define NUMREGS NUMREGBYTES/4
+ /*
+ * 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)) {
+ printk("ignoring non-kernel exception\n");
+ print_regs(®s);
+ return(0);
+ }
+
+ local_irq_save(flags);
+
+ /* Get kgdb spinlock */
+
+ KGDB_SPIN_LOCK(&kgdb_spinlock);
+ rdtscll(kgdb_info.entry_tsc);
+ /*
+ * We depend on this spinlock and the NMI watch dog to control the
+ * other cpus. They will arrive at "in_kgdb()" as a result of the
+ * NMI and will wait there for the following spin locks to be
+ * released.
+ */
+#ifdef CONFIG_SMP
+
+ if (smp_num_cpus > MAX_NO_CPUS) {
+ printk("kgdb : too many cpus, change MAX_NO_CPUS"
+ " in kgdb_stub and make new kernel.\n");
+ goto exit_just_unlock;
+ }
+ if ( spinlock_count == 1){
+ int time,end_time,dum;
+ int i;
+ int cpu_logged_in[MAX_NO_CPUS] = { [0 ... MAX_NO_CPUS -1]= (0)};
+ if (remote_debug){
+ printk("kgdb : cpu %d entry, syncing others\n",
+ smp_processor_id());
+ }
+ for (i = 0; i < smp_num_cpus; i++) {
+ /*
+ * Use trylock as we may already hold the lock if
+ * we are holding the cpu. Net result is all
+ * locked.
+ */
+ spin_trylock(&waitlocks[i]);
+ }
+ for (i = 0; i < MAX_NO_CPUS; i++) cpu_logged_in[i] = 0;
+ /*
+ * Wait for their arrival. We know the watch dog is active if
+ * in_kgdb() has ever been called, as it is always called on a
+ * watchdog tick.
+ */
+ rdtsc(dum,time);
+ end_time = time + 2; /* Note: we use the High order bits! */
+ i=1;
+ if ( (smp_num_cpus > 1)){
+ int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()];
+ smp_send_nmi_allbutself();
+ while ( i < smp_num_cpus && time != end_time){
+ int j;
+ for (j = 0; j < MAX_NO_CPUS; j++) {
+ if (waiting_cpus[j].task &&
+ ! cpu_logged_in[j]){
+ i++;
+ cpu_logged_in[j] = 1;
+ if (remote_debug){
+ printk("kgdb : cpu %d arrived at kgdb\n",j);
+ }
+ break;
+ }
+ if (!waiting_cpus[j].task &&
+ in_kgdb_here_log[j]){
+
+ int wait=100000;
+ while(wait--);
+ if (!waiting_cpus[j].task &&
+ in_kgdb_here_log[j]){
+ printk("kgdb : cpu %d stall"
+ " in in_kgdb\n",j);
+ i++;
+ cpu_logged_in[j] = 1;
+ waiting_cpus[j].task =
+ (struct task_struct *)1;
+ }
+ }
+ }
+
+ if (in_kgdb_entry_log[smp_processor_id()] >
+ (me_in_kgdb + 10)){
+ break;
+ }
+
+ rdtsc(dum,time);
+ }
+ if (i < smp_num_cpus){
+ printk("kgdb : time out, proceeding without sync\n");
+#if 0
+ printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n",
+ waiting_cpus[0].task != 0,
+ waiting_cpus[1].task != 0);
+ printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n",
+ cpu_logged_in[0],
+ cpu_logged_in[1]);
+ printk("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n",
+ in_kgdb_here_log[0] != 0,
+ in_kgdb_here_log[1] != 0);
+#endif
+ entry_state = NO_SYNC;
+ }else{
+#if 0
+ int ent = in_kgdb_entry_log[smp_processor_id()]-
+ me_in_kgdb;
+ printk("kgdb : sync after %d entries\n",ent);
+#endif
+ }
+ }else{
+ if (remote_debug){
+ printk("kgdb : %d cpus, but watchdog not active\n"
+ "proceeding without locking down other cpus\n",
+ smp_num_cpus );
+ entry_state = NO_NMI;
+ }
+ }
+ }
+#endif
+
+ if (remote_debug)
+ {
+ unsigned long *lp = (unsigned long *) &linux_regs ;
+
+ printk("handle_exception(exceptionVector=%d, "
+ "signo=%d, err_code=%d, linux_regs=%p)\n",
+ exceptionVector, signo, err_code, linux_regs) ;
+ if (debug_regs) {
+ print_regs(®s) ;
+ printk("Stk: %8lx %8lx %8lx %8lx"
+ " %8lx %8lx %8lx %8lx\n",
+ lp[0],lp[1],lp[2],lp[3],
+ lp[4],lp[5],lp[6],lp[7]);
+ printk(" %8lx %8lx %8lx %8lx"
+ " %8lx %8lx %8lx %8lx\n",
+ lp[8],lp[9],lp[10],lp[11],
+ lp[12],lp[13],lp[14],lp[15]);
+ printk(" %8lx %8lx %8lx %8lx "
+ "%8lx %8lx %8lx %8lx\n",
+ lp[16],lp[17],lp[18],lp[19],
+ lp[20],lp[21],lp[22],lp[23]);
+ printk(" %8lx %8lx %8lx %8lx "
+ "%8lx %8lx %8lx %8lx\n",
+ lp[24],lp[25],lp[26],lp[27],
+ lp[28],lp[29],lp[30],lp[31]);
+ }
+ }
+
+ /* Disable hardware debugging while we are in kgdb */
+ /* Get the debug register status register */
+ __asm__("movl %0,%%db7"
+ : /* no output */
+ : "r" (0));
+ asm volatile ("movl %%db6, %0\n"
+ : "=r" (hw_breakpoint_status)
+ : );
+
+ switch (exceptionVector)
+ {
+ case 0: /* divide error */
+ case 1: /* debug exception */
+ case 2: /* NMI */
+ case 3: /* breakpoint */
+ case 4: /* overflow */
+ case 5: /* bounds check */
+ case 6: /* invalid opcode */
+ case 7: /* device not available */
+ case 8: /* double fault (errcode) */
+ case 10: /* invalid TSS (errcode) */
+ case 12: /* stack fault (errcode) */
+ case 16: /* floating point error */
+ case 17: /* alignment check (errcode) */
+ default: /* any undocumented */
+ break ;
+ case 11: /* segment not present (errcode) */
+ case 13: /* general protection (errcode) */
+ case 14: /* page fault (special errcode) */
+ case 19: /* cache flush denied */
+ if (mem_err_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.
+ */
+ mem_err = 1 ; // set mem error flag
+ mem_err_expected = 0 ;
+ mem_err_cnt++ ; // helps in debugging
+ /* make valid address */
+ regs.eax = (long) &garbage_loc ;
+ /* make valid address */
+ regs.edx = (long) &garbage_loc ;
+ if (remote_debug)
+ printk("Return after memory error: mem_err_cnt=%d\n",
+ mem_err_cnt);
+ if (debug_regs)
+ print_regs(®s) ;
+ goto exit_kgdb;
+ }
+ break ;
+ }
+ if (remote_debug)
+ printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id());
+
+ gdb_i386vector = exceptionVector;
+ gdb_i386errcode = err_code ;
+ kgdb_info.called_from = __builtin_return_address (0);
+#ifdef CONFIG_SMP
+ /*
+ * OK, we can now communicate, lets tell gdb about the sync.
+ * but only if we had a problem.
+ */
+ switch (entry_state ) {
+ case NO_NMI:
+ to_gdb("NMI not active, other cpus not stopped\n");
+ break;
+ case NO_SYNC:
+ to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n");
+ default:
+ }
+
+#endif
+/*
+ * Set up the gdb function call area.
+ */
+ trap_cpu = smp_processor_id();
+ OLD_esp = NEW_esp = (int) (&linux_regs->esp);
+
+IF_SMP(once_again:)
+ /* 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 'd' : remote_debug = !(remote_debug); // toggle debug flag
+ printk("Remote debug %s\n",
+ remote_debug ? "on" : "off");
+ break;
+ case 'g' : /* return the value of the CPU registers */
+ get_gdb_regs(usethread, ®s, gdb_regs);
+ 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;
+
+ case 'P' :{ /* set the value of a single CPU register -
+ return OK */
+ /*
+ * For some reason, gdb wants to talk about psudo
+ * registers (greater than 15). These may have
+ * meaning for ptrace, but for us it is safe to
+ * ignor them. We do this by dumping them into
+ * _GS which we also ignor, but do have memory for.
+ */
+ int regno;
+
+ ptr = &remcomInBuffer[1];
+ regs_to_gdb_regs(gdb_regs, ®s) ;
+ if ((!usethread || usethread == current) &&
+ hexToInt (&ptr, ®no) &&
+ *ptr++ == '=' &&
+ (regno >= 0)){
+ regno = (regno >= NUMREGS ? _GS : regno);
+ hex2mem (ptr, (char *)&gdb_regs[regno], 4, 0);
+ gdb_regs_to_regs(gdb_regs, ®s) ;
+ strcpy(remcomOutBuffer,"OK");
+ break;
+ }
+ strcpy (remcomOutBuffer, "E01");
+ break;
+ }
+
+ /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ case 'm' :
+ /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
+ ptr = &remcomInBuffer[1];
+ if (hexToInt(&ptr,&addr) &&
+ (*(ptr++) == ',') &&
+ (hexToInt(&ptr,&length))) {
+ ptr = 0;
+ mem2hex((char*) addr,
+ remcomOutBuffer,
+ length,
+ 1);
+ if (mem_err) {
+ strcpy (remcomOutBuffer, "E03");
+ debug_error ("memory fault\n", NULL);
+ }
+ }
+
+ if (ptr){
+ strcpy(remcomOutBuffer,"E01");
+ debug_error(
+ "malformed read memory command: %s\n",
+ remcomInBuffer);
+ }
+ break;
+
+ /* MAA..AA,LLLL:
+ Write LLLL bytes at address AA.AA return OK */
+ case 'M' :
+ /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
+ ptr = &remcomInBuffer[1];
+ if (hexToInt(&ptr,&addr) &&
+ (*(ptr++) == ',') &&
+ (hexToInt(&ptr,&length)) &&
+ (*(ptr++) == ':')) {
+ hex2mem(ptr, (char*) addr, length, 1);
+
+ if (mem_err) {
+ strcpy (remcomOutBuffer, "E03");
+ debug_error ("memory fault\n", NULL);
+ } else {
+ strcpy(remcomOutBuffer,"OK");
+ }
+
+ ptr = 0;
+ }
+ if (ptr){
+ strcpy(remcomOutBuffer,"E02");
+ debug_error(
+ "malformed write memory command: %s\n",
+ remcomInBuffer);
+ }
+ break;
+
+ /* cAA..AA Continue at address AA..AA(optional) */
+ /* sAA..AA Step one instruction from AA..AA(optional)*/
+ /* D detach, reply OK and then continue */
+ case 'c' :
+ case 's' :
+ case 'D' :
+
+ /* try to read optional parameter,
+ pc unchanged if no parm */
+ ptr = &remcomInBuffer[1];
+ if (hexToInt(&ptr,&addr)){
+ if (remote_debug)
+ printk("Changing EIP to 0x%x\n", 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;
+
+ /* detach is a friendly version of continue. Note that
+ debugging is still enabled (e.g hit control C)
+ until the process that issued an ioctl TIOCGDB
+ terminates
+ */
+ if (remcomInBuffer[0] == 'D') {
+ strcpy(remcomOutBuffer,"OK");
+ putpacket(remcomOutBuffer);
+ }
+
+ if (remote_debug)
+ {
+ printk("Resuming execution\n") ;
+ print_regs(®s) ;
+ }
+ asm volatile ("movl %%db6, %0\n"
+ : "=r" (dr6)
+ : );
+ if (!(dr6 & 0x4000)) {
+ for (breakno = 0; breakno < 4; ++breakno) {
+ if (dr6 & (1 << breakno) &&
+ (breakinfo[breakno].type == 0)) {
+ /* Set restore flag */
+ regs.eflags |= 0x10000;
+ break;
+ }
+ }
+ }
+ correct_hw_break();
+ asm volatile (
+ "movl %0, %%db6\n"
+ :
+ : "r" (0) );
+ goto exit_kgdb;
+
+ /* kill the program */
+ case 'k' : /* do nothing */
+ break;
+
+ /* query */
+ case 'q' :
+ switch (remcomInBuffer[1]) {
+ case 'L':
+ /* List threads */
+ thread_list = 2;
+ thread_list_start = (usethread ? :current);
+ unpack_byte(remcomInBuffer+3, &maxthreads);
+ unpack_threadid(remcomInBuffer+5, &thref);
+ do{
+ int buf_thread_limit =
+ (BUFMAX-22)/BUF_THREAD_ID_SIZE;
+ if (maxthreads > buf_thread_limit){
+ maxthreads = buf_thread_limit;
+ }
+ }while (0);
+ 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+NUM_CPUS;
+ threadid++ ){
+ thread = getthread(threadid);
+ if (thread) {
+ int_to_threadref(&thref,
+ threadid);
+ pack_threadid(remcomOutBuffer +
+ 21+nothreads*16,
+ &thref);
+ nothreads++;
+ if (thread_min > threadid)
+ thread_min = threadid;
+ }
+ }
+
+ if (threadid == PID_MAX+NUM_CPUS) {
+ remcomOutBuffer[4] = '1';
+ }
+ pack_hex_byte(remcomOutBuffer+2, nothreads);
+ remcomOutBuffer[21+nothreads*16] = '\0';
+ break;
+
+ case 'C':
+ /* Current thread id */
+ remcomOutBuffer[0] = 'Q';
+ remcomOutBuffer[1] = 'C';
+ threadid = current->pid;
+ if ( !threadid ){
+ /*
+ * idle thread
+ */
+ for (threadid = PID_MAX;
+ threadid < PID_MAX+NUM_CPUS;
+ threadid++){
+ if ( current ==
+ idle_task(threadid -
+ PID_MAX))
+ break;
+ }
+ }
+ int_to_threadref(&thref, threadid);
+ pack_threadid(remcomOutBuffer+2, &thref);
+ remcomOutBuffer[18] = '\0';
+ break;
+
+ case 'E':
+ /* Print exception info */
+ printexceptioninfo(exceptionVector,
+ err_code,
+ remcomOutBuffer);
+ break;
+ }
+ break;
+
+ /* task related */
+ case 'H' :
+ switch (remcomInBuffer[1]) {
+ case 'g':
+ ptr = &remcomInBuffer[2];
+ hexToInt(&ptr, &threadid);
+ thread = getthread(threadid);
+ if (!thread) {
+ remcomOutBuffer[0] = 'E';
+ remcomOutBuffer[1] = '\0';
+ break;
+ }
+ /*
+ * Just in case I forget what this is all about,
+ * the "thread info" command to gdb causes it
+ * to ask for a thread list. It then switches
+ * to each thread and asks for the registers.
+ * For this (and only this) usage, we want to
+ * fudge the registers of tasks not on the run
+ * list (i.e. waiting) to show the routine that
+ * called schedule. Also, gdb, is a minimalist
+ * in that if the current thread is the last
+ * it will not re-read the info when done.
+ * This means that in this case we must show
+ * the real registers. So here is how we do it:
+ * Each entry we keep track of the min
+ * thread in the list (the last that gdb will)
+ * get info for. We also keep track of the
+ * starting thread.
+ * "thread_list" is cleared when switching back
+ * to the min thread if it is was current, or
+ * if it was not current, thread_list is set
+ * to 1. When the switch to current comes,
+ * if thread_list is 1, clear it, else do
+ * nothing.
+ */
+ usethread = thread;
+ if ( (thread_list == 1) &&
+ (thread == thread_list_start)){
+ thread_list = 0;
+ }
+ if ( thread_list && (threadid == thread_min)){
+ if (thread == thread_list_start){
+ thread_list = 0;
+ }else{
+ thread_list = 1;
+ }
+ }
+ /* 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';
+ if (thread_min > threadid)
+ thread_min = threadid;
+ } else {
+ remcomOutBuffer[0] = 'E';
+ remcomOutBuffer[1] = '\0';
+ }
+ break;
+
+ case 'Y':
+ ptr = &remcomInBuffer[1];
+ hexToInt(&ptr, &breakno);
+ ptr++;
+ hexToInt(&ptr, &breaktype);
+ ptr++;
+ hexToInt(&ptr, &length);
+ ptr++;
+ hexToInt(&ptr, &addr);
+ if (set_hw_break(breakno & 0x3,
+ breaktype & 0x3 ,
+ length & 0x3, addr)== 0) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "ERROR");
+ }
+ break;
+
+ /* Remove hardware breakpoint */
+ case 'y':
+ ptr = &remcomInBuffer[1];
+ hexToInt(&ptr, &breakno);
+ if (remove_hw_break(breakno & 0x3) == 0) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "ERROR");
+ }
+ break;
+
+
+ } /* switch */
+
+ /* reply to the request */
+ putpacket(remcomOutBuffer);
+ } /* while(1==1) */
+ /*
+ * reached by goto only.
+ */
+ exit_kgdb:
+ /*
+ * Here is where we set up to trap a gdb function call. NEW_esp
+ * will be changed if we are trying to do this. We handle both
+ * adding and subtracting, thus allowing gdb to put grung on
+ * the stack which it removes later.
+ */
+ if ( NEW_esp != OLD_esp){
+ int * ptr = END_OF_LOOKASIDE;
+ if (NEW_esp < OLD_esp )
+ ptr -= (OLD_esp - NEW_esp)/sizeof(int);
+ *--ptr = linux_regs->eflags;
+ *--ptr = linux_regs->xcs;
+ *--ptr = linux_regs->eip;
+ *--ptr = linux_regs->ecx;
+ *--ptr = linux_regs->ebx;
+ *--ptr = linux_regs->eax;
+ linux_regs->ecx = NEW_esp - (sizeof(int) * 6);
+ linux_regs->ebx = (unsigned int)END_OF_LOOKASIDE;
+ if (NEW_esp < OLD_esp ){
+ linux_regs->eip = (unsigned int)fn_call_stub;
+ }else{
+ linux_regs->eip = (unsigned int)fn_rtn_stub;
+ linux_regs->eax = NEW_esp;
+ }
+ linux_regs->eflags &= ~(IF_BIT | TF_BIT);
+ }
+#ifdef CONFIG_SMP
+ /*
+ * Release gdb wait locks
+ * Sanity check time. Must have at least one cpu to run. Also single
+ * step must not be done if the current cpu is on hold.
+ */
+ if (spinlock_count == 1){
+ int ss_hold = (regs.eflags & 0x100) && kgdb_info.hold_on_sstep;
+ int cpu_avail = 0;
+ int i;
+
+ for (i = 0; i < smp_num_cpus; i++) {
+ if (!hold_cpu(i)){
+ cpu_avail = 1;
+ }
+ }
+ if ( ! cpu_avail) {
+ to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n");
+ goto once_again;
+ }
+ if ( hold_cpu(smp_processor_id()) &&
+ (regs.eflags & 0x100)) {
+ to_gdb("Current cpu must be unblocked to single step\n");
+ goto once_again;
+ }
+ if ( !(ss_hold)){
+ int i;
+ for (i = 0; i < smp_num_cpus; i++) {
+ if ( ! hold_cpu(i) ){
+ spin_unlock(&waitlocks[i]);
+ }
+ }
+ }else{
+ spin_unlock(&waitlocks[smp_processor_id()]);
+ }
+ /* Release kgdb spinlock */
+ KGDB_SPIN_UNLOCK(&kgdb_spinlock);
+ /*
+ * If this cpu is on hold, this is where we
+ * do it. Note, the NMI will pull us out of here,
+ * but will return as the above lock is not held.
+ * We will stay here till another cpu releases the lock for us.
+ */
+ spin_unlock_wait(waitlocks + smp_processor_id());
+ local_irq_restore(flags) ;
+ return(0) ;
+ }
+ exit_just_unlock:
+#endif
+ /* Release kgdb spinlock */
+ KGDB_SPIN_UNLOCK(&kgdb_spinlock);
+ local_irq_restore(flags) ;
+ return(0) ;
+}
+
+
+/* this function is used to set up exception handlers for tracing and
+ * breakpoints.
+ * This function is not needed as the above line does all that is needed.
+ * We leave it for backward compatitability...
+ */
+void set_debug_traps(void)
+{
+ /*
+ * linux_debug_hook is defined in traps.c. We store a pointer
+ * to our own exception handler into it.
+
+ * But really folks, every hear of labeled common, an old Fortran
+ * concept. Lots of folks can reference it and it is define if
+ * anyone does. Only one can initialize it at link time. We do
+ * this with the hook. See the statement above. No need for any
+ * executable code and it is ready as soon as the kernel is
+ * loaded. Very desirable in kernel debugging.
+
+ 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. */
+/* But really, just use the BREAKPOINT macro. We will handle the int stuff
+ */
+
+#ifdef later
+/*
+ * possibly we should not go thru the traps.c code at all? Someday.
+ */
+void do_kgdb_int3(struct pt_regs * regs, long error_code)
+{
+ kgdb_handle_exception(3,5,error_code,regs);
+ return;
+}
+#endif
+#ifdef CONFIG_STACK_OVERFLOW_TEST
+#include <asm/kgdb.h>
+asmlinkage void stack_overflow(void)
+{
+#ifdef BREAKPOINT
+ BREAKPOINT;
+#else
+ printk("Kernel stack overflow, looping forever\n");
+#endif
+ while(1) {
+ }
+}
+#endif
+#ifdef CONFIG_SMP
+
+char gdbconbuf[BUFMAX];
+
+static void kgdb_gdb_message(const char *s, unsigned count)
+{
+ int i;
+ int wcount;
+ char *bufptr;
+ /*
+ * This takes care of NMI while spining out chars to gdb
+ */
+ in_kgdb_console=1;
+ 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);
+
+ }
+ in_kgdb_console=0;
+}
+static void to_gdb(const char *s)
+{
+ int count = 0;
+ while ( s[count] && (count++ < BUFMAX));
+ kgdb_gdb_message(s,count);
+}
+#endif
+#ifdef CONFIG_KGDB_CONSOLE
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+
+void kgdb_console_write(struct console *co, const char *s,
+ unsigned count)
+{
+
+ if (gdb_i386vector == -1) {
+ /*
+ * We have not yet talked to gdb. What to do...
+ * lets break, on continue we can do the write.
+ * But first tell him whats up. Uh, well no can do,
+ * as this IS the console. Oh well...
+ * We do need to wait or the messages will be lost.
+ * Other option would be to tell the above code to
+ * ignore this breakpoint and do an auto return,
+ * but that might confuse gdb. Also this happens
+ * early enough in boot up that we don't have the traps
+ * set up yet, so...
+ */
+ breakpoint();
+ }
+ kgdb_gdb_message(s,count);
+}
+
+static kdev_t kgdb_console_device(struct console *c);
+
+/*
+ * ------------------------------------------------------------
+ * Serial KGDB driver
+ * ------------------------------------------------------------
+ */
+
+static struct console kgdbcons = {
+ name: "kgdb",
+ write: kgdb_console_write,
+#ifdef CONFIG_KGDB_USER_CONSOLE
+ device: kgdb_console_device,
+#endif
+ flags: CON_PRINTBUFFER | CON_ENABLED,
+ index: -1,
+};
+
+/*
+ * The trick here is that this file gets linked before printk.o
+ * That means we get to peer at the console info in the command
+ * line before it does. If we are up, we register, otherwise,
+ * do nothing. By returning 0, we allow printk to look also.
+ */
+static int kgdb_console_enabled;
+
+int __init kgdb_console_init(char *str)
+{
+ if (strncmp(str,"kgdb",4) == 0){
+ register_console(&kgdbcons);
+ kgdb_console_enabled = 1;
+ }
+ return 0; /* let others look at the string */
+}
+__setup("console=",kgdb_console_init);
+
+#ifdef CONFIG_KGDB_USER_CONSOLE
+/* This stuff sort of works, but it knocks out telnet devices
+ * we are leaving it here in case we (or you) find time to figure it out
+ * better..
+ */
+
+/*
+ * We need a real char device as well for when the console is opened for user
+ * space activities.
+ */
+
+static int kgdb_consdev_open(struct inode * inode, struct file * file)
+{
+ return 0;
+}
+
+static ssize_t kgdb_consdev_write(struct file * file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ int size, ret = 0;
+ static char kbuf[128];
+ static DECLARE_MUTEX(sem);
+
+ /* We are not reentrant... */
+ if (down_interruptible(&sem))
+ return -ERESTARTSYS;
+
+ while (count > 0) {
+ /* need to copy the data from user space */
+ size = count;
+ if (size > sizeof(kbuf))
+ size = sizeof(kbuf);
+ if (copy_from_user (kbuf, buf, size)) {
+ ret = -EFAULT;
+ break;;
+ }
+ kgdb_console_write(&kgdbcons, kbuf, size);
+ count -= size;
+ ret += size;
+ buf += size;
+ }
+
+ up(&sem);
+
+ return ret;
+}
+
+struct file_operations kgdb_consdev_fops = {
+ open: kgdb_consdev_open,
+ write: kgdb_consdev_write
+};
+static kdev_t kgdb_console_device(struct console *c)
+{
+ return MKDEV(TTYAUX_MAJOR, 1);
+}
+
+/*
+ * This routine gets called from the serial stub in the i386/lib
+ * This is so it is done late in bring up (just before the console open).
+ */
+void kgdb_console_finit(void)
+{
+ if (kgdb_console_enabled){
+ char * cptr = cdevname(MKDEV(TTYAUX_MAJOR,1));
+ char * cp = cptr;
+ while ( *cptr && *cptr != '(') cptr++;
+ *cptr = 0;
+ unregister_chrdev(TTYAUX_MAJOR,cp);
+ register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops);
+ }
+}
+#endif
+#endif
+#ifdef CONFIG_KGDB_TS
+#include <asm/msr.h> /* time stamp code */
+#include <asm/hardirq.h> /* in_interrupt */
+#ifdef CONFIG_KGDB_TS_64
+#define DATA_POINTS 64
+#endif
+#ifdef CONFIG_KGDB_TS_128
+#define DATA_POINTS 128
+#endif
+#ifdef CONFIG_KGDB_TS_256
+#define DATA_POINTS 256
+#endif
+#ifdef CONFIG_KGDB_TS_512
+#define DATA_POINTS 512
+#endif
+#ifdef CONFIG_KGDB_TS_1024
+#define DATA_POINTS 1024
+#endif
+#ifndef DATA_POINTS
+#define DATA_POINTS 128 /* must be a power of two */
+#endif
+#define INDEX_MASK (DATA_POINTS - 1)
+#if (INDEX_MASK & DATA_POINTS)
+#error "CONFIG_KGDB_TS_COUNT must be a power of 2"
+#endif
+struct kgdb_and_then_struct {
+#ifdef CONFIG_SMP
+ int on_cpu;
+#endif
+ struct task_struct *task;
+ long long at_time;
+ int from_ln;
+ char * in_src;
+ void *from;
+ int *with_pif;
+ int data0;
+ int data1;
+};
+struct kgdb_and_then_struct2 {
+#ifdef CONFIG_SMP
+ int on_cpu;
+#endif
+ struct task_struct *task;
+ long long at_time;
+ int from_ln;
+ char * in_src;
+ void *from;
+ int *with_pif;
+ struct task_struct *t1;
+ struct task_struct *t2;
+};
+struct kgdb_and_then_struct kgdb_data[DATA_POINTS];
+
+struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0];
+int kgdb_and_then_count;
+#ifndef preempt_get_count
+#define preempt_get_count() (0)
+#endif
+
+void kgdb_tstamp(int line, char * source, int data0, int data1)
+{
+ static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED;
+ int flags;
+ local_irq_save(flags);
+ spin_lock(&ts_spin);
+ rdtscll(kgdb_and_then->at_time);
+#ifdef CONFIG_SMP
+ kgdb_and_then->on_cpu = smp_processor_id();
+#endif
+ kgdb_and_then->task = current;
+ kgdb_and_then->from_ln = line;
+ kgdb_and_then->in_src = source;
+ kgdb_and_then->from = __builtin_return_address(0);
+ kgdb_and_then->with_pif = (int *)(((flags & IF_BIT) >> 9)|
+ (in_interrupt()? 2:0)|
+ (preempt_get_count() << 4));
+ kgdb_and_then->data0 = data0;
+ kgdb_and_then->data1 = data1;
+ kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK];
+ spin_unlock(&ts_spin);
+ local_irq_restore(flags);
+#ifdef CONFIG_PREEMPT
+
+#endif
+ return;
+}
+#endif
+typedef int gdb_debug_hook(int exceptionVector,
+ int signo,
+ int err_code,
+ struct pt_regs *linux_regs) ;
+gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; // histerical reasons...
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/kernel/nmi.c linux/arch/i386/kernel/nmi.c
--- linux_test/arch/i386/kernel/nmi.c Wed Aug 14 11:37:54 2002
+++ linux/arch/i386/kernel/nmi.c Thu Aug 22 13:35:53 2002
@@ -24,8 +24,16 @@
#include <asm/smp.h>
#include <asm/mtrr.h>
#include <asm/mpspec.h>
-
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#ifdef CONFIG_SMP
+unsigned int nmi_watchdog = NMI_IO_APIC;
+#else
+unsigned int nmi_watchdog = NMI_LOCAL_APIC;
+#endif
+#else
unsigned int nmi_watchdog = NMI_NONE;
+#endif
static unsigned int nmi_hz = HZ;
unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */
extern void show_registers(struct pt_regs *regs);
@@ -47,28 +55,30 @@
int __init check_nmi_watchdog (void)
{
irq_cpustat_t tmp[NR_CPUS];
- int j, cpu;
+ int j, cpu, err = 0;
printk(KERN_INFO "testing NMI watchdog ... ");
memcpy(tmp, irq_stat, sizeof(tmp));
- sti();
+ cli();
mdelay((10*1000)/nmi_hz); // wait 10 ticks
for (j = 0; j < smp_num_cpus; j++) {
cpu = cpu_logical_map(j);
if (nmi_count(cpu) - tmp[cpu].__nmi_count <= 5) {
printk("CPU#%d: NMI appears to be stuck!\n", cpu);
- return -1;
+ err = -1;
}
}
- printk("OK.\n");
+ if (! err ){
+ printk("OK.\n");
+ }
/* now that we know it works we can reduce NMI frequency to
something more reasonable; makes a difference in some configs */
if (nmi_watchdog == NMI_LOCAL_APIC)
nmi_hz = 1;
-
+ sti();
return 0;
}
@@ -259,6 +269,9 @@
for (i = 0; i < smp_num_cpus; i++)
alert_counter[i] = 0;
}
+#ifdef CONFIG_KGDB
+int tune_watchdog = 5*HZ;
+#endif
void nmi_watchdog_tick (struct pt_regs * regs)
{
@@ -278,13 +291,25 @@
#endif
sum = apic_timer_irqs[cpu];
+ //kgdb_ts(sum,cpu);
+#ifdef CONFIG_KGDB
+ if (! in_kgdb(regs) && last_irq_sums[cpu] == sum ) {
+#else
if (last_irq_sums[cpu] == sum) {
+#endif
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
*/
alert_counter[cpu]++;
+#ifdef CONFIG_KGDB
+ if (alert_counter[cpu] == tune_watchdog) {
+ kgdb_handle_exception(2, SIGTRAP, 0, regs);
+ last_irq_sums[cpu] = sum;
+ alert_counter[cpu] = 0;
+ }
+#endif
if (alert_counter[cpu] == 5*nmi_hz) {
spin_lock(&nmi_print_lock);
/*
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- linux_test/arch/i386/kernel/smp.c Wed Aug 14 11:37:57 2002
+++ linux/arch/i386/kernel/smp.c Thu Aug 22 13:35:53 2002
@@ -581,7 +581,17 @@
send_IPI_mask(1 << cpu, RESCHEDULE_VECTOR);
#endif
}
-
+#ifdef CONFIG_KGDB
+/*
+ * By using the NMI code instead of a vector we just sneak thru the
+ * word generator coming out with just what we want. AND it does
+ * not matter if clustered_apic_mode is set or not.
+ */
+void smp_send_nmi_allbutself(void)
+{
+ send_IPI_allbutself(APIC_DM_NMI);
+}
+#endif
/*
* this function sends a reschedule IPI to all (other) CPUs.
* This should only be used if some 'global' task became runnable,
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- linux_test/arch/i386/kernel/traps.c Wed Aug 14 11:37:58 2002
+++ linux/arch/i386/kernel/traps.c Thu Aug 22 17:14:55 2002
@@ -70,6 +70,7 @@
#include <linux/irq.h>
#include <linux/module.h>
+
asmlinkage int system_call(void);
#ifdef CONFIG_KDB
asmlinkage int kdb_call(void);
@@ -114,6 +115,41 @@
asmlinkage void spurious_interrupt_bug(void);
asmlinkage void machine_check(void);
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#include <linux/init.h>
+extern void int3(void);
+extern void debug(void);
+void set_intr_gate(unsigned int n, void *addr);
+static void set_intr_usr_gate(unsigned int n, void *addr);
+/*
+ * Should be able to call this breakpoint() very early in
+ * bring up. Just hard code the call where needed.
+ * The breakpoint() code is here because set_?_gate() functions
+ * are local (static) to trap.c. They need be done only once,
+ * but it does not hurt to do them over.
+ */
+void breakpoint(void)
+{
+ set_intr_usr_gate(3,&int3); /* disable ints on trap */
+ set_intr_gate(1,&debug);
+
+ BREAKPOINT;
+}
+// gdb_debug_hook * linux_debug_hook;
+#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \
+ { \
+ if (!user_mode(regs) ) \
+ { \
+ kgdb_handle_exception(trapnr, signr, error_code, regs); \
+ after; \
+ } else if ((trapnr == 3) && (regs->eflags &0x200)) __sti(); \
+ }
+#else
+#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after)
+#endif
+
+
int kstack_depth_to_print = 24;
@@ -452,6 +488,15 @@
spin_lock_irq(&die_lock);
bust_spinlocks(1);
printk("%s: %04lx\n", str, err & 0xffff);
+#ifdef CONFIG_KGDB
+ /* This is about the only place we want to go to kgdb even if in
+ * user mode. But we must go in via a trap so within kgdb we will
+ * always be in kernel mode.
+ */
+ if (user_mode(regs))
+ BREAKPOINT;
+#endif
+ CHK_REMOTE_DEBUG(0,SIGTRAP,err,regs,)
show_registers(regs);
dump((char *)str, regs);
bust_spinlocks(0);
@@ -532,6 +577,7 @@
#define DO_ERROR(trapnr, signr, str, name) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
+ CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,)\
do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \
}
@@ -549,7 +595,10 @@
#define DO_VM86_ERROR(trapnr, signr, str, name) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
+ CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,goto skip_trap)\
do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \
+skip_trap: \
+ return; \
}
#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
@@ -610,6 +659,7 @@
regs->eip = fixup;
return;
}
+ CHK_REMOTE_DEBUG(13,SIGSEGV,error_code,regs,)
die("general protection fault", regs, error_code);
}
}
@@ -813,6 +863,12 @@
*/
asmlinkage int do_int3(struct pt_regs * regs, long error_code)
{
+#ifdef CONFIG_KGDB
+ if (!user_mode(regs) ){
+ kgdb_handle_exception(3, SIGTRAP, error_code, regs);
+ return 0;
+ }
+#endif
#ifdef CONFIG_DPROBES_CORE
HOOK_RET(DO_INT3, regs);
#endif
@@ -899,6 +955,17 @@
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
+#ifdef CONFIG_KGDB
+ /*
+ * If this is a kernel mode trap, we need to reset db7 to allow us
+ * to continue sanely ALSO skip the signal delivery
+ */
+ if ((regs->xcs & 3) == 0)
+ goto clear_dr7;
+
+ /* if not kernel, allow ints but only if they were on */
+ if ( regs->eflags & 0x200) local_irq_enable();
+#endif
/* 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.
*/
@@ -923,6 +990,7 @@
: /* no output */
: "r" (0));
#endif
+ CHK_REMOTE_DEBUG(1,SIGTRAP,error_code,regs,)
return 0;
debug_vm86:
@@ -1194,7 +1262,12 @@
{
_set_gate(a,12,3,addr);
}
-
+#ifdef CONFIG_KGDB
+void set_intr_usr_gate(unsigned int n, void *addr)
+{
+ _set_gate(idt_table+n,14,3,addr);
+}
+#endif
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
*((gate_addr)+1) = ((base) & 0xff000000) | \
(((base) & 0x00ff0000)>>16) | \
@@ -1324,13 +1397,13 @@
#endif
set_trap_gate(0,÷_error);
-#ifndef CONFIG_DPROBES_CORE
+#if !defined( CONFIG_DPROBES_CORE) && ! defined(CONFIG_KGDB)
set_trap_gate(1,&debug);
#else
_set_gate(idt_table+1,14,3,&debug);
#endif
set_intr_gate(2,&nmi);
-#ifndef CONFIG_DPROBES_CORE
+#if !defined( CONFIG_DPROBES_CORE) && ! defined(CONFIG_KGDB)
set_system_gate(3,&int3); /* int3-5 can be called from all */
#else
_set_gate(idt_table+3,14,3,&int3);
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/lib/Makefile linux/arch/i386/lib/Makefile
--- linux_test/arch/i386/lib/Makefile Mon May 13 13:44:07 2002
+++ linux/arch/i386/lib/Makefile Thu Aug 22 13:35:53 2002
@@ -13,6 +13,7 @@
obj-$(CONFIG_X86_USE_3DNOW) += mmx.o
obj-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o
+obj-$(CONFIG_KGDB) += kgdb_serial.o
obj-$(CONFIG_DEBUG_IOVIRT) += iodebug.o
include $(TOPDIR)/Rules.make
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/lib/kgdb_serial.c linux/arch/i386/lib/kgdb_serial.c
--- linux_test/arch/i386/lib/kgdb_serial.c Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/lib/kgdb_serial.c Thu Aug 22 13:35:53 2002
@@ -0,0 +1,406 @@
+/*
+ * Serial interface GDB stub
+ *
+ * Written (hacked together) by David Grothe (dave at gcom.com)
+ * Modified to allow invokation early in boot see also
+ * kgdb.h for instructions by George Anzinger(george at mvista.com)
+ *
+ */
+
+#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/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/kgdb_local.h>
+#ifdef CONFIG_KGDB_USER_CONSOLE
+extern void kgdb_console_finit(void);
+#endif
+#define PRNT_off
+#define TEST_EXISTANCE
+#ifdef PRNT
+#define dbprintk(s) printk s
+#else
+#define dbprintk(s)
+#endif
+#define TEST_INTERRUPT_off
+#ifdef TEST_INTERRUPT
+#define intprintk(s) printk s
+#else
+#define intprintk(s)
+#endif
+
+#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
+
+
+#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 ;
+
+struct async_struct *gdb_async_info ;
+static int gdb_async_irq ;
+
+extern void breakpoint(void) ; /* GDB routine */
+
+#define outb_px(a,b) outb_p(b,a)
+/*
+ * Get a byte from the hardware data buffer and return it
+ */
+static int read_data_bfr(struct async_struct *info)
+{
+ char it = inb_p(info->port + UART_LSR);
+
+ if (it & UART_LSR_DR)
+ return(inb_p(info->port + UART_RX));
+ if (it & 0xc ) return( -2);
+
+ 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(struct async_struct *info)
+{
+ int chr ;
+ unsigned long flags;
+ if (atomic_read(&gdb_buf_in_cnt) != 0) {/* intr routine has q'd chars */
+ chr = gdb_buf[gdb_buf_out_inx++] ;
+ gdb_buf_out_inx &= (GDB_BUF_SIZE - 1) ;
+ atomic_dec(&gdb_buf_in_cnt) ;
+ return(chr) ;
+ }
+ save_flags(flags);cli();
+ chr = read_data_bfr(info);
+ restore_flags(flags);
+ return(chr) ; /* read from hardware */
+
+} /* read_char */
+
+/*
+ * Wait until the interface can accept a char, then write it.
+ */
+static void write_char(struct async_struct *info, int chr)
+{
+ while ( !(inb_p(info->port + UART_LSR) & UART_LSR_THRE) ) ;
+
+ outb_p(chr, info->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)
+{
+ struct async_struct *info;
+ int iir;
+
+ info = gdb_async_info ;
+ if (!info || !info->tty || irq != gdb_async_irq)
+ return;
+
+ do
+ {
+ int chr = read_data_bfr(info) ;
+ iir = inb_p(info->port + UART_IIR) ;
+ intprintk(("Debug char on int: %x hex\n",chr));
+ 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 tosses early char */
+ read_char(info) ;
+ }
+ gdb_buf[gdb_buf_in_inx++] = chr ;
+ gdb_buf_in_inx &= (GDB_BUF_SIZE - 1) ;
+ } while (iir & UART_IIR_RDI);
+
+} /* gdb_interrupt */
+
+/*
+ * Just a NULL routine for testing.
+ */
+void gdb_null(void)
+{
+} /* gdb_null */
+/* These structure are filled in with values defined in asm/kgdb_local.h
+ */
+static struct serial_state state = SB_STATE;
+static struct async_struct local_info = SB_INFO;
+static int ok_to_enable_ints = 0;
+static void kgdb_enable_ints_now(void);
+
+extern char * kgdb_version;
+/*
+ * Hook an IRQ for KGDB.
+ *
+ * This routine is called from the ioctl routine of the serial driver
+ * to hand the interface over to the GDB stub code. It is most usually
+ * called from putDebugChar, below. We try to recognize the ioctl call
+ * and for old time sake, throw in a breakpoint in that case.
+ */
+static int ints_disabled = 1;
+int gdb_hook_interrupt(struct async_struct *info,int verb)
+{
+ struct serial_state *state= info->state;
+ int flags,port;
+#ifdef TEST_EXISTANCE
+ int scratch, scratch2;
+#endif
+
+ /* The above fails if memory managment is not set up yet.
+ * Rather than fail the set up, just keep track of the fact
+ * and pick up the interrupt thing later.
+ */
+ gdb_async_info = info ;
+ port = gdb_async_info->port;
+ gdb_async_irq = state->irq ;
+ if(verb){
+ printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n",
+ kgdb_version,
+ port,
+ gdb_async_irq,
+ gdb_async_info->state->custom_divisor);
+ }
+ save_flags(flags);cli();
+#ifdef TEST_EXISTANCE
+ /* Existance test */
+ /* Should not need all this, but just in case.... */
+
+ scratch = inb_p(port + UART_IER);
+ outb_px(port + UART_IER, 0);
+ outb_px(0xff, 0x080);
+ scratch2 = inb_p(port + UART_IER);
+ outb_px(port + UART_IER, scratch);
+ if (scratch2) {
+ printk("gdb_hook_interrupt: Could not clear IER, not a UART!\n");
+ restore_flags(flags);
+ return 1; /* We failed; there's nothing here */
+ }
+ scratch2 = inb_p(port + UART_LCR);
+ outb_px(port + UART_LCR, 0xBF); /* set up for StarTech test */
+ outb_px(port + UART_EFR, 0); /* EFR is the same as FCR */
+ outb_px(port + UART_LCR, 0);
+ outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO);
+ scratch = inb_p(port + UART_IIR) >> 6;
+ if ( scratch == 1){
+ printk("gdb_hook_interrupt: Undefined UART type! Not a UART! \n");
+ restore_flags(flags);
+ return 1;
+ }else{
+ dbprintk(
+ ("gdb_hook_interrupt: UART type is %d where 0=16450, 2=16550 3=16550A\n",
+ scratch));
+ }
+ scratch = inb_p(port + UART_MCR);
+ outb_px(port + UART_MCR, UART_MCR_LOOP | scratch);
+ outb_px(port + UART_MCR, UART_MCR_LOOP | 0x0A);
+ scratch2 = inb_p(port + UART_MSR) & 0xF0;
+ outb_px(port + UART_MCR, scratch);
+ if (scratch2 != 0x90) {
+ printk("gdb_hook_interrupt: Loop back test failed! Not a UART!\n");
+ restore_flags(flags);
+ return scratch2 + 1000; /* force 0 to fail */
+ }
+
+#endif /* test existance */
+ (void)inb_p(port + UART_RX);
+ outb_px(port + UART_IER, 0);
+
+ (void)inb_p(port + UART_RX); /* serial driver comments say */
+ (void)inb_p(port + UART_IIR); /* this clears the interrupt regs */
+ (void)inb_p(port + UART_MSR);
+ outb_px(port + UART_LCR,UART_LCR_WLEN8 | UART_LCR_DLAB);
+ outb_px(port + UART_DLL, info->state->custom_divisor & 0xff); /* LS */
+ outb_px(port + UART_DLM, info->state->custom_divisor >> 8); /* MS */
+ outb_px(port + UART_MCR, info->MCR);
+
+ outb_px(port + UART_FCR,
+ UART_FCR_ENABLE_FIFO |
+ UART_FCR_TRIGGER_1 |
+ UART_FCR_CLEAR_XMIT |
+ UART_FCR_CLEAR_RCVR); /* set fcr */
+ outb_px(port + UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+ outb_px(port + UART_FCR,
+ UART_FCR_ENABLE_FIFO |
+ UART_FCR_TRIGGER_1);/* set fcr */
+
+ restore_flags(flags);
+
+ /*
+ * Call GDB routine ( use breakpoint, only if from ioctl )
+ * Caution, this routine is usually called from a breakpoint so
+ * executing another, could be bad for system health.
+ */
+ if (info != &local_info){ /* must be from ioctl, do a break point */
+ printk("gdb_hook_interrupt: breakpoint\n") ;
+ BREAKPOINT;
+ }
+ return(0) ;
+
+} /* gdb_hook_interrupt */
+
+/*
+ * 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. In the
+ */
+int kgdb_in_isr = 0;
+int kgdb_in_lsr = 0;
+int getDebugChar(void)
+{
+ volatile int chr ;
+
+ dbprintk(("getDebugChar(port %x): ",gdb_async_info->port)) ;
+
+ if (gdb_async_info == NULL){
+ gdb_hook_interrupt(&local_info,0);
+ }
+#if 0
+ { int foo = inb_p(gdb_async_info->port+UART_IIR);
+ if ((foo & 1) == 0) {
+ kgdb_in_isr = foo;
+ kgdb_in_lsr = inb_p(gdb_async_info->port+
+ UART_LSR);
+ }
+#endif
+ while ( (chr = read_char(gdb_async_info)) == -1) ;
+
+ dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')) ;
+ 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.
+ */
+static int count = 3;
+static spinlock_t one_at_atime=SPIN_LOCK_UNLOCKED;
+
+static int __init kgdb_enable_ints(void)
+{
+ if (gdb_async_info == NULL) {
+ gdb_hook_interrupt(&local_info,1);
+ }
+ ok_to_enable_ints = 1;
+ kgdb_enable_ints_now();
+#ifdef CONFIG_KGDB_USER_CONSOLE
+ kgdb_console_finit();
+#endif
+ return 0;
+}
+static void kgdb_enable_ints_now(void)
+{
+ spin_lock(&one_at_atime);
+ if ( !ints_disabled) goto exit;
+ if (max_mapnr && ints_disabled){ /* don't try till mem init */
+#ifdef CONFIG_SERIAL
+ /*
+ * The ifdef here allows the system to be configured
+ * without the serial driver.
+ * Don't make it a module, however, it will steal the port
+ */
+ shutdown_for_kgdb(gdb_async_info);
+#endif
+ ints_disabled = request_irq(gdb_async_info->state->irq,
+ gdb_interrupt,
+ IRQ_T(gdb_async_info),
+ "KGDB-stub", NULL);
+ intprintk(("KGDB: request_irq returned %d\n",ints_disabled));
+ }
+ if (! ints_disabled ){
+ intprintk(("KGDB: Sending %d to port %x offset %d\n",
+ gdb_async_info->IER,
+ (int)gdb_async_info->port,UART_IER));
+ outb_px(gdb_async_info->port + UART_IER,
+ gdb_async_info->IER);
+ }
+exit:
+ spin_unlock(&one_at_atime);
+}
+void putDebugChar(int chr)
+{
+ dbprintk(("putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n",
+ gdb_async_info->port,
+ chr,chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0:1)) ;
+
+ if (gdb_async_info == NULL) {
+ gdb_hook_interrupt(&local_info,0);
+ }
+
+ write_char(gdb_async_info, chr) ; /* this routine will wait */
+ count = ( chr == '#' )? 0 : count++;
+ if ( (count == 2) ){ /* try to enable after */
+ if ( ints_disabled & ok_to_enable_ints)
+ kgdb_enable_ints_now();/* try to enable after */
+
+ /* We do this a lot because, well we really want to get these
+ * interrupts. The serial driver will clear these bits when it
+ * initializes the chip. Every thing else it does is ok, but this.
+ */
+ if (! ints_disabled ){
+ outb_px(gdb_async_info->port + UART_IER,
+ gdb_async_info->IER);
+ }
+ }
+
+} /* putDebugChar */
+
+module_init(kgdb_enable_ints);
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- linux_test/arch/i386/mm/fault.c Wed Aug 14 11:37:59 2002
+++ linux/arch/i386/mm/fault.c Thu Aug 22 13:35:53 2002
@@ -30,7 +30,9 @@
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
#include <asm/hardirq.h>
-
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#endif
extern void die(const char *,struct pt_regs *,long);
/*
@@ -329,6 +331,12 @@
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
*/
+#ifdef CONFIG_KGDB
+ if (!user_mode(regs)){
+ kgdb_handle_exception(14,SIGBUS, error_code, regs);
+ return;
+ }
+#endif
bust_spinlocks(1);
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- linux_test/drivers/char/keyboard.c Wed Aug 14 11:38:29 2002
+++ linux/drivers/char/keyboard.c Thu Aug 22 13:35:53 2002
@@ -263,6 +263,9 @@
} else if (sysrq_pressed) {
if (!up_flag) {
handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty);
+#ifdef CONFIG_KGDB_SYSRQ
+ sysrq_pressed = 0; /* in case we miss the "up" event */
+#endif
goto out;
}
}
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/drivers/char/serial.c linux/drivers/char/serial.c
--- linux_test/drivers/char/serial.c Wed Aug 14 11:38:32 2002
+++ linux/drivers/char/serial.c Thu Aug 22 13:35:53 2002
@@ -1693,6 +1693,15 @@
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
}
+#ifdef CONFIG_KGDB
+void shutdown_for_kgdb(struct async_struct * info)
+{
+ int irq = info->state->irq;
+ while(IRQ_ports[irq]){
+ shutdown(IRQ_ports[irq]) ;
+ }
+}
+#endif
#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
static int baud_table[] = {
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/drivers/char/sysrq.c linux/drivers/char/sysrq.c
--- linux_test/drivers/char/sysrq.c Wed Aug 14 11:38:32 2002
+++ linux/drivers/char/sysrq.c Thu Aug 22 13:35:53 2002
@@ -34,6 +34,19 @@
#include <linux/spinlock.h>
#include <asm/ptrace.h>
+#ifdef CONFIG_KGDB_SYSRQ
+#include <asm/kgdb.h>
+#define GDB_OP &kgdb_op
+static struct sysrq_key_op kgdb_op={
+ handler: (void*)breakpoint,
+ help_msg: "kGdb ",
+ action_msg: "Debug breakpoint\n",
+};
+
+#else
+#define GDB_OP NULL
+#endif
+
extern void reset_vc(unsigned int);
extern struct list_head super_blocks;
@@ -392,7 +405,7 @@
/* d */ &sysrq_dumpregs_op,
/* e */ &sysrq_term_op,
/* f */ NULL,
-/* g */ NULL,
+/* g */ GDB_OP,
/* h */ NULL,
/* i */ &sysrq_kill_op,
/* j */ NULL,
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- linux_test/include/asm-i386/bugs.h Thu Aug 22 13:01:55 2002
+++ linux/include/asm-i386/bugs.h Fri Aug 23 14:49:28 2002
@@ -24,7 +24,18 @@
#include <asm/processor.h>
#include <asm/i387.h>
#include <asm/msr.h>
-
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+/*
+ * Provied the command line "kgdb" initial break
+ */
+int __init kgdb_initial_break(char * str)
+{
+ breakpoint();
+ return 1;
+}
+__setup("kgdb",kgdb_initial_break);
+#endif
static int __init no_halt(char *s)
{
boot_cpu_data.hlt_works_ok = 0;
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/include/asm-i386/hw_irq.h linux/include/asm-i386/hw_irq.h
--- linux_test/include/asm-i386/hw_irq.h Wed Aug 14 15:35:29 2002
+++ linux/include/asm-i386/hw_irq.h Thu Aug 22 13:56:39 2002
@@ -97,6 +97,16 @@
#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
+#ifdef CONFIG_STACK_OVERFLOW_TEST
+#define STACK_OVERFLOW_TEST \
+ "testl $7680,%esp\n\t" \
+ "jnz 10f\n\t" \
+ "call " SYMBOL_NAME_STR(stack_overflow)"\n\t" \
+"10:\n\t"
+#else
+#define STACK_OVERFLOW_TEST
+#endif
+
#define __STR(x) #x
#define STR(x) __STR(x)
@@ -123,6 +133,7 @@
"pushl %edx\n\t" \
"pushl %ecx\n\t" \
"pushl %ebx\n\t" \
+ STACK_OVERFLOW_TEST \
"movl $" STR(__KERNEL_DS) ",%edx\n\t" \
"movl %edx,%ds\n\t" \
"movl %edx,%es\n\t" \
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/include/asm-i386/kgdb.h linux/include/asm-i386/kgdb.h
--- linux_test/include/asm-i386/kgdb.h Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/kgdb.h Thu Aug 22 13:56:56 2002
@@ -0,0 +1,46 @@
+#ifndef __KGDB
+#define __KGDB
+
+/*
+ * This file should not include ANY others. This makes it usable
+ * most anywhere without the fear of include order or inclusion.
+ * Make it so!
+ */
+
+extern void breakpoint(void);
+#define INIT_KGDB_INTS kgdb_enable_ints()
+
+#ifndef BREAKPOINT
+#define BREAKPOINT asm(" int $3");
+#endif
+/*
+ * GDB debug stub (or any debug stub) can point the 'linux_debug_hook'
+ * pointer to its routine and it will be entered as the first thing
+ * when a trap occurs.
+ *
+ * Return values are, at present, undefined.
+ *
+ * The debug hook routine does not necessarily return to its caller.
+ * It has the register image and thus may choose to resume execution
+ * anywhere it pleases.
+ */
+struct pt_regs;
+
+extern int kgdb_handle_exception(int trapno,
+ int signo,
+ int err_code,
+ struct pt_regs *regs) ;
+extern int in_kgdb(struct pt_regs *regs);
+#ifdef CONFIG_KGDB_TS
+void kgdb_tstamp(int line, char * source, int data0, int data1);
+/*
+ * This is the time stamp function. The macro adds the source info and
+ * does a cast on the data to allow most any 32-bit value.
+ */
+
+#define kgdb_ts(data0,data1) kgdb_tstamp(__LINE__,__FILE__,(int)data0,(int)data1)
+#else
+#define kgdb_ts(data0,data1)
+#endif
+#endif /* __KGDB */
+
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/include/asm-i386/kgdb_local.h linux/include/asm-i386/kgdb_local.h
--- linux_test/include/asm-i386/kgdb_local.h Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/kgdb_local.h Fri Aug 23 15:06:09 2002
@@ -0,0 +1,68 @@
+#ifndef __KGDB_LOCAL
+#define ___KGDB_LOCAL
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <asm/kgdb.h>
+#define PORT 0x3f8
+#ifdef CONFIG_KGDB_PORT
+#undef PORT
+#define PORT CONFIG_KGDB_PORT
+#endif
+#define IRQ 4
+#ifdef CONFIG_KGDB_IRQ
+#undef IRQ
+#define IRQ CONFIG_KGDB_IRQ
+#endif
+#define SB_CLOCK 1843200
+#define SB_BASE (SB_CLOCK/16)
+#define SB_BAUD9600 SB_BASE/9600
+#define SB_BAUD192 SB_BASE/19200
+#define SB_BAUD384 SB_BASE/38400
+#define SB_BAUD576 SB_BASE/57600
+#define SB_BAUD1152 SB_BASE/115200
+#ifdef CONFIG_KGDB_9600BAUD
+#define SB_BAUD SB_BAUD9600
+#endif
+#ifdef CONFIG_KGDB_19200BAUD
+#define SB_BAUD SB_BAUD192
+#endif
+#ifdef CONFIG_KGDB_38400BAUD
+#define SB_BAUD SB_BAUD384
+#endif
+#ifdef CONFIG_KGDB_57600BAUD
+#define SB_BAUD SB_BAUD576
+#endif
+#ifdef CONFIG_KGDB_115200BAUD
+#define SB_BAUD SB_BAUD1152
+#endif
+#ifndef SB_BAUD
+#define SB_BAUD SB_BAUD1152 /* Start with this if not given*/
+#endif
+
+#define SB_IER 1
+#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS
+
+#define FLAGS 0
+#define SB_STATE { \
+ magic: SSTATE_MAGIC, \
+ baud_base: SB_BASE, \
+ port: PORT, \
+ irq: IRQ, \
+ flags: FLAGS, \
+ custom_divisor:SB_BAUD}
+#define SB_INFO { \
+ magic: SERIAL_MAGIC, \
+ port: PORT,0,FLAGS, \
+ state: &state, \
+ tty: (struct tty_struct *)&state, \
+ IER: SB_IER, \
+ MCR: SB_MCR}
+extern void putDebugChar(int);
+#ifdef CONFIG_SERIAL
+extern void shutdown_for_kgdb(struct async_struct * info);
+#endif
+#define INIT_KDEBUG putDebugChar("+");
+#endif /* __KGDB_LOCAL */
+
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude -x high-res-timers linux_test/kernel/sched.c linux/kernel/sched.c
--- linux_test/kernel/sched.c Wed Aug 21 18:10:08 2002
+++ linux/kernel/sched.c Thu Aug 22 13:35:53 2002
@@ -1179,6 +1179,13 @@
task_rq_unlock(rq, &flags);
}
+#if defined( CONFIG_KGDB) && defined( sched_find_first_bit)
+struct task_struct * kgdb_get_idle(int this_cpu)
+{
+ return runqueues[this_cpu].idle;
+}
+#endif
+
#ifndef __alpha__
/*
More information about the cgl_discussion
mailing list