kgdb.c 7.42 KB
Newer Older
Alexander Yurtsev's avatar
Alexander Yurtsev committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
#include <common.h>
#include <command.h>
#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\
	    ");
}