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