From ddfbd1f74cba0f8edfc4c95b2734758ee4da3537 Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Fri, 10 Apr 2026 09:23:58 -0700 Subject: [PATCH 1/9] Revert "Update memory map descriptor merging logic to honor the bucket type information (#1429)" This reverts commit b79a6ef972d651bd3f8154919d08f81b97d86ded as it was rejected upstream in favor a different solution. --- MdeModulePkg/Core/Dxe/Mem/Imem.h | 19 -- MdeModulePkg/Core/Dxe/Mem/Page.c | 225 +----------------- .../Core/Dxe/Misc/MemoryProtectionSupport.c | 4 - .../Core/Dxe/Misc/MemoryProtectionSupport.h | 1 - 4 files changed, 4 insertions(+), 245 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/Mem/Imem.h b/MdeModulePkg/Core/Dxe/Mem/Imem.h index cf66fcb7197..84027d628bc 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Imem.h +++ b/MdeModulePkg/Core/Dxe/Mem/Imem.h @@ -146,25 +146,6 @@ CoreInternalAllocatePages ( IN BOOLEAN NeedGuard ); -// MU_CHANGE START: Add function to get the bucket memory type for a given memory region - -/** - Get the memory type for a given bucket. - - @param PhysicalStart The starting address of the memory region. - @param PhysicalEnd The ending address of the memory region. - - @return The memory type for the bucket that contains the given physical address range. - If the address range does not match any special bucket, it returns EfiMaxMemoryType. -**/ -EFI_MEMORY_TYPE -GetBucketMemoryType ( - IN EFI_PHYSICAL_ADDRESS PhysicalStart, - IN EFI_PHYSICAL_ADDRESS PhysicalEnd - ); - -// MU_CHANGE ENDS - // // Internal Global data // diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index cba06b3bd27..860b1f81b6f 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -95,20 +95,6 @@ EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = { // GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN gLoadFixedAddressCodeMemoryReady = FALSE; -// MU_CHANGE START: Add function prototype to be used in CoreAddRange - -/** - Internal function. Moves any memory descriptors that are on the - temporary descriptor stack to heap. - -**/ -VOID -CoreFreeMemoryMapStack ( - VOID - ); - -// MU_CHANGE END - /** Enter critical section by gaining lock on gMemoryLock. @@ -155,102 +141,6 @@ RemoveMemoryMapEntry ( } } -// MU_CHANGE START: Add function to get the bucket memory type for a given memory region - -/** - Helper function to evaluate if memory regions intersect. - - @param Start1 The address of the first byte in the first memory region. - @param End1 The address of the last byte in the first memory region. - @param Start2 The address of the first byte in the second memory region. - @param End2 The address of the last byte in the second memory region. - - @return TRUE if the memory regions intersect, FALSE otherwise. -**/ -STATIC -BOOLEAN -MemoryRegionsIntersect ( - IN EFI_PHYSICAL_ADDRESS Start1, - IN EFI_PHYSICAL_ADDRESS End1, - IN EFI_PHYSICAL_ADDRESS Start2, - IN EFI_PHYSICAL_ADDRESS End2 - ) -{ - return (((Start1 <= End2) && (Start2 <= Start1)) || - ((Start2 <= End1) && (Start1 <= Start2))); -} - -/** - Get the memory type for a given bucket. - - @param PhysicalStart The starting address of the memory region. - @param PhysicalEnd The ending address of the memory region. - - @return The memory type for the bucket that contains the given physical address range. - If the address range does not match any special bucket, it returns EfiMaxMemoryType. -**/ -EFI_MEMORY_TYPE -GetBucketMemoryType ( - IN EFI_PHYSICAL_ADDRESS PhysicalStart, - IN EFI_PHYSICAL_ADDRESS PhysicalEnd - ) -{ - EFI_MEMORY_TYPE BucketType; - - // Find the bucket type for the incoming memory region. - for (BucketType = (EFI_MEMORY_TYPE)0; BucketType < EfiMaxMemoryType; BucketType++) { - // - // If the number of pages for this memory type is not zero, the input region - // better be within the same bucket. We only care about the special memory type - // here because we need these buckets to remain consistent so that the OS resume - // logic can work properly. The same applies to the memory allocation logic. - // - if (mMemoryTypeStatistics[BucketType].Special && (mMemoryTypeStatistics[BucketType].NumberOfPages != 0)) { - if ((PhysicalStart >= mMemoryTypeStatistics[BucketType].BaseAddress) && - (PhysicalEnd <= mMemoryTypeStatistics[BucketType].MaximumAddress)) - { - break; - } else if (MemoryRegionsIntersect ( - PhysicalStart, - PhysicalEnd, - mMemoryTypeStatistics[BucketType].BaseAddress, - mMemoryTypeStatistics[BucketType].MaximumAddress - )) - { - // The start and end overlap the bucket, but not fully inclusive. We should not allow this. - DEBUG (( - DEBUG_ERROR, - "%a: %lx-%lx intersects bucket type %d (%lx-%lx)\n", - __func__, - PhysicalStart, - PhysicalEnd, - BucketType, - mMemoryTypeStatistics[BucketType].BaseAddress, - mMemoryTypeStatistics[BucketType].MaximumAddress - )); - - ASSERT (FALSE); - } - } - } - - // If we can find the bucket type, use it to guide the merging logic below. - // Otherwise, we will not care about the bucket type. - if (BucketType >= EfiMaxMemoryType) { - DEBUG (( - DEBUG_PAGE, - "%a: defaulting to max for %lx -%lx\n", - __func__, - PhysicalStart, - PhysicalEnd - )); - } - - return BucketType; -} - -// MU_CHANGE ENDS - /** Internal function. Adds a ranges to the memory map. The range must not already exist in the map. @@ -274,79 +164,11 @@ CoreAddRange ( LIST_ENTRY *Link; MEMORY_MAP *Entry; - // MU_CHANGE STARTS: Add check to merge memory regions of the bucket type - EFI_MEMORY_TYPE BucketType; - EFI_MEMORY_TYPE MergeType; - BOOLEAN Break; - - // MU_CHANGE ENDs - ASSERT ((Start & EFI_PAGE_MASK) == 0); ASSERT (End > Start); ASSERT_LOCKED (&gMemoryLock); - // MU_CHANGE STARTS: Add check to merge memory regions of the bucket type - // Find the bucket type for the incoming memory region. - Break = FALSE; - for (BucketType = (EFI_MEMORY_TYPE)0; BucketType < EfiMaxMemoryType; BucketType++) { - // - // If the number of pages for this memory type is not zero, the input region better - // be within the same bucket. Otherwise, we will handle the ones we care about, - // the special memory types, in chunks. - // - if (mMemoryTypeStatistics[BucketType].Special && (mMemoryTypeStatistics[BucketType].NumberOfPages != 0)) { - if ((Start <= mMemoryTypeStatistics[BucketType].MaximumAddress) && - (End > mMemoryTypeStatistics[BucketType].MaximumAddress)) - { - // - // The start overlaps the bucket, so we let self-recursion handle the tail, and we - // handle the head. - // - // |----------|---Special Memory Bucket---|----------| - // |--------------^---------------------------^------| - // |------------Start------------------------End-----| - // - CoreAddRange ( - Type, - mMemoryTypeStatistics[BucketType].MaximumAddress + 1, - End, - Attribute - ); - CoreFreeMemoryMapStack (); - End = mMemoryTypeStatistics[BucketType].MaximumAddress; - Break = TRUE; - } - - if ((Start < mMemoryTypeStatistics[BucketType].BaseAddress) && - (End >= mMemoryTypeStatistics[BucketType].BaseAddress)) - { - // The end overlaps the bucket, so we let self-recursion handle the head, and we - // handle the tail. - // - // |----------|---Special Memory Bucket---|----------| - // |------^-------------------^----------------------| - // |----Start----------------End---------------------| - // - CoreAddRange ( - Type, - Start, - mMemoryTypeStatistics[BucketType].BaseAddress - 1, - Attribute - ); - CoreFreeMemoryMapStack (); - Start = mMemoryTypeStatistics[BucketType].BaseAddress; - Break = TRUE; - } - - if (Break) { - break; - } - } - } - - // MU_CHANGE ENDS - DEBUG ((DEBUG_PAGE, "AddRange: %lx-%lx to %d\n", Start, End, Type)); // @@ -391,8 +213,7 @@ CoreAddRange ( // and the same Attribute // - MergeType = GetBucketMemoryType (Start, End); // MU_CHANGE: Add check to merge memory regions of the bucket type - Link = gMemoryMap.ForwardLink; + Link = gMemoryMap.ForwardLink; while (Link != &gMemoryMap) { Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); Link = Link->ForwardLink; @@ -405,14 +226,6 @@ CoreAddRange ( continue; } - // MU_CHANGE STARTS: Add check to merge memory regions of the bucket type - // We need to make sure we can only merge with the same type as the merge type - if (MergeType != GetBucketMemoryType (Entry->Start, Entry->End)) { - continue; - } - - // MU_CHANGE ENDS - if (Entry->End + 1 == Start) { Start = Entry->Start; RemoveMemoryMapEntry (Entry); @@ -974,18 +787,12 @@ CoreAddMemoryDescriptor ( } if (gMemoryTypeInformation[Index].NumberOfPages != 0) { - // MU_CHANGE Starts - // Activate the statistics so that the free page operation can be performed - // with valid bucket information. - mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; - gMemoryTypeInformation[Index].NumberOfPages = 0; CoreFreePages ( mMemoryTypeStatistics[Type].BaseAddress, - (UINTN)mMemoryTypeStatistics[Type].NumberOfPages + gMemoryTypeInformation[Index].NumberOfPages ); - // mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; - // gMemoryTypeInformation[Index].NumberOfPages = 0; - // MU_CHANGE Ends + mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; + gMemoryTypeInformation[Index].NumberOfPages = 0; } } @@ -2233,30 +2040,6 @@ CoreGetMemoryMap ( (Entry->End <= mMemoryTypeStatistics[Type].MaximumAddress)) { MemoryMap->Type = Type; - // MU_CHANGE STARTS: Add check to merge memory regions of the bucket type - } else if (mMemoryTypeStatistics[Type].Special && - (mMemoryTypeStatistics[Type].NumberOfPages > 0) && - MemoryRegionsIntersect ( - Entry->Start, - Entry->End, - mMemoryTypeStatistics[Type].BaseAddress, - mMemoryTypeStatistics[Type].MaximumAddress - )) - { - // There is partial overlap with a special memory type bin. - // This is not allowed, so we will not change the type. - DEBUG (( - DEBUG_ERROR, - "%a: Memory Map entry partially overlaps with a special memory type bin. Bucket Type %d, Type %d, Start 0x%lx, End 0x%lx\n", - __func__, - Type, - Entry->Type, - Entry->Start, - Entry->End - )); - - ASSERT (FALSE); - // MU_CHANGE ENDS } } } diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.c index 356c929daac..60e8cc89373 100644 --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.c +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.c @@ -1106,10 +1106,6 @@ MergeMemoryMapByAttribute ( MemoryBlockLength = (UINT64)(EfiPagesToSize (NewMemoryMapEntry->NumberOfPages)); if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) && (NewMemoryMapEntry->Attribute == NextMemoryMapEntry->Attribute) && - // MU_CHANGE STARTS: Add check to merge memory regions of the bucket type - (GetBucketMemoryType (NewMemoryMapEntry->PhysicalStart, NewMemoryMapEntry->PhysicalStart + EFI_PAGES_TO_SIZE (NewMemoryMapEntry->NumberOfPages) - 1) == - GetBucketMemoryType (NextMemoryMapEntry->PhysicalStart, NextMemoryMapEntry->PhysicalStart + EFI_PAGES_TO_SIZE (NextMemoryMapEntry->NumberOfPages) - 1)) && - // MU_CHANGE ENDS ((NewMemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) { NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.h b/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.h index ee021b676e4..4c4ba7726b2 100644 --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.h +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtectionSupport.h @@ -11,7 +11,6 @@ #include "DxeMain.h" #include "Mem/HeapGuard.h" -#include "Mem/Imem.h" // MU_CHANGE: Include Imem.h for bucket memory type functions #include #include From 9f534fd20ef0f1eaabe13fbeeaf2c45e4a0507b0 Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Wed, 28 Jan 2026 14:27:13 -0800 Subject: [PATCH 2/9] MdeModulePkg: Consolidate Memory Bin Logic This commits splits out logic currently contained in Gcd.c and Page.c to a new file called MemoryBin.c. This is set up in preparation to add support to PEI for memory bins (an S4 resume stability feature). MemoryBin.c takes all global state in as parameters so that DXE core can use globals and PEI core can use HOBs. This was requested not to be a library. Signed-off-by: Oliver Smith-Denny --- MdeModulePkg/Core/Dxe/DxeMain.h | 18 +- MdeModulePkg/Core/Dxe/DxeMain.inf | 1 + MdeModulePkg/Core/Dxe/Gcd/Gcd.c | 184 +++-- MdeModulePkg/Core/Dxe/Mem/MemoryBin.c | 692 ++++++++++++++++++ MdeModulePkg/Core/Dxe/Mem/Page.c | 544 +++++++------- MdeModulePkg/Core/PrivateInclude/MemoryBin.h | 145 ++++ .../Include/Guid/MemoryTypeInformation.h | 15 + MdeModulePkg/MdeModulePkg.dec | 1 + 8 files changed, 1260 insertions(+), 340 deletions(-) create mode 100644 MdeModulePkg/Core/Dxe/Mem/MemoryBin.c create mode 100644 MdeModulePkg/Core/PrivateInclude/MemoryBin.h diff --git a/MdeModulePkg/Core/Dxe/DxeMain.h b/MdeModulePkg/Core/Dxe/DxeMain.h index 328da465b2c..9b325dbda94 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.h +++ b/MdeModulePkg/Core/Dxe/DxeMain.h @@ -263,6 +263,12 @@ extern EFI_GUID *gDxeCoreFileName; extern EFI_LOADED_IMAGE_PROTOCOL *gDxeCoreLoadedImage; extern EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1]; +// MU_CHANGE BEGIN: PEI Bins +extern BOOLEAN mMemoryTypeInformationInitialized; +extern EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1]; +extern EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress; +extern EFI_PHYSICAL_ADDRESS mDefaultBaseAddress; +// MU_CHANGE END: PEI Bins extern BOOLEAN gDispatcherRunning; extern EFI_RUNTIME_ARCH_PROTOCOL gRuntimeTemplate; @@ -284,11 +290,13 @@ CoreInitializePool ( VOID ); -VOID -CoreSetMemoryTypeInformationRange ( - IN EFI_PHYSICAL_ADDRESS Start, - IN UINT64 Length - ); +// MU_CHANGE BEGIN: PEI Bins +// VOID +// CoreSetMemoryTypeInformationRange ( +// IN EFI_PHYSICAL_ADDRESS Start, +// IN UINT64 Length +// ); +// MU_CHANGE END: PEI Bins /** Called to initialize the memory map and add descriptors to diff --git a/MdeModulePkg/Core/Dxe/DxeMain.inf b/MdeModulePkg/Core/Dxe/DxeMain.inf index d95ef8840e8..1a44efb2785 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.inf +++ b/MdeModulePkg/Core/Dxe/DxeMain.inf @@ -50,6 +50,7 @@ Mem/Page.c Mem/MemData.c Mem/Imem.h + Mem/MemoryBin.c # MU_CHANGE: PEI Bins Mem/MemoryProfileRecord.c Mem/HeapGuard.c Mem/HeapGuard.h diff --git a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c index 3c2484d12de..b93ce4ff02d 100644 --- a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c +++ b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c @@ -13,6 +13,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "DxeMain.h" #include "Gcd.h" #include "Mem/HeapGuard.h" +#include // MU_CHANGE: PEI Bins #define MINIMUM_INITIAL_MEMORY_SIZE 0x10000 @@ -2164,30 +2165,32 @@ CoreConvertResourceDescriptorHobAttributesToCapabilities ( return Capabilities; } -/** - Calculate total memory bin size neeeded. - - @return The total memory bin size neeeded. - -**/ -UINT64 -CalculateTotalMemoryBinSizeNeeded ( - VOID - ) -{ - UINTN Index; - UINT64 TotalSize; - - // - // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array - // - TotalSize = 0; - for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - TotalSize += LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); - } - - return TotalSize; -} +// MU_CHANGE BEGIN: PEI Bins - Moved to MemoryBin.c +// /** +// Calculate total memory bin size neeeded. +// +// @return The total memory bin size neeeded. +// +// **/ +// UINT64 +// CalculateTotalMemoryBinSizeNeeded ( +// VOID +// ) +// { +// UINTN Index; +// UINT64 TotalSize; +// +// // +// // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array +// // +// TotalSize = 0; +// for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { +// TotalSize += LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); +// } +// +// return TotalSize; +// } +// MU_CHANGE END: PEI Bins /** Find the largest region in the specified region that is not covered by an existing memory allocation @@ -2270,14 +2273,16 @@ CoreInitializeMemoryServices ( ) { EFI_PEI_HOB_POINTERS Hob; - EFI_MEMORY_TYPE_INFORMATION *EfiMemoryTypeInformation; - UINTN DataSize; + // MU_CHANGE BEGIN: PEI Bins + // EFI_MEMORY_TYPE_INFORMATION *EfiMemoryTypeInformation; + // UINTN DataSize; + // MU_CHANGE END: PEI Bins BOOLEAN Found; EFI_HOB_HANDOFF_INFO_TABLE *PhitHob; EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; EFI_HOB_RESOURCE_DESCRIPTOR *PhitResourceHob; EFI_HOB_RESOURCE_DESCRIPTOR *MemoryTypeInformationResourceHob; - UINTN Count; + // UINTN Count; // MU_CHANGE: PEI Bins EFI_PHYSICAL_ADDRESS BaseAddress; UINT64 Length; UINT64 Attributes; @@ -2285,10 +2290,11 @@ CoreInitializeMemoryServices ( EFI_PHYSICAL_ADDRESS TestedMemoryBaseAddress; UINT64 TestedMemoryLength; EFI_PHYSICAL_ADDRESS HighAddress; - EFI_HOB_GUID_TYPE *GuidHob; + // EFI_HOB_GUID_TYPE *GuidHob; // MU_CHANGE: PEI Bins UINT32 ReservedCodePageNumber; UINT64 MinimalMemorySizeNeeded; - EFI_PHYSICAL_ADDRESS ResourceHobMemoryTop; // MU_CHANGE + EFI_PHYSICAL_ADDRESS ResourceHobMemoryTop; + EFI_STATUS Status; // MU_CHANGE: PEI Bins // // Point at the first HOB. This must be the PHIT HOB. @@ -2327,57 +2333,68 @@ CoreInitializeMemoryServices ( + EFI_PAGES_TO_SIZE (ReservedCodePageNumber); } - // - // See if a Memory Type Information HOB is available - // - MemoryTypeInformationResourceHob = NULL; - GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid); - if (GuidHob != NULL) { - EfiMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob); - DataSize = GET_GUID_HOB_DATA_SIZE (GuidHob); - if ((EfiMemoryTypeInformation != NULL) && (DataSize > 0) && (DataSize <= (EfiMaxMemoryType + 1) * sizeof (EFI_MEMORY_TYPE_INFORMATION))) { - CopyMem (&gMemoryTypeInformation, EfiMemoryTypeInformation, DataSize); - - // - // Look for Resource Descriptor HOB with a ResourceType of System Memory - // and an Owner GUID of gEfiMemoryTypeInformationGuid. If more than 1 is - // found, then set MemoryTypeInformationResourceHob to NULL. - // - Count = 0; - for (Hob.Raw = *HobStart; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { - if ((GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) && (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR2)) { - continue; - } - - ResourceHob = Hob.ResourceDescriptor; - if (!CompareGuid (&ResourceHob->Owner, &gEfiMemoryTypeInformationGuid)) { - continue; - } - - Count++; - if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) { - continue; - } - - if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) { - continue; - } - - if (ResourceHob->ResourceLength >= CalculateTotalMemoryBinSizeNeeded ()) { - MemoryTypeInformationResourceHob = ResourceHob; - } - } - - if (Count > 1) { - MemoryTypeInformationResourceHob = NULL; - } - } + // MU_CHANGE BEGIN: PEI Bins - Replaced inline HOB search with shared MemoryBin functions + // // + // // See if a Memory Type Information HOB is available + // // + // MemoryTypeInformationResourceHob = NULL; + // GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid); + // if (GuidHob != NULL) { + // EfiMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob); + // DataSize = GET_GUID_HOB_DATA_SIZE (GuidHob); + // if ((EfiMemoryTypeInformation != NULL) && (DataSize > 0) && (DataSize <= (EfiMaxMemoryType + 1) * sizeof (EFI_MEMORY_TYPE_INFORMATION))) { + // CopyMem (&gMemoryTypeInformation, EfiMemoryTypeInformation, DataSize); + // + // // + // // Look for Resource Descriptor HOB with a ResourceType of System Memory + // // and an Owner GUID of gEfiMemoryTypeInformationGuid. If more than 1 is + // // found, then set MemoryTypeInformationResourceHob to NULL. + // // + // Count = 0; + // for (Hob.Raw = *HobStart; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + // if ((GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) && (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR2)) { + // continue; + // } + // + // ResourceHob = Hob.ResourceDescriptor; + // if (!CompareGuid (&ResourceHob->Owner, &gEfiMemoryTypeInformationGuid)) { + // continue; + // } + // + // Count++; + // if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) { + // continue; + // } + // + // if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) { + // continue; + // } + // + // if (ResourceHob->ResourceLength >= CalculateTotalMemoryBinSizeNeeded ()) { + // MemoryTypeInformationResourceHob = ResourceHob; + // } + // } + // + // if (Count > 1) { + // MemoryTypeInformationResourceHob = NULL; + // } + // } + // } + Status = PopulateMemoryTypeInformation (gMemoryTypeInformation); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "No Memory Type Information HOB found, S4 resume will likely fail\n")); } + MemoryTypeInformationResourceHob = GetMemoryTypeInformationResourceHob ( + HobStart, + gMemoryTypeInformation + ); + // // Include the total memory bin size needed to make sure memory bin could be allocated successfully. // - MinimalMemorySizeNeeded = MINIMUM_INITIAL_MEMORY_SIZE + CalculateTotalMemoryBinSizeNeeded (); + MinimalMemorySizeNeeded = MINIMUM_INITIAL_MEMORY_SIZE + CalculateTotalMemoryBinSizeNeeded (0, gMemoryTypeInformation); + // MU_CHANGE END: PEI Bins // // Find the Resource Descriptor HOB that contains PHIT range EfiFreeMemoryBottom..EfiFreeMemoryTop @@ -2586,16 +2603,29 @@ CoreInitializeMemoryServices ( Capabilities = CoreConvertResourceDescriptorHobAttributesToCapabilities (EfiGcdMemoryTypeSystemMemory, Attributes); } - if (MemoryTypeInformationResourceHob != NULL) { + // MU_CHANGE BEGIN: PEI Bins + // if (MemoryTypeInformationResourceHob != NULL) { + if ((Status == EFI_SUCCESS) && (MemoryTypeInformationResourceHob != NULL)) { + // MU_CHANGE END: PEI Bins // // If a Memory Type Information Resource HOB was found, then use the address // range of the Memory Type Information Resource HOB as the preferred // address range for the Memory Type Information bins. // + // MU_CHANGE BEGIN: PEI Bins + // CoreSetMemoryTypeInformationRange ( + // MemoryTypeInformationResourceHob->PhysicalStart, + // MemoryTypeInformationResourceHob->ResourceLength + // ); CoreSetMemoryTypeInformationRange ( MemoryTypeInformationResourceHob->PhysicalStart, - MemoryTypeInformationResourceHob->ResourceLength + MemoryTypeInformationResourceHob->ResourceLength, + gMemoryTypeInformation, + &mMemoryTypeInformationInitialized, + mMemoryTypeStatistics, + &mDefaultMaximumAddress ); + // MU_CHANGE END: PEI Bins } // diff --git a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c new file mode 100644 index 00000000000..694cfad946b --- /dev/null +++ b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c @@ -0,0 +1,692 @@ +// MU_CHANGE: PEI Bins - Whole File +/** @file + + Shared logic between cores to work with memory bins for S4 resume stability. This file is duplicated in PEI Core and + DXE Core until a BaseTools feature comes online to support recommended library instances. Any changes to this file + must also be made in MdeModulePkg/Core/Pei/Memory/MemoryBin.c. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define MEMORY_ATTRIBUTE_MASK (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED | \ + EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_16_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_32_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_64_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_PERSISTENT | \ + EFI_RESOURCE_ATTRIBUTE_SPECIAL_PURPOSE ) + +#define TESTED_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED ) + +/** + Allocates pages from the memory map. + + @param Type The type of allocation to perform + @param MemoryType The type of memory to turn the allocated pages + into + @param NumberOfPages The number of pages to allocate + @param Memory A pointer to receive the base allocated memory + address + + @return Status. On success, Memory is filled in with the base address allocated + @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in + spec. + @retval EFI_NOT_FOUND Could not allocate pages match the requirement. + @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. + @retval EFI_SUCCESS Pages successfully allocated. + +**/ +EFI_STATUS +EFIAPI +CoreAllocatePages ( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN NumberOfPages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory + ); + +/** + Frees previous allocated pages. + + @param Memory Base address of memory being freed + @param NumberOfPages The number of pages to free + + @retval EFI_NOT_FOUND Could not find the entry that covers the range + @retval EFI_INVALID_PARAMETER Address not aligned + @return EFI_SUCCESS -Pages successfully freed. + +**/ +EFI_STATUS +EFIAPI +CoreFreePages ( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN NumberOfPages + ); + +/** + Calculate total memory bin size needed. + + @param BinTop The top address of the memory bins. This is an optional parameter. + If non-zero, alignment requirements will be considered in the calculation. + @param MemoryTypeInformation The memory type information array. + + @return The total memory bin size needed. + +**/ +UINT64 +CalculateTotalMemoryBinSizeNeeded ( + IN UINTN BinTop, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ) +{ + UINTN Index; + UINT64 TotalSize; + UINTN Granularity; + + if (MemoryTypeInformation == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return 0; + } + + // + // Loop through each memory type in the order specified by the MemoryTypeInformation[] array + // + TotalSize = 0; + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || + (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) + { + Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } + + // MemoryTypeInformation[Index].NumberOfPages is already aligned to the allocation granularity + TotalSize += LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); + + // BinTop is optional + if (BinTop == 0) { + continue; + } + + BinTop -= (UINTN)LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); + TotalSize += (BinTop & (Granularity - 1)); + BinTop &= ~(Granularity - 1); + } + + return TotalSize; +} + +/** + Get the Memory Type Information HOB if it exists and populate gMemoryTypeInformation. + + @param MemoryTypeInformation The pointer to the memory type information array to be populated. + + @return EFI_STATUS On EFI_SUCCESS, gMemoryTypeInformation points to the + Memory Type Information. + @return EFI_NOT_FOUND No valid Memory Type Information HOB found. +**/ +EFI_STATUS +EFIAPI +PopulateMemoryTypeInformation ( + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ) +{ + UINTN DataSize; + EFI_MEMORY_TYPE_INFORMATION *EfiMemoryTypeInformation; + EFI_HOB_GUID_TYPE *GuidHob; + UINTN Index; + UINT32 Granularity; + + if (MemoryTypeInformation == NULL) { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid); + if (GuidHob != NULL) { + EfiMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob); + DataSize = GET_GUID_HOB_DATA_SIZE (GuidHob); + if ((EfiMemoryTypeInformation != NULL) && (DataSize > 0) && (DataSize <= (EfiMaxMemoryType + 1) * sizeof (EFI_MEMORY_TYPE_INFORMATION))) { + CopyMem (MemoryTypeInformation, EfiMemoryTypeInformation, DataSize); + + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the MemoryTypeInformation[] array is valid + // + if (MemoryTypeInformation[Index].Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || + (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) + { + Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } else { + Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + } + + // Align the number of pages to the allocation granularity + MemoryTypeInformation[Index].NumberOfPages = (UINT32)RShiftU64 (ALIGN_VALUE (LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT), Granularity), EFI_PAGE_SHIFT); + } + } + + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_ERROR, "%a: Invalid Memory Type Information HOB data\n", __func__)); + ASSERT (FALSE); + } + + DEBUG ((DEBUG_ERROR, "%a: No Memory Type Information HOB found\n", __func__)); + + return EFI_NOT_FOUND; +} + +/** + Look for Resource Descriptor HOB with a ResourceType of System Memory + and an Owner GUID of gEfiMemoryTypeInformationGuid. If more than 1 is + found, then return NULL. + + @param HobStart Pointer to the start of the HOB list. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + + @return Non-NULL The pointer to the singular MemoryTypeInformation Resource Descriptor HOB. + @return NULL No valid MemoryTypeInformation Resource Descriptor HOB found. +**/ +EFI_HOB_RESOURCE_DESCRIPTOR * +EFIAPI +GetMemoryTypeInformationResourceHob ( + IN VOID **HobStart, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ) +{ + UINTN Count; + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + EFI_HOB_RESOURCE_DESCRIPTOR *MemoryTypeInformationResourceHob; + + if ((HobStart == NULL) || (MemoryTypeInformation == NULL)) { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return NULL; + } + + // + // See if a Memory Type Information HOB is available + // + MemoryTypeInformationResourceHob = NULL; + Count = 0; + for (Hob.Raw = *HobStart; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + if (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + continue; + } + + ResourceHob = Hob.ResourceDescriptor; + if (!CompareGuid (&ResourceHob->Owner, &gEfiMemoryTypeInformationGuid)) { + continue; + } + + Count++; + if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) { + continue; + } + + if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) { + continue; + } + + if (ResourceHob->ResourceLength >= CalculateTotalMemoryBinSizeNeeded ((UINTN)(ResourceHob->PhysicalStart + ResourceHob->ResourceLength), MemoryTypeInformation)) { + MemoryTypeInformationResourceHob = ResourceHob; + } + } + + if (Count > 1) { + return NULL; + } + + return MemoryTypeInformationResourceHob; +} + +/** + Sets the preferred memory range to use for the Memory Type Information bins. + This service must be called before fist call to CoreAddMemoryDescriptor(). + + If the location of the Memory Type Information bins has already been + established or the size of the range provides is smaller than all the + Memory Type Information bins, then the range provides is not used. + + @param Start The start address of the Memory Type Information range. + @param Length The size, in bytes, of the Memory Type Information range. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeStatistics The memory type statistics array to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. +**/ +VOID +EFIAPI +CoreSetMemoryTypeInformationRange ( + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + ) +{ + EFI_PHYSICAL_ADDRESS Top; + EFI_MEMORY_TYPE Type; + UINTN Index; + UINT64 Size; + UINT64 Alignment; + UINT64 BinSize; + + if ((MemoryTypeInformation == NULL) || + (MemoryTypeInformationInitialized == NULL) || + (MemoryTypeStatistics == NULL) || + (DefaultMaximumAddress == NULL)) + { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return; + } + + // + // Return if Memory Type Information bin locations have already been set + // + if (*MemoryTypeInformationInitialized) { + DEBUG ((DEBUG_ERROR, "%a: Ignored. Bins already set.\n", __func__)); + return; + } + + // + // Return if size of the Memory Type Information bins is greater than Length + // + Top = Start + Length; + Size = 0; + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || + (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) + { + Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } + + BinSize = EFI_PAGES_TO_SIZE ((UINTN)MemoryTypeInformation[Index].NumberOfPages); + BinSize = ALIGN_VALUE (BinSize, Alignment); + + Size += BinSize; + if (Size > Length) { + return; + } + + Top -= BinSize; + + Size += (Top & (Alignment - 1)); + if (Size > Length) { + return; + } + + Top &= ~(Alignment - 1); + } + } + + if (Size > Length) { + return; + } + + // + // Loop through each memory type in the order specified by the + // gMemoryTypeInformation[] array + // + Top = Start + Length; + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the MemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || + (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) + { + Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } + + BinSize = EFI_PAGES_TO_SIZE ((UINTN)MemoryTypeInformation[Index].NumberOfPages); + BinSize = ALIGN_VALUE (BinSize, Alignment); + + Top = (Top - BinSize) & ~(Alignment - 1); + + MemoryTypeStatistics[Type].BaseAddress = Top; + MemoryTypeStatistics[Type].MaximumAddress = Top + BinSize - 1; + + // + // If the current base address is the lowest address so far, then update + // the default maximum address + // + if (MemoryTypeStatistics[Type].BaseAddress < *DefaultMaximumAddress) { + *DefaultMaximumAddress = MemoryTypeStatistics[Type].BaseAddress - 1; + } + + MemoryTypeStatistics[Type].NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)BinSize); + MemoryTypeInformation[Index].NumberOfPages = 0; + } + } + + // + // If the number of pages reserved for a memory type is 0, then all + // allocations for that type should be in the default range. + // + for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + if (Type == (EFI_MEMORY_TYPE)MemoryTypeInformation[Index].Type) { + MemoryTypeStatistics[Type].InformationIndex = Index; + } + } + + MemoryTypeStatistics[Type].CurrentNumberOfPages = 0; + if (MemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { + MemoryTypeStatistics[Type].MaximumAddress = *DefaultMaximumAddress; + } + } + + *MemoryTypeInformationInitialized = TRUE; +} + +/** + Allocate memory bins for each memory type as specified in gMemoryTypeInformation. + + If all the memory types cannot be allocated, then all previously allocated + memory types are freed and the function returns. If this function fails, it will log and expect to be called + again when more memory is added to the system. + + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeStatistics The memory type statistics array to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. +**/ +VOID +EFIAPI +AllocateMemoryTypeInformationBins ( + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN FreeIndex; + UINT64 Alignment; + UINT64 BinSize; + EFI_MEMORY_TYPE Type; + + if ((MemoryTypeInformationInitialized == NULL) || + (MemoryTypeInformation == NULL) || + (MemoryTypeStatistics == NULL) || + (DefaultMaximumAddress == NULL)) + { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return; + } + + // + // Check to see if the statistics for the different memory types have already been established + // + if (*MemoryTypeInformationInitialized) { + return; + } + + // + // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array + // + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || + (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) + { + Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } + + BinSize = EFI_PAGES_TO_SIZE ((UINTN)MemoryTypeInformation[Index].NumberOfPages); + BinSize = ALIGN_VALUE (BinSize, Alignment); + + MemoryTypeInformation[Index].NumberOfPages = (UINT32)EFI_SIZE_TO_PAGES ((UINTN)BinSize); + + // + // Allocate pages for the current memory type from the top of available memory + // + Status = CoreAllocatePages ( + AllocateAnyPages, + Type, + MemoryTypeInformation[Index].NumberOfPages, + &MemoryTypeStatistics[Type].BaseAddress + ); + if (EFI_ERROR (Status)) { + // + // If an error occurs allocating the pages for the current memory type, then + // free all the pages allocates for the previous memory types and return. This + // operation with be retied when/if more memory is added to the system + // + for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[FreeIndex].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[FreeIndex].NumberOfPages != 0) { + CoreFreePages ( + MemoryTypeStatistics[Type].BaseAddress, + MemoryTypeInformation[FreeIndex].NumberOfPages + ); + MemoryTypeStatistics[Type].BaseAddress = 0; + MemoryTypeStatistics[Type].MaximumAddress = MAX_ALLOC_ADDRESS; + } + } + + return; + } + + // + // Compute the address at the top of the current statistics + // + MemoryTypeStatistics[Type].MaximumAddress = + MemoryTypeStatistics[Type].BaseAddress + + LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1; + + // + // If the current base address is the lowest address so far, then update the default + // maximum address + // + if (MemoryTypeStatistics[Type].BaseAddress < *DefaultMaximumAddress) { + *DefaultMaximumAddress = MemoryTypeStatistics[Type].BaseAddress - 1; + } + } + } + + // + // There was enough system memory for all the the memory types were allocated. So, + // those memory areas can be freed for future allocations, and all future memory + // allocations can occur within their respective bins + // + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the MemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + CoreFreePages ( + MemoryTypeStatistics[Type].BaseAddress, + MemoryTypeInformation[Index].NumberOfPages + ); + MemoryTypeStatistics[Type].NumberOfPages = MemoryTypeInformation[Index].NumberOfPages; + MemoryTypeInformation[Index].NumberOfPages = 0; + } + } + + // + // If the number of pages reserved for a memory type is 0, then all allocations for that type + // should be in the default range. + // + for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + if (Type == (EFI_MEMORY_TYPE)MemoryTypeInformation[Index].Type) { + MemoryTypeStatistics[Type].InformationIndex = Index; + } + } + + MemoryTypeStatistics[Type].CurrentNumberOfPages = 0; + if (MemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { + MemoryTypeStatistics[Type].MaximumAddress = *DefaultMaximumAddress; + } + } + + *MemoryTypeInformationInitialized = TRUE; +} + +/** + Update memory type statistics upon memory allocation and free. + + @param OldType The original memory type of the memory region. + @param NewType The new memory type of the memory region. + @param Start The starting physical address of the memory region. + @param NumberOfPages The number of pages in the memory region. + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeStatistics The memory type statistics array to be updated. + @param MemoryTypeInformation The memory type information array to be updated. + @param DefaultBaseAddress Default bin base address. + @param DefaultMaximumAddress Default bin maximum address. +**/ +VOID +EFIAPI +UpdateMemoryStatistics ( + IN EFI_MEMORY_TYPE OldType, + IN EFI_MEMORY_TYPE NewType, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINTN NumberOfPages, + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_PHYSICAL_ADDRESS DefaultBaseAddress, + IN EFI_PHYSICAL_ADDRESS DefaultMaximumAddress + ) +{ + if ((MemoryTypeInformationInitialized == NULL) || + (MemoryTypeStatistics == NULL) || + (MemoryTypeInformation == NULL)) + { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return; + } + + if (!*MemoryTypeInformationInitialized) { + return; + } + + // + // Update counters for the number of pages allocated to each memory type + // + if ((UINT32)OldType < EfiMaxMemoryType) { + if (((Start >= MemoryTypeStatistics[OldType].BaseAddress) && (Start <= MemoryTypeStatistics[OldType].MaximumAddress)) || + ((Start >= DefaultBaseAddress) && (Start <= DefaultMaximumAddress))) + { + if (NumberOfPages > MemoryTypeStatistics[OldType].CurrentNumberOfPages) { + MemoryTypeStatistics[OldType].CurrentNumberOfPages = 0; + } else { + MemoryTypeStatistics[OldType].CurrentNumberOfPages -= NumberOfPages; + } + } + } + + if ((UINT32)NewType < EfiMaxMemoryType) { + if (((Start >= MemoryTypeStatistics[NewType].BaseAddress) && (Start <= MemoryTypeStatistics[NewType].MaximumAddress)) || + ((Start >= DefaultBaseAddress) && (Start <= DefaultMaximumAddress))) + { + MemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages; + if ((MemoryTypeStatistics[NewType].InformationIndex < (UINTN)EfiMaxMemoryType) && + (MemoryTypeStatistics[NewType].CurrentNumberOfPages > MemoryTypeInformation[MemoryTypeStatistics[NewType].InformationIndex].NumberOfPages)) + { + MemoryTypeInformation[MemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)MemoryTypeStatistics[NewType].CurrentNumberOfPages; + } + } + } +} diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index 860b1f81b6f..f7239769c3f 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -10,19 +10,21 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "Imem.h" #include "HeapGuard.h" #include - -// -// Entry for tracking the memory regions for each memory type to coalesce similar memory types -// -typedef struct { - EFI_PHYSICAL_ADDRESS BaseAddress; - EFI_PHYSICAL_ADDRESS MaximumAddress; - UINT64 CurrentNumberOfPages; - UINT64 NumberOfPages; - UINTN InformationIndex; - BOOLEAN Special; - BOOLEAN Runtime; -} EFI_MEMORY_TYPE_STATISTICS; +// MU_CHANGE BEGIN: PEI Bins - Replaced EFI_MEMORY_TYPE_STATISTICS struct with shared header +// // +// // Entry for tracking the memory regions for each memory type to coalesce similar memory types +// // +// typedef struct { +// EFI_PHYSICAL_ADDRESS BaseAddress; +// EFI_PHYSICAL_ADDRESS MaximumAddress; +// UINT64 CurrentNumberOfPages; +// UINT64 NumberOfPages; +// UINTN InformationIndex; +// BOOLEAN Special; +// BOOLEAN Runtime; +// } EFI_MEMORY_TYPE_STATISTICS; +#include +// MU_CHANGE END: PEI Bins // // MemoryMap - The current memory map @@ -540,107 +542,109 @@ CoreLoadingFixedAddressHook ( return; } -/** - Sets the preferred memory range to use for the Memory Type Information bins. - This service must be called before fist call to CoreAddMemoryDescriptor(). - - If the location of the Memory Type Information bins has already been - established or the size of the range provides is smaller than all the - Memory Type Information bins, then the range provides is not used. - - @param Start The start address of the Memory Type Information range. - @param Length The size, in bytes, of the Memory Type Information range. -**/ -VOID -CoreSetMemoryTypeInformationRange ( - IN EFI_PHYSICAL_ADDRESS Start, - IN UINT64 Length - ) -{ - EFI_PHYSICAL_ADDRESS Top; - EFI_MEMORY_TYPE Type; - UINTN Index; - UINTN Size; - - // - // Return if Memory Type Information bin locations have already been set - // - if (mMemoryTypeInformationInitialized) { - DEBUG ((DEBUG_ERROR, "%a: Ignored. Bins already set.\n", __func__)); - return; - } - - // - // Return if size of the Memory Type Information bins is greater than Length - // - Size = 0; - for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - // - // Make sure the memory type in the gMemoryTypeInformation[] array is valid - // - Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); - if ((UINT32)Type > EfiMaxMemoryType) { - continue; - } - - Size += EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); - } - - if (Size > Length) { - return; - } - - // - // Loop through each memory type in the order specified by the - // gMemoryTypeInformation[] array - // - Top = Start + Length; - for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - // - // Make sure the memory type in the gMemoryTypeInformation[] array is valid - // - Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); - if ((UINT32)Type > EfiMaxMemoryType) { - continue; - } - - if (gMemoryTypeInformation[Index].NumberOfPages != 0) { - mMemoryTypeStatistics[Type].MaximumAddress = Top - 1; - Top -= EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); - mMemoryTypeStatistics[Type].BaseAddress = Top; - - // - // If the current base address is the lowest address so far, then update - // the default maximum address - // - if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { - mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; - } - - mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; - gMemoryTypeInformation[Index].NumberOfPages = 0; - } - } - - // - // If the number of pages reserved for a memory type is 0, then all - // allocations for that type should be in the default range. - // - for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { - for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { - mMemoryTypeStatistics[Type].InformationIndex = Index; - } - } - - mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; - if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { - mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; - } - } - - mMemoryTypeInformationInitialized = TRUE; -} +// MU_CHANGE BEGIN: PEI Bins - Moved CoreSetMemoryTypeInformationRange to MemoryBin.c +// /** +// Sets the preferred memory range to use for the Memory Type Information bins. +// This service must be called before fist call to CoreAddMemoryDescriptor(). +// +// If the location of the Memory Type Information bins has already been +// established or the size of the range provides is smaller than all the +// Memory Type Information bins, then the range provides is not used. +// +// @param Start The start address of the Memory Type Information range. +// @param Length The size, in bytes, of the Memory Type Information range. +// **/ +// VOID +// CoreSetMemoryTypeInformationRange ( +// IN EFI_PHYSICAL_ADDRESS Start, +// IN UINT64 Length +// ) +// { +// EFI_PHYSICAL_ADDRESS Top; +// EFI_MEMORY_TYPE Type; +// UINTN Index; +// UINTN Size; +// +// // +// // Return if Memory Type Information bin locations have already been set +// // +// if (mMemoryTypeInformationInitialized) { +// DEBUG ((DEBUG_ERROR, "%a: Ignored. Bins already set.\n", __func__)); +// return; +// } +// +// // +// // Return if size of the Memory Type Information bins is greater than Length +// // +// Size = 0; +// for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { +// // +// // Make sure the memory type in the gMemoryTypeInformation[] array is valid +// // +// Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); +// if ((UINT32)Type > EfiMaxMemoryType) { +// continue; +// } +// +// Size += EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); +// } +// +// if (Size > Length) { +// return; +// } +// +// // +// // Loop through each memory type in the order specified by the +// // gMemoryTypeInformation[] array +// // +// Top = Start + Length; +// for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { +// // +// // Make sure the memory type in the gMemoryTypeInformation[] array is valid +// // +// Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); +// if ((UINT32)Type > EfiMaxMemoryType) { +// continue; +// } +// +// if (gMemoryTypeInformation[Index].NumberOfPages != 0) { +// mMemoryTypeStatistics[Type].MaximumAddress = Top - 1; +// Top -= EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); +// mMemoryTypeStatistics[Type].BaseAddress = Top; +// +// // +// // If the current base address is the lowest address so far, then update +// // the default maximum address +// // +// if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { +// mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; +// } +// +// mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; +// gMemoryTypeInformation[Index].NumberOfPages = 0; +// } +// } +// +// // +// // If the number of pages reserved for a memory type is 0, then all +// // allocations for that type should be in the default range. +// // +// for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { +// for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { +// if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { +// mMemoryTypeStatistics[Type].InformationIndex = Index; +// } +// } +// +// mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; +// if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { +// mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; +// } +// } +// +// mMemoryTypeInformationInitialized = TRUE; +// } +// MU_CHANGE END: PEI Bins /** Called to initialize the memory map and add descriptors to @@ -666,9 +670,11 @@ CoreAddMemoryDescriptor ( ) { EFI_PHYSICAL_ADDRESS End; - EFI_STATUS Status; - UINTN Index; - UINTN FreeIndex; + // MU_CHANGE BEGIN: PEI Bins + // EFI_STATUS Status; + // UINTN Index; + // UINTN FreeIndex; + // MU_CHANGE END: PEI Bins if ((Start & EFI_PAGE_MASK) != 0) { return; @@ -698,122 +704,131 @@ CoreAddMemoryDescriptor ( CoreLoadingFixedAddressHook (); } - // - // Check to see if the statistics for the different memory types have already been established - // - if (mMemoryTypeInformationInitialized) { - return; - } - - // - // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array - // - for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - // - // Make sure the memory type in the gMemoryTypeInformation[] array is valid - // - Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); - if ((UINT32)Type > EfiMaxMemoryType) { - continue; - } - - if (gMemoryTypeInformation[Index].NumberOfPages != 0) { - // - // Allocate pages for the current memory type from the top of available memory - // - Status = CoreAllocatePages ( - AllocateAnyPages, - Type, - gMemoryTypeInformation[Index].NumberOfPages, - &mMemoryTypeStatistics[Type].BaseAddress - ); - if (EFI_ERROR (Status)) { - // - // If an error occurs allocating the pages for the current memory type, then - // free all the pages allocates for the previous memory types and return. This - // operation with be retied when/if more memory is added to the system - // - for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) { - // - // Make sure the memory type in the gMemoryTypeInformation[] array is valid - // - Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[FreeIndex].Type); - if ((UINT32)Type > EfiMaxMemoryType) { - continue; - } - - if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) { - CoreFreePages ( - mMemoryTypeStatistics[Type].BaseAddress, - gMemoryTypeInformation[FreeIndex].NumberOfPages - ); - mMemoryTypeStatistics[Type].BaseAddress = 0; - mMemoryTypeStatistics[Type].MaximumAddress = MAX_ALLOC_ADDRESS; - } - } - - return; - } - - // - // Compute the address at the top of the current statistics - // - mMemoryTypeStatistics[Type].MaximumAddress = - mMemoryTypeStatistics[Type].BaseAddress + - LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1; - - // - // If the current base address is the lowest address so far, then update the default - // maximum address - // - if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { - mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; - } - } - } - - // - // There was enough system memory for all the the memory types were allocated. So, - // those memory areas can be freed for future allocations, and all future memory - // allocations can occur within their respective bins - // - for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - // - // Make sure the memory type in the gMemoryTypeInformation[] array is valid - // - Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); - if ((UINT32)Type > EfiMaxMemoryType) { - continue; - } - - if (gMemoryTypeInformation[Index].NumberOfPages != 0) { - CoreFreePages ( - mMemoryTypeStatistics[Type].BaseAddress, - gMemoryTypeInformation[Index].NumberOfPages - ); - mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; - gMemoryTypeInformation[Index].NumberOfPages = 0; - } - } - - // - // If the number of pages reserved for a memory type is 0, then all allocations for that type - // should be in the default range. - // - for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { - for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { - mMemoryTypeStatistics[Type].InformationIndex = Index; - } - } - - mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; - if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { - mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; - } - } - - mMemoryTypeInformationInitialized = TRUE; + // MU_CHANGE BEGIN: PEI Bins - Replaced inline bin allocation with shared function + // // + // // Check to see if the statistics for the different memory types have already been established + // // + // if (mMemoryTypeInformationInitialized) { + // return; + // } + // + // // + // // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array + // // + // for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // // + // // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // // + // Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); + // if ((UINT32)Type > EfiMaxMemoryType) { + // continue; + // } + // + // if (gMemoryTypeInformation[Index].NumberOfPages != 0) { + // // + // // Allocate pages for the current memory type from the top of available memory + // // + // Status = CoreAllocatePages ( + // AllocateAnyPages, + // Type, + // gMemoryTypeInformation[Index].NumberOfPages, + // &mMemoryTypeStatistics[Type].BaseAddress + // ); + // if (EFI_ERROR (Status)) { + // // + // // If an error occurs allocating the pages for the current memory type, then + // // free all the pages allocates for the previous memory types and return. This + // // operation with be retied when/if more memory is added to the system + // // + // for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) { + // // + // // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // // + // Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[FreeIndex].Type); + // if ((UINT32)Type > EfiMaxMemoryType) { + // continue; + // } + // + // if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) { + // CoreFreePages ( + // mMemoryTypeStatistics[Type].BaseAddress, + // gMemoryTypeInformation[FreeIndex].NumberOfPages + // ); + // mMemoryTypeStatistics[Type].BaseAddress = 0; + // mMemoryTypeStatistics[Type].MaximumAddress = MAX_ALLOC_ADDRESS; + // } + // } + // + // return; + // } + // + // // + // // Compute the address at the top of the current statistics + // // + // mMemoryTypeStatistics[Type].MaximumAddress = + // mMemoryTypeStatistics[Type].BaseAddress + + // LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1; + // + // // + // // If the current base address is the lowest address so far, then update the default + // // maximum address + // // + // if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { + // mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; + // } + // } + // } + // + // // + // // There was enough system memory for all the the memory types were allocated. So, + // // those memory areas can be freed for future allocations, and all future memory + // // allocations can occur within their respective bins + // // + // for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // // + // // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // // + // Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); + // if ((UINT32)Type > EfiMaxMemoryType) { + // continue; + // } + // + // if (gMemoryTypeInformation[Index].NumberOfPages != 0) { + // CoreFreePages ( + // mMemoryTypeStatistics[Type].BaseAddress, + // gMemoryTypeInformation[Index].NumberOfPages + // ); + // mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; + // gMemoryTypeInformation[Index].NumberOfPages = 0; + // } + // } + // + // // + // // If the number of pages reserved for a memory type is 0, then all allocations for that type + // // should be in the default range. + // // + // for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + // for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { + // mMemoryTypeStatistics[Type].InformationIndex = Index; + // } + // } + // + // mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; + // if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { + // mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; + // } + // } + // + // mMemoryTypeInformationInitialized = TRUE; + // Check if we need to allocate the memory bins. This function will immediately return if we have already done so. + AllocateMemoryTypeInformationBins ( + &mMemoryTypeInformationInitialized, + gMemoryTypeInformation, + mMemoryTypeStatistics, + &mDefaultMaximumAddress + ); + // MU_CHANGE END: PEI Bins } /** @@ -938,31 +953,44 @@ CoreConvertPagesEx ( return EFI_NOT_FOUND; } - // - // Update counters for the number of pages allocated to each memory type - // - if ((UINT32)Entry->Type < EfiMaxMemoryType) { - if (((Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress) && (Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress)) || - ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) - { - if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) { - mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0; - } else { - mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages; - } - } - } - - if ((UINT32)NewType < EfiMaxMemoryType) { - if (((Start >= mMemoryTypeStatistics[NewType].BaseAddress) && (Start <= mMemoryTypeStatistics[NewType].MaximumAddress)) || - ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) - { - mMemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages; - if (mMemoryTypeStatistics[NewType].CurrentNumberOfPages > gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages) { - gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)mMemoryTypeStatistics[NewType].CurrentNumberOfPages; - } - } - } + // MU_CHANGE BEGIN: PEI Bins - Replaced inline memory statistics update with shared function + // // + // // Update counters for the number of pages allocated to each memory type + // // + // if ((UINT32)Entry->Type < EfiMaxMemoryType) { + // if (((Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress) && (Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress)) || + // ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) + // { + // if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) { + // mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0; + // } else { + // mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages; + // } + // } + // } + // + // if ((UINT32)NewType < EfiMaxMemoryType) { + // if (((Start >= mMemoryTypeStatistics[NewType].BaseAddress) && (Start <= mMemoryTypeStatistics[NewType].MaximumAddress)) || + // ((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) + // { + // mMemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages; + // if (mMemoryTypeStatistics[NewType].CurrentNumberOfPages > gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages) { + // gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)mMemoryTypeStatistics[NewType].CurrentNumberOfPages; + // } + // } + // } + UpdateMemoryStatistics ( + Entry->Type, + NewType, + Start, + (UINTN)NumberOfPages, + &mMemoryTypeInformationInitialized, + mMemoryTypeStatistics, + gMemoryTypeInformation, + mDefaultBaseAddress, + mDefaultMaximumAddress + ); + // MU_CHANGE END: PEI Bins } // diff --git a/MdeModulePkg/Core/PrivateInclude/MemoryBin.h b/MdeModulePkg/Core/PrivateInclude/MemoryBin.h new file mode 100644 index 00000000000..13d858ef7d9 --- /dev/null +++ b/MdeModulePkg/Core/PrivateInclude/MemoryBin.h @@ -0,0 +1,145 @@ +// MU_CHANGE: PEI Bins - Whole File +/** @file + Shared logic between cores to work with memory bins for S4 resume stability. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#pragma once + +#include + +/** + Calculate total memory bin size needed. + + @param BinTop The top address of the memory bins. This is an optional parameter. + If non-zero, alignment requirements will be considered in the calculation. + @param MemoryTypeInformation The memory type information array. + + @return The total memory bin size needed. + +**/ +UINT64 +CalculateTotalMemoryBinSizeNeeded ( + IN UINTN BinTop, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ); + +/** + Get the Memory Type Information HOB if it exists and populate gMemoryTypeInformation. + + @param MemoryTypeInformation The pointer to the memory type information array to be populated. + + @return EFI_STATUS On EFI_SUCCESS, gMemoryTypeInformation points to the + Memory Type Information. + @return EFI_NOT_FOUND No valid Memory Type Information HOB found. +**/ +EFI_STATUS +EFIAPI +PopulateMemoryTypeInformation ( + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ); + +/** + Look for Resource Descriptor HOB with a ResourceType of System Memory + and an Owner GUID of gEfiMemoryTypeInformationGuid. If more than 1 is + found, then return NULL. + + @param HobStart Pointer to the start of the HOB list. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + + @return Non-NULL The pointer to the singular MemoryTypeInformation Resource Descriptor HOB. + @return NULL No valid MemoryTypeInformation Resource Descriptor HOB found. +**/ +EFI_HOB_RESOURCE_DESCRIPTOR * +EFIAPI +GetMemoryTypeInformationResourceHob ( + IN VOID **HobStart, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ); + +/** + Sets the preferred memory range to use for the Memory Type Information bins. + This service must be called before fist call to CoreAddMemoryDescriptor(). + + If the location of the Memory Type Information bins has already been + established or the size of the range provides is smaller than all the + Memory Type Information bins, then the range provides is not used. + + @param Start The start address of the Memory Type Information range. + @param Length The size, in bytes, of the Memory Type Information range. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeStatistics The memory type statistics array to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. +**/ +VOID +EFIAPI +CoreSetMemoryTypeInformationRange ( + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + ); + +/** + Allocate memory bins for each memory type as specified in gMemoryTypeInformation. + + If all the memory types cannot be allocated, then all previously allocated + memory types are freed and the function returns. If this function fails, it will log and expect to be called + again when more memory is added to the system. + + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeStatistics The memory type statistics array to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. +**/ +VOID +EFIAPI +AllocateMemoryTypeInformationBins ( + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + ); + +/** + Update memory type statistics upon memory allocation and free. + + @param OldType The original memory type of the memory region. + @param NewType The new memory type of the memory region. + @param Start The starting physical address of the memory region. + @param NumberOfPages The number of pages in the memory region. + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeStatistics The memory type statistics array to be updated. + @param MemoryTypeInformation The memory type information array to be updated. + @param DefaultBaseAddress Default bin base address. + @param DefaultMaximumAddress Default bin maximum address. +**/ +VOID +EFIAPI +UpdateMemoryStatistics ( + IN EFI_MEMORY_TYPE OldType, + IN EFI_MEMORY_TYPE NewType, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINTN NumberOfPages, + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_PHYSICAL_ADDRESS DefaultBaseAddress, + IN EFI_PHYSICAL_ADDRESS DefaultMaximumAddress + ); diff --git a/MdeModulePkg/Include/Guid/MemoryTypeInformation.h b/MdeModulePkg/Include/Guid/MemoryTypeInformation.h index d1bb7d985d4..bfea243bcb8 100644 --- a/MdeModulePkg/Include/Guid/MemoryTypeInformation.h +++ b/MdeModulePkg/Include/Guid/MemoryTypeInformation.h @@ -38,4 +38,19 @@ typedef struct { UINT32 NumberOfPages; ///< The pages of this type memory. } EFI_MEMORY_TYPE_INFORMATION; +// MU_CHANGE BEGIN: PEI Bins +// +// Entry in an array that keeps track of memory type statistics per memory bin +// +typedef struct { + EFI_PHYSICAL_ADDRESS BaseAddress; + EFI_PHYSICAL_ADDRESS MaximumAddress; + UINT64 CurrentNumberOfPages; + UINT64 NumberOfPages; + UINTN InformationIndex; + BOOLEAN Special; + BOOLEAN Runtime; +} EFI_MEMORY_TYPE_STATISTICS; +// MU_CHANGE END: PEI Bins + #endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index c29e03fa94d..b888f2317db 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -29,6 +29,7 @@ [Includes.Common.Private] Library/BrotliCustomDecompressLib/brotli/c/include + Core/PrivateInclude # MU_CHANGE: PEI Bins [LibraryClasses] # MU_CHANGE [BEGIN] - Isolate the VariablePolicy locking event into its own code. From a887a3d0a04528dd45e1776b3334937387156dc4 Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Wed, 28 Jan 2026 15:21:15 -0800 Subject: [PATCH 3/9] MdeModulePkg: MemoryBin: Allocate Bins Contiguously Currently, there is no guarantee that the memory bins will be allocated contiguously. However, this is a requirement to use the resource descriptor HOB that describes the memory bin range. This commit updates AllocateMemoryTypeInformationBins() to allocate a contiguous range. Signed-off-by: Oliver Smith-Denny --- MdeModulePkg/Core/Dxe/Mem/MemoryBin.c | 158 ++++++-------------------- 1 file changed, 37 insertions(+), 121 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c index 694cfad946b..47f2ba05923 100644 --- a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c +++ b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -44,51 +43,6 @@ EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ EFI_RESOURCE_ATTRIBUTE_TESTED ) -/** - Allocates pages from the memory map. - - @param Type The type of allocation to perform - @param MemoryType The type of memory to turn the allocated pages - into - @param NumberOfPages The number of pages to allocate - @param Memory A pointer to receive the base allocated memory - address - - @return Status. On success, Memory is filled in with the base address allocated - @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in - spec. - @retval EFI_NOT_FOUND Could not allocate pages match the requirement. - @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. - @retval EFI_SUCCESS Pages successfully allocated. - -**/ -EFI_STATUS -EFIAPI -CoreAllocatePages ( - IN EFI_ALLOCATE_TYPE Type, - IN EFI_MEMORY_TYPE MemoryType, - IN UINTN NumberOfPages, - IN OUT EFI_PHYSICAL_ADDRESS *Memory - ); - -/** - Frees previous allocated pages. - - @param Memory Base address of memory being freed - @param NumberOfPages The number of pages to free - - @retval EFI_NOT_FOUND Could not find the entry that covers the range - @retval EFI_INVALID_PARAMETER Address not aligned - @return EFI_SUCCESS -Pages successfully freed. - -**/ -EFI_STATUS -EFIAPI -CoreFreePages ( - IN EFI_PHYSICAL_ADDRESS Memory, - IN UINTN NumberOfPages - ); - /** Calculate total memory bin size needed. @@ -472,12 +426,11 @@ AllocateMemoryTypeInformationBins ( IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress ) { - EFI_STATUS Status; - UINTN Index; - UINTN FreeIndex; - UINT64 Alignment; - UINT64 BinSize; - EFI_MEMORY_TYPE Type; + UINTN Index; + EFI_MEMORY_TYPE Type; + EFI_PHYSICAL_ADDRESS BaseAddress; + EFI_PHYSICAL_ADDRESS LastBinAddress; + UINT64 RequiredSize; if ((MemoryTypeInformationInitialized == NULL) || (MemoryTypeInformation == NULL) || @@ -496,6 +449,31 @@ AllocateMemoryTypeInformationBins ( return; } + BaseAddress = 0; + RequiredSize = CalculateTotalMemoryBinSizeNeeded (0, MemoryTypeInformation); + if (RequiredSize == 0) { + *MemoryTypeInformationInitialized = TRUE; + return; + } + + DEBUG ((DEBUG_INFO, "%a: Attempting to allocate 0x%llx bytes for all memory bins\n", __func__, RequiredSize)); + + // To ensure we get a contiguous range of memory for our bins, we will attempt to allocate + // all of the memory needed in one go. If that works, we can then carve it up into the individual bins. We allocate + // reserved pages to ensure runtime page allocation granularity is taken into account. + BaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedPages ( + (UINTN)RShiftU64 (RequiredSize, EFI_PAGE_SHIFT) + ); + + if (BaseAddress == 0) { + DEBUG ((DEBUG_ERROR, "%a: Could not allocate contiguous pages for all memory bins\n", __func__)); + ASSERT (FALSE); + return; + } + + LastBinAddress = BaseAddress + RequiredSize; + *DefaultMaximumAddress = BaseAddress - 1; + // // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array // @@ -509,71 +487,9 @@ AllocateMemoryTypeInformationBins ( } if (MemoryTypeInformation[Index].NumberOfPages != 0) { - Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; - if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || - (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || - (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || - (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) - { - Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; - } - - BinSize = EFI_PAGES_TO_SIZE ((UINTN)MemoryTypeInformation[Index].NumberOfPages); - BinSize = ALIGN_VALUE (BinSize, Alignment); - - MemoryTypeInformation[Index].NumberOfPages = (UINT32)EFI_SIZE_TO_PAGES ((UINTN)BinSize); - - // - // Allocate pages for the current memory type from the top of available memory - // - Status = CoreAllocatePages ( - AllocateAnyPages, - Type, - MemoryTypeInformation[Index].NumberOfPages, - &MemoryTypeStatistics[Type].BaseAddress - ); - if (EFI_ERROR (Status)) { - // - // If an error occurs allocating the pages for the current memory type, then - // free all the pages allocates for the previous memory types and return. This - // operation with be retied when/if more memory is added to the system - // - for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) { - // - // Make sure the memory type in the gMemoryTypeInformation[] array is valid - // - Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[FreeIndex].Type); - if ((UINT32)Type > EfiMaxMemoryType) { - continue; - } - - if (MemoryTypeInformation[FreeIndex].NumberOfPages != 0) { - CoreFreePages ( - MemoryTypeStatistics[Type].BaseAddress, - MemoryTypeInformation[FreeIndex].NumberOfPages - ); - MemoryTypeStatistics[Type].BaseAddress = 0; - MemoryTypeStatistics[Type].MaximumAddress = MAX_ALLOC_ADDRESS; - } - } - - return; - } - - // - // Compute the address at the top of the current statistics - // - MemoryTypeStatistics[Type].MaximumAddress = - MemoryTypeStatistics[Type].BaseAddress + - LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1; - - // - // If the current base address is the lowest address so far, then update the default - // maximum address - // - if (MemoryTypeStatistics[Type].BaseAddress < *DefaultMaximumAddress) { - *DefaultMaximumAddress = MemoryTypeStatistics[Type].BaseAddress - 1; - } + MemoryTypeStatistics[Type].BaseAddress = LastBinAddress - LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); + MemoryTypeStatistics[Type].MaximumAddress = LastBinAddress - 1; + LastBinAddress = MemoryTypeStatistics[Type].BaseAddress; } } @@ -582,6 +498,10 @@ AllocateMemoryTypeInformationBins ( // those memory areas can be freed for future allocations, and all future memory // allocations can occur within their respective bins // + FreePages ( + (VOID *)(UINTN)BaseAddress, + (UINTN)RShiftU64 (RequiredSize, EFI_PAGE_SHIFT) + ); for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { // // Make sure the memory type in the MemoryTypeInformation[] array is valid @@ -592,10 +512,6 @@ AllocateMemoryTypeInformationBins ( } if (MemoryTypeInformation[Index].NumberOfPages != 0) { - CoreFreePages ( - MemoryTypeStatistics[Type].BaseAddress, - MemoryTypeInformation[Index].NumberOfPages - ); MemoryTypeStatistics[Type].NumberOfPages = MemoryTypeInformation[Index].NumberOfPages; MemoryTypeInformation[Index].NumberOfPages = 0; } From 44de46e47ed00e8771b3c223e1ea3ee975f3c796 Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Wed, 28 Jan 2026 15:32:45 -0800 Subject: [PATCH 4/9] MdeModulePkg: MemoryBin: Use CalculateTotalMemoryBinSizeNeeded() CoreSetMemoryTypeInformationRange() currently calculates the bin size needed independently from the CalculateTotalMemoryBinSizeNeeded() function. This commit updates to use that function and remove the duplication. Signed-off-by: Oliver Smith-Denny --- MdeModulePkg/Core/Dxe/Mem/MemoryBin.c | 61 ++------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c index 47f2ba05923..effbe50760a 100644 --- a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c +++ b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c @@ -268,8 +268,6 @@ CoreSetMemoryTypeInformationRange ( EFI_MEMORY_TYPE Type; UINTN Index; UINT64 Size; - UINT64 Alignment; - UINT64 BinSize; if ((MemoryTypeInformation == NULL) || (MemoryTypeInformationInitialized == NULL) || @@ -292,45 +290,7 @@ CoreSetMemoryTypeInformationRange ( // // Return if size of the Memory Type Information bins is greater than Length // - Top = Start + Length; - Size = 0; - for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - // - // Make sure the memory type in the gMemoryTypeInformation[] array is valid - // - Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); - if ((UINT32)Type > EfiMaxMemoryType) { - continue; - } - - if (MemoryTypeInformation[Index].NumberOfPages != 0) { - Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; - if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || - (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || - (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || - (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) - { - Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; - } - - BinSize = EFI_PAGES_TO_SIZE ((UINTN)MemoryTypeInformation[Index].NumberOfPages); - BinSize = ALIGN_VALUE (BinSize, Alignment); - - Size += BinSize; - if (Size > Length) { - return; - } - - Top -= BinSize; - - Size += (Top & (Alignment - 1)); - if (Size > Length) { - return; - } - - Top &= ~(Alignment - 1); - } - } + Size = CalculateTotalMemoryBinSizeNeeded ((UINTN)(Start + Length), MemoryTypeInformation); if (Size > Length) { return; @@ -351,22 +311,9 @@ CoreSetMemoryTypeInformationRange ( } if (MemoryTypeInformation[Index].NumberOfPages != 0) { - Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; - if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || - (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || - (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || - (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) - { - Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; - } - - BinSize = EFI_PAGES_TO_SIZE ((UINTN)MemoryTypeInformation[Index].NumberOfPages); - BinSize = ALIGN_VALUE (BinSize, Alignment); - - Top = (Top - BinSize) & ~(Alignment - 1); - + MemoryTypeStatistics[Type].MaximumAddress = Top - 1; + Top -= LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); MemoryTypeStatistics[Type].BaseAddress = Top; - MemoryTypeStatistics[Type].MaximumAddress = Top + BinSize - 1; // // If the current base address is the lowest address so far, then update @@ -376,7 +323,7 @@ CoreSetMemoryTypeInformationRange ( *DefaultMaximumAddress = MemoryTypeStatistics[Type].BaseAddress - 1; } - MemoryTypeStatistics[Type].NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)BinSize); + MemoryTypeStatistics[Type].NumberOfPages = MemoryTypeInformation[Index].NumberOfPages; MemoryTypeInformation[Index].NumberOfPages = 0; } } From cefe3ff62e315b05cb53ad8f74ffa2b7fed80403 Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Mon, 30 Mar 2026 10:48:32 -0700 Subject: [PATCH 5/9] MdeModulePkg: MemoryBin: Add DefaultBin Field to Stats In order to prepare for PEI core to use the memory bin feature, add a DefaultBin field to the stats structure to track which bins simply point to the default bin. This also adds a helper function to initialize the statistics. Signed-off-by: Oliver Smith-Denny --- MdeModulePkg/Core/Dxe/Mem/MemoryBin.c | 79 +++++++++++-------- MdeModulePkg/Core/Dxe/Mem/Page.c | 36 +++++---- .../Include/Guid/MemoryTypeInformation.h | 16 ++-- 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c index effbe50760a..9d74b76ff9c 100644 --- a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c +++ b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c @@ -234,6 +234,51 @@ GetMemoryTypeInformationResourceHob ( return MemoryTypeInformationResourceHob; } +/** + Helper function to set up the bin statistics with the provided bin range + + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeStatistics The memory type statistics to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. +**/ +STATIC +VOID +InitializeBinStatisticsFromRange ( + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + ) +{ + EFI_MEMORY_TYPE Type; + UINTN Index; + + if ((MemoryTypeInformation == NULL) || (MemoryTypeStatistics == NULL) || (DefaultMaximumAddress == NULL)) { + ASSERT (FALSE); + return; + } + + // + // If the number of pages reserved for a memory type is 0, then all + // allocations for that type should be in the default range. + // + for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + if (Type == (EFI_MEMORY_TYPE)MemoryTypeInformation[Index].Type) { + MemoryTypeStatistics[Type].InformationIndex = Index; + } + } + + MemoryTypeStatistics[Type].CurrentNumberOfPages = 0; + if (MemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { + MemoryTypeStatistics[Type].MaximumAddress = *DefaultMaximumAddress; + MemoryTypeStatistics[Type].DefaultBin = TRUE; + } + } +} + /** Sets the preferred memory range to use for the Memory Type Information bins. This service must be called before fist call to CoreAddMemoryDescriptor(). @@ -328,22 +373,7 @@ CoreSetMemoryTypeInformationRange ( } } - // - // If the number of pages reserved for a memory type is 0, then all - // allocations for that type should be in the default range. - // - for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { - for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - if (Type == (EFI_MEMORY_TYPE)MemoryTypeInformation[Index].Type) { - MemoryTypeStatistics[Type].InformationIndex = Index; - } - } - - MemoryTypeStatistics[Type].CurrentNumberOfPages = 0; - if (MemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { - MemoryTypeStatistics[Type].MaximumAddress = *DefaultMaximumAddress; - } - } + InitializeBinStatisticsFromRange (MemoryTypeInformation, MemoryTypeStatistics, DefaultMaximumAddress); *MemoryTypeInformationInitialized = TRUE; } @@ -464,22 +494,7 @@ AllocateMemoryTypeInformationBins ( } } - // - // If the number of pages reserved for a memory type is 0, then all allocations for that type - // should be in the default range. - // - for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { - for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { - if (Type == (EFI_MEMORY_TYPE)MemoryTypeInformation[Index].Type) { - MemoryTypeStatistics[Type].InformationIndex = Index; - } - } - - MemoryTypeStatistics[Type].CurrentNumberOfPages = 0; - if (MemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { - MemoryTypeStatistics[Type].MaximumAddress = *DefaultMaximumAddress; - } - } + InitializeBinStatisticsFromRange (MemoryTypeInformation, MemoryTypeStatistics, DefaultMaximumAddress); *MemoryTypeInformationInitialized = TRUE; } diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index f7239769c3f..755056d34f3 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -48,25 +48,27 @@ UINTN mFreeMapStack = 0; LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList); BOOLEAN mMemoryTypeInformationInitialized = FALSE; +// MU_CHANGE BEGIN: PEI Bins - Added DefaultBin field to struct initializers EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = { - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiReservedMemoryType - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderCode - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderData - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesCode - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesData - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesCode - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesData - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiConventionalMemory - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiUnusableMemory - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIReclaimMemory - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIMemoryNVS - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIO - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiPalCode - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiPersistentMemory - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiUnacceptedMemoryType - { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE } // EfiMaxMemoryType + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiReservedMemoryType + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiLoaderCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiLoaderData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiBootServicesCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiBootServicesData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE, FALSE }, // EfiRuntimeServicesCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE, FALSE }, // EfiRuntimeServicesData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiConventionalMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiUnusableMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiACPIReclaimMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiACPIMemoryNVS + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiMemoryMappedIO + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE, FALSE }, // EfiPalCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiPersistentMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiUnacceptedMemoryType + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE } // EfiMaxMemoryType }; +// MU_CHANGE END: PEI Bins EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = MAX_ALLOC_ADDRESS; EFI_PHYSICAL_ADDRESS mDefaultBaseAddress = MAX_ALLOC_ADDRESS; diff --git a/MdeModulePkg/Include/Guid/MemoryTypeInformation.h b/MdeModulePkg/Include/Guid/MemoryTypeInformation.h index bfea243bcb8..b1348f78030 100644 --- a/MdeModulePkg/Include/Guid/MemoryTypeInformation.h +++ b/MdeModulePkg/Include/Guid/MemoryTypeInformation.h @@ -43,13 +43,15 @@ typedef struct { // Entry in an array that keeps track of memory type statistics per memory bin // typedef struct { - EFI_PHYSICAL_ADDRESS BaseAddress; - EFI_PHYSICAL_ADDRESS MaximumAddress; - UINT64 CurrentNumberOfPages; - UINT64 NumberOfPages; - UINTN InformationIndex; - BOOLEAN Special; - BOOLEAN Runtime; + EFI_PHYSICAL_ADDRESS BaseAddress; ///< The base address of the memory bin. + EFI_PHYSICAL_ADDRESS MaximumAddress; ///< The maximum address of the memory bin. + UINT64 CurrentNumberOfPages; ///< The current number of pages allocated. + UINT64 NumberOfPages; ///< The total number of pages in the bin. + UINTN InformationIndex; ///< The index into the EFI_MEMORY_TYPE_INFORMATION array. + BOOLEAN Special; ///< Indicates if this memory type persists into the OS runtime. + BOOLEAN Runtime; ///< Indicates if this memory type should have the EFI_MEMORY_RUNTIME + ///< attribute applied in the EFI_MEMORY_MAP. + BOOLEAN DefaultBin; ///< Indicates if this memory type is the default bin. } EFI_MEMORY_TYPE_STATISTICS; // MU_CHANGE END: PEI Bins From e2ca921c6a31158f6ef15beae066d156fd6caf1c Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Wed, 11 Mar 2026 14:24:55 -0700 Subject: [PATCH 6/9] MdeModulePkg: MemoryBin: Add Bin Resource Desc Hob Generation This commit adds a parameter to the memory bin allocation function to tell it whether it should create the Resource Descriptor HOB owned by gMemoryTypeInformationGuid. This will be used by PEI to tell DXE where the memory bins are. Signed-off-by: Oliver Smith-Denny --- MdeModulePkg/Core/Dxe/Mem/MemoryBin.c | 19 ++++++++++++++++++- MdeModulePkg/Core/Dxe/Mem/Page.c | 4 +++- MdeModulePkg/Core/PrivateInclude/MemoryBin.h | 6 +++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c index 9d74b76ff9c..a7a13d7c42d 100644 --- a/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c +++ b/MdeModulePkg/Core/Dxe/Mem/MemoryBin.c @@ -393,6 +393,9 @@ CoreSetMemoryTypeInformationRange ( information if the provided range is used. @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the provided range is used. + @param CreateHob TRUE to create Memory Type Information Resource HOB after successful + allocation. This is used for PEI Core to report the bins to DXE Core. + FALSE if HOB creation is not needed. **/ VOID EFIAPI @@ -400,7 +403,8 @@ AllocateMemoryTypeInformationBins ( IN BOOLEAN *MemoryTypeInformationInitialized, IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, - IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress, + IN BOOLEAN CreateHob ) { UINTN Index; @@ -496,6 +500,19 @@ AllocateMemoryTypeInformationBins ( InitializeBinStatisticsFromRange (MemoryTypeInformation, MemoryTypeStatistics, DefaultMaximumAddress); + if (CreateHob) { + // + // Create a Resource Descriptor HOB to report the Memory Type Information bins to DXE Core + // + BuildResourceDescriptorWithOwnerHob ( + EFI_RESOURCE_SYSTEM_MEMORY, + TESTED_MEMORY_ATTRIBUTES, + BaseAddress, + RequiredSize, + &gEfiMemoryTypeInformationGuid + ); + } + *MemoryTypeInformationInitialized = TRUE; } diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index 755056d34f3..446c568e7e0 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -824,11 +824,13 @@ CoreAddMemoryDescriptor ( // // mMemoryTypeInformationInitialized = TRUE; // Check if we need to allocate the memory bins. This function will immediately return if we have already done so. + // Pass FALSE to indicate we don't need to publish the HOB. AllocateMemoryTypeInformationBins ( &mMemoryTypeInformationInitialized, gMemoryTypeInformation, mMemoryTypeStatistics, - &mDefaultMaximumAddress + &mDefaultMaximumAddress, + FALSE ); // MU_CHANGE END: PEI Bins } diff --git a/MdeModulePkg/Core/PrivateInclude/MemoryBin.h b/MdeModulePkg/Core/PrivateInclude/MemoryBin.h index 13d858ef7d9..6c0cf77343b 100644 --- a/MdeModulePkg/Core/PrivateInclude/MemoryBin.h +++ b/MdeModulePkg/Core/PrivateInclude/MemoryBin.h @@ -106,6 +106,9 @@ CoreSetMemoryTypeInformationRange ( information if the provided range is used. @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the provided range is used. + @param CreateHob TRUE to create Memory Type Information Resource HOB after successful + allocation. This is used for PEI Core to report the bins to DXE Core. + FALSE if HOB creation is not needed. **/ VOID EFIAPI @@ -113,7 +116,8 @@ AllocateMemoryTypeInformationBins ( IN BOOLEAN *MemoryTypeInformationInitialized, IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, - IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress, + IN BOOLEAN CreateHob ); /** From 1fa28439211984b14b1424a578eef3f3aeaaac4a Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Mon, 30 Mar 2026 10:58:32 -0700 Subject: [PATCH 7/9] MdeModulePkg: PeiCore: Add Memory Bin Support to Post-Mem PEI This commit adds opt-in support for post-mem PEI memory bins. See the README for full details. MemoryBin.c is duplicated to PeiCore per request. Signed-off-by: Oliver Smith-Denny --- MdeModulePkg/Core/Pei/Memory/MemoryBin.c | 587 ++++++++++++++++++ MdeModulePkg/Core/Pei/Memory/MemoryServices.c | 438 ++++++++++--- MdeModulePkg/Core/Pei/PeiMain.h | 20 + MdeModulePkg/Core/Pei/PeiMain.inf | 3 + MdeModulePkg/MdeModulePkg.dec | 14 + 5 files changed, 967 insertions(+), 95 deletions(-) create mode 100644 MdeModulePkg/Core/Pei/Memory/MemoryBin.c diff --git a/MdeModulePkg/Core/Pei/Memory/MemoryBin.c b/MdeModulePkg/Core/Pei/Memory/MemoryBin.c new file mode 100644 index 00000000000..a7a13d7c42d --- /dev/null +++ b/MdeModulePkg/Core/Pei/Memory/MemoryBin.c @@ -0,0 +1,587 @@ +// MU_CHANGE: PEI Bins - Whole File +/** @file + + Shared logic between cores to work with memory bins for S4 resume stability. This file is duplicated in PEI Core and + DXE Core until a BaseTools feature comes online to support recommended library instances. Any changes to this file + must also be made in MdeModulePkg/Core/Pei/Memory/MemoryBin.c. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define MEMORY_ATTRIBUTE_MASK (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED | \ + EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED | \ + EFI_RESOURCE_ATTRIBUTE_16_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_32_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_64_BIT_IO | \ + EFI_RESOURCE_ATTRIBUTE_PERSISTENT | \ + EFI_RESOURCE_ATTRIBUTE_SPECIAL_PURPOSE ) + +#define TESTED_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED ) + +/** + Calculate total memory bin size needed. + + @param BinTop The top address of the memory bins. This is an optional parameter. + If non-zero, alignment requirements will be considered in the calculation. + @param MemoryTypeInformation The memory type information array. + + @return The total memory bin size needed. + +**/ +UINT64 +CalculateTotalMemoryBinSizeNeeded ( + IN UINTN BinTop, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ) +{ + UINTN Index; + UINT64 TotalSize; + UINTN Granularity; + + if (MemoryTypeInformation == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return 0; + } + + // + // Loop through each memory type in the order specified by the MemoryTypeInformation[] array + // + TotalSize = 0; + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || + (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) + { + Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } + + // MemoryTypeInformation[Index].NumberOfPages is already aligned to the allocation granularity + TotalSize += LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); + + // BinTop is optional + if (BinTop == 0) { + continue; + } + + BinTop -= (UINTN)LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); + TotalSize += (BinTop & (Granularity - 1)); + BinTop &= ~(Granularity - 1); + } + + return TotalSize; +} + +/** + Get the Memory Type Information HOB if it exists and populate gMemoryTypeInformation. + + @param MemoryTypeInformation The pointer to the memory type information array to be populated. + + @return EFI_STATUS On EFI_SUCCESS, gMemoryTypeInformation points to the + Memory Type Information. + @return EFI_NOT_FOUND No valid Memory Type Information HOB found. +**/ +EFI_STATUS +EFIAPI +PopulateMemoryTypeInformation ( + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ) +{ + UINTN DataSize; + EFI_MEMORY_TYPE_INFORMATION *EfiMemoryTypeInformation; + EFI_HOB_GUID_TYPE *GuidHob; + UINTN Index; + UINT32 Granularity; + + if (MemoryTypeInformation == NULL) { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid); + if (GuidHob != NULL) { + EfiMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob); + DataSize = GET_GUID_HOB_DATA_SIZE (GuidHob); + if ((EfiMemoryTypeInformation != NULL) && (DataSize > 0) && (DataSize <= (EfiMaxMemoryType + 1) * sizeof (EFI_MEMORY_TYPE_INFORMATION))) { + CopyMem (MemoryTypeInformation, EfiMemoryTypeInformation, DataSize); + + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the MemoryTypeInformation[] array is valid + // + if (MemoryTypeInformation[Index].Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + if ((MemoryTypeInformation[Index].Type == EfiReservedMemoryType) || + (MemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) || + (MemoryTypeInformation[Index].Type == EfiRuntimeServicesData)) + { + Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + } else { + Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + } + + // Align the number of pages to the allocation granularity + MemoryTypeInformation[Index].NumberOfPages = (UINT32)RShiftU64 (ALIGN_VALUE (LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT), Granularity), EFI_PAGE_SHIFT); + } + } + + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_ERROR, "%a: Invalid Memory Type Information HOB data\n", __func__)); + ASSERT (FALSE); + } + + DEBUG ((DEBUG_ERROR, "%a: No Memory Type Information HOB found\n", __func__)); + + return EFI_NOT_FOUND; +} + +/** + Look for Resource Descriptor HOB with a ResourceType of System Memory + and an Owner GUID of gEfiMemoryTypeInformationGuid. If more than 1 is + found, then return NULL. + + @param HobStart Pointer to the start of the HOB list. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + + @return Non-NULL The pointer to the singular MemoryTypeInformation Resource Descriptor HOB. + @return NULL No valid MemoryTypeInformation Resource Descriptor HOB found. +**/ +EFI_HOB_RESOURCE_DESCRIPTOR * +EFIAPI +GetMemoryTypeInformationResourceHob ( + IN VOID **HobStart, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation + ) +{ + UINTN Count; + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + EFI_HOB_RESOURCE_DESCRIPTOR *MemoryTypeInformationResourceHob; + + if ((HobStart == NULL) || (MemoryTypeInformation == NULL)) { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return NULL; + } + + // + // See if a Memory Type Information HOB is available + // + MemoryTypeInformationResourceHob = NULL; + Count = 0; + for (Hob.Raw = *HobStart; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + if (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + continue; + } + + ResourceHob = Hob.ResourceDescriptor; + if (!CompareGuid (&ResourceHob->Owner, &gEfiMemoryTypeInformationGuid)) { + continue; + } + + Count++; + if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) { + continue; + } + + if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) { + continue; + } + + if (ResourceHob->ResourceLength >= CalculateTotalMemoryBinSizeNeeded ((UINTN)(ResourceHob->PhysicalStart + ResourceHob->ResourceLength), MemoryTypeInformation)) { + MemoryTypeInformationResourceHob = ResourceHob; + } + } + + if (Count > 1) { + return NULL; + } + + return MemoryTypeInformationResourceHob; +} + +/** + Helper function to set up the bin statistics with the provided bin range + + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeStatistics The memory type statistics to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. +**/ +STATIC +VOID +InitializeBinStatisticsFromRange ( + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + ) +{ + EFI_MEMORY_TYPE Type; + UINTN Index; + + if ((MemoryTypeInformation == NULL) || (MemoryTypeStatistics == NULL) || (DefaultMaximumAddress == NULL)) { + ASSERT (FALSE); + return; + } + + // + // If the number of pages reserved for a memory type is 0, then all + // allocations for that type should be in the default range. + // + for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + if (Type == (EFI_MEMORY_TYPE)MemoryTypeInformation[Index].Type) { + MemoryTypeStatistics[Type].InformationIndex = Index; + } + } + + MemoryTypeStatistics[Type].CurrentNumberOfPages = 0; + if (MemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { + MemoryTypeStatistics[Type].MaximumAddress = *DefaultMaximumAddress; + MemoryTypeStatistics[Type].DefaultBin = TRUE; + } + } +} + +/** + Sets the preferred memory range to use for the Memory Type Information bins. + This service must be called before fist call to CoreAddMemoryDescriptor(). + + If the location of the Memory Type Information bins has already been + established or the size of the range provides is smaller than all the + Memory Type Information bins, then the range provides is not used. + + @param Start The start address of the Memory Type Information range. + @param Length The size, in bytes, of the Memory Type Information range. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeStatistics The memory type statistics array to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. +**/ +VOID +EFIAPI +CoreSetMemoryTypeInformationRange ( + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress + ) +{ + EFI_PHYSICAL_ADDRESS Top; + EFI_MEMORY_TYPE Type; + UINTN Index; + UINT64 Size; + + if ((MemoryTypeInformation == NULL) || + (MemoryTypeInformationInitialized == NULL) || + (MemoryTypeStatistics == NULL) || + (DefaultMaximumAddress == NULL)) + { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return; + } + + // + // Return if Memory Type Information bin locations have already been set + // + if (*MemoryTypeInformationInitialized) { + DEBUG ((DEBUG_ERROR, "%a: Ignored. Bins already set.\n", __func__)); + return; + } + + // + // Return if size of the Memory Type Information bins is greater than Length + // + Size = CalculateTotalMemoryBinSizeNeeded ((UINTN)(Start + Length), MemoryTypeInformation); + + if (Size > Length) { + return; + } + + // + // Loop through each memory type in the order specified by the + // gMemoryTypeInformation[] array + // + Top = Start + Length; + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the MemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + MemoryTypeStatistics[Type].MaximumAddress = Top - 1; + Top -= LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); + MemoryTypeStatistics[Type].BaseAddress = Top; + + // + // If the current base address is the lowest address so far, then update + // the default maximum address + // + if (MemoryTypeStatistics[Type].BaseAddress < *DefaultMaximumAddress) { + *DefaultMaximumAddress = MemoryTypeStatistics[Type].BaseAddress - 1; + } + + MemoryTypeStatistics[Type].NumberOfPages = MemoryTypeInformation[Index].NumberOfPages; + MemoryTypeInformation[Index].NumberOfPages = 0; + } + } + + InitializeBinStatisticsFromRange (MemoryTypeInformation, MemoryTypeStatistics, DefaultMaximumAddress); + + *MemoryTypeInformationInitialized = TRUE; +} + +/** + Allocate memory bins for each memory type as specified in gMemoryTypeInformation. + + If all the memory types cannot be allocated, then all previously allocated + memory types are freed and the function returns. If this function fails, it will log and expect to be called + again when more memory is added to the system. + + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeInformation The memory type information array to be used to determine + the size of the memory bins. + @param MemoryTypeStatistics The memory type statistics array to be updated with the memory bin + information if the provided range is used. + @param DefaultMaximumAddress A pointer to the default maximum address to be updated if the + provided range is used. + @param CreateHob TRUE to create Memory Type Information Resource HOB after successful + allocation. This is used for PEI Core to report the bins to DXE Core. + FALSE if HOB creation is not needed. +**/ +VOID +EFIAPI +AllocateMemoryTypeInformationBins ( + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_PHYSICAL_ADDRESS *DefaultMaximumAddress, + IN BOOLEAN CreateHob + ) +{ + UINTN Index; + EFI_MEMORY_TYPE Type; + EFI_PHYSICAL_ADDRESS BaseAddress; + EFI_PHYSICAL_ADDRESS LastBinAddress; + UINT64 RequiredSize; + + if ((MemoryTypeInformationInitialized == NULL) || + (MemoryTypeInformation == NULL) || + (MemoryTypeStatistics == NULL) || + (DefaultMaximumAddress == NULL)) + { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return; + } + + // + // Check to see if the statistics for the different memory types have already been established + // + if (*MemoryTypeInformationInitialized) { + return; + } + + BaseAddress = 0; + RequiredSize = CalculateTotalMemoryBinSizeNeeded (0, MemoryTypeInformation); + if (RequiredSize == 0) { + *MemoryTypeInformationInitialized = TRUE; + return; + } + + DEBUG ((DEBUG_INFO, "%a: Attempting to allocate 0x%llx bytes for all memory bins\n", __func__, RequiredSize)); + + // To ensure we get a contiguous range of memory for our bins, we will attempt to allocate + // all of the memory needed in one go. If that works, we can then carve it up into the individual bins. We allocate + // reserved pages to ensure runtime page allocation granularity is taken into account. + BaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedPages ( + (UINTN)RShiftU64 (RequiredSize, EFI_PAGE_SHIFT) + ); + + if (BaseAddress == 0) { + DEBUG ((DEBUG_ERROR, "%a: Could not allocate contiguous pages for all memory bins\n", __func__)); + ASSERT (FALSE); + return; + } + + LastBinAddress = BaseAddress + RequiredSize; + *DefaultMaximumAddress = BaseAddress - 1; + + // + // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array + // + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the gMemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + MemoryTypeStatistics[Type].BaseAddress = LastBinAddress - LShiftU64 (MemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT); + MemoryTypeStatistics[Type].MaximumAddress = LastBinAddress - 1; + LastBinAddress = MemoryTypeStatistics[Type].BaseAddress; + } + } + + // + // There was enough system memory for all the the memory types were allocated. So, + // those memory areas can be freed for future allocations, and all future memory + // allocations can occur within their respective bins + // + FreePages ( + (VOID *)(UINTN)BaseAddress, + (UINTN)RShiftU64 (RequiredSize, EFI_PAGE_SHIFT) + ); + for (Index = 0; MemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + // + // Make sure the memory type in the MemoryTypeInformation[] array is valid + // + Type = (EFI_MEMORY_TYPE)(MemoryTypeInformation[Index].Type); + if ((UINT32)Type > EfiMaxMemoryType) { + continue; + } + + if (MemoryTypeInformation[Index].NumberOfPages != 0) { + MemoryTypeStatistics[Type].NumberOfPages = MemoryTypeInformation[Index].NumberOfPages; + MemoryTypeInformation[Index].NumberOfPages = 0; + } + } + + InitializeBinStatisticsFromRange (MemoryTypeInformation, MemoryTypeStatistics, DefaultMaximumAddress); + + if (CreateHob) { + // + // Create a Resource Descriptor HOB to report the Memory Type Information bins to DXE Core + // + BuildResourceDescriptorWithOwnerHob ( + EFI_RESOURCE_SYSTEM_MEMORY, + TESTED_MEMORY_ATTRIBUTES, + BaseAddress, + RequiredSize, + &gEfiMemoryTypeInformationGuid + ); + } + + *MemoryTypeInformationInitialized = TRUE; +} + +/** + Update memory type statistics upon memory allocation and free. + + @param OldType The original memory type of the memory region. + @param NewType The new memory type of the memory region. + @param Start The starting physical address of the memory region. + @param NumberOfPages The number of pages in the memory region. + @param MemoryTypeInformationInitialized A pointer to a boolean that indicates whether the memory type + information bins have been initialized. + @param MemoryTypeStatistics The memory type statistics array to be updated. + @param MemoryTypeInformation The memory type information array to be updated. + @param DefaultBaseAddress Default bin base address. + @param DefaultMaximumAddress Default bin maximum address. +**/ +VOID +EFIAPI +UpdateMemoryStatistics ( + IN EFI_MEMORY_TYPE OldType, + IN EFI_MEMORY_TYPE NewType, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINTN NumberOfPages, + IN BOOLEAN *MemoryTypeInformationInitialized, + IN EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics, + IN EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation, + IN EFI_PHYSICAL_ADDRESS DefaultBaseAddress, + IN EFI_PHYSICAL_ADDRESS DefaultMaximumAddress + ) +{ + if ((MemoryTypeInformationInitialized == NULL) || + (MemoryTypeStatistics == NULL) || + (MemoryTypeInformation == NULL)) + { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter(s)\n", __func__)); + ASSERT (FALSE); + return; + } + + if (!*MemoryTypeInformationInitialized) { + return; + } + + // + // Update counters for the number of pages allocated to each memory type + // + if ((UINT32)OldType < EfiMaxMemoryType) { + if (((Start >= MemoryTypeStatistics[OldType].BaseAddress) && (Start <= MemoryTypeStatistics[OldType].MaximumAddress)) || + ((Start >= DefaultBaseAddress) && (Start <= DefaultMaximumAddress))) + { + if (NumberOfPages > MemoryTypeStatistics[OldType].CurrentNumberOfPages) { + MemoryTypeStatistics[OldType].CurrentNumberOfPages = 0; + } else { + MemoryTypeStatistics[OldType].CurrentNumberOfPages -= NumberOfPages; + } + } + } + + if ((UINT32)NewType < EfiMaxMemoryType) { + if (((Start >= MemoryTypeStatistics[NewType].BaseAddress) && (Start <= MemoryTypeStatistics[NewType].MaximumAddress)) || + ((Start >= DefaultBaseAddress) && (Start <= DefaultMaximumAddress))) + { + MemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages; + if ((MemoryTypeStatistics[NewType].InformationIndex < (UINTN)EfiMaxMemoryType) && + (MemoryTypeStatistics[NewType].CurrentNumberOfPages > MemoryTypeInformation[MemoryTypeStatistics[NewType].InformationIndex].NumberOfPages)) + { + MemoryTypeInformation[MemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)MemoryTypeStatistics[NewType].CurrentNumberOfPages; + } + } + } +} diff --git a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c index 80b2f898b18..746d63f63b8 100644 --- a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c +++ b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c @@ -7,6 +7,105 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PeiMain.h" +// MU_CHANGE BEGIN: PEI Bins +#include +#include +// MU_CHANGE END: PEI Bins + +// MU_CHANGE BEGIN: PEI Bins +/** + Initialize memory type information bins based on the memory type information HOB. + + @param[in] PrivateData Pointer to PEI core's private instance data. + @param[in] HobList Pointer to the memory type information HOB. +**/ +STATIC +VOID +InitializeMemoryTypeInformationBins ( + PEI_CORE_INSTANCE *PrivateData, + IN VOID **HobList + ) +{ + EFI_PHYSICAL_ADDRESS BaseBinAddress; + EFI_PHYSICAL_ADDRESS EndBinAddress; + EFI_PEI_HOB_POINTERS Hob; + UINTN Index; + EFI_HOB_RESOURCE_DESCRIPTOR *MemoryTypeInformationResourceHob; + + BaseBinAddress = 0; + EndBinAddress = 0; + + MemoryTypeInformationResourceHob = GetMemoryTypeInformationResourceHob (HobList, PrivateData->MemoryTypeInformation); + + if (MemoryTypeInformationResourceHob != NULL) { + // + // If a Memory Type Information Resource HOB was found, then use the address + // range of the Memory Type Information Resource HOB as the preferred + // address range for the Memory Type Information bins. + // PEI doesn't use the last param here, so we ignore it, but + // the API needs something passed in. + // + CoreSetMemoryTypeInformationRange ( + MemoryTypeInformationResourceHob->PhysicalStart, + MemoryTypeInformationResourceHob->ResourceLength, + PrivateData->MemoryTypeInformation, + &PrivateData->MemoryTypeInformationInitialized, + PrivateData->MemoryTypeStatistics, + &BaseBinAddress + ); + + goto Fixup_PHIT; + } + + // We don't have a Memory Type Information Resource HOB, so we will allocate memory to use for the bins + // and create the HOB ourselves to inform DXE this is where the bins live. + // PEI doesn't use the last param here, so we ignore it, but + // the API needs something passed in. + AllocateMemoryTypeInformationBins ( + &PrivateData->MemoryTypeInformationInitialized, + PrivateData->MemoryTypeInformation, + PrivateData->MemoryTypeStatistics, + &BaseBinAddress, + TRUE + ); + +Fixup_PHIT: + // Set these to 0, ignoring whatever was set here by the memory bin code, PEI doesn't use it + BaseBinAddress = 0; + + // At this point we have set up our memory bins, so we need to fix up the PHIT to reflect the + // new memory allocations. + Hob.Raw = ((EFI_PEI_HOB_POINTERS *)HobList)->Raw; + + if (Hob.Raw == NULL) { + // + // We shouldn't get here, bins won't work without a valid HOB list + // + DEBUG ((DEBUG_ERROR, "%a: No valid HOB list found, can't init memory bins\n", __func__)); + ASSERT (FALSE); + return; + } + + // Find max/min addresses of the bins + for (Index = 0; (EFI_MEMORY_TYPE)Index < EfiMaxMemoryType; Index++) { + if ((PrivateData->MemoryTypeStatistics[Index].NumberOfPages != 0) && !PrivateData->MemoryTypeStatistics[Index].DefaultBin) { + if ((PrivateData->MemoryTypeStatistics[Index].BaseAddress < BaseBinAddress) || (BaseBinAddress == 0)) { + BaseBinAddress = PrivateData->MemoryTypeStatistics[Index].BaseAddress; + } + + if (PrivateData->MemoryTypeStatistics[Index].MaximumAddress > EndBinAddress) { + EndBinAddress = PrivateData->MemoryTypeStatistics[Index].MaximumAddress; + } + } + } + + DEBUG ((DEBUG_INFO, "%a: Memory bins address range 0x%llx - 0x%llx\n", __func__, BaseBinAddress, EndBinAddress)); + // The bins may or may not intersect the PHIT + if ((Hob.HandoffInformationTable->EfiFreeMemoryTop > BaseBinAddress) && (Hob.HandoffInformationTable->EfiFreeMemoryBottom < EndBinAddress)) { + Hob.HandoffInformationTable->EfiFreeMemoryTop = BaseBinAddress; + } +} +// MU_CHANGE END: PEI Bins /** @@ -27,6 +126,12 @@ InitializeMemoryServices ( IN PEI_CORE_INSTANCE *OldCoreData ) { + // MU_CHANGE BEGIN: PEI Bins + VOID *HobList; + EFI_STATUS Status; + EFI_MEMORY_TYPE Type; + // MU_CHANGE END: PEI Bins + PrivateData->SwitchStackSignal = FALSE; // @@ -47,6 +152,69 @@ InitializeMemoryServices ( // Set Ps to point to ServiceTableShadow in Cache // PrivateData->Ps = &(PrivateData->ServiceTableShadow); + // MU_CHANGE BEGIN: PEI Bins + } else { + // We have permanent memory now, so we should set up the memory bins if we can. No matter what, initialize the + // structures + PrivateData->MemoryTypeInformationInitialized = FALSE; + PrivateData->MemoryTypeInformation = NULL; + PrivateData->MemoryTypeStatistics = NULL; + + // Check if the platform has opted in to the PEI memory bins feature + if (FeaturePcdGet (PcdPeiMemoryBinsEnable)) { + Status = PeiGetHobList ((CONST EFI_PEI_SERVICES **)&PrivateData->Ps, &HobList); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return; + } + + PrivateData->MemoryTypeInformation = AllocateZeroPool (sizeof (EFI_MEMORY_TYPE_INFORMATION) * (EfiMaxMemoryType + 1)); + PrivateData->MemoryTypeStatistics = AllocateZeroPool (sizeof (EFI_MEMORY_TYPE_STATISTICS) * (EfiMaxMemoryType + 1)); + + if ((PrivateData->MemoryTypeInformation == NULL) || (PrivateData->MemoryTypeStatistics == NULL)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate memory for PEI memory bins\n", __func__)); + ASSERT (FALSE); + return; + } + + for (Type = 0; Type < EfiMaxMemoryType; Type++) { + PrivateData->MemoryTypeInformation[Type].Type = Type; + + PrivateData->MemoryTypeStatistics[Type].InformationIndex = EfiMaxMemoryType; + + switch (Type) { + case EfiRuntimeServicesCode: + case EfiRuntimeServicesData: + case EfiPalCode: + PrivateData->MemoryTypeStatistics[Type].Runtime = TRUE; + // fall through + case EfiReservedMemoryType: + case EfiACPIReclaimMemory: + case EfiACPIMemoryNVS: + case EfiUnacceptedMemoryType: + PrivateData->MemoryTypeStatistics[Type].Special = TRUE; + break; + default: + break; + } + } + + Status = PopulateMemoryTypeInformation (PrivateData->MemoryTypeInformation); + if (EFI_ERROR (Status)) { + // + // Couldn't find the memory type information HOB, so we can't set up bins + // + DEBUG (( + DEBUG_ERROR, + "Memory Type Information HOB not found during memory services initialization but PPI was produced\n" + )); + ASSERT_EFI_ERROR (Status); + return; + } + + InitializeMemoryTypeInformationBins (PrivateData, &HobList); + } + // MU_CHANGE END: PEI Bins } return; @@ -57,9 +225,9 @@ InitializeMemoryServices ( 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. + This routine will store discovered memory information into PeiCore's private data, + and set the SwitchStackSignal flag. After the PEIM who discovered memory is dispatched, + the 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. @@ -266,15 +434,26 @@ ConvertMemoryAllocationHobs ( @param[in] MemoryType The type of memory allocated by this HOB. **/ +// MU_CHANGE BEGIN: PEI Bins - Added PeiServices parameter and reworked HOB creation logic VOID InternalBuildMemoryAllocationHob ( - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length, - IN EFI_MEMORY_TYPE MemoryType + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN EFI_MEMORY_TYPE MemoryType ) { EFI_PEI_HOB_POINTERS Hob; EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; + EFI_STATUS Status; + PEI_CORE_INSTANCE *PrivateData; + + if (PeiServices == NULL) { + ASSERT (PeiServices != NULL); + return; + } + + PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); // // Search unused(freed) memory allocation HOB. @@ -291,31 +470,47 @@ InternalBuildMemoryAllocationHob ( 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)); + // + // If we didn't find a HOB to reuse, then create a new one. + // + if (MemoryAllocationHob == NULL) { + Status = PeiCreateHob (PeiServices, EFI_HOB_TYPE_MEMORY_ALLOCATION, (UINT16)sizeof (EFI_HOB_MEMORY_ALLOCATION), (VOID **)&MemoryAllocationHob); + if (EFI_ERROR (Status) || (MemoryAllocationHob == NULL)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to create memory allocation HOB Status = %r, MemoryAllocationHob = 0x%llx\n", + __func__, + Status, + MemoryAllocationHob + )); + ASSERT (FALSE); + return; + } + } + + MemoryAllocationHob->Header.HobType = EFI_HOB_TYPE_MEMORY_ALLOCATION; + + // + // If we are using PEI memory bins, track this allocation with the Memory Type Information GUID for DXE to consume. + // Otherwise, just produce a HOB with a zeroed GUID. + // + if (PrivateData->MemoryTypeInformationInitialized && (MemoryType < EfiMaxMemoryType) && + (PrivateData->MemoryTypeStatistics[MemoryType].NumberOfPages != 0)) + { + CopyGuid (&(MemoryAllocationHob->AllocDescriptor.Name), &gEfiMemoryTypeInformationGuid); } else { - // - // No unused(freed) memory allocation HOB found. - // Build memory allocation HOB normally. - // - BuildMemoryAllocationHob ( - BaseAddress, - Length, - MemoryType - ); + 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)); } +// MU_CHANGE END: PEI Bins /** Update or split memory allocation HOB for memory pages allocate and free. @@ -330,8 +525,10 @@ InternalBuildMemoryAllocationHob ( others for pages allocate. **/ +// MU_CHANGE BEGIN: PEI Bins - Added PeiServices parameter VOID UpdateOrSplitMemoryAllocationHob ( + IN CONST EFI_PEI_SERVICES **PeiServices, IN OUT EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob, IN EFI_PHYSICAL_ADDRESS Memory, IN UINT64 Bytes, @@ -345,6 +542,7 @@ UpdateOrSplitMemoryAllocationHob ( // Last pages need to be split out. // InternalBuildMemoryAllocationHob ( + PeiServices, Memory + Bytes, (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + MemoryAllocationHob->AllocDescriptor.MemoryLength) - (Memory + Bytes), MemoryAllocationHob->AllocDescriptor.MemoryType @@ -356,6 +554,7 @@ UpdateOrSplitMemoryAllocationHob ( // First pages need to be split out. // InternalBuildMemoryAllocationHob ( + PeiServices, MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, Memory - MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, MemoryAllocationHob->AllocDescriptor.MemoryType @@ -369,6 +568,7 @@ UpdateOrSplitMemoryAllocationHob ( MemoryAllocationHob->AllocDescriptor.MemoryLength = Bytes; MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType; } +// MU_CHANGE END: PEI Bins /** Merge adjacent free memory ranges in memory allocation HOBs. @@ -456,11 +656,13 @@ MergeFreeMemoryInMemoryAllocationHob ( **/ EFI_STATUS +// MU_CHANGE BEGIN: PEI Bins - Added PeiServices parameter FindFreeMemoryFromMemoryAllocationHob ( - IN EFI_MEMORY_TYPE MemoryType, - IN UINTN Pages, - IN UINTN Granularity, - OUT EFI_PHYSICAL_ADDRESS *Memory + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN UINTN Granularity, + OUT EFI_PHYSICAL_ADDRESS *Memory ) { EFI_PEI_HOB_POINTERS Hob; @@ -503,7 +705,7 @@ FindFreeMemoryFromMemoryAllocationHob ( } if (MemoryAllocationHob != NULL) { - UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, BaseAddress, Bytes, MemoryType); + UpdateOrSplitMemoryAllocationHob (PeiServices, MemoryAllocationHob, BaseAddress, Bytes, MemoryType); *Memory = BaseAddress; return EFI_SUCCESS; } else { @@ -511,12 +713,13 @@ FindFreeMemoryFromMemoryAllocationHob ( // // Retry if there are free memory ranges merged. // - return FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory); + return FindFreeMemoryFromMemoryAllocationHob (PeiServices, MemoryType, Pages, Granularity, Memory); } return EFI_NOT_FOUND; } } +// MU_CHANGE END: PEI Bins /** The purpose of the service is to publish an interface that allows @@ -558,6 +761,7 @@ PeiAllocatePages ( UINTN RemainingMemory; UINTN Granularity; UINTN Padding; + UINTN Index; // MU_CHANGE: PEI Bins if ((MemoryType != EfiLoaderCode) && (MemoryType != EfiLoaderData) && @@ -607,82 +811,126 @@ PeiAllocatePages ( 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 - ); + // MU_CHANGE BEGIN: PEI Bins - Bin-aware memory allocation + // if we are in permanent memory and have memory bins, we need to respect them + if (PrivateData->MemoryTypeInformationInitialized && !PrivateData->MemoryTypeStatistics[MemoryType].DefaultBin) { + FreeMemoryTop = &(PrivateData->MemoryTypeStatistics[MemoryType].MaximumAddress); + FreeMemoryBottom = &(PrivateData->MemoryTypeStatistics[MemoryType].BaseAddress); + } else { + FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); + FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom); + } + // MU_CHANGE END: PEI Bins } + // MU_CHANGE BEGIN: PEI Bins - Two-pass allocation: try bin first, then PHIT, then HOBs // - // Verify that there is sufficient memory to satisfy the allocation. + // We will attempt up to two times here. + // If we don't have memory bins, we will only attempt to allocate from the PHIT. + // If we have memory bins, we'll attempt to allocate from the appropriate bin first. If that fails we'll try + // allocating from the PHIT. // - RemainingMemory = (UINTN)(*FreeMemoryTop - *FreeMemoryBottom); - RemainingPages = (UINTN)(RShiftU64 (RemainingMemory, EFI_PAGE_SHIFT)); + // If allocating from the PHIT fails in either case, we'll then search memory allocation HOBs for free memory. // - // 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)))) - { + for (Index = 0; Index < 2; Index++) { // - // Update the PHIT to reflect the memory usage + // 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. // - *(FreeMemoryTop) -= Pages * EFI_PAGE_SIZE; + Padding = *(FreeMemoryTop) & (Granularity - 1); + if (((UINTN)(*FreeMemoryTop - *FreeMemoryBottom) < Padding) || (*(FreeMemoryTop) - Padding == 0)) { + if ((Index == 0) && PrivateData->MemoryTypeInformationInitialized && !PrivateData->MemoryTypeStatistics[MemoryType].DefaultBin) { + // + // Try the default bin before searching memory allocation HOBs + // + FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); + FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom); + continue; + } - // - // Update the value for the caller - // - *Memory = *(FreeMemoryTop); + goto NotEnoughFreeMemory; + } + + *(FreeMemoryTop) -= Padding; + if (Padding >= EFI_PAGE_SIZE) { + // + // Create a memory allocation HOB to cover + // the pages that we will lose to rounding + // + InternalBuildMemoryAllocationHob ( + PeiServices, + *(FreeMemoryTop), + Padding & ~(UINTN)EFI_PAGE_MASK, + EfiConventionalMemory + ); + } // - // Create a memory allocation HOB. + // Verify that there is sufficient memory to satisfy the allocation. // - InternalBuildMemoryAllocationHob ( - *(FreeMemoryTop), - Pages * EFI_PAGE_SIZE, - MemoryType - ); - - return EFI_SUCCESS; - } else { + RemainingMemory = (UINTN)(*FreeMemoryTop - *FreeMemoryBottom); + RemainingPages = (UINTN)(RShiftU64 (RemainingMemory, EFI_PAGE_SHIFT)); // - // Try to find free memory by searching memory allocation HOBs. + // 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. // - Status = FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory); - if (!EFI_ERROR (Status)) { - return Status; + 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 ( + PeiServices, + *(FreeMemoryTop), + Pages * EFI_PAGE_SIZE, + MemoryType + ); + + return EFI_SUCCESS; } - 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; + // If this was the first allocation attempt, we are using memory bins, and this memory type lands in a bin, + // retry allocation but target the PHIT free region. + if ((Index == 0) && PrivateData->MemoryTypeInformationInitialized && !PrivateData->MemoryTypeStatistics[MemoryType].DefaultBin) { + // + // Try to the default bin before searching memory allocation HOBs + // + FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); + FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom); + continue; + } else { + goto NotEnoughFreeMemory; + } + } + +NotEnoughFreeMemory: + // + // Try to find free memory by searching memory allocation HOBs. + // + Status = FindFreeMemoryFromMemoryAllocationHob ((CONST EFI_PEI_SERVICES **)PeiServices, 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; + // MU_CHANGE END: PEI Bins } /** @@ -817,7 +1065,7 @@ PeiFreePages ( } if (MemoryAllocationHob != NULL) { - UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, Memory, Bytes, EfiConventionalMemory); + UpdateOrSplitMemoryAllocationHob (PeiServices, MemoryAllocationHob, Memory, Bytes, EfiConventionalMemory); // MU_CHANGE: PEI Bins FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob); return EFI_SUCCESS; } else { diff --git a/MdeModulePkg/Core/Pei/PeiMain.h b/MdeModulePkg/Core/Pei/PeiMain.h index 6fdd8db453d..a59195092d9 100644 --- a/MdeModulePkg/Core/Pei/PeiMain.h +++ b/MdeModulePkg/Core/Pei/PeiMain.h @@ -51,6 +51,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include // MU_CHANGE: PEI Bins /// /// It is an FFS type extension used for PeiFindFileEx. It indicates current @@ -337,6 +338,25 @@ struct _PEI_CORE_INSTANCE { DELAYED_DISPATCH_TABLE *DelayedDispatchTable; EFI_PHYSICAL_ADDRESS PlatformBlob; // MU_CHANGE Used by AdvancedLogger + + // MU_CHANGE BEGIN: PEI Bins + // + // Whether memory bins are initialized and being used in PEI + // + BOOLEAN MemoryTypeInformationInitialized; + + // + // Memory type information for all memory types. The array index is the memory type. + // This is used for the memory bin feature, if enabled, to track bin sizes. + // + EFI_MEMORY_TYPE_INFORMATION *MemoryTypeInformation; + + // + // Memory type statistics for all memory types. The array index is the memory type. + // This is used for the memory bin feature, if enabled, to track bin locations. + // + EFI_MEMORY_TYPE_STATISTICS *MemoryTypeStatistics; + // MU_CHANGE END: PEI Bins }; /// diff --git a/MdeModulePkg/Core/Pei/PeiMain.inf b/MdeModulePkg/Core/Pei/PeiMain.inf index dea1b20d085..8547d025f7e 100644 --- a/MdeModulePkg/Core/Pei/PeiMain.inf +++ b/MdeModulePkg/Core/Pei/PeiMain.inf @@ -34,6 +34,7 @@ Reset/Reset.c Ppi/Ppi.c PeiMain/PeiMain.c + Memory/MemoryBin.c # MU_CHANGE: PEI Bins Memory/MemoryServices.c Image/Image.c Hob/Hob.c @@ -83,6 +84,7 @@ gEdkiiMigrationInfoGuid ## SOMETIMES_CONSUMES ## HOB gEfiDelayedDispatchTableGuid ## SOMETIMES_PRODUCES ## HOB gEfiFirmwarePerformanceGuid # MU_CHANGE_161871 - needed to build SEC perf HOB + gEfiMemoryTypeInformationGuid ## SOMETIMES_CONSUMES ## HOB # MU_CHANGE: PEI Bins [Ppis] gEfiPeiStatusCodePpiGuid ## SOMETIMES_CONSUMES # PeiReportStatusService is not ready if this PPI doesn't exist @@ -124,6 +126,7 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdDelayedDispatchMaxDelayUs ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdDelayedDispatchCompletionTimeoutUs ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdDelayedDispatchMaxEntries ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPeiMemoryBinsEnable ## CONSUMES # MU_CHANGE: PEI Bins # [BootMode] # S3_RESUME ## SOMETIMES_CONSUMES diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index b888f2317db..a366d67175f 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -1135,6 +1135,20 @@ # @Prompt Install Internal Event Services Protocol gEfiMdeModulePkgTokenSpaceGuid.PcdInternalEventServicesEnabled|FALSE|BOOLEAN|0x30003003 + # MU_CHANGE BEGIN: PEI Bins + ## Indicates if the PEI memory bins feature is allowed to be enabled. + # TRUE - The PEI memory bins feature is allowed to be enabled. The platform must also produce the + # gEfiMemoryTypeInformationGuid HOB prior to permanent memory installation in order to enroll in the + # feature. + # FALSE - The PEI memory bins feature is not allowed to be enabled. If the gEfiMemoryTypeInformationGuid HOB + # is produced by the platform in PEI, DXE will set up the memory bins. If not, no memory bins will be + # used. + # + # This PCD defaults to FALSE to follow the same behavior prior to the PEI memory bins feature being introduced. + # See MdeModulePkg/Core/MemoryBins.md for more details. + gEfiMdeModulePkgTokenSpaceGuid.PcdPeiMemoryBinsEnable|FALSE|BOOLEAN|0x00010080 + # MU_CHANGE END: PEI Bins + [PcdsFeatureFlag.IA32, PcdsFeatureFlag.ARM, PcdsFeatureFlag.AARCH64] gEfiMdeModulePkgTokenSpaceGuid.PcdPciDegradeResourceForOptionRom|FALSE|BOOLEAN|0x0001003a From ff3e8a2cf08c49cfdae3a050e8874ebce92698a2 Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Thu, 29 Jan 2026 09:51:48 -0800 Subject: [PATCH 8/9] MdeModulePkg: DxeCore: Update Memory Statistics from PEI If memory bins are enabled for PEI, PEI will produce Memory Allocation HOBs marked with gEfiMemoryTypeInformationGuid. If these exist, DXE core will now process the stats from them to have accurate numbers. Signed-off-by: Oliver Smith-Denny --- MdeModulePkg/Core/Dxe/Gcd/Gcd.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c index b93ce4ff02d..770671de4f7 100644 --- a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c +++ b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c @@ -2873,6 +2873,23 @@ CoreInitializeGcdServices ( RShiftU64 (MemoryHob->AllocDescriptor.MemoryLength, EFI_PAGE_SHIFT), Descriptor.Capabilities & (~EFI_MEMORY_RUNTIME) ); + + // MU_CHANGE BEGIN: PEI Bins - Update memory bin statistics from PEI allocation HOBs + // if this Memory Allocation HOB came from PEI, update the memory bin statistics + if (CompareGuid (&MemoryHob->AllocDescriptor.Name, &gEfiMemoryTypeInformationGuid)) { + UpdateMemoryStatistics ( + EfiConventionalMemory, + MemoryHob->AllocDescriptor.MemoryType, + MemoryHob->AllocDescriptor.MemoryBaseAddress, + EFI_SIZE_TO_PAGES ((UINT32)MemoryHob->AllocDescriptor.MemoryLength), + &mMemoryTypeInformationInitialized, + mMemoryTypeStatistics, + gMemoryTypeInformation, + mDefaultBaseAddress, + mDefaultMaximumAddress + ); + } + // MU_CHANGE END: PEI Bins } } } From f74d16a1627df9dcde0b82c34f68079d3effed8c Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Thu, 29 Jan 2026 09:56:38 -0800 Subject: [PATCH 9/9] MdeModulePkg: MemoryBins: Add GoogleTest and README This commit adds unit tests and documentation for the Memory Bin feature. Signed-off-by: Oliver Smith-Denny --- .../Mem/GoogleTest/MemoryBinGoogleTest.cpp | 611 ++++++++++++++++++ .../GoogleTest/MemoryBinGoogleTestHost.inf | 34 + MdeModulePkg/Core/MemoryBins.md | 277 ++++++++ MdeModulePkg/Test/MdeModulePkgHostTest.dsc | 7 + 4 files changed, 929 insertions(+) create mode 100644 MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTest.cpp create mode 100644 MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTestHost.inf create mode 100644 MdeModulePkg/Core/MemoryBins.md diff --git a/MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTest.cpp b/MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTest.cpp new file mode 100644 index 00000000000..9011f5ac7b3 --- /dev/null +++ b/MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTest.cpp @@ -0,0 +1,611 @@ +// MU_CHANGE: PEI Bins - Whole File +/** @file + Unit tests for BaseMemoryBinLib library. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +extern "C" { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + BOOLEAN mMemoryTypeInformationInitialized = FALSE; + + EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = { + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiReservedMemoryType + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiLoaderCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiLoaderData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiBootServicesCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiBootServicesData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE, FALSE }, // EfiRuntimeServicesCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE, FALSE }, // EfiRuntimeServicesData + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiConventionalMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiUnusableMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiACPIReclaimMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiACPIMemoryNVS + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiMemoryMappedIO + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE, FALSE }, // EfiPalCode + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE }, // EfiPersistentMemory + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE, FALSE }, // EfiUnacceptedMemoryType + { 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE, FALSE } // EfiMaxMemoryType + }; + + EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = MAX_ALLOC_ADDRESS; + EFI_PHYSICAL_ADDRESS mDefaultBaseAddress = MAX_ALLOC_ADDRESS; + + EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = { + { EfiReservedMemoryType, 0 }, + { EfiLoaderCode, 0 }, + { EfiLoaderData, 0 }, + { EfiBootServicesCode, 0 }, + { EfiBootServicesData, 0 }, + { EfiRuntimeServicesCode, 0 }, + { EfiRuntimeServicesData, 0 }, + { EfiConventionalMemory, 0 }, + { EfiUnusableMemory, 0 }, + { EfiACPIReclaimMemory, 0 }, + { EfiACPIMemoryNVS, 0 }, + { EfiMemoryMappedIO, 0 }, + { EfiMemoryMappedIOPortSpace, 0 }, + { EfiPalCode, 0 }, + { EfiPersistentMemory, 0 }, + { EfiGcdMemoryTypeUnaccepted, 0 }, + { EfiMaxMemoryType, 0 } + }; +} + +using namespace testing; + +class BaseMemoryBinLibTest : public ::testing::Test { +protected: + StrictMock HobLib; + void + SetUp ( + ) override + { + // + // Reset global state before each test + // + mMemoryTypeInformationInitialized = FALSE; + mDefaultMaximumAddress = MAX_ALLOC_ADDRESS; + mDefaultBaseAddress = MAX_ALLOC_ADDRESS; + + for (UINTN i = 0; i < EfiMaxMemoryType + 1; i++) { + gMemoryTypeInformation[i].NumberOfPages = 0; + } + + for (UINTN i = 0; i < EfiMaxMemoryType + 1; i++) { + mMemoryTypeStatistics[i].BaseAddress = 0; + mMemoryTypeStatistics[i].MaximumAddress = MAX_ALLOC_ADDRESS; + mMemoryTypeStatistics[i].CurrentNumberOfPages = 0; + mMemoryTypeStatistics[i].NumberOfPages = 0; + mMemoryTypeStatistics[i].InformationIndex = EfiMaxMemoryType; + mMemoryTypeStatistics[i].DefaultBin = FALSE; + } + + mMemoryTypeStatistics[EfiReservedMemoryType].Special = TRUE; + mMemoryTypeStatistics[EfiRuntimeServicesCode].Special = TRUE; + mMemoryTypeStatistics[EfiRuntimeServicesCode].Runtime = TRUE; + mMemoryTypeStatistics[EfiRuntimeServicesData].Special = TRUE; + mMemoryTypeStatistics[EfiRuntimeServicesData].Runtime = TRUE; + mMemoryTypeStatistics[EfiACPIReclaimMemory].Special = TRUE; + mMemoryTypeStatistics[EfiACPIMemoryNVS].Special = TRUE; + mMemoryTypeStatistics[EfiPalCode].Special = TRUE; + mMemoryTypeStatistics[EfiPalCode].Runtime = TRUE; + mMemoryTypeStatistics[EfiUnacceptedMemoryType].Special = TRUE; + } +}; + +// +// Test: CalculateTotalMemoryBinSizeNeeded with no pages allocated +// +TEST_F (BaseMemoryBinLibTest, CalculateTotalMemoryBinSizeNeededReturnsZeroWhenNoPagesAllocated) { + UINT64 TotalSize; + + TotalSize = CalculateTotalMemoryBinSizeNeeded (0, gMemoryTypeInformation); + + ASSERT_EQ (TotalSize, (UINT64)0); +} + +// +// Test: CalculateTotalMemoryBinSizeNeeded with pages allocated +// +TEST_F (BaseMemoryBinLibTest, CalculateTotalMemoryBinSizeNeededCalculatesCorrectSize) { + UINT64 TotalSize; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 10; + gMemoryTypeInformation[1].Type = EfiRuntimeServicesData; + gMemoryTypeInformation[1].NumberOfPages = 20; + gMemoryTypeInformation[2].Type = EfiMaxMemoryType; + + TotalSize = CalculateTotalMemoryBinSizeNeeded (0, gMemoryTypeInformation); + + ASSERT_EQ (TotalSize, (UINT64)(30 * EFI_PAGE_SIZE)); +} + +// +// Test: CoreSetMemoryTypeInformationRange initializes bins correctly +// +TEST_F (BaseMemoryBinLibTest, CoreSetMemoryTypeInformationRangeSetsUpBinsCorrectly) { + EFI_PHYSICAL_ADDRESS Start; + UINT64 Length; + + Start = 0x100000; + Length = 0x100000; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 20; + gMemoryTypeInformation[1].Type = EfiRuntimeServicesData; + gMemoryTypeInformation[1].NumberOfPages = 10; + gMemoryTypeInformation[2].Type = EfiReservedMemoryType; + gMemoryTypeInformation[2].NumberOfPages = 5; + gMemoryTypeInformation[3].Type = EfiACPIReclaimMemory; + gMemoryTypeInformation[3].NumberOfPages = 15; + gMemoryTypeInformation[4].Type = EfiACPIMemoryNVS; + gMemoryTypeInformation[4].NumberOfPages = 25; + gMemoryTypeInformation[5].Type = EfiMaxMemoryType; + + CoreSetMemoryTypeInformationRange (Start, Length, gMemoryTypeInformation, &mMemoryTypeInformationInitialized, mMemoryTypeStatistics, &mDefaultMaximumAddress); + + ASSERT_TRUE (mMemoryTypeInformationInitialized); + + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].NumberOfPages, (UINT32)20); + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesData].NumberOfPages, (UINT32)10); + ASSERT_EQ (mMemoryTypeStatistics[EfiReservedMemoryType].NumberOfPages, (UINT32)5); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIReclaimMemory].NumberOfPages, (UINT32)15); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIMemoryNVS].NumberOfPages, (UINT32)25); + + // Confirm contiguous bins + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesData].MaximumAddress + 1, mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress); + ASSERT_EQ (mMemoryTypeStatistics[EfiReservedMemoryType].MaximumAddress + 1, mMemoryTypeStatistics[EfiRuntimeServicesData].BaseAddress); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIReclaimMemory].MaximumAddress + 1, mMemoryTypeStatistics[EfiReservedMemoryType].BaseAddress); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIMemoryNVS].MaximumAddress + 1, mMemoryTypeStatistics[EfiACPIReclaimMemory].BaseAddress); +} + +// +// Test: CoreSetMemoryTypeInformationRange rejects insufficient space +// +TEST_F (BaseMemoryBinLibTest, CoreSetMemoryTypeInformationRangeRejectsInsufficientSpace) { + EFI_PHYSICAL_ADDRESS Start; + UINT64 Length; + + Start = 0x100000; + Length = 0x1000; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 100; // Requires more than 4KB + gMemoryTypeInformation[1].Type = EfiMaxMemoryType; + + CoreSetMemoryTypeInformationRange (Start, Length, gMemoryTypeInformation, &mMemoryTypeInformationInitialized, mMemoryTypeStatistics, &mDefaultMaximumAddress); + + ASSERT_FALSE (mMemoryTypeInformationInitialized); +} + +// +// Test: CoreSetMemoryTypeInformationRange respects already initialized state +// +TEST_F (BaseMemoryBinLibTest, CoreSetMemoryTypeInformationRangeRespectsInitializedState) { + EFI_PHYSICAL_ADDRESS Start; + UINT64 Length; + + mMemoryTypeInformationInitialized = TRUE; + + Start = 0x100000; + Length = 0x100000; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 10; + mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress = 0x1000; + gMemoryTypeInformation[1].Type = EfiMaxMemoryType; + + CoreSetMemoryTypeInformationRange (Start, Length, gMemoryTypeInformation, &mMemoryTypeInformationInitialized, mMemoryTypeStatistics, &mDefaultMaximumAddress); + + ASSERT_TRUE (mMemoryTypeInformationInitialized); + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress, (EFI_PHYSICAL_ADDRESS)0x1000); +} + +// +// Test: UpdateMemoryStatistics updates in-bin statistics correctly +// +TEST_F (BaseMemoryBinLibTest, UpdateMemoryStatisticsUpdatesInBinCorrectly) { + mMemoryTypeInformationInitialized = TRUE; + + mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress = 0x100000; + mMemoryTypeStatistics[EfiRuntimeServicesCode].MaximumAddress = 0x200000; + mMemoryTypeStatistics[EfiRuntimeServicesCode].InformationIndex = 0; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 0; + + UpdateMemoryStatistics ( + EfiConventionalMemory, + EfiRuntimeServicesCode, + 0x150000, // Within bin range + 10, + &mMemoryTypeInformationInitialized, + mMemoryTypeStatistics, + gMemoryTypeInformation, + 0x10000, // Just set these outside of the range + 0x20000 + ); + + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].CurrentNumberOfPages, (UINT32)10); +} + +// +// Test: UpdateMemoryStatistics updates out-of-bin statistics correctly +// +TEST_F (BaseMemoryBinLibTest, UpdateMemoryStatisticsUpdatesOutOfBinCorrectly) { + mMemoryTypeInformationInitialized = TRUE; + + mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress = 0x100000; + mMemoryTypeStatistics[EfiRuntimeServicesCode].MaximumAddress = 0x200000; + mMemoryTypeStatistics[EfiRuntimeServicesCode].InformationIndex = 0; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 0; + + UpdateMemoryStatistics ( + EfiConventionalMemory, + EfiRuntimeServicesCode, + 0x300000, // Outside bin range + 10, + &mMemoryTypeInformationInitialized, + mMemoryTypeStatistics, + gMemoryTypeInformation, + 0x10000, // Just set these outside of the range + 0x20000 + ); + + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].CurrentNumberOfPages, (UINT32)0); +} + +// +// Test: UpdateMemoryStatistics handles freeing memory +// +TEST_F (BaseMemoryBinLibTest, UpdateMemoryStatisticsHandlesFreeingMemory) { + mMemoryTypeInformationInitialized = TRUE; + + mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress = 0x100000; + mMemoryTypeStatistics[EfiRuntimeServicesCode].MaximumAddress = 0x200000; + mMemoryTypeStatistics[EfiRuntimeServicesCode].CurrentNumberOfPages = 20; + mMemoryTypeStatistics[EfiRuntimeServicesCode].InformationIndex = 0; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 20; + + UpdateMemoryStatistics ( + EfiRuntimeServicesCode, + EfiConventionalMemory, + 0x150000, // Within bin range + 10, + &mMemoryTypeInformationInitialized, + mMemoryTypeStatistics, + gMemoryTypeInformation, + 0x10000, // Just set these outside of the range + 0x20000 + ); + + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].CurrentNumberOfPages, (UINT32)10); +} + +// +// Tests for AllocateMemoryTypeInformationBins +// + +// +// Test: AllocateMemoryTypeInformationBins does nothing when already initialized +// +TEST_F (BaseMemoryBinLibTest, DoesNothingWhenAlreadyInitialized) { + mMemoryTypeInformationInitialized = TRUE; + mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress = 0x1000; + + AllocateMemoryTypeInformationBins (&mMemoryTypeInformationInitialized, gMemoryTypeInformation, mMemoryTypeStatistics, &mDefaultMaximumAddress, FALSE); + + // Should remain unchanged since already initialized + ASSERT_TRUE (mMemoryTypeInformationInitialized); + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress, (EFI_PHYSICAL_ADDRESS)0x1000); +} + +// +// Test: AllocateMemoryTypeInformationBins does nothing when no pages needed +// +TEST_F (BaseMemoryBinLibTest, DoesNothingWhenNoPagesNeeded) { + AllocateMemoryTypeInformationBins (&mMemoryTypeInformationInitialized, gMemoryTypeInformation, mMemoryTypeStatistics, &mDefaultMaximumAddress, FALSE); + + ASSERT_TRUE (mMemoryTypeInformationInitialized); + ASSERT_EQ (mMemoryTypeStatistics[EfiReservedMemoryType].NumberOfPages, (UINT32)0); + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].NumberOfPages, (UINT32)0); + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesData].NumberOfPages, (UINT32)0); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIReclaimMemory].NumberOfPages, (UINT32)0); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIMemoryNVS].NumberOfPages, (UINT32)0); +} + +// +// Test: AllocateMemoryTypeInformationBins allocates pages and initializes bins +// +TEST_F (BaseMemoryBinLibTest, AllocatesPagesAndInitializesBins) { + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 20; + gMemoryTypeInformation[1].Type = EfiRuntimeServicesData; + gMemoryTypeInformation[1].NumberOfPages = 10; + gMemoryTypeInformation[2].Type = EfiReservedMemoryType; + gMemoryTypeInformation[2].NumberOfPages = 5; + gMemoryTypeInformation[3].Type = EfiACPIReclaimMemory; + gMemoryTypeInformation[3].NumberOfPages = 15; + gMemoryTypeInformation[4].Type = EfiACPIMemoryNVS; + gMemoryTypeInformation[4].NumberOfPages = 25; + gMemoryTypeInformation[5].Type = EfiMaxMemoryType; + + // HOB should not be built in this case + EXPECT_CALL (HobLib, BuildResourceDescriptorWithOwnerHob (_, _, _, _, _)) + .Times (0); + + AllocateMemoryTypeInformationBins (&mMemoryTypeInformationInitialized, gMemoryTypeInformation, mMemoryTypeStatistics, &mDefaultMaximumAddress, FALSE); + + ASSERT_TRUE (mMemoryTypeInformationInitialized); + + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesCode].NumberOfPages, (UINT32)20); + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesData].NumberOfPages, (UINT32)10); + ASSERT_EQ (mMemoryTypeStatistics[EfiReservedMemoryType].NumberOfPages, (UINT32)5); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIReclaimMemory].NumberOfPages, (UINT32)15); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIMemoryNVS].NumberOfPages, (UINT32)25); + + // Confirm contiguous bins + ASSERT_EQ (mMemoryTypeStatistics[EfiRuntimeServicesData].MaximumAddress + 1, mMemoryTypeStatistics[EfiRuntimeServicesCode].BaseAddress); + ASSERT_EQ (mMemoryTypeStatistics[EfiReservedMemoryType].MaximumAddress + 1, mMemoryTypeStatistics[EfiRuntimeServicesData].BaseAddress); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIReclaimMemory].MaximumAddress + 1, mMemoryTypeStatistics[EfiReservedMemoryType].BaseAddress); + ASSERT_EQ (mMemoryTypeStatistics[EfiACPIMemoryNVS].MaximumAddress + 1, mMemoryTypeStatistics[EfiACPIReclaimMemory].BaseAddress); +} + +// +// Test: AllocateMemoryTypeInformationBins creates HOB when requested +// +TEST_F (BaseMemoryBinLibTest, CreatesHobWhenRequested) { + UINT64 TotalPages = 30; + + gMemoryTypeInformation[0].Type = EfiRuntimeServicesCode; + gMemoryTypeInformation[0].NumberOfPages = 10; + gMemoryTypeInformation[1].Type = EfiRuntimeServicesData; + gMemoryTypeInformation[1].NumberOfPages = 20; + gMemoryTypeInformation[2].Type = EfiMaxMemoryType; + + EXPECT_CALL ( + HobLib, + BuildResourceDescriptorWithOwnerHob ( + EFI_RESOURCE_SYSTEM_MEMORY, + EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED, + _, + TotalPages * EFI_PAGE_SIZE, + &gEfiMemoryTypeInformationGuid + ) + ) + .Times (1); + + AllocateMemoryTypeInformationBins (&mMemoryTypeInformationInitialized, gMemoryTypeInformation, mMemoryTypeStatistics, &mDefaultMaximumAddress, TRUE); + + ASSERT_TRUE (mMemoryTypeInformationInitialized); +} + +// +// Test: PopulateMemoryTypeInformation returns NOT_FOUND when Memory Type Information HOB is missing +// +TEST_F (BaseMemoryBinLibTest, ReturnsNotFoundWhenMemoryTypeInformationHobMissing) { + EFI_STATUS Status; + + EXPECT_CALL (HobLib, GetFirstGuidHob (&gEfiMemoryTypeInformationGuid)) + .WillOnce (Return ((VOID *)NULL)); + + Status = PopulateMemoryTypeInformation (gMemoryTypeInformation); + + ASSERT_EQ (Status, EFI_NOT_FOUND); +} + +// +// Test: PopulateMemoryTypeInformation populates from valid HOB +// +TEST_F (BaseMemoryBinLibTest, PopulatesFromValidHob) { + EFI_STATUS Status; + UINT8 GuidHobBuffer[sizeof (EFI_HOB_GUID_TYPE) + sizeof (EFI_MEMORY_TYPE_INFORMATION) * 7]; + EFI_HOB_GUID_TYPE *GuidHob; + EFI_MEMORY_TYPE_INFORMATION *MemTypeInfo; + + ZeroMem (GuidHobBuffer, sizeof (GuidHobBuffer)); + GuidHob = (EFI_HOB_GUID_TYPE *)GuidHobBuffer; + GuidHob->Header.HobType = EFI_HOB_TYPE_GUID_EXTENSION; + GuidHob->Header.HobLength = sizeof (GuidHobBuffer); + CopyGuid (&GuidHob->Name, &gEfiMemoryTypeInformationGuid); + + MemTypeInfo = (EFI_MEMORY_TYPE_INFORMATION *)(GuidHob + 1); + MemTypeInfo[0].Type = EfiReservedMemoryType; + MemTypeInfo[0].NumberOfPages = 5; + MemTypeInfo[1].Type = EfiRuntimeServicesCode; + MemTypeInfo[1].NumberOfPages = 10; + MemTypeInfo[2].Type = EfiRuntimeServicesData; + MemTypeInfo[2].NumberOfPages = 15; + MemTypeInfo[3].Type = EfiACPIReclaimMemory; + MemTypeInfo[3].NumberOfPages = 20; + MemTypeInfo[4].Type = EfiACPIMemoryNVS; + MemTypeInfo[4].NumberOfPages = 25; + MemTypeInfo[5].Type = EfiPalCode; + MemTypeInfo[5].NumberOfPages = 8; + MemTypeInfo[6].Type = EfiMaxMemoryType; + MemTypeInfo[6].NumberOfPages = 0; + + EXPECT_CALL (HobLib, GetFirstGuidHob (&gEfiMemoryTypeInformationGuid)) + .WillOnce (Return ((VOID *)GuidHob)); + + Status = PopulateMemoryTypeInformation (gMemoryTypeInformation); + + ASSERT_EQ (Status, EFI_SUCCESS); + ASSERT_EQ (gMemoryTypeInformation[0].Type, (UINT32)EfiReservedMemoryType); + ASSERT_EQ (gMemoryTypeInformation[0].NumberOfPages, (UINT32)5); + ASSERT_EQ (gMemoryTypeInformation[1].Type, (UINT32)EfiRuntimeServicesCode); + ASSERT_EQ (gMemoryTypeInformation[1].NumberOfPages, (UINT32)10); + ASSERT_EQ (gMemoryTypeInformation[2].Type, (UINT32)EfiRuntimeServicesData); + ASSERT_EQ (gMemoryTypeInformation[2].NumberOfPages, (UINT32)15); + ASSERT_EQ (gMemoryTypeInformation[3].Type, (UINT32)EfiACPIReclaimMemory); + ASSERT_EQ (gMemoryTypeInformation[3].NumberOfPages, (UINT32)20); + ASSERT_EQ (gMemoryTypeInformation[4].Type, (UINT32)EfiACPIMemoryNVS); + ASSERT_EQ (gMemoryTypeInformation[4].NumberOfPages, (UINT32)25); + ASSERT_EQ (gMemoryTypeInformation[5].Type, (UINT32)EfiPalCode); + ASSERT_EQ (gMemoryTypeInformation[5].NumberOfPages, (UINT32)8); +} + +// +// Test: GetMemoryTypeInformationResourceHob returns NOT_FOUND when no resource HOB exists +// +TEST_F (BaseMemoryBinLibTest, GetMemoryTypeInformationResourceHobReturnsNotFoundWhenNoResourceHob) { + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + UINT8 HobListBuffer[sizeof (EFI_HOB_HANDOFF_INFO_TABLE) + sizeof (EFI_HOB_GENERIC_HEADER)]; + EFI_HOB_HANDOFF_INFO_TABLE *HandoffHob; + EFI_HOB_GENERIC_HEADER *EndOfHobList; + VOID *HobStart; + + ZeroMem (HobListBuffer, sizeof (HobListBuffer)); + + HandoffHob = (EFI_HOB_HANDOFF_INFO_TABLE *)HobListBuffer; + HandoffHob->Header.HobType = EFI_HOB_TYPE_HANDOFF; + HandoffHob->Header.HobLength = sizeof (EFI_HOB_HANDOFF_INFO_TABLE); + + EndOfHobList = (EFI_HOB_GENERIC_HEADER *)(HandoffHob + 1); + EndOfHobList->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST; + EndOfHobList->HobLength = sizeof (EFI_HOB_GENERIC_HEADER); + + HobStart = (VOID *)HobListBuffer; + + ResourceHob = GetMemoryTypeInformationResourceHob (&HobStart, gMemoryTypeInformation); + + ASSERT_EQ (ResourceHob, NULL); +} + +// +// Test: GetMemoryTypeInformationResourceHob finds matching resource HOB +// +TEST_F (BaseMemoryBinLibTest, GetMemoryTypeInformationResourceHobFindsMatchingHob) { + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + UINT8 HobListBuffer[sizeof (EFI_HOB_HANDOFF_INFO_TABLE) + 3 *sizeof (EFI_HOB_RESOURCE_DESCRIPTOR) + sizeof (EFI_HOB_GENERIC_HEADER)]; + EFI_HOB_HANDOFF_INFO_TABLE *HandoffHob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor; + EFI_HOB_GENERIC_HEADER *EndOfHobList; + VOID *HobStart; + EFI_GUID OtherGuid = { 0x12345678, 0x1234, 0x1234, { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 } + }; + + ZeroMem (HobListBuffer, sizeof (HobListBuffer)); + + HandoffHob = (EFI_HOB_HANDOFF_INFO_TABLE *)HobListBuffer; + HandoffHob->Header.HobType = EFI_HOB_TYPE_HANDOFF; + HandoffHob->Header.HobLength = sizeof (EFI_HOB_HANDOFF_INFO_TABLE); + + ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *)(HandoffHob + 1); + ResourceDescriptor->Header.HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR; + ResourceDescriptor->Header.HobLength = sizeof (EFI_HOB_RESOURCE_DESCRIPTOR); + ResourceDescriptor->ResourceType = EFI_RESOURCE_SYSTEM_MEMORY; + CopyGuid (&ResourceDescriptor->Owner, &OtherGuid); + ResourceDescriptor->PhysicalStart = 0x100000; + ResourceDescriptor->ResourceLength = 0x50000; + + ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *)(ResourceDescriptor + 1); + ResourceDescriptor->Header.HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR; + ResourceDescriptor->Header.HobLength = sizeof (EFI_HOB_RESOURCE_DESCRIPTOR); + ResourceDescriptor->ResourceType = EFI_RESOURCE_SYSTEM_MEMORY; + CopyGuid (&ResourceDescriptor->Owner, &gEfiMemoryTypeInformationGuid); + ResourceDescriptor->PhysicalStart = 0x200000; + ResourceDescriptor->ResourceLength = 0x75000; + ResourceDescriptor->ResourceAttribute = EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED; + + ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *)(ResourceDescriptor + 1); + ResourceDescriptor->Header.HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR; + ResourceDescriptor->Header.HobLength = sizeof (EFI_HOB_RESOURCE_DESCRIPTOR); + ResourceDescriptor->ResourceType = EFI_RESOURCE_SYSTEM_MEMORY; + CopyGuid (&ResourceDescriptor->Owner, &OtherGuid); + ResourceDescriptor->PhysicalStart = 0x300000; + ResourceDescriptor->ResourceLength = 0x50000; + + EndOfHobList = (EFI_HOB_GENERIC_HEADER *)(ResourceDescriptor + 1); + EndOfHobList->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST; + EndOfHobList->HobLength = sizeof (EFI_HOB_GENERIC_HEADER); + + HobStart = (VOID *)HobListBuffer; + + ResourceHob = GetMemoryTypeInformationResourceHob (&HobStart, gMemoryTypeInformation); + + ASSERT_EQ (ResourceHob->PhysicalStart, (EFI_PHYSICAL_ADDRESS)0x200000); + ASSERT_EQ (ResourceHob->ResourceLength, (UINT64)0x75000); +} + +// +// Test: GetMemoryTypeInformationResourceHob Fails When Multiple Matching HOBs Exist +// +TEST_F (BaseMemoryBinLibTest, GetMemoryTypeInformationResourceHobFailsWithMultipleMatchingHobs) { + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + UINT8 HobListBuffer[sizeof (EFI_HOB_HANDOFF_INFO_TABLE) + 2 * sizeof (EFI_HOB_RESOURCE_DESCRIPTOR) + sizeof (EFI_HOB_GENERIC_HEADER)]; + EFI_HOB_HANDOFF_INFO_TABLE *HandoffHob; + EFI_HOB_RESOURCE_DESCRIPTOR *FirstMatchingResource; + EFI_HOB_RESOURCE_DESCRIPTOR *SecondMatchingResource; + EFI_HOB_GENERIC_HEADER *EndOfHobList; + VOID *HobStart; + + ZeroMem (HobListBuffer, sizeof (HobListBuffer)); + + HandoffHob = (EFI_HOB_HANDOFF_INFO_TABLE *)HobListBuffer; + HandoffHob->Header.HobType = EFI_HOB_TYPE_HANDOFF; + HandoffHob->Header.HobLength = sizeof (EFI_HOB_HANDOFF_INFO_TABLE); + + // First matching resource HOB + FirstMatchingResource = (EFI_HOB_RESOURCE_DESCRIPTOR *)(HandoffHob + 1); + FirstMatchingResource->Header.HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR; + FirstMatchingResource->Header.HobLength = sizeof (EFI_HOB_RESOURCE_DESCRIPTOR); + FirstMatchingResource->ResourceType = EFI_RESOURCE_SYSTEM_MEMORY; + CopyGuid (&FirstMatchingResource->Owner, &gEfiMemoryTypeInformationGuid); + FirstMatchingResource->PhysicalStart = 0x100000; + FirstMatchingResource->ResourceLength = 0x50000; + FirstMatchingResource->ResourceAttribute = EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED; + + // Second matching resource HOB + SecondMatchingResource = (EFI_HOB_RESOURCE_DESCRIPTOR *)(FirstMatchingResource + 1); + SecondMatchingResource->Header.HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR; + SecondMatchingResource->Header.HobLength = sizeof (EFI_HOB_RESOURCE_DESCRIPTOR); + SecondMatchingResource->ResourceType = EFI_RESOURCE_SYSTEM_MEMORY; + CopyGuid (&SecondMatchingResource->Owner, &gEfiMemoryTypeInformationGuid); + SecondMatchingResource->PhysicalStart = 0x400000; + SecondMatchingResource->ResourceLength = 0x80000; + SecondMatchingResource->ResourceAttribute = EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED; + + EndOfHobList = (EFI_HOB_GENERIC_HEADER *)(SecondMatchingResource + 1); + EndOfHobList->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST; + EndOfHobList->HobLength = sizeof (EFI_HOB_GENERIC_HEADER); + + HobStart = (VOID *)HobListBuffer; + + ResourceHob = GetMemoryTypeInformationResourceHob (&HobStart, gMemoryTypeInformation); + + ASSERT_EQ (ResourceHob, NULL); +} + +int +main ( + int argc, + char *argv[] + ) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTestHost.inf b/MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTestHost.inf new file mode 100644 index 00000000000..4edc3d5a68a --- /dev/null +++ b/MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTestHost.inf @@ -0,0 +1,34 @@ +## MU_CHANGE: PEI Bins - Whole File +## @file +# Unit tests for the Memory Bin feature +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemoryBinGoogleTest + FILE_GUID = 8A3C5C7B-2D4E-4F9A-B1C6-9E8F7D6C5B4A + MODULE_TYPE = HOST_APPLICATION + VERSION_STRING = 1.0 + +[Sources] + MemoryBinGoogleTest.cpp + ../MemoryBin.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[LibraryClasses] + GoogleTestLib + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + HobLib + +[Guids] + gEfiMemoryTypeInformationGuid diff --git a/MdeModulePkg/Core/MemoryBins.md b/MdeModulePkg/Core/MemoryBins.md new file mode 100644 index 00000000000..460a422c36e --- /dev/null +++ b/MdeModulePkg/Core/MemoryBins.md @@ -0,0 +1,277 @@ + +# Memory Bin Feature + +## Table of Contents + +## Background + +The S4 sleep state is the lowest power state defined by the ACPI spec that supports system state restoration. In this +sleep state, the OS or platform FW is responsible for saving the running context in a persistent location. Upon +resumption from S4, this running context needs to be restored to memory and whatever device state is required. Platform +FW is responsible for bringing the system out of S4 and returning to the OS, which generally is the one to restore +system context. Because the OS needs to return the system memory to exactly the state it was before, it must use the +same memory regions as it did the previous boot. In order to do this, UEFI must have the same runtime memory footprint +it did on the previous boot. + +The edk2 memory bin feature is designed to support the S4 sleep state's requirements to keep UEFI's runtime memory +footprint identical between boots of the same FW. + +## Implementation + +### Overview + +There are 5 types of UEFI memory that persist into the OS timeframe: EfiReservedMemoryType, EfiRuntimeServicesCode, +EfiRuntimeServicesData, EfiACPIMemoryNVS, and generally EfiACPIReclaimMemory (the OS is allowed to reclaim this, but +typically does not). edk2 allows a platform to define a memory bin size for each of these memory types, as desired. +Technically, any type can be requested to use a memory bin, but the real use case is for the runtime types. + +The DXE and PEI (if configured) allocators will attempt to allocate all runtime types in the memory bin defined for +them. If the allocation size is too large, it will allocate it elsewhere. Runtime memory may also be allocated by +address outside the bin or reported as a Resource Descriptor HOB; neither of these will land in the memory bin. + +When the EFI_BOOT_SERVICES.GetMemoryMap() API is called, edk2 will examine the memory bin sizes defined by the +platform and create an EFI_MEMORY_MAP descriptor that is the bin size (assuming allocations are lower than the defined +size). This way, any fluctuations in runtime memory usage are covered by the defined bin size, i.e. the bin size is an +overallocation of runtime memory usage which allows for stability during S4 resumes. + +### MemoryBinLib + +MemoryBinLib is the phase agnostic memory bin logic. This logic is shared between DXE core and PEI core to have +standardized memory bin operation. It handles HOB processing and production (for PEI), memory bin setup in the various +forms, and memory statistics. + +### Boot Flow + +A platform opts into either PEI + DXE memory bins or DXE only (the original implementation). A platform may also define +where the memory bin region is. All of these paths are defined below. + +#### Platform Configuration + +A platform opts into the memory bin feature by producing the +[Memory Type Information HOB](https://github.com/tianocore/edk2/blob/HEAD/MdeModulePkg/Include/Guid/MemoryTypeInformation.h). +This HOB defines the type and size of each bin. If this HOB is not produced, no version of memory bins will be used and +runtime allocations will fall wherever the allocator finds space, likely breaking S4 resume. + +A platform can also define where the memory bins should be allocated by creating a resource descriptor HOB with the +owner set to gEfiMemoryTypeInformationGuid. This is recommended as it will create even more stability in the bins by +ensuring they live at a fixed address. + +With the Memory Type Information HOB produced and optionally the Resource Descriptor HOB produced, a platform will opt +into DXE only memory bins. However, it is recommended a platform opt into PEI memory bins as well for greater stability +as post-mem PEI often makes runtime memory allocations; without memory bins in PEI, these either have a high chance of +breaking S4 resume or must be allocated as non-runtime and relocated in DXE to a runtime bin. + +To opt into PEI memory bins a platform must, in any order: + +- Set `gEfiMdeModulePkgTokenSpaceGuid.PcdPeiMemoryBinsEnable` to `TRUE` in their DSC. +- Produce the Memory Type Information HOB in SEC or pre-mem PEI. +- Optionally produce the Resource Descriptor HOB owned by gEfiMemoryTypeInformationGuid in SEC or pre-mem PEI. This must + not be produced post-mem or DXE will ignore the PEI memory bins because of conflicting Resource Descriptor HOBs. + +#### PEI Memory Bins + +##### PEI Setup + +PEI memory bins are enabled in post-mem PEI in order to have memory available for the allocation of the bins. If a +platform allocates any runtime memory before memory is installed, it will not land in the bin and will jeopardize S4 +resume stability. The PEI memory bin feature is only applicable to the PEI_SERVICES.AllocatePages() and +PEI_SERVICES.FreePages() APIs. PEI_SERVICES.AllocatePool() does not have memory types associated with it and per the +PI spec DXE can ignore the EFI_HOB_MEMORY_POOL HOBs this API produces. I.e., PEI pool memory does not persist into DXE +and so therefore cannot persist into the OS runtime and affect S4 stability. + +When permanent memory is discovered, PEI core is relaunched to begin the post-mem PEI phase. When memory services +are initialized post-mem, PEI core will check for `PcdPeiMemoryBinsEnable`, which is the opt in +mechanism to PEI memory bins. If the PCD is not `FALSE`, the memory bin flow will try again in DXE. + +PEI core will next look for the Memory Type Information HOB to get bin types and sizes. If this is found, PEI memory +bins will be enabled. + +The final piece of configuration PEI core looks for is a Resource Descriptor HOB owned by gEfiMemoryTypeInformationGuid. +If one and only one of these HOBs is discovered, PEI core will use this range for the memory bins (if it is large +enough). + +If the Resource Descriptor HOB is not present, PEI core will allocate the memory for the memory bins as one +large contiguous chunk and then split it up amongst the bins. It will then create a Resource Descriptor HOB owned by +gEfiMemoryTypeInformationGuid to tell DXE core where the PEI memory bins are located. + +Finally, PEI core will update the PHIT free memory region to exclude the memory bin region. + +> **Note:** PEI must be very careful in its usage of global writeable variables. Usage of these pre-mem or if PEI Core +> has not been shadowed to memory will generally result in page faults, as flash is write protected. As a result, this +> design does not rely on any writeable global variables for PEI (but retains them in DXE). Instead, the stack based +> PEI_CORE_INSTANCE is given pointers to the relevant structures which are allocated in post-mem PEI in permanent +> memory. This is done to avoid a HOB lookup for every PEI memory allocation while still avoiding global variables. + +##### PEI Operation + +When PEI_SERVICES.AllocatePages() is called, PEI core will attempt to allocate the request in the memory bin, if +defined. If the request fails, PEI core will fall back to its standard allocation mechanism: attempt to allocate from +the PHIT free memory, if there is not enough space, look for a free memory memory allocation HOB. + +When PEI_SERVICES.FreePages() is called, PEI core will free the pages as normal. + +As with allocating from the PHIT HOB, PEI core will only ever walk down the memory range defined in a given bin, that is +new allocations are given the requested size down from the top of the bin and freeing pages does not increase the +available range in the bin. This is done to keep the PEI allocator simple and because most runtime allocations are +expected in DXE. + +PEI core will mark all of the Memory Allocation HOBs for bin types its allocator produces with +gEfiMemoryTypeInformationGuid. This is done so that DXE can use the Memory Allocation HOBs to build bin statistics, +e.g. which allocations landed within a bin and which landed outside of a bin. Updating the Name field of the Memory +Allocation HOB allows DXE Core to distinguish between allocations that PEI Core made and are subject to bin rules and +those that were before the PEI bins were setup; platforms may produce Memory Allocation HOBs to specify static runtime +regions. These should not be included in the bin logic. If PEI Core is not configured to use memory bins, it will not +mark its allocator created Memory Allocation HOBs with the PEI Core GUID. DXE will then ignore all pre-DXE memory +allocations and how they fall into the bins or not. + +#### DXE Memory Bins + +##### DXE Setup + +DXE core is agnostic to whether PEI core set up memory bins or not, it will simply find the relevant HOBs and operate +on them if present, regardless of producer. + +DXE memory bins apply to the EFI_BOOT_SERVICES.AllocatePages() and EFI_BOOT_SERVICES.FreePages() APIs. The Pool APIs +will respect the memory bins, but only because they use the Page APIs under the hood. + +When DXE core initializes memory services, it will look for the Memory Type Information HOB. If this is not present, +bins will not be initialized. It will then look for a Resource Descriptor HOB with owner gEfiMemoryTypeInformationGuid. +If this HOB is present, the DXE memory bins will be at this location. If it is not present, DXE will allocate memory +bins. + +If the Resource Descriptor HOB was present, DXE core will process through the Memory Allocation HOBs that have the +gEfiMemoryTypeInformationGuid to discover if any bin types have been allocated pre-DXE and whether they fall in or out +of the defined bin range. DXE core will seed its memory statistics with this information. See #BDS-Setup for information +on how the memory statistics are used. + +##### DXE Operation + +When EFI_BOOT_SERVICES.AllocatePages() is called, DXE core will attempt to allocate the memory in the defined bin +region. If the allocation succeeds, DXE core will update the memory statistics to indicate the requested number of pages +landed in the bin. If the allocation failed, DXE core will fall back to its standard pattern, attempting to allocate +outside of the bin. It will also update the memory statistics to indicate that the requested number of pages landed +outside of the bin. + +When EFI_BOOT_SERVICES.FreePages() is called, DXE core will free the pages and update the memory statistics to indicate +that the number of pages either in or out of the bin are not being used. + +The DXE allocator is much smarter than the PEI allocator and does account for bin pages freed back and allows them to +be allocated again. + +DXE core will publish the +[gMemoryTypeInformationGuid](https://github.com/tianocore/edk2/blob/HEAD/MdeModulePkg/Include/Guid/MemoryTypeInformation.h#L26) +config table to publish the memory statistics for BDS to consume to +advise a platform on the correct memory bin sizes. See [BDS Advertisement](#bds-advertisement) for more details. + +When EFI_BOOT_SERVICES.GetMemoryMap() is called, DXE core will create a single EFI_MEMORY_DESCRIPTOR for each memory +bin. + +#### BDS Advertisement + +[UefiBootManagerLib](https://github.com/tianocore/edk2/blob/HEAD/MdeModulePkg/Library/UefiBootManagerLib) has a +mechanism to advise a platform on the ideal bin size and location for S4 stability based on the current boot memory +statistics that DXE has collected. + +Just before launching a given boot option, UefiBootManagerLib (generally called BDS for brevity below) consumes the +`gMemoryTypeInformationGuid` config table that DXE core has produced. It reads the currently used bin pages and +compares them against the last used bin pages, using a heuristic to calculate the ideal number of pages for each memory +bin. It then writes the gEfiMemoryTypeInformation.MemoryTypeInformation variable to give the platform each recommended +memory bin size for S4 stability. BDS will optionally reboot the system if the bin size has changed, depending on +[PcdResetOnMemoryTypeInformationChange](https://github.com/tianocore/edk2/blob/HEAD/MdeModulePkg/MdeModulePkg.dec#L1930). + +##### Platform Adjustments + +Prior to post-mem PEI, it is recommended that the platform consume the gEfiMemoryTypeInformation.MemoryTypeInformation +variable if present. Then, the platform can use the BDS recommended values to create the Memory Type Information HOB. + +>**Note:** The recommended path has a dependency on variable reads being available in pre-mem PEI or SEC. Without +> this, the BDS advertisement cannot be acted upon. PEI memory bins may still be used without variable reads in +> pre-mem PEI/SEC, but the greatest chance of S4 resume success will be following the recommended path. + +#### End-to-End Diagrams + +```mermaid +flowchart TD + Start([Boot Start]) --> SecPreMemPEI[SEC/Pre-Memory PEI Phase] + + SecPreMemPEI --> CheckPlatform{Platform Configuration} + + CheckPlatform -->|Produces Memory Type Info HOB| MemTypeHOB[Memory Type Information HOB Created] + + MemTypeHOB --> CheckResourceHOB{Optional: Resource Descriptor HOB?} + + CheckResourceHOB -->|Yes| ResourceHOB[Resource Descriptor HOB
gEfiMemoryTypeInformationGuid] + CheckResourceHOB -->|No| NoResourceHOB[No Resource Descriptor HOB] + + ResourceHOB --> CheckPEIPath{PEI?} + NoResourceHOB --> CheckPEIPath + + CheckPEIPath -->|Yes| MemDiscovered + CheckPEIPath -->|No PEI| DXEOnly[**DXE Only Path**] + + %% PEI + DXE Path + MemDiscovered --> PEICoreRelaunch[PEI Core Relaunched Post-Memory] + PEICoreRelaunch --> CheckPcd{PcdPeiMemoryBinsEnable TRUE?} + + CheckPcd -->|No| DXEOnly[DXE only bins] + CheckPcd -->|Yes| InitPEIBins[Initialize PEI Memory Bins] + + InitPEIBins --> CheckPEIResourceHOB{Resource Descriptor HOB Present?} + + CheckPEIResourceHOB -->|Yes| UsePEIResource[Use Resource HOB Range for Bins] + CheckPEIResourceHOB -->|No| AllocatePEI[Allocate Contiguous Memory for Bins] + + AllocatePEI --> CreatePEIResourceHOB[Create Resource Descriptor HOB
for DXE] + UsePEIResource --> UpdatePHIT1[Update PHIT to Exclude Bin Region] + CreatePEIResourceHOB --> UpdatePHIT1 + + UpdatePHIT1 --> PEIOperation[PEI Operation:
AllocatePages attempts bin first,
falls back to PHIT/free HOBs] + + PEIOperation --> TransitionToDXE[Transition to DXE] + DXEOnly --> TransitionToDXE + + %% DXE Phase Common + TransitionToDXE --> DXEInit[DXE Core Memory Services Init] + + DXEInit --> CheckDXEMemTypeHOB{Memory Type Info HOB Present?} + + CheckDXEMemTypeHOB -->|No| NoBins[No Memory Bins Initialized] + CheckDXEMemTypeHOB -->|Yes| CheckDXEResourceHOB{Resource Descriptor HOB Present?} + + CheckDXEResourceHOB -->|Yes| ProcessMemAlloc[Process Memory Allocation HOBs
Seed Statistics] + CheckDXEResourceHOB -->|No| AllocateDXEBins[Allocate DXE Memory Bins] + + ProcessMemAlloc --> UseDXEResource[Use Resource HOB Range for DXE Bins] + AllocateDXEBins --> DXEOperation + UseDXEResource --> DXEOperation + + DXEOperation[DXE Operation:
AllocatePages/FreePages use bins
Update statistics] + + DXEOperation --> PublishConfig[Publish Config Table] + NoBins --> BDSPhase + + PublishConfig --> BDSPhase[BDS Phase] + + BDSPhase --> ConsumeStats[BDS Consumes Config Table] + ConsumeStats --> CalculateIdeal[Calculate Ideal Bin Size] + CalculateIdeal --> WriteVariable[Write MemoryTypeInformation Variable] + + WriteVariable --> CheckBinChange{Bin Size Changed?} + + CheckBinChange -->|Yes| CheckRebootPCD{PcdResetOnMemoryTypeInformationChange?} + CheckBinChange -->|No| LaunchBootOption + + CheckRebootPCD -->|TRUE| Reboot[System Reboot] + CheckRebootPCD -->|FALSE| LaunchBootOption[Launch Boot Option] + + Reboot --> Start + LaunchBootOption --> GetMemoryMap[GetMemoryMap Called] + GetMemoryMap --> CreateDescriptor[Create EFI_MEMORY_DESCRIPTOR
for Each Bin] + CreateDescriptor --> OSRuntime[OS Runtime] + + style ResourceHOB fill:#d4edda + style NoResourceHOB fill:#f8d7da + style DXEOnly fill:#5c4a1f,color:#fff + style ResourceHOB fill:#1f4a2f,color:#fff + style NoResourceHOB fill:#5a2a2a,color:#fff +``` diff --git a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc index 56ea05c9288..baaac72d896 100644 --- a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc +++ b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc @@ -120,6 +120,13 @@ UefiBootServicesTableLib|MdePkg/Test/Mock/Library/GoogleTest/MockUefiBootServicesTableLib/MockUefiBootServicesTableLib.inf } + # MU_CHANGE BEGIN: PEI Bins + MdeModulePkg/Core/Dxe/Mem/GoogleTest/MemoryBinGoogleTestHost.inf { + + HobLib|MdePkg/Test/Mock/Library/GoogleTest/MockHobLib/MockHobLib.inf + } + # MU_CHANGE END: PEI Bins + # # Build HOST_APPLICATION Libraries #