2004-10-29 18:49:51

by Tom Rini

[permalink] [raw]
Subject: [patch 1/8] A different KGDB stub for -mm


Cc: <[email protected]>, <[email protected]>
The following series of patches adds (or in some cases, replaces) KGDB
support for i386, ppc32, ia64, x86_64 and mips (both 32 and 64bit
tested). This version of KGDB can be used either via serial or ethernet.

Some useful features, such as a descriptive 'info threads' are supported
by a series of GDB patches which Amit is working on getting merged
upstream. These can be found in CVS on kgdb.sourceforge.net in the
gdb module and currently only work on i386. I've sent these patches to
Andrew, who will put them up on kernel.org for easier access.

It is my hope that these patches can be merged into the -mm tree at
this point.

This version of KGDB has a common back-end with hooks for architectures to
what they need to and can't be generalized. Similarly, there are hooks for
I/O so that it can be done by 8250 uart, ethernet via netpoll, or
board-specific UART (e.g. the one found on MIPS SiByte 1250-swarm boards).

kgdb can be told to wait for the system via kgdbwait, or you can just connect
to a running system. Or if you have SysRq, 'g' will drop into KGDB. 8250 can
be configured at compile time or boot time. kgdboe must be configured at
boot-time.

A whole lot of people have helped to get this working, and to avoid an Oscars
moment, I'd like to thank everyone and leave it at that. :)

---

linux-2.6.10-rc1-trini/Documentation/DocBook/Makefile | 2
linux-2.6.10-rc1-trini/Documentation/DocBook/kgdb.tmpl | 175 ++
linux-2.6.10-rc1-trini/Makefile | 2
linux-2.6.10-rc1-trini/include/linux/debugger.h | 59
linux-2.6.10-rc1-trini/include/linux/kgdb.h | 212 ++
linux-2.6.10-rc1-trini/kernel/Makefile | 1
linux-2.6.10-rc1-trini/kernel/kgdb.c | 1390 +++++++++++++++++
linux-2.6.10-rc1-trini/kernel/pid.c | 11
linux-2.6.10-rc1-trini/kernel/sched.c | 7
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 14
10 files changed, 1871 insertions(+), 2 deletions(-)

diff -puN Documentation/DocBook/Makefile~core-lite Documentation/DocBook/Makefile
--- linux-2.6.10-rc1/Documentation/DocBook/Makefile~core-lite 2004-10-29 11:10:47.734122614 -0700
+++ linux-2.6.10-rc1-trini/Documentation/DocBook/Makefile 2004-10-29 11:10:47.751118625 -0700
@@ -11,7 +11,7 @@ DOCBOOKS := wanbook.sgml z8530book.sgml
mousedrivers.sgml deviceiobook.sgml procfs-guide.sgml \
tulip-user.sgml writing_usb_driver.sgml scsidrivers.sgml \
sis900.sgml kernel-api.sgml journal-api.sgml lsm.sgml usb.sgml \
- gadget.sgml libata.sgml mtdnand.sgml librs.sgml
+ gadget.sgml libata.sgml mtdnand.sgml librs.sgml kgdb.sgml

###
# The build process is as follows (targets):
diff -puN /dev/null Documentation/DocBook/kgdb.tmpl
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/Documentation/DocBook/kgdb.tmpl 2004-10-29 11:10:47.852094923 -0700
@@ -0,0 +1,175 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="kgdbInternals">
+ <bookinfo>
+ <title>KGDB Internals</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Tom</firstname>
+ <surname>Rini</surname>
+ <affiliation>
+ <address>
+ <email>[email protected]</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <authorgroup>
+ <author>
+ <firstname>Amit S.</firstname>
+ <surname>Kale</surname>
+ <affiliation>
+ <address>
+ <email>[email protected]</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2004</year>
+ <holder>MontaVista Software, Inc.</holder>
+ </copyright>
+ <copyright>
+ <year>2004</year>
+ <holder>Amit S. Kale</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ 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.
+ </para>
+
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+ <chapter id="Introduction">
+ <title>Introduction</title>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ </chapter>
+ <chapter id="CompilingAKernel">
+ <title>Compiling a kernel</title>
+ <para>
+ To enable <symbol>CONFIG_KGDB</symbol>, look under the "Kernel debugging"
+ and then select "KGDB: kernel debugging with remote gdb".
+ </para>
+ <para>
+ The first choice for I/O is <symbol>CONFIG_KGDB_8250</symbol>. This has
+ sub-options such as <symbol>CONFIG_KGDB_SIMPLE_SERIAL</symbol> which
+ toggles choosing the serial port by ttyS number or by specifying a port
+ and IRQ number.
+ </para>
+ <para>
+ The second choice on most systems for I/O is <symbol>CONFIG_KGDB_ETH</symbol>.
+ This requires that the machine to be debugged has an ethernet card which supports
+ the netpoll API, such as the cards supported by <symbol>CONFIG_E100</symbol>. There
+ are no sub-options for this, but a kernel command line option is required.
+ </chapter>
+ <chapter id="BootingTheKernel">
+ <title>Booting the kernel</title>
+ <para>
+ The Kernel command line option <constant>kgdbwait</constant> makes kgdb wait for
+ gdb connection during booting of a kernel. If the <symbol>CONFIG_KGDB_8250</symbol>
+ 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 <symbol>CONFIG_KGDB_8250</symbol> and
+ <symbol>CONFIG_KGDB_SIMPLE_SERIAL</symbol> then you must pass the option
+ <constant>kgdb8250=portnumber,speed</constant> or on IA64
+ <constant>kgdb8250=portnumber,speed,irq,iomembase</constant> before
+ <constant>kgdbwait</constant>. The values for portnumber are 0-3 and correspond
+ to ttyS0 to ttyS3 respectively. The supported speeds are <constant>9600</constant>,
+ <constant>19200</constant>, <constant>38400</constant>, <constant>57600</constant>,
+ and <constant>115200</constant>.
+ </para>
+ <para>
+ To have KGDB stop the kernel and wait, with the compiled values for the serial
+ driver, pass in: <constant>kgdbwait</constant>.
+ </para>
+ <para>
+ To specify the values of the serial port at boot:
+ <constant>kgdb8250=0,115200</constant>.
+ On IA64 this could also be:
+ <constant>kgdb8250=1,115200,74,0xc0000000ff5e0000</constant>
+ And to have KGDB also stop the kernel and wait for GDB to connect, pass in
+ <constant>kgdbwait</constant> after this arguement.
+ </para>
+ <para>
+ To configure the <symbol>CONFIG_KGDB_ETH</symbol> driver, pass in
+ <constant>kgdboe=[src-port]@&lt;src-ip&gt;/[dev],[tgt-port]@&lt;tgt-ip&gt;/[tgt-macaddr]</constant>
+ where:
+ <itemizedlist>
+ <listitem><para>src-port (optional): source for UDP packets (defaults to <constant>6443</constant>)</para></listitem>
+ <listitem><para>src-ip: source IP to use (interface address)</para></listitem>
+ <listitem><para>dev (optional): network interface (<constant>eth0</constant>)</para></listitem>
+ <listitem><para>tgt-port (optional): port GDB will use (defaults to <constant>6442</constant>)</para></listitem>
+ <listitem><para>tgt-ip: IP address GDB will be connecting from</para></listitem>
+ <listitem><para>tgt-macaddr (optional): ethernet MAC address for logging agent (default is broadcast)</para></listitem>
+ </itemizedlist>
+ </para>
+ </chapter>
+ <chapter id="ConnectingGDB">
+ <title>Connecting gdb</title>
+ <para>
+ 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.
+ </para>
+ <para>
+ Example (serial):
+ </para>
+ <programlisting>
+ % gdb ./vmlinux
+ (gdb) set remotebaud 115200
+ (gdb) target remote /dev/ttyS0
+ </programlisting>
+ <para>
+ Example (ethernet):
+ </para>
+ <programlisting>
+ % gdb ./vmlinux
+ (gdb) target remote udp:192.168.2.2:6443
+ </programlisting>
+ <para>
+ Once connected, you can debug a kernel the way you would debug an
+ application program.
+ </para>
+ </chapter>
+ <chapter id="CommonBackEndReq">
+ <title>The common backend (required)</title>
+ <para>
+ These are the functions of the common backend, found in kernel/kgdb.c
+ which must be supplied by the architecture-specific backend.
+ </para>
+!Iinclude/linux/kgdb.h
+ </chapter>
+ <chapter id="CommonBackEndOpt">
+ <title>The common backend (optional)</title>
+ <para>
+ 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.
+ </para>
+!Ikernel/kgdb.c
+ </chapter>
+</book>
diff -puN Makefile~core-lite Makefile
--- linux-2.6.10-rc1/Makefile~core-lite 2004-10-29 11:10:47.737121910 -0700
+++ linux-2.6.10-rc1-trini/Makefile 2004-10-29 11:10:47.853094689 -0700
@@ -488,6 +488,8 @@ endif

ifndef CONFIG_FRAME_POINTER
CFLAGS += -fomit-frame-pointer
+else
+CFLAGS += -fno-omit-frame-pointer
endif

ifdef CONFIG_DEBUG_INFO
diff -puN /dev/null include/linux/debugger.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/linux/debugger.h 2004-10-29 11:10:47.854094454 -0700
@@ -0,0 +1,59 @@
+#ifndef _DEBUGGER_H_
+#define _DEBUGGER_H_
+
+/*
+ * Copyright (C) 2003-2004 Amit S. Kale
+ *
+ * Definition switchout for debuggers
+ */
+
+/*
+ * KGDB
+ */
+#ifdef CONFIG_KGDB
+
+typedef int gdb_debug_hook(int exVector, int signo, int err_code,
+ struct pt_regs *regs);
+extern gdb_debug_hook *linux_debug_hook;
+#define CHK_DEBUGGER(trapnr,signr,error_code,regs,after) \
+ { \
+ if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs)) \
+ { \
+ (*linux_debug_hook)(trapnr, signr, error_code, regs) ; \
+ after; \
+ } \
+ }
+
+void kgdb_nmihook(int cpu, void *regs);
+static inline void debugger_nmihook(int cpu, void *regs)
+{
+ kgdb_nmihook(cpu, regs);
+}
+
+extern int debugger_step;
+extern atomic_t debugger_active;
+
+/*
+ * No debugger in the kernel
+ */
+#else
+
+#define CHK_DEBUGGER(trapnr,signr,error_code,regs,after) \
+{ \
+ if (0) \
+ after; \
+}
+
+static inline void debugger_nmihook(int cpu, void *regs)
+{
+ /* Do nothing */
+}
+
+#define debugger_step 0
+static const atomic_t debugger_active = { 0 };
+
+#define debugger_memerr_expected 0
+
+#endif
+
+#endif /* _DEBUGGER_H_ */
diff -puN /dev/null include/linux/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/linux/kgdb.h 2004-10-29 11:10:47.854094454 -0700
@@ -0,0 +1,212 @@
+#ifndef _KGDB_H_
+#define _KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/ptrace.h>
+#include <asm/kgdb.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include <linux/debugger.h>
+
+/*
+ * This file should not include ANY others. This makes it usable
+ * most anywhere without the fear of include order or inclusion.
+ * TODO: Make it so!
+ *
+ * This file may be included all the time. It is only active if
+ * CONFIG_KGDB is defined, otherwise it stubs out all the macros
+ * and entry points.
+ */
+
+#if defined(CONFIG_KGDB) && !defined(__ASSEMBLY__)
+/* To enter the debugger explicitly. */
+extern void breakpoint(void);
+extern void kgdb_schedule_breakpoint(void);
+extern void kgdb_process_breakpoint(void);
+extern int kgdb_connected;
+
+extern atomic_t kgdb_setting_breakpoint;
+
+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_disabled,
+ bp_enabled
+};
+
+struct kgdb_bkpt {
+ unsigned long bpt_addr;
+ unsigned char saved_instr[BREAK_INSTR_SIZE];
+ enum kgdb_bptype type;
+ enum kgdb_bpstate state;
+};
+
+#ifndef BREAK_INSTR_SIZE
+#error BREAK_INSTR_SIZE needed by kgdb
+#endif
+
+#ifndef MAX_BREAKPOINTS
+#define MAX_BREAKPOINTS 16
+#endif
+
+#define KGDB_HW_BREAKPOINT 1
+
+/* Required functions. */
+/**
+ * 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.
+ * @InBuffer: The buffer of the packet we have read.
+ * @outBuffer: 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 *InBuffer, char *outBuffer,
+ struct pt_regs *regs);
+
+/* Optional functions. */
+extern int kgdb_arch_init(void);
+extern void kgdb_disable_hw_debug(struct pt_regs *regs);
+extern void kgdb_post_master_code(struct pt_regs *regs, int eVector,
+ int err_code);
+extern int kgdb_set_hw_break(unsigned long addr);
+extern int kgdb_remove_hw_break(unsigned long addr);
+extern void kgdb_remove_all_hw_break(void);
+extern void kgdb_correct_hw_break(void);
+extern void kgdb_shadowinfo(struct pt_regs *regs, char *buffer,
+ unsigned threadid);
+extern struct task_struct *kgdb_get_shadow_thread(struct pt_regs *regs,
+ int threadid);
+extern struct pt_regs *kgdb_shadow_regs(struct pt_regs *regs, int threadid);
+
+/**
+ * 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);
+};
+
+/* Thread reference */
+typedef unsigned char threadref[8];
+
+/**
+ * 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.
+ *
+ * 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) (int);
+ void (*flush) (void);
+ int (*init) (void);
+ void (*late_init) (void);
+};
+
+extern struct kgdb_io kgdb_io_ops;
+extern struct kgdb_arch arch_kgdb_ops;
+extern int kgdb_initialized;
+
+struct uart_port;
+
+extern void kgdb8250_add_port(int i, struct uart_port *serial_req);
+extern int init_kgdboe(void);
+
+int kgdb_hex2long(char **ptr, long *longValue);
+char *kgdb_mem2hex(char *mem, char *buf, int count);
+char *kgdb_hex2mem(char *buf, char *mem, int count);
+int kgdb_get_mem(char *addr, unsigned char *buf, int count);
+int kgdb_set_mem(char *addr, unsigned char *buf, int count);
+
+#else
+#define kgdb_process_breakpoint() do {} while(0)
+#endif /* KGDB && !__ASSEMBLY__ */
+#endif /* _KGDB_H_ */
diff -puN kernel/Makefile~core-lite kernel/Makefile
--- linux-2.6.10-rc1/kernel/Makefile~core-lite 2004-10-29 11:10:47.740121206 -0700
+++ linux-2.6.10-rc1-trini/kernel/Makefile 2004-10-29 11:10:47.855094219 -0700
@@ -24,6 +24,7 @@ obj-$(CONFIG_STOP_MACHINE) += stop_machi
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_SYSFS) += ksysfs.o
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/

