summaryrefslogtreecommitdiff
path: root/EmbeddedPkg/GdbStub/GdbStub.c
diff options
context:
space:
mode:
Diffstat (limited to 'EmbeddedPkg/GdbStub/GdbStub.c')
-rw-r--r--EmbeddedPkg/GdbStub/GdbStub.c1257
1 files changed, 0 insertions, 1257 deletions
diff --git a/EmbeddedPkg/GdbStub/GdbStub.c b/EmbeddedPkg/GdbStub/GdbStub.c
deleted file mode 100644
index 213038ad08..0000000000
--- a/EmbeddedPkg/GdbStub/GdbStub.c
+++ /dev/null
@@ -1,1257 +0,0 @@
-/** @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);
-
- DEBUG ((EFI_D_INFO, "Debug Support Protocol ISA %x\n", DebugSupport->Isa));
- DEBUG ((EFI_D_INFO, "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 notifications 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 Index;
-
- for (Index = 0; Index < MaxEfiException () ; Index++) {
- if (gExceptionType[Index].Exception == EFIExceptionType) {
- return gExceptionType[Index].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;
-
- // Response 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
- information in the EFI Debug Image 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 interrupt 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 subsequent call will ask for the next available 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 transfer control to GdbExceptionHandler () and let
- // us break into the program. We don't want to break into the GDB stub.
- //
- AddSingleStep (SystemContext);
- gCtrlCBreakFlag = FALSE;
- }
-}