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