| /** @file | |
| Serial IO Abstraction for GDB stub. This allows an EFI consoles that shows up on the system | |
| running GDB. One consle for error information and another console for user input/output. | |
| Basic packet format is $packet-data#checksum. So every comand has 4 bytes of overhead: $, | |
| #, 0, 0. The 0 and 0 are the ascii characters for the checksum. | |
| Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <GdbStubInternal.h> | |
| // | |
| // Set TRUE if F Reply package signals a ctrl-c. We can not process the Ctrl-c | |
| // here we need to wait for the periodic callback to do this. | |
| // | |
| BOOLEAN gCtrlCBreakFlag = FALSE; | |
| // | |
| // If the periodic callback is called while we are processing an F packet we need | |
| // to let the callback know to not read from the serail stream as it could steal | |
| // characters from the F reponse packet | |
| // | |
| BOOLEAN gProcessingFPacket = FALSE; | |
| /** | |
| Process a control-C break message. | |
| Currently a place holder, remove the ASSERT when it gets implemented. | |
| @param ErrNo Error infomration from the F reply packet or other source | |
| **/ | |
| VOID | |
| GdbCtrlCBreakMessage ( | |
| IN UINTN ErrNo | |
| ) | |
| { | |
| // See D.10.5 of gdb.pdf | |
| // This should look like a break message. Should look like SIGINT | |
| /* TODO: Make sure if we should do anything with ErrNo */ | |
| //Turn on the global Ctrl-C flag. | |
| gCtrlCBreakFlag = TRUE; | |
| } | |
| /** | |
| Parse the F reply packet and extract the return value and an ErrNo if it exists. | |
| @param Packet Packet to parse like an F reply packet | |
| @param ErrNo Buffer to hold Count bytes that were read | |
| @retval -1 Error, not a valid F reply packet | |
| @retval other Return the return code from the F reply packet | |
| **/ | |
| INTN | |
| GdbParseFReplyPacket ( | |
| IN CHAR8 *Packet, | |
| OUT UINTN *ErrNo | |
| ) | |
| { | |
| INTN RetCode; | |
| if (Packet[0] != 'F') { | |
| // A valid responce would be an F packet | |
| return -1; | |
| } | |
| RetCode = AsciiStrHexToUintn (&Packet[1]); | |
| // Find 1st comma | |
| for (;*Packet != '\0' && *Packet != ','; Packet++); | |
| if (*Packet == '\0') { | |
| *ErrNo = 0; | |
| return RetCode; | |
| } | |
| *ErrNo = AsciiStrHexToUintn (++Packet); | |
| // Find 2nd comma | |
| for (;*Packet != '\0' && *Packet != ','; Packet++); | |
| if (*Packet == '\0') { | |
| return RetCode; | |
| } | |
| if (*(++Packet) == 'C') { | |
| GdbCtrlCBreakMessage (*ErrNo); | |
| } | |
| return RetCode; | |
| } | |
| /** | |
| Read data from a FileDescriptor. On success number of bytes read is returned. Zero indicates | |
| the end of a file. On error -1 is returned. If count is zero, GdbRead returns zero. | |
| @param FileDescriptor Device to talk to. | |
| @param Buffer Buffer to hold Count bytes that were read | |
| @param Count Number of bytes to transfer. | |
| @retval -1 Error | |
| @retval {other} Number of bytes read. | |
| **/ | |
| INTN | |
| GdbRead ( | |
| IN INTN FileDescriptor, | |
| OUT VOID *Buffer, | |
| IN UINTN Count | |
| ) | |
| { | |
| CHAR8 Packet[128]; | |
| UINTN Size; | |
| INTN RetCode; | |
| UINTN ErrNo; | |
| BOOLEAN ReceiveDone = FALSE; | |
| // Send: | |
| // "Fread,XX,YYYYYYYY,XX | |
| // | |
| // XX - FileDescriptor in ASCII | |
| // YYYYYYYY - Buffer address in ASCII | |
| // XX - Count in ASCII | |
| // SS - check sum | |
| // | |
| Size = AsciiSPrint (Packet, sizeof (Packet), "Fread,%x,%x,%x", FileDescriptor, Buffer, Count); | |
| // Packet array is too small if you got this ASSERT | |
| ASSERT (Size < sizeof (Packet)); | |
| gProcessingFPacket = TRUE; | |
| SendPacket (Packet); | |
| Print ((CHAR16 *)L"Packet sent..\n"); | |
| do { | |
| // Reply: | |
| ReceivePacket (Packet, sizeof (Packet)); | |
| Print ((CHAR16 *)L"Command received..%c\n", Packet[0]); | |
| // Process GDB commands | |
| switch (Packet[0]) { | |
| //Write memory command. | |
| //M addr,length:XX... | |
| case 'M': | |
| WriteToMemory (Packet); | |
| break; | |
| //Fretcode, errno, Ctrl-C flag | |
| //retcode - Count read | |
| case 'F': | |
| //Once target receives F reply packet that means the previous | |
| //transactions are finished. | |
| ReceiveDone = TRUE; | |
| break; | |
| //Send empty buffer | |
| default : | |
| SendNotSupported(); | |
| break; | |
| } | |
| } while (ReceiveDone == FALSE); | |
| RetCode = GdbParseFReplyPacket (Packet, &ErrNo); | |
| Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo); | |
| if (ErrNo > 0) { | |
| //Send error to the host if there is any. | |
| SendError ((UINT8)ErrNo); | |
| } | |
| gProcessingFPacket = FALSE; | |
| return RetCode; | |
| } | |
| /** | |
| Write data to a FileDescriptor. On success number of bytes written is returned. Zero indicates | |
| nothing was written. On error -1 is returned. | |
| @param FileDescriptor Device to talk to. | |
| @param Buffer Buffer to hold Count bytes that are to be written | |
| @param Count Number of bytes to transfer. | |
| @retval -1 Error | |
| @retval {other} Number of bytes written. | |
| **/ | |
| INTN | |
| GdbWrite ( | |
| IN INTN FileDescriptor, | |
| OUT CONST VOID *Buffer, | |
| IN UINTN Count | |
| ) | |
| { | |
| CHAR8 Packet[128]; | |
| UINTN Size; | |
| INTN RetCode; | |
| UINTN ErrNo; | |
| BOOLEAN ReceiveDone = FALSE; | |
| // Send: | |
| // #Fwrite,XX,YYYYYYYY,XX$SS | |
| // | |
| // XX - FileDescriptor in ASCII | |
| // YYYYYYYY - Buffer address in ASCII | |
| // XX - Count in ASCII | |
| // SS - check sum | |
| // | |
| Size = AsciiSPrint (Packet, sizeof (Packet), "Fwrite,%x,%x,%x", FileDescriptor, Buffer, Count); | |
| // Packet array is too small if you got this ASSERT | |
| ASSERT (Size < sizeof (Packet)); | |
| SendPacket (Packet); | |
| Print ((CHAR16 *)L"Packet sent..\n"); | |
| do { | |
| // Reply: | |
| ReceivePacket (Packet, sizeof (Packet)); | |
| Print ((CHAR16 *)L"Command received..%c\n", Packet[0]); | |
| // Process GDB commands | |
| switch (Packet[0]) { | |
| //Read memory command. | |
| //m addr,length. | |
| case 'm': | |
| ReadFromMemory (Packet); | |
| break; | |
| //Fretcode, errno, Ctrl-C flag | |
| //retcode - Count read | |
| case 'F': | |
| //Once target receives F reply packet that means the previous | |
| //transactions are finished. | |
| ReceiveDone = TRUE; | |
| break; | |
| //Send empty buffer | |
| default : | |
| SendNotSupported(); | |
| break; | |
| } | |
| } while (ReceiveDone == FALSE); | |
| RetCode = GdbParseFReplyPacket (Packet, &ErrNo); | |
| Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo); | |
| //Send error to the host if there is any. | |
| if (ErrNo > 0) { | |
| SendError((UINT8)ErrNo); | |
| } | |
| return RetCode; | |
| } | |
| /** | |
| Reset the serial device. | |
| @param This Protocol instance pointer. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_DEVICE_ERROR The serial device could not be reset. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GdbSerialReset ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This | |
| ) | |
| { | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Sets the baud rate, receive FIFO depth, transmit/receice time out, parity, | |
| data buts, and stop bits on a serial device. | |
| @param This Protocol instance pointer. | |
| @param BaudRate The requested baud rate. A BaudRate value of 0 will use the the | |
| device's default interface speed. | |
| @param ReveiveFifoDepth The requested depth of the FIFO on the receive side of the | |
| serial interface. A ReceiveFifoDepth value of 0 will use | |
| the device's dfault FIFO depth. | |
| @param Timeout The requested time out for a single character in microseconds. | |
| This timeout applies to both the transmit and receive side of the | |
| interface. A Timeout value of 0 will use the device's default time | |
| out value. | |
| @param Parity The type of parity to use on this serial device. A Parity value of | |
| DefaultParity will use the device's default parity value. | |
| @param DataBits The number of data bits to use on the serial device. A DataBits | |
| vaule of 0 will use the device's default data bit setting. | |
| @param StopBits The number of stop bits to use on this serial device. A StopBits | |
| value of DefaultStopBits will use the device's default number of | |
| stop bits. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_DEVICE_ERROR The serial device could not be reset. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GdbSerialSetAttributes ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN UINT64 BaudRate, | |
| IN UINT32 ReceiveFifoDepth, | |
| IN UINT32 Timeout, | |
| IN EFI_PARITY_TYPE Parity, | |
| IN UINT8 DataBits, | |
| IN EFI_STOP_BITS_TYPE StopBits | |
| ) | |
| { | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Set the control bits on a serial device | |
| @param This Protocol instance pointer. | |
| @param Control Set the bits of Control that are settable. | |
| @retval EFI_SUCCESS The new control bits were set on the serial device. | |
| @retval EFI_UNSUPPORTED The serial device does not support this operation. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GdbSerialSetControl ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN UINT32 Control | |
| ) | |
| { | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Retrieves the status of thecontrol bits on a serial device | |
| @param This Protocol instance pointer. | |
| @param Control A pointer to return the current Control signals from the serial device. | |
| @retval EFI_SUCCESS The control bits were read from the serial device. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GdbSerialGetControl ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| OUT UINT32 *Control | |
| ) | |
| { | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Writes data to a serial device. | |
| @param This Protocol instance pointer. | |
| @param BufferSize On input, the size of the Buffer. On output, the amount of | |
| data actually written. | |
| @param Buffer The buffer of data to write | |
| @retval EFI_SUCCESS The data was written. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_TIMEOUT The data write was stopped due to a timeout. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GdbSerialWrite ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| GDB_SERIAL_DEV *SerialDev; | |
| UINTN Return; | |
| SerialDev = GDB_SERIAL_DEV_FROM_THIS (This); | |
| Return = GdbWrite (SerialDev->OutFileDescriptor, Buffer, *BufferSize); | |
| if (Return == (UINTN)-1) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| if (Return != *BufferSize) { | |
| *BufferSize = Return; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Writes data to a serial device. | |
| @param This Protocol instance pointer. | |
| @param BufferSize On input, the size of the Buffer. On output, the amount of | |
| data returned in Buffer. | |
| @param Buffer The buffer to return the data into. | |
| @retval EFI_SUCCESS The data was read. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_TIMEOUT The data write was stopped due to a timeout. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GdbSerialRead ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| GDB_SERIAL_DEV *SerialDev; | |
| UINTN Return; | |
| SerialDev = GDB_SERIAL_DEV_FROM_THIS (This); | |
| Return = GdbRead (SerialDev->InFileDescriptor, Buffer, *BufferSize); | |
| if (Return == (UINTN)-1) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| if (Return != *BufferSize) { | |
| *BufferSize = Return; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Template used to initailize the GDB Serial IO protocols | |
| // | |
| GDB_SERIAL_DEV gdbSerialDevTemplate = { | |
| GDB_SERIAL_DEV_SIGNATURE, | |
| NULL, | |
| { // SerialIo | |
| SERIAL_IO_INTERFACE_REVISION, | |
| GdbSerialReset, | |
| GdbSerialSetAttributes, | |
| GdbSerialSetControl, | |
| GdbSerialGetControl, | |
| GdbSerialWrite, | |
| GdbSerialRead, | |
| NULL | |
| }, | |
| { // SerialMode | |
| 0, // ControlMask | |
| 0, // Timeout | |
| 0, // BaudRate | |
| 1, // RceiveFifoDepth | |
| 0, // DataBits | |
| 0, // Parity | |
| 0 // StopBits | |
| }, | |
| { | |
| { | |
| { | |
| HARDWARE_DEVICE_PATH, | |
| HW_VENDOR_DP, | |
| { | |
| (UINT8) (sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)), | |
| (UINT8) ((sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)) >> 8) | |
| }, | |
| }, | |
| EFI_SERIAL_IO_PROTOCOL_GUID | |
| }, | |
| 0, | |
| { | |
| END_DEVICE_PATH_TYPE, | |
| END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
| { | |
| (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)), | |
| (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL) >> 8) | |
| } | |
| }, | |
| }, | |
| GDB_STDIN, | |
| GDB_STDOUT | |
| }; | |
| /** | |
| Make two serial consoles: 1) StdIn and StdOut via GDB. 2) StdErr via GDB. | |
| These console show up on the remote system running GDB | |
| **/ | |
| VOID | |
| GdbInitializeSerialConsole ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| GDB_SERIAL_DEV *StdOutSerialDev; | |
| GDB_SERIAL_DEV *StdErrSerialDev; | |
| // Use the template to make a copy of the Serial Console private data structure. | |
| StdOutSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV), &gdbSerialDevTemplate); | |
| ASSERT (StdOutSerialDev != NULL); | |
| // Fixup pointer after the copy | |
| StdOutSerialDev->SerialIo.Mode = &StdOutSerialDev->SerialMode; | |
| StdErrSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV), &gdbSerialDevTemplate); | |
| ASSERT (StdErrSerialDev != NULL); | |
| // Fixup pointer and modify stuff that is different for StdError | |
| StdErrSerialDev->SerialIo.Mode = &StdErrSerialDev->SerialMode; | |
| StdErrSerialDev->DevicePath.Index = 1; | |
| StdErrSerialDev->OutFileDescriptor = GDB_STDERR; | |
| // Make a new handle with Serial IO protocol and its device path on it. | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &StdOutSerialDev->Handle, | |
| &gEfiSerialIoProtocolGuid, &StdOutSerialDev->SerialIo, | |
| &gEfiDevicePathProtocolGuid, &StdOutSerialDev->DevicePath, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // Make a new handle with Serial IO protocol and its device path on it. | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &StdErrSerialDev->Handle, | |
| &gEfiSerialIoProtocolGuid, &StdErrSerialDev->SerialIo, | |
| &gEfiDevicePathProtocolGuid, &StdErrSerialDev->DevicePath, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |