| /** @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; |
| } |
| |