summaryrefslogtreecommitdiffstats
path: root/EmbeddedPkg/GdbStub
diff options
context:
space:
mode:
authorAJFISH <AJFISH@6f19259b-4bc3-4df7-8a09-765794883524>2009-12-06 01:57:05 +0000
committerAJFISH <AJFISH@6f19259b-4bc3-4df7-8a09-765794883524>2009-12-06 01:57:05 +0000
commit2ef2b01e07c02db339f34004445734a2dbdd80e1 (patch)
tree19532a6be8d8bdb0aef04bd00c1efb582f6dc841 /EmbeddedPkg/GdbStub
parentf7753a96ba1653ddd31b01c198a352f6332ac404 (diff)
downloadedk2-2ef2b01e07c02db339f34004445734a2dbdd80e1.tar.gz
edk2-2ef2b01e07c02db339f34004445734a2dbdd80e1.tar.bz2
edk2-2ef2b01e07c02db339f34004445734a2dbdd80e1.zip
Adding support for BeagleBoard.
ArmPkg - Supoprt for ARM specific things that can change as the architecture changes. Plus semihosting JTAG drivers. EmbeddedPkg - Generic support for an embeddded platform. Including a light weight command line shell. BeagleBoardPkg - Platform specifics for BeagleBoard. SD Card works, but USB has issues. Looks like a bug in the open source USB stack (Our internal stack works fine). git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@9518 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'EmbeddedPkg/GdbStub')
-rw-r--r--EmbeddedPkg/GdbStub/Arm/Processor.c717
-rw-r--r--EmbeddedPkg/GdbStub/GdbStub.c1264
-rw-r--r--EmbeddedPkg/GdbStub/GdbStub.inf78
-rw-r--r--EmbeddedPkg/GdbStub/GdbStubInternal.h746
-rw-r--r--EmbeddedPkg/GdbStub/Ia32/Processor.c993
-rw-r--r--EmbeddedPkg/GdbStub/SerialIo.c551
-rw-r--r--EmbeddedPkg/GdbStub/X64/Processor.c963
7 files changed, 5312 insertions, 0 deletions
diff --git a/EmbeddedPkg/GdbStub/Arm/Processor.c b/EmbeddedPkg/GdbStub/Arm/Processor.c
new file mode 100644
index 0000000000..e620d344e6
--- /dev/null
+++ b/EmbeddedPkg/GdbStub/Arm/Processor.c
@@ -0,0 +1,717 @@
+/** @file
+ Processor specific parts of the GDB stub
+
+ 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.
+
+**/
+/** @file
+
+ Copyright (c) 2008, Apple, Inc
+ 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 <Library/CacheMaintenanceLib.h>
+#include <Library/PrintLib.h>
+
+//
+// Array of exception types that need to be hooked by the debugger
+// (efi, gdb) //efi number
+//
+EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {
+ { EXCEPT_ARM_SOFTWARE_INTERRUPT, GDB_SIGTRAP }
+// { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },
+// { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },
+// { EXCEPT_ARM_DATA_ABORT, GDB_SIGEMT },
+// { EXCEPT_ARM_RESERVED, GDB_SIGILL }
+};
+
+// Shut up some annoying RVCT warnings
+#ifdef __CC_ARM
+#pragma diag_suppress 1296
+#endif
+
+UINTN gRegisterOffsets[] = {
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R0),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R1),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R2),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R3),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R4),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R5),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R6),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R7),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R8),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R9),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R10),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R11),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R12),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, SP),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, LR),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, PC),
+ 0x00000F01, // f0
+ 0x00000F02,
+ 0x00000F03,
+ 0x00000F11, // f1
+ 0x00000F12,
+ 0x00000F13,
+ 0x00000F21, // f2
+ 0x00000F22,
+ 0x00000F23,
+ 0x00000F31, // f3
+ 0x00000F32,
+ 0x00000F33,
+ 0x00000F41, // f4
+ 0x00000F42,
+ 0x00000F43,
+ 0x00000F51, // f5
+ 0x00000F52,
+ 0x00000F53,
+ 0x00000F61, // f6
+ 0x00000F62,
+ 0x00000F63,
+ 0x00000F71, // f7
+ 0x00000F72,
+ 0x00000F73,
+ 0x00000FFF, // fps
+ 0x00000FFF,
+ 0x00000FFF,
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)
+};
+
+// restore warnings for RVCT
+#ifdef __CC_ARM
+#pragma diag_default 1296
+#endif
+
+/**
+ 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
+ )
+{
+ if (Isa == IsaArm) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+/**
+ 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 ARM 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;
+ ASSERT(gRegisterOffsets[RegNumber] < 0xF00);
+ TempPtr = ((UINT8 *)SystemContext.SystemContextArm) + gRegisterOffsets[RegNumber];
+ return (UINT32 *)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;
+ CHAR8 Char;
+
+ if (gRegisterOffsets[RegNumber] > 0xF00) {
+ AsciiSPrint(OutBufPtr, 9, "00000000");
+ OutBufPtr += 8;
+ return OutBufPtr;
+ }
+
+ RegSize = 0;
+ while (RegSize < 32) {
+ Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)];
+ if ((Char >= 'A') && (Char <= 'F')) {
+ Char = Char - 'A' + 'a';
+ }
+ *OutBufPtr++ = Char;
+
+ Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)];
+ if ((Char >= 'A') && (Char <= 'F')) {
+ Char = Char - 'A' + 'a';
+ }
+ *OutBufPtr++ = Char;
+
+ 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[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
+ CHAR8 *OutBufPtr; // pointer to the output buffer
+
+ RegNumber = AsciiStrHexToUintn (&InBuffer[1]);
+
+ if (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 Index;
+ CHAR8 *OutBuffer;
+ CHAR8 *OutBufPtr;
+ UINTN RegisterCount = MaxRegisterCount();
+
+ // It is not safe to allocate pool here....
+ OutBuffer = AllocatePool((RegisterCount * 8) + 1); // 8 bytes per register in string format plus a null to terminate
+ OutBufPtr = OutBuffer;
+ for (Index = 0; Index < RegisterCount; Index++) {
+ OutBufPtr = BasicReadRegister (SystemContext, Index, OutBufPtr);
+ }
+
+ *OutBufPtr = '\0';
+ SendPacket(OutBuffer);
+ FreePool(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
+ UINT32 NewValue; // the new value of the RegNumber-th Register
+
+ if (gRegisterOffsets[RegNumber] > 0xF00) {
+ return InBufPtr + 8;
+ }
+
+ NewValue = 0;
+ RegSize = 0;
+ while (RegSize < 32) {
+ TempValue = HexCharToInt(*InBufPtr++);
+
+ if ((INTN)TempValue < 0) {
+ SendError (GDB_EBADMEMDATA);
+ return NULL;
+ }
+
+ NewValue += (TempValue << (RegSize+4));
+ TempValue = HexCharToInt(*InBufPtr++);
+
+ if ((INTN)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 Ponter to the input buffer received from gdb server
+ **/
+VOID
+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 >= 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
+ UINTN MinLength;
+ UINTN RegisterCount = MaxRegisterCount();
+
+ MinLength = (RegisterCount * 8) + 1; // 'G' plus the registers in ASCII format
+
+ if (AsciiStrLen(InBuffer) < MinLength) {
+ //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 < RegisterCount; i++) {
+ InBufPtr = BasicWriteRegister (SystemContext, i, InBufPtr);
+ }
+
+ SendSuccess ();
+}
+
+// What about Thumb?
+// Use SWI 0xdbdbdb as the debug instruction
+#define GDB_ARM_BKPT 0xefdbdbdb
+
+BOOLEAN mSingleStepActive = FALSE;
+UINT32 mSingleStepPC;
+UINT32 mSingleStepData;
+UINTN mSingleStepDataSize;
+
+typedef struct {
+ LIST_ENTRY Link;
+ UINT64 Signature;
+ UINT32 Address;
+ UINT32 Instruction;
+} ARM_SOFTWARE_BREAKPOINT;
+
+#define ARM_SOFTWARE_BREAKPOINT_SIGNATURE SIGNATURE_64('A', 'R', 'M', 'B', 'R', 'K', 'P', 'T')
+#define ARM_SOFTWARE_BREAKPOINT_FROM_LINK(a) CR(a, ARM_SOFTWARE_BREAKPOINT, Link, ARM_SOFTWARE_BREAKPOINT_SIGNATURE)
+
+LIST_ENTRY BreakpointList;
+
+/**
+ Insert Single Step in the SystemContext
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+AddSingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ if (mSingleStepActive) {
+ // Currently don't support nesting
+ return;
+ }
+ mSingleStepActive = TRUE;
+
+ mSingleStepPC = SystemContext.SystemContextArm->PC;
+
+ mSingleStepDataSize = sizeof (UINT32);
+ mSingleStepData = (*(UINT32 *)mSingleStepPC);
+ *(UINT32 *)mSingleStepPC = GDB_ARM_BKPT;
+ if (*(UINT32 *)mSingleStepPC != GDB_ARM_BKPT) {
+ // For some reason our breakpoint did not take
+ mSingleStepActive = FALSE;
+ }
+
+ InvalidateInstructionCacheRange((VOID *)mSingleStepPC, mSingleStepDataSize);
+ //DEBUG((EFI_D_ERROR, "AddSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, mSingleStepData, *(UINT32 *)mSingleStepPC));
+}
+
+
+/**
+ Remove Single Step in the SystemContext
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+RemoveSingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ if (!mSingleStepActive) {
+ return;
+ }
+
+ if (mSingleStepDataSize == sizeof (UINT16)) {
+ *(UINT16 *)mSingleStepPC = (UINT16)mSingleStepData;
+ } else {
+ //DEBUG((EFI_D_ERROR, "RemoveSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, *(UINT32 *)mSingleStepPC, mSingleStepData));
+ *(UINT32 *)mSingleStepPC = mSingleStepData;
+ }
+ InvalidateInstructionCacheRange((VOID *)mSingleStepPC, mSingleStepDataSize);
+ mSingleStepActive = FALSE;
+}
+
+
+
+/** ‘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.SystemContextArm->PC = 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
+ )
+{
+ SendNotSupported();
+}
+
+UINTN
+GetBreakpointDataAddress (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN BreakpointNumber
+ )
+{
+ return 0;
+}
+
+UINTN
+GetBreakpointDetected (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ return 0;
+}
+
+BREAK_TYPE
+GetBreakpointType (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN BreakpointNumber
+ )
+{
+ return NotSupported;
+}
+
+ARM_SOFTWARE_BREAKPOINT *
+SearchBreakpointList (
+ IN UINT32 Address
+ )
+{
+ LIST_ENTRY *Current;
+ ARM_SOFTWARE_BREAKPOINT *Breakpoint;
+
+ Current = GetFirstNode(&BreakpointList);
+ while (!IsNull(&BreakpointList, Current)) {
+ Breakpoint = ARM_SOFTWARE_BREAKPOINT_FROM_LINK(Current);
+
+ if (Address == Breakpoint->Address) {
+ return Breakpoint;
+ }
+
+ Current = GetNextNode(&BreakpointList, Current);
+ }
+
+ return NULL;
+}
+
+VOID
+SetBreakpoint (
+ IN UINT32 Address
+ )
+{
+ ARM_SOFTWARE_BREAKPOINT *Breakpoint;
+
+ Breakpoint = SearchBreakpointList(Address);
+
+ if (Breakpoint != NULL) {
+ return;
+ }
+
+ // create and fill breakpoint structure
+ Breakpoint = AllocatePool(sizeof(ARM_SOFTWARE_BREAKPOINT));
+
+ Breakpoint->Signature = ARM_SOFTWARE_BREAKPOINT_SIGNATURE;
+ Breakpoint->Address = Address;
+ Breakpoint->Instruction = *(UINT32 *)Address;
+
+ // Add it to the list
+ InsertTailList(&BreakpointList, &Breakpoint->Link);
+
+ // Insert the software breakpoint
+ *(UINT32 *)Address = GDB_ARM_BKPT;
+ InvalidateInstructionCacheRange((VOID *)Address, 4);
+
+ //DEBUG((EFI_D_ERROR, "SetBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, Breakpoint->Instruction, *(UINT32 *)Address));
+}
+
+VOID
+ClearBreakpoint (
+ IN UINT32 Address
+ )
+{
+ ARM_SOFTWARE_BREAKPOINT *Breakpoint;
+
+ Breakpoint = SearchBreakpointList(Address);
+
+ if (Breakpoint == NULL) {
+ return;
+ }
+
+ // Add it to the list
+ RemoveEntryList(&Breakpoint->Link);
+
+ // Restore the original instruction
+ *(UINT32 *)Address = Breakpoint->Instruction;
+ InvalidateInstructionCacheRange((VOID *)Address, 4);
+
+ //DEBUG((EFI_D_ERROR, "ClearBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, GDB_ARM_BKPT, *(UINT32 *)Address));
+
+ FreePool(Breakpoint);
+}
+
+VOID
+EFIAPI
+InsertBreakPoint (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ UINTN Type;
+ UINTN Address;
+ UINTN Length;
+ UINTN ErrorCode;
+
+ ErrorCode = ParseBreakpointPacket(PacketData, &Type, &Address, &Length);
+ if (ErrorCode > 0) {
+ SendError ((UINT8)ErrorCode);
+ return;
+ }
+
+ switch (Type) {
+ case 0: //Software breakpoint
+ break;
+
+ default :
+ DEBUG((EFI_D_ERROR, "Insert breakpoint default: %x\n", Type));
+ SendError (GDB_EINVALIDBRKPOINTTYPE);
+ return;
+ }
+
+ SetBreakpoint(Address);
+
+ SendSuccess ();
+}
+
+VOID
+EFIAPI
+RemoveBreakPoint (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ )
+{
+ UINTN Type;
+ UINTN Address;
+ UINTN Length;
+ 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
+ break;
+
+ default:
+ SendError (GDB_EINVALIDBRKPOINTTYPE);
+ return;
+ }
+
+ ClearBreakpoint(Address);
+
+ SendSuccess ();
+}
+
+VOID
+InitializeProcessor (
+ VOID
+ )
+{
+ // Initialize breakpoint list
+ InitializeListHead(&BreakpointList);
+}
+
+BOOLEAN
+ValidateAddress (
+ IN VOID *Address
+ )
+{
+ if ((UINT32)Address < 0x80000000) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+BOOLEAN
+ValidateException (
+ IN EFI_EXCEPTION_TYPE ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ UINT32 ExceptionAddress;
+ UINT32 Instruction;
+
+ // Is it a debugger SWI?
+ ExceptionAddress = SystemContext.SystemContextArm->PC -= 4;
+ Instruction = *(UINT32 *)ExceptionAddress;
+ if (Instruction != GDB_ARM_BKPT) {
+ return FALSE;
+ }
+
+ // Special for SWI-based exception handling. SWI sets up the context
+ // to return to the instruction following the SWI instruction - NOT what we want
+ // for a debugger!
+ SystemContext.SystemContextArm->PC = ExceptionAddress;
+
+ return TRUE;
+}
+
diff --git a/EmbeddedPkg/GdbStub/GdbStub.c b/EmbeddedPkg/GdbStub/GdbStub.c
new file mode 100644
index 0000000000..b121e413f3
--- /dev/null
+++ b/EmbeddedPkg/GdbStub/GdbStub.c
@@ -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;
+ }
+}
diff --git a/EmbeddedPkg/GdbStub/GdbStub.inf b/EmbeddedPkg/GdbStub/GdbStub.inf
new file mode 100644
index 0000000000..2b54f0abef
--- /dev/null
+++ b/EmbeddedPkg/GdbStub/GdbStub.inf
@@ -0,0 +1,78 @@
+#%HEADER%
+#/** @file
+# UEFI GDB stub
+#
+# This is a shell application that will display Hello World.
+# Copyright (c) 2008, Apple, Inc.
+#
+# 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.
+#
+#
+#**/
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = GdbStub
+ FILE_GUID = 1F2CCB4F-D817-404E-98E7-80E4851FB33E
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = GdbStubEntry
+
+[Sources.common]
+ GdbStub.c
+ SerialIo.c
+
+[Sources.ARM]
+ Arm/Processor.c
+
+[Sources.IA32]
+ Ia32/Processor.c
+
+[Sources.X64]
+ X64/Processor.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ DevicePathLib
+ PcdLib
+ GdbSerialLib
+ PrintLib
+ CacheMaintenanceLib
+
+
+[Protocols]
+ gEfiDebugSupportProtocolGuid
+ gEfiDebugPortProtocolGuid
+ gEfiSerialIoProtocolGuid
+
+[Guids]
+ gEfiDebugImageInfoTableGuid
+
+[FeaturePcd.common]
+ gEmbeddedTokenSpaceGuid.PcdGdbSerial
+
+[FixedPcd.common]
+ gEmbeddedTokenSpaceGuid.PcdGdbMaxPacketRetryCount
diff --git a/EmbeddedPkg/GdbStub/GdbStubInternal.h b/EmbeddedPkg/GdbStub/GdbStubInternal.h
new file mode 100644
index 0000000000..20231cff82
--- /dev/null
+++ b/EmbeddedPkg/GdbStub/GdbStubInternal.h
@@ -0,0 +1,746 @@
+/** @file
+ Private include file for GDB stub
+
+ Copyright (c) 2008-2009 Apple Inc. All rights reserved.<BR>
+
+ 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.
+
+**/
+
+#ifndef __GDB_STUB_INTERNAL__
+#define __GDB_STUB_INTERNAL__
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/PcdLib.h>
+#include <Library/GdbSerialLib.h>
+#include <Library/PrintLib.h>
+
+#include <Protocol/DebugSupport.h>
+#include <Protocol/SerialIo.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/LoadedImage.h>
+#include <Guid/DebugImageInfoTable.h>
+#include <IndustryStandard/PeImage.h>
+
+extern CONST CHAR8 mHexToStr[];
+
+// maximum size of input and output buffers
+// This value came from the show remote command of the gdb we tested against
+#define MAX_BUF_SIZE 2000
+
+// maximum size of address buffer
+#define MAX_ADDR_SIZE 32
+
+// maximum size of register number buffer
+#define MAX_REG_NUM_BUF_SIZE 32
+
+// maximum size of length buffer
+#define MAX_LENGTH_SIZE 32
+
+// maximum size of T signal members
+#define MAX_T_SIGNAL_SIZE 64
+
+// the mask used to clear all the cache
+#define TF_BIT 0x00000100
+
+
+//
+// GDB Signal definitions - generic names for interrupts
+//
+#define GDB_SIGILL 4 // Illegal instruction
+#define GDB_SIGTRAP 5 // Trace Trap (Breakpoint and SingleStep)
+#define GDB_SIGEMT 7 // Emulator Trap
+#define GDB_SIGFPE 8 // Floating point exception
+#define GDB_SIGSEGV 11 // Setgment violation, page fault
+
+
+//
+// GDB File I/O Error values, zero means no error
+// Includes all general GDB Unix like error values
+//
+#define GDB_EBADMEMADDRBUFSIZE 11 // the buffer that stores memory Address to be read from/written to is not the right size
+#define GDB_EBADMEMLENGBUFSIZE 12 // the buffer that stores Length is not the right size
+#define GDB_EBADMEMLENGTH 13 // Length, the given number of bytes to read or write, is not the right size
+#define GDB_EBADMEMDATA 14 // one of the bytes or nibbles of the memory is leess than 0
+#define GDB_EBADMEMDATASIZE 15 // the memory data, 'XX..', is too short or too long
+#define GDB_EBADBUFSIZE 21 // the buffer created is not the correct size
+#define GDB_EINVALIDARG 31 // argument is invalid
+#define GDB_ENOSPACE 41 //
+#define GDB_EINVALIDBRKPOINTTYPE 51 // the breakpoint type is not recognized
+#define GDB_EINVALIDREGNUM 61 // given register number is not valid: either <0 or >=Number of Registers
+#define GDB_EUNKNOWN 255 // unknown
+
+
+//
+// These devices are open by GDB so we can just read and write to them
+//
+#define GDB_STDIN 0x00
+#define GDB_STDOUT 0x01
+#define GDB_STDERR 0x02
+
+//
+//Define Register size for different architectures
+//
+#if defined (MDE_CPU_IA32)
+#define REG_SIZE 32
+#elif defined (MDE_CPU_X64)
+#define REG_SIZE 64
+#elif defined (MDE_CPU_ARM)
+#define REG_SIZE 32
+#endif
+
+#define GDB_SERIAL_DEV_SIGNATURE SIGNATURE_32 ('g', 'd', 'b', 's')
+
+typedef struct {
+ VENDOR_DEVICE_PATH VendorDevice;
+ UINT32 Index; // Suport more than one
+ EFI_DEVICE_PATH_PROTOCOL End;
+} GDB_SERIAL_DEVICE_PATH;
+
+//
+// Name: SERIAL_DEV
+// Purpose: To provide device specific information
+// Fields:
+// Signature UINTN: The identity of the serial device
+// SerialIo SERIAL_IO_PROTOCOL: Serial I/O protocol interface
+// SerialMode SERIAL_IO_MODE:
+// DevicePath EFI_DEVICE_PATH_PROTOCOL *: Device path of the serial device
+//
+typedef struct {
+ UINTN Signature;
+ EFI_HANDLE Handle;
+ EFI_SERIAL_IO_PROTOCOL SerialIo;
+ EFI_SERIAL_IO_MODE SerialMode;
+ GDB_SERIAL_DEVICE_PATH DevicePath;
+ INTN InFileDescriptor;
+ INTN OutFileDescriptor;
+} GDB_SERIAL_DEV;
+
+
+#define GDB_SERIAL_DEV_FROM_THIS(a) CR (a, GDB_SERIAL_DEV, SerialIo, GDB_SERIAL_DEV_SIGNATURE)
+
+
+typedef struct {
+ EFI_EXCEPTION_TYPE Exception;
+ UINT8 SignalNo;
+} EFI_EXCEPTION_TYPE_ENTRY;
+
+
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+
+//
+// Byte packed structure for DR6
+// 32-bits on IA-32
+// 64-bits on X64. The upper 32-bits on X64 are reserved
+//
+typedef union {
+ struct {
+ UINT32 B0:1; // Breakpoint condition detected
+ UINT32 B1:1; // Breakpoint condition detected
+ UINT32 B2:1; // Breakpoint condition detected
+ UINT32 B3:1; // Breakpoint condition detected
+ UINT32 Reserved_1:9; // Reserved
+ UINT32 BD:1; // Debug register access detected
+ UINT32 BS:1; // Single step
+ UINT32 BT:1; // Task switch
+ UINT32 Reserved_2:16; // Reserved
+ } Bits;
+ UINTN UintN;
+} IA32_DR6;
+
+//
+// Byte packed structure for DR7
+// 32-bits on IA-32
+// 64-bits on X64. The upper 32-bits on X64 are reserved
+//
+typedef union {
+ struct {
+ UINT32 L0:1; // Local breakpoint enable
+ UINT32 G0:1; // Global breakpoint enable
+ UINT32 L1:1; // Local breakpoint enable
+ UINT32 G1:1; // Global breakpoint enable
+ UINT32 L2:1; // Local breakpoint enable
+ UINT32 G2:1; // Global breakpoint enable
+ UINT32 L3:1; // Local breakpoint enable
+ UINT32 G3:1; // Global breakpoint enable
+ UINT32 LE:1; // Local exact breakpoint enable
+ UINT32 GE:1; // Global exact breakpoint enable
+ UINT32 Reserved_1:3; // Reserved
+ UINT32 GD:1; // Global detect enable
+ UINT32 Reserved_2:2; // Reserved
+ UINT32 RW0:2; // Read/Write field
+ UINT32 LEN0:2; // Length field
+ UINT32 RW1:2; // Read/Write field
+ UINT32 LEN1:2; // Length field
+ UINT32 RW2:2; // Read/Write field
+ UINT32 LEN2:2; // Length field
+ UINT32 RW3:2; // Read/Write field
+ UINT32 LEN3:2; // Length field
+ } Bits;
+ UINTN UintN;
+} IA32_DR7;
+
+#endif /* if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) */
+
+typedef enum {
+ InstructionExecution, //Hardware breakpoint
+ DataWrite, //watch
+ DataRead, //rwatch
+ DataReadWrite, //awatch
+ SoftwareBreakpoint, //Software breakpoint
+ NotSupported
+} BREAK_TYPE;
+
+//
+// Array of exception types that need to be hooked by the debugger
+//
+extern EFI_EXCEPTION_TYPE_ENTRY gExceptionType[];
+
+//
+// Set TRUE if F Reply package signals a ctrl-c. We can not process the Ctrl-c
+// here we need to wait for the periodic callback to do this.
+//
+extern BOOLEAN gCtrlCBreakFlag;
+
+//
+// If the periodic callback is called while we are processing an F packet we need
+// to let the callback know to not read from the serail stream as it could steal
+// characters from the F reponse packet
+//
+extern BOOLEAN gProcessingFPacket;
+
+
+// The offsets of registers SystemContext.
+// The fields in the array are in the gdb ordering.
+//
+extern UINTN gRegisterOffsets[];
+
+/**
+ Return the number of entries in the gExceptionType[]
+
+ @retval UINTN, the number of entries in the gExceptionType[] array.
+ **/
+UINTN
+MaxEfiException (
+ VOID
+ );
+
+
+/**
+ Return the number of entries in the gRegisters[]
+
+ @retval UINTN, the number of entries (registers) in the gRegisters[] array.
+ **/
+UINTN
+MaxRegisterCount (
+ VOID
+ );
+
+
+/**
+ Check to see if the ISA is supported.
+ ISA = Instruction Set Architecture
+
+ @retval TRUE if Isa is supported,
+ FALSE otherwise.
+ **/
+BOOLEAN
+CheckIsa (
+ IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
+ );
+
+
+/**
+ 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
+ );
+
+
+/**
+ 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
+ );
+
+
+/**
+ Empties the given buffer
+ @param *Buf pointer to the first element in buffer to be emptied
+ **/
+VOID
+EmptyBuffer (
+ IN CHAR8 *Buf
+ );
+
+
+/**
+ 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
+ );
+
+
+/** '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
+ );
+
+
+/**
+ Send 'OK' when the function is done executing successfully.
+ **/
+VOID
+SendSuccess (
+ VOID
+ );
+
+
+/**
+ Send empty packet to specify that particular command/functionality is not supported.
+ **/
+VOID
+SendNotSupported (
+ VOID
+ );
+
+/** ‘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 This is the input buffer received from gdb server
+ **/
+VOID
+ReadNthRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ );
+
+
+/** ‘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
+ReadGeneralRegisters (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ );
+
+
+/** ‘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 This is the input buffer received from gdb server
+ **/
+VOID
+WriteNthRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ );
+
+
+/** ‘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
+WriteGeneralRegisters (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ );
+
+
+/** ‘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.
+
+ @param *PacketData Pointer to Payload data for the packet
+ **/
+VOID
+ReadFromMemory (
+ IN CHAR8 *PacketData
+ );
+
+
+/** ‘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.
+
+ @param PacketData Pointer to Payload data for the packet
+ **/
+VOID
+WriteToMemory (
+ IN CHAR8 *PacketData
+ );
+
+
+/** ‘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
+ @param *PacketData Pointer to PacketData
+ **/
+
+VOID
+ContinueAtAddress (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ );
+
+
+/** ‘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
+ @param PacketData Pointer to Payload data for the packet
+ **/
+VOID
+SingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ );
+
+/**
+ Insert Single Step in the SystemContext
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+AddSingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ );
+
+/**
+ Remove Single Step in the SystemContext
+
+ @param SystemContext Register content at time of the exception
+ **/
+VOID
+RemoveSingleStep (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ );
+
+
+/**
+ ‘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
+ );
+
+
+/**
+ ‘z1, [addr], [length]’
+ ‘z2, [addr], [length]’
+ ‘z3, [addr], [length]’
+ ‘z4, [addr], [length]’
+
+ Remove 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
+RemoveBreakPoint(
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *PacketData
+ );
+
+
+/**
+ 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
+ );
+
+
+/**
+ 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
+ );
+
+
+/**
+ Make two serail consoles: 1) StdIn and StdOut via GDB. 2) StdErr via GDB.
+
+ These console show up on the remote system running GDB
+
+**/
+
+VOID
+GdbInitializeSerialConsole (
+ VOID
+ );
+
+
+/**
+ 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
+ );
+
+
+/**
+ 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
+ );
+
+
+/**
+ Read data from a FileDescriptor. On success number of bytes read is returned. Zero indicates
+ the end of a file. On error -1 is returned. If count is zero, GdbRead returns zero.
+
+ @param FileDescriptor Device to talk to.
+ @param Buffer Buffer to hold Count bytes that were read
+ @param Count Number of bytes to transfer.
+
+ @retval -1 Error
+ @retval {other} Number of bytes read.
+
+**/
+INTN
+GdbRead (
+ IN INTN FileDescriptor,
+ OUT VOID *Buffer,
+ IN UINTN Count
+ );
+
+
+/**
+ Write data to a FileDescriptor. On success number of bytes written is returned. Zero indicates
+ nothing was written. On error -1 is returned.
+
+ @param FileDescriptor Device to talk to.
+ @param Buffer Buffer to hold Count bytes that are to be written
+ @param Count Number of bytes to transfer.
+
+ @retval -1 Error
+ @retval {other} Number of bytes written.
+
+**/
+INTN
+GdbWrite (
+ IN INTN FileDescriptor,
+ OUT CONST VOID *Buffer,
+ IN UINTN Count
+ );
+
+UINTN *
+FindPointerToRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber
+ );
+
+CHAR8 *
+BasicReadRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber,
+ IN CHAR8 *OutBufPtr
+ );
+
+VOID
+TransferFromInBufToMem (
+ IN UINTN Length,
+ IN UINT8 *Address,
+ IN CHAR8 *NewData
+ );
+
+VOID
+TransferFromMemToOutBufAndSend (
+ IN UINTN Length,
+ IN UINT8 *Address
+ );
+
+CHAR8 *
+BasicWriteRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN RegNumber,
+ IN CHAR8 *InBufPtr
+ );
+
+VOID
+PrintReg (
+ EFI_SYSTEM_CONTEXT SystemContext
+ );
+
+UINTN
+ParseBreakpointPacket (
+ IN CHAR8 *PacketData,
+ OUT UINTN *Type,
+ OUT UINTN *Address,
+ OUT UINTN *Length
+ );
+
+UINTN
+GetBreakpointDataAddress (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN BreakpointNumber
+ );
+
+UINTN
+GetBreakpointDetected (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ );
+
+BREAK_TYPE
+GetBreakpointType (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN BreakpointNumber
+ );
+
+UINTN
+ConvertLengthData (
+ IN UINTN Length
+ );
+
+EFI_STATUS
+FindNextFreeDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ OUT UINTN *Register
+ );
+
+EFI_STATUS
+EnableDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN Register,
+ IN UINTN Address,
+ IN UINTN Length,
+ IN UINTN Type
+ );
+
+EFI_STATUS
+FindMatchingDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN Address,
+ IN UINTN Length,
+ IN UINTN Type,
+ OUT UINTN *Register
+ );
+
+EFI_STATUS
+DisableDebugRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN UINTN Register
+ );
+
+VOID
+InitializeProcessor (
+ VOID
+ );
+
+BOOLEAN
+ValidateAddress (
+ IN VOID *Address
+ );
+
+BOOLEAN
+ValidateException (
+ IN EFI_EXCEPTION_TYPE ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ );
+
+#endif
diff --git a/EmbeddedPkg/GdbStub/Ia32/Processor.c b/EmbeddedPkg/GdbStub/Ia32/Processor.c
new file mode 100644
index 0000000000..90a2224edf
--- /dev/null
+++ b/EmbeddedPkg/GdbStub/Ia32/Processor.c
@@ -0,0 +1,993 @@
+/** @file
+ Processor specific parts of the GDB stub
+
+ 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>
+
+//
+// Array of exception types that need to be hooked by the debugger
+// {EFI mapping, GDB mapping}
+//
+EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {
+ { EXCEPT_IA32_DIVIDE_ERROR, GDB_SIGFPE },
+ { EXCEPT_IA32_DEBUG, GDB_SIGTRAP },
+ { EXCEPT_IA32_NMI, GDB_SIGEMT },
+ { EXCEPT_IA32_BREAKPOINT, GDB_SIGTRAP },
+ { EXCEPT_IA32_OVERFLOW, GDB_SIGSEGV },
+ { EXCEPT_IA32_BOUND, GDB_SIGSEGV },
+ { EXCEPT_IA32_INVALID_OPCODE, GDB_SIGILL },
+ { EXCEPT_IA32_DOUBLE_FAULT, GDB_SIGEMT },
+ { EXCEPT_IA32_STACK_FAULT, GDB_SIGSEGV },
+ { EXCEPT_IA32_GP_FAULT, GDB_SIGSEGV },
+ { EXCEPT_IA32_PAGE_FAULT, GDB_SIGSEGV },
+ { EXCEPT_IA32_FP_ERROR, GDB_SIGEMT },
+ { EXCEPT_IA32_ALIGNMENT_CHECK, GDB_SIGEMT },
+ { EXCEPT_IA32_MACHINE_CHECK, GDB_SIGEMT }
+};
+
+
+// The offsets of registers SystemContext.
+// The fields in the array are in the gdb ordering.
+//
+//16 regs
+UINTN gRegisterOffsets[] = {
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eax),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ecx),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Edx),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ebx),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Esp),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ebp),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Esi),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Edi),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eip),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eflags),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Cs),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ss),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ds),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Es),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Fs),
+ OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Gs)
+};
+
+
+//Debug only..
+VOID
+PrintReg (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ Print ((CHAR16 *)L"EAX: %x ", SystemContext.SystemContextIa32->Eax);
+ Print ((CHAR16 *)L"ECX: %x ", SystemContext.SystemContextIa32->Ecx);
+ Print ((CHAR16 *)L"EDX: %x ", SystemContext.SystemContextIa32->Edx);
+ Print ((CHAR16 *)L"EBX: %x ", SystemContext.SystemContextIa32->Ebx);
+ Print ((CHAR16 *)L"ESP: %x ", SystemContext.SystemContextIa32->Esp);
+ Print ((CHAR16 *)L"EBP: %x ", SystemContext.SystemContextIa32->Ebp);
+ Print ((CHAR16 *)L"ESI: %x ", SystemContext.SystemContextIa32->Esi);
+ Print ((CHAR16 *)L"EDI: %x ", SystemContext.SystemContextIa32->Edi);
+ Print ((CHAR16 *)L"EIP: %x\n", SystemContext.SystemContextIa32->Eip);
+ Print ((CHAR16 *)L"EFlags: %x\n", SystemContext.SystemContextIa32->Eflags);
+}
+
+//Debug only..
+VOID
+PrintDRreg (
+ IN EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ Print ((CHAR16 *)L"DR0: %x ", SystemContext.SystemContextIa32->Dr0);
+ Print ((CHAR16 *)L"DR1: %x ", SystemContext.SystemContextIa32->Dr1);
+ Print ((CHAR16 *)L"DR2: %x ", SystemContext.SystemContextIa32->Dr2);
+ Print ((CHAR16 *)L"DR3: %x ", SystemContext.SystemContextIa32->Dr3);
+ Print ((CHAR16 *)L"DR6: %x ", SystemContext.SystemContextIa32->Dr6);
+ Print ((CHAR16 *)L"DR7: %x\n", SystemContext.SystemContextIa32->Dr7);
+}
+
+
+/**
+ 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,
+ FALSE otherwise.
+**/
+BOOLEAN
+CheckIsa (
+ IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
+ )
+{
+ return (BOOLEAN)(Isa == IsaIa32);
+}
+
+
+/**
+ 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 IA32 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.SystemContextIa32) + 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 < REG_SIZE) {
+ *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
+EFIAPI
+ReadNthRegister (
+ IN EFI_SYSTEM_CONTEXT SystemContext,
+ IN CHAR8 *InBuffer
+ )
+{
+ UINTN RegNumber;
+ CHAR8 OutBuffer[9]; // 1 reg=8 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[129]; // 16 regs, 8 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 16 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
+ UINT32 NewValue; // the new value of the RegNumber-th Register
+
+ NewValue = 0;
+ RegSize = 0;
+ while (RegSize < REG_SIZE) {
+ 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 Ponter 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) = 129
+ if (AsciiStrLen(InBuffer) != 129) { // 16 regs, 8 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.SystemContextIa32->Eflags |= 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.SystemContextIa32->Eflags &= ~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.SystemContextIa32->Eip = 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.SystemContextIa32->Eip = 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 maching 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;
+}
+
diff --git a/EmbeddedPkg/GdbStub/SerialIo.c b/EmbeddedPkg/GdbStub/SerialIo.c
new file mode 100644
index 0000000000..5afaed06b9
--- /dev/null
+++ b/EmbeddedPkg/GdbStub/SerialIo.c
@@ -0,0 +1,551 @@
+/** @file
+ Serial IO Abstraction for GDB stub. This allows an EFI consoles that shows up on the system
+ running GDB. One consle for error information and another console for user input/output.
+
+ Basic packet format is $packet-data#checksum. So every comand has 4 bytes of overhead: $,
+ #, 0, 0. The 0 and 0 are the ascii characters for the checksum.
+
+
+ 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>
+
+//
+// Set TRUE if F Reply package signals a ctrl-c. We can not process the Ctrl-c
+// here we need to wait for the periodic callback to do this.
+//
+BOOLEAN gCtrlCBreakFlag = FALSE;
+
+//
+// If the periodic callback is called while we are processing an F packet we need
+// to let the callback know to not read from the serail stream as it could steal
+// characters from the F reponse packet
+//
+BOOLEAN gProcessingFPacket = FALSE;
+
+/**
+ Process a control-C break message.
+
+ Currently a place holder, remove the ASSERT when it gets implemented.
+
+ @param ErrNo Error infomration from the F reply packet or other source
+
+**/
+
+VOID
+GdbCtrlCBreakMessage (
+ IN UINTN ErrNo
+ )
+{
+ // See D.10.5 of gdb.pdf
+ // This should look like a break message. Should look like SIGINT
+
+ /* TODO: Make sure if we should do anything with ErrNo */
+ //Turn on the global Ctrl-C flag.
+ gCtrlCBreakFlag = TRUE;
+}
+
+
+/**
+ Parse the F reply packet and extract the return value and an ErrNo if it exists.
+
+ @param Packet Packet to parse like an F reply packet
+ @param ErrNo Buffer to hold Count bytes that were read
+
+ @retval -1 Error, not a valid F reply packet
+ @retval other Return the return code from the F reply packet
+
+**/
+INTN
+GdbParseFReplyPacket (
+ IN CHAR8 *Packet,
+ OUT UINTN *ErrNo
+ )
+{
+ INTN RetCode;
+
+ if (Packet[0] != 'F') {
+ // A valid responce would be an F packet
+ return -1;
+ }
+
+ RetCode = AsciiStrHexToUintn (&Packet[1]);
+
+ // Find 1st comma
+ for (;*Packet != '\0' && *Packet != ','; Packet++);
+ if (*Packet == '\0') {
+ *ErrNo = 0;
+ return RetCode;
+ }
+
+ *ErrNo = AsciiStrHexToUintn (++Packet);
+
+ // Find 2nd comma
+ for (;*Packet != '\0' && *Packet != ','; Packet++);
+ if (*Packet == '\0') {
+ return RetCode;
+ }
+
+ if (*(++Packet) == 'C') {
+ GdbCtrlCBreakMessage (*ErrNo);
+ }
+
+ return RetCode;
+}
+
+
+/**
+ Read data from a FileDescriptor. On success number of bytes read is returned. Zero indicates
+ the end of a file. On error -1 is returned. If count is zero, GdbRead returns zero.
+
+ @param FileDescriptor Device to talk to.
+ @param Buffer Buffer to hold Count bytes that were read
+ @param Count Number of bytes to transfer.
+
+ @retval -1 Error
+ @retval {other} Number of bytes read.
+
+**/
+INTN
+GdbRead (
+ IN INTN FileDescriptor,
+ OUT VOID *Buffer,
+ IN UINTN Count
+ )
+{
+ CHAR8 Packet[128];
+ UINTN Size;
+ INTN RetCode;
+ UINTN ErrNo;
+ BOOLEAN ReceiveDone = FALSE;
+
+ // Send:
+ // "Fread,XX,YYYYYYYY,XX
+ //
+ // XX - FileDescriptor in ASCII
+ // YYYYYYYY - Buffer address in ASCII
+ // XX - Count in ASCII
+ // SS - check sum
+ //
+ Size = AsciiSPrint (Packet, sizeof (Packet), "Fread,%x,%x,%x", FileDescriptor, Buffer, Count);
+ // Packet array is too small if you got this ASSERT
+ ASSERT (Size < sizeof (Packet));
+
+ gProcessingFPacket = TRUE;
+ SendPacket (Packet);
+ Print ((CHAR16 *)L"Packet sent..\n");
+
+ do {
+ // Reply:
+ ReceivePacket (Packet, sizeof (Packet));
+ Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);
+
+ // Process GDB commands
+ switch (Packet[0]) {
+ //Write memory command.
+ //M addr,length:XX...
+ case 'M':
+ WriteToMemory (Packet);
+ break;
+
+ //Fretcode, errno, Ctrl-C flag
+ //retcode - Count read
+ case 'F':
+ //Once target receives F reply packet that means the previous
+ //transactions are finished.
+ ReceiveDone = TRUE;
+ break;
+
+ //Send empty buffer
+ default :
+ SendNotSupported();
+ break;
+ }
+ } while (ReceiveDone == FALSE);
+
+ RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
+ Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);
+
+ if (ErrNo > 0) {
+ //Send error to the host if there is any.
+ SendError ((UINT8)ErrNo);
+ }
+
+ gProcessingFPacket = FALSE;
+
+ return RetCode;
+}
+
+
+/**
+ Write data to a FileDescriptor. On success number of bytes written is returned. Zero indicates
+ nothing was written. On error -1 is returned.
+
+ @param FileDescriptor Device to talk to.
+ @param Buffer Buffer to hold Count bytes that are to be written
+ @param Count Number of bytes to transfer.
+
+ @retval -1 Error
+ @retval {other} Number of bytes written.
+
+**/
+INTN
+GdbWrite (
+ IN INTN FileDescriptor,
+ OUT CONST VOID *Buffer,
+ IN UINTN Count
+ )
+{
+ CHAR8 Packet[128];
+ UINTN Size;
+ INTN RetCode;
+ UINTN ErrNo;
+ BOOLEAN ReceiveDone = FALSE;
+
+ // Send:
+ // #Fwrite,XX,YYYYYYYY,XX$SS
+ //
+ // XX - FileDescriptor in ASCII
+ // YYYYYYYY - Buffer address in ASCII
+ // XX - Count in ASCII
+ // SS - check sum
+ //
+ Size = AsciiSPrint (Packet, sizeof (Packet), "Fwrite,%x,%x,%x", FileDescriptor, Buffer, Count);
+ // Packet array is too small if you got this ASSERT
+ ASSERT (Size < sizeof (Packet));
+
+ SendPacket (Packet);
+ Print ((CHAR16 *)L"Packet sent..\n");
+
+ do {
+ // Reply:
+ ReceivePacket (Packet, sizeof (Packet));
+ Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);
+
+ // Process GDB commands
+ switch (Packet[0]) {
+ //Read memory command.
+ //m addr,length.
+ case 'm':
+ ReadFromMemory (Packet);
+ break;
+
+ //Fretcode, errno, Ctrl-C flag
+ //retcode - Count read
+ case 'F':
+ //Once target receives F reply packet that means the previous
+ //transactions are finished.
+ ReceiveDone = TRUE;
+ break;
+
+ //Send empty buffer
+ default :
+ SendNotSupported();
+ break;
+ }
+ } while (ReceiveDone == FALSE);
+
+ RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
+ Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);
+
+ //Send error to the host if there is any.
+ if (ErrNo > 0) {
+ SendError((UINT8)ErrNo);
+ }
+
+ return RetCode;
+}
+
+
+/**
+ Reset the serial device.
+
+ @param This Protocol instance pointer.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The serial device could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+GdbSerialReset (
+ IN EFI_SERIAL_IO_PROTOCOL *This
+ )
+{
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
+ data buts, and stop bits on a serial device.
+
+ @param This Protocol instance pointer.
+ @param BaudRate The requested baud rate. A BaudRate value of 0 will use the the
+ device's default interface speed.
+ @param ReveiveFifoDepth The requested depth of the FIFO on the receive side of the
+ serial interface. A ReceiveFifoDepth value of 0 will use
+ the device's dfault FIFO depth.
+ @param Timeout The requested time out for a single character in microseconds.
+ This timeout applies to both the transmit and receive side of the
+ interface. A Timeout value of 0 will use the device's default time
+ out value.
+ @param Parity The type of parity to use on this serial device. A Parity value of
+ DefaultParity will use the device's default parity value.
+ @param DataBits The number of data bits to use on the serial device. A DataBits
+ vaule of 0 will use the device's default data bit setting.
+ @param StopBits The number of stop bits to use on this serial device. A StopBits
+ value of DefaultStopBits will use the device's default number of
+ stop bits.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The serial device could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+GdbSerialSetAttributes (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN UINT64 BaudRate,
+ IN UINT32 ReceiveFifoDepth,
+ IN UINT32 Timeout,
+ IN EFI_PARITY_TYPE Parity,
+ IN UINT8 DataBits,
+ IN EFI_STOP_BITS_TYPE StopBits
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Set the control bits on a serial device
+
+ @param This Protocol instance pointer.
+ @param Control Set the bits of Control that are settable.
+
+ @retval EFI_SUCCESS The new control bits were set on the serial device.
+ @retval EFI_UNSUPPORTED The serial device does not support this operation.
+ @retval EFI_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+EFI_STATUS
+EFIAPI
+GdbSerialSetControl (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN UINT32 Control
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Retrieves the status of thecontrol bits on a serial device
+
+ @param This Protocol instance pointer.
+ @param Control A pointer to return the current Control signals from the serial device.
+
+ @retval EFI_SUCCESS The control bits were read from the serial device.
+ @retval EFI_DEVICE_ERROR The serial device is not functioning correctly.
+
+**/
+EFI_STATUS
+EFIAPI
+GdbSerialGetControl (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ OUT UINT32 *Control
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Writes data to a serial device.
+
+ @param This Protocol instance pointer.
+ @param BufferSize On input, the size of the Buffer. On output, the amount of
+ data actually written.
+ @param Buffer The buffer of data to write
+
+ @retval EFI_SUCCESS The data was written.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_TIMEOUT The data write was stopped due to a timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+GdbSerialWrite (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ GDB_SERIAL_DEV *SerialDev;
+ UINTN Return;
+
+ SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
+
+ Return = GdbWrite (SerialDev->OutFileDescriptor, Buffer, *BufferSize);
+ if (Return == (UINTN)-1) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Return != *BufferSize) {
+ *BufferSize = Return;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Writes data to a serial device.
+
+ @param This Protocol instance pointer.
+ @param BufferSize On input, the size of the Buffer. On output, the amount of
+ data returned in Buffer.
+ @param Buffer The buffer to return the data into.
+
+ @retval EFI_SUCCESS The data was read.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_TIMEOUT The data write was stopped due to a timeout.
+
+**/
+
+EFI_STATUS
+EFIAPI
+GdbSerialRead (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ GDB_SERIAL_DEV *SerialDev;
+ UINTN Return;
+
+ SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
+
+ Return = GdbRead (SerialDev->InFileDescriptor, Buffer, *BufferSize);
+ if (Return == (UINTN)-1) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Return != *BufferSize) {
+ *BufferSize = Return;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+//
+// Template used to initailize the GDB Serial IO protocols
+//
+GDB_SERIAL_DEV gdbSerialDevTemplate = {
+ GDB_SERIAL_DEV_SIGNATURE,
+ NULL,
+
+ { // SerialIo
+ SERIAL_IO_INTERFACE_REVISION,
+ GdbSerialReset,
+ GdbSerialSetAttributes,
+ GdbSerialSetControl,
+ GdbSerialGetControl,
+ GdbSerialWrite,
+ GdbSerialRead,
+ NULL
+ },
+ { // SerialMode
+ 0, // ControlMask
+ 0, // Timeout
+ 0, // BaudRate
+ 1, // RceiveFifoDepth
+ 0, // DataBits
+ 0, // Parity
+ 0 // StopBits
+ },
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)) >> 8)
+ },
+ EFI_SERIAL_IO_PROTOCOL_GUID,
+ },
+ 0,
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
+ (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL) >> 8)
+ }
+ },
+ },
+ GDB_STDIN,
+ GDB_STDOUT
+};
+
+
+/**
+ Make two serial consoles: 1) StdIn and StdOut via GDB. 2) StdErr via GDB.
+
+ These console show up on the remote system running GDB
+
+**/
+VOID
+GdbInitializeSerialConsole (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ GDB_SERIAL_DEV *StdOutSerialDev;
+ GDB_SERIAL_DEV *StdErrSerialDev;
+
+ // Use the template to make a copy of the Serial Console private data structure.
+ StdOutSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV), &gdbSerialDevTemplate);
+ ASSERT (StdOutSerialDev != NULL);
+
+ // Fixup pointer after the copy
+ StdOutSerialDev->SerialIo.Mode = &StdOutSerialDev->SerialMode;
+
+ StdErrSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV), &gdbSerialDevTemplate);
+ ASSERT (StdErrSerialDev != NULL);
+
+ // Fixup pointer and modify stuff that is different for StdError
+ StdErrSerialDev->SerialIo.Mode = &StdErrSerialDev->SerialMode;
+ StdErrSerialDev->DevicePath.Index = 1;
+ StdErrSerialDev->OutFileDescriptor = GDB_STDERR;
+
+ // Make a new handle with Serial IO protocol and its device path on it.
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &StdOutSerialDev->Handle,
+ &gEfiSerialIoProtocolGuid, &StdOutSerialDev->SerialIo,
+ &gEfiDevicePathProtocolGuid, &StdOutSerialDev->DevicePath,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ // Make a new handle with Serial IO protocol and its device path on it.
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &StdErrSerialDev->Handle,
+ &gEfiSerialIoProtocolGuid, &StdErrSerialDev->SerialIo,
+ &gEfiDevicePathProtocolGuid, &StdErrSerialDev->DevicePath,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+}
+
diff --git a/EmbeddedPkg/GdbStub/X64/Processor.c b/EmbeddedPkg/GdbStub/X64/Processor.c
new file mode 100644
index 0000000000..0758bc4a84
--- /dev/null
+++ b/EmbeddedPkg/GdbStub/X64/Processor.c
@@ -0,0 +1,963 @@
+/** @file
+ Processor specific parts of the GDB stub
+
+ 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>
+
+//
+// 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 Ponter 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 maching 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;
+}
+