Fix minor buildworld issues, mainly #include file dependancies and fields
[dragonfly.git] / sys / i386 / i386 / i386-gdbstub.c
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 $ */
95 /* $DragonFly: src/sys/i386/i386/Attic/i386-gdbstub.c,v 1.2 2003/06/17 04:28:35 dillon Exp $ */
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 <setjmp.h>
105
106 #include "sio.h"
107 #include "opt_ddb.h"
108
109 void            gdb_handle_exception (db_regs_t *, int, int);
110
111 #if NSIO == 0
112 void
113 gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
114 {
115 }
116 #else
117 /************************************************************************/
118
119 extern 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
133 static int
134 strlen (const char *s)
135 {
136   const char *s1 = s;
137
138   while (*s1++ != '\000');
139
140   return s1 - s;
141 }
142
143 static char *
144 strcpy (char *dst, const char *src)
145 {
146   char *retval = dst;
147
148   while ((*dst++ = *src++) != '\000');
149
150   return retval;
151 }
152
153 static int
154 putDebugChar (int c)            /* write a single character      */
155 {
156   if (gdbdev == NODEV)
157         return 0;
158   (*gdb_putc)(gdbdev, c);
159   return 1;
160 }
161
162 static int
163 getDebugChar (void)             /* read and return a single char */
164 {
165   if (gdbdev == NODEV)
166         return -1;
167   return (*gdb_getc)(gdbdev);
168 }
169
170 static const char hexchars[]="0123456789abcdef";
171
172 static int
173 hex(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>     */
182 static void
183 getpacket (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
245 static void
246 putpacket (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
286 static char  remcomInBuffer[BUFMAX];
287 static char  remcomOutBuffer[BUFMAX];
288
289 static int
290 get_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
302 static int
303 set_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
319 static char *
320 mem2hex (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 */
338 static char *
339 hex2mem (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 */
357 static int
358 computeSignal (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
389 static int
390 hexToInt(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  */
423 void
424 gdb_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 */