2005-11-10 16:39:13

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 00/15] KGDB Support

[ Using a different script that doesn't do individual patch CC's, but should
send to lkml this time ]

I'm once again submitting the KGDB found at
http://sourceforge.net/projects/kgdb for inclusion in the kernel. Right now,
I'm mainly asking for comments again. This series is against 2.6.14, and that
means it will fail, probably voilently, against 2.6.15-rc1 when it comes out
due to the PowerPC merge (aside, Paul, I'll take care of merging the old kgdb
;)), but I'd still like to get this into Andrew's tree at somepoint during
2.6.15 for future inclusion in Linus' tree, and will take back up tracking the
-rc releases at least.

This version of KGDB is designed so that as much code as possible is done in
a core file shared by all architectures, and with I/O (ie
8250 serial, custom uart, ethernet) being common when possible and modular.
The rough splitup of this is that 95% of the interaction with KGDB is done in
files common to all implementations with a small set of architecture specific
things (setjmp/longjmp, actually formatting registers for GDB, single
stepping).

Much of how to use this version (it is slightly different from George
Anzinger's version) is written up in DocBook and viewable that way. I've
tried to do this in as clean a way as possible (notifier when possible for
example) so that it's as minimally instrusive as possible. But, in order to
allow for KGDB to be used very early on (some folks argue this is critical,
some argue it's not) there's a few hooks so we can know if pidhash_init has
been run, or to try and have KGDB break in as soon as possible, if it can't
right then (such as on x86_64 where we're still trying to deal with early
per-cpu things so we can work early).

Comments? Thanks!

--
Tom


2005-11-10 16:39:43

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 01/15] KGDB: core infrastructure

This is the core of the KGDB stub. It provides all of the common code,
documentation and basic Kconfig changes, as well as the common hooks.

Documentation/DocBook/Makefile | 2
Documentation/DocBook/kgdb.tmpl | 224 ++++
MAINTAINERS | 9
include/linux/kgdb.h | 261 +++++
kernel/Makefile | 1
kernel/kgdb.c | 1819 ++++++++++++++++++++++++++++++++++++++++
kernel/pid.c | 11
kernel/sched.c | 4
lib/Kconfig.debug | 53 +
9 files changed, 2382 insertions(+), 2 deletions(-)

Index: linux-2.6.14/Documentation/DocBook/kgdb.tmpl
===================================================================
--- /dev/null
+++ linux-2.6.14/Documentation/DocBook/kgdb.tmpl
@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<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-2005</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_ONLY_MODULES</symbol>.
+ This means that you will only be able to use KGDB after loading a
+ kernel module that defines how you want to be able to talk with
+ KGDB. There are two other choices (more on some architectures) that
+ can be enabled as modules later, if not picked here.
+ </para>
+ <para>The first of these is <symbol>CONFIG_KGDB_8250_NOMODULE</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 of these choices 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.
+ </para>
+ </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=<io or mmio>,<address>,<baud rate>,<irq></constant>
+ before <constant>kgdbwait</constant>. The values <constant>io</constant>
+ or <constant>mmio</constant> refer to if the address being passed
+ next needs to be memory mapped (<constant>mmio</constant>) or not.
+ The <constant>address</constant> must be passed in hex, and
+ <constant>baud rate</constant> and <constant>irq</constant> are
+ base-10. The supported values for <constant>baud rate</constant> 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=io,3f8,115200,3</constant>.
+ On IA64 this could also be:
+ <constant>kgdb8250=mmio,0xc0000000ff5e0000,115200,74</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>
+ There are a few flags which must be set on every architecture in
+ their &lt;asm/kgdb.h&gt; file. These are:
+ <itemizedlist>
+ <listitem>
+ <para>
+ NUMREGBYTES: The size in bytes of all of the registers, so
+ that we can ensure they will all fit into a packet.
+ </para>
+ <para>
+ BUFMAX: The size in bytes of the buffer GDB will read into.
+ This must be larger than NUMREGBYTES.
+ </para>
+ <para>
+ CACHE_FLUSH_IS_SAFE: Set to one if it always safe to call
+ flush_cache_range or flush_icache_range. On some architectures,
+ these functions may not be safe to call on SMP since we keep other
+ CPUs in a holding pattern.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ There are also the following functions for the common backend,
+ found in kernel/kgdb.c that must be supplied by the
+ architecture-specific backend. No weak version of these is provided.
+ </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>
+ <chapter id="DriverSpecificFunctions">
+ <title>Driver-Specific Functions</title>
+ <para>
+ Some of the I/O drivers have additional functions that can be
+ called, that are specific to the driver. Calls from other places
+ to these functions must be wrapped in #ifdefs for the driver in
+ question.
+ </para>
+!Idrivers/serial/8250_kgdb.c
+ </chapter>
+</book>
Index: linux-2.6.14/Documentation/DocBook/Makefile
===================================================================
--- linux-2.6.14.orig/Documentation/DocBook/Makefile
+++ linux-2.6.14/Documentation/DocBook/Makefile
@@ -10,7 +10,7 @@ DOCBOOKS := wanbook.xml z8530book.xml mc
kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
procfs-guide.xml writing_usb_driver.xml \
sis900.xml kernel-api.xml journal-api.xml lsm.xml usb.xml \
- gadget.xml libata.xml mtdnand.xml librs.xml
+ gadget.xml libata.xml mtdnand.xml librs.xml kgdb.xml

###
# The build process is as follows (targets):
Index: linux-2.6.14/include/linux/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/linux/kgdb.h
@@ -0,0 +1,261 @@
+/*
+ * include/linux/kgdb.h
+ *
+ * This provides the hooks and functions that KGDB needs to share between
+ * the core, I/O and arch-specific portions.
+ *
+ * Author: Amit Kale <[email protected]> and
+ * Tom Rini <[email protected]>
+ *
+ * 2001-2004 (c) Amit S. Kale and 2003-2005 (c) MontaVista Software, Inc.
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+#ifdef __KERNEL__
+#ifndef _KGDB_H_
+#define _KGDB_H_
+
+#include <asm/atomic.h>
+
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#include <linux/serial_8250.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+struct tasklet_struct;
+struct pt_regs;
+struct task_struct;
+struct uart_port;
+
+
+/* To enter the debugger explicitly. */
+extern void breakpoint(void);
+extern int kgdb_connected;
+extern int kgdb_may_fault;
+extern struct tasklet_struct kgdb_tasklet_breakpoint;
+
+extern atomic_t kgdb_setting_breakpoint;
+extern atomic_t cpu_doing_single_step;
+
+extern 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;
+};
+
+/* The maximum number of KGDB I/O modules that can be loaded */
+#define MAX_KGDB_IO_HANDLERS 3
+
+#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.
+ * @remcom_in_buffer: The buffer of the packet we have read.
+ * @remcom_out_buffer: The buffer, of %BUFMAX to write a packet into.
+ * @regs: The &struct pt_regs of the current process.
+ *
+ * This function MUST handle the 'c' and 's' command packets,
+ * as well packets to set / remove a hardware breakpoint, if used.
+ * If there are additional packets which the hardware needs to handle,
+ * they are handled here. The code should return -1 if it wants to
+ * process more packets, and a %0 or %1 if it wants to exit from the
+ * kgdb hook.
+ */
+extern int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+ char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *regs);
+
+#ifndef JMP_REGS_ALIGNMENT
+#define JMP_REGS_ALIGNMENT
+#endif
+
+extern unsigned long kgdb_fault_jmp_regs[];
+
+/**
+ * kgdb_fault_setjmp - Store state in case we fault.
+ * @curr_context: An array to store state into.
+ *
+ * Certain functions may try and access memory, and in doing so may
+ * cause a fault. When this happens, we trap it, restore state to
+ * this call, and let ourself know that something bad has happened.
+ */
+extern asmlinkage int kgdb_fault_setjmp(unsigned long *curr_context);
+
+/**
+ * kgdb_fault_longjmp - Restore state when we have faulted.
+ * @curr_context: The previously stored state.
+ *
+ * When something bad does happen, this function is called to
+ * restore the known good state, and set the return value to 1, so
+ * we know something bad happened.
+ */
+extern asmlinkage void kgdb_fault_longjmp(unsigned long *curr_context);
+
+/* Optional functions. */
+extern int kgdb_arch_init(void);
+extern void kgdb_disable_hw_debug(struct pt_regs *regs);
+extern void kgdb_post_master_code(struct pt_regs *regs, int e_vector,
+ int err_code);
+extern void kgdb_roundup_cpus(unsigned long flags);
+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.
+ * @pre_exception: Pointer to a function that will do any prep work for
+ * the I/O driver.
+ * @post_exception: Pointer to a function that will do any cleanup work
+ * for the I/O driver.
+ *
+ * The @init and @late_init function pointers allow for an I/O driver
+ * such as a serial driver to fully initialize the port with @init and
+ * be called very early, yet safely call request_irq() later in the boot
+ * sequence.
+ *
+ * @init is allowed to return a non-0 return value to indicate failure.
+ * If this is called early on, then KGDB will try again when it would call
+ * @late_init. If it has failed later in boot as well, the user will be
+ * notified.
+ */
+struct kgdb_io {
+ int (*read_char) (void);
+ void (*write_char) (u8);
+ void (*flush) (void);
+ int (*init) (void);
+ void (*late_init) (void);
+ void (*pre_exception) (void);
+ void (*post_exception) (void);
+};
+
+extern struct kgdb_io kgdb_io_ops;
+extern struct kgdb_arch arch_kgdb_ops;
+extern int kgdb_initialized;
+
+extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops);
+extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
+
+extern void __init kgdb8250_add_port(int i, struct uart_port *serial_req);
+extern void __init kgdb8250_add_platform_port(int i, struct plat_serial8250_port *serial_req);
+
+extern int kgdb_hex2long(char **ptr, long *long_val);
+extern char *kgdb_mem2hex(char *mem, char *buf, int count);
+extern char *kgdb_hex2mem(char *buf, char *mem, int count);
+extern int kgdb_get_mem(char *addr, unsigned char *buf, int count);
+extern int kgdb_set_mem(char *addr, unsigned char *buf, int count);
+extern int kgdb_handle_exception(int ex_vector, int signo, int err_code,
+ struct pt_regs *regs);
+extern void kgdb_nmihook(int cpu, void *regs);
+extern int debugger_step;
+extern atomic_t debugger_active;
+#else
+/* Stubs for when KGDB is not set. */
+static const atomic_t debugger_active = ATOMIC_INIT(0);
+#endif /* CONFIG_KGDB */
+#endif /* _KGDB_H_ */
+#endif /* __KERNEL__ */
Index: linux-2.6.14/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/kernel/kgdb.c
@@ -0,0 +1,1819 @@
+/*
+ * kernel/kgdb.c
+ *
+ * Maintainer: Tom Rini <[email protected]>
+ *
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002-2004 Timesys Corporation
+ * Copyright (C) 2003-2004 Amit S. Kale <[email protected]>
+ * Copyright (C) 2004 Pavel Machek <[email protected]>
+ * Copyright (C) 2004-2005 Tom Rini <[email protected]>
+ * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd.
+ * Copyright (C) 2005 Wind River System, Inc.
+ *
+ * Contributors at various stages not listed above:
+ * Jason Wessel ( [email protected] )
+ * George Anzinger <[email protected]>
+ * Anurekh Saxena ([email protected])
+ * Lake Stevens Instrument Division (Glenn Engel)
+ * Jim Kingdon, Cygnus Support.
+ *
+ * Original KGDB stub: David Grothe <[email protected]>,
+ * Tigran Aivazian <[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/interrupt.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>
+#include <linux/console.h>
+#include <asm/byteorder.h>
+
+extern int pid_max;
+extern int pidhash_init_done;
+
+/* How many times to count all of the waiting CPUs */
+#define ROUNDUP_WAIT 640000 /* Arbitrary, increase if needed. */
+#define BUF_THREAD_ID_SIZE 16
+
+/*
+ * kgdb_initialized with a value of 1 indicates that kgdb is setup and is
+ * all ready to serve breakpoints and other kernel exceptions. A value of
+ * -1 indicates that we have tried to initialize early, and need to try
+ * again later.
+ */
+int kgdb_initialized;
+/* Is a host GDB connected to us? */
+int kgdb_connected;
+/* Could we be about to try and access a bad memory location? If so we
+ * also need to flag this has happend. */
+int kgdb_may_fault;
+/* All the KGDB handlers are installed */
+int kgdb_from_module_registered = 0;
+
+/* We provide a kgdb_io_ops structure that may be overriden. */
+struct kgdb_io __attribute__ ((weak)) kgdb_io_ops;
+
+static struct kgdb_io kgdb_io_ops_prev[MAX_KGDB_IO_HANDLERS];
+static int kgdb_io_handler_cnt = 0;
+
+/* Export the following symbols for use with kernel modules */
+EXPORT_SYMBOL(kgdb_io_ops);
+EXPORT_SYMBOL(kgdb_tasklet_breakpoint);
+EXPORT_SYMBOL(kgdb_connected);
+EXPORT_SYMBOL(kgdb_register_io_module);
+EXPORT_SYMBOL(kgdb_unregister_io_module);
+EXPORT_SYMBOL(debugger_active);
+
+/*
+ * 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;
+EXPORT_SYMBOL(kgdb_setting_breakpoint);
+struct task_struct *kgdb_usethread, *kgdb_contthread;
+
+int debugger_step;
+atomic_t debugger_active;
+
+/* Our I/O buffers. */
+static char remcom_in_buffer[BUFMAX];
+static char remcom_out_buffer[BUFMAX];
+/* Storage for the registers, in GDB format. */
+static unsigned long gdb_regs[(NUMREGBYTES + sizeof(unsigned long) - 1) /
+ sizeof(unsigned long)];
+/* Storage of registers for handling a fault. */
+unsigned long kgdb_fault_jmp_regs[NUMCRITREGBYTES / sizeof(unsigned long)]
+ JMP_REGS_ALIGNMENT;
+
+struct debuggerinfo_struct {
+ void *debuggerinfo;
+ struct task_struct *task;
+} kgdb_info[NR_CPUS];
+
+/* to keep track of the CPU which is doing the single stepping*/
+atomic_t cpu_doing_single_step = ATOMIC_INIT(-1);
+
+/**
+ * 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.
+ * @e_vector: Original error vector.
+ * @err_code: Original error code.
+ *
+ * This is needed on architectures which support SMP and KGDB.
+ * This function is called after all the slave cpus have been put
+ * to a know spin state and the master CPU has control over KGDB.
+ */
+
+void __attribute__ ((weak))
+ kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code)
+{
+}
+
+/**
+ * kgdb_roundup_cpus - Get other CPUs into a holding pattern
+ * @flags: Current IRQ state
+ *
+ * On SMP systems, we need to get the attention of the other CPUs
+ * and get them be in a known state. This should do what is needed
+ * to get the other CPUs to call kgdb_wait(). Note that on some arches,
+ * the NMI approach is not used for rounding up all the CPUs. For example,
+ * in case of MIPS, smp_call_function() is used to roundup CPUs. In
+ * this case, we have to make sure that interrupts are enabled before
+ * calling smp_call_function(). The argument to this function is
+ * the flags that will be used when restoring the interrupts. There is
+ * local_irq_save() call before kgdb_roundup_cpus().
+ */
+void __attribute__ ((weak))
+ kgdb_roundup_cpus(unsigned long flags)
+{
+}
+
+/**
+ * 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;
+ if (!kgdb_io_ops.read_char)
+ return;
+ do {
+ /* Spin and wait around for the start character, ignore all
+ * other characters */
+ while ((ch = (kgdb_io_ops.read_char())) != '$') ;
+ kgdb_connected = 1;
+ checksum = 0;
+ xmitcsum = -1;
+
+ count = 0;
+
+ /* now, read until a # or end of buffer is found */
+ while (count < (BUFMAX - 1)) {
+ ch = kgdb_io_ops.read_char();
+ if (ch == '#')
+ break;
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+ buffer[count] = 0;
+
+ if (ch == '#') {
+ xmitcsum = hex(kgdb_io_ops.read_char()) << 4;
+ xmitcsum += hex(kgdb_io_ops.read_char());
+
+ if (checksum != xmitcsum)
+ /* failed checksum */
+ kgdb_io_ops.write_char('-');
+ else
+ /* successful transfer */
+ kgdb_io_ops.write_char('+');
+ if (kgdb_io_ops.flush)
+ kgdb_io_ops.flush();
+ }
+ } while (checksum != xmitcsum);
+}
+
+/*
+ * Send the packet in buffer.
+ * Check for gdb connection if asked for.
+ */
+static void put_packet(char *buffer)
+{
+ unsigned char checksum;
+ int count;
+ char ch;
+
+ if (!kgdb_io_ops.write_char)
+ return;
+ /* $<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)
+{
+ kgdb_may_fault = 1;
+ if ((kgdb_fault_setjmp(kgdb_fault_jmp_regs)) != 0) {
+ kgdb_may_fault = 0;
+ return ERR_PTR(-EINVAL);
+ }
+ /* Accessing some registers in a single load instruction is
+ * required to avoid bad side effects for some I/O registers.
+ */
+ if ((count == 2) && (((long)mem & 1) == 0)) {
+ unsigned short tmp_s = *(unsigned short *)mem;
+ mem += 2;
+#ifdef __BIG_ENDIAN
+ *buf++ = hexchars[(tmp_s >> 12) & 0xf];
+ *buf++ = hexchars[(tmp_s >> 8) & 0xf];
+ *buf++ = hexchars[(tmp_s >> 4) & 0xf];
+ *buf++ = hexchars[tmp_s & 0xf];
+#else
+ *buf++ = hexchars[(tmp_s >> 4) & 0xf];
+ *buf++ = hexchars[tmp_s & 0xf];
+ *buf++ = hexchars[(tmp_s >> 12) & 0xf];
+ *buf++ = hexchars[(tmp_s >> 8) & 0xf];
+#endif
+ } else if ((count == 4) && (((long)mem & 3) == 0)) {
+ unsigned long tmp_l = *(unsigned int *)mem;
+ mem += 4;
+#ifdef __BIG_ENDIAN
+ *buf++ = hexchars[(tmp_l >> 28) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 24) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 20) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 16) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 12) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 8) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 4) & 0xf];
+ *buf++ = hexchars[tmp_l & 0xf];
+#else
+ *buf++ = hexchars[(tmp_l >> 4) & 0xf];
+ *buf++ = hexchars[tmp_l & 0xf];
+ *buf++ = hexchars[(tmp_l >> 12) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 8) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 20) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 16) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 28) & 0xf];
+ *buf++ = hexchars[(tmp_l >> 24) & 0xf];
+#endif
+#ifdef CONFIG_64BIT
+ } else if ((count == 8) && (((long)mem & 7) == 0)) {
+ unsigned long long tmp_ll = *(unsigned long long *)mem;
+ mem += 8;
+#ifdef __BIG_ENDIAN
+ *buf++ = hexchars[(tmp_ll >> 60) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 56) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 52) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 48) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 44) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 40) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 36) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 32) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 28) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 24) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 20) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 16) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 12) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 8) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 4) & 0xf];
+ *buf++ = hexchars[tmp_ll & 0xf];
+#else
+ *buf++ = hexchars[(tmp_ll >> 4) & 0xf];
+ *buf++ = hexchars[tmp_ll & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 12) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 8) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 20) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 16) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 28) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 24) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 36) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 32) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 44) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 40) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 52) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 48) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 60) & 0xf];
+ *buf++ = hexchars[(tmp_ll >> 56) & 0xf];
+#endif
+#endif
+ } else {
+ while (count-- > 0) {
+ unsigned char ch = *mem++;
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch & 0xf];
+ }
+ }
+ kgdb_may_fault = 0;
+ *buf = 0;
+ return (buf);
+}
+
+/*
+ * Copy the binary array pointed to by buf into mem. Fix $, #, and
+ * 0x7d escaped with 0x7d. Return a pointer to the character after
+ * the last byte written.
+ */
+static char *kgdb_ebin2mem(char *buf, char *mem, int count)
+{
+ kgdb_may_fault = 1;
+ if ((kgdb_fault_setjmp(kgdb_fault_jmp_regs)) != 0) {
+ kgdb_may_fault = 0;
+ return ERR_PTR(-EINVAL);
+ }
+ for (; count > 0; count--, buf++) {
+ if (*buf == 0x7d)
+ *mem++ = *(++buf) ^ 0x20;
+ else
+ *mem++ = *buf;
+ }
+ kgdb_may_fault = 0;
+ return mem;
+}
+
+/*
+ * convert the hex array pointed to by buf into binary to be placed in mem
+ * return a pointer to the character AFTER the last byte written
+ * May return an error.
+ */
+char *kgdb_hex2mem(char *buf, char *mem, int count)
+{
+ kgdb_may_fault = 1;
+ if ((kgdb_fault_setjmp(kgdb_fault_jmp_regs)) != 0) {
+ kgdb_may_fault = 0;
+ return ERR_PTR(-EINVAL);
+ }
+ if ((count == 2) && (((long)mem & 1) == 0)) {
+ unsigned short tmp_s = 0;
+#ifdef __BIG_ENDIAN
+ tmp_s |= hex(*buf++) << 12;
+ tmp_s |= hex(*buf++) << 8;
+ tmp_s |= hex(*buf++) << 4;
+ tmp_s |= hex(*buf++);
+#else
+ tmp_s |= hex(*buf++) << 4;
+ tmp_s |= hex(*buf++);
+ tmp_s |= hex(*buf++) << 12;
+ tmp_s |= hex(*buf++) << 8;
+#endif
+ *(unsigned short *)mem = tmp_s;
+ mem += 2;
+ } else if ((count == 4) && (((long)mem & 3) == 0)) {
+ unsigned long tmp_l = 0;
+#ifdef __BIG_ENDIAN
+ tmp_l |= hex(*buf++) << 28;
+ tmp_l |= hex(*buf++) << 24;
+ tmp_l |= hex(*buf++) << 20;
+ tmp_l |= hex(*buf++) << 16;
+ tmp_l |= hex(*buf++) << 12;
+ tmp_l |= hex(*buf++) << 8;
+ tmp_l |= hex(*buf++) << 4;
+ tmp_l |= hex(*buf++);
+#else
+ tmp_l |= hex(*buf++) << 4;
+ tmp_l |= hex(*buf++);
+ tmp_l |= hex(*buf++) << 12;
+ tmp_l |= hex(*buf++) << 8;
+ tmp_l |= hex(*buf++) << 20;
+ tmp_l |= hex(*buf++) << 16;
+ tmp_l |= hex(*buf++) << 28;
+ tmp_l |= hex(*buf++) << 24;
+#endif
+ *(unsigned long *)mem = tmp_l;
+ mem += 4;
+ } else {
+ int i;
+ for (i = 0; i < count; i++) {
+ unsigned char ch = hex(*buf++) << 4;
+ ch |= hex(*buf++);
+ *mem++ = ch;
+ }
+ }
+ kgdb_may_fault = 0;
+ return (mem);
+}
+
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+int kgdb_hex2long(char **ptr, long *long_val)
+{
+ int hex_val, num = 0;
+
+ *long_val = 0;
+
+ while (**ptr) {
+ hex_val = hex(**ptr);
+ if (hex_val >= 0) {
+ *long_val = (*long_val << 4) | hex_val;
+ num++;
+ } else
+ break;
+
+ (*ptr)++;
+ }
+
+ return (num);
+}
+
+/* Write memory due to an 'M' or 'X' packet. */
+static char *write_mem_msg(int binary)
+{
+ char *ptr = &remcom_in_buffer[1];
+ unsigned long addr, length;
+
+ if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' &&
+ kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') {
+ if (binary)
+ ptr = kgdb_ebin2mem(ptr, (char *)addr, length);
+ else
+ ptr = kgdb_hex2mem(ptr, (char *)addr, length);
+ if (CACHE_FLUSH_IS_SAFE)
+ flush_icache_range(addr, addr + length + 1);
+ if (IS_ERR(ptr))
+ return ptr;
+ return NULL;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+static inline char *pack_hex_byte(char *pkt, int byte)
+{
+ *pkt++ = hexchars[(byte >> 4) & 0xf];
+ *pkt++ = hexchars[(byte & 0xf)];
+ return pkt;
+}
+
+static inline void error_packet(char *pkt, int error)
+{
+ error = -error;
+ pkt[0] = 'E';
+ pkt[1] = hexchars[(error / 10)];
+ pkt[2] = hexchars[(error % 10)];
+ pkt[3] = '\0';
+}
+
+static char *pack_threadid(char *pkt, 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;
+ int i = 4;
+
+ scan = (unsigned char *)id;
+ while (i--)
+ *scan++ = 0;
+ *scan++ = (value >> 24) & 0xff;
+ *scan++ = (value >> 16) & 0xff;
+ *scan++ = (value >> 8) & 0xff;
+ *scan++ = (value & 0xff);
+}
+
+static struct task_struct *getthread(struct pt_regs *regs, int tid)
+{
+ if (!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)
+ return idle_task(tid - pid_max);
+
+ if (!tid)
+ return NULL;
+
+ return find_task_by_pid(tid);
+}
+
+#ifdef CONFIG_SMP
+static void kgdb_wait(struct pt_regs *regs)
+{
+ unsigned long flags;
+ int processor;
+
+ local_irq_save(flags);
+ processor = smp_processor_id();
+ kgdb_info[processor].debuggerinfo = regs;
+ kgdb_info[processor].task = current;
+ procindebug[processor] = 1;
+
+ /* 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 (CACHE_FLUSH_IS_SAFE) {
+ if (current->mm && addr < TASK_SIZE)
+ flush_cache_range(current->mm->mmap_cache, addr,
+ addr + BREAK_INSTR_SIZE);
+ else
+ flush_icache_range(addr, addr + BREAK_INSTR_SIZE);
+ }
+ kgdb_break[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 (CACHE_FLUSH_IS_SAFE && current->mm &&
+ addr < TASK_SIZE)
+ flush_cache_range(current->mm->mmap_cache,
+ addr, addr + BREAK_INSTR_SIZE);
+ else if (CACHE_FLUSH_IS_SAFE)
+ flush_icache_range(addr,
+ addr + BREAK_INSTR_SIZE);
+ kgdb_break[i].state = bp_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 (CACHE_FLUSH_IS_SAFE && current->mm &&
+ addr < TASK_SIZE)
+ flush_cache_range(current->mm->mmap_cache,
+ addr, addr + BREAK_INSTR_SIZE);
+ else if (CACHE_FLUSH_IS_SAFE)
+ flush_icache_range(addr,
+ addr + BREAK_INSTR_SIZE);
+ }
+ kgdb_break[i].state = bp_disabled;
+ }
+
+ /* Clear hardware breakpoints. */
+ kgdb_remove_all_hw_break();
+
+ return 0;
+}
+
+static inline int shadow_pid(int realpid)
+{
+ if (realpid) {
+ return realpid;
+ }
+ return pid_max + smp_processor_id();
+}
+
+static char gdbmsgbuf[BUFMAX + 1];
+static void kgdb_msg_write(const char *s, int len)
+{
+ int i;
+ int wcount;
+ char *bufptr;
+
+ /* 'O'utput */
+ gdbmsgbuf[0] = 'O';
+
+ /* Fill and send buffers... */
+ while (len > 0) {
+ bufptr = gdbmsgbuf + 1;
+
+ /* Calculate how many this time */
+ if ((len << 1) > (BUFMAX - 2))
+ wcount = (BUFMAX - 2) >> 1;
+ else
+ wcount = len;
+
+ /* Pack in hex chars */
+ for (i = 0; i < wcount; i++)
+ bufptr = pack_hex_byte(bufptr, s[i]);
+ *bufptr = '\0';
+
+ /* Move up */
+ s += wcount;
+ len -= wcount;
+
+ /* Write packet */
+ put_packet(gdbmsgbuf);
+ }
+}
+
+/*
+ * This function does all command procesing for interfacing to gdb.
+ *
+ * Locking hierarchy:
+ * interface locks, if any (begin_session)
+ * kgdb lock (debugger_active)
+ *
+ * Note that since we can be in here prior to our cpumask being filled
+ * out, we err on the side of caution and loop over NR_CPUS instead
+ * of a for_each_online_cpu.
+ *
+ */
+int kgdb_handle_exception(int ex_vector, int signo, int err_code,
+ struct pt_regs *linux_regs)
+{
+ unsigned long length, addr;
+ char *ptr;
+ unsigned long flags;
+ unsigned 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, all_cpus_synced = 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:
+
+ /* Call the I/O drivers pre_exception routine if the I/O
+ * driver defined one
+ */
+ if (kgdb_io_ops.pre_exception)
+ kgdb_io_ops.pre_exception();
+
+ /*
+ * 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();
+
+ if (atomic_read(&cpu_doing_single_step) != -1 &&
+ atomic_read(&cpu_doing_single_step) != procid)
+ udelay(1);
+ }
+
+ /*
+ * Don't enter if the last instance of the exception handler wanted to
+ * come into the debugger again.
+ */
+ if (atomic_read(&cpu_doing_single_step) != -1 &&
+ atomic_read(&cpu_doing_single_step) != procid) {
+ atomic_set(&debugger_active, 0);
+ local_irq_restore(flags);
+ goto acquirelock;
+ }
+
+ kgdb_info[processor].debuggerinfo = linux_regs;
+ kgdb_info[processor].task = current;
+
+ kgdb_disable_hw_debug(linux_regs);
+
+ if (!debugger_step || !kgdb_contthread)
+ for (i = 0; i < NR_CPUS; i++)
+ spin_lock(&slavecpulocks[i]);
+
+ /* Make sure we get the other CPUs */
+ if (!debugger_step || !kgdb_contthread)
+ kgdb_roundup_cpus(flags);
+
+ /* spin_lock code is good enough as a barrier so we don't
+ * need one here */
+ procindebug[smp_processor_id()] = 1;
+
+ /* Wait a reasonable time for the other CPUs to be notified and
+ * be waiting for us. Very early on this could be imperfect
+ * as num_online_cpus() could be 0.*/
+ for (i = 0; i < ROUNDUP_WAIT; i++) {
+ int cpu, num = 0;
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
+ if (procindebug[cpu])
+ num++;
+ }
+ if (num >= num_online_cpus()) {
+ all_cpus_synced = 1;
+ break;
+ }
+ }
+
+ /* Clear the out buffer. */
+ memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
+
+ /* Master processor is completely in the debugger */
+ kgdb_post_master_code(linux_regs, ex_vector, err_code);
+
+ debugger_step = 0;
+ kgdb_contthread = NULL;
+
+ if (kgdb_connected) {
+ /* If we're still unable to roundup all of the CPUs,
+ * send an 'O' packet informing the user again. */
+ if (!all_cpus_synced)
+ kgdb_msg_write("Not all CPUs have been synced for "
+ "KGDB\n", 39);
+ /* Reply to host that an exception has occurred */
+ ptr = remcom_out_buffer;
+ *ptr++ = 'T';
+ *ptr++ = hexchars[(signo >> 4) % 16];
+ *ptr++ = hexchars[signo % 16];
+ ptr += strlen(strcpy(ptr, "thread:"));
+ int_to_threadref(&thref, shadow_pid(current->pid));
+ ptr = pack_threadid(ptr, &thref);
+ *ptr++ = ';';
+
+ put_packet(remcom_out_buffer);
+ }
+
+ kgdb_usethread = kgdb_info[processor].task;
+ kgdb_usethreadid = shadow_pid(kgdb_info[processor].task->pid);
+
+ while (kgdb_io_ops.read_char) {
+ char *bpt_type;
+ error = 0;
+
+ /* Clear the out buffer. */
+ memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
+
+ get_packet(remcom_in_buffer);
+
+ switch (remcom_in_buffer[0]) {
+ case '?':
+ /* We know that this packet is only sent
+ * during initial connect. So to be safe,
+ * we clear out our breakpoints now incase
+ * GDB is reconnecting. */
+ remove_all_break();
+ /* Also, if we haven't been able to roundup all
+ * CPUs, send an 'O' packet informing the user
+ * as much. Only need to do this once. */
+ if (!all_cpus_synced)
+ kgdb_msg_write("Not all CPUs have been "
+ "synced for KGDB\n", 39);
+ remcom_out_buffer[0] = 'S';
+ remcom_out_buffer[1] = hexchars[signo >> 4];
+ remcom_out_buffer[2] = hexchars[signo % 16];
+ break;
+
+ case 'g': /* return the value of the CPU registers */
+ thread = kgdb_usethread;
+
+ if (!thread) {
+ thread = kgdb_info[processor].task;
+ local_debuggerinfo =
+ kgdb_info[processor].debuggerinfo;
+ } else {
+ local_debuggerinfo = NULL;
+ for (i = 0; i < NR_CPUS; i++) {
+ /* Try to find the task on some other
+ * or possibly this node if we do not
+ * find the matching task then we try
+ * to approximate the results.
+ */
+ if (thread == kgdb_info[i].task)
+ local_debuggerinfo =
+ kgdb_info[i].debuggerinfo;
+ }
+ }
+
+ /* All threads that don't have debuggerinfo should be
+ * in __schedule() sleeping, since all other CPUs
+ * are in kgdb_wait, and thus have debuggerinfo. */
+ if (kgdb_usethreadid >= pid_max + num_online_cpus()) {
+ shadowregs = kgdb_shadow_regs(linux_regs,
+ kgdb_usethreadid -
+ pid_max -
+ num_online_cpus
+ ());
+ if (!shadowregs) {
+ error_packet(remcom_out_buffer,
+ -EINVAL);
+ break;
+ }
+ regs_to_gdb_regs(gdb_regs, shadowregs);
+ } else if (local_debuggerinfo)
+ regs_to_gdb_regs(gdb_regs, local_debuggerinfo);
+ else {
+ /* Pull stuff saved during
+ * switch_to; nothing else is
+ * accessible (or even particularly relevant).
+ * This should be enough for a stack trace. */
+ sleeping_thread_to_gdb_regs(gdb_regs, thread);
+ }
+ kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer,
+ NUMREGBYTES);
+ break;
+
+ /* set the value of the CPU registers - return OK */
+ case 'G':
+ kgdb_hex2mem(&remcom_in_buffer[1], (char *)gdb_regs,
+ NUMREGBYTES);
+
+ if (kgdb_usethread && kgdb_usethread != current)
+ error_packet(remcom_out_buffer, -EINVAL);
+ else {
+ gdb_regs_to_regs(gdb_regs, linux_regs);
+ strcpy(remcom_out_buffer, "OK");
+ }
+ break;
+
+ /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ case 'm':
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' &&
+ kgdb_hex2long(&ptr, &length) > 0) {
+ 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 we have not yet completed in
+ * pidhash_init() there isn't much we
+ * can give back.
+ */
+ if (!pidhash_init_done) {
+ if (remcom_in_buffer[1] == 'f')
+ strcpy(remcom_out_buffer,
+ "m0000000000000001");
+ break;
+ }
+
+ if (remcom_in_buffer[1] == 'f') {
+ threadid = 1;
+ }
+ remcom_out_buffer[0] = 'm';
+ ptr = remcom_out_buffer + 1;
+ for (i = 0; i < 17 && threadid < pid_max +
+ numshadowth; threadid++) {
+ thread = getthread(linux_regs,
+ threadid);
+ if (thread) {
+ int_to_threadref(&thref,
+ threadid);
+ pack_threadid(ptr, &thref);
+ ptr += 16;
+ *(ptr++) = ',';
+ i++;
+ }
+ }
+ *(--ptr) = '\0';
+ break;
+
+ case 'C':
+ /* Current thread id */
+ strcpy(remcom_out_buffer, "QC");
+
+ threadid = shadow_pid(current->pid);
+
+ int_to_threadref(&thref, threadid);
+ pack_threadid(remcom_out_buffer + 2, &thref);
+ break;
+ case 'T':
+ if (memcmp(remcom_in_buffer + 1,
+ "ThreadExtraInfo,", 16)) {
+ error_packet(remcom_out_buffer,
+ -EINVAL);
+ break;
+ }
+ threadid = 0;
+ ptr = remcom_in_buffer + 17;
+ kgdb_hex2long(&ptr, &threadid);
+ if (!getthread(linux_regs, threadid)) {
+ error_packet(remcom_out_buffer,
+ -EINVAL);
+ break;
+ }
+ if (threadid < pid_max) {
+ kgdb_mem2hex(getthread(linux_regs,
+ threadid)->comm,
+ remcom_out_buffer, 16);
+ } else if (threadid >= pid_max +
+ num_online_cpus()) {
+ kgdb_shadowinfo(linux_regs,
+ remcom_out_buffer,
+ threadid - pid_max -
+ num_online_cpus());
+ } else {
+ static char tmpstr[23 +
+ BUF_THREAD_ID_SIZE];
+ sprintf(tmpstr, "Shadow task %d"
+ " for pid 0",
+ (int)(threadid - pid_max));
+ kgdb_mem2hex(tmpstr, remcom_out_buffer,
+ strlen(tmpstr));
+ }
+ break;
+ }
+ break;
+
+ /* task related */
+ case 'H':
+ switch (remcom_in_buffer[1]) {
+ case 'g':
+ ptr = &remcom_in_buffer[2];
+ kgdb_hex2long(&ptr, &threadid);
+ thread = getthread(linux_regs, threadid);
+ if (!thread && threadid > 0) {
+ error_packet(remcom_out_buffer,
+ -EINVAL);
+ break;
+ }
+ kgdb_usethread = thread;
+ kgdb_usethreadid = threadid;
+ strcpy(remcom_out_buffer, "OK");
+ break;
+
+ case 'c':
+ ptr = &remcom_in_buffer[2];
+ kgdb_hex2long(&ptr, &threadid);
+ if (!threadid) {
+ kgdb_contthread = NULL;
+ } else {
+ thread = getthread(linux_regs,
+ threadid);
+ if (!thread && threadid > 0) {
+ error_packet(remcom_out_buffer,
+ -EINVAL);
+ break;
+ }
+ kgdb_contthread = thread;
+ }
+ strcpy(remcom_out_buffer, "OK");
+ break;
+ }
+ break;
+
+ /* Query thread status */
+ case 'T':
+ ptr = &remcom_in_buffer[1];
+ kgdb_hex2long(&ptr, &threadid);
+ thread = getthread(linux_regs, threadid);
+ if (thread)
+ strcpy(remcom_out_buffer, "OK");
+ else
+ error_packet(remcom_out_buffer, -EINVAL);
+ break;
+ /* Since GDB-5.3, it's been drafted that '0' is a software
+ * breakpoint, '1' is a hardware breakpoint, so let's do
+ * that.
+ */
+ case 'z':
+ case 'Z':
+ bpt_type = &remcom_in_buffer[1];
+ ptr = &remcom_in_buffer[2];
+
+ if (kgdb_ops->set_hw_breakpoint && *bpt_type >= '1') {
+ /* Unsupported */
+ if (*bpt_type > '4')
+ break;
+ } else if (*bpt_type != '0' && *bpt_type != '1')
+ /* Unsupported. */
+ break;
+ /* Test if this is a hardware breakpoint, and
+ * if we support it. */
+ if (*bpt_type == '1' &&
+ !kgdb_ops->flags & KGDB_HW_BREAKPOINT)
+ /* Unsupported. */
+ break;
+
+ if (*(ptr++) != ',') {
+ error_packet(remcom_out_buffer, -EINVAL);
+ break;
+ } else if (kgdb_hex2long(&ptr, &addr)) {
+ if (*(ptr++) != ',' ||
+ !kgdb_hex2long(&ptr, &length)) {
+ error_packet(remcom_out_buffer,
+ -EINVAL);
+ break;
+ }
+ } else {
+ error_packet(remcom_out_buffer, -EINVAL);
+ break;
+ }
+
+ if (remcom_in_buffer[0] == 'Z' && *bpt_type == '0')
+ error = kgdb_set_sw_break(addr);
+ else if (remcom_in_buffer[0] == 'Z' && *bpt_type == '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(ex_vector, signo,
+ err_code,
+ remcom_in_buffer,
+ remcom_out_buffer,
+ linux_regs);
+
+ if (error >= 0 || remcom_in_buffer[0] == 'D' ||
+ remcom_in_buffer[0] == 'k')
+ goto kgdb_exit;
+
+ } /* switch */
+
+ /* reply to the request */
+ put_packet(remcom_out_buffer);
+ }
+
+ kgdb_exit:
+ /* Call the I/O driver's post_exception routine if the I/O
+ * driver defined one.
+ */
+ if (kgdb_io_ops.post_exception)
+ kgdb_io_ops.post_exception();
+
+ kgdb_info[processor].debuggerinfo = NULL;
+ kgdb_info[processor].task = NULL;
+ procindebug[smp_processor_id()] = 0;
+
+ if (!debugger_step || !kgdb_contthread) {
+ for (i = 0; i < NR_CPUS; i++)
+ spin_unlock(&slavecpulocks[i]);
+ /* Wait till all the processors have quit
+ * from the debugger. */
+ for (i = 0; i < NR_CPUS; i++) {
+ while (procindebug[i]) {
+ int j = 10; /* an arbitrary number */
+
+ while (--j)
+ cpu_relax();
+ barrier();
+ }
+ }
+ }
+
+#ifdef CONFIG_SMP
+ /* This delay has a real purpose. The problem is that if you
+ * are single-stepping, you are sending an NMI to all the
+ * other processors to stop them. Interrupts come in, but
+ * don't get handled. Then you let them go just long enough
+ * to get into their interrupt routines and use up some stack.
+ * You stop them again, and then do the same thing. After a
+ * while you blow the stack on the other processors. This
+ * delay gives some time for interrupts to be cleared out on
+ * the other processors.
+ */
+ if (debugger_step)
+ mdelay(2);
+#endif
+
+ /* Free debugger_active */
+ atomic_set(&debugger_active, 0);
+ local_irq_restore(flags);
+
+ return error;
+}
+
+/*
+ * GDB places a breakpoint at this function to know dynamically
+ * loaded objects. It's not defined static so that only one instance with this
+ * name exists in the kernel.
+ */
+
+int module_event(struct notifier_block *self, unsigned long val, void *data)
+{
+ return 0;
+}
+
+static struct notifier_block kgdb_module_load_nb = {
+ .notifier_call = module_event,
+};
+
+void kgdb_nmihook(int cpu, void *regs)
+{
+#ifdef CONFIG_SMP
+ if (!procindebug[cpu] && atomic_read(&debugger_active) != (cpu + 1))
+ kgdb_wait((struct pt_regs *)regs);
+#endif
+}
+
+/*
+ * This is called when a panic happens. All we need to do is
+ * breakpoint().
+ */
+static int kgdb_panic_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ breakpoint();
+
+ return 0;
+}
+
+static struct notifier_block kgdb_panic_notifier = {
+ .notifier_call = kgdb_panic_notify,
+};
+
+/*
+ * Initialization that needs to be done in either of our entry points.
+ */
+static void __init kgdb_internal_init(void)
+{
+ int i;
+
+ /* Initialize our spinlocks. */
+ for (i = 0; i < NR_CPUS; i++)
+ spin_lock_init(&slavecpulocks[i]);
+
+ for (i = 0; i < MAX_BREAKPOINTS; i++)
+ kgdb_break[i].state = bp_disabled;
+
+ /* Initialize the I/O handles */
+ memset(&kgdb_io_ops_prev, 0, sizeof(kgdb_io_ops_prev));
+
+ /* We can't do much if this fails */
+ register_module_notifier(&kgdb_module_load_nb);
+
+ kgdb_initialized = 1;
+}
+
+static void kgdb_register_for_panic(void)
+{
+ /* Register for panics(). */
+ /* The registration is done in the kgdb_register_for_panic
+ * routine because KGDB should not try to handle a panic when
+ * there are no kgdb_io_ops setup. It is assumed that the
+ * kgdb_io_ops are setup at the time this method is called.
+ */
+ if (!kgdb_from_module_registered) {
+ notifier_chain_register(&panic_notifier_list,
+ &kgdb_panic_notifier);
+ kgdb_from_module_registered = 1;
+ }
+}
+
+static void kgdb_unregister_for_panic(void)
+{
+ /* When this routine is called KGDB should unregister from the
+ * panic handler and clean up, making sure it is not handling any
+ * break exceptions at the time.
+ */
+ if (kgdb_from_module_registered) {
+ kgdb_from_module_registered = 0;
+ notifier_chain_unregister(&panic_notifier_list,
+ &kgdb_panic_notifier);
+ }
+}
+
+int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops)
+{
+
+ if (kgdb_connected) {
+ printk(KERN_ERR "kgdb: Cannot load I/O module while KGDB "
+ "connected.\n");
+ return -EINVAL;
+ }
+
+ /* Save the old values so they can be restored */
+ if (kgdb_io_handler_cnt >= MAX_KGDB_IO_HANDLERS) {
+ printk(KERN_ERR "kgdb: No more I/O handles available.\n");
+ return -EINVAL;
+ }
+
+ /* Check to see if there is an existing driver and if so save
+ * its values.
+ */
+ if (kgdb_io_ops.read_char != NULL) {
+ memcpy(&kgdb_io_ops_prev[kgdb_io_handler_cnt],
+ &kgdb_io_ops, sizeof(struct kgdb_io));
+ kgdb_io_handler_cnt++;
+ }
+
+ /* Initialize the io values for this module */
+ memcpy(&kgdb_io_ops, local_kgdb_io_ops, sizeof(struct kgdb_io));
+
+ /* Make the call to register kgdb if is not initialized */
+ kgdb_register_for_panic();
+
+ return 0;
+}
+
+void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops)
+{
+ int i;
+
+ /* Unregister KGDB if there were no other prior io hooks, else
+ * restore the io hooks.
+ */
+ if (kgdb_io_handler_cnt > 0 && kgdb_io_ops_prev[0].read_char != NULL) {
+ /* First check if the hook that is in use is the one being
+ * removed */
+ if (kgdb_io_ops.read_char == local_kgdb_io_ops->read_char) {
+ /* Set 'i' to the value of where the list should be
+ * shifed */
+ i = kgdb_io_handler_cnt - 1;
+ memcpy(&kgdb_io_ops, &kgdb_io_ops_prev[i],
+ sizeof(struct kgdb_io));
+ } else {
+ /* Simple case to remove an entry for an I/O handler
+ * that is not in use */
+ for (i = 0; i < kgdb_io_handler_cnt; i++) {
+ if (kgdb_io_ops_prev[i].read_char ==
+ local_kgdb_io_ops->read_char)
+ break;
+ }
+ }
+
+ /* Shift all the entries in the handler array so it is
+ * ordered from oldest to newest.
+ */
+ kgdb_io_handler_cnt--;
+ for (; i < kgdb_io_handler_cnt; i++) {
+ memcpy(&kgdb_io_ops_prev[i], &kgdb_io_ops_prev[i + 1],
+ sizeof(struct kgdb_io));
+ }
+ /* Handle the case if we are on the last element and set it
+ * to NULL; */
+ memset(&kgdb_io_ops_prev[kgdb_io_handler_cnt], 0,
+ sizeof(struct kgdb_io));
+
+ if (kgdb_connected)
+ printk(KERN_ERR "kgdb: WARNING: I/O method changed "
+ "while kgdb was connected state.\n");
+ } else {
+ /* KGDB is no longer able to communicate out, so
+ * unregister our hooks and reset state. */
+ kgdb_unregister_for_panic();
+ if (kgdb_connected) {
+ printk(KERN_CRIT "kgdb: I/O module was unloaded while "
+ "a debugging session was running. "
+ "KGDB will be reset.\n");
+ if (remove_all_break() < 0)
+ printk(KERN_CRIT "kgdb: Reset failed.\n");
+ kgdb_connected = 0;
+ }
+ memset(&kgdb_io_ops, 0, sizeof(struct kgdb_io));
+ }
+}
+
+/*
+ * There are times we need to call a tasklet to cause a breakpoint
+ * as calling breakpoint() at that point might be fatal. We have to
+ * check that the exception stack is setup, as tasklets may be scheduled
+ * prior to this. When that happens, it is up to the architecture to
+ * schedule this when it is safe to run.
+ */
+static void kgdb_tasklet_bpt(unsigned long ing)
+{
+ breakpoint();
+}
+
+DECLARE_TASKLET(kgdb_tasklet_breakpoint, kgdb_tasklet_bpt, 0);
+
+/*
+ * This function can be called very early, either via early_param() or
+ * an explicit breakpoint() early on.
+ */
+static void __init kgdb_early_entry(void)
+{
+ /* Let the architecture do any setup that it needs to. */
+ kgdb_arch_init();
+
+ /* Now try the I/O. */
+ /* For early entry kgdb_io_ops.init must be defined */
+ if (!kgdb_io_ops.init || kgdb_io_ops.init()) {
+ /* Try again later. */
+ kgdb_initialized = -1;
+ return;
+ }
+
+ /* Finish up. */
+ kgdb_internal_init();
+
+ /* KGDB can assume that if kgdb_io_ops.init was defined that the
+ * panic registion should be performed at this time. This means
+ * kgdb_io_ops.init did not come from a kernel module and was
+ * initialized statically by a built in.
+ */
+ if (kgdb_io_ops.init)
+ kgdb_register_for_panic();
+}
+
+/*
+ * This function will always be invoked to make sure that KGDB will grab
+ * what it needs to so that if something happens while the system is
+ * running, KGDB will get involved. If kgdb_early_entry() has already
+ * been invoked, there is little we need to do.
+ */
+static int __init kgdb_late_entry(void)
+{
+ int need_break = 0;
+
+ /* If kgdb_initialized is -1 then we were passed kgdbwait. */
+ if (kgdb_initialized == -1)
+ need_break = 1;
+
+ /*
+ * If we haven't tried to initialize KGDB yet, we need to call
+ * kgdb_arch_init before moving onto the I/O.
+ */
+ if (!kgdb_initialized)
+ kgdb_arch_init();
+
+ if (kgdb_initialized != 1) {
+ if (kgdb_io_ops.init && kgdb_io_ops.init()) {
+ /* When KGDB allows I/O via modules and the core
+ * I/O init fails KGDB must default to defering the
+ * I/O setup, and appropriately print an error about
+ * it.
+ */
+ printk(KERN_ERR "kgdb: Could not setup core I/O "
+ "for KGDB.\n");
+ printk(KERN_INFO "kgdb: Defering I/O setup to kernel "
+ "module.\n");
+ memset(&kgdb_io_ops, 0, sizeof(struct kgdb_io));
+ }
+
+ kgdb_internal_init();
+
+ /* KGDB can assume that if kgdb_io_ops.init was defined that
+ * panic registion should be performed at this time. This means
+ * kgdb_io_ops.init did not come from a kernel module and was
+ * initialized statically by a built in.
+ */
+ if (kgdb_io_ops.init)
+ kgdb_register_for_panic();
+ }
+
+ /* Now do any late init of the I/O. */
+ if (kgdb_io_ops.late_init)
+ kgdb_io_ops.late_init();
+
+ if (need_break) {
+ printk(KERN_CRIT "kgdb: Waiting for connection from remote"
+ " gdb...\n");
+ breakpoint();
+ }
+
+ return 0;
+}
+
+late_initcall(kgdb_late_entry);
+
+/*
+ * This function will generate a breakpoint exception. It is used at the
+ * beginning of a program to sync up with a debugger and can be used
+ * otherwise as a quick means to stop program execution and "break" into
+ * the debugger.
+ */
+void breakpoint(void)
+{
+ 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
+
+#ifdef CONFIG_KGDB_CONSOLE
+void kgdb_console_write(struct console *co, const char *s, unsigned count)
+{
+ unsigned long flags;
+
+ /* If we're debugging, or KGDB has not connected, don't try
+ * and print. */
+ if (!kgdb_connected || atomic_read(&debugger_active) != 0)
+ return;
+
+ local_irq_save(flags);
+ kgdb_msg_write(s, count);
+ local_irq_restore(flags);
+}
+
+static struct console kgdbcons = {
+ .name = "kgdb",
+ .write = kgdb_console_write,
+ .flags = CON_PRINTBUFFER | CON_ENABLED,
+};
+static int __init kgdb_console_init(void)
+{
+ register_console(&kgdbcons);
+ return 0;
+}
+
+console_initcall(kgdb_console_init);
+#endif
+
+static int __init opt_kgdb_enter(char *str)
+{
+ /* We've already done this by an explicit breakpoint() call. */
+ if (kgdb_initialized)
+ return 0;
+
+ /* Call breakpoint() which will take care of init. */
+ breakpoint();
+
+ return 0;
+}
+
+early_param("kgdbwait", opt_kgdb_enter);
Index: linux-2.6.14/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/kernel/Makefile
+++ linux-2.6.14/kernel/Makefile
@@ -27,6 +27,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_DETECT_SOFTLOCKUP) += softlockup.o
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
Index: linux-2.6.14/kernel/pid.c
===================================================================
--- linux-2.6.14.orig/kernel/pid.c
+++ linux-2.6.14/kernel/pid.c
@@ -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)
Index: linux-2.6.14/kernel/sched.c
===================================================================
--- linux-2.6.14.orig/kernel/sched.c
+++ linux-2.6.14/kernel/sched.c
@@ -47,6 +47,7 @@
#include <linux/syscalls.h>
#include <linux/times.h>
#include <linux/acct.h>
+#include <linux/kgdb.h>
#include <asm/tlb.h>

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

+ if (atomic_read(&debugger_active))
+ return;
+
if ((in_atomic() || irqs_disabled()) &&
system_state == SYSTEM_RUNNING && !oops_in_progress) {
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -178,3 +178,56 @@ config FRAME_POINTER
on some architectures or you use external debuggers.
If you don't debug the kernel, you can say N.

+config WANT_EXTRA_DEBUG_INFORMATION
+ bool
+ select DEBUG_INFO
+ select FRAME_POINTER if X86
+ default n
+
+config KGDB
+ bool "KGDB: kernel debugging with remote gdb"
+ select WANT_EXTRA_DEBUG_INFORMATION
+ depends on DEBUG_KERNEL
+ help
+ If you say Y here, it will be possible to remotely debug the
+ kernel using gdb. It is strongly suggested that you enable
+ DEBUG_INFO, and if available on your platform, FRAME_POINTER.
+ Documentation of kernel debugger available at
+ http://kgdb.sourceforge.net as well as in DocBook form
+ in Documentation/DocBook/. If unsure, say N.
+
+config KGDB_CONSOLE
+ bool "KGDB: Console messages through gdb"
+ depends on KGDB
+ help
+ If you say Y here, console messages will appear through gdb.
+ Other consoles such as tty or ttyS will continue to work as usual.
+ Note, that if you use this in conjunction with KGDB_ETH, if the
+ ethernet driver runs into an error condition during use with KGDB
+ it is possible to hit an infinite recusrion, causing the kernel
+ to crash, and typically reboot. For this reason, it is preferable
+ to use NETCONSOLE in conjunction with KGDB_ETH instead of
+ KGDB_CONSOLE.
+
+choice
+ prompt "Method for KGDB communication"
+ depends on KGDB
+ default KGDB_ONLY_MODULES
+ help
+ There are a number of different ways in which you can communicate
+ with KGDB. The most common is via serial, with the 8250 driver
+ (should your hardware have an 8250, or ns1655x style uart).
+ Another option is to use the NETPOLL framework and UDP, should
+ your ethernet card support this. Other options may exist.
+ You can elect to have one core I/O driver that is built into the
+ kernel for debugging as the kernel is booting, or using only
+ kernel modules.
+
+config KGDB_ONLY_MODULES
+ bool "KGDB: Use only kernel modules for I/O"
+ depends on MODULES
+ help
+ Use only kernel modules to configure KGDB I/O after the
+ kernel is booted.
+
+endchoice
Index: linux-2.6.14/MAINTAINERS
===================================================================
--- linux-2.6.14.orig/MAINTAINERS
+++ linux-2.6.14/MAINTAINERS
@@ -1415,6 +1415,15 @@ L: [email protected]
L: [email protected]
S: Maintained

+KGDB
+P: Tom Rini
+P: Amit S. Kale
+M: [email protected]
+M: [email protected]
+W: http://sourceforge.net/projects/kgdb
+L: [email protected]
+S: Maintained
+
KPROBES
P: Prasanna S Panchamukhi
M: [email protected]

--
Tom

2005-11-10 16:40:49

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 03/15] KGDB: ppc32-specific changes

This adds basic KGDB support to ppc32 (classic, e500 and 4xx), and support
for kgdb8250 to a number of boards. This was done mostly myself with some
help from Scott Hall for e500 and Frank Rowand pointed out some parts as he
did the ppc64 code, as well as Takeharu KATO <[email protected]>
for some of the 4xx code and testing.

arch/ppc/Kconfig.debug | 36 -
arch/ppc/kernel/Makefile | 2
arch/ppc/kernel/kgdb.c | 329 ++++++++++++
arch/ppc/kernel/ppc-stub.c | 867 --------------------------------
arch/ppc/kernel/setup.c | 16
arch/ppc/mm/fault.c | 9
arch/ppc/platforms/4xx/bubinga.c | 23
arch/ppc/platforms/4xx/ebony.c | 27
arch/ppc/platforms/4xx/ocotea.c | 26
arch/ppc/platforms/4xx/xilinx_ml300.c | 18
arch/ppc/platforms/85xx/sbc8560.c | 36 -
arch/ppc/platforms/chestnut.c | 5
arch/ppc/platforms/pplus.c | 3
arch/ppc/platforms/sandpoint.c | 3
arch/ppc/platforms/spruce.c | 21
arch/ppc/syslib/Makefile | 1
arch/ppc/syslib/gen550.h | 1
arch/ppc/syslib/gen550_kgdb.c | 86 ---
arch/ppc/syslib/ibm44x_common.c | 3
arch/ppc/syslib/mv64x60.c | 55 +-
arch/ppc/syslib/mv64x60_dbg.c | 52 -
arch/ppc/syslib/ppc85xx_setup.c | 13
drivers/serial/Makefile | 1
drivers/serial/cpm_uart/Makefile | 1
drivers/serial/cpm_uart/cpm_uart.h | 50 +
drivers/serial/cpm_uart/cpm_uart_core.c | 115 ++--
drivers/serial/cpm_uart/cpm_uart_cpm2.c | 4
drivers/serial/cpm_uart/cpm_uart_kgdb.c | 191 +++++++
drivers/serial/mpsc.h | 4
drivers/serial/mpsc_kgdb.c | 299 +++++++++++
include/asm-ppc/kgdb.h | 47 -
include/asm-ppc/machdep.h | 2
include/asm-ppc/mv64x60.h | 2
include/asm-ppc/mv64x60_defs.h | 3
lib/Kconfig.debug | 17
35 files changed, 1139 insertions(+), 1229 deletions(-)

Index: linux-2.6.14/arch/ppc/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/arch/ppc/Kconfig.debug
+++ linux-2.6.14/arch/ppc/Kconfig.debug
@@ -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
Index: linux-2.6.14/arch/ppc/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/ppc/kernel/kgdb.c
@@ -0,0 +1,329 @@
+/*
+ * arch/ppc/kernel/kgdb.c
+ *
+ * PowerPC backend to the KGDB stub.
+ *
+ * Maintainer: Tom Rini <[email protected]>
+ *
+ * 1998 (c) Michael AK Tesch ([email protected])
+ * Copyright (C) 2003 Timesys Corporation.
+ * 2004 (c) MontaVista Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+#include <linux/smp.h>
+#include <linux/signal.h>
+#include <linux/ptrace.h>
+#include <asm/current.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+
+/*
+ * This table contains the mapping between PowerPC hardware trap types, and
+ * signals, which are primarily what GDB understands. GDB and the kernel
+ * don't always agree on values, so we use constants taken from gdb-6.2.
+ */
+static struct hard_trap_info
+{
+ unsigned int tt; /* Trap type code for powerpc */
+ unsigned char signo; /* Signal that we map this trap into */
+} hard_trap_info[] = {
+#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 */
+ { 0x2002, 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 */
+}
+
+/* KGDB functions to use existing PowerPC hooks. */
+static void kgdb_debugger(struct pt_regs *regs)
+{
+ kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs);
+}
+
+static int kgdb_breakpoint(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, SIGTRAP, 0, regs);
+
+ if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
+ regs->nip += 4;
+
+ return 1;
+}
+
+static int kgdb_singlestep(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, SIGTRAP, 0, regs);
+ return 1;
+}
+
+int kgdb_iabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs);
+ return 1;
+}
+
+int kgdb_dabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs);
+ return 1;
+}
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ memset(gdb_regs, 0, MAXREG * 4);
+
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = regs->gpr[reg];
+
+#ifndef CONFIG_E500
+ for (reg = 0; reg < 64; reg++)
+ *(ptr++) = 0;
+#else
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = current->thread.evr[reg];
+#endif
+
+ *(ptr++) = regs->nip;
+ *(ptr++) = regs->msr;
+ *(ptr++) = regs->ccr;
+ *(ptr++) = regs->link;
+ *(ptr++) = regs->ctr;
+ *(ptr++) = regs->xer;
+
+#ifdef CONFIG_SPE
+ /* u64 acc */
+ *(ptr++) = (current->thread.acc >> 32);
+ *(ptr++) = (current->thread.acc & 0xffffffff);
+ *(ptr++) = current->thread.spefscr;
+#endif
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ struct pt_regs *regs = (struct pt_regs *)(p->thread.ksp +
+ STACK_FRAME_OVERHEAD);
+ 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];
+
+#ifndef CONFIG_E500
+ for (reg = 0; reg < 64; reg++)
+ *(ptr++) = 0;
+#else
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = current->thread.evr[reg];
+#endif
+
+ *(ptr++) = regs->nip;
+ *(ptr++) = regs->msr;
+ *(ptr++) = regs->ccr;
+ *(ptr++) = regs->link;
+ *(ptr++) = regs->ctr;
+ *(ptr++) = regs->xer;
+
+#ifdef CONFIG_SPE
+ /* u64 acc */
+ *(ptr++) = (current->thread.acc >> 32);
+ *(ptr++) = (current->thread.acc & 0xffffffff);
+ *(ptr++) = current->thread.spefscr;
+#endif
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+#ifdef CONFIG_SPE
+ union {
+ u32 v32[2];
+ u64 v64;
+ } u;
+#endif
+
+ for (reg = 0; reg < 32; reg++)
+ regs->gpr[reg] = *(ptr++);
+
+#ifndef CONFIG_E500
+ for (reg = 0; reg < 64; reg++)
+ ptr++;
+#else
+ for (reg = 0; reg < 32; reg++)
+ current->thread.evr[reg] = *(ptr++);
+#endif
+
+ regs->nip = *(ptr++);
+ regs->msr = *(ptr++);
+ regs->ccr = *(ptr++);
+ regs->link = *(ptr++);
+ regs->ctr = *(ptr++);
+ regs->xer = *(ptr++);
+
+#ifdef CONFIG_SPE
+ /* u64 acc */
+ u.v32[0] = *(ptr++);
+ u.v32[1] = *(ptr++);
+ current->thread.acc = u.v64;
+ current->thread.spefscr = *(ptr++);
+#endif
+}
+
+/*
+ * Save/restore state in case a memory access causes a fault.
+ */
+int kgdb_fault_setjmp(unsigned long *curr_context)
+{
+ __asm__ __volatile__("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"(curr_context));
+ return 0;
+}
+
+void kgdb_fault_longjmp(unsigned long *curr_context)
+{
+ __asm__ __volatile__("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"(curr_context));
+}
+
+/*
+ * 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 = &remcom_in_buffer[1];
+ unsigned long addr;
+
+ switch (remcom_in_buffer[0])
+ {
+ /*
+ * sAA..AA Step one instruction from AA..AA
+ * This will return an error to gdb ..
+ */
+ case 's':
+ case 'c':
+ /* handle the optional parameter */
+ if (kgdb_hex2long (&ptr, &addr))
+ linux_regs->nip = addr;
+
+ atomic_set(&cpu_doing_single_step, -1);
+ /* set the trace bit if we're stepping */
+ if (remcom_in_buffer[0] == 's') {
+#if defined (CONFIG_40x) || defined(CONFIG_BOOKE)
+ mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) |
+ DBCR0_IC | DBCR0_IDM);
+ linux_regs->msr |= MSR_DE;
+#else
+ linux_regs->msr |= MSR_SE;
+#endif
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,
+ smp_processor_id());
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Global data
+ */
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0x7d, 0x82, 0x10, 0x08},
+};
+
+int kgdb_arch_init(void)
+{
+ debugger = kgdb_debugger;
+ debugger_bpt = kgdb_breakpoint;
+ debugger_sstep = kgdb_singlestep;
+ debugger_iabr_match = kgdb_iabr_match;
+ debugger_dabr_match = kgdb_dabr_match;
+
+ return 0;
+}
+
+arch_initcall(kgdb_arch_init);
Index: linux-2.6.14/arch/ppc/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/ppc/kernel/Makefile
+++ linux-2.6.14/arch/ppc/kernel/Makefile
@@ -22,7 +22,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
Index: linux-2.6.14/arch/ppc/kernel/ppc-stub.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/kernel/ppc-stub.c
+++ /dev/null
@@ -1,867 +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) || defined(CONFIG_BOOKE)
- { 0x100, SIGINT }, /* critical input interrupt */
- { 0x200, SIGSEGV }, /* machine check */
- { 0x300, SIGSEGV }, /* data storage */
- { 0x400, SIGBUS }, /* instruction storage */
- { 0x500, SIGINT }, /* interrupt */
- { 0x600, SIGBUS }, /* alignment */
- { 0x700, SIGILL }, /* program */
- { 0x800, SIGILL }, /* reserved */
- { 0x900, SIGILL }, /* reserved */
- { 0xa00, SIGILL }, /* reserved */
- { 0xb00, SIGILL }, /* reserved */
- { 0xc00, SIGCHLD }, /* syscall */
- { 0xd00, SIGILL }, /* reserved */
- { 0xe00, SIGILL }, /* reserved */
- { 0xf00, SIGILL }, /* reserved */
- /*
- ** 0x1000 PIT
- ** 0x1010 FIT
- ** 0x1020 watchdog
- ** 0x1100 data TLB miss
- ** 0x1200 instruction TLB miss
- */
- { 0x2002, SIGTRAP}, /* debug */
-#else
- { 0x200, SIGSEGV }, /* machine check */
- { 0x300, SIGSEGV }, /* address error (store) */
- { 0x400, SIGBUS }, /* instruction bus error */
- { 0x500, SIGINT }, /* interrupt */
- { 0x600, SIGBUS }, /* alingment */
- { 0x700, SIGTRAP }, /* breakpoint trap */
- { 0x800, SIGFPE }, /* fpu unavail */
- { 0x900, SIGALRM }, /* decrementer */
- { 0xa00, SIGILL }, /* reserved */
- { 0xb00, SIGILL }, /* reserved */
- { 0xc00, SIGCHLD }, /* syscall */
- { 0xd00, SIGTRAP }, /* single-step/watch */
- { 0xe00, SIGFPE }, /* fp assist */
-#endif
- { 0, 0} /* Must be last */
-
-};
-
-static int computeSignal(unsigned int tt)
-{
- struct hard_trap_info *ht;
-
- for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
- if (ht->tt == tt)
- return ht->signo;
-
- return SIGHUP; /* default for things we don't know about */
-}
-
-#define PC_REGNUM 64
-#define SP_REGNUM 1
-
-/*
- * This function does all command processing for interfacing to gdb.
- */
-static int
-handle_exception (struct pt_regs *regs)
-{
- int sigval;
- int addr;
- int length;
- char *ptr;
- unsigned int msr;
-
- /* We don't handle user-mode breakpoints. */
- if (user_mode(regs))
- return 0;
-
- if (debugger_fault_handler) {
- debugger_fault_handler(regs);
- panic("kgdb longjump failed!\n");
- }
- if (kgdb_active) {
- printk(KERN_ERR "interrupt while in kgdb, returning\n");
- return 0;
- }
-
- kgdb_active = 1;
- kgdb_started = 1;
-
-#ifdef KGDB_DEBUG
- printk("kgdb: entering handle_exception; trap [0x%x]\n",
- (unsigned int)regs->trap);
-#endif
-
- kgdb_interruptible(0);
- lock_kernel();
- msr = mfmsr();
- mtmsr(msr & ~MSR_EE); /* disable interrupts */
-
- if (regs->nip == (unsigned long)breakinst) {
- /* Skip over breakpoint trap insn */
- regs->nip += 4;
- }
-
- /* reply to host that an exception has occurred */
- sigval = computeSignal(regs->trap);
- ptr = remcomOutBuffer;
-
- *ptr++ = 'T';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
- *ptr++ = hexchars[PC_REGNUM >> 4];
- *ptr++ = hexchars[PC_REGNUM & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)&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++ = ';';
- *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();
- mtmsr(msr);
-
- kgdb_interruptible(1);
- unlock_kernel();
- kgdb_active = 0;
- if (kdebug) {
- printk("remcomInBuffer: %s\n", remcomInBuffer);
- printk("remcomOutBuffer: %s\n", remcomOutBuffer);
- }
- return 1;
-
- case 's':
- kgdb_flush_cache_all();
-#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
- mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC);
- regs->msr |= MSR_DE;
-#else
- regs->msr |= MSR_SE;
-#endif
- unlock_kernel();
- kgdb_active = 0;
- if (kdebug) {
- printk("remcomInBuffer: %s\n", remcomInBuffer);
- printk("remcomOutBuffer: %s\n", remcomOutBuffer);
- }
- return 1;
-
- case 'r': /* Reset (if user process..exit ???)*/
- panic("kgdb reset.");
- break;
- } /* switch */
- if (remcomOutBuffer[0] && kdebug) {
- printk("remcomInBuffer: %s\n", remcomInBuffer);
- printk("remcomOutBuffer: %s\n", remcomOutBuffer);
- }
- /* reply to the request */
- putpacket(remcomOutBuffer);
- } /* while(1) */
-}
-
-/* This function will generate a breakpoint exception. It is used at the
- beginning of a program to sync up with a debugger and can be used
- otherwise as a quick means to stop program execution and "break" into
- the debugger. */
-
-void
-breakpoint(void)
-{
- if (!initialized) {
- printk("breakpoint() called b4 kgdb init\n");
- return;
- }
-
- asm(" .globl breakinst \n\
- breakinst: .long 0x7d821008");
-}
-
-#ifdef CONFIG_KGDB_CONSOLE
-/* Output string in GDB O-packet format if GDB has connected. If nothing
- output, returns 0 (caller must then handle output). */
-int
-kgdb_output_string (const char* s, unsigned int count)
-{
- char buffer[512];
-
- if (!kgdb_started)
- return 0;
-
- count = (count <= (sizeof(buffer) / 2 - 2))
- ? count : (sizeof(buffer) / 2 - 2);
-
- buffer[0] = 'O';
- mem2hex (s, &buffer[1], count);
- putpacket(buffer);
-
- return 1;
-}
-#endif
-
-static void sysrq_handle_gdb(int key, struct pt_regs *pt_regs,
- struct tty_struct *tty)
-{
- printk("Entering GDB stub\n");
- breakpoint();
-}
-static struct sysrq_key_op sysrq_gdb_op = {
- .handler = sysrq_handle_gdb,
- .help_msg = "Gdb",
- .action_msg = "GDB",
-};
-
-static int gdb_register_sysrq(void)
-{
- printk("Registering GDB sysrq handler\n");
- register_sysrq_key('g', &sysrq_gdb_op);
- return 0;
-}
-module_init(gdb_register_sysrq);
Index: linux-2.6.14/arch/ppc/kernel/setup.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/kernel/setup.c
+++ linux-2.6.14/arch/ppc/kernel/setup.c
@@ -49,10 +49,6 @@
#include <asm/ppc_sys.h>
#endif

-#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);
@@ -727,18 +723,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)
Index: linux-2.6.14/arch/ppc/mm/fault.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/mm/fault.c
+++ linux-2.6.14/arch/ppc/mm/fault.c
@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/highmem.h>
#include <linux/module.h>
+#include <linux/kgdb.h>

#include <asm/page.h>
#include <asm/pgtable.h>
@@ -328,6 +329,14 @@ bad_page_fault(struct pt_regs *regs, uns
return;
}

+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault) {
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+ }
+#endif
+
/* kernel has accessed a bad area */
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
if (debugger_kernel_faults)
Index: linux-2.6.14/arch/ppc/platforms/4xx/bubinga.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/4xx/bubinga.c
+++ linux-2.6.14/arch/ppc/platforms/4xx/bubinga.c
@@ -4,7 +4,7 @@
* Author: SAW (IBM), derived from walnut.c.
* Maintained by MontaVista Software <[email protected]>
*
- * 2003 (c) MontaVista Softare Inc. This file is licensed under the
+ * 2003-2004 (c) MontaVista Softare Inc. This file is licensed under the
* terms of the GNU General Public License version 2. This program is
* licensed "as is" without any warranty of any kind, whether express
* or implied.
@@ -101,17 +101,26 @@ bubinga_early_serial_map(void)
port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
port.line = 0;

- if (early_serial_setup(&port) != 0) {
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&port) != 0)
printk("Early serial init of port 0 failed\n");
- }
+#endif
+
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &port);
+#endif

port.membase = (void*)ACTING_UART1_IO_BASE;
port.irq = ACTING_UART1_INT;
port.line = 1;

- if (early_serial_setup(&port) != 0) {
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&port) != 0)
printk("Early serial init of port 1 failed\n");
- }
+#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(1, &port);
+#endif
}

void __init
@@ -256,8 +265,4 @@ platform_init(unsigned long r3, unsigned
ppc_md.nvram_read_val = todc_direct_read_val;
ppc_md.nvram_write_val = todc_direct_write_val;
#endif
-#ifdef CONFIG_KGDB
- ppc_md.early_serial_map = bubinga_early_serial_map;
-#endif
}
-
Index: linux-2.6.14/arch/ppc/platforms/4xx/ebony.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/4xx/ebony.c
+++ linux-2.6.14/arch/ppc/platforms/4xx/ebony.c
@@ -35,6 +35,7 @@
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
+#include <linux/kgdb.h>

#include <asm/system.h>
#include <asm/pgtable.h>
@@ -229,14 +230,20 @@ ebony_early_serial_map(void)
port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
port.line = 0;

- if (early_serial_setup(&port) != 0) {
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&port) != 0)
printk("Early serial init of port 0 failed\n");
- }
+#endif

-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
/* Configure debug serial access */
gen550_init(0, &port);
+#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &port);
+#endif

+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250)
/* Purge TLB entry added in head_44x.S for early serial access */
_tlbie(UART0_IO_BASE);
#endif
@@ -246,14 +253,18 @@ ebony_early_serial_map(void)
port.uartclk = clocks.uart1;
port.line = 1;

- if (early_serial_setup(&port) != 0) {
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&port) != 1)
printk("Early serial init of port 1 failed\n");
- }
+#endif

-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
/* Configure debug serial access */
gen550_init(1, &port);
#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(1, &port);
+#endif
}

static void __init
@@ -339,8 +350,4 @@ void __init platform_init(unsigned long

ppc_md.nvram_read_val = todc_direct_read_val;
ppc_md.nvram_write_val = todc_direct_write_val;
-#ifdef CONFIG_KGDB
- ppc_md.early_serial_map = ebony_early_serial_map;
-#endif
}
-
Index: linux-2.6.14/arch/ppc/platforms/4xx/ocotea.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/4xx/ocotea.c
+++ linux-2.6.14/arch/ppc/platforms/4xx/ocotea.c
@@ -33,6 +33,7 @@
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
+#include <linux/kgdb.h>

#include <asm/system.h>
#include <asm/pgtable.h>
@@ -252,14 +253,20 @@ ocotea_early_serial_map(void)
port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
port.line = 0;

- if (early_serial_setup(&port) != 0) {
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&port) != 0)
printk("Early serial init of port 0 failed\n");
- }
+#endif

-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
/* Configure debug serial access */
gen550_init(0, &port);
+#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &port);
+#endif

+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250)
/* Purge TLB entry added in head_44x.S for early serial access */
_tlbie(UART0_IO_BASE);
#endif
@@ -269,14 +276,18 @@ ocotea_early_serial_map(void)
port.uartclk = clocks.uart1;
port.line = 1;

- if (early_serial_setup(&port) != 0) {
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&port) != 1)
printk("Early serial init of port 1 failed\n");
- }
+#endif

-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
/* Configure debug serial access */
gen550_init(1, &port);
#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(1, &port);
+#endif
}

static void __init
@@ -355,8 +366,5 @@ void __init platform_init(unsigned long

ppc_md.nvram_read_val = todc_direct_read_val;
ppc_md.nvram_write_val = todc_direct_write_val;
-#ifdef CONFIG_KGDB
- ppc_md.early_serial_map = ocotea_early_serial_map;
-#endif
ppc_md.init = ocotea_init;
}
Index: linux-2.6.14/arch/ppc/platforms/4xx/xilinx_ml300.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/4xx/xilinx_ml300.c
+++ linux-2.6.14/arch/ppc/platforms/4xx/xilinx_ml300.c
@@ -42,9 +42,6 @@
* ppc4xx_map_io arch/ppc/syslib/ppc4xx_setup.c
* start_kernel init/main.c
* setup_arch arch/ppc/kernel/setup.c
- * #if defined(CONFIG_KGDB)
- * *ppc_md.kgdb_map_scc() == gen550_kgdb_map_scc
- * #endif
* *ppc_md.setup_arch == ml300_setup_arch this file
* ppc4xx_setup_arch arch/ppc/syslib/ppc4xx_setup.c
* ppc4xx_find_bridges arch/ppc/syslib/ppc405_pci.c
@@ -83,7 +80,6 @@ ml300_map_io(void)
static void __init
ml300_early_serial_map(void)
{
-#ifdef CONFIG_SERIAL_8250
struct serial_state old_ports[] = { SERIAL_PORT_DFNS };
struct uart_port port;
int i;
@@ -99,11 +95,14 @@ ml300_early_serial_map(void)
port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
port.line = i;

- if (early_serial_setup(&port) != 0) {
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&port) != 0)
printk("Early serial init of port %d failed\n", i);
- }
+#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(i, &port)
+#endif
}
-#endif /* CONFIG_SERIAL_8250 */
}

void __init
@@ -138,9 +137,4 @@ platform_init(unsigned long r3, unsigned
#if defined(XPAR_POWER_0_POWERDOWN_BASEADDR)
ppc_md.power_off = xilinx_power_off;
#endif
-
-#ifdef CONFIG_KGDB
- ppc_md.early_serial_map = ml300_early_serial_map;
-#endif
}
-
Index: linux-2.6.14/arch/ppc/platforms/85xx/sbc8560.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/85xx/sbc8560.c
+++ linux-2.6.14/arch/ppc/platforms/85xx/sbc8560.c
@@ -53,7 +53,6 @@
#include <syslib/ppc85xx_common.h>
#include <syslib/ppc85xx_setup.h>

-#ifdef CONFIG_SERIAL_8250
static void __init
sbc8560_early_serial_map(void)
{
@@ -69,27 +68,34 @@ sbc8560_early_serial_map(void)
uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART0_SIZE);
uart_req.type = PORT_16650;

-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
- gen550_init(0, &uart_req);
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&uart_req) != 0)
+ printk("Early serial init of port 0 failed\n");
#endif
-
- if (early_serial_setup(&uart_req) != 0)
- printk("Early serial init of port 0 failed\n");
-
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ gen550_init(0, &uart_req);
+#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &uart_req);
+#endif
+
/* Assume early_serial_setup() doesn't modify uart_req */
uart_req.line = 1;
uart_req.mapbase = UARTB_ADDR;
uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART1_SIZE);
uart_req.irq = MPC85xx_IRQ_EXT10;
-
-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
- gen550_init(1, &uart_req);
+
+#ifdef CONFIG_SERIAL_8250
+ if (early_serial_setup(&uart_req) != 0)
+ printk("Early serial init of port 1 failed\n");
#endif
-
- if (early_serial_setup(&uart_req) != 0)
- printk("Early serial init of port 1 failed\n");
-}
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ gen550_init(1, &uart_req);
#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(1, &uart_req);
+#endif
+}

/* ************************************************************************
*
@@ -117,9 +123,7 @@ sbc8560_setup_arch(void)
/* setup PCI host bridges */
mpc85xx_setup_hose();
#endif
-#ifdef CONFIG_SERIAL_8250
sbc8560_early_serial_map();
-#endif
#ifdef CONFIG_SERIAL_TEXT_DEBUG
/* Invalidate the entry we stole earlier the serial ports
* should be properly mapped */
Index: linux-2.6.14/arch/ppc/platforms/chestnut.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/chestnut.c
+++ linux-2.6.14/arch/ppc/platforms/chestnut.c
@@ -495,7 +495,7 @@ chestnut_power_off(void)
static void __init
chestnut_map_io(void)
{
-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250)
io_block_mapping(CHESTNUT_UART_BASE, CHESTNUT_UART_BASE, 0x100000,
_PAGE_IO);
#endif
@@ -570,9 +570,6 @@ platform_init(unsigned long r3, unsigned
#if defined(CONFIG_SERIAL_TEXT_DEBUG)
ppc_md.progress = gen550_progress;
#endif
-#if defined(CONFIG_KGDB)
- ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
-#endif

if (ppc_md.progress)
ppc_md.progress("chestnut_init(): exit", 0);
Index: linux-2.6.14/arch/ppc/platforms/pplus.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/pplus.c
+++ linux-2.6.14/arch/ppc/platforms/pplus.c
@@ -907,9 +907,6 @@ platform_init(unsigned long r3, unsigned
#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
-#ifdef CONFIG_KGDB
- ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
-#endif
#ifdef CONFIG_SMP
ppc_md.smp_ops = &pplus_smp_ops;
#endif /* CONFIG_SMP */
Index: linux-2.6.14/arch/ppc/platforms/sandpoint.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/sandpoint.c
+++ linux-2.6.14/arch/ppc/platforms/sandpoint.c
@@ -750,9 +750,6 @@ platform_init(unsigned long r3, unsigned
ppc_md.nvram_read_val = todc_mc146818_read_val;
ppc_md.nvram_write_val = todc_mc146818_write_val;

-#ifdef CONFIG_KGDB
- ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
-#endif
#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif
Index: linux-2.6.14/arch/ppc/platforms/spruce.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/platforms/spruce.c
+++ linux-2.6.14/arch/ppc/platforms/spruce.c
@@ -181,26 +181,32 @@ spruce_early_serial_map(void)
serial_req.membase = (u_char *)UART0_IO_BASE;
serial_req.regshift = 0;

-#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG)
- gen550_init(0, &serial_req);
-#endif
#ifdef CONFIG_SERIAL_8250
if (early_serial_setup(&serial_req) != 0)
printk("Early serial init of port 0 failed\n");
#endif
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ gen550_init(0, &serial_req);
+#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &port);
+#endif

/* Assume early_serial_setup() doesn't modify serial_req */
serial_req.line = 1;
serial_req.irq = UART1_INT;
serial_req.membase = (u_char *)UART1_IO_BASE;

-#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG)
- gen550_init(1, &serial_req);
-#endif
#ifdef CONFIG_SERIAL_8250
if (early_serial_setup(&serial_req) != 0)
printk("Early serial init of port 1 failed\n");
#endif
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ gen550_init(1, &serial_req);
+#endif
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(1, &serial_req);
+#endif
}

TODC_ALLOC();
@@ -319,7 +325,4 @@ platform_init(unsigned long r3, unsigned
#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
-#ifdef CONFIG_KGDB
- ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
-#endif
}
Index: linux-2.6.14/arch/ppc/syslib/gen550.h
===================================================================
--- linux-2.6.14.orig/arch/ppc/syslib/gen550.h
+++ linux-2.6.14/arch/ppc/syslib/gen550.h
@@ -13,4 +13,3 @@

extern void gen550_progress(char *, unsigned short);
extern void gen550_init(int, struct uart_port *);
-extern void gen550_kgdb_map_scc(void);
Index: linux-2.6.14/arch/ppc/syslib/gen550_kgdb.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/syslib/gen550_kgdb.c
+++ /dev/null
@@ -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);
-}
Index: linux-2.6.14/arch/ppc/syslib/ibm44x_common.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/syslib/ibm44x_common.c
+++ linux-2.6.14/arch/ppc/syslib/ibm44x_common.c
@@ -161,9 +161,6 @@ void __init ibm44x_platform_init(void)
#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
-#ifdef CONFIG_KGDB
- ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
-#endif

/*
* The Abatron BDI JTAG debugger does not tolerate others
Index: linux-2.6.14/arch/ppc/syslib/Makefile
===================================================================
--- linux-2.6.14.orig/arch/ppc/syslib/Makefile
+++ linux-2.6.14/arch/ppc/syslib/Makefile
@@ -80,7 +80,6 @@ obj-$(CONFIG_PCI_8260) += m82xx_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
ifeq ($(CONFIG_SERIAL_MPSC_CONSOLE),y)
Index: linux-2.6.14/arch/ppc/syslib/mv64x60.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/syslib/mv64x60.c
+++ linux-2.6.14/arch/ppc/syslib/mv64x60.c
@@ -242,6 +242,12 @@ static struct resource mv64x60_mpsc0_res
.end = MV64x60_IRQ_SDMA_0,
.flags = IORESOURCE_IRQ,
},
+ [4] = {
+ .name = "mpsc 0 irq",
+ .start = MV64x60_IRQ_MPSC_0,
+ .end = MV64x60_IRQ_MPSC_0,
+ .flags = IORESOURCE_IRQ,
+ },
};

static struct platform_device mpsc0_device = {
@@ -299,6 +305,12 @@ static struct resource mv64x60_mpsc1_res
.end = MV64360_IRQ_SDMA_1,
.flags = IORESOURCE_IRQ,
},
+ [4] = {
+ .name = "mpsc 1 irq",
+ .start = MV64360_IRQ_MPSC_1,
+ .end = MV64360_IRQ_MPSC_1,
+ .flags = IORESOURCE_IRQ,
+ },
};

static struct platform_device mpsc1_device = {
@@ -1427,12 +1439,46 @@ mv64x60_pd_fixup(struct mv64x60_handle *
static int __init
mv64x60_add_pds(void)
{
- return platform_add_devices(mv64x60_pd_devs,
- ARRAY_SIZE(mv64x60_pd_devs));
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(mv64x60_pd_devs); i++) {
+ if (mv64x60_pd_devs[i]) {
+ ret = platform_device_register(mv64x60_pd_devs[i]);
+ }
+ if (ret) {
+ while (--i >= 0)
+ platform_device_unregister(mv64x60_pd_devs[i]);
+ break;
+ }
+ }
+ return ret;
}
arch_initcall(mv64x60_add_pds);

/*
+ * mv64x60_early_get_pdev_data()
+ *
+ * Get the data associated with a platform device by name and number.
+ */
+struct platform_device * __init
+mv64x60_early_get_pdev_data(const char *name, int id, int remove)
+{
+ int i;
+ struct platform_device *pdev;
+
+ for (i = 0; i <ARRAY_SIZE(mv64x60_pd_devs); i++) {
+ if ((pdev = mv64x60_pd_devs[i]) &&
+ pdev->id == id &&
+ !strcmp(pdev->name, name)) {
+ if (remove)
+ mv64x60_pd_devs[i] = NULL;
+ return pdev;
+ }
+ }
+ return NULL;
+}
+
+/*
*****************************************************************************
*
* GT64260-Specific Routines
@@ -1765,6 +1811,11 @@ gt64260a_chip_specific_init(struct mv64x
r->start = MV64x60_IRQ_SDMA_0;
r->end = MV64x60_IRQ_SDMA_0;
}
+ if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 1))
+ != NULL) {
+ r->start = GT64260_IRQ_MPSC_1;
+ r->end = GT64260_IRQ_MPSC_1;
+ }
#endif
}

Index: linux-2.6.14/arch/ppc/syslib/mv64x60_dbg.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/syslib/mv64x60_dbg.c
+++ linux-2.6.14/arch/ppc/syslib/mv64x60_dbg.c
@@ -36,7 +36,7 @@ static struct mv64x60_handle mv64x60_dbg
void
mv64x60_progress_init(u32 base)
{
- mv64x60_dbg_bh.v_base = base;
+ mv64x60_dbg_bh.v_base = (void*)base;
return;
}

@@ -71,53 +71,3 @@ mv64x60_mpsc_progress(char *s, unsigned
return;
}
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
-
-
-#if defined(CONFIG_KGDB)
-
-#if defined(CONFIG_KGDB_TTYS0)
-#define KGDB_PORT 0
-#elif defined(CONFIG_KGDB_TTYS1)
-#define KGDB_PORT 1
-#else
-#error "Invalid kgdb_tty port"
-#endif
-
-void
-putDebugChar(unsigned char c)
-{
- mv64x60_polled_putc(KGDB_PORT, (char)c);
-}
-
-int
-getDebugChar(void)
-{
- unsigned char c;
-
- while (!mv64x60_polled_getc(KGDB_PORT, &c));
- return (int)c;
-}
-
-void
-putDebugString(char* str)
-{
- while (*str != '\0') {
- putDebugChar(*str);
- str++;
- }
- putDebugChar('\r');
- return;
-}
-
-void
-kgdb_interruptible(int enable)
-{
-}
-
-void
-kgdb_map_scc(void)
-{
- if (ppc_md.early_serial_map)
- ppc_md.early_serial_map();
-}
-#endif /* CONFIG_KGDB */
Index: linux-2.6.14/arch/ppc/syslib/ppc85xx_setup.c
===================================================================
--- linux-2.6.14.orig/arch/ppc/syslib/ppc85xx_setup.c
+++ linux-2.6.14/arch/ppc/syslib/ppc85xx_setup.c
@@ -71,7 +71,6 @@ mpc85xx_calibrate_decr(void)
mtspr(SPRN_TCR, TCR_DIE);
}

-#ifdef CONFIG_SERIAL_8250
void __init
mpc85xx_early_serial_map(void)
{
@@ -87,7 +86,7 @@ mpc85xx_early_serial_map(void)
pdata[0].mapbase += binfo->bi_immr_base;
pdata[0].membase = ioremap(pdata[0].mapbase, MPC85xx_UART0_SIZE);

-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250)
memset(&serial_req, 0, sizeof (serial_req));
serial_req.iotype = SERIAL_IO_MEM;
serial_req.mapbase = pdata[0].mapbase;
@@ -95,18 +94,24 @@ mpc85xx_early_serial_map(void)
serial_req.regshift = 0;

gen550_init(0, &serial_req);
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &serial_req);
+#endif
#endif

pdata[1].uartclk = binfo->bi_busfreq;
pdata[1].mapbase += binfo->bi_immr_base;
pdata[1].membase = ioremap(pdata[1].mapbase, MPC85xx_UART0_SIZE);

-#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB_8250)
/* Assume gen550_init() doesn't modify serial_req */
serial_req.mapbase = pdata[1].mapbase;
serial_req.membase = pdata[1].membase;

gen550_init(1, &serial_req);
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(1, &serial_req);
+#endif
#endif
}
#endif
@@ -365,5 +370,3 @@ mpc85xx_setup_hose(void)
return;
}
#endif /* CONFIG_PCI */
-
-
Index: linux-2.6.14/drivers/serial/mpsc_kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/drivers/serial/mpsc_kgdb.c
@@ -0,0 +1,299 @@
+/*
+ * drivers/serial/mpsc_kgdb.c
+ *
+ * KGDB driver for the Marvell MultiProtocol Serial Controller (MPCS)
+ *
+ * Based on the polled boot loader driver by Ajit Prem ([email protected])
+ *
+ * Author: Randy Vinson <[email protected]>
+ *
+ * 2005 (c) MontaVista Software, Inc.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/kgdb.h>
+#include <linux/mv643xx.h>
+#include <linux/device.h>
+#include <asm/mv64x60.h>
+#include <asm/serial.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "mpsc.h"
+
+/* Speed of the UART. */
+static int kgdbmpsc_baud = CONFIG_KGDB_BAUDRATE;
+
+/* Index of the UART, matches ttyMX naming. */
+static int kgdbmpsc_ttyMM = CONFIG_KGDB_PORT_NUM;
+
+#define MPSC_INTR_REG_SELECT(x) ((x) + (8 * kgdbmpsc_ttyMM))
+
+static int kgdbmpsc_init(void);
+
+static struct platform_device mpsc_dev, shared_dev;
+
+static void __iomem *mpsc_base;
+static void __iomem *brg_base;
+static void __iomem *routing_base;
+static void __iomem *sdma_base;
+
+static unsigned int mpsc_irq;
+
+static void kgdb_write_debug_char(int c)
+{
+ u32 data;
+
+ data = readl(mpsc_base + MPSC_MPCR);
+ writeb(c, mpsc_base + MPSC_CHR_1);
+ mb();
+ data = readl(mpsc_base + MPSC_CHR_2);
+ data |= MPSC_CHR_2_TTCS;
+ writel(data, mpsc_base + MPSC_CHR_2);
+ mb();
+
+ while (readl(mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS) ;
+}
+
+static int kgdb_get_debug_char(void)
+{
+ unsigned char c;
+
+ while (!(readl(sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE)) &
+ MPSC_INTR_CAUSE_RCC)) ;
+
+ c = readb(mpsc_base + MPSC_CHR_10 + (1 << 1));
+ mb();
+ writeb(c, mpsc_base + MPSC_CHR_10 + (1 << 1));
+ mb();
+ writel(~MPSC_INTR_CAUSE_RCC, sdma_base +
+ MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE));
+ return (c);
+}
+
+/*
+ * This is the receiver interrupt routine for the GDB stub.
+ * All that we need to do is verify that the interrupt happened on the
+ * line we're in charge of. If this is true, schedule a breakpoint and
+ * return.
+ */
+static irqreturn_t
+kgdbmpsc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (irq != mpsc_irq)
+ return IRQ_NONE;
+ /*
+ * If there is some other CPU in KGDB then this is a
+ * spurious interrupt. so return without even checking a byte
+ */
+ if (atomic_read(&debugger_active))
+ return IRQ_NONE;
+
+ if (readl(sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE)) &
+ MPSC_INTR_CAUSE_RCC)
+ breakpoint();
+
+ return IRQ_HANDLED;
+}
+
+static int __init kgdbmpsc_init(void)
+{
+ struct mpsc_pdata *pdata;
+ u32 cdv;
+
+ if (!brg_base || !mpsc_base || !routing_base || !sdma_base)
+ return -1;
+
+ /* Set MPSC Routing to enable both ports */
+ writel(0x0, routing_base + MPSC_MRR);
+
+ /* MPSC 0/1 Rx & Tx get clocks BRG0/1 */
+ writel(0x00000100, routing_base + MPSC_RCRR);
+ writel(0x00000100, routing_base + MPSC_TCRR);
+
+ /* Disable all MPSC interrupts and clear any pending interrupts */
+ writel(0, sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_MASK));
+ writel(0, sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_CAUSE));
+
+ pdata = (struct mpsc_pdata *)mpsc_dev.dev.platform_data;
+
+ /* cdv = (clock/(2*16*baud rate)) for 16X mode. */
+ cdv = ((pdata->brg_clk_freq / (32 * kgdbmpsc_baud)) - 1);
+ writel((pdata->brg_clk_src << 18) | (1 << 16) | cdv,
+ brg_base + BRG_BCR);
+
+ /* Put MPSC into UART mode, no null modem, 16x clock mode */
+ writel(0x000004c4, mpsc_base + MPSC_MMCRL);
+ writel(0x04400400, mpsc_base + MPSC_MMCRH);
+
+ writel(0, mpsc_base + MPSC_CHR_1);
+ writel(0, mpsc_base + MPSC_CHR_9);
+ writel(0, mpsc_base + MPSC_CHR_10);
+ writel(4, mpsc_base + MPSC_CHR_3);
+ writel(0x20000000, mpsc_base + MPSC_CHR_4);
+ writel(0x9000, mpsc_base + MPSC_CHR_5);
+ writel(0, mpsc_base + MPSC_CHR_6);
+ writel(0, mpsc_base + MPSC_CHR_7);
+ writel(0, mpsc_base + MPSC_CHR_8);
+
+ /* 8 data bits, 1 stop bit */
+ writel((3 << 12), mpsc_base + MPSC_MPCR);
+
+ /* Enter "hunt" mode */
+ writel((1 << 31), mpsc_base + MPSC_CHR_2);
+
+ udelay(100);
+ return 0;
+}
+
+static void __iomem *__init
+kgdbmpsc_map_resource(struct platform_device *pd, int type, int num)
+{
+ void __iomem *base = NULL;
+ struct resource *r;
+
+ if ((r = platform_get_resource(pd, IORESOURCE_MEM, num)))
+ base = ioremap(r->start, r->end - r->start + 1);
+ return base;
+}
+
+static void __iomem *__init
+kgdbmpsc_unmap_resource(struct platform_device *pd, int type, int num,
+ void __iomem * base)
+{
+ if (base)
+ iounmap(base);
+ return NULL;
+}
+
+static void __init
+kgdbmpsc_reserve_resource(struct platform_device *pd, int type, int num)
+{
+ struct resource *r;
+
+ if ((r = platform_get_resource(pd, IORESOURCE_MEM, num)))
+ request_mem_region(r->start, r->end - r->start + 1, "kgdb");
+}
+
+static int __init kgdbmpsc_local_init(void)
+{
+ if (!mpsc_dev.num_resources || !shared_dev.num_resources)
+ return 1; /* failure */
+
+ mpsc_base = kgdbmpsc_map_resource(&mpsc_dev, IORESOURCE_MEM,
+ MPSC_BASE_ORDER);
+ brg_base = kgdbmpsc_map_resource(&mpsc_dev, IORESOURCE_MEM,
+ MPSC_BRG_BASE_ORDER);
+
+ /* get the platform data for the shared registers and get them mapped */
+ routing_base = kgdbmpsc_map_resource(&shared_dev,
+ IORESOURCE_MEM,
+ MPSC_ROUTING_BASE_ORDER);
+ sdma_base =
+ kgdbmpsc_map_resource(&shared_dev, IORESOURCE_MEM,
+ MPSC_SDMA_INTR_BASE_ORDER);
+
+ mpsc_irq = platform_get_irq(&mpsc_dev, 1);
+
+ if (mpsc_base && brg_base && routing_base && sdma_base)
+ return 0; /* success */
+
+ return 1; /* failure */
+}
+
+static void __init kgdbmpsc_local_exit(void)
+{
+ if (sdma_base)
+ sdma_base = kgdbmpsc_unmap_resource(&shared_dev, IORESOURCE_MEM,
+ MPSC_SDMA_INTR_BASE_ORDER,
+ sdma_base);
+ if (routing_base)
+ routing_base = kgdbmpsc_unmap_resource(&shared_dev,
+ IORESOURCE_MEM,
+ MPSC_ROUTING_BASE_ORDER,
+ routing_base);
+ if (brg_base)
+ brg_base = kgdbmpsc_unmap_resource(&mpsc_dev, IORESOURCE_MEM,
+ MPSC_BRG_BASE_ORDER,
+ brg_base);
+ if (mpsc_base)
+ mpsc_base = kgdbmpsc_unmap_resource(&mpsc_dev, IORESOURCE_MEM,
+ MPSC_BASE_ORDER, mpsc_base);
+}
+
+static void __init kgdbmpsc_update_pdata(struct platform_device *pdev)
+{
+
+ snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s%u", pdev->name, pdev->id);
+}
+
+static int __init kgdbmpsc_pdev_init(void)
+{
+ struct platform_device *pdev;
+
+ /* get the platform data for the specified port. */
+ pdev = mv64x60_early_get_pdev_data(MPSC_CTLR_NAME, kgdbmpsc_ttyMM, 1);
+ if (pdev) {
+ memcpy(&mpsc_dev, pdev, sizeof(struct platform_device));
+ if (platform_notify) {
+ kgdbmpsc_update_pdata(&mpsc_dev);
+ platform_notify(&mpsc_dev.dev);
+ }
+
+ /* get the platform data for the shared registers. */
+ pdev = mv64x60_early_get_pdev_data(MPSC_SHARED_NAME, 0, 0);
+ if (pdev) {
+ memcpy(&shared_dev, pdev,
+ sizeof(struct platform_device));
+ if (platform_notify) {
+ kgdbmpsc_update_pdata(&shared_dev);
+ platform_notify(&shared_dev.dev);
+ }
+ }
+ }
+ return 0;
+}
+
+postcore_initcall(kgdbmpsc_pdev_init);
+
+static int __init kgdbmpsc_init_io(void)
+{
+
+ kgdbmpsc_pdev_init();
+
+ if (kgdbmpsc_local_init()) {
+ kgdbmpsc_local_exit();
+ return -1;
+ }
+
+ if (kgdbmpsc_init() == -1)
+ return -1;
+ return 0;
+}
+
+static void __init kgdbmpsc_hookup_irq(void)
+{
+ unsigned int msk;
+ if (!request_irq(mpsc_irq, kgdbmpsc_interrupt, 0, "kgdb mpsc", NULL)) {
+ /* Enable interrupt */
+ msk = readl(sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_MASK));
+ msk |= MPSC_INTR_CAUSE_RCC;
+ writel(msk, sdma_base + MPSC_INTR_REG_SELECT(MPSC_INTR_MASK));
+
+ kgdbmpsc_reserve_resource(&mpsc_dev, IORESOURCE_MEM,
+ MPSC_BASE_ORDER);
+ kgdbmpsc_reserve_resource(&mpsc_dev, IORESOURCE_MEM,
+ MPSC_BRG_BASE_ORDER);
+ }
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_get_debug_char,
+ .write_char = kgdb_write_debug_char,
+ .init = kgdbmpsc_init_io,
+ .late_init = kgdbmpsc_hookup_irq,
+};
Index: linux-2.6.14/drivers/serial/Makefile
===================================================================
--- linux-2.6.14.orig/drivers/serial/Makefile
+++ linux-2.6.14/drivers/serial/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_SERIAL_IMX) += imx.o
obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
obj-$(CONFIG_SERIAL_ICOM) += icom.o
obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
+obj-$(CONFIG_KGDB_MPSC) += mpsc_kgdb.o
obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
obj-$(CONFIG_SERIAL_JSM) += jsm/
Index: linux-2.6.14/drivers/serial/mpsc.h
===================================================================
--- linux-2.6.14.orig/drivers/serial/mpsc.h
+++ linux-2.6.14/drivers/serial/mpsc.h
@@ -207,6 +207,10 @@ struct mpsc_port_info *mpsc_device_remov
#define MPSC_RCRR 0x0004
#define MPSC_TCRR 0x0008

+/* MPSC Interrupt registers (offset from MV64x60_SDMA_INTR_OFFSET) */
+#define MPSC_INTR_CAUSE 0x0004
+#define MPSC_INTR_MASK 0x0084
+#define MPSC_INTR_CAUSE_RCC (1<<6)
/*
*****************************************************************************
*
Index: linux-2.6.14/include/asm-ppc/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-ppc/kgdb.h
+++ linux-2.6.14/include/asm-ppc/kgdb.h
@@ -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,46 +14,31 @@

#ifndef __ASSEMBLY__

-/* Things specific to the gen550 backend. */
-struct uart_port;
-
-extern void gen550_progress(char *, unsigned short);
-extern void gen550_kgdb_map_scc(void);
-extern void gen550_init(int, struct uart_port *);
-
-/* Things specific to the pmac backend. */
-extern void zs_kgdb_hook(int tty_num);
-
-/* To init the kgdb engine. (called by serial hook)*/
-extern void set_debug_traps(void);
-
-/* To enter the debugger explicitly. */
-extern void breakpoint(void);
+#define BREAK_INSTR_SIZE 4
+#ifndef CONFIG_E500
+#define MAXREG (PT_FPSCR+1)
+#else
+/* 32 GPRs (8 bytes), nip, msr, ccr, link, ctr, xer, acc (8 bytes), spefscr*/
+#define MAXREG ((32*2)+6+2+1)
+#endif
+#define NUMREGBYTES (MAXREG * sizeof(int))
+#define BUFMAX ((NUMREGBYTES * 2) + 512)
+#define OUTBUFMAX ((NUMREGBYTES * 2) + 512)
+/* CR/LR, R1, R2, R13-R31 inclusive. */
+#define NUMCRITREGBYTES (23 * sizeof(int))
+#define BREAKPOINT() asm(".long 0x7d821008"); /* twge r2, r2 */
+#define CACHE_FLUSH_IS_SAFE 1

/* For taking exceptions
* these are defined in traps.c
*/
+struct pt_regs;
extern void (*debugger)(struct pt_regs *regs);
extern int (*debugger_bpt)(struct pt_regs *regs);
extern int (*debugger_sstep)(struct pt_regs *regs);
extern int (*debugger_iabr_match)(struct pt_regs *regs);
extern int (*debugger_dabr_match)(struct pt_regs *regs);
extern void (*debugger_fault_handler)(struct pt_regs *regs);
-
-/* What we bring to the party */
-int kgdb_bpt(struct pt_regs *regs);
-int kgdb_sstep(struct pt_regs *regs);
-void kgdb(struct pt_regs *regs);
-int kgdb_iabr_match(struct pt_regs *regs);
-int kgdb_dabr_match(struct pt_regs *regs);
-
-/*
- * external low-level support routines (ie macserial.c)
- */
-extern void kgdb_interruptible(int); /* control interrupts from serial */
-extern void putDebugChar(char); /* write a single character */
-extern char getDebugChar(void); /* read and return a single char */
-
#endif /* !(__ASSEMBLY__) */
#endif /* !(_PPC_KGDB_H) */
#endif /* __KERNEL__ */
Index: linux-2.6.14/include/asm-ppc/machdep.h
===================================================================
--- linux-2.6.14.orig/include/asm-ppc/machdep.h
+++ linux-2.6.14/include/asm-ppc/machdep.h
@@ -59,9 +59,7 @@ struct machdep_calls {
unsigned long (*find_end_of_memory)(void);
void (*setup_io_mappings)(void);

- void (*early_serial_map)(void);
void (*progress)(char *, unsigned short);
- void (*kgdb_map_scc)(void);

unsigned char (*nvram_read_val)(int addr);
void (*nvram_write_val)(int addr, unsigned char val);
Index: linux-2.6.14/include/asm-ppc/mv64x60_defs.h
===================================================================
--- linux-2.6.14.orig/include/asm-ppc/mv64x60_defs.h
+++ linux-2.6.14/include/asm-ppc/mv64x60_defs.h
@@ -57,7 +57,8 @@
#define MV64x60_IRQ_I2C 37
#define MV64x60_IRQ_BRG 39
#define MV64x60_IRQ_MPSC_0 40
-#define MV64x60_IRQ_MPSC_1 42
+#define MV64360_IRQ_MPSC_1 41
+#define GT64260_IRQ_MPSC_1 42
#define MV64x60_IRQ_COMM 43
#define MV64x60_IRQ_P0_GPP_0_7 56
#define MV64x60_IRQ_P0_GPP_8_15 57
Index: linux-2.6.14/include/asm-ppc/mv64x60.h
===================================================================
--- linux-2.6.14.orig/include/asm-ppc/mv64x60.h
+++ linux-2.6.14/include/asm-ppc/mv64x60.h
@@ -347,6 +347,8 @@ u32 mv64x60_calc_mem_size(struct mv64x60

void mv64x60_progress_init(u32 base);
void mv64x60_mpsc_progress(char *s, unsigned short hex);
+struct platform_device * mv64x60_early_get_pdev_data(const char *name,
+ int id, int remove);

extern struct mv64x60_32bit_window
gt64260_32bit_windows[MV64x60_32BIT_WIN_COUNT];
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -187,7 +187,7 @@ config WANT_EXTRA_DEBUG_INFORMATION
config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- 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. It is strongly suggested that you enable
@@ -213,6 +213,8 @@ choice
prompt "Method for KGDB communication"
depends on KGDB
default KGDB_ONLY_MODULES
+ default KGDB_MPSC if SERIAL_MPSC
+ default KGDB_CPM_UART if (8xx || 8260)
help
There are a number of different ways in which you can communicate
with KGDB. The most common is via serial, with the 8250 driver
@@ -230,4 +232,17 @@ config KGDB_ONLY_MODULES
Use only kernel modules to configure KGDB I/O after the
kernel is booted.

+config KGDB_MPSC
+ bool "KGDB on MV64x60 MPSC"
+ depends on SERIAL_MPSC
+ help
+ Uses a Marvell GT64260B or MV64x60 Multi-Purpose Serial
+ Controller (MPSC) channel. Note that the GT64260A is not
+ supported.
+
+config KGDB_CPM_UART
+ bool "KGDB: On CPM UART"
+ depends on PPC && (CPM2 || 8xx)
+ help
+ Uses CPM UART to communicate with the host GDB.
endchoice
Index: linux-2.6.14/drivers/serial/cpm_uart/Makefile
===================================================================
--- linux-2.6.14.orig/drivers/serial/cpm_uart/Makefile
+++ linux-2.6.14/drivers/serial/cpm_uart/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o
# Select the correct platform objects.
cpm_uart-objs-$(CONFIG_CPM2) += cpm_uart_cpm2.o
cpm_uart-objs-$(CONFIG_8xx) += cpm_uart_cpm1.o
+cpm_uart-objs-$(CONFIG_KGDB) += cpm_uart_kgdb.o

cpm_uart-objs := cpm_uart_core.o $(cpm_uart-objs-y)
Index: linux-2.6.14/drivers/serial/cpm_uart/cpm_uart.h
===================================================================
--- linux-2.6.14.orig/drivers/serial/cpm_uart/cpm_uart.h
+++ linux-2.6.14/drivers/serial/cpm_uart/cpm_uart.h
@@ -42,6 +42,38 @@

#define SCC_WAIT_CLOSING 100

+#ifdef CONFIG_KGDB
+
+/* Speed of the debug UART. */
+#if defined(CONFIG_KGDB_9600BAUD)
+#define KGDB_BAUD B9600
+#elif defined(CONFIG_KGDB_19200BAUD)
+#define KGDB_BAUD B19200
+#elif defined(CONFIG_KGDB_38400BAUD)
+#define KGDB_BAUD B38400
+#elif defined(CONFIG_KGDB_57600BAUD)
+#define KGDB_BAUD B57600
+#else
+#define KGDB_BAUD B115200 /* Start with this if not given */
+#endif
+
+#ifdef CONFIG_KGDB_CPM_UART_SCC1
+#define KGDB_PINFO_INDEX UART_SCC1
+#elif CONFIG_KGDB_CPM_UART_SCC2
+#define KGDB_PINFO_INDEX UART_SCC2
+#elif CONFIG_KGDB_CPM_UART_SCC3
+#define KGDB_PINFO_INDEX UART_SCC3
+#elif CONFIG_KGDB_CPM_UART_SCC4
+#define KGDB_PINFO_INDEX UART_SCC4
+#elif CONFIG_KGDB_CPM_UART_SMC1
+#define KGDB_PINFO_INDEX UART_SMC1
+#elif CONFIG_KGDB_CPM_UART_SMC2
+#define KGDB_PINFO_INDEX UART_SMC2
+#error The S(M)CC for kgdb console is undefined
+#endif
+
+#endif /* CONFIG_KGDB */
+
struct uart_cpm_port {
struct uart_port port;
u16 rx_nrfifos;
@@ -77,6 +109,9 @@ extern int cpm_uart_port_map[UART_NR];
extern int cpm_uart_nr;
extern struct uart_cpm_port cpm_uart_ports[UART_NR];

+void cpm_uart_early_write(int index, const char *s, u_int count);
+int cpm_uart_early_setup(int index,int early);
+
/* these are located in their respective files */
void cpm_line_cr_cmd(int line, int cmd);
int cpm_uart_init_portdesc(void);
@@ -90,4 +125,19 @@ void scc2_lineif(struct uart_cpm_port *p
void scc3_lineif(struct uart_cpm_port *pinfo);
void scc4_lineif(struct uart_cpm_port *pinfo);

+static inline unsigned long cpu2cpm_addr(void *addr)
+{
+ if ((unsigned long)addr >= CPM_ADDR)
+ return (unsigned long)addr;
+ return virt_to_bus(addr);
+}
+
+static inline void *cpm2cpu_addr(unsigned long addr)
+{
+ if (addr >= CPM_ADDR)
+ return (void *)addr;
+ return bus_to_virt(addr);
+}
+
+
#endif /* CPM_UART_H */
Index: linux-2.6.14/drivers/serial/cpm_uart/cpm_uart_core.c
===================================================================
--- linux-2.6.14.orig/drivers/serial/cpm_uart/cpm_uart_core.c
+++ linux-2.6.14/drivers/serial/cpm_uart/cpm_uart_core.c
@@ -71,20 +71,6 @@ static void cpm_uart_initbd(struct uart_

/**************************************************************/

-static inline unsigned long cpu2cpm_addr(void *addr)
-{
- if ((unsigned long)addr >= CPM_ADDR)
- return (unsigned long)addr;
- return virt_to_bus(addr);
-}
-
-static inline void *cpm2cpu_addr(unsigned long addr)
-{
- if (addr >= CPM_ADDR)
- return (void *)addr;
- return bus_to_virt(addr);
-}
-
/*
* Check, if transmit buffers are processed
*/
@@ -993,22 +979,17 @@ struct uart_cpm_port cpm_uart_ports[UART
},
};

-#ifdef CONFIG_SERIAL_CPM_CONSOLE
-/*
- * Print a string to the serial port trying not to disturb
- * any possible real use of the port...
- *
- * Note that this is called with interrupts already disabled
- */
-static void cpm_uart_console_write(struct console *co, const char *s,
+void cpm_uart_early_write(int index, const char *s,
u_int count)
{
- struct uart_cpm_port *pinfo =
- &cpm_uart_ports[cpm_uart_port_map[co->index]];
+ struct uart_cpm_port *pinfo;
unsigned int i;
volatile cbd_t *bdp, *bdbase;
volatile unsigned char *cp;

+ BUG_ON(index>UART_NR);
+ pinfo = &cpm_uart_ports[index];
+
/* Get the address of the host memory buffer.
*/
bdp = pinfo->tx_cur;
@@ -1072,36 +1053,16 @@ static void cpm_uart_console_write(struc
pinfo->tx_cur = (volatile cbd_t *) bdp;
}

-/*
- * Setup console. Be careful is called early !
- */
-static int __init cpm_uart_console_setup(struct console *co, char *options)
+int cpm_uart_early_setup(int index, int early)
{
+ int ret;
struct uart_port *port;
struct uart_cpm_port *pinfo;
- int baud = 38400;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
- int ret;

+ BUG_ON(index>UART_NR);
port =
- (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]];
+ (struct uart_port *)&cpm_uart_ports[index];
pinfo = (struct uart_cpm_port *)port;
-
- pinfo->flags |= FLAG_CONSOLE;
-
- if (options) {
- uart_parse_options(options, &baud, &parity, &bits, &flow);
- } else {
- bd_t *bd = (bd_t *) __res;
-
- if (bd->bi_baudrate)
- baud = bd->bi_baudrate;
- else
- baud = 9600;
- }
-
/*
* Setup any port IO, connect any baud rate generators,
* etc. This is expected to be handled by board
@@ -1109,7 +1070,6 @@ static int __init cpm_uart_console_setup
*/
if (pinfo->set_lineif)
pinfo->set_lineif(pinfo);
-
if (IS_SMC(pinfo)) {
pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
@@ -1117,8 +1077,7 @@ static int __init cpm_uart_console_setup
pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
}
-
- ret = cpm_uart_allocbuf(pinfo, 1);
+ ret = cpm_uart_allocbuf(pinfo, early);

if (ret)
return ret;
@@ -1130,6 +1089,56 @@ static int __init cpm_uart_console_setup
else
cpm_uart_init_scc(pinfo);

+ return 0;
+}
+
+#ifdef CONFIG_SERIAL_CPM_CONSOLE
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ * Note that this is called with interrupts already disabled
+ */
+
+static void cpm_uart_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ cpm_uart_early_write(cpm_uart_port_map[co->index],s,count);
+}
+
+/*
+ * Setup console. Be careful is called early !
+ */
+static int __init cpm_uart_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ struct uart_cpm_port *pinfo;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret;
+
+ port =
+ (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]];
+ pinfo = (struct uart_cpm_port *)port;
+
+ pinfo->flags |= FLAG_CONSOLE;
+
+ if (options) {
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ } else {
+ bd_t *bd = (bd_t *) __res;
+
+ if (bd->bi_baudrate)
+ baud = bd->bi_baudrate;
+ else
+ baud = 9600;
+ }
+
+ ret = cpm_uart_early_setup(cpm_uart_port_map[co->index], 1);
+ if(ret)
+ return ret;
uart_set_options(port, co, baud, parity, bits, flow);

return 0;
@@ -1191,6 +1200,10 @@ static int __init cpm_uart_init(void)

for (i = 0; i < cpm_uart_nr; i++) {
int con = cpm_uart_port_map[i];
+
+ /* We are not interested in ports yet utilized by kgdb */
+ if(con == KGDB_PINFO_INDEX)
+ continue;
cpm_uart_ports[con].port.line = i;
cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF;
uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port);
Index: linux-2.6.14/drivers/serial/cpm_uart/cpm_uart_cpm2.c
===================================================================
--- linux-2.6.14.orig/drivers/serial/cpm_uart/cpm_uart_cpm2.c
+++ linux-2.6.14/drivers/serial/cpm_uart/cpm_uart_cpm2.c
@@ -261,6 +261,10 @@ int cpm_uart_init_portdesc(void)
{
pr_debug("CPM uart[-]:init portdesc\n");

+ /* Check if we have called this yet. This may happen if early kgdb
+ breakpoint is on */
+ if(cpm_uart_nr)
+ return 0;
cpm_uart_nr = 0;
#ifdef CONFIG_SERIAL_CPM_SMC1
cpm_uart_ports[UART_SMC1].smcp = (smc_t *) & cpm2_immr->im_smc[0];
Index: linux-2.6.14/drivers/serial/cpm_uart/cpm_uart_kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/drivers/serial/cpm_uart/cpm_uart_kgdb.c
@@ -0,0 +1,191 @@
+/*
+ * drivers/serial/cpm_uart/cpm_uart_kgdb.c
+ *
+ * CPM UART interface for kgdb.
+ *
+ * Author: Vitaly Bordug <[email protected]>
+ *
+ * Used some bits from drivers/serial/kgdb_8250.c as a template
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/kgdb.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include <asm/io.h>
+#include <asm/serial.h> /* For BASE_BAUD and SERIAL_PORT_DFNS */
+
+#include "cpm_uart.h"
+
+#define GDB_BUF_SIZE 512 /* power of 2, please */
+
+
+static char kgdb_buf[GDB_BUF_SIZE], *kgdbp;
+static int kgdb_chars;
+
+/* Forward declarations. */
+
+/*
+ * Receive character from the serial port. This only works well
+ * before the port is initialize for real use.
+ */
+static int kgdb_wait_key(char *obuf)
+{
+ struct uart_cpm_port *pinfo;
+
+ u_char c, *cp;
+ volatile cbd_t *bdp;
+ int i;
+
+ pinfo = &cpm_uart_ports[KGDB_PINFO_INDEX];
+
+ /* Get the address of the host memory buffer.
+ */
+ bdp = pinfo->rx_cur;
+ while (bdp->cbd_sc & BD_SC_EMPTY);
+
+ /* If the buffer address is in the CPM DPRAM, don't
+ * convert it.
+ */
+ cp = cpm2cpu_addr(bdp->cbd_bufaddr);
+
+ if (obuf) {
+ i = c = bdp->cbd_datlen;
+ while (i-- > 0)
+ {
+ *obuf++ = *cp++;
+ }
+ } else {
+ c = *cp;
+ }
+ bdp->cbd_sc |= BD_SC_EMPTY;
+
+ if (bdp->cbd_sc & BD_SC_WRAP) {
+ bdp = pinfo->rx_bd_base;
+ } else {
+ bdp++;
+ }
+ pinfo->rx_cur = (cbd_t *)bdp;
+
+ return((int)c);
+}
+
+
+/*
+ * Wait until the interface can accept a char, then write it.
+ */
+static void
+kgdb_put_debug_char(int chr)
+{
+ static char ch[2];
+ ch[0]=(char)chr;
+ cpm_uart_early_write(KGDB_PINFO_INDEX, ch, 1);
+}
+
+
+/*
+ * Get a char if available, return -1 if nothing available.
+ * Empty the receive buffer first, then look at the interface hardware.
+ */
+static int
+kgdb_get_debug_char(void)
+{
+ if (kgdb_chars<=0) {
+ kgdb_chars = kgdb_wait_key(kgdb_buf);
+ kgdbp = kgdb_buf;
+ }
+ kgdb_chars--;
+
+ return (*kgdbp++);
+}
+
+static void termios_set_options(int index,
+ int baud, int parity, int bits, int flow)
+{
+ struct termios termios;
+ struct uart_port *port;
+ struct uart_cpm_port *pinfo;
+
+ BUG_ON(index>UART_NR);
+
+ port =
+ (struct uart_port *)&cpm_uart_ports[index];
+ pinfo = (struct uart_cpm_port *)port;
+
+ /*
+ * Ensure that the serial console lock is initialised
+ * early.
+ */
+ spin_lock_init(&port->lock);
+
+ memset(&termios, 0, sizeof(struct termios));
+
+ termios.c_cflag = CREAD | HUPCL | CLOCAL;
+
+ termios.c_cflag |= baud;
+
+ if (bits == 7)
+ termios.c_cflag |= CS7;
+ else
+ termios.c_cflag |= CS8;
+
+ switch (parity) {
+ case 'o': case 'O':
+ termios.c_cflag |= PARODD;
+ /*fall through*/
+ case 'e': case 'E':
+ termios.c_cflag |= PARENB;
+ break;
+ }
+
+ if (flow == 'r')
+ termios.c_cflag |= CRTSCTS;
+
+ port->ops->set_termios(port, &termios, NULL);
+}
+
+/*
+ * Returns:
+ * 0 on success, 1 on failure.
+ */
+static int kgdb_init(void)
+{
+ struct uart_port *port;
+ struct uart_cpm_port *pinfo;
+
+ int use_bootmem = 0; /* use dma by default */
+
+ if(!cpm_uart_nr)
+ {
+ use_bootmem = 1;
+ cpm_uart_init_portdesc();
+ }
+ port = (struct uart_port *)&cpm_uart_ports[KGDB_PINFO_INDEX];
+ pinfo = (struct uart_cpm_port *)port;
+
+ if (cpm_uart_early_setup(KGDB_PINFO_INDEX, use_bootmem))
+ return 1;
+
+ termios_set_options(KGDB_PINFO_INDEX, KGDB_BAUD,'n',8,'n');
+ pinfo->sccp->scc_sccm |= UART_SCCM_TX;
+ return 0;
+}
+
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_get_debug_char,
+ .write_char = kgdb_put_debug_char,
+ .init = kgdb_init,
+};
+

--
Tom

2005-11-10 16:40:09

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 02/15] KGDB: i386-specific changes

This adds the basic support for i386. The only real changes outside of new
KGDB files and Makefile/related is that for support early on we must set some
traps sooner rather than later, but it is safe to always do this. Also, to
break in as early as possible, i386 now calls parse_early_param() to
explicitly look at anything marked early_param(). We also add a few more
notify_die() calls in areas where KGDB needs to take a peek sometimes.
Finally, we add some labels to switch_to macros so that when backtracing we
can see where we really are.

arch/i386/kernel/Makefile | 1
arch/i386/kernel/kgdb-jmp.S | 74 ++++++++++
arch/i386/kernel/kgdb.c | 300 ++++++++++++++++++++++++++++++++++++++++++++
arch/i386/kernel/setup.c | 3
arch/i386/kernel/traps.c | 14 +-
arch/i386/mm/fault.c | 4
include/asm-i386/kdebug.h | 1
include/asm-i386/kgdb.h | 49 +++++++
include/asm-i386/system.h | 8 +
lib/Kconfig.debug | 2
10 files changed, 449 insertions(+), 7 deletions(-)

Index: linux-2.6.14/arch/i386/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/i386/kernel/kgdb.c
@@ -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]>
+ * Updated by: Tom Rini <[email protected]>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by
+ * David Grothe <[email protected]>
+ * Additional support from 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 <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 <asm/apicdef.h>
+#include <asm/desc.h>
+#include <asm/kdebug.h>
+
+#include "mach_ipi.h"
+
+/* Put the error code here just in case the user cares. */
+int gdb_i386errcode;
+/* Likewise, the vector number here (since GDB only gets the signal
+ number through the usual means, and that's not very specific). */
+int gdb_i386vector = -1;
+
+extern atomic_t cpu_doing_single_step;
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ gdb_regs[_EAX] = regs->eax;
+ gdb_regs[_EBX] = regs->ebx;
+ gdb_regs[_ECX] = regs->ecx;
+ gdb_regs[_EDX] = regs->edx;
+ gdb_regs[_ESI] = regs->esi;
+ gdb_regs[_EDI] = regs->edi;
+ gdb_regs[_EBP] = regs->ebp;
+ gdb_regs[_DS] = regs->xds;
+ gdb_regs[_ES] = regs->xes;
+ gdb_regs[_PS] = regs->eflags;
+ gdb_regs[_CS] = regs->xcs;
+ gdb_regs[_PC] = regs->eip;
+ gdb_regs[_ESP] = (int)(&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];
+}
+
+static struct hw_breakpoint {
+ unsigned enabled;
+ unsigned type;
+ unsigned len;
+ unsigned addr;
+} breakinfo[4] = {
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+};
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+ int correctit;
+ int breakbit;
+ unsigned dr7;
+
+ asm volatile ("movl %%db7, %0\n":"=r" (dr7)
+ :);
+ do {
+ unsigned addr0, addr1, addr2, addr3;
+ asm volatile ("movl %%db0, %0\n"
+ "movl %%db1, %1\n"
+ "movl %%db2, %2\n"
+ "movl %%db3, %3\n":"=r" (addr0), "=r"(addr1),
+ "=r"(addr2), "=r"(addr3):);
+ } while (0);
+ correctit = 0;
+ for (breakno = 0; breakno < 3; breakno++) {
+ breakbit = 2 << (breakno << 1);
+ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 |= breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ dr7 |= (((breakinfo[breakno].len << 2) |
+ breakinfo[breakno].type) << 16) <<
+ (breakno << 2);
+ switch (breakno) {
+ case 0:
+ asm volatile ("movl %0, %%dr0\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 1:
+ asm volatile ("movl %0, %%dr1\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 2:
+ asm volatile ("movl %0, %%dr2\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 3:
+ asm volatile ("movl %0, %%dr3\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+ }
+ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 &= ~breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ }
+ }
+ if (correctit)
+ asm volatile ("movl %0, %%db7\n"::"r" (dr7));
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ /* Disable hardware debugging while we are in kgdb */
+ asm volatile ("movl %0,%%db7": /* no output */ :"r" (0));
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_i386vector = e_vector;
+ gdb_i386errcode = err_code;
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ send_IPI_allbutself(APIC_DM_NMI);
+}
+
+int kgdb_arch_handle_exception(int e_vector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ long addr;
+ char *ptr;
+ int newPC, dr6;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ case 's':
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr))
+ linux_regs->eip = addr;
+ newPC = linux_regs->eip;
+
+ /* clear the trace bit */
+ linux_regs->eflags &= ~TF_MASK;
+ atomic_set(&cpu_doing_single_step, -1);
+
+ /* set the trace bit if we're stepping */
+ if (remcom_in_buffer[0] == 's') {
+ linux_regs->eflags |= TF_MASK;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,
+ smp_processor_id());
+ }
+
+ asm volatile ("movl %%db6, %0\n":"=r" (dr6));
+ if (!(dr6 & 0x4000)) {
+ long breakno;
+ for (breakno = 0; breakno < 4; ++breakno) {
+ if (dr6 & (1 << breakno) &&
+ breakinfo[breakno].type == 0) {
+ /* Set restore flag */
+ linux_regs->eflags |= X86_EFLAGS_RF;
+ break;
+ }
+ }
+ }
+ kgdb_correct_hw_break();
+ asm volatile ("movl %0, %%db6\n"::"r" (0));
+
+ return (0);
+ } /* switch */
+ /* this means that we do not want to exit from the handler */
+ return -1;
+}
+
+/* Register KGDB with the i386die_chain so that we hook into all of the right
+ * spots. */
+static int kgdb_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *args = ptr;
+ struct pt_regs *regs = args->regs;
+
+ /* Bad memory access? */
+ if (cmd == DIE_PAGE_FAULT_NO_CONTEXT && atomic_read(&debugger_active)
+ && kgdb_may_fault) {
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ return NOTIFY_STOP;
+ } else if (cmd == DIE_PAGE_FAULT)
+ /* A normal page fault, ignore. */
+ return NOTIFY_DONE;
+ else if (cmd == DIE_NMI && atomic_read(&debugger_active)) {
+ /* CPU roundup */
+ kgdb_nmihook(smp_processor_id(), regs);
+ return NOTIFY_STOP;
+ } else if (cmd == DIE_NMI_IPI || user_mode(regs) ||
+ (cmd == DIE_DEBUG && atomic_read(&debugger_active)))
+ /* Normal watchdog event or userspace debugging, or spurious
+ * debug exception, ignore. */
+ return NOTIFY_DONE;
+
+ kgdb_handle_exception(args->trapnr, args->signr, args->err, regs);
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_notify,
+};
+
+int kgdb_arch_init(void)
+{
+ notifier_chain_register(&i386die_chain, &kgdb_notifier);
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0xcc},
+};
Index: linux-2.6.14/arch/i386/kernel/kgdb-jmp.S
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/i386/kernel/kgdb-jmp.S
@@ -0,0 +1,74 @@
+/*
+ * arch/i386/kernel/kgdb-jmp.S
+ *
+ * Save and restore system registers so that within a limited frame we
+ * may have a fault and "jump back" to a known safe location.
+ *
+ * Author: George Anzinger <[email protected]>
+ *
+ * Cribbed from glibc, which carries the following:
+ * Copyright (C) 1996, 1996, 1997, 2000, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2005 by MontaVista Software.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include <linux/linkage.h>
+
+#define PCOFF 0
+#define LINKAGE 4 /* just the return address */
+#define PTR_SIZE 4
+#define PARMS LINKAGE /* no space for saved regs */
+#define JMPBUF PARMS
+#define VAL JMPBUF+PTR_SIZE
+
+#define JB_BX 0
+#define JB_SI 1
+#define JB_DI 2
+#define JB_BP 3
+#define JB_SP 4
+#define JB_PC 5
+
+/* This must be called prior to kgdb_fault_longjmp and
+ * kgdb_fault_longjmp must not be called outside of the context of the
+ * last call to kgdb_fault_setjmp.
+ * kgdb_fault_setjmp(int *jmp_buf[6])
+ */
+ENTRY(kgdb_fault_setjmp)
+ movl JMPBUF(%esp), %eax
+
+ /* Save registers. */
+ movl %ebx, (JB_BX*4)(%eax)
+ movl %esi, (JB_SI*4)(%eax)
+ movl %edi, (JB_DI*4)(%eax)
+ /* Save SP as it will be after we return. */
+ leal JMPBUF(%esp), %ecx
+ movl %ecx, (JB_SP*4)(%eax)
+ movl PCOFF(%esp), %ecx /* Save PC we are returning to now. */
+ movl %ecx, (JB_PC*4)(%eax)
+ movl %ebp, (JB_BP*4)(%eax) /* Save caller's frame pointer. */
+
+ /* Restore state so we can now try the access. */
+ movl JMPBUF(%esp), %ecx /* User's jmp_buf in %ecx. */
+ /* Save the return address now. */
+ movl (JB_PC*4)(%ecx), %edx
+ /* Restore registers. */
+ movl $0, %eax
+ movl (JB_SP*4)(%ecx), %esp
+ jmp *%edx /* Jump to saved PC. */
+
+/* kgdb_fault_longjmp(int *jmp_buf[6]) */
+ENTRY(kgdb_fault_longjmp)
+ movl JMPBUF(%esp), %ecx /* User's jmp_buf in %ecx. */
+ /* Save the return address now. */
+ movl (JB_PC*4)(%ecx), %edx
+ /* Restore registers. */
+ movl (JB_BX*4)(%ecx), %ebx
+ movl (JB_SI*4)(%ecx), %esi
+ movl (JB_DI*4)(%ecx), %edi
+ movl (JB_BP*4)(%ecx), %ebp
+ movl $1, %eax
+ movl (JB_SP*4)(%ecx), %esp
+ jmp *%edx /* Jump to saved PC. */
Index: linux-2.6.14/arch/i386/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/i386/kernel/Makefile
+++ linux-2.6.14/arch/i386/kernel/Makefile
@@ -34,6 +34,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 kgdb-jmp.o

EXTRA_AFLAGS := -traditional

Index: linux-2.6.14/arch/i386/kernel/setup.c
===================================================================
--- linux-2.6.14.orig/arch/i386/kernel/setup.c
+++ linux-2.6.14/arch/i386/kernel/setup.c
@@ -148,6 +148,7 @@ EXPORT_SYMBOL(ist_info);
struct e820map e820;

extern void early_cpu_init(void);
+extern void early_trap_init(void);
extern void dmi_scan_machine(void);
extern void generic_apic_probe(char *);
extern int root_mountflags;
@@ -1481,6 +1482,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
@@ -1537,6 +1539,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();

Index: linux-2.6.14/arch/i386/kernel/traps.c
===================================================================
--- linux-2.6.14.orig/arch/i386/kernel/traps.c
+++ linux-2.6.14/arch/i386/kernel/traps.c
@@ -333,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");
@@ -759,6 +759,7 @@ fastcall void __kprobes do_debug(struct
*/
clear_dr7:
set_debugreg(0, 7);
+ notify_die(DIE_DEBUG, "debug2", regs, condition, error_code, SIGTRAP);
return;

debug_vm86:
@@ -1063,6 +1064,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)
{
@@ -1079,10 +1086,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);
@@ -1092,7 +1097,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);
Index: linux-2.6.14/arch/i386/mm/fault.c
===================================================================
--- linux-2.6.14.orig/arch/i386/mm/fault.c
+++ linux-2.6.14/arch/i386/mm/fault.c
@@ -433,6 +433,10 @@ no_context:
if (is_prefetch(regs, address, error_code))
return;

+ if (notify_die(DIE_PAGE_FAULT_NO_CONTEXT, "no context", regs,
+ error_code, 14, SIGSEGV) == NOTIFY_STOP)
+ return;
+
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
Index: linux-2.6.14/include/asm-i386/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-i386/kgdb.h
@@ -0,0 +1,49 @@
+#ifdef __KERNEL__
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+/************************************************************************/
+/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
+/* at least NUMREGBYTES*2 are needed for register packets */
+/* Longer buffer is needed to list all threads */
+#define BUFMAX 1024
+
+/* Number of bytes of registers. */
+#define NUMREGBYTES 64
+/* Number of bytes of registers we need to save for a setjmp/longjmp. */
+#define NUMCRITREGBYTES 24
+
+/*
+ * Note that this register image is in a different order than
+ * the register image that Linux produces at interrupt time.
+ *
+ * Linux's register image is defined by struct pt_regs in ptrace.h.
+ * Just why GDB uses a different order is a historical mystery.
+ */
+enum regnames { _EAX, /* 0 */
+ _ECX, /* 1 */
+ _EDX, /* 2 */
+ _EBX, /* 3 */
+ _ESP, /* 4 */
+ _EBP, /* 5 */
+ _ESI, /* 6 */
+ _EDI, /* 7 */
+ _PC, /* 8 also known as eip */
+ _PS, /* 9 also known as eflags */
+ _CS, /* 10 */
+ _SS, /* 11 */
+ _DS, /* 12 */
+ _ES, /* 13 */
+ _FS, /* 14 */
+ _GS /* 15 */
+};
+
+#define BREAKPOINT() asm(" int $3");
+#define BREAK_INSTR_SIZE 1
+#define CACHE_FLUSH_IS_SAFE 1
+#endif /* _ASM_KGDB_H_ */
+#endif /* __KERNEL__ */
Index: linux-2.6.14/include/asm-i386/system.h
===================================================================
--- linux-2.6.14.orig/include/asm-i386/system.h
+++ linux-2.6.14/include/asm-i386/system.h
@@ -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("pushl %%ebp\n\t" \
+ asm volatile(".globl __switch_to_begin\n" \
+ "__switch_to_begin:" \
+ "pushl %%ebp\n\t" \
"movl %%esp,%0\n\t" /* save ESP */ \
"movl %5,%%esp\n\t" /* restore ESP */ \
"movl $1f,%1\n\t" /* save EIP */ \
@@ -22,6 +26,8 @@ extern struct task_struct * FASTCALL(__s
"jmp __switch_to\n" \
"1:\t" \
"popl %%ebp\n\t" \
+ ".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), \
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -187,7 +187,7 @@ config WANT_EXTRA_DEBUG_INFORMATION
config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- 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. It is strongly suggested that you enable
Index: linux-2.6.14/include/asm-i386/kdebug.h
===================================================================
--- linux-2.6.14.orig/include/asm-i386/kdebug.h
+++ linux-2.6.14/include/asm-i386/kdebug.h
@@ -39,6 +39,7 @@ enum die_val {
DIE_CALL,
DIE_NMI_IPI,
DIE_PAGE_FAULT,
+ DIE_PAGE_FAULT_NO_CONTEXT,
};

static inline int notify_die(enum die_val val, const char *str,

--
Tom

2005-11-10 16:41:12

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 04/15] KGDB: 8250-like UART driver

This is the I/O driver for any 8250-compatible UARTs. This also adds some
small hooks into the normal serial core so that we can take away the uart and
make sure only KGDB is trying to use it. We also make sure that if KGDB_8250
is enabled, SERIAL_8250_NR_UARTS is set to the default of 4. This is so that
if we can always depend on that constant in the 8250 driver for giving our
table a size. We add a hook to the main core for unregistering a port
with a struct uart_port and add a hook into uart_add_one_port for
discovered 8250-like uarts.

drivers/serial/8250.c | 21 +
drivers/serial/8250_kgdb.c | 504 +++++++++++++++++++++++++++++++++++++++++++
drivers/serial/Kconfig | 2
drivers/serial/Makefile | 1
drivers/serial/serial_core.c | 7
include/linux/serial_8250.h | 1
lib/Kconfig.debug | 59 ++++-
7 files changed, 593 insertions(+), 2 deletions(-)

Index: linux-2.6.14/drivers/serial/8250.c
===================================================================
--- linux-2.6.14.orig/drivers/serial/8250.c
+++ linux-2.6.14/drivers/serial/8250.c
@@ -2520,6 +2520,27 @@ void serial8250_unregister_port(int line
}
EXPORT_SYMBOL(serial8250_unregister_port);

+/**
+ * serial8250_unregister_by_port - remove a 16x50 serial port
+ * at runtime.
+ * @port: A &struct uart_port that describes the port to remove.
+ *
+ * Remove one serial port. This may not be called from interrupt
+ * context. We hand the port back to the our control.
+ */
+void serial8250_unregister_by_port(struct uart_port *port)
+{
+ struct uart_8250_port *uart;
+
+ down(&serial_sem);
+ uart = serial8250_find_match_or_unused(port);
+ up(&serial_sem);
+
+ if (uart)
+ serial8250_unregister_port(uart->port.line);
+}
+EXPORT_SYMBOL(serial8250_unregister_by_port);
+
static int __init serial8250_init(void)
{
int ret, i;
Index: linux-2.6.14/drivers/serial/Kconfig
===================================================================
--- linux-2.6.14.orig/drivers/serial/Kconfig
+++ linux-2.6.14/drivers/serial/Kconfig
@@ -87,7 +87,7 @@ config SERIAL_8250_ACPI

config SERIAL_8250_NR_UARTS
int "Maximum number of 8250/16550 serial ports"
- depends on SERIAL_8250
+ depends on SERIAL_8250 || KGDB_8250
default "4"
help
Set this to the number of serial ports you want the driver
Index: linux-2.6.14/drivers/serial/Makefile
===================================================================
--- linux-2.6.14.orig/drivers/serial/Makefile
+++ linux-2.6.14/drivers/serial/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_SERIAL_JSM) += jsm/
obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
+obj-$(CONFIG_KGDB_8250) += 8250_kgdb.o
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -212,7 +212,7 @@ config KGDB_CONSOLE
choice
prompt "Method for KGDB communication"
depends on KGDB
- default KGDB_ONLY_MODULES
+ default KGDB_8250_NOMODULE
default KGDB_MPSC if SERIAL_MPSC
default KGDB_CPM_UART if (8xx || 8260)
help
@@ -232,6 +232,14 @@ config KGDB_ONLY_MODULES
Use only kernel modules to configure KGDB I/O after the
kernel is booted.

+config KGDB_8250_NOMODULE
+ bool "KGDB: On generic serial port (8250)"
+ select KGDB_8250
+ help
+ Uses generic serial port (8250) to communicate with the host
+ GDB. This is independent of the normal (SERIAL_8250) driver
+ for this chipset.
+
config KGDB_MPSC
bool "KGDB on MV64x60 MPSC"
depends on SERIAL_MPSC
@@ -246,3 +254,52 @@ config KGDB_CPM_UART
help
Uses CPM UART to communicate with the host GDB.
endchoice
+
+config KGDB_8250
+ tristate "KGDB: On generic serial port (8250)" if !KGDB_8250_NOMODULE
+ depends on m && KGDB_ONLY_MODULES
+ help
+ Uses generic serial port (8250) to communicate with the host
+ GDB. This is independent of the normal (SERIAL_8250) driver
+ for this chipset.
+
+config KGDB_SIMPLE_SERIAL
+ bool "Simple selection of KGDB serial port"
+ depends on KGDB_8250
+ default y
+ help
+ If you say Y here, you will only have to pick the baud rate
+ and port number that you wish to use for KGDB. Note that this
+ only works on architectures that register known serial ports
+ early on. If you say N, you will have to provide, either here
+ or on the command line, the type (I/O or MMIO), IRQ and
+ address to use. If in doubt, say Y.
+
+config KGDB_BAUDRATE
+ int "Debug serial port baud rate"
+ depends on (KGDB_8250 && KGDB_SIMPLE_SERIAL)
+ default "115200"
+ help
+ gdb and the kernel stub need to agree on the baud rate to be
+ used. Standard rates from 9600 to 115200 are allowed, and this
+ may be overridden via the commandline.
+
+config KGDB_PORT_NUM
+ int "Serial port number for KGDB"
+ range 0 1 if KGDB_MPSC
+ range 0 3
+ depends on (KGDB_8250 && KGDB_SIMPLE_SERIAL) || KGDB_MPSC
+ default "1"
+ help
+ Pick the port number (0 based) for KGDB to use.
+
+config KGDB_8250_CONF_STRING
+ string "Configuration string for KGDB"
+ depends on KGDB_8250 && !KGDB_SIMPLE_SERIAL
+ default "io,2f8,115200,3" if X86
+ help
+ The format of this string should be <io or
+ mmio>,<address>,<baud rate>,<irq>. For example, to use the
+ serial port on an i386 box located at 0x2f8 and 115200 baud
+ on IRQ 3 at use:
+ io,2f8,115200,3
Index: linux-2.6.14/drivers/serial/8250_kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/drivers/serial/8250_kgdb.c
@@ -0,0 +1,504 @@
+/*
+ * 8250 interface for kgdb.
+ *
+ * This is a merging of many different drivers, and all of the people have
+ * had an impact in some form or another:
+ *
+ * 2004-2005 (c) MontaVista Software, Inc.
+ * 2005 (c) Wind River Systems, Inc.
+ *
+ * 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]>,
+ * Jason Wessel <[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_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 */
+
+#include "8250.h"
+
+#define GDB_BUF_SIZE 512 /* power of 2, please */
+
+MODULE_DESCRIPTION("KGDB driver for the 8250");
+MODULE_LICENSE("GPL");
+/* These will conflict with early_param otherwise. */
+#ifdef CONFIG_KGDB_8250_MODULE
+static char config[256];
+module_param_string(kgdb8250, config, 256, 0);
+MODULE_PARM_DESC(kgdb8250,
+ " kgdb8250=<io or mmio>,<address>,<baud rate>,<irq>\n");
+static struct kgdb_io local_kgdb_io_ops;
+#endif /* CONFIG_KGDB_8250_MODULE */
+
+/* Speed of the UART. */
+static int kgdb8250_baud;
+
+/* Flag for if we need to call request_mem_region */
+static int kgdb8250_needs_request_mem_region;
+
+static char kgdb8250_buf[GDB_BUF_SIZE];
+static atomic_t kgdb8250_buf_in_cnt;
+static int kgdb8250_buf_out_inx;
+
+/* Old-style serial definitions, if existant, and a counter. */
+#ifdef CONFIG_KGDB_SIMPLE_SERIAL
+static int __initdata should_copy_rs_table = 1;
+static struct serial_state old_rs_table[] __initdata = {
+#ifdef SERIAL_PORT_DFNS
+ SERIAL_PORT_DFNS
+#endif
+};
+#endif
+
+/* Our internal table of UARTS. */
+#define UART_NR CONFIG_SERIAL_8250_NR_UARTS
+static struct uart_port kgdb8250_ports[UART_NR];
+
+static struct uart_port *current_port;
+
+/* Base of the UART. */
+static void *kgdb8250_addr;
+
+/* Forward declarations. */
+static int kgdb8250_uart_init(void);
+static int __init kgdb_init_io(void);
+static int __init kgdb8250_opt(char *str);
+
+/* These are much shorter calls to ioread8/iowrite8 that take into
+ * account our shifts, etc. */
+static inline unsigned int kgdb_ioread(u8 mask)
+{
+ return ioread8(kgdb8250_addr + (mask << current_port->regshift));
+}
+
+static inline void kgdb_iowrite(u8 val, u8 mask)
+{
+ iowrite8(val, kgdb8250_addr + (mask << current_port->regshift));
+}
+
+/*
+ * Wait until the interface can accept a char, then write it.
+ */
+static void kgdb_put_debug_char(u8 chr)
+{
+ while (!(kgdb_ioread(UART_LSR) & UART_LSR_THRE)) ;
+
+ kgdb_iowrite(chr, UART_TX);
+}
+
+/*
+ * Get a byte from the hardware data buffer and return it
+ */
+static int read_data_bfr(void)
+{
+ char it = kgdb_ioread(UART_LSR);
+
+ if (it & UART_LSR_DR)
+ return kgdb_ioread(UART_RX);
+
+ /*
+ * If we have a framing error assume somebody messed with
+ * our uart. Reprogram it and send '-' both ways...
+ */
+ if (it & 0xc) {
+ kgdb8250_uart_init();
+ kgdb_put_debug_char('-');
+ return '-';
+ }
+
+ return -1;
+}
+
+/*
+ * Get a char if available, return -1 if nothing available.
+ * Empty the receive buffer first, then look at the interface hardware.
+ */
+static int kgdb_get_debug_char(void)
+{
+ int retchr;
+
+ /* intr routine has q'd chars */
+ if (atomic_read(&kgdb8250_buf_in_cnt) != 0) {
+ retchr = kgdb8250_buf[kgdb8250_buf_out_inx++];
+ kgdb8250_buf_out_inx &= (GDB_BUF_SIZE - 1);
+ atomic_dec(&kgdb8250_buf_in_cnt);
+ return retchr;
+ }
+
+ do {
+ retchr = read_data_bfr();
+ } while (retchr < 0);
+
+ return retchr;
+}
+
+/*
+ * This is the receiver interrupt routine for the GDB stub.
+ * All that we need to do is verify that the interrupt happened on the
+ * line we're in charge of. If this is true, schedule a breakpoint and
+ * return.
+ */
+static irqreturn_t
+kgdb8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (kgdb_ioread(UART_IIR) & UART_IIR_RDI) {
+ /* Throw away the data if another I/O routine is active. */
+ if (kgdb_io_ops.read_char != kgdb_get_debug_char &&
+ (kgdb_ioread(UART_LSR) & UART_LSR_DR))
+ kgdb_ioread(UART_RX);
+ else
+ breakpoint();
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Initializes the UART.
+ * Returns:
+ * 0 on success, 1 on failure.
+ */
+static int
+kgdb8250_uart_init (void)
+{
+ unsigned int ier, base_baud = current_port->uartclk ?
+ current_port->uartclk / 16 : BASE_BAUD;
+
+ /* test uart existance */
+ if(kgdb_ioread(UART_LSR) == 0xff)
+ return -1;
+
+ /* disable interrupts */
+ kgdb_iowrite(0, UART_IER);
+
+#if defined(CONFIG_ARCH_OMAP1510)
+ /* Workaround to enable 115200 baud on OMAP1510 internal ports */
+ if (cpu_is_omap1510() && is_omap_port((void *)kgdb8250_addr)) {
+ if (kgdb8250_baud == 115200) {
+ base_baud = 1;
+ kgdb8250_baud = 1;
+ kgdb_iowrite(1, UART_OMAP_OSC_12M_SEL);
+ } else
+ kgdb_iowrite(0, UART_OMAP_OSC_12M_SEL);
+ }
+#endif
+ /* set DLAB */
+ kgdb_iowrite(UART_LCR_DLAB, UART_LCR);
+
+ /* set baud */
+ kgdb_iowrite((base_baud / kgdb8250_baud) & 0xff, UART_DLL);
+ kgdb_iowrite((base_baud / kgdb8250_baud) >> 8, UART_DLM);
+
+ /* reset DLAB, set LCR */
+ kgdb_iowrite(UART_LCR_WLEN8, UART_LCR);
+
+ /* set DTR and RTS */
+ kgdb_iowrite(UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS, UART_MCR);
+
+ /* setup fifo */
+ kgdb_iowrite(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR
+ | UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_8,
+ UART_FCR);
+
+ /* clear pending interrupts */
+ kgdb_ioread(UART_IIR);
+ kgdb_ioread(UART_RX);
+ kgdb_ioread(UART_LSR);
+ kgdb_ioread(UART_MSR);
+
+ /* turn on RX interrupt only */
+ kgdb_iowrite(UART_IER_RDI, UART_IER);
+
+ /*
+ * Borrowed from the main 8250 driver.
+ * Try writing and reading the UART_IER_UUE bit (b6).
+ * If it works, this is probably one of the Xscale platform's
+ * internal UARTs.
+ * We're going to explicitly set the UUE bit to 0 before
+ * trying to write and read a 1 just to make sure it's not
+ * already a 1 and maybe locked there before we even start start.
+ */
+ ier = kgdb_ioread(UART_IER);
+ kgdb_iowrite(ier & ~UART_IER_UUE, UART_IER);
+ if (!(kgdb_ioread(UART_IER) & UART_IER_UUE)) {
+ /*
+ * OK it's in a known zero state, try writing and reading
+ * without disturbing the current state of the other bits.
+ */
+ kgdb_iowrite(ier | UART_IER_UUE, UART_IER);
+ if (kgdb_ioread(UART_IER) & UART_IER_UUE)
+ /*
+ * It's an Xscale.
+ */
+ ier |= UART_IER_UUE | UART_IER_RTOIE;
+ }
+ kgdb_iowrite(ier, UART_IER);
+ return 0;
+}
+
+/*
+ * Copy the old serial_state table to our uart_port table if we haven't
+ * had values specifically configured in. We need to make sure this only
+ * happens once.
+ */
+static void __init kgdb8250_copy_rs_table(void)
+{
+#ifdef CONFIG_KGDB_SIMPLE_SERIAL
+ int i;
+
+ if (!should_copy_rs_table)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(old_rs_table); i++) {
+ kgdb8250_ports[i].iobase = old_rs_table[i].port;
+ kgdb8250_ports[i].irq = irq_canonicalize(old_rs_table[i].irq);
+ kgdb8250_ports[i].uartclk = old_rs_table[i].baud_base * 16;
+ kgdb8250_ports[i].membase = old_rs_table[i].iomem_base;
+ kgdb8250_ports[i].iotype = old_rs_table[i].io_type;
+ kgdb8250_ports[i].regshift = old_rs_table[i].iomem_reg_shift;
+ kgdb8250_ports[i].line = i;
+ }
+
+ should_copy_rs_table = 0;
+#endif
+}
+
+/*
+ * Hookup our IRQ line now that it is safe to do so, after we grab any
+ * memory regions we might need to. If we haven't been initialized yet,
+ * go ahead and copy the old_rs_table in.
+ */
+static void __init kgdb8250_late_init(void)
+{
+ /* Try and copy the old_rs_table. */
+ kgdb8250_copy_rs_table();
+
+#if defined(CONFIG_SERIAL_8250) || defined (CONFIG_SERIAL_8250_MODULE)
+ /* Take the port away from the main driver. */
+ serial8250_unregister_by_port(current_port);
+
+ /* Now reinit the port as the above has disabled things. */
+ kgdb8250_uart_init();
+#endif
+ /* We may need to call request_mem_region() first. */
+ if (kgdb8250_needs_request_mem_region)
+ request_mem_region(current_port->mapbase,
+ 8 << current_port->regshift, "kgdb");
+ if (request_irq(current_port->irq, kgdb8250_interrupt, SA_SHIRQ,
+ "GDB-stub", current_port) < 0)
+ printk(KERN_ERR "KGDB failed to request the serial IRQ (%d)\n",
+ current_port->irq);
+}
+
+static __init int kgdb_init_io(void)
+{
+ /* Give us the basic table of uarts. */
+ kgdb8250_copy_rs_table();
+
+#ifdef CONFIG_KGDB_8250_MODULE
+ if (strlen(config)) {
+ if (kgdb8250_opt(config))
+ return -EINVAL;
+ } else {
+ printk(KERN_ERR "kgdb8250: argument error, usage: "
+ "kgdb8250=<io or mmio>,<address>,<baud rate>" ",<irq>\n");
+ return -EINVAL;
+ }
+#endif /* CONFIG_KGDB_8250_MODULE */
+#ifdef CONFIG_KGDB_SIMPLE_SERIAL
+ kgdb8250_baud = CONFIG_KGDB_BAUDRATE;
+
+ /* Setup our pointer to the serial port now. */
+ current_port = &kgdb8250_ports[CONFIG_KGDB_PORT_NUM];
+#else
+ if (kgdb8250_opt(CONFIG_KGDB_8250_CONF_STRING))
+ return -EINVAL;
+#endif
+
+
+ /* Internal driver setup. */
+ switch (current_port->iotype) {
+ case UPIO_MEM:
+ if (current_port->mapbase)
+ kgdb8250_needs_request_mem_region = 1;
+ if (current_port->flags & UPF_IOREMAP) {
+ current_port->membase = ioremap(current_port->mapbase,
+ 8 << current_port->regshift);
+ if (!current_port->membase)
+ return -EIO; /* Failed. */
+ }
+ kgdb8250_addr = current_port->membase;
+ break;
+ case UPIO_PORT:
+ default:
+ kgdb8250_addr = ioport_map(current_port->iobase,
+ 8 << current_port->regshift);
+ if (!kgdb8250_addr)
+ return -EIO; /* Failed. */
+ }
+
+ if (kgdb8250_uart_init() == -1) {
+ printk(KERN_ERR "kgdb8250: init failed\n");
+ return -EIO;
+ }
+#ifdef CONFIG_KGDB_8250_MODULE
+ /* Attach the kgdb irq. When this is built into the kernel, it
+ * is called as a part of late_init sequence.
+ */
+ kgdb8250_late_init();
+ if (kgdb_register_io_module(&local_kgdb_io_ops))
+ return -EINVAL;
+
+ printk(KERN_INFO "kgdb8250: debugging enabled\n");
+#endif /* CONFIG_KGD_8250_MODULE */
+
+ return 0;
+}
+
+#ifdef CONFIG_KGDB_8250_MODULE
+/* If it is a module the kgdb_io_ops should be a static which
+ * is passed to the KGDB I/O initialization
+ */
+static struct kgdb_io local_kgdb_io_ops = {
+#else /* ! CONFIG_KGDB_8250_MODULE */
+struct kgdb_io kgdb_io_ops = {
+#endif /* ! CONFIG_KGD_8250_MODULE */
+ .read_char = kgdb_get_debug_char,
+ .write_char = kgdb_put_debug_char,
+ .init = kgdb_init_io,
+ .late_init = kgdb8250_late_init,
+};
+
+/**
+ * kgdb8250_add_port - Define a serial port for use with KGDB
+ * @i: The index of the port being added
+ * @serial_req: The &struct uart_port describing the port
+ *
+ * On platforms where we must register the serial device
+ * dynamically, this is the best option if a platform also normally
+ * calls early_serial_setup().
+ */
+void __init kgdb8250_add_port(int i, struct uart_port *serial_req)
+{
+ /* Make sure we've got the built-in data before we override. */
+ kgdb8250_copy_rs_table();
+
+ /* Copy the whole thing over. */
+ memcpy(&kgdb8250_ports[i], serial_req, sizeof(struct uart_port));
+}
+
+/**
+ * kgdb8250_add_platform_port - Define a serial port for use with KGDB
+ * @i: The index of the port being added
+ * @p: The &struct plat_serial8250_port describing the port
+ *
+ * On platforms where we must register the serial device
+ * dynamically, this is the best option if a platform normally
+ * handles uart setup with an array of &struct plat_serial8250_port.
+ */
+void __init kgdb8250_add_platform_port(int i, struct plat_serial8250_port *p)
+{
+ /* Make sure we've got the built-in data before we override. */
+ kgdb8250_copy_rs_table();
+
+ kgdb8250_ports[i].iobase = p->iobase;
+ kgdb8250_ports[i].membase = p->membase;
+ kgdb8250_ports[i].irq = p->irq;
+ kgdb8250_ports[i].uartclk = p->uartclk;
+ kgdb8250_ports[i].regshift = p->regshift;
+ kgdb8250_ports[i].iotype = p->iotype;
+ kgdb8250_ports[i].flags = p->flags;
+ kgdb8250_ports[i].mapbase = p->mapbase;
+}
+
+/*
+ * Syntax for this cmdline option is:
+ * kgdb8250=<io or mmio>,<address>,<baud rate>,<irq>"
+ */
+static int __init kgdb8250_opt(char *str)
+{
+ /* We'll fill out and use the first slot. */
+ current_port = &kgdb8250_ports[0];
+
+ if (!strncmp(str, "io", 2)) {
+ current_port->iotype = UPIO_PORT;
+ str += 2;
+ } else if (!strncmp(str, "mmio", 4)) {
+ current_port->iotype = UPIO_MEM;
+ str += 4;
+ } else
+ goto errout;
+
+ if (*str != ',')
+ goto errout;
+ str++;
+
+ if (current_port->iotype == UPIO_PORT)
+ current_port->iobase = simple_strtoul(str, &str, 16);
+ else
+ current_port->membase =
+ (unsigned char *)simple_strtoul(str, &str, 16);
+
+ if (*str != ',')
+ goto errout;
+ str++;
+
+ kgdb8250_baud = simple_strtoul(str, &str, 10);
+ if (!kgdb8250_baud)
+ goto errout;
+
+ if (*str != ',')
+ goto errout;
+ str++;
+
+ current_port->irq = simple_strtoul(str, &str, 10);
+
+#ifdef CONFIG_KGDB_SIMPLE_SERIAL
+ should_copy_rs_table = 0;
+#endif
+
+ return 0;
+
+ errout:
+ printk(KERN_ERR "Invalid syntax for option kgdb8250=\n");
+ return 1;
+}
+
+#ifdef CONFIG_KGDB_8250_MODULE
+static void cleanup_kgdb8250(void)
+{
+ kgdb_unregister_io_module(&local_kgdb_io_ops);
+
+ /* Clean up the irq and memory */
+ free_irq(current_port->irq, current_port);
+
+ if (kgdb8250_needs_request_mem_region)
+ release_mem_region(current_port->mapbase,
+ 8 << current_port->regshift);
+ /* Hook up the serial port back to what it was previously
+ * hooked up to.
+ */
+#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE)
+ /* Give the port back to the 8250 driver. */
+ serial8250_register_port(current_port);
+#endif
+}
+
+module_init(kgdb_init_io);
+module_exit(cleanup_kgdb8250);
+#else /* ! CONFIG_KGDB_8250_MODULE */
+early_param("kgdb8250", kgdb8250_opt);
+#endif /* ! CONFIG_KGDB_8250_MODULE */
Index: linux-2.6.14/include/linux/serial_8250.h
===================================================================
--- linux-2.6.14.orig/include/linux/serial_8250.h
+++ linux-2.6.14/include/linux/serial_8250.h
@@ -54,6 +54,7 @@ struct uart_port;

int serial8250_register_port(struct uart_port *);
void serial8250_unregister_port(int line);
+void serial8250_unregister_by_port(struct uart_port *port);
void serial8250_suspend_port(int line);
void serial8250_resume_port(int line);

Index: linux-2.6.14/drivers/serial/serial_core.c
===================================================================
--- linux-2.6.14.orig/drivers/serial/serial_core.c
+++ linux-2.6.14/drivers/serial/serial_core.c
@@ -33,6 +33,7 @@
#include <linux/device.h>
#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
#include <linux/delay.h>
+#include <linux/kgdb.h>

#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -2237,6 +2238,12 @@ int uart_add_one_port(struct uart_driver
port->cons && !(port->cons->flags & CON_ENABLED))
register_console(port->cons);

+#if defined(CONFIG_KGDB_8250)
+ /* Add any 8250-like ports we find later. */
+ if (port->type <= PORT_MAX_8250)
+ kgdb8250_add_port(port->line, port);
+#endif
+
out:
up(&port_sem);


--
Tom

2005-11-10 16:43:28

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 09/15] KGDB: SuperH-specific changes

This adds basic support for KGDB on SuperH as well as adding some architecture
specific notes to the DocBook file and converting the 7751 to use this. I
have tested all combinations of 8250 and SCI(F) ports being used as KGDB and
console that I could (one of each usable to me).

Documentation/DocBook/kgdb.tmpl | 16
arch/sh/Kconfig.debug | 92 --
arch/sh/Makefile | 1
arch/sh/boards/se/7751/setup.c | 139 ---
arch/sh/kernel/Makefile | 2
arch/sh/kernel/cpu/sh3/ex.S | 2
arch/sh/kernel/cpu/sh4/ex.S | 2
arch/sh/kernel/entry.S | 30
arch/sh/kernel/kgdb-jmp.S | 32
arch/sh/kernel/kgdb.c | 363 +++++++++
arch/sh/kernel/kgdb_jmp.S | 33
arch/sh/kernel/kgdb_stub.c | 1491 ----------------------------------------
arch/sh/kernel/setup.c | 94 --
arch/sh/kernel/time.c | 11
arch/sh/kernel/traps.c | 20
arch/sh/mm/extable.c | 7
arch/sh/mm/fault-nommu.c | 14
arch/sh/mm/fault.c | 12
drivers/serial/sh-sci.c | 298 ++++---
include/asm-sh/kgdb.h | 116 ---
include/asm-sh/system.h | 40 +
lib/Kconfig.debug | 6
22 files changed, 676 insertions(+), 2145 deletions(-)

Index: linux-2.6.14/arch/sh/boards/se/7751/setup.c
===================================================================
--- linux-2.6.14.orig/arch/sh/boards/se/7751/setup.c
+++ linux-2.6.14/arch/sh/boards/se/7751/setup.c
@@ -18,10 +18,6 @@
#include <asm/io.h>
#include <asm/se7751/se7751.h>

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#endif
-
/*
* Configure the Super I/O chip
*/
@@ -83,12 +79,6 @@ const char *get_system_type(void)
return "7751 SolutionEngine";
}

-#ifdef CONFIG_SH_KGDB
-static int kgdb_uart_setup(void);
-static struct kgdb_sermap kgdb_uart_sermap =
-{ "ttyS", 0, kgdb_uart_setup, NULL };
-#endif
-
/*
* Initialize the board
*/
@@ -96,133 +86,4 @@ void __init platform_setup(void)
{
/* Call init_smsc() replacement to set up SuperIO. */
/* XXX: RTC setting comes here */
-#ifdef CONFIG_SH_KGDB
- kgdb_register_sermap(&kgdb_uart_sermap);
-#endif
-}
-
-/*********************************************************************
- * Currently a hack (e.g. does not interact well w/serial.c, lots of *
- * hardcoded stuff) but may be useful if SCI/F needs debugging. *
- * Mostly copied from x86 code (see files asm-i386/kgdb_local.h and *
- * arch/i386/lib/kgdb_serial.c). *
- *********************************************************************/
-
-#ifdef CONFIG_SH_KGDB
-#include <linux/types.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/serial_reg.h>
-
-#define COM1_PORT 0x3f8 /* Base I/O address */
-#define COM1_IRQ 4 /* IRQ not used yet */
-#define COM2_PORT 0x2f8 /* Base I/O address */
-#define COM2_IRQ 3 /* IRQ not used yet */
-
-#define SB_CLOCK 1843200 /* Serial baud clock */
-#define SB_BASE (SB_CLOCK/16)
-#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS
-
-struct uart_port {
- int base;
-};
-#define UART_NPORTS 2
-struct uart_port uart_ports[] = {
- { COM1_PORT },
- { COM2_PORT },
-};
-struct uart_port *kgdb_uart_port;
-
-#define UART_IN(reg) inb_p(kgdb_uart_port->base + reg)
-#define UART_OUT(reg,v) outb_p((v), kgdb_uart_port->base + reg)
-
-/* Basic read/write functions for the UART */
-#define UART_LSR_RXCERR (UART_LSR_BI | UART_LSR_FE | UART_LSR_PE)
-static int kgdb_uart_getchar(void)
-{
- int lsr;
- int c = -1;
-
- while (c == -1) {
- lsr = UART_IN(UART_LSR);
- if (lsr & UART_LSR_DR)
- c = UART_IN(UART_RX);
- if ((lsr & UART_LSR_RXCERR))
- c = -1;
- }
- return c;
-}
-
-static void kgdb_uart_putchar(int c)
-{
- while ((UART_IN(UART_LSR) & UART_LSR_THRE) == 0)
- ;
- UART_OUT(UART_TX, c);
-}
-
-/*
- * Initialize UART to configured/requested values.
- * (But we don't interrupts yet, or interact w/serial.c)
- */
-static int kgdb_uart_setup(void)
-{
- int port;
- int lcr = 0;
- int bdiv = 0;
-
- if (kgdb_portnum >= UART_NPORTS) {
- KGDB_PRINTK("uart port %d invalid.\n", kgdb_portnum);
- return -1;
- }
-
- kgdb_uart_port = &uart_ports[kgdb_portnum];
-
- /* Init sequence from gdb_hook_interrupt */
- UART_IN(UART_RX);
- UART_OUT(UART_IER, 0);
-
- UART_IN(UART_RX); /* Serial driver comments say */
- UART_IN(UART_IIR); /* this clears interrupt regs */
- UART_IN(UART_MSR);
-
- /* Figure basic LCR values */
- switch (kgdb_bits) {
- case '7':
- lcr |= UART_LCR_WLEN7;
- break;
- default: case '8':
- lcr |= UART_LCR_WLEN8;
- break;
- }
- switch (kgdb_parity) {
- case 'O':
- lcr |= UART_LCR_PARITY;
- break;
- case 'E':
- lcr |= (UART_LCR_PARITY | UART_LCR_EPAR);
- break;
- default: break;
- }
-
- /* Figure the baud rate divisor */
- bdiv = (SB_BASE/kgdb_baud);
-
- /* Set the baud rate and LCR values */
- UART_OUT(UART_LCR, (lcr | UART_LCR_DLAB));
- UART_OUT(UART_DLL, (bdiv & 0xff));
- UART_OUT(UART_DLM, ((bdiv >> 8) & 0xff));
- UART_OUT(UART_LCR, lcr);
-
- /* Set the MCR */
- UART_OUT(UART_MCR, SB_MCR);
-
- /* Turn off FIFOs for now */
- UART_OUT(UART_FCR, 0);
-
- /* Setup complete: initialize function pointers */
- kgdb_getchar = kgdb_uart_getchar;
- kgdb_putchar = kgdb_uart_putchar;
-
- return 0;
}
-#endif /* CONFIG_SH_KGDB */
Index: linux-2.6.14/arch/sh/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/arch/sh/Kconfig.debug
+++ linux-2.6.14/arch/sh/Kconfig.debug
@@ -29,96 +29,4 @@ config EARLY_PRINTK
This option is only useful porting the kernel to a new machine,
when the kernel may crash or hang before the serial console is
initialised. If unsure, say N.
-
-config KGDB
- bool "Include KGDB kernel debugger"
- 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.
-
-menu "KGDB configuration options"
- depends on KGDB
-
-config MORE_COMPILE_OPTIONS
- bool "Add any additional compile options"
- help
- If you want to add additional CFLAGS to the kernel build, enable this
- option and then enter what you would like to add in the next question.
- Note however that -g is already appended with the selection of KGDB.
-
-config COMPILE_OPTIONS
- string "Additional compile arguments"
- depends on MORE_COMPILE_OPTIONS
-
-config KGDB_NMI
- bool "Enter KGDB on NMI"
- default n
-
-config KGDB_THREAD
- bool "Include KGDB thread support"
- default y
-
-config SH_KGDB_CONSOLE
- bool "Console messages through GDB"
- default n
-
-config KGDB_SYSRQ
- bool "Allow SysRq 'G' to enter KGDB"
- default y
-
-config KGDB_KERNEL_ASSERTS
- bool "Include KGDB kernel assertions"
- default n
-
-comment "Serial port setup"
-
-config KGDB_DEFPORT
- int "Port number (ttySCn)"
- default "1"
-
-config KGDB_DEFBAUD
- int "Baud rate"
- default "115200"
-
-choice
- prompt "Parity"
- depends on KGDB
- default KGDB_DEFPARITY_N
-
-config KGDB_DEFPARITY_N
- bool "None"
-
-config KGDB_DEFPARITY_E
- bool "Even"
-
-config KGDB_DEFPARITY_O
- bool "Odd"
-
-endchoice
-
-choice
- prompt "Data bits"
- depends on KGDB
- default KGDB_DEFBITS_8
-
-config KGDB_DEFBITS_8
- bool "8"
-
-config KGDB_DEFBITS_7
- bool "7"
-
-endchoice
-
-endmenu
-
-config FRAME_POINTER
- bool "Compile the kernel with frame pointers"
- 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.
-
endmenu
Index: linux-2.6.14/arch/sh/kernel/cpu/sh3/ex.S
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/cpu/sh3/ex.S
+++ linux-2.6.14/arch/sh/kernel/cpu/sh3/ex.S
@@ -43,7 +43,7 @@ ENTRY(exception_handling_table)
.long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
.long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
ENTRY(nmi_slot)
-#if defined (CONFIG_KGDB_NMI)
+#if defined (CONFIG_KGDB)
.long debug_enter /* 1C0 */ ! Allow trap to debugger
#else
.long exception_none /* 1C0 */ ! Not implemented yet
Index: linux-2.6.14/arch/sh/kernel/cpu/sh4/ex.S
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/cpu/sh4/ex.S
+++ linux-2.6.14/arch/sh/kernel/cpu/sh4/ex.S
@@ -47,7 +47,7 @@ ENTRY(exception_handling_table)
.long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
.long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
ENTRY(nmi_slot)
-#if defined (CONFIG_KGDB_NMI)
+#if defined (CONFIG_KGDB)
.long debug_enter /* 1C0 */ ! Allow trap to debugger
#else
.long exception_none /* 1C0 */ ! Not implemented yet
Index: linux-2.6.14/arch/sh/kernel/entry.S
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/entry.S
+++ linux-2.6.14/arch/sh/kernel/entry.S
@@ -92,7 +92,7 @@ INTEVT = 0xff000028
MMU_TEA = 0xff00000c ! TLB Exception Address Register
#endif

-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
NMI_VEC = 0x1c0 ! Must catch early for debounce
#endif

@@ -244,31 +244,33 @@ call_dae:
2: .long do_address_error
#endif /* CONFIG_MMU */

-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_KGDB)
! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present.
! If both are configured, handle the debug traps (breakpoints) in SW,
! but still allow BIOS traps to FW.

.align 2
debug_kernel:
-#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB)
+#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_KGDB)
/* Force BIOS call to FW (debug_trap put TRA in r8) */
mov r8,r0
shlr2 r0
cmp/eq #0x3f,r0
bt debug_kernel_fw
-#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_KGDB */

-debug_enter:
-#if defined(CONFIG_SH_KGDB)
+ .align 2
+ .globl debug_enter
+debug_enter:
+#if defined(CONFIG_KGDB)
/* Jump to kgdb, pass stacked regs as arg */
debug_kernel_sw:
mov.l 3f, r0
jmp @r0
mov r15, r4
.align 2
-3: .long kgdb_handle_exception
-#endif /* CONFIG_SH_KGDB */
+3: .long kgdb_exception_handler
+#endif /* CONFIG_KGDB */

#if defined(CONFIG_SH_STANDARD_BIOS)
/* Unwind the stack and jmp to the debug entry */
@@ -310,12 +312,12 @@ debug_kernel_fw:
2: .long gdb_vbr_vector
#endif /* CONFIG_SH_STANDARD_BIOS */

-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_KGDB */


.align 2
-debug_trap:
-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+debug_trap:
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_KGDB)
mov #OFF_SR, r0
mov.l @(r0,r15), r0 ! get status register
shll r0
@@ -659,7 +661,7 @@ skip_restore:
6: or k0, k2 ! Set the IMASK-bits
ldc k2, ssr
!
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
! Clear in_nmi
mov.l 4f, k0
mov #0, k1
@@ -711,7 +713,7 @@ tlb_miss:
interrupt:
mov.l 2f, k2
mov.l 3f, k3
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
! Debounce (filter nested NMI)
mov.l @k2, k0
mov.l 5f, k1
@@ -726,7 +728,7 @@ interrupt:
5: .long NMI_VEC
6: .long in_nmi
0:
-#endif /* defined(CONFIG_KGDB_NMI) */
+#endif /* defined(CONFIG_KGDB) */
bra handle_exception
mov.l @k2, k2

Index: linux-2.6.14/arch/sh/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/sh/kernel/kgdb.c
@@ -0,0 +1,363 @@
+/*
+ * arch/sh/kernel/kgdb.c
+ *
+ * Contains SH-specific low-level support for KGDB.
+ *
+ * Containes extracts from code by Glenn Engel, Jim Kingdon,
+ * David Grothe <[email protected]>, Tigran Aivazian <[email protected]>,
+ * Amit S. Kale <[email protected]>, William Gatliff <[email protected]>,
+ * Ben Lee, Steve Chamberlain and Benoit Miller <[email protected]>,
+ * Henry Bell <[email protected]> and Jeremy Siegel <[email protected]>
+ *
+ * Maintainer: Tom Rini <[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.
+ */
+
+#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/linkage.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+
+#include <asm/system.h>
+#include <asm/current.h>
+#include <asm/signal.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+
+extern void per_cpu_trap_init(void);
+extern atomic_t cpu_doing_single_step;
+
+/* Function pointers for linkage */
+static struct kgdb_regs trap_registers;
+
+/* Globals. */
+char in_nmi; /* Set during NMI to prevent reentry */
+
+/* TRA differs sh3/4 */
+#if defined(CONFIG_CPU_SH3)
+#define TRA 0xffffffd0
+#elif defined(CONFIG_CPU_SH4)
+#define TRA 0xff000020
+#endif
+
+/* Macros for single step instruction identification */
+#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900)
+#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00)
+#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
+ (((op) & 0x7f ) << 1))
+#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00)
+#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00)
+#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000)
+#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+ (((op) & 0x7ff) << 1))
+#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023)
+#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8)
+#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000)
+#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+ (((op) & 0x7ff) << 1))
+#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003)
+#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b)
+#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b)
+#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_RTS(op) ((op) == 0xb)
+#define OPCODE_RTE(op) ((op) == 0x2b)
+
+#define SR_T_BIT_MASK 0x1
+#define STEP_OPCODE 0xc320
+#define BIOS_CALL_TRAP 0x3f
+
+/* Exception codes as per SH-4 core manual */
+#define ADDRESS_ERROR_LOAD_VEC 7
+#define ADDRESS_ERROR_STORE_VEC 8
+#define TRAP_VEC 11
+#define INVALID_INSN_VEC 12
+#define INVALID_SLOT_VEC 13
+#define NMI_VEC 14
+#define SERIAL_BREAK_VEC 58
+
+/* Misc static */
+static int stepped_address;
+static short stepped_opcode;
+
+/* Translate SH-3/4 exception numbers to unix-like signal values */
+static int compute_signal(const int excep_code)
+{
+ switch (excep_code) {
+ case INVALID_INSN_VEC:
+ case INVALID_SLOT_VEC:
+ return SIGILL;
+ case ADDRESS_ERROR_LOAD_VEC:
+ case ADDRESS_ERROR_STORE_VEC:
+ return SIGSEGV;
+ case SERIAL_BREAK_VEC:
+ case NMI_VEC:
+ return SIGINT;
+ default:
+ /* Act like it was a break/trap. */
+ return SIGTRAP;
+ }
+}
+
+/*
+ * Translate the registers of the system into the format that GDB wants. Since
+ * we use a local structure to store things, instead of getting them out
+ * of pt_regs, we can just do a memcpy.
+ */
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *ign)
+{
+ memcpy(gdb_regs, &trap_registers, sizeof(trap_registers));
+}
+
+/*
+ * On SH we save: r1 (prev->thread.sp) r2 (prev->thread.pc) r4 (prev) r5 (next)
+ * r6 (next->thread.sp) r7 (next->thread.pc)
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ int count;
+
+ for (count = 0; count < 16; count++)
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = p->thread.pc;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+ *(gdb_regs++) = 0;
+}
+
+/*
+ * Translate the registers values that GDB has given us back into the
+ * format of the system. See the comment above about memcpy.
+ */
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *ign)
+{
+ memcpy(&trap_registers, gdb_regs, sizeof(trap_registers));
+}
+
+/* Calculate the new address for after a step */
+static short *get_step_address(void)
+{
+ short op = *(short *)trap_registers.pc;
+ long addr;
+
+ /* BT */
+ if (OPCODE_BT(op)) {
+ if (trap_registers.sr & SR_T_BIT_MASK)
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 2;
+ }
+
+ /* BTS */
+ else if (OPCODE_BTS(op)) {
+ if (trap_registers.sr & SR_T_BIT_MASK)
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 4; /* Not in delay slot */
+ }
+
+ /* BF */
+ else if (OPCODE_BF(op)) {
+ if (!(trap_registers.sr & SR_T_BIT_MASK))
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 2;
+ }
+
+ /* BFS */
+ else if (OPCODE_BFS(op)) {
+ if (!(trap_registers.sr & SR_T_BIT_MASK))
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 4; /* Not in delay slot */
+ }
+
+ /* BRA */
+ else if (OPCODE_BRA(op))
+ addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
+
+ /* BRAF */
+ else if (OPCODE_BRAF(op))
+ addr = trap_registers.pc + 4
+ + trap_registers.regs[OPCODE_BRAF_REG(op)];
+
+ /* BSR */
+ else if (OPCODE_BSR(op))
+ addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
+
+ /* BSRF */
+ else if (OPCODE_BSRF(op))
+ addr = trap_registers.pc + 4
+ + trap_registers.regs[OPCODE_BSRF_REG(op)];
+
+ /* JMP */
+ else if (OPCODE_JMP(op))
+ addr = trap_registers.regs[OPCODE_JMP_REG(op)];
+
+ /* JSR */
+ else if (OPCODE_JSR(op))
+ addr = trap_registers.regs[OPCODE_JSR_REG(op)];
+
+ /* RTS */
+ else if (OPCODE_RTS(op))
+ addr = trap_registers.pr;
+
+ /* RTE */
+ else if (OPCODE_RTE(op))
+ addr = trap_registers.regs[15];
+
+ /* Other */
+ else
+ addr = trap_registers.pc + 2;
+
+ kgdb_flush_icache_range(addr, addr + 2);
+ return (short *)addr;
+}
+
+/* The command loop, read and act on requests */
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ struct pt_regs *ign)
+{
+ unsigned long addr;
+ char *ptr = &remcom_in_buffer[1];
+
+ /* Examine first char of buffer to see what we need to do */
+ switch (remcom_in_buffer[0]) {
+ case 'c': /* Continue at address AA..AA (optional) */
+ case 's': /* Step one instruction from AA..AA */
+ /* Try to read optional parameter, PC unchanged if none */
+ if (kgdb_hex2long(&ptr, &addr))
+ trap_registers.pc = addr;
+
+ atomic_set(&cpu_doing_single_step, -1);
+ if (remcom_in_buffer[0] == 's') {
+ /* Replace the instruction immediately after the
+ * current instruction (i.e. next in the expected
+ * flow of control) with a trap instruction, so that
+ * returning will cause only a single instruction to
+ * be executed. Note that this model is slightly
+ * broken for instructions with delay slots
+ * (e.g. B[TF]S, BSR, BRA etc), where both the branch
+ * and the instruction in the delay slot will be
+ * executed.
+ */
+ /* Determine where the target instruction will send
+ * us to */
+ unsigned short *next_addr = get_step_address();
+ stepped_address = (int)next_addr;
+
+ /* Replace it */
+ stepped_opcode = *(short *)next_addr;
+ *next_addr = STEP_OPCODE;
+
+ /* Flush and return */
+ kgdb_flush_icache_range((long)next_addr,
+ (long)next_addr + 2);
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,
+ smp_processor_id());
+ }
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * When an exception has occured, we are called. We need to set things
+ * up so that we can call kgdb_handle_exception to handle requests from
+ * the remote GDB.
+ */
+void kgdb_exception_handler(struct pt_regs *regs)
+{
+ int excep_code, vbr_val;
+ int count;
+
+ /* Copy kernel regs (from stack) */
+ for (count = 0; count < 16; count++)
+ trap_registers.regs[count] = regs->regs[count];
+ trap_registers.pc = regs->pc;
+ trap_registers.pr = regs->pr;
+ trap_registers.sr = regs->sr;
+ trap_registers.gbr = regs->gbr;
+ trap_registers.mach = regs->mach;
+ trap_registers.macl = regs->macl;
+
+ __asm__ __volatile__("stc vbr, %0":"=r"(vbr_val));
+ trap_registers.vbr = vbr_val;
+
+ /* Get the execption code. */
+ __asm__ __volatile__("stc r2_bank, %0":"=r"(excep_code));
+
+ excep_code >>= 5;
+
+ /* If we got an NMI, and KGDB is not yet initialized, call
+ * breakpoint() to try and initialize everything for us. */
+ if (excep_code == NMI_VEC && !kgdb_initialized) {
+ breakpoint();
+ return;
+ }
+
+ /* TRAP_VEC exception indicates a software trap inserted in place of
+ * code by GDB so back up PC by one instruction, as this instruction
+ * will later be replaced by its original one. Do NOT do this for
+ * trap 0xff, since that indicates a compiled-in breakpoint which
+ * will not be replaced (and we would retake the trap forever) */
+ if (excep_code == TRAP_VEC &&
+ (*(volatile unsigned long *)TRA != (0xff << 2)))
+ trap_registers.pc -= 2;
+
+ /* If we have been single-stepping, put back the old instruction.
+ * We use stepped_address in case we have stopped more than one
+ * instruction away. */
+ if (stepped_opcode != 0) {
+ *(short *)stepped_address = stepped_opcode;
+ kgdb_flush_icache_range(stepped_address, stepped_address + 2);
+ }
+ stepped_opcode = 0;
+
+ /* Call the stub to do the processing. Note that not everything we
+ * need to send back and forth lives in pt_regs. */
+ kgdb_handle_exception(excep_code, compute_signal(excep_code), 0, regs);
+
+ /* Copy back the (maybe modified) registers */
+ for (count = 0; count < 16; count++)
+ regs->regs[count] = trap_registers.regs[count];
+ regs->pc = trap_registers.pc;
+ regs->pr = trap_registers.pr;
+ regs->sr = trap_registers.sr;
+ regs->gbr = trap_registers.gbr;
+ regs->mach = trap_registers.mach;
+ regs->macl = trap_registers.macl;
+
+ vbr_val = trap_registers.vbr;
+ __asm__ __volatile__("ldc %0, vbr": :"r"(vbr_val));
+}
+
+int __init kgdb_arch_init(void)
+{
+ per_cpu_trap_init();
+
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ .gdb_bpt_instr = {0xff, 0xc3},
+#else
+ .gdb_bpt_instr = {0xc3, 0xff},
+#endif
+};
Index: linux-2.6.14/arch/sh/kernel/kgdb_jmp.S
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/kgdb_jmp.S
+++ /dev/null
@@ -1,33 +0,0 @@
-#include <linux/linkage.h>
-
-ENTRY(setjmp)
- add #(9*4), r4
- sts.l pr, @-r4
- mov.l r15, @-r4
- mov.l r14, @-r4
- mov.l r13, @-r4
- mov.l r12, @-r4
- mov.l r11, @-r4
- mov.l r10, @-r4
- mov.l r9, @-r4
- mov.l r8, @-r4
- rts
- mov #0, r0
-
-ENTRY(longjmp)
- mov.l @r4+, r8
- mov.l @r4+, r9
- mov.l @r4+, r10
- mov.l @r4+, r11
- mov.l @r4+, r12
- mov.l @r4+, r13
- mov.l @r4+, r14
- mov.l @r4+, r15
- lds.l @r4+, pr
- mov r5, r0
- tst r0, r0
- bf 1f
- mov #1, r0 ! in case val==0
-1: rts
- nop
-
Index: linux-2.6.14/arch/sh/kernel/kgdb-jmp.S
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/sh/kernel/kgdb-jmp.S
@@ -0,0 +1,32 @@
+#include <linux/linkage.h>
+
+ENTRY(kgdb_fault_setjmp)
+ add #(9*4), r4
+ sts.l pr, @-r4
+ mov.l r15, @-r4
+ mov.l r14, @-r4
+ mov.l r13, @-r4
+ mov.l r12, @-r4
+ mov.l r11, @-r4
+ mov.l r10, @-r4
+ mov.l r9, @-r4
+ mov.l r8, @-r4
+ rts
+ mov #0, r0
+
+ENTRY(kgdb_fault_longjmp)
+ mov.l @r4+, r8
+ mov.l @r4+, r9
+ mov.l @r4+, r10
+ mov.l @r4+, r11
+ mov.l @r4+, r12
+ mov.l @r4+, r13
+ mov.l @r4+, r14
+ mov.l @r4+, r15
+ lds.l @r4+, pr
+ mov r5, r0
+ tst r0, r0
+ bf 1f
+ mov #1, r0
+1: rts
+ nop
Index: linux-2.6.14/arch/sh/kernel/kgdb_stub.c
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/kgdb_stub.c
+++ /dev/null
@@ -1,1491 +0,0 @@
-/*
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Containes extracts from code by Glenn Engel, Jim Kingdon,
- * David Grothe <[email protected]>, Tigran Aivazian <[email protected]>,
- * Amit S. Kale <[email protected]>, William Gatliff <[email protected]>,
- * Ben Lee, Steve Chamberlain and Benoit Miller <[email protected]>.
- *
- * This version by Henry Bell <[email protected]>
- * Minor modifications by Jeremy Siegel <[email protected]>
- *
- * Contains low-level support for remote debug using GDB.
- *
- * To enable debugger support, two things need to happen. A call to
- * set_debug_traps() is necessary in order to allow any breakpoints
- * or error conditions to be properly intercepted and reported to gdb.
- * A breakpoint also needs to be generated to begin communication. This
- * is most easily accomplished by a call to breakpoint() which does
- * a trapa if the initialisation phase has been successfully completed.
- *
- * In this case, set_debug_traps() is not used to "take over" exceptions;
- * other kernel code is modified instead to enter the kgdb functions here
- * when appropriate (see entry.S for breakpoint traps and NMI interrupts,
- * see traps.c for kernel error exceptions).
- *
- * The following gdb commands are supported:
- *
- * Command Function Return value
- *
- * g return the value of the CPU registers hex data or ENN
- * G set the value of the CPU registers OK or ENN
- *
- * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
- * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
- * XAA..AA,LLLL: Same, but data is binary (not hex) OK or ENN
- *
- * c Resume at current address SNN ( signal NN)
- * cAA..AA Continue at address AA..AA SNN
- * CNN; Resume at current address with signal SNN
- * CNN;AA..AA Resume at address AA..AA with signal SNN
- *
- * s Step one instruction SNN
- * sAA..AA Step one instruction from AA..AA SNN
- * SNN; Step one instruction with signal SNN
- * SNNAA..AA Step one instruction from AA..AA w/NN SNN
- *
- * k kill (Detach GDB)
- *
- * d Toggle debug flag
- * D Detach GDB
- *
- * Hct Set thread t for operations, OK or ENN
- * c = 'c' (step, cont), c = 'g' (other
- * operations)
- *
- * qC Query current thread ID QCpid
- * qfThreadInfo Get list of current threads (first) m<id>
- * qsThreadInfo " " " " " (subsequent)
- * qOffsets Get section offsets Text=x;Data=y;Bss=z
- *
- * TXX Find if thread XX is alive OK or ENN
- * ? What was the last sigval ? SNN (signal NN)
- * O Output to GDB console
- *
- * Remote communication protocol.
- *
- * A debug packet whose contents are <data> is encapsulated for
- * transmission in the form:
- *
- * $ <data> # CSUM1 CSUM2
- *
- * <data> must be ASCII alphanumeric and cannot include characters
- * '$' or '#'. If <data> starts with two characters followed by
- * ':', then the existing stubs interpret this as a sequence number.
- *
- * CSUM1 and CSUM2 are ascii hex representation of an 8-bit
- * checksum of <data>, the most significant nibble is sent first.
- * the hex digits 0-9,a-f are used.
- *
- * Receiver responds with:
- *
- * + - if CSUM is correct and ready for next packet
- * - - if CSUM is incorrect
- *
- * Responses can be run-length encoded to save space. A '*' means that
- * the next character is an ASCII encoding giving a repeat count which
- * stands for that many repititions of the character preceding the '*'.
- * The encoding is n+29, yielding a printable character where n >=3
- * (which is where RLE starts to win). Don't use an n > 126.
- *
- * So "0* " means the same as "0000".
- */
-
-#include <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/linkage.h>
-#include <linux/init.h>
-
-#include <asm/system.h>
-#include <asm/current.h>
-#include <asm/signal.h>
-#include <asm/pgtable.h>
-#include <asm/ptrace.h>
-#include <asm/kgdb.h>
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-#include <linux/console.h>
-#endif
-
-/* Function pointers for linkage */
-kgdb_debug_hook_t *kgdb_debug_hook;
-kgdb_bus_error_hook_t *kgdb_bus_err_hook;
-
-int (*kgdb_getchar)(void);
-void (*kgdb_putchar)(int);
-
-static void put_debug_char(int c)
-{
- if (!kgdb_putchar)
- return;
- (*kgdb_putchar)(c);
-}
-static int get_debug_char(void)
-{
- if (!kgdb_getchar)
- return -1;
- return (*kgdb_getchar)();
-}
-
-/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */
-#define BUFMAX 1024
-#define NUMREGBYTES (MAXREG*4)
-#define OUTBUFMAX (NUMREGBYTES*2+512)
-
-enum regs {
- R0 = 0, R1, R2, R3, R4, R5, R6, R7,
- R8, R9, R10, R11, R12, R13, R14, R15,
- PC, PR, GBR, VBR, MACH, MACL, SR,
- /* */
- MAXREG
-};
-
-static unsigned int registers[MAXREG];
-struct kgdb_regs trap_registers;
-
-char kgdb_in_gdb_mode;
-char in_nmi; /* Set during NMI to prevent reentry */
-int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */
-int kgdb_enabled = 1; /* Default to enabled, cmdline can disable */
-int kgdb_halt;
-
-/* Exposed for user access */
-struct task_struct *kgdb_current;
-unsigned int kgdb_g_imask;
-int kgdb_trapa_val;
-int kgdb_excode;
-
-/* Default values for SCI (can override via kernel args in setup.c) */
-#ifndef CONFIG_KGDB_DEFPORT
-#define CONFIG_KGDB_DEFPORT 1
-#endif
-
-#ifndef CONFIG_KGDB_DEFBAUD
-#define CONFIG_KGDB_DEFBAUD 115200
-#endif
-
-#if defined(CONFIG_KGDB_DEFPARITY_E)
-#define CONFIG_KGDB_DEFPARITY 'E'
-#elif defined(CONFIG_KGDB_DEFPARITY_O)
-#define CONFIG_KGDB_DEFPARITY 'O'
-#else /* CONFIG_KGDB_DEFPARITY_N */
-#define CONFIG_KGDB_DEFPARITY 'N'
-#endif
-
-#ifdef CONFIG_KGDB_DEFBITS_7
-#define CONFIG_KGDB_DEFBITS '7'
-#else /* CONFIG_KGDB_DEFBITS_8 */
-#define CONFIG_KGDB_DEFBITS '8'
-#endif
-
-/* SCI/UART settings, used in kgdb_console_setup() */
-int kgdb_portnum = CONFIG_KGDB_DEFPORT;
-int kgdb_baud = CONFIG_KGDB_DEFBAUD;
-char kgdb_parity = CONFIG_KGDB_DEFPARITY;
-char kgdb_bits = CONFIG_KGDB_DEFBITS;
-
-/* Jump buffer for setjmp/longjmp */
-static jmp_buf rem_com_env;
-
-/* TRA differs sh3/4 */
-#if defined(CONFIG_CPU_SH3)
-#define TRA 0xffffffd0
-#elif defined(CONFIG_CPU_SH4)
-#define TRA 0xff000020
-#endif
-
-/* Macros for single step instruction identification */
-#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900)
-#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00)
-#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
- (((op) & 0x7f ) << 1))
-#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00)
-#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00)
-#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000)
-#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
- (((op) & 0x7ff) << 1))
-#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023)
-#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8)
-#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000)
-#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
- (((op) & 0x7ff) << 1))
-#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003)
-#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf)
-#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b)
-#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf)
-#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b)
-#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf)
-#define OPCODE_RTS(op) ((op) == 0xb)
-#define OPCODE_RTE(op) ((op) == 0x2b)
-
-#define SR_T_BIT_MASK 0x1
-#define STEP_OPCODE 0xc320
-#define BIOS_CALL_TRAP 0x3f
-
-/* Exception codes as per SH-4 core manual */
-#define ADDRESS_ERROR_LOAD_VEC 7
-#define ADDRESS_ERROR_STORE_VEC 8
-#define TRAP_VEC 11
-#define INVALID_INSN_VEC 12
-#define INVALID_SLOT_VEC 13
-#define NMI_VEC 14
-#define USER_BREAK_VEC 15
-#define SERIAL_BREAK_VEC 58
-
-/* Misc static */
-static int stepped_address;
-static short stepped_opcode;
-static const char hexchars[] = "0123456789abcdef";
-static char in_buffer[BUFMAX];
-static char out_buffer[OUTBUFMAX];
-
-static void kgdb_to_gdb(const char *s);
-
-#ifdef CONFIG_KGDB_THREAD
-static struct task_struct *trapped_thread;
-static struct task_struct *current_thread;
-typedef unsigned char threadref[8];
-#define BUF_THREAD_ID_SIZE 16
-#endif
-
-/* Return addr as a real volatile address */
-static inline unsigned int ctrl_inl(const unsigned long addr)
-{
- return *(volatile unsigned long *) addr;
-}
-
-/* Correctly set *addr using volatile */
-static inline void ctrl_outl(const unsigned int b, unsigned long addr)
-{
- *(volatile unsigned long *) addr = b;
-}
-
-/* Get high hex bits */
-static char highhex(const int x)
-{
- return hexchars[(x >> 4) & 0xf];
-}
-
-/* Get low hex bits */
-static char lowhex(const int x)
-{
- return hexchars[x & 0xf];
-}
-
-/* Convert ch to hex */
-static int hex(const char ch)
-{
- if ((ch >= 'a') && (ch <= 'f'))
- return (ch - 'a' + 10);
- if ((ch >= '0') && (ch <= '9'))
- return (ch - '0');
- if ((ch >= 'A') && (ch <= 'F'))
- return (ch - 'A' + 10);
- return (-1);
-}
-
-/* Convert the memory pointed to by mem into hex, placing result in buf.
- Returns a pointer to the last char put in buf (null) */
-static char *mem_to_hex(const char *mem, char *buf, const int count)
-{
- int i;
- int ch;
- unsigned short s_val;
- unsigned long l_val;
-
- /* Check for 16 or 32 */
- if (count == 2 && ((long) mem & 1) == 0) {
- s_val = *(unsigned short *) mem;
- mem = (char *) &s_val;
- } else if (count == 4 && ((long) mem & 3) == 0) {
- l_val = *(unsigned long *) mem;
- mem = (char *) &l_val;
- }
- for (i = 0; i < count; i++) {
- ch = *mem++;
- *buf++ = highhex(ch);
- *buf++ = lowhex(ch);
- }
- *buf = 0;
- return (buf);
-}
-
-/* Convert the hex array pointed to by buf into binary, to be placed in mem.
- Return a pointer to the character after the last byte written */
-static char *hex_to_mem(const char *buf, char *mem, const int count)
-{
- int i;
- unsigned char ch;
-
- for (i = 0; i < count; i++) {
- ch = hex(*buf++) << 4;
- ch = ch + hex(*buf++);
- *mem++ = ch;
- }
- return (mem);
-}
-
-/* While finding valid hex chars, convert to an integer, then return it */
-static int hex_to_int(char **ptr, int *int_value)
-{
- int num_chars = 0;
- int hex_value;
-
- *int_value = 0;
-
- while (**ptr) {
- hex_value = hex(**ptr);
- if (hex_value >= 0) {
- *int_value = (*int_value << 4) | hex_value;
- num_chars++;
- } else
- break;
- (*ptr)++;
- }
- return num_chars;
-}
-
-/* Copy the binary array pointed to by buf into mem. Fix $, #,
- and 0x7d escaped with 0x7d. Return a pointer to the character
- after the last byte written. */
-static char *ebin_to_mem(const char *buf, char *mem, int count)
-{
- for (; count > 0; count--, buf++) {
- if (*buf == 0x7d)
- *mem++ = *(++buf) ^ 0x20;
- else
- *mem++ = *buf;
- }
- return mem;
-}
-
-/* Pack a hex byte */
-static char *pack_hex_byte(char *pkt, int byte)
-{
- *pkt++ = hexchars[(byte >> 4) & 0xf];
- *pkt++ = hexchars[(byte & 0xf)];
- return pkt;
-}
-
-#ifdef CONFIG_KGDB_THREAD
-
-/* Pack a thread ID */
-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;
-}
-
-/* Convert an integer into our threadref */
-static void int_to_threadref(threadref * id, const int value)
-{
- unsigned char *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);
-}
-
-/* Return a task structure ptr for a particular pid */
-static struct task_struct *get_thread(int pid)
-{
- struct task_struct *thread;
-
- /* Use PID_MAX w/gdb for pid 0 */
- if (pid == PID_MAX) pid = 0;
-
- /* First check via PID */
- thread = find_task_by_pid(pid);
-
- if (thread)
- return thread;
-
- /* Start at the start */
- thread = init_tasks[0];
-
- /* Walk along the linked list of tasks */
- do {
- if (thread->pid == pid)
- return thread;
- thread = thread->next_task;
- } while (thread != init_tasks[0]);
-
- return NULL;
-}
-
-#endif /* CONFIG_KGDB_THREAD */
-
-/* Scan for the start char '$', read the packet and check the checksum */
-static void get_packet(char *buffer, int buflen)
-{
- unsigned char checksum;
- unsigned char xmitcsum;
- int i;
- int count;
- char ch;
-
- do {
- /* Ignore everything until the start character */
- while ((ch = get_debug_char()) != '$');
-
- checksum = 0;
- xmitcsum = -1;
- count = 0;
-
- /* Now, read until a # or end of buffer is found */
- while (count < (buflen - 1)) {
- ch = get_debug_char();
-
- if (ch == '#')
- break;
-
- checksum = checksum + ch;
- buffer[count] = ch;
- count = count + 1;
- }
-
- buffer[count] = 0;
-
- /* Continue to read checksum following # */
- if (ch == '#') {
- xmitcsum = hex(get_debug_char()) << 4;
- xmitcsum += hex(get_debug_char());
-
- /* Checksum */
- if (checksum != xmitcsum)
- put_debug_char('-'); /* Failed checksum */
- else {
- /* Ack successful transfer */
- put_debug_char('+');
-
- /* If a sequence char is present, reply
- the sequence ID */
- if (buffer[2] == ':') {
- put_debug_char(buffer[0]);
- put_debug_char(buffer[1]);
-
- /* Remove sequence chars from buffer */
- count = strlen(buffer);
- for (i = 3; i <= count; i++)
- buffer[i - 3] = buffer[i];
- }
- }
- }
- }
- while (checksum != xmitcsum); /* Keep trying while we fail */
-}
-
-/* Send the packet in the buffer with run-length encoding */
-static void put_packet(char *buffer)
-{
- int checksum;
- char *src;
- int runlen;
- int encode;
-
- do {
- src = buffer;
- put_debug_char('$');
- checksum = 0;
-
- /* Continue while we still have chars left */
- while (*src) {
- /* Check for runs up to 99 chars long */
- for (runlen = 1; runlen < 99; runlen++) {
- if (src[0] != src[runlen])
- break;
- }
-
- if (runlen > 3) {
- /* Got a useful amount, send encoding */
- encode = runlen + ' ' - 4;
- put_debug_char(*src); checksum += *src;
- put_debug_char('*'); checksum += '*';
- put_debug_char(encode); checksum += encode;
- src += runlen;
- } else {
- /* Otherwise just send the current char */
- put_debug_char(*src); checksum += *src;
- src += 1;
- }
- }
-
- /* '#' Separator, put high and low components of checksum */
- put_debug_char('#');
- put_debug_char(highhex(checksum));
- put_debug_char(lowhex(checksum));
- }
- while ((get_debug_char()) != '+'); /* While no ack */
-}
-
-/* A bus error has occurred - perform a longjmp to return execution and
- allow handling of the error */
-static void kgdb_handle_bus_error(void)
-{
- longjmp(rem_com_env, 1);
-}
-
-/* Translate SH-3/4 exception numbers to unix-like signal values */
-static int compute_signal(const int excep_code)
-{
- int sigval;
-
- switch (excep_code) {
-
- case INVALID_INSN_VEC:
- case INVALID_SLOT_VEC:
- sigval = SIGILL;
- break;
- case ADDRESS_ERROR_LOAD_VEC:
- case ADDRESS_ERROR_STORE_VEC:
- sigval = SIGSEGV;
- break;
-
- case SERIAL_BREAK_VEC:
- case NMI_VEC:
- sigval = SIGINT;
- break;
-
- case USER_BREAK_VEC:
- case TRAP_VEC:
- sigval = SIGTRAP;
- break;
-
- default:
- sigval = SIGBUS; /* "software generated" */
- break;
- }
-
- return (sigval);
-}
-
-/* Make a local copy of the registers passed into the handler (bletch) */
-static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs,
- int *gdb_regs)
-{
- gdb_regs[R0] = regs->regs[R0];
- gdb_regs[R1] = regs->regs[R1];
- gdb_regs[R2] = regs->regs[R2];
- gdb_regs[R3] = regs->regs[R3];
- gdb_regs[R4] = regs->regs[R4];
- gdb_regs[R5] = regs->regs[R5];
- gdb_regs[R6] = regs->regs[R6];
- gdb_regs[R7] = regs->regs[R7];
- gdb_regs[R8] = regs->regs[R8];
- gdb_regs[R9] = regs->regs[R9];
- gdb_regs[R10] = regs->regs[R10];
- gdb_regs[R11] = regs->regs[R11];
- gdb_regs[R12] = regs->regs[R12];
- gdb_regs[R13] = regs->regs[R13];
- gdb_regs[R14] = regs->regs[R14];
- gdb_regs[R15] = regs->regs[R15];
- gdb_regs[PC] = regs->pc;
- gdb_regs[PR] = regs->pr;
- gdb_regs[GBR] = regs->gbr;
- gdb_regs[MACH] = regs->mach;
- gdb_regs[MACL] = regs->macl;
- gdb_regs[SR] = regs->sr;
- gdb_regs[VBR] = regs->vbr;
-}
-
-/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
-static void gdb_regs_to_kgdb_regs(const int *gdb_regs,
- struct kgdb_regs *regs)
-{
- regs->regs[R0] = gdb_regs[R0];
- regs->regs[R1] = gdb_regs[R1];
- regs->regs[R2] = gdb_regs[R2];
- regs->regs[R3] = gdb_regs[R3];
- regs->regs[R4] = gdb_regs[R4];
- regs->regs[R5] = gdb_regs[R5];
- regs->regs[R6] = gdb_regs[R6];
- regs->regs[R7] = gdb_regs[R7];
- regs->regs[R8] = gdb_regs[R8];
- regs->regs[R9] = gdb_regs[R9];
- regs->regs[R10] = gdb_regs[R10];
- regs->regs[R11] = gdb_regs[R11];
- regs->regs[R12] = gdb_regs[R12];
- regs->regs[R13] = gdb_regs[R13];
- regs->regs[R14] = gdb_regs[R14];
- regs->regs[R15] = gdb_regs[R15];
- regs->pc = gdb_regs[PC];
- regs->pr = gdb_regs[PR];
- regs->gbr = gdb_regs[GBR];
- regs->mach = gdb_regs[MACH];
- regs->macl = gdb_regs[MACL];
- regs->sr = gdb_regs[SR];
- regs->vbr = gdb_regs[VBR];
-}
-
-#ifdef CONFIG_KGDB_THREAD
-/* Make a local copy of registers from the specified thread */
-asmlinkage void ret_from_fork(void);
-static void thread_regs_to_gdb_regs(const struct task_struct *thread,
- int *gdb_regs)
-{
- int regno;
- int *tregs;
-
- /* Initialize to zero */
- for (regno = 0; regno < MAXREG; regno++)
- gdb_regs[regno] = 0;
-
- /* Just making sure... */
- if (thread == NULL)
- return;
-
- /* A new fork has pt_regs on the stack from a fork() call */
- if (thread->thread.pc == (unsigned long)ret_from_fork) {
-
- int vbr_val;
- struct pt_regs *kregs;
- kregs = (struct pt_regs*)thread->thread.sp;
-
- gdb_regs[R0] = kregs->regs[R0];
- gdb_regs[R1] = kregs->regs[R1];
- gdb_regs[R2] = kregs->regs[R2];
- gdb_regs[R3] = kregs->regs[R3];
- gdb_regs[R4] = kregs->regs[R4];
- gdb_regs[R5] = kregs->regs[R5];
- gdb_regs[R6] = kregs->regs[R6];
- gdb_regs[R7] = kregs->regs[R7];
- gdb_regs[R8] = kregs->regs[R8];
- gdb_regs[R9] = kregs->regs[R9];
- gdb_regs[R10] = kregs->regs[R10];
- gdb_regs[R11] = kregs->regs[R11];
- gdb_regs[R12] = kregs->regs[R12];
- gdb_regs[R13] = kregs->regs[R13];
- gdb_regs[R14] = kregs->regs[R14];
- gdb_regs[R15] = kregs->regs[R15];
- gdb_regs[PC] = kregs->pc;
- gdb_regs[PR] = kregs->pr;
- gdb_regs[GBR] = kregs->gbr;
- gdb_regs[MACH] = kregs->mach;
- gdb_regs[MACL] = kregs->macl;
- gdb_regs[SR] = kregs->sr;
-
- asm("stc vbr, %0":"=r"(vbr_val));
- gdb_regs[VBR] = vbr_val;
- return;
- }
-
- /* Otherwise, we have only some registers from switch_to() */
- tregs = (int *)thread->thread.sp;
- gdb_regs[R15] = (int)tregs;
- gdb_regs[R14] = *tregs++;
- gdb_regs[R13] = *tregs++;
- gdb_regs[R12] = *tregs++;
- gdb_regs[R11] = *tregs++;
- gdb_regs[R10] = *tregs++;
- gdb_regs[R9] = *tregs++;
- gdb_regs[R8] = *tregs++;
- gdb_regs[PR] = *tregs++;
- gdb_regs[GBR] = *tregs++;
- gdb_regs[PC] = thread->thread.pc;
-}
-#endif /* CONFIG_KGDB_THREAD */
-
-/* Calculate the new address for after a step */
-static short *get_step_address(void)
-{
- short op = *(short *) trap_registers.pc;
- long addr;
-
- /* BT */
- if (OPCODE_BT(op)) {
- if (trap_registers.sr & SR_T_BIT_MASK)
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 2;
- }
-
- /* BTS */
- else if (OPCODE_BTS(op)) {
- if (trap_registers.sr & SR_T_BIT_MASK)
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 4; /* Not in delay slot */
- }
-
- /* BF */
- else if (OPCODE_BF(op)) {
- if (!(trap_registers.sr & SR_T_BIT_MASK))
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 2;
- }
-
- /* BFS */
- else if (OPCODE_BFS(op)) {
- if (!(trap_registers.sr & SR_T_BIT_MASK))
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 4; /* Not in delay slot */
- }
-
- /* BRA */
- else if (OPCODE_BRA(op))
- addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
-
- /* BRAF */
- else if (OPCODE_BRAF(op))
- addr = trap_registers.pc + 4
- + trap_registers.regs[OPCODE_BRAF_REG(op)];
-
- /* BSR */
- else if (OPCODE_BSR(op))
- addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
-
- /* BSRF */
- else if (OPCODE_BSRF(op))
- addr = trap_registers.pc + 4
- + trap_registers.regs[OPCODE_BSRF_REG(op)];
-
- /* JMP */
- else if (OPCODE_JMP(op))
- addr = trap_registers.regs[OPCODE_JMP_REG(op)];
-
- /* JSR */
- else if (OPCODE_JSR(op))
- addr = trap_registers.regs[OPCODE_JSR_REG(op)];
-
- /* RTS */
- else if (OPCODE_RTS(op))
- addr = trap_registers.pr;
-
- /* RTE */
- else if (OPCODE_RTE(op))
- addr = trap_registers.regs[15];
-
- /* Other */
- else
- addr = trap_registers.pc + 2;
-
- kgdb_flush_icache_range(addr, addr + 2);
- return (short *) addr;
-}
-
-/* Set up a single-step. Replace the instruction immediately after the
- current instruction (i.e. next in the expected flow of control) with a
- trap instruction, so that returning will cause only a single instruction
- to be executed. Note that this model is slightly broken for instructions
- with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch
- and the instruction in the delay slot will be executed. */
-static void do_single_step(void)
-{
- unsigned short *addr = 0;
-
- /* Determine where the target instruction will send us to */
- addr = get_step_address();
- stepped_address = (int)addr;
-
- /* Replace it */
- stepped_opcode = *(short *)addr;
- *addr = STEP_OPCODE;
-
- /* Flush and return */
- kgdb_flush_icache_range((long) addr, (long) addr + 2);
- return;
-}
-
-/* Undo a single step */
-static void undo_single_step(void)
-{
- /* If we have stepped, put back the old instruction */
- /* Use stepped_address in case we stopped elsewhere */
- if (stepped_opcode != 0) {
- *(short*)stepped_address = stepped_opcode;
- kgdb_flush_icache_range(stepped_address, stepped_address + 2);
- }
- stepped_opcode = 0;
-}
-
-/* Send a signal message */
-static void send_signal_msg(const int signum)
-{
-#ifndef CONFIG_KGDB_THREAD
- out_buffer[0] = 'S';
- out_buffer[1] = highhex(signum);
- out_buffer[2] = lowhex(signum);
- out_buffer[3] = 0;
- put_packet(out_buffer);
-#else /* CONFIG_KGDB_THREAD */
- int threadid;
- threadref thref;
- char *out = out_buffer;
- const char *tstring = "thread";
-
- *out++ = 'T';
- *out++ = highhex(signum);
- *out++ = lowhex(signum);
-
- while (*tstring) {
- *out++ = *tstring++;
- }
- *out++ = ':';
-
- threadid = trapped_thread->pid;
- if (threadid == 0) threadid = PID_MAX;
- int_to_threadref(&thref, threadid);
- pack_threadid(out, &thref);
- out += BUF_THREAD_ID_SIZE;
- *out++ = ';';
-
- *out = 0;
- put_packet(out_buffer);
-#endif /* CONFIG_KGDB_THREAD */
-}
-
-/* Reply that all was well */
-static void send_ok_msg(void)
-{
- strcpy(out_buffer, "OK");
- put_packet(out_buffer);
-}
-
-/* Reply that an error occurred */
-static void send_err_msg(void)
-{
- strcpy(out_buffer, "E01");
- put_packet(out_buffer);
-}
-
-/* Empty message indicates unrecognised command */
-static void send_empty_msg(void)
-{
- put_packet("");
-}
-
-/* Read memory due to 'm' message */
-static void read_mem_msg(void)
-{
- char *ptr;
- int addr;
- int length;
-
- /* Jmp, disable bus error handler */
- if (setjmp(rem_com_env) == 0) {
-
- kgdb_nofault = 1;
-
- /* Walk through, have m<addr>,<length> */
- ptr = &in_buffer[1];
- if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
- if (hex_to_int(&ptr, &length)) {
- ptr = 0;
- if (length * 2 > OUTBUFMAX)
- length = OUTBUFMAX / 2;
- mem_to_hex((char *) addr, out_buffer, length);
- }
- if (ptr)
- send_err_msg();
- else
- put_packet(out_buffer);
- } else
- send_err_msg();
-
- /* Restore bus error handler */
- kgdb_nofault = 0;
-}
-
-/* Write memory due to 'M' or 'X' message */
-static void write_mem_msg(int binary)
-{
- char *ptr;
- int addr;
- int length;
-
- if (setjmp(rem_com_env) == 0) {
-
- kgdb_nofault = 1;
-
- /* Walk through, have M<addr>,<length>:<data> */
- ptr = &in_buffer[1];
- if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
- if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) {
- if (binary)
- ebin_to_mem(ptr, (char*)addr, length);
- else
- hex_to_mem(ptr, (char*)addr, length);
- kgdb_flush_icache_range(addr, addr + length);
- ptr = 0;
- send_ok_msg();
- }
- if (ptr)
- send_err_msg();
- } else
- send_err_msg();
-
- /* Restore bus error handler */
- kgdb_nofault = 0;
-}
-
-/* Continue message */
-static void continue_msg(void)
-{
- /* Try to read optional parameter, PC unchanged if none */
- char *ptr = &in_buffer[1];
- int addr;
-
- if (hex_to_int(&ptr, &addr))
- trap_registers.pc = addr;
-}
-
-/* Continue message with signal */
-static void continue_with_sig_msg(void)
-{
- int signal;
- char *ptr = &in_buffer[1];
- int addr;
-
- /* Report limitation */
- kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n");
-
- /* Signal */
- hex_to_int(&ptr, &signal);
- if (*ptr == ';')
- ptr++;
-
- /* Optional address */
- if (hex_to_int(&ptr, &addr))
- trap_registers.pc = addr;
-}
-
-/* Step message */
-static void step_msg(void)
-{
- continue_msg();
- do_single_step();
-}
-
-/* Step message with signal */
-static void step_with_sig_msg(void)
-{
- continue_with_sig_msg();
- do_single_step();
-}
-
-/* Send register contents */
-static void send_regs_msg(void)
-{
-#ifdef CONFIG_KGDB_THREAD
- if (!current_thread)
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
- else
- thread_regs_to_gdb_regs(current_thread, registers);
-#else
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
-#endif
-
- mem_to_hex((char *) registers, out_buffer, NUMREGBYTES);
- put_packet(out_buffer);
-}
-
-/* Set register contents - currently can't set other thread's registers */
-static void set_regs_msg(void)
-{
-#ifdef CONFIG_KGDB_THREAD
- if (!current_thread) {
-#endif
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
- hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES);
- gdb_regs_to_kgdb_regs(registers, &trap_registers);
- send_ok_msg();
-#ifdef CONFIG_KGDB_THREAD
- } else
- send_err_msg();
-#endif
-}
-
-
-#ifdef CONFIG_KGDB_THREAD
-
-/* Set the status for a thread */
-void set_thread_msg(void)
-{
- int threadid;
- struct task_struct *thread = NULL;
- char *ptr;
-
- switch (in_buffer[1]) {
-
- /* To select which thread for gG etc messages, i.e. supported */
- case 'g':
-
- ptr = &in_buffer[2];
- hex_to_int(&ptr, &threadid);
- thread = get_thread(threadid);
-
- /* If we haven't found it */
- if (!thread) {
- send_err_msg();
- break;
- }
-
- /* Set current_thread (or not) */
- if (thread == trapped_thread)
- current_thread = NULL;
- else
- current_thread = thread;
- send_ok_msg();
- break;
-
- /* To select which thread for cCsS messages, i.e. unsupported */
- case 'c':
- send_ok_msg();
- break;
-
- default:
- send_empty_msg();
- break;
- }
-}
-
-/* Is a thread alive? */
-static void thread_status_msg(void)
-{
- char *ptr;
- int threadid;
- struct task_struct *thread = NULL;
-
- ptr = &in_buffer[1];
- hex_to_int(&ptr, &threadid);
- thread = get_thread(threadid);
- if (thread)
- send_ok_msg();
- else
- send_err_msg();
-}
-/* Send the current thread ID */
-static void thread_id_msg(void)
-{
- int threadid;
- threadref thref;
-
- out_buffer[0] = 'Q';
- out_buffer[1] = 'C';
-
- if (current_thread)
- threadid = current_thread->pid;
- else if (trapped_thread)
- threadid = trapped_thread->pid;
- else /* Impossible, but just in case! */
- {
- send_err_msg();
- return;
- }
-
- /* Translate pid 0 to PID_MAX for gdb */
- if (threadid == 0) threadid = PID_MAX;
-
- int_to_threadref(&thref, threadid);
- pack_threadid(out_buffer + 2, &thref);
- out_buffer[2 + BUF_THREAD_ID_SIZE] = '\0';
- put_packet(out_buffer);
-}
-
-/* Send thread info */
-static void thread_info_msg(void)
-{
- struct task_struct *thread = NULL;
- int threadid;
- char *pos;
- threadref thref;
-
- /* Start with 'm' */
- out_buffer[0] = 'm';
- pos = &out_buffer[1];
-
- /* For all possible thread IDs - this will overrun if > 44 threads! */
- /* Start at 1 and include PID_MAX (since GDB won't use pid 0...) */
- for (threadid = 1; threadid <= PID_MAX; threadid++) {
-
- read_lock(&tasklist_lock);
- thread = get_thread(threadid);
- read_unlock(&tasklist_lock);
-
- /* If it's a valid thread */
- if (thread) {
- int_to_threadref(&thref, threadid);
- pack_threadid(pos, &thref);
- pos += BUF_THREAD_ID_SIZE;
- *pos++ = ',';
- }
- }
- *--pos = 0; /* Lose final comma */
- put_packet(out_buffer);
-
-}
-
-/* Return printable info for gdb's 'info threads' command */
-static void thread_extra_info_msg(void)
-{
- int threadid;
- struct task_struct *thread = NULL;
- char buffer[20], *ptr;
- int i;
-
- /* Extract thread ID */
- ptr = &in_buffer[17];
- hex_to_int(&ptr, &threadid);
- thread = get_thread(threadid);
-
- /* If we don't recognise it, say so */
- if (thread == NULL)
- strcpy(buffer, "(unknown)");
- else
- strcpy(buffer, thread->comm);
-
- /* Construct packet */
- for (i = 0, ptr = out_buffer; buffer[i]; i++)
- ptr = pack_hex_byte(ptr, buffer[i]);
-
- if (thread->thread.pc == (unsigned long)ret_from_fork) {
- strcpy(buffer, "<new fork>");
- for (i = 0; buffer[i]; i++)
- ptr = pack_hex_byte(ptr, buffer[i]);
- }
-
- *ptr = '\0';
- put_packet(out_buffer);
-}
-
-/* Handle all qFooBarBaz messages - have to use an if statement as
- opposed to a switch because q messages can have > 1 char id. */
-static void query_msg(void)
-{
- const char *q_start = &in_buffer[1];
-
- /* qC = return current thread ID */
- if (strncmp(q_start, "C", 1) == 0)
- thread_id_msg();
-
- /* qfThreadInfo = query all threads (first) */
- else if (strncmp(q_start, "fThreadInfo", 11) == 0)
- thread_info_msg();
-
- /* qsThreadInfo = query all threads (subsequent). We know we have sent
- them all after the qfThreadInfo message, so there are no to send */
- else if (strncmp(q_start, "sThreadInfo", 11) == 0)
- put_packet("l"); /* el = last */
-
- /* qThreadExtraInfo = supply printable information per thread */
- else if (strncmp(q_start, "ThreadExtraInfo", 15) == 0)
- thread_extra_info_msg();
-
- /* Unsupported - empty message as per spec */
- else
- send_empty_msg();
-}
-#endif /* CONFIG_KGDB_THREAD */
-
-/*
- * Bring up the ports..
- */
-static int kgdb_serial_setup(void)
-{
- extern int kgdb_console_setup(struct console *co, char *options);
- struct console dummy;
-
- kgdb_console_setup(&dummy, 0);
-
- return 0;
-}
-
-/* The command loop, read and act on requests */
-static void kgdb_command_loop(const int excep_code, const int trapa_value)
-{
- int sigval;
-
- if (excep_code == NMI_VEC) {
-#ifndef CONFIG_KGDB_NMI
- KGDB_PRINTK("Ignoring unexpected NMI?\n");
- return;
-#else /* CONFIG_KGDB_NMI */
- if (!kgdb_enabled) {
- kgdb_enabled = 1;
- kgdb_init();
- }
-#endif /* CONFIG_KGDB_NMI */
- }
-
- /* Ignore if we're disabled */
- if (!kgdb_enabled)
- return;
-
-#ifdef CONFIG_KGDB_THREAD
- /* Until GDB specifies a thread */
- current_thread = NULL;
- trapped_thread = current;
-#endif
-
- /* Enter GDB mode (e.g. after detach) */
- if (!kgdb_in_gdb_mode) {
- /* Do serial setup, notify user, issue preemptive ack */
- kgdb_serial_setup();
- KGDB_PRINTK("Waiting for GDB (on %s%d at %d baud)\n",
- (kgdb_porttype ? kgdb_porttype->name : ""),
- kgdb_portnum, kgdb_baud);
- kgdb_in_gdb_mode = 1;
- put_debug_char('+');
- }
-
- /* Reply to host that an exception has occurred */
- sigval = compute_signal(excep_code);
- send_signal_msg(sigval);
-
- /* TRAP_VEC exception indicates a software trap inserted in place of
- code by GDB so back up PC by one instruction, as this instruction
- will later be replaced by its original one. Do NOT do this for
- trap 0xff, since that indicates a compiled-in breakpoint which
- will not be replaced (and we would retake the trap forever) */
- if ((excep_code == TRAP_VEC) && (trapa_value != (0xff << 2))) {
- trap_registers.pc -= 2;
- }
-
- /* Undo any stepping we may have done */
- undo_single_step();
-
- while (1) {
-
- out_buffer[0] = 0;
- get_packet(in_buffer, BUFMAX);
-
- /* Examine first char of buffer to see what we need to do */
- switch (in_buffer[0]) {
-
- case '?': /* Send which signal we've received */
- send_signal_msg(sigval);
- break;
-
- case 'g': /* Return the values of the CPU registers */
- send_regs_msg();
- break;
-
- case 'G': /* Set the value of the CPU registers */
- set_regs_msg();
- break;
-
- case 'm': /* Read LLLL bytes address AA..AA */
- read_mem_msg();
- break;
-
- case 'M': /* Write LLLL bytes address AA..AA, ret OK */
- write_mem_msg(0); /* 0 = data in hex */
- break;
-
- case 'X': /* Write LLLL bytes esc bin address AA..AA */
- if (kgdb_bits == '8')
- write_mem_msg(1); /* 1 = data in binary */
- else
- send_empty_msg();
- break;
-
- case 'C': /* Continue, signum included, we ignore it */
- continue_with_sig_msg();
- return;
-
- case 'c': /* Continue at address AA..AA (optional) */
- continue_msg();
- return;
-
- case 'S': /* Step, signum included, we ignore it */
- step_with_sig_msg();
- return;
-
- case 's': /* Step one instruction from AA..AA */
- step_msg();
- return;
-
-#ifdef CONFIG_KGDB_THREAD
-
- case 'H': /* Task related */
- set_thread_msg();
- break;
-
- case 'T': /* Query thread status */
- thread_status_msg();
- break;
-
- case 'q': /* Handle query - currently thread-related */
- query_msg();
- break;
-#endif
-
- case 'k': /* 'Kill the program' with a kernel ? */
- break;
-
- case 'D': /* Detach from program, send reply OK */
- kgdb_in_gdb_mode = 0;
- send_ok_msg();
- get_debug_char();
- return;
-
- default:
- send_empty_msg();
- break;
- }
- }
-}
-
-/* There has been an exception, most likely a breakpoint. */
-void kgdb_handle_exception(struct pt_regs *regs)
-{
- int excep_code, vbr_val;
- int count;
- int trapa_value = ctrl_inl(TRA);
-
- /* Copy kernel regs (from stack) */
- for (count = 0; count < 16; count++)
- trap_registers.regs[count] = regs->regs[count];
- trap_registers.pc = regs->pc;
- trap_registers.pr = regs->pr;
- trap_registers.sr = regs->sr;
- trap_registers.gbr = regs->gbr;
- trap_registers.mach = regs->mach;
- trap_registers.macl = regs->macl;
-
- asm("stc vbr, %0":"=r"(vbr_val));
- trap_registers.vbr = vbr_val;
-
- /* Get excode for command loop call, user access */
- asm("stc r2_bank, %0":"=r"(excep_code));
- kgdb_excode = excep_code;
-
- /* Other interesting environment items for reference */
- asm("stc r6_bank, %0":"=r"(kgdb_g_imask));
- kgdb_current = current;
- kgdb_trapa_val = trapa_value;
-
- /* Act on the exception */
- kgdb_command_loop(excep_code >> 5, trapa_value);
-
- kgdb_current = NULL;
-
- /* Copy back the (maybe modified) registers */
- for (count = 0; count < 16; count++)
- regs->regs[count] = trap_registers.regs[count];
- regs->pc = trap_registers.pc;
- regs->pr = trap_registers.pr;
- regs->sr = trap_registers.sr;
- regs->gbr = trap_registers.gbr;
- regs->mach = trap_registers.mach;
- regs->macl = trap_registers.macl;
-
- vbr_val = trap_registers.vbr;
- asm("ldc %0, vbr": :"r"(vbr_val));
-
- return;
-}
-
-/* Trigger a breakpoint by function */
-void breakpoint(void)
-{
- if (!kgdb_enabled) {
- kgdb_enabled = 1;
- kgdb_init();
- }
- BREAKPOINT();
-}
-
-/* Initialise the KGDB data structures and serial configuration */
-int kgdb_init(void)
-{
- if (!kgdb_enabled)
- return 1;
-
- in_nmi = 0;
- kgdb_nofault = 0;
- stepped_opcode = 0;
- kgdb_in_gdb_mode = 0;
-
- if (kgdb_serial_setup() != 0) {
- KGDB_PRINTK("serial setup error\n");
- return -1;
- }
-
- /* Init ptr to exception handler */
- kgdb_debug_hook = kgdb_handle_exception;
- kgdb_bus_err_hook = kgdb_handle_bus_error;
-
- /* Enter kgdb now if requested, or just report init done */
- if (kgdb_halt) {
- kgdb_in_gdb_mode = 1;
- put_debug_char('+');
- breakpoint();
- }
- else
- {
- KGDB_PRINTK("stub is initialized.\n");
- }
-
- return 0;
-}
-
-/* Make function available for "user messages"; console will use it too. */
-
-char gdbmsgbuf[BUFMAX];
-#define MAXOUT ((BUFMAX-2)/2)
-
-static void kgdb_msg_write(const char *s, unsigned count)
-{
- int i;
- int wcount;
- char *bufptr;
-
- /* 'O'utput */
- gdbmsgbuf[0] = 'O';
-
- /* Fill and send buffers... */
- while (count > 0) {
- bufptr = gdbmsgbuf + 1;
-
- /* Calculate how many this time */
- wcount = (count > MAXOUT) ? MAXOUT : count;
-
- /* Pack in hex chars */
- for (i = 0; i < wcount; i++)
- bufptr = pack_hex_byte(bufptr, s[i]);
- *bufptr = '\0';
-
- /* Move up */
- s += wcount;
- count -= wcount;
-
- /* Write packet */
- put_packet(gdbmsgbuf);
- }
-}
-
-static void kgdb_to_gdb(const char *s)
-{
- kgdb_msg_write(s, strlen(s));
-}
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-void kgdb_console_write(struct console *co, const char *s, unsigned count)
-{
- /* Bail if we're not talking to GDB */
- if (!kgdb_in_gdb_mode)
- return;
-
- kgdb_msg_write(s, count);
-}
-#endif
Index: linux-2.6.14/arch/sh/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/Makefile
+++ linux-2.6.14/arch/sh/kernel/Makefile
@@ -13,7 +13,7 @@ obj-y += cpu/
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_CF_ENABLER) += cf-enabler.o
obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o
-obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o
+obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o
obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
Index: linux-2.6.14/arch/sh/kernel/setup.c
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/setup.c
+++ linux-2.6.14/arch/sh/kernel/setup.c
@@ -27,10 +27,6 @@
#include <asm/irq.h>
#include <asm/setup.h>

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-static int kgdb_parse_options(char *options);
-#endif
extern void * __rd_start, * __rd_end;
/*
* Machine setup..
@@ -557,93 +553,3 @@ struct seq_operations cpuinfo_op = {
.show = show_cpuinfo,
};
#endif /* CONFIG_PROC_FS */
-
-#ifdef CONFIG_SH_KGDB
-/*
- * Parse command-line kgdb options. By default KGDB is enabled,
- * entered on error (or other action) using default serial info.
- * The command-line option can include a serial port specification
- * and an action to override default or configured behavior.
- */
-struct kgdb_sermap kgdb_sci_sermap =
-{ "ttySC", 5, kgdb_sci_setup, NULL };
-
-struct kgdb_sermap *kgdb_serlist = &kgdb_sci_sermap;
-struct kgdb_sermap *kgdb_porttype = &kgdb_sci_sermap;
-
-void kgdb_register_sermap(struct kgdb_sermap *map)
-{
- struct kgdb_sermap *last;
-
- for (last = kgdb_serlist; last->next; last = last->next)
- ;
- last->next = map;
- if (!map->namelen) {
- map->namelen = strlen(map->name);
- }
-}
-
-static int __init kgdb_parse_options(char *options)
-{
- char c;
- int baud;
-
- /* Check for port spec (or use default) */
-
- /* Determine port type and instance */
- if (!memcmp(options, "tty", 3)) {
- struct kgdb_sermap *map = kgdb_serlist;
-
- while (map && memcmp(options, map->name, map->namelen))
- map = map->next;
-
- if (!map) {
- KGDB_PRINTK("unknown port spec in %s\n", options);
- return -1;
- }
-
- kgdb_porttype = map;
- kgdb_serial_setup = map->setup_fn;
- kgdb_portnum = options[map->namelen] - '0';
- options += map->namelen + 1;
-
- options = (*options == ',') ? options+1 : options;
-
- /* Read optional parameters (baud/parity/bits) */
- baud = simple_strtoul(options, &options, 10);
- if (baud != 0) {
- kgdb_baud = baud;
-
- c = toupper(*options);
- if (c == 'E' || c == 'O' || c == 'N') {
- kgdb_parity = c;
- options++;
- }
-
- c = *options;
- if (c == '7' || c == '8') {
- kgdb_bits = c;
- options++;
- }
- options = (*options == ',') ? options+1 : options;
- }
- }
-
- /* Check for action specification */
- if (!memcmp(options, "halt", 4)) {
- kgdb_halt = 1;
- options += 4;
- } else if (!memcmp(options, "disabled", 8)) {
- kgdb_enabled = 0;
- options += 8;
- }
-
- if (*options) {
- KGDB_PRINTK("ignored unknown options: %s\n", options);
- return 0;
- }
- return 1;
-}
-__setup("kgdb=", kgdb_parse_options);
-#endif /* CONFIG_SH_KGDB */
-
Index: linux-2.6.14/arch/sh/kernel/time.c
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/time.c
+++ linux-2.6.14/arch/sh/kernel/time.c
@@ -34,9 +34,6 @@
#include <asm/rtc.h>
#include <asm/freq.h>
#include <asm/cpu/timer.h>
-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#endif

#include <linux/timex.h>
#include <linux/irq.h>
@@ -643,12 +640,4 @@ void __init time_init(void)
ctrl_outl(interval, TMU0_TCOR);
ctrl_outl(interval, TMU0_TCNT);
ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
-
-#if defined(CONFIG_SH_KGDB)
- /*
- * Set up kgdb as requested. We do it here because the serial
- * init uses the timer vars we just set up for figuring baud.
- */
- kgdb_init();
-#endif
}
Index: linux-2.6.14/arch/sh/kernel/traps.c
===================================================================
--- linux-2.6.14.orig/arch/sh/kernel/traps.c
+++ linux-2.6.14/arch/sh/kernel/traps.c
@@ -27,6 +27,7 @@
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>

#include <asm/system.h>
#include <asm/uaccess.h>
@@ -35,17 +36,8 @@
#include <asm/processor.h>
#include <asm/sections.h>

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#define CHK_REMOTE_DEBUG(regs) \
-{ \
- if ((kgdb_debug_hook != (kgdb_debug_hook_t *) NULL) && (!user_mode(regs))) \
- { \
- (*kgdb_debug_hook)(regs); \
- } \
-}
-#else
-#define CHK_REMOTE_DEBUG(regs)
+#ifndef CONFIG_KGDB
+#define kgdb_handle_exception(t, s, e, r)
#endif

#define DO_ERROR(trapnr, signr, str, name, tsk) \
@@ -66,7 +58,7 @@ asmlinkage void do_##name(unsigned long
local_irq_enable(); \
tsk->thread.error_code = error_code; \
tsk->thread.trap_no = trapnr; \
- CHK_REMOTE_DEBUG(&regs); \
+ kgdb_handle_exception(trapnr, signr, error_code, &regs); \
force_sig(signr, tsk); \
die_if_no_fixup(str,&regs,error_code); \
}
@@ -93,10 +85,12 @@ void die(const char * str, struct pt_reg
{
static int die_counter;

+#ifdef CONFIG_KGDB
+ kgdb_handle_exception(1, SIGTRAP, err, regs);
+#endif
console_verbose();
spin_lock_irq(&die_lock);
printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
- CHK_REMOTE_DEBUG(regs);
show_regs(regs);
spin_unlock_irq(&die_lock);
do_exit(SIGSEGV);
Index: linux-2.6.14/arch/sh/Makefile
===================================================================
--- linux-2.6.14.orig/arch/sh/Makefile
+++ linux-2.6.14/arch/sh/Makefile
@@ -23,7 +23,6 @@ cflags-$(CONFIG_CPU_SH4) += -m4 \
$(call cc-option,-mno-implicit-fp,-m4-nofpu)

cflags-$(CONFIG_SH_DSP) += -Wa,-dsp
-cflags-$(CONFIG_SH_KGDB) += -g

cflags-$(CONFIG_MORE_COMPILE_OPTIONS) += \
$(shell echo $(CONFIG_COMPILE_OPTIONS) | sed -e 's/"//g')
Index: linux-2.6.14/arch/sh/mm/extable.c
===================================================================
--- linux-2.6.14.orig/arch/sh/mm/extable.c
+++ linux-2.6.14/arch/sh/mm/extable.c
@@ -6,6 +6,7 @@

#include <linux/config.h>
#include <linux/module.h>
+#include <linux/kgdb.h>
#include <asm/uaccess.h>

int fixup_exception(struct pt_regs *regs)
@@ -17,6 +18,12 @@ int fixup_exception(struct pt_regs *regs
regs->pc = fixup->fixup;
return 1;
}
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Never reached. */
+#endif

return 0;
}
Index: linux-2.6.14/arch/sh/mm/fault.c
===================================================================
--- linux-2.6.14.orig/arch/sh/mm/fault.c
+++ linux-2.6.14/arch/sh/mm/fault.c
@@ -28,7 +28,6 @@
#include <asm/pgalloc.h>
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
-#include <asm/kgdb.h>

extern void die(const char *,struct pt_regs *,long);

@@ -45,11 +44,6 @@ asmlinkage void do_page_fault(struct pt_
struct vm_area_struct * vma;
unsigned long page;

-#ifdef CONFIG_SH_KGDB
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
tsk = current;
mm = tsk->mm;

@@ -153,6 +147,7 @@ no_context:
}
die("Oops", regs, writeaccess);
do_exit(SIGKILL);
+ dump_stack();

/*
* We ran out of memory, or some other thing happened to us that made
@@ -199,11 +194,6 @@ asmlinkage int __do_page_fault(struct pt
pte_t *pte;
pte_t entry;

-#ifdef CONFIG_SH_KGDB
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
#ifdef CONFIG_SH_STORE_QUEUES
addrmax = P4SEG_STORE_QUE + 0x04000000;
#endif
Index: linux-2.6.14/arch/sh/mm/fault-nommu.c
===================================================================
--- linux-2.6.14.orig/arch/sh/mm/fault-nommu.c
+++ linux-2.6.14/arch/sh/mm/fault-nommu.c
@@ -29,10 +29,6 @@
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>

-#if defined(CONFIG_SH_KGDB)
-#include <asm/kgdb.h>
-#endif
-
extern void die(const char *,struct pt_regs *,long);

/*
@@ -43,11 +39,6 @@ extern void die(const char *,struct pt_r
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
unsigned long address)
{
-#if defined(CONFIG_SH_KGDB)
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
@@ -69,11 +60,6 @@ asmlinkage void do_page_fault(struct pt_
asmlinkage int __do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
unsigned long address)
{
-#if defined(CONFIG_SH_KGDB)
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
if (address >= TASK_SIZE)
return 1;

Index: linux-2.6.14/Documentation/DocBook/kgdb.tmpl
===================================================================
--- linux-2.6.14.orig/Documentation/DocBook/kgdb.tmpl
+++ linux-2.6.14/Documentation/DocBook/kgdb.tmpl
@@ -122,6 +122,10 @@
serial driver, pass in: <constant>kgdbwait</constant>.
</para>
<para>
+ To specify the values of the SH SCI(F) serial port at boot:
+ <constant>kgdbsci=0,115200</constant>.
+ </para>
+ <para>
To specify the values of the serial port at boot:
<constant>kgdb8250=io,3f8,115200,3</constant>.
On IA64 this could also be:
@@ -171,6 +175,18 @@
application program.
</para>
</chapter>
+ <chapter id="ArchitectureNotes">
+ <title>Architecture specific notes</title>
+ <para>
+ SuperH: The NMI switch found on some boards can be used to trigger an
+ initial breakpoint. Subsequent triggers do nothing. If console
+ is enabled on the SCI(F) serial port, and that is the port being used
+ for KGDB, then you must trigger a breakpoint via sysrq, NMI, or
+ some other method prior to connecting, or echo a control-c to the
+ serial port. Also, to use the SCI(F) port for KGDB, the
+ <symbol>CONFIG_SERIAL_SH_SCI</symbol> driver must be enabled.
+ </para>
+ </chapter>
<chapter id="CommonBackEndReq">
<title>The common backend (required)</title>
<para>
Index: linux-2.6.14/drivers/serial/sh-sci.c
===================================================================
--- linux-2.6.14.orig/drivers/serial/sh-sci.c
+++ linux-2.6.14/drivers/serial/sh-sci.c
@@ -42,6 +42,7 @@
#include <linux/delay.h>
#include <linux/console.h>
#include <linux/bitops.h>
+#include <linux/kgdb.h>

#ifdef CONFIG_CPU_FREQ
#include <linux/notifier.h>
@@ -65,14 +66,16 @@

#include "sh-sci.h"

-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-
-static int kgdb_get_char(struct sci_port *port);
-static void kgdb_put_char(struct sci_port *port, char c);
-static void kgdb_handle_error(struct sci_port *port);
-static struct sci_port *kgdb_sci_port;
-#endif /* CONFIG_SH_KGDB */
+#ifdef CONFIG_KGDB_SH_SCI
+/* Speed of the UART. */
+static int kgdbsci_baud = CONFIG_KGDB_BAUDRATE
+
+/* Index of the UART, matches ttySCX naming. */
+static int kgdbsci_ttySC = CONFIG_KGDB_PORT_NUM;
+
+/* Make life easier on us. */
+#define KGDBPORT sci_ports[kgdbsci_ttySC]
+#endif /* CONFIG_KGDB_SH_SCI */

#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static struct sci_port *serial_console_port = 0;
@@ -85,18 +88,15 @@ static void sci_start_rx(struct uart_por
static void sci_stop_rx(struct uart_port *port);
static int sci_request_irq(struct sci_port *port);
static void sci_free_irq(struct sci_port *port);
+static void sci_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old);
+static int kgdbsci_init(void);

static struct sci_port sci_ports[SCI_NPORTS];
static struct uart_driver sci_uart_driver;

-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
-
-static void handle_error(struct uart_port *port)
-{ /* Clear error flags */
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
-}
-
-static int get_char(struct uart_port *port)
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_KGDB_SH_SCI)
+static int get_char_for_gdb(struct uart_port *port)
{
unsigned long flags;
unsigned short status;
@@ -106,7 +106,8 @@ static int get_char(struct uart_port *po
do {
status = sci_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) {
- handle_error(port);
+ /* Clear error flags. */
+ sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
continue;
}
} while (!(status & SCxSR_RDxF(port)));
@@ -117,21 +118,7 @@ static int get_char(struct uart_port *po

return c;
}
-
-/* Taken from sh-stub.c of GDB 4.18 */
-static const char hexchars[] = "0123456789abcdef";
-
-static __inline__ char highhex(int x)
-{
- return hexchars[(x >> 4) & 0xf];
-}
-
-static __inline__ char lowhex(int x)
-{
- return hexchars[x & 0xf];
-}
-
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_KGDB_SH_SCI */

/*
* Send the packet in buffer. The host gets one chance to read it.
@@ -163,21 +150,14 @@ static void put_string(struct sci_port *
const unsigned char *p = buffer;
int i;

-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+#ifdef CONFIG_SH_STANDARD_BIOS
int checksum;
- int usegdb=0;
+ const char hexchars[] = "0123456789abcdef";

-#ifdef CONFIG_SH_STANDARD_BIOS
/* This call only does a trap the first time it is
* called, and so is safe to do here unconditionally
*/
- usegdb |= sh_bios_in_gdb_mode();
-#endif
-#ifdef CONFIG_SH_KGDB
- usegdb |= (kgdb_in_gdb_mode && (port == kgdb_sci_port));
-#endif
-
- if (usegdb) {
+ if (sh_bios_in_gdb_mode()) {
/* $<packet info>#<checksum>. */
do {
unsigned char c;
@@ -189,18 +169,18 @@ static void put_string(struct sci_port *
int h, l;

c = *p++;
- h = highhex(c);
- l = lowhex(c);
+ h = hexchars[c >> 4];
+ l = hexchars[c % 16];
put_char(port, h);
put_char(port, l);
checksum += h + l;
}
put_char(port, '#');
- put_char(port, highhex(checksum));
- put_char(port, lowhex(checksum));
+ put_char(port, hexchars[checksum >> 4]);
+ put_char(port, hexchars[checksum & 16]);
} while (get_char(port) != '+');
} else
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif /* CONFIG_SH_STANDARD_BIOS */
for (i=0; i<count; i++) {
if (*p == 10)
put_char(port, '\r');
@@ -210,90 +190,163 @@ static void put_string(struct sci_port *
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */


-#ifdef CONFIG_SH_KGDB
-
-/* Is the SCI ready, ie is there a char waiting? */
-static int kgdb_is_char_ready(struct sci_port *port)
+#ifdef CONFIG_KGDB_SH_SCI
+static int kgdbsci_read_char(void)
{
- unsigned short status = sci_in(port, SCxSR);
-
- if (status & (SCxSR_ERRORS(port) | SCxSR_BRK(port)))
- kgdb_handle_error(port);
-
- return (status & SCxSR_RDxF(port));
+ return get_char_for_gdb(&KGDBPORT.port);
}

-/* Write a char */
-static void kgdb_put_char(struct sci_port *port, char c)
+/* Called from kgdbstub.c to put a character, just a wrapper */
+static void kgdbsci_write_char(int c)
{
- unsigned short status;
-
- do
- status = sci_in(port, SCxSR);
- while (!(status & SCxSR_TDxE(port)));
+ unsigned short status;

- sci_out(port, SCxTDR, c);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+ do
+ status = sci_in(&KGDBPORT.port, SCxSR);
+ while (!(status & SCxSR_TDxE(&KGDBPORT.port)));
+
+ sci_out(&KGDBPORT.port, SCxTDR, c);
+ sci_in(&KGDBPORT.port, SCxSR); /* Dummy read */
+ sci_out(&KGDBPORT.port, SCxSR, SCxSR_TDxE_CLEAR(&KGDBPORT.port));
}

-/* Get a char if there is one, else ret -1 */
-static int kgdb_get_char(struct sci_port *port)
+#ifndef CONFIG_SERIAL_SH_SCI_CONSOLE
+/* If we don't have console, we never hookup IRQs. But we need to
+ * hookup one so that we can interrupt the system.
+ */
+static irqreturn_t kgdbsci_rx_interrupt(int irq, void *ptr,
+ struct pt_regs *regs)
{
- int c;
-
- if (kgdb_is_char_ready(port) == 0)
- c = -1;
- else {
- c = sci_in(port, SCxRDR);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- }
+ struct uart_port *port = ptr;

- return c;
-}
+ if (!(sci_in(port, SCxSR) & SCxSR_RDxF(port)))
+ return IRQ_NONE;

-/* Called from kgdbstub.c to get a character, i.e. is blocking */
-static int kgdb_sci_getchar(void)
-{
- volatile int c;
+ if (kgdb_io_ops.init != kgdbsci_init) {
+ /* Throw away the data if another I/O routine is active */
+ get_char_for_gdb(&KGDBPORT.port);
+ } else
+ /* We've got an interrupt, so go ahead and call breakpoint() */
+ breakpoint();

- /* Keep trying to read a character, this could be neater */
- while ((c = kgdb_get_char(kgdb_sci_port)) < 0);
+ sci_in(port, SCxSR); /* dummy read */
+ sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));

- return c;
+ return IRQ_HANDLED;
}

-/* Called from kgdbstub.c to put a character, just a wrapper */
-static void kgdb_sci_putchar(int c)
+static irqreturn_t kgdbsci_mpxed_interrupt(int irq, void *ptr,
+ struct pt_regs *regs)
{
+ unsigned short ssr_status, scr_status;
+ struct uart_port *port = ptr;
+
+ ssr_status = sci_in(port,SCxSR);
+ scr_status = sci_in(port,SCSCR);
+
+ /* Rx Interrupt */
+ if ((ssr_status&0x0002) && (scr_status&0x0040))
+ kgdbsci_rx_interrupt(irq, ptr, regs);

- kgdb_put_char(kgdb_sci_port, c);
+ return IRQ_HANDLED;
}

-/* Clear any errors on the SCI */
-static void kgdb_handle_error(struct sci_port *port)
+static void __init kgdbsci_lateinit(void)
{
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); /* Clear error flags */
+ if (KGDBPORT.irqs[0] == KGDBPORT.irqs[1]) {
+ if (!KGDBPORT.irqs[0]) {
+ printk(KERN_ERR "kgdbsci: Cannot allocate irq.\n");
+ return;
+ }
+ if (request_irq(KGDBPORT.irqs[0], kgdbsci_mpxed_interrupt,
+ SA_INTERRUPT, "kgdbsci",
+ &KGDBPORT.port)) {
+ printk(KERN_ERR "kgdbsci: Cannot allocate irq.\n");
+ return;
+ }
+ } else {
+ if (KGDBPORT.irqs[1])
+ request_irq(KGDBPORT.irqs[1],
+ kgdbsci_rx_interrupt, SA_INTERRUPT,
+ "kgdbsci", &KGDBPORT.port);
+ }
}
+#endif

-/* Breakpoint if there's a break sent on the serial port */
-static void kgdb_break_interrupt(int irq, void *ptr, struct pt_regs *regs)
+/*
+ * We use the normal init routine to setup the port, so we can't be
+ * in here too early.
+ */
+static int kgdbsci_init(void)
{
- struct sci_port *port = ptr;
- unsigned short status = sci_in(port, SCxSR);
+ struct termios termios;

- if (status & SCxSR_BRK(port)) {
+ memset(&termios, 0, sizeof(struct termios));

- /* Break into the debugger if a break is detected */
- BREAKPOINT();
+ termios.c_cflag = CREAD | HUPCL | CLOCAL | CS8;
+ switch (kgdbsci_baud) {
+ case 9600:
+ termios.c_cflag |= B9600;
+ break;
+ case 19200:
+ termios.c_cflag |= B19200;
+ break;
+ case 38400:
+ termios.c_cflag |= B38400;
+ break;
+ case 57600:
+ termios.c_cflag |= B57600;
+ break;
+ case 115200:
+ termios.c_cflag |= B115200;
+ break;
+ }
+ sci_set_termios(&KGDBPORT.port, &termios, NULL);

- /* Clear */
- sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
- }
+ return 0;
}

-#endif /* CONFIG_SH_KGDB */
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdbsci_read_char,
+ .write_char = kgdbsci_write_char,
+ .init = kgdbsci_init,
+#ifndef CONFIG_SERIAL_SH_SCI_CONSOLE
+ .late_init = kgdbsci_lateinit,
+#else /* ! CONFIG_SERIAL_SH_SCI_CONSOLE */
+ .late_init = NULL,
+#endif /* ! CONFIG_SERIAL_SH_SCI_CONSOLE */
+ .pre_exception = NULL,
+ .post_exception = NULL
+};
+
+/*
+ * Syntax for this cmdline option is "kgdbsci=ttyno,baudrate".
+ */
+static int __init
+kgdbsci_opt(char *str)
+{
+ /* We might have anywhere from 1 to 3 ports. */
+ if (*str < '0' || *str > SCI_NPORTS + '0')
+ goto errout;
+ kgdbsci_ttySC = *str - '0';
+ str++;
+ if (*str != ',')
+ goto errout;
+ str++;
+ kgdbsci_baud = simple_strtoul(str, &str, 10);
+ if (kgdbsci_baud != 9600 && kgdbsci_baud != 19200 &&
+ kgdbsci_baud != 38400 && kgdbsci_baud != 57600 &&
+ kgdbsci_baud != 115200)
+ goto errout;
+
+ return 0;
+
+errout:
+ printk(KERN_ERR "Invalid syntax for option kgdbsci=\n");
+ return 1;
+}
+__setup("kgdbsci", kgdbsci_opt);
+#endif /* CONFIG_KGDB_SH_SCI */

#if defined(__H8300S__)
enum { sci_disable, sci_enable };
@@ -541,6 +594,16 @@ static inline void sci_receive_chars(str
continue;
}

+#ifdef CONFIG_KGDB_SH_SCI
+ /* We assume that a ^C on the port KGDB
+ * is using means that KGDB wants to
+ * interrupt the running system.
+ */
+ if (port->line == KGDBPORT.port.line &&
+ c == 3)
+ breakpoint();
+#endif
+
/* Store data and status */
tty->flip.char_buf_ptr[i] = c;
if (status&SCxSR_FER(port)) {
@@ -1552,6 +1615,7 @@ static int __init sci_console_init(void)
console_initcall(sci_console_init);
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */

+#if 0
#ifdef CONFIG_SH_KGDB
/*
* FIXME: Most of this can go away.. at the moment, we rely on
@@ -1597,30 +1661,9 @@ int __init kgdb_console_setup(struct con
return uart_set_options(port, co, baud, parity, bits, flow);
}
#endif /* CONFIG_SH_KGDB */
+#endif /* 0 */

-#ifdef CONFIG_SH_KGDB_CONSOLE
-static struct console kgdb_console = {
- .name = "ttySC",
- .write = kgdb_console_write,
- .setup = kgdb_console_setup,
- .flags = CON_PRINTBUFFER | CON_ENABLED,
- .index = -1,
- .data = &sci_uart_driver,
-};
-
-/* Register the KGDB console so we get messages (d'oh!) */
-static int __init kgdb_console_init(void)
-{
- register_console(&kgdb_console);
- return 0;
-}
-
-console_initcall(kgdb_console_init);
-#endif /* CONFIG_SH_KGDB_CONSOLE */
-
-#if defined(CONFIG_SH_KGDB_CONSOLE)
-#define SCI_CONSOLE &kgdb_console
-#elif defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
+#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
#define SCI_CONSOLE &serial_console
#else
#define SCI_CONSOLE 0
@@ -1689,4 +1732,3 @@ static void __exit sci_exit(void)

module_init(sci_init);
module_exit(sci_exit);
-
Index: linux-2.6.14/include/asm-sh/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-sh/kgdb.h
+++ linux-2.6.14/include/asm-sh/kgdb.h
@@ -2,94 +2,39 @@
* May be copied or modified under the terms of the GNU General Public
* License. See linux/COPYING for more information.
*
- * Based on original code by Glenn Engel, Jim Kingdon,
- * David Grothe <[email protected]>, Tigran Aivazian, <[email protected]> and
- * Amit S. Kale <[email protected]>
- *
- * Super-H port based on sh-stub.c (Ben Lee and Steve Chamberlain) by
- * Henry Bell <[email protected]>
- *
- * Header file for low-level support for remote debug using GDB.
+ * Based on a file that was modified or based on files by: Glenn Engel,
+ * Jim Kingdon, David Grothe <[email protected]>, Tigran Aivazian <[email protected]>,
+ * Amit S. Kale <[email protected]>, sh-stub.c from Ben Lee and
+ * Steve Chamberlain, Henry Bell <[email protected]>
+ *
+ * Maintainer: Tom Rini <[email protected]>
*
*/

#ifndef __KGDB_H
#define __KGDB_H

-#include <asm/ptrace.h>
-
-struct console;
+/* Based on sh-gdb.c from gdb-6.1, Glenn
+ Engel at HP Ben Lee and Steve Chamberlain */
+#define NUMREGBYTES 112 /* 92 */
+#define NUMCRITREGBYTES (9 << 2)
+#define BUFMAX 400

-/* Same as pt_regs but has vbr in place of syscall_nr */
+#ifndef __ASSEMBLY__
struct kgdb_regs {
unsigned long regs[16];
unsigned long pc;
unsigned long pr;
- unsigned long sr;
unsigned long gbr;
+ unsigned long vbr;
unsigned long mach;
unsigned long macl;
- unsigned long vbr;
-};
-
-/* State info */
-extern char kgdb_in_gdb_mode;
-extern int kgdb_done_init;
-extern int kgdb_enabled;
-extern int kgdb_nofault; /* Ignore bus errors (in gdb mem access) */
-extern int kgdb_halt; /* Execute initial breakpoint at startup */
-extern char in_nmi; /* Debounce flag to prevent NMI reentry*/
-
-/* SCI */
-extern int kgdb_portnum;
-extern int kgdb_baud;
-extern char kgdb_parity;
-extern char kgdb_bits;
-extern int kgdb_console_setup(struct console *, char *);
-
-/* Init and interface stuff */
-extern int kgdb_init(void);
-extern int (*kgdb_serial_setup)(void);
-extern int (*kgdb_getchar)(void);
-extern void (*kgdb_putchar)(int);
-
-struct kgdb_sermap {
- char *name;
- int namelen;
- int (*setup_fn)(struct console *, char *);
- struct kgdb_sermap *next;
+ unsigned long sr;
};
-extern void kgdb_register_sermap(struct kgdb_sermap *map);
-extern struct kgdb_sermap *kgdb_porttype;

-/* Trap functions */
-typedef void (kgdb_debug_hook_t)(struct pt_regs *regs);
-typedef void (kgdb_bus_error_hook_t)(void);
-extern kgdb_debug_hook_t *kgdb_debug_hook;
-extern kgdb_bus_error_hook_t *kgdb_bus_err_hook;
-
-extern void breakpoint(void);
-
-/* Console */
-struct console;
-void kgdb_console_write(struct console *co, const char *s, unsigned count);
-void kgdb_console_init(void);
-
-/* Prototypes for jmp fns */
-#define _JBLEN 9
-typedef int jmp_buf[_JBLEN];
-extern void longjmp(jmp_buf __jmpb, int __retval);
-extern int setjmp(jmp_buf __jmpb);
-
-/* Variadic macro to print our own message to the console */
-#define KGDB_PRINTK(...) printk("KGDB: " __VA_ARGS__)
-
-/* Forced breakpoint */
-#define BREAKPOINT() do { \
- if (kgdb_enabled) { \
- asm volatile("trapa #0xff"); \
- } \
-} while (0)
+#define BREAKPOINT() asm("trapa #0xff");
+#define BREAK_INSTR_SIZE 2
+#define CACHE_FLUSH_IS_SAFE 1

/* KGDB should be able to flush all kernel text space */
#if defined(CONFIG_CPU_SH4)
@@ -102,30 +47,5 @@ extern int setjmp(jmp_buf __jmpb);
#else
#define kgdb_flush_icache_range(start, end) do { } while (0)
#endif
-
-/* Kernel assert macros */
-#ifdef CONFIG_KGDB_KERNEL_ASSERTS
-
-/* Predefined conditions */
-#define KA_VALID_ERRNO(errno) ((errno) > 0 && (errno) <= EMEDIUMTYPE)
-#define KA_VALID_PTR_ERR(ptr) KA_VALID_ERRNO(-PTR_ERR(ptr))
-#define KA_VALID_KPTR(ptr) (!(ptr) || \
- ((void *)(ptr) >= (void *)PAGE_OFFSET && \
- (void *)(ptr) < ERR_PTR(-EMEDIUMTYPE)))
-#define KA_VALID_PTRORERR(errptr) \
- (KA_VALID_KPTR(errptr) || KA_VALID_PTR_ERR(errptr))
-#define KA_HELD_GKL() (current->lock_depth >= 0)
-
-/* The actual assert */
-#define KGDB_ASSERT(condition, message) do { \
- if (!(condition) && (kgdb_enabled)) { \
- KGDB_PRINTK("Assertion failed at %s:%d: %s\n", \
- __FILE__, __LINE__, message);\
- BREAKPOINT(); \
- } \
-} while (0)
-#else
-#define KGDB_ASSERT(condition, message)
-#endif
-
+#endif /* !__ASSEMBLY__ */
#endif
Index: linux-2.6.14/include/asm-sh/system.h
===================================================================
--- linux-2.6.14.orig/include/asm-sh/system.h
+++ linux-2.6.14/include/asm-sh/system.h
@@ -7,6 +7,7 @@
*/

#include <linux/config.h>
+#include <asm/types.h>

/*
* switch_to() should switch tasks to task nr n, first
@@ -252,6 +253,45 @@ static __inline__ unsigned long __xchg(u
return x;
}

+static inline unsigned long __cmpxchg_u32(volatile int * m, unsigned long old,
+ unsigned long new)
+{
+ __u32 retval;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ retval = *m;
+ if (retval == old)
+ *m = new;
+ local_irq_restore(flags); /* implies memory barrier */
+ return retval;
+}
+
+/* This function doesn't exist, so you'll get a linker error
+ * if something tries to do an invalid cmpxchg(). */
+extern void __cmpxchg_called_with_bad_pointer(void);
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
+ unsigned long new, int size)
+{
+ switch (size) {
+ case 4:
+ return __cmpxchg_u32(ptr, old, new);
+ }
+ __cmpxchg_called_with_bad_pointer();
+ return old;
+}
+
+#define cmpxchg(ptr,o,n) \
+ ({ \
+ __typeof__(*(ptr)) _o_ = (o); \
+ __typeof__(*(ptr)) _n_ = (n); \
+ (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
+ (unsigned long)_n_, sizeof(*(ptr))); \
+ })
+
/* XXX
* disable hlt during certain critical i/o operations
*/
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -170,7 +170,7 @@ config DEBUG_FS

config FRAME_POINTER
bool "Compile the kernel with frame pointers"
- depends on DEBUG_KERNEL && (X86 || CRIS || M68K || M68KNOMMU || FRV || UML)
+ depends on DEBUG_KERNEL && (X86 || CRIS || M68K || M68KNOMMU || FRV || UML || SUPERH)
default y if DEBUG_INFO && UML
help
If you say Y here the resulting kernel image will be slightly larger
@@ -181,13 +181,13 @@ config FRAME_POINTER
config WANT_EXTRA_DEBUG_INFORMATION
bool
select DEBUG_INFO
- select FRAME_POINTER if X86
+ select FRAME_POINTER if X86 || SUPERH
default n

config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- depends on DEBUG_KERNEL && (X86 || MIPS || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS || (SUPERH && !SUPERH64) || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. It is strongly suggested that you enable

--
Tom

2005-11-10 16:43:56

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 10/15] KGDB: ARM-specific changes

This adds a backend, written by Deepak Saxena <[email protected]> and George
Davis <[email protected]> as well as support for the TI OMAP boards, ADI
Coyote, PXA2xx, and ARM Versatile. Geoff Levand <[email protected]>
Nicolas Pitre, and Manish Lachwani have contributed various fixups here as
well. This should only require (on boards that don't have a custom uart)
registering the uart with KGDB to add any other boards, or using kgdboe it
should Just Work.

arch/arm/kernel/Makefile | 1
arch/arm/kernel/kgdb-jmp.S | 30 ++++
arch/arm/kernel/kgdb.c | 208 ++++++++++++++++++++++++++++++++++
arch/arm/kernel/setup.c | 5
arch/arm/kernel/traps.c | 11 +
arch/arm/mach-ixp2000/core.c | 5
arch/arm/mach-ixp2000/ixdp2x01.c | 6
arch/arm/mach-ixp4xx/coyote-setup.c | 5
arch/arm/mach-ixp4xx/ixdp425-setup.c | 12 +
arch/arm/mach-omap1/board-osk.c | 2
arch/arm/mach-omap1/serial.c | 4
arch/arm/mach-pxa/Makefile | 1
arch/arm/mach-pxa/kgdb-serial.c | 98 ++++++++++++++++
arch/arm/mach-versatile/kgdb_serial.c | 121 +++++++++++++++++++
arch/arm/mm/extable.c | 7 +
drivers/serial/amba-pl011.c | 2
include/asm-arm/kgdb.h | 87 ++++++++++++++
include/asm-arm/system.h | 41 ++++++
lib/Kconfig.debug | 2
19 files changed, 642 insertions(+), 6 deletions(-)

Index: linux-2.6.14/arch/arm/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/arm/kernel/kgdb.c
@@ -0,0 +1,208 @@
+/*
+ * arch/arm/kernel/kgdb.c
+ *
+ * ARM KGDB support
+ *
+ * Copyright (c) 2002-2004 MontaVista Software, Inc
+ *
+ * Authors: George Davis <[email protected]>
+ * Deepak Saxena <[email protected]>
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/personality.h>
+#include <linux/ptrace.h>
+#include <linux/elf.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/ptrace.h>
+#include <asm/traps.h>
+
+/* Make a local copy of the registers passed into the handler (bletch) */
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ int regno;
+
+ /* Initialize all to zero (??) */
+ for (regno = 0; regno < GDB_MAX_REGS; regno++)
+ gdb_regs[regno] = 0;
+
+ gdb_regs[_R0] = kernel_regs->ARM_r0;
+ gdb_regs[_R1] = kernel_regs->ARM_r1;
+ gdb_regs[_R2] = kernel_regs->ARM_r2;
+ gdb_regs[_R3] = kernel_regs->ARM_r3;
+ gdb_regs[_R4] = kernel_regs->ARM_r4;
+ gdb_regs[_R5] = kernel_regs->ARM_r5;
+ gdb_regs[_R6] = kernel_regs->ARM_r6;
+ gdb_regs[_R7] = kernel_regs->ARM_r7;
+ gdb_regs[_R8] = kernel_regs->ARM_r8;
+ gdb_regs[_R9] = kernel_regs->ARM_r9;
+ gdb_regs[_R10] = kernel_regs->ARM_r10;
+ gdb_regs[_FP] = kernel_regs->ARM_fp;
+ gdb_regs[_IP] = kernel_regs->ARM_ip;
+ gdb_regs[_SP] = kernel_regs->ARM_sp;
+ gdb_regs[_LR] = kernel_regs->ARM_lr;
+ gdb_regs[_PC] = kernel_regs->ARM_pc;
+ gdb_regs[_CPSR] = kernel_regs->ARM_cpsr;
+}
+
+/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ kernel_regs->ARM_r0 = gdb_regs[_R0];
+ kernel_regs->ARM_r1 = gdb_regs[_R1];
+ kernel_regs->ARM_r2 = gdb_regs[_R2];
+ kernel_regs->ARM_r3 = gdb_regs[_R3];
+ kernel_regs->ARM_r4 = gdb_regs[_R4];
+ kernel_regs->ARM_r5 = gdb_regs[_R5];
+ kernel_regs->ARM_r6 = gdb_regs[_R6];
+ kernel_regs->ARM_r7 = gdb_regs[_R7];
+ kernel_regs->ARM_r8 = gdb_regs[_R8];
+ kernel_regs->ARM_r9 = gdb_regs[_R9];
+ kernel_regs->ARM_r10 = gdb_regs[_R10];
+ kernel_regs->ARM_fp = gdb_regs[_FP];
+ kernel_regs->ARM_ip = gdb_regs[_IP];
+ kernel_regs->ARM_sp = gdb_regs[_SP];
+ kernel_regs->ARM_lr = gdb_regs[_LR];
+ kernel_regs->ARM_pc = gdb_regs[_PC];
+ kernel_regs->ARM_cpsr = gdb_regs[GDB_MAX_REGS - 1];
+}
+
+static inline struct pt_regs *kgdb_get_user_regs(struct task_struct *task)
+{
+ return (struct pt_regs *)
+ ((unsigned long)task->thread_info + THREAD_SIZE -
+ 8 - sizeof(struct pt_regs));
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
+ struct task_struct *task)
+{
+ int regno;
+ struct pt_regs *thread_regs;
+
+ /* Just making sure... */
+ if (task == NULL)
+ return;
+
+ /* Initialize to zero */
+ for (regno = 0; regno < GDB_MAX_REGS; regno++)
+ gdb_regs[regno] = 0;
+
+ /* Otherwise, we have only some registers from switch_to() */
+ thread_regs = kgdb_get_user_regs(task);
+ gdb_regs[_R0] = thread_regs->ARM_r0; /* Not really valid? */
+ gdb_regs[_R1] = thread_regs->ARM_r1; /* " " */
+ gdb_regs[_R2] = thread_regs->ARM_r2; /* " " */
+ gdb_regs[_R3] = thread_regs->ARM_r3; /* " " */
+ gdb_regs[_R4] = thread_regs->ARM_r4;
+ gdb_regs[_R5] = thread_regs->ARM_r5;
+ gdb_regs[_R6] = thread_regs->ARM_r6;
+ gdb_regs[_R7] = thread_regs->ARM_r7;
+ gdb_regs[_R8] = thread_regs->ARM_r8;
+ gdb_regs[_R9] = thread_regs->ARM_r9;
+ gdb_regs[_R10] = thread_regs->ARM_r10;
+ gdb_regs[_FP] = thread_regs->ARM_fp;
+ gdb_regs[_IP] = thread_regs->ARM_ip;
+ gdb_regs[_SP] = thread_regs->ARM_sp;
+ gdb_regs[_LR] = thread_regs->ARM_lr;
+ gdb_regs[_PC] = thread_regs->ARM_pc;
+ gdb_regs[_CPSR] = thread_regs->ARM_cpsr;
+}
+
+static int compiled_break;
+
+int kgdb_arch_handle_exception(int exception_vector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ long addr;
+ char *ptr;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ kgdb_contthread = NULL;
+
+ /*
+ * Try to read optional parameter, pc unchanged if no parm.
+ * If this was a compiled breakpoint, we need to move
+ * to the next instruction or we will just breakpoint
+ * over and over again.
+ */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr)) {
+ linux_regs->ARM_pc = addr;
+ } else if (compiled_break == 1) {
+ linux_regs->ARM_pc += 4;
+ }
+
+ compiled_break = 0;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
+{
+ kgdb_handle_exception(1, SIGTRAP, 0, regs);
+
+ return 0;
+}
+
+static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)
+{
+ compiled_break = 1;
+ kgdb_handle_exception(1, SIGTRAP, 0, regs);
+
+ return 0;
+}
+
+static struct undef_hook kgdb_brkpt_hook = {
+ .instr_mask = 0xffffffff,
+ .instr_val = KGDB_BREAKINST,
+ .fn = kgdb_brk_fn
+};
+
+static struct undef_hook kgdb_compiled_brkpt_hook = {
+ .instr_mask = 0xffffffff,
+ .instr_val = KGDB_COMPILED_BREAK,
+ .fn = kgdb_compiled_brk_fn
+};
+
+/*
+ * Register our undef instruction hooks with ARM undef core.
+ * We regsiter a hook specifically looking for the KGB break inst
+ * and we handle the normal undef case within the do_undefinstr
+ * handler.
+ */
+int kgdb_arch_init(void)
+{
+ register_undef_hook(&kgdb_brkpt_hook);
+ register_undef_hook(&kgdb_compiled_brkpt_hook);
+
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifndef __ARMEB__
+ .gdb_bpt_instr = {0xfe, 0xde, 0xff, 0xe7}
+#else
+ .gdb_bpt_instr = {0xe7, 0xff, 0xde, 0xfe}
+#endif
+};
Index: linux-2.6.14/arch/arm/kernel/kgdb-jmp.S
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/arm/kernel/kgdb-jmp.S
@@ -0,0 +1,30 @@
+/*
+ * arch/arm/kernel/kgdb-jmp.S
+ *
+ * Trivial setjmp and longjmp procedures to support bus error recovery
+ * which may occur during kgdb memory read/write operations.
+ *
+ * Author: MontaVista Software, Inc. <[email protected]>
+ * [email protected]
+ *
+ * 2002-2005 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program as licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+#include <linux/linkage.h>
+
+ENTRY (kgdb_fault_setjmp)
+ /* Save registers */
+ stmia r0, {r0-r14}
+ str lr,[r0, #60]
+ mrs r1,cpsr
+ str r1,[r0,#64]
+ ldr r1,[r0,#4]
+ mov r0, #0
+ mov pc,lr
+
+ENTRY (kgdb_fault_longjmp)
+ /* Restore registers */
+ mov r1,#1
+ str r1,[r0]
+ ldmia r0,{r0-pc}^
Index: linux-2.6.14/arch/arm/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/arm/kernel/Makefile
+++ linux-2.6.14/arch/arm/kernel/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o
obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o

obj-$(CONFIG_IWMMXT) += iwmmxt.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
Index: linux-2.6.14/arch/arm/kernel/setup.c
===================================================================
--- linux-2.6.14.orig/arch/arm/kernel/setup.c
+++ linux-2.6.14/arch/arm/kernel/setup.c
@@ -785,6 +785,11 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con;
#endif
#endif
+
+#if defined(CONFIG_KGDB)
+ extern void __init early_trap_init(void);
+ early_trap_init();
+#endif
}


Index: linux-2.6.14/arch/arm/kernel/traps.c
===================================================================
--- linux-2.6.14.orig/arch/arm/kernel/traps.c
+++ linux-2.6.14/arch/arm/kernel/traps.c
@@ -270,6 +270,7 @@ asmlinkage void do_undefinstr(struct pt_
unsigned int instr;
struct undef_hook *hook;
siginfo_t info;
+ mm_segment_t fs;
void __user *pc;

/*
@@ -279,12 +280,15 @@ asmlinkage void do_undefinstr(struct pt_
*/
regs->ARM_pc -= correction;

+ fs = get_fs();
+ set_fs(KERNEL_DS);
pc = (void __user *)instruction_pointer(regs);
if (thumb_mode(regs)) {
get_user(instr, (u16 __user *)pc);
} else {
get_user(instr, (u32 __user *)pc);
}
+ set_fs(fs);

spin_lock_irq(&undef_lock);
list_for_each_entry(hook, &undef_hook, node) {
@@ -670,6 +674,13 @@ EXPORT_SYMBOL(abort);

void __init trap_init(void)
{
+#if defined(CONFIG_KGDB)
+ return;
+}
+
+void __init early_trap_init(void)
+{
+#endif
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern char __kuser_helper_start[], __kuser_helper_end[];
Index: linux-2.6.14/arch/arm/mach-ixp2000/core.c
===================================================================
--- linux-2.6.14.orig/arch/arm/mach-ixp2000/core.c
+++ linux-2.6.14/arch/arm/mach-ixp2000/core.c
@@ -34,6 +34,7 @@
#include <asm/system.h>
#include <asm/tlbflush.h>
#include <asm/pgtable.h>
+#include <asm/kgdb.h>

#include <asm/mach/map.h>
#include <asm/mach/time.h>
@@ -144,6 +145,10 @@ void __init ixp2000_map_io(void)

iotable_init(ixp2000_io_desc, ARRAY_SIZE(ixp2000_io_desc));

+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &ixp2000_serial_port);
+#endif
+
/* Set slowport to 8-bit mode. */
ixp2000_reg_write(IXP2000_SLOWPORT_FRM, 1);
}
Index: linux-2.6.14/arch/arm/mach-ixp2000/ixdp2x01.c
===================================================================
--- linux-2.6.14.orig/arch/arm/mach-ixp2000/ixdp2x01.c
+++ linux-2.6.14/arch/arm/mach-ixp2000/ixdp2x01.c
@@ -38,6 +38,7 @@
#include <asm/system.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
+#include <asm/kgdb.h>

#include <asm/mach/pci.h>
#include <asm/mach/map.h>
@@ -175,6 +176,11 @@ static void __init ixdp2x01_map_io(void)

early_serial_setup(&ixdp2x01_serial_ports[0]);
early_serial_setup(&ixdp2x01_serial_ports[1]);
+
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &ixdp2x01_serial_ports[0]);
+ kgdb8250_add_port(1, &ixdp2x01_serial_ports[1]);
+#endif
}


Index: linux-2.6.14/arch/arm/mach-ixp4xx/coyote-setup.c
===================================================================
--- linux-2.6.14.orig/arch/arm/mach-ixp4xx/coyote-setup.c
+++ linux-2.6.14/arch/arm/mach-ixp4xx/coyote-setup.c
@@ -91,9 +91,12 @@ static void __init coyote_init(void)
coyote_uart_data[0].irq = IRQ_IXP4XX_UART1;
}

-
ixp4xx_sys_init();
platform_add_devices(coyote_devices, ARRAY_SIZE(coyote_devices));
+
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &coyote_serial_port);
+#endif
}

#ifdef CONFIG_ARCH_ADI_COYOTE
Index: linux-2.6.14/arch/arm/mach-ixp4xx/ixdp425-setup.c
===================================================================
--- linux-2.6.14.orig/arch/arm/mach-ixp4xx/ixdp425-setup.c
+++ linux-2.6.14/arch/arm/mach-ixp4xx/ixdp425-setup.c
@@ -23,6 +23,7 @@
#include <asm/irq.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
+#include <asm/kgdb.h>

static struct flash_platform_data ixdp425_flash_data = {
.map_name = "cfi_probe",
@@ -77,7 +78,8 @@ static struct plat_serial8250_port ixdp4
.mapbase = IXP4XX_UART1_BASE_PHYS,
.membase = (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET,
.irq = IRQ_IXP4XX_UART1,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
+ UPF_SHARE_IRQ,
.iotype = UPIO_MEM,
.regshift = 2,
.uartclk = IXP4XX_UART_XTAL,
@@ -86,7 +88,8 @@ static struct plat_serial8250_port ixdp4
.mapbase = IXP4XX_UART2_BASE_PHYS,
.membase = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
.irq = IRQ_IXP4XX_UART1,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
+ UPF_SHARE_IRQ,
.iotype = UPIO_MEM,
.regshift = 2,
.uartclk = IXP4XX_UART_XTAL,
@@ -121,6 +124,11 @@ static void __init ixdp425_init(void)
}

platform_add_devices(ixdp425_devices, ARRAY_SIZE(ixdp425_devices));
+
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_port(0, &ixdp425_serial_ports[0]);
+ kgdb8250_add_port(1, &ixdp425_serial_ports[1]);
+#endif
}

#ifdef CONFIG_ARCH_IXDP425
Index: linux-2.6.14/arch/arm/mach-omap1/board-osk.c
===================================================================
--- linux-2.6.14.orig/arch/arm/mach-omap1/board-osk.c
+++ linux-2.6.14/arch/arm/mach-omap1/board-osk.c
@@ -46,7 +46,7 @@
#include <asm/arch/tc.h>
#include <asm/arch/common.h>

-static int __initdata osk_serial_ports[OMAP_MAX_NR_PORTS] = {1, 0, 0};
+static int __initdata osk_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1};

static struct mtd_partition osk_partitions[] = {
/* bootloader (U-Boot, etc) in first sector */
Index: linux-2.6.14/arch/arm/mach-omap1/serial.c
===================================================================
--- linux-2.6.14.orig/arch/arm/mach-omap1/serial.c
+++ linux-2.6.14/arch/arm/mach-omap1/serial.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/serial.h>
#include <linux/tty.h>
+#include <linux/kgdb.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>

@@ -194,6 +195,9 @@ void __init omap_serial_init(int ports[O
break;
}
omap_serial_reset(&serial_platform_data[i]);
+#ifdef CONFIG_KGDB_8250
+ kgdb8250_add_platform_port(i, &serial_platform_data[i]);
+#endif
}
}

Index: linux-2.6.14/arch/arm/mach-pxa/kgdb-serial.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/arm/mach-pxa/kgdb-serial.c
@@ -0,0 +1,98 @@
+/*
+ * linux/arch/arm/mach-pxa/kgdb-serial.c
+ *
+ * Provides low level kgdb serial support hooks for PXA2xx boards
+ *
+ * Author: Nicolas Pitre
+ * Copyright: (C) 2002-2005 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/serial_reg.h>
+#include <linux/kgdb.h>
+#include <asm/processor.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#if defined(CONFIG_KGDB_PXA_FFUART)
+
+#define UART FFUART
+#define CKEN_UART CKEN6_FFUART
+#define GPIO_RX_MD GPIO34_FFRXD_MD
+#define GPIO_TX_MD GPIO39_FFTXD_MD
+
+#elif defined(CONFIG_KGDB_PXA_BTUART)
+
+#define UART BTUART
+#define CKEN_UART CKEN7_BTUART
+#define GPIO_RX_MD GPIO42_BTRXD_MD
+#define GPIO_TX_MD GPIO43_BTTXD_MD
+
+#elif defined(CONFIG_KGDB_PXA_STUART)
+
+#define UART STUART
+#define CKEN_UART CKEN5_STUART
+#define GPIO_RX_MD GPIO46_STRXD_MD
+#define GPIO_TX_MD GPIO47_STTXD_MD
+
+#endif
+
+#define UART_BAUDRATE (CONFIG_KGDB_BAUDRATE)
+
+static volatile unsigned long *port = (unsigned long *)&UART;
+
+static int kgdb_serial_init(void)
+{
+ pxa_set_cken(CKEN_UART, 1);
+ pxa_gpio_mode(GPIO_RX_MD);
+ pxa_gpio_mode(GPIO_TX_MD);
+
+ port[UART_IER] = 0;
+ port[UART_LCR] = LCR_DLAB;
+ port[UART_DLL] = ((921600 / UART_BAUDRATE) & 0xff);
+ port[UART_DLM] = ((921600 / UART_BAUDRATE) >> 8);
+ port[UART_LCR] = LCR_WLS1 | LCR_WLS0;
+ port[UART_MCR] = 0;
+ port[UART_IER] = IER_UUE;
+ port[UART_FCR] = FCR_ITL_16;
+
+ return 0;
+}
+
+static void kgdb_serial_putchar(int c)
+{
+ if (!(CKEN & CKEN_UART) || port[UART_IER] != IER_UUE)
+ kgdb_serial_init();
+ while (!(port[UART_LSR] & LSR_TDRQ))
+ cpu_relax();
+ port[UART_TX] = c;
+}
+
+static void kgdb_serial_flush(void)
+{
+ if ((CKEN & CKEN_UART) && (port[UART_IER] & IER_UUE))
+ while (!(port[UART_LSR] & LSR_TEMT))
+ cpu_relax();
+}
+
+static int kgdb_serial_getchar(void)
+{
+ unsigned char c;
+ if (!(CKEN & CKEN_UART) || port[UART_IER] != IER_UUE)
+ kgdb_serial_init();
+ while (!(port[UART_LSR] & UART_LSR_DR))
+ cpu_relax();
+ c = port[UART_RX];
+ return c;
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .init = kgdb_serial_init,
+ .write_char = kgdb_serial_putchar,
+ .flush = kgdb_serial_flush,
+ .read_char = kgdb_serial_getchar,
+};
Index: linux-2.6.14/arch/arm/mach-pxa/Makefile
===================================================================
--- linux-2.6.14.orig/arch/arm/mach-pxa/Makefile
+++ linux-2.6.14/arch/arm/mach-pxa/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_LEDS) += $(led-y)

# Misc features
obj-$(CONFIG_PM) += pm.o sleep.o
+obj-$(CONFIG_KGDB_PXA_SERIAL) += kgdb-serial.o

ifeq ($(CONFIG_PXA27x),y)
obj-$(CONFIG_PM) += standby.o
Index: linux-2.6.14/arch/arm/mach-versatile/kgdb_serial.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/arm/mach-versatile/kgdb_serial.c
@@ -0,0 +1,121 @@
+/*
+ * arch/arm/mach-versatile/kgdb_serial.c
+ *
+ * Author: Manish Lachwani, [email protected]
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * Support for KGDB on ARM Versatile.
+ */
+#include <linux/config.h>
+#include <linux/serial_reg.h>
+#include <linux/kgdb.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/hardware.h>
+#include <asm/hardware/amba_serial.h>
+#include <asm/arch-versatile/hardware.h>
+
+#define ARM_BAUD_38400 23
+/*
+ * Functions that will be used later
+ */
+#define UART_GET_INT_STATUS(p) readb((p) + UART010_IIR)
+#define UART_GET_MIS(p) readw((p) + UART011_MIS)
+#define UART_PUT_ICR(p, c) writel((c), (p) + UART010_ICR)
+#define UART_GET_FR(p) readb((p) + UART01x_FR)
+#define UART_GET_CHAR(p) readb((p) + UART01x_DR)
+#define UART_PUT_CHAR(p, c) writel((c), (p) + UART01x_DR)
+#define UART_GET_RSR(p) readb((p) + UART01x_RSR)
+#define UART_GET_CR(p) readb((p) + UART010_CR)
+#define UART_PUT_CR(p,c) writel((c), (p) + UART010_CR)
+#define UART_GET_LCRL(p) readb((p) + UART010_LCRL)
+#define UART_PUT_LCRL(p,c) writel((c), (p) + UART010_LCRL)
+#define UART_GET_LCRM(p) readb((p) + UART010_LCRM)
+#define UART_PUT_LCRM(p,c) writel((c), (p) + UART010_LCRM)
+#define UART_GET_LCRH(p) readb((p) + UART010_LCRH)
+#define UART_PUT_LCRH(p,c) writel((c), (p) + UART010_LCRH)
+#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0)
+#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0)
+#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART01x_FR_TMSK) == 0)
+
+/*
+ * KGDB IRQ
+ */
+static int kgdb_irq = 12;
+static volatile unsigned char *port = NULL;
+
+static int kgdb_serial_init(void)
+{
+ int rate = ARM_BAUD_38400;
+
+ port = IO_ADDRESS(0x101F1000);
+ UART_PUT_CR(port, 0);
+
+ /* Set baud rate */
+ UART_PUT_LCRM(port, ((rate & 0xf00) >> 8));
+ UART_PUT_LCRL(port, (rate & 0xff));
+ UART_PUT_LCRH(port, UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN);
+ UART_PUT_CR(port, UART01x_CR_UARTEN);
+
+ return 0;
+}
+
+static void kgdb_serial_putchar(int ch)
+{
+ unsigned int status;
+
+ do {
+ status = UART_GET_FR(port);
+ } while (!UART_TX_READY(status));
+
+ UART_PUT_CHAR(port, ch);
+}
+
+static int kgdb_serial_getchar(void)
+{
+ unsigned int status;
+ int ch;
+
+ do {
+ status = UART_GET_FR(port);
+ } while (!UART_RX_DATA(status));
+ ch = UART_GET_CHAR(port);
+ return ch;
+}
+
+static struct uart_port kgdb_amba_port = {
+ .irq = 12,
+ .iobase = 0,
+ .iotype = UPIO_MEM,
+ .membase = (unsigned char *)IO_ADDRESS(0x101F1000),
+};
+
+static irqreturn_t kgdb_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int status = UART_GET_MIS(port);
+
+ if (irq != kgdb_irq)
+ return IRQ_NONE;
+
+ if (status & 0x40)
+ breakpoint();
+
+ return IRQ_HANDLED;
+}
+
+static void __init kgdb_hookup_irq(void)
+{
+ request_irq(kgdb_irq, kgdb_interrupt, SA_SHIRQ, "GDB-stub",
+ &kgdb_amba_port);
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .init = kgdb_serial_init,
+ .write_char = kgdb_serial_putchar,
+ .read_char = kgdb_serial_getchar,
+ .late_init = kgdb_hookup_irq,
+};
Index: linux-2.6.14/arch/arm/mm/extable.c
===================================================================
--- linux-2.6.14.orig/arch/arm/mm/extable.c
+++ linux-2.6.14/arch/arm/mm/extable.c
@@ -2,6 +2,7 @@
* linux/arch/arm/mm/extable.c
*/
#include <linux/module.h>
+#include <linux/kgdb.h>
#include <asm/uaccess.h>

int fixup_exception(struct pt_regs *regs)
@@ -11,6 +12,12 @@ int fixup_exception(struct pt_regs *regs
fixup = search_exception_tables(instruction_pointer(regs));
if (fixup)
regs->ARM_pc = fixup->fixup;
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+#endif

return fixup != NULL;
}
Index: linux-2.6.14/drivers/serial/amba-pl011.c
===================================================================
--- linux-2.6.14.orig/drivers/serial/amba-pl011.c
+++ linux-2.6.14/drivers/serial/amba-pl011.c
@@ -350,7 +350,7 @@ static int pl011_startup(struct uart_por
/*
* Allocate the IRQ
*/
- retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
+ retval = request_irq(uap->port.irq, pl011_int, SA_SHIRQ, "uart-pl011", uap);
if (retval)
goto clk_dis;

Index: linux-2.6.14/include/asm-arm/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-arm/kgdb.h
@@ -0,0 +1,87 @@
+/*
+ * include/asm-arm/kgdb.h
+ *
+ * ARM KGDB support
+ *
+ * Author: Deepak Saxena <[email protected]>
+ *
+ * Copyright (C) 2002 MontaVista Software Inc.
+ *
+ */
+
+#ifndef __ASM_KGDB_H__
+#define __ASM_KGDB_H__
+
+#include <linux/config.h>
+#include <asm/ptrace.h>
+
+
+/*
+ * GDB assumes that we're a user process being debugged, so
+ * it will send us an SWI command to write into memory as the
+ * debug trap. When an SWI occurs, the next instruction addr is
+ * placed into R14_svc before jumping to the vector trap.
+ * This doesn't work for kernel debugging as we are already in SVC
+ * we would loose the kernel's LR, which is a bad thing. This
+ * is bad thing.
+ *
+ * By doing this as an undefined instruction trap, we force a mode
+ * switch from SVC to UND mode, allowing us to save full kernel state.
+ *
+ * We also define a KGDB_COMPILED_BREAK which can be used to compile
+ * in breakpoints. This is important for things like sysrq-G and for
+ * the initial breakpoint from trap_init().
+ *
+ * Note to ARM HW designers: Add real trap support like SH && PPC to
+ * make our lives much much simpler. :)
+ */
+#define BREAK_INSTR_SIZE 4
+#define GDB_BREAKINST 0xef9f0001
+#define KGDB_BREAKINST 0xe7ffdefe
+#define KGDB_COMPILED_BREAK 0xe7ffdeff
+#define CACHE_FLUSH_IS_SAFE 1
+
+#ifndef __ASSEMBLY__
+
+#define BREAKPOINT() asm(".word 0xe7ffdeff")
+
+
+extern void kgdb_handle_bus_error(void);
+extern int kgdb_fault_expected;
+
+/*
+ * From Amit S. Kale:
+ *
+ * In the register packet, words 0-15 are R0 to R10, FP, IP, SP, LR, PC. But
+ * Register 16 isn't cpsr. GDB passes CPSR in word 25. There are 9 words in
+ * between which are unused. Passing only 26 words to gdb is sufficient.
+ * GDB can figure out that floating point registers are not passed.
+ * GDB_MAX_REGS should be 26.
+ */
+#define GDB_MAX_REGS (26)
+
+#define KGDB_MAX_NO_CPUS 1
+#define BUFMAX 400
+#define NUMREGBYTES (GDB_MAX_REGS << 2)
+#define NUMCRITREGBYTES (32 << 2)
+
+#define _R0 0
+#define _R1 1
+#define _R2 2
+#define _R3 3
+#define _R4 4
+#define _R5 5
+#define _R6 6
+#define _R7 7
+#define _R8 8
+#define _R9 9
+#define _R10 10
+#define _FP 11
+#define _IP 12
+#define _SP 13
+#define _LR 14
+#define _PC 15
+#define _CPSR (GDB_MAX_REGS - 1)
+
+#endif /* !__ASSEMBLY__ */
+#endif /* __ASM_KGDB_H__ */
Index: linux-2.6.14/include/asm-arm/system.h
===================================================================
--- linux-2.6.14.orig/include/asm-arm/system.h
+++ linux-2.6.14/include/asm-arm/system.h
@@ -405,6 +405,47 @@ static inline unsigned long __xchg(unsig
return ret;
}

+#define __HAVE_ARCH_CMPXCHG 1
+
+#include <asm/types.h>
+
+static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old,
+ unsigned long new)
+{
+ u32 retval;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ retval = *m;
+ if (retval == old)
+ *m = new;
+ local_irq_restore(flags); /* implies memory barrier */
+
+ return retval;
+}
+
+/* This function doesn't exist, so you'll get a linker error
+ if something tries to do an invalid cmpxchg(). */
+extern void __cmpxchg_called_with_bad_pointer(void);
+
+static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
+ unsigned long new, int size)
+{
+ switch (size) {
+ case 4:
+ return __cmpxchg_u32(ptr, old, new);
+ }
+ __cmpxchg_called_with_bad_pointer();
+ return old;
+}
+
+#define cmpxchg(ptr,o,n) \
+ ({ \
+ __typeof__(*(ptr)) _o_ = (o); \
+ __typeof__(*(ptr)) _n_ = (n); \
+ (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
+ (unsigned long)_n_, sizeof(*(ptr))); \
+ })
#endif /* __ASSEMBLY__ */

#define arch_align_stack(x) (x)
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -187,7 +187,7 @@ config WANT_EXTRA_DEBUG_INFORMATION
config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- depends on DEBUG_KERNEL && (X86 || MIPS || (SUPERH && !SUPERH64) || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (ARM || X86 || MIPS || (SUPERH && !SUPERH64) || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. It is strongly suggested that you enable

--
Tom

2005-11-10 16:44:43

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 12/15] KGDB: netpoll-based ethernet driver

This is the simple KGDB over Ethernet I/O driver that uses netpoll for all of
the heavy lifting. At one point this was very similar to the version Matt
Mackall wrote and is currently in Andrew's tree. Since then it has been
reworked to fit into our model.

drivers/net/Makefile | 1
drivers/net/kgdboe.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/Kconfig.debug | 24 ++++++
3 files changed, 214 insertions(+)

Index: linux-2.6.14/drivers/net/kgdboe.c
===================================================================
--- /dev/null
+++ linux-2.6.14/drivers/net/kgdboe.c
@@ -0,0 +1,189 @@
+/*
+ * drivers/net/kgdboe.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-2005 (c) MontaVista Software, Inc.
+ * 2005 (c) Wind River Systems, Inc.
+ *
+ * Contributors at various stages not listed above:
+ * San Mehat <[email protected]>, Robert Walsh <[email protected]>,
+ * wangdi <[email protected]>, Matt Mackall <[email protected]>,
+ * Pavel Machek <[email protected]>, Jason Wessel <[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/interrupt.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;
+/* 0 = unconfigured, 1 = netpoll options parsed, 2 = fully configured. */
+static int configured;
+static struct kgdb_io local_kgdb_io_ops;
+
+MODULE_DESCRIPTION("KGDB driver for network interfaces");
+MODULE_LICENSE("GPL");
+static char config[256];
+module_param_string(kgdboe, config, 256, 0);
+MODULE_PARM_DESC(kgdboe, " kgdboe=[src-port]@[src-ip]/[dev],"
+ "[tgt-port]@<tgt-ip>/<tgt-macaddr>\n");
+
+static void rx_hook(struct netpoll *np, int port, char *msg, int len)
+{
+ int i;
+
+ np->remote_port = port;
+
+ /*
+ * This could be GDB trying to attach. But it could also be GDB
+ * finishing up a session, with kgdb_connected=0 but GDB sending
+ * an ACK for the final packet. To make sure we don't try and
+ * make a breakpoint when GDB is leaving, make sure that if
+ * !kgdb_connected the only len == 1 packet we allow is ^C.
+ */
+ if (!kgdb_connected && (len != 1 || msg[0] == 3) &&
+ !atomic_read(&kgdb_setting_breakpoint))
+ tasklet_schedule(&kgdb_tasklet_breakpoint);
+
+ for (i = 0; i < len; i++) {
+ if (msg[i] == 3)
+ tasklet_schedule(&kgdb_tasklet_breakpoint);
+
+ if (atomic_read(&in_count) >= IN_BUF_SIZE) {
+ /* buffer overflow, clear it */
+ in_head = in_tail = 0;
+ atomic_set(&in_count, 0);
+ break;
+ }
+ in_buf[in_head++] = msg[i];
+ in_head &= (IN_BUF_SIZE - 1);
+ atomic_inc(&in_count);
+ }
+}
+
+static struct netpoll np = {
+ .name = "kgdboe",
+ .dev_name = "eth0",
+ .rx_hook = rx_hook,
+ .local_port = 6443,
+ .remote_port = 6442,
+ .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+};
+
+static void eth_pre_exception_handler(void)
+{
+ netpoll_set_trap(1);
+}
+
+static void eth_post_exception_handler(void)
+{
+ netpoll_set_trap(0);
+}
+
+static int eth_get_char(void)
+{
+ int chr;
+
+ while (atomic_read(&in_count) == 0)
+ netpoll_poll(&np);
+
+ chr = in_buf[in_tail++];
+ in_tail &= (IN_BUF_SIZE - 1);
+ atomic_dec(&in_count);
+ return chr;
+}
+
+static void eth_flush_buf(void)
+{
+ if (out_count && np.dev) {
+ netpoll_send_udp(&np, out_buf, out_count);
+ memset(out_buf, 0, sizeof(out_buf));
+ out_count = 0;
+ }
+}
+
+static void eth_put_char(u8 chr)
+{
+ out_buf[out_count++] = chr;
+ if (out_count == OUT_BUF_SIZE)
+ eth_flush_buf();
+}
+
+static int option_setup(char *opt)
+{
+ configured = !netpoll_parse_options(&np, opt);
+ return 0;
+}
+__setup("kgdboe=", option_setup);
+
+static int init_kgdboe(void)
+{
+ /* Already done? */
+ if (configured == 2)
+ return 0;
+
+ if (strlen(config))
+ option_setup(config);
+
+ if (!configured) {
+ printk("kgdboe: configuration incorrect - kgdboe not "
+ "loaded.\n");
+ printk(" Usage: kgdboe=[src-port]@[src-ip]/[dev],[tgt-port]"
+ "@<tgt-ip>/<tgt-macaddr>\n");
+ return -EINVAL;
+ }
+
+ if (netpoll_setup(&np)) {
+ printk("kgdboe: netpoll_setup failed kgdboe failed\n");
+ return -EINVAL;
+ }
+
+ if (kgdb_register_io_module(&local_kgdb_io_ops))
+ return -EINVAL;
+
+ printk(KERN_INFO "kgdboe: debugging over ethernet enabled\n");
+
+ configured = 2;
+
+ return 0;
+}
+
+static void cleanup_kgdboe(void)
+{
+ netpoll_cleanup(&np);
+ configured = 0;
+
+ kgdb_unregister_io_module(&local_kgdb_io_ops);
+}
+
+static struct kgdb_io local_kgdb_io_ops = {
+ .read_char = eth_get_char,
+ .write_char = eth_put_char,
+ .init = init_kgdboe,
+ .flush = eth_flush_buf,
+ .pre_exception = eth_pre_exception_handler,
+ .post_exception = eth_post_exception_handler
+};
+
+module_init(init_kgdboe);
+module_exit(cleanup_kgdboe);
Index: linux-2.6.14/drivers/net/Makefile
===================================================================
--- linux-2.6.14.orig/drivers/net/Makefile
+++ linux-2.6.14/drivers/net/Makefile
@@ -201,3 +201,4 @@ obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_ETRAX_ETHERNET) += cris/

obj-$(CONFIG_NETCONSOLE) += netconsole.o
+obj-$(CONFIG_KGDBOE) += kgdboe.o
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -241,6 +241,18 @@ config KGDB_8250_NOMODULE
GDB. This is independent of the normal (SERIAL_8250) driver
for this chipset.

+config KGDBOE_NOMODULE
+ bool "KGDB: On ethernet - in kernel"
+ select KGDBOE
+ select NETPOLL
+ select NETPOLL_TRAP
+ select NETPOLL_RX
+ help
+ Uses the NETPOLL API to communicate with the host GDB via UDP.
+ In order for this to work, the ethernet interface specified must
+ support the NETPOLL API, and this must be initialized at boot.
+ See the documentation for syntax.
+
config KGDB_MPSC
bool "KGDB on MV64x60 MPSC"
depends on SERIAL_MPSC
@@ -260,6 +272,18 @@ config KGDB_SIBYTE
depends on MIPS && SIBYTE_SB1xxx_SOC
endchoice

+config KGDBOE
+ tristate "KGDB: On ethernet" if !KGDBOE_NOMODULE
+ depends on m && KGDB_ONLY_MODULES
+ select NETPOLL
+ select NETPOLL_TRAP
+ select NETPOLL_RX
+ help
+ Uses the NETPOLL API to communicate with the host GDB via UDP.
+ In order for this to work, the ethernet interface specified must
+ support the NETPOLL API, and this must be initialized at boot.
+ See the documentation for syntax.
+
config KGDB_8250
tristate "KGDB: On generic serial port (8250)" if !KGDB_8250_NOMODULE
depends on m && KGDB_ONLY_MODULES

--
Tom

2005-11-10 16:45:09

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 13/15] KGDB: CFI annotations for better unwinding

There are some parts of the kernel where you simply cannot unwind past. This
does not, however, stop GDB from trying, and getting stuck in an infinite loop
(it will get garbage back, try and unwind that, repeat). The way to fix this
is to manually insert some DWARF2 information into the kernel so that GDB will
cleanly (or almost cleanly, it will get the same frame twice, and stop) stop.
George wrote much of the IMHO hard part here (i386 & the macros) and I
expanded this to all of the other arches. This adds a small amount to the
DEBUG_INFO=y kernel size, but that's not unexpected.

Makefile | 6
arch/arm/kernel/entry-armv.S | 2
arch/i386/kernel/entry.S | 111 +++++-
arch/i386/kernel/head.S | 5
arch/i386/kernel/process.c | 20 +
arch/i386/kernel/smpboot.c | 3
arch/x86_64/kernel/entry.S | 2
include/asm-arm/kgdb.h | 7
include/asm-generic/kgdb.h | 34 +
include/asm-i386/kgdb.h | 59 +--
include/asm-mips/kgdb.h | 4
include/asm-ppc/kgdb.h | 1
include/asm-sh/kgdb.h | 1
include/asm-x86_64/kgdb.h | 4
include/linux/dwarf2-lang.h | 300 ++++++++++++++++
include/linux/dwarf2.h | 775 +++++++++++++++++++++++++++++++++++++++++++
scripts/dwarfh.awk | 19 +
17 files changed, 1322 insertions(+), 31 deletions(-)

Index: linux-2.6.14/arch/arm/kernel/entry-armv.S
===================================================================
--- linux-2.6.14.orig/arch/arm/kernel/entry-armv.S
+++ linux-2.6.14/arch/arm/kernel/entry-armv.S
@@ -15,6 +15,7 @@
*/
#include <linux/config.h>

+#include <asm/kgdb.h>
#include <asm/glue.h>
#include <asm/vfpmacros.h>
#include <asm/hardware.h> /* should be moved into entry-macro.S */
@@ -215,6 +216,7 @@ svc_preempt:
beq preempt_return @ go again
b 1b
#endif
+ CFI_END_FRAME(__irq_svc)

.align 5
__und_svc:
Index: linux-2.6.14/arch/i386/kernel/entry.S
===================================================================
--- linux-2.6.14.orig/arch/i386/kernel/entry.S
+++ linux-2.6.14/arch/i386/kernel/entry.S
@@ -123,7 +123,7 @@ VM_MASK = 0x00020000
.previous


-ENTRY(ret_from_fork)
+KPROBE_ENTRY(ret_from_fork)
pushl %eax
call schedule_tail
GET_THREAD_INFO(%ebp)
@@ -470,7 +470,7 @@ ENTRY(simd_coprocessor_error)
pushl $do_simd_coprocessor_error
jmp error_code

-ENTRY(device_not_available)
+KPROBE_ENTRY(device_not_available)
pushl $-1 # mark this as an int
SAVE_ALL
movl %cr0, %eax
@@ -653,7 +653,7 @@ ENTRY(machine_check)
jmp error_code
#endif

-ENTRY(spurious_interrupt_bug)
+KPROBE_ENTRY(spurious_interrupt_bug)
pushl $0
pushl $do_spurious_interrupt_bug
jmp error_code
@@ -661,3 +661,108 @@ ENTRY(spurious_interrupt_bug)
#include "syscall_table.S"

syscall_table_size=(.-sys_call_table)
+
+# Here we do call frames. We cheat a bit as we only really need
+# correct frames at locations we can actually look at from a
+# debugger. Since the break instruction trap actually goes thru
+# some of this code, we don't really need info on those areas, but
+# only after the fact. I.e. if we can not step or break in a
+# location or end up with a return address pointing at the
+# location, we don't need a correct call frame for it.
+
+#ifdef CONFIG_KGDB
+
+#include <linux/dwarf2-lang.h>
+/*
+ * The register numbers as known by gdb
+ */
+
+#define _EAX 0
+#define _ECX 1
+#define _EDX 2
+#define _EBX 3
+#define _ESP 4
+#define _EBP 5
+#define _ESI 6
+#define _EDI 7
+#define _PC 8
+#define _EIP 8
+#define _PS 9
+#define _EFLAGS 9
+#define _CS 10
+#define _SS 11
+#define _DS 12
+#define _ES 13
+#define _FS 14
+#define _GS 15
+ /*
+ * This code uses macros defined in linux/dwarf2-lang.h
+ * They attempt to follow the dwarf2 naming conventions... sort of..
+ */
+ENTRY(end_of_stack_stop_unwind_function)
+ .long end_of_stack_stop_unwind_function+1
+
+ .text
+
+ CFI_preamble(c1,_PC,1,1)
+ CFA_define_reference(_ESP,OLDESP) /* Stack pointer */
+ CFA_expression(_EIP)
+ CFA_exp_OP_dup /* copy old esp */
+ CFA_exp_OP_consts(CS-OLDESP) /* offset to CS address */
+ CFA_exp_OP_plus /* should be CS address */
+ CFA_exp_OP_deref /* get the CS */
+ CFA_exp_OP_const4s(VM_MASK|3) /* prepare to mask it */
+ CFA_exp_OP_and /* mask it, zero means kernel */
+ CFA_exp_OP_bra(eip_user_rtn) /* branch if user */
+ CFA_exp_OP_const4s(EIP-OLDESP) /* offset to return address */
+ CFA_exp_OP_plus /* add that in */
+ CFA_exp_OP_skip(eip_end) /* done if kernel, skip out */
+eip_user_rtn:
+ CFA_exp_OP_addr(end_of_stack_stop_unwind_function)/*dummy function */
+eip_end:
+ CFA_expression_end
+ CFA_define_offset(_EBX,EBX-OLDESP)
+ CFA_define_offset(_ECX,ECX-OLDESP)
+ CFA_define_offset(_EDX,EDX-OLDESP)
+ CFA_define_offset(_ESI,ESI-OLDESP)
+ CFA_define_offset(_EDI,EDI-OLDESP)
+ CFA_define_offset(_EBP,EBP-OLDESP)
+ CFA_define_offset(_EAX,EAX-OLDESP)
+ CFA_define_offset(_EFLAGS,EFLAGS-OLDESP)
+ CFI_postamble()
+
+/*
+ * This provides an uwind for our dummy end of unwind function.
+ * Current convention is to provied an undefined return address.
+ */
+ CFI_preamble(c2,_PC,1,1)
+ CFA_define_reference(_ESP,0) /* Stack pointer */
+ CFA_undefine_reg(_EIP)
+ CFI_postamble()
+
+ FDE_preamble(c2,end_of_stack_stop_unwind_function, \
+ end_of_stack_stop_unwind_function+5)
+ FDE_postamble()
+ /*
+ * This is VERY sloppy. At this point all we want to do is get
+ * the frame right for back tracing. It will not be good if
+ * you try to single step. We use already defined labels.
+ * We want to cover all call outs.
+ * We could also recode this as just one FDE, but this works and
+ * I want to get it out.
+ */
+ FDE_preamble(c1,ret_from_fork,ret_from_exception)
+ CFA_define_cfa_offset(4) /* one extra word on stack */
+ FDE_postamble()
+
+ FDE_preamble(c1,ret_from_exception,device_not_available_emulate)
+ FDE_postamble()
+
+ FDE_preamble(c1,device_not_available_emulate,debug)
+ CFA_define_cfa_offset(4) /* one extra word on stack */
+ FDE_postamble()
+
+ FDE_preamble(c1, debug,spurious_interrupt_bug)
+ FDE_postamble()
+
+#endif
Index: linux-2.6.14/arch/i386/kernel/head.S
===================================================================
--- linux-2.6.14.orig/arch/i386/kernel/head.S
+++ linux-2.6.14/arch/i386/kernel/head.S
@@ -11,6 +11,7 @@
#include <linux/config.h>
#include <linux/threads.h>
#include <linux/linkage.h>
+#include <asm/kgdb.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -332,7 +333,9 @@ is386: movl $2,%ecx # set MP
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.
-
+ /* This dwarf code tells gdb that this is the end of the unwind */
+ /* This uses the CFA set up for pc=1 located in entry.S */
+ CFI_END_FRAME(is386)
/*
* We depend on ET to be correct. This checks for 287/387.
*/
Index: linux-2.6.14/arch/i386/kernel/process.c
===================================================================
--- linux-2.6.14.orig/arch/i386/kernel/process.c
+++ linux-2.6.14/arch/i386/kernel/process.c
@@ -335,7 +335,27 @@ __asm__(".section .text\n"
"call *%ebx\n\t"
"pushl %eax\n\t"
"call do_exit\n"
+ "kernel_thread_helper_end:\n\t"
".previous");
+#ifdef CONFIG_KGDB
+#include <linux/dwarf2-lang.h>
+
+ /* This dwarf code tells gdb that this is the end of the unwind */
+ /* This uses the CFA set up for pc=1 located in entry.S */
+#define _ESP 4
+#define _PC 8
+#define _EIP 8
+__asm__(
+ QUOTE_THIS(
+ CFI_preamble(dwarf_4,_PC,1,1)
+ CFA_define_reference(_ESP,0) /* Stack pointer */
+ CFA_undefine_reg(_EIP)
+ CFI_postamble()
+
+ FDE_preamble(dwarf_4,kernel_thread_helper,kernel_thread_helper_end)
+ FDE_postamble()
+ ));
+#endif

/*
* Create a kernel thread
Index: linux-2.6.14/arch/i386/kernel/smpboot.c
===================================================================
--- linux-2.6.14.orig/arch/i386/kernel/smpboot.c
+++ linux-2.6.14/arch/i386/kernel/smpboot.c
@@ -538,6 +538,9 @@ void __devinit initialize_secondary(void

asm volatile(
"movl %0,%%esp\n\t"
+#ifdef CONFIG_KGDB
+ "pushl end_of_stack_stop_unwind_function\n\t"
+#endif
"jmp *%1"
:
:"r" (current->thread.esp),"r" (current->thread.eip));
Index: linux-2.6.14/arch/x86_64/kernel/entry.S
===================================================================
--- linux-2.6.14.orig/arch/x86_64/kernel/entry.S
+++ linux-2.6.14/arch/x86_64/kernel/entry.S
@@ -41,6 +41,7 @@
#include <asm/unistd.h>
#include <asm/thread_info.h>
#include <asm/hw_irq.h>
+#include <asm/kgdb.h>

.code64

@@ -840,6 +841,7 @@ ENTRY(kernel_thread)
UNFAKE_STACK_FRAME
ret
CFI_ENDPROC
+ CFI_END_FRAME(kernel_thread)


child_rip:
Index: linux-2.6.14/include/asm-arm/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-arm/kgdb.h
+++ linux-2.6.14/include/asm-arm/kgdb.h
@@ -14,6 +14,7 @@

#include <linux/config.h>
#include <asm/ptrace.h>
+#include <asm-generic/kgdb.h>


/*
@@ -48,6 +49,7 @@

extern void kgdb_handle_bus_error(void);
extern int kgdb_fault_expected;
+#endif /* !__ASSEMBLY__ */

/*
* From Amit S. Kale:
@@ -83,5 +85,8 @@ extern int kgdb_fault_expected;
#define _PC 15
#define _CPSR (GDB_MAX_REGS - 1)

-#endif /* !__ASSEMBLY__ */
+/* So that we can denote the end of a frame for tracing, in the simple
+ * case. */
+#define CFI_END_FRAME(func) __CFI_END_FRAME(_PC,_SP,func)
+
#endif /* __ASM_KGDB_H__ */
Index: linux-2.6.14/include/asm-generic/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-generic/kgdb.h
@@ -0,0 +1,34 @@
+/*
+ * include/asm-generic/kgdb.h
+ *
+ * This provides the assembly level information so that KGDB can provide
+ * a GDB that has been patched with enough information to know to stop
+ * trying to unwind the function.
+ *
+ * Author: Tom Rini <[email protected]>
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under the terms
+ * of the GNU General Public License version 2. This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#ifndef __ASM_GENERIC_KGDB_H__
+#define __ASM_GENERIC_KGDB_H__
+
+#include <linux/dwarf2-lang.h>
+#ifdef __ASSEMBLY__
+#ifdef CONFIG_KGDB
+/* This MUST be put at the end of a given assembly function */
+#define __CFI_END_FRAME(pc,sp,func) \
+CAT3(.Lend_,func,:) \
+ CFI_preamble(func,pc,0x1,-DATA_ALIGN_FACTOR) \
+ CFA_define_reference(sp, 0) \
+ CFA_undefine_reg(pc) \
+ CFI_postamble() \
+ FDE_preamble(func,func,CAT3(.Lend,_,func)) \
+ FDE_postamble()
+#else
+#define __CFI_END_FRAME(pc,sp,fn)
+#endif /* CONFIG_KGDB */
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_GENERIC_KGDB_H__ */
Index: linux-2.6.14/include/asm-i386/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-i386/kgdb.h
+++ linux-2.6.14/include/asm-i386/kgdb.h
@@ -6,6 +6,39 @@
* Copyright (C) 2001-2004 Amit S. Kale
*/

+#include <asm-generic/kgdb.h>
+
+/*
+ * Note that this register image is in a different order than
+ * the register image that Linux produces at interrupt time.
+ *
+ * Linux's register image is defined by struct pt_regs in ptrace.h.
+ * Just why GDB uses a different order is a historical mystery.
+ */
+#define _EAX 0
+#define _ECX 1
+#define _EDX 2
+#define _EBX 3
+#define _ESP 4
+#define _EBP 5
+#define _ESI 6
+#define _EDI 7
+#define _PC 8
+#define _EIP 8
+#define _PS 9
+#define _EFLAGS 9
+#define _CS 10
+#define _SS 11
+#define _DS 12
+#define _ES 13
+#define _FS 14
+#define _GS 15
+
+/* So that we can denote the end of a frame for tracing, in the simple
+ * case. */
+#define CFI_END_FRAME(func) __CFI_END_FRAME(_EIP,_ESP,func)
+
+#ifndef __ASSEMBLY__
/************************************************************************/
/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
/* at least NUMREGBYTES*2 are needed for register packets */
@@ -17,33 +50,9 @@
/* Number of bytes of registers we need to save for a setjmp/longjmp. */
#define NUMCRITREGBYTES 24

-/*
- * Note that this register image is in a different order than
- * the register image that Linux produces at interrupt time.
- *
- * Linux's register image is defined by struct pt_regs in ptrace.h.
- * Just why GDB uses a different order is a historical mystery.
- */
-enum regnames { _EAX, /* 0 */
- _ECX, /* 1 */
- _EDX, /* 2 */
- _EBX, /* 3 */
- _ESP, /* 4 */
- _EBP, /* 5 */
- _ESI, /* 6 */
- _EDI, /* 7 */
- _PC, /* 8 also known as eip */
- _PS, /* 9 also known as eflags */
- _CS, /* 10 */
- _SS, /* 11 */
- _DS, /* 12 */
- _ES, /* 13 */
- _FS, /* 14 */
- _GS /* 15 */
-};
-
#define BREAKPOINT() asm(" int $3");
#define BREAK_INSTR_SIZE 1
#define CACHE_FLUSH_IS_SAFE 1
+#endif /* !__ASSEMBLY__ */
#endif /* _ASM_KGDB_H_ */
#endif /* __KERNEL__ */
Index: linux-2.6.14/include/asm-mips/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-mips/kgdb.h
+++ linux-2.6.14/include/asm-mips/kgdb.h
@@ -2,6 +2,9 @@
#ifndef _ASM_KGDB_H_
#define _ASM_KGDB_H_

+#include <asm-generic/kgdb.h>
+
+#ifndef __ASSEMBLY__
#define BUFMAX 2048
#define NUMREGBYTES (90*sizeof(long))
#define NUMCRITREGBYTES (12*sizeof(long))
@@ -17,5 +20,6 @@

extern int kgdb_early_setup;

+#endif /* !__ASSEMBLY__ */
#endif /* _ASM_KGDB_H_ */
#endif /* __KERNEL__ */
Index: linux-2.6.14/include/asm-ppc/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-ppc/kgdb.h
+++ linux-2.6.14/include/asm-ppc/kgdb.h
@@ -12,6 +12,7 @@
#ifndef _PPC_KGDB_H
#define _PPC_KGDB_H

+#include <asm-generic/kgdb.h>
#ifndef __ASSEMBLY__

#define BREAK_INSTR_SIZE 4
Index: linux-2.6.14/include/asm-sh/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-sh/kgdb.h
+++ linux-2.6.14/include/asm-sh/kgdb.h
@@ -14,6 +14,7 @@
#ifndef __KGDB_H
#define __KGDB_H

+#include <asm-generic/kgdb.h>
/* Based on sh-gdb.c from gdb-6.1, Glenn
Engel at HP Ben Lee and Steve Chamberlain */
#define NUMREGBYTES 112 /* 92 */
Index: linux-2.6.14/include/asm-x86_64/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/asm-x86_64/kgdb.h
+++ linux-2.6.14/include/asm-x86_64/kgdb.h
@@ -6,6 +6,8 @@
* Copyright (C) 2001-2004 Amit S. Kale
*/

+#include <asm-generic/kgdb.h>
+
/*
* Note that this register image is in a different order than
* the register image that Linux produces at interrupt time.
@@ -36,6 +38,8 @@
#define NUMREGBYTES ((_PS+1)*8)
#define NUMCRITREGBYTES (8 * 8) /* 8 registers. */

+/* Help GDB to know when to stop backtracing. */
+#define CFI_END_FRAME(func) __CFI_END_FRAME(_PC,_RSP,func)
#ifndef __ASSEMBLY__
/* BUFMAX defines the maximum number of characters in inbound/outbound
* buffers at least NUMREGBYTES*2 are needed for register packets, and
Index: linux-2.6.14/include/linux/dwarf2.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/linux/dwarf2.h
@@ -0,0 +1,775 @@
+/* Declarations and definitions of codes relating to the DWARF2 symbolic
+ debugging information format.
+ Copyright (C) 1992, 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002,
+ 2003 Free Software Foundation, Inc.
+
+ Written by Gary Funck ([email protected]) The Ada Joint Program
+ Office (AJPO), Florida State Unviversity and Silicon Graphics Inc.
+ provided support for this effort -- June 21, 1995.
+
+ Derived from the DWARF 1 implementation written by Ron Guilmette
+ ([email protected]), November 1990.
+
+ This file is part of GCC.
+
+ GCC 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.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/* This file is derived from the DWARF specification (a public document)
+ Revision 2.0.0 (July 27, 1993) developed by the UNIX International
+ Programming Languages Special Interest Group (UI/PLSIG) and distributed
+ by UNIX International. Copies of this specification are available from
+ UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054.
+
+ This file also now contains definitions from the DWARF 3 specification. */
+
+/* This file is shared between GCC and GDB, and should not contain
+ prototypes. */
+
+#ifndef _ELF_DWARF2_H
+#define _ELF_DWARF2_H
+
+/* Structure found in the .debug_line section. */
+typedef struct
+{
+ unsigned char li_length [4];
+ unsigned char li_version [2];
+ unsigned char li_prologue_length [4];
+ unsigned char li_min_insn_length [1];
+ unsigned char li_default_is_stmt [1];
+ unsigned char li_line_base [1];
+ unsigned char li_line_range [1];
+ unsigned char li_opcode_base [1];
+}
+DWARF2_External_LineInfo;
+
+typedef struct
+{
+ unsigned long li_length;
+ unsigned short li_version;
+ unsigned int li_prologue_length;
+ unsigned char li_min_insn_length;
+ unsigned char li_default_is_stmt;
+ int li_line_base;
+ unsigned char li_line_range;
+ unsigned char li_opcode_base;
+}
+DWARF2_Internal_LineInfo;
+
+/* Structure found in .debug_pubnames section. */
+typedef struct
+{
+ unsigned char pn_length [4];
+ unsigned char pn_version [2];
+ unsigned char pn_offset [4];
+ unsigned char pn_size [4];
+}
+DWARF2_External_PubNames;
+
+typedef struct
+{
+ unsigned long pn_length;
+ unsigned short pn_version;
+ unsigned long pn_offset;
+ unsigned long pn_size;
+}
+DWARF2_Internal_PubNames;
+
+/* Structure found in .debug_info section. */
+typedef struct
+{
+ unsigned char cu_length [4];
+ unsigned char cu_version [2];
+ unsigned char cu_abbrev_offset [4];
+ unsigned char cu_pointer_size [1];
+}
+DWARF2_External_CompUnit;
+
+typedef struct
+{
+ unsigned long cu_length;
+ unsigned short cu_version;
+ unsigned long cu_abbrev_offset;
+ unsigned char cu_pointer_size;
+}
+DWARF2_Internal_CompUnit;
+
+typedef struct
+{
+ unsigned char ar_length [4];
+ unsigned char ar_version [2];
+ unsigned char ar_info_offset [4];
+ unsigned char ar_pointer_size [1];
+ unsigned char ar_segment_size [1];
+}
+DWARF2_External_ARange;
+
+typedef struct
+{
+ unsigned long ar_length;
+ unsigned short ar_version;
+ unsigned long ar_info_offset;
+ unsigned char ar_pointer_size;
+ unsigned char ar_segment_size;
+}
+DWARF2_Internal_ARange;
+
+
+/* Tag names and codes. */
+enum dwarf_tag
+ {
+ DW_TAG_padding = 0x00,
+ DW_TAG_array_type = 0x01,
+ DW_TAG_class_type = 0x02,
+ DW_TAG_entry_point = 0x03,
+ DW_TAG_enumeration_type = 0x04,
+ DW_TAG_formal_parameter = 0x05,
+ DW_TAG_imported_declaration = 0x08,
+ DW_TAG_label = 0x0a,
+ DW_TAG_lexical_block = 0x0b,
+ DW_TAG_member = 0x0d,
+ DW_TAG_pointer_type = 0x0f,
+ DW_TAG_reference_type = 0x10,
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_string_type = 0x12,
+ DW_TAG_structure_type = 0x13,
+ DW_TAG_subroutine_type = 0x15,
+ DW_TAG_typedef = 0x16,
+ DW_TAG_union_type = 0x17,
+ DW_TAG_unspecified_parameters = 0x18,
+ DW_TAG_variant = 0x19,
+ DW_TAG_common_block = 0x1a,
+ DW_TAG_common_inclusion = 0x1b,
+ DW_TAG_inheritance = 0x1c,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_module = 0x1e,
+ DW_TAG_ptr_to_member_type = 0x1f,
+ DW_TAG_set_type = 0x20,
+ DW_TAG_subrange_type = 0x21,
+ DW_TAG_with_stmt = 0x22,
+ DW_TAG_access_declaration = 0x23,
+ DW_TAG_base_type = 0x24,
+ DW_TAG_catch_block = 0x25,
+ DW_TAG_const_type = 0x26,
+ DW_TAG_constant = 0x27,
+ DW_TAG_enumerator = 0x28,
+ DW_TAG_file_type = 0x29,
+ DW_TAG_friend = 0x2a,
+ DW_TAG_namelist = 0x2b,
+ DW_TAG_namelist_item = 0x2c,
+ DW_TAG_packed_type = 0x2d,
+ DW_TAG_subprogram = 0x2e,
+ DW_TAG_template_type_param = 0x2f,
+ DW_TAG_template_value_param = 0x30,
+ DW_TAG_thrown_type = 0x31,
+ DW_TAG_try_block = 0x32,
+ DW_TAG_variant_part = 0x33,
+ DW_TAG_variable = 0x34,
+ DW_TAG_volatile_type = 0x35,
+ /* DWARF 3. */
+ DW_TAG_dwarf_procedure = 0x36,
+ DW_TAG_restrict_type = 0x37,
+ DW_TAG_interface_type = 0x38,
+ DW_TAG_namespace = 0x39,
+ DW_TAG_imported_module = 0x3a,
+ DW_TAG_unspecified_type = 0x3b,
+ DW_TAG_partial_unit = 0x3c,
+ DW_TAG_imported_unit = 0x3d,
+ /* SGI/MIPS Extensions. */
+ DW_TAG_MIPS_loop = 0x4081,
+ /* HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz . */
+ DW_TAG_HP_array_descriptor = 0x4090,
+ /* GNU extensions. */
+ DW_TAG_format_label = 0x4101, /* For FORTRAN 77 and Fortran 90. */
+ DW_TAG_function_template = 0x4102, /* For C++. */
+ DW_TAG_class_template = 0x4103, /* For C++. */
+ DW_TAG_GNU_BINCL = 0x4104,
+ DW_TAG_GNU_EINCL = 0x4105,
+ /* Extensions for UPC. See: http://upc.gwu.edu/~upc. */
+ DW_TAG_upc_shared_type = 0x8765,
+ DW_TAG_upc_strict_type = 0x8766,
+ DW_TAG_upc_relaxed_type = 0x8767,
+ /* PGI (STMicroelectronics) extensions. No documentation available. */
+ DW_TAG_PGI_kanji_type = 0xA000,
+ DW_TAG_PGI_interface_block = 0xA020
+ };
+
+#define DW_TAG_lo_user 0x4080
+#define DW_TAG_hi_user 0xffff
+
+/* Flag that tells whether entry has a child or not. */
+#define DW_children_no 0
+#define DW_children_yes 1
+
+/* Form names and codes. */
+enum dwarf_form
+ {
+ DW_FORM_addr = 0x01,
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16
+ };
+
+/* Attribute names and codes. */
+enum dwarf_attribute
+ {
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ DW_AT_ordering = 0x09,
+ DW_AT_subscr_data = 0x0a,
+ DW_AT_byte_size = 0x0b,
+ DW_AT_bit_offset = 0x0c,
+ DW_AT_bit_size = 0x0d,
+ DW_AT_element_list = 0x0f,
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ DW_AT_member = 0x14,
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ DW_AT_producer = 0x25,
+ DW_AT_prototyped = 0x27,
+ DW_AT_return_addr = 0x2a,
+ DW_AT_start_scope = 0x2c,
+ DW_AT_stride_size = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ DW_AT_macro_info = 0x43,
+ DW_AT_namelist_items = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ /* DWARF 3 values. */
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ /* SGI/MIPS extensions. */
+ DW_AT_MIPS_fde = 0x2001,
+ DW_AT_MIPS_loop_begin = 0x2002,
+ DW_AT_MIPS_tail_loop_begin = 0x2003,
+ DW_AT_MIPS_epilog_begin = 0x2004,
+ DW_AT_MIPS_loop_unroll_factor = 0x2005,
+ DW_AT_MIPS_software_pipeline_depth = 0x2006,
+ DW_AT_MIPS_linkage_name = 0x2007,
+ DW_AT_MIPS_stride = 0x2008,
+ DW_AT_MIPS_abstract_name = 0x2009,
+ DW_AT_MIPS_clone_origin = 0x200a,
+ DW_AT_MIPS_has_inlines = 0x200b,
+ /* HP extensions. */
+ DW_AT_HP_block_index = 0x2000,
+ DW_AT_HP_unmodifiable = 0x2001, /* Same as DW_AT_MIPS_fde. */
+ DW_AT_HP_actuals_stmt_list = 0x2010,
+ DW_AT_HP_proc_per_section = 0x2011,
+ DW_AT_HP_raw_data_ptr = 0x2012,
+ DW_AT_HP_pass_by_reference = 0x2013,
+ DW_AT_HP_opt_level = 0x2014,
+ DW_AT_HP_prof_version_id = 0x2015,
+ DW_AT_HP_opt_flags = 0x2016,
+ DW_AT_HP_cold_region_low_pc = 0x2017,
+ DW_AT_HP_cold_region_high_pc = 0x2018,
+ DW_AT_HP_all_variables_modifiable = 0x2019,
+ DW_AT_HP_linkage_name = 0x201a,
+ DW_AT_HP_prof_flags = 0x201b, /* In comp unit of procs_info for -g. */
+ /* GNU extensions. */
+ DW_AT_sf_names = 0x2101,
+ DW_AT_src_info = 0x2102,
+ DW_AT_mac_info = 0x2103,
+ DW_AT_src_coords = 0x2104,
+ DW_AT_body_begin = 0x2105,
+ DW_AT_body_end = 0x2106,
+ DW_AT_GNU_vector = 0x2107,
+ /* VMS extensions. */
+ DW_AT_VMS_rtnbeg_pd_address = 0x2201,
+ /* UPC extension. */
+ DW_AT_upc_threads_scaled = 0x3210,
+ /* PGI (STMicroelectronics) extensions. */
+ DW_AT_PGI_lbase = 0x3a00,
+ DW_AT_PGI_soffset = 0x3a01,
+ DW_AT_PGI_lstride = 0x3a02
+ };
+
+#define DW_AT_lo_user 0x2000 /* Implementation-defined range start. */
+#define DW_AT_hi_user 0x3ff0 /* Implementation-defined range end. */
+
+/* Location atom names and codes. */
+enum dwarf_location_atom
+ {
+ DW_OP_addr = 0x03,
+ DW_OP_deref = 0x06,
+ DW_OP_const1u = 0x08,
+ DW_OP_const1s = 0x09,
+ DW_OP_const2u = 0x0a,
+ DW_OP_const2s = 0x0b,
+ DW_OP_const4u = 0x0c,
+ DW_OP_const4s = 0x0d,
+ DW_OP_const8u = 0x0e,
+ DW_OP_const8s = 0x0f,
+ DW_OP_constu = 0x10,
+ DW_OP_consts = 0x11,
+ DW_OP_dup = 0x12,
+ DW_OP_drop = 0x13,
+ DW_OP_over = 0x14,
+ DW_OP_pick = 0x15,
+ DW_OP_swap = 0x16,
+ DW_OP_rot = 0x17,
+ DW_OP_xderef = 0x18,
+ DW_OP_abs = 0x19,
+ DW_OP_and = 0x1a,
+ DW_OP_div = 0x1b,
+ DW_OP_minus = 0x1c,
+ DW_OP_mod = 0x1d,
+ DW_OP_mul = 0x1e,
+ DW_OP_neg = 0x1f,
+ DW_OP_not = 0x20,
+ DW_OP_or = 0x21,
+ DW_OP_plus = 0x22,
+ DW_OP_plus_uconst = 0x23,
+ DW_OP_shl = 0x24,
+ DW_OP_shr = 0x25,
+ DW_OP_shra = 0x26,
+ DW_OP_xor = 0x27,
+ DW_OP_bra = 0x28,
+ DW_OP_eq = 0x29,
+ DW_OP_ge = 0x2a,
+ DW_OP_gt = 0x2b,
+ DW_OP_le = 0x2c,
+ DW_OP_lt = 0x2d,
+ DW_OP_ne = 0x2e,
+ DW_OP_skip = 0x2f,
+ DW_OP_lit0 = 0x30,
+ DW_OP_lit1 = 0x31,
+ DW_OP_lit2 = 0x32,
+ DW_OP_lit3 = 0x33,
+ DW_OP_lit4 = 0x34,
+ DW_OP_lit5 = 0x35,
+ DW_OP_lit6 = 0x36,
+ DW_OP_lit7 = 0x37,
+ DW_OP_lit8 = 0x38,
+ DW_OP_lit9 = 0x39,
+ DW_OP_lit10 = 0x3a,
+ DW_OP_lit11 = 0x3b,
+ DW_OP_lit12 = 0x3c,
+ DW_OP_lit13 = 0x3d,
+ DW_OP_lit14 = 0x3e,
+ DW_OP_lit15 = 0x3f,
+ DW_OP_lit16 = 0x40,
+ DW_OP_lit17 = 0x41,
+ DW_OP_lit18 = 0x42,
+ DW_OP_lit19 = 0x43,
+ DW_OP_lit20 = 0x44,
+ DW_OP_lit21 = 0x45,
+ DW_OP_lit22 = 0x46,
+ DW_OP_lit23 = 0x47,
+ DW_OP_lit24 = 0x48,
+ DW_OP_lit25 = 0x49,
+ DW_OP_lit26 = 0x4a,
+ DW_OP_lit27 = 0x4b,
+ DW_OP_lit28 = 0x4c,
+ DW_OP_lit29 = 0x4d,
+ DW_OP_lit30 = 0x4e,
+ DW_OP_lit31 = 0x4f,
+ DW_OP_reg0 = 0x50,
+ DW_OP_reg1 = 0x51,
+ DW_OP_reg2 = 0x52,
+ DW_OP_reg3 = 0x53,
+ DW_OP_reg4 = 0x54,
+ DW_OP_reg5 = 0x55,
+ DW_OP_reg6 = 0x56,
+ DW_OP_reg7 = 0x57,
+ DW_OP_reg8 = 0x58,
+ DW_OP_reg9 = 0x59,
+ DW_OP_reg10 = 0x5a,
+ DW_OP_reg11 = 0x5b,
+ DW_OP_reg12 = 0x5c,
+ DW_OP_reg13 = 0x5d,
+ DW_OP_reg14 = 0x5e,
+ DW_OP_reg15 = 0x5f,
+ DW_OP_reg16 = 0x60,
+ DW_OP_reg17 = 0x61,
+ DW_OP_reg18 = 0x62,
+ DW_OP_reg19 = 0x63,
+ DW_OP_reg20 = 0x64,
+ DW_OP_reg21 = 0x65,
+ DW_OP_reg22 = 0x66,
+ DW_OP_reg23 = 0x67,
+ DW_OP_reg24 = 0x68,
+ DW_OP_reg25 = 0x69,
+ DW_OP_reg26 = 0x6a,
+ DW_OP_reg27 = 0x6b,
+ DW_OP_reg28 = 0x6c,
+ DW_OP_reg29 = 0x6d,
+ DW_OP_reg30 = 0x6e,
+ DW_OP_reg31 = 0x6f,
+ DW_OP_breg0 = 0x70,
+ DW_OP_breg1 = 0x71,
+ DW_OP_breg2 = 0x72,
+ DW_OP_breg3 = 0x73,
+ DW_OP_breg4 = 0x74,
+ DW_OP_breg5 = 0x75,
+ DW_OP_breg6 = 0x76,
+ DW_OP_breg7 = 0x77,
+ DW_OP_breg8 = 0x78,
+ DW_OP_breg9 = 0x79,
+ DW_OP_breg10 = 0x7a,
+ DW_OP_breg11 = 0x7b,
+ DW_OP_breg12 = 0x7c,
+ DW_OP_breg13 = 0x7d,
+ DW_OP_breg14 = 0x7e,
+ DW_OP_breg15 = 0x7f,
+ DW_OP_breg16 = 0x80,
+ DW_OP_breg17 = 0x81,
+ DW_OP_breg18 = 0x82,
+ DW_OP_breg19 = 0x83,
+ DW_OP_breg20 = 0x84,
+ DW_OP_breg21 = 0x85,
+ DW_OP_breg22 = 0x86,
+ DW_OP_breg23 = 0x87,
+ DW_OP_breg24 = 0x88,
+ DW_OP_breg25 = 0x89,
+ DW_OP_breg26 = 0x8a,
+ DW_OP_breg27 = 0x8b,
+ DW_OP_breg28 = 0x8c,
+ DW_OP_breg29 = 0x8d,
+ DW_OP_breg30 = 0x8e,
+ DW_OP_breg31 = 0x8f,
+ DW_OP_regx = 0x90,
+ DW_OP_fbreg = 0x91,
+ DW_OP_bregx = 0x92,
+ DW_OP_piece = 0x93,
+ DW_OP_deref_size = 0x94,
+ DW_OP_xderef_size = 0x95,
+ DW_OP_nop = 0x96,
+ /* DWARF 3 extensions. */
+ DW_OP_push_object_address = 0x97,
+ DW_OP_call2 = 0x98,
+ DW_OP_call4 = 0x99,
+ DW_OP_call_ref = 0x9a,
+ /* GNU extensions. */
+ DW_OP_GNU_push_tls_address = 0xe0,
+ /* HP extensions. */
+ DW_OP_HP_unknown = 0xe0, /* Ouch, the same as GNU_push_tls_address. */
+ DW_OP_HP_is_value = 0xe1,
+ DW_OP_HP_fltconst4 = 0xe2,
+ DW_OP_HP_fltconst8 = 0xe3,
+ DW_OP_HP_mod_range = 0xe4,
+ DW_OP_HP_unmod_range = 0xe5,
+ DW_OP_HP_tls = 0xe6
+ };
+
+#define DW_OP_lo_user 0xe0 /* Implementation-defined range start. */
+#define DW_OP_hi_user 0xff /* Implementation-defined range end. */
+
+/* Type encodings. */
+enum dwarf_type
+ {
+ DW_ATE_void = 0x0,
+ DW_ATE_address = 0x1,
+ DW_ATE_boolean = 0x2,
+ DW_ATE_complex_float = 0x3,
+ DW_ATE_float = 0x4,
+ DW_ATE_signed = 0x5,
+ DW_ATE_signed_char = 0x6,
+ DW_ATE_unsigned = 0x7,
+ DW_ATE_unsigned_char = 0x8,
+ /* DWARF 3. */
+ DW_ATE_imaginary_float = 0x9,
+ /* HP extensions. */
+ DW_ATE_HP_float80 = 0x80, /* Floating-point (80 bit). */
+ DW_ATE_HP_complex_float80 = 0x81, /* Complex floating-point (80 bit). */
+ DW_ATE_HP_float128 = 0x82, /* Floating-point (128 bit). */
+ DW_ATE_HP_complex_float128 = 0x83, /* Complex floating-point (128 bit). */
+ DW_ATE_HP_floathpintel = 0x84, /* Floating-point (82 bit IA64). */
+ DW_ATE_HP_imaginary_float80 = 0x85,
+ DW_ATE_HP_imaginary_float128 = 0x86
+ };
+
+#define DW_ATE_lo_user 0x80
+#define DW_ATE_hi_user 0xff
+
+/* Array ordering names and codes. */
+enum dwarf_array_dim_ordering
+ {
+ DW_ORD_row_major = 0,
+ DW_ORD_col_major = 1
+ };
+
+/* Access attribute. */
+enum dwarf_access_attribute
+ {
+ DW_ACCESS_public = 1,
+ DW_ACCESS_protected = 2,
+ DW_ACCESS_private = 3
+ };
+
+/* Visibility. */
+enum dwarf_visibility_attribute
+ {
+ DW_VIS_local = 1,
+ DW_VIS_exported = 2,
+ DW_VIS_qualified = 3
+ };
+
+/* Virtuality. */
+enum dwarf_virtuality_attribute
+ {
+ DW_VIRTUALITY_none = 0,
+ DW_VIRTUALITY_virtual = 1,
+ DW_VIRTUALITY_pure_virtual = 2
+ };
+
+/* Case sensitivity. */
+enum dwarf_id_case
+ {
+ DW_ID_case_sensitive = 0,
+ DW_ID_up_case = 1,
+ DW_ID_down_case = 2,
+ DW_ID_case_insensitive = 3
+ };
+
+/* Calling convention. */
+enum dwarf_calling_convention
+ {
+ DW_CC_normal = 0x1,
+ DW_CC_program = 0x2,
+ DW_CC_nocall = 0x3
+ };
+
+#define DW_CC_lo_user 0x40
+#define DW_CC_hi_user 0xff
+
+/* Inline attribute. */
+enum dwarf_inline_attribute
+ {
+ DW_INL_not_inlined = 0,
+ DW_INL_inlined = 1,
+ DW_INL_declared_not_inlined = 2,
+ DW_INL_declared_inlined = 3
+ };
+
+/* Discriminant lists. */
+enum dwarf_discrim_list
+ {
+ DW_DSC_label = 0,
+ DW_DSC_range = 1
+ };
+
+/* Line number opcodes. */
+enum dwarf_line_number_ops
+ {
+ DW_LNS_extended_op = 0,
+ DW_LNS_copy = 1,
+ DW_LNS_advance_pc = 2,
+ DW_LNS_advance_line = 3,
+ DW_LNS_set_file = 4,
+ DW_LNS_set_column = 5,
+ DW_LNS_negate_stmt = 6,
+ DW_LNS_set_basic_block = 7,
+ DW_LNS_const_add_pc = 8,
+ DW_LNS_fixed_advance_pc = 9,
+ /* DWARF 3. */
+ DW_LNS_set_prologue_end = 10,
+ DW_LNS_set_epilogue_begin = 11,
+ DW_LNS_set_isa = 12
+ };
+
+/* Line number extended opcodes. */
+enum dwarf_line_number_x_ops
+ {
+ DW_LNE_end_sequence = 1,
+ DW_LNE_set_address = 2,
+ DW_LNE_define_file = 3,
+ /* HP extensions. */
+ DW_LNE_HP_negate_is_UV_update = 0x11,
+ DW_LNE_HP_push_context = 0x12,
+ DW_LNE_HP_pop_context = 0x13,
+ DW_LNE_HP_set_file_line_column = 0x14,
+ DW_LNE_HP_set_routine_name = 0x15,
+ DW_LNE_HP_set_sequence = 0x16,
+ DW_LNE_HP_negate_post_semantics = 0x17,
+ DW_LNE_HP_negate_function_exit = 0x18,
+ DW_LNE_HP_negate_front_end_logical = 0x19,
+ DW_LNE_HP_define_proc = 0x20
+ };
+
+/* Call frame information. */
+enum dwarf_call_frame_info
+ {
+ DW_CFA_advance_loc = 0x40,
+ DW_CFA_offset = 0x80,
+ DW_CFA_restore = 0xc0,
+ DW_CFA_nop = 0x00,
+ DW_CFA_set_loc = 0x01,
+ DW_CFA_advance_loc1 = 0x02,
+ DW_CFA_advance_loc2 = 0x03,
+ DW_CFA_advance_loc4 = 0x04,
+ DW_CFA_offset_extended = 0x05,
+ DW_CFA_restore_extended = 0x06,
+ DW_CFA_undefined = 0x07,
+ DW_CFA_same_value = 0x08,
+ DW_CFA_register = 0x09,
+ DW_CFA_remember_state = 0x0a,
+ DW_CFA_restore_state = 0x0b,
+ DW_CFA_def_cfa = 0x0c,
+ DW_CFA_def_cfa_register = 0x0d,
+ DW_CFA_def_cfa_offset = 0x0e,
+ /* DWARF 3. */
+ DW_CFA_def_cfa_expression = 0x0f,
+ DW_CFA_expression = 0x10,
+ DW_CFA_offset_extended_sf = 0x11,
+ DW_CFA_def_cfa_sf = 0x12,
+ DW_CFA_def_cfa_offset_sf = 0x13,
+ /* SGI/MIPS specific. */
+ DW_CFA_MIPS_advance_loc8 = 0x1d,
+ /* GNU extensions. */
+ DW_CFA_GNU_window_save = 0x2d,
+ DW_CFA_GNU_args_size = 0x2e,
+ DW_CFA_GNU_negative_offset_extended = 0x2f
+ };
+
+#define DW_CIE_ID 0xffffffff
+#define DW_CIE_VERSION 1
+
+#define DW_CFA_extended 0
+#define DW_CFA_lo_user 0x1c
+#define DW_CFA_hi_user 0x3f
+
+#define DW_CHILDREN_no 0x00
+#define DW_CHILDREN_yes 0x01
+
+#define DW_ADDR_none 0
+
+/* Source language names and codes. */
+enum dwarf_source_language
+ {
+ DW_LANG_C89 = 0x0001,
+ DW_LANG_C = 0x0002,
+ DW_LANG_Ada83 = 0x0003,
+ DW_LANG_C_plus_plus = 0x0004,
+ DW_LANG_Cobol74 = 0x0005,
+ DW_LANG_Cobol85 = 0x0006,
+ DW_LANG_Fortran77 = 0x0007,
+ DW_LANG_Fortran90 = 0x0008,
+ DW_LANG_Pascal83 = 0x0009,
+ DW_LANG_Modula2 = 0x000a,
+ DW_LANG_Java = 0x000b,
+ /* DWARF 3. */
+ DW_LANG_C99 = 0x000c,
+ DW_LANG_Ada95 = 0x000d,
+ DW_LANG_Fortran95 = 0x000e,
+ /* MIPS. */
+ DW_LANG_Mips_Assembler = 0x8001,
+ /* UPC. */
+ DW_LANG_Upc = 0x8765
+ };
+
+#define DW_LANG_lo_user 0x8000 /* Implementation-defined range start. */
+#define DW_LANG_hi_user 0xffff /* Implementation-defined range start. */
+
+/* Names and codes for macro information. */
+enum dwarf_macinfo_record_type
+ {
+ DW_MACINFO_define = 1,
+ DW_MACINFO_undef = 2,
+ DW_MACINFO_start_file = 3,
+ DW_MACINFO_end_file = 4,
+ DW_MACINFO_vendor_ext = 255
+ };
+
+/* @@@ For use with GNU frame unwind information. */
+
+#define DW_EH_PE_absptr 0x00
+#define DW_EH_PE_omit 0xff
+
+#define DW_EH_PE_uleb128 0x01
+#define DW_EH_PE_udata2 0x02
+#define DW_EH_PE_udata4 0x03
+#define DW_EH_PE_udata8 0x04
+#define DW_EH_PE_sleb128 0x09
+#define DW_EH_PE_sdata2 0x0A
+#define DW_EH_PE_sdata4 0x0B
+#define DW_EH_PE_sdata8 0x0C
+#define DW_EH_PE_signed 0x08
+
+#define DW_EH_PE_pcrel 0x10
+#define DW_EH_PE_textrel 0x20
+#define DW_EH_PE_datarel 0x30
+#define DW_EH_PE_funcrel 0x40
+#define DW_EH_PE_aligned 0x50
+
+#define DW_EH_PE_indirect 0x80
+
+#endif /* _ELF_DWARF2_H */
Index: linux-2.6.14/include/linux/dwarf2-lang.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/linux/dwarf2-lang.h
@@ -0,0 +1,300 @@
+#ifndef DWARF2_LANG
+#define DWARF2_LANG
+
+/*
+ * This 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 file defines macros that allow generation of DWARF debug records
+ * for asm files. This file is platform independent. Register numbers
+ * (which are about the only thing that is platform dependent) are to be
+ * supplied by a platform defined file.
+ */
+/*
+ * We need this to work for both asm and C. In asm we are using the
+ * old comment trick to concatenate while C uses the new ANSI thing.
+ * Here we have concat macro... The multi level thing is to allow and
+ * macros used in the names to be resolved prior to the cat (at which
+ * time they are no longer the same string).
+ */
+#define CAT3(a,b,c) _CAT3(a,b,c)
+#define _CAT3(a,b,c) __CAT3(a,b,c)
+#ifndef __STDC__
+#define __CAT3(a,b,c) a/**/b/**/c
+#else
+#define __CAT3(a,b,c) a##b##c
+#endif
+#ifdef __ASSEMBLY__
+#define IFC(a)
+#define IFN_C(a) a
+#define NL ;
+#define QUOTE_THIS(a) a
+#define DWARF_preamble .section .debug_frame,"",%progbits;
+#else
+#define IFC(a) a
+#define IFN_C(a)
+#define NL \n\t
+#define QUOTE_THIS(a) _QUOTE_THIS(a)
+#define _QUOTE_THIS(a) #a
+/* Don't let CPP see the " and , \042=" \054=, */
+#define DWARF_preamble .section .debug_frame \054\042\042\054%progbits
+#endif
+
+#ifdef CONFIG_64BIT
+#define DATA_ALIGN_FACTOR 8
+#define ADDR_LOC .quad
+#else
+#define DATA_ALIGN_FACTOR 4
+#define ADDR_LOC .long
+#endif
+
+#include <linux/dwarf2-defs.h>
+/*
+ * This macro starts a debug frame section. The debug_frame describes
+ * where to find the registers that the enclosing function saved on
+ * entry.
+ *
+ * ORD is use by the label generator and should be the same as what is
+ * passed to CFI_postamble.
+ *
+ * pc, pc register gdb ordinal.
+ *
+ * code_align this is the factor used to define locations or regions
+ * where the given definitions apply. If you use labels to define these
+ * this should be 1.
+ *
+ * data_align this is the factor used to define register offsets. If
+ * you use struct offset, this should be the size of the register in
+ * bytes or the negative of that. This is how it is used: you will
+ * define a register as the reference register, say the stack pointer,
+ * then you will say where a register is located relative to this
+ * reference registers value, say 40 for register 3 (the gdb register
+ * number). The <40> will be multiplied by <data_align> to define the
+ * byte offset of the given register (3, in this example). So if your
+ * <40> is the byte offset and the reference register points at the
+ * begining, you would want 1 for the data_offset. If <40> was the 40th
+ * 4-byte element in that structure you would want 4. And if your
+ * reference register points at the end of the structure you would want
+ * a negative data_align value(and you would have to do other math as
+ * well).
+ */
+
+#define CFI_preamble(ORD, pc, code_align, data_align) \
+ DWARF_preamble NL \
+ .align DATA_ALIGN_FACTOR NL \
+ .globl CAT3(frame,_,ORD) NL \
+CAT3(frame,_,ORD): NL \
+ .long 7f-6f NL \
+6: \
+ .long DW_CIE_ID NL \
+ .byte DW_CIE_VERSION NL \
+ .byte 0 NL \
+ .uleb128 code_align NL \
+ .sleb128 data_align NL \
+ .byte pc NL
+
+/*
+ * After the above macro and prior to the CFI_postamble, you need to
+ * define the initial state. This starts with defining the reference
+ * register and, usually the pc. Here are some helper macros:
+ */
+
+#define CFA_define_reference(reg, offset) \
+ .byte DW_CFA_def_cfa NL \
+ .uleb128 reg NL \
+ .uleb128 (offset) NL
+
+#define CFA_define_offset(reg, offset) \
+ .byte (DW_CFA_offset + reg) NL \
+ .uleb128 (offset) NL
+
+#define CFA_restore(reg) \
+ .byte (DW_CFA_restore + reg) NL
+
+#define CFI_postamble() \
+ .align DATA_ALIGN_FACTOR NL \
+7: NL \
+.previous NL
+
+/*
+ * So now your code pushs stuff on the stack, you need a new location
+ * and the rules for what to do. This starts a running description of
+ * the call frame. You need to describe what changes with respect to
+ * the call registers as the location of the pc moves through the code.
+ * The following builds an FDE (fram descriptor entry?). Like the
+ * above, it has a preamble and a postamble. It also is tied to the CFI
+ * above.
+ * The preamble macro is tied to the CFI thru the first parameter. The
+ * second is the code start address and then the code end address+1.
+ */
+#define FDE_preamble(ORD, initial_address, end_address) \
+ DWARF_preamble NL \
+ .align DATA_ALIGN_FACTOR NL \
+ .long 9f-8f NL \
+8: \
+ .long CAT3(frame,_,ORD) NL \
+ ADDR_LOC initial_address NL \
+ ADDR_LOC (end_address - initial_address) NL
+
+#define FDE_postamble() \
+ .align DATA_ALIGN_FACTOR NL \
+9: NL \
+.previous NL
+
+/*
+ * That done, you can now add registers, subtract registers, move the
+ * reference and even change the reference. You can also define a new
+ * area of code the info applies to. For discontinuous bits you should
+ * start a new FDE. You may have as many as you like.
+ */
+
+/*
+ * To advance the stack address by <bytes> (0x3f max)
+ */
+
+#define CFA_advance_loc(bytes) \
+ .byte DW_CFA_advance_loc+bytes NL
+
+/*
+ * This one is good for 0xff or 255
+ */
+#define CFA_advance_loc1(bytes) \
+ .byte DW_CFA_advance_loc1 NL \
+ .byte bytes NL
+
+#define CFA_undefine_reg(reg) \
+ .byte DW_CFA_undefined NL \
+ .uleb128 reg NL
+/*
+ * With the above you can define all the register locations. But
+ * suppose the reference register moves... Takes the new offset NOT an
+ * increment. This is how esp is tracked if it is not saved.
+ */
+
+#define CFA_define_cfa_offset(offset) \
+ .byte DW_CFA_def_cfa_offset NL \
+ .uleb128 (offset) NL
+/*
+ * Or suppose you want to use a different reference register...
+ */
+#define CFA_define_cfa_register(reg) \
+ .byte DW_CFA_def_cfa_register NL \
+ .uleb128 reg NL
+
+/*
+ * If you want to mess with the stack pointer, here is the expression.
+ * The stack starts empty.
+ */
+#define CFA_def_cfa_expression \
+ .byte DW_CFA_def_cfa_expression NL \
+ .uleb128 20f-10f NL \
+10: NL
+/*
+ * This expression is to be used for other regs. The stack starts with the
+ * stack address.
+ */
+
+#define CFA_expression(reg) \
+ .byte DW_CFA_expression NL \
+ .uleb128 reg NL \
+ .uleb128 20f-10f NL \
+10: NL
+/*
+ * Here we do the expression stuff. You should code the above followed
+ * by expression OPs followed by CFA_expression_end.
+ */
+
+
+#define CFA_expression_end \
+20: NL
+
+#define CFA_exp_OP_const4s(a) \
+ .byte DW_OP_const4s NL \
+ .long a NL
+
+#define CFA_exp_OP_swap .byte DW_OP_swap NL
+#define CFA_exp_OP_dup .byte DW_OP_dup NL
+#define CFA_exp_OP_drop .byte DW_OP_drop NL
+/*
+ * All these work on the top two elements on the stack, replacing them
+ * with the result. Top comes first where it matters. True is 1, false 0.
+ */
+#define CFA_exp_OP_deref .byte DW_OP_deref NL
+#define CFA_exp_OP_and .byte DW_OP_and NL
+#define CFA_exp_OP_div .byte DW_OP_div NL
+#define CFA_exp_OP_minus .byte DW_OP_minus NL
+#define CFA_exp_OP_mod .byte DW_OP_mod NL
+#define CFA_exp_OP_neg .byte DW_OP_neg NL
+#define CFA_exp_OP_plus .byte DW_OP_plus NL
+#define CFA_exp_OP_not .byte DW_OP_not NL
+#define CFA_exp_OP_or .byte DW_OP_or NL
+#define CFA_exp_OP_xor .byte DW_OP_xor NL
+#define CFA_exp_OP_le .byte DW_OP_le NL
+#define CFA_exp_OP_ge .byte DW_OP_ge NL
+#define CFA_exp_OP_eq .byte DW_OP_eq NL
+#define CFA_exp_OP_lt .byte DW_OP_lt NL
+#define CFA_exp_OP_gt .byte DW_OP_gt NL
+#define CFA_exp_OP_ne .byte DW_OP_ne NL
+/*
+ * These take a parameter as noted
+ */
+/*
+ * Unconditional skip to loc. loc is a label (loc:)
+ */
+#define CFA_exp_OP_skip(loc) \
+ .byte DW_OP_skip NL \
+ .hword loc-.-2 NL
+/*
+ * Conditional skip to loc (TOS != 0, TOS--) (loc is a label)
+ */
+#define CFA_exp_OP_bra(loc) \
+ .byte DW_OP_bra NL \
+ .hword loc-.-2 NL
+
+/*
+ * TOS += no (an unsigned number)
+ */
+#define CFA_exp_OP_plus_uconst(no) \
+ .byte DW_OP_plus_uconst NL \
+ .uleb128 no NL
+
+/*
+ * ++TOS = no (a unsigned number)
+ */
+#define CFA_exp_OP_constu(no) \
+ .byte DW_OP_constu NL \
+ .uleb128 no NL
+/*
+ * ++TOS = no (a signed number)
+ */
+#define CFA_exp_OP_consts(no) \
+ .byte DW_OP_consts NL \
+ .sleb128 no NL
+/*
+ * ++TOS = no (an unsigned byte)
+ */
+#define CFA_exp_OP_const1u(no) \
+ .byte DW_OP_const1u NL \
+ .byte no NL
+
+
+/*
+ * ++TOS = no (a address)
+ */
+#define CFA_exp_OP_addr(no) \
+ .byte DW_OP_addr NL \
+ .long no NL
+
+/*
+ * Push current frames value for "reg" + offset
+ * We take advantage of the opcode assignments to make this a litteral reg
+ * rather than use the DW_OP_bregx opcode.
+ */
+
+#define CFA_exp_OP_breg(reg,offset) \
+ .byte DW_OP_breg0+reg NL \
+ .sleb128 offset NL
+#endif
Index: linux-2.6.14/Makefile
===================================================================
--- linux-2.6.14.orig/Makefile
+++ linux-2.6.14/Makefile
@@ -1316,4 +1316,8 @@ clean := -f $(if $(KBUILD_SRC),$(srctree

endif # skip-makefile

-FORCE:
+include/linux/dwarf2-defs.h: $(srctree)/include/linux/dwarf2.h $(srctree)/scripts/dwarfh.awk
+ mkdir -p include/linux/
+ awk -f $(srctree)/scripts/dwarfh.awk $(srctree)/include/linux/dwarf2.h > include/linux/dwarf2-defs.h
+
+FORCE: include/linux/dwarf2-defs.h
Index: linux-2.6.14/scripts/dwarfh.awk
===================================================================
--- /dev/null
+++ linux-2.6.14/scripts/dwarfh.awk
@@ -0,0 +1,19 @@
+BEGIN {
+ print "#ifndef _ELF_DWARF_H"
+ print "/* Machine generated from dwarf2.h by scripts/dwarfh.awk */"
+}
+$2 == "=" {
+ gsub(/,/, "", $3)
+ print "#define " $1 "\t " $3
+}
+$1 == "#define" {
+ print $0
+ while( index($0,"\\") == length($0)){
+ getline
+ print $0
+ }
+}
+/.*/ {}
+END {
+ print "#endif"
+}

--
Tom

2005-11-10 16:42:55

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 08/15] KGDB: x86_64-specific changes

This adds support for the x86_64 architecture. In addition to what was noted
in the core-lite patch about stuff outside of new files, we add -g0 to
compiling of syscalls.o as otherwise we run into problems when debugging
modules, and like i386 annotate switch_to().

arch/x86_64/Kconfig.debug | 3
arch/x86_64/kernel/Makefile | 1
arch/x86_64/kernel/kgdb-jmp.S | 65 +++++
arch/x86_64/kernel/kgdb.c | 460 ++++++++++++++++++++++++++++++++++++++++++
include/asm-x86_64/kgdb.h | 50 ++++
include/asm-x86_64/system.h | 6
include/linux/kgdb.h | 4
kernel/kgdb.c | 14 +
lib/Kconfig.debug | 2
9 files changed, 599 insertions(+), 6 deletions(-)

Index: linux-2.6.14/arch/x86_64/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/arch/x86_64/Kconfig.debug
+++ linux-2.6.14/arch/x86_64/Kconfig.debug
@@ -51,7 +51,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
Index: linux-2.6.14/arch/x86_64/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/x86_64/kernel/kgdb.c
@@ -0,0 +1,460 @@
+/*
+ *
+ * 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 <[email protected]>
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd.
+ */
+/****************************************************************************
+ * 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 <asm/apicdef.h>
+#include <asm/mach_apic.h>
+#include <asm/kdebug.h>
+#include <asm/debugreg.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;
+}
+
+extern void thread_return(void);
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ gdb_regs[_RAX] = 0;
+ gdb_regs[_RBX] = 0;
+ gdb_regs[_RCX] = 0;
+ gdb_regs[_RDX] = 0;
+ gdb_regs[_RSI] = 0;
+ gdb_regs[_RDI] = 0;
+ gdb_regs[_RBP] = *(unsigned long *)p->thread.rsp;
+ gdb_regs[_PS] = *(unsigned long *)(p->thread.rsp + 8);
+ gdb_regs[_PC] = (unsigned long)&thread_return;
+ gdb_regs[_R8] = 0;
+ gdb_regs[_R9] = 0;
+ gdb_regs[_R10] = 0;
+ gdb_regs[_R11] = 0;
+ gdb_regs[_R12] = 0;
+ gdb_regs[_R13] = 0;
+ gdb_regs[_R14] = 0;
+ gdb_regs[_R15] = 0;
+ gdb_regs[_RSP] = p->thread.rsp;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ regs->rax = gdb_regs[_RAX];
+ regs->rbx = gdb_regs[_RBX];
+ regs->rcx = gdb_regs[_RCX];
+ regs->rdx = gdb_regs[_RDX];
+ regs->rsi = gdb_regs[_RSI];
+ regs->rdi = gdb_regs[_RDI];
+ regs->rbp = gdb_regs[_RBP];
+ regs->eflags = gdb_regs[_PS];
+ regs->rip = gdb_regs[_PC];
+ regs->r8 = gdb_regs[_R8];
+ regs->r9 = gdb_regs[_R9];
+ regs->r10 = gdb_regs[_R10];
+ regs->r11 = gdb_regs[_R11];
+ regs->r12 = gdb_regs[_R12];
+ regs->r13 = gdb_regs[_R13];
+ regs->r14 = gdb_regs[_R14];
+ regs->r15 = gdb_regs[_R15];
+#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 e_vector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_x86_64vector = e_vector;
+ gdb_x86_64errcode = err_code;
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ send_IPI_allbutself(APIC_DM_NMI);
+}
+
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+ char *remcomInBuffer, char *remcomOutBuffer,
+ struct pt_regs *linux_regs)
+{
+ unsigned long addr, length;
+ unsigned long breakno, breaktype;
+ char *ptr;
+ int newPC;
+ unsigned long dr6;
+
+ switch (remcomInBuffer[0]) {
+ case 'c':
+ case 's':
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcomInBuffer[1];
+ if (kgdb_hex2long(&ptr, &addr))
+ linux_regs->rip = addr;
+ newPC = linux_regs->rip;
+
+ /* clear the trace bit */
+ linux_regs->eflags &= ~TF_MASK;
+
+ atomic_set(&cpu_doing_single_step, -1);
+ /* set the trace bit if we're stepping */
+ if (remcomInBuffer[0] == 's') {
+ linux_regs->eflags |= TF_MASK;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,
+ 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;
+}
+
+static struct pt_regs *in_interrupt_stack(unsigned long rsp, int cpu)
+{
+ struct pt_regs *regs;
+ unsigned long end = (unsigned long)cpu_pda[cpu].irqstackptr;
+ if (rsp <= end && rsp >= end - IRQSTACKSIZE + 8) {
+ regs = *(((struct pt_regs **)end) - 1);
+ return regs;
+ }
+ return NULL;
+}
+
+static struct pt_regs *in_exception_stack(unsigned long rsp, int cpu)
+{
+ int i;
+ struct tss_struct *init_tss = &__get_cpu_var(init_tss);
+ for (i = 0; i < N_EXCEPTION_STACKS; i++)
+ if (rsp >= init_tss[cpu].ist[i] &&
+ rsp <= init_tss[cpu].ist[i] + EXCEPTION_STKSZ) {
+ struct pt_regs *r =
+ (void *)init_tss[cpu].ist[i] + EXCEPTION_STKSZ;
+ return r - 1;
+ }
+ return NULL;
+}
+
+void kgdb_shadowinfo(struct pt_regs *regs, char *buffer, unsigned threadid)
+{
+ static char intr_desc[] = "Stack at interrupt entrypoint";
+ static char exc_desc[] = "Stack at exception entrypoint";
+ struct pt_regs *stregs;
+ int cpu = 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 *args = ptr;
+ struct pt_regs *regs = args->regs;
+
+ if (cmd == DIE_PAGE_FAULT_NO_CONTEXT && atomic_read(&debugger_active)
+ && kgdb_may_fault) {
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ return NOTIFY_STOP;
+ /* CPU roundup? */
+ } else if (atomic_read(&debugger_active) && cmd == DIE_NMI_IPI) {
+ kgdb_nmihook(smp_processor_id(), regs);
+ return NOTIFY_STOP;
+ /* See if KGDB is interested. */
+ } else if (cmd == DIE_PAGE_FAULT || user_mode(regs) ||
+ cmd == DIE_NMI_IPI || (cmd == DIE_DEBUG &&
+ atomic_read(&debugger_active)))
+ /* Userpace events, normal watchdog event, or spurious
+ * debug exception. Ignore. */
+ return NOTIFY_DONE;
+
+ kgdb_handle_exception(args->trapnr, args->signr, args->err, regs);
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_notify,
+ .priority = 0x7fffffff, /* we need to notified first */
+};
+
+int kgdb_arch_init(void)
+{
+ 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,
+};
Index: linux-2.6.14/arch/x86_64/kernel/kgdb-jmp.S
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/x86_64/kernel/kgdb-jmp.S
@@ -0,0 +1,65 @@
+/*
+ * arch/x86_64/kernel/kgdb-jmp.S
+ *
+ * Save and restore system registers so that within a limited frame we
+ * may have a fault and "jump back" to a known safe location.
+ *
+ * Author: Tom Rini <[email protected]>
+ *
+ * Cribbed from glibc, which carries the following:
+ * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2005 by MontaVista Software.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include <linux/linkage.h>
+
+#define JB_RBX 0
+#define JB_RBP 1
+#define JB_R12 2
+#define JB_R13 3
+#define JB_R14 4
+#define JB_R15 5
+#define JB_RSP 6
+#define JB_PC 7
+
+ .code64
+
+/* This must be called prior to kgdb_fault_longjmp and
+ * kgdb_fault_longjmp must not be called outside of the context of the
+ * last call to kgdb_fault_setjmp.
+ */
+ENTRY(kgdb_fault_setjmp)
+ /* Save registers. */
+ movq %rbx, (JB_RBX*8)(%rdi)
+ movq %rbp, (JB_RBP*8)(%rdi)
+ movq %r12, (JB_R12*8)(%rdi)
+ movq %r13, (JB_R13*8)(%rdi)
+ movq %r14, (JB_R14*8)(%rdi)
+ movq %r15, (JB_R15*8)(%rdi)
+ leaq 8(%rsp), %rdx /* Save SP as it will be after we return. */
+ movq %rdx, (JB_RSP*8)(%rdi)
+ movq (%rsp), %rax /* Save PC we are returning to now. */
+ movq %rax, (JB_PC*8)(%rdi)
+ /* Set return value for setjmp. */
+ mov $0,%eax
+ movq (JB_PC*8)(%rdi),%rdx
+ movq (JB_RSP*8)(%rdi),%rsp
+ jmpq *%rdx
+
+ENTRY(kgdb_fault_longjmp)
+ /* Restore registers. */
+ movq (JB_RBX*8)(%rdi),%rbx
+ movq (JB_RBP*8)(%rdi),%rbp
+ movq (JB_R12*8)(%rdi),%r12
+ movq (JB_R13*8)(%rdi),%r13
+ movq (JB_R14*8)(%rdi),%r14
+ movq (JB_R15*8)(%rdi),%r15
+ /* Set return value for setjmp. */
+ movq (JB_PC*8)(%rdi),%rdx
+ movq (JB_RSP*8)(%rdi),%rsp
+ mov $1,%eax
+ jmpq *%rdx
Index: linux-2.6.14/arch/x86_64/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/x86_64/kernel/Makefile
+++ linux-2.6.14/arch/x86_64/kernel/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_GART_IOMMU) += pci-gart.o a
obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o pci-dma.o
obj-$(CONFIG_SWIOTLB) += swiotlb.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o
obj-$(CONFIG_X86_PM_TIMER) += pmtimer.o

obj-$(CONFIG_MODULES) += module.o
Index: linux-2.6.14/include/asm-x86_64/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-x86_64/kgdb.h
@@ -0,0 +1,50 @@
+#ifdef __KERNEL__
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+/*
+ * Note that this register image is in a different order than
+ * the register image that Linux produces at interrupt time.
+ *
+ * Linux's register image is defined by struct pt_regs in ptrace.h.
+ * Just why GDB uses a different order is a historical mystery.
+ */
+#define _RAX 0
+#define _RDX 1
+#define _RCX 2
+#define _RBX 3
+#define _RSI 4
+#define _RDI 5
+#define _RBP 6
+#define _RSP 7
+#define _R8 8
+#define _R9 9
+#define _R10 10
+#define _R11 11
+#define _R12 12
+#define _R13 13
+#define _R14 14
+#define _R15 15
+#define _PC 16
+#define _PS 17
+
+/* Number of bytes of registers. */
+#define NUMREGBYTES ((_PS+1)*8)
+#define NUMCRITREGBYTES (8 * 8) /* 8 registers. */
+
+#ifndef __ASSEMBLY__
+/* BUFMAX defines the maximum number of characters in inbound/outbound
+ * buffers at least NUMREGBYTES*2 are needed for register packets, and
+ * a longer buffer is needed to list all threads. */
+#define BUFMAX 1024
+#define BREAKPOINT() asm(" int $3");
+#define CHECK_EXCEPTION_STACK() ((&__get_cpu_var(init_tss))[0].ist[0])
+#define BREAK_INSTR_SIZE 1
+#define CACHE_FLUSH_IS_SAFE 1
+#endif /* !__ASSEMBLY__ */
+#endif /* _ASM_KGDB_H_ */
+#endif /* __KERNEL__ */
Index: linux-2.6.14/include/asm-x86_64/system.h
===================================================================
--- linux-2.6.14.orig/include/asm-x86_64/system.h
+++ linux-2.6.14/include/asm-x86_64/system.h
@@ -27,7 +27,9 @@
,"rcx","rbx","rdx","r8","r9","r10","r11","r12","r13","r14","r15"

#define switch_to(prev,next,last) \
- asm volatile(SAVE_CONTEXT \
+ asm volatile(".globl __switch_to_begin\n\t" \
+ "__switch_to_begin:\n\t" \
+ SAVE_CONTEXT \
"movq %%rsp,%P[threadrsp](%[prev])\n\t" /* save RSP */ \
"movq %P[threadrsp](%[next]),%%rsp\n\t" /* restore RSP */ \
"call __switch_to\n\t" \
@@ -39,6 +41,8 @@
"movq %%rax,%%rdi\n\t" \
"jc ret_from_fork\n\t" \
RESTORE_CONTEXT \
+ ".globl __switch_to_end\n\t" \
+ "__switch_to_end:\n\t" \
: "=a" (last) \
: [next] "S" (next), [prev] "D" (prev), \
[threadrsp] "i" (offsetof(struct task_struct, thread.rsp)), \
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -187,7 +187,7 @@ config WANT_EXTRA_DEBUG_INFORMATION
config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- depends on DEBUG_KERNEL && (X86 || MIPS || IA64 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. It is strongly suggested that you enable
Index: linux-2.6.14/include/linux/kgdb.h
===================================================================
--- linux-2.6.14.orig/include/linux/kgdb.h
+++ linux-2.6.14/include/linux/kgdb.h
@@ -24,6 +24,10 @@
#include <linux/linkage.h>
#include <linux/init.h>

+#ifndef CHECK_EXCEPTION_STACK
+#define CHECK_EXCEPTION_STACK() 1
+#endif
+
struct tasklet_struct;
struct pt_regs;
struct task_struct;
Index: linux-2.6.14/kernel/kgdb.c
===================================================================
--- linux-2.6.14.orig/kernel/kgdb.c
+++ linux-2.6.14/kernel/kgdb.c
@@ -1629,7 +1629,8 @@ void kgdb_unregister_io_module(struct kg
*/
static void kgdb_tasklet_bpt(unsigned long ing)
{
- breakpoint();
+ if(CHECK_EXCEPTION_STACK())
+ breakpoint();
}

DECLARE_TASKLET(kgdb_tasklet_breakpoint, kgdb_tasklet_bpt, 0);
@@ -1640,6 +1641,17 @@ DECLARE_TASKLET(kgdb_tasklet_breakpoint,
*/
static void __init kgdb_early_entry(void)
{
+ /*
+ * Don't try and do anything until the architecture is able to
+ * setup the exception stack. In this case, it is up to the
+ * architecture to hook in and look at us when they are ready.
+ */
+ if(!CHECK_EXCEPTION_STACK()) {
+ kgdb_initialized = -1;
+ tasklet_schedule(&kgdb_tasklet_breakpoint);
+ return;
+ }
+
/* Let the architecture do any setup that it needs to. */
kgdb_arch_init();


--
Tom

2005-11-10 16:44:21

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 11/15] KGDB: ppc64-specific changes

This adds basic KGDB support to ppc64, and support for kgdb8250 on the 'Maple'
board. All of this was done by Frank Rowand (who is on vacation right now,
but I'll try and answer for him). This should work on any ppc64 board via
kgdboe, so long as there is an eth driver that supports netpoll. At the
moment this is mutually exclusive with XMON. It is probably possible to allow
them to be chained, but that sounds dangerous to me. This is similar to
ppc32, but ppc32 does not explicitly test.

arch/ppc64/Kconfig.debug | 14 -
arch/ppc64/kernel/Makefile | 1
arch/ppc64/kernel/kgdb.c | 421 +++++++++++++++++++++++++++++++++++++++++++++
arch/ppc64/kernel/setup.c | 6
arch/ppc64/mm/fault.c | 8
include/asm-ppc64/kgdb.h | 54 +++++
lib/Kconfig.debug | 2
7 files changed, 497 insertions(+), 9 deletions(-)

Index: linux-2.6.14/arch/ppc64/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/arch/ppc64/Kconfig.debug
+++ linux-2.6.14/arch/ppc64/Kconfig.debug
@@ -28,16 +28,9 @@ config DEBUG_STACK_USAGE

This option will slow down process creation somewhat.

-config DEBUGGER
- bool "Enable debugger hooks"
- depends on DEBUG_KERNEL
- help
- Include in-kernel hooks for kernel debuggers. Unless you are
- intending to debug the kernel, say N here.
-
config XMON
bool "Include xmon kernel debugger"
- depends on DEBUGGER && !PPC_ISERIES
+ depends on DEBUG_KERNEL && !PPC_ISERIES
help
Include in-kernel hooks for the xmon kernel monitor/debugger.
Unless you are intending to debug the kernel, say N here.
@@ -55,6 +48,11 @@ config XMON_DEFAULT
xmon is normally disabled unless booted with 'xmon=on'.
Use 'xmon=off' to disable xmon init during runtime.

+config DEBUGGER
+ bool
+ depends on KGDB || XMON
+ default y
+
config PPCDBG
bool "Include PPCDBG realtime debugging"
depends on DEBUG_KERNEL
Index: linux-2.6.14/arch/ppc64/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/ppc64/kernel/kgdb.c
@@ -0,0 +1,421 @@
+/*
+ * arch/ppc64/kernel/kgdb.c
+ *
+ * PowerPC64 backend to the KGDB stub.
+ *
+ * Maintainer: Tom Rini <[email protected]>
+ *
+ * Copied from arch/ppc/kernel/kgdb.c, updated for ppc64
+ *
+ * Copyright (C) 1996 Paul Mackerras (setjmp/longjmp)
+ * 1998 (c) Michael AK Tesch ([email protected])
+ * Copyright (C) 2003 Timesys Corporation.
+ * 2004 (c) MontaVista Software, Inc.
+ * 2005 (c) MontaVista Software, Inc.
+ * PPC64 Mods (C) 2005 Frank Rowand ([email protected])
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+#include <linux/smp.h>
+#include <linux/signal.h>
+#include <linux/ptrace.h>
+#include <asm/current.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+
+/*
+ * This table contains the mapping between PowerPC64 hardware trap types, and
+ * signals, which are primarily what GDB understands. GDB and the kernel
+ * don't always agree on values, so we use constants taken from gdb-6.2.
+ */
+static struct hard_trap_info
+{
+ unsigned int tt; /* Trap type code for powerpc */
+ unsigned char signo; /* Signal that we map this trap into */
+} hard_trap_info[] = {
+ { 0x0100, 0x02 /* SIGINT */ }, /* system reset */
+ { 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */
+ { 0x0300, 0x0b /* SIGSEGV */ }, /* data access */
+ { 0x0380, 0x0b /* SIGSEGV */ }, /* data SLB access */
+ { 0x0400, 0x0a /* SIGBUS */ }, /* instruction access */
+ { 0x0480, 0x0a /* SIGBUS */ }, /* instruction segment */
+ { 0x0500, 0x02 /* SIGINT */ }, /* interrupt */
+ { 0x0600, 0x0a /* SIGBUS */ }, /* alignment */
+ { 0x0700, 0x04 /* SIGILL */ }, /* program */
+ { 0x0800, 0x08 /* SIGFPE */ }, /* fpu unavailable */
+ { 0x0900, 0x0e /* SIGALRM */ }, /* decrementer */
+ { 0x0a00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0b00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0c00, 0x14 /* SIGCHLD */ }, /* syscall */
+ { 0x0d00, 0x05 /* SIGTRAP */ }, /* single step */
+ { 0x0e00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0f00, 0x04 /* SIGILL */ }, /* performance monitor */
+ { 0x0f20, 0x08 /* SIGFPE */ }, /* altivec unavailable */
+ { 0x1300, 0x05 /* SIGTRAP */ }, /* instruction address break */
+ { 0x1500, 0x04 /* SIGILL */ }, /* soft patch */
+ { 0x1600, 0x04 /* SIGILL */ }, /* maintenance */
+ { 0x1700, 0x04 /* SIGILL */ }, /* altivec assist */
+ { 0x1800, 0x04 /* SIGILL */ }, /* thermal */
+ { 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 */
+}
+
+static int kgdb_call_nmi_hook(struct pt_regs *regs)
+{
+ kgdb_nmihook(smp_processor_id(), regs);
+ return 0;
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ smp_send_debugger_break(MSG_ALL_BUT_SELF);
+}
+
+/* KGDB functions to use existing PowerPC64 hooks. */
+static int kgdb_debugger(struct pt_regs *regs)
+{
+ return kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs);
+}
+
+static int kgdb_breakpoint(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, SIGTRAP, 0, regs);
+
+ if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
+ regs->nip += 4;
+
+ return 1;
+}
+
+static int kgdb_singlestep(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, SIGTRAP, 0, regs);
+ return 1;
+}
+
+int kgdb_iabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs);
+ return 1;
+}
+
+int kgdb_dabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs);
+ return 1;
+}
+
+#define PACK64(ptr,src) do { *(ptr++) = (src); } while(0)
+
+#define PACK32(ptr,src) do { \
+ u32 *ptr32; \
+ ptr32 = (u32 *)ptr; \
+ *(ptr32++) = (src); \
+ ptr = (unsigned long *)ptr32; \
+ } while(0)
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ memset(gdb_regs, 0, NUMREGBYTES);
+
+ for (reg = 0; reg < 32; reg++)
+ PACK64(ptr, regs->gpr[reg]);
+
+ /* fp registers not used by kernel, leave zero */
+ ptr += 32;
+
+ PACK64(ptr, regs->nip);
+ PACK64(ptr, regs->msr);
+ PACK32(ptr, regs->ccr);
+ PACK64(ptr, regs->link);
+ PACK64(ptr, regs->ctr);
+ PACK32(ptr, regs->xer);
+
+#if 0
+ Following are in struct thread_struct, not struct pt_regs,
+ ignoring for now since kernel does not use them. Would it
+ make sense to get them from the thread that kgdb is set to?
+
+ If this code is enabled, update the definition of NUMREGBYTES to
+ include the vector registers and vector state registers.
+
+ PACK32(ptr, p->thread->fpscr);
+
+ /* vr registers not used by kernel, leave zero */
+ ptr += 64;
+
+ PACK32(ptr, p->thread->vscr);
+ PACK32(ptr, p->thread->vrsave);
+#else
+ /* fpscr not used by kernel, leave zero */
+ PACK32(ptr, 0);
+#endif
+
+ BUG_ON((unsigned long)ptr >
+ (unsigned long)(((void *)gdb_regs) + NUMREGBYTES));
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ struct pt_regs *regs = (struct pt_regs *)(p->thread.ksp +
+ STACK_FRAME_OVERHEAD);
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ memset(gdb_regs, 0, NUMREGBYTES);
+
+ /* Regs GPR0-2 */
+ for (reg = 0; reg < 3; reg++)
+ PACK64(ptr, regs->gpr[reg]);
+
+ /* Regs GPR3-13 are caller saved, not in regs->gpr[] */
+ for (reg = 3; reg < 14; reg++)
+ PACK64(ptr, 0);
+
+ /* Regs GPR14-31 */
+ for (reg = 14; reg < 32; reg++)
+ PACK64(ptr, regs->gpr[reg]);
+
+ /* fp registers not used by kernel, leave zero */
+ ptr += 32;
+
+ PACK64(ptr, regs->nip);
+ PACK64(ptr, regs->msr);
+ PACK32(ptr, regs->ccr);
+ PACK64(ptr, regs->link);
+ PACK64(ptr, regs->ctr);
+ PACK32(ptr, regs->xer);
+
+#if 0
+ Following are in struct thread_struct, not struct pt_regs,
+ ignoring for now since kernel does not use them. Would it
+ make sense to get them from the thread that kgdb is set to?
+
+ If this code is enabled, update the definition of NUMREGBYTES to
+ include the vector registers and vector state registers.
+
+ PACK32(ptr, p->thread->fpscr);
+
+ /* vr registers not used by kernel, leave zero */
+ ptr += 64;
+
+ PACK32(ptr, p->thread->vscr);
+ PACK32(ptr, p->thread->vrsave);
+#else
+ /* fpscr not used by kernel, leave zero */
+ PACK32(ptr, 0);
+#endif
+
+ BUG_ON((unsigned long)ptr >
+ (unsigned long)(((void *)gdb_regs) + NUMREGBYTES));
+}
+
+#define UNPACK64(dest,ptr) do { dest = *(ptr++); } while(0)
+
+#define UNPACK32(dest,ptr) do { \
+ u32 *ptr32; \
+ ptr32 = (u32 *)ptr; \
+ dest = *(ptr32++); \
+ ptr = (unsigned long *)ptr32; \
+ } while(0)
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ UNPACK64(regs->gpr[reg], ptr);
+
+ /* fp registers not used by kernel, leave zero */
+ ptr += 32;
+
+ UNPACK64(regs->nip, ptr);
+ UNPACK64(regs->msr, ptr);
+ UNPACK32(regs->ccr, ptr);
+ UNPACK64(regs->link, ptr);
+ UNPACK64(regs->ctr, ptr);
+ UNPACK32(regs->xer, ptr);
+
+#if 0
+ Following are in struct thread_struct, not struct pt_regs,
+ ignoring for now since kernel does not use them. Would it
+ make sense to get them from the thread that kgdb is set to?
+
+ If this code is enabled, update the definition of NUMREGBYTES to
+ include the vector registers and vector state registers.
+
+ /* fpscr, vscr, vrsave not used by kernel, leave unchanged */
+
+ UNPACK32(p->thread->fpscr, ptr);
+
+ /* vr registers not used by kernel, leave zero */
+ ptr += 64;
+
+ UNPACK32(p->thread->vscr, ptr);
+ UNPACK32(p->thread->vrsave, ptr);
+#endif
+
+ BUG_ON((unsigned long)ptr >
+ (unsigned long)(((void *)gdb_regs) + NUMREGBYTES));
+}
+
+/*
+ * This function does PowerPC64 specific procesing for interfacing to gdb.
+ */
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ char *ptr = &remcom_in_buffer[1];
+ unsigned long addr;
+
+ switch (remcom_in_buffer[0]) {
+ /*
+ * sAA..AA Step one instruction from AA..AA
+ * This will return an error to gdb ..
+ */
+ case 's':
+ case 'c':
+ /* handle the optional parameter */
+ if (kgdb_hex2long(&ptr, &addr))
+ linux_regs->nip = addr;
+
+ atomic_set(&cpu_doing_single_step, -1);
+ /* set the trace bit if we're stepping */
+ if (remcom_in_buffer[0] == 's') {
+ linux_regs->msr |= MSR_SE;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,
+ smp_processor_id());
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+int kgdb_fault_setjmp(unsigned long *curr_context)
+{
+ __asm__ __volatile__("mflr 0; std 0,0(%0)\n\
+ std 1,8(%0)\n\
+ std 2,16(%0)\n\
+ mfcr 0; std 0,24(%0)\n\
+ std 13,32(%0)\n\
+ std 14,40(%0)\n\
+ std 15,48(%0)\n\
+ std 16,56(%0)\n\
+ std 17,64(%0)\n\
+ std 18,72(%0)\n\
+ std 19,80(%0)\n\
+ std 20,88(%0)\n\
+ std 21,96(%0)\n\
+ std 22,104(%0)\n\
+ std 23,112(%0)\n\
+ std 24,120(%0)\n\
+ std 25,128(%0)\n\
+ std 26,136(%0)\n\
+ std 27,144(%0)\n\
+ std 28,152(%0)\n\
+ std 29,160(%0)\n\
+ std 30,168(%0)\n\
+ std 31,176(%0)\n" : : "r" (curr_context));
+ return 0;
+}
+
+void kgdb_fault_longjmp(unsigned long *curr_context)
+{
+ __asm__ __volatile__("ld 13,32(%0)\n\
+ ld 14,40(%0)\n\
+ ld 15,48(%0)\n\
+ ld 16,56(%0)\n\
+ ld 17,64(%0)\n\
+ ld 18,72(%0)\n\
+ ld 19,80(%0)\n\
+ ld 20,88(%0)\n\
+ ld 21,96(%0)\n\
+ ld 22,104(%0)\n\
+ ld 23,112(%0)\n\
+ ld 24,120(%0)\n\
+ ld 25,128(%0)\n\
+ ld 26,136(%0)\n\
+ ld 27,144(%0)\n\
+ ld 28,152(%0)\n\
+ ld 29,160(%0)\n\
+ ld 30,168(%0)\n\
+ ld 31,176(%0)\n\
+ ld 0,24(%0)\n\
+ mtcrf 0x38,0\n\
+ ld 0,0(%0)\n\
+ ld 1,8(%0)\n\
+ ld 2,16(%0)\n\
+ mtlr 0\n\
+ mr 3,1\n" : : "r" (curr_context));
+}
+
+/*
+ * Global data
+ */
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0x7d, 0x82, 0x10, 0x08},
+};
+
+int kgdb_not_implemented(struct pt_regs *regs)
+{
+ return 0;
+}
+
+int kgdb_arch_init(void)
+{
+#ifdef CONFIG_XMON
+#error Both XMON and KGDB selected in .config. Unselect one of them.
+#endif
+
+ __debugger_ipi = kgdb_call_nmi_hook;
+ __debugger = kgdb_debugger;
+ __debugger_bpt = kgdb_breakpoint;
+ __debugger_sstep = kgdb_singlestep;
+ __debugger_iabr_match = kgdb_iabr_match;
+ __debugger_dabr_match = kgdb_dabr_match;
+ __debugger_fault_handler = kgdb_not_implemented;
+
+ return 0;
+}
+
+arch_initcall(kgdb_arch_init);
Index: linux-2.6.14/arch/ppc64/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/ppc64/kernel/Makefile
+++ linux-2.6.14/arch/ppc64/kernel/Makefile
@@ -56,6 +56,7 @@ vio-obj-$(CONFIG_PPC_ISERIES) += iSeries
obj-$(CONFIG_IBMVIO) += vio.o $(vio-obj-y)
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_MPIC) += mpic.o
+obj-$(CONFIG_KGDB) += kgdb.o

obj-$(CONFIG_PPC_PMAC) += pmac_setup.o pmac_feature.o pmac_pci.o \
pmac_time.o pmac_nvram.o pmac_low_i2c.o \
Index: linux-2.6.14/arch/ppc64/kernel/setup.c
===================================================================
--- linux-2.6.14.orig/arch/ppc64/kernel/setup.c
+++ linux-2.6.14/arch/ppc64/kernel/setup.c
@@ -33,6 +33,7 @@
#include <linux/unistd.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
+#include <linux/kgdb.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/processor.h>
@@ -1219,6 +1220,11 @@ void __init generic_find_legacy_serial_p
serial_ports[index].irq,
serial_ports[index].uartclk);

+#ifdef CONFIG_KGDB_8250
+ /* Tell KGDB about this. */
+ kgdb8250_add_platform_port(index, &serial_ports[index]);
+#endif
+
/* Get phys address of IO reg for port 1 */
if (index != 0)
goto next_port;
Index: linux-2.6.14/arch/ppc64/mm/fault.c
===================================================================
--- linux-2.6.14.orig/arch/ppc64/mm/fault.c
+++ linux-2.6.14/arch/ppc64/mm/fault.c
@@ -30,6 +30,7 @@
#include <linux/smp_lock.h>
#include <linux/module.h>
#include <linux/kprobes.h>
+#include <linux/kgdb.h>

#include <asm/page.h>
#include <asm/pgtable.h>
@@ -327,6 +328,13 @@ void bad_page_fault(struct pt_regs *regs
regs->nip = entry->fixup;
return;
}
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault) {
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+ }
+#endif

/* kernel has accessed a bad area */
die("Kernel access of bad area", regs, sig);
Index: linux-2.6.14/include/asm-ppc64/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-ppc64/kgdb.h
@@ -0,0 +1,54 @@
+/*
+ * kgdb.h: Defines and declarations for serial line source level
+ * remote debugging of the Linux kernel using gdb.
+ *
+ * copied from include/asm-ppc, modified for ppc64
+ *
+ * PPC64 Mods (C) 2005 Frank Rowand ([email protected])
+ * 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])
+ */
+#ifdef __KERNEL__
+#ifndef _PPC64_KGDB_H
+#define _PPC64_KGDB_H
+
+#ifndef __ASSEMBLY__
+
+#define BREAK_INSTR_SIZE 4
+#if 1
+/*
+ * Does not include vector registers and vector state registers.
+ *
+ * 64 bit (8 byte) registers:
+ * 32 gpr, 32 fpr, nip, msr, link, ctr
+ * 32 bit (4 byte) registers:
+ * ccr, xer, fpscr
+ */
+#define NUMREGBYTES ((68 * 8) + (3 * 4))
+#else
+/*
+ * Includes vector registers and vector state registers.
+ *
+ * 128 bit (16 byte) registers:
+ * 32 vr
+ * 64 bit (8 byte) registers:
+ * 32 gpr, 32 fpr, nip, msr, link, ctr
+ * 32 bit (4 byte) registers:
+ * ccr, xer, fpscr, vscr, vrsave
+ */
+#define NUMREGBYTES ((128 * 16) + (68 * 8) + (5 * 4))
+#endif
+#define NUMCRITREGBYTES 184
+#define BUFMAX ((NUMREGBYTES * 2) + 512)
+#define OUTBUFMAX ((NUMREGBYTES * 2) + 512)
+#define BREAKPOINT() asm(".long 0x7d821008"); /* twge r2, r2 */
+#define CACHE_FLUSH_IS_SAFE 1
+
+#endif /* !(__ASSEMBLY__) */
+
+#endif /* !(_PPC64_KGDB_H) */
+
+#endif /* __KERNEL__ */
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -187,7 +187,7 @@ config WANT_EXTRA_DEBUG_INFORMATION
config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- depends on DEBUG_KERNEL && (ARM || X86 || MIPS || (SUPERH && !SUPERH64) || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (ARM || X86 || MIPS || PPC64 || (SUPERH && !SUPERH64) || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. It is strongly suggested that you enable

--
Tom

2005-11-10 16:42:31

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 07/15] x86_64: Add a notifier hook to the "no context" part of do_page_fault

This adds a call to notify_die() in the "no context" portion of
do_page_fault() as someone on the chain might care and want to do a fixup.

arch/x86_64/mm/fault.c | 4 ++++
include/asm-x86_64/kdebug.h | 1 +
2 files changed, 5 insertions(+)

Index: linux-2.6.14/arch/x86_64/mm/fault.c
===================================================================
--- linux-2.6.14.orig/arch/x86_64/mm/fault.c
+++ linux-2.6.14/arch/x86_64/mm/fault.c
@@ -518,6 +518,10 @@ no_context:
if (is_errata93(regs, address))
return;

+ if (notify_die(DIE_PAGE_FAULT_NO_CONTEXT, "no context", regs,
+ error_code, 14, SIGSEGV) == NOTIFY_STOP)
+ return;
+
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
Index: linux-2.6.14/include/asm-x86_64/kdebug.h
===================================================================
--- linux-2.6.14.orig/include/asm-x86_64/kdebug.h
+++ linux-2.6.14/include/asm-x86_64/kdebug.h
@@ -33,6 +33,7 @@ enum die_val {
DIE_CALL,
DIE_NMI_IPI,
DIE_PAGE_FAULT,
+ DIE_PAGE_FAULT_NO_CONTEXT,
};

static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig)

--
Tom

2005-11-10 16:41:47

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 05/15] KGDB: MIPS-specific changes

This adds basic support to the MIPS architecture as well as support
specifically for the MIPS Malta (via 8250) and SiByte 1250-SWARM
(32 and 64bit, custom uart), all from Manish Lachwani. This looks like it
should work on anything with an 8250-compatible uart, and be fairly easy
to convert other boards with custom uarts. This also adds the base functions
for debugging into the serial_txx9 driver, from Ralf.

arch/mips/Kconfig.debug | 19
arch/mips/kernel/Makefile | 3
arch/mips/kernel/gdb-low.S | 370 ----------
arch/mips/kernel/gdb-stub.c | 1091 ------------------------------
arch/mips/kernel/irq.c | 32
arch/mips/kernel/kgdb-jmp.c | 116 +++
arch/mips/kernel/kgdb-setjmp.S | 27
arch/mips/kernel/kgdb.c | 296 ++++++++
arch/mips/kernel/kgdb_handler.S | 58 +
arch/mips/kernel/traps.c | 26
arch/mips/mips-boards/generic/Makefile | 1
arch/mips/mips-boards/generic/init.c | 62 -
arch/mips/mips-boards/malta/malta_setup.c | 8
arch/mips/mm/extable.c | 7
arch/mips/sibyte/cfe/setup.c | 14
arch/mips/sibyte/sb1250/Makefile | 1
arch/mips/sibyte/sb1250/irq.c | 57 -
arch/mips/sibyte/sb1250/irq_handler.S | 2
arch/mips/sibyte/sb1250/kgdb_sibyte.c | 164 ++++
arch/mips/sibyte/swarm/Makefile | 2
arch/mips/sibyte/swarm/dbg_io.c | 76 --
drivers/serial/serial_txx9.c | 90 ++
include/asm-mips/kdebug.h | 47 +
include/asm-mips/kgdb.h | 21
lib/Kconfig.debug | 7
25 files changed, 880 insertions(+), 1717 deletions(-)

Index: linux-2.6.14/arch/mips/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/arch/mips/Kconfig.debug
+++ linux-2.6.14/arch/mips/Kconfig.debug
@@ -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
Index: linux-2.6.14/arch/mips/kernel/gdb-low.S
===================================================================
--- linux-2.6.14.orig/arch/mips/kernel/gdb-low.S
+++ /dev/null
@@ -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_32BIT
-#define DMFC0 mfc0
-#define DMTC0 mtc0
-#define LDC1 lwc1
-#define SDC1 lwc1
-#endif
-#ifdef CONFIG_64BIT
-#define DMFC0 dmfc0
-#define DMTC0 dmtc0
-#define LDC1 ldc1
-#define SDC1 ldc1
-#endif
-
-/*
- * [jsun] We reserves about 2x GDB_FR_SIZE in stack. The lower (addressed)
- * part is used to store registers and passed to exception handler.
- * The upper part is reserved for "call func" feature where gdb client
- * saves some of the regs, setups call frame and passes args.
- *
- * A trace shows about 200 bytes are used to store about half of all regs.
- * The rest should be big enough for frame setup and passing args.
- */
-
-/*
- * The low level trap handler
- */
- .align 5
- NESTED(trap_low, GDB_FR_SIZE, sp)
- .set noat
- .set noreorder
-
- mfc0 k0, CP0_STATUS
- sll k0, 3 /* extract cu0 bit */
- bltz k0, 1f
- move k1, sp
-
- /*
- * Called from user mode, go somewhere else.
- */
- 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
Index: linux-2.6.14/arch/mips/kernel/gdb-stub.c
===================================================================
--- linux-2.6.14.orig/arch/mips/kernel/gdb-stub.c
+++ /dev/null
@@ -1,1091 +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);
-
-int kgdb_enabled;
-
-/*
- * 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
Index: linux-2.6.14/arch/mips/kernel/irq.c
===================================================================
--- linux-2.6.14.orig/arch/mips/kernel/irq.c
+++ linux-2.6.14/arch/mips/kernel/irq.c
@@ -26,6 +26,10 @@
#include <asm/atomic.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+#include <asm/kgdb.h>
+
+/* Keep track of if we've done certain initialization already or not. */
+int kgdb_early_setup;

/*
* 'what should we do if we get a hw irq event on an illegal vector'.
@@ -103,23 +107,13 @@ skip:
return 0;
}

-#ifdef CONFIG_KGDB
-extern void breakpoint(void);
-extern void set_debug_traps(void);
-
-static int kgdb_flag = 1;
-static int __init nokgdb(char *str)
-{
- kgdb_flag = 0;
- return 1;
-}
-__setup("nokgdb", nokgdb);
-#endif
-
void __init init_IRQ(void)
{
int i;

+ if (kgdb_early_setup)
+ return;
+
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = NULL;
@@ -129,12 +123,12 @@ void __init init_IRQ(void)
}

arch_init_irq();
-
#ifdef CONFIG_KGDB
- if (kgdb_flag) {
- printk("Wait for gdb client connection ...\n");
- set_debug_traps();
- breakpoint();
- }
+ /*
+ * We have been called before kgdb_arch_init(). Hence,
+ * we dont want the traps to be reinitialized
+ */
+ if (kgdb_early_setup == 0)
+ kgdb_early_setup = 1;
#endif
}
Index: linux-2.6.14/arch/mips/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/mips/kernel/kgdb.c
@@ -0,0 +1,296 @@
+/*
+ * 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-2005 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 <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <linux/kgdb.h>
+#include <linux/init.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 */
+/* { 11, SIGILL }, */ /* CPU unusable */
+ { 12, SIGFPE }, /* overflow */
+ { 13, SIGTRAP }, /* trap */
+ { 14, SIGSEGV }, /* virtual instruction cache coherency */
+ { 15, SIGFPE }, /* floating point exception */
+ { 23, SIGSEGV }, /* watch */
+ { 31, SIGSEGV }, /* virtual data cache coherency */
+ { 0, 0} /* Must be last */
+};
+
+/* Save the normal trap handlers for user-mode traps. */
+void *saved_vectors[32];
+
+extern void trap_low(void);
+extern void breakinst(void);
+extern void init_IRQ(void);
+
+void kgdb_call_nmi_hook(void *ignored)
+{
+ kgdb_nmihook(smp_processor_id(), (void *)0);
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ local_irq_restore(flags);
+ smp_call_function(kgdb_call_nmi_hook, 0, 0, 0);
+ local_irq_save(flags);
+}
+
+static int compute_signal(int tt)
+{
+ struct hard_trap_info *ht;
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ if (ht->tt == tt)
+ return ht->signo;
+
+ return SIGHUP; /* default for things we don't know about */
+}
+
+/*
+ * Set up exception handlers for tracing and breakpoints
+ */
+void handle_exception(struct pt_regs *regs)
+{
+ int trap = (regs->cp0_cause & 0x7c) >> 2;
+
+ if (fixup_exception(regs)) {
+ return;
+ }
+
+ if (atomic_read(&debugger_active))
+ kgdb_nmihook(smp_processor_id(), regs);
+
+ if (atomic_read(&kgdb_setting_breakpoint))
+ if ((trap == 9) && (regs->cp0_epc == (unsigned long)breakinst))
+ regs->cp0_epc += 4;
+
+ kgdb_handle_exception(0, compute_signal(trap), 0, regs);
+
+ /* In SMP mode, __flush_cache_all does IPI */
+ __flush_cache_all();
+}
+
+void set_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low);
+
+ local_irq_restore(flags);
+}
+
+#if 0
+/* This should be called before we exit kgdb_handle_exception() I believe.
+ * -- Tom
+ */
+void restore_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ set_except_vector(ht->tt, saved_vectors[ht->tt]);
+ local_irq_restore(flags);
+}
+#endif
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg = 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;
+
+ /* See if KGDB is interested. */
+ if (user_mode(regs))
+ /* Userpace events, ignore. */
+ return NOTIFY_DONE;
+
+ kgdb_handle_exception(trap, compute_signal(trap), 0, regs);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_mips_notify,
+};
+
+/*
+ * Handle the 's' and 'c' commands
+ */
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ struct pt_regs *regs)
+{
+ char *ptr;
+ unsigned long address;
+ int cpu = smp_processor_id();
+
+ switch (remcom_in_buffer[0]) {
+ case 's':
+ case 'c':
+ /* handle the optional parameter */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &address))
+ regs->cp0_epc = address;
+
+ atomic_set(&cpu_doing_single_step, -1);
+ if (remcom_in_buffer[0] == 's')
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step, cpu);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ .gdb_bpt_instr = {0xd},
+#else
+ .gdb_bpt_instr = {0x00, 0x00, 0x00, 0x0d},
+#endif
+};
+
+/*
+ * We use kgdb_early_setup so that functions we need to call now don't
+ * cause trouble when called again later.
+ */
+int kgdb_arch_init(void)
+{
+ /* Board-specifics. */
+ /* Force some calls to happen earlier. */
+ if (kgdb_early_setup == 0) {
+ trap_init();
+ init_IRQ();
+ kgdb_early_setup = 1;
+ }
+
+ /* Set our traps. */
+ /* This needs to be done more finely grained again, paired in
+ * a before/after in kgdb_handle_exception(...) -- Tom */
+ set_debug_traps();
+ notifier_chain_register(&mips_die_chain, &kgdb_notifier);
+
+ return 0;
+}
Index: linux-2.6.14/arch/mips/kernel/kgdb_handler.S
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/mips/kernel/kgdb_handler.S
@@ -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)
Index: linux-2.6.14/arch/mips/kernel/kgdb-jmp.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/mips/kernel/kgdb-jmp.c
@@ -0,0 +1,116 @@
+/*
+ * arch/mips/kernel/kgdb-jmp.c
+ *
+ * Save and restore system registers so that within a limited frame we
+ * may have a fault and "jump back" to a known safe location.
+ *
+ * Author: Tom Rini <[email protected]>
+ * Author: Manish Lachwani <[email protected]>
+ *
+ * Cribbed from glibc, which carries the following:
+ * Copyright (C) 1996, 1997, 2000, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2005 by MontaVista Software.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include <linux/kgdb.h>
+#include <asm/interrupt.h>
+
+#ifdef CONFIG_MIPS64
+/*
+ * MIPS 64-bit
+ */
+
+int kgdb_fault_setjmp_aux(unsigned long *curr_context, int sp, int fp)
+{
+ __asm__ __volatile__ ("sd $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__ ("sd $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__ ("sd $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__ ("sd $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__ ("sd $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__ ("sd $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__ ("sd $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__ ("sd $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__ ("sd $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__ ("sd $31, %0" : : "m" (curr_context[9]));
+ curr_context[10] = (long *)sp;
+ curr_context[11] = (long *)fp;
+
+ return 0;
+}
+
+void kgdb_fault_longjmp(unsigned long *curr_context)
+{
+ unsigned long sp_val, fp_val;
+
+ __asm__ __volatile__ ("ld $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__ ("ld $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__ ("ld $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__ ("ld $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__ ("ld $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__ ("ld $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__ ("ld $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__ ("ld $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__ ("ld $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__ ("ld $25, %0" : : "m" (curr_context[9]));
+ sp_val = curr_context[10];
+ fp_val = curr_context[11];
+ __asm__ __volatile__ ("ld $29, %0\n\t"
+ "ld $30, %1\n\t" : : "m" (sp_val), "m" (fp_val));
+
+ __asm__ __volatile__ ("dli $2, 1");
+ __asm__ __volatile__ ("j $25");
+
+ for (;;);
+}
+#else
+/*
+ * MIPS 32-bit
+ */
+
+int kgdb_fault_setjmp_aux(unsigned long *curr_context, int sp, int fp)
+{
+ __asm__ __volatile__("sw $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__("sw $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__("sw $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__("sw $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__("sw $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__("sw $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__("sw $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__("sw $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__("sw $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__("sw $31, %0" : : "m" (curr_context[9]));
+ curr_context[10] = (long *)sp;
+ curr_context[11] = (long *)fp;
+
+ return 0;
+}
+
+void kgdb_fault_longjmp(unsigned long *curr_context)
+{
+ unsigned long sp_val, fp_val;
+
+ __asm__ __volatile__("lw $gp, %0" : : "m" (curr_context[0]));
+ __asm__ __volatile__("lw $16, %0" : : "m" (curr_context[1]));
+ __asm__ __volatile__("lw $17, %0" : : "m" (curr_context[2]));
+ __asm__ __volatile__("lw $18, %0" : : "m" (curr_context[3]));
+ __asm__ __volatile__("lw $19, %0" : : "m" (curr_context[4]));
+ __asm__ __volatile__("lw $20, %0" : : "m" (curr_context[5]));
+ __asm__ __volatile__("lw $21, %0" : : "m" (curr_context[6]));
+ __asm__ __volatile__("lw $22, %0" : : "m" (curr_context[7]));
+ __asm__ __volatile__("lw $23, %0" : : "m" (curr_context[8]));
+ __asm__ __volatile__("lw $25, %0" : : "m" (curr_context[9]));
+ sp_val = curr_context[10];
+ fp_val = curr_context[11];
+ __asm__ __volatile__("lw $29, %0\n\t"
+ "lw $30, %1\n\t" : : "m" (sp_val), "m" (fp_val));
+
+ __asm__ __volatile__("li $2, 1");
+ __asm__ __volatile__("jr $25");
+
+ for (;;);
+}
+#endif
Index: linux-2.6.14/arch/mips/kernel/kgdb-setjmp.S
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/mips/kernel/kgdb-setjmp.S
@@ -0,0 +1,27 @@
+/*
+ * arch/mips/kernel/kgdb-jmp.c
+ *
+ * Save and restore system registers so that within a limited frame we
+ * may have a fault and "jump back" to a known safe location.
+ *
+ * Copyright (C) 2005 by MontaVista Software.
+ * Author: Manish Lachwani ([email protected])
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program as licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+ENTRY (kgdb_fault_setjmp)
+ move a1, sp
+ move a2, fp
+#ifdef CONFIG_MIPS64
+ nop
+#endif
+ j kgdb_fault_setjmp_aux
+ .end kgdb_fault_setjmp
Index: linux-2.6.14/arch/mips/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/mips/kernel/Makefile
+++ linux-2.6.14/arch/mips/kernel/Makefile
@@ -52,7 +52,8 @@ 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 kgdb-jmp.o \
+ kgdb-setjmp.o
obj-$(CONFIG_PROC_FS) += proc.o

obj-$(CONFIG_64BIT) += cpu-bugs64.o
Index: linux-2.6.14/arch/mips/kernel/traps.c
===================================================================
--- linux-2.6.14.orig/arch/mips/kernel/traps.c
+++ linux-2.6.14/arch/mips/kernel/traps.c
@@ -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/kgdb.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_tlbm(void);
extern asmlinkage void handle_tlbl(void);
@@ -69,6 +73,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 ...
@@ -271,8 +290,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[];
@@ -958,6 +979,9 @@ void __init trap_init(void)
extern char except_vec4;
unsigned long i;

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

/*
Index: linux-2.6.14/arch/mips/mips-boards/generic/init.c
===================================================================
--- linux-2.6.14.orig/arch/mips/mips-boards/generic/init.c
+++ linux-2.6.14/arch/mips/mips-boards/generic/init.c
@@ -35,15 +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);
-#endif
-
int prom_argc;
int *_prom_argv, *_prom_envp;

@@ -170,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++);
- }
-
- kgdb_enabled = 1;
- /* Breakpoint is invoked after interrupts are initialised */
- }
-}
-#endif
-
void __init prom_init(void)
{
prom_argc = fw_arg0;
Index: linux-2.6.14/arch/mips/mips-boards/generic/Makefile
===================================================================
--- linux-2.6.14.orig/arch/mips/mips-boards/generic/Makefile
+++ linux-2.6.14/arch/mips/mips-boards/generic/Makefile
@@ -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)
Index: linux-2.6.14/arch/mips/mips-boards/malta/malta_setup.c
===================================================================
--- linux-2.6.14.orig/arch/mips/mips-boards/malta/malta_setup.c
+++ linux-2.6.14/arch/mips/mips-boards/malta/malta_setup.c
@@ -48,10 +48,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 },
@@ -124,10 +120,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)) {
Index: linux-2.6.14/arch/mips/mm/extable.c
===================================================================
--- linux-2.6.14.orig/arch/mips/mm/extable.c
+++ linux-2.6.14/arch/mips/mm/extable.c
@@ -3,6 +3,7 @@
*/
#include <linux/module.h>
#include <linux/spinlock.h>
+#include <linux/kgdb.h>
#include <asm/branch.h>
#include <asm/uaccess.h>

@@ -16,6 +17,12 @@ int fixup_exception(struct pt_regs *regs

return 1;
}
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ /* Restore our previous state. */
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+#endif

return 0;
}
Index: linux-2.6.14/arch/mips/sibyte/cfe/setup.c
===================================================================
--- linux-2.6.14.orig/arch/mips/sibyte/cfe/setup.c
+++ linux-2.6.14/arch/mips/sibyte/cfe/setup.c
@@ -58,10 +58,6 @@ int cfe_cons_handle;
extern unsigned long initrd_start, initrd_end;
#endif

-#ifdef CONFIG_KGDB
-extern int kgdb_port;
-#endif
-
static void ATTRIB_NORET cfe_linux_exit(void *arg)
{
int warm = *(int *)arg;
@@ -242,9 +238,6 @@ void __init prom_init(void)
int argc = fw_arg0;
char **envp = (char **) fw_arg2;
int *prom_vec = (int *) fw_arg3;
-#ifdef CONFIG_KGDB
- char *arg;
-#endif

_machine_restart = cfe_linux_restart;
_machine_halt = cfe_linux_halt;
@@ -308,13 +301,6 @@ void __init prom_init(void)
}
}

-#ifdef CONFIG_KGDB
- if ((arg = strstr(arcs_cmdline,"kgdb=duart")) != NULL)
- kgdb_port = (arg[10] == '0') ? 0 : 1;
- else
- kgdb_port = 1;
-#endif
-
#ifdef CONFIG_BLK_DEV_INITRD
{
char *ptr;
Index: linux-2.6.14/arch/mips/sibyte/sb1250/irq.c
===================================================================
--- linux-2.6.14.orig/arch/mips/sibyte/sb1250/irq.c
+++ linux-2.6.14/arch/mips/sibyte/sb1250/irq.c
@@ -31,6 +31,7 @@
#include <asm/system.h>
#include <asm/ptrace.h>
#include <asm/io.h>
+#include <asm/kgdb.h>

#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_int.h>
@@ -60,16 +61,6 @@ static void sb1250_set_affinity(unsigned
extern unsigned long ldt_eoi_space;
#endif

-#ifdef CONFIG_KGDB
-static int kgdb_irq;
-
-/* Default to UART1 */
-int kgdb_port = 1;
-#ifdef CONFIG_SIBYTE_SB1250_DUART
-extern char sb1250_duart_present[];
-#endif
-#endif
-
static struct hw_interrupt_type sb1250_irq_type = {
"SB1250-IMR",
startup_sb1250_irq,
@@ -338,6 +329,11 @@ void __init arch_init_irq(void)
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;

+#ifdef CONFIG_KGDB
+ if (kgdb_early_setup)
+ return;
+#endif
+
/* Default everything to IP2 */
for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */
bus_writeq(IMR_IP2_VAL,
@@ -387,45 +383,4 @@ void __init arch_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 */
- bus_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port)));
-
- sb1250_steal_irq(kgdb_irq);
- bus_writeq(IMR_IP6_VAL,
- IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
- (kgdb_irq<<3)));
- sb1250_unmask_irq(0, kgdb_irq);
- }
-#endif
}
-
-#ifdef CONFIG_KGDB
-
-#include <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 */
Index: linux-2.6.14/arch/mips/sibyte/sb1250/irq_handler.S
===================================================================
--- linux-2.6.14.orig/arch/mips/sibyte/sb1250/irq_handler.S
+++ linux-2.6.14/arch/mips/sibyte/sb1250/irq_handler.S
@@ -102,7 +102,7 @@
2:
#endif

-#ifdef CONFIG_KGDB
+#ifdef CONFIG_KGDB_SIBYTE
/* KGDB (uart 1) interrupt is routed to IP[6] */
andi t1, s0, CAUSEF_IP6
beqz t1, 1f
Index: linux-2.6.14/arch/mips/sibyte/sb1250/kgdb_sibyte.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/mips/sibyte/sb1250/kgdb_sibyte.c
@@ -0,0 +1,164 @@
+/*
+ * 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);
+
+/* Forward declarations. */
+static void kgdbsibyte_init_duart(void);
+static int kgdb_init_io(void);
+
+#define IMR_IP6_VAL K_INT_MAP_I4
+#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+
+static void kgdb_swarm_write_char(int c)
+{
+ while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0) ;
+ duart_out(R_DUART_TX_HOLD, c);
+}
+
+static int kgdb_swarm_read_char(void)
+{
+ int ret_char;
+ unsigned int status;
+
+ status = duart_in(R_DUART_STATUS);
+ while ((status & M_DUART_RX_RDY) == 0) {
+ status = duart_in(R_DUART_STATUS);
+ }
+
+ /*
+ * Check for framing error
+ */
+ if (status & M_DUART_FRM_ERR) {
+ kgdbsibyte_init_duart();
+ kgdb_swarm_write_char('-');
+ return '-';
+ }
+
+ ret_char = duart_in(R_DUART_RX_HOLD);
+
+ return ret_char;
+}
+
+void sb1250_kgdb_interrupt(struct pt_regs *regs)
+{
+ int kgdb_irq = K_INT_UART_0 + kgdb_port;
+ /*
+ * Clear break-change status (allow some time for the remote
+ * host to stop the break, since we would see another
+ * interrupt on the end-of-break too)
+ */
+ kstat_this_cpu.irqs[kgdb_irq]++;
+ mdelay(500);
+ duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
+ M_DUART_RX_EN | M_DUART_TX_EN);
+ if (kgdb_io_ops.init != kgdb_init_io) {
+ /* Throw away the data if another I/O routine is
+ * active.
+ */
+ unsigned int status;
+
+ status = duart_in(R_DUART_STATUS);
+ while ((status & M_DUART_RX_RDY) == 0) {
+ status = duart_in(R_DUART_STATUS);
+ }
+ /*
+ * Check for framing error
+ */
+ if (status & M_DUART_FRM_ERR) {
+ kgdbsibyte_init_duart();
+ }
+ duart_in(R_DUART_RX_HOLD);
+ } else
+ breakpoint();
+
+}
+
+/*
+ * We use port #1 and we set it for 115200 BAUD, 8n1.
+ */
+static void kgdbsibyte_init_duart(void)
+{
+ /* Set 8n1. */
+ duart_out(R_DUART_MODE_REG_1,
+ V_DUART_BITS_PER_CHAR_8 | V_DUART_PARITY_MODE_NONE);
+ duart_out(R_DUART_MODE_REG_2, M_DUART_STOP_BIT_LEN_1);
+ /* Set baud rate of 115200. */
+ duart_out(R_DUART_CLK_SEL, V_DUART_BAUD_RATE(115200));
+ /* Enable rx and tx */
+ duart_out(R_DUART_CMD, M_DUART_RX_EN | M_DUART_TX_EN);
+}
+
+static int kgdb_init_io(void)
+{
+#ifdef CONFIG_SIBYTE_SB1250_DUART
+ sb1250_duart_present[kgdb_port] = 0;
+#endif
+
+ kgdbsibyte_init_duart();
+
+ return 0;
+}
+
+/*
+ * Hookup our IRQ line. We will already have been initialized a
+ * this point.
+ */
+static void __init kgdbsibyte_hookup_irq(void)
+{
+ /* Steal the IRQ. */
+ kgdb_irq = K_INT_UART_0 + kgdb_port;
+
+ /* Setup uart 1 settings, mapper */
+ __raw_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port)));
+
+ sb1250_steal_irq(kgdb_irq);
+
+ __raw_writeq(IMR_IP6_VAL,
+ IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+ (kgdb_irq << 3)));
+
+ sb1250_unmask_irq(0, kgdb_irq);
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_swarm_read_char,
+ .write_char = kgdb_swarm_write_char,
+ .init = kgdb_init_io,
+ .late_init = kgdbsibyte_hookup_irq,
+ .pre_exception = NULL,
+ .post_exception = NULL
+};
Index: linux-2.6.14/arch/mips/sibyte/sb1250/Makefile
===================================================================
--- linux-2.6.14.orig/arch/mips/sibyte/sb1250/Makefile
+++ linux-2.6.14/arch/mips/sibyte/sb1250/Makefile
@@ -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)
Index: linux-2.6.14/arch/mips/sibyte/swarm/dbg_io.c
===================================================================
--- linux-2.6.14.orig/arch/mips/sibyte/swarm/dbg_io.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * kgdb debug routines for SiByte boards.
- *
- * Copyright (C) 2001 MontaVista Software Inc.
- * Author: Jun Sun, [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);
-}
-
Index: linux-2.6.14/arch/mips/sibyte/swarm/Makefile
===================================================================
--- linux-2.6.14.orig/arch/mips/sibyte/swarm/Makefile
+++ linux-2.6.14/arch/mips/sibyte/swarm/Makefile
@@ -1,3 +1 @@
lib-y = setup.o rtc_xicor1241.o rtc_m41t81.o
-
-lib-$(CONFIG_KGDB) += dbg_io.o
Index: linux-2.6.14/include/asm-mips/kdebug.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-mips/kdebug.h
@@ -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 */
Index: linux-2.6.14/include/asm-mips/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-mips/kgdb.h
@@ -0,0 +1,21 @@
+#ifdef __KERNEL__
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+#define BUFMAX 2048
+#define NUMREGBYTES (90*sizeof(long))
+#define NUMCRITREGBYTES (12*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 CACHE_FLUSH_IS_SAFE 0
+
+extern int kgdb_early_setup;
+
+#endif /* _ASM_KGDB_H_ */
+#endif /* __KERNEL__ */
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -187,7 +187,7 @@ config WANT_EXTRA_DEBUG_INFORMATION
config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- depends on DEBUG_KERNEL && (X86 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. It is strongly suggested that you enable
@@ -215,6 +215,7 @@ choice
default KGDB_8250_NOMODULE
default KGDB_MPSC if SERIAL_MPSC
default KGDB_CPM_UART if (8xx || 8260)
+ default KGDB_SIBYTE if SIBYTE_SB1xxx_SOC
help
There are a number of different ways in which you can communicate
with KGDB. The most common is via serial, with the 8250 driver
@@ -253,6 +254,10 @@ config KGDB_CPM_UART
depends on PPC && (CPM2 || 8xx)
help
Uses CPM UART to communicate with the host GDB.
+
+config KGDB_SIBYTE
+ bool "KGDB: On the Broadcom SWARM serial port"
+ depends on MIPS && SIBYTE_SB1xxx_SOC
endchoice

config KGDB_8250
Index: linux-2.6.14/drivers/serial/serial_txx9.c
===================================================================
--- linux-2.6.14.orig/drivers/serial/serial_txx9.c
+++ linux-2.6.14/drivers/serial/serial_txx9.c
@@ -1184,6 +1184,96 @@ static struct pci_driver serial_txx9_pci
MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl);
#endif /* ENABLE_SERIAL_TXX9_PCI */

+/******************************************************************************/
+/* BEG: KDBG Routines */
+/******************************************************************************/
+
+#ifdef CONFIG_KGDB
+int kgdb_init_count = 0;
+
+void txx9_sio_kgdb_hook(unsigned int port, unsigned int baud_rate)
+{
+ static struct resource kgdb_resource;
+ int ret;
+ struct uart_txx9_port *up = &serial_txx9_ports[port];
+
+ /* prevent initialization by driver */
+ kgdb_resource.name = "serial_txx9(debug)";
+ kgdb_resource.start = up->port.membase;
+ kgdb_resource.end = up->port.membase + 36 - 1;
+ kgdb_resource.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+
+ ret = request_resource(&iomem_resource, &kgdb_resource);
+ if(ret == -EBUSY)
+ printk(" serial_txx9(debug): request_resource failed\n");
+
+ return;
+}
+void
+txx9_sio_kdbg_init( unsigned int port_number )
+{
+ if (port_number == 1) {
+ txx9_sio_kgdb_hook(port_number, 38400);
+ } else {
+ printk("Bad Port Number [%u] != [1]\n",port_number);
+ }
+ return;
+}
+
+u8
+txx9_sio_kdbg_rd( void )
+{
+ unsigned int status,ch;
+ struct uart_txx9_port *up = &serial_txx9_ports[1];
+
+ if (kgdb_init_count == 0) {
+ txx9_sio_kdbg_init(1);
+ kgdb_init_count = 1;
+ }
+
+ while (1) {
+ status = sio_in(up, TXX9_SIDISR);
+ if ( status & 0x1f ) {
+ ch = sio_in(up, TXX9_SIRFIFO );
+ break;
+ }
+ }
+
+ return (ch);
+}
+
+int
+txx9_sio_kdbg_wr( u8 ch )
+{
+ unsigned int status;
+ struct uart_txx9_port *up = &serial_txx9_ports[1];
+
+ if (kgdb_init_count == 0) {
+ txx9_sio_kdbg_init(1);
+ kgdb_init_count = 1;
+ }
+
+ while (1) {
+ status = sio_in(up, TXX9_SICISR);
+ if (status & TXX9_SICISR_TRDY) {
+ if ( ch == '\n' ) {
+ txx9_sio_kdbg_wr( '\r' );
+ }
+ sio_out(up, TXX9_SITFIFO, (u32)ch );
+
+ break;
+ }
+ }
+
+ return (1);
+}
+#endif /* CONFIG_KGDB */
+
+
+/******************************************************************************/
+/* END: KDBG Routines */
+/******************************************************************************/
+
static int __init serial_txx9_init(void)
{
int ret;

--
Tom

2005-11-10 16:42:15

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 06/15] KGDB: IA64-specific changes

This is support of the IA64 arch for KGDB. This is primarily the work of
Robert Picco. There are also some IA64 changes in the serial patch so that
IA64 can pass in IRQ and iomembase, and all of this is documented in the
DocBook files.

arch/ia64/kernel/Makefile | 1
arch/ia64/kernel/entry.S | 4
arch/ia64/kernel/ivt.S | 16
arch/ia64/kernel/kgdb-jmp.S | 238 ++++++++++
arch/ia64/kernel/kgdb.c | 1022 ++++++++++++++++++++++++++++++++++++++++++++
arch/ia64/kernel/process.c | 6
arch/ia64/kernel/smp.c | 17
arch/ia64/kernel/traps.c | 36 +
arch/ia64/kernel/unwind.c | 87 +++
arch/ia64/mm/extable.c | 6
arch/ia64/mm/fault.c | 4
include/asm-ia64/kgdb.h | 36 +
lib/Kconfig.debug | 2
13 files changed, 1470 insertions(+), 5 deletions(-)

Index: linux-2.6.14/arch/ia64/kernel/entry.S
===================================================================
--- linux-2.6.14.orig/arch/ia64/kernel/entry.S
+++ linux-2.6.14/arch/ia64/kernel/entry.S
@@ -952,9 +952,9 @@ GLOBAL_ENTRY(ia64_leave_kernel)
shr.u r18=r19,16 // get byte size of existing "dirty" partition
;;
mov r16=ar.bsp // get existing backing store pointer
- addl r17=THIS_CPU(ia64_phys_stacked_size_p8),r0
+(pUStk) addl r17=THIS_CPU(ia64_phys_stacked_size_p8),r0
;;
- ld4 r17=[r17] // r17 = cpu_data->phys_stacked_size_p8
+(pUStk) ld4 r17=[r17] // r17 = cpu_data->phys_stacked_size_p8
(pKStk) br.cond.dpnt skip_rbs_switch

/*
Index: linux-2.6.14/arch/ia64/kernel/ivt.S
===================================================================
--- linux-2.6.14.orig/arch/ia64/kernel/ivt.S
+++ linux-2.6.14/arch/ia64/kernel/ivt.S
@@ -53,6 +53,14 @@
#include <asm/unistd.h>
#include <asm/errno.h>

+#ifdef CONFIG_KGDB
+#define KGDB_ENABLE_PSR_DB mov r31=psr;; movl r30=IA64_PSR_DB;; \
+ or r31=r31,r30;; \
+ mov psr.l=r31;; srlz.i;;
+#else
+#define KGDB_ENABLE_PSR_DB
+#endif
+
#if 1
# define PSR_DEFAULT_BITS psr.ac
#else
@@ -478,6 +486,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
@@ -819,6 +828,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
@@ -1065,6 +1075,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
@@ -1098,6 +1109,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
@@ -1140,6 +1152,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
Index: linux-2.6.14/arch/ia64/kernel/kgdb.c
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/ia64/kernel/kgdb.c
@@ -0,0 +1,1022 @@
+/*
+ *
+ * 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 <asm/cacheflush.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;
+ }
+
+ mb();
+ return found;
+}
+
+int kgdb_arch_set_hw_breakpoint(unsigned long addr, int len,
+ enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 1);
+}
+
+int kgdb_arch_remove_hw_breakpoint(unsigned long addr, int len,
+ enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 0);
+}
+
+int kgdb_remove_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 0);
+
+}
+
+void kgdb_remove_all_hw_break(void)
+{
+ int i;
+
+ for (i = 0; i < HW_BREAKPOINT; i++)
+ memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+}
+
+int kgdb_set_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 1);
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status;
+
+ hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L,
+ hw_breakpoint_status ^ IA64_PSR_DB);
+}
+
+volatile static struct smp_unw {
+ struct unw_frame_info *unw;
+ struct task_struct *task;
+} smp_unw[NR_CPUS];
+
+static int inline kgdb_get_blocked_state(struct task_struct *p,
+ struct unw_frame_info *unw)
+{
+ unsigned long ip;
+ int count = 0;
+
+ unw_init_from_blocked_task(unw, p);
+ ip = 0UL;
+ do {
+ if (unw_unwind(unw) < 0)
+ return -1;
+ unw_get_ip(unw, &ip);
+ if (!in_sched_functions(ip))
+ break;
+ } while (count++ < 16);
+
+ if (!ip)
+ return -1;
+ else
+ return 0;
+}
+
+static void inline kgdb_wait(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L,
+ hw_breakpoint_status ^ IA64_PSR_DB);
+ kgdb_nmihook(smp_processor_id(), regs);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status);
+ return;
+}
+
+static void inline normalize(struct unw_frame_info *running,
+ struct pt_regs *regs)
+{
+ unsigned long sp;
+
+ do {
+ unw_get_sp(running, &sp);
+ if ((sp + 0x10) >= (unsigned long)regs)
+ break;
+ } while (unw_unwind(running) >= 0);
+
+ return;
+}
+
+static void kgdb_init_running(struct unw_frame_info *unw, void *data)
+{
+ struct pt_regs *regs;
+
+ regs = data;
+ normalize(unw, regs);
+ smp_unw[smp_processor_id()].unw = unw;
+ kgdb_wait(regs);
+}
+
+void kgdb_wait_ipi(struct pt_regs *regs)
+{
+ struct unw_frame_info unw;
+
+ smp_unw[smp_processor_id()].task = current;
+
+ if (user_mode(regs)) {
+ smp_unw[smp_processor_id()].unw = (struct unw_frame_info *)1;
+ kgdb_wait(regs);
+ } else {
+ if (current->state == TASK_RUNNING)
+ unw_init_running(kgdb_init_running, regs);
+ else {
+ if (kgdb_get_blocked_state(current, &unw))
+ smp_unw[smp_processor_id()].unw =
+ (struct unw_frame_info *)1;
+ else
+ smp_unw[smp_processor_id()].unw = &unw;
+ kgdb_wait(regs);
+ }
+ }
+
+ smp_unw[smp_processor_id()].unw = NULL;
+ return;
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ if (num_online_cpus() > 1)
+ smp_send_nmi_allbutself();
+}
+
+static void do_kgdb_handle_exception(struct unw_frame_info *, void *data);
+
+struct kgdb_state {
+ int e_vector;
+ int signo;
+ unsigned long err_code;
+ struct pt_regs *regs;
+ struct unw_frame_info *unw;
+ char *inbuf;
+ char *outbuf;
+ int unwind;
+ int ret;
+};
+
+static void inline kgdb_pc(struct pt_regs *regs, unsigned long pc)
+{
+ regs->cr_iip = pc & ~0xf;
+ ia64_psr(regs)->ri = pc & 0x3;
+ return;
+}
+
+volatile int kgdb_hwbreak_sstep[NR_CPUS];
+
+int kgdb_arch_handle_exception(int e_vector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ struct kgdb_state info;
+
+ info.e_vector = e_vector;
+ info.signo = signo;
+ info.err_code = err_code;
+ info.unw = (void *)0;
+ info.inbuf = remcom_in_buffer;
+ info.outbuf = remcom_out_buffer;
+ info.unwind = 0;
+ info.ret = -1;
+
+ if (remcom_in_buffer[0] == 'c' || remcom_in_buffer[0] == 's') {
+ info.regs = linux_regs;
+ do_kgdb_handle_exception(NULL, &info);
+ } else if (kgdb_usethread == current) {
+ info.regs = linux_regs;
+ info.unwind = 1;
+ unw_init_running(do_kgdb_handle_exception, &info);
+ } else if (kgdb_usethread->state != TASK_RUNNING) {
+ struct unw_frame_info unw_info;
+
+ if (kgdb_get_blocked_state(kgdb_usethread, &unw_info)) {
+ info.ret = 1;
+ goto bad;
+ }
+ info.regs = NULL;
+ do_kgdb_handle_exception(&unw_info, &info);
+ } else {
+ int i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (smp_unw[i].task == kgdb_usethread && smp_unw[i].unw
+ && smp_unw[i].unw != (struct unw_frame_info *)1) {
+ info.regs = NULL;
+ do_kgdb_handle_exception(smp_unw[i].unw, &info);
+ break;
+ } else {
+ info.ret = 1;
+ goto bad;
+ }
+ }
+
+ bad:
+ if (info.ret != -1 && remcom_in_buffer[0] == 'p') {
+ unsigned long bad = 0xbad4badbadbadbadUL;
+
+ printk("kgdb_arch_handle_exception: p packet bad (%s)\n",
+ remcom_in_buffer);
+ kgdb_mem2hex((char *)&bad, remcom_out_buffer, sizeof(bad));
+ remcom_out_buffer[sizeof(bad) * 2] = 0;
+ info.ret = -1;
+ }
+ return info.ret;
+}
+
+static void do_kgdb_handle_exception(struct unw_frame_info *unw_info,
+ void *data)
+{
+ long addr;
+ char *ptr;
+ unsigned long newPC;
+ int e_vector, signo;
+ unsigned long err_code;
+ struct pt_regs *linux_regs;
+ struct kgdb_state *info;
+ char *remcom_in_buffer, *remcom_out_buffer;
+
+ info = data;
+ info->unw = unw_info;
+ e_vector = info->e_vector;
+ signo = info->signo;
+ err_code = info->err_code;
+ remcom_in_buffer = info->inbuf;
+ remcom_out_buffer = info->outbuf;
+ linux_regs = info->regs;
+
+ if (info->unwind)
+ normalize(unw_info, linux_regs);
+
+ switch (remcom_in_buffer[0]) {
+ case 'p':
+ {
+ 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 (e_vector == 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' || e_vector == 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 (e_vector != 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,
+};
Index: linux-2.6.14/arch/ia64/kernel/kgdb-jmp.S
===================================================================
--- /dev/null
+++ linux-2.6.14/arch/ia64/kernel/kgdb-jmp.S
@@ -0,0 +1,238 @@
+/* setjmp() and longjmp() assembler support for kdb on ia64.
+
+ This code was copied from glibc CVS as of 2001-06-27 and modified where
+ necessary to fit the kernel.
+ Keith Owens <[email protected]> 2001-06-27
+ */
+
+/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang <[email protected]>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If
+ not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <asm/asmmacro.h>
+GLOBAL_ENTRY(kgdb_fault_setjmp)
+ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)
+ alloc loc1=ar.pfs,2,2,2,0
+ mov r16=ar.unat
+ ;;
+ mov r17=ar.fpsr
+ mov r2=in0
+ add r3=8,in0
+ ;;
+.mem.offset 0,0;
+ st8.spill.nta [r2]=sp,16 // r12 (sp)
+.mem.offset 8,0;
+ st8.spill.nta [r3]=gp,16 // r1 (gp)
+ ;;
+ st8.nta [r2]=r16,16 // save caller's unat
+ st8.nta [r3]=r17,16 // save fpsr
+ add r8=0xa0,in0
+ ;;
+.mem.offset 160,0;
+ st8.spill.nta [r2]=r4,16 // r4
+.mem.offset 168,0;
+ st8.spill.nta [r3]=r5,16 // r5
+ add r9=0xb0,in0
+ ;;
+ stf.spill.nta [r8]=f2,32
+ stf.spill.nta [r9]=f3,32
+ mov loc0=rp
+ .body
+ ;;
+ stf.spill.nta [r8]=f4,32
+ stf.spill.nta [r9]=f5,32
+ mov r17=b1
+ ;;
+ stf.spill.nta [r8]=f16,32
+ stf.spill.nta [r9]=f17,32
+ mov r18=b2
+ ;;
+ stf.spill.nta [r8]=f18,32
+ stf.spill.nta [r9]=f19,32
+ mov r19=b3
+ ;;
+ stf.spill.nta [r8]=f20,32
+ stf.spill.nta [r9]=f21,32
+ mov r20=b4
+ ;;
+ stf.spill.nta [r8]=f22,32
+ stf.spill.nta [r9]=f23,32
+ mov r21=b5
+ ;;
+ stf.spill.nta [r8]=f24,32
+ stf.spill.nta [r9]=f25,32
+ mov r22=ar.lc
+ ;;
+ stf.spill.nta [r8]=f26,32
+ stf.spill.nta [r9]=f27,32
+ mov r24=pr
+ ;;
+ stf.spill.nta [r8]=f28,32
+ stf.spill.nta [r9]=f29,32
+ ;;
+ stf.spill.nta [r8]=f30
+ stf.spill.nta [r9]=f31
+
+.mem.offset 0,0;
+ st8.spill.nta [r2]=r6,16 // r6
+.mem.offset 8,0;
+ st8.spill.nta [r3]=r7,16 // r7
+ ;;
+ mov r23=ar.bsp
+ mov r25=ar.unat
+ st8.nta [r2]=loc0,16 // b0
+ st8.nta [r3]=r17,16 // b1
+ ;;
+ st8.nta [r2]=r18,16 // b2
+ st8.nta [r3]=r19,16 // b3
+ ;;
+ st8.nta [r2]=r20,16 // b4
+ st8.nta [r3]=r21,16 // b5
+ ;;
+ st8.nta [r2]=loc1,16 // ar.pfs
+ st8.nta [r3]=r22,16 // ar.lc
+ ;;
+ st8.nta [r2]=r24,16 // pr
+ st8.nta [r3]=r23,16 // ar.bsp
+ ;;
+ st8.nta [r2]=r25 // ar.unat
+ st8.nta [r3]=in0 // &__jmp_buf
+ mov r8=0
+ mov rp=loc0
+ mov ar.pfs=loc1
+ br.ret.sptk.few rp
+END(kdba_setjmp)
+#define pPos p6 /* is rotate count positive? */
+#define pNeg p7 /* is rotate count negative? */
+GLOBAL_ENTRY(kgdb_fault_longjmp)
+ alloc r8=ar.pfs,2,1,0,0
+ mov r27=ar.rsc
+ add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr
+ ;;
+ ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr
+ mov r10=ar.bsp
+ and r11=~0x3,r27 // clear ar.rsc.mode
+ ;;
+ flushrs // flush dirty regs to backing store (must be first in insn grp)
+ ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp
+ sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf
+ ;;
+ ld8 r25=[r2] // r25 <- jmpbuf.ar_unat
+ extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f
+ ;;
+ cmp.lt pNeg,pPos=r8,r0
+ mov r2=in0
+ ;;
+(pPos) mov r16=r8
+(pNeg) add r16=64,r8
+(pPos) sub r17=64,r8
+(pNeg) sub r17=r0,r8
+ ;;
+ mov ar.rsc=r11 // put RSE in enforced lazy mode
+ shr.u r8=r25,r16
+ add r3=8,in0 // r3 <- &jmpbuf.r1
+ shl r9=r25,r17
+ ;;
+ or r25=r8,r9
+ ;;
+ mov r26=ar.rnat
+ mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12)
+ ;;
+ ld8.fill.nta sp=[r2],16 // r12 (sp)
+ ld8.fill.nta gp=[r3],16 // r1 (gp)
+ dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ ;;
+ ld8.nta r16=[r2],16 // caller's unat
+ ld8.nta r17=[r3],16 // fpsr
+ ;;
+ ld8.fill.nta r4=[r2],16 // r4
+ ld8.fill.nta r5=[r3],16 // r5 (gp)
+ cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp)
+ ;;
+ ld8.fill.nta r6=[r2],16 // r6
+ ld8.fill.nta r7=[r3],16 // r7
+ ;;
+ mov ar.unat=r16 // restore caller's unat
+ mov ar.fpsr=r17 // restore fpsr
+ ;;
+ ld8.nta r16=[r2],16 // b0
+ ld8.nta r17=[r3],16 // b1
+ ;;
+(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ mov ar.bspstore=r23 // restore ar.bspstore
+ ;;
+ ld8.nta r18=[r2],16 // b2
+ ld8.nta r19=[r3],16 // b3
+ ;;
+ ld8.nta r20=[r2],16 // b4
+ ld8.nta r21=[r3],16 // b5
+ ;;
+ ld8.nta r11=[r2],16 // ar.pfs
+ ld8.nta r22=[r3],56 // ar.lc
+ ;;
+ ld8.nta r24=[r2],32 // pr
+ mov b0=r16
+ ;;
+ ldf.fill.nta f2=[r2],32
+ ldf.fill.nta f3=[r3],32
+ mov b1=r17
+ ;;
+ ldf.fill.nta f4=[r2],32
+ ldf.fill.nta f5=[r3],32
+ mov b2=r18
+ ;;
+ ldf.fill.nta f16=[r2],32
+ ldf.fill.nta f17=[r3],32
+ mov b3=r19
+ ;;
+ ldf.fill.nta f18=[r2],32
+ ldf.fill.nta f19=[r3],32
+ mov b4=r20
+ ;;
+ ldf.fill.nta f20=[r2],32
+ ldf.fill.nta f21=[r3],32
+ mov b5=r21
+ ;;
+ ldf.fill.nta f22=[r2],32
+ ldf.fill.nta f23=[r3],32
+ mov ar.lc=r22
+ ;;
+ ldf.fill.nta f24=[r2],32
+ ldf.fill.nta f25=[r3],32
+ cmp.eq p8,p9=0,in1
+ ;;
+ ldf.fill.nta f26=[r2],32
+ ldf.fill.nta f27=[r3],32
+ mov ar.pfs=r11
+ ;;
+ ldf.fill.nta f28=[r2],32
+ ldf.fill.nta f29=[r3],32
+ ;;
+ ldf.fill.nta f30=[r2]
+ ldf.fill.nta f31=[r3]
+(p8) mov r8=1
+
+ mov ar.rnat=r26 // restore ar.rnat
+ ;;
+ mov ar.rsc=r27 // restore ar.rsc
+(p9) mov r8=in1
+
+ invala // virt. -> phys. regnum mapping may change
+ mov pr=r24,-1
+ br.ret.sptk.few rp
+END(kgdb_fault_longjmp)
Index: linux-2.6.14/arch/ia64/kernel/Makefile
===================================================================
--- linux-2.6.14.orig/arch/ia64/kernel/Makefile
+++ linux-2.6.14/arch/ia64/kernel/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_r
obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o
obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o
mca_recovery-y += mca_drv.o mca_drv_asm.o
+obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o

# The gate DSO image is built using a special linker script.
targets += gate.so gate-syms.o
Index: linux-2.6.14/arch/ia64/kernel/process.c
===================================================================
--- linux-2.6.14.orig/arch/ia64/kernel/process.c
+++ linux-2.6.14/arch/ia64/kernel/process.c
@@ -451,6 +451,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
@@ -679,6 +682,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;
Index: linux-2.6.14/arch/ia64/kernel/smp.c
===================================================================
--- linux-2.6.14.orig/arch/ia64/kernel/smp.c
+++ linux-2.6.14/arch/ia64/kernel/smp.c
@@ -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);
@@ -305,6 +314,14 @@ smp_call_function_single (int cpuid, voi
}
EXPORT_SYMBOL(smp_call_function_single);

+#ifdef CONFIG_KGDB
+void
+smp_send_nmi_allbutself(void)
+{
+ send_IPI_allbutself(IPI_KGDB_INTERRUPT);
+}
+#endif
+
/*
* this function sends a 'generic call function' IPI to all other CPUs
* in the system.
Index: linux-2.6.14/arch/ia64/kernel/traps.c
===================================================================
--- linux-2.6.14.orig/arch/ia64/kernel/traps.c
+++ linux-2.6.14/arch/ia64/kernel/traps.c
@@ -16,6 +16,7 @@
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <linux/hardirq.h>
#include <linux/kprobes.h>
+#include <linux/kgdb.h>

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

+#ifdef CONFIG_KGDB
+ kgdb_handle_exception(1, SIGTRAP, err, regs);
+#endif
+
bust_spinlocks(0);
die.lock_owner = -1;
spin_unlock_irq(&die.lock);
@@ -158,7 +163,9 @@ __kprobes ia64_bad_break (unsigned long
== NOTIFY_STOP) {
return;
}
+#ifndef CONFIG_KGDB
die_if_kernel("bugcheck!", regs, break_num);
+#endif
sig = SIGILL; code = ILL_ILLOPC;
break;

@@ -220,8 +227,10 @@ __kprobes ia64_bad_break (unsigned long
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;
@@ -229,6 +238,15 @@ __kprobes ia64_bad_break (unsigned long
sig = SIGTRAP; code = TRAP_BRKPT;
}
}
+#ifdef CONFIG_KGDB
+ /*
+ * We don't want to trap simulator system calls.
+ */
+ if (break_num != 0x80001) {
+ kgdb_handle_exception(11, sig, break_num, regs);
+ return;
+ }
+#endif
siginfo.si_signo = sig;
siginfo.si_errno = 0;
siginfo.si_code = code;
@@ -544,10 +562,21 @@ ia64_fault (unsigned long vector, unsign
}
sprintf(buf, "Unsupported data reference");
break;
-
case 29: /* Debug */
- case 35: /* Taken Branch Trap */
case 36: /* Single Step Trap */
+#ifdef CONFIG_KGDB
+ if (vector == 36 && !user_mode(&regs) &&
+ kgdb_hwbreak_sstep[smp_processor_id()]) {
+ kgdb_hwbreak_sstep[smp_processor_id()] = 0;
+ regs.cr_ipsr &= ~IA64_PSR_SS;
+ return;
+ } else if (!user_mode(&regs)) {
+ kgdb_handle_exception(vector, SIGTRAP, isr, &regs);
+ return;
+ }
+#endif
+ /* Fall */
+ case 35: /* Taken Branch Trap */
if (fsys_mode(current, &regs)) {
extern char __kernel_syscall_via_break[];
/*
@@ -665,6 +694,9 @@ ia64_fault (unsigned long vector, unsign
sprintf(buf, "Fault %lu", vector);
break;
}
+#ifdef CONFIG_KGDB
+ kgdb_handle_exception(vector, SIGTRAP, isr, &regs);
+#endif
die_if_kernel(buf, &regs, error);
force_sig(SIGILL, current);
}
Index: linux-2.6.14/arch/ia64/kernel/unwind.c
===================================================================
--- linux-2.6.14.orig/arch/ia64/kernel/unwind.c
+++ linux-2.6.14/arch/ia64/kernel/unwind.c
@@ -72,10 +72,68 @@
# define STAT(x...)
#endif

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

typedef unsigned long unw_word;
typedef unsigned char unw_hash_index_t;
@@ -238,6 +296,24 @@ static struct {
#endif
};

+#ifdef CONFIG_KGDB
+/*
+ * This makes it safe to call breakpoint() very early
+ * in setup_arch providing:
+ * 1) breakpoint isn't called between lines in cpu_init
+ * where init_mm.mm_count is incremented and ia64_mmu_init
+ * is called. Otherwise the test below is invalid.
+ * 2) the memory examined doesn't result in tlbmiss.
+ */
+static unsigned long inline kgdb_unimpl_va_mask(void)
+{
+ if (atomic_read(&init_mm.mm_count) > 1)
+ return local_cpu_data->unimpl_va_mask;
+ else
+ return 0UL;
+}
+#endif
+
static inline int
read_only (void *addr)
{
@@ -1786,7 +1862,11 @@ run_script (struct unw_script *script, s

case UNW_INSN_LOAD:
#ifdef UNW_DEBUG
+#ifdef CONFIG_KGDB
+ if ((s[val] & (kgdb_unimpl_va_mask() | 0x7)) != 0
+#else
if ((s[val] & (local_cpu_data->unimpl_va_mask | 0x7)) != 0
+#endif
|| s[val] < TASK_SIZE)
{
UNW_DPRINT(0, "unwind.%s: rejecting bad psp=0x%lx\n",
@@ -1821,7 +1901,11 @@ find_save_locs (struct unw_frame_info *i
struct unw_script *scr;
unsigned long flags = 0;

+#ifdef CONFIG_KGDB
+ if ((info->ip & (kgdb_unimpl_va_mask() | 0xf)) || info->ip < TASK_SIZE) {
+#else
if ((info->ip & (local_cpu_data->unimpl_va_mask | 0xf)) || info->ip < TASK_SIZE) {
+#endif
/* don't let obviously bad addresses pollute the cache */
/* FIXME: should really be level 0 but it occurs too often. KAO */
UNW_DPRINT(1, "unwind.%s: rejecting bad ip=0x%lx\n", __FUNCTION__, info->ip);
@@ -2249,6 +2333,9 @@ unw_init (void)

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

/*
Index: linux-2.6.14/arch/ia64/mm/extable.c
===================================================================
--- linux-2.6.14.orig/arch/ia64/mm/extable.c
+++ linux-2.6.14/arch/ia64/mm/extable.c
@@ -7,6 +7,7 @@

#include <linux/config.h>
#include <linux/sort.h>
+#include <linux/kgdb.h>

#include <asm/uaccess.h>
#include <asm/module.h>
@@ -74,6 +75,11 @@ search_extable (const struct exception_t
else
last = mid - 1;
}
+#ifdef CONFIG_KGDB
+ if (atomic_read(&debugger_active) && kgdb_may_fault)
+ kgdb_fault_longjmp(kgdb_fault_jmp_regs);
+ /* Not reached. */
+#endif
return NULL;
}

Index: linux-2.6.14/arch/ia64/mm/fault.c
===================================================================
--- linux-2.6.14.orig/arch/ia64/mm/fault.c
+++ linux-2.6.14/arch/ia64/mm/fault.c
@@ -249,6 +249,10 @@ ia64_do_page_fault (unsigned long addres
*/
bust_spinlocks(1);

+#ifdef CONFIG_KGDB
+ kgdb_handle_exception(14, SIGSEGV, isr, regs);
+#endif
+
if (address < PAGE_SIZE)
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address);
else
Index: linux-2.6.14/include/asm-ia64/kgdb.h
===================================================================
--- /dev/null
+++ linux-2.6.14/include/asm-ia64/kgdb.h
@@ -0,0 +1,36 @@
+#ifdef __KERNEL__
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/threads.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. We set this to 0 so that certain GDB
+ * packets will fail, forcing the use of others, which are more friendly
+ * on ia64. */
+#define NUMREGBYTES 0
+
+#define NUMCRITREGBYTES (70*8)
+#define JMP_REGS_ALIGNMENT __attribute__ ((aligned (16)))
+
+#define BREAKNUM 0x00003333300LL
+#define KGDBBREAKNUM 0x6665UL
+#define BREAKPOINT() asm volatile ("break.m 0x6665")
+#define BREAK_INSTR_SIZE 16
+#define CACHE_FLUSH_IS_SAFE 1
+
+struct pt_regs;
+extern volatile int kgdb_hwbreak_sstep[NR_CPUS];
+extern void smp_send_nmi_allbutself(void);
+extern void kgdb_wait_ipi(struct pt_regs *);
+#endif /* _ASM_KGDB_H_ */
+#endif /* __KERNEL__ */
Index: linux-2.6.14/lib/Kconfig.debug
===================================================================
--- linux-2.6.14.orig/lib/Kconfig.debug
+++ linux-2.6.14/lib/Kconfig.debug
@@ -187,7 +187,7 @@ config WANT_EXTRA_DEBUG_INFORMATION
config KGDB
bool "KGDB: kernel debugging with remote gdb"
select WANT_EXTRA_DEBUG_INFORMATION
- depends on DEBUG_KERNEL && (X86 || MIPS || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS || IA64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. It is strongly suggested that you enable

--
Tom

2005-11-10 16:45:58

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 14/15] KGDB: Fix for 'lost' SysRq events

It is possible that when SysRq-G is triggered via the keyboard that we will
miss the "up" event and once KGDB lets the kernel go another SysRq will be
required to clear this, without this change.

drivers/char/keyboard.c | 1 +
1 file changed, 1 insertion(+)

Index: linux-2.6.14/drivers/char/keyboard.c
===================================================================
--- linux-2.6.14.orig/drivers/char/keyboard.c
+++ linux-2.6.14/drivers/char/keyboard.c
@@ -1069,6 +1069,7 @@ static void kbd_keycode(unsigned int key
}
if (sysrq_down && down && !rep) {
handle_sysrq(kbd_sysrq_xlate[keycode], regs, tty);
+ sysrq_down = 0; /* In case we miss the 'up' event. */
return;
}
#endif

--
Tom

2005-11-10 16:45:57

by Tom Rini

[permalink] [raw]
Subject: [PATCH,RFC 2.6.14 15/15] KGDB: Better support for loaded modules

This allows for KGDB to better deal with autoloaded modules. The way this
works, requires a patch to GDB. This patch can be found at
ftp://source.mvista.com/pub/kgdb/gdb-6.3-kgdb-module-notification.patch

The way this works is that the solib-search-path must contain the location of
any module to be debugged, and then when a module is loaded GDB acts like a
shared library has been loaded now, and can be used that way.

include/linux/module.h | 16 ++++++++++++++
kernel/module.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 72 insertions(+)

Index: linux-2.6.14/include/linux/module.h
===================================================================
--- linux-2.6.14.orig/include/linux/module.h
+++ linux-2.6.14/include/linux/module.h
@@ -210,8 +210,17 @@ enum module_state
MODULE_STATE_LIVE,
MODULE_STATE_COMING,
MODULE_STATE_GOING,
+ MODULE_STATE_GONE,
};

+#ifdef CONFIG_KGDB
+#define MAX_SECTNAME 31
+struct mod_section {
+ void *address;
+ char name[MAX_SECTNAME + 1];
+};
+#endif
+
/* Similar stuff for section attributes. */
#define MODULE_SECT_NAME_LEN 32
struct module_sect_attr
@@ -239,6 +248,13 @@ struct module
/* Unique handle for this module */
char name[MODULE_NAME_LEN];

+#ifdef CONFIG_KGDB
+ /* keep kgdb info at the begining so that gdb doesn't have a chance to
+ * miss out any fields */
+ unsigned long num_sections;
+ struct mod_section *mod_sections;
+#endif
+
/* Sysfs stuff. */
struct module_kobject mkobj;
struct module_param_attrs *param_attrs;
Index: linux-2.6.14/kernel/module.c
===================================================================
--- linux-2.6.14.orig/kernel/module.c
+++ linux-2.6.14/kernel/module.c
@@ -624,6 +624,12 @@ sys_delete_module(const char __user *nam
if (ret != 0)
goto out;

+ down(&notify_mutex);
+ notifier_call_chain(&module_notify_list, MODULE_STATE_GOING,
+ mod);
+ up(&notify_mutex);
+
+
/* Never wait if forced. */
if (!forced && module_refcount(mod) != 0)
wait_for_zero_refcount(mod);
@@ -636,6 +642,11 @@ sys_delete_module(const char __user *nam
}
free_module(mod);

+ down(&notify_mutex);
+ notifier_call_chain(&module_notify_list, MODULE_STATE_GONE,
+ NULL);
+ up(&notify_mutex);
+
out:
up(&module_mutex);
return ret;
@@ -1174,6 +1185,11 @@ static void free_module(struct module *m
/* Arch-specific cleanup. */
module_arch_cleanup(mod);

+#ifdef CONFIG_KGDB
+ /* kgdb info */
+ vfree(mod->mod_sections);
+#endif
+
/* Module unload stuff */
module_unload_free(mod);

@@ -1408,6 +1424,31 @@ static void setup_modinfo(struct module
}
#endif

+#ifdef CONFIG_KGDB
+int add_modsects (struct module *mod, Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const
+ char *secstrings)
+{
+ int i;
+
+ mod->num_sections = hdr->e_shnum - 1;
+ mod->mod_sections = vmalloc((hdr->e_shnum - 1)*
+ sizeof (struct mod_section));
+
+ if (mod->mod_sections == NULL) {
+ return -ENOMEM;
+ }
+
+ for (i = 1; i < hdr->e_shnum; i++) {
+ mod->mod_sections[i - 1].address = (void *)sechdrs[i].sh_addr;
+ strncpy(mod->mod_sections[i - 1].name, secstrings +
+ sechdrs[i].sh_name, MAX_SECTNAME);
+ mod->mod_sections[i - 1].name[MAX_SECTNAME] = '\0';
+ }
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_KALLSYMS
int is_exported(const char *name, const struct module *mod)
{
@@ -1777,6 +1818,12 @@ static struct module *load_module(void _

add_kallsyms(mod, sechdrs, symindex, strindex, secstrings);

+#ifdef CONFIG_KGDB
+ if ((err = add_modsects(mod, hdr, sechdrs, secstrings)) < 0) {
+ goto nomodsectinfo;
+ }
+#endif
+
err = module_finalize(hdr, sechdrs, mod);
if (err < 0)
goto cleanup;
@@ -1842,6 +1889,11 @@ static struct module *load_module(void _
arch_cleanup:
module_arch_cleanup(mod);
cleanup:
+
+#ifdef CONFIG_KGDB
+nomodsectinfo:
+ vfree(mod->mod_sections);
+#endif
module_unload_free(mod);
module_free(mod, mod->module_init);
free_core:
@@ -1915,6 +1967,10 @@ sys_init_module(void __user *umod,
/* Init routine failed: abort. Try to protect us from
buggy refcounters. */
mod->state = MODULE_STATE_GOING;
+ down(&notify_mutex);
+ notifier_call_chain(&module_notify_list, MODULE_STATE_GOING,
+ mod);
+ up(&notify_mutex);
synchronize_sched();
if (mod->unsafe)
printk(KERN_ERR "%s: module is now stuck!\n",

--
Tom

2005-11-11 00:55:28

by Paul Mackerras

[permalink] [raw]
Subject: Re: [PATCH,RFC 2.6.14 11/15] KGDB: ppc64-specific changes

Tom Rini writes:

> This adds basic KGDB support to ppc64, and support for kgdb8250 on the 'Maple'
> board. All of this was done by Frank Rowand (who is on vacation right now,
> but I'll try and answer for him). This should work on any ppc64 board via
> kgdboe, so long as there is an eth driver that supports netpoll. At the
> moment this is mutually exclusive with XMON. It is probably possible to allow
> them to be chained, but that sounds dangerous to me. This is similar to
> ppc32, but ppc32 does not explicitly test.

We already have infrastructure to allow either xmon or kdb to be used,
and in fact both can be built in and you can select at runtime which
you prefer. See the __debugger stuff in system.h. You should just be
able to hook kgdb into that same infrastructure. We're also planning
to move to using the die_notify stuff for getting all the significant
events to the debugger.

Paul.

2005-11-11 01:13:06

by Tom Rini

[permalink] [raw]
Subject: Re: [PATCH,RFC 2.6.14 11/15] KGDB: ppc64-specific changes

On Fri, Nov 11, 2005 at 11:55:22AM +1100, Paul Mackerras wrote:
> Tom Rini writes:
>
> > This adds basic KGDB support to ppc64, and support for kgdb8250 on the 'Maple'
> > board. All of this was done by Frank Rowand (who is on vacation right now,
> > but I'll try and answer for him). This should work on any ppc64 board via
> > kgdboe, so long as there is an eth driver that supports netpoll. At the
> > moment this is mutually exclusive with XMON. It is probably possible to allow
> > them to be chained, but that sounds dangerous to me. This is similar to
> > ppc32, but ppc32 does not explicitly test.
>
> We already have infrastructure to allow either xmon or kdb to be used,
> and in fact both can be built in and you can select at runtime which
> you prefer. See the __debugger stuff in system.h. You should just be
> able to hook kgdb into that same infrastructure. We're also planning
> to move to using the die_notify stuff for getting all the significant
> events to the debugger.

The notify_die stuff is wonderful. If I had a ppc64 board, I'd give
that a whirl myself. This does, unless there was something I missed,
tie into the existing debugger pointer stuff (thats similar but
different from ppc32). Assuming it hasn't started already, if I can
find time I might give it a wack on arch/powerpc, since I assume my
AlBook works (if not, I see my LongTrail might ;))

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