/** @file MMI management. Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
Copyright (c) 2016 - 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "StandaloneMmCore.h" // // MM_HANDLER_STATE_NOTIFIER // // // MM_HANDLER - used for each MM handler // #define MMI_ENTRY_SIGNATURE SIGNATURE_32('m','m','i','e') typedef struct { UINTN Signature; LIST_ENTRY AllEntries; // All entries EFI_GUID HandlerType; // Type of interrupt LIST_ENTRY MmiHandlers; // All handlers } MMI_ENTRY; #define MMI_HANDLER_SIGNATURE SIGNATURE_32('m','m','i','h') typedef struct { UINTN Signature; LIST_ENTRY Link; // Link on MMI_ENTRY.MmiHandlers EFI_MM_HANDLER_ENTRY_POINT Handler; // The mm handler's entry point MMI_ENTRY *MmiEntry; } MMI_HANDLER; LIST_ENTRY mRootMmiHandlerList = INITIALIZE_LIST_HEAD_VARIABLE (mRootMmiHandlerList); LIST_ENTRY mMmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mMmiEntryList); /** Finds the MMI entry for the requested handler type. @param HandlerType The type of the interrupt @param Create Create a new entry if not found @return MMI entry **/ MMI_ENTRY * EFIAPI MmCoreFindMmiEntry ( IN EFI_GUID *HandlerType, IN BOOLEAN Create ) { LIST_ENTRY *Link; MMI_ENTRY *Item; MMI_ENTRY *MmiEntry; // // Search the MMI entry list for the matching GUID // MmiEntry = NULL; for (Link = mMmiEntryList.ForwardLink; Link != &mMmiEntryList; Link = Link->ForwardLink) { Item = CR (Link, MMI_ENTRY, AllEntries, MMI_ENTRY_SIGNATURE); if (CompareGuid (&Item->HandlerType, HandlerType)) { // // This is the MMI entry // MmiEntry = Item; break; } } // // If the protocol entry was not found and Create is TRUE, then // allocate a new entry // if ((MmiEntry == NULL) && Create) { MmiEntry = AllocatePool (sizeof (MMI_ENTRY)); if (MmiEntry != NULL) { // // Initialize new MMI entry structure // MmiEntry->Signature = MMI_ENTRY_SIGNATURE; CopyGuid ((VOID *)&MmiEntry->HandlerType, HandlerType); InitializeListHead (&MmiEntry->MmiHandlers); // // Add it to MMI entry list // InsertTailList (&mMmiEntryList, &MmiEntry->AllEntries); } } return MmiEntry; } /** Manage MMI of a particular type. @param HandlerType Points to the handler type or NULL for root MMI handlers. @param Context Points to an optional context buffer. @param CommBuffer Points to the optional communication buffer. @param CommBufferSize Points to the size of the optional communication buffer. @retval EFI_WARN_INTERRUPT_SOURCE_PENDING Interrupt source was processed successfully but not quiesced. @retval EFI_INTERRUPT_PENDING One or more MMI sources could not be quiesced. @retval EFI_NOT_FOUND Interrupt source was not handled or quiesced. @retval EFI_SUCCESS Interrupt source was handled and quiesced. **/ EFI_STATUS EFIAPI MmiManage ( IN CONST EFI_GUID *HandlerType, IN CONST VOID *Context OPTIONAL, IN OUT VOID *CommBuffer OPTIONAL, IN OUT UINTN *CommBufferSize OPTIONAL ) { LIST_ENTRY *Link; LIST_ENTRY *Head; MMI_ENTRY *MmiEntry; MMI_HANDLER *MmiHandler; BOOLEAN SuccessReturn; EFI_STATUS Status; Status = EFI_NOT_FOUND; SuccessReturn = FALSE; if (HandlerType == NULL) { // // Root MMI handler // Head = &mRootMmiHandlerList; } else { // // Non-root MMI handler // MmiEntry = MmCoreFindMmiEntry ((EFI_GUID *)HandlerType, FALSE); if (MmiEntry == NULL) { // // There is no handler registered for this interrupt source // return Status; } Head = &MmiEntry->MmiHandlers; } for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) { MmiHandler = CR (Link, MMI_HANDLER, Link, MMI_HANDLER_SIGNATURE); Status = MmiHandler->Handler ( (EFI_HANDLE)MmiHandler, Context, CommBuffer, CommBufferSize ); switch (Status) { case EFI_INTERRUPT_PENDING: // // If a handler returns EFI_INTERRUPT_PENDING and HandlerType is not NULL then // no additional handlers will be processed and EFI_INTERRUPT_PENDING will be returned. // if (HandlerType != NULL) { return EFI_INTERRUPT_PENDING; } break; case EFI_SUCCESS: // // If at least one of the handlers returns EFI_SUCCESS then the function will return // EFI_SUCCESS. If a handler returns EFI_SUCCESS and HandlerType is not NULL then no // additional handlers will be processed. // if (HandlerType != NULL) { return EFI_SUCCESS; } SuccessReturn = TRUE; break; case EFI_WARN_INTERRUPT_SOURCE_QUIESCED: // // If at least one of the handlers returns EFI_WARN_INTERRUPT_SOURCE_QUIESCED // then the function will return EFI_SUCCESS. // SuccessReturn = TRUE; break; case EFI_WARN_INTERRUPT_SOURCE_PENDING: // // If all the handlers returned EFI_WARN_INTERRUPT_SOURCE_PENDING // then EFI_WARN_INTERRUPT_SOURCE_PENDING will be returned. // break; default: // // Unexpected status code returned. // ASSERT (FALSE); break; } } if (SuccessReturn) { Status = EFI_SUCCESS; } return Status; } /** Registers a handler to execute within MM. @param Handler Handler service function pointer. @param HandlerType Points to the handler type or NULL for root MMI handlers. @param DispatchHandle On return, contains a unique handle which can be used to later unregister the handler function. @retval EFI_SUCCESS Handler register success. @retval EFI_INVALID_PARAMETER Handler or DispatchHandle is NULL. **/ EFI_STATUS EFIAPI MmiHandlerRegister ( IN EFI_MM_HANDLER_ENTRY_POINT Handler, IN CONST EFI_GUID *HandlerType OPTIONAL, OUT EFI_HANDLE *DispatchHandle ) { MMI_HANDLER *MmiHandler; MMI_ENTRY *MmiEntry; LIST_ENTRY *List; if ((Handler == NULL) || (DispatchHandle == NULL)) { return EFI_INVALID_PARAMETER; } MmiHandler = AllocateZeroPool (sizeof (MMI_HANDLER)); if (MmiHandler == NULL) { return EFI_OUT_OF_RESOURCES; } MmiHandler->Signature = MMI_HANDLER_SIGNATURE; MmiHandler->Handler = Handler; if (HandlerType == NULL) { // // This is root MMI handler // MmiEntry = NULL; List = &mRootMmiHandlerList; } else { // // None root MMI handler // MmiEntry = MmCoreFindMmiEntry ((EFI_GUID *)HandlerType, TRUE); if (MmiEntry == NULL) { return EFI_OUT_OF_RESOURCES; } List = &MmiEntry->MmiHandlers; } MmiHandler->MmiEntry = MmiEntry; InsertTailList (List, &MmiHandler->Link); *DispatchHandle = (EFI_HANDLE)MmiHandler; return EFI_SUCCESS; } /** Unregister a handler in MM. @param DispatchHandle The handle that was specified when the handler was registered. @retval EFI_SUCCESS Handler function was successfully unregistered. @retval EFI_INVALID_PARAMETER DispatchHandle does not refer to a valid handle. **/ EFI_STATUS EFIAPI MmiHandlerUnRegister ( IN EFI_HANDLE DispatchHandle ) { MMI_HANDLER *MmiHandler; MMI_ENTRY *MmiEntry; MmiHandler = (MMI_HANDLER *)DispatchHandle; if (MmiHandler == NULL) { return EFI_INVALID_PARAMETER; } if (MmiHandler->Signature != MMI_HANDLER_SIGNATURE) { return EFI_INVALID_PARAMETER; } MmiEntry = MmiHandler->MmiEntry; RemoveEntryList (&MmiHandler->Link); FreePool (MmiHandler); if (MmiEntry == NULL) { // // This is root MMI handler // return EFI_SUCCESS; } if (IsListEmpty (&MmiEntry->MmiHandlers)) { // // No handler registered for this interrupt now, remove the MMI_ENTRY // RemoveEntryList (&MmiEntry->AllEntries); FreePool (MmiEntry); } return EFI_SUCCESS; }