2 Processor specific parts of the GDB stub
4 Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include <GdbStubInternal.h>
17 #include <Library/CacheMaintenanceLib.h>
18 #include <Library/PrintLib.h>
21 // Array of exception types that need to be hooked by the debugger
22 // (efi, gdb) //efi number
24 EFI_EXCEPTION_TYPE_ENTRY gExceptionType
[] = {
25 { EXCEPT_ARM_SOFTWARE_INTERRUPT
, GDB_SIGTRAP
}
26 // { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },
27 // { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },
28 // { EXCEPT_ARM_DATA_ABORT, GDB_SIGEMT },
29 // { EXCEPT_ARM_RESERVED, GDB_SIGILL }
32 // Shut up some annoying RVCT warnings
34 #pragma diag_suppress 1296
37 UINTN gRegisterOffsets
[] = {
38 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R0
),
39 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R1
),
40 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R2
),
41 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R3
),
42 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R4
),
43 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R5
),
44 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R6
),
45 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R7
),
46 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R8
),
47 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R9
),
48 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R10
),
49 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R11
),
50 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R12
),
51 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, SP
),
52 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, LR
),
53 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, PC
),
79 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, CPSR
)
82 // restore warnings for RVCT
84 #pragma diag_default 1296
88 Return the number of entries in the gExceptionType[]
90 @retval UINTN, the number of entries in the gExceptionType[] array.
97 return sizeof (gExceptionType
) / sizeof (EFI_EXCEPTION_TYPE_ENTRY
);
102 Return the number of entries in the gRegisters[]
104 @retval UINTN, the number of entries (registers) in the gRegisters[] array.
111 return sizeof (gRegisterOffsets
) / sizeof (UINTN
);
116 Check to see if the ISA is supported.
117 ISA = Instruction Set Architecture
119 @retval TRUE if Isa is supported
124 IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
136 This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
137 It is, by default, set to find the register pointer of the ARM member
138 @param SystemContext Register content at time of the exception
139 @param RegNumber The register to which we want to find a pointer
140 @retval the pointer to the RegNumber-th pointer
143 FindPointerToRegister (
144 IN EFI_SYSTEM_CONTEXT SystemContext
,
149 ASSERT(gRegisterOffsets
[RegNumber
] < 0xF00);
150 TempPtr
= ((UINT8
*)SystemContext
.SystemContextArm
) + gRegisterOffsets
[RegNumber
];
151 return (UINT32
*)TempPtr
;
156 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
157 @param SystemContext Register content at time of the exception
158 @param RegNumber the number of the register that we want to read
159 @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
160 @retval the pointer to the next character of the output buffer that is available to be written on.
164 IN EFI_SYSTEM_CONTEXT SystemContext
,
172 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
173 AsciiSPrint (OutBufPtr
, 9, "00000000");
179 while (RegSize
< 32) {
180 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister (SystemContext
, RegNumber
) >> (RegSize
+4)) & 0xf)];
181 if ((Char
>= 'A') && (Char
<= 'F')) {
182 Char
= Char
- 'A' + 'a';
186 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister (SystemContext
, RegNumber
) >> RegSize
) & 0xf)];
187 if ((Char
>= 'A') && (Char
<= 'F')) {
188 Char
= Char
- 'A' + 'a';
192 RegSize
= RegSize
+ 8;
199 Reads the n-th register's value into an output buffer and sends it as a packet
200 @param SystemContext Register content at time of the exception
201 @param InBuffer Pointer to the input buffer received from gdb server
205 IN EFI_SYSTEM_CONTEXT SystemContext
,
210 CHAR8 OutBuffer
[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
211 CHAR8
*OutBufPtr
; // pointer to the output buffer
213 RegNumber
= AsciiStrHexToUintn (&InBuffer
[1]);
215 if (RegNumber
>= MaxRegisterCount ()) {
216 SendError (GDB_EINVALIDREGNUM
);
220 OutBufPtr
= OutBuffer
;
221 OutBufPtr
= BasicReadRegister (SystemContext
, RegNumber
, OutBufPtr
);
223 *OutBufPtr
= '\0'; // the end of the buffer
224 SendPacket (OutBuffer
);
229 Reads the general registers into an output buffer and sends it as a packet
230 @param SystemContext Register content at time of the exception
234 ReadGeneralRegisters (
235 IN EFI_SYSTEM_CONTEXT SystemContext
241 UINTN RegisterCount
= MaxRegisterCount ();
243 // It is not safe to allocate pool here....
244 OutBuffer
= AllocatePool ((RegisterCount
* 8) + 1); // 8 bytes per register in string format plus a null to terminate
245 OutBufPtr
= OutBuffer
;
246 for (Index
= 0; Index
< RegisterCount
; Index
++) {
247 OutBufPtr
= BasicReadRegister (SystemContext
, Index
, OutBufPtr
);
251 SendPacket (OutBuffer
);
252 FreePool (OutBuffer
);
257 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
258 @param SystemContext Register content at time of the exception
259 @param RegNumber the number of the register that we want to write
260 @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
261 @retval the pointer to the next character of the input buffer that can be used
264 *BasicWriteRegister (
265 IN EFI_SYSTEM_CONTEXT SystemContext
,
271 UINTN TempValue
; // the value transferred from a hex char
272 UINT32 NewValue
; // the new value of the RegNumber-th Register
274 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
280 while (RegSize
< 32) {
281 TempValue
= HexCharToInt (*InBufPtr
++);
283 if ((INTN
)TempValue
< 0) {
284 SendError (GDB_EBADMEMDATA
);
288 NewValue
+= (TempValue
<< (RegSize
+4));
289 TempValue
= HexCharToInt (*InBufPtr
++);
291 if ((INTN
)TempValue
< 0) {
292 SendError (GDB_EBADMEMDATA
);
296 NewValue
+= (TempValue
<< RegSize
);
297 RegSize
= RegSize
+ 8;
299 *(FindPointerToRegister (SystemContext
, RegNumber
)) = NewValue
;
304 /** ‘P n...=r...’
305 Writes the new value of n-th register received into the input buffer to the n-th register
306 @param SystemContext Register content at time of the exception
307 @param InBuffer Ponter to the input buffer received from gdb server
311 IN EFI_SYSTEM_CONTEXT SystemContext
,
316 CHAR8 RegNumBuffer
[MAX_REG_NUM_BUF_SIZE
]; // put the 'n..' part of the message into this array
318 CHAR8
*InBufPtr
; // pointer to the input buffer
320 // find the register number to write
321 InBufPtr
= &InBuffer
[1];
322 RegNumBufPtr
= RegNumBuffer
;
323 while (*InBufPtr
!= '=') {
324 *RegNumBufPtr
++ = *InBufPtr
++;
326 *RegNumBufPtr
= '\0';
327 RegNumber
= AsciiStrHexToUintn (RegNumBuffer
);
329 // check if this is a valid Register Number
330 if (RegNumber
>= MaxRegisterCount ()) {
331 SendError (GDB_EINVALIDREGNUM
);
334 InBufPtr
++; // skips the '=' character
335 BasicWriteRegister (SystemContext
, RegNumber
, InBufPtr
);
341 Writes the new values received into the input buffer to the general registers
342 @param SystemContext Register content at time of the exception
343 @param InBuffer Pointer to the input buffer received from gdb server
348 WriteGeneralRegisters (
349 IN EFI_SYSTEM_CONTEXT SystemContext
,
354 CHAR8
*InBufPtr
; /// pointer to the input buffer
356 UINTN RegisterCount
= MaxRegisterCount ();
358 MinLength
= (RegisterCount
* 8) + 1; // 'G' plus the registers in ASCII format
360 if (AsciiStrLen (InBuffer
) < MinLength
) {
361 //Bad message. Message is not the right length
362 SendError (GDB_EBADBUFSIZE
);
366 InBufPtr
= &InBuffer
[1];
368 // Read the new values for the registers from the input buffer to an array, NewValueArray.
369 // The values in the array are in the gdb ordering
370 for (i
= 0; i
< RegisterCount
; i
++) {
371 InBufPtr
= BasicWriteRegister (SystemContext
, i
, InBufPtr
);
378 // Use SWI 0xdbdbdb as the debug instruction
379 #define GDB_ARM_BKPT 0xefdbdbdb
381 BOOLEAN mSingleStepActive
= FALSE
;
382 UINT32 mSingleStepPC
;
383 UINT32 mSingleStepData
;
384 UINTN mSingleStepDataSize
;
391 } ARM_SOFTWARE_BREAKPOINT
;
393 #define ARM_SOFTWARE_BREAKPOINT_SIGNATURE SIGNATURE_64('A', 'R', 'M', 'B', 'R', 'K', 'P', 'T')
394 #define ARM_SOFTWARE_BREAKPOINT_FROM_LINK(a) CR(a, ARM_SOFTWARE_BREAKPOINT, Link, ARM_SOFTWARE_BREAKPOINT_SIGNATURE)
396 LIST_ENTRY BreakpointList
;
399 Insert Single Step in the SystemContext
401 @param SystemContext Register content at time of the exception
405 IN EFI_SYSTEM_CONTEXT SystemContext
408 if (mSingleStepActive
) {
409 // Currently don't support nesting
412 mSingleStepActive
= TRUE
;
414 mSingleStepPC
= SystemContext
.SystemContextArm
->PC
;
416 mSingleStepDataSize
= sizeof (UINT32
);
417 mSingleStepData
= (*(UINT32
*)mSingleStepPC
);
418 *(UINT32
*)mSingleStepPC
= GDB_ARM_BKPT
;
419 if (*(UINT32
*)mSingleStepPC
!= GDB_ARM_BKPT
) {
420 // For some reason our breakpoint did not take
421 mSingleStepActive
= FALSE
;
424 InvalidateInstructionCacheRange ((VOID
*)mSingleStepPC
, mSingleStepDataSize
);
425 //DEBUG((EFI_D_ERROR, "AddSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, mSingleStepData, *(UINT32 *)mSingleStepPC));
430 Remove Single Step in the SystemContext
432 @param SystemContext Register content at time of the exception
436 IN EFI_SYSTEM_CONTEXT SystemContext
439 if (!mSingleStepActive
) {
443 if (mSingleStepDataSize
== sizeof (UINT16
)) {
444 *(UINT16
*)mSingleStepPC
= (UINT16
)mSingleStepData
;
446 //DEBUG((EFI_D_ERROR, "RemoveSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, *(UINT32 *)mSingleStepPC, mSingleStepData));
447 *(UINT32
*)mSingleStepPC
= mSingleStepData
;
449 InvalidateInstructionCacheRange ((VOID
*)mSingleStepPC
, mSingleStepDataSize
);
450 mSingleStepActive
= FALSE
;
456 Continue. addr is Address to resume. If addr is omitted, resume at current
459 @param SystemContext Register content at time of the exception
464 IN EFI_SYSTEM_CONTEXT SystemContext
,
468 if (PacketData
[1] != '\0') {
469 SystemContext
.SystemContextArm
->PC
= AsciiStrHexToUintn (&PacketData
[1]);
475 Single step. addr is the Address at which to resume. If addr is omitted, resume
478 @param SystemContext Register content at time of the exception
483 IN EFI_SYSTEM_CONTEXT SystemContext
,
491 GetBreakpointDataAddress (
492 IN EFI_SYSTEM_CONTEXT SystemContext
,
493 IN UINTN BreakpointNumber
500 GetBreakpointDetected (
501 IN EFI_SYSTEM_CONTEXT SystemContext
509 IN EFI_SYSTEM_CONTEXT SystemContext
,
510 IN UINTN BreakpointNumber
516 ARM_SOFTWARE_BREAKPOINT
*
517 SearchBreakpointList (
522 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
524 Current
= GetFirstNode (&BreakpointList
);
525 while (!IsNull (&BreakpointList
, Current
)) {
526 Breakpoint
= ARM_SOFTWARE_BREAKPOINT_FROM_LINK(Current
);
528 if (Address
== Breakpoint
->Address
) {
532 Current
= GetNextNode (&BreakpointList
, Current
);
543 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
545 Breakpoint
= SearchBreakpointList (Address
);
547 if (Breakpoint
!= NULL
) {
551 // create and fill breakpoint structure
552 Breakpoint
= AllocatePool (sizeof(ARM_SOFTWARE_BREAKPOINT
));
554 Breakpoint
->Signature
= ARM_SOFTWARE_BREAKPOINT_SIGNATURE
;
555 Breakpoint
->Address
= Address
;
556 Breakpoint
->Instruction
= *(UINT32
*)Address
;
558 // Add it to the list
559 InsertTailList (&BreakpointList
, &Breakpoint
->Link
);
561 // Insert the software breakpoint
562 *(UINT32
*)Address
= GDB_ARM_BKPT
;
563 InvalidateInstructionCacheRange ((VOID
*)Address
, 4);
565 //DEBUG((EFI_D_ERROR, "SetBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, Breakpoint->Instruction, *(UINT32 *)Address));
573 ARM_SOFTWARE_BREAKPOINT
*Breakpoint
;
575 Breakpoint
= SearchBreakpointList (Address
);
577 if (Breakpoint
== NULL
) {
581 // Add it to the list
582 RemoveEntryList (&Breakpoint
->Link
);
584 // Restore the original instruction
585 *(UINT32
*)Address
= Breakpoint
->Instruction
;
586 InvalidateInstructionCacheRange ((VOID
*)Address
, 4);
588 //DEBUG((EFI_D_ERROR, "ClearBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, GDB_ARM_BKPT, *(UINT32 *)Address));
590 FreePool (Breakpoint
);
596 IN EFI_SYSTEM_CONTEXT SystemContext
,
605 ErrorCode
= ParseBreakpointPacket (PacketData
, &Type
, &Address
, &Length
);
607 SendError ((UINT8
)ErrorCode
);
612 case 0: //Software breakpoint
616 DEBUG((EFI_D_ERROR
, "Insert breakpoint default: %x\n", Type
));
617 SendError (GDB_EINVALIDBRKPOINTTYPE
);
621 SetBreakpoint (Address
);
629 IN EFI_SYSTEM_CONTEXT SystemContext
,
638 //Parse breakpoint packet data
639 ErrorCode
= ParseBreakpointPacket (PacketData
, &Type
, &Address
, &Length
);
641 SendError ((UINT8
)ErrorCode
);
646 case 0: //Software breakpoint
650 SendError (GDB_EINVALIDBRKPOINTTYPE
);
654 ClearBreakpoint (Address
);
660 InitializeProcessor (
664 // Initialize breakpoint list
665 InitializeListHead (&BreakpointList
);
673 if ((UINT32
)Address
< 0x80000000) {
682 IN EFI_EXCEPTION_TYPE ExceptionType
,
683 IN OUT EFI_SYSTEM_CONTEXT SystemContext
686 UINT32 ExceptionAddress
;
689 // Is it a debugger SWI?
690 ExceptionAddress
= SystemContext
.SystemContextArm
->PC
-= 4;
691 Instruction
= *(UINT32
*)ExceptionAddress
;
692 if (Instruction
!= GDB_ARM_BKPT
) {
696 // Special for SWI-based exception handling. SWI sets up the context
697 // to return to the instruction following the SWI instruction - NOT what we want
699 SystemContext
.SystemContextArm
->PC
= ExceptionAddress
;