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 it's 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 it's 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 #include <sys/param.h>
96 #include <sys/reboot.h>
97 #include <sys/systm.h>
98
99 #include <machine/cons.h>
100
101 #include <ddb/ddb.h>
102
103 #include <setjmp.h>
104
105 #include "sio.h"
106
107 #if NSIO == 0
108 void
109 gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
110 {
111 }
112 #else
113 /************************************************************************/
114
115 void gdb_handle_exception (db_regs_t *, int, int);
116
117 extern jmp_buf db_jmpbuf;
118
119 /************************************************************************/
120 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
121 /* at least NUMREGBYTES*2 are needed for register packets */
122 #define BUFMAX 400
123
124 /* Create private copies of common functions used by the stub. This prevents
125 nasty interactions between app code and the stub (for instance if user steps
126 into strlen, etc..) */
127 /* XXX this is fairly bogus. strlen() and strcpy() should be reentrant,
128 and are reentrant under FreeBSD. In any case, our versions should not
129 be named the same as the standard versions, so that the address `strlen'
130 is unambiguous... */
131
132 static int
133 strlen (const char *s)
134 {
135 const char *s1 = s;
136
137 while (*s1++ != '\000');
138
139 return s1 - s;
140 }
141
142 static char *
143 strcpy (char *dst, const char *src)
144 {
145 char *retval = dst;
146
147 while ((*dst++ = *src++) != '\000');
148
149 return retval;
150 }
151
152 /* XXX sio always uses its major with minor 0 no matter what we specify. */
153 #define REMOTE_DEV 0
154
155 static int
156 putDebugChar (int c) /* write a single character */
157 {
158 siocnputc (REMOTE_DEV, c);
159 return 1;
160 }
161
162 static int
163 getDebugChar (void) /* read and return a single char */
164 {
165 return siocngetc (REMOTE_DEV);
166 }
167
168 static const char hexchars[]="0123456789abcdef";
169
170 static int
171 hex(char ch)
172 {
173 if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
174 if ((ch >= '') && (ch <= '9')) return (ch-'');
175 if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
176 return (-1);
177 }
178
179 /* scan for the sequence $<data>#<checksum> */
180 static void
181 getpacket (char *buffer)
182 {
183 unsigned char checksum;
184 unsigned char xmitcsum;
185 int i;
186 int count;
187 unsigned char ch;
188
189 do
190 {
191 /* wait around for the start character, ignore all other characters */
192
193 while ((ch = (getDebugChar () & 0x7f)) != '$');
194
195 checksum = 0;
196 xmitcsum = -1;
197
198 count = 0;
199
200 /* now, read until a # or end of buffer is found */
201
202 while (count < BUFMAX)
203 {
204 ch = getDebugChar () & 0x7f;
205 if (ch == '#')
206 break;
207 checksum = checksum + ch;
208 buffer[count] = ch;
209 count = count + 1;
210 }
211 buffer[count] = 0;
212
213 if (ch == '#')
214 {
215 xmitcsum = hex (getDebugChar () & 0x7f) << 4;
216 xmitcsum += hex (getDebugChar () & 0x7f);
217
218 if (checksum != xmitcsum)
219 putDebugChar ('-'); /* failed checksum */
220 else
221 {
222 putDebugChar ('+'); /* successful transfer */
223 /* if a sequence char is present, reply the sequence ID */
224 if (buffer[2] == ':')
225 {
226 putDebugChar (buffer[0]);
227 putDebugChar (buffer[1]);
228
229 /* remove sequence chars from buffer */
230
231 count = strlen (buffer);
232 for (i=3; i <= count; i++)
233 buffer[i-3] = buffer[i];
234 }
235 }
236 }
237 }
238 while (checksum != xmitcsum);
239 }
240
241 /* send the packet in buffer. */
242
243 static void
244 putpacket (char *buffer)
245 {
246 unsigned char checksum;
247 int count;
248 unsigned char ch;
249
250 /* $<packet info>#<checksum>. */
251 do
252 {
253 putDebugChar ('$');
254 checksum = 0;
255 count = 0;
256
257 while (ch=buffer[count])
258 {
259 putDebugChar (ch);
260 checksum += ch;
261 count += 1;
262 }
263
264 putDebugChar ('#');
265 putDebugChar (hexchars[checksum >> 4]);
266 putDebugChar (hexchars[checksum & 0xf]);
267 }
268 while ((getDebugChar () & 0x7f) != '+');
269 }
270
271 static char remcomInBuffer[BUFMAX];
272 static char remcomOutBuffer[BUFMAX];
273
274 static int
275 get_char (vm_offset_t addr)
276 {
277 char data;
278
279 if (setjmp (db_jmpbuf))
280 return -1;
281
282 db_read_bytes (addr, 1, &data);
283
284 return data & 0xff;
285 }
286
287 static int
288 set_char (vm_offset_t addr, int val)
289 {
290 char data;
291
292 if (setjmp (db_jmpbuf))
293 return -1;
294
295 data = val;
296
297 db_write_bytes (addr, 1, &data);
298 return 0;
299 }
300
301 /* convert the memory pointed to by mem into hex, placing result in buf */
302 /* return a pointer to the last char put in buf (null) */
303
304 static char *
305 mem2hex (vm_offset_t mem, char *buf, int count)
306 {
307 int i;
308 int ch;
309
310 for (i=0;i<count;i++) {
311 ch = get_char (mem++);
312 if (ch == -1)
313 return NULL;
314 *buf++ = hexchars[ch >> 4];
315 *buf++ = hexchars[ch % 16];
316 }
317 *buf = 0;
318 return(buf);
319 }
320
321 /* convert the hex array pointed to by buf into binary to be placed in mem */
322 /* return a pointer to the character AFTER the last byte written */
323 static char *
324 hex2mem (char *buf, vm_offset_t mem, int count)
325 {
326 int i;
327 int ch;
328 int rv;
329
330 for (i=0;i<count;i++) {
331 ch = hex(*buf++) << 4;
332 ch = ch + hex(*buf++);
333 rv = set_char (mem++, ch);
334 if (rv == -1)
335 return NULL;
336 }
337 return(buf);
338 }
339
340 /* this function takes the 386 exception vector and attempts to
341 translate this number into a unix compatible signal value */
342 static int
343 computeSignal (int exceptionVector)
344 {
345 int sigval;
346 switch (exceptionVector & ~T_USER)
347 {
348 case 0: sigval = 8; break; /* divide by zero */
349 case 1: sigval = 5; break; /* debug exception */
350 case 3: sigval = 5; break; /* breakpoint */
351 case 4: sigval = 16; break; /* into instruction (overflow) */
352 case 5: sigval = 16; break; /* bound instruction */
353 case 6: sigval = 4; break; /* Invalid opcode */
354 case 7: sigval = 8; break; /* coprocessor not available */
355 case 8: sigval = 7; break; /* double fault */
356 case 9: sigval = 11; break; /* coprocessor segment overrun */
357 case 10: sigval = 5; break; /* Invalid TSS (also single-step) */
358 case 11: sigval = 11; break; /* Segment not present */
359 case 12: sigval = 11; break; /* stack exception */
360 case 13: sigval = 11; break; /* general protection */
361 case 14: sigval = 11; break; /* page fault */
362 case 16: sigval = 7; break; /* coprocessor error */
363 default:
364 sigval = 7; /* "software generated"*/
365 }
366 return (sigval);
367 }
368
369 /*
370 * While we find nice hex chars, build an int.
371 * Return number of chars processed.
372 */
373
374 static int
375 hexToInt(char **ptr, int *intValue)
376 {
377 int numChars = 0;
378 int hexValue;
379
380 *intValue = 0;
381
382 while (**ptr)
383 {
384 hexValue = hex(**ptr);
385 if (hexValue >=0)
386 {
387 *intValue = (*intValue <<4) | hexValue;
388 numChars ++;
389 }
390 else
391 break;
392
393 (*ptr)++;
394 }
395
396 return (numChars);
397 }
398
399 #define NUMREGBYTES (sizeof registers)
400 #define PC 8
401 #define SP 4
402 #define FP 5
403 #define NUM_REGS 14
404
405 /*
406 * This function does all command procesing for interfacing to gdb.
407 */
408 void
409 gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
410 {
411 int sigval;
412 int addr, length;
413 char * ptr;
414 struct i386regs {
415 unsigned int eax;
416 unsigned int ecx;
417 unsigned int edx;
418 unsigned int ebx;
419 unsigned int esp;
420 unsigned int ebp;
421 unsigned int esi;
422 unsigned int edi;
423 unsigned int eip;
424 unsigned int eflags;
425 unsigned int cs;
426 unsigned int ss;
427 unsigned int ds;
428 unsigned int es;
429 };
430 struct i386regs registers;
431
432 registers.eax = raw_regs->tf_eax;
433 registers.ebx = raw_regs->tf_ebx;
434 registers.ecx = raw_regs->tf_ecx;
435 registers.edx = raw_regs->tf_edx;
436
437 registers.esp = raw_regs->tf_esp;
438 registers.ebp = raw_regs->tf_ebp;
439 registers.esi = raw_regs->tf_esi;
440 registers.edi = raw_regs->tf_edi;
441
442 registers.eip = raw_regs->tf_eip;
443 registers.eflags = raw_regs->tf_eflags;
444
445 registers.cs = raw_regs->tf_cs;
446 registers.ss = raw_regs->tf_ss;
447 registers.ds = raw_regs->tf_ds;
448 registers.es = raw_regs->tf_es;
449
450 /* reply to host that an exception has occurred */
451 sigval = computeSignal (type);
452 ptr = remcomOutBuffer;
453
454 *ptr++ = 'T';
455 *ptr++ = hexchars[sigval >> 4];
456 *ptr++ = hexchars[sigval & 0xf];
457
458 *ptr++ = hexchars[PC >> 4];
459 *ptr++ = hexchars[PC & 0xf];
460 *ptr++ = ':';
461 ptr = mem2hex ((vm_offset_t)®isters.eip, ptr, 4);
462 *ptr++ = ';';
463
464 *ptr++ = hexchars[FP >> 4];
465 *ptr++ = hexchars[FP & 0xf];
466 *ptr++ = ':';
467 ptr = mem2hex ((vm_offset_t)®isters.ebp, ptr, 4);
468 *ptr++ = ';';
469
470 *ptr++ = hexchars[SP >> 4];
471 *ptr++ = hexchars[SP & 0xf];
472 *ptr++ = ':';
473 ptr = mem2hex ((vm_offset_t)®isters.esp, ptr, 4);
474 *ptr++ = ';';
475
476 *ptr++ = 0;
477
478 putpacket (remcomOutBuffer);
479
480 while (1)
481 {
482 remcomOutBuffer[0] = 0;
483
484 getpacket (remcomInBuffer);
485 switch (remcomInBuffer[0])
486 {
487 case '?':
488 remcomOutBuffer[0] = 'S';
489 remcomOutBuffer[1] = hexchars[sigval >> 4];
490 remcomOutBuffer[2] = hexchars[sigval % 16];
491 remcomOutBuffer[3] = 0;
492 break;
493
494 case 'D': /* detach; say OK and turn off gdb */
495 putpacket(remcomOutBuffer);
496 boothowto &= ~RB_GDB;
497 return;
498
499 case 'g': /* return the value of the CPU registers */
500 mem2hex ((vm_offset_t)®isters, remcomOutBuffer, NUMREGBYTES);
501 break;
502
503 case 'G': /* set the value of the CPU registers - return OK */
504 hex2mem (&remcomInBuffer[1], (vm_offset_t)®isters, NUMREGBYTES);
505 strcpy (remcomOutBuffer, "OK");
506 break;
507
508 case 'P': /* Set the value of one register */
509 {
510 int regno;
511
512 ptr = &remcomInBuffer[1];
513
514 if (hexToInt (&ptr, ®no)
515 && *ptr++ == '='
516 && regno < NUM_REGS)
517 {
518 hex2mem (ptr, (vm_offset_t)®isters + regno * 4, 4);
519 strcpy(remcomOutBuffer,"OK");
520 }
521 else
522 strcpy (remcomOutBuffer, "P01");
523 break;
524 }
525 case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
526 /* Try to read %x,%x. */
527
528 ptr = &remcomInBuffer[1];
529
530 if (hexToInt (&ptr, &addr)
531 && *(ptr++) == ','
532 && hexToInt (&ptr, &length))
533 {
534 if (mem2hex((vm_offset_t) addr, remcomOutBuffer, length) == NULL)
535 strcpy (remcomOutBuffer, "E03");
536 break;
537 }
538 else
539 strcpy (remcomOutBuffer, "E01");
540 break;
541
542 case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
543
544 /* Try to read '%x,%x:'. */
545
546 ptr = &remcomInBuffer[1];
547
548 if (hexToInt(&ptr,&addr)
549 && *(ptr++) == ','
550 && hexToInt(&ptr, &length)
551 && *(ptr++) == ':')
552 {
553 if (hex2mem(ptr, (vm_offset_t) addr, length) == NULL)
554 strcpy (remcomOutBuffer, "E03");
555 else
556 strcpy (remcomOutBuffer, "OK");
557 }
558 else
559 strcpy (remcomOutBuffer, "E02");
560 break;
561
562 /* cAA..AA Continue at address AA..AA(optional) */
563 /* sAA..AA Step one instruction from AA..AA(optional) */
564 case 'c' :
565 case 's' :
566 /* try to read optional parameter, pc unchanged if no parm */
567
568 ptr = &remcomInBuffer[1];
569 if (hexToInt(&ptr,&addr))
570 registers.eip = addr;
571
572
573 /* set the trace bit if we're stepping */
574 if (remcomInBuffer[0] == 's')
575 registers.eflags |= PSL_T;
576 else
577 registers.eflags &= ~PSL_T;
578
579 raw_regs->tf_eax = registers.eax;
580 raw_regs->tf_ebx = registers.ebx;
581 raw_regs->tf_ecx = registers.ecx;
582 raw_regs->tf_edx = registers.edx;
583
584 raw_regs->tf_esp = registers.esp;
585 raw_regs->tf_ebp = registers.ebp;
586 raw_regs->tf_esi = registers.esi;
587 raw_regs->tf_edi = registers.edi;
588
589 raw_regs->tf_eip = registers.eip;
590 raw_regs->tf_eflags = registers.eflags;
591
592 raw_regs->tf_cs = registers.cs;
593 raw_regs->tf_ss = registers.ss;
594 raw_regs->tf_ds = registers.ds;
595 raw_regs->tf_es = registers.es;
596 return;
597
598 } /* switch */
599
600 /* reply to the request */
601 putpacket (remcomOutBuffer);
602 }
603 }
604 #endif /* NSIO > 0 */
Cache object: 358a90552636c8a1c485f52ed0344603
|