2 UEFI Memory pool management functions.
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 STATIC EFI_LOCK mPoolMemoryLock
= EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY
);
20 #define POOL_FREE_SIGNATURE SIGNATURE_32('p','f','r','0')
28 #define POOL_HEAD_SIGNATURE SIGNATURE_32('p','h','d','0')
37 #define SIZE_OF_POOL_HEAD OFFSET_OF(POOL_HEAD,Data)
39 #define POOL_TAIL_SIGNATURE SIGNATURE_32('p','t','a','l')
46 #define POOL_OVERHEAD (SIZE_OF_POOL_HEAD + sizeof(POOL_TAIL))
48 #define HEAD_TO_TAIL(a) \
49 ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL)));
52 // Each element is the sum of the 2 previous ones: this allows us to migrate
53 // blocks between bins by splitting them up, while not wasting too much memory
54 // as we would in a strict power-of-2 sequence
56 STATIC CONST UINT16 mPoolSizeTable
[] = {
57 128, 256, 384, 640, 1024, 1664, 2688, 4352, 7040, 11392, 18432, 29824
60 #define SIZE_TO_LIST(a) (GetPoolIndexFromSize (a))
61 #define LIST_TO_SIZE(a) (mPoolSizeTable [a])
63 #define MAX_POOL_LIST (ARRAY_SIZE (mPoolSizeTable))
65 #define MAX_POOL_SIZE (MAX_ADDRESS - POOL_OVERHEAD)
71 #define POOL_SIGNATURE SIGNATURE_32('p','l','s','t')
75 EFI_MEMORY_TYPE MemoryType
;
76 LIST_ENTRY FreeList
[MAX_POOL_LIST
];
81 // Pool header for each memory type.
83 POOL mPoolHead
[EfiMaxMemoryType
];
86 // List of pool header to search for the appropriate memory type.
88 LIST_ENTRY mPoolHeadList
= INITIALIZE_LIST_HEAD_VARIABLE (mPoolHeadList
);
91 Get pool size table index from the specified size.
93 @param Size The specified size to get index from pool table.
95 @return The index of pool size table.
100 GetPoolIndexFromSize (
106 for (Index
= 0; Index
< MAX_POOL_LIST
; Index
++) {
107 if (mPoolSizeTable
[Index
] >= Size
) {
111 return MAX_POOL_LIST
;
115 Called to initialize the pool.
126 for (Type
=0; Type
< EfiMaxMemoryType
; Type
++) {
127 mPoolHead
[Type
].Signature
= 0;
128 mPoolHead
[Type
].Used
= 0;
129 mPoolHead
[Type
].MemoryType
= (EFI_MEMORY_TYPE
) Type
;
130 for (Index
=0; Index
< MAX_POOL_LIST
; Index
++) {
131 InitializeListHead (&mPoolHead
[Type
].FreeList
[Index
]);
138 Look up pool head for specified memory type.
140 @param MemoryType Memory type of which pool head is looked for
142 @return Pointer of Corresponding pool head.
147 IN EFI_MEMORY_TYPE MemoryType
154 if ((UINT32
)MemoryType
< EfiMaxMemoryType
) {
155 return &mPoolHead
[MemoryType
];
159 // MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI
160 // OS loaders that are provided by operating system vendors.
161 // MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use.
163 if ((UINT32
) MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
165 for (Link
= mPoolHeadList
.ForwardLink
; Link
!= &mPoolHeadList
; Link
= Link
->ForwardLink
) {
166 Pool
= CR(Link
, POOL
, Link
, POOL_SIGNATURE
);
167 if (Pool
->MemoryType
== MemoryType
) {
172 Pool
= CoreAllocatePoolI (EfiBootServicesData
, sizeof (POOL
));
177 Pool
->Signature
= POOL_SIGNATURE
;
179 Pool
->MemoryType
= MemoryType
;
180 for (Index
=0; Index
< MAX_POOL_LIST
; Index
++) {
181 InitializeListHead (&Pool
->FreeList
[Index
]);
184 InsertHeadList (&mPoolHeadList
, &Pool
->Link
);
195 Allocate pool of a particular type.
197 @param PoolType Type of pool to allocate
198 @param Size The amount of pool to allocate
199 @param Buffer The address to return a pointer to the allocated
202 @retval EFI_INVALID_PARAMETER Buffer is NULL.
203 PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
204 PoolType is EfiPersistentMemory.
205 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
206 @retval EFI_SUCCESS Pool successfully allocated.
211 CoreInternalAllocatePool (
212 IN EFI_MEMORY_TYPE PoolType
,
220 // If it's not a valid type, fail it
222 if ((PoolType
>= EfiMaxMemoryType
&& PoolType
< MEMORY_TYPE_OEM_RESERVED_MIN
) ||
223 (PoolType
== EfiConventionalMemory
) || (PoolType
== EfiPersistentMemory
)) {
224 return EFI_INVALID_PARAMETER
;
227 if (Buffer
== NULL
) {
228 return EFI_INVALID_PARAMETER
;
234 // If size is too large, fail it
235 // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES
237 if (Size
> MAX_POOL_SIZE
) {
238 return EFI_OUT_OF_RESOURCES
;
242 // Acquire the memory lock and make the allocation
244 Status
= CoreAcquireLockOrFail (&mPoolMemoryLock
);
245 if (EFI_ERROR (Status
)) {
246 return EFI_OUT_OF_RESOURCES
;
249 *Buffer
= CoreAllocatePoolI (PoolType
, Size
);
250 CoreReleaseLock (&mPoolMemoryLock
);
251 return (*Buffer
!= NULL
) ? EFI_SUCCESS
: EFI_OUT_OF_RESOURCES
;
255 Allocate pool of a particular type.
257 @param PoolType Type of pool to allocate
258 @param Size The amount of pool to allocate
259 @param Buffer The address to return a pointer to the allocated
262 @retval EFI_INVALID_PARAMETER Buffer is NULL.
263 PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
264 PoolType is EfiPersistentMemory.
265 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
266 @retval EFI_SUCCESS Pool successfully allocated.
272 IN EFI_MEMORY_TYPE PoolType
,
279 Status
= CoreInternalAllocatePool (PoolType
, Size
, Buffer
);
280 if (!EFI_ERROR (Status
)) {
282 (EFI_PHYSICAL_ADDRESS
) (UINTN
) RETURN_ADDRESS (0),
283 MemoryProfileActionAllocatePool
,
289 InstallMemoryAttributesTableOnMemoryAllocation (PoolType
);
295 Internal function. Used by the pool functions to allocate pages
296 to back pool allocation requests.
298 @param PoolType The type of memory for the new pool pages
299 @param NoPages No of pages to allocate
300 @param Granularity Bits to align.
302 @return The allocated memory, or NULL
307 CoreAllocatePoolPagesI (
308 IN EFI_MEMORY_TYPE PoolType
,
316 Status
= CoreAcquireLockOrFail (&gMemoryLock
);
317 if (EFI_ERROR (Status
)) {
321 Buffer
= CoreAllocatePoolPages (PoolType
, NoPages
, Granularity
);
322 CoreReleaseMemoryLock ();
324 if (Buffer
!= NULL
) {
325 ApplyMemoryProtectionPolicy (EfiConventionalMemory
, PoolType
,
326 (EFI_PHYSICAL_ADDRESS
)(UINTN
)Buffer
, EFI_PAGES_TO_SIZE (NoPages
));
332 Internal function to allocate pool of a particular type.
333 Caller must have the memory lock held
335 @param PoolType Type of pool to allocate
336 @param Size The amount of pool to allocate
338 @return The allocate pool, or NULL
343 IN EFI_MEMORY_TYPE PoolType
,
355 UINTN Offset
, MaxOffset
;
359 ASSERT_LOCKED (&mPoolMemoryLock
);
361 if (PoolType
== EfiACPIReclaimMemory
||
362 PoolType
== EfiACPIMemoryNVS
||
363 PoolType
== EfiRuntimeServicesCode
||
364 PoolType
== EfiRuntimeServicesData
) {
366 Granularity
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
368 Granularity
= DEFAULT_PAGE_ALLOCATION_GRANULARITY
;
372 // Adjust the size by the pool header & tail overhead
376 // Adjusting the Size to be of proper alignment so that
377 // we don't get an unaligned access fault later when
378 // pool_Tail is being initialized
380 Size
= ALIGN_VARIABLE (Size
);
382 Size
+= POOL_OVERHEAD
;
383 Index
= SIZE_TO_LIST(Size
);
384 Pool
= LookupPoolHead (PoolType
);
391 // If allocation is over max size, just allocate pages for the request
394 if (Index
>= SIZE_TO_LIST (Granularity
)) {
395 NoPages
= EFI_SIZE_TO_PAGES(Size
) + EFI_SIZE_TO_PAGES (Granularity
) - 1;
396 NoPages
&= ~(UINTN
)(EFI_SIZE_TO_PAGES (Granularity
) - 1);
397 Head
= CoreAllocatePoolPagesI (PoolType
, NoPages
, Granularity
);
402 // If there's no free pool in the proper list size, go get some more pages
404 if (IsListEmpty (&Pool
->FreeList
[Index
])) {
406 Offset
= LIST_TO_SIZE (Index
);
407 MaxOffset
= Granularity
;
410 // Check the bins holding larger blocks, and carve one up if needed
412 while (++Index
< SIZE_TO_LIST (Granularity
)) {
413 if (!IsListEmpty (&Pool
->FreeList
[Index
])) {
414 Free
= CR (Pool
->FreeList
[Index
].ForwardLink
, POOL_FREE
, Link
, POOL_FREE_SIGNATURE
);
415 RemoveEntryList (&Free
->Link
);
416 NewPage
= (VOID
*) Free
;
417 MaxOffset
= LIST_TO_SIZE (Index
);
425 NewPage
= CoreAllocatePoolPagesI (PoolType
, EFI_SIZE_TO_PAGES (Granularity
), Granularity
);
426 if (NewPage
== NULL
) {
431 // Serve the allocation request from the head of the allocated block
434 Head
= (POOL_HEAD
*) NewPage
;
437 // Carve up remaining space into free pool blocks
440 while (Offset
< MaxOffset
) {
441 ASSERT (Index
< MAX_POOL_LIST
);
442 FSize
= LIST_TO_SIZE(Index
);
444 while (Offset
+ FSize
<= MaxOffset
) {
445 Free
= (POOL_FREE
*) &NewPage
[Offset
];
446 Free
->Signature
= POOL_FREE_SIGNATURE
;
447 Free
->Index
= (UINT32
)Index
;
448 InsertHeadList (&Pool
->FreeList
[Index
], &Free
->Link
);
454 ASSERT (Offset
== MaxOffset
);
459 // Remove entry from free pool list
461 Free
= CR (Pool
->FreeList
[Index
].ForwardLink
, POOL_FREE
, Link
, POOL_FREE_SIGNATURE
);
462 RemoveEntryList (&Free
->Link
);
464 Head
= (POOL_HEAD
*) Free
;
472 // If we have a pool buffer, fill in the header & tail info
474 Head
->Signature
= POOL_HEAD_SIGNATURE
;
476 Head
->Type
= (EFI_MEMORY_TYPE
) PoolType
;
477 Tail
= HEAD_TO_TAIL (Head
);
478 Tail
->Signature
= POOL_TAIL_SIGNATURE
;
481 DEBUG_CLEAR_MEMORY (Buffer
, Size
- POOL_OVERHEAD
);
485 "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType
,
487 (UINT64
)(Size
- POOL_OVERHEAD
),
492 // Account the allocation
497 DEBUG ((DEBUG_ERROR
| DEBUG_POOL
, "AllocatePool: failed to allocate %ld bytes\n", (UINT64
) Size
));
508 @param Buffer The allocated pool entry to free
509 @param PoolType Pointer to pool type
511 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
512 @retval EFI_SUCCESS Pool successfully freed.
517 CoreInternalFreePool (
519 OUT EFI_MEMORY_TYPE
*PoolType OPTIONAL
524 if (Buffer
== NULL
) {
525 return EFI_INVALID_PARAMETER
;
528 CoreAcquireLock (&mPoolMemoryLock
);
529 Status
= CoreFreePoolI (Buffer
, PoolType
);
530 CoreReleaseLock (&mPoolMemoryLock
);
537 @param Buffer The allocated pool entry to free
539 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
540 @retval EFI_SUCCESS Pool successfully freed.
550 EFI_MEMORY_TYPE PoolType
;
552 Status
= CoreInternalFreePool (Buffer
, &PoolType
);
553 if (!EFI_ERROR (Status
)) {
555 (EFI_PHYSICAL_ADDRESS
) (UINTN
) RETURN_ADDRESS (0),
556 MemoryProfileActionFreePool
,
562 InstallMemoryAttributesTableOnMemoryAllocation (PoolType
);
568 Internal function. Frees pool pages allocated via CoreAllocatePoolPagesI().
570 @param PoolType The type of memory for the pool pages
571 @param Memory The base address to free
572 @param NoPages The number of pages to free
578 IN EFI_MEMORY_TYPE PoolType
,
579 IN EFI_PHYSICAL_ADDRESS Memory
,
583 CoreAcquireMemoryLock ();
584 CoreFreePoolPages (Memory
, NoPages
);
585 CoreReleaseMemoryLock ();
587 ApplyMemoryProtectionPolicy (PoolType
, EfiConventionalMemory
,
588 (EFI_PHYSICAL_ADDRESS
)(UINTN
)Memory
, EFI_PAGES_TO_SIZE (NoPages
));
592 Internal function to free a pool entry.
593 Caller must have the memory lock held
595 @param Buffer The allocated pool entry to free
596 @param PoolType Pointer to pool type
598 @retval EFI_INVALID_PARAMETER Buffer not valid
599 @retval EFI_SUCCESS Buffer successfully freed.
605 OUT EFI_MEMORY_TYPE
*PoolType OPTIONAL
620 ASSERT(Buffer
!= NULL
);
622 // Get the head & tail of the pool entry
624 Head
= CR (Buffer
, POOL_HEAD
, Data
, POOL_HEAD_SIGNATURE
);
625 ASSERT(Head
!= NULL
);
627 if (Head
->Signature
!= POOL_HEAD_SIGNATURE
) {
628 return EFI_INVALID_PARAMETER
;
631 Tail
= HEAD_TO_TAIL (Head
);
632 ASSERT(Tail
!= NULL
);
637 ASSERT (Tail
->Signature
== POOL_TAIL_SIGNATURE
);
638 ASSERT (Head
->Size
== Tail
->Size
);
639 ASSERT_LOCKED (&mPoolMemoryLock
);
641 if (Tail
->Signature
!= POOL_TAIL_SIGNATURE
) {
642 return EFI_INVALID_PARAMETER
;
645 if (Head
->Size
!= Tail
->Size
) {
646 return EFI_INVALID_PARAMETER
;
650 // Determine the pool type and account for it
653 Pool
= LookupPoolHead (Head
->Type
);
655 return EFI_INVALID_PARAMETER
;
658 DEBUG ((DEBUG_POOL
, "FreePool: %p (len %lx) %,ld\n", Head
->Data
, (UINT64
)(Head
->Size
- POOL_OVERHEAD
), (UINT64
) Pool
->Used
));
660 if (Head
->Type
== EfiACPIReclaimMemory
||
661 Head
->Type
== EfiACPIMemoryNVS
||
662 Head
->Type
== EfiRuntimeServicesCode
||
663 Head
->Type
== EfiRuntimeServicesData
) {
665 Granularity
= RUNTIME_PAGE_ALLOCATION_GRANULARITY
;
667 Granularity
= DEFAULT_PAGE_ALLOCATION_GRANULARITY
;
670 if (PoolType
!= NULL
) {
671 *PoolType
= Head
->Type
;
675 // Determine the pool list
677 Index
= SIZE_TO_LIST(Size
);
678 DEBUG_CLEAR_MEMORY (Head
, Size
);
681 // If it's not on the list, it must be pool pages
683 if (Index
>= SIZE_TO_LIST (Granularity
)) {
686 // Return the memory pages back to free memory
688 NoPages
= EFI_SIZE_TO_PAGES(Size
) + EFI_SIZE_TO_PAGES (Granularity
) - 1;
689 NoPages
&= ~(UINTN
)(EFI_SIZE_TO_PAGES (Granularity
) - 1);
690 CoreFreePoolPagesI (Pool
->MemoryType
, (EFI_PHYSICAL_ADDRESS
) (UINTN
) Head
, NoPages
);
695 // Put the pool entry onto the free pool list
697 Free
= (POOL_FREE
*) Head
;
698 ASSERT(Free
!= NULL
);
699 Free
->Signature
= POOL_FREE_SIGNATURE
;
700 Free
->Index
= (UINT32
)Index
;
701 InsertHeadList (&Pool
->FreeList
[Index
], &Free
->Link
);
704 // See if all the pool entries in the same page as Free are freed pool
707 NewPage
= (CHAR8
*)((UINTN
)Free
& ~(Granularity
- 1));
708 Free
= (POOL_FREE
*) &NewPage
[0];
709 ASSERT(Free
!= NULL
);
711 if (Free
->Signature
== POOL_FREE_SIGNATURE
) {
716 while ((Offset
< Granularity
) && (AllFree
)) {
717 Free
= (POOL_FREE
*) &NewPage
[Offset
];
718 ASSERT(Free
!= NULL
);
719 if (Free
->Signature
!= POOL_FREE_SIGNATURE
) {
722 Offset
+= LIST_TO_SIZE(Free
->Index
);
728 // All of the pool entries in the same page as Free are free pool
730 // Remove all of these pool entries from the free loop lists.
732 Free
= (POOL_FREE
*) &NewPage
[0];
733 ASSERT(Free
!= NULL
);
736 while (Offset
< Granularity
) {
737 Free
= (POOL_FREE
*) &NewPage
[Offset
];
738 ASSERT(Free
!= NULL
);
739 RemoveEntryList (&Free
->Link
);
740 Offset
+= LIST_TO_SIZE(Free
->Index
);
746 CoreFreePoolPagesI (Pool
->MemoryType
, (EFI_PHYSICAL_ADDRESS
) (UINTN
)NewPage
,
747 EFI_SIZE_TO_PAGES (Granularity
));
753 // If this is an OS/OEM specific memory type, then check to see if the last
754 // portion of that memory type has been freed. If it has, then free the
755 // list entry for that memory type
757 if (((UINT32
) Pool
->MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) && Pool
->Used
== 0) {
758 RemoveEntryList (&Pool
->Link
);
759 CoreFreePoolI (Pool
, NULL
);