Merge git://git.kernel.org/pub/scm/linux/kernel/git/mingo/linux-2.6-kgdb
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Apr 2008 15:37:01 +0000 (08:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Apr 2008 15:37:01 +0000 (08:37 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/mingo/linux-2.6-kgdb:
  kgdb: always use icache flush for sw breakpoints
  kgdb: fix SMP NMI kgdb_handle_exception exit race
  kgdb: documentation fixes
  kgdb: allow static kgdbts boot configuration
  kgdb: add documentation
  kgdb: Kconfig fix
  kgdb: add kgdb internal test suite
  kgdb: fix several kgdb regressions
  kgdb: kgdboc pl011 I/O module
  kgdb: fix optional arch functions and probe_kernel_*
  kgdb: add x86 HW breakpoints
  kgdb: print breakpoint removed on exception
  kgdb: clocksource watchdog
  kgdb: fix NMI hangs
  kgdb: fix kgdboc dynamic module configuration
  kgdb: document parameters
  x86: kgdb support
  consoles: polling support, kgdboc
  kgdb: core
  uaccess: add probe_kernel_write()

33 files changed:
Documentation/DocBook/Makefile
Documentation/DocBook/kgdb.tmpl [new file with mode: 0644]
Documentation/kernel-parameters.txt
MAINTAINERS
arch/x86/Kconfig
arch/x86/kernel/Makefile
arch/x86/kernel/kgdb.c [new file with mode: 0644]
arch/x86/kernel/setup64.c
arch/x86/kernel/traps_32.c
arch/x86/kernel/traps_64.c
drivers/char/tty_io.c
drivers/misc/Makefile
drivers/misc/kgdbts.c [new file with mode: 0644]
drivers/serial/8250.c
drivers/serial/Kconfig
drivers/serial/Makefile
drivers/serial/amba-pl011.c
drivers/serial/kgdboc.c [new file with mode: 0644]
drivers/serial/serial_core.c
include/asm-x86/kdebug.h
include/asm-x86/kgdb.h [new file with mode: 0644]
include/linux/clocksource.h
include/linux/kgdb.h [new file with mode: 0644]
include/linux/serial_core.h
include/linux/tty_driver.h
include/linux/uaccess.h
kernel/Makefile
kernel/kgdb.c [new file with mode: 0644]
kernel/time/clocksource.c
lib/Kconfig.debug
lib/Kconfig.kgdb [new file with mode: 0644]
mm/Makefile
mm/maccess.c [new file with mode: 0644]

index 300e170..e471bc4 100644 (file)
@@ -9,7 +9,7 @@
 DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml videobook.xml \
            kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
            procfs-guide.xml writing_usb_driver.xml networking.xml \
-           kernel-api.xml filesystems.xml lsm.xml usb.xml \
+           kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
            gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
            genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml
 
diff --git a/Documentation/DocBook/kgdb.tmpl b/Documentation/DocBook/kgdb.tmpl
new file mode 100644 (file)
index 0000000..97618be
--- /dev/null
@@ -0,0 +1,447 @@
+<?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="kgdbOnLinux">
+ <bookinfo>
+  <title>Using kgdb and the kgdb Internals</title>
+
+  <authorgroup>
+   <author>
+    <firstname>Jason</firstname>
+    <surname>Wessel</surname>
+    <affiliation>
+     <address>
+      <email>jason.wessel@windriver.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <authorgroup>
+   <author>
+    <firstname>Tom</firstname>
+    <surname>Rini</surname>
+    <affiliation>
+     <address>
+      <email>trini@kernel.crashing.org</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <authorgroup>
+   <author>
+    <firstname>Amit S.</firstname>
+    <surname>Kale</surname>
+    <affiliation>
+     <address>
+      <email>amitkale@linsyssoft.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2008</year>
+   <holder>Wind River Systems, Inc.</holder>
+  </copyright>
+  <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.  The expectation is that gdb can
+    be used to "break in" to the kernel to inspect memory, variables
+    and look through a cal stack information similar to what an
+    application developer would use gdb for.  It is possible to place
+    breakpoints in kernel code and perform some limited execution
+    stepping.
+    </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 kernel
+    to be debugged runs on the test machine. The development machine
+    runs an instance of gdb against the vmlinux file which contains
+    the symbols (not boot image such as bzImage, zImage, uImage...).
+    In gdb the developer specifies the connection parameters and
+    connects to kgdb.  Depending on which kgdb I/O modules exist in
+    the kernel for a given architecture, it may be possible to debug
+    the test machine's kernel with the development machine using a
+    rs232 or ethernet connection.
+    </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>
+    Next you should choose one of more I/O drivers to interconnect debugging
+    host and debugged target.  Early boot debugging requires a KGDB
+    I/O driver that supports early debugging and the driver must be
+    built into the kernel directly. Kgdb I/O driver configuration
+    takes place via kernel or module parameters, see following
+    chapter.
+    </para>
+    <para>
+    The kgdb test compile options are described in the kgdb test suite chapter.
+    </para>
+
+  </chapter>
+  <chapter id="EnableKGDB">
+   <title>Enable kgdb for debugging</title>
+   <para>
+   In order to use kgdb you must activate it by passing configuration
+   information to one of the kgdb I/O drivers.  If you do not pass any
+   configuration information kgdb will not do anything at all.  Kgdb
+   will only actively hook up to the kernel trap hooks if a kgdb I/O
+   driver is loaded and configured.  If you unconfigure a kgdb I/O
+   driver, kgdb will unregister all the kernel hook points.
+   </para>
+   <para>
+   All drivers can be reconfigured at run time, if
+   <symbol>CONFIG_SYSFS</symbol> and <symbol>CONFIG_MODULES</symbol>
+   are enabled, by echo'ing a new config string to
+   <constant>/sys/module/&lt;driver&gt;/parameter/&lt;option&gt;</constant>.
+   The driver can be unconfigured by passing an empty string.  You cannot
+   change the configuration while the debugger is attached.  Make sure
+   to detach the debugger with the <constant>detach</constant> command
+   prior to trying unconfigure a kgdb I/O driver.
+   </para>
+   <sect1 id="kgdbwait">
+   <title>Kernel parameter: kgdbwait</title>
+   <para>
+   The Kernel command line option <constant>kgdbwait</constant> makes
+   kgdb wait for a debugger connection during booting of a kernel.  You
+   can only use this option you compiled a kgdb I/O driver into the
+   kernel and you specified the I/O driver configuration as a kernel
+   command line option.  The kgdbwait parameter should always follow the
+   configuration parameter for the kgdb I/O driver in the kernel
+   command line else the I/O driver will not be configured prior to
+   asking the kernel to use it to wait.
+   </para>
+   <para>
+   The kernel will stop and wait as early as the I/O driver and
+   architecture will allow when you use this option.  If you build the
+   kgdb I/O driver as a kernel module kgdbwait will not do anything.
+   </para>
+   </sect1>
+  <sect1 id="kgdboc">
+  <title>Kernel parameter: kgdboc</title>
+  <para>
+  The kgdboc driver was originally an abbreviation meant to stand for
+  "kgdb over console".  Kgdboc is designed to work with a single
+  serial port. It was meant to cover the circumstance
+  where you wanted to use a serial console as your primary console as
+  well as using it to perform kernel debugging.  Of course you can
+  also use kgdboc without assigning a console to the same port.
+  </para>
+  <sect2 id="UsingKgdboc">
+  <title>Using kgdboc</title>
+  <para>
+  You can configure kgdboc via sysfs or a module or kernel boot line
+  parameter depending on if you build with CONFIG_KGDBOC as a module
+  or built-in.
+  <orderedlist>
+  <listitem><para>From the module load or build-in</para>
+  <para><constant>kgdboc=&lt;tty-device&gt;,[baud]</constant></para>
+  <para>
+  The example here would be if your console port was typically ttyS0, you would use something like <constant>kgdboc=ttyS0,115200</constant> or on the ARM Versatile AB you would likely use <constant>kgdboc=ttyAMA0,115200</constant>
+  </para>
+  </listitem>
+  <listitem><para>From sysfs</para>
+  <para><constant>echo ttyS0 &gt; /sys/module/kgdboc/parameters/kgdboc</constant></para>
+  </listitem>
+  </orderedlist>
+  </para>
+  <para>
+  NOTE: Kgdboc does not support interrupting the target via the
+  gdb remote protocol.  You must manually send a sysrq-g unless you
+  have a proxy that splits console output to a terminal problem and
+  has a separate port for the debugger to connect to that sends the
+  sysrq-g for you.
+  </para>
+  <para>When using kgdboc with no debugger proxy, you can end up
+  connecting the debugger for one of two entry points.  If an
+  exception occurs after you have loaded kgdboc a message should print
+  on the console stating it is waiting for the debugger.  In case you
+  disconnect your terminal program and then connect the debugger in
+  its place.  If you want to interrupt the target system and forcibly
+  enter a debug session you have to issue a Sysrq sequence and then
+  type the letter <constant>g</constant>.  Then you disconnect the
+  terminal session and connect gdb.  Your options if you don't like
+  this are to hack gdb to send the sysrq-g for you as well as on the
+  initial connect, or to use a debugger proxy that allows an
+  unmodified gdb to do the debugging.
+  </para>
+  </sect2>
+  </sect1>
+  <sect1 id="kgdbcon">
+  <title>Kernel parameter: kgdbcon</title>
+  <para>
+  Kgdb supports using the gdb serial protocol to send console messages
+  to the debugger when the debugger is connected and running.  There
+  are two ways to activate this feature.
+  <orderedlist>
+  <listitem><para>Activate with the kernel command line option:</para>
+  <para><constant>kgdbcon</constant></para>
+  </listitem>
+  <listitem><para>Use sysfs before configuring an io driver</para>
+  <para>
+  <constant>echo 1 &gt; /sys/module/kgdb/parameters/kgdb_use_con</constant>
+  </para>
+  <para>
+  NOTE: If you do this after you configure the kgdb I/O driver, the
+  setting will not take effect until the next point the I/O is
+  reconfigured.
+  </para>
+  </listitem>
+  </orderedlist>
+  </para>
+  <para>
+  IMPORTANT NOTE: Using this option with kgdb over the console
+  (kgdboc) or kgdb over ethernet (kgdboe) is not supported.
+  </para>
+  </sect1>
+  </chapter>
+  <chapter id="ConnectingGDB">
+  <title>Connecting gdb</title>
+    <para>
+    If you are using kgdboc, you need to have used kgdbwait as a boot
+    argument, issued a sysrq-g, or the system you are going to debug
+    has already taken an exception and is waiting for the debugger to
+    attach before you can connect gdb.
+    </para>
+    <para>
+    If you are not using different kgdb I/O driver other than kgdboc,
+    you should be able to connect and the target will automatically
+    respond.
+    </para>
+    <para>
+    Example (using a serial port):
+    </para>
+    <programlisting>
+    % gdb ./vmlinux
+    (gdb) set remotebaud 115200
+    (gdb) target remote /dev/ttyS0
+    </programlisting>
+    <para>
+    Example (kgdb to a terminal server):
+    </para>
+    <programlisting>
+    % gdb ./vmlinux
+    (gdb) target remote udp:192.168.2.2:6443
+    </programlisting>
+    <para>
+    Example (kgdb over 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>
+    <para>
+    If you are having problems connecting or something is going
+    seriously wrong while debugging, it will most often be the case
+    that you want to enable gdb to be verbose about its target
+    communications.  You do this prior to issuing the <constant>target
+    remote</constant> command by typing in: <constant>set remote debug 1</constant>
+    </para>
+  </chapter>
+  <chapter id="KGDBTestSuite">
+    <title>kgdb Test Suite</title>
+    <para>
+    When kgdb is enabled in the kernel config you can also elect to
+    enable the config parameter KGDB_TESTS.  Turning this on will
+    enable a special kgdb I/O module which is designed to test the
+    kgdb internal functions.
+    </para>
+    <para>
+    The kgdb tests are mainly intended for developers to test the kgdb
+    internals as well as a tool for developing a new kgdb architecture
+    specific implementation.  These tests are not really for end users
+    of the Linux kernel.  The primary source of documentation would be
+    to look in the drivers/misc/kgdbts.c file.
+    </para>
+    <para>
+    The kgdb test suite can also be configured at compile time to run
+    the core set of tests by setting the kernel config parameter
+    KGDB_TESTS_ON_BOOT.  This particular option is aimed at automated
+    regression testing and does not require modifying the kernel boot
+    config arguments.  If this is turned on, the kgdb test suite can
+    be disabled by specifying "kgdbts=" as a kernel boot argument.
+    </para>
+  </chapter>
+  <chapter id="CommonBackEndReq">
+  <title>KGDB Internals</title>
+  <sect1 id="kgdbArchitecture">
+    <title>Architecture Specifics</title>
+      <para>
+      Kgdb is organized into three basic components:
+      <orderedlist>
+      <listitem><para>kgdb core</para>
+      <para>
+      The kgdb core is found in kernel/kgdb.c.  It contains:
+      <itemizedlist>
+      <listitem><para>All the logic to implement the gdb serial protocol</para></listitem>
+      <listitem><para>A generic OS exception handler which includes sync'ing the processors into a stopped state on an multi cpu system.</para></listitem>
+      <listitem><para>The API to talk to the kgdb I/O drivers</para></listitem>
+      <listitem><para>The API to make calls to the arch specific kgdb implementation</para></listitem>
+      <listitem><para>The logic to perform safe memory reads and writes to memory while using the debugger</para></listitem>
+      <listitem><para>A full implementation for software breakpoints unless overridden by the arch</para></listitem>
+      </itemizedlist>
+      </para>
+      </listitem>
+      <listitem><para>kgdb arch specific implementation</para>
+      <para>
+      This implementation is generally found in arch/*/kernel/kgdb.c.
+      As an example, arch/x86/kernel/kgdb.c contains the specifics to
+      implement HW breakpoint as well as the initialization to
+      dynamically register and unregister for the trap handlers on
+      this architecture.  The arch specific portion implements:
+      <itemizedlist>
+      <listitem><para>contains an arch specific trap catcher which
+      invokes kgdb_handle_exception() to start kgdb about doing its
+      work</para></listitem>
+      <listitem><para>translation to and from gdb specific packet format to pt_regs</para></listitem>
+      <listitem><para>Registration and unregistration of architecture specific trap hooks</para></listitem>
+      <listitem><para>Any special exception handling and cleanup</para></listitem>
+      <listitem><para>NMI exception handling and cleanup</para></listitem>
+      <listitem><para>(optional)HW breakpoints</para></listitem>
+      </itemizedlist>
+      </para>
+      </listitem>
+      <listitem><para>kgdb I/O driver</para>
+      <para>
+      Each kgdb I/O driver has to provide an implemenation for the following:
+      <itemizedlist>
+      <listitem><para>configuration via builtin or module</para></listitem>
+      <listitem><para>dynamic configuration and kgdb hook registration calls</para></listitem>
+      <listitem><para>read and write character interface</para></listitem>
+      <listitem><para>A cleanup handler for unconfiguring from the kgdb core</para></listitem>
+      <listitem><para>(optional) Early debug methodology</para></listitem>
+      </itemizedlist>
+      Any given kgdb I/O driver has to operate very closely with the
+      hardware and must do it in such a way that does not enable
+      interrupts or change other parts of the system context without
+      completely restoring them. The kgdb core will repeatedly "poll"
+      a kgdb I/O driver for characters when it needs input.  The I/O
+      driver is expected to return immediately if there is no data
+      available.  Doing so allows for the future possibility to touch
+      watch dog hardware in such a way as to have a target system not
+      reset when these are enabled.
+      </para>
+      </listitem>
+      </orderedlist>
+      </para>
+      <para>
+      If you are intent on adding kgdb architecture specific support
+      for a new architecture, the architecture should define
+      <constant>HAVE_ARCH_KGDB</constant> in the architecture specific
+      Kconfig file.  This will enable kgdb for the architecture, and
+      at that point you must create an architecture specific kgdb
+      implementation.
+      </para>
+      <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 1 if it is 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 unless marked as (optional), in
+      which case a default function maybe used if the architecture
+      does not need to provide a specific implementation.
+      </para>
+!Iinclude/linux/kgdb.h
+  </sect1>
+  <sect1 id="kgdbocDesign">
+  <title>kgdboc internals</title>
+  <para>
+  The kgdboc driver is actually a very thin driver that relies on the
+  underlying low level to the hardware driver having "polling hooks"
+  which the to which the tty driver is attached.  In the initial
+  implementation of kgdboc it the serial_core was changed to expose a
+  low level uart hook for doing polled mode reading and writing of a
+  single character while in an atomic context.  When kgdb makes an I/O
+  request to the debugger, kgdboc invokes a call back in the serial
+  core which in turn uses the call back in the uart driver.  It is
+  certainly possible to extend kgdboc to work with non-uart based
+  consoles in the future.
+  </para>
+  <para>
+  When using kgdboc with a uart, the uart driver must implement two callbacks in the <constant>struct uart_ops</constant>. Example from drivers/8250.c:<programlisting>
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char = serial8250_get_poll_char,
+       .poll_put_char = serial8250_put_poll_char,
+#endif
+  </programlisting>
+  Any implementation specifics around creating a polling driver use the
+  <constant>#ifdef CONFIG_CONSOLE_POLL</constant>, as shown above.
+  Keep in mind that polling hooks have to be implemented in such a way
+  that they can be called from an atomic context and have to restore
+  the state of the uart chip on return such that the system can return
+  to normal when the debugger detaches.  You need to be very careful
+  with any kind of lock you consider, because failing here is most
+  going to mean pressing the reset button.
+  </para>
+  </sect1>
+  </chapter>
+  <chapter id="credits">
+     <title>Credits</title>
+       <para>
+               The following people have contributed to this document:
+               <orderedlist>
+                       <listitem><para>Amit Kale<email>amitkale@linsyssoft.com</email></para></listitem>
+                       <listitem><para>Tom Rini<email>trini@kernel.crashing.org</email></para></listitem>
+               </orderedlist>
+                In March 2008 this document was completely rewritten by:
+               <itemizedlist>
+               <listitem><para>Jason Wessel<email>jason.wessel@windriver.com</email></para></listitem>
+               </itemizedlist>
+       </para>
+  </chapter>
+</book>
+
index f9ea080..c867f50 100644 (file)
@@ -941,6 +941,11 @@ and is between 256 and 4096 characters. It is defined in the file
        kstack=N        [X86-32,X86-64] Print N words from the kernel stack
                        in oops dumps.
 
+       kgdboc=         [HW] kgdb over consoles.
+                       Requires a tty driver that supports console polling.
+                       (only serial suported for now)
+                       Format: <serial_device>[,baud]
+
        l2cr=           [PPC]
 
        lapic           [X86-32,APIC] Enable the local APIC even if BIOS
index e467758..3eceebb 100644 (file)
@@ -2319,6 +2319,12 @@ L:       linux-kernel@vger.kernel.org
 L:     kexec@lists.infradead.org
 S:     Maintained
 
+KGDB
+P:     Jason Wessel
+M:     jason.wessel@windriver.com
+L:     kgdb-bugreport@lists.sourceforge.net
+S:     Maintained
+
 KPROBES
 P:     Ananth N Mavinakayanahalli
 M:     ananth@in.ibm.com
index 701c4a2..2a59dbb 100644 (file)
@@ -23,6 +23,7 @@ config X86
        select HAVE_KPROBES
        select HAVE_KRETPROBES
        select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64)
+       select HAVE_ARCH_KGDB
 
 
 config GENERIC_LOCKBREAK
index 530ed6a..c3920ea 100644 (file)
@@ -67,6 +67,7 @@ obj-$(CONFIG_MODULES)         += module_$(BITS).o
 obj-$(CONFIG_ACPI_SRAT)        += srat_32.o
 obj-$(CONFIG_EFI)              += efi.o efi_$(BITS).o efi_stub_$(BITS).o
 obj-$(CONFIG_DOUBLEFAULT)      += doublefault_32.o
+obj-$(CONFIG_KGDB)             += kgdb.o
 obj-$(CONFIG_VM86)             += vm86_32.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
 
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..8c7e555
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * 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 <amitkale@linsyssoft.com>
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd.
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ * Copyright (C) 2007-2008 Jason Wessel, Wind River Systems, Inc.
+ */
+/****************************************************************************
+ *  Contributor:     Lake Stevens Instrument Division$
+ *  Written by:      Glenn Engel $
+ *  Updated by:             Amit Kale<akale@veritas.com>
+ *  Updated by:             Tom Rini <trini@kernel.crashing.org>
+ *  Updated by:             Jason Wessel <jason.wessel@windriver.com>
+ *  Modified for 386 by Jim Kingdon, Cygnus Support.
+ *  Origianl kgdb, compatibility with 2.1.xx kernel by
+ *  David Grothe <dave@gcom.com>
+ *  Integrated into 2.2.5 kernel by Tigran Aivazian <tigran@sco.com>
+ *  X86_64 changes from Andi Kleen's patch merged by Jim Houston
+ */
+#include <linux/spinlock.h>
+#include <linux/kdebug.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/nmi.h>
+
+#include <asm/apicdef.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_X86_32
+# include <mach_ipi.h>
+#else
+# include <asm/mach_apic.h>
+#endif
+
+/*
+ * Put the error code here just in case the user cares:
+ */
+static int gdb_x86errcode;
+
+/*
+ * Likewise, the vector number here (since GDB only gets the signal
+ * number through the usual means, and that's not very specific):
+ */
+static int gdb_x86vector = -1;
+
+/**
+ *     pt_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.
+ */
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       gdb_regs[GDB_AX]        = regs->ax;
+       gdb_regs[GDB_BX]        = regs->bx;
+       gdb_regs[GDB_CX]        = regs->cx;
+       gdb_regs[GDB_DX]        = regs->dx;
+       gdb_regs[GDB_SI]        = regs->si;
+       gdb_regs[GDB_DI]        = regs->di;
+       gdb_regs[GDB_BP]        = regs->bp;
+       gdb_regs[GDB_PS]        = regs->flags;
+       gdb_regs[GDB_PC]        = regs->ip;
+#ifdef CONFIG_X86_32
+       gdb_regs[GDB_DS]        = regs->ds;
+       gdb_regs[GDB_ES]        = regs->es;
+       gdb_regs[GDB_CS]        = regs->cs;
+       gdb_regs[GDB_SS]        = __KERNEL_DS;
+       gdb_regs[GDB_FS]        = 0xFFFF;
+       gdb_regs[GDB_GS]        = 0xFFFF;
+#else
+       gdb_regs[GDB_R8]        = regs->r8;
+       gdb_regs[GDB_R9]        = regs->r9;
+       gdb_regs[GDB_R10]       = regs->r10;
+       gdb_regs[GDB_R11]       = regs->r11;
+       gdb_regs[GDB_R12]       = regs->r12;
+       gdb_regs[GDB_R13]       = regs->r13;
+       gdb_regs[GDB_R14]       = regs->r14;
+       gdb_regs[GDB_R15]       = regs->r15;
+#endif
+       gdb_regs[GDB_SP]        = regs->sp;
+}
+
+/**
+ *     sleeping_thread_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.
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+       gdb_regs[GDB_AX]        = 0;
+       gdb_regs[GDB_BX]        = 0;
+       gdb_regs[GDB_CX]        = 0;
+       gdb_regs[GDB_DX]        = 0;
+       gdb_regs[GDB_SI]        = 0;
+       gdb_regs[GDB_DI]        = 0;
+       gdb_regs[GDB_BP]        = *(unsigned long *)p->thread.sp;
+#ifdef CONFIG_X86_32
+       gdb_regs[GDB_DS]        = __KERNEL_DS;
+       gdb_regs[GDB_ES]        = __KERNEL_DS;
+       gdb_regs[GDB_PS]        = 0;
+       gdb_regs[GDB_CS]        = __KERNEL_CS;
+       gdb_regs[GDB_PC]        = p->thread.ip;
+       gdb_regs[GDB_SS]        = __KERNEL_DS;
+       gdb_regs[GDB_FS]        = 0xFFFF;
+       gdb_regs[GDB_GS]        = 0xFFFF;
+#else
+       gdb_regs[GDB_PS]        = *(unsigned long *)(p->thread.sp + 8);
+       gdb_regs[GDB_PC]        = 0;
+       gdb_regs[GDB_R8]        = 0;
+       gdb_regs[GDB_R9]        = 0;
+       gdb_regs[GDB_R10]       = 0;
+       gdb_regs[GDB_R11]       = 0;
+       gdb_regs[GDB_R12]       = 0;
+       gdb_regs[GDB_R13]       = 0;
+       gdb_regs[GDB_R14]       = 0;
+       gdb_regs[GDB_R15]       = 0;
+#endif
+       gdb_regs[GDB_SP]        = p->thread.sp;
+}
+
+/**
+ *     gdb_regs_to_pt_regs - Convert GDB regs to ptrace regs.
+ *     @gdb_regs: A pointer to hold the registers we've received 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.
+ */
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       regs->ax                = gdb_regs[GDB_AX];
+       regs->bx                = gdb_regs[GDB_BX];
+       regs->cx                = gdb_regs[GDB_CX];
+       regs->dx                = gdb_regs[GDB_DX];
+       regs->si                = gdb_regs[GDB_SI];
+       regs->di                = gdb_regs[GDB_DI];
+       regs->bp                = gdb_regs[GDB_BP];
+       regs->flags             = gdb_regs[GDB_PS];
+       regs->ip                = gdb_regs[GDB_PC];
+#ifdef CONFIG_X86_32
+       regs->ds                = gdb_regs[GDB_DS];
+       regs->es                = gdb_regs[GDB_ES];
+       regs->cs                = gdb_regs[GDB_CS];
+#else
+       regs->r8                = gdb_regs[GDB_R8];
+       regs->r9                = gdb_regs[GDB_R9];
+       regs->r10               = gdb_regs[GDB_R10];
+       regs->r11               = gdb_regs[GDB_R11];
+       regs->r12               = gdb_regs[GDB_R12];
+       regs->r13               = gdb_regs[GDB_R13];
+       regs->r14               = gdb_regs[GDB_R14];
+       regs->r15               = gdb_regs[GDB_R15];
+#endif
+}
+
+static struct hw_breakpoint {
+       unsigned                enabled;
+       unsigned                type;
+       unsigned                len;
+       unsigned long           addr;
+} breakinfo[4];
+
+static void kgdb_correct_hw_break(void)
+{
+       unsigned long dr7;
+       int correctit = 0;
+       int breakbit;
+       int breakno;
+
+       get_debugreg(dr7, 7);
+       for (breakno = 0; breakno < 4; breakno++) {
+               breakbit = 2 << (breakno << 1);
+               if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+                       correctit = 1;
+                       dr7 |= breakbit;
+                       dr7 &= ~(0xf0000 << (breakno << 2));
+                       dr7 |= ((breakinfo[breakno].len << 2) |
+                                breakinfo[breakno].type) <<
+                              ((breakno << 2) + 16);
+                       if (breakno >= 0 && breakno <= 3)
+                               set_debugreg(breakinfo[breakno].addr, breakno);
+
+               } else {
+                       if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+                               correctit = 1;
+                               dr7 &= ~breakbit;
+                               dr7 &= ~(0xf0000 << (breakno << 2));
+                       }
+               }
+       }
+       if (correctit)
+               set_debugreg(dr7, 7);
+}
+
+static int
+kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
+{
+       int i;
+
+       for (i = 0; i < 4; i++)
+               if (breakinfo[i].addr == addr && breakinfo[i].enabled)
+                       break;
+       if (i == 4)
+               return -1;
+
+       breakinfo[i].enabled = 0;
+
+       return 0;
+}
+
+static void kgdb_remove_all_hw_break(void)
+{
+       int i;
+
+       for (i = 0; i < 4; i++)
+               memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+}
+
+static int
+kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
+{
+       unsigned type;
+       int i;
+
+       for (i = 0; i < 4; i++)
+               if (!breakinfo[i].enabled)
+                       break;
+       if (i == 4)
+               return -1;
+
+       switch (bptype) {
+       case BP_HARDWARE_BREAKPOINT:
+               type = 0;
+               len  = 1;
+               break;
+       case BP_WRITE_WATCHPOINT:
+               type = 1;
+               break;
+       case BP_ACCESS_WATCHPOINT:
+               type = 3;
+               break;
+       default:
+               return -1;
+       }
+
+       if (len == 1 || len == 2 || len == 4)
+               breakinfo[i].len  = len - 1;
+       else
+               return -1;
+
+       breakinfo[i].enabled = 1;
+       breakinfo[i].addr = addr;
+       breakinfo[i].type = type;
+
+       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 kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+       /* Disable hardware debugging while we are in kgdb: */
+       set_debugreg(0UL, 7);
+}
+
+/**
+ *     kgdb_post_primary_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 primary CPU has control over KGDB.
+ */
+void kgdb_post_primary_code(struct pt_regs *regs, int e_vector, int err_code)
+{
+       /* primary processor is completely in the debugger */
+       gdb_x86vector = e_vector;
+       gdb_x86errcode = err_code;
+}
+
+#ifdef CONFIG_SMP
+/**
+ *     kgdb_roundup_cpus - Get other CPUs into a holding pattern
+ *     @flags: Current IRQ state
+ *
+ *     On SMP systems, we need to get the attention of the other CPUs
+ *     and get them be in a known state.  This should do what is needed
+ *     to get the other CPUs to call kgdb_wait(). Note that on some arches,
+ *     the NMI approach is not used for rounding up all the CPUs. For example,
+ *     in case of MIPS, smp_call_function() is used to roundup CPUs. In
+ *     this case, we have to make sure that interrupts are enabled before
+ *     calling smp_call_function(). The argument to this function is
+ *     the flags that will be used when restoring the interrupts. There is
+ *     local_irq_save() call before kgdb_roundup_cpus().
+ *
+ *     On non-SMP systems, this is not called.
+ */
+void kgdb_roundup_cpus(unsigned long flags)
+{
+       send_IPI_allbutself(APIC_DM_NMI);
+}
+#endif
+
+/**
+ *     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 bytes 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 callback.
+ */
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+                              char *remcomInBuffer, char *remcomOutBuffer,
+                              struct pt_regs *linux_regs)
+{
+       unsigned long addr;
+       unsigned long dr6;
+       char *ptr;
+       int newPC;
+
+       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->ip = addr;
+       case 'D':
+       case 'k':
+               newPC = linux_regs->ip;
+
+               /* clear the trace bit */
+               linux_regs->flags &= ~TF_MASK;
+               atomic_set(&kgdb_cpu_doing_single_step, -1);
+
+               /* set the trace bit if we're stepping */
+               if (remcomInBuffer[0] == 's') {
+                       linux_regs->flags |= TF_MASK;
+                       kgdb_single_step = 1;
+                       if (kgdb_contthread) {
+                               atomic_set(&kgdb_cpu_doing_single_step,
+                                          raw_smp_processor_id());
+                       }
+               }
+
+               get_debugreg(dr6, 6);
+               if (!(dr6 & 0x4000)) {
+                       int breakno;
+
+                       for (breakno = 0; breakno < 4; breakno++) {
+                               if (dr6 & (1 << breakno) &&
+                                   breakinfo[breakno].type == 0) {
+                                       /* Set restore flag: */
+                                       linux_regs->flags |= X86_EFLAGS_RF;
+                                       break;
+                               }
+                       }
+               }
+               set_debugreg(0UL, 6);
+               kgdb_correct_hw_break();
+
+               return 0;
+       }
+
+       /* this means that we do not want to exit from the handler: */
+       return -1;
+}
+
+static inline int
+single_step_cont(struct pt_regs *regs, struct die_args *args)
+{
+       /*
+        * Single step exception from kernel space to user space so
+        * eat the exception and continue the process:
+        */
+       printk(KERN_ERR "KGDB: trap/step from kernel to user space, "
+                       "resuming...\n");
+       kgdb_arch_handle_exception(args->trapnr, args->signr,
+                                  args->err, "c", "", regs);
+
+       return NOTIFY_STOP;
+}
+
+static int was_in_debug_nmi[NR_CPUS];
+
+static int __kgdb_notify(struct die_args *args, unsigned long cmd)
+{
+       struct pt_regs *regs = args->regs;
+
+       switch (cmd) {
+       case DIE_NMI:
+               if (atomic_read(&kgdb_active) != -1) {
+                       /* KGDB CPU roundup */
+                       kgdb_nmicallback(raw_smp_processor_id(), regs);
+                       was_in_debug_nmi[raw_smp_processor_id()] = 1;
+                       touch_nmi_watchdog();
+                       return NOTIFY_STOP;
+               }
+               return NOTIFY_DONE;
+
+       case DIE_NMI_IPI:
+               if (atomic_read(&kgdb_active) != -1) {
+                       /* KGDB CPU roundup */
+                       kgdb_nmicallback(raw_smp_processor_id(), regs);
+                       was_in_debug_nmi[raw_smp_processor_id()] = 1;
+                       touch_nmi_watchdog();
+               }
+               return NOTIFY_DONE;
+
+       case DIE_NMIUNKNOWN:
+               if (was_in_debug_nmi[raw_smp_processor_id()]) {
+                       was_in_debug_nmi[raw_smp_processor_id()] = 0;
+                       return NOTIFY_STOP;
+               }
+               return NOTIFY_DONE;
+
+       case DIE_NMIWATCHDOG:
+               if (atomic_read(&kgdb_active) != -1) {
+                       /* KGDB CPU roundup: */
+                       kgdb_nmicallback(raw_smp_processor_id(), regs);
+                       return NOTIFY_STOP;
+               }
+               /* Enter debugger: */
+               break;
+
+       case DIE_DEBUG:
+               if (atomic_read(&kgdb_cpu_doing_single_step) ==
+                       raw_smp_processor_id() &&
+                       user_mode(regs))
+                       return single_step_cont(regs, args);
+               /* fall through */
+       default:
+               if (user_mode(regs))
+                       return NOTIFY_DONE;
+       }
+
+       if (kgdb_handle_exception(args->trapnr, args->signr, args->err, regs))
+               return NOTIFY_DONE;
+
+       /* Must touch watchdog before return to normal operation */
+       touch_nmi_watchdog();
+       return NOTIFY_STOP;
+}
+
+static int
+kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
+{
+       unsigned long flags;
+       int ret;
+
+       local_irq_save(flags);
+       ret = __kgdb_notify(ptr, cmd);
+       local_irq_restore(flags);
+
+       return ret;
+}
+
+static struct notifier_block kgdb_notifier = {
+       .notifier_call  = kgdb_notify,
+
+       /*
+        * Lowest-prio notifier priority, we want to be notified last:
+        */
+       .priority       = -INT_MAX,
+};
+
+/**
+ *     kgdb_arch_init - Perform any architecture specific initalization.
+ *
+ *     This function will handle the initalization of any architecture
+ *     specific callbacks.
+ */
+int kgdb_arch_init(void)
+{
+       return register_die_notifier(&kgdb_notifier);
+}
+
+/**
+ *     kgdb_arch_exit - Perform any architecture specific uninitalization.
+ *
+ *     This function will handle the uninitalization of any architecture
+ *     specific callbacks, for dynamic registration and unregistration.
+ */
+void kgdb_arch_exit(void)
+{
+       unregister_die_notifier(&kgdb_notifier);
+}
+
+/**
+ *
+ *     kgdb_skipexception - Bail out of KGDB when we've been triggered.
+ *     @exception: Exception vector number
+ *     @regs: Current &struct pt_regs.
+ *
+ *     On some architectures we need to skip a breakpoint exception when
+ *     it occurs after a breakpoint has been removed.
+ *
+ * Skip an int3 exception when it occurs after a breakpoint has been
+ * removed. Backtrack eip by 1 since the int3 would have caused it to
+ * increment by 1.
+ */
+int kgdb_skipexception(int exception, struct pt_regs *regs)
+{
+       if (exception == 3 && kgdb_isremovedbreak(regs->ip - 1)) {
+               regs->ip -= 1;
+               return 1;
+       }
+       return 0;
+}
+
+unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
+{
+       if (exception == 3)
+               return instruction_pointer(regs) - 1;
+       return instruction_pointer(regs);
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+       /* Breakpoint instruction: */
+       .gdb_bpt_instr          = { 0xcc },
+       .flags                  = KGDB_HW_BREAKPOINT,
+       .set_hw_breakpoint      = kgdb_set_hw_break,
+       .remove_hw_breakpoint   = kgdb_remove_hw_break,
+       .remove_all_hw_break    = kgdb_remove_all_hw_break,
+       .correct_hw_break       = kgdb_correct_hw_break,
+};
index 4be499c..9042fb0 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/bootmem.h>
 #include <linux/bitops.h>
 #include <linux/module.h>
