Commit 5d5314d6 authored by Jason Wessel's avatar Jason Wessel

kdb: core for kgdb back end (1 of 2)

This patch contains only the kdb core.  Because the change set was
large, it was split.  The next patch in the series includes the
instrumentation into the core kernel which are mainly helper functions
for kdb.

This work is directly derived from kdb v4.4 found at:

ftp://oss.sgi.com/projects/kdb/download/v4.4/

The kdb internals have been re-organized to make them mostly platform
independent and to connect everything to the debug core which is used by
gdbstub (which has long been known as kgdb).

The original version of kdb was 58,000 lines worth of changes to
support x86.  From that implementation only the kdb shell, and basic
commands for memory access, runcontrol, lsmod, and dmesg where carried
forward.

This is a generic implementation which aims to cover all the current
architectures using the kgdb core: ppc, arm, x86, mips, sparc, sh and
blackfin.  More archictectures can be added by implementing the
architecture specific kgdb functions.

[mort@sgi.com: Compile fix with hugepages enabled]
[mort@sgi.com: Clean breakpoint code renaming kdba_ -> kdb_]
[mort@sgi.com: fix new line after printing registers]
[mort@sgi.com: Remove the concept of global vs. local breakpoints]
[mort@sgi.com: Rework kdb_si_swapinfo to use more generic name]
[mort@sgi.com: fix the information dump macros, remove 'arch' from the names]
[sfr@canb.auug.org.au: include fixup to include linux/slab.h]

CC: linux-arch@vger.kernel.org
Signed-off-by: default avatarJason Wessel <jason.wessel@windriver.com>
Signed-off-by: default avatarMartin Hicks <mort@sgi.com>
parent e8861129
#ifndef _KDB_H
#define _KDB_H
/*
* Kernel Debugger Architecture Independent Global Headers
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 2000-2007 Silicon Graphics, Inc. All Rights Reserved.
* Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
* Copyright (C) 2009 Jason Wessel <jason.wessel@windriver.com>
*/
#ifdef CONFIG_KGDB_KDB
#include <linux/init.h>
#include <linux/sched.h>
#include <asm/atomic.h>
#define KDB_POLL_FUNC_MAX 5
/*
* kdb_initial_cpu is initialized to -1, and is set to the cpu
* number whenever the kernel debugger is entered.
*/
extern int kdb_initial_cpu;
extern atomic_t kdb_event;
/*
* kdb_diemsg
*
* Contains a pointer to the last string supplied to the
* kernel 'die' panic function.
*/
extern const char *kdb_diemsg;
#define KDB_FLAG_EARLYKDB (1 << 0) /* set from boot parameter kdb=early */
#define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */
#define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */
#define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */
#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when
* kdb is off */
#define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available,
* kdb is disabled */
#define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do
* not use keyboard */
#define KDB_FLAG_NO_I8042 (1 << 7) /* No i8042 chip is available, do
* not use keyboard */
extern int kdb_flags; /* Global flags, see kdb_state for per cpu state */
extern void kdb_save_flags(void);
extern void kdb_restore_flags(void);
#define KDB_FLAG(flag) (kdb_flags & KDB_FLAG_##flag)
#define KDB_FLAG_SET(flag) ((void)(kdb_flags |= KDB_FLAG_##flag))
#define KDB_FLAG_CLEAR(flag) ((void)(kdb_flags &= ~KDB_FLAG_##flag))
/*
* External entry point for the kernel debugger. The pt_regs
* at the time of entry are supplied along with the reason for
* entry to the kernel debugger.
*/
typedef enum {
KDB_REASON_ENTER = 1, /* KDB_ENTER() trap/fault - regs valid */
KDB_REASON_ENTER_SLAVE, /* KDB_ENTER_SLAVE() trap/fault - regs valid */
KDB_REASON_BREAK, /* Breakpoint inst. - regs valid */
KDB_REASON_DEBUG, /* Debug Fault - regs valid */
KDB_REASON_OOPS, /* Kernel Oops - regs valid */
KDB_REASON_SWITCH, /* CPU switch - regs valid*/
KDB_REASON_KEYBOARD, /* Keyboard entry - regs valid */
KDB_REASON_NMI, /* Non-maskable interrupt; regs valid */
KDB_REASON_RECURSE, /* Recursive entry to kdb;
* regs probably valid */
KDB_REASON_SSTEP, /* Single Step trap. - regs valid */
} kdb_reason_t;
extern int kdb_printf(const char *, ...)
__attribute__ ((format (printf, 1, 2)));
typedef int (*kdb_printf_t)(const char *, ...)
__attribute__ ((format (printf, 1, 2)));
extern void kdb_init(int level);
/* Access to kdb specific polling devices */
typedef int (*get_char_func)(void);
extern get_char_func kdb_poll_funcs[];
extern int kdb_get_kbd_char(void);
static inline
int kdb_process_cpu(const struct task_struct *p)
{
unsigned int cpu = task_thread_info(p)->cpu;
if (cpu > num_possible_cpus())
cpu = 0;
return cpu;
}
/* kdb access to register set for stack dumping */
extern struct pt_regs *kdb_current_regs;
#else /* ! CONFIG_KGDB_KDB */
#define kdb_printf(...)
#define kdb_init(x)
#endif /* CONFIG_KGDB_KDB */
enum {
KDB_NOT_INITIALIZED,
KDB_INIT_EARLY,
KDB_INIT_FULL,
};
#endif /* !_KDB_H */
...@@ -3,3 +3,4 @@ ...@@ -3,3 +3,4 @@
# #
obj-$(CONFIG_KGDB) += debug_core.o gdbstub.o obj-$(CONFIG_KGDB) += debug_core.o gdbstub.o
obj-$(CONFIG_KGDB_KDB) += kdb/
# 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
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)
This diff is collapsed.
/*
* 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_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_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;
}
# 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
/*
* 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,
};
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);
/* 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;
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;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment