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