-/** @file
- UEFI driver that implements a GDB stub
-
- Note: Any code in the path of the Serial IO output can not call DEBUG as will
- will blow out the stack. Serial IO calls DEBUG, debug calls Serail IO, ...
-
-
- 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>
-#include <Protocol/DebugPort.h>
-
-
-UINTN gMaxProcessorIndex = 0;
-
-//
-// Buffers for basic gdb communication
-//
-CHAR8 gInBuffer[MAX_BUF_SIZE];
-CHAR8 gOutBuffer[MAX_BUF_SIZE];
-
-// Assume gdb does a "qXfer:libraries:read::offset,length" when it connects so we can default
-// this value to FALSE. Since gdb can reconnect its self a global default is not good enough
-BOOLEAN gSymbolTableUpdate = FALSE;
-EFI_EVENT gEvent;
-VOID *gGdbSymbolEventHandlerRegistration = NULL;
-
-//
-// Globals for returning XML from qXfer:libraries:read packet
-//
-UINTN gPacketqXferLibraryOffset = 0;
-UINTN gEfiDebugImageTableEntry = 0;
-EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *gDebugImageTableHeader = NULL;
-EFI_DEBUG_IMAGE_INFO *gDebugTable = NULL;
-CHAR8 gXferLibraryBuffer[2000];
-
-GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mHexToStr[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
-
-
-VOID
-EFIAPI
-GdbSymbolEventHandler (
- IN EFI_EVENT Event,
- IN VOID *Context
- )
-{
-}
-
-
-/**
- The user Entry Point for Application. The user code starts with this function
- as the real entry point for the image goes into a library that calls this
- function.
-
- @param[in] ImageHandle The firmware allocated handle for the EFI image.
- @param[in] SystemTable A pointer to the EFI System Table.
-
- @retval EFI_SUCCESS The entry point is executed successfully.
- @retval other Some error occurs when executing this entry point.
-
-**/
-EFI_STATUS
-EFIAPI
-GdbStubEntry (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-
-{
- EFI_STATUS Status;
- EFI_DEBUG_SUPPORT_PROTOCOL *DebugSupport;
- UINTN HandleCount;
- EFI_HANDLE *Handles;
- UINTN Index;
- UINTN Processor;
- BOOLEAN IsaSupported;
-
-
- Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&gDebugImageTableHeader);
- if (EFI_ERROR (Status)) {
- gDebugImageTableHeader = NULL;
- }
-
- Status = gBS->LocateHandleBuffer (
- ByProtocol,
- &gEfiDebugSupportProtocolGuid,
- NULL,
- &HandleCount,
- &Handles
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((EFI_D_ERROR, "Debug Support Protocol not found\n"));
-
- return Status;
- }
-
- DebugSupport = NULL;
- IsaSupported = FALSE;
- do {
- HandleCount--;
- Status = gBS->HandleProtocol (
- Handles[HandleCount],
- &gEfiDebugSupportProtocolGuid,
- (VOID **) &DebugSupport
- );
- if (!EFI_ERROR (Status)) {
- if (CheckIsa (DebugSupport->Isa)) {
- // We found what we are looking for so break out of the loop
- IsaSupported = TRUE;
- break;
- }
- }
- } while (HandleCount > 0);
- FreePool (Handles);
-
- if (!IsaSupported) {
- DEBUG ((EFI_D_ERROR, "Debug Support Protocol does not support our ISA\n"));
-
- return EFI_NOT_FOUND;
- }
-
- Status = DebugSupport->GetMaximumProcessorIndex (DebugSupport, &gMaxProcessorIndex);
- ASSERT_EFI_ERROR (Status);
-
- // Make this an EFI_D_INFO after we get everything debugged.
- DEBUG ((EFI_D_ERROR, "Debug Support Protocol ISA %x\n", DebugSupport->Isa));
- DEBUG ((EFI_D_ERROR, "Debug Support Protocol Processor Index %d\n", gMaxProcessorIndex));
-
- // Call processor-specific init routine
- InitializeProcessor();
-
- for (Processor = 0; Processor <= gMaxProcessorIndex; Processor++) {
-
- for (Index = 0; Index < MaxEfiException (); Index++) {
- Status = DebugSupport->RegisterExceptionCallback (DebugSupport, Processor, GdbExceptionHandler, gExceptionType[Index].Exception);
- ASSERT_EFI_ERROR (Status);
- }
- //
- // Current edk2 DebugPort is not interrupt context safe so we can not use it
- //
- Status = DebugSupport->RegisterPeriodicCallback (DebugSupport, Processor, GdbPeriodicCallBack);
- ASSERT_EFI_ERROR (Status);
- }
-
- //
- // This even fires every time an image is added. This allows the stub to know when gdb needs
- // to update the symbol table.
- //
- Status = gBS->CreateEvent (
- EVT_NOTIFY_SIGNAL,
- TPL_CALLBACK,
- GdbSymbolEventHandler,
- NULL,
- &gEvent
- );
- ASSERT_EFI_ERROR (Status);
-
- //
- // Register for protocol notifactions on this event
- //
- Status = gBS->RegisterProtocolNotify (
- &gEfiLoadedImageProtocolGuid,
- gEvent,
- &gGdbSymbolEventHandlerRegistration
- );
- ASSERT_EFI_ERROR (Status);
-
-
- if (PcdGetBool (PcdGdbSerial)) {
- GdbInitializeSerialConsole ();
- }
-
- return EFI_SUCCESS;
-}
-
-
-
-/**
- Transfer length bytes of input buffer, starting at Address, to memory.
-
- @param length the number of the bytes to be transferred/written
- @param *address the start address of the transferring/writing the memory
- @param *new_data the new data to be written to memory
- **/
-
-VOID
-TransferFromInBufToMem (
- IN UINTN Length,
- IN unsigned char *Address,
- IN CHAR8 *NewData
- )
-{
- CHAR8 c1;
- CHAR8 c2;
-
- while (Length-- > 0) {
- c1 = (CHAR8)HexCharToInt (*NewData++);
- c2 = (CHAR8)HexCharToInt (*NewData++);
-
- if ((c1 < 0) || (c2 < 0)) {
- Print ((CHAR16 *)L"Bad message from write to memory..\n");
- SendError (GDB_EBADMEMDATA);
- return;
- }
- *Address++ = (UINT8)((c1 << 4) + c2);
- }
-
- SendSuccess();
-}
-
-
-/**
- Transfer Length bytes of memory starting at Address to an output buffer, OutBuffer. This function will finally send the buffer
- as a packet.
-
- @param Length the number of the bytes to be transferred/read
- @param *address pointer to the start address of the transferring/reading the memory
- **/
-
-VOID
-TransferFromMemToOutBufAndSend (
- IN UINTN Length,
- IN unsigned char *Address
- )
-{
- // there are Length bytes and every byte is represented as 2 hex chars
- CHAR8 OutBuffer[MAX_BUF_SIZE];
- CHAR8 *OutBufPtr; // pointer to the output buffer
- CHAR8 Char;
-
- if (ValidateAddress(Address) == FALSE) {
- SendError(14);
- return;
- }
-
- OutBufPtr = OutBuffer;
- while (Length > 0) {
-
- Char = mHexToStr[*Address >> 4];
- if ((Char >= 'A') && (Char <= 'F')) {
- Char = Char - 'A' + 'a';
- }
- *OutBufPtr++ = Char;
-
- Char = mHexToStr[*Address & 0x0f];
- if ((Char >= 'A') && (Char <= 'F')) {
- Char = Char - 'A' + 'a';
- }
- *OutBufPtr++ = Char;
-
- Address++;
- Length--;
- }
-
- *OutBufPtr = '\0' ; // the end of the buffer
- SendPacket (OutBuffer);
-}
-
-
-
-/**
- Send a GDB Remote Serial Protocol Packet
-
- $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$',
- the packet teminating character '#' and the two digit checksum.
-
- If an ack '+' is not sent resend the packet, but timeout eventually so we don't end up
- in an infinit loop. This is so if you unplug the debugger code just keeps running
-
- @param PacketData Payload data for the packet
-
-
- @retval Number of bytes of packet data sent.
-
-**/
-UINTN
-SendPacket (
- IN CHAR8 *PacketData
- )
-{
- UINT8 CheckSum;
- UINTN Timeout;
- CHAR8 *Ptr;
- CHAR8 TestChar;
- UINTN Count;
-
- Timeout = PcdGet32 (PcdGdbMaxPacketRetryCount);
-
- Count = 0;
- do {
-
- Ptr = PacketData;
-
- if (Timeout-- == 0) {
- // Only try a finite number of times so we don't get stuck in the loop
- return Count;
- }
-
- // Packet prefix
- GdbPutChar ('$');
-
- for (CheckSum = 0, Count =0 ; *Ptr != '\0'; Ptr++, Count++) {
- GdbPutChar (*Ptr);
- CheckSum = CheckSum + *Ptr;
- }
-
- // Packet terminating character and checksum
- GdbPutChar ('#');
- GdbPutChar (mHexToStr[CheckSum >> 4]);
- GdbPutChar (mHexToStr[CheckSum & 0x0F]);
-
- TestChar = GdbGetChar ();
- } while (TestChar != '+');
-
- return Count;
-}
-
-/**
- Receive a GDB Remote Serial Protocol Packet
-
- $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$',
- the packet teminating character '#' and the two digit checksum.
-
- If host re-starts sending a packet without ending the previous packet, only the last valid packet is proccessed.
- (In other words, if received packet is '$12345$12345$123456#checksum', only '$123456#checksum' will be processed.)
-
- If an ack '+' is not sent resend the packet
-
- @param PacketData Payload data for the packet
-
- @retval Number of bytes of packet data received.
-
-**/
-UINTN
-ReceivePacket (
- OUT CHAR8 *PacketData,
- IN UINTN PacketDataSize
- )
-{
- UINT8 CheckSum;
- UINTN Index;
- CHAR8 Char;
- CHAR8 SumString[3];
- CHAR8 TestChar;
-
- ZeroMem (PacketData, PacketDataSize);
-
- for (;;) {
- // wait for the start of a packet
- TestChar = GdbGetChar ();
- while (TestChar != '$') {
- TestChar = GdbGetChar ();
- };
-
- retry:
- for (Index = 0, CheckSum = 0; Index < (PacketDataSize - 1); Index++) {
- Char = GdbGetChar ();
- if (Char == '$') {
- goto retry;
- }
- if (Char == '#') {
- break;
- }
-
- PacketData[Index] = Char;
- CheckSum = CheckSum + Char;
- }
- PacketData[Index] = '\0';
-
- if (Index == PacketDataSize) {
- continue;
- }
-
- SumString[0] = GdbGetChar ();
- SumString[1] = GdbGetChar ();
- SumString[2] = '\0';
-
- if (AsciiStrHexToUintn (SumString) == CheckSum) {
- // Ack: Success
- GdbPutChar ('+');
-
- // Null terminate the callers string
- PacketData[Index] = '\0';
- return Index;
- } else {
- // Ack: Failure
- GdbPutChar ('-');
- }
- }
-
- //return 0;
-}
-
-
-/**
- Empties the given buffer
- @param Buf pointer to the first element in buffer to be emptied
- **/
-VOID
-EmptyBuffer (
- IN CHAR8 *Buf
- )
-{
- *Buf = '\0';
-}
-
-
-/**
- Converts an 8-bit Hex Char into a INTN.
-
- @param Char the hex character to be converted into UINTN
- @retval a INTN, from 0 to 15, that corressponds to Char
- -1 if Char is not a hex character
- **/
-INTN
-HexCharToInt (
- IN CHAR8 Char
- )
-{
- if ((Char >= 'A') && (Char <= 'F')) {
- return Char - 'A' + 10;
- } else if ((Char >= 'a') && (Char <= 'f')) {
- return Char - 'a' + 10;
- } else if ((Char >= '0') && (Char <= '9')) {
- return Char - '0';
- } else { // if not a hex value, return a negative value
- return -1;
- }
-}
-
- // 'E' + the biggest error number is 255, so its 2 hex digits + buffer end
-CHAR8 *gError = "E__";
-
-/** 'E NN'
- Send an error with the given error number after converting to hex.
- The error number is put into the buffer in hex. '255' is the biggest errno we can send.
- ex: 162 will be sent as A2.
-
- @param errno the error number that will be sent
- **/
-VOID
-EFIAPI
-SendError (
- IN UINT8 ErrorNum
- )
-{
- //
- // Replace _, or old data, with current errno
- //
- gError[1] = mHexToStr [ErrorNum >> 4];
- gError[2] = mHexToStr [ErrorNum & 0x0f];
-
- SendPacket (gError); // send buffer
-}
-
-
-
-/**
- Send 'OK' when the function is done executing successfully.
- **/
-VOID
-EFIAPI
-SendSuccess (
- VOID
- )
-{
- SendPacket ("OK"); // send buffer
-}
-
-
-/**
- Send empty packet to specify that particular command/functionality is not supported.
- **/
-VOID
-EFIAPI
-SendNotSupported (
- VOID
- )
-{
- SendPacket ("");
-}
-
-
-
-/**
- Send the T signal with the given exception type (in gdb order) and possibly with n:r pairs related to the watchpoints
-
- @param SystemContext Register content at time of the exception
- @param GdbExceptionType GDB exception type
- **/
-VOID
-GdbSendTSignal (
- IN EFI_SYSTEM_CONTEXT SystemContext,
- IN UINT8 GdbExceptionType
- )
-{
- CHAR8 TSignalBuffer[128];
- CHAR8 *TSignalPtr;
- UINTN BreakpointDetected;
- BREAK_TYPE BreakType;
- UINTN DataAddress;
- CHAR8 *WatchStrPtr = NULL;
- UINTN RegSize;
-
- TSignalPtr = &TSignalBuffer[0];
-
- //Construct TSignal packet
- *TSignalPtr++ = 'T';
-
- //
- // replace _, or previous value, with Exception type
- //
- *TSignalPtr++ = mHexToStr [GdbExceptionType >> 4];
- *TSignalPtr++ = mHexToStr [GdbExceptionType & 0x0f];
-
- if (GdbExceptionType == GDB_SIGTRAP) {
- if (gSymbolTableUpdate) {
- //
- // We can only send back on reason code. So if the flag is set it means the breakpoint is from our event handler
- //
- WatchStrPtr = "library:;";
- while (*WatchStrPtr != '\0') {
- *TSignalPtr++ = *WatchStrPtr++;
- }
- gSymbolTableUpdate = FALSE;
- } else {
-
-
- //
- // possible n:r pairs
- //
-
- //Retrieve the breakpoint number
- BreakpointDetected = GetBreakpointDetected (SystemContext);
-
- //Figure out if the exception is happend due to watch, rwatch or awatch.
- BreakType = GetBreakpointType (SystemContext, BreakpointDetected);
-
- //INFO: rwatch is not supported due to the way IA32 debug registers work
- if ((BreakType == DataWrite) || (BreakType == DataRead) || (BreakType == DataReadWrite)) {
-
- //Construct n:r pair
- DataAddress = GetBreakpointDataAddress (SystemContext, BreakpointDetected);
-
- //Assign appropriate buffer to print particular watchpoint type
- if (BreakType == DataWrite) {
- WatchStrPtr = "watch";
- } else if (BreakType == DataRead) {
- WatchStrPtr = "rwatch";
- } else if (BreakType == DataReadWrite) {
- WatchStrPtr = "awatch";
- }
-
- while (*WatchStrPtr != '\0') {
- *TSignalPtr++ = *WatchStrPtr++;
- }
-
- *TSignalPtr++ = ':';
-
- //Set up series of bytes in big-endian byte order. "awatch" won't work with little-endian byte order.
- RegSize = REG_SIZE;
- while (RegSize > 0) {
- RegSize = RegSize-4;
- *TSignalPtr++ = mHexToStr[(UINT8)(DataAddress >> RegSize) & 0xf];
- }
-
- //Always end n:r pair with ';'
- *TSignalPtr++ = ';';
- }
- }
- }
-
- *TSignalPtr = '\0';
-
- SendPacket (TSignalBuffer);
-}
-
-
-/**
- Translates the EFI mapping to GDB mapping
-
- @param EFIExceptionType EFI Exception that is being processed
- @retval UINTN that corresponds to EFIExceptionType's GDB exception type number
- **/
-UINT8
-ConvertEFItoGDBtype (
- IN EFI_EXCEPTION_TYPE EFIExceptionType
- )
-{
- UINTN i;
-
- for (i=0; i < MaxEfiException() ; i++) {
- if (gExceptionType[i].Exception == EFIExceptionType) {
- return gExceptionType[i].SignalNo;
- }
- }
- return GDB_SIGTRAP; // this is a GDB trap
-}
-
-
-/** "m addr,length"
- Find the Length of the area to read and the start addres. Finally, pass them to
- another function, TransferFromMemToOutBufAndSend, that will read from that memory space and
- send it as a packet.
- **/
-
-VOID
-EFIAPI
-ReadFromMemory (
- CHAR8 *PacketData
- )
-{
- UINTN Address;
- UINTN Length;
- CHAR8 AddressBuffer[MAX_ADDR_SIZE]; // the buffer that will hold the address in hex chars
- CHAR8 *AddrBufPtr; // pointer to the address buffer
- CHAR8 *InBufPtr; /// pointer to the input buffer
-
- AddrBufPtr = AddressBuffer;
- InBufPtr = &PacketData[1];
- while (*InBufPtr != ',') {
- *AddrBufPtr++ = *InBufPtr++;
- }
- *AddrBufPtr = '\0';
-
- InBufPtr++; // this skips ',' in the buffer
-
- /* Error checking */
- if (AsciiStrLen(AddressBuffer) >= MAX_ADDR_SIZE) {
- Print((CHAR16 *)L"Address is too long\n");
- SendError (GDB_EBADMEMADDRBUFSIZE);
- return;
- }
-
- // 2 = 'm' + ','
- if (AsciiStrLen(PacketData) - AsciiStrLen(AddressBuffer) - 2 >= MAX_LENGTH_SIZE) {
- Print((CHAR16 *)L"Length is too long\n");
- SendError (GDB_EBADMEMLENGTH);
- return;
- }
-
- Address = AsciiStrHexToUintn (AddressBuffer);
- Length = AsciiStrHexToUintn (InBufPtr);
-
- TransferFromMemToOutBufAndSend (Length, (unsigned char *)Address);
-}
-
-
-/** "M addr,length :XX..."
- Find the Length of the area in bytes to write and the start addres. Finally, pass them to
- another function, TransferFromInBufToMem, that will write to that memory space the info in
- the input buffer.
- **/
-VOID
-EFIAPI
-WriteToMemory (
- IN CHAR8 *PacketData
- )
-{
- UINTN Address;
- UINTN Length;
- UINTN MessageLength;
- CHAR8 AddressBuffer[MAX_ADDR_SIZE]; // the buffer that will hold the Address in hex chars
- CHAR8 LengthBuffer[MAX_LENGTH_SIZE]; // the buffer that will hold the Length in hex chars
- CHAR8 *AddrBufPtr; // pointer to the Address buffer
- CHAR8 *LengthBufPtr; // pointer to the Length buffer
- CHAR8 *InBufPtr; /// pointer to the input buffer
-
- AddrBufPtr = AddressBuffer;
- LengthBufPtr = LengthBuffer;
- InBufPtr = &PacketData[1];
-
- while (*InBufPtr != ',') {
- *AddrBufPtr++ = *InBufPtr++;
- }
- *AddrBufPtr = '\0';
-
- InBufPtr++; // this skips ',' in the buffer
-
- while (*InBufPtr != ':') {
- *LengthBufPtr++ = *InBufPtr++;
- }
- *LengthBufPtr = '\0';
-
- InBufPtr++; // this skips ':' in the buffer
-
- Address = AsciiStrHexToUintn (AddressBuffer);
- Length = AsciiStrHexToUintn (LengthBuffer);
-
- /* Error checking */
-
- //Check if Address is not too long.
- if (AsciiStrLen(AddressBuffer) >= MAX_ADDR_SIZE) {
- Print ((CHAR16 *)L"Address too long..\n");
- SendError (GDB_EBADMEMADDRBUFSIZE);
- return;
- }
-
- //Check if message length is not too long
- if (AsciiStrLen(LengthBuffer) >= MAX_LENGTH_SIZE) {
- Print ((CHAR16 *)L"Length too long..\n");
- SendError (GDB_EBADMEMLENGBUFSIZE);
- return;
- }
-
- // Check if Message is not too long/short.
- // 3 = 'M' + ',' + ':'
- MessageLength = (AsciiStrLen(PacketData) - AsciiStrLen(AddressBuffer) - AsciiStrLen(LengthBuffer) - 3);
- if (MessageLength != (2*Length)) {
- //Message too long/short. New data is not the right size.
- SendError (GDB_EBADMEMDATASIZE);
- return;
- }
- TransferFromInBufToMem (Length, (unsigned char *)Address, InBufPtr);
-}
-
-/**
- Parses breakpoint packet data and captures Breakpoint type, Address and length.
- In case of an error, function returns particular error code. Returning 0 meaning
- no error.
-
- @param PacketData Pointer to the payload data for the packet.
- @param Type Breakpoint type
- @param Address Breakpoint address
- @param Length Breakpoint length in Bytes (1 byte, 2 byte, 4 byte)
-
- @retval 1 Success
- @retval {other} Particular error code
-
-**/
-UINTN
-ParseBreakpointPacket (
- IN CHAR8 *PacketData,
- OUT UINTN *Type,
- OUT UINTN *Address,
- OUT UINTN *Length
- )
-{
- CHAR8 AddressBuffer[MAX_ADDR_SIZE];
- CHAR8 *AddressBufferPtr;
- CHAR8 *PacketDataPtr;
-
- PacketDataPtr = &PacketData[1];
- AddressBufferPtr = AddressBuffer;
-
- *Type = AsciiStrHexToUintn (PacketDataPtr);
-
- //Breakpoint/watchpoint type should be between 0 to 4
- if (*Type > 4) {
- Print ((CHAR16 *)L"Type is invalid\n");
- return 22; //EINVAL: Invalid argument.
- }
-
- //Skip ',' in the buffer.
- while (*PacketDataPtr++ != ',');
-
- //Parse Address information
- while (*PacketDataPtr != ',') {
- *AddressBufferPtr++ = *PacketDataPtr++;
- }
- *AddressBufferPtr = '\0';
-
- //Check if Address is not too long.
- if (AsciiStrLen(AddressBuffer) >= MAX_ADDR_SIZE) {
- Print ((CHAR16 *)L"Address too long..\n");
- return 40; //EMSGSIZE: Message size too long.
- }
-
- *Address = AsciiStrHexToUintn (AddressBuffer);
-
- PacketDataPtr++; //This skips , in the buffer
-
- //Parse Length information
- *Length = AsciiStrHexToUintn (PacketDataPtr);
-
- //Length should be 1, 2 or 4 bytes
- if (*Length > 4) {
- Print ((CHAR16 *)L"Length is invalid\n");
- return 22; //EINVAL: Invalid argument
- }
-
- return 0; //0 = No error
-}
-
-UINTN
-gXferObjectReadResponse (
- IN CHAR8 Type,
- IN CHAR8 *Str
- )
-{
- CHAR8 *OutBufPtr; // pointer to the output buffer
- CHAR8 Char;
- UINTN Count;
-
- // responce starts with 'm' or 'l' if it is the end
- OutBufPtr = gOutBuffer;
- *OutBufPtr++ = Type;
- Count = 1;
-
- // Binary data encoding
- OutBufPtr = gOutBuffer;
- while (*Str != '\0') {
- Char = *Str++;
- if ((Char == 0x7d) || (Char == 0x23) || (Char == 0x24) || (Char == 0x2a)) {
- // escape character
- *OutBufPtr++ = 0x7d;
-
- Char ^= 0x20;
- }
- *OutBufPtr++ = Char;
- Count++;
- }
-
- *OutBufPtr = '\0' ; // the end of the buffer
- SendPacket (gOutBuffer);
-
- return Count;
-}
-
-
-/**
- Note: This should be a library function. In the Apple case you have to add
- the size of the PE/COFF header into the starting address to make things work
- right as there is no way to pad the Mach-O for the size of the PE/COFF header.
-
-
- Returns a pointer to the PDB file name for a PE/COFF image that has been
- loaded into system memory with the PE/COFF Loader Library functions.
-
- Returns the PDB file name for the PE/COFF image specified by Pe32Data. If
- the PE/COFF image specified by Pe32Data is not a valid, then NULL is
- returned. If the PE/COFF image specified by Pe32Data does not contain a
- debug directory entry, then NULL is returned. If the debug directory entry
- in the PE/COFF image specified by Pe32Data does not contain a PDB file name,
- then NULL is returned.
- If Pe32Data is NULL, then ASSERT().
-
- @param Pe32Data Pointer to the PE/COFF image that is loaded in system
- memory.
- @param DebugBase Address that the debugger would use as the base of the image
-
- @return The PDB file name for the PE/COFF image specified by Pe32Data or NULL
- if it cannot be retrieved. DebugBase is only valid if PDB file name is
- valid.
-
-**/
-VOID *
-EFIAPI
-PeCoffLoaderGetDebuggerInfo (
- IN VOID *Pe32Data,
- OUT VOID **DebugBase
- )
-{
- EFI_IMAGE_DOS_HEADER *DosHdr;
- EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
- EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
- EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;
- UINTN DirCount;
- VOID *CodeViewEntryPointer;
- INTN TEImageAdjust;
- UINT32 NumberOfRvaAndSizes;
- UINT16 Magic;
- UINTN SizeOfHeaders;
-
- ASSERT (Pe32Data != NULL);
-
- TEImageAdjust = 0;
- DirectoryEntry = NULL;
- DebugEntry = NULL;
- NumberOfRvaAndSizes = 0;
- SizeOfHeaders = 0;
-
- DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
- if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
- //
- // DOS image header is present, so read the PE header after the DOS image header.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
- } else {
- //
- // DOS image header is not present, so PE header is at the image base.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
- }
-
- if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
- if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) {
- DirectoryEntry = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG];
- TEImageAdjust = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize;
- DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te +
- Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress +
- TEImageAdjust);
- }
- SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize;
-
- // __APPLE__ check this math...
- *DebugBase = ((CHAR8 *)Pe32Data) - TEImageAdjust;
- } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
-
- *DebugBase = Pe32Data;
-
-
- //
- // NOTE: We use Machine field to identify PE32/PE32+, instead of Magic.
- // It is due to backward-compatibility, for some system might
- // generate PE32+ image with PE32 Magic.
- //
- switch (Hdr.Pe32->FileHeader.Machine) {
- case EFI_IMAGE_MACHINE_IA32:
- //
- // Assume PE32 image with IA32 Machine field.
- //
- Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
- break;
- case EFI_IMAGE_MACHINE_X64:
- case EFI_IMAGE_MACHINE_IA64:
- //
- // Assume PE32+ image with X64 or IPF Machine field
- //
- Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
- break;
- default:
- //
- // For unknow Machine field, use Magic in optional Header
- //
- Magic = Hdr.Pe32->OptionalHeader.Magic;
- }
-
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
- //
- // Use PE32 offset get Debug Directory Entry
- //
- SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
- NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
- DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
- DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);
- } else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
- //
- // Use PE32+ offset get Debug Directory Entry
- //
- SizeOfHeaders = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders;
- NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
- DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
- DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);
- }
-
- if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
- DirectoryEntry = NULL;
- DebugEntry = NULL;
- }
- } else {
- return NULL;
- }
-
- if (DebugEntry == NULL || DirectoryEntry == NULL) {
- return NULL;
- }
-
- for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) {
- if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
- if (DebugEntry->SizeOfData > 0) {
- CodeViewEntryPointer = (VOID *) ((UINTN) DebugEntry->RVA + ((UINTN)Pe32Data) + (UINTN)TEImageAdjust);
- switch (* (UINT32 *) CodeViewEntryPointer) {
- case CODEVIEW_SIGNATURE_NB10:
- return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));
- case CODEVIEW_SIGNATURE_RSDS:
- return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));
- case CODEVIEW_SIGNATURE_MTOC:
- *DebugBase = (VOID *)(UINTN)((UINTN)DebugBase - SizeOfHeaders);
- return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));
- default:
- break;
- }
- }
- }
- }
-
- (void)SizeOfHeaders;
- return NULL;
-}
-
-
-
-/**
- Process "qXfer:object:read:annex:offset,length" request.
-
- Returns an XML document that contains loaded libraries. In our case it is
- infomration in the EFI Debug Inmage Table converted into an XML document.
-
- GDB will call with an arbitrary length (it can't know the real length and
- will reply with chunks of XML that are easy for us to deal with. Gdb will
- keep calling until we say we are done. XML doc looks like:
-
- <library-list>
- <library name="/a/a/c/d.dSYM"><segment address="0x10000000"/></library>
- <library name="/a/m/e/e.pdb"><segment address="0x20000000"/></library>
- <library name="/a/l/f/f.dll"><segment address="0x30000000"/></library>
- </library-list>
-
- Since we can not allocate memory in interupt context this module has
- assumptions about how it will get called:
- 1) Length will generally be max remote packet size (big enough)
- 2) First Offset of an XML document read needs to be 0
- 3) This code will return back small chunks of the XML document on every read.
- Each subseqent call will ask for the next availble part of the document.
-
- Note: The only variable size element in the XML is:
- " <library name=\"%s\"><segment address=\"%p\"/></library>\n" and it is
- based on the file path and name of the symbol file. If the symbol file name
- is bigger than the max gdb remote packet size we could update this code
- to respond back in chunks.
-
- @param Offset offset into special data area
- @param Length number of bytes to read starting at Offset
-
- **/
-VOID
-QxferLibrary (
- IN UINTN Offset,
- IN UINTN Length
- )
-{
- VOID *LoadAddress;
- CHAR8 *Pdb;
- UINTN Size;
-
- if (Offset != gPacketqXferLibraryOffset) {
- SendError (GDB_EINVALIDARG);
- Print (L"\nqXferLibrary (%d, %d) != %d\n", Offset, Length, gPacketqXferLibraryOffset);
-
- // Force a retry from the beginning
- gPacketqXferLibraryOffset = 0;
- return;
- }
-
- if (Offset == 0) {
- gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', "<library-list>\n");
-
- // The owner of the table may have had to ralloc it so grab a fresh copy every time
- // we assume qXferLibrary will get called over and over again until the entire XML table is
- // returned in a tight loop. Since we are in the debugger the table should not get updated
- gDebugTable = gDebugImageTableHeader->EfiDebugImageInfoTable;
- gEfiDebugImageTableEntry = 0;
- return;
- }
-
- if (gDebugTable != NULL) {
- for (; gEfiDebugImageTableEntry < gDebugImageTableHeader->TableSize; gEfiDebugImageTableEntry++, gDebugTable++) {
- if (gDebugTable->NormalImage != NULL) {
- if ((gDebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
- (gDebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
- Pdb = PeCoffLoaderGetDebuggerInfo (
- gDebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase,
- &LoadAddress
- );
- if (Pdb != NULL) {
- Size = AsciiSPrint (
- gXferLibraryBuffer,
- sizeof (gXferLibraryBuffer),
- " <library name=\"%a\"><segment address=\"0x%p\"/></library>\n",
- Pdb,
- LoadAddress
- );
- if ((Size != 0) && (Size != (sizeof (gXferLibraryBuffer) - 1))) {
- gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', gXferLibraryBuffer);
-
- // Update loop variables so we are in the right place when we get back
- gEfiDebugImageTableEntry++;
- gDebugTable++;
- return;
- } else {
- // We could handle <library> entires larger than sizeof (gXferLibraryBuffer) here if
- // needed by breaking up into N packets
- // "<library name=\"%s
- // the rest of the string (as many packets as required
- // \"><segment address=\"%d\"/></library> (fixed size)
- //
- // But right now we just skip any entry that is too big
- }
- }
- }
- }
- }
- }
-
-
- gXferObjectReadResponse ('l', "</library-list>\n");
- gPacketqXferLibraryOffset = 0;
- return;
-}
-
-
-/**
- Exception Hanldler for GDB. It will be called for all exceptions
- registered via the gExceptionType[] array.
-
- @param ExceptionType Exception that is being processed
- @param SystemContext Register content at time of the exception
- **/
-VOID
-EFIAPI
-GdbExceptionHandler (
- IN EFI_EXCEPTION_TYPE ExceptionType,
- IN OUT EFI_SYSTEM_CONTEXT SystemContext
- )
-{
- UINT8 GdbExceptionType;
- CHAR8 *Ptr;
-
-
- if (ValidateException(ExceptionType, SystemContext) == FALSE) {
- return;
- }
-
- RemoveSingleStep (SystemContext);
-
- GdbExceptionType = ConvertEFItoGDBtype (ExceptionType);
- GdbSendTSignal (SystemContext, GdbExceptionType);
-
- for( ; ; ) {
- ReceivePacket (gInBuffer, MAX_BUF_SIZE);
-
- switch (gInBuffer[0]) {
- case '?':
- GdbSendTSignal (SystemContext, GdbExceptionType);
- break;
-
- case 'c':
- ContinueAtAddress (SystemContext, gInBuffer);
- return;
-
- case 'g':
- ReadGeneralRegisters (SystemContext);
- break;
-
- case 'G':
- WriteGeneralRegisters (SystemContext, gInBuffer);
- break;
-
- case 'H':
- //Return "OK" packet since we don't have more than one thread.
- SendSuccess ();
- break;
-
- case 'm':
- ReadFromMemory (gInBuffer);
- break;
-
- case 'M':
- WriteToMemory (gInBuffer);
- break;
-
- case 'P':
- WriteNthRegister (SystemContext, gInBuffer);
- break;
-
- //
- // Still debugging this code. Not used in Darwin
- //
- case 'q':
- // General Query Packets
- if (AsciiStrnCmp (gInBuffer, "qSupported", 10) == 0) {
- // return what we currently support, we don't parse what gdb suports
- AsciiSPrint (gOutBuffer, MAX_BUF_SIZE, "qXfer:libraries:read+;PacketSize=%d", MAX_BUF_SIZE);
- SendPacket (gOutBuffer);
- } else if (AsciiStrnCmp (gInBuffer, "qXfer:libraries:read::", 22) == 0) {
- // ‘qXfer:libraries:read::offset,length
- // gInBuffer[22] is offset string, ++Ptr is length string’
- for (Ptr = &gInBuffer[22]; *Ptr != ','; Ptr++);
-
- // Not sure if multi-radix support is required. Currently only support decimal
- QxferLibrary (AsciiStrHexToUintn (&gInBuffer[22]), AsciiStrHexToUintn (++Ptr));
- } if (AsciiStrnCmp (gInBuffer, "qOffsets", 10) == 0) {
- AsciiSPrint (gOutBuffer, MAX_BUF_SIZE, "Text=1000;Data=f000;Bss=f000");
- SendPacket (gOutBuffer);
- } else {
- //Send empty packet
- SendNotSupported ();
- }
- break;
-
- case 's':
- SingleStep (SystemContext, gInBuffer);
- return;
-
- case 'z':
- RemoveBreakPoint (SystemContext, gInBuffer);
- break;
-
- case 'Z':
- InsertBreakPoint (SystemContext, gInBuffer);
- break;
-
- default:
- //Send empty packet
- SendNotSupported ();
- break;
- }
- }
-}
-
-
-/**
- Periodic callback for GDB. This function is used to catch a ctrl-c or other
- break in type command from GDB.
-
- @param SystemContext Register content at time of the call
- **/
-VOID
-EFIAPI
-GdbPeriodicCallBack (
- IN OUT EFI_SYSTEM_CONTEXT SystemContext
- )
-{
- //
- // gCtrlCBreakFlag may have been set from a previous F response package
- // and we set the global as we need to process it at a point where we
- // can update the system context. If we are in the middle of processing
- // a F Packet it is not safe to read the GDB serial stream so we need
- // to skip it on this check
- //
- if (!gCtrlCBreakFlag && !gProcessingFPacket) {
- //
- // Ctrl-C was not pending so grab any pending characters and see if they
- // are a Ctrl-c (0x03). If so set the Ctrl-C global.
- //
- while (TRUE) {
- if (!GdbIsCharAvailable ()) {
- //
- // No characters are pending so exit the loop
- //
- break;
- }
-
- if (GdbGetChar () == 0x03) {
- gCtrlCBreakFlag = TRUE;
- //
- // We have a ctrl-c so exit the loop
- //
- break;
- }
- }
- }
-
- if (gCtrlCBreakFlag) {
- //
- // Update the context to force a single step trap when we exit the GDB
- // stub. This will trasfer control to GdbExceptionHandler () and let
- // us break into the program. We don't want to break into the GDB stub.
- //
- AddSingleStep (SystemContext);
- gCtrlCBreakFlag = FALSE;
- }
-}
+/** @file\r
+ UEFI driver that implements a GDB stub\r
+\r
+ Note: Any code in the path of the Serial IO output can not call DEBUG as will\r
+ will blow out the stack. Serial IO calls DEBUG, debug calls Serail IO, ...\r
+\r
+\r
+ Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <GdbStubInternal.h>\r
+#include <Protocol/DebugPort.h>\r
+\r
+\r
+UINTN gMaxProcessorIndex = 0;\r
+\r
+//\r
+// Buffers for basic gdb communication\r
+//\r
+CHAR8 gInBuffer[MAX_BUF_SIZE];\r
+CHAR8 gOutBuffer[MAX_BUF_SIZE];\r
+\r
+// Assume gdb does a "qXfer:libraries:read::offset,length" when it connects so we can default\r
+// this value to FALSE. Since gdb can reconnect its self a global default is not good enough\r
+BOOLEAN gSymbolTableUpdate = FALSE;\r
+EFI_EVENT gEvent;\r
+VOID *gGdbSymbolEventHandlerRegistration = NULL;\r
+\r
+//\r
+// Globals for returning XML from qXfer:libraries:read packet\r
+//\r
+UINTN gPacketqXferLibraryOffset = 0;\r
+UINTN gEfiDebugImageTableEntry = 0;\r
+EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *gDebugImageTableHeader = NULL;\r
+EFI_DEBUG_IMAGE_INFO *gDebugTable = NULL;\r
+CHAR8 gXferLibraryBuffer[2000];\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mHexToStr[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};\r
+\r
+\r
+VOID\r
+EFIAPI\r
+GdbSymbolEventHandler (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+}\r
+\r
+\r
+/**\r
+ The user Entry Point for Application. The user code starts with this function\r
+ as the real entry point for the image goes into a library that calls this\r
+ function.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The entry point is executed successfully.\r
+ @retval other Some error occurs when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GdbStubEntry (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DEBUG_SUPPORT_PROTOCOL *DebugSupport;\r
+ UINTN HandleCount;\r
+ EFI_HANDLE *Handles;\r
+ UINTN Index;\r
+ UINTN Processor;\r
+ BOOLEAN IsaSupported;\r
+\r
+ Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&gDebugImageTableHeader);\r
+ if (EFI_ERROR (Status)) {\r
+ gDebugImageTableHeader = NULL;\r
+ }\r
+\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiDebugSupportProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Debug Support Protocol not found\n"));\r
+\r
+ return Status;\r
+ }\r
+\r
+ DebugSupport = NULL;\r
+ IsaSupported = FALSE;\r
+ do {\r
+ HandleCount--;\r
+ Status = gBS->HandleProtocol (\r
+ Handles[HandleCount],\r
+ &gEfiDebugSupportProtocolGuid,\r
+ (VOID **) &DebugSupport\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ if (CheckIsa (DebugSupport->Isa)) {\r
+ // We found what we are looking for so break out of the loop\r
+ IsaSupported = TRUE;\r
+ break;\r
+ }\r
+ }\r
+ } while (HandleCount > 0);\r
+ FreePool (Handles);\r
+\r
+ if (!IsaSupported) {\r
+ DEBUG ((EFI_D_ERROR, "Debug Support Protocol does not support our ISA\n"));\r
+\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = DebugSupport->GetMaximumProcessorIndex (DebugSupport, &gMaxProcessorIndex);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ DEBUG ((EFI_D_INFO, "Debug Support Protocol ISA %x\n", DebugSupport->Isa));\r
+ DEBUG ((EFI_D_INFO, "Debug Support Protocol Processor Index %d\n", gMaxProcessorIndex));\r
+\r
+ // Call processor-specific init routine\r
+ InitializeProcessor ();\r
+\r
+ for (Processor = 0; Processor <= gMaxProcessorIndex; Processor++) {\r
+ for (Index = 0; Index < MaxEfiException (); Index++) {\r
+ Status = DebugSupport->RegisterExceptionCallback (DebugSupport, Processor, GdbExceptionHandler, gExceptionType[Index].Exception);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ //\r
+ // Current edk2 DebugPort is not interrupt context safe so we can not use it\r
+ //\r
+ Status = DebugSupport->RegisterPeriodicCallback (DebugSupport, Processor, GdbPeriodicCallBack);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ //\r
+ // This even fires every time an image is added. This allows the stub to know when gdb needs\r
+ // to update the symbol table.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ GdbSymbolEventHandler,\r
+ NULL,\r
+ &gEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Register for protocol notifications on this event\r
+ //\r
+ Status = gBS->RegisterProtocolNotify (\r
+ &gEfiLoadedImageProtocolGuid,\r
+ gEvent,\r
+ &gGdbSymbolEventHandlerRegistration\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+\r
+ if (PcdGetBool (PcdGdbSerial)) {\r
+ GdbInitializeSerialConsole ();\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Transfer length bytes of input buffer, starting at Address, to memory.\r
+\r
+ @param length the number of the bytes to be transferred/written\r
+ @param *address the start address of the transferring/writing the memory\r
+ @param *new_data the new data to be written to memory\r
+ **/\r
+\r
+VOID\r
+TransferFromInBufToMem (\r
+ IN UINTN Length,\r
+ IN unsigned char *Address,\r
+ IN CHAR8 *NewData\r
+ )\r
+{\r
+ CHAR8 c1;\r
+ CHAR8 c2;\r
+\r
+ while (Length-- > 0) {\r
+ c1 = (CHAR8)HexCharToInt (*NewData++);\r
+ c2 = (CHAR8)HexCharToInt (*NewData++);\r
+\r
+ if ((c1 < 0) || (c2 < 0)) {\r
+ Print ((CHAR16 *)L"Bad message from write to memory..\n");\r
+ SendError (GDB_EBADMEMDATA);\r
+ return;\r
+ }\r
+ *Address++ = (UINT8)((c1 << 4) + c2);\r
+ }\r
+\r
+ SendSuccess();\r
+}\r
+\r
+\r
+/**\r
+ Transfer Length bytes of memory starting at Address to an output buffer, OutBuffer. This function will finally send the buffer\r
+ as a packet.\r
+\r
+ @param Length the number of the bytes to be transferred/read\r
+ @param *address pointer to the start address of the transferring/reading the memory\r
+ **/\r
+\r
+VOID\r
+TransferFromMemToOutBufAndSend (\r
+ IN UINTN Length,\r
+ IN unsigned char *Address\r
+ )\r
+{\r
+ // there are Length bytes and every byte is represented as 2 hex chars\r
+ CHAR8 OutBuffer[MAX_BUF_SIZE];\r
+ CHAR8 *OutBufPtr; // pointer to the output buffer\r
+ CHAR8 Char;\r
+\r
+ if (ValidateAddress(Address) == FALSE) {\r
+ SendError(14);\r
+ return;\r
+ }\r
+\r
+ OutBufPtr = OutBuffer;\r
+ while (Length > 0) {\r
+\r
+ Char = mHexToStr[*Address >> 4];\r
+ if ((Char >= 'A') && (Char <= 'F')) {\r
+ Char = Char - 'A' + 'a';\r
+ }\r
+ *OutBufPtr++ = Char;\r
+\r
+ Char = mHexToStr[*Address & 0x0f];\r
+ if ((Char >= 'A') && (Char <= 'F')) {\r
+ Char = Char - 'A' + 'a';\r
+ }\r
+ *OutBufPtr++ = Char;\r
+\r
+ Address++;\r
+ Length--;\r
+ }\r
+\r
+ *OutBufPtr = '\0' ; // the end of the buffer\r
+ SendPacket (OutBuffer);\r
+}\r
+\r
+\r
+\r
+/**\r
+ Send a GDB Remote Serial Protocol Packet\r
+\r
+ $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$',\r
+ the packet teminating character '#' and the two digit checksum.\r
+\r
+ If an ack '+' is not sent resend the packet, but timeout eventually so we don't end up\r
+ in an infinit loop. This is so if you unplug the debugger code just keeps running\r
+\r
+ @param PacketData Payload data for the packet\r
+\r
+\r
+ @retval Number of bytes of packet data sent.\r
+\r
+**/\r
+UINTN\r
+SendPacket (\r
+ IN CHAR8 *PacketData\r
+ )\r
+{\r
+ UINT8 CheckSum;\r
+ UINTN Timeout;\r
+ CHAR8 *Ptr;\r
+ CHAR8 TestChar;\r
+ UINTN Count;\r
+\r
+ Timeout = PcdGet32 (PcdGdbMaxPacketRetryCount);\r
+\r
+ Count = 0;\r
+ do {\r
+\r
+ Ptr = PacketData;\r
+\r
+ if (Timeout-- == 0) {\r
+ // Only try a finite number of times so we don't get stuck in the loop\r
+ return Count;\r
+ }\r
+\r
+ // Packet prefix\r
+ GdbPutChar ('$');\r
+\r
+ for (CheckSum = 0, Count =0 ; *Ptr != '\0'; Ptr++, Count++) {\r
+ GdbPutChar (*Ptr);\r
+ CheckSum = CheckSum + *Ptr;\r
+ }\r
+\r
+ // Packet terminating character and checksum\r
+ GdbPutChar ('#');\r
+ GdbPutChar (mHexToStr[CheckSum >> 4]);\r
+ GdbPutChar (mHexToStr[CheckSum & 0x0F]);\r
+\r
+ TestChar = GdbGetChar ();\r
+ } while (TestChar != '+');\r
+\r
+ return Count;\r
+}\r
+\r
+/**\r
+ Receive a GDB Remote Serial Protocol Packet\r
+\r
+ $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$',\r
+ the packet teminating character '#' and the two digit checksum.\r
+\r
+ If host re-starts sending a packet without ending the previous packet, only the last valid packet is proccessed.\r
+ (In other words, if received packet is '$12345$12345$123456#checksum', only '$123456#checksum' will be processed.)\r
+\r
+ If an ack '+' is not sent resend the packet\r
+\r
+ @param PacketData Payload data for the packet\r
+\r
+ @retval Number of bytes of packet data received.\r
+\r
+**/\r
+UINTN\r
+ReceivePacket (\r
+ OUT CHAR8 *PacketData,\r
+ IN UINTN PacketDataSize\r
+ )\r
+{\r
+ UINT8 CheckSum;\r
+ UINTN Index;\r
+ CHAR8 Char;\r
+ CHAR8 SumString[3];\r
+ CHAR8 TestChar;\r
+\r
+ ZeroMem (PacketData, PacketDataSize);\r
+\r
+ for (;;) {\r
+ // wait for the start of a packet\r
+ TestChar = GdbGetChar ();\r
+ while (TestChar != '$') {\r
+ TestChar = GdbGetChar ();\r
+ };\r
+\r
+ retry:\r
+ for (Index = 0, CheckSum = 0; Index < (PacketDataSize - 1); Index++) {\r
+ Char = GdbGetChar ();\r
+ if (Char == '$') {\r
+ goto retry;\r
+ }\r
+ if (Char == '#') {\r
+ break;\r
+ }\r
+\r
+ PacketData[Index] = Char;\r
+ CheckSum = CheckSum + Char;\r
+ }\r
+ PacketData[Index] = '\0';\r
+\r
+ if (Index == PacketDataSize) {\r
+ continue;\r
+ }\r
+\r
+ SumString[0] = GdbGetChar ();\r
+ SumString[1] = GdbGetChar ();\r
+ SumString[2] = '\0';\r
+\r
+ if (AsciiStrHexToUintn (SumString) == CheckSum) {\r
+ // Ack: Success\r
+ GdbPutChar ('+');\r
+\r
+ // Null terminate the callers string\r
+ PacketData[Index] = '\0';\r
+ return Index;\r
+ } else {\r
+ // Ack: Failure\r
+ GdbPutChar ('-');\r
+ }\r
+ }\r
+\r
+ //return 0;\r
+}\r
+\r
+\r
+/**\r
+ Empties the given buffer\r
+ @param Buf pointer to the first element in buffer to be emptied\r
+ **/\r
+VOID\r
+EmptyBuffer (\r
+ IN CHAR8 *Buf\r
+ )\r
+{\r
+ *Buf = '\0';\r
+}\r
+\r
+\r
+/**\r
+ Converts an 8-bit Hex Char into a INTN.\r
+\r
+ @param Char the hex character to be converted into UINTN\r
+ @retval a INTN, from 0 to 15, that corressponds to Char\r
+ -1 if Char is not a hex character\r
+ **/\r
+INTN\r
+HexCharToInt (\r
+ IN CHAR8 Char\r
+ )\r
+{\r
+ if ((Char >= 'A') && (Char <= 'F')) {\r
+ return Char - 'A' + 10;\r
+ } else if ((Char >= 'a') && (Char <= 'f')) {\r
+ return Char - 'a' + 10;\r
+ } else if ((Char >= '0') && (Char <= '9')) {\r
+ return Char - '0';\r
+ } else { // if not a hex value, return a negative value\r
+ return -1;\r
+ }\r
+}\r
+\r
+ // 'E' + the biggest error number is 255, so its 2 hex digits + buffer end\r
+CHAR8 *gError = "E__";\r
+\r
+/** 'E NN'\r
+ Send an error with the given error number after converting to hex.\r
+ The error number is put into the buffer in hex. '255' is the biggest errno we can send.\r
+ ex: 162 will be sent as A2.\r
+\r
+ @param errno the error number that will be sent\r
+ **/\r
+VOID\r
+EFIAPI\r
+SendError (\r
+ IN UINT8 ErrorNum\r
+ )\r
+{\r
+ //\r
+ // Replace _, or old data, with current errno\r
+ //\r
+ gError[1] = mHexToStr [ErrorNum >> 4];\r
+ gError[2] = mHexToStr [ErrorNum & 0x0f];\r
+\r
+ SendPacket (gError); // send buffer\r
+}\r
+\r
+\r
+\r
+/**\r
+ Send 'OK' when the function is done executing successfully.\r
+ **/\r
+VOID\r
+EFIAPI\r
+SendSuccess (\r
+ VOID\r
+ )\r
+{\r
+ SendPacket ("OK"); // send buffer\r
+}\r
+\r
+\r
+/**\r
+ Send empty packet to specify that particular command/functionality is not supported.\r
+ **/\r
+VOID\r
+EFIAPI\r
+SendNotSupported (\r
+ VOID\r
+ )\r
+{\r
+ SendPacket ("");\r
+}\r
+\r
+\r
+/**\r
+ Send the T signal with the given exception type (in gdb order) and possibly with n:r pairs related to the watchpoints\r
+\r
+ @param SystemContext Register content at time of the exception\r
+ @param GdbExceptionType GDB exception type\r
+ **/\r
+VOID\r
+GdbSendTSignal (\r
+ IN EFI_SYSTEM_CONTEXT SystemContext,\r
+ IN UINT8 GdbExceptionType\r
+ )\r
+{\r
+ CHAR8 TSignalBuffer[128];\r
+ CHAR8 *TSignalPtr;\r
+ UINTN BreakpointDetected;\r
+ BREAK_TYPE BreakType;\r
+ UINTN DataAddress;\r
+ CHAR8 *WatchStrPtr = NULL;\r
+ UINTN RegSize;\r
+\r
+ TSignalPtr = &TSignalBuffer[0];\r
+\r
+ //Construct TSignal packet\r
+ *TSignalPtr++ = 'T';\r
+\r
+ //\r
+ // replace _, or previous value, with Exception type\r
+ //\r
+ *TSignalPtr++ = mHexToStr [GdbExceptionType >> 4];\r
+ *TSignalPtr++ = mHexToStr [GdbExceptionType & 0x0f];\r
+\r
+ if (GdbExceptionType == GDB_SIGTRAP) {\r
+ if (gSymbolTableUpdate) {\r
+ //\r
+ // We can only send back on reason code. So if the flag is set it means the breakpoint is from our event handler\r
+ //\r
+ WatchStrPtr = "library:;";\r
+ while (*WatchStrPtr != '\0') {\r
+ *TSignalPtr++ = *WatchStrPtr++;\r
+ }\r
+ gSymbolTableUpdate = FALSE;\r
+ } else {\r
+\r
+\r
+ //\r
+ // possible n:r pairs\r
+ //\r
+\r
+ //Retrieve the breakpoint number\r
+ BreakpointDetected = GetBreakpointDetected (SystemContext);\r
+\r
+ //Figure out if the exception is happend due to watch, rwatch or awatch.\r
+ BreakType = GetBreakpointType (SystemContext, BreakpointDetected);\r
+\r
+ //INFO: rwatch is not supported due to the way IA32 debug registers work\r
+ if ((BreakType == DataWrite) || (BreakType == DataRead) || (BreakType == DataReadWrite)) {\r
+\r
+ //Construct n:r pair\r
+ DataAddress = GetBreakpointDataAddress (SystemContext, BreakpointDetected);\r
+\r
+ //Assign appropriate buffer to print particular watchpoint type\r
+ if (BreakType == DataWrite) {\r
+ WatchStrPtr = "watch";\r
+ } else if (BreakType == DataRead) {\r
+ WatchStrPtr = "rwatch";\r
+ } else if (BreakType == DataReadWrite) {\r
+ WatchStrPtr = "awatch";\r
+ }\r
+\r
+ while (*WatchStrPtr != '\0') {\r
+ *TSignalPtr++ = *WatchStrPtr++;\r
+ }\r
+\r
+ *TSignalPtr++ = ':';\r
+\r
+ //Set up series of bytes in big-endian byte order. "awatch" won't work with little-endian byte order.\r
+ RegSize = REG_SIZE;\r
+ while (RegSize > 0) {\r
+ RegSize = RegSize-4;\r
+ *TSignalPtr++ = mHexToStr[(UINT8)(DataAddress >> RegSize) & 0xf];\r
+ }\r
+\r
+ //Always end n:r pair with ';'\r
+ *TSignalPtr++ = ';';\r
+ }\r
+ }\r
+ }\r
+\r
+ *TSignalPtr = '\0';\r
+\r
+ SendPacket (TSignalBuffer);\r
+}\r
+\r
+\r
+/**\r
+ Translates the EFI mapping to GDB mapping\r
+\r
+ @param EFIExceptionType EFI Exception that is being processed\r
+ @retval UINTN that corresponds to EFIExceptionType's GDB exception type number\r
+ **/\r
+UINT8\r
+ConvertEFItoGDBtype (\r
+ IN EFI_EXCEPTION_TYPE EFIExceptionType\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < MaxEfiException () ; Index++) {\r
+ if (gExceptionType[Index].Exception == EFIExceptionType) {\r
+ return gExceptionType[Index].SignalNo;\r
+ }\r
+ }\r
+ return GDB_SIGTRAP; // this is a GDB trap\r
+}\r
+\r
+\r
+/** "m addr,length"\r
+ Find the Length of the area to read and the start addres. Finally, pass them to\r
+ another function, TransferFromMemToOutBufAndSend, that will read from that memory space and\r
+ send it as a packet.\r
+ **/\r
+\r
+VOID\r
+EFIAPI\r
+ReadFromMemory (\r
+ CHAR8 *PacketData\r
+ )\r
+{\r
+ UINTN Address;\r
+ UINTN Length;\r
+ CHAR8 AddressBuffer[MAX_ADDR_SIZE]; // the buffer that will hold the address in hex chars\r
+ CHAR8 *AddrBufPtr; // pointer to the address buffer\r
+ CHAR8 *InBufPtr; /// pointer to the input buffer\r
+\r
+ AddrBufPtr = AddressBuffer;\r
+ InBufPtr = &PacketData[1];\r
+ while (*InBufPtr != ',') {\r
+ *AddrBufPtr++ = *InBufPtr++;\r
+ }\r
+ *AddrBufPtr = '\0';\r
+\r
+ InBufPtr++; // this skips ',' in the buffer\r
+\r
+ /* Error checking */\r
+ if (AsciiStrLen (AddressBuffer) >= MAX_ADDR_SIZE) {\r
+ Print((CHAR16 *)L"Address is too long\n");\r
+ SendError (GDB_EBADMEMADDRBUFSIZE);\r
+ return;\r
+ }\r
+\r
+ // 2 = 'm' + ','\r
+ if (AsciiStrLen (PacketData) - AsciiStrLen (AddressBuffer) - 2 >= MAX_LENGTH_SIZE) {\r
+ Print((CHAR16 *)L"Length is too long\n");\r
+ SendError (GDB_EBADMEMLENGTH);\r
+ return;\r
+ }\r
+\r
+ Address = AsciiStrHexToUintn (AddressBuffer);\r
+ Length = AsciiStrHexToUintn (InBufPtr);\r
+\r
+ TransferFromMemToOutBufAndSend (Length, (unsigned char *)Address);\r
+}\r
+\r
+\r
+/** "M addr,length :XX..."\r
+ Find the Length of the area in bytes to write and the start addres. Finally, pass them to\r
+ another function, TransferFromInBufToMem, that will write to that memory space the info in\r
+ the input buffer.\r
+ **/\r
+VOID\r
+EFIAPI\r
+WriteToMemory (\r
+ IN CHAR8 *PacketData\r
+ )\r
+{\r
+ UINTN Address;\r
+ UINTN Length;\r
+ UINTN MessageLength;\r
+ CHAR8 AddressBuffer[MAX_ADDR_SIZE]; // the buffer that will hold the Address in hex chars\r
+ CHAR8 LengthBuffer[MAX_LENGTH_SIZE]; // the buffer that will hold the Length in hex chars\r
+ CHAR8 *AddrBufPtr; // pointer to the Address buffer\r
+ CHAR8 *LengthBufPtr; // pointer to the Length buffer\r
+ CHAR8 *InBufPtr; /// pointer to the input buffer\r
+\r
+ AddrBufPtr = AddressBuffer;\r
+ LengthBufPtr = LengthBuffer;\r
+ InBufPtr = &PacketData[1];\r
+\r
+ while (*InBufPtr != ',') {\r
+ *AddrBufPtr++ = *InBufPtr++;\r
+ }\r
+ *AddrBufPtr = '\0';\r
+\r
+ InBufPtr++; // this skips ',' in the buffer\r
+\r
+ while (*InBufPtr != ':') {\r
+ *LengthBufPtr++ = *InBufPtr++;\r
+ }\r
+ *LengthBufPtr = '\0';\r
+\r
+ InBufPtr++; // this skips ':' in the buffer\r
+\r
+ Address = AsciiStrHexToUintn (AddressBuffer);\r
+ Length = AsciiStrHexToUintn (LengthBuffer);\r
+\r
+ /* Error checking */\r
+\r
+ //Check if Address is not too long.\r
+ if (AsciiStrLen (AddressBuffer) >= MAX_ADDR_SIZE) {\r
+ Print ((CHAR16 *)L"Address too long..\n");\r
+ SendError (GDB_EBADMEMADDRBUFSIZE);\r
+ return;\r
+ }\r
+\r
+ //Check if message length is not too long\r
+ if (AsciiStrLen (LengthBuffer) >= MAX_LENGTH_SIZE) {\r
+ Print ((CHAR16 *)L"Length too long..\n");\r
+ SendError (GDB_EBADMEMLENGBUFSIZE);\r
+ return;\r
+ }\r
+\r
+ // Check if Message is not too long/short.\r
+ // 3 = 'M' + ',' + ':'\r
+ MessageLength = (AsciiStrLen (PacketData) - AsciiStrLen (AddressBuffer) - AsciiStrLen (LengthBuffer) - 3);\r
+ if (MessageLength != (2*Length)) {\r
+ //Message too long/short. New data is not the right size.\r
+ SendError (GDB_EBADMEMDATASIZE);\r
+ return;\r
+ }\r
+ TransferFromInBufToMem (Length, (unsigned char *)Address, InBufPtr);\r
+}\r
+\r
+/**\r
+ Parses breakpoint packet data and captures Breakpoint type, Address and length.\r
+ In case of an error, function returns particular error code. Returning 0 meaning\r
+ no error.\r
+\r
+ @param PacketData Pointer to the payload data for the packet.\r
+ @param Type Breakpoint type\r
+ @param Address Breakpoint address\r
+ @param Length Breakpoint length in Bytes (1 byte, 2 byte, 4 byte)\r
+\r
+ @retval 1 Success\r
+ @retval {other} Particular error code\r
+\r
+**/\r
+UINTN\r
+ParseBreakpointPacket (\r
+ IN CHAR8 *PacketData,\r
+ OUT UINTN *Type,\r
+ OUT UINTN *Address,\r
+ OUT UINTN *Length\r
+ )\r
+{\r
+ CHAR8 AddressBuffer[MAX_ADDR_SIZE];\r
+ CHAR8 *AddressBufferPtr;\r
+ CHAR8 *PacketDataPtr;\r
+\r
+ PacketDataPtr = &PacketData[1];\r
+ AddressBufferPtr = AddressBuffer;\r
+\r
+ *Type = AsciiStrHexToUintn (PacketDataPtr);\r
+\r
+ //Breakpoint/watchpoint type should be between 0 to 4\r
+ if (*Type > 4) {\r
+ Print ((CHAR16 *)L"Type is invalid\n");\r
+ return 22; //EINVAL: Invalid argument.\r
+ }\r
+\r
+ //Skip ',' in the buffer.\r
+ while (*PacketDataPtr++ != ',');\r
+\r
+ //Parse Address information\r
+ while (*PacketDataPtr != ',') {\r
+ *AddressBufferPtr++ = *PacketDataPtr++;\r
+ }\r
+ *AddressBufferPtr = '\0';\r
+\r
+ //Check if Address is not too long.\r
+ if (AsciiStrLen (AddressBuffer) >= MAX_ADDR_SIZE) {\r
+ Print ((CHAR16 *)L"Address too long..\n");\r
+ return 40; //EMSGSIZE: Message size too long.\r
+ }\r
+\r
+ *Address = AsciiStrHexToUintn (AddressBuffer);\r
+\r
+ PacketDataPtr++; //This skips , in the buffer\r
+\r
+ //Parse Length information\r
+ *Length = AsciiStrHexToUintn (PacketDataPtr);\r
+\r
+ //Length should be 1, 2 or 4 bytes\r
+ if (*Length > 4) {\r
+ Print ((CHAR16 *)L"Length is invalid\n");\r
+ return 22; //EINVAL: Invalid argument\r
+ }\r
+\r
+ return 0; //0 = No error\r
+}\r
+\r
+UINTN\r
+gXferObjectReadResponse (\r
+ IN CHAR8 Type,\r
+ IN CHAR8 *Str\r
+ )\r
+{\r
+ CHAR8 *OutBufPtr; // pointer to the output buffer\r
+ CHAR8 Char;\r
+ UINTN Count;\r
+\r
+ // Response starts with 'm' or 'l' if it is the end\r
+ OutBufPtr = gOutBuffer;\r
+ *OutBufPtr++ = Type;\r
+ Count = 1;\r
+\r
+ // Binary data encoding\r
+ OutBufPtr = gOutBuffer;\r
+ while (*Str != '\0') {\r
+ Char = *Str++;\r
+ if ((Char == 0x7d) || (Char == 0x23) || (Char == 0x24) || (Char == 0x2a)) {\r
+ // escape character\r
+ *OutBufPtr++ = 0x7d;\r
+\r
+ Char ^= 0x20;\r
+ }\r
+ *OutBufPtr++ = Char;\r
+ Count++;\r
+ }\r
+\r
+ *OutBufPtr = '\0' ; // the end of the buffer\r
+ SendPacket (gOutBuffer);\r
+\r
+ return Count;\r
+}\r
+\r
+\r
+/**\r
+ Note: This should be a library function. In the Apple case you have to add\r
+ the size of the PE/COFF header into the starting address to make things work\r
+ right as there is no way to pad the Mach-O for the size of the PE/COFF header.\r
+\r
+\r
+ Returns a pointer to the PDB file name for a PE/COFF image that has been\r
+ loaded into system memory with the PE/COFF Loader Library functions.\r
+\r
+ Returns the PDB file name for the PE/COFF image specified by Pe32Data. If\r
+ the PE/COFF image specified by Pe32Data is not a valid, then NULL is\r
+ returned. If the PE/COFF image specified by Pe32Data does not contain a\r
+ debug directory entry, then NULL is returned. If the debug directory entry\r
+ in the PE/COFF image specified by Pe32Data does not contain a PDB file name,\r
+ then NULL is returned.\r
+ If Pe32Data is NULL, then ASSERT().\r
+\r
+ @param Pe32Data Pointer to the PE/COFF image that is loaded in system\r
+ memory.\r
+ @param DebugBase Address that the debugger would use as the base of the image\r
+\r
+ @return The PDB file name for the PE/COFF image specified by Pe32Data or NULL\r
+ if it cannot be retrieved. DebugBase is only valid if PDB file name is\r
+ valid.\r
+\r
+**/\r
+VOID *\r
+EFIAPI\r
+PeCoffLoaderGetDebuggerInfo (\r
+ IN VOID *Pe32Data,\r
+ OUT VOID **DebugBase\r
+ )\r
+{\r
+ EFI_IMAGE_DOS_HEADER *DosHdr;\r
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r
+ EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;\r
+ EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;\r
+ UINTN DirCount;\r
+ VOID *CodeViewEntryPointer;\r
+ INTN TEImageAdjust;\r
+ UINT32 NumberOfRvaAndSizes;\r
+ UINT16 Magic;\r
+ UINTN SizeOfHeaders;\r
+\r
+ ASSERT (Pe32Data != NULL);\r
+\r
+ TEImageAdjust = 0;\r
+ DirectoryEntry = NULL;\r
+ DebugEntry = NULL;\r
+ NumberOfRvaAndSizes = 0;\r
+ SizeOfHeaders = 0;\r
+\r
+ DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;\r
+ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
+ //\r
+ // DOS image header is present, so read the PE header after the DOS image header.\r
+ //\r
+ Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));\r
+ } else {\r
+ //\r
+ // DOS image header is not present, so PE header is at the image base.\r
+ //\r
+ Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;\r
+ }\r
+\r
+ if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {\r
+ if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) {\r
+ DirectoryEntry = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG];\r
+ TEImageAdjust = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize;\r
+ DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te +\r
+ Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress +\r
+ TEImageAdjust);\r
+ }\r
+ SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize;\r
+\r
+ // __APPLE__ check this math...\r
+ *DebugBase = ((CHAR8 *)Pe32Data) - TEImageAdjust;\r
+ } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {\r
+\r
+ *DebugBase = Pe32Data;\r
+\r
+\r
+ //\r
+ // NOTE: We use Machine field to identify PE32/PE32+, instead of Magic.\r
+ // It is due to backward-compatibility, for some system might\r
+ // generate PE32+ image with PE32 Magic.\r
+ //\r
+ switch (Hdr.Pe32->FileHeader.Machine) {\r
+ case EFI_IMAGE_MACHINE_IA32:\r
+ //\r
+ // Assume PE32 image with IA32 Machine field.\r
+ //\r
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;\r
+ break;\r
+ case EFI_IMAGE_MACHINE_X64:\r
+ case EFI_IMAGE_MACHINE_IA64:\r
+ //\r
+ // Assume PE32+ image with X64 or IPF Machine field\r
+ //\r
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;\r
+ break;\r
+ default:\r
+ //\r
+ // For unknow Machine field, use Magic in optional Header\r
+ //\r
+ Magic = Hdr.Pe32->OptionalHeader.Magic;\r
+ }\r
+\r
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset get Debug Directory Entry\r
+ //\r
+ SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;\r
+ NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
+ DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);\r
+ DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);\r
+ } else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r
+ //\r
+ // Use PE32+ offset get Debug Directory Entry\r
+ //\r
+ SizeOfHeaders = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders;\r
+ NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
+ DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);\r
+ DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);\r
+ }\r
+\r
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {\r
+ DirectoryEntry = NULL;\r
+ DebugEntry = NULL;\r
+ }\r
+ } else {\r
+ return NULL;\r
+ }\r
+\r
+ if (DebugEntry == NULL || DirectoryEntry == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) {\r
+ if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {\r
+ if (DebugEntry->SizeOfData > 0) {\r
+ CodeViewEntryPointer = (VOID *) ((UINTN) DebugEntry->RVA + ((UINTN)Pe32Data) + (UINTN)TEImageAdjust);\r
+ switch (* (UINT32 *) CodeViewEntryPointer) {\r
+ case CODEVIEW_SIGNATURE_NB10:\r
+ return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));\r
+ case CODEVIEW_SIGNATURE_RSDS:\r
+ return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));\r
+ case CODEVIEW_SIGNATURE_MTOC:\r
+ *DebugBase = (VOID *)(UINTN)((UINTN)DebugBase - SizeOfHeaders);\r
+ return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ (void)SizeOfHeaders;\r
+ return NULL;\r
+}\r
+\r
+\r
+/**\r
+ Process "qXfer:object:read:annex:offset,length" request.\r
+\r
+ Returns an XML document that contains loaded libraries. In our case it is\r
+ information in the EFI Debug Image Table converted into an XML document.\r
+\r
+ GDB will call with an arbitrary length (it can't know the real length and\r
+ will reply with chunks of XML that are easy for us to deal with. Gdb will\r
+ keep calling until we say we are done. XML doc looks like:\r
+\r
+ <library-list>\r
+ <library name="/a/a/c/d.dSYM"><segment address="0x10000000"/></library>\r
+ <library name="/a/m/e/e.pdb"><segment address="0x20000000"/></library>\r
+ <library name="/a/l/f/f.dll"><segment address="0x30000000"/></library>\r
+ </library-list>\r
+\r
+ Since we can not allocate memory in interrupt context this module has\r
+ assumptions about how it will get called:\r
+ 1) Length will generally be max remote packet size (big enough)\r
+ 2) First Offset of an XML document read needs to be 0\r
+ 3) This code will return back small chunks of the XML document on every read.\r
+ Each subsequent call will ask for the next available part of the document.\r
+\r
+ Note: The only variable size element in the XML is:\r
+ " <library name=\"%s\"><segment address=\"%p\"/></library>\n" and it is\r
+ based on the file path and name of the symbol file. If the symbol file name\r
+ is bigger than the max gdb remote packet size we could update this code\r
+ to respond back in chunks.\r
+\r
+ @param Offset offset into special data area\r
+ @param Length number of bytes to read starting at Offset\r
+\r
+ **/\r
+VOID\r
+QxferLibrary (\r
+ IN UINTN Offset,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ VOID *LoadAddress;\r
+ CHAR8 *Pdb;\r
+ UINTN Size;\r
+\r
+ if (Offset != gPacketqXferLibraryOffset) {\r
+ SendError (GDB_EINVALIDARG);\r
+ Print (L"\nqXferLibrary (%d, %d) != %d\n", Offset, Length, gPacketqXferLibraryOffset);\r
+\r
+ // Force a retry from the beginning\r
+ gPacketqXferLibraryOffset = 0;\r
+\r
+ return;\r
+ }\r
+\r
+ if (Offset == 0) {\r
+ gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', "<library-list>\n");\r
+\r
+ // The owner of the table may have had to ralloc it so grab a fresh copy every time\r
+ // we assume qXferLibrary will get called over and over again until the entire XML table is\r
+ // returned in a tight loop. Since we are in the debugger the table should not get updated\r
+ gDebugTable = gDebugImageTableHeader->EfiDebugImageInfoTable;\r
+ gEfiDebugImageTableEntry = 0;\r
+ return;\r
+ }\r
+\r
+ if (gDebugTable != NULL) {\r
+ for (; gEfiDebugImageTableEntry < gDebugImageTableHeader->TableSize; gEfiDebugImageTableEntry++, gDebugTable++) {\r
+ if (gDebugTable->NormalImage != NULL) {\r
+ if ((gDebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&\r
+ (gDebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {\r
+ Pdb = PeCoffLoaderGetDebuggerInfo (\r
+ gDebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase,\r
+ &LoadAddress\r
+ );\r
+ if (Pdb != NULL) {\r
+ Size = AsciiSPrint (\r
+ gXferLibraryBuffer,\r
+ sizeof (gXferLibraryBuffer),\r
+ " <library name=\"%a\"><segment address=\"0x%p\"/></library>\n",\r
+ Pdb,\r
+ LoadAddress\r
+ );\r
+ if ((Size != 0) && (Size != (sizeof (gXferLibraryBuffer) - 1))) {\r
+ gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', gXferLibraryBuffer);\r
+\r
+ // Update loop variables so we are in the right place when we get back\r
+ gEfiDebugImageTableEntry++;\r
+ gDebugTable++;\r
+ return;\r
+ } else {\r
+ // We could handle <library> entires larger than sizeof (gXferLibraryBuffer) here if\r
+ // needed by breaking up into N packets\r
+ // "<library name=\"%s\r
+ // the rest of the string (as many packets as required\r
+ // \"><segment address=\"%d\"/></library> (fixed size)\r
+ //\r
+ // But right now we just skip any entry that is too big\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ gXferObjectReadResponse ('l', "</library-list>\n");\r
+ gPacketqXferLibraryOffset = 0;\r
+ return;\r
+}\r
+\r
+\r
+/**\r
+ Exception Hanldler for GDB. It will be called for all exceptions\r
+ registered via the gExceptionType[] array.\r
+\r
+ @param ExceptionType Exception that is being processed\r
+ @param SystemContext Register content at time of the exception\r
+ **/\r
+VOID\r
+EFIAPI\r
+GdbExceptionHandler (\r
+ IN EFI_EXCEPTION_TYPE ExceptionType,\r
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
+ )\r
+{\r
+ UINT8 GdbExceptionType;\r
+ CHAR8 *Ptr;\r
+\r
+\r
+ if (ValidateException (ExceptionType, SystemContext) == FALSE) {\r
+ return;\r
+ }\r
+\r
+ RemoveSingleStep (SystemContext);\r
+\r
+ GdbExceptionType = ConvertEFItoGDBtype (ExceptionType);\r
+ GdbSendTSignal (SystemContext, GdbExceptionType);\r
+\r
+ for( ; ; ) {\r
+ ReceivePacket (gInBuffer, MAX_BUF_SIZE);\r
+\r
+ switch (gInBuffer[0]) {\r
+ case '?':\r
+ GdbSendTSignal (SystemContext, GdbExceptionType);\r
+ break;\r
+\r
+ case 'c':\r
+ ContinueAtAddress (SystemContext, gInBuffer);\r
+ return;\r
+\r
+ case 'g':\r
+ ReadGeneralRegisters (SystemContext);\r
+ break;\r
+\r
+ case 'G':\r
+ WriteGeneralRegisters (SystemContext, gInBuffer);\r
+ break;\r
+\r
+ case 'H':\r
+ //Return "OK" packet since we don't have more than one thread.\r
+ SendSuccess ();\r
+ break;\r
+\r
+ case 'm':\r
+ ReadFromMemory (gInBuffer);\r
+ break;\r
+\r
+ case 'M':\r
+ WriteToMemory (gInBuffer);\r
+ break;\r
+\r
+ case 'P':\r
+ WriteNthRegister (SystemContext, gInBuffer);\r
+ break;\r
+\r
+ //\r
+ // Still debugging this code. Not used in Darwin\r
+ //\r
+ case 'q':\r
+ // General Query Packets\r
+ if (AsciiStrnCmp (gInBuffer, "qSupported", 10) == 0) {\r
+ // return what we currently support, we don't parse what gdb suports\r
+ AsciiSPrint (gOutBuffer, MAX_BUF_SIZE, "qXfer:libraries:read+;PacketSize=%d", MAX_BUF_SIZE);\r
+ SendPacket (gOutBuffer);\r
+ } else if (AsciiStrnCmp (gInBuffer, "qXfer:libraries:read::", 22) == 0) {\r
+ // ‘qXfer:libraries:read::offset,length\r
+ // gInBuffer[22] is offset string, ++Ptr is length string’\r
+ for (Ptr = &gInBuffer[22]; *Ptr != ','; Ptr++);\r
+\r
+ // Not sure if multi-radix support is required. Currently only support decimal\r
+ QxferLibrary (AsciiStrHexToUintn (&gInBuffer[22]), AsciiStrHexToUintn (++Ptr));\r
+ } if (AsciiStrnCmp (gInBuffer, "qOffsets", 10) == 0) {\r
+ AsciiSPrint (gOutBuffer, MAX_BUF_SIZE, "Text=1000;Data=f000;Bss=f000");\r
+ SendPacket (gOutBuffer);\r
+ } else {\r
+ //Send empty packet\r
+ SendNotSupported ();\r
+ }\r
+ break;\r
+\r
+ case 's':\r
+ SingleStep (SystemContext, gInBuffer);\r
+ return;\r
+\r
+ case 'z':\r
+ RemoveBreakPoint (SystemContext, gInBuffer);\r
+ break;\r
+\r
+ case 'Z':\r
+ InsertBreakPoint (SystemContext, gInBuffer);\r
+ break;\r
+\r
+ default:\r
+ //Send empty packet\r
+ SendNotSupported ();\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Periodic callback for GDB. This function is used to catch a ctrl-c or other\r
+ break in type command from GDB.\r
+\r
+ @param SystemContext Register content at time of the call\r
+ **/\r
+VOID\r
+EFIAPI\r
+GdbPeriodicCallBack (\r
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
+ )\r
+{\r
+ //\r
+ // gCtrlCBreakFlag may have been set from a previous F response package\r
+ // and we set the global as we need to process it at a point where we\r
+ // can update the system context. If we are in the middle of processing\r
+ // a F Packet it is not safe to read the GDB serial stream so we need\r
+ // to skip it on this check\r
+ //\r
+ if (!gCtrlCBreakFlag && !gProcessingFPacket) {\r
+ //\r
+ // Ctrl-C was not pending so grab any pending characters and see if they\r
+ // are a Ctrl-c (0x03). If so set the Ctrl-C global.\r
+ //\r
+ while (TRUE) {\r
+ if (!GdbIsCharAvailable ()) {\r
+ //\r
+ // No characters are pending so exit the loop\r
+ //\r
+ break;\r
+ }\r
+\r
+ if (GdbGetChar () == 0x03) {\r
+ gCtrlCBreakFlag = TRUE;\r
+ //\r
+ // We have a ctrl-c so exit the loop\r
+ //\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (gCtrlCBreakFlag) {\r
+ //\r
+ // Update the context to force a single step trap when we exit the GDB\r
+ // stub. This will transfer control to GdbExceptionHandler () and let\r
+ // us break into the program. We don't want to break into the GDB stub.\r
+ //\r
+ AddSingleStep (SystemContext);\r
+ gCtrlCBreakFlag = FALSE;\r
+ }\r
+}\r