diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/kernel/ppc-stub.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc/kernel/ppc-stub.c')
-rw-r--r-- | arch/ppc/kernel/ppc-stub.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c new file mode 100644 index 000000000000..d61889c24046 --- /dev/null +++ b/arch/ppc/kernel/ppc-stub.c | |||
@@ -0,0 +1,867 @@ | |||
1 | /* | ||
2 | * ppc-stub.c: KGDB support for the Linux kernel. | ||
3 | * | ||
4 | * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC | ||
5 | * some stuff borrowed from Paul Mackerras' xmon | ||
6 | * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) | ||
7 | * | ||
8 | * Modifications to run under Linux | ||
9 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | ||
10 | * | ||
11 | * This file originally came from the gdb sources, and the | ||
12 | * copyright notices have been retained below. | ||
13 | */ | ||
14 | |||
15 | /**************************************************************************** | ||
16 | |||
17 | THIS SOFTWARE IS NOT COPYRIGHTED | ||
18 | |||
19 | HP offers the following for use in the public domain. HP makes no | ||
20 | warranty with regard to the software or its performance and the | ||
21 | user accepts the software "AS IS" with all faults. | ||
22 | |||
23 | HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD | ||
24 | TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
25 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
26 | |||
27 | ****************************************************************************/ | ||
28 | |||
29 | /**************************************************************************** | ||
30 | * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ | ||
31 | * | ||
32 | * Module name: remcom.c $ | ||
33 | * Revision: 1.34 $ | ||
34 | * Date: 91/03/09 12:29:49 $ | ||
35 | * Contributor: Lake Stevens Instrument Division$ | ||
36 | * | ||
37 | * Description: low level support for gdb debugger. $ | ||
38 | * | ||
39 | * Considerations: only works on target hardware $ | ||
40 | * | ||
41 | * Written by: Glenn Engel $ | ||
42 | * ModuleState: Experimental $ | ||
43 | * | ||
44 | * NOTES: See Below $ | ||
45 | * | ||
46 | * Modified for SPARC by Stu Grossman, Cygnus Support. | ||
47 | * | ||
48 | * This code has been extensively tested on the Fujitsu SPARClite demo board. | ||
49 | * | ||
50 | * To enable debugger support, two things need to happen. One, a | ||
51 | * call to set_debug_traps() is necessary in order to allow any breakpoints | ||
52 | * or error conditions to be properly intercepted and reported to gdb. | ||
53 | * Two, a breakpoint needs to be generated to begin communication. This | ||
54 | * is most easily accomplished by a call to breakpoint(). Breakpoint() | ||
55 | * simulates a breakpoint by executing a trap #1. | ||
56 | * | ||
57 | ************* | ||
58 | * | ||
59 | * The following gdb commands are supported: | ||
60 | * | ||
61 | * command function Return value | ||
62 | * | ||
63 | * g return the value of the CPU registers hex data or ENN | ||
64 | * G set the value of the CPU registers OK or ENN | ||
65 | * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz | ||
66 | * | ||
67 | * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN | ||
68 | * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN | ||
69 | * | ||
70 | * c Resume at current address SNN ( signal NN) | ||
71 | * cAA..AA Continue at address AA..AA SNN | ||
72 | * | ||
73 | * s Step one instruction SNN | ||
74 | * sAA..AA Step one instruction from AA..AA SNN | ||
75 | * | ||
76 | * k kill | ||
77 | * | ||
78 | * ? What was the last sigval ? SNN (signal NN) | ||
79 | * | ||
80 | * bBB..BB Set baud rate to BB..BB OK or BNN, then sets | ||
81 | * baud rate | ||
82 | * | ||
83 | * All commands and responses are sent with a packet which includes a | ||
84 | * checksum. A packet consists of | ||
85 | * | ||
86 | * $<packet info>#<checksum>. | ||
87 | * | ||
88 | * where | ||
89 | * <packet info> :: <characters representing the command or response> | ||
90 | * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>> | ||
91 | * | ||
92 | * When a packet is received, it is first acknowledged with either '+' or '-'. | ||
93 | * '+' indicates a successful transfer. '-' indicates a failed transfer. | ||
94 | * | ||
95 | * Example: | ||
96 | * | ||
97 | * Host: Reply: | ||
98 | * $m0,10#2a +$00010203040506070809101112131415#42 | ||
99 | * | ||
100 | ****************************************************************************/ | ||
101 | |||
102 | #include <linux/config.h> | ||
103 | #include <linux/kernel.h> | ||
104 | #include <linux/string.h> | ||
105 | #include <linux/mm.h> | ||
106 | #include <linux/smp.h> | ||
107 | #include <linux/smp_lock.h> | ||
108 | #include <linux/init.h> | ||
109 | #include <linux/sysrq.h> | ||
110 | |||
111 | #include <asm/cacheflush.h> | ||
112 | #include <asm/system.h> | ||
113 | #include <asm/signal.h> | ||
114 | #include <asm/kgdb.h> | ||
115 | #include <asm/pgtable.h> | ||
116 | #include <asm/ptrace.h> | ||
117 | |||
118 | void breakinst(void); | ||
119 | |||
120 | /* | ||
121 | * BUFMAX defines the maximum number of characters in inbound/outbound buffers | ||
122 | * at least NUMREGBYTES*2 are needed for register packets | ||
123 | */ | ||
124 | #define BUFMAX 2048 | ||
125 | static char remcomInBuffer[BUFMAX]; | ||
126 | static char remcomOutBuffer[BUFMAX]; | ||
127 | |||
128 | static int initialized; | ||
129 | static int kgdb_active; | ||
130 | static int kgdb_started; | ||
131 | static u_int fault_jmp_buf[100]; | ||
132 | static int kdebug; | ||
133 | |||
134 | |||
135 | static const char hexchars[]="0123456789abcdef"; | ||
136 | |||
137 | /* Place where we save old trap entries for restoration - sparc*/ | ||
138 | /* struct tt_entry kgdb_savettable[256]; */ | ||
139 | /* typedef void (*trapfunc_t)(void); */ | ||
140 | |||
141 | static void kgdb_fault_handler(struct pt_regs *regs); | ||
142 | static int handle_exception (struct pt_regs *regs); | ||
143 | |||
144 | #if 0 | ||
145 | /* Install an exception handler for kgdb */ | ||
146 | static void exceptionHandler(int tnum, unsigned int *tfunc) | ||
147 | { | ||
148 | /* We are dorking with a live trap table, all irqs off */ | ||
149 | } | ||
150 | #endif | ||
151 | |||
152 | int | ||
153 | kgdb_setjmp(long *buf) | ||
154 | { | ||
155 | asm ("mflr 0; stw 0,0(%0);" | ||
156 | "stw 1,4(%0); stw 2,8(%0);" | ||
157 | "mfcr 0; stw 0,12(%0);" | ||
158 | "stmw 13,16(%0)" | ||
159 | : : "r" (buf)); | ||
160 | /* XXX should save fp regs as well */ | ||
161 | return 0; | ||
162 | } | ||
163 | void | ||
164 | kgdb_longjmp(long *buf, int val) | ||
165 | { | ||
166 | if (val == 0) | ||
167 | val = 1; | ||
168 | asm ("lmw 13,16(%0);" | ||
169 | "lwz 0,12(%0); mtcrf 0x38,0;" | ||
170 | "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" | ||
171 | "mtlr 0; mr 3,%1" | ||
172 | : : "r" (buf), "r" (val)); | ||
173 | } | ||
174 | /* Convert ch from a hex digit to an int */ | ||
175 | static int | ||
176 | hex(unsigned char ch) | ||
177 | { | ||
178 | if (ch >= 'a' && ch <= 'f') | ||
179 | return ch-'a'+10; | ||
180 | if (ch >= '0' && ch <= '9') | ||
181 | return ch-'0'; | ||
182 | if (ch >= 'A' && ch <= 'F') | ||
183 | return ch-'A'+10; | ||
184 | return -1; | ||
185 | } | ||
186 | |||
187 | /* Convert the memory pointed to by mem into hex, placing result in buf. | ||
188 | * Return a pointer to the last char put in buf (null), in case of mem fault, | ||
189 | * return 0. | ||
190 | */ | ||
191 | static unsigned char * | ||
192 | mem2hex(const char *mem, char *buf, int count) | ||
193 | { | ||
194 | unsigned char ch; | ||
195 | unsigned short tmp_s; | ||
196 | unsigned long tmp_l; | ||
197 | |||
198 | if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { | ||
199 | debugger_fault_handler = kgdb_fault_handler; | ||
200 | |||
201 | /* Accessing 16 bit and 32 bit objects in a single | ||
202 | ** load instruction is required to avoid bad side | ||
203 | ** effects for some IO registers. | ||
204 | */ | ||
205 | |||
206 | if ((count == 2) && (((long)mem & 1) == 0)) { | ||
207 | tmp_s = *(unsigned short *)mem; | ||
208 | mem += 2; | ||
209 | *buf++ = hexchars[(tmp_s >> 12) & 0xf]; | ||
210 | *buf++ = hexchars[(tmp_s >> 8) & 0xf]; | ||
211 | *buf++ = hexchars[(tmp_s >> 4) & 0xf]; | ||
212 | *buf++ = hexchars[tmp_s & 0xf]; | ||
213 | |||
214 | } else if ((count == 4) && (((long)mem & 3) == 0)) { | ||
215 | tmp_l = *(unsigned int *)mem; | ||
216 | mem += 4; | ||
217 | *buf++ = hexchars[(tmp_l >> 28) & 0xf]; | ||
218 | *buf++ = hexchars[(tmp_l >> 24) & 0xf]; | ||
219 | *buf++ = hexchars[(tmp_l >> 20) & 0xf]; | ||
220 | *buf++ = hexchars[(tmp_l >> 16) & 0xf]; | ||
221 | *buf++ = hexchars[(tmp_l >> 12) & 0xf]; | ||
222 | *buf++ = hexchars[(tmp_l >> 8) & 0xf]; | ||
223 | *buf++ = hexchars[(tmp_l >> 4) & 0xf]; | ||
224 | *buf++ = hexchars[tmp_l & 0xf]; | ||
225 | |||
226 | } else { | ||
227 | while (count-- > 0) { | ||
228 | ch = *mem++; | ||
229 | *buf++ = hexchars[ch >> 4]; | ||
230 | *buf++ = hexchars[ch & 0xf]; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | } else { | ||
235 | /* error condition */ | ||
236 | } | ||
237 | debugger_fault_handler = NULL; | ||
238 | *buf = 0; | ||
239 | return buf; | ||
240 | } | ||
241 | |||
242 | /* convert the hex array pointed to by buf into binary to be placed in mem | ||
243 | * return a pointer to the character AFTER the last byte written. | ||
244 | */ | ||
245 | static char * | ||
246 | hex2mem(char *buf, char *mem, int count) | ||
247 | { | ||
248 | unsigned char ch; | ||
249 | int i; | ||
250 | char *orig_mem; | ||
251 | unsigned short tmp_s; | ||
252 | unsigned long tmp_l; | ||
253 | |||
254 | orig_mem = mem; | ||
255 | |||
256 | if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { | ||
257 | debugger_fault_handler = kgdb_fault_handler; | ||
258 | |||
259 | /* Accessing 16 bit and 32 bit objects in a single | ||
260 | ** store instruction is required to avoid bad side | ||
261 | ** effects for some IO registers. | ||
262 | */ | ||
263 | |||
264 | if ((count == 2) && (((long)mem & 1) == 0)) { | ||
265 | tmp_s = hex(*buf++) << 12; | ||
266 | tmp_s |= hex(*buf++) << 8; | ||
267 | tmp_s |= hex(*buf++) << 4; | ||
268 | tmp_s |= hex(*buf++); | ||
269 | |||
270 | *(unsigned short *)mem = tmp_s; | ||
271 | mem += 2; | ||
272 | |||
273 | } else if ((count == 4) && (((long)mem & 3) == 0)) { | ||
274 | tmp_l = hex(*buf++) << 28; | ||
275 | tmp_l |= hex(*buf++) << 24; | ||
276 | tmp_l |= hex(*buf++) << 20; | ||
277 | tmp_l |= hex(*buf++) << 16; | ||
278 | tmp_l |= hex(*buf++) << 12; | ||
279 | tmp_l |= hex(*buf++) << 8; | ||
280 | tmp_l |= hex(*buf++) << 4; | ||
281 | tmp_l |= hex(*buf++); | ||
282 | |||
283 | *(unsigned long *)mem = tmp_l; | ||
284 | mem += 4; | ||
285 | |||
286 | } else { | ||
287 | for (i=0; i<count; i++) { | ||
288 | ch = hex(*buf++) << 4; | ||
289 | ch |= hex(*buf++); | ||
290 | *mem++ = ch; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | |||
295 | /* | ||
296 | ** Flush the data cache, invalidate the instruction cache. | ||
297 | */ | ||
298 | flush_icache_range((int)orig_mem, (int)orig_mem + count - 1); | ||
299 | |||
300 | } else { | ||
301 | /* error condition */ | ||
302 | } | ||
303 | debugger_fault_handler = NULL; | ||
304 | return mem; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * While we find nice hex chars, build an int. | ||
309 | * Return number of chars processed. | ||
310 | */ | ||
311 | static int | ||
312 | hexToInt(char **ptr, int *intValue) | ||
313 | { | ||
314 | int numChars = 0; | ||
315 | int hexValue; | ||
316 | |||
317 | *intValue = 0; | ||
318 | |||
319 | if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { | ||
320 | debugger_fault_handler = kgdb_fault_handler; | ||
321 | while (**ptr) { | ||
322 | hexValue = hex(**ptr); | ||
323 | if (hexValue < 0) | ||
324 | break; | ||
325 | |||
326 | *intValue = (*intValue << 4) | hexValue; | ||
327 | numChars ++; | ||
328 | |||
329 | (*ptr)++; | ||
330 | } | ||
331 | } else { | ||
332 | /* error condition */ | ||
333 | } | ||
334 | debugger_fault_handler = NULL; | ||
335 | |||
336 | return (numChars); | ||
337 | } | ||
338 | |||
339 | /* scan for the sequence $<data>#<checksum> */ | ||
340 | static void | ||
341 | getpacket(char *buffer) | ||
342 | { | ||
343 | unsigned char checksum; | ||
344 | unsigned char xmitcsum; | ||
345 | int i; | ||
346 | int count; | ||
347 | unsigned char ch; | ||
348 | |||
349 | do { | ||
350 | /* wait around for the start character, ignore all other | ||
351 | * characters */ | ||
352 | while ((ch = (getDebugChar() & 0x7f)) != '$') ; | ||
353 | |||
354 | checksum = 0; | ||
355 | xmitcsum = -1; | ||
356 | |||
357 | count = 0; | ||
358 | |||
359 | /* now, read until a # or end of buffer is found */ | ||
360 | while (count < BUFMAX) { | ||
361 | ch = getDebugChar() & 0x7f; | ||
362 | if (ch == '#') | ||
363 | break; | ||
364 | checksum = checksum + ch; | ||
365 | buffer[count] = ch; | ||
366 | count = count + 1; | ||
367 | } | ||
368 | |||
369 | if (count >= BUFMAX) | ||
370 | continue; | ||
371 | |||
372 | buffer[count] = 0; | ||
373 | |||
374 | if (ch == '#') { | ||
375 | xmitcsum = hex(getDebugChar() & 0x7f) << 4; | ||
376 | xmitcsum |= hex(getDebugChar() & 0x7f); | ||
377 | if (checksum != xmitcsum) | ||
378 | putDebugChar('-'); /* failed checksum */ | ||
379 | else { | ||
380 | putDebugChar('+'); /* successful transfer */ | ||
381 | /* if a sequence char is present, reply the ID */ | ||
382 | if (buffer[2] == ':') { | ||
383 | putDebugChar(buffer[0]); | ||
384 | putDebugChar(buffer[1]); | ||
385 | /* remove sequence chars from buffer */ | ||
386 | count = strlen(buffer); | ||
387 | for (i=3; i <= count; i++) | ||
388 | buffer[i-3] = buffer[i]; | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | } while (checksum != xmitcsum); | ||
393 | } | ||
394 | |||
395 | /* send the packet in buffer. */ | ||
396 | static void putpacket(unsigned char *buffer) | ||
397 | { | ||
398 | unsigned char checksum; | ||
399 | int count; | ||
400 | unsigned char ch, recv; | ||
401 | |||
402 | /* $<packet info>#<checksum>. */ | ||
403 | do { | ||
404 | putDebugChar('$'); | ||
405 | checksum = 0; | ||
406 | count = 0; | ||
407 | |||
408 | while ((ch = buffer[count])) { | ||
409 | putDebugChar(ch); | ||
410 | checksum += ch; | ||
411 | count += 1; | ||
412 | } | ||
413 | |||
414 | putDebugChar('#'); | ||
415 | putDebugChar(hexchars[checksum >> 4]); | ||
416 | putDebugChar(hexchars[checksum & 0xf]); | ||
417 | recv = getDebugChar(); | ||
418 | } while ((recv & 0x7f) != '+'); | ||
419 | } | ||
420 | |||
421 | static void kgdb_flush_cache_all(void) | ||
422 | { | ||
423 | flush_instruction_cache(); | ||
424 | } | ||
425 | |||
426 | /* Set up exception handlers for tracing and breakpoints | ||
427 | * [could be called kgdb_init()] | ||
428 | */ | ||
429 | void set_debug_traps(void) | ||
430 | { | ||
431 | #if 0 | ||
432 | unsigned char c; | ||
433 | |||
434 | save_and_cli(flags); | ||
435 | |||
436 | /* In case GDB is started before us, ack any packets (presumably | ||
437 | * "$?#xx") sitting there. | ||
438 | * | ||
439 | * I've found this code causes more problems than it solves, | ||
440 | * so that's why it's commented out. GDB seems to work fine | ||
441 | * now starting either before or after the kernel -bwb | ||
442 | */ | ||
443 | |||
444 | while((c = getDebugChar()) != '$'); | ||
445 | while((c = getDebugChar()) != '#'); | ||
446 | c = getDebugChar(); /* eat first csum byte */ | ||
447 | c = getDebugChar(); /* eat second csum byte */ | ||
448 | putDebugChar('+'); /* ack it */ | ||
449 | #endif | ||
450 | debugger = kgdb; | ||
451 | debugger_bpt = kgdb_bpt; | ||
452 | debugger_sstep = kgdb_sstep; | ||
453 | debugger_iabr_match = kgdb_iabr_match; | ||
454 | debugger_dabr_match = kgdb_dabr_match; | ||
455 | |||
456 | initialized = 1; | ||
457 | } | ||
458 | |||
459 | static void kgdb_fault_handler(struct pt_regs *regs) | ||
460 | { | ||
461 | kgdb_longjmp((long*)fault_jmp_buf, 1); | ||
462 | } | ||
463 | |||
464 | int kgdb_bpt(struct pt_regs *regs) | ||
465 | { | ||
466 | return handle_exception(regs); | ||
467 | } | ||
468 | |||
469 | int kgdb_sstep(struct pt_regs *regs) | ||
470 | { | ||
471 | return handle_exception(regs); | ||
472 | } | ||
473 | |||
474 | void kgdb(struct pt_regs *regs) | ||
475 | { | ||
476 | handle_exception(regs); | ||
477 | } | ||
478 | |||
479 | int kgdb_iabr_match(struct pt_regs *regs) | ||
480 | { | ||
481 | printk(KERN_ERR "kgdb doesn't support iabr, what?!?\n"); | ||
482 | return handle_exception(regs); | ||
483 | } | ||
484 | |||
485 | int kgdb_dabr_match(struct pt_regs *regs) | ||
486 | { | ||
487 | printk(KERN_ERR "kgdb doesn't support dabr, what?!?\n"); | ||
488 | return handle_exception(regs); | ||
489 | } | ||
490 | |||
491 | /* Convert the hardware trap type code to a unix signal number. */ | ||
492 | /* | ||
493 | * This table contains the mapping between PowerPC hardware trap types, and | ||
494 | * signals, which are primarily what GDB understands. | ||
495 | */ | ||
496 | static struct hard_trap_info | ||
497 | { | ||
498 | unsigned int tt; /* Trap type code for powerpc */ | ||
499 | unsigned char signo; /* Signal that we map this trap into */ | ||
500 | } hard_trap_info[] = { | ||
501 | #if defined(CONFIG_40x) || defined(CONFIG_BOOKE) | ||
502 | { 0x100, SIGINT }, /* critical input interrupt */ | ||
503 | { 0x200, SIGSEGV }, /* machine check */ | ||
504 | { 0x300, SIGSEGV }, /* data storage */ | ||
505 | { 0x400, SIGBUS }, /* instruction storage */ | ||
506 | { 0x500, SIGINT }, /* interrupt */ | ||
507 | { 0x600, SIGBUS }, /* alignment */ | ||
508 | { 0x700, SIGILL }, /* program */ | ||
509 | { 0x800, SIGILL }, /* reserved */ | ||
510 | { 0x900, SIGILL }, /* reserved */ | ||
511 | { 0xa00, SIGILL }, /* reserved */ | ||
512 | { 0xb00, SIGILL }, /* reserved */ | ||
513 | { 0xc00, SIGCHLD }, /* syscall */ | ||
514 | { 0xd00, SIGILL }, /* reserved */ | ||
515 | { 0xe00, SIGILL }, /* reserved */ | ||
516 | { 0xf00, SIGILL }, /* reserved */ | ||
517 | /* | ||
518 | ** 0x1000 PIT | ||
519 | ** 0x1010 FIT | ||
520 | ** 0x1020 watchdog | ||
521 | ** 0x1100 data TLB miss | ||
522 | ** 0x1200 instruction TLB miss | ||
523 | */ | ||
524 | { 0x2002, SIGTRAP}, /* debug */ | ||
525 | #else | ||
526 | { 0x200, SIGSEGV }, /* machine check */ | ||
527 | { 0x300, SIGSEGV }, /* address error (store) */ | ||
528 | { 0x400, SIGBUS }, /* instruction bus error */ | ||
529 | { 0x500, SIGINT }, /* interrupt */ | ||
530 | { 0x600, SIGBUS }, /* alingment */ | ||
531 | { 0x700, SIGTRAP }, /* breakpoint trap */ | ||
532 | { 0x800, SIGFPE }, /* fpu unavail */ | ||
533 | { 0x900, SIGALRM }, /* decrementer */ | ||
534 | { 0xa00, SIGILL }, /* reserved */ | ||
535 | { 0xb00, SIGILL }, /* reserved */ | ||
536 | { 0xc00, SIGCHLD }, /* syscall */ | ||
537 | { 0xd00, SIGTRAP }, /* single-step/watch */ | ||
538 | { 0xe00, SIGFPE }, /* fp assist */ | ||
539 | #endif | ||
540 | { 0, 0} /* Must be last */ | ||
541 | |||
542 | }; | ||
543 | |||
544 | static int computeSignal(unsigned int tt) | ||
545 | { | ||
546 | struct hard_trap_info *ht; | ||
547 | |||
548 | for (ht = hard_trap_info; ht->tt && ht->signo; ht++) | ||
549 | if (ht->tt == tt) | ||
550 | return ht->signo; | ||
551 | |||
552 | return SIGHUP; /* default for things we don't know about */ | ||
553 | } | ||
554 | |||
555 | #define PC_REGNUM 64 | ||
556 | #define SP_REGNUM 1 | ||
557 | |||
558 | /* | ||
559 | * This function does all command processing for interfacing to gdb. | ||
560 | */ | ||
561 | static int | ||
562 | handle_exception (struct pt_regs *regs) | ||
563 | { | ||
564 | int sigval; | ||
565 | int addr; | ||
566 | int length; | ||
567 | char *ptr; | ||
568 | unsigned int msr; | ||
569 | |||
570 | /* We don't handle user-mode breakpoints. */ | ||
571 | if (user_mode(regs)) | ||
572 | return 0; | ||
573 | |||
574 | if (debugger_fault_handler) { | ||
575 | debugger_fault_handler(regs); | ||
576 | panic("kgdb longjump failed!\n"); | ||
577 | } | ||
578 | if (kgdb_active) { | ||
579 | printk(KERN_ERR "interrupt while in kgdb, returning\n"); | ||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | kgdb_active = 1; | ||
584 | kgdb_started = 1; | ||
585 | |||
586 | #ifdef KGDB_DEBUG | ||
587 | printk("kgdb: entering handle_exception; trap [0x%x]\n", | ||
588 | (unsigned int)regs->trap); | ||
589 | #endif | ||
590 | |||
591 | kgdb_interruptible(0); | ||
592 | lock_kernel(); | ||
593 | msr = mfmsr(); | ||
594 | mtmsr(msr & ~MSR_EE); /* disable interrupts */ | ||
595 | |||
596 | if (regs->nip == (unsigned long)breakinst) { | ||
597 | /* Skip over breakpoint trap insn */ | ||
598 | regs->nip += 4; | ||
599 | } | ||
600 | |||
601 | /* reply to host that an exception has occurred */ | ||
602 | sigval = computeSignal(regs->trap); | ||
603 | ptr = remcomOutBuffer; | ||
604 | |||
605 | *ptr++ = 'T'; | ||
606 | *ptr++ = hexchars[sigval >> 4]; | ||
607 | *ptr++ = hexchars[sigval & 0xf]; | ||
608 | *ptr++ = hexchars[PC_REGNUM >> 4]; | ||
609 | *ptr++ = hexchars[PC_REGNUM & 0xf]; | ||
610 | *ptr++ = ':'; | ||
611 | ptr = mem2hex((char *)®s->nip, ptr, 4); | ||
612 | *ptr++ = ';'; | ||
613 | *ptr++ = hexchars[SP_REGNUM >> 4]; | ||
614 | *ptr++ = hexchars[SP_REGNUM & 0xf]; | ||
615 | *ptr++ = ':'; | ||
616 | ptr = mem2hex(((char *)regs) + SP_REGNUM*4, ptr, 4); | ||
617 | *ptr++ = ';'; | ||
618 | *ptr++ = 0; | ||
619 | |||
620 | putpacket(remcomOutBuffer); | ||
621 | if (kdebug) | ||
622 | printk("remcomOutBuffer: %s\n", remcomOutBuffer); | ||
623 | |||
624 | /* XXX We may want to add some features dealing with poking the | ||
625 | * XXX page tables, ... (look at sparc-stub.c for more info) | ||
626 | * XXX also required hacking to the gdb sources directly... | ||
627 | */ | ||
628 | |||
629 | while (1) { | ||
630 | remcomOutBuffer[0] = 0; | ||
631 | |||
632 | getpacket(remcomInBuffer); | ||
633 | switch (remcomInBuffer[0]) { | ||
634 | case '?': /* report most recent signal */ | ||
635 | remcomOutBuffer[0] = 'S'; | ||
636 | remcomOutBuffer[1] = hexchars[sigval >> 4]; | ||
637 | remcomOutBuffer[2] = hexchars[sigval & 0xf]; | ||
638 | remcomOutBuffer[3] = 0; | ||
639 | break; | ||
640 | #if 0 | ||
641 | case 'q': /* this screws up gdb for some reason...*/ | ||
642 | { | ||
643 | extern long _start, sdata, __bss_start; | ||
644 | |||
645 | ptr = &remcomInBuffer[1]; | ||
646 | if (strncmp(ptr, "Offsets", 7) != 0) | ||
647 | break; | ||
648 | |||
649 | ptr = remcomOutBuffer; | ||
650 | sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", | ||
651 | &_start, &sdata, &__bss_start); | ||
652 | break; | ||
653 | } | ||
654 | #endif | ||
655 | case 'd': | ||
656 | /* toggle debug flag */ | ||
657 | kdebug ^= 1; | ||
658 | break; | ||
659 | |||
660 | case 'g': /* return the value of the CPU registers. | ||
661 | * some of them are non-PowerPC names :( | ||
662 | * they are stored in gdb like: | ||
663 | * struct { | ||
664 | * u32 gpr[32]; | ||
665 | * f64 fpr[32]; | ||
666 | * u32 pc, ps, cnd, lr; (ps=msr) | ||
667 | * u32 cnt, xer, mq; | ||
668 | * } | ||
669 | */ | ||
670 | { | ||
671 | int i; | ||
672 | ptr = remcomOutBuffer; | ||
673 | /* General Purpose Regs */ | ||
674 | ptr = mem2hex((char *)regs, ptr, 32 * 4); | ||
675 | /* Floating Point Regs - FIXME */ | ||
676 | /*ptr = mem2hex((char *), ptr, 32 * 8);*/ | ||
677 | for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ | ||
678 | ptr[i] = '0'; | ||
679 | } | ||
680 | ptr += 32*8*2; | ||
681 | /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ | ||
682 | ptr = mem2hex((char *)®s->nip, ptr, 4); | ||
683 | ptr = mem2hex((char *)®s->msr, ptr, 4); | ||
684 | ptr = mem2hex((char *)®s->ccr, ptr, 4); | ||
685 | ptr = mem2hex((char *)®s->link, ptr, 4); | ||
686 | ptr = mem2hex((char *)®s->ctr, ptr, 4); | ||
687 | ptr = mem2hex((char *)®s->xer, ptr, 4); | ||
688 | } | ||
689 | break; | ||
690 | |||
691 | case 'G': /* set the value of the CPU registers */ | ||
692 | { | ||
693 | ptr = &remcomInBuffer[1]; | ||
694 | |||
695 | /* | ||
696 | * If the stack pointer has moved, you should pray. | ||
697 | * (cause only god can help you). | ||
698 | */ | ||
699 | |||
700 | /* General Purpose Regs */ | ||
701 | hex2mem(ptr, (char *)regs, 32 * 4); | ||
702 | |||
703 | /* Floating Point Regs - FIXME?? */ | ||
704 | /*ptr = hex2mem(ptr, ??, 32 * 8);*/ | ||
705 | ptr += 32*8*2; | ||
706 | |||
707 | /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ | ||
708 | ptr = hex2mem(ptr, (char *)®s->nip, 4); | ||
709 | ptr = hex2mem(ptr, (char *)®s->msr, 4); | ||
710 | ptr = hex2mem(ptr, (char *)®s->ccr, 4); | ||
711 | ptr = hex2mem(ptr, (char *)®s->link, 4); | ||
712 | ptr = hex2mem(ptr, (char *)®s->ctr, 4); | ||
713 | ptr = hex2mem(ptr, (char *)®s->xer, 4); | ||
714 | |||
715 | strcpy(remcomOutBuffer,"OK"); | ||
716 | } | ||
717 | break; | ||
718 | case 'H': | ||
719 | /* don't do anything, yet, just acknowledge */ | ||
720 | hexToInt(&ptr, &addr); | ||
721 | strcpy(remcomOutBuffer,"OK"); | ||
722 | break; | ||
723 | |||
724 | case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ | ||
725 | /* Try to read %x,%x. */ | ||
726 | |||
727 | ptr = &remcomInBuffer[1]; | ||
728 | |||
729 | if (hexToInt(&ptr, &addr) && *ptr++ == ',' | ||
730 | && hexToInt(&ptr, &length)) { | ||
731 | if (mem2hex((char *)addr, remcomOutBuffer, | ||
732 | length)) | ||
733 | break; | ||
734 | strcpy(remcomOutBuffer, "E03"); | ||
735 | } else | ||
736 | strcpy(remcomOutBuffer, "E01"); | ||
737 | break; | ||
738 | |||
739 | case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ | ||
740 | /* Try to read '%x,%x:'. */ | ||
741 | |||
742 | ptr = &remcomInBuffer[1]; | ||
743 | |||
744 | if (hexToInt(&ptr, &addr) && *ptr++ == ',' | ||
745 | && hexToInt(&ptr, &length) | ||
746 | && *ptr++ == ':') { | ||
747 | if (hex2mem(ptr, (char *)addr, length)) | ||
748 | strcpy(remcomOutBuffer, "OK"); | ||
749 | else | ||
750 | strcpy(remcomOutBuffer, "E03"); | ||
751 | flush_icache_range(addr, addr+length); | ||
752 | } else | ||
753 | strcpy(remcomOutBuffer, "E02"); | ||
754 | break; | ||
755 | |||
756 | |||
757 | case 'k': /* kill the program, actually just continue */ | ||
758 | case 'c': /* cAA..AA Continue; address AA..AA optional */ | ||
759 | /* try to read optional parameter, pc unchanged if no parm */ | ||
760 | |||
761 | ptr = &remcomInBuffer[1]; | ||
762 | if (hexToInt(&ptr, &addr)) | ||
763 | regs->nip = addr; | ||
764 | |||
765 | /* Need to flush the instruction cache here, as we may have deposited a | ||
766 | * breakpoint, and the icache probably has no way of knowing that a data ref to | ||
767 | * some location may have changed something that is in the instruction cache. | ||
768 | */ | ||
769 | kgdb_flush_cache_all(); | ||
770 | mtmsr(msr); | ||
771 | |||
772 | kgdb_interruptible(1); | ||
773 | unlock_kernel(); | ||
774 | kgdb_active = 0; | ||
775 | if (kdebug) { | ||
776 | printk("remcomInBuffer: %s\n", remcomInBuffer); | ||
777 | printk("remcomOutBuffer: %s\n", remcomOutBuffer); | ||
778 | } | ||
779 | return 1; | ||
780 | |||
781 | case 's': | ||
782 | kgdb_flush_cache_all(); | ||
783 | #if defined(CONFIG_40x) || defined(CONFIG_BOOKE) | ||
784 | mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC); | ||
785 | regs->msr |= MSR_DE; | ||
786 | #else | ||
787 | regs->msr |= MSR_SE; | ||
788 | #endif | ||
789 | unlock_kernel(); | ||
790 | kgdb_active = 0; | ||
791 | if (kdebug) { | ||
792 | printk("remcomInBuffer: %s\n", remcomInBuffer); | ||
793 | printk("remcomOutBuffer: %s\n", remcomOutBuffer); | ||
794 | } | ||
795 | return 1; | ||
796 | |||
797 | case 'r': /* Reset (if user process..exit ???)*/ | ||
798 | panic("kgdb reset."); | ||
799 | break; | ||
800 | } /* switch */ | ||
801 | if (remcomOutBuffer[0] && kdebug) { | ||
802 | printk("remcomInBuffer: %s\n", remcomInBuffer); | ||
803 | printk("remcomOutBuffer: %s\n", remcomOutBuffer); | ||
804 | } | ||
805 | /* reply to the request */ | ||
806 | putpacket(remcomOutBuffer); | ||
807 | } /* while(1) */ | ||
808 | } | ||
809 | |||
810 | /* This function will generate a breakpoint exception. It is used at the | ||
811 | beginning of a program to sync up with a debugger and can be used | ||
812 | otherwise as a quick means to stop program execution and "break" into | ||
813 | the debugger. */ | ||
814 | |||
815 | void | ||
816 | breakpoint(void) | ||
817 | { | ||
818 | if (!initialized) { | ||
819 | printk("breakpoint() called b4 kgdb init\n"); | ||
820 | return; | ||
821 | } | ||
822 | |||
823 | asm(" .globl breakinst \n\ | ||
824 | breakinst: .long 0x7d821008"); | ||
825 | } | ||
826 | |||
827 | #ifdef CONFIG_KGDB_CONSOLE | ||
828 | /* Output string in GDB O-packet format if GDB has connected. If nothing | ||
829 | output, returns 0 (caller must then handle output). */ | ||
830 | int | ||
831 | kgdb_output_string (const char* s, unsigned int count) | ||
832 | { | ||
833 | char buffer[512]; | ||
834 | |||
835 | if (!kgdb_started) | ||
836 | return 0; | ||
837 | |||
838 | count = (count <= (sizeof(buffer) / 2 - 2)) | ||
839 | ? count : (sizeof(buffer) / 2 - 2); | ||
840 | |||
841 | buffer[0] = 'O'; | ||
842 | mem2hex (s, &buffer[1], count); | ||
843 | putpacket(buffer); | ||
844 | |||
845 | return 1; | ||
846 | } | ||
847 | #endif | ||
848 | |||
849 | static void sysrq_handle_gdb(int key, struct pt_regs *pt_regs, | ||
850 | struct tty_struct *tty) | ||
851 | { | ||
852 | printk("Entering GDB stub\n"); | ||
853 | breakpoint(); | ||
854 | } | ||
855 | static struct sysrq_key_op sysrq_gdb_op = { | ||
856 | .handler = sysrq_handle_gdb, | ||
857 | .help_msg = "Gdb", | ||
858 | .action_msg = "GDB", | ||
859 | }; | ||
860 | |||
861 | static int gdb_register_sysrq(void) | ||
862 | { | ||
863 | printk("Registering GDB sysrq handler\n"); | ||
864 | register_sysrq_key('g', &sysrq_gdb_op); | ||
865 | return 0; | ||
866 | } | ||
867 | module_init(gdb_register_sysrq); | ||