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