mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 00:38:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
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/
|
1079
kernel/debug/debug_core.c
Normal file
1079
kernel/debug/debug_core.c
Normal file
File diff suppressed because it is too large
Load diff
85
kernel/debug/debug_core.h
Normal file
85
kernel/debug/debug_core.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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;
|
||||
atomic_t *send_ready;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
int enter_kgdb;
|
||||
};
|
||||
|
||||
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);
|
||||
extern int kdb_common_init_state(struct kgdb_state *ks);
|
||||
extern int kdb_common_deinit_state(void);
|
||||
#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_ */
|
1145
kernel/debug/gdbstub.c
Normal file
1145
kernel/debug/gdbstub.c
Normal file
File diff suppressed because it is too large
Load diff
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)
|
553
kernel/debug/kdb/kdb_bp.c
Normal file
553
kernel/debug/kdb/kdb_bp.c
Normal file
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* 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 (strncasecmp(argv[nextarg], "datar", sizeof("datar")) == 0)
|
||||
bp->bp_type = BP_ACCESS_WATCHPOINT;
|
||||
else if (strncasecmp(argv[nextarg], "dataw", sizeof("dataw")) == 0)
|
||||
bp->bp_type = BP_WRITE_WATCHPOINT;
|
||||
else if (strncasecmp(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);
|
||||
#ifdef CONFIG_DEBUG_RODATA
|
||||
if (!bp->bp_type) {
|
||||
kdb_printf("Software breakpoints are unavailable.\n"
|
||||
" Change the kernel CONFIG_DEBUG_RODATA=n\n"
|
||||
" OR use hw breaks: help bph\n");
|
||||
}
|
||||
#endif
|
||||
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;
|
||||
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
|
||||
*/
|
||||
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) command.
|
||||
*
|
||||
* ss
|
||||
*
|
||||
* Parameters:
|
||||
* argc Argument count
|
||||
* argv Argument vector
|
||||
* Outputs:
|
||||
* None.
|
||||
* Returns:
|
||||
* KDB_CMD_SS for success, a kdb error if failure.
|
||||
* Locking:
|
||||
* None.
|
||||
* Remarks:
|
||||
*
|
||||
* Set the arch specific option to trigger a debug trap after the next
|
||||
* instruction.
|
||||
*/
|
||||
|
||||
static int kdb_ss(int argc, const char **argv)
|
||||
{
|
||||
if (argc != 0)
|
||||
return KDB_ARGCOUNT;
|
||||
/*
|
||||
* Set trace flag and go.
|
||||
*/
|
||||
KDB_STATE_SET(DOING_SS);
|
||||
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);
|
||||
/*
|
||||
* 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 "kdb_private.h"
|
||||
|
||||
|
||||
static void kdb_show_stack(struct task_struct *p, void *addr)
|
||||
{
|
||||
int old_lvl = console_loglevel;
|
||||
console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
|
||||
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;
|
||||
|
||||
/* Prompt after each proc in bta */
|
||||
kdbgetintenv("BTAPROMPT", &btaprompt);
|
||||
|
||||
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 (KDB_FLAG(CMD_INTERRUPT))
|
||||
return 0;
|
||||
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;
|
||||
}
|
31
kernel/debug/kdb/kdb_cmds
Normal file
31
kernel/debug/kdb/kdb_cmds
Normal file
|
@ -0,0 +1,31 @@
|
|||
# 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"
|
||||
pid R
|
||||
-dumpcommon
|
||||
-bta
|
||||
endefcmd
|
||||
|
||||
defcmd dumpcpu "" "Same as dumpall but only tasks on cpus"
|
||||
pid R
|
||||
-dumpcommon
|
||||
-btc
|
||||
endefcmd
|
||||
|
182
kernel/debug/kdb/kdb_debugger.c
Normal file
182
kernel/debug/kdb/kdb_debugger.c
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* 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 <linux/export.h>
|
||||
#include <linux/hardirq.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);
|
||||
|
||||
static struct kgdb_state *kdb_ks;
|
||||
|
||||
int kdb_common_init_state(struct kgdb_state *ks)
|
||||
{
|
||||
kdb_initial_cpu = atomic_read(&kgdb_active);
|
||||
kdb_current_task = kgdb_info[ks->cpu].task;
|
||||
kdb_current_regs = kgdb_info[ks->cpu].debuggerinfo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kdb_common_deinit_state(void)
|
||||
{
|
||||
kdb_initial_cpu = -1;
|
||||
kdb_current_task = NULL;
|
||||
kdb_current_regs = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
kdb_ks = ks;
|
||||
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;
|
||||
|
||||
if (ks->err_code == KDB_REASON_SYSTEM_NMI && ks->signo == SIGTRAP)
|
||||
reason = KDB_REASON_SYSTEM_NMI;
|
||||
|
||||
else if (in_nmi())
|
||||
reason = KDB_REASON_NMI;
|
||||
|
||||
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_common_init_state(ks);
|
||||
/* Remove any breakpoints as needed by kdb and clear single step */
|
||||
kdb_bp_remove();
|
||||
KDB_STATE_CLEAR(DOING_SS);
|
||||
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);
|
||||
}
|
||||
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_common_deinit_state();
|
||||
KDB_STATE_CLEAR(PAGER);
|
||||
kdbnearsym_cleanup();
|
||||
if (error == KDB_CMD_KGDB) {
|
||||
if (KDB_STATE(DOING_KGDB))
|
||||
KDB_STATE_CLEAR(DOING_KGDB);
|
||||
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;
|
||||
}
|
||||
|
||||
void kdb_gdb_state_pass(char *buf)
|
||||
{
|
||||
gdbstub_state(kdb_ks, buf);
|
||||
}
|
860
kernel/debug/kdb/kdb_io.c
Normal file
860
kernel/debug/kdb/kdb_io.c
Normal file
|
@ -0,0 +1,860 @@
|
|||
/*
|
||||
* 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 int kgdb_transition_check(char *buffer)
|
||||
{
|
||||
if (buffer[0] != '+' && buffer[0] != '$') {
|
||||
KDB_STATE_SET(KGDB_TRANS);
|
||||
kdb_printf("%s", buffer);
|
||||
} else {
|
||||
int slen = strlen(buffer);
|
||||
if (slen > 3 && buffer[slen - 3] == '#') {
|
||||
kdb_gdb_state_pass(buffer);
|
||||
strcpy(buffer, "kgdb");
|
||||
KDB_STATE_SET(DOING_KGDB);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
static int last_crlf;
|
||||
|
||||
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;
|
||||
if (key != 10 && key != 13)
|
||||
last_crlf = 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 10: /* new line */
|
||||
case 13: /* carriage return */
|
||||
/* handle \n after \r */
|
||||
if (last_crlf && last_crlf != key)
|
||||
break;
|
||||
last_crlf = key;
|
||||
*lastchar++ = '\n';
|
||||
*lastchar++ = '\0';
|
||||
if (!KDB_STATE(KGDB_TRANS)) {
|
||||
KDB_STATE_SET(KGDB_TRANS);
|
||||
kdb_printf("%s", buffer);
|
||||
}
|
||||
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)) {
|
||||
if (kgdb_transition_check(buffer))
|
||||
return buffer;
|
||||
} else {
|
||||
kdb_printf("%c", key);
|
||||
}
|
||||
}
|
||||
/* Special escape to kgdb */
|
||||
if (lastchar - buffer >= 5 &&
|
||||
strcmp(lastchar - 5, "$?#3f") == 0) {
|
||||
kdb_gdb_state_pass(lastchar - 5);
|
||||
strcpy(buffer, "kgdb");
|
||||
KDB_STATE_SET(DOING_KGDB);
|
||||
return buffer;
|
||||
}
|
||||
if (lastchar - buffer >= 11 &&
|
||||
strcmp(lastchar - 11, "$qSupported") == 0) {
|
||||
kdb_gdb_state_pass(lastchar - 11);
|
||||
strcpy(buffer, "kgdb");
|
||||
KDB_STATE_SET(DOING_KGDB);
|
||||
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 colcount;
|
||||
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("COLUMNS", &colcount);
|
||||
if (diag || colcount <= 1)
|
||||
colcount = 80;
|
||||
|
||||
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 && !dbg_io_ops->is_console) {
|
||||
len = retlen;
|
||||
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 = CONSOLE_LOGLEVEL_SILENT;
|
||||
printk(KERN_INFO "%s", kdb_buffer);
|
||||
}
|
||||
|
||||
if (KDB_STATE(PAGER)) {
|
||||
/*
|
||||
* Check printed string to decide how to bump the
|
||||
* kdb_nextline to control when the more prompt should
|
||||
* show up.
|
||||
*/
|
||||
int got = 0;
|
||||
len = retlen;
|
||||
while (len--) {
|
||||
if (kdb_buffer[len] == '\n') {
|
||||
kdb_nextline++;
|
||||
got = 0;
|
||||
} else if (kdb_buffer[len] == '\r') {
|
||||
got = 0;
|
||||
} else {
|
||||
got++;
|
||||
}
|
||||
}
|
||||
kdb_nextline += got / (colcount + 1);
|
||||
}
|
||||
|
||||
/* check for having reached the LINES number of printed lines */
|
||||
if (kdb_nextline >= linecount) {
|
||||
char buf1[16] = "";
|
||||
|
||||
/* 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> ";
|
||||
|
||||
kdb_input_flush();
|
||||
c = console_drivers;
|
||||
|
||||
if (dbg_io_ops && !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("\r");
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kdb_printf);
|
263
kernel/debug/kdb/kdb_keyboard.c
Normal file
263
kernel/debug/kdb/kdb_keyboard.c
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* 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;
|
||||
static int kbd_last_ret;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
if (scancode == 0x9c)
|
||||
kbd_last_ret = 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 == 0x1c) {
|
||||
kbd_last_ret = 1;
|
||||
return 13;
|
||||
}
|
||||
|
||||
return keychar & 0xff;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kdb_get_kbd_char);
|
||||
|
||||
/*
|
||||
* Best effort cleanup of ENTER break codes on leaving KDB. Called on
|
||||
* exiting KDB, when we know we processed an ENTER or KP ENTER scan
|
||||
* code.
|
||||
*/
|
||||
void kdb_kbd_cleanup_state(void)
|
||||
{
|
||||
int scancode, scanstatus;
|
||||
|
||||
/*
|
||||
* Nothing to clean up, since either
|
||||
* ENTER was never pressed, or has already
|
||||
* gotten cleaned up.
|
||||
*/
|
||||
if (!kbd_last_ret)
|
||||
return;
|
||||
|
||||
kbd_last_ret = 0;
|
||||
/*
|
||||
* Enter key. Need to absorb the break code here, lest it gets
|
||||
* leaked out if we exit KDB as the result of processing 'g'.
|
||||
*
|
||||
* This has several interesting implications:
|
||||
* + Need to handle KP ENTER, which has break code 0xe0 0x9c.
|
||||
* + Need to handle repeat ENTER and repeat KP ENTER. Repeats
|
||||
* only get a break code at the end of the repeated
|
||||
* sequence. This means we can't propagate the repeated key
|
||||
* press, and must swallow it away.
|
||||
* + Need to handle possible PS/2 mouse input.
|
||||
* + Need to handle mashed keys.
|
||||
*/
|
||||
|
||||
while (1) {
|
||||
while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0)
|
||||
cpu_relax();
|
||||
|
||||
/*
|
||||
* Fetch the scancode.
|
||||
*/
|
||||
scancode = inb(KBD_DATA_REG);
|
||||
scanstatus = inb(KBD_STATUS_REG);
|
||||
|
||||
/*
|
||||
* Skip mouse input.
|
||||
*/
|
||||
if (scanstatus & KBD_STAT_MOUSE_OBF)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If we see 0xe0, this is either a break code for KP
|
||||
* ENTER, or a repeat make for KP ENTER. Either way,
|
||||
* since the second byte is equivalent to an ENTER,
|
||||
* skip the 0xe0 and try again.
|
||||
*
|
||||
* If we see 0x1c, this must be a repeat ENTER or KP
|
||||
* ENTER (and we swallowed 0xe0 before). Try again.
|
||||
*
|
||||
* We can also see make and break codes for other keys
|
||||
* mashed before or after pressing ENTER. Thus, if we
|
||||
* see anything other than 0x9c, we have to try again.
|
||||
*
|
||||
* Note, if you held some key as ENTER was depressed,
|
||||
* that break code would get leaked out.
|
||||
*/
|
||||
if (scancode != 0x9c)
|
||||
continue;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
2879
kernel/debug/kdb/kdb_main.c
Normal file
2879
kernel/debug/kdb/kdb_main.c
Normal file
File diff suppressed because it is too large
Load diff
260
kernel/debug/kdb/kdb_private.h
Normal file
260
kernel/debug/kdb/kdb_private.h
Normal file
|
@ -0,0 +1,260 @@
|
|||
#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 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_KGDB (-1005)
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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_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 kdbgetu64arg(const char *, u64 *);
|
||||
extern char *kdbgetenv(const char *);
|
||||
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_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_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_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 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 char *kdb_getstr(char *, size_t, char *);
|
||||
extern void kdb_gdb_state_pass(char *buf);
|
||||
|
||||
/* 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_KDB_KEYBOARD
|
||||
extern void kdb_kbd_cleanup_state(void);
|
||||
#else /* ! CONFIG_KDB_KEYBOARD */
|
||||
#define kdb_kbd_cleanup_state()
|
||||
#endif /* ! CONFIG_KDB_KEYBOARD */
|
||||
|
||||
#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 = 0;
|
||||
unsigned long offset = 0;
|
||||
#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);
|
||||
memcpy(res, vaddr + (addr & (PAGE_SIZE - 1)), size);
|
||||
kunmap_atomic(vaddr);
|
||||
|
||||
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 careful 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 (is_idle_task(p)) {
|
||||
/* 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];
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue