| /** @file | |
| EFI PEI Core dispatch services | |
| Copyright (c) 2006 - 2024, Intel Corporation. All rights reserved.<BR> | |
| (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> | |
| Copyright (c) Microsoft Corporation. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "PeiMain.h" | |
| // | |
| // Utility global variables | |
| // | |
| /** | |
| DelayedDispatchDispatcher | |
| Delayed Dispach cycle (ie one pass) through each entry, calling functions when their | |
| time has expired. When DelayedGroupId is specified, if there are any of the specified entries | |
| in the dispatch queue during dispatch, repeat the DelayedDispatch cycle. | |
| @param DelayedDispatchTable Pointer to dispatch table | |
| @param OPTIONAL DelayedGroupId used to insure particular time is met. | |
| @return BOOLEAN | |
| **/ | |
| BOOLEAN | |
| DelayedDispatchDispatcher ( | |
| IN DELAYED_DISPATCH_TABLE *DelayedDispatchTable, | |
| IN EFI_GUID *DelayedGroupId OPTIONAL | |
| ); | |
| /** | |
| DelayedDispatch End of PEI callback function. Insure that all of the delayed dispatch | |
| entries are complete before exiting PEI. | |
| @param[in] PeiServices - Pointer to PEI Services Table. | |
| @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that | |
| caused this function to execute. | |
| @param[in] Ppi - Pointer to the PPI data associated with this function. | |
| @retval EFI_STATUS - Always return EFI_SUCCESS | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiDelayedDispatchOnEndOfPei ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, | |
| IN VOID *Ppi | |
| ); | |
| EFI_DELAYED_DISPATCH_PPI mDelayedDispatchPpi = { PeiDelayedDispatchRegister, PeiDelayedDispatchWaitOnEvent }; | |
| EFI_PEI_PPI_DESCRIPTOR mDelayedDispatchDesc = { | |
| (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), | |
| &gEfiPeiDelayedDispatchPpiGuid, | |
| &mDelayedDispatchPpi | |
| }; | |
| EFI_PEI_NOTIFY_DESCRIPTOR mDelayedDispatchNotifyDesc = { | |
| EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, | |
| &gEfiEndOfPeiSignalPpiGuid, | |
| PeiDelayedDispatchOnEndOfPei | |
| }; | |
| /** | |
| Helper function to look up DELAYED_DISPATCH_TABLE published in HOB. | |
| @return Pointer to DELAYED_DISPATCH_TABLE from HOB | |
| **/ | |
| DELAYED_DISPATCH_TABLE * | |
| GetDelayedDispatchTable ( | |
| VOID | |
| ) | |
| { | |
| EFI_HOB_GUID_TYPE *GuidHob; | |
| GuidHob = GetFirstGuidHob (&gEfiDelayedDispatchTableGuid); | |
| if (GuidHob == NULL) { | |
| // There is something off about the build if this happens. We do want to | |
| // assert here to catch it during development. | |
| DEBUG ((DEBUG_ERROR, "%a - Delayed Dispatch Hob not available.\n", __func__)); | |
| ASSERT (FALSE); | |
| return NULL; | |
| } | |
| return (DELAYED_DISPATCH_TABLE *)GET_GUID_HOB_DATA (GuidHob); | |
| } | |
| /** | |
| Register a callback to be called after a minimum delay has occurred. | |
| This service is the single member function of the EFI_DELAYED_DISPATCH_PPI | |
| @param[in] This Pointer to the EFI_DELAYED_DISPATCH_PPI instance | |
| @param[in] Function Function to call back | |
| @param[in] Context Context data | |
| @param[in] DelayedGroupId GUID for this Delayed Dispatch request. | |
| @param[in] Delay Delay interval | |
| @retval EFI_SUCCESS Function successfully loaded | |
| @retval EFI_INVALID_PARAMETER One of the Arguments is not supported | |
| @retval EFI_OUT_OF_RESOURCES No more entries | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiDelayedDispatchRegister ( | |
| IN EFI_DELAYED_DISPATCH_PPI *This, | |
| IN EFI_DELAYED_DISPATCH_FUNCTION Function, | |
| IN UINT64 Context, | |
| IN EFI_GUID *DelayedGroupId OPTIONAL, | |
| IN UINT32 Delay | |
| ) | |
| { | |
| DELAYED_DISPATCH_TABLE *DelayedDispatchTable; | |
| DELAYED_DISPATCH_ENTRY *Entry; | |
| EFI_STATUS Status; | |
| // Check input parameters | |
| if ((Function == NULL) || (Delay > FixedPcdGet32 (PcdDelayedDispatchMaxDelayUs)) || (This == NULL)) { | |
| DEBUG ((DEBUG_ERROR, "%a Invalid parameter. Function: %Lx, Delay: %u, This: %p\n", __func__, (UINT64)(UINTN)Function, Delay, This)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto Exit; | |
| } | |
| // Get delayed dispatch table | |
| DelayedDispatchTable = GetDelayedDispatchTable (); | |
| if (DelayedDispatchTable == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a Unable to locate dispatch table\n", __func__)); | |
| Status = EFI_UNSUPPORTED; | |
| goto Exit; | |
| } | |
| // Check for available entry slots | |
| if (DelayedDispatchTable->Count >= PcdGet32 (PcdDelayedDispatchMaxEntries)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a DelayedDispatchTable->Count = %d. PcdDelayedDispatchMaxEntries (%d) is too small.\n", | |
| __func__, | |
| DelayedDispatchTable->Count, | |
| PcdGet32 (PcdDelayedDispatchMaxEntries) | |
| )); | |
| ASSERT (DelayedDispatchTable->Count < PcdGet32 (PcdDelayedDispatchMaxEntries)); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Exit; | |
| } | |
| Entry = &DelayedDispatchTable->Entry[DelayedDispatchTable->Count]; | |
| Entry->Function = Function; | |
| Entry->Context = Context; | |
| Status = SafeUint64Add (GET_TIME_IN_US (), Delay, &Entry->DispatchTime); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a Delay overflow\n", __func__)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto Exit; | |
| } | |
| if (DelayedGroupId == NULL) { | |
| ZeroMem (&Entry->DelayedGroupId, sizeof (EFI_GUID)); | |
| } else { | |
| CopyGuid (&Entry->DelayedGroupId, DelayedGroupId); | |
| } | |
| Entry->MicrosecondDelay = Delay; | |
| DelayedDispatchTable->Count++; | |
| DEBUG ((DEBUG_INFO, "%a Adding dispatch Entry\n", __func__)); | |
| DEBUG ((DEBUG_INFO, " Requested Delay = %d\n", Delay)); | |
| DEBUG ((DEBUG_INFO, " Trigger Time = %d\n", Entry->DispatchTime)); | |
| DEBUG ((DEBUG_INFO, " Context = 0x%016lx\n", Entry->Context)); | |
| DEBUG ((DEBUG_INFO, " Function = %Lx\n", (UINT64)(UINTN)Entry->Function)); | |
| DEBUG ((DEBUG_INFO, " DelayedGroupId = %g\n", &Entry->DelayedGroupId)); | |
| if (Delay == 0) { | |
| // Force early dispatch point | |
| DelayedDispatchDispatcher (DelayedDispatchTable, NULL); | |
| } | |
| Status = EFI_SUCCESS; | |
| Exit: | |
| return Status; | |
| } | |
| /** | |
| DelayedDispatchDispatcher | |
| Delayed Dispach cycle (ie one pass) through each entry, calling functions when their | |
| time has expired. When DelayedGroupId is specified, if there are any of the specified entries | |
| in the dispatch queue during dispatch, repeat the DelayedDispatch cycle. | |
| @param DelayedDispatchTable Pointer to dispatch table | |
| @param OPTIONAL DelayedGroupId used to insure particular time is met. | |
| @return BOOLEAN | |
| **/ | |
| BOOLEAN | |
| DelayedDispatchDispatcher ( | |
| IN DELAYED_DISPATCH_TABLE *DelayedDispatchTable, | |
| IN EFI_GUID *DelayedGroupId OPTIONAL | |
| ) | |
| { | |
| BOOLEAN Dispatched; | |
| UINT64 TimeCurrent; | |
| UINT64 MaxDispatchTime; | |
| UINTN Index1; | |
| BOOLEAN DelayedGroupIdPresent; | |
| DELAYED_DISPATCH_ENTRY *Entry; | |
| EFI_STATUS Status; | |
| Dispatched = FALSE; | |
| DelayedGroupIdPresent = TRUE; | |
| Status = SafeUint64Add (GET_TIME_IN_US (), FixedPcdGet32 (PcdDelayedDispatchCompletionTimeoutUs), &MaxDispatchTime); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a Delay overflow\n", __func__)); | |
| return FALSE; | |
| } | |
| while ((DelayedDispatchTable->Count > 0) && (DelayedGroupIdPresent)) { | |
| DelayedGroupIdPresent = FALSE; | |
| DelayedDispatchTable->DispCount++; | |
| // If dispatching is messed up, clear DelayedDispatchTable and exit. | |
| TimeCurrent = GET_TIME_IN_US (); | |
| if (TimeCurrent > MaxDispatchTime) { | |
| DEBUG ((DEBUG_ERROR, "%a - DelayedDispatch Completion timeout!\n", __func__)); | |
| ReportStatusCode ((EFI_ERROR_MAJOR | EFI_ERROR_CODE), (EFI_SOFTWARE_PEI_CORE | EFI_SW_EC_ABORTED)); | |
| ASSERT (FALSE); | |
| DelayedDispatchTable->Count = 0; | |
| break; | |
| } | |
| // Check each entry in the table for possible dispatch | |
| for (Index1 = 0; Index1 < DelayedDispatchTable->Count;) { | |
| Entry = &DelayedDispatchTable->Entry[Index1]; | |
| // If DelayedGroupId is present, insure there is an additional check of the table. | |
| if (DelayedGroupId != NULL) { | |
| if (CompareGuid (DelayedGroupId, &Entry->DelayedGroupId)) { | |
| DelayedGroupIdPresent = TRUE; | |
| } | |
| } | |
| TimeCurrent = GET_TIME_IN_US (); | |
| if (TimeCurrent >= Entry->DispatchTime) { | |
| // Time expired, invoked the function | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "Delayed dispatch entry %d @ %p, Target=%d, Act=%d Disp=%d\n", | |
| Index1, | |
| Entry->Function, | |
| Entry->DispatchTime, | |
| TimeCurrent, | |
| DelayedDispatchTable->DispCount | |
| )); | |
| Dispatched = TRUE; | |
| Entry->MicrosecondDelay = 0; | |
| Entry->Function ( | |
| &Entry->Context, | |
| &Entry->MicrosecondDelay | |
| ); | |
| DEBUG ((DEBUG_ERROR, "Delayed dispatch Function returned delay=%d\n", Entry->MicrosecondDelay)); | |
| if (Entry->MicrosecondDelay == 0) { | |
| // NewTime = 0 = delete this entry from the table | |
| DelayedDispatchTable->Count--; | |
| CopyMem (Entry, Entry+1, sizeof (DELAYED_DISPATCH_ENTRY) * (DelayedDispatchTable->Count - Index1)); | |
| } else { | |
| if (Entry->MicrosecondDelay > FixedPcdGet32 (PcdDelayedDispatchMaxDelayUs)) { | |
| DEBUG ((DEBUG_ERROR, "%a Illegal new delay %d requested\n", __func__, Entry->MicrosecondDelay)); | |
| ASSERT (FALSE); | |
| Entry->MicrosecondDelay = FixedPcdGet32 (PcdDelayedDispatchMaxDelayUs); | |
| } | |
| // NewTime != 0 - update the time from us to Dispatch time | |
| Status = SafeUint64Add (GET_TIME_IN_US (), Entry->MicrosecondDelay, &Entry->DispatchTime); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a Delay overflow, this event will likely never be fired...\n", __func__)); | |
| Entry->DispatchTime = MAX_UINT64; | |
| } | |
| Index1++; | |
| } | |
| } else { | |
| Index1++; | |
| } | |
| } | |
| } | |
| return Dispatched; | |
| } | |
| /** | |
| Wait on a registered Delayed Dispatch unit that has a DelayedGroupId. Continue | |
| to dispatch all registered delayed dispatch entries until *ALL* entries with | |
| DelayedGroupId have completed. | |
| Example usage: | |
| 1. Register a Delayed Dispatch entry with a DelayedGroupId. | |
| 2. Call this function with the DelayedGroupId | |
| 3. The registered function in #1 will be called after the specified delay. | |
| 4. This function will wait until all entries with the DelayedGroupId have completed. | |
| @param[in] This The Delayed Dispatch PPI pointer. | |
| @param[in] DelayedGroupId Delayed dispatch request ID the caller will wait on | |
| @retval EFI_SUCCESS The operation succeeds. | |
| @retval EFI_INVALID_PARAMETER The parameters are invalid. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiDelayedDispatchWaitOnEvent ( | |
| IN EFI_DELAYED_DISPATCH_PPI *This, | |
| IN EFI_GUID DelayedGroupId | |
| ) | |
| { | |
| PERF_FUNCTION_BEGIN (); | |
| EFI_STATUS Status; | |
| DELAYED_DISPATCH_TABLE *DelayedDispatchTable; | |
| // Get delayed dispatch table | |
| DelayedDispatchTable = GetDelayedDispatchTable (); | |
| if (DelayedDispatchTable == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a Unable to locate dispatch table\n", __func__)); | |
| Status = EFI_UNSUPPORTED; | |
| goto Exit; | |
| } | |
| if (IsZeroGuid (&DelayedGroupId)) { | |
| DEBUG ((DEBUG_ERROR, "%a Delayed Group ID is a null GUID\n", __func__)); | |
| Status = EFI_UNSUPPORTED; | |
| goto Exit; | |
| } | |
| DEBUG ((DEBUG_INFO, "Delayed dispatch on %g. Count=%d, DispatchCount=%d\n", &DelayedGroupId, DelayedDispatchTable->Count, DelayedDispatchTable->DispCount)); | |
| PERF_EVENT_SIGNAL_BEGIN (&DelayedGroupId); | |
| DelayedDispatchDispatcher (DelayedDispatchTable, &DelayedGroupId); | |
| PERF_EVENT_SIGNAL_END (&DelayedGroupId); | |
| Status = EFI_SUCCESS; | |
| Exit: | |
| PERF_FUNCTION_END (); | |
| return Status; | |
| } | |
| /** | |
| DelayedDispatch End of PEI callback function. Insure that all of the delayed dispatch | |
| entries are complete before exiting PEI. | |
| @param[in] PeiServices - Pointer to PEI Services Table. | |
| @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that | |
| caused this function to execute. | |
| @param[in] Ppi - Pointer to the PPI data associated with this function. | |
| @retval EFI_STATUS - Always return EFI_SUCCESS | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiDelayedDispatchOnEndOfPei ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, | |
| IN VOID *Ppi | |
| ) | |
| { | |
| DELAYED_DISPATCH_TABLE *DelayedDispatchTable; | |
| // Get delayed dispatch table | |
| DelayedDispatchTable = GetDelayedDispatchTable (); | |
| if (DelayedDispatchTable == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a Unable to locate dispatch table\n", __func__)); | |
| return EFI_UNSUPPORTED; | |
| } | |
| PERF_INMODULE_BEGIN ("PerfDelayedDispatchEndOfPei"); | |
| while (DelayedDispatchTable->Count > 0) { | |
| DelayedDispatchDispatcher (DelayedDispatchTable, NULL); | |
| } | |
| DEBUG ((DEBUG_ERROR, "%a Count of dispatch cycles is %d\n", __func__, DelayedDispatchTable->DispCount)); | |
| PERF_INMODULE_END ("PerfDelayedDispatchEndOfPei"); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Discover all PEIMs and optional Apriori file in one FV. There is at most one | |
| Apriori file in one FV. | |
| @param Private Pointer to the private data passed in from caller | |
| @param CoreFileHandle The instance of PEI_CORE_FV_HANDLE. | |
| **/ | |
| VOID | |
| DiscoverPeimsAndOrderWithApriori ( | |
| IN PEI_CORE_INSTANCE *Private, | |
| IN PEI_CORE_FV_HANDLE *CoreFileHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PEI_FILE_HANDLE FileHandle; | |
| EFI_PEI_FILE_HANDLE AprioriFileHandle; | |
| EFI_GUID *Apriori; | |
| UINTN Index; | |
| UINTN Index2; | |
| UINTN PeimIndex; | |
| UINTN PeimCount; | |
| EFI_GUID *Guid; | |
| EFI_PEI_FILE_HANDLE *TempFileHandles; | |
| EFI_GUID *TempFileGuid; | |
| EFI_PEI_FIRMWARE_VOLUME_PPI *FvPpi; | |
| EFI_FV_FILE_INFO FileInfo; | |
| FvPpi = CoreFileHandle->FvPpi; | |
| // | |
| // Walk the FV and find all the PEIMs and the Apriori file. | |
| // | |
| AprioriFileHandle = NULL; | |
| Private->CurrentFvFileHandles = NULL; | |
| Guid = NULL; | |
| // | |
| // If the current FV has been scanned, directly get its cached records. | |
| // | |
| if (CoreFileHandle->ScanFv) { | |
| Private->CurrentFvFileHandles = CoreFileHandle->FvFileHandles; | |
| return; | |
| } | |
| TempFileHandles = Private->TempFileHandles; | |
| TempFileGuid = Private->TempFileGuid; | |
| // | |
| // Go ahead to scan this FV, get PeimCount and cache FileHandles within it to TempFileHandles. | |
| // | |
| PeimCount = 0; | |
| FileHandle = NULL; | |
| do { | |
| Status = FvPpi->FindFileByType (FvPpi, PEI_CORE_INTERNAL_FFS_FILE_DISPATCH_TYPE, CoreFileHandle->FvHandle, &FileHandle); | |
| if (!EFI_ERROR (Status)) { | |
| if (PeimCount >= Private->TempPeimCount) { | |
| // | |
| // Run out of room, grow the buffer. | |
| // | |
| TempFileHandles = AllocatePool ( | |
| sizeof (EFI_PEI_FILE_HANDLE) * (Private->TempPeimCount + TEMP_FILE_GROWTH_STEP) | |
| ); | |
| ASSERT (TempFileHandles != NULL); | |
| CopyMem ( | |
| TempFileHandles, | |
| Private->TempFileHandles, | |
| sizeof (EFI_PEI_FILE_HANDLE) * Private->TempPeimCount | |
| ); | |
| Private->TempFileHandles = TempFileHandles; | |
| TempFileGuid = AllocatePool ( | |
| sizeof (EFI_GUID) * (Private->TempPeimCount + TEMP_FILE_GROWTH_STEP) | |
| ); | |
| ASSERT (TempFileGuid != NULL); | |
| CopyMem ( | |
| TempFileGuid, | |
| Private->TempFileGuid, | |
| sizeof (EFI_GUID) * Private->TempPeimCount | |
| ); | |
| Private->TempFileGuid = TempFileGuid; | |
| Private->TempPeimCount = Private->TempPeimCount + TEMP_FILE_GROWTH_STEP; | |
| } | |
| TempFileHandles[PeimCount++] = FileHandle; | |
| } | |
| } while (!EFI_ERROR (Status)); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a(): Found 0x%x PEI FFS files in the %dth FV\n", | |
| __func__, | |
| PeimCount, | |
| Private->CurrentPeimFvCount | |
| )); | |
| if (PeimCount == 0) { | |
| // | |
| // No PEIM FFS file is found, set ScanFv flag and return. | |
| // | |
| CoreFileHandle->ScanFv = TRUE; | |
| return; | |
| } | |
| // | |
| // Record PeimCount, allocate buffer for PeimState and FvFileHandles. | |
| // | |
| CoreFileHandle->PeimCount = PeimCount; | |
| CoreFileHandle->PeimState = AllocateZeroPool (sizeof (UINT8) * PeimCount); | |
| ASSERT (CoreFileHandle->PeimState != NULL); | |
| CoreFileHandle->FvFileHandles = AllocateZeroPool (sizeof (EFI_PEI_FILE_HANDLE) * PeimCount); | |
| ASSERT (CoreFileHandle->FvFileHandles != NULL); | |
| // | |
| // Get Apriori File handle | |
| // | |
| Private->AprioriCount = 0; | |
| Status = FvPpi->FindFileByName (FvPpi, &gPeiAprioriFileNameGuid, &CoreFileHandle->FvHandle, &AprioriFileHandle); | |
| if (!EFI_ERROR (Status) && (AprioriFileHandle != NULL)) { | |
| // | |
| // Read the Apriori file | |
| // | |
| Status = FvPpi->FindSectionByType (FvPpi, EFI_SECTION_RAW, AprioriFileHandle, (VOID **)&Apriori); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Calculate the number of PEIMs in the Apriori file | |
| // | |
| Status = FvPpi->GetFileInfo (FvPpi, AprioriFileHandle, &FileInfo); | |
| ASSERT_EFI_ERROR (Status); | |
| Private->AprioriCount = FileInfo.BufferSize; | |
| if (IS_SECTION2 (FileInfo.Buffer)) { | |
| Private->AprioriCount -= sizeof (EFI_COMMON_SECTION_HEADER2); | |
| } else { | |
| Private->AprioriCount -= sizeof (EFI_COMMON_SECTION_HEADER); | |
| } | |
| Private->AprioriCount /= sizeof (EFI_GUID); | |
| for (Index = 0; Index < PeimCount; Index++) { | |
| // | |
| // Make an array of file name GUIDs that matches the FileHandle array so we can convert | |
| // quickly from file name to file handle | |
| // | |
| Status = FvPpi->GetFileInfo (FvPpi, TempFileHandles[Index], &FileInfo); | |
| ASSERT_EFI_ERROR (Status); | |
| CopyMem (&TempFileGuid[Index], &FileInfo.FileName, sizeof (EFI_GUID)); | |
| } | |
| // | |
| // Walk through TempFileGuid array to find out who is invalid PEIM GUID in Apriori file. | |
| // Add available PEIMs in Apriori file into FvFileHandles array. | |
| // | |
| Index = 0; | |
| for (Index2 = 0; Index2 < Private->AprioriCount; Index2++) { | |
| Guid = ScanGuid (TempFileGuid, PeimCount * sizeof (EFI_GUID), &Apriori[Index2]); | |
| if (Guid != NULL) { | |
| PeimIndex = ((UINTN)Guid - (UINTN)&TempFileGuid[0])/sizeof (EFI_GUID); | |
| CoreFileHandle->FvFileHandles[Index++] = TempFileHandles[PeimIndex]; | |
| // | |
| // Since we have copied the file handle we can remove it from this list. | |
| // | |
| TempFileHandles[PeimIndex] = NULL; | |
| } | |
| } | |
| // | |
| // Update valid AprioriCount | |
| // | |
| Private->AprioriCount = Index; | |
| // | |
| // Add in any PEIMs not in the Apriori file | |
| // | |
| for (Index2 = 0; Index2 < PeimCount; Index2++) { | |
| if (TempFileHandles[Index2] != NULL) { | |
| CoreFileHandle->FvFileHandles[Index++] = TempFileHandles[Index2]; | |
| TempFileHandles[Index2] = NULL; | |
| } | |
| } | |
| ASSERT (Index == PeimCount); | |
| } | |
| } else { | |
| CopyMem (CoreFileHandle->FvFileHandles, TempFileHandles, sizeof (EFI_PEI_FILE_HANDLE) * PeimCount); | |
| } | |
| // | |
| // The current FV File Handles have been cached. So that we don't have to scan the FV again. | |
| // Instead, we can retrieve the file handles within this FV from cached records. | |
| // | |
| CoreFileHandle->ScanFv = TRUE; | |
| Private->CurrentFvFileHandles = CoreFileHandle->FvFileHandles; | |
| } | |
| // | |
| // This is the minimum memory required by DxeCore initialization. When LMFA feature enabled, | |
| // This part of memory still need reserved on the very top of memory so that the DXE Core could | |
| // use these memory for data initialization. This macro should be sync with the same marco | |
| // defined in DXE Core. | |
| // | |
| #define MINIMUM_INITIAL_MEMORY_SIZE 0x10000 | |
| /** | |
| This function is to test if the memory range described in resource HOB is available or not. | |
| This function should only be invoked when Loading Module at Fixed Address(LMFA) feature is enabled. Some platform may allocate the | |
| memory before PeiLoadFixAddressHook in invoked. so this function is to test if the memory range described by the input resource HOB is | |
| available or not. | |
| @param PrivateData Pointer to the private data passed in from caller | |
| @param ResourceHob Pointer to a resource HOB which described the memory range described by the input resource HOB | |
| **/ | |
| BOOLEAN | |
| PeiLoadFixAddressIsMemoryRangeAvailable ( | |
| IN PEI_CORE_INSTANCE *PrivateData, | |
| IN EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob | |
| ) | |
| { | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryHob; | |
| BOOLEAN IsAvailable; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| IsAvailable = TRUE; | |
| if ((PrivateData == NULL) || (ResourceHob == NULL)) { | |
| return FALSE; | |
| } | |
| // | |
| // test if the memory range describe in the HOB is already allocated. | |
| // | |
| for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| // | |
| // See if this is a memory allocation HOB | |
| // | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_MEMORY_ALLOCATION) { | |
| MemoryHob = Hob.MemoryAllocation; | |
| if ((MemoryHob->AllocDescriptor.MemoryBaseAddress == ResourceHob->PhysicalStart) && | |
| (MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength == ResourceHob->PhysicalStart + ResourceHob->ResourceLength)) | |
| { | |
| IsAvailable = FALSE; | |
| break; | |
| } | |
| } | |
| } | |
| return IsAvailable; | |
| } | |
| /** | |
| Hook function for Loading Module at Fixed Address feature | |
| This function should only be invoked when Loading Module at Fixed Address(LMFA) feature is enabled. When feature is | |
| configured as Load Modules at Fix Absolute Address, this function is to validate the top address assigned by user. When | |
| feature is configured as Load Modules at Fixed Offset, the function is to find the top address which is TOLM-TSEG in general. | |
| And also the function will re-install PEI memory. | |
| @param PrivateData Pointer to the private data passed in from caller | |
| **/ | |
| VOID | |
| PeiLoadFixAddressHook ( | |
| IN PEI_CORE_INSTANCE *PrivateData | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS TopLoadingAddress; | |
| UINT64 PeiMemorySize; | |
| UINT64 TotalReservedMemorySize; | |
| UINT64 MemoryRangeEnd; | |
| EFI_PHYSICAL_ADDRESS HighAddress; | |
| EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; | |
| EFI_HOB_RESOURCE_DESCRIPTOR *NextResourceHob; | |
| EFI_HOB_RESOURCE_DESCRIPTOR *CurrentResourceHob; | |
| EFI_PEI_HOB_POINTERS CurrentHob; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_PEI_HOB_POINTERS NextHob; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryHob; | |
| // | |
| // Initialize Local Variables | |
| // | |
| CurrentResourceHob = NULL; | |
| ResourceHob = NULL; | |
| NextResourceHob = NULL; | |
| HighAddress = 0; | |
| TopLoadingAddress = 0; | |
| MemoryRangeEnd = 0; | |
| CurrentHob.Raw = PrivateData->HobList.Raw; | |
| PeiMemorySize = PrivateData->PhysicalMemoryLength; | |
| // | |
| // The top reserved memory include 3 parts: the topest range is for DXE core initialization with the size MINIMUM_INITIAL_MEMORY_SIZE | |
| // then RuntimeCodePage range and Boot time code range. | |
| // | |
| TotalReservedMemorySize = MINIMUM_INITIAL_MEMORY_SIZE + EFI_PAGES_TO_SIZE (PcdGet32 (PcdLoadFixAddressRuntimeCodePageNumber)); | |
| TotalReservedMemorySize += EFI_PAGES_TO_SIZE (PcdGet32 (PcdLoadFixAddressBootTimeCodePageNumber)); | |
| // | |
| // PEI memory range lies below the top reserved memory | |
| // | |
| TotalReservedMemorySize += PeiMemorySize; | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO: PcdLoadFixAddressRuntimeCodePageNumber= 0x%x.\n", PcdGet32 (PcdLoadFixAddressRuntimeCodePageNumber))); | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO: PcdLoadFixAddressBootTimeCodePageNumber= 0x%x.\n", PcdGet32 (PcdLoadFixAddressBootTimeCodePageNumber))); | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO: PcdLoadFixAddressPeiCodePageNumber= 0x%x.\n", PcdGet32 (PcdLoadFixAddressPeiCodePageNumber))); | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO: Total Reserved Memory Size = 0x%lx.\n", TotalReservedMemorySize)); | |
| // | |
| // Loop through the system memory typed HOB to merge the adjacent memory range | |
| // | |
| for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| // | |
| // See if this is a resource descriptor HOB | |
| // | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { | |
| ResourceHob = Hob.ResourceDescriptor; | |
| // | |
| // If range described in this HOB is not system memory or higher than MAX_ADDRESS, ignored. | |
| // | |
| if ((ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) || | |
| (ResourceHob->PhysicalStart + ResourceHob->ResourceLength > MAX_ADDRESS)) | |
| { | |
| continue; | |
| } | |
| for (NextHob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (NextHob); NextHob.Raw = GET_NEXT_HOB (NextHob)) { | |
| if (NextHob.Raw == Hob.Raw) { | |
| continue; | |
| } | |
| // | |
| // See if this is a resource descriptor HOB | |
| // | |
| if (GET_HOB_TYPE (NextHob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { | |
| NextResourceHob = NextHob.ResourceDescriptor; | |
| // | |
| // test if range described in this NextResourceHob is system memory and have the same attribute. | |
| // Note: Here is a assumption that system memory should always be healthy even without test. | |
| // | |
| if ((NextResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && | |
| (((NextResourceHob->ResourceAttribute^ResourceHob->ResourceAttribute) & (~EFI_RESOURCE_ATTRIBUTE_TESTED)) == 0)) | |
| { | |
| // | |
| // See if the memory range described in ResourceHob and NextResourceHob is adjacent | |
| // | |
| if (((ResourceHob->PhysicalStart <= NextResourceHob->PhysicalStart) && | |
| (ResourceHob->PhysicalStart + ResourceHob->ResourceLength >= NextResourceHob->PhysicalStart)) || | |
| ((ResourceHob->PhysicalStart >= NextResourceHob->PhysicalStart) && | |
| (ResourceHob->PhysicalStart <= NextResourceHob->PhysicalStart + NextResourceHob->ResourceLength))) | |
| { | |
| MemoryRangeEnd = ((ResourceHob->PhysicalStart + ResourceHob->ResourceLength) > (NextResourceHob->PhysicalStart + NextResourceHob->ResourceLength)) ? | |
| (ResourceHob->PhysicalStart + ResourceHob->ResourceLength) : (NextResourceHob->PhysicalStart + NextResourceHob->ResourceLength); | |
| ResourceHob->PhysicalStart = (ResourceHob->PhysicalStart < NextResourceHob->PhysicalStart) ? | |
| ResourceHob->PhysicalStart : NextResourceHob->PhysicalStart; | |
| ResourceHob->ResourceLength = (MemoryRangeEnd - ResourceHob->PhysicalStart); | |
| ResourceHob->ResourceAttribute = ResourceHob->ResourceAttribute & (~EFI_RESOURCE_ATTRIBUTE_TESTED); | |
| // | |
| // Delete the NextResourceHob by marking it as unused. | |
| // | |
| GET_HOB_TYPE (NextHob) = EFI_HOB_TYPE_UNUSED; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Some platform is already allocated pages before the HOB re-org. Here to build dedicated resource HOB to describe | |
| // the allocated memory range | |
| // | |
| for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| // | |
| // See if this is a memory allocation HOB | |
| // | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_MEMORY_ALLOCATION) { | |
| MemoryHob = Hob.MemoryAllocation; | |
| for (NextHob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (NextHob); NextHob.Raw = GET_NEXT_HOB (NextHob)) { | |
| // | |
| // See if this is a resource descriptor HOB | |
| // | |
| if (GET_HOB_TYPE (NextHob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { | |
| NextResourceHob = NextHob.ResourceDescriptor; | |
| // | |
| // If range described in this HOB is not system memory or higher than MAX_ADDRESS, ignored. | |
| // | |
| if ((NextResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) || (NextResourceHob->PhysicalStart + NextResourceHob->ResourceLength > MAX_ADDRESS)) { | |
| continue; | |
| } | |
| // | |
| // If the range describe in memory allocation HOB belongs to the memory range described by the resource HOB | |
| // | |
| if ((MemoryHob->AllocDescriptor.MemoryBaseAddress >= NextResourceHob->PhysicalStart) && | |
| (MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength <= NextResourceHob->PhysicalStart + NextResourceHob->ResourceLength)) | |
| { | |
| // | |
| // Build separate resource HOB for this allocated range | |
| // | |
| if (MemoryHob->AllocDescriptor.MemoryBaseAddress > NextResourceHob->PhysicalStart) { | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| NextResourceHob->ResourceAttribute, | |
| NextResourceHob->PhysicalStart, | |
| (MemoryHob->AllocDescriptor.MemoryBaseAddress - NextResourceHob->PhysicalStart) | |
| ); | |
| } | |
| if (MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength < NextResourceHob->PhysicalStart + NextResourceHob->ResourceLength) { | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| NextResourceHob->ResourceAttribute, | |
| MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength, | |
| (NextResourceHob->PhysicalStart + NextResourceHob->ResourceLength -(MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength)) | |
| ); | |
| } | |
| NextResourceHob->PhysicalStart = MemoryHob->AllocDescriptor.MemoryBaseAddress; | |
| NextResourceHob->ResourceLength = MemoryHob->AllocDescriptor.MemoryLength; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Try to find and validate the TOP address. | |
| // | |
| if ((INT64)PcdGet64 (PcdLoadModuleAtFixAddressEnable) > 0 ) { | |
| // | |
| // The LMFA feature is enabled as load module at fixed absolute address. | |
| // | |
| TopLoadingAddress = (EFI_PHYSICAL_ADDRESS)PcdGet64 (PcdLoadModuleAtFixAddressEnable); | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO: Loading module at fixed absolute address.\n")); | |
| // | |
| // validate the Address. Loop the resource descriptor HOB to make sure the address is in valid memory range | |
| // | |
| if ((TopLoadingAddress & EFI_PAGE_MASK) != 0) { | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED ERROR:Top Address 0x%lx is invalid since top address should be page align. \n", TopLoadingAddress)); | |
| ASSERT (FALSE); | |
| } | |
| // | |
| // Search for a memory region that is below MAX_ADDRESS and in which TopLoadingAddress lies | |
| // | |
| for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| // | |
| // See if this is a resource descriptor HOB | |
| // | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { | |
| ResourceHob = Hob.ResourceDescriptor; | |
| // | |
| // See if this resource descriptor HOB describes tested system memory below MAX_ADDRESS | |
| // | |
| if ((ResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && | |
| (ResourceHob->PhysicalStart + ResourceHob->ResourceLength <= MAX_ADDRESS)) | |
| { | |
| // | |
| // See if Top address specified by user is valid. | |
| // | |
| if ((ResourceHob->PhysicalStart + TotalReservedMemorySize < TopLoadingAddress) && | |
| ((ResourceHob->PhysicalStart + ResourceHob->ResourceLength - MINIMUM_INITIAL_MEMORY_SIZE) >= TopLoadingAddress) && | |
| PeiLoadFixAddressIsMemoryRangeAvailable (PrivateData, ResourceHob)) | |
| { | |
| CurrentResourceHob = ResourceHob; | |
| CurrentHob = Hob; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if (CurrentResourceHob != NULL) { | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO:Top Address 0x%lx is valid \n", TopLoadingAddress)); | |
| TopLoadingAddress += MINIMUM_INITIAL_MEMORY_SIZE; | |
| } else { | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED ERROR:Top Address 0x%lx is invalid \n", TopLoadingAddress)); | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED ERROR:The recommended Top Address for the platform is: \n")); | |
| // | |
| // Print the recommended Top address range. | |
| // | |
| for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| // | |
| // See if this is a resource descriptor HOB | |
| // | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { | |
| ResourceHob = Hob.ResourceDescriptor; | |
| // | |
| // See if this resource descriptor HOB describes tested system memory below MAX_ADDRESS | |
| // | |
| if ((ResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && | |
| (ResourceHob->PhysicalStart + ResourceHob->ResourceLength <= MAX_ADDRESS)) | |
| { | |
| // | |
| // See if Top address specified by user is valid. | |
| // | |
| if ((ResourceHob->ResourceLength > TotalReservedMemorySize) && PeiLoadFixAddressIsMemoryRangeAvailable (PrivateData, ResourceHob)) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "(0x%lx, 0x%lx)\n", | |
| (ResourceHob->PhysicalStart + TotalReservedMemorySize -MINIMUM_INITIAL_MEMORY_SIZE), | |
| (ResourceHob->PhysicalStart + ResourceHob->ResourceLength -MINIMUM_INITIAL_MEMORY_SIZE) | |
| )); | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Assert here | |
| // | |
| ASSERT (FALSE); | |
| return; | |
| } | |
| } else { | |
| // | |
| // The LMFA feature is enabled as load module at fixed offset relative to TOLM | |
| // Parse the Hob list to find the topest available memory. Generally it is (TOLM - TSEG) | |
| // | |
| // | |
| // Search for a tested memory region that is below MAX_ADDRESS | |
| // | |
| for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| // | |
| // See if this is a resource descriptor HOB | |
| // | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { | |
| ResourceHob = Hob.ResourceDescriptor; | |
| // | |
| // See if this resource descriptor HOB describes tested system memory below MAX_ADDRESS | |
| // | |
| if ((ResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && | |
| (ResourceHob->PhysicalStart + ResourceHob->ResourceLength <= MAX_ADDRESS) && | |
| (ResourceHob->ResourceLength > TotalReservedMemorySize) && PeiLoadFixAddressIsMemoryRangeAvailable (PrivateData, ResourceHob)) | |
| { | |
| // | |
| // See if this is the highest largest system memory region below MaxAddress | |
| // | |
| if (ResourceHob->PhysicalStart > HighAddress) { | |
| CurrentResourceHob = ResourceHob; | |
| CurrentHob = Hob; | |
| HighAddress = CurrentResourceHob->PhysicalStart; | |
| } | |
| } | |
| } | |
| } | |
| if (CurrentResourceHob == NULL) { | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED ERROR:The System Memory is too small\n")); | |
| // | |
| // Assert here | |
| // | |
| ASSERT (FALSE); | |
| return; | |
| } else { | |
| TopLoadingAddress = CurrentResourceHob->PhysicalStart + CurrentResourceHob->ResourceLength; | |
| } | |
| } | |
| if (CurrentResourceHob != NULL) { | |
| // | |
| // rebuild resource HOB for PEI memory and reserved memory | |
| // | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_TESTED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| (TopLoadingAddress - TotalReservedMemorySize), | |
| TotalReservedMemorySize | |
| ); | |
| // | |
| // rebuild resource for the remain memory if necessary | |
| // | |
| if (CurrentResourceHob->PhysicalStart < TopLoadingAddress - TotalReservedMemorySize) { | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| CurrentResourceHob->PhysicalStart, | |
| (TopLoadingAddress - TotalReservedMemorySize - CurrentResourceHob->PhysicalStart) | |
| ); | |
| } | |
| if (CurrentResourceHob->PhysicalStart + CurrentResourceHob->ResourceLength > TopLoadingAddress ) { | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| TopLoadingAddress, | |
| (CurrentResourceHob->PhysicalStart + CurrentResourceHob->ResourceLength - TopLoadingAddress) | |
| ); | |
| } | |
| // | |
| // Delete CurrentHob by marking it as unused since the memory range described by is rebuilt. | |
| // | |
| GET_HOB_TYPE (CurrentHob) = EFI_HOB_TYPE_UNUSED; | |
| } | |
| // | |
| // Cache the top address for Loading Module at Fixed Address feature | |
| // | |
| PrivateData->LoadModuleAtFixAddressTopAddress = TopLoadingAddress - MINIMUM_INITIAL_MEMORY_SIZE; | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO: Top address = 0x%lx\n", PrivateData->LoadModuleAtFixAddressTopAddress)); | |
| // | |
| // reinstall the PEI memory relative to TopLoadingAddress | |
| // | |
| PrivateData->PhysicalMemoryBegin = TopLoadingAddress - TotalReservedMemorySize; | |
| PrivateData->FreePhysicalMemoryTop = PrivateData->PhysicalMemoryBegin + PeiMemorySize; | |
| } | |
| /** | |
| This routine is invoked in switch stack as PeiCore Entry. | |
| @param SecCoreData Points to a data structure containing information about the PEI core's operating | |
| environment, such as the size and location of temporary RAM, the stack location and | |
| the BFV location. | |
| @param Private Pointer to old core data that is used to initialize the | |
| core's data areas. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PeiCoreEntry ( | |
| IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData, | |
| IN PEI_CORE_INSTANCE *Private | |
| ) | |
| { | |
| // | |
| // Entry PEI Phase 2 | |
| // | |
| PeiCore (SecCoreData, NULL, Private); | |
| } | |
| /** | |
| Check SwitchStackSignal and switch stack if SwitchStackSignal is TRUE. | |
| @param[in] SecCoreData Points to a data structure containing information about the PEI core's operating | |
| environment, such as the size and location of temporary RAM, the stack location and | |
| the BFV location. | |
| @param[in] Private Pointer to the private data passed in from caller. | |
| **/ | |
| VOID | |
| PeiCheckAndSwitchStack ( | |
| IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData, | |
| IN PEI_CORE_INSTANCE *Private | |
| ) | |
| { | |
| VOID *LoadFixPeiCodeBegin; | |
| EFI_STATUS Status; | |
| CONST EFI_PEI_SERVICES **PeiServices; | |
| UINT64 NewStackSize; | |
| EFI_PHYSICAL_ADDRESS TopOfOldStack; | |
| EFI_PHYSICAL_ADDRESS TopOfNewStack; | |
| UINTN StackOffset; | |
| BOOLEAN StackOffsetPositive; | |
| EFI_PHYSICAL_ADDRESS TemporaryRamBase; | |
| UINTN TemporaryRamSize; | |
| UINTN TemporaryStackSize; | |
| VOID *TemporaryStackBase; | |
| UINTN PeiTemporaryRamSize; | |
| VOID *PeiTemporaryRamBase; | |
| EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI *TemporaryRamSupportPpi; | |
| EFI_PHYSICAL_ADDRESS BaseOfNewHeap; | |
| EFI_PHYSICAL_ADDRESS HoleMemBase; | |
| UINTN HoleMemSize; | |
| UINTN HeapTemporaryRamSize; | |
| EFI_PHYSICAL_ADDRESS TempBase1; | |
| UINTN TempSize1; | |
| EFI_PHYSICAL_ADDRESS TempBase2; | |
| UINTN TempSize2; | |
| UINTN Index; | |
| PeiServices = (CONST EFI_PEI_SERVICES **)&Private->Ps; | |
| if (Private->SwitchStackSignal) { | |
| // | |
| // Before switch stack from temporary memory to permanent memory, calculate the heap and stack | |
| // usage in temporary memory for debugging. | |
| // | |
| DEBUG_CODE_BEGIN (); | |
| UINT32 *StackPointer; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| for ( StackPointer = (UINT32 *)SecCoreData->StackBase; | |
| (StackPointer < (UINT32 *)((UINTN)SecCoreData->StackBase + SecCoreData->StackSize)) \ | |
| && (*StackPointer == PcdGet32 (PcdInitValueInTempStack)); | |
| StackPointer++) | |
| { | |
| } | |
| DEBUG ((DEBUG_INFO, "Temp Stack : BaseAddress=0x%p Length=0x%X\n", SecCoreData->StackBase, (UINT32)SecCoreData->StackSize)); | |
| DEBUG ((DEBUG_INFO, "Temp Heap : BaseAddress=0x%p Length=0x%X\n", SecCoreData->PeiTemporaryRamBase, (UINT32)SecCoreData->PeiTemporaryRamSize)); | |
| DEBUG ((DEBUG_INFO, "Total temporary memory: %d bytes.\n", (UINT32)SecCoreData->TemporaryRamSize)); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| " temporary memory stack ever used: %d bytes.\n", | |
| (UINT32)(SecCoreData->StackSize - ((UINTN)StackPointer - (UINTN)SecCoreData->StackBase)) | |
| )); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| " temporary memory heap used for HobList: %d bytes.\n", | |
| (UINT32)((UINTN)Private->HobList.HandoffInformationTable->EfiFreeMemoryBottom - (UINTN)Private->HobList.Raw) | |
| )); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| " temporary memory heap occupied by memory pages: %d bytes.\n", | |
| (UINT32)(UINTN)(Private->HobList.HandoffInformationTable->EfiMemoryTop - Private->HobList.HandoffInformationTable->EfiFreeMemoryTop) | |
| )); | |
| for (Hob.Raw = Private->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_MEMORY_ALLOCATION) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "Memory Allocation 0x%08x 0x%0lx - 0x%0lx\n", \ | |
| Hob.MemoryAllocation->AllocDescriptor.MemoryType, \ | |
| Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress, \ | |
| Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress + Hob.MemoryAllocation->AllocDescriptor.MemoryLength - 1 | |
| )); | |
| } | |
| } | |
| DEBUG_CODE_END (); | |
| if ((PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { | |
| // | |
| // Loading Module at Fixed Address is enabled | |
| // | |
| PeiLoadFixAddressHook (Private); | |
| // | |
| // If Loading Module at Fixed Address is enabled, Allocating memory range for Pei code range. | |
| // | |
| LoadFixPeiCodeBegin = AllocatePages ((UINTN)PcdGet32 (PcdLoadFixAddressPeiCodePageNumber)); | |
| DEBUG ((DEBUG_INFO, "LOADING MODULE FIXED INFO: PeiCodeBegin = 0x%lX, PeiCodeTop= 0x%lX\n", (UINT64)(UINTN)LoadFixPeiCodeBegin, (UINT64)((UINTN)LoadFixPeiCodeBegin + PcdGet32 (PcdLoadFixAddressPeiCodePageNumber) * EFI_PAGE_SIZE))); | |
| } | |
| // | |
| // Reserve the size of new stack at bottom of physical memory | |
| // | |
| // The size of new stack in permanent memory must be the same size | |
| // or larger than the size of old stack in temporary memory. | |
| // But if new stack is smaller than the size of old stack, we also reserve | |
| // the size of old stack at bottom of permanent memory. | |
| // | |
| NewStackSize = RShiftU64 (Private->PhysicalMemoryLength, 1); | |
| NewStackSize = ALIGN_VALUE (NewStackSize, EFI_PAGE_SIZE); | |
| NewStackSize = MIN (PcdGet32 (PcdPeiCoreMaxPeiStackSize), NewStackSize); | |
| DEBUG ((DEBUG_INFO, "Old Stack size %d, New stack size %d\n", (UINT32)SecCoreData->StackSize, (UINT32)NewStackSize)); | |
| ASSERT (NewStackSize >= SecCoreData->StackSize); | |
| // | |
| // Calculate stack offset and heap offset between temporary memory and new permanent | |
| // memory separately. | |
| // | |
| TopOfOldStack = (UINTN)SecCoreData->StackBase + SecCoreData->StackSize; | |
| TopOfNewStack = Private->PhysicalMemoryBegin + NewStackSize; | |
| if (TopOfNewStack >= TopOfOldStack) { | |
| StackOffsetPositive = TRUE; | |
| StackOffset = (UINTN)(TopOfNewStack - TopOfOldStack); | |
| } else { | |
| StackOffsetPositive = FALSE; | |
| StackOffset = (UINTN)(TopOfOldStack - TopOfNewStack); | |
| } | |
| Private->StackOffsetPositive = StackOffsetPositive; | |
| Private->StackOffset = StackOffset; | |
| // | |
| // Build Stack HOB that describes the permanent memory stack | |
| // | |
| DEBUG ((DEBUG_INFO, "Stack Hob: BaseAddress=0x%lX Length=0x%lX\n", TopOfNewStack - NewStackSize, NewStackSize)); | |
| BuildStackHob (TopOfNewStack - NewStackSize, NewStackSize); | |
| // | |
| // Cache information from SecCoreData into locals before SecCoreData is converted to a permanent memory address | |
| // | |
| TemporaryRamBase = (EFI_PHYSICAL_ADDRESS)(UINTN)SecCoreData->TemporaryRamBase; | |
| TemporaryRamSize = SecCoreData->TemporaryRamSize; | |
| TemporaryStackSize = SecCoreData->StackSize; | |
| TemporaryStackBase = SecCoreData->StackBase; | |
| PeiTemporaryRamSize = SecCoreData->PeiTemporaryRamSize; | |
| PeiTemporaryRamBase = SecCoreData->PeiTemporaryRamBase; | |
| // | |
| // TemporaryRamSupportPpi is produced by platform's SEC | |
| // | |
| Status = PeiServicesLocatePpi ( | |
| &gEfiTemporaryRamSupportPpiGuid, | |
| 0, | |
| NULL, | |
| (VOID **)&TemporaryRamSupportPpi | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Heap Offset | |
| // | |
| BaseOfNewHeap = TopOfNewStack; | |
| if (BaseOfNewHeap >= (UINTN)SecCoreData->PeiTemporaryRamBase) { | |
| Private->HeapOffsetPositive = TRUE; | |
| Private->HeapOffset = (UINTN)(BaseOfNewHeap - (UINTN)SecCoreData->PeiTemporaryRamBase); | |
| } else { | |
| Private->HeapOffsetPositive = FALSE; | |
| Private->HeapOffset = (UINTN)((UINTN)SecCoreData->PeiTemporaryRamBase - BaseOfNewHeap); | |
| } | |
| DEBUG ((DEBUG_INFO, "Heap Offset = 0x%lX Stack Offset = 0x%lX\n", (UINT64)Private->HeapOffset, (UINT64)Private->StackOffset)); | |
| // | |
| // Calculate new HandOffTable and PrivateData address in permanent memory's stack | |
| // | |
| if (StackOffsetPositive) { | |
| SecCoreData = (CONST EFI_SEC_PEI_HAND_OFF *)((UINTN)(VOID *)SecCoreData + StackOffset); | |
| Private = (PEI_CORE_INSTANCE *)((UINTN)(VOID *)Private + StackOffset); | |
| } else { | |
| SecCoreData = (CONST EFI_SEC_PEI_HAND_OFF *)((UINTN)(VOID *)SecCoreData - StackOffset); | |
| Private = (PEI_CORE_INSTANCE *)((UINTN)(VOID *)Private - StackOffset); | |
| } | |
| // | |
| // Temporary Ram Support PPI is provided by platform, it will copy | |
| // temporary memory to permanent memory and do stack switching. | |
| // After invoking Temporary Ram Support PPI, the following code's | |
| // stack is in permanent memory. | |
| // | |
| TemporaryRamSupportPpi->TemporaryRamMigration ( | |
| PeiServices, | |
| TemporaryRamBase, | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)(TopOfNewStack - TemporaryStackSize), | |
| TemporaryRamSize | |
| ); | |
| // | |
| // Migrate memory pages allocated in pre-memory phase. | |
| // It could not be called before calling TemporaryRamSupportPpi->TemporaryRamMigration() | |
| // as the migrated memory pages may be overridden by TemporaryRamSupportPpi->TemporaryRamMigration(). | |
| // | |
| MigrateMemoryPages (Private, TRUE); | |
| // | |
| // Entry PEI Phase 2 | |
| // | |
| PeiCore (SecCoreData, NULL, Private); | |
| } else { | |
| // | |
| // Migrate memory pages allocated in pre-memory phase. | |
| // | |
| MigrateMemoryPages (Private, FALSE); | |
| // | |
| // Migrate the PEI Services Table pointer from temporary RAM to permanent RAM. | |
| // | |
| MigratePeiServicesTablePointer (); | |
| // | |
| // Heap Offset | |
| // | |
| BaseOfNewHeap = TopOfNewStack; | |
| HoleMemBase = TopOfNewStack; | |
| HoleMemSize = TemporaryRamSize - PeiTemporaryRamSize - TemporaryStackSize; | |
| if (HoleMemSize != 0) { | |
| // | |
| // Make sure HOB List start address is 8 byte alignment. | |
| // | |
| BaseOfNewHeap = ALIGN_VALUE (BaseOfNewHeap + HoleMemSize, 8); | |
| } | |
| if (BaseOfNewHeap >= (UINTN)SecCoreData->PeiTemporaryRamBase) { | |
| Private->HeapOffsetPositive = TRUE; | |
| Private->HeapOffset = (UINTN)(BaseOfNewHeap - (UINTN)SecCoreData->PeiTemporaryRamBase); | |
| } else { | |
| Private->HeapOffsetPositive = FALSE; | |
| Private->HeapOffset = (UINTN)((UINTN)SecCoreData->PeiTemporaryRamBase - BaseOfNewHeap); | |
| } | |
| DEBUG ((DEBUG_INFO, "Heap Offset = 0x%lX Stack Offset = 0x%lX\n", (UINT64)Private->HeapOffset, (UINT64)Private->StackOffset)); | |
| // | |
| // Migrate Heap | |
| // | |
| HeapTemporaryRamSize = (UINTN)(Private->HobList.HandoffInformationTable->EfiFreeMemoryBottom - Private->HobList.HandoffInformationTable->EfiMemoryBottom); | |
| ASSERT (BaseOfNewHeap + HeapTemporaryRamSize <= Private->FreePhysicalMemoryTop); | |
| CopyMem ((UINT8 *)(UINTN)BaseOfNewHeap, PeiTemporaryRamBase, HeapTemporaryRamSize); | |
| // | |
| // Migrate Stack | |
| // | |
| CopyMem ((UINT8 *)(UINTN)(TopOfNewStack - TemporaryStackSize), TemporaryStackBase, TemporaryStackSize); | |
| // | |
| // Copy Hole Range Data | |
| // | |
| if (HoleMemSize != 0) { | |
| // | |
| // Prepare Hole | |
| // | |
| if (PeiTemporaryRamBase < TemporaryStackBase) { | |
| TempBase1 = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiTemporaryRamBase; | |
| TempSize1 = PeiTemporaryRamSize; | |
| TempBase2 = (EFI_PHYSICAL_ADDRESS)(UINTN)TemporaryStackBase; | |
| TempSize2 = TemporaryStackSize; | |
| } else { | |
| TempBase1 = (EFI_PHYSICAL_ADDRESS)(UINTN)TemporaryStackBase; | |
| TempSize1 = TemporaryStackSize; | |
| TempBase2 = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiTemporaryRamBase; | |
| TempSize2 = PeiTemporaryRamSize; | |
| } | |
| if (TemporaryRamBase < TempBase1) { | |
| Private->HoleData[0].Base = TemporaryRamBase; | |
| Private->HoleData[0].Size = (UINTN)(TempBase1 - TemporaryRamBase); | |
| } | |
| if (TempBase1 + TempSize1 < TempBase2) { | |
| Private->HoleData[1].Base = TempBase1 + TempSize1; | |
| Private->HoleData[1].Size = (UINTN)(TempBase2 - TempBase1 - TempSize1); | |
| } | |
| if (TempBase2 + TempSize2 < TemporaryRamBase + TemporaryRamSize) { | |
| Private->HoleData[2].Base = TempBase2 + TempSize2; | |
| Private->HoleData[2].Size = (UINTN)(TemporaryRamBase + TemporaryRamSize - TempBase2 - TempSize2); | |
| } | |
| // | |
| // Copy Hole Range data. | |
| // | |
| for (Index = 0; Index < HOLE_MAX_NUMBER; Index++) { | |
| if (Private->HoleData[Index].Size > 0) { | |
| if (HoleMemBase > Private->HoleData[Index].Base) { | |
| Private->HoleData[Index].OffsetPositive = TRUE; | |
| Private->HoleData[Index].Offset = (UINTN)(HoleMemBase - Private->HoleData[Index].Base); | |
| } else { | |
| Private->HoleData[Index].OffsetPositive = FALSE; | |
| Private->HoleData[Index].Offset = (UINTN)(Private->HoleData[Index].Base - HoleMemBase); | |
| } | |
| CopyMem ((VOID *)(UINTN)HoleMemBase, (VOID *)(UINTN)Private->HoleData[Index].Base, Private->HoleData[Index].Size); | |
| HoleMemBase = HoleMemBase + Private->HoleData[Index].Size; | |
| } | |
| } | |
| } | |
| // | |
| // Switch new stack | |
| // | |
| SwitchStack ( | |
| (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiCoreEntry, | |
| (VOID *)SecCoreData, | |
| (VOID *)Private, | |
| (VOID *)(UINTN)TopOfNewStack | |
| ); | |
| } | |
| // | |
| // Code should not come here | |
| // | |
| ASSERT (FALSE); | |
| } | |
| } | |
| /** | |
| Migrate a PEIM from temporary RAM to permanent memory. | |
| @param PeimFileHandle Pointer to the FFS file header of the image. | |
| @param MigratedFileHandle Pointer to the FFS file header of the migrated image. | |
| @retval EFI_SUCCESS Successfully migrated the PEIM to permanent memory. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| MigratePeim ( | |
| IN EFI_PEI_FILE_HANDLE FileHandle, | |
| IN EFI_PEI_FILE_HANDLE MigratedFileHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_FFS_FILE_HEADER *FileHeader; | |
| VOID *Pe32Data; | |
| VOID *ImageAddress; | |
| CHAR8 *AsciiString; | |
| UINTN Index; | |
| Status = EFI_SUCCESS; | |
| FileHeader = (EFI_FFS_FILE_HEADER *)FileHandle; | |
| ASSERT (!IS_FFS_FILE2 (FileHeader)); | |
| ImageAddress = NULL; | |
| PeiGetPe32Data (MigratedFileHandle, &ImageAddress); | |
| if (ImageAddress != NULL) { | |
| DEBUG_CODE_BEGIN (); | |
| AsciiString = PeCoffLoaderGetPdbPointer (ImageAddress); | |
| for (Index = 0; AsciiString[Index] != 0; Index++) { | |
| if ((AsciiString[Index] == '\\') || (AsciiString[Index] == '/')) { | |
| AsciiString = AsciiString + Index + 1; | |
| Index = 0; | |
| } else if (AsciiString[Index] == '.') { | |
| AsciiString[Index] = 0; | |
| } | |
| } | |
| DEBUG ((DEBUG_VERBOSE, "%a", AsciiString)); | |
| DEBUG_CODE_END (); | |
| Pe32Data = (VOID *)((UINTN)ImageAddress - (UINTN)MigratedFileHandle + (UINTN)FileHandle); | |
| Status = LoadAndRelocatePeCoffImageInPlace (Pe32Data, ImageAddress); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Migrate Status Code Callback function pointers inside an FV from temporary memory to permanent memory. | |
| @param OrgFvHandle Address of FV handle in temporary memory. | |
| @param FvHandle Address of FV handle in permanent memory. | |
| @param FvSize Size of the FV. | |
| **/ | |
| VOID | |
| ConvertStatusCodeCallbacks ( | |
| IN UINTN OrgFvHandle, | |
| IN UINTN FvHandle, | |
| IN UINTN FvSize | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS Hob; | |
| UINTN *NumberOfEntries; | |
| UINTN *CallbackEntry; | |
| UINTN Index; | |
| Hob.Raw = GetFirstGuidHob (&gStatusCodeCallbackGuid); | |
| while (Hob.Raw != NULL) { | |
| NumberOfEntries = GET_GUID_HOB_DATA (Hob); | |
| CallbackEntry = NumberOfEntries + 1; | |
| for (Index = 0; Index < *NumberOfEntries; Index++) { | |
| if (((VOID *)CallbackEntry[Index]) != NULL) { | |
| if ((CallbackEntry[Index] >= OrgFvHandle) && (CallbackEntry[Index] < (OrgFvHandle + FvSize))) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "Migrating CallbackEntry[%Lu] from 0x%0*Lx to ", | |
| (UINT64)Index, | |
| (sizeof CallbackEntry[Index]) * 2, | |
| (UINT64)CallbackEntry[Index] | |
| )); | |
| if (OrgFvHandle > FvHandle) { | |
| CallbackEntry[Index] = CallbackEntry[Index] - (OrgFvHandle - FvHandle); | |
| } else { | |
| CallbackEntry[Index] = CallbackEntry[Index] + (FvHandle - OrgFvHandle); | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "0x%0*Lx\n", | |
| (sizeof CallbackEntry[Index]) * 2, | |
| (UINT64)CallbackEntry[Index] | |
| )); | |
| } | |
| } | |
| } | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| Hob.Raw = GetNextGuidHob (&gStatusCodeCallbackGuid, Hob.Raw); | |
| } | |
| } | |
| /** | |
| Migrates PEIMs in the given firmware volume. | |
| @param Private Pointer to the PeiCore's private data structure. | |
| @param FvIndex The firmware volume index to migrate. | |
| @param OrgFvHandle The handle to the firmware volume in temporary memory. | |
| @param FvHandle The handle to the firmware volume in permanent memory. | |
| @retval EFI_SUCCESS The PEIMs in the FV were migrated successfully | |
| @retval EFI_INVALID_PARAMETER The Private pointer is NULL or FvCount is invalid. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| MigratePeimsInFv ( | |
| IN PEI_CORE_INSTANCE *Private, | |
| IN UINTN FvIndex, | |
| IN UINTN OrgFvHandle, | |
| IN UINTN FvHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| volatile UINTN FileIndex; | |
| EFI_PEI_FILE_HANDLE MigratedFileHandle; | |
| EFI_PEI_FILE_HANDLE FileHandle; | |
| if ((Private == NULL) || (FvIndex >= Private->FvCount)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (Private->Fv[FvIndex].ScanFv) { | |
| for (FileIndex = 0; FileIndex < Private->Fv[FvIndex].PeimCount; FileIndex++) { | |
| if (Private->Fv[FvIndex].FvFileHandles[FileIndex] != NULL) { | |
| FileHandle = Private->Fv[FvIndex].FvFileHandles[FileIndex]; | |
| MigratedFileHandle = (EFI_PEI_FILE_HANDLE)((UINTN)FileHandle - OrgFvHandle + FvHandle); | |
| DEBUG ((DEBUG_VERBOSE, " Migrating FileHandle %2d ", FileIndex)); | |
| Status = MigratePeim (FileHandle, MigratedFileHandle); | |
| DEBUG ((DEBUG_VERBOSE, "\n")); | |
| ASSERT_EFI_ERROR (Status); | |
| if (!EFI_ERROR (Status)) { | |
| Private->Fv[FvIndex].FvFileHandles[FileIndex] = MigratedFileHandle; | |
| if (FvIndex == Private->CurrentPeimFvCount) { | |
| Private->CurrentFvFileHandles[FileIndex] = MigratedFileHandle; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Migrate FVs out of temporary RAM before the cache is flushed. | |
| @param Private PeiCore's private data structure | |
| @param SecCoreData Points to a data structure containing information about the PEI core's operating | |
| environment, such as the size and location of temporary RAM, the stack location and | |
| the BFV location. | |
| @retval EFI_SUCCESS Successfully migrated installed FVs from temporary RAM to permanent memory. | |
| @retval EFI_OUT_OF_RESOURCES Insufficient memory exists to allocate needed pages. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| EvacuateTempRam ( | |
| IN PEI_CORE_INSTANCE *Private, | |
| IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| volatile UINTN FvIndex; | |
| volatile UINTN FvChildIndex; | |
| UINTN ChildFvOffset; | |
| EFI_PHYSICAL_ADDRESS FvHeaderAddress; | |
| EFI_FIRMWARE_VOLUME_HEADER *FvHeader; | |
| EFI_FIRMWARE_VOLUME_HEADER *ChildFvHeader; | |
| EFI_FIRMWARE_VOLUME_HEADER *MigratedFvHeader; | |
| EFI_FIRMWARE_VOLUME_HEADER *RawDataFvHeader; | |
| EFI_FIRMWARE_VOLUME_HEADER *MigratedChildFvHeader; | |
| PEI_CORE_FV_HANDLE PeiCoreFvHandle; | |
| EFI_PEI_CORE_FV_LOCATION_PPI *PeiCoreFvLocationPpi; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EDKII_MIGRATION_INFO *MigrationInfo; | |
| TO_MIGRATE_FV_INFO *ToMigrateFvInfo; | |
| UINT32 FvMigrationFlags; | |
| EDKII_MIGRATED_FV_INFO MigratedFvInfo; | |
| UINTN Index; | |
| ASSERT (Private->PeiMemoryInstalled); | |
| DEBUG ((DEBUG_VERBOSE, "Beginning evacuation of content in temporary RAM.\n")); | |
| // | |
| // By default migrate all FVs and copy raw data | |
| // | |
| FvMigrationFlags = FLAGS_FV_RAW_DATA_COPY; | |
| // | |
| // Migrate PPI Pointers of PEI_CORE from temporary memory to newly loaded PEI_CORE in permanent memory. | |
| // | |
| Status = PeiLocatePpi ((CONST EFI_PEI_SERVICES **)&Private->Ps, &gEfiPeiCoreFvLocationPpiGuid, 0, NULL, (VOID **)&PeiCoreFvLocationPpi); | |
| if (!EFI_ERROR (Status) && (PeiCoreFvLocationPpi->PeiCoreFvLocation != NULL)) { | |
| PeiCoreFvHandle.FvHandle = (EFI_PEI_FV_HANDLE)PeiCoreFvLocationPpi->PeiCoreFvLocation; | |
| } else { | |
| PeiCoreFvHandle.FvHandle = (EFI_PEI_FV_HANDLE)SecCoreData->BootFirmwareVolumeBase; | |
| } | |
| if (Private->PeimDispatcherReenter) { | |
| // | |
| // PEI_CORE should be migrated after dispatcher re-enters from main memory. | |
| // | |
| for (FvIndex = 0; FvIndex < Private->FvCount; FvIndex++) { | |
| if (Private->Fv[FvIndex].FvHandle == PeiCoreFvHandle.FvHandle) { | |
| CopyMem (&PeiCoreFvHandle, &Private->Fv[FvIndex], sizeof (PEI_CORE_FV_HANDLE)); | |
| break; | |
| } | |
| } | |
| Status = EFI_SUCCESS; | |
| ConvertPeiCorePpiPointers (Private, &PeiCoreFvHandle); | |
| } | |
| Hob.Raw = GetFirstGuidHob (&gEdkiiMigrationInfoGuid); | |
| if (Hob.Raw != NULL) { | |
| MigrationInfo = GET_GUID_HOB_DATA (Hob); | |
| } else { | |
| MigrationInfo = NULL; | |
| } | |
| for (FvIndex = 0; FvIndex < Private->FvCount; FvIndex++) { | |
| FvHeader = Private->Fv[FvIndex].FvHeader; | |
| ASSERT (FvHeader != NULL); | |
| ASSERT (FvIndex < Private->FvCount); | |
| DEBUG ((DEBUG_VERBOSE, "FV[%02d] at 0x%x.\n", FvIndex, (UINTN)FvHeader)); | |
| if ( | |
| !( | |
| ((EFI_PHYSICAL_ADDRESS)(UINTN)FvHeader >= Private->PhysicalMemoryBegin) && | |
| (((EFI_PHYSICAL_ADDRESS)(UINTN)FvHeader + (FvHeader->FvLength - 1)) < Private->FreePhysicalMemoryTop) | |
| ) | |
| ) | |
| { | |
| if ((MigrationInfo == NULL) || (MigrationInfo->MigrateAll == TRUE)) { | |
| if (!Private->PeimDispatcherReenter) { | |
| // | |
| // Migration before dispatcher reentery is supported only when gEdkiiMigrationInfoGuid | |
| // HOB is built for selective FV migration. | |
| // | |
| return EFI_SUCCESS; | |
| } | |
| } else { | |
| for (Index = 0; Index < MigrationInfo->ToMigrateFvCount; Index++) { | |
| ToMigrateFvInfo = ((TO_MIGRATE_FV_INFO *)(MigrationInfo + 1)) + Index; | |
| if (ToMigrateFvInfo->FvOrgBaseOnTempRam == (UINT32)(UINTN)FvHeader) { | |
| // | |
| // This FV is to migrate | |
| // | |
| FvMigrationFlags = ToMigrateFvInfo->FvMigrationFlags; | |
| break; | |
| } | |
| } | |
| if ((Index == MigrationInfo->ToMigrateFvCount) || | |
| ((!Private->PeimDispatcherReenter) && | |
| (((FvMigrationFlags & FLAGS_FV_MIGRATE_BEFORE_PEI_CORE_REENTRY) == 0) || | |
| (FvHeader == PeiCoreFvHandle.FvHandle)))) | |
| { | |
| // | |
| // This FV is not expected to migrate | |
| // | |
| // FV should not be migrated before dispatcher reentry if any of the below condition is true: | |
| // a. MigrationInfo HOB is not built with flag FLAGS_FV_MIGRATE_BEFORE_PEI_CORE_REENTRY. | |
| // b. FV contains currently executing PEI Core. | |
| // | |
| continue; | |
| } | |
| } | |
| // | |
| // Allocate pages to save the rebased PEIMs, the PEIMs will get dispatched later. | |
| // | |
| Status = PeiServicesAllocatePages ( | |
| EfiBootServicesCode, | |
| EFI_SIZE_TO_PAGES ((UINTN)FvHeader->FvLength), | |
| &FvHeaderAddress | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| MigratedFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvHeaderAddress; | |
| CopyMem (MigratedFvHeader, FvHeader, (UINTN)FvHeader->FvLength); | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| " Migrating FV[%d] from 0x%08X to 0x%08X\n", | |
| FvIndex, | |
| (UINTN)FvHeader, | |
| (UINTN)MigratedFvHeader | |
| )); | |
| // | |
| // Create hob to save MigratedFvInfo, this hob will only be produced when | |
| // Migration feature PCD PcdMigrateTemporaryRamFirmwareVolumes is set to TRUE. | |
| // | |
| MigratedFvInfo.FvOrgBase = (UINT32)(UINTN)FvHeader; | |
| MigratedFvInfo.FvNewBase = (UINT32)(UINTN)MigratedFvHeader; | |
| MigratedFvInfo.FvDataBase = 0; | |
| MigratedFvInfo.FvLength = (UINT32)(UINTN)FvHeader->FvLength; | |
| // | |
| // When FLAGS_FV_RAW_DATA_COPY bit is set, copy the context to the raw pages and | |
| // reset raw data base address in MigratedFvInfo hob. | |
| // | |
| if ((FvMigrationFlags & FLAGS_FV_RAW_DATA_COPY) == FLAGS_FV_RAW_DATA_COPY) { | |
| // | |
| // Allocate pages to save the raw PEIMs | |
| // | |
| Status = PeiServicesAllocatePages ( | |
| EfiBootServicesCode, | |
| EFI_SIZE_TO_PAGES ((UINTN)FvHeader->FvLength), | |
| &FvHeaderAddress | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| RawDataFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvHeaderAddress; | |
| CopyMem (RawDataFvHeader, FvHeader, (UINTN)FvHeader->FvLength); | |
| MigratedFvInfo.FvDataBase = (UINT32)(UINTN)RawDataFvHeader; | |
| } | |
| BuildGuidDataHob (&gEdkiiMigratedFvInfoGuid, &MigratedFvInfo, sizeof (MigratedFvInfo)); | |
| // | |
| // Migrate any children for this FV now | |
| // | |
| for (FvChildIndex = FvIndex; FvChildIndex < Private->FvCount; FvChildIndex++) { | |
| ChildFvHeader = Private->Fv[FvChildIndex].FvHeader; | |
| if ( | |
| ((UINTN)ChildFvHeader > (UINTN)FvHeader) && | |
| (((UINTN)ChildFvHeader + ChildFvHeader->FvLength) < ((UINTN)FvHeader) + FvHeader->FvLength) | |
| ) | |
| { | |
| DEBUG ((DEBUG_VERBOSE, " Child FV[%02d] is being migrated.\n", FvChildIndex)); | |
| ChildFvOffset = (UINTN)ChildFvHeader - (UINTN)FvHeader; | |
| DEBUG ((DEBUG_VERBOSE, " Child FV offset = 0x%x.\n", ChildFvOffset)); | |
| MigratedChildFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)MigratedFvHeader + ChildFvOffset); | |
| Private->Fv[FvChildIndex].FvHeader = MigratedChildFvHeader; | |
| Private->Fv[FvChildIndex].FvHandle = (EFI_PEI_FV_HANDLE)MigratedChildFvHeader; | |
| DEBUG ((DEBUG_VERBOSE, " Child migrated FV header at 0x%x.\n", (UINTN)MigratedChildFvHeader)); | |
| Status = MigratePeimsInFv (Private, FvChildIndex, (UINTN)ChildFvHeader, (UINTN)MigratedChildFvHeader); | |
| ASSERT_EFI_ERROR (Status); | |
| ConvertPpiPointersFv ( | |
| Private, | |
| (UINTN)ChildFvHeader, | |
| (UINTN)MigratedChildFvHeader, | |
| (UINTN)ChildFvHeader->FvLength - 1 | |
| ); | |
| ConvertStatusCodeCallbacks ( | |
| (UINTN)ChildFvHeader, | |
| (UINTN)MigratedChildFvHeader, | |
| (UINTN)ChildFvHeader->FvLength - 1 | |
| ); | |
| ConvertFvHob (Private, (UINTN)ChildFvHeader, (UINTN)MigratedChildFvHeader); | |
| } | |
| } | |
| Private->Fv[FvIndex].FvHeader = MigratedFvHeader; | |
| Private->Fv[FvIndex].FvHandle = (EFI_PEI_FV_HANDLE)MigratedFvHeader; | |
| Status = MigratePeimsInFv (Private, FvIndex, (UINTN)FvHeader, (UINTN)MigratedFvHeader); | |
| ASSERT_EFI_ERROR (Status); | |
| ConvertPpiPointersFv ( | |
| Private, | |
| (UINTN)FvHeader, | |
| (UINTN)MigratedFvHeader, | |
| (UINTN)FvHeader->FvLength - 1 | |
| ); | |
| ConvertStatusCodeCallbacks ( | |
| (UINTN)FvHeader, | |
| (UINTN)MigratedFvHeader, | |
| (UINTN)FvHeader->FvLength - 1 | |
| ); | |
| ConvertFvHob (Private, (UINTN)FvHeader, (UINTN)MigratedFvHeader); | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Conduct PEIM dispatch. | |
| @param SecCoreData Points to a data structure containing information about the PEI core's operating | |
| environment, such as the size and location of temporary RAM, the stack location and | |
| the BFV location. | |
| @param Private Pointer to the private data passed in from caller | |
| **/ | |
| VOID | |
| PeiDispatcher ( | |
| IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData, | |
| IN PEI_CORE_INSTANCE *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 Index1; | |
| UINT32 Index2; | |
| CONST EFI_PEI_SERVICES **PeiServices; | |
| EFI_PEI_FILE_HANDLE PeimFileHandle; | |
| UINTN FvCount; | |
| UINTN PeimCount; | |
| UINT32 AuthenticationState; | |
| EFI_PHYSICAL_ADDRESS EntryPoint; | |
| EFI_PEIM_ENTRY_POINT2 PeimEntryPoint; | |
| UINTN SaveCurrentPeimCount; | |
| UINTN SaveCurrentFvCount; | |
| EFI_PEI_FILE_HANDLE SaveCurrentFileHandle; | |
| EFI_FV_FILE_INFO FvFileInfo; | |
| PEI_CORE_FV_HANDLE *CoreFvHandle; | |
| EFI_HOB_GUID_TYPE *GuidHob; | |
| UINT32 TableSize; | |
| PeiServices = (CONST EFI_PEI_SERVICES **)&Private->Ps; | |
| PeimEntryPoint = NULL; | |
| PeimFileHandle = NULL; | |
| EntryPoint = 0; | |
| if (Private->DelayedDispatchTable == NULL) { | |
| GuidHob = GetFirstGuidHob (&gEfiDelayedDispatchTableGuid); | |
| if (GuidHob != NULL) { | |
| Private->DelayedDispatchTable = (DELAYED_DISPATCH_TABLE *)(GET_GUID_HOB_DATA (GuidHob)); | |
| } else { | |
| TableSize = sizeof (DELAYED_DISPATCH_TABLE) + (PcdGet32 (PcdDelayedDispatchMaxEntries) * sizeof (DELAYED_DISPATCH_ENTRY)); | |
| Private->DelayedDispatchTable = BuildGuidHob (&gEfiDelayedDispatchTableGuid, TableSize); | |
| if (Private->DelayedDispatchTable != NULL) { | |
| ZeroMem (Private->DelayedDispatchTable, TableSize); | |
| Status = PeiServicesInstallPpi (&mDelayedDispatchDesc); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a Failed to install Delayed Dispatch PPI: %r!\n", __func__, Status)); | |
| ASSERT_EFI_ERROR (Status); | |
| } else { | |
| Status = PeiServicesNotifyPpi (&mDelayedDispatchNotifyDesc); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a Failed to notify Delayed Dispatch on End of Pei: %r!\n", __func__, Status)); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if ((Private->PeiMemoryInstalled) && | |
| (PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) || | |
| (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) || | |
| PcdGetBool (PcdShadowPeimOnS3Boot)) | |
| ) | |
| { | |
| // | |
| // Once real memory is available, shadow the RegisterForShadow modules. And meanwhile | |
| // update the modules' status from PEIM_STATE_REGISTER_FOR_SHADOW to PEIM_STATE_DONE. | |
| // | |
| SaveCurrentPeimCount = Private->CurrentPeimCount; | |
| SaveCurrentFvCount = Private->CurrentPeimFvCount; | |
| SaveCurrentFileHandle = Private->CurrentFileHandle; | |
| for (Index1 = 0; Index1 < Private->FvCount; Index1++) { | |
| for (Index2 = 0; Index2 < Private->Fv[Index1].PeimCount; Index2++) { | |
| if (Private->Fv[Index1].PeimState[Index2] == PEIM_STATE_REGISTER_FOR_SHADOW) { | |
| PeimFileHandle = Private->Fv[Index1].FvFileHandles[Index2]; | |
| Private->CurrentFileHandle = PeimFileHandle; | |
| Private->CurrentPeimFvCount = Index1; | |
| Private->CurrentPeimCount = Index2; | |
| Status = PeiLoadImage ( | |
| (CONST EFI_PEI_SERVICES **)&Private->Ps, | |
| PeimFileHandle, | |
| PEIM_STATE_REGISTER_FOR_SHADOW, | |
| &EntryPoint, | |
| &AuthenticationState | |
| ); | |
| if (Status == EFI_SUCCESS) { | |
| // | |
| // PEIM_STATE_REGISTER_FOR_SHADOW move to PEIM_STATE_DONE | |
| // | |
| Private->Fv[Index1].PeimState[Index2]++; | |
| // | |
| // Call the PEIM entry point | |
| // | |
| PeimEntryPoint = (EFI_PEIM_ENTRY_POINT2)(UINTN)EntryPoint; | |
| PERF_START_IMAGE_BEGIN (PeimFileHandle); | |
| PeimEntryPoint (PeimFileHandle, (const EFI_PEI_SERVICES **)&Private->Ps); | |
| PERF_START_IMAGE_END (PeimFileHandle); | |
| } | |
| // | |
| // Process the Notify list and dispatch any notifies for | |
| // newly installed PPIs. | |
| // | |
| ProcessDispatchNotifyList (Private); | |
| } | |
| } | |
| } | |
| Private->CurrentFileHandle = SaveCurrentFileHandle; | |
| Private->CurrentPeimFvCount = SaveCurrentFvCount; | |
| Private->CurrentPeimCount = SaveCurrentPeimCount; | |
| } | |
| // | |
| // This is the main dispatch loop. It will search known FVs for PEIMs and | |
| // attempt to dispatch them. If any PEIM gets dispatched through a single | |
| // pass of the dispatcher, it will start over from the BFV again to see | |
| // if any new PEIMs dependencies got satisfied. With a well ordered | |
| // FV where PEIMs are found in the order their dependencies are also | |
| // satisfied, this dispatcher should run only once. | |
| // | |
| do { | |
| // | |
| // In case that reenter PeiCore happens, the last pass record is still available. | |
| // | |
| if (!Private->PeimDispatcherReenter) { | |
| Private->PeimNeedingDispatch = FALSE; | |
| Private->PeimDispatchOnThisPass = FALSE; | |
| } else { | |
| Private->PeimDispatcherReenter = FALSE; | |
| } | |
| for (FvCount = Private->CurrentPeimFvCount; FvCount < Private->FvCount; FvCount++) { | |
| CoreFvHandle = FindNextCoreFvHandle (Private, FvCount); | |
| ASSERT (CoreFvHandle != NULL); | |
| // | |
| // If the FV has corresponding EFI_PEI_FIRMWARE_VOLUME_PPI instance, then dispatch it. | |
| // | |
| if (CoreFvHandle->FvPpi == NULL) { | |
| continue; | |
| } | |
| Private->CurrentPeimFvCount = FvCount; | |
| if (Private->CurrentPeimCount == 0) { | |
| // | |
| // When going through each FV, at first, search Apriori file to | |
| // reorder all PEIMs to ensure the PEIMs in Apriori file to get | |
| // dispatch at first. | |
| // | |
| DiscoverPeimsAndOrderWithApriori (Private, CoreFvHandle); | |
| } | |
| // | |
| // Start to dispatch all modules within the current FV. | |
| // | |
| for (PeimCount = Private->CurrentPeimCount; | |
| PeimCount < Private->Fv[FvCount].PeimCount; | |
| PeimCount++) | |
| { | |
| Private->CurrentPeimCount = PeimCount; | |
| PeimFileHandle = Private->CurrentFileHandle = Private->CurrentFvFileHandles[PeimCount]; | |
| if (Private->Fv[FvCount].PeimState[PeimCount] == PEIM_STATE_NOT_DISPATCHED) { | |
| if (!DepexSatisfied (Private, PeimFileHandle, PeimCount)) { | |
| Private->PeimNeedingDispatch = TRUE; | |
| } else { | |
| Status = CoreFvHandle->FvPpi->GetFileInfo (CoreFvHandle->FvPpi, PeimFileHandle, &FvFileInfo); | |
| ASSERT_EFI_ERROR (Status); | |
| if (FvFileInfo.FileType == EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE) { | |
| // | |
| // For FV type file, Produce new FvInfo PPI and FV HOB | |
| // | |
| Status = ProcessFvFile (Private, &Private->Fv[FvCount], PeimFileHandle); | |
| if (Status == EFI_SUCCESS) { | |
| // | |
| // PEIM_STATE_NOT_DISPATCHED move to PEIM_STATE_DISPATCHED | |
| // | |
| Private->Fv[FvCount].PeimState[PeimCount]++; | |
| Private->PeimDispatchOnThisPass = TRUE; | |
| } else { | |
| // | |
| // The related GuidedSectionExtraction/Decompress PPI for the | |
| // encapsulated FV image section may be installed in the rest | |
| // of this do-while loop, so need to make another pass. | |
| // | |
| Private->PeimNeedingDispatch = TRUE; | |
| } | |
| } else { | |
| // | |
| // For PEIM driver, Load its entry point | |
| // | |
| Status = PeiLoadImage ( | |
| PeiServices, | |
| PeimFileHandle, | |
| PEIM_STATE_NOT_DISPATCHED, | |
| &EntryPoint, | |
| &AuthenticationState | |
| ); | |
| if (Status == EFI_SUCCESS) { | |
| // | |
| // The PEIM has its dependencies satisfied, and its entry point | |
| // has been found, so invoke it. | |
| // | |
| PERF_START_IMAGE_BEGIN (PeimFileHandle); | |
| REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( | |
| EFI_PROGRESS_CODE, | |
| (EFI_SOFTWARE_PEI_CORE | EFI_SW_PC_INIT_BEGIN), | |
| (VOID *)(&PeimFileHandle), | |
| sizeof (PeimFileHandle) | |
| ); | |
| Status = VerifyPeim (Private, CoreFvHandle->FvHandle, PeimFileHandle, AuthenticationState); | |
| if (Status != EFI_SECURITY_VIOLATION) { | |
| // | |
| // PEIM_STATE_NOT_DISPATCHED move to PEIM_STATE_DISPATCHED | |
| // | |
| Private->Fv[FvCount].PeimState[PeimCount]++; | |
| // | |
| // Call the PEIM entry point for PEIM driver | |
| // | |
| PeimEntryPoint = (EFI_PEIM_ENTRY_POINT2)(UINTN)EntryPoint; | |
| PeimEntryPoint (PeimFileHandle, (const EFI_PEI_SERVICES **)PeiServices); | |
| Private->PeimDispatchOnThisPass = TRUE; | |
| } else { | |
| // | |
| // The related GuidedSectionExtraction PPI for the | |
| // signed PEIM image section may be installed in the rest | |
| // of this do-while loop, so need to make another pass. | |
| // | |
| Private->PeimNeedingDispatch = TRUE; | |
| } | |
| REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( | |
| EFI_PROGRESS_CODE, | |
| (EFI_SOFTWARE_PEI_CORE | EFI_SW_PC_INIT_END), | |
| (VOID *)(&PeimFileHandle), | |
| sizeof (PeimFileHandle) | |
| ); | |
| PERF_START_IMAGE_END (PeimFileHandle); | |
| } | |
| } | |
| PeiCheckAndSwitchStack (SecCoreData, Private); | |
| // | |
| // Process the Notify list and dispatch any notifies for | |
| // newly installed PPIs. | |
| // | |
| ProcessDispatchNotifyList (Private); | |
| // | |
| // Recheck SwitchStackSignal after ProcessDispatchNotifyList() | |
| // in case PeiInstallPeiMemory() is done in a callback with | |
| // EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH. | |
| // | |
| PeiCheckAndSwitchStack (SecCoreData, Private); | |
| if ((Private->PeiMemoryInstalled) && (Private->Fv[FvCount].PeimState[PeimCount] == PEIM_STATE_REGISTER_FOR_SHADOW) && \ | |
| (PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) || | |
| (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) || | |
| PcdGetBool (PcdShadowPeimOnS3Boot)) | |
| ) | |
| { | |
| // | |
| // If memory is available we shadow images by default for performance reasons. | |
| // We call the entry point a 2nd time so the module knows it's shadowed. | |
| // | |
| // PERF_START (PeiServices, L"PEIM", PeimFileHandle, 0); | |
| if ((Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) && !PcdGetBool (PcdShadowPeimOnBoot) && | |
| !PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes)) | |
| { | |
| // | |
| // Load PEIM into Memory for Register for shadow PEIM. | |
| // | |
| Status = PeiLoadImage ( | |
| PeiServices, | |
| PeimFileHandle, | |
| PEIM_STATE_REGISTER_FOR_SHADOW, | |
| &EntryPoint, | |
| &AuthenticationState | |
| ); | |
| if (Status == EFI_SUCCESS) { | |
| PeimEntryPoint = (EFI_PEIM_ENTRY_POINT2)(UINTN)EntryPoint; | |
| } | |
| } | |
| ASSERT (PeimEntryPoint != NULL); | |
| PeimEntryPoint (PeimFileHandle, (const EFI_PEI_SERVICES **)PeiServices); | |
| // PERF_END (PeiServices, L"PEIM", PeimFileHandle, 0); | |
| // | |
| // PEIM_STATE_REGISTER_FOR_SHADOW move to PEIM_STATE_DONE | |
| // | |
| Private->Fv[FvCount].PeimState[PeimCount]++; | |
| // | |
| // Process the Notify list and dispatch any notifies for | |
| // newly installed PPIs. | |
| // | |
| ProcessDispatchNotifyList (Private); | |
| } | |
| } | |
| } | |
| // Dispatch pending delalyed dispatch requests | |
| if (Private->DelayedDispatchTable != NULL) { | |
| if (DelayedDispatchDispatcher (Private->DelayedDispatchTable, NULL)) { | |
| ProcessDispatchNotifyList (Private); | |
| } | |
| } | |
| } | |
| // | |
| // Before walking through the next FV, we should set them to NULL/0 to | |
| // start at the beginning of the next FV. | |
| // | |
| Private->CurrentFileHandle = NULL; | |
| Private->CurrentPeimCount = 0; | |
| Private->CurrentFvFileHandles = NULL; | |
| Private->AprioriCount = 0; | |
| } | |
| // | |
| // Before making another pass, we should set it to 0 to | |
| // go through all the FVs. | |
| // | |
| Private->CurrentPeimFvCount = 0; | |
| // | |
| // PeimNeedingDispatch being TRUE means we found a PEIM/FV that did not get | |
| // dispatched. So we need to make another pass | |
| // | |
| // PeimDispatchOnThisPass being TRUE means we dispatched a PEIM/FV on this | |
| // pass. If we did not dispatch a PEIM/FV there is no point in trying again | |
| // as it will fail the next time too (nothing has changed). | |
| // | |
| // Also continue dispatch loop if there are outstanding delay- | |
| // dispatch registrations still running. | |
| } while ((Private->PeimNeedingDispatch && Private->PeimDispatchOnThisPass) || | |
| (Private->DelayedDispatchTable->Count > 0)); | |
| } | |
| /** | |
| Initialize the Dispatcher's data members | |
| @param PrivateData PeiCore's private data structure | |
| @param OldCoreData Old data from SecCore | |
| NULL if being run in non-permanent memory mode. | |
| @param SecCoreData Points to a data structure containing information about the PEI core's operating | |
| environment, such as the size and location of temporary RAM, the stack location and | |
| the BFV location. | |
| @return None. | |
| **/ | |
| VOID | |
| InitializeDispatcherData ( | |
| IN PEI_CORE_INSTANCE *PrivateData, | |
| IN PEI_CORE_INSTANCE *OldCoreData, | |
| IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData | |
| ) | |
| { | |
| if (OldCoreData == NULL) { | |
| PrivateData->PeimDispatcherReenter = FALSE; | |
| PeiInitializeFv (PrivateData, SecCoreData); | |
| } else { | |
| PeiReinitializeFv (PrivateData); | |
| } | |
| return; | |
| } | |
| /** | |
| This routine parses the Dependency Expression, if available, and | |
| decides if the module can be executed. | |
| @param Private PeiCore's private data structure | |
| @param FileHandle PEIM's file handle | |
| @param PeimCount Peim count in all dispatched PEIMs. | |
| @retval TRUE Can be dispatched | |
| @retval FALSE Cannot be dispatched | |
| **/ | |
| BOOLEAN | |
| DepexSatisfied ( | |
| IN PEI_CORE_INSTANCE *Private, | |
| IN EFI_PEI_FILE_HANDLE FileHandle, | |
| IN UINTN PeimCount | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VOID *DepexData; | |
| EFI_FV_FILE_INFO FileInfo; | |
| Status = PeiServicesFfsGetFileInfo (FileHandle, &FileInfo); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_DISPATCH, "Evaluate PEI DEPEX for FFS(Unknown)\n")); | |
| } else { | |
| DEBUG ((DEBUG_DISPATCH, "Evaluate PEI DEPEX for FFS(%g)\n", &FileInfo.FileName)); | |
| } | |
| if (PeimCount < Private->AprioriCount) { | |
| // | |
| // If it's in the Apriori file then we set DEPEX to TRUE | |
| // | |
| DEBUG ((DEBUG_DISPATCH, " RESULT = TRUE (Apriori)\n")); | |
| return TRUE; | |
| } | |
| // | |
| // Depex section not in the encapsulated section. | |
| // | |
| Status = PeiServicesFfsFindSectionData ( | |
| EFI_SECTION_PEI_DEPEX, | |
| FileHandle, | |
| (VOID **)&DepexData | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // If there is no DEPEX, assume the module can be executed | |
| // | |
| DEBUG ((DEBUG_DISPATCH, " RESULT = TRUE (No DEPEX)\n")); | |
| return TRUE; | |
| } | |
| // | |
| // Evaluate a given DEPEX | |
| // | |
| return PeimDispatchReadiness (&Private->Ps, DepexData); | |
| } | |
| /** | |
| This routine enables a PEIM to register itself for shadow when the PEI Foundation | |
| discovers permanent memory. | |
| @param FileHandle File handle of a PEIM. | |
| @retval EFI_NOT_FOUND The file handle doesn't point to PEIM itself. | |
| @retval EFI_ALREADY_STARTED Indicate that the PEIM has been registered itself. | |
| @retval EFI_SUCCESS Successfully to register itself. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiRegisterForShadow ( | |
| IN EFI_PEI_FILE_HANDLE FileHandle | |
| ) | |
| { | |
| PEI_CORE_INSTANCE *Private; | |
| Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); | |
| if (Private->CurrentFileHandle != FileHandle) { | |
| // | |
| // The FileHandle must be for the current PEIM | |
| // | |
| return EFI_NOT_FOUND; | |
| } | |
| if (Private->Fv[Private->CurrentPeimFvCount].PeimState[Private->CurrentPeimCount] >= PEIM_STATE_REGISTER_FOR_SHADOW) { | |
| // | |
| // If the PEIM has already entered the PEIM_STATE_REGISTER_FOR_SHADOW or PEIM_STATE_DONE then it's already been started | |
| // | |
| return EFI_ALREADY_STARTED; | |
| } | |
| Private->Fv[Private->CurrentPeimFvCount].PeimState[Private->CurrentPeimCount] = PEIM_STATE_REGISTER_FOR_SHADOW; | |
| return EFI_SUCCESS; | |
| } |