+#include <linux/kgdb.h>
 #include <asm/pda.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -251,6 +252,17 @@ void __cpuinit cpu_init (void)
        load_TR_desc();
        load_LDT(&init_mm.context);
 
+#ifdef CONFIG_KGDB
+       /*
+        * If the kgdb is connected no debug regs should be altered.  This
+        * is only applicable when KGDB and a KGDB I/O module are built
+        * into the kernel and you are using early debugging with
+        * kgdbwait. KGDB will control the kernel HW breakpoint registers.
+        */
+       if (kgdb_connected && arch_kgdb_ops.correct_hw_break)
+               arch_kgdb_ops.correct_hw_break();
+       else {
+#endif
        /*
         * Clear all 6 debug registers:
         */
@@ -261,6 +273,10 @@ void __cpuinit cpu_init (void)
        set_debugreg(0UL, 3);
        set_debugreg(0UL, 6);
        set_debugreg(0UL, 7);
+#ifdef CONFIG_KGDB
+       /* If the kgdb is connected no debug regs should be altered. */
+       }
+#endif
 
        fpu_init(); 
 
index bb9107c..65791ca 100644 (file)
@@ -730,6 +730,8 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
 static __kprobes void
 unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 {
+       if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
+               return;
 #ifdef CONFIG_MCA
        /*
         * Might actually be able to figure out what the guilty party
index 33292ac..79aa6fc 100644 (file)
@@ -602,8 +602,13 @@ void die(const char * str, struct pt_regs * regs, long err)
 
 void __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic)
 {
-       unsigned long flags = oops_begin();
+       unsigned long flags;
 
+       if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) ==
+           NOTIFY_STOP)
+               return;
+
+       flags = oops_begin();
        /*
         * We are in trouble anyway, lets at least try
         * to get a message out.
@@ -808,6 +813,8 @@ io_check_error(unsigned char reason, struct pt_regs * regs)
 static __kprobes void
 unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
 {
+       if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
+               return;
        printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n",
                reason);
        printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n");
index 613ec81..4d3c701 100644 (file)
@@ -1155,6 +1155,48 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
        return NULL;
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+
+/**
+ *     tty_find_polling_driver -       find device of a polled tty
+ *     @name: name string to match
+ *     @line: pointer to resulting tty line nr
+ *
+ *     This routine returns a tty driver structure, given a name
+ *     and the condition that the tty driver is capable of polled
+ *     operation.
+ */
+struct tty_driver *tty_find_polling_driver(char *name, int *line)
+{
+       struct tty_driver *p, *res = NULL;
+       int tty_line = 0;
+       char *str;
+
+       mutex_lock(&tty_mutex);
+       /* Search through the tty devices to look for a match */
+       list_for_each_entry(p, &tty_drivers, tty_drivers) {
+               str = name + strlen(p->name);
+               tty_line = simple_strtoul(str, &str, 10);
+               if (*str == ',')
+                       str++;
+               if (*str == '\0')
+                       str = 0;
+
+               if (tty_line >= 0 && tty_line <= p->num && p->poll_init &&
+                               !p->poll_init(p, tty_line, str)) {
+
+                       res = p;
+                       *line = tty_line;
+                       break;
+               }
+       }
+       mutex_unlock(&tty_mutex);
+
+       return res;
+}
+EXPORT_SYMBOL_GPL(tty_find_polling_driver);
+#endif
+
 /**
  *     tty_check_change        -       check for POSIX terminal changes
  *     @tty: tty to check
@@ -3850,6 +3892,11 @@ void tty_set_operations(struct tty_driver *driver,
        driver->write_proc = op->write_proc;
        driver->tiocmget = op->tiocmget;
        driver->tiocmset = op->tiocmset;
+#ifdef CONFIG_CONSOLE_POLL
+       driver->poll_init = op->poll_init;
+       driver->poll_get_char = op->poll_get_char;
+       driver->poll_put_char = op->poll_put_char;
+#endif
 }
 
 
index 3b12f5d..bbc69fd 100644 (file)
@@ -22,3 +22,4 @@ obj-$(CONFIG_FUJITSU_LAPTOP)  += fujitsu-laptop.o
 obj-$(CONFIG_EEPROM_93CX6)     += eeprom_93cx6.o
 obj-$(CONFIG_INTEL_MENLOW)     += intel_menlow.o
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
+obj-$(CONFIG_KGDB_TESTS)       += kgdbts.o
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
new file mode 100644 (file)
index 0000000..6d6286c
--- /dev/null
@@ -0,0 +1,1090 @@
+/*
+ * kgdbts is a test suite for kgdb for the sole purpose of validating
+ * that key pieces of the kgdb internals are working properly such as
+ * HW/SW breakpoints, single stepping, and NMI.
+ *
+ * Created by: Jason Wessel <jason.wessel@windriver.com>
+ *
+ * Copyright (c) 2008 Wind River Systems, 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* Information about the kgdb test suite.
+ * -------------------------------------
+ *
+ * The kgdb test suite is designed as a KGDB I/O module which
+ * simulates the communications that a debugger would have with kgdb.
+ * The tests are broken up in to a line by line and referenced here as
+ * a "get" which is kgdb requesting input and "put" which is kgdb
+ * sending a response.
+ *
+ * The kgdb suite can be invoked from the kernel command line
+ * arguments system or executed dynamically at run time.  The test
+ * suite uses the variable "kgdbts" to obtain the information about
+ * which tests to run and to configure the verbosity level.  The
+ * following are the various characters you can use with the kgdbts=
+ * line:
+ *
+ * When using the "kgdbts=" you only choose one of the following core
+ * test types:
+ * A = Run all the core tests silently
+ * V1 = Run all the core tests with minimal output
+ * V2 = Run all the core tests in debug mode
+ *
+ * You can also specify optional tests:
+ * N## = Go to sleep with interrupts of for ## seconds
+ *       to test the HW NMI watchdog
+ * F## = Break at do_fork for ## iterations
+ * S## = Break at sys_open for ## iterations
+ *
+ * NOTE: that the do_fork and sys_open tests are mutually exclusive.
+ *
+ * To invoke the kgdb test suite from boot you use a kernel start
+ * argument as follows:
+ *     kgdbts=V1 kgdbwait
+ * Or if you wanted to perform the NMI test for 6 seconds and do_fork
+ * test for 100 forks, you could use:
+ *     kgdbts=V1N6F100 kgdbwait
+ *
+ * The test suite can also be invoked at run time with:
+ *     echo kgdbts=V1N6F100 > /sys/module/kgdbts/parameters/kgdbts
+ * Or as another example:
+ *     echo kgdbts=V2 > /sys/module/kgdbts/parameters/kgdbts
+ *
+ * When developing a new kgdb arch specific implementation or
+ * using these tests for the purpose of regression testing,
+ * several invocations are required.
+ *
+ * 1) Boot with the test suite enabled by using the kernel arguments
+ *       "kgdbts=V1F100 kgdbwait"
+ *    ## If kgdb arch specific implementation has NMI use
+ *       "kgdbts=V1N6F100
+ *
+ * 2) After the system boot run the basic test.
+ * echo kgdbts=V1 > /sys/module/kgdbts/parameters/kgdbts
+ *
+ * 3) Run the concurrency tests.  It is best to use n+1
+ *    while loops where n is the number of cpus you have
+ *    in your system.  The example below uses only two
+ *    loops.
+ *
+ * ## This tests break points on sys_open
+ * while [ 1 ] ; do find / > /dev/null 2>&1 ; done &
+ * while [ 1 ] ; do find / > /dev/null 2>&1 ; done &
+ * echo kgdbts=V1S10000 > /sys/module/kgdbts/parameters/kgdbts
+ * fg # and hit control-c
+ * fg # and hit control-c
+ * ## This tests break points on do_fork
+ * while [ 1 ] ; do date > /dev/null ; done &
+ * while [ 1 ] ; do date > /dev/null ; done &
+ * echo kgdbts=V1F1000 > /sys/module/kgdbts/parameters/kgdbts
+ * fg # and hit control-c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/kgdb.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+
+#define v1printk(a...) do { \
+       if (verbose) \
+               printk(KERN_INFO a); \
+       } while (0)
+#define v2printk(a...) do { \
+       if (verbose > 1) \
+               printk(KERN_INFO a); \
+               touch_nmi_watchdog();   \
+       } while (0)
+#define eprintk(a...) do { \
+               printk(KERN_ERR a); \
+               WARN_ON(1); \
+       } while (0)
+#define MAX_CONFIG_LEN         40
+
+static const char hexchars[] = "0123456789abcdef";
+static struct kgdb_io kgdbts_io_ops;
+static char get_buf[BUFMAX];
+static int get_buf_cnt;
+static char put_buf[BUFMAX];
+static int put_buf_cnt;
+static char scratch_buf[BUFMAX];
+static int verbose;
+static int repeat_test;
+static int test_complete;
+static int send_ack;
+static int final_ack;
+static int hw_break_val;
+static int hw_break_val2;
+#if defined(CONFIG_ARM) || defined(CONFIG_MIPS)
+static int arch_needs_sstep_emulation = 1;
+#else
+static int arch_needs_sstep_emulation;
+#endif
+static unsigned long sstep_addr;
+static int sstep_state;
+
+/* Storage for the registers, in GDB format. */
+static unsigned long kgdbts_gdb_regs[(NUMREGBYTES +
+                                       sizeof(unsigned long) - 1) /
+                                       sizeof(unsigned long)];
+static struct pt_regs kgdbts_regs;
+
+/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */
+static int configured          = -1;
+
+#ifdef CONFIG_KGDB_TESTS_BOOT_STRING
+static char config[MAX_CONFIG_LEN] = CONFIG_KGDB_TESTS_BOOT_STRING;
+#else
+static char config[MAX_CONFIG_LEN];
+#endif
+static struct kparam_string kps = {
+       .string                 = config,
+       .maxlen                 = MAX_CONFIG_LEN,
+};
+
+static void fill_get_buf(char *buf);
+
+struct test_struct {
+       char *get;
+       char *put;
+       void (*get_handler)(char *);
+       int (*put_handler)(char *, char *);
+};
+
+struct test_state {
+       char *name;
+       struct test_struct *tst;
+       int idx;
+       int (*run_test) (int, int);
+       int (*validate_put) (char *);
+};
+
+static struct test_state ts;
+
+static int kgdbts_unreg_thread(void *ptr)
+{
+       /* Wait until the tests are complete and then ungresiter the I/O
+        * driver.
+        */
+       while (!final_ack)
+               msleep_interruptible(1500);
+
+       if (configured)
+               kgdb_unregister_io_module(&kgdbts_io_ops);
+       configured = 0;
+
+       return 0;
+}
+
+/* This is noinline such that it can be used for a single location to
+ * place a breakpoint
+ */
+static noinline void kgdbts_break_test(void)
+{
+       v2printk("kgdbts: breakpoint complete\n");
+}
+
+/* Lookup symbol info in the kernel */
+static unsigned long lookup_addr(char *arg)
+{
+       unsigned long addr = 0;
+
+       if (!strcmp(arg, "kgdbts_break_test"))
+               addr = (unsigned long)kgdbts_break_test;
+       else if (!strcmp(arg, "sys_open"))
+               addr = (unsigned long)sys_open;
+       else if (!strcmp(arg, "do_fork"))
+               addr = (unsigned long)do_fork;
+       else if (!strcmp(arg, "hw_break_val"))
+               addr = (unsigned long)&hw_break_val;
+       return addr;
+}
+
+static void break_helper(char *bp_type, char *arg, unsigned long vaddr)
+{
+       unsigned long addr;
+
+       if (arg)
+               addr = lookup_addr(arg);
+       else
+               addr = vaddr;
+
+       sprintf(scratch_buf, "%s,%lx,%i", bp_type, addr,
+               BREAK_INSTR_SIZE);
+       fill_get_buf(scratch_buf);
+}
+
+static void sw_break(char *arg)
+{
+       break_helper("Z0", arg, 0);
+}
+
+static void sw_rem_break(char *arg)
+{
+       break_helper("z0", arg, 0);
+}
+
+static void hw_break(char *arg)
+{
+       break_helper("Z1", arg, 0);
+}
+
+static void hw_rem_break(char *arg)
+{
+       break_helper("z1", arg, 0);
+}
+
+static void hw_write_break(char *arg)
+{
+       break_helper("Z2", arg, 0);
+}
+
+static void hw_rem_write_break(char *arg)
+{
+       break_helper("z2", arg, 0);
+}
+
+static void hw_access_break(char *arg)
+{
+       break_helper("Z4", arg, 0);
+}
+
+static void hw_rem_access_break(char *arg)
+{
+       break_helper("z4", arg, 0);
+}
+
+static void hw_break_val_access(void)
+{
+       hw_break_val2 = hw_break_val;
+}
+
+static void hw_break_val_write(void)
+{
+       hw_break_val++;
+}
+
+static int check_and_rewind_pc(char *put_str, char *arg)
+{
+       unsigned long addr = lookup_addr(arg);
+       int offset = 0;
+
+       kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs,
+                NUMREGBYTES);
+       gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
+       v2printk("Stopped at IP: %lx\n", instruction_pointer(&kgdbts_regs));
+#ifdef CONFIG_X86
+       /* On x86 a breakpoint stop requires it to be decremented */
+       if (addr + 1 == kgdbts_regs.ip)
+               offset = -1;
+#endif
+       if (strcmp(arg, "silent") &&
+               instruction_pointer(&kgdbts_regs) + offset != addr) {
+               eprintk("kgdbts: BP mismatch %lx expected %lx\n",
+                          instruction_pointer(&kgdbts_regs) + offset, addr);
+               return 1;
+       }
+#ifdef CONFIG_X86
+       /* On x86 adjust the instruction pointer if needed */
+       kgdbts_regs.ip += offset;
+#endif
+       return 0;
+}
+
+static int check_single_step(char *put_str, char *arg)
+{
+       unsigned long addr = lookup_addr(arg);
+       /*
+        * From an arch indepent point of view the instruction pointer
+        * should be on a different instruction
+        */
+       kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs,
+                NUMREGBYTES);
+       gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
+       v2printk("Singlestep stopped at IP: %lx\n",
+                  instruction_pointer(&kgdbts_regs));
+       if (instruction_pointer(&kgdbts_regs) == addr) {
+               eprintk("kgdbts: SingleStep failed at %lx\n",
+                          instruction_pointer(&kgdbts_regs));
+               return 1;
+       }
+
+       return 0;
+}
+
+static void write_regs(char *arg)
+{
+       memset(scratch_buf, 0, sizeof(scratch_buf));
+       scratch_buf[0] = 'G';
+       pt_regs_to_gdb_regs(kgdbts_gdb_regs, &kgdbts_regs);
+       kgdb_mem2hex((char *)kgdbts_gdb_regs, &scratch_buf[1], NUMREGBYTES);
+       fill_get_buf(scratch_buf);
+}
+
+static void skip_back_repeat_test(char *arg)
+{
+       int go_back = simple_strtol(arg, NULL, 10);
+
+       repeat_test--;
+       if (repeat_test <= 0)
+               ts.idx++;
+       else
+               ts.idx -= go_back;
+       fill_get_buf(ts.tst[ts.idx].get);
+}
+
+static int got_break(char *put_str, char *arg)
+{
+       test_complete = 1;
+       if (!strncmp(put_str+1, arg, 2)) {
+               if (!strncmp(arg, "T0", 2))
+                       test_complete = 2;
+               return 0;
+       }
+       return 1;
+}
+
+static void emul_sstep_get(char *arg)
+{
+       if (!arch_needs_sstep_emulation) {
+               fill_get_buf(arg);
+               return;
+       }
+       switch (sstep_state) {
+       case 0:
+               v2printk("Emulate single step\n");
+               /* Start by looking at the current PC */
+               fill_get_buf("g");
+               break;
+       case 1:
+               /* set breakpoint */
+               break_helper("Z0", 0, sstep_addr);
+               break;
+       case 2:
+               /* Continue */
+               fill_get_buf("c");
+               break;
+       case 3:
+               /* Clear breakpoint */
+               break_helper("z0", 0, sstep_addr);
+               break;
+       default:
+               eprintk("kgdbts: ERROR failed sstep get emulation\n");
+       }
+       sstep_state++;
+}
+
+static int emul_sstep_put(char *put_str, char *arg)
+{
+       if (!arch_needs_sstep_emulation) {
+               if (!strncmp(put_str+1, arg, 2))
+                       return 0;
+               return 1;
+       }
+       switch (sstep_state) {
+       case 1:
+               /* validate the "g" packet to get the IP */
+               kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs,
+                        NUMREGBYTES);
+               gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
+               v2printk("Stopped at IP: %lx\n",
+                        instruction_pointer(&kgdbts_regs));
+               /* Want to stop at IP + break instruction size by default */
+               sstep_addr = instruction_pointer(&kgdbts_regs) +
+                       BREAK_INSTR_SIZE;
+               break;
+       case 2:
+               if (strncmp(put_str, "$OK", 3)) {
+                       eprintk("kgdbts: failed sstep break set\n");
+                       return 1;
+               }
+               break;
+       case 3:
+               if (strncmp(put_str, "$T0", 3)) {
+                       eprintk("kgdbts: failed continue sstep\n");
+                       return 1;
+               }
+               break;
+       case 4:
+               if (strncmp(put_str, "$OK", 3)) {
+                       eprintk("kgdbts: failed sstep break unset\n");
+                       return 1;
+               }
+               /* Single step is complete so continue on! */
+               sstep_state = 0;
+               return 0;
+       default:
+               eprintk("kgdbts: ERROR failed sstep put emulation\n");
+       }
+
+       /* Continue on the same test line until emulation is complete */
+       ts.idx--;
+       return 0;
+}
+
+static int final_ack_set(char *put_str, char *arg)
+{
+       if (strncmp(put_str+1, arg, 2))
+               return 1;
+       final_ack = 1;
+       return 0;
+}
+/*
+ * Test to plant a breakpoint and detach, which should clear out the
+ * breakpoint and restore the original instruction.
+ */
+static struct test_struct plant_and_detach_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
+       { "D", "OK" }, /* Detach */
+       { "", "" },
+};
+
+/*
+ * Simple test to write in a software breakpoint, check for the
+ * correct stop location and detach.
+ */
+static struct test_struct sw_breakpoint_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
+       { "c", "T0*", }, /* Continue */
+       { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+       { "write", "OK", write_regs },
+       { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
+       { "D", "OK" }, /* Detach */
+       { "D", "OK", 0,  got_break }, /* If the test worked we made it here */
+       { "", "" },
+};
+
+/*
+ * Test a known bad memory read location to test the fault handler and
+ * read bytes 1-8 at the bad address
+ */
+static struct test_struct bad_read_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "m0,1", "E*" }, /* read 1 byte at address 1 */
+       { "m0,2", "E*" }, /* read 1 byte at address 2 */
+       { "m0,3", "E*" }, /* read 1 byte at address 3 */
+       { "m0,4", "E*" }, /* read 1 byte at address 4 */
+       { "m0,5", "E*" }, /* read 1 byte at address 5 */
+       { "m0,6", "E*" }, /* read 1 byte at address 6 */
+       { "m0,7", "E*" }, /* read 1 byte at address 7 */
+       { "m0,8", "E*" }, /* read 1 byte at address 8 */
+       { "D", "OK" }, /* Detach which removes all breakpoints and continues */
+       { "", "" },
+};
+
+/*
+ * Test for hitting a breakpoint, remove it, single step, plant it
+ * again and detach.
+ */
+static struct test_struct singlestep_break_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
+       { "c", "T0*", }, /* Continue */
+       { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+       { "write", "OK", write_regs }, /* Write registers */
+       { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
+       { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
+       { "g", "kgdbts_break_test", 0, check_single_step },
+       { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
+       { "c", "T0*", }, /* Continue */
+       { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+       { "write", "OK", write_regs }, /* Write registers */
+       { "D", "OK" }, /* Remove all breakpoints and continues */
+       { "", "" },
+};
+
+/*
+ * Test for hitting a breakpoint at do_fork for what ever the number
+ * of iterations required by the variable repeat_test.
+ */
+static struct test_struct do_fork_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "do_fork", "OK", sw_break, }, /* set sw breakpoint */
+       { "c", "T0*", }, /* Continue */
+       { "g", "do_fork", 0, check_and_rewind_pc }, /* check location */
+       { "write", "OK", write_regs }, /* Write registers */
+       { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */
+       { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
+       { "g", "do_fork", 0, check_single_step },
+       { "do_fork", "OK", sw_break, }, /* set sw breakpoint */
+       { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
+       { "D", "OK", 0, final_ack_set }, /* detach and unregister I/O */
+       { "", "" },
+};
+
+/* Test for hitting a breakpoint at sys_open for what ever the number
+ * of iterations required by the variable repeat_test.
+ */
+static struct test_struct sys_open_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "sys_open", "OK", sw_break, }, /* set sw breakpoint */
+       { "c", "T0*", }, /* Continue */
+       { "g", "sys_open", 0, check_and_rewind_pc }, /* check location */
+       { "write", "OK", write_regs }, /* Write registers */
+       { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */
+       { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
+       { "g", "sys_open", 0, check_single_step },
+       { "sys_open", "OK", sw_break, }, /* set sw breakpoint */
+       { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
+       { "D", "OK", 0, final_ack_set }, /* detach and unregister I/O */
+       { "", "" },
+};
+
+/*
+ * Test for hitting a simple hw breakpoint
+ */
+static struct test_struct hw_breakpoint_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "kgdbts_break_test", "OK", hw_break, }, /* set hw breakpoint */
+       { "c", "T0*", }, /* Continue */
+       { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+       { "write", "OK", write_regs },
+       { "kgdbts_break_test", "OK", hw_rem_break }, /*remove breakpoint */
+       { "D", "OK" }, /* Detach */
+       { "D", "OK", 0,  got_break }, /* If the test worked we made it here */
+       { "", "" },
+};
+
+/*
+ * Test for hitting a hw write breakpoint
+ */
+static struct test_struct hw_write_break_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "hw_break_val", "OK", hw_write_break, }, /* set hw breakpoint */
+       { "c", "T0*", 0, got_break }, /* Continue */
+       { "g", "silent", 0, check_and_rewind_pc },
+       { "write", "OK", write_regs },
+       { "hw_break_val", "OK", hw_rem_write_break }, /*remove breakpoint */
+       { "D", "OK" }, /* Detach */
+       { "D", "OK", 0,  got_break }, /* If the test worked we made it here */
+       { "", "" },
+};
+
+/*
+ * Test for hitting a hw access breakpoint
+ */
+static struct test_struct hw_access_break_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "hw_break_val", "OK", hw_access_break, }, /* set hw breakpoint */
+       { "c", "T0*", 0, got_break }, /* Continue */
+       { "g", "silent", 0, check_and_rewind_pc },
+       { "write", "OK", write_regs },
+       { "hw_break_val", "OK", hw_rem_access_break }, /*remove breakpoint */
+       { "D", "OK" }, /* Detach */
+       { "D", "OK", 0,  got_break }, /* If the test worked we made it here */
+       { "", "" },
+};
+
+/*
+ * Test for hitting a hw access breakpoint
+ */
+static struct test_struct nmi_sleep_test[] = {
+       { "?", "S0*" }, /* Clear break points */
+       { "c", "T0*", 0, got_break }, /* Continue */
+       { "D", "OK" }, /* Detach */
+       { "D", "OK", 0,  got_break }, /* If the test worked we made it here */
+       { "", "" },
+};
+
+static void fill_get_buf(char *buf)
+{
+       unsigned char checksum = 0;
+       int count = 0;
+       char ch;
+
+       strcpy(get_buf, "$");
+       strcat(get_buf, buf);
+       while ((ch = buf[count])) {
+               checksum += ch;
+               count++;
+       }
+       strcat(get_buf, "#");
+       get_buf[count + 2] = hexchars[checksum >> 4];
+       get_buf[count + 3] = hexchars[checksum & 0xf];
+       get_buf[count + 4] = '\0';
+       v2printk("get%i: %s\n", ts.idx, get_buf);
+}
+
+static int validate_simple_test(char *put_str)
+{
+       char *chk_str;
+
+       if (ts.tst[ts.idx].put_handler)
+               return ts.tst[ts.idx].put_handler(put_str,
+                       ts.tst[ts.idx].put);
+
+       chk_str = ts.tst[ts.idx].put;
+       if (*put_str == '$')
+               put_str++;
+
+       while (*chk_str != '\0' && *put_str != '\0') {
+               /* If someone does a * to match the rest of the string, allow
+                * it, or stop if the recieved string is complete.
+                */
+               if (*put_str == '#' || *chk_str == '*')
+                       return 0;
+               if (*put_str != *chk_str)
+                       return 1;
+
+               chk_str++;
+               put_str++;
+       }
+       if (*chk_str == '\0' && (*put_str == '\0' || *put_str == '#'))
+               return 0;
+
+       return 1;
+}
+
+static int run_simple_test(int is_get_char, int chr)
+{
+       int ret = 0;
+       if (is_get_char) {
+               /* Send an ACK on the get if a prior put completed and set the
+                * send ack variable
+                */
+               if (send_ack) {
+                       send_ack = 0;
+                       return '+';
+               }
+               /* On the first get char, fill the transmit buffer and then
+                * take from the get_string.
+                */
+               if (get_buf_cnt == 0) {
+                       if (ts.tst[ts.idx].get_handler)
+                               ts.tst[ts.idx].get_handler(ts.tst[ts.idx].get);
+                       else
+                               fill_get_buf(ts.tst[ts.idx].get);
+               }
+
+               if (get_buf[get_buf_cnt] == '\0') {
+                       eprintk("kgdbts: ERROR GET: EOB on '%s' at %i\n",
+                          ts.name, ts.idx);
+                       get_buf_cnt = 0;
+                       fill_get_buf("D");
+               }
+               ret = get_buf[get_buf_cnt];
+               get_buf_cnt++;
+               return ret;
+       }
+
+       /* This callback is a put char which is when kgdb sends data to
+        * this I/O module.
+        */
+       if (ts.tst[ts.idx].get[0] == '\0' &&
+               ts.tst[ts.idx].put[0] == '\0') {
+               eprintk("kgdbts: ERROR: beyond end of test on"
+                          " '%s' line %i\n", ts.name, ts.idx);
+               return 0;
+       }
+
+       if (put_buf_cnt >= BUFMAX) {
+               eprintk("kgdbts: ERROR: put buffer overflow on"
+                          " '%s' line %i\n", ts.name, ts.idx);
+               put_buf_cnt = 0;
+               return 0;
+       }
+       /* Ignore everything until the first valid packet start '$' */
+       if (put_buf_cnt == 0 && chr != '$')
+               return 0;
+
+       put_buf[put_buf_cnt] = chr;
+       put_buf_cnt++;
+
+       /* End of packet == #XX so look for the '#' */
+       if (put_buf_cnt > 3 && put_buf[put_buf_cnt - 3] == '#') {
+               put_buf[put_buf_cnt] = '\0';
+               v2printk("put%i: %s\n", ts.idx, put_buf);
+               /* Trigger check here */
+               if (ts.validate_put && ts.validate_put(put_buf)) {
+                       eprintk("kgdbts: ERROR PUT: end of test "
+                          "buffer on '%s' line %i expected %s got %s\n",
+                          ts.name, ts.idx, ts.tst[ts.idx].put, put_buf);
+               }
+               ts.idx++;
+               put_buf_cnt = 0;
+               get_buf_cnt = 0;
+               send_ack = 1;
+       }
+       return 0;
+}
+
+static void init_simple_test(void)
+{
+       memset(&ts, 0, sizeof(ts));
+       ts.run_test = run_simple_test;
+       ts.validate_put = validate_simple_test;
+}
+
+static void run_plant_and_detach_test(int is_early)
+{
+       char before[BREAK_INSTR_SIZE];
+       char after[BREAK_INSTR_SIZE];
+
+       probe_kernel_read(before, (char *)kgdbts_break_test,
+         BREAK_INSTR_SIZE);
+       init_simple_test();
+       ts.tst = plant_and_detach_test;
+       ts.name = "plant_and_detach_test";
+       /* Activate test with initial breakpoint */
+       if (!is_early)
+               kgdb_breakpoint();
+       probe_kernel_read(after, (char *)kgdbts_break_test,
+         BREAK_INSTR_SIZE);
+       if (memcmp(before, after, BREAK_INSTR_SIZE)) {
+               printk(KERN_CRIT "kgdbts: ERROR kgdb corrupted memory\n");
+               panic("kgdb memory corruption");
+       }
+
+       /* complete the detach test */
+       if (!is_early)
+               kgdbts_break_test();
+}
+
+static void run_breakpoint_test(int is_hw_breakpoint)
+{
+       test_complete = 0;
+       init_simple_test();
+       if (is_hw_breakpoint) {
+               ts.tst = hw_breakpoint_test;
+               ts.name = "hw_breakpoint_test";
+       } else {
+               ts.tst = sw_breakpoint_test;
+               ts.name = "sw_breakpoint_test";
+       }
+       /* Activate test with initial breakpoint */
+       kgdb_breakpoint();
+       /* run code with the break point in it */
+       kgdbts_break_test();
+       kgdb_breakpoint();
+
+       if (test_complete)
+               return;
+
+       eprintk("kgdbts: ERROR %s test failed\n", ts.name);
+}
+
+static void run_hw_break_test(int is_write_test)
+{
+       test_complete = 0;
+       init_simple_test();
+       if (is_write_test) {
+               ts.tst = hw_write_break_test;
+               ts.name = "hw_write_break_test";
+       } else {
+               ts.tst = hw_access_break_test;
+               ts.name = "hw_access_break_test";
+       }
+       /* Activate test with initial breakpoint */
+       kgdb_breakpoint();
+       hw_break_val_access();
+       if (is_write_test) {
+               if (test_complete == 2)
+                       eprintk("kgdbts: ERROR %s broke on access\n",
+                               ts.name);
+               hw_break_val_write();
+       }
+       kgdb_breakpoint();
+
+       if (test_complete == 1)
+               return;
+
+       eprintk("kgdbts: ERROR %s test failed\n", ts.name);
+}
+
+static void run_nmi_sleep_test(int nmi_sleep)
+{
+       unsigned long flags;
+
+       init_simple_test();
+       ts.tst = nmi_sleep_test;
+       ts.name = "nmi_sleep_test";
+       /* Activate test with initial breakpoint */
+       kgdb_breakpoint();
+       local_irq_save(flags);
+       mdelay(nmi_sleep*1000);
+       touch_nmi_watchdog();
+       local_irq_restore(flags);
+       if (test_complete != 2)
+               eprintk("kgdbts: ERROR nmi_test did not hit nmi\n");
+       kgdb_breakpoint();
+       if (test_complete == 1)
+               return;
+
+       eprintk("kgdbts: ERROR %s test failed\n", ts.name);
+}
+
+static void run_bad_read_test(void)
+{
+       init_simple_test();
+       ts.tst = bad_read_test;
+       ts.name = "bad_read_test";
+       /* Activate test with initial breakpoint */
+       kgdb_breakpoint();
+}
+
+static void run_do_fork_test(void)
+{
+       init_simple_test();
+       ts.tst = do_fork_test;
+       ts.name = "do_fork_test";
+       /* Activate test with initial breakpoint */
+       kgdb_breakpoint();
+}
+
+static void run_sys_open_test(void)
+{
+       init_simple_test();
+       ts.tst = sys_open_test;
+       ts.name = "sys_open_test";
+       /* Activate test with initial breakpoint */
+       kgdb_breakpoint();
+}
+
+static void run_singlestep_break_test(void)
+{
+       init_simple_test();
+       ts.tst = singlestep_break_test;
+       ts.name = "singlestep_breakpoint_test";
+       /* Activate test with initial breakpoint */
+       kgdb_breakpoint();
+       kgdbts_break_test();
+       kgdbts_break_test();
+}
+
+static void kgdbts_run_tests(void)
+{
+       char *ptr;
+       int fork_test = 0;
+       int sys_open_test = 0;
+       int nmi_sleep = 0;
+
+       ptr = strstr(config, "F");
+       if (ptr)
+               fork_test = simple_strtol(ptr+1, NULL, 10);
+       ptr = strstr(config, "S");
+       if (ptr)
+               sys_open_test = simple_strtol(ptr+1, NULL, 10);
+       ptr = strstr(config, "N");
+       if (ptr)
+               nmi_sleep = simple_strtol(ptr+1, NULL, 10);
+
+       /* required internal KGDB tests */
+       v1printk("kgdbts:RUN plant and detach test\n");
+       run_plant_and_detach_test(0);
+       v1printk("kgdbts:RUN sw breakpoint test\n");
+       run_breakpoint_test(0);
+       v1printk("kgdbts:RUN bad memory access test\n");
+       run_bad_read_test();
+       v1printk("kgdbts:RUN singlestep breakpoint test\n");
+       run_singlestep_break_test();
+
+       /* ===Optional tests=== */
+
+       /* All HW break point tests */
+       if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) {
+               v1printk("kgdbts:RUN hw breakpoint test\n");
+               run_breakpoint_test(1);
+               v1printk("kgdbts:RUN hw write breakpoint test\n");
+               run_hw_break_test(1);
+               v1printk("kgdbts:RUN access write breakpoint test\n");
+               run_hw_break_test(0);
+       }
+
+       if (nmi_sleep) {
+               v1printk("kgdbts:RUN NMI sleep %i seconds test\n", nmi_sleep);
+               run_nmi_sleep_test(nmi_sleep);
+       }
+
+       /* If the do_fork test is run it will be the last test that is
+        * executed because a kernel thread will be spawned at the very
+        * end to unregister the debug hooks.
+        */
+       if (fork_test) {
+               repeat_test = fork_test;
+               printk(KERN_INFO "kgdbts:RUN do_fork for %i breakpoints\n",
+                       repeat_test);
+               kthread_run(kgdbts_unreg_thread, 0, "kgdbts_unreg");
+               run_do_fork_test();
+               return;
+       }
+
+       /* If the sys_open test is run it will be the last test that is
+        * executed because a kernel thread will be spawned at the very
+        * end to unregister the debug hooks.
+        */
+       if (sys_open_test) {
+               repeat_test = sys_open_test;
+               printk(KERN_INFO "kgdbts:RUN sys_open for %i breakpoints\n",
+                       repeat_test);
+               kthread_run(kgdbts_unreg_thread, 0, "kgdbts_unreg");
+               run_sys_open_test();
+               return;
+       }
+       /* Shutdown and unregister */
+       kgdb_unregister_io_module(&kgdbts_io_ops);
+       configured = 0;
+}
+
+static int kgdbts_option_setup(char *opt)
+{
+       if (strlen(opt) > MAX_CONFIG_LEN) {
+               printk(KERN_ERR "kgdbts: config string too long\n");
+               return -ENOSPC;
+       }
+       strcpy(config, opt);
+
+       verbose = 0;
+       if (strstr(config, "V1"))
+               verbose = 1;
+       if (strstr(config, "V2"))
+               verbose = 2;
+
+       return 0;
+}
+
+__setup("kgdbts=", kgdbts_option_setup);
+
+static int configure_kgdbts(void)
+{
+       int err = 0;
+
+       if (!strlen(config) || isspace(config[0]))
+               goto noconfig;
+       err = kgdbts_option_setup(config);
+       if (err)
+               goto noconfig;
+
+       final_ack = 0;
+       run_plant_and_detach_test(1);
+
+       err = kgdb_register_io_module(&kgdbts_io_ops);
+       if (err) {
+               configured = 0;
+               return err;
+       }
+       configured = 1;
+       kgdbts_run_tests();
+
+       return err;
+
+noconfig:
+       config[0] = 0;
+       configured = 0;
+
+       return err;
+}
+
+static int __init init_kgdbts(void)
+{
+       /* Already configured? */
+       if (configured == 1)
+               return 0;
+
+       return configure_kgdbts();
+}
+
+static void cleanup_kgdbts(void)
+{
+       if (configured == 1)
+               kgdb_unregister_io_module(&kgdbts_io_ops);
+}
+
+static int kgdbts_get_char(void)
+{
+       int val = 0;
+
+       if (ts.run_test)
+               val = ts.run_test(1, 0);
+
+       return val;
+}
+
+static void kgdbts_put_char(u8 chr)
+{
+       if (ts.run_test)
+               ts.run_test(0, chr);
+}
+
+static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp)
+{
+       int len = strlen(kmessage);
+
+       if (len >= MAX_CONFIG_LEN) {
+               printk(KERN_ERR "kgdbts: config string too long\n");
+               return -ENOSPC;
+       }
+
+       /* Only copy in the string if the init function has not run yet */
+       if (configured < 0) {
+               strcpy(config, kmessage);
+               return 0;
+       }
+
+       if (kgdb_connected) {
+               printk(KERN_ERR
+              "kgdbts: Cannot reconfigure while KGDB is connected.\n");
+
+               return -EBUSY;
+       }
+
+       strcpy(config, kmessage);
+       /* Chop out \n char as a result of echo */
+       if (config[len - 1] == '\n')
+               config[len - 1] = '\0';
+
+       if (configured == 1)
+               cleanup_kgdbts();
+
+       /* Go and configure with the new params. */
+       return configure_kgdbts();
+}
+
+static void kgdbts_pre_exp_handler(void)
+{
+       /* Increment the module count when the debugger is active */
+       if (!kgdb_connected)
+               try_module_get(THIS_MODULE);
+}
+
+static void kgdbts_post_exp_handler(void)
+{
+       /* decrement the module count when the debugger detaches */
+       if (!kgdb_connected)
+               module_put(THIS_MODULE);
+}
+
+static struct kgdb_io kgdbts_io_ops = {
+       .name                   = "kgdbts",
+       .read_char              = kgdbts_get_char,
+       .write_char             = kgdbts_put_char,
+       .pre_exception          = kgdbts_pre_exp_handler,
+       .post_exception         = kgdbts_post_exp_handler,
+};
+
+module_init(init_kgdbts);
+module_exit(cleanup_kgdbts);
+module_param_call(kgdbts, param_set_kgdbts_var, param_get_string, &kps, 0644);
+MODULE_PARM_DESC(kgdbts, "<A|V1|V2>[F#|S#][N#]");
+MODULE_DESCRIPTION("KGDB Test Suite");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wind River Systems, Inc.");
+
index 77f7a7f..96a585e 100644 (file)
@@ -1740,6 +1740,60 @@ static inline void wait_for_xmitr(struct uart_8250_port *up, int bits)
        }
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int serial8250_get_poll_char(struct uart_port *port)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned char lsr = serial_inp(up, UART_LSR);
+
+       while (!(lsr & UART_LSR_DR))
+               lsr = serial_inp(up, UART_LSR);
+
+       return serial_inp(up, UART_RX);
+}
+
+
+static void serial8250_put_poll_char(struct uart_port *port,
+                        unsigned char c)
+{
+       unsigned int ier;
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+       /*
+        *      First save the IER then disable the interrupts
+        */
+       ier = serial_in(up, UART_IER);
+       if (up->capabilities & UART_CAP_UUE)
+               serial_out(up, UART_IER, UART_IER_UUE);
+       else
+               serial_out(up, UART_IER, 0);
+
+       wait_for_xmitr(up, BOTH_EMPTY);
+       /*
+        *      Send the character out.
+        *      If a LF, also do CR...
+        */
+       serial_out(up, UART_TX, c);
+       if (c == 10) {
+               wait_for_xmitr(up, BOTH_EMPTY);
+               serial_out(up, UART_TX, 13);
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the IER
+        */
+       wait_for_xmitr(up, BOTH_EMPTY);
+       serial_out(up, UART_IER, ier);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
 static int serial8250_startup(struct uart_port *port)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
@@ -2386,6 +2440,10 @@ static struct uart_ops serial8250_pops = {
        .request_port   = serial8250_request_port,
        .config_port    = serial8250_config_port,
        .verify_port    = serial8250_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char = serial8250_get_poll_char,
+       .poll_put_char = serial8250_put_poll_char,
+#endif
 };
 
 static struct uart_8250_port serial8250_ports[UART_NR];
index cf627cd..f7cd950 100644 (file)
@@ -961,6 +961,9 @@ config SERIAL_CORE
 config SERIAL_CORE_CONSOLE
        bool
 
+config CONSOLE_POLL
+       bool
+
 config SERIAL_68328
        bool "68328 serial support"
        depends on M68328 || M68EZ328 || M68VZ328
index 640cfe4..3cbea54 100644 (file)
@@ -66,4 +66,5 @@ obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
+obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
 obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
index 40604a0..08adc1d 100644 (file)
@@ -314,6 +314,32 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
        spin_unlock_irqrestore(&uap->port.lock, flags);
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+static int pl010_get_poll_char(struct uart_port *port)
+{
+       struct uart_amba_port *uap = (struct uart_amba_port *)port;
+       unsigned int status;
+
+       do {
+               status = readw(uap->port.membase + UART01x_FR);
+       } while (status & UART01x_FR_RXFE);
+
+       return readw(uap->port.membase + UART01x_DR);
+}
+
+static void pl010_put_poll_char(struct uart_port *port,
+                        unsigned char ch)
+{
+       struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+       while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+               barrier();
+
+       writew(ch, uap->port.membase + UART01x_DR);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
 static int pl011_startup(struct uart_port *port)
 {
        struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -572,6 +598,10 @@ static struct uart_ops amba_pl011_pops = {
        .request_port   = pl010_request_port,
        .config_port    = pl010_config_port,
        .verify_port    = pl010_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char = pl010_get_poll_char,
+       .poll_put_char = pl010_put_poll_char,
+#endif
 };
 
 static struct uart_amba_port *amba_ports[UART_NR];
diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c
new file mode 100644 (file)
index 0000000..9cf0332
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Based on the same principle as kgdboe using the NETPOLL api, this
+ * driver uses a console polling api to implement a gdb serial inteface
+ * which is multiplexed on a console port.
+ *
+ * Maintainer: Jason Wessel <jason.wessel@windriver.com>
+ *
+ * 2007-2008 (c) Jason Wessel - Wind River Systems, 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/kernel.h>
+#include <linux/ctype.h>
+#include <linux/kgdb.h>
+#include <linux/tty.h>
+
+#define MAX_CONFIG_LEN         40
+
+static struct kgdb_io          kgdboc_io_ops;
+
+/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */
+static int configured          = -1;
+
+static char config[MAX_CONFIG_LEN];
+static struct kparam_string kps = {
+       .string                 = config,
+       .maxlen                 = MAX_CONFIG_LEN,
+};
+
+static struct tty_driver       *kgdb_tty_driver;
+static int                     kgdb_tty_line;
+
+static int kgdboc_option_setup(char *opt)
+{
+       if (strlen(opt) > MAX_CONFIG_LEN) {
+               printk(KERN_ERR "kgdboc: config string too long\n");
+               return -ENOSPC;
+       }
+       strcpy(config, opt);
+
+       return 0;
+}
+
+__setup("kgdboc=", kgdboc_option_setup);
+
+static int configure_kgdboc(void)
+{
+       struct tty_driver *p;
+       int tty_line = 0;
+       int err;
+
+       err = kgdboc_option_setup(config);
+       if (err || !strlen(config) || isspace(config[0]))
+               goto noconfig;
+
+       err = -ENODEV;
+
+       p = tty_find_polling_driver(config, &tty_line);
+       if (!p)
+               goto noconfig;
+
+       kgdb_tty_driver = p;
+       kgdb_tty_line = tty_line;
+
+       err = kgdb_register_io_module(&kgdboc_io_ops);
+       if (err)
+               goto noconfig;
+
+       configured = 1;
+
+       return 0;
+
+noconfig:
+       config[0] = 0;
+       configured = 0;
+
+       return err;
+}
+
+static int __init init_kgdboc(void)
+{
+       /* Already configured? */
+       if (configured == 1)
+               return 0;
+
+       return configure_kgdboc();
+}
+
+static void cleanup_kgdboc(void)
+{
+       if (configured == 1)
+               kgdb_unregister_io_module(&kgdboc_io_ops);
+}
+
+static int kgdboc_get_char(void)
+{
+       return kgdb_tty_driver->poll_get_char(kgdb_tty_driver, kgdb_tty_line);
+}
+
+static void kgdboc_put_char(u8 chr)
+{
+       kgdb_tty_driver->poll_put_char(kgdb_tty_driver, kgdb_tty_line, chr);
+}
+
+static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp)
+{
+       int len = strlen(kmessage);
+
+       if (len >= MAX_CONFIG_LEN) {
+               printk(KERN_ERR "kgdboc: config string too long\n");
+               return -ENOSPC;
+       }
+
+       /* Only copy in the string if the init function has not run yet */
+       if (configured < 0) {
+               strcpy(config, kmessage);
+               return 0;
+       }
+
+       if (kgdb_connected) {
+               printk(KERN_ERR
+                      "kgdboc: Cannot reconfigure while KGDB is connected.\n");
+
+               return -EBUSY;
+       }
+
+       strcpy(config, kmessage);
+       /* Chop out \n char as a result of echo */
+       if (config[len - 1] == '\n')
+               config[len - 1] = '\0';
+
+       if (configured == 1)
+               cleanup_kgdboc();
+
+       /* Go and configure with the new params. */
+       return configure_kgdboc();
+}
+
+static void kgdboc_pre_exp_handler(void)
+{
+       /* Increment the module count when the debugger is active */
+       if (!kgdb_connected)
+               try_module_get(THIS_MODULE);
+}
+
+static void kgdboc_post_exp_handler(void)
+{
+       /* decrement the module count when the debugger detaches */
+       if (!kgdb_connected)
+               module_put(THIS_MODULE);
+}
+
+static struct kgdb_io kgdboc_io_ops = {
+       .name                   = "kgdboc",
+       .read_char              = kgdboc_get_char,
+       .write_char             = kgdboc_put_char,
+       .pre_exception          = kgdboc_pre_exp_handler,
+       .post_exception         = kgdboc_post_exp_handler,
+};
+
+module_init(init_kgdboc);
+module_exit(cleanup_kgdboc);
+module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
+MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]");
+MODULE_DESCRIPTION("KGDB Console TTY Driver");
+MODULE_LICENSE("GPL");
index 0f5a179..c32c1ca 100644 (file)
@@ -1771,7 +1771,7 @@ static int uart_read_proc(char *page, char **start, off_t off,
 }
 #endif
 
-#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
 /*
  *     uart_console_write - write a console message to a serial port
  *     @port: the port to write the message
@@ -1827,7 +1827,7 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
  *     options.  The format of the string is <baud><parity><bits><flow>,
  *     eg: 115200n8r
  */
-void __init
+void
 uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
 {
        char *s = options;
@@ -1842,6 +1842,7 @@ uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
        if (*s)
                *flow = *s;
 }
+EXPORT_SYMBOL_GPL(uart_parse_options);
 
 struct baud_rates {
        unsigned int rate;
@@ -1872,7 +1873,7 @@ static const struct baud_rates baud_rates[] = {
  *     @bits: number of data bits
  *     @flow: flow control character - 'r' (rts)
  */
-int __init
+int
 uart_set_options(struct uart_port *port, struct console *co,
                 int baud, int parity, int bits, int flow)
 {
@@ -1924,10 +1925,16 @@ uart_set_options(struct uart_port *port, struct console *co,
        port->mctrl |= TIOCM_DTR;
 
        port->ops->set_termios(port, &termios, &dummy);
-       co->cflag = termios.c_cflag;
+       /*
+        * Allow the setting of the UART parameters with a NULL console
+        * too:
+        */
+       if (co)
+               co->cflag = termios.c_cflag;
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(uart_set_options);
 #endif /* CONFIG_SERIAL_CORE_CONSOLE */
 
 static void uart_change_pm(struct uart_state *state, int pm_state)
@@ -2182,6 +2189,60 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
        }
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+
+static int uart_poll_init(struct tty_driver *driver, int line, char *options)
+{
+       struct uart_driver *drv = driver->driver_state;
+       struct uart_state *state = drv->state + line;
+       struct uart_port *port;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       if (!state || !state->port)
+               return -1;
+
+       port = state->port;
+       if (!(port->ops->poll_get_char && port->ops->poll_put_char))
+               return -1;
+
+       if (options) {
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+               return uart_set_options(port, NULL, baud, parity, bits, flow);
+       }
+
+       return 0;
+}
+
+static int uart_poll_get_char(struct tty_driver *driver, int line)
+{
+       struct uart_driver *drv = driver->driver_state;
+       struct uart_state *state = drv->state + line;
+       struct uart_port *port;
+
+       if (!state || !state->port)
+               return -1;
+
+       port = state->port;
+       return port->ops->poll_get_char(port);
+}
+
+static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
+{
+       struct uart_driver *drv = driver->driver_state;
+       struct uart_state *state = drv->state + line;
+       struct uart_port *port;
+
+       if (!state || !state->port)
+               return;
+
+       port = state->port;
+       port->ops->poll_put_char(port, ch);
+}
+#endif
+
 static const struct tty_operations uart_ops = {
        .open           = uart_open,
        .close          = uart_close,
@@ -2206,6 +2267,11 @@ static const struct tty_operations uart_ops = {
 #endif
        .tiocmget       = uart_tiocmget,
        .tiocmset       = uart_tiocmset,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_init      = uart_poll_init,
+       .poll_get_char  = uart_poll_get_char,
+       .poll_put_char  = uart_poll_put_char,
+#endif
 };
 
 /**
index 0c41753..96651bb 100644 (file)
@@ -20,6 +20,7 @@ enum die_val {
        DIE_CALL,
        DIE_NMI_IPI,
        DIE_PAGE_FAULT,
+       DIE_NMIUNKNOWN,
 };
 
 extern void printk_address(unsigned long address, int reliable);
diff --git a/include/asm-x86/kgdb.h b/include/asm-x86/kgdb.h
new file mode 100644 (file)
index 0000000..484c475
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ * Copyright (C) 2008 Wind River Systems, Inc.
+ */
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound
+ * buffers at least NUMREGBYTES*2 are needed for register packets
+ * Longer buffer is needed to list all threads
+ */
+#define BUFMAX                 1024
+
+/*
+ *  Note that this register image is in a different order than
+ *  the register image that Linux produces at interrupt time.
+ *
+ *  Linux's register image is defined by struct pt_regs in ptrace.h.
+ *  Just why GDB uses a different order is a historical mystery.
+ */
+#ifdef CONFIG_X86_32
+enum regnames {
+       GDB_AX,                 /* 0 */
+       GDB_CX,                 /* 1 */
+       GDB_DX,                 /* 2 */
+       GDB_BX,                 /* 3 */
+       GDB_SP,                 /* 4 */
+       GDB_BP,                 /* 5 */
+       GDB_SI,                 /* 6 */
+       GDB_DI,                 /* 7 */
+       GDB_PC,                 /* 8 also known as eip */
+       GDB_PS,                 /* 9 also known as eflags */
+       GDB_CS,                 /* 10 */
+       GDB_SS,                 /* 11 */
+       GDB_DS,                 /* 12 */
+       GDB_ES,                 /* 13 */
+       GDB_FS,                 /* 14 */
+       GDB_GS,                 /* 15 */
+};
+#else /* ! CONFIG_X86_32 */
+enum regnames {
+       GDB_AX,                 /* 0 */
+       GDB_DX,                 /* 1 */
+       GDB_CX,                 /* 2 */
+       GDB_BX,                 /* 3 */
+       GDB_SI,                 /* 4 */
+       GDB_DI,                 /* 5 */
+       GDB_BP,                 /* 6 */
+       GDB_SP,                 /* 7 */
+       GDB_R8,                 /* 8 */
+       GDB_R9,                 /* 9 */
+       GDB_R10,                /* 10 */
+       GDB_R11,                /* 11 */
+       GDB_R12,                /* 12 */
+       GDB_R13,                /* 13 */
+       GDB_R14,                /* 14 */
+       GDB_R15,                /* 15 */
+       GDB_PC,                 /* 16 */
+       GDB_PS,                 /* 17 */
+};
+#endif /* CONFIG_X86_32 */
+
+/*
+ * Number of bytes of registers:
+ */
+#ifdef CONFIG_X86_32
+# define NUMREGBYTES           64
+#else
+# define NUMREGBYTES           ((GDB_PS+1)*8)
+#endif
+
+static inline void arch_kgdb_breakpoint(void)
+{
+       asm("   int $3");
+}
+#define BREAK_INSTR_SIZE       1
+#define CACHE_FLUSH_IS_SAFE    1
+
+#endif                         /* _ASM_KGDB_H_ */
index 85778a4..3509447 100644 (file)
@@ -216,6 +216,7 @@ static inline void clocksource_calculate_interval(struct clocksource *c,
 /* used to install a new clocksource */
 extern int clocksource_register(struct clocksource*);
 extern void clocksource_unregister(struct clocksource*);
+extern void clocksource_touch_watchdog(void);
 extern struct clocksource* clocksource_get_next(void);
 extern void clocksource_change_rating(struct clocksource *cs, int rating);
 extern void clocksource_resume(void);
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
new file mode 100644 (file)
index 0000000..9757b1a
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * This provides the callbacks and functions that KGDB needs to share between
+ * the core, I/O and arch-specific portions.
+ *
+ * Author: Amit Kale <amitkale@linsyssoft.com> and
+ *         Tom Rini <trini@kernel.crashing.org>
+ *
+ * 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.
+ */
+#ifndef _KGDB_H_
+#define _KGDB_H_
+
+#include <linux/serial_8250.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/atomic.h>
+#include <asm/kgdb.h>
+
+struct pt_regs;
+
+/**
+ *     kgdb_skipexception - (optional) exit kgdb_handle_exception early
+ *     @exception: Exception vector number
+ *     @regs: Current &struct pt_regs.
+ *
+ *     On some architectures it is required to skip a breakpoint
+ *     exception when it occurs after a breakpoint has been removed.
+ *     This can be implemented in the architecture specific portion of
+ *     for kgdb.
+ */
+extern int kgdb_skipexception(int exception, struct pt_regs *regs);
+
+/**
+ *     kgdb_post_primary_code - (optional) Save error vector/code numbers.
+ *     @regs: Original pt_regs.
+ *     @e_vector: Original error vector.
+ *     @err_code: Original error code.
+ *
+ *     This is usually needed on architectures which support SMP and
+ *     KGDB.  This function is called after all the secondary cpus have
+ *     been put to a know spin state and the primary CPU has control over
+ *     KGDB.
+ */
+extern void kgdb_post_primary_code(struct pt_regs *regs, int e_vector,
+                                 int err_code);
+
+/**
+ *     kgdb_disable_hw_debug - (optional) Disable hardware debugging hook
+ *     @regs: Current &struct pt_regs.
+ *
+ *     This function will be called if the particular architecture must
+ *     disable hardware debugging while it is processing gdb packets or
+ *     handling exception.
+ */
+extern void kgdb_disable_hw_debug(struct pt_regs *regs);
+
+struct tasklet_struct;
+struct task_struct;
+struct uart_port;
+
+/**
+ *     kgdb_breakpoint - compiled in breakpoint
+ *
+ *     This will be impelmented a static inline per architecture.  This
+ *     function is called by the kgdb core to execute an architecture
+ *     specific trap to cause kgdb to enter the exception processing.
+ *
+ */
+void kgdb_breakpoint(void);
+
+extern int kgdb_connected;
+
+extern atomic_t                        kgdb_setting_breakpoint;
+extern atomic_t                        kgdb_cpu_doing_single_step;
+
+extern struct task_struct      *kgdb_usethread;
+extern struct task_struct      *kgdb_contthread;
+
+enum kgdb_bptype {
+       BP_BREAKPOINT = 0,
+       BP_HARDWARE_BREAKPOINT,
+       BP_WRITE_WATCHPOINT,
+       BP_READ_WATCHPOINT,
+       BP_ACCESS_WATCHPOINT
+};
+
+enum kgdb_bpstate {
+       BP_UNDEFINED = 0,
+       BP_REMOVED,
+       BP_SET,
+       BP_ACTIVE
+};
+
+struct kgdb_bkpt {
+       unsigned long           bpt_addr;
+       unsigned char           saved_instr[BREAK_INSTR_SIZE];
+       enum kgdb_bptype        type;
+       enum kgdb_bpstate       state;
+};
+
+#ifndef KGDB_MAX_BREAKPOINTS
+# define KGDB_MAX_BREAKPOINTS  1000
+#endif
+
+#define KGDB_HW_BREAKPOINT     1
+
+/*
+ * Functions each KGDB-supporting architecture must provide:
+ */
+
+/**
+ *     kgdb_arch_init - Perform any architecture specific initalization.
+ *
+ *     This function will handle the initalization of any architecture
+ *     specific callbacks.
+ */
+extern int kgdb_arch_init(void);
+
+/**
+ *     kgdb_arch_exit - Perform any architecture specific uninitalization.
+ *
+ *     This function will handle the uninitalization of any architecture
+ *     specific callbacks, for dynamic registration and unregistration.
+ */
+extern void kgdb_arch_exit(void);
+
+/**
+ *     pt_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 pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs);
+
+/**
+ *     sleeping_thread_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_pt_regs - Convert GDB regs to ptrace regs.
+ *     @gdb_regs: A pointer to hold the registers we've received 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_pt_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 bytes 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 callback.
+ */
+extern int
+kgdb_arch_handle_exception(int vector, int signo, int err_code,
+                          char *remcom_in_buffer,
+                          char *remcom_out_buffer,
+                          struct pt_regs *regs);
+
+/**
+ *     kgdb_roundup_cpus - Get other CPUs into a holding pattern
+ *     @flags: Current IRQ state
+ *
+ *     On SMP systems, we need to get the attention of the other CPUs
+ *     and get them be in a known state.  This should do what is needed
+ *     to get the other CPUs to call kgdb_wait(). Note that on some arches,
+ *     the NMI approach is not used for rounding up all the CPUs. For example,
+ *     in case of MIPS, smp_call_function() is used to roundup CPUs. In
+ *     this case, we have to make sure that interrupts are enabled before
+ *     calling smp_call_function(). The argument to this function is
+ *     the flags that will be used when restoring the interrupts. There is
+ *     local_irq_save() call before kgdb_roundup_cpus().
+ *
+ *     On non-SMP systems, this is not called.
+ */
+extern void kgdb_roundup_cpus(unsigned long flags);
+
+/* Optional functions. */
+extern int kgdb_validate_break_address(unsigned long addr);
+extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr);
+extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle);
+
+/**
+ * struct kgdb_arch - Describe architecture specific values.
+ * @gdb_bpt_instr: The instruction to trigger a breakpoint.
+ * @flags: Flags for the breakpoint, currently just %KGDB_HW_BREAKPOINT.
+ * @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.
+ * @remove_all_hw_break: Allow an architecture to specify how to remove all
+ * hardware breakpoints.
+ * @correct_hw_break: Allow an architecture to specify how to correct the
+ * hardware debug registers.
+ */
+struct kgdb_arch {
+       unsigned char           gdb_bpt_instr[BREAK_INSTR_SIZE];
+       unsigned long           flags;
+
+       int     (*set_breakpoint)(unsigned long, char *);
+       int     (*remove_breakpoint)(unsigned long, char *);
+       int     (*set_hw_breakpoint)(unsigned long, int, enum kgdb_bptype);
+       int     (*remove_hw_breakpoint)(unsigned long, int, enum kgdb_bptype);
+       void    (*remove_all_hw_break)(void);
+       void    (*correct_hw_break)(void);
+};
+
+/**
+ * struct kgdb_io - Describe the interface for an I/O driver to talk with KGDB.
+ * @name: Name of the I/O driver.
+ * @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.
+ * @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.
+ */
+struct kgdb_io {
+       const char              *name;
+       int                     (*read_char) (void);
+       void                    (*write_char) (u8);
+       void                    (*flush) (void);
+       int                     (*init) (void);
+       void                    (*pre_exception) (void);
+       void                    (*post_exception) (void);
+};
+
+extern struct kgdb_arch                arch_kgdb_ops;
+
+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 int kgdb_hex2long(char **ptr, long *long_val);
+extern int kgdb_mem2hex(char *mem, char *buf, int count);
+extern int kgdb_hex2mem(char *buf, char *mem, int count);
+
+extern int kgdb_isremovedbreak(unsigned long addr);
+
+extern int
+kgdb_handle_exception(int ex_vector, int signo, int err_code,
+                     struct pt_regs *regs);
+extern int kgdb_nmicallback(int cpu, void *regs);
+
+extern int                     kgdb_single_step;
+extern atomic_t                        kgdb_active;
+
+#endif /* _KGDB_H_ */
index 289942f..7cb094a 100644 (file)
@@ -213,6 +213,10 @@ struct uart_ops {
        void            (*config_port)(struct uart_port *, int);
        int             (*verify_port)(struct uart_port *, struct serial_struct *);
        int             (*ioctl)(struct uart_port *, unsigned int, unsigned long);
+#ifdef CONFIG_CONSOLE_POLL
+       void    (*poll_put_char)(struct uart_port *, unsigned char);
+       int             (*poll_get_char)(struct uart_port *);
+#endif
 };
 
 #define UART_CONFIG_TYPE       (1 << 0)
index 85c95cd..21f69ac 100644 (file)
 #include <linux/cdev.h>
 
 struct tty_struct;
+struct tty_driver;
 
 struct tty_operations {
        int  (*open)(struct tty_struct * tty, struct file * filp);
@@ -157,6 +158,11 @@ struct tty_operations {
        int (*tiocmget)(struct tty_struct *tty, struct file *file);
        int (*tiocmset)(struct tty_struct *tty, struct file *file,
                        unsigned int set, unsigned int clear);
+#ifdef CONFIG_CONSOLE_POLL
+       int (*poll_init)(struct tty_driver *driver, int line, char *options);
+       int (*poll_get_char)(struct tty_driver *driver, int line);
+       void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
+#endif
 };
 
 struct tty_driver {
@@ -220,6 +226,11 @@ struct tty_driver {
        int (*tiocmget)(struct tty_struct *tty, struct file *file);
        int (*tiocmset)(struct tty_struct *tty, struct file *file,
                        unsigned int set, unsigned int clear);
+#ifdef CONFIG_CONSOLE_POLL
+       int (*poll_init)(struct tty_driver *driver, int line, char *options);
+       int (*poll_get_char)(struct tty_driver *driver, int line);
+       void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
+#endif
 
        struct list_head tty_drivers;
 };
@@ -230,6 +241,7 @@ struct tty_driver *alloc_tty_driver(int lines);
 void put_tty_driver(struct tty_driver *driver);
 void tty_set_operations(struct tty_driver *driver,
                        const struct tty_operations *op);
+extern struct tty_driver *tty_find_polling_driver(char *name, int *line);
 
 /* tty driver magic number */
 #define TTY_DRIVER_MAGIC               0x5402
index 975c963..fec6dec 100644 (file)
@@ -84,4 +84,26 @@ static inline unsigned long __copy_from_user_nocache(void *to,
                ret;                                    \
        })
 
+/*
+ * probe_kernel_read(): safely attempt to read from a location
+ * @dst: pointer to the buffer that shall take the data
+ * @src: address to read from
+ * @size: size of the data chunk
+ *
+ * Safely read from address @src to the buffer at @dst.  If a kernel fault
+ * happens, handle that and return -EFAULT.
+ */
+extern long probe_kernel_read(void *dst, void *src, size_t size);
+
+/*
+ * probe_kernel_write(): safely attempt to write to a location
+ * @dst: address to write to
+ * @src: pointer to the data that shall be written
+ * @size: size of the data chunk
+ *
+ * Safely write to address @dst from the buffer at @src.  If a kernel fault
+ * happens, handle that and return -EFAULT.
+ */
+extern long probe_kernel_write(void *dst, void *src, size_t size);
+
 #endif         /* __LINUX_UACCESS_H__ */
index f45c69e..6c5f081 100644 (file)
@@ -53,6 +53,7 @@ obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
 obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
 obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
 obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KGDB) += kgdb.o
 obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
 obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
 obj-$(CONFIG_SECCOMP) += seccomp.o
diff --git a/kernel/kgdb.c b/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..1bd0ec1
--- /dev/null
@@ -0,0 +1,1700 @@
+/*
+ * KGDB stub.
+ *
+ * Maintainer: Jason Wessel <jason.wessel@windriver.com>
+ *
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002-2004 Timesys Corporation
+ * Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com>
+ * Copyright (C) 2004 Pavel Machek <pavel@suse.cz>
+ * Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org>
+ * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd.
+ * Copyright (C) 2005-2008 Wind River Systems, Inc.
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ * Copyright (C) 2008 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
+ *
+ * Contributors at various stages not listed above:
+ *  Jason Wessel ( jason.wessel@windriver.com )
+ *  George Anzinger <george@mvista.com>
+ *  Anurekh Saxena (anurekh.saxena@timesys.com)
+ *  Lake Stevens Instrument Division (Glenn Engel)
+ *  Jim Kingdon, Cygnus Support.
+ *
+ * Original KGDB stub: David Grothe <dave@gcom.com>,
+ * Tigran Aivazian <tigran@sco.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+#include <linux/pid_namespace.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/console.h>
+#include <linux/threads.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/sysrq.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+#include <linux/pid.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+
+#include <asm/cacheflush.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+
+static int kgdb_break_asap;
+
+struct kgdb_state {
+       int                     ex_vector;
+       int                     signo;
+       int                     err_code;
+       int                     cpu;
+       int                     pass_exception;
+       long                    threadid;
+       long                    kgdb_usethreadid;
+       struct pt_regs          *linux_regs;
+};
+
+static struct debuggerinfo_struct {
+       void                    *debuggerinfo;
+       struct task_struct      *task;
+} kgdb_info[NR_CPUS];
+
+/**
+ * kgdb_connected - Is a host GDB connected to us?
+ */
+int                            kgdb_connected;
+EXPORT_SYMBOL_GPL(kgdb_connected);
+
+/* All the KGDB handlers are installed */
+static int                     kgdb_io_module_registered;
+
+/* Guard for recursive entry */
+static int                     exception_level;
+
+static struct kgdb_io          *kgdb_io_ops;
+static DEFINE_SPINLOCK(kgdb_registration_lock);
+
+/* kgdb console driver is loaded */
+static int kgdb_con_registered;
+/* determine if kgdb console output should be used */
+static int kgdb_use_con;
+
+static int __init opt_kgdb_con(char *str)
+{
+       kgdb_use_con = 1;
+       return 0;
+}
+
+early_param("kgdbcon", opt_kgdb_con);
+
+module_param(kgdb_use_con, int, 0644);
+
+/*
+ * Holds information about breakpoints in a kernel. These breakpoints are
+ * added and removed by gdb.
+ */
+static struct kgdb_bkpt                kgdb_break[KGDB_MAX_BREAKPOINTS] = {
+       [0 ... KGDB_MAX_BREAKPOINTS-1] = { .state = BP_UNDEFINED }
+};
+
+/*
+ * The CPU# of the active CPU, or -1 if none:
+ */
+atomic_t                       kgdb_active = ATOMIC_INIT(-1);
+
+/*
+ * We use NR_CPUs not PERCPU, in case kgdb is used to debug early
+ * bootup code (which might not have percpu set up yet):
+ */
+static atomic_t                        passive_cpu_wait[NR_CPUS];
+static atomic_t                        cpu_in_kgdb[NR_CPUS];
+atomic_t                       kgdb_setting_breakpoint;
+
+struct task_struct             *kgdb_usethread;
+struct task_struct             *kgdb_contthread;
+
+int                            kgdb_single_step;
+
+/* 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)];
+
+/* to keep track of the CPU which is doing the single stepping*/
+atomic_t                       kgdb_cpu_doing_single_step = ATOMIC_INIT(-1);
+
+/*
+ * If you are debugging a problem where roundup (the collection of
+ * all other CPUs) is a problem [this should be extremely rare],
+ * then use the nokgdbroundup option to avoid roundup. In that case
+ * the other CPUs might interfere with your debugging context, so
+ * use this with care:
+ */
+int                            kgdb_do_roundup = 1;
+
+static int __init opt_nokgdbroundup(char *str)
+{
+       kgdb_do_roundup = 0;
+
+       return 0;
+}
+
+early_param("nokgdbroundup", opt_nokgdbroundup);
+
+/*
+ * Finally, some KGDB code :-)
+ */
+
+/*
+ * Weak aliases for breakpoint management,
+ * can be overriden by architectures when needed:
+ */
+int __weak kgdb_validate_break_address(unsigned long addr)
+{
+       char tmp_variable[BREAK_INSTR_SIZE];
+
+       return probe_kernel_read(tmp_variable, (char *)addr, BREAK_INSTR_SIZE);
+}
+
+int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
+{
+       int err;
+
+       err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE);
+       if (err)
+               return err;
+
+       return probe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr,
+                                 BREAK_INSTR_SIZE);
+}
+
+int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
+{
+       return probe_kernel_write((char *)addr,
+                                 (char *)bundle, BREAK_INSTR_SIZE);
+}
+
+unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs)
+{
+       return instruction_pointer(regs);
+}
+
+int __weak kgdb_arch_init(void)
+{
+       return 0;
+}
+
+int __weak kgdb_skipexception(int exception, struct pt_regs *regs)
+{
+       return 0;
+}
+
+void __weak
+kgdb_post_primary_code(struct pt_regs *regs, int e_vector, int err_code)
+{
+       return;
+}
+
+/**
+ *     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 __weak kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+}
+
+/*
+ * GDB remote protocol parser:
+ */
+
+static const char      hexchars[] = "0123456789abcdef";
+
+static int hex(char ch)
+{
+       if ((ch >= 'a') && (ch <= 'f'))
+               return ch - 'a' + 10;
+       if ((ch >= '0') && (ch <= '9'))
+               return ch - '0';
+       if ((ch >= 'A') && (ch <= 'F'))
+               return ch - 'A' + 10;
+       return -1;
+}
+
+/* scan for the sequence $<data>#<checksum> */
+static void get_packet(char *buffer)
+{
+       unsigned char checksum;
+       unsigned char xmitcsum;
+       int count;
+       char ch;
+
+       do {
+               /*
+                * Spin and wait around for the start character, ignore all
+                * other characters:
+                */
+               while ((ch = (kgdb_io_ops->read_char())) != '$')
+                       /* nothing */;
+
+               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;
+
+       /*
+        * $<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 & 0xf]);
+               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;
+               }
+       }
+}
+
+static char *pack_hex_byte(char *pkt, u8 byte)
+{
+       *pkt++ = hexchars[byte >> 4];
+       *pkt++ = hexchars[byte & 0xf];
+
+       return pkt;
+}
+
+/*
+ * 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.
+ */
+int kgdb_mem2hex(char *mem, char *buf, int count)
+{
+       char *tmp;
+       int err;
+
+       /*
+        * We use the upper half of buf as an intermediate buffer for the
+        * raw memory copy.  Hex conversion will work against this one.
+        */
+       tmp = buf + count;
+
+       err = probe_kernel_read(tmp, mem, count);
+       if (!err) {
+               while (count > 0) {
+                       buf = pack_hex_byte(buf, *tmp);
+                       tmp++;
+                       count--;
+               }
+
+               *buf = 0;
+       }
+
+       return err;
+}
+
+/*
+ * 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 int kgdb_ebin2mem(char *buf, char *mem, int count)
+{
+       int err = 0;
+       char c;
+
+       while (count-- > 0) {
+               c = *buf++;
+               if (c == 0x7d)
+                       c = *buf++ ^ 0x20;
+
+               err = probe_kernel_write(mem, &c, 1);
+               if (err)
+                       break;
+
+               mem++;
+       }
+
+       return err;
+}
+
+/*
+ * 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.
+ */
+int kgdb_hex2mem(char *buf, char *mem, int count)
+{
+       char *tmp_raw;
+       char *tmp_hex;
+
+       /*
+        * We use the upper half of buf as an intermediate buffer for the
+        * raw memory that is converted from hex.
+        */
+       tmp_raw = buf + count * 2;
+
+       tmp_hex = tmp_raw - 1;
+       while (tmp_hex >= buf) {
+               tmp_raw--;
+               *tmp_raw = hex(*tmp_hex--);
+               *tmp_raw |= hex(*tmp_hex--) << 4;
+       }
+
+       return probe_kernel_write(mem, tmp_raw, count);
+}
+
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+int kgdb_hex2long(char **ptr, long *long_val)
+{
+       int hex_val;
+       int num = 0;
+
+       *long_val = 0;
+
+       while (**ptr) {
+               hex_val = hex(**ptr);
+               if (hex_val < 0)
+                       break;
+
+               *long_val = (*long_val << 4) | hex_val;
+               num++;
+               (*ptr)++;
+       }
+
+       return num;
+}
+
+/* Write memory due to an 'M' or 'X' packet. */
+static int write_mem_msg(int binary)
+{
+       char *ptr = &remcom_in_buffer[1];
+       unsigned long addr;
+       unsigned long length;
+       int err;
+
+       if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' &&
+           kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') {
+               if (binary)
+                       err = kgdb_ebin2mem(ptr, (char *)addr, length);
+               else
+                       err = kgdb_hex2mem(ptr, (char *)addr, length);
+               if (err)
+                       return err;
+               if (CACHE_FLUSH_IS_SAFE)
+                       flush_icache_range(addr, addr + length + 1);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static 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';
+}
+
+/*
+ * Thread ID accessors. We represent a flat TID space to GDB, where
+ * the per CPU idle threads (which under Linux all have PID 0) are
+ * remapped to negative TIDs.
+ */
+
+#define BUF_THREAD_ID_SIZE     16
+
+static char *pack_threadid(char *pkt, unsigned char *id)
+{
+       char *limit;
+
+       limit = pkt + BUF_THREAD_ID_SIZE;
+       while (pkt < limit)
+               pkt = pack_hex_byte(pkt, *id++);
+
+       return pkt;
+}
+
+static void int_to_threadref(unsigned char *id, int value)
+{
+       unsigned char *scan;
+       int i = 4;
+
+       scan = (unsigned char *)id;
+       while (i--)
+               *scan++ = 0;
+       *scan++ = (value >> 24) & 0xff;
+       *scan++ = (value >> 16) & 0xff;
+       *scan++ = (value >> 8) & 0xff;
+       *scan++ = (value & 0xff);
+}
+
+static struct task_struct *getthread(struct pt_regs *regs, int tid)
+{
+       /*
+        * Non-positive TIDs are remapped idle tasks:
+        */
+       if (tid <= 0)
+               return idle_task(-tid);
+
+       /*
+        * find_task_by_pid_ns() does not take the tasklist lock anymore
+        * but is nicely RCU locked - hence is a pretty resilient
+        * thing to use:
+        */
+       return find_task_by_pid_ns(tid, &init_pid_ns);
+}
+
+/*
+ * CPU debug state control:
+ */
+
+#ifdef CONFIG_SMP
+static void kgdb_wait(struct pt_regs *regs)
+{
+       unsigned long flags;
+       int cpu;
+
+       local_irq_save(flags);
+       cpu = raw_smp_processor_id();
+       kgdb_info[cpu].debuggerinfo = regs;
+       kgdb_info[cpu].task = current;
+       /*
+        * Make sure the above info reaches the primary CPU before
+        * our cpu_in_kgdb[] flag setting does:
+        */
+       smp_wmb();
+       atomic_set(&cpu_in_kgdb[cpu], 1);
+
+       /* Wait till primary CPU is done with debugging */
+       while (atomic_read(&passive_cpu_wait[cpu]))
+               cpu_relax();
+
+       kgdb_info[cpu].debuggerinfo = NULL;
+       kgdb_info[cpu].task = NULL;
+
+       /* fix up hardware debug registers on local cpu */
+       if (arch_kgdb_ops.correct_hw_break)
+               arch_kgdb_ops.correct_hw_break();
+
+       /* Signal the primary CPU that we are done: */
+       atomic_set(&cpu_in_kgdb[cpu], 0);
+       clocksource_touch_watchdog();
+       local_irq_restore(flags);
+}
+#endif
+
+/*
+ * Some architectures need cache flushes when we set/clear a
+ * breakpoint:
+ */
+static void kgdb_flush_swbreak_addr(unsigned long addr)
+{
+       if (!CACHE_FLUSH_IS_SAFE)
+               return;
+
+       if (current->mm && current->mm->mmap_cache) {
+               flush_cache_range(current->mm->mmap_cache,
+                                 addr, addr + BREAK_INSTR_SIZE);
+       }
+       /* Force flush instruction cache if it was outside the mm */
+       flush_icache_range(addr, addr + BREAK_INSTR_SIZE);
+}
+
+/*
+ * SW breakpoint management:
+ */
+static int kgdb_activate_sw_breakpoints(void)
+{
+       unsigned long addr;
+       int error = 0;
+       int i;
+
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if (kgdb_break[i].state != BP_SET)
+                       continue;
+
+               addr = kgdb_break[i].bpt_addr;
+               error = kgdb_arch_set_breakpoint(addr,
+                               kgdb_break[i].saved_instr);
+               if (error)
+                       return error;
+
+               kgdb_flush_swbreak_addr(addr);
+               kgdb_break[i].state = BP_ACTIVE;
+       }
+       return 0;
+}
+
+static int kgdb_set_sw_break(unsigned long addr)
+{
+       int err = kgdb_validate_break_address(addr);
+       int breakno = -1;
+       int i;
+
+       if (err)
+               return err;
+
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if ((kgdb_break[i].state == BP_SET) &&
+                                       (kgdb_break[i].bpt_addr == addr))
+                       return -EEXIST;
+       }
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if (kgdb_break[i].state == BP_REMOVED &&
+                                       kgdb_break[i].bpt_addr == addr) {
+                       breakno = i;
+                       break;
+               }
+       }
+
+       if (breakno == -1) {
+               for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+                       if (kgdb_break[i].state == BP_UNDEFINED) {
+                               breakno = i;
+                               break;
+                       }
+               }
+       }
+
+       if (breakno == -1)
+               return -E2BIG;
+
+       kgdb_break[breakno].state = BP_SET;
+       kgdb_break[breakno].type = BP_BREAKPOINT;
+       kgdb_break[breakno].bpt_addr = addr;
+
+       return 0;
+}
+
+static int kgdb_deactivate_sw_breakpoints(void)
+{
+       unsigned long addr;
+       int error = 0;
+       int i;
+
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if (kgdb_break[i].state != BP_ACTIVE)
+                       continue;
+               addr = kgdb_break[i].bpt_addr;
+               error = kgdb_arch_remove_breakpoint(addr,
+                                       kgdb_break[i].saved_instr);
+               if (error)
+                       return error;
+
+               kgdb_flush_swbreak_addr(addr);
+               kgdb_break[i].state = BP_SET;
+       }
+       return 0;
+}
+
+static int kgdb_remove_sw_break(unsigned long addr)
+{
+       int i;
+
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if ((kgdb_break[i].state == BP_SET) &&
+                               (kgdb_break[i].bpt_addr == addr)) {
+                       kgdb_break[i].state = BP_REMOVED;
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+int kgdb_isremovedbreak(unsigned long addr)
+{
+       int i;
+
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if ((kgdb_break[i].state == BP_REMOVED) &&
+                                       (kgdb_break[i].bpt_addr == addr))
+                       return 1;
+       }
+       return 0;
+}
+
+int remove_all_break(void)
+{
+       unsigned long addr;
+       int error;
+       int i;
+
+       /* Clear memory breakpoints. */
+       for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+               if (kgdb_break[i].state != BP_ACTIVE)
+                       goto setundefined;
+               addr = kgdb_break[i].bpt_addr;
+               error = kgdb_arch_remove_breakpoint(addr,
+                               kgdb_break[i].saved_instr);
+               if (error)
+                       printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n",
+                          addr);
+setundefined:
+               kgdb_break[i].state = BP_UNDEFINED;
+       }
+
+       /* Clear hardware breakpoints. */
+       if (arch_kgdb_ops.remove_all_hw_break)
+               arch_kgdb_ops.remove_all_hw_break();
+
+       return 0;
+}
+
+/*
+ * Remap normal tasks to their real PID, idle tasks to -1 ... -NR_CPUs:
+ */
+static inline int shadow_pid(int realpid)
+{
+       if (realpid)
+               return realpid;
+
+       return -1-raw_smp_processor_id();
+}
+
+static char gdbmsgbuf[BUFMAX + 1];
+
+static void kgdb_msg_write(const char *s, int len)
+{
+       char *bufptr;
+       int wcount;
+       int i;
+
+       /* '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);
+       }
+}
+
+/*
+ * Return true if there is a valid kgdb I/O module.  Also if no
+ * debugger is attached a message can be printed to the console about
+ * waiting for the debugger to attach.
+ *
+ * The print_wait argument is only to be true when called from inside
+ * the core kgdb_handle_exception, because it will wait for the
+ * debugger to attach.
+ */
+static int kgdb_io_ready(int print_wait)
+{
+       if (!kgdb_io_ops)
+               return 0;
+       if (kgdb_connected)
+               return 1;
+       if (atomic_read(&kgdb_setting_breakpoint))
+               return 1;
+       if (print_wait)
+               printk(KERN_CRIT "KGDB: Waiting for remote debugger\n");
+       return 1;
+}
+
+/*
+ * All the functions that start with gdb_cmd are the various
+ * operations to implement the handlers for the gdbserial protocol
+ * where KGDB is communicating with an external debugger
+ */
+
+/* Handle the '?' status packets */
+static void gdb_cmd_status(struct kgdb_state *ks)
+{
+       /*
+        * We know that this packet is only sent
+        * during initial connect.  So to be safe,
+        * we clear out our breakpoints now in case
+        * GDB is reconnecting.
+        */
+       remove_all_break();
+
+       remcom_out_buffer[0] = 'S';
+       pack_hex_byte(&remcom_out_buffer[1], ks->signo);
+}
+
+/* Handle the 'g' get registers request */
+static void gdb_cmd_getregs(struct kgdb_state *ks)
+{
+       struct task_struct *thread;
+       void *local_debuggerinfo;
+       int i;
+
+       thread = kgdb_usethread;
+       if (!thread) {
+               thread = kgdb_info[ks->cpu].task;
+               local_debuggerinfo = kgdb_info[ks->cpu].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 (local_debuggerinfo) {
+               pt_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);
+}
+
+/* Handle the 'G' set registers request */
+static void gdb_cmd_setregs(struct kgdb_state *ks)
+{
+       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_pt_regs(gdb_regs, ks->linux_regs);
+               strcpy(remcom_out_buffer, "OK");
+       }
+}
+
+/* Handle the 'm' memory read bytes */
+static void gdb_cmd_memread(struct kgdb_state *ks)
+{
+       char *ptr = &remcom_in_buffer[1];
+       unsigned long length;
+       unsigned long addr;
+       int err;
+
+       if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' &&
+                                       kgdb_hex2long(&ptr, &length) > 0) {
+               err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length);
+               if (err)
+                       error_packet(remcom_out_buffer, err);
+       } else {
+               error_packet(remcom_out_buffer, -EINVAL);
+       }
+}
+
+/* Handle the 'M' memory write bytes */
+static void gdb_cmd_memwrite(struct kgdb_state *ks)
+{
+       int err = write_mem_msg(0);
+
+       if (err)
+               error_packet(remcom_out_buffer, err);
+       else
+               strcpy(remcom_out_buffer, "OK");
+}
+
+/* Handle the 'X' memory binary write bytes */
+static void gdb_cmd_binwrite(struct kgdb_state *ks)
+{
+       int err = write_mem_msg(1);
+
+       if (err)
+               error_packet(remcom_out_buffer, err);
+       else
+               strcpy(remcom_out_buffer, "OK");
+}
+
+/* Handle the 'D' or 'k', detach or kill packets */
+static void gdb_cmd_detachkill(struct kgdb_state *ks)
+{
+       int error;
+
+       /* The detach case */
+       if (remcom_in_buffer[0] == 'D') {
+               error = remove_all_break();
+               if (error < 0) {
+                       error_packet(remcom_out_buffer, error);
+               } else {
+                       strcpy(remcom_out_buffer, "OK");
+                       kgdb_connected = 0;
+               }
+               put_packet(remcom_out_buffer);
+       } else {
+               /*
+                * Assume the kill case, with no exit code checking,
+                * trying to force detach the debugger:
+                */
+               remove_all_break();
+               kgdb_connected = 0;
+       }
+}
+
+/* Handle the 'R' reboot packets */
+static int gdb_cmd_reboot(struct kgdb_state *ks)
+{
+       /* For now, only honor R0 */
+       if (strcmp(remcom_in_buffer, "R0") == 0) {
+               printk(KERN_CRIT "Executing emergency reboot\n");
+               strcpy(remcom_out_buffer, "OK");
+               put_packet(remcom_out_buffer);
+
+               /*
+                * Execution should not return from
+                * machine_emergency_restart()
+                */
+               machine_emergency_restart();
+               kgdb_connected = 0;
+
+               return 1;
+       }
+       return 0;
+}
+
+/* Handle the 'q' query packets */
+static void gdb_cmd_query(struct kgdb_state *ks)
+{
+       struct task_struct *thread;
+       unsigned char thref[8];
+       char *ptr;
+       int i;
+
+       switch (remcom_in_buffer[1]) {
+       case 's':
+       case 'f':
+               if (memcmp(remcom_in_buffer + 2, "ThreadInfo", 10)) {
+                       error_packet(remcom_out_buffer, -EINVAL);
+                       break;
+               }
+
+               if (remcom_in_buffer[1] == 'f')
+                       ks->threadid = 1;
+
+               remcom_out_buffer[0] = 'm';
+               ptr = remcom_out_buffer + 1;
+
+               for (i = 0; i < 17; ks->threadid++) {
+                       thread = getthread(ks->linux_regs, ks->threadid);
+                       if (thread) {
+                               int_to_threadref(thref, ks->threadid);
+                               pack_threadid(ptr, thref);
+                               ptr += BUF_THREAD_ID_SIZE;
+                               *(ptr++) = ',';
+                               i++;
+                       }
+               }
+               *(--ptr) = '\0';
+               break;
+
+       case 'C':
+               /* Current thread id */
+               strcpy(remcom_out_buffer, "QC");
+               ks->threadid = shadow_pid(current->pid);
+               int_to_threadref(thref, ks->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;
+               }
+               ks->threadid = 0;
+               ptr = remcom_in_buffer + 17;
+               kgdb_hex2long(&ptr, &ks->threadid);
+               if (!getthread(ks->linux_regs, ks->threadid)) {
+                       error_packet(remcom_out_buffer, -EINVAL);
+                       break;
+               }
+               if (ks->threadid > 0) {
+                       kgdb_mem2hex(getthread(ks->linux_regs,
+                                       ks->threadid)->comm,
+                                       remcom_out_buffer, 16);
+               } else {
+                       static char tmpstr[23 + BUF_THREAD_ID_SIZE];
+
+                       sprintf(tmpstr, "Shadow task %d for pid 0",
+                                       (int)(-ks->threadid-1));
+                       kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr));
+               }
+               break;
+       }
+}
+
+/* Handle the 'H' task query packets */
+static void gdb_cmd_task(struct kgdb_state *ks)
+{
+       struct task_struct *thread;
+       char *ptr;
+
+       switch (remcom_in_buffer[1]) {
+       case 'g':
+               ptr = &remcom_in_buffer[2];
+               kgdb_hex2long(&ptr, &ks->threadid);
+               thread = getthread(ks->linux_regs, ks->threadid);
+               if (!thread && ks->threadid > 0) {
+                       error_packet(remcom_out_buffer, -EINVAL);
+                       break;
+               }
+               kgdb_usethread = thread;
+               ks->kgdb_usethreadid = ks->threadid;
+               strcpy(remcom_out_buffer, "OK");
+               break;
+       case 'c':
+               ptr = &remcom_in_buffer[2];
+               kgdb_hex2long(&ptr, &ks->threadid);
+               if (!ks->threadid) {
+                       kgdb_contthread = NULL;
+               } else {
+                       thread = getthread(ks->linux_regs, ks->threadid);
+                       if (!thread && ks->threadid > 0) {
+                               error_packet(remcom_out_buffer, -EINVAL);
+                               break;
+                       }
+                       kgdb_contthread = thread;
+               }
+               strcpy(remcom_out_buffer, "OK");
+               break;
+       }
+}
+
+/* Handle the 'T' thread query packets */
+static void gdb_cmd_thread(struct kgdb_state *ks)
+{
+       char *ptr = &remcom_in_buffer[1];
+       struct task_struct *thread;
+
+       kgdb_hex2long(&ptr, &ks->threadid);
+       thread = getthread(ks->linux_regs, ks->threadid);
+       if (thread)
+               strcpy(remcom_out_buffer, "OK");
+       else
+               error_packet(remcom_out_buffer, -EINVAL);
+}
+
+/* Handle the 'z' or 'Z' breakpoint remove or set packets */
+static void gdb_cmd_break(struct kgdb_state *ks)
+{
+       /*
+        * Since GDB-5.3, it's been drafted that '0' is a software
+        * breakpoint, '1' is a hardware breakpoint, so let's do that.
+        */
+       char *bpt_type = &remcom_in_buffer[1];
+       char *ptr = &remcom_in_buffer[2];
+       unsigned long addr;
+       unsigned long length;
+       int error = 0;
+
+       if (arch_kgdb_ops.set_hw_breakpoint && *bpt_type >= '1') {
+               /* Unsupported */
+               if (*bpt_type > '4')
+                       return;
+       } else {
+               if (*bpt_type != '0' && *bpt_type != '1')
+                       /* Unsupported. */
+                       return;
+       }
+
+       /*
+        * Test if this is a hardware breakpoint, and
+        * if we support it:
+        */
+       if (*bpt_type == '1' && !(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT))
+               /* Unsupported. */
+               return;
+
+       if (*(ptr++) != ',') {
+               error_packet(remcom_out_buffer, -EINVAL);
+               return;
+       }
+       if (!kgdb_hex2long(&ptr, &addr)) {
+               error_packet(remcom_out_buffer, -EINVAL);
+               return;
+       }
+       if (*(ptr++) != ',' ||
+               !kgdb_hex2long(&ptr, &length)) {
+               error_packet(remcom_out_buffer, -EINVAL);
+               return;
+       }
+
+       if (remcom_in_buffer[0] == 'Z' && *bpt_type == '0')
+               error = kgdb_set_sw_break(addr);
+       else if (remcom_in_buffer[0] == 'z' && *bpt_type == '0')
+               error = kgdb_remove_sw_break(addr);
+       else if (remcom_in_buffer[0] == 'Z')
+               error = arch_kgdb_ops.set_hw_breakpoint(addr,
+                       (int)length, *bpt_type - '0');
+       else if (remcom_in_buffer[0] == 'z')
+               error = arch_kgdb_ops.remove_hw_breakpoint(addr,
+                       (int) length, *bpt_type - '0');
+
+       if (error == 0)
+               strcpy(remcom_out_buffer, "OK");
+       else
+               error_packet(remcom_out_buffer, error);
+}
+
+/* Handle the 'C' signal / exception passing packets */
+static int gdb_cmd_exception_pass(struct kgdb_state *ks)
+{
+       /* C09 == pass exception
+        * C15 == detach kgdb, pass exception
+        */
+       if (remcom_in_buffer[1] == '0' && remcom_in_buffer[2] == '9') {
+
+               ks->pass_exception = 1;
+               remcom_in_buffer[0] = 'c';
+
+       } else if (remcom_in_buffer[1] == '1' && remcom_in_buffer[2] == '5') {
+
+               ks->pass_exception = 1;
+               remcom_in_buffer[0] = 'D';
+               remove_all_break();
+               kgdb_connected = 0;
+               return 1;
+
+       } else {
+               error_packet(remcom_out_buffer, -EINVAL);
+               return 0;
+       }
+
+       /* Indicate fall through */
+       return -1;
+}
+
+/*
+ * This function performs all gdbserial command procesing
+ */
+static int gdb_serial_stub(struct kgdb_state *ks)
+{
+       int error = 0;
+       int tmp;
+
+       /* Clear the out buffer. */
+       memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
+
+       if (kgdb_connected) {
+               unsigned char thref[8];
+               char *ptr;
+
+               /* Reply to host that an exception has occurred */
+               ptr = remcom_out_buffer;
+               *ptr++ = 'T';
+               ptr = pack_hex_byte(ptr, ks->signo);
+               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[ks->cpu].task;
+       ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid);
+       ks->pass_exception = 0;
+
+       while (1) {
+               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 '?': /* gdbserial status */
+                       gdb_cmd_status(ks);
+                       break;
+               case 'g': /* return the value of the CPU registers */
+                       gdb_cmd_getregs(ks);
+                       break;
+               case 'G': /* set the value of the CPU registers - return OK */
+                       gdb_cmd_setregs(ks);
+                       break;
+               case 'm': /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
+                       gdb_cmd_memread(ks);
+                       break;
+               case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */
+                       gdb_cmd_memwrite(ks);
+                       break;
+               case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */
+                       gdb_cmd_binwrite(ks);
+                       break;
+                       /* kill or detach. KGDB should treat this like a
+                        * continue.
+                        */
+               case 'D': /* Debugger detach */
+               case 'k': /* Debugger detach via kill */
+                       gdb_cmd_detachkill(ks);
+                       goto default_handle;
+               case 'R': /* Reboot */
+                       if (gdb_cmd_reboot(ks))
+                               goto default_handle;
+                       break;
+               case 'q': /* query command */
+                       gdb_cmd_query(ks);
+                       break;
+               case 'H': /* task related */
+                       gdb_cmd_task(ks);
+                       break;
+               case 'T': /* Query thread status */
+                       gdb_cmd_thread(ks);
+                       break;
+               case 'z': /* Break point remove */
+               case 'Z': /* Break point set */
+                       gdb_cmd_break(ks);
+                       break;
+               case 'C': /* Exception passing */
+                       tmp = gdb_cmd_exception_pass(ks);
+                       if (tmp > 0)
+                               goto default_handle;
+                       if (tmp == 0)
+                               break;
+                       /* Fall through on tmp < 0 */
+               case 'c': /* Continue packet */
+               case 's': /* Single step packet */
+                       if (kgdb_contthread && kgdb_contthread != current) {
+                               /* Can't switch threads in kgdb */
+                               error_packet(remcom_out_buffer, -EINVAL);
+                               break;
+                       }
+                       kgdb_activate_sw_breakpoints();
+                       /* Fall through to default processing */
+               default:
+default_handle:
+                       error = kgdb_arch_handle_exception(ks->ex_vector,
+                                               ks->signo,
+                                               ks->err_code,
+                                               remcom_in_buffer,
+                                               remcom_out_buffer,
+                                               ks->linux_regs);
+                       /*
+                        * Leave cmd processing on error, detach,
+                        * kill, continue, or single step.
+                        */
+                       if (error >= 0 || remcom_in_buffer[0] == 'D' ||
+                           remcom_in_buffer[0] == 'k') {
+                               error = 0;
+                               goto kgdb_exit;
+                       }
+
+               }
+
+               /* reply to the request */
+               put_packet(remcom_out_buffer);
+       }
+
+kgdb_exit:
+       if (ks->pass_exception)
+               error = 1;
+       return error;
+}
+
+static int kgdb_reenter_check(struct kgdb_state *ks)
+{
+       unsigned long addr;
+
+       if (atomic_read(&kgdb_active) != raw_smp_processor_id())
+               return 0;
+
+       /* Panic on recursive debugger calls: */
+       exception_level++;
+       addr = kgdb_arch_pc(ks->ex_vector, ks->linux_regs);
+       kgdb_deactivate_sw_breakpoints();
+
+       /*
+        * If the break point removed ok at the place exception
+        * occurred, try to recover and print a warning to the end
+        * user because the user planted a breakpoint in a place that
+        * KGDB needs in order to function.
+        */
+       if (kgdb_remove_sw_break(addr) == 0) {
+               exception_level = 0;
+               kgdb_skipexception(ks->ex_vector, ks->linux_regs);
+               kgdb_activate_sw_breakpoints();
+               printk(KERN_CRIT "KGDB: re-enter error: breakpoint removed %lx\n",
+                       addr);
+               WARN_ON_ONCE(1);
+
+               return 1;
+       }
+       remove_all_break();
+       kgdb_skipexception(ks->ex_vector, ks->linux_regs);
+
+       if (exception_level > 1) {
+               dump_stack();
+               panic("Recursive entry to debugger");
+       }
+
+       printk(KERN_CRIT "KGDB: re-enter exception: ALL breakpoints killed\n");
+       dump_stack();
+       panic("Recursive entry to debugger");
+
+       return 1;
+}
+
+/*
+ * kgdb_handle_exception() - main entry point from a kernel exception
+ *
+ * Locking hierarchy:
+ *     interface locks, if any (begin_session)
+ *     kgdb lock (kgdb_active)
+ */
+int
+kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
+{
+       struct kgdb_state kgdb_var;
+       struct kgdb_state *ks = &kgdb_var;
+       unsigned long flags;
+       int error = 0;
+       int i, cpu;
+
+       ks->cpu                 = raw_smp_processor_id();
+       ks->ex_vector           = evector;
+       ks->signo               = signo;
+       ks->ex_vector           = evector;
+       ks->err_code            = ecode;
+       ks->kgdb_usethreadid    = 0;
+       ks->linux_regs          = regs;
+
+       if (kgdb_reenter_check(ks))
+               return 0; /* Ouch, double exception ! */
+
+acquirelock:
+       /*
+        * Interrupts will be restored by the 'trap return' code, except when
+        * single stepping.
+        */
+       local_irq_save(flags);
+
+       cpu = raw_smp_processor_id();
+
+       /*
+        * Acquire the kgdb_active lock:
+        */
+       while (atomic_cmpxchg(&kgdb_active, -1, cpu) != -1)
+               cpu_relax();
+
+       /*
+        * Do not start the debugger connection on this CPU if the last
+        * instance of the exception handler wanted to come into the
+        * debugger on a different CPU via a single step
+        */
+       if (atomic_read(&kgdb_cpu_doing_single_step) != -1 &&
+           atomic_read(&kgdb_cpu_doing_single_step) != cpu) {
+
+               atomic_set(&kgdb_active, -1);
+               clocksource_touch_watchdog();
+               local_irq_restore(flags);
+
+               goto acquirelock;
+       }
+
+       if (!kgdb_io_ready(1)) {
+               error = 1;
+               goto kgdb_restore; /* No I/O connection, so resume the system */
+       }
+
+       /*
+        * Don't enter if we have hit a removed breakpoint.
+        */
+       if (kgdb_skipexception(ks->ex_vector, ks->linux_regs))
+               goto kgdb_restore;
+
+       /* Call the I/O driver's pre_exception routine */
+       if (kgdb_io_ops->pre_exception)
+               kgdb_io_ops->pre_exception();
+
+       kgdb_info[ks->cpu].debuggerinfo = ks->linux_regs;
+       kgdb_info[ks->cpu].task = current;
+
+       kgdb_disable_hw_debug(ks->linux_regs);
+
+       /*
+        * Get the passive CPU lock which will hold all the non-primary
+        * CPU in a spin state while the debugger is active
+        */
+       if (!kgdb_single_step || !kgdb_contthread) {
+               for (i = 0; i < NR_CPUS; i++)
+                       atomic_set(&passive_cpu_wait[i], 1);
+       }
+
+       /*
+        * spin_lock code is good enough as a barrier so we don't
+        * need one here:
+        */
+       atomic_set(&cpu_in_kgdb[ks->cpu], 1);
+
+#ifdef CONFIG_SMP
+       /* Signal the other CPUs to enter kgdb_wait() */
+       if ((!kgdb_single_step || !kgdb_contthread) && kgdb_do_roundup)
+               kgdb_roundup_cpus(flags);
+#endif
+
+       /*
+        * Wait for the other CPUs to be notified and be waiting for us:
+        */
+       for_each_online_cpu(i) {
+               while (!atomic_read(&cpu_in_kgdb[i]))
+                       cpu_relax();
+       }
+
+       /*
+        * At this point the primary processor is completely
+        * in the debugger and all secondary CPUs are quiescent
+        */
+       kgdb_post_primary_code(ks->linux_regs, ks->ex_vector, ks->err_code);
+       kgdb_deactivate_sw_breakpoints();
+       kgdb_single_step = 0;
+       kgdb_contthread = NULL;
+       exception_level = 0;
+
+       /* Talk to debugger with gdbserial protocol */
+       error = gdb_serial_stub(ks);
+
+       /* Call the I/O driver's post_exception routine */
+       if (kgdb_io_ops->post_exception)
+               kgdb_io_ops->post_exception();
+
+       kgdb_info[ks->cpu].debuggerinfo = NULL;
+       kgdb_info[ks->cpu].task = NULL;
+       atomic_set(&cpu_in_kgdb[ks->cpu], 0);
+
+       if (!kgdb_single_step || !kgdb_contthread) {
+               for (i = NR_CPUS-1; i >= 0; i--)
+                       atomic_set(&passive_cpu_wait[i], 0);
+               /*
+                * Wait till all the CPUs have quit
+                * from the debugger.
+                */
+               for_each_online_cpu(i) {
+                       while (atomic_read(&cpu_in_kgdb[i]))
+                               cpu_relax();
+               }
+       }
+
+kgdb_restore:
+       /* Free kgdb_active */
+       atomic_set(&kgdb_active, -1);
+       clocksource_touch_watchdog();
+       local_irq_restore(flags);
+
+       return error;
+}
+
+int kgdb_nmicallback(int cpu, void *regs)
+{
+#ifdef CONFIG_SMP
+       if (!atomic_read(&cpu_in_kgdb[cpu]) &&
+                       atomic_read(&kgdb_active) != cpu &&
+                       atomic_read(&cpu_in_kgdb[atomic_read(&kgdb_active)])) {
+               kgdb_wait((struct pt_regs *)regs);
+               return 0;
+       }
+#endif
+       return 1;
+}
+
+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(&kgdb_active) != -1)
+               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,
+       .index          = -1,
+};
+
+#ifdef CONFIG_MAGIC_SYSRQ
+static void sysrq_handle_gdb(int key, struct tty_struct *tty)
+{
+       if (!kgdb_io_ops) {
+               printk(KERN_CRIT "ERROR: No KGDB I/O module available\n");
+               return;
+       }
+       if (!kgdb_connected)
+               printk(KERN_CRIT "Entering KGDB\n");
+
+       kgdb_breakpoint();
+}
+
+static struct sysrq_key_op sysrq_gdb_op = {
+       .handler        = sysrq_handle_gdb,
+       .help_msg       = "Gdb",
+       .action_msg     = "GDB",
+};
+#endif
+
+static void kgdb_register_callbacks(void)
+{
+       if (!kgdb_io_module_registered) {
+               kgdb_io_module_registered = 1;
+               kgdb_arch_init();
+#ifdef CONFIG_MAGIC_SYSRQ
+               register_sysrq_key('g', &sysrq_gdb_op);
+#endif
+               if (kgdb_use_con && !kgdb_con_registered) {
+                       register_console(&kgdbcons);
+                       kgdb_con_registered = 1;
+               }
+       }
+}
+
+static void kgdb_unregister_callbacks(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_io_module_registered) {
+               kgdb_io_module_registered = 0;
+               kgdb_arch_exit();
+#ifdef CONFIG_MAGIC_SYSRQ
+               unregister_sysrq_key('g', &sysrq_gdb_op);
+#endif
+               if (kgdb_con_registered) {
+                       unregister_console(&kgdbcons);
+                       kgdb_con_registered = 0;
+               }
+       }
+}
+
+static void kgdb_initial_breakpoint(void)
+{
+       kgdb_break_asap = 0;
+
+       printk(KERN_CRIT "kgdb: Waiting for connection from remote gdb...\n");
+       kgdb_breakpoint();
+}
+
+/**
+ *     kgdb_register_io_module - register KGDB IO module
+ *     @new_kgdb_io_ops: the io ops vector
+ *
+ *     Register it with the KGDB core.
+ */
+int kgdb_register_io_module(struct kgdb_io *new_kgdb_io_ops)
+{
+       int err;
+
+       spin_lock(&kgdb_registration_lock);
+
+       if (kgdb_io_ops) {
+               spin_unlock(&kgdb_registration_lock);
+
+               printk(KERN_ERR "kgdb: Another I/O driver is already "
+                               "registered with KGDB.\n");
+               return -EBUSY;
+       }
+
+       if (new_kgdb_io_ops->init) {
+               err = new_kgdb_io_ops->init();
+               if (err) {
+                       spin_unlock(&kgdb_registration_lock);
+                       return err;
+               }
+       }
+
+       kgdb_io_ops = new_kgdb_io_ops;
+
+       spin_unlock(&kgdb_registration_lock);
+
+       printk(KERN_INFO "kgdb: Registered I/O driver %s.\n",
+              new_kgdb_io_ops->name);
+
+       /* Arm KGDB now. */
+       kgdb_register_callbacks();
+
+       if (kgdb_break_asap)
+               kgdb_initial_breakpoint();
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(kgdb_register_io_module);
+
+/**
+ *     kkgdb_unregister_io_module - unregister KGDB IO module
+ *     @old_kgdb_io_ops: the io ops vector
+ *
+ *     Unregister it with the KGDB core.
+ */
+void kgdb_unregister_io_module(struct kgdb_io *old_kgdb_io_ops)
+{
+       BUG_ON(kgdb_connected);
+
+       /*
+        * KGDB is no longer able to communicate out, so
+        * unregister our callbacks and reset state.
+        */
+       kgdb_unregister_callbacks();
+
+       spin_lock(&kgdb_registration_lock);
+
+       WARN_ON_ONCE(kgdb_io_ops != old_kgdb_io_ops);
+       kgdb_io_ops = NULL;
+
+       spin_unlock(&kgdb_registration_lock);
+
+       printk(KERN_INFO
+               "kgdb: Unregistered I/O driver %s, debugger disabled.\n",
+               old_kgdb_io_ops->name);
+}
+EXPORT_SYMBOL_GPL(kgdb_unregister_io_module);
+
+/**
+ * kgdb_breakpoint - generate breakpoint exception
+ *
+ * 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 kgdb_breakpoint(void)
+{
+       atomic_set(&kgdb_setting_breakpoint, 1);
+       wmb(); /* Sync point before breakpoint */
+       arch_kgdb_breakpoint();
+       wmb(); /* Sync point after breakpoint */
+       atomic_set(&kgdb_setting_breakpoint, 0);
+}
+EXPORT_SYMBOL_GPL(kgdb_breakpoint);
+
+static int __init opt_kgdb_wait(char *str)
+{
+       kgdb_break_asap = 1;
+
+       if (kgdb_io_module_registered)
+               kgdb_initial_breakpoint();
+
+       return 0;
+}
+
+early_param("kgdbwait", opt_kgdb_wait);
index 7f60097..f61402b 100644 (file)
@@ -222,6 +222,18 @@ void clocksource_resume(void)
 }
 
 /**
+ * clocksource_touch_watchdog - Update watchdog
+ *
+ * Update the watchdog after exception contexts such as kgdb so as not
+ * to incorrectly trip the watchdog.
+ *
+ */
+void clocksource_touch_watchdog(void)
+{
+       clocksource_resume_watchdog();
+}
+
+/**
  * clocksource_get_next - Returns the selected clocksource
  *
  */
