• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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