diff -puN /dev/null kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/kernel/kgdb.c 2004-10-29 11:10:56.536056614 -0700
@@ -0,0 +1,1390 @@
+/*
+ * 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) 2002-2004 Timesys Corporation
+ * Copyright (C) 2003-2004 Amit S. Kale
+ * Copyright (C) 2004 Pavel Machek <[email protected]>
+ * Copyright (C) 2004 Tom Rini <[email protected]>
+ *
+ * Restructured KGDB for 2.6 kernels.
+ * thread support, support for multiple processors,support for ia-32(x86)
+ * hardware debugging, Console support, handling nmi watchdog
+ * - Amit S. Kale ( [email protected] )
+ *
+ * Several enhancements by George Anzinger <[email protected]>
+ * Generic KGDB Support
+ * Implemented by Anurekh Saxena ([email protected])
+ *
+ * Contributor: Lake Stevens Instrument Division
+ * Written by: Glenn Engel
+ *
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <[email protected]>
+ * Integrated into 2.2.5 kernel by Tigran Aivazian <[email protected]>
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/threads.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <linux/kgdb.h>
+#include <asm/atomic.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <asm/cacheflush.h>
+#include <linux/init.h>
+#include <linux/sysrq.h>
+
+extern int pid_max;
+extern int pidhash_init_done;
+
+#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 = 0;
+/* Is a host GDB connected to us? */
+int kgdb_connected;
+
+/*
+ * 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 volatile int procindebug[NR_CPUS];
+atomic_t kgdb_setting_breakpoint;
+struct task_struct *kgdb_usethread, *kgdb_contthread;
+
+int debugger_step;
+atomic_t debugger_active;
+
+/* This will point to kgdb_handle_exception by default.
+ * The architecture code can override this in its init function
+ */
+gdb_debug_hook *linux_debug_hook;
+
+/* 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)];
+
+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;
+
+/**
+ * kgdb_arch_init - Perform any architecture specific initalization.
+ *
+ * RETURN:
+ * The return value is ignored.
+ *
+ * This function will handle the initalization of any architecture
+ * specific hooks.
+ */
+int __attribute__ ((weak))
+ kgdb_arch_init(void)
+{
+ return 0;
+}
+
+/**
+ * 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.
+ */
+void __attribute__ ((weak))
+ kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+}
+
+/**
+ * kgdb_set_hw_break - Set a hardware breakpoint at @addr.
+ * @addr: The address to set a hardware breakpoint at.
+ */
+int __attribute__ ((weak))
+ kgdb_set_hw_break(unsigned long addr)
+{
+ return 0;
+}
+
+/**
+ * kgdb_remove_hw_break - Remove a hardware breakpoint at @addr.
+ * @addr: The address to remove a hardware breakpoint from.
+ */
+int __attribute__ ((weak))
+ kgdb_remove_hw_break(unsigned long addr)
+{
+ return 0;
+}
+
+/**
+ * kgdb_remove_all_hw_break - Clear all hardware breakpoints.
+ */
+void __attribute__ ((weak))
+ kgdb_remove_all_hw_break(void)
+{
+}
+
+/**
+ * kgdb_correct_hw_break - Correct hardware breakpoints.
+ *
+ * A hook to allow for changes to the hardware breakpoint, called
+ * after a single step (s) or continue (c) packet, and once we're about
+ * to let the kernel continue running.
+ *
+ * This is used to set the hardware breakpoint registers for all the
+ * slave cpus on an SMP configuration. This must be called after any
+ * changes are made to the hardware breakpoints (such as by a single
+ * step (s) or continue (c) packet. This is only required on
+ * architectures that support SMP and every processor has its own set
+ * of breakpoint registers.
+ */
+void __attribute__ ((weak))
+ kgdb_correct_hw_break(void)
+{
+}
+
+/**
+ * kgdb_post_master_code - Save error vector/code numbers.
+ * @regs: Original pt_regs.
+ * @eVector: 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.
+ */
+
+void __attribute__ ((weak))
+ kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+{
+}
+
+/**
+ * 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.
+ */
+void __attribute__ ((weak))
+ 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.
+ */
+struct task_struct __attribute__ ((weak))
+ * kgdb_get_shadow_thread(struct pt_regs *regs, int threadid)
+{
+ return NULL;
+}
+
+/**
+ * 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.
+ */
+struct pt_regs __attribute__ ((weak))
+ * kgdb_shadow_regs(struct pt_regs *regs, int threadid)
+{
+ return NULL;
+}
+
+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 $<data>#<checksum> */
+static void get_packet(char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int count;
+ char ch;
+
+ do {
+ /* wait around for the start character, ignore all other
+ * characters */
+ while ((ch = (kgdb_io_ops.read_char())) != '$')
+ ; /* Spin. */
+ 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)
+ kgdb_io_ops.write_char('-'); /* failed checksum */
+ else
+ kgdb_io_ops.write_char('+'); /* successful transfer */
+ 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;
+
+ /* $<packet info>#<checksum>. */
+ 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;
+ }
+ }
+}
+
+/*
+ * 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)
+{
+ int i;
+ unsigned char ch;
+
+ for (i = 0; i < count; i++) {
+ if ((unsigned long)mem < TASK_SIZE)
+ return ERR_PTR(-EINVAL);
+ ch = *mem++;
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch % 16];
+ }
+ *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)
+{
+ for (; count > 0; count--, buf++) {
+ if ((unsigned long)mem < TASK_SIZE)
+ return ERR_PTR(-EINVAL);
+ if (*buf == 0x7d)
+ *mem++ = *(++buf) ^ 0x20;
+ else
+ *mem++ = *buf;
+ }
+ 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)
+{
+ int i;
+ unsigned char ch;
+
+ for (i = 0; i < count; i++) {
+ ch = hex(*buf++) << 4;
+ ch = ch + hex(*buf++);
+ if ((unsigned long)mem < TASK_SIZE)
+ return ERR_PTR(-EINVAL);
+ *mem++ = ch;
+ }
+ return (mem);
+}
+
+/*
+ * While we find nice hex chars, build a longValue.
+ * Return number of chars processed.
+ */
+int kgdb_hex2long(char **ptr, long *longValue)
+{
+ int numChars = 0;
+ int hexValue;
+
+ *longValue = 0;
+
+ while (**ptr) {
+ hexValue = hex(**ptr);
+ if (hexValue >= 0) {
+ *longValue = (*longValue << 4) | hexValue;
+ numChars++;
+ } else
+ break;
+
+ (*ptr)++;
+ }
+
+ return (numChars);
+}
+
+/* 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, 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);
+ 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, threadref * id)
+{
+ char *limit;
+ unsigned char *altid;
+
+ altid = (unsigned char *)id;
+ limit = pkt + BUF_THREAD_ID_SIZE;
+ while (pkt < limit)
+ pkt = pack_hex_byte(pkt, *altid++);
+
+ return pkt;
+}
+
+void int_to_threadref(threadref * id, int value)
+{
+ unsigned char *scan;
+
+ scan = (unsigned char *)id;
+ {
+ int i = 4;
+ while (i--)
+ *scan++ = 0;
+ }
+ *scan++ = (value >> 24) & 0xff;
+ *scan++ = (value >> 16) & 0xff;
+ *scan++ = (value >> 8) & 0xff;
+ *scan++ = (value & 0xff);
+}
+
+static struct task_struct *getthread(struct pt_regs *regs, int tid)
+{
+ int i;
+ struct task_struct *p;
+
+ if (!pidhash_init_done)
+ return current;
+
+ if (tid >= pid_max + num_online_cpus() + kgdb_ops->shadowth)
+ return NULL;
+
+ if (tid >= pid_max + num_online_cpus())
+ return kgdb_get_shadow_thread(regs, tid - pid_max -
+ num_online_cpus());
+
+ if (tid >= pid_max) {
+ i = 0;
+ do_each_task_pid(0, PIDTYPE_PGID, p) {
+ if (tid == pid_max + i)
+ return p;
+ i++;
+ } while_each_task_pid(0, PIDTYPE_PGID, p);
+ return NULL;
+ }
+
+ 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 = smp_processor_id();
+ procindebug[processor] = 1;
+ kgdb_info[processor].debuggerinfo = regs;
+ kgdb_info[processor].task = current;
+
+ /* Wait till master processor goes completely into the debugger.
+ * FIXME: this looks racy */
+ while (!procindebug[atomic_read(&debugger_active) - 1]) {
+ int i = 10; /* an arbitrary number */
+
+ while (--i)
+ cpu_relax();
+ barrier();
+ }
+
+ /* Wait till master processor is done with debugging */
+ spin_lock(slavecpulocks + processor);
+
+ /* This has been taken from x86 kgdb implementation and
+ * will be needed by architectures that have SMP support
+ */
+ kgdb_correct_hw_break();
+
+ kgdb_info[processor].debuggerinfo = NULL;
+ kgdb_info[processor].task = NULL;
+
+ /* Signal the master processor that we are done */
+ procindebug[processor] = 0;
+ spin_unlock(slavecpulocks + processor);
+ local_irq_restore(flags);
+}
+#endif
+
+int kgdb_get_mem(char *addr, unsigned char *buf, int count)
+{
+ while (count) {
+ if ((unsigned long)addr < TASK_SIZE)
+ return -EINVAL;
+ *buf++ = *addr++;
+ count--;
+ }
+ return 0;
+}
+
+int kgdb_set_mem(char *addr, unsigned char *buf, int count)
+{
+ while (count) {
+ if ((unsigned long)addr < TASK_SIZE)
+ return -EINVAL;
+ *addr++ = *buf++;
+ count--;
+ }
+ return 0;
+}
+
+static int kgdb_set_sw_break(unsigned long addr)
+{
+ int i, breakno = -1;
+ int error;
+
+ for (i = 0; i < MAX_BREAKPOINTS; i++) {
+ if ((kgdb_break[i].state == bp_enabled) &&
+ (kgdb_break[i].bpt_addr == addr)) {
+ return -EEXIST;
+ }
+
+ if (kgdb_break[i].state == bp_disabled) {
+ if ((breakno == -1) || (kgdb_break[i].bpt_addr == addr))
+ breakno = i;
+ }
+ }
+ if (breakno == -1)
+ return -E2BIG;
+
+ if (kgdb_ops->set_breakpoint) {
+ if ((error = kgdb_ops->set_breakpoint(addr, kgdb_break[breakno].saved_instr)) < 0)
+ return error;
+ } else {
+ if ((error = kgdb_get_mem((char *)addr, kgdb_break[breakno].saved_instr,
+ BREAK_INSTR_SIZE)) < 0)
+ return error;
+
+ if ((error = kgdb_set_mem((char *)addr, kgdb_ops->gdb_bpt_instr,
+ BREAK_INSTR_SIZE)) < 0)
+ return error;
+ }
+ 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[breakno].state = bp_enabled;
+ kgdb_break[breakno].type = bp_breakpoint;
+ kgdb_break[breakno].bpt_addr = addr;
+
+ return 0;
+}
+
+static int kgdb_remove_sw_break(unsigned long addr)
+{
+ int i;
+ int error;
+
+ for (i = 0; i < MAX_BREAKPOINTS; i++) {
+ if ((kgdb_break[i].state == bp_enabled) &&
+ (kgdb_break[i].bpt_addr == addr)) {
+ if (kgdb_ops->remove_breakpoint) {
+ if ((error = kgdb_ops->remove_breakpoint(addr,
+ kgdb_break[i].saved_instr)) < 0)
+ return error;
+ } else if ((error =
+ kgdb_set_mem((char *)addr,
+ kgdb_break[i].saved_instr,
+ BREAK_INSTR_SIZE)) < 0)
+ return error;
+ 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_disabled;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+int remove_all_break(void)
+{
+ int i;
+ int error;
+
+ /* Clear memory breakpoints. */
+ for (i = 0; i < MAX_BREAKPOINTS; i++) {
+ if (kgdb_break[i].state == bp_enabled) {
+ unsigned long addr = kgdb_break[i].bpt_addr;
+ if ((error =
+ kgdb_set_mem((char *)addr,
+ kgdb_break[i].saved_instr,
+ BREAK_INSTR_SIZE)) < 0)
+ return error;
+ 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_disabled;
+ }
+
+ /* Clear hardware breakpoints. */
+ kgdb_remove_all_hw_break();
+
+ return 0;
+}
+
+static inline int shadow_pid(int realpid)
+{
+ if (realpid) {
+ return realpid;
+ }
+ if(!pidhash_init_done)
+ return 0;
+ return pid_max + smp_processor_id();
+}
+
+/*
+ * This function does all command procesing for interfacing to gdb.
+ *
+ * Locking hierarchy:
+ * interface locks, if any (begin_session)
+ * kgdb lock (debugger_active)
+ *
+ */
+int kgdb_handle_exception(int exVector, int signo, int err_code,
+ struct pt_regs *linux_regs)
+{
+ unsigned long length, addr;
+ char *ptr;
+ unsigned long flags;
+ int i;
+ long threadid;
+ threadref thref;
+ struct task_struct *thread = NULL;
+ unsigned procid;
+ int numshadowth = num_online_cpus() + kgdb_ops->shadowth;
+ long kgdb_usethreadid = 0;
+ int error = 0;
+ struct pt_regs *shadowregs;
+ int processor = smp_processor_id();
+ void * local_debuggerinfo;
+
+ /* Panic on recursive debugger calls. */
+ if (atomic_read(&debugger_active) == smp_processor_id() + 1) {
+ return 0;
+ }
+
+acquirelock:
+
+ /*
+ * Interrupts will be restored by the 'trap return' code, except when
+ * single stepping.
+ */
+ local_irq_save(flags);
+
+ /* Hold debugger_active */
+ procid = smp_processor_id();
+ while (cmpxchg(&atomic_read(&debugger_active), 0, (procid + 1)) != 0) {
+ int i = 25; /* an arbitrary number */
+
+ while (--i)
+ cpu_relax();
+ }
+ /*
+ * 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);
+ goto acquirelock;
+ }
+
+ 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 < num_online_cpus(); i++) {
+ spin_lock(&slavecpulocks[i]);
+ }
+ }
+
+ /* spin_lock code is good enough as a barrier so we don't
+ * need one here */
+ procindebug[smp_processor_id()] = 1;
+
+ /* 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, exVector, err_code);
+
+ debugger_step = 0;
+ kgdb_contthread = NULL;
+
+ if (kgdb_connected) {
+ /* 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 (1) {
+ 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();
+ 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_each_online_cpu (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_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;
+
+ case 'G': /* set the value of the CPU registers - return OK */
+ 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) {
+ if (IS_ERR(ptr = kgdb_mem2hex((char *)addr,
+ remcom_out_buffer,
+ length)))
+ 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':
+ if (IS_ERR(ptr = write_mem_msg(0)))
+ 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':
+ if (IS_ERR(ptr = write_mem_msg(1)))
+ 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':
+ if ((error = remove_all_break()) < 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;
+
+ /* 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 (remcom_in_buffer[1] == 'f') {
+ threadid = 1;
+ }
+ remcom_out_buffer[0] = 'm';
+ ptr = remcom_out_buffer + 1;
+ for (i = 0; i < 32 && 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' ||
+ (*bpt_type >= '1' && kgdb_ops->set_hw_breakpoint)) &&
+ !(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 == '1')
+ error = kgdb_set_hw_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' && *bpt_type == '1')
+ error = kgdb_remove_hw_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;
+ }
+
+ /* Followthrough to default processing */
+
+ default:
+ default_handle:
+ error = kgdb_arch_handle_exception(exVector, 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:
+ kgdb_info[processor].debuggerinfo = NULL;
+ kgdb_info[processor].task = NULL;
+ procindebug[smp_processor_id()] = 0;
+
+ if(!debugger_step || !kgdb_contthread){
+ for (i = 0; i < num_online_cpus(); i++) {
+ spin_unlock(&slavecpulocks[i]);
+ }
+ /* Wait till all the processors have quit
+ * from the debugger
+ */
+ for_each_online_cpu (i) {
+ while (procindebug[i]) {
+ int j = 10; /* an arbitrary number */
+
+ while (--j)
+ cpu_relax();
+ barrier();
+ }
+ }
+ }
+
+ /* 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,
+};
+
+/*
+ * Sometimes we need to schedule a breakpoint because we can't break
+ * right where we are.
+ */
+static int kgdb_need_breakpoint[NR_CPUS];
+
+void kgdb_schedule_breakpoint(void)
+{
+ kgdb_need_breakpoint[smp_processor_id()] = 1;
+}
+
+void kgdb_process_breakpoint(void)
+{
+ /*
+ * Handle a breakpoint queued from inside network driver code
+ * to avoid reentrancy issues
+ */
+ if (kgdb_need_breakpoint[smp_processor_id()]) {
+ kgdb_need_breakpoint[smp_processor_id()] = 0;
+ breakpoint();
+ }
+}
+
+void kgdb_nmihook(int cpu, void *regs)
+{
+#ifdef CONFIG_SMP
+ if (!procindebug[cpu] && atomic_read(&debugger_active) != (cpu + 1)) {
+ kgdb_wait((struct pt_regs *)regs);
+ }
+#endif
+}
+
+/*
+ * 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_disabled;
+
+ linux_debug_hook = kgdb_handle_exception;
+
+ /* We can't do much if this fails */
+ register_module_notifier(&kgdb_module_load_nb);
+
+ /* Clear things. */
+ atomic_set(&debugger_active, 0);
+ atomic_set(&kgdb_setting_breakpoint, 0);
+ memset(kgdb_need_breakpoint, 0, sizeof(kgdb_need_breakpoint));
+
+ kgdb_initialized = 1;
+}
+
+/*
+ * This function can be called very early, either via early_param() or
+ * an explicit breakpoint() early on.
+ */
+static void __init kgdb_early_entry(void)
+{
+ int ret;
+
+ /*
+ * Don't try and do anything until the architecture is able to
+ * setup the exception tables, if needed. If they aren't ready
+ * yet, we'll do a special case and call kgdb_schedule_breakpoint()
+ * as at that point, this test must be true.
+ */
+ if(!CHECK_EXCEPTION_STACK()) {
+ kgdb_schedule_breakpoint();
+ return;
+ }
+
+ /* Let the architecture do any setup that it needs to. */
+ kgdb_arch_init();
+
+ atomic_set(&cpu_doing_single_step,-1);
+
+ /* Now try the I/O. */
+ ret = kgdb_io_ops.init();
+ printk("Got back %d\n", ret);
+ if (ret) {
+ /* Try again later. */
+ kgdb_initialized = -1;
+ printk("Going away\n");
+ return;
+ }
+
+ /* Finish up. */
+ kgdb_internal_init();
+}
+
+/*
+ * 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()) {
+ printk(KERN_ERR "Could not setup I/O for KGDB.\n");
+ return -1;
+ }
+ kgdb_internal_init();
+ }
+
+ /* 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 "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)
+{
+ if (kgdb_initialized != 1) {
+ 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;
+ }
+ }
+
+ atomic_set(&kgdb_setting_breakpoint, 1);
+ wmb();
+ BREAKPOINT();
+ wmb();
+ atomic_set(&kgdb_setting_breakpoint, 0);
+}
+EXPORT_SYMBOL(breakpoint);
+
+#ifdef CONFIG_MAGIC_SYSRQ
+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);
+#endif
+
+static int __init opt_kgdb_enter(char *str)
+{
+ /* We've already done this by an explicit breakpoint() call. */
+ if (kgdb_initialized)
+ return 0;
+
+ /* Call breakpoint() which will take care of init. */
+ breakpoint();
+
+ return 0;
+}
+early_param("kgdbwait", opt_kgdb_enter);
diff -puN kernel/pid.c~core-lite kernel/pid.c
--- linux-2.6.10-rc1/kernel/pid.c~core-lite 2004-10-29 11:10:47.742120737 -0700
+++ linux-2.6.10-rc1-trini/kernel/pid.c 2004-10-29 11:10:47.857093750 -0700
@@ -250,8 +250,13 @@ void switch_exec_pids(task_t *leader, ta
/*
* The pid hash table is scaled according to the amount of memory in the
* machine. From a minimum of 16 slots up to 4096 slots at one gigabyte or
- * more.
+ * more. KGDB needs to know if this function has been called already,
+ * since we might have entered KGDB very early.
*/
+#ifdef CONFIG_KGDB
+int pidhash_init_done;
+#endif
+
void __init pidhash_init(void)
{
int i, j, pidhash_size;
@@ -273,6 +278,10 @@ void __init pidhash_init(void)
for (j = 0; j < pidhash_size; j++)
INIT_HLIST_HEAD(&pid_hash[i][j]);
}
+
+#ifdef CONFIG_KGDB
+ pidhash_init_done = 1;
+#endif
}

void __init pidmap_init(void)
diff -puN kernel/sched.c~core-lite kernel/sched.c
--- linux-2.6.10-rc1/kernel/sched.c~core-lite 2004-10-29 11:10:47.745120033 -0700
+++ linux-2.6.10-rc1-trini/kernel/sched.c 2004-10-29 11:10:47.861092811 -0700
@@ -44,6 +44,7 @@
#include <linux/seq_file.h>
#include <linux/syscalls.h>
#include <linux/times.h>
+#include <linux/debugger.h>
#include <asm/tlb.h>

#include <asm/unistd.h>
@@ -4567,6 +4568,12 @@ void __might_sleep(char *file, int line)
#if defined(in_atomic)
static unsigned long prev_jiffy; /* ratelimiting */

+ if (atomic_read(&debugger_active))
+ return;
+
+ if (atomic_read(&debugger_active))
+ return;
+
if ((in_atomic() || irqs_disabled()) &&
system_state == SYSTEM_RUNNING) {
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
diff -puN lib/Kconfig.debug~core-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~core-lite 2004-10-29 11:10:47.747119563 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:11:06.512714888 -0700
@@ -82,6 +82,7 @@ config DEBUG_BUGVERBOSE
config DEBUG_INFO
bool "Compile the kernel with debug info"
depends on DEBUG_KERNEL && (ALPHA || CRIS || X86 || IA64 || M68K || MIPS || PARISC || PPC32 || PPC64 || ARCH_S390 || (SUPERH && !SUPERH64) || SPARC64 || V850 || X86_64)
+ default y if KGDB
help
If you say Y here the resulting kernel image will include
debugging info resulting in a larger kernel image.
@@ -104,9 +105,22 @@ if !X86_64
config FRAME_POINTER
bool "Compile the kernel with frame pointers"
depends on X86 || CRIS || M68KNOMMU || PARISC
+ default y if KGDB
help
If you say Y here the resulting kernel image will be slightly larger
and slower, but it will give very useful debugging information.
If you don't debug the kernel, you can say N, but we may not be able
to solve problems without frame pointers.
endif
+
+config KGDB
+ bool "KGDB: kernel debugging with remote gdb"
+ depends on DEBUG_KERNEL
+ help
+ If you say Y here, it will be possible to remotely debug the
+ kernel using gdb. This enlarges your kernel image disk size by
+ several megabytes and requires a machine with more than 128 MB
+ RAM to avoid excessive linking time.
+ Documentation of kernel debugger available at
+ http://kgdb.sourceforge.net
+ This is only useful for kernel hackers. If unsure, say N.
_


2004-10-29 18:53:09

by Tom Rini

[permalink] [raw]
Subject: [patch 2/8] KGDB support for i386


This adds support for KGDB on the i386 architecture. The only slightly
related change is to make i386 parse early_params now as KGDB registers as
one. We also add an early_trap_init() call which does the parts of
trap_init() which need to be done for KGDB to be entered, and can be done
safely this early.

As an aside, I believe the hunk to arch/i386/kernel/signal.c can be dropped in
a post 2.6.10-rc1 tree as something similar using unlikey() has been put in.

---

linux-2.6.10-rc1-trini/arch/i386/kernel/Makefile | 1
linux-2.6.10-rc1-trini/arch/i386/kernel/irq.c | 3
linux-2.6.10-rc1-trini/arch/i386/kernel/kgdb.c | 300 +++++++++++++++++++++++
linux-2.6.10-rc1-trini/arch/i386/kernel/nmi.c | 11
linux-2.6.10-rc1-trini/arch/i386/kernel/setup.c | 3
linux-2.6.10-rc1-trini/arch/i386/kernel/signal.c | 3
linux-2.6.10-rc1-trini/arch/i386/kernel/traps.c | 31 +-
linux-2.6.10-rc1-trini/arch/i386/mm/fault.c | 10
linux-2.6.10-rc1-trini/include/asm-i386/kgdb.h | 48 +++
linux-2.6.10-rc1-trini/include/asm-i386/system.h | 10
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
11 files changed, 405 insertions(+), 17 deletions(-)

diff -puN arch/i386/kernel/Makefile~i386-lite arch/i386/kernel/Makefile
--- linux-2.6.10-rc1/arch/i386/kernel/Makefile~i386-lite 2004-10-29 11:26:44.461448834 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/Makefile 2004-10-29 11:26:44.481444139 -0700
@@ -32,6 +32,7 @@ obj-$(CONFIG_ACPI_SRAT) += srat.o
obj-$(CONFIG_HPET_TIMER) += time_hpet.o
obj-$(CONFIG_EFI) += efi.o efi_stub.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_KGDB) += kgdb.o

EXTRA_AFLAGS := -traditional

diff -puN arch/i386/kernel/irq.c~i386-lite arch/i386/kernel/irq.c
--- linux-2.6.10-rc1/arch/i386/kernel/irq.c~i386-lite 2004-10-29 11:26:44.463448364 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/irq.c 2004-10-29 11:26:44.482443904 -0700
@@ -15,6 +15,7 @@
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
+#include <linux/kgdb.h>

#ifndef CONFIG_X86_LOCAL_APIC
/*
@@ -103,6 +104,8 @@ asmlinkage unsigned int do_IRQ(struct pt

irq_exit();

+ kgdb_process_breakpoint();
+
return 1;
}

diff -puN /dev/null arch/i386/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/kgdb.c 2004-10-29 11:26:44.482443904 -0700
@@ -0,0 +1,300 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ */
+/*
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale<[email protected]>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <[email protected]>
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/vm86.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <asm/desc.h>
+#include <asm/kdebug.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)(&regs->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] = *(unsigned long *)(p->thread.esp + 4);
+ 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];
+#if 0 /* can't change these */
+ regs->esp = gdb_regs[_ESP];
+ regs->xss = gdb_regs[_SS];
+ regs->fs = gdb_regs[_FS];
+ regs->gs = gdb_regs[_GS];
+#endif
+
+}
+
+struct hw_breakpoint {
+ unsigned enabled;
+ unsigned type;
+ unsigned len;
+ unsigned addr;
+} breakinfo[4] = {
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+};
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+ int correctit;
+ int breakbit;
+ unsigned dr7;
+
+ asm volatile ("movl %%db7, %0\n":"=r" (dr7)
+ :);
+ do {
+ unsigned addr0, addr1, addr2, addr3;
+ asm volatile ("movl %%db0, %0\n"
+ "movl %%db1, %1\n"
+ "movl %%db2, %2\n"
+ "movl %%db3, %3\n":"=r" (addr0), "=r"(addr1),
+ "=r"(addr2), "=r"(addr3):);
+ } while (0);
+ correctit = 0;
+ for (breakno = 0; breakno < 3; breakno++) {
+ breakbit = 2 << (breakno << 1);
+ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 |= breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ dr7 |= (((breakinfo[breakno].len << 2) |
+ breakinfo[breakno].type) << 16) <<
+ (breakno << 2);
+ switch (breakno) {
+ case 0:
+ asm volatile ("movl %0, %%dr0\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 1:
+ asm volatile ("movl %0, %%dr1\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 2:
+ asm volatile ("movl %0, %%dr2\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 3:
+ asm volatile ("movl %0, %%dr3\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+ }
+ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 &= ~breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ }
+ }
+ if (correctit) {
+ asm volatile ("movl %0, %%db7\n"::"r" (dr7));
+ }
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ /* Disable hardware debugging while we are in kgdb */
+ asm volatile ("movl %0,%%db7": /* no output */ :"r" (0));
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_i386vector = eVector;
+ gdb_i386errcode = err_code;
+}
+
+int kgdb_arch_handle_exception(int exceptionVector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ long addr;
+ long breakno;
+ char *ptr;
+ int newPC;
+ int dr6;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ case 's':
+ if (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(remcom_out_buffer, "E00");
+ break;
+ }
+
+ /* 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;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,smp_processor_id());
+ }
+
+ asm volatile ("movl %%db6, %0\n":"=r" (dr6));
+ if (!(dr6 & 0x4000)) {
+ for (breakno = 0; breakno < 4; ++breakno) {
+ if (dr6 & (1 << breakno)) {
+ if (breakinfo[breakno].type == 0) {
+ /* Set restore flag */
+ linux_regs->eflags |= X86_EFLAGS_RF;
+ break;
+ }
+ }
+ }
+ }
+ kgdb_correct_hw_break();
+ asm volatile ("movl %0, %%db6\n"::"r" (0));
+
+ return (0);
+ } /* switch */
+ return -1; /* this means that we do not want to exit from the handler */
+}
+
+/* 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 = (struct die_args *)ptr;
+ struct pt_regs *regs = args->regs;
+
+ /* See if KGDB is interested. */
+ if (cmd == DIE_PAGE_FAULT)
+ return NOTIFY_DONE;
+ else if (user_mode(regs)) {
+ if (cmd == DIE_OOPS)
+ CHK_DEBUGGER(args->trapnr, args->signr, args->err,
+ args->regs,);
+ return NOTIFY_DONE;
+ } else if (cmd == DIE_NMI_IPI) {
+ if (atomic_read(&debugger_active)) {
+ debugger_nmihook(smp_processor_id(), ptr);
+ return NOTIFY_STOP;
+ }
+ }
+
+ CHK_DEBUGGER(args->trapnr, args->signr, args->err, args->regs,);
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_notify,
+};
+
+int kgdb_arch_init(void) {
+ notifier_chain_register(&i386die_chain, &kgdb_notifier);
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0xcc},
+};
diff -puN arch/i386/kernel/nmi.c~i386-lite arch/i386/kernel/nmi.c
--- linux-2.6.10-rc1/arch/i386/kernel/nmi.c~i386-lite 2004-10-29 11:26:44.465447895 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/nmi.c 2004-10-29 11:26:44.483443669 -0700
@@ -26,6 +26,7 @@
#include <linux/nmi.h>
#include <linux/sysdev.h>
#include <linux/sysctl.h>
+#include <linux/debugger.h>

#include <asm/smp.h>
#include <asm/mtrr.h>
@@ -481,7 +482,15 @@ void nmi_watchdog_tick (struct pt_regs *

sum = irq_stat[cpu].apic_timer_irqs;

- if (last_irq_sums[cpu] == sum) {
+ if (atomic_read(&debugger_active)) {
+ /*
+ * The machine is in debugger, hold this cpu if already
+ * not held.
+ */
+ debugger_nmihook(cpu, regs);
+ alert_counter[cpu] = 0;
+
+ } else if (last_irq_sums[cpu] == sum) {
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
diff -puN arch/i386/kernel/setup.c~i386-lite arch/i386/kernel/setup.c
--- linux-2.6.10-rc1/arch/i386/kernel/setup.c~i386-lite 2004-10-29 11:26:44.467447425 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/setup.c 2004-10-29 11:26:44.484443435 -0700
@@ -117,6 +117,7 @@ struct e820map e820;
unsigned char aux_device_present;

extern void early_cpu_init(void);
+extern void early_trap_init(void);
extern void dmi_scan_machine(void);
extern void generic_apic_probe(char *);
extern int root_mountflags;
@@ -1302,6 +1303,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
@@ -1358,6 +1360,7 @@ void __init setup_arch(char **cmdline_p)
data_resource.end = virt_to_phys(_edata)-1;

parse_cmdline_early(cmdline_p);
+ parse_early_param();

max_low_pfn = setup_memory();

diff -puN arch/i386/kernel/signal.c~i386-lite arch/i386/kernel/signal.c
--- linux-2.6.10-rc1/arch/i386/kernel/signal.c~i386-lite 2004-10-29 11:26:44.469446956 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/signal.c 2004-10-29 11:26:44.485443200 -0700
@@ -600,7 +600,8 @@ int fastcall do_signal(struct pt_regs *r
* have been cleared if the watchpoint triggered
* inside the kernel.
*/
- __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7]));
+ if (current->thread.debugreg[7])
+ __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7]));

/* Whee! Actually deliver the signal. */
handle_signal(signr, &info, &ka, oldset, regs);
diff -puN arch/i386/kernel/traps.c~i386-lite arch/i386/kernel/traps.c
--- linux-2.6.10-rc1/arch/i386/kernel/traps.c~i386-lite 2004-10-29 11:26:44.471446486 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/traps.c 2004-10-29 11:26:44.486442965 -0700
@@ -53,6 +53,7 @@

#include <linux/irq.h>
#include <linux/module.h>
+#include <linux/debugger.h>

#include "mach_traps.h"

