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