1 /****************************************************************************
3 THIS SOFTWARE IS NOT COPYRIGHTED
5 HP offers the following for use in the public domain. HP makes no
6 warranty with regard to the software or its performance and the
7 user accepts the software "AS IS" with all faults.
9 HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
10 TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
11 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13 ****************************************************************************/
15 /****************************************************************************
16 * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
18 * Module name: remcom.c $
20 * Date: 91/03/09 12:29:49 $
21 * Contributor: Lake Stevens Instrument Division$
23 * Description: low level support for gdb debugger. $
25 * Considerations: only works on target hardware $
27 * Written by: Glenn Engel $
28 * ModuleState: Experimental $
32 * Modified for FreeBSD by Stu Grossman.
34 * To enable debugger support, two things need to happen. One, a
35 * call to set_debug_traps() is necessary in order to allow any breakpoints
36 * or error conditions to be properly intercepted and reported to gdb.
37 * Two, a breakpoint needs to be generated to begin communication. This
38 * is most easily accomplished by a call to breakpoint(). Breakpoint()
39 * simulates a breakpoint by executing a trap #1.
41 * The external function exceptionHandler() is
42 * used to attach a specific handler to a specific 386 vector number.
43 * It should use the same privilege level it runs at. It should
44 * install it as an interrupt gate so that interrupts are masked
45 * while the handler runs.
46 * Also, need to assign exceptionHook and oldExceptionHook.
48 * Because gdb will sometimes write to the stack area to execute function
49 * calls, this program cannot rely on using the supervisor stack so it
50 * uses its own stack area reserved in the int array remcomStack.
54 * The following gdb commands are supported:
56 * command function Return value
58 * g return the value of the CPU registers hex data or ENN
59 * G set the value of the CPU registers OK or ENN
61 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
62 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
64 * c Resume at current address SNN ( signal NN)
65 * cAA..AA Continue at address AA..AA SNN
67 * s Step one instruction SNN
68 * sAA..AA Step one instruction from AA..AA SNN
72 * ? What was the last sigval ? SNN (signal NN)
76 * All commands and responses are sent with a packet which includes a
77 * checksum. A packet consists of
79 * $<packet info>#<checksum>.
82 * <packet info> :: <characters representing the command or response>
83 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
85 * When a packet is received, it is first acknowledged with either '+' or '-'.
86 * '+' indicates a successful transfer. '-' indicates a failed transfer.
91 * $m0,10#2a +$00010203040506070809101112131415#42
93 ****************************************************************************/
94 /* $FreeBSD: src/sys/i386/i386/i386-gdbstub.c,v 1.13.2.1 2000/08/03 00:54:41 peter Exp $ */
95 /* $DragonFly: src/sys/cpu/i386/misc/i386-gdbstub.c,v 1.7 2007/05/07 05:21:37 dillon Exp $ */
99 #include <sys/param.h>
100 #include <sys/reboot.h>
101 #include <sys/systm.h>
102 #include <sys/cons.h>
108 void gdb_handle_exception (db_regs_t
*, int, int);
110 /************************************************************************/
112 extern jmp_buf db_jmpbuf
;
114 /************************************************************************/
115 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
116 /* at least NUMREGBYTES*2 are needed for register packets */
119 /* Create private copies of common functions used by the stub. This prevents
120 nasty interactions between app code and the stub (for instance if user steps
121 into strlen, etc..) */
123 #define strlen gdb_strlen
124 #define strcpy gdb_strcpy
127 strlen (const char *s
)
131 while (*s1
++ != '\000');
137 strcpy (char *dst
, const char *src
)
141 while ((*dst
++ = *src
++) != '\000');
147 putDebugChar (int c
) /* write a single character */
151 gdb_tab
->cn_putc(gdb_tab
->cn_gdbprivate
, c
);
156 getDebugChar (void) /* read and return a single char */
160 return gdb_tab
->cn_getc(gdb_tab
->cn_gdbprivate
);
163 static const char hexchars
[]="0123456789abcdef";
168 if ((ch
>= 'a') && (ch
<= 'f')) return (ch
-'a'+10);
169 if ((ch
>= '0') && (ch
<= '9')) return (ch
-'0');
170 if ((ch
>= 'A') && (ch
<= 'F')) return (ch
-'A'+10);
174 /* scan for the sequence $<data>#<checksum> */
176 getpacket (char *buffer
)
178 unsigned char checksum
;
179 unsigned char xmitcsum
;
186 /* wait around for the start character, ignore all other characters */
188 while ((ch
= (getDebugChar () & 0x7f)) != '$');
195 /* now, read until a # or end of buffer is found */
197 while (count
< BUFMAX
)
199 ch
= getDebugChar () & 0x7f;
202 checksum
= checksum
+ ch
;
210 xmitcsum
= hex (getDebugChar () & 0x7f) << 4;
211 xmitcsum
+= hex (getDebugChar () & 0x7f);
213 if (checksum
!= xmitcsum
)
214 putDebugChar ('-'); /* failed checksum */
217 putDebugChar ('+'); /* successful transfer */
218 /* if a sequence char is present, reply the sequence ID */
219 if (buffer
[2] == ':')
221 putDebugChar (buffer
[0]);
222 putDebugChar (buffer
[1]);
224 /* remove sequence chars from buffer */
226 count
= strlen (buffer
);
227 for (i
=3; i
<= count
; i
++)
228 buffer
[i
-3] = buffer
[i
];
233 while (checksum
!= xmitcsum
);
236 /* send the packet in buffer. */
239 putpacket (char *buffer
)
241 unsigned char checksum
;
245 /* $<packet info>#<checksum>. */
249 * This is a non-standard hack to allow use of the serial console for
250 * operation as well as debugging. Simply turn on 'remotechat' in gdb.
252 * This extension is not part of the Cygnus protocol, is kinda gross,
253 * but gets the job done.
255 #ifdef GDB_REMOTE_CHAT
265 while ((ch
=buffer
[count
]) != 0)
273 putDebugChar (hexchars
[checksum
>> 4]);
274 putDebugChar (hexchars
[checksum
& 0xf]);
276 while ((getDebugChar () & 0x7f) != '+');
279 static char remcomInBuffer
[BUFMAX
];
280 static char remcomOutBuffer
[BUFMAX
];
283 get_char (vm_offset_t addr
)
287 if (setjmp (db_jmpbuf
))
290 db_read_bytes (addr
, 1, &data
);
296 set_char (vm_offset_t addr
, int val
)
300 if (setjmp (db_jmpbuf
))
305 db_write_bytes (addr
, 1, &data
);
309 /* convert the memory pointed to by mem into hex, placing result in buf */
310 /* return a pointer to the last char put in buf (null) */
313 mem2hex (vm_offset_t mem
, char *buf
, int count
)
318 for (i
=0;i
<count
;i
++) {
319 ch
= get_char (mem
++);
322 *buf
++ = hexchars
[ch
>> 4];
323 *buf
++ = hexchars
[ch
% 16];
329 /* convert the hex array pointed to by buf into binary to be placed in mem */
330 /* return a pointer to the character AFTER the last byte written */
332 hex2mem (char *buf
, vm_offset_t mem
, int count
)
338 for (i
=0;i
<count
;i
++) {
339 ch
= hex(*buf
++) << 4;
340 ch
= ch
+ hex(*buf
++);
341 rv
= set_char (mem
++, ch
);
348 /* this function takes the 386 exception vector and attempts to
349 translate this number into a unix compatible signal value */
351 computeSignal (int exceptionVector
)
354 switch (exceptionVector
& ~T_USER
)
356 case 0: sigval
= 8; break; /* divide by zero */
357 case 1: sigval
= 5; break; /* debug exception */
358 case 3: sigval
= 5; break; /* breakpoint */
359 case 4: sigval
= 16; break; /* into instruction (overflow) */
360 case 5: sigval
= 16; break; /* bound instruction */
361 case 6: sigval
= 4; break; /* Invalid opcode */
362 case 7: sigval
= 8; break; /* coprocessor not available */
363 case 8: sigval
= 7; break; /* double fault */
364 case 9: sigval
= 11; break; /* coprocessor segment overrun */
365 case 10: sigval
= 5; break; /* Invalid TSS (also single-step) */
366 case 11: sigval
= 11; break; /* Segment not present */
367 case 12: sigval
= 11; break; /* stack exception */
368 case 13: sigval
= 11; break; /* general protection */
369 case 14: sigval
= 11; break; /* page fault */
370 case 16: sigval
= 7; break; /* coprocessor error */
372 sigval
= 7; /* "software generated"*/
378 * While we find nice hex chars, build an int.
379 * Return number of chars processed.
383 hexToInt(char **ptr
, int *intValue
)
392 hexValue
= hex(**ptr
);
395 *intValue
= (*intValue
<<4) | hexValue
;
407 #define NUMREGBYTES (sizeof registers)
414 * This function does all command procesing for interfacing to gdb.
417 gdb_handle_exception (db_regs_t
*raw_regs
, int type
, int code
)
438 struct i386regs registers
;
440 registers
.eax
= raw_regs
->tf_eax
;
441 registers
.ebx
= raw_regs
->tf_ebx
;
442 registers
.ecx
= raw_regs
->tf_ecx
;
443 registers
.edx
= raw_regs
->tf_edx
;
445 registers
.esp
= raw_regs
->tf_esp
;
446 registers
.ebp
= raw_regs
->tf_ebp
;
447 registers
.esi
= raw_regs
->tf_esi
;
448 registers
.edi
= raw_regs
->tf_edi
;
450 registers
.eip
= raw_regs
->tf_eip
;
451 registers
.eflags
= raw_regs
->tf_eflags
;
453 registers
.cs
= raw_regs
->tf_cs
;
454 registers
.ss
= raw_regs
->tf_ss
;
455 registers
.ds
= raw_regs
->tf_ds
;
456 registers
.es
= raw_regs
->tf_es
;
458 /* reply to host that an exception has occurred */
459 sigval
= computeSignal (type
);
460 ptr
= remcomOutBuffer
;
463 *ptr
++ = hexchars
[sigval
>> 4];
464 *ptr
++ = hexchars
[sigval
& 0xf];
466 *ptr
++ = hexchars
[PC
>> 4];
467 *ptr
++ = hexchars
[PC
& 0xf];
469 ptr
= mem2hex ((vm_offset_t
)®isters
.eip
, ptr
, 4);
472 *ptr
++ = hexchars
[FP
>> 4];
473 *ptr
++ = hexchars
[FP
& 0xf];
475 ptr
= mem2hex ((vm_offset_t
)®isters
.ebp
, ptr
, 4);
478 *ptr
++ = hexchars
[SP
>> 4];
479 *ptr
++ = hexchars
[SP
& 0xf];
481 ptr
= mem2hex ((vm_offset_t
)®isters
.esp
, ptr
, 4);
486 putpacket (remcomOutBuffer
);
490 remcomOutBuffer
[0] = 0;
492 getpacket (remcomInBuffer
);
493 switch (remcomInBuffer
[0])
496 remcomOutBuffer
[0] = 'S';
497 remcomOutBuffer
[1] = hexchars
[sigval
>> 4];
498 remcomOutBuffer
[2] = hexchars
[sigval
% 16];
499 remcomOutBuffer
[3] = 0;
502 case 'D': /* detach; say OK and turn off gdb */
503 putpacket(remcomOutBuffer
);
504 boothowto
&= ~RB_GDB
;
507 case 'g': /* return the value of the CPU registers */
508 mem2hex ((vm_offset_t
)®isters
, remcomOutBuffer
, NUMREGBYTES
);
511 case 'G': /* set the value of the CPU registers - return OK */
512 hex2mem (&remcomInBuffer
[1], (vm_offset_t
)®isters
, NUMREGBYTES
);
513 strcpy (remcomOutBuffer
, "OK");
516 case 'P': /* Set the value of one register */
520 ptr
= &remcomInBuffer
[1];
522 if (hexToInt (&ptr
, ®no
)
526 hex2mem (ptr
, (vm_offset_t
)®isters
+ regno
* 4, 4);
527 strcpy(remcomOutBuffer
,"OK");
530 strcpy (remcomOutBuffer
, "P01");
533 case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
534 /* Try to read %x,%x. */
536 ptr
= &remcomInBuffer
[1];
538 if (hexToInt (&ptr
, &addr
)
540 && hexToInt (&ptr
, &length
))
542 if (mem2hex((vm_offset_t
) addr
, remcomOutBuffer
, length
) == NULL
)
543 strcpy (remcomOutBuffer
, "E03");
547 strcpy (remcomOutBuffer
, "E01");
550 case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
552 /* Try to read '%x,%x:'. */
554 ptr
= &remcomInBuffer
[1];
556 if (hexToInt(&ptr
,&addr
)
558 && hexToInt(&ptr
, &length
)
561 if (hex2mem(ptr
, (vm_offset_t
) addr
, length
) == NULL
)
562 strcpy (remcomOutBuffer
, "E03");
564 strcpy (remcomOutBuffer
, "OK");
567 strcpy (remcomOutBuffer
, "E02");
570 /* cAA..AA Continue at address AA..AA(optional) */
571 /* sAA..AA Step one instruction from AA..AA(optional) */
574 /* try to read optional parameter, pc unchanged if no parm */
576 ptr
= &remcomInBuffer
[1];
577 if (hexToInt(&ptr
,&addr
))
578 registers
.eip
= addr
;
581 /* set the trace bit if we're stepping */
582 if (remcomInBuffer
[0] == 's')
583 registers
.eflags
|= PSL_T
;
585 registers
.eflags
&= ~PSL_T
;
587 raw_regs
->tf_eax
= registers
.eax
;
588 raw_regs
->tf_ebx
= registers
.ebx
;
589 raw_regs
->tf_ecx
= registers
.ecx
;
590 raw_regs
->tf_edx
= registers
.edx
;
592 raw_regs
->tf_esp
= registers
.esp
;
593 raw_regs
->tf_ebp
= registers
.ebp
;
594 raw_regs
->tf_esi
= registers
.esi
;
595 raw_regs
->tf_edi
= registers
.edi
;
597 raw_regs
->tf_eip
= registers
.eip
;
598 raw_regs
->tf_eflags
= registers
.eflags
;
600 raw_regs
->tf_cs
= registers
.cs
;
601 raw_regs
->tf_ss
= registers
.ss
;
602 raw_regs
->tf_ds
= registers
.ds
;
603 raw_regs
->tf_es
= registers
.es
;
608 /* reply to the request */
609 putpacket (remcomOutBuffer
);