@@ -332,7 +333,7 @@ void die(const char * str, struct pt_reg
#endif
if (nl)
printk("\n");
- notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
+ notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
show_registers(regs);
} else
printk(KERN_ERR "Recursive die() failure, output suppressed\n");
@@ -711,7 +712,7 @@ asmlinkage void do_debug(struct pt_regs
tsk->thread.debugreg[6] = condition;

/* Mask out spurious TF errors due to lazy TF clearing */
- if (condition & DR_STEP) {
+ if (condition & DR_STEP && !debugger_step) {
/*
* The TF error should be masked out only if the current
* process is not traced and if the TRAP flag has been set
@@ -733,12 +734,14 @@ asmlinkage void do_debug(struct pt_regs
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
-
- /* If this is a kernel mode trap, save the user PC on entry to
- * the kernel, that's what the debugger can make sense of.
- */
- info.si_addr = ((regs->xcs & 3) == 0) ? (void __user *)tsk->thread.eip
- : (void __user *)regs->eip;
+
+ /* If this is a kernel mode trap, we need to reset db7 to allow us
+ * to continue sanely */
+ if ((regs->xcs & 3) == 0)
+ goto clear_dr7;
+
+ info.si_addr = (void *)regs->eip;
+
force_sig_info(SIGTRAP, &info, tsk);

/* Disable additional traps. They'll be re-enabled when
@@ -748,6 +751,7 @@ clear_dr7:
__asm__("movl %0,%%db7"
: /* no output */
: "r" (0));
+ notify_die(DIE_DEBUG, "debug", regs, condition, error_code, SIGTRAP);
return;

debug_vm86:
@@ -1005,6 +1009,12 @@ static void __init set_task_gate(unsigne
_set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
}

+/* Some traps need to be set early. */
+void __init early_trap_init(void) {
+ set_intr_gate(1,&debug);
+ set_system_intr_gate(3, &int3); /* int3 can be called from all */
+ set_intr_gate(14,&page_fault);
+}

void __init trap_init(void)
{
@@ -1019,10 +1029,8 @@ void __init trap_init(void)
#endif

set_trap_gate(0,&divide_error);
- set_intr_gate(1,&debug);
set_intr_gate(2,&nmi);
- set_system_intr_gate(3, &int3); /* int3-5 can be called from all */
- set_system_gate(4,&overflow);
+ set_system_gate(4,&overflow); /* int4/5 can be called from all */
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
@@ -1032,7 +1040,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~i386-lite arch/i386/mm/fault.c
--- linux-2.6.10-rc1/arch/i386/mm/fault.c~i386-lite 2004-10-29 11:26:44.473446017 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/mm/fault.c 2004-10-29 11:26:44.486442965 -0700
@@ -2,6 +2,11 @@
* linux/arch/i386/mm/fault.c
*
* Copyright (C) 1995 Linus Torvalds
+ *
+ * Change History
+ *
+ * Tigran Aivazian <[email protected]> Remote debugging support.
+ *
*/

#include <linux/signal.h>
@@ -21,6 +26,7 @@
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <linux/highmem.h>
#include <linux/module.h>
+#include <linux/debugger.h>

#include <asm/system.h>
#include <asm/uaccess.h>
@@ -426,6 +432,10 @@ no_context:
if (is_prefetch(regs, address, error_code))
return;

+ if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
+ SIGSEGV) == NOTIFY_BAD)
+ return;
+
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
diff -puN /dev/null include/asm-i386/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-i386/kgdb.h 2004-10-29 11:26:44.487442730 -0700
@@ -0,0 +1,48 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/ptrace.h>
+
+/************************************************************************/
+/* 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
+/*
+ * 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 CHECK_EXCEPTION_STACK() 1
+
+#endif /* _ASM_KGDB_H_ */
diff -puN include/asm-i386/system.h~i386-lite include/asm-i386/system.h
--- linux-2.6.10-rc1/include/asm-i386/system.h~i386-lite 2004-10-29 11:26:44.476445313 -0700
+++ linux-2.6.10-rc1-trini/include/asm-i386/system.h 2004-10-29 11:26:44.487442730 -0700
@@ -12,9 +12,13 @@
struct task_struct; /* one of the stranger aspects of C forward declarations.. */
extern struct task_struct * FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next));

+/* sleeping_thread_to_gdb_regs depends on this code. Correct it if you change
+ * any of the following */
#define switch_to(prev,next,last) do { \
unsigned long esi,edi; \
- asm volatile("pushfl\n\t" \
+ asm volatile(".globl __switch_to_begin\n" \
+ "__switch_to_begin:" \
+ "pushfl\n\t" \
"pushl %%ebp\n\t" \
"movl %%esp,%0\n\t" /* save ESP */ \
"movl %5,%%esp\n\t" /* restore ESP */ \
@@ -23,7 +27,9 @@ extern struct task_struct * FASTCALL(__s
"jmp __switch_to\n" \
"1:\t" \
"popl %%ebp\n\t" \
- "popfl" \
+ "popfl\n" \
+ ".globl __switch_to_end\n" \
+ "__switch_to_end:\n" \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), \
"=a" (last),"=S" (esi),"=D" (edi) \
:"m" (next->thread.esp),"m" (next->thread.eip), \
diff -puN lib/Kconfig.debug~i386-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~i386-lite 2004-10-29 11:26:44.477445078 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:58.640565630 -0700
@@ -115,7 +115,7 @@ endif

config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL
+ depends on DEBUG_KERNEL && (X86)
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_

2004-10-29 19:02:16

by Tom Rini

[permalink] [raw]
Subject: [patch 3/8] KGDB support for ppc32


Cc: Matt Porter <[email protected]>
This adds KGDB support for ppc32 and was done by myself. Note that this
currently doesn't work on 40x || BOOKE, but that problem is more generic (the
current ppc stub doesn't work either) and Matt Porter is close to having a
tested solution now.

---

linux-2.6.10-rc1-trini/arch/ppc/Kconfig.debug | 36
linux-2.6.10-rc1-trini/arch/ppc/kernel/Makefile | 2
linux-2.6.10-rc1-trini/arch/ppc/kernel/irq.c | 5
linux-2.6.10-rc1-trini/arch/ppc/kernel/kgdb.c | 276 +++++
linux-2.6.10-rc1-trini/arch/ppc/kernel/setup.c | 16
linux-2.6.10-rc1-trini/arch/ppc/platforms/sandpoint.c | 11
linux-2.6.10-rc1-trini/arch/ppc/syslib/Makefile | 2
linux-2.6.10-rc1-trini/arch/ppc/syslib/ppc4xx_serial.c | 5
linux-2.6.10-rc1-trini/include/asm-ppc/kgdb.h | 20
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
linux-2.6.10-rc1/arch/ppc/kernel/ppc-stub.c | 879 -----------------
linux-2.6.10-rc1/arch/ppc/syslib/gen550_kgdb.c | 86 -
linux-2.6.10-rc1/arch/ppc/syslib/ppc4xx_kgdb.c | 124 --
13 files changed, 295 insertions(+), 1169 deletions(-)

diff -puN arch/ppc/Kconfig.debug~ppc-lite arch/ppc/Kconfig.debug
--- linux-2.6.10-rc1/arch/ppc/Kconfig.debug~ppc-lite 2004-10-29 11:26:44.705391556 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/Kconfig.debug 2004-10-29 11:26:44.729385922 -0700
@@ -2,42 +2,6 @@ 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 <http://kgdb.sourceforge.net/> 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
diff -puN arch/ppc/kernel/Makefile~ppc-lite arch/ppc/kernel/Makefile
--- linux-2.6.10-rc1/arch/ppc/kernel/Makefile~ppc-lite 2004-10-29 11:26:44.706391321 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/Makefile 2004-10-29 11:26:44.729385922 -0700
@@ -20,7 +20,7 @@ obj-$(CONFIG_POWER4) += cpu_setup_power
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-mapping.o
obj-$(CONFIG_PCI) += pci.o
-obj-$(CONFIG_KGDB) += ppc-stub.o
+obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_SMP) += smp.o smp-tbsync.o
obj-$(CONFIG_TAU) += temp.o
obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o
diff -puN arch/ppc/kernel/irq.c~ppc-lite arch/ppc/kernel/irq.c
--- linux-2.6.10-rc1/arch/ppc/kernel/irq.c~ppc-lite 2004-10-29 11:26:44.708390852 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/irq.c 2004-10-29 11:26:44.730385688 -0700
@@ -48,6 +48,7 @@
#include <linux/cpumask.h>
#include <linux/profile.h>
#include <linux/bitops.h>
+#include <linux/kgdb.h>

#include <asm/uaccess.h>
#include <asm/system.h>
@@ -155,7 +156,9 @@ void do_IRQ(struct pt_regs *regs)
if (irq != -2 && first)
/* That's not SMP safe ... but who cares ? */
ppc_spurious_interrupts++;
- irq_exit();
+ irq_exit();
+
+ kgdb_process_breakpoint();
}

void __init init_IRQ(void)
diff -puN /dev/null arch/ppc/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/kgdb.c 2004-10-29 11:26:44.730385688 -0700
@@ -0,0 +1,276 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * 1998 (c) Michael AK Tesch ([email protected])
+ * Copyright (C) 2003 Timesys Corporation.
+ * KGDB for the PowerPC processor
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <linux/kgdb.h>
+#include <asm/current.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+
+/* 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)
+ { 0x0100, 0x02 /* SIGINT */ }, /* critical input interrupt */
+ { 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */
+ { 0x0300, 0x0b /* SIGSEGV */ }, /* data storage */
+ { 0x0400, 0x0a /* SIGBUS */ }, /* instruction storage */
+ { 0x0500, 0x02 /* SIGINT */ }, /* interrupt */
+ { 0x0600, 0x0a /* SIGBUS */ }, /* alignment */
+ { 0x0700, 0x04 /* SIGILL */ }, /* program */
+ { 0x0800, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0900, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0a00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0b00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0c00, 0x14 /* SIGCHLD */ }, /* syscall */
+ { 0x0d00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0e00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0f00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x2000, 0x05 /* SIGTRAP */}, /* debug */
+#else
+ { 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */
+ { 0x0300, 0x0b /* SIGSEGV */ }, /* address error (store) */
+ { 0x0400, 0x0a /* SIGBUS */ }, /* instruction bus error */
+ { 0x0500, 0x02 /* SIGINT */ }, /* interrupt */
+ { 0x0600, 0x0a /* SIGBUS */ }, /* alingment */
+ { 0x0700, 0x05 /* SIGTRAP */ }, /* breakpoint trap */
+ { 0x0800, 0x08 /* SIGFPE */}, /* fpu unavail */
+ { 0x0900, 0x0e /* SIGALRM */ }, /* decrementer */
+ { 0x0a00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0b00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0c00, 0x14 /* SIGCHLD */ }, /* syscall */
+ { 0x0d00, 0x05 /* SIGTRAP */ }, /* single-step/watch */
+ { 0x0e00, 0x08 /* SIGFPE */ }, /* fp assist */
+#endif
+ { 0x0000, 0x000 } /* 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 */
+}
+
+/*
+ * Routines
+ */
+static void kgdb_debugger (struct pt_regs *regs)
+{
+ (*linux_debug_hook) (0, computeSignal(regs->trap), 0, regs);
+}
+
+static int kgdb_breakpoint (struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, SIGTRAP, 0, regs);
+
+ if (atomic_read (&kgdb_setting_breakpoint))
+ regs->nip += 4;
+
+ return 1;
+}
+
+static int kgdb_singlestep (struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, SIGTRAP, 0, regs);
+ return 1;
+}
+
+int kgdb_iabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, computeSignal(regs->trap), 0, regs);
+ return 1;
+}
+
+int kgdb_dabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, computeSignal(regs->trap), 0, regs);
+ return 1;
+}
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ memset(gdb_regs, 0, MAXREG*4);
+
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = regs->gpr[reg];
+
+ for (reg = 0; reg < 64; reg++)
+ *(ptr++) = 0;
+
+ *(ptr++) = regs->nip;
+ *(ptr++) = regs->msr;
+ *(ptr++) = regs->ccr;
+ *(ptr++) = regs->link;
+ *(ptr++) = regs->ctr;
+ *(ptr++) = regs->xer;
+}
+
+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);
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ 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 */
+ for (reg = 3; reg < 14; reg++)
+ *(ptr++) = 0;
+
+ /* Regs GPR14-31 */
+ for (reg = 14; reg < 32; reg++)
+ *(ptr++) = regs->gpr[reg];
+
+ for (reg = 0; reg < 64; reg++)
+ *(ptr++) = 0;
+
+ *(ptr++) = regs->nip;
+ *(ptr++) = regs->msr;
+ *(ptr++) = regs->ccr;
+ *(ptr++) = regs->link;
+ *(ptr++) = regs->ctr;
+ *(ptr++) = regs->xer;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ regs->gpr[reg] = *(ptr++);
+
+ for (reg = 0; reg < 64; reg++)
+ ptr++;
+
+ regs->nip = *(ptr++);
+ regs->msr = *(ptr++);
+ regs->ccr = *(ptr++);
+ regs->link = *(ptr++);
+ regs->ctr = *(ptr++);
+ regs->xer = *(ptr++);
+}
+
+/*
+ * This function does PoerPC 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;
+ 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':
+ if (kgdb_contthread && kgdb_contthread != current)
+ {
+ strcpy(remcom_out_buffer, "E00");
+ break;
+ }
+
+ /* handle the optional parameter */
+ ptr = &remcom_in_buffer[1];
+ 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)
+ linux_regs->msr |= MSR_DE;
+ current->thread.dbcr0 |= (DBCR0_IDM | DBCR0_IC);
+#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 -L arch/ppc/kernel/ppc-stub.c -puN arch/ppc/kernel/ppc-stub.c~ppc-lite /dev/null
--- linux-2.6.10-rc1/arch/ppc/kernel/ppc-stub.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,879 +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 ([email protected])
- *
- * Modifications to run under Linux
- * Copyright (C) 1995 David S. Miller ([email protected])
- *
- * 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
- *
- * $<packet info>#<checksum>.
- *
- * where
- * <packet info> :: <characters representing the command or response>
- * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>>
- *
- * When a packet is received, it is first acknowledged with either '+' or '-'.
- * '+' indicates a successful transfer. '-' indicates a failed transfer.
- *
- * Example:
- *
- * Host: Reply:
- * $m0,10#2a +$00010203040506070809101112131415#42
- *
- ****************************************************************************/
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
-#include <linux/sysrq.h>
-
-#include <asm/cacheflush.h>
-#include <asm/system.h>
-#include <asm/signal.h>
-#include <asm/kgdb.h>
-#include <asm/pgtable.h>
-#include <asm/ptrace.h>
-
-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<count; i++) {
- ch = hex(*buf++) << 4;
- ch |= hex(*buf++);
- *mem++ = ch;
- }
- }
-
-
- /*
- ** Flush the data cache, invalidate the instruction cache.
- */
- flush_icache_range((int)orig_mem, (int)orig_mem + count - 1);
-
- } else {
- /* error condition */
- }
- debugger_fault_handler = NULL;
- return mem;
-}
-
-/*
- * 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;
-
- if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
- debugger_fault_handler = kgdb_fault_handler;
- while (**ptr) {
- hexValue = hex(**ptr);
- if (hexValue < 0)
- break;
-
- *intValue = (*intValue << 4) | hexValue;
- numChars ++;
-
- (*ptr)++;
- }
- } else {
- /* error condition */
- }
- debugger_fault_handler = NULL;
-
- return (numChars);
-}
-
-/* scan for the sequence $<data>#<checksum> */
-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;
-
- /* $<packet info>#<checksum>. */
- 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)
- { 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
- */
- { 0x2000, 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;
-
-#if defined(CONFIG_40x)
- *ptr++ = 'S';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
-#else
- *ptr++ = 'T';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
- *ptr++ = hexchars[PC_REGNUM >> 4];
- *ptr++ = hexchars[PC_REGNUM & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)&regs->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++ = ';';
-#endif
-
- *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 *)&regs->nip, ptr, 4);
- ptr = mem2hex((char *)&regs->msr, ptr, 4);
- ptr = mem2hex((char *)&regs->ccr, ptr, 4);
- ptr = mem2hex((char *)&regs->link, ptr, 4);
- ptr = mem2hex((char *)&regs->ctr, ptr, 4);
- ptr = mem2hex((char *)&regs->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 *)&regs->nip, 4);
- ptr = hex2mem(ptr, (char *)&regs->msr, 4);
- ptr = hex2mem(ptr, (char *)&regs->ccr, 4);
- ptr = hex2mem(ptr, (char *)&regs->link, 4);
- ptr = hex2mem(ptr, (char *)&regs->ctr, 4);
- ptr = hex2mem(ptr, (char *)&regs->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();
-#if defined(CONFIG_40x)
- strcpy(remcomOutBuffer, "OK");
- putpacket(remcomOutBuffer);
-#endif
- 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)
- regs->msr |= MSR_DE;
- regs->dbcr0 |= (DBCR0_IDM | DBCR0_IC);
- mtmsr(msr);
-#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~ppc-lite arch/ppc/kernel/setup.c
--- linux-2.6.10-rc1/arch/ppc/kernel/setup.c~ppc-lite 2004-10-29 11:26:44.713389678 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/setup.c 2004-10-29 11:26:44.733384983 -0700
@@ -40,10 +40,6 @@
#include <asm/xmon.h>
#include <asm/ocp.h>

-#if defined CONFIG_KGDB
-#include <asm/kgdb.h>
-#endif
-
extern void platform_init(unsigned long r3, unsigned long r4,
unsigned long r5, unsigned long r6, unsigned long r7);
extern void bootx_init(unsigned long r4, unsigned long phys);
@@ -699,18 +695,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/platforms/sandpoint.c~ppc-lite arch/ppc/platforms/sandpoint.c
--- linux-2.6.10-rc1/arch/ppc/platforms/sandpoint.c~ppc-lite 2004-10-29 11:26:44.715389209 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/platforms/sandpoint.c 2004-10-29 11:26:44.733384983 -0700
@@ -252,8 +252,7 @@ sandpoint_find_bridges(void)
return;
}

