1 /****************************************************************************
2
3 THIS SOFTWARE IS NOT COPYRIGHTED
4
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.
8
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.
12
13 ****************************************************************************/
14
15 #include <sys/cdefs.h>
16 __FBSDID("$FreeBSD: releng/5.2/sys/amd64/amd64/amd64-gdbstub.c 123180 2003-12-06 23:19:47Z peter $");
17
18 /****************************************************************************
19 * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
20 *
21 * Module name: remcom.c $
22 * Revision: 1.34 $
23 * Date: 91/03/09 12:29:49 $
24 * Contributor: Lake Stevens Instrument Division$
25 *
26 * Description: low level support for gdb debugger. $
27 *
28 * Considerations: only works on target hardware $
29 *
30 * Written by: Glenn Engel $
31 * ModuleState: Experimental $
32 *
33 * NOTES: See Below $
34 *
35 * Modified for FreeBSD by Stu Grossman.
36 *
37 * To enable debugger support, two things need to happen. One, a
38 * call to set_debug_traps() is necessary in order to allow any breakpoints
39 * or error conditions to be properly intercepted and reported to gdb.
40 * Two, a breakpoint needs to be generated to begin communication. This
41 * is most easily accomplished by a call to breakpoint(). Breakpoint()
42 * simulates a breakpoint by executing a trap #1.
43 *
44 * The external function exceptionHandler() is
45 * used to attach a specific handler to a specific 386 vector number.
46 * It should use the same privilege level it runs at. It should
47 * install it as an interrupt gate so that interrupts are masked
48 * while the handler runs.
49 * Also, need to assign exceptionHook and oldExceptionHook.
50 *
51 * Because gdb will sometimes write to the stack area to execute function
52 * calls, this program cannot rely on using the supervisor stack so it
53 * uses its own stack area reserved in the int array remcomStack.
54 *
55 *************
56 *
57 * The following gdb commands are supported:
58 *
59 * command function Return value
60 *
61 * g return the value of the CPU registers hex data or ENN
62 * G set the value of the CPU registers OK or ENN
63 *
64 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
65 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
66 *
67 * c Resume at current address SNN ( signal NN)
68 * cAA..AA Continue at address AA..AA SNN
69 *
70 * s Step one instruction SNN
71 * sAA..AA Step one instruction from AA..AA SNN
72 *
73 * k kill
74 *
75 * ? What was the last sigval ? SNN (signal NN)
76 *
77 * D detach OK
78 *
79 * All commands and responses are sent with a packet which includes a
80 * checksum. A packet consists of
81 *
82 * $<packet info>#<checksum>.
83 *
84 * where
85 * <packet info> :: <characters representing the command or response>
86 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
87 *
88 * When a packet is received, it is first acknowledged with either '+' or '-'.
89 * '+' indicates a successful transfer. '-' indicates a failed transfer.
90 *
91 * Example:
92 *
93 * Host: Reply:
94 * $m0,10#2a +$00010203040506070809101112131415#42
95 *
96 ****************************************************************************/
97
98 #include <sys/param.h>
99 #include <sys/reboot.h>
100 #include <sys/systm.h>
101 #include <sys/cons.h>
102
103 #include <ddb/ddb.h>
104
105 #include <machine/setjmp.h>
106
107 #include "opt_ddb.h"
108
109 int gdb_handle_exception (db_regs_t *, int, int);
110
111 /************************************************************************/
112
113 extern jmp_buf db_jmpbuf;
114
115 /************************************************************************/
116 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
117 /* at least NUMREGBYTES*2 are needed for register packets */
118 #define BUFMAX 400
119
120 /* Create private copies of common functions used by the stub. This prevents
121 nasty interactions between app code and the stub (for instance if user steps
122 into strlen, etc..) */
123
124 static int
125 gdb_strlen (const char *s)
126 {
127 const char *s1 = s;
128
129 while (*s1++ != '\000');
130
131 return s1 - s;
132 }
133
134 static char *
135 gdb_strcpy (char *dst, const char *src)
136 {
137 char *retval = dst;
138
139 while ((*dst++ = *src++) != '\000');
140
141 return retval;
142 }
143
144 static int
145 putDebugChar (int c) /* write a single character */
146 {
147 if (gdb_arg == NULL)
148 return 0;
149 (*gdb_putc)(gdb_arg, c);
150 return 1;
151 }
152
153 static int
154 getDebugChar (void) /* read and return a single char */
155 {
156 if (gdb_arg == NULL)
157 return -1;
158 return (*gdb_getc)(gdb_arg);
159 }
160
161 static const char hexchars[]="0123456789abcdef";
162
163 static int
164 hex(char ch)
165 {
166 if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
167 if ((ch >= '') && (ch <= '9')) return (ch-'');
168 if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
169 return (-1);
170 }
171
172 /* scan for the sequence $<data>#<checksum> */
173 static void
174 getpacket (char *buffer)
175 {
176 unsigned char checksum;
177 unsigned char xmitcsum;
178 int i;
179 int count;
180 unsigned char ch;
181
182 do
183 {
184 /* wait around for the start character, ignore all other characters */
185
186 while ((ch = (getDebugChar () & 0x7f)) != '$');
187
188 checksum = 0;
189 xmitcsum = -1;
190
191 count = 0;
192
193 /* now, read until a # or end of buffer is found */
194
195 while (count < BUFMAX)
196 {
197 ch = getDebugChar () & 0x7f;
198 if (ch == '#')
199 break;
200 checksum = checksum + ch;
201 buffer[count] = ch;
202 count = count + 1;
203 }
204 buffer[count] = 0;
205
206 if (ch == '#')
207 {
208 xmitcsum = hex (getDebugChar () & 0x7f) << 4;
209 xmitcsum += hex (getDebugChar () & 0x7f);
210
211 if (checksum != xmitcsum)
212 putDebugChar ('-'); /* failed checksum */
213 else
214 {
215 putDebugChar ('+'); /* successful transfer */
216 /* if a sequence char is present, reply the sequence ID */
217 if (buffer[2] == ':')
218 {
219 putDebugChar (buffer[0]);
220 putDebugChar (buffer[1]);
221
222 /* remove sequence chars from buffer */
223
224 count = gdb_strlen (buffer);
225 for (i=3; i <= count; i++)
226 buffer[i-3] = buffer[i];
227 }
228 }
229 }
230 }
231 while (checksum != xmitcsum);
232 }
233
234 /* send the packet in buffer. */
235
236 static void
237 putpacket (char *buffer)
238 {
239 unsigned char checksum;
240 int count;
241 unsigned char ch;
242
243 /* $<packet info>#<checksum>. */
244 do
245 {
246 /*
247 * This is a non-standard hack to allow use of the serial console for
248 * operation as well as debugging. Simply turn on 'remotechat' in gdb.
249 *
250 * This extension is not part of the Cygnus protocol, is kinda gross,
251 * but gets the job done.
252 */
253 #ifdef GDB_REMOTE_CHAT
254 putDebugChar ('|');
255 putDebugChar ('|');
256 putDebugChar ('|');
257 putDebugChar ('|');
258 #endif
259 putDebugChar ('$');
260 checksum = 0;
261 count = 0;
262
263 while ((ch=buffer[count]) != 0)
264 {
265 putDebugChar (ch);
266 checksum += ch;
267 count += 1;
268 }
269
270 putDebugChar ('#');
271 putDebugChar (hexchars[checksum >> 4]);
272 putDebugChar (hexchars[checksum & 0xf]);
273 }
274 while ((getDebugChar () & 0x7f) != '+');
275 }
276
277 static char remcomInBuffer[BUFMAX];
278 static char remcomOutBuffer[BUFMAX];
279
280 static int
281 get_char (vm_offset_t addr)
282 {
283 char data;
284
285 if (setjmp (db_jmpbuf))
286 return -1;
287
288 db_read_bytes (addr, 1, &data);
289
290 return data & 0xff;
291 }
292
293 static int
294 set_char (vm_offset_t addr, int val)
295 {
296 char data;
297
298 if (setjmp (db_jmpbuf))
299 return -1;
300
301 data = val;
302
303 db_write_bytes (addr, 1, &data);
304 return 0;
305 }
306
307 /* convert the memory pointed to by mem into hex, placing result in buf */
308 /* return a pointer to the last char put in buf (null) */
309
310 static char *
311 mem2hex (vm_offset_t mem, char *buf, int count)
312 {
313 int i;
314 int ch;
315
316 for (i=0;i<count;i++) {
317 ch = get_char (mem++);
318 if (ch == -1)
319 return NULL;
320 *buf++ = hexchars[ch >> 4];
321 *buf++ = hexchars[ch % 16];
322 }
323 *buf = 0;
324 return(buf);
325 }
326
327 /* convert the hex array pointed to by buf into binary to be placed in mem */
328 /* return a pointer to the character AFTER the last byte written */
329 static char *
330 hex2mem (char *buf, vm_offset_t mem, int count)
331 {
332 int i;
333 int ch;
334 int rv;
335
336 for (i=0;i<count;i++) {
337 ch = hex(*buf++) << 4;
338 ch = ch + hex(*buf++);
339 rv = set_char (mem++, ch);
340 if (rv == -1)
341 return NULL;
342 }
343 return(buf);
344 }
345
346 /* this function takes the 386 exception vector and attempts to
347 translate this number into a unix compatible signal value */
348 static int
349 computeSignal (int exceptionVector)
350 {
351 int sigval;
352 switch (exceptionVector & ~T_USER)
353 {
354 case 0: sigval = 8; break; /* divide by zero */
355 case 1: sigval = 5; break; /* debug exception */
356 case 3: sigval = 5; break; /* breakpoint */
357 case 4: sigval = 16; break; /* into instruction (overflow) */
358 case 5: sigval = 16; break; /* bound instruction */
359 case 6: sigval = 4; break; /* Invalid opcode */
360 case 7: sigval = 8; break; /* coprocessor not available */
361 case 8: sigval = 7; break; /* double fault */
362 case 9: sigval = 11; break; /* coprocessor segment overrun */
363 case 10: sigval = 5; break; /* Invalid TSS (also single-step) */
364 case 11: sigval = 11; break; /* Segment not present */
365 case 12: sigval = 11; break; /* stack exception */
366 case 13: sigval = 11; break; /* general protection */
367 case 14: sigval = 11; break; /* page fault */
368 case 16: sigval = 7; break; /* coprocessor error */
369 default:
370 sigval = 7; /* "software generated"*/
371 }
372 return (sigval);
373 }
374
375 /*
376 * While we find nice hex chars, build an int.
377 * Return number of chars processed.
378 */
379
380 static int
381 hexToInt(char **ptr, int *intValue)
382 {
383 int numChars = 0;
384 int hexValue;
385
386 *intValue = 0;
387
388 while (**ptr)
389 {
390 hexValue = hex(**ptr);
391 if (hexValue >=0)
392 {
393 *intValue = (*intValue <<4) | hexValue;
394 numChars ++;
395 }
396 else
397 break;
398
399 (*ptr)++;
400 }
401
402 return (numChars);
403 }
404
405 #define NUMREGBYTES (sizeof registers)
406 #define PC 8
407 #define SP 4
408 #define FP 5
409 #define NUM_REGS 14
410
411 /*
412 * This function does all command procesing for interfacing to gdb.
413 */
414 int
415 gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
416 {
417 int sigval;
418 int addr, length;
419 char * ptr;
420 struct i386regs {
421 unsigned int rax;
422 unsigned int rcx;
423 unsigned int rdx;
424 unsigned int rbx;
425 unsigned int rsp;
426 unsigned int rbp;
427 unsigned int rsi;
428 unsigned int rdi;
429 unsigned int rip;
430 unsigned int rflags;
431 unsigned int cs;
432 unsigned int ss;
433 unsigned int ds;
434 unsigned int es;
435 };
436 struct i386regs registers;
437
438 registers.rax = raw_regs->tf_rax;
439 registers.rbx = raw_regs->tf_rbx;
440 registers.rcx = raw_regs->tf_rcx;
441 registers.rdx = raw_regs->tf_rdx;
442
443 registers.rsp = raw_regs->tf_rsp;
444 registers.rbp = raw_regs->tf_rbp;
445 registers.rsi = raw_regs->tf_rsi;
446 registers.rdi = raw_regs->tf_rdi;
447
448 registers.rip = raw_regs->tf_rip;
449 registers.rflags = raw_regs->tf_rflags;
450
451 registers.cs = raw_regs->tf_cs;
452 registers.ss = raw_regs->tf_ss;
453 registers.ds = 0; /* XXX rds() */
454 registers.es = 0; /* XXX res() */
455
456 /* reply to host that an exception has occurred */
457 sigval = computeSignal (type);
458 ptr = remcomOutBuffer;
459
460 *ptr++ = 'T';
461 *ptr++ = hexchars[sigval >> 4];
462 *ptr++ = hexchars[sigval & 0xf];
463
464 *ptr++ = hexchars[PC >> 4];
465 *ptr++ = hexchars[PC & 0xf];
466 *ptr++ = ':';
467 ptr = mem2hex ((vm_offset_t)®isters.rip, ptr, 4);
468 *ptr++ = ';';
469
470 *ptr++ = hexchars[FP >> 4];
471 *ptr++ = hexchars[FP & 0xf];
472 *ptr++ = ':';
473 ptr = mem2hex ((vm_offset_t)®isters.rbp, ptr, 4);
474 *ptr++ = ';';
475
476 *ptr++ = hexchars[SP >> 4];
477 *ptr++ = hexchars[SP & 0xf];
478 *ptr++ = ':';
479 ptr = mem2hex ((vm_offset_t)®isters.rsp, ptr, 4);
480 *ptr++ = ';';
481
482 *ptr++ = 0;
483
484 putpacket (remcomOutBuffer);
485
486 while (1)
487 {
488 if (gdb_arg == NULL)
489 return 1; /* somebody has removed the gdb device */
490 remcomOutBuffer[0] = 0;
491
492 getpacket (remcomInBuffer);
493 switch (remcomInBuffer[0])
494 {
495 case '?':
496 remcomOutBuffer[0] = 'S';
497 remcomOutBuffer[1] = hexchars[sigval >> 4];
498 remcomOutBuffer[2] = hexchars[sigval % 16];
499 remcomOutBuffer[3] = 0;
500 break;
501
502 case 'D': /* detach; say OK and turn off gdb */
503 putpacket(remcomOutBuffer);
504 boothowto &= ~RB_GDB;
505 return 0;
506
507 case 'g': /* return the value of the CPU registers */
508 mem2hex ((vm_offset_t)®isters, remcomOutBuffer, NUMREGBYTES);
509 break;
510
511 case 'G': /* set the value of the CPU registers - return OK */
512 hex2mem (&remcomInBuffer[1], (vm_offset_t)®isters, NUMREGBYTES);
513 gdb_strcpy (remcomOutBuffer, "OK");
514 break;
515
516 case 'P': /* Set the value of one register */
517 {
518 int regno;
519
520 ptr = &remcomInBuffer[1];
521
522 if (hexToInt (&ptr, ®no)
523 && *ptr++ == '='
524 && regno < NUM_REGS)
525 {
526 hex2mem (ptr, (vm_offset_t)®isters + regno * 4, 4);
527 gdb_strcpy(remcomOutBuffer,"OK");
528 }
529 else
530 gdb_strcpy (remcomOutBuffer, "P01");
531 break;
532 }
533 case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
534 /* Try to read %x,%x. */
535
536 ptr = &remcomInBuffer[1];
537
538 if (hexToInt (&ptr, &addr)
539 && *(ptr++) == ','
540 && hexToInt (&ptr, &length))
541 {
542 if (mem2hex((vm_offset_t) addr, remcomOutBuffer, length) == NULL)
543 gdb_strcpy (remcomOutBuffer, "E03");
544 break;
545 }
546 else
547 gdb_strcpy (remcomOutBuffer, "E01");
548 break;
549
550 case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
551
552 /* Try to read '%x,%x:'. */
553
554 ptr = &remcomInBuffer[1];
555
556 if (hexToInt(&ptr,&addr)
557 && *(ptr++) == ','
558 && hexToInt(&ptr, &length)
559 && *(ptr++) == ':')
560 {
561 if (hex2mem(ptr, (vm_offset_t) addr, length) == NULL)
562 gdb_strcpy (remcomOutBuffer, "E03");
563 else
564 gdb_strcpy (remcomOutBuffer, "OK");
565 }
566 else
567 gdb_strcpy (remcomOutBuffer, "E02");
568 break;
569
570 /* cAA..AA Continue at address AA..AA(optional) */
571 /* sAA..AA Step one instruction from AA..AA(optional) */
572 case 'c' :
573 case 's' :
574 /* try to read optional parameter, pc unchanged if no parm */
575
576 ptr = &remcomInBuffer[1];
577 if (hexToInt(&ptr,&addr))
578 registers.rip = addr;
579
580
581 /* set the trace bit if we're stepping */
582 if (remcomInBuffer[0] == 's')
583 registers.rflags |= PSL_T;
584 else
585 registers.rflags &= ~PSL_T;
586
587 raw_regs->tf_rax = registers.rax;
588 raw_regs->tf_rbx = registers.rbx;
589 raw_regs->tf_rcx = registers.rcx;
590 raw_regs->tf_rdx = registers.rdx;
591
592 raw_regs->tf_rsp = registers.rsp;
593 raw_regs->tf_rbp = registers.rbp;
594 raw_regs->tf_rsi = registers.rsi;
595 raw_regs->tf_rdi = registers.rdi;
596
597 raw_regs->tf_rip = registers.rip;
598 raw_regs->tf_rflags = registers.rflags;
599
600 raw_regs->tf_cs = registers.cs;
601 raw_regs->tf_ss = registers.ss;
602 #if 0
603 raw_regs->tf_ds = registers.ds;
604 raw_regs->tf_es = registers.es;
605 #endif
606 return 0;
607
608 } /* switch */
609
610 /* reply to the request */
611 putpacket (remcomOutBuffer);
612 }
613 return 0;
614 }
Cache object: e5dcf259377da29d67cc413851ca8417
|