123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 |
- /** @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>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #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 processed.
- (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;
- }
- }
|