-#if defined(CONFIG_SERIAL_8250) && \
- (defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG))
+#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG)
static void __init
sandpoint_early_serial_map(void)
{
@@ -689,16 +688,10 @@ platform_init(unsigned long r3, unsigned
ppc_md.nvram_read_val = todc_mc146818_read_val;
ppc_md.nvram_write_val = todc_mc146818_write_val;

-#if defined(CONFIG_SERIAL_8250) && \
- (defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG))
+#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG)
sandpoint_early_serial_map();
-#ifdef CONFIG_KGDB
- ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
-#endif
-#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif
-#endif

#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
ppc_ide_md.default_irq = sandpoint_ide_default_irq;
diff -puN arch/ppc/syslib/Makefile~ppc-lite arch/ppc/syslib/Makefile
--- linux-2.6.10-rc1/arch/ppc/syslib/Makefile~ppc-lite 2004-10-29 11:26:44.716388974 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/syslib/Makefile 2004-10-29 11:26:44.734384749 -0700
@@ -25,7 +25,6 @@ obj-$(CONFIG_GEN_RTC) += todc_time.o
obj-$(CONFIG_PPC4xx_DMA) += ppc4xx_dma.o
obj-$(CONFIG_PPC4xx_EDMA) += ppc4xx_sgdma.o
ifeq ($(CONFIG_40x),y)
-obj-$(CONFIG_KGDB) += ppc4xx_kgdb.o
obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o ppc405_pci.o
endif
endif
@@ -72,7 +71,6 @@ obj-$(CONFIG_PCI_8260) += m8260_pci.o i
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
obj-$(CONFIG_BOOTX_TEXT) += btext.o
diff -L arch/ppc/syslib/gen550_kgdb.c -puN arch/ppc/syslib/gen550_kgdb.c~ppc-lite /dev/null
--- linux-2.6.10-rc1/arch/ppc/syslib/gen550_kgdb.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,86 +0,0 @@
-/*
- * arch/ppc/syslib/gen550_kgdb.c
- *
- * 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 <[email protected]>
- *
- * 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 <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#include <asm/machdep.h>
-
-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 -L arch/ppc/syslib/ppc4xx_kgdb.c -puN arch/ppc/syslib/ppc4xx_kgdb.c~ppc-lite /dev/null
--- linux-2.6.10-rc1/arch/ppc/syslib/ppc4xx_kgdb.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,124 +0,0 @@
-#include <linux/config.h>
-#include <linux/types.h>
-#include <asm/ibm4xx.h>
-#include <linux/kernel.h>
-
-
-
-#define LSR_DR 0x01 /* Data ready */
-#define LSR_OE 0x02 /* Overrun */
-#define LSR_PE 0x04 /* Parity error */
-#define LSR_FE 0x08 /* Framing error */
-#define LSR_BI 0x10 /* Break */
-#define LSR_THRE 0x20 /* Xmit holding register empty */
-#define LSR_TEMT 0x40 /* Xmitter empty */
-#define LSR_ERR 0x80 /* Error */
-
-#include <platforms/4xx/ibm_ocp.h>
-
-extern struct NS16550* COM_PORTS[];
-#ifndef NULL
-#define NULL 0x00
-#endif
-
-static volatile struct NS16550 *kgdb_debugport = NULL;
-
-volatile struct NS16550 *
-NS16550_init(int chan)
-{
- volatile struct NS16550 *com_port;
- int quot;
-#ifdef BASE_BAUD
- quot = BASE_BAUD / 9600;
-#else
- quot = 0x000c; /* 0xc = 9600 baud (on a pc) */
-#endif
-
- com_port = (struct NS16550 *) COM_PORTS[chan];
-
- com_port->lcr = 0x00;
- com_port->ier = 0xFF;
- com_port->ier = 0x00;
- com_port->lcr = com_port->lcr | 0x80; /* Access baud rate */
- com_port->dll = ( quot & 0x00ff ); /* 0xc = 9600 baud */
- com_port->dlm = ( quot & 0xff00 ) >> 8;
- com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */
- com_port->mcr = 0x00; /* RTS/DTR */
- com_port->fcr = 0x07; /* Clear & enable FIFOs */
-
- return( com_port );
-}
-
-
-void
-NS16550_putc(volatile struct NS16550 *com_port, unsigned char c)
-{
- while ((com_port->lsr & LSR_THRE) == 0)
- ;
- com_port->thr = c;
- return;
-}
-
-unsigned char
-NS16550_getc(volatile struct NS16550 *com_port)
-{
- while ((com_port->lsr & LSR_DR) == 0)
- ;
- return (com_port->rbr);
-}
-
-unsigned char
-NS16550_tstc(volatile struct NS16550 *com_port)
-{
- return ((com_port->lsr & LSR_DR) != 0);
-}
-
-
-#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
-
-void putDebugChar( unsigned char c )
-{
- if ( kgdb_debugport == NULL )
- kgdb_debugport = NS16550_init(KGDB_PORT);
- NS16550_putc( kgdb_debugport, c );
-}
-
-int getDebugChar( void )
-{
- if (kgdb_debugport == NULL)
- kgdb_debugport = NS16550_init(KGDB_PORT);
-
- return(NS16550_getc(kgdb_debugport));
-}
-
-void kgdb_interruptible(int enable)
-{
- return;
-}
-
-void putDebugString(char* str)
-{
- while (*str != '\0') {
- putDebugChar(*str);
- str++;
- }
- putDebugChar('\r');
- return;
-}
-
-void
-kgdb_map_scc(void)
-{
- printk("kgdb init \n");
- kgdb_debugport = NS16550_init(KGDB_PORT);
-}
diff -puN arch/ppc/syslib/ppc4xx_serial.c~ppc-lite arch/ppc/syslib/ppc4xx_serial.c
--- linux-2.6.10-rc1/arch/ppc/syslib/ppc4xx_serial.c~ppc-lite 2004-10-29 11:26:44.722387566 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/syslib/ppc4xx_serial.c 2004-10-29 11:26:44.734384749 -0700
@@ -20,11 +20,6 @@

#if defined(CONFIG_IBM405GP) || defined(CONFIG_IBM405CR)

-#ifdef CONFIG_KGDB
-#include <asm/kgdb.h>
-#include <linux/init.h>
-#endif
-
#ifdef CONFIG_DEBUG_BRINGUP

#include <linux/console.h>
diff -puN include/asm-ppc/kgdb.h~ppc-lite include/asm-ppc/kgdb.h
--- linux-2.6.10-rc1/include/asm-ppc/kgdb.h~ppc-lite 2004-10-29 11:26:44.723387331 -0700
+++ linux-2.6.10-rc1-trini/include/asm-ppc/kgdb.h 2004-10-29 11:26:44.735384514 -0700
@@ -2,6 +2,8 @@
* kgdb.h: Defines and declarations for serial line source level
* remote debugging of the Linux kernel using gdb.
*
+ * PPC Mods (C) 2004 Tom Rini ([email protected])
+ * PPC Mods (C) 2003 John Whitney ([email protected])
* PPC Mods (C) 1998 Michael Tesch ([email protected])
*
* Copyright (C) 1995 David S. Miller ([email protected])
@@ -12,6 +14,15 @@

#ifndef __ASSEMBLY__

+#define BREAK_INSTR_SIZE 4
+#define MAXREG (PT_FPSCR+1)
+#define NUMREGBYTES (MAXREG * sizeof(int))
+#define BUFMAX ((NUMREGBYTES * 2) + 512)
+#define OUTBUFMAX ((NUMREGBYTES * 2) + 512)
+#define BREAKPOINT() asm(".long 0x7d821008"); /* twge r2, r2 */
+
+#define CHECK_EXCEPTION_STACK() 1
+
/* Things specific to the gen550 backend. */
struct uart_port;

@@ -19,15 +30,6 @@ extern void gen550_progress(char *, unsi
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
*/
diff -puN lib/Kconfig.debug~ppc-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~ppc-lite 2004-10-29 11:26:44.725386861 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:57.778768052 -0700
@@ -115,7 +115,7 @@ endif

config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86)
+ depends on DEBUG_KERNEL && (X86 || ((!SMP || BROKEN) && (PPC32)))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_

2004-10-29 19:04:02

by Tom Rini

[permalink] [raw]
Subject: [patch 4/8] 8250 uart driver for KGDB


Cc: Russell King <[email protected]>
This adds an 8250 uart driver for KGDB and adds a hook into 8250.c to
unregister a port based on the IRQ, serial8250_release_irq(). We use
serial8250_unregister_port() to do the real work here, however. Note
that we can't use unregister_port in kgdb_8250.c since we don't know
what the 'port' is, in 8250 terms, only the IRQ. The other change is
that we have SERIAL_8250_NR_UARTS always be defined if KGDB_8250.

---

linux-2.6.10-rc1-trini/drivers/serial/8250.c | 39 +
linux-2.6.10-rc1-trini/drivers/serial/Kconfig | 4
linux-2.6.10-rc1-trini/drivers/serial/Makefile | 1
linux-2.6.10-rc1-trini/drivers/serial/kgdb_8250.c | 477 ++++++++++++++++++++
linux-2.6.10-rc1-trini/drivers/serial/serial_core.c | 6
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 101 ++++
6 files changed, 625 insertions(+), 3 deletions(-)

diff -puN drivers/serial/8250.c~8250 drivers/serial/8250.c
--- linux-2.6.10-rc1/drivers/serial/8250.c~8250 2004-10-29 11:26:44.964330757 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/8250.c 2004-10-29 11:26:44.977327706 -0700
@@ -1348,12 +1348,17 @@ static void serial8250_break_ctl(struct
spin_unlock_irqrestore(&up->port.lock, flags);
}

+static int released_irq = -1;
+
static int serial8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
int retval;

+ if (up->port.irq == released_irq)
+ return -EBUSY;
+
up->capabilities = uart_config[up->port.type].flags;
up->mcr = 0;

@@ -1990,6 +1995,9 @@ serial8250_register_ports(struct uart_dr
for (i = 0; i < UART_NR; i++) {
struct uart_8250_port *up = &serial8250_ports[i];

+ if (up->port.irq == released_irq)
+ continue;
+
up->port.line = i;
up->port.ops = &serial8250_pops;
up->port.dev = dev;
@@ -2327,6 +2335,9 @@ int serial8250_register_port(struct uart
struct uart_8250_port *uart;
int ret = -ENOSPC;

+ if (port->line == released_irq)
+ return -EBUSY;
+
down(&serial_sem);

uart = serial8250_find_match_or_unused(port);
@@ -2376,6 +2387,34 @@ void serial8250_unregister_port(int line
}
EXPORT_SYMBOL(serial8250_unregister_port);

+/**
+ * serial8250_release_irq - remove a 16x50 serial port at runtime based
+ * on IRQ
+ * @irq: IRQ number
+ *
+ * Remove one serial port. This may not be called from interrupt
+ * context. This is called when the caller know the IRQ of the
+ * port they want, but not where it is in our table. This allows
+ * for one port, based on IRQ, to be permanently released from us.
+ */
+
+int serial8250_release_irq(int irq)
+{
+ struct uart_8250_port *up;
+ int ttyS;
+
+ if (released_irq != -1)
+ return -EBUSY;
+
+ released_irq = irq;
+ for (ttyS = 0; ttyS < UART_NR; ttyS++){
+ up = &serial8250_ports[ttyS];
+ if (up->port.irq == irq && (irq_lists + irq)->head)
+ serial8250_unregister_port(up->port.line);
+ }
+ return 0;
+}
+
static int __init serial8250_init(void)
{
int ret, i;
diff -puN drivers/serial/Kconfig~8250 drivers/serial/Kconfig
--- linux-2.6.10-rc1/drivers/serial/Kconfig~8250 2004-10-29 11:26:44.966330288 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/Kconfig 2004-10-29 11:26:44.978327471 -0700
@@ -86,8 +86,8 @@ config SERIAL_8250_ACPI
namespace, say Y here. If unsure, say N.

config SERIAL_8250_NR_UARTS
- int "Maximum number of non-legacy 8250/16550 serial ports"
- depends on SERIAL_8250
+ int "Maximum number of non-legacy 8250/16550 serial ports" if SERIAL_8250
+ depends on SERIAL_8250 || KGDB_8250
default "4"
---help---
Set this to the number of non-legacy serial ports you want
diff -puN drivers/serial/Makefile~8250 drivers/serial/Makefile
--- linux-2.6.10-rc1/drivers/serial/Makefile~8250 2004-10-29 11:26:44.967330053 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/Makefile 2004-10-29 11:26:44.978327471 -0700
@@ -42,3 +42,4 @@ obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += s
obj-$(CONFIG_SERIAL_CPM) += cpm_uart/
obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
+obj-$(CONFIG_KGDB_8250) += kgdb_8250.o
diff -puN /dev/null drivers/serial/kgdb_8250.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/kgdb_8250.c 2004-10-29 11:26:44.979327236 -0700
@@ -0,0 +1,477 @@
+/*
+ * 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:
+ *
+ * Amit Kale <[email protected]>, David Grothe <[email protected]>,
+ * Scott Foehner <[email protected]>, George Anzinger <[email protected]>,
+ * Robert Walsh <[email protected]>, wangdi <[email protected]>,
+ * San Mehat, Tom Rini <[email protected]>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/serialP.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/serial.h> /* For BASE_BAUD and SERIAL_PORT_DFNS */
+
+#define GDB_BUF_SIZE 512 /* power of 2, please */
+
+/* Speed of the UART. */
+#if defined(CONFIG_KGDB_9600BAUD)
+static int kgdb8250_baud = 9600;
+#elif defined(CONFIG_KGDB_19200BAUD)
+static int kgdb8250_baud = 19200;
+#elif defined(CONFIG_KGDB_38400BAUD)
+static int kgdb8250_baud = 38400;
+#elif defined(CONFIG_KGDB_57600BAUD)
+static int kgdb8250_baud = 57600;
+#else
+static int kgdb8250_baud = 115200; /* Start with this if not given */
+#endif
+
+/* Index of the UART, matches ttySX naming. */
+#if defined(CONFIG_KGDB_TTYS1)
+static int kgdb8250_ttyS = 1;
+#elif defined(CONFIG_KGDB_TTYS2)
+static int kgdb8250_ttyS = 2;
+#elif defined(CONFIG_KGDB_TTYS3)
+static int kgdb8250_ttyS = 3;
+#else
+static int kgdb8250_ttyS = 0; /* Start with this if not given */
+#endif
+
+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. */
+static int old_rs_table_copied;
+static struct serial_state old_rs_table[] = {
+#ifdef SERIAL_PORT_DFNS
+ SERIAL_PORT_DFNS
+#endif
+};
+
+/* Our internal table of UARTS. */
+#define UART_NR (ARRAY_SIZE(old_rs_table) + CONFIG_SERIAL_8250_NR_UARTS)
+static struct uart_port kgdb8250_ports[UART_NR] = {
+#ifndef CONFIG_KGDB_SIMPLE_SERIAL
+ {
+#if defined(CONFIG_KGDB_IRQ) && defined(CONFIG_KGDB_PORT)
+ .irq = CONFIG_KGDB_IRQ,
+ .iobase = CONFIG_KGDB_PORT,
+ .iotype = UPIO_PORT,
+#endif
+#ifdef CONFIG_KGDB_IOMEMBASE
+ .membase = (unsigned char *)CONFIG_KGDB_IOMEMBASE,
+ .iotype = UPIO_MEM,
+#endif
+ },
+#endif
+};
+
+/* Macros to easily get what we want from kgdb8250_ports[kgdb8250_ttyS] */
+#define CURRENTPORT kgdb8250_ports[kgdb8250_ttyS]
+#define KGDB8250_IRQ CURRENTPORT.irq
+#define KGDB8250_REG_SHIFT CURRENTPORT.regshift
+
+/* Base of the UART. */
+static unsigned long kgdb8250_addr;
+/* Pointers for I/O. */
+static void (*serial_outb) (unsigned char val, unsigned long addr);
+static unsigned long (*serial_inb) (unsigned long addr);
+
+/* Forward declarations. */
+static int kgdb8250_init(void);
+extern int serial8250_release_irq(int irq);
+
+extern atomic_t debugger_active;
+
+static unsigned long
+direct_inb(unsigned long addr)
+{
+ return readb((void *)addr);
+}
+
+static void
+direct_outb(unsigned char val, unsigned long addr)
+{
+ writeb(val, (void *)addr);
+}
+
+static unsigned long
+io_inb(unsigned long port)
+{
+ return inb(port);
+}
+
+static void
+io_outb(unsigned char val, unsigned long port)
+{
+ outb(val, port);
+}
+
+/*
+ * Wait until the interface can accept a char, then write it.
+ */
+static void
+kgdb_put_debug_char(int chr)
+{
+ while (!(serial_inb(kgdb8250_addr + (UART_LSR << KGDB8250_REG_SHIFT)) &
+ UART_LSR_THRE))
+ ;
+
+ serial_outb(chr, kgdb8250_addr + (UART_TX << KGDB8250_REG_SHIFT));
+}
+
+/*
+ * Get a byte from the hardware data buffer and return it
+ */
+static int
+read_data_bfr(void)
+{
+ char it = serial_inb(kgdb8250_addr + (UART_LSR << KGDB8250_REG_SHIFT));
+
+ if (it & UART_LSR_DR)
+ return serial_inb(kgdb8250_addr +
+ (UART_RX << KGDB8250_REG_SHIFT));
+
+ /*
+ * If we have a framing error assume somebody messed with
+ * our uart. Reprogram it and send '-' both ways...
+ */
+ if (it & 0xc) {
+ kgdb8250_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;
+ unsigned long flags;
+ local_irq_save(flags);
+
+ /* 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);
+ goto out;
+ }
+
+ do {
+ retchr = read_data_bfr();
+ } while (retchr < 0);
+
+out:
+ local_irq_restore(flags);
+
+ 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, struct pt_regs *regs)
+{
+ char iir;
+
+ if (irq != KGDB8250_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;
+
+ iir = serial_inb(kgdb8250_addr + (UART_IIR << KGDB8250_REG_SHIFT));
+ if (iir & UART_IIR_RDI)
+ kgdb_schedule_breakpoint();
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Returns:
+ * 0 on success, 1 on failure.
+ */
+static int
+kgdb8250_init(void)
+{
+ unsigned cval;
+ int bits = 8;
+ int parity = 'n';
+ int cflag = CREAD | HUPCL | CLOCAL;
+ char ier = UART_IER_RDI;
+
+ /*
+ * Now construct a cflag setting.
+ */
+ switch (kgdb8250_baud) {
+ case 1200:
+ cflag |= B1200;
+ break;
+ case 2400:
+ cflag |= B2400;
+ break;
+ case 4800:
+ cflag |= B4800;
+ break;
+ case 19200:
+ cflag |= B19200;
+ break;
+ case 38400:
+ cflag |= B38400;
+ break;
+ case 57600:
+ cflag |= B57600;
+ break;
+ case 115200:
+ cflag |= B115200;
+ break;
+ default:
+ kgdb8250_baud = 9600;
+ /* Fall through */
+ case 9600:
+ cflag |= B9600;
+ break;
+ }
+ switch (bits) {
+ case 7:
+ cflag |= CS7;
+ break;
+ default:
+ case 8:
+ cflag |= CS8;
+ break;
+ }
+ switch (parity) {
+ case 'o':
+ case 'O':
+ cflag |= PARODD;
+ break;
+ case 'e':
+ case 'E':
+ cflag |= PARENB;
+ break;
+ }
+
+ /*
+ * Divisor, bytesize and parity
+ *
+ */
+
+ cval = cflag & (CSIZE | CSTOPB);
+ cval >>= 4;
+ if (cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ /* Disable UART interrupts, set DTR and RTS high and set speed. */
+ cval = 0x3;
+ /* set DLAB */
+ serial_outb(cval | UART_LCR_DLAB, kgdb8250_addr +
+ (UART_LCR << KGDB8250_REG_SHIFT));
+ /* LS */
+ serial_outb(BASE_BAUD / kgdb8250_baud & 0xff, kgdb8250_addr +
+ (UART_DLL << KGDB8250_REG_SHIFT));
+ /* MS */
+ serial_outb(BASE_BAUD / kgdb8250_baud >> 8, kgdb8250_addr +
+ (UART_DLM << KGDB8250_REG_SHIFT));
+ /* reset DLAB */
+ serial_outb(cval, kgdb8250_addr + (UART_LCR << KGDB8250_REG_SHIFT));
+
+ /*
+ * XScale-specific bits that need to be set
+ */
+ if (CURRENTPORT.type == PORT_XSCALE)
+ ier |= UART_IER_UUE | UART_IER_RTOIE;
+
+ /* turn on interrupts */
+ serial_outb(ier, kgdb8250_addr + (UART_IER << KGDB8250_REG_SHIFT));
+ serial_outb(UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS,
+ kgdb8250_addr + (UART_MCR << KGDB8250_REG_SHIFT));
+
+ /*
+ * If we read 0xff from the LSR, there is no UART here.
+ */
+ if (serial_inb(kgdb8250_addr + (UART_LSR << KGDB8250_REG_SHIFT)) ==
+ 0xff)
+ return -1;
+ 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
+kgdb8250_copy_rs_table(void)
+{
+#ifdef CONFIG_KGDB_SIMPLE_SERIAL
+ int i;
+
+ 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;
+ }
+#endif
+
+ old_rs_table_copied = 1;
+}
+
+/*
+ * Perform static initalization tasks which we need to always do,
+ * even if KGDB isn't going to be invoked immediately.
+ */
+static int
+kgdb8250_local_init(void)
+{
+ if (old_rs_table_copied == 0)
+ kgdb8250_copy_rs_table();
+
+ switch (CURRENTPORT.iotype) {
+ case UPIO_MEM:
+ if (CURRENTPORT.mapbase) {
+ if (!request_mem_region(CURRENTPORT.mapbase,
+ 8 << KGDB8250_REG_SHIFT,
+ "kgdb"))
+ return 1; /* Failed. */
+ }
+ if (CURRENTPORT.flags & UPF_IOREMAP) {
+ CURRENTPORT.membase = ioremap(CURRENTPORT.mapbase,
+ 8 << KGDB8250_REG_SHIFT);
+ if (!CURRENTPORT.membase)
+ return 1; /* Failed. */
+ }
+ kgdb8250_addr = (unsigned long)CURRENTPORT.membase;
+ serial_outb = direct_outb;
+ serial_inb = direct_inb;
+ break;
+ case UPIO_PORT:
+ default:
+ kgdb8250_addr = CURRENTPORT.iobase;
+ serial_outb = io_outb;
+ serial_inb = io_inb;
+ }
+
+ return 0;
+}
+
+static int
+kgdb_init_io(void)
+{
+ if (kgdb8250_local_init())
+ return -1;
+
+#ifdef CONFIG_SERIAL_8250
+ if (serial8250_release_irq(KGDB8250_IRQ))
+ return -1;
+#endif
+
+ if (kgdb8250_init() == -1)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Hookup our IRQ line. We will already have been initialized at
+ * this point.
+ */
+static void __init kgdb8250_hookup_irq(void)
+{
+ request_irq(KGDB8250_IRQ, kgdb8250_interrupt, SA_SHIRQ,
+ "GDB-stub", &CURRENTPORT);
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_get_debug_char,
+ .write_char = kgdb_put_debug_char,
+ .init = kgdb_init_io,
+ .late_init = kgdb8250_hookup_irq,
+};
+
+void
+kgdb8250_add_port(int i, struct uart_port *serial_req)
+{
+ /* Copy the old table in if needed. */
+ if (old_rs_table_copied == 0)
+ kgdb8250_copy_rs_table();
+
+ /* Copy the whole thing over. */
+ memcpy(&kgdb8250_ports[i], serial_req, sizeof(struct uart_port));
+}
+
+/*
+ * Syntax for this cmdline option is "kgdb8250=ttyno,baudrate"
+ * with ",irq,iomembase" tacked on the end on IA64.
+ */
+static int __init
+kgdb8250_opt(char *str)
+{
+ if (*str < '0' || *str > '3')
+ goto errout;
+ kgdb8250_ttyS = *str - '0';
+ str++;
+ if (*str != ',')
+ goto errout;
+ str++;
+ kgdb8250_baud = simple_strtoul(str, &str, 10);
+ if (kgdb8250_baud != 9600 && kgdb8250_baud != 19200 &&
+ kgdb8250_baud != 38400 && kgdb8250_baud != 57600 &&
+ kgdb8250_baud != 115200)
+ goto errout;
+
+#ifdef CONFIG_IA64
+ if (*str == ',') {
+ str++;
+ KGDB8250_IRQ = simple_strtoul(str, &str, 10);
+ if (*str == ',') {
+ str++;
+ CURRENTPORT.iotype = SERIAL_IO_MEM;
+ CURRENTPORT.membase =
+ (unsigned char *)simple_strtoul(str, &str, 0);
+ }
+ }
+#endif
+
+ return 0;
+
+errout:
+ printk(KERN_ERR "Invalid syntax for option kgdb8250=\n");
+ return 1;
+}
+early_param("kgdb8250", kgdb8250_opt);
diff -puN drivers/serial/serial_core.c~8250 drivers/serial/serial_core.c
--- linux-2.6.10-rc1/drivers/serial/serial_core.c~8250 2004-10-29 11:26:44.970329349 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/serial_core.c 2004-10-29 11:26:44.981326767 -0700
@@ -903,7 +903,11 @@ uart_tiocmset(struct tty_struct *tty, st
static void uart_break_ctl(struct tty_struct *tty, int break_state)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->port;
+ struct uart_port *port;
+
+ if (!state)
+ return;
+ port = state->port;

BUG_ON(!kernel_locked());

diff -puN lib/Kconfig.debug~8250 lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~8250 2004-10-29 11:26:44.972328879 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:57.089929849 -0700
@@ -124,3 +124,104 @@ config KGDB
Documentation of kernel debugger available at
http://kgdb.sourceforge.net
This is only useful for kernel hackers. If unsure, say N.
+
+choice
+ prompt "Method for KGDB communication"
+ depends on KGDB
+ default KGDB_8250
+ help
+ There are a number of different ways in which you can communicate
+ with KGDB. The oldest is using a serial driver. A newer method
+ is to use UDP packets and a special network driver.
+
+config KGDB_8250
+ bool "KGDB: On generic serial port (8250)"
+ help
+ Uses generic serial port (8250) for kgdb. This is independent of the
+ option 9250/16550 and compatible serial port.
+
+endchoice
+
+config KGDB_SIMPLE_SERIAL
+ bool "Simple selection of KGDB serial port"
+ depends on KGDB_8250
+ help
+ If you say Y here, you will only have to pick the baud rate
+ and serial port (ttyS) that you wish to use for KGDB. If you
+ say N, you will have provide the I/O port and IRQ number. Note
+ that if your serial ports are iomapped, then you must say Y here.
+ If in doubt, say Y.
+
+choice
+ depends on KGDB_8250
+ prompt "Debug serial port BAUD"
+ default KGDB_115200BAUD
+ help
+ Gdb and the kernel stub need to agree on the baud rate to be
+ used. Some systems (x86 family at this writing) allow this to
+ be configured.
+
+config KGDB_9600BAUD
+ bool "9600"
+
+config KGDB_19200BAUD
+ bool "19200"
+
+config KGDB_38400BAUD
+ bool "38400"
+
+config KGDB_57600BAUD
+ bool "57600"
+
+config KGDB_115200BAUD
+ bool "115200"
+endchoice
+
+choice
+ prompt "Serial port for KGDB"
+ depends on KGDB_SIMPLE_SERIAL
+ default KGDB_TTYS0
+
+config KGDB_TTYS0
+ bool "ttyS0"
+
+config KGDB_TTYS1
+ bool "ttyS1"
+
+config KGDB_TTYS2
+ bool "ttyS2"
+
+config KGDB_TTYS3
+ bool "ttyS3"
+
+endchoice
+
+config KGDB_IOMEMBASE
+ hex "hex MMIO address of the debug serial port"
+ depends on !KGDB_SIMPLE_SERIAL && IA64
+ help
+ Some systems (IA64) don't access ports through legacy I/O port
+ space. Instead an MMIO address is used to access the UART.
+ There aren't any standard addresses on most system. The value
+ is platform dependent.
+
+config KGDB_PORT
+ hex "hex I/O port address of the debug serial port"
+ depends on !KGDB_SIMPLE_SERIAL && KGDB_8250 && !IA64
+ default 3f8
+ help
+ Some systems (x86 family at this writing) allow the port
+ address to be configured. The number entered is assumed to be
+ hex, don't put 0x in front of it. The standard address are:
+ COM1 3f8 , irq 4 and COM2 2f8 irq 3. Setserial /dev/ttySx
+ will tell you what you have. It is good to test the serial
+ connection with a live system before trying to debug.
+
+config KGDB_IRQ
+ int "IRQ of the debug serial port"
+ depends on !KGDB_SIMPLE_SERIAL && KGDB_8250 && !IA64
+ default 4
+ help
+ This is the irq for the debug port. If everything is working
+ correctly and the kernel has interrupts on a control C to the
+ port should cause a break into the kernel debug stub.
_

2004-10-29 19:14:27

by Tom Rini

[permalink] [raw]
Subject: [patch 5/8] KGDB support for MIPS


Cc: Ralf Baechle <[email protected]>, Manish Lachwani <[email protected]>
This adds KGDB support for MIPS and was done by Manish Lachwani. This has
been tested on MIPS Malta and Sibyte 1250 SWARM (32 & 64).

---

linux-2.6.10-rc1-trini/arch/mips/Kconfig.debug | 19
linux-2.6.10-rc1-trini/arch/mips/kernel/Makefile | 2
linux-2.6.10-rc1-trini/arch/mips/kernel/irq.c | 3
linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb.c | 292 ++
linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb_handler.S | 58
linux-2.6.10-rc1-trini/arch/mips/kernel/traps.c | 29
linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/Makefile | 1
linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/init.c | 64
linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_int.c | 25
linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_setup.c | 8
linux-2.6.10-rc1-trini/arch/mips/sibyte/cfe/setup.c | 14
linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/Makefile | 1
linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/irq.c | 72
linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/kgdb_sibyte.c | 158 +
linux-2.6.10-rc1-trini/arch/mips/sibyte/swarm/Makefile | 2
linux-2.6.10-rc1-trini/include/asm-mips/kdebug.h | 47
linux-2.6.10-rc1-trini/include/asm-mips/kgdb.h | 20
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 9
linux-2.6.10-rc1/arch/mips/kernel/gdb-low.S | 370 ---
linux-2.6.10-rc1/arch/mips/kernel/gdb-stub.c | 1089 ----------
linux-2.6.10-rc1/arch/mips/sibyte/swarm/dbg_io.c | 76
21 files changed, 633 insertions(+), 1726 deletions(-)

diff -puN arch/mips/Kconfig.debug~mips-lite arch/mips/Kconfig.debug
--- linux-2.6.10-rc1/arch/mips/Kconfig.debug~mips-lite 2004-10-29 11:26:45.172281931 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/Kconfig.debug 2004-10-29 11:26:45.204274419 -0700
@@ -27,25 +27,6 @@ config DEBUG_STACK_USAGE

This option will slow down process creation somewhat.

-config KGDB
- bool "Remote GDB kernel debugging"
- depends on DEBUG_KERNEL
- select DEBUG_INFO
- help
- If you say Y here, it will be possible to remotely debug the MIPS
- kernel using gdb. This enlarges your kernel image disk size by
- several megabytes and requires a machine with more than 16 MB,
- better 32 MB RAM to avoid excessive linking time. This is only
- useful for kernel hackers. If unsure, say N.
-
-config GDB_CONSOLE
- bool "Console output to GDB"
- depends on KGDB
- help
- If you are using GDB for remote debugging over a serial port and
- would like kernel messages to be formatted into GDB $O packets so
- that GDB prints them as program output, say 'Y'.
-
config SB1XXX_CORELIS
bool "Corelis Debugger"
depends on SIBYTE_SB1xxx_SOC
diff -puN arch/mips/kernel/Makefile~mips-lite arch/mips/kernel/Makefile
--- linux-2.6.10-rc1/arch/mips/kernel/Makefile~mips-lite 2004-10-29 11:26:45.173281696 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/Makefile 2004-10-29 11:26:45.204274419 -0700
@@ -48,7 +48,7 @@ obj-$(CONFIG_MIPS32_COMPAT) += ioctl32.o
obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o
obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o ptrace32.o

-obj-$(CONFIG_KGDB) += gdb-low.o gdb-stub.o
+obj-$(CONFIG_KGDB) += kgdb_handler.o kgdb.o
obj-$(CONFIG_PROC_FS) += proc.o

obj-$(CONFIG_MIPS64) += cpu-bugs64.o
diff -L arch/mips/kernel/gdb-low.S -puN arch/mips/kernel/gdb-low.S~mips-lite /dev/null
--- linux-2.6.10-rc1/arch/mips/kernel/gdb-low.S
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,370 +0,0 @@
-/*
- * gdb-low.S contains the low-level trap handler for the GDB stub.
- *
- * Copyright (C) 1995 Andreas Busse
- */
-#include <linux/config.h>
-#include <linux/sys.h>
-
-#include <asm/asm.h>
-#include <asm/errno.h>
-#include <asm/mipsregs.h>
-#include <asm/regdef.h>
-#include <asm/stackframe.h>
-#include <asm/gdb-stub.h>
-
-#ifdef CONFIG_MIPS32
-#define DMFC0 mfc0
-#define DMTC0 mtc0
-#define LDC1 lwc1
-#define SDC1 lwc1
-#endif
-#ifdef CONFIG_MIPS64
-#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.
- */
- lui k1, %hi(saved_vectors)
- mfc0 k0, CP0_CAUSE
- andi k0, k0, 0x7c
- add k1, k1, k0
- lw k0, %lo(saved_vectors)(k1)
- jr k0
- nop
-1:
- move k0, sp
- 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,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:
- 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 -L arch/mips/kernel/gdb-stub.c -puN arch/mips/kernel/gdb-stub.c~mips-lite /dev/null
--- linux-2.6.10-rc1/arch/mips/kernel/gdb-stub.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,1089 +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 <[email protected]>
- *
- * Copyright (C) 1995 Andreas Busse
- *
- * Copyright (C) 2003 MontaVista Software Inc.
- * Author: Jun Sun, [email protected] or [email protected]
- */
-
-/*
- * 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
- *
- * $<packet info>#<checksum>.
- *
- * where
- * <packet info> :: <characters representing the command or response>
- * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
- *
- * When a packet is received, it is first acknowledged with either '+' or '-'.
- * '+' indicates a successful transfer. '-' indicates a failed transfer.
- *
- * Example:
- *
- * Host: Reply:
- * $m0,10#2a +$00010203040506070809101112131415#42
- *
- *
- * ==============
- * 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 <go to "Kernel Hacking" and turn on remote debugging>
- * $ 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 <linux/config.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/reboot.h>
-
-#include <asm/asm.h>
-#include <asm/cacheflush.h>
-#include <asm/mipsregs.h>
-#include <asm/pgtable.h>
-#include <asm/system.h>
-#include <asm/gdb-stub.h>
-#include <asm/inst.h>
-
-/*
- * 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);
-
-/*
- * spin locks for smp case
- */
-static spinlock_t kgdb_lock = SPIN_LOCK_UNLOCKED;
-static spinlock_t kgdb_cpulock[NR_CPUS] = { [0 ... NR_CPUS-1] = 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 $<data>#<checksum>
- */
-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;
-
- /*
- * $<packet info>#<checksum>.
- */
-
- 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; i<count; i++)
- {
- if (binary) {
- ch = *buf++;
- if (ch == 0x7d)
- ch = 0x20 ^ *buf++;
- }
- else {
- ch = hex(*buf++) << 4;
- ch |= hex(*buf++);
- }
- if (kgdb_write_byte(ch, mem++) != 0)
- return 0;
- }
-
- return mem;
-}
-
-/*
- * This table contains the mapping between SPARC hardware trap types, and
- * signals, which are primarily what GDB understands. It also indicates
- * which hardware traps we need to commandeer when initializing the stub.
- */
-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 */
- { 10, SIGILL }, /* reserved instruction */
-/* { 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];
-
-/*
- * Set up exception handlers for tracing and breakpoints
- */
-void set_debug_traps(void)
-{
- struct hard_trap_info *ht;
- unsigned long flags;
- unsigned char c;
-
- 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);
-
- 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 = *(&regs->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;
-
- /* avoid deadlock if someone is make IPC */
- if (spin_is_locked(&smp_call_lock))
- return;
-
- async_bp.addr = *epc;
- *epc = (unsigned long)async_breakpoint;
-}
-
-void kgdb_wait(void *arg)
-{
- unsigned flags;
- int cpu = smp_processor_id();
-
- local_irq_save(flags);
-
- spin_lock(&kgdb_cpulock[cpu]);
- spin_unlock(&kgdb_cpulock[cpu]);
-
- local_irq_restore(flags);
-}
-
-
-/*
- * 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 (spin_trylock(&kgdb_cpulock[i]) == 0)
- panic("kgdb: couldn't get cpulock %d\n", i);
-
- /*
- * force other cpus to enter kgdb
- */
- smp_call_function(kgdb_wait, NULL, 0, 0);
-
- /*
- * 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 *)&regs->cp0_epc, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- /*
- * Send frame pointer
- */
- *ptr++ = hexchars[REG_FP >> 4];
- *ptr++ = hexchars[REG_FP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)&regs->reg30, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- /*
- * Send stack pointer
- */
- *ptr++ = hexchars[REG_SP >> 4];
- *ptr++ = hexchars[REG_SP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)&regs->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 *)&regs->reg0, ptr, 32*sizeof(long), 0); /* r0...r31 */
- ptr = mem2hex((char *)&regs->cp0_status, ptr, 6*sizeof(long), 0); /* cp0 */
- ptr = mem2hex((char *)&regs->fpr0, ptr, 32*sizeof(long), 0); /* f0...31 */
- ptr = mem2hex((char *)&regs->cp1_fsr, ptr, 2*sizeof(long), 0); /* cp1 */
- ptr = mem2hex((char *)&regs->frame_ptr, ptr, 2*sizeof(long), 0); /* frp */
- ptr = mem2hex((char *)&regs->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 *)&regs->reg0, 32*sizeof(long), 0, 0);
- ptr += 32*(2*sizeof(long));
- hex2mem(ptr, (char *)&regs->cp0_status, 6*sizeof(long), 0, 0);
- ptr += 6*(2*sizeof(long));
- hex2mem(ptr, (char *)&regs->fpr0, 32*sizeof(long), 0, 0);
- ptr += 32*(2*sizeof(long));
- hex2mem(ptr, (char *)&regs->cp1_fsr, 2*sizeof(long), 0, 0);
- ptr += 2*(2*sizeof(long));
- hex2mem(ptr, (char *)&regs->frame_ptr, 2*sizeof(long), 0, 0);
- ptr += 2*(2*sizeof(long));
- hex2mem(ptr, (char *)&regs->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--)
- 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 *malloc(size_t size)
-{
- return kmalloc(size, GFP_ATOMIC);
-}
-
-static void 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~mips-lite arch/mips/kernel/irq.c
--- linux-2.6.10-rc1/arch/mips/kernel/irq.c~mips-lite 2004-10-29 11:26:45.179280287 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/irq.c 2004-10-29 11:26:45.207273714 -0700
@@ -22,6 +22,7 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>

#include <asm/atomic.h>
#include <asm/system.h>
@@ -420,6 +421,8 @@ out:

irq_exit();

+ kgdb_process_breakpoint();
+
return 1;
}

diff -puN /dev/null arch/mips/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb.c 2004-10-29 11:26:45.208273480 -0700
@@ -0,0 +1,292 @@
+/*
+ * 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 <[email protected]>
+ *
+ * Copyright (C) 1995 Andreas Busse
+ *
+ * Copyright (C) 2003 MontaVista Software Inc.
+ * Author: Jun Sun, [email protected] or [email protected]
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, [email protected] or [email protected]
+ *
+ * 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 <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/debugger.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <asm/inst.h>
+#include <asm/gdb-stub.h>
+#include <asm/cacheflush.h>
+#include <asm/kdebug.h>
+
+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 */
+ { 10, SIGILL }, /* reserved instruction */
+/* { 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 */
+};
+
+extern atomic_t cpu_doing_single_step;
+
+/* Save the normal trap handlers for user-mode traps. */
+void *saved_vectors[32];
+extern void trap_low(void);
+extern void breakinst(void);
+
+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 (atomic_read(&debugger_active))
+ debugger_nmihook(smp_processor_id(), regs);
+
+ if (atomic_read (&kgdb_setting_breakpoint))
+ if ( (trap == 9) && (regs->cp0_epc == (unsigned long)breakinst) )
+ regs->cp0_epc += 4;
+
+ (*linux_debug_hook) (0, compute_signal(trap), 0, regs);
+
+ /* In SMP mode, __flush_cache_all does IPI */
+ __flush_cache_all();
+}
+
+void set_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low);
+
+ local_irq_restore(flags);
+}
+
+#if 0
+/* This should be called before we exit kgdb_handle_exception() I believe.
+ * -- Tom
+ */
+void restore_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ set_except_vector(ht->tt, saved_vectors[ht->tt]);
+ local_irq_restore(flags);
+}
+#endif
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg = 0;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ *(ptr++) = regs->cp0_status;
+ *(ptr++) = regs->lo;
+ *(ptr++) = regs->hi;
+ *(ptr++) = regs->cp0_badvaddr;
+ *(ptr++) = regs->cp0_cause;
+ *(ptr++) = regs->cp0_epc;
+
+ return;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg = 0;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ regs->regs[reg] = *(ptr++);
+
+ regs->cp0_status = *(ptr++);
+ regs->lo = *(ptr++);
+ regs->hi = *(ptr++);
+ regs->cp0_badvaddr = *(ptr++);
+ regs->cp0_cause = *(ptr++);
+ regs->cp0_epc = *(ptr++);
+
+ return;
+}
+
+/*
+ * Similar to regs_to_gdb_regs() except that process is sleeping and so
+ * we may not be able to get all the info.
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ int reg = 0;
+ struct thread_info *ti = p->thread_info;
+ unsigned long ksp = (unsigned long)ti + THREAD_SIZE -32;
+ struct pt_regs *regs = (struct pt_regs *)ksp - 1;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 16; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ /* S0 - S7 */
+ for (reg = 16; reg < 24; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ for (reg = 24; reg < 28; reg++)
+ *(ptr++) = 0;
+
+ /* GP, SP, FP, RA */
+ for (reg = 28; reg < 32; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ *(ptr++) = regs->cp0_status;
+ *(ptr++) = regs->lo;
+ *(ptr++) = regs->hi;
+ *(ptr++) = regs->cp0_badvaddr;
+ *(ptr++) = regs->cp0_cause;
+ *(ptr++) = regs->cp0_epc;
+
+ return;
+}
+
+/*
+ * Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
+ * then try to fall into the debugger
+ */
+static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *args = (struct die_args *)ptr;
+ struct pt_regs *regs = args->regs;
+ int trap = (regs->cp0_cause & 0x7c) >> 2;
+
+ if (cmd == DIE_PAGE_FAULT)
+ return NOTIFY_DONE;
+
+ if (user_mode(regs)) {
+ if (cmd == DIE_OOPS)
+ CHK_DEBUGGER(trap, compute_signal(trap), 0, regs, 0);
+ return NOTIFY_DONE;
+ }
+
+ CHK_DEBUGGER(trap, compute_signal(trap), 0, regs, 0);
+ 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 *InBuffer, char *outBuffer,
+ struct pt_regs *regs)
+{
+ char *ptr;
+ unsigned long address;
+ int cpu = smp_processor_id ();
+
+ switch (InBuffer[0]) {
+ case 's':
+ case 'c':
+ if (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(outBuffer, "E00");
+ break;
+ }
+
+ /* handle the optional parameter */
+ ptr = &InBuffer[1];
+ if (kgdb_hex2long (&ptr, &address))
+ regs->cp0_epc = address;
+
+ atomic_set(&cpu_doing_single_step,-1);
+ if (InBuffer[0] == 's')
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step, cpu);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ .gdb_bpt_instr = {0xd},
+#else
+ .gdb_bpt_instr = {0x00, 0x00, 0x00, 0x0d},
+#endif
+};
+
+/*
+ * Note that on some boards (such as MIPS Malta) we need to do additional
+ * bringup. We do this via kgdb_mips_init(). We provide a weak version
+ * here as others (such as BCM91250A-SWARM) need no extra bringup.
+ */
+void __attribute__ ((weak)) kgdb_mips_init(void)
+{
+}
+
+int kgdb_arch_init(void)
+{
+ /* Board-specifics. */
+ kgdb_mips_init();
+
+ /* Set our traps. */
+ /* This needs to be done more finely grained again, paired in
+ * a before/after in kgdb_handle_exception(...) -- Tom */
+ set_debug_traps();
+ notifier_chain_register(&mips_die_chain, &kgdb_notifier);
+
+ return 0;
+}
diff -puN /dev/null arch/mips/kernel/kgdb_handler.S
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb_handler.S 2004-10-29 11:26:45.208273480 -0700
@@ -0,0 +1,58 @@
+/*
+ * arch/mips/kernel/kgdb_handler.S
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, [email protected] or [email protected]
+ *
+ * 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 <linux/config.h>
+#include <linux/sys.h>
+
+#include <asm/asm.h>
+#include <asm/errno.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+ .align 5
+ NESTED(trap_low, PT_SIZE, sp)
+ .set noat
+ .set noreorder
+ SAVE_ALL
+ /*
+ * Check for privileged instructions in user mode. For
+ * this, check the cu0 bit in the CPU status register.
+ */
+ mfc0 k0, CP0_STATUS
+ sll k0, 3
+ bltz k0, 1f
+ move k1, sp
+
+ /*
+ * GDB userland from within KGDB. If a user mode address
+ * then jump to the saved exception handler
+ */
+ lui k1, %hi(saved_vectors)
+ mfc0 k0, CP0_CAUSE
+ andi k0, k0, 0x7c
+ add k1, k1, k0
+ lw k0, %lo(saved_vectors)(k1)
+ jr k0
+ nop
+1:
+ .set at
+ .set reorder
+ move a0, sp
+ jal handle_exception
+ j ret_from_exception
+ END(trap_low)
diff -puN arch/mips/kernel/traps.c~mips-lite arch/mips/kernel/traps.c
--- linux-2.6.10-rc1/arch/mips/kernel/traps.c~mips-lite 2004-10-29 11:26:45.181279818 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/traps.c 2004-10-29 11:26:45.210273010 -0700
@@ -10,6 +10,8 @@
* Kevin D. Kissell, [email protected] and Carsten Langgaard, [email protected]
* Copyright (C) 2000, 01 MIPS Technologies, Inc.
* Copyright (C) 2002, 2003, 2004 Maciej W. Rozycki
+ *
+ * KGDB specific changes - Manish Lachwani ([email protected])
*/
#include <linux/config.h>
#include <linux/init.h>
@@ -20,6 +22,7 @@
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
+#include <linux/debugger.h>

