diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/Documentation/DocBook/Makefile linux-2.6.18.kgdb/Documentation/DocBook/Makefile
--- linux-2.6.18/Documentation/DocBook/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/Documentation/DocBook/Makefile 2008-06-10 16:18:58.000000000 +0400
@@ -11,7 +11,7 @@ DOCBOOKS := wanbook.xml z8530book.xml mc
procfs-guide.xml writing_usb_driver.xml \
kernel-api.xml journal-api.xml lsm.xml usb.xml \
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
- genericirq.xml
+ genericirq.xml kgdb.xml
###
# The build process is as follows (targets):
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/Documentation/DocBook/kgdb.tmpl linux-2.6.18.kgdb/Documentation/DocBook/kgdb.tmpl
--- linux-2.6.18/Documentation/DocBook/kgdb.tmpl 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/Documentation/DocBook/kgdb.tmpl 2008-06-10 16:19:47.000000000 +0400
@@ -0,0 +1,250 @@
+
+
+
+
+
+ KGDB Internals
+
+
+
+ Tom
+ Rini
+
+
+ trini@kernel.crashing.org
+
+
+
+
+
+
+
+ Amit S.
+ Kale
+
+
+ amitkale@linsyssoft.com
+
+
+
+
+
+
+ 2004-2005
+ MontaVista Software, Inc.
+
+
+ 2004
+ Amit S. Kale
+
+
+
+
+ This file is licensed under the terms of the GNU General Public License
+ version 2. This program is licensed "as is" without any warranty of any
+ kind, whether express or implied.
+
+
+
+
+
+
+
+ Introduction
+
+ kgdb is a source level debugger for linux kernel. It is used along
+ with gdb to debug a linux kernel. Kernel developers can debug a kernel
+ similar to application programs with the use of kgdb. It makes it
+ possible to place breakpoints in kernel code, step through the code
+ and observe variables.
+
+
+ Two machines are required for using kgdb. One of these machines is a
+ development machine and the other is a test machine. The machines are
+ typically connected through a serial line, a null-modem cable which
+ connects their serial ports. It is also possible however, to use an
+ ethernet connection between the machines. The kernel to be debugged
+ runs on the test machine. gdb runs on the development machine. The
+ serial line or ethernet connection is used by gdb to communicate to
+ the kernel being debugged.
+
+
+
+ Compiling a kernel
+
+ To enable CONFIG_KGDB , look under the "Kernel debugging"
+ and then select "KGDB: kernel debugging with remote gdb".
+
+
+ The first choice for I/O is CONFIG_KGDB_ONLY_MODULES .
+ This means that you will only be able to use KGDB after loading a
+ kernel module that defines how you want to be able to talk with
+ KGDB. There are two other choices (more on some architectures) that
+ can be enabled as modules later, if not picked here.
+
+ The first of these is CONFIG_KGDB_8250_NOMODULE .
+ This has sub-options such as CONFIG_KGDB_SIMPLE_SERIAL
+ which toggles choosing the serial port by ttyS number or by specifying
+ a port and IRQ number.
+
+
+ The second of these choices on most systems for I/O is
+ CONFIG_KGDBOE . This requires that the machine to be
+ debugged has an ethernet card which supports the netpoll API, such as
+ the cards supported by CONFIG_E100 . There are no
+ sub-options for this, but a kernel command line option is required.
+
+
+
+ Booting the kernel
+
+ The Kernel command line option kgdbwait makes kgdb
+ wait for gdb connection during booting of a kernel. If the
+ CONFIG_KGDB_8250 driver is used (or if applicable,
+ another serial driver) this breakpoint will happen very early on, before
+ console output. If you wish to change serial port information and you
+ have enabled both CONFIG_KGDB_8250 and
+ CONFIG_KGDB_SIMPLE_SERIAL then you must pass the option
+ kgdb8250=<io or mmio>,<address>,<baud
+ rate>,<irq> before kgdbwait .
+ The values io or mmio refer to
+ if the address being passed next needs to be memory mapped
+ (mmio ) or not. The address must
+ be passed in hex and is the hardware address and will be remapped if
+ passed as mmio . The value
+ baud rate and irq are base-10.
+ The supported values for baud rate are
+ 9600 , 19200 ,
+ 38400 , 57600 , and
+ 115200 .
+
+
+ To have KGDB stop the kernel and wait, with the compiled values for the
+ serial driver, pass in: kgdbwait .
+
+
+ To specify the values of the SH SCI(F) serial port at boot:
+ kgdbsci=0,115200 .
+
+
+ To specify the values of the serial port at boot:
+ kgdb8250=io,3f8,115200,3 .
+ On IA64 this could also be:
+ kgdb8250=mmio,0xff5e0000,115200,74
+ And to have KGDB also stop the kernel and wait for GDB to connect, pass in
+ kgdbwait after this arguement.
+
+
+ To configure the CONFIG_KGDBOE driver, pass in
+ kgdboe=[src-port]@<src-ip>/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]
+ where:
+
+ src-port (optional): source for UDP packets (defaults to 6443 )
+ src-ip: source IP to use (interface address)
+ dev (optional): network interface (eth0 )
+ tgt-port (optional): port GDB will use (defaults to 6442 )
+ tgt-ip: IP address GDB will be connecting from
+ tgt-macaddr (optional): ethernet MAC address for logging agent (default is broadcast)
+
+
+
+ The CONFIG_KGDBOE driver can be reconfigured at run
+ time, if CONFIG_SYSFS and
+ CONFIG_MODULES by echo'ing a new config string to
+ /sys/module/kgdboe/parameter/kgdboe . The
+ driver can be unconfigured with the special string
+ not_configured .
+
+
+
+ Connecting gdb
+
+ If you have used any of the methods to have KGDB stop and create
+ an initial breakpoint described in the previous chapter, kgdb prints
+ the message "Waiting for connection from remote gdb..." on the console
+ and waits for connection from gdb. At this point you connect gdb to kgdb.
+
+
+ Example (serial):
+
+
+ % gdb ./vmlinux
+ (gdb) set remotebaud 115200
+ (gdb) target remote /dev/ttyS0
+
+
+ Example (ethernet):
+
+
+ % gdb ./vmlinux
+ (gdb) target remote udp:192.168.2.2:6443
+
+
+ Once connected, you can debug a kernel the way you would debug an
+ application program.
+
+
+
+ Architecture specific notes
+
+ SuperH: The NMI switch found on some boards can be used to trigger an
+ initial breakpoint. Subsequent triggers do nothing. If console
+ is enabled on the SCI(F) serial port, and that is the port being used
+ for KGDB, then you must trigger a breakpoint via sysrq, NMI, or
+ some other method prior to connecting, or echo a control-c to the
+ serial port. Also, to use the SCI(F) port for KGDB, the
+ CONFIG_SERIAL_SH_SCI driver must be enabled.
+
+
+
+ The common backend (required)
+
+ There are a few flags which must be set on every architecture in
+ their <asm/kgdb.h> file. These are:
+
+
+
+ NUMREGBYTES: The size in bytes of all of the registers, so
+ that we can ensure they will all fit into a packet.
+
+
+ BUFMAX: The size in bytes of the buffer GDB will read into.
+ This must be larger than NUMREGBYTES.
+
+
+ CACHE_FLUSH_IS_SAFE: Set to one if it always safe to call
+ flush_cache_range or flush_icache_range. On some architectures,
+ these functions may not be safe to call on SMP since we keep other
+ CPUs in a holding pattern.
+
+
+
+
+
+ There are also the following functions for the common backend,
+ found in kernel/kgdb.c that must be supplied by the
+ architecture-specific backend. No weak version of these is provided.
+
+!Iinclude/linux/kgdb.h
+
+
+ The common backend (optional)
+
+ These functions are part of the common backend, found in kernel/kgdb.c
+ and are optionally implemented. Some functions (with _hw_ in the name)
+ end up being required on arches which use hardware breakpoints.
+
+!Ikernel/kgdb.c
+
+
+ Driver-Specific Functions
+
+ Some of the I/O drivers have additional functions that can be
+ called, that are specific to the driver. Calls from other places
+ to these functions must be wrapped in #ifdefs for the driver in
+ question.
+
+!Idrivers/serial/8250_kgdb.c
+
+
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/MAINTAINERS linux-2.6.18.kgdb/MAINTAINERS
--- linux-2.6.18/MAINTAINERS 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/MAINTAINERS 2008-06-10 16:18:58.000000000 +0400
@@ -1685,6 +1685,15 @@ L: linux-kernel@vger.kernel.org
L: fastboot@osdl.org
S: Maintained
+KGDB
+P: Tom Rini
+P: Amit S. Kale
+M: trini@kernel.crashing.org
+M: amitkale@linsyssoft.com
+W: http://sourceforge.net/projects/kgdb
+L: kgdb-bugreport@lists.sourceforge.net
+S: Maintained
+
KPROBES
P: Prasanna S Panchamukhi
M: prasanna@in.ibm.com
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/Makefile linux-2.6.18.kgdb/Makefile
--- linux-2.6.18/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/Makefile 2008-06-10 16:19:57.000000000 +0400
@@ -990,6 +990,7 @@ MRPROPER_DIRS += include/config include
MRPROPER_FILES += .config .config.old include/asm .version .old_version \
include/linux/autoconf.h include/linux/version.h \
include/linux/utsrelease.h \
+ include/linux/dwarf2-defs.h \
Module.symvers tags TAGS cscope*
# clean - Delete most, but leave enough to build external modules
@@ -1416,7 +1417,11 @@ clean := -f $(if $(KBUILD_SRC),$(srctree
endif # skip-makefile
PHONY += FORCE
-FORCE:
+include/linux/dwarf2-defs.h: $(srctree)/include/linux/dwarf2.h $(srctree)/scripts/dwarfh.awk
+ mkdir -p include/linux/
+ awk -f $(srctree)/scripts/dwarfh.awk $(srctree)/include/linux/dwarf2.h > include/linux/dwarf2-defs.h
+
+FORCE: include/linux/dwarf2-defs.h
# Declare the contents of the .PHONY variable as phony. We keep that
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/kernel/Makefile linux-2.6.18.kgdb/arch/arm/kernel/Makefile
--- linux-2.6.18/arch/arm/kernel/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/kernel/Makefile 2008-06-10 16:19:51.000000000 +0400
@@ -20,6 +20,7 @@ obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
+obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o
obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/kernel/entry-armv.S linux-2.6.18.kgdb/arch/arm/kernel/entry-armv.S
--- linux-2.6.18/arch/arm/kernel/entry-armv.S 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/kernel/entry-armv.S 2008-06-10 16:19:58.000000000 +0400
@@ -15,6 +15,7 @@
* it to save wrong values... Be aware!
*/
+#include
#include
#include
#include
@@ -232,6 +233,7 @@ svc_preempt:
beq preempt_return @ go again
b 1b
#endif
+ CFI_END_FRAME(__irq_svc)
.align 5
__und_svc:
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/kernel/kgdb-jmp.S linux-2.6.18.kgdb/arch/arm/kernel/kgdb-jmp.S
--- linux-2.6.18/arch/arm/kernel/kgdb-jmp.S 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/arm/kernel/kgdb-jmp.S 2008-06-10 16:19:51.000000000 +0400
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/kernel/kgdb-jmp.S
+ *
+ * Trivial setjmp and longjmp procedures to support bus error recovery
+ * which may occur during kgdb memory read/write operations.
+ *
+ * Author: MontaVista Software, Inc.
+ * source@mvista.com
+ *
+ * 2002-2005 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program as licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+#include
+
+ENTRY (kgdb_fault_setjmp)
+ /* Save registers */
+ stmia r0, {r0-r14}
+ str lr,[r0, #60]
+ mrs r1,cpsr
+ str r1,[r0,#64]
+ ldr r1,[r0,#4]
+ mov r0, #0
+ mov pc,lr
+
+ENTRY (kgdb_fault_longjmp)
+ /* Restore registers */
+ mov r1,#1
+ str r1,[r0]
+ ldr r1,[r0, #64]
+ msr spsr,r1
+ ldmia r0,{r0-pc}^
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/kernel/kgdb.c linux-2.6.18.kgdb/arch/arm/kernel/kgdb.c
--- linux-2.6.18/arch/arm/kernel/kgdb.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/arm/kernel/kgdb.c 2008-06-10 16:19:51.000000000 +0400
@@ -0,0 +1,208 @@
+/*
+ * arch/arm/kernel/kgdb.c
+ *
+ * ARM KGDB support
+ *
+ * Copyright (c) 2002-2004 MontaVista Software, Inc
+ *
+ * Authors: George Davis
+ * Deepak Saxena
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Make a local copy of the registers passed into the handler (bletch) */
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ int regno;
+
+ /* Initialize all to zero (??) */
+ for (regno = 0; regno < GDB_MAX_REGS; regno++)
+ gdb_regs[regno] = 0;
+
+ gdb_regs[_R0] = kernel_regs->ARM_r0;
+ gdb_regs[_R1] = kernel_regs->ARM_r1;
+ gdb_regs[_R2] = kernel_regs->ARM_r2;
+ gdb_regs[_R3] = kernel_regs->ARM_r3;
+ gdb_regs[_R4] = kernel_regs->ARM_r4;
+ gdb_regs[_R5] = kernel_regs->ARM_r5;
+ gdb_regs[_R6] = kernel_regs->ARM_r6;
+ gdb_regs[_R7] = kernel_regs->ARM_r7;
+ gdb_regs[_R8] = kernel_regs->ARM_r8;
+ gdb_regs[_R9] = kernel_regs->ARM_r9;
+ gdb_regs[_R10] = kernel_regs->ARM_r10;
+ gdb_regs[_FP] = kernel_regs->ARM_fp;
+ gdb_regs[_IP] = kernel_regs->ARM_ip;
+ gdb_regs[_SP] = kernel_regs->ARM_sp;
+ gdb_regs[_LR] = kernel_regs->ARM_lr;
+ gdb_regs[_PC] = kernel_regs->ARM_pc;
+ gdb_regs[_CPSR] = kernel_regs->ARM_cpsr;
+}
+
+/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ kernel_regs->ARM_r0 = gdb_regs[_R0];
+ kernel_regs->ARM_r1 = gdb_regs[_R1];
+ kernel_regs->ARM_r2 = gdb_regs[_R2];
+ kernel_regs->ARM_r3 = gdb_regs[_R3];
+ kernel_regs->ARM_r4 = gdb_regs[_R4];
+ kernel_regs->ARM_r5 = gdb_regs[_R5];
+ kernel_regs->ARM_r6 = gdb_regs[_R6];
+ kernel_regs->ARM_r7 = gdb_regs[_R7];
+ kernel_regs->ARM_r8 = gdb_regs[_R8];
+ kernel_regs->ARM_r9 = gdb_regs[_R9];
+ kernel_regs->ARM_r10 = gdb_regs[_R10];
+ kernel_regs->ARM_fp = gdb_regs[_FP];
+ kernel_regs->ARM_ip = gdb_regs[_IP];
+ kernel_regs->ARM_sp = gdb_regs[_SP];
+ kernel_regs->ARM_lr = gdb_regs[_LR];
+ kernel_regs->ARM_pc = gdb_regs[_PC];
+ kernel_regs->ARM_cpsr = gdb_regs[GDB_MAX_REGS - 1];
+}
+
+static inline struct pt_regs *kgdb_get_user_regs(struct task_struct *task)
+{
+ return (struct pt_regs *)
+ ((unsigned long)task->thread_info + THREAD_SIZE -
+ 8 - sizeof(struct pt_regs));
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
+ struct task_struct *task)
+{
+ int regno;
+ struct pt_regs *thread_regs;
+
+ /* Just making sure... */
+ if (task == NULL)
+ return;
+
+ /* Initialize to zero */
+ for (regno = 0; regno < GDB_MAX_REGS; regno++)
+ gdb_regs[regno] = 0;
+
+ /* Otherwise, we have only some registers from switch_to() */
+ thread_regs = kgdb_get_user_regs(task);
+ gdb_regs[_R0] = thread_regs->ARM_r0; /* Not really valid? */
+ gdb_regs[_R1] = thread_regs->ARM_r1; /* " " */
+ gdb_regs[_R2] = thread_regs->ARM_r2; /* " " */
+ gdb_regs[_R3] = thread_regs->ARM_r3; /* " " */
+ gdb_regs[_R4] = thread_regs->ARM_r4;
+ gdb_regs[_R5] = thread_regs->ARM_r5;
+ gdb_regs[_R6] = thread_regs->ARM_r6;
+ gdb_regs[_R7] = thread_regs->ARM_r7;
+ gdb_regs[_R8] = thread_regs->ARM_r8;
+ gdb_regs[_R9] = thread_regs->ARM_r9;
+ gdb_regs[_R10] = thread_regs->ARM_r10;
+ gdb_regs[_FP] = thread_regs->ARM_fp;
+ gdb_regs[_IP] = thread_regs->ARM_ip;
+ gdb_regs[_SP] = thread_regs->ARM_sp;
+ gdb_regs[_LR] = thread_regs->ARM_lr;
+ gdb_regs[_PC] = thread_regs->ARM_pc;
+ gdb_regs[_CPSR] = thread_regs->ARM_cpsr;
+}
+
+static int compiled_break;
+
+int kgdb_arch_handle_exception(int exception_vector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ long addr;
+ char *ptr;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ kgdb_contthread = NULL;
+
+ /*
+ * Try to read optional parameter, pc unchanged if no parm.
+ * If this was a compiled breakpoint, we need to move
+ * to the next instruction or we will just breakpoint
+ * over and over again.
+ */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr)) {
+ linux_regs->ARM_pc = addr;
+ } else if (compiled_break == 1) {
+ linux_regs->ARM_pc += 4;
+ }
+
+ compiled_break = 0;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
+{
+ kgdb_handle_exception(1, SIGTRAP, 0, regs);
+
+ return 0;
+}
+
+static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)
+{
+ compiled_break = 1;
+ kgdb_handle_exception(1, SIGTRAP, 0, regs);
+
+ return 0;
+}
+
+static struct undef_hook kgdb_brkpt_hook = {
+ .instr_mask = 0xffffffff,
+ .instr_val = KGDB_BREAKINST,
+ .fn = kgdb_brk_fn
+};
+
+static struct undef_hook kgdb_compiled_brkpt_hook = {
+ .instr_mask = 0xffffffff,
+ .instr_val = KGDB_COMPILED_BREAK,
+ .fn = kgdb_compiled_brk_fn
+};
+
+/*
+ * Register our undef instruction hooks with ARM undef core.
+ * We regsiter a hook specifically looking for the KGB break inst
+ * and we handle the normal undef case within the do_undefinstr
+ * handler.
+ */
+int kgdb_arch_init(void)
+{
+ register_undef_hook(&kgdb_brkpt_hook);
+ register_undef_hook(&kgdb_compiled_brkpt_hook);
+
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifndef __ARMEB__
+ .gdb_bpt_instr = {0xfe, 0xde, 0xff, 0xe7}
+#else
+ .gdb_bpt_instr = {0xe7, 0xff, 0xde, 0xfe}
+#endif
+};
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/kernel/setup.c linux-2.6.18.kgdb/arch/arm/kernel/setup.c
--- linux-2.6.18/arch/arm/kernel/setup.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/kernel/setup.c 2008-06-10 16:19:51.000000000 +0400
@@ -829,6 +829,11 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con;
#endif
#endif
+
+#if defined(CONFIG_KGDB)
+ extern void __init early_trap_init(void);
+ early_trap_init();
+#endif
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/kernel/traps.c linux-2.6.18.kgdb/arch/arm/kernel/traps.c
--- linux-2.6.18/arch/arm/kernel/traps.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/kernel/traps.c 2008-06-10 16:19:51.000000000 +0400
@@ -278,6 +278,7 @@ asmlinkage void do_undefinstr(struct pt_
unsigned int instr;
struct undef_hook *hook;
siginfo_t info;
+ mm_segment_t fs;
void __user *pc;
/*
@@ -287,12 +288,15 @@ asmlinkage void do_undefinstr(struct pt_
*/
regs->ARM_pc -= correction;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
pc = (void __user *)instruction_pointer(regs);
if (thumb_mode(regs)) {
get_user(instr, (u16 __user *)pc);
} else {
get_user(instr, (u32 __user *)pc);
}
+ set_fs(fs);
spin_lock_irq(&undef_lock);
list_for_each_entry(hook, &undef_hook, node) {
@@ -684,6 +688,13 @@ EXPORT_SYMBOL(abort);
void __init trap_init(void)
{
+#if defined(CONFIG_KGDB)
+ return;
+}
+
+void __init early_trap_init(void)
+{
+#endif
unsigned long vectors = CONFIG_VECTORS_BASE;
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-ixp2000/core.c linux-2.6.18.kgdb/arch/arm/mach-ixp2000/core.c
--- linux-2.6.18/arch/arm/mach-ixp2000/core.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/mach-ixp2000/core.c 2008-06-10 16:19:51.000000000 +0400
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
@@ -184,6 +185,9 @@ static struct platform_device ixp2000_se
void __init ixp2000_uart_init(void)
{
platform_device_register(&ixp2000_serial_device);
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &ixp2000_serial_port);
+#endif
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-ixp2000/ixdp2x01.c linux-2.6.18.kgdb/arch/arm/mach-ixp2000/ixdp2x01.c
--- linux-2.6.18/arch/arm/mach-ixp2000/ixdp2x01.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/mach-ixp2000/ixdp2x01.c 2008-06-10 16:19:51.000000000 +0400
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
@@ -413,6 +414,11 @@ static void __init ixdp2x01_init_machine
platform_add_devices(ixdp2x01_devices, ARRAY_SIZE(ixdp2x01_devices));
ixp2000_uart_init();
ixdp2x01_uart_init();
+
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &ixdp425_serial_ports[0]);
+ kgdb8250_add_port(1, &ixdp425_serial_ports[1]);
+#endif
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-ixp4xx/coyote-setup.c linux-2.6.18.kgdb/arch/arm/mach-ixp4xx/coyote-setup.c
--- linux-2.6.18/arch/arm/mach-ixp4xx/coyote-setup.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/mach-ixp4xx/coyote-setup.c 2008-06-10 16:19:51.000000000 +0400
@@ -96,6 +96,10 @@ static void __init coyote_init(void)
}
platform_add_devices(coyote_devices, ARRAY_SIZE(coyote_devices));
+
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &coyote_serial_port);
+#endif
}
#ifdef CONFIG_ARCH_ADI_COYOTE
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-ixp4xx/ixdp425-setup.c linux-2.6.18.kgdb/arch/arm/mach-ixp4xx/ixdp425-setup.c
--- linux-2.6.18/arch/arm/mach-ixp4xx/ixdp425-setup.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/mach-ixp4xx/ixdp425-setup.c 2008-06-10 16:19:51.000000000 +0400
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
static struct flash_platform_data ixdp425_flash_data = {
.map_name = "cfi_probe",
@@ -76,7 +77,8 @@ static struct plat_serial8250_port ixdp4
.mapbase = IXP4XX_UART1_BASE_PHYS,
.membase = (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET,
.irq = IRQ_IXP4XX_UART1,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
+ UPF_SHARE_IRQ,
.iotype = UPIO_MEM,
.regshift = 2,
.uartclk = IXP4XX_UART_XTAL,
@@ -85,7 +87,8 @@ static struct plat_serial8250_port ixdp4
.mapbase = IXP4XX_UART2_BASE_PHYS,
.membase = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
.irq = IRQ_IXP4XX_UART2,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
+ UPF_SHARE_IRQ,
.iotype = UPIO_MEM,
.regshift = 2,
.uartclk = IXP4XX_UART_XTAL,
@@ -116,6 +119,11 @@ static void __init ixdp425_init(void)
IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1;
platform_add_devices(ixdp425_devices, ARRAY_SIZE(ixdp425_devices));
+
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &ixdp425_serial_ports[0]);
+ kgdb8250_add_port(1, &ixdp425_serial_ports[1]);
+#endif
}
#ifdef CONFIG_ARCH_IXDP425
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-omap1/serial.c linux-2.6.18.kgdb/arch/arm/mach-omap1/serial.c
--- linux-2.6.18/arch/arm/mach-omap1/serial.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/mach-omap1/serial.c 2008-06-10 16:19:51.000000000 +0400
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -199,6 +200,9 @@ void __init omap_serial_init(void)
break;
}
omap_serial_reset(&serial_platform_data[i]);
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_platform_port(i, &serial_platform_data[i]);
+#endif
}
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-pxa/Makefile linux-2.6.18.kgdb/arch/arm/mach-pxa/Makefile
--- linux-2.6.18/arch/arm/mach-pxa/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/mach-pxa/Makefile 2008-06-10 16:19:51.000000000 +0400
@@ -31,6 +31,7 @@ obj-$(CONFIG_LEDS) += $(led-y)
# Misc features
obj-$(CONFIG_PM) += pm.o sleep.o
obj-$(CONFIG_PXA_SSP) += ssp.o
+obj-$(CONFIG_KGDB_PXA_SERIAL) += kgdb-serial.o
ifeq ($(CONFIG_PXA27x),y)
obj-$(CONFIG_PM) += standby.o
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-pxa/kgdb-serial.c linux-2.6.18.kgdb/arch/arm/mach-pxa/kgdb-serial.c
--- linux-2.6.18/arch/arm/mach-pxa/kgdb-serial.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/arm/mach-pxa/kgdb-serial.c 2008-06-10 16:19:51.000000000 +0400
@@ -0,0 +1,98 @@
+/*
+ * linux/arch/arm/mach-pxa/kgdb-serial.c
+ *
+ * Provides low level kgdb serial support hooks for PXA2xx boards
+ *
+ * Author: Nicolas Pitre
+ * Copyright: (C) 2002-2005 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(CONFIG_KGDB_PXA_FFUART)
+
+#define UART FFUART
+#define CKEN_UART CKEN6_FFUART
+#define GPIO_RX_MD GPIO34_FFRXD_MD
+#define GPIO_TX_MD GPIO39_FFTXD_MD
+
+#elif defined(CONFIG_KGDB_PXA_BTUART)
+
+#define UART BTUART
+#define CKEN_UART CKEN7_BTUART
+#define GPIO_RX_MD GPIO42_BTRXD_MD
+#define GPIO_TX_MD GPIO43_BTTXD_MD
+
+#elif defined(CONFIG_KGDB_PXA_STUART)
+
+#define UART STUART
+#define CKEN_UART CKEN5_STUART
+#define GPIO_RX_MD GPIO46_STRXD_MD
+#define GPIO_TX_MD GPIO47_STTXD_MD
+
+#endif
+
+#define UART_BAUDRATE (CONFIG_KGDB_BAUDRATE)
+
+static volatile unsigned long *port = (unsigned long *)&UART;
+
+static int kgdb_serial_init(void)
+{
+ pxa_set_cken(CKEN_UART, 1);
+ pxa_gpio_mode(GPIO_RX_MD);
+ pxa_gpio_mode(GPIO_TX_MD);
+
+ port[UART_IER] = 0;
+ port[UART_LCR] = LCR_DLAB;
+ port[UART_DLL] = ((921600 / UART_BAUDRATE) & 0xff);
+ port[UART_DLM] = ((921600 / UART_BAUDRATE) >> 8);
+ port[UART_LCR] = LCR_WLS1 | LCR_WLS0;
+ port[UART_MCR] = 0;
+ port[UART_IER] = IER_UUE;
+ port[UART_FCR] = FCR_ITL_16;
+
+ return 0;
+}
+
+static void kgdb_serial_putchar(int c)
+{
+ if (!(CKEN & CKEN_UART) || port[UART_IER] != IER_UUE)
+ kgdb_serial_init();
+ while (!(port[UART_LSR] & LSR_TDRQ))
+ cpu_relax();
+ port[UART_TX] = c;
+}
+
+static void kgdb_serial_flush(void)
+{
+ if ((CKEN & CKEN_UART) && (port[UART_IER] & IER_UUE))
+ while (!(port[UART_LSR] & LSR_TEMT))
+ cpu_relax();
+}
+
+static int kgdb_serial_getchar(void)
+{
+ unsigned char c;
+ if (!(CKEN & CKEN_UART) || port[UART_IER] != IER_UUE)
+ kgdb_serial_init();
+ while (!(port[UART_LSR] & UART_LSR_DR))
+ cpu_relax();
+ c = port[UART_RX];
+ return c;
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .init = kgdb_serial_init,
+ .write_char = kgdb_serial_putchar,
+ .flush = kgdb_serial_flush,
+ .read_char = kgdb_serial_getchar,
+};
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mach-versatile/kgdb_serial.c linux-2.6.18.kgdb/arch/arm/mach-versatile/kgdb_serial.c
--- linux-2.6.18/arch/arm/mach-versatile/kgdb_serial.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/arm/mach-versatile/kgdb_serial.c 2008-06-10 16:19:51.000000000 +0400
@@ -0,0 +1,121 @@
+/*
+ * arch/arm/mach-versatile/kgdb_serial.c
+ *
+ * Author: Manish Lachwani, mlachwani@mvista.com
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * Support for KGDB on ARM Versatile.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define ARM_BAUD_38400 23
+/*
+ * Functions that will be used later
+ */
+#define UART_GET_INT_STATUS(p) readb((p) + UART010_IIR)
+#define UART_GET_MIS(p) readw((p) + UART011_MIS)
+#define UART_PUT_ICR(p, c) writel((c), (p) + UART010_ICR)
+#define UART_GET_FR(p) readb((p) + UART01x_FR)
+#define UART_GET_CHAR(p) readb((p) + UART01x_DR)
+#define UART_PUT_CHAR(p, c) writel((c), (p) + UART01x_DR)
+#define UART_GET_RSR(p) readb((p) + UART01x_RSR)
+#define UART_GET_CR(p) readb((p) + UART010_CR)
+#define UART_PUT_CR(p,c) writel((c), (p) + UART010_CR)
+#define UART_GET_LCRL(p) readb((p) + UART010_LCRL)
+#define UART_PUT_LCRL(p,c) writel((c), (p) + UART010_LCRL)
+#define UART_GET_LCRM(p) readb((p) + UART010_LCRM)
+#define UART_PUT_LCRM(p,c) writel((c), (p) + UART010_LCRM)
+#define UART_GET_LCRH(p) readb((p) + UART010_LCRH)
+#define UART_PUT_LCRH(p,c) writel((c), (p) + UART010_LCRH)
+#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0)
+#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0)
+#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART01x_FR_TMSK) == 0)
+
+/*
+ * KGDB IRQ
+ */
+static int kgdb_irq = 12;
+static volatile unsigned char *port = NULL;
+
+static int kgdb_serial_init(void)
+{
+ int rate = ARM_BAUD_38400;
+
+ port = IO_ADDRESS(0x101F1000);
+ UART_PUT_CR(port, 0);
+
+ /* Set baud rate */
+ UART_PUT_LCRM(port, ((rate & 0xf00) >> 8));
+ UART_PUT_LCRL(port, (rate & 0xff));
+ UART_PUT_LCRH(port, UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN);
+ UART_PUT_CR(port, UART01x_CR_UARTEN);
+
+ return 0;
+}
+
+static void kgdb_serial_putchar(int ch)
+{
+ unsigned int status;
+
+ do {
+ status = UART_GET_FR(port);
+ } while (!UART_TX_READY(status));
+
+ UART_PUT_CHAR(port, ch);
+}
+
+static int kgdb_serial_getchar(void)
+{
+ unsigned int status;
+ int ch;
+
+ do {
+ status = UART_GET_FR(port);
+ } while (!UART_RX_DATA(status));
+ ch = UART_GET_CHAR(port);
+ return ch;
+}
+
+static struct uart_port kgdb_amba_port = {
+ .irq = 12,
+ .iobase = 0,
+ .iotype = UPIO_MEM,
+ .membase = (unsigned char *)IO_ADDRESS(0x101F1000),
+};
+
+static irqreturn_t kgdb_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int status = UART_GET_MIS(port);
+
+ if (irq != kgdb_irq)
+ return IRQ_NONE;
+
+ if (status & 0x40)
+ breakpoint();
+
+ return IRQ_HANDLED;
+}
+
+static void __init kgdb_hookup_irq(void)
+{
+ request_irq(kgdb_irq, kgdb_interrupt, SA_SHIRQ, "GDB-stub",
+ &kgdb_amba_port);
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .init = kgdb_serial_init,
+ .write_char = kgdb_serial_putchar,
+ .read_char = kgdb_serial_getchar,
+ .late_init = kgdb_hookup_irq,
+};
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/arm/mm/extable.c linux-2.6.18.kgdb/arch/arm/mm/extable.c
--- linux-2.6.18/arch/arm/mm/extable.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/arm/mm/extable.c 2008-06-10 16:19:51.000000000 +0400
@@ -2,6 +2,7 @@
* linux/arch/arm/mm/extable.c
*/
#include
+#include
#include
int fixup_exception(struct pt_regs *regs)
@@ -11,6 +12,12 @@ int fixup_exception(struct pt_regs *regs
fixup = search_exception_tables(instruction_pointer(regs));
if (fixup)
regs->ARM_pc = fixup->fixup;
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+#endif
return fixup != NULL;
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/Makefile linux-2.6.18.kgdb/arch/i386/kernel/Makefile
--- linux-2.6.18/arch/i386/kernel/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/kernel/Makefile 2008-06-10 16:19:17.000000000 +0400
@@ -39,6 +39,7 @@ obj-$(CONFIG_VM86) += vm86.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_HPET_TIMER) += hpet.o
obj-$(CONFIG_K8_NB) += k8.o
+obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o
EXTRA_AFLAGS := -traditional
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/entry.S linux-2.6.18.kgdb/arch/i386/kernel/entry.S
--- linux-2.6.18/arch/i386/kernel/entry.S 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/kernel/entry.S 2008-06-10 16:19:58.000000000 +0400
@@ -201,7 +201,7 @@ VM_MASK = 0x00020000
CFI_OFFSET ecx, ECX-OLDESP;\
CFI_OFFSET ebx, EBX-OLDESP
-ENTRY(ret_from_fork)
+KPROBE_ENTRY(ret_from_fork)
CFI_STARTPROC
pushl %eax
CFI_ADJUST_CFA_OFFSET 4
@@ -664,7 +664,7 @@ ENTRY(simd_coprocessor_error)
jmp error_code
CFI_ENDPROC
-ENTRY(device_not_available)
+KPROBE_ENTRY(device_not_available)
RING0_INT_FRAME
pushl $-1 # mark this as an int
CFI_ADJUST_CFA_OFFSET 4
@@ -909,7 +909,7 @@ ENTRY(machine_check)
CFI_ENDPROC
#endif
-ENTRY(spurious_interrupt_bug)
+KPROBE_ENTRY(spurious_interrupt_bug)
RING0_INT_FRAME
pushl $0
CFI_ADJUST_CFA_OFFSET 4
@@ -953,3 +953,108 @@ ENDPROC(arch_unwind_init_running)
#include "syscall_table.S"
syscall_table_size=(.-sys_call_table)
+
+# Here we do call frames. We cheat a bit as we only really need
+# correct frames at locations we can actually look at from a
+# debugger. Since the break instruction trap actually goes thru
+# some of this code, we don't really need info on those areas, but
+# only after the fact. I.e. if we can not step or break in a
+# location or end up with a return address pointing at the
+# location, we don't need a correct call frame for it.
+
+#ifdef CONFIG_KGDB
+
+#include
+/*
+ * The register numbers as known by gdb
+ */
+
+#define _EAX 0
+#define _ECX 1
+#define _EDX 2
+#define _EBX 3
+#define _ESP 4
+#define _EBP 5
+#define _ESI 6
+#define _EDI 7
+#define _PC 8
+#define _EIP 8
+#define _PS 9
+#define _EFLAGS 9
+#define _CS 10
+#define _SS 11
+#define _DS 12
+#define _ES 13
+#define _FS 14
+#define _GS 15
+ /*
+ * This code uses macros defined in linux/dwarf2-lang.h
+ * They attempt to follow the dwarf2 naming conventions... sort of..
+ */
+ENTRY(end_of_stack_stop_unwind_function)
+ .long end_of_stack_stop_unwind_function+1
+
+ .text
+
+ CFI_preamble(c1,_PC,1,1)
+ CFA_define_reference(_ESP,OLDESP) /* Stack pointer */
+ CFA_expression(_EIP)
+ CFA_exp_OP_dup /* copy old esp */
+ CFA_exp_OP_consts(CS-OLDESP) /* offset to CS address */
+ CFA_exp_OP_plus /* should be CS address */
+ CFA_exp_OP_deref /* get the CS */
+ CFA_exp_OP_const4s(VM_MASK|3) /* prepare to mask it */
+ CFA_exp_OP_and /* mask it, zero means kernel */
+ CFA_exp_OP_bra(eip_user_rtn) /* branch if user */
+ CFA_exp_OP_const4s(EIP-OLDESP) /* offset to return address */
+ CFA_exp_OP_plus /* add that in */
+ CFA_exp_OP_skip(eip_end) /* done if kernel, skip out */
+eip_user_rtn:
+ CFA_exp_OP_addr(end_of_stack_stop_unwind_function)/*dummy function */
+eip_end:
+ CFA_expression_end
+ CFA_define_offset(_EBX,EBX-OLDESP)
+ CFA_define_offset(_ECX,ECX-OLDESP)
+ CFA_define_offset(_EDX,EDX-OLDESP)
+ CFA_define_offset(_ESI,ESI-OLDESP)
+ CFA_define_offset(_EDI,EDI-OLDESP)
+ CFA_define_offset(_EBP,EBP-OLDESP)
+ CFA_define_offset(_EAX,EAX-OLDESP)
+ CFA_define_offset(_EFLAGS,EFLAGS-OLDESP)
+ CFI_postamble()
+
+/*
+ * This provides an uwind for our dummy end of unwind function.
+ * Current convention is to provied an undefined return address.
+ */
+ CFI_preamble(c2,_PC,1,1)
+ CFA_define_reference(_ESP,0) /* Stack pointer */
+ CFA_undefine_reg(_EIP)
+ CFI_postamble()
+
+ FDE_preamble(c2,end_of_stack_stop_unwind_function, \
+ end_of_stack_stop_unwind_function+5)
+ FDE_postamble()
+ /*
+ * This is VERY sloppy. At this point all we want to do is get
+ * the frame right for back tracing. It will not be good if
+ * you try to single step. We use already defined labels.
+ * We want to cover all call outs.
+ * We could also recode this as just one FDE, but this works and
+ * I want to get it out.
+ */
+ FDE_preamble(c1,ret_from_fork,ret_from_exception)
+ CFA_define_cfa_offset(4) /* one extra word on stack */
+ FDE_postamble()
+
+ FDE_preamble(c1,ret_from_exception,device_not_available_emulate)
+ FDE_postamble()
+
+ FDE_preamble(c1,device_not_available_emulate,debug)
+ CFA_define_cfa_offset(4) /* one extra word on stack */
+ FDE_postamble()
+
+ FDE_preamble(c1, debug,spurious_interrupt_bug)
+ FDE_postamble()
+
+#endif
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/head.S linux-2.6.18.kgdb/arch/i386/kernel/head.S
--- linux-2.6.18/arch/i386/kernel/head.S 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/kernel/head.S 2008-06-10 16:19:58.000000000 +0400
@@ -10,6 +10,7 @@
.text
#include
#include
+#include
#include
#include
#include
@@ -326,6 +327,10 @@ is386: movl $2,%ecx # set MP
#endif /* CONFIG_SMP */
jmp start_kernel
+ /* This dwarf code tells gdb that this is the end of the unwind */
+ /* This uses the CFA set up for pc=1 located in entry.S */
+ CFI_END_FRAME(is386)
+
/*
* We depend on ET to be correct. This checks for 287/387.
*/
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/kgdb-jmp.S linux-2.6.18.kgdb/arch/i386/kernel/kgdb-jmp.S
--- linux-2.6.18/arch/i386/kernel/kgdb-jmp.S 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/i386/kernel/kgdb-jmp.S 2008-06-10 16:19:17.000000000 +0400
@@ -0,0 +1,74 @@
+/*
+ * arch/i386/kernel/kgdb-jmp.S
+ *
+ * Save and restore system registers so that within a limited frame we
+ * may have a fault and "jump back" to a known safe location.
+ *
+ * Author: George Anzinger
+ *
+ * Cribbed from glibc, which carries the following:
+ * Copyright (C) 1996, 1996, 1997, 2000, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2005 by MontaVista Software.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include
+
+#define PCOFF 0
+#define LINKAGE 4 /* just the return address */
+#define PTR_SIZE 4
+#define PARMS LINKAGE /* no space for saved regs */
+#define JMPBUF PARMS
+#define VAL JMPBUF+PTR_SIZE
+
+#define JB_BX 0
+#define JB_SI 1
+#define JB_DI 2
+#define JB_BP 3
+#define JB_SP 4
+#define JB_PC 5
+
+/* This must be called prior to kgdb_fault_longjmp and
+ * kgdb_fault_longjmp must not be called outside of the context of the
+ * last call to kgdb_fault_setjmp.
+ * kgdb_fault_setjmp(int *jmp_buf[6])
+ */
+ENTRY(kgdb_fault_setjmp)
+ movl JMPBUF(%esp), %eax
+
+ /* Save registers. */
+ movl %ebx, (JB_BX*4)(%eax)
+ movl %esi, (JB_SI*4)(%eax)
+ movl %edi, (JB_DI*4)(%eax)
+ /* Save SP as it will be after we return. */
+ leal JMPBUF(%esp), %ecx
+ movl %ecx, (JB_SP*4)(%eax)
+ movl PCOFF(%esp), %ecx /* Save PC we are returning to now. */
+ movl %ecx, (JB_PC*4)(%eax)
+ movl %ebp, (JB_BP*4)(%eax) /* Save caller's frame pointer. */
+
+ /* Restore state so we can now try the access. */
+ movl JMPBUF(%esp), %ecx /* User's jmp_buf in %ecx. */
+ /* Save the return address now. */
+ movl (JB_PC*4)(%ecx), %edx
+ /* Restore registers. */
+ movl $0, %eax
+ movl (JB_SP*4)(%ecx), %esp
+ jmp *%edx /* Jump to saved PC. */
+
+/* kgdb_fault_longjmp(int *jmp_buf[6]) */
+ENTRY(kgdb_fault_longjmp)
+ movl JMPBUF(%esp), %ecx /* User's jmp_buf in %ecx. */
+ /* Save the return address now. */
+ movl (JB_PC*4)(%ecx), %edx
+ /* Restore registers. */
+ movl (JB_BX*4)(%ecx), %ebx
+ movl (JB_SI*4)(%ecx), %esi
+ movl (JB_DI*4)(%ecx), %edi
+ movl (JB_BP*4)(%ecx), %ebp
+ movl $1, %eax
+ movl (JB_SP*4)(%ecx), %esp
+ jmp *%edx /* Jump to saved PC. */
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/kgdb.c linux-2.6.18.kgdb/arch/i386/kernel/kgdb.c
--- linux-2.6.18/arch/i386/kernel/kgdb.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/i386/kernel/kgdb.c 2008-06-10 16:20:15.000000000 +0400
@@ -0,0 +1,363 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ */
+/*
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale
+ * Updated by: Tom Rini
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by
+ * David Grothe
+ * Additional support from Tigran Aivazian
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* for linux pt_regs struct */
+#include
+#include
+#include
+#include
+#include
+
+#include "mach_ipi.h"
+
+/* Put the error code here just in case the user cares. */
+int gdb_i386errcode;
+/* Likewise, the vector number here (since GDB only gets the signal
+ number through the usual means, and that's not very specific). */
+int gdb_i386vector = -1;
+
+extern atomic_t cpu_doing_single_step;
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ gdb_regs[_EAX] = regs->eax;
+ gdb_regs[_EBX] = regs->ebx;
+ gdb_regs[_ECX] = regs->ecx;
+ gdb_regs[_EDX] = regs->edx;
+ gdb_regs[_ESI] = regs->esi;
+ gdb_regs[_EDI] = regs->edi;
+ gdb_regs[_EBP] = regs->ebp;
+ gdb_regs[_DS] = regs->xds;
+ gdb_regs[_ES] = regs->xes;
+ gdb_regs[_PS] = regs->eflags;
+ gdb_regs[_CS] = regs->xcs;
+ gdb_regs[_PC] = regs->eip;
+ gdb_regs[_ESP] = (int)(®s->esp);
+ gdb_regs[_SS] = __KERNEL_DS;
+ gdb_regs[_FS] = 0xFFFF;
+ gdb_regs[_GS] = 0xFFFF;
+}
+
+/*
+ * Extracts ebp, esp and eip values understandable by gdb from the values
+ * saved by switch_to.
+ * thread.esp points to ebp. flags and ebp are pushed in switch_to hence esp
+ * prior to entering switch_to is 8 greater then the value that is saved.
+ * If switch_to changes, change following code appropriately.
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ gdb_regs[_EAX] = 0;
+ gdb_regs[_EBX] = 0;
+ gdb_regs[_ECX] = 0;
+ gdb_regs[_EDX] = 0;
+ gdb_regs[_ESI] = 0;
+ gdb_regs[_EDI] = 0;
+ gdb_regs[_EBP] = *(unsigned long *)p->thread.esp;
+ gdb_regs[_DS] = __KERNEL_DS;
+ gdb_regs[_ES] = __KERNEL_DS;
+ gdb_regs[_PS] = 0;
+ gdb_regs[_CS] = __KERNEL_CS;
+ gdb_regs[_PC] = p->thread.eip;
+ gdb_regs[_ESP] = p->thread.esp;
+ gdb_regs[_SS] = __KERNEL_DS;
+ gdb_regs[_FS] = 0xFFFF;
+ gdb_regs[_GS] = 0xFFFF;
+}
+
+void gdb_regs_to_regs(unsigned long *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];
+}
+
+static struct hw_breakpoint {
+ unsigned enabled;
+ unsigned type;
+ unsigned len;
+ unsigned addr;
+} breakinfo[4] = {
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+};
+
+void kgdb_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 kgdb_remove_hw_break(unsigned long addr)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (breakinfo[i].addr == addr && breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+
+ breakinfo[idx].enabled = 0;
+ return 0;
+}
+
+void kgdb_remove_all_hw_break(void)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (breakinfo[i].enabled) {
+ /* Do what? */
+ ;
+ }
+ memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+ }
+}
+
+int kgdb_set_hw_break(unsigned long addr)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (!breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+
+ breakinfo[idx].enabled = 1;
+ breakinfo[idx].type = 1;
+ breakinfo[idx].len = 1;
+ breakinfo[idx].addr = addr;
+ return 0;
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ /* Disable hardware debugging while we are in kgdb */
+ asm volatile ("movl %0,%%db7": /* no output */ :"r" (0));
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_i386vector = e_vector;
+ gdb_i386errcode = err_code;
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ send_IPI_allbutself(APIC_DM_NMI);
+}
+
+int kgdb_arch_handle_exception(int e_vector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ long addr;
+ char *ptr;
+ int newPC, dr6;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ case 's':
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr))
+ linux_regs->eip = addr;
+ newPC = linux_regs->eip;
+
+ /* clear the trace bit */
+ linux_regs->eflags &= ~TF_MASK;
+ atomic_set(&cpu_doing_single_step, -1);
+
+ /* set the trace bit if we're stepping */
+ if (remcom_in_buffer[0] == 's') {
+ linux_regs->eflags |= TF_MASK;
+ debugger_step = 1;
+ atomic_set(&cpu_doing_single_step,smp_processor_id());
+ }
+
+ asm volatile ("movl %%db6, %0\n":"=r" (dr6));
+ if (!(dr6 & 0x4000)) {
+ long breakno;
+ for (breakno = 0; breakno < 4; ++breakno) {
+ if (dr6 & (1 << breakno) &&
+ breakinfo[breakno].type == 0) {
+ /* Set restore flag */
+ linux_regs->eflags |= X86_EFLAGS_RF;
+ break;
+ }
+ }
+ }
+ kgdb_correct_hw_break();
+ asm volatile ("movl %0, %%db6\n"::"r" (0));
+
+ return (0);
+ } /* switch */
+ /* this means that we do not want to exit from the handler */
+ return -1;
+}
+
+/* Register KGDB with the i386die_chain so that we hook into all of the right
+ * spots. */
+static int kgdb_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *args = ptr;
+ struct pt_regs *regs = args->regs;
+
+ /* Bad memory access? */
+ if (cmd == DIE_PAGE_FAULT_NO_CONTEXT && atomic_read(&debugger_active)
+ && kgdb_may_fault) {
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ return NOTIFY_STOP;
+ } else if (cmd == DIE_PAGE_FAULT)
+ /* A normal page fault, ignore. */
+ return NOTIFY_DONE;
+ else if ((cmd == DIE_NMI || cmd == DIE_NMI_IPI ||
+ cmd == DIE_NMIWATCHDOG) && atomic_read(&debugger_active)) {
+ /* CPU roundup */
+ kgdb_nmihook(smp_processor_id(), regs);
+ return NOTIFY_STOP;
+ } else if (cmd == DIE_NMI_IPI || cmd == DIE_NMI || user_mode(regs) ||
+ (cmd == DIE_DEBUG && atomic_read(&debugger_active)))
+ /* Normal watchdog event or userspace debugging, or spurious
+ * debug exception, ignore. */
+ return NOTIFY_DONE;
+
+ kgdb_handle_exception(args->trapnr, args->signr, args->err, regs);
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_notify,
+};
+
+int kgdb_arch_init(void)
+{
+ atomic_notifier_chain_register(&i386die_chain, &kgdb_notifier);
+ return 0;
+}
+
+/*
+ * Skip an int3 exception when it occurs after a breakpoint has been
+ * removed. Backtrack eip by 1 since the int3 would have caused it to
+ * increment by 1.
+ */
+
+int kgdb_skipexception(int exception, struct pt_regs *regs)
+{
+ if (exception == 3 && kgdb_isremovedbreak(regs->eip - 1)) {
+ regs->eip -= 1;
+ return 1;
+ }
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0xcc},
+ .flags = KGDB_HW_BREAKPOINT,
+};
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/process.c linux-2.6.18.kgdb/arch/i386/kernel/process.c
--- linux-2.6.18/arch/i386/kernel/process.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/kernel/process.c 2008-06-10 16:19:58.000000000 +0400
@@ -328,7 +328,27 @@ __asm__(".section .text\n"
"call *%ebx\n\t"
"pushl %eax\n\t"
"call do_exit\n"
+ "kernel_thread_helper_end:\n\t"
".previous");
+#ifdef CONFIG_KGDB
+#include
+
+ /* This dwarf code tells gdb that this is the end of the unwind */
+ /* This uses the CFA set up for pc=1 located in entry.S */
+#define _ESP 4
+#define _PC 8
+#define _EIP 8
+__asm__(
+ QUOTE_THIS(
+ CFI_preamble(dwarf_4,_PC,1,1)
+ CFA_define_reference(_ESP,0) /* Stack pointer */
+ CFA_undefine_reg(_EIP)
+ CFI_postamble()
+
+ FDE_preamble(dwarf_4,kernel_thread_helper,kernel_thread_helper_end)
+ FDE_postamble()
+ ));
+#endif
/*
* Create a kernel thread
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/setup.c linux-2.6.18.kgdb/arch/i386/kernel/setup.c
--- linux-2.6.18/arch/i386/kernel/setup.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/kernel/setup.c 2008-06-10 16:19:17.000000000 +0400
@@ -148,6 +148,7 @@ EXPORT_SYMBOL(ist_info);
struct e820map e820;
extern void early_cpu_init(void);
+extern void early_trap_init(void);
extern void generic_apic_probe(char *);
extern int root_mountflags;
@@ -1444,6 +1445,7 @@ void __init setup_arch(char **cmdline_p)
memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data));
pre_setup_arch_hook();
early_cpu_init();
+ early_trap_init();
/*
* FIXME: This isn't an official loader_type right
@@ -1500,6 +1502,7 @@ void __init setup_arch(char **cmdline_p)
data_resource.end = virt_to_phys(_edata)-1;
parse_cmdline_early(cmdline_p);
+ parse_early_param();
#ifdef CONFIG_EARLY_PRINTK
{
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/smpboot.c linux-2.6.18.kgdb/arch/i386/kernel/smpboot.c
--- linux-2.6.18/arch/i386/kernel/smpboot.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/kernel/smpboot.c 2008-06-10 16:19:58.000000000 +0400
@@ -592,6 +592,9 @@ void __devinit initialize_secondary(void
asm volatile(
"movl %0,%%esp\n\t"
+#ifdef CONFIG_KGDB
+ "pushl end_of_stack_stop_unwind_function\n\t"
+#endif
"jmp *%1"
:
:"r" (current->thread.esp),"r" (current->thread.eip));
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/kernel/traps.c linux-2.6.18.kgdb/arch/i386/kernel/traps.c
--- linux-2.6.18/arch/i386/kernel/traps.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/kernel/traps.c 2008-06-10 16:19:17.000000000 +0400
@@ -863,6 +863,7 @@ fastcall void __kprobes do_debug(struct
*/
clear_dr7:
set_debugreg(0, 7);
+ notify_die(DIE_DEBUG, "debug2", regs, condition, error_code, SIGTRAP);
return;
debug_vm86:
@@ -1167,6 +1168,12 @@ static void __init set_task_gate(unsigne
_set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
}
+/* Some traps need to be set early. */
+void __init early_trap_init(void) {
+ set_intr_gate(1,&debug);
+ set_system_intr_gate(3, &int3); /* int3 can be called from all */
+ set_intr_gate(14,&page_fault);
+}
void __init trap_init(void)
{
@@ -1183,10 +1190,8 @@ void __init trap_init(void)
#endif
set_trap_gate(0,÷_error);
- set_intr_gate(1,&debug);
set_intr_gate(2,&nmi);
- set_system_intr_gate(3, &int3); /* int3/4 can be called from all */
- set_system_gate(4,&overflow);
+ set_system_gate(4,&overflow); /* int4/5 can be called from all */
set_trap_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
@@ -1196,7 +1201,6 @@ void __init trap_init(void)
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
- set_intr_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/i386/mm/fault.c linux-2.6.18.kgdb/arch/i386/mm/fault.c
--- linux-2.6.18/arch/i386/mm/fault.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/i386/mm/fault.c 2008-06-10 16:19:17.000000000 +0400
@@ -539,6 +539,10 @@ no_context:
if (is_prefetch(regs, address, error_code))
return;
+ if (notify_die(DIE_PAGE_FAULT_NO_CONTEXT, "no context", regs,
+ error_code, 14, SIGSEGV) == NOTIFY_STOP)
+ return;
+
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/Makefile linux-2.6.18.kgdb/arch/ia64/kernel/Makefile
--- linux-2.6.18/arch/ia64/kernel/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/kernel/Makefile 2008-06-10 16:19:32.000000000 +0400
@@ -31,6 +31,7 @@ obj-$(CONFIG_KPROBES) += kprobes.o jpro
obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o
obj-$(CONFIG_AUDIT) += audit.o
mca_recovery-y += mca_drv.o mca_drv_asm.o
+obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o
# The gate DSO image is built using a special linker script.
targets += gate.so gate-syms.o
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/entry.S linux-2.6.18.kgdb/arch/ia64/kernel/entry.S
--- linux-2.6.18/arch/ia64/kernel/entry.S 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/kernel/entry.S 2008-06-10 16:20:23.000000000 +0400
@@ -953,9 +953,9 @@ GLOBAL_ENTRY(ia64_leave_kernel)
shr.u r18=r19,16 // get byte size of existing "dirty" partition
;;
mov r16=ar.bsp // get existing backing store pointer
- addl r17=THIS_CPU(ia64_phys_stacked_size_p8),r0
+(pUStk) addl r17=THIS_CPU(ia64_phys_stacked_size_p8),r0
;;
- ld4 r17=[r17] // r17 = cpu_data->phys_stacked_size_p8
+(pUStk) ld4 r17=[r17] // r17 = cpu_data->phys_stacked_size_p8
(pKStk) br.cond.dpnt skip_rbs_switch
/*
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/ivt.S linux-2.6.18.kgdb/arch/ia64/kernel/ivt.S
--- linux-2.6.18/arch/ia64/kernel/ivt.S 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/kernel/ivt.S 2008-06-10 16:20:23.000000000 +0400
@@ -52,6 +52,14 @@
#include
#include
+#ifdef CONFIG_KGDB
+#define KGDB_ENABLE_PSR_DB mov r31=psr;; movl r30=IA64_PSR_DB;; \
+ or r31=r31,r30;; \
+ mov psr.l=r31;; srlz.i;;
+#else
+#define KGDB_ENABLE_PSR_DB
+#endif
+
#if 1
# define PSR_DEFAULT_BITS psr.ac
#else
@@ -519,6 +527,7 @@ ENTRY(page_fault)
movl r14=ia64_leave_kernel
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
mov rp=r14
;;
adds out2=16,r12 // out2 = pointer to pt_regs
@@ -863,6 +872,7 @@ ENTRY(interrupt)
srlz.i // ensure everybody knows psr.ic is back on
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
;;
MCA_RECOVER_RANGE(interrupt)
alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group
@@ -1110,6 +1120,7 @@ ENTRY(non_syscall)
movl r15=ia64_leave_kernel
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
mov rp=r15
;;
br.call.sptk.many b6=ia64_bad_break // avoid WAW on CFM and ignore return addr
@@ -1143,6 +1154,7 @@ ENTRY(dispatch_unaligned_handler)
adds r3=8,r2 // set up second base pointer
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
movl r14=ia64_leave_kernel
;;
mov rp=r14
@@ -1185,6 +1197,10 @@ ENTRY(dispatch_to_fault_handler)
adds r3=8,r2 // set up second base pointer for SAVE_REST
;;
SAVE_REST
+ cmp.eq p6,p0=29,out0
+(p6) br.cond.spnt 1f;; // debug_vector
+ KGDB_ENABLE_PSR_DB
+1:
movl r14=ia64_leave_kernel
;;
mov rp=r14
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/kgdb-jmp.S linux-2.6.18.kgdb/arch/ia64/kernel/kgdb-jmp.S
--- linux-2.6.18/arch/ia64/kernel/kgdb-jmp.S 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/ia64/kernel/kgdb-jmp.S 2008-06-10 16:19:32.000000000 +0400
@@ -0,0 +1,238 @@
+/* setjmp() and longjmp() assembler support for kdb on ia64.
+
+ This code was copied from glibc CVS as of 2001-06-27 and modified where
+ necessary to fit the kernel.
+ Keith Owens 2001-06-27
+ */
+
+/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang .
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If
+ not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include
+GLOBAL_ENTRY(kgdb_fault_setjmp)
+ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)
+ alloc loc1=ar.pfs,2,2,2,0
+ mov r16=ar.unat
+ ;;
+ mov r17=ar.fpsr
+ mov r2=in0
+ add r3=8,in0
+ ;;
+.mem.offset 0,0;
+ st8.spill.nta [r2]=sp,16 // r12 (sp)
+.mem.offset 8,0;
+ st8.spill.nta [r3]=gp,16 // r1 (gp)
+ ;;
+ st8.nta [r2]=r16,16 // save caller's unat
+ st8.nta [r3]=r17,16 // save fpsr
+ add r8=0xa0,in0
+ ;;
+.mem.offset 160,0;
+ st8.spill.nta [r2]=r4,16 // r4
+.mem.offset 168,0;
+ st8.spill.nta [r3]=r5,16 // r5
+ add r9=0xb0,in0
+ ;;
+ stf.spill.nta [r8]=f2,32
+ stf.spill.nta [r9]=f3,32
+ mov loc0=rp
+ .body
+ ;;
+ stf.spill.nta [r8]=f4,32
+ stf.spill.nta [r9]=f5,32
+ mov r17=b1
+ ;;
+ stf.spill.nta [r8]=f16,32
+ stf.spill.nta [r9]=f17,32
+ mov r18=b2
+ ;;
+ stf.spill.nta [r8]=f18,32
+ stf.spill.nta [r9]=f19,32
+ mov r19=b3
+ ;;
+ stf.spill.nta [r8]=f20,32
+ stf.spill.nta [r9]=f21,32
+ mov r20=b4
+ ;;
+ stf.spill.nta [r8]=f22,32
+ stf.spill.nta [r9]=f23,32
+ mov r21=b5
+ ;;
+ stf.spill.nta [r8]=f24,32
+ stf.spill.nta [r9]=f25,32
+ mov r22=ar.lc
+ ;;
+ stf.spill.nta [r8]=f26,32
+ stf.spill.nta [r9]=f27,32
+ mov r24=pr
+ ;;
+ stf.spill.nta [r8]=f28,32
+ stf.spill.nta [r9]=f29,32
+ ;;
+ stf.spill.nta [r8]=f30
+ stf.spill.nta [r9]=f31
+
+.mem.offset 0,0;
+ st8.spill.nta [r2]=r6,16 // r6
+.mem.offset 8,0;
+ st8.spill.nta [r3]=r7,16 // r7
+ ;;
+ mov r23=ar.bsp
+ mov r25=ar.unat
+ st8.nta [r2]=loc0,16 // b0
+ st8.nta [r3]=r17,16 // b1
+ ;;
+ st8.nta [r2]=r18,16 // b2
+ st8.nta [r3]=r19,16 // b3
+ ;;
+ st8.nta [r2]=r20,16 // b4
+ st8.nta [r3]=r21,16 // b5
+ ;;
+ st8.nta [r2]=loc1,16 // ar.pfs
+ st8.nta [r3]=r22,16 // ar.lc
+ ;;
+ st8.nta [r2]=r24,16 // pr
+ st8.nta [r3]=r23,16 // ar.bsp
+ ;;
+ st8.nta [r2]=r25 // ar.unat
+ st8.nta [r3]=in0 // &__jmp_buf
+ mov r8=0
+ mov rp=loc0
+ mov ar.pfs=loc1
+ br.ret.sptk.few rp
+END(kdba_setjmp)
+#define pPos p6 /* is rotate count positive? */
+#define pNeg p7 /* is rotate count negative? */
+GLOBAL_ENTRY(kgdb_fault_longjmp)
+ alloc r8=ar.pfs,2,1,0,0
+ mov r27=ar.rsc
+ add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr
+ ;;
+ ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr
+ mov r10=ar.bsp
+ and r11=~0x3,r27 // clear ar.rsc.mode
+ ;;
+ flushrs // flush dirty regs to backing store (must be first in insn grp)
+ ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp
+ sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf
+ ;;
+ ld8 r25=[r2] // r25 <- jmpbuf.ar_unat
+ extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f
+ ;;
+ cmp.lt pNeg,pPos=r8,r0
+ mov r2=in0
+ ;;
+(pPos) mov r16=r8
+(pNeg) add r16=64,r8
+(pPos) sub r17=64,r8
+(pNeg) sub r17=r0,r8
+ ;;
+ mov ar.rsc=r11 // put RSE in enforced lazy mode
+ shr.u r8=r25,r16
+ add r3=8,in0 // r3 <- &jmpbuf.r1
+ shl r9=r25,r17
+ ;;
+ or r25=r8,r9
+ ;;
+ mov r26=ar.rnat
+ mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12)
+ ;;
+ ld8.fill.nta sp=[r2],16 // r12 (sp)
+ ld8.fill.nta gp=[r3],16 // r1 (gp)
+ dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ ;;
+ ld8.nta r16=[r2],16 // caller's unat
+ ld8.nta r17=[r3],16 // fpsr
+ ;;
+ ld8.fill.nta r4=[r2],16 // r4
+ ld8.fill.nta r5=[r3],16 // r5 (gp)
+ cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp)
+ ;;
+ ld8.fill.nta r6=[r2],16 // r6
+ ld8.fill.nta r7=[r3],16 // r7
+ ;;
+ mov ar.unat=r16 // restore caller's unat
+ mov ar.fpsr=r17 // restore fpsr
+ ;;
+ ld8.nta r16=[r2],16 // b0
+ ld8.nta r17=[r3],16 // b1
+ ;;
+(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ mov ar.bspstore=r23 // restore ar.bspstore
+ ;;
+ ld8.nta r18=[r2],16 // b2
+ ld8.nta r19=[r3],16 // b3
+ ;;
+ ld8.nta r20=[r2],16 // b4
+ ld8.nta r21=[r3],16 // b5
+ ;;
+ ld8.nta r11=[r2],16 // ar.pfs
+ ld8.nta r22=[r3],56 // ar.lc
+ ;;
+ ld8.nta r24=[r2],32 // pr
+ mov b0=r16
+ ;;
+ ldf.fill.nta f2=[r2],32
+ ldf.fill.nta f3=[r3],32
+ mov b1=r17
+ ;;
+ ldf.fill.nta f4=[r2],32
+ ldf.fill.nta f5=[r3],32
+ mov b2=r18
+ ;;
+ ldf.fill.nta f16=[r2],32
+ ldf.fill.nta f17=[r3],32
+ mov b3=r19
+ ;;
+ ldf.fill.nta f18=[r2],32
+ ldf.fill.nta f19=[r3],32
+ mov b4=r20
+ ;;
+ ldf.fill.nta f20=[r2],32
+ ldf.fill.nta f21=[r3],32
+ mov b5=r21
+ ;;
+ ldf.fill.nta f22=[r2],32
+ ldf.fill.nta f23=[r3],32
+ mov ar.lc=r22
+ ;;
+ ldf.fill.nta f24=[r2],32
+ ldf.fill.nta f25=[r3],32
+ cmp.eq p8,p9=0,in1
+ ;;
+ ldf.fill.nta f26=[r2],32
+ ldf.fill.nta f27=[r3],32
+ mov ar.pfs=r11
+ ;;
+ ldf.fill.nta f28=[r2],32
+ ldf.fill.nta f29=[r3],32
+ ;;
+ ldf.fill.nta f30=[r2]
+ ldf.fill.nta f31=[r3]
+(p8) mov r8=1
+
+ mov ar.rnat=r26 // restore ar.rnat
+ ;;
+ mov ar.rsc=r27 // restore ar.rsc
+(p9) mov r8=in1
+
+ invala // virt. -> phys. regnum mapping may change
+ mov pr=r24,-1
+ br.ret.sptk.few rp
+END(kgdb_fault_longjmp)
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/kgdb.c linux-2.6.18.kgdb/arch/ia64/kernel/kgdb.c
--- linux-2.6.18/arch/ia64/kernel/kgdb.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/ia64/kernel/kgdb.c 2008-06-10 16:19:32.000000000 +0400
@@ -0,0 +1,1131 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * (c) Copyright 2005 Hewlett-Packard Development Company, L.P.
+ * Bob Picco
+ */
+/*
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* for linux pt_regs struct */
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define NUM_REGS 590
+#define REGISTER_BYTES (NUM_REGS*8+128*8)
+#define REGISTER_BYTE(N) (((N) * 8) \
+ + ((N) <= IA64_FR0_REGNUM ? \
+ 0 : 8 * (((N) > IA64_FR127_REGNUM) ? 128 : (N) - IA64_FR0_REGNUM)))
+#define REGISTER_SIZE(N) \
+ (((N) >= IA64_FR0_REGNUM && (N) <= IA64_FR127_REGNUM) ? 16 : 8)
+#define IA64_GR0_REGNUM 0
+#define IA64_FR0_REGNUM 128
+#define IA64_FR127_REGNUM (IA64_FR0_REGNUM+127)
+#define IA64_PR0_REGNUM 256
+#define IA64_BR0_REGNUM 320
+#define IA64_VFP_REGNUM 328
+#define IA64_PR_REGNUM 330
+#define IA64_IP_REGNUM 331
+#define IA64_PSR_REGNUM 332
+#define IA64_CFM_REGNUM 333
+#define IA64_AR0_REGNUM 334
+#define IA64_NAT0_REGNUM 462
+#define IA64_NAT31_REGNUM (IA64_NAT0_REGNUM+31)
+#define IA64_NAT32_REGNUM (IA64_NAT0_REGNUM+32)
+#define IA64_RSC_REGNUM (IA64_AR0_REGNUM+16)
+#define IA64_BSP_REGNUM (IA64_AR0_REGNUM+17)
+#define IA64_BSPSTORE_REGNUM (IA64_AR0_REGNUM+18)
+#define IA64_RNAT_REGNUM (IA64_AR0_REGNUM+19)
+#define IA64_FCR_REGNUM (IA64_AR0_REGNUM+21)
+#define IA64_EFLAG_REGNUM (IA64_AR0_REGNUM+24)
+#define IA64_CSD_REGNUM (IA64_AR0_REGNUM+25)
+#define IA64_SSD_REGNUM (IA64_AR0_REGNUM+26)
+#define IA64_CFLG_REGNUM (IA64_AR0_REGNUM+27)
+#define IA64_FSR_REGNUM (IA64_AR0_REGNUM+28)
+#define IA64_FIR_REGNUM (IA64_AR0_REGNUM+29)
+#define IA64_FDR_REGNUM (IA64_AR0_REGNUM+30)
+#define IA64_CCV_REGNUM (IA64_AR0_REGNUM+32)
+#define IA64_UNAT_REGNUM (IA64_AR0_REGNUM+36)
+#define IA64_FPSR_REGNUM (IA64_AR0_REGNUM+40)
+#define IA64_ITC_REGNUM (IA64_AR0_REGNUM+44)
+#define IA64_PFS_REGNUM (IA64_AR0_REGNUM+64)
+#define IA64_LC_REGNUM (IA64_AR0_REGNUM+65)
+#define IA64_EC_REGNUM (IA64_AR0_REGNUM+66)
+
+#define REGISTER_INDEX(N) (REGISTER_BYTE(N) / sizeof (unsigned long))
+#define BREAK_INSTR_ALIGN (~0xfULL)
+
+#define ptoff(V) ((unsigned int) &((struct pt_regs *)0x0)->V)
+struct reg_to_ptreg_index {
+ unsigned int reg;
+ unsigned int ptregoff;
+};
+
+static struct reg_to_ptreg_index gr_reg_to_ptreg_index[] = {
+ {IA64_GR0_REGNUM + 1, ptoff(r1)},
+ {IA64_GR0_REGNUM + 2, ptoff(r2)},
+ {IA64_GR0_REGNUM + 3, ptoff(r3)},
+ {IA64_GR0_REGNUM + 8, ptoff(r8)},
+ {IA64_GR0_REGNUM + 9, ptoff(r9)},
+ {IA64_GR0_REGNUM + 10, ptoff(r10)},
+ {IA64_GR0_REGNUM + 11, ptoff(r11)},
+ {IA64_GR0_REGNUM + 12, ptoff(r12)},
+ {IA64_GR0_REGNUM + 13, ptoff(r13)},
+ {IA64_GR0_REGNUM + 14, ptoff(r14)},
+ {IA64_GR0_REGNUM + 15, ptoff(r15)},
+ {IA64_GR0_REGNUM + 16, ptoff(r16)},
+ {IA64_GR0_REGNUM + 17, ptoff(r17)},
+ {IA64_GR0_REGNUM + 18, ptoff(r18)},
+ {IA64_GR0_REGNUM + 19, ptoff(r19)},
+ {IA64_GR0_REGNUM + 20, ptoff(r20)},
+ {IA64_GR0_REGNUM + 21, ptoff(r21)},
+ {IA64_GR0_REGNUM + 22, ptoff(r22)},
+ {IA64_GR0_REGNUM + 23, ptoff(r23)},
+ {IA64_GR0_REGNUM + 24, ptoff(r24)},
+ {IA64_GR0_REGNUM + 25, ptoff(r25)},
+ {IA64_GR0_REGNUM + 26, ptoff(r26)},
+ {IA64_GR0_REGNUM + 27, ptoff(r27)},
+ {IA64_GR0_REGNUM + 28, ptoff(r28)},
+ {IA64_GR0_REGNUM + 29, ptoff(r29)},
+ {IA64_GR0_REGNUM + 30, ptoff(r30)},
+ {IA64_GR0_REGNUM + 31, ptoff(r31)},
+};
+
+static struct reg_to_ptreg_index br_reg_to_ptreg_index[] = {
+ {IA64_BR0_REGNUM, ptoff(b0)},
+ {IA64_BR0_REGNUM + 6, ptoff(b6)},
+ {IA64_BR0_REGNUM + 7, ptoff(b7)},
+};
+
+static struct reg_to_ptreg_index ar_reg_to_ptreg_index[] = {
+ {IA64_PFS_REGNUM, ptoff(ar_pfs)},
+ {IA64_UNAT_REGNUM, ptoff(ar_unat)},
+ {IA64_RNAT_REGNUM, ptoff(ar_rnat)},
+ {IA64_BSPSTORE_REGNUM, ptoff(ar_bspstore)},
+ {IA64_RSC_REGNUM, ptoff(ar_rsc)},
+ {IA64_CSD_REGNUM, ptoff(ar_csd)},
+ {IA64_SSD_REGNUM, ptoff(ar_ssd)},
+ {IA64_FPSR_REGNUM, ptoff(ar_fpsr)},
+ {IA64_CCV_REGNUM, ptoff(ar_ccv)},
+};
+
+extern atomic_t cpu_doing_single_step;
+
+static int kgdb_gr_reg(int regnum, struct unw_frame_info *info,
+ unsigned long *reg, int rw)
+{
+ char nat;
+
+ if ((regnum >= IA64_GR0_REGNUM && regnum <= (IA64_GR0_REGNUM + 1)) ||
+ (regnum >= (IA64_GR0_REGNUM + 4) &&
+ regnum <= (IA64_GR0_REGNUM + 7)))
+ return !unw_access_gr(info, regnum - IA64_GR0_REGNUM,
+ reg, &nat, rw);
+ else
+ return 0;
+}
+static int kgdb_gr_ptreg(int regnum, struct pt_regs * ptregs,
+ struct unw_frame_info *info, unsigned long *reg, int rw)
+{
+ int i, result = 1;
+ char nat;
+
+ if (!((regnum >= (IA64_GR0_REGNUM + 2) &&
+ regnum <= (IA64_GR0_REGNUM + 3)) ||
+ (regnum >= (IA64_GR0_REGNUM + 8) &&
+ regnum <= (IA64_GR0_REGNUM + 15)) ||
+ (regnum >= (IA64_GR0_REGNUM + 16) &&
+ regnum <= (IA64_GR0_REGNUM + 31))))
+ return 0;
+ else if (rw && ptregs) {
+ for (i = 0; i < ARRAY_SIZE(gr_reg_to_ptreg_index); i++)
+ if (gr_reg_to_ptreg_index[i].reg == regnum) {
+ *((unsigned long *)(((void *)ptregs) +
+ gr_reg_to_ptreg_index[i].ptregoff)) = *reg;
+ break;
+ }
+ } else if (!rw && ptregs) {
+ for (i = 0; i < ARRAY_SIZE(gr_reg_to_ptreg_index); i++)
+ if (gr_reg_to_ptreg_index[i].reg == regnum) {
+ *reg = *((unsigned long *)
+ (((void *)ptregs) +
+ gr_reg_to_ptreg_index[i].ptregoff));
+ break;
+ }
+ } else
+ result = !unw_access_gr(info, regnum - IA64_GR0_REGNUM,
+ reg, &nat, rw);
+ return result;
+}
+
+static int kgdb_br_reg(int regnum, struct pt_regs * ptregs,
+ struct unw_frame_info *info, unsigned long *reg, int rw)
+{
+ int i, result = 1;
+
+ if (!(regnum >= IA64_BR0_REGNUM && regnum <= (IA64_BR0_REGNUM + 7)))
+ return 0;
+
+ switch (regnum) {
+ case IA64_BR0_REGNUM:
+ case IA64_BR0_REGNUM + 6:
+ case IA64_BR0_REGNUM + 7:
+ if (rw) {
+ for (i = 0; i < ARRAY_SIZE(br_reg_to_ptreg_index); i++)
+ if (br_reg_to_ptreg_index[i].reg == regnum) {
+ *((unsigned long *)
+ (((void *)ptregs) +
+ br_reg_to_ptreg_index[i].ptregoff)) =
+ *reg;
+ break;
+ }
+ } else
+ for (i = 0; i < ARRAY_SIZE(br_reg_to_ptreg_index); i++)
+ if (br_reg_to_ptreg_index[i].reg == regnum) {
+ *reg = *((unsigned long *)
+ (((void *)ptregs) +
+ br_reg_to_ptreg_index[i].
+ ptregoff));
+ break;
+ }
+ break;
+ case IA64_BR0_REGNUM + 1:
+ case IA64_BR0_REGNUM + 2:
+ case IA64_BR0_REGNUM + 3:
+ case IA64_BR0_REGNUM + 4:
+ case IA64_BR0_REGNUM + 5:
+ result = !unw_access_br(info, regnum - IA64_BR0_REGNUM,
+ reg, rw);
+ break;
+ }
+
+ return result;
+}
+
+static int kgdb_fr_reg(int regnum, char *inbuffer, struct pt_regs * ptregs,
+ struct unw_frame_info *info, unsigned long *reg,
+ struct ia64_fpreg *freg, int rw)
+{
+ int result = 1;
+
+ if (!(regnum >= IA64_FR0_REGNUM && regnum <= (IA64_FR0_REGNUM + 127)))
+ return 0;
+
+ switch (regnum) {
+ case IA64_FR0_REGNUM + 6:
+ case IA64_FR0_REGNUM + 7:
+ case IA64_FR0_REGNUM + 8:
+ case IA64_FR0_REGNUM + 9:
+ case IA64_FR0_REGNUM + 10:
+ case IA64_FR0_REGNUM + 11:
+ case IA64_FR0_REGNUM + 12:
+ if (rw) {
+ char *ptr = inbuffer;
+
+ freg->u.bits[0] = *reg;
+ kgdb_hex2long(&ptr, &freg->u.bits[1]);
+ *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM + 6))) =
+ *freg;
+ break;
+ } else if (!ptregs)
+ result = !unw_access_fr(info, regnum - IA64_FR0_REGNUM,
+ freg, rw);
+ else
+ *freg =
+ *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM + 6)));
+ break;
+ default:
+ if (!rw)
+ result = !unw_access_fr(info, regnum - IA64_FR0_REGNUM,
+ freg, rw);
+ else
+ result = 0;
+ break;
+ }
+
+ return result;
+}
+
+static int kgdb_ar_reg(int regnum, struct pt_regs * ptregs,
+ struct unw_frame_info *info, unsigned long *reg, int rw)
+{
+ int result = 0, i;
+
+ if (!(regnum >= IA64_AR0_REGNUM && regnum <= IA64_EC_REGNUM))
+ return 0;
+
+ if (rw && ptregs) {
+ for (i = 0; i < ARRAY_SIZE(ar_reg_to_ptreg_index); i++)
+ if (ar_reg_to_ptreg_index[i].reg == regnum) {
+ *((unsigned long *) (((void *)ptregs) +
+ ar_reg_to_ptreg_index[i].ptregoff)) =
+ *reg;
+ result = 1;
+ break;
+ }
+ } else if (ptregs) {
+ for (i = 0; i < ARRAY_SIZE(ar_reg_to_ptreg_index); i++)
+ if (ar_reg_to_ptreg_index[i].reg == regnum) {
+ *reg = *((unsigned long *) (((void *)ptregs) +
+ ar_reg_to_ptreg_index[i].ptregoff));
+ result = 1;
+ break;
+ }
+ }
+
+ if (result)
+ return result;
+
+ result = 1;
+
+ switch (regnum) {
+ case IA64_CSD_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_CSD, reg, rw);
+ break;
+ case IA64_SSD_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_SSD, reg, rw);
+ break;
+ case IA64_UNAT_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw);
+ break;
+ case IA64_RNAT_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw);
+ break;
+ case IA64_BSPSTORE_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw);
+ break;
+ case IA64_PFS_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw);
+ break;
+ case IA64_LC_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_LC, reg, rw);
+ break;
+ case IA64_EC_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_EC, reg, rw);
+ break;
+ case IA64_FPSR_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_FPSR, reg, rw);
+ break;
+ case IA64_RSC_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_RSC, reg, rw);
+ break;
+ case IA64_CCV_REGNUM:
+ result = !unw_access_ar(info, UNW_AR_CCV, reg, rw);
+ break;
+ default:
+ result = 0;
+ }
+
+ return result;
+}
+
+void kgdb_get_reg(char *outbuffer, int regnum, struct unw_frame_info *info,
+ struct pt_regs *ptregs)
+{
+ unsigned long reg, size = 0, *mem = ®
+ struct ia64_fpreg freg;
+
+ if (kgdb_gr_reg(regnum, info, ®, 0) ||
+ kgdb_gr_ptreg(regnum, ptregs, info, ®, 0) ||
+ kgdb_br_reg(regnum, ptregs, info, ®, 0) ||
+ kgdb_ar_reg(regnum, ptregs, info, ®, 0))
+ size = sizeof(reg);
+ else if (kgdb_fr_reg(regnum, NULL, ptregs, info, ®, &freg, 0)) {
+ size = sizeof(freg);
+ mem = (unsigned long *)&freg;
+ } else if (regnum == IA64_IP_REGNUM) {
+ if (!ptregs) {
+ unw_get_ip(info, ®);
+ size = sizeof(reg);
+ } else {
+ reg = ptregs->cr_iip;
+ size = sizeof(reg);
+ }
+ } else if (regnum == IA64_CFM_REGNUM) {
+ if (!ptregs)
+ unw_get_cfm(info, ®);
+ else
+ reg = ptregs->cr_ifs;
+ size = sizeof(reg);
+ } else if (regnum == IA64_PSR_REGNUM) {
+ if (!ptregs && kgdb_usethread)
+ ptregs = (struct pt_regs *)
+ ((unsigned long)kgdb_usethread +
+ IA64_STK_OFFSET) - 1;
+ if (ptregs)
+ reg = ptregs->cr_ipsr;
+ size = sizeof(reg);
+ } else if (regnum == IA64_PR_REGNUM) {
+ if (ptregs)
+ reg = ptregs->pr;
+ else
+ unw_access_pr(info, ®, 0);
+ size = sizeof(reg);
+ } else if (regnum == IA64_BSP_REGNUM) {
+ unw_get_bsp(info, ®);
+ size = sizeof(reg);
+ }
+
+ if (size) {
+ kgdb_mem2hex((char *) mem, outbuffer, size);
+ outbuffer[size*2] = 0;
+ }
+ else
+ strcpy(outbuffer, "E0");
+
+ return;
+}
+
+void kgdb_put_reg(char *inbuffer, char *outbuffer, int regnum,
+ struct unw_frame_info *info, struct pt_regs *ptregs)
+{
+ unsigned long reg;
+ struct ia64_fpreg freg;
+ char *ptr = inbuffer;
+
+ kgdb_hex2long(&ptr, ®);
+ strcpy(outbuffer, "OK");
+
+ if (kgdb_gr_reg(regnum, info, ®, 1) ||
+ kgdb_gr_ptreg(regnum, ptregs, info, ®, 1) ||
+ kgdb_br_reg(regnum, ptregs, info, ®, 1) ||
+ kgdb_fr_reg(regnum, inbuffer, ptregs, info, ®, &freg, 1) ||
+ kgdb_ar_reg(regnum, ptregs, info, ®, 1)) ;
+ else if (regnum == IA64_IP_REGNUM)
+ ptregs->cr_iip = reg;
+ else if (regnum == IA64_CFM_REGNUM)
+ ptregs->cr_ifs = reg;
+ else if (regnum == IA64_PSR_REGNUM)
+ ptregs->cr_ipsr = reg;
+ else if (regnum == IA64_PR_REGNUM)
+ ptregs->pr = reg;
+ else
+ strcpy(outbuffer, "E01");
+ return;
+}
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+
+}
+
+#define MAX_HW_BREAKPOINT (20)
+long hw_break_total_dbr, hw_break_total_ibr;
+#define HW_BREAKPOINT (hw_break_total_dbr + hw_break_total_ibr)
+#define WATCH_INSTRUCTION 0x0
+#define WATCH_WRITE 0x1
+#define WATCH_READ 0x2
+#define WATCH_ACCESS 0x3
+
+#define HWCAP_DBR ((1 << WATCH_WRITE) | (1 << WATCH_READ))
+#define HWCAP_IBR (1 << WATCH_INSTRUCTION)
+struct hw_breakpoint {
+ unsigned enabled;
+ unsigned long capable;
+ unsigned long type;
+ unsigned long mask;
+ unsigned long addr;
+} *breakinfo;
+
+static struct hw_breakpoint hwbreaks[MAX_HW_BREAKPOINT];
+
+enum instruction_type { A, I, M, F, B, L, X, u };
+
+static enum instruction_type bundle_encoding[32][3] = {
+ {M, I, I}, /* 00 */
+ {M, I, I}, /* 01 */
+ {M, I, I}, /* 02 */
+ {M, I, I}, /* 03 */
+ {M, L, X}, /* 04 */
+ {M, L, X}, /* 05 */
+ {u, u, u}, /* 06 */
+ {u, u, u}, /* 07 */
+ {M, M, I}, /* 08 */
+ {M, M, I}, /* 09 */
+ {M, M, I}, /* 0A */
+ {M, M, I}, /* 0B */
+ {M, F, I}, /* 0C */
+ {M, F, I}, /* 0D */
+ {M, M, F}, /* 0E */
+ {M, M, F}, /* 0F */
+ {M, I, B}, /* 10 */
+ {M, I, B}, /* 11 */
+ {M, B, B}, /* 12 */
+ {M, B, B}, /* 13 */
+ {u, u, u}, /* 14 */
+ {u, u, u}, /* 15 */
+ {B, B, B}, /* 16 */
+ {B, B, B}, /* 17 */
+ {M, M, B}, /* 18 */
+ {M, M, B}, /* 19 */
+ {u, u, u}, /* 1A */
+ {u, u, u}, /* 1B */
+ {M, F, B}, /* 1C */
+ {M, F, B}, /* 1D */
+ {u, u, u}, /* 1E */
+ {u, u, u}, /* 1F */
+};
+
+int kgdb_validate_break_address(unsigned long addr)
+{
+ int error;
+ char tmp_variable[BREAK_INSTR_SIZE];
+ error = kgdb_get_mem((char *)(addr & BREAK_INSTR_ALIGN), tmp_variable,
+ BREAK_INSTR_SIZE);
+ return error;
+}
+
+int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
+{
+ extern unsigned long _start[];
+ unsigned long slot = addr & BREAK_INSTR_ALIGN, bundle_addr;
+ unsigned long template;
+ struct bundle {
+ struct {
+ unsigned long long template:5;
+ unsigned long long slot0:41;
+ unsigned long long slot1_p0:64 - 46;
+ } quad0;
+ struct {
+ unsigned long long slot1_p1:41 - (64 - 46);
+ unsigned long long slot2:41;
+ } quad1;
+ } bundle;
+ int ret;
+
+ bundle_addr = addr & ~0xFULL;
+
+ if (bundle_addr == (unsigned long)_start)
+ return 0;
+
+ ret = kgdb_get_mem((char *)bundle_addr, (char *)&bundle,
+ BREAK_INSTR_SIZE);
+ if (ret < 0)
+ return ret;
+
+ if (slot > 2)
+ slot = 0;
+
+ memcpy(saved_instr, &bundle, BREAK_INSTR_SIZE);
+ template = bundle.quad0.template;
+
+ if (slot == 1 && bundle_encoding[template][1] == L)
+ slot = 2;
+
+ switch (slot) {
+ case 0:
+ bundle.quad0.slot0 = BREAKNUM;
+ break;
+ case 1:
+ bundle.quad0.slot1_p0 = BREAKNUM;
+ bundle.quad1.slot1_p1 = (BREAKNUM >> (64 - 46));
+ break;
+ case 2:
+ bundle.quad1.slot2 = BREAKNUM;
+ break;
+ }
+
+ return kgdb_set_mem((char *)bundle_addr, (char *)&bundle,
+ BREAK_INSTR_SIZE);
+}
+
+int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
+{
+ extern unsigned long _start[];
+
+ addr = addr & BREAK_INSTR_ALIGN;
+ if (addr == (unsigned long)_start)
+ return 0;
+ return kgdb_set_mem((char *)addr, (char *)bundle, BREAK_INSTR_SIZE);
+}
+
+static int hw_breakpoint_init;
+
+void do_init_hw_break(void)
+{
+ s64 status;
+ int i;
+
+ hw_breakpoint_init = 1;
+
+#ifdef CONFIG_IA64_HP_SIM
+ hw_break_total_ibr = 8;
+ hw_break_total_dbr = 8;
+ status = 0;
+#else
+ status = ia64_pal_debug_info(&hw_break_total_ibr, &hw_break_total_dbr);
+#endif
+
+ if (status) {
+ printk(KERN_INFO "do_init_hw_break: pal call failed %d\n",
+ (int)status);
+ return;
+ }
+
+ if (HW_BREAKPOINT > MAX_HW_BREAKPOINT) {
+ printk(KERN_INFO "do_init_hw_break: %d exceeds max %d\n",
+ (int)HW_BREAKPOINT, (int)MAX_HW_BREAKPOINT);
+
+ while ((HW_BREAKPOINT > MAX_HW_BREAKPOINT)
+ && hw_break_total_ibr != 1)
+ hw_break_total_ibr--;
+ while (HW_BREAKPOINT > MAX_HW_BREAKPOINT)
+ hw_break_total_dbr--;
+ }
+
+ breakinfo = hwbreaks;
+
+ memset(breakinfo, 0, HW_BREAKPOINT * sizeof(struct hw_breakpoint));
+
+ for (i = 0; i < hw_break_total_dbr; i++)
+ breakinfo[i].capable = HWCAP_DBR;
+
+ for (; i < HW_BREAKPOINT; i++)
+ breakinfo[i].capable = HWCAP_IBR;
+
+ return;
+}
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+
+ if (!breakinfo)
+ return;
+
+ for (breakno = 0; breakno < HW_BREAKPOINT; breakno++) {
+ if (breakinfo[breakno].enabled) {
+ if (breakinfo[breakno].capable & HWCAP_IBR) {
+ int ibreakno = breakno - hw_break_total_dbr;
+ ia64_set_ibr(ibreakno << 1,
+ breakinfo[breakno].addr);
+ ia64_set_ibr((ibreakno << 1) + 1,
+ (~breakinfo[breakno].mask &
+ ((1UL << 56UL) - 1)) |
+ (1UL << 56UL) | (1UL << 63UL));
+ } else {
+ ia64_set_dbr(breakno << 1,
+ breakinfo[breakno].addr);
+ ia64_set_dbr((breakno << 1) + 1,
+ (~breakinfo[breakno].
+ mask & ((1UL << 56UL) - 1)) |
+ (1UL << 56UL) |
+ (breakinfo[breakno].type << 62UL));
+ }
+ } else {
+ if (breakinfo[breakno].capable & HWCAP_IBR)
+ ia64_set_ibr(((breakno -
+ hw_break_total_dbr) << 1) + 1,
+ 0);
+ else
+ ia64_set_dbr((breakno << 1) + 1, 0);
+ }
+ }
+
+ return;
+}
+
+int hardware_breakpoint(unsigned long addr, int length, int type, int action)
+{
+ int breakno, found, watch;
+ unsigned long mask;
+ extern unsigned long _start[];
+
+ if (!hw_breakpoint_init)
+ do_init_hw_break();
+
+ if (!breakinfo)
+ return 0;
+ else if (addr == (unsigned long)_start)
+ return 1;
+
+ if (type == WATCH_ACCESS)
+ mask = HWCAP_DBR;
+ else
+ mask = 1UL << type;
+
+ for (watch = 0, found = 0, breakno = 0; breakno < HW_BREAKPOINT;
+ breakno++) {
+ if (action) {
+ if (breakinfo[breakno].enabled
+ || !(breakinfo[breakno].capable & mask))
+ continue;
+ breakinfo[breakno].enabled = 1;
+ breakinfo[breakno].type = type;
+ breakinfo[breakno].mask = length - 1;
+ breakinfo[breakno].addr = addr;
+ watch = breakno;
+ } else if (breakinfo[breakno].enabled &&
+ ((length < 0 && breakinfo[breakno].addr == addr) ||
+ ((breakinfo[breakno].capable & mask) &&
+ (breakinfo[breakno].mask == (length - 1)) &&
+ (breakinfo[breakno].addr == addr)))) {
+ breakinfo[breakno].enabled = 0;
+ breakinfo[breakno].type = 0UL;
+ } else
+ continue;
+ found++;
+ if (type != WATCH_ACCESS)
+ break;
+ else if (found == 2)
+ break;
+ else
+ mask = HWCAP_IBR;
+ }
+
+ if (type == WATCH_ACCESS && found == 1) {
+ breakinfo[watch].enabled = 0;
+ found = 0;
+ }
+
+ mb();
+ return found;
+}
+
+int kgdb_arch_set_hw_breakpoint(unsigned long addr, int len,
+ enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 1);
+}
+
+int kgdb_arch_remove_hw_breakpoint(unsigned long addr, int len,
+ enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 0);
+}
+
+int kgdb_remove_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 0);
+
+}
+
+void kgdb_remove_all_hw_break(void)
+{
+ int i;
+
+ for (i = 0; i < HW_BREAKPOINT; i++)
+ memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+}
+
+int kgdb_set_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 1);
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status;
+
+ hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L,
+ hw_breakpoint_status ^ IA64_PSR_DB);
+}
+
+volatile static struct smp_unw {
+ struct unw_frame_info *unw;
+ struct task_struct *task;
+} smp_unw[NR_CPUS];
+
+static int inline kgdb_get_blocked_state(struct task_struct *p,
+ struct unw_frame_info *unw)
+{
+ unsigned long ip;
+ int count = 0;
+
+ unw_init_from_blocked_task(unw, p);
+ ip = 0UL;
+ do {
+ if (unw_unwind(unw) < 0)
+ return -1;
+ unw_get_ip(unw, &ip);
+ if (!in_sched_functions(ip))
+ break;
+ } while (count++ < 16);
+
+ if (!ip)
+ return -1;
+ else
+ return 0;
+}
+
+static void inline kgdb_wait(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L,
+ hw_breakpoint_status ^ IA64_PSR_DB);
+ kgdb_nmihook(smp_processor_id(), regs);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status);
+
+ return;
+}
+
+static void inline normalize(struct unw_frame_info *running,
+ struct pt_regs *regs)
+{
+ unsigned long sp;
+
+ do {
+ unw_get_sp(running, &sp);
+ if ((sp + 0x10) >= (unsigned long)regs)
+ break;
+ } while (unw_unwind(running) >= 0);
+
+ return;
+}
+
+static void kgdb_init_running(struct unw_frame_info *unw, void *data)
+{
+ struct pt_regs *regs;
+
+ regs = data;
+ normalize(unw, regs);
+ smp_unw[smp_processor_id()].unw = unw;
+ kgdb_wait(regs);
+}
+
+void kgdb_wait_ipi(struct pt_regs *regs)
+{
+ struct unw_frame_info unw;
+
+ smp_unw[smp_processor_id()].task = current;
+
+ if (user_mode(regs)) {
+ smp_unw[smp_processor_id()].unw = (struct unw_frame_info *)1;
+ kgdb_wait(regs);
+ } else {
+ if (current->state == TASK_RUNNING)
+ unw_init_running(kgdb_init_running, regs);
+ else {
+ if (kgdb_get_blocked_state(current, &unw))
+ smp_unw[smp_processor_id()].unw =
+ (struct unw_frame_info *)1;
+ else
+ smp_unw[smp_processor_id()].unw = &unw;
+ kgdb_wait(regs);
+ }
+ }
+
+ smp_unw[smp_processor_id()].unw = NULL;
+ return;
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ if (num_online_cpus() > 1)
+ smp_send_nmi_allbutself();
+}
+
+static volatile int kgdb_hwbreak_sstep[NR_CPUS];
+
+static int kgdb_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *args = ptr;
+ struct pt_regs *regs = args->regs;
+ unsigned long err = args->err;
+
+ switch (cmd) {
+ default:
+ return NOTIFY_DONE;
+ case DIE_PAGE_FAULT_NO_CONTEXT:
+ if (atomic_read(&debugger_active) && kgdb_may_fault) {
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ return NOTIFY_STOP;
+ }
+ break;
+ case DIE_BREAK:
+ if (user_mode(regs) || err == 0x80001)
+ return NOTIFY_DONE;
+ break;
+ case DIE_FAULT:
+ if (user_mode(regs))
+ return NOTIFY_DONE;
+ else if (err == 36 && kgdb_hwbreak_sstep[smp_processor_id()]) {
+ kgdb_hwbreak_sstep[smp_processor_id()] = 0;
+ regs->cr_ipsr &= ~IA64_PSR_SS;
+ return NOTIFY_STOP;
+ }
+ case DIE_MCA_MONARCH_PROCESS:
+ case DIE_INIT_MONARCH_PROCESS:
+ break;
+ }
+
+ kgdb_handle_exception(args->trapnr, args->signr, args->err, regs);
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_notify,
+};
+
+int kgdb_arch_init(void)
+{
+ atomic_notifier_chain_register(&ia64die_chain, &kgdb_notifier);
+ return 0;
+}
+
+static void do_kgdb_handle_exception(struct unw_frame_info *, void *data);
+
+struct kgdb_state {
+ int e_vector;
+ int signo;
+ unsigned long err_code;
+ struct pt_regs *regs;
+ struct unw_frame_info *unw;
+ char *inbuf;
+ char *outbuf;
+ int unwind;
+ int ret;
+};
+
+static void inline kgdb_pc(struct pt_regs *regs, unsigned long pc)
+{
+ regs->cr_iip = pc & ~0xf;
+ ia64_psr(regs)->ri = pc & 0x3;
+ return;
+}
+
+int kgdb_arch_handle_exception(int e_vector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ struct kgdb_state info;
+
+ info.e_vector = e_vector;
+ info.signo = signo;
+ info.err_code = err_code;
+ info.unw = (void *)0;
+ info.inbuf = remcom_in_buffer;
+ info.outbuf = remcom_out_buffer;
+ info.unwind = 0;
+ info.ret = -1;
+
+ if (remcom_in_buffer[0] == 'c' || remcom_in_buffer[0] == 's') {
+ info.regs = linux_regs;
+ do_kgdb_handle_exception(NULL, &info);
+ } else if (kgdb_usethread == current) {
+ info.regs = linux_regs;
+ info.unwind = 1;
+ unw_init_running(do_kgdb_handle_exception, &info);
+ } else if (kgdb_usethread->state != TASK_RUNNING) {
+ struct unw_frame_info unw_info;
+
+ if (kgdb_get_blocked_state(kgdb_usethread, &unw_info)) {
+ info.ret = 1;
+ goto bad;
+ }
+ info.regs = NULL;
+ do_kgdb_handle_exception(&unw_info, &info);
+ } else {
+ int i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (smp_unw[i].task == kgdb_usethread && smp_unw[i].unw
+ && smp_unw[i].unw != (struct unw_frame_info *)1) {
+ info.regs = NULL;
+ do_kgdb_handle_exception(smp_unw[i].unw, &info);
+ break;
+ } else {
+ info.ret = 1;
+ goto bad;
+ }
+ }
+
+ bad:
+ if (info.ret != -1 && remcom_in_buffer[0] == 'p') {
+ unsigned long bad = 0xbad4badbadbadbadUL;
+
+ printk("kgdb_arch_handle_exception: p packet bad (%s)\n",
+ remcom_in_buffer);
+ kgdb_mem2hex((char *)&bad, remcom_out_buffer, sizeof(bad));
+ remcom_out_buffer[sizeof(bad) * 2] = 0;
+ info.ret = -1;
+ }
+ return info.ret;
+}
+
+/*
+ * This is done because I evidently made an incorrect 'p' encoding
+ * when my patch for gdb was committed. It was later corrected. This
+ * check supports both my wrong encoding of the register number and
+ * the correct encoding. Eventually this should be eliminated and
+ * kgdb_hex2long should be demarshalling the regnum.
+ */
+static inline int check_packet(unsigned int regnum, char *packet)
+{
+ static int check_done, swap;
+ unsigned long reglong;
+
+ if (likely(check_done)) {
+ if (swap) {
+ kgdb_hex2long(&packet, ®long);
+ regnum = (int) reglong;
+ }
+
+ } else {
+ if (regnum > NUM_REGS) {
+ kgdb_hex2long(&packet, ®long);
+ regnum = (int) reglong;
+ swap = 1;
+ }
+ check_done = 1;
+ }
+ return regnum;
+}
+
+static void do_kgdb_handle_exception(struct unw_frame_info *unw_info,
+ void *data)
+{
+ long addr;
+ char *ptr;
+ unsigned long newPC;
+ int e_vector, signo;
+ unsigned long err_code;
+ struct pt_regs *linux_regs;
+ struct kgdb_state *info;
+ char *remcom_in_buffer, *remcom_out_buffer;
+
+ info = data;
+ info->unw = unw_info;
+ e_vector = info->e_vector;
+ signo = info->signo;
+ err_code = info->err_code;
+ remcom_in_buffer = info->inbuf;
+ remcom_out_buffer = info->outbuf;
+ linux_regs = info->regs;
+
+ if (info->unwind)
+ normalize(unw_info, linux_regs);
+
+ switch (remcom_in_buffer[0]) {
+ case 'p':
+ {
+ unsigned int regnum;
+
+ kgdb_hex2mem(&remcom_in_buffer[1], (char *)®num,
+ sizeof(regnum));
+ regnum = check_packet(regnum, &remcom_in_buffer[1]);
+ if (regnum >= NUM_REGS) {
+ remcom_out_buffer[0] = 'E';
+ remcom_out_buffer[1] = 0;
+ } else
+ kgdb_get_reg(remcom_out_buffer, regnum,
+ unw_info, linux_regs);
+ break;
+ }
+ case 'P':
+ {
+ unsigned int regno;
+ long v;
+ char *ptr;
+
+ ptr = &remcom_in_buffer[1];
+ if ((!kgdb_usethread || kgdb_usethread == current) &&
+ kgdb_hex2long(&ptr, &v) &&
+ *ptr++ == '=' && (v >= 0)) {
+ regno = (unsigned int)v;
+ regno = (regno >= NUM_REGS ? 0 : regno);
+ kgdb_put_reg(ptr, remcom_out_buffer, regno,
+ unw_info, linux_regs);
+ } else
+ strcpy(remcom_out_buffer, "E01");
+ break;
+ }
+ case 'c':
+ case 's':
+ if (e_vector == TRAP_BRKPT && err_code == KGDBBREAKNUM) {
+ if (ia64_psr(linux_regs)->ri < 2)
+ kgdb_pc(linux_regs, linux_regs->cr_iip +
+ ia64_psr(linux_regs)->ri + 1);
+ else
+ kgdb_pc(linux_regs, linux_regs->cr_iip + 16);
+ }
+
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr)) {
+ linux_regs->cr_iip = addr;
+ }
+ newPC = linux_regs->cr_iip;
+
+ /* clear the trace bit */
+ linux_regs->cr_ipsr &= ~IA64_PSR_SS;
+
+ atomic_set(&cpu_doing_single_step, -1);
+
+ /* set the trace bit if we're stepping or took a hardware break */
+ if (remcom_in_buffer[0] == 's' || e_vector == TRAP_HWBKPT) {
+ linux_regs->cr_ipsr |= IA64_PSR_SS;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,
+ smp_processor_id());
+ }
+
+ kgdb_correct_hw_break();
+
+ /* if not hardware breakpoint, then reenable them */
+ if (e_vector != TRAP_HWBKPT)
+ linux_regs->cr_ipsr |= IA64_PSR_DB;
+ else {
+ kgdb_hwbreak_sstep[smp_processor_id()] = 1;
+ linux_regs->cr_ipsr &= ~IA64_PSR_DB;
+ }
+
+ info->ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .set_hw_breakpoint = kgdb_arch_set_hw_breakpoint,
+ .remove_hw_breakpoint = kgdb_arch_remove_hw_breakpoint,
+ .gdb_bpt_instr = {0xcc},
+ .flags = KGDB_HW_BREAKPOINT,
+};
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/process.c linux-2.6.18.kgdb/arch/ia64/kernel/process.c
--- linux-2.6.18/arch/ia64/kernel/process.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/kernel/process.c 2008-06-10 16:20:23.000000000 +0400
@@ -458,6 +458,9 @@ copy_thread (int nr, unsigned long clone
*/
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
+#ifdef CONFIG_KGDB
+ child_ptregs->cr_ipsr |= IA64_PSR_DB;
+#endif
/*
* NOTE: The calling convention considers all floating point
@@ -686,6 +689,9 @@ kernel_thread (int (*fn)(void *), void *
regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
/* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
+#ifdef CONFIG_KGDB
+ regs.pt.cr_ipsr |= IA64_PSR_DB;
+#endif
regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/smp.c linux-2.6.18.kgdb/arch/ia64/kernel/smp.c
--- linux-2.6.18/arch/ia64/kernel/smp.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/kernel/smp.c 2008-06-10 16:19:32.000000000 +0400
@@ -47,6 +47,7 @@
#include
#include
#include
+#include
/*
* Structure and data for smp_call_function(). This is designed to minimise static memory
@@ -66,6 +67,9 @@ static volatile struct call_data_struct
#define IPI_CALL_FUNC 0
#define IPI_CPU_STOP 1
+#ifdef CONFIG_KGDB
+#define IPI_KGDB_INTERRUPT 2
+#endif
/* This needs to be cacheline aligned because it is written to by *other* CPUs. */
static DEFINE_PER_CPU(u64, ipi_operation) ____cacheline_aligned;
@@ -155,6 +159,11 @@ handle_IPI (int irq, void *dev_id, struc
case IPI_CPU_STOP:
stop_this_cpu();
break;
+#ifdef CONFIG_KGDB
+ case IPI_KGDB_INTERRUPT:
+ kgdb_wait_ipi(regs);
+ break;
+#endif
default:
printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which);
@@ -305,6 +314,14 @@ smp_call_function_single (int cpuid, voi
}
EXPORT_SYMBOL(smp_call_function_single);
+#ifdef CONFIG_KGDB
+void
+smp_send_nmi_allbutself(void)
+{
+ send_IPI_allbutself(IPI_KGDB_INTERRUPT);
+}
+#endif
+
/*
* this function sends a 'generic call function' IPI to all other CPUs
* in the system.
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/traps.c linux-2.6.18.kgdb/arch/ia64/kernel/traps.c
--- linux-2.6.18/arch/ia64/kernel/traps.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/kernel/traps.c 2008-06-10 16:19:32.000000000 +0400
@@ -200,8 +200,12 @@ __kprobes ia64_bad_break (unsigned long
break;
default:
- if (break_num < 0x40000 || break_num > 0x100000)
+ if (break_num < 0x40000 || break_num > 0x100000) {
+ if (notify_die(DIE_BREAK, "bad break", regs,
+ break_num, TRAP_BRKPT, SIGTRAP) == NOTIFY_STOP)
+ return;
die_if_kernel("Bad break", regs, break_num);
+ }
if (break_num < 0x80000) {
sig = SIGILL; code = __ILL_BREAK;
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/kernel/unwind.c linux-2.6.18.kgdb/arch/ia64/kernel/unwind.c
--- linux-2.6.18/arch/ia64/kernel/unwind.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/kernel/unwind.c 2008-06-10 16:20:23.000000000 +0400
@@ -72,10 +72,68 @@
# define STAT(x...)
#endif
+#ifdef CONFIG_KGDB
+#define KGDB_EARLY_SIZE 100
+static struct unw_reg_state __initdata kgdb_reg_state[KGDB_EARLY_SIZE];
+static struct unw_labeled_state __initdata kgdb_labeled_state[KGDB_EARLY_SIZE];
+void __initdata *kgdb_reg_state_free, __initdata *kgdb_labeled_state_free;
+
+static void __init
+kgdb_malloc_init(void)
+{
+ int i;
+
+ kgdb_reg_state_free = kgdb_reg_state;
+ for (i = 1; i < KGDB_EARLY_SIZE; i++) {
+ *((unsigned long *) &kgdb_reg_state[i]) = (unsigned long) kgdb_reg_state_free;
+ kgdb_reg_state_free = &kgdb_reg_state[i];
+ }
+
+ kgdb_labeled_state_free = kgdb_labeled_state;
+ for (i = 1; i < KGDB_EARLY_SIZE; i++) {
+ *((unsigned long *) &kgdb_labeled_state[i]) =
+ (unsigned long) kgdb_labeled_state_free;
+ kgdb_labeled_state_free = &kgdb_labeled_state[i];
+ }
+
+}
+
+static void * __init
+kgdb_malloc(void **mem)
+{
+ void *p;
+
+ p = *mem;
+ *mem = *((void **) p);
+ return p;
+}
+
+static void __init
+kgdb_free(void **mem, void *p)
+{
+ *((void **)p) = *mem;
+ *mem = p;
+}
+
+#define alloc_reg_state() (!malloc_sizes[0].cs_cachep ? \
+ kgdb_malloc(&kgdb_reg_state_free) : \
+ kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC))
+#define free_reg_state(usr) (!malloc_sizes[0].cs_cachep ? \
+ kgdb_free(&kgdb_reg_state_free, usr) : \
+ kfree(usr))
+#define alloc_labeled_state() (!malloc_sizes[0].cs_cachep ? \
+ kgdb_malloc(&kgdb_labeled_state_free) : \
+ kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC))
+#define free_labeled_state(usr) (!malloc_sizes[0].cs_cachep ? \
+ kgdb_free(&kgdb_labeled_state_free, usr) : \
+ kfree(usr))
+
+#else
#define alloc_reg_state() kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC)
#define free_reg_state(usr) kfree(usr)
#define alloc_labeled_state() kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC)
#define free_labeled_state(usr) kfree(usr)
+#endif
typedef unsigned long unw_word;
typedef unsigned char unw_hash_index_t;
@@ -238,6 +296,24 @@ static struct {
#endif
};
+#ifdef CONFIG_KGDB
+/*
+ * This makes it safe to call breakpoint() very early
+ * in setup_arch providing:
+ * 1) breakpoint isn't called between lines in cpu_init
+ * where init_mm.mm_count is incremented and ia64_mmu_init
+ * is called. Otherwise the test below is invalid.
+ * 2) the memory examined doesn't result in tlbmiss.
+ */
+static unsigned long inline kgdb_unimpl_va_mask(void)
+{
+ if (atomic_read(&init_mm.mm_count) > 1)
+ return local_cpu_data->unimpl_va_mask;
+ else
+ return 0UL;
+}
+#endif
+
static inline int
read_only (void *addr)
{
@@ -1786,7 +1862,11 @@ run_script (struct unw_script *script, s
case UNW_INSN_LOAD:
#ifdef UNW_DEBUG
+#ifdef CONFIG_KGDB
+ if ((s[val] & (kgdb_unimpl_va_mask() | 0x7)) != 0
+#else
if ((s[val] & (local_cpu_data->unimpl_va_mask | 0x7)) != 0
+#endif
|| s[val] < TASK_SIZE)
{
UNW_DPRINT(0, "unwind.%s: rejecting bad psp=0x%lx\n",
@@ -1821,7 +1901,11 @@ find_save_locs (struct unw_frame_info *i
struct unw_script *scr;
unsigned long flags = 0;
+#ifdef CONFIG_KGDB
+ if ((info->ip & (kgdb_unimpl_va_mask() | 0xf)) || info->ip < TASK_SIZE) {
+#else
if ((info->ip & (local_cpu_data->unimpl_va_mask | 0xf)) || info->ip < TASK_SIZE) {
+#endif
/* don't let obviously bad addresses pollute the cache */
/* FIXME: should really be level 0 but it occurs too often. KAO */
UNW_DPRINT(1, "unwind.%s: rejecting bad ip=0x%lx\n", __FUNCTION__, info->ip);
@@ -2249,6 +2333,9 @@ unw_init (void)
init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) __gp,
__start_unwind, __end_unwind);
+#ifdef CONFIG_KGDB
+ kgdb_malloc_init();
+#endif
}
/*
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/mm/extable.c linux-2.6.18.kgdb/arch/ia64/mm/extable.c
--- linux-2.6.18/arch/ia64/mm/extable.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/mm/extable.c 2008-06-10 16:19:32.000000000 +0400
@@ -6,6 +6,7 @@
*/
#include
+#include
#include
#include
@@ -73,6 +74,11 @@ search_extable (const struct exception_t
else
last = mid - 1;
}
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+#endif
return NULL;
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/ia64/mm/fault.c linux-2.6.18.kgdb/arch/ia64/mm/fault.c
--- linux-2.6.18/arch/ia64/mm/fault.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/ia64/mm/fault.c 2008-06-10 16:19:32.000000000 +0400
@@ -266,6 +266,10 @@ ia64_do_page_fault (unsigned long addres
*/
bust_spinlocks(1);
+ if (notify_die(DIE_PAGE_FAULT_NO_CONTEXT, "no context", regs,
+ isr, 14, SIGSEGV) == NOTIFY_STOP)
+ return;
+
if (address < PAGE_SIZE)
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address);
else
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/Kconfig.debug linux-2.6.18.kgdb/arch/mips/Kconfig.debug
--- linux-2.6.18/arch/mips/Kconfig.debug 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/Kconfig.debug 2008-06-10 16:19:28.000000000 +0400
@@ -37,25 +37,6 @@ config DEBUG_STACK_USAGE
This option will slow down process creation somewhat.
-config KGDB
- bool "Remote GDB kernel debugging"
- depends on DEBUG_KERNEL
- select DEBUG_INFO
- help
- If you say Y here, it will be possible to remotely debug the MIPS
- kernel using gdb. This enlarges your kernel image disk size by
- several megabytes and requires a machine with more than 16 MB,
- better 32 MB RAM to avoid excessive linking time. This is only
- useful for kernel hackers. If unsure, say N.
-
-config GDB_CONSOLE
- bool "Console output to GDB"
- depends on KGDB
- help
- If you are using GDB for remote debugging over a serial port and
- would like kernel messages to be formatted into GDB $O packets so
- that GDB prints them as program output, say 'Y'.
-
config SB1XXX_CORELIS
bool "Corelis Debugger"
depends on SIBYTE_SB1xxx_SOC
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/Makefile linux-2.6.18.kgdb/arch/mips/kernel/Makefile
--- linux-2.6.18/arch/mips/kernel/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/kernel/Makefile 2008-06-10 16:19:28.000000000 +0400
@@ -59,7 +59,8 @@ obj-$(CONFIG_MIPS32_COMPAT) += linux32.o
obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o
obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o ptrace32.o
-obj-$(CONFIG_KGDB) += gdb-low.o gdb-stub.o
+obj-$(CONFIG_KGDB) += kgdb_handler.o kgdb.o kgdb-jmp.o \
+ kgdb-setjmp.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_64BIT) += cpu-bugs64.o
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/gdb-low.S linux-2.6.18.kgdb/arch/mips/kernel/gdb-low.S
--- linux-2.6.18/arch/mips/kernel/gdb-low.S 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/kernel/gdb-low.S 1970-01-01 03:00:00.000000000 +0300
@@ -1,394 +0,0 @@
-/*
- * gdb-low.S contains the low-level trap handler for the GDB stub.
- *
- * Copyright (C) 1995 Andreas Busse
- */
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef CONFIG_32BIT
-#define DMFC0 mfc0
-#define DMTC0 mtc0
-#define LDC1 lwc1
-#define SDC1 lwc1
-#endif
-#ifdef CONFIG_64BIT
-#define DMFC0 dmfc0
-#define DMTC0 dmtc0
-#define LDC1 ldc1
-#define SDC1 ldc1
-#endif
-
-/*
- * [jsun] We reserves about 2x GDB_FR_SIZE in stack. The lower (addressed)
- * part is used to store registers and passed to exception handler.
- * The upper part is reserved for "call func" feature where gdb client
- * saves some of the regs, setups call frame and passes args.
- *
- * A trace shows about 200 bytes are used to store about half of all regs.
- * The rest should be big enough for frame setup and passing args.
- */
-
-/*
- * The low level trap handler
- */
- .align 5
- NESTED(trap_low, GDB_FR_SIZE, sp)
- .set noat
- .set noreorder
-
- mfc0 k0, CP0_STATUS
- sll k0, 3 /* extract cu0 bit */
- bltz k0, 1f
- move k1, sp
-
- /*
- * Called from user mode, go somewhere else.
- */
- mfc0 k0, CP0_CAUSE
- andi k0, k0, 0x7c
-#ifdef CONFIG_64BIT
- dsll k0, k0, 1
-#endif
- PTR_L k1, saved_vectors(k0)
- jr k1
- nop
-1:
- move k0, sp
- PTR_SUBU sp, k1, GDB_FR_SIZE*2 # see comment above
- LONG_S k0, GDB_FR_REG29(sp)
- LONG_S $2, GDB_FR_REG2(sp)
-
-/*
- * First save the CP0 and special registers
- */
-
- mfc0 v0, CP0_STATUS
- LONG_S v0, GDB_FR_STATUS(sp)
- mfc0 v0, CP0_CAUSE
- LONG_S v0, GDB_FR_CAUSE(sp)
- DMFC0 v0, CP0_EPC
- LONG_S v0, GDB_FR_EPC(sp)
- DMFC0 v0, CP0_BADVADDR
- LONG_S v0, GDB_FR_BADVADDR(sp)
- mfhi v0
- LONG_S v0, GDB_FR_HI(sp)
- mflo v0
- LONG_S v0, GDB_FR_LO(sp)
-
-/*
- * Now the integer registers
- */
-
- LONG_S zero, GDB_FR_REG0(sp) /* I know... */
- LONG_S $1, GDB_FR_REG1(sp)
- /* v0 already saved */
- LONG_S $3, GDB_FR_REG3(sp)
- LONG_S $4, GDB_FR_REG4(sp)
- LONG_S $5, GDB_FR_REG5(sp)
- LONG_S $6, GDB_FR_REG6(sp)
- LONG_S $7, GDB_FR_REG7(sp)
- LONG_S $8, GDB_FR_REG8(sp)
- LONG_S $9, GDB_FR_REG9(sp)
- LONG_S $10, GDB_FR_REG10(sp)
- LONG_S $11, GDB_FR_REG11(sp)
- LONG_S $12, GDB_FR_REG12(sp)
- LONG_S $13, GDB_FR_REG13(sp)
- LONG_S $14, GDB_FR_REG14(sp)
- LONG_S $15, GDB_FR_REG15(sp)
- LONG_S $16, GDB_FR_REG16(sp)
- LONG_S $17, GDB_FR_REG17(sp)
- LONG_S $18, GDB_FR_REG18(sp)
- LONG_S $19, GDB_FR_REG19(sp)
- LONG_S $20, GDB_FR_REG20(sp)
- LONG_S $21, GDB_FR_REG21(sp)
- LONG_S $22, GDB_FR_REG22(sp)
- LONG_S $23, GDB_FR_REG23(sp)
- LONG_S $24, GDB_FR_REG24(sp)
- LONG_S $25, GDB_FR_REG25(sp)
- LONG_S $26, GDB_FR_REG26(sp)
- LONG_S $27, GDB_FR_REG27(sp)
- LONG_S $28, GDB_FR_REG28(sp)
- /* sp already saved */
- LONG_S $30, GDB_FR_REG30(sp)
- LONG_S $31, GDB_FR_REG31(sp)
-
- CLI /* disable interrupts */
- TRACE_IRQS_OFF
-
-/*
- * Followed by the floating point registers
- */
- mfc0 v0, CP0_STATUS /* FPU enabled? */
- srl v0, v0, 16
- andi v0, v0, (ST0_CU1 >> 16)
-
- beqz v0,2f /* disabled, skip */
- nop
-
- SDC1 $0, GDB_FR_FPR0(sp)
- SDC1 $1, GDB_FR_FPR1(sp)
- SDC1 $2, GDB_FR_FPR2(sp)
- SDC1 $3, GDB_FR_FPR3(sp)
- SDC1 $4, GDB_FR_FPR4(sp)
- SDC1 $5, GDB_FR_FPR5(sp)
- SDC1 $6, GDB_FR_FPR6(sp)
- SDC1 $7, GDB_FR_FPR7(sp)
- SDC1 $8, GDB_FR_FPR8(sp)
- SDC1 $9, GDB_FR_FPR9(sp)
- SDC1 $10, GDB_FR_FPR10(sp)
- SDC1 $11, GDB_FR_FPR11(sp)
- SDC1 $12, GDB_FR_FPR12(sp)
- SDC1 $13, GDB_FR_FPR13(sp)
- SDC1 $14, GDB_FR_FPR14(sp)
- SDC1 $15, GDB_FR_FPR15(sp)
- SDC1 $16, GDB_FR_FPR16(sp)
- SDC1 $17, GDB_FR_FPR17(sp)
- SDC1 $18, GDB_FR_FPR18(sp)
- SDC1 $19, GDB_FR_FPR19(sp)
- SDC1 $20, GDB_FR_FPR20(sp)
- SDC1 $21, GDB_FR_FPR21(sp)
- SDC1 $22, GDB_FR_FPR22(sp)
- SDC1 $23, GDB_FR_FPR23(sp)
- SDC1 $24, GDB_FR_FPR24(sp)
- SDC1 $25, GDB_FR_FPR25(sp)
- SDC1 $26, GDB_FR_FPR26(sp)
- SDC1 $27, GDB_FR_FPR27(sp)
- SDC1 $28, GDB_FR_FPR28(sp)
- SDC1 $29, GDB_FR_FPR29(sp)
- SDC1 $30, GDB_FR_FPR30(sp)
- SDC1 $31, GDB_FR_FPR31(sp)
-
-/*
- * FPU control registers
- */
-
- cfc1 v0, CP1_STATUS
- LONG_S v0, GDB_FR_FSR(sp)
- cfc1 v0, CP1_REVISION
- LONG_S v0, GDB_FR_FIR(sp)
-
-/*
- * Current stack frame ptr
- */
-
-2:
- LONG_S sp, GDB_FR_FRP(sp)
-
-/*
- * CP0 registers (R4000/R4400 unused registers skipped)
- */
-
- mfc0 v0, CP0_INDEX
- LONG_S v0, GDB_FR_CP0_INDEX(sp)
- mfc0 v0, CP0_RANDOM
- LONG_S v0, GDB_FR_CP0_RANDOM(sp)
- DMFC0 v0, CP0_ENTRYLO0
- LONG_S v0, GDB_FR_CP0_ENTRYLO0(sp)
- DMFC0 v0, CP0_ENTRYLO1
- LONG_S v0, GDB_FR_CP0_ENTRYLO1(sp)
- DMFC0 v0, CP0_CONTEXT
- LONG_S v0, GDB_FR_CP0_CONTEXT(sp)
- mfc0 v0, CP0_PAGEMASK
- LONG_S v0, GDB_FR_CP0_PAGEMASK(sp)
- mfc0 v0, CP0_WIRED
- LONG_S v0, GDB_FR_CP0_WIRED(sp)
- DMFC0 v0, CP0_ENTRYHI
- LONG_S v0, GDB_FR_CP0_ENTRYHI(sp)
- mfc0 v0, CP0_PRID
- LONG_S v0, GDB_FR_CP0_PRID(sp)
-
- .set at
-
-/*
- * Continue with the higher level handler
- */
-
- move a0,sp
-
- jal handle_exception
- nop
-
-/*
- * Restore all writable registers, in reverse order
- */
-
- .set noat
-
- LONG_L v0, GDB_FR_CP0_ENTRYHI(sp)
- LONG_L v1, GDB_FR_CP0_WIRED(sp)
- DMTC0 v0, CP0_ENTRYHI
- mtc0 v1, CP0_WIRED
- LONG_L v0, GDB_FR_CP0_PAGEMASK(sp)
- LONG_L v1, GDB_FR_CP0_ENTRYLO1(sp)
- mtc0 v0, CP0_PAGEMASK
- DMTC0 v1, CP0_ENTRYLO1
- LONG_L v0, GDB_FR_CP0_ENTRYLO0(sp)
- LONG_L v1, GDB_FR_CP0_INDEX(sp)
- DMTC0 v0, CP0_ENTRYLO0
- LONG_L v0, GDB_FR_CP0_CONTEXT(sp)
- mtc0 v1, CP0_INDEX
- DMTC0 v0, CP0_CONTEXT
-
-
-/*
- * Next, the floating point registers
- */
- mfc0 v0, CP0_STATUS /* check if the FPU is enabled */
- srl v0, v0, 16
- andi v0, v0, (ST0_CU1 >> 16)
-
- beqz v0, 3f /* disabled, skip */
- nop
-
- LDC1 $31, GDB_FR_FPR31(sp)
- LDC1 $30, GDB_FR_FPR30(sp)
- LDC1 $29, GDB_FR_FPR29(sp)
- LDC1 $28, GDB_FR_FPR28(sp)
- LDC1 $27, GDB_FR_FPR27(sp)
- LDC1 $26, GDB_FR_FPR26(sp)
- LDC1 $25, GDB_FR_FPR25(sp)
- LDC1 $24, GDB_FR_FPR24(sp)
- LDC1 $23, GDB_FR_FPR23(sp)
- LDC1 $22, GDB_FR_FPR22(sp)
- LDC1 $21, GDB_FR_FPR21(sp)
- LDC1 $20, GDB_FR_FPR20(sp)
- LDC1 $19, GDB_FR_FPR19(sp)
- LDC1 $18, GDB_FR_FPR18(sp)
- LDC1 $17, GDB_FR_FPR17(sp)
- LDC1 $16, GDB_FR_FPR16(sp)
- LDC1 $15, GDB_FR_FPR15(sp)
- LDC1 $14, GDB_FR_FPR14(sp)
- LDC1 $13, GDB_FR_FPR13(sp)
- LDC1 $12, GDB_FR_FPR12(sp)
- LDC1 $11, GDB_FR_FPR11(sp)
- LDC1 $10, GDB_FR_FPR10(sp)
- LDC1 $9, GDB_FR_FPR9(sp)
- LDC1 $8, GDB_FR_FPR8(sp)
- LDC1 $7, GDB_FR_FPR7(sp)
- LDC1 $6, GDB_FR_FPR6(sp)
- LDC1 $5, GDB_FR_FPR5(sp)
- LDC1 $4, GDB_FR_FPR4(sp)
- LDC1 $3, GDB_FR_FPR3(sp)
- LDC1 $2, GDB_FR_FPR2(sp)
- LDC1 $1, GDB_FR_FPR1(sp)
- LDC1 $0, GDB_FR_FPR0(sp)
-
-/*
- * Now the CP0 and integer registers
- */
-
-3:
-#ifdef CONFIG_MIPS_MT_SMTC
- /* Read-modify write of Status must be atomic */
- mfc0 t2, CP0_TCSTATUS
- ori t1, t2, TCSTATUS_IXMT
- mtc0 t1, CP0_TCSTATUS
- andi t2, t2, TCSTATUS_IXMT
- _ehb
- DMT 9 # dmt t1
- jal mips_ihb
- nop
-#endif /* CONFIG_MIPS_MT_SMTC */
- mfc0 t0, CP0_STATUS
- ori t0, 0x1f
- xori t0, 0x1f
- mtc0 t0, CP0_STATUS
-#ifdef CONFIG_MIPS_MT_SMTC
- andi t1, t1, VPECONTROL_TE
- beqz t1, 9f
- nop
- EMT # emt
-9:
- mfc0 t1, CP0_TCSTATUS
- xori t1, t1, TCSTATUS_IXMT
- or t1, t1, t2
- mtc0 t1, CP0_TCSTATUS
- _ehb
-#endif /* CONFIG_MIPS_MT_SMTC */
- LONG_L v0, GDB_FR_STATUS(sp)
- LONG_L v1, GDB_FR_EPC(sp)
- mtc0 v0, CP0_STATUS
- DMTC0 v1, CP0_EPC
- LONG_L v0, GDB_FR_HI(sp)
- LONG_L v1, GDB_FR_LO(sp)
- mthi v0
- mtlo v1
- LONG_L $31, GDB_FR_REG31(sp)
- LONG_L $30, GDB_FR_REG30(sp)
- LONG_L $28, GDB_FR_REG28(sp)
- LONG_L $27, GDB_FR_REG27(sp)
- LONG_L $26, GDB_FR_REG26(sp)
- LONG_L $25, GDB_FR_REG25(sp)
- LONG_L $24, GDB_FR_REG24(sp)
- LONG_L $23, GDB_FR_REG23(sp)
- LONG_L $22, GDB_FR_REG22(sp)
- LONG_L $21, GDB_FR_REG21(sp)
- LONG_L $20, GDB_FR_REG20(sp)
- LONG_L $19, GDB_FR_REG19(sp)
- LONG_L $18, GDB_FR_REG18(sp)
- LONG_L $17, GDB_FR_REG17(sp)
- LONG_L $16, GDB_FR_REG16(sp)
- LONG_L $15, GDB_FR_REG15(sp)
- LONG_L $14, GDB_FR_REG14(sp)
- LONG_L $13, GDB_FR_REG13(sp)
- LONG_L $12, GDB_FR_REG12(sp)
- LONG_L $11, GDB_FR_REG11(sp)
- LONG_L $10, GDB_FR_REG10(sp)
- LONG_L $9, GDB_FR_REG9(sp)
- LONG_L $8, GDB_FR_REG8(sp)
- LONG_L $7, GDB_FR_REG7(sp)
- LONG_L $6, GDB_FR_REG6(sp)
- LONG_L $5, GDB_FR_REG5(sp)
- LONG_L $4, GDB_FR_REG4(sp)
- LONG_L $3, GDB_FR_REG3(sp)
- LONG_L $2, GDB_FR_REG2(sp)
- LONG_L $1, GDB_FR_REG1(sp)
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
- LONG_L k0, GDB_FR_EPC(sp)
- LONG_L $29, GDB_FR_REG29(sp) /* Deallocate stack */
- jr k0
- rfe
-#else
- LONG_L sp, GDB_FR_REG29(sp) /* Deallocate stack */
-
- .set mips3
- eret
- .set mips0
-#endif
- .set at
- .set reorder
- END(trap_low)
-
-LEAF(kgdb_read_byte)
-4: lb t0, (a0)
- sb t0, (a1)
- li v0, 0
- jr ra
- .section __ex_table,"a"
- PTR 4b, kgdbfault
- .previous
- END(kgdb_read_byte)
-
-LEAF(kgdb_write_byte)
-5: sb a0, (a1)
- li v0, 0
- jr ra
- .section __ex_table,"a"
- PTR 5b, kgdbfault
- .previous
- END(kgdb_write_byte)
-
- .type kgdbfault@function
- .ent kgdbfault
-
-kgdbfault: li v0, -EFAULT
- jr ra
- .end kgdbfault
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/gdb-stub.c linux-2.6.18.kgdb/arch/mips/kernel/gdb-stub.c
--- linux-2.6.18/arch/mips/kernel/gdb-stub.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/kernel/gdb-stub.c 1970-01-01 03:00:00.000000000 +0300
@@ -1,1154 +0,0 @@
-/*
- * arch/mips/kernel/gdb-stub.c
- *
- * Originally written by Glenn Engel, Lake Stevens Instrument Division
- *
- * Contributed by HP Systems
- *
- * Modified for SPARC by Stu Grossman, Cygnus Support.
- *
- * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
- * Send complaints, suggestions etc. to
- *
- * Copyright (C) 1995 Andreas Busse
- *
- * Copyright (C) 2003 MontaVista Software Inc.
- * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
- */
-
-/*
- * To enable debugger support, two things need to happen. One, a
- * call to set_debug_traps() is necessary in order to allow any breakpoints
- * or error conditions to be properly intercepted and reported to gdb.
- * Two, a breakpoint needs to be generated to begin communication. This
- * is most easily accomplished by a call to breakpoint(). Breakpoint()
- * simulates a breakpoint by executing a BREAK instruction.
- *
- *
- * 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)
- *
- * bBB..BB Set baud rate to BB..BB OK or BNN, then sets
- * baud rate
- *
- * All commands and responses are sent with a packet which includes a
- * checksum. A packet consists of
- *
- * $#.
- *
- * where
- * ::
- * :: < two hex digits computed as modulo 256 sum of >
- *
- * When a packet is received, it is first acknowledged with either '+' or '-'.
- * '+' indicates a successful transfer. '-' indicates a failed transfer.
- *
- * Example:
- *
- * Host: Reply:
- * $m0,10#2a +$00010203040506070809101112131415#42
- *
- *
- * ==============
- * MORE EXAMPLES:
- * ==============
- *
- * For reference -- the following are the steps that one
- * company took (RidgeRun Inc) to get remote gdb debugging
- * going. In this scenario the host machine was a PC and the
- * target platform was a Galileo EVB64120A MIPS evaluation
- * board.
- *
- * Step 1:
- * First download gdb-5.0.tar.gz from the internet.
- * and then build/install the package.
- *
- * Example:
- * $ tar zxf gdb-5.0.tar.gz
- * $ cd gdb-5.0
- * $ ./configure --target=mips-linux-elf
- * $ make
- * $ install
- * $ which mips-linux-elf-gdb
- * /usr/local/bin/mips-linux-elf-gdb
- *
- * Step 2:
- * Configure linux for remote debugging and build it.
- *
- * Example:
- * $ cd ~/linux
- * $ make menuconfig
- * $ make
- *
- * Step 3:
- * Download the kernel to the remote target and start
- * the kernel running. It will promptly halt and wait
- * for the host gdb session to connect. It does this
- * since the "Kernel Hacking" option has defined
- * CONFIG_KGDB which in turn enables your calls
- * to:
- * set_debug_traps();
- * breakpoint();
- *
- * Step 4:
- * Start the gdb session on the host.
- *
- * Example:
- * $ mips-linux-elf-gdb vmlinux
- * (gdb) set remotebaud 115200
- * (gdb) target remote /dev/ttyS1
- * ...at this point you are connected to
- * the remote target and can use gdb
- * in the normal fasion. Setting
- * breakpoints, single stepping,
- * printing variables, etc.
- */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/*
- * external low-level support routines
- */
-
-extern int putDebugChar(char c); /* write a single character */
-extern char getDebugChar(void); /* read and return a single char */
-extern void trap_low(void);
-
-/*
- * breakpoint and test functions
- */
-extern void breakpoint(void);
-extern void breakinst(void);
-extern void async_breakpoint(void);
-extern void async_breakinst(void);
-extern void adel(void);
-
-/*
- * local prototypes
- */
-
-static void getpacket(char *buffer);
-static void putpacket(char *buffer);
-static int computeSignal(int tt);
-static int hex(unsigned char ch);
-static int hexToInt(char **ptr, int *intValue);
-static int hexToLong(char **ptr, long *longValue);
-static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault);
-void handle_exception(struct gdb_regs *regs);
-
-int kgdb_enabled;
-
-/*
- * spin locks for smp case
- */
-static DEFINE_SPINLOCK(kgdb_lock);
-static raw_spinlock_t kgdb_cpulock[NR_CPUS] = {
- [0 ... NR_CPUS-1] = __RAW_SPIN_LOCK_UNLOCKED,
-};
-
-/*
- * BUFMAX defines the maximum number of characters in inbound/outbound buffers
- * at least NUMREGBYTES*2 are needed for register packets
- */
-#define BUFMAX 2048
-
-static char input_buffer[BUFMAX];
-static char output_buffer[BUFMAX];
-static int initialized; /* !0 means we've been initialized */
-static int kgdb_started;
-static const char hexchars[]="0123456789abcdef";
-
-/* Used to prevent crashes in memory access. Note that they'll crash anyway if
- we haven't set up fault handlers yet... */
-int kgdb_read_byte(unsigned char *address, unsigned char *dest);
-int kgdb_write_byte(unsigned char val, unsigned char *dest);
-
-/*
- * Convert ch from a hex digit to an int
- */
-static int hex(unsigned char ch)
-{
- if (ch >= 'a' && ch <= 'f')
- return ch-'a'+10;
- if (ch >= '0' && ch <= '9')
- return ch-'0';
- if (ch >= 'A' && ch <= 'F')
- return ch-'A'+10;
- return -1;
-}
-
-/*
- * scan for the sequence $#
- */
-static void getpacket(char *buffer)
-{
- unsigned char checksum;
- unsigned char xmitcsum;
- int i;
- int count;
- unsigned char ch;
-
- do {
- /*
- * wait around for the start character,
- * ignore all other characters
- */
- while ((ch = (getDebugChar() & 0x7f)) != '$') ;
-
- checksum = 0;
- xmitcsum = -1;
- count = 0;
-
- /*
- * now, read until a # or end of buffer is found
- */
- while (count < BUFMAX) {
- ch = getDebugChar();
- if (ch == '#')
- break;
- checksum = checksum + ch;
- buffer[count] = ch;
- count = count + 1;
- }
-
- if (count >= BUFMAX)
- continue;
-
- buffer[count] = 0;
-
- if (ch == '#') {
- xmitcsum = hex(getDebugChar() & 0x7f) << 4;
- xmitcsum |= hex(getDebugChar() & 0x7f);
-
- if (checksum != xmitcsum)
- putDebugChar('-'); /* failed checksum */
- else {
- putDebugChar('+'); /* successful transfer */
-
- /*
- * if a sequence char is present,
- * reply the sequence ID
- */
- if (buffer[2] == ':') {
- putDebugChar(buffer[0]);
- putDebugChar(buffer[1]);
-
- /*
- * remove sequence chars from buffer
- */
- count = strlen(buffer);
- for (i=3; i <= count; i++)
- buffer[i-3] = buffer[i];
- }
- }
- }
- }
- while (checksum != xmitcsum);
-}
-
-/*
- * send the packet in buffer.
- */
-static void putpacket(char *buffer)
-{
- unsigned char checksum;
- int count;
- unsigned char ch;
-
- /*
- * $#.
- */
-
- do {
- putDebugChar('$');
- checksum = 0;
- count = 0;
-
- while ((ch = buffer[count]) != 0) {
- if (!(putDebugChar(ch)))
- return;
- checksum += ch;
- count += 1;
- }
-
- putDebugChar('#');
- putDebugChar(hexchars[checksum >> 4]);
- putDebugChar(hexchars[checksum & 0xf]);
-
- }
- while ((getDebugChar() & 0x7f) != '+');
-}
-
-
-/*
- * Convert the memory pointed to by mem into hex, placing result in buf.
- * Return a pointer to the last char put in buf (null), in case of mem fault,
- * return 0.
- * may_fault is non-zero if we are reading from arbitrary memory, but is currently
- * not used.
- */
-static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault)
-{
- unsigned char ch;
-
- while (count-- > 0) {
- if (kgdb_read_byte(mem++, &ch) != 0)
- return 0;
- *buf++ = hexchars[ch >> 4];
- *buf++ = hexchars[ch & 0xf];
- }
-
- *buf = 0;
-
- return buf;
-}
-
-/*
- * convert the hex array pointed to by buf into binary to be placed in mem
- * return a pointer to the character AFTER the last byte written
- * may_fault is non-zero if we are reading from arbitrary memory, but is currently
- * not used.
- */
-static char *hex2mem(char *buf, char *mem, int count, int binary, int may_fault)
-{
- int i;
- unsigned char ch;
-
- for (i=0; itt && ht->signo; ht++)
- saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low);
-
- putDebugChar('+'); /* 'hello world' */
- /*
- * In case GDB is started before us, ack any packets
- * (presumably "$?#xx") sitting there.
- */
- while((c = getDebugChar()) != '$');
- while((c = getDebugChar()) != '#');
- c = getDebugChar(); /* eat first csum byte */
- c = getDebugChar(); /* eat second csum byte */
- putDebugChar('+'); /* ack it */
-
- initialized = 1;
- local_irq_restore(flags);
-}
-
-void restore_debug_traps(void)
-{
- struct hard_trap_info *ht;
- unsigned long flags;
-
- local_irq_save(flags);
- for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
- set_except_vector(ht->tt, saved_vectors[ht->tt]);
- local_irq_restore(flags);
-}
-
-/*
- * Convert the MIPS hardware trap type code to a Unix signal number.
- */
-static int computeSignal(int tt)
-{
- struct hard_trap_info *ht;
-
- for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
- if (ht->tt == tt)
- return ht->signo;
-
- return SIGHUP; /* default for things we don't know about */
-}
-
-/*
- * While we find nice hex chars, build an int.
- * Return number of chars processed.
- */
-static int hexToInt(char **ptr, int *intValue)
-{
- int numChars = 0;
- int hexValue;
-
- *intValue = 0;
-
- while (**ptr) {
- hexValue = hex(**ptr);
- if (hexValue < 0)
- break;
-
- *intValue = (*intValue << 4) | hexValue;
- numChars ++;
-
- (*ptr)++;
- }
-
- return (numChars);
-}
-
-static int hexToLong(char **ptr, long *longValue)
-{
- int numChars = 0;
- int hexValue;
-
- *longValue = 0;
-
- while (**ptr) {
- hexValue = hex(**ptr);
- if (hexValue < 0)
- break;
-
- *longValue = (*longValue << 4) | hexValue;
- numChars ++;
-
- (*ptr)++;
- }
-
- return numChars;
-}
-
-
-#if 0
-/*
- * Print registers (on target console)
- * Used only to debug the stub...
- */
-void show_gdbregs(struct gdb_regs * regs)
-{
- /*
- * Saved main processor registers
- */
- printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg0, regs->reg1, regs->reg2, regs->reg3,
- regs->reg4, regs->reg5, regs->reg6, regs->reg7);
- printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg8, regs->reg9, regs->reg10, regs->reg11,
- regs->reg12, regs->reg13, regs->reg14, regs->reg15);
- printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg16, regs->reg17, regs->reg18, regs->reg19,
- regs->reg20, regs->reg21, regs->reg22, regs->reg23);
- printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg24, regs->reg25, regs->reg26, regs->reg27,
- regs->reg28, regs->reg29, regs->reg30, regs->reg31);
-
- /*
- * Saved cp0 registers
- */
- printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n",
- regs->cp0_epc, regs->cp0_status, regs->cp0_cause);
-}
-#endif /* dead code */
-
-/*
- * We single-step by setting breakpoints. When an exception
- * is handled, we need to restore the instructions hoisted
- * when the breakpoints were set.
- *
- * This is where we save the original instructions.
- */
-static struct gdb_bp_save {
- unsigned long addr;
- unsigned int val;
-} step_bp[2];
-
-#define BP 0x0000000d /* break opcode */
-
-/*
- * Set breakpoint instructions for single stepping.
- */
-static void single_step(struct gdb_regs *regs)
-{
- union mips_instruction insn;
- unsigned long targ;
- int is_branch, is_cond, i;
-
- targ = regs->cp0_epc;
- insn.word = *(unsigned int *)targ;
- is_branch = is_cond = 0;
-
- switch (insn.i_format.opcode) {
- /*
- * jr and jalr are in r_format format.
- */
- case spec_op:
- switch (insn.r_format.func) {
- case jalr_op:
- case jr_op:
- targ = *(®s->reg0 + insn.r_format.rs);
- is_branch = 1;
- break;
- }
- break;
-
- /*
- * This group contains:
- * bltz_op, bgez_op, bltzl_op, bgezl_op,
- * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
- */
- case bcond_op:
- is_branch = is_cond = 1;
- targ += 4 + (insn.i_format.simmediate << 2);
- break;
-
- /*
- * These are unconditional and in j_format.
- */
- case jal_op:
- case j_op:
- is_branch = 1;
- targ += 4;
- targ >>= 28;
- targ <<= 28;
- targ |= (insn.j_format.target << 2);
- break;
-
- /*
- * These are conditional.
- */
- case beq_op:
- case beql_op:
- case bne_op:
- case bnel_op:
- case blez_op:
- case blezl_op:
- case bgtz_op:
- case bgtzl_op:
- case cop0_op:
- case cop1_op:
- case cop2_op:
- case cop1x_op:
- is_branch = is_cond = 1;
- targ += 4 + (insn.i_format.simmediate << 2);
- break;
- }
-
- if (is_branch) {
- i = 0;
- if (is_cond && targ != (regs->cp0_epc + 8)) {
- step_bp[i].addr = regs->cp0_epc + 8;
- step_bp[i++].val = *(unsigned *)(regs->cp0_epc + 8);
- *(unsigned *)(regs->cp0_epc + 8) = BP;
- }
- step_bp[i].addr = targ;
- step_bp[i].val = *(unsigned *)targ;
- *(unsigned *)targ = BP;
- } else {
- step_bp[0].addr = regs->cp0_epc + 4;
- step_bp[0].val = *(unsigned *)(regs->cp0_epc + 4);
- *(unsigned *)(regs->cp0_epc + 4) = BP;
- }
-}
-
-/*
- * If asynchronously interrupted by gdb, then we need to set a breakpoint
- * at the interrupted instruction so that we wind up stopped with a
- * reasonable stack frame.
- */
-static struct gdb_bp_save async_bp;
-
-/*
- * Swap the interrupted EPC with our asynchronous breakpoint routine.
- * This is safer than stuffing the breakpoint in-place, since no cache
- * flushes (or resulting smp_call_functions) are required. The
- * assumption is that only one CPU will be handling asynchronous bp's,
- * and only one can be active at a time.
- */
-extern spinlock_t smp_call_lock;
-
-void set_async_breakpoint(unsigned long *epc)
-{
- /* skip breaking into userland */
- if ((*epc & 0x80000000) == 0)
- return;
-
-#ifdef CONFIG_SMP
- /* avoid deadlock if someone is make IPC */
- if (spin_is_locked(&smp_call_lock))
- return;
-#endif
-
- async_bp.addr = *epc;
- *epc = (unsigned long)async_breakpoint;
-}
-
-static void kgdb_wait(void *arg)
-{
- unsigned flags;
- int cpu = smp_processor_id();
-
- local_irq_save(flags);
-
- __raw_spin_lock(&kgdb_cpulock[cpu]);
- __raw_spin_unlock(&kgdb_cpulock[cpu]);
-
- local_irq_restore(flags);
-}
-
-/*
- * GDB stub needs to call kgdb_wait on all processor with interrupts
- * disabled, so it uses it's own special variant.
- */
-static int kgdb_smp_call_kgdb_wait(void)
-{
-#ifdef CONFIG_SMP
- struct call_data_struct data;
- int i, cpus = num_online_cpus() - 1;
- int cpu = smp_processor_id();
-
- /*
- * Can die spectacularly if this CPU isn't yet marked online
- */
- BUG_ON(!cpu_online(cpu));
-
- if (!cpus)
- return 0;
-
- if (spin_is_locked(&smp_call_lock)) {
- /*
- * Some other processor is trying to make us do something
- * but we're not going to respond... give up
- */
- return -1;
- }
-
- /*
- * We will continue here, accepting the fact that
- * the kernel may deadlock if another CPU attempts
- * to call smp_call_function now...
- */
-
- data.func = kgdb_wait;
- data.info = NULL;
- atomic_set(&data.started, 0);
- data.wait = 0;
-
- spin_lock(&smp_call_lock);
- call_data = &data;
- mb();
-
- /* Send a message to all other CPUs and wait for them to respond */
- for (i = 0; i < NR_CPUS; i++)
- if (cpu_online(i) && i != cpu)
- core_send_ipi(i, SMP_CALL_FUNCTION);
-
- /* Wait for response */
- /* FIXME: lock-up detection, backtrace on lock-up */
- while (atomic_read(&data.started) != cpus)
- barrier();
-
- call_data = NULL;
- spin_unlock(&smp_call_lock);
-#endif
-
- return 0;
-}
-
-/*
- * This function does all command processing for interfacing to gdb. It
- * returns 1 if you should skip the instruction at the trap address, 0
- * otherwise.
- */
-void handle_exception (struct gdb_regs *regs)
-{
- int trap; /* Trap type */
- int sigval;
- long addr;
- int length;
- char *ptr;
- unsigned long *stack;
- int i;
- int bflag = 0;
-
- kgdb_started = 1;
-
- /*
- * acquire the big kgdb spinlock
- */
- if (!spin_trylock(&kgdb_lock)) {
- /*
- * some other CPU has the lock, we should go back to
- * receive the gdb_wait IPC
- */
- return;
- }
-
- /*
- * If we're in async_breakpoint(), restore the real EPC from
- * the breakpoint.
- */
- if (regs->cp0_epc == (unsigned long)async_breakinst) {
- regs->cp0_epc = async_bp.addr;
- async_bp.addr = 0;
- }
-
- /*
- * acquire the CPU spinlocks
- */
- for (i = num_online_cpus()-1; i >= 0; i--)
- if (__raw_spin_trylock(&kgdb_cpulock[i]) == 0)
- panic("kgdb: couldn't get cpulock %d\n", i);
-
- /*
- * force other cpus to enter kgdb
- */
- kgdb_smp_call_kgdb_wait();
-
- /*
- * If we're in breakpoint() increment the PC
- */
- trap = (regs->cp0_cause & 0x7c) >> 2;
- if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst)
- regs->cp0_epc += 4;
-
- /*
- * If we were single_stepping, restore the opcodes hoisted
- * for the breakpoint[s].
- */
- if (step_bp[0].addr) {
- *(unsigned *)step_bp[0].addr = step_bp[0].val;
- step_bp[0].addr = 0;
-
- if (step_bp[1].addr) {
- *(unsigned *)step_bp[1].addr = step_bp[1].val;
- step_bp[1].addr = 0;
- }
- }
-
- stack = (long *)regs->reg29; /* stack ptr */
- sigval = computeSignal(trap);
-
- /*
- * reply to host that an exception has occurred
- */
- ptr = output_buffer;
-
- /*
- * Send trap type (converted to signal)
- */
- *ptr++ = 'T';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
-
- /*
- * Send Error PC
- */
- *ptr++ = hexchars[REG_EPC >> 4];
- *ptr++ = hexchars[REG_EPC & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®s->cp0_epc, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- /*
- * Send frame pointer
- */
- *ptr++ = hexchars[REG_FP >> 4];
- *ptr++ = hexchars[REG_FP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®s->reg30, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- /*
- * Send stack pointer
- */
- *ptr++ = hexchars[REG_SP >> 4];
- *ptr++ = hexchars[REG_SP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®s->reg29, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- *ptr++ = 0;
- putpacket(output_buffer); /* send it off... */
-
- /*
- * Wait for input from remote GDB
- */
- while (1) {
- output_buffer[0] = 0;
- getpacket(input_buffer);
-
- switch (input_buffer[0])
- {
- case '?':
- output_buffer[0] = 'S';
- output_buffer[1] = hexchars[sigval >> 4];
- output_buffer[2] = hexchars[sigval & 0xf];
- output_buffer[3] = 0;
- break;
-
- /*
- * Detach debugger; let CPU run
- */
- case 'D':
- putpacket(output_buffer);
- goto finish_kgdb;
- break;
-
- case 'd':
- /* toggle debug flag */
- break;
-
- /*
- * Return the value of the CPU registers
- */
- case 'g':
- ptr = output_buffer;
- ptr = mem2hex((char *)®s->reg0, ptr, 32*sizeof(long), 0); /* r0...r31 */
- ptr = mem2hex((char *)®s->cp0_status, ptr, 6*sizeof(long), 0); /* cp0 */
- ptr = mem2hex((char *)®s->fpr0, ptr, 32*sizeof(long), 0); /* f0...31 */
- ptr = mem2hex((char *)®s->cp1_fsr, ptr, 2*sizeof(long), 0); /* cp1 */
- ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*sizeof(long), 0); /* frp */
- ptr = mem2hex((char *)®s->cp0_index, ptr, 16*sizeof(long), 0); /* cp0 */
- break;
-
- /*
- * set the value of the CPU registers - return OK
- */
- case 'G':
- {
- ptr = &input_buffer[1];
- hex2mem(ptr, (char *)®s->reg0, 32*sizeof(long), 0, 0);
- ptr += 32*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->cp0_status, 6*sizeof(long), 0, 0);
- ptr += 6*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->fpr0, 32*sizeof(long), 0, 0);
- ptr += 32*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->cp1_fsr, 2*sizeof(long), 0, 0);
- ptr += 2*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->frame_ptr, 2*sizeof(long), 0, 0);
- ptr += 2*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->cp0_index, 16*sizeof(long), 0, 0);
- strcpy(output_buffer,"OK");
- }
- break;
-
- /*
- * mAA..AA,LLLL Read LLLL bytes at address AA..AA
- */
- case 'm':
- ptr = &input_buffer[1];
-
- if (hexToLong(&ptr, &addr)
- && *ptr++ == ','
- && hexToInt(&ptr, &length)) {
- if (mem2hex((char *)addr, output_buffer, length, 1))
- break;
- strcpy (output_buffer, "E03");
- } else
- strcpy(output_buffer,"E01");
- break;
-
- /*
- * XAA..AA,LLLL: Write LLLL escaped binary bytes at address AA.AA
- */
- case 'X':
- bflag = 1;
- /* fall through */
-
- /*
- * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK
- */
- case 'M':
- ptr = &input_buffer[1];
-
- if (hexToLong(&ptr, &addr)
- && *ptr++ == ','
- && hexToInt(&ptr, &length)
- && *ptr++ == ':') {
- if (hex2mem(ptr, (char *)addr, length, bflag, 1))
- strcpy(output_buffer, "OK");
- else
- strcpy(output_buffer, "E03");
- }
- else
- strcpy(output_buffer, "E02");
- break;
-
- /*
- * cAA..AA Continue at address AA..AA(optional)
- */
- case 'c':
- /* try to read optional parameter, pc unchanged if no parm */
-
- ptr = &input_buffer[1];
- if (hexToLong(&ptr, &addr))
- regs->cp0_epc = addr;
-
- goto exit_kgdb_exception;
- break;
-
- /*
- * kill the program; let us try to restart the machine
- * Reset the whole machine.
- */
- case 'k':
- case 'r':
- machine_restart("kgdb restarts machine");
- break;
-
- /*
- * Step to next instruction
- */
- case 's':
- /*
- * There is no single step insn in the MIPS ISA, so we
- * use breakpoints and continue, instead.
- */
- single_step(regs);
- goto exit_kgdb_exception;
- /* NOTREACHED */
- break;
-
- /*
- * Set baud rate (bBB)
- * FIXME: Needs to be written
- */
- case 'b':
- {
-#if 0
- int baudrate;
- extern void set_timer_3();
-
- ptr = &input_buffer[1];
- if (!hexToInt(&ptr, &baudrate))
- {
- strcpy(output_buffer,"B01");
- break;
- }
-
- /* Convert baud rate to uart clock divider */
-
- switch (baudrate)
- {
- case 38400:
- baudrate = 16;
- break;
- case 19200:
- baudrate = 33;
- break;
- case 9600:
- baudrate = 65;
- break;
- default:
- baudrate = 0;
- strcpy(output_buffer,"B02");
- goto x1;
- }
-
- if (baudrate) {
- putpacket("OK"); /* Ack before changing speed */
- set_timer_3(baudrate); /* Set it */
- }
-#endif
- }
- break;
-
- } /* switch */
-
- /*
- * reply to the request
- */
-
- putpacket(output_buffer);
-
- } /* while */
-
- return;
-
-finish_kgdb:
- restore_debug_traps();
-
-exit_kgdb_exception:
- /* release locks so other CPUs can go */
- for (i = num_online_cpus()-1; i >= 0; i--)
- __raw_spin_unlock(&kgdb_cpulock[i]);
- spin_unlock(&kgdb_lock);
-
- __flush_cache_all();
- return;
-}
-
-/*
- * This function will generate a breakpoint exception. It is used at the
- * beginning of a program to sync up with a debugger and can be used
- * otherwise as a quick means to stop program execution and "break" into
- * the debugger.
- */
-void breakpoint(void)
-{
- if (!initialized)
- return;
-
- __asm__ __volatile__(
- ".globl breakinst\n\t"
- ".set\tnoreorder\n\t"
- "nop\n"
- "breakinst:\tbreak\n\t"
- "nop\n\t"
- ".set\treorder"
- );
-}
-
-/* Nothing but the break; don't pollute any registers */
-void async_breakpoint(void)
-{
- __asm__ __volatile__(
- ".globl async_breakinst\n\t"
- ".set\tnoreorder\n\t"
- "nop\n"
- "async_breakinst:\tbreak\n\t"
- "nop\n\t"
- ".set\treorder"
- );
-}
-
-void adel(void)
-{
- __asm__ __volatile__(
- ".globl\tadel\n\t"
- "lui\t$8,0x8000\n\t"
- "lw\t$9,1($8)\n\t"
- );
-}
-
-/*
- * malloc is needed by gdb client in "call func()", even a private one
- * will make gdb happy
- */
-static void * __attribute_used__ malloc(size_t size)
-{
- return kmalloc(size, GFP_ATOMIC);
-}
-
-static void __attribute_used__ free (void *where)
-{
- kfree(where);
-}
-
-#ifdef CONFIG_GDB_CONSOLE
-
-void gdb_putsn(const char *str, int l)
-{
- char outbuf[18];
-
- if (!kgdb_started)
- return;
-
- outbuf[0]='O';
-
- while(l) {
- int i = (l>8)?8:l;
- mem2hex((char *)str, &outbuf[1], i, 0);
- outbuf[(i*2)+1]=0;
- putpacket(outbuf);
- str += i;
- l -= i;
- }
-}
-
-static void gdb_console_write(struct console *con, const char *s, unsigned n)
-{
- gdb_putsn(s, n);
-}
-
-static struct console gdb_console = {
- .name = "gdb",
- .write = gdb_console_write,
- .flags = CON_PRINTBUFFER,
- .index = -1
-};
-
-static int __init register_gdb_console(void)
-{
- register_console(&gdb_console);
-
- return 0;
-}
-
-console_initcall(register_gdb_console);
-
-#endif
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/irq.c linux-2.6.18.kgdb/arch/mips/kernel/irq.c
--- linux-2.6.18/arch/mips/kernel/irq.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/kernel/irq.c 2008-06-10 16:19:28.000000000 +0400
@@ -25,6 +25,10 @@
#include
#include
#include
+#include
+
+/* Keep track of if we've done certain initialization already or not. */
+int kgdb_early_setup;
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
@@ -115,23 +119,13 @@ asmlinkage void spurious_interrupt(struc
atomic_inc(&irq_err_count);
}
-#ifdef CONFIG_KGDB
-extern void breakpoint(void);
-extern void set_debug_traps(void);
-
-static int kgdb_flag = 1;
-static int __init nokgdb(char *str)
-{
- kgdb_flag = 0;
- return 1;
-}
-__setup("nokgdb", nokgdb);
-#endif
-
void __init init_IRQ(void)
{
int i;
+ if (kgdb_early_setup)
+ return;
+
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = NULL;
@@ -144,12 +138,12 @@ void __init init_IRQ(void)
}
arch_init_irq();
-
#ifdef CONFIG_KGDB
- if (kgdb_flag) {
- printk("Wait for gdb client connection ...\n");
- set_debug_traps();
- breakpoint();
- }
+ /*
+ * We have been called before kgdb_arch_init(). Hence,
+ * we dont want the traps to be reinitialized
+ */
+ if (kgdb_early_setup == 0)
+ kgdb_early_setup = 1;
#endif
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/kgdb-jmp.c linux-2.6.18.kgdb/arch/mips/kernel/kgdb-jmp.c
--- linux-2.6.18/arch/mips/kernel/kgdb-jmp.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/mips/kernel/kgdb-jmp.c 2008-06-10 16:19:28.000000000 +0400
@@ -0,0 +1,116 @@
+/*
+ * arch/mips/kernel/kgdb-jmp.c
+ *
+ * Save and restore system registers so that within a limited frame we
+ * may have a fault and "jump back" to a known safe location.
+ *
+ * Author: Tom Rini
+ * Author: Manish Lachwani
+ *
+ * Cribbed from glibc, which carries the following:
+ * Copyright (C) 1996, 1997, 2000, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2005 by MontaVista Software.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include
+#include
+
+#ifdef CONFIG_MIPS64
+/*
+ * MIPS 64-bit
+ */
+
+int kgdb_fault_setjmp_aux(unsigned long *curr_context, int sp, int fp)
+{
+ __asm__ __volatile__ ("sd $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__ ("sd $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__ ("sd $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__ ("sd $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__ ("sd $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__ ("sd $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__ ("sd $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__ ("sd $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__ ("sd $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__ ("sd $31, %0" : : "m" (curr_context[9]));
+ curr_context[10] = (long *)sp;
+ curr_context[11] = (long *)fp;
+
+ return 0;
+}
+
+void kgdb_fault_longjmp(unsigned long *curr_context)
+{
+ unsigned long sp_val, fp_val;
+
+ __asm__ __volatile__ ("ld $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__ ("ld $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__ ("ld $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__ ("ld $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__ ("ld $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__ ("ld $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__ ("ld $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__ ("ld $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__ ("ld $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__ ("ld $25, %0" : : "m" (curr_context[9]));
+ sp_val = curr_context[10];
+ fp_val = curr_context[11];
+ __asm__ __volatile__ ("ld $29, %0\n\t"
+ "ld $30, %1\n\t" : : "m" (sp_val), "m" (fp_val));
+
+ __asm__ __volatile__ ("dli $2, 1");
+ __asm__ __volatile__ ("j $25");
+
+ for (;;);
+}
+#else
+/*
+ * MIPS 32-bit
+ */
+
+int kgdb_fault_setjmp_aux(unsigned long *curr_context, int sp, int fp)
+{
+ __asm__ __volatile__("sw $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__("sw $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__("sw $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__("sw $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__("sw $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__("sw $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__("sw $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__("sw $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__("sw $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__("sw $31, %0" : : "m" (curr_context[9]));
+ curr_context[10] = (long *)sp;
+ curr_context[11] = (long *)fp;
+
+ return 0;
+}
+
+void kgdb_fault_longjmp(unsigned long *curr_context)
+{
+ unsigned long sp_val, fp_val;
+
+ __asm__ __volatile__("lw $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__("lw $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__("lw $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__("lw $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__("lw $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__("lw $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__("lw $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__("lw $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__("lw $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__("lw $25, %0" : : "m" (curr_context[9]));
+ sp_val = curr_context[10];
+ fp_val = curr_context[11];
+ __asm__ __volatile__("lw $29, %0\n\t"
+ "lw $30, %1\n\t" : : "m" (sp_val), "m" (fp_val));
+
+ __asm__ __volatile__("li $2, 1");
+ __asm__ __volatile__("jr $25");
+
+ for (;;);
+}
+#endif
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/kgdb-setjmp.S linux-2.6.18.kgdb/arch/mips/kernel/kgdb-setjmp.S
--- linux-2.6.18/arch/mips/kernel/kgdb-setjmp.S 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/mips/kernel/kgdb-setjmp.S 2008-06-10 16:19:28.000000000 +0400
@@ -0,0 +1,28 @@
+/*
+ * arch/mips/kernel/kgdb-jmp.c
+ *
+ * Save and restore system registers so that within a limited frame we
+ * may have a fault and "jump back" to a known safe location.
+ *
+ * Copyright (C) 2005 by MontaVista Software.
+ * Author: Manish Lachwani (mlachwani@mvista.com)
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include
+#include
+#include
+#include
+
+ .ent kgdb_fault_setjmp,0
+ENTRY (kgdb_fault_setjmp)
+ move a1, sp
+ move a2, fp
+#ifdef CONFIG_MIPS64
+ nop
+#endif
+ j kgdb_fault_setjmp_aux
+ .end kgdb_fault_setjmp
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/kgdb.c linux-2.6.18.kgdb/arch/mips/kernel/kgdb.c
--- linux-2.6.18/arch/mips/kernel/kgdb.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/mips/kernel/kgdb.c 2008-06-10 16:19:28.000000000 +0400
@@ -0,0 +1,297 @@
+/*
+ * arch/mips/kernel/kgdb.c
+ *
+ * Originally written by Glenn Engel, Lake Stevens Instrument Division
+ *
+ * Contributed by HP Systems
+ *
+ * Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
+ * Send complaints, suggestions etc. to
+ *
+ * Copyright (C) 1995 Andreas Busse
+ *
+ * Copyright (C) 2003 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ *
+ * Copyright (C) 2004-2005 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* for linux pt_regs struct */
+#include
+#include
+#include
+#include
+#include
+#include
+
+static struct hard_trap_info {
+ unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */
+ unsigned char signo; /* Signal that we map this trap into */
+} hard_trap_info[] = {
+ { 6, SIGBUS }, /* instruction bus error */
+ { 7, SIGBUS }, /* data bus error */
+ { 9, SIGTRAP }, /* break */
+/* { 11, SIGILL }, */ /* CPU unusable */
+ { 12, SIGFPE }, /* overflow */
+ { 13, SIGTRAP }, /* trap */
+ { 14, SIGSEGV }, /* virtual instruction cache coherency */
+ { 15, SIGFPE }, /* floating point exception */
+ { 23, SIGSEGV }, /* watch */
+ { 31, SIGSEGV }, /* virtual data cache coherency */
+ { 0, 0} /* Must be last */
+};
+
+/* Save the normal trap handlers for user-mode traps. */
+void *saved_vectors[32];
+
+extern void trap_low(void);
+extern void breakinst(void);
+extern void init_IRQ(void);
+
+void kgdb_call_nmi_hook(void *ignored)
+{
+ kgdb_nmihook(smp_processor_id(), (void *)0);
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ local_irq_restore(flags);
+ smp_call_function(kgdb_call_nmi_hook, 0, 0, 0);
+ local_irq_save(flags);
+}
+
+static int compute_signal(int tt)
+{
+ struct hard_trap_info *ht;
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ if (ht->tt == tt)
+ return ht->signo;
+
+ return SIGHUP; /* default for things we don't know about */
+}
+
+/*
+ * Set up exception handlers for tracing and breakpoints
+ */
+void handle_exception(struct pt_regs *regs)
+{
+ int trap = (regs->cp0_cause & 0x7c) >> 2;
+
+ if (fixup_exception(regs)) {
+ return;
+ }
+
+ if (atomic_read(&debugger_active))
+ kgdb_nmihook(smp_processor_id(), regs);
+
+ if (atomic_read(&kgdb_setting_breakpoint))
+ if ((trap == 9) && (regs->cp0_epc == (unsigned long)breakinst))
+ regs->cp0_epc += 4;
+
+ kgdb_handle_exception(0, compute_signal(trap), 0, regs);
+
+ /* In SMP mode, __flush_cache_all does IPI */
+ __flush_cache_all();
+}
+
+void set_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low);
+
+ local_irq_restore(flags);
+}
+
+#if 0
+/* This should be called before we exit kgdb_handle_exception() I believe.
+ * -- Tom
+ */
+void restore_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ set_except_vector(ht->tt, saved_vectors[ht->tt]);
+ local_irq_restore(flags);
+}
+#endif
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ gdb_reg_t *ptr = (gdb_reg_t*)gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ *(ptr++) = regs->cp0_status;
+ *(ptr++) = regs->lo;
+ *(ptr++) = regs->hi;
+ *(ptr++) = regs->cp0_badvaddr;
+ *(ptr++) = regs->cp0_cause;
+ *(ptr++) = regs->cp0_epc;
+
+ return;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+
+ int reg;
+ const gdb_reg_t *ptr = (gdb_reg_t*)gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ regs->regs[reg] = *(ptr++);
+
+ regs->cp0_status = *(ptr++);
+ regs->lo = *(ptr++);
+ regs->hi = *(ptr++);
+ regs->cp0_badvaddr = *(ptr++);
+ regs->cp0_cause = *(ptr++);
+ regs->cp0_epc = *(ptr++);
+
+ return;
+}
+
+/*
+ * Similar to regs_to_gdb_regs() except that process is sleeping and so
+ * we may not be able to get all the info.
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ int reg;
+ struct thread_info *ti = p->thread_info;
+ unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32;
+ struct pt_regs *regs = (struct pt_regs *)ksp - 1;
+ gdb_reg_t *ptr = (gdb_reg_t*)gdb_regs;
+
+ for (reg = 0; reg < 16; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ /* S0 - S7 */
+ for (reg = 16; reg < 24; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ for (reg = 24; reg < 28; reg++)
+ *(ptr++) = 0;
+
+ /* GP, SP, FP, RA */
+ for (reg = 28; reg < 32; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ *(ptr++) = regs->cp0_status;
+ *(ptr++) = regs->lo;
+ *(ptr++) = regs->hi;
+ *(ptr++) = regs->cp0_badvaddr;
+ *(ptr++) = regs->cp0_cause;
+ *(ptr++) = regs->cp0_epc;
+
+ return;
+}
+
+/*
+ * Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
+ * then try to fall into the debugger
+ */
+static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *args = (struct die_args *)ptr;
+ struct pt_regs *regs = args->regs;
+ int trap = (regs->cp0_cause & 0x7c) >> 2;
+
+ /* See if KGDB is interested. */
+ if (user_mode(regs))
+ /* Userpace events, ignore. */
+ return NOTIFY_DONE;
+
+ kgdb_handle_exception(trap, compute_signal(trap), 0, regs);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_mips_notify,
+};
+
+/*
+ * Handle the 's' and 'c' commands
+ */
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ struct pt_regs *regs)
+{
+ char *ptr;
+ unsigned long address;
+ int cpu = smp_processor_id();
+
+ switch (remcom_in_buffer[0]) {
+ case 's':
+ case 'c':
+ /* handle the optional parameter */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &address))
+ regs->cp0_epc = address;
+
+ atomic_set(&cpu_doing_single_step, -1);
+ if (remcom_in_buffer[0] == 's')
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step, cpu);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ .gdb_bpt_instr = {0xd},
+#else
+ .gdb_bpt_instr = {0x00, 0x00, 0x00, 0x0d},
+#endif
+};
+
+/*
+ * We use kgdb_early_setup so that functions we need to call now don't
+ * cause trouble when called again later.
+ */
+int kgdb_arch_init(void)
+{
+ /* Board-specifics. */
+ /* Force some calls to happen earlier. */
+ if (kgdb_early_setup == 0) {
+ trap_init();
+ init_IRQ();
+ kgdb_early_setup = 1;
+ }
+
+ /* Set our traps. */
+ /* This needs to be done more finely grained again, paired in
+ * a before/after in kgdb_handle_exception(...) -- Tom */
+ set_debug_traps();
+ notifier_chain_register(&mips_die_chain, &kgdb_notifier);
+
+ return 0;
+}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/kgdb_handler.S linux-2.6.18.kgdb/arch/mips/kernel/kgdb_handler.S
--- linux-2.6.18/arch/mips/kernel/kgdb_handler.S 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/mips/kernel/kgdb_handler.S 2008-06-10 16:19:28.000000000 +0400
@@ -0,0 +1,57 @@
+/*
+ * arch/mips/kernel/kgdb_handler.S
+ *
+ * Copyright (C) 2004-2005 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/*
+ * Trap Handler for the new KGDB framework. The main KGDB handler is
+ * handle_exception that will be called from here
+ *
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+ .align 5
+ NESTED(trap_low, PT_SIZE, sp)
+ .set noat
+ .set noreorder
+
+ /*
+ * Check for privileged instructions in user mode. For
+ * this, check the cu0 bit in the CPU status register.
+ */
+ mfc0 k0, CP0_STATUS
+ sll k0, 3
+ bltz k0, 1f
+ move k1, sp
+
+ /*
+ * GDB userland from within KGDB. If a user mode address
+ * then jump to the saved exception handler
+ */
+ mfc0 k1, CP0_CAUSE
+ andi k1, k1, 0x7c
+ PTR_L k0, saved_vectors(k1)
+ jr k0
+ nop
+1:
+ SAVE_ALL
+ .set at
+ .set reorder
+ move a0, sp
+ jal handle_exception
+ j ret_from_exception
+ END(trap_low)
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/kernel/traps.c linux-2.6.18.kgdb/arch/mips/kernel/traps.c
--- linux-2.6.18/arch/mips/kernel/traps.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/kernel/traps.c 2008-06-10 16:19:28.000000000 +0400
@@ -10,6 +10,8 @@
* Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
* Copyright (C) 2000, 01 MIPS Technologies, Inc.
* Copyright (C) 2002, 2003, 2004, 2005 Maciej W. Rozycki
+ *
+ * KGDB specific changes - Manish Lachwani (mlachwani@mvista.com)
*/
#include
#include
@@ -20,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
@@ -40,6 +43,7 @@
#include
#include
#include
+#include
extern asmlinkage void handle_int(void);
extern asmlinkage void handle_tlbm(void);
@@ -78,6 +82,21 @@ void (*board_bind_eic_interrupt)(int irq
*/
#define MODULE_RANGE (8*1024*1024)
+struct notifier_block *mips_die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+ int err = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&die_notifier_lock, flags);
+ err = notifier_chain_register(&mips_die_chain, nb);
+ spin_unlock_irqrestore(&die_notifier_lock, flags);
+
+ return err;
+}
+
/*
* This routine abuses get_user()/put_user() to reference pointers
* with at least a bit of error checking ...
@@ -1387,6 +1406,11 @@ void __init trap_init(void)
extern char except_vec4;
unsigned long i;
+#if defined(CONFIG_KGDB)
+ if (kgdb_early_setup)
+ return; /* Already done */
+#endif
+
if (cpu_has_veic || cpu_has_vint)
ebase = (unsigned long) alloc_bootmem_low_pages (0x200 + VECTORSPACING*64);
else
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/mips-boards/generic/Makefile linux-2.6.18.kgdb/arch/mips/mips-boards/generic/Makefile
--- linux-2.6.18/arch/mips/mips-boards/generic/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/mips-boards/generic/Makefile 2008-06-10 16:19:28.000000000 +0400
@@ -21,6 +21,5 @@
obj-y := reset.o display.o init.o memory.o printf.o \
cmdline.o time.o
obj-$(CONFIG_PCI) += pci.o
-obj-$(CONFIG_KGDB) += gdb_hook.o
EXTRA_AFLAGS := $(CFLAGS)
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/mips-boards/generic/init.c linux-2.6.18.kgdb/arch/mips/mips-boards/generic/init.c
--- linux-2.6.18/arch/mips/mips-boards/generic/init.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/mips-boards/generic/init.c 2008-06-10 16:19:28.000000000 +0400
@@ -37,15 +37,6 @@
#include
-#ifdef CONFIG_KGDB
-extern int rs_kgdb_hook(int, int);
-extern int rs_putDebugChar(char);
-extern char rs_getDebugChar(void);
-extern int saa9730_kgdb_hook(int);
-extern int saa9730_putDebugChar(char);
-extern char saa9730_getDebugChar(void);
-#endif
-
int prom_argc;
int *_prom_argv, *_prom_envp;
@@ -172,58 +163,6 @@ static void __init console_config(void)
}
#endif
-#ifdef CONFIG_KGDB
-void __init kgdb_config (void)
-{
- extern int (*generic_putDebugChar)(char);
- extern char (*generic_getDebugChar)(void);
- char *argptr;
- int line, speed;
-
- argptr = prom_getcmdline();
- if ((argptr = strstr(argptr, "kgdb=ttyS")) != NULL) {
- argptr += strlen("kgdb=ttyS");
- if (*argptr != '0' && *argptr != '1')
- printk("KGDB: Unknown serial line /dev/ttyS%c, "
- "falling back to /dev/ttyS1\n", *argptr);
- line = *argptr == '0' ? 0 : 1;
- printk("KGDB: Using serial line /dev/ttyS%d for session\n", line);
-
- speed = 0;
- if (*++argptr == ',')
- {
- int c;
- while ((c = *++argptr) && ('0' <= c && c <= '9'))
- speed = speed * 10 + c - '0';
- }
-#ifdef CONFIG_MIPS_ATLAS
- if (line == 1) {
- speed = saa9730_kgdb_hook(speed);
- generic_putDebugChar = saa9730_putDebugChar;
- generic_getDebugChar = saa9730_getDebugChar;
- }
- else
-#endif
- {
- speed = rs_kgdb_hook(line, speed);
- generic_putDebugChar = rs_putDebugChar;
- generic_getDebugChar = rs_getDebugChar;
- }
-
- prom_printf("KGDB: Using serial line /dev/ttyS%d at %d for session, "
- "please connect your debugger\n", line ? 1 : 0, speed);
-
- {
- char *s;
- for (s = "Please connect GDB to this port\r\n"; *s; )
- generic_putDebugChar (*s++);
- }
-
- /* Breakpoint is invoked after interrupts are initialised */
- }
-}
-#endif
-
void __init mips_nmi_setup (void)
{
void *base;
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/mips-boards/malta/malta_setup.c linux-2.6.18.kgdb/arch/mips/mips-boards/malta/malta_setup.c
--- linux-2.6.18/arch/mips/mips-boards/malta/malta_setup.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/mips-boards/malta/malta_setup.c 2008-06-10 16:19:28.000000000 +0400
@@ -46,10 +46,6 @@ extern void mips_reboot_setup(void);
extern void mips_time_init(void);
extern unsigned long mips_rtc_get_time(void);
-#ifdef CONFIG_KGDB
-extern void kgdb_config(void);
-#endif
-
struct resource standard_io_resources[] = {
{ .name = "dma1", .start = 0x00, .end = 0x1f, .flags = IORESOURCE_BUSY },
{ .name = "timer", .start = 0x40, .end = 0x5f, .flags = IORESOURCE_BUSY },
@@ -124,10 +120,6 @@ void __init plat_mem_setup(void)
*/
enable_dma(4);
-#ifdef CONFIG_KGDB
- kgdb_config ();
-#endif
-
if ((mips_revision_corid == MIPS_REVISION_CORID_BONITO64) ||
(mips_revision_corid == MIPS_REVISION_CORID_CORE_20K) ||
(mips_revision_corid == MIPS_REVISION_CORID_CORE_EMUL_BON)) {
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/mm/extable.c linux-2.6.18.kgdb/arch/mips/mm/extable.c
--- linux-2.6.18/arch/mips/mm/extable.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/mm/extable.c 2008-06-10 16:19:28.000000000 +0400
@@ -3,6 +3,7 @@
*/
#include
#include
+#include
#include
#include
@@ -16,6 +17,12 @@ int fixup_exception(struct pt_regs *regs
return 1;
}
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+#endif
return 0;
}
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/sibyte/cfe/setup.c linux-2.6.18.kgdb/arch/mips/sibyte/cfe/setup.c
--- linux-2.6.18/arch/mips/sibyte/cfe/setup.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/sibyte/cfe/setup.c 2008-06-10 16:19:28.000000000 +0400
@@ -58,10 +58,6 @@ int cfe_cons_handle;
extern unsigned long initrd_start, initrd_end;
#endif
-#ifdef CONFIG_KGDB
-extern int kgdb_port;
-#endif
-
static void ATTRIB_NORET cfe_linux_exit(void *arg)
{
int warm = *(int *)arg;
@@ -242,9 +238,6 @@ void __init prom_init(void)
int argc = fw_arg0;
char **envp = (char **) fw_arg2;
int *prom_vec = (int *) fw_arg3;
-#ifdef CONFIG_KGDB
- char *arg;
-#endif
_machine_restart = cfe_linux_restart;
_machine_halt = cfe_linux_halt;
@@ -308,13 +301,6 @@ void __init prom_init(void)
}
}
-#ifdef CONFIG_KGDB
- if ((arg = strstr(arcs_cmdline,"kgdb=duart")) != NULL)
- kgdb_port = (arg[10] == '0') ? 0 : 1;
- else
- kgdb_port = 1;
-#endif
-
#ifdef CONFIG_BLK_DEV_INITRD
{
char *ptr;
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/sibyte/sb1250/Makefile linux-2.6.18.kgdb/arch/mips/sibyte/sb1250/Makefile
--- linux-2.6.18/arch/mips/sibyte/sb1250/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/sibyte/sb1250/Makefile 2008-06-10 16:19:28.000000000 +0400
@@ -4,5 +4,6 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SIBYTE_TBPROF) += bcm1250_tbprof.o
obj-$(CONFIG_SIBYTE_STANDALONE) += prom.o
obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o
+obj-$(CONFIG_KGDB_SIBYTE) += kgdb_sibyte.o
EXTRA_AFLAGS := $(CFLAGS)
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/sibyte/sb1250/irq.c linux-2.6.18.kgdb/arch/mips/sibyte/sb1250/irq.c
--- linux-2.6.18/arch/mips/sibyte/sb1250/irq.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/sibyte/sb1250/irq.c 2008-06-10 16:19:28.000000000 +0400
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
@@ -59,16 +60,6 @@ static void sb1250_set_affinity(unsigned
extern unsigned long ldt_eoi_space;
#endif
-#ifdef CONFIG_KGDB
-static int kgdb_irq;
-
-/* Default to UART1 */
-int kgdb_port = 1;
-#ifdef CONFIG_SIBYTE_SB1250_DUART
-extern char sb1250_duart_present[];
-#endif
-#endif
-
static struct irq_chip sb1250_irq_type = {
.typename = "SB1250-IMR",
.startup = startup_sb1250_irq,
@@ -324,6 +315,11 @@ void __init arch_init_irq(void)
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;
+#ifdef CONFIG_KGDB
+ if (kgdb_early_setup)
+ return;
+#endif
+
/* Default everything to IP2 */
for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */
__raw_writeq(IMR_IP2_VAL,
@@ -375,50 +371,6 @@ void __init arch_init_irq(void)
/* Enable necessary IPs, disable the rest */
change_c0_status(ST0_IM, imask);
-#ifdef CONFIG_KGDB
- if (kgdb_flag) {
- kgdb_irq = K_INT_UART_0 + kgdb_port;
-
-#ifdef CONFIG_SIBYTE_SB1250_DUART
- sb1250_duart_present[kgdb_port] = 0;
-#endif
- /* Setup uart 1 settings, mapper */
- __raw_writeq(M_DUART_IMR_BRK,
- IOADDR(A_DUART_IMRREG(kgdb_port)));
-
- sb1250_steal_irq(kgdb_irq);
- __raw_writeq(IMR_IP6_VAL,
- IOADDR(A_IMR_REGISTER(0,
- R_IMR_INTERRUPT_MAP_BASE) +
- (kgdb_irq << 3)));
- sb1250_unmask_irq(0, kgdb_irq);
- }
-#endif
-}
-
-#ifdef CONFIG_KGDB
-
-#include
-
-#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-
-static void sb1250_kgdb_interrupt(struct pt_regs *regs)
-{
- /*
- * Clear break-change status (allow some time for the remote
- * host to stop the break, since we would see another
- * interrupt on the end-of-break too)
- */
- kstat_this_cpu.irqs[kgdb_irq]++;
- mdelay(500);
- duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
- M_DUART_RX_EN | M_DUART_TX_EN);
- set_async_breakpoint(®s->cp0_epc);
-}
-
-#endif /* CONFIG_KGDB */
-
static inline int dclz(unsigned long long x)
{
int lz;
@@ -473,7 +425,7 @@ asmlinkage void plat_irq_dispatch(struct
sb1250_mailbox_interrupt(regs);
#endif
-#ifdef CONFIG_KGDB
+#ifdef CONFIG_KGDB_SIBYTE
else if (pending & CAUSEF_IP6) /* KGDB (uart 1) */
sb1250_kgdb_interrupt(regs);
#endif
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/sibyte/sb1250/kgdb_sibyte.c linux-2.6.18.kgdb/arch/mips/sibyte/sb1250/kgdb_sibyte.c
--- linux-2.6.18/arch/mips/sibyte/sb1250/kgdb_sibyte.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/mips/sibyte/sb1250/kgdb_sibyte.c 2008-06-10 16:19:28.000000000 +0400
@@ -0,0 +1,164 @@
+/*
+ * arch/mips/sibyte/sb1250/kgdb_sibyte.c
+ *
+ * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
+ *
+ * 2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+/*
+ * Support for KGDB on the Broadcom Sibyte. The SWARM board
+ * for example does not have a 8250/16550 compatible serial
+ * port. Hence, we need to have a driver for the serial
+ * ports to handle KGDB. This board needs nothing in addition
+ * to what is normally provided by the gdb portion of the stub.
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+int kgdb_port = 1;
+static int kgdb_irq;
+
+extern char sb1250_duart_present[];
+extern int sb1250_steal_irq(int irq);
+
+/* Forward declarations. */
+static void kgdbsibyte_init_duart(void);
+static int kgdb_init_io(void);
+
+#define IMR_IP6_VAL K_INT_MAP_I4
+#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+
+static void kgdb_swarm_write_char(int c)
+{
+ while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0) ;
+ duart_out(R_DUART_TX_HOLD, c);
+}
+
+static int kgdb_swarm_read_char(void)
+{
+ int ret_char;
+ unsigned int status;
+
+ status = duart_in(R_DUART_STATUS);
+ while ((status & M_DUART_RX_RDY) == 0) {
+ status = duart_in(R_DUART_STATUS);
+ }
+
+ /*
+ * Check for framing error
+ */
+ if (status & M_DUART_FRM_ERR) {
+ kgdbsibyte_init_duart();
+ kgdb_swarm_write_char('-');
+ return '-';
+ }
+
+ ret_char = duart_in(R_DUART_RX_HOLD);
+
+ return ret_char;
+}
+
+void sb1250_kgdb_interrupt(struct pt_regs *regs)
+{
+ int kgdb_irq = K_INT_UART_0 + kgdb_port;
+ /*
+ * Clear break-change status (allow some time for the remote
+ * host to stop the break, since we would see another
+ * interrupt on the end-of-break too)
+ */
+ kstat_this_cpu.irqs[kgdb_irq]++;
+ mdelay(500);
+ duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
+ M_DUART_RX_EN | M_DUART_TX_EN);
+ if (kgdb_io_ops.init != kgdb_init_io) {
+ /* Throw away the data if another I/O routine is
+ * active.
+ */
+ unsigned int status;
+
+ status = duart_in(R_DUART_STATUS);
+ while ((status & M_DUART_RX_RDY) == 0) {
+ status = duart_in(R_DUART_STATUS);
+ }
+ /*
+ * Check for framing error
+ */
+ if (status & M_DUART_FRM_ERR) {
+ kgdbsibyte_init_duart();
+ }
+ duart_in(R_DUART_RX_HOLD);
+ } else
+ breakpoint();
+
+}
+
+/*
+ * We use port #1 and we set it for 115200 BAUD, 8n1.
+ */
+static void kgdbsibyte_init_duart(void)
+{
+ /* Set 8n1. */
+ duart_out(R_DUART_MODE_REG_1,
+ V_DUART_BITS_PER_CHAR_8 | V_DUART_PARITY_MODE_NONE);
+ duart_out(R_DUART_MODE_REG_2, M_DUART_STOP_BIT_LEN_1);
+ /* Set baud rate of 115200. */
+ duart_out(R_DUART_CLK_SEL, V_DUART_BAUD_RATE(115200));
+ /* Enable rx and tx */
+ duart_out(R_DUART_CMD, M_DUART_RX_EN | M_DUART_TX_EN);
+}
+
+static int kgdb_init_io(void)
+{
+#ifdef CONFIG_SIBYTE_SB1250_DUART
+ sb1250_duart_present[kgdb_port] = 0;
+#endif
+
+ kgdbsibyte_init_duart();
+
+ return 0;
+}
+
+/*
+ * Hookup our IRQ line. We will already have been initialized a
+ * this point.
+ */
+static void __init kgdbsibyte_hookup_irq(void)
+{
+ /* Steal the IRQ. */
+ kgdb_irq = K_INT_UART_0 + kgdb_port;
+
+ /* Setup uart 1 settings, mapper */
+ __raw_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port)));
+
+ sb1250_steal_irq(kgdb_irq);
+
+ __raw_writeq(IMR_IP6_VAL,
+ IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+ (kgdb_irq << 3)));
+
+ sb1250_unmask_irq(0, kgdb_irq);
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_swarm_read_char,
+ .write_char = kgdb_swarm_write_char,
+ .init = kgdb_init_io,
+ .late_init = kgdbsibyte_hookup_irq,
+ .pre_exception = NULL,
+ .post_exception = NULL
+};
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/sibyte/swarm/Makefile linux-2.6.18.kgdb/arch/mips/sibyte/swarm/Makefile
--- linux-2.6.18/arch/mips/sibyte/swarm/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/sibyte/swarm/Makefile 2008-06-10 16:19:28.000000000 +0400
@@ -1,3 +1 @@
lib-y = setup.o rtc_xicor1241.o rtc_m41t81.o
-
-lib-$(CONFIG_KGDB) += dbg_io.o
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/sibyte/swarm/dbg_io.c linux-2.6.18.kgdb/arch/mips/sibyte/swarm/dbg_io.c
--- linux-2.6.18/arch/mips/sibyte/swarm/dbg_io.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/sibyte/swarm/dbg_io.c 1970-01-01 03:00:00.000000000 +0300
@@ -1,76 +0,0 @@
-/*
- * kgdb debug routines for SiByte boards.
- *
- * Copyright (C) 2001 MontaVista Software Inc.
- * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-/* -------------------- BEGINNING OF CONFIG --------------------- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/*
- * We use the second serial port for kgdb traffic.
- * 115200, 8, N, 1.
- */
-
-#define BAUD_RATE 115200
-#define CLK_DIVISOR V_DUART_BAUD_RATE(BAUD_RATE)
-#define DATA_BITS V_DUART_BITS_PER_CHAR_8 /* or 7 */
-#define PARITY V_DUART_PARITY_MODE_NONE /* or even */
-#define STOP_BITS M_DUART_STOP_BIT_LEN_1 /* or 2 */
-
-static int duart_initialized = 0; /* 0: need to be init'ed by kgdb */
-
-/* -------------------- END OF CONFIG --------------------- */
-extern int kgdb_port;
-
-#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-
-void putDebugChar(unsigned char c);
-unsigned char getDebugChar(void);
-static void
-duart_init(int clk_divisor, int data, int parity, int stop)
-{
- duart_out(R_DUART_MODE_REG_1, data | parity);
- duart_out(R_DUART_MODE_REG_2, stop);
- duart_out(R_DUART_CLK_SEL, clk_divisor);
-
- duart_out(R_DUART_CMD, M_DUART_RX_EN | M_DUART_TX_EN); /* enable rx and tx */
-}
-
-void
-putDebugChar(unsigned char c)
-{
- if (!duart_initialized) {
- duart_initialized = 1;
- duart_init(CLK_DIVISOR, DATA_BITS, PARITY, STOP_BITS);
- }
- while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0);
- duart_out(R_DUART_TX_HOLD, c);
-}
-
-unsigned char
-getDebugChar(void)
-{
- if (!duart_initialized) {
- duart_initialized = 1;
- duart_init(CLK_DIVISOR, DATA_BITS, PARITY, STOP_BITS);
- }
- while ((duart_in(R_DUART_STATUS) & M_DUART_RX_RDY) == 0) ;
- return duart_in(R_DUART_RX_HOLD);
-}
-
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/mips/tx4938/common/Makefile linux-2.6.18.kgdb/arch/mips/tx4938/common/Makefile
--- linux-2.6.18/arch/mips/tx4938/common/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/mips/tx4938/common/Makefile 2008-06-10 16:19:28.000000000 +0400
@@ -7,5 +7,5 @@
#
obj-y += prom.o setup.o irq.o rtc_rx5c348.o
-obj-$(CONFIG_KGDB) += dbgio.o
+obj-$(CONFIG_KGDB_8250) += dbgio.o
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/powerpc/Kconfig.debug linux-2.6.18.kgdb/arch/powerpc/Kconfig.debug
--- linux-2.6.18/arch/powerpc/Kconfig.debug 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/powerpc/Kconfig.debug 2008-06-10 16:19:22.000000000 +0400
@@ -18,52 +18,9 @@ config DEBUG_STACK_USAGE
This option will slow down process creation somewhat.
-config DEBUGGER
- bool "Enable debugger hooks"
- depends on DEBUG_KERNEL
- help
- Include in-kernel hooks for kernel debuggers. Unless you are
- intending to debug the kernel, say N here.
-
-config KGDB
- bool "Include kgdb kernel debugger"
- depends on DEBUGGER && (BROKEN || PPC_GEN550 || 4xx)
- select DEBUG_INFO
- help
- Include in-kernel hooks for kgdb, the Linux kernel source level
- debugger. See for more information.
- Unless you are intending to debug the kernel, say N here.
-
-choice
- prompt "Serial Port"
- depends on KGDB
- default KGDB_TTYS1
-
-config KGDB_TTYS0
- bool "ttyS0"
-
-config KGDB_TTYS1
- bool "ttyS1"
-
-config KGDB_TTYS2
- bool "ttyS2"
-
-config KGDB_TTYS3
- bool "ttyS3"
-
-endchoice
-
-config KGDB_CONSOLE
- bool "Enable serial console thru kgdb port"
- depends on KGDB && 8xx || CPM2
- help
- If you enable this, all serial console messages will be sent
- over the gdb stub.
- If unsure, say N.
-
config XMON
bool "Include xmon kernel debugger"
- depends on DEBUGGER && !PPC_ISERIES
+ depends on DEBUG_KERNEL && !PPC_ISERIES
help
Include in-kernel hooks for the xmon kernel monitor/debugger.
Unless you are intending to debug the kernel, say N here.
@@ -82,6 +39,11 @@ config XMON_DEFAULT
xmon is normally disabled unless booted with 'xmon=on'.
Use 'xmon=off' to disable xmon init during runtime.
+config DEBUGGER
+ bool
+ depends on KGDB || XMON
+ default y
+
config IRQSTACKS
bool "Use separate kernel stacks when processing interrupts"
depends on PPC64
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/powerpc/kernel/Makefile linux-2.6.18.kgdb/arch/powerpc/kernel/Makefile
--- linux-2.6.18/arch/powerpc/kernel/Makefile 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.kgdb/arch/powerpc/kernel/Makefile 2008-06-10 16:19:22.000000000 +0400
@@ -60,6 +60,7 @@ obj-$(CONFIG_BOOTX_TEXT) += btext.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o
+obj-$(CONFIG_KGDB) += kgdb.o
module-$(CONFIG_PPC64) += module_64.o
obj-$(CONFIG_MODULES) += $(module-y)
diff -rupN -X ../client-cleanup/dontdiff linux-2.6.18/arch/powerpc/kernel/kgdb.c linux-2.6.18.kgdb/arch/powerpc/kernel/kgdb.c
--- linux-2.6.18/arch/powerpc/kernel/kgdb.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6.18.kgdb/arch/powerpc/kernel/kgdb.c 2008-06-10 16:19:22.000000000 +0400
@@ -0,0 +1,568 @@
+/*
+ * arch/powerpc/kernel/kgdb.c
+ *
+ * PowerPC backend to the KGDB stub.
+ *
+ * Maintainer: Tom Rini
+ *
+ * Copied from arch/ppc/kernel/kgdb.c, updated for ppc64
+ *
+ * Copyright (C) 1996 Paul Mackerras (setjmp/longjmp)
+ * 1998 (c) Michael AK Tesch (tesch@cs.wisc.edu)
+ * Copyright (C) 2003 Timesys Corporation.
+ * Copyright (C) 2004-2006 MontaVista Software, Inc.
+ * PPC64 Mods (C) 2005 Frank Rowand (frowand@mvista.com)
+ * PPC32 support restored by Vitaly Wool and
+ * Sergei Shtylyov
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include