| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | /* kgdb support for MN10300
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * Written by David Howells (dhowells@redhat.com) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public Licence | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version | 
					
						
							|  |  |  |  * 2 of the Licence, or (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | #include <linux/ptrace.h>
 | 
					
						
							|  |  |  | #include <linux/kgdb.h>
 | 
					
						
							|  |  |  | #include <linux/uaccess.h>
 | 
					
						
							|  |  |  | #include <unit/leds.h>
 | 
					
						
							|  |  |  | #include <unit/serial.h>
 | 
					
						
							|  |  |  | #include <asm/debugger.h>
 | 
					
						
							|  |  |  | #include <asm/serial-regs.h>
 | 
					
						
							|  |  |  | #include "internal.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Software single-stepping breakpoint save (used by __switch_to()) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct thread_info *kgdb_sstep_thread; | 
					
						
							|  |  |  | u8 *kgdb_sstep_bp_addr[2]; | 
					
						
							|  |  |  | u8 kgdb_sstep_bp[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copy kernel exception frame registers to the GDB register file | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long ssp = (unsigned long) (regs + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_D0]	= regs->d0; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_D1]	= regs->d1; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_D2]	= regs->d2; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_D3]	= regs->d3; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_A0]	= regs->a0; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_A1]	= regs->a1; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_A2]	= regs->a2; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_A3]	= regs->a3; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_SP]	= (regs->epsw & EPSW_nSL) ? regs->sp : ssp; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_PC]	= regs->pc; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_MDR]	= regs->mdr; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_EPSW]	= regs->epsw; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_LIR]	= regs->lir; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_LAR]	= regs->lar; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_MDRQ]	= regs->mdrq; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E0]	= regs->e0; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E1]	= regs->e1; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E2]	= regs->e2; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E3]	= regs->e3; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E4]	= regs->e4; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E5]	= regs->e5; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E6]	= regs->e6; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_E7]	= regs->e7; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_SSP]	= ssp; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_MSP]	= 0; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_USP]	= regs->sp; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_MCRH]	= regs->mcrh; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_MCRL]	= regs->mcrl; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_MCVF]	= regs->mcvf; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_DUMMY0]	= 0; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_DUMMY1]	= 0; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_FS0]	= 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Extracts kernel SP/PC values understandable by gdb from the values | 
					
						
							|  |  |  |  * saved by switch_to(). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_SSP]	= p->thread.sp; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_PC]	= p->thread.pc; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_A3]	= p->thread.a3; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_USP]	= p->thread.usp; | 
					
						
							|  |  |  | 	gdb_regs[GDB_FR_FPCR]	= p->thread.fpu_state.fpcr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Fill kernel exception frame registers from the GDB register file | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	regs->d0	= gdb_regs[GDB_FR_D0]; | 
					
						
							|  |  |  | 	regs->d1	= gdb_regs[GDB_FR_D1]; | 
					
						
							|  |  |  | 	regs->d2	= gdb_regs[GDB_FR_D2]; | 
					
						
							|  |  |  | 	regs->d3	= gdb_regs[GDB_FR_D3]; | 
					
						
							|  |  |  | 	regs->a0	= gdb_regs[GDB_FR_A0]; | 
					
						
							|  |  |  | 	regs->a1	= gdb_regs[GDB_FR_A1]; | 
					
						
							|  |  |  | 	regs->a2	= gdb_regs[GDB_FR_A2]; | 
					
						
							|  |  |  | 	regs->a3	= gdb_regs[GDB_FR_A3]; | 
					
						
							|  |  |  | 	regs->sp	= gdb_regs[GDB_FR_SP]; | 
					
						
							|  |  |  | 	regs->pc	= gdb_regs[GDB_FR_PC]; | 
					
						
							|  |  |  | 	regs->mdr	= gdb_regs[GDB_FR_MDR]; | 
					
						
							|  |  |  | 	regs->epsw	= gdb_regs[GDB_FR_EPSW]; | 
					
						
							|  |  |  | 	regs->lir	= gdb_regs[GDB_FR_LIR]; | 
					
						
							|  |  |  | 	regs->lar	= gdb_regs[GDB_FR_LAR]; | 
					
						
							|  |  |  | 	regs->mdrq	= gdb_regs[GDB_FR_MDRQ]; | 
					
						
							|  |  |  | 	regs->e0	= gdb_regs[GDB_FR_E0]; | 
					
						
							|  |  |  | 	regs->e1	= gdb_regs[GDB_FR_E1]; | 
					
						
							|  |  |  | 	regs->e2	= gdb_regs[GDB_FR_E2]; | 
					
						
							|  |  |  | 	regs->e3	= gdb_regs[GDB_FR_E3]; | 
					
						
							|  |  |  | 	regs->e4	= gdb_regs[GDB_FR_E4]; | 
					
						
							|  |  |  | 	regs->e5	= gdb_regs[GDB_FR_E5]; | 
					
						
							|  |  |  | 	regs->e6	= gdb_regs[GDB_FR_E6]; | 
					
						
							|  |  |  | 	regs->e7	= gdb_regs[GDB_FR_E7]; | 
					
						
							|  |  |  | 	regs->sp	= gdb_regs[GDB_FR_SSP]; | 
					
						
							|  |  |  | 	/* gdb_regs[GDB_FR_MSP]; */ | 
					
						
							|  |  |  | 	// regs->usp	= gdb_regs[GDB_FR_USP];
 | 
					
						
							|  |  |  | 	regs->mcrh	= gdb_regs[GDB_FR_MCRH]; | 
					
						
							|  |  |  | 	regs->mcrl	= gdb_regs[GDB_FR_MCRL]; | 
					
						
							|  |  |  | 	regs->mcvf	= gdb_regs[GDB_FR_MCVF]; | 
					
						
							|  |  |  | 	/* gdb_regs[GDB_FR_DUMMY0]; */ | 
					
						
							|  |  |  | 	/* gdb_regs[GDB_FR_DUMMY1]; */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// regs->fpcr	= gdb_regs[GDB_FR_FPCR];
 | 
					
						
							|  |  |  | 	// regs->fs0	= gdb_regs[GDB_FR_FS0];
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct kgdb_arch arch_kgdb_ops = { | 
					
						
							|  |  |  | 	.gdb_bpt_instr	= { 0xff }, | 
					
						
							|  |  |  | 	.flags		= KGDB_HW_BREAKPOINT, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | static const unsigned char mn10300_kgdb_insn_sizes[256] = | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* 1  2  3  4  5  6  7  8  9  a  b  c  d  e  f */ | 
					
						
							|  |  |  | 	1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3,	/* 0 */ | 
					
						
							|  |  |  | 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ | 
					
						
							|  |  |  | 	2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ | 
					
						
							|  |  |  | 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ | 
					
						
							|  |  |  | 	1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ | 
					
						
							|  |  |  | 	1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ | 
					
						
							|  |  |  | 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ | 
					
						
							|  |  |  | 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ | 
					
						
							|  |  |  | 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ | 
					
						
							|  |  |  | 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ | 
					
						
							|  |  |  | 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ | 
					
						
							|  |  |  | 	2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ | 
					
						
							|  |  |  | 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ | 
					
						
							|  |  |  | 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ | 
					
						
							|  |  |  | 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ | 
					
						
							|  |  |  | 	0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1  /* f */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  |  * Attempt to emulate single stepping by means of breakpoint instructions. | 
					
						
							|  |  |  |  * Although there is a single-step trace flag in EPSW, its use is not | 
					
						
							|  |  |  |  * sufficiently documented and is only intended for use with the JTAG debugger. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int kgdb_arch_do_singlestep(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long arg; | 
					
						
							|  |  |  | 	unsigned size; | 
					
						
							|  |  |  | 	u8 *pc = (u8 *)regs->pc, *sp = (u8 *)(regs + 1), cur; | 
					
						
							|  |  |  | 	u8 *x = NULL, *y = NULL; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = probe_kernel_read(&cur, pc, 1); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	size = mn10300_kgdb_insn_sizes[cur]; | 
					
						
							|  |  |  | 	if (size > 0) { | 
					
						
							|  |  |  | 		x = pc + size; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (cur) { | 
					
						
							|  |  |  | 		/* Bxx (d8,PC) */ | 
					
						
							|  |  |  | 	case 0xc0 ... 0xca: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&arg, pc + 1, 1); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		x = pc + 2; | 
					
						
							|  |  |  | 		if (arg >= 0 && arg <= 2) | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		y = pc + (s8)arg; | 
					
						
							|  |  |  | 		goto set_x_and_y; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* LXX (d8,PC) */ | 
					
						
							|  |  |  | 	case 0xd0 ... 0xda: | 
					
						
							|  |  |  | 		x = pc + 1; | 
					
						
							|  |  |  | 		if (regs->pc == regs->lar) | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		y = (u8 *)regs->lar; | 
					
						
							|  |  |  | 		goto set_x_and_y; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* SETLB - loads the next four bytes into the LIR register
 | 
					
						
							|  |  |  | 		 * (which mustn't include a breakpoint instruction) */ | 
					
						
							|  |  |  | 	case 0xdb: | 
					
						
							|  |  |  | 		x = pc + 5; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* JMP (d16,PC) or CALL (d16,PC) */ | 
					
						
							|  |  |  | 	case 0xcc: | 
					
						
							|  |  |  | 	case 0xcd: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&arg, pc + 1, 2); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		x = pc + (s16)arg; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* JMP (d32,PC) or CALL (d32,PC) */ | 
					
						
							|  |  |  | 	case 0xdc: | 
					
						
							|  |  |  | 	case 0xdd: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&arg, pc + 1, 4); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		x = pc + (s32)arg; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* RETF */ | 
					
						
							|  |  |  | 	case 0xde: | 
					
						
							|  |  |  | 		x = (u8 *)regs->mdr; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* RET */ | 
					
						
							|  |  |  | 	case 0xdf: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&arg, pc + 2, 1); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&x, sp + (s8)arg, 4); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 0xf0: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&cur, pc + 1, 1); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (cur >= 0xf0 && cur <= 0xf7) { | 
					
						
							|  |  |  | 			/* JMP (An) / CALLS (An) */ | 
					
						
							|  |  |  | 			switch (cur & 3) { | 
					
						
							|  |  |  | 			case 0: x = (u8 *)regs->a0; break; | 
					
						
							|  |  |  | 			case 1: x = (u8 *)regs->a1; break; | 
					
						
							|  |  |  | 			case 2: x = (u8 *)regs->a2; break; | 
					
						
							|  |  |  | 			case 3: x = (u8 *)regs->a3; break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		} else if (cur == 0xfc) { | 
					
						
							|  |  |  | 			/* RETS */ | 
					
						
							|  |  |  | 			ret = probe_kernel_read(&x, sp, 4); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				return ret; | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		} else if (cur == 0xfd) { | 
					
						
							|  |  |  | 			/* RTI */ | 
					
						
							|  |  |  | 			ret = probe_kernel_read(&x, sp + 4, 4); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				return ret; | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			x = pc + 2; | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* potential 3-byte conditional branches */ | 
					
						
							|  |  |  | 	case 0xf8: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&cur, pc + 1, 1); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		x = pc + 3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (cur >= 0xe8 && cur <= 0xeb) { | 
					
						
							|  |  |  | 			ret = probe_kernel_read(&arg, pc + 2, 1); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				return ret; | 
					
						
							|  |  |  | 			if (arg >= 0 && arg <= 3) | 
					
						
							|  |  |  | 				goto set_x; | 
					
						
							|  |  |  | 			y = pc + (s8)arg; | 
					
						
							|  |  |  | 			goto set_x_and_y; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 0xfa: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&cur, pc + 1, 1); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (cur == 0xff) { | 
					
						
							|  |  |  | 			/* CALLS (d16,PC) */ | 
					
						
							|  |  |  | 			ret = probe_kernel_read(&arg, pc + 2, 2); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				return ret; | 
					
						
							|  |  |  | 			x = pc + (s16)arg; | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		x = pc + 4; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 0xfc: | 
					
						
							|  |  |  | 		ret = probe_kernel_read(&cur, pc + 1, 1); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (cur == 0xff) { | 
					
						
							|  |  |  | 			/* CALLS (d32,PC) */ | 
					
						
							|  |  |  | 			ret = probe_kernel_read(&arg, pc + 2, 4); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				return ret; | 
					
						
							|  |  |  | 			x = pc + (s32)arg; | 
					
						
							|  |  |  | 			goto set_x; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		x = pc + 6; | 
					
						
							|  |  |  | 		goto set_x; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | set_x: | 
					
						
							|  |  |  | 	kgdb_sstep_bp_addr[0] = x; | 
					
						
							|  |  |  | 	kgdb_sstep_bp_addr[1] = NULL; | 
					
						
							|  |  |  | 	ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	kgdb_sstep_thread = current_thread_info(); | 
					
						
							|  |  |  | 	debugger_local_cache_flushinv_one(x); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | set_x_and_y: | 
					
						
							|  |  |  | 	kgdb_sstep_bp_addr[0] = x; | 
					
						
							|  |  |  | 	kgdb_sstep_bp_addr[1] = y; | 
					
						
							|  |  |  | 	ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	ret = probe_kernel_read(&kgdb_sstep_bp[1], y, 1); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	ret = probe_kernel_write(y, &arch_kgdb_ops.gdb_bpt_instr, 1); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		probe_kernel_write(kgdb_sstep_bp_addr[0], | 
					
						
							|  |  |  | 				   &kgdb_sstep_bp[0], 1); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		kgdb_sstep_thread = current_thread_info(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	debugger_local_cache_flushinv_one(x); | 
					
						
							|  |  |  | 	debugger_local_cache_flushinv_one(y); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Remove emplaced single-step breakpoints, returning true if we hit one of | 
					
						
							|  |  |  |  * them. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static bool kgdb_arch_undo_singlestep(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	bool hit = false; | 
					
						
							|  |  |  | 	u8 *x = kgdb_sstep_bp_addr[0], *y = kgdb_sstep_bp_addr[1]; | 
					
						
							|  |  |  | 	u8 opcode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (kgdb_sstep_thread == current_thread_info()) { | 
					
						
							|  |  |  | 		if (x) { | 
					
						
							|  |  |  | 			if (x == (u8 *)regs->pc) | 
					
						
							|  |  |  | 				hit = true; | 
					
						
							|  |  |  | 			if (probe_kernel_read(&opcode, x, | 
					
						
							|  |  |  | 					      1) < 0 || | 
					
						
							|  |  |  | 			    opcode != 0xff) | 
					
						
							|  |  |  | 				BUG(); | 
					
						
							|  |  |  | 			probe_kernel_write(x, &kgdb_sstep_bp[0], 1); | 
					
						
							|  |  |  | 			debugger_local_cache_flushinv_one(x); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (y) { | 
					
						
							|  |  |  | 			if (y == (u8 *)regs->pc) | 
					
						
							|  |  |  | 				hit = true; | 
					
						
							|  |  |  | 			if (probe_kernel_read(&opcode, y, | 
					
						
							|  |  |  | 					      1) < 0 || | 
					
						
							|  |  |  | 			    opcode != 0xff) | 
					
						
							|  |  |  | 				BUG(); | 
					
						
							|  |  |  | 			probe_kernel_write(y, &kgdb_sstep_bp[1], 1); | 
					
						
							|  |  |  | 			debugger_local_cache_flushinv_one(y); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kgdb_sstep_bp_addr[0] = NULL; | 
					
						
							|  |  |  | 	kgdb_sstep_bp_addr[1] = NULL; | 
					
						
							|  |  |  | 	kgdb_sstep_thread = NULL; | 
					
						
							|  |  |  | 	return hit; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Catch a single-step-pending thread being deleted and make sure the global | 
					
						
							|  |  |  |  * single-step state is cleared.  At this point the breakpoints should have | 
					
						
							|  |  |  |  * been removed by __switch_to(). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-05-05 15:05:45 +00:00
										 |  |  | void arch_release_thread_info(struct thread_info *ti) | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (kgdb_sstep_thread == ti) { | 
					
						
							|  |  |  | 		kgdb_sstep_thread = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* However, we may now be running in degraded mode, with most
 | 
					
						
							|  |  |  | 		 * of the CPUs disabled until such a time as KGDB is reentered, | 
					
						
							|  |  |  | 		 * so force immediate reentry */ | 
					
						
							|  |  |  | 		kgdb_breakpoint(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Handle unknown packets and [CcsDk] packets | 
					
						
							|  |  |  |  * - at this point breakpoints have been installed | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | int kgdb_arch_handle_exception(int vector, int signo, int err_code, | 
					
						
							|  |  |  | 			       char *remcom_in_buffer, char *remcom_out_buffer, | 
					
						
							|  |  |  | 			       struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	long addr; | 
					
						
							|  |  |  | 	char *ptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (remcom_in_buffer[0]) { | 
					
						
							|  |  |  | 	case 'c': | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | 	case 's': | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | 		/* try to read optional parameter, pc unchanged if no parm */ | 
					
						
							|  |  |  | 		ptr = &remcom_in_buffer[1]; | 
					
						
							|  |  |  | 		if (kgdb_hex2long(&ptr, &addr)) | 
					
						
							|  |  |  | 			regs->pc = addr; | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | 	case 'D': | 
					
						
							|  |  |  | 	case 'k': | 
					
						
							|  |  |  | 		atomic_set(&kgdb_cpu_doing_single_step, -1); | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | 		if (remcom_in_buffer[0] == 's') { | 
					
						
							|  |  |  | 			kgdb_arch_do_singlestep(regs); | 
					
						
							|  |  |  | 			kgdb_single_step = 1; | 
					
						
							|  |  |  | 			atomic_set(&kgdb_cpu_doing_single_step, | 
					
						
							|  |  |  | 				   raw_smp_processor_id()); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return -1; /* this means that we do not want to exit from the handler */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Handle event interception | 
					
						
							|  |  |  |  * - returns 0 if the exception should be skipped, -ERROR otherwise. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int debugger_intercept(enum exception_code excep, int signo, int si_code, | 
					
						
							|  |  |  | 		       struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:32 +00:00
										 |  |  | 	if (kgdb_arch_undo_singlestep(regs)) { | 
					
						
							|  |  |  | 		excep = EXCEP_TRAP; | 
					
						
							|  |  |  | 		signo = SIGTRAP; | 
					
						
							|  |  |  | 		si_code = TRAP_TRACE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-18 16:54:31 +00:00
										 |  |  | 	ret = kgdb_handle_exception(excep, signo, si_code, regs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debugger_local_cache_flushinv(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Determine if we've hit a debugger special breakpoint | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int at_debugger_breakpoint(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return regs->pc == (unsigned long)&__arch_kgdb_breakpoint; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Initialise kgdb | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int kgdb_arch_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Do something, perhaps, but don't know what. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void kgdb_arch_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SMP
 | 
					
						
							|  |  |  | void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	kgdb_nmicallback(arch_smp_processor_id(), regs); | 
					
						
							|  |  |  | 	debugger_local_cache_flushinv(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void kgdb_roundup_cpus(unsigned long flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	smp_jump_to_debugger(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 |