#include <asm/bootinfo.h>
#include <asm/branch.h>
@@ -37,6 +40,7 @@
#include <asm/mmu_context.h>
#include <asm/watch.h>
#include <asm/types.h>
+#include <asm/kdebug.h>

extern asmlinkage void handle_mod(void);
extern asmlinkage void handle_tlbl(void);
@@ -72,6 +76,21 @@ int (*board_be_handler)(struct pt_regs *
*/
#define MODULE_RANGE (8*1024*1024)

+struct notifier_block *mips_die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+ int err = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&die_notifier_lock, flags);
+ err = notifier_chain_register(&mips_die_chain, nb);
+ spin_unlock_irqrestore(&die_notifier_lock, flags);
+
+ return err;
+}
+
/*
* This routine abuses get_user()/put_user() to reference pointers
* with at least a bit of error checking ...
@@ -265,8 +284,10 @@ NORET_TYPE void __die(const char * str,
void __die_if_kernel(const char * str, struct pt_regs * regs,
const char * file, const char * func, unsigned long line)
{
- if (!user_mode(regs))
+ if (!user_mode(regs)) {
+ notify_die(DIE_TRAP, (char *)str, regs, 0);
__die(str, regs, file, func, line);
+ }
}

extern const struct exception_table_entry __start___dbe_table[];
@@ -921,6 +942,9 @@ void __init per_cpu_trap_init(void)
tlb_init();
}

+/* Keep track of if we've done certain initialization already or not. */
+int kgdb_early_setup;
+
void __init trap_init(void)
{
extern char except_vec3_generic, except_vec3_r4000;
@@ -928,6 +952,9 @@ void __init trap_init(void)
extern char except_vec4;
unsigned long i;

+ if (kgdb_early_setup)
+ return; /* Already done */
+
per_cpu_trap_init();

/*
diff -puN arch/mips/mips-boards/generic/Makefile~mips-lite arch/mips/mips-boards/generic/Makefile
--- linux-2.6.10-rc1/arch/mips/mips-boards/generic/Makefile~mips-lite 2004-10-29 11:26:45.183279348 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/Makefile 2004-10-29 11:26:45.210273010 -0700
@@ -21,6 +21,5 @@
obj-y := mipsIRQ.o reset.o display.o init.o memory.o \
printf.o cmdline.o time.o
obj-$(CONFIG_PCI) += pci.o
-obj-$(CONFIG_KGDB) += gdb_hook.o

EXTRA_AFLAGS := $(CFLAGS)
diff -puN arch/mips/mips-boards/generic/init.c~mips-lite arch/mips/mips-boards/generic/init.c
--- linux-2.6.10-rc1/arch/mips/mips-boards/generic/init.c~mips-lite 2004-10-29 11:26:45.185278879 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/init.c 2004-10-29 11:26:45.210273010 -0700
@@ -35,17 +35,6 @@
#include <asm/mips-boards/malta.h>
#endif

-#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);
-
-int remote_debug = 0;
-#endif
-
int prom_argc;
int *_prom_argv, *_prom_envp;

@@ -172,59 +161,6 @@ static void __init console_config(void)
}
#endif

-#ifdef CONFIG_KGDB
-void __init kgdb_config (void)
-{
- extern int (*generic_putDebugChar)(char);
- extern char (*generic_getDebugChar)(void);
- char *argptr;
- int line, speed;
-
- argptr = prom_getcmdline();
- if ((argptr = strstr(argptr, "kgdb=ttyS")) != NULL) {
- argptr += strlen("kgdb=ttyS");
- if (*argptr != '0' && *argptr != '1')
- printk("KGDB: Unknown serial line /dev/ttyS%c, "
- "falling back to /dev/ttyS1\n", *argptr);
- line = *argptr == '0' ? 0 : 1;
- printk("KGDB: Using serial line /dev/ttyS%d for session\n", line);
-
- speed = 0;
- if (*++argptr == ',')
- {
- int c;
- while ((c = *++argptr) && ('0' <= c && c <= '9'))
- speed = speed * 10 + c - '0';
- }
-#ifdef CONFIG_MIPS_ATLAS
- if (line == 1) {
- speed = saa9730_kgdb_hook(speed);
- generic_putDebugChar = saa9730_putDebugChar;
- generic_getDebugChar = saa9730_getDebugChar;
- }
- else
-#endif
- {
- speed = rs_kgdb_hook(line, speed);
- generic_putDebugChar = rs_putDebugChar;
- generic_getDebugChar = rs_getDebugChar;
- }
-
- prom_printf("KGDB: Using serial line /dev/ttyS%d at %d for session, "
- "please connect your debugger\n", line ? 1 : 0, speed);
-
- {
- char *s;
- for (s = "Please connect GDB to this port\r\n"; *s; )
- generic_putDebugChar (*s++);
- }
-
- remote_debug = 1;
- /* Breakpoint is invoked after interrupts are initialised */
- }
-}
-#endif
-
void __init prom_init(void)
{
prom_argc = fw_arg0;
diff -puN arch/mips/mips-boards/malta/malta_int.c~mips-lite arch/mips/mips-boards/malta/malta_int.c
--- linux-2.6.10-rc1/arch/mips/mips-boards/malta/malta_int.c~mips-lite 2004-10-29 11:26:45.187278409 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_int.c 2004-10-29 11:26:45.211272776 -0700
@@ -40,12 +40,7 @@
#include <asm/mips-boards/msc01_pci.h>

extern asmlinkage void mipsIRQ(void);
-
-#ifdef CONFIG_KGDB
-extern void breakpoint(void);
-extern void set_debug_traps(void);
-extern int remote_debug;
-#endif
+extern int kgdb_early_setup;

static spinlock_t mips_irq_lock = SPIN_LOCK_UNLOCKED;

@@ -189,16 +184,20 @@ void corehi_irqdispatch(struct pt_regs *

void __init init_IRQ(void)
{
+ if (kgdb_early_setup)
+ return; /* Already done. */
set_except_vector(0, mipsIRQ);
init_generic_irq();
init_i8259_irqs();
+}

#ifdef CONFIG_KGDB
- if (remote_debug) {
- set_debug_traps();
- breakpoint();
- }
-#endif
+void kgdb_mips_init(void)
+{
+ trap_init();
+ set_except_vector(0, mipsIRQ);
+ init_generic_irq();
+ init_i8259_irqs();
+ kgdb_early_setup = 1;
}
-
-
+#endif
diff -puN arch/mips/mips-boards/malta/malta_setup.c~mips-lite arch/mips/mips-boards/malta/malta_setup.c
--- linux-2.6.10-rc1/arch/mips/mips-boards/malta/malta_setup.c~mips-lite 2004-10-29 11:26:45.188278175 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_setup.c 2004-10-29 11:26:45.211272776 -0700
@@ -41,10 +41,6 @@ extern void mips_time_init(void);
extern void mips_timer_setup(struct irqaction *irq);
extern unsigned long mips_rtc_get_time(void);

-#ifdef CONFIG_KGDB
-extern void kgdb_config(void);
-#endif
-
struct resource standard_io_resources[] = {
{ "dma1", 0x00, 0x1f, IORESOURCE_BUSY },
{ "timer", 0x40, 0x5f, IORESOURCE_BUSY },
@@ -93,10 +89,6 @@ static int __init malta_setup(void)
*/
enable_dma(4);

-#ifdef CONFIG_KGDB
- kgdb_config ();
-#endif
-
if ((mips_revision_corid == MIPS_REVISION_CORID_BONITO64) ||
(mips_revision_corid == MIPS_REVISION_CORID_CORE_20K) ||
(mips_revision_corid == MIPS_REVISION_CORID_CORE_EMUL_BON)) {
diff -puN arch/mips/sibyte/cfe/setup.c~mips-lite arch/mips/sibyte/cfe/setup.c
--- linux-2.6.10-rc1/arch/mips/sibyte/cfe/setup.c~mips-lite 2004-10-29 11:26:45.190277705 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/cfe/setup.c 2004-10-29 11:26:45.212272541 -0700
@@ -62,10 +62,6 @@ extern void * __rd_start, * __rd_end;
static int reboot_smp = 0;
#endif

-#ifdef CONFIG_KGDB
-extern int kgdb_port;
-#endif
-
static void cfe_linux_exit(void)
{
#ifdef CONFIG_SMP
@@ -243,9 +239,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 = (void (*)(char *))cfe_linux_exit;
_machine_halt = cfe_linux_exit;
@@ -309,13 +302,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~mips-lite arch/mips/sibyte/sb1250/Makefile
--- linux-2.6.10-rc1/arch/mips/sibyte/sb1250/Makefile~mips-lite 2004-10-29 11:26:45.192277236 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/Makefile 2004-10-29 11:26:45.212272541 -0700
@@ -4,5 +4,6 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SIBYTE_TBPROF) += bcm1250_tbprof.o
obj-$(CONFIG_SIBYTE_STANDALONE) += prom.o
obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o
+obj-$(CONFIG_KGDB_SIBYTE) += kgdb_sibyte.o

EXTRA_AFLAGS := $(CFLAGS)
diff -puN arch/mips/sibyte/sb1250/irq.c~mips-lite arch/mips/sibyte/sb1250/irq.c
--- linux-2.6.10-rc1/arch/mips/sibyte/sb1250/irq.c~mips-lite 2004-10-29 11:26:45.194276766 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/irq.c 2004-10-29 11:26:45.212272541 -0700
@@ -59,27 +59,7 @@ static void sb1250_set_affinity(unsigned
#ifdef CONFIG_SIBYTE_HAS_LDT
extern unsigned long ldt_eoi_space;
#endif
-
-#ifdef CONFIG_KGDB
-#include <asm/gdb-stub.h>
-extern void breakpoint(void);
-static int kgdb_irq;
-
-/* 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
+extern int kgdb_early_setup;

static struct hw_interrupt_type sb1250_irq_type = {
"SB1250-IMR",
@@ -340,6 +320,11 @@ void __init init_IRQ(void)
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;

+#ifdef CONFIG_KGDB
+ if (kgdb_early_setup == 1)
+ return;
+#endif
+
/* Default everything to IP2 */
for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */
__raw_writeq(IMR_IP2_VAL,
@@ -389,49 +374,4 @@ void __init init_IRQ(void)
/* Enable necessary IPs, disable the rest */
change_c0_status(ST0_IM, imask);
set_except_vector(0, sb1250_irq_handler);
-
-#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);
-
- prom_printf("Waiting for GDB on UART port %d\n", kgdb_port);
- set_debug_traps();
- breakpoint();
- }
-#endif
-}
-
-#ifdef CONFIG_KGDB
-
-#include <linux/delay.h>
-
-#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 sb1250_kgdb_interrupt(struct pt_regs *regs)
-{
- /*
- * Clear break-change status (allow some time for the remote
- * host to stop the break, since we would see another
- * interrupt on the end-of-break too)
- */
- kstat_this_cpu.irqs[kgdb_irq]++;
- mdelay(500);
- duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
- M_DUART_RX_EN | M_DUART_TX_EN);
- set_async_breakpoint(&regs->cp0_epc);
}
-
-#endif /* CONFIG_KGDB */
diff -puN /dev/null arch/mips/sibyte/sb1250/kgdb_sibyte.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/kgdb_sibyte.c 2004-10-29 11:26:45.213272306 -0700
@@ -0,0 +1,158 @@
+/*
+ * arch/mips/sibyte/sb1250/kgdb_sibyte.c
+ *
+ * Author: Manish Lachwani, [email protected] or [email protected]
+ *
+ * 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 <linux/delay.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+
+#include <asm/io.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_uart.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/addrspace.h>
+
+int kgdb_port = 1;
+static int kgdb_irq;
+
+extern char sb1250_duart_present[];
+extern int sb1250_steal_irq(int irq);
+extern int kgdb_early_setup;
+
+/* Forward declarations. */
+static void kgdbsibyte_init_duart(void);
+
+#define IMR_IP6_VAL K_INT_MAP_I4
+#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+
+static void kgdb_swarm_write_char(int c)
+{
+ while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0);
+ duart_out(R_DUART_TX_HOLD, c);
+}
+
+static int kgdb_swarm_read_char(void)
+{
+ unsigned long flags;
+ int ret_char;
+ unsigned int status;
+
+ local_irq_save(flags);
+
+ status = duart_in(R_DUART_STATUS);
+ while ((status & M_DUART_RX_RDY) == 0) {
+ status = duart_in(R_DUART_STATUS);
+ }
+
+ /*
+ * Check for framing error
+ */
+ if (status & M_DUART_FRM_ERR) {
+ kgdbsibyte_init_duart();
+ kgdb_swarm_write_char('-');
+ return '-';
+ }
+
+ ret_char = duart_in(R_DUART_RX_HOLD);
+ local_irq_restore(flags);
+
+ return ret_char;
+}
+
+void sb1250_kgdb_interrupt(struct pt_regs *regs)
+{
+ int kgdb_irq = K_INT_UART_0 + kgdb_port;
+ /*
+ * Clear break-change status (allow some time for the remote
+ * host to stop the break, since we would see another
+ * interrupt on the end-of-break too)
+ */
+ kstat_this_cpu.irqs[kgdb_irq]++;
+ mdelay(500);
+ duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
+ M_DUART_RX_EN | M_DUART_TX_EN);
+ kgdb_schedule_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;
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_swarm_read_char,
+ .write_char = kgdb_swarm_write_char,
+ .init = kgdb_init_io,
+ .late_init = kgdbsibyte_hookup_irq,
+};
+
+void kgdb_mips_init(void)
+{
+ if (kgdb_early_setup == 0) {
+ trap_init();
+ init_IRQ();
+ kgdb_early_setup = 1;
+ }
+}
+
+/*
+ * 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);
+}
diff -puN arch/mips/sibyte/swarm/Makefile~mips-lite arch/mips/sibyte/swarm/Makefile
--- linux-2.6.10-rc1/arch/mips/sibyte/swarm/Makefile~mips-lite 2004-10-29 11:26:45.196276297 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/swarm/Makefile 2004-10-29 11:26:45.213272306 -0700
@@ -1,3 +1 @@
lib-y = setup.o rtc_xicor1241.o rtc_m41t81.o
-
-lib-$(CONFIG_KGDB) += dbg_io.o
diff -L arch/mips/sibyte/swarm/dbg_io.c -puN arch/mips/sibyte/swarm/dbg_io.c~mips-lite /dev/null
--- linux-2.6.10-rc1/arch/mips/sibyte/swarm/dbg_io.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,76 +0,0 @@
-/*
- * kgdb debug routines for SiByte boards.
- *
- * Copyright (C) 2001 MontaVista Software Inc.
- * Author: Jun Sun, [email protected] or [email protected]
- *
- * 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 <linux/delay.h>
-#include <asm/io.h>
-#include <asm/sibyte/sb1250.h>
-#include <asm/sibyte/sb1250_regs.h>
-#include <asm/sibyte/sb1250_uart.h>
-#include <asm/sibyte/sb1250_int.h>
-#include <asm/addrspace.h>
-
-/*
- * 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 /dev/null include/asm-mips/kdebug.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-mips/kdebug.h 2004-10-29 11:26:45.214272071 -0700
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, [email protected] or [email protected]
+ *
+ * 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 <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args {
+ struct pt_regs *regs;
+ const char *str;
+ long err;
+};
+
+int register_die_notifier(struct notifier_block *nb);
+extern struct notifier_block *mips_die_chain;
+
+enum die_val {
+ DIE_OOPS = 1,
+ DIE_PANIC,
+ DIE_DIE,
+ DIE_KERNELDEBUG,
+ DIE_TRAP,
+ DIE_PAGE_FAULT,
+};
+
+/*
+ * trap number can be computed from regs and signr can be computed using
+ * compute_signal()
+ */
+static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err)
+{
+ struct die_args args = { .regs=regs, .str=str, .err=err };
+ return notifier_call_chain(&mips_die_chain, val, &args);
+}
+
+#endif /* _MIPS_KDEBUG_H */
diff -puN /dev/null include/asm-mips/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-mips/kgdb.h 2004-10-29 11:26:45.214272071 -0700
@@ -0,0 +1,20 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+#include <linux/config.h>
+
+#define BUFMAX 2048
+#define NUMREGBYTES (90*sizeof(long))
+#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 CHECK_EXCEPTION_STACK() 1
+
+#endif /* _ASM_KGDB_H_ */
diff -puN lib/Kconfig.debug~mips-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~mips-lite 2004-10-29 11:26:45.200275358 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:56.134154345 -0700
@@ -115,7 +115,7 @@ endif

