Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb: debug_core,kdb: fix crash when arch does not have single step kgdb,x86: use macro HBP_NUM to replace magic number 4 kgdb,mips: remove unused kgdb_cpu_doing_single_step operations mm,kdb,kgdb: Add a debug reference for the kdb kmap usage KGDB: Remove set but unused newPC ftrace,kdb: Allow dumping a specific cpu's buffer with ftdump ftrace,kdb: Extend kdb to be able to dump the ftrace buffer kgdb,powerpc: Replace hardcoded offset by BREAK_INSTR_SIZE arm,kgdb: Add ability to trap into debugger on notify_die gdbstub: do not directly use dbg_reg_def[] in gdb_cmd_reg_set() gdbstub: Implement gdbserial 'p' and 'P' packets kgdb,arm: Individual register get/set for arm kgdb,mips: Individual register get/set for mips kgdb,x86: Individual register get/set for x86 kgdb,kdb: individual register set and and get API gdbstub: Optimize kgdb's "thread:" response for the gdb serial protocol kgdb: remove custom hex_to_bin()implementation
This commit is contained in:
@ -70,11 +70,11 @@ extern int kgdb_fault_expected;
|
|||||||
#define _GP_REGS 16
|
#define _GP_REGS 16
|
||||||
#define _FP_REGS 8
|
#define _FP_REGS 8
|
||||||
#define _EXTRA_REGS 2
|
#define _EXTRA_REGS 2
|
||||||
#define GDB_MAX_REGS (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS)
|
#define DBG_MAX_REG_NUM (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS)
|
||||||
|
|
||||||
#define KGDB_MAX_NO_CPUS 1
|
#define KGDB_MAX_NO_CPUS 1
|
||||||
#define BUFMAX 400
|
#define BUFMAX 400
|
||||||
#define NUMREGBYTES (GDB_MAX_REGS << 2)
|
#define NUMREGBYTES (DBG_MAX_REG_NUM << 2)
|
||||||
#define NUMCRITREGBYTES (32 << 2)
|
#define NUMCRITREGBYTES (32 << 2)
|
||||||
|
|
||||||
#define _R0 0
|
#define _R0 0
|
||||||
@ -93,7 +93,7 @@ extern int kgdb_fault_expected;
|
|||||||
#define _SPT 13
|
#define _SPT 13
|
||||||
#define _LR 14
|
#define _LR 14
|
||||||
#define _PC 15
|
#define _PC 15
|
||||||
#define _CPSR (GDB_MAX_REGS - 1)
|
#define _CPSR (DBG_MAX_REG_NUM - 1)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* So that we can denote the end of a frame for tracing,
|
* So that we can denote the end of a frame for tracing,
|
||||||
|
@ -10,57 +10,62 @@
|
|||||||
* Deepak Saxena <dsaxena@plexity.net>
|
* Deepak Saxena <dsaxena@plexity.net>
|
||||||
*/
|
*/
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
#include <linux/kdebug.h>
|
||||||
#include <linux/kgdb.h>
|
#include <linux/kgdb.h>
|
||||||
#include <asm/traps.h>
|
#include <asm/traps.h>
|
||||||
|
|
||||||
/* Make a local copy of the registers passed into the handler (bletch) */
|
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
|
||||||
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
|
|
||||||
{
|
{
|
||||||
int regno;
|
{ "r0", 4, offsetof(struct pt_regs, ARM_r0)},
|
||||||
|
{ "r1", 4, offsetof(struct pt_regs, ARM_r1)},
|
||||||
|
{ "r2", 4, offsetof(struct pt_regs, ARM_r2)},
|
||||||
|
{ "r3", 4, offsetof(struct pt_regs, ARM_r3)},
|
||||||
|
{ "r4", 4, offsetof(struct pt_regs, ARM_r4)},
|
||||||
|
{ "r5", 4, offsetof(struct pt_regs, ARM_r5)},
|
||||||
|
{ "r6", 4, offsetof(struct pt_regs, ARM_r6)},
|
||||||
|
{ "r7", 4, offsetof(struct pt_regs, ARM_r7)},
|
||||||
|
{ "r8", 4, offsetof(struct pt_regs, ARM_r8)},
|
||||||
|
{ "r9", 4, offsetof(struct pt_regs, ARM_r9)},
|
||||||
|
{ "r10", 4, offsetof(struct pt_regs, ARM_r10)},
|
||||||
|
{ "fp", 4, offsetof(struct pt_regs, ARM_fp)},
|
||||||
|
{ "ip", 4, offsetof(struct pt_regs, ARM_ip)},
|
||||||
|
{ "sp", 4, offsetof(struct pt_regs, ARM_sp)},
|
||||||
|
{ "lr", 4, offsetof(struct pt_regs, ARM_lr)},
|
||||||
|
{ "pc", 4, offsetof(struct pt_regs, ARM_pc)},
|
||||||
|
{ "f0", 12, -1 },
|
||||||
|
{ "f1", 12, -1 },
|
||||||
|
{ "f2", 12, -1 },
|
||||||
|
{ "f3", 12, -1 },
|
||||||
|
{ "f4", 12, -1 },
|
||||||
|
{ "f5", 12, -1 },
|
||||||
|
{ "f6", 12, -1 },
|
||||||
|
{ "f7", 12, -1 },
|
||||||
|
{ "fps", 4, -1 },
|
||||||
|
{ "cpsr", 4, offsetof(struct pt_regs, ARM_cpsr)},
|
||||||
|
};
|
||||||
|
|
||||||
/* Initialize all to zero. */
|
char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
|
||||||
for (regno = 0; regno < GDB_MAX_REGS; regno++)
|
{
|
||||||
gdb_regs[regno] = 0;
|
if (regno >= DBG_MAX_REG_NUM || regno < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
gdb_regs[_R0] = kernel_regs->ARM_r0;
|
if (dbg_reg_def[regno].offset != -1)
|
||||||
gdb_regs[_R1] = kernel_regs->ARM_r1;
|
memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
|
||||||
gdb_regs[_R2] = kernel_regs->ARM_r2;
|
dbg_reg_def[regno].size);
|
||||||
gdb_regs[_R3] = kernel_regs->ARM_r3;
|
else
|
||||||
gdb_regs[_R4] = kernel_regs->ARM_r4;
|
memset(mem, 0, dbg_reg_def[regno].size);
|
||||||
gdb_regs[_R5] = kernel_regs->ARM_r5;
|
return dbg_reg_def[regno].name;
|
||||||
gdb_regs[_R6] = kernel_regs->ARM_r6;
|
|
||||||
gdb_regs[_R7] = kernel_regs->ARM_r7;
|
|
||||||
gdb_regs[_R8] = kernel_regs->ARM_r8;
|
|
||||||
gdb_regs[_R9] = kernel_regs->ARM_r9;
|
|
||||||
gdb_regs[_R10] = kernel_regs->ARM_r10;
|
|
||||||
gdb_regs[_FP] = kernel_regs->ARM_fp;
|
|
||||||
gdb_regs[_IP] = kernel_regs->ARM_ip;
|
|
||||||
gdb_regs[_SPT] = kernel_regs->ARM_sp;
|
|
||||||
gdb_regs[_LR] = kernel_regs->ARM_lr;
|
|
||||||
gdb_regs[_PC] = kernel_regs->ARM_pc;
|
|
||||||
gdb_regs[_CPSR] = kernel_regs->ARM_cpsr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
|
int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
|
||||||
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
|
|
||||||
{
|
{
|
||||||
kernel_regs->ARM_r0 = gdb_regs[_R0];
|
if (regno >= DBG_MAX_REG_NUM || regno < 0)
|
||||||
kernel_regs->ARM_r1 = gdb_regs[_R1];
|
return -EINVAL;
|
||||||
kernel_regs->ARM_r2 = gdb_regs[_R2];
|
|
||||||
kernel_regs->ARM_r3 = gdb_regs[_R3];
|
if (dbg_reg_def[regno].offset != -1)
|
||||||
kernel_regs->ARM_r4 = gdb_regs[_R4];
|
memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
|
||||||
kernel_regs->ARM_r5 = gdb_regs[_R5];
|
dbg_reg_def[regno].size);
|
||||||
kernel_regs->ARM_r6 = gdb_regs[_R6];
|
return 0;
|
||||||
kernel_regs->ARM_r7 = gdb_regs[_R7];
|
|
||||||
kernel_regs->ARM_r8 = gdb_regs[_R8];
|
|
||||||
kernel_regs->ARM_r9 = gdb_regs[_R9];
|
|
||||||
kernel_regs->ARM_r10 = gdb_regs[_R10];
|
|
||||||
kernel_regs->ARM_fp = gdb_regs[_FP];
|
|
||||||
kernel_regs->ARM_ip = gdb_regs[_IP];
|
|
||||||
kernel_regs->ARM_sp = gdb_regs[_SPT];
|
|
||||||
kernel_regs->ARM_lr = gdb_regs[_LR];
|
|
||||||
kernel_regs->ARM_pc = gdb_regs[_PC];
|
|
||||||
kernel_regs->ARM_cpsr = gdb_regs[_CPSR];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -176,6 +181,33 @@ void kgdb_roundup_cpus(unsigned long flags)
|
|||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __kgdb_notify(struct die_args *args, unsigned long cmd)
|
||||||
|
{
|
||||||
|
struct pt_regs *regs = args->regs;
|
||||||
|
|
||||||
|
if (kgdb_handle_exception(1, args->signr, cmd, regs))
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
return NOTIFY_STOP;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
local_irq_save(flags);
|
||||||
|
ret = __kgdb_notify(ptr, cmd);
|
||||||
|
local_irq_restore(flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block kgdb_notifier = {
|
||||||
|
.notifier_call = kgdb_notify,
|
||||||
|
.priority = -INT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kgdb_arch_init - Perform any architecture specific initalization.
|
* kgdb_arch_init - Perform any architecture specific initalization.
|
||||||
*
|
*
|
||||||
@ -184,6 +216,11 @@ void kgdb_roundup_cpus(unsigned long flags)
|
|||||||
*/
|
*/
|
||||||
int kgdb_arch_init(void)
|
int kgdb_arch_init(void)
|
||||||
{
|
{
|
||||||
|
int ret = register_die_notifier(&kgdb_notifier);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
register_undef_hook(&kgdb_brkpt_hook);
|
register_undef_hook(&kgdb_brkpt_hook);
|
||||||
register_undef_hook(&kgdb_compiled_brkpt_hook);
|
register_undef_hook(&kgdb_compiled_brkpt_hook);
|
||||||
|
|
||||||
@ -200,6 +237,7 @@ void kgdb_arch_exit(void)
|
|||||||
{
|
{
|
||||||
unregister_undef_hook(&kgdb_brkpt_hook);
|
unregister_undef_hook(&kgdb_brkpt_hook);
|
||||||
unregister_undef_hook(&kgdb_compiled_brkpt_hook);
|
unregister_undef_hook(&kgdb_compiled_brkpt_hook);
|
||||||
|
unregister_die_notifier(&kgdb_notifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,28 +8,27 @@
|
|||||||
#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \
|
#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \
|
||||||
(_MIPS_ISA == _MIPS_ISA_MIPS32)
|
(_MIPS_ISA == _MIPS_ISA_MIPS32)
|
||||||
|
|
||||||
#define KGDB_GDB_REG_SIZE 32
|
#define KGDB_GDB_REG_SIZE 32
|
||||||
|
#define GDB_SIZEOF_REG sizeof(u32)
|
||||||
|
|
||||||
#elif (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
|
#elif (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
|
||||||
(_MIPS_ISA == _MIPS_ISA_MIPS64)
|
(_MIPS_ISA == _MIPS_ISA_MIPS64)
|
||||||
|
|
||||||
#ifdef CONFIG_32BIT
|
#ifdef CONFIG_32BIT
|
||||||
#define KGDB_GDB_REG_SIZE 32
|
#define KGDB_GDB_REG_SIZE 32
|
||||||
|
#define GDB_SIZEOF_REG sizeof(u32)
|
||||||
#else /* CONFIG_CPU_32BIT */
|
#else /* CONFIG_CPU_32BIT */
|
||||||
#define KGDB_GDB_REG_SIZE 64
|
#define KGDB_GDB_REG_SIZE 64
|
||||||
|
#define GDB_SIZEOF_REG sizeof(u64)
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#error "Need to set KGDB_GDB_REG_SIZE for MIPS ISA"
|
#error "Need to set KGDB_GDB_REG_SIZE for MIPS ISA"
|
||||||
#endif /* _MIPS_ISA */
|
#endif /* _MIPS_ISA */
|
||||||
|
|
||||||
#define BUFMAX 2048
|
#define BUFMAX 2048
|
||||||
#if (KGDB_GDB_REG_SIZE == 32)
|
#define DBG_MAX_REG_NUM 72
|
||||||
#define NUMREGBYTES (90*sizeof(u32))
|
#define NUMREGBYTES (DBG_MAX_REG_NUM * sizeof(GDB_SIZEOF_REG))
|
||||||
#define NUMCRITREGBYTES (12*sizeof(u32))
|
#define NUMCRITREGBYTES (12 * sizeof(GDB_SIZEOF_REG))
|
||||||
#else
|
|
||||||
#define NUMREGBYTES (90*sizeof(u64))
|
|
||||||
#define NUMCRITREGBYTES (12*sizeof(u64))
|
|
||||||
#endif
|
|
||||||
#define BREAK_INSTR_SIZE 4
|
#define BREAK_INSTR_SIZE 4
|
||||||
#define CACHE_FLUSH_IS_SAFE 0
|
#define CACHE_FLUSH_IS_SAFE 0
|
||||||
|
|
||||||
|
@ -50,6 +50,151 @@ static struct hard_trap_info {
|
|||||||
{ 0, 0} /* Must be last */
|
{ 0, 0} /* Must be last */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
|
||||||
|
{
|
||||||
|
{ "zero", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) },
|
||||||
|
{ "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) },
|
||||||
|
{ "v0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) },
|
||||||
|
{ "v1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) },
|
||||||
|
{ "a0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) },
|
||||||
|
{ "a1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) },
|
||||||
|
{ "a2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) },
|
||||||
|
{ "a3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) },
|
||||||
|
{ "t0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) },
|
||||||
|
{ "t1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) },
|
||||||
|
{ "t2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) },
|
||||||
|
{ "t3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) },
|
||||||
|
{ "t4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) },
|
||||||
|
{ "t5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) },
|
||||||
|
{ "t6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) },
|
||||||
|
{ "t7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) },
|
||||||
|
{ "s0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[16]) },
|
||||||
|
{ "s1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[17]) },
|
||||||
|
{ "s2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[18]) },
|
||||||
|
{ "s3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[19]) },
|
||||||
|
{ "s4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[20]) },
|
||||||
|
{ "s5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[21]) },
|
||||||
|
{ "s6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[22]) },
|
||||||
|
{ "s7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[23]) },
|
||||||
|
{ "t8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[24]) },
|
||||||
|
{ "t9", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[25]) },
|
||||||
|
{ "k0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[26]) },
|
||||||
|
{ "k1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[27]) },
|
||||||
|
{ "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[28]) },
|
||||||
|
{ "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[29]) },
|
||||||
|
{ "s8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[30]) },
|
||||||
|
{ "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[31]) },
|
||||||
|
{ "sr", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_status) },
|
||||||
|
{ "lo", GDB_SIZEOF_REG, offsetof(struct pt_regs, lo) },
|
||||||
|
{ "hi", GDB_SIZEOF_REG, offsetof(struct pt_regs, hi) },
|
||||||
|
{ "bad", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_badvaddr) },
|
||||||
|
{ "cause", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_cause) },
|
||||||
|
{ "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_epc) },
|
||||||
|
{ "f0", GDB_SIZEOF_REG, 0 },
|
||||||
|
{ "f1", GDB_SIZEOF_REG, 1 },
|
||||||
|
{ "f2", GDB_SIZEOF_REG, 2 },
|
||||||
|
{ "f3", GDB_SIZEOF_REG, 3 },
|
||||||
|
{ "f4", GDB_SIZEOF_REG, 4 },
|
||||||
|
{ "f5", GDB_SIZEOF_REG, 5 },
|
||||||
|
{ "f6", GDB_SIZEOF_REG, 6 },
|
||||||
|
{ "f7", GDB_SIZEOF_REG, 7 },
|
||||||
|
{ "f8", GDB_SIZEOF_REG, 8 },
|
||||||
|
{ "f9", GDB_SIZEOF_REG, 9 },
|
||||||
|
{ "f10", GDB_SIZEOF_REG, 10 },
|
||||||
|
{ "f11", GDB_SIZEOF_REG, 11 },
|
||||||
|
{ "f12", GDB_SIZEOF_REG, 12 },
|
||||||
|
{ "f13", GDB_SIZEOF_REG, 13 },
|
||||||
|
{ "f14", GDB_SIZEOF_REG, 14 },
|
||||||
|
{ "f15", GDB_SIZEOF_REG, 15 },
|
||||||
|
{ "f16", GDB_SIZEOF_REG, 16 },
|
||||||
|
{ "f17", GDB_SIZEOF_REG, 17 },
|
||||||
|
{ "f18", GDB_SIZEOF_REG, 18 },
|
||||||
|
{ "f19", GDB_SIZEOF_REG, 19 },
|
||||||
|
{ "f20", GDB_SIZEOF_REG, 20 },
|
||||||
|
{ "f21", GDB_SIZEOF_REG, 21 },
|
||||||
|
{ "f22", GDB_SIZEOF_REG, 22 },
|
||||||
|
{ "f23", GDB_SIZEOF_REG, 23 },
|
||||||
|
{ "f24", GDB_SIZEOF_REG, 24 },
|
||||||
|
{ "f25", GDB_SIZEOF_REG, 25 },
|
||||||
|
{ "f26", GDB_SIZEOF_REG, 26 },
|
||||||
|
{ "f27", GDB_SIZEOF_REG, 27 },
|
||||||
|
{ "f28", GDB_SIZEOF_REG, 28 },
|
||||||
|
{ "f29", GDB_SIZEOF_REG, 29 },
|
||||||
|
{ "f30", GDB_SIZEOF_REG, 30 },
|
||||||
|
{ "f31", GDB_SIZEOF_REG, 31 },
|
||||||
|
{ "fsr", GDB_SIZEOF_REG, 0 },
|
||||||
|
{ "fir", GDB_SIZEOF_REG, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
int fp_reg;
|
||||||
|
|
||||||
|
if (regno < 0 || regno >= DBG_MAX_REG_NUM)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (dbg_reg_def[regno].offset != -1 && regno < 38) {
|
||||||
|
memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
} else if (current && dbg_reg_def[regno].offset != -1 && regno < 72) {
|
||||||
|
/* FP registers 38 -> 69 */
|
||||||
|
if (!(regs->cp0_status & ST0_CU1))
|
||||||
|
return 0;
|
||||||
|
if (regno == 70) {
|
||||||
|
/* Process the fcr31/fsr (register 70) */
|
||||||
|
memcpy((void *)¤t->thread.fpu.fcr31, mem,
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
goto out_save;
|
||||||
|
} else if (regno == 71) {
|
||||||
|
/* Ignore the fir (register 71) */
|
||||||
|
goto out_save;
|
||||||
|
}
|
||||||
|
fp_reg = dbg_reg_def[regno].offset;
|
||||||
|
memcpy((void *)¤t->thread.fpu.fpr[fp_reg], mem,
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
out_save:
|
||||||
|
restore_fp(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
int fp_reg;
|
||||||
|
|
||||||
|
if (regno >= DBG_MAX_REG_NUM || regno < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (dbg_reg_def[regno].offset != -1 && regno < 38) {
|
||||||
|
/* First 38 registers */
|
||||||
|
memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
} else if (current && dbg_reg_def[regno].offset != -1 && regno < 72) {
|
||||||
|
/* FP registers 38 -> 69 */
|
||||||
|
if (!(regs->cp0_status & ST0_CU1))
|
||||||
|
goto out;
|
||||||
|
save_fp(current);
|
||||||
|
if (regno == 70) {
|
||||||
|
/* Process the fcr31/fsr (register 70) */
|
||||||
|
memcpy(mem, (void *)¤t->thread.fpu.fcr31,
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
goto out;
|
||||||
|
} else if (regno == 71) {
|
||||||
|
/* Ignore the fir (register 71) */
|
||||||
|
memset(mem, 0, dbg_reg_def[regno].size);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fp_reg = dbg_reg_def[regno].offset;
|
||||||
|
memcpy(mem, (void *)¤t->thread.fpu.fpr[fp_reg],
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return dbg_reg_def[regno].name;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void arch_kgdb_breakpoint(void)
|
void arch_kgdb_breakpoint(void)
|
||||||
{
|
{
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
@ -84,64 +229,6 @@ static int compute_signal(int tt)
|
|||||||
return SIGHUP; /* default for things we don't know about */
|
return SIGHUP; /* default for things we don't know about */
|
||||||
}
|
}
|
||||||
|
|
||||||
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
int reg;
|
|
||||||
|
|
||||||
#if (KGDB_GDB_REG_SIZE == 32)
|
|
||||||
u32 *ptr = (u32 *)gdb_regs;
|
|
||||||
#else
|
|
||||||
u64 *ptr = (u64 *)gdb_regs;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (reg = 0; reg < 32; reg++)
|
|
||||||
*(ptr++) = regs->regs[reg];
|
|
||||||
|
|
||||||
*(ptr++) = regs->cp0_status;
|
|
||||||
*(ptr++) = regs->lo;
|
|
||||||
*(ptr++) = regs->hi;
|
|
||||||
*(ptr++) = regs->cp0_badvaddr;
|
|
||||||
*(ptr++) = regs->cp0_cause;
|
|
||||||
*(ptr++) = regs->cp0_epc;
|
|
||||||
|
|
||||||
/* FP REGS */
|
|
||||||
if (!(current && (regs->cp0_status & ST0_CU1)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
save_fp(current);
|
|
||||||
for (reg = 0; reg < 32; reg++)
|
|
||||||
*(ptr++) = current->thread.fpu.fpr[reg];
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
int reg;
|
|
||||||
|
|
||||||
#if (KGDB_GDB_REG_SIZE == 32)
|
|
||||||
const u32 *ptr = (u32 *)gdb_regs;
|
|
||||||
#else
|
|
||||||
const u64 *ptr = (u64 *)gdb_regs;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (reg = 0; reg < 32; reg++)
|
|
||||||
regs->regs[reg] = *(ptr++);
|
|
||||||
|
|
||||||
regs->cp0_status = *(ptr++);
|
|
||||||
regs->lo = *(ptr++);
|
|
||||||
regs->hi = *(ptr++);
|
|
||||||
regs->cp0_badvaddr = *(ptr++);
|
|
||||||
regs->cp0_cause = *(ptr++);
|
|
||||||
regs->cp0_epc = *(ptr++);
|
|
||||||
|
|
||||||
/* FP REGS from current */
|
|
||||||
if (!(current && (regs->cp0_status & ST0_CU1)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (reg = 0; reg < 32; reg++)
|
|
||||||
current->thread.fpu.fpr[reg] = *(ptr++);
|
|
||||||
restore_fp(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Similar to regs_to_gdb_regs() except that process is sleeping and so
|
* Similar to regs_to_gdb_regs() except that process is sleeping and so
|
||||||
* we may not be able to get all the info.
|
* we may not be able to get all the info.
|
||||||
@ -242,7 +329,7 @@ static struct notifier_block kgdb_notifier = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle the 's' and 'c' commands
|
* Handle the 'c' command
|
||||||
*/
|
*/
|
||||||
int kgdb_arch_handle_exception(int vector, int signo, int err_code,
|
int kgdb_arch_handle_exception(int vector, int signo, int err_code,
|
||||||
char *remcom_in_buffer, char *remcom_out_buffer,
|
char *remcom_in_buffer, char *remcom_out_buffer,
|
||||||
@ -250,20 +337,14 @@ int kgdb_arch_handle_exception(int vector, int signo, int err_code,
|
|||||||
{
|
{
|
||||||
char *ptr;
|
char *ptr;
|
||||||
unsigned long address;
|
unsigned long address;
|
||||||
int cpu = smp_processor_id();
|
|
||||||
|
|
||||||
switch (remcom_in_buffer[0]) {
|
switch (remcom_in_buffer[0]) {
|
||||||
case 's':
|
|
||||||
case 'c':
|
case 'c':
|
||||||
/* handle the optional parameter */
|
/* handle the optional parameter */
|
||||||
ptr = &remcom_in_buffer[1];
|
ptr = &remcom_in_buffer[1];
|
||||||
if (kgdb_hex2long(&ptr, &address))
|
if (kgdb_hex2long(&ptr, &address))
|
||||||
regs->cp0_epc = address;
|
regs->cp0_epc = address;
|
||||||
|
|
||||||
atomic_set(&kgdb_cpu_doing_single_step, -1);
|
|
||||||
if (remcom_in_buffer[0] == 's')
|
|
||||||
atomic_set(&kgdb_cpu_doing_single_step, cpu);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ static int kgdb_handle_breakpoint(struct pt_regs *regs)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
|
if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
|
||||||
regs->nip += 4;
|
regs->nip += BREAK_INSTR_SIZE;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,11 @@ enum regnames {
|
|||||||
GDB_FS, /* 14 */
|
GDB_FS, /* 14 */
|
||||||
GDB_GS, /* 15 */
|
GDB_GS, /* 15 */
|
||||||
};
|
};
|
||||||
|
#define GDB_ORIG_AX 41
|
||||||
|
#define DBG_MAX_REG_NUM 16
|
||||||
#define NUMREGBYTES ((GDB_GS+1)*4)
|
#define NUMREGBYTES ((GDB_GS+1)*4)
|
||||||
#else /* ! CONFIG_X86_32 */
|
#else /* ! CONFIG_X86_32 */
|
||||||
enum regnames64 {
|
enum regnames {
|
||||||
GDB_AX, /* 0 */
|
GDB_AX, /* 0 */
|
||||||
GDB_BX, /* 1 */
|
GDB_BX, /* 1 */
|
||||||
GDB_CX, /* 2 */
|
GDB_CX, /* 2 */
|
||||||
@ -59,15 +61,15 @@ enum regnames64 {
|
|||||||
GDB_R14, /* 14 */
|
GDB_R14, /* 14 */
|
||||||
GDB_R15, /* 15 */
|
GDB_R15, /* 15 */
|
||||||
GDB_PC, /* 16 */
|
GDB_PC, /* 16 */
|
||||||
|
GDB_PS, /* 17 */
|
||||||
|
GDB_CS, /* 18 */
|
||||||
|
GDB_SS, /* 19 */
|
||||||
};
|
};
|
||||||
|
#define GDB_ORIG_AX 57
|
||||||
enum regnames32 {
|
#define DBG_MAX_REG_NUM 20
|
||||||
GDB_PS = 34,
|
/* 17 64 bit regs and 3 32 bit regs */
|
||||||
GDB_CS,
|
#define NUMREGBYTES ((17 * 8) + (3 * 4))
|
||||||
GDB_SS,
|
#endif /* ! CONFIG_X86_32 */
|
||||||
};
|
|
||||||
#define NUMREGBYTES ((GDB_SS+1)*4)
|
|
||||||
#endif /* CONFIG_X86_32 */
|
|
||||||
|
|
||||||
static inline void arch_kgdb_breakpoint(void)
|
static inline void arch_kgdb_breakpoint(void)
|
||||||
{
|
{
|
||||||
|
@ -49,55 +49,94 @@
|
|||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
|
||||||
/**
|
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
|
||||||
* pt_regs_to_gdb_regs - Convert ptrace regs to GDB regs
|
|
||||||
* @gdb_regs: A pointer to hold the registers in the order GDB wants.
|
|
||||||
* @regs: The &struct pt_regs of the current process.
|
|
||||||
*
|
|
||||||
* Convert the pt_regs in @regs into the format for registers that
|
|
||||||
* GDB expects, stored in @gdb_regs.
|
|
||||||
*/
|
|
||||||
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_X86_32
|
|
||||||
u32 *gdb_regs32 = (u32 *)gdb_regs;
|
|
||||||
#endif
|
|
||||||
gdb_regs[GDB_AX] = regs->ax;
|
|
||||||
gdb_regs[GDB_BX] = regs->bx;
|
|
||||||
gdb_regs[GDB_CX] = regs->cx;
|
|
||||||
gdb_regs[GDB_DX] = regs->dx;
|
|
||||||
gdb_regs[GDB_SI] = regs->si;
|
|
||||||
gdb_regs[GDB_DI] = regs->di;
|
|
||||||
gdb_regs[GDB_BP] = regs->bp;
|
|
||||||
gdb_regs[GDB_PC] = regs->ip;
|
|
||||||
#ifdef CONFIG_X86_32
|
#ifdef CONFIG_X86_32
|
||||||
gdb_regs[GDB_PS] = regs->flags;
|
{ "ax", 4, offsetof(struct pt_regs, ax) },
|
||||||
gdb_regs[GDB_DS] = regs->ds;
|
{ "cx", 4, offsetof(struct pt_regs, cx) },
|
||||||
gdb_regs[GDB_ES] = regs->es;
|
{ "dx", 4, offsetof(struct pt_regs, dx) },
|
||||||
gdb_regs[GDB_CS] = regs->cs;
|
{ "bx", 4, offsetof(struct pt_regs, bx) },
|
||||||
gdb_regs[GDB_FS] = 0xFFFF;
|
{ "sp", 4, offsetof(struct pt_regs, sp) },
|
||||||
gdb_regs[GDB_GS] = 0xFFFF;
|
{ "bp", 4, offsetof(struct pt_regs, bp) },
|
||||||
if (user_mode_vm(regs)) {
|
{ "si", 4, offsetof(struct pt_regs, si) },
|
||||||
gdb_regs[GDB_SS] = regs->ss;
|
{ "di", 4, offsetof(struct pt_regs, di) },
|
||||||
gdb_regs[GDB_SP] = regs->sp;
|
{ "ip", 4, offsetof(struct pt_regs, ip) },
|
||||||
} else {
|
{ "flags", 4, offsetof(struct pt_regs, flags) },
|
||||||
gdb_regs[GDB_SS] = __KERNEL_DS;
|
{ "cs", 4, offsetof(struct pt_regs, cs) },
|
||||||
gdb_regs[GDB_SP] = kernel_stack_pointer(regs);
|
{ "ss", 4, offsetof(struct pt_regs, ss) },
|
||||||
}
|
{ "ds", 4, offsetof(struct pt_regs, ds) },
|
||||||
|
{ "es", 4, offsetof(struct pt_regs, es) },
|
||||||
|
{ "fs", 4, -1 },
|
||||||
|
{ "gs", 4, -1 },
|
||||||
#else
|
#else
|
||||||
gdb_regs[GDB_R8] = regs->r8;
|
{ "ax", 8, offsetof(struct pt_regs, ax) },
|
||||||
gdb_regs[GDB_R9] = regs->r9;
|
{ "bx", 8, offsetof(struct pt_regs, bx) },
|
||||||
gdb_regs[GDB_R10] = regs->r10;
|
{ "cx", 8, offsetof(struct pt_regs, cx) },
|
||||||
gdb_regs[GDB_R11] = regs->r11;
|
{ "dx", 8, offsetof(struct pt_regs, dx) },
|
||||||
gdb_regs[GDB_R12] = regs->r12;
|
{ "si", 8, offsetof(struct pt_regs, dx) },
|
||||||
gdb_regs[GDB_R13] = regs->r13;
|
{ "di", 8, offsetof(struct pt_regs, di) },
|
||||||
gdb_regs[GDB_R14] = regs->r14;
|
{ "bp", 8, offsetof(struct pt_regs, bp) },
|
||||||
gdb_regs[GDB_R15] = regs->r15;
|
{ "sp", 8, offsetof(struct pt_regs, sp) },
|
||||||
gdb_regs32[GDB_PS] = regs->flags;
|
{ "r8", 8, offsetof(struct pt_regs, r8) },
|
||||||
gdb_regs32[GDB_CS] = regs->cs;
|
{ "r9", 8, offsetof(struct pt_regs, r9) },
|
||||||
gdb_regs32[GDB_SS] = regs->ss;
|
{ "r10", 8, offsetof(struct pt_regs, r10) },
|
||||||
gdb_regs[GDB_SP] = kernel_stack_pointer(regs);
|
{ "r11", 8, offsetof(struct pt_regs, r11) },
|
||||||
|
{ "r12", 8, offsetof(struct pt_regs, r12) },
|
||||||
|
{ "r13", 8, offsetof(struct pt_regs, r13) },
|
||||||
|
{ "r14", 8, offsetof(struct pt_regs, r14) },
|
||||||
|
{ "r15", 8, offsetof(struct pt_regs, r15) },
|
||||||
|
{ "ip", 8, offsetof(struct pt_regs, ip) },
|
||||||
|
{ "flags", 4, offsetof(struct pt_regs, flags) },
|
||||||
|
{ "cs", 4, offsetof(struct pt_regs, cs) },
|
||||||
|
{ "ss", 4, offsetof(struct pt_regs, ss) },
|
||||||
#endif
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
#ifdef CONFIG_X86_32
|
||||||
|
regno == GDB_SS || regno == GDB_FS || regno == GDB_GS ||
|
||||||
|
#endif
|
||||||
|
regno == GDB_SP || regno == GDB_ORIG_AX)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (dbg_reg_def[regno].offset != -1)
|
||||||
|
memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
if (regno == GDB_ORIG_AX) {
|
||||||
|
memcpy(mem, ®s->orig_ax, sizeof(regs->orig_ax));
|
||||||
|
return "orig_ax";
|
||||||
|
}
|
||||||
|
if (regno >= DBG_MAX_REG_NUM || regno < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (dbg_reg_def[regno].offset != -1)
|
||||||
|
memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
|
||||||
|
dbg_reg_def[regno].size);
|
||||||
|
|
||||||
|
switch (regno) {
|
||||||
|
#ifdef CONFIG_X86_32
|
||||||
|
case GDB_SS:
|
||||||
|
if (!user_mode_vm(regs))
|
||||||
|
*(unsigned long *)mem = __KERNEL_DS;
|
||||||
|
break;
|
||||||
|
case GDB_SP:
|
||||||
|
if (!user_mode_vm(regs))
|
||||||
|
*(unsigned long *)mem = kernel_stack_pointer(regs);
|
||||||
|
break;
|
||||||
|
case GDB_GS:
|
||||||
|
case GDB_FS:
|
||||||
|
*(unsigned long *)mem = 0xFFFF;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return dbg_reg_def[regno].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,54 +189,13 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
|
|||||||
gdb_regs[GDB_SP] = p->thread.sp;
|
gdb_regs[GDB_SP] = p->thread.sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* gdb_regs_to_pt_regs - Convert GDB regs to ptrace regs.
|
|
||||||
* @gdb_regs: A pointer to hold the registers we've received from GDB.
|
|
||||||
* @regs: A pointer to a &struct pt_regs to hold these values in.
|
|
||||||
*
|
|
||||||
* Convert the GDB regs in @gdb_regs into the pt_regs, and store them
|
|
||||||
* in @regs.
|
|
||||||
*/
|
|
||||||
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
#ifndef CONFIG_X86_32
|
|
||||||
u32 *gdb_regs32 = (u32 *)gdb_regs;
|
|
||||||
#endif
|
|
||||||
regs->ax = gdb_regs[GDB_AX];
|
|
||||||
regs->bx = gdb_regs[GDB_BX];
|
|
||||||
regs->cx = gdb_regs[GDB_CX];
|
|
||||||
regs->dx = gdb_regs[GDB_DX];
|
|
||||||
regs->si = gdb_regs[GDB_SI];
|
|
||||||
regs->di = gdb_regs[GDB_DI];
|
|
||||||
regs->bp = gdb_regs[GDB_BP];
|
|
||||||
regs->ip = gdb_regs[GDB_PC];
|
|
||||||
#ifdef CONFIG_X86_32
|
|
||||||
regs->flags = gdb_regs[GDB_PS];
|
|
||||||
regs->ds = gdb_regs[GDB_DS];
|
|
||||||
regs->es = gdb_regs[GDB_ES];
|
|
||||||
regs->cs = gdb_regs[GDB_CS];
|
|
||||||
#else
|
|
||||||
regs->r8 = gdb_regs[GDB_R8];
|
|
||||||
regs->r9 = gdb_regs[GDB_R9];
|
|
||||||
regs->r10 = gdb_regs[GDB_R10];
|
|
||||||
regs->r11 = gdb_regs[GDB_R11];
|
|
||||||
regs->r12 = gdb_regs[GDB_R12];
|
|
||||||
regs->r13 = gdb_regs[GDB_R13];
|
|
||||||
regs->r14 = gdb_regs[GDB_R14];
|
|
||||||
regs->r15 = gdb_regs[GDB_R15];
|
|
||||||
regs->flags = gdb_regs32[GDB_PS];
|
|
||||||
regs->cs = gdb_regs32[GDB_CS];
|
|
||||||
regs->ss = gdb_regs32[GDB_SS];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct hw_breakpoint {
|
static struct hw_breakpoint {
|
||||||
unsigned enabled;
|
unsigned enabled;
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
int len;
|
int len;
|
||||||
int type;
|
int type;
|
||||||
struct perf_event **pev;
|
struct perf_event **pev;
|
||||||
} breakinfo[4];
|
} breakinfo[HBP_NUM];
|
||||||
|
|
||||||
static unsigned long early_dr7;
|
static unsigned long early_dr7;
|
||||||
|
|
||||||
@ -205,7 +203,7 @@ static void kgdb_correct_hw_break(void)
|
|||||||
{
|
{
|
||||||
int breakno;
|
int breakno;
|
||||||
|
|
||||||
for (breakno = 0; breakno < 4; breakno++) {
|
for (breakno = 0; breakno < HBP_NUM; breakno++) {
|
||||||
struct perf_event *bp;
|
struct perf_event *bp;
|
||||||
struct arch_hw_breakpoint *info;
|
struct arch_hw_breakpoint *info;
|
||||||
int val;
|
int val;
|
||||||
@ -292,10 +290,10 @@ kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < HBP_NUM; i++)
|
||||||
if (breakinfo[i].addr == addr && breakinfo[i].enabled)
|
if (breakinfo[i].addr == addr && breakinfo[i].enabled)
|
||||||
break;
|
break;
|
||||||
if (i == 4)
|
if (i == HBP_NUM)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (hw_break_release_slot(i)) {
|
if (hw_break_release_slot(i)) {
|
||||||
@ -313,7 +311,7 @@ static void kgdb_remove_all_hw_break(void)
|
|||||||
int cpu = raw_smp_processor_id();
|
int cpu = raw_smp_processor_id();
|
||||||
struct perf_event *bp;
|
struct perf_event *bp;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < HBP_NUM; i++) {
|
||||||
if (!breakinfo[i].enabled)
|
if (!breakinfo[i].enabled)
|
||||||
continue;
|
continue;
|
||||||
bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
|
bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
|
||||||
@ -333,10 +331,10 @@ kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < HBP_NUM; i++)
|
||||||
if (!breakinfo[i].enabled)
|
if (!breakinfo[i].enabled)
|
||||||
break;
|
break;
|
||||||
if (i == 4)
|
if (i == HBP_NUM)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
switch (bptype) {
|
switch (bptype) {
|
||||||
@ -397,7 +395,7 @@ void kgdb_disable_hw_debug(struct pt_regs *regs)
|
|||||||
|
|
||||||
/* Disable hardware debugging while we are in kgdb: */
|
/* Disable hardware debugging while we are in kgdb: */
|
||||||
set_debugreg(0UL, 7);
|
set_debugreg(0UL, 7);
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < HBP_NUM; i++) {
|
||||||
if (!breakinfo[i].enabled)
|
if (!breakinfo[i].enabled)
|
||||||
continue;
|
continue;
|
||||||
if (dbg_is_early) {
|
if (dbg_is_early) {
|
||||||
@ -458,7 +456,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
|
|||||||
{
|
{
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int newPC;
|
|
||||||
|
|
||||||
switch (remcomInBuffer[0]) {
|
switch (remcomInBuffer[0]) {
|
||||||
case 'c':
|
case 'c':
|
||||||
@ -469,8 +466,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
|
|||||||
linux_regs->ip = addr;
|
linux_regs->ip = addr;
|
||||||
case 'D':
|
case 'D':
|
||||||
case 'k':
|
case 'k':
|
||||||
newPC = linux_regs->ip;
|
|
||||||
|
|
||||||
/* clear the trace bit */
|
/* clear the trace bit */
|
||||||
linux_regs->flags &= ~X86_EFLAGS_TF;
|
linux_regs->flags &= ~X86_EFLAGS_TF;
|
||||||
atomic_set(&kgdb_cpu_doing_single_step, -1);
|
atomic_set(&kgdb_cpu_doing_single_step, -1);
|
||||||
@ -645,7 +640,7 @@ void kgdb_arch_late(void)
|
|||||||
attr.bp_len = HW_BREAKPOINT_LEN_1;
|
attr.bp_len = HW_BREAKPOINT_LEN_1;
|
||||||
attr.bp_type = HW_BREAKPOINT_W;
|
attr.bp_type = HW_BREAKPOINT_W;
|
||||||
attr.disabled = 1;
|
attr.disabled = 1;
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < HBP_NUM; i++) {
|
||||||
if (breakinfo[i].pev)
|
if (breakinfo[i].pev)
|
||||||
continue;
|
continue;
|
||||||
breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
|
breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
|
||||||
|
@ -90,6 +90,19 @@ struct kgdb_bkpt {
|
|||||||
enum kgdb_bpstate state;
|
enum kgdb_bpstate state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dbg_reg_def_t {
|
||||||
|
char *name;
|
||||||
|
int size;
|
||||||
|
int offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef DBG_MAX_REG_NUM
|
||||||
|
#define DBG_MAX_REG_NUM 0
|
||||||
|
#else
|
||||||
|
extern struct dbg_reg_def_t dbg_reg_def[];
|
||||||
|
extern char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs);
|
||||||
|
extern int dbg_set_reg(int regno, void *mem, struct pt_regs *regs);
|
||||||
|
#endif
|
||||||
#ifndef KGDB_MAX_BREAKPOINTS
|
#ifndef KGDB_MAX_BREAKPOINTS
|
||||||
# define KGDB_MAX_BREAKPOINTS 1000
|
# define KGDB_MAX_BREAKPOINTS 1000
|
||||||
#endif
|
#endif
|
||||||
@ -281,7 +294,7 @@ extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
|
|||||||
extern struct kgdb_io *dbg_io_ops;
|
extern struct kgdb_io *dbg_io_ops;
|
||||||
|
|
||||||
extern int kgdb_hex2long(char **ptr, unsigned long *long_val);
|
extern int kgdb_hex2long(char **ptr, unsigned long *long_val);
|
||||||
extern int kgdb_mem2hex(char *mem, char *buf, int count);
|
extern char *kgdb_mem2hex(char *mem, char *buf, int count);
|
||||||
extern int kgdb_hex2mem(char *buf, char *mem, int count);
|
extern int kgdb_hex2mem(char *buf, char *mem, int count);
|
||||||
|
|
||||||
extern int kgdb_isremovedbreak(unsigned long addr);
|
extern int kgdb_isremovedbreak(unsigned long addr);
|
||||||
|
@ -605,6 +605,8 @@ cpu_master_loop:
|
|||||||
if (dbg_kdb_mode) {
|
if (dbg_kdb_mode) {
|
||||||
kgdb_connected = 1;
|
kgdb_connected = 1;
|
||||||
error = kdb_stub(ks);
|
error = kdb_stub(ks);
|
||||||
|
if (error == -1)
|
||||||
|
continue;
|
||||||
kgdb_connected = 0;
|
kgdb_connected = 0;
|
||||||
} else {
|
} else {
|
||||||
error = gdb_serial_stub(ks);
|
error = gdb_serial_stub(ks);
|
||||||
|
@ -52,17 +52,6 @@ static unsigned long gdb_regs[(NUMREGBYTES +
|
|||||||
* GDB remote protocol parser:
|
* GDB remote protocol parser:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int hex(char ch)
|
|
||||||
{
|
|
||||||
if ((ch >= 'a') && (ch <= 'f'))
|
|
||||||
return ch - 'a' + 10;
|
|
||||||
if ((ch >= '0') && (ch <= '9'))
|
|
||||||
return ch - '0';
|
|
||||||
if ((ch >= 'A') && (ch <= 'F'))
|
|
||||||
return ch - 'A' + 10;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KGDB_KDB
|
#ifdef CONFIG_KGDB_KDB
|
||||||
static int gdbstub_read_wait(void)
|
static int gdbstub_read_wait(void)
|
||||||
{
|
{
|
||||||
@ -123,8 +112,8 @@ static void get_packet(char *buffer)
|
|||||||
buffer[count] = 0;
|
buffer[count] = 0;
|
||||||
|
|
||||||
if (ch == '#') {
|
if (ch == '#') {
|
||||||
xmitcsum = hex(gdbstub_read_wait()) << 4;
|
xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4;
|
||||||
xmitcsum += hex(gdbstub_read_wait());
|
xmitcsum += hex_to_bin(gdbstub_read_wait());
|
||||||
|
|
||||||
if (checksum != xmitcsum)
|
if (checksum != xmitcsum)
|
||||||
/* failed checksum */
|
/* failed checksum */
|
||||||
@ -236,7 +225,7 @@ void gdbstub_msg_write(const char *s, int len)
|
|||||||
* buf. Return a pointer to the last char put in buf (null). May
|
* buf. Return a pointer to the last char put in buf (null). May
|
||||||
* return an error.
|
* return an error.
|
||||||
*/
|
*/
|
||||||
int kgdb_mem2hex(char *mem, char *buf, int count)
|
char *kgdb_mem2hex(char *mem, char *buf, int count)
|
||||||
{
|
{
|
||||||
char *tmp;
|
char *tmp;
|
||||||
int err;
|
int err;
|
||||||
@ -248,17 +237,16 @@ int kgdb_mem2hex(char *mem, char *buf, int count)
|
|||||||
tmp = buf + count;
|
tmp = buf + count;
|
||||||
|
|
||||||
err = probe_kernel_read(tmp, mem, count);
|
err = probe_kernel_read(tmp, mem, count);
|
||||||
if (!err) {
|
if (err)
|
||||||
while (count > 0) {
|
return NULL;
|
||||||
buf = pack_hex_byte(buf, *tmp);
|
while (count > 0) {
|
||||||
tmp++;
|
buf = pack_hex_byte(buf, *tmp);
|
||||||
count--;
|
tmp++;
|
||||||
}
|
count--;
|
||||||
|
|
||||||
*buf = 0;
|
|
||||||
}
|
}
|
||||||
|
*buf = 0;
|
||||||
|
|
||||||
return err;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -280,8 +268,8 @@ int kgdb_hex2mem(char *buf, char *mem, int count)
|
|||||||
tmp_hex = tmp_raw - 1;
|
tmp_hex = tmp_raw - 1;
|
||||||
while (tmp_hex >= buf) {
|
while (tmp_hex >= buf) {
|
||||||
tmp_raw--;
|
tmp_raw--;
|
||||||
*tmp_raw = hex(*tmp_hex--);
|
*tmp_raw = hex_to_bin(*tmp_hex--);
|
||||||
*tmp_raw |= hex(*tmp_hex--) << 4;
|
*tmp_raw |= hex_to_bin(*tmp_hex--) << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
return probe_kernel_write(mem, tmp_raw, count);
|
return probe_kernel_write(mem, tmp_raw, count);
|
||||||
@ -304,7 +292,7 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val)
|
|||||||
(*ptr)++;
|
(*ptr)++;
|
||||||
}
|
}
|
||||||
while (**ptr) {
|
while (**ptr) {
|
||||||
hex_val = hex(**ptr);
|
hex_val = hex_to_bin(**ptr);
|
||||||
if (hex_val < 0)
|
if (hex_val < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -339,6 +327,32 @@ static int kgdb_ebin2mem(char *buf, char *mem, int count)
|
|||||||
return probe_kernel_write(mem, c, size);
|
return probe_kernel_write(mem, c, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DBG_MAX_REG_NUM > 0
|
||||||
|
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int idx = 0;
|
||||||
|
char *ptr = (char *)gdb_regs;
|
||||||
|
|
||||||
|
for (i = 0; i < DBG_MAX_REG_NUM; i++) {
|
||||||
|
dbg_get_reg(i, ptr + idx, regs);
|
||||||
|
idx += dbg_reg_def[i].size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int idx = 0;
|
||||||
|
char *ptr = (char *)gdb_regs;
|
||||||
|
|
||||||
|
for (i = 0; i < DBG_MAX_REG_NUM; i++) {
|
||||||
|
dbg_set_reg(i, ptr + idx, regs);
|
||||||
|
idx += dbg_reg_def[i].size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* DBG_MAX_REG_NUM > 0 */
|
||||||
|
|
||||||
/* Write memory due to an 'M' or 'X' packet. */
|
/* Write memory due to an 'M' or 'X' packet. */
|
||||||
static int write_mem_msg(int binary)
|
static int write_mem_msg(int binary)
|
||||||
{
|
{
|
||||||
@ -378,28 +392,31 @@ static void error_packet(char *pkt, int error)
|
|||||||
* remapped to negative TIDs.
|
* remapped to negative TIDs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define BUF_THREAD_ID_SIZE 16
|
#define BUF_THREAD_ID_SIZE 8
|
||||||
|
|
||||||
static char *pack_threadid(char *pkt, unsigned char *id)
|
static char *pack_threadid(char *pkt, unsigned char *id)
|
||||||
{
|
{
|
||||||
char *limit;
|
unsigned char *limit;
|
||||||
|
int lzero = 1;
|
||||||
|
|
||||||
limit = pkt + BUF_THREAD_ID_SIZE;
|
limit = id + (BUF_THREAD_ID_SIZE / 2);
|
||||||
while (pkt < limit)
|
while (id < limit) {
|
||||||
pkt = pack_hex_byte(pkt, *id++);
|
if (!lzero || *id != 0) {
|
||||||
|
pkt = pack_hex_byte(pkt, *id);
|
||||||
|
lzero = 0;
|
||||||
|
}
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lzero)
|
||||||
|
pkt = pack_hex_byte(pkt, 0);
|
||||||
|
|
||||||
return pkt;
|
return pkt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void int_to_threadref(unsigned char *id, int value)
|
static void int_to_threadref(unsigned char *id, int value)
|
||||||
{
|
{
|
||||||
unsigned char *scan;
|
put_unaligned_be32(value, id);
|
||||||
int i = 4;
|
|
||||||
|
|
||||||
scan = (unsigned char *)id;
|
|
||||||
while (i--)
|
|
||||||
*scan++ = 0;
|
|
||||||
put_unaligned_be32(value, scan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct task_struct *getthread(struct pt_regs *regs, int tid)
|
static struct task_struct *getthread(struct pt_regs *regs, int tid)
|
||||||
@ -463,8 +480,7 @@ static void gdb_cmd_status(struct kgdb_state *ks)
|
|||||||
pack_hex_byte(&remcom_out_buffer[1], ks->signo);
|
pack_hex_byte(&remcom_out_buffer[1], ks->signo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle the 'g' get registers request */
|
static void gdb_get_regs_helper(struct kgdb_state *ks)
|
||||||
static void gdb_cmd_getregs(struct kgdb_state *ks)
|
|
||||||
{
|
{
|
||||||
struct task_struct *thread;
|
struct task_struct *thread;
|
||||||
void *local_debuggerinfo;
|
void *local_debuggerinfo;
|
||||||
@ -505,6 +521,12 @@ static void gdb_cmd_getregs(struct kgdb_state *ks)
|
|||||||
*/
|
*/
|
||||||
sleeping_thread_to_gdb_regs(gdb_regs, thread);
|
sleeping_thread_to_gdb_regs(gdb_regs, thread);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the 'g' get registers request */
|
||||||
|
static void gdb_cmd_getregs(struct kgdb_state *ks)
|
||||||
|
{
|
||||||
|
gdb_get_regs_helper(ks);
|
||||||
kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES);
|
kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,13 +549,13 @@ static void gdb_cmd_memread(struct kgdb_state *ks)
|
|||||||
char *ptr = &remcom_in_buffer[1];
|
char *ptr = &remcom_in_buffer[1];
|
||||||
unsigned long length;
|
unsigned long length;
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
int err;
|
char *err;
|
||||||
|
|
||||||
if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' &&
|
if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' &&
|
||||||
kgdb_hex2long(&ptr, &length) > 0) {
|
kgdb_hex2long(&ptr, &length) > 0) {
|
||||||
err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length);
|
err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length);
|
||||||
if (err)
|
if (!err)
|
||||||
error_packet(remcom_out_buffer, err);
|
error_packet(remcom_out_buffer, -EINVAL);
|
||||||
} else {
|
} else {
|
||||||
error_packet(remcom_out_buffer, -EINVAL);
|
error_packet(remcom_out_buffer, -EINVAL);
|
||||||
}
|
}
|
||||||
@ -550,6 +572,60 @@ static void gdb_cmd_memwrite(struct kgdb_state *ks)
|
|||||||
strcpy(remcom_out_buffer, "OK");
|
strcpy(remcom_out_buffer, "OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DBG_MAX_REG_NUM > 0
|
||||||
|
static char *gdb_hex_reg_helper(int regnum, char *out)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < regnum; i++)
|
||||||
|
offset += dbg_reg_def[i].size;
|
||||||
|
return kgdb_mem2hex((char *)gdb_regs + offset, out,
|
||||||
|
dbg_reg_def[i].size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the 'p' individual regster get */
|
||||||
|
static void gdb_cmd_reg_get(struct kgdb_state *ks)
|
||||||
|
{
|
||||||
|
unsigned long regnum;
|
||||||
|
char *ptr = &remcom_in_buffer[1];
|
||||||
|
|
||||||
|
kgdb_hex2long(&ptr, ®num);
|
||||||
|
if (regnum >= DBG_MAX_REG_NUM) {
|
||||||
|
error_packet(remcom_out_buffer, -EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gdb_get_regs_helper(ks);
|
||||||
|
gdb_hex_reg_helper(regnum, remcom_out_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the 'P' individual regster set */
|
||||||
|
static void gdb_cmd_reg_set(struct kgdb_state *ks)
|
||||||
|
{
|
||||||
|
unsigned long regnum;
|
||||||
|
char *ptr = &remcom_in_buffer[1];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
kgdb_hex2long(&ptr, ®num);
|
||||||
|
if (*ptr++ != '=' ||
|
||||||
|
!(!kgdb_usethread || kgdb_usethread == current) ||
|
||||||
|
!dbg_get_reg(regnum, gdb_regs, ks->linux_regs)) {
|
||||||
|
error_packet(remcom_out_buffer, -EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memset(gdb_regs, 0, sizeof(gdb_regs));
|
||||||
|
while (i < sizeof(gdb_regs) * 2)
|
||||||
|
if (hex_to_bin(ptr[i]) >= 0)
|
||||||
|
i++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
i = i / 2;
|
||||||
|
kgdb_hex2mem(ptr, (char *)gdb_regs, i);
|
||||||
|
dbg_set_reg(regnum, gdb_regs, ks->linux_regs);
|
||||||
|
strcpy(remcom_out_buffer, "OK");
|
||||||
|
}
|
||||||
|
#endif /* DBG_MAX_REG_NUM > 0 */
|
||||||
|
|
||||||
/* Handle the 'X' memory binary write bytes */
|
/* Handle the 'X' memory binary write bytes */
|
||||||
static void gdb_cmd_binwrite(struct kgdb_state *ks)
|
static void gdb_cmd_binwrite(struct kgdb_state *ks)
|
||||||
{
|
{
|
||||||
@ -612,7 +688,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
|
|||||||
{
|
{
|
||||||
struct task_struct *g;
|
struct task_struct *g;
|
||||||
struct task_struct *p;
|
struct task_struct *p;
|
||||||
unsigned char thref[8];
|
unsigned char thref[BUF_THREAD_ID_SIZE];
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int i;
|
int i;
|
||||||
int cpu;
|
int cpu;
|
||||||
@ -632,8 +708,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
|
|||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
ks->thr_query = 0;
|
ks->thr_query = 0;
|
||||||
int_to_threadref(thref, -cpu - 2);
|
int_to_threadref(thref, -cpu - 2);
|
||||||
pack_threadid(ptr, thref);
|
ptr = pack_threadid(ptr, thref);
|
||||||
ptr += BUF_THREAD_ID_SIZE;
|
|
||||||
*(ptr++) = ',';
|
*(ptr++) = ',';
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -642,8 +717,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
|
|||||||
do_each_thread(g, p) {
|
do_each_thread(g, p) {
|
||||||
if (i >= ks->thr_query && !finished) {
|
if (i >= ks->thr_query && !finished) {
|
||||||
int_to_threadref(thref, p->pid);
|
int_to_threadref(thref, p->pid);
|
||||||
pack_threadid(ptr, thref);
|
ptr = pack_threadid(ptr, thref);
|
||||||
ptr += BUF_THREAD_ID_SIZE;
|
|
||||||
*(ptr++) = ',';
|
*(ptr++) = ',';
|
||||||
ks->thr_query++;
|
ks->thr_query++;
|
||||||
if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0)
|
if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0)
|
||||||
@ -858,11 +932,14 @@ int gdb_serial_stub(struct kgdb_state *ks)
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
int tmp;
|
int tmp;
|
||||||
|
|
||||||
/* Clear the out buffer. */
|
/* Initialize comm buffer and globals. */
|
||||||
memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
|
memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
|
||||||
|
kgdb_usethread = kgdb_info[ks->cpu].task;
|
||||||
|
ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid);
|
||||||
|
ks->pass_exception = 0;
|
||||||
|
|
||||||
if (kgdb_connected) {
|
if (kgdb_connected) {
|
||||||
unsigned char thref[8];
|
unsigned char thref[BUF_THREAD_ID_SIZE];
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
/* Reply to host that an exception has occurred */
|
/* Reply to host that an exception has occurred */
|
||||||
@ -876,10 +953,6 @@ int gdb_serial_stub(struct kgdb_state *ks)
|
|||||||
put_packet(remcom_out_buffer);
|
put_packet(remcom_out_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
kgdb_usethread = kgdb_info[ks->cpu].task;
|
|
||||||
ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid);
|
|
||||||
ks->pass_exception = 0;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
error = 0;
|
error = 0;
|
||||||
|
|
||||||
@ -904,6 +977,14 @@ int gdb_serial_stub(struct kgdb_state *ks)
|
|||||||
case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */
|
case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */
|
||||||
gdb_cmd_memwrite(ks);
|
gdb_cmd_memwrite(ks);
|
||||||
break;
|
break;
|
||||||
|
#if DBG_MAX_REG_NUM > 0
|
||||||
|
case 'p': /* pXX Return gdb register XX (in hex) */
|
||||||
|
gdb_cmd_reg_get(ks);
|
||||||
|
break;
|
||||||
|
case 'P': /* PXX=aaaa Set gdb register XX to aaaa (in hex) */
|
||||||
|
gdb_cmd_reg_set(ks);
|
||||||
|
break;
|
||||||
|
#endif /* DBG_MAX_REG_NUM > 0 */
|
||||||
case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */
|
case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */
|
||||||
gdb_cmd_binwrite(ks);
|
gdb_cmd_binwrite(ks);
|
||||||
break;
|
break;
|
||||||
|
@ -312,7 +312,7 @@ int kdbgetularg(const char *arg, unsigned long *value)
|
|||||||
|
|
||||||
if (endp == arg) {
|
if (endp == arg) {
|
||||||
/*
|
/*
|
||||||
* Try base 16, for us folks too lazy to type the
|
* Also try base 16, for us folks too lazy to type the
|
||||||
* leading 0x...
|
* leading 0x...
|
||||||
*/
|
*/
|
||||||
val = simple_strtoul(arg, &endp, 16);
|
val = simple_strtoul(arg, &endp, 16);
|
||||||
@ -325,6 +325,25 @@ int kdbgetularg(const char *arg, unsigned long *value)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kdbgetu64arg(const char *arg, u64 *value)
|
||||||
|
{
|
||||||
|
char *endp;
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
val = simple_strtoull(arg, &endp, 0);
|
||||||
|
|
||||||
|
if (endp == arg) {
|
||||||
|
|
||||||
|
val = simple_strtoull(arg, &endp, 16);
|
||||||
|
if (endp == arg)
|
||||||
|
return KDB_BADINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* kdb_set - This function implements the 'set' command. Alter an
|
* kdb_set - This function implements the 'set' command. Alter an
|
||||||
* existing environment variable or create a new one.
|
* existing environment variable or create a new one.
|
||||||
@ -1770,11 +1789,65 @@ static int kdb_go(int argc, const char **argv)
|
|||||||
*/
|
*/
|
||||||
static int kdb_rd(int argc, const char **argv)
|
static int kdb_rd(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
int diag = kdb_check_regs();
|
int len = kdb_check_regs();
|
||||||
if (diag)
|
#if DBG_MAX_REG_NUM > 0
|
||||||
return diag;
|
int i;
|
||||||
|
char *rname;
|
||||||
|
int rsize;
|
||||||
|
u64 reg64;
|
||||||
|
u32 reg32;
|
||||||
|
u16 reg16;
|
||||||
|
u8 reg8;
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
return len;
|
||||||
|
|
||||||
|
for (i = 0; i < DBG_MAX_REG_NUM; i++) {
|
||||||
|
rsize = dbg_reg_def[i].size * 2;
|
||||||
|
if (rsize > 16)
|
||||||
|
rsize = 2;
|
||||||
|
if (len + strlen(dbg_reg_def[i].name) + 4 + rsize > 80) {
|
||||||
|
len = 0;
|
||||||
|
kdb_printf("\n");
|
||||||
|
}
|
||||||
|
if (len)
|
||||||
|
len += kdb_printf(" ");
|
||||||
|
switch(dbg_reg_def[i].size * 8) {
|
||||||
|
case 8:
|
||||||
|
rname = dbg_get_reg(i, ®8, kdb_current_regs);
|
||||||
|
if (!rname)
|
||||||
|
break;
|
||||||
|
len += kdb_printf("%s: %02x", rname, reg8);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
rname = dbg_get_reg(i, ®16, kdb_current_regs);
|
||||||
|
if (!rname)
|
||||||
|
break;
|
||||||
|
len += kdb_printf("%s: %04x", rname, reg16);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
rname = dbg_get_reg(i, ®32, kdb_current_regs);
|
||||||
|
if (!rname)
|
||||||
|
break;
|
||||||
|
len += kdb_printf("%s: %08x", rname, reg32);
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
rname = dbg_get_reg(i, ®64, kdb_current_regs);
|
||||||
|
if (!rname)
|
||||||
|
break;
|
||||||
|
len += kdb_printf("%s: %016llx", rname, reg64);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
len += kdb_printf("%s: ??", dbg_reg_def[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kdb_printf("\n");
|
||||||
|
#else
|
||||||
|
if (len)
|
||||||
|
return len;
|
||||||
|
|
||||||
kdb_dumpregs(kdb_current_regs);
|
kdb_dumpregs(kdb_current_regs);
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1782,32 +1855,67 @@ static int kdb_rd(int argc, const char **argv)
|
|||||||
* kdb_rm - This function implements the 'rm' (register modify) command.
|
* kdb_rm - This function implements the 'rm' (register modify) command.
|
||||||
* rm register-name new-contents
|
* rm register-name new-contents
|
||||||
* Remarks:
|
* Remarks:
|
||||||
* Currently doesn't allow modification of control or
|
* Allows register modification with the same restrictions as gdb
|
||||||
* debug registers.
|
|
||||||
*/
|
*/
|
||||||
static int kdb_rm(int argc, const char **argv)
|
static int kdb_rm(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
|
#if DBG_MAX_REG_NUM > 0
|
||||||
int diag;
|
int diag;
|
||||||
int ind = 0;
|
const char *rname;
|
||||||
unsigned long contents;
|
int i;
|
||||||
|
u64 reg64;
|
||||||
|
u32 reg32;
|
||||||
|
u16 reg16;
|
||||||
|
u8 reg8;
|
||||||
|
|
||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
return KDB_ARGCOUNT;
|
return KDB_ARGCOUNT;
|
||||||
/*
|
/*
|
||||||
* Allow presence or absence of leading '%' symbol.
|
* Allow presence or absence of leading '%' symbol.
|
||||||
*/
|
*/
|
||||||
if (argv[1][0] == '%')
|
rname = argv[1];
|
||||||
ind = 1;
|
if (*rname == '%')
|
||||||
|
rname++;
|
||||||
|
|
||||||
diag = kdbgetularg(argv[2], &contents);
|
diag = kdbgetu64arg(argv[2], ®64);
|
||||||
if (diag)
|
if (diag)
|
||||||
return diag;
|
return diag;
|
||||||
|
|
||||||
diag = kdb_check_regs();
|
diag = kdb_check_regs();
|
||||||
if (diag)
|
if (diag)
|
||||||
return diag;
|
return diag;
|
||||||
|
|
||||||
|
diag = KDB_BADREG;
|
||||||
|
for (i = 0; i < DBG_MAX_REG_NUM; i++) {
|
||||||
|
if (strcmp(rname, dbg_reg_def[i].name) == 0) {
|
||||||
|
diag = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!diag) {
|
||||||
|
switch(dbg_reg_def[i].size * 8) {
|
||||||
|
case 8:
|
||||||
|
reg8 = reg64;
|
||||||
|
dbg_set_reg(i, ®8, kdb_current_regs);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
reg16 = reg64;
|
||||||
|
dbg_set_reg(i, ®16, kdb_current_regs);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
reg32 = reg64;
|
||||||
|
dbg_set_reg(i, ®32, kdb_current_regs);
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
dbg_set_reg(i, ®64, kdb_current_regs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diag;
|
||||||
|
#else
|
||||||
kdb_printf("ERROR: Register set currently not implemented\n");
|
kdb_printf("ERROR: Register set currently not implemented\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_MAGIC_SYSRQ)
|
#if defined(CONFIG_MAGIC_SYSRQ)
|
||||||
|
@ -57,5 +57,8 @@ obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
|
|||||||
obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o
|
obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o
|
||||||
obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o
|
obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o
|
||||||
obj-$(CONFIG_EVENT_TRACING) += power-traces.o
|
obj-$(CONFIG_EVENT_TRACING) += power-traces.o
|
||||||
|
ifeq ($(CONFIG_TRACING),y)
|
||||||
|
obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
|
||||||
|
endif
|
||||||
|
|
||||||
libftrace-y := ftrace.o
|
libftrace-y := ftrace.o
|
||||||
|
@ -101,10 +101,7 @@ static inline void ftrace_enable_cpu(void)
|
|||||||
preempt_enable();
|
preempt_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static cpumask_var_t __read_mostly tracing_buffer_mask;
|
cpumask_var_t __read_mostly tracing_buffer_mask;
|
||||||
|
|
||||||
#define for_each_tracing_cpu(cpu) \
|
|
||||||
for_each_cpu(cpu, tracing_buffer_mask)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ftrace_dump_on_oops - variable to dump ftrace buffer on oops
|
* ftrace_dump_on_oops - variable to dump ftrace buffer on oops
|
||||||
@ -1539,11 +1536,6 @@ int trace_vprintk(unsigned long ip, const char *fmt, va_list args)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(trace_vprintk);
|
EXPORT_SYMBOL_GPL(trace_vprintk);
|
||||||
|
|
||||||
enum trace_file_type {
|
|
||||||
TRACE_FILE_LAT_FMT = 1,
|
|
||||||
TRACE_FILE_ANNOTATE = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void trace_iterator_increment(struct trace_iterator *iter)
|
static void trace_iterator_increment(struct trace_iterator *iter)
|
||||||
{
|
{
|
||||||
/* Don't allow ftrace to trace into the ring buffers */
|
/* Don't allow ftrace to trace into the ring buffers */
|
||||||
@ -1641,7 +1633,7 @@ struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find the next real entry, and increment the iterator to the next entry */
|
/* Find the next real entry, and increment the iterator to the next entry */
|
||||||
static void *find_next_entry_inc(struct trace_iterator *iter)
|
void *trace_find_next_entry_inc(struct trace_iterator *iter)
|
||||||
{
|
{
|
||||||
iter->ent = __find_next_entry(iter, &iter->cpu,
|
iter->ent = __find_next_entry(iter, &iter->cpu,
|
||||||
&iter->lost_events, &iter->ts);
|
&iter->lost_events, &iter->ts);
|
||||||
@ -1676,19 +1668,19 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (iter->idx < 0)
|
if (iter->idx < 0)
|
||||||
ent = find_next_entry_inc(iter);
|
ent = trace_find_next_entry_inc(iter);
|
||||||
else
|
else
|
||||||
ent = iter;
|
ent = iter;
|
||||||
|
|
||||||
while (ent && iter->idx < i)
|
while (ent && iter->idx < i)
|
||||||
ent = find_next_entry_inc(iter);
|
ent = trace_find_next_entry_inc(iter);
|
||||||
|
|
||||||
iter->pos = *pos;
|
iter->pos = *pos;
|
||||||
|
|
||||||
return ent;
|
return ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tracing_iter_reset(struct trace_iterator *iter, int cpu)
|
void tracing_iter_reset(struct trace_iterator *iter, int cpu)
|
||||||
{
|
{
|
||||||
struct trace_array *tr = iter->tr;
|
struct trace_array *tr = iter->tr;
|
||||||
struct ring_buffer_event *event;
|
struct ring_buffer_event *event;
|
||||||
@ -2049,7 +2041,7 @@ int trace_empty(struct trace_iterator *iter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Called with trace_event_read_lock() held. */
|
/* Called with trace_event_read_lock() held. */
|
||||||
static enum print_line_t print_trace_line(struct trace_iterator *iter)
|
enum print_line_t print_trace_line(struct trace_iterator *iter)
|
||||||
{
|
{
|
||||||
enum print_line_t ret;
|
enum print_line_t ret;
|
||||||
|
|
||||||
@ -3211,7 +3203,7 @@ waitagain:
|
|||||||
|
|
||||||
trace_event_read_lock();
|
trace_event_read_lock();
|
||||||
trace_access_lock(iter->cpu_file);
|
trace_access_lock(iter->cpu_file);
|
||||||
while (find_next_entry_inc(iter) != NULL) {
|
while (trace_find_next_entry_inc(iter) != NULL) {
|
||||||
enum print_line_t ret;
|
enum print_line_t ret;
|
||||||
int len = iter->seq.len;
|
int len = iter->seq.len;
|
||||||
|
|
||||||
@ -3294,7 +3286,7 @@ tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter)
|
|||||||
if (ret != TRACE_TYPE_NO_CONSUME)
|
if (ret != TRACE_TYPE_NO_CONSUME)
|
||||||
trace_consume(iter);
|
trace_consume(iter);
|
||||||
rem -= count;
|
rem -= count;
|
||||||
if (!find_next_entry_inc(iter)) {
|
if (!trace_find_next_entry_inc(iter)) {
|
||||||
rem = 0;
|
rem = 0;
|
||||||
iter->ent = NULL;
|
iter->ent = NULL;
|
||||||
break;
|
break;
|
||||||
@ -3350,7 +3342,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
|
|||||||
if (ret <= 0)
|
if (ret <= 0)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
if (!iter->ent && !find_next_entry_inc(iter)) {
|
if (!iter->ent && !trace_find_next_entry_inc(iter)) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
@ -4414,7 +4406,7 @@ static struct notifier_block trace_die_notifier = {
|
|||||||
*/
|
*/
|
||||||
#define KERN_TRACE KERN_EMERG
|
#define KERN_TRACE KERN_EMERG
|
||||||
|
|
||||||
static void
|
void
|
||||||
trace_printk_seq(struct trace_seq *s)
|
trace_printk_seq(struct trace_seq *s)
|
||||||
{
|
{
|
||||||
/* Probably should print a warning here. */
|
/* Probably should print a warning here. */
|
||||||
@ -4429,6 +4421,13 @@ trace_printk_seq(struct trace_seq *s)
|
|||||||
trace_seq_init(s);
|
trace_seq_init(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void trace_init_global_iter(struct trace_iterator *iter)
|
||||||
|
{
|
||||||
|
iter->tr = &global_trace;
|
||||||
|
iter->trace = current_trace;
|
||||||
|
iter->cpu_file = TRACE_PIPE_ALL_CPU;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
__ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
|
__ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
|
||||||
{
|
{
|
||||||
@ -4454,8 +4453,10 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
|
|||||||
if (disable_tracing)
|
if (disable_tracing)
|
||||||
ftrace_kill();
|
ftrace_kill();
|
||||||
|
|
||||||
|
trace_init_global_iter(&iter);
|
||||||
|
|
||||||
for_each_tracing_cpu(cpu) {
|
for_each_tracing_cpu(cpu) {
|
||||||
atomic_inc(&global_trace.data[cpu]->disabled);
|
atomic_inc(&iter.tr->data[cpu]->disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
old_userobj = trace_flags & TRACE_ITER_SYM_USEROBJ;
|
old_userobj = trace_flags & TRACE_ITER_SYM_USEROBJ;
|
||||||
@ -4504,7 +4505,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
|
|||||||
iter.iter_flags |= TRACE_FILE_LAT_FMT;
|
iter.iter_flags |= TRACE_FILE_LAT_FMT;
|
||||||
iter.pos = -1;
|
iter.pos = -1;
|
||||||
|
|
||||||
if (find_next_entry_inc(&iter) != NULL) {
|
if (trace_find_next_entry_inc(&iter) != NULL) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = print_trace_line(&iter);
|
ret = print_trace_line(&iter);
|
||||||
@ -4526,7 +4527,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
|
|||||||
trace_flags |= old_userobj;
|
trace_flags |= old_userobj;
|
||||||
|
|
||||||
for_each_tracing_cpu(cpu) {
|
for_each_tracing_cpu(cpu) {
|
||||||
atomic_dec(&global_trace.data[cpu]->disabled);
|
atomic_dec(&iter.tr->data[cpu]->disabled);
|
||||||
}
|
}
|
||||||
tracing_on();
|
tracing_on();
|
||||||
}
|
}
|
||||||
|
@ -338,6 +338,14 @@ struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
|
|||||||
struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
|
struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
|
||||||
int *ent_cpu, u64 *ent_ts);
|
int *ent_cpu, u64 *ent_ts);
|
||||||
|
|
||||||
|
int trace_empty(struct trace_iterator *iter);
|
||||||
|
|
||||||
|
void *trace_find_next_entry_inc(struct trace_iterator *iter);
|
||||||
|
|
||||||
|
void trace_init_global_iter(struct trace_iterator *iter);
|
||||||
|
|
||||||
|
void tracing_iter_reset(struct trace_iterator *iter, int cpu);
|
||||||
|
|
||||||
void default_wait_pipe(struct trace_iterator *iter);
|
void default_wait_pipe(struct trace_iterator *iter);
|
||||||
void poll_wait_pipe(struct trace_iterator *iter);
|
void poll_wait_pipe(struct trace_iterator *iter);
|
||||||
|
|
||||||
@ -380,6 +388,15 @@ void tracing_start_sched_switch_record(void);
|
|||||||
int register_tracer(struct tracer *type);
|
int register_tracer(struct tracer *type);
|
||||||
void unregister_tracer(struct tracer *type);
|
void unregister_tracer(struct tracer *type);
|
||||||
int is_tracing_stopped(void);
|
int is_tracing_stopped(void);
|
||||||
|
enum trace_file_type {
|
||||||
|
TRACE_FILE_LAT_FMT = 1,
|
||||||
|
TRACE_FILE_ANNOTATE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern cpumask_var_t __read_mostly tracing_buffer_mask;
|
||||||
|
|
||||||
|
#define for_each_tracing_cpu(cpu) \
|
||||||
|
for_each_cpu(cpu, tracing_buffer_mask)
|
||||||
|
|
||||||
extern int process_new_ksym_entry(char *ksymname, int op, unsigned long addr);
|
extern int process_new_ksym_entry(char *ksymname, int op, unsigned long addr);
|
||||||
|
|
||||||
@ -471,6 +488,8 @@ trace_array_vprintk(struct trace_array *tr,
|
|||||||
unsigned long ip, const char *fmt, va_list args);
|
unsigned long ip, const char *fmt, va_list args);
|
||||||
int trace_array_printk(struct trace_array *tr,
|
int trace_array_printk(struct trace_array *tr,
|
||||||
unsigned long ip, const char *fmt, ...);
|
unsigned long ip, const char *fmt, ...);
|
||||||
|
void trace_printk_seq(struct trace_seq *s);
|
||||||
|
enum print_line_t print_trace_line(struct trace_iterator *iter);
|
||||||
|
|
||||||
extern unsigned long trace_flags;
|
extern unsigned long trace_flags;
|
||||||
|
|
||||||
|
136
kernel/trace/trace_kdb.c
Normal file
136
kernel/trace/trace_kdb.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* kdb helper for dumping the ftrace buffer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Jason Wessel <jason.wessel@windriver.com>
|
||||||
|
*
|
||||||
|
* ftrace_dump_buf based on ftrace_dump:
|
||||||
|
* Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
|
||||||
|
* Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kgdb.h>
|
||||||
|
#include <linux/kdb.h>
|
||||||
|
#include <linux/ftrace.h>
|
||||||
|
|
||||||
|
#include "../debug/kdb/kdb_private.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "trace_output.h"
|
||||||
|
|
||||||
|
static void ftrace_dump_buf(int skip_lines, long cpu_file)
|
||||||
|
{
|
||||||
|
/* use static because iter can be a bit big for the stack */
|
||||||
|
static struct trace_iterator iter;
|
||||||
|
unsigned int old_userobj;
|
||||||
|
int cnt = 0, cpu;
|
||||||
|
|
||||||
|
trace_init_global_iter(&iter);
|
||||||
|
|
||||||
|
for_each_tracing_cpu(cpu) {
|
||||||
|
atomic_inc(&iter.tr->data[cpu]->disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
old_userobj = trace_flags;
|
||||||
|
|
||||||
|
/* don't look at user memory in panic mode */
|
||||||
|
trace_flags &= ~TRACE_ITER_SYM_USEROBJ;
|
||||||
|
|
||||||
|
kdb_printf("Dumping ftrace buffer:\n");
|
||||||
|
|
||||||
|
/* reset all but tr, trace, and overruns */
|
||||||
|
memset(&iter.seq, 0,
|
||||||
|
sizeof(struct trace_iterator) -
|
||||||
|
offsetof(struct trace_iterator, seq));
|
||||||
|
iter.iter_flags |= TRACE_FILE_LAT_FMT;
|
||||||
|
iter.pos = -1;
|
||||||
|
|
||||||
|
if (cpu_file == TRACE_PIPE_ALL_CPU) {
|
||||||
|
for_each_tracing_cpu(cpu) {
|
||||||
|
iter.buffer_iter[cpu] =
|
||||||
|
ring_buffer_read_prepare(iter.tr->buffer, cpu);
|
||||||
|
ring_buffer_read_start(iter.buffer_iter[cpu]);
|
||||||
|
tracing_iter_reset(&iter, cpu);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iter.cpu_file = cpu_file;
|
||||||
|
iter.buffer_iter[cpu_file] =
|
||||||
|
ring_buffer_read_prepare(iter.tr->buffer, cpu_file);
|
||||||
|
ring_buffer_read_start(iter.buffer_iter[cpu_file]);
|
||||||
|
tracing_iter_reset(&iter, cpu_file);
|
||||||
|
}
|
||||||
|
if (!trace_empty(&iter))
|
||||||
|
trace_find_next_entry_inc(&iter);
|
||||||
|
while (!trace_empty(&iter)) {
|
||||||
|
if (!cnt)
|
||||||
|
kdb_printf("---------------------------------\n");
|
||||||
|
cnt++;
|
||||||
|
|
||||||
|
if (trace_find_next_entry_inc(&iter) != NULL && !skip_lines)
|
||||||
|
print_trace_line(&iter);
|
||||||
|
if (!skip_lines)
|
||||||
|
trace_printk_seq(&iter.seq);
|
||||||
|
else
|
||||||
|
skip_lines--;
|
||||||
|
if (KDB_FLAG(CMD_INTERRUPT))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cnt)
|
||||||
|
kdb_printf(" (ftrace buffer empty)\n");
|
||||||
|
else
|
||||||
|
kdb_printf("---------------------------------\n");
|
||||||
|
|
||||||
|
out:
|
||||||
|
trace_flags = old_userobj;
|
||||||
|
|
||||||
|
for_each_tracing_cpu(cpu) {
|
||||||
|
atomic_dec(&iter.tr->data[cpu]->disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_tracing_cpu(cpu)
|
||||||
|
if (iter.buffer_iter[cpu])
|
||||||
|
ring_buffer_read_finish(iter.buffer_iter[cpu]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kdb_ftdump - Dump the ftrace log buffer
|
||||||
|
*/
|
||||||
|
static int kdb_ftdump(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int skip_lines = 0;
|
||||||
|
long cpu_file;
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
if (argc > 2)
|
||||||
|
return KDB_ARGCOUNT;
|
||||||
|
|
||||||
|
if (argc) {
|
||||||
|
skip_lines = simple_strtol(argv[1], &cp, 0);
|
||||||
|
if (*cp)
|
||||||
|
skip_lines = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
cpu_file = simple_strtol(argv[2], &cp, 0);
|
||||||
|
if (*cp || cpu_file >= NR_CPUS || cpu_file < 0 ||
|
||||||
|
!cpu_online(cpu_file))
|
||||||
|
return KDB_BADINT;
|
||||||
|
} else {
|
||||||
|
cpu_file = TRACE_PIPE_ALL_CPU;
|
||||||
|
}
|
||||||
|
|
||||||
|
kdb_trap_printk++;
|
||||||
|
ftrace_dump_buf(skip_lines, cpu_file);
|
||||||
|
kdb_trap_printk--;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __init int kdb_ftrace_register(void)
|
||||||
|
{
|
||||||
|
kdb_register_repeat("ftdump", kdb_ftdump, "[skip_#lines] [cpu]",
|
||||||
|
"Dump ftrace log", 0, KDB_REPEAT_NONE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
late_initcall(kdb_ftrace_register);
|
@ -26,6 +26,7 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/hash.h>
|
#include <linux/hash.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/kgdb.h>
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -470,6 +471,12 @@ void debug_kmap_atomic(enum km_type type)
|
|||||||
warn_count--;
|
warn_count--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_KGDB_KDB
|
||||||
|
if (unlikely(type == KM_KDB && atomic_read(&kgdb_active) == -1)) {
|
||||||
|
WARN_ON(1);
|
||||||
|
warn_count--;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KGDB_KDB */
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user