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 /****************************************************************************
16 * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
17 *
18 * Module name: remcom.c $
19 * Revision: 1.34 $
20 * Date: 91/03/09 12:29:49 $
21 * Contributor: Lake Stevens Instrument Division$
22 *
23 * Description: low level support for gdb debugger. $
24 *
25 * Considerations: only works on target hardware $
26 *
27 * Written by: Glenn Engel $
28 * ModuleState: Experimental $
29 *
30 * NOTES: See Below $
31 *
32 * Modified for FreeBSD by Stu Grossman.
33 *
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.
40 *
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.
47 *
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.
51 *
52 *************
53 *
54 * The following gdb commands are supported:
55 *
56 * command function Return value
57 *
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
60 *
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
63 *
64 * c Resume at current address SNN ( signal NN)
65 * cAA..AA Continue at address AA..AA SNN
66 *
67 * s Step one instruction SNN
68 * sAA..AA Step one instruction from AA..AA SNN
69 *
70 * k kill
71 *
72 * ? What was the last sigval ? SNN (signal NN)
73 *
74 * D detach OK
75 *
76 * All commands and responses are sent with a packet which includes a
77 * checksum. A packet consists of
78 *
79 * $<packet info>#<checksum>.
80 *
81 * where
82 * <packet info> :: <characters representing the command or response>
83 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
84 *
85 * When a packet is received, it is first acknowledged with either '+' or '-'.
86 * '+' indicates a successful transfer. '-' indicates a failed transfer.
87 *
88 * Example:
89 *
90 * Host: Reply:
91 * $m0,10#2a +$00010203040506070809101112131415#42
92 *
93 ****************************************************************************/
94
95 /* $FreeBSD: releng/5.0/sys/i386/i386/i386-gdbstub.c 89990 2002-01-30 18:51:24Z bde $ */
96
97 #include <sys/param.h>
98 #include <sys/reboot.h>
99 #include <sys/systm.h>
100 #include <sys/cons.h>
101
102 #include <ddb/ddb.h>
103
104 #include <machine/setjmp.h>
105
106 #include "opt_ddb.h"
107
108 int gdb_handle_exception (db_regs_t *, int, int);
109
110 /************************************************************************/
111
112 extern jmp_buf db_jmpbuf;
113
114 /************************************************************************/
115 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
116 /* at least NUMREGBYTES*2 are needed for register packets */
117 #define BUFMAX 400
118
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..) */
122
123 #define strlen gdb_strlen
124 #define strcpy gdb_strcpy
125
126 static int
127 strlen (const char *s)
128 {
129 const char *s1 = s;
130
131 while (*s1++ != '\000');
132
133 return s1 - s;
134 }
135
136 static char *
137 strcpy (char *dst, const char *src)
138 {
139 char *retval = dst;
140
141 while ((*dst++ = *src++) != '\000');
142
143 return retval;
144 }
145
146 static int
147 putDebugChar (int c) /* write a single character */
148 {
149 if (gdbdev == NODEV)
150 return 0;
151 (*gdb_putc)(gdbdev, c);
152 return 1;
153 }
154
155 static int
156 getDebugChar (void) /* read and return a single char */
157 {
158 if (gdbdev == NODEV)
159 return -1;
160 return (*gdb_getc)(gdbdev);
161 }
162
163 static const char hexchars[]="0123456789abcdef";
164
165 static int
166 hex(char ch)
167 {
168 if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
169 if ((ch >= '') && (ch <= '9')) return (ch-'');
170 if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
171 return (-1);
172 }
173
174 /* scan for the sequence $<data>#<checksum> */
175 static void
176 getpacket (char *buffer)
177 {
178 unsigned char checksum;
179 unsigned char xmitcsum;
180 int i;
181 int count;
182 unsigned char ch;
183
184 do
185 {
186 /* wait around for the start character, ignore all other characters */
187
188 while ((ch = (getDebugChar () & 0x7f)) != '$');
189
190 checksum = 0;
191 xmitcsum = -1;
192
193 count = 0;
194
195 /* now, read until a # or end of buffer is found */
196
197 while (count < BUFMAX)
198 {
199 ch = getDebugChar () & 0x7f;
200 if (ch == '#')
201 break;
202 checksum = checksum + ch;
203 buffer[count] = ch;
204 count = count + 1;
205 }
206 buffer[count] = 0;
207
208 if (ch == '#')
209 {
210 xmitcsum = hex (getDebugChar () & 0x7f) << 4;
211 xmitcsum += hex (getDebugChar () & 0x7f);
212
213 if (checksum != xmitcsum)
214 putDebugChar ('-'); /* failed checksum */
215 else
216 {
217 putDebugChar ('+'); /* successful transfer */
218 /* if a sequence char is present, reply the sequence ID */
219 if (buffer[2] == ':')
220 {
221 putDebugChar (buffer[0]);
222 putDebugChar (buffer[1]);
223
224 /* remove sequence chars from buffer */
225
226 count = strlen (buffer);
227 for (i=3; i <= count; i++)
228 buffer[i-3] = buffer[i];
229 }
230 }
231 }
232 }
233 while (checksum != xmitcsum);
234 }
235
236 /* send the packet in buffer. */
237
238 static void
239 putpacket (char *buffer)
240 {
241 unsigned char checksum;
242 int count;
243 unsigned char ch;
244
245 /* $<packet info>#<checksum>. */
246 do
247 {
248 /*
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.
251 *
252 * This extension is not part of the Cygnus protocol, is kinda gross,
253 * but gets the job done.
254 */
255 #ifdef GDB_REMOTE_CHAT
256 putDebugChar ('|');
257 putDebugChar ('|');
258 putDebugChar ('|');
259 putDebugChar ('|');
260 #endif
261 putDebugChar ('$');
262 checksum = 0;
263 count = 0;
264
265 while ((ch=buffer[count]) != 0)
266 {
267 putDebugChar (ch);
268 checksum += ch;
269 count += 1;
270 }
271
272 putDebugChar ('#');
273 putDebugChar (hexchars[checksum >> 4]);
274 putDebugChar (hexchars[checksum & 0xf]);
275 }
276 while ((getDebugChar () & 0x7f) != '+');
277 }
278
279 static char remcomInBuffer[BUFMAX];
280 static char remcomOutBuffer[BUFMAX];
281
282 static int
283 get_char (vm_offset_t addr)
284 {
285 char data;
286
287 if (setjmp (db_jmpbuf))
288 return -1;
289
290 db_read_bytes (addr, 1, &data);
291
292 return data & 0xff;
293 }
294
295 static int
296 set_char (vm_offset_t addr, int val)
297 {
298 char data;
299
300 if (setjmp (db_jmpbuf))
301 return -1;
302
303 data = val;
304
305 db_write_bytes (addr, 1, &data);
306 return 0;
307 }
308
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) */
311
312 static char *
313 mem2hex (vm_offset_t mem, char *buf, int count)
314 {
315 int i;
316 int ch;
317
318 for (i=0;i<count;i++) {
319 ch = get_char (mem++);
320 if (ch == -1)
321 return NULL;
322 *buf++ = hexchars[ch >> 4];
323 *buf++ = hexchars[ch % 16];
324 }
325 *buf = 0;
326 return(buf);
327 }
328
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 */
331 static char *
332 hex2mem (char *buf, vm_offset_t mem, int count)
333 {
334 int i;
335 int ch;
336 int rv;
337
338 for (i=0;i<count;i++) {
339 ch = hex(*buf++) << 4;
340 ch = ch + hex(*buf++);
341 rv = set_char (mem++, ch);
342 if (rv == -1)
343 return NULL;
344 }
345 return(buf);
346 }
347
348 /* this function takes the 386 exception vector and attempts to
349 translate this number into a unix compatible signal value */
350 static int
351 computeSignal (int exceptionVector)
352 {
353 int sigval;
354 switch (exceptionVector & ~T_USER)
355 {
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 */
371 default:
372 sigval = 7; /* "software generated"*/
373 }
374 return (sigval);
375 }
376
377 /*
378 * While we find nice hex chars, build an int.
379 * Return number of chars processed.
380 */
381
382 static int
383 hexToInt(char **ptr, int *intValue)
384 {
385 int numChars = 0;
386 int hexValue;
387
388 *intValue = 0;
389
390 while (**ptr)
391 {
392 hexValue = hex(**ptr);
393 if (hexValue >=0)
394 {
395 *intValue = (*intValue <<4) | hexValue;
396 numChars ++;
397 }
398 else
399 break;
400
401 (*ptr)++;
402 }
403
404 return (numChars);
405 }
406
407 #define NUMREGBYTES (sizeof registers)
408 #define PC 8
409 #define SP 4
410 #define FP 5
411 #define NUM_REGS 14
412
413 /*
414 * This function does all command procesing for interfacing to gdb.
415 */
416 int
417 gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
418 {
419 int sigval;
420 int addr, length;
421 char * ptr;
422 struct i386regs {
423 unsigned int eax;
424 unsigned int ecx;
425 unsigned int edx;
426 unsigned int ebx;
427 unsigned int esp;
428 unsigned int ebp;
429 unsigned int esi;
430 unsigned int edi;
431 unsigned int eip;
432 unsigned int eflags;
433 unsigned int cs;
434 unsigned int ss;
435 unsigned int ds;
436 unsigned int es;
437 };
438 struct i386regs registers;
439
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;
444
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;
449
450 registers.eip = raw_regs->tf_eip;
451 registers.eflags = raw_regs->tf_eflags;
452
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;
457
458 /* reply to host that an exception has occurred */
459 sigval = computeSignal (type);
460 ptr = remcomOutBuffer;
461
462 *ptr++ = 'T';
463 *ptr++ = hexchars[sigval >> 4];
464 *ptr++ = hexchars[sigval & 0xf];
465
466 *ptr++ = hexchars[PC >> 4];
467 *ptr++ = hexchars[PC & 0xf];
468 *ptr++ = ':';
469 ptr = mem2hex ((vm_offset_t)®isters.eip, ptr, 4);
470 *ptr++ = ';';
471
472 *ptr++ = hexchars[FP >> 4];
473 *ptr++ = hexchars[FP & 0xf];
474 *ptr++ = ':';
475 ptr = mem2hex ((vm_offset_t)®isters.ebp, ptr, 4);
476 *ptr++ = ';';
477
478 *ptr++ = hexchars[SP >> 4];
479 *ptr++ = hexchars[SP & 0xf];
480 *ptr++ = ':';
481 ptr = mem2hex ((vm_offset_t)®isters.esp, ptr, 4);
482 *ptr++ = ';';
483
484 *ptr++ = 0;
485
486 putpacket (remcomOutBuffer);
487
488 while (1)
489 {
490 if (gdbdev == NODEV)
491 return 1; /* somebody has removed the gdb device */
492 remcomOutBuffer[0] = 0;
493
494 getpacket (remcomInBuffer);
495 switch (remcomInBuffer[0])
496 {
497 case '?':
498 remcomOutBuffer[0] = 'S';
499 remcomOutBuffer[1] = hexchars[sigval >> 4];
500 remcomOutBuffer[2] = hexchars[sigval % 16];
501 remcomOutBuffer[3] = 0;
502 break;
503
504 case 'D': /* detach; say OK and turn off gdb */
505 putpacket(remcomOutBuffer);
506 boothowto &= ~RB_GDB;
507 return 0;
508
509 case 'g': /* return the value of the CPU registers */
510 mem2hex ((vm_offset_t)®isters, remcomOutBuffer, NUMREGBYTES);
511 break;
512
513 case 'G': /* set the value of the CPU registers - return OK */
514 hex2mem (&remcomInBuffer[1], (vm_offset_t)®isters, NUMREGBYTES);
515 strcpy (remcomOutBuffer, "OK");
516 break;
517
518 case 'P': /* Set the value of one register */
519 {
520 int regno;
521
522 ptr = &remcomInBuffer[1];
523
524 if (hexToInt (&ptr, ®no)
525 && *ptr++ == '='
526 && regno < NUM_REGS)
527 {
528 hex2mem (ptr, (vm_offset_t)®isters + regno * 4, 4);
529 strcpy(remcomOutBuffer,"OK");
530 }
531 else
532 strcpy (remcomOutBuffer, "P01");
533 break;
534 }
535 case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
536 /* Try to read %x,%x. */
537
538 ptr = &remcomInBuffer[1];
539
540 if (hexToInt (&ptr, &addr)
541 && *(ptr++) == ','
542 && hexToInt (&ptr, &length))
543 {
544 if (mem2hex((vm_offset_t) addr, remcomOutBuffer, length) == NULL)
545 strcpy (remcomOutBuffer, "E03");
546 break;
547 }
548 else
549 strcpy (remcomOutBuffer, "E01");
550 break;
551
552 case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
553
554 /* Try to read '%x,%x:'. */
555
556 ptr = &remcomInBuffer[1];
557
558 if (hexToInt(&ptr,&addr)
559 && *(ptr++) == ','
560 && hexToInt(&ptr, &length)
561 && *(ptr++) == ':')
562 {
563 if (hex2mem(ptr, (vm_offset_t) addr, length) == NULL)
564 strcpy (remcomOutBuffer, "E03");
565 else
566 strcpy (remcomOutBuffer, "OK");
567 }
568 else
569 strcpy (remcomOutBuffer, "E02");
570 break;
571
572 /* cAA..AA Continue at address AA..AA(optional) */
573 /* sAA..AA Step one instruction from AA..AA(optional) */
574 case 'c' :
575 case 's' :
576 /* try to read optional parameter, pc unchanged if no parm */
577
578 ptr = &remcomInBuffer[1];
579 if (hexToInt(&ptr,&addr))
580 registers.eip = addr;
581
582
583 /* set the trace bit if we're stepping */
584 if (remcomInBuffer[0] == 's')
585 registers.eflags |= PSL_T;
586 else
587 registers.eflags &= ~PSL_T;
588
589 raw_regs->tf_eax = registers.eax;
590 raw_regs->tf_ebx = registers.ebx;
591 raw_regs->tf_ecx = registers.ecx;
592 raw_regs->tf_edx = registers.edx;
593
594 raw_regs->tf_esp = registers.esp;
595 raw_regs->tf_ebp = registers.ebp;
596 raw_regs->tf_esi = registers.esi;
597 raw_regs->tf_edi = registers.edi;
598
599 raw_regs->tf_eip = registers.eip;
600 raw_regs->tf_eflags = registers.eflags;
601
602 raw_regs->tf_cs = registers.cs;
603 raw_regs->tf_ss = registers.ss;
604 raw_regs->tf_ds = registers.ds;
605 raw_regs->tf_es = registers.es;
606 return 0;
607
608 } /* switch */
609
610 /* reply to the request */
611 putpacket (remcomOutBuffer);
612 }
613 return 0;
614 }
Cache object: 9e6f88b6214338175788fa16236274b8
|