diff --git a/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h index b8a490a8c3..60073c78b4 100644 --- a/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h +++ b/MdeModulePkg/Core/PiSmmCore/PiSmmCore.h @@ -93,6 +93,7 @@ typedef struct { SMI_ENTRY *SmiEntry; VOID *Context; // for profile UINTN ContextSize; // for profile + BOOLEAN ToRemove; // To remove this SMI_HANDLER later } SMI_HANDLER; // diff --git a/MdeModulePkg/Core/PiSmmCore/Smi.c b/MdeModulePkg/Core/PiSmmCore/Smi.c index 2985f989c3..a84a1f48d3 100644 --- a/MdeModulePkg/Core/PiSmmCore/Smi.c +++ b/MdeModulePkg/Core/PiSmmCore/Smi.c @@ -8,6 +8,11 @@ #include "PiSmmCore.h" +// +// mSmiManageCallingDepth is used to track the depth of recursive calls of SmiManage. +// +UINTN mSmiManageCallingDepth = 0; + LIST_ENTRY mSmiEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mSmiEntryList); SMI_ENTRY mRootSmiEntry = { @@ -79,6 +84,40 @@ SmmCoreFindSmiEntry ( return SmiEntry; } +/** + Remove SmiHandler and free the memory it used. + If SmiEntry is empty, remove SmiEntry and free the memory it used. + + @param SmiHandler Points to SMI handler. + @param SmiEntry Points to SMI Entry or NULL for root SMI handlers. + + @retval TRUE SmiEntry is removed. + @retval FALSE SmiEntry is not removed. +**/ +BOOLEAN +RemoveSmiHandler ( + IN SMI_HANDLER *SmiHandler, + IN SMI_ENTRY *SmiEntry + ) +{ + ASSERT (SmiHandler->ToRemove); + RemoveEntryList (&SmiHandler->Link); + FreePool (SmiHandler); + + // + // Remove the SMI_ENTRY if all handlers have been removed. + // + if (SmiEntry != NULL) { + if (IsListEmpty (&SmiEntry->SmiHandlers)) { + RemoveEntryList (&SmiEntry->AllEntries); + FreePool (SmiEntry); + return TRUE; + } + } + + return FALSE; +} + /** Manage SMI of a particular type. @@ -104,15 +143,17 @@ SmiManage ( { LIST_ENTRY *Link; LIST_ENTRY *Head; + LIST_ENTRY *EntryLink; SMI_ENTRY *SmiEntry; SMI_HANDLER *SmiHandler; - BOOLEAN SuccessReturn; + EFI_STATUS ReturnStatus; + BOOLEAN WillReturn; EFI_STATUS Status; PERF_FUNCTION_BEGIN (); - - Status = EFI_NOT_FOUND; - SuccessReturn = FALSE; + mSmiManageCallingDepth++; + Status = EFI_NOT_FOUND; + ReturnStatus = Status; if (HandlerType == NULL) { // // Root SMI handler @@ -152,7 +193,16 @@ SmiManage ( // if (HandlerType != NULL) { PERF_FUNCTION_END (); - return EFI_INTERRUPT_PENDING; + 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; @@ -165,10 +215,10 @@ SmiManage ( // if (HandlerType != NULL) { PERF_FUNCTION_END (); - return EFI_SUCCESS; + WillReturn = TRUE; } - SuccessReturn = TRUE; + ReturnStatus = EFI_SUCCESS; break; case EFI_WARN_INTERRUPT_SOURCE_QUIESCED: @@ -176,7 +226,7 @@ SmiManage ( // If at least one of the handlers returns EFI_WARN_INTERRUPT_SOURCE_QUIESCED // then the function will return EFI_SUCCESS. // - SuccessReturn = TRUE; + ReturnStatus = EFI_SUCCESS; break; case EFI_WARN_INTERRUPT_SOURCE_PENDING: @@ -184,6 +234,10 @@ SmiManage ( // 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: @@ -193,14 +247,74 @@ SmiManage ( ASSERT (FALSE); break; } + + if (WillReturn) { + break; + } } - if (SuccessReturn) { - Status = EFI_SUCCESS; + ASSERT (mSmiManageCallingDepth > 0); + mSmiManageCallingDepth--; + + // + // SmiHandlerUnRegister() calls from SMI handlers are deferred till this point. + // Before returned from SmiManage, delete the SmiHandler which is + // marked as ToRemove. + // Note that SmiManage can be called recursively. + // + if (mSmiManageCallingDepth == 0) { + // + // Go through all SmiHandler in root SMI handlers + // + for ( Link = GetFirstNode (&mRootSmiEntry.SmiHandlers) + ; !IsNull (&mRootSmiEntry.SmiHandlers, Link); + ) + { + // + // SmiHandler might be removed in below, so cache the next link in Link + // + SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + Link = GetNextNode (&mRootSmiEntry.SmiHandlers, Link); + if (SmiHandler->ToRemove) { + // + // Remove SmiHandler if the ToRemove is set. + // + RemoveSmiHandler (SmiHandler, NULL); + } + } + + // + // Go through all SmiHandler in non-root SMI handlers + // + for ( EntryLink = GetFirstNode (&mSmiEntryList) + ; !IsNull (&mSmiEntryList, EntryLink); + ) + { + // + // SmiEntry might be removed in below, so cache the next link in EntryLink + // + SmiEntry = CR (EntryLink, SMI_ENTRY, AllEntries, SMI_ENTRY_SIGNATURE); + EntryLink = GetNextNode (&mSmiEntryList, EntryLink); + for ( Link = GetFirstNode (&SmiEntry->SmiHandlers) + ; !IsNull (&SmiEntry->SmiHandlers, Link); + ) + { + // + // SmiHandler might be removed in below, so cache the next link in Link + // + SmiHandler = CR (Link, SMI_HANDLER, Link, SMI_HANDLER_SIGNATURE); + Link = GetNextNode (&SmiEntry->SmiHandlers, Link); + if (SmiHandler->ToRemove) { + if (RemoveSmiHandler (SmiHandler, SmiEntry)) { + break; + } + } + } + } } PERF_FUNCTION_END (); - return Status; + return ReturnStatus; } /** @@ -238,6 +352,7 @@ SmiHandlerRegister ( SmiHandler->Signature = SMI_HANDLER_SIGNATURE; SmiHandler->Handler = Handler; SmiHandler->CallerAddr = (UINTN)RETURN_ADDRESS (0); + SmiHandler->ToRemove = FALSE; if (HandlerType == NULL) { // @@ -318,30 +433,27 @@ SmiHandlerUnRegister ( } } - if ((EFI_HANDLE)SmiHandler != DispatchHandle) { + if (SmiHandler == NULL) { return EFI_INVALID_PARAMETER; } - SmiEntry = SmiHandler->SmiEntry; + ASSERT ((EFI_HANDLE)SmiHandler == DispatchHandle); + SmiHandler->ToRemove = TRUE; - RemoveEntryList (&SmiHandler->Link); - FreePool (SmiHandler); - - if (SmiEntry == NULL) { + if (mSmiManageCallingDepth > 0) { // - // This is root SMI handler + // This function is called from SmiManage() + // Do not delete or remove SmiHandler or SmiEntry now. + // SmiManage will handle it later // return EFI_SUCCESS; } - if (IsListEmpty (&SmiEntry->SmiHandlers)) { - // - // No handler registered for this interrupt now, remove the SMI_ENTRY - // - RemoveEntryList (&SmiEntry->AllEntries); - - FreePool (SmiEntry); - } + SmiEntry = SmiHandler->SmiEntry; + // + // For root SMI handler, use NULL for SmiEntry + // + RemoveSmiHandler (SmiHandler, (SmiEntry == &mRootSmiEntry) ? NULL : SmiEntry); return EFI_SUCCESS; }