xref: /openbmc/u-boot/arch/powerpc/lib/kgdb.c (revision c0dcece7)
1 #include <common.h>
2 #include <command.h>
3 #include <kgdb.h>
4 #include <asm/signal.h>
5 #include <asm/processor.h>
6 
7 #define PC_REGNUM 64
8 #define SP_REGNUM 1
9 
10 void breakinst(void);
11 
12 int
13 kgdb_setjmp(long *buf)
14 {
15 	unsigned long temp;
16 
17 	asm volatile("mflr %0; stw %0,0(%1);"
18 	     "stw %%r1,4(%1); stw %%r2,8(%1);"
19 	     "mfcr %0; stw %0,12(%1);"
20 	     "stmw %%r13,16(%1)"
21 	     : "=&r"(temp) : "r" (buf));
22 	/* XXX should save fp regs as well */
23 	return 0;
24 }
25 
26 void
27 kgdb_longjmp(long *buf, int val)
28 {
29 	unsigned long temp;
30 
31 	if (val == 0)
32 		val = 1;
33 
34 	asm volatile("lmw %%r13,16(%1);"
35 	     "lwz %0,12(%1); mtcrf 0x38,%0;"
36 	     "lwz %0,0(%1); lwz %%r1,4(%1); lwz %%r2,8(%1);"
37 	     "mtlr %0; mr %%r3,%2"
38 	     : "=&r"(temp) : "r" (buf), "r" (val));
39 }
40 
41 static inline unsigned long
42 get_msr(void)
43 {
44 	unsigned long msr;
45 	asm volatile("mfmsr %0" : "=r" (msr):);
46 	return msr;
47 }
48 
49 static inline void
50 set_msr(unsigned long msr)
51 {
52 	asm volatile("mtmsr %0" : : "r" (msr));
53 }
54 
55 /* Convert the SPARC hardware trap type code to a unix signal number. */
56 /*
57  * This table contains the mapping between PowerPC hardware trap types, and
58  * signals, which are primarily what GDB understands.
59  */
60 static struct hard_trap_info
61 {
62 	unsigned int tt;		/* Trap type code for powerpc */
63 	unsigned char signo;		/* Signal that we map this trap into */
64 } hard_trap_info[] = {
65 	{ 0x200, SIGSEGV },			/* machine check */
66 	{ 0x300, SIGSEGV },			/* address error (store) */
67 	{ 0x400, SIGBUS },			/* instruction bus error */
68 	{ 0x500, SIGINT },			/* interrupt */
69 	{ 0x600, SIGBUS },			/* alingment */
70 	{ 0x700, SIGTRAP },			/* breakpoint trap */
71 	{ 0x800, SIGFPE },			/* fpu unavail */
72 	{ 0x900, SIGALRM },			/* decrementer */
73 	{ 0xa00, SIGILL },			/* reserved */
74 	{ 0xb00, SIGILL },			/* reserved */
75 	{ 0xc00, SIGCHLD },			/* syscall */
76 	{ 0xd00, SIGTRAP },			/* single-step/watch */
77 	{ 0xe00, SIGFPE },			/* fp assist */
78 	{ 0, 0}				/* Must be last */
79 };
80 
81 static int
82 computeSignal(unsigned int tt)
83 {
84 	struct hard_trap_info *ht;
85 
86 	for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
87 		if (ht->tt == tt)
88 			return ht->signo;
89 
90 	return SIGHUP;         /* default for things we don't know about */
91 }
92 
93 void
94 kgdb_enter(struct pt_regs *regs, kgdb_data *kdp)
95 {
96 	unsigned long msr;
97 
98 	kdp->private[0] = msr = get_msr();
99 	set_msr(msr & ~MSR_EE);	/* disable interrupts */
100 
101 	if (regs->nip == (unsigned long)breakinst) {
102 		/* Skip over breakpoint trap insn */
103 		regs->nip += 4;
104 	}
105 	regs->msr &= ~MSR_SE;
106 
107 	/* reply to host that an exception has occurred */
108 	kdp->sigval = computeSignal(regs->trap);
109 
110 	kdp->nregs = 2;
111 
112 	kdp->regs[0].num = PC_REGNUM;
113 	kdp->regs[0].val = regs->nip;
114 
115 	kdp->regs[1].num = SP_REGNUM;
116 	kdp->regs[1].val = regs->gpr[SP_REGNUM];
117 }
118 
119 void
120 kgdb_exit(struct pt_regs *regs, kgdb_data *kdp)
121 {
122 	unsigned long msr = kdp->private[0];
123 
124 	if (kdp->extype & KGDBEXIT_WITHADDR)
125 		regs->nip = kdp->exaddr;
126 
127 	switch (kdp->extype & KGDBEXIT_TYPEMASK) {
128 
129 	case KGDBEXIT_KILL:
130 	case KGDBEXIT_CONTINUE:
131 		set_msr(msr);
132 		break;
133 
134 	case KGDBEXIT_SINGLE:
135 		regs->msr |= MSR_SE;
136 #if 0
137 		set_msr(msr | MSR_SE);
138 #endif
139 		break;
140 	}
141 }
142 
143 int
144 kgdb_trap(struct pt_regs *regs)
145 {
146 	return (regs->trap);
147 }
148 
149 /* return the value of the CPU registers.
150  * some of them are non-PowerPC names :(
151  * they are stored in gdb like:
152  * struct {
153  *     u32 gpr[32];
154  *     f64 fpr[32];
155  *     u32 pc, ps, cnd, lr; (ps=msr)
156  *     u32 cnt, xer, mq;
157  * }
158  */
159 
160 #define SPACE_REQUIRED	((32*4)+(32*8)+(6*4))
161 
162 #ifdef CONFIG_8260
163 /* store floating double indexed */
164 #define STFDI(n,p)	__asm__ __volatile__ ("stfd " #n ",%0" : "=o"(p[2*n]))
165 /* store floating double multiple */
166 #define STFDM(p)	{ STFDI( 0,p); STFDI( 1,p); STFDI( 2,p); STFDI( 3,p); \
167 			  STFDI( 4,p); STFDI( 5,p); STFDI( 6,p); STFDI( 7,p); \
168 			  STFDI( 8,p); STFDI( 9,p); STFDI(10,p); STFDI(11,p); \
169 			  STFDI(12,p); STFDI(13,p); STFDI(14,p); STFDI(15,p); \
170 			  STFDI(16,p); STFDI(17,p); STFDI(18,p); STFDI(19,p); \
171 			  STFDI(20,p); STFDI(21,p); STFDI(22,p); STFDI(23,p); \
172 			  STFDI(24,p); STFDI(25,p); STFDI(26,p); STFDI(27,p); \
173 			  STFDI(28,p); STFDI(29,p); STFDI(30,p); STFDI(31,p); }
174 #endif
175 
176 int
177 kgdb_getregs(struct pt_regs *regs, char *buf, int max)
178 {
179 	int i;
180 	unsigned long *ptr = (unsigned long *)buf;
181 
182 	if (max < SPACE_REQUIRED)
183 		kgdb_error(KGDBERR_NOSPACE);
184 
185 	if ((unsigned long)ptr & 3)
186 		kgdb_error(KGDBERR_ALIGNFAULT);
187 
188 	/* General Purpose Regs */
189 	for (i = 0; i < 32; i++)
190 		*ptr++ = regs->gpr[i];
191 
192 	/* Floating Point Regs */
193 #ifdef CONFIG_8260
194 	STFDM(ptr);
195 	ptr += 32*2;
196 #else
197 	for (i = 0; i < 32; i++) {
198 		*ptr++ = 0;
199 		*ptr++ = 0;
200 	}
201 #endif
202 
203 	/* pc, msr, cr, lr, ctr, xer, (mq is unused) */
204 	*ptr++ = regs->nip;
205 	*ptr++ = regs->msr;
206 	*ptr++ = regs->ccr;
207 	*ptr++ = regs->link;
208 	*ptr++ = regs->ctr;
209 	*ptr++ = regs->xer;
210 
211 	return (SPACE_REQUIRED);
212 }
213 
214 /* set the value of the CPU registers */
215 
216 #ifdef CONFIG_8260
217 /* load floating double */
218 #define LFD(n,v)	__asm__ __volatile__ ("lfd " #n ",%0" :: "o"(v))
219 /* load floating double indexed */
220 #define LFDI(n,p)	__asm__ __volatile__ ("lfd " #n ",%0" :: "o"((p)[2*n]))
221 /* load floating double multiple */
222 #define LFDM(p)		{ LFDI( 0,p); LFDI( 1,p); LFDI( 2,p); LFDI( 3,p); \
223 			  LFDI( 4,p); LFDI( 5,p); LFDI( 6,p); LFDI( 7,p); \
224 			  LFDI( 8,p); LFDI( 9,p); LFDI(10,p); LFDI(11,p); \
225 			  LFDI(12,p); LFDI(13,p); LFDI(14,p); LFDI(15,p); \
226 			  LFDI(16,p); LFDI(17,p); LFDI(18,p); LFDI(19,p); \
227 			  LFDI(20,p); LFDI(21,p); LFDI(22,p); LFDI(23,p); \
228 			  LFDI(24,p); LFDI(25,p); LFDI(26,p); LFDI(27,p); \
229 			  LFDI(28,p); LFDI(29,p); LFDI(30,p); LFDI(31,p); }
230 #endif
231 
232 void
233 kgdb_putreg(struct pt_regs *regs, int regno, char *buf, int length)
234 {
235 	unsigned long *ptr = (unsigned long *)buf;
236 
237 	if (regno < 0 || regno >= 70)
238 		kgdb_error(KGDBERR_BADPARAMS);
239 	else if (regno >= 32 && regno < 64) {
240 		if (length < 8)
241 			kgdb_error(KGDBERR_NOSPACE);
242 	}
243 	else {
244 		if (length < 4)
245 			kgdb_error(KGDBERR_NOSPACE);
246 	}
247 
248 	if ((unsigned long)ptr & 3)
249 		kgdb_error(KGDBERR_ALIGNFAULT);
250 
251 	if (regno >= 0 && regno < 32)
252 		regs->gpr[regno] = *ptr;
253 	else switch (regno) {
254 
255 #ifdef CONFIG_8260
256 #define caseF(n) \
257 	case (n) + 32:	LFD(n, *ptr);		break;
258 
259 caseF( 0) caseF( 1) caseF( 2) caseF( 3) caseF( 4) caseF( 5) caseF( 6) caseF( 7)
260 caseF( 8) caseF( 9) caseF(10) caseF(11) caseF(12) caseF(13) caseF(14) caseF(15)
261 caseF(16) caseF(17) caseF(18) caseF(19) caseF(20) caseF(21) caseF(22) caseF(23)
262 caseF(24) caseF(25) caseF(26) caseF(27) caseF(28) caseF(29) caseF(30) caseF(31)
263 
264 #undef caseF
265 #endif
266 
267 	case 64:	regs->nip = *ptr;	break;
268 	case 65:	regs->msr = *ptr;	break;
269 	case 66:	regs->ccr = *ptr;	break;
270 	case 67:	regs->link = *ptr;	break;
271 	case 68:	regs->ctr = *ptr;	break;
272 	case 69:	regs->ctr = *ptr;	break;
273 
274 	default:
275 		kgdb_error(KGDBERR_BADPARAMS);
276 	}
277 }
278 
279 void
280 kgdb_putregs(struct pt_regs *regs, char *buf, int length)
281 {
282 	int i;
283 	unsigned long *ptr = (unsigned long *)buf;
284 
285 	if (length < SPACE_REQUIRED)
286 		kgdb_error(KGDBERR_NOSPACE);
287 
288 	if ((unsigned long)ptr & 3)
289 		kgdb_error(KGDBERR_ALIGNFAULT);
290 
291 	/*
292 	 * If the stack pointer has moved, you should pray.
293 	 * (cause only god can help you).
294 	 */
295 
296 	/* General Purpose Regs */
297 	for (i = 0; i < 32; i++)
298 		regs->gpr[i] = *ptr++;
299 
300 	/* Floating Point Regs */
301 #ifdef CONFIG_8260
302 	LFDM(ptr);
303 #endif
304 	ptr += 32*2;
305 
306 	/* pc, msr, cr, lr, ctr, xer, (mq is unused) */
307 	regs->nip = *ptr++;
308 	regs->msr = *ptr++;
309 	regs->ccr = *ptr++;
310 	regs->link = *ptr++;
311 	regs->ctr = *ptr++;
312 	regs->xer = *ptr++;
313 }
314 
315 /* This function will generate a breakpoint exception.  It is used at the
316    beginning of a program to sync up with a debugger and can be used
317    otherwise as a quick means to stop program execution and "break" into
318    the debugger. */
319 
320 void
321 kgdb_breakpoint(int argc, char * const argv[])
322 {
323 	asm("	.globl breakinst\n\
324 	     breakinst: .long 0x7d821008\n\
325 	    ");
326 }
327