blob: e035245c875a7fad5bc2c7073493b6ec024a0369 [file] [log] [blame]
/** @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;
BOOLEAN ToRemove; // To remove this MMI_HANDLER later
} MMI_HANDLER;
//
// mMmiManageCallingDepth is used to track the depth of recursive calls of MmiManage.
//
UINTN mMmiManageCallingDepth = 0;
LIST_ENTRY mRootMmiHandlerList = INITIALIZE_LIST_HEAD_VARIABLE (mRootMmiHandlerList);
LIST_ENTRY mMmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mMmiEntryList);
/**
Remove MmiHandler and free the memory it used.
If MmiEntry is empty, remove MmiEntry and free the memory it used.
@param MmiHandler Points to MMI handler.
@param MmiEntry Points to MMI Entry or NULL for root MMI handlers.
@retval TRUE MmiEntry is removed.
@retval FALSE MmiEntry is not removed.
**/
BOOLEAN
RemoveMmiHandler (
IN MMI_HANDLER *MmiHandler,
IN MMI_ENTRY *MmiEntry
)
{
ASSERT (MmiHandler->ToRemove);
RemoveEntryList (&MmiHandler->Link);
FreePool (MmiHandler);
//
// Remove the MMI_ENTRY if all handlers have been removed.
//
if (MmiEntry != NULL) {
if (IsListEmpty (&MmiEntry->MmiHandlers)) {
RemoveEntryList (&MmiEntry->AllEntries);
FreePool (MmiEntry);
return TRUE;
}
}
return FALSE;
}
/**
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;
LIST_ENTRY *EntryLink;
MMI_ENTRY *MmiEntry;
MMI_HANDLER *MmiHandler;
EFI_STATUS ReturnStatus;
BOOLEAN WillReturn;
EFI_STATUS Status;
mMmiManageCallingDepth++;
Status = EFI_NOT_FOUND;
ReturnStatus = Status;
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) {
ReturnStatus = EFI_INTERRUPT_PENDING;
WillReturn = TRUE;
} else {
//
// If any other handler's result sets ReturnStatus as EFI_SUCCESS, the return status
// will be EFI_SUCCESS.
//
if (ReturnStatus != EFI_SUCCESS) {
ReturnStatus = Status;
}
}
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) {
WillReturn = TRUE;
}
ReturnStatus = EFI_SUCCESS;
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.
//
ReturnStatus = EFI_SUCCESS;
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.
//
if (ReturnStatus != EFI_SUCCESS) {
ReturnStatus = Status;
}
break;
default:
//
// Unexpected status code returned.
//
ASSERT_EFI_ERROR (Status);
break;
}
if (WillReturn) {
break;
}
}
ASSERT (mMmiManageCallingDepth > 0);
mMmiManageCallingDepth--;
//
// MmiHandlerUnRegister() calls from MMI handlers are deferred till this point.
// Before returned from MmiManage, delete the MmiHandler which is
// marked as ToRemove.
// Note that MmiManage can be called recursively.
//
if (mMmiManageCallingDepth == 0) {
//
// Go through all MmiHandler in root Mmi handlers
//
for ( Link = GetFirstNode (&mRootMmiHandlerList)
; !IsNull (&mRootMmiHandlerList, Link);
)
{
//
// MmiHandler might be removed in below, so cache the next link in Link
//
MmiHandler = CR (Link, MMI_HANDLER, Link, MMI_HANDLER_SIGNATURE);
Link = GetNextNode (&mRootMmiHandlerList, Link);
if (MmiHandler->ToRemove) {
//
// Remove MmiHandler if the ToRemove is set.
//
RemoveMmiHandler (MmiHandler, NULL);
}
}
//
// Go through all MmiHandler in non-root MMI handlers
//
for ( EntryLink = GetFirstNode (&mMmiEntryList)
; !IsNull (&mMmiEntryList, EntryLink);
)
{
//
// MmiEntry might be removed in below, so cache the next link in EntryLink
//
MmiEntry = CR (EntryLink, MMI_ENTRY, AllEntries, MMI_ENTRY_SIGNATURE);
EntryLink = GetNextNode (&mMmiEntryList, EntryLink);
for ( Link = GetFirstNode (&MmiEntry->MmiHandlers)
; !IsNull (&MmiEntry->MmiHandlers, Link);
)
{
//
// MmiHandler might be removed in below, so cache the next link in Link
//
MmiHandler = CR (Link, MMI_HANDLER, Link, MMI_HANDLER_SIGNATURE);
Link = GetNextNode (&MmiEntry->MmiHandlers, Link);
if (MmiHandler->ToRemove) {
//
// Remove MmiHandler if the ToRemove is set.
//
if (RemoveMmiHandler (MmiHandler, MmiEntry)) {
break;
}
}
}
}
}
return ReturnStatus;
}
/**
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;
MmiHandler->ToRemove = FALSE;
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;
}
MmiHandler->ToRemove = TRUE;
if (mMmiManageCallingDepth > 0) {
//
// This function is called from MmiManage()
// Do not delete or remove MmiHandler or MmiEntry now.
//
return EFI_SUCCESS;
}
MmiEntry = MmiHandler->MmiEntry;
RemoveMmiHandler (MmiHandler, MmiEntry);
return EFI_SUCCESS;
}