Home | History | Annotate | Download | only in PiSmmCore
      1 /** @file
      2   SMI management.
      3 
      4   Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
      5   This program and the accompanying materials are licensed and made available
      6   under the terms and conditions of the BSD License which accompanies this
      7   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 "PiSmmCore.h"
     16 
     17 //
     18 // SMM_HANDLER - used for each SMM handler
     19 //
     20 
     21 #define SMI_ENTRY_SIGNATURE  SIGNATURE_32('s','m','i','e')
     22 
     23  typedef struct {
     24   UINTN       Signature;
     25   LIST_ENTRY  AllEntries;  // All entries
     26 
     27   EFI_GUID    HandlerType; // Type of interrupt
     28   LIST_ENTRY  SmiHandlers; // All handlers
     29 } SMI_ENTRY;
     30 
     31 #define SMI_HANDLER_SIGNATURE  SIGNATURE_32('s','m','i','h')
     32 
     33  typedef struct {
     34   UINTN                         Signature;
     35   LIST_ENTRY                    Link;        // Link on SMI_ENTRY.SmiHandlers
     36   EFI_SMM_HANDLER_ENTRY_POINT2  Handler;     // The smm handler's entry point
     37   SMI_ENTRY                     *SmiEntry;
     38 } SMI_HANDLER;
     39 
     40 LIST_ENTRY  mRootSmiHandlerList = INITIALIZE_LIST_HEAD_VARIABLE (mRootSmiHandlerList);
     41 LIST_ENTRY  mSmiEntryList       = INITIALIZE_LIST_HEAD_VARIABLE (mSmiEntryList);
     42 
     43 /**
     44   Finds the SMI entry for the requested handler type.
     45 
     46   @param  HandlerType            The type of the interrupt
     47   @param  Create                 Create a new entry if not found
     48 
     49   @return SMI entry
     50 
     51 **/
     52 SMI_ENTRY  *
     53 EFIAPI
     54 SmmCoreFindSmiEntry (
     55   IN EFI_GUID  *HandlerType,
     56   IN BOOLEAN   Create
     57   )
     58 {
     59   LIST_ENTRY  *Link;
     60   SMI_ENTRY   *Item;
     61   SMI_ENTRY   *SmiEntry;
     62 
     63   //
     64   // Search the SMI entry list for the matching GUID
     65   //
     66   SmiEntry = NULL;
     67   for (Link = mSmiEntryList.ForwardLink;
     68        Link != &mSmiEntryList;
     69        Link = Link->ForwardLink) {
     70 
     71     Item = CR (Link, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE);
     72     if (CompareGuid (&Item->HandlerType, HandlerType)) {
     73       //
     74       // This is the SMI entry
     75       //
     76       SmiEntry = Item;
     77       break;
     78     }
     79   }
     80 
     81   //
     82   // If the protocol entry was not found and Create is TRUE, then
     83   // allocate a new entry
     84   //
     85   if ((SmiEntry == NULL) && Create) {
     86     SmiEntry = AllocatePool (sizeof(SMI_ENTRY));
     87     if (SmiEntry != NULL) {
     88       //
     89       // Initialize new SMI entry structure
     90       //
     91       SmiEntry->Signature = SMI_ENTRY_SIGNATURE;
     92       CopyGuid ((VOID *)&SmiEntry->HandlerType, HandlerType);
     93       InitializeListHead (&SmiEntry->SmiHandlers);
     94 
     95       //
     96       // Add it to SMI entry list
     97       //
     98       InsertTailList (&mSmiEntryList, &SmiEntry->AllEntries);
     99     }
    100   }
    101   return SmiEntry;
    102 }
    103 
    104 /**
    105   Manage SMI of a particular type.
    106 
    107   @param  HandlerType    Points to the handler type or NULL for root SMI handlers.
    108   @param  Context        Points to an optional context buffer.
    109   @param  CommBuffer     Points to the optional communication buffer.
    110   @param  CommBufferSize Points to the size of the optional communication buffer.
    111 
    112   @retval EFI_WARN_INTERRUPT_SOURCE_PENDING  Interrupt source was processed successfully but not quiesced.
    113   @retval EFI_INTERRUPT_PENDING              One or more SMI sources could not be quiesced.
    114   @retval EFI_NOT_FOUND                      Interrupt source was not handled or quiesced.
    115   @retval EFI_SUCCESS                        Interrupt source was handled and quiesced.
    116 
    117 **/
    118 EFI_STATUS
    119 EFIAPI
    120 SmiManage (
    121   IN     CONST EFI_GUID  *HandlerType,
    122   IN     CONST VOID      *Context         OPTIONAL,
    123   IN OUT VOID            *CommBuffer      OPTIONAL,
    124   IN OUT UINTN           *CommBufferSize  OPTIONAL
    125   )
    126 {
    127   LIST_ENTRY   *Link;
    128   LIST_ENTRY   *Head;
    129   SMI_ENTRY    *SmiEntry;
    130   SMI_HANDLER  *SmiHandler;
    131   BOOLEAN      SuccessReturn;
    132   EFI_STATUS   Status;
    133 
    134   Status = EFI_NOT_FOUND;
    135   SuccessReturn = FALSE;
    136   if (HandlerType == NULL) {
    137     //
    138     // Root SMI handler
    139     //
    140 
    141     Head = &mRootSmiHandlerList;
    142   } else {
    143     //
    144     // Non-root SMI handler
    145     //
    146     SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, FALSE);
    147     if (SmiEntry == NULL) {
    148       //
    149       // There is no handler registered for this interrupt source
    150       //
    151       return Status;
    152     }
    153 
    154     Head = &SmiEntry->SmiHandlers;
    155   }
    156 
    157   for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
    158     SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);
    159 
    160     Status = SmiHandler->Handler (
    161                (EFI_HANDLE) SmiHandler,
    162                Context,
    163                CommBuffer,
    164                CommBufferSize
    165                );
    166 
    167     switch (Status) {
    168     case EFI_INTERRUPT_PENDING:
    169       //
    170       // If a handler returns EFI_INTERRUPT_PENDING and HandlerType is not NULL then
    171       // no additional handlers will be processed and EFI_INTERRUPT_PENDING will be returned.
    172       //
    173       if (HandlerType != NULL) {
    174         return EFI_INTERRUPT_PENDING;
    175       }
    176       break;
    177 
    178     case EFI_SUCCESS:
    179       //
    180       // If at least one of the handlers returns EFI_SUCCESS then the function will return
    181       // EFI_SUCCESS. If a handler returns EFI_SUCCESS and HandlerType is not NULL then no
    182       // additional handlers will be processed.
    183       //
    184       if (HandlerType != NULL) {
    185         return EFI_SUCCESS;
    186       }
    187       SuccessReturn = TRUE;
    188       break;
    189 
    190     case EFI_WARN_INTERRUPT_SOURCE_QUIESCED:
    191       //
    192       // If at least one of the handlers returns EFI_WARN_INTERRUPT_SOURCE_QUIESCED
    193       // then the function will return EFI_SUCCESS.
    194       //
    195       SuccessReturn = TRUE;
    196       break;
    197 
    198     case EFI_WARN_INTERRUPT_SOURCE_PENDING:
    199       //
    200       // If all the handlers returned EFI_WARN_INTERRUPT_SOURCE_PENDING
    201       // then EFI_WARN_INTERRUPT_SOURCE_PENDING will be returned.
    202       //
    203       break;
    204 
    205     default:
    206       //
    207       // Unexpected status code returned.
    208       //
    209       ASSERT (FALSE);
    210       break;
    211     }
    212   }
    213 
    214   if (SuccessReturn) {
    215     Status = EFI_SUCCESS;
    216   }
    217 
    218   return Status;
    219 }
    220 
    221 /**
    222   Registers a handler to execute within SMM.
    223 
    224   @param  Handler        Handler service funtion pointer.
    225   @param  HandlerType    Points to the handler type or NULL for root SMI handlers.
    226   @param  DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function.
    227 
    228   @retval EFI_SUCCESS           Handler register success.
    229   @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL.
    230 
    231 **/
    232 EFI_STATUS
    233 EFIAPI
    234 SmiHandlerRegister (
    235   IN  EFI_SMM_HANDLER_ENTRY_POINT2  Handler,
    236   IN  CONST EFI_GUID                *HandlerType  OPTIONAL,
    237   OUT EFI_HANDLE                    *DispatchHandle
    238   )
    239 {
    240   SMI_HANDLER  *SmiHandler;
    241   SMI_ENTRY    *SmiEntry;
    242   LIST_ENTRY   *List;
    243 
    244   if (Handler == NULL || DispatchHandle == NULL) {
    245     return EFI_INVALID_PARAMETER;
    246   }
    247 
    248   SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER));
    249   if (SmiHandler == NULL) {
    250     return EFI_OUT_OF_RESOURCES;
    251   }
    252 
    253   SmiHandler->Signature = SMI_HANDLER_SIGNATURE;
    254   SmiHandler->Handler = Handler;
    255 
    256   if (HandlerType == NULL) {
    257     //
    258     // This is root SMI handler
    259     //
    260     SmiEntry = NULL;
    261     List = &mRootSmiHandlerList;
    262   } else {
    263     //
    264     // None root SMI handler
    265     //
    266     SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, TRUE);
    267     if (SmiEntry == NULL) {
    268       return EFI_OUT_OF_RESOURCES;
    269     }
    270 
    271     List = &SmiEntry->SmiHandlers;
    272   }
    273 
    274   SmiHandler->SmiEntry = SmiEntry;
    275   InsertTailList (List, &SmiHandler->Link);
    276 
    277   *DispatchHandle = (EFI_HANDLE) SmiHandler;
    278 
    279   return EFI_SUCCESS;
    280 }
    281 
    282 /**
    283   Unregister a handler in SMM.
    284 
    285   @param  DispatchHandle  The handle that was specified when the handler was registered.
    286 
    287   @retval EFI_SUCCESS           Handler function was successfully unregistered.
    288   @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle.
    289 
    290 **/
    291 EFI_STATUS
    292 EFIAPI
    293 SmiHandlerUnRegister (
    294   IN EFI_HANDLE  DispatchHandle
    295   )
    296 {
    297   SMI_HANDLER  *SmiHandler;
    298   SMI_ENTRY    *SmiEntry;
    299 
    300   SmiHandler = (SMI_HANDLER *) DispatchHandle;
    301 
    302   if (SmiHandler == NULL) {
    303     return EFI_INVALID_PARAMETER;
    304   }
    305 
    306   if (SmiHandler->Signature != SMI_HANDLER_SIGNATURE) {
    307     return EFI_INVALID_PARAMETER;
    308   }
    309 
    310   SmiEntry = SmiHandler->SmiEntry;
    311 
    312   RemoveEntryList (&SmiHandler->Link);
    313   FreePool (SmiHandler);
    314 
    315   if (SmiEntry == NULL) {
    316     //
    317     // This is root SMI handler
    318     //
    319     return EFI_SUCCESS;
    320   }
    321 
    322   if (IsListEmpty (&SmiEntry->SmiHandlers)) {
    323     //
    324     // No handler registered for this interrupt now, remove the SMI_ENTRY
    325     //
    326     RemoveEntryList (&SmiEntry->AllEntries);
    327 
    328     FreePool (SmiEntry);
    329   }
    330 
    331   return EFI_SUCCESS;
    332 }
    333