--- /dev/null
+/** @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