mirror of
				https://gitlab.com/qemu-project/edk2.git
				synced 2025-11-03 07:59:00 +08:00 
			
		
		
		
	In last patch, we add code support to unregister MMI handler inside itself. However, the code doesn't support unregister MMI handler insider other MMI handler. While this is not a must-have usage. So add check to disallow unregister MMI handler in other MMI handler. Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Jiaxin Wu <jiaxin.wu@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Sami Mujawar <sami.mujawar@arm.com> Cc: Ray Ni <ray.ni@intel.com> Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com> Message-Id: <20240301030133.628-5-zhiguang.liu@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			354 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			354 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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);
 | 
						|
MMI_HANDLER  *mCurrentMmiHandler = NULL;
 | 
						|
 | 
						|
/**
 | 
						|
  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;) {
 | 
						|
    MmiHandler = CR (Link, MMI_HANDLER, Link, MMI_HANDLER_SIGNATURE);
 | 
						|
    //
 | 
						|
    // To support unregister MMI handler inside MMI handler itself,
 | 
						|
    // get next node before handler is executed, since LIST_ENTRY that
 | 
						|
    // Link points to may be freed if unregister MMI handler.
 | 
						|
    //
 | 
						|
    Link = Link->ForwardLink;
 | 
						|
    //
 | 
						|
    // Assign gCurrentMmiHandle before calling the MMI handler and
 | 
						|
    // set to NULL when it returns.
 | 
						|
    //
 | 
						|
    mCurrentMmiHandler = MmiHandler;
 | 
						|
    Status             = MmiHandler->Handler (
 | 
						|
                                       (EFI_HANDLE)MmiHandler,
 | 
						|
                                       Context,
 | 
						|
                                       CommBuffer,
 | 
						|
                                       CommBufferSize
 | 
						|
                                       );
 | 
						|
    mCurrentMmiHandler = NULL;
 | 
						|
 | 
						|
    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_EFI_ERROR (Status);
 | 
						|
        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;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Do not allow to unregister MMI Handler inside other MMI Handler
 | 
						|
  //
 | 
						|
  if ((mCurrentMmiHandler != NULL) && (mCurrentMmiHandler != MmiHandler)) {
 | 
						|
    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;
 | 
						|
}
 |