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