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:
@@ -75,7 +75,7 @@ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
|
||||
obj-$(CONFIG_GCOV_KERNEL) += gcov/
|
||||
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
obj-$(CONFIG_KGDB) += kgdb.o
|
||||
obj-$(CONFIG_KGDB) += debug/
|
||||
obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
|
||||
obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
|
||||
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/seq_file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kdb.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sched.h> /* for cond_resched */
|
||||
@@ -516,6 +517,26 @@ static int kallsyms_open(struct inode *inode, struct file *file)
|
||||
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 = {
|
||||
.open = kallsyms_open,
|
||||
.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);
|
||||
EXPORT_SYMBOL_GPL(module_mutex);
|
||||
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? */
|
||||
int modules_disabled = 0;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/kdb.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/kmsg_dump.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);
|
||||
}
|
||||
|
||||
#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
|
||||
*/
|
||||
@@ -586,6 +603,14 @@ asmlinkage int printk(const char *fmt, ...)
|
||||
va_list args;
|
||||
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);
|
||||
r = vprintk(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
@@ -7759,9 +7759,9 @@ void normalize_rt_tasks(void)
|
||||
|
||||
#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
|
||||
* 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);
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_IA64) || defined(CONFIG_KGDB_KDB) */
|
||||
|
||||
#ifdef CONFIG_IA64
|
||||
/**
|
||||
* set_curr_task - set the current task for a given cpu.
|
||||
* @cpu: the processor in question.
|
||||
|
||||
@@ -2735,3 +2735,43 @@ void __init signals_init(void)
|
||||
{
|
||||
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 */
|
||||
|
||||
Reference in New Issue
Block a user