2 Processor specific parts of the GDB stub
4 Copyright (c) 2008-2010, Apple Inc. All rights reserved.
6 All rights reserved. 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.
17 #include <GdbDebugAgent.h>
18 #include <Library/CacheMaintenanceLib.h>
19 #include <Library/PrintLib.h>
20 #include <Library/ArmLib.h>
23 // Externs from the exception handler assembly file
26 ExceptionHandlersStart (
31 ExceptionHandlersEnd (
36 CommonExceptionEntry (
41 AsmCommonExceptionEntry (
47 // Array of exception types that need to be hooked by the debugger
48 // (efi, gdb) //efi number
50 EFI_EXCEPTION_TYPE_ENTRY gExceptionType
[] = {
51 { EXCEPT_ARM_SOFTWARE_INTERRUPT
, GDB_SIGTRAP
},
52 { EXCEPT_ARM_UNDEFINED_INSTRUCTION
, GDB_SIGTRAP
},
53 { EXCEPT_ARM_PREFETCH_ABORT
, GDB_SIGTRAP
},
54 { EXCEPT_ARM_DATA_ABORT
, GDB_SIGTRAP
}, // GDB_SIGEMT
55 { EXCEPT_ARM_RESERVED
, GDB_SIGTRAP
}, // GDB_SIGILL
56 { EXCEPT_ARM_FIQ
, GDB_SIGINT
} // Used for ctrl-c
59 // Shut up some annoying RVCT warnings
61 #pragma diag_suppress 1296
64 UINTN gRegisterOffsets
[] = {
65 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R0
),
66 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R1
),
67 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R2
),
68 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R3
),
69 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R4
),
70 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R5
),
71 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R6
),
72 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R7
),
73 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R8
),
74 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R9
),
75 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R10
),
76 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R11
),
77 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, R12
),
78 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, SP
),
79 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, LR
),
80 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, PC
),
106 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM
, CPSR
)
109 // restore warnings for RVCT
111 #pragma diag_default 1296
116 Return the number of entries in the gExceptionType[]
118 @retval UINTN, the number of entries in the gExceptionType[] array.
125 return sizeof (gExceptionType
)/sizeof (EFI_EXCEPTION_TYPE_ENTRY
);
132 Check to see if the ISA is supported.
133 ISA = Instruction Set Architecture
135 @retval TRUE if Isa is supported
140 IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
152 This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
153 It is, by default, set to find the register pointer of the ARM member
154 @param SystemContext Register content at time of the exception
155 @param RegNumber The register to which we want to find a pointer
156 @retval the pointer to the RegNumber-th pointer
159 FindPointerToRegister(
160 IN EFI_SYSTEM_CONTEXT SystemContext
,
165 ASSERT(gRegisterOffsets
[RegNumber
] < 0xF00);
166 TempPtr
= ((UINT8
*)SystemContext
.SystemContextArm
) + gRegisterOffsets
[RegNumber
];
167 return (UINT32
*)TempPtr
;
172 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
173 @param SystemContext Register content at time of the exception
174 @param RegNumber the number of the register that we want to read
175 @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
176 @retval the pointer to the next character of the output buffer that is available to be written on.
180 IN EFI_SYSTEM_CONTEXT SystemContext
,
188 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
189 AsciiSPrint(OutBufPtr
, 9, "00000000");
195 while (RegSize
< 32) {
196 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister(SystemContext
, RegNumber
) >> (RegSize
+4)) & 0xf)];
197 if ((Char
>= 'A') && (Char
<= 'F')) {
198 Char
= Char
- 'A' + 'a';
202 Char
= mHexToStr
[(UINT8
)((*FindPointerToRegister(SystemContext
, RegNumber
) >> RegSize
) & 0xf)];
203 if ((Char
>= 'A') && (Char
<= 'F')) {
204 Char
= Char
- 'A' + 'a';
208 RegSize
= RegSize
+ 8;
215 Reads the n-th register's value into an output buffer and sends it as a packet
216 @param SystemContext Register content at time of the exception
217 @param InBuffer Pointer to the input buffer received from gdb server
221 IN EFI_SYSTEM_CONTEXT SystemContext
,
226 CHAR8 OutBuffer
[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
227 CHAR8
*OutBufPtr
; // pointer to the output buffer
229 RegNumber
= AsciiStrHexToUintn (&InBuffer
[1]);
231 if (RegNumber
>= (sizeof (gRegisterOffsets
)/sizeof (UINTN
))) {
232 SendError (GDB_EINVALIDREGNUM
);
236 OutBufPtr
= OutBuffer
;
237 OutBufPtr
= BasicReadRegister (SystemContext
, RegNumber
, OutBufPtr
);
239 *OutBufPtr
= '\0'; // the end of the buffer
240 SendPacket(OutBuffer
);
245 Reads the general registers into an output buffer and sends it as a packet
246 @param SystemContext Register content at time of the exception
250 ReadGeneralRegisters (
251 IN EFI_SYSTEM_CONTEXT SystemContext
255 // a UINT32 takes 8 ascii characters
256 CHAR8 OutBuffer
[(sizeof (gRegisterOffsets
) * 2) + 1];
259 // It is not safe to allocate pool here....
260 OutBufPtr
= OutBuffer
;
261 for (Index
= 0; Index
< (sizeof (gRegisterOffsets
)/sizeof (UINTN
)); Index
++) {
262 OutBufPtr
= BasicReadRegister (SystemContext
, Index
, OutBufPtr
);
266 SendPacket(OutBuffer
);
271 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
272 @param SystemContext Register content at time of the exception
273 @param RegNumber the number of the register that we want to write
274 @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
275 @retval the pointer to the next character of the input buffer that can be used
279 IN EFI_SYSTEM_CONTEXT SystemContext
,
285 UINTN TempValue
; // the value transferred from a hex char
286 UINT32 NewValue
; // the new value of the RegNumber-th Register
288 if (gRegisterOffsets
[RegNumber
] > 0xF00) {
294 while (RegSize
< 32) {
295 TempValue
= HexCharToInt(*InBufPtr
++);
297 if ((INTN
)TempValue
< 0) {
298 SendError (GDB_EBADMEMDATA
);
302 NewValue
+= (TempValue
<< (RegSize
+4));
303 TempValue
= HexCharToInt(*InBufPtr
++);
305 if ((INTN
)TempValue
< 0) {
306 SendError (GDB_EBADMEMDATA
);
310 NewValue
+= (TempValue
<< RegSize
);
311 RegSize
= RegSize
+ 8;
313 *(FindPointerToRegister(SystemContext
, RegNumber
)) = NewValue
;
318 /** ‘P n...=r...’
319 Writes the new value of n-th register received into the input buffer to the n-th register
320 @param SystemContext Register content at time of the exception
321 @param InBuffer Ponter to the input buffer received from gdb server
325 IN EFI_SYSTEM_CONTEXT SystemContext
,
330 CHAR8 RegNumBuffer
[MAX_REG_NUM_BUF_SIZE
]; // put the 'n..' part of the message into this array
332 CHAR8
*InBufPtr
; // pointer to the input buffer
334 // find the register number to write
335 InBufPtr
= &InBuffer
[1];
336 RegNumBufPtr
= RegNumBuffer
;
337 while (*InBufPtr
!= '=') {
338 *RegNumBufPtr
++ = *InBufPtr
++;
340 *RegNumBufPtr
= '\0';
341 RegNumber
= AsciiStrHexToUintn (RegNumBuffer
);
343 // check if this is a valid Register Number
344 if (RegNumber
>= (sizeof (gRegisterOffsets
)/sizeof (UINTN
))) {
345 SendError (GDB_EINVALIDREGNUM
);
348 InBufPtr
++; // skips the '=' character
349 BasicWriteRegister (SystemContext
, RegNumber
, InBufPtr
);
355 Writes the new values received into the input buffer to the general registers
356 @param SystemContext Register content at time of the exception
357 @param InBuffer Pointer to the input buffer received from gdb server
362 WriteGeneralRegisters (
363 IN EFI_SYSTEM_CONTEXT SystemContext
,
368 CHAR8
*InBufPtr
; /// pointer to the input buffer
370 UINTN RegisterCount
= (sizeof (gRegisterOffsets
)/sizeof (UINTN
));
372 MinLength
= (RegisterCount
* 8) + 1; // 'G' plus the registers in ASCII format
374 if (AsciiStrLen(InBuffer
) < MinLength
) {
375 //Bad message. Message is not the right length
376 SendError (GDB_EBADBUFSIZE
);
380 InBufPtr
= &InBuffer
[1];
382 // Read the new values for the registers from the input buffer to an array, NewValueArray.
383 // The values in the array are in the gdb ordering
384 for(i
= 0; i
< RegisterCount
; i
++) {
385 InBufPtr
= BasicWriteRegister (SystemContext
, i
, InBufPtr
);
395 Continue. addr is Address to resume. If addr is omitted, resume at current
398 @param SystemContext Register content at time of the exception
403 IN EFI_SYSTEM_CONTEXT SystemContext
,
407 if (PacketData
[1] != '\0') {
408 SystemContext
.SystemContextArm
->PC
= AsciiStrHexToUintn(&PacketData
[1]);
414 Single step. addr is the Address at which to resume. If addr is omitted, resume
417 @param SystemContext Register content at time of the exception
422 IN EFI_SYSTEM_CONTEXT SystemContext
,
433 IN EFI_SYSTEM_CONTEXT SystemContext
,
443 IN EFI_SYSTEM_CONTEXT SystemContext
,
452 Send the T signal with the given exception type (in gdb order) and possibly
453 with n:r pairs related to the watchpoints
455 @param SystemContext Register content at time of the exception
456 @param GdbExceptionType GDB exception type
459 ProcessorSendTSignal (
460 IN EFI_SYSTEM_CONTEXT SystemContext
,
461 IN UINT8 GdbExceptionType
,
462 IN OUT CHAR8
*TSignalPtr
,
463 IN UINTN SizeOfBuffer
470 FIQ state is only changed by FIQ exception. We don't want to take FIQ
471 ticks in the GDB stub. The stub disables FIQ on entry, but this is the
472 third instruction that executes in the execption handler. Thus we have
473 a crack we need to test for.
475 @param PC PC of execption
477 @return TRUE We are in the GDB stub exception preamble
478 @return FALSE We are not in GDB stub code
485 UINT32 VectorBase
= PcdGet32 (PcdCpuVectorBaseAddress
);
486 UINT32 Length
= (UINTN
)ExceptionHandlersEnd
- (UINTN
)ExceptionHandlersStart
;
488 if ((PC
>= VectorBase
) && (PC
<= (VectorBase
+ Length
))) {
497 Check to see if this exception is related to ctrl-c handling.
499 In this scheme we dedicate FIQ to the ctrl-c handler so it is
500 independent of the rest of the system.
502 SaveAndSetDebugTimerInterrupt () can be used to
504 @param ExceptionType Exception that is being processed
505 @param SystemContext Register content at time of the exception
507 @return TRUE This was a ctrl-c check that did not find a ctrl-c
508 @return FALSE This was not a ctrl-c check or some one hit ctrl-c
512 IN EFI_EXCEPTION_TYPE ExceptionType
,
513 IN OUT EFI_SYSTEM_CONTEXT SystemContext
517 BOOLEAN Return
= TRUE
;
519 if (ExceptionType
!= EXCEPT_ARM_FIQ
) {
520 // Skip it as it is not related to ctrl-c
524 if (InFiqCrack (SystemContext
.SystemContextArm
->PC
)) {
525 // We are in our own interrupt preable, so skip this tick.
526 // We never want to let gdb see the debug stub running if we can help it
531 if (!GdbIsCharAvailable ()) {
533 // No characters are pending so exit the loop
539 Char
= GdbGetChar ();
542 // We have a ctrl-c so exit and process exception for ctrl-c
549 DebugAgentTimerEndOfInterrupt ();
551 // Force an exit from the exception handler as we are done
557 Enable/Disable the interrupt of debug timer and return the interrupt state
558 prior to the operation.
560 If EnableStatus is TRUE, enable the interrupt of debug timer.
561 If EnableStatus is FALSE, disable the interrupt of debug timer.
563 @param[in] EnableStatus Enable/Disable.
565 @retval TRUE Debug timer interrupt were enabled on entry to this call.
566 @retval FALSE Debug timer interrupt were disabled on entry to this call.
571 SaveAndSetDebugTimerInterrupt (
572 IN BOOLEAN EnableStatus
577 FiqEnabled
= ArmGetFiqState ();
580 DebugAgentTimerSetPeriod (PcdGet32 (PcdGdbTimerPeriodMilliseconds
));
583 DebugAgentTimerSetPeriod (0);
598 Initialize debug agent.
600 This function is used to set up debug enviroment. It may enable interrupts.
602 @param[in] InitFlag Init flag is used to decide initialize process.
603 @param[in] Context Context needed according to InitFlag, it was optional.
608 InitializeDebugAgent (
610 IN VOID
*Context OPTIONAL
620 // Disable interrupts
622 IrqEnabled
= ArmGetInterruptState ();
623 ArmDisableInterrupts ();
627 // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.
629 Length
= (UINTN
)ExceptionHandlersEnd
- (UINTN
)ExceptionHandlersStart
;
632 // Reserve space for the exception handlers
634 VectorBase
= (UINT32
*)(UINTN
)PcdGet32 (PcdCpuVectorBaseAddress
);
637 // Copy our assembly code into the page that contains the exception vectors.
638 CopyMem ((VOID
*)VectorBase
, (VOID
*)ExceptionHandlersStart
, Length
);
641 // Patch in the common Assembly exception handler
643 Offset
= (UINTN
)CommonExceptionEntry
- (UINTN
)ExceptionHandlersStart
;
644 *(UINTN
*) (((UINT8
*)VectorBase
) + Offset
) = (UINTN
)AsmCommonExceptionEntry
;
646 // Flush Caches since we updated executable stuff
647 InvalidateInstructionCacheRange ((VOID
*)PcdGet32(PcdCpuVectorBaseAddress
), Length
);
649 // setup a timer so gdb can break in via ctrl-c
650 DebugAgentTimerIntialize ();
653 ArmEnableInterrupts ();