The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/amd64/amd64/amd64-gdbstub.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 #include <sys/cdefs.h>
   16 __FBSDID("$FreeBSD: releng/5.2/sys/amd64/amd64/amd64-gdbstub.c 123180 2003-12-06 23:19:47Z peter $");
   17 
   18 /****************************************************************************
   19  *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
   20  *
   21  *  Module name: remcom.c $
   22  *  Revision: 1.34 $
   23  *  Date: 91/03/09 12:29:49 $
   24  *  Contributor:     Lake Stevens Instrument Division$
   25  *
   26  *  Description:     low level support for gdb debugger. $
   27  *
   28  *  Considerations:  only works on target hardware $
   29  *
   30  *  Written by:      Glenn Engel $
   31  *  ModuleState:     Experimental $
   32  *
   33  *  NOTES:           See Below $
   34  *
   35  *  Modified for FreeBSD by Stu Grossman.
   36  *
   37  *  To enable debugger support, two things need to happen.  One, a
   38  *  call to set_debug_traps() is necessary in order to allow any breakpoints
   39  *  or error conditions to be properly intercepted and reported to gdb.
   40  *  Two, a breakpoint needs to be generated to begin communication.  This
   41  *  is most easily accomplished by a call to breakpoint().  Breakpoint()
   42  *  simulates a breakpoint by executing a trap #1.
   43  *
   44  *  The external function exceptionHandler() is
   45  *  used to attach a specific handler to a specific 386 vector number.
   46  *  It should use the same privilege level it runs at.  It should
   47  *  install it as an interrupt gate so that interrupts are masked
   48  *  while the handler runs.
   49  *  Also, need to assign exceptionHook and oldExceptionHook.
   50  *
   51  *  Because gdb will sometimes write to the stack area to execute function
   52  *  calls, this program cannot rely on using the supervisor stack so it
   53  *  uses its own stack area reserved in the int array remcomStack.
   54  *
   55  *************
   56  *
   57  *    The following gdb commands are supported:
   58  *
   59  * command          function                               Return value
   60  *
   61  *    g             return the value of the CPU registers  hex data or ENN
   62  *    G             set the value of the CPU registers     OK or ENN
   63  *
   64  *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
   65  *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
   66  *
   67  *    c             Resume at current address              SNN   ( signal NN)
   68  *    cAA..AA       Continue at address AA..AA             SNN
   69  *
   70  *    s             Step one instruction                   SNN
   71  *    sAA..AA       Step one instruction from AA..AA       SNN
   72  *
   73  *    k             kill
   74  *
   75  *    ?             What was the last sigval ?             SNN   (signal NN)
   76  *
   77  *    D             detach                                 OK
   78  *
   79  * All commands and responses are sent with a packet which includes a
   80  * checksum.  A packet consists of
   81  *
   82  * $<packet info>#<checksum>.
   83  *
   84  * where
   85  * <packet info> :: <characters representing the command or response>
   86  * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
   87  *
   88  * When a packet is received, it is first acknowledged with either '+' or '-'.
   89  * '+' indicates a successful transfer.  '-' indicates a failed transfer.
   90  *
   91  * Example:
   92  *
   93  * Host:                  Reply:
   94  * $m0,10#2a               +$00010203040506070809101112131415#42
   95  *
   96  ****************************************************************************/
   97 
   98 #include <sys/param.h>
   99 #include <sys/reboot.h>
  100 #include <sys/systm.h>
  101 #include <sys/cons.h>
  102 
  103 #include <ddb/ddb.h>
  104 
  105 #include <machine/setjmp.h>
  106 
  107 #include "opt_ddb.h"
  108 
  109 int             gdb_handle_exception (db_regs_t *, int, int);
  110 
  111 /************************************************************************/
  112 
  113 extern jmp_buf  db_jmpbuf;
  114 
  115 /************************************************************************/
  116 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
  117 /* at least NUMREGBYTES*2 are needed for register packets */
  118 #define BUFMAX 400
  119 
  120 /* Create private copies of common functions used by the stub.  This prevents
  121    nasty interactions between app code and the stub (for instance if user steps
  122    into strlen, etc..) */
  123 
  124 static int
  125 gdb_strlen (const char *s)
  126 {
  127   const char *s1 = s;
  128 
  129   while (*s1++ != '\000');
  130 
  131   return s1 - s;
  132 }
  133 
  134 static char *
  135 gdb_strcpy (char *dst, const char *src)
  136 {
  137   char *retval = dst;
  138 
  139   while ((*dst++ = *src++) != '\000');
  140 
  141   return retval;
  142 }
  143 
  144 static int
  145 putDebugChar (int c)            /* write a single character      */
  146 {
  147   if (gdb_arg == NULL)
  148         return 0;
  149   (*gdb_putc)(gdb_arg, c);
  150   return 1;
  151 }
  152 
  153 static int
  154 getDebugChar (void)             /* read and return a single char */
  155 {
  156   if (gdb_arg == NULL)
  157         return -1;
  158   return (*gdb_getc)(gdb_arg);
  159 }
  160 
  161 static const char hexchars[]="0123456789abcdef";
  162 
  163 static int
  164 hex(char ch)
  165 {
  166   if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
  167   if ((ch >= '') && (ch <= '9')) return (ch-'');
  168   if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
  169   return (-1);
  170 }
  171 
  172 /* scan for the sequence $<data>#<checksum>     */
  173 static void
  174 getpacket (char *buffer)
  175 {
  176   unsigned char checksum;
  177   unsigned char xmitcsum;
  178   int i;
  179   int count;
  180   unsigned char ch;
  181 
  182   do
  183     {
  184       /* wait around for the start character, ignore all other characters */
  185 
  186       while ((ch = (getDebugChar () & 0x7f)) != '$');
  187 
  188       checksum = 0;
  189       xmitcsum = -1;
  190 
  191       count = 0;
  192 
  193       /* now, read until a # or end of buffer is found */
  194 
  195       while (count < BUFMAX)
  196         {
  197           ch = getDebugChar () & 0x7f;
  198           if (ch == '#')
  199             break;
  200           checksum = checksum + ch;
  201           buffer[count] = ch;
  202           count = count + 1;
  203         }
  204       buffer[count] = 0;
  205 
  206       if (ch == '#')
  207         {
  208           xmitcsum = hex (getDebugChar () & 0x7f) << 4;
  209           xmitcsum += hex (getDebugChar () & 0x7f);
  210 
  211           if (checksum != xmitcsum)
  212             putDebugChar ('-');  /* failed checksum */
  213           else
  214             {
  215               putDebugChar ('+'); /* successful transfer */
  216               /* if a sequence char is present, reply the sequence ID */
  217               if (buffer[2] == ':')
  218                 {
  219                   putDebugChar (buffer[0]);
  220                   putDebugChar (buffer[1]);
  221 
  222                   /* remove sequence chars from buffer */
  223 
  224                   count = gdb_strlen (buffer);
  225                   for (i=3; i <= count; i++)
  226                     buffer[i-3] = buffer[i];
  227                 }
  228             }
  229         }
  230     }
  231   while (checksum != xmitcsum);
  232 }
  233 
  234 /* send the packet in buffer.  */
  235 
  236 static void
  237 putpacket (char *buffer)
  238 {
  239   unsigned char checksum;
  240   int count;
  241   unsigned char ch;
  242 
  243   /*  $<packet info>#<checksum>. */
  244   do
  245     {
  246 /*
  247  * This is a non-standard hack to allow use of the serial console for
  248  * operation as well as debugging.  Simply turn on 'remotechat' in gdb.
  249  *
  250  * This extension is not part of the Cygnus protocol, is kinda gross,
  251  * but gets the job done.
  252  */
  253 #ifdef GDB_REMOTE_CHAT
  254       putDebugChar ('|');
  255       putDebugChar ('|');
  256       putDebugChar ('|');
  257       putDebugChar ('|');
  258 #endif
  259       putDebugChar ('$');
  260       checksum = 0;
  261       count = 0;
  262 
  263       while ((ch=buffer[count]) != 0)
  264         {
  265           putDebugChar (ch);
  266           checksum += ch;
  267           count += 1;
  268         }
  269 
  270       putDebugChar ('#');
  271       putDebugChar (hexchars[checksum >> 4]);
  272       putDebugChar (hexchars[checksum & 0xf]);
  273     }
  274   while ((getDebugChar () & 0x7f) != '+');
  275 }
  276 
  277 static char  remcomInBuffer[BUFMAX];
  278 static char  remcomOutBuffer[BUFMAX];
  279 
  280 static int
  281 get_char (vm_offset_t addr)
  282 {
  283   char data;
  284 
  285   if (setjmp (db_jmpbuf))
  286     return -1;
  287 
  288   db_read_bytes (addr, 1, &data);
  289 
  290   return data & 0xff;
  291 }
  292 
  293 static int
  294 set_char (vm_offset_t addr, int val)
  295 {
  296   char data;
  297 
  298   if (setjmp (db_jmpbuf))
  299     return -1;
  300 
  301   data = val;
  302 
  303   db_write_bytes (addr, 1, &data);
  304   return 0;
  305 }
  306 
  307 /* convert the memory pointed to by mem into hex, placing result in buf */
  308 /* return a pointer to the last char put in buf (null) */
  309 
  310 static char *
  311 mem2hex (vm_offset_t mem, char *buf, int count)
  312 {
  313       int i;
  314       int ch;
  315 
  316       for (i=0;i<count;i++) {
  317           ch = get_char (mem++);
  318           if (ch == -1)
  319             return NULL;
  320           *buf++ = hexchars[ch >> 4];
  321           *buf++ = hexchars[ch % 16];
  322       }
  323       *buf = 0;
  324       return(buf);
  325 }
  326 
  327 /* convert the hex array pointed to by buf into binary to be placed in mem */
  328 /* return a pointer to the character AFTER the last byte written */
  329 static char *
  330 hex2mem (char *buf, vm_offset_t mem, int count)
  331 {
  332       int i;
  333       int ch;
  334       int rv;
  335 
  336       for (i=0;i<count;i++) {
  337           ch = hex(*buf++) << 4;
  338           ch = ch + hex(*buf++);
  339           rv = set_char (mem++, ch);
  340           if (rv == -1)
  341             return NULL;
  342       }
  343       return(buf);
  344 }
  345 
  346 /* this function takes the 386 exception vector and attempts to
  347    translate this number into a unix compatible signal value */
  348 static int
  349 computeSignal (int exceptionVector)
  350 {
  351   int sigval;
  352   switch (exceptionVector & ~T_USER)
  353     {
  354     case 0: sigval = 8; break; /* divide by zero */
  355     case 1: sigval = 5; break; /* debug exception */
  356     case 3: sigval = 5; break; /* breakpoint */
  357     case 4: sigval = 16; break; /* into instruction (overflow) */
  358     case 5: sigval = 16; break; /* bound instruction */
  359     case 6: sigval = 4; break; /* Invalid opcode */
  360     case 7: sigval = 8; break; /* coprocessor not available */
  361     case 8: sigval = 7; break; /* double fault */
  362     case 9: sigval = 11; break; /* coprocessor segment overrun */
  363     case 10: sigval = 5; break; /* Invalid TSS (also single-step) */
  364     case 11: sigval = 11; break; /* Segment not present */
  365     case 12: sigval = 11; break; /* stack exception */
  366     case 13: sigval = 11; break; /* general protection */
  367     case 14: sigval = 11; break; /* page fault */
  368     case 16: sigval = 7; break; /* coprocessor error */
  369     default:
  370       sigval = 7;         /* "software generated"*/
  371     }
  372   return (sigval);
  373 }
  374 
  375 /*
  376  * While we find nice hex chars, build an int.
  377  * Return number of chars processed.
  378  */
  379 
  380 static int
  381 hexToInt(char **ptr, int *intValue)
  382 {
  383     int numChars = 0;
  384     int hexValue;
  385 
  386     *intValue = 0;
  387 
  388     while (**ptr)
  389     {
  390         hexValue = hex(**ptr);
  391         if (hexValue >=0)
  392         {
  393             *intValue = (*intValue <<4) | hexValue;
  394             numChars ++;
  395         }
  396         else
  397             break;
  398 
  399         (*ptr)++;
  400     }
  401 
  402     return (numChars);
  403 }
  404 
  405 #define NUMREGBYTES (sizeof registers)
  406 #define PC 8
  407 #define SP 4
  408 #define FP 5
  409 #define NUM_REGS 14
  410 
  411 /*
  412  * This function does all command procesing for interfacing to gdb.
  413  */
  414 int
  415 gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
  416 {
  417   int    sigval;
  418   int    addr, length;
  419   char * ptr;
  420   struct i386regs {
  421     unsigned int rax;
  422     unsigned int rcx;
  423     unsigned int rdx;
  424     unsigned int rbx;
  425     unsigned int rsp;
  426     unsigned int rbp;
  427     unsigned int rsi;
  428     unsigned int rdi;
  429     unsigned int rip;
  430     unsigned int rflags;
  431     unsigned int cs;
  432     unsigned int ss;
  433     unsigned int ds;
  434     unsigned int es;
  435   };
  436   struct i386regs registers;
  437 
  438   registers.rax = raw_regs->tf_rax;
  439   registers.rbx = raw_regs->tf_rbx;
  440   registers.rcx = raw_regs->tf_rcx;
  441   registers.rdx = raw_regs->tf_rdx;
  442 
  443   registers.rsp = raw_regs->tf_rsp;
  444   registers.rbp = raw_regs->tf_rbp;
  445   registers.rsi = raw_regs->tf_rsi;
  446   registers.rdi = raw_regs->tf_rdi;
  447 
  448   registers.rip = raw_regs->tf_rip;
  449   registers.rflags = raw_regs->tf_rflags;
  450 
  451   registers.cs = raw_regs->tf_cs;
  452   registers.ss = raw_regs->tf_ss;
  453   registers.ds = 0;     /* XXX rds() */
  454   registers.es = 0;     /* XXX res() */
  455 
  456   /* reply to host that an exception has occurred */
  457   sigval = computeSignal (type);
  458   ptr = remcomOutBuffer;
  459 
  460   *ptr++ = 'T';
  461   *ptr++ = hexchars[sigval >> 4];
  462   *ptr++ = hexchars[sigval & 0xf];
  463 
  464   *ptr++ = hexchars[PC >> 4];
  465   *ptr++ = hexchars[PC & 0xf];
  466   *ptr++ = ':';
  467   ptr = mem2hex ((vm_offset_t)&registers.rip, ptr, 4);
  468   *ptr++ = ';';
  469 
  470   *ptr++ = hexchars[FP >> 4];
  471   *ptr++ = hexchars[FP & 0xf];
  472   *ptr++ = ':';
  473   ptr = mem2hex ((vm_offset_t)&registers.rbp, ptr, 4);
  474   *ptr++ = ';';
  475 
  476   *ptr++ = hexchars[SP >> 4];
  477   *ptr++ = hexchars[SP & 0xf];
  478   *ptr++ = ':';
  479   ptr = mem2hex ((vm_offset_t)&registers.rsp, ptr, 4);
  480   *ptr++ = ';';
  481 
  482   *ptr++ = 0;
  483 
  484   putpacket (remcomOutBuffer);
  485 
  486   while (1)
  487     {
  488       if (gdb_arg == NULL)
  489         return 1;               /* somebody has removed the gdb device */
  490       remcomOutBuffer[0] = 0;
  491 
  492       getpacket (remcomInBuffer);
  493       switch (remcomInBuffer[0]) 
  494         {
  495         case '?':
  496           remcomOutBuffer[0] = 'S';
  497           remcomOutBuffer[1] = hexchars[sigval >> 4];
  498           remcomOutBuffer[2] = hexchars[sigval % 16];
  499           remcomOutBuffer[3] = 0;
  500           break;
  501 
  502         case 'D':               /* detach; say OK and turn off gdb */
  503           putpacket(remcomOutBuffer);
  504           boothowto &= ~RB_GDB;
  505           return 0;
  506 
  507         case 'g':               /* return the value of the CPU registers */
  508           mem2hex ((vm_offset_t)&registers, remcomOutBuffer, NUMREGBYTES);
  509           break;
  510 
  511         case 'G':               /* set the value of the CPU registers - return OK */
  512           hex2mem (&remcomInBuffer[1], (vm_offset_t)&registers, NUMREGBYTES);
  513           gdb_strcpy (remcomOutBuffer, "OK");
  514           break;
  515 
  516         case 'P':               /* Set the value of one register */
  517           {
  518             int regno;
  519 
  520             ptr = &remcomInBuffer[1];
  521 
  522             if (hexToInt (&ptr, &regno)
  523                 && *ptr++ == '='
  524                 && regno < NUM_REGS)
  525               {
  526                 hex2mem (ptr, (vm_offset_t)&registers + regno * 4, 4);
  527                 gdb_strcpy(remcomOutBuffer,"OK");
  528               }
  529             else
  530               gdb_strcpy (remcomOutBuffer, "P01");
  531             break;
  532           }
  533         case 'm':       /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
  534           /* Try to read %x,%x.  */
  535 
  536           ptr = &remcomInBuffer[1];
  537 
  538           if (hexToInt (&ptr, &addr)
  539               && *(ptr++) == ','
  540               && hexToInt (&ptr, &length))
  541             {
  542               if (mem2hex((vm_offset_t) addr, remcomOutBuffer, length) == NULL)
  543                 gdb_strcpy (remcomOutBuffer, "E03");
  544               break;
  545             }
  546           else
  547             gdb_strcpy (remcomOutBuffer, "E01");
  548           break;
  549 
  550         case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
  551 
  552           /* Try to read '%x,%x:'.  */
  553 
  554           ptr = &remcomInBuffer[1];
  555 
  556           if (hexToInt(&ptr,&addr)
  557               && *(ptr++) == ','
  558               && hexToInt(&ptr, &length)
  559               && *(ptr++) == ':')
  560             {
  561               if (hex2mem(ptr, (vm_offset_t) addr, length) == NULL)
  562                 gdb_strcpy (remcomOutBuffer, "E03");
  563               else
  564                 gdb_strcpy (remcomOutBuffer, "OK");
  565             }
  566           else
  567             gdb_strcpy (remcomOutBuffer, "E02");
  568           break;
  569 
  570           /* cAA..AA    Continue at address AA..AA(optional) */
  571           /* sAA..AA   Step one instruction from AA..AA(optional) */
  572         case 'c' :
  573         case 's' :
  574           /* try to read optional parameter, pc unchanged if no parm */
  575 
  576           ptr = &remcomInBuffer[1];
  577           if (hexToInt(&ptr,&addr))
  578             registers.rip = addr;
  579 
  580 
  581           /* set the trace bit if we're stepping */
  582           if (remcomInBuffer[0] == 's')
  583             registers.rflags |= PSL_T;
  584           else
  585             registers.rflags &= ~PSL_T;
  586 
  587           raw_regs->tf_rax = registers.rax;
  588           raw_regs->tf_rbx = registers.rbx;
  589           raw_regs->tf_rcx = registers.rcx;
  590           raw_regs->tf_rdx = registers.rdx;
  591 
  592           raw_regs->tf_rsp = registers.rsp;
  593           raw_regs->tf_rbp = registers.rbp;
  594           raw_regs->tf_rsi = registers.rsi;
  595           raw_regs->tf_rdi = registers.rdi;
  596 
  597           raw_regs->tf_rip = registers.rip;
  598           raw_regs->tf_rflags = registers.rflags;
  599 
  600           raw_regs->tf_cs = registers.cs;
  601           raw_regs->tf_ss = registers.ss;
  602 #if 0
  603           raw_regs->tf_ds = registers.ds;
  604           raw_regs->tf_es = registers.es;
  605 #endif
  606           return 0;
  607 
  608         } /* switch */
  609 
  610       /* reply to the request */
  611       putpacket (remcomOutBuffer);
  612     }
  613   return 0;
  614 }

Cache object: e5dcf259377da29d67cc413851ca8417


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.