Add the beginning of a GDB based Debug Agent. IA-32 and X64 don't have low level interrupt code yet. I've been testing on ARM.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10334 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
816
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.c
Executable file
816
EmbeddedPkg/Library/GdbDebugAgent/GdbDebugAgent.c
Executable file
@ -0,0 +1,816 @@
|
||||
/** @file
|
||||
Debug Agent library implementition with empty functions.
|
||||
|
||||
Copyright (c) 2010, Intel Corporation
|
||||
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 "GdbDebugAgent.h"
|
||||
|
||||
|
||||
UINTN gMaxProcessorIndex = 0;
|
||||
|
||||
//
|
||||
// Buffers for basic gdb communication
|
||||
//
|
||||
CHAR8 gInBuffer[MAX_BUF_SIZE];
|
||||
CHAR8 gOutBuffer[MAX_BUF_SIZE];
|
||||
|
||||
|
||||
//
|
||||
// Globals for returning XML from qXfer:libraries:read packet
|
||||
//
|
||||
UINTN gPacketqXferLibraryOffset = 0;
|
||||
UINTN gEfiDebugImageTableEntry = 0;
|
||||
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'};
|
||||
|
||||
|
||||
// add-symbol-file c:/work/edk2/Build/BeagleBoard/DEBUG_ARMGCC/ARM/BeagleBoardPkg/Sec/Sec/DEBUG/BeagleBoardSec.dll 0x80008360
|
||||
CHAR8 *qXferHack = "<library name=\"c:/work/edk2/Build/BeagleBoard/DEBUG_ARMGCC/ARM/BeagleBoardPkg/Sec/Sec/DEBUG/BeagleBoardSec.dll\"><segment address=\"0x80008360\"/></library>";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
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
|
||||
)
|
||||
{
|
||||
gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', "<library-list>\n");
|
||||
gPacketqXferLibraryOffset += gXferObjectReadResponse ('m', qXferHack);
|
||||
gXferObjectReadResponse ('l', "</library-list>\n");
|
||||
gPacketqXferLibraryOffset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
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)) {
|
||||
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;
|
||||
|
||||
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 ("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
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) {
|
||||
SendError (GDB_EBADMEMADDRBUFSIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2 = 'm' + ','
|
||||
if (AsciiStrLen(PacketData) - AsciiStrLen(AddressBuffer) - 2 >= MAX_LENGTH_SIZE) {
|
||||
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) {
|
||||
SendError (GDB_EBADMEMADDRBUFSIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if message length is not too long
|
||||
if (AsciiStrLen(LengthBuffer) >= MAX_LENGTH_SIZE) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
return 22; //EINVAL: Invalid argument
|
||||
}
|
||||
|
||||
return 0; //0 = No error
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
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;
|
||||
|
||||
TSignalPtr = &TSignalBuffer[0];
|
||||
|
||||
//Construct TSignal packet
|
||||
*TSignalPtr++ = 'T';
|
||||
|
||||
//
|
||||
// replace _, or previous value, with Exception type
|
||||
//
|
||||
*TSignalPtr++ = mHexToStr [GdbExceptionType >> 4];
|
||||
*TSignalPtr++ = mHexToStr [GdbExceptionType & 0x0f];
|
||||
|
||||
ProcessorSendTSignal (SystemContext, GdbExceptionType, TSignalPtr, sizeof (TSignalBuffer) - 2);
|
||||
|
||||
SendPacket (TSignalBuffer);
|
||||
}
|
||||
|
||||
VOID
|
||||
GdbFWrite (
|
||||
IN UINTN Fd,
|
||||
IN CHAR8 *Data,
|
||||
IN UINTN DataSize
|
||||
)
|
||||
{
|
||||
CHAR8 Buffer[128];
|
||||
|
||||
AsciiSPrint (Buffer, sizeof (Buffer), "Fwrite,%x,%x,%x", Fd, Data, DataSize);
|
||||
SendPacket (Buffer);
|
||||
|
||||
for( ; ; ) {
|
||||
ReceivePacket (gInBuffer, MAX_BUF_SIZE);
|
||||
|
||||
switch (gInBuffer[0]) {
|
||||
case 'm':
|
||||
ReadFromMemory (gInBuffer);
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
WriteToMemory (gInBuffer);
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
GdbFPutString (
|
||||
IN CHAR8 *String
|
||||
)
|
||||
{
|
||||
UINTN Len = AsciiStrSize (String);
|
||||
|
||||
GdbFWrite (2, String, Len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
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 (ProcessorControlC (ExceptionType, SystemContext)) {
|
||||
// We tried to process a control C handler and there is nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
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 'D':
|
||||
// gdb wants to disconnect so return "OK" packet since.
|
||||
SendSuccess ();
|
||||
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) {
|
||||
// <20>qXfer:libraries:read::offset,length
|
||||
// gInBuffer[22] is offset string, ++Ptr is length string<6E>
|
||||
for (Ptr = &gInBuffer[22]; *Ptr != ','; Ptr++);
|
||||
|
||||
// Not sure if multi-radix support is required. Currently only support decimal
|
||||
QxferLibrary (AsciiStrHexToUintn (&gInBuffer[22]), AsciiStrHexToUintn (++Ptr));
|
||||
} else if (AsciiStrnCmp (gInBuffer, "qOffsets", 8) == 0) {
|
||||
AsciiSPrint (gOutBuffer, MAX_BUF_SIZE, "Text=1000;Data=f000;Bss=f000");
|
||||
SendPacket (gOutBuffer);
|
||||
} else if (AsciiStrnCmp (gInBuffer, "qAttached", 9) == 0) {
|
||||
// remote server attached to an existing process
|
||||
SendPacket ("1");
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user