Home | History | Annotate | Download | only in Ipf
      1 /** @file
      2   IPF specific functions to support Debug Support protocol.
      3 
      4 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "PlDebugSupport.h"
     16 
     17 BOOLEAN  mInHandler = FALSE;
     18 
     19 //
     20 // number of bundles to swap in ivt
     21 //
     22 #define NUM_BUNDLES_IN_STUB 5
     23 #define NUM_IVT_ENTRIES     64
     24 
     25 typedef struct {
     26   BUNDLE         OrigBundles[NUM_BUNDLES_IN_STUB];
     27   CALLBACK_FUNC  RegisteredCallback;
     28 } IVT_ENTRY;
     29 
     30 IVT_ENTRY IvtEntryTable[NUM_IVT_ENTRIES];
     31 
     32 //
     33 // IPF context record is overallocated by 512 bytes to guarantee a 512 byte alignment exists
     34 // within the buffer and still have a large enough buffer to hold a whole IPF context record.
     35 //
     36 UINT8     IpfContextBuf[sizeof (EFI_SYSTEM_CONTEXT_IPF) + 512];
     37 
     38 //
     39 // The PatchSaveBuffer is used to store the original bundles from the IVT where it is patched
     40 // with the common handler.
     41 //
     42 UINT8     PatchSaveBuffer[0x400];
     43 UINTN     ExternalInterruptCount;
     44 
     45 
     46 /**
     47   IPF specific DebugSupport driver initialization.
     48 
     49   Must be public because it's referenced from DebugSupport.c
     50 
     51   @retval  EFI_SUCCESS     Always.
     52 
     53 **/
     54 EFI_STATUS
     55 PlInitializeDebugSupportDriver (
     56   VOID
     57   )
     58 {
     59   ZeroMem (IvtEntryTable, sizeof (IvtEntryTable));
     60   ExternalInterruptCount = 0;
     61   return EFI_SUCCESS;
     62 }
     63 
     64 /**
     65   Unload handler that is called during UnloadImage() - deallocates pool memory
     66   used by the driver.
     67 
     68   Must be public because it's referenced from DebugSuport.c
     69 
     70   @param  ImageHandle    The firmware allocated handle for the EFI image.
     71 
     72   @retval EFI_SUCCESS    Always.
     73 
     74 **/
     75 EFI_STATUS
     76 EFIAPI
     77 PlUnloadDebugSupportDriver (
     78   IN EFI_HANDLE       ImageHandle
     79   )
     80 {
     81   EFI_EXCEPTION_TYPE  ExceptionType;
     82 
     83   for (ExceptionType = 0; ExceptionType < NUM_IVT_ENTRIES; ExceptionType++) {
     84     ManageIvtEntryTable (ExceptionType, NULL, NULL);
     85   }
     86 
     87   return EFI_SUCCESS;
     88 }
     89 
     90 /**
     91   C routine that is called for all registered exceptions.  This is the main
     92   exception dispatcher.
     93 
     94   Must be public because it's referenced from AsmFuncs.s.
     95 
     96   @param  ExceptionType        Specifies which processor exception.
     97   @param  Context              System Context.
     98 **/
     99 VOID
    100 CommonHandler (
    101   IN EFI_EXCEPTION_TYPE ExceptionType,
    102   IN EFI_SYSTEM_CONTEXT Context
    103   )
    104 {
    105   DEBUG_CODE_BEGIN ();
    106     if (mInHandler) {
    107       DEBUG ((EFI_D_INFO, "ERROR: Re-entered debugger!\n"
    108                                     "       ExceptionType == %X\n"
    109                                     "       Context       == %X\n"
    110                                     "       Context.SystemContextIpf->CrIip  == %LX\n"
    111                                     "       Context.SystemContextIpf->CrIpsr == %LX\n"
    112                                     "       mInHandler     == %X\n",
    113                                     (INT32)ExceptionType,
    114                                     Context,
    115                                     Context.SystemContextIpf->CrIip,
    116                                     Context.SystemContextIpf->CrIpsr,
    117                                     mInHandler));
    118     }
    119   DEBUG_CODE_END ();
    120 
    121   ASSERT (!mInHandler);
    122   mInHandler = TRUE;
    123   if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) {
    124     if (ExceptionType != EXCEPT_IPF_EXTERNAL_INTERRUPT) {
    125       IvtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, Context.SystemContextIpf);
    126     } else {
    127       IvtEntryTable[ExceptionType].RegisteredCallback (Context.SystemContextIpf);
    128     }
    129   } else {
    130     ASSERT (0);
    131   }
    132 
    133   mInHandler = FALSE;
    134 }
    135 
    136 /**
    137   Given an integer number, return the physical address of the entry point in the IFT.
    138 
    139   @param  HandlerIndex       Index of the Handler
    140   @param  EntryPoint         IFT Entrypoint
    141 
    142 **/
    143 VOID
    144 GetHandlerEntryPoint (
    145   UINTN   HandlerIndex,
    146   VOID    **EntryPoint
    147   )
    148 {
    149   UINT8 *TempPtr;
    150 
    151   //
    152   // get base address of IVT
    153   //
    154   TempPtr = GetIva ();
    155 
    156   if (HandlerIndex < 20) {
    157     //
    158     // first 20 provide 64 bundles per vector
    159     //
    160     TempPtr += 0x400 * HandlerIndex;
    161   } else {
    162     //
    163     // the rest provide 16 bundles per vector
    164     //
    165     TempPtr += 0x5000 + 0x100 * (HandlerIndex - 20);
    166   }
    167 
    168   *EntryPoint = (VOID *) TempPtr;
    169 }
    170 
    171 /**
    172   This is the worker function that uninstalls and removes all handlers.
    173 
    174   @param  ExceptionType     Specifies which processor exception.
    175   @param  NewBundles        New Boundles.
    176   @param  NewCallback       A pointer to the new function to be registered.
    177 
    178   @retval EFI_ALEADY_STARTED Ivt already hooked.
    179   @retval EFI_SUCCESS        Successfully uninstalled.
    180 
    181 **/
    182 EFI_STATUS
    183 ManageIvtEntryTable (
    184   IN  EFI_EXCEPTION_TYPE           ExceptionType,
    185   IN  BUNDLE                       NewBundles[NUM_BUNDLES_IN_STUB],
    186   IN  CALLBACK_FUNC                NewCallback
    187   )
    188 {
    189   BUNDLE  *B0Ptr;
    190   UINT64  InterruptFlags;
    191   EFI_TPL OldTpl;
    192 
    193   //
    194   // Get address of bundle 0
    195   //
    196   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);
    197 
    198   if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) {
    199     //
    200     // we've already installed to this vector
    201     //
    202     if (NewCallback != NULL) {
    203       //
    204       // if the input handler is non-null, error
    205       //
    206       return EFI_ALREADY_STARTED;
    207     } else {
    208       //
    209       // else remove the previously installed handler
    210       //
    211       OldTpl          = gBS->RaiseTPL (TPL_HIGH_LEVEL);
    212       InterruptFlags  = ProgramInterruptFlags (DISABLE_INTERRUPTS);
    213       if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) {
    214         UnchainExternalInterrupt ();
    215       } else {
    216         UnhookEntry (ExceptionType);
    217       }
    218 
    219       ProgramInterruptFlags (InterruptFlags);
    220       gBS->RestoreTPL (OldTpl);
    221       //
    222       // re-init IvtEntryTable
    223       //
    224       ZeroMem (&IvtEntryTable[ExceptionType], sizeof (IVT_ENTRY));
    225     }
    226   } else {
    227     //
    228     // no user handler installed on this vector
    229     //
    230     if (NewCallback != NULL) {
    231       OldTpl          = gBS->RaiseTPL (TPL_HIGH_LEVEL);
    232       InterruptFlags  = ProgramInterruptFlags (DISABLE_INTERRUPTS);
    233       if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) {
    234         ChainExternalInterrupt (NewCallback);
    235       } else {
    236         HookEntry (ExceptionType, NewBundles, NewCallback);
    237       }
    238 
    239       ProgramInterruptFlags (InterruptFlags);
    240       gBS->RestoreTPL (OldTpl);
    241     }
    242   }
    243 
    244   return EFI_SUCCESS;
    245 }
    246 
    247 /**
    248   Saves original IVT contents and inserts a few new bundles which are fixed up
    249   to store the ExceptionType and then call the common handler.
    250 
    251   @param  ExceptionType      Specifies which processor exception.
    252   @param  NewBundles         New Boundles.
    253   @param  NewCallback        A pointer to the new function to be hooked.
    254 
    255 **/
    256 VOID
    257 HookEntry (
    258   IN  EFI_EXCEPTION_TYPE  ExceptionType,
    259   IN  BUNDLE              NewBundles[4],
    260   IN  CALLBACK_FUNC       NewCallback
    261   )
    262 {
    263   BUNDLE  *FixupBundle;
    264   BUNDLE  *B0Ptr;
    265 
    266   //
    267   // Get address of bundle 0
    268   //
    269   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);
    270 
    271   //
    272   // copy original bundles from IVT to IvtEntryTable so we can restore them later
    273   //
    274   CopyMem (
    275     IvtEntryTable[ExceptionType].OrigBundles,
    276     B0Ptr,
    277     sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB
    278     );
    279   //
    280   // insert new B0
    281   //
    282   CopyMem (B0Ptr, NewBundles, sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB);
    283 
    284   //
    285   // fixup IVT entry so it stores its index and whether or not to chain...
    286   //
    287   FixupBundle = B0Ptr + 2;
    288   FixupBundle->High |= ExceptionType << 36;
    289 
    290   InstructionCacheFlush (B0Ptr, 5);
    291   IvtEntryTable[ExceptionType].RegisteredCallback = NewCallback;
    292 }
    293 
    294 /**
    295   Restores original IVT contents when unregistering a callback function.
    296 
    297   @param  ExceptionType     Specifies which processor exception.
    298 
    299 **/
    300 VOID
    301 UnhookEntry (
    302   IN  EFI_EXCEPTION_TYPE  ExceptionType
    303   )
    304 {
    305   BUNDLE  *B0Ptr;
    306 
    307   //
    308   // Get address of bundle 0
    309   //
    310   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);
    311   //
    312   // restore original bundles in IVT
    313   //
    314   CopyMem (
    315     B0Ptr,
    316     IvtEntryTable[ExceptionType].OrigBundles,
    317     sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB
    318     );
    319   InstructionCacheFlush (B0Ptr, 5);
    320 }
    321 
    322 /**
    323   Sets up cache flush and calls assembly function to chain external interrupt.
    324 
    325   Records new callback in IvtEntryTable.
    326 
    327   @param  NewCallback     A pointer to the interrupt handle.
    328 
    329 **/
    330 VOID
    331 ChainExternalInterrupt (
    332   IN  CALLBACK_FUNC  NewCallback
    333   )
    334 {
    335   VOID  *Start;
    336 
    337   Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400);
    338   IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NewCallback;
    339   ChainHandler ();
    340   InstructionCacheFlush (Start, 0x400);
    341 }
    342 
    343 /**
    344   Sets up cache flush and calls assembly function to restore external interrupt.
    345   Removes registered callback from IvtEntryTable.
    346 
    347 **/
    348 VOID
    349 UnchainExternalInterrupt (
    350   VOID
    351   )
    352 {
    353   VOID  *Start;
    354 
    355   Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400);
    356   UnchainHandler ();
    357   InstructionCacheFlush (Start, 0x400);
    358   IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NULL;
    359 }
    360 
    361 /**
    362   Returns the maximum value that may be used for the ProcessorIndex parameter in
    363   RegisterPeriodicCallback() and RegisterExceptionCallback().
    364 
    365   Hard coded to support only 1 processor for now.
    366 
    367   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
    368   @param  MaxProcessorIndex     Pointer to a caller-allocated UINTN in which the maximum supported
    369                                 processor index is returned. Always 0 returned.
    370 
    371   @retval EFI_SUCCESS           Always returned with **MaxProcessorIndex set to 0.
    372 
    373 **/
    374 EFI_STATUS
    375 EFIAPI
    376 GetMaximumProcessorIndex (
    377   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,
    378   OUT UINTN                        *MaxProcessorIndex
    379   )
    380 {
    381   *MaxProcessorIndex = 0;
    382   return (EFI_SUCCESS);
    383 }
    384 
    385 /**
    386   Registers a function to be called back periodically in interrupt context.
    387 
    388   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
    389   @param  ProcessorIndex        Specifies which processor the callback function applies to.
    390   @param  PeriodicCallback      A pointer to a function of type PERIODIC_CALLBACK that is the main
    391                                 periodic entry point of the debug agent.
    392 
    393   @retval EFI_SUCCESS           The function completed successfully.
    394   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
    395                                 function was previously registered.
    396   @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
    397                                 function.
    398 **/
    399 EFI_STATUS
    400 EFIAPI
    401 RegisterPeriodicCallback (
    402   IN EFI_DEBUG_SUPPORT_PROTOCOL     *This,
    403   IN UINTN                          ProcessorIndex,
    404   IN EFI_PERIODIC_CALLBACK          PeriodicCallback
    405   )
    406 {
    407   return ManageIvtEntryTable (EXCEPT_IPF_EXTERNAL_INTERRUPT, NULL, PeriodicCallback);
    408 }
    409 
    410 /**
    411   Registers a function to be called when a given processor exception occurs.
    412 
    413   This code executes in boot services context.
    414 
    415   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
    416   @param  ProcessorIndex        Specifies which processor the callback function applies to.
    417   @param  ExceptionCallback     A pointer to a function of type EXCEPTION_CALLBACK that is called
    418                                 when the processor exception specified by ExceptionType occurs.
    419   @param  ExceptionType         Specifies which processor exception to hook.
    420 
    421   @retval EFI_SUCCESS           The function completed successfully.
    422   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
    423                                 function was previously registered.
    424   @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
    425                                 function.
    426 **/
    427 EFI_STATUS
    428 EFIAPI
    429 RegisterExceptionCallback (
    430   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,
    431   IN UINTN                         ProcessorIndex,
    432   IN EFI_EXCEPTION_CALLBACK        ExceptionCallback,
    433   IN EFI_EXCEPTION_TYPE            ExceptionType
    434   )
    435 {
    436   return ManageIvtEntryTable (
    437           ExceptionType,
    438           (BUNDLE *) ((EFI_PLABEL *) HookStub)->EntryPoint,
    439           ExceptionCallback
    440           );
    441 }
    442 
    443 /**
    444   Invalidates processor instruction cache for a memory range. Subsequent execution in this range
    445   causes a fresh memory fetch to retrieve code to be executed.
    446 
    447   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
    448   @param  ProcessorIndex        Specifies which processor's instruction cache is to be invalidated.
    449   @param  Start                 Specifies the physical base of the memory range to be invalidated.
    450   @param  Length                Specifies the minimum number of bytes in the processor's instruction
    451                                 cache to invalidate.
    452 
    453   @retval EFI_SUCCESS           Always returned.
    454 
    455 **/
    456 EFI_STATUS
    457 EFIAPI
    458 InvalidateInstructionCache (
    459   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,
    460   IN UINTN                         ProcessorIndex,
    461   IN VOID                          *Start,
    462   IN UINTN                         Length
    463   )
    464 {
    465   InstructionCacheFlush (Start, Length);
    466   return EFI_SUCCESS;
    467 }
    468