config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86 || ((!SMP || BROKEN) && (PPC32)))
+ depends on DEBUG_KERNEL && (X86 || MIPS32 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
@@ -128,6 +128,7 @@ config KGDB
choice
prompt "Method for KGDB communication"
depends on KGDB
+ default KGDB_SIBYTE if SIBYTE_SB1xxx_SOC
default KGDB_8250
help
There are a number of different ways in which you can communicate
@@ -140,6 +141,10 @@ config KGDB_8250
Uses generic serial port (8250) for kgdb. This is independent of the
option 9250/16550 and compatible serial port.

+config KGDB_SIBYTE
+ bool "KGDB: On the Broadcom SWARM serial port"
+ depends on MIPS && SIBYTE_SB1xxx_SOC
+
endchoice

config KGDB_SIMPLE_SERIAL
@@ -153,7 +158,7 @@ config KGDB_SIMPLE_SERIAL
If in doubt, say Y.

choice
- depends on KGDB_8250
+ depends on KGDB_8250 || KGDB_SIBYTE
prompt "Debug serial port BAUD"
default KGDB_115200BAUD
help
_

2004-10-29 19:19:07

by Tom Rini

[permalink] [raw]
Subject: [patch 6/8] KGDB support for ia64


Cc: David Mosberger-Tang <[email protected]>, Robert Picco <[email protected]>, Keldon Jones <[email protected]>
This adds support for KGDB to ia64 and was done by Robert Picco and Keldon
Jones.

---

linux-2.6.10-rc1-trini/arch/ia64/kernel/Makefile | 1
linux-2.6.10-rc1-trini/arch/ia64/kernel/irq.c | 4
linux-2.6.10-rc1-trini/arch/ia64/kernel/ivt.S | 17
linux-2.6.10-rc1-trini/arch/ia64/kernel/kgdb.c | 991 ++++++++++++++++++++++
linux-2.6.10-rc1-trini/arch/ia64/kernel/process.c | 6
linux-2.6.10-rc1-trini/arch/ia64/kernel/smp.c | 17
linux-2.6.10-rc1-trini/arch/ia64/kernel/traps.c | 26
linux-2.6.10-rc1-trini/arch/ia64/kernel/unwind.c | 61 +
linux-2.6.10-rc1-trini/arch/ia64/mm/fault.c | 3
linux-2.6.10-rc1-trini/drivers/firmware/pcdp.c | 19
linux-2.6.10-rc1-trini/include/asm-ia64/kgdb.h | 34
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
12 files changed, 1179 insertions(+), 2 deletions(-)

diff -puN arch/ia64/kernel/Makefile~ia64-lite arch/ia64/kernel/Makefile
--- linux-2.6.10-rc1/arch/ia64/kernel/Makefile~ia64-lite 2004-10-29 11:26:45.490207282 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/Makefile 2004-10-29 11:26:45.514201648 -0700
@@ -19,6 +19,7 @@ obj-$(CONFIG_PERFMON) += perfmon_defaul
obj-$(CONFIG_IA64_CYCLONE) += cyclone.o
obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o
mca_recovery-y += mca_drv.o mca_drv_asm.o
+obj-$(CONFIG_KGDB) += kgdb.o

# The gate DSO image is built using a special linker script.
targets += gate.so gate-syms.o
diff -puN arch/ia64/kernel/irq.c~ia64-lite arch/ia64/kernel/irq.c
--- linux-2.6.10-rc1/arch/ia64/kernel/irq.c~ia64-lite 2004-10-29 11:26:45.492206812 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/irq.c 2004-10-29 11:26:45.515201413 -0700
@@ -42,6 +42,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>
#include <linux/notifier.h>
#include <linux/bitops.h>

@@ -543,6 +544,9 @@ unsigned int do_IRQ(unsigned long irq, s
desc->handler->end(irq);
spin_unlock(&desc->lock);
}
+
+ kgdb_process_breakpoint();
+
return 1;
}

diff -puN arch/ia64/kernel/ivt.S~ia64-lite arch/ia64/kernel/ivt.S
--- linux-2.6.10-rc1/arch/ia64/kernel/ivt.S~ia64-lite 2004-10-29 11:26:45.494206343 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/ivt.S 2004-10-29 11:26:45.516201178 -0700
@@ -68,6 +68,13 @@
# define DBG_FAULT(i)
#endif

+#ifdef CONFIG_KGDB
+#define KGDB_ENABLE_PSR_DB mov r31=psr;; movl r30=IA64_PSR_DB;; or r31=r31,r30;; \
+ mov psr.l=r31;; srlz.i;;
+#else
+#define KGDB_ENABLE_PSR_DB
+#endif
+
#define MINSTATE_VIRT /* needed by minstate.h */
#include "minstate.h"