index 80db357..aaba784 100644 (file)
@@ -612,3 +612,5 @@ config PROVIDE_OHCI1394_DMA_INIT
          See Documentation/debugging-via-ohci1394.txt for more information.
 
 source "samples/Kconfig"
+
+source "lib/Kconfig.kgdb"
diff --git a/lib/Kconfig.kgdb b/lib/Kconfig.kgdb
new file mode 100644 (file)
index 0000000..f2e01ac
--- /dev/null
@@ -0,0 +1,58 @@
+
+menuconfig KGDB
+       bool "KGDB: kernel debugging with remote gdb"
+       select FRAME_POINTER
+       depends on HAVE_ARCH_KGDB
+       depends on DEBUG_KERNEL && EXPERIMENTAL
+       help
+         If you say Y here, it will be possible to remotely debug the
+         kernel using gdb.  Documentation of kernel debugger is available
+         at http://kgdb.sourceforge.net as well as in DocBook form
+         in Documentation/DocBook/.  If unsure, say N.
+
+config HAVE_ARCH_KGDB_SHADOW_INFO
+       bool
+
+config HAVE_ARCH_KGDB
+       bool
+
+config KGDB_SERIAL_CONSOLE
+       tristate "KGDB: use kgdb over the serial console"
+       depends on KGDB
+       select CONSOLE_POLL
+       select MAGIC_SYSRQ
+       default y
+       help
+         Share a serial console with kgdb. Sysrq-g must be used
+         to break in initially.
+
+config KGDB_TESTS
+       bool "KGDB: internal test suite"
+       depends on KGDB
+       default n
+       help
+         This is a kgdb I/O module specifically designed to test
+         kgdb's internal functions.  This kgdb I/O module is
+         intended to for the development of new kgdb stubs
+         as well as regression testing the kgdb internals.
+         See the drivers/misc/kgdbts.c for the details about
+         the tests.  The most basic of this I/O module is to boot
+         a kernel boot arguments "kgdbwait kgdbts=V1F100"
+
+config KGDB_TESTS_ON_BOOT
+       bool "KGDB: Run tests on boot"
+       depends on KGDB_TESTS
+       default n
+       help
+         Run the kgdb tests on boot up automatically without the need
+         to pass in a kernel parameter
+
+config KGDB_TESTS_BOOT_STRING
+       string "KGDB: which internal kgdb tests to run"
+       depends on KGDB_TESTS_ON_BOOT
+       default "V1F100"
+       help
+         This is the command string to send the kgdb test suite on
+         boot.  See the drivers/misc/kgdbts.c for detailed
+         information about other strings you could use beyond the
+         default of V1F100.
index a5b0dd9..18c143b 100644 (file)
@@ -8,7 +8,7 @@ mmu-$(CONFIG_MMU)       := fremap.o highmem.o madvise.o memory.o mincore.o \
                           vmalloc.o
 
 obj-y                  := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \
