2 Processor specific parts of the GDB stub
4 Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <GdbStubInternal.h>
11 #include <Library/CacheMaintenanceLib.h>
12 #include <Library/PrintLib.h>
15 // Array of exception types that need to be hooked by the debugger
16 // (efi, gdb) //efi number
18 EFI_EXCEPTION_TYPE_ENTRY gExceptionType
[] = {
19 { EXCEPT_ARM_SOFTWARE_INTERRUPT
, GDB_SIGTRAP
}
20 // { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },
21 // { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },
22 // { EXCEPT_ARM_DATA_ABORT, GDB_SIGEMT },
23 // { EXCEPT_ARM_RESERVED, GDB_SIGILL }
26 // Shut up some annoying RVCT warnings
28 #pragma diag_suppress 1296
31 UINTN gRegisterOffsets
[] = {
32 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R0
),
33 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R1
),
34 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R2
),
35 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R3
),
36 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R4
),
37 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R5
),
38 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R6
),
39 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R7
),
40 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R8
),
41 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R9
),
42 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R10
),
43 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R11
),
44 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R12
),
45 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, SP
),
46 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, LR
),
47 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, PC
),
73 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, CPSR
)
76 // restore warnings for RVCT
78 #pragma diag_default 1296
82 Return the number of entries in the gExceptionType[]
84 @retval UINTN, the number of entries in the gExceptionType[] array.
91 return sizeof (gExceptionType
) / sizeof (EFI_EXCEPTION_TYPE_ENTRY
);
96 Return the number of entries in the gRegisters[]
98 @retval UINTN, the number of entries (registers) in the gRegisters[] array.
105 return sizeof (gRegisterOffsets
) / sizeof (UINTN
);
110 Check to see if the ISA is supported.
111 ISA = Instruction Set Architecture
113 @retval TRUE if Isa is supported
118 IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
130 This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
131 It is, by default, set to find the register pointer of the ARM member
132 @param SystemContext Register content at time of the exception
133 @param RegNumber The register to which we want to find a pointer
134 @retval the pointer to the RegNumber-th pointer
137 FindPointerToRegister (
138 IN EFI_SYSTEM_CONTEXT SystemContext
,
143 ASSERT(gRegisterOffsets
[RegNumber
] < 0xF00);
144 TempPtr
= ((UINT8
*)SystemContext
.SystemContextArm
) + gRegisterOffsets
[RegNumber
];
145 return (UINT32
*)TempPtr
;
150 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
151 @param SystemContext Register content at time of the exception
152 @param RegNumber the number of the register that we want to read
153 @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
154 @retval the pointer to the next character of the output buffer that is available to be written on.
158 IN EFI_SYSTEM_CONTEXT SystemContext
,
166 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
167 AsciiSPrint (OutBufPtr
, 9, "00000000");
173 while (RegSize
< 32) {
174 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister (SystemContext
, RegNumber
) >> (RegSize
+4)) & 0xf)];
175 if ((Char
>= 'A') && (Char
<= 'F')) {
176 Char
= Char
- 'A' + 'a';
180 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister (SystemContext
, RegNumber
) >> RegSize
) & 0xf)];
181 if ((Char
>= 'A') && (Char
<= 'F')) {
182 Char
= Char
- 'A' + 'a';
186 RegSize
= RegSize
+ 8;
193 Reads the n-th register's value into an output buffer and sends it as a packet
194 @param SystemContext Register content at time of the exception
195 @param InBuffer Pointer to the input buffer received from gdb server
199 IN EFI_SYSTEM_CONTEXT SystemContext
,
204 CHAR8 OutBuffer
[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
205 CHAR8
*OutBufPtr
; // pointer to the output buffer
207 RegNumber
= AsciiStrHexToUintn (&InBuffer
[1]);
209 if (RegNumber
>= MaxRegisterCount ()) {
210 SendError (GDB_EINVALIDREGNUM
);
214 OutBufPtr
= OutBuffer
;
215 OutBufPtr
= BasicReadRegister (SystemContext
, RegNumber
, OutBufPtr
);
217 *OutBufPtr
= '\0'; // the end of the buffer
218 SendPacket (OutBuffer
);
223 Reads the general registers into an output buffer and sends it as a packet
224 @param SystemContext Register content at time of the exception
228 ReadGeneralRegisters (
229 IN EFI_SYSTEM_CONTEXT SystemContext
235 UINTN RegisterCount
= MaxRegisterCount ();
237 // It is not safe to allocate pool here....
238 OutBuffer
= AllocatePool ((RegisterCount
* 8) + 1); // 8 bytes per register in string format plus a null to terminate
239 OutBufPtr
= OutBuffer
;
240 for (Index
= 0; Index
< RegisterCount
; Index
++) {
241 OutBufPtr
= BasicReadRegister (SystemContext
, Index
, OutBufPtr
);
245 SendPacket (OutBuffer
);
246 FreePool (OutBuffer
);
251 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
252 @param SystemContext Register content at time of the exception
253 @param RegNumber the number of the register that we want to write
254 @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
255 @retval the pointer to the next character of the input buffer that can be used
258 *BasicWriteRegister (
259 IN EFI_SYSTEM_CONTEXT SystemContext
,
265 UINTN TempValue
; // the value transferred from a hex char
266 UINT32 NewValue
; // the new value of the RegNumber-th Register
268 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
274 while (RegSize
< 32) {
275 TempValue
= HexCharToInt (*InBufPtr
++);
277 if ((INTN
)TempValue
< 0) {
278 SendError (GDB_EBADMEMDATA
);
282 NewValue
+= (TempValue
<< (RegSize
+4));
283 TempValue
= HexCharToInt (*InBufPtr
++);
285 if ((INTN
)TempValue
< 0) {
286 SendError (GDB_EBADMEMDATA
);
290 NewValue
+= (TempValue
<< RegSize
);
291 RegSize
= RegSize
+ 8;
293 *(FindPointerToRegister (SystemContext
, RegNumber
)) = NewValue
;
298 /** ‘P n...=r...’
299 Writes the new value of n-th register received into the input buffer to the n-th register
300 @param SystemContext Register content at time of the exception
301 @param InBuffer Pointer to the input buffer received from gdb server
305 IN EFI_SYSTEM_CONTEXT SystemContext
,
310 CHAR8 RegNumBuffer
[MAX_REG_NUM_BUF_SIZE
]; // put the 'n..' part of the message into this array
312 CHAR8
*InBufPtr
; // pointer to the input buffer
314 // find the register number to write
315 InBufPtr
= &InBuffer
[1];
316 RegNumBufPtr
= RegNumBuffer
;
317 while (*InBufPtr
!= '=') {
318 *RegNumBufPtr
++ = *InBufPtr
++;
320 *RegNumBufPtr
= '\0';
321 RegNumber
= AsciiStrHexToUintn (RegNumBuffer
);
323 // check if this is a valid Register Number
324 if (RegNumber
>= MaxRegisterCount ()) {
325 SendError (GDB_EINVALIDREGNUM
);
328 InBufPtr
++; // skips the '=' character
329 BasicWriteRegister (SystemContext
, RegNumber
, InBufPtr
);
335 Writes the new values received into the input buffer to the general registers
336 @param SystemContext Register content at time of the exception
337 @param InBuffer Pointer to the input buffer received from gdb server
342 WriteGeneralRegisters (
343 IN EFI_SYSTEM_CONTEXT SystemContext
,
348 CHAR8
*InBufPtr
; /// pointer to the input buffer
350 UINTN RegisterCount
= MaxRegisterCount ();
352 MinLength
= (RegisterCount
* 8) + 1; // 'G' plus the registers in ASCII format
354 if (AsciiStrLen (InBuffer
) < MinLength
) {
355 //Bad message. Message is not the right length
356 SendError (GDB_EBADBUFSIZE
);
360 InBufPtr
= &InBuffer
[1];
362 // Read the new values for the registers from the input buffer to an array, NewValueArray.
363 // The values in the array are in the gdb ordering
364 for (i
= 0; i
< RegisterCount
; i
++) {
365 InBufPtr
= BasicWriteRegister (SystemContext
, i
, InBufPtr
);
372 // Use SWI 0xdbdbdb as the debug instruction
373 #define GDB_ARM_BKPT 0xefdbdbdb
375 BOOLEAN mSingleStepActive
= FALSE
;
376 UINT32 mSingleStepPC
;
377 UINT32 mSingleStepData
;
378 UINTN mSingleStepDataSize
;
385 } ARM_SOFTWARE_BREAKPOINT
;
387 #define ARM_SOFTWARE_BREAKPOINT_SIGNATURE SIGNATURE_64('A', 'R', 'M', 'B', 'R', 'K', 'P', 'T')
388 #define ARM_SOFTWARE_BREAKPOINT_FROM_LINK(a) CR(a, ARM_SOFTWARE_BREAKPOINT, Link, ARM_SOFTWARE_BREAKPOINT_SIGNATURE)
390 LIST_ENTRY BreakpointList
;
393 Insert Single Step in the SystemContext
395 @param SystemContext Register content at time of the exception
399 IN EFI_SYSTEM_CONTEXT SystemContext
402 if (mSingleStepActive
) {
403 // Currently don't support nesting
406 mSingleStepActive
= TRUE
;
408 mSingleStepPC
= SystemContext
.SystemContextArm
->PC
;
410 mSingleStepDataSize
= sizeof (UINT32
);
411 mSingleStepData
= (*(UINT32
*)mSingleStepPC
);
412 *(UINT32
*)mSingleStepPC
= GDB_ARM_BKPT
;
413 if (*(UINT32
*)mSingleStepPC
!= GDB_ARM_BKPT
) {
414 // For some reason our breakpoint did not take
415 mSingleStepActive
= FALSE
;
418 InvalidateInstructionCacheRange ((VOID
*)mSingleStepPC
, mSingleStepDataSize
);
419 //DEBUG((DEBUG_ERROR, "AddSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, mSingleStepData, *(UINT32 *)mSingleStepPC));
424 Remove Single Step in the SystemContext
426 @param SystemContext Register content at time of the exception
430 IN EFI_SYSTEM_CONTEXT SystemContext
433 if (!mSingleStepActive
) {
437 if (mSingleStepDataSize
== sizeof (UINT16
)) {
438 *(UINT16
*)mSingleStepPC
= (UINT16
)mSingleStepData
;
440 //DEBUG((DEBUG_ERROR, "RemoveSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, *(UINT32 *)mSingleStepPC, mSingleStepData));
441 *(UINT32
*)mSingleStepPC
= mSingleStepData
;
443 InvalidateInstructionCacheRange ((VOID
*)mSingleStepPC
, mSingleStepDataSize
);
444 mSingleStepActive
= FALSE
;
450 Continue. addr is Address to resume. If addr is omitted, resume at current
453 @param SystemContext Register content at time of the exception
458 IN EFI_SYSTEM_CONTEXT SystemContext
,
462 if (PacketData
[1] != '\0') {
463 SystemContext
.SystemContextArm
->PC
= AsciiStrHexToUintn (&PacketData
[1]);
469 Single step. addr is the Address at which to resume. If addr is omitted, resume
472 @param SystemContext Register content at time of the exception
477 IN EFI_SYSTEM_CONTEXT SystemContext
,
485 GetBreakpointDataAddress (
486 IN EFI_SYSTEM_CONTEXT SystemContext
,
487 IN UINTN BreakpointNumber
494 GetBreakpointDetected (
495 IN EFI_SYSTEM_CONTEXT SystemContext
503 IN EFI_SYSTEM_CONTEXT SystemContext
,
504 IN UINTN BreakpointNumber
510 ARM_SOFTWARE_BREAKPOINT
*
511 SearchBreakpointList (
516 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
518 Current
= GetFirstNode (&BreakpointList
);
519 while (!IsNull (&BreakpointList
, Current
)) {
520 Breakpoint
= ARM_SOFTWARE_BREAKPOINT_FROM_LINK(Current
);
522 if (Address
== Breakpoint
->Address
) {
526 Current
= GetNextNode (&BreakpointList
, Current
);
537 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
539 Breakpoint
= SearchBreakpointList (Address
);
541 if (Breakpoint
!= NULL
) {
545 // create and fill breakpoint structure
546 Breakpoint
= AllocatePool (sizeof(ARM_SOFTWARE_BREAKPOINT
));
548 Breakpoint
->Signature
= ARM_SOFTWARE_BREAKPOINT_SIGNATURE
;
549 Breakpoint
->Address
= Address
;
550 Breakpoint
->Instruction
= *(UINT32
*)Address
;
552 // Add it to the list
553 InsertTailList (&BreakpointList
, &Breakpoint
->Link
);
555 // Insert the software breakpoint
556 *(UINT32
*)Address
= GDB_ARM_BKPT
;
557 InvalidateInstructionCacheRange ((VOID
*)Address
, 4);
559 //DEBUG((DEBUG_ERROR, "SetBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, Breakpoint->Instruction, *(UINT32 *)Address));
567 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
569 Breakpoint
= SearchBreakpointList (Address
);
571 if (Breakpoint
== NULL
) {
575 // Add it to the list
576 RemoveEntryList (&Breakpoint
->Link
);
578 // Restore the original instruction
579 *(UINT32
*)Address
= Breakpoint
->Instruction
;
580 InvalidateInstructionCacheRange ((VOID
*)Address
, 4);
582 //DEBUG((DEBUG_ERROR, "ClearBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, GDB_ARM_BKPT, *(UINT32 *)Address));
584 FreePool (Breakpoint
);
590 IN EFI_SYSTEM_CONTEXT SystemContext
,
599 ErrorCode
= ParseBreakpointPacket (PacketData
, &Type
, &Address
, &Length
);
601 SendError ((UINT8
)ErrorCode
);
606 case 0: //Software breakpoint
610 DEBUG((DEBUG_ERROR
, "Insert breakpoint default: %x\n", Type
));
611 SendError (GDB_EINVALIDBRKPOINTTYPE
);
615 SetBreakpoint (Address
);
623 IN EFI_SYSTEM_CONTEXT SystemContext
,
632 //Parse breakpoint packet data
633 ErrorCode
= ParseBreakpointPacket (PacketData
, &Type
, &Address
, &Length
);
635 SendError ((UINT8
)ErrorCode
);
640 case 0: //Software breakpoint
644 SendError (GDB_EINVALIDBRKPOINTTYPE
);
648 ClearBreakpoint (Address
);
654 InitializeProcessor (
658 // Initialize breakpoint list
659 InitializeListHead (&BreakpointList
);
667 if ((UINT32
)Address
< 0x80000000) {
676 IN EFI_EXCEPTION_TYPE ExceptionType
,
677 IN OUT EFI_SYSTEM_CONTEXT SystemContext
680 UINT32 ExceptionAddress
;
683 // Is it a debugger SWI?
684 ExceptionAddress
= SystemContext
.SystemContextArm
->PC
-= 4;
685 Instruction
= *(UINT32
*)ExceptionAddress
;
686 if (Instruction
!= GDB_ARM_BKPT
) {
690 // Special for SWI-based exception handling. SWI sets up the context
691 // to return to the instruction following the SWI instruction - NOT what we want
693 SystemContext
.SystemContextArm
->PC
= ExceptionAddress
;