@@ -473,6 +480,7 @@ ENTRY(page_fault)
movl r14=ia64_leave_kernel
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
mov rp=r14
;;
adds out2=16,r12 // out2 = pointer to pt_regs
@@ -733,6 +741,8 @@ ENTRY(break_fault)
;;
srlz.i // guarantee that interruption collection is on
;;
+ KGDB_ENABLE_PSR_DB
+ ;;
(p15) ssm psr.i // restore psr.i
;;
mov r3=NR_syscalls - 1
@@ -776,6 +786,7 @@ ENTRY(interrupt)
srlz.i // ensure everybody knows psr.ic is back on
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
;;
alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group
mov out0=cr.ivr // pass cr.ivr as first arg
@@ -1003,6 +1014,7 @@ ENTRY(non_syscall)
movl r15=ia64_leave_kernel
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
mov rp=r15
;;
br.call.sptk.many b6=ia64_bad_break // avoid WAW on CFM and ignore return addr
@@ -1036,6 +1048,7 @@ ENTRY(dispatch_unaligned_handler)
adds r3=8,r2 // set up second base pointer
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
movl r14=ia64_leave_kernel
;;
mov rp=r14
@@ -1078,6 +1091,10 @@ ENTRY(dispatch_to_fault_handler)
adds r3=8,r2 // set up second base pointer for SAVE_REST
;;
SAVE_REST
+ cmp.eq p6,p0=29,out0
+(p6) br.cond.spnt 1f;; // debug_vector
+ KGDB_ENABLE_PSR_DB
+1:
movl r14=ia64_leave_kernel
;;
mov rp=r14
diff -puN /dev/null arch/ia64/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/kgdb.c 2004-10-29 11:26:45.518200709 -0700
@@ -0,0 +1,991 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ */
+/*
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale<[email protected]>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <[email protected]>
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <asm/unwind.h>
+#include <asm/rse.h>
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+
+#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 ptoff(V) ((unsigned int) &((struct pt_regs *)0x0)->V)
+struct reg_to_ptreg_index {
+ unsigned int reg;
+ unsigned int ptregoff;
+};
+struct reg_to_ptreg_index gr_reg_to_ptreg_index[] = {
+ {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+1, ptoff(r1)},
+ {IA64_GR0_REGNUM+12, ptoff(r12)},
+ {IA64_GR0_REGNUM+13, ptoff(r13)},
+ {IA64_GR0_REGNUM+14, ptoff(r14)},
+ {IA64_GR0_REGNUM+15, ptoff(r15)},
+};
+
+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)},
+};
+
+extern atomic_t cpu_doing_single_step;
+
+void kgdb_get_reg(char *outbuffer, int regnum, struct unw_frame_info *info, struct pt_regs *ptregs)
+{
+ unsigned long reg, size = 0, *mem = &reg;
+ char nat;
+ struct ia64_fpreg freg;
+ int i;
+
+ if ((regnum >= IA64_GR0_REGNUM && regnum <= (IA64_GR0_REGNUM+1)) ||
+ (regnum >= (IA64_GR0_REGNUM+4) && regnum <= (IA64_GR0_REGNUM+7)) ||
+ (regnum >= (IA64_GR0_REGNUM+16) && regnum <= (IA64_GR0_REGNUM+31))) {
+ unw_access_gr(info, regnum - IA64_GR0_REGNUM, &reg, &nat, 0);
+ size = sizeof(reg);
+ }
+ else if ((regnum >= (IA64_GR0_REGNUM+2) && regnum <= (IA64_GR0_REGNUM+3)) ||
+ (regnum >= (IA64_GR0_REGNUM+8) && regnum <= (IA64_GR0_REGNUM+15))) {
+ if (ptregs) {
+ for (i = 0; i < (sizeof (gr_reg_to_ptreg_index) /
+ sizeof(gr_reg_to_ptreg_index[0])); i++)
+ if (gr_reg_to_ptreg_index[0].reg == regnum) {
+ reg = * ((unsigned long *)
+ (((void *) ptregs) + gr_reg_to_ptreg_index[i].ptregoff));
+ break;
+ }
+ } else
+ unw_access_gr(info, regnum - IA64_GR0_REGNUM, &reg, &nat, 0);
+ size = sizeof(reg);
+ }
+ else if (regnum >= IA64_BR0_REGNUM && regnum <= (IA64_BR0_REGNUM+7)) switch(regnum) {
+ case IA64_BR0_REGNUM:
+ case IA64_BR0_REGNUM+6:
+ case IA64_BR0_REGNUM+7:
+ if (ptregs) {
+ for (i = 0; i < (sizeof(br_reg_to_ptreg_index) /
+ sizeof(br_reg_to_ptreg_index[0])); i++)
+ if (br_reg_to_ptreg_index[i].reg == regnum) {
+ reg = * ((unsigned long *)
+ (((void *) ptregs) + br_reg_to_ptreg_index[i].ptregoff));
+ break;
+ }
+ } else
+ unw_access_br(info, regnum - IA64_BR0_REGNUM, &reg, 0);
+ size = sizeof(reg);
+ 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:
+ unw_access_br(info, regnum - IA64_BR0_REGNUM, &reg, 0);
+ size = sizeof(reg);
+ break;
+ }
+ else if (regnum >= IA64_FR0_REGNUM && regnum <= (IA64_FR0_REGNUM + 127)) 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 (!ptregs)
+ unw_access_fr(info, regnum - IA64_FR0_REGNUM, &freg, 0);
+ else {
+ freg = *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM+6)));
+ }
+ size = sizeof(freg);
+ mem = (unsigned long *) &freg;
+ break;
+ default:
+ unw_access_fr(info, regnum - IA64_FR0_REGNUM, &freg, 0);
+ break;
+ }
+ else if (regnum == IA64_IP_REGNUM) {
+ if (!ptregs)
+ unw_get_ip(info, &reg);
+ else
+ reg = ptregs->cr_iip;
+ size = sizeof(reg);
+ }
+ else if (regnum == IA64_CFM_REGNUM) {
+ if (!ptregs)
+ unw_get_cfm(info, &reg);
+ 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, &reg, 0);
+ size = sizeof(reg);
+ }
+ else if (regnum == IA64_BSP_REGNUM) {
+ unw_get_bsp(info, &reg);
+ size = sizeof(reg);
+ }
+ else if (regnum >= IA64_AR0_REGNUM && regnum <= IA64_EC_REGNUM) switch (regnum) {
+ case IA64_CSD_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_csd;
+ else
+ unw_access_ar(info, UNW_AR_CSD, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_SSD_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_ssd;
+ else
+ unw_access_ar(info, UNW_AR_SSD, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_UNAT_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_unat;
+ else
+ unw_access_ar(info, UNW_AR_UNAT, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_RNAT_REGNUM:
+ unw_access_ar(info, UNW_AR_RNAT, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_BSPSTORE_REGNUM:
+ unw_access_ar(info, UNW_AR_BSPSTORE, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_PFS_REGNUM:
+ unw_access_ar(info, UNW_AR_PFS, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_LC_REGNUM:
+ unw_access_ar(info, UNW_AR_LC, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_EC_REGNUM:
+ unw_access_ar(info, UNW_AR_EC, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_FPSR_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_fpsr;
+ else
+ unw_access_ar(info, UNW_AR_FPSR, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_RSC_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_rsc;
+ else
+ unw_access_ar(info, UNW_AR_RNAT, &reg, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_CCV_REGNUM:
+ unw_access_ar(info, UNW_AR_CCV, &reg, 0);
+ size = sizeof(reg);
+ break;
+ }
+
+ 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;
+ char nat = 0;
+ struct ia64_fpreg freg;
+ int i;
+ char *ptr;
+
+ ptr = inbuffer;
+ kgdb_hex2long(&ptr, &reg);
+ strcpy(outbuffer, "OK");
+
+ if ((regnum >= IA64_GR0_REGNUM && regnum <= (IA64_GR0_REGNUM+1)) ||
+ (regnum >= (IA64_GR0_REGNUM+4) && regnum <= (IA64_GR0_REGNUM+7)) ||
+ (regnum >= (IA64_GR0_REGNUM+16) && regnum <= (IA64_GR0_REGNUM+31))) {
+ unw_access_gr(info, regnum - IA64_GR0_REGNUM, &reg, &nat, 1);
+ }
+ else if ((regnum >= (IA64_GR0_REGNUM+2) && regnum <= (IA64_GR0_REGNUM+3)) ||
+ (regnum >= (IA64_GR0_REGNUM+8) && regnum <= (IA64_GR0_REGNUM+15))) {
+ for (i = 0; i < (sizeof (gr_reg_to_ptreg_index) /
+ sizeof(gr_reg_to_ptreg_index[0])); i++)
+ if (gr_reg_to_ptreg_index[0].reg == regnum) {
+ * ((unsigned long *)
+ (((void *) ptregs) + gr_reg_to_ptreg_index[i].ptregoff)) = reg;
+ break;
+ }
+ }
+ else if (regnum >= IA64_BR0_REGNUM && regnum <= (IA64_BR0_REGNUM+7)) switch(regnum) {
+ case IA64_BR0_REGNUM:
+ case IA64_BR0_REGNUM+6:
+ case IA64_BR0_REGNUM+7:
+ for (i = 0; i < (sizeof(br_reg_to_ptreg_index) /
+ sizeof(br_reg_to_ptreg_index[0])); i++)
+ if (br_reg_to_ptreg_index[i].reg == regnum) {
+ * ((unsigned long *)
+ (((void *) ptregs) + br_reg_to_ptreg_index[i].ptregoff)) = reg;
+ 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:
+ unw_access_br(info, regnum - IA64_BR0_REGNUM, &reg, 1);
+ break;
+ }
+ else if (regnum >= IA64_FR0_REGNUM && regnum <= (IA64_FR0_REGNUM + 127)) 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:
+ freg.u.bits[0] = reg;
+ kgdb_hex2long(&ptr, &freg.u.bits[1]);
+ *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM+6))) = freg;
+ break;
+ default:
+ break;
+ }
+ 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 if (regnum >= IA64_AR0_REGNUM && regnum <= IA64_EC_REGNUM) switch (regnum) {
+ case IA64_CSD_REGNUM:
+ ptregs->ar_csd = reg;
+ break;
+ case IA64_SSD_REGNUM:
+ ptregs->ar_ssd = reg;
+ case IA64_UNAT_REGNUM:
+ ptregs->ar_unat = reg;
+ case IA64_RNAT_REGNUM:
+ unw_access_ar(info, UNW_AR_RNAT, &reg, 1);
+ break;
+ case IA64_BSPSTORE_REGNUM:
+ unw_access_ar(info, UNW_AR_BSPSTORE, &reg, 1);
+ break;
+ case IA64_PFS_REGNUM:
+ unw_access_ar(info, UNW_AR_PFS, &reg, 1);
+ break;
+ case IA64_LC_REGNUM:
+ unw_access_ar(info, UNW_AR_LC, &reg, 1);
+ break;
+ case IA64_EC_REGNUM:
+ unw_access_ar(info, UNW_AR_EC, &reg, 1);
+ break;
+ case IA64_FPSR_REGNUM:
+ ptregs->ar_fpsr = reg;
+ break;
+ case IA64_RSC_REGNUM:
+ ptregs->ar_rsc = reg;
+ break;
+ case IA64_CCV_REGNUM:
+ unw_access_ar(info, UNW_AR_CCV, &reg, 1);
+ break;
+ }
+ else
+ strcpy(outbuffer, "E01");
+
+ return;
+}
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+
+}
+
+#define MAX_HW_BREAKPOINT (20)
+long hw_break_total_dbr, hw_break_total_ibr;
+#define HW_BREAKPOINT (hw_break_total_dbr + hw_break_total_ibr)
+#define WATCH_INSTRUCTION 0x0
+#define WATCH_WRITE 0x1
+#define WATCH_READ 0x2
+#define WATCH_ACCESS 0x3
+
+#define HWCAP_DBR ((1 << WATCH_WRITE) | (1 << WATCH_READ))
+#define HWCAP_IBR (1 << WATCH_INSTRUCTION)
+struct hw_breakpoint {
+ unsigned enabled;
+ unsigned long capable;
+ unsigned long type;
+ unsigned long mask;
+ unsigned long addr;
+} *breakinfo;
+
+static struct hw_breakpoint hwbreaks[MAX_HW_BREAKPOINT];
+
+enum instruction_type {A, I, M, F, B, L, X, u};
+
+static enum instruction_type bundle_encoding[32][3] = {
+ { M, I, I }, /* 00 */
+ { M, I, I }, /* 01 */
+ { M, I, I }, /* 02 */
+ { M, I, I }, /* 03 */
+ { M, L, X }, /* 04 */
+ { M, L, X }, /* 05 */
+ { u, u, u }, /* 06 */
+ { u, u, u }, /* 07 */
+ { M, M, I }, /* 08 */
+ { M, M, I }, /* 09 */
+ { M, M, I }, /* 0A */
+ { M, M, I }, /* 0B */
+ { M, F, I }, /* 0C */
+ { M, F, I }, /* 0D */
+ { M, M, F }, /* 0E */
+ { M, M, F }, /* 0F */
+ { M, I, B }, /* 10 */
+ { M, I, B }, /* 11 */
+ { M, B, B }, /* 12 */
+ { M, B, B }, /* 13 */
+ { u, u, u }, /* 14 */
+ { u, u, u }, /* 15 */
+ { B, B, B }, /* 16 */
+ { B, B, B }, /* 17 */
+ { M, M, B }, /* 18 */
+ { M, M, B }, /* 19 */
+ { u, u, u }, /* 1A */
+ { u, u, u }, /* 1B */
+ { M, F, B }, /* 1C */
+ { M, F, B }, /* 1D */
+ { u, u, u }, /* 1E */
+ { u, u, u }, /* 1F */
+};
+
+static int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
+{
+ extern unsigned long _start[];
+ unsigned long slot = addr & 0xf, 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);
+}
+
+static int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
+{
+ extern unsigned long _start[];
+
+ addr = addr & ~0xFULL;
+ if (addr == (unsigned long) _start)
+ return 0;
+ return kgdb_set_mem((char *) addr, (char *) bundle, BREAK_INSTR_SIZE);
+}
+
+static int hw_breakpoint_init;
+
+void do_init_hw_break(void)
+{
+ s64 status;
+ int i;
+
+ hw_breakpoint_init = 1;
+
+#ifdef CONFIG_IA64_HP_SIM
+ hw_break_total_ibr = 8;
+ hw_break_total_dbr = 8;
+ status = 0;
+#else
+ status = ia64_pal_debug_info(&hw_break_total_ibr, &hw_break_total_dbr);
+#endif
+
+ if (status) {
+ printk(KERN_INFO "do_init_hw_break: pal call failed %d\n", (int) status);
+ return;
+ }
+
+ if (HW_BREAKPOINT > MAX_HW_BREAKPOINT) {
+ printk(KERN_INFO "do_init_hw_break: %d exceeds max %d\n", (int) HW_BREAKPOINT,
+ (int) MAX_HW_BREAKPOINT);
+
+ while ((HW_BREAKPOINT > MAX_HW_BREAKPOINT) && hw_break_total_ibr != 1)
+ hw_break_total_ibr--;
+ while (HW_BREAKPOINT > MAX_HW_BREAKPOINT)
+ hw_break_total_dbr--;
+ }
+
+ breakinfo = hwbreaks;
+
+ memset(breakinfo, 0, HW_BREAKPOINT * sizeof(struct hw_breakpoint));
+
+ for (i = 0; i < hw_break_total_dbr; i++)
+ breakinfo[i].capable = HWCAP_DBR;
+
+ for (; i < HW_BREAKPOINT; i++)
+ breakinfo[i].capable = HWCAP_IBR;
+
+ return;
+}
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+
+ if (!breakinfo)
+ return;
+
+ for (breakno = 0; breakno < HW_BREAKPOINT; breakno++) {
+ if (breakinfo[breakno].enabled) {
+ if (breakinfo[breakno].capable & HWCAP_IBR) {
+ int ibreakno = breakno - hw_break_total_dbr;
+ ia64_set_ibr(ibreakno << 1, breakinfo[breakno].addr);
+ ia64_set_ibr((ibreakno << 1) + 1,
+ (~breakinfo[breakno].mask & ((1UL << 56UL) - 1)) |
+ (1UL << 56UL) | (1UL << 63UL));
+ }
+ else {
+ ia64_set_dbr(breakno << 1, breakinfo[breakno].addr);
+ ia64_set_dbr((breakno << 1) + 1,
+ (~breakinfo[breakno].mask & ((1UL << 56UL) - 1)) |
+ (1UL << 56UL) | (breakinfo[breakno].type << 62UL));
+ }
+ }
+ else {
+ if (breakinfo[breakno].capable & HWCAP_IBR)
+ ia64_set_ibr(((breakno - hw_break_total_dbr) << 1) + 1, 0);
+ else
+ ia64_set_dbr((breakno << 1) + 1, 0);
+ }
+ }
+
+ return;
+}
+
+int hardware_breakpoint(unsigned long addr, int length, int type, int action)
+{
+ int breakno, found, watch;
+ unsigned long mask;
+ extern unsigned long _start[];
+
+ if (!hw_breakpoint_init)
+ do_init_hw_break();
+
+ if (!breakinfo)
+ return 0;
+ else if (addr == (unsigned long) _start)
+ return 1;
+
+ if (type == WATCH_ACCESS)
+ mask = HWCAP_DBR;
+ else
+ mask = 1UL << type;
+
+ for (watch = 0, found = 0, breakno = 0; breakno < HW_BREAKPOINT; breakno++) {
+ if (action) {
+ if (breakinfo[breakno].enabled || !(breakinfo[breakno].capable & mask))
+ continue;
+ breakinfo[breakno].enabled = 1;
+ breakinfo[breakno].type = type;
+ breakinfo[breakno].mask = length - 1;
+ breakinfo[breakno].addr = addr;
+ watch = breakno;
+ } else if (breakinfo[breakno].enabled &&
+ ((length < 0 && breakinfo[breakno].addr == addr) ||
+ ((breakinfo[breakno].capable & mask) &&
+ (breakinfo[breakno].mask == (length - 1)) &&
+ (breakinfo[breakno].addr == addr)))) {
+ breakinfo[breakno].enabled = 0;
+ breakinfo[breakno].type = 0UL;
+ }
+ else
+ continue;
+ found++;
+ if (type != WATCH_ACCESS)
+ break;
+ else if (found == 2)
+ break;
+ else
+ mask = HWCAP_IBR;
+ }
+
+ if (type == WATCH_ACCESS && found == 1) {
+ breakinfo[watch].enabled = 0;
+ found = 0;
+ }
+
+ return found;
+}
+
+int kgdb_arch_set_hw_breakpoint(unsigned long addr, int len, enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 1);
+}
+
+int kgdb_arch_remove_hw_breakpoint(unsigned long addr, int len, enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 0);
+}
+
+int kgdb_remove_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 0);
+
+}
+
+void kgdb_remove_all_hw_break(void)
+{
+ int i;
+
+ for (i = 0; i < HW_BREAKPOINT; i++)
+ memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+}
+
+int kgdb_set_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 1);
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status;
+
+ hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status ^ IA64_PSR_DB);
+}
+
+volatile static struct smp_unw {
+ struct unw_frame_info *unw;
+ struct task_struct *task;
+} smp_unw[NR_CPUS];
+
+static int inline kgdb_get_blocked_state(struct task_struct *p, struct unw_frame_info *unw)
+{
+ unsigned long ip;
+ int count = 0;
+
+ unw_init_from_blocked_task(unw, p);
+ ip = 0UL;
+ do {
+ if (unw_unwind(unw) < 0)
+ return -1;
+ unw_get_ip(unw, &ip);
+ if (!in_sched_functions(ip))
+ break;
+ } while (count++ < 16);
+
+ if (!ip)
+ return -1;
+ else
+ return 0;
+}
+
+static void inline kgdb_wait(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status ^ IA64_PSR_DB);
+ kgdb_nmihook(smp_processor_id(), regs);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status);
+ return;
+}
+
+static void inline normalize(struct unw_frame_info *running, struct pt_regs *regs)
+{
+ unsigned long sp;
+
+ do {
+ unw_get_sp(running, &sp);
+ if ((sp + 0x10) >= (unsigned long) regs)
+ break;
+ } while (unw_unwind(running) >= 0);
+
+ return;
+}
+
+static void kgdb_init_running(struct unw_frame_info *unw, void *data)
+{
+ struct pt_regs *regs;
+
+ regs = data;
+ normalize(unw, regs);
+ smp_unw[smp_processor_id()].unw = unw;
+ kgdb_wait(regs);
+}
+
+void kgdb_wait_ipi(struct pt_regs *regs)
+{
+ struct unw_frame_info unw;
+
+ smp_unw[smp_processor_id()].task = current;
+
+ if (user_mode(regs)) {
+ smp_unw[smp_processor_id()].unw = (struct unw_frame_info *) 1;
+ kgdb_wait(regs);
+ }
+ else {
+ if (current->state == TASK_RUNNING)
+ unw_init_running(kgdb_init_running, regs);
+ else {
+ if (kgdb_get_blocked_state(current, &unw))
+ smp_unw[smp_processor_id()].unw = (struct unw_frame_info *) 1;
+ else
+ smp_unw[smp_processor_id()].unw = &unw;
+ kgdb_wait(regs);
+ }
+ }
+
+ smp_unw[smp_processor_id()].unw = NULL;
+ return;
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+{
+ if (num_online_cpus() > 1)
+ smp_send_nmi_allbutself();
+}
+
+static void do_kgdb_handle_exception(struct unw_frame_info *, void *data);
+
+struct kgdb_state {
+ int exceptionVector;
+ int signo;
+ unsigned long err_code;
+ struct pt_regs *regs;
+ struct unw_frame_info *unw;
+ char *inbuf;
+ char *outbuf;
+ int unwind;
+ int ret;
+};
+
+static void inline kgdb_pc(struct pt_regs *regs, unsigned long pc)
+{
+ regs->cr_iip = pc & ~0xf;
+ ia64_psr(regs)->ri = pc & 0x3;
+ return;
+}
+
+volatile int kgdb_hwbreak_sstep[NR_CPUS];
+
+int kgdb_arch_handle_exception(int exceptionVector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer, struct pt_regs *linux_regs)
+{
+ struct kgdb_state info;
+
+ info.exceptionVector = exceptionVector;
+ 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.unwind = 1;
+ info.regs = linux_regs;
+ unw_init_running(do_kgdb_handle_exception, &info);
+ } else if (kgdb_usethread->state != TASK_RUNNING) {
+ struct unw_frame_info unw_info;
+
+ if (kgdb_get_blocked_state(kgdb_usethread, &unw_info)) {
+ info.ret = 1;
+ goto bad;
+ }
+ info.regs = NULL;
+ do_kgdb_handle_exception(&unw_info, &info);
+ }
+ else {
+ int i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (smp_unw[i].task == kgdb_usethread && smp_unw[i].unw &&
+ smp_unw[i].unw != (struct unw_frame_info *) 1) {
+ info.regs = NULL;
+ do_kgdb_handle_exception(smp_unw[i].unw, &info);
+ break;
+ }
+ else {
+ info.ret = 1;
+ goto bad;
+ }
+ }
+
+bad:
+ if (info.ret != -1 && remcom_in_buffer[0] == 'p') {
+ unsigned long bad = 0xbad4badbadbadbadUL;
+
+ printk("kgdb_arch_handle_exception: p packet bad\n");
+ kgdb_mem2hex((char *) &bad, remcom_out_buffer, sizeof (bad));
+ remcom_out_buffer[sizeof (bad) * 2] = 0;
+ info.ret = -1;
+ }
+ return info.ret;
+}
+
+
+
+static void do_kgdb_handle_exception(struct unw_frame_info *unw_info, void *data)
+{
+ long addr;
+ char *ptr;
+ unsigned long newPC;
+ int exceptionVector, 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;
+ exceptionVector = info->exceptionVector;
+ 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':
+ {
+ int regnum;
+
+ kgdb_hex2mem(&remcom_in_buffer[1], (char *) &regnum, sizeof(regnum));
+ 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':
+ {
+ 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 = (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 (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(remcom_out_buffer, "E00");
+ break;
+ }
+
+ if (exceptionVector == 11 && 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' || exceptionVector == 29) {
+ linux_regs->cr_ipsr |= IA64_PSR_SS;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,smp_processor_id());
+ }
+
+ kgdb_correct_hw_break();
+
+ /* if not hardware breakpoint, then reenable them */
+ if (exceptionVector != 29)
+ linux_regs->cr_ipsr |= IA64_PSR_DB;
+ else {
+ kgdb_hwbreak_sstep[smp_processor_id()] = 1;
+ linux_regs->cr_ipsr &= ~IA64_PSR_DB;
+ }
+
+ info->ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .set_breakpoint = kgdb_arch_set_breakpoint,
+ .remove_breakpoint = kgdb_arch_remove_breakpoint,
+ .set_hw_breakpoint = kgdb_arch_set_hw_breakpoint,
+ .remove_hw_breakpoint = kgdb_arch_remove_hw_breakpoint,
+ .gdb_bpt_instr = {0xcc},
+ .flags = KGDB_HW_BREAKPOINT,
+};
diff -puN arch/ia64/kernel/process.c~ia64-lite arch/ia64/kernel/process.c
--- linux-2.6.10-rc1/arch/ia64/kernel/process.c~ia64-lite 2004-10-29 11:26:45.496205873 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/process.c 2004-10-29 11:26:45.518200709 -0700
@@ -415,6 +415,9 @@ copy_thread (int nr, unsigned long clone
*/
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
+#ifdef CONFIG_KGDB
+ child_ptregs->cr_ipsr |= IA64_PSR_DB;
+#endif

/*
* NOTE: The calling convention considers all floating point
@@ -643,6 +646,9 @@ kernel_thread (int (*fn)(void *), void *
regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
/* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
+#ifdef CONFIG_KGDB
+ regs.pt.cr_ipsr |= IA64_PSR_DB;
+#endif
regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
diff -puN arch/ia64/kernel/smp.c~ia64-lite arch/ia64/kernel/smp.c
--- linux-2.6.10-rc1/arch/ia64/kernel/smp.c~ia64-lite 2004-10-29 11:26:45.498205404 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/smp.c 2004-10-29 11:26:45.519200474 -0700
@@ -47,6 +47,7 @@
#include <asm/tlbflush.h>
#include <asm/unistd.h>
#include <asm/mca.h>
+#include <linux/kgdb.h>

/*
* Structure and data for smp_call_function(). This is designed to minimise static memory
@@ -66,6 +67,9 @@ static volatile struct call_data_struct

#define IPI_CALL_FUNC 0
#define IPI_CPU_STOP 1
+#ifdef CONFIG_KGDB
+#define IPI_KGDB_INTERRUPT 2
+#endif

/* This needs to be cacheline aligned because it is written to by *other* CPUs. */
static DEFINE_PER_CPU(u64, ipi_operation) ____cacheline_aligned;
@@ -155,6 +159,11 @@ handle_IPI (int irq, void *dev_id, struc
case IPI_CPU_STOP:
stop_this_cpu();
break;
+#ifdef CONFIG_KGDB
+ case IPI_KGDB_INTERRUPT:
+ kgdb_wait_ipi(regs);
+ break;
+#endif

default:
printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which);
@@ -303,6 +312,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~ia64-lite arch/ia64/kernel/traps.c
--- linux-2.6.10-rc1/arch/ia64/kernel/traps.c~ia64-lite 2004-10-29 11:26:45.500204934 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/traps.c 2004-10-29 11:26:45.520200239 -0700
@@ -15,6 +15,7 @@
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <linux/hardirq.h>
+#include <linux/kgdb.h>

#include <asm/fpswa.h>
#include <asm/ia32.h>
@@ -92,6 +93,8 @@ die (const char *str, struct pt_regs *re
} else
printk(KERN_ERR "Recursive die() failure, output suppressed\n");

+ CHK_DEBUGGER(1, SIGTRAP, err, regs,);
+
bust_spinlocks(0);
die.lock_owner = -1;
spin_unlock_irq(&die.lock);
@@ -119,7 +122,9 @@ ia64_bad_break (unsigned long break_num,

switch (break_num) {
case 0: /* unknown error (used by GCC for __builtin_abort()) */
+#ifndef CONFIG_KGDB
die_if_kernel("bugcheck!", regs, break_num);
+#endif
sig = SIGILL; code = ILL_ILLOPC;
break;

@@ -172,8 +177,10 @@ ia64_bad_break (unsigned long break_num,
break;

default:
+#ifndef CONFIG_KGDB
if (break_num < 0x40000 || break_num > 0x100000)
die_if_kernel("Bad break", regs, break_num);
+#endif

if (break_num < 0x80000) {
sig = SIGILL; code = __ILL_BREAK;
@@ -181,6 +188,13 @@ ia64_bad_break (unsigned long break_num,
sig = SIGTRAP; code = TRAP_BRKPT;
}
}
+#ifdef CONFIG_KGDB
+ /*
+ * We don't want to trap simulator system calls.
+ */
+ if (break_num != 0x80001)
+ CHK_DEBUGGER(11, sig, break_num, regs, return);
+#endif
siginfo.si_signo = sig;
siginfo.si_errno = 0;
siginfo.si_code = code;
@@ -487,9 +501,18 @@ ia64_fault (unsigned long vector, unsign
sprintf(buf, "Unsupported data reference");
break;

+ case 36: /* Single Step Trap */
+#ifdef CONFIG_KGDB
+ if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs) &&
+ kgdb_hwbreak_sstep[smp_processor_id()]) {
+ kgdb_hwbreak_sstep[smp_processor_id()] = 0;
+ regs->cr_ipsr &= ~IA64_PSR_SS;
+ return;
+ }
+#endif
case 29: /* Debug */
+ CHK_DEBUGGER(vector, SIGTRAP, isr, regs, return);
case 35: /* Taken Branch Trap */
- case 36: /* Single Step Trap */
if (fsys_mode(current, regs)) {
extern char __kernel_syscall_via_break[];
/*
@@ -604,6 +627,7 @@ ia64_fault (unsigned long vector, unsign
sprintf(buf, "Fault %lu", vector);
break;
}
+ CHK_DEBUGGER(vector, SIGTRAP, isr, regs,);
die_if_kernel(buf, regs, error);
force_sig(SIGILL, current);
}
diff -puN arch/ia64/kernel/unwind.c~ia64-lite arch/ia64/kernel/unwind.c
--- linux-2.6.10-rc1/arch/ia64/kernel/unwind.c~ia64-lite 2004-10-29 11:26:45.502204465 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/unwind.c 2004-10-29 11:26:45.522199770 -0700
@@ -74,10 +74,68 @@
# define STAT(x...)
#endif

+#ifdef CONFIG_KGDB
+#define KGDB_EARLY_SIZE 100
+static struct unw_reg_state __initdata kgdb_reg_state[KGDB_EARLY_SIZE];
+static struct unw_labeled_state __initdata kgdb_labeled_state[KGDB_EARLY_SIZE];
+void __initdata *kgdb_reg_state_free, __initdata *kgdb_labeled_state_free;
+
+static void __init
+kgdb_malloc_init(void)
+{
+ int i;
+
+ kgdb_reg_state_free = kgdb_reg_state;
+ for (i = 1; i < KGDB_EARLY_SIZE; i++) {
+ *((unsigned long *) &kgdb_reg_state[i]) = (unsigned long) kgdb_reg_state_free;
+ kgdb_reg_state_free = &kgdb_reg_state[i];
+ }
+
+ kgdb_labeled_state_free = kgdb_labeled_state;
+ for (i = 1; i < KGDB_EARLY_SIZE; i++) {
+ *((unsigned long *) &kgdb_labeled_state[i]) =
+ (unsigned long) kgdb_labeled_state_free;
+ kgdb_labeled_state_free = &kgdb_labeled_state[i];
+ }
+
+}
+
+static void * __init
+kgdb_malloc(void **mem)
+{
+ void *p;
+
+ p = *mem;
+ *mem = *((void **) p);
+ return p;
+}
+
+static void __init
+kgdb_free(void **mem, void *p)
+{
+ *((void **)p) = *mem;
+ *mem = p;
+}
+
+#define alloc_reg_state() (!malloc_sizes[0].cs_cachep ? \
+ kgdb_malloc(&kgdb_reg_state_free) : \
+ kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC))
+#define free_reg_state(usr) (!malloc_sizes[0].cs_cachep ? \
+ kgdb_free(&kgdb_reg_state_free, usr) : \
+ kfree(usr))
+#define alloc_labeled_state() (!malloc_sizes[0].cs_cachep ? \
+ kgdb_malloc(&kgdb_labeled_state_free) : \
+ kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC))
+#define free_labeled_state(usr) (!malloc_sizes[0].cs_cachep ? \
+ kgdb_free(&kgdb_labeled_state_free, usr) : \
+ kfree(usr))
+
+#else
#define alloc_reg_state() kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC)
#define free_reg_state(usr) kfree(usr)
#define alloc_labeled_state() kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC)
#define free_labeled_state(usr) kfree(usr)
+#endif

typedef unsigned long unw_word;
typedef unsigned char unw_hash_index_t;
@@ -2261,6 +2319,9 @@ unw_init (void)

init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) __gp,
__start_unwind, __end_unwind);
+#ifdef CONFIG_KGDB
+ kgdb_malloc_init();
+#endif
}

/*
diff -puN arch/ia64/mm/fault.c~ia64-lite arch/ia64/mm/fault.c
--- linux-2.6.10-rc1/arch/ia64/mm/fault.c~ia64-lite 2004-10-29 11:26:45.504203995 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/mm/fault.c 2004-10-29 11:26:45.522199770 -0700
@@ -9,6 +9,7 @@
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
+#include <linux/kgdb.h>

#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -232,6 +233,8 @@ ia64_do_page_fault (unsigned long addres
*/
bust_spinlocks(1);

+ CHK_DEBUGGER(14, SIGSEGV, isr, regs,);
+
if (address < PAGE_SIZE)
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address);
else
diff -puN drivers/firmware/pcdp.c~ia64-lite drivers/firmware/pcdp.c
--- linux-2.6.10-rc1/drivers/firmware/pcdp.c~ia64-lite 2004-10-29 11:26:45.506203526 -0700
+++ linux-2.6.10-rc1-trini/drivers/firmware/pcdp.c 2004-10-29 11:26:45.523199535 -0700
@@ -56,7 +56,11 @@ uart_edge_level(int rev, struct pcdp_uar
}

static void __init
+#ifndef CONFIG_KGDB
setup_serial_console(int rev, struct pcdp_uart *uart)
+#else
+setup_serial_console(int rev, struct pcdp_uart *uart, int line)
+#endif
{
#ifdef CONFIG_SERIAL_8250_CONSOLE
struct uart_port port;
@@ -64,6 +68,9 @@ setup_serial_console(int rev, struct pcd
int mapsize = 64;

memset(&port, 0, sizeof(port));
+#ifdef CONFIG_KGDB
+ port.line = line;
+#endif
port.uartclk = uart->clock_rate;
if (!port.uartclk) /* some FW doesn't supply this */
port.uartclk = BASE_BAUD * 16;
@@ -110,6 +117,9 @@ setup_serial_console(int rev, struct pcd

snprintf(options, sizeof(options), "%lun%d", uart->baud,
uart->bits ? uart->bits : 8);
+#ifdef CONFIG_KGDB
+ if (!line)
+#endif
add_preferred_console("ttyS", port.line, options);

printk(KERN_INFO "PCDP: serial console at %s 0x%lx (ttyS%d, options %s)\n",
@@ -156,10 +166,19 @@ efi_setup_pcdp_console(char *cmdline)
for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) {
if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) {
if (uart->type == PCDP_CONSOLE_UART) {
+#ifndef CONFIG_KGDB
setup_serial_console(pcdp->rev, uart);
return;
+#else
+ setup_serial_console(pcdp->rev, uart, 0);
+ serial = 0;
+#endif
}
}
+#ifdef CONFIG_KGDB
+ else if (uart->type == PCDP_DEBUG_UART)
+ setup_serial_console(pcdp->rev, uart, 1);
+#endif
}

end = (struct pcdp_device *) ((u8 *) pcdp + pcdp->length);
diff -puN /dev/null include/asm-ia64/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-ia64/kgdb.h 2004-10-29 11:26:45.523199535 -0700
@@ -0,0 +1,34 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/ptrace.h>
+
+/************************************************************************/
+/* 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 0
+
+#define BREAKNUM 0x00003333300LL
+#define KGDBBREAKNUM 0x6665UL
+
+extern volatile int kgdb_hwbreak_sstep[NR_CPUS];
+
+#define BREAKPOINT() asm volatile ("break.m 0x6665")
+#define BREAK_INSTR_SIZE 16
+
+#define CHECK_EXCEPTION_STACK() 1
+
+#define SERIAL_PORT_DFNS {0,}, {0,}, {0,}, {0,}
+
+extern void smp_send_nmi_allbutself(void);
+extern void kgdb_wait_ipi(struct pt_regs *);
+
+#endif /* _ASM_KGDB_H_ */
diff -puN lib/Kconfig.debug~ia64-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~ia64-lite 2004-10-29 11:26:45.510202587 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:55.162382599 -0700
@@ -115,7 +115,7 @@ endif

config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86 || MIPS32 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS32 || IA64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_

2004-10-29 19:21:04

by Tom Rini

[permalink] [raw]
Subject: [patch 7/8] KGDB support for x86_64


Cc: Andi Kleen <[email protected]>
This adds KGDB support for x86_64.

---

linux-2.6.10-rc1-trini/arch/x86_64/Kconfig.debug | 3
linux-2.6.10-rc1-trini/arch/x86_64/kernel/Makefile | 3
linux-2.6.10-rc1-trini/arch/x86_64/kernel/irq.c | 8
linux-2.6.10-rc1-trini/arch/x86_64/kernel/kgdb.c | 448 +++++++++++++++++++++
linux-2.6.10-rc1-trini/arch/x86_64/kernel/traps.c | 3
linux-2.6.10-rc1-trini/include/asm-x86_64/kgdb.h | 56 ++
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
7 files changed, 518 insertions(+), 5 deletions(-)

diff -puN arch/x86_64/Kconfig.debug~x86_64-lite arch/x86_64/Kconfig.debug
--- linux-2.6.10-rc1/arch/x86_64/Kconfig.debug~x86_64-lite 2004-10-29 11:26:45.744147657 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/Kconfig.debug 2004-10-29 11:26:45.757144605 -0700
@@ -41,7 +41,4 @@ config IOMMU_LEAK
Add a simple leak tracer to the IOMMU code. This is useful when you
are debugging a buggy device driver that leaks IOMMU mappings.

-#config X86_REMOTE_DEBUG
-# bool "kgdb debugging stub"
-
endmenu
diff -puN arch/x86_64/kernel/Makefile~x86_64-lite arch/x86_64/kernel/Makefile
--- linux-2.6.10-rc1/arch/x86_64/kernel/Makefile~x86_64-lite 2004-10-29 11:26:45.746147187 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/Makefile 2004-10-29 11:26:45.757144605 -0700
@@ -10,6 +10,8 @@ obj-y := process.o semaphore.o signal.o
setup64.o bootflag.o e820.o reboot.o warmreboot.o
obj-y += mce.o

+CFLAGS_vsyscall.o := -g0
+
obj-$(CONFIG_MTRR) += ../../i386/kernel/cpu/mtrr/
obj-$(CONFIG_ACPI_BOOT) += acpi/
obj-$(CONFIG_X86_MSR) += msr.o
@@ -27,6 +29,7 @@ obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o
obj-$(CONFIG_SWIOTLB) += swiotlb.o

obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_KGDB) += kgdb.o

obj-y += topology.o
obj-y += intel_cacheinfo.o
diff -puN arch/x86_64/kernel/irq.c~x86_64-lite arch/x86_64/kernel/irq.c
--- linux-2.6.10-rc1/arch/x86_64/kernel/irq.c~x86_64-lite 2004-10-29 11:26:45.748146718 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/irq.c 2004-10-29 11:26:45.757144605 -0700
@@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/seq_file.h>
#include <linux/module.h>
+#include <linux/kgdb.h>
#include <asm/uaccess.h>
#include <asm/io_apic.h>

@@ -103,6 +104,13 @@ asmlinkage unsigned int do_IRQ(struct pt
__do_IRQ(irq, regs);
irq_exit();

+ /*
+ * Do not call breakpoint as on the x86_64 architecture if the
+ * exception tables are not set.
+ */
+ if(CHECK_EXCEPTION_STACK())
+ kgdb_process_breakpoint();
+
return 1;
}

diff -puN /dev/null arch/x86_64/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/kgdb.c 2004-10-29 11:26:45.758144370 -0700
@@ -0,0 +1,448 @@
+/*
+ *
+ * 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
+ */
+/****************************************************************************
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale<[email protected]>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <[email protected]>
+ * Integrated into 2.2.5 kernel by Tigran Aivazian <[email protected]>
+ * X86_64 changes from Andi Kleen's patch merged by Jim Houston
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <asm/kdebug.h>
+
+/* 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;
+}
+
+struct task_struct *__switch_to(struct task_struct *prev_p,
+ struct task_struct *next_p);
+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] = 0;
+ gdb_regs[_PS] = 0;
+ gdb_regs[_PC] = (unsigned long)__switch_to;
+ 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];
+#if 0 /* can't change these */
+ regs->rsp = gdb_regs[_RSP];
+ regs->ss = gdb_regs[_SS];
+ regs->fs = gdb_regs[_FS];
+ regs->gs = gdb_regs[_GS];
+#endif
+
+} /* 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}};
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+ int correctit;
+ int breakbit;
+ unsigned long dr7;
+
+ asm volatile ("movq %%db7, %0\n":"=r" (dr7):);
+ do {
+ unsigned long addr0, addr1, addr2, addr3;
+ asm volatile ("movq %%db0, %0\n"
+ "movq %%db1, %1\n"
+ "movq %%db2, %2\n"
+ "movq %%db3, %3\n":"=r" (addr0), "=r"(addr1),
+ "=r"(addr2), "=r"(addr3):);
+ } while (0);
+ correctit = 0;
+ for (breakno = 0; breakno < 3; breakno++) {
+ breakbit = 2 << (breakno << 1);
+ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 |= breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ dr7 |= (((breakinfo[breakno].len << 2) |
+ breakinfo[breakno].type) << 16) <<
+ (breakno << 2);
+ switch (breakno) {
+ case 0:
+ asm volatile ("movq %0, %%dr0\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 1:
+ asm volatile ("movq %0, %%dr1\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 2:
+ asm volatile ("movq %0, %%dr2\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 3:
+ asm volatile ("movq %0, %%dr3\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+ }
+ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 &= ~breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ }
+ }
+ if (correctit) {
+ asm volatile ("movq %0, %%db7\n"::"r" (dr7));
+ }
+}
+
+int kgdb_remove_hw_break(unsigned long addr)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (breakinfo[i].addr == addr && breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+
+ breakinfo[idx].enabled = 0;
+ return 0;
+}
+
+int kgdb_set_hw_break(unsigned long addr)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (!breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+
+ breakinfo[idx].enabled = 1;
+ breakinfo[idx].type = 1;
+ breakinfo[idx].len = 1;
+ breakinfo[idx].addr = addr;
+ return 0;
+}
+
+int remove_hw_break(unsigned breakno)
+{
+ if (!breakinfo[breakno].enabled) {
+ return -1;
+ }
+ breakinfo[breakno].enabled = 0;
+ return 0;
+}
+
+int set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr)
+{
+ if (breakinfo[breakno].enabled) {
+ return -1;
+ }
+ breakinfo[breakno].enabled = 1;
+ breakinfo[breakno].type = type;
+ breakinfo[breakno].len = len;
+ breakinfo[breakno].addr = addr;
+ return 0;
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ /* Disable hardware debugging while we are in kgdb */
+ asm volatile ("movq %0,%%db7": /* no output */ :"r" (0UL));
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_x86_64vector = eVector;
+ gdb_x86_64errcode = err_code;
+}
+
+int kgdb_arch_handle_exception(int exceptionVector, int signo, int err_code,
+ char *remcomInBuffer, char *remcomOutBuffer,
+ struct pt_regs *linux_regs)
+{
+ unsigned long addr, length;
+ unsigned long breakno, breaktype;
+ char *ptr;
+ int newPC;
+ unsigned long dr6;
+
+ switch (remcomInBuffer[0]) {
+ case 'c':
+ case 's':
+ if (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(remcomOutBuffer, "E00");
+ break;
+ }
+
+ /* 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,smp_processor_id());
+
+ }
+
+ asm volatile ("movq %%db6, %0\n":"=r" (dr6));
+ if (!(dr6 & 0x4000)) {
+ for (breakno = 0; breakno < 4; ++breakno) {
+ if (dr6 & (1 << breakno)) {
+ if (breakinfo[breakno].type == 0) {
+ /* Set restore flag */
+ linux_regs->eflags |= X86_EFLAGS_RF;
+ break;
+ }
+ }
+ }
+ }
+ kgdb_correct_hw_break();
+ asm volatile ("movq %0, %%db6\n"::"r" (0UL));
+
+ return (0);
+
+ case 'Y':
+ ptr = &remcomInBuffer[1];
+ kgdb_hex2long(&ptr, &breakno);
+ ptr++;
+ kgdb_hex2long(&ptr, &breaktype);
+ ptr++;
+ kgdb_hex2long(&ptr, &length);
+ ptr++;
+ kgdb_hex2long(&ptr, &addr);
+ if (set_hw_break(breakno & 0x3, breaktype & 0x3,
+ length & 0x3, addr) == 0) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "ERROR");
+ }
+ break;
+
+ /* Remove hardware breakpoint */
+ case 'y':
+ ptr = &remcomInBuffer[1];
+ kgdb_hex2long(&ptr, &breakno);
+ if (remove_hw_break(breakno & 0x3) == 0) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "ERROR");
+ }
+ break;
+
+ } /* switch */
+ return -1; /* this means that we do not want to exit from the handler */
+}
+
+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 = &per_cpu(init_tss,get_cpu());
+ 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 = hard_smp_processor_id();
+
+ if ((stregs = in_interrupt_stack(regs->rsp, cpu))) {
+ kgdb_mem2hex(intr_desc, buffer, strlen(intr_desc));
+ } else if ((stregs = in_exception_stack(regs->rsp, cpu))) {
+ 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 = hard_smp_processor_id();
+
+ if ((stregs = in_interrupt_stack(regs->rsp, cpu))) {
+ return current;
+ } else if ((stregs = in_exception_stack(regs->rsp, cpu))) {
+ return current;
+ }
+ return NULL;
+}
+
+struct pt_regs *kgdb_shadow_regs(struct pt_regs *regs, int threadid)
+{
+ struct pt_regs *stregs;
+ int cpu = hard_smp_processor_id();
+
+ if ((stregs = in_interrupt_stack(regs->rsp, cpu))) {
+ return stregs;
+ } else if ((stregs = in_exception_stack(regs->rsp, cpu))) {
+ 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 *d = ptr;
+
+ if (cmd == DIE_DEBUG && user_mode(d->regs))
+ return NOTIFY_STOP;
+ else if (cmd == DIE_NMI_IPI) {
+ if (atomic_read(&debugger_active))
+ debugger_nmihook(smp_processor_id(), ptr);
+ } else
+ CHK_DEBUGGER(d->trapnr, d->signr, d->err, d->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)
+{
+ notifier_chain_register(&die_chain, &kgdb_notifier);
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0xcc},
+ .flags = KGDB_HW_BREAKPOINT,
+ .shadowth = 1,
+};
diff -puN arch/x86_64/kernel/traps.c~x86_64-lite arch/x86_64/kernel/traps.c
--- linux-2.6.10-rc1/arch/x86_64/kernel/traps.c~x86_64-lite 2004-10-29 11:26:45.750146248 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/traps.c 2004-10-29 11:26:45.759144136 -0700
@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/debugger.h>

#include <asm/system.h>
#include <asm/uaccess.h>
@@ -642,7 +643,7 @@ asmlinkage void *do_debug(struct pt_regs
tsk->thread.debugreg6 = condition;

/* Mask out spurious TF errors due to lazy TF clearing */
- if (condition & DR_STEP) {
+ if (condition & DR_STEP && !debugger_step) {
/*
* The TF error should be masked out only if the current
* process is not traced and if the TRAP flag has been set
diff -puN /dev/null include/asm-x86_64/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-x86_64/kgdb.h 2004-10-29 11:26:45.759144136 -0700
@@ -0,0 +1,56 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/ptrace.h>
+
+/************************************************************************/
+/* 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
+
+/*
+ * 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 { _RAX,
+ _RDX,
+ _RCX,
+ _RBX,
+ _RSI,
+ _RDI,
+ _RBP,
+ _RSP,
+ _R8,
+ _R9,
+ _R10,
+ _R11,
+ _R12,
+ _R13,
+ _R14,
+ _R15,
+ _PC,
+ _PS,
+ _LASTREG = _PS
+};
+
+/* Number of bytes of registers. */
+#define NUMREGBYTES (_LASTREG*8)
+
+#define BREAKPOINT() asm(" int $3");
+#define BREAK_INSTR_SIZE 1
+
+/*
+ * Call kgdb_process_breakpoint() only if the interrupt and exception
+ * stacks of x86_64 are setup.
+ */
+#define CHECK_EXCEPTION_STACK() ((&per_cpu(init_tss, get_cpu ()))[0].ist[0])
+#endif /* _ASM_KGDB_H_ */
diff -puN lib/Kconfig.debug~x86_64-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~x86_64-lite 2004-10-29 11:26:45.753145544 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:54.317581029 -0700
@@ -115,7 +115,7 @@ endif

config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86 || MIPS32 || IA64 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS32 || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_

2004-10-29 19:22:54

by Tom Rini

[permalink] [raw]
Subject: [patch 8/8] KGDB over ethernet driver


Cc: Jeff Garzik <[email protected]>, Matt Mackall <[email protected]>
This adds a KGDB over ethernet driver that makes use of the netpoll API to
function.

---

linux-2.6.10-rc1-trini/drivers/net/Makefile | 1
linux-2.6.10-rc1-trini/drivers/net/kgdb_eth.c | 132 ++++++++++++++++++++++++++
linux-2.6.10-rc1-trini/kernel/kgdb.c | 9 +
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 8 +
4 files changed, 150 insertions(+)

diff -puN drivers/net/Makefile~eth drivers/net/Makefile
--- linux-2.6.10-rc1/drivers/net/Makefile~eth 2004-10-29 11:26:45.949099534 -0700
+++ linux-2.6.10-rc1-trini/drivers/net/Makefile 2004-10-29 11:26:45.958097421 -0700
@@ -196,3 +196,4 @@ obj-$(CONFIG_HAMRADIO) += hamradio/
obj-$(CONFIG_IRDA) += irda/

obj-$(CONFIG_NETCONSOLE) += netconsole.o
+obj-$(CONFIG_KGDB_ETH) += kgdb_eth.o
diff -puN /dev/null drivers/net/kgdb_eth.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/drivers/net/kgdb_eth.c 2004-10-29 11:26:45.958097421 -0700
@@ -0,0 +1,132 @@
+/*
+ * drivers/net/kgdb_eth.c
+ *
+ * A network interface for GDB.
+ * Based upon 'gdbserial' by David Grothe <[email protected]>
+ * and Scott Foehner <[email protected]>
+ *
+ * Maintainers: Amit S. Kale <[email protected]> and
+ * Tom Rini <[email protected]>
+ *
+ * 2004 (c) Amit S. Kale <[email protected]>
+ * 2004 (c) MontaVista Software, Inc.
+ *
+ * Other folks:
+ * San Mehat <[email protected]>
+ * Robert Walsh <[email protected]>
+ * wangdi <[email protected]>.
+ * Matt Mackall <[email protected]>
+ * Pavel Machek <[email protected]>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/kgdb.h>
+#include <linux/netpoll.h>
+#include <linux/init.h>
+
+#include <asm/atomic.h>
+
+#define IN_BUF_SIZE 512 /* power of 2, please */
+#define OUT_BUF_SIZE 30 /* We don't want to send too big of a packet. */
+
+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;
+static int configured;
+
+static void rx_hook(struct netpoll *np, int port, char *msg, int len);
+
+static struct netpoll np = {
+ .name = "kgdboe",
+ .dev_name = "eth0",
+ .rx_hook = rx_hook,
+ .local_port = 6443,
+ .remote_port = 6442,
+ .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+};
+
+static int eth_getDebugChar(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_flushDebugChar(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_putDebugChar(int chr)
+{
+ out_buf[out_count++] = chr;
+ if (out_count == OUT_BUF_SIZE)
+ eth_flushDebugChar();
+}
+
+static void rx_hook(struct netpoll *np, int port, char *msg, int len)
+{
+ int i;
+
+ np->remote_port = port;
+
+ /* Is this gdb trying to attach? */
+ if (!kgdb_connected)
+ kgdb_schedule_breakpoint();
+
+ for (i = 0; i < len; i++) {
+ if (msg[i] == 3)
+ kgdb_schedule_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);
+ }
+}
+
+/* We must be passed configuration options. */
+static int option_setup(char *opt)
+{
+ configured = !netpoll_parse_options(&np, opt);
+ return 0;
+}
+__setup("kgdboe=", option_setup);
+
+int init_kgdboe(void)
+{
+ if (!configured || netpoll_setup(&np))
+ return 1;
+
+ printk(KERN_INFO "kgdb: debugging over ethernet enabled\n");
+
+ return 0;
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = eth_getDebugChar,
+ .write_char = eth_putDebugChar,
+ .init = init_kgdboe,
+ .flush = eth_flushDebugChar,
+};
diff -puN kernel/kgdb.c~eth kernel/kgdb.c
--- linux-2.6.10-rc1/kernel/kgdb.c~eth 2004-10-29 11:26:45.952098830 -0700
+++ linux-2.6.10-rc1-trini/kernel/kgdb.c 2004-10-29 11:26:45.959097187 -0700
@@ -52,6 +52,7 @@
#include <asm/cacheflush.h>
#include <linux/init.h>
#include <linux/sysrq.h>
+#include <linux/netpoll.h>

extern int pid_max;
extern int pidhash_init_done;
@@ -730,6 +731,10 @@ int kgdb_handle_exception(int exVector,

acquirelock:

+#ifdef CONFIG_KGDB_ETH
+ netpoll_set_trap(1);
+#endif
+
/*
* Interrupts will be restored by the 'trap return' code, except when
* single stepping.
@@ -1144,6 +1149,10 @@ acquirelock:
}

kgdb_exit:
+#ifdef CONFIG_KGDB_ETH
+ netpoll_set_trap(0);
+#endif
+
kgdb_info[processor].debuggerinfo = NULL;
kgdb_info[processor].task = NULL;
procindebug[smp_processor_id()] = 0;
diff -puN lib/Kconfig.debug~eth lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~eth 2004-10-29 11:26:45.954098360 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:26:45.960096952 -0700
@@ -145,6 +145,14 @@ config KGDB_SIBYTE
bool "KGDB: On the Broadcom SWARM serial port"
depends on MIPS && SIBYTE_SB1xxx_SOC

+config KGDB_ETH
+ bool "KGDB: On ethernet"
+ select NETPOLL
+ select NETPOLL_TRAP
+ select NETPOLL_RX
+ help
+ Uses ethernet interface for kgdb.
+
endchoice

config KGDB_SIMPLE_SERIAL
_

2004-10-29 19:26:55

by Tom Rini

[permalink] [raw]
Subject: Re: [patch 1/8] A different KGDB stub for -mm

On Fri, Oct 29, 2004 at 11:32:20AM -0700, Tom Rini wrote:

> Cc: <[email protected]>, <[email protected]>
> The following series of patches adds (or in some cases, replaces) KGDB
> support for i386, ppc32, ia64, x86_64 and mips (both 32 and 64bit
> tested). This version of KGDB can be used either via serial or ethernet.

... Not off to a good start today, forgot to refresh the descs one last
time before sending. Only this and I think 2/8 will go out twice, sorry
for the noise.

--
Tom Rini
http://gate.crashing.org/~trini/

2004-10-29 20:00:52

by Tom Rini

[permalink] [raw]
Subject: Re: [patch 1/8] A different KGDB stub for -mm


> diff -puN kernel/sched.c~core-lite kernel/sched.c
> --- linux-2.6.10-rc1/kernel/sched.c~core-lite 2004-10-29 11:26:42.888818087 -0700
> +++ linux-2.6.10-rc1-trini/kernel/sched.c 2004-10-29 11:26:42.904814331 -0700
[snip]
> @@ -4567,6 +4568,12 @@ void __might_sleep(char *file, int line)
> #if defined(in_atomic)
> static unsigned long prev_jiffy; /* ratelimiting */
>
> + if (atomic_read(&debugger_active))
> + return;
> +
> + if (atomic_read(&debugger_active))
> + return;
> +
> if ((in_atomic() || irqs_disabled()) &&
> system_state == SYSTEM_RUNNING) {
> if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)

Olaf Hering pointed this out to me privately. Obvious patch follows.

(And, er, I fogot this on all of the other patches too, as I said, not a
good start to the day...)

Signed-off-by: Tom Rini <[email protected]>

diff -u linux-2.6.10-rc1/kernel/sched.c linux-2.6.10-rc1/kernel/sched.c
--- linux-2.6.10-rc1/kernel/sched.c
+++ linux-2.6.10-rc1/kernel/sched.c
@@ -4571,9 +4571,6 @@
if (atomic_read(&debugger_active))
return;

- if (atomic_read(&debugger_active))
- return;
-
if ((in_atomic() || irqs_disabled()) &&
system_state == SYSTEM_RUNNING) {
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)

--
Tom Rini
http://gate.crashing.org/~trini/

2004-10-29 23:24:32

by Andi Kleen

[permalink] [raw]
Subject: Re: [patch 7/8] KGDB support for x86_64

On Fri, Oct 29, 2004 at 11:34:21AM -0700, Tom Rini wrote:
>
> Cc: Andi Kleen <[email protected]>
> This adds KGDB support for x86_64.

Can you please explain what the additional hooks are good for?


> +++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/Makefile 2004-10-29 11:26:45.757144605 -0700
> @@ -10,6 +10,8 @@ obj-y := process.o semaphore.o signal.o
> setup64.o bootflag.o e820.o reboot.o warmreboot.o
> obj-y += mce.o
>
> +CFLAGS_vsyscall.o := -g0
> +

What's this?


> +#include <linux/kgdb.h>
> #include <asm/uaccess.h>
> #include <asm/io_apic.h>
>
> @@ -103,6 +104,13 @@ asmlinkage unsigned int do_IRQ(struct pt
> __do_IRQ(irq, regs);
> irq_exit();
>
> + /*
> + * Do not call breakpoint as on the x86_64 architecture if the
> + * exception tables are not set.
> + */
> + if(CHECK_EXCEPTION_STACK())
> + kgdb_process_breakpoint();
> +

And that? Why do you need to check breakpoints in interrupts?

> #include <linux/moduleparam.h>
> +#include <linux/debugger.h>
>
> #include <asm/system.h>
> #include <asm/uaccess.h>
> @@ -642,7 +643,7 @@ asmlinkage void *do_debug(struct pt_regs
> tsk->thread.debugreg6 = condition;
>
> /* Mask out spurious TF errors due to lazy TF clearing */
> - if (condition & DR_STEP) {
> + if (condition & DR_STEP && !debugger_step) {

This also looks bogus. What is this exactly for and why is it not
handled through the debug chain?


-Andi

2004-10-30 19:05:28

by Tom Rini

[permalink] [raw]
Subject: Re: [patch 3/8] KGDB support for ppc32

On Fri, Oct 29, 2004 at 11:33:29AM -0700, Tom Rini wrote:
>
> Cc: Matt Porter <[email protected]>
> This adds KGDB support for ppc32 and was done by myself. Note that this
> currently doesn't work on 40x || BOOKE, but that problem is more generic (the
> current ppc stub doesn't work either) and Matt Porter is close to having a
> tested solution now.

As Matt has posted his work which makes KGDB functional again, the
following small patch is needed on top of this to make 40x || BOOKE work
with this stub.

Signed-off-by: Tom Rini <[email protected]>

linux-2.6.10-rc1/arch/ppc/kernel/kgdb.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
--- linux-2.6.10-rc1/arch/ppc/kernel/kgdb.c
+++ linux-2.6.10-rc1/arch/ppc/kernel/kgdb.c
@@ -54,7 +54,7 @@
{ 0x0d00, 0x04 /* SIGILL */ }, /* reserved */
{ 0x0e00, 0x04 /* SIGILL */ }, /* reserved */
{ 0x0f00, 0x04 /* SIGILL */ }, /* reserved */
- { 0x2000, 0x05 /* SIGTRAP */}, /* debug */
+ { 0x2002, 0x05 /* SIGTRAP */}, /* debug */
#else
{ 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */
{ 0x0300, 0x0b /* SIGSEGV */ }, /* address error (store) */
@@ -240,8 +240,8 @@
if (remcom_in_buffer[0] == 's')
{
#if defined (CONFIG_40x) || defined(CONFIG_BOOKE)
+ mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC);
linux_regs->msr |= MSR_DE;
- current->thread.dbcr0 |= (DBCR0_IDM | DBCR0_IC);
#else
linux_regs->msr |= MSR_SE;
#endif

--
Tom Rini
http://gate.crashing.org/~trini/