1 /** @file
2 Processor specific parts of the GDB stub
3
4 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
5
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
10
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.
13
14 **/
15
16
17 #include <GdbDebugAgent.h>
18 #include <Library/PrintLib.h>
19 #include <Library/ArmLib.h>
20
21 //
22 // Externs from the exception handler assembly file
23 //
24 VOID
25 ExceptionHandlersStart (
26 VOID
27 );
28
29 VOID
30 ExceptionHandlersEnd (
31 VOID
32 );
33
34 VOID
35 CommonExceptionEntry (
36 VOID
37 );
38
39 VOID
40 AsmCommonExceptionEntry (
41 VOID
42 );
43
44
45 //
46 // Array of exception types that need to be hooked by the debugger
47 // (efi, gdb) //efi number
48 //
49 EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {
50 { EXCEPT_ARM_SOFTWARE_INTERRUPT, GDB_SIGTRAP },
51 { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },
52 { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },
53 { EXCEPT_ARM_DATA_ABORT, GDB_SIGTRAP }, // GDB_SIGEMT
54 { EXCEPT_ARM_RESERVED, GDB_SIGTRAP }, // GDB_SIGILL
55 { EXCEPT_ARM_FIQ, GDB_SIGINT } // Used for ctrl-c
56 };
57
58 // Shut up some annoying RVCT warnings
59 #ifdef __CC_ARM
60 #pragma diag_suppress 1296
61 #endif
62
63 UINTN gRegisterOffsets[] = {
64 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R0),
65 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R1),
66 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R2),
67 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R3),
68 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R4),
69 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R5),
70 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R6),
71 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R7),
72 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R8),
73 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R9),
74 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R10),
75 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R11),
76 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R12),
77 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, SP),
78 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, LR),
79 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, PC),
80 0x00000F01, // f0
81 0x00000F02,
82 0x00000F03,
83 0x00000F11, // f1
84 0x00000F12,
85 0x00000F13,
86 0x00000F21, // f2
87 0x00000F22,
88 0x00000F23,
89 0x00000F31, // f3
90 0x00000F32,
91 0x00000F33,
92 0x00000F41, // f4
93 0x00000F42,
94 0x00000F43,
95 0x00000F51, // f5
96 0x00000F52,
97 0x00000F53,
98 0x00000F61, // f6
99 0x00000F62,
100 0x00000F63,
101 0x00000F71, // f7
102 0x00000F72,
103 0x00000F73,
104 0x00000FFF, // fps
105 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)
106 };
107
108 // restore warnings for RVCT
109 #ifdef __CC_ARM
110 #pragma diag_default 1296
111 #endif
112
113
114 /**
115 Return the number of entries in the gExceptionType[]
116
117 @retval UINTN, the number of entries in the gExceptionType[] array.
118 **/
119 UINTN
MaxEfiException(VOID)120 MaxEfiException (
121 VOID
122 )
123 {
124 return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY);
125 }
126
127
128
129
130 /**
131 Check to see if the ISA is supported.
132 ISA = Instruction Set Architecture
133
134 @retval TRUE if Isa is supported
135
136 **/
137 BOOLEAN
CheckIsa(IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa)138 CheckIsa (
139 IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
140 )
141 {
142 if (Isa == IsaArm) {
143 return TRUE;
144 } else {
145 return FALSE;
146 }
147 }
148
149
150 /**
151 This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
152 It is, by default, set to find the register pointer of the ARM member
153 @param SystemContext Register content at time of the exception
154 @param RegNumber The register to which we want to find a pointer
155 @retval the pointer to the RegNumber-th pointer
156 **/
157 UINTN *
FindPointerToRegister(IN EFI_SYSTEM_CONTEXT SystemContext,IN UINTN RegNumber)158 FindPointerToRegister(
159 IN EFI_SYSTEM_CONTEXT SystemContext,
160 IN UINTN RegNumber
161 )
162 {
163 UINT8 *TempPtr;
164 ASSERT(gRegisterOffsets[RegNumber] < 0xF00);
165 TempPtr = ((UINT8 *)SystemContext.SystemContextArm) + gRegisterOffsets[RegNumber];
166 return (UINT32 *)TempPtr;
167 }
168
169
170 /**
171 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
172 @param SystemContext Register content at time of the exception
173 @param RegNumber the number of the register that we want to read
174 @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
175 @retval the pointer to the next character of the output buffer that is available to be written on.
176 **/
177 CHAR8 *
BasicReadRegister(IN EFI_SYSTEM_CONTEXT SystemContext,IN UINTN RegNumber,IN CHAR8 * OutBufPtr)178 BasicReadRegister (
179 IN EFI_SYSTEM_CONTEXT SystemContext,
180 IN UINTN RegNumber,
181 IN CHAR8 *OutBufPtr
182 )
183 {
184 UINTN RegSize;
185 CHAR8 Char;
186
187 if (gRegisterOffsets[RegNumber] > 0xF00) {
188 AsciiSPrint(OutBufPtr, 9, "00000000");
189 OutBufPtr += 8;
190 return OutBufPtr;
191 }
192
193 RegSize = 0;
194 while (RegSize < 32) {
195 Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)];
196 if ((Char >= 'A') && (Char <= 'F')) {
197 Char = Char - 'A' + 'a';
198 }
199 *OutBufPtr++ = Char;
200
201 Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)];
202 if ((Char >= 'A') && (Char <= 'F')) {
203 Char = Char - 'A' + 'a';
204 }
205 *OutBufPtr++ = Char;
206
207 RegSize = RegSize + 8;
208 }
209 return OutBufPtr;
210 }
211
212
213 /**
214 Reads the n-th register's value into an output buffer and sends it as a packet
215 @param SystemContext Register content at time of the exception
216 @param InBuffer Pointer to the input buffer received from gdb server
217 **/
218 VOID
ReadNthRegister(IN EFI_SYSTEM_CONTEXT SystemContext,IN CHAR8 * InBuffer)219 ReadNthRegister (
220 IN EFI_SYSTEM_CONTEXT SystemContext,
221 IN CHAR8 *InBuffer
222 )
223 {
224 UINTN RegNumber;
225 CHAR8 OutBuffer[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
226 CHAR8 *OutBufPtr; // pointer to the output buffer
227
228 RegNumber = AsciiStrHexToUintn (&InBuffer[1]);
229
230 if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {
231 SendError (GDB_EINVALIDREGNUM);
232 return;
233 }
234
235 OutBufPtr = OutBuffer;
236 OutBufPtr = BasicReadRegister (SystemContext, RegNumber, OutBufPtr);
237
238 *OutBufPtr = '\0'; // the end of the buffer
239 SendPacket(OutBuffer);
240 }
241
242
243 /**
244 Reads the general registers into an output buffer and sends it as a packet
245 @param SystemContext Register content at time of the exception
246 **/
247 VOID
248 EFIAPI
ReadGeneralRegisters(IN EFI_SYSTEM_CONTEXT SystemContext)249 ReadGeneralRegisters (
250 IN EFI_SYSTEM_CONTEXT SystemContext
251 )
252 {
253 UINTN Index;
254 // a UINT32 takes 8 ascii characters
255 CHAR8 OutBuffer[(sizeof (gRegisterOffsets) * 2) + 1];
256 CHAR8 *OutBufPtr;
257
258 // It is not safe to allocate pool here....
259 OutBufPtr = OutBuffer;
260 for (Index = 0; Index < (sizeof (gRegisterOffsets)/sizeof (UINTN)); Index++) {
261 OutBufPtr = BasicReadRegister (SystemContext, Index, OutBufPtr);
262 }
263
264 *OutBufPtr = '\0';
265 SendPacket(OutBuffer);
266 }
267
268
269 /**
270 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
271 @param SystemContext Register content at time of the exception
272 @param RegNumber the number of the register that we want to write
273 @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
274 @retval the pointer to the next character of the input buffer that can be used
275 **/
276 CHAR8 *
BasicWriteRegister(IN EFI_SYSTEM_CONTEXT SystemContext,IN UINTN RegNumber,IN CHAR8 * InBufPtr)277 BasicWriteRegister (
278 IN EFI_SYSTEM_CONTEXT SystemContext,
279 IN UINTN RegNumber,
280 IN CHAR8 *InBufPtr
281 )
282 {
283 UINTN RegSize;
284 UINTN TempValue; // the value transferred from a hex char
285 UINT32 NewValue; // the new value of the RegNumber-th Register
286
287 if (gRegisterOffsets[RegNumber] > 0xF00) {
288 return InBufPtr + 8;
289 }
290
291 NewValue = 0;
292 RegSize = 0;
293 while (RegSize < 32) {
294 TempValue = HexCharToInt(*InBufPtr++);
295
296 if ((INTN)TempValue < 0) {
297 SendError (GDB_EBADMEMDATA);
298 return NULL;
299 }
300
301 NewValue += (TempValue << (RegSize+4));
302 TempValue = HexCharToInt(*InBufPtr++);
303
304 if ((INTN)TempValue < 0) {
305 SendError (GDB_EBADMEMDATA);
306 return NULL;
307 }
308
309 NewValue += (TempValue << RegSize);
310 RegSize = RegSize + 8;
311 }
312 *(FindPointerToRegister(SystemContext, RegNumber)) = NewValue;
313 return InBufPtr;
314 }
315
316
317 /** ‘P n...=r...’
318 Writes the new value of n-th register received into the input buffer to the n-th register
319 @param SystemContext Register content at time of the exception
320 @param InBuffer Ponter to the input buffer received from gdb server
321 **/
322 VOID
WriteNthRegister(IN EFI_SYSTEM_CONTEXT SystemContext,IN CHAR8 * InBuffer)323 WriteNthRegister (
324 IN EFI_SYSTEM_CONTEXT SystemContext,
325 IN CHAR8 *InBuffer
326 )
327 {
328 UINTN RegNumber;
329 CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array
330 CHAR8 *RegNumBufPtr;
331 CHAR8 *InBufPtr; // pointer to the input buffer
332
333 // find the register number to write
334 InBufPtr = &InBuffer[1];
335 RegNumBufPtr = RegNumBuffer;
336 while (*InBufPtr != '=') {
337 *RegNumBufPtr++ = *InBufPtr++;
338 }
339 *RegNumBufPtr = '\0';
340 RegNumber = AsciiStrHexToUintn (RegNumBuffer);
341
342 // check if this is a valid Register Number
343 if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {
344 SendError (GDB_EINVALIDREGNUM);
345 return;
346 }
347 InBufPtr++; // skips the '=' character
348 BasicWriteRegister (SystemContext, RegNumber, InBufPtr);
349 SendSuccess();
350 }
351
352
353 /** ‘G XX...’
354 Writes the new values received into the input buffer to the general registers
355 @param SystemContext Register content at time of the exception
356 @param InBuffer Pointer to the input buffer received from gdb server
357 **/
358
359 VOID
360 EFIAPI
WriteGeneralRegisters(IN EFI_SYSTEM_CONTEXT SystemContext,IN CHAR8 * InBuffer)361 WriteGeneralRegisters (
362 IN EFI_SYSTEM_CONTEXT SystemContext,
363 IN CHAR8 *InBuffer
364 )
365 {
366 UINTN i;
367 CHAR8 *InBufPtr; /// pointer to the input buffer
368 UINTN MinLength;
369 UINTN RegisterCount = (sizeof (gRegisterOffsets)/sizeof (UINTN));
370
371 MinLength = (RegisterCount * 8) + 1; // 'G' plus the registers in ASCII format
372
373 if (AsciiStrLen(InBuffer) < MinLength) {
374 //Bad message. Message is not the right length
375 SendError (GDB_EBADBUFSIZE);
376 return;
377 }
378
379 InBufPtr = &InBuffer[1];
380
381 // Read the new values for the registers from the input buffer to an array, NewValueArray.
382 // The values in the array are in the gdb ordering
383 for(i = 0; i < RegisterCount; i++) {
384 InBufPtr = BasicWriteRegister (SystemContext, i, InBufPtr);
385 }
386
387 SendSuccess ();
388 }
389
390
391
392
393 /**
394 Continue. addr is Address to resume. If addr is omitted, resume at current
395 Address.
396
397 @param SystemContext Register content at time of the exception
398 **/
399 VOID
400 EFIAPI
ContinueAtAddress(IN EFI_SYSTEM_CONTEXT SystemContext,IN CHAR8 * PacketData)401 ContinueAtAddress (
402 IN EFI_SYSTEM_CONTEXT SystemContext,
403 IN CHAR8 *PacketData
404 )
405 {
406 if (PacketData[1] != '\0') {
407 SystemContext.SystemContextArm->PC = AsciiStrHexToUintn(&PacketData[1]);
408 }
409 }
410
411
412 /** ‘s [addr ]’
413 Single step. addr is the Address at which to resume. If addr is omitted, resume
414 at same Address.
415
416 @param SystemContext Register content at time of the exception
417 **/
418 VOID
419 EFIAPI
SingleStep(IN EFI_SYSTEM_CONTEXT SystemContext,IN CHAR8 * PacketData)420 SingleStep (
421 IN EFI_SYSTEM_CONTEXT SystemContext,
422 IN CHAR8 *PacketData
423 )
424 {
425 SendNotSupported();
426 }
427
428
429 VOID
430 EFIAPI
InsertBreakPoint(IN EFI_SYSTEM_CONTEXT SystemContext,IN CHAR8 * PacketData)431 InsertBreakPoint (
432 IN EFI_SYSTEM_CONTEXT SystemContext,
433 IN CHAR8 *PacketData
434 )
435 {
436 SendNotSupported ();
437 }
438
439 VOID
440 EFIAPI
RemoveBreakPoint(IN EFI_SYSTEM_CONTEXT SystemContext,IN CHAR8 * PacketData)441 RemoveBreakPoint (
442 IN EFI_SYSTEM_CONTEXT SystemContext,
443 IN CHAR8 *PacketData
444 )
445 {
446 SendNotSupported ();
447 }
448
449
450 /**
451 Send the T signal with the given exception type (in gdb order) and possibly
452 with n:r pairs related to the watchpoints
453
454 @param SystemContext Register content at time of the exception
455 @param GdbExceptionType GDB exception type
456 **/
457 VOID
ProcessorSendTSignal(IN EFI_SYSTEM_CONTEXT SystemContext,IN UINT8 GdbExceptionType,IN OUT CHAR8 * TSignalPtr,IN UINTN SizeOfBuffer)458 ProcessorSendTSignal (
459 IN EFI_SYSTEM_CONTEXT SystemContext,
460 IN UINT8 GdbExceptionType,
461 IN OUT CHAR8 *TSignalPtr,
462 IN UINTN SizeOfBuffer
463 )
464 {
465 *TSignalPtr = '\0';
466 }
467
468 /**
469 FIQ state is only changed by FIQ exception. We don't want to take FIQ
470 ticks in the GDB stub. The stub disables FIQ on entry, but this is the
471 third instruction that executes in the execption handler. Thus we have
472 a crack we need to test for.
473
474 @param PC PC of execption
475
476 @return TRUE We are in the GDB stub exception preamble
477 @return FALSE We are not in GDB stub code
478 **/
479 BOOLEAN
InFiqCrack(IN UINT32 PC)480 InFiqCrack (
481 IN UINT32 PC
482 )
483 {
484 UINT64 VectorBase = PcdGet64 (PcdCpuVectorBaseAddress);
485 UINT32 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
486
487 if ((PC >= VectorBase) && (PC <= (VectorBase + Length))) {
488 return TRUE;
489 }
490
491 return FALSE;
492 }
493
494
495 /**
496 Check to see if this exception is related to ctrl-c handling.
497
498 In this scheme we dedicate FIQ to the ctrl-c handler so it is
499 independent of the rest of the system.
500
501 SaveAndSetDebugTimerInterrupt () can be used to
502
503 @param ExceptionType Exception that is being processed
504 @param SystemContext Register content at time of the exception
505
506 @return TRUE This was a ctrl-c check that did not find a ctrl-c
507 @return FALSE This was not a ctrl-c check or some one hit ctrl-c
508 **/
509 BOOLEAN
ProcessorControlC(IN EFI_EXCEPTION_TYPE ExceptionType,IN OUT EFI_SYSTEM_CONTEXT SystemContext)510 ProcessorControlC (
511 IN EFI_EXCEPTION_TYPE ExceptionType,
512 IN OUT EFI_SYSTEM_CONTEXT SystemContext
513 )
514 {
515 CHAR8 Char;
516 BOOLEAN Return = TRUE;
517
518 if (ExceptionType != EXCEPT_ARM_FIQ) {
519 // Skip it as it is not related to ctrl-c
520 return FALSE;
521 }
522
523 if (InFiqCrack (SystemContext.SystemContextArm->PC)) {
524 // We are in our own interrupt preable, so skip this tick.
525 // We never want to let gdb see the debug stub running if we can help it
526 return FALSE;
527 }
528
529 while (TRUE) {
530 if (!GdbIsCharAvailable ()) {
531 //
532 // No characters are pending so exit the loop
533 //
534 Return = TRUE;
535 break;
536 }
537
538 Char = GdbGetChar ();
539 if (Char == 0x03) {
540 //
541 // We have a ctrl-c so exit and process exception for ctrl-c
542 //
543 Return = FALSE;
544 break;
545 }
546 }
547
548 DebugAgentTimerEndOfInterrupt ();
549
550 // Force an exit from the exception handler as we are done
551 return Return;
552 }
553
554
555 /**
556 Enable/Disable the interrupt of debug timer and return the interrupt state
557 prior to the operation.
558
559 If EnableStatus is TRUE, enable the interrupt of debug timer.
560 If EnableStatus is FALSE, disable the interrupt of debug timer.
561
562 @param[in] EnableStatus Enable/Disable.
563
564 @retval TRUE Debug timer interrupt were enabled on entry to this call.
565 @retval FALSE Debug timer interrupt were disabled on entry to this call.
566
567 **/
568 BOOLEAN
569 EFIAPI
SaveAndSetDebugTimerInterrupt(IN BOOLEAN EnableStatus)570 SaveAndSetDebugTimerInterrupt (
571 IN BOOLEAN EnableStatus
572 )
573 {
574 BOOLEAN FiqEnabled;
575
576 FiqEnabled = ArmGetFiqState ();
577
578 if (EnableStatus) {
579 DebugAgentTimerSetPeriod (PcdGet32 (PcdGdbTimerPeriodMilliseconds));
580 ArmEnableFiq ();
581 } else {
582 DebugAgentTimerSetPeriod (0);
583 ArmDisableFiq ();
584 }
585
586 return FiqEnabled;
587 }
588
589
590
591 VOID
592 GdbFPutString (
593 IN CHAR8 *String
594 );
595
596 /**
597 Initialize debug agent.
598
599 This function is used to set up debug environment to support source level debugging.
600 If certain Debug Agent Library instance has to save some private data in the stack,
601 this function must work on the mode that doesn't return to the caller, then
602 the caller needs to wrap up all rest of logic after InitializeDebugAgent() into one
603 function and pass it into InitializeDebugAgent(). InitializeDebugAgent() is
604 responsible to invoke the passing-in function at the end of InitializeDebugAgent().
605
606 If the parameter Function is not NULL, Debug Agent Library instance will invoke it by
607 passing in the Context to be its parameter.
608
609 If Function() is NULL, Debug Agent Library instance will return after setup debug
610 environment.
611
612 @param[in] InitFlag Init flag is used to decide the initialize process.
613 @param[in] Context Context needed according to InitFlag; it was optional.
614 @param[in] Function Continue function called by debug agent library; it was
615 optional.
616
617 **/
618 VOID
619 EFIAPI
InitializeDebugAgent(IN UINT32 InitFlag,IN VOID * Context,OPTIONAL IN DEBUG_AGENT_CONTINUE Function OPTIONAL)620 InitializeDebugAgent (
621 IN UINT32 InitFlag,
622 IN VOID *Context, OPTIONAL
623 IN DEBUG_AGENT_CONTINUE Function OPTIONAL
624 )
625 {
626 UINTN Offset;
627 UINTN Length;
628 BOOLEAN IrqEnabled;
629 UINT64 *VectorBase;
630
631
632 //
633 // Disable interrupts
634 //
635 IrqEnabled = ArmGetInterruptState ();
636 ArmDisableInterrupts ();
637 ArmDisableFiq ();
638
639 //
640 // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.
641 //
642 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
643
644 //
645 // Reserve space for the exception handlers
646 //
647 VectorBase = (UINT64 *)(UINTN)PcdGet64 (PcdCpuVectorBaseAddress);
648
649
650 // Copy our assembly code into the page that contains the exception vectors.
651 CopyMem ((VOID *)VectorBase, (VOID *)ExceptionHandlersStart, Length);
652
653 //
654 // Patch in the common Assembly exception handler
655 //
656 Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;
657 *(UINTN *) (((UINT8 *)VectorBase) + Offset) = (UINTN)AsmCommonExceptionEntry;
658
659 // Flush Caches since we updated executable stuff
660 InvalidateInstructionCacheRange ((VOID *)(UINTN)PcdGet64(PcdCpuVectorBaseAddress), Length);
661
662 // setup a timer so gdb can break in via ctrl-c
663 DebugAgentTimerIntialize ();
664
665 if (IrqEnabled) {
666 ArmEnableInterrupts ();
667 }
668
669 if (Function != NULL) {
670 Function (Context);
671 }
672
673 return;
674 }
675
676