-                          page_alloc.o page-writeback.o pdflush.o \
+                          maccess.o page_alloc.o page-writeback.o pdflush.o \
                           readahead.o swap.o truncate.o vmscan.o \
                           prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \
                           page_isolation.o $(mmu-y)
diff --git a/mm/maccess.c b/mm/maccess.c
new file mode 100644 (file)
index 0000000..ac40796
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Access kernel memory without faulting.
+ */
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+
+/**
+ * probe_kernel_read(): safely attempt to read from a location
+ * @dst: pointer to the buffer that shall take the data
+ * @src: address to read from
+ * @size: size of the data chunk
+ *
+ * Safely read from address @src to the buffer at @dst.  If a kernel fault
+ * happens, handle that and return -EFAULT.
+ */
+long probe_kernel_read(void *dst, void *src, size_t size)
+{
+       long ret;
+       mm_segment_t old_fs = get_fs();
+
+       set_fs(KERNEL_DS);
+       pagefault_disable();
+       ret = __copy_from_user_inatomic(dst,
+                       (__force const void __user *)src, size);
+       pagefault_enable();
+       set_fs(old_fs);
+
+       return ret ? -EFAULT : 0;
+}
+EXPORT_SYMBOL_GPL(probe_kernel_read);
+
+/**
+ * probe_kernel_write(): safely attempt to write to a location
+ * @dst: address to write to
+ * @src: pointer to the data that shall be written
+ * @size: size of the data chunk
+ *
+ * Safely write to address @dst from the buffer at @src.  If a kernel fault
+ * happens, handle that and return -EFAULT.
+ */
+long probe_kernel_write(void *dst, void *src, size_t size)
+{
+       long ret;
+       mm_segment_t old_fs = get_fs();
+
+       set_fs(KERNEL_DS);
+       pagefault_disable();
+       ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
+       pagefault_enable();
+       set_fs(old_fs);
+
+       return ret ? -EFAULT : 0;
+}
+EXPORT_SYMBOL_GPL(probe_kernel_write);