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 UINTN gRegisterOffsets
[] = {
27 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R0
),
28 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R1
),
29 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R2
),
30 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R3
),
31 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R4
),
32 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R5
),
33 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R6
),
34 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R7
),
35 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R8
),
36 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R9
),
37 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R10
),
38 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R11
),
39 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, R12
),
40 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, SP
),
41 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, LR
),
42 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, PC
),
68 OFFSET_OF (EFI_SYSTEM_CONTEXT_ARM
, CPSR
)
72 Return the number of entries in the gExceptionType[]
74 @retval UINTN, the number of entries in the gExceptionType[] array.
81 return sizeof (gExceptionType
) / sizeof (EFI_EXCEPTION_TYPE_ENTRY
);
85 Return the number of entries in the gRegisters[]
87 @retval UINTN, the number of entries (registers) in the gRegisters[] array.
94 return sizeof (gRegisterOffsets
) / sizeof (UINTN
);
98 Check to see if the ISA is supported.
99 ISA = Instruction Set Architecture
101 @retval TRUE if Isa is supported
106 IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
117 This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
118 It is, by default, set to find the register pointer of the ARM member
119 @param SystemContext Register content at time of the exception
120 @param RegNumber The register to which we want to find a pointer
121 @retval the pointer to the RegNumber-th pointer
124 FindPointerToRegister (
125 IN EFI_SYSTEM_CONTEXT SystemContext
,
131 ASSERT (gRegisterOffsets
[RegNumber
] < 0xF00);
132 TempPtr
= ((UINT8
*)SystemContext
.SystemContextArm
) + gRegisterOffsets
[RegNumber
];
133 return (UINT32
*)TempPtr
;
137 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
138 @param SystemContext Register content at time of the exception
139 @param RegNumber the number of the register that we want to read
140 @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
141 @retval the pointer to the next character of the output buffer that is available to be written on.
145 IN EFI_SYSTEM_CONTEXT SystemContext
,
153 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
154 AsciiSPrint (OutBufPtr
, 9, "00000000");
160 while (RegSize
< 32) {
161 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister (SystemContext
, RegNumber
) >> (RegSize
+4)) & 0xf)];
162 if ((Char
>= 'A') && (Char
<= 'F')) {
163 Char
= Char
- 'A' + 'a';
168 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister (SystemContext
, RegNumber
) >> RegSize
) & 0xf)];
169 if ((Char
>= 'A') && (Char
<= 'F')) {
170 Char
= Char
- 'A' + 'a';
175 RegSize
= RegSize
+ 8;
182 Reads the n-th register's value into an output buffer and sends it as a packet
183 @param SystemContext Register content at time of the exception
184 @param InBuffer Pointer to the input buffer received from gdb server
188 IN EFI_SYSTEM_CONTEXT SystemContext
,
193 CHAR8 OutBuffer
[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
194 CHAR8
*OutBufPtr
; // pointer to the output buffer
196 RegNumber
= AsciiStrHexToUintn (&InBuffer
[1]);
198 if (RegNumber
>= MaxRegisterCount ()) {
199 SendError (GDB_EINVALIDREGNUM
);
203 OutBufPtr
= OutBuffer
;
204 OutBufPtr
= BasicReadRegister (SystemContext
, RegNumber
, OutBufPtr
);
206 *OutBufPtr
= '\0'; // the end of the buffer
207 SendPacket (OutBuffer
);
211 Reads the general registers into an output buffer and sends it as a packet
212 @param SystemContext Register content at time of the exception
216 ReadGeneralRegisters (
217 IN EFI_SYSTEM_CONTEXT SystemContext
223 UINTN RegisterCount
= MaxRegisterCount ();
225 // It is not safe to allocate pool here....
226 OutBuffer
= AllocatePool ((RegisterCount
* 8) + 1); // 8 bytes per register in string format plus a null to terminate
227 OutBufPtr
= OutBuffer
;
228 for (Index
= 0; Index
< RegisterCount
; Index
++) {
229 OutBufPtr
= BasicReadRegister (SystemContext
, Index
, OutBufPtr
);
233 SendPacket (OutBuffer
);
234 FreePool (OutBuffer
);
238 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
239 @param SystemContext Register content at time of the exception
240 @param RegNumber the number of the register that we want to write
241 @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
242 @retval the pointer to the next character of the input buffer that can be used
247 IN EFI_SYSTEM_CONTEXT SystemContext
,
253 UINTN TempValue
; // the value transferred from a hex char
254 UINT32 NewValue
; // the new value of the RegNumber-th Register
256 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
262 while (RegSize
< 32) {
263 TempValue
= HexCharToInt (*InBufPtr
++);
265 if ((INTN
)TempValue
< 0) {
266 SendError (GDB_EBADMEMDATA
);
270 NewValue
+= (TempValue
<< (RegSize
+4));
271 TempValue
= HexCharToInt (*InBufPtr
++);
273 if ((INTN
)TempValue
< 0) {
274 SendError (GDB_EBADMEMDATA
);
278 NewValue
+= (TempValue
<< RegSize
);
279 RegSize
= RegSize
+ 8;
282 *(FindPointerToRegister (SystemContext
, RegNumber
)) = NewValue
;
287 Writes the new value of n-th register received into the input buffer to the n-th register
288 @param SystemContext Register content at time of the exception
289 @param InBuffer Pointer to the input buffer received from gdb server
293 IN EFI_SYSTEM_CONTEXT SystemContext
,
298 CHAR8 RegNumBuffer
[MAX_REG_NUM_BUF_SIZE
]; // put the 'n..' part of the message into this array
300 CHAR8
*InBufPtr
; // pointer to the input buffer
302 // find the register number to write
303 InBufPtr
= &InBuffer
[1];
304 RegNumBufPtr
= RegNumBuffer
;
305 while (*InBufPtr
!= '=') {
306 *RegNumBufPtr
++ = *InBufPtr
++;
309 *RegNumBufPtr
= '\0';
310 RegNumber
= AsciiStrHexToUintn (RegNumBuffer
);
312 // check if this is a valid Register Number
313 if (RegNumber
>= MaxRegisterCount ()) {
314 SendError (GDB_EINVALIDREGNUM
);
318 InBufPtr
++; // skips the '=' character
319 BasicWriteRegister (SystemContext
, RegNumber
, InBufPtr
);
324 Writes the new values received into the input buffer to the general registers
325 @param SystemContext Register content at time of the exception
326 @param InBuffer Pointer to the input buffer received from gdb server
330 WriteGeneralRegisters (
331 IN EFI_SYSTEM_CONTEXT SystemContext
,
336 CHAR8
*InBufPtr
; /// pointer to the input buffer
338 UINTN RegisterCount
= MaxRegisterCount ();
340 MinLength
= (RegisterCount
* 8) + 1; // 'G' plus the registers in ASCII format
342 if (AsciiStrLen (InBuffer
) < MinLength
) {
343 // Bad message. Message is not the right length
344 SendError (GDB_EBADBUFSIZE
);
348 InBufPtr
= &InBuffer
[1];
350 // Read the new values for the registers from the input buffer to an array, NewValueArray.
351 // The values in the array are in the gdb ordering
352 for (i
= 0; i
< RegisterCount
; i
++) {
353 InBufPtr
= BasicWriteRegister (SystemContext
, i
, InBufPtr
);
360 // Use SWI 0xdbdbdb as the debug instruction
361 #define GDB_ARM_BKPT 0xefdbdbdb
363 BOOLEAN mSingleStepActive
= FALSE
;
364 UINT32 mSingleStepPC
;
365 UINT32 mSingleStepData
;
366 UINTN mSingleStepDataSize
;
373 } ARM_SOFTWARE_BREAKPOINT
;
375 #define ARM_SOFTWARE_BREAKPOINT_SIGNATURE SIGNATURE_64('A', 'R', 'M', 'B', 'R', 'K', 'P', 'T')
376 #define ARM_SOFTWARE_BREAKPOINT_FROM_LINK(a) CR(a, ARM_SOFTWARE_BREAKPOINT, Link, ARM_SOFTWARE_BREAKPOINT_SIGNATURE)
378 LIST_ENTRY BreakpointList
;
381 Insert Single Step in the SystemContext
383 @param SystemContext Register content at time of the exception
387 IN EFI_SYSTEM_CONTEXT SystemContext
390 if (mSingleStepActive
) {
391 // Currently don't support nesting
395 mSingleStepActive
= TRUE
;
397 mSingleStepPC
= SystemContext
.SystemContextArm
->PC
;
399 mSingleStepDataSize
= sizeof (UINT32
);
400 mSingleStepData
= (*(UINT32
*)mSingleStepPC
);
401 *(UINT32
*)mSingleStepPC
= GDB_ARM_BKPT
;
402 if (*(UINT32
*)mSingleStepPC
!= GDB_ARM_BKPT
) {
403 // For some reason our breakpoint did not take
404 mSingleStepActive
= FALSE
;
407 InvalidateInstructionCacheRange ((VOID
*)mSingleStepPC
, mSingleStepDataSize
);
408 // DEBUG((DEBUG_ERROR, "AddSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, mSingleStepData, *(UINT32 *)mSingleStepPC));
412 Remove Single Step in the SystemContext
414 @param SystemContext Register content at time of the exception
418 IN EFI_SYSTEM_CONTEXT SystemContext
421 if (!mSingleStepActive
) {
425 if (mSingleStepDataSize
== sizeof (UINT16
)) {
426 *(UINT16
*)mSingleStepPC
= (UINT16
)mSingleStepData
;
428 // DEBUG((DEBUG_ERROR, "RemoveSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, *(UINT32 *)mSingleStepPC, mSingleStepData));
429 *(UINT32
*)mSingleStepPC
= mSingleStepData
;
432 InvalidateInstructionCacheRange ((VOID
*)mSingleStepPC
, mSingleStepDataSize
);
433 mSingleStepActive
= FALSE
;
437 Continue. addr is Address to resume. If addr is omitted, resume at current
440 @param SystemContext Register content at time of the exception
445 IN EFI_SYSTEM_CONTEXT SystemContext
,
449 if (PacketData
[1] != '\0') {
450 SystemContext
.SystemContextArm
->PC
= AsciiStrHexToUintn (&PacketData
[1]);
455 Single step. addr is the Address at which to resume. If addr is omitted, resume
458 @param SystemContext Register content at time of the exception
463 IN EFI_SYSTEM_CONTEXT SystemContext
,
471 GetBreakpointDataAddress (
472 IN EFI_SYSTEM_CONTEXT SystemContext
,
473 IN UINTN BreakpointNumber
480 GetBreakpointDetected (
481 IN EFI_SYSTEM_CONTEXT SystemContext
489 IN EFI_SYSTEM_CONTEXT SystemContext
,
490 IN UINTN BreakpointNumber
496 ARM_SOFTWARE_BREAKPOINT
*
497 SearchBreakpointList (
502 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
504 Current
= GetFirstNode (&BreakpointList
);
505 while (!IsNull (&BreakpointList
, Current
)) {
506 Breakpoint
= ARM_SOFTWARE_BREAKPOINT_FROM_LINK (Current
);
508 if (Address
== Breakpoint
->Address
) {
512 Current
= GetNextNode (&BreakpointList
, Current
);
523 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
525 Breakpoint
= SearchBreakpointList (Address
);
527 if (Breakpoint
!= NULL
) {
531 // create and fill breakpoint structure
532 Breakpoint
= AllocatePool (sizeof (ARM_SOFTWARE_BREAKPOINT
));
534 Breakpoint
->Signature
= ARM_SOFTWARE_BREAKPOINT_SIGNATURE
;
535 Breakpoint
->Address
= Address
;
536 Breakpoint
->Instruction
= *(UINT32
*)Address
;
538 // Add it to the list
539 InsertTailList (&BreakpointList
, &Breakpoint
->Link
);
541 // Insert the software breakpoint
542 *(UINT32
*)Address
= GDB_ARM_BKPT
;
543 InvalidateInstructionCacheRange ((VOID
*)Address
, 4);
545 // DEBUG((DEBUG_ERROR, "SetBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, Breakpoint->Instruction, *(UINT32 *)Address));
553 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
555 Breakpoint
= SearchBreakpointList (Address
);
557 if (Breakpoint
== NULL
) {
561 // Add it to the list
562 RemoveEntryList (&Breakpoint
->Link
);
564 // Restore the original instruction
565 *(UINT32
*)Address
= Breakpoint
->Instruction
;
566 InvalidateInstructionCacheRange ((VOID
*)Address
, 4);
568 // DEBUG((DEBUG_ERROR, "ClearBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, GDB_ARM_BKPT, *(UINT32 *)Address));
570 FreePool (Breakpoint
);
576 IN EFI_SYSTEM_CONTEXT SystemContext
,
585 ErrorCode
= ParseBreakpointPacket (PacketData
, &Type
, &Address
, &Length
);
587 SendError ((UINT8
)ErrorCode
);
592 case 0: // Software breakpoint
596 DEBUG ((DEBUG_ERROR
, "Insert breakpoint default: %x\n", Type
));
597 SendError (GDB_EINVALIDBRKPOINTTYPE
);
601 SetBreakpoint (Address
);
609 IN EFI_SYSTEM_CONTEXT SystemContext
,
618 // Parse breakpoint packet data
619 ErrorCode
= ParseBreakpointPacket (PacketData
, &Type
, &Address
, &Length
);
621 SendError ((UINT8
)ErrorCode
);
626 case 0: // Software breakpoint
630 SendError (GDB_EINVALIDBRKPOINTTYPE
);
634 ClearBreakpoint (Address
);
640 InitializeProcessor (
644 // Initialize breakpoint list
645 InitializeListHead (&BreakpointList
);
653 if ((UINT32
)Address
< 0x80000000) {
662 IN EFI_EXCEPTION_TYPE ExceptionType
,
663 IN OUT EFI_SYSTEM_CONTEXT SystemContext
666 UINT32 ExceptionAddress
;
669 // Is it a debugger SWI?
670 ExceptionAddress
= SystemContext
.SystemContextArm
->PC
-= 4;
671 Instruction
= *(UINT32
*)ExceptionAddress
;
672 if (Instruction
!= GDB_ARM_BKPT
) {
676 // Special for SWI-based exception handling. SWI sets up the context
677 // to return to the instruction following the SWI instruction - NOT what we want
679 SystemContext
.SystemContextArm
->PC
= ExceptionAddress
;