Home | History | Annotate | Download | only in GdbStub
      1 /** @file
      2   Serial IO Abstraction for GDB stub. This allows an EFI consoles that shows up on the system
      3   running GDB. One consle for error information and another console for user input/output.
      4 
      5   Basic packet format is $packet-data#checksum. So every comand has 4 bytes of overhead: $,
      6   #, 0, 0. The 0 and 0 are the ascii characters for the checksum.
      7 
      8 
      9   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
     10 
     11   This program and the accompanying materials
     12   are licensed and made available under the terms and conditions of the BSD License
     13   which accompanies this distribution.  The full text of the license may be found at
     14   http://opensource.org/licenses/bsd-license.php
     15 
     16   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     17   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     18 
     19 **/
     20 
     21 #include <GdbStubInternal.h>
     22 
     23 //
     24 // Set TRUE if F Reply package signals a ctrl-c. We can not process the Ctrl-c
     25 // here we need to wait for the periodic callback to do this.
     26 //
     27 BOOLEAN gCtrlCBreakFlag = FALSE;
     28 
     29 //
     30 // If the periodic callback is called while we are processing an F packet we need
     31 // to let the callback know to not read from the serail stream as it could steal
     32 // characters from the F reponse packet
     33 //
     34 BOOLEAN gProcessingFPacket = FALSE;
     35 
     36 /**
     37   Process a control-C break message.
     38 
     39   Currently a place holder, remove the ASSERT when it gets implemented.
     40 
     41   @param  ErrNo   Error infomration from the F reply packet or other source
     42 
     43 **/
     44 
     45 VOID
     46 GdbCtrlCBreakMessage (
     47   IN  UINTN ErrNo
     48   )
     49 {
     50   // See D.10.5 of gdb.pdf
     51   // This should look like a break message. Should look like SIGINT
     52 
     53   /* TODO: Make sure if we should do anything with ErrNo */
     54   //Turn on the global Ctrl-C flag.
     55   gCtrlCBreakFlag = TRUE;
     56 }
     57 
     58 
     59 /**
     60   Parse the F reply packet and extract the return value and an ErrNo if it exists.
     61 
     62   @param  Packet  Packet to parse like an F reply packet
     63   @param  ErrNo   Buffer to hold Count bytes that were read
     64 
     65   @retval -1      Error, not a valid F reply packet
     66   @retval other   Return the return code from the F reply packet
     67 
     68 **/
     69 INTN
     70 GdbParseFReplyPacket (
     71   IN  CHAR8   *Packet,
     72   OUT UINTN   *ErrNo
     73   )
     74 {
     75   INTN   RetCode;
     76 
     77   if (Packet[0] != 'F') {
     78     // A valid responce would be an F packet
     79     return -1;
     80   }
     81 
     82   RetCode = AsciiStrHexToUintn (&Packet[1]);
     83 
     84   // Find 1st comma
     85   for (;*Packet != '\0' && *Packet != ',';  Packet++);
     86   if (*Packet == '\0') {
     87     *ErrNo = 0;
     88     return RetCode;
     89   }
     90 
     91   *ErrNo = AsciiStrHexToUintn (++Packet);
     92 
     93   // Find 2nd comma
     94   for (;*Packet != '\0' && *Packet != ',';  Packet++);
     95   if (*Packet == '\0') {
     96     return RetCode;
     97   }
     98 
     99   if (*(++Packet) == 'C') {
    100     GdbCtrlCBreakMessage (*ErrNo);
    101   }
    102 
    103   return RetCode;
    104 }
    105 
    106 
    107 /**
    108   Read data from a FileDescriptor. On success number of bytes read is returned. Zero indicates
    109   the end of a file. On error -1 is returned. If count is zero, GdbRead returns zero.
    110 
    111   @param  FileDescriptor   Device to talk to.
    112   @param  Buffer           Buffer to hold Count bytes that were read
    113   @param  Count            Number of bytes to transfer.
    114 
    115   @retval -1               Error
    116   @retval {other}          Number of bytes read.
    117 
    118 **/
    119 INTN
    120 GdbRead (
    121   IN  INTN    FileDescriptor,
    122   OUT VOID    *Buffer,
    123   IN  UINTN   Count
    124   )
    125 {
    126   CHAR8   Packet[128];
    127   UINTN   Size;
    128   INTN    RetCode;
    129   UINTN   ErrNo;
    130   BOOLEAN ReceiveDone = FALSE;
    131 
    132   // Send:
    133   // "Fread,XX,YYYYYYYY,XX
    134   //
    135   // XX - FileDescriptor in ASCII
    136   // YYYYYYYY - Buffer address in ASCII
    137   // XX - Count in ASCII
    138   // SS - check sum
    139   //
    140   Size = AsciiSPrint (Packet, sizeof (Packet), "Fread,%x,%x,%x", FileDescriptor, Buffer, Count);
    141   // Packet array is too small if you got this ASSERT
    142   ASSERT (Size < sizeof (Packet));
    143 
    144   gProcessingFPacket = TRUE;
    145   SendPacket (Packet);
    146   Print ((CHAR16 *)L"Packet sent..\n");
    147 
    148   do {
    149     // Reply:
    150     ReceivePacket (Packet, sizeof (Packet));
    151     Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);
    152 
    153     // Process GDB commands
    154     switch (Packet[0]) {
    155       //Write memory command.
    156       //M addr,length:XX...
    157       case    'M':
    158         WriteToMemory (Packet);
    159         break;
    160 
    161       //Fretcode, errno, Ctrl-C flag
    162       //retcode - Count read
    163       case    'F':
    164         //Once target receives F reply packet that means the previous
    165         //transactions are finished.
    166         ReceiveDone = TRUE;
    167         break;
    168 
    169       //Send empty buffer
    170       default    :
    171         SendNotSupported();
    172         break;
    173     }
    174   } while (ReceiveDone == FALSE);
    175 
    176   RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
    177   Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);
    178 
    179   if (ErrNo > 0) {
    180     //Send error to the host if there is any.
    181     SendError ((UINT8)ErrNo);
    182   }
    183 
    184   gProcessingFPacket = FALSE;
    185 
    186   return RetCode;
    187 }
    188 
    189 
    190 /**
    191   Write data to a FileDescriptor. On success number of bytes written is returned. Zero indicates
    192   nothing was written. On error -1 is returned.
    193 
    194   @param  FileDescriptor   Device to talk to.
    195   @param  Buffer           Buffer to hold Count bytes that are to be written
    196   @param  Count            Number of bytes to transfer.
    197 
    198   @retval -1               Error
    199   @retval {other}          Number of bytes written.
    200 
    201 **/
    202 INTN
    203 GdbWrite (
    204   IN  INTN          FileDescriptor,
    205   OUT CONST VOID    *Buffer,
    206   IN  UINTN         Count
    207   )
    208 {
    209   CHAR8   Packet[128];
    210   UINTN   Size;
    211   INTN    RetCode;
    212   UINTN   ErrNo;
    213   BOOLEAN ReceiveDone = FALSE;
    214 
    215   // Send:
    216   // #Fwrite,XX,YYYYYYYY,XX$SS
    217   //
    218   // XX - FileDescriptor in ASCII
    219   // YYYYYYYY - Buffer address in ASCII
    220   // XX - Count in ASCII
    221   // SS - check sum
    222   //
    223   Size = AsciiSPrint (Packet, sizeof (Packet), "Fwrite,%x,%x,%x", FileDescriptor, Buffer, Count);
    224   // Packet array is too small if you got this ASSERT
    225   ASSERT (Size < sizeof (Packet));
    226 
    227   SendPacket (Packet);
    228   Print ((CHAR16 *)L"Packet sent..\n");
    229 
    230   do {
    231     // Reply:
    232     ReceivePacket (Packet, sizeof (Packet));
    233     Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);
    234 
    235     // Process GDB commands
    236     switch (Packet[0]) {
    237       //Read memory command.
    238       //m addr,length.
    239       case    'm':
    240         ReadFromMemory (Packet);
    241         break;
    242 
    243       //Fretcode, errno, Ctrl-C flag
    244       //retcode - Count read
    245       case    'F':
    246         //Once target receives F reply packet that means the previous
    247         //transactions are finished.
    248         ReceiveDone = TRUE;
    249         break;
    250 
    251       //Send empty buffer
    252       default    :
    253         SendNotSupported();
    254         break;
    255     }
    256   } while (ReceiveDone == FALSE);
    257 
    258   RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
    259   Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);
    260 
    261   //Send error to the host if there is any.
    262   if (ErrNo > 0) {
    263     SendError((UINT8)ErrNo);
    264   }
    265 
    266   return RetCode;
    267 }
    268 
    269 
    270 /**
    271   Reset the serial device.
    272 
    273   @param  This              Protocol instance pointer.
    274 
    275   @retval EFI_SUCCESS       The device was reset.
    276   @retval EFI_DEVICE_ERROR  The serial device could not be reset.
    277 
    278 **/
    279 EFI_STATUS
    280 EFIAPI
    281 GdbSerialReset (
    282   IN EFI_SERIAL_IO_PROTOCOL  *This
    283   )
    284 {
    285   return EFI_SUCCESS;
    286 }
    287 
    288 
    289 /**
    290   Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
    291   data buts, and stop bits on a serial device.
    292 
    293   @param  This             Protocol instance pointer.
    294   @param  BaudRate         The requested baud rate. A BaudRate value of 0 will use the the
    295                            device's default interface speed.
    296   @param  ReveiveFifoDepth The requested depth of the FIFO on the receive side of the
    297                            serial interface. A ReceiveFifoDepth value of 0 will use
    298                            the device's dfault FIFO depth.
    299   @param  Timeout          The requested time out for a single character in microseconds.
    300                            This timeout applies to both the transmit and receive side of the
    301                            interface. A Timeout value of 0 will use the device's default time
    302                            out value.
    303   @param  Parity           The type of parity to use on this serial device. A Parity value of
    304                            DefaultParity will use the device's default parity value.
    305   @param  DataBits         The number of data bits to use on the serial device. A DataBits
    306                            vaule of 0 will use the device's default data bit setting.
    307   @param  StopBits         The number of stop bits to use on this serial device. A StopBits
    308                            value of DefaultStopBits will use the device's default number of
    309                            stop bits.
    310 
    311   @retval EFI_SUCCESS      The device was reset.
    312   @retval EFI_DEVICE_ERROR The serial device could not be reset.
    313 
    314 **/
    315 EFI_STATUS
    316 EFIAPI
    317 GdbSerialSetAttributes (
    318   IN EFI_SERIAL_IO_PROTOCOL  *This,
    319   IN UINT64                  BaudRate,
    320   IN UINT32                  ReceiveFifoDepth,
    321   IN UINT32                  Timeout,
    322   IN EFI_PARITY_TYPE         Parity,
    323   IN UINT8                   DataBits,
    324   IN EFI_STOP_BITS_TYPE      StopBits
    325   )
    326 {
    327   return EFI_UNSUPPORTED;
    328 }
    329 
    330 
    331 /**
    332   Set the control bits on a serial device
    333 
    334   @param  This             Protocol instance pointer.
    335   @param  Control          Set the bits of Control that are settable.
    336 
    337   @retval EFI_SUCCESS      The new control bits were set on the serial device.
    338   @retval EFI_UNSUPPORTED  The serial device does not support this operation.
    339   @retval EFI_DEVICE_ERROR The serial device is not functioning correctly.
    340 
    341 **/
    342 EFI_STATUS
    343 EFIAPI
    344 GdbSerialSetControl (
    345   IN EFI_SERIAL_IO_PROTOCOL  *This,
    346   IN UINT32                  Control
    347   )
    348 {
    349   return EFI_UNSUPPORTED;
    350 }
    351 
    352 
    353 /**
    354   Retrieves the status of thecontrol bits on a serial device
    355 
    356   @param  This              Protocol instance pointer.
    357   @param  Control           A pointer to return the current Control signals from the serial device.
    358 
    359   @retval EFI_SUCCESS       The control bits were read from the serial device.
    360   @retval EFI_DEVICE_ERROR  The serial device is not functioning correctly.
    361 
    362 **/
    363 EFI_STATUS
    364 EFIAPI
    365 GdbSerialGetControl (
    366   IN EFI_SERIAL_IO_PROTOCOL  *This,
    367   OUT UINT32                 *Control
    368   )
    369 {
    370   return EFI_UNSUPPORTED;
    371 }
    372 
    373 
    374 /**
    375   Writes data to a serial device.
    376 
    377   @param  This              Protocol instance pointer.
    378   @param  BufferSize        On input, the size of the Buffer. On output, the amount of
    379                             data actually written.
    380   @param  Buffer            The buffer of data to write
    381 
    382   @retval EFI_SUCCESS       The data was written.
    383   @retval EFI_DEVICE_ERROR  The device reported an error.
    384   @retval EFI_TIMEOUT       The data write was stopped due to a timeout.
    385 
    386 **/
    387 EFI_STATUS
    388 EFIAPI
    389 GdbSerialWrite (
    390   IN EFI_SERIAL_IO_PROTOCOL  *This,
    391   IN OUT UINTN               *BufferSize,
    392   IN VOID                    *Buffer
    393   )
    394 {
    395   GDB_SERIAL_DEV  *SerialDev;
    396   UINTN            Return;
    397 
    398   SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
    399 
    400   Return = GdbWrite (SerialDev->OutFileDescriptor, Buffer, *BufferSize);
    401   if (Return == (UINTN)-1) {
    402     return EFI_DEVICE_ERROR;
    403   }
    404 
    405   if (Return != *BufferSize) {
    406     *BufferSize = Return;
    407   }
    408 
    409   return EFI_SUCCESS;
    410 }
    411 
    412 /**
    413   Writes data to a serial device.
    414 
    415   @param  This              Protocol instance pointer.
    416   @param  BufferSize        On input, the size of the Buffer. On output, the amount of
    417                             data returned in Buffer.
    418   @param  Buffer            The buffer to return the data into.
    419 
    420   @retval EFI_SUCCESS       The data was read.
    421   @retval EFI_DEVICE_ERROR  The device reported an error.
    422   @retval EFI_TIMEOUT       The data write was stopped due to a timeout.
    423 
    424 **/
    425 
    426 EFI_STATUS
    427 EFIAPI
    428 GdbSerialRead (
    429   IN EFI_SERIAL_IO_PROTOCOL  *This,
    430   IN OUT UINTN               *BufferSize,
    431   OUT VOID                   *Buffer
    432   )
    433 {
    434   GDB_SERIAL_DEV  *SerialDev;
    435   UINTN            Return;
    436 
    437   SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
    438 
    439   Return = GdbRead (SerialDev->InFileDescriptor, Buffer, *BufferSize);
    440   if (Return == (UINTN)-1) {
    441     return EFI_DEVICE_ERROR;
    442   }
    443 
    444   if (Return != *BufferSize) {
    445     *BufferSize = Return;
    446   }
    447 
    448   return EFI_SUCCESS;
    449 }
    450 
    451 
    452 //
    453 // Template used to initailize the GDB Serial IO protocols
    454 //
    455 GDB_SERIAL_DEV gdbSerialDevTemplate = {
    456   GDB_SERIAL_DEV_SIGNATURE,
    457   NULL,
    458 
    459   { // SerialIo
    460     SERIAL_IO_INTERFACE_REVISION,
    461     GdbSerialReset,
    462     GdbSerialSetAttributes,
    463     GdbSerialSetControl,
    464     GdbSerialGetControl,
    465     GdbSerialWrite,
    466     GdbSerialRead,
    467     NULL
    468   },
    469   { // SerialMode
    470     0,      // ControlMask
    471     0,      // Timeout
    472     0,      // BaudRate
    473     1,      // RceiveFifoDepth
    474     0,      // DataBits
    475     0,      // Parity
    476     0       // StopBits
    477   },
    478   {
    479     {
    480       {
    481         HARDWARE_DEVICE_PATH,
    482         HW_VENDOR_DP,
    483         {
    484           (UINT8) (sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)),
    485           (UINT8) ((sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)) >> 8)
    486         },
    487       },
    488       EFI_SERIAL_IO_PROTOCOL_GUID
    489     },
    490     0,
    491     {
    492       END_DEVICE_PATH_TYPE,
    493       END_ENTIRE_DEVICE_PATH_SUBTYPE,
    494       {
    495         (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
    496         (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL) >> 8)
    497       }
    498     },
    499   },
    500   GDB_STDIN,
    501   GDB_STDOUT
    502 };
    503 
    504 
    505 /**
    506   Make two serial consoles: 1) StdIn and StdOut via GDB. 2) StdErr via GDB.
    507 
    508   These console show up on the remote system running GDB
    509 
    510 **/
    511 VOID
    512 GdbInitializeSerialConsole (
    513   VOID
    514   )
    515 {
    516   EFI_STATUS      Status;
    517   GDB_SERIAL_DEV  *StdOutSerialDev;
    518   GDB_SERIAL_DEV  *StdErrSerialDev;
    519 
    520   // Use the template to make a copy of the Serial Console private data structure.
    521   StdOutSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV),  &gdbSerialDevTemplate);
    522   ASSERT (StdOutSerialDev != NULL);
    523 
    524   // Fixup pointer after the copy
    525   StdOutSerialDev->SerialIo.Mode = &StdOutSerialDev->SerialMode;
    526 
    527   StdErrSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV),  &gdbSerialDevTemplate);
    528   ASSERT (StdErrSerialDev != NULL);
    529 
    530   // Fixup pointer and modify stuff that is different for StdError
    531   StdErrSerialDev->SerialIo.Mode = &StdErrSerialDev->SerialMode;
    532   StdErrSerialDev->DevicePath.Index = 1;
    533   StdErrSerialDev->OutFileDescriptor = GDB_STDERR;
    534 
    535   // Make a new handle with Serial IO protocol and its device path on it.
    536   Status = gBS->InstallMultipleProtocolInterfaces (
    537                   &StdOutSerialDev->Handle,
    538                   &gEfiSerialIoProtocolGuid,   &StdOutSerialDev->SerialIo,
    539                   &gEfiDevicePathProtocolGuid, &StdOutSerialDev->DevicePath,
    540                   NULL
    541                   );
    542   ASSERT_EFI_ERROR (Status);
    543 
    544   // Make a new handle with Serial IO protocol and its device path on it.
    545   Status = gBS->InstallMultipleProtocolInterfaces (
    546                   &StdErrSerialDev->Handle,
    547                   &gEfiSerialIoProtocolGuid,   &StdErrSerialDev->SerialIo,
    548                   &gEfiDevicePathProtocolGuid, &StdErrSerialDev->DevicePath,
    549                   NULL
    550                   );
    551   ASSERT_EFI_ERROR (Status);
    552 }
    553 
    554