/** @file | |
MMI management. | |
Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR> | |
Copyright (c) 2016 - 2021, Arm Limited. All rights reserved.<BR> | |
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; | |
} |