diff options
Diffstat (limited to 'arch/sh/kernel/kgdb.c')
| -rw-r--r-- | arch/sh/kernel/kgdb.c | 105 | 
1 files changed, 82 insertions, 23 deletions
diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index b117781bfea..38b313909ac 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -1,7 +1,7 @@  /*   * SuperH KGDB support   * - * Copyright (C) 2008 - 2009  Paul Mundt + * Copyright (C) 2008 - 2012  Paul Mundt   *   * Single stepping taken from the old stub by Henry Bell and Jeremy Siegel.   * @@ -164,42 +164,89 @@ static void undo_single_step(struct pt_regs *linux_regs)  	stepped_opcode = 0;  } -void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) -{ -	int i; +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { +	{ "r0",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) }, +	{ "r1",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) }, +	{ "r2",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) }, +	{ "r3",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) }, +	{ "r4",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) }, +	{ "r5",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) }, +	{ "r6",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) }, +	{ "r7",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) }, +	{ "r8",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) }, +	{ "r9",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) }, +	{ "r10",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) }, +	{ "r11",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) }, +	{ "r12",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) }, +	{ "r13",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) }, +	{ "r14",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) }, +	{ "r15",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) }, +	{ "pc",		GDB_SIZEOF_REG, offsetof(struct pt_regs, pc) }, +	{ "pr",		GDB_SIZEOF_REG, offsetof(struct pt_regs, pr) }, +	{ "sr",		GDB_SIZEOF_REG, offsetof(struct pt_regs, sr) }, +	{ "gbr",	GDB_SIZEOF_REG, offsetof(struct pt_regs, gbr) }, +	{ "mach",	GDB_SIZEOF_REG, offsetof(struct pt_regs, mach) }, +	{ "macl",	GDB_SIZEOF_REG, offsetof(struct pt_regs, macl) }, +	{ "vbr",	GDB_SIZEOF_REG, -1 }, +}; -	for (i = 0; i < 16; i++) -		gdb_regs[GDB_R0 + i] = regs->regs[i]; +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ +	if (regno < 0 || regno >= DBG_MAX_REG_NUM) +		return -EINVAL; -	gdb_regs[GDB_PC] = regs->pc; -	gdb_regs[GDB_PR] = regs->pr; -	gdb_regs[GDB_SR] = regs->sr; -	gdb_regs[GDB_GBR] = regs->gbr; -	gdb_regs[GDB_MACH] = regs->mach; -	gdb_regs[GDB_MACL] = regs->macl; +	if (dbg_reg_def[regno].offset != -1) +		memcpy((void *)regs + dbg_reg_def[regno].offset, mem, +		       dbg_reg_def[regno].size); -	__asm__ __volatile__ ("stc vbr, %0" : "=r" (gdb_regs[GDB_VBR])); +	return 0;  } -void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)  { -	int i; +	if (regno >= DBG_MAX_REG_NUM || regno < 0) +		return NULL; -	for (i = 0; i < 16; i++) -		regs->regs[GDB_R0 + i] = gdb_regs[GDB_R0 + i]; +	if (dbg_reg_def[regno].size != -1) +		memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, +		       dbg_reg_def[regno].size); + +	switch (regno) { +	case GDB_VBR: +		__asm__ __volatile__ ("stc vbr, %0" : "=r" (mem)); +		break; +	} -	regs->pc = gdb_regs[GDB_PC]; -	regs->pr = gdb_regs[GDB_PR]; -	regs->sr = gdb_regs[GDB_SR]; -	regs->gbr = gdb_regs[GDB_GBR]; -	regs->mach = gdb_regs[GDB_MACH]; -	regs->macl = gdb_regs[GDB_MACL]; +	return dbg_reg_def[regno].name;  }  void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)  { +	struct pt_regs *thread_regs = task_pt_regs(p); +	int reg; + +	/* Initialize to zero */ +	for (reg = 0; reg < DBG_MAX_REG_NUM; reg++) +		gdb_regs[reg] = 0; + +	/* +	 * Copy out GP regs 8 to 14. +	 * +	 * switch_to() relies on SR.RB toggling, so regs 0->7 are banked +	 * and need privileged instructions to get to. The r15 value we +	 * fetch from the thread info directly. +	 */ +	for (reg = GDB_R8; reg < GDB_R15; reg++) +		gdb_regs[reg] = thread_regs->regs[reg]; +  	gdb_regs[GDB_R15] = p->thread.sp;  	gdb_regs[GDB_PC] = p->thread.pc; + +	/* +	 * Additional registers we have context for +	 */ +	gdb_regs[GDB_PR] = thread_regs->pr; +	gdb_regs[GDB_GBR] = thread_regs->gbr;  }  int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, @@ -264,6 +311,18 @@ BUILD_TRAP_HANDLER(singlestep)  	local_irq_restore(flags);  } +static void kgdb_call_nmi_hook(void *ignored) +{ +	kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ +	local_irq_enable(); +	smp_call_function(kgdb_call_nmi_hook, NULL, 0); +	local_irq_disable(); +} +  static int __kgdb_notify(struct die_args *args, unsigned long cmd)  {  	int ret;  |