]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Core/PiSmmCore/Smi.c
Add PI SMM IPL and PI SMM Core
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Smi.c
diff --git a/MdeModulePkg/Core/PiSmmCore/Smi.c b/MdeModulePkg/Core/PiSmmCore/Smi.c
new file mode 100644 (file)
index 0000000..ccf6c0d
--- /dev/null
@@ -0,0 +1,333 @@
+/** @file\r
+  SMI management.\r
+\r
+  Copyright (c) 2009 - 2010, Intel Corporation.  All rights reserved.<BR>\r
+  This program and the accompanying materials are licensed and made available \r
+  under the terms and conditions of the BSD License which accompanies this \r
+  distribution.  The full text of the license may be found at        \r
+  http://opensource.org/licenses/bsd-license.php                                            \r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
+\r
+**/\r
+\r
+#include "PiSmmCore.h"\r
+\r
+//\r
+// SMM_HANDLER - used for each SMM handler\r
+//\r
+\r
+#define SMI_ENTRY_SIGNATURE  SIGNATURE_32('s','m','i','e')\r
+\r
+ typedef struct {\r
+  UINTN       Signature;\r
+  LIST_ENTRY  AllEntries;  // All entries\r
+\r
+  EFI_GUID    HandlerType; // Type of interrupt\r
+  LIST_ENTRY  SmiHandlers; // All handlers\r
+} SMI_ENTRY;\r
+\r
+#define SMI_HANDLER_SIGNATURE  SIGNATURE_32('s','m','i','h')\r
+\r
+ typedef struct {\r
+  UINTN                         Signature;\r
+  LIST_ENTRY                    Link;        // Link on SMI_ENTRY.SmiHandlers\r
+  EFI_SMM_HANDLER_ENTRY_POINT2  Handler;     // The smm handler's entry point\r
+  SMI_ENTRY                     *SmiEntry;\r
+} SMI_HANDLER;\r
+\r
+LIST_ENTRY  mRootSmiHandlerList = INITIALIZE_LIST_HEAD_VARIABLE (mRootSmiHandlerList);\r
+LIST_ENTRY  mSmiEntryList       = INITIALIZE_LIST_HEAD_VARIABLE (mSmiEntryList);\r
+\r
+/**\r
+  Finds the SMI entry for the requested handler type.\r
+\r
+  @param  HandlerType            The type of the interrupt\r
+  @param  Create                 Create a new entry if not found\r
+\r
+  @return SMI entry\r
+\r
+**/\r
+SMI_ENTRY  *\r
+EFIAPI\r
+SmmCoreFindSmiEntry (\r
+  IN EFI_GUID  *HandlerType,\r
+  IN BOOLEAN   Create\r
+  )\r
+{\r
+  LIST_ENTRY  *Link;\r
+  SMI_ENTRY   *Item;\r
+  SMI_ENTRY   *SmiEntry;\r
+\r
+  //\r
+  // Search the SMI entry list for the matching GUID\r
+  //\r
+  SmiEntry = NULL;\r
+  for (Link = mSmiEntryList.ForwardLink;\r
+       Link != &mSmiEntryList;\r
+       Link = Link->ForwardLink) {\r
+\r
+    Item = CR (Link, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE);\r
+    if (CompareGuid (&Item->HandlerType, HandlerType)) {\r
+      //\r
+      // This is the SMI entry\r
+      //\r
+      SmiEntry = Item;\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If the protocol entry was not found and Create is TRUE, then\r
+  // allocate a new entry\r
+  //\r
+  if ((SmiEntry == NULL) && Create) {\r
+    SmiEntry = AllocatePool (sizeof(SMI_ENTRY));\r
+    if (SmiEntry != NULL) {\r
+      //\r
+      // Initialize new SMI entry structure\r
+      //\r
+      SmiEntry->Signature = SMI_ENTRY_SIGNATURE;\r
+      CopyGuid ((VOID *)&SmiEntry->HandlerType, HandlerType);\r
+      InitializeListHead (&SmiEntry->SmiHandlers);\r
+\r
+      //\r
+      // Add it to SMI entry list\r
+      //\r
+      InsertTailList (&mSmiEntryList, &SmiEntry->AllEntries);\r
+    }\r
+  }\r
+  return SmiEntry;\r
+}\r
+\r
+/**\r
+  Manage SMI of a particular type.\r
+\r
+  @param  HandlerType    Points to the handler type or NULL for root SMI handlers.\r
+  @param  Context        Points to an optional context buffer.\r
+  @param  CommBuffer     Points to the optional communication buffer.\r
+  @param  CommBufferSize Points to the size of the optional communication buffer.\r
+\r
+  @retval EFI_SUCCESS                        Interrupt source was processed successfully but not quiesced.\r
+  @retval EFI_INTERRUPT_PENDING              One or more SMI sources could not be quiesced.\r
+  @retval EFI_WARN_INTERRUPT_SOURCE_PENDING  Interrupt source was not handled or quiesced.\r
+  @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED Interrupt source was handled and quiesced.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmiManage (\r
+  IN     CONST EFI_GUID  *HandlerType,\r
+  IN     CONST VOID      *Context         OPTIONAL,\r
+  IN OUT VOID            *CommBuffer      OPTIONAL,\r
+  IN OUT UINTN           *CommBufferSize  OPTIONAL\r
+  )\r
+{\r
+  LIST_ENTRY   *Link;\r
+  LIST_ENTRY   *Head;\r
+  SMI_ENTRY    *SmiEntry;\r
+  SMI_HANDLER  *SmiHandler;\r
+  BOOLEAN      InterruptQuiesced;\r
+  EFI_STATUS   Status;\r
+  \r
+  if (HandlerType == NULL) {\r
+    //\r
+    // Root SMI handler\r
+    //\r
+    Status = EFI_WARN_INTERRUPT_SOURCE_PENDING;\r
+\r
+    Head = &mRootSmiHandlerList;\r
+    for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {\r
+      SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);\r
+\r
+      Status = SmiHandler->Handler (\r
+                 (EFI_HANDLE) SmiHandler,\r
+                 Context,\r
+                 CommBuffer,\r
+                 CommBufferSize\r
+                 );\r
+      if (Status == EFI_SUCCESS || Status == EFI_INTERRUPT_PENDING) {\r
+        return Status;\r
+      }\r
+    }\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Non-root SMI handler\r
+  //\r
+  SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, FALSE);\r
+  if (SmiEntry == NULL) {\r
+    //\r
+    // There is no handler registered for this interrupt source\r
+    //\r
+    return EFI_WARN_INTERRUPT_SOURCE_PENDING;\r
+  }\r
+\r
+  InterruptQuiesced = FALSE;\r
+  Head = &SmiEntry->SmiHandlers;\r
+  for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {\r
+    SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE);\r
+\r
+    Status = SmiHandler->Handler (\r
+               (EFI_HANDLE) SmiHandler,\r
+               Context,\r
+               CommBuffer,\r
+               CommBufferSize\r
+               );\r
+\r
+    switch (Status) {\r
+    case EFI_INTERRUPT_PENDING:\r
+      //\r
+      // If a handler returns EFI_INTERRUPT_PENDING, the interrupt could not be\r
+      // quiesced, then no additional handlers will be processed,\r
+      // and EFI_INTERRUPT_PENDING will be returned\r
+      //\r
+      return EFI_INTERRUPT_PENDING;\r
+\r
+    case EFI_SUCCESS:\r
+      //\r
+      // If handler return EFI_SUCCESS, the interrupt was handled and quiesced,\r
+      // no other handlers should still be called,\r
+      // and EFI_WARN_INTERRUPT_SOURCE_QUIESCED will be returned\r
+      //\r
+      return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;\r
+\r
+    case EFI_WARN_INTERRUPT_SOURCE_QUIESCED:\r
+      //\r
+      // If at least one of the handlers report EFI_WARN_INTERRUPT_SOURCE_QUIESCED,\r
+      // then this function will return EFI_WARN_INTERRUPT_SOURCE_QUIESCED\r
+      //\r
+      InterruptQuiesced = TRUE;\r
+      break;\r
+\r
+    default:\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (InterruptQuiesced) {\r
+    Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED;\r
+  } else {\r
+    //\r
+    // If no handler report EFI_WARN_INTERRUPT_SOURCE_QUIESCED, then this\r
+    // function will return EFI_INTERRUPT_PENDING\r
+    //\r
+    Status = EFI_INTERRUPT_PENDING;\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Registers a handler to execute within SMM.\r
+\r
+  @param  Handler        Handler service funtion pointer.\r
+  @param  HandlerType    Points to the handler type or NULL for root SMI handlers.\r
+  @param  DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function.\r
+\r
+  @retval EFI_SUCCESS           Handler register success.\r
+  @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmiHandlerRegister (\r
+  IN  EFI_SMM_HANDLER_ENTRY_POINT2  Handler,\r
+  IN  CONST EFI_GUID                *HandlerType  OPTIONAL,\r
+  OUT EFI_HANDLE                    *DispatchHandle\r
+  )\r
+{\r
+  SMI_HANDLER  *SmiHandler;\r
+  SMI_ENTRY    *SmiEntry;\r
+  LIST_ENTRY   *List;\r
+\r
+  if (Handler == NULL || DispatchHandle == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  SmiHandler = AllocateZeroPool (sizeof (SMI_HANDLER));\r
+  if (SmiHandler == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  SmiHandler->Signature = SMI_HANDLER_SIGNATURE;\r
+  SmiHandler->Handler = Handler;\r
+\r
+  if (HandlerType == NULL) {\r
+    //\r
+    // This is root SMI handler\r
+    //\r
+    SmiEntry = NULL;\r
+    List = &mRootSmiHandlerList;\r
+  } else {\r
+    //\r
+    // None root SMI handler\r
+    //\r
+    SmiEntry = SmmCoreFindSmiEntry ((EFI_GUID *) HandlerType, TRUE);\r
+    if (SmiEntry == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    List = &SmiEntry->SmiHandlers;\r
+  }\r
+\r
+  SmiHandler->SmiEntry = SmiEntry;\r
+  InsertTailList (List, &SmiHandler->Link);\r
+\r
+  *DispatchHandle = (EFI_HANDLE) SmiHandler;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Unregister a handler in SMM.\r
+\r
+  @param  DispatchHandle  The handle that was specified when the handler was registered.\r
+\r
+  @retval EFI_SUCCESS           Handler function was successfully unregistered.\r
+  @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmiHandlerUnRegister (\r
+  IN EFI_HANDLE  DispatchHandle\r
+  )\r
+{\r
+  SMI_HANDLER  *SmiHandler;\r
+  SMI_ENTRY    *SmiEntry;\r
+\r
+  SmiHandler = (SMI_HANDLER *) DispatchHandle;\r
+\r
+  if (SmiHandler == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (SmiHandler->Signature != SMI_HANDLER_SIGNATURE) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  SmiEntry = SmiHandler->SmiEntry;\r
+\r
+  RemoveEntryList (&SmiHandler->Link);\r
+  FreePool (SmiHandler);\r
+\r
+  if (SmiEntry == NULL) {\r
+    //\r
+    // This is root SMI handler\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (IsListEmpty (&SmiEntry->SmiHandlers)) {\r
+    //\r
+    // No handler registered for this interrupt now, remove the SMI_ENTRY\r
+    //\r
+    RemoveEntryList (&SmiEntry->AllEntries);\r
+\r
+    FreePool (SmiEntry);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r