| #include <common.h> | 
 | #include <command.h> | 
 |  | 
 | #if (CONFIG_COMMANDS & CFG_CMD_KGDB) || defined(CONFIG_CMD_KGDB) | 
 |  | 
 | #include <kgdb.h> | 
 | #include <asm/signal.h> | 
 | #include <asm/processor.h> | 
 |  | 
 | #define PC_REGNUM 64 | 
 | #define SP_REGNUM 1 | 
 |  | 
 | void breakinst(void); | 
 |  | 
 | int | 
 | kgdb_setjmp(long *buf) | 
 | { | 
 | 	asm ("mflr 0; stw 0,0(%0);" | 
 | 	     "stw 1,4(%0); stw 2,8(%0);" | 
 | 	     "mfcr 0; stw 0,12(%0);" | 
 | 	     "stmw 13,16(%0)" | 
 | 	     : : "r" (buf)); | 
 | 	/* XXX should save fp regs as well */ | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | kgdb_longjmp(long *buf, int val) | 
 | { | 
 | 	if (val == 0) | 
 | 		val = 1; | 
 | 	asm ("lmw 13,16(%0);" | 
 | 	     "lwz 0,12(%0); mtcrf 0x38,0;" | 
 | 	     "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" | 
 | 	     "mtlr 0; mr 3,%1" | 
 | 	     : : "r" (buf), "r" (val)); | 
 | } | 
 |  | 
 | static inline unsigned long | 
 | get_msr(void) | 
 | { | 
 | 	unsigned long msr; | 
 | 	asm volatile("mfmsr %0" : "=r" (msr):); | 
 | 	return msr; | 
 | } | 
 |  | 
 | static inline void | 
 | set_msr(unsigned long msr) | 
 | { | 
 | 	asm volatile("mtmsr %0" : : "r" (msr)); | 
 | } | 
 |  | 
 | /* Convert the SPARC hardware trap type code to a unix signal number. */ | 
 | /* | 
 |  * This table contains the mapping between PowerPC hardware trap types, and | 
 |  * signals, which are primarily what GDB understands. | 
 |  */ | 
 | static struct hard_trap_info | 
 | { | 
 | 	unsigned int tt;		/* Trap type code for powerpc */ | 
 | 	unsigned char signo;		/* Signal that we map this trap into */ | 
 | } hard_trap_info[] = { | 
 | 	{ 0x200, SIGSEGV },			/* machine check */ | 
 | 	{ 0x300, SIGSEGV },			/* address error (store) */ | 
 | 	{ 0x400, SIGBUS },			/* instruction bus error */ | 
 | 	{ 0x500, SIGINT },			/* interrupt */ | 
 | 	{ 0x600, SIGBUS },			/* alingment */ | 
 | 	{ 0x700, SIGTRAP },			/* breakpoint trap */ | 
 | 	{ 0x800, SIGFPE },			/* fpu unavail */ | 
 | 	{ 0x900, SIGALRM },			/* decrementer */ | 
 | 	{ 0xa00, SIGILL },			/* reserved */ | 
 | 	{ 0xb00, SIGILL },			/* reserved */ | 
 | 	{ 0xc00, SIGCHLD },			/* syscall */ | 
 | 	{ 0xd00, SIGTRAP },			/* single-step/watch */ | 
 | 	{ 0xe00, SIGFPE },			/* fp assist */ | 
 | 	{ 0, 0}				/* Must be last */ | 
 | }; | 
 |  | 
 | static int | 
 | computeSignal(unsigned int tt) | 
 | { | 
 | 	struct hard_trap_info *ht; | 
 |  | 
 | 	for (ht = hard_trap_info; ht->tt && ht->signo; ht++) | 
 | 		if (ht->tt == tt) | 
 | 			return ht->signo; | 
 |  | 
 | 	return SIGHUP;         /* default for things we don't know about */ | 
 | } | 
 |  | 
 | void | 
 | kgdb_enter(struct pt_regs *regs, kgdb_data *kdp) | 
 | { | 
 | 	unsigned long msr; | 
 |  | 
 | 	kdp->private[0] = msr = get_msr(); | 
 | 	set_msr(msr & ~MSR_EE);	/* disable interrupts */ | 
 |  | 
 | 	if (regs->nip == (unsigned long)breakinst) { | 
 | 		/* Skip over breakpoint trap insn */ | 
 | 		regs->nip += 4; | 
 | 	} | 
 | 	regs->msr &= ~MSR_SE; | 
 |  | 
 | 	/* reply to host that an exception has occurred */ | 
 | 	kdp->sigval = computeSignal(regs->trap); | 
 |  | 
 | 	kdp->nregs = 2; | 
 |  | 
 | 	kdp->regs[0].num = PC_REGNUM; | 
 | 	kdp->regs[0].val = regs->nip; | 
 |  | 
 | 	kdp->regs[1].num = SP_REGNUM; | 
 | 	kdp->regs[1].val = regs->gpr[SP_REGNUM]; | 
 | } | 
 |  | 
 | void | 
 | kgdb_exit(struct pt_regs *regs, kgdb_data *kdp) | 
 | { | 
 | 	unsigned long msr = kdp->private[0]; | 
 |  | 
 | 	if (kdp->extype & KGDBEXIT_WITHADDR) | 
 | 		regs->nip = kdp->exaddr; | 
 |  | 
 | 	switch (kdp->extype & KGDBEXIT_TYPEMASK) { | 
 |  | 
 | 	case KGDBEXIT_KILL: | 
 | 	case KGDBEXIT_CONTINUE: | 
 | 		set_msr(msr); | 
 | 		break; | 
 |  | 
 | 	case KGDBEXIT_SINGLE: | 
 | 		regs->msr |= MSR_SE; | 
 | #if 0 | 
 | 		set_msr(msr | MSR_SE); | 
 | #endif | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | int | 
 | kgdb_trap(struct pt_regs *regs) | 
 | { | 
 | 	return (regs->trap); | 
 | } | 
 |  | 
 | /* return the value of the CPU registers. | 
 |  * some of them are non-PowerPC names :( | 
 |  * they are stored in gdb like: | 
 |  * struct { | 
 |  *     u32 gpr[32]; | 
 |  *     f64 fpr[32]; | 
 |  *     u32 pc, ps, cnd, lr; (ps=msr) | 
 |  *     u32 cnt, xer, mq; | 
 |  * } | 
 |  */ | 
 |  | 
 | #define SPACE_REQUIRED	((32*4)+(32*8)+(6*4)) | 
 |  | 
 | #ifdef CONFIG_8260 | 
 | /* store floating double indexed */ | 
 | #define STFDI(n,p)	__asm__ __volatile__ ("stfd " #n ",%0" : "=o"(p[2*n])) | 
 | /* store floating double multiple */ | 
 | #define STFDM(p)	{ STFDI( 0,p); STFDI( 1,p); STFDI( 2,p); STFDI( 3,p); \ | 
 | 			  STFDI( 4,p); STFDI( 5,p); STFDI( 6,p); STFDI( 7,p); \ | 
 | 			  STFDI( 8,p); STFDI( 9,p); STFDI(10,p); STFDI(11,p); \ | 
 | 			  STFDI(12,p); STFDI(13,p); STFDI(14,p); STFDI(15,p); \ | 
 | 			  STFDI(16,p); STFDI(17,p); STFDI(18,p); STFDI(19,p); \ | 
 | 			  STFDI(20,p); STFDI(21,p); STFDI(22,p); STFDI(23,p); \ | 
 | 			  STFDI(24,p); STFDI(25,p); STFDI(26,p); STFDI(27,p); \ | 
 | 			  STFDI(28,p); STFDI(29,p); STFDI(30,p); STFDI(31,p); } | 
 | #endif | 
 |  | 
 | int | 
 | kgdb_getregs(struct pt_regs *regs, char *buf, int max) | 
 | { | 
 | 	int i; | 
 | 	unsigned long *ptr = (unsigned long *)buf; | 
 |  | 
 | 	if (max < SPACE_REQUIRED) | 
 | 		kgdb_error(KGDBERR_NOSPACE); | 
 |  | 
 | 	if ((unsigned long)ptr & 3) | 
 | 		kgdb_error(KGDBERR_ALIGNFAULT); | 
 |  | 
 | 	/* General Purpose Regs */ | 
 | 	for (i = 0; i < 32; i++) | 
 | 		*ptr++ = regs->gpr[i]; | 
 |  | 
 | 	/* Floating Point Regs */ | 
 | #ifdef CONFIG_8260 | 
 | 	STFDM(ptr); | 
 | 	ptr += 32*2; | 
 | #else | 
 | 	for (i = 0; i < 32; i++) { | 
 | 		*ptr++ = 0; | 
 | 		*ptr++ = 0; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	/* pc, msr, cr, lr, ctr, xer, (mq is unused) */ | 
 | 	*ptr++ = regs->nip; | 
 | 	*ptr++ = regs->msr; | 
 | 	*ptr++ = regs->ccr; | 
 | 	*ptr++ = regs->link; | 
 | 	*ptr++ = regs->ctr; | 
 | 	*ptr++ = regs->xer; | 
 |  | 
 | 	return (SPACE_REQUIRED); | 
 | } | 
 |  | 
 | /* set the value of the CPU registers */ | 
 |  | 
 | #ifdef CONFIG_8260 | 
 | /* load floating double */ | 
 | #define LFD(n,v)	__asm__ __volatile__ ("lfd " #n ",%0" :: "o"(v)) | 
 | /* load floating double indexed */ | 
 | #define LFDI(n,p)	__asm__ __volatile__ ("lfd " #n ",%0" :: "o"((p)[2*n])) | 
 | /* load floating double multiple */ | 
 | #define LFDM(p)		{ LFDI( 0,p); LFDI( 1,p); LFDI( 2,p); LFDI( 3,p); \ | 
 | 			  LFDI( 4,p); LFDI( 5,p); LFDI( 6,p); LFDI( 7,p); \ | 
 | 			  LFDI( 8,p); LFDI( 9,p); LFDI(10,p); LFDI(11,p); \ | 
 | 			  LFDI(12,p); LFDI(13,p); LFDI(14,p); LFDI(15,p); \ | 
 | 			  LFDI(16,p); LFDI(17,p); LFDI(18,p); LFDI(19,p); \ | 
 | 			  LFDI(20,p); LFDI(21,p); LFDI(22,p); LFDI(23,p); \ | 
 | 			  LFDI(24,p); LFDI(25,p); LFDI(26,p); LFDI(27,p); \ | 
 | 			  LFDI(28,p); LFDI(29,p); LFDI(30,p); LFDI(31,p); } | 
 | #endif | 
 |  | 
 | void | 
 | kgdb_putreg(struct pt_regs *regs, int regno, char *buf, int length) | 
 | { | 
 | 	unsigned long *ptr = (unsigned long *)buf; | 
 |  | 
 | 	if (regno < 0 || regno >= 70) | 
 | 		kgdb_error(KGDBERR_BADPARAMS); | 
 | 	else if (regno >= 32 && regno < 64) { | 
 | 		if (length < 8) | 
 | 			kgdb_error(KGDBERR_NOSPACE); | 
 | 	} | 
 | 	else { | 
 | 		if (length < 4) | 
 | 			kgdb_error(KGDBERR_NOSPACE); | 
 | 	} | 
 |  | 
 | 	if ((unsigned long)ptr & 3) | 
 | 		kgdb_error(KGDBERR_ALIGNFAULT); | 
 |  | 
 | 	if (regno >= 0 && regno < 32) | 
 | 		regs->gpr[regno] = *ptr; | 
 | 	else switch (regno) { | 
 |  | 
 | #ifdef CONFIG_8260 | 
 | #define caseF(n) \ | 
 | 	case (n) + 32:	LFD(n, *ptr);		break; | 
 |  | 
 | caseF( 0) caseF( 1) caseF( 2) caseF( 3) caseF( 4) caseF( 5) caseF( 6) caseF( 7) | 
 | caseF( 8) caseF( 9) caseF(10) caseF(11) caseF(12) caseF(13) caseF(14) caseF(15) | 
 | caseF(16) caseF(17) caseF(18) caseF(19) caseF(20) caseF(21) caseF(22) caseF(23) | 
 | caseF(24) caseF(25) caseF(26) caseF(27) caseF(28) caseF(29) caseF(30) caseF(31) | 
 |  | 
 | #undef caseF | 
 | #endif | 
 |  | 
 | 	case 64:	regs->nip = *ptr;	break; | 
 | 	case 65:	regs->msr = *ptr;	break; | 
 | 	case 66:	regs->ccr = *ptr;	break; | 
 | 	case 67:	regs->link = *ptr;	break; | 
 | 	case 68:	regs->ctr = *ptr;	break; | 
 | 	case 69:	regs->ctr = *ptr;	break; | 
 |  | 
 | 	default: | 
 | 		kgdb_error(KGDBERR_BADPARAMS); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | kgdb_putregs(struct pt_regs *regs, char *buf, int length) | 
 | { | 
 | 	int i; | 
 | 	unsigned long *ptr = (unsigned long *)buf; | 
 |  | 
 | 	if (length < SPACE_REQUIRED) | 
 | 		kgdb_error(KGDBERR_NOSPACE); | 
 |  | 
 | 	if ((unsigned long)ptr & 3) | 
 | 		kgdb_error(KGDBERR_ALIGNFAULT); | 
 |  | 
 | 	/* | 
 | 	 * If the stack pointer has moved, you should pray. | 
 | 	 * (cause only god can help you). | 
 | 	 */ | 
 |  | 
 | 	/* General Purpose Regs */ | 
 | 	for (i = 0; i < 32; i++) | 
 | 		regs->gpr[i] = *ptr++; | 
 |  | 
 | 	/* Floating Point Regs */ | 
 | #ifdef CONFIG_8260 | 
 | 	LFDM(ptr); | 
 | #endif | 
 | 	ptr += 32*2; | 
 |  | 
 | 	/* pc, msr, cr, lr, ctr, xer, (mq is unused) */ | 
 | 	regs->nip = *ptr++; | 
 | 	regs->msr = *ptr++; | 
 | 	regs->ccr = *ptr++; | 
 | 	regs->link = *ptr++; | 
 | 	regs->ctr = *ptr++; | 
 | 	regs->xer = *ptr++; | 
 | } | 
 |  | 
 | /* This function will generate a breakpoint exception.  It is used at the | 
 |    beginning of a program to sync up with a debugger and can be used | 
 |    otherwise as a quick means to stop program execution and "break" into | 
 |    the debugger. */ | 
 |  | 
 | void | 
 | kgdb_breakpoint(int argc, char *argv[]) | 
 | { | 
 | 	asm("	.globl breakinst\n\ | 
 | 	     breakinst: .long 0x7d821008\n\ | 
 | 	    "); | 
 | } | 
 |  | 
 | #endif /* CFG_CMD_KGDB */ |