GIT e5ff36527000b603ad7f0fb26a66bdf3fd563895 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb.git#for_mm commit e5ff36527000b603ad7f0fb26a66bdf3fd563895 Author: Jason Wessel Date: Sun Jul 29 09:55:35 2007 -0500 xmon_or_kgdb.patch Subject: [PATCH] Turn off xmon if kgdb is active on ppc and powerpc Xmon and kgdb are mutuall exclusive at the moment so xmon should be turned off in the kernel config options if kgdb selected. The case with "make allmodconfig" was failing and this patch addresses the problem. Signed-off-by: Jason Wessel commit 6f29ef3ccaa648fa85be6d1435aa5a840f5c576d Author: Jason Wessel Date: Sun Jul 29 09:55:34 2007 -0500 asm_debug.patch Subject: [PATCH] Add ability to unoptimize module compiles and dwarf2 asm This patch adds in the ability to unoptimize a single kernel module to make source stepping more linear and not optimize out variables that the developer might like to inspect. It used with adding COPTIMIZE=-O0 to the build line. Also when creating a debug kernel add in dwarf2 sections for the assembled code. This makes source stepping the assembly files possible as well as having references into the .S files in the back trace information. Signed-off-by: Jason Wessel commit f680146fcb9096037ea25806bbf3ba884fce7811 Author: Jason Wessel Date: Sun Jul 29 09:55:32 2007 -0500 mips64_pt_regs_padding.patch Subject: [PATCH] pad pt_regs on MIPS64 for function arguments in an exception When using KGDB the pt_regs has the function arguments saved to the stack. 48 bytes are required for MIPS 64 for this purpose. Signed-off-by: Jason Wessel commit 9f5e5119bd2e68d8f489bdd6af44ae2f114c7443 Author: Jason Wessel Date: Sun Jul 29 09:55:31 2007 -0500 x86_64-hw_breakpoints.patch Subject: [PATCH] This adds hardware breakpoint support for x86_64. This is the minimal set of routines support hw breakpoints on the x86 arch rewritten by Jason Wessel, based on the work by Milind Dumbare. Also in cluded are bug fixes and review by Sergei Shtylyov. Signed-off-by: Milind Dumbare Signed-off-by: Jason Wessel Signed-off-by: Sergei Shtylyov arch/x86_64/kernel/kgdb.c | 128 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 8 deletions(-) commit fe50718f1cc6e98044ea1743820a99e53400d208 Author: Jason Wessel Date: Sun Jul 29 09:55:30 2007 -0500 i386-hw_breakpoints.patch Subject: [PATCH] This adds hardware breakpoint support for i386. This is the minimal set of routines support hw breakpoints on the x86 arch rewritten by Jason Wessel, based on the work by Milind Dumbare. Also in cluded are bug fixes and review by Sergei Shtylyov. Signed-off-by: Milind Dumbare Signed-off-by: Jason Wessel Signed-off-by: Sergei Shtylyov arch/i386/kernel/kgdb.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 3 deletions(-) commit e40b1a67e48c3e3d193a7c155a47619b60c1c443 Author: Jason Wessel Date: Sun Jul 29 09:55:28 2007 -0500 powerpc.patch Subject: [PATCH] Fixed singlestep on critical exception stack on ppc. Signed-Off-By Milind Dumbare Signed-off-by: Jason Wessel commit 4d8229d06f6c792555360e1eab1351147373f551 Author: Jason Wessel Date: Sun Jul 29 09:55:27 2007 -0500 core.patch Subject: [PATCH] Soft lockup fixes to kgdb core Allow KGDB to obtain processor sync control from the softlockup routines. Signed-off-by: Milind Dumbare Signed-off-by: Jason Wessel commit 4258f4ecbe1b88e38945396253b6a1ae4db288f9 Author: Jason Wessel Date: Sun Jul 29 09:55:26 2007 -0500 module.patch Subject: [PATCH] This allows for KGDB to better deal with autoloaded modules. The way this works, requires a patch to GDB. This patch can be found at: ftp://ftp..kernel.org/pub/linux/kernel/people/trini/patches/gdb/6.3/gdb-6.3-kgdb-module-notification.patch The way this works is that the solib-search-path must contain the location of any module to be debugged, and then when a module is loaded GDB acts like a shared library has been loaded now, and can be used that way. Even when not using kgdb+gdb, it is quite useful for a debugger+ICE/jtag. Signed-off-by: Milind Dumbare Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit c2a6e8f879d1716a0c7cfbc2b3ac6169e3a92c8a Author: Jason Wessel Date: Sun Jul 29 09:55:24 2007 -0500 sysrq_bugfix.patch Subject: [PATCH] Fix possibility of missing SysRq-G It is possible that when SysRq-G is triggered via the keyboard that we will miss the "up" event and once KGDB lets the kernel go another SysRq will be required to clear this, without this change. Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit 36d59c2d8471ca0a136050a2fa83f48650196645 Author: Jason Wessel Date: Sun Jul 29 09:55:23 2007 -0500 arm-lite.patch Subject: [PATCH] KGDB arch support for ARM This adds a backend, written by Deepak Saxena and George Davis as well as support for the TI OMAP boards, ADI Coyote, PXA2xx, ARM Versatile and PNX4008. Geoff Levand , Nicolas Pitre, and Manish Lachwani have contributed various fixups here as well. This should only require (on boards that don't have a custom uart) registering the uart with KGDB to add any other boards, or using kgdboe it should Just Work. Signed-off-by: Milind Dumbare Signed-off-by: Dmitry Antipov Signed-off-by: Tom Rini Signed-off-by: Deepak Saxena Signed-off-by: Vitaly Wool Signed-off-by: Jason Wessel commit bb7a67bc3edf411e705438fc11c9f15a26d23b14 Author: Jason Wessel Date: Sun Jul 29 09:55:22 2007 -0500 sh-lite.patch Subject: [PATCH] This adds basic support for KGDB on SuperH This patch also adds some architecture specific notes to the DocBook file and converting the 7751 to use this. I have tested all combinations of 8250 and SCI(F) ports being used as KGDB and console that I could (one of each usable to me). The sh-sci.c file needs to be ported forward as it is known to be broken for KGDB... Signed-off-by: Milind Dumbare Signed-off-by: Tom Rini commit 6e7440606107845d9e2d53bf7e47a4a04c7b2aef Author: Jason Wessel Date: Sun Jul 29 09:55:20 2007 -0500 x86_64-lite.patch Subject: [PATCH] This adds support for the x86_64 architecture. An extra annotation is added to the switch_to in order to aid in stack tracing. Signed-off-by: Milind Dumbare Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit a35d9d1d38112543ab8d8c563f89e1ac67b39176 Author: Jason Wessel Date: Sun Jul 29 09:55:19 2007 -0500 x86_64-no_context_hook.patch Subject: [PATCH] This adds a call to notify_die() A call to notify_die is added in the "no context" portion of do_page_fault() as someone on the chain might care and want to do a fixup. Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit fe4f65161b9b46d9e1cf854643067eda33ae3820 Author: Jason Wessel Date: Sun Jul 29 09:55:17 2007 -0500 ia64-lite.patch Subject: [PATCH] This is support of the IA64 arch for KGDB Bob Picco is the primary contributor and maintainer of the IA64 KGDB arch. Signed-off-by: Milind Dumbare Signed-off-by: Bob Picco commit ced711b022982247b4967789b72c6a6960c60e9a Author: Jason Wessel Date: Sun Jul 29 09:55:16 2007 -0500 mips-lite.patch Subject: [PATCH] This adds basic support to the MIPS architecture This patch also adds support for the MIPS Malta (via 8250), SiByte 1250-SWARM (32 and 64bit, custom UART), all from Manish Lachwani, and Toshiba TX493x platforms from Sergei Shtylyov. Signed-off-by: Vitaly Wool Signed-off-by: Geoff Levand Signed-off-by: Sergei Shtylyov Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit 8f4b7521664cc0de10e343d408ed955525293908 Author: Jason Wessel Date: Sun Jul 29 09:55:14 2007 -0500 powerpc-lite.patch Subject: [PATCH] This adds basic KGDB support for both arch/ppc and arch/powerpc. Signed-off-by: Vitaly Wool Signed-off-by: Sergey Shtylyov Signed-off-by: Vitaly Bordug Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit 096dab7e3725c7c493bff7e46b94be957f93e666 Author: Jason Wessel Date: Sun Jul 29 09:55:13 2007 -0500 i386-lite.patch Subject: [PATCH] This adds the basic support for i386. The only real changes outside of new KGDB files and Makefile/related is that for support early on we must set some traps sooner rather than later, but it is safe to always do this. Also, to break in as early as possible, i386 now calls parse_early_param() to explicitly look at anything marked early_param(). We also add a few more notify_die() calls in areas where KGDB needs to take a peek sometimes. Finally, we add some labels to switch_to macros so that when backtracing we can see where we really are. Signed-off-by: Milind Dumbare Signed-off-by: Jason Wessel commit c5ffc00d6f85f35bfd15db745d162c464e2dcfca Author: Jason Wessel Date: Sun Jul 29 09:55:11 2007 -0500 eth.patch Subject: [PATCH] This is the simple KGDB over Ethernet I/O driver that NET_POLL At one point this was very similar to the version Matt Mackall wrote and is currently in Andrew's tree. Since then it has been reworked to fit into the KGDB model. Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit 3ecf38fde114453b9d820ad2c32f5195eb080001 Author: Jason Wessel Date: Sun Jul 29 09:55:10 2007 -0500 netpoll_pass_skb_to_rx_hook.patch Subject: [PATCH] Pass skb via NET_POLL rx routine It will allow kgdboe for example to determine the MAC of the GDB host. Signed-off-by: Jason Wessel commit 7166e7be05ec11f59f5c776b62be4a68539c0f38 Author: Jason Wessel Date: Sun Jul 29 09:55:08 2007 -0500 8250.patch Subject: [PATCH] This is the I/O driver for any 8250-compatible UARTs. This also adds some small hooks into the normal serial core so that we can take away the uart and make sure only KGDB is trying to use it. We also make sure that if KGDB_8250 is enabled, SERIAL_8250_NR_UARTS is set to the default of 4. This is so that if we can always depend on that constant in the 8250 driver for giving our table a size. We add a hook to the main core for unregistering a port with a struct uart_port and add a hook into uart_add_one_port for discovered 8250-like uarts. Signed-off-by: Milind Dumbare Signed-off-by: Dmitry Antipov Signed-off-by: Tom Rini Signed-off-by: Jason Wessel commit 4bcb290f392505d60757520a7a96d1cd924d027c Author: Jason Wessel Date: Sun Jul 29 09:55:07 2007 -0500 core-lite.patch Subject: [PATCH] This is the core of the KGDB stub. It provides all of the common code, documentation and basic Kconfig changes, as well as the common hooks. Signed-off-by: Milind Dumbare Signed-off-by: Bob Picco Signed-off-by: Dmitry Antipov Signed-off-by: Tom Rini Signed-off-by: Jason Wessel Signed-off-by: Andrew Morton --- Documentation/DocBook/kgdb.tmpl | 250 + MAINTAINERS | 9 Makefile | 8 arch/arm/kernel/Makefile | 1 arch/arm/kernel/kgdb-jmp.S | 32 arch/arm/kernel/kgdb.c | 201 + arch/arm/kernel/setup.c | 5 arch/arm/kernel/traps.c | 11 arch/arm/mach-ixp2000/core.c | 4 arch/arm/mach-ixp2000/ixdp2x01.c | 6 arch/arm/mach-ixp4xx/coyote-setup.c | 4 arch/arm/mach-ixp4xx/ixdp425-setup.c | 21 arch/arm/mach-omap1/serial.c | 4 arch/arm/mach-pnx4008/core.c | 4 arch/arm/mach-pxa/Makefile | 1 arch/arm/mach-pxa/kgdb-serial.c | 97 arch/arm/mach-versatile/core.c | 8 arch/arm/mm/extable.c | 7 arch/i386/kernel/Makefile | 5 arch/i386/kernel/kgdb-jmp.S | 74 arch/i386/kernel/kgdb.c | 392 ++ arch/i386/kernel/setup.c | 2 arch/i386/kernel/traps.c | 12 arch/i386/mm/fault.c | 4 arch/ia64/kernel/Makefile | 1 arch/ia64/kernel/kgdb-jmp.S | 240 + arch/ia64/kernel/kgdb.c | 943 +++++ arch/ia64/kernel/smp.c | 17 arch/ia64/kernel/traps.c | 6 arch/ia64/mm/extable.c | 6 arch/ia64/mm/fault.c | 4 arch/mips/Kconfig | 12 arch/mips/Kconfig.debug | 22 arch/mips/au1000/common/Makefile | 1 arch/mips/au1000/common/dbg_io.c | 121 arch/mips/basler/excite/Makefile | 1 arch/mips/basler/excite/excite_dbg_io.c | 121 arch/mips/basler/excite/excite_irq.c | 7 arch/mips/basler/excite/excite_setup.c | 4 arch/mips/ddb5xxx/ddb5477/Makefile | 1 arch/mips/ddb5xxx/ddb5477/kgdb_io.c | 136 arch/mips/gt64120/momenco_ocelot/Makefile | 2 arch/mips/gt64120/momenco_ocelot/dbg_io.c | 121 arch/mips/jmr3927/rbhma3100/Makefile | 1 arch/mips/jmr3927/rbhma3100/kgdb_io.c | 105 arch/mips/kernel/Makefile | 3 arch/mips/kernel/gdb-low.S | 394 -- arch/mips/kernel/gdb-stub.c | 1154 ------ arch/mips/kernel/irq.c | 33 arch/mips/kernel/kgdb-jmp.c | 112 arch/mips/kernel/kgdb-setjmp.S | 28 arch/mips/kernel/kgdb.c | 294 + arch/mips/kernel/kgdb_handler.S | 339 + arch/mips/kernel/traps.c | 9 arch/mips/mips-boards/atlas/Makefile | 1 arch/mips/mips-boards/atlas/atlas_gdb.c | 97 arch/mips/mips-boards/atlas/atlas_setup.c | 7 arch/mips/mips-boards/generic/Makefile | 1 arch/mips/mips-boards/generic/gdb_hook.c | 133 arch/mips/mips-boards/generic/init.c | 62 arch/mips/mips-boards/malta/malta_setup.c | 8 arch/mips/mm/extable.c | 7 arch/mips/pci/fixup-atlas.c | 21 arch/mips/philips/pnx8550/common/Makefile | 1 arch/mips/philips/pnx8550/common/gdb_hook.c | 109 arch/mips/philips/pnx8550/common/setup.c | 11 arch/mips/pmc-sierra/yosemite/Makefile | 1 arch/mips/pmc-sierra/yosemite/dbg_io.c | 180 arch/mips/pmc-sierra/yosemite/irq.c | 9 arch/mips/sgi-ip22/ip22-setup.c | 24 arch/mips/sgi-ip27/Makefile | 1 arch/mips/sgi-ip27/ip27-dbgio.c | 60 arch/mips/sibyte/bcm1480/irq.c | 80 arch/mips/sibyte/cfe/setup.c | 14 arch/mips/sibyte/sb1250/Makefile | 1 arch/mips/sibyte/sb1250/irq.c | 64 arch/mips/sibyte/sb1250/kgdb_sibyte.c | 145 arch/mips/sibyte/swarm/Makefile | 2 arch/mips/sibyte/swarm/dbg_io.c | 76 arch/mips/tx4927/common/Makefile | 1 arch/mips/tx4927/common/tx4927_dbgio.c | 47 arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c | 12 arch/mips/tx4938/common/Makefile | 1 arch/mips/tx4938/common/dbgio.c | 50 arch/mips/tx4938/toshiba_rbtx4938/setup.c | 10 arch/powerpc/Kconfig.debug | 50 arch/powerpc/kernel/Makefile | 6 arch/powerpc/kernel/kgdb.c | 393 ++ arch/powerpc/kernel/kgdb_setjmp32.S | 41 arch/powerpc/kernel/kgdb_setjmp64.S | 77 arch/powerpc/kernel/legacy_serial.c | 6 arch/powerpc/kernel/setup_32.c | 16 arch/powerpc/mm/fault.c | 8 arch/powerpc/platforms/powermac/setup.c | 6 arch/ppc/Kconfig.debug | 38 arch/ppc/amiga/config.c | 6 arch/ppc/kernel/Makefile | 2 arch/ppc/kernel/kgdb.c | 346 + arch/ppc/kernel/kgdb_setjmp32.S | 41 arch/ppc/kernel/ppc-stub.c | 866 ---- arch/ppc/kernel/setup.c | 16 arch/ppc/mm/fault.c | 9 arch/ppc/platforms/4xx/bamboo.c | 23 arch/ppc/platforms/4xx/bubinga.c | 25 arch/ppc/platforms/4xx/ebony.c | 27 arch/ppc/platforms/4xx/luan.c | 13 arch/ppc/platforms/4xx/ocotea.c | 26 arch/ppc/platforms/4xx/taishan.c | 7 arch/ppc/platforms/4xx/xilinx_ml300.c | 5 arch/ppc/platforms/4xx/xilinx_ml403.c | 3 arch/ppc/platforms/4xx/yucca.c | 3 arch/ppc/platforms/83xx/mpc834x_sys.c | 5 arch/ppc/platforms/85xx/mpc8540_ads.c | 7 arch/ppc/platforms/85xx/mpc8560_ads.c | 1 arch/ppc/platforms/85xx/mpc85xx_cds_common.c | 7 arch/ppc/platforms/85xx/sbc8560.c | 38 arch/ppc/platforms/85xx/tqm85xx.c | 7 arch/ppc/platforms/apus_setup.c | 6 arch/ppc/platforms/chestnut.c | 7 arch/ppc/platforms/ev64260.c | 12 arch/ppc/platforms/hdpu.c | 19 arch/ppc/platforms/lopec.c | 3 arch/ppc/platforms/pplus.c | 5 arch/ppc/platforms/radstone_ppc7d.c | 12 arch/ppc/platforms/sandpoint.c | 5 arch/ppc/platforms/spruce.c | 24 arch/ppc/syslib/Makefile | 1 arch/ppc/syslib/gen550.h | 1 arch/ppc/syslib/gen550_kgdb.c | 83 arch/ppc/syslib/ibm44x_common.c | 3 arch/ppc/syslib/mv64x60.c | 61 arch/ppc/syslib/mv64x60_dbg.c | 52 arch/ppc/syslib/ppc4xx_setup.c | 1 arch/ppc/syslib/ppc83xx_setup.c | 24 arch/ppc/syslib/ppc85xx_setup.c | 26 arch/sh/Kconfig.debug | 79 arch/sh/kernel/Makefile | 2 arch/sh/kernel/kgdb-jmp.S | 32 arch/sh/kernel/kgdb.c | 363 + arch/sh/kernel/kgdb_jmp.S | 33 arch/sh/kernel/kgdb_stub.c | 1093 ----- arch/sh/kernel/time.c | 7 arch/sh/kernel/traps.c | 24 arch/sh/mm/extable.c | 7 arch/sh/mm/fault-nommu.c | 14 arch/sh/mm/fault.c | 12 arch/x86_64/Kconfig.debug | 3 arch/x86_64/kernel/Makefile | 1 arch/x86_64/kernel/kgdb-jmp.S | 65 arch/x86_64/kernel/kgdb.c | 468 ++ arch/x86_64/mm/fault.c | 4 drivers/char/keyboard.c | 1 drivers/net/Makefile | 1 drivers/net/kgdboe.c | 294 + drivers/serial/8250.c | 19 drivers/serial/8250_kgdb.c | 515 ++ drivers/serial/Kconfig | 2 drivers/serial/Makefile | 4 drivers/serial/amba-pl011.c | 3 drivers/serial/cpm_uart/Makefile | 1 drivers/serial/cpm_uart/cpm_uart.h | 39 drivers/serial/cpm_uart/cpm_uart_core.c | 109 drivers/serial/cpm_uart/cpm_uart_cpm1.c | 28 drivers/serial/cpm_uart/cpm_uart_cpm2.c | 4 drivers/serial/cpm_uart/cpm_uart_kgdb.c | 185 drivers/serial/mpsc_kgdb.c | 347 + drivers/serial/pl011_kgdb.c | 113 drivers/serial/pxa.c | 5 drivers/serial/serial_core.c | 23 drivers/serial/serial_txx9.c | 13 drivers/serial/serial_txx9_kgdb.c | 152 drivers/serial/sh-sci.c | 23 include/asm-arm/kgdb.h | 103 include/asm-arm/system.h | 36 include/asm-generic/kgdb.h | 103 include/asm-i386/kdebug.h | 1 include/asm-i386/kgdb.h | 51 include/asm-ia64/kdebug.h | 1 include/asm-ia64/kgdb.h | 37 include/asm-mips/asmmacro-32.h | 43 include/asm-mips/asmmacro-64.h | 99 include/asm-mips/kdebug.h | 31 include/asm-mips/kgdb.h | 48 include/asm-mips/ptrace.h | 2 include/asm-powerpc/kgdb.h | 64 include/asm-ppc/kgdb.h | 59 include/asm-ppc/machdep.h | 2 include/asm-ppc/mv64x60.h | 2 include/asm-ppc/mv64x60_defs.h | 3 include/asm-sh/kgdb.h | 100 include/asm-x86_64/kdebug.h | 1 include/asm-x86_64/kgdb.h | 52 include/asm-x86_64/system.h | 6 include/linux/kgdb.h | 289 + include/linux/module.h | 16 include/linux/netpoll.h | 2 include/linux/serial_8250.h | 1 kernel/Makefile | 1 kernel/kgdb.c | 1886 ++++++++++ kernel/module.c | 62 kernel/sched.c | 4 kernel/softlockup.c | 4 kernel/timer.c | 6 lib/Kconfig.debug | 2 lib/Kconfig.kgdb | 256 + net/core/netpoll.c | 3 206 files changed, 10551 insertions(+), 6018 deletions(-) diff -puN Documentation/DocBook/Makefile~git-kgdb Documentation/DocBook/Makefile diff -puN /dev/null Documentation/DocBook/kgdb.tmpl --- /dev/null +++ a/Documentation/DocBook/kgdb.tmpl @@ -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 -puN MAINTAINERS~git-kgdb MAINTAINERS --- a/MAINTAINERS~git-kgdb +++ a/MAINTAINERS @@ -2202,6 +2202,15 @@ L: linux-kernel@vger.kernel.org L: kexec@lists.infradead.org S: Maintained +KGDB +P: Jason Wessel +M: jason.wessel@windriver.com +P: Amit S. Kale +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 -puN Makefile~git-kgdb Makefile --- a/Makefile~git-kgdb +++ a/Makefile @@ -484,10 +484,14 @@ endif # $(dot-config) all: vmlinux ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE -CFLAGS += -Os +COPTIMIZE = -Os else -CFLAGS += -O2 +COPTIMIZE = -O2 endif +# COPTIMIZE may be overridden on the make command line with +# make ... COPTIMIZE="" +# The resulting object may be easier to debug with KGDB +CFLAGS += $(COPTIMIZE) include $(srctree)/arch/$(ARCH)/Makefile diff -puN arch/arm/kernel/Makefile~git-kgdb arch/arm/kernel/Makefile --- a/arch/arm/kernel/Makefile~git-kgdb +++ a/arch/arm/kernel/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.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 -puN /dev/null arch/arm/kernel/kgdb-jmp.S --- /dev/null +++ a/arch/arm/kernel/kgdb-jmp.S @@ -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 -puN /dev/null arch/arm/kernel/kgdb.c --- /dev/null +++ a/arch/arm/kernel/kgdb.c @@ -0,0 +1,201 @@ +/* + * 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 + +/* 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[_CPSR]; +} + +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 = task_pt_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 'D': + case 'k': + 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 /* ! __ARMEB__ */ + .gdb_bpt_instr = {0xe7, 0xff, 0xde, 0xfe} +#endif +}; diff -puN arch/arm/kernel/setup.c~git-kgdb arch/arm/kernel/setup.c --- a/arch/arm/kernel/setup.c~git-kgdb +++ a/arch/arm/kernel/setup.c @@ -837,6 +837,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 -puN arch/arm/kernel/traps.c~git-kgdb arch/arm/kernel/traps.c --- a/arch/arm/kernel/traps.c~git-kgdb +++ a/arch/arm/kernel/traps.c @@ -302,6 +302,7 @@ asmlinkage void __exception do_undefinst unsigned int instr; struct undef_hook *hook; siginfo_t info; + mm_segment_t fs; void __user *pc; unsigned long flags; @@ -312,6 +313,8 @@ asmlinkage void __exception do_undefinst */ regs->ARM_pc -= correction; + fs = get_fs(); + set_fs(KERNEL_DS); pc = (void __user *)instruction_pointer(regs); if (processor_mode(regs) == SVC_MODE) { @@ -321,6 +324,7 @@ asmlinkage void __exception do_undefinst } else { get_user(instr, (u32 __user *)pc); } + set_fs(fs); spin_lock_irqsave(&undef_lock, flags); list_for_each_entry(hook, &undef_hook, node) { @@ -706,6 +710,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 -puN arch/arm/mach-ixp2000/core.c~git-kgdb arch/arm/mach-ixp2000/core.c --- a/arch/arm/mach-ixp2000/core.c~git-kgdb +++ a/arch/arm/mach-ixp2000/core.c @@ -25,6 +25,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_platform_port(0, &ixp2000_serial_port); +#endif } diff -puN arch/arm/mach-ixp2000/ixdp2x01.c~git-kgdb arch/arm/mach-ixp2000/ixdp2x01.c --- a/arch/arm/mach-ixp2000/ixdp2x01.c~git-kgdb +++ a/arch/arm/mach-ixp2000/ixdp2x01.c @@ -30,6 +30,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_platform_port(0, ixdp2x01_serial_port1); + kgdb8250_add_platform_port(1, ixdp2x01_serial_port1); +#endif } diff -puN arch/arm/mach-ixp4xx/coyote-setup.c~git-kgdb arch/arm/mach-ixp4xx/coyote-setup.c --- a/arch/arm/mach-ixp4xx/coyote-setup.c~git-kgdb +++ a/arch/arm/mach-ixp4xx/coyote-setup.c @@ -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_platform_port(0, &coyote_uart_data); +#endif } #ifdef CONFIG_ARCH_ADI_COYOTE diff -puN arch/arm/mach-ixp4xx/ixdp425-setup.c~git-kgdb arch/arm/mach-ixp4xx/ixdp425-setup.c --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c~git-kgdb +++ a/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -152,7 +153,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, @@ -161,7 +163,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, @@ -219,12 +222,22 @@ static void __init ixdp425_init(void) platform_add_devices(ixdp425_devices, ARRAY_SIZE(ixdp425_devices)); } +static void __init ixdp425_map_io(void) +{ + ixp4xx_map_io(); + +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(0, &ixdp425_uart_data[0]); + kgdb8250_add_platform_port(1, &ixdp425_uart_data[1]); +#endif +} + #ifdef CONFIG_ARCH_IXDP425 MACHINE_START(IXDP425, "Intel IXDP425 Development Platform") /* Maintainer: MontaVista Software, Inc. */ .phys_io = IXP4XX_PERIPHERAL_BASE_PHYS, .io_pg_offst = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc, - .map_io = ixp4xx_map_io, + .map_io = ixdp425_map_io, .init_irq = ixp4xx_init_irq, .timer = &ixp4xx_timer, .boot_params = 0x0100, @@ -237,7 +250,7 @@ MACHINE_START(IXDP465, "Intel IXDP465 De /* Maintainer: MontaVista Software, Inc. */ .phys_io = IXP4XX_PERIPHERAL_BASE_PHYS, .io_pg_offst = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc, - .map_io = ixp4xx_map_io, + .map_io = ixdp425_map_io, .init_irq = ixp4xx_init_irq, .timer = &ixp4xx_timer, .boot_params = 0x0100, diff -puN arch/arm/mach-omap1/serial.c~git-kgdb arch/arm/mach-omap1/serial.c --- a/arch/arm/mach-omap1/serial.c~git-kgdb +++ a/arch/arm/mach-omap1/serial.c @@ -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 -puN arch/arm/mach-pnx4008/core.c~git-kgdb arch/arm/mach-pnx4008/core.c --- a/arch/arm/mach-pnx4008/core.c~git-kgdb +++ a/arch/arm/mach-pnx4008/core.c @@ -224,6 +224,10 @@ static void __init pnx4008_init(void) spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); /* Switch on the UART clocks */ pnx4008_uart_init(); +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(0, &platform_serial_ports[0]); + kgdb8250_add_platform_port(1, &platform_serial_ports[1]); +#endif } static struct map_desc pnx4008_io_desc[] __initdata = { diff -puN arch/arm/mach-pxa/Makefile~git-kgdb arch/arm/mach-pxa/Makefile --- a/arch/arm/mach-pxa/Makefile~git-kgdb +++ a/arch/arm/mach-pxa/Makefile @@ -32,6 +32,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 -puN /dev/null arch/arm/mach-pxa/kgdb-serial.c --- /dev/null +++ a/arch/arm/mach-pxa/kgdb-serial.c @@ -0,0 +1,97 @@ +/* + * 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 + +#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(u8 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 -puN arch/arm/mach-versatile/core.c~git-kgdb arch/arm/mach-versatile/core.c --- a/arch/arm/mach-versatile/core.c~git-kgdb +++ a/arch/arm/mach-versatile/core.c @@ -184,6 +184,14 @@ static struct map_desc versatile_io_desc .type = MT_DEVICE }, #endif +#ifdef CONFIG_KGDB_AMBA_PL011 + { + .virtual = IO_ADDRESS(CONFIG_KGDB_AMBA_BASE), + .pfn = __phys_to_pfn(CONFIG_KGDB_AMBA_BASE), + .length = SZ_4K, + .type = MT_DEVICE + }, +#endif #ifdef CONFIG_PCI { .virtual = IO_ADDRESS(VERSATILE_PCI_CORE_BASE), diff -puN arch/arm/mm/extable.c~git-kgdb arch/arm/mm/extable.c --- a/arch/arm/mm/extable.c~git-kgdb +++ a/arch/arm/mm/extable.c @@ -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 -puN arch/i386/kernel/Makefile~git-kgdb arch/i386/kernel/Makefile --- a/arch/i386/kernel/Makefile~git-kgdb +++ a/arch/i386/kernel/Makefile @@ -36,6 +36,11 @@ obj-y += sysenter.o vsyscall.o obj-$(CONFIG_ACPI_SRAT) += srat.o obj-$(CONFIG_EFI) += efi.o efi_stub.o obj-$(CONFIG_DOUBLEFAULT) += doublefault.o +<<<<<<< HEAD/arch/i386/kernel/Makefile +======= +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o +obj-$(CONFIG_SERIAL_8250) += legacy_serial.o +>>>>>>> /arch/i386/kernel/Makefile obj-$(CONFIG_VM86) += vm86.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_HPET_TIMER) += hpet.o diff -puN /dev/null arch/i386/kernel/kgdb-jmp.S --- /dev/null +++ a/arch/i386/kernel/kgdb-jmp.S @@ -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 -puN /dev/null arch/i386/kernel/kgdb.c --- /dev/null +++ a/arch/i386/kernel/kgdb.c @@ -0,0 +1,392 @@ +/* + * + * 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. + * Copyright (C) 2007 Wind River Systems, Inc. + * Copyright (C) 2007 MontaVista Software, Inc. + */ +/* + * Contributor: Lake Stevens Instrument Division$ + * Written by: Glenn Engel $ + * Updated by: Amit Kale + * Updated by: Tom Rini + * Updated by: Jason Wessel + * 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 }, +}; + +static void kgdb_correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned long dr7; + + get_debugreg(dr7, 7); + 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: + set_debugreg(breakinfo[breakno].addr, 0); + break; + + case 1: + set_debugreg(breakinfo[breakno].addr, 1); + break; + + case 2: + set_debugreg(breakinfo[breakno].addr, 2); + break; + + case 3: + set_debugreg(breakinfo[breakno].addr, 3); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) + set_debugreg(dr7, 7); +} + +static int kgdb_remove_hw_break(unsigned long addr, int len, + enum kgdb_bptype bptype) +{ + 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; +} + +static void kgdb_remove_all_hw_break(void) +{ + int i; + + for (i = 0; i < 4; i++) { + memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); + } +} + +static int kgdb_set_hw_break(unsigned long addr, int len, + enum kgdb_bptype bptype) +{ + int i, idx = -1; + for (i = 0; i < 4; i++) { + if (!breakinfo[i].enabled) { + idx = i; + break; + } + } + if (idx == -1) + return -1; + if (bptype == bp_hardware_breakpoint) { + breakinfo[idx].type = 0; + breakinfo[idx].len = 0; + } else if (bptype == bp_write_watchpoint) { + breakinfo[idx].type = 1; + if (len == 1 || len == 2 || len == 4) + breakinfo[idx].len = len - 1; + else + return -1; + } else if (bptype == bp_access_watchpoint) { + breakinfo[idx].type = 3; + if (len == 1 || len == 2 || len == 4) + breakinfo[idx].len = len - 1; + else + return -1; + } else + return -1; + breakinfo[idx].enabled = 1; + breakinfo[idx].addr = addr; + return 0; +} + +void kgdb_disable_hw_debug(struct pt_regs *regs) +{ + /* Disable hardware debugging while we are in kgdb */ + set_debugreg(0, 7); +} + +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; +} + +#ifdef CONFIG_SMP +void kgdb_roundup_cpus(unsigned long flags) +{ + send_IPI_allbutself(APIC_DM_NMI); +} +#endif + +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, + raw_smp_processor_id()); + } + + get_debugreg(dr6, 6); + 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; + } + } + } + set_debugreg(0, 6); + kgdb_correct_hw_break(); + + 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(raw_smp_processor_id(), regs); + return NOTIFY_STOP; + } else if (cmd == DIE_DEBUG + && atomic_read(&cpu_doing_single_step) == + raw_smp_processor_id() && user_mode(regs)) { + /* single step exception from kernel space to user space so + * eat the exception and continue the process + */ + printk(KERN_ERR "KGDB: trap/step from kernel to user" + " space, resuming...\n"); + kgdb_arch_handle_exception(args->trapnr, args->signr, + args->err, "c", "", 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) +{ + register_die_notifier(&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; +} + +unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs) +{ + if (exception == 3) { + return instruction_pointer(regs) - 1; + } + return instruction_pointer(regs); +} + +struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = {0xcc}, + .flags = KGDB_HW_BREAKPOINT, + .set_hw_breakpoint = kgdb_set_hw_break, + .remove_hw_breakpoint = kgdb_remove_hw_break, + .remove_all_hw_break = kgdb_remove_all_hw_break, + .correct_hw_break = kgdb_correct_hw_break, +}; diff -puN arch/i386/kernel/setup.c~git-kgdb arch/i386/kernel/setup.c --- a/arch/i386/kernel/setup.c~git-kgdb +++ a/arch/i386/kernel/setup.c @@ -115,6 +115,7 @@ EXPORT_SYMBOL(ist_info); #endif extern void early_cpu_init(void); +extern void early_trap_init(void); extern int root_mountflags; unsigned long saved_videomode; @@ -509,6 +510,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 diff -puN arch/i386/kernel/traps.c~git-kgdb arch/i386/kernel/traps.c --- a/arch/i386/kernel/traps.c~git-kgdb +++ a/arch/i386/kernel/traps.c @@ -881,6 +881,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: @@ -1146,6 +1147,12 @@ static void __init set_task_gate(unsigne _set_gate(n, DESCTYPE_TASK, (void *)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) { @@ -1162,10 +1169,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); @@ -1175,7 +1180,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 -puN arch/i386/mm/fault.c~git-kgdb arch/i386/mm/fault.c --- a/arch/i386/mm/fault.c~git-kgdb +++ a/arch/i386/mm/fault.c @@ -506,6 +506,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 -puN arch/ia64/kernel/Makefile~git-kgdb arch/ia64/kernel/Makefile --- a/arch/ia64/kernel/Makefile~git-kgdb +++ a/arch/ia64/kernel/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_AUDIT) += audit.o obj-$(CONFIG_PCI_MSI) += msi_ia64.o mca_recovery-y += mca_drv.o mca_drv_asm.o obj-$(CONFIG_IA64_MC_ERR_INJECT)+= err_inject.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o obj-$(CONFIG_IA64_ESI) += esi.o ifneq ($(CONFIG_IA64_ESI),) diff -puN /dev/null arch/ia64/kernel/kgdb-jmp.S --- /dev/null +++ a/arch/ia64/kernel/kgdb-jmp.S @@ -0,0 +1,240 @@ +/* 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 -puN /dev/null arch/ia64/kernel/kgdb.c --- /dev/null +++ a/arch/ia64/kernel/kgdb.c @@ -0,0 +1,943 @@ +/* + * + * 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, 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 +#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; + +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); +} + +volatile static struct smp_unw { + struct unw_frame_info *unw; + struct task_struct *task; +} smp_unw[NR_CPUS]; + +static inline int 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 inline void 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 inline void 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) +{ + register_die_notifier(&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 inline void 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(KERN_ERR "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()); + } + + /* 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 = { + .gdb_bpt_instr = {0xcc}, +}; diff -puN arch/ia64/kernel/smp.c~git-kgdb arch/ia64/kernel/smp.c --- a/arch/ia64/kernel/smp.c~git-kgdb +++ a/arch/ia64/kernel/smp.c @@ -48,6 +48,7 @@ #include #include #include +#include /* * Note: alignment of 4 entries/cacheline was empirically determined @@ -79,6 +80,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 #define IPI_KDUMP_CPU_STOP 3 /* This needs to be cacheline aligned because it is written to by *other* CPUs. */ @@ -169,6 +173,11 @@ handle_IPI (int irq, void *dev_id) case IPI_CPU_STOP: stop_this_cpu(); break; +#ifdef CONFIG_KGDB + case IPI_KGDB_INTERRUPT: + kgdb_wait_ipi(get_irq_regs()); + break; +#endif #ifdef CONFIG_KEXEC case IPI_KDUMP_CPU_STOP: unw_init_running(kdump_cpu_freeze, NULL); @@ -401,6 +410,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 -puN arch/ia64/kernel/traps.c~git-kgdb arch/ia64/kernel/traps.c --- a/arch/ia64/kernel/traps.c~git-kgdb +++ a/arch/ia64/kernel/traps.c @@ -155,8 +155,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 -puN arch/ia64/mm/extable.c~git-kgdb arch/ia64/mm/extable.c --- a/arch/ia64/mm/extable.c~git-kgdb +++ a/arch/ia64/mm/extable.c @@ -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 -puN arch/ia64/mm/fault.c~git-kgdb arch/ia64/mm/fault.c --- a/arch/ia64/mm/fault.c~git-kgdb +++ a/arch/ia64/mm/fault.c @@ -254,6 +254,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 -puN arch/mips/Kconfig~git-kgdb arch/mips/Kconfig --- a/arch/mips/Kconfig~git-kgdb +++ a/arch/mips/Kconfig @@ -53,7 +53,6 @@ config BASLER_EXCITE select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_BIG_ENDIAN - select SYS_SUPPORTS_KGDB help The eXcite is a smart camera platform manufactured by Basler Vision Technologies AG. @@ -261,7 +260,6 @@ config MOMENCO_OCELOT select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_BIG_ENDIAN - select SYS_SUPPORTS_KGDB help The Ocelot is a MIPS-based Single Board Computer (SBC) made by Momentum Computer . @@ -286,8 +284,6 @@ config DDB5477 select SYS_HAS_CPU_R5432 select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_64BIT_KERNEL if EXPERIMENTAL - select SYS_SUPPORTS_KGDB - select SYS_SUPPORTS_KGDB select SYS_SUPPORTS_LITTLE_ENDIAN help This enables support for the R5432-based NEC DDB Vrc-5477, @@ -312,7 +308,6 @@ config PMC_MSP select SYS_HAS_CPU_MIPS32_R2 select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_BIG_ENDIAN - select SYS_SUPPORTS_KGDB select IRQ_CPU select SERIAL_8250 select SERIAL_8250_CONSOLE @@ -336,7 +331,6 @@ config PMC_YOSEMITE select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_BIG_ENDIAN select SYS_SUPPORTS_HIGHMEM - select SYS_SUPPORTS_KGDB select SYS_SUPPORTS_SMP help Yosemite is an evaluation board for the RM9000x2 processor @@ -416,7 +410,6 @@ config SGI_IP27 select SYS_HAS_CPU_R10000 select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_BIG_ENDIAN - select SYS_SUPPORTS_KGDB select SYS_SUPPORTS_NUMA select SYS_SUPPORTS_SMP select GENERIC_HARDIRQS_NO__DO_IRQ @@ -465,7 +458,6 @@ config SIBYTE_SWARM select SYS_HAS_CPU_SB1 select SYS_SUPPORTS_BIG_ENDIAN select SYS_SUPPORTS_HIGHMEM - select SYS_SUPPORTS_KGDB select SYS_SUPPORTS_LITTLE_ENDIAN config SIBYTE_SENTOSA @@ -606,7 +598,6 @@ config TOSHIBA_RBTX4927 select SYS_SUPPORTS_64BIT_KERNEL select SYS_SUPPORTS_LITTLE_ENDIAN select SYS_SUPPORTS_BIG_ENDIAN - select SYS_SUPPORTS_KGDB select GENERIC_HARDIRQS_NO__DO_IRQ help This Toshiba board is based on the TX4927 processor. Say Y here to @@ -625,7 +616,6 @@ config TOSHIBA_RBTX4938 select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_LITTLE_ENDIAN select SYS_SUPPORTS_BIG_ENDIAN - select SYS_SUPPORTS_KGDB select GENERIC_HARDIRQS_NO__DO_IRQ select GENERIC_GPIO help @@ -809,7 +799,6 @@ config IRQ_MSP_CIC config DDB5XXX_COMMON bool - select SYS_SUPPORTS_KGDB config MIPS_BOARDS_GEN bool @@ -840,7 +829,6 @@ config SOC_PNX8550 select SYS_HAS_EARLY_PRINTK select SYS_SUPPORTS_32BIT_KERNEL select GENERIC_HARDIRQS_NO__DO_IRQ - select SYS_SUPPORTS_KGDB select GENERIC_GPIO config SWAP_IO_SPACE diff -puN arch/mips/Kconfig.debug~git-kgdb arch/mips/Kconfig.debug --- a/arch/mips/Kconfig.debug~git-kgdb +++ a/arch/mips/Kconfig.debug @@ -46,28 +46,6 @@ config SMTC_IDLE_HOOK_DEBUG arch/mips/kernel/smtc.c. This debugging option result in significant overhead so should be disabled in production kernels. -config KGDB - bool "Remote GDB kernel debugging" - depends on DEBUG_KERNEL && SYS_SUPPORTS_KGDB - 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 SYS_SUPPORTS_KGDB - bool - -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 -puN arch/mips/au1000/common/Makefile~git-kgdb arch/mips/au1000/common/Makefile --- a/arch/mips/au1000/common/Makefile~git-kgdb +++ a/arch/mips/au1000/common/Makefile @@ -10,5 +10,4 @@ obj-y += prom.o irq.o puts.o time.o rese au1xxx_irqmap.o clocks.o platform.o power.o setup.o \ sleeper.o cputable.o dma.o dbdma.o gpio.o -obj-$(CONFIG_KGDB) += dbg_io.o obj-$(CONFIG_PCI) += pci.o diff -puN arch/mips/au1000/common/dbg_io.c~git-kgdb /dev/null --- a/arch/mips/au1000/common/dbg_io.c +++ /dev/null @@ -1,121 +0,0 @@ - -#include -#include - -#ifdef CONFIG_KGDB - -/* - * FIXME the user should be able to select the - * uart to be used for debugging. - */ -#define DEBUG_BASE UART_DEBUG_BASE -/**/ - -/* we need uint32 uint8 */ -/* #include "types.h" */ -typedef unsigned char uint8; -typedef unsigned int uint32; - -#define UART16550_BAUD_2400 2400 -#define UART16550_BAUD_4800 4800 -#define UART16550_BAUD_9600 9600 -#define UART16550_BAUD_19200 19200 -#define UART16550_BAUD_38400 38400 -#define UART16550_BAUD_57600 57600 -#define UART16550_BAUD_115200 115200 - -#define UART16550_PARITY_NONE 0 -#define UART16550_PARITY_ODD 0x08 -#define UART16550_PARITY_EVEN 0x18 -#define UART16550_PARITY_MARK 0x28 -#define UART16550_PARITY_SPACE 0x38 - -#define UART16550_DATA_5BIT 0x0 -#define UART16550_DATA_6BIT 0x1 -#define UART16550_DATA_7BIT 0x2 -#define UART16550_DATA_8BIT 0x3 - -#define UART16550_STOP_1BIT 0x0 -#define UART16550_STOP_2BIT 0x4 - - -#define UART_RX 0 /* Receive buffer */ -#define UART_TX 4 /* Transmit buffer */ -#define UART_IER 8 /* Interrupt Enable Register */ -#define UART_IIR 0xC /* Interrupt ID Register */ -#define UART_FCR 0x10 /* FIFO Control Register */ -#define UART_LCR 0x14 /* Line Control Register */ -#define UART_MCR 0x18 /* Modem Control Register */ -#define UART_LSR 0x1C /* Line Status Register */ -#define UART_MSR 0x20 /* Modem Status Register */ -#define UART_CLK 0x28 /* Baud Rat4e Clock Divider */ -#define UART_MOD_CNTRL 0x100 /* Module Control */ - -/* memory-mapped read/write of the port */ -#define UART16550_READ(y) (au_readl(DEBUG_BASE + y) & 0xff) -#define UART16550_WRITE(y,z) (au_writel(z&0xff, DEBUG_BASE + y)) - -extern unsigned long get_au1x00_uart_baud_base(void); -extern unsigned long cal_r4koff(void); - -void debugInit(uint32 baud, uint8 data, uint8 parity, uint8 stop) -{ - - if (UART16550_READ(UART_MOD_CNTRL) != 0x3) { - UART16550_WRITE(UART_MOD_CNTRL, 3); - } - cal_r4koff(); - - /* disable interrupts */ - UART16550_WRITE(UART_IER, 0); - - /* set up baud rate */ - { - uint32 divisor; - - /* set divisor */ - divisor = get_au1x00_uart_baud_base() / baud; - UART16550_WRITE(UART_CLK, divisor & 0xffff); - } - - /* set data format */ - UART16550_WRITE(UART_LCR, (data | parity | stop)); -} - -static int remoteDebugInitialized = 0; - -uint8 getDebugChar(void) -{ - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(UART16550_BAUD_115200, - UART16550_DATA_8BIT, - UART16550_PARITY_NONE, - UART16550_STOP_1BIT); - } - - while((UART16550_READ(UART_LSR) & 0x1) == 0); - return UART16550_READ(UART_RX); -} - - -int putDebugChar(uint8 byte) -{ -// int i; - - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(UART16550_BAUD_115200, - UART16550_DATA_8BIT, - UART16550_PARITY_NONE, - UART16550_STOP_1BIT); - } - - while ((UART16550_READ(UART_LSR)&0x40) == 0); - UART16550_WRITE(UART_TX, byte); - //for (i=0;i<0xfff;i++); - - return 1; -} - -#endif diff -puN arch/mips/basler/excite/Makefile~git-kgdb arch/mips/basler/excite/Makefile --- a/arch/mips/basler/excite/Makefile~git-kgdb +++ a/arch/mips/basler/excite/Makefile @@ -5,5 +5,4 @@ obj-$(CONFIG_BASLER_EXCITE) += excite_irq.o excite_prom.o excite_setup.o \ excite_device.o excite_procfs.o -obj-$(CONFIG_KGDB) += excite_dbg_io.o obj-m += excite_iodev.o diff -puN arch/mips/basler/excite/excite_dbg_io.c~git-kgdb /dev/null --- a/arch/mips/basler/excite/excite_dbg_io.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2004 by Basler Vision Technologies AG - * Author: Thomas Koeller - * - * 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. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_SERIAL_8250) && CONFIG_SERIAL_8250_NR_UARTS > 1 -#error Debug port used by serial driver -#endif - -#define UART_CLK 25000000 -#define BASE_BAUD (UART_CLK / 16) -#define REGISTER_BASE_0 0x0208UL -#define REGISTER_BASE_1 0x0238UL - -#define REGISTER_BASE_DBG REGISTER_BASE_1 - -#define CPRR 0x0004 -#define UACFG 0x0200 -#define UAINTS 0x0204 -#define UARBR (REGISTER_BASE_DBG + 0x0000) -#define UATHR (REGISTER_BASE_DBG + 0x0004) -#define UADLL (REGISTER_BASE_DBG + 0x0008) -#define UAIER (REGISTER_BASE_DBG + 0x000c) -#define UADLH (REGISTER_BASE_DBG + 0x0010) -#define UAIIR (REGISTER_BASE_DBG + 0x0014) -#define UAFCR (REGISTER_BASE_DBG + 0x0018) -#define UALCR (REGISTER_BASE_DBG + 0x001c) -#define UAMCR (REGISTER_BASE_DBG + 0x0020) -#define UALSR (REGISTER_BASE_DBG + 0x0024) -#define UAMSR (REGISTER_BASE_DBG + 0x0028) -#define UASCR (REGISTER_BASE_DBG + 0x002c) - -#define PARITY_NONE 0 -#define PARITY_ODD 0x08 -#define PARITY_EVEN 0x18 -#define PARITY_MARK 0x28 -#define PARITY_SPACE 0x38 - -#define DATA_5BIT 0x0 -#define DATA_6BIT 0x1 -#define DATA_7BIT 0x2 -#define DATA_8BIT 0x3 - -#define STOP_1BIT 0x0 -#define STOP_2BIT 0x4 - -#define BAUD_DBG 57600 -#define PARITY_DBG PARITY_NONE -#define DATA_DBG DATA_8BIT -#define STOP_DBG STOP_1BIT - -/* Initialize the serial port for KGDB debugging */ -void __init excite_kgdb_init(void) -{ - const u32 divisor = BASE_BAUD / BAUD_DBG; - - /* Take the UART out of reset */ - titan_writel(0x00ff1cff, CPRR); - titan_writel(0x00000000, UACFG); - titan_writel(0x00000002, UACFG); - - titan_writel(0x0, UALCR); - titan_writel(0x0, UAIER); - - /* Disable FIFOs */ - titan_writel(0x00, UAFCR); - - titan_writel(0x80, UALCR); - titan_writel(divisor & 0xff, UADLL); - titan_writel((divisor & 0xff00) >> 8, UADLH); - titan_writel(0x0, UALCR); - - titan_writel(DATA_DBG | PARITY_DBG | STOP_DBG, UALCR); - - /* Enable receiver interrupt */ - titan_readl(UARBR); - titan_writel(0x1, UAIER); -} - -int getDebugChar(void) -{ - while (!(titan_readl(UALSR) & 0x1)); - return titan_readl(UARBR); -} - -int putDebugChar(int data) -{ - while (!(titan_readl(UALSR) & 0x20)); - titan_writel(data, UATHR); - return 1; -} - -/* KGDB interrupt handler */ -asmlinkage void excite_kgdb_inthdl(void) -{ - if (unlikely( - ((titan_readl(UAIIR) & 0x7) == 4) - && ((titan_readl(UARBR) & 0xff) == 0x3))) - set_async_breakpoint(®s->cp0_epc); -} diff -puN arch/mips/basler/excite/excite_irq.c~git-kgdb arch/mips/basler/excite/excite_irq.c --- a/arch/mips/basler/excite/excite_irq.c~git-kgdb +++ a/arch/mips/basler/excite/excite_irq.c @@ -50,10 +50,6 @@ void __init arch_init_irq(void) mips_cpu_irq_init(); rm7k_cpu_irq_init(); rm9k_cpu_irq_init(); - -#ifdef CONFIG_KGDB - excite_kgdb_init(); -#endif } asmlinkage void plat_irq_dispatch(void) @@ -90,9 +86,6 @@ asmlinkage void plat_irq_dispatch(void) msgint = msgintflags & msgintmask & (0x1 << (TITAN_MSGINT % 0x20)); if ((pending & (1 << TITAN_IRQ)) && msgint) { ocd_writel(msgint, INTP0Clear0 + (TITAN_MSGINT / 0x20 * 0x10)); -#if defined(CONFIG_KGDB) - excite_kgdb_inthdl(); -#endif do_IRQ(TITAN_IRQ); return; } diff -puN arch/mips/basler/excite/excite_setup.c~git-kgdb arch/mips/basler/excite/excite_setup.c --- a/arch/mips/basler/excite/excite_setup.c~git-kgdb +++ a/arch/mips/basler/excite/excite_setup.c @@ -96,13 +96,13 @@ static int __init excite_init_console(vo /* Take the DUART out of reset */ titan_writel(0x00ff1cff, CPRR); -#if defined(CONFIG_KGDB) || (CONFIG_SERIAL_8250_NR_UARTS > 1) +#if (CONFIG_SERIAL_8250_NR_UARTS > 1) /* Enable both ports */ titan_writel(MASK_SER0 | MASK_SER1, UACFG); #else /* Enable port #0 only */ titan_writel(MASK_SER0, UACFG); -#endif /* defined(CONFIG_KGDB) */ +#endif /* * Set up serial port #0. Do not use autodetection; the result is diff -puN arch/mips/ddb5xxx/ddb5477/Makefile~git-kgdb arch/mips/ddb5xxx/ddb5477/Makefile --- a/arch/mips/ddb5xxx/ddb5477/Makefile~git-kgdb +++ a/arch/mips/ddb5xxx/ddb5477/Makefile @@ -6,4 +6,3 @@ obj-y += ddb5477-platform.o irq.o irq lcd44780.o obj-$(CONFIG_RUNTIME_DEBUG) += debug.o -obj-$(CONFIG_KGDB) += kgdb_io.o diff -puN arch/mips/ddb5xxx/ddb5477/kgdb_io.c~git-kgdb /dev/null --- a/arch/mips/ddb5xxx/ddb5477/kgdb_io.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * kgdb io functions for DDB5477. We use the second serial port (upper one). - * - * Copyright (C) 2001 MontaVista Software Inc. - * Author: 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. - * - */ - -/* ======================= CONFIG ======================== */ - -/* [jsun] we use the second serial port for kdb */ -#define BASE 0xbfa04240 -#define MAX_BAUD 115200 - -/* distance in bytes between two serial registers */ -#define REG_OFFSET 8 - -/* - * 0 - kgdb does serial init - * 1 - kgdb skip serial init - */ -static int remoteDebugInitialized = 0; - -/* - * the default baud rate *if* kgdb does serial init - */ -#define BAUD_DEFAULT UART16550_BAUD_38400 - -/* ======================= END OF CONFIG ======================== */ - -typedef unsigned char uint8; -typedef unsigned int uint32; - -#define UART16550_BAUD_2400 2400 -#define UART16550_BAUD_4800 4800 -#define UART16550_BAUD_9600 9600 -#define UART16550_BAUD_19200 19200 -#define UART16550_BAUD_38400 38400 -#define UART16550_BAUD_57600 57600 -#define UART16550_BAUD_115200 115200 - -#define UART16550_PARITY_NONE 0 -#define UART16550_PARITY_ODD 0x08 -#define UART16550_PARITY_EVEN 0x18 -#define UART16550_PARITY_MARK 0x28 -#define UART16550_PARITY_SPACE 0x38 - -#define UART16550_DATA_5BIT 0x0 -#define UART16550_DATA_6BIT 0x1 -#define UART16550_DATA_7BIT 0x2 -#define UART16550_DATA_8BIT 0x3 - -#define UART16550_STOP_1BIT 0x0 -#define UART16550_STOP_2BIT 0x4 - -/* register offset */ -#define OFS_RCV_BUFFER 0 -#define OFS_TRANS_HOLD 0 -#define OFS_SEND_BUFFER 0 -#define OFS_INTR_ENABLE (1*REG_OFFSET) -#define OFS_INTR_ID (2*REG_OFFSET) -#define OFS_DATA_FORMAT (3*REG_OFFSET) -#define OFS_LINE_CONTROL (3*REG_OFFSET) -#define OFS_MODEM_CONTROL (4*REG_OFFSET) -#define OFS_RS232_OUTPUT (4*REG_OFFSET) -#define OFS_LINE_STATUS (5*REG_OFFSET) -#define OFS_MODEM_STATUS (6*REG_OFFSET) -#define OFS_RS232_INPUT (6*REG_OFFSET) -#define OFS_SCRATCH_PAD (7*REG_OFFSET) - -#define OFS_DIVISOR_LSB (0*REG_OFFSET) -#define OFS_DIVISOR_MSB (1*REG_OFFSET) - - -/* memory-mapped read/write of the port */ -#define UART16550_READ(y) (*((volatile uint8*)(BASE + y))) -#define UART16550_WRITE(y, z) ((*((volatile uint8*)(BASE + y))) = z) - -void debugInit(uint32 baud, uint8 data, uint8 parity, uint8 stop) -{ - /* disable interrupts */ - UART16550_WRITE(OFS_INTR_ENABLE, 0); - - /* set up baud rate */ - { - uint32 divisor; - - /* set DIAB bit */ - UART16550_WRITE(OFS_LINE_CONTROL, 0x80); - - /* set divisor */ - divisor = MAX_BAUD / baud; - UART16550_WRITE(OFS_DIVISOR_LSB, divisor & 0xff); - UART16550_WRITE(OFS_DIVISOR_MSB, (divisor & 0xff00) >> 8); - - /* clear DIAB bit */ - UART16550_WRITE(OFS_LINE_CONTROL, 0x0); - } - - /* set data format */ - UART16550_WRITE(OFS_DATA_FORMAT, data | parity | stop); -} - - -uint8 getDebugChar(void) -{ - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(BAUD_DEFAULT, - UART16550_DATA_8BIT, - UART16550_PARITY_NONE, UART16550_STOP_1BIT); - } - - while ((UART16550_READ(OFS_LINE_STATUS) & 0x1) == 0); - return UART16550_READ(OFS_RCV_BUFFER); -} - - -int putDebugChar(uint8 byte) -{ - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(BAUD_DEFAULT, - UART16550_DATA_8BIT, - UART16550_PARITY_NONE, UART16550_STOP_1BIT); - } - - while ((UART16550_READ(OFS_LINE_STATUS) & 0x20) == 0); - UART16550_WRITE(OFS_SEND_BUFFER, byte); - return 1; -} diff -puN arch/mips/gt64120/momenco_ocelot/Makefile~git-kgdb arch/mips/gt64120/momenco_ocelot/Makefile --- a/arch/mips/gt64120/momenco_ocelot/Makefile~git-kgdb +++ a/arch/mips/gt64120/momenco_ocelot/Makefile @@ -3,5 +3,3 @@ # obj-y += irq.o ocelot-platform.o prom.o reset.o setup.o - -obj-$(CONFIG_KGDB) += dbg_io.o diff -puN arch/mips/gt64120/momenco_ocelot/dbg_io.c~git-kgdb /dev/null --- a/arch/mips/gt64120/momenco_ocelot/dbg_io.c +++ /dev/null @@ -1,121 +0,0 @@ - -#include /* For the serial port location and base baud */ - -/* --- CONFIG --- */ - -typedef unsigned char uint8; -typedef unsigned int uint32; - -/* --- END OF CONFIG --- */ - -#define UART16550_BAUD_2400 2400 -#define UART16550_BAUD_4800 4800 -#define UART16550_BAUD_9600 9600 -#define UART16550_BAUD_19200 19200 -#define UART16550_BAUD_38400 38400 -#define UART16550_BAUD_57600 57600 -#define UART16550_BAUD_115200 115200 - -#define UART16550_PARITY_NONE 0 -#define UART16550_PARITY_ODD 0x08 -#define UART16550_PARITY_EVEN 0x18 -#define UART16550_PARITY_MARK 0x28 -#define UART16550_PARITY_SPACE 0x38 - -#define UART16550_DATA_5BIT 0x0 -#define UART16550_DATA_6BIT 0x1 -#define UART16550_DATA_7BIT 0x2 -#define UART16550_DATA_8BIT 0x3 - -#define UART16550_STOP_1BIT 0x0 -#define UART16550_STOP_2BIT 0x4 - -/* ----------------------------------------------------- */ - -/* === CONFIG === */ - -/* [jsun] we use the second serial port for kdb */ -#define BASE OCELOT_SERIAL1_BASE -#define MAX_BAUD OCELOT_BASE_BAUD - -/* === END OF CONFIG === */ - -#define REG_OFFSET 4 - -/* register offset */ -#define OFS_RCV_BUFFER 0 -#define OFS_TRANS_HOLD 0 -#define OFS_SEND_BUFFER 0 -#define OFS_INTR_ENABLE (1*REG_OFFSET) -#define OFS_INTR_ID (2*REG_OFFSET) -#define OFS_DATA_FORMAT (3*REG_OFFSET) -#define OFS_LINE_CONTROL (3*REG_OFFSET) -#define OFS_MODEM_CONTROL (4*REG_OFFSET) -#define OFS_RS232_OUTPUT (4*REG_OFFSET) -#define OFS_LINE_STATUS (5*REG_OFFSET) -#define OFS_MODEM_STATUS (6*REG_OFFSET) -#define OFS_RS232_INPUT (6*REG_OFFSET) -#define OFS_SCRATCH_PAD (7*REG_OFFSET) - -#define OFS_DIVISOR_LSB (0*REG_OFFSET) -#define OFS_DIVISOR_MSB (1*REG_OFFSET) - - -/* memory-mapped read/write of the port */ -#define UART16550_READ(y) (*((volatile uint8*)(BASE + y))) -#define UART16550_WRITE(y, z) ((*((volatile uint8*)(BASE + y))) = z) - -void debugInit(uint32 baud, uint8 data, uint8 parity, uint8 stop) -{ - /* disable interrupts */ - UART16550_WRITE(OFS_INTR_ENABLE, 0); - - /* set up baud rate */ - { - uint32 divisor; - - /* set DIAB bit */ - UART16550_WRITE(OFS_LINE_CONTROL, 0x80); - - /* set divisor */ - divisor = MAX_BAUD / baud; - UART16550_WRITE(OFS_DIVISOR_LSB, divisor & 0xff); - UART16550_WRITE(OFS_DIVISOR_MSB, (divisor & 0xff00) >> 8); - - /* clear DIAB bit */ - UART16550_WRITE(OFS_LINE_CONTROL, 0x0); - } - - /* set data format */ - UART16550_WRITE(OFS_DATA_FORMAT, data | parity | stop); -} - -static int remoteDebugInitialized = 0; - -uint8 getDebugChar(void) -{ - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(UART16550_BAUD_38400, - UART16550_DATA_8BIT, - UART16550_PARITY_NONE, UART16550_STOP_1BIT); - } - - while ((UART16550_READ(OFS_LINE_STATUS) & 0x1) == 0); - return UART16550_READ(OFS_RCV_BUFFER); -} - - -int putDebugChar(uint8 byte) -{ - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(UART16550_BAUD_38400, - UART16550_DATA_8BIT, - UART16550_PARITY_NONE, UART16550_STOP_1BIT); - } - - while ((UART16550_READ(OFS_LINE_STATUS) & 0x20) == 0); - UART16550_WRITE(OFS_SEND_BUFFER, byte); - return 1; -} diff -puN arch/mips/jmr3927/rbhma3100/Makefile~git-kgdb arch/mips/jmr3927/rbhma3100/Makefile --- a/arch/mips/jmr3927/rbhma3100/Makefile~git-kgdb +++ a/arch/mips/jmr3927/rbhma3100/Makefile @@ -3,4 +3,3 @@ # obj-y += init.o irq.o setup.o -obj-$(CONFIG_KGDB) += kgdb_io.o diff -puN arch/mips/jmr3927/rbhma3100/kgdb_io.c~git-kgdb /dev/null --- a/arch/mips/jmr3927/rbhma3100/kgdb_io.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * BRIEF MODULE DESCRIPTION - * Low level uart routines to directly access a TX[34]927 SIO. - * - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ahennessy@mvista.com or source@mvista.com - * - * Based on arch/mips/ddb5xxx/ddb5477/kgdb_io.c - * - * Copyright (C) 2000-2001 Toshiba Corporation - * - * 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. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include - -#define TIMEOUT 0xffffff - -static int remoteDebugInitialized = 0; -static void debugInit(int baud); - -int putDebugChar(unsigned char c) -{ - int i = 0; - - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(38400); - } - - do { - slow_down(); - i++; - if (i>TIMEOUT) { - break; - } - } while (!(tx3927_sioptr(0)->cisr & TXx927_SICISR_TXALS)); - tx3927_sioptr(0)->tfifo = c; - - return 1; -} - -unsigned char getDebugChar(void) -{ - int i = 0; - int dicr; - char c; - - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(38400); - } - - /* diable RX int. */ - dicr = tx3927_sioptr(0)->dicr; - tx3927_sioptr(0)->dicr = 0; - - do { - slow_down(); - i++; - if (i>TIMEOUT) { - break; - } - } while (tx3927_sioptr(0)->disr & TXx927_SIDISR_UVALID) - ; - c = tx3927_sioptr(0)->rfifo; - - /* clear RX int. status */ - tx3927_sioptr(0)->disr &= ~TXx927_SIDISR_RDIS; - /* enable RX int. */ - tx3927_sioptr(0)->dicr = dicr; - - return c; -} - -static void debugInit(int baud) -{ - tx3927_sioptr(0)->lcr = 0x020; - tx3927_sioptr(0)->dicr = 0; - tx3927_sioptr(0)->disr = 0x4100; - tx3927_sioptr(0)->cisr = 0x014; - tx3927_sioptr(0)->fcr = 0; - tx3927_sioptr(0)->flcr = 0x02; - tx3927_sioptr(0)->bgr = ((JMR3927_BASE_BAUD + baud / 2) / baud) | - TXx927_SIBGR_BCLK_T0; -} diff -puN arch/mips/kernel/Makefile~git-kgdb arch/mips/kernel/Makefile --- a/arch/mips/kernel/Makefile~git-kgdb +++ a/arch/mips/kernel/Makefile @@ -58,7 +58,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 -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 -puN arch/mips/kernel/gdb-low.S~git-kgdb /dev/null --- a/arch/mips/kernel/gdb-low.S +++ /dev/null @@ -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 -puN arch/mips/kernel/gdb-stub.c~git-kgdb /dev/null --- a/arch/mips/kernel/gdb-stub.c +++ /dev/null @@ -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 __used *malloc(size_t size) -{ - return kmalloc(size, GFP_ATOMIC); -} - -static void __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 -puN arch/mips/kernel/irq.c~git-kgdb arch/mips/kernel/irq.c --- a/arch/mips/kernel/irq.c~git-kgdb +++ a/arch/mips/kernel/irq.c @@ -21,11 +21,15 @@ #include #include #include +#include #include #include #include +/* Keep track of if we've done certain initialization already or not. */ +int kgdb_early_setup; + static unsigned long irq_map[NR_IRQS / BITS_PER_LONG]; int allocate_irqno(void) @@ -138,28 +142,23 @@ asmlinkage void spurious_interrupt(void) 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) +void __init init_IRQ(void) { - kgdb_flag = 0; - return 1; -} -__setup("nokgdb", nokgdb); + +#ifdef CONFIG_KGDB + if (kgdb_early_setup) + return; #endif -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 -puN /dev/null arch/mips/kernel/kgdb-jmp.c --- /dev/null +++ a/arch/mips/kernel/kgdb-jmp.c @@ -0,0 +1,112 @@ +/* + * 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-2006 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 + +#ifdef CONFIG_64BIT +/* + * MIPS 64-bit + */ + +int kgdb_fault_setjmp_aux(unsigned long *curr_context, unsigned long sp, + unsigned long 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] = sp; + curr_context[11] = fp; + + return 0; +} + +void kgdb_fault_longjmp(unsigned long *curr_context) +{ + __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])); + __asm__ __volatile__ ("ld $29, %0\n\t" + "ld $30, %1\n\t" : : + "m" (curr_context[10]), "m" (curr_context[11])); + + __asm__ __volatile__ ("dli $2, 1"); + __asm__ __volatile__ ("j $25"); + + for (; ; ); +} +#else +/* + * MIPS 32-bit + */ + +int kgdb_fault_setjmp_aux(unsigned long *curr_context, unsigned long sp, + unsigned long 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] = sp; + curr_context[11] = fp; + + return 0; +} + +void kgdb_fault_longjmp(unsigned long *curr_context) +{ + __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])); + + __asm__ __volatile__("lw $29, %0\n\t" + "lw $30, %1\n\t" : : + "m" (curr_context[10]), "m" (curr_context[11])); + + __asm__ __volatile__("li $2, 1"); + __asm__ __volatile__("jr $25"); + + for (; ; ); +} +#endif diff -puN /dev/null arch/mips/kernel/kgdb-setjmp.S --- /dev/null +++ a/arch/mips/kernel/kgdb-setjmp.S @@ -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_64BIT + nop +#endif + j kgdb_fault_setjmp_aux + .end kgdb_fault_setjmp diff -puN /dev/null arch/mips/kernel/kgdb.c --- /dev/null +++ a/arch/mips/kernel/kgdb.c @@ -0,0 +1,294 @@ +/* + * 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 /* for linux pt_regs struct */ +#include +#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_enable(); + smp_call_function(kgdb_call_nmi_hook, 0, 0, 0); + local_irq_disable(); +} + +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 */ + local_irq_enable(); + __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); +} + +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int reg; +#if (KGDB_GDB_REG_SIZE == 32) + u32 *ptr = (u32 *)gdb_regs; +#else + u64 *ptr = (u64 *)gdb_regs; +#endif + + 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; +#if (KGDB_GDB_REG_SIZE == 32) + const u32 *ptr = (u32 *)gdb_regs; +#else + const u64 *ptr = (u64 *)gdb_regs; +#endif + + 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 = task_thread_info(p); + unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32; + struct pt_regs *regs = (struct pt_regs *)ksp - 1; +#if (KGDB_GDB_REG_SIZE == 32) + u32 *ptr = (u32 *)gdb_regs; +#else + u64 *ptr = (u64 *)gdb_regs; +#endif + + 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 /* ! CONFIG_CPU_LITTLE_ENDIAN */ + .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. + */ +__init 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(); + register_die_notifier(&kgdb_notifier); + + return 0; +} diff -puN /dev/null arch/mips/kernel/kgdb_handler.S --- /dev/null +++ a/arch/mips/kernel/kgdb_handler.S @@ -0,0 +1,339 @@ +/* + * arch/mips/kernel/kgdb_handler.S + * + * Copyright (C) 2007 Wind River Systems, Inc + * + * 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 + +#ifdef CONFIG_32BIT +#define DMFC0 mfc0 +#define DMTC0 mtc0 +#define LDC1 lwc1 +#define SDC1 swc1 +#endif +#ifdef CONFIG_64BIT +#define DMFC0 dmfc0 +#define DMTC0 dmtc0 +#define LDC1 ldc1 +#define SDC1 sdc1 +#endif + +#include + +/* + * [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. + */ +#if defined(CONFIG_32BIT) + lui k1, %hi(saved_vectors) + mfc0 k0, CP0_CAUSE + andi k0, k0, 0x7c + add k1, k1, k0 + lw k0, %lo(saved_vectors)(k1) +#elif defined(CONFIG_64BIT) && defined(CONFIG_BUILD_ELF64) + DMFC0 k0, CP0_CAUSE + lui k1, %highest(saved_vectors) + andi k0, k0, 0x7c /* mask exception type */ + dsll k0, 1 /* turn into byte offset */ + daddiu k1, %higher(saved_vectors) + dsll k1, k1, 16 + daddiu k1, %hi(saved_vectors) + dsll k1, k1, 16 + daddu k1, k1, k0 + LONG_L k0, %lo(saved_vectors)(k1) +#else +#error "MIPS configuration is unsupported for kgdb!!" +#endif + jr k0 + 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 */ + +/* + * Followed by the floating point registers + */ + mfc0 v0, CP0_STATUS /* FPU enabled? */ + srl v0, v0, 16 + andi v0, v0, (ST0_CU1 >> 16) + + beqz v0,3f /* disabled, skip */ + nop + + li t0, 0 +#ifdef CONFIG_64BIT + mfc0 t0, CP0_STATUS +#endif + fpu_save_double_kgdb sp t0 t1 # clobbers t1 + + +/* + * Current stack frame ptr + */ + +3: + 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 FPU is enabled */ + srl v0, v0, 16 + andi v0, v0, (ST0_CU1 >> 16) + + beqz v0, 3f /* disabled, skip */ + nop + + li t0, 0 +#ifdef CONFIG_64BIT + mfc0 t0, CP0_STATUS +#endif + fpu_restore_double_kgdb sp t0 t1 # clobbers t1 + + +/* + * Now the CP0 and integer registers + */ + +3: + mfc0 t0, CP0_STATUS + ori t0, 0x1f + xori t0, 0x1f + mtc0 t0, CP0_STATUS + + 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 -puN arch/mips/kernel/traps.c~git-kgdb arch/mips/kernel/traps.c --- a/arch/mips/kernel/traps.c~git-kgdb +++ a/arch/mips/kernel/traps.c @@ -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 @@ -21,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -1456,6 +1460,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 -puN arch/mips/mips-boards/atlas/Makefile~git-kgdb arch/mips/mips-boards/atlas/Makefile --- a/arch/mips/mips-boards/atlas/Makefile~git-kgdb +++ a/arch/mips/mips-boards/atlas/Makefile @@ -17,4 +17,3 @@ # obj-y := atlas_int.o atlas_setup.o -obj-$(CONFIG_KGDB) += atlas_gdb.o diff -puN arch/mips/mips-boards/atlas/atlas_gdb.c~git-kgdb /dev/null --- a/arch/mips/mips-boards/atlas/atlas_gdb.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * This is the interface to the remote debugger stub. - */ -#include -#include -#include - -#define INB(a) inb((unsigned long)a) -#define OUTB(x,a) outb(x,(unsigned long)a) - -/* - * This is the interface to the remote debugger stub - * if the Philips part is used for the debug port, - * called from the platform setup code. - */ -void *saa9730_base = (void *)ATLAS_SAA9730_REG; - -static int saa9730_kgdb_active = 0; - -#define SAA9730_BAUDCLOCK(baud) (((ATLAS_SAA9730_BAUDCLOCK/(baud))/16)-1) - -int saa9730_kgdb_hook(int speed) -{ - int baudclock; - t_uart_saa9730_regmap *kgdb_uart = (t_uart_saa9730_regmap *)(saa9730_base + SAA9730_UART_REGS_ADDR); - - /* - * Clear all interrupts - */ - (void) INB(&kgdb_uart->Lsr); - (void) INB(&kgdb_uart->Msr); - (void) INB(&kgdb_uart->Thr_Rbr); - (void) INB(&kgdb_uart->Iir_Fcr); - - /* - * Now, initialize the UART - */ - /* 8 data bits, one stop bit, no parity */ - OUTB(SAA9730_LCR_DATA8, &kgdb_uart->Lcr); - - baudclock = SAA9730_BAUDCLOCK(speed); - - OUTB((baudclock >> 16) & 0xff, &kgdb_uart->BaudDivMsb); - OUTB( baudclock & 0xff, &kgdb_uart->BaudDivLsb); - - /* Set RTS/DTR active */ - OUTB(SAA9730_MCR_DTR | SAA9730_MCR_RTS, &kgdb_uart->Mcr); - saa9730_kgdb_active = 1; - - return speed; -} - -int saa9730_putDebugChar(char c) -{ - t_uart_saa9730_regmap *kgdb_uart = (t_uart_saa9730_regmap *)(saa9730_base + SAA9730_UART_REGS_ADDR); - - if (!saa9730_kgdb_active) { /* need to init device first */ - return 0; - } - - while (!(INB(&kgdb_uart->Lsr) & SAA9730_LSR_THRE)) - ; - OUTB(c, &kgdb_uart->Thr_Rbr); - - return 1; -} - -char saa9730_getDebugChar(void) -{ - t_uart_saa9730_regmap *kgdb_uart = (t_uart_saa9730_regmap *)(saa9730_base + SAA9730_UART_REGS_ADDR); - char c; - - if (!saa9730_kgdb_active) { /* need to init device first */ - return 0; - } - while (!(INB(&kgdb_uart->Lsr) & SAA9730_LSR_DR)) - ; - - c = INB(&kgdb_uart->Thr_Rbr); - return(c); -} diff -puN arch/mips/mips-boards/atlas/atlas_setup.c~git-kgdb arch/mips/mips-boards/atlas/atlas_setup.c --- a/arch/mips/mips-boards/atlas/atlas_setup.c~git-kgdb +++ a/arch/mips/mips-boards/atlas/atlas_setup.c @@ -38,10 +38,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 - static void __init serial_init(void); const char *get_system_type(void) @@ -59,9 +55,6 @@ void __init plat_mem_setup(void) serial_init (); -#ifdef CONFIG_KGDB - kgdb_config(); -#endif mips_reboot_setup(); board_time_init = mips_time_init; diff -puN arch/mips/mips-boards/generic/Makefile~git-kgdb arch/mips/mips-boards/generic/Makefile --- a/arch/mips/mips-boards/generic/Makefile~git-kgdb +++ a/arch/mips/mips-boards/generic/Makefile @@ -23,4 +23,3 @@ obj-y := reset.o display.o init.o mem obj-$(CONFIG_EARLY_PRINTK) += console.o obj-$(CONFIG_PCI) += pci.o -obj-$(CONFIG_KGDB) += gdb_hook.o diff -puN arch/mips/mips-boards/generic/gdb_hook.c~git-kgdb /dev/null --- a/arch/mips/mips-boards/generic/gdb_hook.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * This is the interface to the remote debugger stub. - */ -#include -#include -#include -#include - -#include -#include - -static struct serial_state rs_table[] = { - SERIAL_PORT_DFNS /* Defined in serial.h */ -}; - -static struct async_struct kdb_port_info = {0}; - -int (*generic_putDebugChar)(char); -char (*generic_getDebugChar)(void); - -static __inline__ unsigned int serial_in(struct async_struct *info, int offset) -{ - return inb(info->port + offset); -} - -static __inline__ void serial_out(struct async_struct *info, int offset, - int value) -{ - outb(value, info->port+offset); -} - -int rs_kgdb_hook(int tty_no, int speed) { - int t; - struct serial_state *ser = &rs_table[tty_no]; - - kdb_port_info.state = ser; - kdb_port_info.magic = SERIAL_MAGIC; - kdb_port_info.port = ser->port; - kdb_port_info.flags = ser->flags; - - /* - * Clear all interrupts - */ - serial_in(&kdb_port_info, UART_LSR); - serial_in(&kdb_port_info, UART_RX); - serial_in(&kdb_port_info, UART_IIR); - serial_in(&kdb_port_info, UART_MSR); - - /* - * Now, initialize the UART - */ - serial_out(&kdb_port_info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ - if (kdb_port_info.flags & ASYNC_FOURPORT) { - kdb_port_info.MCR = UART_MCR_DTR | UART_MCR_RTS; - t = UART_MCR_DTR | UART_MCR_OUT1; - } else { - kdb_port_info.MCR - = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; - t = UART_MCR_DTR | UART_MCR_RTS; - } - - kdb_port_info.MCR = t; /* no interrupts, please */ - serial_out(&kdb_port_info, UART_MCR, kdb_port_info.MCR); - - /* - * and set the speed of the serial port - */ - if (speed == 0) - speed = 9600; - - t = kdb_port_info.state->baud_base / speed; - /* set DLAB */ - serial_out(&kdb_port_info, UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); - serial_out(&kdb_port_info, UART_DLL, t & 0xff);/* LS of divisor */ - serial_out(&kdb_port_info, UART_DLM, t >> 8); /* MS of divisor */ - /* reset DLAB */ - serial_out(&kdb_port_info, UART_LCR, UART_LCR_WLEN8); - - return speed; -} - -int putDebugChar(char c) -{ - return generic_putDebugChar(c); -} - -char getDebugChar(void) -{ - return generic_getDebugChar(); -} - -int rs_putDebugChar(char c) -{ - - if (!kdb_port_info.state) { /* need to init device first */ - return 0; - } - - while ((serial_in(&kdb_port_info, UART_LSR) & UART_LSR_THRE) == 0) - ; - - serial_out(&kdb_port_info, UART_TX, c); - - return 1; -} - -char rs_getDebugChar(void) -{ - if (!kdb_port_info.state) { /* need to init device first */ - return 0; - } - - while (!(serial_in(&kdb_port_info, UART_LSR) & 1)) - ; - - return serial_in(&kdb_port_info, UART_RX); -} diff -puN arch/mips/mips-boards/generic/init.c~git-kgdb arch/mips/mips-boards/generic/init.c --- a/arch/mips/mips-boards/generic/init.c~git-kgdb +++ a/arch/mips/mips-boards/generic/init.c @@ -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; @@ -173,59 +164,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; - } - - pr_info("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 -puN arch/mips/mips-boards/malta/malta_setup.c~git-kgdb arch/mips/mips-boards/malta/malta_setup.c --- a/arch/mips/mips-boards/malta/malta_setup.c~git-kgdb +++ a/arch/mips/mips-boards/malta/malta_setup.c @@ -39,10 +39,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 }, @@ -99,10 +95,6 @@ void __init plat_mem_setup(void) */ enable_dma(4); -#ifdef CONFIG_KGDB - kgdb_config (); -#endif - if (mips_revision_sconid == MIPS_REVISION_SCON_BONITO) { char *argptr; diff -puN arch/mips/mm/extable.c~git-kgdb arch/mips/mm/extable.c --- a/arch/mips/mm/extable.c~git-kgdb +++ a/arch/mips/mm/extable.c @@ -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 -puN arch/mips/pci/fixup-atlas.c~git-kgdb arch/mips/pci/fixup-atlas.c --- a/arch/mips/pci/fixup-atlas.c~git-kgdb +++ a/arch/mips/pci/fixup-atlas.c @@ -68,24 +68,3 @@ int pcibios_plat_dev_init(struct pci_dev { return 0; } - -#ifdef CONFIG_KGDB -/* - * The PCI scan may have moved the saa9730 I/O address, so reread - * the address here. - * This does mean that it's not possible to debug the PCI bus configuration - * code, but it is better than nothing... - */ - -static void atlas_saa9730_base_fixup (struct pci_dev *pdev) -{ - extern void *saa9730_base; - if (pdev->bus == 0 && PCI_SLOT(pdev->devfn) == 19) - (void) pci_read_config_dword (pdev, 0x14, (u32 *)&saa9730_base); - printk ("saa9730_base = %x\n", saa9730_base); -} - -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA9730, - atlas_saa9730_base_fixup); - -#endif diff -puN arch/mips/philips/pnx8550/common/Makefile~git-kgdb arch/mips/philips/pnx8550/common/Makefile --- a/arch/mips/philips/pnx8550/common/Makefile~git-kgdb +++ a/arch/mips/philips/pnx8550/common/Makefile @@ -24,4 +24,3 @@ obj-y := setup.o prom.o int.o reset.o time.o proc.o platform.o obj-$(CONFIG_PCI) += pci.o -obj-$(CONFIG_KGDB) += gdb_hook.o diff -puN arch/mips/philips/pnx8550/common/gdb_hook.c~git-kgdb /dev/null --- a/arch/mips/philips/pnx8550/common/gdb_hook.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * ######################################################################## - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * ######################################################################## - * - * This is the interface to the remote debugger stub. - * - */ -#include -#include -#include -#include -#include - -#include -#include - -#include - -static struct serial_state rs_table[IP3106_NR_PORTS] = { -}; -static struct async_struct kdb_port_info = {0}; - -void rs_kgdb_hook(int tty_no) -{ - struct serial_state *ser = &rs_table[tty_no]; - - kdb_port_info.state = ser; - kdb_port_info.magic = SERIAL_MAGIC; - kdb_port_info.port = tty_no; - kdb_port_info.flags = ser->flags; - - /* - * Clear all interrupts - */ - /* Clear all the transmitter FIFO counters (pointer and status) */ - ip3106_lcr(UART_BASE, tty_no) |= IP3106_UART_LCR_TX_RST; - /* Clear all the receiver FIFO counters (pointer and status) */ - ip3106_lcr(UART_BASE, tty_no) |= IP3106_UART_LCR_RX_RST; - /* Clear all interrupts */ - ip3106_iclr(UART_BASE, tty_no) = IP3106_UART_INT_ALLRX | - IP3106_UART_INT_ALLTX; - - /* - * Now, initialize the UART - */ - ip3106_lcr(UART_BASE, tty_no) = IP3106_UART_LCR_8BIT; - ip3106_baud(UART_BASE, tty_no) = 5; // 38400 Baud -} - -int putDebugChar(char c) -{ - /* Wait until FIFO not full */ - while (((ip3106_fifo(UART_BASE, kdb_port_info.port) & IP3106_UART_FIFO_TXFIFO) >> 16) >= 16) - ; - /* Send one char */ - ip3106_fifo(UART_BASE, kdb_port_info.port) = c; - - return 1; -} - -char getDebugChar(void) -{ - char ch; - - /* Wait until there is a char in the FIFO */ - while (!((ip3106_fifo(UART_BASE, kdb_port_info.port) & - IP3106_UART_FIFO_RXFIFO) >> 8)) - ; - /* Read one char */ - ch = ip3106_fifo(UART_BASE, kdb_port_info.port) & - IP3106_UART_FIFO_RBRTHR; - /* Advance the RX FIFO read pointer */ - ip3106_lcr(UART_BASE, kdb_port_info.port) |= IP3106_UART_LCR_RX_NEXT; - return (ch); -} - -void rs_disable_debug_interrupts(void) -{ - ip3106_ien(UART_BASE, kdb_port_info.port) = 0; /* Disable all interrupts */ -} - -void rs_enable_debug_interrupts(void) -{ - /* Clear all the transmitter FIFO counters (pointer and status) */ - ip3106_lcr(UART_BASE, kdb_port_info.port) |= IP3106_UART_LCR_TX_RST; - /* Clear all the receiver FIFO counters (pointer and status) */ - ip3106_lcr(UART_BASE, kdb_port_info.port) |= IP3106_UART_LCR_RX_RST; - /* Clear all interrupts */ - ip3106_iclr(UART_BASE, kdb_port_info.port) = IP3106_UART_INT_ALLRX | - IP3106_UART_INT_ALLTX; - ip3106_ien(UART_BASE, kdb_port_info.port) = IP3106_UART_INT_ALLRX; /* Enable RX interrupts */ -} diff -puN arch/mips/philips/pnx8550/common/setup.c~git-kgdb arch/mips/philips/pnx8550/common/setup.c --- a/arch/mips/philips/pnx8550/common/setup.c~git-kgdb +++ a/arch/mips/philips/pnx8550/common/setup.c @@ -145,16 +145,5 @@ void __init plat_mem_setup(void) ip3106_baud(UART_BASE, pnx8550_console_port) = 5; } -#ifdef CONFIG_KGDB - argptr = prom_getcmdline(); - if ((argptr = strstr(argptr, "kgdb=ttyS")) != NULL) { - int line; - argptr += strlen("kgdb=ttyS"); - line = *argptr == '0' ? 0 : 1; - rs_kgdb_hook(line); - pr_info("KGDB: Using ttyS%i for session, " - "please connect your debugger\n", line ? 1 : 0); - } -#endif return; } diff -puN arch/mips/pmc-sierra/yosemite/Makefile~git-kgdb arch/mips/pmc-sierra/yosemite/Makefile --- a/arch/mips/pmc-sierra/yosemite/Makefile~git-kgdb +++ a/arch/mips/pmc-sierra/yosemite/Makefile @@ -4,5 +4,4 @@ obj-y += irq.o i2c-yosemite.o prom.o py-console.o setup.o -obj-$(CONFIG_KGDB) += dbg_io.o obj-$(CONFIG_SMP) += smp.o diff -puN arch/mips/pmc-sierra/yosemite/dbg_io.c~git-kgdb /dev/null --- a/arch/mips/pmc-sierra/yosemite/dbg_io.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2003 PMC-Sierra - * Author: Manish Lachwani (lachwani@pmc-sierra.com) - * - * 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. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* - * Support for KGDB for the Yosemite board. We make use of single serial - * port to be used for KGDB as well as console. The second serial port - * seems to be having a problem. Single IRQ is allocated for both the - * ports. Hence, the interrupt routing code needs to figure out whether - * the interrupt came from channel A or B. - */ - -#include - -/* - * Baud rate, Parity, Data and Stop bit settings for the - * serial port on the Yosemite. Note that the Early printk - * patch has been added. So, we should be all set to go - */ -#define YOSEMITE_BAUD_2400 2400 -#define YOSEMITE_BAUD_4800 4800 -#define YOSEMITE_BAUD_9600 9600 -#define YOSEMITE_BAUD_19200 19200 -#define YOSEMITE_BAUD_38400 38400 -#define YOSEMITE_BAUD_57600 57600 -#define YOSEMITE_BAUD_115200 115200 - -#define YOSEMITE_PARITY_NONE 0 -#define YOSEMITE_PARITY_ODD 0x08 -#define YOSEMITE_PARITY_EVEN 0x18 -#define YOSEMITE_PARITY_MARK 0x28 -#define YOSEMITE_PARITY_SPACE 0x38 - -#define YOSEMITE_DATA_5BIT 0x0 -#define YOSEMITE_DATA_6BIT 0x1 -#define YOSEMITE_DATA_7BIT 0x2 -#define YOSEMITE_DATA_8BIT 0x3 - -#define YOSEMITE_STOP_1BIT 0x0 -#define YOSEMITE_STOP_2BIT 0x4 - -/* This is crucial */ -#define SERIAL_REG_OFS 0x1 - -#define SERIAL_RCV_BUFFER 0x0 -#define SERIAL_TRANS_HOLD 0x0 -#define SERIAL_SEND_BUFFER 0x0 -#define SERIAL_INTR_ENABLE (1 * SERIAL_REG_OFS) -#define SERIAL_INTR_ID (2 * SERIAL_REG_OFS) -#define SERIAL_DATA_FORMAT (3 * SERIAL_REG_OFS) -#define SERIAL_LINE_CONTROL (3 * SERIAL_REG_OFS) -#define SERIAL_MODEM_CONTROL (4 * SERIAL_REG_OFS) -#define SERIAL_RS232_OUTPUT (4 * SERIAL_REG_OFS) -#define SERIAL_LINE_STATUS (5 * SERIAL_REG_OFS) -#define SERIAL_MODEM_STATUS (6 * SERIAL_REG_OFS) -#define SERIAL_RS232_INPUT (6 * SERIAL_REG_OFS) -#define SERIAL_SCRATCH_PAD (7 * SERIAL_REG_OFS) - -#define SERIAL_DIVISOR_LSB (0 * SERIAL_REG_OFS) -#define SERIAL_DIVISOR_MSB (1 * SERIAL_REG_OFS) - -/* - * Functions to READ and WRITE to serial port 0 - */ -#define SERIAL_READ(ofs) (*((volatile unsigned char*) \ - (TITAN_SERIAL_BASE + ofs))) - -#define SERIAL_WRITE(ofs, val) ((*((volatile unsigned char*) \ - (TITAN_SERIAL_BASE + ofs))) = val) - -/* - * Functions to READ and WRITE to serial port 1 - */ -#define SERIAL_READ_1(ofs) (*((volatile unsigned char*) \ - (TITAN_SERIAL_BASE_1 + ofs))) - -#define SERIAL_WRITE_1(ofs, val) ((*((volatile unsigned char*) \ - (TITAN_SERIAL_BASE_1 + ofs))) = val) - -/* - * Second serial port initialization - */ -void init_second_port(void) -{ - /* Disable Interrupts */ - SERIAL_WRITE_1(SERIAL_LINE_CONTROL, 0x0); - SERIAL_WRITE_1(SERIAL_INTR_ENABLE, 0x0); - - { - unsigned int divisor; - - SERIAL_WRITE_1(SERIAL_LINE_CONTROL, 0x80); - divisor = TITAN_SERIAL_BASE_BAUD / YOSEMITE_BAUD_115200; - SERIAL_WRITE_1(SERIAL_DIVISOR_LSB, divisor & 0xff); - - SERIAL_WRITE_1(SERIAL_DIVISOR_MSB, - (divisor & 0xff00) >> 8); - SERIAL_WRITE_1(SERIAL_LINE_CONTROL, 0x0); - } - - SERIAL_WRITE_1(SERIAL_DATA_FORMAT, YOSEMITE_DATA_8BIT | - YOSEMITE_PARITY_NONE | YOSEMITE_STOP_1BIT); - - /* Enable Interrupts */ - SERIAL_WRITE_1(SERIAL_INTR_ENABLE, 0xf); -} - -/* Initialize the serial port for KGDB debugging */ -void debugInit(unsigned int baud, unsigned char data, unsigned char parity, - unsigned char stop) -{ - /* Disable Interrupts */ - SERIAL_WRITE(SERIAL_LINE_CONTROL, 0x0); - SERIAL_WRITE(SERIAL_INTR_ENABLE, 0x0); - - { - unsigned int divisor; - - SERIAL_WRITE(SERIAL_LINE_CONTROL, 0x80); - - divisor = TITAN_SERIAL_BASE_BAUD / baud; - SERIAL_WRITE(SERIAL_DIVISOR_LSB, divisor & 0xff); - - SERIAL_WRITE(SERIAL_DIVISOR_MSB, (divisor & 0xff00) >> 8); - SERIAL_WRITE(SERIAL_LINE_CONTROL, 0x0); - } - - SERIAL_WRITE(SERIAL_DATA_FORMAT, data | parity | stop); -} - -static int remoteDebugInitialized = 0; - -unsigned char getDebugChar(void) -{ - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(YOSEMITE_BAUD_115200, - YOSEMITE_DATA_8BIT, - YOSEMITE_PARITY_NONE, YOSEMITE_STOP_1BIT); - } - - while ((SERIAL_READ(SERIAL_LINE_STATUS) & 0x1) == 0); - return SERIAL_READ(SERIAL_RCV_BUFFER); -} - -int putDebugChar(unsigned char byte) -{ - if (!remoteDebugInitialized) { - remoteDebugInitialized = 1; - debugInit(YOSEMITE_BAUD_115200, - YOSEMITE_DATA_8BIT, - YOSEMITE_PARITY_NONE, YOSEMITE_STOP_1BIT); - } - - while ((SERIAL_READ(SERIAL_LINE_STATUS) & 0x20) == 0); - SERIAL_WRITE(SERIAL_SEND_BUFFER, byte); - - return 1; -} diff -puN arch/mips/pmc-sierra/yosemite/irq.c~git-kgdb arch/mips/pmc-sierra/yosemite/irq.c --- a/arch/mips/pmc-sierra/yosemite/irq.c~git-kgdb +++ a/arch/mips/pmc-sierra/yosemite/irq.c @@ -137,10 +137,6 @@ asmlinkage void plat_irq_dispatch(void) } } -#ifdef CONFIG_KGDB -extern void init_second_port(void); -#endif - /* * Initialize the next level interrupt handler */ @@ -152,11 +148,6 @@ void __init arch_init_irq(void) rm7k_cpu_irq_init(); rm9k_cpu_irq_init(); -#ifdef CONFIG_KGDB - /* At this point, initialize the second serial port */ - init_second_port(); -#endif - #ifdef CONFIG_GDB_CONSOLE register_gdb_console(); #endif diff -puN arch/mips/sgi-ip22/ip22-setup.c~git-kgdb arch/mips/sgi-ip22/ip22-setup.c --- a/arch/mips/sgi-ip22/ip22-setup.c~git-kgdb +++ a/arch/mips/sgi-ip22/ip22-setup.c @@ -101,30 +101,6 @@ void __init plat_mem_setup(void) add_preferred_console("arc", 0, NULL); } -#ifdef CONFIG_KGDB - { - char *kgdb_ttyd = prom_getcmdline(); - - if ((kgdb_ttyd = strstr(kgdb_ttyd, "kgdb=ttyd")) != NULL) { - int line; - kgdb_ttyd += strlen("kgdb=ttyd"); - if (*kgdb_ttyd != '1' && *kgdb_ttyd != '2') - printk(KERN_INFO "KGDB: Uknown serial line /dev/ttyd%c" - ", falling back to /dev/ttyd1\n", *kgdb_ttyd); - line = *kgdb_ttyd == '2' ? 0 : 1; - printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for " - "session\n", line ? 1 : 2); - rs_kgdb_hook(line); - - printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for " - "session, please connect your debugger\n", line ? 1:2); - - kgdb_enabled = 1; - /* Breakpoints and stuff are in sgi_irq_setup() */ - } - } -#endif - #if defined(CONFIG_VT) && defined(CONFIG_SGI_NEWPORT_CONSOLE) { ULONG *gfxinfo; diff -puN arch/mips/sgi-ip27/Makefile~git-kgdb arch/mips/sgi-ip27/Makefile --- a/arch/mips/sgi-ip27/Makefile~git-kgdb +++ a/arch/mips/sgi-ip27/Makefile @@ -7,5 +7,4 @@ obj-y := ip27-berr.o ip27-irq.o ip27-ini ip27-xtalk.o obj-$(CONFIG_EARLY_PRINTK) += ip27-console.o -obj-$(CONFIG_KGDB) += ip27-dbgio.o obj-$(CONFIG_SMP) += ip27-smp.o diff -puN arch/mips/sgi-ip27/ip27-dbgio.c~git-kgdb /dev/null --- a/arch/mips/sgi-ip27/ip27-dbgio.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Copyright 2004 Ralf Baechle - */ -#include -#include -#include -#include -#include - -#include -#include -#include - -#define IOC3_CLK (22000000 / 3) -#define IOC3_FLAGS (0) - -static inline struct ioc3_uartregs *console_uart(void) -{ - struct ioc3 *ioc3; - - ioc3 = (struct ioc3 *)KL_CONFIG_CH_CONS_INFO(get_nasid())->memory_base; - - return &ioc3->sregs.uarta; -} - -unsigned char getDebugChar(void) -{ - struct ioc3_uartregs *uart = console_uart(); - - while ((uart->iu_lsr & UART_LSR_DR) == 0); - return uart->iu_rbr; -} - -void putDebugChar(unsigned char c) -{ - struct ioc3_uartregs *uart = console_uart(); - - while ((uart->iu_lsr & UART_LSR_THRE) == 0); - uart->iu_thr = c; -} diff -puN arch/mips/sibyte/bcm1480/irq.c~git-kgdb arch/mips/sibyte/bcm1480/irq.c --- a/arch/mips/sibyte/bcm1480/irq.c~git-kgdb +++ a/arch/mips/sibyte/bcm1480/irq.c @@ -57,30 +57,6 @@ static void bcm1480_set_affinity(unsigne extern unsigned long ht_eoi_space; #endif -#ifdef CONFIG_KGDB -#include -extern void breakpoint(void); -static int kgdb_irq; -#ifdef CONFIG_GDB_CONSOLE -extern void register_gdb_console(void); -#endif - -/* kgdb is on when configured. Pass "nokgdb" kernel arg to turn it off */ -static int kgdb_flag = 1; -static int __init nokgdb(char *str) -{ - kgdb_flag = 0; - return 1; -} -__setup("nokgdb", nokgdb); - -/* Default to UART1 */ -int kgdb_port = 1; -#ifdef CONFIG_SIBYTE_SB1250_DUART -extern char sb1250_duart_present[]; -#endif -#endif - static struct irq_chip bcm1480_irq_type = { .name = "BCM1480-IMR", .ack = ack_bcm1480_irq, @@ -394,62 +370,11 @@ void __init arch_init_irq(void) * does its own management of IP7. */ -#ifdef CONFIG_KGDB - imask |= STATUSF_IP6; -#endif /* Enable necessary IPs, disable the rest */ change_c0_status(ST0_IM, imask); -#ifdef CONFIG_KGDB - if (kgdb_flag) { - kgdb_irq = K_BCM1480_INT_UART_0 + kgdb_port; - -#ifdef CONFIG_SIBYTE_SB1250_DUART - sb1250_duart_present[kgdb_port] = 0; -#endif - /* Setup uart 1 settings, mapper */ - /* QQQ FIXME */ - __raw_writeq(M_DUART_IMR_BRK, IO_SPACE_BASE + A_DUART_IMRREG(kgdb_port)); - - bcm1480_steal_irq(kgdb_irq); - __raw_writeq(IMR_IP6_VAL, - IO_SPACE_BASE + A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) + - (kgdb_irq<<3)); - bcm1480_unmask_irq(0, kgdb_irq); - -#ifdef CONFIG_GDB_CONSOLE - register_gdb_console(); -#endif - printk("Waiting for GDB on UART port %d\n", kgdb_port); - set_debug_traps(); - breakpoint(); - } -#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 bcm1480_kgdb_interrupt(void) -{ - /* - * 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.irqs[smp_processor_id()][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(&get_irq_regs()->cp0_epc); -} - -#endif /* CONFIG_KGDB */ - extern void bcm1480_timer_interrupt(void); extern void bcm1480_mailbox_interrupt(void); @@ -478,11 +403,6 @@ asmlinkage void plat_irq_dispatch(void) bcm1480_mailbox_interrupt(); #endif -#ifdef CONFIG_KGDB - else if (pending & CAUSEF_IP6) - bcm1480_kgdb_interrupt(); /* KGDB (uart 1) */ -#endif - else if (pending & CAUSEF_IP2) { unsigned long long mask_h, mask_l; unsigned long base; diff -puN arch/mips/sibyte/cfe/setup.c~git-kgdb arch/mips/sibyte/cfe/setup.c --- a/arch/mips/sibyte/cfe/setup.c~git-kgdb +++ a/arch/mips/sibyte/cfe/setup.c @@ -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 __noreturn 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 -puN arch/mips/sibyte/sb1250/Makefile~git-kgdb arch/mips/sibyte/sb1250/Makefile --- a/arch/mips/sibyte/sb1250/Makefile~git-kgdb +++ a/arch/mips/sibyte/sb1250/Makefile @@ -3,3 +3,4 @@ obj-y := setup.o irq.o time.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SIBYTE_STANDALONE) += prom.o obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o +obj-$(CONFIG_KGDB_SIBYTE) += kgdb_sibyte.o diff -puN arch/mips/sibyte/sb1250/irq.c~git-kgdb arch/mips/sibyte/sb1250/irq.c --- a/arch/mips/sibyte/sb1250/irq.c~git-kgdb +++ a/arch/mips/sibyte/sb1250/irq.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -56,16 +57,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 = { .name = "SB1250-IMR", .ack = ack_sb1250_irq, @@ -304,6 +295,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, @@ -349,58 +345,16 @@ void __init arch_init_irq(void) * does its own management of IP7. */ -#ifdef CONFIG_KGDB +#ifdef CONFIG_KGDB_SIBYTE imask |= STATUSF_IP6; #endif /* 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(void) -{ - /* - * 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(&get_irq_regs()->cp0_epc); -} - -#endif /* CONFIG_KGDB */ - extern void sb1250_timer_interrupt(void); extern void sb1250_mailbox_interrupt(void); +extern void sb1250_kgdb_interrupt(void); asmlinkage void plat_irq_dispatch(void) { @@ -437,7 +391,7 @@ asmlinkage void plat_irq_dispatch(void) sb1250_mailbox_interrupt(); #endif -#ifdef CONFIG_KGDB +#ifdef CONFIG_KGDB_SIBYTE else if (pending & CAUSEF_IP6) /* KGDB (uart 1) */ sb1250_kgdb_interrupt(); #endif diff -puN /dev/null arch/mips/sibyte/sb1250/kgdb_sibyte.c --- /dev/null +++ a/arch/mips/sibyte/sb1250/kgdb_sibyte.c @@ -0,0 +1,145 @@ +/* + * 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 kgdbsibyte_write_char(u8 c) +{ + while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0) ; + duart_out(R_DUART_TX_HOLD, c); +} + +static int kgdbsibyte_read_char(void) +{ + int ret_char; + unsigned int status; + + do { + status = duart_in(R_DUART_STATUS); + } while ((status & M_DUART_RX_RDY) == 0); + + /* + * Check for framing error + */ + if (status & M_DUART_FRM_ERR) { + kgdbsibyte_init_duart(); + kgdbsibyte_write_char('-'); + return '-'; + } + + ret_char = duart_in(R_DUART_RX_HOLD); + + return ret_char; +} + +void sb1250_kgdb_interrupt(void) +{ + 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); + 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 = kgdbsibyte_read_char, + .write_char = kgdbsibyte_write_char, + .init = kgdb_init_io, + .late_init = kgdbsibyte_hookup_irq, +}; diff -puN arch/mips/sibyte/swarm/Makefile~git-kgdb arch/mips/sibyte/swarm/Makefile --- a/arch/mips/sibyte/swarm/Makefile~git-kgdb +++ a/arch/mips/sibyte/swarm/Makefile @@ -1,3 +1 @@ lib-y = setup.o rtc_xicor1241.o rtc_m41t81.o - -lib-$(CONFIG_KGDB) += dbg_io.o diff -puN arch/mips/sibyte/swarm/dbg_io.c~git-kgdb /dev/null --- a/arch/mips/sibyte/swarm/dbg_io.c +++ /dev/null @@ -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 -puN arch/mips/tx4927/common/Makefile~git-kgdb arch/mips/tx4927/common/Makefile --- a/arch/mips/tx4927/common/Makefile~git-kgdb +++ a/arch/mips/tx4927/common/Makefile @@ -9,4 +9,3 @@ obj-y += tx4927_prom.o tx4927_setup.o tx4927_irq.o obj-$(CONFIG_TOSHIBA_FPCIB0) += smsc_fdc37m81x.o -obj-$(CONFIG_KGDB) += tx4927_dbgio.o diff -puN arch/mips/tx4927/common/tx4927_dbgio.c~git-kgdb /dev/null --- a/arch/mips/tx4927/common/tx4927_dbgio.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * linux/arch/mips/tx4927/common/tx4927_dbgio.c - * - * kgdb interface for gdb - * - * Author: MontaVista Software, Inc. - * source@mvista.com - * - * Copyright 2001-2002 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 as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include - -u8 getDebugChar(void) -{ - extern u8 txx9_sio_kdbg_rd(void); - return (txx9_sio_kdbg_rd()); -} - - -int putDebugChar(u8 byte) -{ - extern int txx9_sio_kdbg_wr( u8 ch ); - return (txx9_sio_kdbg_wr(byte)); -} diff -puN arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c~git-kgdb arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c --- a/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c~git-kgdb +++ a/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c @@ -76,7 +76,7 @@ #include #include #endif -#ifdef CONFIG_SERIAL_TXX9 +#if defined(CONFIG_SERIAL_TXX9) || defined(CONFIG_KGDB_TXX9) #include #include #include @@ -954,9 +954,10 @@ void __init toshiba_rbtx4927_setup(void) #endif /* CONFIG_PCI */ -#ifdef CONFIG_SERIAL_TXX9 +#if defined (CONFIG_SERIAL_TXX9) || defined(CONFIG_KGDB_TXX9) { extern int early_serial_txx9_setup(struct uart_port *port); + extern int txx9_kgdb_add_port(int n, struct uart_port *port); int i; struct uart_port req; for(i = 0; i < 2; i++) { @@ -968,7 +969,12 @@ void __init toshiba_rbtx4927_setup(void) req.irq = 32 + i; req.flags |= UPF_BUGGY_UART /*HAVE_CTS_LINE*/; req.uartclk = 50000000; +#ifdef CONFIG_SERIAL_TXX9 early_serial_txx9_setup(&req); +#endif +#ifdef CONFIG_KGDB_TXX9 + txx9_kgdb_add_port(i, &req); +#endif } } #ifdef CONFIG_SERIAL_TXX9_CONSOLE @@ -977,7 +983,7 @@ void __init toshiba_rbtx4927_setup(void) strcat(argptr, " console=ttyS0,38400"); } #endif -#endif +#endif /* defined(CONFIG_SERIAL_TXX9) || defined(CONFIG_KGDB_TXX9) */ #ifdef CONFIG_ROOT_NFS argptr = prom_getcmdline(); diff -puN arch/mips/tx4938/common/Makefile~git-kgdb arch/mips/tx4938/common/Makefile --- a/arch/mips/tx4938/common/Makefile~git-kgdb +++ a/arch/mips/tx4938/common/Makefile @@ -7,5 +7,4 @@ # obj-y += prom.o setup.o irq.o -obj-$(CONFIG_KGDB) += dbgio.o diff -puN arch/mips/tx4938/common/dbgio.c~git-kgdb /dev/null --- a/arch/mips/tx4938/common/dbgio.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * linux/arch/mips/tx4938/common/dbgio.c - * - * kgdb interface for gdb - * - * Author: MontaVista Software, Inc. - * source@mvista.com - * - * Copyright 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 as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Support for TX4938 in 2.6 - Hiroshi DOYU - */ - -#include -#include -#include - -extern u8 txx9_sio_kdbg_rd(void); -extern int txx9_sio_kdbg_wr( u8 ch ); - -u8 getDebugChar(void) -{ - return (txx9_sio_kdbg_rd()); -} - -int putDebugChar(u8 byte) -{ - return (txx9_sio_kdbg_wr(byte)); -} - diff -puN arch/mips/tx4938/toshiba_rbtx4938/setup.c~git-kgdb arch/mips/tx4938/toshiba_rbtx4938/setup.c --- a/arch/mips/tx4938/toshiba_rbtx4938/setup.c~git-kgdb +++ a/arch/mips/tx4938/toshiba_rbtx4938/setup.c @@ -30,7 +30,7 @@ #include #include #include -#ifdef CONFIG_SERIAL_TXX9 +#if defined(CONFIG_SERIAL_TXX9) || defined(CONFIG_KGDB_TXX9) #include #include #include @@ -886,9 +886,10 @@ void __init toshiba_rbtx4938_setup(void) set_io_port_base(RBTX4938_ETHER_BASE); #endif -#ifdef CONFIG_SERIAL_TXX9 +#if defined (CONFIG_SERIAL_TXX9) || defined (CONFIG_KGDB_TXX9) { extern int early_serial_txx9_setup(struct uart_port *port); + extern int txx9_kgdb_add_port(int n, struct uart_port *port); int i; struct uart_port req; for(i = 0; i < 2; i++) { @@ -900,7 +901,12 @@ void __init toshiba_rbtx4938_setup(void) req.irq = 32 + i; req.flags |= UPF_BUGGY_UART /*HAVE_CTS_LINE*/; req.uartclk = 50000000; +#ifdef CONFIG_SERIAL_TXX9 early_serial_txx9_setup(&req); +#endif +#ifdef CONFIG_KGDB_TXX9 + txx9_kgdb_add_port(i, &req); +#endif } } #ifdef CONFIG_SERIAL_TXX9_CONSOLE diff -puN arch/powerpc/Kconfig.debug~git-kgdb arch/powerpc/Kconfig.debug --- a/arch/powerpc/Kconfig.debug~git-kgdb +++ a/arch/powerpc/Kconfig.debug @@ -41,52 +41,9 @@ config HCALL_STATS This option will add a small amount of overhead to all hypervisor calls. -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 + depends on DEBUG_KERNEL && !KGDB help Include in-kernel hooks for the xmon kernel monitor/debugger. Unless you are intending to debug the kernel, say N here. @@ -116,6 +73,11 @@ config XMON_DISASSEMBLY to say Y here, unless you're building for a memory-constrained system. +config DEBUGGER + bool + depends on KGDB || XMON + default y + config IRQSTACKS bool "Use separate kernel stacks when processing interrupts" depends on PPC64 diff -puN arch/powerpc/kernel/Makefile~git-kgdb arch/powerpc/kernel/Makefile --- a/arch/powerpc/kernel/Makefile~git-kgdb +++ a/arch/powerpc/kernel/Makefile @@ -63,6 +63,12 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o +ifeq ($(CONFIG_PPC32),y) +obj-$(CONFIG_KGDB) += kgdb.o kgdb_setjmp32.o +else +obj-$(CONFIG_KGDB) += kgdb.o kgdb_setjmp64.o +endif + module-$(CONFIG_PPC64) += module_64.o obj-$(CONFIG_MODULES) += $(module-y) diff -puN /dev/null arch/powerpc/kernel/kgdb.c --- /dev/null +++ a/arch/powerpc/kernel/kgdb.c @@ -0,0 +1,393 @@ +/* + * arch/powerpc/kernel/kgdb.c + * + * PowerPC backend to the KGDB stub. + * + * Maintainer: Tom Rini + * + * Copied from arch/ppc/kernel/kgdb.c, updated for ppc64 + * + * 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 + +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. GDB and the kernel + * don't always agree on values, so we use constants taken from gdb-6.2. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x0100, 0x02 /* SIGINT */ }, /* system reset */ + { 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */ + { 0x0300, 0x0b /* SIGSEGV */ }, /* data access */ + { 0x0400, 0x0b /* SIGSEGV */ }, /* instruction access */ + { 0x0500, 0x02 /* SIGINT */ }, /* external interrupt */ + { 0x0600, 0x0a /* SIGBUS */ }, /* alignment */ + { 0x0700, 0x05 /* SIGTRAP */ }, /* program check */ + { 0x0800, 0x08 /* SIGFPE */ }, /* fp unavailable */ + { 0x0900, 0x0e /* SIGALRM */ }, /* decrementer */ + { 0x0c00, 0x14 /* SIGCHLD */ }, /* system call */ +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + { 0x2002, 0x05 /* SIGTRAP */ }, /* debug */ +#if defined(CONFIG_FSL_BOOKE) + { 0x2010, 0x08 /* SIGFPE */ }, /* spe unavailable */ + { 0x2020, 0x08 /* SIGFPE */ }, /* spe unavailable */ + { 0x2030, 0x08 /* SIGFPE */ }, /* spe fp data */ + { 0x2040, 0x08 /* SIGFPE */ }, /* spe fp data */ + { 0x2050, 0x08 /* SIGFPE */ }, /* spe fp round */ + { 0x2060, 0x0e /* SIGILL */ }, /* performace monitor */ + { 0x2900, 0x08 /* SIGFPE */ }, /* apu unavailable */ + { 0x3100, 0x0e /* SIGALRM */ }, /* fixed interval timer */ + { 0x3200, 0x02 /* SIGINT */ }, /* watchdog */ +#else /* ! CONFIG_FSL_BOOKE */ + { 0x1000, 0x0e /* SIGALRM */ }, /* prog interval timer */ + { 0x1010, 0x0e /* SIGALRM */ }, /* fixed interval timer */ + { 0x1020, 0x02 /* SIGINT */ }, /* watchdog */ + { 0x2010, 0x08 /* SIGFPE */ }, /* fp unavailable */ + { 0x2020, 0x08 /* SIGFPE */ }, /* ap unavailable */ +#endif +#else /* ! (defined(CONFIG_40x) || defined(CONFIG_BOOKE)) */ + { 0x0d00, 0x05 /* SIGTRAP */ }, /* single-step */ +#if defined(CONFIG_8xx) + { 0x1000, 0x04 /* SIGILL */ }, /* software emulation */ +#else /* ! CONFIG_8xx */ + { 0x0f00, 0x04 /* SIGILL */ }, /* performance monitor */ + { 0x0f20, 0x08 /* SIGFPE */ }, /* altivec unavailable */ + { 0x1300, 0x05 /* SIGTRAP */ }, /* instruction address break */ +#if defined(CONFIG_PPC64) + { 0x1200, 0x05 /* SIGILL */ }, /* system error */ + { 0x1500, 0x04 /* SIGILL */ }, /* soft patch */ + { 0x1600, 0x04 /* SIGILL */ }, /* maintenance */ + { 0x1700, 0x08 /* SIGFPE */ }, /* altivec assist */ + { 0x1800, 0x04 /* SIGILL */ }, /* thermal */ +#else /* ! CONFIG_PPC64 */ + { 0x1400, 0x02 /* SIGINT */ }, /* SMI */ + { 0x1600, 0x08 /* SIGFPE */ }, /* altivec assist */ + { 0x1700, 0x04 /* SIGILL */ }, /* TAU */ + { 0x2000, 0x05 /* SIGTRAP */ }, /* run mode */ +#endif +#endif +#endif + { 0x0000, 0x00 } /* Must be last */ +}; + +extern atomic_t cpu_doing_single_step; + +static int computeSignal(unsigned 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 */ +} + +static int kgdb_call_nmi_hook(struct pt_regs *regs) +{ + kgdb_nmihook(smp_processor_id(), regs); + return 0; +} + +#ifdef CONFIG_SMP +void kgdb_roundup_cpus(unsigned long flags) +{ + smp_send_debugger_break(MSG_ALL_BUT_SELF); +} +#endif + +/* KGDB functions to use existing PowerPC64 hooks. */ +static int kgdb_debugger(struct pt_regs *regs) +{ + return kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs); +} + +static int kgdb_breakpoint(struct pt_regs *regs) +{ + if (user_mode(regs)) + return 0; + + kgdb_handle_exception(0, SIGTRAP, 0, regs); + + if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr)) + regs->nip += 4; + + return 1; +} + +static int kgdb_singlestep(struct pt_regs *regs) +{ + struct thread_info *thread_info, *exception_thread_info; + + if (user_mode(regs)) + return 0; + + /* + * On Book E and perhaps other processsors, singlestep is handled on + * the critical exception stack. This causes current_thread_info() + * to fail, since it it locates the thread_info by masking off + * the low bits of the current stack pointer. We work around + * this issue by copying the thread_info from the kernel stack + * before calling kgdb_handle_exception, and copying it back + * afterwards. On most processors the copy is avoided since + * exception_thread_info == thread_info. + */ + thread_info = (struct thread_info *)(regs->gpr[1] & ~(THREAD_SIZE-1)); + exception_thread_info = current_thread_info(); + + if (thread_info != exception_thread_info) + memcpy(exception_thread_info, thread_info, sizeof *thread_info); + + kgdb_handle_exception(0, SIGTRAP, 0, regs); + + if (thread_info != exception_thread_info) + memcpy(thread_info, exception_thread_info, sizeof *thread_info); + + return 1; +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + if (user_mode(regs)) + return 0; + + kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs); + return 1; +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + if (user_mode(regs)) + return 0; + + kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs); + return 1; +} + +#define PACK64(ptr, src) do { *(ptr++) = (src); } while (0) + +#define PACK32(ptr, src) do { \ + u32 *ptr32; \ + ptr32 = (u32 *)ptr; \ + *(ptr32++) = (src); \ + ptr = (unsigned long *)ptr32; \ + } while (0) + + +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + unsigned long *ptr = gdb_regs; + int reg; + + memset(gdb_regs, 0, NUMREGBYTES); + + for (reg = 0; reg < 32; reg++) + PACK64(ptr, regs->gpr[reg]); + +#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_SPE + for (reg = 0; reg < 32; reg++) + PACK64(ptr, current->thread.evr[reg]); +#else + ptr += 32; +#endif +#else + /* fp registers not used by kernel, leave zero */ + ptr += 32 * 8 / sizeof(long); +#endif + + PACK64(ptr, regs->nip); + PACK64(ptr, regs->msr); + PACK32(ptr, regs->ccr); + PACK64(ptr, regs->link); + PACK64(ptr, regs->ctr); + PACK32(ptr, regs->xer); + + BUG_ON((unsigned long)ptr > + (unsigned long)(((void *)gdb_regs) + NUMREGBYTES)); +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + struct pt_regs *regs = (struct pt_regs *)(p->thread.ksp + + STACK_FRAME_OVERHEAD); + unsigned long *ptr = gdb_regs; + int reg; + + memset(gdb_regs, 0, NUMREGBYTES); + + /* Regs GPR0-2 */ + for (reg = 0; reg < 3; reg++) + PACK64(ptr, regs->gpr[reg]); + + /* Regs GPR3-13 are caller saved, not in regs->gpr[] */ + ptr += 11; + + /* Regs GPR14-31 */ + for (reg = 14; reg < 32; reg++) + PACK64(ptr, regs->gpr[reg]); + +#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_SPE + for (reg = 0; reg < 32; reg++) + PACK64(ptr, p->thread.evr[reg]); +#else + ptr += 32; +#endif +#else + /* fp registers not used by kernel, leave zero */ + ptr += 32 * 8 / sizeof(long); +#endif + + PACK64(ptr, regs->nip); + PACK64(ptr, regs->msr); + PACK32(ptr, regs->ccr); + PACK64(ptr, regs->link); + PACK64(ptr, regs->ctr); + PACK32(ptr, regs->xer); + + BUG_ON((unsigned long)ptr > + (unsigned long)(((void *)gdb_regs) + NUMREGBYTES)); +} + +#define UNPACK64(dest, ptr) do { dest = *(ptr++); } while (0) + +#define UNPACK32(dest, ptr) do { \ + u32 *ptr32; \ + ptr32 = (u32 *)ptr; \ + dest = *(ptr32++); \ + ptr = (unsigned long *)ptr32; \ + } while (0) + +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + unsigned long *ptr = gdb_regs; + int reg; +#ifdef CONFIG_SPE + union { + u32 v32[2]; + u64 v64; + } acc; +#endif + + for (reg = 0; reg < 32; reg++) + UNPACK64(regs->gpr[reg], ptr); + +#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_SPE + for (reg = 0; reg < 32; reg++) + UNPACK64(current->thread.evr[reg], ptr); +#else + ptr += 32; +#endif +#else + /* fp registers not used by kernel, leave zero */ + ptr += 32 * 8 / sizeof(int); +#endif + + UNPACK64(regs->nip, ptr); + UNPACK64(regs->msr, ptr); + UNPACK32(regs->ccr, ptr); + UNPACK64(regs->link, ptr); + UNPACK64(regs->ctr, ptr); + UNPACK32(regs->xer, ptr); + + BUG_ON((unsigned long)ptr > + (unsigned long)(((void *)gdb_regs) + NUMREGBYTES)); +} + +/* + * This function does PowerPC specific procesing for interfacing to gdb. + */ +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *linux_regs) +{ + char *ptr = &remcom_in_buffer[1]; + unsigned long addr; + + switch (remcom_in_buffer[0]) { + /* + * sAA..AA Step one instruction from AA..AA + * This will return an error to gdb .. + */ + case 's': + case 'c': + /* handle the optional parameter */ + if (kgdb_hex2long(&ptr, &addr)) + linux_regs->nip = addr; + + atomic_set(&cpu_doing_single_step, -1); + /* set the trace bit if we're stepping */ + if (remcom_in_buffer[0] == 's') { +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + mtspr(SPRN_DBCR0, + mfspr(SPRN_DBCR0) | DBCR0_IC | DBCR0_IDM); + linux_regs->msr |= MSR_DE; +#else + linux_regs->msr |= MSR_SE; +#endif + debugger_step = 1; + if (kgdb_contthread) + atomic_set(&cpu_doing_single_step, + smp_processor_id()); + } + return 0; + } + + return -1; +} + +/* + * Global data + */ +struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = {0x7d, 0x82, 0x10, 0x08}, +}; + +int kgdb_not_implemented(struct pt_regs *regs) +{ + return 0; +} + +int kgdb_arch_init(void) +{ +#ifdef CONFIG_XMON +#error Both XMON and KGDB selected in .config. Unselect one of them. +#endif + + __debugger_ipi = kgdb_call_nmi_hook; + __debugger = kgdb_debugger; + __debugger_bpt = kgdb_breakpoint; + __debugger_sstep = kgdb_singlestep; + __debugger_iabr_match = kgdb_iabr_match; + __debugger_dabr_match = kgdb_dabr_match; + __debugger_fault_handler = kgdb_not_implemented; + + return 0; +} + +arch_initcall(kgdb_arch_init); diff -puN /dev/null arch/powerpc/kernel/kgdb_setjmp32.S --- /dev/null +++ a/arch/powerpc/kernel/kgdb_setjmp32.S @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1996 Paul Mackerras + * + * 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 + + .text + +/* + * Save/restore state in case a memory access causes a fault. + * + * int kgdb_fault_setjmp(unsigned long *curr_context); + * void kgdb_fault_longjmp(unsigned long *curr_context); + */ + +_GLOBAL(kgdb_fault_setjmp) + mflr r0 + stw r0,0(r3) + stw r1,4(r3) + stw r2,8(r3) + mfcr r0 + stw r0,12(r3) + stmw r13,16(r3) + li r3,0 + blr + +_GLOBAL(kgdb_fault_longjmp) + lmw r13,16(r3) + lwz r0,12(r3) + mtcrf 0x38,r0 + lwz r0,0(r3) + lwz r1,4(r3) + lwz r2,8(r3) + mtlr r0 + mr r3,r1 + blr diff -puN /dev/null arch/powerpc/kernel/kgdb_setjmp64.S --- /dev/null +++ a/arch/powerpc/kernel/kgdb_setjmp64.S @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1996 Paul Mackerras + * + * 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 + + .text + +/* + * Save/restore state in case a memory access causes a fault. + * + * int kgdb_fault_setjmp(unsigned long *curr_context); + * void kgdb_fault_longjmp(unsigned long *curr_context); + */ + +_GLOBAL(kgdb_fault_setjmp) + mflr r0 + std r0,0(r3) + std r1,8(r3) + std r2,16(r3) + mfcr r0 + std r0,24(r3) + std r13,32(r3) + std r14,40(r3) + std r15,48(r3) + std r16,56(r3) + std r17,64(r3) + std r18,72(r3) + std r19,80(r3) + std r20,88(r3) + std r21,96(r3) + std r22,104(r3) + std r23,112(r3) + std r24,120(r3) + std r25,128(r3) + std r26,136(r3) + std r27,144(r3) + std r28,152(r3) + std r29,160(r3) + std r30,168(r3) + std r31,176(r3) + li r3,0 + blr + +_GLOBAL(kgdb_fault_longjmp) + ld r13,32(r3) + ld r14,40(r3) + ld r15,48(r3) + ld r16,56(r3) + ld r17,64(r3) + ld r18,72(r3) + ld r19,80(r3) + ld r20,88(r3) + ld r21,96(r3) + ld r22,104(r3) + ld r23,112(r3) + ld r24,120(r3) + ld r25,128(r3) + ld r26,136(r3) + ld r27,144(r3) + ld r28,152(r3) + ld r29,160(r3) + ld r30,168(r3) + ld r31,176(r3) + ld r0,24(r3) + mtcrf 0x38,r0 + ld r0,0(r3) + ld r1,8(r3) + ld r2,16(r3) + mtlr r0 + mr r3,r1 + blr diff -puN arch/powerpc/kernel/legacy_serial.c~git-kgdb arch/powerpc/kernel/legacy_serial.c --- a/arch/powerpc/kernel/legacy_serial.c~git-kgdb +++ a/arch/powerpc/kernel/legacy_serial.c @@ -11,6 +11,9 @@ #include #include #include +#ifdef CONFIG_KGDB_8250 +#include +#endif #undef DEBUG @@ -487,6 +490,9 @@ static int __init serial_dev_init(void) fixup_port_pio(i, np, port); if ((port->iotype == UPIO_MEM) || (port->iotype == UPIO_TSI)) fixup_port_mmio(i, np, port); +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_platform_port(i, port); +#endif } DBG("Registering platform serial ports\n"); diff -puN arch/powerpc/kernel/setup_32.c~git-kgdb arch/powerpc/kernel/setup_32.c --- a/arch/powerpc/kernel/setup_32.c~git-kgdb +++ a/arch/powerpc/kernel/setup_32.c @@ -45,10 +45,6 @@ #define DBG(fmt...) -#if defined CONFIG_KGDB -#include -#endif - extern void bootx_init(unsigned long r4, unsigned long phys); struct ide_machdep_calls ppc_ide_md; @@ -245,18 +241,6 @@ void __init setup_arch(char **cmdline_p) xmon_setup(); -#if defined(CONFIG_KGDB) - if (ppc_md.kgdb_map_scc) - ppc_md.kgdb_map_scc(); - set_debug_traps(); - if (strstr(cmd_line, "gdb")) { - if (ppc_md.progress) - ppc_md.progress("setup_arch: kgdb breakpoint", 0x4000); - printk("kgdb breakpoint activated\n"); - breakpoint(); - } -#endif - /* * Set cache line size based on type of cpu as a default. * Systems with OF can look in the properties on the cpu node(s) diff -puN arch/powerpc/mm/fault.c~git-kgdb arch/powerpc/mm/fault.c --- a/arch/powerpc/mm/fault.c~git-kgdb +++ a/arch/powerpc/mm/fault.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -413,6 +414,13 @@ void bad_page_fault(struct pt_regs *regs return; } +#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 + /* kernel has accessed a bad area */ switch (regs->trap) { diff -puN arch/powerpc/platforms/powermac/setup.c~git-kgdb arch/powerpc/platforms/powermac/setup.c --- a/arch/powerpc/platforms/powermac/setup.c~git-kgdb +++ a/arch/powerpc/platforms/powermac/setup.c @@ -98,8 +98,6 @@ extern struct machdep_calls pmac_md; int sccdbg; #endif -extern void zs_kgdb_hook(int tty_num); - sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; EXPORT_SYMBOL(sys_ctrler); @@ -330,10 +328,6 @@ static void __init pmac_setup_arch(void) l2cr_init(); #endif /* CONFIG_PPC32 */ -#ifdef CONFIG_KGDB - zs_kgdb_hook(0); -#endif - find_via_cuda(); find_via_pmu(); smu_init(); diff -puN arch/ppc/Kconfig.debug~git-kgdb arch/ppc/Kconfig.debug --- a/arch/ppc/Kconfig.debug~git-kgdb +++ a/arch/ppc/Kconfig.debug @@ -2,45 +2,9 @@ menu "Kernel hacking" source "lib/Kconfig.debug" -config KGDB - bool "Include kgdb kernel debugger" - depends on DEBUG_KERNEL && (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 DEBUG_KERNEL + depends on DEBUG_KERNEL && !KGDB help Include in-kernel hooks for the xmon kernel monitor/debugger. Unless you are intending to debug the kernel, say N here. diff -puN arch/ppc/amiga/config.c~git-kgdb arch/ppc/amiga/config.c --- a/arch/ppc/amiga/config.c~git-kgdb +++ a/arch/ppc/amiga/config.c @@ -753,17 +753,11 @@ static void amiga_serial_putc(char c) void amiga_serial_console_write(struct console *co, const char *s, unsigned int count) { -#if 0 /* def CONFIG_KGDB */ - /* FIXME:APUS GDB doesn't seem to like O-packages before it is - properly connected with the target. */ - __gdb_output_string (s, count); -#else while (count--) { if (*s == '\n') amiga_serial_putc('\r'); amiga_serial_putc(*s++); } -#endif } #ifdef CONFIG_SERIAL_CONSOLE diff -puN arch/ppc/kernel/Makefile~git-kgdb arch/ppc/kernel/Makefile --- a/arch/ppc/kernel/Makefile~git-kgdb +++ a/arch/ppc/kernel/Makefile @@ -14,7 +14,7 @@ obj-y := entry.o traps.o time.o misc. obj-$(CONFIG_MODULES) += ppc_ksyms.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_RAPIDIO) += rio.o -obj-$(CONFIG_KGDB) += ppc-stub.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb_setjmp32.o obj-$(CONFIG_SMP) += smp.o smp-tbsync.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o diff -puN /dev/null arch/ppc/kernel/kgdb.c --- /dev/null +++ a/arch/ppc/kernel/kgdb.c @@ -0,0 +1,346 @@ +/* + * arch/ppc/kernel/kgdb.c + * + * PowerPC backend to the KGDB stub. + * + * Maintainer: Tom Rini + * + * 1998 (c) Michael AK Tesch (tesch@cs.wisc.edu) + * Copyright (C) 2003 Timesys Corporation. + * Copyright (C) 2004, 2006 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 +#include +#include +#include +#include +#include +p#include +#include +#include + +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. GDB and the kernel + * don't always agree on values, so we use constants taken from gdb-6.2. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x0100, 0x02 /* SIGINT */ }, /* system reset */ + { 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */ + { 0x0300, 0x0b /* SIGSEGV */ }, /* data access */ + { 0x0400, 0x0b /* SIGSEGV */ }, /* instruction access */ + { 0x0500, 0x02 /* SIGINT */ }, /* external interrupt */ + { 0x0600, 0x0a /* SIGBUS */ }, /* alignment */ + { 0x0700, 0x05 /* SIGTRAP */ }, /* program check */ + { 0x0800, 0x08 /* SIGFPE */ }, /* fp unavailable */ + { 0x0900, 0x0e /* SIGALRM */ }, /* decrementer */ + { 0x0c00, 0x14 /* SIGCHLD */ }, /* system call */ +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + { 0x2002, 0x05 /* SIGTRAP */ }, /* debug */ +#if defined(CONFIG_FSL_BOOKE) + { 0x2010, 0x08 /* SIGFPE */ }, /* spe unavailable */ + { 0x2020, 0x08 /* SIGFPE */ }, /* spe unavailable */ + { 0x2030, 0x08 /* SIGFPE */ }, /* spe fp data */ + { 0x2040, 0x08 /* SIGFPE */ }, /* spe fp data */ + { 0x2050, 0x08 /* SIGFPE */ }, /* spe fp round */ + { 0x2060, 0x0e /* SIGILL */ }, /* performace monitor */ + { 0x2900, 0x08 /* SIGFPE */ }, /* apu unavailable */ + { 0x3100, 0x0e /* SIGALRM */ }, /* fixed interval timer */ + { 0x3200, 0x02 /* SIGINT */ }, /* watchdog */ +#else /* ! CONFIG_FSL_BOOKE */ + { 0x1000, 0x0e /* SIGALRM */ }, /* prog interval timer */ + { 0x1010, 0x0e /* SIGALRM */ }, /* fixed interval timer */ + { 0x1020, 0x02 /* SIGINT */ }, /* watchdog */ + { 0x2010, 0x08 /* SIGFPE */ }, /* fp unavailable */ + { 0x2020, 0x08 /* SIGFPE */ }, /* ap unavailable */ +#endif +#else /* ! (defined(CONFIG_40x) || defined(CONFIG_BOOKE)) */ + { 0x0d00, 0x05 /* SIGTRAP */ }, /* single-step */ +#if defined(CONFIG_8xx) + { 0x1000, 0x04 /* SIGILL */ }, /* software emulation */ +#else /* ! CONFIG_8xx */ + { 0x0f00, 0x04 /* SIGILL */ }, /* performance monitor */ + { 0x0f20, 0x08 /* SIGFPE */ }, /* altivec unavailable */ + { 0x1300, 0x05 /* SIGTRAP */ }, /* instruction address break */ + { 0x1400, 0x02 /* SIGINT */ }, /* SMI */ + { 0x1600, 0x08 /* SIGFPE */ }, /* altivec assist */ + { 0x1700, 0x04 /* SIGILL */ }, /* TAU */ + { 0x2000, 0x05 /* SIGTRAP */ }, /* run mode */ +#endif +#endif + { 0x0000, 0x00 } /* Must be last */ +}; + +extern atomic_t cpu_doing_single_step; + +static int computeSignal(unsigned 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 */ +} + +/* KGDB functions to use existing PowerPC hooks. */ +static void kgdb_debugger(struct pt_regs *regs) +{ + kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs); +} + +static int kgdb_breakpoint(struct pt_regs *regs) +{ + if (user_mode(regs)) + return 0; + + kgdb_handle_exception(0, SIGTRAP, 0, regs); + + if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr)) + regs->nip += 4; + + return 1; +} + +static int kgdb_singlestep(struct pt_regs *regs) +{ + struct thread_info *thread_info, *exception_thread_info; + + if (user_mode(regs)) + return 0; + /* + * On Book E and perhaps other processsors, singlestep is handled on + * the critical exception stack. This causes current_thread_info() + * to fail, since it it locates the thread_info by masking off + * the low bits of the current stack pointer. We work around + * this issue by copying the thread_info from the kernel stack + * before calling kgdb_handle_exception, and copying it back + * afterwards. On most processors the copy is avoided since + * exception_thread_info == thread_info. + */ + thread_info = (struct thread_info *)(regs->gpr[1] & ~(THREAD_SIZE-1)); + exception_thread_info = current_thread_info(); + + if (thread_info != exception_thread_info) + memcpy(exception_thread_info, thread_info, sizeof *thread_info); + + kgdb_handle_exception(0, SIGTRAP, 0, regs); + + if (thread_info != exception_thread_info) + memcpy(thread_info, exception_thread_info, sizeof *thread_info); + + return 1; +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + if (user_mode(regs)) + return 0; + + kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs); + return 1; +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + if (user_mode(regs)) + return 0; + + kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs); + return 1; +} + +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + unsigned long *ptr = gdb_regs; + int reg; + + memset(gdb_regs, 0, MAXREG * 4); + + for (reg = 0; reg < 32; reg++) + *(ptr++) = regs->gpr[reg]; + +#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_SPE + for (reg = 0; reg < 32; reg++) + *(ptr++) = current->thread.evr[reg]; +#else + ptr += 32; +#endif +#else + ptr += 64; +#endif + + *(ptr++) = regs->nip; + *(ptr++) = regs->msr; + *(ptr++) = regs->ccr; + *(ptr++) = regs->link; + *(ptr++) = regs->ctr; + *(ptr++) = regs->xer; + +#ifdef CONFIG_SPE + /* u64 acc */ + *(ptr++) = current->thread.acc >> 32; + *(ptr++) = current->thread.acc & 0xffffffff; + *(ptr++) = current->thread.spefscr; +#endif +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + struct pt_regs *regs = (struct pt_regs *)(p->thread.ksp + + STACK_FRAME_OVERHEAD); + unsigned long *ptr = gdb_regs; + int reg; + + memset(gdb_regs, 0, MAXREG * 4); + + /* Regs GPR0-2 */ + for (reg = 0; reg < 3; reg++) + *(ptr++) = regs->gpr[reg]; + + /* Regs GPR3-13 are not saved */ + ptr += 11; + + /* Regs GPR14-31 */ + for (reg = 14; reg < 32; reg++) + *(ptr++) = regs->gpr[reg]; + +#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_SPE + for (reg = 0; reg < 32; reg++) + *(ptr++) = p->thread.evr[reg]; +#else + ptr += 32; +#endif +#else + ptr += 64; +#endif + + *(ptr++) = regs->nip; + *(ptr++) = regs->msr; + *(ptr++) = regs->ccr; + *(ptr++) = regs->link; + *(ptr++) = regs->ctr; + *(ptr++) = regs->xer; + +#ifdef CONFIG_SPE + /* u64 acc */ + *(ptr++) = p->thread.acc >> 32; + *(ptr++) = p->thread.acc & 0xffffffff; + *(ptr++) = p->thread.spefscr; +#endif +} + +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + unsigned long *ptr = gdb_regs; + int reg; +#ifdef CONFIG_SPE + union { + u32 v32[2]; + u64 v64; + } acc; +#endif + + for (reg = 0; reg < 32; reg++) + regs->gpr[reg] = *(ptr++); + +#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_SPE + for (reg = 0; reg < 32; reg++) + current->thread.evr[reg] = *(ptr++); +#else + ptr += 32; +#endif +#else + ptr += 64; +#endif + + regs->nip = *(ptr++); + regs->msr = *(ptr++); + regs->ccr = *(ptr++); + regs->link = *(ptr++); + regs->ctr = *(ptr++); + regs->xer = *(ptr++); + +#ifdef CONFIG_SPE + /* u64 acc */ + acc.v32[0] = *(ptr++); + acc.v32[1] = *(ptr++); + current->thread.acc = acc.v64; + current->thread.spefscr = *(ptr++); +#endif +} + +/* + * This function does PowerPC specific processing for interfacing to gdb. + */ +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *linux_regs) +{ + char *ptr = &remcom_in_buffer[1]; + unsigned long addr; + + switch (remcom_in_buffer[0]) { + /* + * sAA..AA Step one instruction from AA..AA + * This will return an error to gdb .. + */ + case 's': + case 'c': + /* handle the optional parameter */ + if (kgdb_hex2long (&ptr, &addr)) + linux_regs->nip = addr; + + atomic_set(&cpu_doing_single_step, -1); + /* set the trace bit if we're stepping */ + if (remcom_in_buffer[0] == 's') { +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + mtspr(SPRN_DBCR0, + mfspr(SPRN_DBCR0) | DBCR0_IC | DBCR0_IDM); + linux_regs->msr |= MSR_DE; +#else + linux_regs->msr |= MSR_SE; +#endif + debugger_step = 1; + if (kgdb_contthread) + atomic_set(&cpu_doing_single_step, + smp_processor_id()); + } + return 0; + } + + return -1; +} + +/* + * Global data + */ +struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = {0x7d, 0x82, 0x10, 0x08}, +}; + +int kgdb_arch_init(void) +{ + debugger = kgdb_debugger; + debugger_bpt = kgdb_breakpoint; + debugger_sstep = kgdb_singlestep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; + + return 0; +} + +arch_initcall(kgdb_arch_init); diff -puN /dev/null arch/ppc/kernel/kgdb_setjmp32.S --- /dev/null +++ a/arch/ppc/kernel/kgdb_setjmp32.S @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1996 Paul Mackerras + * + * 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 + + .text + +/* + * Save/restore state in case a memory access causes a fault. + * + * int kgdb_fault_setjmp(unsigned long *curr_context); + * void kgdb_fault_longjmp(unsigned long *curr_context); + */ + +_GLOBAL(kgdb_fault_setjmp) + mflr r0 + stw r0,0(r3) + stw r1,4(r3) + stw r2,8(r3) + mfcr r0 + stw r0,12(r3) + stmw r13,16(r3) + li r3,0 + blr + +_GLOBAL(kgdb_fault_longjmp) + lmw r13,16(r3) + lwz r0,12(r3) + mtcrf 0x38,r0 + lwz r0,0(r3) + lwz r1,4(r3) + lwz r2,8(r3) + mtlr r0 + mr r3,r1 + blr diff -puN arch/ppc/kernel/ppc-stub.c~git-kgdb /dev/null --- a/arch/ppc/kernel/ppc-stub.c +++ /dev/null @@ -1,866 +0,0 @@ -/* - * ppc-stub.c: KGDB support for the Linux kernel. - * - * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC - * some stuff borrowed from Paul Mackerras' xmon - * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) - * - * Modifications to run under Linux - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * - * This file originally came from the gdb sources, and the - * copyright notices have been retained below. - */ - -/**************************************************************************** - - THIS SOFTWARE IS NOT COPYRIGHTED - - HP offers the following for use in the public domain. HP makes no - warranty with regard to the software or its performance and the - user accepts the software "AS IS" with all faults. - - HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD - TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - -****************************************************************************/ - -/**************************************************************************** - * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ - * - * Module name: remcom.c $ - * Revision: 1.34 $ - * Date: 91/03/09 12:29:49 $ - * Contributor: Lake Stevens Instrument Division$ - * - * Description: low level support for gdb debugger. $ - * - * Considerations: only works on target hardware $ - * - * Written by: Glenn Engel $ - * ModuleState: Experimental $ - * - * NOTES: See Below $ - * - * Modified for SPARC by Stu Grossman, Cygnus Support. - * - * This code has been extensively tested on the Fujitsu SPARClite demo board. - * - * To enable debugger support, two things need to happen. One, a - * call to set_debug_traps() is necessary in order to allow any breakpoints - * or error conditions to be properly intercepted and reported to gdb. - * Two, a breakpoint needs to be generated to begin communication. This - * is most easily accomplished by a call to breakpoint(). Breakpoint() - * simulates a breakpoint by executing a trap #1. - * - ************* - * - * The following gdb commands are supported: - * - * command function Return value - * - * g return the value of the CPU registers hex data or ENN - * G set the value of the CPU registers OK or ENN - * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz - * - * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN - * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN - * - * c Resume at current address SNN ( signal NN) - * cAA..AA Continue at address AA..AA SNN - * - * s Step one instruction SNN - * sAA..AA Step one instruction from AA..AA SNN - * - * k kill - * - * ? What was the last sigval ? SNN (signal NN) - * - * bBB..BB Set baud rate to BB..BB OK or BNN, then sets - * baud rate - * - * All commands and responses are sent with a packet which includes a - * checksum. A packet consists of - * - * $#. - * - * where - * :: - * :: > - * - * When a packet is received, it is first acknowledged with either '+' or '-'. - * '+' indicates a successful transfer. '-' indicates a failed transfer. - * - * Example: - * - * Host: Reply: - * $m0,10#2a +$00010203040506070809101112131415#42 - * - ****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -void breakinst(void); - -/* - * 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 remcomInBuffer[BUFMAX]; -static char remcomOutBuffer[BUFMAX]; - -static int initialized; -static int kgdb_active; -static int kgdb_started; -static u_int fault_jmp_buf[100]; -static int kdebug; - - -static const char hexchars[]="0123456789abcdef"; - -/* Place where we save old trap entries for restoration - sparc*/ -/* struct tt_entry kgdb_savettable[256]; */ -/* typedef void (*trapfunc_t)(void); */ - -static void kgdb_fault_handler(struct pt_regs *regs); -static int handle_exception (struct pt_regs *regs); - -#if 0 -/* Install an exception handler for kgdb */ -static void exceptionHandler(int tnum, unsigned int *tfunc) -{ - /* We are dorking with a live trap table, all irqs off */ -} -#endif - -int -kgdb_setjmp(long *buf) -{ - asm ("mflr 0; stw 0,0(%0);" - "stw 1,4(%0); stw 2,8(%0);" - "mfcr 0; stw 0,12(%0);" - "stmw 13,16(%0)" - : : "r" (buf)); - /* XXX should save fp regs as well */ - return 0; -} -void -kgdb_longjmp(long *buf, int val) -{ - if (val == 0) - val = 1; - asm ("lmw 13,16(%0);" - "lwz 0,12(%0); mtcrf 0x38,0;" - "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" - "mtlr 0; mr 3,%1" - : : "r" (buf), "r" (val)); -} -/* Convert ch from a hex digit to an int */ -static int -hex(unsigned char ch) -{ - if (ch >= 'a' && ch <= 'f') - return ch-'a'+10; - if (ch >= '0' && ch <= '9') - return ch-'0'; - if (ch >= 'A' && ch <= 'F') - return ch-'A'+10; - return -1; -} - -/* Convert the memory pointed to by mem into hex, placing result in buf. - * Return a pointer to the last char put in buf (null), in case of mem fault, - * return 0. - */ -static unsigned char * -mem2hex(const char *mem, char *buf, int count) -{ - unsigned char ch; - unsigned short tmp_s; - unsigned long tmp_l; - - if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { - debugger_fault_handler = kgdb_fault_handler; - - /* Accessing 16 bit and 32 bit objects in a single - ** load instruction is required to avoid bad side - ** effects for some IO registers. - */ - - if ((count == 2) && (((long)mem & 1) == 0)) { - tmp_s = *(unsigned short *)mem; - mem += 2; - *buf++ = hexchars[(tmp_s >> 12) & 0xf]; - *buf++ = hexchars[(tmp_s >> 8) & 0xf]; - *buf++ = hexchars[(tmp_s >> 4) & 0xf]; - *buf++ = hexchars[tmp_s & 0xf]; - - } else if ((count == 4) && (((long)mem & 3) == 0)) { - tmp_l = *(unsigned int *)mem; - mem += 4; - *buf++ = hexchars[(tmp_l >> 28) & 0xf]; - *buf++ = hexchars[(tmp_l >> 24) & 0xf]; - *buf++ = hexchars[(tmp_l >> 20) & 0xf]; - *buf++ = hexchars[(tmp_l >> 16) & 0xf]; - *buf++ = hexchars[(tmp_l >> 12) & 0xf]; - *buf++ = hexchars[(tmp_l >> 8) & 0xf]; - *buf++ = hexchars[(tmp_l >> 4) & 0xf]; - *buf++ = hexchars[tmp_l & 0xf]; - - } else { - while (count-- > 0) { - ch = *mem++; - *buf++ = hexchars[ch >> 4]; - *buf++ = hexchars[ch & 0xf]; - } - } - - } else { - /* error condition */ - } - debugger_fault_handler = NULL; - *buf = 0; - return buf; -} - -/* convert the hex array pointed to by buf into binary to be placed in mem - * return a pointer to the character AFTER the last byte written. -*/ -static char * -hex2mem(char *buf, char *mem, int count) -{ - unsigned char ch; - int i; - char *orig_mem; - unsigned short tmp_s; - unsigned long tmp_l; - - orig_mem = mem; - - if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { - debugger_fault_handler = kgdb_fault_handler; - - /* Accessing 16 bit and 32 bit objects in a single - ** store instruction is required to avoid bad side - ** effects for some IO registers. - */ - - if ((count == 2) && (((long)mem & 1) == 0)) { - tmp_s = hex(*buf++) << 12; - tmp_s |= hex(*buf++) << 8; - tmp_s |= hex(*buf++) << 4; - tmp_s |= hex(*buf++); - - *(unsigned short *)mem = tmp_s; - mem += 2; - - } else if ((count == 4) && (((long)mem & 3) == 0)) { - tmp_l = hex(*buf++) << 28; - tmp_l |= hex(*buf++) << 24; - tmp_l |= hex(*buf++) << 20; - tmp_l |= hex(*buf++) << 16; - tmp_l |= hex(*buf++) << 12; - tmp_l |= hex(*buf++) << 8; - tmp_l |= hex(*buf++) << 4; - tmp_l |= hex(*buf++); - - *(unsigned long *)mem = tmp_l; - mem += 4; - - } else { - for (i=0; i# */ -static void -getpacket(char *buffer) -{ - unsigned char checksum; - unsigned char xmitcsum; - int i; - int count; - unsigned char ch; - - do { - /* wait around for the start character, ignore all other - * characters */ - while ((ch = (getDebugChar() & 0x7f)) != '$') ; - - checksum = 0; - xmitcsum = -1; - - count = 0; - - /* now, read until a # or end of buffer is found */ - while (count < BUFMAX) { - ch = getDebugChar() & 0x7f; - if (ch == '#') - break; - checksum = checksum + ch; - buffer[count] = ch; - count = count + 1; - } - - if (count >= BUFMAX) - continue; - - buffer[count] = 0; - - if (ch == '#') { - xmitcsum = hex(getDebugChar() & 0x7f) << 4; - xmitcsum |= hex(getDebugChar() & 0x7f); - if (checksum != xmitcsum) - putDebugChar('-'); /* failed checksum */ - else { - putDebugChar('+'); /* successful transfer */ - /* if a sequence char is present, reply the ID */ - if (buffer[2] == ':') { - putDebugChar(buffer[0]); - putDebugChar(buffer[1]); - /* remove sequence chars from buffer */ - count = strlen(buffer); - for (i=3; i <= count; i++) - buffer[i-3] = buffer[i]; - } - } - } - } while (checksum != xmitcsum); -} - -/* send the packet in buffer. */ -static void putpacket(unsigned char *buffer) -{ - unsigned char checksum; - int count; - unsigned char ch, recv; - - /* $#. */ - do { - putDebugChar('$'); - checksum = 0; - count = 0; - - while ((ch = buffer[count])) { - putDebugChar(ch); - checksum += ch; - count += 1; - } - - putDebugChar('#'); - putDebugChar(hexchars[checksum >> 4]); - putDebugChar(hexchars[checksum & 0xf]); - recv = getDebugChar(); - } while ((recv & 0x7f) != '+'); -} - -static void kgdb_flush_cache_all(void) -{ - flush_instruction_cache(); -} - -/* Set up exception handlers for tracing and breakpoints - * [could be called kgdb_init()] - */ -void set_debug_traps(void) -{ -#if 0 - unsigned char c; - - save_and_cli(flags); - - /* In case GDB is started before us, ack any packets (presumably - * "$?#xx") sitting there. - * - * I've found this code causes more problems than it solves, - * so that's why it's commented out. GDB seems to work fine - * now starting either before or after the kernel -bwb - */ - - while((c = getDebugChar()) != '$'); - while((c = getDebugChar()) != '#'); - c = getDebugChar(); /* eat first csum byte */ - c = getDebugChar(); /* eat second csum byte */ - putDebugChar('+'); /* ack it */ -#endif - debugger = kgdb; - debugger_bpt = kgdb_bpt; - debugger_sstep = kgdb_sstep; - debugger_iabr_match = kgdb_iabr_match; - debugger_dabr_match = kgdb_dabr_match; - - initialized = 1; -} - -static void kgdb_fault_handler(struct pt_regs *regs) -{ - kgdb_longjmp((long*)fault_jmp_buf, 1); -} - -int kgdb_bpt(struct pt_regs *regs) -{ - return handle_exception(regs); -} - -int kgdb_sstep(struct pt_regs *regs) -{ - return handle_exception(regs); -} - -void kgdb(struct pt_regs *regs) -{ - handle_exception(regs); -} - -int kgdb_iabr_match(struct pt_regs *regs) -{ - printk(KERN_ERR "kgdb doesn't support iabr, what?!?\n"); - return handle_exception(regs); -} - -int kgdb_dabr_match(struct pt_regs *regs) -{ - printk(KERN_ERR "kgdb doesn't support dabr, what?!?\n"); - return handle_exception(regs); -} - -/* Convert the hardware trap type code to a unix signal number. */ -/* - * This table contains the mapping between PowerPC hardware trap types, and - * signals, which are primarily what GDB understands. - */ -static struct hard_trap_info -{ - unsigned int tt; /* Trap type code for powerpc */ - unsigned char signo; /* Signal that we map this trap into */ -} hard_trap_info[] = { -#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) - { 0x100, SIGINT }, /* critical input interrupt */ - { 0x200, SIGSEGV }, /* machine check */ - { 0x300, SIGSEGV }, /* data storage */ - { 0x400, SIGBUS }, /* instruction storage */ - { 0x500, SIGINT }, /* interrupt */ - { 0x600, SIGBUS }, /* alignment */ - { 0x700, SIGILL }, /* program */ - { 0x800, SIGILL }, /* reserved */ - { 0x900, SIGILL }, /* reserved */ - { 0xa00, SIGILL }, /* reserved */ - { 0xb00, SIGILL }, /* reserved */ - { 0xc00, SIGCHLD }, /* syscall */ - { 0xd00, SIGILL }, /* reserved */ - { 0xe00, SIGILL }, /* reserved */ - { 0xf00, SIGILL }, /* reserved */ - /* - ** 0x1000 PIT - ** 0x1010 FIT - ** 0x1020 watchdog - ** 0x1100 data TLB miss - ** 0x1200 instruction TLB miss - */ - { 0x2002, SIGTRAP}, /* debug */ -#else - { 0x200, SIGSEGV }, /* machine check */ - { 0x300, SIGSEGV }, /* address error (store) */ - { 0x400, SIGBUS }, /* instruction bus error */ - { 0x500, SIGINT }, /* interrupt */ - { 0x600, SIGBUS }, /* alingment */ - { 0x700, SIGTRAP }, /* breakpoint trap */ - { 0x800, SIGFPE }, /* fpu unavail */ - { 0x900, SIGALRM }, /* decrementer */ - { 0xa00, SIGILL }, /* reserved */ - { 0xb00, SIGILL }, /* reserved */ - { 0xc00, SIGCHLD }, /* syscall */ - { 0xd00, SIGTRAP }, /* single-step/watch */ - { 0xe00, SIGFPE }, /* fp assist */ -#endif - { 0, 0} /* Must be last */ - -}; - -static int computeSignal(unsigned 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 */ -} - -#define PC_REGNUM 64 -#define SP_REGNUM 1 - -/* - * This function does all command processing for interfacing to gdb. - */ -static int -handle_exception (struct pt_regs *regs) -{ - int sigval; - int addr; - int length; - char *ptr; - unsigned int msr; - - /* We don't handle user-mode breakpoints. */ - if (user_mode(regs)) - return 0; - - if (debugger_fault_handler) { - debugger_fault_handler(regs); - panic("kgdb longjump failed!\n"); - } - if (kgdb_active) { - printk(KERN_ERR "interrupt while in kgdb, returning\n"); - return 0; - } - - kgdb_active = 1; - kgdb_started = 1; - -#ifdef KGDB_DEBUG - printk("kgdb: entering handle_exception; trap [0x%x]\n", - (unsigned int)regs->trap); -#endif - - kgdb_interruptible(0); - lock_kernel(); - msr = mfmsr(); - mtmsr(msr & ~MSR_EE); /* disable interrupts */ - - if (regs->nip == (unsigned long)breakinst) { - /* Skip over breakpoint trap insn */ - regs->nip += 4; - } - - /* reply to host that an exception has occurred */ - sigval = computeSignal(regs->trap); - ptr = remcomOutBuffer; - - *ptr++ = 'T'; - *ptr++ = hexchars[sigval >> 4]; - *ptr++ = hexchars[sigval & 0xf]; - *ptr++ = hexchars[PC_REGNUM >> 4]; - *ptr++ = hexchars[PC_REGNUM & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®s->nip, ptr, 4); - *ptr++ = ';'; - *ptr++ = hexchars[SP_REGNUM >> 4]; - *ptr++ = hexchars[SP_REGNUM & 0xf]; - *ptr++ = ':'; - ptr = mem2hex(((char *)regs) + SP_REGNUM*4, ptr, 4); - *ptr++ = ';'; - *ptr++ = 0; - - putpacket(remcomOutBuffer); - if (kdebug) - printk("remcomOutBuffer: %s\n", remcomOutBuffer); - - /* XXX We may want to add some features dealing with poking the - * XXX page tables, ... (look at sparc-stub.c for more info) - * XXX also required hacking to the gdb sources directly... - */ - - while (1) { - remcomOutBuffer[0] = 0; - - getpacket(remcomInBuffer); - switch (remcomInBuffer[0]) { - case '?': /* report most recent signal */ - remcomOutBuffer[0] = 'S'; - remcomOutBuffer[1] = hexchars[sigval >> 4]; - remcomOutBuffer[2] = hexchars[sigval & 0xf]; - remcomOutBuffer[3] = 0; - break; -#if 0 - case 'q': /* this screws up gdb for some reason...*/ - { - extern long _start, sdata, __bss_start; - - ptr = &remcomInBuffer[1]; - if (strncmp(ptr, "Offsets", 7) != 0) - break; - - ptr = remcomOutBuffer; - sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", - &_start, &sdata, &__bss_start); - break; - } -#endif - case 'd': - /* toggle debug flag */ - kdebug ^= 1; - break; - - case 'g': /* return the value of the CPU registers. - * some of them are non-PowerPC names :( - * they are stored in gdb like: - * struct { - * u32 gpr[32]; - * f64 fpr[32]; - * u32 pc, ps, cnd, lr; (ps=msr) - * u32 cnt, xer, mq; - * } - */ - { - int i; - ptr = remcomOutBuffer; - /* General Purpose Regs */ - ptr = mem2hex((char *)regs, ptr, 32 * 4); - /* Floating Point Regs - FIXME */ - /*ptr = mem2hex((char *), ptr, 32 * 8);*/ - for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ - ptr[i] = '0'; - } - ptr += 32*8*2; - /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ - ptr = mem2hex((char *)®s->nip, ptr, 4); - ptr = mem2hex((char *)®s->msr, ptr, 4); - ptr = mem2hex((char *)®s->ccr, ptr, 4); - ptr = mem2hex((char *)®s->link, ptr, 4); - ptr = mem2hex((char *)®s->ctr, ptr, 4); - ptr = mem2hex((char *)®s->xer, ptr, 4); - } - break; - - case 'G': /* set the value of the CPU registers */ - { - ptr = &remcomInBuffer[1]; - - /* - * If the stack pointer has moved, you should pray. - * (cause only god can help you). - */ - - /* General Purpose Regs */ - hex2mem(ptr, (char *)regs, 32 * 4); - - /* Floating Point Regs - FIXME?? */ - /*ptr = hex2mem(ptr, ??, 32 * 8);*/ - ptr += 32*8*2; - - /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ - ptr = hex2mem(ptr, (char *)®s->nip, 4); - ptr = hex2mem(ptr, (char *)®s->msr, 4); - ptr = hex2mem(ptr, (char *)®s->ccr, 4); - ptr = hex2mem(ptr, (char *)®s->link, 4); - ptr = hex2mem(ptr, (char *)®s->ctr, 4); - ptr = hex2mem(ptr, (char *)®s->xer, 4); - - strcpy(remcomOutBuffer,"OK"); - } - break; - case 'H': - /* don't do anything, yet, just acknowledge */ - hexToInt(&ptr, &addr); - strcpy(remcomOutBuffer,"OK"); - break; - - case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ - /* Try to read %x,%x. */ - - ptr = &remcomInBuffer[1]; - - if (hexToInt(&ptr, &addr) && *ptr++ == ',' - && hexToInt(&ptr, &length)) { - if (mem2hex((char *)addr, remcomOutBuffer, - length)) - break; - strcpy(remcomOutBuffer, "E03"); - } else - strcpy(remcomOutBuffer, "E01"); - break; - - case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ - /* Try to read '%x,%x:'. */ - - ptr = &remcomInBuffer[1]; - - if (hexToInt(&ptr, &addr) && *ptr++ == ',' - && hexToInt(&ptr, &length) - && *ptr++ == ':') { - if (hex2mem(ptr, (char *)addr, length)) - strcpy(remcomOutBuffer, "OK"); - else - strcpy(remcomOutBuffer, "E03"); - flush_icache_range(addr, addr+length); - } else - strcpy(remcomOutBuffer, "E02"); - break; - - - case 'k': /* kill the program, actually just continue */ - case 'c': /* cAA..AA Continue; address AA..AA optional */ - /* try to read optional parameter, pc unchanged if no parm */ - - ptr = &remcomInBuffer[1]; - if (hexToInt(&ptr, &addr)) - regs->nip = addr; - -/* Need to flush the instruction cache here, as we may have deposited a - * breakpoint, and the icache probably has no way of knowing that a data ref to - * some location may have changed something that is in the instruction cache. - */ - kgdb_flush_cache_all(); - mtmsr(msr); - - kgdb_interruptible(1); - unlock_kernel(); - kgdb_active = 0; - if (kdebug) { - printk("remcomInBuffer: %s\n", remcomInBuffer); - printk("remcomOutBuffer: %s\n", remcomOutBuffer); - } - return 1; - - case 's': - kgdb_flush_cache_all(); -#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) - mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC); - regs->msr |= MSR_DE; -#else - regs->msr |= MSR_SE; -#endif - unlock_kernel(); - kgdb_active = 0; - if (kdebug) { - printk("remcomInBuffer: %s\n", remcomInBuffer); - printk("remcomOutBuffer: %s\n", remcomOutBuffer); - } - return 1; - - case 'r': /* Reset (if user process..exit ???)*/ - panic("kgdb reset."); - break; - } /* switch */ - if (remcomOutBuffer[0] && kdebug) { - printk("remcomInBuffer: %s\n", remcomInBuffer); - printk("remcomOutBuffer: %s\n", remcomOutBuffer); - } - /* reply to the request */ - putpacket(remcomOutBuffer); - } /* while(1) */ -} - -/* This function will generate a breakpoint exception. It is used at the - beginning of a program to sync up with a debugger and can be used - otherwise as a quick means to stop program execution and "break" into - the debugger. */ - -void -breakpoint(void) -{ - if (!initialized) { - printk("breakpoint() called b4 kgdb init\n"); - return; - } - - asm(" .globl breakinst \n\ - breakinst: .long 0x7d821008"); -} - -#ifdef CONFIG_KGDB_CONSOLE -/* Output string in GDB O-packet format if GDB has connected. If nothing - output, returns 0 (caller must then handle output). */ -int -kgdb_output_string (const char* s, unsigned int count) -{ - char buffer[512]; - - if (!kgdb_started) - return 0; - - count = (count <= (sizeof(buffer) / 2 - 2)) - ? count : (sizeof(buffer) / 2 - 2); - - buffer[0] = 'O'; - mem2hex (s, &buffer[1], count); - putpacket(buffer); - - return 1; -} -#endif - -static void sysrq_handle_gdb(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) -{ - printk("Entering GDB stub\n"); - breakpoint(); -} -static struct sysrq_key_op sysrq_gdb_op = { - .handler = sysrq_handle_gdb, - .help_msg = "Gdb", - .action_msg = "GDB", -}; - -static int gdb_register_sysrq(void) -{ - printk("Registering GDB sysrq handler\n"); - register_sysrq_key('g', &sysrq_gdb_op); - return 0; -} -module_init(gdb_register_sysrq); diff -puN arch/ppc/kernel/setup.c~git-kgdb arch/ppc/kernel/setup.c --- a/arch/ppc/kernel/setup.c~git-kgdb +++ a/arch/ppc/kernel/setup.c @@ -48,10 +48,6 @@ #include #endif -#if defined CONFIG_KGDB -#include -#endif - extern void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7); extern void reloc_got2(unsigned long offset); @@ -509,18 +505,6 @@ void __init setup_arch(char **cmdline_p) #endif /* CONFIG_XMON */ if ( ppc_md.progress ) ppc_md.progress("setup_arch: enter", 0x3eab); -#if defined(CONFIG_KGDB) - if (ppc_md.kgdb_map_scc) - ppc_md.kgdb_map_scc(); - set_debug_traps(); - if (strstr(cmd_line, "gdb")) { - if (ppc_md.progress) - ppc_md.progress("setup_arch: kgdb breakpoint", 0x4000); - printk("kgdb breakpoint activated\n"); - breakpoint(); - } -#endif - /* * Set cache line size based on type of cpu as a default. * Systems with OF can look in the properties on the cpu node(s) diff -puN arch/ppc/mm/fault.c~git-kgdb arch/ppc/mm/fault.c --- a/arch/ppc/mm/fault.c~git-kgdb +++ a/arch/ppc/mm/fault.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -328,6 +329,14 @@ bad_page_fault(struct pt_regs *regs, uns return; } +#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 + /* kernel has accessed a bad area */ #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_kernel_faults) diff -puN arch/ppc/platforms/4xx/bamboo.c~git-kgdb arch/ppc/platforms/4xx/bamboo.c --- a/arch/ppc/platforms/4xx/bamboo.c~git-kgdb +++ a/arch/ppc/platforms/4xx/bamboo.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -338,10 +339,13 @@ bamboo_early_serial_map(void) printk("Early serial init of port 0 failed\n"); } -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(0, &port); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &port); +#endif port.membase = ioremap64(PPC440EP_UART1_ADDR, 8); port.irq = 1; @@ -352,10 +356,13 @@ bamboo_early_serial_map(void) printk("Early serial init of port 1 failed\n"); } -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(1, &port); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &port); +#endif port.membase = ioremap64(PPC440EP_UART2_ADDR, 8); port.irq = 3; @@ -366,10 +373,13 @@ bamboo_early_serial_map(void) printk("Early serial init of port 2 failed\n"); } -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(2, &port); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(2, &port); +#endif port.membase = ioremap64(PPC440EP_UART3_ADDR, 8); port.irq = 4; @@ -379,6 +389,10 @@ bamboo_early_serial_map(void) if (early_serial_setup(&port) != 0) { printk("Early serial init of port 3 failed\n"); } + +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(3, &port); +#endif } static void __init @@ -436,8 +450,5 @@ void __init platform_init(unsigned long ppc_md.nvram_read_val = todc_direct_read_val; ppc_md.nvram_write_val = todc_direct_write_val; -#ifdef CONFIG_KGDB - ppc_md.early_serial_map = bamboo_early_serial_map; -#endif } diff -puN arch/ppc/platforms/4xx/bubinga.c~git-kgdb arch/ppc/platforms/4xx/bubinga.c --- a/arch/ppc/platforms/4xx/bubinga.c~git-kgdb +++ a/arch/ppc/platforms/4xx/bubinga.c @@ -4,7 +4,7 @@ * Author: SAW (IBM), derived from walnut.c. * Maintained by MontaVista Software * - * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * 2003-2004 (c) MontaVista Softare 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. @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include @@ -101,17 +101,26 @@ bubinga_early_serial_map(void) port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; port.line = 0; - if (early_serial_setup(&port) != 0) { +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&port) != 0) printk("Early serial init of port 0 failed\n"); - } +#endif + +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &port); +#endif port.membase = (void*)ACTING_UART1_IO_BASE; port.irq = ACTING_UART1_INT; port.line = 1; - if (early_serial_setup(&port) != 0) { +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&port) != 0) printk("Early serial init of port 1 failed\n"); - } +#endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &port); +#endif } void __init @@ -258,8 +267,4 @@ platform_init(unsigned long r3, unsigned ppc_md.nvram_read_val = todc_direct_read_val; ppc_md.nvram_write_val = todc_direct_write_val; #endif -#ifdef CONFIG_KGDB - ppc_md.early_serial_map = bubinga_early_serial_map; -#endif } - diff -puN arch/ppc/platforms/4xx/ebony.c~git-kgdb arch/ppc/platforms/4xx/ebony.c --- a/arch/ppc/platforms/4xx/ebony.c~git-kgdb +++ a/arch/ppc/platforms/4xx/ebony.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -227,14 +228,20 @@ ebony_early_serial_map(void) port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; port.line = 0; - if (early_serial_setup(&port) != 0) { +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&port) != 0) printk("Early serial init of port 0 failed\n"); - } +#endif -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(0, &port); +#endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &port); +#endif +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) /* Purge TLB entry added in head_44x.S for early serial access */ _tlbie(UART0_IO_BASE); #endif @@ -244,14 +251,18 @@ ebony_early_serial_map(void) port.uartclk = clocks.uart1; port.line = 1; - if (early_serial_setup(&port) != 0) { +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&port) != 1) printk("Early serial init of port 1 failed\n"); - } +#endif -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(1, &port); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &port); +#endif } static void __init @@ -328,8 +339,4 @@ void __init platform_init(unsigned long ppc_md.nvram_read_val = todc_direct_read_val; ppc_md.nvram_write_val = todc_direct_write_val; -#ifdef CONFIG_KGDB - ppc_md.early_serial_map = ebony_early_serial_map; -#endif } - diff -puN arch/ppc/platforms/4xx/luan.c~git-kgdb arch/ppc/platforms/4xx/luan.c --- a/arch/ppc/platforms/4xx/luan.c~git-kgdb +++ a/arch/ppc/platforms/4xx/luan.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -284,6 +285,9 @@ luan_early_serial_map(void) if (early_serial_setup(&port) != 0) { printk("Early serial init of port 0 failed\n"); } +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &port); +#endif port.membase = ioremap64(PPC440SP_UART1_ADDR, 8); port.irq = UART1_INT; @@ -293,6 +297,9 @@ luan_early_serial_map(void) if (early_serial_setup(&port) != 0) { printk("Early serial init of port 1 failed\n"); } +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &port); +#endif port.membase = ioremap64(PPC440SP_UART2_ADDR, 8); port.irq = UART2_INT; @@ -302,6 +309,9 @@ luan_early_serial_map(void) if (early_serial_setup(&port) != 0) { printk("Early serial init of port 2 failed\n"); } +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(2, &port); +#endif } static void __init @@ -361,7 +371,4 @@ void __init platform_init(unsigned long ppc_md.get_irq = NULL; /* Set in ppc4xx_pic_init() */ ppc_md.calibrate_decr = luan_calibrate_decr; -#ifdef CONFIG_KGDB - ppc_md.early_serial_map = luan_early_serial_map; -#endif } diff -puN arch/ppc/platforms/4xx/ocotea.c~git-kgdb arch/ppc/platforms/4xx/ocotea.c --- a/arch/ppc/platforms/4xx/ocotea.c~git-kgdb +++ a/arch/ppc/platforms/4xx/ocotea.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -250,14 +251,20 @@ ocotea_early_serial_map(void) port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; port.line = 0; - if (early_serial_setup(&port) != 0) { +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&port) != 0) printk("Early serial init of port 0 failed\n"); - } +#endif -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(0, &port); +#endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &port); +#endif +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) /* Purge TLB entry added in head_44x.S for early serial access */ _tlbie(UART0_IO_BASE); #endif @@ -267,14 +274,18 @@ ocotea_early_serial_map(void) port.uartclk = clocks.uart1; port.line = 1; - if (early_serial_setup(&port) != 0) { +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&port) != 1) printk("Early serial init of port 1 failed\n"); - } +#endif -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(1, &port); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &port); +#endif } static void __init @@ -344,8 +355,5 @@ void __init platform_init(unsigned long ppc_md.nvram_read_val = todc_direct_read_val; ppc_md.nvram_write_val = todc_direct_write_val; -#ifdef CONFIG_KGDB - ppc_md.early_serial_map = ocotea_early_serial_map; -#endif ppc_md.init = ocotea_init; } diff -puN arch/ppc/platforms/4xx/taishan.c~git-kgdb arch/ppc/platforms/4xx/taishan.c --- a/arch/ppc/platforms/4xx/taishan.c~git-kgdb +++ a/arch/ppc/platforms/4xx/taishan.c @@ -311,7 +311,7 @@ taishan_early_serial_map(void) if (early_serial_setup(&port) != 0) printk("Early serial init of port 0 failed\n"); -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(0, &port); @@ -327,7 +327,7 @@ taishan_early_serial_map(void) if (early_serial_setup(&port) != 0) printk("Early serial init of port 1 failed\n"); -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG /* Configure debug serial access */ gen550_init(1, &port); #endif @@ -388,9 +388,6 @@ void __init platform_init(unsigned long ppc_md.calibrate_decr = taishan_calibrate_decr; -#ifdef CONFIG_KGDB - ppc_md.early_serial_map = taishan_early_serial_map; -#endif ppc_md.init = taishan_init; } diff -puN arch/ppc/platforms/4xx/xilinx_ml300.c~git-kgdb arch/ppc/platforms/4xx/xilinx_ml300.c --- a/arch/ppc/platforms/4xx/xilinx_ml300.c~git-kgdb +++ a/arch/ppc/platforms/4xx/xilinx_ml300.c @@ -16,6 +16,8 @@ #include #include #include +#include + #include #include @@ -41,9 +43,6 @@ * ppc4xx_map_io arch/ppc/syslib/ppc4xx_setup.c * start_kernel init/main.c * setup_arch arch/ppc/kernel/setup.c - * #if defined(CONFIG_KGDB) - * *ppc_md.kgdb_map_scc() == gen550_kgdb_map_scc - * #endif * *ppc_md.setup_arch == ml300_setup_arch this file * ppc4xx_setup_arch arch/ppc/syslib/ppc4xx_setup.c * ppc4xx_find_bridges arch/ppc/syslib/ppc405_pci.c diff -puN arch/ppc/platforms/4xx/xilinx_ml403.c~git-kgdb arch/ppc/platforms/4xx/xilinx_ml403.c --- a/arch/ppc/platforms/4xx/xilinx_ml403.c~git-kgdb +++ a/arch/ppc/platforms/4xx/xilinx_ml403.c @@ -43,9 +43,6 @@ * ppc4xx_map_io arch/ppc/syslib/ppc4xx_setup.c * start_kernel init/main.c * setup_arch arch/ppc/kernel/setup.c - * #if defined(CONFIG_KGDB) - * *ppc_md.kgdb_map_scc() == gen550_kgdb_map_scc - * #endif * *ppc_md.setup_arch == ml403_setup_arch this file * ppc4xx_setup_arch arch/ppc/syslib/ppc4xx_setup.c * ppc4xx_find_bridges arch/ppc/syslib/ppc405_pci.c diff -puN arch/ppc/platforms/4xx/yucca.c~git-kgdb arch/ppc/platforms/4xx/yucca.c --- a/arch/ppc/platforms/4xx/yucca.c~git-kgdb +++ a/arch/ppc/platforms/4xx/yucca.c @@ -387,7 +387,4 @@ void __init platform_init(unsigned long ppc_md.get_irq = NULL; /* Set in ppc4xx_pic_init() */ ppc_md.calibrate_decr = yucca_calibrate_decr; -#ifdef CONFIG_KGDB - ppc_md.early_serial_map = yucca_early_serial_map; -#endif } diff -puN arch/ppc/platforms/83xx/mpc834x_sys.c~git-kgdb arch/ppc/platforms/83xx/mpc834x_sys.c --- a/arch/ppc/platforms/83xx/mpc834x_sys.c~git-kgdb +++ a/arch/ppc/platforms/83xx/mpc834x_sys.c @@ -42,11 +42,11 @@ #include #include #include -#include #include #include #include +#include #ifndef CONFIG_PCI unsigned long isa_io_base = 0; @@ -114,7 +114,9 @@ mpc834x_sys_setup_arch(void) /* setup PCI host bridges */ mpc83xx_setup_hose(); #endif +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_KGDB_8250) mpc83xx_early_serial_map(); +#endif /* setup the board related info for the MDIO bus */ mdata = (struct gianfar_mdio_data *) ppc_sys_get_pdata(MPC83xx_MDIO); @@ -334,7 +336,6 @@ platform_init(unsigned long r3, unsigned ppc_md.get_rtc_time = NULL; ppc_md.calibrate_decr = mpc83xx_calibrate_decr; - ppc_md.early_serial_map = mpc83xx_early_serial_map; #if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ diff -puN arch/ppc/platforms/85xx/mpc8540_ads.c~git-kgdb arch/ppc/platforms/85xx/mpc8540_ads.c --- a/arch/ppc/platforms/85xx/mpc8540_ads.c~git-kgdb +++ a/arch/ppc/platforms/85xx/mpc8540_ads.c @@ -43,11 +43,11 @@ #include #include #include -#include #include #include #include +#include /* ************************************************************************ * @@ -77,7 +77,7 @@ mpc8540ads_setup_arch(void) mpc85xx_setup_hose(); #endif -#ifdef CONFIG_SERIAL_8250 +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_KGDB_8250) mpc85xx_early_serial_map(); #endif @@ -215,9 +215,6 @@ platform_init(unsigned long r3, unsigned #if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ -#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_KGDB) - ppc_md.early_serial_map = mpc85xx_early_serial_map; -#endif /* CONFIG_SERIAL_8250 && CONFIG_KGDB */ if (ppc_md.progress) ppc_md.progress("mpc8540ads_init(): exit", 0); diff -puN arch/ppc/platforms/85xx/mpc8560_ads.c~git-kgdb arch/ppc/platforms/85xx/mpc8560_ads.c --- a/arch/ppc/platforms/85xx/mpc8560_ads.c~git-kgdb +++ a/arch/ppc/platforms/85xx/mpc8560_ads.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include diff -puN arch/ppc/platforms/85xx/mpc85xx_cds_common.c~git-kgdb arch/ppc/platforms/85xx/mpc85xx_cds_common.c --- a/arch/ppc/platforms/85xx/mpc85xx_cds_common.c~git-kgdb +++ a/arch/ppc/platforms/85xx/mpc85xx_cds_common.c @@ -47,12 +47,12 @@ #include #include #include -#include #include #include #include #include +#include #ifndef CONFIG_PCI @@ -436,7 +436,7 @@ mpc85xx_cds_setup_arch(void) mpc85xx_setup_hose(); #endif -#ifdef CONFIG_SERIAL_8250 +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_KGDB_8250) mpc85xx_early_serial_map(); #endif @@ -590,9 +590,6 @@ platform_init(unsigned long r3, unsigned #if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ -#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_KGDB) - ppc_md.early_serial_map = mpc85xx_early_serial_map; -#endif /* CONFIG_SERIAL_8250 && CONFIG_KGDB */ if (ppc_md.progress) ppc_md.progress("mpc85xx_cds_init(): exit", 0); diff -puN arch/ppc/platforms/85xx/sbc8560.c~git-kgdb arch/ppc/platforms/85xx/sbc8560.c --- a/arch/ppc/platforms/85xx/sbc8560.c~git-kgdb +++ a/arch/ppc/platforms/85xx/sbc8560.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -44,14 +45,13 @@ #include #include #include -#include #include #include #include #include +#include -#ifdef CONFIG_SERIAL_8250 static void __init sbc8560_early_serial_map(void) { @@ -67,12 +67,16 @@ sbc8560_early_serial_map(void) uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART0_SIZE); uart_req.type = PORT_16650; -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) - gen550_init(0, &uart_req); +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&uart_req) != 0) + printk(KERN_ERR "Early serial init of port 0 failed\n"); +#endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + gen550_init(0, &uart_req); +#endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &uart_req); #endif - - if (early_serial_setup(&uart_req) != 0) - printk("Early serial init of port 0 failed\n"); /* Assume early_serial_setup() doesn't modify uart_req */ uart_req.line = 1; @@ -80,14 +84,17 @@ sbc8560_early_serial_map(void) uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART1_SIZE); uart_req.irq = MPC85xx_IRQ_EXT10; -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) - gen550_init(1, &uart_req); +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&uart_req) != 0) + printk(KERN_ERR "Early serial init of port 0 failed\n"); #endif - - if (early_serial_setup(&uart_req) != 0) - printk("Early serial init of port 1 failed\n"); -} +#ifdef CONFIG_SERIAL_TEXT_DEBUG + gen550_init(0, &uart_req); +#endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &uart_req); #endif +} /* ************************************************************************ * @@ -116,9 +123,7 @@ sbc8560_setup_arch(void) /* setup PCI host bridges */ mpc85xx_setup_hose(); #endif -#ifdef CONFIG_SERIAL_8250 sbc8560_early_serial_map(); -#endif #ifdef CONFIG_SERIAL_TEXT_DEBUG /* Invalidate the entry we stole earlier the serial ports * should be properly mapped */ @@ -225,9 +230,6 @@ platform_init(unsigned long r3, unsigned #if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ -#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_KGDB) - ppc_md.early_serial_map = sbc8560_early_serial_map; -#endif /* CONFIG_SERIAL_8250 && CONFIG_KGDB */ if (ppc_md.progress) ppc_md.progress("sbc8560_init(): exit", 0); diff -puN arch/ppc/platforms/85xx/tqm85xx.c~git-kgdb arch/ppc/platforms/85xx/tqm85xx.c --- a/arch/ppc/platforms/85xx/tqm85xx.c~git-kgdb +++ a/arch/ppc/platforms/85xx/tqm85xx.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -55,6 +54,7 @@ #include #include #include +#include #ifndef CONFIG_PCI unsigned long isa_io_base = 0; @@ -121,7 +121,7 @@ tqm85xx_setup_arch(void) #endif #ifndef CONFIG_MPC8560 -#if defined(CONFIG_SERIAL_8250) +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_KGDB_8250) mpc85xx_early_serial_map(); #endif @@ -400,9 +400,6 @@ platform_init(unsigned long r3, unsigned #if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ -#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_KGDB) - ppc_md.early_serial_map = mpc85xx_early_serial_map; -#endif /* CONFIG_SERIAL_8250 && CONFIG_KGDB */ #endif /* CONFIG_MPC8560 */ if (ppc_md.progress) diff -puN arch/ppc/platforms/apus_setup.c~git-kgdb arch/ppc/platforms/apus_setup.c --- a/arch/ppc/platforms/apus_setup.c~git-kgdb +++ a/arch/ppc/platforms/apus_setup.c @@ -598,12 +598,6 @@ int __debug_serinit( void ) ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ -#ifdef CONFIG_KGDB - /* turn Rx interrupts on for GDB */ - amiga_custom.intena = IF_SETCLR | IF_RBF; - ser_RTSon(); -#endif - return 0; } diff -puN arch/ppc/platforms/chestnut.c~git-kgdb arch/ppc/platforms/chestnut.c --- a/arch/ppc/platforms/chestnut.c~git-kgdb +++ a/arch/ppc/platforms/chestnut.c @@ -35,9 +35,9 @@ #include #include #include -#include #include #include +#include #include static void __iomem *sram_base; /* Virtual addr of Internal SRAM */ @@ -493,7 +493,7 @@ chestnut_power_off(void) static void __init chestnut_map_io(void) { -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) io_block_mapping(CHESTNUT_UART_BASE, CHESTNUT_UART_BASE, 0x100000, _PAGE_IO); #endif @@ -567,9 +567,6 @@ platform_init(unsigned long r3, unsigned #if defined(CONFIG_SERIAL_TEXT_DEBUG) ppc_md.progress = gen550_progress; #endif -#if defined(CONFIG_KGDB) - ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; -#endif if (ppc_md.progress) ppc_md.progress("chestnut_init(): exit", 0); diff -puN arch/ppc/platforms/ev64260.c~git-kgdb arch/ppc/platforms/ev64260.c --- a/arch/ppc/platforms/ev64260.c~git-kgdb +++ a/arch/ppc/platforms/ev64260.c @@ -331,7 +331,7 @@ ev64260_early_serial_map(void) port.iotype = UPIO_MEM; port.flags = STD_COM_FLAGS; -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG gen550_init(0, &port); #endif @@ -569,7 +569,7 @@ ev64260_set_bat(void) return; } -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#ifdef CONFIG_SERIAL_TEXT_DEBUG static void __init ev64260_map_io(void) { @@ -625,20 +625,12 @@ platform_init(unsigned long r3, unsigned ppc_md.setup_io_mappings = ev64260_map_io; ppc_md.progress = gen550_progress; #endif -#if defined(CONFIG_KGDB) - ppc_md.setup_io_mappings = ev64260_map_io; - ppc_md.early_serial_map = ev64260_early_serial_map; -#endif #elif defined(CONFIG_SERIAL_MPSC_CONSOLE) #ifdef CONFIG_SERIAL_TEXT_DEBUG ppc_md.setup_io_mappings = ev64260_map_io; ppc_md.progress = mv64x60_mpsc_progress; mv64x60_progress_init(CONFIG_MV64X60_NEW_BASE); #endif /* CONFIG_SERIAL_TEXT_DEBUG */ -#ifdef CONFIG_KGDB - ppc_md.setup_io_mappings = ev64260_map_io; - ppc_md.early_serial_map = ev64260_early_serial_map; -#endif /* CONFIG_KGDB */ #endif diff -puN arch/ppc/platforms/hdpu.c~git-kgdb arch/ppc/platforms/hdpu.c --- a/arch/ppc/platforms/hdpu.c~git-kgdb +++ a/arch/ppc/platforms/hdpu.c @@ -281,25 +281,6 @@ static void __init hdpu_setup_bridge(voi #if defined(CONFIG_SERIAL_MPSC_CONSOLE) static void __init hdpu_early_serial_map(void) { -#ifdef CONFIG_KGDB - static char first_time = 1; - -#if defined(CONFIG_KGDB_TTYS0) -#define KGDB_PORT 0 -#elif defined(CONFIG_KGDB_TTYS1) -#define KGDB_PORT 1 -#else -#error "Invalid kgdb_tty port" -#endif - - if (first_time) { - gt_early_mpsc_init(KGDB_PORT, - B9600 | CS8 | CREAD | HUPCL | CLOCAL); - first_time = 0; - } - - return; -#endif } #endif diff -puN arch/ppc/platforms/lopec.c~git-kgdb arch/ppc/platforms/lopec.c --- a/arch/ppc/platforms/lopec.c~git-kgdb +++ a/arch/ppc/platforms/lopec.c @@ -32,7 +32,8 @@ #include #include #include -#include + +#include /* * Define all of the IRQ senses and polarities. Taken from the diff -puN arch/ppc/platforms/pplus.c~git-kgdb arch/ppc/platforms/pplus.c --- a/arch/ppc/platforms/pplus.c~git-kgdb +++ a/arch/ppc/platforms/pplus.c @@ -35,9 +35,9 @@ #include #include #include -#include #include +#include #include "pplus.h" #undef DUMP_DBATS @@ -893,9 +893,6 @@ platform_init(unsigned long r3, unsigned #ifdef CONFIG_SERIAL_TEXT_DEBUG ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_TEXT_DEBUG */ -#ifdef CONFIG_KGDB - ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; -#endif #ifdef CONFIG_SMP smp_ops = &pplus_smp_ops; #endif /* CONFIG_SMP */ diff -puN arch/ppc/platforms/radstone_ppc7d.c~git-kgdb arch/ppc/platforms/radstone_ppc7d.c --- a/arch/ppc/platforms/radstone_ppc7d.c~git-kgdb +++ a/arch/ppc/platforms/radstone_ppc7d.c @@ -85,7 +85,7 @@ unsigned char __res[sizeof(bd_t)]; * Serial port code *****************************************************************************/ -#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) +#ifdef CONFIG_SERIAL_TEXT_DEBUG static void __init ppc7d_early_serial_map(void) { #if defined(CONFIG_SERIAL_MPSC_CONSOLE) @@ -114,10 +114,10 @@ static void __init ppc7d_early_serial_ma if (early_serial_setup(&serial_req) != 0) printk(KERN_ERR "Early serial init of port 1 failed\n"); #else -#error CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX +#error CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX #endif } -#endif /* CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG */ +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ /***************************************************************************** * Low-level board support code @@ -1460,18 +1460,16 @@ void __init platform_init(unsigned long PPC7D_CPLD_COMS_COM4_TXEN, PPC7D_CPLD_COMS); #endif /* CONFIG_SERIAL_MPSC */ -#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) +#ifdef CONFIG_SERIAL_TEXT_DEBUG ppc7d_early_serial_map(); -#ifdef CONFIG_SERIAL_TEXT_DEBUG #if defined(CONFIG_SERIAL_MPSC_CONSOLE) ppc_md.progress = mv64x60_mpsc_progress; #elif defined(CONFIG_SERIAL_8250) ppc_md.progress = gen550_progress; #else -#error CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX +#error CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX #endif /* CONFIG_SERIAL_8250 */ #endif /* CONFIG_SERIAL_TEXT_DEBUG */ -#endif /* CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG */ /* Enable write access to user flash. This is necessary for * flash probe. diff -puN arch/ppc/platforms/sandpoint.c~git-kgdb arch/ppc/platforms/sandpoint.c --- a/arch/ppc/platforms/sandpoint.c~git-kgdb +++ a/arch/ppc/platforms/sandpoint.c @@ -95,9 +95,9 @@ #include #include #include -#include #include +#include #include "sandpoint.h" /* Set non-zero if an X2 Sandpoint detected. */ @@ -730,9 +730,6 @@ platform_init(unsigned long r3, unsigned ppc_md.nvram_read_val = todc_mc146818_read_val; ppc_md.nvram_write_val = todc_mc146818_write_val; -#ifdef CONFIG_KGDB - ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; -#endif #ifdef CONFIG_SERIAL_TEXT_DEBUG ppc_md.progress = gen550_progress; #endif diff -puN arch/ppc/platforms/spruce.c~git-kgdb arch/ppc/platforms/spruce.c --- a/arch/ppc/platforms/spruce.c~git-kgdb +++ a/arch/ppc/platforms/spruce.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -38,9 +39,9 @@ #include #include #include -#include #include +#include #include "spruce.h" @@ -179,26 +180,32 @@ spruce_early_serial_map(void) serial_req.membase = (u_char *)UART0_IO_BASE; serial_req.regshift = 0; -#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) - gen550_init(0, &serial_req); -#endif #ifdef CONFIG_SERIAL_8250 if (early_serial_setup(&serial_req) != 0) printk("Early serial init of port 0 failed\n"); #endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + gen550_init(0, &serial_req); +#endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &port); +#endif /* Assume early_serial_setup() doesn't modify serial_req */ serial_req.line = 1; serial_req.irq = UART1_INT; serial_req.membase = (u_char *)UART1_IO_BASE; -#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) - gen550_init(1, &serial_req); -#endif #ifdef CONFIG_SERIAL_8250 if (early_serial_setup(&serial_req) != 0) printk("Early serial init of port 1 failed\n"); #endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + gen550_init(1, &serial_req); +#endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &serial_req); +#endif } TODC_ALLOC(); @@ -317,7 +324,4 @@ platform_init(unsigned long r3, unsigned #ifdef CONFIG_SERIAL_TEXT_DEBUG ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_TEXT_DEBUG */ -#ifdef CONFIG_KGDB - ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; -#endif } diff -puN arch/ppc/syslib/Makefile~git-kgdb arch/ppc/syslib/Makefile --- a/arch/ppc/syslib/Makefile~git-kgdb +++ a/arch/ppc/syslib/Makefile @@ -78,7 +78,6 @@ obj-$(CONFIG_PCI_8260) += m82xx_pci.o p obj-$(CONFIG_8260_PCI9) += m8260_pci_erratum9.o obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o ifeq ($(CONFIG_PPC_GEN550),y) -obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o endif ifeq ($(CONFIG_SERIAL_MPSC_CONSOLE),y) diff -puN arch/ppc/syslib/gen550.h~git-kgdb arch/ppc/syslib/gen550.h --- a/arch/ppc/syslib/gen550.h~git-kgdb +++ a/arch/ppc/syslib/gen550.h @@ -11,4 +11,3 @@ extern void gen550_progress(char *, unsigned short); extern void gen550_init(int, struct uart_port *); -extern void gen550_kgdb_map_scc(void); diff -puN arch/ppc/syslib/gen550_kgdb.c~git-kgdb /dev/null --- a/arch/ppc/syslib/gen550_kgdb.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Generic 16550 kgdb support intended to be useful on a variety - * of platforms. To enable this support, it is necessary to set - * the CONFIG_GEN550 option. Any virtual mapping of the serial - * port(s) to be used can be accomplished by setting - * ppc_md.early_serial_map to a platform-specific mapping function. - * - * Adapted from ppc4xx_kgdb.c. - * - * Author: Matt Porter - * - * 2002-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. - */ - -#include -#include - -#include - -extern unsigned long serial_init(int, void *); -extern unsigned long serial_getc(unsigned long); -extern unsigned long serial_putc(unsigned long, unsigned char); - -#if defined(CONFIG_KGDB_TTYS0) -#define KGDB_PORT 0 -#elif defined(CONFIG_KGDB_TTYS1) -#define KGDB_PORT 1 -#elif defined(CONFIG_KGDB_TTYS2) -#define KGDB_PORT 2 -#elif defined(CONFIG_KGDB_TTYS3) -#define KGDB_PORT 3 -#else -#error "invalid kgdb_tty port" -#endif - -static volatile unsigned int kgdb_debugport; - -void putDebugChar(unsigned char c) -{ - if (kgdb_debugport == 0) - kgdb_debugport = serial_init(KGDB_PORT, NULL); - - serial_putc(kgdb_debugport, c); -} - -int getDebugChar(void) -{ - if (kgdb_debugport == 0) - kgdb_debugport = serial_init(KGDB_PORT, NULL); - - return(serial_getc(kgdb_debugport)); -} - -void kgdb_interruptible(int enable) -{ - return; -} - -void putDebugString(char* str) -{ - while (*str != '\0') { - putDebugChar(*str); - str++; - } - putDebugChar('\r'); - return; -} - -/* - * Note: gen550_init() must be called already on the port we are going - * to use. - */ -void -gen550_kgdb_map_scc(void) -{ - printk(KERN_DEBUG "kgdb init\n"); - if (ppc_md.early_serial_map) - ppc_md.early_serial_map(); - kgdb_debugport = serial_init(KGDB_PORT, NULL); -} diff -puN arch/ppc/syslib/ibm44x_common.c~git-kgdb arch/ppc/syslib/ibm44x_common.c --- a/arch/ppc/syslib/ibm44x_common.c~git-kgdb +++ a/arch/ppc/syslib/ibm44x_common.c @@ -192,9 +192,6 @@ void __init ibm44x_platform_init(unsigne #ifdef CONFIG_SERIAL_TEXT_DEBUG ppc_md.progress = gen550_progress; #endif /* CONFIG_SERIAL_TEXT_DEBUG */ -#ifdef CONFIG_KGDB - ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; -#endif /* * The Abatron BDI JTAG debugger does not tolerate others diff -puN arch/ppc/syslib/mv64x60.c~git-kgdb arch/ppc/syslib/mv64x60.c --- a/arch/ppc/syslib/mv64x60.c~git-kgdb +++ a/arch/ppc/syslib/mv64x60.c @@ -242,6 +242,12 @@ static struct resource mv64x60_mpsc0_res .end = MV64x60_IRQ_SDMA_0, .flags = IORESOURCE_IRQ, }, + [4] = { + .name = "mpsc 0 irq", + .start = MV64x60_IRQ_MPSC_0, + .end = MV64x60_IRQ_MPSC_0, + .flags = IORESOURCE_IRQ, + }, }; static struct platform_device mpsc0_device = { @@ -299,6 +305,12 @@ static struct resource mv64x60_mpsc1_res .end = MV64360_IRQ_SDMA_1, .flags = IORESOURCE_IRQ, }, + [4] = { + .name = "mpsc 1 irq", + .start = MV64360_IRQ_MPSC_1, + .end = MV64360_IRQ_MPSC_1, + .flags = IORESOURCE_IRQ, + }, }; static struct platform_device mpsc1_device = { @@ -1462,12 +1474,47 @@ mv64x60_pd_fixup(struct mv64x60_handle * static int __init mv64x60_add_pds(void) { - return platform_add_devices(mv64x60_pd_devs, - ARRAY_SIZE(mv64x60_pd_devs)); + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(mv64x60_pd_devs); i++) { + if (mv64x60_pd_devs[i]) { + ret = platform_device_register(mv64x60_pd_devs[i]); + } + if (ret) { + while (--i >= 0) + platform_device_unregister(mv64x60_pd_devs[i]); + break; + } + } + return ret; } arch_initcall(mv64x60_add_pds); /* + * mv64x60_early_get_pdev_data() + * + * Get the data associated with a platform device by name and number. + */ +struct platform_device *__init +mv64x60_early_get_pdev_data(const char *name, int id, int remove) +{ + int i; + struct platform_device *pdev; + + for (i = 0; i < ARRAY_SIZE(mv64x60_pd_devs); i++) { + pdev = mv64x60_pd_devs[i]; + if (pdev && + pdev->id == id && + !strcmp(pdev->name, name)) { + if (remove) + mv64x60_pd_devs[i] = NULL; + return pdev; + } + } + return NULL; +} + +/* ***************************************************************************** * * GT64260-Specific Routines @@ -1794,12 +1841,16 @@ gt64260a_chip_specific_init(struct mv64x mv64x60_mpsc0_pdata.cache_mgmt = 1; mv64x60_mpsc1_pdata.mirror_regs = 1; mv64x60_mpsc1_pdata.cache_mgmt = 1; - - if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0)) - != NULL) { + r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0); + if (r != NULL) { r->start = MV64x60_IRQ_SDMA_0; r->end = MV64x60_IRQ_SDMA_0; } + r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 1); + if (r != NULL) { + r->start = GT64260_IRQ_MPSC_1; + r->end = GT64260_IRQ_MPSC_1; + } #endif } diff -puN arch/ppc/syslib/mv64x60_dbg.c~git-kgdb arch/ppc/syslib/mv64x60_dbg.c --- a/arch/ppc/syslib/mv64x60_dbg.c~git-kgdb +++ a/arch/ppc/syslib/mv64x60_dbg.c @@ -34,7 +34,7 @@ static struct mv64x60_handle mv64x60_dbg void mv64x60_progress_init(u32 base) { - mv64x60_dbg_bh.v_base = base; + mv64x60_dbg_bh.v_base = (void *)base; return; } @@ -69,53 +69,3 @@ mv64x60_mpsc_progress(char *s, unsigned return; } #endif /* CONFIG_SERIAL_TEXT_DEBUG */ - - -#if defined(CONFIG_KGDB) - -#if defined(CONFIG_KGDB_TTYS0) -#define KGDB_PORT 0 -#elif defined(CONFIG_KGDB_TTYS1) -#define KGDB_PORT 1 -#else -#error "Invalid kgdb_tty port" -#endif - -void -putDebugChar(unsigned char c) -{ - mv64x60_polled_putc(KGDB_PORT, (char)c); -} - -int -getDebugChar(void) -{ - unsigned char c; - - while (!mv64x60_polled_getc(KGDB_PORT, &c)); - return (int)c; -} - -void -putDebugString(char* str) -{ - while (*str != '\0') { - putDebugChar(*str); - str++; - } - putDebugChar('\r'); - return; -} - -void -kgdb_interruptible(int enable) -{ -} - -void -kgdb_map_scc(void) -{ - if (ppc_md.early_serial_map) - ppc_md.early_serial_map(); -} -#endif /* CONFIG_KGDB */ diff -puN arch/ppc/syslib/ppc4xx_setup.c~git-kgdb arch/ppc/syslib/ppc4xx_setup.c --- a/arch/ppc/syslib/ppc4xx_setup.c~git-kgdb +++ a/arch/ppc/syslib/ppc4xx_setup.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff -puN arch/ppc/syslib/ppc83xx_setup.c~git-kgdb arch/ppc/syslib/ppc83xx_setup.c --- a/arch/ppc/syslib/ppc83xx_setup.c~git-kgdb +++ a/arch/ppc/syslib/ppc83xx_setup.c @@ -30,12 +30,12 @@ #include /* for linux/serial_core.h */ #include #include +#include #include #include #include #include -#include #include #include @@ -44,6 +44,7 @@ #include #include #endif +#include phys_addr_t immrbar; @@ -87,11 +88,11 @@ mpc83xx_calibrate_decr(void) tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); } -#ifdef CONFIG_SERIAL_8250 +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_KGDB_8250) void __init mpc83xx_early_serial_map(void) { -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) struct uart_port serial_req; #endif struct plat_serial8250_port *pdata; @@ -103,27 +104,40 @@ mpc83xx_early_serial_map(void) pdata[0].mapbase += binfo->bi_immr_base; pdata[0].membase = ioremap(pdata[0].mapbase, 0x100); -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) memset(&serial_req, 0, sizeof (serial_req)); serial_req.iotype = UPIO_MEM; serial_req.mapbase = pdata[0].mapbase; serial_req.membase = pdata[0].membase; serial_req.regshift = 0; + serial_req.irq = pdata[0].irq; + serial_req.flags = pdata[0].flags; + serial_req.uartclk = pdata[0].uartclk; +#ifdef CONFIG_SERIAL_TEXT_DEBUG gen550_init(0, &serial_req); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &serial_req); +#endif +#endif pdata[1].uartclk = binfo->bi_busfreq; pdata[1].mapbase += binfo->bi_immr_base; pdata[1].membase = ioremap(pdata[1].mapbase, 0x100); -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) /* Assume gen550_init() doesn't modify serial_req */ serial_req.mapbase = pdata[1].mapbase; serial_req.membase = pdata[1].membase; +#ifdef CONFIG_SERIAL_TEXT_DEBUG gen550_init(1, &serial_req); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &serial_req); +#endif +#endif } #endif diff -puN arch/ppc/syslib/ppc85xx_setup.c~git-kgdb arch/ppc/syslib/ppc85xx_setup.c --- a/arch/ppc/syslib/ppc85xx_setup.c~git-kgdb +++ a/arch/ppc/syslib/ppc85xx_setup.c @@ -19,16 +19,17 @@ #include /* for linux/serial_core.h */ #include #include +#include #include #include #include #include #include -#include #include #include +#include extern void abort(void); @@ -69,11 +70,11 @@ mpc85xx_calibrate_decr(void) mtspr(SPRN_TCR, TCR_DIE); } -#ifdef CONFIG_SERIAL_8250 +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_KGDB_8250) void __init mpc85xx_early_serial_map(void) { -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) struct uart_port serial_req; #endif struct plat_serial8250_port *pdata; @@ -85,27 +86,40 @@ mpc85xx_early_serial_map(void) pdata[0].mapbase += binfo->bi_immr_base; pdata[0].membase = ioremap(pdata[0].mapbase, MPC85xx_UART0_SIZE); -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) memset(&serial_req, 0, sizeof (serial_req)); serial_req.iotype = UPIO_MEM; serial_req.mapbase = pdata[0].mapbase; serial_req.membase = pdata[0].membase; serial_req.regshift = 0; + serial_req.irq = pdata[0].irq; + serial_req.flags = pdata[0].flags; + serial_req.uartclk = pdata[0].uartclk; +#ifdef CONFIG_SERIAL_TEXT_DEBUG gen550_init(0, &serial_req); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(0, &serial_req); +#endif +#endif pdata[1].uartclk = binfo->bi_busfreq; pdata[1].mapbase += binfo->bi_immr_base; pdata[1].membase = ioremap(pdata[1].mapbase, MPC85xx_UART0_SIZE); -#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250) /* Assume gen550_init() doesn't modify serial_req */ serial_req.mapbase = pdata[1].mapbase; serial_req.membase = pdata[1].membase; +#ifdef CONFIG_SERIAL_TEXT_DEBUG gen550_init(1, &serial_req); #endif +#ifdef CONFIG_KGDB_8250 + kgdb8250_add_port(1, &serial_req); +#endif +#endif } #endif @@ -363,5 +377,3 @@ mpc85xx_setup_hose(void) return; } #endif /* CONFIG_PCI */ - - diff -puN arch/sh/Kconfig.debug~git-kgdb arch/sh/Kconfig.debug --- a/arch/sh/Kconfig.debug~git-kgdb +++ a/arch/sh/Kconfig.debug @@ -82,83 +82,4 @@ config 4KSTACKS on the VM subsystem for higher order allocations. This option will also use IRQ stacks to compensate for the reduced stackspace. -config SH_KGDB - bool "Include KGDB kernel debugger" - select FRAME_POINTER - select DEBUG_INFO - depends on CPU_SH3 || CPU_SH4 - 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. - -menu "KGDB configuration options" - depends on SH_KGDB - -config MORE_COMPILE_OPTIONS - bool "Add any additional compile options" - help - If you want to add additional CFLAGS to the kernel build, enable this - option and then enter what you would like to add in the next question. - Note however that -g is already appended with the selection of KGDB. - -config COMPILE_OPTIONS - string "Additional compile arguments" - depends on MORE_COMPILE_OPTIONS - -config KGDB_NMI - bool "Enter KGDB on NMI" - default n - -config SH_KGDB_CONSOLE - bool "Console messages through GDB" - depends on !SERIAL_SH_SCI_CONSOLE - select SERIAL_CORE_CONSOLE - default n - -config KGDB_SYSRQ - bool "Allow SysRq 'G' to enter KGDB" - default y - -comment "Serial port setup" - -config KGDB_DEFPORT - int "Port number (ttySCn)" - default "1" - -config KGDB_DEFBAUD - int "Baud rate" - default "115200" - -choice - prompt "Parity" - depends on SH_KGDB - default KGDB_DEFPARITY_N - -config KGDB_DEFPARITY_N - bool "None" - -config KGDB_DEFPARITY_E - bool "Even" - -config KGDB_DEFPARITY_O - bool "Odd" - -endchoice - -choice - prompt "Data bits" - depends on SH_KGDB - default KGDB_DEFBITS_8 - -config KGDB_DEFBITS_8 - bool "8" - -config KGDB_DEFBITS_7 - bool "7" - -endchoice - -endmenu - endmenu diff -puN arch/sh/kernel/Makefile~git-kgdb arch/sh/kernel/Makefile --- a/arch/sh/kernel/Makefile~git-kgdb +++ a/arch/sh/kernel/Makefile @@ -14,7 +14,7 @@ obj-$(CONFIG_VSYSCALL) += vsyscall/ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_CF_ENABLER) += cf-enabler.o obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o -obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MODULES) += sh_ksyms.o module.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o diff -puN /dev/null arch/sh/kernel/kgdb-jmp.S --- /dev/null +++ a/arch/sh/kernel/kgdb-jmp.S @@ -0,0 +1,32 @@ +#include + +ENTRY(kgdb_fault_setjmp) + add #(9*4), r4 + sts.l pr, @-r4 + mov.l r15, @-r4 + mov.l r14, @-r4 + mov.l r13, @-r4 + mov.l r12, @-r4 + mov.l r11, @-r4 + mov.l r10, @-r4 + mov.l r9, @-r4 + mov.l r8, @-r4 + rts + mov #0, r0 + +ENTRY(kgdb_fault_longjmp) + mov.l @r4+, r8 + mov.l @r4+, r9 + mov.l @r4+, r10 + mov.l @r4+, r11 + mov.l @r4+, r12 + mov.l @r4+, r13 + mov.l @r4+, r14 + mov.l @r4+, r15 + lds.l @r4+, pr + mov r5, r0 + tst r0, r0 + bf 1f + mov #1, r0 +1: rts + nop diff -puN /dev/null arch/sh/kernel/kgdb.c --- /dev/null +++ a/arch/sh/kernel/kgdb.c @@ -0,0 +1,363 @@ +/* + * arch/sh/kernel/kgdb.c + * + * Contains SH-specific low-level support for KGDB. + * + * Containes extracts from code by Glenn Engel, Jim Kingdon, + * David Grothe , Tigran Aivazian , + * Amit S. Kale , William Gatliff , + * Ben Lee, Steve Chamberlain and Benoit Miller , + * Henry Bell and Jeremy Siegel + * + * Maintainer: Tom Rini + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern void per_cpu_trap_init(void); +extern atomic_t cpu_doing_single_step; + +/* Function pointers for linkage */ +static struct kgdb_regs trap_registers; + +/* Globals. */ +char in_nmi; /* Set during NMI to prevent reentry */ + +/* TRA differs sh3/4 */ +#if defined(CONFIG_CPU_SH3) +#define TRA 0xffffffd0 +#elif defined(CONFIG_CPU_SH4) +#define TRA 0xff000020 +#endif + +/* Macros for single step instruction identification */ +#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900) +#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00) +#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \ + (((op) & 0x7f ) << 1)) +#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00) +#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00) +#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000) +#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \ + (((op) & 0x7ff) << 1)) +#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023) +#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8) +#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000) +#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \ + (((op) & 0x7ff) << 1)) +#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003) +#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf) +#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b) +#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf) +#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b) +#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf) +#define OPCODE_RTS(op) ((op) == 0xb) +#define OPCODE_RTE(op) ((op) == 0x2b) + +#define SR_T_BIT_MASK 0x1 +#define STEP_OPCODE 0xc320 +#define BIOS_CALL_TRAP 0x3f + +/* Exception codes as per SH-4 core manual */ +#define ADDRESS_ERROR_LOAD_VEC 7 +#define ADDRESS_ERROR_STORE_VEC 8 +#define TRAP_VEC 11 +#define INVALID_INSN_VEC 12 +#define INVALID_SLOT_VEC 13 +#define NMI_VEC 14 +#define SERIAL_BREAK_VEC 58 + +/* Misc static */ +static int stepped_address; +static short stepped_opcode; + +/* Translate SH-3/4 exception numbers to unix-like signal values */ +static int compute_signal(const int excep_code) +{ + switch (excep_code) { + case INVALID_INSN_VEC: + case INVALID_SLOT_VEC: + return SIGILL; + case ADDRESS_ERROR_LOAD_VEC: + case ADDRESS_ERROR_STORE_VEC: + return SIGSEGV; + case SERIAL_BREAK_VEC: + case NMI_VEC: + return SIGINT; + default: + /* Act like it was a break/trap. */ + return SIGTRAP; + } +} + +/* + * Translate the registers of the system into the format that GDB wants. Since + * we use a local structure to store things, instead of getting them out + * of pt_regs, we can just do a memcpy. + */ +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *ign) +{ + memcpy(gdb_regs, &trap_registers, sizeof(trap_registers)); +} + +/* + * On SH we save: r1 (prev->thread.sp) r2 (prev->thread.pc) r4 (prev) r5 (next) + * r6 (next->thread.sp) r7 (next->thread.pc) + */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + int count; + + for (count = 0; count < 16; count++) + *(gdb_regs++) = 0; + *(gdb_regs++) = p->thread.pc; + *(gdb_regs++) = 0; + *(gdb_regs++) = 0; + *(gdb_regs++) = 0; + *(gdb_regs++) = 0; + *(gdb_regs++) = 0; + *(gdb_regs++) = 0; +} + +/* + * Translate the registers values that GDB has given us back into the + * format of the system. See the comment above about memcpy. + */ +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *ign) +{ + memcpy(&trap_registers, gdb_regs, sizeof(trap_registers)); +} + +/* Calculate the new address for after a step */ +static short *get_step_address(void) +{ + short op = *(short *)trap_registers.pc; + long addr; + + /* BT */ + if (OPCODE_BT(op)) { + if (trap_registers.sr & SR_T_BIT_MASK) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 2; + } + + /* BTS */ + else if (OPCODE_BTS(op)) { + if (trap_registers.sr & SR_T_BIT_MASK) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 4; /* Not in delay slot */ + } + + /* BF */ + else if (OPCODE_BF(op)) { + if (!(trap_registers.sr & SR_T_BIT_MASK)) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 2; + } + + /* BFS */ + else if (OPCODE_BFS(op)) { + if (!(trap_registers.sr & SR_T_BIT_MASK)) + addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); + else + addr = trap_registers.pc + 4; /* Not in delay slot */ + } + + /* BRA */ + else if (OPCODE_BRA(op)) + addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op); + + /* BRAF */ + else if (OPCODE_BRAF(op)) + addr = trap_registers.pc + 4 + + trap_registers.regs[OPCODE_BRAF_REG(op)]; + + /* BSR */ + else if (OPCODE_BSR(op)) + addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op); + + /* BSRF */ + else if (OPCODE_BSRF(op)) + addr = trap_registers.pc + 4 + + trap_registers.regs[OPCODE_BSRF_REG(op)]; + + /* JMP */ + else if (OPCODE_JMP(op)) + addr = trap_registers.regs[OPCODE_JMP_REG(op)]; + + /* JSR */ + else if (OPCODE_JSR(op)) + addr = trap_registers.regs[OPCODE_JSR_REG(op)]; + + /* RTS */ + else if (OPCODE_RTS(op)) + addr = trap_registers.pr; + + /* RTE */ + else if (OPCODE_RTE(op)) + addr = trap_registers.regs[15]; + + /* Other */ + else + addr = trap_registers.pc + 2; + + kgdb_flush_icache_range(addr, addr + 2); + return (short *)addr; +} + +/* The command loop, read and act on requests */ +int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *ign) +{ + unsigned long addr; + char *ptr = &remcom_in_buffer[1]; + + /* Examine first char of buffer to see what we need to do */ + switch (remcom_in_buffer[0]) { + case 'c': /* Continue at address AA..AA (optional) */ + case 's': /* Step one instruction from AA..AA */ + /* Try to read optional parameter, PC unchanged if none */ + if (kgdb_hex2long(&ptr, &addr)) + trap_registers.pc = addr; + + atomic_set(&cpu_doing_single_step, -1); + if (remcom_in_buffer[0] == 's') { + /* Replace the instruction immediately after the + * current instruction (i.e. next in the expected + * flow of control) with a trap instruction, so that + * returning will cause only a single instruction to + * be executed. Note that this model is slightly + * broken for instructions with delay slots + * (e.g. B[TF]S, BSR, BRA etc), where both the branch + * and the instruction in the delay slot will be + * executed. + */ + /* Determine where the target instruction will send + * us to */ + unsigned short *next_addr = get_step_address(); + stepped_address = (int)next_addr; + + /* Replace it */ + stepped_opcode = *(short *)next_addr; + *next_addr = STEP_OPCODE; + + /* Flush and return */ + kgdb_flush_icache_range((long)next_addr, + (long)next_addr + 2); + if (kgdb_contthread) + atomic_set(&cpu_doing_single_step, + smp_processor_id()); + } + return 0; + } + return -1; +} + +/* + * When an exception has occured, we are called. We need to set things + * up so that we can call kgdb_handle_exception to handle requests from + * the remote GDB. + */ +void kgdb_exception_handler(struct pt_regs *regs) +{ + int excep_code, vbr_val; + int count; + + /* Copy kernel regs (from stack) */ + for (count = 0; count < 16; count++) + trap_registers.regs[count] = regs->regs[count]; + trap_registers.pc = regs->pc; + trap_registers.pr = regs->pr; + trap_registers.sr = regs->sr; + trap_registers.gbr = regs->gbr; + trap_registers.mach = regs->mach; + trap_registers.macl = regs->macl; + + __asm__ __volatile__("stc vbr, %0":"=r"(vbr_val)); + trap_registers.vbr = vbr_val; + + /* Get the execption code. */ + __asm__ __volatile__("stc r2_bank, %0":"=r"(excep_code)); + + excep_code >>= 5; + + /* If we got an NMI, and KGDB is not yet initialized, call + * breakpoint() to try and initialize everything for us. */ + if (excep_code == NMI_VEC && !kgdb_initialized) { + breakpoint(); + return; + } + + /* TRAP_VEC exception indicates a software trap inserted in place of + * code by GDB so back up PC by one instruction, as this instruction + * will later be replaced by its original one. Do NOT do this for + * trap 0xff, since that indicates a compiled-in breakpoint which + * will not be replaced (and we would retake the trap forever) */ + if (excep_code == TRAP_VEC && + (*(volatile unsigned long *)TRA != (0xff << 2))) + trap_registers.pc -= 2; + + /* If we have been single-stepping, put back the old instruction. + * We use stepped_address in case we have stopped more than one + * instruction away. */ + if (stepped_opcode != 0) { + *(short *)stepped_address = stepped_opcode; + kgdb_flush_icache_range(stepped_address, stepped_address + 2); + } + stepped_opcode = 0; + + /* Call the stub to do the processing. Note that not everything we + * need to send back and forth lives in pt_regs. */ + kgdb_handle_exception(excep_code, compute_signal(excep_code), 0, regs); + + /* Copy back the (maybe modified) registers */ + for (count = 0; count < 16; count++) + regs->regs[count] = trap_registers.regs[count]; + regs->pc = trap_registers.pc; + regs->pr = trap_registers.pr; + regs->sr = trap_registers.sr; + regs->gbr = trap_registers.gbr; + regs->mach = trap_registers.mach; + regs->macl = trap_registers.macl; + + vbr_val = trap_registers.vbr; + __asm__ __volatile__("ldc %0, vbr": :"r"(vbr_val)); +} + +int __init kgdb_arch_init(void) +{ + per_cpu_trap_init(); + + return 0; +} + +struct kgdb_arch arch_kgdb_ops = { +#ifdef CONFIG_CPU_LITTLE_ENDIAN + .gdb_bpt_instr = {0xff, 0xc3}, +#else /* ! CONFIG_CPU_LITTLE_ENDIAN */ + .gdb_bpt_instr = {0xc3, 0xff}, +#endif +}; diff -puN arch/sh/kernel/kgdb_jmp.S~git-kgdb /dev/null --- a/arch/sh/kernel/kgdb_jmp.S +++ /dev/null @@ -1,33 +0,0 @@ -#include - -ENTRY(setjmp) - add #(9*4), r4 - sts.l pr, @-r4 - mov.l r15, @-r4 - mov.l r14, @-r4 - mov.l r13, @-r4 - mov.l r12, @-r4 - mov.l r11, @-r4 - mov.l r10, @-r4 - mov.l r9, @-r4 - mov.l r8, @-r4 - rts - mov #0, r0 - -ENTRY(longjmp) - mov.l @r4+, r8 - mov.l @r4+, r9 - mov.l @r4+, r10 - mov.l @r4+, r11 - mov.l @r4+, r12 - mov.l @r4+, r13 - mov.l @r4+, r14 - mov.l @r4+, r15 - lds.l @r4+, pr - mov r5, r0 - tst r0, r0 - bf 1f - mov #1, r0 ! in case val==0 -1: rts - nop - diff -puN arch/sh/kernel/kgdb_stub.c~git-kgdb /dev/null --- a/arch/sh/kernel/kgdb_stub.c +++ /dev/null @@ -1,1093 +0,0 @@ -/* - * May be copied or modified under the terms of the GNU General Public - * License. See linux/COPYING for more information. - * - * Contains extracts from code by Glenn Engel, Jim Kingdon, - * David Grothe , Tigran Aivazian , - * Amit S. Kale , William Gatliff , - * Ben Lee, Steve Chamberlain and Benoit Miller . - * - * This version by Henry Bell - * Minor modifications by Jeremy Siegel - * - * Contains low-level support for remote debug using GDB. - * - * To enable debugger support, two things need to happen. 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. - * A breakpoint also needs to be generated to begin communication. This - * is most easily accomplished by a call to breakpoint() which does - * a trapa if the initialisation phase has been successfully completed. - * - * In this case, set_debug_traps() is not used to "take over" exceptions; - * other kernel code is modified instead to enter the kgdb functions here - * when appropriate (see entry.S for breakpoint traps and NMI interrupts, - * see traps.c for kernel error exceptions). - * - * 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 - * XAA..AA,LLLL: Same, but data is binary (not hex) OK or ENN - * - * c Resume at current address SNN ( signal NN) - * cAA..AA Continue at address AA..AA SNN - * CNN; Resume at current address with signal SNN - * CNN;AA..AA Resume at address AA..AA with signal SNN - * - * s Step one instruction SNN - * sAA..AA Step one instruction from AA..AA SNN - * SNN; Step one instruction with signal SNN - * SNNAA..AA Step one instruction from AA..AA w/NN SNN - * - * k kill (Detach GDB) - * - * d Toggle debug flag - * D Detach GDB - * - * Hct Set thread t for operations, OK or ENN - * c = 'c' (step, cont), c = 'g' (other - * operations) - * - * qC Query current thread ID QCpid - * qfThreadInfo Get list of current threads (first) m - * qsThreadInfo " " " " " (subsequent) - * qOffsets Get section offsets Text=x;Data=y;Bss=z - * - * TXX Find if thread XX is alive OK or ENN - * ? What was the last sigval ? SNN (signal NN) - * O Output to GDB console - * - * Remote communication protocol. - * - * A debug packet whose contents are is encapsulated for - * transmission in the form: - * - * $ # CSUM1 CSUM2 - * - * must be ASCII alphanumeric and cannot include characters - * '$' or '#'. If starts with two characters followed by - * ':', then the existing stubs interpret this as a sequence number. - * - * CSUM1 and CSUM2 are ascii hex representation of an 8-bit - * checksum of , the most significant nibble is sent first. - * the hex digits 0-9,a-f are used. - * - * Receiver responds with: - * - * + - if CSUM is correct and ready for next packet - * - - if CSUM is incorrect - * - * Responses can be run-length encoded to save space. A '*' means that - * the next character is an ASCII encoding giving a repeat count which - * stands for that many repetitions of the character preceding the '*'. - * The encoding is n+29, yielding a printable character where n >=3 - * (which is where RLE starts to win). Don't use an n > 126. - * - * So "0* " means the same as "0000". - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Function pointers for linkage */ -kgdb_debug_hook_t *kgdb_debug_hook; -kgdb_bus_error_hook_t *kgdb_bus_err_hook; - -int (*kgdb_getchar)(void); -void (*kgdb_putchar)(int); - -static void put_debug_char(int c) -{ - if (!kgdb_putchar) - return; - (*kgdb_putchar)(c); -} -static int get_debug_char(void) -{ - if (!kgdb_getchar) - return -1; - return (*kgdb_getchar)(); -} - -/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */ -#define BUFMAX 1024 -#define NUMREGBYTES (MAXREG*4) -#define OUTBUFMAX (NUMREGBYTES*2+512) - -enum regs { - R0 = 0, R1, R2, R3, R4, R5, R6, R7, - R8, R9, R10, R11, R12, R13, R14, R15, - PC, PR, GBR, VBR, MACH, MACL, SR, - /* */ - MAXREG -}; - -static unsigned int registers[MAXREG]; -struct kgdb_regs trap_registers; - -char kgdb_in_gdb_mode; -char in_nmi; /* Set during NMI to prevent reentry */ -int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */ -int kgdb_enabled = 1; /* Default to enabled, cmdline can disable */ - -/* Exposed for user access */ -struct task_struct *kgdb_current; -unsigned int kgdb_g_imask; -int kgdb_trapa_val; -int kgdb_excode; - -/* Default values for SCI (can override via kernel args in setup.c) */ -#ifndef CONFIG_KGDB_DEFPORT -#define CONFIG_KGDB_DEFPORT 1 -#endif - -#ifndef CONFIG_KGDB_DEFBAUD -#define CONFIG_KGDB_DEFBAUD 115200 -#endif - -#if defined(CONFIG_KGDB_DEFPARITY_E) -#define CONFIG_KGDB_DEFPARITY 'E' -#elif defined(CONFIG_KGDB_DEFPARITY_O) -#define CONFIG_KGDB_DEFPARITY 'O' -#else /* CONFIG_KGDB_DEFPARITY_N */ -#define CONFIG_KGDB_DEFPARITY 'N' -#endif - -#ifdef CONFIG_KGDB_DEFBITS_7 -#define CONFIG_KGDB_DEFBITS '7' -#else /* CONFIG_KGDB_DEFBITS_8 */ -#define CONFIG_KGDB_DEFBITS '8' -#endif - -/* SCI/UART settings, used in kgdb_console_setup() */ -int kgdb_portnum = CONFIG_KGDB_DEFPORT; -int kgdb_baud = CONFIG_KGDB_DEFBAUD; -char kgdb_parity = CONFIG_KGDB_DEFPARITY; -char kgdb_bits = CONFIG_KGDB_DEFBITS; - -/* Jump buffer for setjmp/longjmp */ -static jmp_buf rem_com_env; - -/* TRA differs sh3/4 */ -#if defined(CONFIG_CPU_SH3) -#define TRA 0xffffffd0 -#elif defined(CONFIG_CPU_SH4) -#define TRA 0xff000020 -#endif - -/* Macros for single step instruction identification */ -#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900) -#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00) -#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \ - (((op) & 0x7f ) << 1)) -#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00) -#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00) -#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000) -#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \ - (((op) & 0x7ff) << 1)) -#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023) -#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8) -#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000) -#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \ - (((op) & 0x7ff) << 1)) -#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003) -#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf) -#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b) -#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf) -#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b) -#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf) -#define OPCODE_RTS(op) ((op) == 0xb) -#define OPCODE_RTE(op) ((op) == 0x2b) - -#define SR_T_BIT_MASK 0x1 -#define STEP_OPCODE 0xc320 -#define BIOS_CALL_TRAP 0x3f - -/* Exception codes as per SH-4 core manual */ -#define ADDRESS_ERROR_LOAD_VEC 7 -#define ADDRESS_ERROR_STORE_VEC 8 -#define TRAP_VEC 11 -#define INVALID_INSN_VEC 12 -#define INVALID_SLOT_VEC 13 -#define NMI_VEC 14 -#define USER_BREAK_VEC 15 -#define SERIAL_BREAK_VEC 58 - -/* Misc static */ -static int stepped_address; -static short stepped_opcode; -static char in_buffer[BUFMAX]; -static char out_buffer[OUTBUFMAX]; - -static void kgdb_to_gdb(const char *s); - -/* Convert ch to hex */ -static int hex(const char ch) -{ - if ((ch >= 'a') && (ch <= 'f')) - return (ch - 'a' + 10); - if ((ch >= '0') && (ch <= '9')) - return (ch - '0'); - if ((ch >= 'A') && (ch <= 'F')) - return (ch - 'A' + 10); - return (-1); -} - -/* Convert the memory pointed to by mem into hex, placing result in buf. - Returns a pointer to the last char put in buf (null) */ -static char *mem_to_hex(const char *mem, char *buf, const int count) -{ - int i; - int ch; - unsigned short s_val; - unsigned long l_val; - - /* Check for 16 or 32 */ - if (count == 2 && ((long) mem & 1) == 0) { - s_val = *(unsigned short *) mem; - mem = (char *) &s_val; - } else if (count == 4 && ((long) mem & 3) == 0) { - l_val = *(unsigned long *) mem; - mem = (char *) &l_val; - } - for (i = 0; i < count; i++) { - ch = *mem++; - *buf++ = highhex(ch); - *buf++ = lowhex(ch); - } - *buf = 0; - return (buf); -} - -/* Convert the hex array pointed to by buf into binary, to be placed in mem. - Return a pointer to the character after the last byte written */ -static char *hex_to_mem(const char *buf, char *mem, const int count) -{ - int i; - unsigned char ch; - - for (i = 0; i < count; i++) { - ch = hex(*buf++) << 4; - ch = ch + hex(*buf++); - *mem++ = ch; - } - return (mem); -} - -/* While finding valid hex chars, convert to an integer, then return it */ -static int hex_to_int(char **ptr, int *int_value) -{ - int num_chars = 0; - int hex_value; - - *int_value = 0; - - while (**ptr) { - hex_value = hex(**ptr); - if (hex_value >= 0) { - *int_value = (*int_value << 4) | hex_value; - num_chars++; - } else - break; - (*ptr)++; - } - return num_chars; -} - -/* Copy the binary array pointed to by buf into mem. Fix $, #, - and 0x7d escaped with 0x7d. Return a pointer to the character - after the last byte written. */ -static char *ebin_to_mem(const char *buf, char *mem, int count) -{ - for (; count > 0; count--, buf++) { - if (*buf == 0x7d) - *mem++ = *(++buf) ^ 0x20; - else - *mem++ = *buf; - } - return mem; -} - -/* Pack a hex byte */ -static char *pack_hex_byte(char *pkt, int byte) -{ - *pkt++ = hexchars[(byte >> 4) & 0xf]; - *pkt++ = hexchars[(byte & 0xf)]; - return pkt; -} - -/* Scan for the start char '$', read the packet and check the checksum */ -static void get_packet(char *buffer, int buflen) -{ - unsigned char checksum; - unsigned char xmitcsum; - int i; - int count; - char ch; - - do { - /* Ignore everything until the start character */ - while ((ch = get_debug_char()) != '$'); - - checksum = 0; - xmitcsum = -1; - count = 0; - - /* Now, read until a # or end of buffer is found */ - while (count < (buflen - 1)) { - ch = get_debug_char(); - - if (ch == '#') - break; - - checksum = checksum + ch; - buffer[count] = ch; - count = count + 1; - } - - buffer[count] = 0; - - /* Continue to read checksum following # */ - if (ch == '#') { - xmitcsum = hex(get_debug_char()) << 4; - xmitcsum += hex(get_debug_char()); - - /* Checksum */ - if (checksum != xmitcsum) - put_debug_char('-'); /* Failed checksum */ - else { - /* Ack successful transfer */ - put_debug_char('+'); - - /* If a sequence char is present, reply - the sequence ID */ - if (buffer[2] == ':') { - put_debug_char(buffer[0]); - put_debug_char(buffer[1]); - - /* Remove sequence chars from buffer */ - count = strlen(buffer); - for (i = 3; i <= count; i++) - buffer[i - 3] = buffer[i]; - } - } - } - } - while (checksum != xmitcsum); /* Keep trying while we fail */ -} - -/* Send the packet in the buffer with run-length encoding */ -static void put_packet(char *buffer) -{ - int checksum; - char *src; - int runlen; - int encode; - - do { - src = buffer; - put_debug_char('$'); - checksum = 0; - - /* Continue while we still have chars left */ - while (*src) { - /* Check for runs up to 99 chars long */ - for (runlen = 1; runlen < 99; runlen++) { - if (src[0] != src[runlen]) - break; - } - - if (runlen > 3) { - /* Got a useful amount, send encoding */ - encode = runlen + ' ' - 4; - put_debug_char(*src); checksum += *src; - put_debug_char('*'); checksum += '*'; - put_debug_char(encode); checksum += encode; - src += runlen; - } else { - /* Otherwise just send the current char */ - put_debug_char(*src); checksum += *src; - src += 1; - } - } - - /* '#' Separator, put high and low components of checksum */ - put_debug_char('#'); - put_debug_char(highhex(checksum)); - put_debug_char(lowhex(checksum)); - } - while ((get_debug_char()) != '+'); /* While no ack */ -} - -/* A bus error has occurred - perform a longjmp to return execution and - allow handling of the error */ -static void kgdb_handle_bus_error(void) -{ - longjmp(rem_com_env, 1); -} - -/* Translate SH-3/4 exception numbers to unix-like signal values */ -static int compute_signal(const int excep_code) -{ - int sigval; - - switch (excep_code) { - - case INVALID_INSN_VEC: - case INVALID_SLOT_VEC: - sigval = SIGILL; - break; - case ADDRESS_ERROR_LOAD_VEC: - case ADDRESS_ERROR_STORE_VEC: - sigval = SIGSEGV; - break; - - case SERIAL_BREAK_VEC: - case NMI_VEC: - sigval = SIGINT; - break; - - case USER_BREAK_VEC: - case TRAP_VEC: - sigval = SIGTRAP; - break; - - default: - sigval = SIGBUS; /* "software generated" */ - break; - } - - return (sigval); -} - -/* Make a local copy of the registers passed into the handler (bletch) */ -static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs, - int *gdb_regs) -{ - gdb_regs[R0] = regs->regs[R0]; - gdb_regs[R1] = regs->regs[R1]; - gdb_regs[R2] = regs->regs[R2]; - gdb_regs[R3] = regs->regs[R3]; - gdb_regs[R4] = regs->regs[R4]; - gdb_regs[R5] = regs->regs[R5]; - gdb_regs[R6] = regs->regs[R6]; - gdb_regs[R7] = regs->regs[R7]; - gdb_regs[R8] = regs->regs[R8]; - gdb_regs[R9] = regs->regs[R9]; - gdb_regs[R10] = regs->regs[R10]; - gdb_regs[R11] = regs->regs[R11]; - gdb_regs[R12] = regs->regs[R12]; - gdb_regs[R13] = regs->regs[R13]; - gdb_regs[R14] = regs->regs[R14]; - gdb_regs[R15] = regs->regs[R15]; - gdb_regs[PC] = regs->pc; - gdb_regs[PR] = regs->pr; - gdb_regs[GBR] = regs->gbr; - gdb_regs[MACH] = regs->mach; - gdb_regs[MACL] = regs->macl; - gdb_regs[SR] = regs->sr; - gdb_regs[VBR] = regs->vbr; -} - -/* Copy local gdb registers back to kgdb regs, for later copy to kernel */ -static void gdb_regs_to_kgdb_regs(const int *gdb_regs, - struct kgdb_regs *regs) -{ - regs->regs[R0] = gdb_regs[R0]; - regs->regs[R1] = gdb_regs[R1]; - regs->regs[R2] = gdb_regs[R2]; - regs->regs[R3] = gdb_regs[R3]; - regs->regs[R4] = gdb_regs[R4]; - regs->regs[R5] = gdb_regs[R5]; - regs->regs[R6] = gdb_regs[R6]; - regs->regs[R7] = gdb_regs[R7]; - regs->regs[R8] = gdb_regs[R8]; - regs->regs[R9] = gdb_regs[R9]; - regs->regs[R10] = gdb_regs[R10]; - regs->regs[R11] = gdb_regs[R11]; - regs->regs[R12] = gdb_regs[R12]; - regs->regs[R13] = gdb_regs[R13]; - regs->regs[R14] = gdb_regs[R14]; - regs->regs[R15] = gdb_regs[R15]; - regs->pc = gdb_regs[PC]; - regs->pr = gdb_regs[PR]; - regs->gbr = gdb_regs[GBR]; - regs->mach = gdb_regs[MACH]; - regs->macl = gdb_regs[MACL]; - regs->sr = gdb_regs[SR]; - regs->vbr = gdb_regs[VBR]; -} - -/* Calculate the new address for after a step */ -static short *get_step_address(void) -{ - short op = *(short *) trap_registers.pc; - long addr; - - /* BT */ - if (OPCODE_BT(op)) { - if (trap_registers.sr & SR_T_BIT_MASK) - addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); - else - addr = trap_registers.pc + 2; - } - - /* BTS */ - else if (OPCODE_BTS(op)) { - if (trap_registers.sr & SR_T_BIT_MASK) - addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); - else - addr = trap_registers.pc + 4; /* Not in delay slot */ - } - - /* BF */ - else if (OPCODE_BF(op)) { - if (!(trap_registers.sr & SR_T_BIT_MASK)) - addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); - else - addr = trap_registers.pc + 2; - } - - /* BFS */ - else if (OPCODE_BFS(op)) { - if (!(trap_registers.sr & SR_T_BIT_MASK)) - addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); - else - addr = trap_registers.pc + 4; /* Not in delay slot */ - } - - /* BRA */ - else if (OPCODE_BRA(op)) - addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op); - - /* BRAF */ - else if (OPCODE_BRAF(op)) - addr = trap_registers.pc + 4 - + trap_registers.regs[OPCODE_BRAF_REG(op)]; - - /* BSR */ - else if (OPCODE_BSR(op)) - addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op); - - /* BSRF */ - else if (OPCODE_BSRF(op)) - addr = trap_registers.pc + 4 - + trap_registers.regs[OPCODE_BSRF_REG(op)]; - - /* JMP */ - else if (OPCODE_JMP(op)) - addr = trap_registers.regs[OPCODE_JMP_REG(op)]; - - /* JSR */ - else if (OPCODE_JSR(op)) - addr = trap_registers.regs[OPCODE_JSR_REG(op)]; - - /* RTS */ - else if (OPCODE_RTS(op)) - addr = trap_registers.pr; - - /* RTE */ - else if (OPCODE_RTE(op)) - addr = trap_registers.regs[15]; - - /* Other */ - else - addr = trap_registers.pc + 2; - - kgdb_flush_icache_range(addr, addr + 2); - return (short *) addr; -} - -/* Set up a single-step. Replace the instruction immediately after the - current instruction (i.e. next in the expected flow of control) with a - trap instruction, so that returning will cause only a single instruction - to be executed. Note that this model is slightly broken for instructions - with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch - and the instruction in the delay slot will be executed. */ -static void do_single_step(void) -{ - unsigned short *addr = 0; - - /* Determine where the target instruction will send us to */ - addr = get_step_address(); - stepped_address = (int)addr; - - /* Replace it */ - stepped_opcode = *(short *)addr; - *addr = STEP_OPCODE; - - /* Flush and return */ - kgdb_flush_icache_range((long) addr, (long) addr + 2); - return; -} - -/* Undo a single step */ -static void undo_single_step(void) -{ - /* If we have stepped, put back the old instruction */ - /* Use stepped_address in case we stopped elsewhere */ - if (stepped_opcode != 0) { - *(short*)stepped_address = stepped_opcode; - kgdb_flush_icache_range(stepped_address, stepped_address + 2); - } - stepped_opcode = 0; -} - -/* Send a signal message */ -static void send_signal_msg(const int signum) -{ - out_buffer[0] = 'S'; - out_buffer[1] = highhex(signum); - out_buffer[2] = lowhex(signum); - out_buffer[3] = 0; - put_packet(out_buffer); -} - -/* Reply that all was well */ -static void send_ok_msg(void) -{ - strcpy(out_buffer, "OK"); - put_packet(out_buffer); -} - -/* Reply that an error occurred */ -static void send_err_msg(void) -{ - strcpy(out_buffer, "E01"); - put_packet(out_buffer); -} - -/* Empty message indicates unrecognised command */ -static void send_empty_msg(void) -{ - put_packet(""); -} - -/* Read memory due to 'm' message */ -static void read_mem_msg(void) -{ - char *ptr; - int addr; - int length; - - /* Jmp, disable bus error handler */ - if (setjmp(rem_com_env) == 0) { - - kgdb_nofault = 1; - - /* Walk through, have m, */ - ptr = &in_buffer[1]; - if (hex_to_int(&ptr, &addr) && (*ptr++ == ',')) - if (hex_to_int(&ptr, &length)) { - ptr = 0; - if (length * 2 > OUTBUFMAX) - length = OUTBUFMAX / 2; - mem_to_hex((char *) addr, out_buffer, length); - } - if (ptr) - send_err_msg(); - else - put_packet(out_buffer); - } else - send_err_msg(); - - /* Restore bus error handler */ - kgdb_nofault = 0; -} - -/* Write memory due to 'M' or 'X' message */ -static void write_mem_msg(int binary) -{ - char *ptr; - int addr; - int length; - - if (setjmp(rem_com_env) == 0) { - - kgdb_nofault = 1; - - /* Walk through, have M,: */ - ptr = &in_buffer[1]; - if (hex_to_int(&ptr, &addr) && (*ptr++ == ',')) - if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) { - if (binary) - ebin_to_mem(ptr, (char*)addr, length); - else - hex_to_mem(ptr, (char*)addr, length); - kgdb_flush_icache_range(addr, addr + length); - ptr = 0; - send_ok_msg(); - } - if (ptr) - send_err_msg(); - } else - send_err_msg(); - - /* Restore bus error handler */ - kgdb_nofault = 0; -} - -/* Continue message */ -static void continue_msg(void) -{ - /* Try to read optional parameter, PC unchanged if none */ - char *ptr = &in_buffer[1]; - int addr; - - if (hex_to_int(&ptr, &addr)) - trap_registers.pc = addr; -} - -/* Continue message with signal */ -static void continue_with_sig_msg(void) -{ - int signal; - char *ptr = &in_buffer[1]; - int addr; - - /* Report limitation */ - kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n"); - - /* Signal */ - hex_to_int(&ptr, &signal); - if (*ptr == ';') - ptr++; - - /* Optional address */ - if (hex_to_int(&ptr, &addr)) - trap_registers.pc = addr; -} - -/* Step message */ -static void step_msg(void) -{ - continue_msg(); - do_single_step(); -} - -/* Step message with signal */ -static void step_with_sig_msg(void) -{ - continue_with_sig_msg(); - do_single_step(); -} - -/* Send register contents */ -static void send_regs_msg(void) -{ - kgdb_regs_to_gdb_regs(&trap_registers, registers); - mem_to_hex((char *) registers, out_buffer, NUMREGBYTES); - put_packet(out_buffer); -} - -/* Set register contents - currently can't set other thread's registers */ -static void set_regs_msg(void) -{ - kgdb_regs_to_gdb_regs(&trap_registers, registers); - hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES); - gdb_regs_to_kgdb_regs(registers, &trap_registers); - send_ok_msg(); -} - -#ifdef CONFIG_SH_KGDB_CONSOLE -/* - * Bring up the ports.. - */ -static int kgdb_serial_setup(void) -{ - extern int kgdb_console_setup(struct console *co, char *options); - struct console dummy; - - kgdb_console_setup(&dummy, 0); - - return 0; -} -#else -#define kgdb_serial_setup() 0 -#endif - -/* The command loop, read and act on requests */ -static void kgdb_command_loop(const int excep_code, const int trapa_value) -{ - int sigval; - - if (excep_code == NMI_VEC) { -#ifndef CONFIG_KGDB_NMI - printk(KERN_NOTICE "KGDB: Ignoring unexpected NMI?\n"); - return; -#else /* CONFIG_KGDB_NMI */ - if (!kgdb_enabled) { - kgdb_enabled = 1; - kgdb_init(); - } -#endif /* CONFIG_KGDB_NMI */ - } - - /* Ignore if we're disabled */ - if (!kgdb_enabled) - return; - - /* Enter GDB mode (e.g. after detach) */ - if (!kgdb_in_gdb_mode) { - /* Do serial setup, notify user, issue preemptive ack */ - printk(KERN_NOTICE "KGDB: Waiting for GDB\n"); - kgdb_in_gdb_mode = 1; - put_debug_char('+'); - } - - /* Reply to host that an exception has occurred */ - sigval = compute_signal(excep_code); - send_signal_msg(sigval); - - /* TRAP_VEC exception indicates a software trap inserted in place of - code by GDB so back up PC by one instruction, as this instruction - will later be replaced by its original one. Do NOT do this for - trap 0xff, since that indicates a compiled-in breakpoint which - will not be replaced (and we would retake the trap forever) */ - if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2))) - trap_registers.pc -= 2; - - /* Undo any stepping we may have done */ - undo_single_step(); - - while (1) { - out_buffer[0] = 0; - get_packet(in_buffer, BUFMAX); - - /* Examine first char of buffer to see what we need to do */ - switch (in_buffer[0]) { - case '?': /* Send which signal we've received */ - send_signal_msg(sigval); - break; - - case 'g': /* Return the values of the CPU registers */ - send_regs_msg(); - break; - - case 'G': /* Set the value of the CPU registers */ - set_regs_msg(); - break; - - case 'm': /* Read LLLL bytes address AA..AA */ - read_mem_msg(); - break; - - case 'M': /* Write LLLL bytes address AA..AA, ret OK */ - write_mem_msg(0); /* 0 = data in hex */ - break; - - case 'X': /* Write LLLL bytes esc bin address AA..AA */ - if (kgdb_bits == '8') - write_mem_msg(1); /* 1 = data in binary */ - else - send_empty_msg(); - break; - - case 'C': /* Continue, signum included, we ignore it */ - continue_with_sig_msg(); - return; - - case 'c': /* Continue at address AA..AA (optional) */ - continue_msg(); - return; - - case 'S': /* Step, signum included, we ignore it */ - step_with_sig_msg(); - return; - - case 's': /* Step one instruction from AA..AA */ - step_msg(); - return; - - case 'k': /* 'Kill the program' with a kernel ? */ - break; - - case 'D': /* Detach from program, send reply OK */ - kgdb_in_gdb_mode = 0; - send_ok_msg(); - get_debug_char(); - return; - - default: - send_empty_msg(); - break; - } - } -} - -/* There has been an exception, most likely a breakpoint. */ -static void handle_exception(struct pt_regs *regs) -{ - int excep_code, vbr_val; - int count; - int trapa_value = ctrl_inl(TRA); - - /* Copy kernel regs (from stack) */ - for (count = 0; count < 16; count++) - trap_registers.regs[count] = regs->regs[count]; - trap_registers.pc = regs->pc; - trap_registers.pr = regs->pr; - trap_registers.sr = regs->sr; - trap_registers.gbr = regs->gbr; - trap_registers.mach = regs->mach; - trap_registers.macl = regs->macl; - - asm("stc vbr, %0":"=r"(vbr_val)); - trap_registers.vbr = vbr_val; - - /* Get excode for command loop call, user access */ - asm("stc r2_bank, %0":"=r"(excep_code)); - kgdb_excode = excep_code; - - /* Other interesting environment items for reference */ - asm("stc r6_bank, %0":"=r"(kgdb_g_imask)); - kgdb_current = current; - kgdb_trapa_val = trapa_value; - - /* Act on the exception */ - kgdb_command_loop(excep_code, trapa_value); - - kgdb_current = NULL; - - /* Copy back the (maybe modified) registers */ - for (count = 0; count < 16; count++) - regs->regs[count] = trap_registers.regs[count]; - regs->pc = trap_registers.pc; - regs->pr = trap_registers.pr; - regs->sr = trap_registers.sr; - regs->gbr = trap_registers.gbr; - regs->mach = trap_registers.mach; - regs->macl = trap_registers.macl; - - vbr_val = trap_registers.vbr; - asm("ldc %0, vbr": :"r"(vbr_val)); -} - -asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) -{ - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); - handle_exception(regs); -} - -/* Initialise the KGDB data structures and serial configuration */ -int kgdb_init(void) -{ - if (!kgdb_enabled) - return 1; - - in_nmi = 0; - kgdb_nofault = 0; - stepped_opcode = 0; - kgdb_in_gdb_mode = 0; - - if (kgdb_serial_setup() != 0) { - printk(KERN_NOTICE "KGDB: serial setup error\n"); - return -1; - } - - /* Init ptr to exception handler */ - kgdb_debug_hook = handle_exception; - kgdb_bus_err_hook = kgdb_handle_bus_error; - - /* Enter kgdb now if requested, or just report init done */ - printk(KERN_NOTICE "KGDB: stub is initialized.\n"); - - return 0; -} - -/* Make function available for "user messages"; console will use it too. */ - -char gdbmsgbuf[BUFMAX]; -#define MAXOUT ((BUFMAX-2)/2) - -static void kgdb_msg_write(const char *s, unsigned count) -{ - int i; - int wcount; - char *bufptr; - - /* 'O'utput */ - gdbmsgbuf[0] = 'O'; - - /* Fill and send buffers... */ - while (count > 0) { - bufptr = gdbmsgbuf + 1; - - /* Calculate how many this time */ - wcount = (count > MAXOUT) ? MAXOUT : count; - - /* Pack in hex chars */ - for (i = 0; i < wcount; i++) - bufptr = pack_hex_byte(bufptr, s[i]); - *bufptr = '\0'; - - /* Move up */ - s += wcount; - count -= wcount; - - /* Write packet */ - put_packet(gdbmsgbuf); - } -} - -static void kgdb_to_gdb(const char *s) -{ - kgdb_msg_write(s, strlen(s)); -} - -#ifdef CONFIG_SH_KGDB_CONSOLE -void kgdb_console_write(struct console *co, const char *s, unsigned count) -{ - /* Bail if we're not talking to GDB */ - if (!kgdb_in_gdb_mode) - return; - - kgdb_msg_write(s, count); -} -#endif - -#ifdef CONFIG_KGDB_SYSRQ -static void sysrq_handle_gdb(int key, struct tty_struct *tty) -{ - printk("Entering GDB stub\n"); - breakpoint(); -} - -static struct sysrq_key_op sysrq_gdb_op = { - .handler = sysrq_handle_gdb, - .help_msg = "Gdb", - .action_msg = "GDB", -}; - -static int gdb_register_sysrq(void) -{ - printk("Registering GDB sysrq handler\n"); - register_sysrq_key('g', &sysrq_gdb_op); - return 0; -} -module_init(gdb_register_sysrq); -#endif diff -puN arch/sh/kernel/time.c~git-kgdb arch/sh/kernel/time.c --- a/arch/sh/kernel/time.c~git-kgdb +++ a/arch/sh/kernel/time.c @@ -259,11 +259,4 @@ void __init time_init(void) ((sh_hpt_frequency + 500) / 1000) / 1000, ((sh_hpt_frequency + 500) / 1000) % 1000); -#if defined(CONFIG_SH_KGDB) - /* - * Set up kgdb as requested. We do it here because the serial - * init uses the timer vars we just set up for figuring baud. - */ - kgdb_init(); -#endif } diff -puN arch/sh/kernel/traps.c~git-kgdb arch/sh/kernel/traps.c --- a/arch/sh/kernel/traps.c~git-kgdb +++ a/arch/sh/kernel/traps.c @@ -25,16 +25,10 @@ #include #include #include +#include -#ifdef CONFIG_SH_KGDB -#include -#define CHK_REMOTE_DEBUG(regs) \ -{ \ - if (kgdb_debug_hook && !user_mode(regs))\ - (*kgdb_debug_hook)(regs); \ -} -#else -#define CHK_REMOTE_DEBUG(regs) +#ifndef CONFIG_KGDB +#define kgdb_handle_exception(t, s, e, r) #endif #ifdef CONFIG_CPU_SH2 @@ -91,7 +85,9 @@ void die(const char * str, struct pt_reg printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); - CHK_REMOTE_DEBUG(regs); +#ifdef CONFIG_KGDB + kgdb_handle_exception(1, SIGTRAP, err, regs); +#endif print_modules(); show_regs(regs); @@ -700,7 +696,9 @@ asmlinkage void do_reserved_inst(unsigne lookup_exception_vector(error_code); local_irq_enable(); - CHK_REMOTE_DEBUG(regs); +#ifdef CONFIG_KGDB + kgdb_handle_exception(1, SIGILL, error_code, regs); +#endif force_sig(SIGILL, tsk); die_if_no_fixup("reserved instruction", regs, error_code); } @@ -771,7 +769,9 @@ asmlinkage void do_illegal_slot_inst(uns lookup_exception_vector(error_code); local_irq_enable(); - CHK_REMOTE_DEBUG(regs); +#ifdef CONFIG_KGDB + kgdb_handle_exception(1, SIGILL, error_code, regs); +#endif force_sig(SIGILL, tsk); die_if_no_fixup("illegal slot instruction", regs, error_code); } diff -puN arch/sh/mm/extable.c~git-kgdb arch/sh/mm/extable.c --- a/arch/sh/mm/extable.c~git-kgdb +++ a/arch/sh/mm/extable.c @@ -5,6 +5,7 @@ */ #include +#include #include int fixup_exception(struct pt_regs *regs) @@ -16,6 +17,12 @@ int fixup_exception(struct pt_regs *regs regs->pc = fixup->fixup; return 1; } +#ifdef CONFIG_KGDB + if (atomic_read(&debugger_active) && kgdb_may_fault) + /* Restore our previous state. */ + kgdb_fault_longjmp(kgdb_fault_jmp_regs); + /* Never reached. */ +#endif return 0; } diff -puN arch/sh/mm/fault-nommu.c~git-kgdb arch/sh/mm/fault-nommu.c --- a/arch/sh/mm/fault-nommu.c~git-kgdb +++ a/arch/sh/mm/fault-nommu.c @@ -28,10 +28,6 @@ #include #include -#if defined(CONFIG_SH_KGDB) -#include -#endif - extern void die(const char *,struct pt_regs *,long); /* @@ -42,11 +38,6 @@ extern void die(const char *,struct pt_r asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, unsigned long address) { -#if defined(CONFIG_SH_KGDB) - if (kgdb_nofault && kgdb_bus_err_hook) - kgdb_bus_err_hook(); -#endif - /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. @@ -68,11 +59,6 @@ asmlinkage void do_page_fault(struct pt_ asmlinkage int __do_page_fault(struct pt_regs *regs, unsigned long writeaccess, unsigned long address) { -#if defined(CONFIG_SH_KGDB) - if (kgdb_nofault && kgdb_bus_err_hook) - kgdb_bus_err_hook(); -#endif - if (address >= TASK_SIZE) return 1; diff -puN arch/sh/mm/fault.c~git-kgdb arch/sh/mm/fault.c --- a/arch/sh/mm/fault.c~git-kgdb +++ a/arch/sh/mm/fault.c @@ -18,7 +18,6 @@ #include #include #include -#include /* * This routine handles page faults. It determines the address, @@ -39,11 +38,6 @@ asmlinkage void __kprobes do_page_fault( trace_hardirqs_on(); local_irq_enable(); -#ifdef CONFIG_SH_KGDB - if (kgdb_nofault && kgdb_bus_err_hook) - kgdb_bus_err_hook(); -#endif - tsk = current; mm = tsk->mm; si_code = SEGV_MAPERR; @@ -201,6 +195,7 @@ no_context: die("Oops", regs, writeaccess); bust_spinlocks(0); do_exit(SIGKILL); + dump_stack(); /* * We ran out of memory, or some other thing happened to us that made @@ -263,11 +258,6 @@ asmlinkage int __kprobes __do_page_fault spinlock_t *ptl = NULL; int ret = 1; -#ifdef CONFIG_SH_KGDB - if (kgdb_nofault && kgdb_bus_err_hook) - kgdb_bus_err_hook(); -#endif - /* * We don't take page faults for P1, P2, and parts of P4, these * are always mapped, whether it be due to legacy behaviour in diff -puN arch/x86_64/Kconfig.debug~git-kgdb arch/x86_64/Kconfig.debug --- a/arch/x86_64/Kconfig.debug~git-kgdb +++ a/arch/x86_64/Kconfig.debug @@ -55,7 +55,4 @@ config DEBUG_STACK_USAGE This option will slow down process creation somewhat. -#config X86_REMOTE_DEBUG -# bool "kgdb debugging stub" - endmenu diff -puN arch/x86_64/kernel/Makefile~git-kgdb arch/x86_64/kernel/Makefile --- a/arch/x86_64/kernel/Makefile~git-kgdb +++ a/arch/x86_64/kernel/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_IOMMU) += pci-gart.o apert obj-$(CONFIG_CALGARY_IOMMU) += pci-calgary.o tce.o obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o obj-$(CONFIG_X86_PM_TIMER) += pmtimer.o obj-$(CONFIG_X86_VSMP) += vsmp.o obj-$(CONFIG_K8_NB) += k8.o diff -puN /dev/null arch/x86_64/kernel/kgdb-jmp.S --- /dev/null +++ a/arch/x86_64/kernel/kgdb-jmp.S @@ -0,0 +1,65 @@ +/* + * arch/x86_64/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: Tom Rini + * + * Cribbed from glibc, which carries the following: + * Copyright (C) 2001, 2003, 2004 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 JB_RBX 0 +#define JB_RBP 1 +#define JB_R12 2 +#define JB_R13 3 +#define JB_R14 4 +#define JB_R15 5 +#define JB_RSP 6 +#define JB_PC 7 + + .code64 + +/* 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. + */ +ENTRY(kgdb_fault_setjmp) + /* Save registers. */ + movq %rbx, (JB_RBX*8)(%rdi) + movq %rbp, (JB_RBP*8)(%rdi) + movq %r12, (JB_R12*8)(%rdi) + movq %r13, (JB_R13*8)(%rdi) + movq %r14, (JB_R14*8)(%rdi) + movq %r15, (JB_R15*8)(%rdi) + leaq 8(%rsp), %rdx /* Save SP as it will be after we return. */ + movq %rdx, (JB_RSP*8)(%rdi) + movq (%rsp), %rax /* Save PC we are returning to now. */ + movq %rax, (JB_PC*8)(%rdi) + /* Set return value for setjmp. */ + mov $0,%eax + movq (JB_PC*8)(%rdi),%rdx + movq (JB_RSP*8)(%rdi),%rsp + jmpq *%rdx + +ENTRY(kgdb_fault_longjmp) + /* Restore registers. */ + movq (JB_RBX*8)(%rdi),%rbx + movq (JB_RBP*8)(%rdi),%rbp + movq (JB_R12*8)(%rdi),%r12 + movq (JB_R13*8)(%rdi),%r13 + movq (JB_R14*8)(%rdi),%r14 + movq (JB_R15*8)(%rdi),%r15 + /* Set return value for setjmp. */ + movq (JB_PC*8)(%rdi),%rdx + movq (JB_RSP*8)(%rdi),%rsp + mov $1,%eax + jmpq *%rdx diff -puN /dev/null arch/x86_64/kernel/kgdb.c --- /dev/null +++ a/arch/x86_64/kernel/kgdb.c @@ -0,0 +1,468 @@ +/* + * + * 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) 2004 Amit S. Kale + * Copyright (C) 2000-2001 VERITAS Software Corporation. + * Copyright (C) 2002 Andi Kleen, SuSE Labs + * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd. + * Copyright (C) 2007 Jason Wessel, Wind River Systems, Inc. + * Copyright (C) 2007 MontaVista Software, Inc. + */ +/**************************************************************************** + * 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 + * Integrated into 2.2.5 kernel by Tigran Aivazian + * X86_64 changes from Andi Kleen's patch merged by Jim Houston + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include + +/* Put the error code here just in case the user cares. */ +int gdb_x86_64errcode; +/* Likewise, the vector number here (since GDB only gets the signal + number through the usual means, and that's not very specific). */ +int gdb_x86_64vector = -1; + +extern atomic_t cpu_doing_single_step; + +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_RAX] = regs->rax; + gdb_regs[_RBX] = regs->rbx; + gdb_regs[_RCX] = regs->rcx; + gdb_regs[_RDX] = regs->rdx; + gdb_regs[_RSI] = regs->rsi; + gdb_regs[_RDI] = regs->rdi; + gdb_regs[_RBP] = regs->rbp; + gdb_regs[_PS] = regs->eflags; + gdb_regs[_PC] = regs->rip; + gdb_regs[_R8] = regs->r8; + gdb_regs[_R9] = regs->r9; + gdb_regs[_R10] = regs->r10; + gdb_regs[_R11] = regs->r11; + gdb_regs[_R12] = regs->r12; + gdb_regs[_R13] = regs->r13; + gdb_regs[_R14] = regs->r14; + gdb_regs[_R15] = regs->r15; + gdb_regs[_RSP] = regs->rsp; +} + +extern void thread_return(void); +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + gdb_regs[_RAX] = 0; + gdb_regs[_RBX] = 0; + gdb_regs[_RCX] = 0; + gdb_regs[_RDX] = 0; + gdb_regs[_RSI] = 0; + gdb_regs[_RDI] = 0; + gdb_regs[_RBP] = *(unsigned long *)p->thread.rsp; + gdb_regs[_PS] = *(unsigned long *)(p->thread.rsp + 8); + gdb_regs[_PC] = (unsigned long)&thread_return; + gdb_regs[_R8] = 0; + gdb_regs[_R9] = 0; + gdb_regs[_R10] = 0; + gdb_regs[_R11] = 0; + gdb_regs[_R12] = 0; + gdb_regs[_R13] = 0; + gdb_regs[_R14] = 0; + gdb_regs[_R15] = 0; + gdb_regs[_RSP] = p->thread.rsp; +} + +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + regs->rax = gdb_regs[_RAX]; + regs->rbx = gdb_regs[_RBX]; + regs->rcx = gdb_regs[_RCX]; + regs->rdx = gdb_regs[_RDX]; + regs->rsi = gdb_regs[_RSI]; + regs->rdi = gdb_regs[_RDI]; + regs->rbp = gdb_regs[_RBP]; + regs->eflags = gdb_regs[_PS]; + regs->rip = gdb_regs[_PC]; + regs->r8 = gdb_regs[_R8]; + regs->r9 = gdb_regs[_R9]; + regs->r10 = gdb_regs[_R10]; + regs->r11 = gdb_regs[_R11]; + regs->r12 = gdb_regs[_R12]; + regs->r13 = gdb_regs[_R13]; + regs->r14 = gdb_regs[_R14]; + regs->r15 = gdb_regs[_R15]; +} /* gdb_regs_to_regs */ + +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned long addr; +} breakinfo[4] = { + { .enabled = 0 }, + { .enabled = 0 }, + { .enabled = 0 }, + { .enabled = 0 }, +}; + +static void kgdb_correct_hw_break(void) +{ + int breakno; + int correctit = 0; + int breakbit; + unsigned long dr7; + + get_debugreg(dr7, 7); + for (breakno = 0; breakno < 4; 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) << + ((breakno << 2) + 16); + switch (breakno) { + case 0: + set_debugreg(breakinfo[breakno].addr, 0); + break; + + case 1: + set_debugreg(breakinfo[breakno].addr, 1); + break; + + case 2: + set_debugreg(breakinfo[breakno].addr, 2); + break; + + case 3: + set_debugreg(breakinfo[breakno].addr, 3); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) + set_debugreg(dr7, 7); +} + +static int kgdb_remove_hw_break(unsigned long addr, int len, + enum kgdb_bptype bptype) +{ + 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; +} + +static void kgdb_remove_all_hw_break(void) +{ + int i; + + for (i = 0; i < 4; i++) { + memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); + } +} + +static int kgdb_set_hw_break(unsigned long addr, int len, + enum kgdb_bptype bptype) +{ + int i, idx = -1; + for (i = 0; i < 4; i++) { + if (!breakinfo[i].enabled) { + idx = i; + break; + } + } + if (idx == -1) + return -1; + if (bptype == bp_hardware_breakpoint) { + breakinfo[idx].type = 0; + breakinfo[idx].len = 0; + } else if (bptype == bp_write_watchpoint) { + breakinfo[idx].type = 1; + if (len == 1 || len == 2 || len == 4) + breakinfo[idx].len = len - 1; + else + return -1; + } else if (bptype == bp_access_watchpoint) { + breakinfo[idx].type = 3; + if (len == 1 || len == 2 || len == 4) + breakinfo[idx].len = len - 1; + else + return -1; + } else + return -1; + breakinfo[idx].enabled = 1; + breakinfo[idx].addr = addr; + return 0; +} + +void kgdb_disable_hw_debug(struct pt_regs *regs) +{ + /* Disable hardware debugging while we are in kgdb */ + set_debugreg(0UL, 7); +} + +void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code) +{ + /* Master processor is completely in the debugger */ + gdb_x86_64vector = e_vector; + gdb_x86_64errcode = 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 *remcomInBuffer, char *remcomOutBuffer, + struct pt_regs *linux_regs) +{ + unsigned long addr; + unsigned long breakno; + char *ptr; + int newPC; + unsigned long dr6; + + switch (remcomInBuffer[0]) { + case 'c': + case 's': + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + linux_regs->rip = addr; + newPC = linux_regs->rip; + + /* 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 (remcomInBuffer[0] == 's') { + linux_regs->eflags |= TF_MASK; + debugger_step = 1; + if (kgdb_contthread) + atomic_set(&cpu_doing_single_step, + raw_smp_processor_id()); + + } + + get_debugreg(dr6, 6); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno)) { + if (breakinfo[breakno].type == 0) { + /* Set restore flag */ + linux_regs->eflags |= + X86_EFLAGS_RF; + break; + } + } + } + } + set_debugreg(0UL, 6); + kgdb_correct_hw_break(); + + return (0); + } /* switch */ + return -1; +} + +static struct pt_regs *in_interrupt_stack(unsigned long rsp, int cpu) +{ + struct pt_regs *regs; + unsigned long end = (unsigned long)cpu_pda(cpu)->irqstackptr; + if (rsp <= end && rsp >= end - IRQSTACKSIZE + 8) { + regs = *(((struct pt_regs **)end) - 1); + return regs; + } + return NULL; +} + +static struct pt_regs *in_exception_stack(unsigned long rsp, int cpu) +{ + int i; + struct tss_struct *init_tss = &__get_cpu_var(init_tss); + for (i = 0; i < N_EXCEPTION_STACKS; i++) + if (rsp >= init_tss[cpu].ist[i] && + rsp <= init_tss[cpu].ist[i] + EXCEPTION_STKSZ) { + struct pt_regs *r = + (void *)init_tss[cpu].ist[i] + EXCEPTION_STKSZ; + return r - 1; + } + return NULL; +} + +void kgdb_shadowinfo(struct pt_regs *regs, char *buffer, unsigned threadid) +{ + static char intr_desc[] = "Stack at interrupt entrypoint"; + static char exc_desc[] = "Stack at exception entrypoint"; + struct pt_regs *stregs; + int cpu = raw_smp_processor_id(); + stregs = in_interrupt_stack(regs->rsp, cpu); + if (stregs) + kgdb_mem2hex(intr_desc, buffer, strlen(intr_desc)); + else { + stregs = in_exception_stack(regs->rsp, cpu); + if (stregs) + kgdb_mem2hex(exc_desc, buffer, strlen(exc_desc)); + } +} + +struct task_struct *kgdb_get_shadow_thread(struct pt_regs *regs, int threadid) +{ + struct pt_regs *stregs; + int cpu = raw_smp_processor_id(); + + stregs = in_interrupt_stack(regs->rsp, cpu); + if (stregs) + return current; + else { + stregs = in_exception_stack(regs->rsp, cpu); + if (stregs) + return current; + } + + return NULL; +} + +struct pt_regs *kgdb_shadow_regs(struct pt_regs *regs, int threadid) +{ + struct pt_regs *stregs; + int cpu = raw_smp_processor_id(); + + stregs = in_interrupt_stack(regs->rsp, cpu); + if (stregs) + return stregs; + else { + stregs = in_exception_stack(regs->rsp, cpu); + if (stregs) + return stregs; + } + + return NULL; +} + +/* Register KGDB with the die_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; + + if (cmd == DIE_PAGE_FAULT_NO_CONTEXT && atomic_read(&debugger_active) + && kgdb_may_fault) { + kgdb_fault_longjmp(kgdb_fault_jmp_regs); + return NOTIFY_STOP; + /* CPU roundup? */ + } else if (atomic_read(&debugger_active) && cmd == DIE_NMI_IPI) { + kgdb_nmihook(raw_smp_processor_id(), regs); + return NOTIFY_STOP; + /* See if KGDB is interested. */ + } else if (cmd == DIE_DEBUG + && atomic_read(&cpu_doing_single_step) == + raw_smp_processor_id() + && user_mode(regs)) { + /* single step exception from kernel space to user space so + * eat the exception and continue the process + */ + printk(KERN_ERR "KGDB: trap/step from kernel to user space," + " resuming...\n"); + kgdb_arch_handle_exception(args->trapnr, args->signr, + args->err, "c", "", regs); + return NOTIFY_STOP; + } else if (cmd == DIE_PAGE_FAULT || user_mode(regs) || + cmd == DIE_NMI_IPI || (cmd == DIE_DEBUG && + atomic_read(&debugger_active))) + /* Userpace events, normal watchdog event, 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, + .priority = 0x7fffffff, /* we need to notified first */ +}; + +int kgdb_arch_init(void) +{ + register_die_notifier(&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->rip - 1)) { + regs->rip -= 1; + return 1; + } + return 0; +} + +unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs) +{ + if (exception == 3) { + return instruction_pointer(regs) - 1; + } + return instruction_pointer(regs); +} + +struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = {0xcc}, + .flags = KGDB_HW_BREAKPOINT, + .shadowth = 1, + .set_hw_breakpoint = kgdb_set_hw_break, + .remove_hw_breakpoint = kgdb_remove_hw_break, + .remove_all_hw_break = kgdb_remove_all_hw_break, + .correct_hw_break = kgdb_correct_hw_break, +}; diff -puN arch/x86_64/mm/fault.c~git-kgdb arch/x86_64/mm/fault.c --- a/arch/x86_64/mm/fault.c~git-kgdb +++ a/arch/x86_64/mm/fault.c @@ -533,6 +533,10 @@ no_context: if (is_errata93(regs, address)) 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 -puN drivers/char/keyboard.c~git-kgdb drivers/char/keyboard.c --- a/drivers/char/keyboard.c~git-kgdb +++ a/drivers/char/keyboard.c @@ -1156,6 +1156,7 @@ static void kbd_keycode(unsigned int key sysrq_down = 0; if (sysrq_down && down && !rep) { handle_sysrq(kbd_sysrq_xlate[keycode], tty); + sysrq_down = 0; /* In case we miss the 'up' event. */ return; } #endif diff -puN drivers/net/Makefile~git-kgdb drivers/net/Makefile --- a/drivers/net/Makefile~git-kgdb +++ a/drivers/net/Makefile @@ -232,6 +232,7 @@ obj-$(CONFIG_ETRAX_ETHERNET) += cris/ obj-$(CONFIG_ENP2611_MSF_NET) += ixp2000/ obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_KGDBOE) += kgdboe.o obj-$(CONFIG_FS_ENET) += fs_enet/ diff -puN /dev/null drivers/net/kgdboe.c --- /dev/null +++ a/drivers/net/kgdboe.c @@ -0,0 +1,294 @@ +/* + * drivers/net/kgdboe.c + * + * A network interface for GDB. + * Based upon 'gdbserial' by David Grothe + * and Scott Foehner + * + * Maintainers: Amit S. Kale and + * Tom Rini + * + * 2004 (c) Amit S. Kale + * 2004-2005 (c) MontaVista Software, Inc. + * 2005 (c) Wind River Systems, Inc. + * + * Contributors at various stages not listed above: + * San Mehat , Robert Walsh , + * wangdi , Matt Mackall , + * Pavel Machek , Jason Wessel + * + * 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 + +#define IN_BUF_SIZE 512 /* power of 2, please */ +#define NOT_CONFIGURED_STRING "not_configured" +#define OUT_BUF_SIZE 30 /* We don't want to send too big of a packet. */ +#define MAX_KGDBOE_CONFIG_STR 256 + +static char in_buf[IN_BUF_SIZE], out_buf[OUT_BUF_SIZE]; +static int in_head, in_tail, out_count; +static atomic_t in_count; +/* 0 = unconfigured, 1 = netpoll options parsed, 2 = fully configured. */ +static int configured; +static struct kgdb_io local_kgdb_io_ops; +static int use_dynamic_mac; + +MODULE_DESCRIPTION("KGDB driver for network interfaces"); +MODULE_LICENSE("GPL"); +static char config[MAX_KGDBOE_CONFIG_STR] = NOT_CONFIGURED_STRING; +static struct kparam_string kps = { + .string = config, + .maxlen = MAX_KGDBOE_CONFIG_STR, +}; + +static void rx_hook(struct netpoll *np, int port, char *msg, int len, + struct sk_buff *skb) +{ + int i; + + np->remote_port = port; + + /* Copy the MAC address if we need to. */ + if (use_dynamic_mac) { + memcpy(np->remote_mac, eth_hdr(skb)->h_source, + sizeof(np->remote_mac)); + use_dynamic_mac = 0; + } + + /* + * This could be GDB trying to attach. But it could also be GDB + * finishing up a session, with kgdb_connected=0 but GDB sending + * an ACK for the final packet. To make sure we don't try and + * make a breakpoint when GDB is leaving, make sure that if + * !kgdb_connected the only len == 1 packet we allow is ^C. + */ + if (!kgdb_connected && (len != 1 || msg[0] == 3) && + !atomic_read(&kgdb_setting_breakpoint)) { + tasklet_schedule(&kgdb_tasklet_breakpoint); + } + + for (i = 0; i < len; i++) { + if (msg[i] == 3) + tasklet_schedule(&kgdb_tasklet_breakpoint); + + if (atomic_read(&in_count) >= IN_BUF_SIZE) { + /* buffer overflow, clear it */ + in_head = in_tail = 0; + atomic_set(&in_count, 0); + break; + } + in_buf[in_head++] = msg[i]; + in_head &= (IN_BUF_SIZE - 1); + atomic_inc(&in_count); + } +} + +static struct netpoll np = { + .dev_name = "eth0", + .name = "kgdboe", + .rx_hook = rx_hook, + .local_port = 6443, + .remote_port = 6442, + .remote_mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, +}; + +static void eth_pre_exception_handler(void) +{ + /* Increment the module count when the debugger is active */ + if (!kgdb_connected) + try_module_get(THIS_MODULE); + netpoll_set_trap(1); +} + +static void eth_post_exception_handler(void) +{ + /* decrement the module count when the debugger detaches */ + if (!kgdb_connected) + module_put(THIS_MODULE); + netpoll_set_trap(0); +} + +static int eth_get_char(void) +{ + int chr; + + while (atomic_read(&in_count) == 0) + netpoll_poll(&np); + + chr = in_buf[in_tail++]; + in_tail &= (IN_BUF_SIZE - 1); + atomic_dec(&in_count); + return chr; +} + +static void eth_flush_buf(void) +{ + if (out_count && np.dev) { + netpoll_send_udp(&np, out_buf, out_count); + memset(out_buf, 0, sizeof(out_buf)); + out_count = 0; + } +} + +static void eth_put_char(u8 chr) +{ + out_buf[out_count++] = chr; + if (out_count == OUT_BUF_SIZE) + eth_flush_buf(); +} + +static int option_setup(char *opt) +{ + char opt_scratch[MAX_KGDBOE_CONFIG_STR]; + + /* If we're being given a new configuration, copy it in. */ + if (opt != config) + strcpy(config, opt); + /* But work on a copy as netpoll_parse_options will eat it. */ + strcpy(opt_scratch, opt); + configured = !netpoll_parse_options(&np, opt_scratch); + + use_dynamic_mac = 1; + + return 0; +} +__setup("kgdboe=", option_setup); + +/* With our config string set by some means, configure kgdboe. */ +static int configure_kgdboe(void) +{ + /* Try out the string. */ + option_setup(config); + + if (!configured) { + printk(KERN_ERR "kgdboe: configuration incorrect - kgdboe not " + "loaded.\n"); + printk(KERN_ERR " Usage: kgdboe=[src-port]@[src-ip]/[dev]," + "[tgt-port]@/\n"); + return -EINVAL; + } + + /* Bring it up. */ + if (netpoll_setup(&np)) { + printk(KERN_ERR "kgdboe: netpoll_setup failed kgdboe failed\n"); + return -EINVAL; + } + + if (kgdb_register_io_module(&local_kgdb_io_ops)) { + netpoll_cleanup(&np); + return -EINVAL; + } + + configured = 2; + + return 0; +} + +static int init_kgdboe(void) +{ + int ret; + + /* Already done? */ + if (configured == 2) + return 0; + + /* OK, go ahead and do it. */ + ret = configure_kgdboe(); + + if (configured == 2) + printk(KERN_INFO "kgdboe: debugging over ethernet enabled\n"); + + return ret; +} + +static void cleanup_kgdboe(void) +{ + netpoll_cleanup(&np); + configured = 0; + kgdb_unregister_io_module(&local_kgdb_io_ops); +} + +static int param_set_kgdboe_var(const char *kmessage, struct kernel_param *kp) +{ + char kmessage_save[MAX_KGDBOE_CONFIG_STR]; + int msg_len = strlen(kmessage); + + if (msg_len + 1 > MAX_KGDBOE_CONFIG_STR) { + printk(KERN_ERR "%s: string doesn't fit in %u chars.\n", + kp->name, MAX_KGDBOE_CONFIG_STR - 1); + return -ENOSPC; + } + + if (kgdb_connected) { + printk(KERN_ERR "kgdboe: Cannot reconfigure while KGDB is " + "connected.\n"); + return 0; + } + + /* Start the reconfiguration process by saving the old string */ + strncpy(kmessage_save, config, sizeof(kmessage_save)); + + + /* Copy in the new param and strip out invalid characters so we + * can optionally specify the MAC. + */ + strncpy(config, kmessage, sizeof(config)); + msg_len--; + while (msg_len > 0 && + (config[msg_len] < ',' || config[msg_len] > 'f')) { + config[msg_len] = '\0'; + msg_len--; + } + + /* Check to see if we are unconfiguring the io module and that it + * was in a fully configured state, as this is the only time that + * netpoll_cleanup should get called + */ + if (configured == 2 && strcmp(config, NOT_CONFIGURED_STRING) == 0) { + printk(KERN_INFO "kgdboe: reverting to unconfigured state\n"); + cleanup_kgdboe(); + return 0; + } else + /* Go and configure with the new params. */ + configure_kgdboe(); + + if (configured == 2) + return 0; + + /* If the new string was invalid, revert to the previous state, which + * is at a minimum not_configured. */ + strncpy(config, kmessage_save, sizeof(config)); + if (strcmp(kmessage_save, NOT_CONFIGURED_STRING) != 0) { + printk(KERN_INFO "kgdboe: reverting to prior configuration\n"); + /* revert back to the original config */ + strncpy(config, kmessage_save, sizeof(config)); + configure_kgdboe(); + } + return 0; +} + +static struct kgdb_io local_kgdb_io_ops = { + .read_char = eth_get_char, + .write_char = eth_put_char, + .init = init_kgdboe, + .flush = eth_flush_buf, + .pre_exception = eth_pre_exception_handler, + .post_exception = eth_post_exception_handler +}; + +module_init(init_kgdboe); +module_exit(cleanup_kgdboe); +module_param_call(kgdboe, param_set_kgdboe_var, param_get_string, &kps, 0644); +MODULE_PARM_DESC(kgdboe, " kgdboe=[src-port]@[src-ip]/[dev]," + "[tgt-port]@/\n"); diff -puN drivers/serial/8250.c~git-kgdb drivers/serial/8250.c --- a/drivers/serial/8250.c~git-kgdb +++ a/drivers/serial/8250.c @@ -2859,6 +2859,25 @@ void serial8250_unregister_port(int line } EXPORT_SYMBOL(serial8250_unregister_port); +/** + * serial8250_unregister_by_port - remove a 16x50 serial port + * at runtime. + * @port: A &struct uart_port that describes the port to remove. + * + * Remove one serial port. This may not be called from interrupt + * context. We hand the port back to the our control. + */ +void serial8250_unregister_by_port(struct uart_port *port) +{ + struct uart_8250_port *uart; + + uart = serial8250_find_match_or_unused(port); + + if (uart) + serial8250_unregister_port(uart->port.line); +} +EXPORT_SYMBOL(serial8250_unregister_by_port); + static int __init serial8250_init(void) { int ret, i; diff -puN /dev/null drivers/serial/8250_kgdb.c --- /dev/null +++ a/drivers/serial/8250_kgdb.c @@ -0,0 +1,515 @@ +/* + * 8250 interface for kgdb. + * + * This is a merging of many different drivers, and all of the people have + * had an impact in some form or another: + * + * 2004-2005 (c) MontaVista Software, Inc. + * 2005-2006 (c) Wind River Systems, Inc. + * + * Amit Kale , David Grothe , + * Scott Foehner , George Anzinger , + * Robert Walsh , wangdi , + * San Mehat, Tom Rini , + * Jason Wessel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For BASE_BAUD and SERIAL_PORT_DFNS */ + +#include "8250.h" + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +MODULE_DESCRIPTION("KGDB driver for the 8250"); +MODULE_LICENSE("GPL"); +/* These will conflict with early_param otherwise. */ +#ifdef CONFIG_KGDB_8250_MODULE +static char config[256]; +module_param_string(kgdb8250, config, 256, 0); +MODULE_PARM_DESC(kgdb8250, + " kgdb8250=,
,,\n"); +static struct kgdb_io local_kgdb_io_ops; +#endif /* CONFIG_KGDB_8250_MODULE */ + +/* Speed of the UART. */ +static int kgdb8250_baud; + +/* Flag for if we need to call request_mem_region */ +static int kgdb8250_needs_request_mem_region; + +static char kgdb8250_buf[GDB_BUF_SIZE]; +static atomic_t kgdb8250_buf_in_cnt; +static int kgdb8250_buf_out_inx; + +/* Old-style serial definitions, if existant, and a counter. */ +#ifdef CONFIG_KGDB_SIMPLE_SERIAL +static int __initdata should_copy_rs_table = 1; +static struct serial_state old_rs_table[] __initdata = { +#ifdef SERIAL_PORT_DFNS + SERIAL_PORT_DFNS +#endif +}; +#endif + +/* Our internal table of UARTS. */ +#define UART_NR CONFIG_SERIAL_8250_NR_UARTS +static struct uart_port kgdb8250_ports[UART_NR]; + +static struct uart_port *current_port; + +/* Base of the UART. */ +static void *kgdb8250_addr; + +/* Forward declarations. */ +static int kgdb8250_uart_init(void); +static int __init kgdb_init_io(void); +static int __init kgdb8250_opt(char *str); + +/* These are much shorter calls to ioread8/iowrite8 that take into + * account our shifts, etc. */ +static inline unsigned int kgdb_ioread(u8 mask) +{ + return ioread8(kgdb8250_addr + (mask << current_port->regshift)); +} + +static inline void kgdb_iowrite(u8 val, u8 mask) +{ + iowrite8(val, kgdb8250_addr + (mask << current_port->regshift)); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void kgdb_put_debug_char(u8 chr) +{ + while (!(kgdb_ioread(UART_LSR) & UART_LSR_THRE)) ; + + kgdb_iowrite(chr, UART_TX); +} + +/* + * Get a byte from the hardware data buffer and return it + */ +static int read_data_bfr(void) +{ + char it = kgdb_ioread(UART_LSR); + + if (it & UART_LSR_DR) + return kgdb_ioread(UART_RX); + + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + kgdb8250_uart_init(); + kgdb_put_debug_char('-'); + return '-'; + } + + return -1; +} + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + */ +static int kgdb_get_debug_char(void) +{ + int retchr; + + /* intr routine has q'd chars */ + if (atomic_read(&kgdb8250_buf_in_cnt) != 0) { + retchr = kgdb8250_buf[kgdb8250_buf_out_inx++]; + kgdb8250_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&kgdb8250_buf_in_cnt); + return retchr; + } + + do { + retchr = read_data_bfr(); + } while (retchr < 0); + + return retchr; +} + +/* + * This is the receiver interrupt routine for the GDB stub. + * All that we need to do is verify that the interrupt happened on the + * line we're in charge of. If this is true, schedule a breakpoint and + * return. + */ +static irqreturn_t +kgdb8250_interrupt(int irq, void *dev_id) +{ + if (kgdb_ioread(UART_IIR) & UART_IIR_RDI) { + /* Throw away the data if another I/O routine is active. */ + if (kgdb_io_ops.read_char != kgdb_get_debug_char && + (kgdb_ioread(UART_LSR) & UART_LSR_DR)) + kgdb_ioread(UART_RX); + else + breakpoint(); + } + + return IRQ_HANDLED; +} + +/* + * Initializes the UART. + * Returns: + * 0 on success, 1 on failure. + */ +static int +kgdb8250_uart_init (void) +{ + unsigned int ier, base_baud = current_port->uartclk ? + current_port->uartclk / 16 : BASE_BAUD; + + /* test uart existance */ + if (kgdb_ioread(UART_LSR) == 0xff) + return -1; + + /* disable interrupts */ + kgdb_iowrite(0, UART_IER); + +#if defined(CONFIG_ARCH_OMAP1510) + /* Workaround to enable 115200 baud on OMAP1510 internal ports */ + if (cpu_is_omap1510() && is_omap_port((void *)kgdb8250_addr)) { + if (kgdb8250_baud == 115200) { + base_baud = 1; + kgdb8250_baud = 1; + kgdb_iowrite(1, UART_OMAP_OSC_12M_SEL); + } else + kgdb_iowrite(0, UART_OMAP_OSC_12M_SEL); + } +#endif + /* set DLAB */ + kgdb_iowrite(UART_LCR_DLAB, UART_LCR); + + /* set baud */ + kgdb_iowrite((base_baud / kgdb8250_baud) & 0xff, UART_DLL); + kgdb_iowrite((base_baud / kgdb8250_baud) >> 8, UART_DLM); + + /* reset DLAB, set LCR */ + kgdb_iowrite(UART_LCR_WLEN8, UART_LCR); + + /* set DTR and RTS */ + kgdb_iowrite(UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS, UART_MCR); + + /* setup fifo */ + kgdb_iowrite(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR + | UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_8, + UART_FCR); + + /* clear pending interrupts */ + kgdb_ioread(UART_IIR); + kgdb_ioread(UART_RX); + kgdb_ioread(UART_LSR); + kgdb_ioread(UART_MSR); + + /* turn on RX interrupt only */ + kgdb_iowrite(UART_IER_RDI, UART_IER); + + /* + * Borrowed from the main 8250 driver. + * Try writing and reading the UART_IER_UUE bit (b6). + * If it works, this is probably one of the Xscale platform's + * internal UARTs. + * We're going to explicitly set the UUE bit to 0 before + * trying to write and read a 1 just to make sure it's not + * already a 1 and maybe locked there before we even start start. + */ + ier = kgdb_ioread(UART_IER); + kgdb_iowrite(ier & ~UART_IER_UUE, UART_IER); + if (!(kgdb_ioread(UART_IER) & UART_IER_UUE)) { + /* + * OK it's in a known zero state, try writing and reading + * without disturbing the current state of the other bits. + */ + kgdb_iowrite(ier | UART_IER_UUE, UART_IER); + if (kgdb_ioread(UART_IER) & UART_IER_UUE) + /* + * It's an Xscale. + */ + ier |= UART_IER_UUE | UART_IER_RTOIE; + } + kgdb_iowrite(ier, UART_IER); + return 0; +} + +/* + * Copy the old serial_state table to our uart_port table if we haven't + * had values specifically configured in. We need to make sure this only + * happens once. + */ +static void __init kgdb8250_copy_rs_table(void) +{ +#ifdef CONFIG_KGDB_SIMPLE_SERIAL + int i; + + if (!should_copy_rs_table) + return; + + for (i = 0; i < ARRAY_SIZE(old_rs_table); i++) { + kgdb8250_ports[i].iobase = old_rs_table[i].port; + kgdb8250_ports[i].irq = irq_canonicalize(old_rs_table[i].irq); + kgdb8250_ports[i].uartclk = old_rs_table[i].baud_base * 16; + kgdb8250_ports[i].membase = old_rs_table[i].iomem_base; + kgdb8250_ports[i].iotype = old_rs_table[i].io_type; + kgdb8250_ports[i].regshift = old_rs_table[i].iomem_reg_shift; + kgdb8250_ports[i].line = i; + } + + should_copy_rs_table = 0; +#endif +} + +/* + * Hookup our IRQ line now that it is safe to do so, after we grab any + * memory regions we might need to. If we haven't been initialized yet, + * go ahead and copy the old_rs_table in. + */ +static void __init kgdb8250_late_init(void) +{ + /* Try and copy the old_rs_table. */ + kgdb8250_copy_rs_table(); + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + /* Take the port away from the main driver. */ + serial8250_unregister_by_port(current_port); + + /* Now reinit the port as the above has disabled things. */ + kgdb8250_uart_init(); +#endif + /* We may need to call request_mem_region() first. */ + if (kgdb8250_needs_request_mem_region) + request_mem_region(current_port->mapbase, + 8 << current_port->regshift, "kgdb"); + if (request_irq(current_port->irq, kgdb8250_interrupt, IRQF_SHARED, + "GDB-stub", current_port) < 0) + printk(KERN_ERR "KGDB failed to request the serial IRQ (%d)\n", + current_port->irq); +} + +static __init int kgdb_init_io(void) +{ + /* Give us the basic table of uarts. */ + kgdb8250_copy_rs_table(); + + /* We're either a module and parse a config string, or we have a + * semi-static config. */ +#ifdef CONFIG_KGDB_8250_MODULE + if (strlen(config)) { + if (kgdb8250_opt(config)) + return -EINVAL; + } else { + printk(KERN_ERR "kgdb8250: argument error, usage: " + "kgdb8250=,
,,\n"); + return -EINVAL; + } +#elif defined(CONFIG_KGDB_SIMPLE_SERIAL) + kgdb8250_baud = CONFIG_KGDB_BAUDRATE; + + /* Setup our pointer to the serial port now. */ + current_port = &kgdb8250_ports[CONFIG_KGDB_PORT_NUM]; +#else + if (kgdb8250_opt(CONFIG_KGDB_8250_CONF_STRING)) + return -EINVAL; +#endif + + + /* Internal driver setup. */ + switch (current_port->iotype) { + case UPIO_MEM: + if (current_port->mapbase) + kgdb8250_needs_request_mem_region = 1; + if (current_port->flags & UPF_IOREMAP) { + current_port->membase = ioremap(current_port->mapbase, + 8 << current_port->regshift); + if (!current_port->membase) + return -EIO; /* Failed. */ + } + kgdb8250_addr = current_port->membase; + break; + case UPIO_PORT: + default: + kgdb8250_addr = ioport_map(current_port->iobase, + 8 << current_port->regshift); + if (!kgdb8250_addr) + return -EIO; /* Failed. */ + } + + if (kgdb8250_uart_init() == -1) { + printk(KERN_ERR "kgdb8250: init failed\n"); + return -EIO; + } +#ifdef CONFIG_KGDB_8250_MODULE + /* Attach the kgdb irq. When this is built into the kernel, it + * is called as a part of late_init sequence. + */ + kgdb8250_late_init(); + if (kgdb_register_io_module(&local_kgdb_io_ops)) + return -EINVAL; + + printk(KERN_INFO "kgdb8250: debugging enabled\n"); +#endif /* CONFIG_KGD_8250_MODULE */ + + return 0; +} + +#ifdef CONFIG_KGDB_8250_MODULE +/* If it is a module the kgdb_io_ops should be a static which + * is passed to the KGDB I/O initialization + */ +static struct kgdb_io local_kgdb_io_ops = { +#else /* ! CONFIG_KGDB_8250_MODULE */ +struct kgdb_io kgdb_io_ops = { +#endif /* ! CONFIG_KGD_8250_MODULE */ + .read_char = kgdb_get_debug_char, + .write_char = kgdb_put_debug_char, + .init = kgdb_init_io, + .late_init = kgdb8250_late_init, +}; + +/** + * kgdb8250_add_port - Define a serial port for use with KGDB + * @i: The index of the port being added + * @serial_req: The &struct uart_port describing the port + * + * On platforms where we must register the serial device + * dynamically, this is the best option if a platform also normally + * calls early_serial_setup(). + */ +void kgdb8250_add_port(int i, struct uart_port *serial_req) +{ + /* Make sure we've got the built-in data before we override. */ + kgdb8250_copy_rs_table(); + + /* Copy the whole thing over. */ + if (current_port != &kgdb8250_ports[i]) + memcpy(&kgdb8250_ports[i], serial_req, + sizeof(struct uart_port)); +} + +/** + * kgdb8250_add_platform_port - Define a serial port for use with KGDB + * @i: The index of the port being added + * @p: The &struct plat_serial8250_port describing the port + * + * On platforms where we must register the serial device + * dynamically, this is the best option if a platform normally + * handles uart setup with an array of &struct plat_serial8250_port. + */ +void __init kgdb8250_add_platform_port(int i, struct plat_serial8250_port *p) +{ + /* Make sure we've got the built-in data before we override. */ + kgdb8250_copy_rs_table(); + + kgdb8250_ports[i].iobase = p->iobase; + kgdb8250_ports[i].membase = p->membase; + kgdb8250_ports[i].irq = p->irq; + kgdb8250_ports[i].uartclk = p->uartclk; + kgdb8250_ports[i].regshift = p->regshift; + kgdb8250_ports[i].iotype = p->iotype; + kgdb8250_ports[i].flags = p->flags; + kgdb8250_ports[i].mapbase = p->mapbase; +} + +/* + * Syntax for this cmdline option is: + * kgdb8250=,
,," + */ +static int __init kgdb8250_opt(char *str) +{ + /* We'll fill out and use the first slot. */ + current_port = &kgdb8250_ports[0]; + + if (!strncmp(str, "io", 2)) { + current_port->iotype = UPIO_PORT; + str += 2; + } else if (!strncmp(str, "mmap", 4)) { + current_port->iotype = UPIO_MEM; + current_port->flags |= UPF_IOREMAP; + str += 4; + } else if (!strncmp(str, "mmio", 4)) { + current_port->iotype = UPIO_MEM; + current_port->flags &= ~UPF_IOREMAP; + str += 4; + } else + goto errout; + + if (*str != ',') + goto errout; + str++; + + if (current_port->iotype == UPIO_PORT) + current_port->iobase = simple_strtoul(str, &str, 16); + else { + if (current_port->flags & UPF_IOREMAP) + current_port->mapbase = + (unsigned long) simple_strtoul(str, &str, 16); + else + current_port->membase = + (void *) simple_strtoul(str, &str, 16); + } + + if (*str != ',') + goto errout; + str++; + + kgdb8250_baud = simple_strtoul(str, &str, 10); + if (!kgdb8250_baud) + goto errout; + + if (*str != ',') + goto errout; + str++; + + current_port->irq = simple_strtoul(str, &str, 10); + +#ifdef CONFIG_KGDB_SIMPLE_SERIAL + should_copy_rs_table = 0; +#endif + + return 0; + +errout: + printk(KERN_ERR "Invalid syntax for option kgdb8250=\n"); + return 1; +} + +#ifdef CONFIG_KGDB_8250_MODULE +static void cleanup_kgdb8250(void) +{ + kgdb_unregister_io_module(&local_kgdb_io_ops); + + /* Clean up the irq and memory */ + free_irq(current_port->irq, current_port); + + if (kgdb8250_needs_request_mem_region) + release_mem_region(current_port->mapbase, + 8 << current_port->regshift); + /* Hook up the serial port back to what it was previously + * hooked up to. + */ +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + /* Give the port back to the 8250 driver. */ + serial8250_register_port(current_port); +#endif +} + +module_init(kgdb_init_io); +module_exit(cleanup_kgdb8250); +#else /* ! CONFIG_KGDB_8250_MODULE */ +early_param("kgdb8250", kgdb8250_opt); +#endif /* ! CONFIG_KGDB_8250_MODULE */ diff -puN drivers/serial/Kconfig~git-kgdb drivers/serial/Kconfig --- a/drivers/serial/Kconfig~git-kgdb +++ a/drivers/serial/Kconfig @@ -121,7 +121,7 @@ config SERIAL_8250_CS config SERIAL_8250_NR_UARTS int "Maximum number of 8250/16550 serial ports" - depends on SERIAL_8250 + depends on SERIAL_8250 || KGDB_8250 default "4" help Set this to the number of serial ports you want the driver diff -puN drivers/serial/Makefile~git-kgdb drivers/serial/Makefile --- a/drivers/serial/Makefile~git-kgdb +++ a/drivers/serial/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mc obj-$(CONFIG_SERIAL_8250_AU1X00) += 8250_au1x00.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o +obj-$(CONFIG_KGDB_AMBA_PL011) += pl011_kgdb.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_PXA) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o @@ -51,11 +52,13 @@ obj-$(CONFIG_SERIAL_IMX) += imx.o obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o +obj-$(CONFIG_KGDB_MPSC) += mpsc_kgdb.o obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o +obj-$(CONFIG_KGDB_TXX9) += serial_txx9_kgdb.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o @@ -64,3 +67,4 @@ obj-$(CONFIG_SERIAL_UARTLITE) += uartlit obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o +obj-$(CONFIG_KGDB_8250) += 8250_kgdb.o diff -puN drivers/serial/amba-pl011.c~git-kgdb drivers/serial/amba-pl011.c --- a/drivers/serial/amba-pl011.c~git-kgdb +++ a/drivers/serial/amba-pl011.c @@ -332,7 +332,8 @@ static int pl011_startup(struct uart_por /* * Allocate the IRQ */ - retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); + retval = request_irq(uap->port.irq, pl011_int, SA_SHIRQ, + "uart-pl011", uap); if (retval) goto clk_dis; diff -puN drivers/serial/cpm_uart/Makefile~git-kgdb drivers/serial/cpm_uart/Makefile --- a/drivers/serial/cpm_uart/Makefile~git-kgdb +++ a/drivers/serial/cpm_uart/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o # Select the correct platform objects. cpm_uart-objs-$(CONFIG_CPM2) += cpm_uart_cpm2.o cpm_uart-objs-$(CONFIG_8xx) += cpm_uart_cpm1.o +cpm_uart-objs-$(CONFIG_KGDB_CPM_UART) += cpm_uart_kgdb.o cpm_uart-objs := cpm_uart_core.o $(cpm_uart-objs-y) diff -puN drivers/serial/cpm_uart/cpm_uart.h~git-kgdb drivers/serial/cpm_uart/cpm_uart.h --- a/drivers/serial/cpm_uart/cpm_uart.h~git-kgdb +++ a/drivers/serial/cpm_uart/cpm_uart.h @@ -50,6 +50,41 @@ #define SCC_WAIT_CLOSING 100 +#ifdef CONFIG_KGDB_CPM_UART + +/* Speed of the debug UART. */ +#if CONFIG_KGDB_BAUDRATE == 9600 +#define KGDB_BAUD B9600 +#elif CONFIG_KGDB_BAUDRATE == 19200 +#define KGDB_BAUD B19200 +#elif CONFIG_KGDB_BAUDRATE == 38400 +#define KGDB_BAUD B38400 +#elif CONFIG_KGDB_BAUDRATE == 57600 +#define KGDB_BAUD B57600 +#elif CONFIG_KGDB_BAUDRATE == 115200 +#define KGDB_BAUD B115200 /* Start with this if not given */ +#else +#error Unsupported baud rate +#endif + +#if defined(CONFIG_KGDB_CPM_UART_SCC1) +#define KGDB_PINFO_INDEX UART_SCC1 +#elif defined(CONFIG_KGDB_CPM_UART_SCC2) +#define KGDB_PINFO_INDEX UART_SCC2 +#elif defined(CONFIG_KGDB_CPM_UART_SCC3) +#define KGDB_PINFO_INDEX UART_SCC3 +#elif defined(CONFIG_KGDB_CPM_UART_SCC4) +#define KGDB_PINFO_INDEX UART_SCC4 +#elif defined(CONFIG_KGDB_CPM_UART_SMC1) +#define KGDB_PINFO_INDEX UART_SMC1 +#elif defined(CONFIG_KGDB_CPM_UART_SMC2) +#define KGDB_PINFO_INDEX UART_SMC2 +#else +#error The port for KGDB is undefined +#endif + +#endif /* CONFIG_KGDB_CPM_UART */ + struct uart_cpm_port { struct uart_port port; u16 rx_nrfifos; @@ -86,6 +121,9 @@ extern int cpm_uart_port_map[UART_NR]; extern int cpm_uart_nr; extern struct uart_cpm_port cpm_uart_ports[UART_NR]; +void cpm_uart_early_write(int index, const char *s, u_int count); +int cpm_uart_early_setup(int index, int early); + /* these are located in their respective files */ void cpm_line_cr_cmd(int line, int cmd); int cpm_uart_init_portdesc(void); @@ -132,5 +170,4 @@ static inline void *cpm2cpu_addr(unsigne return 0; } - #endif /* CPM_UART_H */ diff -puN drivers/serial/cpm_uart/cpm_uart_core.c~git-kgdb drivers/serial/cpm_uart/cpm_uart_core.c --- a/drivers/serial/cpm_uart/cpm_uart_core.c~git-kgdb +++ a/drivers/serial/cpm_uart/cpm_uart_core.c @@ -1073,22 +1073,17 @@ int cpm_uart_drv_get_platform_data(struc return 0; } -#ifdef CONFIG_SERIAL_CPM_CONSOLE -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * Note that this is called with interrupts already disabled - */ -static void cpm_uart_console_write(struct console *co, const char *s, +void cpm_uart_early_write(int index, const char *s, u_int count) { - struct uart_cpm_port *pinfo = - &cpm_uart_ports[cpm_uart_port_map[co->index]]; + struct uart_cpm_port *pinfo; unsigned int i; volatile cbd_t *bdp, *bdbase; volatile unsigned char *cp; + BUG_ON(index > UART_NR); + pinfo = &cpm_uart_ports[index]; + /* Get the address of the host memory buffer. */ bdp = pinfo->tx_cur; @@ -1152,19 +1147,14 @@ static void cpm_uart_console_write(struc pinfo->tx_cur = (volatile cbd_t *) bdp; } - -static int __init cpm_uart_console_setup(struct console *co, char *options) +int cpm_uart_early_setup(int index, int early) { + int ret; struct uart_port *port; struct uart_cpm_port *pinfo; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; struct fs_uart_platform_info *pdata; - struct platform_device* pdev = early_uart_get_pdev(co->index); + struct platform_device *pdev = early_uart_get_pdev(index); if (!pdev) { pr_info("cpm_uart: console: compat mode\n"); @@ -1172,8 +1162,9 @@ static int __init cpm_uart_console_setup cpm_uart_init_portdesc(); } + BUG_ON(index > UART_NR); port = - (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]]; + (struct uart_port *)&cpm_uart_ports[index]; pinfo = (struct uart_cpm_port *)port; if (!pdev) { if (pinfo->set_lineif) @@ -1187,15 +1178,6 @@ static int __init cpm_uart_console_setup cpm_uart_drv_get_platform_data(pdev, 1); } - pinfo->flags |= FLAG_CONSOLE; - - if (options) { - uart_parse_options(options, &baud, &parity, &bits, &flow); - } else { - if ((baud = uart_baudrate()) == -1) - baud = 9600; - } - if (IS_SMC(pinfo)) { pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); @@ -1203,8 +1185,7 @@ static int __init cpm_uart_console_setup pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); } - - ret = cpm_uart_allocbuf(pinfo, 1); + ret = cpm_uart_allocbuf(pinfo, early); if (ret) return ret; @@ -1216,6 +1197,62 @@ static int __init cpm_uart_console_setup else cpm_uart_init_scc(pinfo); + return 0; +} + +#ifdef CONFIG_SERIAL_CPM_CONSOLE +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * Note that this is called with interrupts already disabled + */ + +static void cpm_uart_console_write(struct console *co, const char *s, + u_int count) +{ + cpm_uart_early_write(cpm_uart_port_map[co->index], s, count); +} + +/* + * Setup console. Be careful is called early ! + */ +static int __init cpm_uart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + struct uart_cpm_port *pinfo; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + +#ifdef CONFIG_KGDB_CPM_UART + /* We are not interested in ports yet utilized by kgdb */ + if (co->index == KGDB_PINFO_INDEX) + return 0; +#endif + + port = + (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]]; + pinfo = (struct uart_cpm_port *)port; + + pinfo->flags |= FLAG_CONSOLE; + + if (options) { + uart_parse_options(options, &baud, &parity, &bits, &flow); + } else { + bd_t *bd = (bd_t *) __res; + + if (bd->bi_baudrate) + baud = bd->bi_baudrate; + else + baud = 9600; + } + + ret = cpm_uart_early_setup(cpm_uart_port_map[co->index], 1); + if (ret) + return ret; uart_set_options(port, co, baud, parity, bits, flow); return 0; @@ -1266,6 +1303,12 @@ static int cpm_uart_drv_probe(struct dev pdata = pdev->dev.platform_data; +#ifdef CONFIG_KGDB_CPM_UART + /* We are not interested in ports yet utilized by kgdb */ + if (cpm_uart_id2nr(fs_uart_get_id(pdata)) == KGDB_PINFO_INDEX) + return ret; +#endif + if ((ret = cpm_uart_drv_get_platform_data(pdev, 0))) return ret; @@ -1363,6 +1406,12 @@ static int cpm_uart_init(void) { for (i = 0; i < cpm_uart_nr; i++) { int con = cpm_uart_port_map[i]; + +#ifdef CONFIG_KGDB_CPM_UART + /* We are not interested in ports yet utilized by kgdb */ + if (con == KGDB_PINFO_INDEX) + continue; +#endif cpm_uart_ports[con].port.line = i; cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF; if (cpm_uart_ports[con].set_lineif) diff -puN drivers/serial/cpm_uart/cpm_uart_cpm1.c~git-kgdb drivers/serial/cpm_uart/cpm_uart_cpm1.c --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c~git-kgdb +++ a/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -53,6 +53,7 @@ void cpm_line_cr_cmd(int line, int cmd) { ushort val; volatile cpm8xx_t *cp = cpmp; + unsigned *bcsr_io; switch (line) { case UART_SMC1: @@ -95,12 +96,35 @@ void scc1_lineif(struct uart_cpm_port *p { /* XXX SCC1: insert port configuration here */ pinfo->brg = 1; + +#if defined (CONFIG_MPC885ADS) || defined (CONFIG_MPC86XADS) + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + + if (bcsr_io == NULL) { + printk(KERN_CRIT "Could not remap BCSR\n"); + return; + } + out_be32(bcsr_io, in_be32(bcsr_io) & ~BCSR1_RS232EN_1); + iounmap(bcsr_io); +#endif } void scc2_lineif(struct uart_cpm_port *pinfo) { /* XXX SCC2: insert port configuration here */ pinfo->brg = 2; + unsigned *bcsr_io; + +#if defined (CONFIG_MPC885ADS) || defined (CONFIG_MPC86XADS) + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + + if (bcsr_io == NULL) { + printk(KERN_CRIT "Could not remap BCSR\n"); + return; + } + out_be32(bcsr_io, in_be32(bcsr_io) & ~BCSR1_RS232EN_2); + iounmap(bcsr_io); +#endif } void scc3_lineif(struct uart_cpm_port *pinfo) @@ -189,6 +213,10 @@ int cpm_uart_init_portdesc(void) { pr_debug("CPM uart[-]:init portdesc\n"); + /* Check if we have called this yet. This may happen if early kgdb + breakpoint is on */ + if (cpm_uart_nr) + return 0; cpm_uart_nr = 0; #ifdef CONFIG_SERIAL_CPM_SMC1 cpm_uart_ports[UART_SMC1].smcp = &cpmp->cp_smc[0]; diff -puN drivers/serial/cpm_uart/cpm_uart_cpm2.c~git-kgdb drivers/serial/cpm_uart/cpm_uart_cpm2.c --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c~git-kgdb +++ a/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -289,6 +289,10 @@ int cpm_uart_init_portdesc(void) #endif pr_debug("CPM uart[-]:init portdesc\n"); + /* Check if we have called this yet. This may happen if early kgdb + breakpoint is on */ + if (cpm_uart_nr) + return 0; cpm_uart_nr = 0; #ifdef CONFIG_SERIAL_CPM_SMC1 cpm_uart_ports[UART_SMC1].smcp = (smc_t *) cpm2_map(im_smc[0]); diff -puN /dev/null drivers/serial/cpm_uart/cpm_uart_kgdb.c --- /dev/null +++ a/drivers/serial/cpm_uart/cpm_uart_kgdb.c @@ -0,0 +1,185 @@ +/* + * drivers/serial/cpm_uart/cpm_uart_kgdb.c + * + * CPM UART interface for kgdb. + * + * Author: Vitaly Bordug + * + * Used some bits from drivers/serial/kgdb_8250.c as a template + * + * 2005-2007 (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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For BASE_BAUD and SERIAL_PORT_DFNS */ + +#include "cpm_uart.h" + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + + +static char kgdb_buf[GDB_BUF_SIZE], *kgdbp; +static int kgdb_chars; + +/* Forward declarations. */ + +/* + * Receive character from the serial port. This only works well + * before the port is initialize for real use. + */ +static int kgdb_wait_key(char *obuf) +{ + struct uart_cpm_port *pinfo; + u_char c, *cp; + volatile cbd_t *bdp; + int i; + + pinfo = &cpm_uart_ports[KGDB_PINFO_INDEX]; + + /* Get the address of the host memory buffer. + */ + bdp = pinfo->rx_cur; + while (bdp->cbd_sc & BD_SC_EMPTY); + + /* If the buffer address is in the CPM DPRAM, don't + * convert it. + */ + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); + + if (obuf) { + i = c = bdp->cbd_datlen; + while (i-- > 0) + *obuf++ = *cp++; + } else + c = *cp; + bdp->cbd_sc |= BD_SC_EMPTY; + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = pinfo->rx_bd_base; + else + bdp++; + pinfo->rx_cur = (cbd_t *)bdp; + + return (int)c; +} + + +/* + * Wait until the interface can accept a char, then write it. + */ +static void kgdb_put_debug_char(u8 chr) +{ + static char ch[2]; + + ch[0] = (char)chr; + cpm_uart_early_write(KGDB_PINFO_INDEX, ch, 1); +} + + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + */ +static int kgdb_get_debug_char(void) +{ + if (kgdb_chars <= 0) { + kgdb_chars = kgdb_wait_key(kgdb_buf); + kgdbp = kgdb_buf; + } + kgdb_chars--; + + return (*kgdbp++); +} + +static void termios_set_options(int index, + int baud, int parity, int bits, int flow) +{ + struct ktermios termios; + struct uart_port *port; + struct uart_cpm_port *pinfo; + + BUG_ON(index > UART_NR); + + port = (struct uart_port *)&cpm_uart_ports[index]; + pinfo = (struct uart_cpm_port *)port; + + /* + * Ensure that the serial console lock is initialised + * early. + */ + spin_lock_init(&port->lock); + + memset(&termios, 0, sizeof(struct termios)); + + termios.c_cflag = CREAD | HUPCL | CLOCAL; + + termios.c_cflag |= baud; + + if (bits == 7) + termios.c_cflag |= CS7; + else + termios.c_cflag |= CS8; + + switch (parity) { + case 'o': case 'O': + termios.c_cflag |= PARODD; + /*fall through*/ + case 'e': case 'E': + termios.c_cflag |= PARENB; + break; + } + + if (flow == 'r') + termios.c_cflag |= CRTSCTS; + + port->ops->set_termios(port, &termios, NULL); +} + +/* + * Returns: + * 0 on success, 1 on failure. + */ +static int kgdb_init(void) +{ + struct uart_port *port; + struct uart_cpm_port *pinfo; + int use_bootmem = 0; /* use dma by default */ + + if (!cpm_uart_nr) { + use_bootmem = 1; + cpm_uart_init_portdesc(); + } + port = (struct uart_port *)&cpm_uart_ports[KGDB_PINFO_INDEX]; + pinfo = (struct uart_cpm_port *)port; + + if (cpm_uart_early_setup(KGDB_PINFO_INDEX, use_bootmem)) + return 1; + + termios_set_options(KGDB_PINFO_INDEX, KGDB_BAUD, 'n', 8, 'n'); + if (IS_SMC(pinfo)) + pinfo->smcp->smc_smcm |= SMCM_TX; + else + pinfo->sccp->scc_sccm |= UART_SCCM_TX; + + return 0; +} + + +struct kgdb_io kgdb_io_ops = { + .read_char = kgdb_get_debug_char, + .write_char = kgdb_put_debug_char, + .init = kgdb_init, +}; + diff -puN /dev/null drivers/serial/mpsc_kgdb.c --- /dev/null +++ a/drivers/serial/mpsc_kgdb.c @@ -0,0 +1,347 @@ +/* + * drivers/serial/mpsc_kgdb.c + * + * KGDB driver for the Marvell MultiProtocol Serial Controller (MPCS) + * + * Based on the polled boot loader driver by Ajit Prem (ajit.prem@motorola.com) + * + * Author: Randy Vinson + * + * Copyright (C) 2005-2006 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Main MPSC Configuration Register Offsets */ +#define MPSC_MMCRL 0x0000 +#define MPSC_MMCRH 0x0004 +#define MPSC_MPCR 0x0008 +#define MPSC_CHR_1 0x000c +#define MPSC_CHR_2 0x0010 +#define MPSC_CHR_3 0x0014 +#define MPSC_CHR_4 0x0018 +#define MPSC_CHR_5 0x001c +#define MPSC_CHR_6 0x0020 +#define MPSC_CHR_7 0x0024 +#define MPSC_CHR_8 0x0028 +#define MPSC_CHR_9 0x002c +#define MPSC_CHR_10 0x0030 +#define MPSC_CHR_11 0x0034 + +#define MPSC_MPCR_FRZ (1 << 9) +#define MPSC_MPCR_CL_5 0 +#define MPSC_MPCR_CL_6 1 +#define MPSC_MPCR_CL_7 2 +#define MPSC_MPCR_CL_8 3 +#define MPSC_MPCR_SBL_1 0 +#define MPSC_MPCR_SBL_2 1 + +#define MPSC_CHR_2_TEV (1<<1) +#define MPSC_CHR_2_TA (1<<7) +#define MPSC_CHR_2_TTCS (1<<9) +#define MPSC_CHR_2_REV (1<<17) +#define MPSC_CHR_2_RA (1<<23) +#define MPSC_CHR_2_CRD (1<<25) +#define MPSC_CHR_2_EH (1<<31) +#define MPSC_CHR_2_PAR_ODD 0 +#define MPSC_CHR_2_PAR_SPACE 1 +#define MPSC_CHR_2_PAR_EVEN 2 +#define MPSC_CHR_2_PAR_MARK 3 + +/* MPSC Signal Routing */ +#define MPSC_MRR 0x0000 +#define MPSC_RCRR 0x0004 +#define MPSC_TCRR 0x0008 + +/* MPSC Interrupt registers (offset from MV64x60_SDMA_INTR_OFFSET) */ +#define MPSC_INTR_CAUSE 0x0004 +#define MPSC_INTR_MASK 0x0084 +#define MPSC_INTR_CAUSE_RCC (1<<6) + +/* Baud Rate Generator Interface Registers */ +#define BRG_BCR 0x0000 +#define BRG_BTR 0x0004 + +/* Speed of the UART. */ +static int kgdbmpsc_baud = CONFIG_KGDB_BAUDRATE; + +/* Index of the UART, matches ttyMX naming. */ +static int kgdbmpsc_ttyMM = CONFIG_KGDB_PORT_NUM; + +#define MPSC_INTR_REG_SELECT(x) ((x) + (8 * kgdbmpsc_ttyMM)) + +static int kgdbmpsc_init(void); + +static struct platform_device mpsc_dev, shared_dev; + +static void __iomem *mpsc_base; +static void __iomem *brg_base; +static void __iomem *routing_base; +static void __iomem *sdma_base; + +static unsigned int mpsc_irq; + +static void kgdb_write_debug_char(u8 c) +{ + u32 data; + + data = readl(mpsc_base + MPSC_MPCR); + writeb(c, mpsc_base + MPSC_CHR_1); + mb(); /* sync serial */ + data = readl(mpsc_base + MPSC_CHR_2); + data |= MPSC_CHR_2_TTCS; + writel(data, mpsc_base + MPSC_CHR_2); + mb(); /* sync serial */ + + while (readl(mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS) ; +} + +static int kgdb_get_debug_char(void) +{ + unsigned char c; + + while (!(readl(sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE)) & + MPSC_INTR_CAUSE_RCC)) ; + + c = readb(mpsc_base + MPSC_CHR_10 + (1 << 1)); + mb(); /* sync serial */ + writeb(c, mpsc_base + MPSC_CHR_10 + (1 << 1)); + mb(); /* sync serial */ + writel(~MPSC_INTR_CAUSE_RCC, sdma_base + + MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE)); + return (c); +} + +/* + * This is the receiver interrupt routine for the GDB stub. + * All that we need to do is verify that the interrupt happened on the + * line we're in charge of. If this is true, schedule a breakpoint and + * return. + */ +static irqreturn_t kgdbmpsc_interrupt(int irq, void *dev_id) +{ + if (irq != mpsc_irq) + return IRQ_NONE; + /* + * If there is some other CPU in KGDB then this is a + * spurious interrupt. so return without even checking a byte + */ + if (atomic_read(&debugger_active)) + return IRQ_NONE; + + if (readl(sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE)) & + MPSC_INTR_CAUSE_RCC) + breakpoint(); + + return IRQ_HANDLED; +} + +static int __init kgdbmpsc_init(void) +{ + struct mpsc_pdata *pdata; + u32 cdv; + + if (!brg_base || !mpsc_base || !routing_base || !sdma_base) + return -1; + + /* Set MPSC Routing to enable both ports */ + writel(0x0, routing_base + MPSC_MRR); + + /* MPSC 0/1 Rx & Tx get clocks BRG0/1 */ + writel(0x00000100, routing_base + MPSC_RCRR); + writel(0x00000100, routing_base + MPSC_TCRR); + + /* Disable all MPSC interrupts and clear any pending interrupts */ + writel(0, sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_MASK)); + writel(0, sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE)); + + pdata = (struct mpsc_pdata *)mpsc_dev.dev.platform_data; + + /* cdv = (clock/(2*16*baud rate)) for 16X mode. */ + cdv = ((pdata->brg_clk_freq / (32 * kgdbmpsc_baud)) - 1); + writel((pdata->brg_clk_src << 18) | (1 << 16) | cdv, + brg_base + BRG_BCR); + + /* Put MPSC into UART mode, no null modem, 16x clock mode */ + writel(0x000004c4, mpsc_base + MPSC_MMCRL); + writel(0x04400400, mpsc_base + MPSC_MMCRH); + + writel(0, mpsc_base + MPSC_CHR_1); + writel(0, mpsc_base + MPSC_CHR_9); + writel(0, mpsc_base + MPSC_CHR_10); + writel(4, mpsc_base + MPSC_CHR_3); + writel(0x20000000, mpsc_base + MPSC_CHR_4); + writel(0x9000, mpsc_base + MPSC_CHR_5); + writel(0, mpsc_base + MPSC_CHR_6); + writel(0, mpsc_base + MPSC_CHR_7); + writel(0, mpsc_base + MPSC_CHR_8); + + /* 8 data bits, 1 stop bit */ + writel((3 << 12), mpsc_base + MPSC_MPCR); + + /* Enter "hunt" mode */ + writel((1 << 31), mpsc_base + MPSC_CHR_2); + + udelay(100); + return 0; +} + +static void __iomem *__init +kgdbmpsc_map_resource(struct platform_device *pd, int type, int num) +{ + void __iomem *base = NULL; + struct resource *r; + + r = platform_get_resource(pd, IORESOURCE_MEM, num); + if (r) + base = ioremap(r->start, r->end - r->start + 1); + return base; +} + +static void __iomem *__init +kgdbmpsc_unmap_resource(struct platform_device *pd, int type, int num, + void __iomem *base) +{ + if (base) + iounmap(base); + return NULL; +} + +static void __init +kgdbmpsc_reserve_resource(struct platform_device *pd, int type, int num) +{ + struct resource *r; + + r = platform_get_resource(pd, IORESOURCE_MEM, num); + if (r) + request_mem_region(r->start, r->end - r->start + 1, "kgdb"); +} + +static int __init kgdbmpsc_local_init(void) +{ + if (!mpsc_dev.num_resources || !shared_dev.num_resources) + return 1; /* failure */ + + mpsc_base = kgdbmpsc_map_resource(&mpsc_dev, IORESOURCE_MEM, + MPSC_BASE_ORDER); + brg_base = kgdbmpsc_map_resource(&mpsc_dev, IORESOURCE_MEM, + MPSC_BRG_BASE_ORDER); + + /* get the platform data for the shared registers and get them mapped */ + routing_base = kgdbmpsc_map_resource(&shared_dev, + IORESOURCE_MEM, + MPSC_ROUTING_BASE_ORDER); + sdma_base = + kgdbmpsc_map_resource(&shared_dev, IORESOURCE_MEM, + MPSC_SDMA_INTR_BASE_ORDER); + + mpsc_irq = platform_get_irq(&mpsc_dev, 1); + + if (mpsc_base && brg_base && routing_base && sdma_base) + return 0; /* success */ + + return 1; /* failure */ +} + +static void __init kgdbmpsc_local_exit(void) +{ + if (sdma_base) + sdma_base = kgdbmpsc_unmap_resource(&shared_dev, IORESOURCE_MEM, + MPSC_SDMA_INTR_BASE_ORDER, + sdma_base); + if (routing_base) + routing_base = kgdbmpsc_unmap_resource(&shared_dev, + IORESOURCE_MEM, + MPSC_ROUTING_BASE_ORDER, + routing_base); + if (brg_base) + brg_base = kgdbmpsc_unmap_resource(&mpsc_dev, IORESOURCE_MEM, + MPSC_BRG_BASE_ORDER, + brg_base); + if (mpsc_base) + mpsc_base = kgdbmpsc_unmap_resource(&mpsc_dev, IORESOURCE_MEM, + MPSC_BASE_ORDER, mpsc_base); +} + +static void __init kgdbmpsc_update_pdata(struct platform_device *pdev) +{ + + snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id); +} + +static int __init kgdbmpsc_pdev_init(void) +{ + struct platform_device *pdev; + + /* get the platform data for the specified port. */ + pdev = mv64x60_early_get_pdev_data(MPSC_CTLR_NAME, kgdbmpsc_ttyMM, 1); + if (pdev) { + memcpy(&mpsc_dev, pdev, sizeof(struct platform_device)); + if (platform_notify) { + kgdbmpsc_update_pdata(&mpsc_dev); + platform_notify(&mpsc_dev.dev); + } + + /* get the platform data for the shared registers. */ + pdev = mv64x60_early_get_pdev_data(MPSC_SHARED_NAME, 0, 0); + if (pdev) { + memcpy(&shared_dev, pdev, + sizeof(struct platform_device)); + if (platform_notify) { + kgdbmpsc_update_pdata(&shared_dev); + platform_notify(&shared_dev.dev); + } + } + } + return 0; +} + +postcore_initcall(kgdbmpsc_pdev_init); + +static int __init kgdbmpsc_init_io(void) +{ + + kgdbmpsc_pdev_init(); + + if (kgdbmpsc_local_init()) { + kgdbmpsc_local_exit(); + return -1; + } + + if (kgdbmpsc_init() == -1) + return -1; + return 0; +} + +static void __init kgdbmpsc_hookup_irq(void) +{ + unsigned int msk; + if (!request_irq(mpsc_irq, kgdbmpsc_interrupt, 0, "kgdb mpsc", NULL)) { + /* Enable interrupt */ + msk = readl(sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_MASK)); + msk |= MPSC_INTR_CAUSE_RCC; + writel(msk, sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_MASK)); + + kgdbmpsc_reserve_resource(&mpsc_dev, IORESOURCE_MEM, + MPSC_BASE_ORDER); + kgdbmpsc_reserve_resource(&mpsc_dev, IORESOURCE_MEM, + MPSC_BRG_BASE_ORDER); + } +} + +struct kgdb_io kgdb_io_ops = { + .read_char = kgdb_get_debug_char, + .write_char = kgdb_write_debug_char, + .init = kgdbmpsc_init_io, + .late_init = kgdbmpsc_hookup_irq, +}; diff -puN /dev/null drivers/serial/pl011_kgdb.c --- /dev/null +++ a/drivers/serial/pl011_kgdb.c @@ -0,0 +1,113 @@ +/* + * driver/serial/pl011_kgdb.c + * + * Support for KGDB on ARM AMBA PL011 UARTs + * + * Authors: Manish Lachwani + * Deepak Saxena + * + * Copyright (c) 2005-2007 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 expressor implied. + * + */ +#include +#include +#include +#include + +#include +#include + +static int kgdb_irq = CONFIG_KGDB_AMBA_IRQ; + +#define UART_DIVISOR (CONFIG_KGDB_AMBA_UARTCLK * 4 / CONFIG_KGDB_BAUDRATE) +/* + * Todo: IO_ADDRESS is not very generic across ARM... + */ +static volatile unsigned char *kgdb_port = + (unsigned char *)IO_ADDRESS(CONFIG_KGDB_AMBA_BASE); + +/* + * Init code taken from amba-pl011.c. + */ +static int kgdb_serial_init(void) +{ + writew(0, kgdb_port + UART010_CR); + + /* Set baud rate */ + writew(UART_DIVISOR & 0x3f, kgdb_port + UART011_FBRD); + writew(UART_DIVISOR >> 6, kgdb_port + UART011_IBRD); + + writew(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, kgdb_port + + UART010_LCRH); + writew(UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE, + kgdb_port + UART010_CR); + + writew(UART011_RXIM, kgdb_port + UART011_IMSC); + + return 0; +} + +static void kgdb_serial_putchar(u8 ch) +{ + unsigned int status; + + do { + status = readw(kgdb_port + UART01x_FR); + } while (status & UART01x_FR_TXFF); + + writew(ch, kgdb_port + UART01x_DR); +} + +static int kgdb_serial_getchar(void) +{ + unsigned int status; + int ch; + +#ifdef CONFIG_DEBUG_LL + printascii("Entering serial_getchar loop"); +#endif + do { + status = readw(kgdb_port + UART01x_FR); + } while (status & UART01x_FR_RXFE); + ch = readw(kgdb_port + UART01x_DR); +#ifdef CONFIG_DEBUG_LL + printascii("Exited serial_getchar loop"); + printascii("Read char: "); + printch(ch); + printascii("\n"); +#endif + return ch; +} + +static irqreturn_t kgdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int status = readw(kgdb_port + UART011_MIS); + +#ifdef CONFIG_DEBUG_LL + printascii("KGDB irq\n"); +#endif + 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, "KGDB-serial", + kgdb_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 -puN drivers/serial/pxa.c~git-kgdb drivers/serial/pxa.c --- a/drivers/serial/pxa.c~git-kgdb +++ a/drivers/serial/pxa.c @@ -42,6 +42,9 @@ #include #include #include +#ifdef CONFIG_KGDB_CONSOLE +#include +#endif #include #include @@ -690,6 +693,8 @@ serial_pxa_console_init(void) console_initcall(serial_pxa_console_init); #define PXA_CONSOLE &serial_pxa_console +#elif defined(CONFIG_KGDB_CONSOLE) +#define PXA_CONSOLE &kgdbcons #else #define PXA_CONSOLE NULL #endif diff -puN drivers/serial/serial_core.c~git-kgdb drivers/serial/serial_core.c --- a/drivers/serial/serial_core.c~git-kgdb +++ a/drivers/serial/serial_core.c @@ -33,6 +33,7 @@ #include /* for serial_state and serial_icounter_struct */ #include #include +#include #include #include @@ -58,6 +59,12 @@ static struct lock_class_key port_lock_k #define uart_console(port) (0) #endif +#ifdef CONFIG_KGDB_CONSOLE +#define uart_kgdb(port) (port->cons && !strcmp(port->cons->name, "kgdb")) +#else +#define uart_kgdb(port) (0) +#endif + static void uart_change_speed(struct uart_state *state, struct ktermios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); static void uart_change_pm(struct uart_state *state, int pm_state); @@ -1675,6 +1682,9 @@ static int uart_line_info(char *buf, str mmio ? (unsigned long long)port->mapbase : (unsigned long long) port->iobase, port->irq); + if (port->iotype == UPIO_MEM) + ret += sprintf(buf+ret, " membase 0x%08lX", + (unsigned long) port->membase); if (port->type == PORT_UNKNOWN) { strcat(buf, "\n"); @@ -2073,7 +2083,8 @@ uart_report_port(struct uart_driver *drv case UPIO_TSI: case UPIO_DWAPB: snprintf(address, sizeof(address), - "MMIO 0x%llx", (unsigned long long)port->mapbase); + "MMIO 0x%llx mem 0x%p", (unsigned long long)port->mapbase, + port->membase); break; default: strlcpy(address, "*unknown*", sizeof(address)); @@ -2128,9 +2139,9 @@ uart_configure_port(struct uart_driver * /* * Power down all ports by default, except the - * console if we have one. + * console (real or kgdb) if we have one. */ - if (!uart_console(port)) + if (!uart_console(port) && !uart_kgdb(port)) uart_change_pm(state, 3); } } @@ -2321,6 +2332,12 @@ int uart_add_one_port(struct uart_driver */ port->flags &= ~UPF_DEAD; +#if defined(CONFIG_KGDB_8250) + /* Add any 8250-like ports we find later. */ + if (port->type <= PORT_MAX_8250) + kgdb8250_add_port(port->line, port); +#endif + out: mutex_unlock(&state->mutex); mutex_unlock(&port_mutex); diff -puN drivers/serial/serial_txx9.c~git-kgdb drivers/serial/serial_txx9.c --- a/drivers/serial/serial_txx9.c~git-kgdb +++ a/drivers/serial/serial_txx9.c @@ -36,6 +36,10 @@ static char *serial_version = "1.10"; static char *serial_name = "TX39/49 Serial driver"; +#ifndef CONFIG_KGDB_TXX9 +#define CONFIG_KGDB_PORT_NUM -1 +#endif + #define PASS_LIMIT 256 #if !defined(CONFIG_SERIAL_TXX9_STDSERIAL) @@ -469,6 +473,9 @@ static int serial_txx9_startup(struct ua unsigned long flags; int retval; + if (up->port.line == CONFIG_KGDB_PORT_NUM) + return -EBUSY; + /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) @@ -803,6 +810,9 @@ static void __init serial_txx9_register_ for (i = 0; i < UART_NR; i++) { struct uart_txx9_port *up = &serial_txx9_ports[i]; + if (up->port.line == CONFIG_KGDB_PORT_NUM) + continue; + up->port.line = i; up->port.ops = &serial_txx9_pops; up->port.dev = dev; @@ -971,6 +981,9 @@ static int __devinit serial_txx9_registe mutex_lock(&serial_txx9_mutex); for (i = 0; i < UART_NR; i++) { + if (i == CONFIG_KGDB_PORT_NUM) + continue; + uart = &serial_txx9_ports[i]; if (uart_match_port(&uart->port, port)) { uart_remove_one_port(&serial_txx9_reg, &uart->port); diff -puN /dev/null drivers/serial/serial_txx9_kgdb.c --- /dev/null +++ a/drivers/serial/serial_txx9_kgdb.c @@ -0,0 +1,152 @@ +/* + * drivers/serial/serial_txx9_kgdb.c + * + * kgdb interface for gdb + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * Copyright (C) 2005-2006 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +/* Speed of the UART. */ +static unsigned int kgdb_txx9_baud = CONFIG_KGDB_BAUDRATE; + +#define TXX9_NPORT 4 /* TX4939 has 4 UARTs, others only have 2 */ + +static struct uart_port kgdb_txx9_ports[TXX9_NPORT]; +static struct uart_port *kgdb_port; + +/* TXX9 Serial Registers */ +#define TXX9_SILCR 0x00 +#define TXX9_SIDISR 0x08 +#define TXX9_SISCISR 0x0c +#define TXX9_SIFCR 0x10 +#define TXX9_SIFLCR 0x14 +#define TXX9_SIBGR 0x18 +#define TXX9_SITFIFO 0x1c +#define TXX9_SIRFIFO 0x20 + +/* SILCR : Line Control */ +#define TXX9_SILCR_SCS_IMCLK_BG 0x00000020 +#define TXX9_SILCR_SCS_SCLK_BG 0x00000060 +#define TXX9_SILCR_USBL_1BIT 0x00000000 +#define TXX9_SILCR_UMODE_8BIT 0x00000000 + +/* SIDISR : DMA/Int. Status */ +#define TXX9_SIDISR_RFDN_MASK 0x0000001f + +/* SISCISR : Status Change Int. Status */ +#define TXX9_SISCISR_TRDY 0x00000004 + +/* SIFCR : FIFO Control */ +#define TXX9_SIFCR_SWRST 0x00008000 + +/* SIBGR : Baud Rate Control */ +#define TXX9_SIBGR_BCLK_T0 0x00000000 +#define TXX9_SIBGR_BCLK_T2 0x00000100 +#define TXX9_SIBGR_BCLK_T4 0x00000200 +#define TXX9_SIBGR_BCLK_T6 0x00000300 + +static inline unsigned int sio_in(struct uart_port *port, int offset) +{ + return *(volatile u32 *)(port->membase + offset); +} + +static inline void sio_out(struct uart_port *port, int offset, + unsigned int value) +{ + *(volatile u32 *)(port->membase + offset) = value; +} + +void __init txx9_kgdb_add_port(int n, struct uart_port *port) +{ + memcpy(&kgdb_txx9_ports[n], port, sizeof(struct uart_port)); +} + +static int txx9_kgdb_init(void) +{ + unsigned int quot, sibgr; + + kgdb_port = &kgdb_txx9_ports[CONFIG_KGDB_PORT_NUM]; + + if (kgdb_port->iotype != UPIO_MEM && + kgdb_port->iotype != UPIO_MEM32) + return -1; + + /* Reset the UART. */ + sio_out(kgdb_port, TXX9_SIFCR, TXX9_SIFCR_SWRST); +#ifdef CONFIG_CPU_TX49XX + /* + * TX4925 BUG WORKAROUND. Accessing SIOC register + * immediately after soft reset causes bus error. + */ + iob(); + udelay(1); +#endif + /* Wait until reset is complete. */ + while (sio_in(kgdb_port, TXX9_SIFCR) & TXX9_SIFCR_SWRST); + + /* Select the frame format and input clock. */ + sio_out(kgdb_port, TXX9_SILCR, + TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | + ((kgdb_port->flags & UPF_MAGIC_MULTIPLIER) ? + TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); + + /* Select the input clock prescaler that fits the baud rate. */ + quot = (kgdb_port->uartclk + 8 * kgdb_txx9_baud) / + (16 * kgdb_txx9_baud); + if (quot < (256 << 1)) + sibgr = (quot >> 1) | TXX9_SIBGR_BCLK_T0; + else if (quot < ( 256 << 3)) + sibgr = (quot >> 3) | TXX9_SIBGR_BCLK_T2; + else if (quot < ( 256 << 5)) + sibgr = (quot >> 5) | TXX9_SIBGR_BCLK_T4; + else if (quot < ( 256 << 7)) + sibgr = (quot >> 7) | TXX9_SIBGR_BCLK_T6; + else + sibgr = 0xff | TXX9_SIBGR_BCLK_T6; + + sio_out(kgdb_port, TXX9_SIBGR, sibgr); + + /* Enable receiver and transmitter. */ + sio_out(kgdb_port, TXX9_SIFLCR, 0); + + return 0; +} + +static void txx9_kgdb_late_init(void) +{ + request_mem_region(kgdb_port->mapbase, 0x40, "serial_txx9(debug)"); +} + +static int txx9_kgdb_read(void) +{ + while (!(sio_in(kgdb_port, TXX9_SIDISR) & TXX9_SIDISR_RFDN_MASK)); + + return sio_in(kgdb_port, TXX9_SIRFIFO); +} + +static void txx9_kgdb_write(u8 ch) +{ + while (!(sio_in(kgdb_port, TXX9_SISCISR) & TXX9_SISCISR_TRDY)); + + sio_out(kgdb_port, TXX9_SITFIFO, ch); +} + +struct kgdb_io kgdb_io_ops = { + .read_char = txx9_kgdb_read, + .write_char = txx9_kgdb_write, + .init = txx9_kgdb_init, + .late_init = txx9_kgdb_late_init +}; diff -puN drivers/serial/sh-sci.c~git-kgdb drivers/serial/sh-sci.c --- a/drivers/serial/sh-sci.c~git-kgdb +++ a/drivers/serial/sh-sci.c @@ -119,7 +119,8 @@ static int get_char(struct uart_port *po do { status = sci_in(port, SCxSR); if (status & SCxSR_ERRORS(port)) { - handle_error(port); + /* Clear error flags. */ + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); continue; } } while (!(status & SCxSR_RDxF(port))); @@ -185,18 +186,18 @@ static void put_string(struct sci_port * int h, l; c = *p++; - h = highhex(c); - l = lowhex(c); + h = hexchars[c >> 4]; + l = hexchars[c % 16]; put_char(port, h); put_char(port, l); checksum += h + l; } put_char(port, '#'); - put_char(port, highhex(checksum)); - put_char(port, lowhex(checksum)); + put_char(port, hexchars[checksum >> 4]); + put_char(port, hexchars[checksum & 16]); } while (get_char(port) != '+'); } else -#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ +#endif /* CONFIG_SH_STANDARD_BIOS */ for (i=0; iline == KGDBPORT.port.line && + c == 3) + breakpoint(); +#endif + /* Store data and status */ if (status&SCxSR_FER(port)) { flag = TTY_FRAME; diff -puN /dev/null include/asm-arm/kgdb.h --- /dev/null +++ a/include/asm-arm/kgdb.h @@ -0,0 +1,103 @@ +/* + * include/asm-arm/kgdb.h + * + * ARM KGDB support + * + * Author: Deepak Saxena + * + * Copyright (C) 2002 MontaVista Software Inc. + * + */ + +#ifndef __ASM_KGDB_H__ +#define __ASM_KGDB_H__ + +#include +#include + + +/* + * GDB assumes that we're a user process being debugged, so + * it will send us an SWI command to write into memory as the + * debug trap. When an SWI occurs, the next instruction addr is + * placed into R14_svc before jumping to the vector trap. + * This doesn't work for kernel debugging as we are already in SVC + * we would loose the kernel's LR, which is a bad thing. This + * is bad thing. + * + * By doing this as an undefined instruction trap, we force a mode + * switch from SVC to UND mode, allowing us to save full kernel state. + * + * We also define a KGDB_COMPILED_BREAK which can be used to compile + * in breakpoints. This is important for things like sysrq-G and for + * the initial breakpoint from trap_init(). + * + * Note to ARM HW designers: Add real trap support like SH && PPC to + * make our lives much much simpler. :) + */ +#define BREAK_INSTR_SIZE 4 +#define GDB_BREAKINST 0xef9f0001 +#define KGDB_BREAKINST 0xe7ffdefe +#define KGDB_COMPILED_BREAK 0xe7ffdeff +#define CACHE_FLUSH_IS_SAFE 1 + +#ifndef __ASSEMBLY__ + +#define BREAKPOINT() asm(".word 0xe7ffdeff") + + +extern void kgdb_handle_bus_error(void); +extern int kgdb_fault_expected; +#endif /* !__ASSEMBLY__ */ + +/* + * From Kevin Hilman: + * + * gdb is expecting the following registers layout. + * + * r0-r15: 1 long word each + * f0-f7: unused, 3 long words each !! + * fps: unused, 1 long word + * cpsr: 1 long word + * + * Even though f0-f7 and fps are not used, they need to be + * present in the registers sent for correct processing in + * the host-side gdb. + * + * In particular, it is crucial that CPSR is in the right place, + * otherwise gdb will not be able to correctly interpret stepping over + * conditional branches. + */ +#define _GP_REGS 16 +#define _FP_REGS 8 +#define _EXTRA_REGS 2 +#define GDB_MAX_REGS (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS) + +#define KGDB_MAX_NO_CPUS 1 +#define BUFMAX 400 +#define NUMREGBYTES (GDB_MAX_REGS << 2) +#define NUMCRITREGBYTES (32 << 2) + +#define _R0 0 +#define _R1 1 +#define _R2 2 +#define _R3 3 +#define _R4 4 +#define _R5 5 +#define _R6 6 +#define _R7 7 +#define _R8 8 +#define _R9 9 +#define _R10 10 +#define _FP 11 +#define _IP 12 +#define _SP 13 +#define _LR 14 +#define _PC 15 +#define _CPSR (GDB_MAX_REGS - 1) + +/* So that we can denote the end of a frame for tracing, in the simple + * case. */ +#define CFI_END_FRAME(func) __CFI_END_FRAME(_PC, _SP, func) + +#endif /* __ASM_KGDB_H__ */ diff -puN include/asm-arm/system.h~git-kgdb include/asm-arm/system.h --- a/include/asm-arm/system.h~git-kgdb +++ a/include/asm-arm/system.h @@ -350,6 +350,42 @@ static inline unsigned long __xchg(unsig extern void disable_hlt(void); extern void enable_hlt(void); +#ifndef CONFIG_SMP +/* + * Atomic compare and exchange. + */ +#define __HAVE_ARCH_CMPXCHG 1 + +extern unsigned long wrong_size_cmpxchg(volatile void *ptr); + +static inline unsigned long __cmpxchg(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + unsigned long flags, prev; + volatile unsigned long *p = ptr; + + if (size == 4) { + local_irq_save(flags); + pref = *p; + if (prev == old) + *p = new; + local_irq_restore(flags); + return(prev); + } else + return wrong_size_cmpxchg(ptr); +} + +#define cmpxchg(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, sizeof(*(ptr))); \ +}) + +#endif + #endif /* __ASSEMBLY__ */ #define arch_align_stack(x) (x) diff -puN /dev/null include/asm-generic/kgdb.h --- /dev/null +++ a/include/asm-generic/kgdb.h @@ -0,0 +1,103 @@ +/* + * include/asm-generic/kgdb.h + * + * This provides the assembly level information so that KGDB can provide + * a GDB that has been patched with enough information to know to stop + * trying to unwind the function. + * + * Author: Tom Rini + * + * 2005 (c) MontaVista Software, Inc. + * 2006 (c) Embedded Alley Solutions, 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. + */ + +#ifndef __ASM_GENERIC_KGDB_H__ +#define __ASM_GENERIC_KGDB_H__ + +#ifdef CONFIG_X86 +/** + * kgdb_skipexception - Bail of of KGDB when we've been triggered. + * @exception: Exception vector number + * @regs: Current &struct pt_regs. + * + * On some architectures we need to skip a breakpoint exception when + * it occurs after a breakpoint has been removed. + */ +int kgdb_skipexception(int exception, struct pt_regs *regs); +#else +static inline int kgdb_skipexception(int exception, struct pt_regs *regs) +{ + return 0; +} +#endif + +#if defined(CONFIG_X86) +/** + * kgdb_post_master_code - Save error vector/code numbers. + * @regs: Original pt_regs. + * @e_vector: Original error vector. + * @err_code: Original error code. + * + * This is needed on architectures which support SMP and KGDB. + * This function is called after all the slave cpus have been put + * to a know spin state and the master CPU has control over KGDB. + */ +extern void kgdb_post_master_code(struct pt_regs *regs, int e_vector, + int err_code); + +/** + * kgdb_disable_hw_debug - Disable hardware debugging while we in kgdb. + * @regs: Current &struct pt_regs. + * + * This function will be called if the particular architecture must + * disable hardware debugging while it is processing gdb packets or + * handling exception. + */ +extern void kgdb_disable_hw_debug(struct pt_regs *regs); +#else +#define kgdb_disable_hw_debug(regs) do { } while (0) +#define kgdb_post_master_code(regs, v, c) do { } while (0) +#endif + +#ifdef CONFIG_KGDB_ARCH_HAS_SHADOW_INFO +/** + * kgdb_shadowinfo - Get shadowed information on @threadid. + * @regs: The &struct pt_regs of the current process. + * @buffer: A buffer of %BUFMAX size. + * @threadid: The thread id of the shadowed process to get information on. + */ +extern void kgdb_shadowinfo(struct pt_regs *regs, char *buffer, + unsigned threadid); + +/** + * kgdb_get_shadow_thread - Get the shadowed &task_struct of @threadid. + * @regs: The &struct pt_regs of the current thread. + * @threadid: The thread id of the shadowed process to get information on. + * + * RETURN: + * This returns a pointer to the &struct task_struct of the shadowed + * thread, @threadid. + */ +extern struct task_struct *kgdb_get_shadow_thread(struct pt_regs *regs, + int threadid); + +/** + * kgdb_shadow_regs - Return the shadowed registers of @threadid. + * @regs: The &struct pt_regs of the current thread. + * @threadid: The thread id we want the &struct pt_regs for. + * + * RETURN: + * The a pointer to the &struct pt_regs of the shadowed thread @threadid. + */ +extern struct pt_regs *kgdb_shadow_regs(struct pt_regs *regs, int threadid); +#else +#define kgdb_shadowinfo(regs, buf, threadid) do { } while (0) +#define kgdb_get_shadow_thread(regs, threadid) NULL +#define kgdb_shadow_regs(regs, threadid) NULL +#endif + +#endif /* __ASM_GENERIC_KGDB_H__ */ diff -puN include/asm-i386/kdebug.h~git-kgdb include/asm-i386/kdebug.h --- a/include/asm-i386/kdebug.h~git-kgdb +++ a/include/asm-i386/kdebug.h @@ -28,6 +28,7 @@ enum die_val { DIE_CALL, DIE_NMI_IPI, DIE_PAGE_FAULT, + DIE_PAGE_FAULT_NO_CONTEXT, }; #endif diff -puN /dev/null include/asm-i386/kgdb.h --- /dev/null +++ a/include/asm-i386/kgdb.h @@ -0,0 +1,51 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +#include + +/* + * Copyright (C) 2001-2004 Amit S. Kale + */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 1024 + +/* Number of bytes of registers. */ +#define NUMREGBYTES 64 +/* Number of bytes of registers we need to save for a setjmp/longjmp. */ +#define NUMCRITREGBYTES 24 + +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + */ +enum regnames { _EAX, /* 0 */ + _ECX, /* 1 */ + _EDX, /* 2 */ + _EBX, /* 3 */ + _ESP, /* 4 */ + _EBP, /* 5 */ + _ESI, /* 6 */ + _EDI, /* 7 */ + _PC, /* 8 also known as eip */ + _PS, /* 9 also known as eflags */ + _CS, /* 10 */ + _SS, /* 11 */ + _DS, /* 12 */ + _ES, /* 13 */ + _FS, /* 14 */ + _GS /* 15 */ +}; + +#define BREAKPOINT() asm(" int $3"); +#define BREAK_INSTR_SIZE 1 +#define CACHE_FLUSH_IS_SAFE 1 +#endif /* _ASM_KGDB_H_ */ +#endif /* __KERNEL__ */ diff -puN include/asm-ia64/kdebug.h~git-kgdb include/asm-ia64/kdebug.h --- a/include/asm-ia64/kdebug.h~git-kgdb +++ a/include/asm-ia64/kdebug.h @@ -69,6 +69,7 @@ enum die_val { DIE_KDEBUG_LEAVE, DIE_KDUMP_ENTER, DIE_KDUMP_LEAVE, + DIE_PAGE_FAULT_NO_CONTEXT, }; #endif diff -puN /dev/null include/asm-ia64/kgdb.h --- /dev/null +++ a/include/asm-ia64/kgdb.h @@ -0,0 +1,37 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +/* + * Copyright (C) 2001-2004 Amit S. Kale + */ + +#include +#include + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 1024 + +/* Number of bytes of registers. We set this to 0 so that certain GDB + * packets will fail, forcing the use of others, which are more friendly + * on ia64. */ +#define NUMREGBYTES 0 + +#define NUMCRITREGBYTES (70*8) +#define JMP_REGS_ALIGNMENT __attribute__ ((aligned (16))) + +#define BREAKNUM 0x00003333300LL +#define KGDBBREAKNUM 0x6665UL +#define BREAKPOINT() asm volatile ("break.m 0x6665") +#define BREAK_INSTR_SIZE 16 +#define CACHE_FLUSH_IS_SAFE 1 + +struct pt_regs; +extern volatile int kgdb_hwbreak_sstep[NR_CPUS]; +extern void smp_send_nmi_allbutself(void); +extern void kgdb_wait_ipi(struct pt_regs *); +#endif /* _ASM_KGDB_H_ */ +#endif /* __KERNEL__ */ diff -puN include/asm-mips/asmmacro-32.h~git-kgdb include/asm-mips/asmmacro-32.h --- a/include/asm-mips/asmmacro-32.h~git-kgdb +++ a/include/asm-mips/asmmacro-32.h @@ -11,6 +11,28 @@ #include #include #include +#include + + .macro fpu_save_double_kgdb stack status tmp1 = t0 + cfc1 \tmp1, fcr31 + sdc1 $f0, GDB_FR_FPR0(\stack) + sdc1 $f2, GDB_FR_FPR2(\stack) + sdc1 $f4, GDB_FR_FPR4(\stack) + sdc1 $f6, GDB_FR_FPR6(\stack) + sdc1 $f8, GDB_FR_FPR8(\stack) + sdc1 $f10, GDB_FR_FPR10(\stack) + sdc1 $f12, GDB_FR_FPR12(\stack) + sdc1 $f14, GDB_FR_FPR14(\stack) + sdc1 $f16, GDB_FR_FPR16(\stack) + sdc1 $f18, GDB_FR_FPR18(\stack) + sdc1 $f20, GDB_FR_FPR20(\stack) + sdc1 $f22, GDB_FR_FPR22(\stack) + sdc1 $f24, GDB_FR_FPR24(\stack) + sdc1 $f26, GDB_FR_FPR26(\stack) + sdc1 $f28, GDB_FR_FPR28(\stack) + sdc1 $f30, GDB_FR_FPR30(\stack) + sw \tmp1, GDB_FR_FSR(\stack) + .endm .macro fpu_save_double thread status tmp1=t0 cfc1 \tmp1, fcr31 @@ -91,6 +113,27 @@ ctc1 \tmp, fcr31 .endm + .macro fpu_restore_double_kgdb stack status tmp = t0 + lw \tmp, GDB_FR_FSR(\stack) + ldc1 $f0, GDB_FR_FPR0(\stack) + ldc1 $f2, GDB_FR_FPR2(\stack) + ldc1 $f4, GDB_FR_FPR4(\stack) + ldc1 $f6, GDB_FR_FPR6(\stack) + ldc1 $f8, GDB_FR_FPR8(\stack) + ldc1 $f10, GDB_FR_FPR10(\stack) + ldc1 $f12, GDB_FR_FPR12(\stack) + ldc1 $f14, GDB_FR_FPR14(\stack) + ldc1 $f16, GDB_FR_FPR16(\stack) + ldc1 $f18, GDB_FR_FPR18(\stack) + ldc1 $f20, GDB_FR_FPR20(\stack) + ldc1 $f22, GDB_FR_FPR22(\stack) + ldc1 $f24, GDB_FR_FPR24(\stack) + ldc1 $f26, GDB_FR_FPR26(\stack) + ldc1 $f28, GDB_FR_FPR28(\stack) + ldc1 $f30, GDB_FR_FPR30(\stack) + ctc1 \tmp, fcr31 + .endm + .macro fpu_restore_single thread tmp=t0 lw \tmp, THREAD_FCR31(\thread) lwc1 $f0, THREAD_FPR0(\thread) diff -puN include/asm-mips/asmmacro-64.h~git-kgdb include/asm-mips/asmmacro-64.h --- a/include/asm-mips/asmmacro-64.h~git-kgdb +++ a/include/asm-mips/asmmacro-64.h @@ -12,6 +12,7 @@ #include #include #include +#include .macro fpu_save_16even thread tmp=t0 cfc1 \tmp, fcr31 @@ -53,6 +54,46 @@ sdc1 $f31, THREAD_FPR31(\thread) .endm + .macro fpu_save_16odd_kgdb stack + sdc1 $f1, GDB_FR_FPR1(\stack) + sdc1 $f3, GDB_FR_FPR3(\stack) + sdc1 $f5, GDB_FR_FPR5(\stack) + sdc1 $f7, GDB_FR_FPR7(\stack) + sdc1 $f9, GDB_FR_FPR9(\stack) + sdc1 $f11, GDB_FR_FPR11(\stack) + sdc1 $f13, GDB_FR_FPR13(\stack) + sdc1 $f15, GDB_FR_FPR15(\stack) + sdc1 $f17, GDB_FR_FPR17(\stack) + sdc1 $f19, GDB_FR_FPR19(\stack) + sdc1 $f21, GDB_FR_FPR21(\stack) + sdc1 $f23, GDB_FR_FPR23(\stack) + sdc1 $f25, GDB_FR_FPR25(\stack) + sdc1 $f27, GDB_FR_FPR27(\stack) + sdc1 $f29, GDB_FR_FPR29(\stack) + sdc1 $f31, GDB_FR_FPR31(\stack) + .endm + + .macro fpu_save_16even_kgdb stack tmp = t0 + cfc1 \tmp, fcr31 + sdc1 $f0, GDB_FR_FPR0(\stack) + sdc1 $f2, GDB_FR_FPR2(\stack) + sdc1 $f4, GDB_FR_FPR4(\stack) + sdc1 $f6, GDB_FR_FPR6(\stack) + sdc1 $f8, GDB_FR_FPR8(\stack) + sdc1 $f10, GDB_FR_FPR10(\stack) + sdc1 $f12, GDB_FR_FPR12(\stack) + sdc1 $f14, GDB_FR_FPR14(\stack) + sdc1 $f16, GDB_FR_FPR16(\stack) + sdc1 $f18, GDB_FR_FPR18(\stack) + sdc1 $f20, GDB_FR_FPR20(\stack) + sdc1 $f22, GDB_FR_FPR22(\stack) + sdc1 $f24, GDB_FR_FPR24(\stack) + sdc1 $f26, GDB_FR_FPR26(\stack) + sdc1 $f28, GDB_FR_FPR28(\stack) + sdc1 $f30, GDB_FR_FPR30(\stack) + sw \tmp, GDB_FR_FSR(\stack) + .endm + .macro fpu_save_double thread status tmp sll \tmp, \status, 5 bgez \tmp, 2f @@ -61,6 +102,15 @@ fpu_save_16even \thread \tmp .endm + .macro fpu_save_double_kgdb stack status tmp + sll \tmp, \status, 5 + bgez \tmp, 2f + nop + fpu_save_16odd_kgdb \stack +2: + fpu_save_16even_kgdb \stack \tmp + .endm + .macro fpu_restore_16even thread tmp=t0 lw \tmp, THREAD_FCR31(\thread) ldc1 $f0, THREAD_FPR0(\thread) @@ -101,6 +151,46 @@ ldc1 $f31, THREAD_FPR31(\thread) .endm + .macro fpu_restore_16even_kgdb stack tmp = t0 + lw \tmp, GDB_FR_FSR(\stack) + ldc1 $f0, GDB_FR_FPR0(\stack) + ldc1 $f2, GDB_FR_FPR2(\stack) + ldc1 $f4, GDB_FR_FPR4(\stack) + ldc1 $f6, GDB_FR_FPR6(\stack) + ldc1 $f8, GDB_FR_FPR8(\stack) + ldc1 $f10, GDB_FR_FPR10(\stack) + ldc1 $f12, GDB_FR_FPR12(\stack) + ldc1 $f14, GDB_FR_FPR14(\stack) + ldc1 $f16, GDB_FR_FPR16(\stack) + ldc1 $f18, GDB_FR_FPR18(\stack) + ldc1 $f20, GDB_FR_FPR20(\stack) + ldc1 $f22, GDB_FR_FPR22(\stack) + ldc1 $f24, GDB_FR_FPR24(\stack) + ldc1 $f26, GDB_FR_FPR26(\stack) + ldc1 $f28, GDB_FR_FPR28(\stack) + ldc1 $f30, GDB_FR_FPR30(\stack) + ctc1 \tmp, fcr31 + .endm + + .macro fpu_restore_16odd_kgdb stack + ldc1 $f1, GDB_FR_FPR1(\stack) + ldc1 $f3, GDB_FR_FPR3(\stack) + ldc1 $f5, GDB_FR_FPR5(\stack) + ldc1 $f7, GDB_FR_FPR7(\stack) + ldc1 $f9, GDB_FR_FPR9(\stack) + ldc1 $f11, GDB_FR_FPR11(\stack) + ldc1 $f13, GDB_FR_FPR13(\stack) + ldc1 $f15, GDB_FR_FPR15(\stack) + ldc1 $f17, GDB_FR_FPR17(\stack) + ldc1 $f19, GDB_FR_FPR19(\stack) + ldc1 $f21, GDB_FR_FPR21(\stack) + ldc1 $f23, GDB_FR_FPR23(\stack) + ldc1 $f25, GDB_FR_FPR25(\stack) + ldc1 $f27, GDB_FR_FPR27(\stack) + ldc1 $f29, GDB_FR_FPR29(\stack) + ldc1 $f31, GDB_FR_FPR31(\stack) + .endm + .macro fpu_restore_double thread status tmp sll \tmp, \status, 5 bgez \tmp, 1f # 16 register mode? @@ -109,6 +199,15 @@ 1: fpu_restore_16even \thread \tmp .endm + .macro fpu_restore_double_kgdb stack status tmp + sll \tmp, \status, 5 + bgez \tmp, 1f # 16 register mode? + nop + + fpu_restore_16odd_kgdb \stack +1: fpu_restore_16even_kgdb \stack \tmp + .endm + .macro cpu_save_nonscratch thread LONG_S s0, THREAD_REG16(\thread) LONG_S s1, THREAD_REG17(\thread) diff -puN include/asm-mips/kdebug.h~git-kgdb include/asm-mips/kdebug.h --- a/include/asm-mips/kdebug.h~git-kgdb +++ a/include/asm-mips/kdebug.h @@ -1 +1,30 @@ -#include +/* + * + * Copyright (C) 2004 MontaVista Software Inc. + * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com + * + * 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. + * + */ +#ifndef _MIPS_KDEBUG_H +#define _MIPS_KDEBUG_H + +#include + +struct pt_regs; + +extern struct atomic_notifier_head mips_die_head; + +enum die_val { + DIE_OOPS = 1, + DIE_PANIC, + DIE_DIE, + DIE_KERNELDEBUG, + DIE_TRAP, + DIE_PAGE_FAULT, +}; + +#endif /* _MIPS_KDEBUG_H */ diff -puN /dev/null include/asm-mips/kgdb.h --- /dev/null +++ a/include/asm-mips/kgdb.h @@ -0,0 +1,48 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +#include +#include + +#ifndef __ASSEMBLY__ +#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \ + (_MIPS_ISA == _MIPS_ISA_MIPS32) + +#define KGDB_GDB_REG_SIZE 32 + +#elif (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \ + (_MIPS_ISA == _MIPS_ISA_MIPS64) + +#ifdef CONFIG_32BIT +#define KGDB_GDB_REG_SIZE 32 +#else /* CONFIG_CPU_32BIT */ +#define KGDB_GDB_REG_SIZE 64 +#endif +#else +#error "Need to set KGDB_GDB_REG_SIZE for MIPS ISA" +#endif /* _MIPS_ISA */ + +#define BUFMAX 2048 +#if (KGDB_GDB_REG_SIZE == 32) +#define NUMREGBYTES (90*sizeof(u32)) +#define NUMCRITREGBYTES (12*sizeof(u32)) +#else +#define NUMREGBYTES (90*sizeof(u64)) +#define NUMCRITREGBYTES (12*sizeof(u64)) +#endif +#define BREAK_INSTR_SIZE 4 +#define BREAKPOINT() __asm__ __volatile__( \ + ".globl breakinst\n\t" \ + ".set\tnoreorder\n\t" \ + "nop\n" \ + "breakinst:\tbreak\n\t" \ + "nop\n\t" \ + ".set\treorder") +#define CACHE_FLUSH_IS_SAFE 0 + +extern int kgdb_early_setup; + +#endif /* !__ASSEMBLY__ */ +#endif /* _ASM_KGDB_H_ */ +#endif /* __KERNEL__ */ diff -puN include/asm-mips/ptrace.h~git-kgdb include/asm-mips/ptrace.h --- a/include/asm-mips/ptrace.h~git-kgdb +++ a/include/asm-mips/ptrace.h @@ -28,7 +28,7 @@ * system call/exception. As usual the registers k0/k1 aren't being saved. */ struct pt_regs { -#ifdef CONFIG_32BIT +#if defined(CONFIG_32BIT) || defined(CONFIG_KGDB) /* Pad bytes for argument save space on the stack. */ unsigned long pad0[6]; #endif diff -puN /dev/null include/asm-powerpc/kgdb.h --- /dev/null +++ a/include/asm-powerpc/kgdb.h @@ -0,0 +1,64 @@ +/* + * include/asm-powerpc/kgdb.h + * + * The PowerPC (32/64) specific defines / externs for KGDB. Based on + * the previous 32bit and 64bit specific files, which had the following + * copyrights: + * + * PPC64 Mods (C) 2005 Frank Rowand (frowand@mvista.com) + * PPC Mods (C) 2004 Tom Rini (trini@mvista.com) + * PPC Mods (C) 2003 John Whitney (john.whitney@timesys.com) + * PPC Mods (C) 1998 Michael Tesch (tesch@cs.wisc.edu) + * + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Author: Tom Rini + * + * 2006 (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. + */ +#ifdef __KERNEL__ +#ifndef __POWERPC_KGDB_H__ +#define __POWERPC_KGDB_H__ + +#include + +#ifndef __ASSEMBLY__ + +#define BREAK_INSTR_SIZE 4 +#define BUFMAX ((NUMREGBYTES * 2) + 512) +#define OUTBUFMAX ((NUMREGBYTES * 2) + 512) +#define BREAKPOINT() asm(".long 0x7d821008"); /* twge r2, r2 */ +#define CACHE_FLUSH_IS_SAFE 1 + +/* The number bytes of registers we have to save depends on a few + * things. For 64bit we default to not including vector registers and + * vector state registers. */ +#ifdef CONFIG_PPC64 +/* + * 64 bit (8 byte) registers: + * 32 gpr, 32 fpr, nip, msr, link, ctr + * 32 bit (4 byte) registers: + * ccr, xer, fpscr + */ +#define NUMREGBYTES ((68 * 8) + (3 * 4)) +#define NUMCRITREGBYTES 184 +#else /* CONFIG_PPC32 */ +/* On non-E500 family PPC32 we determine the size by picking the last + * register we need, but on E500 we skip sections so we list what we + * need to store, and add it up. */ +#ifndef CONFIG_E500 +#define MAXREG (PT_FPSCR+1) +#else +/* 32 GPRs (8 bytes), nip, msr, ccr, link, ctr, xer, acc (8 bytes), spefscr*/ +#define MAXREG ((32*2)+6+2+1) +#endif +#define NUMREGBYTES (MAXREG * sizeof(int)) +/* CR/LR, R1, R2, R13-R31 inclusive. */ +#define NUMCRITREGBYTES (23 * sizeof(int)) +#endif /* 32/64 */ +#endif /* !(__ASSEMBLY__) */ +#endif /* !__POWERPC_KGDB_H__ */ +#endif /* __KERNEL__ */ diff -puN include/asm-ppc/kgdb.h~git-kgdb include/asm-ppc/kgdb.h --- a/include/asm-ppc/kgdb.h~git-kgdb +++ a/include/asm-ppc/kgdb.h @@ -1,57 +1,18 @@ -/* - * kgdb.h: Defines and declarations for serial line source level - * remote debugging of the Linux kernel using gdb. - * - * PPC Mods (C) 1998 Michael Tesch (tesch@cs.wisc.edu) - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ #ifdef __KERNEL__ -#ifndef _PPC_KGDB_H -#define _PPC_KGDB_H - +#ifndef __PPC_KGDB_H__ +#define __PPC_KGDB_H__ +#include #ifndef __ASSEMBLY__ - -/* Things specific to the gen550 backend. */ -struct uart_port; - -extern void gen550_progress(char *, unsigned short); -extern void gen550_kgdb_map_scc(void); -extern void gen550_init(int, struct uart_port *); - -/* Things specific to the pmac backend. */ -extern void zs_kgdb_hook(int tty_num); - -/* To init the kgdb engine. (called by serial hook)*/ -extern void set_debug_traps(void); - -/* To enter the debugger explicitly. */ -extern void breakpoint(void); - -/* For taking exceptions - * these are defined in traps.c - */ -extern int (*debugger)(struct pt_regs *regs); + /* For taking exceptions + * these are defined in traps.c + */ +struct pt_regs; +extern void (*debugger)(struct pt_regs *regs); extern int (*debugger_bpt)(struct pt_regs *regs); extern int (*debugger_sstep)(struct pt_regs *regs); extern int (*debugger_iabr_match)(struct pt_regs *regs); extern int (*debugger_dabr_match)(struct pt_regs *regs); extern void (*debugger_fault_handler)(struct pt_regs *regs); - -/* What we bring to the party */ -int kgdb_bpt(struct pt_regs *regs); -int kgdb_sstep(struct pt_regs *regs); -void kgdb(struct pt_regs *regs); -int kgdb_iabr_match(struct pt_regs *regs); -int kgdb_dabr_match(struct pt_regs *regs); - -/* - * external low-level support routines (ie macserial.c) - */ -extern void kgdb_interruptible(int); /* control interrupts from serial */ -extern void putDebugChar(char); /* write a single character */ -extern char getDebugChar(void); /* read and return a single char */ - -#endif /* !(__ASSEMBLY__) */ -#endif /* !(_PPC_KGDB_H) */ +#endif /* !__ASSEMBLY__ */ +#endif /* __PPC_KGDB_H__ */ #endif /* __KERNEL__ */ diff -puN include/asm-ppc/machdep.h~git-kgdb include/asm-ppc/machdep.h --- a/include/asm-ppc/machdep.h~git-kgdb +++ a/include/asm-ppc/machdep.h @@ -72,9 +72,7 @@ struct machdep_calls { unsigned long (*find_end_of_memory)(void); void (*setup_io_mappings)(void); - void (*early_serial_map)(void); void (*progress)(char *, unsigned short); - void (*kgdb_map_scc)(void); unsigned char (*nvram_read_val)(int addr); void (*nvram_write_val)(int addr, unsigned char val); diff -puN include/asm-ppc/mv64x60.h~git-kgdb include/asm-ppc/mv64x60.h --- a/include/asm-ppc/mv64x60.h~git-kgdb +++ a/include/asm-ppc/mv64x60.h @@ -340,6 +340,8 @@ u32 mv64x60_calc_mem_size(struct mv64x60 void mv64x60_progress_init(u32 base); void mv64x60_mpsc_progress(char *s, unsigned short hex); +struct platform_device *mv64x60_early_get_pdev_data(const char *name, + int id, int remove); extern struct mv64x60_32bit_window gt64260_32bit_windows[MV64x60_32BIT_WIN_COUNT]; diff -puN include/asm-ppc/mv64x60_defs.h~git-kgdb include/asm-ppc/mv64x60_defs.h --- a/include/asm-ppc/mv64x60_defs.h~git-kgdb +++ a/include/asm-ppc/mv64x60_defs.h @@ -57,7 +57,8 @@ #define MV64x60_IRQ_I2C 37 #define MV64x60_IRQ_BRG 39 #define MV64x60_IRQ_MPSC_0 40 -#define MV64x60_IRQ_MPSC_1 42 +#define MV64360_IRQ_MPSC_1 41 +#define GT64260_IRQ_MPSC_1 42 #define MV64x60_IRQ_COMM 43 #define MV64x60_IRQ_P0_GPP_0_7 56 #define MV64x60_IRQ_P0_GPP_8_15 57 diff -puN include/asm-sh/kgdb.h~git-kgdb include/asm-sh/kgdb.h --- a/include/asm-sh/kgdb.h~git-kgdb +++ a/include/asm-sh/kgdb.h @@ -2,78 +2,41 @@ * May be copied or modified under the terms of the GNU General Public * License. See linux/COPYING for more information. * - * Based on original code by Glenn Engel, Jim Kingdon, - * David Grothe , Tigran Aivazian, and - * Amit S. Kale - * - * Super-H port based on sh-stub.c (Ben Lee and Steve Chamberlain) by - * Henry Bell - * - * Header file for low-level support for remote debug using GDB. + * Based on a file that was modified or based on files by: Glenn Engel, + * Jim Kingdon, David Grothe , Tigran Aivazian , + * Amit S. Kale , sh-stub.c from Ben Lee and + * Steve Chamberlain, Henry Bell + * + * Maintainer: Tom Rini * */ #ifndef __KGDB_H #define __KGDB_H -#include -#include +#include -struct console; +/* Based on sh-gdb.c from gdb-6.1, Glenn + Engel at HP Ben Lee and Steve Chamberlain */ +#define NUMREGBYTES 112 /* 92 */ +#define NUMCRITREGBYTES (9 << 2) +#define BUFMAX 400 -/* Same as pt_regs but has vbr in place of syscall_nr */ +#ifndef __ASSEMBLY__ struct kgdb_regs { - unsigned long regs[16]; - unsigned long pc; - unsigned long pr; - unsigned long sr; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - unsigned long vbr; + unsigned long regs[16]; + unsigned long pc; + unsigned long pr; + unsigned long gbr; + unsigned long vbr; + unsigned long mach; + unsigned long macl; + unsigned long sr; }; -/* State info */ -extern char kgdb_in_gdb_mode; -extern int kgdb_done_init; -extern int kgdb_enabled; -extern int kgdb_nofault; /* Ignore bus errors (in gdb mem access) */ -extern int kgdb_halt; /* Execute initial breakpoint at startup */ -extern char in_nmi; /* Debounce flag to prevent NMI reentry*/ - -/* SCI */ -extern int kgdb_portnum; -extern int kgdb_baud; -extern char kgdb_parity; -extern char kgdb_bits; - -/* Init and interface stuff */ -extern int kgdb_init(void); -extern int (*kgdb_getchar)(void); -extern void (*kgdb_putchar)(int); - -/* Trap functions */ -typedef void (kgdb_debug_hook_t)(struct pt_regs *regs); -typedef void (kgdb_bus_error_hook_t)(void); -extern kgdb_debug_hook_t *kgdb_debug_hook; -extern kgdb_bus_error_hook_t *kgdb_bus_err_hook; - -/* Console */ -void kgdb_console_write(struct console *co, const char *s, unsigned count); -extern int kgdb_console_setup(struct console *, char *); - -/* Prototypes for jmp fns */ -#define _JBLEN 9 -typedef int jmp_buf[_JBLEN]; -extern void longjmp(jmp_buf __jmpb, int __retval); -extern int setjmp(jmp_buf __jmpb); - -/* Forced breakpoint */ -#define breakpoint() \ -do { \ - if (kgdb_enabled) \ - __asm__ __volatile__("trapa #0x3c"); \ -} while (0) +#define BREAKPOINT() asm("trapa #0xff"); +#define BREAK_INSTR_SIZE 2 +#define CACHE_FLUSH_IS_SAFE 1 /* KGDB should be able to flush all kernel text space */ #if defined(CONFIG_CPU_SH4) @@ -86,18 +49,5 @@ do { \ #define kgdb_flush_icache_range(start, end) do { } while (0) #endif -/* Taken from sh-stub.c of GDB 4.18 */ -static const char hexchars[] = "0123456789abcdef"; - -/* Get high hex bits */ -static inline char highhex(const int x) -{ - return hexchars[(x >> 4) & 0xf]; -} - -/* Get low hex bits */ -static inline char lowhex(const int x) -{ - return hexchars[x & 0xf]; -} +#endif /* !__ASSEMBLY__ */ #endif diff -puN include/asm-x86_64/kdebug.h~git-kgdb include/asm-x86_64/kdebug.h --- a/include/asm-x86_64/kdebug.h~git-kgdb +++ a/include/asm-x86_64/kdebug.h @@ -23,6 +23,7 @@ enum die_val { DIE_CALL, DIE_NMI_IPI, DIE_PAGE_FAULT, + DIE_PAGE_FAULT_NO_CONTEXT, }; extern void printk_address(unsigned long address); diff -puN /dev/null include/asm-x86_64/kgdb.h --- /dev/null +++ a/include/asm-x86_64/kgdb.h @@ -0,0 +1,52 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +/* + * Copyright (C) 2001-2004 Amit S. Kale + */ + +#include + +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + */ +#define _RAX 0 +#define _RDX 1 +#define _RCX 2 +#define _RBX 3 +#define _RSI 4 +#define _RDI 5 +#define _RBP 6 +#define _RSP 7 +#define _R8 8 +#define _R9 9 +#define _R10 10 +#define _R11 11 +#define _R12 12 +#define _R13 13 +#define _R14 14 +#define _R15 15 +#define _PC 16 +#define _PS 17 + +/* Number of bytes of registers. */ +#define NUMREGBYTES ((_PS+1)*8) +#define NUMCRITREGBYTES (8 * 8) /* 8 registers. */ + +#ifndef __ASSEMBLY__ +/* BUFMAX defines the maximum number of characters in inbound/outbound + * buffers at least NUMREGBYTES*2 are needed for register packets, and + * a longer buffer is needed to list all threads. */ +#define BUFMAX 1024 +#define BREAKPOINT() asm(" int $3"); +#define CHECK_EXCEPTION_STACK() ((&__get_cpu_var(init_tss))[0].ist[0]) +#define BREAK_INSTR_SIZE 1 +#define CACHE_FLUSH_IS_SAFE 1 +#endif /* !__ASSEMBLY__ */ +#endif /* _ASM_KGDB_H_ */ +#endif /* __KERNEL__ */ diff -puN include/asm-x86_64/system.h~git-kgdb include/asm-x86_64/system.h --- a/include/asm-x86_64/system.h~git-kgdb +++ a/include/asm-x86_64/system.h @@ -22,7 +22,9 @@ /* Save restore flags to clear handle leaking NT */ #define switch_to(prev,next,last) \ - asm volatile(SAVE_CONTEXT \ + asm volatile(".globl __switch_to_begin\n\t" \ + "__switch_to_begin:\n\t" \ + SAVE_CONTEXT \ "movq %%rsp,%P[threadrsp](%[prev])\n\t" /* save RSP */ \ "movq %P[threadrsp](%[next]),%%rsp\n\t" /* restore RSP */ \ "call __switch_to\n\t" \ @@ -34,6 +36,8 @@ "movq %%rax,%%rdi\n\t" \ "jc ret_from_fork\n\t" \ RESTORE_CONTEXT \ + "\n.globl __switch_to_end\n\t" \ + "__switch_to_end:\n\t" \ : "=a" (last) \ : [next] "S" (next), [prev] "D" (prev), \ [threadrsp] "i" (offsetof(struct task_struct, thread.rsp)), \ diff -puN /dev/null include/linux/kgdb.h --- /dev/null +++ a/include/linux/kgdb.h @@ -0,0 +1,289 @@ +/* + * include/linux/kgdb.h + * + * This provides the hooks and functions that KGDB needs to share between + * the core, I/O and arch-specific portions. + * + * Author: Amit Kale and + * Tom Rini + * + * 2001-2004 (c) Amit S. Kale and 2003-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. + */ +#ifdef __KERNEL__ +#ifndef _KGDB_H_ +#define _KGDB_H_ + +#include + +#ifdef CONFIG_KGDB +#include +#include +#include +#include + +#ifndef CHECK_EXCEPTION_STACK +#define CHECK_EXCEPTION_STACK() 1 +#endif + +struct tasklet_struct; +struct pt_regs; +struct task_struct; +struct uart_port; + +#ifdef CONFIG_KGDB_CONSOLE +extern struct console kgdbcons; +#endif + +/* To enter the debugger explicitly. */ +extern void breakpoint(void); +extern int kgdb_connected; +extern int kgdb_may_fault; +extern struct tasklet_struct kgdb_tasklet_breakpoint; + +extern atomic_t kgdb_setting_breakpoint; +extern atomic_t cpu_doing_single_step; +extern atomic_t kgdb_sync_softlockup[NR_CPUS]; + +extern struct task_struct *kgdb_usethread, *kgdb_contthread; + +enum kgdb_bptype { + bp_breakpoint = '0', + bp_hardware_breakpoint, + bp_write_watchpoint, + bp_read_watchpoint, + bp_access_watchpoint +}; + +enum kgdb_bpstate { + bp_none = 0, + bp_removed, + bp_set, + bp_active +}; + +struct kgdb_bkpt { + unsigned long bpt_addr; + unsigned char saved_instr[BREAK_INSTR_SIZE]; + enum kgdb_bptype type; + enum kgdb_bpstate state; +}; + +/* The maximum number of KGDB I/O modules that can be loaded */ +#define MAX_KGDB_IO_HANDLERS 3 + +#ifndef MAX_BREAKPOINTS +#define MAX_BREAKPOINTS 1000 +#endif + +#define KGDB_HW_BREAKPOINT 1 + +/* Required functions. */ +/** + * kgdb_arch_init - Perform any architecture specific initalization. + * + * This function will handle the initalization of any architecture + * specific hooks. + */ +extern int kgdb_arch_init(void); + +/** + * regs_to_gdb_regs - Convert ptrace regs to GDB regs + * @gdb_regs: A pointer to hold the registers in the order GDB wants. + * @regs: The &struct pt_regs of the current process. + * + * Convert the pt_regs in @regs into the format for registers that + * GDB expects, stored in @gdb_regs. + */ +extern void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs); + +/** + * sleeping_regs_to_gdb_regs - Convert ptrace regs to GDB regs + * @gdb_regs: A pointer to hold the registers in the order GDB wants. + * @p: The &struct task_struct of the desired process. + * + * Convert the register values of the sleeping process in @p to + * the format that GDB expects. + * This function is called when kgdb does not have access to the + * &struct pt_regs and therefore it should fill the gdb registers + * @gdb_regs with what has been saved in &struct thread_struct + * thread field during switch_to. + */ +extern void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, + struct task_struct *p); + +/** + * gdb_regs_to_regs - Convert GDB regs to ptrace regs. + * @gdb_regs: A pointer to hold the registers we've recieved from GDB. + * @regs: A pointer to a &struct pt_regs to hold these values in. + * + * Convert the GDB regs in @gdb_regs into the pt_regs, and store them + * in @regs. + */ +extern void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs); + +/** + * kgdb_arch_handle_exception - Handle architecture specific GDB packets. + * @vector: The error vector of the exception that happened. + * @signo: The signal number of the exception that happened. + * @err_code: The error code of the exception that happened. + * @remcom_in_buffer: The buffer of the packet we have read. + * @remcom_out_buffer: The buffer, of %BUFMAX to write a packet into. + * @regs: The &struct pt_regs of the current process. + * + * This function MUST handle the 'c' and 's' command packets, + * as well packets to set / remove a hardware breakpoint, if used. + * If there are additional packets which the hardware needs to handle, + * they are handled here. The code should return -1 if it wants to + * process more packets, and a %0 or %1 if it wants to exit from the + * kgdb hook. + */ +extern int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, + char *remcom_out_buffer, + struct pt_regs *regs); + +/** + * kgdb_roundup_cpus - Get other CPUs into a holding pattern + * @flags: Current IRQ state + * + * On SMP systems, we need to get the attention of the other CPUs + * and get them be in a known state. This should do what is needed + * to get the other CPUs to call kgdb_wait(). Note that on some arches, + * the NMI approach is not used for rounding up all the CPUs. For example, + * in case of MIPS, smp_call_function() is used to roundup CPUs. In + * this case, we have to make sure that interrupts are enabled before + * calling smp_call_function(). The argument to this function is + * the flags that will be used when restoring the interrupts. There is + * local_irq_save() call before kgdb_roundup_cpus(). + * + * On non-SMP systems, this is not called. + */ +extern void kgdb_roundup_cpus(unsigned long flags); + +#ifndef JMP_REGS_ALIGNMENT +#define JMP_REGS_ALIGNMENT +#endif + +extern unsigned long kgdb_fault_jmp_regs[]; + +/** + * kgdb_fault_setjmp - Store state in case we fault. + * @curr_context: An array to store state into. + * + * Certain functions may try and access memory, and in doing so may + * cause a fault. When this happens, we trap it, restore state to + * this call, and let ourself know that something bad has happened. + */ +extern asmlinkage int kgdb_fault_setjmp(unsigned long *curr_context); + +/** + * kgdb_fault_longjmp - Restore state when we have faulted. + * @curr_context: The previously stored state. + * + * When something bad does happen, this function is called to + * restore the known good state, and set the return value to 1, so + * we know something bad happened. + */ +extern asmlinkage void kgdb_fault_longjmp(unsigned long *curr_context); + +/* Optional functions. */ +extern int kgdb_validate_break_address(unsigned long addr); +extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr); +extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle); + +/** + * struct kgdb_arch - Desribe architecture specific values. + * @gdb_bpt_instr: The instruction to trigger a breakpoint. + * @flags: Flags for the breakpoint, currently just %KGDB_HW_BREAKPOINT. + * @shadowth: A value of %1 indicates we shadow information on processes. + * @set_breakpoint: Allow an architecture to specify how to set a software + * breakpoint. + * @remove_breakpoint: Allow an architecture to specify how to remove a + * software breakpoint. + * @set_hw_breakpoint: Allow an architecture to specify how to set a hardware + * breakpoint. + * @remove_hw_breakpoint: Allow an architecture to specify how to remove a + * hardware breakpoint. + * + * The @shadowth flag is an option to shadow information not retrievable by + * gdb otherwise. This is deprecated in favor of a binutils which supports + * CFI macros. + */ +struct kgdb_arch { + unsigned char gdb_bpt_instr[BREAK_INSTR_SIZE]; + unsigned long flags; + unsigned shadowth; + int (*set_breakpoint) (unsigned long, char *); + int (*remove_breakpoint)(unsigned long, char *); + int (*set_hw_breakpoint)(unsigned long, int, enum kgdb_bptype); + int (*remove_hw_breakpoint)(unsigned long, int, enum kgdb_bptype); + void (*remove_all_hw_break)(void); + void (*correct_hw_break)(void); +}; + +/** + * struct kgdb_io - Desribe the interface for an I/O driver to talk with KGDB. + * @read_char: Pointer to a function that will return one char. + * @write_char: Pointer to a function that will write one char. + * @flush: Pointer to a function that will flush any pending writes. + * @init: Pointer to a function that will initialize the device. + * @late_init: Pointer to a function that will do any setup that has + * other dependencies. + * @pre_exception: Pointer to a function that will do any prep work for + * the I/O driver. + * @post_exception: Pointer to a function that will do any cleanup work + * for the I/O driver. + * + * The @init and @late_init function pointers allow for an I/O driver + * such as a serial driver to fully initialize the port with @init and + * be called very early, yet safely call request_irq() later in the boot + * sequence. + * + * @init is allowed to return a non-0 return value to indicate failure. + * If this is called early on, then KGDB will try again when it would call + * @late_init. If it has failed later in boot as well, the user will be + * notified. + */ +struct kgdb_io { + int (*read_char) (void); + void (*write_char) (u8); + void (*flush) (void); + int (*init) (void); + void (*late_init) (void); + void (*pre_exception) (void); + void (*post_exception) (void); +}; + +extern struct kgdb_io kgdb_io_ops; +extern struct kgdb_arch arch_kgdb_ops; +extern int kgdb_initialized; + +extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops); +extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops); + +extern void __init kgdb8250_add_port(int i, struct uart_port *serial_req); +extern void __init kgdb8250_add_platform_port(int i, + struct plat_serial8250_port *serial_req); + +extern int kgdb_hex2long(char **ptr, long *long_val); +extern char *kgdb_mem2hex(char *mem, char *buf, int count); +extern char *kgdb_hex2mem(char *buf, char *mem, int count); +extern int kgdb_get_mem(char *addr, unsigned char *buf, int count); +extern int kgdb_set_mem(char *addr, unsigned char *buf, int count); + +int kgdb_isremovedbreak(unsigned long addr); + +extern int kgdb_handle_exception(int ex_vector, int signo, int err_code, + struct pt_regs *regs); +extern void kgdb_nmihook(int cpu, void *regs); +extern int debugger_step; +extern atomic_t debugger_active; +#else +/* Stubs for when KGDB is not set. */ +static const atomic_t debugger_active = ATOMIC_INIT(0); +#endif /* CONFIG_KGDB */ +#endif /* _KGDB_H_ */ +#endif /* __KERNEL__ */ diff -puN include/linux/module.h~git-kgdb include/linux/module.h --- a/include/linux/module.h~git-kgdb +++ a/include/linux/module.h @@ -226,8 +226,17 @@ enum module_state MODULE_STATE_LIVE, MODULE_STATE_COMING, MODULE_STATE_GOING, + MODULE_STATE_GONE, }; +#ifdef CONFIG_KGDB +#define MAX_SECTNAME 31 +struct mod_section { + void *address; + char name[MAX_SECTNAME + 1]; +}; +#endif + /* Similar stuff for section attributes. */ struct module_sect_attr { @@ -255,6 +264,13 @@ struct module /* Unique handle for this module */ char name[MODULE_NAME_LEN]; +#ifdef CONFIG_KGDB + /* keep kgdb info at the begining so that gdb doesn't have a chance to + * miss out any fields */ + unsigned long num_sections; + struct mod_section *mod_sections; +#endif + /* Sysfs stuff. */ struct module_kobject mkobj; struct module_param_attrs *param_attrs; diff -puN include/linux/netpoll.h~git-kgdb include/linux/netpoll.h --- a/include/linux/netpoll.h~git-kgdb +++ a/include/linux/netpoll.h @@ -16,7 +16,7 @@ struct netpoll { struct net_device *dev; char dev_name[IFNAMSIZ]; const char *name; - void (*rx_hook)(struct netpoll *, int, char *, int); + void (*rx_hook)(struct netpoll *, int, char *, int, struct sk_buff *); u32 local_ip, remote_ip; u16 local_port, remote_port; diff -puN include/linux/serial_8250.h~git-kgdb include/linux/serial_8250.h --- a/include/linux/serial_8250.h~git-kgdb +++ a/include/linux/serial_8250.h @@ -57,6 +57,7 @@ struct uart_port; int serial8250_register_port(struct uart_port *); void serial8250_unregister_port(int line); +void serial8250_unregister_by_port(struct uart_port *port); void serial8250_suspend_port(int line); void serial8250_resume_port(int line); diff -puN kernel/Makefile~git-kgdb kernel/Makefile --- a/kernel/Makefile~git-kgdb +++ a/kernel/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_STOP_MACHINE) += stop_machi obj-$(CONFIG_AUDIT) += audit.o auditfilter.o obj-$(CONFIG_AUDITSYSCALL) += auditsc.o audit_tree.o obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_SYSFS) += ksysfs.o obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ diff -puN /dev/null kernel/kgdb.c --- /dev/null +++ a/kernel/kgdb.c @@ -0,0 +1,1886 @@ +/* + * kernel/kgdb.c + * + * Maintainer: Jason Wessel + * + * Copyright (C) 2000-2001 VERITAS Software Corporation. + * Copyright (C) 2002-2004 Timesys Corporation + * Copyright (C) 2003-2004 Amit S. Kale + * Copyright (C) 2004 Pavel Machek + * Copyright (C) 2004-2006 Tom Rini + * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. + * Copyright (C) 2005-2007 Wind River Systems, Inc. + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Contributors at various stages not listed above: + * Jason Wessel ( jason.wessel@windriver.com ) + * George Anzinger + * Anurekh Saxena (anurekh.saxena@timesys.com) + * Lake Stevens Instrument Division (Glenn Engel) + * Jim Kingdon, Cygnus Support. + * + * Original KGDB stub: David Grothe , + * Tigran Aivazian + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int pid_max; +/* How many times to count all of the waiting CPUs */ +#define ROUNDUP_WAIT 640000 /* Arbitrary, increase if needed. */ +#define BUF_THREAD_ID_SIZE 16 + +/* + * kgdb_initialized with a value of 1 indicates that kgdb is setup and is + * all ready to serve breakpoints and other kernel exceptions. A value of + * -1 indicates that we have tried to initialize early, and need to try + * again later. + */ +int kgdb_initialized; +/* Is a host GDB connected to us? */ +int kgdb_connected; +EXPORT_SYMBOL(kgdb_connected); + +/* Could we be about to try and access a bad memory location? + * If so we also need to flag this has happened. */ +int kgdb_may_fault; + +/* All the KGDB handlers are installed */ +int kgdb_from_module_registered; +/* Guard for recursive entry */ +static int exception_level; + +/* We provide a kgdb_io_ops structure that may be overriden. */ +struct kgdb_io __attribute__((weak)) kgdb_io_ops; +EXPORT_SYMBOL(kgdb_io_ops); + +static struct kgdb_io kgdb_io_ops_prev[MAX_KGDB_IO_HANDLERS]; +static int kgdb_io_handler_cnt; + +/* + * Holds information about breakpoints in a kernel. These breakpoints are + * added and removed by gdb. + */ +struct kgdb_bkpt kgdb_break[MAX_BREAKPOINTS]; + +struct kgdb_arch *kgdb_ops = &arch_kgdb_ops; + +static const char hexchars[] = "0123456789abcdef"; + +static spinlock_t slavecpulocks[NR_CPUS]; +static atomic_t procindebug[NR_CPUS]; +atomic_t kgdb_setting_breakpoint; +EXPORT_SYMBOL(kgdb_setting_breakpoint); +struct task_struct *kgdb_usethread, *kgdb_contthread; + +int debugger_step; +atomic_t debugger_active; +EXPORT_SYMBOL(debugger_active); + +/* Our I/O buffers. */ +static char remcom_in_buffer[BUFMAX]; +static char remcom_out_buffer[BUFMAX]; +/* Storage for the registers, in GDB format. */ +static unsigned long gdb_regs[(NUMREGBYTES + sizeof(unsigned long) - 1) / + sizeof(unsigned long)]; +/* Storage of registers for handling a fault. */ +unsigned long kgdb_fault_jmp_regs[NUMCRITREGBYTES / sizeof(unsigned long)] + JMP_REGS_ALIGNMENT; +static int kgdb_notify_reboot(struct notifier_block *this, + unsigned long code, void *x); +struct debuggerinfo_struct { + void *debuggerinfo; + struct task_struct *task; +} kgdb_info[NR_CPUS]; + +/* to keep track of the CPU which is doing the single stepping*/ +atomic_t cpu_doing_single_step = ATOMIC_INIT(-1); + +atomic_t kgdb_sync_softlockup[NR_CPUS] = {ATOMIC_INIT(0)}; + +/* reboot notifier block */ +static struct notifier_block kgdb_reboot_notifier = { + .notifier_call = kgdb_notify_reboot, + .next = NULL, + .priority = INT_MAX, +}; + +int __attribute__((weak)) + kgdb_validate_break_address(unsigned long addr) +{ + int error = 0; + char tmp_variable[BREAK_INSTR_SIZE]; + error = kgdb_get_mem((char *)addr, tmp_variable, BREAK_INSTR_SIZE); + return error; +} + +int __attribute__((weak)) + kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) +{ + int error = kgdb_get_mem((char *)addr, + saved_instr, BREAK_INSTR_SIZE); + if (error < 0) + return error; + + error = kgdb_set_mem((char *)addr, kgdb_ops->gdb_bpt_instr, + BREAK_INSTR_SIZE); + if (error < 0) + return error; + return 0; +} + +int __attribute__((weak)) + kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) +{ + + int error = kgdb_set_mem((char *)addr, (char *)bundle, + BREAK_INSTR_SIZE); + if (error < 0) + return error; + return 0; +} + +unsigned long __attribute__((weak)) + kgdb_arch_pc(int exception, struct pt_regs *regs) +{ + return instruction_pointer(regs); +} + +static int hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* scan for the sequence $# */ +static void get_packet(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int count; + char ch; + + if (!kgdb_io_ops.read_char) + return; + do { + /* Spin and wait around for the start character, ignore all + * other characters */ + while ((ch = (kgdb_io_ops.read_char())) != '$') ; + kgdb_connected = 1; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < (BUFMAX - 1)) { + ch = kgdb_io_ops.read_char(); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(kgdb_io_ops.read_char()) << 4; + xmitcsum += hex(kgdb_io_ops.read_char()); + + if (checksum != xmitcsum) + /* failed checksum */ + kgdb_io_ops.write_char('-'); + else + /* successful transfer */ + kgdb_io_ops.write_char('+'); + if (kgdb_io_ops.flush) + kgdb_io_ops.flush(); + } + } while (checksum != xmitcsum); +} + +/* + * Send the packet in buffer. + * Check for gdb connection if asked for. + */ +static void put_packet(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + if (!kgdb_io_ops.write_char) + return; + /* $#. */ + while (1) { + kgdb_io_ops.write_char('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + kgdb_io_ops.write_char(ch); + checksum += ch; + count++; + } + + kgdb_io_ops.write_char('#'); + kgdb_io_ops.write_char(hexchars[checksum >> 4]); + kgdb_io_ops.write_char(hexchars[checksum % 16]); + if (kgdb_io_ops.flush) + kgdb_io_ops.flush(); + + /* Now see what we get in reply. */ + ch = kgdb_io_ops.read_char(); + + if (ch == 3) + ch = kgdb_io_ops.read_char(); + + /* If we get an ACK, we are done. */ + if (ch == '+') + return; + + /* If we get the start of another packet, this means + * that GDB is attempting to reconnect. We will NAK + * the packet being sent, and stop trying to send this + * packet. */ + if (ch == '$') { + kgdb_io_ops.write_char('-'); + if (kgdb_io_ops.flush) + kgdb_io_ops.flush(); + return; + } + } +} + +/* + * Wrap kgdb_fault_setjmp() call to enable the kernel faults and save/restore + * the state before/after a fault has happened. + * Note that it *must* be inline to work correctly. + */ +static inline int fault_setjmp(void) +{ +#ifdef CONFIG_PREEMPT + int count = preempt_count(); +#endif + + /* + * kgdb_fault_setjmp() returns 0 after the jump buffer has been setup, + * and non-zero when an expected kernel fault has happened. + */ + if (kgdb_fault_setjmp(kgdb_fault_jmp_regs) == 0) { + kgdb_may_fault = 1; + return 0; + } else { +#ifdef CONFIG_PREEMPT + preempt_count() = count; +#endif + kgdb_may_fault = 0; + return 1; + } +} + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null). May return an error. + */ +char *kgdb_mem2hex(char *mem, char *buf, int count) +{ + if (fault_setjmp() != 0) + return ERR_PTR(-EINVAL); + + /* + * Accessing some registers in a single load instruction is + * required to avoid bad side effects for some I/O registers. + */ + if ((count == 2) && (((long)mem & 1) == 0)) { + unsigned short tmp_s = *(unsigned short *)mem; + + mem += 2; +#ifdef __BIG_ENDIAN + *buf++ = hexchars[(tmp_s >> 12) & 0xf]; + *buf++ = hexchars[(tmp_s >> 8) & 0xf]; + *buf++ = hexchars[(tmp_s >> 4) & 0xf]; + *buf++ = hexchars[tmp_s & 0xf]; +#else + *buf++ = hexchars[(tmp_s >> 4) & 0xf]; + *buf++ = hexchars[tmp_s & 0xf]; + *buf++ = hexchars[(tmp_s >> 12) & 0xf]; + *buf++ = hexchars[(tmp_s >> 8) & 0xf]; +#endif + } else if ((count == 4) && (((long)mem & 3) == 0)) { + unsigned long tmp_l = *(unsigned int *)mem; + + mem += 4; +#ifdef __BIG_ENDIAN + *buf++ = hexchars[(tmp_l >> 28) & 0xf]; + *buf++ = hexchars[(tmp_l >> 24) & 0xf]; + *buf++ = hexchars[(tmp_l >> 20) & 0xf]; + *buf++ = hexchars[(tmp_l >> 16) & 0xf]; + *buf++ = hexchars[(tmp_l >> 12) & 0xf]; + *buf++ = hexchars[(tmp_l >> 8) & 0xf]; + *buf++ = hexchars[(tmp_l >> 4) & 0xf]; + *buf++ = hexchars[tmp_l & 0xf]; +#else + *buf++ = hexchars[(tmp_l >> 4) & 0xf]; + *buf++ = hexchars[tmp_l & 0xf]; + *buf++ = hexchars[(tmp_l >> 12) & 0xf]; + *buf++ = hexchars[(tmp_l >> 8) & 0xf]; + *buf++ = hexchars[(tmp_l >> 20) & 0xf]; + *buf++ = hexchars[(tmp_l >> 16) & 0xf]; + *buf++ = hexchars[(tmp_l >> 28) & 0xf]; + *buf++ = hexchars[(tmp_l >> 24) & 0xf]; +#endif +#ifdef CONFIG_64BIT + } else if ((count == 8) && (((long)mem & 7) == 0)) { + unsigned long long tmp_ll = *(unsigned long long *)mem; + + mem += 8; +#ifdef __BIG_ENDIAN + *buf++ = hexchars[(tmp_ll >> 60) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 56) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 52) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 48) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 44) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 40) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 36) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 32) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 28) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 24) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 20) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 16) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 12) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 8) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 4) & 0xf]; + *buf++ = hexchars[tmp_ll & 0xf]; +#else + *buf++ = hexchars[(tmp_ll >> 4) & 0xf]; + *buf++ = hexchars[tmp_ll & 0xf]; + *buf++ = hexchars[(tmp_ll >> 12) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 8) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 20) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 16) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 28) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 24) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 36) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 32) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 44) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 40) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 52) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 48) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 60) & 0xf]; + *buf++ = hexchars[(tmp_ll >> 56) & 0xf]; +#endif +#endif + } else + while (count-- > 0) { + unsigned char ch = *mem++; + + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + kgdb_may_fault = 0; + *buf = 0; + return (buf); +} + +/* + * Copy the binary array pointed to by buf into mem. Fix $, #, and + * 0x7d escaped with 0x7d. Return a pointer to the character after + * the last byte written. + */ +static char *kgdb_ebin2mem(char *buf, char *mem, int count) +{ + if (fault_setjmp() != 0) + return ERR_PTR(-EINVAL); + + for (; count > 0; count--, buf++) + if (*buf == 0x7d) + *mem++ = *(++buf) ^ 0x20; + else + *mem++ = *buf; + + kgdb_may_fault = 0; + return mem; +} + +/* + * 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 return an error. + */ +char *kgdb_hex2mem(char *buf, char *mem, int count) +{ + if (fault_setjmp() != 0) + return ERR_PTR(-EINVAL); + + if ((count == 2) && (((long)mem & 1) == 0)) { + unsigned short tmp_s = 0; + +#ifdef __BIG_ENDIAN + tmp_s |= hex(*buf++) << 12; + tmp_s |= hex(*buf++) << 8; + tmp_s |= hex(*buf++) << 4; + tmp_s |= hex(*buf++); +#else + tmp_s |= hex(*buf++) << 4; + tmp_s |= hex(*buf++); + tmp_s |= hex(*buf++) << 12; + tmp_s |= hex(*buf++) << 8; +#endif + *(unsigned short *)mem = tmp_s; + mem += 2; + } else if ((count == 4) && (((long)mem & 3) == 0)) { + unsigned long tmp_l = 0; + +#ifdef __BIG_ENDIAN + tmp_l |= hex(*buf++) << 28; + tmp_l |= hex(*buf++) << 24; + tmp_l |= hex(*buf++) << 20; + tmp_l |= hex(*buf++) << 16; + tmp_l |= hex(*buf++) << 12; + tmp_l |= hex(*buf++) << 8; + tmp_l |= hex(*buf++) << 4; + tmp_l |= hex(*buf++); +#else + tmp_l |= hex(*buf++) << 4; + tmp_l |= hex(*buf++); + tmp_l |= hex(*buf++) << 12; + tmp_l |= hex(*buf++) << 8; + tmp_l |= hex(*buf++) << 20; + tmp_l |= hex(*buf++) << 16; + tmp_l |= hex(*buf++) << 28; + tmp_l |= hex(*buf++) << 24; +#endif + *(unsigned long *)mem = tmp_l; + mem += 4; + } else { + int i; + + for (i = 0; i < count; i++) { + unsigned char ch = hex(*buf++) << 4; + + ch |= hex(*buf++); + *mem++ = ch; + } + } + kgdb_may_fault = 0; + return (mem); +} + +/* + * While we find nice hex chars, build a long_val. + * Return number of chars processed. + */ +int kgdb_hex2long(char **ptr, long *long_val) +{ + int hex_val; + int num = 0; + + *long_val = 0; + + while (**ptr) { + hex_val = hex(**ptr); + if (hex_val >= 0) { + *long_val = (*long_val << 4) | hex_val; + num++; + } else + break; + + (*ptr)++; + } + + return (num); +} + +/* Write memory due to an 'M' or 'X' packet. */ +static char *write_mem_msg(int binary) +{ + char *ptr = &remcom_in_buffer[1]; + unsigned long addr; + unsigned long length; + + if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' && + kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') { + if (binary) + ptr = kgdb_ebin2mem(ptr, (char *)addr, length); + else + ptr = kgdb_hex2mem(ptr, (char *)addr, length); + if (CACHE_FLUSH_IS_SAFE) + flush_icache_range(addr, addr + length + 1); + if (IS_ERR(ptr)) + return ptr; + return NULL; + } + + return ERR_PTR(-EINVAL); +} + +static inline char *pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +static inline void error_packet(char *pkt, int error) +{ + error = -error; + pkt[0] = 'E'; + pkt[1] = hexchars[(error / 10)]; + pkt[2] = hexchars[(error % 10)]; + pkt[3] = '\0'; +} + +static char *pack_threadid(char *pkt, unsigned char *id) +{ + char *limit; + + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *id++); + + return pkt; +} + +void int_to_threadref(unsigned char *id, int value) +{ + unsigned char *scan; + int i = 4; + + scan = (unsigned char *)id; + while (i--) + *scan++ = 0; + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} + +static struct task_struct *getthread(struct pt_regs *regs, int tid) +{ + if (init_pid_ns.last_pid == 0) + return current; + + if (num_online_cpus() && + (tid >= pid_max + num_online_cpus() + kgdb_ops->shadowth)) + return NULL; + + if (kgdb_ops->shadowth && (tid >= pid_max + num_online_cpus())) + return kgdb_get_shadow_thread(regs, tid - pid_max - + num_online_cpus()); + + if (tid >= pid_max) + return idle_task(tid - pid_max); + + if (!tid) + return NULL; + + return find_task_by_pid(tid); +} + +#ifdef CONFIG_SMP +static void kgdb_wait(struct pt_regs *regs) +{ + unsigned long flags; + int processor; + + local_irq_save(flags); + processor = raw_smp_processor_id(); + kgdb_info[processor].debuggerinfo = regs; + kgdb_info[processor].task = current; + atomic_set(&procindebug[processor], 1); + atomic_set(&kgdb_sync_softlockup[raw_smp_processor_id()], 1); + + /* Wait till master processor goes completely into the debugger. + * FIXME: this looks racy */ + while (!atomic_read(&procindebug[atomic_read(&debugger_active) - 1])) { + int i = 10; /* an arbitrary number */ + + while (--i) + cpu_relax(); + } + + /* Wait till master processor is done with debugging */ + spin_lock(&slavecpulocks[processor]); + + kgdb_info[processor].debuggerinfo = NULL; + kgdb_info[processor].task = NULL; + + /* fix up hardware debug registers on local cpu */ + if (kgdb_ops->correct_hw_break) + kgdb_ops->correct_hw_break(); + /* Signal the master processor that we are done */ + atomic_set(&procindebug[processor], 0); + spin_unlock(&slavecpulocks[processor]); + local_irq_restore(flags); +} +#endif + +int kgdb_get_mem(char *addr, unsigned char *buf, int count) +{ + if (fault_setjmp() != 0) + return -EINVAL; + + while (count) { + if ((unsigned long)addr < TASK_SIZE) { + kgdb_may_fault = 0; + return -EINVAL; + } + *buf++ = *addr++; + count--; + } + kgdb_may_fault = 0; + return 0; +} + +int kgdb_set_mem(char *addr, unsigned char *buf, int count) +{ + if (fault_setjmp() != 0) + return -EINVAL; + + while (count) { + if ((unsigned long)addr < TASK_SIZE) { + kgdb_may_fault = 0; + return -EINVAL; + } + *addr++ = *buf++; + count--; + } + kgdb_may_fault = 0; + return 0; +} +int kgdb_activate_sw_breakpoints(void) +{ + int i; + int error = 0; + unsigned long addr; + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if (kgdb_break[i].state != bp_set) + continue; + addr = kgdb_break[i].bpt_addr; + if ((error = kgdb_arch_set_breakpoint(addr, + kgdb_break[i].saved_instr))) + return error; + + if (CACHE_FLUSH_IS_SAFE) { + if (current->mm && addr < TASK_SIZE) + flush_cache_range(current->mm->mmap_cache, + addr, addr + BREAK_INSTR_SIZE); + else + flush_icache_range(addr, addr + + BREAK_INSTR_SIZE); + } + + kgdb_break[i].state = bp_active; + } + return 0; +} + +static int kgdb_set_sw_break(unsigned long addr) +{ + int i; + int breakno = -1; + int error = kgdb_validate_break_address(addr); + if (error < 0) + return error; + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if ((kgdb_break[i].state == bp_set) && + (kgdb_break[i].bpt_addr == addr)) + return -EEXIST; + } + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if (kgdb_break[i].state == bp_removed && + kgdb_break[i].bpt_addr == addr) { + breakno = i; + break; + } + } + + if (breakno == -1) { + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if (kgdb_break[i].state == bp_none) { + breakno = i; + break; + } + } + } + if (breakno == -1) + return -E2BIG; + + kgdb_break[breakno].state = bp_set; + kgdb_break[breakno].type = bp_breakpoint; + kgdb_break[breakno].bpt_addr = addr; + + return 0; +} + +int kgdb_deactivate_sw_breakpoints(void) +{ + int i; + int error = 0; + unsigned long addr; + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if (kgdb_break[i].state != bp_active) + continue; + addr = kgdb_break[i].bpt_addr; + if ((error = kgdb_arch_remove_breakpoint(addr, + kgdb_break[i].saved_instr))) + return error; + + if (CACHE_FLUSH_IS_SAFE && current->mm && + addr < TASK_SIZE) + flush_cache_range(current->mm->mmap_cache, + addr, addr + BREAK_INSTR_SIZE); + else if (CACHE_FLUSH_IS_SAFE) + flush_icache_range(addr, + addr + BREAK_INSTR_SIZE); + kgdb_break[i].state = bp_set; + } + return 0; +} + +static int kgdb_remove_sw_break(unsigned long addr) +{ + int i; + + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if ((kgdb_break[i].state == bp_set) && + (kgdb_break[i].bpt_addr == addr)) { + kgdb_break[i].state = bp_removed; + return 0; + } + } + return -ENOENT; +} + +int kgdb_isremovedbreak(unsigned long addr) +{ + int i; + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if ((kgdb_break[i].state == bp_removed) && + (kgdb_break[i].bpt_addr == addr)) + return 1; + } + return 0; +} + +int remove_all_break(void) +{ + int i; + int error; + unsigned long addr; + + /* Clear memory breakpoints. */ + for (i = 0; i < MAX_BREAKPOINTS; i++) { + if (kgdb_break[i].state != bp_set) + continue; + addr = kgdb_break[i].bpt_addr; + if ((error = kgdb_arch_remove_breakpoint(addr, + kgdb_break[i].saved_instr))) + return error; + kgdb_break[i].state = bp_removed; + } + + /* Clear hardware breakpoints. */ + if (kgdb_ops->remove_all_hw_break) + kgdb_ops->remove_all_hw_break(); + + return 0; +} + +static inline int shadow_pid(int realpid) +{ + if (realpid) + return realpid; + + return pid_max + raw_smp_processor_id(); +} + +static char gdbmsgbuf[BUFMAX + 1]; +static void kgdb_msg_write(const char *s, int len) +{ + int i; + int wcount; + char *bufptr; + + /* 'O'utput */ + gdbmsgbuf[0] = 'O'; + + /* Fill and send buffers... */ + while (len > 0) { + bufptr = gdbmsgbuf + 1; + + /* Calculate how many this time */ + if ((len << 1) > (BUFMAX - 2)) + wcount = (BUFMAX - 2) >> 1; + else + wcount = len; + + /* Pack in hex chars */ + for (i = 0; i < wcount; i++) + bufptr = pack_hex_byte(bufptr, s[i]); + *bufptr = '\0'; + + /* Move up */ + s += wcount; + len -= wcount; + + /* Write packet */ + put_packet(gdbmsgbuf); + } +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * Locking hierarchy: + * interface locks, if any (begin_session) + * kgdb lock (debugger_active) + * + * Note that since we can be in here prior to our cpumask being filled + * out, we err on the side of caution and loop over NR_CPUS instead + * of a for_each_online_cpu. + * + */ +int kgdb_handle_exception(int ex_vector, int signo, int err_code, + struct pt_regs *linux_regs) +{ + unsigned long length; + unsigned long addr; + char *ptr; + unsigned long flags; + unsigned i; + long threadid; + unsigned char thref[8]; + struct task_struct *thread = NULL; + unsigned procid; + int numshadowth = num_online_cpus() + kgdb_ops->shadowth; + long kgdb_usethreadid = 0; + int error = 0; + int all_cpus_synced = 0; + struct pt_regs *shadowregs; + int processor = raw_smp_processor_id(); + void *local_debuggerinfo; + + /* Panic on recursive debugger calls. */ + if (atomic_read(&debugger_active) == raw_smp_processor_id() + 1) { + exception_level++; + addr = kgdb_arch_pc(ex_vector, linux_regs); + kgdb_deactivate_sw_breakpoints(); + /* If the break point removed ok at the place exception + * occurred, try to recover and print a warning to the end + * user because the user planted a breakpoint in a place that + * KGDB needs in order to function. + */ + if (kgdb_remove_sw_break(addr) == 0) { + exception_level = 0; + kgdb_skipexception(ex_vector, linux_regs); + kgdb_activate_sw_breakpoints(); + printk(KERN_CRIT + "KGDB: re-enter error: breakpoint removed\n"); + WARN_ON(1); + return 0; + } + remove_all_break(); + kgdb_skipexception(ex_vector, linux_regs); + if (exception_level > 1) + panic("Recursive entry to debugger"); + + printk(KERN_CRIT + "KGDB: re-enter exception: ALL breakpoints killed\n"); + panic("Recursive entry to debugger"); + return 0; + } + + acquirelock: + + /* + * Interrupts will be restored by the 'trap return' code, except when + * single stepping. + */ + local_irq_save(flags); + + /* Hold debugger_active */ + procid = raw_smp_processor_id(); + + while (cmpxchg(&atomic_read(&debugger_active), 0, (procid + 1)) != 0) { + int i = 25; /* an arbitrary number */ + + while (--i) + cpu_relax(); + + if (atomic_read(&cpu_doing_single_step) != -1 && + atomic_read(&cpu_doing_single_step) != procid) + udelay(1); + } + + atomic_set(&kgdb_sync_softlockup[raw_smp_processor_id()], 1); + + /* + * Don't enter if the last instance of the exception handler wanted to + * come into the debugger again. + */ + if (atomic_read(&cpu_doing_single_step) != -1 && + atomic_read(&cpu_doing_single_step) != procid) { + atomic_set(&debugger_active, 0); + local_irq_restore(flags); + goto acquirelock; + } + + /* + * Don't enter if we have hit a removed breakpoint. + */ + if (kgdb_skipexception(ex_vector, linux_regs)) + goto kgdb_restore; + + /* + * Call the I/O drivers pre_exception routine + * if the I/O driver defined one + */ + if (kgdb_io_ops.pre_exception) + kgdb_io_ops.pre_exception(); + + kgdb_info[processor].debuggerinfo = linux_regs; + kgdb_info[processor].task = current; + + kgdb_disable_hw_debug(linux_regs); + + if (!debugger_step || !kgdb_contthread) + for (i = 0; i < NR_CPUS; i++) + spin_lock(&slavecpulocks[i]); + +#ifdef CONFIG_SMP + /* Make sure we get the other CPUs */ + if (!debugger_step || !kgdb_contthread) + kgdb_roundup_cpus(flags); +#endif + + /* spin_lock code is good enough as a barrier so we don't + * need one here */ + atomic_set(&procindebug[processor], 1); + + /* Wait a reasonable time for the other CPUs to be notified and + * be waiting for us. Very early on this could be imperfect + * as num_online_cpus() could be 0.*/ + for (i = 0; i < ROUNDUP_WAIT; i++) { + int cpu; + int num = 0; + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (atomic_read(&procindebug[cpu])) + num++; + } + if (num >= num_online_cpus()) { + all_cpus_synced = 1; + break; + } + } + + /* Clear the out buffer. */ + memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); + + /* Master processor is completely in the debugger */ + kgdb_post_master_code(linux_regs, ex_vector, err_code); + kgdb_deactivate_sw_breakpoints(); + debugger_step = 0; + kgdb_contthread = NULL; + exception_level = 0; + + if (kgdb_connected) { + /* If we're still unable to roundup all of the CPUs, + * send an 'O' packet informing the user again. */ + if (!all_cpus_synced) + kgdb_msg_write("Not all CPUs have been synced for " + "KGDB\n", 39); + /* Reply to host that an exception has occurred */ + ptr = remcom_out_buffer; + *ptr++ = 'T'; + *ptr++ = hexchars[(signo >> 4) % 16]; + *ptr++ = hexchars[signo % 16]; + ptr += strlen(strcpy(ptr, "thread:")); + int_to_threadref(thref, shadow_pid(current->pid)); + ptr = pack_threadid(ptr, thref); + *ptr++ = ';'; + + put_packet(remcom_out_buffer); + } + + kgdb_usethread = kgdb_info[processor].task; + kgdb_usethreadid = shadow_pid(kgdb_info[processor].task->pid); + + while (kgdb_io_ops.read_char) { + char *bpt_type; + error = 0; + + /* Clear the out buffer. */ + memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); + + get_packet(remcom_in_buffer); + + switch (remcom_in_buffer[0]) { + case '?': + /* We know that this packet is only sent + * during initial connect. So to be safe, + * we clear out our breakpoints now incase + * GDB is reconnecting. */ + remove_all_break(); + /* Also, if we haven't been able to roundup all + * CPUs, send an 'O' packet informing the user + * as much. Only need to do this once. */ + if (!all_cpus_synced) + kgdb_msg_write("Not all CPUs have been " + "synced for KGDB\n", 39); + remcom_out_buffer[0] = 'S'; + remcom_out_buffer[1] = hexchars[signo >> 4]; + remcom_out_buffer[2] = hexchars[signo % 16]; + break; + + case 'g': /* return the value of the CPU registers */ + thread = kgdb_usethread; + + if (!thread) { + thread = kgdb_info[processor].task; + local_debuggerinfo = + kgdb_info[processor].debuggerinfo; + } else { + local_debuggerinfo = NULL; + for (i = 0; i < NR_CPUS; i++) { + /* Try to find the task on some other + * or possibly this node if we do not + * find the matching task then we try + * to approximate the results. + */ + if (thread == kgdb_info[i].task) + local_debuggerinfo = + kgdb_info[i].debuggerinfo; + } + } + + /* All threads that don't have debuggerinfo should be + * in __schedule() sleeping, since all other CPUs + * are in kgdb_wait, and thus have debuggerinfo. */ + if (kgdb_ops->shadowth && + kgdb_usethreadid >= pid_max + num_online_cpus()) { + shadowregs = kgdb_shadow_regs(linux_regs, + kgdb_usethreadid - + pid_max - + num_online_cpus + ()); + if (!shadowregs) { + error_packet(remcom_out_buffer, + -EINVAL); + break; + } + regs_to_gdb_regs(gdb_regs, shadowregs); + } else if (local_debuggerinfo) + regs_to_gdb_regs(gdb_regs, local_debuggerinfo); + else { + /* Pull stuff saved during + * switch_to; nothing else is + * accessible (or even particularly relevant). + * This should be enough for a stack trace. */ + sleeping_thread_to_gdb_regs(gdb_regs, thread); + } + kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, + NUMREGBYTES); + break; + + /* set the value of the CPU registers - return OK */ + case 'G': + kgdb_hex2mem(&remcom_in_buffer[1], (char *)gdb_regs, + NUMREGBYTES); + + if (kgdb_usethread && kgdb_usethread != current) + error_packet(remcom_out_buffer, -EINVAL); + else { + gdb_regs_to_regs(gdb_regs, linux_regs); + strcpy(remcom_out_buffer, "OK"); + } + break; + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && + kgdb_hex2long(&ptr, &length) > 0) { + ptr = kgdb_mem2hex((char *)addr, + remcom_out_buffer, + length); + if (IS_ERR(ptr)) + error_packet(remcom_out_buffer, + PTR_ERR(ptr)); + } else + error_packet(remcom_out_buffer, -EINVAL); + break; + + /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */ + case 'M': + ptr = write_mem_msg(0); + if (IS_ERR(ptr)) + error_packet(remcom_out_buffer, PTR_ERR(ptr)); + else + strcpy(remcom_out_buffer, "OK"); + break; + /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */ + case 'X': + ptr = write_mem_msg(1); + if (IS_ERR(ptr)) + error_packet(remcom_out_buffer, PTR_ERR(ptr)); + else + strcpy(remcom_out_buffer, "OK"); + break; + + /* kill or detach. KGDB should treat this like a + * continue. + */ + case 'D': + error = remove_all_break(); + if (error < 0) + error_packet(remcom_out_buffer, error); + else { + strcpy(remcom_out_buffer, "OK"); + kgdb_connected = 0; + } + put_packet(remcom_out_buffer); + goto default_handle; + + case 'k': + /* Don't care about error from remove_all_break */ + remove_all_break(); + kgdb_connected = 0; + goto default_handle; + + /* Reboot */ + case 'R': + /* For now, only honor R0 */ + if (strcmp(remcom_in_buffer, "R0") == 0) { + printk(KERN_CRIT "Executing reboot\n"); + strcpy(remcom_out_buffer, "OK"); + put_packet(remcom_out_buffer); + emergency_sync(); + /* Execution should not return from + * machine_restart() + */ + machine_restart(NULL); + kgdb_connected = 0; + goto default_handle; + } + + /* query */ + case 'q': + switch (remcom_in_buffer[1]) { + case 's': + case 'f': + if (memcmp(remcom_in_buffer + 2, "ThreadInfo", + 10)) { + error_packet(remcom_out_buffer, + -EINVAL); + break; + } + + /* + * If we have not yet completed in + * pidhash_init() there isn't much we + * can give back. + */ + if (init_pid_ns.last_pid == 0) { + if (remcom_in_buffer[1] == 'f') + strcpy(remcom_out_buffer, + "m0000000000000001"); + break; + } + + if (remcom_in_buffer[1] == 'f') + threadid = 1; + + remcom_out_buffer[0] = 'm'; + ptr = remcom_out_buffer + 1; + for (i = 0; i < 17 && threadid < pid_max + + numshadowth; threadid++) { + thread = getthread(linux_regs, + threadid); + if (thread) { + int_to_threadref(thref, + threadid); + pack_threadid(ptr, thref); + ptr += 16; + *(ptr++) = ','; + i++; + } + } + *(--ptr) = '\0'; + break; + + case 'C': + /* Current thread id */ + strcpy(remcom_out_buffer, "QC"); + + threadid = shadow_pid(current->pid); + + int_to_threadref(thref, threadid); + pack_threadid(remcom_out_buffer + 2, thref); + break; + case 'T': + if (memcmp(remcom_in_buffer + 1, + "ThreadExtraInfo,", 16)) { + error_packet(remcom_out_buffer, + -EINVAL); + break; + } + threadid = 0; + ptr = remcom_in_buffer + 17; + kgdb_hex2long(&ptr, &threadid); + if (!getthread(linux_regs, threadid)) { + error_packet(remcom_out_buffer, + -EINVAL); + break; + } + if (threadid < pid_max) + kgdb_mem2hex(getthread(linux_regs, + threadid)->comm, + remcom_out_buffer, 16); + else if (threadid >= pid_max + + num_online_cpus()) + kgdb_shadowinfo(linux_regs, + remcom_out_buffer, + threadid - pid_max - + num_online_cpus()); + else { + static char tmpstr[23 + + BUF_THREAD_ID_SIZE]; + sprintf(tmpstr, "Shadow task %d" + " for pid 0", + (int)(threadid - pid_max)); + kgdb_mem2hex(tmpstr, remcom_out_buffer, + strlen(tmpstr)); + } + break; + } + break; + + /* task related */ + case 'H': + switch (remcom_in_buffer[1]) { + case 'g': + ptr = &remcom_in_buffer[2]; + kgdb_hex2long(&ptr, &threadid); + thread = getthread(linux_regs, threadid); + if (!thread && threadid > 0) { + error_packet(remcom_out_buffer, + -EINVAL); + break; + } + kgdb_usethread = thread; + kgdb_usethreadid = threadid; + strcpy(remcom_out_buffer, "OK"); + break; + + case 'c': + ptr = &remcom_in_buffer[2]; + kgdb_hex2long(&ptr, &threadid); + if (!threadid) + kgdb_contthread = NULL; + else { + thread = getthread(linux_regs, + threadid); + if (!thread && threadid > 0) { + error_packet(remcom_out_buffer, + -EINVAL); + break; + } + kgdb_contthread = thread; + } + strcpy(remcom_out_buffer, "OK"); + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcom_in_buffer[1]; + kgdb_hex2long(&ptr, &threadid); + thread = getthread(linux_regs, threadid); + if (thread) + strcpy(remcom_out_buffer, "OK"); + else + error_packet(remcom_out_buffer, -EINVAL); + break; + /* Since GDB-5.3, it's been drafted that '0' is a software + * breakpoint, '1' is a hardware breakpoint, so let's do + * that. + */ + case 'z': + case 'Z': + bpt_type = &remcom_in_buffer[1]; + ptr = &remcom_in_buffer[2]; + + if (kgdb_ops->set_hw_breakpoint && *bpt_type >= '1') { + /* Unsupported */ + if (*bpt_type > '4') + break; + } else if (*bpt_type != '0' && *bpt_type != '1') + /* Unsupported. */ + break; + /* Test if this is a hardware breakpoint, and + * if we support it. */ + if (*bpt_type == '1' && + !(kgdb_ops->flags & KGDB_HW_BREAKPOINT)) + /* Unsupported. */ + break; + + if (*(ptr++) != ',') { + error_packet(remcom_out_buffer, -EINVAL); + break; + } else if (kgdb_hex2long(&ptr, &addr)) { + if (*(ptr++) != ',' || + !kgdb_hex2long(&ptr, &length)) { + error_packet(remcom_out_buffer, + -EINVAL); + break; + } + } else { + error_packet(remcom_out_buffer, -EINVAL); + break; + } + + if (remcom_in_buffer[0] == 'Z' && *bpt_type == '0') + error = kgdb_set_sw_break(addr); + else if (remcom_in_buffer[0] == 'z' && *bpt_type == '0') + error = kgdb_remove_sw_break(addr); + else if (remcom_in_buffer[0] == 'Z') + error = kgdb_ops->set_hw_breakpoint(addr, + (int)length, + *bpt_type); + else if (remcom_in_buffer[0] == 'z') + error = kgdb_ops->remove_hw_breakpoint(addr, + (int) + length, + *bpt_type); + + if (error == 0) + strcpy(remcom_out_buffer, "OK"); + else + error_packet(remcom_out_buffer, error); + + break; + case 'c': + case 's': + if (kgdb_contthread && kgdb_contthread != current) { + /* Can't switch threads in kgdb */ + error_packet(remcom_out_buffer, -EINVAL); + break; + } + kgdb_activate_sw_breakpoints(); + /* Followthrough to default processing */ + default: +default_handle: + error = kgdb_arch_handle_exception(ex_vector, signo, + err_code, + remcom_in_buffer, + remcom_out_buffer, + linux_regs); + + if (error >= 0 || remcom_in_buffer[0] == 'D' || + remcom_in_buffer[0] == 'k') + goto kgdb_exit; + + } /* switch */ + + /* reply to the request */ + put_packet(remcom_out_buffer); + } + + kgdb_exit: + /* + * Call the I/O driver's post_exception routine + * if the I/O driver defined one. + */ + if (kgdb_io_ops.post_exception) + kgdb_io_ops.post_exception(); + + kgdb_info[processor].debuggerinfo = NULL; + kgdb_info[processor].task = NULL; + atomic_set(&procindebug[processor], 0); + + if (!debugger_step || !kgdb_contthread) { + for (i = 0; i < NR_CPUS; i++) + spin_unlock(&slavecpulocks[i]); + /* Wait till all the processors have quit + * from the debugger. */ + for (i = 0; i < NR_CPUS; i++) { + while (atomic_read(&procindebug[i])) { + int j = 10; /* an arbitrary number */ + + while (--j) + cpu_relax(); + } + } + } + +#ifdef CONFIG_SMP + /* This delay has a real purpose. The problem is that if you + * are single-stepping, you are sending an NMI to all the + * other processors to stop them. Interrupts come in, but + * don't get handled. Then you let them go just long enough + * to get into their interrupt routines and use up some stack. + * You stop them again, and then do the same thing. After a + * while you blow the stack on the other processors. This + * delay gives some time for interrupts to be cleared out on + * the other processors. + */ + if (debugger_step) + mdelay(2); +#endif + kgdb_restore: + /* Free debugger_active */ + atomic_set(&debugger_active, 0); + local_irq_restore(flags); + + return error; +} + +/* + * GDB places a breakpoint at this function to know dynamically + * loaded objects. It's not defined static so that only one instance with this + * name exists in the kernel. + */ + +int module_event(struct notifier_block *self, unsigned long val, void *data) +{ + return 0; +} + +static struct notifier_block kgdb_module_load_nb = { + .notifier_call = module_event, +}; + +void kgdb_nmihook(int cpu, void *regs) +{ +#ifdef CONFIG_SMP + if (!atomic_read(&procindebug[cpu]) && + atomic_read(&debugger_active) != (cpu + 1)) + kgdb_wait((struct pt_regs *)regs); +#endif +} + +/* + * This is called when a panic happens. All we need to do is + * breakpoint(). + */ +static int kgdb_panic_notify(struct notifier_block *self, unsigned long cmd, + void *ptr) +{ + breakpoint(); + + return 0; +} + +static struct notifier_block kgdb_panic_notifier = { + .notifier_call = kgdb_panic_notify, +}; + +/* + * Initialization that needs to be done in either of our entry points. + */ +static void __init kgdb_internal_init(void) +{ + int i; + + /* Initialize our spinlocks. */ + for (i = 0; i < NR_CPUS; i++) + spin_lock_init(&slavecpulocks[i]); + + for (i = 0; i < MAX_BREAKPOINTS; i++) + kgdb_break[i].state = bp_none; + + /* Initialize the I/O handles */ + memset(&kgdb_io_ops_prev, 0, sizeof(kgdb_io_ops_prev)); + + /* We can't do much if this fails */ + register_module_notifier(&kgdb_module_load_nb); + + kgdb_initialized = 1; +} + +static void kgdb_register_for_panic(void) +{ + /* Register for panics(). */ + /* The registration is done in the kgdb_register_for_panic + * routine because KGDB should not try to handle a panic when + * there are no kgdb_io_ops setup. It is assumed that the + * kgdb_io_ops are setup at the time this method is called. + */ + if (!kgdb_from_module_registered) { + atomic_notifier_chain_register(&panic_notifier_list, + &kgdb_panic_notifier); + kgdb_from_module_registered = 1; + } +} + +static void kgdb_unregister_for_panic(void) +{ + /* When this routine is called KGDB should unregister from the + * panic handler and clean up, making sure it is not handling any + * break exceptions at the time. + */ + if (kgdb_from_module_registered) { + kgdb_from_module_registered = 0; + atomic_notifier_chain_unregister(&panic_notifier_list, + &kgdb_panic_notifier); + } +} + +int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops) +{ + + if (kgdb_connected) { + printk(KERN_ERR "kgdb: Cannot load I/O module while KGDB " + "connected.\n"); + return -EINVAL; + } + + /* Save the old values so they can be restored */ + if (kgdb_io_handler_cnt >= MAX_KGDB_IO_HANDLERS) { + printk(KERN_ERR "kgdb: No more I/O handles available.\n"); + return -EINVAL; + } + + /* Check to see if there is an existing driver and if so save its + * values. Also check to make sure the same driver was not trying + * to re-register. + */ + if (kgdb_io_ops.read_char != NULL && + kgdb_io_ops.read_char != local_kgdb_io_ops->read_char) { + memcpy(&kgdb_io_ops_prev[kgdb_io_handler_cnt], + &kgdb_io_ops, sizeof(struct kgdb_io)); + kgdb_io_handler_cnt++; + } + + /* Initialize the io values for this module */ + memcpy(&kgdb_io_ops, local_kgdb_io_ops, sizeof(struct kgdb_io)); + + /* Make the call to register kgdb if is not initialized */ + kgdb_register_for_panic(); + + return 0; +} +EXPORT_SYMBOL(kgdb_register_io_module); + +void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops) +{ + int i; + + /* Unregister KGDB if there were no other prior io hooks, else + * restore the io hooks. + */ + if (kgdb_io_handler_cnt > 0 && kgdb_io_ops_prev[0].read_char != NULL) { + /* First check if the hook that is in use is the one being + * removed */ + if (kgdb_io_ops.read_char == local_kgdb_io_ops->read_char) { + /* Set 'i' to the value of where the list should be + * shifed */ + i = kgdb_io_handler_cnt - 1; + memcpy(&kgdb_io_ops, &kgdb_io_ops_prev[i], + sizeof(struct kgdb_io)); + } else { + /* Simple case to remove an entry for an I/O handler + * that is not in use */ + for (i = 0; i < kgdb_io_handler_cnt; i++) { + if (kgdb_io_ops_prev[i].read_char == + local_kgdb_io_ops->read_char) + break; + } + } + + /* Shift all the entries in the handler array so it is + * ordered from oldest to newest. + */ + kgdb_io_handler_cnt--; + for (; i < kgdb_io_handler_cnt; i++) + memcpy(&kgdb_io_ops_prev[i], &kgdb_io_ops_prev[i + 1], + sizeof(struct kgdb_io)); + + /* Handle the case if we are on the last element and set it + * to NULL; */ + memset(&kgdb_io_ops_prev[kgdb_io_handler_cnt], 0, + sizeof(struct kgdb_io)); + + if (kgdb_connected) + printk(KERN_ERR "kgdb: WARNING: I/O method changed " + "while kgdb was connected state.\n"); + } else { + /* KGDB is no longer able to communicate out, so + * unregister our hooks and reset state. */ + kgdb_unregister_for_panic(); + if (kgdb_connected) { + printk(KERN_CRIT "kgdb: I/O module was unloaded while " + "a debugging session was running. " + "KGDB will be reset.\n"); + if (remove_all_break() < 0) + printk(KERN_CRIT "kgdb: Reset failed.\n"); + kgdb_connected = 0; + } + memset(&kgdb_io_ops, 0, sizeof(struct kgdb_io)); + } +} +EXPORT_SYMBOL(kgdb_unregister_io_module); + +/* + * There are times we need to call a tasklet to cause a breakpoint + * as calling breakpoint() at that point might be fatal. We have to + * check that the exception stack is setup, as tasklets may be scheduled + * prior to this. When that happens, it is up to the architecture to + * schedule this when it is safe to run. + */ +static void kgdb_tasklet_bpt(unsigned long ing) +{ + if (CHECK_EXCEPTION_STACK()) + breakpoint(); +} +EXPORT_SYMBOL(kgdb_tasklet_breakpoint); + +DECLARE_TASKLET(kgdb_tasklet_breakpoint, kgdb_tasklet_bpt, 0); + +/* + * This function can be called very early, either via early_param() or + * an explicit breakpoint() early on. + */ +static void __init kgdb_early_entry(void) +{ + /* Let the architecture do any setup that it needs to. */ + kgdb_arch_init(); + + /* + * Don't try and do anything until the architecture is able to + * setup the exception stack. In this case, it is up to the + * architecture to hook in and look at us when they are ready. + */ + + if (!CHECK_EXCEPTION_STACK()) { + kgdb_initialized = -1; + /* any kind of break point is deferred to late_init */ + return; + } + + /* Now try the I/O. */ + /* For early entry kgdb_io_ops.init must be defined */ + if (!kgdb_io_ops.init || kgdb_io_ops.init()) { + /* Try again later. */ + kgdb_initialized = -1; + return; + } + + /* Finish up. */ + kgdb_internal_init(); + + /* KGDB can assume that if kgdb_io_ops.init was defined that the + * panic registion should be performed at this time. This means + * kgdb_io_ops.init did not come from a kernel module and was + * initialized statically by a built in. + */ + if (kgdb_io_ops.init) + kgdb_register_for_panic(); +} + +/* + * This function will always be invoked to make sure that KGDB will grab + * what it needs to so that if something happens while the system is + * running, KGDB will get involved. If kgdb_early_entry() has already + * been invoked, there is little we need to do. + */ +static int __init kgdb_late_entry(void) +{ + int need_break = 0; + + /* If kgdb_initialized is -1 then we were passed kgdbwait. */ + if (kgdb_initialized == -1) + need_break = 1; + + /* + * If we haven't tried to initialize KGDB yet, we need to call + * kgdb_arch_init before moving onto the I/O. + */ + if (!kgdb_initialized) + kgdb_arch_init(); + + if (kgdb_initialized != 1) { + if (kgdb_io_ops.init && kgdb_io_ops.init()) { + /* When KGDB allows I/O via modules and the core + * I/O init fails KGDB must default to defering the + * I/O setup, and appropriately print an error about + * it. + */ + printk(KERN_ERR "kgdb: Could not setup core I/O " + "for KGDB.\n"); + printk(KERN_INFO "kgdb: Defering I/O setup to kernel " + "module.\n"); + memset(&kgdb_io_ops, 0, sizeof(struct kgdb_io)); + } + + kgdb_internal_init(); + + /* KGDB can assume that if kgdb_io_ops.init was defined that + * panic registion should be performed at this time. This means + * kgdb_io_ops.init did not come from a kernel module and was + * initialized statically by a built in. + */ + if (kgdb_io_ops.init) + kgdb_register_for_panic(); + } + + /* Registering to reboot notifier list*/ + register_reboot_notifier(&kgdb_reboot_notifier); + + /* Now do any late init of the I/O. */ + if (kgdb_io_ops.late_init) + kgdb_io_ops.late_init(); + + if (need_break) { + printk(KERN_CRIT "kgdb: Waiting for connection from remote" + " gdb...\n"); + breakpoint(); + } + + return 0; +} + +late_initcall(kgdb_late_entry); + +/* + * 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) +{ + atomic_set(&kgdb_setting_breakpoint, 1); + wmb(); /* Sync point before breakpoint */ + BREAKPOINT(); + wmb(); /* Sync point after breakpoint */ + atomic_set(&kgdb_setting_breakpoint, 0); +} +EXPORT_SYMBOL(breakpoint); + +#ifdef CONFIG_MAGIC_SYSRQ +static void sysrq_handle_gdb(int key, struct tty_struct *tty) +{ + printk("Entering GDB stub\n"); + breakpoint(); +} +static struct sysrq_key_op sysrq_gdb_op = { + .handler = sysrq_handle_gdb, + .help_msg = "Gdb", + .action_msg = "GDB", +}; + +static int gdb_register_sysrq(void) +{ + printk(KERN_INFO "Registering GDB sysrq handler\n"); + register_sysrq_key('g', &sysrq_gdb_op); + return 0; +} + +module_init(gdb_register_sysrq); +#endif + +static int kgdb_notify_reboot(struct notifier_block *this, + unsigned long code, void *x) +{ + + unsigned long flags; + + /* If we're debugging, or KGDB has not connected, don't try + * and print. */ + if (!kgdb_connected || atomic_read(&debugger_active) != 0) + return 0; + if ((code == SYS_RESTART) || (code == SYS_HALT) || + (code == SYS_POWER_OFF)) { + local_irq_save(flags); + put_packet("X00"); + local_irq_restore(flags); + } + return NOTIFY_DONE; +} + +#ifdef CONFIG_KGDB_CONSOLE +void kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + unsigned long flags; + + /* If we're debugging, or KGDB has not connected, don't try + * and print. */ + if (!kgdb_connected || atomic_read(&debugger_active) != 0) + return; + + local_irq_save(flags); + kgdb_msg_write(s, count); + local_irq_restore(flags); +} + +struct console kgdbcons = { + .name = "kgdb", + .write = kgdb_console_write, + .flags = CON_PRINTBUFFER | CON_ENABLED, +}; +static int __init kgdb_console_init(void) +{ + register_console(&kgdbcons); + return 0; +} + +console_initcall(kgdb_console_init); +#endif + +static int __init opt_kgdb_enter(char *str) +{ + /* We've already done this by an explicit breakpoint() call. */ + if (kgdb_initialized) + return 0; + + kgdb_early_entry(); + if (kgdb_initialized == 1) + printk(KERN_CRIT "Waiting for connection from remote " + "gdb...\n"); + else { + printk(KERN_CRIT "KGDB cannot initialize I/O yet.\n"); + return 0; + } + + breakpoint(); + + return 0; +} + +early_param("kgdbwait", opt_kgdb_enter); diff -puN kernel/module.c~git-kgdb kernel/module.c --- a/kernel/module.c~git-kgdb +++ a/kernel/module.c @@ -65,6 +65,7 @@ extern int module_sysfs_initialized; * (add/delete uses stop_machine). */ static DEFINE_MUTEX(module_mutex); static LIST_HEAD(modules); +static DECLARE_MUTEX(notify_mutex); static BLOCKING_NOTIFIER_HEAD(module_notify_list); @@ -710,6 +711,12 @@ sys_delete_module(const char __user *nam if (ret != 0) goto out; + down(¬ify_mutex); + blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, + mod); + up(¬ify_mutex); + + /* Never wait if forced. */ if (!forced && module_refcount(mod) != 0) wait_for_zero_refcount(mod); @@ -722,6 +729,11 @@ sys_delete_module(const char __user *nam } free_module(mod); + down(¬ify_mutex); + blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GONE, + NULL); + up(¬ify_mutex); + out: mutex_unlock(&module_mutex); return ret; @@ -842,6 +854,9 @@ static ssize_t show_initstate(struct mod case MODULE_STATE_GOING: state = "going"; break; + case MODULE_STATE_GONE: + state = "gone"; + break; } return sprintf(buffer, "%s\n", state); } @@ -1206,6 +1221,11 @@ static void free_module(struct module *m /* Arch-specific cleanup. */ module_arch_cleanup(mod); +#ifdef CONFIG_KGDB + /* kgdb info */ + vfree(mod->mod_sections); +#endif + /* Module unload stuff */ module_unload_free(mod); @@ -1465,6 +1485,31 @@ static void setup_modinfo(struct module } } +#ifdef CONFIG_KGDB +int add_modsects (struct module *mod, Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const + char *secstrings) +{ + int i; + + mod->num_sections = hdr->e_shnum - 1; + mod->mod_sections = vmalloc((hdr->e_shnum - 1) * + sizeof (struct mod_section)); + + if (mod->mod_sections == NULL) { + return -ENOMEM; + } + + for (i = 1; i < hdr->e_shnum; i++) { + mod->mod_sections[i - 1].address = (void *)sechdrs[i].sh_addr; + strncpy(mod->mod_sections[i - 1].name, secstrings + + sechdrs[i].sh_name, MAX_SECTNAME); + mod->mod_sections[i - 1].name[MAX_SECTNAME] = '\0'; + } + + return 0; +} +#endif + #ifdef CONFIG_KALLSYMS static int is_exported(const char *name, const struct module *mod) { @@ -1880,6 +1925,13 @@ static struct module *load_module(void _ add_kallsyms(mod, sechdrs, symindex, strindex, secstrings); +#ifdef CONFIG_KGDB + err = add_modsects(mod, hdr, sechdrs, secstrings); + if (err < 0) { + goto nomodsectinfo; + } +#endif + err = module_finalize(hdr, sechdrs, mod); if (err < 0) goto cleanup; @@ -1940,6 +1992,11 @@ static struct module *load_module(void _ arch_cleanup: module_arch_cleanup(mod); cleanup: + +#ifdef CONFIG_KGDB +nomodsectinfo: + vfree(mod->mod_sections); +#endif module_unload_free(mod); module_free(mod, mod->module_init); free_core: @@ -2011,6 +2068,11 @@ sys_init_module(void __user *umod, /* Init routine failed: abort. Try to protect us from buggy refcounters. */ mod->state = MODULE_STATE_GOING; + down(¬ify_mutex); + blocking_notifier_call_chain(&module_notify_list, + MODULE_STATE_GOING, + mod); + up(¬ify_mutex); synchronize_sched(); if (mod->unsafe) printk(KERN_ERR "%s: module is now stuck!\n", diff -puN kernel/sched.c~git-kgdb kernel/sched.c --- a/kernel/sched.c~git-kgdb +++ a/kernel/sched.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -6557,6 +6558,9 @@ void __might_sleep(char *file, int line) #ifdef in_atomic static unsigned long prev_jiffy; /* ratelimiting */ + if (atomic_read(&debugger_active)) + return; + if ((in_atomic() || irqs_disabled()) && system_state == SYSTEM_RUNNING && !oops_in_progress) { if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy) diff -puN kernel/softlockup.c~git-kgdb kernel/softlockup.c --- a/kernel/softlockup.c~git-kgdb +++ a/kernel/softlockup.c @@ -14,6 +14,7 @@ #include #include #include +#include static DEFINE_SPINLOCK(print_lock); @@ -48,6 +49,9 @@ static unsigned long get_timestamp(void) void touch_softlockup_watchdog(void) { __raw_get_cpu_var(touch_timestamp) = get_timestamp(); +#ifdef CONFIG_KGDB + atomic_set(&kgdb_sync_softlockup[raw_smp_processor_id()], 0); +#endif } EXPORT_SYMBOL(touch_softlockup_watchdog); diff -puN kernel/timer.c~git-kgdb kernel/timer.c --- a/kernel/timer.c~git-kgdb +++ a/kernel/timer.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -897,7 +898,10 @@ static void run_timer_softirq(struct sof void run_local_timers(void) { raise_softirq(TIMER_SOFTIRQ); - softlockup_tick(); +#ifdef CONFIG_KGDB + if (!atomic_read(&kgdb_sync_softlockup[smp_processor_id()])) +#endif + softlockup_tick(); } /* diff -puN lib/Kconfig.debug~git-kgdb lib/Kconfig.debug --- a/lib/Kconfig.debug~git-kgdb +++ a/lib/Kconfig.debug @@ -496,3 +496,5 @@ config FAULT_INJECTION_STACKTRACE_FILTER select FRAME_POINTER help Provide stacktrace filter for fault-injection capabilities + +source "lib/Kconfig.kgdb" diff -puN /dev/null lib/Kconfig.kgdb --- /dev/null +++ a/lib/Kconfig.kgdb @@ -0,0 +1,256 @@ + +config WANT_EXTRA_DEBUG_INFORMATION + bool + select DEBUG_INFO + select UNWIND_INFO + select FRAME_POINTER if X86 || SUPERH + default n + +config UNWIND_INFO + bool + default n + +config KGDB + bool "KGDB: kernel debugging with remote gdb" + select WANT_EXTRA_DEBUG_INFORMATION + select KGDB_ARCH_HAS_SHADOW_INFO if X86_64 + depends on DEBUG_KERNEL && (ARM || X86 || MIPS || (SUPERH && !SUPERH64) || IA64 || PPC) + help + If you say Y here, it will be possible to remotely debug the + kernel using gdb. Documentation of kernel debugger is available + at http://kgdb.sourceforge.net as well as in DocBook form + in Documentation/DocBook/. If unsure, say N. + +config KGDB_ARCH_HAS_SHADOW_INFO + bool + +config KGDB_CONSOLE + bool "KGDB: Console messages through gdb" + depends on KGDB + help + If you say Y here, console messages will appear through gdb. + Other consoles such as tty or ttyS will continue to work as usual. + Note, that if you use this in conjunction with KGDB_ETH, if the + ethernet driver runs into an error condition during use with KGDB + it is possible to hit an infinite recusrion, causing the kernel + to crash, and typically reboot. For this reason, it is preferable + to use NETCONSOLE in conjunction with KGDB_ETH instead of + KGDB_CONSOLE. + +choice + prompt "Method for KGDB communication" + depends on KGDB + default KGDB_MPSC if SERIAL_MPSC + default KGDB_CPM_UART if (CPM2 || 8xx) + default KGDB_SIBYTE if SIBYTE_SB1xxx_SOC + default KGDB_TXX9 if CPU_TX49XX + default KGDB_SH_SCI if SERIAL_SH_SCI + default KGDB_PXA_SERIAL if ARCH_PXA + default KGDB_AMBA_PL011 if ARM_AMBA + default KGDB_8250_NOMODULE + help + There are a number of different ways in which you can communicate + with KGDB. The most common is via serial, with the 8250 driver + (should your hardware have an 8250, or ns1655x style uart). + Another option is to use the NETPOLL framework and UDP, should + your ethernet card support this. Other options may exist. + You can elect to have one core I/O driver that is built into the + kernel for debugging as the kernel is booting, or using only + kernel modules. + +config KGDB_ONLY_MODULES + bool "KGDB: Use only kernel modules for I/O" + depends on MODULES + help + Use only kernel modules to configure KGDB I/O after the + kernel is booted. + +config KGDB_8250_NOMODULE + bool "KGDB: On generic serial port (8250)" + select KGDB_8250 + select SERIAL_8250 + help + Uses generic serial port (8250) to communicate with the host + GDB. This is independent of the normal (SERIAL_8250) driver + for this chipset. + +config KGDBOE_NOMODULE + bool "KGDB: On ethernet - in kernel" + select KGDBOE + help + Uses the NETPOLL API to communicate with the host GDB via UDP. + In order for this to work, the ethernet interface specified must + support the NETPOLL API, and this must be initialized at boot. + See the documentation for syntax. + +config KGDB_MPSC + bool "KGDB: On MV64x60 MPSC" + depends on SERIAL_MPSC + help + Uses a Marvell GT64260B or MV64x60 Multi-Purpose Serial + Controller (MPSC) channel. Note that the GT64260A is not + supported. + +config KGDB_CPM_UART + bool "KGDB: On CPM UART" + depends on PPC && (CPM2 || 8xx) + help + Uses CPM UART to communicate with the host GDB. + +config KGDB_SIBYTE + bool "KGDB: On Broadcom SB1xxx serial port" + depends on MIPS && SIBYTE_SB1xxx_SOC + +config KGDB_TXX9 + bool "KGDB: On TX49xx serial port" + depends on MIPS && CPU_TX49XX + help + Uses TX49xx serial port to communicate with the host KGDB. + +config KGDB_SH_SCI + bool "KGDB: On SH SCI(F) serial port" + depends on SUPERH && SERIAL_SH_SCI + help + Uses the SH SCI(F) serial port to communicate with the host GDB. + +config KGDB_AMBA_PL011 + bool "KGDB: On ARM AMBA PL011 Serial Port" + depends on ARM && ARCH_VERSATILE + help + Enables the KGDB serial driver for the AMBA bus PL011 serial + devices from ARM. + +config KGDB_PXA_SERIAL + bool "KGDB: On the PXA2xx serial port" + depends on ARCH_PXA + help + Enables the KGDB serial driver for Intel PXA SOC +endchoice + +choice + prompt "PXA UART to use for KGDB" + depends on KGDB_PXA_SERIAL + default KGDB_PXA_FFUART + +config KGDB_PXA_FFUART + bool "FFUART" + +config KGDB_PXA_BTUART + bool "BTUART" + +config KGDB_PXA_STUART + bool "STUART" +endchoice + +choice + prompt "SCC/SMC to use for KGDB" + depends on KGDB_CPM_UART + default KGDB_CPM_UART_SCC4 if ADS8272 + +config KGDB_CPM_UART_SCC1 + bool "SCC1" + depends on SERIAL_CPM_SCC1 + +config KGDB_CPM_UART_SCC2 + bool "SCC2" + depends on SERIAL_CPM_SCC2 + +config KGDB_CPM_UART_SCC3 + bool "SCC3" + depends on SERIAL_CPM_SCC3 + +config KGDB_CPM_UART_SCC4 + bool "SCC4" + depends on SERIAL_CPM_SCC4 + +config KGDB_CPM_UART_SMC1 + bool "SMC1" + depends on SERIAL_CPM_SMC1 + +config KGDB_CPM_UART_SMC2 + bool "SMC2" + depends on SERIAL_CPM_SMC2 +endchoice + +config KGDBOE + tristate "KGDB: On ethernet" if !KGDBOE_NOMODULE + depends on m && KGDB + select NETPOLL + select NETPOLL_TRAP + help + Uses the NETPOLL API to communicate with the host GDB via UDP. + In order for this to work, the ethernet interface specified must + support the NETPOLL API, and this must be initialized at boot. + See the documentation for syntax. + +config KGDB_8250 + tristate "KGDB: On generic serial port (8250)" if !KGDB_8250_NOMODULE + depends on m && KGDB_ONLY_MODULES + help + Uses generic serial port (8250) to communicate with the host + GDB. This is independent of the normal (SERIAL_8250) driver + for this chipset. + +config KGDB_SIMPLE_SERIAL + bool "Simple selection of KGDB serial port" + depends on KGDB_8250_NOMODULE + default y + help + If you say Y here, you will only have to pick the baud rate + and port number that you wish to use for KGDB. Note that this + only works on architectures that register known serial ports + early on. If you say N, you will have to provide, either here + or on the command line, the type (I/O or MMIO), IRQ and + address to use. If in doubt, say Y. + +config KGDB_BAUDRATE + int "Debug serial port baud rate" + depends on (KGDB_8250 && KGDB_SIMPLE_SERIAL) || \ + KGDB_MPSC || KGDB_CPM_UART || \ + KGDB_TXX9 || KGDB_PXA_SERIAL || KGDB_AMBA_PL011 + default "115200" + help + gdb and the kernel stub need to agree on the baud rate to be + used. Standard rates from 9600 to 115200 are allowed, and this + may be overridden via the commandline. + +config KGDB_PORT_NUM + int "Serial port number for KGDB" + range 0 1 if KGDB_MPSC + range 0 3 + depends on (KGDB_8250 && KGDB_SIMPLE_SERIAL) || KGDB_MPSC || KGDB_TXX9 + default "1" + help + Pick the port number (0 based) for KGDB to use. + +config KGDB_AMBA_BASE + hex "AMBA PL011 Serial Port Base Address" + default 0x101f2000 if ARCH_VERSATILE + depends on KGDB_AMBA_PL011 + help + Base address of the AMBA port that KGDB will use. + +config KGDB_AMBA_UARTCLK + int "AMBAPL011 Serial UART Clock Frequency" + default 24000000 if ARCH_VERSATILE + depends on KGDB_AMBA_PL011 + help + Frequency (in HZ) of the ARM AMBA UART clock + +config KGDB_AMBA_IRQ + int "AMBA PL011 Serial Port IRQ" + default 13 if ARCH_VERSATILE + depends on KGDB_AMBA_PL011 + help + Pick the IRQ of the AMBA port that KGDB will use. + +config KGDB_8250_CONF_STRING + string "Configuration string for KGDB" + depends on KGDB_8250_NOMODULE && !KGDB_SIMPLE_SERIAL + default "io,2f8,115200,3" if X86 + help + The format of this string should be ,
,,. For example, to use the + serial port on an i386 box located at 0x2f8 and 115200 baud + on IRQ 3 at use: + io,2f8,115200,3 diff -puN net/core/netpoll.c~git-kgdb net/core/netpoll.c --- a/net/core/netpoll.c~git-kgdb +++ a/net/core/netpoll.c @@ -505,7 +505,8 @@ int __netpoll_rx(struct sk_buff *skb) np->rx_hook(np, ntohs(uh->source), (char *)(uh+1), - ulen - sizeof(struct udphdr)); + ulen - sizeof(struct udphdr), + skb); kfree_skb(skb); return 1; _