| /** @file | |
| EFI PEI Core memory services | |
| Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "PeiMain.h" | |
| /** | |
| Initialize the memory services. | |
| @param PrivateData Points to PeiCore's private instance data. | |
| @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 OldCoreData Pointer to the PEI Core data. | |
| NULL if being run in non-permanent memory mode. | |
| **/ | |
| VOID | |
| InitializeMemoryServices ( | |
| IN PEI_CORE_INSTANCE *PrivateData, | |
| IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData, | |
| IN PEI_CORE_INSTANCE *OldCoreData | |
| ) | |
| { | |
| PrivateData->SwitchStackSignal = FALSE; | |
| // | |
| // First entering PeiCore, following code will initialized some field | |
| // in PeiCore's private data according to hand off data from SEC core. | |
| // | |
| if (OldCoreData == NULL) { | |
| PrivateData->PeiMemoryInstalled = FALSE; | |
| PrivateData->HobList.Raw = SecCoreData->PeiTemporaryRamBase; | |
| PeiCoreBuildHobHandoffInfoTable ( | |
| BOOT_WITH_FULL_CONFIGURATION, | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)SecCoreData->PeiTemporaryRamBase, | |
| (UINTN)SecCoreData->PeiTemporaryRamSize | |
| ); | |
| // | |
| // Set Ps to point to ServiceTableShadow in Cache | |
| // | |
| PrivateData->Ps = &(PrivateData->ServiceTableShadow); | |
| } | |
| return; | |
| } | |
| /** | |
| This function registers the found memory configuration with the PEI Foundation. | |
| The usage model is that the PEIM that discovers the permanent memory shall invoke this service. | |
| This routine will hold discoveried memory information into PeiCore's private data, | |
| and set SwitchStackSignal flag. After PEIM who discovery memory is dispatched, | |
| PeiDispatcher will migrate temporary memory to permanent memory. | |
| @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
| @param MemoryBegin Start of memory address. | |
| @param MemoryLength Length of memory. | |
| @return EFI_SUCCESS Always success. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiInstallPeiMemory ( | |
| IN CONST EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PHYSICAL_ADDRESS MemoryBegin, | |
| IN UINT64 MemoryLength | |
| ) | |
| { | |
| PEI_CORE_INSTANCE *PrivateData; | |
| DEBUG ((DEBUG_INFO, "PeiInstallPeiMemory MemoryBegin 0x%LX, MemoryLength 0x%LX\n", MemoryBegin, MemoryLength)); | |
| PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); | |
| // | |
| // PEI_SERVICE.InstallPeiMemory should only be called one time during whole PEI phase. | |
| // If it is invoked more than one time, ASSERT information is given for developer debugging in debug tip and | |
| // simply return EFI_SUCCESS in release tip to ignore it. | |
| // | |
| if (PrivateData->PeiMemoryInstalled) { | |
| DEBUG ((DEBUG_ERROR, "ERROR: PeiInstallPeiMemory is called more than once!\n")); | |
| ASSERT (FALSE); | |
| return EFI_SUCCESS; | |
| } | |
| PrivateData->PhysicalMemoryBegin = MemoryBegin; | |
| PrivateData->PhysicalMemoryLength = MemoryLength; | |
| PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength; | |
| PrivateData->SwitchStackSignal = TRUE; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Migrate memory pages allocated in pre-memory phase. | |
| Copy memory pages at temporary heap top to permanent heap top. | |
| @param[in] Private Pointer to the private data passed in from caller. | |
| @param[in] TemporaryRamMigrated Temporary memory has been migrated to permanent memory. | |
| **/ | |
| VOID | |
| MigrateMemoryPages ( | |
| IN PEI_CORE_INSTANCE *Private, | |
| IN BOOLEAN TemporaryRamMigrated | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS NewMemPagesBase; | |
| EFI_PHYSICAL_ADDRESS MemPagesBase; | |
| Private->MemoryPages.Size = (UINTN)(Private->HobList.HandoffInformationTable->EfiMemoryTop - | |
| Private->HobList.HandoffInformationTable->EfiFreeMemoryTop); | |
| if (Private->MemoryPages.Size == 0) { | |
| // | |
| // No any memory page allocated in pre-memory phase. | |
| // | |
| return; | |
| } | |
| Private->MemoryPages.Base = Private->HobList.HandoffInformationTable->EfiFreeMemoryTop; | |
| ASSERT (Private->MemoryPages.Size <= Private->FreePhysicalMemoryTop); | |
| NewMemPagesBase = Private->FreePhysicalMemoryTop - Private->MemoryPages.Size; | |
| NewMemPagesBase &= ~(UINT64)EFI_PAGE_MASK; | |
| ASSERT (NewMemPagesBase >= Private->PhysicalMemoryBegin); | |
| // | |
| // Copy memory pages at temporary heap top to permanent heap top. | |
| // | |
| if (TemporaryRamMigrated) { | |
| // | |
| // Memory pages at temporary heap top has been migrated to permanent heap, | |
| // Here still needs to copy them from permanent heap to permanent heap top. | |
| // | |
| MemPagesBase = Private->MemoryPages.Base; | |
| if (Private->HeapOffsetPositive) { | |
| MemPagesBase += Private->HeapOffset; | |
| } else { | |
| MemPagesBase -= Private->HeapOffset; | |
| } | |
| CopyMem ((VOID *)(UINTN)NewMemPagesBase, (VOID *)(UINTN)MemPagesBase, Private->MemoryPages.Size); | |
| } else { | |
| CopyMem ((VOID *)(UINTN)NewMemPagesBase, (VOID *)(UINTN)Private->MemoryPages.Base, Private->MemoryPages.Size); | |
| } | |
| if (NewMemPagesBase >= Private->MemoryPages.Base) { | |
| Private->MemoryPages.OffsetPositive = TRUE; | |
| Private->MemoryPages.Offset = (UINTN)(NewMemPagesBase - Private->MemoryPages.Base); | |
| } else { | |
| Private->MemoryPages.OffsetPositive = FALSE; | |
| Private->MemoryPages.Offset = (UINTN)(Private->MemoryPages.Base - NewMemPagesBase); | |
| } | |
| DEBUG ((DEBUG_INFO, "Pages Offset = 0x%lX\n", (UINT64)Private->MemoryPages.Offset)); | |
| Private->FreePhysicalMemoryTop = NewMemPagesBase; | |
| } | |
| /** | |
| Migrate the base address in firmware volume allocation HOBs | |
| from temporary memory to PEI installed memory. | |
| @param[in] PrivateData Pointer to PeiCore's private data structure. | |
| @param[in] OrgFvHandle Address of FV Handle in temporary memory. | |
| @param[in] FvHandle Address of FV Handle in permanent memory. | |
| **/ | |
| VOID | |
| ConvertFvHob ( | |
| IN PEI_CORE_INSTANCE *PrivateData, | |
| IN UINTN OrgFvHandle, | |
| IN UINTN FvHandle | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_HOB_FIRMWARE_VOLUME *FirmwareVolumeHob; | |
| EFI_HOB_FIRMWARE_VOLUME2 *FirmwareVolume2Hob; | |
| EFI_HOB_FIRMWARE_VOLUME3 *FirmwareVolume3Hob; | |
| DEBUG ((DEBUG_INFO, "Converting FVs in FV HOB.\n")); | |
| for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { | |
| if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV) { | |
| FirmwareVolumeHob = Hob.FirmwareVolume; | |
| if (FirmwareVolumeHob->BaseAddress == OrgFvHandle) { | |
| FirmwareVolumeHob->BaseAddress = FvHandle; | |
| } | |
| } else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV2) { | |
| FirmwareVolume2Hob = Hob.FirmwareVolume2; | |
| if (FirmwareVolume2Hob->BaseAddress == OrgFvHandle) { | |
| FirmwareVolume2Hob->BaseAddress = FvHandle; | |
| } | |
| } else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV3) { | |
| FirmwareVolume3Hob = Hob.FirmwareVolume3; | |
| if (FirmwareVolume3Hob->BaseAddress == OrgFvHandle) { | |
| FirmwareVolume3Hob->BaseAddress = FvHandle; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Migrate MemoryBaseAddress in memory allocation HOBs | |
| from the temporary memory to PEI installed memory. | |
| @param[in] PrivateData Pointer to PeiCore's private data structure. | |
| **/ | |
| VOID | |
| ConvertMemoryAllocationHobs ( | |
| IN PEI_CORE_INSTANCE *PrivateData | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; | |
| EFI_PHYSICAL_ADDRESS OldMemPagesBase; | |
| UINTN OldMemPagesSize; | |
| if (PrivateData->MemoryPages.Size == 0) { | |
| // | |
| // No any memory page allocated in pre-memory phase. | |
| // | |
| return; | |
| } | |
| OldMemPagesBase = PrivateData->MemoryPages.Base; | |
| OldMemPagesSize = PrivateData->MemoryPages.Size; | |
| MemoryAllocationHob = NULL; | |
| Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); | |
| while (Hob.Raw != NULL) { | |
| MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; | |
| if ((MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress >= OldMemPagesBase) && | |
| (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress < (OldMemPagesBase + OldMemPagesSize)) | |
| ) | |
| { | |
| if (PrivateData->MemoryPages.OffsetPositive) { | |
| MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress += PrivateData->MemoryPages.Offset; | |
| } else { | |
| MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress -= PrivateData->MemoryPages.Offset; | |
| } | |
| } | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); | |
| } | |
| } | |
| /** | |
| Internal function to build a HOB for the memory allocation. | |
| It will search and reuse the unused(freed) memory allocation HOB, | |
| or build memory allocation HOB normally if no unused(freed) memory allocation HOB found. | |
| @param[in] BaseAddress The 64 bit physical address of the memory. | |
| @param[in] Length The length of the memory allocation in bytes. | |
| @param[in] MemoryType The type of memory allocated by this HOB. | |
| **/ | |
| VOID | |
| InternalBuildMemoryAllocationHob ( | |
| IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
| IN UINT64 Length, | |
| IN EFI_MEMORY_TYPE MemoryType | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; | |
| // | |
| // Search unused(freed) memory allocation HOB. | |
| // | |
| MemoryAllocationHob = NULL; | |
| Hob.Raw = GetFirstHob (EFI_HOB_TYPE_UNUSED); | |
| while (Hob.Raw != NULL) { | |
| if (Hob.Header->HobLength == sizeof (EFI_HOB_MEMORY_ALLOCATION)) { | |
| MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; | |
| break; | |
| } | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| Hob.Raw = GetNextHob (EFI_HOB_TYPE_UNUSED, Hob.Raw); | |
| } | |
| if (MemoryAllocationHob != NULL) { | |
| // | |
| // Reuse the unused(freed) memory allocation HOB. | |
| // | |
| MemoryAllocationHob->Header.HobType = EFI_HOB_TYPE_MEMORY_ALLOCATION; | |
| ZeroMem (&(MemoryAllocationHob->AllocDescriptor.Name), sizeof (EFI_GUID)); | |
| MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = BaseAddress; | |
| MemoryAllocationHob->AllocDescriptor.MemoryLength = Length; | |
| MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType; | |
| // | |
| // Zero the reserved space to match HOB spec | |
| // | |
| ZeroMem (MemoryAllocationHob->AllocDescriptor.Reserved, sizeof (MemoryAllocationHob->AllocDescriptor.Reserved)); | |
| } else { | |
| // | |
| // No unused(freed) memory allocation HOB found. | |
| // Build memory allocation HOB normally. | |
| // | |
| BuildMemoryAllocationHob ( | |
| BaseAddress, | |
| Length, | |
| MemoryType | |
| ); | |
| } | |
| } | |
| /** | |
| Update or split memory allocation HOB for memory pages allocate and free. | |
| @param[in, out] MemoryAllocationHob Pointer to the memory allocation HOB | |
| that needs to be updated or split. | |
| On output, it will be filled with | |
| the input Memory, Bytes and MemoryType. | |
| @param[in] Memory Memory to allocate or free. | |
| @param[in] Bytes Bytes to allocate or free. | |
| @param[in] MemoryType EfiConventionalMemory for pages free, | |
| others for pages allocate. | |
| **/ | |
| VOID | |
| UpdateOrSplitMemoryAllocationHob ( | |
| IN OUT EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob, | |
| IN EFI_PHYSICAL_ADDRESS Memory, | |
| IN UINT64 Bytes, | |
| IN EFI_MEMORY_TYPE MemoryType | |
| ) | |
| { | |
| if ((Memory + Bytes) < | |
| (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + MemoryAllocationHob->AllocDescriptor.MemoryLength)) | |
| { | |
| // | |
| // Last pages need to be split out. | |
| // | |
| InternalBuildMemoryAllocationHob ( | |
| Memory + Bytes, | |
| (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + MemoryAllocationHob->AllocDescriptor.MemoryLength) - (Memory + Bytes), | |
| MemoryAllocationHob->AllocDescriptor.MemoryType | |
| ); | |
| } | |
| if (Memory > MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress) { | |
| // | |
| // First pages need to be split out. | |
| // | |
| InternalBuildMemoryAllocationHob ( | |
| MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, | |
| Memory - MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, | |
| MemoryAllocationHob->AllocDescriptor.MemoryType | |
| ); | |
| } | |
| // | |
| // Update the memory allocation HOB. | |
| // | |
| MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = Memory; | |
| MemoryAllocationHob->AllocDescriptor.MemoryLength = Bytes; | |
| MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType; | |
| } | |
| /** | |
| Merge adjacent free memory ranges in memory allocation HOBs. | |
| @retval TRUE There are free memory ranges merged. | |
| @retval FALSE No free memory ranges merged. | |
| **/ | |
| BOOLEAN | |
| MergeFreeMemoryInMemoryAllocationHob ( | |
| VOID | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_PEI_HOB_POINTERS Hob2; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryHob; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryHob2; | |
| UINT64 Start; | |
| UINT64 End; | |
| BOOLEAN Merged; | |
| Merged = FALSE; | |
| Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); | |
| while (Hob.Raw != NULL) { | |
| if (Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) { | |
| MemoryHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; | |
| Start = MemoryHob->AllocDescriptor.MemoryBaseAddress; | |
| End = MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength; | |
| Hob2.Raw = GET_NEXT_HOB (Hob); | |
| Hob2.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); | |
| while (Hob2.Raw != NULL) { | |
| if (Hob2.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) { | |
| MemoryHob2 = (EFI_HOB_MEMORY_ALLOCATION *)Hob2.Raw; | |
| if (Start == (MemoryHob2->AllocDescriptor.MemoryBaseAddress + MemoryHob2->AllocDescriptor.MemoryLength)) { | |
| // | |
| // Merge adjacent two free memory ranges. | |
| // | |
| MemoryHob2->AllocDescriptor.MemoryLength += MemoryHob->AllocDescriptor.MemoryLength; | |
| Merged = TRUE; | |
| // | |
| // Mark MemoryHob to be unused(freed). | |
| // | |
| MemoryHob->Header.HobType = EFI_HOB_TYPE_UNUSED; | |
| break; | |
| } else if (End == MemoryHob2->AllocDescriptor.MemoryBaseAddress) { | |
| // | |
| // Merge adjacent two free memory ranges. | |
| // | |
| MemoryHob2->AllocDescriptor.MemoryBaseAddress = MemoryHob->AllocDescriptor.MemoryBaseAddress; | |
| MemoryHob2->AllocDescriptor.MemoryLength += MemoryHob->AllocDescriptor.MemoryLength; | |
| Merged = TRUE; | |
| // | |
| // Mark MemoryHob to be unused(freed). | |
| // | |
| MemoryHob->Header.HobType = EFI_HOB_TYPE_UNUSED; | |
| break; | |
| } | |
| } | |
| Hob2.Raw = GET_NEXT_HOB (Hob2); | |
| Hob2.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob2.Raw); | |
| } | |
| } | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); | |
| } | |
| return Merged; | |
| } | |
| /** | |
| Find free memory by searching memory allocation HOBs. | |
| @param[in] MemoryType The type of memory to allocate. | |
| @param[in] Pages The number of contiguous 4 KB pages to allocate. | |
| @param[in] Granularity Page allocation granularity. | |
| @param[out] Memory Pointer to a physical address. On output, the address is set to the base | |
| of the page range that was allocated. | |
| @retval EFI_SUCCESS The memory range was successfully allocated. | |
| @retval EFI_NOT_FOUND No memory allocation HOB with big enough free memory found. | |
| **/ | |
| EFI_STATUS | |
| FindFreeMemoryFromMemoryAllocationHob ( | |
| IN EFI_MEMORY_TYPE MemoryType, | |
| IN UINTN Pages, | |
| IN UINTN Granularity, | |
| OUT EFI_PHYSICAL_ADDRESS *Memory | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; | |
| UINT64 Bytes; | |
| EFI_PHYSICAL_ADDRESS BaseAddress; | |
| Bytes = LShiftU64 (Pages, EFI_PAGE_SHIFT); | |
| BaseAddress = 0; | |
| MemoryAllocationHob = NULL; | |
| Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); | |
| while (Hob.Raw != NULL) { | |
| if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) && | |
| (Hob.MemoryAllocation->AllocDescriptor.MemoryLength >= Bytes)) | |
| { | |
| // | |
| // Found one memory allocation HOB with big enough free memory. | |
| // | |
| MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; | |
| BaseAddress = MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + | |
| MemoryAllocationHob->AllocDescriptor.MemoryLength - Bytes; | |
| // | |
| // Make sure the granularity could be satisfied. | |
| // | |
| BaseAddress &= ~((EFI_PHYSICAL_ADDRESS)Granularity - 1); | |
| if (BaseAddress >= MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress) { | |
| break; | |
| } | |
| BaseAddress = 0; | |
| MemoryAllocationHob = NULL; | |
| } | |
| // | |
| // Continue to find. | |
| // | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); | |
| } | |
| if (MemoryAllocationHob != NULL) { | |
| UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, BaseAddress, Bytes, MemoryType); | |
| *Memory = BaseAddress; | |
| return EFI_SUCCESS; | |
| } else { | |
| if (MergeFreeMemoryInMemoryAllocationHob ()) { | |
| // | |
| // Retry if there are free memory ranges merged. | |
| // | |
| return FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory); | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| /** | |
| The purpose of the service is to publish an interface that allows | |
| PEIMs to allocate memory ranges that are managed by the PEI Foundation. | |
| Prior to InstallPeiMemory() being called, PEI will allocate pages from the heap. | |
| After InstallPeiMemory() is called, PEI will allocate pages within the region | |
| of memory provided by InstallPeiMemory() service in a best-effort fashion. | |
| Location-specific allocations are not managed by the PEI foundation code. | |
| @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
| @param MemoryType The type of memory to allocate. | |
| @param Pages The number of contiguous 4 KB pages to allocate. | |
| @param Memory Pointer to a physical address. On output, the address is set to the base | |
| of the page range that was allocated. | |
| @retval EFI_SUCCESS The memory range was successfully allocated. | |
| @retval EFI_OUT_OF_RESOURCES The pages could not be allocated. | |
| @retval EFI_INVALID_PARAMETER Type is not equal to EfiLoaderCode, EfiLoaderData, EfiRuntimeServicesCode, | |
| EfiRuntimeServicesData, EfiBootServicesCode, EfiBootServicesData, | |
| EfiACPIReclaimMemory, EfiReservedMemoryType, or EfiACPIMemoryNVS. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiAllocatePages ( | |
| IN CONST EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_MEMORY_TYPE MemoryType, | |
| IN UINTN Pages, | |
| OUT EFI_PHYSICAL_ADDRESS *Memory | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| PEI_CORE_INSTANCE *PrivateData; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_PHYSICAL_ADDRESS *FreeMemoryTop; | |
| EFI_PHYSICAL_ADDRESS *FreeMemoryBottom; | |
| UINTN RemainingPages; | |
| UINTN RemainingMemory; | |
| UINTN Granularity; | |
| UINTN Padding; | |
| if ((MemoryType != EfiLoaderCode) && | |
| (MemoryType != EfiLoaderData) && | |
| (MemoryType != EfiRuntimeServicesCode) && | |
| (MemoryType != EfiRuntimeServicesData) && | |
| (MemoryType != EfiBootServicesCode) && | |
| (MemoryType != EfiBootServicesData) && | |
| (MemoryType != EfiACPIReclaimMemory) && | |
| (MemoryType != EfiReservedMemoryType) && | |
| (MemoryType != EfiACPIMemoryNVS)) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; | |
| PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); | |
| Hob.Raw = PrivateData->HobList.Raw; | |
| if (Hob.Raw == NULL) { | |
| // | |
| // HOB is not initialized yet. | |
| // | |
| return EFI_NOT_AVAILABLE_YET; | |
| } | |
| if ((RUNTIME_PAGE_ALLOCATION_GRANULARITY > DEFAULT_PAGE_ALLOCATION_GRANULARITY) && | |
| ((MemoryType == EfiReservedMemoryType) || | |
| (MemoryType == EfiACPIMemoryNVS) || | |
| (MemoryType == EfiRuntimeServicesCode) || | |
| (MemoryType == EfiRuntimeServicesData))) | |
| { | |
| Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "AllocatePages: aligning allocation to %d KB\n", | |
| Granularity / SIZE_1KB | |
| )); | |
| } | |
| if (!PrivateData->PeiMemoryInstalled && PrivateData->SwitchStackSignal) { | |
| // | |
| // When PeiInstallMemory is called but temporary memory has *not* been moved to permanent memory, | |
| // the AllocatePage will depend on the field of PEI_CORE_INSTANCE structure. | |
| // | |
| FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop); | |
| FreeMemoryBottom = &(PrivateData->PhysicalMemoryBegin); | |
| } else { | |
| FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); | |
| FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom); | |
| } | |
| // | |
| // Check to see if on correct boundary for the memory type. | |
| // If not aligned, make the allocation aligned and that we are not trying to allocate page 0, which is used for | |
| // null detection. | |
| // | |
| Padding = *(FreeMemoryTop) & (Granularity - 1); | |
| if (((UINTN)(*FreeMemoryTop - *FreeMemoryBottom) < Padding) || (*(FreeMemoryTop) - Padding == 0)) { | |
| DEBUG ((DEBUG_ERROR, "AllocatePages failed: Out of space after padding.\n")); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| *(FreeMemoryTop) -= Padding; | |
| if (Padding >= EFI_PAGE_SIZE) { | |
| // | |
| // Create a memory allocation HOB to cover | |
| // the pages that we will lose to rounding | |
| // | |
| InternalBuildMemoryAllocationHob ( | |
| *(FreeMemoryTop), | |
| Padding & ~(UINTN)EFI_PAGE_MASK, | |
| EfiConventionalMemory | |
| ); | |
| } | |
| // | |
| // Verify that there is sufficient memory to satisfy the allocation. | |
| // | |
| RemainingMemory = (UINTN)(*FreeMemoryTop - *FreeMemoryBottom); | |
| RemainingPages = (UINTN)(RShiftU64 (RemainingMemory, EFI_PAGE_SHIFT)); | |
| // | |
| // The number of remaining pages needs to be greater than or equal to that of | |
| // the request pages. In addition, there should be enough space left to hold a | |
| // Memory Allocation HOB. | |
| // | |
| Pages = ALIGN_VALUE (Pages, EFI_SIZE_TO_PAGES (Granularity)); | |
| if ((RemainingPages > Pages) || | |
| ((RemainingPages == Pages) && | |
| ((RemainingMemory & EFI_PAGE_MASK) >= sizeof (EFI_HOB_MEMORY_ALLOCATION)))) | |
| { | |
| // | |
| // Update the PHIT to reflect the memory usage | |
| // | |
| *(FreeMemoryTop) -= Pages * EFI_PAGE_SIZE; | |
| // | |
| // Update the value for the caller | |
| // | |
| *Memory = *(FreeMemoryTop); | |
| // | |
| // Create a memory allocation HOB. | |
| // | |
| InternalBuildMemoryAllocationHob ( | |
| *(FreeMemoryTop), | |
| Pages * EFI_PAGE_SIZE, | |
| MemoryType | |
| ); | |
| return EFI_SUCCESS; | |
| } else { | |
| // | |
| // Try to find free memory by searching memory allocation HOBs. | |
| // | |
| Status = FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory); | |
| if (!EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| DEBUG ((DEBUG_ERROR, "AllocatePages failed: No 0x%lx Pages is available.\n", (UINT64)Pages)); | |
| DEBUG ((DEBUG_ERROR, "There is only left 0x%lx pages memory resource to be allocated.\n", (UINT64)RemainingPages)); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| } | |
| /** | |
| Mark the memory allocation HOB to be unused(freed) and update *FreeMemoryTop | |
| if MemoryBaseAddress == *FreeMemoryTop. | |
| @param[in] PrivateData Pointer to PeiCore's private data structure. | |
| @param[in, out] MemoryAllocationHobToFree Pointer to memory allocation HOB to be freed. | |
| **/ | |
| VOID | |
| FreeMemoryAllocationHob ( | |
| IN PEI_CORE_INSTANCE *PrivateData, | |
| IN OUT EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHobToFree | |
| ) | |
| { | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_PHYSICAL_ADDRESS *FreeMemoryTop; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; | |
| Hob.Raw = PrivateData->HobList.Raw; | |
| if (!PrivateData->PeiMemoryInstalled && PrivateData->SwitchStackSignal) { | |
| // | |
| // When PeiInstallMemory is called but temporary memory has *not* been moved to permanent memory, | |
| // use the FreePhysicalMemoryTop field of PEI_CORE_INSTANCE structure. | |
| // | |
| FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop); | |
| } else { | |
| FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); | |
| } | |
| if (MemoryAllocationHobToFree->AllocDescriptor.MemoryBaseAddress == *FreeMemoryTop) { | |
| // | |
| // Update *FreeMemoryTop. | |
| // | |
| *FreeMemoryTop += MemoryAllocationHobToFree->AllocDescriptor.MemoryLength; | |
| // | |
| // Mark the memory allocation HOB to be unused(freed). | |
| // | |
| MemoryAllocationHobToFree->Header.HobType = EFI_HOB_TYPE_UNUSED; | |
| MemoryAllocationHob = NULL; | |
| Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); | |
| while (Hob.Raw != NULL) { | |
| if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) && | |
| (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress == *FreeMemoryTop)) | |
| { | |
| // | |
| // Found memory allocation HOB that has EfiConventionalMemory MemoryType and | |
| // MemoryBaseAddress == new *FreeMemoryTop. | |
| // | |
| MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; | |
| break; | |
| } | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); | |
| } | |
| // | |
| // Free memory allocation HOB iteratively. | |
| // | |
| if (MemoryAllocationHob != NULL) { | |
| FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob); | |
| } | |
| } | |
| } | |
| /** | |
| Frees memory pages. | |
| @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
| @param[in] Memory The base physical address of the pages to be freed. | |
| @param[in] Pages The number of contiguous 4 KB pages to free. | |
| @retval EFI_SUCCESS The requested pages were freed. | |
| @retval EFI_INVALID_PARAMETER Memory is not a page-aligned address or Pages is invalid. | |
| @retval EFI_NOT_FOUND The requested memory pages were not allocated with | |
| AllocatePages(). | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiFreePages ( | |
| IN CONST EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PHYSICAL_ADDRESS Memory, | |
| IN UINTN Pages | |
| ) | |
| { | |
| PEI_CORE_INSTANCE *PrivateData; | |
| UINT64 Bytes; | |
| UINT64 Start; | |
| UINT64 End; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; | |
| Bytes = LShiftU64 (Pages, EFI_PAGE_SHIFT); | |
| Start = Memory; | |
| End = Start + Bytes - 1; | |
| if ((Pages == 0) || ((Start & EFI_PAGE_MASK) != 0) || (Start >= End)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); | |
| Hob.Raw = PrivateData->HobList.Raw; | |
| if (Hob.Raw == NULL) { | |
| // | |
| // HOB is not initialized yet. | |
| // | |
| return EFI_NOT_AVAILABLE_YET; | |
| } | |
| MemoryAllocationHob = NULL; | |
| Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); | |
| while (Hob.Raw != NULL) { | |
| if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType != EfiConventionalMemory) && | |
| (Memory >= Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress) && | |
| ((Memory + Bytes) <= (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress + Hob.MemoryAllocation->AllocDescriptor.MemoryLength))) | |
| { | |
| // | |
| // Found the memory allocation HOB that includes the memory pages to be freed. | |
| // | |
| MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; | |
| break; | |
| } | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); | |
| } | |
| if (MemoryAllocationHob != NULL) { | |
| UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, Memory, Bytes, EfiConventionalMemory); | |
| FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob); | |
| return EFI_SUCCESS; | |
| } else { | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| /** | |
| Pool allocation service. Before permanent memory is discovered, the pool will | |
| be allocated in the heap in temporary memory. Generally, the size of the heap in temporary | |
| memory does not exceed 64K, so the biggest pool size could be allocated is | |
| 64K. | |
| @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. | |
| @param Size Amount of memory required | |
| @param Buffer Address of pointer to the buffer | |
| @retval EFI_SUCCESS The allocation was successful | |
| @retval EFI_OUT_OF_RESOURCES There is not enough heap to satisfy the requirement | |
| to allocate the requested size. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PeiAllocatePool ( | |
| IN CONST EFI_PEI_SERVICES **PeiServices, | |
| IN UINTN Size, | |
| OUT VOID **Buffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HOB_MEMORY_POOL *Hob; | |
| // | |
| // If some "post-memory" PEIM wishes to allocate larger pool, | |
| // it should use AllocatePages service instead. | |
| // | |
| // | |
| // Generally, the size of heap in temporary memory does not exceed 64K, | |
| // HobLength is multiples of 8 bytes, so the maximum size of pool is 0xFFF8 - sizeof (EFI_HOB_MEMORY_POOL) | |
| // | |
| if (Size > (0xFFF8 - sizeof (EFI_HOB_MEMORY_POOL))) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = PeiServicesCreateHob ( | |
| EFI_HOB_TYPE_MEMORY_POOL, | |
| (UINT16)(sizeof (EFI_HOB_MEMORY_POOL) + Size), | |
| (VOID **)&Hob | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| *Buffer = NULL; | |
| } else { | |
| *Buffer = Hob + 1; | |
| } | |
| return Status; | |
| } |