Home | History | Annotate | Download | only in ArmExceptionLib
      1 /* @file
      2 *  Main file supporting the SEC Phase for Versatile Express
      3 *
      4 *  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
      5 *  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
      6 *  Copyright (c) 2016 HP Development Company, L.P.
      7 *
      8 *  This program and the accompanying materials
      9 *  are licensed and made available under the terms and conditions of the BSD License
     10 *  which accompanies this distribution.  The full text of the license may be found at
     11 *  http://opensource.org/licenses/bsd-license.php
     12 *
     13 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 *
     16 **/
     17 
     18 #include <Uefi.h>
     19 #include <Library/CpuExceptionHandlerLib.h>
     20 
     21 #include <Library/ArmLib.h>
     22 #include <Library/PcdLib.h>
     23 #include <Library/CacheMaintenanceLib.h>
     24 #include <Library/BaseLib.h>
     25 #include <Library/BaseMemoryLib.h>
     26 #include <Library/DebugLib.h>
     27 #include <Library/DefaultExceptionHandlerLib.h>
     28 
     29 STATIC
     30 RETURN_STATUS
     31 CopyExceptionHandlers(
     32   IN  PHYSICAL_ADDRESS        BaseAddress
     33   );
     34 
     35 EFI_STATUS
     36 EFIAPI
     37 RegisterExceptionHandler(
     38   IN EFI_EXCEPTION_TYPE            ExceptionType,
     39   IN EFI_CPU_INTERRUPT_HANDLER     InterruptHandler
     40   );
     41 
     42 VOID
     43 ExceptionHandlersStart(
     44   VOID
     45   );
     46 
     47 VOID
     48 ExceptionHandlersEnd(
     49   VOID
     50   );
     51 
     52 RETURN_STATUS ArchVectorConfig(
     53   IN  UINTN       VectorBaseAddress
     54   );
     55 
     56 // these globals are provided by the architecture specific source (Arm or AArch64)
     57 extern UINTN                    gMaxExceptionNumber;
     58 extern EFI_EXCEPTION_CALLBACK   gExceptionHandlers[];
     59 extern EFI_EXCEPTION_CALLBACK   gDebuggerExceptionHandlers[];
     60 extern PHYSICAL_ADDRESS         gExceptionVectorAlignmentMask;
     61 extern UINTN                    gDebuggerNoHandlerValue;
     62 
     63 // A compiler flag adjusts the compilation of this library to a variant where
     64 // the vectors are relocated (copied) to another location versus using the
     65 // vectors in-place.  Since this effects an assembly .align directive we must
     66 // address this at library build time.  Since this affects the build of the
     67 // library we cannot represent this in a PCD since PCDs are evaluated on
     68 // a per-module basis.
     69 #if defined(ARM_RELOCATE_VECTORS)
     70 STATIC CONST BOOLEAN gArmRelocateVectorTable = TRUE;
     71 #else
     72 STATIC CONST BOOLEAN gArmRelocateVectorTable = FALSE;
     73 #endif
     74 
     75 
     76 /**
     77 Initializes all CPU exceptions entries and provides the default exception handlers.
     78 
     79 Caller should try to get an array of interrupt and/or exception vectors that are in use and need to
     80 persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification.
     81 If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL.
     82 If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly.
     83 
     84 @param[in]  VectorInfo    Pointer to reserved vector list.
     85 
     86 @retval EFI_SUCCESS           CPU Exception Entries have been successfully initialized
     87 with default exception handlers.
     88 @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
     89 @retval EFI_UNSUPPORTED       This function is not supported.
     90 
     91 **/
     92 EFI_STATUS
     93 EFIAPI
     94 InitializeCpuExceptionHandlers(
     95   IN EFI_VECTOR_HANDOFF_INFO       *VectorInfo OPTIONAL
     96   )
     97 {
     98   RETURN_STATUS     Status;
     99   UINTN             VectorBase;
    100 
    101   Status = EFI_SUCCESS;
    102 
    103   // if we are requested to copy exceptin handlers to another location
    104   if (gArmRelocateVectorTable) {
    105 
    106     VectorBase = PcdGet64(PcdCpuVectorBaseAddress);
    107     Status = CopyExceptionHandlers(VectorBase);
    108 
    109   }
    110   else { // use VBAR to point to where our exception handlers are
    111 
    112     // The vector table must be aligned for the architecture.  If this
    113     // assertion fails ensure the appropriate FFS alignment is in effect,
    114     // which can be accomplished by ensuring the proper Align=X statement
    115     // in the platform packaging rules.  For ARM Align=32 is required and
    116     // for AArch64 Align=4K is required.  Align=Auto can be used but this
    117     // is known to cause an issue with populating the reset vector area
    118     // for encapsulated FVs.
    119     ASSERT(((UINTN)ExceptionHandlersStart & gExceptionVectorAlignmentMask) == 0);
    120 
    121     // We do not copy the Exception Table at PcdGet64(PcdCpuVectorBaseAddress). We just set Vector
    122     // Base Address to point into CpuDxe code.
    123     VectorBase = (UINTN)ExceptionHandlersStart;
    124 
    125     Status = RETURN_SUCCESS;
    126   }
    127 
    128   if (!RETURN_ERROR(Status)) {
    129     // call the architecture-specific routine to prepare for the new vector
    130     // configuration to take effect
    131     ArchVectorConfig(VectorBase);
    132 
    133     ArmWriteVBar(VectorBase);
    134   }
    135 
    136   return RETURN_SUCCESS;
    137 }
    138 
    139 /**
    140 Copies exception handlers to the speciifed address.
    141 
    142 Caller should try to get an array of interrupt and/or exception vectors that are in use and need to
    143 persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification.
    144 If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL.
    145 If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly.
    146 
    147 @param[in]  VectorInfo    Pointer to reserved vector list.
    148 
    149 @retval EFI_SUCCESS           CPU Exception Entries have been successfully initialized
    150 with default exception handlers.
    151 @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
    152 @retval EFI_UNSUPPORTED       This function is not supported.
    153 
    154 **/
    155 STATIC
    156 RETURN_STATUS
    157 CopyExceptionHandlers(
    158   IN  PHYSICAL_ADDRESS        BaseAddress
    159   )
    160 {
    161   RETURN_STATUS        Status;
    162   UINTN                Length;
    163   UINTN                Index;
    164   UINT32               *VectorBase;
    165 
    166   // ensure that the destination value specifies an address meeting the vector alignment requirements
    167   ASSERT ((BaseAddress & gExceptionVectorAlignmentMask) == 0);
    168 
    169   //
    170   // Copy an implementation of the exception vectors to PcdCpuVectorBaseAddress.
    171   //
    172   Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
    173 
    174   VectorBase = (UINT32 *)(UINTN)BaseAddress;
    175 
    176   if (FeaturePcdGet(PcdDebuggerExceptionSupport) == TRUE) {
    177     // Save existing vector table, in case debugger is already hooked in
    178     CopyMem((VOID *)gDebuggerExceptionHandlers, (VOID *)VectorBase, sizeof (EFI_EXCEPTION_CALLBACK)* (gMaxExceptionNumber+1));
    179   }
    180 
    181   // Copy our assembly code into the page that contains the exception vectors.
    182   CopyMem((VOID *)VectorBase, (VOID *)ExceptionHandlersStart, Length);
    183 
    184   //
    185   // Initialize the C entry points for interrupts
    186   //
    187   for (Index = 0; Index <= gMaxExceptionNumber; Index++) {
    188     if (!FeaturePcdGet(PcdDebuggerExceptionSupport) ||
    189       (gDebuggerExceptionHandlers[Index] == 0) || (gDebuggerExceptionHandlers[Index] == (VOID *)gDebuggerNoHandlerValue)) {
    190 
    191       Status = RegisterExceptionHandler(Index, NULL);
    192       ASSERT_EFI_ERROR(Status);
    193     }
    194     else {
    195       // If the debugger has already hooked put its vector back
    196       VectorBase[Index] = (UINT32)(UINTN)gDebuggerExceptionHandlers[Index];
    197     }
    198   }
    199 
    200   // Flush Caches since we updated executable stuff
    201   InvalidateInstructionCacheRange((VOID *)(UINTN)BaseAddress, Length);
    202 
    203   return RETURN_SUCCESS;
    204 }
    205 
    206 
    207 /**
    208 Initializes all CPU interrupt/exceptions entries and provides the default interrupt/exception handlers.
    209 
    210 Caller should try to get an array of interrupt and/or exception vectors that are in use and need to
    211 persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification.
    212 If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL.
    213 If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly.
    214 
    215 @param[in]  VectorInfo    Pointer to reserved vector list.
    216 
    217 @retval EFI_SUCCESS           All CPU interrupt/exception entries have been successfully initialized
    218 with default interrupt/exception handlers.
    219 @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
    220 @retval EFI_UNSUPPORTED       This function is not supported.
    221 
    222 **/
    223 EFI_STATUS
    224 EFIAPI
    225 InitializeCpuInterruptHandlers(
    226 IN EFI_VECTOR_HANDOFF_INFO       *VectorInfo OPTIONAL
    227 )
    228 {
    229   // not needed, this is what the CPU driver is for
    230   return EFI_UNSUPPORTED;
    231 }
    232 
    233 /**
    234 Registers a function to be called from the processor exception handler. (On ARM/AArch64 this only
    235 provides exception handlers, not interrupt handling which is provided through the Hardware Interrupt
    236 Protocol.)
    237 
    238 This function registers and enables the handler specified by ExceptionHandler for a processor
    239 interrupt or exception type specified by ExceptionType. If ExceptionHandler is NULL, then the
    240 handler for the processor interrupt or exception type specified by ExceptionType is uninstalled.
    241 The installed handler is called once for each processor interrupt or exception.
    242 NOTE: This function should be invoked after InitializeCpuExceptionHandlers() or
    243 InitializeCpuInterruptHandlers() invoked, otherwise EFI_UNSUPPORTED returned.
    244 
    245 @param[in]  ExceptionType     Defines which interrupt or exception to hook.
    246 @param[in]  ExceptionHandler  A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
    247 when a processor interrupt occurs. If this parameter is NULL, then the handler
    248 will be uninstalled.
    249 
    250 @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
    251 @retval EFI_ALREADY_STARTED   ExceptionHandler is not NULL, and a handler for ExceptionType was
    252 previously installed.
    253 @retval EFI_INVALID_PARAMETER ExceptionHandler is NULL, and a handler for ExceptionType was not
    254 previously installed.
    255 @retval EFI_UNSUPPORTED       The interrupt specified by ExceptionType is not supported,
    256 or this function is not supported.
    257 **/
    258 RETURN_STATUS
    259 RegisterCpuInterruptHandler(
    260   IN EFI_EXCEPTION_TYPE             ExceptionType,
    261   IN EFI_CPU_INTERRUPT_HANDLER      ExceptionHandler
    262   ) {
    263   if (ExceptionType > gMaxExceptionNumber) {
    264     return RETURN_UNSUPPORTED;
    265   }
    266 
    267   if ((ExceptionHandler != NULL) && (gExceptionHandlers[ExceptionType] != NULL)) {
    268     return RETURN_ALREADY_STARTED;
    269   }
    270 
    271   gExceptionHandlers[ExceptionType] = ExceptionHandler;
    272 
    273   return RETURN_SUCCESS;
    274 }
    275 
    276 /**
    277 Register exception handler.
    278 
    279 @param  This                  A pointer to the SMM_CPU_SERVICE_PROTOCOL instance.
    280 @param  ExceptionType         Defines which interrupt or exception to hook. Type EFI_EXCEPTION_TYPE and
    281 the valid values for this parameter are defined in EFI_DEBUG_SUPPORT_PROTOCOL
    282 of the UEFI 2.0 specification.
    283 @param  InterruptHandler      A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER
    284 that is called when a processor interrupt occurs.
    285 If this parameter is NULL, then the handler will be uninstalled.
    286 
    287 @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
    288 @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was previously installed.
    289 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not previously installed.
    290 @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
    291 
    292 **/
    293 EFI_STATUS
    294 EFIAPI
    295 RegisterExceptionHandler(
    296   IN EFI_EXCEPTION_TYPE            ExceptionType,
    297   IN EFI_CPU_INTERRUPT_HANDLER     InterruptHandler
    298   )
    299 {
    300   return RegisterCpuInterruptHandler(ExceptionType, InterruptHandler);
    301 }
    302 
    303 VOID
    304 EFIAPI
    305 CommonCExceptionHandler(
    306   IN     EFI_EXCEPTION_TYPE           ExceptionType,
    307   IN OUT EFI_SYSTEM_CONTEXT           SystemContext
    308   )
    309 {
    310   if (ExceptionType <= gMaxExceptionNumber) {
    311     if (gExceptionHandlers[ExceptionType]) {
    312       gExceptionHandlers[ExceptionType](ExceptionType, SystemContext);
    313       return;
    314     }
    315   }
    316   else {
    317     DEBUG((EFI_D_ERROR, "Unknown exception type %d\n", ExceptionType));
    318     ASSERT(FALSE);
    319   }
    320 
    321   DefaultExceptionHandler(ExceptionType, SystemContext);
    322 }
    323