summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/Dxe/Mem/Pool.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/Dxe/Mem/Pool.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/Dxe/Mem/Pool.c857
1 files changed, 857 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/Dxe/Mem/Pool.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/Dxe/Mem/Pool.c
new file mode 100644
index 00000000..0294ec5f
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Core/Dxe/Mem/Pool.c
@@ -0,0 +1,857 @@
+/** @file
+ UEFI Memory pool management functions.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "DxeMain.h"
+#include "Imem.h"
+#include "HeapGuard.h"
+
+STATIC EFI_LOCK mPoolMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
+
+#define POOL_FREE_SIGNATURE SIGNATURE_32('p','f','r','0')
+typedef struct {
+ UINT32 Signature;
+ UINT32 Index;
+ LIST_ENTRY Link;
+} POOL_FREE;
+
+
+#define POOL_HEAD_SIGNATURE SIGNATURE_32('p','h','d','0')
+#define POOLPAGE_HEAD_SIGNATURE SIGNATURE_32('p','h','d','1')
+typedef struct {
+ UINT32 Signature;
+ UINT32 Reserved;
+ EFI_MEMORY_TYPE Type;
+ UINTN Size;
+ CHAR8 Data[1];
+} POOL_HEAD;
+
+#define SIZE_OF_POOL_HEAD OFFSET_OF(POOL_HEAD,Data)
+
+#define POOL_TAIL_SIGNATURE SIGNATURE_32('p','t','a','l')
+typedef struct {
+ UINT32 Signature;
+ UINT32 Reserved;
+ UINTN Size;
+} POOL_TAIL;
+
+#define POOL_OVERHEAD (SIZE_OF_POOL_HEAD + sizeof(POOL_TAIL))
+
+#define HEAD_TO_TAIL(a) \
+ ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL)));
+
+//
+// Each element is the sum of the 2 previous ones: this allows us to migrate
+// blocks between bins by splitting them up, while not wasting too much memory
+// as we would in a strict power-of-2 sequence
+//
+STATIC CONST UINT16 mPoolSizeTable[] = {
+ 128, 256, 384, 640, 1024, 1664, 2688, 4352, 7040, 11392, 18432, 29824
+};
+
+#define SIZE_TO_LIST(a) (GetPoolIndexFromSize (a))
+#define LIST_TO_SIZE(a) (mPoolSizeTable [a])
+
+#define MAX_POOL_LIST (ARRAY_SIZE (mPoolSizeTable))
+
+#define MAX_POOL_SIZE (MAX_ADDRESS - POOL_OVERHEAD)
+
+//
+// Globals
+//
+
+#define POOL_SIGNATURE SIGNATURE_32('p','l','s','t')
+typedef struct {
+ INTN Signature;
+ UINTN Used;
+ EFI_MEMORY_TYPE MemoryType;
+ LIST_ENTRY FreeList[MAX_POOL_LIST];
+ LIST_ENTRY Link;
+} POOL;
+
+//
+// Pool header for each memory type.
+//
+POOL mPoolHead[EfiMaxMemoryType];
+
+//
+// List of pool header to search for the appropriate memory type.
+//
+LIST_ENTRY mPoolHeadList = INITIALIZE_LIST_HEAD_VARIABLE (mPoolHeadList);
+
+/**
+ Get pool size table index from the specified size.
+
+ @param Size The specified size to get index from pool table.
+
+ @return The index of pool size table.
+
+**/
+STATIC
+UINTN
+GetPoolIndexFromSize (
+ UINTN Size
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < MAX_POOL_LIST; Index++) {
+ if (mPoolSizeTable [Index] >= Size) {
+ return Index;
+ }
+ }
+ return MAX_POOL_LIST;
+}
+
+/**
+ Called to initialize the pool.
+
+**/
+VOID
+CoreInitializePool (
+ VOID
+ )
+{
+ UINTN Type;
+ UINTN Index;
+
+ for (Type=0; Type < EfiMaxMemoryType; Type++) {
+ mPoolHead[Type].Signature = 0;
+ mPoolHead[Type].Used = 0;
+ mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE) Type;
+ for (Index=0; Index < MAX_POOL_LIST; Index++) {
+ InitializeListHead (&mPoolHead[Type].FreeList[Index]);
+ }
+ }
+}
+
+
+/**
+ Look up pool head for specified memory type.
+
+ @param MemoryType Memory type of which pool head is looked for
+
+ @return Pointer of Corresponding pool head.
+
+**/
+POOL *
+LookupPoolHead (
+ IN EFI_MEMORY_TYPE MemoryType
+ )
+{
+ LIST_ENTRY *Link;
+ POOL *Pool;
+ UINTN Index;
+
+ if ((UINT32)MemoryType < EfiMaxMemoryType) {
+ return &mPoolHead[MemoryType];
+ }
+
+ //
+ // MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI
+ // OS loaders that are provided by operating system vendors.
+ // MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use.
+ //
+ if ((UINT32) MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
+
+ for (Link = mPoolHeadList.ForwardLink; Link != &mPoolHeadList; Link = Link->ForwardLink) {
+ Pool = CR(Link, POOL, Link, POOL_SIGNATURE);
+ if (Pool->MemoryType == MemoryType) {
+ return Pool;
+ }
+ }
+
+ Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL), FALSE);
+ if (Pool == NULL) {
+ return NULL;
+ }
+
+ Pool->Signature = POOL_SIGNATURE;
+ Pool->Used = 0;
+ Pool->MemoryType = MemoryType;
+ for (Index=0; Index < MAX_POOL_LIST; Index++) {
+ InitializeListHead (&Pool->FreeList[Index]);
+ }
+
+ InsertHeadList (&mPoolHeadList, &Pool->Link);
+
+ return Pool;
+ }
+
+ return NULL;
+}
+
+
+
+/**
+ Allocate pool of a particular type.
+
+ @param PoolType Type of pool to allocate
+ @param Size The amount of pool to allocate
+ @param Buffer The address to return a pointer to the allocated
+ pool
+
+ @retval EFI_INVALID_PARAMETER Buffer is NULL.
+ PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
+ PoolType is EfiPersistentMemory.
+ @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
+ @retval EFI_SUCCESS Pool successfully allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+CoreInternalAllocatePool (
+ IN EFI_MEMORY_TYPE PoolType,
+ IN UINTN Size,
+ OUT VOID **Buffer
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN NeedGuard;
+
+ //
+ // If it's not a valid type, fail it
+ //
+ if ((PoolType >= EfiMaxMemoryType && PoolType < MEMORY_TYPE_OEM_RESERVED_MIN) ||
+ (PoolType == EfiConventionalMemory) || (PoolType == EfiPersistentMemory)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Buffer = NULL;
+
+ //
+ // If size is too large, fail it
+ // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES
+ //
+ if (Size > MAX_POOL_SIZE) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NeedGuard = IsPoolTypeToGuard (PoolType) && !mOnGuarding;
+
+ //
+ // Acquire the memory lock and make the allocation
+ //
+ Status = CoreAcquireLockOrFail (&mPoolMemoryLock);
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *Buffer = CoreAllocatePoolI (PoolType, Size, NeedGuard);
+ CoreReleaseLock (&mPoolMemoryLock);
+ return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
+}
+
+/**
+ Allocate pool of a particular type.
+
+ @param PoolType Type of pool to allocate
+ @param Size The amount of pool to allocate
+ @param Buffer The address to return a pointer to the allocated
+ pool
+
+ @retval EFI_INVALID_PARAMETER Buffer is NULL.
+ PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
+ PoolType is EfiPersistentMemory.
+ @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
+ @retval EFI_SUCCESS Pool successfully allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+CoreAllocatePool (
+ IN EFI_MEMORY_TYPE PoolType,
+ IN UINTN Size,
+ OUT VOID **Buffer
+ )
+{
+ EFI_STATUS Status;
+
+ Status = CoreInternalAllocatePool (PoolType, Size, Buffer);
+ if (!EFI_ERROR (Status)) {
+ CoreUpdateProfile (
+ (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
+ MemoryProfileActionAllocatePool,
+ PoolType,
+ Size,
+ *Buffer,
+ NULL
+ );
+ InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
+ }
+ return Status;
+}
+
+/**
+ Internal function. Used by the pool functions to allocate pages
+ to back pool allocation requests.
+
+ @param PoolType The type of memory for the new pool pages
+ @param NoPages No of pages to allocate
+ @param Granularity Bits to align.
+ @param NeedGuard Flag to indicate Guard page is needed or not
+
+ @return The allocated memory, or NULL
+
+**/
+STATIC
+VOID *
+CoreAllocatePoolPagesI (
+ IN EFI_MEMORY_TYPE PoolType,
+ IN UINTN NoPages,
+ IN UINTN Granularity,
+ IN BOOLEAN NeedGuard
+ )
+{
+ VOID *Buffer;
+ EFI_STATUS Status;
+
+ Status = CoreAcquireLockOrFail (&gMemoryLock);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity, NeedGuard);
+ CoreReleaseMemoryLock ();
+
+ if (Buffer != NULL) {
+ if (NeedGuard) {
+ SetGuardForMemory ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, NoPages);
+ }
+ ApplyMemoryProtectionPolicy(EfiConventionalMemory, PoolType,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (NoPages));
+ }
+ return Buffer;
+}
+
+/**
+ Internal function to allocate pool of a particular type.
+ Caller must have the memory lock held
+
+ @param PoolType Type of pool to allocate
+ @param Size The amount of pool to allocate
+ @param NeedGuard Flag to indicate Guard page is needed or not
+
+ @return The allocate pool, or NULL
+
+**/
+VOID *
+CoreAllocatePoolI (
+ IN EFI_MEMORY_TYPE PoolType,
+ IN UINTN Size,
+ IN BOOLEAN NeedGuard
+ )
+{
+ POOL *Pool;
+ POOL_FREE *Free;
+ POOL_HEAD *Head;
+ POOL_TAIL *Tail;
+ CHAR8 *NewPage;
+ VOID *Buffer;
+ UINTN Index;
+ UINTN FSize;
+ UINTN Offset, MaxOffset;
+ UINTN NoPages;
+ UINTN Granularity;
+ BOOLEAN HasPoolTail;
+ BOOLEAN PageAsPool;
+
+ ASSERT_LOCKED (&mPoolMemoryLock);
+
+ if (PoolType == EfiACPIReclaimMemory ||
+ PoolType == EfiACPIMemoryNVS ||
+ PoolType == EfiRuntimeServicesCode ||
+ PoolType == EfiRuntimeServicesData) {
+
+ Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
+ } else {
+ Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
+ }
+
+ //
+ // Adjust the size by the pool header & tail overhead
+ //
+
+ HasPoolTail = !(NeedGuard &&
+ ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
+ PageAsPool = (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) && !mOnGuarding);
+
+ //
+ // Adjusting the Size to be of proper alignment so that
+ // we don't get an unaligned access fault later when
+ // pool_Tail is being initialized
+ //
+ Size = ALIGN_VARIABLE (Size);
+
+ Size += POOL_OVERHEAD;
+ Index = SIZE_TO_LIST(Size);
+ Pool = LookupPoolHead (PoolType);
+ if (Pool== NULL) {
+ return NULL;
+ }
+ Head = NULL;
+
+ //
+ // If allocation is over max size, just allocate pages for the request
+ // (slow)
+ //
+ if (Index >= SIZE_TO_LIST (Granularity) || NeedGuard || PageAsPool) {
+ if (!HasPoolTail) {
+ Size -= sizeof (POOL_TAIL);
+ }
+ NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
+ NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
+ Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity, NeedGuard);
+ if (NeedGuard) {
+ Head = AdjustPoolHeadA ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size);
+ }
+ goto Done;
+ }
+
+ //
+ // If there's no free pool in the proper list size, go get some more pages
+ //
+ if (IsListEmpty (&Pool->FreeList[Index])) {
+
+ Offset = LIST_TO_SIZE (Index);
+ MaxOffset = Granularity;
+
+ //
+ // Check the bins holding larger blocks, and carve one up if needed
+ //
+ while (++Index < SIZE_TO_LIST (Granularity)) {
+ if (!IsListEmpty (&Pool->FreeList[Index])) {
+ Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
+ RemoveEntryList (&Free->Link);
+ NewPage = (VOID *) Free;
+ MaxOffset = LIST_TO_SIZE (Index);
+ goto Carve;
+ }
+ }
+
+ //
+ // Get another page
+ //
+ NewPage = CoreAllocatePoolPagesI (PoolType, EFI_SIZE_TO_PAGES (Granularity),
+ Granularity, NeedGuard);
+ if (NewPage == NULL) {
+ goto Done;
+ }
+
+ //
+ // Serve the allocation request from the head of the allocated block
+ //
+Carve:
+ Head = (POOL_HEAD *) NewPage;
+
+ //
+ // Carve up remaining space into free pool blocks
+ //
+ Index--;
+ while (Offset < MaxOffset) {
+ ASSERT (Index < MAX_POOL_LIST);
+ FSize = LIST_TO_SIZE(Index);
+
+ while (Offset + FSize <= MaxOffset) {
+ Free = (POOL_FREE *) &NewPage[Offset];
+ Free->Signature = POOL_FREE_SIGNATURE;
+ Free->Index = (UINT32)Index;
+ InsertHeadList (&Pool->FreeList[Index], &Free->Link);
+ Offset += FSize;
+ }
+ Index -= 1;
+ }
+
+ ASSERT (Offset == MaxOffset);
+ goto Done;
+ }
+
+ //
+ // Remove entry from free pool list
+ //
+ Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
+ RemoveEntryList (&Free->Link);
+
+ Head = (POOL_HEAD *) Free;
+
+Done:
+ Buffer = NULL;
+
+ if (Head != NULL) {
+
+ //
+ // Account the allocation
+ //
+ Pool->Used += Size;
+
+ //
+ // If we have a pool buffer, fill in the header & tail info
+ //
+ Head->Signature = (PageAsPool) ? POOLPAGE_HEAD_SIGNATURE : POOL_HEAD_SIGNATURE;
+ Head->Size = Size;
+ Head->Type = (EFI_MEMORY_TYPE) PoolType;
+ Buffer = Head->Data;
+
+ if (HasPoolTail) {
+ Tail = HEAD_TO_TAIL (Head);
+ Tail->Signature = POOL_TAIL_SIGNATURE;
+ Tail->Size = Size;
+
+ Size -= POOL_OVERHEAD;
+ } else {
+ Size -= SIZE_OF_POOL_HEAD;
+ }
+
+ DEBUG_CLEAR_MEMORY (Buffer, Size);
+
+ DEBUG ((
+ DEBUG_POOL,
+ "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,
+ Buffer,
+ (UINT64)Size,
+ (UINT64) Pool->Used
+ ));
+
+
+ } else {
+ DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));
+ }
+
+ return Buffer;
+}
+
+
+
+/**
+ Frees pool.
+
+ @param Buffer The allocated pool entry to free
+ @param PoolType Pointer to pool type
+
+ @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
+ @retval EFI_SUCCESS Pool successfully freed.
+
+**/
+EFI_STATUS
+EFIAPI
+CoreInternalFreePool (
+ IN VOID *Buffer,
+ OUT EFI_MEMORY_TYPE *PoolType OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CoreAcquireLock (&mPoolMemoryLock);
+ Status = CoreFreePoolI (Buffer, PoolType);
+ CoreReleaseLock (&mPoolMemoryLock);
+ return Status;
+}
+
+/**
+ Frees pool.
+
+ @param Buffer The allocated pool entry to free
+
+ @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
+ @retval EFI_SUCCESS Pool successfully freed.
+
+**/
+EFI_STATUS
+EFIAPI
+CoreFreePool (
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_MEMORY_TYPE PoolType;
+
+ Status = CoreInternalFreePool (Buffer, &PoolType);
+ if (!EFI_ERROR (Status)) {
+ CoreUpdateProfile (
+ (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
+ MemoryProfileActionFreePool,
+ PoolType,
+ 0,
+ Buffer,
+ NULL
+ );
+ InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
+ }
+ return Status;
+}
+
+/**
+ Internal function. Frees pool pages allocated via CoreAllocatePoolPagesI().
+
+ @param PoolType The type of memory for the pool pages
+ @param Memory The base address to free
+ @param NoPages The number of pages to free
+
+**/
+STATIC
+VOID
+CoreFreePoolPagesI (
+ IN EFI_MEMORY_TYPE PoolType,
+ IN EFI_PHYSICAL_ADDRESS Memory,
+ IN UINTN NoPages
+ )
+{
+ CoreAcquireMemoryLock ();
+ CoreFreePoolPages (Memory, NoPages);
+ CoreReleaseMemoryLock ();
+
+ GuardFreedPagesChecked (Memory, NoPages);
+ ApplyMemoryProtectionPolicy (PoolType, EfiConventionalMemory,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)Memory, EFI_PAGES_TO_SIZE (NoPages));
+}
+
+/**
+ Internal function. Frees guarded pool pages.
+
+ @param PoolType The type of memory for the pool pages
+ @param Memory The base address to free
+ @param NoPages The number of pages to free
+
+**/
+STATIC
+VOID
+CoreFreePoolPagesWithGuard (
+ IN EFI_MEMORY_TYPE PoolType,
+ IN EFI_PHYSICAL_ADDRESS Memory,
+ IN UINTN NoPages
+ )
+{
+ EFI_PHYSICAL_ADDRESS MemoryGuarded;
+ UINTN NoPagesGuarded;
+
+ MemoryGuarded = Memory;
+ NoPagesGuarded = NoPages;
+
+ AdjustMemoryF (&Memory, &NoPages);
+ //
+ // It's safe to unset Guard page inside memory lock because there should
+ // be no memory allocation occurred in updating memory page attribute at
+ // this point. And unsetting Guard page before free will prevent Guard
+ // page just freed back to pool from being allocated right away before
+ // marking it usable (from non-present to present).
+ //
+ UnsetGuardForMemory (MemoryGuarded, NoPagesGuarded);
+ if (NoPages > 0) {
+ CoreFreePoolPagesI (PoolType, Memory, NoPages);
+ }
+}
+
+/**
+ Internal function to free a pool entry.
+ Caller must have the memory lock held
+
+ @param Buffer The allocated pool entry to free
+ @param PoolType Pointer to pool type
+
+ @retval EFI_INVALID_PARAMETER Buffer not valid
+ @retval EFI_SUCCESS Buffer successfully freed.
+
+**/
+EFI_STATUS
+CoreFreePoolI (
+ IN VOID *Buffer,
+ OUT EFI_MEMORY_TYPE *PoolType OPTIONAL
+ )
+{
+ POOL *Pool;
+ POOL_HEAD *Head;
+ POOL_TAIL *Tail;
+ POOL_FREE *Free;
+ UINTN Index;
+ UINTN NoPages;
+ UINTN Size;
+ CHAR8 *NewPage;
+ UINTN Offset;
+ BOOLEAN AllFree;
+ UINTN Granularity;
+ BOOLEAN IsGuarded;
+ BOOLEAN HasPoolTail;
+ BOOLEAN PageAsPool;
+
+ ASSERT(Buffer != NULL);
+ //
+ // Get the head & tail of the pool entry
+ //
+ Head = BASE_CR (Buffer, POOL_HEAD, Data);
+ ASSERT(Head != NULL);
+
+ if (Head->Signature != POOL_HEAD_SIGNATURE &&
+ Head->Signature != POOLPAGE_HEAD_SIGNATURE) {
+ ASSERT (Head->Signature == POOL_HEAD_SIGNATURE ||
+ Head->Signature == POOLPAGE_HEAD_SIGNATURE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IsGuarded = IsPoolTypeToGuard (Head->Type) &&
+ IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
+ HasPoolTail = !(IsGuarded &&
+ ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
+ PageAsPool = (Head->Signature == POOLPAGE_HEAD_SIGNATURE);
+
+ if (HasPoolTail) {
+ Tail = HEAD_TO_TAIL (Head);
+ ASSERT (Tail != NULL);
+
+ //
+ // Debug
+ //
+ ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
+ ASSERT (Head->Size == Tail->Size);
+
+ if (Tail->Signature != POOL_TAIL_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Head->Size != Tail->Size) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ ASSERT_LOCKED (&mPoolMemoryLock);
+
+ //
+ // Determine the pool type and account for it
+ //
+ Size = Head->Size;
+ Pool = LookupPoolHead (Head->Type);
+ if (Pool == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Pool->Used -= Size;
+ DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64) Pool->Used));
+
+ if (Head->Type == EfiACPIReclaimMemory ||
+ Head->Type == EfiACPIMemoryNVS ||
+ Head->Type == EfiRuntimeServicesCode ||
+ Head->Type == EfiRuntimeServicesData) {
+
+ Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
+ } else {
+ Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
+ }
+
+ if (PoolType != NULL) {
+ *PoolType = Head->Type;
+ }
+
+ //
+ // Determine the pool list
+ //
+ Index = SIZE_TO_LIST(Size);
+ DEBUG_CLEAR_MEMORY (Head, Size);
+
+ //
+ // If it's not on the list, it must be pool pages
+ //
+ if (Index >= SIZE_TO_LIST (Granularity) || IsGuarded || PageAsPool) {
+
+ //
+ // Return the memory pages back to free memory
+ //
+ NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
+ NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
+ if (IsGuarded) {
+ Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
+ CoreFreePoolPagesWithGuard (
+ Pool->MemoryType,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)Head,
+ NoPages
+ );
+ } else {
+ CoreFreePoolPagesI (
+ Pool->MemoryType,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)Head,
+ NoPages
+ );
+ }
+
+ } else {
+
+ //
+ // Put the pool entry onto the free pool list
+ //
+ Free = (POOL_FREE *) Head;
+ ASSERT(Free != NULL);
+ Free->Signature = POOL_FREE_SIGNATURE;
+ Free->Index = (UINT32)Index;
+ InsertHeadList (&Pool->FreeList[Index], &Free->Link);
+
+ //
+ // See if all the pool entries in the same page as Free are freed pool
+ // entries
+ //
+ NewPage = (CHAR8 *)((UINTN)Free & ~(Granularity - 1));
+ Free = (POOL_FREE *) &NewPage[0];
+ ASSERT(Free != NULL);
+
+ if (Free->Signature == POOL_FREE_SIGNATURE) {
+
+ AllFree = TRUE;
+ Offset = 0;
+
+ while ((Offset < Granularity) && (AllFree)) {
+ Free = (POOL_FREE *) &NewPage[Offset];
+ ASSERT(Free != NULL);
+ if (Free->Signature != POOL_FREE_SIGNATURE) {
+ AllFree = FALSE;
+ }
+ Offset += LIST_TO_SIZE(Free->Index);
+ }
+
+ if (AllFree) {
+
+ //
+ // All of the pool entries in the same page as Free are free pool
+ // entries
+ // Remove all of these pool entries from the free loop lists.
+ //
+ Free = (POOL_FREE *) &NewPage[0];
+ ASSERT(Free != NULL);
+ Offset = 0;
+
+ while (Offset < Granularity) {
+ Free = (POOL_FREE *) &NewPage[Offset];
+ ASSERT(Free != NULL);
+ RemoveEntryList (&Free->Link);
+ Offset += LIST_TO_SIZE(Free->Index);
+ }
+
+ //
+ // Free the page
+ //
+ CoreFreePoolPagesI (Pool->MemoryType, (EFI_PHYSICAL_ADDRESS) (UINTN)NewPage,
+ EFI_SIZE_TO_PAGES (Granularity));
+ }
+ }
+ }
+
+ //
+ // If this is an OS/OEM specific memory type, then check to see if the last
+ // portion of that memory type has been freed. If it has, then free the
+ // list entry for that memory type
+ //
+ if (((UINT32) Pool->MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) && Pool->Used == 0) {
+ RemoveEntryList (&Pool->Link);
+ CoreFreePoolI (Pool, NULL);
+ }
+
+ return EFI_SUCCESS;
+}
+