1 | /* |
---|
2 | * Low-level support for LM32 remote debuging with GDB. |
---|
3 | * Contributed by Jon Beniston <jon@beniston.com> |
---|
4 | * Modified for RTEMS with thread support by Michael Walle <michael@walle.cc> |
---|
5 | * |
---|
6 | * Redistribution and use in source and binary forms, with or without |
---|
7 | * modification, are permitted provided that the following conditions |
---|
8 | * are met: |
---|
9 | * 1. Redistributions of source code must retain the above copyright |
---|
10 | * notice, this list of conditions and the following disclaimer. |
---|
11 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
12 | * notice, this list of conditions and the following disclaimer in the |
---|
13 | * documentation and/or other materials provided with the distribution. |
---|
14 | * |
---|
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
25 | * SUCH DAMAGE. |
---|
26 | */ |
---|
27 | |
---|
28 | /* $Id$ */ |
---|
29 | |
---|
30 | #include <bsp.h> |
---|
31 | #include <string.h> |
---|
32 | #include <signal.h> |
---|
33 | #include <rtems.h> |
---|
34 | #include <rtems/score/cpu.h> |
---|
35 | #include "gdb_if.h" |
---|
36 | |
---|
37 | /* Enable support for run-length encoding */ |
---|
38 | #undef GDB_RLE_ENABLED |
---|
39 | /* Enable support for restart packets */ |
---|
40 | #undef GDB_RESTART_ENABLED |
---|
41 | |
---|
42 | #define GDB_STUB_ENABLE_THREAD_SUPPORT |
---|
43 | |
---|
44 | /* |
---|
45 | * The following external functions provide character input and output. |
---|
46 | */ |
---|
47 | extern char gdb_get_debug_char(void); |
---|
48 | extern void gdb_put_debug_char(char); |
---|
49 | extern void gdb_console_init(void); |
---|
50 | extern void gdb_ack_irq(void); |
---|
51 | extern void *_deba; |
---|
52 | |
---|
53 | /* Function prototypes */ |
---|
54 | static void allow_nested_exception(void); |
---|
55 | static void disallow_nested_exception(void); |
---|
56 | static char *mem2hex(unsigned char *mem, char *buf, int count); |
---|
57 | static unsigned char *hex2mem(char *buf, unsigned char *mem, int count); |
---|
58 | static unsigned char *bin2mem(char *buf, unsigned char *mem, int count); |
---|
59 | static int compute_signal(int eid); |
---|
60 | static void flush_cache(void); |
---|
61 | static int hex2int(char **ptr, int *int_value); |
---|
62 | static char *getpacket(void); |
---|
63 | static void putpacket(char *buffer); |
---|
64 | |
---|
65 | unsigned int registers[NUM_REGS]; |
---|
66 | |
---|
67 | /* BUFMAX defines the maximum number of characters in inbound/outbound buffers */ |
---|
68 | #define BUFMAX 1500 |
---|
69 | |
---|
70 | /* I/O packet buffers */ |
---|
71 | static char remcomInBuffer[BUFMAX]; |
---|
72 | static char remcomOutBuffer[BUFMAX]; |
---|
73 | |
---|
74 | /* |
---|
75 | * Set by debugger to indicate that when handling memory faults (bus errors), the |
---|
76 | * handler should set the mem_err flag and skip over the faulting instruction |
---|
77 | */ |
---|
78 | static volatile int may_fault; |
---|
79 | |
---|
80 | /* |
---|
81 | * Set by bus error exception handler, this indicates to caller of mem2hex, |
---|
82 | * hex2mem or bin2mem that there has been an error. |
---|
83 | */ |
---|
84 | static volatile int mem_err; |
---|
85 | |
---|
86 | /* Indicates if we're single stepping */ |
---|
87 | static unsigned char stepping; |
---|
88 | static char branch_step; |
---|
89 | |
---|
90 | /* Saved instructions */ |
---|
91 | static unsigned int *seq_ptr; |
---|
92 | static unsigned int seq_insn; |
---|
93 | static unsigned int *branch_ptr; |
---|
94 | static unsigned int branch_insn; |
---|
95 | |
---|
96 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
97 | static char do_threads; |
---|
98 | int current_thread_registers[NUM_REGS]; |
---|
99 | #endif |
---|
100 | |
---|
101 | /* this mapping is used to copy the registers from a debug interrupt frame |
---|
102 | * see gdb_handle_break() */ |
---|
103 | static unsigned char reg_map[] = { |
---|
104 | 0, LM32_INT_REG_R1, LM32_INT_REG_R2, LM32_INT_REG_R3, LM32_INT_REG_R4, |
---|
105 | LM32_INT_REG_R5, LM32_INT_REG_R6, LM32_INT_REG_R7, LM32_INT_REG_R8, |
---|
106 | LM32_INT_REG_R9, LM32_INT_REG_R10, LM32_INT_REG_R11, LM32_INT_REG_R12, |
---|
107 | LM32_INT_REG_R13, LM32_INT_REG_R14, LM32_INT_REG_R15, LM32_INT_REG_R16, |
---|
108 | LM32_INT_REG_R17, LM32_INT_REG_R18, LM32_INT_REG_R19, LM32_INT_REG_R20, |
---|
109 | LM32_INT_REG_R21, LM32_INT_REG_R22, LM32_INT_REG_R23, LM32_INT_REG_R24, |
---|
110 | LM32_INT_REG_R25, LM32_INT_REG_GP, LM32_INT_REG_FP, LM32_INT_REG_SP, |
---|
111 | LM32_INT_REG_RA, LM32_INT_REG_EA, LM32_INT_REG_BA, LM32_INT_REG_PC, |
---|
112 | LM32_INT_REG_EID, LM32_INT_REG_EBA, LM32_INT_REG_DEBA, LM32_INT_REG_IE |
---|
113 | }; |
---|
114 | |
---|
115 | /* |
---|
116 | * Conversion helper functions |
---|
117 | */ |
---|
118 | |
---|
119 | /* For integer to ASCII conversion */ |
---|
120 | #define highhex(x) gdb_hexchars [(x >> 4) & 0xf] |
---|
121 | #define lowhex(x) gdb_hexchars [x & 0xf] |
---|
122 | const char gdb_hexchars[]="0123456789abcdef"; |
---|
123 | |
---|
124 | /* Convert ch from a hex digit to an int */ |
---|
125 | static int hex( |
---|
126 | unsigned char ch |
---|
127 | ) |
---|
128 | { |
---|
129 | if (ch >= 'a' && ch <= 'f') |
---|
130 | return ch-'a'+10; |
---|
131 | if (ch >= '0' && ch <= '9') |
---|
132 | return ch-'0'; |
---|
133 | if (ch >= 'A' && ch <= 'F') |
---|
134 | return ch-'A'+10; |
---|
135 | return -1; |
---|
136 | } |
---|
137 | |
---|
138 | /* |
---|
139 | * Convert the memory pointed to by mem into hex, placing result in buf. |
---|
140 | * Return a pointer to the last char put in buf ('\0'), in case of mem fault, |
---|
141 | * return NULL. |
---|
142 | */ |
---|
143 | static char *mem2hex( |
---|
144 | unsigned char *mem, |
---|
145 | char *buf, int count |
---|
146 | ) |
---|
147 | { |
---|
148 | unsigned char ch; |
---|
149 | |
---|
150 | while (count-- > 0) |
---|
151 | { |
---|
152 | ch = *mem++; |
---|
153 | if (mem_err) |
---|
154 | return NULL; |
---|
155 | *buf++ = highhex(ch); |
---|
156 | *buf++ = lowhex(ch); |
---|
157 | } |
---|
158 | |
---|
159 | *buf = '\0'; |
---|
160 | return buf; |
---|
161 | } |
---|
162 | |
---|
163 | /* |
---|
164 | * Convert the hex array pointed to by buf into binary to be placed in mem. |
---|
165 | * Return a pointer to the character AFTER the last byte written. |
---|
166 | */ |
---|
167 | static unsigned char *hex2mem( |
---|
168 | char *buf, |
---|
169 | unsigned char *mem, |
---|
170 | int count |
---|
171 | ) |
---|
172 | { |
---|
173 | int i; |
---|
174 | unsigned char ch; |
---|
175 | |
---|
176 | for (i = 0; i < count; i++) |
---|
177 | { |
---|
178 | /* Convert hex data to 8-bit value */ |
---|
179 | ch = hex(*buf++) << 4; |
---|
180 | ch |= hex(*buf++); |
---|
181 | /* Attempt to write data to memory */ |
---|
182 | *mem++ = ch; |
---|
183 | /* Return NULL if write caused an exception */ |
---|
184 | if (mem_err) |
---|
185 | return NULL; |
---|
186 | } |
---|
187 | return mem; |
---|
188 | } |
---|
189 | |
---|
190 | /* |
---|
191 | * Copy the binary data pointed to by buf to mem and return a pointer to the |
---|
192 | * character AFTER the last byte written $, # and 0x7d are escaped with 0x7d. |
---|
193 | */ |
---|
194 | static unsigned char *bin2mem( |
---|
195 | char *buf, |
---|
196 | unsigned char *mem, |
---|
197 | int count |
---|
198 | ) |
---|
199 | { |
---|
200 | int i; |
---|
201 | unsigned char c; |
---|
202 | |
---|
203 | for (i = 0; i < count; i++) |
---|
204 | { |
---|
205 | /* Convert binary data to unsigned byte */ |
---|
206 | c = *buf++; |
---|
207 | if (c == 0x7d) |
---|
208 | c = *buf++ ^ 0x20; |
---|
209 | /* Attempt to write value to memory */ |
---|
210 | *mem++ = c; |
---|
211 | /* Return NULL if write caused an exception */ |
---|
212 | if (mem_err) |
---|
213 | return NULL; |
---|
214 | } |
---|
215 | |
---|
216 | return mem; |
---|
217 | } |
---|
218 | |
---|
219 | /* |
---|
220 | * While we find nice hex chars, build an int. |
---|
221 | * Return number of chars processed. |
---|
222 | */ |
---|
223 | static int hex2int( |
---|
224 | char **ptr, |
---|
225 | int *int_value |
---|
226 | ) |
---|
227 | { |
---|
228 | int num_chars = 0; |
---|
229 | int hex_value; |
---|
230 | |
---|
231 | *int_value = 0; |
---|
232 | |
---|
233 | while(**ptr) |
---|
234 | { |
---|
235 | hex_value = hex(**ptr); |
---|
236 | if (hex_value < 0) |
---|
237 | break; |
---|
238 | |
---|
239 | *int_value = (*int_value << 4) | hex_value; |
---|
240 | num_chars ++; |
---|
241 | |
---|
242 | (*ptr)++; |
---|
243 | } |
---|
244 | |
---|
245 | return (num_chars); |
---|
246 | } |
---|
247 | |
---|
248 | /* Convert the exception identifier to a signal number. */ |
---|
249 | static int compute_signal( |
---|
250 | int eid |
---|
251 | ) |
---|
252 | { |
---|
253 | switch (eid) |
---|
254 | { |
---|
255 | case LM32_EXCEPTION_RESET: |
---|
256 | return 0; |
---|
257 | case LM32_EXCEPTION_INTERRUPT: |
---|
258 | return SIGINT; |
---|
259 | case LM32_EXCEPTION_DATA_BREAKPOINT: |
---|
260 | case LM32_EXCEPTION_INST_BREAKPOINT: |
---|
261 | return SIGTRAP; |
---|
262 | case LM32_EXCEPTION_INST_BUS_ERROR: |
---|
263 | case LM32_EXCEPTION_DATA_BUS_ERROR: |
---|
264 | return SIGSEGV; |
---|
265 | case LM32_EXCEPTION_DIVIDE_BY_ZERO: |
---|
266 | return SIGFPE; |
---|
267 | } |
---|
268 | |
---|
269 | return SIGHUP; /* default for things we don't know about */ |
---|
270 | } |
---|
271 | |
---|
272 | /* Scan for the sequence $<data>#<checksum> */ |
---|
273 | static char *getpacket(void) |
---|
274 | { |
---|
275 | char *buffer = &remcomInBuffer[0]; |
---|
276 | unsigned char checksum; |
---|
277 | unsigned char xmitcsum; |
---|
278 | int count; |
---|
279 | char ch; |
---|
280 | |
---|
281 | while (1) |
---|
282 | { |
---|
283 | /* wait around for the start character, ignore all other characters */ |
---|
284 | while ((ch = gdb_get_debug_char()) != '$'); |
---|
285 | |
---|
286 | retry: |
---|
287 | checksum = 0; |
---|
288 | xmitcsum = -1; |
---|
289 | count = 0; |
---|
290 | |
---|
291 | /* now, read until a # or end of buffer is found */ |
---|
292 | while (count < BUFMAX) |
---|
293 | { |
---|
294 | ch = gdb_get_debug_char(); |
---|
295 | if (ch == '$') |
---|
296 | goto retry; |
---|
297 | if (ch == '#') |
---|
298 | break; |
---|
299 | checksum = checksum + ch; |
---|
300 | buffer[count] = ch; |
---|
301 | count = count + 1; |
---|
302 | } |
---|
303 | buffer[count] = 0; |
---|
304 | |
---|
305 | if (ch == '#') |
---|
306 | { |
---|
307 | ch = gdb_get_debug_char(); |
---|
308 | xmitcsum = hex(ch) << 4; |
---|
309 | ch = gdb_get_debug_char(); |
---|
310 | xmitcsum += hex(ch); |
---|
311 | |
---|
312 | if (checksum != xmitcsum) |
---|
313 | { |
---|
314 | /* failed checksum */ |
---|
315 | gdb_put_debug_char('-'); |
---|
316 | } |
---|
317 | else |
---|
318 | { |
---|
319 | /* successful transfer */ |
---|
320 | gdb_put_debug_char('+'); |
---|
321 | |
---|
322 | /* if a sequence char is present, reply the sequence ID */ |
---|
323 | if (buffer[2] == ':') |
---|
324 | { |
---|
325 | gdb_put_debug_char(buffer[0]); |
---|
326 | gdb_put_debug_char(buffer[1]); |
---|
327 | |
---|
328 | return &buffer[3]; |
---|
329 | } |
---|
330 | |
---|
331 | return &buffer[0]; |
---|
332 | } |
---|
333 | } |
---|
334 | } |
---|
335 | } |
---|
336 | |
---|
337 | /* Send the packet in buffer. */ |
---|
338 | static void putpacket( |
---|
339 | char *buffer |
---|
340 | ) |
---|
341 | { |
---|
342 | unsigned char checksum; |
---|
343 | int count; |
---|
344 | unsigned char ch; |
---|
345 | |
---|
346 | #ifdef GDB_RLE_ENABLED |
---|
347 | int run_length; |
---|
348 | int run_idx; |
---|
349 | char run_length_char; |
---|
350 | #endif |
---|
351 | |
---|
352 | /* $<packet info>#<checksum>. */ |
---|
353 | do { |
---|
354 | gdb_put_debug_char('$'); |
---|
355 | checksum = 0; |
---|
356 | count = 0; |
---|
357 | |
---|
358 | #ifdef GDB_RLE_ENABLED |
---|
359 | while (ch = buffer[count]) |
---|
360 | { |
---|
361 | /* Transmit character */ |
---|
362 | gdb_put_debug_char(ch); |
---|
363 | checksum += ch; |
---|
364 | count += 1; |
---|
365 | |
---|
366 | /* |
---|
367 | * Determine how many consecutive characters there are that are the same |
---|
368 | * as the character we just transmitted |
---|
369 | */ |
---|
370 | run_length = 0; |
---|
371 | run_idx = count; |
---|
372 | while ((buffer[run_idx++] == ch) && (run_length < 97)) |
---|
373 | run_length++; |
---|
374 | /* Encode run length as an ASCII character */ |
---|
375 | run_length_char = (char)(run_length + 29); |
---|
376 | if ( (run_length >= 3) |
---|
377 | && (run_length_char != '$') |
---|
378 | && (run_length_char != '#') |
---|
379 | && (run_length_char != '+') |
---|
380 | && (run_length_char != '-') |
---|
381 | ) |
---|
382 | { |
---|
383 | /* Transmit run-length */ |
---|
384 | gdb_put_debug_char('*'); |
---|
385 | checksum += '*'; |
---|
386 | gdb_put_debug_char(run_length_char); |
---|
387 | checksum += run_length_char; |
---|
388 | count += run_length; |
---|
389 | } |
---|
390 | } |
---|
391 | #else |
---|
392 | while ((ch = buffer[count])) |
---|
393 | { |
---|
394 | gdb_put_debug_char(ch); |
---|
395 | checksum += ch; |
---|
396 | count += 1; |
---|
397 | } |
---|
398 | #endif |
---|
399 | |
---|
400 | gdb_put_debug_char('#'); |
---|
401 | gdb_put_debug_char(highhex(checksum)); |
---|
402 | gdb_put_debug_char(lowhex(checksum)); |
---|
403 | } while (gdb_get_debug_char() != '+'); |
---|
404 | } |
---|
405 | |
---|
406 | static void allow_nested_exception(void) |
---|
407 | { |
---|
408 | mem_err = 0; |
---|
409 | may_fault = 1; |
---|
410 | } |
---|
411 | |
---|
412 | static void disallow_nested_exception(void) |
---|
413 | { |
---|
414 | mem_err = 0; |
---|
415 | may_fault = 0; |
---|
416 | } |
---|
417 | |
---|
418 | /* Flush the instruction cache */ |
---|
419 | static void flush_cache(void) |
---|
420 | { |
---|
421 | /* |
---|
422 | * Executing this does no harm on CPUs without a cache. We flush data cache as |
---|
423 | * well as instruction cache in case the debugger has accessed memory |
---|
424 | * directly. |
---|
425 | */ |
---|
426 | __asm__ __volatile__ ("wcsr ICC, r0\n" |
---|
427 | "nop\n" |
---|
428 | "nop\n" |
---|
429 | "nop\n" |
---|
430 | "wcsr DCC, r0\n" |
---|
431 | "nop\n" |
---|
432 | "nop\n" |
---|
433 | "nop" |
---|
434 | ); |
---|
435 | } |
---|
436 | |
---|
437 | /* Set a h/w breakpoint at the given address */ |
---|
438 | static int set_hw_breakpoint( |
---|
439 | int address, |
---|
440 | int length |
---|
441 | ) |
---|
442 | { |
---|
443 | int bp; |
---|
444 | |
---|
445 | /* Find a free break point register and then set it */ |
---|
446 | __asm__ ("rcsr %0, BP0" : "=r" (bp)); |
---|
447 | if ((bp & 0x01) == 0) |
---|
448 | { |
---|
449 | __asm__ ("wcsr BP0, %0" : : "r" (address | 1)); |
---|
450 | return 1; |
---|
451 | } |
---|
452 | __asm__ ("rcsr %0, BP1" : "=r" (bp)); |
---|
453 | if ((bp & 0x01) == 0) |
---|
454 | { |
---|
455 | __asm__ ("wcsr BP1, %0" : : "r" (address | 1)); |
---|
456 | return 1; |
---|
457 | } |
---|
458 | __asm__ ("rcsr %0, BP2" : "=r" (bp)); |
---|
459 | if ((bp & 0x01) == 0) |
---|
460 | { |
---|
461 | __asm__ ("wcsr BP2, %0" : : "r" (address | 1)); |
---|
462 | return 1; |
---|
463 | } |
---|
464 | __asm__ ("rcsr %0, BP3" : "=r" (bp)); |
---|
465 | if ((bp & 0x01) == 0) |
---|
466 | { |
---|
467 | __asm__ ("wcsr BP3, %0" : : "r" (address | 1)); |
---|
468 | return 1; |
---|
469 | } |
---|
470 | |
---|
471 | /* No free breakpoint registers */ |
---|
472 | return -1; |
---|
473 | } |
---|
474 | |
---|
475 | /* Remove a h/w breakpoint which should be set at the given address */ |
---|
476 | static int disable_hw_breakpoint( |
---|
477 | int address, |
---|
478 | int length |
---|
479 | ) |
---|
480 | { |
---|
481 | int bp; |
---|
482 | |
---|
483 | /* Try to find matching breakpoint register */ |
---|
484 | __asm__ ("rcsr %0, BP0" : "=r" (bp)); |
---|
485 | if ((bp & 0xfffffffc) == (address & 0xfffffffc)) |
---|
486 | { |
---|
487 | __asm__ ("wcsr BP0, %0" : : "r" (0)); |
---|
488 | return 1; |
---|
489 | } |
---|
490 | __asm__ ("rcsr %0, BP1" : "=r" (bp)); |
---|
491 | if ((bp & 0xfffffffc) == (address & 0xfffffffc)) |
---|
492 | { |
---|
493 | __asm__ ("wcsr BP1, %0" : : "r" (0)); |
---|
494 | return 1; |
---|
495 | } |
---|
496 | __asm__ ("rcsr %0, BP2" : "=r" (bp)); |
---|
497 | if ((bp & 0xfffffffc) == (address & 0xfffffffc)) |
---|
498 | { |
---|
499 | __asm__ ("wcsr BP2, %0" : : "r" (0)); |
---|
500 | return 1; |
---|
501 | } |
---|
502 | __asm__ ("rcsr %0, BP3" : "=r" (bp)); |
---|
503 | if ((bp & 0xfffffffc) == (address & 0xfffffffc)) |
---|
504 | { |
---|
505 | __asm__ ("wcsr BP3, %0" : : "r" (0)); |
---|
506 | return 1; |
---|
507 | } |
---|
508 | |
---|
509 | /* Breakpoint not found */ |
---|
510 | return -1; |
---|
511 | } |
---|
512 | |
---|
513 | /* |
---|
514 | * This support function prepares and sends the message containing the |
---|
515 | * basic information about this exception. |
---|
516 | */ |
---|
517 | static void gdb_stub_report_exception_info( |
---|
518 | int thread |
---|
519 | ) |
---|
520 | { |
---|
521 | char *ptr; |
---|
522 | int sigval; |
---|
523 | |
---|
524 | /* Convert exception ID to a signal number */ |
---|
525 | sigval = compute_signal(registers[LM32_REG_EID]); |
---|
526 | |
---|
527 | /* Set pointer to start of output buffer */ |
---|
528 | ptr = remcomOutBuffer; |
---|
529 | |
---|
530 | *ptr++ = 'T'; |
---|
531 | *ptr++ = highhex(sigval); |
---|
532 | *ptr++ = lowhex(sigval); |
---|
533 | |
---|
534 | *ptr++ = highhex(LM32_REG_PC); |
---|
535 | *ptr++ = lowhex(LM32_REG_PC); |
---|
536 | *ptr++ = ':'; |
---|
537 | ptr = mem2hex((unsigned char *)&(registers[LM32_REG_PC]), ptr, 4); |
---|
538 | *ptr++ = ';'; |
---|
539 | |
---|
540 | *ptr++ = highhex(LM32_REG_SP); |
---|
541 | *ptr++ = lowhex(LM32_REG_SP); |
---|
542 | *ptr++ = ':'; |
---|
543 | ptr = mem2hex((unsigned char *)&(registers[LM32_REG_SP]), ptr, 4); |
---|
544 | *ptr++ = ';'; |
---|
545 | |
---|
546 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
547 | if (do_threads) |
---|
548 | { |
---|
549 | *ptr++ = 't'; |
---|
550 | *ptr++ = 'h'; |
---|
551 | *ptr++ = 'r'; |
---|
552 | *ptr++ = 'e'; |
---|
553 | *ptr++ = 'a'; |
---|
554 | *ptr++ = 'd'; |
---|
555 | *ptr++ = ':'; |
---|
556 | ptr = thread2vhstr(ptr, thread); |
---|
557 | *ptr++ = ';'; |
---|
558 | } |
---|
559 | #endif |
---|
560 | |
---|
561 | *ptr++ = '\0'; |
---|
562 | } |
---|
563 | |
---|
564 | /* |
---|
565 | * This function does all command procesing for interfacing to gdb. The error |
---|
566 | * codes we return are errno numbers. |
---|
567 | */ |
---|
568 | void handle_exception(void) |
---|
569 | { |
---|
570 | int addr; |
---|
571 | int length; |
---|
572 | char *ptr; |
---|
573 | int err; |
---|
574 | int reg; |
---|
575 | unsigned insn; |
---|
576 | unsigned opcode; |
---|
577 | unsigned branch_target = 0; |
---|
578 | int current_thread; |
---|
579 | int thread; |
---|
580 | void *regptr; |
---|
581 | int host_has_detached = 0; |
---|
582 | int binary; |
---|
583 | |
---|
584 | thread = 0; |
---|
585 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
586 | if (do_threads) |
---|
587 | thread = rtems_gdb_stub_get_current_thread(); |
---|
588 | #endif |
---|
589 | current_thread = thread; |
---|
590 | |
---|
591 | /* |
---|
592 | * Check for bus error caused by this code (rather than the program being |
---|
593 | * debugged) |
---|
594 | */ |
---|
595 | if (may_fault && (registers[LM32_REG_EID] == LM32_EXCEPTION_DATA_BUS_ERROR)) |
---|
596 | { |
---|
597 | /* Indicate that a fault occured */ |
---|
598 | mem_err = 1; |
---|
599 | /* Skip over faulting instruction */ |
---|
600 | registers[LM32_REG_PC] += 4; |
---|
601 | /* Resume execution */ |
---|
602 | return; |
---|
603 | } |
---|
604 | |
---|
605 | if (stepping) |
---|
606 | { |
---|
607 | /* Remove breakpoints */ |
---|
608 | *seq_ptr = seq_insn; |
---|
609 | if (branch_step) |
---|
610 | *branch_ptr = branch_insn; |
---|
611 | stepping = 0; |
---|
612 | } |
---|
613 | |
---|
614 | /* Reply to host that an exception has occured with some basic info */ |
---|
615 | gdb_stub_report_exception_info(thread); |
---|
616 | putpacket(remcomOutBuffer); |
---|
617 | |
---|
618 | while (!host_has_detached) |
---|
619 | { |
---|
620 | remcomOutBuffer[0] = '\0'; |
---|
621 | ptr = getpacket(); |
---|
622 | binary = 0; |
---|
623 | |
---|
624 | switch (*ptr++) |
---|
625 | { |
---|
626 | /* Return last signal */ |
---|
627 | case '?': |
---|
628 | gdb_stub_report_exception_info(thread); |
---|
629 | break; |
---|
630 | |
---|
631 | /* Detach - exit from debugger */ |
---|
632 | case 'D': |
---|
633 | strcpy(remcomOutBuffer, "OK"); |
---|
634 | host_has_detached = 1; |
---|
635 | break; |
---|
636 | |
---|
637 | /* Return the value of the CPU registers */ |
---|
638 | case 'g': |
---|
639 | regptr = registers; |
---|
640 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
641 | if (do_threads && current_thread != thread ) |
---|
642 | regptr = ¤t_thread_registers; |
---|
643 | #endif |
---|
644 | ptr = mem2hex((unsigned char*)regptr, remcomOutBuffer, NUM_REGS * 4); |
---|
645 | break; |
---|
646 | |
---|
647 | /* Set the value of the CPU registers */ |
---|
648 | case 'G': |
---|
649 | regptr = registers; |
---|
650 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
651 | if (do_threads && current_thread != thread ) |
---|
652 | regptr = ¤t_thread_registers; |
---|
653 | #endif |
---|
654 | hex2mem(ptr, (unsigned char*)regptr, NUM_REGS * 4); |
---|
655 | strcpy(remcomOutBuffer, "OK"); |
---|
656 | break; |
---|
657 | |
---|
658 | /* Return the value of the specified register */ |
---|
659 | case 'p': |
---|
660 | if (hex2int(&ptr, ®)) |
---|
661 | { |
---|
662 | ptr = remcomOutBuffer; |
---|
663 | ptr = mem2hex((unsigned char *)®isters[reg], ptr, 4); |
---|
664 | } else |
---|
665 | strcpy(remcomOutBuffer, "E22"); |
---|
666 | break; |
---|
667 | |
---|
668 | /* Set the specified register to the given value */ |
---|
669 | case 'P': |
---|
670 | if (hex2int(&ptr, ®) |
---|
671 | && *ptr++ == '=') |
---|
672 | { |
---|
673 | hex2mem(ptr, (unsigned char *)®isters[reg], 4); |
---|
674 | strcpy(remcomOutBuffer, "OK"); |
---|
675 | } |
---|
676 | else |
---|
677 | strcpy(remcomOutBuffer, "E22"); |
---|
678 | break; |
---|
679 | |
---|
680 | /* Read memory */ |
---|
681 | case 'm': |
---|
682 | /* Try to read %x,%x. */ |
---|
683 | if (hex2int(&ptr, &addr) |
---|
684 | && *ptr++ == ',' |
---|
685 | && hex2int(&ptr, &length) |
---|
686 | && length < (sizeof(remcomOutBuffer)/2)) |
---|
687 | { |
---|
688 | allow_nested_exception(); |
---|
689 | if (NULL == mem2hex((unsigned char *)addr, remcomOutBuffer, length)) |
---|
690 | strcpy(remcomOutBuffer, "E14"); |
---|
691 | disallow_nested_exception(); |
---|
692 | } |
---|
693 | else |
---|
694 | strcpy(remcomOutBuffer,"E22"); |
---|
695 | break; |
---|
696 | |
---|
697 | /* Write memory */ |
---|
698 | case 'X': |
---|
699 | binary = 1; |
---|
700 | case 'M': |
---|
701 | /* Try to read '%x,%x:'. */ |
---|
702 | if (hex2int(&ptr, &addr) |
---|
703 | && *ptr++ == ',' |
---|
704 | && hex2int(&ptr, &length) |
---|
705 | && *ptr++ == ':') |
---|
706 | { |
---|
707 | allow_nested_exception(); |
---|
708 | if (binary) |
---|
709 | err = (int)bin2mem(ptr, (unsigned char *)addr, length); |
---|
710 | else |
---|
711 | err = (int)hex2mem(ptr, (unsigned char *)addr, length); |
---|
712 | if (err) |
---|
713 | strcpy(remcomOutBuffer, "OK"); |
---|
714 | else |
---|
715 | strcpy(remcomOutBuffer, "E14"); |
---|
716 | disallow_nested_exception(); |
---|
717 | } |
---|
718 | else |
---|
719 | strcpy(remcomOutBuffer, "E22"); |
---|
720 | break; |
---|
721 | |
---|
722 | /* Continue */ |
---|
723 | case 'c': |
---|
724 | /* try to read optional parameter, pc unchanged if no parm */ |
---|
725 | if (hex2int(&ptr, &addr)) |
---|
726 | registers[LM32_REG_PC] = addr; |
---|
727 | flush_cache(); |
---|
728 | return; |
---|
729 | |
---|
730 | /* Step */ |
---|
731 | case 's': |
---|
732 | /* try to read optional parameter, pc unchanged if no parm */ |
---|
733 | if (hex2int(&ptr, &addr)) |
---|
734 | registers[LM32_REG_PC] = addr; |
---|
735 | stepping = 1; |
---|
736 | /* Is instruction a branch? */ |
---|
737 | insn = *(unsigned int*)registers[LM32_REG_PC]; |
---|
738 | opcode = insn & 0xfc000000; |
---|
739 | if ( (opcode == 0xe0000000) |
---|
740 | || (opcode == 0xf8000000) |
---|
741 | ) |
---|
742 | { |
---|
743 | branch_step = 1; |
---|
744 | branch_target = registers[LM32_REG_PC] |
---|
745 | + (((signed)insn << 6) >> 4); |
---|
746 | } |
---|
747 | else if ( (opcode == 0x44000000) |
---|
748 | || (opcode == 0x48000000) |
---|
749 | || (opcode == 0x4c000000) |
---|
750 | || (opcode == 0x50000000) |
---|
751 | || (opcode == 0x54000000) |
---|
752 | || (opcode == 0x5c000000) |
---|
753 | ) |
---|
754 | { |
---|
755 | branch_step = 1; |
---|
756 | branch_target = registers[LM32_REG_PC] + |
---|
757 | + (((signed)insn << 16) >> 14); |
---|
758 | } |
---|
759 | else if ( (opcode == 0xd8000000) |
---|
760 | || (opcode == 0xc0000000) |
---|
761 | ) |
---|
762 | { |
---|
763 | branch_step = 1; |
---|
764 | branch_target = registers[(insn >> 21) & 0x1f]; |
---|
765 | } |
---|
766 | else |
---|
767 | branch_step = 0; |
---|
768 | |
---|
769 | /* Set breakpoint after instruction we're stepping */ |
---|
770 | seq_ptr = (unsigned int *)registers[LM32_REG_PC]; |
---|
771 | seq_ptr++; |
---|
772 | seq_insn = *seq_ptr; |
---|
773 | *seq_ptr = LM32_BREAK; |
---|
774 | |
---|
775 | /* Make sure one insn doesn't get replaced twice */ |
---|
776 | if (seq_ptr == (unsigned int*)branch_target) |
---|
777 | branch_step = 0; |
---|
778 | |
---|
779 | if (branch_step) |
---|
780 | { |
---|
781 | /* Set breakpoint on branch target */ |
---|
782 | branch_ptr = (unsigned int*)branch_target; |
---|
783 | branch_insn = *branch_ptr; |
---|
784 | *branch_ptr = LM32_BREAK; |
---|
785 | } |
---|
786 | flush_cache(); |
---|
787 | return; |
---|
788 | |
---|
789 | case 'Z': |
---|
790 | switch (*ptr++) |
---|
791 | { |
---|
792 | /* Insert h/w breakpoint */ |
---|
793 | case '1': |
---|
794 | if (*ptr++ == ',' |
---|
795 | && hex2int(&ptr, &addr) |
---|
796 | && *ptr++ == ',' |
---|
797 | && hex2int(&ptr, &length)) |
---|
798 | { |
---|
799 | err = set_hw_breakpoint(addr, length); |
---|
800 | if (err > 0) |
---|
801 | strcpy(remcomOutBuffer, "OK"); |
---|
802 | else if (err < 0) |
---|
803 | strcpy(remcomOutBuffer, "E28"); |
---|
804 | } |
---|
805 | else |
---|
806 | strcpy(remcomOutBuffer, "E22"); |
---|
807 | break; |
---|
808 | } |
---|
809 | break; |
---|
810 | |
---|
811 | case 'z': |
---|
812 | switch (*ptr++) |
---|
813 | { |
---|
814 | /* Remove h/w breakpoint */ |
---|
815 | case '1': |
---|
816 | if (*ptr++ == ',' |
---|
817 | && hex2int(&ptr, &addr) |
---|
818 | && *ptr++ == ',' |
---|
819 | && hex2int(&ptr, &length)) |
---|
820 | { |
---|
821 | err = disable_hw_breakpoint(addr, length); |
---|
822 | if (err > 0) |
---|
823 | strcpy(remcomOutBuffer, "OK"); |
---|
824 | else if (err < 0) |
---|
825 | strcpy(remcomOutBuffer, "E28"); |
---|
826 | } |
---|
827 | else |
---|
828 | strcpy(remcomOutBuffer, "E22"); |
---|
829 | break; |
---|
830 | } |
---|
831 | break; |
---|
832 | |
---|
833 | /* Query */ |
---|
834 | case 'q': |
---|
835 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
836 | rtems_gdb_process_query( |
---|
837 | remcomInBuffer, |
---|
838 | remcomOutBuffer, |
---|
839 | do_threads, |
---|
840 | thread ); |
---|
841 | #endif |
---|
842 | break; |
---|
843 | |
---|
844 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
845 | /* Thread alive */ |
---|
846 | case 'T': |
---|
847 | { |
---|
848 | int testThread; |
---|
849 | |
---|
850 | if (vhstr2thread(&remcomInBuffer[1], &testThread) == NULL) |
---|
851 | { |
---|
852 | strcpy(remcomOutBuffer, "E01"); |
---|
853 | break; |
---|
854 | } |
---|
855 | |
---|
856 | if (rtems_gdb_index_to_stub_id(testThread) == NULL) |
---|
857 | strcpy(remcomOutBuffer, "E02"); |
---|
858 | else |
---|
859 | strcpy(remcomOutBuffer, "OK"); |
---|
860 | } |
---|
861 | break; |
---|
862 | #endif |
---|
863 | |
---|
864 | /* Set thread */ |
---|
865 | case 'H': |
---|
866 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
867 | if (remcomInBuffer[1] != 'g') |
---|
868 | break; |
---|
869 | |
---|
870 | if (!do_threads) |
---|
871 | break; |
---|
872 | |
---|
873 | { |
---|
874 | int tmp, ret; |
---|
875 | |
---|
876 | /* Set new generic thread */ |
---|
877 | if (vhstr2thread(&remcomInBuffer[2], &tmp) == NULL) |
---|
878 | { |
---|
879 | strcpy(remcomOutBuffer, "E01"); |
---|
880 | break; |
---|
881 | } |
---|
882 | |
---|
883 | /* 0 means `thread' */ |
---|
884 | if (tmp == 0) |
---|
885 | tmp = thread; |
---|
886 | |
---|
887 | if (tmp == current_thread) |
---|
888 | { |
---|
889 | /* No changes */ |
---|
890 | strcpy(remcomOutBuffer, "OK"); |
---|
891 | break; |
---|
892 | } |
---|
893 | |
---|
894 | /* Save current thread registers if necessary */ |
---|
895 | if (current_thread != thread) |
---|
896 | { |
---|
897 | ret = rtems_gdb_stub_set_thread_regs( |
---|
898 | current_thread, (unsigned int *) ¤t_thread_registers); |
---|
899 | } |
---|
900 | |
---|
901 | /* Read new registers if necessary */ |
---|
902 | if (tmp != thread) |
---|
903 | { |
---|
904 | ret = rtems_gdb_stub_get_thread_regs( |
---|
905 | tmp, (unsigned int *) ¤t_thread_registers); |
---|
906 | |
---|
907 | if (!ret) |
---|
908 | { |
---|
909 | /* Thread does not exist */ |
---|
910 | strcpy(remcomOutBuffer, "E02"); |
---|
911 | break; |
---|
912 | } |
---|
913 | } |
---|
914 | |
---|
915 | current_thread = tmp; |
---|
916 | strcpy(remcomOutBuffer, "OK"); |
---|
917 | } |
---|
918 | #endif |
---|
919 | break; |
---|
920 | |
---|
921 | #ifdef GDB_RESTART_ENABLED |
---|
922 | /* Reset */ |
---|
923 | case 'r': |
---|
924 | case 'R': |
---|
925 | /* We reset by branching to the reset exception handler. */ |
---|
926 | registers[LM32_REG_PC] = 0; |
---|
927 | return; |
---|
928 | #endif |
---|
929 | } |
---|
930 | |
---|
931 | /* reply to the request */ |
---|
932 | putpacket(remcomOutBuffer); |
---|
933 | } |
---|
934 | } |
---|
935 | |
---|
936 | void gdb_handle_break(rtems_vector_number vector, CPU_Interrupt_frame *frame) |
---|
937 | { |
---|
938 | int i; |
---|
939 | unsigned int *int_regs = (unsigned int*)frame; |
---|
940 | |
---|
941 | /* copy extended frame to registers */ |
---|
942 | registers[LM32_REG_R0] = 0; |
---|
943 | for (i = 1; i < NUM_REGS; i++) |
---|
944 | { |
---|
945 | registers[i] = int_regs[reg_map[i]]; |
---|
946 | } |
---|
947 | |
---|
948 | /* now call the real handler */ |
---|
949 | handle_exception(); |
---|
950 | gdb_ack_irq(); |
---|
951 | |
---|
952 | /* copy registers back to extended frame */ |
---|
953 | for (i = 1; i < NUM_REGS; i++) |
---|
954 | { |
---|
955 | int_regs[reg_map[i]] = registers[i]; |
---|
956 | } |
---|
957 | } |
---|
958 | |
---|
959 | void lm32_gdb_stub_install(int enable_threads) |
---|
960 | { |
---|
961 | unsigned int dc; |
---|
962 | |
---|
963 | /* set DEBA and remap all exception */ |
---|
964 | __asm__("wcsr DEBA, %0" : : "r" (&_deba)); |
---|
965 | __asm__("rcsr %0, DC" : "=r" (dc)); |
---|
966 | dc |= 0x2; |
---|
967 | __asm__("wcsr DC, %0" : : "r" (dc)); |
---|
968 | |
---|
969 | #if defined(GDB_STUB_ENABLE_THREAD_SUPPORT) |
---|
970 | if( enable_threads ) |
---|
971 | do_threads = 1; |
---|
972 | else |
---|
973 | do_threads = 0; |
---|
974 | #endif |
---|
975 | |
---|
976 | gdb_console_init(); |
---|
977 | } |
---|
978 | |
---|