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