+++ /dev/null
-/** @file\r
- Processor specific parts of the GDB stub\r
-\r
- Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>\r
-\r
- This program and the accompanying materials\r
- are licensed and made available under the terms and conditions of the BSD License\r
- which accompanies this distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-\r
-**/\r
-\r
-\r
-#include <GdbDebugAgent.h>\r
-#include <Library/PrintLib.h>\r
-#include <Library/ArmLib.h>\r
-\r
-//\r
-// Externs from the exception handler assembly file\r
-//\r
-VOID\r
-ExceptionHandlersStart (\r
- VOID\r
- );\r
-\r
-VOID\r
-ExceptionHandlersEnd (\r
- VOID\r
- );\r
-\r
-VOID\r
-CommonExceptionEntry (\r
- VOID\r
- );\r
-\r
-VOID\r
-AsmCommonExceptionEntry (\r
- VOID\r
- );\r
-\r
-\r
-//\r
-// Array of exception types that need to be hooked by the debugger\r
-// (efi, gdb) //efi number\r
-//\r
-EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {\r
- { EXCEPT_ARM_SOFTWARE_INTERRUPT, GDB_SIGTRAP },\r
- { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },\r
- { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },\r
- { EXCEPT_ARM_DATA_ABORT, GDB_SIGTRAP }, // GDB_SIGEMT\r
- { EXCEPT_ARM_RESERVED, GDB_SIGTRAP }, // GDB_SIGILL\r
- { EXCEPT_ARM_FIQ, GDB_SIGINT } // Used for ctrl-c\r
-};\r
-\r
-// Shut up some annoying RVCT warnings\r
-#ifdef __CC_ARM\r
-#pragma diag_suppress 1296\r
-#endif\r
-\r
-UINTN gRegisterOffsets[] = {\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R0),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R1),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R2),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R3),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R4),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R5),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R6),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R7),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R8),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R9),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R10),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R11),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R12),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, SP),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, LR),\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, PC),\r
- 0x00000F01, // f0\r
- 0x00000F02,\r
- 0x00000F03,\r
- 0x00000F11, // f1\r
- 0x00000F12,\r
- 0x00000F13,\r
- 0x00000F21, // f2\r
- 0x00000F22,\r
- 0x00000F23,\r
- 0x00000F31, // f3\r
- 0x00000F32,\r
- 0x00000F33,\r
- 0x00000F41, // f4\r
- 0x00000F42,\r
- 0x00000F43,\r
- 0x00000F51, // f5\r
- 0x00000F52,\r
- 0x00000F53,\r
- 0x00000F61, // f6\r
- 0x00000F62,\r
- 0x00000F63,\r
- 0x00000F71, // f7\r
- 0x00000F72,\r
- 0x00000F73,\r
- 0x00000FFF, // fps\r
- OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)\r
-};\r
-\r
-// restore warnings for RVCT\r
-#ifdef __CC_ARM\r
-#pragma diag_default 1296\r
-#endif\r
-\r
-\r
-/**\r
- Return the number of entries in the gExceptionType[]\r
-\r
- @retval UINTN, the number of entries in the gExceptionType[] array.\r
- **/\r
-UINTN\r
-MaxEfiException (\r
- VOID\r
- )\r
-{\r
- return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY);\r
-}\r
-\r
-\r
-\r
-\r
-/**\r
- Check to see if the ISA is supported.\r
- ISA = Instruction Set Architecture\r
-\r
- @retval TRUE if Isa is supported\r
-\r
-**/\r
-BOOLEAN\r
-CheckIsa (\r
- IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa\r
- )\r
-{\r
- if (Isa == IsaArm) {\r
- return TRUE;\r
- } else {\r
- return FALSE;\r
- }\r
-}\r
-\r
-\r
-/**\r
- This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering\r
- It is, by default, set to find the register pointer of the ARM member\r
- @param SystemContext Register content at time of the exception\r
- @param RegNumber The register to which we want to find a pointer\r
- @retval the pointer to the RegNumber-th pointer\r
- **/\r
-UINTN *\r
-FindPointerToRegister(\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN UINTN RegNumber\r
- )\r
-{\r
- UINT8 *TempPtr;\r
- ASSERT(gRegisterOffsets[RegNumber] < 0xF00);\r
- TempPtr = ((UINT8 *)SystemContext.SystemContextArm) + gRegisterOffsets[RegNumber];\r
- return (UINT32 *)TempPtr;\r
-}\r
-\r
-\r
-/**\r
- Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr\r
- @param SystemContext Register content at time of the exception\r
- @param RegNumber the number of the register that we want to read\r
- @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.\r
- @retval the pointer to the next character of the output buffer that is available to be written on.\r
- **/\r
-CHAR8 *\r
-BasicReadRegister (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN UINTN RegNumber,\r
- IN CHAR8 *OutBufPtr\r
- )\r
-{\r
- UINTN RegSize;\r
- CHAR8 Char;\r
-\r
- if (gRegisterOffsets[RegNumber] > 0xF00) {\r
- AsciiSPrint(OutBufPtr, 9, "00000000");\r
- OutBufPtr += 8;\r
- return OutBufPtr;\r
- }\r
-\r
- RegSize = 0;\r
- while (RegSize < 32) {\r
- Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)];\r
- if ((Char >= 'A') && (Char <= 'F')) {\r
- Char = Char - 'A' + 'a';\r
- }\r
- *OutBufPtr++ = Char;\r
-\r
- Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)];\r
- if ((Char >= 'A') && (Char <= 'F')) {\r
- Char = Char - 'A' + 'a';\r
- }\r
- *OutBufPtr++ = Char;\r
-\r
- RegSize = RegSize + 8;\r
- }\r
- return OutBufPtr;\r
-}\r
-\r
-\r
-/**\r
- Reads the n-th register's value into an output buffer and sends it as a packet\r
- @param SystemContext Register content at time of the exception\r
- @param InBuffer Pointer to the input buffer received from gdb server\r
- **/\r
-VOID\r
-ReadNthRegister (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN CHAR8 *InBuffer\r
- )\r
-{\r
- UINTN RegNumber;\r
- CHAR8 OutBuffer[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)\r
- CHAR8 *OutBufPtr; // pointer to the output buffer\r
-\r
- RegNumber = AsciiStrHexToUintn (&InBuffer[1]);\r
-\r
- if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {\r
- SendError (GDB_EINVALIDREGNUM);\r
- return;\r
- }\r
-\r
- OutBufPtr = OutBuffer;\r
- OutBufPtr = BasicReadRegister (SystemContext, RegNumber, OutBufPtr);\r
-\r
- *OutBufPtr = '\0'; // the end of the buffer\r
- SendPacket(OutBuffer);\r
-}\r
-\r
-\r
-/**\r
- Reads the general registers into an output buffer and sends it as a packet\r
- @param SystemContext Register content at time of the exception\r
- **/\r
-VOID\r
-EFIAPI\r
-ReadGeneralRegisters (\r
- IN EFI_SYSTEM_CONTEXT SystemContext\r
- )\r
-{\r
- UINTN Index;\r
- // a UINT32 takes 8 ascii characters\r
- CHAR8 OutBuffer[(sizeof (gRegisterOffsets) * 2) + 1];\r
- CHAR8 *OutBufPtr;\r
-\r
- // It is not safe to allocate pool here....\r
- OutBufPtr = OutBuffer;\r
- for (Index = 0; Index < (sizeof (gRegisterOffsets)/sizeof (UINTN)); Index++) {\r
- OutBufPtr = BasicReadRegister (SystemContext, Index, OutBufPtr);\r
- }\r
-\r
- *OutBufPtr = '\0';\r
- SendPacket(OutBuffer);\r
-}\r
-\r
-\r
-/**\r
- Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr\r
- @param SystemContext Register content at time of the exception\r
- @param RegNumber the number of the register that we want to write\r
- @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.\r
- @retval the pointer to the next character of the input buffer that can be used\r
- **/\r
-CHAR8 *\r
-BasicWriteRegister (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN UINTN RegNumber,\r
- IN CHAR8 *InBufPtr\r
- )\r
-{\r
- UINTN RegSize;\r
- UINTN TempValue; // the value transferred from a hex char\r
- UINT32 NewValue; // the new value of the RegNumber-th Register\r
-\r
- if (gRegisterOffsets[RegNumber] > 0xF00) {\r
- return InBufPtr + 8;\r
- }\r
-\r
- NewValue = 0;\r
- RegSize = 0;\r
- while (RegSize < 32) {\r
- TempValue = HexCharToInt(*InBufPtr++);\r
-\r
- if ((INTN)TempValue < 0) {\r
- SendError (GDB_EBADMEMDATA);\r
- return NULL;\r
- }\r
-\r
- NewValue += (TempValue << (RegSize+4));\r
- TempValue = HexCharToInt(*InBufPtr++);\r
-\r
- if ((INTN)TempValue < 0) {\r
- SendError (GDB_EBADMEMDATA);\r
- return NULL;\r
- }\r
-\r
- NewValue += (TempValue << RegSize);\r
- RegSize = RegSize + 8;\r
- }\r
- *(FindPointerToRegister(SystemContext, RegNumber)) = NewValue;\r
- return InBufPtr;\r
-}\r
-\r
-\r
-/** ‘P n...=r...’\r
- Writes the new value of n-th register received into the input buffer to the n-th register\r
- @param SystemContext Register content at time of the exception\r
- @param InBuffer Ponter to the input buffer received from gdb server\r
- **/\r
-VOID\r
-WriteNthRegister (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN CHAR8 *InBuffer\r
- )\r
-{\r
- UINTN RegNumber;\r
- CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array\r
- CHAR8 *RegNumBufPtr;\r
- CHAR8 *InBufPtr; // pointer to the input buffer\r
-\r
- // find the register number to write\r
- InBufPtr = &InBuffer[1];\r
- RegNumBufPtr = RegNumBuffer;\r
- while (*InBufPtr != '=') {\r
- *RegNumBufPtr++ = *InBufPtr++;\r
- }\r
- *RegNumBufPtr = '\0';\r
- RegNumber = AsciiStrHexToUintn (RegNumBuffer);\r
-\r
- // check if this is a valid Register Number\r
- if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {\r
- SendError (GDB_EINVALIDREGNUM);\r
- return;\r
- }\r
- InBufPtr++; // skips the '=' character\r
- BasicWriteRegister (SystemContext, RegNumber, InBufPtr);\r
- SendSuccess();\r
-}\r
-\r
-\r
-/** ‘G XX...’\r
- Writes the new values received into the input buffer to the general registers\r
- @param SystemContext Register content at time of the exception\r
- @param InBuffer Pointer to the input buffer received from gdb server\r
- **/\r
-\r
-VOID\r
-EFIAPI\r
-WriteGeneralRegisters (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN CHAR8 *InBuffer\r
- )\r
-{\r
- UINTN i;\r
- CHAR8 *InBufPtr; /// pointer to the input buffer\r
- UINTN MinLength;\r
- UINTN RegisterCount = (sizeof (gRegisterOffsets)/sizeof (UINTN));\r
-\r
- MinLength = (RegisterCount * 8) + 1; // 'G' plus the registers in ASCII format\r
-\r
- if (AsciiStrLen(InBuffer) < MinLength) {\r
- //Bad message. Message is not the right length\r
- SendError (GDB_EBADBUFSIZE);\r
- return;\r
- }\r
-\r
- InBufPtr = &InBuffer[1];\r
-\r
- // Read the new values for the registers from the input buffer to an array, NewValueArray.\r
- // The values in the array are in the gdb ordering\r
- for(i = 0; i < RegisterCount; i++) {\r
- InBufPtr = BasicWriteRegister (SystemContext, i, InBufPtr);\r
- }\r
-\r
- SendSuccess ();\r
-}\r
-\r
-\r
-\r
-\r
-/**\r
- Continue. addr is Address to resume. If addr is omitted, resume at current\r
- Address.\r
-\r
- @param SystemContext Register content at time of the exception\r
- **/\r
-VOID\r
-EFIAPI\r
-ContinueAtAddress (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN CHAR8 *PacketData\r
- )\r
-{\r
- if (PacketData[1] != '\0') {\r
- SystemContext.SystemContextArm->PC = AsciiStrHexToUintn(&PacketData[1]);\r
- }\r
-}\r
-\r
-\r
-/** ‘s [addr ]’\r
- Single step. addr is the Address at which to resume. If addr is omitted, resume\r
- at same Address.\r
-\r
- @param SystemContext Register content at time of the exception\r
- **/\r
-VOID\r
-EFIAPI\r
-SingleStep (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN CHAR8 *PacketData\r
- )\r
-{\r
- SendNotSupported();\r
-}\r
-\r
-\r
-VOID\r
-EFIAPI\r
-InsertBreakPoint (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN CHAR8 *PacketData\r
- )\r
-{\r
- SendNotSupported ();\r
-}\r
-\r
-VOID\r
-EFIAPI\r
-RemoveBreakPoint (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN CHAR8 *PacketData\r
- )\r
-{\r
- SendNotSupported ();\r
-}\r
-\r
-\r
-/**\r
- Send the T signal with the given exception type (in gdb order) and possibly\r
- with n:r pairs related to the watchpoints\r
-\r
- @param SystemContext Register content at time of the exception\r
- @param GdbExceptionType GDB exception type\r
- **/\r
-VOID\r
-ProcessorSendTSignal (\r
- IN EFI_SYSTEM_CONTEXT SystemContext,\r
- IN UINT8 GdbExceptionType,\r
- IN OUT CHAR8 *TSignalPtr,\r
- IN UINTN SizeOfBuffer\r
- )\r
-{\r
- *TSignalPtr = '\0';\r
-}\r
-\r
-/**\r
- FIQ state is only changed by FIQ exception. We don't want to take FIQ\r
- ticks in the GDB stub. The stub disables FIQ on entry, but this is the\r
- third instruction that executes in the execption handler. Thus we have\r
- a crack we need to test for.\r
-\r
- @param PC PC of execption\r
-\r
- @return TRUE We are in the GDB stub exception preamble\r
- @return FALSE We are not in GDB stub code\r
- **/\r
-BOOLEAN\r
-InFiqCrack (\r
- IN UINT32 PC\r
- )\r
-{\r
- UINT64 VectorBase = PcdGet64 (PcdCpuVectorBaseAddress);\r
- UINT32 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;\r
-\r
- if ((PC >= VectorBase) && (PC <= (VectorBase + Length))) {\r
- return TRUE;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-\r
-/**\r
- Check to see if this exception is related to ctrl-c handling.\r
-\r
- In this scheme we dedicate FIQ to the ctrl-c handler so it is\r
- independent of the rest of the system.\r
-\r
- SaveAndSetDebugTimerInterrupt () can be used to\r
-\r
- @param ExceptionType Exception that is being processed\r
- @param SystemContext Register content at time of the exception\r
-\r
- @return TRUE This was a ctrl-c check that did not find a ctrl-c\r
- @return FALSE This was not a ctrl-c check or some one hit ctrl-c\r
- **/\r
-BOOLEAN\r
-ProcessorControlC (\r
- IN EFI_EXCEPTION_TYPE ExceptionType,\r
- IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
- )\r
-{\r
- CHAR8 Char;\r
- BOOLEAN Return = TRUE;\r
-\r
- if (ExceptionType != EXCEPT_ARM_FIQ) {\r
- // Skip it as it is not related to ctrl-c\r
- return FALSE;\r
- }\r
-\r
- if (InFiqCrack (SystemContext.SystemContextArm->PC)) {\r
- // We are in our own interrupt preable, so skip this tick.\r
- // We never want to let gdb see the debug stub running if we can help it\r
- return FALSE;\r
- }\r
-\r
- while (TRUE) {\r
- if (!GdbIsCharAvailable ()) {\r
- //\r
- // No characters are pending so exit the loop\r
- //\r
- Return = TRUE;\r
- break;\r
- }\r
-\r
- Char = GdbGetChar ();\r
- if (Char == 0x03) {\r
- //\r
- // We have a ctrl-c so exit and process exception for ctrl-c\r
- //\r
- Return = FALSE;\r
- break;\r
- }\r
- }\r
-\r
- DebugAgentTimerEndOfInterrupt ();\r
-\r
- // Force an exit from the exception handler as we are done\r
- return Return;\r
-}\r
-\r
-\r
-/**\r
- Enable/Disable the interrupt of debug timer and return the interrupt state\r
- prior to the operation.\r
-\r
- If EnableStatus is TRUE, enable the interrupt of debug timer.\r
- If EnableStatus is FALSE, disable the interrupt of debug timer.\r
-\r
- @param[in] EnableStatus Enable/Disable.\r
-\r
- @retval TRUE Debug timer interrupt were enabled on entry to this call.\r
- @retval FALSE Debug timer interrupt were disabled on entry to this call.\r
-\r
-**/\r
-BOOLEAN\r
-EFIAPI\r
-SaveAndSetDebugTimerInterrupt (\r
- IN BOOLEAN EnableStatus\r
- )\r
-{\r
- BOOLEAN FiqEnabled;\r
-\r
- FiqEnabled = ArmGetFiqState ();\r
-\r
- if (EnableStatus) {\r
- DebugAgentTimerSetPeriod (PcdGet32 (PcdGdbTimerPeriodMilliseconds));\r
- ArmEnableFiq ();\r
- } else {\r
- DebugAgentTimerSetPeriod (0);\r
- ArmDisableFiq ();\r
- }\r
-\r
- return FiqEnabled;\r
-}\r
-\r
-\r
-\r
-VOID\r
-GdbFPutString (\r
- IN CHAR8 *String\r
- );\r
-\r
-/**\r
- Initialize debug agent.\r
-\r
- This function is used to set up debug environment to support source level debugging.\r
- If certain Debug Agent Library instance has to save some private data in the stack,\r
- this function must work on the mode that doesn't return to the caller, then\r
- the caller needs to wrap up all rest of logic after InitializeDebugAgent() into one\r
- function and pass it into InitializeDebugAgent(). InitializeDebugAgent() is\r
- responsible to invoke the passing-in function at the end of InitializeDebugAgent().\r
-\r
- If the parameter Function is not NULL, Debug Agent Library instance will invoke it by\r
- passing in the Context to be its parameter.\r
-\r
- If Function() is NULL, Debug Agent Library instance will return after setup debug\r
- environment.\r
-\r
- @param[in] InitFlag Init flag is used to decide the initialize process.\r
- @param[in] Context Context needed according to InitFlag; it was optional.\r
- @param[in] Function Continue function called by debug agent library; it was\r
- optional.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-InitializeDebugAgent (\r
- IN UINT32 InitFlag,\r
- IN VOID *Context, OPTIONAL\r
- IN DEBUG_AGENT_CONTINUE Function OPTIONAL\r
- )\r
-{\r
- UINTN Offset;\r
- UINTN Length;\r
- BOOLEAN IrqEnabled;\r
- UINT64 *VectorBase;\r
-\r
-\r
- //\r
- // Disable interrupts\r
- //\r
- IrqEnabled = ArmGetInterruptState ();\r
- ArmDisableInterrupts ();\r
- ArmDisableFiq ();\r
-\r
- //\r
- // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.\r
- //\r
- Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;\r
-\r
- //\r
- // Reserve space for the exception handlers\r
- //\r
- VectorBase = (UINT64 *)(UINTN)PcdGet64 (PcdCpuVectorBaseAddress);\r
-\r
-\r
- // Copy our assembly code into the page that contains the exception vectors.\r
- CopyMem ((VOID *)VectorBase, (VOID *)ExceptionHandlersStart, Length);\r
-\r
- //\r
- // Patch in the common Assembly exception handler\r
- //\r
- Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;\r
- *(UINTN *) (((UINT8 *)VectorBase) + Offset) = (UINTN)AsmCommonExceptionEntry;\r
-\r
- // Flush Caches since we updated executable stuff\r
- InvalidateInstructionCacheRange ((VOID *)(UINTN)PcdGet64(PcdCpuVectorBaseAddress), Length);\r
-\r
- // setup a timer so gdb can break in via ctrl-c\r
- DebugAgentTimerIntialize ();\r
-\r
- if (IrqEnabled) {\r
- ArmEnableInterrupts ();\r
- }\r
-\r
- if (Function != NULL) {\r
- Function (Context);\r
- }\r
-\r
- return;\r
-}\r
-\r