Merge branch 'kdb-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb
* 'kdb-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb: (25 commits) kdb,debug_core: Allow the debug core to receive a panic notification MAINTAINERS: update kgdb, kdb, and debug_core info debug_core,kdb: Allow the debug core to process a recursive debug entry printk,kdb: capture printk() when in kdb shell kgdboc,kdb: Allow kdb to work on a non open console port kgdb: Add the ability to schedule a breakpoint via a tasklet mips,kgdb: kdb low level trap catch and stack trace powerpc,kgdb: Introduce low level trap catching x86,kgdb: Add low level debug hook kgdb: remove post_primary_code references kgdb,docs: Update the kgdb docs to include kdb kgdboc,keyboard: Keyboard driver for kdb with kgdb kgdb: gdb "monitor" -> kdb passthrough sparc,sunzilog: Add console polling support for sunzilog serial driver sh,sh-sci: Use NO_POLL_CHAR in the SCIF polled console code kgdb,8250,pl011: Return immediately from console poll kgdb: core changes to support kdb kdb: core for kgdb back end (2 of 2) kdb: core for kgdb back end (1 of 2) kgdb,blackfin: Add in kgdb_arch_set_pc for blackfin ...
This commit is contained in:
commit
90b9a32d8f
51 changed files with 9250 additions and 2026 deletions
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<book id="kgdbOnLinux">
|
<book id="kgdbOnLinux">
|
||||||
<bookinfo>
|
<bookinfo>
|
||||||
<title>Using kgdb and the kgdb Internals</title>
|
<title>Using kgdb, kdb and the kernel debugger internals</title>
|
||||||
|
|
||||||
<authorgroup>
|
<authorgroup>
|
||||||
<author>
|
<author>
|
||||||
|
@ -17,33 +17,8 @@
|
||||||
</affiliation>
|
</affiliation>
|
||||||
</author>
|
</author>
|
||||||
</authorgroup>
|
</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>
|
<copyright>
|
||||||
<year>2008</year>
|
<year>2008,2010</year>
|
||||||
<holder>Wind River Systems, Inc.</holder>
|
<holder>Wind River Systems, Inc.</holder>
|
||||||
</copyright>
|
</copyright>
|
||||||
<copyright>
|
<copyright>
|
||||||
|
@ -69,41 +44,76 @@
|
||||||
<chapter id="Introduction">
|
<chapter id="Introduction">
|
||||||
<title>Introduction</title>
|
<title>Introduction</title>
|
||||||
<para>
|
<para>
|
||||||
kgdb is a source level debugger for linux kernel. It is used along
|
The kernel has two different debugger front ends (kdb and kgdb)
|
||||||
with gdb to debug a linux kernel. The expectation is that gdb can
|
which interface to the debug core. It is possible to use either
|
||||||
be used to "break in" to the kernel to inspect memory, variables
|
of the debugger front ends and dynamically transition between them
|
||||||
and look through call stack information similar to what an
|
if you configure the kernel properly at compile and runtime.
|
||||||
application developer would use gdb for. It is possible to place
|
|
||||||
breakpoints in kernel code and perform some limited execution
|
|
||||||
stepping.
|
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Two machines are required for using kgdb. One of these machines is a
|
Kdb is simplistic shell-style interface which you can use on a
|
||||||
development machine and the other is a test machine. The kernel
|
system console with a keyboard or serial console. You can use it
|
||||||
to be debugged runs on the test machine. The development machine
|
to inspect memory, registers, process lists, dmesg, and even set
|
||||||
runs an instance of gdb against the vmlinux file which contains
|
breakpoints to stop in a certain location. Kdb is not a source
|
||||||
the symbols (not boot image such as bzImage, zImage, uImage...).
|
level debugger, although you can set breakpoints and execute some
|
||||||
In gdb the developer specifies the connection parameters and
|
basic kernel run control. Kdb is mainly aimed at doing some
|
||||||
connects to kgdb. The type of connection a developer makes with
|
analysis to aid in development or diagnosing kernel problems. You
|
||||||
gdb depends on the availability of kgdb I/O modules compiled as
|
can access some symbols by name in kernel built-ins or in kernel
|
||||||
builtin's or kernel modules in the test machine's kernel.
|
modules if the code was built
|
||||||
|
with <symbol>CONFIG_KALLSYMS</symbol>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Kgdb is intended to be used as a source level debugger for the
|
||||||
|
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 call stack
|
||||||
|
information similar to the way an application developer would use
|
||||||
|
gdb to debug an application. 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 the target machine. The
|
||||||
|
kernel to be debugged runs on the target 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. The type of connection a
|
||||||
|
developer makes with gdb depends on the availability of kgdb I/O
|
||||||
|
modules compiled as built-ins or loadable kernel modules in the test
|
||||||
|
machine's kernel.
|
||||||
</para>
|
</para>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="CompilingAKernel">
|
<chapter id="CompilingAKernel">
|
||||||
<title>Compiling a kernel</title>
|
<title>Compiling a kernel</title>
|
||||||
|
<para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>In order to enable compilation of kdb, you must first enable kgdb.</para></listitem>
|
||||||
|
<listitem><para>The kgdb test compile options are described in the kgdb test suite chapter.</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
<sect1 id="CompileKGDB">
|
||||||
|
<title>Kernel config options for kgdb</title>
|
||||||
<para>
|
<para>
|
||||||
To enable <symbol>CONFIG_KGDB</symbol> you should first turn on
|
To enable <symbol>CONFIG_KGDB</symbol> you should first turn on
|
||||||
"Prompt for development and/or incomplete code/drivers"
|
"Prompt for development and/or incomplete code/drivers"
|
||||||
(CONFIG_EXPERIMENTAL) in "General setup", then under the
|
(CONFIG_EXPERIMENTAL) in "General setup", then under the
|
||||||
"Kernel debugging" select "KGDB: kernel debugging with remote gdb".
|
"Kernel debugging" select "KGDB: kernel debugger".
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
While it is not a hard requirement that you have symbols in your
|
||||||
|
vmlinux file, gdb tends not to be very useful without the symbolic
|
||||||
|
data, so you will want to turn
|
||||||
|
on <symbol>CONFIG_DEBUG_INFO</symbol> which is called "Compile the
|
||||||
|
kernel with debug info" in the config menu.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
It is advised, but not required that you turn on the
|
It is advised, but not required that you turn on the
|
||||||
CONFIG_FRAME_POINTER kernel option. This option inserts code to
|
<symbol>CONFIG_FRAME_POINTER</symbol> kernel option which is called "Compile the
|
||||||
into the compiled executable which saves the frame information in
|
kernel with frame pointers" in the config menu. This option
|
||||||
registers or on the stack at different points which will allow a
|
inserts code to into the compiled executable which saves the frame
|
||||||
debugger such as gdb to more accurately construct stack back traces
|
information in registers or on the stack at different points which
|
||||||
while debugging the kernel.
|
allows a debugger such as gdb to more accurately construct
|
||||||
|
stack back traces while debugging the kernel.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
If the architecture that you are using supports the kernel option
|
If the architecture that you are using supports the kernel option
|
||||||
|
@ -116,38 +126,160 @@
|
||||||
this option.
|
this option.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Next you should choose one of more I/O drivers to interconnect debugging
|
Next you should choose one of more I/O drivers to interconnect
|
||||||
host and debugged target. Early boot debugging requires a KGDB
|
debugging host and debugged target. Early boot debugging requires
|
||||||
I/O driver that supports early debugging and the driver must be
|
a KGDB I/O driver that supports early debugging and the driver
|
||||||
built into the kernel directly. Kgdb I/O driver configuration
|
must be built into the kernel directly. Kgdb I/O driver
|
||||||
takes place via kernel or module parameters, see following
|
configuration takes place via kernel or module parameters which
|
||||||
chapter.
|
you can learn more about in the in the section that describes the
|
||||||
|
parameter "kgdboc".
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>Here is an example set of .config symbols to enable or
|
||||||
The kgdb test compile options are described in the kgdb test suite chapter.
|
disable for kgdb:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para># CONFIG_DEBUG_RODATA is not set</para></listitem>
|
||||||
|
<listitem><para>CONFIG_FRAME_POINTER=y</para></listitem>
|
||||||
|
<listitem><para>CONFIG_KGDB=y</para></listitem>
|
||||||
|
<listitem><para>CONFIG_KGDB_SERIAL_CONSOLE=y</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
|
</sect1>
|
||||||
|
<sect1 id="CompileKDB">
|
||||||
|
<title>Kernel config options for kdb</title>
|
||||||
|
<para>Kdb is quite a bit more complex than the simple gdbstub
|
||||||
|
sitting on top of the kernel's debug core. Kdb must implement a
|
||||||
|
shell, and also adds some helper functions in other parts of the
|
||||||
|
kernel, responsible for printing out interesting data such as what
|
||||||
|
you would see if you ran "lsmod", or "ps". In order to build kdb
|
||||||
|
into the kernel you follow the same steps as you would for kgdb.
|
||||||
|
</para>
|
||||||
|
<para>The main config option for kdb
|
||||||
|
is <symbol>CONFIG_KGDB_KDB</symbol> which is called "KGDB_KDB:
|
||||||
|
include kdb frontend for kgdb" in the config menu. In theory you
|
||||||
|
would have already also selected an I/O driver such as the
|
||||||
|
CONFIG_KGDB_SERIAL_CONSOLE interface if you plan on using kdb on a
|
||||||
|
serial port, when you were configuring kgdb.
|
||||||
|
</para>
|
||||||
|
<para>If you want to use a PS/2-style keyboard with kdb, you would
|
||||||
|
select CONFIG_KDB_KEYBOARD which is called "KGDB_KDB: keyboard as
|
||||||
|
input device" in the config menu. The CONFIG_KDB_KEYBOARD option
|
||||||
|
is not used for anything in the gdb interface to kgdb. The
|
||||||
|
CONFIG_KDB_KEYBOARD option only works with kdb.
|
||||||
|
</para>
|
||||||
|
<para>Here is an example set of .config symbols to enable/disable kdb:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para># CONFIG_DEBUG_RODATA is not set</para></listitem>
|
||||||
|
<listitem><para>CONFIG_FRAME_POINTER=y</para></listitem>
|
||||||
|
<listitem><para>CONFIG_KGDB=y</para></listitem>
|
||||||
|
<listitem><para>CONFIG_KGDB_SERIAL_CONSOLE=y</para></listitem>
|
||||||
|
<listitem><para>CONFIG_KGDB_KDB=y</para></listitem>
|
||||||
|
<listitem><para>CONFIG_KDB_KEYBOARD=y</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="EnableKGDB">
|
<chapter id="kgdbKernelArgs">
|
||||||
<title>Enable kgdb for debugging</title>
|
<title>Kernel Debugger Boot Arguments</title>
|
||||||
<para>
|
<para>This section describes the various runtime kernel
|
||||||
In order to use kgdb you must activate it by passing configuration
|
parameters that affect the configuration of the kernel debugger.
|
||||||
information to one of the kgdb I/O drivers. If you do not pass any
|
The following chapter covers using kdb and kgdb as well as
|
||||||
configuration information kgdb will not do anything at all. Kgdb
|
provides some examples of the configuration parameters.</para>
|
||||||
will only actively hook up to the kernel trap hooks if a kgdb I/O
|
<sect1 id="kgdboc">
|
||||||
driver is loaded and configured. If you unconfigure a kgdb I/O
|
<title>Kernel parameter: kgdboc</title>
|
||||||
driver, kgdb will unregister all the kernel hook points.
|
<para>The kgdboc driver was originally an abbreviation meant to
|
||||||
|
stand for "kgdb over console". Today it is the primary mechanism
|
||||||
|
to configure how to communicate from gdb to kgdb as well as the
|
||||||
|
devices you want to use to interact with the kdb shell.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>For kgdb/gdb, kgdboc is designed to work with a single serial
|
||||||
All drivers can be reconfigured at run time, if
|
port. It is intended to cover the circumstance where you want to
|
||||||
<symbol>CONFIG_SYSFS</symbol> and <symbol>CONFIG_MODULES</symbol>
|
use a serial console as your primary console as well as using it to
|
||||||
are enabled, by echo'ing a new config string to
|
perform kernel debugging. It is also possible to use kgdb on a
|
||||||
<constant>/sys/module/<driver>/parameter/<option></constant>.
|
serial port which is not designated as a system console. Kgdboc
|
||||||
The driver can be unconfigured by passing an empty string. You cannot
|
may be configured as a kernel built-in or a kernel loadable module.
|
||||||
change the configuration while the debugger is attached. Make sure
|
You can only make use of <constant>kgdbwait</constant> and early
|
||||||
to detach the debugger with the <constant>detach</constant> command
|
debugging if you build kgdboc into the kernel as a built-in.
|
||||||
prior to trying unconfigure a kgdb I/O driver.
|
|
||||||
</para>
|
</para>
|
||||||
|
<sect2 id="kgdbocArgs">
|
||||||
|
<title>kgdboc arguments</title>
|
||||||
|
<para>Usage: <constant>kgdboc=[kbd][[,]serial_device][,baud]</constant></para>
|
||||||
|
<sect3 id="kgdbocArgs1">
|
||||||
|
<title>Using loadable module or built-in</title>
|
||||||
|
<para>
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>As a kernel built-in:</para>
|
||||||
|
<para>Use the kernel boot argument: <constant>kgdboc=<tty-device>,[baud]</constant></para></listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>As a kernel loadable module:</para>
|
||||||
|
<para>Use the command: <constant>modprobe kgdboc kgdboc=<tty-device>,[baud]</constant></para>
|
||||||
|
<para>Here are two examples of how you might formate the kgdboc
|
||||||
|
string. The first is for an x86 target using the first serial port.
|
||||||
|
The second example is for the ARM Versatile AB using the second
|
||||||
|
serial port.
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para><constant>kgdboc=ttyS0,115200</constant></para></listitem>
|
||||||
|
<listitem><para><constant>kgdboc=ttyAMA1,115200</constant></para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist></para>
|
||||||
|
</sect3>
|
||||||
|
<sect3 id="kgdbocArgs2">
|
||||||
|
<title>Configure kgdboc at runtime with sysfs</title>
|
||||||
|
<para>At run time you can enable or disable kgdboc by echoing a
|
||||||
|
parameters into the sysfs. Here are two examples:</para>
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>Enable kgdboc on ttyS0</para>
|
||||||
|
<para><constant>echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc</constant></para></listitem>
|
||||||
|
<listitem><para>Disable kgdboc</para>
|
||||||
|
<para><constant>echo "" > /sys/module/kgdboc/parameters/kgdboc</constant></para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
<para>NOTE: You do not need to specify the baud if you are
|
||||||
|
configuring the console on tty which is already configured or
|
||||||
|
open.</para>
|
||||||
|
</sect3>
|
||||||
|
<sect3 id="kgdbocArgs3">
|
||||||
|
<title>More examples</title>
|
||||||
|
<para>You can configure kgdboc to use the keyboard, and or a serial device
|
||||||
|
depending on if you are using kdb and or kgdb, in one of the
|
||||||
|
following scenarios.
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>kdb and kgdb over only a serial port</para>
|
||||||
|
<para><constant>kgdboc=<serial_device>[,baud]</constant></para>
|
||||||
|
<para>Example: <constant>kgdboc=ttyS0,115200</constant></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>kdb and kgdb with keyboard and a serial port</para>
|
||||||
|
<para><constant>kgdboc=kbd,<serial_device>[,baud]</constant></para>
|
||||||
|
<para>Example: <constant>kgdboc=kbd,ttyS0,115200</constant></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>kdb with a keyboard</para>
|
||||||
|
<para><constant>kgdboc=kbd</constant></para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</para>
|
||||||
|
</sect3>
|
||||||
|
<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 program.
|
||||||
|
A console proxy has a separate TCP port for the debugger and a separate
|
||||||
|
TCP port for the "human" console. The proxy can take care of sending
|
||||||
|
the sysrq-g for you.
|
||||||
|
</para>
|
||||||
|
<para>When using kgdboc with no debugger proxy, you can end up
|
||||||
|
connecting the debugger at 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
|
||||||
|
this 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="kgdbwait">
|
<sect1 id="kgdbwait">
|
||||||
<title>Kernel parameter: kgdbwait</title>
|
<title>Kernel parameter: kgdbwait</title>
|
||||||
<para>
|
<para>
|
||||||
|
@ -162,103 +294,204 @@
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
The kernel will stop and wait as early as the I/O driver and
|
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
|
architecture allows when you use this option. If you build the
|
||||||
kgdb I/O driver as a kernel module kgdbwait will not do anything.
|
kgdb I/O driver as a loadable kernel module kgdbwait will not do
|
||||||
|
anything.
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
<sect1 id="kgdboc">
|
<sect1 id="kgdbcon">
|
||||||
<title>Kernel parameter: kgdboc</title>
|
<title>Kernel parameter: kgdbcon</title>
|
||||||
<para>
|
<para> The kgdbcon feature allows you to see printk() messages
|
||||||
The kgdboc driver was originally an abbreviation meant to stand for
|
inside gdb while gdb is connected to the kernel. Kdb does not make
|
||||||
"kgdb over console". Kgdboc is designed to work with a single
|
use of the kgdbcon feature.
|
||||||
serial port. It was meant to cover the circumstance
|
</para>
|
||||||
where you wanted to use a serial console as your primary console as
|
<para>Kgdb supports using the gdb serial protocol to send console
|
||||||
well as using it to perform kernel debugging. Of course you can
|
messages to the debugger when the debugger is connected and running.
|
||||||
also use kgdboc without assigning a console to the same port.
|
There are two ways to activate this feature.
|
||||||
</para>
|
<orderedlist>
|
||||||
<sect2 id="UsingKgdboc">
|
<listitem><para>Activate with the kernel command line option:</para>
|
||||||
<title>Using kgdboc</title>
|
<para><constant>kgdbcon</constant></para>
|
||||||
<para>
|
</listitem>
|
||||||
You can configure kgdboc via sysfs or a module or kernel boot line
|
<listitem><para>Use sysfs before configuring an I/O driver</para>
|
||||||
parameter depending on if you build with CONFIG_KGDBOC as a module
|
<para>
|
||||||
or built-in.
|
<constant>echo 1 > /sys/module/kgdb/parameters/kgdb_use_con</constant>
|
||||||
<orderedlist>
|
</para>
|
||||||
<listitem><para>From the module load or build-in</para>
|
<para>
|
||||||
<para><constant>kgdboc=<tty-device>,[baud]</constant></para>
|
NOTE: If you do this after you configure the kgdb I/O driver, the
|
||||||
<para>
|
setting will not take effect until the next point the I/O is
|
||||||
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>
|
reconfigured.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem><para>From sysfs</para>
|
</orderedlist>
|
||||||
<para><constant>echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc</constant></para>
|
<para>IMPORTANT NOTE: You cannot use kgdboc + kgdbcon on a tty that is an
|
||||||
</listitem>
|
active system console. An example incorrect usage is <constant>console=ttyS0,115200 kgdboc=ttyS0 kgdbcon</constant>
|
||||||
</orderedlist>
|
</para>
|
||||||
</para>
|
<para>It is possible to use this option with kgdboc on a tty that is not a system console.
|
||||||
<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 > /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) is not supported.
|
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="ConnectingGDB">
|
<chapter id="usingKDB">
|
||||||
<title>Connecting gdb</title>
|
<title>Using kdb</title>
|
||||||
|
<para>
|
||||||
|
</para>
|
||||||
|
<sect1 id="quickKDBserial">
|
||||||
|
<title>Quick start for kdb on a serial port</title>
|
||||||
|
<para>This is a quick example of how to use kdb.</para>
|
||||||
|
<para><orderedlist>
|
||||||
|
<listitem><para>Boot kernel with arguments:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><constant>console=ttyS0,115200 kgdboc=ttyS0,115200</constant></para></listitem>
|
||||||
|
</itemizedlist></para>
|
||||||
|
<para>OR</para>
|
||||||
|
<para>Configure kgdboc after the kernel booted; assuming you are using a serial port console:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><constant>echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc</constant></para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>Enter the kernel debugger manually or by waiting for an oops or fault. There are several ways you can enter the kernel debugger manually; all involve using the sysrq-g, which means you must have enabled CONFIG_MAGIC_SYSRQ=y in your kernel config.</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>When logged in as root or with a super user session you can run:</para>
|
||||||
|
<para><constant>echo g > /proc/sysrq-trigger</constant></para></listitem>
|
||||||
|
<listitem><para>Example using minicom 2.2</para>
|
||||||
|
<para>Press: <constant>Control-a</constant></para>
|
||||||
|
<para>Press: <constant>f</constant></para>
|
||||||
|
<para>Press: <constant>g</constant></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>When you have telneted to a terminal server that supports sending a remote break</para>
|
||||||
|
<para>Press: <constant>Control-]</constant></para>
|
||||||
|
<para>Type in:<constant>send break</constant></para>
|
||||||
|
<para>Press: <constant>Enter</constant></para>
|
||||||
|
<para>Press: <constant>g</constant></para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>From the kdb prompt you can run the "help" command to see a complete list of the commands that are available.</para>
|
||||||
|
<para>Some useful commands in kdb include:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>lsmod -- Shows where kernel modules are loaded</para></listitem>
|
||||||
|
<listitem><para>ps -- Displays only the active processes</para></listitem>
|
||||||
|
<listitem><para>ps A -- Shows all the processes</para></listitem>
|
||||||
|
<listitem><para>summary -- Shows kernel version info and memory usage</para></listitem>
|
||||||
|
<listitem><para>bt -- Get a backtrace of the current process using dump_stack()</para></listitem>
|
||||||
|
<listitem><para>dmesg -- View the kernel syslog buffer</para></listitem>
|
||||||
|
<listitem><para>go -- Continue the system</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>When you are done using kdb you need to consider rebooting the
|
||||||
|
system or using the "go" command to resuming normal kernel
|
||||||
|
execution. If you have paused the kernel for a lengthy period of
|
||||||
|
time, applications that rely on timely networking or anything to do
|
||||||
|
with real wall clock time could be adversely affected, so you
|
||||||
|
should take this into consideration when using the kernel
|
||||||
|
debugger.</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist></para>
|
||||||
|
</sect1>
|
||||||
|
<sect1 id="quickKDBkeyboard">
|
||||||
|
<title>Quick start for kdb using a keyboard connected console</title>
|
||||||
|
<para>This is a quick example of how to use kdb with a keyboard.</para>
|
||||||
|
<para><orderedlist>
|
||||||
|
<listitem><para>Boot kernel with arguments:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><constant>kgdboc=kbd</constant></para></listitem>
|
||||||
|
</itemizedlist></para>
|
||||||
|
<para>OR</para>
|
||||||
|
<para>Configure kgdboc after the kernel booted:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><constant>echo kbd > /sys/module/kgdboc/parameters/kgdboc</constant></para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>Enter the kernel debugger manually or by waiting for an oops or fault. There are several ways you can enter the kernel debugger manually; all involve using the sysrq-g, which means you must have enabled CONFIG_MAGIC_SYSRQ=y in your kernel config.</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>When logged in as root or with a super user session you can run:</para>
|
||||||
|
<para><constant>echo g > /proc/sysrq-trigger</constant></para></listitem>
|
||||||
|
<listitem><para>Example using a laptop keyboard</para>
|
||||||
|
<para>Press and hold down: <constant>Alt</constant></para>
|
||||||
|
<para>Press and hold down: <constant>Fn</constant></para>
|
||||||
|
<para>Press and release the key with the label: <constant>SysRq</constant></para>
|
||||||
|
<para>Release: <constant>Fn</constant></para>
|
||||||
|
<para>Press and release: <constant>g</constant></para>
|
||||||
|
<para>Release: <constant>Alt</constant></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>Example using a PS/2 101-key keyboard</para>
|
||||||
|
<para>Press and hold down: <constant>Alt</constant></para>
|
||||||
|
<para>Press and release the key with the label: <constant>SysRq</constant></para>
|
||||||
|
<para>Press and release: <constant>g</constant></para>
|
||||||
|
<para>Release: <constant>Alt</constant></para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Now type in a kdb command such as "help", "dmesg", "bt" or "go" to continue kernel execution.</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist></para>
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
||||||
|
<chapter id="EnableKGDB">
|
||||||
|
<title>Using kgdb / gdb</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 kgdb I/O 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/<driver>/parameter/<option></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 to unconfigure a kgdb I/O driver.
|
||||||
|
</para>
|
||||||
|
<sect1 id="ConnectingGDB">
|
||||||
|
<title>Connecting with gdb to a serial port</title>
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>Configure kgdboc</para>
|
||||||
|
<para>Boot kernel with arguments:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><constant>kgdboc=ttyS0,115200</constant></para></listitem>
|
||||||
|
</itemizedlist></para>
|
||||||
|
<para>OR</para>
|
||||||
|
<para>Configure kgdboc after the kernel booted:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><constant>echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc</constant></para></listitem>
|
||||||
|
</itemizedlist></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Stop kernel execution (break into the debugger)</para>
|
||||||
|
<para>In order to connect to gdb via kgdboc, the kernel must
|
||||||
|
first be stopped. There are several ways to stop the kernel which
|
||||||
|
include using kgdbwait as a boot argument, via a sysrq-g, or running
|
||||||
|
the kernel until it takes an exception where it waits for the
|
||||||
|
debugger to attach.
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>When logged in as root or with a super user session you can run:</para>
|
||||||
|
<para><constant>echo g > /proc/sysrq-trigger</constant></para></listitem>
|
||||||
|
<listitem><para>Example using minicom 2.2</para>
|
||||||
|
<para>Press: <constant>Control-a</constant></para>
|
||||||
|
<para>Press: <constant>f</constant></para>
|
||||||
|
<para>Press: <constant>g</constant></para>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>When you have telneted to a terminal server that supports sending a remote break</para>
|
||||||
|
<para>Press: <constant>Control-]</constant></para>
|
||||||
|
<para>Type in:<constant>send break</constant></para>
|
||||||
|
<para>Press: <constant>Enter</constant></para>
|
||||||
|
<para>Press: <constant>g</constant></para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Connect from from gdb</para>
|
||||||
<para>
|
<para>
|
||||||
If you are using kgdboc, you need to have used kgdbwait as a boot
|
Example (using a directly connected port):
|
||||||
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>
|
</para>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
% gdb ./vmlinux
|
% gdb ./vmlinux
|
||||||
|
@ -266,7 +499,7 @@
|
||||||
(gdb) target remote /dev/ttyS0
|
(gdb) target remote /dev/ttyS0
|
||||||
</programlisting>
|
</programlisting>
|
||||||
<para>
|
<para>
|
||||||
Example (kgdb to a terminal server on tcp port 2012):
|
Example (kgdb to a terminal server on TCP port 2012):
|
||||||
</para>
|
</para>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
% gdb ./vmlinux
|
% gdb ./vmlinux
|
||||||
|
@ -283,6 +516,83 @@
|
||||||
communications. You do this prior to issuing the <constant>target
|
communications. You do this prior to issuing the <constant>target
|
||||||
remote</constant> command by typing in: <constant>set debug remote 1</constant>
|
remote</constant> command by typing in: <constant>set debug remote 1</constant>
|
||||||
</para>
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
<para>Remember if you continue in gdb, and need to "break in" again,
|
||||||
|
you need to issue an other sysrq-g. It is easy to create a simple
|
||||||
|
entry point by putting a breakpoint at <constant>sys_sync</constant>
|
||||||
|
and then you can run "sync" from a shell or script to break into the
|
||||||
|
debugger.</para>
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
||||||
|
<chapter id="switchKdbKgdb">
|
||||||
|
<title>kgdb and kdb interoperability</title>
|
||||||
|
<para>It is possible to transition between kdb and kgdb dynamically.
|
||||||
|
The debug core will remember which you used the last time and
|
||||||
|
automatically start in the same mode.</para>
|
||||||
|
<sect1>
|
||||||
|
<title>Switching between kdb and kgdb</title>
|
||||||
|
<sect2>
|
||||||
|
<title>Switching from kgdb to kdb</title>
|
||||||
|
<para>
|
||||||
|
There are two ways to switch from kgdb to kdb: you can use gdb to
|
||||||
|
issue a maintenance packet, or you can blindly type the command $3#33.
|
||||||
|
Whenever kernel debugger stops in kgdb mode it will print the
|
||||||
|
message <constant>KGDB or $3#33 for KDB</constant>. It is important
|
||||||
|
to note that you have to type the sequence correctly in one pass.
|
||||||
|
You cannot type a backspace or delete because kgdb will interpret
|
||||||
|
that as part of the debug stream.
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>Change from kgdb to kdb by blindly typing:</para>
|
||||||
|
<para><constant>$3#33</constant></para></listitem>
|
||||||
|
<listitem><para>Change from kgdb to kdb with gdb</para>
|
||||||
|
<para><constant>maintenance packet 3</constant></para>
|
||||||
|
<para>NOTE: Now you must kill gdb. Typically you press control-z and
|
||||||
|
issue the command: kill -9 %</para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
<sect2>
|
||||||
|
<title>Change from kdb to kgdb</title>
|
||||||
|
<para>There are two ways you can change from kdb to kgdb. You can
|
||||||
|
manually enter kgdb mode by issuing the kgdb command from the kdb
|
||||||
|
shell prompt, or you can connect gdb while the kdb shell prompt is
|
||||||
|
active. The kdb shell looks for the typical first commands that gdb
|
||||||
|
would issue with the gdb remote protocol and if it sees one of those
|
||||||
|
commands it automatically changes into kgdb mode.</para>
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>From kdb issue the command:</para>
|
||||||
|
<para><constant>kgdb</constant></para>
|
||||||
|
<para>Now disconnect your terminal program and connect gdb in its place</para></listitem>
|
||||||
|
<listitem><para>At the kdb prompt, disconnect the terminal program and connect gdb in its place.</para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
<sect1>
|
||||||
|
<title>Running kdb commands from gdb</title>
|
||||||
|
<para>It is possible to run a limited set of kdb commands from gdb,
|
||||||
|
using the gdb monitor command. You don't want to execute any of the
|
||||||
|
run control or breakpoint operations, because it can disrupt the
|
||||||
|
state of the kernel debugger. You should be using gdb for
|
||||||
|
breakpoints and run control operations if you have gdb connected.
|
||||||
|
The more useful commands to run are things like lsmod, dmesg, ps or
|
||||||
|
possibly some of the memory information commands. To see all the kdb
|
||||||
|
commands you can run <constant>monitor help</constant>.</para>
|
||||||
|
<para>Example:
|
||||||
|
<informalexample><programlisting>
|
||||||
|
(gdb) monitor ps
|
||||||
|
1 idle process (state I) and
|
||||||
|
27 sleeping system daemon (state M) processes suppressed,
|
||||||
|
use 'ps A' to see all.
|
||||||
|
Task Addr Pid Parent [*] cpu State Thread Command
|
||||||
|
|
||||||
|
0xc78291d0 1 0 0 0 S 0xc7829404 init
|
||||||
|
0xc7954150 942 1 0 0 S 0xc7954384 dropbear
|
||||||
|
0xc78789c0 944 1 0 0 S 0xc7878bf4 sh
|
||||||
|
(gdb)
|
||||||
|
</programlisting></informalexample>
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="KGDBTestSuite">
|
<chapter id="KGDBTestSuite">
|
||||||
<title>kgdb Test Suite</title>
|
<title>kgdb Test Suite</title>
|
||||||
|
@ -309,34 +619,36 @@
|
||||||
</para>
|
</para>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="CommonBackEndReq">
|
<chapter id="CommonBackEndReq">
|
||||||
<title>KGDB Internals</title>
|
<title>Kernel Debugger Internals</title>
|
||||||
<sect1 id="kgdbArchitecture">
|
<sect1 id="kgdbArchitecture">
|
||||||
<title>Architecture Specifics</title>
|
<title>Architecture Specifics</title>
|
||||||
<para>
|
<para>
|
||||||
Kgdb is organized into three basic components:
|
The kernel debugger is organized into a number of components:
|
||||||
<orderedlist>
|
<orderedlist>
|
||||||
<listitem><para>kgdb core</para>
|
<listitem><para>The debug core</para>
|
||||||
<para>
|
<para>
|
||||||
The kgdb core is found in kernel/kgdb.c. It contains:
|
The debug core is found in kernel/debugger/debug_core.c. It contains:
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>All the logic to implement the gdb serial protocol</para></listitem>
|
<listitem><para>A generic OS exception handler which includes
|
||||||
<listitem><para>A generic OS exception handler which includes sync'ing the processors into a stopped state on an multi cpu system.</para></listitem>
|
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 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 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>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>
|
<listitem><para>A full implementation for software breakpoints unless overridden by the arch</para></listitem>
|
||||||
|
<listitem><para>The API to invoke either the kdb or kgdb frontend to the debug core.</para></listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem><para>kgdb arch specific implementation</para>
|
<listitem><para>kgdb arch-specific implementation</para>
|
||||||
<para>
|
<para>
|
||||||
This implementation is generally found in arch/*/kernel/kgdb.c.
|
This implementation is generally found in arch/*/kernel/kgdb.c.
|
||||||
As an example, arch/x86/kernel/kgdb.c contains the specifics to
|
As an example, arch/x86/kernel/kgdb.c contains the specifics to
|
||||||
implement HW breakpoint as well as the initialization to
|
implement HW breakpoint as well as the initialization to
|
||||||
dynamically register and unregister for the trap handlers on
|
dynamically register and unregister for the trap handlers on
|
||||||
this architecture. The arch specific portion implements:
|
this architecture. The arch-specific portion implements:
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>contains an arch specific trap catcher which
|
<listitem><para>contains an arch-specific trap catcher which
|
||||||
invokes kgdb_handle_exception() to start kgdb about doing its
|
invokes kgdb_handle_exception() to start kgdb about doing its
|
||||||
work</para></listitem>
|
work</para></listitem>
|
||||||
<listitem><para>translation to and from gdb specific packet format to pt_regs</para></listitem>
|
<listitem><para>translation to and from gdb specific packet format to pt_regs</para></listitem>
|
||||||
|
@ -347,11 +659,35 @@
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem><para>gdbstub frontend (aka kgdb)</para>
|
||||||
|
<para>The gdbstub is located in kernel/debug/gdbstub.c. It contains:</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>All the logic to implement the gdb serial protocol</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
|
<listitem><para>kdb frontend</para>
|
||||||
|
<para>The kdb debugger shell is broken down into a number of
|
||||||
|
components. The kdb core is located in kernel/debug/kdb. There
|
||||||
|
are a number of helper functions in some of the other kernel
|
||||||
|
components to make it possible for kdb to examine and report
|
||||||
|
information about the kernel without taking locks that could
|
||||||
|
cause a kernel deadlock. The kdb core contains implements the following functionality.</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>A simple shell</para></listitem>
|
||||||
|
<listitem><para>The kdb core command set</para></listitem>
|
||||||
|
<listitem><para>A registration API to register additional kdb shell commands.</para>
|
||||||
|
<para>A good example of a self-contained kdb module is the "ftdump" command for dumping the ftrace buffer. See: kernel/trace/trace_kdb.c</para></listitem>
|
||||||
|
<listitem><para>The implementation for kdb_printf() which
|
||||||
|
emits messages directly to I/O drivers, bypassing the kernel
|
||||||
|
log.</para></listitem>
|
||||||
|
<listitem><para>SW / HW breakpoint management for the kdb shell</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
<listitem><para>kgdb I/O driver</para>
|
<listitem><para>kgdb I/O driver</para>
|
||||||
<para>
|
<para>
|
||||||
Each kgdb I/O driver has to provide an implemenation for the following:
|
Each kgdb I/O driver has to provide an implementation for the following:
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>configuration via builtin or module</para></listitem>
|
<listitem><para>configuration via built-in or module</para></listitem>
|
||||||
<listitem><para>dynamic configuration and kgdb hook registration calls</para></listitem>
|
<listitem><para>dynamic configuration and kgdb hook registration calls</para></listitem>
|
||||||
<listitem><para>read and write character interface</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>A cleanup handler for unconfiguring from the kgdb core</para></listitem>
|
||||||
|
@ -416,15 +752,15 @@
|
||||||
underlying low level to the hardware driver having "polling hooks"
|
underlying low level to the hardware driver having "polling hooks"
|
||||||
which the to which the tty driver is attached. In the initial
|
which the to which the tty driver is attached. In the initial
|
||||||
implementation of kgdboc it the serial_core was changed to expose a
|
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
|
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
|
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
|
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
|
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
|
certainly possible to extend kgdboc to work with non-UART based
|
||||||
consoles in the future.
|
consoles in the future.
|
||||||
</para>
|
</para>
|
||||||
<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>
|
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
|
#ifdef CONFIG_CONSOLE_POLL
|
||||||
.poll_get_char = serial8250_get_poll_char,
|
.poll_get_char = serial8250_get_poll_char,
|
||||||
.poll_put_char = serial8250_put_poll_char,
|
.poll_put_char = serial8250_put_poll_char,
|
||||||
|
@ -434,7 +770,7 @@
|
||||||
<constant>#ifdef CONFIG_CONSOLE_POLL</constant>, as shown above.
|
<constant>#ifdef CONFIG_CONSOLE_POLL</constant>, as shown above.
|
||||||
Keep in mind that polling hooks have to be implemented in such a way
|
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
|
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
|
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
|
to normal when the debugger detaches. You need to be very careful
|
||||||
with any kind of lock you consider, because failing here is most
|
with any kind of lock you consider, because failing here is most
|
||||||
going to mean pressing the reset button.
|
going to mean pressing the reset button.
|
||||||
|
@ -453,6 +789,10 @@
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>Jason Wessel<email>jason.wessel@windriver.com</email></para></listitem>
|
<listitem><para>Jason Wessel<email>jason.wessel@windriver.com</email></para></listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
In Jan 2010 this document was updated to include kdb.
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>Jason Wessel<email>jason.wessel@windriver.com</email></para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
</chapter>
|
</chapter>
|
||||||
</book>
|
</book>
|
||||||
|
|
|
@ -58,6 +58,7 @@ parameter is applicable:
|
||||||
ISAPNP ISA PnP code is enabled.
|
ISAPNP ISA PnP code is enabled.
|
||||||
ISDN Appropriate ISDN support is enabled.
|
ISDN Appropriate ISDN support is enabled.
|
||||||
JOY Appropriate joystick support is enabled.
|
JOY Appropriate joystick support is enabled.
|
||||||
|
KGDB Kernel debugger support is enabled.
|
||||||
KVM Kernel Virtual Machine support is enabled.
|
KVM Kernel Virtual Machine support is enabled.
|
||||||
LIBATA Libata driver is enabled
|
LIBATA Libata driver is enabled
|
||||||
LP Printer support is enabled.
|
LP Printer support is enabled.
|
||||||
|
@ -1120,10 +1121,15 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||||
use the HighMem zone if it exists, and the Normal
|
use the HighMem zone if it exists, and the Normal
|
||||||
zone if it does not.
|
zone if it does not.
|
||||||
|
|
||||||
kgdboc= [HW] kgdb over consoles.
|
kgdboc= [KGDB,HW] kgdb over consoles.
|
||||||
Requires a tty driver that supports console polling.
|
Requires a tty driver that supports console polling,
|
||||||
(only serial supported for now)
|
or a supported polling keyboard driver (non-usb).
|
||||||
Format: <serial_device>[,baud]
|
Serial only format: <serial_device>[,baud]
|
||||||
|
keyboard only format: kbd
|
||||||
|
keyboard and serial format: kbd,<serial_device>[,baud]
|
||||||
|
|
||||||
|
kgdbwait [KGDB] Stop kernel execution and enter the
|
||||||
|
kernel debugger at the earliest opportunity.
|
||||||
|
|
||||||
kmac= [MIPS] korina ethernet MAC address.
|
kmac= [MIPS] korina ethernet MAC address.
|
||||||
Configure the RouterBoard 532 series on-chip
|
Configure the RouterBoard 532 series on-chip
|
||||||
|
|
|
@ -3319,15 +3319,17 @@ F: include/linux/key-type.h
|
||||||
F: include/keys/
|
F: include/keys/
|
||||||
F: security/keys/
|
F: security/keys/
|
||||||
|
|
||||||
KGDB
|
KGDB / KDB /debug_core
|
||||||
M: Jason Wessel <jason.wessel@windriver.com>
|
M: Jason Wessel <jason.wessel@windriver.com>
|
||||||
|
W: http://kgdb.wiki.kernel.org/
|
||||||
L: kgdb-bugreport@lists.sourceforge.net
|
L: kgdb-bugreport@lists.sourceforge.net
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: Documentation/DocBook/kgdb.tmpl
|
F: Documentation/DocBook/kgdb.tmpl
|
||||||
F: drivers/misc/kgdbts.c
|
F: drivers/misc/kgdbts.c
|
||||||
F: drivers/serial/kgdboc.c
|
F: drivers/serial/kgdboc.c
|
||||||
|
F: include/linux/kdb.h
|
||||||
F: include/linux/kgdb.h
|
F: include/linux/kgdb.h
|
||||||
F: kernel/kgdb.c
|
F: kernel/debug/
|
||||||
|
|
||||||
KMEMCHECK
|
KMEMCHECK
|
||||||
M: Vegard Nossum <vegardno@ifi.uio.no>
|
M: Vegard Nossum <vegardno@ifi.uio.no>
|
||||||
|
|
|
@ -20,6 +20,7 @@ enum km_type {
|
||||||
KM_SOFTIRQ1,
|
KM_SOFTIRQ1,
|
||||||
KM_L1_CACHE,
|
KM_L1_CACHE,
|
||||||
KM_L2_CACHE,
|
KM_L2_CACHE,
|
||||||
|
KM_KDB,
|
||||||
KM_TYPE_NR
|
KM_TYPE_NR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,11 @@ sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
|
||||||
gdb_regs[_CPSR] = thread_regs->ARM_cpsr;
|
gdb_regs[_CPSR] = thread_regs->ARM_cpsr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
|
||||||
|
{
|
||||||
|
regs->ARM_pc = pc;
|
||||||
|
}
|
||||||
|
|
||||||
static int compiled_break;
|
static int compiled_break;
|
||||||
|
|
||||||
int kgdb_arch_handle_exception(int exception_vector, int signo,
|
int kgdb_arch_handle_exception(int exception_vector, int signo,
|
||||||
|
|
|
@ -439,6 +439,11 @@ int kgdb_validate_break_address(unsigned long addr)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
|
||||||
|
{
|
||||||
|
regs->retx = ip;
|
||||||
|
}
|
||||||
|
|
||||||
int kgdb_arch_init(void)
|
int kgdb_arch_init(void)
|
||||||
{
|
{
|
||||||
kgdb_single_step = 0;
|
kgdb_single_step = 0;
|
||||||
|
|
|
@ -38,6 +38,8 @@ extern int kgdb_early_setup;
|
||||||
extern void *saved_vectors[32];
|
extern void *saved_vectors[32];
|
||||||
extern void handle_exception(struct pt_regs *regs);
|
extern void handle_exception(struct pt_regs *regs);
|
||||||
extern void breakinst(void);
|
extern void breakinst(void);
|
||||||
|
extern int kgdb_ll_trap(int cmd, const char *str,
|
||||||
|
struct pt_regs *regs, long err, int trap, int sig);
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,11 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
|
||||||
*(ptr++) = regs->cp0_epc;
|
*(ptr++) = regs->cp0_epc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
|
||||||
|
{
|
||||||
|
regs->cp0_epc = pc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
|
* Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
|
||||||
* then try to fall into the debugger
|
* then try to fall into the debugger
|
||||||
|
@ -198,7 +203,7 @@ static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd,
|
||||||
if (atomic_read(&kgdb_active) != -1)
|
if (atomic_read(&kgdb_active) != -1)
|
||||||
kgdb_nmicallback(smp_processor_id(), regs);
|
kgdb_nmicallback(smp_processor_id(), regs);
|
||||||
|
|
||||||
if (kgdb_handle_exception(trap, compute_signal(trap), 0, regs))
|
if (kgdb_handle_exception(trap, compute_signal(trap), cmd, regs))
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
if (atomic_read(&kgdb_setting_breakpoint))
|
if (atomic_read(&kgdb_setting_breakpoint))
|
||||||
|
@ -212,6 +217,26 @@ static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd,
|
||||||
return NOTIFY_STOP;
|
return NOTIFY_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
|
||||||
|
int kgdb_ll_trap(int cmd, const char *str,
|
||||||
|
struct pt_regs *regs, long err, int trap, int sig)
|
||||||
|
{
|
||||||
|
struct die_args args = {
|
||||||
|
.regs = regs,
|
||||||
|
.str = str,
|
||||||
|
.err = err,
|
||||||
|
.trapnr = trap,
|
||||||
|
.signr = sig,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!kgdb_io_module_registered)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
return kgdb_mips_notify(NULL, cmd, &args);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
|
||||||
|
|
||||||
static struct notifier_block kgdb_notifier = {
|
static struct notifier_block kgdb_notifier = {
|
||||||
.notifier_call = kgdb_mips_notify,
|
.notifier_call = kgdb_mips_notify,
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/kgdb.h>
|
#include <linux/kgdb.h>
|
||||||
#include <linux/kdebug.h>
|
#include <linux/kdebug.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
|
||||||
#include <asm/bootinfo.h>
|
#include <asm/bootinfo.h>
|
||||||
#include <asm/branch.h>
|
#include <asm/branch.h>
|
||||||
|
@ -185,6 +186,11 @@ void show_stack(struct task_struct *task, unsigned long *sp)
|
||||||
regs.regs[29] = task->thread.reg29;
|
regs.regs[29] = task->thread.reg29;
|
||||||
regs.regs[31] = 0;
|
regs.regs[31] = 0;
|
||||||
regs.cp0_epc = task->thread.reg31;
|
regs.cp0_epc = task->thread.reg31;
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
} else if (atomic_read(&kgdb_active) != -1 &&
|
||||||
|
kdb_current_regs) {
|
||||||
|
memcpy(®s, kdb_current_regs, sizeof(regs));
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
} else {
|
} else {
|
||||||
prepare_frametrace(®s);
|
prepare_frametrace(®s);
|
||||||
}
|
}
|
||||||
|
@ -360,6 +366,8 @@ void __noreturn die(const char * str, struct pt_regs * regs)
|
||||||
unsigned long dvpret = dvpe();
|
unsigned long dvpret = dvpe();
|
||||||
#endif /* CONFIG_MIPS_MT_SMTC */
|
#endif /* CONFIG_MIPS_MT_SMTC */
|
||||||
|
|
||||||
|
notify_die(DIE_OOPS, str, (struct pt_regs *)regs, SIGSEGV, 0, 0);
|
||||||
|
|
||||||
console_verbose();
|
console_verbose();
|
||||||
spin_lock_irq(&die_lock);
|
spin_lock_irq(&die_lock);
|
||||||
bust_spinlocks(1);
|
bust_spinlocks(1);
|
||||||
|
@ -704,6 +712,11 @@ static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
char b[40];
|
char b[40];
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
|
||||||
|
if (kgdb_ll_trap(DIE_TRAP, str, regs, code, 0, 0) == NOTIFY_STOP)
|
||||||
|
return;
|
||||||
|
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
|
||||||
|
|
||||||
if (notify_die(DIE_TRAP, str, regs, code, 0, 0) == NOTIFY_STOP)
|
if (notify_die(DIE_TRAP, str, regs, code, 0, 0) == NOTIFY_STOP)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ enum km_type {
|
||||||
KM_SOFTIRQ1,
|
KM_SOFTIRQ1,
|
||||||
KM_PPC_SYNC_PAGE,
|
KM_PPC_SYNC_PAGE,
|
||||||
KM_PPC_SYNC_ICACHE,
|
KM_PPC_SYNC_ICACHE,
|
||||||
|
KM_KDB,
|
||||||
KM_TYPE_NR
|
KM_TYPE_NR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/kdebug.h>
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <asm/machdep.h>
|
#include <asm/machdep.h>
|
||||||
|
@ -115,7 +116,8 @@ void kgdb_roundup_cpus(unsigned long flags)
|
||||||
/* KGDB functions to use existing PowerPC64 hooks. */
|
/* KGDB functions to use existing PowerPC64 hooks. */
|
||||||
static int kgdb_debugger(struct pt_regs *regs)
|
static int kgdb_debugger(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
return kgdb_handle_exception(0, computeSignal(TRAP(regs)), 0, regs);
|
return !kgdb_handle_exception(1, computeSignal(TRAP(regs)),
|
||||||
|
DIE_OOPS, regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kgdb_handle_breakpoint(struct pt_regs *regs)
|
static int kgdb_handle_breakpoint(struct pt_regs *regs)
|
||||||
|
@ -123,7 +125,7 @@ static int kgdb_handle_breakpoint(struct pt_regs *regs)
|
||||||
if (user_mode(regs))
|
if (user_mode(regs))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (kgdb_handle_exception(0, SIGTRAP, 0, regs) != 0)
|
if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
|
if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
|
||||||
|
@ -309,6 +311,11 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||||
(unsigned long)(((void *)gdb_regs) + NUMREGBYTES));
|
(unsigned long)(((void *)gdb_regs) + NUMREGBYTES));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
|
||||||
|
{
|
||||||
|
regs->nip = pc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function does PowerPC specific procesing for interfacing to gdb.
|
* This function does PowerPC specific procesing for interfacing to gdb.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -815,12 +815,15 @@ void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (reason & REASON_TRAP) {
|
if (reason & REASON_TRAP) {
|
||||||
|
/* Debugger is first in line to stop recursive faults in
|
||||||
|
* rcu_lock, notify_die, or atomic_notifier_call_chain */
|
||||||
|
if (debugger_bpt(regs))
|
||||||
|
return;
|
||||||
|
|
||||||
/* trap exception */
|
/* trap exception */
|
||||||
if (notify_die(DIE_BPT, "breakpoint", regs, 5, 5, SIGTRAP)
|
if (notify_die(DIE_BPT, "breakpoint", regs, 5, 5, SIGTRAP)
|
||||||
== NOTIFY_STOP)
|
== NOTIFY_STOP)
|
||||||
return;
|
return;
|
||||||
if (debugger_bpt(regs))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!(regs->msr & MSR_PR) && /* not user-mode */
|
if (!(regs->msr & MSR_PR) && /* not user-mode */
|
||||||
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
|
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
|
||||||
|
|
|
@ -237,6 +237,18 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
if (exception == 60)
|
||||||
|
return instruction_pointer(regs) - 2;
|
||||||
|
return instruction_pointer(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
|
||||||
|
{
|
||||||
|
regs->pc = ip;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The primary entry points for the kgdb debug trap table entries.
|
* The primary entry points for the kgdb debug trap table entries.
|
||||||
*/
|
*/
|
||||||
|
@ -247,7 +259,7 @@ BUILD_TRAP_HANDLER(singlestep)
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
regs->pc -= instruction_size(__raw_readw(regs->pc - 4));
|
regs->pc -= instruction_size(__raw_readw(regs->pc - 4));
|
||||||
kgdb_handle_exception(vec >> 2, SIGTRAP, 0, regs);
|
kgdb_handle_exception(0, SIGTRAP, 0, regs);
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,12 @@ void kgdb_arch_exit(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
|
||||||
|
{
|
||||||
|
regs->pc = ip;
|
||||||
|
regs->npc = regs->pc + 4;
|
||||||
|
}
|
||||||
|
|
||||||
struct kgdb_arch arch_kgdb_ops = {
|
struct kgdb_arch arch_kgdb_ops = {
|
||||||
/* Breakpoint instruction: ta 0x7d */
|
/* Breakpoint instruction: ta 0x7d */
|
||||||
.gdb_bpt_instr = { 0x91, 0xd0, 0x20, 0x7d },
|
.gdb_bpt_instr = { 0x91, 0xd0, 0x20, 0x7d },
|
||||||
|
|
|
@ -181,6 +181,12 @@ void kgdb_arch_exit(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
|
||||||
|
{
|
||||||
|
regs->tpc = ip;
|
||||||
|
regs->tnpc = regs->tpc + 4;
|
||||||
|
}
|
||||||
|
|
||||||
struct kgdb_arch arch_kgdb_ops = {
|
struct kgdb_arch arch_kgdb_ops = {
|
||||||
/* Breakpoint instruction: ta 0x72 */
|
/* Breakpoint instruction: ta 0x72 */
|
||||||
.gdb_bpt_instr = { 0x91, 0xd0, 0x20, 0x72 },
|
.gdb_bpt_instr = { 0x91, 0xd0, 0x20, 0x72 },
|
||||||
|
|
|
@ -76,4 +76,7 @@ static inline void arch_kgdb_breakpoint(void)
|
||||||
#define BREAK_INSTR_SIZE 1
|
#define BREAK_INSTR_SIZE 1
|
||||||
#define CACHE_FLUSH_IS_SAFE 1
|
#define CACHE_FLUSH_IS_SAFE 1
|
||||||
|
|
||||||
|
extern int kgdb_ll_trap(int cmd, const char *str,
|
||||||
|
struct pt_regs *regs, long err, int trap, int sig);
|
||||||
|
|
||||||
#endif /* _ASM_X86_KGDB_H */
|
#endif /* _ASM_X86_KGDB_H */
|
||||||
|
|
|
@ -47,20 +47,8 @@
|
||||||
#include <asm/debugreg.h>
|
#include <asm/debugreg.h>
|
||||||
#include <asm/apicdef.h>
|
#include <asm/apicdef.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
|
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
* pt_regs_to_gdb_regs - Convert ptrace regs to GDB regs
|
||||||
* @gdb_regs: A pointer to hold the registers in the order GDB wants.
|
* @gdb_regs: A pointer to hold the registers in the order GDB wants.
|
||||||
|
@ -399,23 +387,6 @@ void kgdb_disable_hw_debug(struct pt_regs *regs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
#ifdef CONFIG_SMP
|
||||||
/**
|
/**
|
||||||
* kgdb_roundup_cpus - Get other CPUs into a holding pattern
|
* kgdb_roundup_cpus - Get other CPUs into a holding pattern
|
||||||
|
@ -567,7 +538,7 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd)
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kgdb_handle_exception(args->trapnr, args->signr, args->err, regs))
|
if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs))
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
/* Must touch watchdog before return to normal operation */
|
/* Must touch watchdog before return to normal operation */
|
||||||
|
@ -575,6 +546,26 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd)
|
||||||
return NOTIFY_STOP;
|
return NOTIFY_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
|
||||||
|
int kgdb_ll_trap(int cmd, const char *str,
|
||||||
|
struct pt_regs *regs, long err, int trap, int sig)
|
||||||
|
{
|
||||||
|
struct die_args args = {
|
||||||
|
.regs = regs,
|
||||||
|
.str = str,
|
||||||
|
.err = err,
|
||||||
|
.trapnr = trap,
|
||||||
|
.signr = sig,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!kgdb_io_module_registered)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
return __kgdb_notify(&args, cmd);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
|
kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
|
||||||
{
|
{
|
||||||
|
@ -690,6 +681,11 @@ unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
|
||||||
return instruction_pointer(regs);
|
return instruction_pointer(regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
|
||||||
|
{
|
||||||
|
regs->ip = ip;
|
||||||
|
}
|
||||||
|
|
||||||
struct kgdb_arch arch_kgdb_ops = {
|
struct kgdb_arch arch_kgdb_ops = {
|
||||||
/* Breakpoint instruction: */
|
/* Breakpoint instruction: */
|
||||||
.gdb_bpt_instr = { 0xcc },
|
.gdb_bpt_instr = { 0xcc },
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/kprobes.h>
|
#include <linux/kprobes.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/kdebug.h>
|
#include <linux/kdebug.h>
|
||||||
|
#include <linux/kgdb.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
||||||
|
@ -451,6 +452,11 @@ void restart_nmi(void)
|
||||||
/* May run on IST stack. */
|
/* May run on IST stack. */
|
||||||
dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
|
dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
|
||||||
|
if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
|
||||||
|
== NOTIFY_STOP)
|
||||||
|
return;
|
||||||
|
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
|
if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
|
||||||
== NOTIFY_STOP)
|
== NOTIFY_STOP)
|
||||||
|
|
|
@ -1891,8 +1891,8 @@ static int serial8250_get_poll_char(struct uart_port *port)
|
||||||
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
||||||
unsigned char lsr = serial_inp(up, UART_LSR);
|
unsigned char lsr = serial_inp(up, UART_LSR);
|
||||||
|
|
||||||
while (!(lsr & UART_LSR_DR))
|
if (!(lsr & UART_LSR_DR))
|
||||||
lsr = serial_inp(up, UART_LSR);
|
return NO_POLL_CHAR;
|
||||||
|
|
||||||
return serial_inp(up, UART_RX);
|
return serial_inp(up, UART_RX);
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,9 +342,9 @@ static int pl010_get_poll_char(struct uart_port *port)
|
||||||
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
struct uart_amba_port *uap = (struct uart_amba_port *)port;
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
|
|
||||||
do {
|
status = readw(uap->port.membase + UART01x_FR);
|
||||||
status = readw(uap->port.membase + UART01x_FR);
|
if (status & UART01x_FR_RXFE)
|
||||||
} while (status & UART01x_FR_RXFE);
|
return NO_POLL_CHAR;
|
||||||
|
|
||||||
return readw(uap->port.membase + UART01x_DR);
|
return readw(uap->port.membase + UART01x_DR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/kgdb.h>
|
#include <linux/kgdb.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
#include <linux/tty.h>
|
#include <linux/tty.h>
|
||||||
|
#include <linux/console.h>
|
||||||
|
|
||||||
#define MAX_CONFIG_LEN 40
|
#define MAX_CONFIG_LEN 40
|
||||||
|
|
||||||
|
@ -32,6 +34,40 @@ static struct kparam_string kps = {
|
||||||
static struct tty_driver *kgdb_tty_driver;
|
static struct tty_driver *kgdb_tty_driver;
|
||||||
static int kgdb_tty_line;
|
static int kgdb_tty_line;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KDB_KEYBOARD
|
||||||
|
static int kgdboc_register_kbd(char **cptr)
|
||||||
|
{
|
||||||
|
if (strncmp(*cptr, "kbd", 3) == 0) {
|
||||||
|
if (kdb_poll_idx < KDB_POLL_FUNC_MAX) {
|
||||||
|
kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char;
|
||||||
|
kdb_poll_idx++;
|
||||||
|
if (cptr[0][3] == ',')
|
||||||
|
*cptr += 4;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kgdboc_unregister_kbd(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < kdb_poll_idx; i++) {
|
||||||
|
if (kdb_poll_funcs[i] == kdb_get_kbd_char) {
|
||||||
|
kdb_poll_idx--;
|
||||||
|
kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx];
|
||||||
|
kdb_poll_funcs[kdb_poll_idx] = NULL;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else /* ! CONFIG_KDB_KEYBOARD */
|
||||||
|
#define kgdboc_register_kbd(x) 0
|
||||||
|
#define kgdboc_unregister_kbd()
|
||||||
|
#endif /* ! CONFIG_KDB_KEYBOARD */
|
||||||
|
|
||||||
static int kgdboc_option_setup(char *opt)
|
static int kgdboc_option_setup(char *opt)
|
||||||
{
|
{
|
||||||
if (strlen(opt) > MAX_CONFIG_LEN) {
|
if (strlen(opt) > MAX_CONFIG_LEN) {
|
||||||
|
@ -45,25 +81,51 @@ static int kgdboc_option_setup(char *opt)
|
||||||
|
|
||||||
__setup("kgdboc=", kgdboc_option_setup);
|
__setup("kgdboc=", kgdboc_option_setup);
|
||||||
|
|
||||||
|
static void cleanup_kgdboc(void)
|
||||||
|
{
|
||||||
|
kgdboc_unregister_kbd();
|
||||||
|
if (configured == 1)
|
||||||
|
kgdb_unregister_io_module(&kgdboc_io_ops);
|
||||||
|
}
|
||||||
|
|
||||||
static int configure_kgdboc(void)
|
static int configure_kgdboc(void)
|
||||||
{
|
{
|
||||||
struct tty_driver *p;
|
struct tty_driver *p;
|
||||||
int tty_line = 0;
|
int tty_line = 0;
|
||||||
int err;
|
int err;
|
||||||
|
char *cptr = config;
|
||||||
|
struct console *cons;
|
||||||
|
|
||||||
err = kgdboc_option_setup(config);
|
err = kgdboc_option_setup(config);
|
||||||
if (err || !strlen(config) || isspace(config[0]))
|
if (err || !strlen(config) || isspace(config[0]))
|
||||||
goto noconfig;
|
goto noconfig;
|
||||||
|
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
|
kgdboc_io_ops.is_console = 0;
|
||||||
|
kgdb_tty_driver = NULL;
|
||||||
|
|
||||||
p = tty_find_polling_driver(config, &tty_line);
|
if (kgdboc_register_kbd(&cptr))
|
||||||
|
goto do_register;
|
||||||
|
|
||||||
|
p = tty_find_polling_driver(cptr, &tty_line);
|
||||||
if (!p)
|
if (!p)
|
||||||
goto noconfig;
|
goto noconfig;
|
||||||
|
|
||||||
|
cons = console_drivers;
|
||||||
|
while (cons) {
|
||||||
|
int idx;
|
||||||
|
if (cons->device && cons->device(cons, &idx) == p &&
|
||||||
|
idx == tty_line) {
|
||||||
|
kgdboc_io_ops.is_console = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cons = cons->next;
|
||||||
|
}
|
||||||
|
|
||||||
kgdb_tty_driver = p;
|
kgdb_tty_driver = p;
|
||||||
kgdb_tty_line = tty_line;
|
kgdb_tty_line = tty_line;
|
||||||
|
|
||||||
|
do_register:
|
||||||
err = kgdb_register_io_module(&kgdboc_io_ops);
|
err = kgdb_register_io_module(&kgdboc_io_ops);
|
||||||
if (err)
|
if (err)
|
||||||
goto noconfig;
|
goto noconfig;
|
||||||
|
@ -75,6 +137,7 @@ static int configure_kgdboc(void)
|
||||||
noconfig:
|
noconfig:
|
||||||
config[0] = 0;
|
config[0] = 0;
|
||||||
configured = 0;
|
configured = 0;
|
||||||
|
cleanup_kgdboc();
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -88,20 +151,18 @@ static int __init init_kgdboc(void)
|
||||||
return configure_kgdboc();
|
return configure_kgdboc();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_kgdboc(void)
|
|
||||||
{
|
|
||||||
if (configured == 1)
|
|
||||||
kgdb_unregister_io_module(&kgdboc_io_ops);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int kgdboc_get_char(void)
|
static int kgdboc_get_char(void)
|
||||||
{
|
{
|
||||||
|
if (!kgdb_tty_driver)
|
||||||
|
return -1;
|
||||||
return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver,
|
return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver,
|
||||||
kgdb_tty_line);
|
kgdb_tty_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kgdboc_put_char(u8 chr)
|
static void kgdboc_put_char(u8 chr)
|
||||||
{
|
{
|
||||||
|
if (!kgdb_tty_driver)
|
||||||
|
return;
|
||||||
kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver,
|
kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver,
|
||||||
kgdb_tty_line, chr);
|
kgdb_tty_line, chr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,11 @@ static int sci_poll_get_char(struct uart_port *port)
|
||||||
handle_error(port);
|
handle_error(port);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} while (!(status & SCxSR_RDxF(port)));
|
break;
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
if (!(status & SCxSR_RDxF(port)))
|
||||||
|
return NO_POLL_CHAR;
|
||||||
|
|
||||||
c = sci_in(port, SCxRDR);
|
c = sci_in(port, SCxRDR);
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,8 @@ struct uart_sunzilog_port {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void sunzilog_putchar(struct uart_port *port, int ch);
|
||||||
|
|
||||||
#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase))
|
#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase))
|
||||||
#define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT))
|
#define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT))
|
||||||
|
|
||||||
|
@ -996,6 +998,50 @@ static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *se
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_CONSOLE_POLL
|
||||||
|
static int sunzilog_get_poll_char(struct uart_port *port)
|
||||||
|
{
|
||||||
|
unsigned char ch, r1;
|
||||||
|
struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
|
||||||
|
struct zilog_channel __iomem *channel
|
||||||
|
= ZILOG_CHANNEL_FROM_PORT(&up->port);
|
||||||
|
|
||||||
|
|
||||||
|
r1 = read_zsreg(channel, R1);
|
||||||
|
if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
|
||||||
|
writeb(ERR_RES, &channel->control);
|
||||||
|
ZSDELAY();
|
||||||
|
ZS_WSYNC(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
ch = readb(&channel->control);
|
||||||
|
ZSDELAY();
|
||||||
|
|
||||||
|
/* This funny hack depends upon BRK_ABRT not interfering
|
||||||
|
* with the other bits we care about in R1.
|
||||||
|
*/
|
||||||
|
if (ch & BRK_ABRT)
|
||||||
|
r1 |= BRK_ABRT;
|
||||||
|
|
||||||
|
if (!(ch & Rx_CH_AV))
|
||||||
|
return NO_POLL_CHAR;
|
||||||
|
|
||||||
|
ch = readb(&channel->data);
|
||||||
|
ZSDELAY();
|
||||||
|
|
||||||
|
ch &= up->parity_mask;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunzilog_put_poll_char(struct uart_port *port,
|
||||||
|
unsigned char ch)
|
||||||
|
{
|
||||||
|
struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port;
|
||||||
|
|
||||||
|
sunzilog_putchar(&up->port, ch);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_CONSOLE_POLL */
|
||||||
|
|
||||||
static struct uart_ops sunzilog_pops = {
|
static struct uart_ops sunzilog_pops = {
|
||||||
.tx_empty = sunzilog_tx_empty,
|
.tx_empty = sunzilog_tx_empty,
|
||||||
.set_mctrl = sunzilog_set_mctrl,
|
.set_mctrl = sunzilog_set_mctrl,
|
||||||
|
@ -1013,6 +1059,10 @@ static struct uart_ops sunzilog_pops = {
|
||||||
.request_port = sunzilog_request_port,
|
.request_port = sunzilog_request_port,
|
||||||
.config_port = sunzilog_config_port,
|
.config_port = sunzilog_config_port,
|
||||||
.verify_port = sunzilog_verify_port,
|
.verify_port = sunzilog_verify_port,
|
||||||
|
#ifdef CONFIG_CONSOLE_POLL
|
||||||
|
.poll_get_char = sunzilog_get_poll_char,
|
||||||
|
.poll_put_char = sunzilog_put_poll_char,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static int uart_chip_count;
|
static int uart_chip_count;
|
||||||
|
|
|
@ -28,7 +28,8 @@ KMAP_D(15) KM_UML_USERCOPY,
|
||||||
KMAP_D(16) KM_IRQ_PTE,
|
KMAP_D(16) KM_IRQ_PTE,
|
||||||
KMAP_D(17) KM_NMI,
|
KMAP_D(17) KM_NMI,
|
||||||
KMAP_D(18) KM_NMI_PTE,
|
KMAP_D(18) KM_NMI_PTE,
|
||||||
KMAP_D(19) KM_TYPE_NR
|
KMAP_D(19) KM_KDB,
|
||||||
|
KMAP_D(20) KM_TYPE_NR
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef KMAP_D
|
#undef KMAP_D
|
||||||
|
|
117
include/linux/kdb.h
Normal file
117
include/linux/kdb.h
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
#ifndef _KDB_H
|
||||||
|
#define _KDB_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kernel Debugger Architecture Independent Global Headers
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2007 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
* Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
|
||||||
|
* Copyright (C) 2009 Jason Wessel <jason.wessel@windriver.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
|
#define KDB_POLL_FUNC_MAX 5
|
||||||
|
extern int kdb_poll_idx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_initial_cpu is initialized to -1, and is set to the cpu
|
||||||
|
* number whenever the kernel debugger is entered.
|
||||||
|
*/
|
||||||
|
extern int kdb_initial_cpu;
|
||||||
|
extern atomic_t kdb_event;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_diemsg
|
||||||
|
*
|
||||||
|
* Contains a pointer to the last string supplied to the
|
||||||
|
* kernel 'die' panic function.
|
||||||
|
*/
|
||||||
|
extern const char *kdb_diemsg;
|
||||||
|
|
||||||
|
#define KDB_FLAG_EARLYKDB (1 << 0) /* set from boot parameter kdb=early */
|
||||||
|
#define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */
|
||||||
|
#define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */
|
||||||
|
#define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */
|
||||||
|
#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when
|
||||||
|
* kdb is off */
|
||||||
|
#define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available,
|
||||||
|
* kdb is disabled */
|
||||||
|
#define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do
|
||||||
|
* not use keyboard */
|
||||||
|
#define KDB_FLAG_NO_I8042 (1 << 7) /* No i8042 chip is available, do
|
||||||
|
* not use keyboard */
|
||||||
|
|
||||||
|
extern int kdb_flags; /* Global flags, see kdb_state for per cpu state */
|
||||||
|
|
||||||
|
extern void kdb_save_flags(void);
|
||||||
|
extern void kdb_restore_flags(void);
|
||||||
|
|
||||||
|
#define KDB_FLAG(flag) (kdb_flags & KDB_FLAG_##flag)
|
||||||
|
#define KDB_FLAG_SET(flag) ((void)(kdb_flags |= KDB_FLAG_##flag))
|
||||||
|
#define KDB_FLAG_CLEAR(flag) ((void)(kdb_flags &= ~KDB_FLAG_##flag))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* External entry point for the kernel debugger. The pt_regs
|
||||||
|
* at the time of entry are supplied along with the reason for
|
||||||
|
* entry to the kernel debugger.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KDB_REASON_ENTER = 1, /* KDB_ENTER() trap/fault - regs valid */
|
||||||
|
KDB_REASON_ENTER_SLAVE, /* KDB_ENTER_SLAVE() trap/fault - regs valid */
|
||||||
|
KDB_REASON_BREAK, /* Breakpoint inst. - regs valid */
|
||||||
|
KDB_REASON_DEBUG, /* Debug Fault - regs valid */
|
||||||
|
KDB_REASON_OOPS, /* Kernel Oops - regs valid */
|
||||||
|
KDB_REASON_SWITCH, /* CPU switch - regs valid*/
|
||||||
|
KDB_REASON_KEYBOARD, /* Keyboard entry - regs valid */
|
||||||
|
KDB_REASON_NMI, /* Non-maskable interrupt; regs valid */
|
||||||
|
KDB_REASON_RECURSE, /* Recursive entry to kdb;
|
||||||
|
* regs probably valid */
|
||||||
|
KDB_REASON_SSTEP, /* Single Step trap. - regs valid */
|
||||||
|
} kdb_reason_t;
|
||||||
|
|
||||||
|
extern int kdb_trap_printk;
|
||||||
|
extern int vkdb_printf(const char *fmt, va_list args)
|
||||||
|
__attribute__ ((format (printf, 1, 0)));
|
||||||
|
extern int kdb_printf(const char *, ...)
|
||||||
|
__attribute__ ((format (printf, 1, 2)));
|
||||||
|
typedef int (*kdb_printf_t)(const char *, ...)
|
||||||
|
__attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
|
extern void kdb_init(int level);
|
||||||
|
|
||||||
|
/* Access to kdb specific polling devices */
|
||||||
|
typedef int (*get_char_func)(void);
|
||||||
|
extern get_char_func kdb_poll_funcs[];
|
||||||
|
extern int kdb_get_kbd_char(void);
|
||||||
|
|
||||||
|
static inline
|
||||||
|
int kdb_process_cpu(const struct task_struct *p)
|
||||||
|
{
|
||||||
|
unsigned int cpu = task_thread_info(p)->cpu;
|
||||||
|
if (cpu > num_possible_cpus())
|
||||||
|
cpu = 0;
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* kdb access to register set for stack dumping */
|
||||||
|
extern struct pt_regs *kdb_current_regs;
|
||||||
|
|
||||||
|
#else /* ! CONFIG_KGDB_KDB */
|
||||||
|
#define kdb_printf(...)
|
||||||
|
#define kdb_init(x)
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
|
enum {
|
||||||
|
KDB_NOT_INITIALIZED,
|
||||||
|
KDB_INIT_EARLY,
|
||||||
|
KDB_INIT_FULL,
|
||||||
|
};
|
||||||
|
#endif /* !_KDB_H */
|
|
@ -16,10 +16,12 @@
|
||||||
#include <linux/serial_8250.h>
|
#include <linux/serial_8250.h>
|
||||||
#include <linux/linkage.h>
|
#include <linux/linkage.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
|
||||||
#include <asm/atomic.h>
|
#include <asm/atomic.h>
|
||||||
|
#ifdef CONFIG_HAVE_ARCH_KGDB
|
||||||
#include <asm/kgdb.h>
|
#include <asm/kgdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB
|
||||||
struct pt_regs;
|
struct pt_regs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,20 +35,6 @@ struct pt_regs;
|
||||||
*/
|
*/
|
||||||
extern int kgdb_skipexception(int exception, struct pt_regs *regs);
|
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
|
* kgdb_disable_hw_debug - (optional) Disable hardware debugging hook
|
||||||
* @regs: Current &struct pt_regs.
|
* @regs: Current &struct pt_regs.
|
||||||
|
@ -72,6 +60,7 @@ struct uart_port;
|
||||||
void kgdb_breakpoint(void);
|
void kgdb_breakpoint(void);
|
||||||
|
|
||||||
extern int kgdb_connected;
|
extern int kgdb_connected;
|
||||||
|
extern int kgdb_io_module_registered;
|
||||||
|
|
||||||
extern atomic_t kgdb_setting_breakpoint;
|
extern atomic_t kgdb_setting_breakpoint;
|
||||||
extern atomic_t kgdb_cpu_doing_single_step;
|
extern atomic_t kgdb_cpu_doing_single_step;
|
||||||
|
@ -202,6 +191,17 @@ kgdb_arch_handle_exception(int vector, int signo, int err_code,
|
||||||
*/
|
*/
|
||||||
extern void kgdb_roundup_cpus(unsigned long flags);
|
extern void kgdb_roundup_cpus(unsigned long flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kgdb_arch_set_pc - Generic call back to the program counter
|
||||||
|
* @regs: Current &struct pt_regs.
|
||||||
|
* @pc: The new value for the program counter
|
||||||
|
*
|
||||||
|
* This function handles updating the program counter and requires an
|
||||||
|
* architecture specific implementation.
|
||||||
|
*/
|
||||||
|
extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc);
|
||||||
|
|
||||||
|
|
||||||
/* Optional functions. */
|
/* Optional functions. */
|
||||||
extern int kgdb_validate_break_address(unsigned long addr);
|
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_set_breakpoint(unsigned long addr, char *saved_instr);
|
||||||
|
@ -247,6 +247,8 @@ struct kgdb_arch {
|
||||||
* the I/O driver.
|
* the I/O driver.
|
||||||
* @post_exception: Pointer to a function that will do any cleanup work
|
* @post_exception: Pointer to a function that will do any cleanup work
|
||||||
* for the I/O driver.
|
* for the I/O driver.
|
||||||
|
* @is_console: 1 if the end device is a console 0 if the I/O device is
|
||||||
|
* not a console
|
||||||
*/
|
*/
|
||||||
struct kgdb_io {
|
struct kgdb_io {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -256,6 +258,7 @@ struct kgdb_io {
|
||||||
int (*init) (void);
|
int (*init) (void);
|
||||||
void (*pre_exception) (void);
|
void (*pre_exception) (void);
|
||||||
void (*post_exception) (void);
|
void (*post_exception) (void);
|
||||||
|
int is_console;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct kgdb_arch arch_kgdb_ops;
|
extern struct kgdb_arch arch_kgdb_ops;
|
||||||
|
@ -264,12 +267,14 @@ extern unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs);
|
||||||
|
|
||||||
extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_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 void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
|
||||||
|
extern struct kgdb_io *dbg_io_ops;
|
||||||
|
|
||||||
extern int kgdb_hex2long(char **ptr, unsigned long *long_val);
|
extern int kgdb_hex2long(char **ptr, unsigned long *long_val);
|
||||||
extern int kgdb_mem2hex(char *mem, char *buf, int count);
|
extern int kgdb_mem2hex(char *mem, char *buf, int count);
|
||||||
extern int kgdb_hex2mem(char *buf, char *mem, int count);
|
extern int kgdb_hex2mem(char *buf, char *mem, int count);
|
||||||
|
|
||||||
extern int kgdb_isremovedbreak(unsigned long addr);
|
extern int kgdb_isremovedbreak(unsigned long addr);
|
||||||
|
extern void kgdb_schedule_breakpoint(void);
|
||||||
|
|
||||||
extern int
|
extern int
|
||||||
kgdb_handle_exception(int ex_vector, int signo, int err_code,
|
kgdb_handle_exception(int ex_vector, int signo, int err_code,
|
||||||
|
@ -278,5 +283,9 @@ extern int kgdb_nmicallback(int cpu, void *regs);
|
||||||
|
|
||||||
extern int kgdb_single_step;
|
extern int kgdb_single_step;
|
||||||
extern atomic_t kgdb_active;
|
extern atomic_t kgdb_active;
|
||||||
|
#define in_dbg_master() \
|
||||||
|
(raw_smp_processor_id() == atomic_read(&kgdb_active))
|
||||||
|
#else /* ! CONFIG_KGDB */
|
||||||
|
#define in_dbg_master() (0)
|
||||||
|
#endif /* ! CONFIG_KGDB */
|
||||||
#endif /* _KGDB_H_ */
|
#endif /* _KGDB_H_ */
|
||||||
|
|
|
@ -250,6 +250,7 @@ struct uart_ops {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define NO_POLL_CHAR 0x00ff0000
|
||||||
#define UART_CONFIG_TYPE (1 << 0)
|
#define UART_CONFIG_TYPE (1 << 0)
|
||||||
#define UART_CONFIG_IRQ (1 << 1)
|
#define UART_CONFIG_IRQ (1 << 1)
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
#include <linux/ftrace.h>
|
#include <linux/ftrace.h>
|
||||||
#include <linux/async.h>
|
#include <linux/async.h>
|
||||||
#include <linux/kmemcheck.h>
|
#include <linux/kmemcheck.h>
|
||||||
|
@ -675,6 +676,7 @@ asmlinkage void __init start_kernel(void)
|
||||||
buffer_init();
|
buffer_init();
|
||||||
key_init();
|
key_init();
|
||||||
security_init();
|
security_init();
|
||||||
|
kdb_init(KDB_INIT_FULL);
|
||||||
vfs_caches_init(totalram_pages);
|
vfs_caches_init(totalram_pages);
|
||||||
signals_init();
|
signals_init();
|
||||||
/* rootfs populating might need page-writeback */
|
/* rootfs populating might need page-writeback */
|
||||||
|
|
|
@ -75,7 +75,7 @@ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
|
||||||
obj-$(CONFIG_GCOV_KERNEL) += gcov/
|
obj-$(CONFIG_GCOV_KERNEL) += gcov/
|
||||||
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
|
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
|
||||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||||
obj-$(CONFIG_KGDB) += kgdb.o
|
obj-$(CONFIG_KGDB) += debug/
|
||||||
obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
|
obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
|
||||||
obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
|
obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
|
||||||
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
|
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
|
||||||
|
|
6
kernel/debug/Makefile
Normal file
6
kernel/debug/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#
|
||||||
|
# Makefile for the linux kernel debugger
|
||||||
|
#
|
||||||
|
|
||||||
|
obj-$(CONFIG_KGDB) += debug_core.o gdbstub.o
|
||||||
|
obj-$(CONFIG_KGDB_KDB) += kdb/
|
967
kernel/debug/debug_core.c
Normal file
967
kernel/debug/debug_core.c
Normal file
|
@ -0,0 +1,967 @@
|
||||||
|
/*
|
||||||
|
* Kernel Debug Core
|
||||||
|
*
|
||||||
|
* 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-2009 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/string.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/sysrq.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kgdb.h>
|
||||||
|
#include <linux/kdb.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>
|
||||||
|
|
||||||
|
#include "debug_core.h"
|
||||||
|
|
||||||
|
static int kgdb_break_asap;
|
||||||
|
|
||||||
|
struct debuggerinfo_struct 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 */
|
||||||
|
int kgdb_io_module_registered;
|
||||||
|
|
||||||
|
/* Guard for recursive entry */
|
||||||
|
static int exception_level;
|
||||||
|
|
||||||
|
struct kgdb_io *dbg_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;
|
||||||
|
/* Next cpu to become the master debug core */
|
||||||
|
int dbg_switch_cpu;
|
||||||
|
|
||||||
|
/* Use kdb or gdbserver mode */
|
||||||
|
int dbg_kdb_mode = 1;
|
||||||
|
|
||||||
|
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);
|
||||||
|
EXPORT_SYMBOL_GPL(kgdb_active);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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];
|
||||||
|
static atomic_t kgdb_break_tasklet_var;
|
||||||
|
atomic_t kgdb_setting_breakpoint;
|
||||||
|
|
||||||
|
struct task_struct *kgdb_usethread;
|
||||||
|
struct task_struct *kgdb_contthread;
|
||||||
|
|
||||||
|
int kgdb_single_step;
|
||||||
|
static pid_t kgdb_sstep_pid;
|
||||||
|
|
||||||
|
/* 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:
|
||||||
|
*/
|
||||||
|
static 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_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __weak kgdb_validate_break_address(unsigned long addr)
|
||||||
|
{
|
||||||
|
char tmp_variable[BREAK_INSTR_SIZE];
|
||||||
|
int err;
|
||||||
|
/* Validate setting the breakpoint and then removing it. In the
|
||||||
|
* remove fails, the kernel needs to emit a bad message because we
|
||||||
|
* are deep trouble not being able to put things back the way we
|
||||||
|
* found them.
|
||||||
|
*/
|
||||||
|
err = kgdb_arch_set_breakpoint(addr, tmp_variable);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = kgdb_arch_remove_breakpoint(addr, tmp_variable);
|
||||||
|
if (err)
|
||||||
|
printk(KERN_ERR "KGDB: Critical breakpoint error, kernel "
|
||||||
|
"memory destroyed at: %lx", addr);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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:
|
||||||
|
*/
|
||||||
|
int dbg_activate_sw_breakpoints(void)
|
||||||
|
{
|
||||||
|
unsigned long addr;
|
||||||
|
int error;
|
||||||
|
int ret = 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) {
|
||||||
|
ret = error;
|
||||||
|
printk(KERN_INFO "KGDB: BP install failed: %lx", addr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
kgdb_flush_swbreak_addr(addr);
|
||||||
|
kgdb_break[i].state = BP_ACTIVE;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dbg_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dbg_deactivate_sw_breakpoints(void)
|
||||||
|
{
|
||||||
|
unsigned long addr;
|
||||||
|
int error;
|
||||||
|
int ret = 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) {
|
||||||
|
printk(KERN_INFO "KGDB: BP remove failed: %lx\n", addr);
|
||||||
|
ret = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
kgdb_flush_swbreak_addr(addr);
|
||||||
|
kgdb_break[i].state = BP_SET;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dbg_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 dbg_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 (!dbg_io_ops)
|
||||||
|
return 0;
|
||||||
|
if (kgdb_connected)
|
||||||
|
return 1;
|
||||||
|
if (atomic_read(&kgdb_setting_breakpoint))
|
||||||
|
return 1;
|
||||||
|
if (print_wait) {
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
if (!dbg_kdb_mode)
|
||||||
|
printk(KERN_CRIT "KGDB: waiting... or $3#33 for KDB\n");
|
||||||
|
#else
|
||||||
|
printk(KERN_CRIT "KGDB: Waiting for remote debugger\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
dbg_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 (dbg_remove_sw_break(addr) == 0) {
|
||||||
|
exception_level = 0;
|
||||||
|
kgdb_skipexception(ks->ex_vector, ks->linux_regs);
|
||||||
|
dbg_activate_sw_breakpoints();
|
||||||
|
printk(KERN_CRIT "KGDB: re-enter error: breakpoint removed %lx\n",
|
||||||
|
addr);
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dbg_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");
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
/* Allow kdb to debug itself one level */
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
dump_stack();
|
||||||
|
panic("Recursive entry to debugger");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbg_cpu_switch(int cpu, int next_cpu)
|
||||||
|
{
|
||||||
|
/* Mark the cpu we are switching away from as a slave when it
|
||||||
|
* holds the kgdb_active token. This must be done so that the
|
||||||
|
* that all the cpus wait in for the debug core will not enter
|
||||||
|
* again as the master. */
|
||||||
|
if (cpu == atomic_read(&kgdb_active)) {
|
||||||
|
kgdb_info[cpu].exception_state |= DCPU_IS_SLAVE;
|
||||||
|
kgdb_info[cpu].exception_state &= ~DCPU_WANT_MASTER;
|
||||||
|
}
|
||||||
|
kgdb_info[next_cpu].exception_state |= DCPU_NEXT_MASTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int sstep_tries = 100;
|
||||||
|
int error;
|
||||||
|
int i, cpu;
|
||||||
|
int trace_on = 0;
|
||||||
|
acquirelock:
|
||||||
|
/*
|
||||||
|
* Interrupts will be restored by the 'trap return' code, except when
|
||||||
|
* single stepping.
|
||||||
|
*/
|
||||||
|
local_irq_save(flags);
|
||||||
|
|
||||||
|
cpu = ks->cpu;
|
||||||
|
kgdb_info[cpu].debuggerinfo = regs;
|
||||||
|
kgdb_info[cpu].task = current;
|
||||||
|
kgdb_info[cpu].ret_state = 0;
|
||||||
|
kgdb_info[cpu].irq_depth = hardirq_count() >> HARDIRQ_SHIFT;
|
||||||
|
/*
|
||||||
|
* Make sure the above info reaches the primary CPU before
|
||||||
|
* our cpu_in_kgdb[] flag setting does:
|
||||||
|
*/
|
||||||
|
atomic_inc(&cpu_in_kgdb[cpu]);
|
||||||
|
|
||||||
|
if (exception_level == 1)
|
||||||
|
goto cpu_master_loop;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPU will loop if it is a slave or request to become a kgdb
|
||||||
|
* master cpu and acquire the kgdb_active lock:
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
cpu_loop:
|
||||||
|
if (kgdb_info[cpu].exception_state & DCPU_NEXT_MASTER) {
|
||||||
|
kgdb_info[cpu].exception_state &= ~DCPU_NEXT_MASTER;
|
||||||
|
goto cpu_master_loop;
|
||||||
|
} else if (kgdb_info[cpu].exception_state & DCPU_WANT_MASTER) {
|
||||||
|
if (atomic_cmpxchg(&kgdb_active, -1, cpu) == cpu)
|
||||||
|
break;
|
||||||
|
} else if (kgdb_info[cpu].exception_state & DCPU_IS_SLAVE) {
|
||||||
|
if (!atomic_read(&passive_cpu_wait[cpu]))
|
||||||
|
goto return_normal;
|
||||||
|
} else {
|
||||||
|
return_normal:
|
||||||
|
/* Return to normal operation by executing any
|
||||||
|
* hw breakpoint fixup.
|
||||||
|
*/
|
||||||
|
if (arch_kgdb_ops.correct_hw_break)
|
||||||
|
arch_kgdb_ops.correct_hw_break();
|
||||||
|
if (trace_on)
|
||||||
|
tracing_on();
|
||||||
|
atomic_dec(&cpu_in_kgdb[cpu]);
|
||||||
|
touch_softlockup_watchdog_sync();
|
||||||
|
clocksource_touch_watchdog();
|
||||||
|
local_irq_restore(flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
cpu_relax();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For single stepping, try to only enter on the processor
|
||||||
|
* that was single stepping. To gaurd against a deadlock, the
|
||||||
|
* kernel will only try for the value of sstep_tries before
|
||||||
|
* giving up and continuing on.
|
||||||
|
*/
|
||||||
|
if (atomic_read(&kgdb_cpu_doing_single_step) != -1 &&
|
||||||
|
(kgdb_info[cpu].task &&
|
||||||
|
kgdb_info[cpu].task->pid != kgdb_sstep_pid) && --sstep_tries) {
|
||||||
|
atomic_set(&kgdb_active, -1);
|
||||||
|
touch_softlockup_watchdog_sync();
|
||||||
|
clocksource_touch_watchdog();
|
||||||
|
local_irq_restore(flags);
|
||||||
|
|
||||||
|
goto acquirelock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kgdb_io_ready(1)) {
|
||||||
|
kgdb_info[cpu].ret_state = 1;
|
||||||
|
goto kgdb_restore; /* No I/O connection, 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 (dbg_io_ops->pre_exception)
|
||||||
|
dbg_io_ops->pre_exception();
|
||||||
|
|
||||||
|
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) {
|
||||||
|
for (i = 0; i < NR_CPUS; i++)
|
||||||
|
atomic_inc(&passive_cpu_wait[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
/* Signal the other CPUs to enter kgdb_wait() */
|
||||||
|
if ((!kgdb_single_step) && 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 (kgdb_do_roundup && !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
|
||||||
|
*/
|
||||||
|
dbg_deactivate_sw_breakpoints();
|
||||||
|
kgdb_single_step = 0;
|
||||||
|
kgdb_contthread = current;
|
||||||
|
exception_level = 0;
|
||||||
|
trace_on = tracing_is_on();
|
||||||
|
if (trace_on)
|
||||||
|
tracing_off();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
cpu_master_loop:
|
||||||
|
if (dbg_kdb_mode) {
|
||||||
|
kgdb_connected = 1;
|
||||||
|
error = kdb_stub(ks);
|
||||||
|
} else {
|
||||||
|
error = gdb_serial_stub(ks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == DBG_PASS_EVENT) {
|
||||||
|
dbg_kdb_mode = !dbg_kdb_mode;
|
||||||
|
kgdb_connected = 0;
|
||||||
|
} else if (error == DBG_SWITCH_CPU_EVENT) {
|
||||||
|
dbg_cpu_switch(cpu, dbg_switch_cpu);
|
||||||
|
goto cpu_loop;
|
||||||
|
} else {
|
||||||
|
kgdb_info[cpu].ret_state = error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call the I/O driver's post_exception routine */
|
||||||
|
if (dbg_io_ops->post_exception)
|
||||||
|
dbg_io_ops->post_exception();
|
||||||
|
|
||||||
|
atomic_dec(&cpu_in_kgdb[ks->cpu]);
|
||||||
|
|
||||||
|
if (!kgdb_single_step) {
|
||||||
|
for (i = NR_CPUS-1; i >= 0; i--)
|
||||||
|
atomic_dec(&passive_cpu_wait[i]);
|
||||||
|
/*
|
||||||
|
* Wait till all the CPUs have quit from the debugger,
|
||||||
|
* but allow a CPU that hit an exception and is
|
||||||
|
* waiting to become the master to remain in the debug
|
||||||
|
* core.
|
||||||
|
*/
|
||||||
|
for_each_online_cpu(i) {
|
||||||
|
while (kgdb_do_roundup &&
|
||||||
|
atomic_read(&cpu_in_kgdb[i]) &&
|
||||||
|
!(kgdb_info[i].exception_state &
|
||||||
|
DCPU_WANT_MASTER))
|
||||||
|
cpu_relax();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kgdb_restore:
|
||||||
|
if (atomic_read(&kgdb_cpu_doing_single_step) != -1) {
|
||||||
|
int sstep_cpu = atomic_read(&kgdb_cpu_doing_single_step);
|
||||||
|
if (kgdb_info[sstep_cpu].task)
|
||||||
|
kgdb_sstep_pid = kgdb_info[sstep_cpu].task->pid;
|
||||||
|
else
|
||||||
|
kgdb_sstep_pid = 0;
|
||||||
|
}
|
||||||
|
if (trace_on)
|
||||||
|
tracing_on();
|
||||||
|
/* Free kgdb_active */
|
||||||
|
atomic_set(&kgdb_active, -1);
|
||||||
|
touch_softlockup_watchdog_sync();
|
||||||
|
clocksource_touch_watchdog();
|
||||||
|
local_irq_restore(flags);
|
||||||
|
|
||||||
|
return kgdb_info[cpu].ret_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ks->cpu = raw_smp_processor_id();
|
||||||
|
ks->ex_vector = evector;
|
||||||
|
ks->signo = signo;
|
||||||
|
ks->err_code = ecode;
|
||||||
|
ks->kgdb_usethreadid = 0;
|
||||||
|
ks->linux_regs = regs;
|
||||||
|
|
||||||
|
if (kgdb_reenter_check(ks))
|
||||||
|
return 0; /* Ouch, double exception ! */
|
||||||
|
kgdb_info[ks->cpu].exception_state |= DCPU_WANT_MASTER;
|
||||||
|
ret = kgdb_cpu_enter(ks, regs);
|
||||||
|
kgdb_info[ks->cpu].exception_state &= ~(DCPU_WANT_MASTER |
|
||||||
|
DCPU_IS_SLAVE);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kgdb_nmicallback(int cpu, void *regs)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
struct kgdb_state kgdb_var;
|
||||||
|
struct kgdb_state *ks = &kgdb_var;
|
||||||
|
|
||||||
|
memset(ks, 0, sizeof(struct kgdb_state));
|
||||||
|
ks->cpu = cpu;
|
||||||
|
ks->linux_regs = regs;
|
||||||
|
|
||||||
|
if (!atomic_read(&cpu_in_kgdb[cpu]) &&
|
||||||
|
atomic_read(&kgdb_active) != -1 &&
|
||||||
|
atomic_read(&kgdb_active) != cpu) {
|
||||||
|
kgdb_info[cpu].exception_state |= DCPU_IS_SLAVE;
|
||||||
|
kgdb_cpu_enter(ks, regs);
|
||||||
|
kgdb_info[cpu].exception_state &= ~DCPU_IS_SLAVE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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 || dbg_kdb_mode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
local_irq_save(flags);
|
||||||
|
gdbstub_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_dbg(int key, struct tty_struct *tty)
|
||||||
|
{
|
||||||
|
if (!dbg_io_ops) {
|
||||||
|
printk(KERN_CRIT "ERROR: No KGDB I/O module available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!kgdb_connected) {
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
if (!dbg_kdb_mode)
|
||||||
|
printk(KERN_CRIT "KGDB or $3#33 for KDB\n");
|
||||||
|
#else
|
||||||
|
printk(KERN_CRIT "Entering KGDB\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
kgdb_breakpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sysrq_key_op sysrq_dbg_op = {
|
||||||
|
.handler = sysrq_handle_dbg,
|
||||||
|
.help_msg = "debug(G)",
|
||||||
|
.action_msg = "DEBUG",
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int kgdb_panic_event(struct notifier_block *self,
|
||||||
|
unsigned long val,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
if (dbg_kdb_mode)
|
||||||
|
kdb_printf("PANIC: %s\n", (char *)data);
|
||||||
|
kgdb_breakpoint();
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block kgdb_panic_event_nb = {
|
||||||
|
.notifier_call = kgdb_panic_event,
|
||||||
|
.priority = INT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void kgdb_register_callbacks(void)
|
||||||
|
{
|
||||||
|
if (!kgdb_io_module_registered) {
|
||||||
|
kgdb_io_module_registered = 1;
|
||||||
|
kgdb_arch_init();
|
||||||
|
atomic_notifier_chain_register(&panic_notifier_list,
|
||||||
|
&kgdb_panic_event_nb);
|
||||||
|
#ifdef CONFIG_MAGIC_SYSRQ
|
||||||
|
register_sysrq_key('g', &sysrq_dbg_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;
|
||||||
|
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||||
|
&kgdb_panic_event_nb);
|
||||||
|
kgdb_arch_exit();
|
||||||
|
#ifdef CONFIG_MAGIC_SYSRQ
|
||||||
|
unregister_sysrq_key('g', &sysrq_dbg_op);
|
||||||
|
#endif
|
||||||
|
if (kgdb_con_registered) {
|
||||||
|
unregister_console(&kgdbcons);
|
||||||
|
kgdb_con_registered = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are times a tasklet needs to be used vs a compiled in
|
||||||
|
* break point so as to cause an exception outside a kgdb I/O module,
|
||||||
|
* such as is the case with kgdboe, where calling a breakpoint in the
|
||||||
|
* I/O driver itself would be fatal.
|
||||||
|
*/
|
||||||
|
static void kgdb_tasklet_bpt(unsigned long ing)
|
||||||
|
{
|
||||||
|
kgdb_breakpoint();
|
||||||
|
atomic_set(&kgdb_break_tasklet_var, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DECLARE_TASKLET(kgdb_tasklet_breakpoint, kgdb_tasklet_bpt, 0);
|
||||||
|
|
||||||
|
void kgdb_schedule_breakpoint(void)
|
||||||
|
{
|
||||||
|
if (atomic_read(&kgdb_break_tasklet_var) ||
|
||||||
|
atomic_read(&kgdb_active) != -1 ||
|
||||||
|
atomic_read(&kgdb_setting_breakpoint))
|
||||||
|
return;
|
||||||
|
atomic_inc(&kgdb_break_tasklet_var);
|
||||||
|
tasklet_schedule(&kgdb_tasklet_breakpoint);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kgdb_schedule_breakpoint);
|
||||||
|
|
||||||
|
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_dbg_io_ops: the io ops vector
|
||||||
|
*
|
||||||
|
* Register it with the KGDB core.
|
||||||
|
*/
|
||||||
|
int kgdb_register_io_module(struct kgdb_io *new_dbg_io_ops)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
spin_lock(&kgdb_registration_lock);
|
||||||
|
|
||||||
|
if (dbg_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_dbg_io_ops->init) {
|
||||||
|
err = new_dbg_io_ops->init();
|
||||||
|
if (err) {
|
||||||
|
spin_unlock(&kgdb_registration_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg_io_ops = new_dbg_io_ops;
|
||||||
|
|
||||||
|
spin_unlock(&kgdb_registration_lock);
|
||||||
|
|
||||||
|
printk(KERN_INFO "kgdb: Registered I/O driver %s.\n",
|
||||||
|
new_dbg_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_dbg_io_ops: the io ops vector
|
||||||
|
*
|
||||||
|
* Unregister it with the KGDB core.
|
||||||
|
*/
|
||||||
|
void kgdb_unregister_io_module(struct kgdb_io *old_dbg_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(dbg_io_ops != old_dbg_io_ops);
|
||||||
|
dbg_io_ops = NULL;
|
||||||
|
|
||||||
|
spin_unlock(&kgdb_registration_lock);
|
||||||
|
|
||||||
|
printk(KERN_INFO
|
||||||
|
"kgdb: Unregistered I/O driver %s, debugger disabled.\n",
|
||||||
|
old_dbg_io_ops->name);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kgdb_unregister_io_module);
|
||||||
|
|
||||||
|
int dbg_io_get_char(void)
|
||||||
|
{
|
||||||
|
int ret = dbg_io_ops->read_char();
|
||||||
|
if (ret == NO_POLL_CHAR)
|
||||||
|
return -1;
|
||||||
|
if (!dbg_kdb_mode)
|
||||||
|
return ret;
|
||||||
|
if (ret == 127)
|
||||||
|
return 8;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_inc(&kgdb_setting_breakpoint);
|
||||||
|
wmb(); /* Sync point before breakpoint */
|
||||||
|
arch_kgdb_breakpoint();
|
||||||
|
wmb(); /* Sync point after breakpoint */
|
||||||
|
atomic_dec(&kgdb_setting_breakpoint);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kgdb_breakpoint);
|
||||||
|
|
||||||
|
static int __init opt_kgdb_wait(char *str)
|
||||||
|
{
|
||||||
|
kgdb_break_asap = 1;
|
||||||
|
|
||||||
|
kdb_init(KDB_INIT_EARLY);
|
||||||
|
if (kgdb_io_module_registered)
|
||||||
|
kgdb_initial_breakpoint();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
early_param("kgdbwait", opt_kgdb_wait);
|
81
kernel/debug/debug_core.h
Normal file
81
kernel/debug/debug_core.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Created by: Jason Wessel <jason.wessel@windriver.com>
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* 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 _DEBUG_CORE_H_
|
||||||
|
#define _DEBUG_CORE_H_
|
||||||
|
/*
|
||||||
|
* These are the private implementation headers between the kernel
|
||||||
|
* debugger core and the debugger front end code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* kernel debug core data structures */
|
||||||
|
struct kgdb_state {
|
||||||
|
int ex_vector;
|
||||||
|
int signo;
|
||||||
|
int err_code;
|
||||||
|
int cpu;
|
||||||
|
int pass_exception;
|
||||||
|
unsigned long thr_query;
|
||||||
|
unsigned long threadid;
|
||||||
|
long kgdb_usethreadid;
|
||||||
|
struct pt_regs *linux_regs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Exception state values */
|
||||||
|
#define DCPU_WANT_MASTER 0x1 /* Waiting to become a master kgdb cpu */
|
||||||
|
#define DCPU_NEXT_MASTER 0x2 /* Transition from one master cpu to another */
|
||||||
|
#define DCPU_IS_SLAVE 0x4 /* Slave cpu enter exception */
|
||||||
|
#define DCPU_SSTEP 0x8 /* CPU is single stepping */
|
||||||
|
|
||||||
|
struct debuggerinfo_struct {
|
||||||
|
void *debuggerinfo;
|
||||||
|
struct task_struct *task;
|
||||||
|
int exception_state;
|
||||||
|
int ret_state;
|
||||||
|
int irq_depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct debuggerinfo_struct kgdb_info[];
|
||||||
|
|
||||||
|
/* kernel debug core break point routines */
|
||||||
|
extern int dbg_remove_all_break(void);
|
||||||
|
extern int dbg_set_sw_break(unsigned long addr);
|
||||||
|
extern int dbg_remove_sw_break(unsigned long addr);
|
||||||
|
extern int dbg_activate_sw_breakpoints(void);
|
||||||
|
extern int dbg_deactivate_sw_breakpoints(void);
|
||||||
|
|
||||||
|
/* polled character access to i/o module */
|
||||||
|
extern int dbg_io_get_char(void);
|
||||||
|
|
||||||
|
/* stub return value for switching between the gdbstub and kdb */
|
||||||
|
#define DBG_PASS_EVENT -12345
|
||||||
|
/* Switch from one cpu to another */
|
||||||
|
#define DBG_SWITCH_CPU_EVENT -123456
|
||||||
|
extern int dbg_switch_cpu;
|
||||||
|
|
||||||
|
/* gdbstub interface functions */
|
||||||
|
extern int gdb_serial_stub(struct kgdb_state *ks);
|
||||||
|
extern void gdbstub_msg_write(const char *s, int len);
|
||||||
|
|
||||||
|
/* gdbstub functions used for kdb <-> gdbstub transition */
|
||||||
|
extern int gdbstub_state(struct kgdb_state *ks, char *cmd);
|
||||||
|
extern int dbg_kdb_mode;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
extern int kdb_stub(struct kgdb_state *ks);
|
||||||
|
extern int kdb_parse(const char *cmdstr);
|
||||||
|
#else /* ! CONFIG_KGDB_KDB */
|
||||||
|
static inline int kdb_stub(struct kgdb_state *ks)
|
||||||
|
{
|
||||||
|
return DBG_PASS_EVENT;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
|
|
||||||
|
#endif /* _DEBUG_CORE_H_ */
|
1017
kernel/debug/gdbstub.c
Normal file
1017
kernel/debug/gdbstub.c
Normal file
File diff suppressed because it is too large
Load diff
1
kernel/debug/kdb/.gitignore
vendored
Normal file
1
kernel/debug/kdb/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
gen-kdb_cmds.c
|
25
kernel/debug/kdb/Makefile
Normal file
25
kernel/debug/kdb/Makefile
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
# License. See the file "COPYING" in the main directory of this archive
|
||||||
|
# for more details.
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
# Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
CCVERSION := $(shell $(CC) -v 2>&1 | sed -ne '$$p')
|
||||||
|
obj-y := kdb_io.o kdb_main.o kdb_support.o kdb_bt.o gen-kdb_cmds.o kdb_bp.o kdb_debugger.o
|
||||||
|
obj-$(CONFIG_KDB_KEYBOARD) += kdb_keyboard.o
|
||||||
|
|
||||||
|
clean-files := gen-kdb_cmds.c
|
||||||
|
|
||||||
|
quiet_cmd_gen-kdb = GENKDB $@
|
||||||
|
cmd_gen-kdb = $(AWK) 'BEGIN {print "\#include <linux/stddef.h>"; print "\#include <linux/init.h>"} \
|
||||||
|
/^\#/{next} \
|
||||||
|
/^[ \t]*$$/{next} \
|
||||||
|
{gsub(/"/, "\\\"", $$0); \
|
||||||
|
print "static __initdata char kdb_cmd" cmds++ "[] = \"" $$0 "\\n\";"} \
|
||||||
|
END {print "extern char *kdb_cmds[]; char __initdata *kdb_cmds[] = {"; for (i = 0; i < cmds; ++i) {print " kdb_cmd" i ","}; print(" NULL\n};");}' \
|
||||||
|
$(filter-out %/Makefile,$^) > $@#
|
||||||
|
|
||||||
|
$(obj)/gen-kdb_cmds.c: $(src)/kdb_cmds $(src)/Makefile
|
||||||
|
$(call cmd,gen-kdb)
|
564
kernel/debug/kdb/kdb_bp.c
Normal file
564
kernel/debug/kdb/kdb_bp.c
Normal file
|
@ -0,0 +1,564 @@
|
||||||
|
/*
|
||||||
|
* Kernel Debugger Architecture Independent Breakpoint Handler
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
#include <linux/kgdb.h>
|
||||||
|
#include <linux/smp.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include "kdb_private.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Table of kdb_breakpoints
|
||||||
|
*/
|
||||||
|
kdb_bp_t kdb_breakpoints[KDB_MAXBPT];
|
||||||
|
|
||||||
|
static void kdb_setsinglestep(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
KDB_STATE_SET(DOING_SS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *kdb_rwtypes[] = {
|
||||||
|
"Instruction(i)",
|
||||||
|
"Instruction(Register)",
|
||||||
|
"Data Write",
|
||||||
|
"I/O",
|
||||||
|
"Data Access"
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *kdb_bptype(kdb_bp_t *bp)
|
||||||
|
{
|
||||||
|
if (bp->bp_type < 0 || bp->bp_type > 4)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return kdb_rwtypes[bp->bp_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kdb_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp)
|
||||||
|
{
|
||||||
|
int nextarg = *nextargp;
|
||||||
|
int diag;
|
||||||
|
|
||||||
|
bp->bph_length = 1;
|
||||||
|
if ((argc + 1) != nextarg) {
|
||||||
|
if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0)
|
||||||
|
bp->bp_type = BP_ACCESS_WATCHPOINT;
|
||||||
|
else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0)
|
||||||
|
bp->bp_type = BP_WRITE_WATCHPOINT;
|
||||||
|
else if (strnicmp(argv[nextarg], "inst", sizeof("inst")) == 0)
|
||||||
|
bp->bp_type = BP_HARDWARE_BREAKPOINT;
|
||||||
|
else
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
|
||||||
|
bp->bph_length = 1;
|
||||||
|
|
||||||
|
nextarg++;
|
||||||
|
|
||||||
|
if ((argc + 1) != nextarg) {
|
||||||
|
unsigned long len;
|
||||||
|
|
||||||
|
diag = kdbgetularg((char *)argv[nextarg],
|
||||||
|
&len);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
|
||||||
|
|
||||||
|
if (len > 8)
|
||||||
|
return KDB_BADLENGTH;
|
||||||
|
|
||||||
|
bp->bph_length = len;
|
||||||
|
nextarg++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((argc + 1) != nextarg)
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
*nextargp = nextarg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _kdb_bp_remove(kdb_bp_t *bp)
|
||||||
|
{
|
||||||
|
int ret = 1;
|
||||||
|
if (!bp->bp_installed)
|
||||||
|
return ret;
|
||||||
|
if (!bp->bp_type)
|
||||||
|
ret = dbg_remove_sw_break(bp->bp_addr);
|
||||||
|
else
|
||||||
|
ret = arch_kgdb_ops.remove_hw_breakpoint(bp->bp_addr,
|
||||||
|
bp->bph_length,
|
||||||
|
bp->bp_type);
|
||||||
|
if (ret == 0)
|
||||||
|
bp->bp_installed = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kdb_handle_bp(struct pt_regs *regs, kdb_bp_t *bp)
|
||||||
|
{
|
||||||
|
if (KDB_DEBUG(BP))
|
||||||
|
kdb_printf("regs->ip = 0x%lx\n", instruction_pointer(regs));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup single step
|
||||||
|
*/
|
||||||
|
kdb_setsinglestep(regs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset delay attribute
|
||||||
|
*/
|
||||||
|
bp->bp_delay = 0;
|
||||||
|
bp->bp_delayed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _kdb_bp_install(struct pt_regs *regs, kdb_bp_t *bp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
/*
|
||||||
|
* Install the breakpoint, if it is not already installed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (KDB_DEBUG(BP))
|
||||||
|
kdb_printf("%s: bp_installed %d\n",
|
||||||
|
__func__, bp->bp_installed);
|
||||||
|
if (!KDB_STATE(SSBPT))
|
||||||
|
bp->bp_delay = 0;
|
||||||
|
if (bp->bp_installed)
|
||||||
|
return 1;
|
||||||
|
if (bp->bp_delay || (bp->bp_delayed && KDB_STATE(DOING_SS))) {
|
||||||
|
if (KDB_DEBUG(BP))
|
||||||
|
kdb_printf("%s: delayed bp\n", __func__);
|
||||||
|
kdb_handle_bp(regs, bp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!bp->bp_type)
|
||||||
|
ret = dbg_set_sw_break(bp->bp_addr);
|
||||||
|
else
|
||||||
|
ret = arch_kgdb_ops.set_hw_breakpoint(bp->bp_addr,
|
||||||
|
bp->bph_length,
|
||||||
|
bp->bp_type);
|
||||||
|
if (ret == 0) {
|
||||||
|
bp->bp_installed = 1;
|
||||||
|
} else {
|
||||||
|
kdb_printf("%s: failed to set breakpoint at 0x%lx\n",
|
||||||
|
__func__, bp->bp_addr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_bp_install
|
||||||
|
*
|
||||||
|
* Install kdb_breakpoints prior to returning from the
|
||||||
|
* kernel debugger. This allows the kdb_breakpoints to be set
|
||||||
|
* upon functions that are used internally by kdb, such as
|
||||||
|
* printk(). This function is only called once per kdb session.
|
||||||
|
*/
|
||||||
|
void kdb_bp_install(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < KDB_MAXBPT; i++) {
|
||||||
|
kdb_bp_t *bp = &kdb_breakpoints[i];
|
||||||
|
|
||||||
|
if (KDB_DEBUG(BP)) {
|
||||||
|
kdb_printf("%s: bp %d bp_enabled %d\n",
|
||||||
|
__func__, i, bp->bp_enabled);
|
||||||
|
}
|
||||||
|
if (bp->bp_enabled)
|
||||||
|
_kdb_bp_install(regs, bp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_bp_remove
|
||||||
|
*
|
||||||
|
* Remove kdb_breakpoints upon entry to the kernel debugger.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* None.
|
||||||
|
* Outputs:
|
||||||
|
* None.
|
||||||
|
* Returns:
|
||||||
|
* None.
|
||||||
|
* Locking:
|
||||||
|
* None.
|
||||||
|
* Remarks:
|
||||||
|
*/
|
||||||
|
void kdb_bp_remove(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = KDB_MAXBPT - 1; i >= 0; i--) {
|
||||||
|
kdb_bp_t *bp = &kdb_breakpoints[i];
|
||||||
|
|
||||||
|
if (KDB_DEBUG(BP)) {
|
||||||
|
kdb_printf("%s: bp %d bp_enabled %d\n",
|
||||||
|
__func__, i, bp->bp_enabled);
|
||||||
|
}
|
||||||
|
if (bp->bp_enabled)
|
||||||
|
_kdb_bp_remove(bp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_printbp
|
||||||
|
*
|
||||||
|
* Internal function to format and print a breakpoint entry.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* None.
|
||||||
|
* Outputs:
|
||||||
|
* None.
|
||||||
|
* Returns:
|
||||||
|
* None.
|
||||||
|
* Locking:
|
||||||
|
* None.
|
||||||
|
* Remarks:
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void kdb_printbp(kdb_bp_t *bp, int i)
|
||||||
|
{
|
||||||
|
kdb_printf("%s ", kdb_bptype(bp));
|
||||||
|
kdb_printf("BP #%d at ", i);
|
||||||
|
kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT);
|
||||||
|
|
||||||
|
if (bp->bp_enabled)
|
||||||
|
kdb_printf("\n is enabled");
|
||||||
|
else
|
||||||
|
kdb_printf("\n is disabled");
|
||||||
|
|
||||||
|
kdb_printf("\taddr at %016lx, hardtype=%d installed=%d\n",
|
||||||
|
bp->bp_addr, bp->bp_type, bp->bp_installed);
|
||||||
|
|
||||||
|
kdb_printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_bp
|
||||||
|
*
|
||||||
|
* Handle the bp commands.
|
||||||
|
*
|
||||||
|
* [bp|bph] <addr-expression> [DATAR|DATAW]
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* argc Count of arguments in argv
|
||||||
|
* argv Space delimited command line arguments
|
||||||
|
* Outputs:
|
||||||
|
* None.
|
||||||
|
* Returns:
|
||||||
|
* Zero for success, a kdb diagnostic if failure.
|
||||||
|
* Locking:
|
||||||
|
* None.
|
||||||
|
* Remarks:
|
||||||
|
*
|
||||||
|
* bp Set breakpoint on all cpus. Only use hardware assist if need.
|
||||||
|
* bph Set breakpoint on all cpus. Force hardware register
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int kdb_bp(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int i, bpno;
|
||||||
|
kdb_bp_t *bp, *bp_check;
|
||||||
|
int diag;
|
||||||
|
int free;
|
||||||
|
char *symname = NULL;
|
||||||
|
long offset = 0ul;
|
||||||
|
int nextarg;
|
||||||
|
kdb_bp_t template = {0};
|
||||||
|
|
||||||
|
if (argc == 0) {
|
||||||
|
/*
|
||||||
|
* Display breakpoint table
|
||||||
|
*/
|
||||||
|
for (bpno = 0, bp = kdb_breakpoints; bpno < KDB_MAXBPT;
|
||||||
|
bpno++, bp++) {
|
||||||
|
if (bp->bp_free)
|
||||||
|
continue;
|
||||||
|
kdb_printbp(bp, bpno);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextarg = 1;
|
||||||
|
diag = kdbgetaddrarg(argc, argv, &nextarg, &template.bp_addr,
|
||||||
|
&offset, &symname);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
if (!template.bp_addr)
|
||||||
|
return KDB_BADINT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find an empty bp structure to allocate
|
||||||
|
*/
|
||||||
|
free = KDB_MAXBPT;
|
||||||
|
for (bpno = 0, bp = kdb_breakpoints; bpno < KDB_MAXBPT; bpno++, bp++) {
|
||||||
|
if (bp->bp_free)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpno == KDB_MAXBPT)
|
||||||
|
return KDB_TOOMANYBPT;
|
||||||
|
|
||||||
|
if (strcmp(argv[0], "bph") == 0) {
|
||||||
|
template.bp_type = BP_HARDWARE_BREAKPOINT;
|
||||||
|
diag = kdb_parsebp(argc, argv, &nextarg, &template);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
} else {
|
||||||
|
template.bp_type = BP_BREAKPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for clashing breakpoints.
|
||||||
|
*
|
||||||
|
* Note, in this design we can't have hardware breakpoints
|
||||||
|
* enabled for both read and write on the same address.
|
||||||
|
*/
|
||||||
|
for (i = 0, bp_check = kdb_breakpoints; i < KDB_MAXBPT;
|
||||||
|
i++, bp_check++) {
|
||||||
|
if (!bp_check->bp_free &&
|
||||||
|
bp_check->bp_addr == template.bp_addr) {
|
||||||
|
kdb_printf("You already have a breakpoint at "
|
||||||
|
kdb_bfd_vma_fmt0 "\n", template.bp_addr);
|
||||||
|
return KDB_DUPBPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template.bp_enabled = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Actually allocate the breakpoint found earlier
|
||||||
|
*/
|
||||||
|
*bp = template;
|
||||||
|
bp->bp_free = 0;
|
||||||
|
|
||||||
|
kdb_printbp(bp, bpno);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_bc
|
||||||
|
*
|
||||||
|
* Handles the 'bc', 'be', and 'bd' commands
|
||||||
|
*
|
||||||
|
* [bd|bc|be] <breakpoint-number>
|
||||||
|
* [bd|bc|be] *
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* argc Count of arguments in argv
|
||||||
|
* argv Space delimited command line arguments
|
||||||
|
* Outputs:
|
||||||
|
* None.
|
||||||
|
* Returns:
|
||||||
|
* Zero for success, a kdb diagnostic for failure
|
||||||
|
* Locking:
|
||||||
|
* None.
|
||||||
|
* Remarks:
|
||||||
|
*/
|
||||||
|
static int kdb_bc(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
unsigned long addr;
|
||||||
|
kdb_bp_t *bp = NULL;
|
||||||
|
int lowbp = KDB_MAXBPT;
|
||||||
|
int highbp = 0;
|
||||||
|
int done = 0;
|
||||||
|
int i;
|
||||||
|
int diag = 0;
|
||||||
|
|
||||||
|
int cmd; /* KDBCMD_B? */
|
||||||
|
#define KDBCMD_BC 0
|
||||||
|
#define KDBCMD_BE 1
|
||||||
|
#define KDBCMD_BD 2
|
||||||
|
|
||||||
|
if (strcmp(argv[0], "be") == 0)
|
||||||
|
cmd = KDBCMD_BE;
|
||||||
|
else if (strcmp(argv[0], "bd") == 0)
|
||||||
|
cmd = KDBCMD_BD;
|
||||||
|
else
|
||||||
|
cmd = KDBCMD_BC;
|
||||||
|
|
||||||
|
if (argc != 1)
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
|
||||||
|
if (strcmp(argv[1], "*") == 0) {
|
||||||
|
lowbp = 0;
|
||||||
|
highbp = KDB_MAXBPT;
|
||||||
|
} else {
|
||||||
|
diag = kdbgetularg(argv[1], &addr);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For addresses less than the maximum breakpoint number,
|
||||||
|
* assume that the breakpoint number is desired.
|
||||||
|
*/
|
||||||
|
if (addr < KDB_MAXBPT) {
|
||||||
|
bp = &kdb_breakpoints[addr];
|
||||||
|
lowbp = highbp = addr;
|
||||||
|
highbp++;
|
||||||
|
} else {
|
||||||
|
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT;
|
||||||
|
i++, bp++) {
|
||||||
|
if (bp->bp_addr == addr) {
|
||||||
|
lowbp = highbp = i;
|
||||||
|
highbp++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now operate on the set of breakpoints matching the input
|
||||||
|
* criteria (either '*' for all, or an individual breakpoint).
|
||||||
|
*/
|
||||||
|
for (bp = &kdb_breakpoints[lowbp], i = lowbp;
|
||||||
|
i < highbp;
|
||||||
|
i++, bp++) {
|
||||||
|
if (bp->bp_free)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
done++;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case KDBCMD_BC:
|
||||||
|
bp->bp_enabled = 0;
|
||||||
|
|
||||||
|
kdb_printf("Breakpoint %d at "
|
||||||
|
kdb_bfd_vma_fmt " cleared\n",
|
||||||
|
i, bp->bp_addr);
|
||||||
|
|
||||||
|
bp->bp_addr = 0;
|
||||||
|
bp->bp_free = 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case KDBCMD_BE:
|
||||||
|
bp->bp_enabled = 1;
|
||||||
|
|
||||||
|
kdb_printf("Breakpoint %d at "
|
||||||
|
kdb_bfd_vma_fmt " enabled",
|
||||||
|
i, bp->bp_addr);
|
||||||
|
|
||||||
|
kdb_printf("\n");
|
||||||
|
break;
|
||||||
|
case KDBCMD_BD:
|
||||||
|
if (!bp->bp_enabled)
|
||||||
|
break;
|
||||||
|
|
||||||
|
bp->bp_enabled = 0;
|
||||||
|
|
||||||
|
kdb_printf("Breakpoint %d at "
|
||||||
|
kdb_bfd_vma_fmt " disabled\n",
|
||||||
|
i, bp->bp_addr);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (bp->bp_delay && (cmd == KDBCMD_BC || cmd == KDBCMD_BD)) {
|
||||||
|
bp->bp_delay = 0;
|
||||||
|
KDB_STATE_CLEAR(SSBPT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!done) ? KDB_BPTNOTFOUND : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_ss
|
||||||
|
*
|
||||||
|
* Process the 'ss' (Single Step) and 'ssb' (Single Step to Branch)
|
||||||
|
* commands.
|
||||||
|
*
|
||||||
|
* ss
|
||||||
|
* ssb
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* argc Argument count
|
||||||
|
* argv Argument vector
|
||||||
|
* Outputs:
|
||||||
|
* None.
|
||||||
|
* Returns:
|
||||||
|
* KDB_CMD_SS[B] for success, a kdb error if failure.
|
||||||
|
* Locking:
|
||||||
|
* None.
|
||||||
|
* Remarks:
|
||||||
|
*
|
||||||
|
* Set the arch specific option to trigger a debug trap after the next
|
||||||
|
* instruction.
|
||||||
|
*
|
||||||
|
* For 'ssb', set the trace flag in the debug trap handler
|
||||||
|
* after printing the current insn and return directly without
|
||||||
|
* invoking the kdb command processor, until a branch instruction
|
||||||
|
* is encountered.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int kdb_ss(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int ssb = 0;
|
||||||
|
|
||||||
|
ssb = (strcmp(argv[0], "ssb") == 0);
|
||||||
|
if (argc != 0)
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
/*
|
||||||
|
* Set trace flag and go.
|
||||||
|
*/
|
||||||
|
KDB_STATE_SET(DOING_SS);
|
||||||
|
if (ssb) {
|
||||||
|
KDB_STATE_SET(DOING_SSB);
|
||||||
|
return KDB_CMD_SSB;
|
||||||
|
}
|
||||||
|
return KDB_CMD_SS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the breakpoint table and register breakpoint commands. */
|
||||||
|
|
||||||
|
void __init kdb_initbptab(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
kdb_bp_t *bp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First time initialization.
|
||||||
|
*/
|
||||||
|
memset(&kdb_breakpoints, '\0', sizeof(kdb_breakpoints));
|
||||||
|
|
||||||
|
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++)
|
||||||
|
bp->bp_free = 1;
|
||||||
|
|
||||||
|
kdb_register_repeat("bp", kdb_bp, "[<vaddr>]",
|
||||||
|
"Set/Display breakpoints", 0, KDB_REPEAT_NO_ARGS);
|
||||||
|
kdb_register_repeat("bl", kdb_bp, "[<vaddr>]",
|
||||||
|
"Display breakpoints", 0, KDB_REPEAT_NO_ARGS);
|
||||||
|
if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT)
|
||||||
|
kdb_register_repeat("bph", kdb_bp, "[<vaddr>]",
|
||||||
|
"[datar [length]|dataw [length]] Set hw brk", 0, KDB_REPEAT_NO_ARGS);
|
||||||
|
kdb_register_repeat("bc", kdb_bc, "<bpnum>",
|
||||||
|
"Clear Breakpoint", 0, KDB_REPEAT_NONE);
|
||||||
|
kdb_register_repeat("be", kdb_bc, "<bpnum>",
|
||||||
|
"Enable Breakpoint", 0, KDB_REPEAT_NONE);
|
||||||
|
kdb_register_repeat("bd", kdb_bc, "<bpnum>",
|
||||||
|
"Disable Breakpoint", 0, KDB_REPEAT_NONE);
|
||||||
|
|
||||||
|
kdb_register_repeat("ss", kdb_ss, "",
|
||||||
|
"Single Step", 1, KDB_REPEAT_NO_ARGS);
|
||||||
|
kdb_register_repeat("ssb", kdb_ss, "",
|
||||||
|
"Single step to branch/call", 0, KDB_REPEAT_NO_ARGS);
|
||||||
|
/*
|
||||||
|
* Architecture dependent initialization.
|
||||||
|
*/
|
||||||
|
}
|
210
kernel/debug/kdb/kdb_bt.c
Normal file
210
kernel/debug/kdb/kdb_bt.c
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* Kernel Debugger Architecture Independent Stack Traceback
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
#include <linux/nmi.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include "kdb_private.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void kdb_show_stack(struct task_struct *p, void *addr)
|
||||||
|
{
|
||||||
|
int old_lvl = console_loglevel;
|
||||||
|
console_loglevel = 15;
|
||||||
|
kdb_trap_printk++;
|
||||||
|
kdb_set_current_task(p);
|
||||||
|
if (addr) {
|
||||||
|
show_stack((struct task_struct *)p, addr);
|
||||||
|
} else if (kdb_current_regs) {
|
||||||
|
#ifdef CONFIG_X86
|
||||||
|
show_stack(p, &kdb_current_regs->sp);
|
||||||
|
#else
|
||||||
|
show_stack(p, NULL);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
show_stack(p, NULL);
|
||||||
|
}
|
||||||
|
console_loglevel = old_lvl;
|
||||||
|
kdb_trap_printk--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_bt
|
||||||
|
*
|
||||||
|
* This function implements the 'bt' command. Print a stack
|
||||||
|
* traceback.
|
||||||
|
*
|
||||||
|
* bt [<address-expression>] (addr-exp is for alternate stacks)
|
||||||
|
* btp <pid> Kernel stack for <pid>
|
||||||
|
* btt <address-expression> Kernel stack for task structure at
|
||||||
|
* <address-expression>
|
||||||
|
* bta [DRSTCZEUIMA] All useful processes, optionally
|
||||||
|
* filtered by state
|
||||||
|
* btc [<cpu>] The current process on one cpu,
|
||||||
|
* default is all cpus
|
||||||
|
*
|
||||||
|
* bt <address-expression> refers to a address on the stack, that location
|
||||||
|
* is assumed to contain a return address.
|
||||||
|
*
|
||||||
|
* btt <address-expression> refers to the address of a struct task.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* argc argument count
|
||||||
|
* argv argument vector
|
||||||
|
* Outputs:
|
||||||
|
* None.
|
||||||
|
* Returns:
|
||||||
|
* zero for success, a kdb diagnostic if error
|
||||||
|
* Locking:
|
||||||
|
* none.
|
||||||
|
* Remarks:
|
||||||
|
* Backtrack works best when the code uses frame pointers. But even
|
||||||
|
* without frame pointers we should get a reasonable trace.
|
||||||
|
*
|
||||||
|
* mds comes in handy when examining the stack to do a manual traceback or
|
||||||
|
* to get a starting point for bt <address-expression>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
kdb_bt1(struct task_struct *p, unsigned long mask,
|
||||||
|
int argcount, int btaprompt)
|
||||||
|
{
|
||||||
|
char buffer[2];
|
||||||
|
if (kdb_getarea(buffer[0], (unsigned long)p) ||
|
||||||
|
kdb_getarea(buffer[0], (unsigned long)(p+1)-1))
|
||||||
|
return KDB_BADADDR;
|
||||||
|
if (!kdb_task_state(p, mask))
|
||||||
|
return 0;
|
||||||
|
kdb_printf("Stack traceback for pid %d\n", p->pid);
|
||||||
|
kdb_ps1(p);
|
||||||
|
kdb_show_stack(p, NULL);
|
||||||
|
if (btaprompt) {
|
||||||
|
kdb_getstr(buffer, sizeof(buffer),
|
||||||
|
"Enter <q> to end, <cr> to continue:");
|
||||||
|
if (buffer[0] == 'q') {
|
||||||
|
kdb_printf("\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
touch_nmi_watchdog();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
kdb_bt(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int diag;
|
||||||
|
int argcount = 5;
|
||||||
|
int btaprompt = 1;
|
||||||
|
int nextarg;
|
||||||
|
unsigned long addr;
|
||||||
|
long offset;
|
||||||
|
|
||||||
|
kdbgetintenv("BTARGS", &argcount); /* Arguments to print */
|
||||||
|
kdbgetintenv("BTAPROMPT", &btaprompt); /* Prompt after each
|
||||||
|
* proc in bta */
|
||||||
|
|
||||||
|
if (strcmp(argv[0], "bta") == 0) {
|
||||||
|
struct task_struct *g, *p;
|
||||||
|
unsigned long cpu;
|
||||||
|
unsigned long mask = kdb_task_state_string(argc ? argv[1] :
|
||||||
|
NULL);
|
||||||
|
if (argc == 0)
|
||||||
|
kdb_ps_suppressed();
|
||||||
|
/* Run the active tasks first */
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
p = kdb_curr_task(cpu);
|
||||||
|
if (kdb_bt1(p, mask, argcount, btaprompt))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Now the inactive tasks */
|
||||||
|
kdb_do_each_thread(g, p) {
|
||||||
|
if (task_curr(p))
|
||||||
|
continue;
|
||||||
|
if (kdb_bt1(p, mask, argcount, btaprompt))
|
||||||
|
return 0;
|
||||||
|
} kdb_while_each_thread(g, p);
|
||||||
|
} else if (strcmp(argv[0], "btp") == 0) {
|
||||||
|
struct task_struct *p;
|
||||||
|
unsigned long pid;
|
||||||
|
if (argc != 1)
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
diag = kdbgetularg((char *)argv[1], &pid);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
p = find_task_by_pid_ns(pid, &init_pid_ns);
|
||||||
|
if (p) {
|
||||||
|
kdb_set_current_task(p);
|
||||||
|
return kdb_bt1(p, ~0UL, argcount, 0);
|
||||||
|
}
|
||||||
|
kdb_printf("No process with pid == %ld found\n", pid);
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[0], "btt") == 0) {
|
||||||
|
if (argc != 1)
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
diag = kdbgetularg((char *)argv[1], &addr);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
kdb_set_current_task((struct task_struct *)addr);
|
||||||
|
return kdb_bt1((struct task_struct *)addr, ~0UL, argcount, 0);
|
||||||
|
} else if (strcmp(argv[0], "btc") == 0) {
|
||||||
|
unsigned long cpu = ~0;
|
||||||
|
struct task_struct *save_current_task = kdb_current_task;
|
||||||
|
char buf[80];
|
||||||
|
if (argc > 1)
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
if (argc == 1) {
|
||||||
|
diag = kdbgetularg((char *)argv[1], &cpu);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
}
|
||||||
|
/* Recursive use of kdb_parse, do not use argv after
|
||||||
|
* this point */
|
||||||
|
argv = NULL;
|
||||||
|
if (cpu != ~0) {
|
||||||
|
if (cpu >= num_possible_cpus() || !cpu_online(cpu)) {
|
||||||
|
kdb_printf("no process for cpu %ld\n", cpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sprintf(buf, "btt 0x%p\n", KDB_TSK(cpu));
|
||||||
|
kdb_parse(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
kdb_printf("btc: cpu status: ");
|
||||||
|
kdb_parse("cpu\n");
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
sprintf(buf, "btt 0x%p\n", KDB_TSK(cpu));
|
||||||
|
kdb_parse(buf);
|
||||||
|
touch_nmi_watchdog();
|
||||||
|
}
|
||||||
|
kdb_set_current_task(save_current_task);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if (argc) {
|
||||||
|
nextarg = 1;
|
||||||
|
diag = kdbgetaddrarg(argc, argv, &nextarg, &addr,
|
||||||
|
&offset, NULL);
|
||||||
|
if (diag)
|
||||||
|
return diag;
|
||||||
|
kdb_show_stack(kdb_current_task, (void *)addr);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return kdb_bt1(kdb_current_task, ~0UL, argcount, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTREACHED */
|
||||||
|
return 0;
|
||||||
|
}
|
35
kernel/debug/kdb/kdb_cmds
Normal file
35
kernel/debug/kdb/kdb_cmds
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Initial commands for kdb, alter to suit your needs.
|
||||||
|
# These commands are executed in kdb_init() context, no SMP, no
|
||||||
|
# processes. Commands that require process data (including stack or
|
||||||
|
# registers) are not reliable this early. set and bp commands should
|
||||||
|
# be safe. Global breakpoint commands affect each cpu as it is booted.
|
||||||
|
|
||||||
|
# Standard debugging information for first level support, just type archkdb
|
||||||
|
# or archkdbcpu or archkdbshort at the kdb prompt.
|
||||||
|
|
||||||
|
defcmd dumpcommon "" "Common kdb debugging"
|
||||||
|
set BTAPROMPT 0
|
||||||
|
set LINES 10000
|
||||||
|
-summary
|
||||||
|
-cpu
|
||||||
|
-ps
|
||||||
|
-dmesg 600
|
||||||
|
-bt
|
||||||
|
endefcmd
|
||||||
|
|
||||||
|
defcmd dumpall "" "First line debugging"
|
||||||
|
set BTSYMARG 1
|
||||||
|
set BTARGS 9
|
||||||
|
pid R
|
||||||
|
-dumpcommon
|
||||||
|
-bta
|
||||||
|
endefcmd
|
||||||
|
|
||||||
|
defcmd dumpcpu "" "Same as dumpall but only tasks on cpus"
|
||||||
|
set BTSYMARG 1
|
||||||
|
set BTARGS 9
|
||||||
|
pid R
|
||||||
|
-dumpcommon
|
||||||
|
-btc
|
||||||
|
endefcmd
|
||||||
|
|
169
kernel/debug/kdb/kdb_debugger.c
Normal file
169
kernel/debug/kdb/kdb_debugger.c
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Created by: Jason Wessel <jason.wessel@windriver.com>
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2. This program is licensed "as is" without any
|
||||||
|
* warranty of any kind, whether express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kgdb.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
#include <linux/kdebug.h>
|
||||||
|
#include "kdb_private.h"
|
||||||
|
#include "../debug_core.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KDB interface to KGDB internals
|
||||||
|
*/
|
||||||
|
get_char_func kdb_poll_funcs[] = {
|
||||||
|
dbg_io_get_char,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
EXPORT_SYMBOL_GPL(kdb_poll_funcs);
|
||||||
|
|
||||||
|
int kdb_poll_idx = 1;
|
||||||
|
EXPORT_SYMBOL_GPL(kdb_poll_idx);
|
||||||
|
|
||||||
|
int kdb_stub(struct kgdb_state *ks)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
kdb_bp_t *bp;
|
||||||
|
unsigned long addr = kgdb_arch_pc(ks->ex_vector, ks->linux_regs);
|
||||||
|
kdb_reason_t reason = KDB_REASON_OOPS;
|
||||||
|
kdb_dbtrap_t db_result = KDB_DB_NOBPT;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (KDB_STATE(REENTRY)) {
|
||||||
|
reason = KDB_REASON_SWITCH;
|
||||||
|
KDB_STATE_CLEAR(REENTRY);
|
||||||
|
addr = instruction_pointer(ks->linux_regs);
|
||||||
|
}
|
||||||
|
ks->pass_exception = 0;
|
||||||
|
if (atomic_read(&kgdb_setting_breakpoint))
|
||||||
|
reason = KDB_REASON_KEYBOARD;
|
||||||
|
|
||||||
|
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) {
|
||||||
|
if ((bp->bp_enabled) && (bp->bp_addr == addr)) {
|
||||||
|
reason = KDB_REASON_BREAK;
|
||||||
|
db_result = KDB_DB_BPT;
|
||||||
|
if (addr != instruction_pointer(ks->linux_regs))
|
||||||
|
kgdb_arch_set_pc(ks->linux_regs, addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reason == KDB_REASON_BREAK || reason == KDB_REASON_SWITCH) {
|
||||||
|
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) {
|
||||||
|
if (bp->bp_free)
|
||||||
|
continue;
|
||||||
|
if (bp->bp_addr == addr) {
|
||||||
|
bp->bp_delay = 1;
|
||||||
|
bp->bp_delayed = 1;
|
||||||
|
/*
|
||||||
|
* SSBPT is set when the kernel debugger must single step a
|
||||||
|
* task in order to re-establish an instruction breakpoint
|
||||||
|
* which uses the instruction replacement mechanism. It is
|
||||||
|
* cleared by any action that removes the need to single-step
|
||||||
|
* the breakpoint.
|
||||||
|
*/
|
||||||
|
reason = KDB_REASON_BREAK;
|
||||||
|
db_result = KDB_DB_BPT;
|
||||||
|
KDB_STATE_SET(SSBPT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason != KDB_REASON_BREAK && ks->ex_vector == 0 &&
|
||||||
|
ks->signo == SIGTRAP) {
|
||||||
|
reason = KDB_REASON_SSTEP;
|
||||||
|
db_result = KDB_DB_BPT;
|
||||||
|
}
|
||||||
|
/* Set initial kdb state variables */
|
||||||
|
KDB_STATE_CLEAR(KGDB_TRANS);
|
||||||
|
kdb_initial_cpu = ks->cpu;
|
||||||
|
kdb_current_task = kgdb_info[ks->cpu].task;
|
||||||
|
kdb_current_regs = kgdb_info[ks->cpu].debuggerinfo;
|
||||||
|
/* Remove any breakpoints as needed by kdb and clear single step */
|
||||||
|
kdb_bp_remove();
|
||||||
|
KDB_STATE_CLEAR(DOING_SS);
|
||||||
|
KDB_STATE_CLEAR(DOING_SSB);
|
||||||
|
KDB_STATE_SET(PAGER);
|
||||||
|
/* zero out any offline cpu data */
|
||||||
|
for_each_present_cpu(i) {
|
||||||
|
if (!cpu_online(i)) {
|
||||||
|
kgdb_info[i].debuggerinfo = NULL;
|
||||||
|
kgdb_info[i].task = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ks->err_code == DIE_OOPS || reason == KDB_REASON_OOPS) {
|
||||||
|
ks->pass_exception = 1;
|
||||||
|
KDB_FLAG_SET(CATASTROPHIC);
|
||||||
|
}
|
||||||
|
kdb_initial_cpu = ks->cpu;
|
||||||
|
if (KDB_STATE(SSBPT) && reason == KDB_REASON_SSTEP) {
|
||||||
|
KDB_STATE_CLEAR(SSBPT);
|
||||||
|
KDB_STATE_CLEAR(DOING_SS);
|
||||||
|
} else {
|
||||||
|
/* Start kdb main loop */
|
||||||
|
error = kdb_main_loop(KDB_REASON_ENTER, reason,
|
||||||
|
ks->err_code, db_result, ks->linux_regs);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Upon exit from the kdb main loop setup break points and restart
|
||||||
|
* the system based on the requested continue state
|
||||||
|
*/
|
||||||
|
kdb_initial_cpu = -1;
|
||||||
|
kdb_current_task = NULL;
|
||||||
|
kdb_current_regs = NULL;
|
||||||
|
KDB_STATE_CLEAR(PAGER);
|
||||||
|
kdbnearsym_cleanup();
|
||||||
|
if (error == KDB_CMD_KGDB) {
|
||||||
|
if (KDB_STATE(DOING_KGDB) || KDB_STATE(DOING_KGDB2)) {
|
||||||
|
/*
|
||||||
|
* This inteface glue which allows kdb to transition in into
|
||||||
|
* the gdb stub. In order to do this the '?' or '' gdb serial
|
||||||
|
* packet response is processed here. And then control is
|
||||||
|
* passed to the gdbstub.
|
||||||
|
*/
|
||||||
|
if (KDB_STATE(DOING_KGDB))
|
||||||
|
gdbstub_state(ks, "?");
|
||||||
|
else
|
||||||
|
gdbstub_state(ks, "");
|
||||||
|
KDB_STATE_CLEAR(DOING_KGDB);
|
||||||
|
KDB_STATE_CLEAR(DOING_KGDB2);
|
||||||
|
}
|
||||||
|
return DBG_PASS_EVENT;
|
||||||
|
}
|
||||||
|
kdb_bp_install(ks->linux_regs);
|
||||||
|
dbg_activate_sw_breakpoints();
|
||||||
|
/* Set the exit state to a single step or a continue */
|
||||||
|
if (KDB_STATE(DOING_SS))
|
||||||
|
gdbstub_state(ks, "s");
|
||||||
|
else
|
||||||
|
gdbstub_state(ks, "c");
|
||||||
|
|
||||||
|
KDB_FLAG_CLEAR(CATASTROPHIC);
|
||||||
|
|
||||||
|
/* Invoke arch specific exception handling prior to system resume */
|
||||||
|
kgdb_info[ks->cpu].ret_state = gdbstub_state(ks, "e");
|
||||||
|
if (ks->pass_exception)
|
||||||
|
kgdb_info[ks->cpu].ret_state = 1;
|
||||||
|
if (error == KDB_CMD_CPU) {
|
||||||
|
KDB_STATE_SET(REENTRY);
|
||||||
|
/*
|
||||||
|
* Force clear the single step bit because kdb emulates this
|
||||||
|
* differently vs the gdbstub
|
||||||
|
*/
|
||||||
|
kgdb_single_step = 0;
|
||||||
|
dbg_deactivate_sw_breakpoints();
|
||||||
|
return DBG_SWITCH_CPU_EVENT;
|
||||||
|
}
|
||||||
|
return kgdb_info[ks->cpu].ret_state;
|
||||||
|
}
|
||||||
|
|
826
kernel/debug/kdb/kdb_io.c
Normal file
826
kernel/debug/kdb/kdb_io.c
Normal file
|
@ -0,0 +1,826 @@
|
||||||
|
/*
|
||||||
|
* Kernel Debugger Architecture Independent Console I/O handler
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kdev_t.h>
|
||||||
|
#include <linux/console.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/smp.h>
|
||||||
|
#include <linux/nmi.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/kgdb.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
#include <linux/kallsyms.h>
|
||||||
|
#include "kdb_private.h"
|
||||||
|
|
||||||
|
#define CMD_BUFLEN 256
|
||||||
|
char kdb_prompt_str[CMD_BUFLEN];
|
||||||
|
|
||||||
|
int kdb_trap_printk;
|
||||||
|
|
||||||
|
static void kgdb_transition_check(char *buffer)
|
||||||
|
{
|
||||||
|
int slen = strlen(buffer);
|
||||||
|
if (strncmp(buffer, "$?#3f", slen) != 0 &&
|
||||||
|
strncmp(buffer, "$qSupported#37", slen) != 0 &&
|
||||||
|
strncmp(buffer, "+$qSupported#37", slen) != 0) {
|
||||||
|
KDB_STATE_SET(KGDB_TRANS);
|
||||||
|
kdb_printf("%s", buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kdb_read_get_key(char *buffer, size_t bufsize)
|
||||||
|
{
|
||||||
|
#define ESCAPE_UDELAY 1000
|
||||||
|
#define ESCAPE_DELAY (2*1000000/ESCAPE_UDELAY) /* 2 seconds worth of udelays */
|
||||||
|
char escape_data[5]; /* longest vt100 escape sequence is 4 bytes */
|
||||||
|
char *ped = escape_data;
|
||||||
|
int escape_delay = 0;
|
||||||
|
get_char_func *f, *f_escape = NULL;
|
||||||
|
int key;
|
||||||
|
|
||||||
|
for (f = &kdb_poll_funcs[0]; ; ++f) {
|
||||||
|
if (*f == NULL) {
|
||||||
|
/* Reset NMI watchdog once per poll loop */
|
||||||
|
touch_nmi_watchdog();
|
||||||
|
f = &kdb_poll_funcs[0];
|
||||||
|
}
|
||||||
|
if (escape_delay == 2) {
|
||||||
|
*ped = '\0';
|
||||||
|
ped = escape_data;
|
||||||
|
--escape_delay;
|
||||||
|
}
|
||||||
|
if (escape_delay == 1) {
|
||||||
|
key = *ped++;
|
||||||
|
if (!*ped)
|
||||||
|
--escape_delay;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
key = (*f)();
|
||||||
|
if (key == -1) {
|
||||||
|
if (escape_delay) {
|
||||||
|
udelay(ESCAPE_UDELAY);
|
||||||
|
--escape_delay;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (bufsize <= 2) {
|
||||||
|
if (key == '\r')
|
||||||
|
key = '\n';
|
||||||
|
*buffer++ = key;
|
||||||
|
*buffer = '\0';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (escape_delay == 0 && key == '\e') {
|
||||||
|
escape_delay = ESCAPE_DELAY;
|
||||||
|
ped = escape_data;
|
||||||
|
f_escape = f;
|
||||||
|
}
|
||||||
|
if (escape_delay) {
|
||||||
|
*ped++ = key;
|
||||||
|
if (f_escape != f) {
|
||||||
|
escape_delay = 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ped - escape_data == 1) {
|
||||||
|
/* \e */
|
||||||
|
continue;
|
||||||
|
} else if (ped - escape_data == 2) {
|
||||||
|
/* \e<something> */
|
||||||
|
if (key != '[')
|
||||||
|
escape_delay = 2;
|
||||||
|
continue;
|
||||||
|
} else if (ped - escape_data == 3) {
|
||||||
|
/* \e[<something> */
|
||||||
|
int mapkey = 0;
|
||||||
|
switch (key) {
|
||||||
|
case 'A': /* \e[A, up arrow */
|
||||||
|
mapkey = 16;
|
||||||
|
break;
|
||||||
|
case 'B': /* \e[B, down arrow */
|
||||||
|
mapkey = 14;
|
||||||
|
break;
|
||||||
|
case 'C': /* \e[C, right arrow */
|
||||||
|
mapkey = 6;
|
||||||
|
break;
|
||||||
|
case 'D': /* \e[D, left arrow */
|
||||||
|
mapkey = 2;
|
||||||
|
break;
|
||||||
|
case '1': /* dropthrough */
|
||||||
|
case '3': /* dropthrough */
|
||||||
|
/* \e[<1,3,4>], may be home, del, end */
|
||||||
|
case '4':
|
||||||
|
mapkey = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mapkey != -1) {
|
||||||
|
if (mapkey > 0) {
|
||||||
|
escape_data[0] = mapkey;
|
||||||
|
escape_data[1] = '\0';
|
||||||
|
}
|
||||||
|
escape_delay = 2;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (ped - escape_data == 4) {
|
||||||
|
/* \e[<1,3,4><something> */
|
||||||
|
int mapkey = 0;
|
||||||
|
if (key == '~') {
|
||||||
|
switch (escape_data[2]) {
|
||||||
|
case '1': /* \e[1~, home */
|
||||||
|
mapkey = 1;
|
||||||
|
break;
|
||||||
|
case '3': /* \e[3~, del */
|
||||||
|
mapkey = 4;
|
||||||
|
break;
|
||||||
|
case '4': /* \e[4~, end */
|
||||||
|
mapkey = 5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mapkey > 0) {
|
||||||
|
escape_data[0] = mapkey;
|
||||||
|
escape_data[1] = '\0';
|
||||||
|
}
|
||||||
|
escape_delay = 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break; /* A key to process */
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_read
|
||||||
|
*
|
||||||
|
* This function reads a string of characters, terminated by
|
||||||
|
* a newline, or by reaching the end of the supplied buffer,
|
||||||
|
* from the current kernel debugger console device.
|
||||||
|
* Parameters:
|
||||||
|
* buffer - Address of character buffer to receive input characters.
|
||||||
|
* bufsize - size, in bytes, of the character buffer
|
||||||
|
* Returns:
|
||||||
|
* Returns a pointer to the buffer containing the received
|
||||||
|
* character string. This string will be terminated by a
|
||||||
|
* newline character.
|
||||||
|
* Locking:
|
||||||
|
* No locks are required to be held upon entry to this
|
||||||
|
* function. It is not reentrant - it relies on the fact
|
||||||
|
* that while kdb is running on only one "master debug" cpu.
|
||||||
|
* Remarks:
|
||||||
|
*
|
||||||
|
* The buffer size must be >= 2. A buffer size of 2 means that the caller only
|
||||||
|
* wants a single key.
|
||||||
|
*
|
||||||
|
* An escape key could be the start of a vt100 control sequence such as \e[D
|
||||||
|
* (left arrow) or it could be a character in its own right. The standard
|
||||||
|
* method for detecting the difference is to wait for 2 seconds to see if there
|
||||||
|
* are any other characters. kdb is complicated by the lack of a timer service
|
||||||
|
* (interrupts are off), by multiple input sources and by the need to sometimes
|
||||||
|
* return after just one key. Escape sequence processing has to be done as
|
||||||
|
* states in the polling loop.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *kdb_read(char *buffer, size_t bufsize)
|
||||||
|
{
|
||||||
|
char *cp = buffer;
|
||||||
|
char *bufend = buffer+bufsize-2; /* Reserve space for newline
|
||||||
|
* and null byte */
|
||||||
|
char *lastchar;
|
||||||
|
char *p_tmp;
|
||||||
|
char tmp;
|
||||||
|
static char tmpbuffer[CMD_BUFLEN];
|
||||||
|
int len = strlen(buffer);
|
||||||
|
int len_tmp;
|
||||||
|
int tab = 0;
|
||||||
|
int count;
|
||||||
|
int i;
|
||||||
|
int diag, dtab_count;
|
||||||
|
int key;
|
||||||
|
|
||||||
|
|
||||||
|
diag = kdbgetintenv("DTABCOUNT", &dtab_count);
|
||||||
|
if (diag)
|
||||||
|
dtab_count = 30;
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
cp += len;
|
||||||
|
if (*(buffer+len-1) == '\n')
|
||||||
|
cp--;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastchar = cp;
|
||||||
|
*cp = '\0';
|
||||||
|
kdb_printf("%s", buffer);
|
||||||
|
poll_again:
|
||||||
|
key = kdb_read_get_key(buffer, bufsize);
|
||||||
|
if (key == -1)
|
||||||
|
return buffer;
|
||||||
|
if (key != 9)
|
||||||
|
tab = 0;
|
||||||
|
switch (key) {
|
||||||
|
case 8: /* backspace */
|
||||||
|
if (cp > buffer) {
|
||||||
|
if (cp < lastchar) {
|
||||||
|
memcpy(tmpbuffer, cp, lastchar - cp);
|
||||||
|
memcpy(cp-1, tmpbuffer, lastchar - cp);
|
||||||
|
}
|
||||||
|
*(--lastchar) = '\0';
|
||||||
|
--cp;
|
||||||
|
kdb_printf("\b%s \r", cp);
|
||||||
|
tmp = *cp;
|
||||||
|
*cp = '\0';
|
||||||
|
kdb_printf(kdb_prompt_str);
|
||||||
|
kdb_printf("%s", buffer);
|
||||||
|
*cp = tmp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 13: /* enter */
|
||||||
|
*lastchar++ = '\n';
|
||||||
|
*lastchar++ = '\0';
|
||||||
|
kdb_printf("\n");
|
||||||
|
return buffer;
|
||||||
|
case 4: /* Del */
|
||||||
|
if (cp < lastchar) {
|
||||||
|
memcpy(tmpbuffer, cp+1, lastchar - cp - 1);
|
||||||
|
memcpy(cp, tmpbuffer, lastchar - cp - 1);
|
||||||
|
*(--lastchar) = '\0';
|
||||||
|
kdb_printf("%s \r", cp);
|
||||||
|
tmp = *cp;
|
||||||
|
*cp = '\0';
|
||||||
|
kdb_printf(kdb_prompt_str);
|
||||||
|
kdb_printf("%s", buffer);
|
||||||
|
*cp = tmp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: /* Home */
|
||||||
|
if (cp > buffer) {
|
||||||
|
kdb_printf("\r");
|
||||||
|
kdb_printf(kdb_prompt_str);
|
||||||
|
cp = buffer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5: /* End */
|
||||||
|
if (cp < lastchar) {
|
||||||
|
kdb_printf("%s", cp);
|
||||||
|
cp = lastchar;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: /* Left */
|
||||||
|
if (cp > buffer) {
|
||||||
|
kdb_printf("\b");
|
||||||
|
--cp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 14: /* Down */
|
||||||
|
memset(tmpbuffer, ' ',
|
||||||
|
strlen(kdb_prompt_str) + (lastchar-buffer));
|
||||||
|
*(tmpbuffer+strlen(kdb_prompt_str) +
|
||||||
|
(lastchar-buffer)) = '\0';
|
||||||
|
kdb_printf("\r%s\r", tmpbuffer);
|
||||||
|
*lastchar = (char)key;
|
||||||
|
*(lastchar+1) = '\0';
|
||||||
|
return lastchar;
|
||||||
|
case 6: /* Right */
|
||||||
|
if (cp < lastchar) {
|
||||||
|
kdb_printf("%c", *cp);
|
||||||
|
++cp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 16: /* Up */
|
||||||
|
memset(tmpbuffer, ' ',
|
||||||
|
strlen(kdb_prompt_str) + (lastchar-buffer));
|
||||||
|
*(tmpbuffer+strlen(kdb_prompt_str) +
|
||||||
|
(lastchar-buffer)) = '\0';
|
||||||
|
kdb_printf("\r%s\r", tmpbuffer);
|
||||||
|
*lastchar = (char)key;
|
||||||
|
*(lastchar+1) = '\0';
|
||||||
|
return lastchar;
|
||||||
|
case 9: /* Tab */
|
||||||
|
if (tab < 2)
|
||||||
|
++tab;
|
||||||
|
p_tmp = buffer;
|
||||||
|
while (*p_tmp == ' ')
|
||||||
|
p_tmp++;
|
||||||
|
if (p_tmp > cp)
|
||||||
|
break;
|
||||||
|
memcpy(tmpbuffer, p_tmp, cp-p_tmp);
|
||||||
|
*(tmpbuffer + (cp-p_tmp)) = '\0';
|
||||||
|
p_tmp = strrchr(tmpbuffer, ' ');
|
||||||
|
if (p_tmp)
|
||||||
|
++p_tmp;
|
||||||
|
else
|
||||||
|
p_tmp = tmpbuffer;
|
||||||
|
len = strlen(p_tmp);
|
||||||
|
count = kallsyms_symbol_complete(p_tmp,
|
||||||
|
sizeof(tmpbuffer) -
|
||||||
|
(p_tmp - tmpbuffer));
|
||||||
|
if (tab == 2 && count > 0) {
|
||||||
|
kdb_printf("\n%d symbols are found.", count);
|
||||||
|
if (count > dtab_count) {
|
||||||
|
count = dtab_count;
|
||||||
|
kdb_printf(" But only first %d symbols will"
|
||||||
|
" be printed.\nYou can change the"
|
||||||
|
" environment variable DTABCOUNT.",
|
||||||
|
count);
|
||||||
|
}
|
||||||
|
kdb_printf("\n");
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (kallsyms_symbol_next(p_tmp, i) < 0)
|
||||||
|
break;
|
||||||
|
kdb_printf("%s ", p_tmp);
|
||||||
|
*(p_tmp + len) = '\0';
|
||||||
|
}
|
||||||
|
if (i >= dtab_count)
|
||||||
|
kdb_printf("...");
|
||||||
|
kdb_printf("\n");
|
||||||
|
kdb_printf(kdb_prompt_str);
|
||||||
|
kdb_printf("%s", buffer);
|
||||||
|
} else if (tab != 2 && count > 0) {
|
||||||
|
len_tmp = strlen(p_tmp);
|
||||||
|
strncpy(p_tmp+len_tmp, cp, lastchar-cp+1);
|
||||||
|
len_tmp = strlen(p_tmp);
|
||||||
|
strncpy(cp, p_tmp+len, len_tmp-len + 1);
|
||||||
|
len = len_tmp - len;
|
||||||
|
kdb_printf("%s", cp);
|
||||||
|
cp += len;
|
||||||
|
lastchar += len;
|
||||||
|
}
|
||||||
|
kdb_nextline = 1; /* reset output line number */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (key >= 32 && lastchar < bufend) {
|
||||||
|
if (cp < lastchar) {
|
||||||
|
memcpy(tmpbuffer, cp, lastchar - cp);
|
||||||
|
memcpy(cp+1, tmpbuffer, lastchar - cp);
|
||||||
|
*++lastchar = '\0';
|
||||||
|
*cp = key;
|
||||||
|
kdb_printf("%s\r", cp);
|
||||||
|
++cp;
|
||||||
|
tmp = *cp;
|
||||||
|
*cp = '\0';
|
||||||
|
kdb_printf(kdb_prompt_str);
|
||||||
|
kdb_printf("%s", buffer);
|
||||||
|
*cp = tmp;
|
||||||
|
} else {
|
||||||
|
*++lastchar = '\0';
|
||||||
|
*cp++ = key;
|
||||||
|
/* The kgdb transition check will hide
|
||||||
|
* printed characters if we think that
|
||||||
|
* kgdb is connecting, until the check
|
||||||
|
* fails */
|
||||||
|
if (!KDB_STATE(KGDB_TRANS))
|
||||||
|
kgdb_transition_check(buffer);
|
||||||
|
else
|
||||||
|
kdb_printf("%c", key);
|
||||||
|
}
|
||||||
|
/* Special escape to kgdb */
|
||||||
|
if (lastchar - buffer >= 5 &&
|
||||||
|
strcmp(lastchar - 5, "$?#3f") == 0) {
|
||||||
|
strcpy(buffer, "kgdb");
|
||||||
|
KDB_STATE_SET(DOING_KGDB);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
if (lastchar - buffer >= 14 &&
|
||||||
|
strcmp(lastchar - 14, "$qSupported#37") == 0) {
|
||||||
|
strcpy(buffer, "kgdb");
|
||||||
|
KDB_STATE_SET(DOING_KGDB2);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
goto poll_again;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_getstr
|
||||||
|
*
|
||||||
|
* Print the prompt string and read a command from the
|
||||||
|
* input device.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* buffer Address of buffer to receive command
|
||||||
|
* bufsize Size of buffer in bytes
|
||||||
|
* prompt Pointer to string to use as prompt string
|
||||||
|
* Returns:
|
||||||
|
* Pointer to command buffer.
|
||||||
|
* Locking:
|
||||||
|
* None.
|
||||||
|
* Remarks:
|
||||||
|
* For SMP kernels, the processor number will be
|
||||||
|
* substituted for %d, %x or %o in the prompt.
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *kdb_getstr(char *buffer, size_t bufsize, char *prompt)
|
||||||
|
{
|
||||||
|
if (prompt && kdb_prompt_str != prompt)
|
||||||
|
strncpy(kdb_prompt_str, prompt, CMD_BUFLEN);
|
||||||
|
kdb_printf(kdb_prompt_str);
|
||||||
|
kdb_nextline = 1; /* Prompt and input resets line number */
|
||||||
|
return kdb_read(buffer, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_input_flush
|
||||||
|
*
|
||||||
|
* Get rid of any buffered console input.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* none
|
||||||
|
* Returns:
|
||||||
|
* nothing
|
||||||
|
* Locking:
|
||||||
|
* none
|
||||||
|
* Remarks:
|
||||||
|
* Call this function whenever you want to flush input. If there is any
|
||||||
|
* outstanding input, it ignores all characters until there has been no
|
||||||
|
* data for approximately 1ms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void kdb_input_flush(void)
|
||||||
|
{
|
||||||
|
get_char_func *f;
|
||||||
|
int res;
|
||||||
|
int flush_delay = 1;
|
||||||
|
while (flush_delay) {
|
||||||
|
flush_delay--;
|
||||||
|
empty:
|
||||||
|
touch_nmi_watchdog();
|
||||||
|
for (f = &kdb_poll_funcs[0]; *f; ++f) {
|
||||||
|
res = (*f)();
|
||||||
|
if (res != -1) {
|
||||||
|
flush_delay = 1;
|
||||||
|
goto empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flush_delay)
|
||||||
|
mdelay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_printf
|
||||||
|
*
|
||||||
|
* Print a string to the output device(s).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* printf-like format and optional args.
|
||||||
|
* Returns:
|
||||||
|
* 0
|
||||||
|
* Locking:
|
||||||
|
* None.
|
||||||
|
* Remarks:
|
||||||
|
* use 'kdbcons->write()' to avoid polluting 'log_buf' with
|
||||||
|
* kdb output.
|
||||||
|
*
|
||||||
|
* If the user is doing a cmd args | grep srch
|
||||||
|
* then kdb_grepping_flag is set.
|
||||||
|
* In that case we need to accumulate full lines (ending in \n) before
|
||||||
|
* searching for the pattern.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char kdb_buffer[256]; /* A bit too big to go on stack */
|
||||||
|
static char *next_avail = kdb_buffer;
|
||||||
|
static int size_avail;
|
||||||
|
static int suspend_grep;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* search arg1 to see if it contains arg2
|
||||||
|
* (kdmain.c provides flags for ^pat and pat$)
|
||||||
|
*
|
||||||
|
* return 1 for found, 0 for not found
|
||||||
|
*/
|
||||||
|
static int kdb_search_string(char *searched, char *searchfor)
|
||||||
|
{
|
||||||
|
char firstchar, *cp;
|
||||||
|
int len1, len2;
|
||||||
|
|
||||||
|
/* not counting the newline at the end of "searched" */
|
||||||
|
len1 = strlen(searched)-1;
|
||||||
|
len2 = strlen(searchfor);
|
||||||
|
if (len1 < len2)
|
||||||
|
return 0;
|
||||||
|
if (kdb_grep_leading && kdb_grep_trailing && len1 != len2)
|
||||||
|
return 0;
|
||||||
|
if (kdb_grep_leading) {
|
||||||
|
if (!strncmp(searched, searchfor, len2))
|
||||||
|
return 1;
|
||||||
|
} else if (kdb_grep_trailing) {
|
||||||
|
if (!strncmp(searched+len1-len2, searchfor, len2))
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
firstchar = *searchfor;
|
||||||
|
cp = searched;
|
||||||
|
while ((cp = strchr(cp, firstchar))) {
|
||||||
|
if (!strncmp(cp, searchfor, len2))
|
||||||
|
return 1;
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vkdb_printf(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
int diag;
|
||||||
|
int linecount;
|
||||||
|
int logging, saved_loglevel = 0;
|
||||||
|
int saved_trap_printk;
|
||||||
|
int got_printf_lock = 0;
|
||||||
|
int retlen = 0;
|
||||||
|
int fnd, len;
|
||||||
|
char *cp, *cp2, *cphold = NULL, replaced_byte = ' ';
|
||||||
|
char *moreprompt = "more> ";
|
||||||
|
struct console *c = console_drivers;
|
||||||
|
static DEFINE_SPINLOCK(kdb_printf_lock);
|
||||||
|
unsigned long uninitialized_var(flags);
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
saved_trap_printk = kdb_trap_printk;
|
||||||
|
kdb_trap_printk = 0;
|
||||||
|
|
||||||
|
/* Serialize kdb_printf if multiple cpus try to write at once.
|
||||||
|
* But if any cpu goes recursive in kdb, just print the output,
|
||||||
|
* even if it is interleaved with any other text.
|
||||||
|
*/
|
||||||
|
if (!KDB_STATE(PRINTF_LOCK)) {
|
||||||
|
KDB_STATE_SET(PRINTF_LOCK);
|
||||||
|
spin_lock_irqsave(&kdb_printf_lock, flags);
|
||||||
|
got_printf_lock = 1;
|
||||||
|
atomic_inc(&kdb_event);
|
||||||
|
} else {
|
||||||
|
__acquire(kdb_printf_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
diag = kdbgetintenv("LINES", &linecount);
|
||||||
|
if (diag || linecount <= 1)
|
||||||
|
linecount = 24;
|
||||||
|
|
||||||
|
diag = kdbgetintenv("LOGGING", &logging);
|
||||||
|
if (diag)
|
||||||
|
logging = 0;
|
||||||
|
|
||||||
|
if (!kdb_grepping_flag || suspend_grep) {
|
||||||
|
/* normally, every vsnprintf starts a new buffer */
|
||||||
|
next_avail = kdb_buffer;
|
||||||
|
size_avail = sizeof(kdb_buffer);
|
||||||
|
}
|
||||||
|
vsnprintf(next_avail, size_avail, fmt, ap);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If kdb_parse() found that the command was cmd xxx | grep yyy
|
||||||
|
* then kdb_grepping_flag is set, and kdb_grep_string contains yyy
|
||||||
|
*
|
||||||
|
* Accumulate the print data up to a newline before searching it.
|
||||||
|
* (vsnprintf does null-terminate the string that it generates)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* skip the search if prints are temporarily unconditional */
|
||||||
|
if (!suspend_grep && kdb_grepping_flag) {
|
||||||
|
cp = strchr(kdb_buffer, '\n');
|
||||||
|
if (!cp) {
|
||||||
|
/*
|
||||||
|
* Special cases that don't end with newlines
|
||||||
|
* but should be written without one:
|
||||||
|
* The "[nn]kdb> " prompt should
|
||||||
|
* appear at the front of the buffer.
|
||||||
|
*
|
||||||
|
* The "[nn]more " prompt should also be
|
||||||
|
* (MOREPROMPT -> moreprompt)
|
||||||
|
* written * but we print that ourselves,
|
||||||
|
* we set the suspend_grep flag to make
|
||||||
|
* it unconditional.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (next_avail == kdb_buffer) {
|
||||||
|
/*
|
||||||
|
* these should occur after a newline,
|
||||||
|
* so they will be at the front of the
|
||||||
|
* buffer
|
||||||
|
*/
|
||||||
|
cp2 = kdb_buffer;
|
||||||
|
len = strlen(kdb_prompt_str);
|
||||||
|
if (!strncmp(cp2, kdb_prompt_str, len)) {
|
||||||
|
/*
|
||||||
|
* We're about to start a new
|
||||||
|
* command, so we can go back
|
||||||
|
* to normal mode.
|
||||||
|
*/
|
||||||
|
kdb_grepping_flag = 0;
|
||||||
|
goto kdb_printit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* no newline; don't search/write the buffer
|
||||||
|
until one is there */
|
||||||
|
len = strlen(kdb_buffer);
|
||||||
|
next_avail = kdb_buffer + len;
|
||||||
|
size_avail = sizeof(kdb_buffer) - len;
|
||||||
|
goto kdb_print_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The newline is present; print through it or discard
|
||||||
|
* it, depending on the results of the search.
|
||||||
|
*/
|
||||||
|
cp++; /* to byte after the newline */
|
||||||
|
replaced_byte = *cp; /* remember what/where it was */
|
||||||
|
cphold = cp;
|
||||||
|
*cp = '\0'; /* end the string for our search */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We now have a newline at the end of the string
|
||||||
|
* Only continue with this output if it contains the
|
||||||
|
* search string.
|
||||||
|
*/
|
||||||
|
fnd = kdb_search_string(kdb_buffer, kdb_grep_string);
|
||||||
|
if (!fnd) {
|
||||||
|
/*
|
||||||
|
* At this point the complete line at the start
|
||||||
|
* of kdb_buffer can be discarded, as it does
|
||||||
|
* not contain what the user is looking for.
|
||||||
|
* Shift the buffer left.
|
||||||
|
*/
|
||||||
|
*cphold = replaced_byte;
|
||||||
|
strcpy(kdb_buffer, cphold);
|
||||||
|
len = strlen(kdb_buffer);
|
||||||
|
next_avail = kdb_buffer + len;
|
||||||
|
size_avail = sizeof(kdb_buffer) - len;
|
||||||
|
goto kdb_print_out;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* at this point the string is a full line and
|
||||||
|
* should be printed, up to the null.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
kdb_printit:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write to all consoles.
|
||||||
|
*/
|
||||||
|
retlen = strlen(kdb_buffer);
|
||||||
|
if (!dbg_kdb_mode && kgdb_connected) {
|
||||||
|
gdbstub_msg_write(kdb_buffer, retlen);
|
||||||
|
} else {
|
||||||
|
if (!dbg_io_ops->is_console) {
|
||||||
|
len = strlen(kdb_buffer);
|
||||||
|
cp = kdb_buffer;
|
||||||
|
while (len--) {
|
||||||
|
dbg_io_ops->write_char(*cp);
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (c) {
|
||||||
|
c->write(c, kdb_buffer, retlen);
|
||||||
|
touch_nmi_watchdog();
|
||||||
|
c = c->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (logging) {
|
||||||
|
saved_loglevel = console_loglevel;
|
||||||
|
console_loglevel = 0;
|
||||||
|
printk(KERN_INFO "%s", kdb_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KDB_STATE(PAGER) && strchr(kdb_buffer, '\n'))
|
||||||
|
kdb_nextline++;
|
||||||
|
|
||||||
|
/* check for having reached the LINES number of printed lines */
|
||||||
|
if (kdb_nextline == linecount) {
|
||||||
|
char buf1[16] = "";
|
||||||
|
#if defined(CONFIG_SMP)
|
||||||
|
char buf2[32];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Watch out for recursion here. Any routine that calls
|
||||||
|
* kdb_printf will come back through here. And kdb_read
|
||||||
|
* uses kdb_printf to echo on serial consoles ...
|
||||||
|
*/
|
||||||
|
kdb_nextline = 1; /* In case of recursion */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pause until cr.
|
||||||
|
*/
|
||||||
|
moreprompt = kdbgetenv("MOREPROMPT");
|
||||||
|
if (moreprompt == NULL)
|
||||||
|
moreprompt = "more> ";
|
||||||
|
|
||||||
|
#if defined(CONFIG_SMP)
|
||||||
|
if (strchr(moreprompt, '%')) {
|
||||||
|
sprintf(buf2, moreprompt, get_cpu());
|
||||||
|
put_cpu();
|
||||||
|
moreprompt = buf2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
kdb_input_flush();
|
||||||
|
c = console_drivers;
|
||||||
|
|
||||||
|
if (!dbg_io_ops->is_console) {
|
||||||
|
len = strlen(moreprompt);
|
||||||
|
cp = moreprompt;
|
||||||
|
while (len--) {
|
||||||
|
dbg_io_ops->write_char(*cp);
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (c) {
|
||||||
|
c->write(c, moreprompt, strlen(moreprompt));
|
||||||
|
touch_nmi_watchdog();
|
||||||
|
c = c->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logging)
|
||||||
|
printk("%s", moreprompt);
|
||||||
|
|
||||||
|
kdb_read(buf1, 2); /* '2' indicates to return
|
||||||
|
* immediately after getting one key. */
|
||||||
|
kdb_nextline = 1; /* Really set output line 1 */
|
||||||
|
|
||||||
|
/* empty and reset the buffer: */
|
||||||
|
kdb_buffer[0] = '\0';
|
||||||
|
next_avail = kdb_buffer;
|
||||||
|
size_avail = sizeof(kdb_buffer);
|
||||||
|
if ((buf1[0] == 'q') || (buf1[0] == 'Q')) {
|
||||||
|
/* user hit q or Q */
|
||||||
|
KDB_FLAG_SET(CMD_INTERRUPT); /* command interrupted */
|
||||||
|
KDB_STATE_CLEAR(PAGER);
|
||||||
|
/* end of command output; back to normal mode */
|
||||||
|
kdb_grepping_flag = 0;
|
||||||
|
kdb_printf("\n");
|
||||||
|
} else if (buf1[0] == ' ') {
|
||||||
|
kdb_printf("\n");
|
||||||
|
suspend_grep = 1; /* for this recursion */
|
||||||
|
} else if (buf1[0] == '\n') {
|
||||||
|
kdb_nextline = linecount - 1;
|
||||||
|
kdb_printf("\r");
|
||||||
|
suspend_grep = 1; /* for this recursion */
|
||||||
|
} else if (buf1[0] && buf1[0] != '\n') {
|
||||||
|
/* user hit something other than enter */
|
||||||
|
suspend_grep = 1; /* for this recursion */
|
||||||
|
kdb_printf("\nOnly 'q' or 'Q' are processed at more "
|
||||||
|
"prompt, input ignored\n");
|
||||||
|
} else if (kdb_grepping_flag) {
|
||||||
|
/* user hit enter */
|
||||||
|
suspend_grep = 1; /* for this recursion */
|
||||||
|
kdb_printf("\n");
|
||||||
|
}
|
||||||
|
kdb_input_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For grep searches, shift the printed string left.
|
||||||
|
* replaced_byte contains the character that was overwritten with
|
||||||
|
* the terminating null, and cphold points to the null.
|
||||||
|
* Then adjust the notion of available space in the buffer.
|
||||||
|
*/
|
||||||
|
if (kdb_grepping_flag && !suspend_grep) {
|
||||||
|
*cphold = replaced_byte;
|
||||||
|
strcpy(kdb_buffer, cphold);
|
||||||
|
len = strlen(kdb_buffer);
|
||||||
|
next_avail = kdb_buffer + len;
|
||||||
|
size_avail = sizeof(kdb_buffer) - len;
|
||||||
|
}
|
||||||
|
|
||||||
|
kdb_print_out:
|
||||||
|
suspend_grep = 0; /* end of what may have been a recursive call */
|
||||||
|
if (logging)
|
||||||
|
console_loglevel = saved_loglevel;
|
||||||
|
if (KDB_STATE(PRINTF_LOCK) && got_printf_lock) {
|
||||||
|
got_printf_lock = 0;
|
||||||
|
spin_unlock_irqrestore(&kdb_printf_lock, flags);
|
||||||
|
KDB_STATE_CLEAR(PRINTF_LOCK);
|
||||||
|
atomic_dec(&kdb_event);
|
||||||
|
} else {
|
||||||
|
__release(kdb_printf_lock);
|
||||||
|
}
|
||||||
|
kdb_trap_printk = saved_trap_printk;
|
||||||
|
preempt_enable();
|
||||||
|
return retlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kdb_printf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
r = vkdb_printf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
212
kernel/debug/kdb/kdb_keyboard.c
Normal file
212
kernel/debug/kdb/kdb_keyboard.c
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Kernel Debugger Architecture Dependent Console I/O handler
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
#include <linux/keyboard.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
/* Keyboard Controller Registers on normal PCs. */
|
||||||
|
|
||||||
|
#define KBD_STATUS_REG 0x64 /* Status register (R) */
|
||||||
|
#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */
|
||||||
|
|
||||||
|
/* Status Register Bits */
|
||||||
|
|
||||||
|
#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
|
||||||
|
#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
|
||||||
|
|
||||||
|
static int kbd_exists;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the keyboard controller has a keypress for us.
|
||||||
|
* Some parts (Enter Release, LED change) are still blocking polled here,
|
||||||
|
* but hopefully they are all short.
|
||||||
|
*/
|
||||||
|
int kdb_get_kbd_char(void)
|
||||||
|
{
|
||||||
|
int scancode, scanstatus;
|
||||||
|
static int shift_lock; /* CAPS LOCK state (0-off, 1-on) */
|
||||||
|
static int shift_key; /* Shift next keypress */
|
||||||
|
static int ctrl_key;
|
||||||
|
u_short keychar;
|
||||||
|
|
||||||
|
if (KDB_FLAG(NO_I8042) || KDB_FLAG(NO_VT_CONSOLE) ||
|
||||||
|
(inb(KBD_STATUS_REG) == 0xff && inb(KBD_DATA_REG) == 0xff)) {
|
||||||
|
kbd_exists = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
kbd_exists = 1;
|
||||||
|
|
||||||
|
if ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the scancode
|
||||||
|
*/
|
||||||
|
scancode = inb(KBD_DATA_REG);
|
||||||
|
scanstatus = inb(KBD_STATUS_REG);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore mouse events.
|
||||||
|
*/
|
||||||
|
if (scanstatus & KBD_STAT_MOUSE_OBF)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore release, trigger on make
|
||||||
|
* (except for shift keys, where we want to
|
||||||
|
* keep the shift state so long as the key is
|
||||||
|
* held down).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (((scancode&0x7f) == 0x2a) || ((scancode&0x7f) == 0x36)) {
|
||||||
|
/*
|
||||||
|
* Next key may use shift table
|
||||||
|
*/
|
||||||
|
if ((scancode & 0x80) == 0)
|
||||||
|
shift_key = 1;
|
||||||
|
else
|
||||||
|
shift_key = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((scancode&0x7f) == 0x1d) {
|
||||||
|
/*
|
||||||
|
* Left ctrl key
|
||||||
|
*/
|
||||||
|
if ((scancode & 0x80) == 0)
|
||||||
|
ctrl_key = 1;
|
||||||
|
else
|
||||||
|
ctrl_key = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((scancode & 0x80) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
scancode &= 0x7f;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate scancode
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (scancode == 0x3a) {
|
||||||
|
/*
|
||||||
|
* Toggle caps lock
|
||||||
|
*/
|
||||||
|
shift_lock ^= 1;
|
||||||
|
|
||||||
|
#ifdef KDB_BLINK_LED
|
||||||
|
kdb_toggleled(0x4);
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scancode == 0x0e) {
|
||||||
|
/*
|
||||||
|
* Backspace
|
||||||
|
*/
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special Key */
|
||||||
|
switch (scancode) {
|
||||||
|
case 0xF: /* Tab */
|
||||||
|
return 9;
|
||||||
|
case 0x53: /* Del */
|
||||||
|
return 4;
|
||||||
|
case 0x47: /* Home */
|
||||||
|
return 1;
|
||||||
|
case 0x4F: /* End */
|
||||||
|
return 5;
|
||||||
|
case 0x4B: /* Left */
|
||||||
|
return 2;
|
||||||
|
case 0x48: /* Up */
|
||||||
|
return 16;
|
||||||
|
case 0x50: /* Down */
|
||||||
|
return 14;
|
||||||
|
case 0x4D: /* Right */
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scancode == 0xe0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For Japanese 86/106 keyboards
|
||||||
|
* See comment in drivers/char/pc_keyb.c.
|
||||||
|
* - Masahiro Adegawa
|
||||||
|
*/
|
||||||
|
if (scancode == 0x73)
|
||||||
|
scancode = 0x59;
|
||||||
|
else if (scancode == 0x7d)
|
||||||
|
scancode = 0x7c;
|
||||||
|
|
||||||
|
if (!shift_lock && !shift_key && !ctrl_key) {
|
||||||
|
keychar = plain_map[scancode];
|
||||||
|
} else if ((shift_lock || shift_key) && key_maps[1]) {
|
||||||
|
keychar = key_maps[1][scancode];
|
||||||
|
} else if (ctrl_key && key_maps[4]) {
|
||||||
|
keychar = key_maps[4][scancode];
|
||||||
|
} else {
|
||||||
|
keychar = 0x0020;
|
||||||
|
kdb_printf("Unknown state/scancode (%d)\n", scancode);
|
||||||
|
}
|
||||||
|
keychar &= 0x0fff;
|
||||||
|
if (keychar == '\t')
|
||||||
|
keychar = ' ';
|
||||||
|
switch (KTYP(keychar)) {
|
||||||
|
case KT_LETTER:
|
||||||
|
case KT_LATIN:
|
||||||
|
if (isprint(keychar))
|
||||||
|
break; /* printable characters */
|
||||||
|
/* drop through */
|
||||||
|
case KT_SPEC:
|
||||||
|
if (keychar == K_ENTER)
|
||||||
|
break;
|
||||||
|
/* drop through */
|
||||||
|
default:
|
||||||
|
return -1; /* ignore unprintables */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((scancode & 0x7f) == 0x1c) {
|
||||||
|
/*
|
||||||
|
* enter key. All done. Absorb the release scancode.
|
||||||
|
*/
|
||||||
|
while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0)
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the scancode
|
||||||
|
*/
|
||||||
|
scancode = inb(KBD_DATA_REG);
|
||||||
|
scanstatus = inb(KBD_STATUS_REG);
|
||||||
|
|
||||||
|
while (scanstatus & KBD_STAT_MOUSE_OBF) {
|
||||||
|
scancode = inb(KBD_DATA_REG);
|
||||||
|
scanstatus = inb(KBD_STATUS_REG);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scancode != 0x9c) {
|
||||||
|
/*
|
||||||
|
* Wasn't an enter-release, why not?
|
||||||
|
*/
|
||||||
|
kdb_printf("kdb: expected enter got 0x%x status 0x%x\n",
|
||||||
|
scancode, scanstatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keychar & 0xff;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kdb_get_kbd_char);
|
2849
kernel/debug/kdb/kdb_main.c
Normal file
2849
kernel/debug/kdb/kdb_main.c
Normal file
File diff suppressed because it is too large
Load diff
300
kernel/debug/kdb/kdb_private.h
Normal file
300
kernel/debug/kdb/kdb_private.h
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
#ifndef _KDBPRIVATE_H
|
||||||
|
#define _KDBPRIVATE_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kernel Debugger Architecture Independent Private Headers
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kgdb.h>
|
||||||
|
#include "../debug_core.h"
|
||||||
|
|
||||||
|
/* Kernel Debugger Error codes. Must not overlap with command codes. */
|
||||||
|
#define KDB_NOTFOUND (-1)
|
||||||
|
#define KDB_ARGCOUNT (-2)
|
||||||
|
#define KDB_BADWIDTH (-3)
|
||||||
|
#define KDB_BADRADIX (-4)
|
||||||
|
#define KDB_NOTENV (-5)
|
||||||
|
#define KDB_NOENVVALUE (-6)
|
||||||
|
#define KDB_NOTIMP (-7)
|
||||||
|
#define KDB_ENVFULL (-8)
|
||||||
|
#define KDB_ENVBUFFULL (-9)
|
||||||
|
#define KDB_TOOMANYBPT (-10)
|
||||||
|
#define KDB_TOOMANYDBREGS (-11)
|
||||||
|
#define KDB_DUPBPT (-12)
|
||||||
|
#define KDB_BPTNOTFOUND (-13)
|
||||||
|
#define KDB_BADMODE (-14)
|
||||||
|
#define KDB_BADINT (-15)
|
||||||
|
#define KDB_INVADDRFMT (-16)
|
||||||
|
#define KDB_BADREG (-17)
|
||||||
|
#define KDB_BADCPUNUM (-18)
|
||||||
|
#define KDB_BADLENGTH (-19)
|
||||||
|
#define KDB_NOBP (-20)
|
||||||
|
#define KDB_BADADDR (-21)
|
||||||
|
|
||||||
|
/* Kernel Debugger Command codes. Must not overlap with error codes. */
|
||||||
|
#define KDB_CMD_GO (-1001)
|
||||||
|
#define KDB_CMD_CPU (-1002)
|
||||||
|
#define KDB_CMD_SS (-1003)
|
||||||
|
#define KDB_CMD_SSB (-1004)
|
||||||
|
#define KDB_CMD_KGDB (-1005)
|
||||||
|
#define KDB_CMD_KGDB2 (-1006)
|
||||||
|
|
||||||
|
/* Internal debug flags */
|
||||||
|
#define KDB_DEBUG_FLAG_BP 0x0002 /* Breakpoint subsystem debug */
|
||||||
|
#define KDB_DEBUG_FLAG_BB_SUMM 0x0004 /* Basic block analysis, summary only */
|
||||||
|
#define KDB_DEBUG_FLAG_AR 0x0008 /* Activation record, generic */
|
||||||
|
#define KDB_DEBUG_FLAG_ARA 0x0010 /* Activation record, arch specific */
|
||||||
|
#define KDB_DEBUG_FLAG_BB 0x0020 /* All basic block analysis */
|
||||||
|
#define KDB_DEBUG_FLAG_STATE 0x0040 /* State flags */
|
||||||
|
#define KDB_DEBUG_FLAG_MASK 0xffff /* All debug flags */
|
||||||
|
#define KDB_DEBUG_FLAG_SHIFT 16 /* Shift factor for dbflags */
|
||||||
|
|
||||||
|
#define KDB_DEBUG(flag) (kdb_flags & \
|
||||||
|
(KDB_DEBUG_FLAG_##flag << KDB_DEBUG_FLAG_SHIFT))
|
||||||
|
#define KDB_DEBUG_STATE(text, value) if (KDB_DEBUG(STATE)) \
|
||||||
|
kdb_print_state(text, value)
|
||||||
|
|
||||||
|
#if BITS_PER_LONG == 32
|
||||||
|
|
||||||
|
#define KDB_PLATFORM_ENV "BYTESPERWORD=4"
|
||||||
|
|
||||||
|
#define kdb_machreg_fmt "0x%lx"
|
||||||
|
#define kdb_machreg_fmt0 "0x%08lx"
|
||||||
|
#define kdb_bfd_vma_fmt "0x%lx"
|
||||||
|
#define kdb_bfd_vma_fmt0 "0x%08lx"
|
||||||
|
#define kdb_elfw_addr_fmt "0x%x"
|
||||||
|
#define kdb_elfw_addr_fmt0 "0x%08x"
|
||||||
|
#define kdb_f_count_fmt "%d"
|
||||||
|
|
||||||
|
#elif BITS_PER_LONG == 64
|
||||||
|
|
||||||
|
#define KDB_PLATFORM_ENV "BYTESPERWORD=8"
|
||||||
|
|
||||||
|
#define kdb_machreg_fmt "0x%lx"
|
||||||
|
#define kdb_machreg_fmt0 "0x%016lx"
|
||||||
|
#define kdb_bfd_vma_fmt "0x%lx"
|
||||||
|
#define kdb_bfd_vma_fmt0 "0x%016lx"
|
||||||
|
#define kdb_elfw_addr_fmt "0x%x"
|
||||||
|
#define kdb_elfw_addr_fmt0 "0x%016x"
|
||||||
|
#define kdb_f_count_fmt "%ld"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KDB_MAXBPT describes the total number of breakpoints
|
||||||
|
* supported by this architecure.
|
||||||
|
*/
|
||||||
|
#define KDB_MAXBPT 16
|
||||||
|
|
||||||
|
/* Maximum number of arguments to a function */
|
||||||
|
#define KDB_MAXARGS 16
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KDB_REPEAT_NONE = 0, /* Do not repeat this command */
|
||||||
|
KDB_REPEAT_NO_ARGS, /* Repeat the command without arguments */
|
||||||
|
KDB_REPEAT_WITH_ARGS, /* Repeat the command including its arguments */
|
||||||
|
} kdb_repeat_t;
|
||||||
|
|
||||||
|
typedef int (*kdb_func_t)(int, const char **);
|
||||||
|
|
||||||
|
/* Symbol table format returned by kallsyms. */
|
||||||
|
typedef struct __ksymtab {
|
||||||
|
unsigned long value; /* Address of symbol */
|
||||||
|
const char *mod_name; /* Module containing symbol or
|
||||||
|
* "kernel" */
|
||||||
|
unsigned long mod_start;
|
||||||
|
unsigned long mod_end;
|
||||||
|
const char *sec_name; /* Section containing symbol */
|
||||||
|
unsigned long sec_start;
|
||||||
|
unsigned long sec_end;
|
||||||
|
const char *sym_name; /* Full symbol name, including
|
||||||
|
* any version */
|
||||||
|
unsigned long sym_start;
|
||||||
|
unsigned long sym_end;
|
||||||
|
} kdb_symtab_t;
|
||||||
|
extern int kallsyms_symbol_next(char *prefix_name, int flag);
|
||||||
|
extern int kallsyms_symbol_complete(char *prefix_name, int max_len);
|
||||||
|
|
||||||
|
/* Exported Symbols for kernel loadable modules to use. */
|
||||||
|
extern int kdb_register(char *, kdb_func_t, char *, char *, short);
|
||||||
|
extern int kdb_register_repeat(char *, kdb_func_t, char *, char *,
|
||||||
|
short, kdb_repeat_t);
|
||||||
|
extern int kdb_unregister(char *);
|
||||||
|
|
||||||
|
extern int kdb_getarea_size(void *, unsigned long, size_t);
|
||||||
|
extern int kdb_putarea_size(unsigned long, void *, size_t);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like get_user and put_user, kdb_getarea and kdb_putarea take variable
|
||||||
|
* names, not pointers. The underlying *_size functions take pointers.
|
||||||
|
*/
|
||||||
|
#define kdb_getarea(x, addr) kdb_getarea_size(&(x), addr, sizeof((x)))
|
||||||
|
#define kdb_putarea(addr, x) kdb_putarea_size(addr, &(x), sizeof((x)))
|
||||||
|
|
||||||
|
extern int kdb_getphysword(unsigned long *word,
|
||||||
|
unsigned long addr, size_t size);
|
||||||
|
extern int kdb_getword(unsigned long *, unsigned long, size_t);
|
||||||
|
extern int kdb_putword(unsigned long, unsigned long, size_t);
|
||||||
|
|
||||||
|
extern int kdbgetularg(const char *, unsigned long *);
|
||||||
|
extern int kdb_set(int, const char **);
|
||||||
|
extern char *kdbgetenv(const char *);
|
||||||
|
extern int kdbgetintenv(const char *, int *);
|
||||||
|
extern int kdbgetaddrarg(int, const char **, int*, unsigned long *,
|
||||||
|
long *, char **);
|
||||||
|
extern int kdbgetsymval(const char *, kdb_symtab_t *);
|
||||||
|
extern int kdbnearsym(unsigned long, kdb_symtab_t *);
|
||||||
|
extern void kdbnearsym_cleanup(void);
|
||||||
|
extern char *kdb_strdup(const char *str, gfp_t type);
|
||||||
|
extern void kdb_symbol_print(unsigned long, const kdb_symtab_t *, unsigned int);
|
||||||
|
|
||||||
|
/* Routine for debugging the debugger state. */
|
||||||
|
extern void kdb_print_state(const char *, int);
|
||||||
|
|
||||||
|
extern int kdb_state;
|
||||||
|
#define KDB_STATE_KDB 0x00000001 /* Cpu is inside kdb */
|
||||||
|
#define KDB_STATE_LEAVING 0x00000002 /* Cpu is leaving kdb */
|
||||||
|
#define KDB_STATE_CMD 0x00000004 /* Running a kdb command */
|
||||||
|
#define KDB_STATE_KDB_CONTROL 0x00000008 /* This cpu is under
|
||||||
|
* kdb control */
|
||||||
|
#define KDB_STATE_HOLD_CPU 0x00000010 /* Hold this cpu inside kdb */
|
||||||
|
#define KDB_STATE_DOING_SS 0x00000020 /* Doing ss command */
|
||||||
|
#define KDB_STATE_DOING_SSB 0x00000040 /* Doing ssb command,
|
||||||
|
* DOING_SS is also set */
|
||||||
|
#define KDB_STATE_SSBPT 0x00000080 /* Install breakpoint
|
||||||
|
* after one ss, independent of
|
||||||
|
* DOING_SS */
|
||||||
|
#define KDB_STATE_REENTRY 0x00000100 /* Valid re-entry into kdb */
|
||||||
|
#define KDB_STATE_SUPPRESS 0x00000200 /* Suppress error messages */
|
||||||
|
#define KDB_STATE_PAGER 0x00000400 /* pager is available */
|
||||||
|
#define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching
|
||||||
|
* back to initial cpu */
|
||||||
|
#define KDB_STATE_PRINTF_LOCK 0x00001000 /* Holds kdb_printf lock */
|
||||||
|
#define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */
|
||||||
|
#define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */
|
||||||
|
#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been
|
||||||
|
* adjusted */
|
||||||
|
#define KDB_STATE_GO1 0x00010000 /* go only releases one cpu */
|
||||||
|
#define KDB_STATE_KEYBOARD 0x00020000 /* kdb entered via
|
||||||
|
* keyboard on this cpu */
|
||||||
|
#define KDB_STATE_KEXEC 0x00040000 /* kexec issued */
|
||||||
|
#define KDB_STATE_DOING_KGDB 0x00080000 /* kgdb enter now issued */
|
||||||
|
#define KDB_STATE_DOING_KGDB2 0x00100000 /* kgdb enter now issued */
|
||||||
|
#define KDB_STATE_KGDB_TRANS 0x00200000 /* Transition to kgdb */
|
||||||
|
#define KDB_STATE_ARCH 0xff000000 /* Reserved for arch
|
||||||
|
* specific use */
|
||||||
|
|
||||||
|
#define KDB_STATE(flag) (kdb_state & KDB_STATE_##flag)
|
||||||
|
#define KDB_STATE_SET(flag) ((void)(kdb_state |= KDB_STATE_##flag))
|
||||||
|
#define KDB_STATE_CLEAR(flag) ((void)(kdb_state &= ~KDB_STATE_##flag))
|
||||||
|
|
||||||
|
extern int kdb_nextline; /* Current number of lines displayed */
|
||||||
|
|
||||||
|
typedef struct _kdb_bp {
|
||||||
|
unsigned long bp_addr; /* Address breakpoint is present at */
|
||||||
|
unsigned int bp_free:1; /* This entry is available */
|
||||||
|
unsigned int bp_enabled:1; /* Breakpoint is active in register */
|
||||||
|
unsigned int bp_type:4; /* Uses hardware register */
|
||||||
|
unsigned int bp_installed:1; /* Breakpoint is installed */
|
||||||
|
unsigned int bp_delay:1; /* Do delayed bp handling */
|
||||||
|
unsigned int bp_delayed:1; /* Delayed breakpoint */
|
||||||
|
unsigned int bph_length; /* HW break length */
|
||||||
|
} kdb_bp_t;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
extern kdb_bp_t kdb_breakpoints[/* KDB_MAXBPT */];
|
||||||
|
|
||||||
|
/* The KDB shell command table */
|
||||||
|
typedef struct _kdbtab {
|
||||||
|
char *cmd_name; /* Command name */
|
||||||
|
kdb_func_t cmd_func; /* Function to execute command */
|
||||||
|
char *cmd_usage; /* Usage String for this command */
|
||||||
|
char *cmd_help; /* Help message for this command */
|
||||||
|
short cmd_flags; /* Parsing flags */
|
||||||
|
short cmd_minlen; /* Minimum legal # command
|
||||||
|
* chars required */
|
||||||
|
kdb_repeat_t cmd_repeat; /* Does command auto repeat on enter? */
|
||||||
|
} kdbtab_t;
|
||||||
|
|
||||||
|
extern int kdb_bt(int, const char **); /* KDB display back trace */
|
||||||
|
|
||||||
|
/* KDB breakpoint management functions */
|
||||||
|
extern void kdb_initbptab(void);
|
||||||
|
extern void kdb_bp_install(struct pt_regs *);
|
||||||
|
extern void kdb_bp_remove(void);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KDB_DB_BPT, /* Breakpoint */
|
||||||
|
KDB_DB_SS, /* Single-step trap */
|
||||||
|
KDB_DB_SSB, /* Single step to branch */
|
||||||
|
KDB_DB_SSBPT, /* Single step over breakpoint */
|
||||||
|
KDB_DB_NOBPT /* Spurious breakpoint */
|
||||||
|
} kdb_dbtrap_t;
|
||||||
|
|
||||||
|
extern int kdb_main_loop(kdb_reason_t, kdb_reason_t,
|
||||||
|
int, kdb_dbtrap_t, struct pt_regs *);
|
||||||
|
|
||||||
|
/* Miscellaneous functions and data areas */
|
||||||
|
extern int kdb_grepping_flag;
|
||||||
|
extern char kdb_grep_string[];
|
||||||
|
extern int kdb_grep_leading;
|
||||||
|
extern int kdb_grep_trailing;
|
||||||
|
extern char *kdb_cmds[];
|
||||||
|
extern void kdb_syslog_data(char *syslog_data[]);
|
||||||
|
extern unsigned long kdb_task_state_string(const char *);
|
||||||
|
extern char kdb_task_state_char (const struct task_struct *);
|
||||||
|
extern unsigned long kdb_task_state(const struct task_struct *p,
|
||||||
|
unsigned long mask);
|
||||||
|
extern void kdb_ps_suppressed(void);
|
||||||
|
extern void kdb_ps1(const struct task_struct *p);
|
||||||
|
extern void kdb_print_nameval(const char *name, unsigned long val);
|
||||||
|
extern void kdb_send_sig_info(struct task_struct *p, struct siginfo *info);
|
||||||
|
extern void kdb_meminfo_proc_show(void);
|
||||||
|
extern const char *kdb_walk_kallsyms(loff_t *pos);
|
||||||
|
extern char *kdb_getstr(char *, size_t, char *);
|
||||||
|
|
||||||
|
/* Defines for kdb_symbol_print */
|
||||||
|
#define KDB_SP_SPACEB 0x0001 /* Space before string */
|
||||||
|
#define KDB_SP_SPACEA 0x0002 /* Space after string */
|
||||||
|
#define KDB_SP_PAREN 0x0004 /* Parenthesis around string */
|
||||||
|
#define KDB_SP_VALUE 0x0008 /* Print the value of the address */
|
||||||
|
#define KDB_SP_SYMSIZE 0x0010 /* Print the size of the symbol */
|
||||||
|
#define KDB_SP_NEWLINE 0x0020 /* Newline after string */
|
||||||
|
#define KDB_SP_DEFAULT (KDB_SP_VALUE|KDB_SP_PAREN)
|
||||||
|
|
||||||
|
#define KDB_TSK(cpu) kgdb_info[cpu].task
|
||||||
|
#define KDB_TSKREGS(cpu) kgdb_info[cpu].debuggerinfo
|
||||||
|
|
||||||
|
extern struct task_struct *kdb_curr_task(int);
|
||||||
|
|
||||||
|
#define kdb_task_has_cpu(p) (task_curr(p))
|
||||||
|
|
||||||
|
/* Simplify coexistence with NPTL */
|
||||||
|
#define kdb_do_each_thread(g, p) do_each_thread(g, p)
|
||||||
|
#define kdb_while_each_thread(g, p) while_each_thread(g, p)
|
||||||
|
|
||||||
|
#define GFP_KDB (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
|
||||||
|
|
||||||
|
extern void *debug_kmalloc(size_t size, gfp_t flags);
|
||||||
|
extern void debug_kfree(void *);
|
||||||
|
extern void debug_kusage(void);
|
||||||
|
|
||||||
|
extern void kdb_set_current_task(struct task_struct *);
|
||||||
|
extern struct task_struct *kdb_current_task;
|
||||||
|
#ifdef CONFIG_MODULES
|
||||||
|
extern struct list_head *kdb_modules;
|
||||||
|
#endif /* CONFIG_MODULES */
|
||||||
|
|
||||||
|
extern char kdb_prompt_str[];
|
||||||
|
|
||||||
|
#define KDB_WORD_SIZE ((int)sizeof(unsigned long))
|
||||||
|
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
|
#endif /* !_KDBPRIVATE_H */
|
927
kernel/debug/kdb/kdb_support.c
Normal file
927
kernel/debug/kdb/kdb_support.c
Normal file
|
@ -0,0 +1,927 @@
|
||||||
|
/*
|
||||||
|
* Kernel Debugger Architecture Independent Support Functions
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved.
|
||||||
|
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
|
||||||
|
* 03/02/13 added new 2.5 kallsyms <xavier.bru@bull.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/kallsyms.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/hardirq.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include "kdb_private.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdbgetsymval - Return the address of the given symbol.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* symname Character string containing symbol name
|
||||||
|
* symtab Structure to receive results
|
||||||
|
* Returns:
|
||||||
|
* 0 Symbol not found, symtab zero filled
|
||||||
|
* 1 Symbol mapped to module/symbol/section, data in symtab
|
||||||
|
*/
|
||||||
|
int kdbgetsymval(const char *symname, kdb_symtab_t *symtab)
|
||||||
|
{
|
||||||
|
if (KDB_DEBUG(AR))
|
||||||
|
kdb_printf("kdbgetsymval: symname=%s, symtab=%p\n", symname,
|
||||||
|
symtab);
|
||||||
|
memset(symtab, 0, sizeof(*symtab));
|
||||||
|
symtab->sym_start = kallsyms_lookup_name(symname);
|
||||||
|
if (symtab->sym_start) {
|
||||||
|
if (KDB_DEBUG(AR))
|
||||||
|
kdb_printf("kdbgetsymval: returns 1, "
|
||||||
|
"symtab->sym_start=0x%lx\n",
|
||||||
|
symtab->sym_start);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (KDB_DEBUG(AR))
|
||||||
|
kdb_printf("kdbgetsymval: returns 0\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(kdbgetsymval);
|
||||||
|
|
||||||
|
static char *kdb_name_table[100]; /* arbitrary size */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdbnearsym - Return the name of the symbol with the nearest address
|
||||||
|
* less than 'addr'.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address to check for symbol near
|
||||||
|
* symtab Structure to receive results
|
||||||
|
* Returns:
|
||||||
|
* 0 No sections contain this address, symtab zero filled
|
||||||
|
* 1 Address mapped to module/symbol/section, data in symtab
|
||||||
|
* Remarks:
|
||||||
|
* 2.6 kallsyms has a "feature" where it unpacks the name into a
|
||||||
|
* string. If that string is reused before the caller expects it
|
||||||
|
* then the caller sees its string change without warning. To
|
||||||
|
* avoid cluttering up the main kdb code with lots of kdb_strdup,
|
||||||
|
* tests and kfree calls, kdbnearsym maintains an LRU list of the
|
||||||
|
* last few unique strings. The list is sized large enough to
|
||||||
|
* hold active strings, no kdb caller of kdbnearsym makes more
|
||||||
|
* than ~20 later calls before using a saved value.
|
||||||
|
*/
|
||||||
|
int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
unsigned long symbolsize;
|
||||||
|
unsigned long offset;
|
||||||
|
#define knt1_size 128 /* must be >= kallsyms table size */
|
||||||
|
char *knt1 = NULL;
|
||||||
|
|
||||||
|
if (KDB_DEBUG(AR))
|
||||||
|
kdb_printf("kdbnearsym: addr=0x%lx, symtab=%p\n", addr, symtab);
|
||||||
|
memset(symtab, 0, sizeof(*symtab));
|
||||||
|
|
||||||
|
if (addr < 4096)
|
||||||
|
goto out;
|
||||||
|
knt1 = debug_kmalloc(knt1_size, GFP_ATOMIC);
|
||||||
|
if (!knt1) {
|
||||||
|
kdb_printf("kdbnearsym: addr=0x%lx cannot kmalloc knt1\n",
|
||||||
|
addr);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
symtab->sym_name = kallsyms_lookup(addr, &symbolsize , &offset,
|
||||||
|
(char **)(&symtab->mod_name), knt1);
|
||||||
|
if (offset > 8*1024*1024) {
|
||||||
|
symtab->sym_name = NULL;
|
||||||
|
addr = offset = symbolsize = 0;
|
||||||
|
}
|
||||||
|
symtab->sym_start = addr - offset;
|
||||||
|
symtab->sym_end = symtab->sym_start + symbolsize;
|
||||||
|
ret = symtab->sym_name != NULL && *(symtab->sym_name) != '\0';
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
int i;
|
||||||
|
/* Another 2.6 kallsyms "feature". Sometimes the sym_name is
|
||||||
|
* set but the buffer passed into kallsyms_lookup is not used,
|
||||||
|
* so it contains garbage. The caller has to work out which
|
||||||
|
* buffer needs to be saved.
|
||||||
|
*
|
||||||
|
* What was Rusty smoking when he wrote that code?
|
||||||
|
*/
|
||||||
|
if (symtab->sym_name != knt1) {
|
||||||
|
strncpy(knt1, symtab->sym_name, knt1_size);
|
||||||
|
knt1[knt1_size-1] = '\0';
|
||||||
|
}
|
||||||
|
for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) {
|
||||||
|
if (kdb_name_table[i] &&
|
||||||
|
strcmp(kdb_name_table[i], knt1) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i >= ARRAY_SIZE(kdb_name_table)) {
|
||||||
|
debug_kfree(kdb_name_table[0]);
|
||||||
|
memcpy(kdb_name_table, kdb_name_table+1,
|
||||||
|
sizeof(kdb_name_table[0]) *
|
||||||
|
(ARRAY_SIZE(kdb_name_table)-1));
|
||||||
|
} else {
|
||||||
|
debug_kfree(knt1);
|
||||||
|
knt1 = kdb_name_table[i];
|
||||||
|
memcpy(kdb_name_table+i, kdb_name_table+i+1,
|
||||||
|
sizeof(kdb_name_table[0]) *
|
||||||
|
(ARRAY_SIZE(kdb_name_table)-i-1));
|
||||||
|
}
|
||||||
|
i = ARRAY_SIZE(kdb_name_table) - 1;
|
||||||
|
kdb_name_table[i] = knt1;
|
||||||
|
symtab->sym_name = kdb_name_table[i];
|
||||||
|
knt1 = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symtab->mod_name == NULL)
|
||||||
|
symtab->mod_name = "kernel";
|
||||||
|
if (KDB_DEBUG(AR))
|
||||||
|
kdb_printf("kdbnearsym: returns %d symtab->sym_start=0x%lx, "
|
||||||
|
"symtab->mod_name=%p, symtab->sym_name=%p (%s)\n", ret,
|
||||||
|
symtab->sym_start, symtab->mod_name, symtab->sym_name,
|
||||||
|
symtab->sym_name);
|
||||||
|
|
||||||
|
out:
|
||||||
|
debug_kfree(knt1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdbnearsym_cleanup(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) {
|
||||||
|
if (kdb_name_table[i]) {
|
||||||
|
debug_kfree(kdb_name_table[i]);
|
||||||
|
kdb_name_table[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char ks_namebuf[KSYM_NAME_LEN+1], ks_namebuf_prev[KSYM_NAME_LEN+1];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kallsyms_symbol_complete
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* prefix_name prefix of a symbol name to lookup
|
||||||
|
* max_len maximum length that can be returned
|
||||||
|
* Returns:
|
||||||
|
* Number of symbols which match the given prefix.
|
||||||
|
* Notes:
|
||||||
|
* prefix_name is changed to contain the longest unique prefix that
|
||||||
|
* starts with this prefix (tab completion).
|
||||||
|
*/
|
||||||
|
int kallsyms_symbol_complete(char *prefix_name, int max_len)
|
||||||
|
{
|
||||||
|
loff_t pos = 0;
|
||||||
|
int prefix_len = strlen(prefix_name), prev_len = 0;
|
||||||
|
int i, number = 0;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
while ((name = kdb_walk_kallsyms(&pos))) {
|
||||||
|
if (strncmp(name, prefix_name, prefix_len) == 0) {
|
||||||
|
strcpy(ks_namebuf, name);
|
||||||
|
/* Work out the longest name that matches the prefix */
|
||||||
|
if (++number == 1) {
|
||||||
|
prev_len = min_t(int, max_len-1,
|
||||||
|
strlen(ks_namebuf));
|
||||||
|
memcpy(ks_namebuf_prev, ks_namebuf, prev_len);
|
||||||
|
ks_namebuf_prev[prev_len] = '\0';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (i = 0; i < prev_len; i++) {
|
||||||
|
if (ks_namebuf[i] != ks_namebuf_prev[i]) {
|
||||||
|
prev_len = i;
|
||||||
|
ks_namebuf_prev[i] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prev_len > prefix_len)
|
||||||
|
memcpy(prefix_name, ks_namebuf_prev, prev_len+1);
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kallsyms_symbol_next
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* prefix_name prefix of a symbol name to lookup
|
||||||
|
* flag 0 means search from the head, 1 means continue search.
|
||||||
|
* Returns:
|
||||||
|
* 1 if a symbol matches the given prefix.
|
||||||
|
* 0 if no string found
|
||||||
|
*/
|
||||||
|
int kallsyms_symbol_next(char *prefix_name, int flag)
|
||||||
|
{
|
||||||
|
int prefix_len = strlen(prefix_name);
|
||||||
|
static loff_t pos;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (!flag)
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
while ((name = kdb_walk_kallsyms(&pos))) {
|
||||||
|
if (strncmp(name, prefix_name, prefix_len) == 0) {
|
||||||
|
strncpy(prefix_name, name, strlen(name)+1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_symbol_print - Standard method for printing a symbol name and offset.
|
||||||
|
* Inputs:
|
||||||
|
* addr Address to be printed.
|
||||||
|
* symtab Address of symbol data, if NULL this routine does its
|
||||||
|
* own lookup.
|
||||||
|
* punc Punctuation for string, bit field.
|
||||||
|
* Remarks:
|
||||||
|
* The string and its punctuation is only printed if the address
|
||||||
|
* is inside the kernel, except that the value is always printed
|
||||||
|
* when requested.
|
||||||
|
*/
|
||||||
|
void kdb_symbol_print(unsigned long addr, const kdb_symtab_t *symtab_p,
|
||||||
|
unsigned int punc)
|
||||||
|
{
|
||||||
|
kdb_symtab_t symtab, *symtab_p2;
|
||||||
|
if (symtab_p) {
|
||||||
|
symtab_p2 = (kdb_symtab_t *)symtab_p;
|
||||||
|
} else {
|
||||||
|
symtab_p2 = &symtab;
|
||||||
|
kdbnearsym(addr, symtab_p2);
|
||||||
|
}
|
||||||
|
if (!(symtab_p2->sym_name || (punc & KDB_SP_VALUE)))
|
||||||
|
return;
|
||||||
|
if (punc & KDB_SP_SPACEB)
|
||||||
|
kdb_printf(" ");
|
||||||
|
if (punc & KDB_SP_VALUE)
|
||||||
|
kdb_printf(kdb_machreg_fmt0, addr);
|
||||||
|
if (symtab_p2->sym_name) {
|
||||||
|
if (punc & KDB_SP_VALUE)
|
||||||
|
kdb_printf(" ");
|
||||||
|
if (punc & KDB_SP_PAREN)
|
||||||
|
kdb_printf("(");
|
||||||
|
if (strcmp(symtab_p2->mod_name, "kernel"))
|
||||||
|
kdb_printf("[%s]", symtab_p2->mod_name);
|
||||||
|
kdb_printf("%s", symtab_p2->sym_name);
|
||||||
|
if (addr != symtab_p2->sym_start)
|
||||||
|
kdb_printf("+0x%lx", addr - symtab_p2->sym_start);
|
||||||
|
if (punc & KDB_SP_SYMSIZE)
|
||||||
|
kdb_printf("/0x%lx",
|
||||||
|
symtab_p2->sym_end - symtab_p2->sym_start);
|
||||||
|
if (punc & KDB_SP_PAREN)
|
||||||
|
kdb_printf(")");
|
||||||
|
}
|
||||||
|
if (punc & KDB_SP_SPACEA)
|
||||||
|
kdb_printf(" ");
|
||||||
|
if (punc & KDB_SP_NEWLINE)
|
||||||
|
kdb_printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_strdup - kdb equivalent of strdup, for disasm code.
|
||||||
|
* Inputs:
|
||||||
|
* str The string to duplicate.
|
||||||
|
* type Flags to kmalloc for the new string.
|
||||||
|
* Returns:
|
||||||
|
* Address of the new string, NULL if storage could not be allocated.
|
||||||
|
* Remarks:
|
||||||
|
* This is not in lib/string.c because it uses kmalloc which is not
|
||||||
|
* available when string.o is used in boot loaders.
|
||||||
|
*/
|
||||||
|
char *kdb_strdup(const char *str, gfp_t type)
|
||||||
|
{
|
||||||
|
int n = strlen(str)+1;
|
||||||
|
char *s = kmalloc(n, type);
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
return strcpy(s, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_getarea_size - Read an area of data. The kdb equivalent of
|
||||||
|
* copy_from_user, with kdb messages for invalid addresses.
|
||||||
|
* Inputs:
|
||||||
|
* res Pointer to the area to receive the result.
|
||||||
|
* addr Address of the area to copy.
|
||||||
|
* size Size of the area.
|
||||||
|
* Returns:
|
||||||
|
* 0 for success, < 0 for error.
|
||||||
|
*/
|
||||||
|
int kdb_getarea_size(void *res, unsigned long addr, size_t size)
|
||||||
|
{
|
||||||
|
int ret = probe_kernel_read((char *)res, (char *)addr, size);
|
||||||
|
if (ret) {
|
||||||
|
if (!KDB_STATE(SUPPRESS)) {
|
||||||
|
kdb_printf("kdb_getarea: Bad address 0x%lx\n", addr);
|
||||||
|
KDB_STATE_SET(SUPPRESS);
|
||||||
|
}
|
||||||
|
ret = KDB_BADADDR;
|
||||||
|
} else {
|
||||||
|
KDB_STATE_CLEAR(SUPPRESS);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_putarea_size - Write an area of data. The kdb equivalent of
|
||||||
|
* copy_to_user, with kdb messages for invalid addresses.
|
||||||
|
* Inputs:
|
||||||
|
* addr Address of the area to write to.
|
||||||
|
* res Pointer to the area holding the data.
|
||||||
|
* size Size of the area.
|
||||||
|
* Returns:
|
||||||
|
* 0 for success, < 0 for error.
|
||||||
|
*/
|
||||||
|
int kdb_putarea_size(unsigned long addr, void *res, size_t size)
|
||||||
|
{
|
||||||
|
int ret = probe_kernel_read((char *)addr, (char *)res, size);
|
||||||
|
if (ret) {
|
||||||
|
if (!KDB_STATE(SUPPRESS)) {
|
||||||
|
kdb_printf("kdb_putarea: Bad address 0x%lx\n", addr);
|
||||||
|
KDB_STATE_SET(SUPPRESS);
|
||||||
|
}
|
||||||
|
ret = KDB_BADADDR;
|
||||||
|
} else {
|
||||||
|
KDB_STATE_CLEAR(SUPPRESS);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_getphys - Read data from a physical address. Validate the
|
||||||
|
* address is in range, use kmap_atomic() to get data
|
||||||
|
* similar to kdb_getarea() - but for phys addresses
|
||||||
|
* Inputs:
|
||||||
|
* res Pointer to the word to receive the result
|
||||||
|
* addr Physical address of the area to copy
|
||||||
|
* size Size of the area
|
||||||
|
* Returns:
|
||||||
|
* 0 for success, < 0 for error.
|
||||||
|
*/
|
||||||
|
static int kdb_getphys(void *res, unsigned long addr, size_t size)
|
||||||
|
{
|
||||||
|
unsigned long pfn;
|
||||||
|
void *vaddr;
|
||||||
|
struct page *page;
|
||||||
|
|
||||||
|
pfn = (addr >> PAGE_SHIFT);
|
||||||
|
if (!pfn_valid(pfn))
|
||||||
|
return 1;
|
||||||
|
page = pfn_to_page(pfn);
|
||||||
|
vaddr = kmap_atomic(page, KM_KDB);
|
||||||
|
memcpy(res, vaddr + (addr & (PAGE_SIZE - 1)), size);
|
||||||
|
kunmap_atomic(vaddr, KM_KDB);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_getphysword
|
||||||
|
* Inputs:
|
||||||
|
* word Pointer to the word to receive the result.
|
||||||
|
* addr Address of the area to copy.
|
||||||
|
* size Size of the area.
|
||||||
|
* Returns:
|
||||||
|
* 0 for success, < 0 for error.
|
||||||
|
*/
|
||||||
|
int kdb_getphysword(unsigned long *word, unsigned long addr, size_t size)
|
||||||
|
{
|
||||||
|
int diag;
|
||||||
|
__u8 w1;
|
||||||
|
__u16 w2;
|
||||||
|
__u32 w4;
|
||||||
|
__u64 w8;
|
||||||
|
*word = 0; /* Default value if addr or size is invalid */
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
diag = kdb_getphys(&w1, addr, sizeof(w1));
|
||||||
|
if (!diag)
|
||||||
|
*word = w1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
diag = kdb_getphys(&w2, addr, sizeof(w2));
|
||||||
|
if (!diag)
|
||||||
|
*word = w2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
diag = kdb_getphys(&w4, addr, sizeof(w4));
|
||||||
|
if (!diag)
|
||||||
|
*word = w4;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (size <= sizeof(*word)) {
|
||||||
|
diag = kdb_getphys(&w8, addr, sizeof(w8));
|
||||||
|
if (!diag)
|
||||||
|
*word = w8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* drop through */
|
||||||
|
default:
|
||||||
|
diag = KDB_BADWIDTH;
|
||||||
|
kdb_printf("kdb_getphysword: bad width %ld\n", (long) size);
|
||||||
|
}
|
||||||
|
return diag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_getword - Read a binary value. Unlike kdb_getarea, this treats
|
||||||
|
* data as numbers.
|
||||||
|
* Inputs:
|
||||||
|
* word Pointer to the word to receive the result.
|
||||||
|
* addr Address of the area to copy.
|
||||||
|
* size Size of the area.
|
||||||
|
* Returns:
|
||||||
|
* 0 for success, < 0 for error.
|
||||||
|
*/
|
||||||
|
int kdb_getword(unsigned long *word, unsigned long addr, size_t size)
|
||||||
|
{
|
||||||
|
int diag;
|
||||||
|
__u8 w1;
|
||||||
|
__u16 w2;
|
||||||
|
__u32 w4;
|
||||||
|
__u64 w8;
|
||||||
|
*word = 0; /* Default value if addr or size is invalid */
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
diag = kdb_getarea(w1, addr);
|
||||||
|
if (!diag)
|
||||||
|
*word = w1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
diag = kdb_getarea(w2, addr);
|
||||||
|
if (!diag)
|
||||||
|
*word = w2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
diag = kdb_getarea(w4, addr);
|
||||||
|
if (!diag)
|
||||||
|
*word = w4;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (size <= sizeof(*word)) {
|
||||||
|
diag = kdb_getarea(w8, addr);
|
||||||
|
if (!diag)
|
||||||
|
*word = w8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* drop through */
|
||||||
|
default:
|
||||||
|
diag = KDB_BADWIDTH;
|
||||||
|
kdb_printf("kdb_getword: bad width %ld\n", (long) size);
|
||||||
|
}
|
||||||
|
return diag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_putword - Write a binary value. Unlike kdb_putarea, this
|
||||||
|
* treats data as numbers.
|
||||||
|
* Inputs:
|
||||||
|
* addr Address of the area to write to..
|
||||||
|
* word The value to set.
|
||||||
|
* size Size of the area.
|
||||||
|
* Returns:
|
||||||
|
* 0 for success, < 0 for error.
|
||||||
|
*/
|
||||||
|
int kdb_putword(unsigned long addr, unsigned long word, size_t size)
|
||||||
|
{
|
||||||
|
int diag;
|
||||||
|
__u8 w1;
|
||||||
|
__u16 w2;
|
||||||
|
__u32 w4;
|
||||||
|
__u64 w8;
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
w1 = word;
|
||||||
|
diag = kdb_putarea(addr, w1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
w2 = word;
|
||||||
|
diag = kdb_putarea(addr, w2);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
w4 = word;
|
||||||
|
diag = kdb_putarea(addr, w4);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (size <= sizeof(word)) {
|
||||||
|
w8 = word;
|
||||||
|
diag = kdb_putarea(addr, w8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* drop through */
|
||||||
|
default:
|
||||||
|
diag = KDB_BADWIDTH;
|
||||||
|
kdb_printf("kdb_putword: bad width %ld\n", (long) size);
|
||||||
|
}
|
||||||
|
return diag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_task_state_string - Convert a string containing any of the
|
||||||
|
* letters DRSTCZEUIMA to a mask for the process state field and
|
||||||
|
* return the value. If no argument is supplied, return the mask
|
||||||
|
* that corresponds to environment variable PS, DRSTCZEU by
|
||||||
|
* default.
|
||||||
|
* Inputs:
|
||||||
|
* s String to convert
|
||||||
|
* Returns:
|
||||||
|
* Mask for process state.
|
||||||
|
* Notes:
|
||||||
|
* The mask folds data from several sources into a single long value, so
|
||||||
|
* be carefull not to overlap the bits. TASK_* bits are in the LSB,
|
||||||
|
* special cases like UNRUNNABLE are in the MSB. As of 2.6.10-rc1 there
|
||||||
|
* is no overlap between TASK_* and EXIT_* but that may not always be
|
||||||
|
* true, so EXIT_* bits are shifted left 16 bits before being stored in
|
||||||
|
* the mask.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* unrunnable is < 0 */
|
||||||
|
#define UNRUNNABLE (1UL << (8*sizeof(unsigned long) - 1))
|
||||||
|
#define RUNNING (1UL << (8*sizeof(unsigned long) - 2))
|
||||||
|
#define IDLE (1UL << (8*sizeof(unsigned long) - 3))
|
||||||
|
#define DAEMON (1UL << (8*sizeof(unsigned long) - 4))
|
||||||
|
|
||||||
|
unsigned long kdb_task_state_string(const char *s)
|
||||||
|
{
|
||||||
|
long res = 0;
|
||||||
|
if (!s) {
|
||||||
|
s = kdbgetenv("PS");
|
||||||
|
if (!s)
|
||||||
|
s = "DRSTCZEU"; /* default value for ps */
|
||||||
|
}
|
||||||
|
while (*s) {
|
||||||
|
switch (*s) {
|
||||||
|
case 'D':
|
||||||
|
res |= TASK_UNINTERRUPTIBLE;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
res |= RUNNING;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
res |= TASK_INTERRUPTIBLE;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
res |= TASK_STOPPED;
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
res |= TASK_TRACED;
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
res |= EXIT_ZOMBIE << 16;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
res |= EXIT_DEAD << 16;
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
res |= UNRUNNABLE;
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
res |= IDLE;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
res |= DAEMON;
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
res = ~0UL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
kdb_printf("%s: unknown flag '%c' ignored\n",
|
||||||
|
__func__, *s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_task_state_char - Return the character that represents the task state.
|
||||||
|
* Inputs:
|
||||||
|
* p struct task for the process
|
||||||
|
* Returns:
|
||||||
|
* One character to represent the task state.
|
||||||
|
*/
|
||||||
|
char kdb_task_state_char (const struct task_struct *p)
|
||||||
|
{
|
||||||
|
int cpu;
|
||||||
|
char state;
|
||||||
|
unsigned long tmp;
|
||||||
|
|
||||||
|
if (!p || probe_kernel_read(&tmp, (char *)p, sizeof(unsigned long)))
|
||||||
|
return 'E';
|
||||||
|
|
||||||
|
cpu = kdb_process_cpu(p);
|
||||||
|
state = (p->state == 0) ? 'R' :
|
||||||
|
(p->state < 0) ? 'U' :
|
||||||
|
(p->state & TASK_UNINTERRUPTIBLE) ? 'D' :
|
||||||
|
(p->state & TASK_STOPPED) ? 'T' :
|
||||||
|
(p->state & TASK_TRACED) ? 'C' :
|
||||||
|
(p->exit_state & EXIT_ZOMBIE) ? 'Z' :
|
||||||
|
(p->exit_state & EXIT_DEAD) ? 'E' :
|
||||||
|
(p->state & TASK_INTERRUPTIBLE) ? 'S' : '?';
|
||||||
|
if (p->pid == 0) {
|
||||||
|
/* Idle task. Is it really idle, apart from the kdb
|
||||||
|
* interrupt? */
|
||||||
|
if (!kdb_task_has_cpu(p) || kgdb_info[cpu].irq_depth == 1) {
|
||||||
|
if (cpu != kdb_initial_cpu)
|
||||||
|
state = 'I'; /* idle task */
|
||||||
|
}
|
||||||
|
} else if (!p->mm && state == 'S') {
|
||||||
|
state = 'M'; /* sleeping system daemon */
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_task_state - Return true if a process has the desired state
|
||||||
|
* given by the mask.
|
||||||
|
* Inputs:
|
||||||
|
* p struct task for the process
|
||||||
|
* mask mask from kdb_task_state_string to select processes
|
||||||
|
* Returns:
|
||||||
|
* True if the process matches at least one criteria defined by the mask.
|
||||||
|
*/
|
||||||
|
unsigned long kdb_task_state(const struct task_struct *p, unsigned long mask)
|
||||||
|
{
|
||||||
|
char state[] = { kdb_task_state_char(p), '\0' };
|
||||||
|
return (mask & kdb_task_state_string(state)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_print_nameval - Print a name and its value, converting the
|
||||||
|
* value to a symbol lookup if possible.
|
||||||
|
* Inputs:
|
||||||
|
* name field name to print
|
||||||
|
* val value of field
|
||||||
|
*/
|
||||||
|
void kdb_print_nameval(const char *name, unsigned long val)
|
||||||
|
{
|
||||||
|
kdb_symtab_t symtab;
|
||||||
|
kdb_printf(" %-11.11s ", name);
|
||||||
|
if (kdbnearsym(val, &symtab))
|
||||||
|
kdb_symbol_print(val, &symtab,
|
||||||
|
KDB_SP_VALUE|KDB_SP_SYMSIZE|KDB_SP_NEWLINE);
|
||||||
|
else
|
||||||
|
kdb_printf("0x%lx\n", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last ditch allocator for debugging, so we can still debug even when
|
||||||
|
* the GFP_ATOMIC pool has been exhausted. The algorithms are tuned
|
||||||
|
* for space usage, not for speed. One smallish memory pool, the free
|
||||||
|
* chain is always in ascending address order to allow coalescing,
|
||||||
|
* allocations are done in brute force best fit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct debug_alloc_header {
|
||||||
|
u32 next; /* offset of next header from start of pool */
|
||||||
|
u32 size;
|
||||||
|
void *caller;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The memory returned by this allocator must be aligned, which means
|
||||||
|
* so must the header size. Do not assume that sizeof(struct
|
||||||
|
* debug_alloc_header) is a multiple of the alignment, explicitly
|
||||||
|
* calculate the overhead of this header, including the alignment.
|
||||||
|
* The rest of this code must not use sizeof() on any header or
|
||||||
|
* pointer to a header.
|
||||||
|
*/
|
||||||
|
#define dah_align 8
|
||||||
|
#define dah_overhead ALIGN(sizeof(struct debug_alloc_header), dah_align)
|
||||||
|
|
||||||
|
static u64 debug_alloc_pool_aligned[256*1024/dah_align]; /* 256K pool */
|
||||||
|
static char *debug_alloc_pool = (char *)debug_alloc_pool_aligned;
|
||||||
|
static u32 dah_first, dah_first_call = 1, dah_used, dah_used_max;
|
||||||
|
|
||||||
|
/* Locking is awkward. The debug code is called from all contexts,
|
||||||
|
* including non maskable interrupts. A normal spinlock is not safe
|
||||||
|
* in NMI context. Try to get the debug allocator lock, if it cannot
|
||||||
|
* be obtained after a second then give up. If the lock could not be
|
||||||
|
* previously obtained on this cpu then only try once.
|
||||||
|
*
|
||||||
|
* sparse has no annotation for "this function _sometimes_ acquires a
|
||||||
|
* lock", so fudge the acquire/release notation.
|
||||||
|
*/
|
||||||
|
static DEFINE_SPINLOCK(dap_lock);
|
||||||
|
static int get_dap_lock(void)
|
||||||
|
__acquires(dap_lock)
|
||||||
|
{
|
||||||
|
static int dap_locked = -1;
|
||||||
|
int count;
|
||||||
|
if (dap_locked == smp_processor_id())
|
||||||
|
count = 1;
|
||||||
|
else
|
||||||
|
count = 1000;
|
||||||
|
while (1) {
|
||||||
|
if (spin_trylock(&dap_lock)) {
|
||||||
|
dap_locked = -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!count--)
|
||||||
|
break;
|
||||||
|
udelay(1000);
|
||||||
|
}
|
||||||
|
dap_locked = smp_processor_id();
|
||||||
|
__acquire(dap_lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *debug_kmalloc(size_t size, gfp_t flags)
|
||||||
|
{
|
||||||
|
unsigned int rem, h_offset;
|
||||||
|
struct debug_alloc_header *best, *bestprev, *prev, *h;
|
||||||
|
void *p = NULL;
|
||||||
|
if (!get_dap_lock()) {
|
||||||
|
__release(dap_lock); /* we never actually got it */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
h = (struct debug_alloc_header *)(debug_alloc_pool + dah_first);
|
||||||
|
if (dah_first_call) {
|
||||||
|
h->size = sizeof(debug_alloc_pool_aligned) - dah_overhead;
|
||||||
|
dah_first_call = 0;
|
||||||
|
}
|
||||||
|
size = ALIGN(size, dah_align);
|
||||||
|
prev = best = bestprev = NULL;
|
||||||
|
while (1) {
|
||||||
|
if (h->size >= size && (!best || h->size < best->size)) {
|
||||||
|
best = h;
|
||||||
|
bestprev = prev;
|
||||||
|
if (h->size == size)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!h->next)
|
||||||
|
break;
|
||||||
|
prev = h;
|
||||||
|
h = (struct debug_alloc_header *)(debug_alloc_pool + h->next);
|
||||||
|
}
|
||||||
|
if (!best)
|
||||||
|
goto out;
|
||||||
|
rem = best->size - size;
|
||||||
|
/* The pool must always contain at least one header */
|
||||||
|
if (best->next == 0 && bestprev == NULL && rem < dah_overhead)
|
||||||
|
goto out;
|
||||||
|
if (rem >= dah_overhead) {
|
||||||
|
best->size = size;
|
||||||
|
h_offset = ((char *)best - debug_alloc_pool) +
|
||||||
|
dah_overhead + best->size;
|
||||||
|
h = (struct debug_alloc_header *)(debug_alloc_pool + h_offset);
|
||||||
|
h->size = rem - dah_overhead;
|
||||||
|
h->next = best->next;
|
||||||
|
} else
|
||||||
|
h_offset = best->next;
|
||||||
|
best->caller = __builtin_return_address(0);
|
||||||
|
dah_used += best->size;
|
||||||
|
dah_used_max = max(dah_used, dah_used_max);
|
||||||
|
if (bestprev)
|
||||||
|
bestprev->next = h_offset;
|
||||||
|
else
|
||||||
|
dah_first = h_offset;
|
||||||
|
p = (char *)best + dah_overhead;
|
||||||
|
memset(p, POISON_INUSE, best->size - 1);
|
||||||
|
*((char *)p + best->size - 1) = POISON_END;
|
||||||
|
out:
|
||||||
|
spin_unlock(&dap_lock);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_kfree(void *p)
|
||||||
|
{
|
||||||
|
struct debug_alloc_header *h;
|
||||||
|
unsigned int h_offset;
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
if ((char *)p < debug_alloc_pool ||
|
||||||
|
(char *)p >= debug_alloc_pool + sizeof(debug_alloc_pool_aligned)) {
|
||||||
|
kfree(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!get_dap_lock()) {
|
||||||
|
__release(dap_lock); /* we never actually got it */
|
||||||
|
return; /* memory leak, cannot be helped */
|
||||||
|
}
|
||||||
|
h = (struct debug_alloc_header *)((char *)p - dah_overhead);
|
||||||
|
memset(p, POISON_FREE, h->size - 1);
|
||||||
|
*((char *)p + h->size - 1) = POISON_END;
|
||||||
|
h->caller = NULL;
|
||||||
|
dah_used -= h->size;
|
||||||
|
h_offset = (char *)h - debug_alloc_pool;
|
||||||
|
if (h_offset < dah_first) {
|
||||||
|
h->next = dah_first;
|
||||||
|
dah_first = h_offset;
|
||||||
|
} else {
|
||||||
|
struct debug_alloc_header *prev;
|
||||||
|
unsigned int prev_offset;
|
||||||
|
prev = (struct debug_alloc_header *)(debug_alloc_pool +
|
||||||
|
dah_first);
|
||||||
|
while (1) {
|
||||||
|
if (!prev->next || prev->next > h_offset)
|
||||||
|
break;
|
||||||
|
prev = (struct debug_alloc_header *)
|
||||||
|
(debug_alloc_pool + prev->next);
|
||||||
|
}
|
||||||
|
prev_offset = (char *)prev - debug_alloc_pool;
|
||||||
|
if (prev_offset + dah_overhead + prev->size == h_offset) {
|
||||||
|
prev->size += dah_overhead + h->size;
|
||||||
|
memset(h, POISON_FREE, dah_overhead - 1);
|
||||||
|
*((char *)h + dah_overhead - 1) = POISON_END;
|
||||||
|
h = prev;
|
||||||
|
h_offset = prev_offset;
|
||||||
|
} else {
|
||||||
|
h->next = prev->next;
|
||||||
|
prev->next = h_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (h_offset + dah_overhead + h->size == h->next) {
|
||||||
|
struct debug_alloc_header *next;
|
||||||
|
next = (struct debug_alloc_header *)
|
||||||
|
(debug_alloc_pool + h->next);
|
||||||
|
h->size += dah_overhead + next->size;
|
||||||
|
h->next = next->next;
|
||||||
|
memset(next, POISON_FREE, dah_overhead - 1);
|
||||||
|
*((char *)next + dah_overhead - 1) = POISON_END;
|
||||||
|
}
|
||||||
|
spin_unlock(&dap_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_kusage(void)
|
||||||
|
{
|
||||||
|
struct debug_alloc_header *h_free, *h_used;
|
||||||
|
#ifdef CONFIG_IA64
|
||||||
|
/* FIXME: using dah for ia64 unwind always results in a memory leak.
|
||||||
|
* Fix that memory leak first, then set debug_kusage_one_time = 1 for
|
||||||
|
* all architectures.
|
||||||
|
*/
|
||||||
|
static int debug_kusage_one_time;
|
||||||
|
#else
|
||||||
|
static int debug_kusage_one_time = 1;
|
||||||
|
#endif
|
||||||
|
if (!get_dap_lock()) {
|
||||||
|
__release(dap_lock); /* we never actually got it */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
h_free = (struct debug_alloc_header *)(debug_alloc_pool + dah_first);
|
||||||
|
if (dah_first == 0 &&
|
||||||
|
(h_free->size == sizeof(debug_alloc_pool_aligned) - dah_overhead ||
|
||||||
|
dah_first_call))
|
||||||
|
goto out;
|
||||||
|
if (!debug_kusage_one_time)
|
||||||
|
goto out;
|
||||||
|
debug_kusage_one_time = 0;
|
||||||
|
kdb_printf("%s: debug_kmalloc memory leak dah_first %d\n",
|
||||||
|
__func__, dah_first);
|
||||||
|
if (dah_first) {
|
||||||
|
h_used = (struct debug_alloc_header *)debug_alloc_pool;
|
||||||
|
kdb_printf("%s: h_used %p size %d\n", __func__, h_used,
|
||||||
|
h_used->size);
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
h_used = (struct debug_alloc_header *)
|
||||||
|
((char *)h_free + dah_overhead + h_free->size);
|
||||||
|
kdb_printf("%s: h_used %p size %d caller %p\n",
|
||||||
|
__func__, h_used, h_used->size, h_used->caller);
|
||||||
|
h_free = (struct debug_alloc_header *)
|
||||||
|
(debug_alloc_pool + h_free->next);
|
||||||
|
} while (h_free->next);
|
||||||
|
h_used = (struct debug_alloc_header *)
|
||||||
|
((char *)h_free + dah_overhead + h_free->size);
|
||||||
|
if ((char *)h_used - debug_alloc_pool !=
|
||||||
|
sizeof(debug_alloc_pool_aligned))
|
||||||
|
kdb_printf("%s: h_used %p size %d caller %p\n",
|
||||||
|
__func__, h_used, h_used->size, h_used->caller);
|
||||||
|
out:
|
||||||
|
spin_unlock(&dap_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Maintain a small stack of kdb_flags to allow recursion without disturbing
|
||||||
|
* the global kdb state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int kdb_flags_stack[4], kdb_flags_index;
|
||||||
|
|
||||||
|
void kdb_save_flags(void)
|
||||||
|
{
|
||||||
|
BUG_ON(kdb_flags_index >= ARRAY_SIZE(kdb_flags_stack));
|
||||||
|
kdb_flags_stack[kdb_flags_index++] = kdb_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kdb_restore_flags(void)
|
||||||
|
{
|
||||||
|
BUG_ON(kdb_flags_index <= 0);
|
||||||
|
kdb_flags = kdb_flags_stack[--kdb_flags_index];
|
||||||
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
||||||
#include <linux/sched.h> /* for cond_resched */
|
#include <linux/sched.h> /* for cond_resched */
|
||||||
|
@ -516,6 +517,26 @@ static int kallsyms_open(struct inode *inode, struct file *file)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
const char *kdb_walk_kallsyms(loff_t *pos)
|
||||||
|
{
|
||||||
|
static struct kallsym_iter kdb_walk_kallsyms_iter;
|
||||||
|
if (*pos == 0) {
|
||||||
|
memset(&kdb_walk_kallsyms_iter, 0,
|
||||||
|
sizeof(kdb_walk_kallsyms_iter));
|
||||||
|
reset_iter(&kdb_walk_kallsyms_iter, 0);
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
if (!update_iter(&kdb_walk_kallsyms_iter, *pos))
|
||||||
|
return NULL;
|
||||||
|
++*pos;
|
||||||
|
/* Some debugging symbols have no name. Ignore them. */
|
||||||
|
if (kdb_walk_kallsyms_iter.name[0])
|
||||||
|
return kdb_walk_kallsyms_iter.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
|
|
||||||
static const struct file_operations kallsyms_operations = {
|
static const struct file_operations kallsyms_operations = {
|
||||||
.open = kallsyms_open,
|
.open = kallsyms_open,
|
||||||
.read = seq_read,
|
.read = seq_read,
|
||||||
|
|
1764
kernel/kgdb.c
1764
kernel/kgdb.c
File diff suppressed because it is too large
Load diff
|
@ -77,6 +77,10 @@
|
||||||
DEFINE_MUTEX(module_mutex);
|
DEFINE_MUTEX(module_mutex);
|
||||||
EXPORT_SYMBOL_GPL(module_mutex);
|
EXPORT_SYMBOL_GPL(module_mutex);
|
||||||
static LIST_HEAD(modules);
|
static LIST_HEAD(modules);
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
|
|
||||||
|
|
||||||
/* Block module loading/unloading? */
|
/* Block module loading/unloading? */
|
||||||
int modules_disabled = 0;
|
int modules_disabled = 0;
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <linux/bootmem.h>
|
#include <linux/bootmem.h>
|
||||||
#include <linux/syscalls.h>
|
#include <linux/syscalls.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
#include <linux/kmsg_dump.h>
|
#include <linux/kmsg_dump.h>
|
||||||
#include <linux/syslog.h>
|
#include <linux/syslog.h>
|
||||||
|
@ -413,6 +414,22 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
|
||||||
return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
|
return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
/* kdb dmesg command needs access to the syslog buffer. do_syslog()
|
||||||
|
* uses locks so it cannot be used during debugging. Just tell kdb
|
||||||
|
* where the start and end of the physical and logical logs are. This
|
||||||
|
* is equivalent to do_syslog(3).
|
||||||
|
*/
|
||||||
|
void kdb_syslog_data(char *syslog_data[4])
|
||||||
|
{
|
||||||
|
syslog_data[0] = log_buf;
|
||||||
|
syslog_data[1] = log_buf + log_buf_len;
|
||||||
|
syslog_data[2] = log_buf + log_end -
|
||||||
|
(logged_chars < log_buf_len ? logged_chars : log_buf_len);
|
||||||
|
syslog_data[3] = log_buf + log_end;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call the console drivers on a range of log_buf
|
* Call the console drivers on a range of log_buf
|
||||||
*/
|
*/
|
||||||
|
@ -586,6 +603,14 @@ asmlinkage int printk(const char *fmt, ...)
|
||||||
va_list args;
|
va_list args;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
if (unlikely(kdb_trap_printk)) {
|
||||||
|
va_start(args, fmt);
|
||||||
|
r = vkdb_printf(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
r = vprintk(fmt, args);
|
r = vprintk(fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
|
@ -7759,9 +7759,9 @@ void normalize_rt_tasks(void)
|
||||||
|
|
||||||
#endif /* CONFIG_MAGIC_SYSRQ */
|
#endif /* CONFIG_MAGIC_SYSRQ */
|
||||||
|
|
||||||
#ifdef CONFIG_IA64
|
#if defined(CONFIG_IA64) || defined(CONFIG_KGDB_KDB)
|
||||||
/*
|
/*
|
||||||
* These functions are only useful for the IA64 MCA handling.
|
* These functions are only useful for the IA64 MCA handling, or kdb.
|
||||||
*
|
*
|
||||||
* They can only be called when the whole system has been
|
* They can only be called when the whole system has been
|
||||||
* stopped - every CPU needs to be quiescent, and no scheduling
|
* stopped - every CPU needs to be quiescent, and no scheduling
|
||||||
|
@ -7781,6 +7781,9 @@ struct task_struct *curr_task(int cpu)
|
||||||
return cpu_curr(cpu);
|
return cpu_curr(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* defined(CONFIG_IA64) || defined(CONFIG_KGDB_KDB) */
|
||||||
|
|
||||||
|
#ifdef CONFIG_IA64
|
||||||
/**
|
/**
|
||||||
* set_curr_task - set the current task for a given cpu.
|
* set_curr_task - set the current task for a given cpu.
|
||||||
* @cpu: the processor in question.
|
* @cpu: the processor in question.
|
||||||
|
|
|
@ -2735,3 +2735,43 @@ void __init signals_init(void)
|
||||||
{
|
{
|
||||||
sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC);
|
sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
/*
|
||||||
|
* kdb_send_sig_info - Allows kdb to send signals without exposing
|
||||||
|
* signal internals. This function checks if the required locks are
|
||||||
|
* available before calling the main signal code, to avoid kdb
|
||||||
|
* deadlocks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
kdb_send_sig_info(struct task_struct *t, struct siginfo *info)
|
||||||
|
{
|
||||||
|
static struct task_struct *kdb_prev_t;
|
||||||
|
int sig, new_t;
|
||||||
|
if (!spin_trylock(&t->sighand->siglock)) {
|
||||||
|
kdb_printf("Can't do kill command now.\n"
|
||||||
|
"The sigmask lock is held somewhere else in "
|
||||||
|
"kernel, try again later\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spin_unlock(&t->sighand->siglock);
|
||||||
|
new_t = kdb_prev_t != t;
|
||||||
|
kdb_prev_t = t;
|
||||||
|
if (t->state != TASK_RUNNING && new_t) {
|
||||||
|
kdb_printf("Process is not RUNNING, sending a signal from "
|
||||||
|
"kdb risks deadlock\n"
|
||||||
|
"on the run queue locks. "
|
||||||
|
"The signal has _not_ been sent.\n"
|
||||||
|
"Reissue the kill command if you want to risk "
|
||||||
|
"the deadlock.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sig = info->si_signo;
|
||||||
|
if (send_sig_info(sig, info, t))
|
||||||
|
kdb_printf("Fail to deliver Signal %d to process %d.\n",
|
||||||
|
sig, t->pid);
|
||||||
|
else
|
||||||
|
kdb_printf("Signal %d is sent to process %d.\n", sig, t->pid);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
|
|
|
@ -3,7 +3,7 @@ config HAVE_ARCH_KGDB
|
||||||
bool
|
bool
|
||||||
|
|
||||||
menuconfig KGDB
|
menuconfig KGDB
|
||||||
bool "KGDB: kernel debugging with remote gdb"
|
bool "KGDB: kernel debugger"
|
||||||
depends on HAVE_ARCH_KGDB
|
depends on HAVE_ARCH_KGDB
|
||||||
depends on DEBUG_KERNEL && EXPERIMENTAL
|
depends on DEBUG_KERNEL && EXPERIMENTAL
|
||||||
help
|
help
|
||||||
|
@ -57,4 +57,26 @@ config KGDB_TESTS_BOOT_STRING
|
||||||
information about other strings you could use beyond the
|
information about other strings you could use beyond the
|
||||||
default of V1F100.
|
default of V1F100.
|
||||||
|
|
||||||
|
config KGDB_LOW_LEVEL_TRAP
|
||||||
|
bool "KGDB: Allow debugging with traps in notifiers"
|
||||||
|
depends on X86 || MIPS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This will add an extra call back to kgdb for the breakpoint
|
||||||
|
exception handler on which will will allow kgdb to step
|
||||||
|
through a notify handler.
|
||||||
|
|
||||||
|
config KGDB_KDB
|
||||||
|
bool "KGDB_KDB: include kdb frontend for kgdb"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
KDB frontend for kernel
|
||||||
|
|
||||||
|
config KDB_KEYBOARD
|
||||||
|
bool "KGDB_KDB: keyboard as input device"
|
||||||
|
depends on VT && KGDB_KDB
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
KDB can use a PS/2 type keyboard for an input device
|
||||||
|
|
||||||
endif # KGDB
|
endif # KGDB
|
||||||
|
|
Loading…
Reference in a new issue