]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EmbeddedPkg/GdbStub/GdbStub.c
Adding support for BeagleBoard.
[mirror_edk2.git] / EmbeddedPkg / GdbStub / GdbStub.c
diff --git a/EmbeddedPkg/GdbStub/GdbStub.c b/EmbeddedPkg/GdbStub/GdbStub.c
new file mode 100644 (file)
index 0000000..b121e41
--- /dev/null
@@ -0,0 +1,1264 @@
+/** @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.
+  
+  All rights reserved. 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;
+  }
+}