/** @file | |
Processor specific parts of the GDB stub | |
Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <GdbStubInternal.h> | |
// | |
// Array of exception types that need to be hooked by the debugger | |
// | |
EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = { | |
{ EXCEPT_X64_DIVIDE_ERROR, GDB_SIGFPE }, | |
{ EXCEPT_X64_DEBUG, GDB_SIGTRAP }, | |
{ EXCEPT_X64_NMI, GDB_SIGEMT }, | |
{ EXCEPT_X64_BREAKPOINT, GDB_SIGTRAP }, | |
{ EXCEPT_X64_OVERFLOW, GDB_SIGSEGV }, | |
{ EXCEPT_X64_BOUND, GDB_SIGSEGV }, | |
{ EXCEPT_X64_INVALID_OPCODE, GDB_SIGILL }, | |
{ EXCEPT_X64_DOUBLE_FAULT, GDB_SIGEMT }, | |
{ EXCEPT_X64_STACK_FAULT, GDB_SIGSEGV }, | |
{ EXCEPT_X64_GP_FAULT, GDB_SIGSEGV }, | |
{ EXCEPT_X64_PAGE_FAULT, GDB_SIGSEGV }, | |
{ EXCEPT_X64_FP_ERROR, GDB_SIGEMT }, | |
{ EXCEPT_X64_ALIGNMENT_CHECK, GDB_SIGEMT }, | |
{ EXCEPT_X64_MACHINE_CHECK, GDB_SIGEMT } | |
}; | |
// The offsets of registers SystemContextX64. | |
// The fields in the array are in the gdb ordering. | |
// HAVE TO DOUBLE-CHECK THE ORDER of the 24 regs | |
// | |
UINTN gRegisterOffsets[] = { | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rax), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rcx), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rdx), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rbx), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rsp), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rbp), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rsi), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rdi), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rip), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Rflags), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Cs), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Ss), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Ds), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Es), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Fs), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, Gs), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R8), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R9), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R10), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R11), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R12), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R13), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R14), | |
OFFSET_OF (EFI_SYSTEM_CONTEXT_X64, R15) | |
}; | |
/** | |
Return the number of entries in the gExceptionType[] | |
@retval UINTN, the number of entries in the gExceptionType[] array. | |
**/ | |
UINTN | |
MaxEfiException ( | |
VOID | |
) | |
{ | |
return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY); | |
} | |
/** | |
Return the number of entries in the gRegisters[] | |
@retval UINTN, the number of entries (registers) in the gRegisters[] array. | |
**/ | |
UINTN | |
MaxRegisterCount ( | |
VOID | |
) | |
{ | |
return sizeof (gRegisterOffsets)/sizeof (UINTN); | |
} | |
/** | |
Check to see if the ISA is supported. | |
ISA = Instruction Set Architecture | |
@retval TRUE if Isa is supported | |
**/ | |
BOOLEAN | |
CheckIsa ( | |
IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa | |
) | |
{ | |
return (BOOLEAN)(Isa == IsaX64); | |
} | |
/** | |
This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering | |
It is, by default, set to find the register pointer of the X64 member | |
@param SystemContext Register content at time of the exception | |
@param RegNumber The register to which we want to find a pointer | |
@retval the pointer to the RegNumber-th pointer | |
**/ | |
UINTN * | |
FindPointerToRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN RegNumber | |
) | |
{ | |
UINT8 *TempPtr; | |
TempPtr = ((UINT8 *)SystemContext.SystemContextX64) + gRegisterOffsets[RegNumber]; | |
return (UINTN *)TempPtr; | |
} | |
/** | |
Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr | |
@param SystemContext Register content at time of the exception | |
@param RegNumber the number of the register that we want to read | |
@param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on. | |
@retval the pointer to the next character of the output buffer that is available to be written on. | |
**/ | |
CHAR8 * | |
BasicReadRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN RegNumber, | |
IN CHAR8 *OutBufPtr | |
) | |
{ | |
UINTN RegSize; | |
RegSize = 0; | |
while (RegSize < 64) { | |
*OutBufPtr++ = mHexToStr[((*FindPointerToRegister (SystemContext, RegNumber) >> (RegSize+4)) & 0xf)]; | |
*OutBufPtr++ = mHexToStr[((*FindPointerToRegister (SystemContext, RegNumber) >> RegSize) & 0xf)]; | |
RegSize = RegSize + 8; | |
} | |
return OutBufPtr; | |
} | |
/** ‘p n’ | |
Reads the n-th register's value into an output buffer and sends it as a packet | |
@param SystemContext Register content at time of the exception | |
@param InBuffer Pointer to the input buffer received from gdb server | |
**/ | |
VOID | |
ReadNthRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN CHAR8 *InBuffer | |
) | |
{ | |
UINTN RegNumber; | |
CHAR8 OutBuffer[17]; // 1 reg=16 hex chars, and the end '\0' (escape seq) | |
CHAR8 *OutBufPtr; // pointer to the output buffer | |
RegNumber = AsciiStrHexToUintn (&InBuffer[1]); | |
if ((RegNumber < 0) || (RegNumber >= MaxRegisterCount ())) { | |
SendError (GDB_EINVALIDREGNUM); | |
return; | |
} | |
OutBufPtr = OutBuffer; | |
OutBufPtr = BasicReadRegister (SystemContext, RegNumber, OutBufPtr); | |
*OutBufPtr = '\0'; // the end of the buffer | |
SendPacket (OutBuffer); | |
} | |
/** ‘g’ | |
Reads the general registers into an output buffer and sends it as a packet | |
@param SystemContext Register content at time of the exception | |
**/ | |
VOID | |
EFIAPI | |
ReadGeneralRegisters ( | |
IN EFI_SYSTEM_CONTEXT SystemContext | |
) | |
{ | |
UINTN i; | |
CHAR8 OutBuffer[385]; // 24 regs, 16 hex chars each, and the end '\0' (escape seq) | |
CHAR8 *OutBufPtr; // pointer to the output buffer | |
OutBufPtr = OutBuffer; | |
for (i = 0; i < MaxRegisterCount (); i++) { | |
// there are only 24 registers to read | |
OutBufPtr = BasicReadRegister (SystemContext, i, OutBufPtr); | |
} | |
*OutBufPtr = '\0'; // the end of the buffer | |
SendPacket (OutBuffer); | |
} | |
/** | |
Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr | |
@param SystemContext Register content at time of the exception | |
@param RegNumber the number of the register that we want to write | |
@param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on. | |
@retval the pointer to the next character of the input buffer that can be used | |
**/ | |
CHAR8 * | |
BasicWriteRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN RegNumber, | |
IN CHAR8 *InBufPtr | |
) | |
{ | |
UINTN RegSize; | |
UINTN TempValue; // the value transferred from a hex char | |
UINT64 NewValue; // the new value of the RegNumber-th Register | |
NewValue = 0; | |
RegSize = 0; | |
while (RegSize < 64) { | |
TempValue = HexCharToInt (*InBufPtr++); | |
if (TempValue < 0) { | |
SendError (GDB_EBADMEMDATA); | |
return NULL; | |
} | |
NewValue += (TempValue << (RegSize+4)); | |
TempValue = HexCharToInt (*InBufPtr++); | |
if (TempValue < 0) { | |
SendError (GDB_EBADMEMDATA); | |
return NULL; | |
} | |
NewValue += (TempValue << RegSize); | |
RegSize = RegSize + 8; | |
} | |
*(FindPointerToRegister (SystemContext, RegNumber)) = NewValue; | |
return InBufPtr; | |
} | |
/** ‘P n...=r...’ | |
Writes the new value of n-th register received into the input buffer to the n-th register | |
@param SystemContext Register content at time of the exception | |
@param InBuffer Pointer to the input buffer received from gdb server | |
**/ | |
VOID | |
EFIAPI | |
WriteNthRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN CHAR8 *InBuffer | |
) | |
{ | |
UINTN RegNumber; | |
CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array | |
CHAR8 *RegNumBufPtr; | |
CHAR8 *InBufPtr; // pointer to the input buffer | |
// find the register number to write | |
InBufPtr = &InBuffer[1]; | |
RegNumBufPtr = RegNumBuffer; | |
while (*InBufPtr != '=') { | |
*RegNumBufPtr++ = *InBufPtr++; | |
} | |
*RegNumBufPtr = '\0'; | |
RegNumber = AsciiStrHexToUintn (RegNumBuffer); | |
// check if this is a valid Register Number | |
if ((RegNumber < 0) || (RegNumber >= MaxRegisterCount ())) { | |
SendError (GDB_EINVALIDREGNUM); | |
return; | |
} | |
InBufPtr++; // skips the '=' character | |
BasicWriteRegister (SystemContext, RegNumber, InBufPtr); | |
SendSuccess (); | |
} | |
/** ‘G XX...’ | |
Writes the new values received into the input buffer to the general registers | |
@param SystemContext Register content at time of the exception | |
@param InBuffer Pointer to the input buffer received from gdb server | |
**/ | |
VOID | |
EFIAPI | |
WriteGeneralRegisters ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN CHAR8 *InBuffer | |
) | |
{ | |
UINTN i; | |
CHAR8 *InBufPtr; /// pointer to the input buffer | |
// check to see if the buffer is the right size which is | |
// 1 (for 'G') + 16 (for 16 registers) * 8 ( for 8 hex chars each) = 385 | |
if (AsciiStrLen (InBuffer) != 385) { | |
// 24 regs, 16 hex chars each, and the end '\0' (escape seq) | |
// Bad message. Message is not the right length | |
SendError (GDB_EBADBUFSIZE); | |
return; | |
} | |
InBufPtr = &InBuffer[1]; | |
// Read the new values for the registers from the input buffer to an array, NewValueArray. | |
// The values in the array are in the gdb ordering | |
for (i = 0; i < MaxRegisterCount (); i++) { | |
// there are only 16 registers to write | |
InBufPtr = BasicWriteRegister (SystemContext, i, InBufPtr); | |
} | |
SendSuccess (); | |
} | |
/** | |
Insert Single Step in the SystemContext | |
@param SystemContext Register content at time of the exception | |
**/ | |
VOID | |
AddSingleStep ( | |
IN EFI_SYSTEM_CONTEXT SystemContext | |
) | |
{ | |
SystemContext.SystemContextX64->Rflags |= TF_BIT; // Setting the TF bit. | |
} | |
/** | |
Remove Single Step in the SystemContext | |
@param SystemContext Register content at time of the exception | |
**/ | |
VOID | |
RemoveSingleStep ( | |
IN EFI_SYSTEM_CONTEXT SystemContext | |
) | |
{ | |
SystemContext.SystemContextX64->Rflags &= ~TF_BIT; // clearing the TF bit. | |
} | |
/** ‘c [addr ]’ | |
Continue. addr is Address to resume. If addr is omitted, resume at current | |
Address. | |
@param SystemContext Register content at time of the exception | |
**/ | |
VOID | |
EFIAPI | |
ContinueAtAddress ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN CHAR8 *PacketData | |
) | |
{ | |
if (PacketData[1] != '\0') { | |
SystemContext.SystemContextX64->Rip = AsciiStrHexToUintn (&PacketData[1]); | |
} | |
} | |
/** ‘s [addr ]’ | |
Single step. addr is the Address at which to resume. If addr is omitted, resume | |
at same Address. | |
@param SystemContext Register content at time of the exception | |
**/ | |
VOID | |
EFIAPI | |
SingleStep ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN CHAR8 *PacketData | |
) | |
{ | |
if (PacketData[1] != '\0') { | |
SystemContext.SystemContextX64->Rip = AsciiStrHexToUintn (&PacketData[1]); | |
} | |
AddSingleStep (SystemContext); | |
} | |
/** | |
Returns breakpoint data address from DR0-DR3 based on the input breakpoint | |
number | |
@param SystemContext Register content at time of the exception | |
@param BreakpointNumber Breakpoint number | |
@retval Address Data address from DR0-DR3 based on the | |
breakpoint number. | |
**/ | |
UINTN | |
GetBreakpointDataAddress ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN BreakpointNumber | |
) | |
{ | |
UINTN Address; | |
if (BreakpointNumber == 1) { | |
Address = SystemContext.SystemContextIa32->Dr0; | |
} else if (BreakpointNumber == 2) { | |
Address = SystemContext.SystemContextIa32->Dr1; | |
} else if (BreakpointNumber == 3) { | |
Address = SystemContext.SystemContextIa32->Dr2; | |
} else if (BreakpointNumber == 4) { | |
Address = SystemContext.SystemContextIa32->Dr3; | |
} else { | |
Address = 0; | |
} | |
return Address; | |
} | |
/** | |
Returns currently detected breakpoint value based on the register | |
DR6 B0-B3 field. | |
If no breakpoint is detected then it returns 0. | |
@param SystemContext Register content at time of the exception | |
@retval {1-4} Currently detected breakpoint value | |
@retval 0 No breakpoint detected. | |
**/ | |
UINTN | |
GetBreakpointDetected ( | |
IN EFI_SYSTEM_CONTEXT SystemContext | |
) | |
{ | |
IA32_DR6 Dr6; | |
UINTN BreakpointNumber; | |
Dr6.UintN = SystemContext.SystemContextIa32->Dr6; | |
if (Dr6.Bits.B0 == 1) { | |
BreakpointNumber = 1; | |
} else if (Dr6.Bits.B1 == 1) { | |
BreakpointNumber = 2; | |
} else if (Dr6.Bits.B2 == 1) { | |
BreakpointNumber = 3; | |
} else if (Dr6.Bits.B3 == 1) { | |
BreakpointNumber = 4; | |
} else { | |
BreakpointNumber = 0; // No breakpoint detected | |
} | |
return BreakpointNumber; | |
} | |
/** | |
Returns Breakpoint type (InstructionExecution, DataWrite, DataRead | |
or DataReadWrite) based on the Breakpoint number | |
@param SystemContext Register content at time of the exception | |
@param BreakpointNumber Breakpoint number | |
@retval BREAK_TYPE Breakpoint type value read from register DR7 RWn | |
field. For unknown value, it returns NotSupported. | |
**/ | |
BREAK_TYPE | |
GetBreakpointType ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN BreakpointNumber | |
) | |
{ | |
IA32_DR7 Dr7; | |
BREAK_TYPE Type = NotSupported; // Default is NotSupported type | |
Dr7.UintN = SystemContext.SystemContextIa32->Dr7; | |
if (BreakpointNumber == 1) { | |
Type = (BREAK_TYPE)Dr7.Bits.RW0; | |
} else if (BreakpointNumber == 2) { | |
Type = (BREAK_TYPE)Dr7.Bits.RW1; | |
} else if (BreakpointNumber == 3) { | |
Type = (BREAK_TYPE)Dr7.Bits.RW2; | |
} else if (BreakpointNumber == 4) { | |
Type = (BREAK_TYPE)Dr7.Bits.RW3; | |
} | |
return Type; | |
} | |
/** | |
Parses Length and returns the length which DR7 LENn field accepts. | |
For example: If we receive 1-Byte length then we should return 0. | |
Zero gets written to DR7 LENn field. | |
@param Length Breakpoint length in Bytes (1 byte, 2 byte, 4 byte) | |
@retval Length Appropriate converted values which DR7 LENn field accepts. | |
**/ | |
UINTN | |
ConvertLengthData ( | |
IN UINTN Length | |
) | |
{ | |
if (Length == 1) { | |
// 1-Byte length | |
return 0; | |
} else if (Length == 2) { | |
// 2-Byte length | |
return 1; | |
} else if (Length == 4) { | |
// 4-Byte length | |
return 3; | |
} else { | |
// Undefined or 8-byte length | |
return 2; | |
} | |
} | |
/** | |
Finds the next free debug register. If all the registers are occupied then | |
EFI_OUT_OF_RESOURCES is returned. | |
@param SystemContext Register content at time of the exception | |
@param Register Register value (0 - 3 for the first free debug register) | |
@retval EFI_STATUS Appropriate status value. | |
**/ | |
EFI_STATUS | |
FindNextFreeDebugRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
OUT UINTN *Register | |
) | |
{ | |
IA32_DR7 Dr7; | |
Dr7.UintN = SystemContext.SystemContextIa32->Dr7; | |
if (Dr7.Bits.G0 == 0) { | |
*Register = 0; | |
} else if (Dr7.Bits.G1 == 0) { | |
*Register = 1; | |
} else if (Dr7.Bits.G2 == 0) { | |
*Register = 2; | |
} else if (Dr7.Bits.G3 == 0) { | |
*Register = 3; | |
} else { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Enables the debug register. Writes Address value to appropriate DR0-3 register. | |
Sets LENn, Gn, RWn bits in DR7 register. | |
@param SystemContext Register content at time of the exception | |
@param Register Register value (0 - 3) | |
@param Address Breakpoint address value | |
@param Type Breakpoint type (Instruction, Data write, | |
Data read or write etc.) | |
@retval EFI_STATUS Appropriate status value. | |
**/ | |
EFI_STATUS | |
EnableDebugRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN Register, | |
IN UINTN Address, | |
IN UINTN Length, | |
IN UINTN Type | |
) | |
{ | |
IA32_DR7 Dr7; | |
// Convert length data | |
Length = ConvertLengthData (Length); | |
// For Instruction execution, length should be 0 | |
// (Ref. Intel reference manual 18.2.4) | |
if ((Type == 0) && (Length != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle | |
// software breakpoint. We should send empty packet in both these cases. | |
if ((Type == (BREAK_TYPE)DataRead) || | |
(Type == (BREAK_TYPE)SoftwareBreakpoint)) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
// Read DR7 so appropriate Gn, RWn and LENn bits can be modified. | |
Dr7.UintN = SystemContext.SystemContextIa32->Dr7; | |
if (Register == 0) { | |
SystemContext.SystemContextIa32->Dr0 = Address; | |
Dr7.Bits.G0 = 1; | |
Dr7.Bits.RW0 = Type; | |
Dr7.Bits.LEN0 = Length; | |
} else if (Register == 1) { | |
SystemContext.SystemContextIa32->Dr1 = Address; | |
Dr7.Bits.G1 = 1; | |
Dr7.Bits.RW1 = Type; | |
Dr7.Bits.LEN1 = Length; | |
} else if (Register == 2) { | |
SystemContext.SystemContextIa32->Dr2 = Address; | |
Dr7.Bits.G2 = 1; | |
Dr7.Bits.RW2 = Type; | |
Dr7.Bits.LEN2 = Length; | |
} else if (Register == 3) { | |
SystemContext.SystemContextIa32->Dr3 = Address; | |
Dr7.Bits.G3 = 1; | |
Dr7.Bits.RW3 = Type; | |
Dr7.Bits.LEN3 = Length; | |
} else { | |
return EFI_INVALID_PARAMETER; | |
} | |
// Update Dr7 with appropriate Gn, RWn and LENn bits | |
SystemContext.SystemContextIa32->Dr7 = Dr7.UintN; | |
return EFI_SUCCESS; | |
} | |
/** | |
Returns register number 0 - 3 for the matching debug register. | |
This function compares incoming Address, Type, Length and | |
if there is a match then it returns the appropriate register number. | |
In case of mismatch, function returns EFI_NOT_FOUND message. | |
@param SystemContext Register content at time of the exception | |
@param Address Breakpoint address value | |
@param Length Breakpoint length value | |
@param Type Breakpoint type (Instruction, Data write, Data read | |
or write etc.) | |
@param Register Register value to be returned | |
@retval EFI_STATUS Appropriate status value. | |
**/ | |
EFI_STATUS | |
FindMatchingDebugRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN Address, | |
IN UINTN Length, | |
IN UINTN Type, | |
OUT UINTN *Register | |
) | |
{ | |
IA32_DR7 Dr7; | |
// Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle | |
// software breakpoint. We should send empty packet in both these cases. | |
if ((Type == (BREAK_TYPE)DataRead) || | |
(Type == (BREAK_TYPE)SoftwareBreakpoint)) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
// Convert length data | |
Length = ConvertLengthData (Length); | |
Dr7.UintN = SystemContext.SystemContextIa32->Dr7; | |
if ((Dr7.Bits.G0 == 1) && | |
(Dr7.Bits.LEN0 == Length) && | |
(Dr7.Bits.RW0 == Type) && | |
(Address == SystemContext.SystemContextIa32->Dr0)) | |
{ | |
*Register = 0; | |
} else if ((Dr7.Bits.G1 == 1) && | |
(Dr7.Bits.LEN1 == Length) && | |
(Dr7.Bits.RW1 == Type) && | |
(Address == SystemContext.SystemContextIa32->Dr1)) | |
{ | |
*Register = 1; | |
} else if ((Dr7.Bits.G2 == 1) && | |
(Dr7.Bits.LEN2 == Length) && | |
(Dr7.Bits.RW2 == Type) && | |
(Address == SystemContext.SystemContextIa32->Dr2)) | |
{ | |
*Register = 2; | |
} else if ((Dr7.Bits.G3 == 1) && | |
(Dr7.Bits.LEN3 == Length) && | |
(Dr7.Bits.RW3 == Type) && | |
(Address == SystemContext.SystemContextIa32->Dr3)) | |
{ | |
*Register = 3; | |
} else { | |
Print ((CHAR16 *)L"No match found..\n"); | |
return EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Disables the particular debug register. | |
@param SystemContext Register content at time of the exception | |
@param Register Register to be disabled | |
@retval EFI_STATUS Appropriate status value. | |
**/ | |
EFI_STATUS | |
DisableDebugRegister ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN UINTN Register | |
) | |
{ | |
IA32_DR7 Dr7; | |
UINTN Address = 0; | |
// Read DR7 register so appropriate Gn, RWn and LENn bits can be turned off. | |
Dr7.UintN = SystemContext.SystemContextIa32->Dr7; | |
if (Register == 0) { | |
SystemContext.SystemContextIa32->Dr0 = Address; | |
Dr7.Bits.G0 = 0; | |
Dr7.Bits.RW0 = 0; | |
Dr7.Bits.LEN0 = 0; | |
} else if (Register == 1) { | |
SystemContext.SystemContextIa32->Dr1 = Address; | |
Dr7.Bits.G1 = 0; | |
Dr7.Bits.RW1 = 0; | |
Dr7.Bits.LEN1 = 0; | |
} else if (Register == 2) { | |
SystemContext.SystemContextIa32->Dr2 = Address; | |
Dr7.Bits.G2 = 0; | |
Dr7.Bits.RW2 = 0; | |
Dr7.Bits.LEN2 = 0; | |
} else if (Register == 3) { | |
SystemContext.SystemContextIa32->Dr3 = Address; | |
Dr7.Bits.G3 = 0; | |
Dr7.Bits.RW3 = 0; | |
Dr7.Bits.LEN3 = 0; | |
} else { | |
return EFI_INVALID_PARAMETER; | |
} | |
// Update DR7 register so appropriate Gn, RWn and LENn bits can be turned off. | |
SystemContext.SystemContextIa32->Dr7 = Dr7.UintN; | |
return EFI_SUCCESS; | |
} | |
/** | |
‘Z1, [addr], [length]’ | |
‘Z2, [addr], [length]’ | |
‘Z3, [addr], [length]’ | |
‘Z4, [addr], [length]’ | |
Insert hardware breakpoint/watchpoint at address addr of size length | |
@param SystemContext Register content at time of the exception | |
@param *PacketData Pointer to the Payload data for the packet | |
**/ | |
VOID | |
EFIAPI | |
InsertBreakPoint ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN CHAR8 *PacketData | |
) | |
{ | |
UINTN Type; | |
UINTN Address; | |
UINTN Length; | |
UINTN Register; | |
EFI_STATUS Status; | |
BREAK_TYPE BreakType = NotSupported; | |
UINTN ErrorCode; | |
ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length); | |
if (ErrorCode > 0) { | |
SendError ((UINT8)ErrorCode); | |
return; | |
} | |
switch (Type) { | |
case 0: // Software breakpoint | |
BreakType = SoftwareBreakpoint; | |
break; | |
case 1: // Hardware breakpoint | |
BreakType = InstructionExecution; | |
break; | |
case 2: // Write watchpoint | |
BreakType = DataWrite; | |
break; | |
case 3: // Read watchpoint | |
BreakType = DataRead; | |
break; | |
case 4: // Access watchpoint | |
BreakType = DataReadWrite; | |
break; | |
default: | |
Print ((CHAR16 *)L"Insert breakpoint default: %x\n", Type); | |
SendError (GDB_EINVALIDBRKPOINTTYPE); | |
return; | |
} | |
// Find next free debug register | |
Status = FindNextFreeDebugRegister (SystemContext, &Register); | |
if (EFI_ERROR (Status)) { | |
Print ((CHAR16 *)L"No space left on device\n"); | |
SendError (GDB_ENOSPACE); | |
return; | |
} | |
// Write Address, length data at particular DR register | |
Status = EnableDebugRegister (SystemContext, Register, Address, Length, (UINTN)BreakType); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_UNSUPPORTED) { | |
Print ((CHAR16 *)L"Not supported\n"); | |
SendNotSupported (); | |
return; | |
} | |
Print ((CHAR16 *)L"Invalid argument\n"); | |
SendError (GDB_EINVALIDARG); | |
return; | |
} | |
SendSuccess (); | |
} | |
/** | |
‘z1, [addr], [length]’ | |
‘z2, [addr], [length]’ | |
‘z3, [addr], [length]’ | |
‘z4, [addr], [length]’ | |
Remove hardware breakpoint/watchpoint at address addr of size length | |
@param *PacketData Pointer to the Payload data for the packet | |
**/ | |
VOID | |
EFIAPI | |
RemoveBreakPoint ( | |
IN EFI_SYSTEM_CONTEXT SystemContext, | |
IN CHAR8 *PacketData | |
) | |
{ | |
UINTN Type; | |
UINTN Address; | |
UINTN Length; | |
UINTN Register; | |
BREAK_TYPE BreakType = NotSupported; | |
EFI_STATUS Status; | |
UINTN ErrorCode; | |
// Parse breakpoint packet data | |
ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length); | |
if (ErrorCode > 0) { | |
SendError ((UINT8)ErrorCode); | |
return; | |
} | |
switch (Type) { | |
case 0: // Software breakpoint | |
BreakType = SoftwareBreakpoint; | |
break; | |
case 1: // Hardware breakpoint | |
BreakType = InstructionExecution; | |
break; | |
case 2: // Write watchpoint | |
BreakType = DataWrite; | |
break; | |
case 3: // Read watchpoint | |
BreakType = DataRead; | |
break; | |
case 4: // Access watchpoint | |
BreakType = DataReadWrite; | |
break; | |
default: | |
SendError (GDB_EINVALIDBRKPOINTTYPE); | |
return; | |
} | |
// Find matching debug register | |
Status = FindMatchingDebugRegister (SystemContext, Address, Length, (UINTN)BreakType, &Register); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_UNSUPPORTED) { | |
Print ((CHAR16 *)L"Not supported.\n"); | |
SendNotSupported (); | |
return; | |
} | |
Print ((CHAR16 *)L"No matching register found.\n"); | |
SendError (GDB_ENOSPACE); | |
return; | |
} | |
// Remove breakpoint | |
Status = DisableDebugRegister (SystemContext, Register); | |
if (EFI_ERROR (Status)) { | |
Print ((CHAR16 *)L"Invalid argument.\n"); | |
SendError (GDB_EINVALIDARG); | |
return; | |
} | |
SendSuccess (); | |
} | |
VOID | |
InitializeProcessor ( | |
VOID | |
) | |
{ | |
} | |
BOOLEAN | |
ValidateAddress ( | |
IN VOID *Address | |
) | |
{ | |
return TRUE; | |
} | |
BOOLEAN | |
ValidateException ( | |
IN EFI_EXCEPTION_TYPE ExceptionType, | |
IN OUT EFI_SYSTEM_CONTEXT SystemContext | |
) | |
{ | |
return TRUE; | |
} |