1 /** @file
2   UEFI Memory pool management functions.
3 
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "DxeMain.h"
10 #include "Imem.h"
11 #include "HeapGuard.h"
12 
13 STATIC EFI_LOCK mPoolMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
14 
15 #define POOL_FREE_SIGNATURE   SIGNATURE_32('p','f','r','0')
16 typedef struct {
17   UINT32          Signature;
18   UINT32          Index;
19   LIST_ENTRY      Link;
20 } POOL_FREE;
21 
22 
23 #define POOL_HEAD_SIGNATURE       SIGNATURE_32('p','h','d','0')
24 #define POOLPAGE_HEAD_SIGNATURE   SIGNATURE_32('p','h','d','1')
25 typedef struct {
26   UINT32          Signature;
27   UINT32          Reserved;
28   EFI_MEMORY_TYPE Type;
29   UINTN           Size;
30   CHAR8           Data[1];
31 } POOL_HEAD;
32 
33 #define SIZE_OF_POOL_HEAD OFFSET_OF(POOL_HEAD,Data)
34 
35 #define POOL_TAIL_SIGNATURE   SIGNATURE_32('p','t','a','l')
36 typedef struct {
37   UINT32      Signature;
38   UINT32      Reserved;
39   UINTN       Size;
40 } POOL_TAIL;
41 
42 #define POOL_OVERHEAD (SIZE_OF_POOL_HEAD + sizeof(POOL_TAIL))
43 
44 #define HEAD_TO_TAIL(a)   \
45   ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL)));
46 
47 //
48 // Each element is the sum of the 2 previous ones: this allows us to migrate
49 // blocks between bins by splitting them up, while not wasting too much memory
50 // as we would in a strict power-of-2 sequence
51 //
52 STATIC CONST UINT16 mPoolSizeTable[] = {
53   128, 256, 384, 640, 1024, 1664, 2688, 4352, 7040, 11392, 18432, 29824
54 };
55 
56 #define SIZE_TO_LIST(a)   (GetPoolIndexFromSize (a))
57 #define LIST_TO_SIZE(a)   (mPoolSizeTable [a])
58 
59 #define MAX_POOL_LIST     (ARRAY_SIZE (mPoolSizeTable))
60 
61 #define MAX_POOL_SIZE     (MAX_ADDRESS - POOL_OVERHEAD)
62 
63 //
64 // Globals
65 //
66 
67 #define POOL_SIGNATURE  SIGNATURE_32('p','l','s','t')
68 typedef struct {
69     INTN             Signature;
70     UINTN            Used;
71     EFI_MEMORY_TYPE  MemoryType;
72     LIST_ENTRY       FreeList[MAX_POOL_LIST];
73     LIST_ENTRY       Link;
74 } POOL;
75 
76 //
77 // Pool header for each memory type.
78 //
79 POOL            mPoolHead[EfiMaxMemoryType];
80 
81 //
82 // List of pool header to search for the appropriate memory type.
83 //
84 LIST_ENTRY      mPoolHeadList = INITIALIZE_LIST_HEAD_VARIABLE (mPoolHeadList);
85 
86 /**
87   Get pool size table index from the specified size.
88 
89   @param  Size          The specified size to get index from pool table.
90 
91   @return               The index of pool size table.
92 
93 **/
94 STATIC
95 UINTN
GetPoolIndexFromSize(UINTN Size)96 GetPoolIndexFromSize (
97   UINTN   Size
98   )
99 {
100   UINTN   Index;
101 
102   for (Index = 0; Index < MAX_POOL_LIST; Index++) {
103     if (mPoolSizeTable [Index] >= Size) {
104       return Index;
105     }
106   }
107   return MAX_POOL_LIST;
108 }
109 
110 /**
111   Called to initialize the pool.
112 
113 **/
114 VOID
CoreInitializePool(VOID)115 CoreInitializePool (
116   VOID
117   )
118 {
119   UINTN  Type;
120   UINTN  Index;
121 
122   for (Type=0; Type < EfiMaxMemoryType; Type++) {
123     mPoolHead[Type].Signature  = 0;
124     mPoolHead[Type].Used       = 0;
125     mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE) Type;
126     for (Index=0; Index < MAX_POOL_LIST; Index++) {
127       InitializeListHead (&mPoolHead[Type].FreeList[Index]);
128     }
129   }
130 }
131 
132 
133 /**
134   Look up pool head for specified memory type.
135 
136   @param  MemoryType             Memory type of which pool head is looked for
137 
138   @return Pointer of Corresponding pool head.
139 
140 **/
141 POOL *
LookupPoolHead(IN EFI_MEMORY_TYPE MemoryType)142 LookupPoolHead (
143   IN EFI_MEMORY_TYPE  MemoryType
144   )
145 {
146   LIST_ENTRY      *Link;
147   POOL            *Pool;
148   UINTN           Index;
149 
150   if ((UINT32)MemoryType < EfiMaxMemoryType) {
151     return &mPoolHead[MemoryType];
152   }
153 
154   //
155   // MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI
156   // OS loaders that are provided by operating system vendors.
157   // MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use.
158   //
159   if ((UINT32) MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
160 
161     for (Link = mPoolHeadList.ForwardLink; Link != &mPoolHeadList; Link = Link->ForwardLink) {
162       Pool = CR(Link, POOL, Link, POOL_SIGNATURE);
163       if (Pool->MemoryType == MemoryType) {
164         return Pool;
165       }
166     }
167 
168     Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL), FALSE);
169     if (Pool == NULL) {
170       return NULL;
171     }
172 
173     Pool->Signature = POOL_SIGNATURE;
174     Pool->Used      = 0;
175     Pool->MemoryType = MemoryType;
176     for (Index=0; Index < MAX_POOL_LIST; Index++) {
177       InitializeListHead (&Pool->FreeList[Index]);
178     }
179 
180     InsertHeadList (&mPoolHeadList, &Pool->Link);
181 
182     return Pool;
183   }
184 
185   return NULL;
186 }
187 
188 
189 
190 /**
191   Allocate pool of a particular type.
192 
193   @param  PoolType               Type of pool to allocate
194   @param  Size                   The amount of pool to allocate
195   @param  Buffer                 The address to return a pointer to the allocated
196                                  pool
197 
198   @retval EFI_INVALID_PARAMETER  Buffer is NULL.
199                                  PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
200                                  PoolType is EfiPersistentMemory.
201   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
202   @retval EFI_SUCCESS            Pool successfully allocated.
203 
204 **/
205 EFI_STATUS
206 EFIAPI
CoreInternalAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)207 CoreInternalAllocatePool (
208   IN EFI_MEMORY_TYPE  PoolType,
209   IN UINTN            Size,
210   OUT VOID            **Buffer
211   )
212 {
213   EFI_STATUS            Status;
214   BOOLEAN               NeedGuard;
215 
216   //
217   // If it's not a valid type, fail it
218   //
219   if ((PoolType >= EfiMaxMemoryType && PoolType < MEMORY_TYPE_OEM_RESERVED_MIN) ||
220        (PoolType == EfiConventionalMemory) || (PoolType == EfiPersistentMemory)) {
221     return EFI_INVALID_PARAMETER;
222   }
223 
224   if (Buffer == NULL) {
225     return EFI_INVALID_PARAMETER;
226   }
227 
228   *Buffer = NULL;
229 
230   //
231   // If size is too large, fail it
232   // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES
233   //
234   if (Size > MAX_POOL_SIZE) {
235     return EFI_OUT_OF_RESOURCES;
236   }
237 
238   NeedGuard = IsPoolTypeToGuard (PoolType) && !mOnGuarding;
239 
240   //
241   // Acquire the memory lock and make the allocation
242   //
243   Status = CoreAcquireLockOrFail (&mPoolMemoryLock);
244   if (EFI_ERROR (Status)) {
245     return EFI_OUT_OF_RESOURCES;
246   }
247 
248   *Buffer = CoreAllocatePoolI (PoolType, Size, NeedGuard);
249   CoreReleaseLock (&mPoolMemoryLock);
250   return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
251 }
252 
253 /**
254   Allocate pool of a particular type.
255 
256   @param  PoolType               Type of pool to allocate
257   @param  Size                   The amount of pool to allocate
258   @param  Buffer                 The address to return a pointer to the allocated
259                                  pool
260 
261   @retval EFI_INVALID_PARAMETER  Buffer is NULL.
262                                  PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
263                                  PoolType is EfiPersistentMemory.
264   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
265   @retval EFI_SUCCESS            Pool successfully allocated.
266 
267 **/
268 EFI_STATUS
269 EFIAPI
CoreAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)270 CoreAllocatePool (
271   IN EFI_MEMORY_TYPE  PoolType,
272   IN UINTN            Size,
273   OUT VOID            **Buffer
274   )
275 {
276   EFI_STATUS  Status;
277 
278   Status = CoreInternalAllocatePool (PoolType, Size, Buffer);
279   if (!EFI_ERROR (Status)) {
280     CoreUpdateProfile (
281       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
282       MemoryProfileActionAllocatePool,
283       PoolType,
284       Size,
285       *Buffer,
286       NULL
287       );
288     InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
289   }
290   return Status;
291 }
292 
293 /**
294   Internal function.  Used by the pool functions to allocate pages
295   to back pool allocation requests.
296 
297   @param  PoolType               The type of memory for the new pool pages
298   @param  NoPages                No of pages to allocate
299   @param  Granularity            Bits to align.
300   @param  NeedGuard              Flag to indicate Guard page is needed or not
301 
302   @return The allocated memory, or NULL
303 
304 **/
305 STATIC
306 VOID *
CoreAllocatePoolPagesI(IN EFI_MEMORY_TYPE PoolType,IN UINTN NoPages,IN UINTN Granularity,IN BOOLEAN NeedGuard)307 CoreAllocatePoolPagesI (
308   IN EFI_MEMORY_TYPE    PoolType,
309   IN UINTN              NoPages,
310   IN UINTN              Granularity,
311   IN BOOLEAN            NeedGuard
312   )
313 {
314   VOID        *Buffer;
315   EFI_STATUS  Status;
316 
317   Status = CoreAcquireLockOrFail (&gMemoryLock);
318   if (EFI_ERROR (Status)) {
319     return NULL;
320   }
321 
322   Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity, NeedGuard);
323   CoreReleaseMemoryLock ();
324 
325   if (Buffer != NULL) {
326     if (NeedGuard) {
327       SetGuardForMemory ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, NoPages);
328     }
329     ApplyMemoryProtectionPolicy(EfiConventionalMemory, PoolType,
330       (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (NoPages));
331   }
332   return Buffer;
333 }
334 
335 /**
336   Internal function to allocate pool of a particular type.
337   Caller must have the memory lock held
338 
339   @param  PoolType               Type of pool to allocate
340   @param  Size                   The amount of pool to allocate
341   @param  NeedGuard              Flag to indicate Guard page is needed or not
342 
343   @return The allocate pool, or NULL
344 
345 **/
346 VOID *
CoreAllocatePoolI(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,IN BOOLEAN NeedGuard)347 CoreAllocatePoolI (
348   IN EFI_MEMORY_TYPE  PoolType,
349   IN UINTN            Size,
350   IN BOOLEAN          NeedGuard
351   )
352 {
353   POOL        *Pool;
354   POOL_FREE   *Free;
355   POOL_HEAD   *Head;
356   POOL_TAIL   *Tail;
357   CHAR8       *NewPage;
358   VOID        *Buffer;
359   UINTN       Index;
360   UINTN       FSize;
361   UINTN       Offset, MaxOffset;
362   UINTN       NoPages;
363   UINTN       Granularity;
364   BOOLEAN     HasPoolTail;
365   BOOLEAN     PageAsPool;
366 
367   ASSERT_LOCKED (&mPoolMemoryLock);
368 
369   if  (PoolType == EfiACPIReclaimMemory   ||
370        PoolType == EfiACPIMemoryNVS       ||
371        PoolType == EfiRuntimeServicesCode ||
372        PoolType == EfiRuntimeServicesData) {
373 
374     Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
375   } else {
376     Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
377   }
378 
379   //
380   // Adjust the size by the pool header & tail overhead
381   //
382 
383   HasPoolTail  = !(NeedGuard &&
384                    ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
385   PageAsPool = (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) && !mOnGuarding);
386 
387   //
388   // Adjusting the Size to be of proper alignment so that
389   // we don't get an unaligned access fault later when
390   // pool_Tail is being initialized
391   //
392   Size = ALIGN_VARIABLE (Size);
393 
394   Size += POOL_OVERHEAD;
395   Index = SIZE_TO_LIST(Size);
396   Pool = LookupPoolHead (PoolType);
397   if (Pool== NULL) {
398     return NULL;
399   }
400   Head = NULL;
401 
402   //
403   // If allocation is over max size, just allocate pages for the request
404   // (slow)
405   //
406   if (Index >= SIZE_TO_LIST (Granularity) || NeedGuard || PageAsPool) {
407     if (!HasPoolTail) {
408       Size -= sizeof (POOL_TAIL);
409     }
410     NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
411     NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
412     Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity, NeedGuard);
413     if (NeedGuard) {
414       Head = AdjustPoolHeadA ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size);
415     }
416     goto Done;
417   }
418 
419   //
420   // If there's no free pool in the proper list size, go get some more pages
421   //
422   if (IsListEmpty (&Pool->FreeList[Index])) {
423 
424     Offset = LIST_TO_SIZE (Index);
425     MaxOffset = Granularity;
426 
427     //
428     // Check the bins holding larger blocks, and carve one up if needed
429     //
430     while (++Index < SIZE_TO_LIST (Granularity)) {
431       if (!IsListEmpty (&Pool->FreeList[Index])) {
432         Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
433         RemoveEntryList (&Free->Link);
434         NewPage = (VOID *) Free;
435         MaxOffset = LIST_TO_SIZE (Index);
436         goto Carve;
437       }
438     }
439 
440     //
441     // Get another page
442     //
443     NewPage = CoreAllocatePoolPagesI (PoolType, EFI_SIZE_TO_PAGES (Granularity),
444                                       Granularity, NeedGuard);
445     if (NewPage == NULL) {
446       goto Done;
447     }
448 
449     //
450     // Serve the allocation request from the head of the allocated block
451     //
452 Carve:
453     Head = (POOL_HEAD *) NewPage;
454 
455     //
456     // Carve up remaining space into free pool blocks
457     //
458     Index--;
459     while (Offset < MaxOffset) {
460       ASSERT (Index < MAX_POOL_LIST);
461       FSize = LIST_TO_SIZE(Index);
462 
463       while (Offset + FSize <= MaxOffset) {
464         Free = (POOL_FREE *) &NewPage[Offset];
465         Free->Signature = POOL_FREE_SIGNATURE;
466         Free->Index     = (UINT32)Index;
467         InsertHeadList (&Pool->FreeList[Index], &Free->Link);
468         Offset += FSize;
469       }
470       Index -= 1;
471     }
472 
473     ASSERT (Offset == MaxOffset);
474     goto Done;
475   }
476 
477   //
478   // Remove entry from free pool list
479   //
480   Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
481   RemoveEntryList (&Free->Link);
482 
483   Head = (POOL_HEAD *) Free;
484 
485 Done:
486   Buffer = NULL;
487 
488   if (Head != NULL) {
489 
490     //
491     // Account the allocation
492     //
493     Pool->Used += Size;
494 
495     //
496     // If we have a pool buffer, fill in the header & tail info
497     //
498     Head->Signature = (PageAsPool) ? POOLPAGE_HEAD_SIGNATURE : POOL_HEAD_SIGNATURE;
499     Head->Size      = Size;
500     Head->Type      = (EFI_MEMORY_TYPE) PoolType;
501     Buffer          = Head->Data;
502 
503     if (HasPoolTail) {
504       Tail            = HEAD_TO_TAIL (Head);
505       Tail->Signature = POOL_TAIL_SIGNATURE;
506       Tail->Size      = Size;
507 
508       Size -= POOL_OVERHEAD;
509     } else {
510       Size -= SIZE_OF_POOL_HEAD;
511     }
512 
513     DEBUG_CLEAR_MEMORY (Buffer, Size);
514 
515     DEBUG ((
516       DEBUG_POOL,
517       "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,
518       Buffer,
519       (UINT64)Size,
520       (UINT64) Pool->Used
521       ));
522 
523 
524   } else {
525     DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));
526   }
527 
528   return Buffer;
529 }
530 
531 
532 
533 /**
534   Frees pool.
535 
536   @param  Buffer                 The allocated pool entry to free
537   @param  PoolType               Pointer to pool type
538 
539   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
540   @retval EFI_SUCCESS            Pool successfully freed.
541 
542 **/
543 EFI_STATUS
544 EFIAPI
CoreInternalFreePool(IN VOID * Buffer,OUT EFI_MEMORY_TYPE * PoolType OPTIONAL)545 CoreInternalFreePool (
546   IN VOID               *Buffer,
547   OUT EFI_MEMORY_TYPE   *PoolType OPTIONAL
548   )
549 {
550   EFI_STATUS Status;
551 
552   if (Buffer == NULL) {
553     return EFI_INVALID_PARAMETER;
554   }
555 
556   CoreAcquireLock (&mPoolMemoryLock);
557   Status = CoreFreePoolI (Buffer, PoolType);
558   CoreReleaseLock (&mPoolMemoryLock);
559   return Status;
560 }
561 
562 /**
563   Frees pool.
564 
565   @param  Buffer                 The allocated pool entry to free
566 
567   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
568   @retval EFI_SUCCESS            Pool successfully freed.
569 
570 **/
571 EFI_STATUS
572 EFIAPI
CoreFreePool(IN VOID * Buffer)573 CoreFreePool (
574   IN VOID  *Buffer
575   )
576 {
577   EFI_STATUS        Status;
578   EFI_MEMORY_TYPE   PoolType;
579 
580   Status = CoreInternalFreePool (Buffer, &PoolType);
581   if (!EFI_ERROR (Status)) {
582     CoreUpdateProfile (
583       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
584       MemoryProfileActionFreePool,
585       PoolType,
586       0,
587       Buffer,
588       NULL
589       );
590     InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
591   }
592   return Status;
593 }
594 
595 /**
596   Internal function.  Frees pool pages allocated via CoreAllocatePoolPagesI().
597 
598   @param  PoolType               The type of memory for the pool pages
599   @param  Memory                 The base address to free
600   @param  NoPages                The number of pages to free
601 
602 **/
603 STATIC
604 VOID
CoreFreePoolPagesI(IN EFI_MEMORY_TYPE PoolType,IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NoPages)605 CoreFreePoolPagesI (
606   IN EFI_MEMORY_TYPE        PoolType,
607   IN EFI_PHYSICAL_ADDRESS   Memory,
608   IN UINTN                  NoPages
609   )
610 {
611   CoreAcquireMemoryLock ();
612   CoreFreePoolPages (Memory, NoPages);
613   CoreReleaseMemoryLock ();
614 
615   GuardFreedPagesChecked (Memory, NoPages);
616   ApplyMemoryProtectionPolicy (PoolType, EfiConventionalMemory,
617     (EFI_PHYSICAL_ADDRESS)(UINTN)Memory, EFI_PAGES_TO_SIZE (NoPages));
618 }
619 
620 /**
621   Internal function.  Frees guarded pool pages.
622 
623   @param  PoolType               The type of memory for the pool pages
624   @param  Memory                 The base address to free
625   @param  NoPages                The number of pages to free
626 
627 **/
628 STATIC
629 VOID
CoreFreePoolPagesWithGuard(IN EFI_MEMORY_TYPE PoolType,IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NoPages)630 CoreFreePoolPagesWithGuard (
631   IN EFI_MEMORY_TYPE        PoolType,
632   IN EFI_PHYSICAL_ADDRESS   Memory,
633   IN UINTN                  NoPages
634   )
635 {
636   EFI_PHYSICAL_ADDRESS    MemoryGuarded;
637   UINTN                   NoPagesGuarded;
638 
639   MemoryGuarded  = Memory;
640   NoPagesGuarded = NoPages;
641 
642   AdjustMemoryF (&Memory, &NoPages);
643   //
644   // It's safe to unset Guard page inside memory lock because there should
645   // be no memory allocation occurred in updating memory page attribute at
646   // this point. And unsetting Guard page before free will prevent Guard
647   // page just freed back to pool from being allocated right away before
648   // marking it usable (from non-present to present).
649   //
650   UnsetGuardForMemory (MemoryGuarded, NoPagesGuarded);
651   if (NoPages > 0) {
652     CoreFreePoolPagesI (PoolType, Memory, NoPages);
653   }
654 }
655 
656 /**
657   Internal function to free a pool entry.
658   Caller must have the memory lock held
659 
660   @param  Buffer                 The allocated pool entry to free
661   @param  PoolType               Pointer to pool type
662 
663   @retval EFI_INVALID_PARAMETER  Buffer not valid
664   @retval EFI_SUCCESS            Buffer successfully freed.
665 
666 **/
667 EFI_STATUS
CoreFreePoolI(IN VOID * Buffer,OUT EFI_MEMORY_TYPE * PoolType OPTIONAL)668 CoreFreePoolI (
669   IN VOID               *Buffer,
670   OUT EFI_MEMORY_TYPE   *PoolType OPTIONAL
671   )
672 {
673   POOL        *Pool;
674   POOL_HEAD   *Head;
675   POOL_TAIL   *Tail;
676   POOL_FREE   *Free;
677   UINTN       Index;
678   UINTN       NoPages;
679   UINTN       Size;
680   CHAR8       *NewPage;
681   UINTN       Offset;
682   BOOLEAN     AllFree;
683   UINTN       Granularity;
684   BOOLEAN     IsGuarded;
685   BOOLEAN     HasPoolTail;
686   BOOLEAN     PageAsPool;
687 
688   ASSERT(Buffer != NULL);
689   //
690   // Get the head & tail of the pool entry
691   //
692   Head = BASE_CR (Buffer, POOL_HEAD, Data);
693   ASSERT(Head != NULL);
694 
695   if (Head->Signature != POOL_HEAD_SIGNATURE &&
696       Head->Signature != POOLPAGE_HEAD_SIGNATURE) {
697     ASSERT (Head->Signature == POOL_HEAD_SIGNATURE ||
698             Head->Signature == POOLPAGE_HEAD_SIGNATURE);
699     return EFI_INVALID_PARAMETER;
700   }
701 
702   IsGuarded   = IsPoolTypeToGuard (Head->Type) &&
703                 IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
704   HasPoolTail = !(IsGuarded &&
705                   ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
706   PageAsPool = (Head->Signature == POOLPAGE_HEAD_SIGNATURE);
707 
708   if (HasPoolTail) {
709     Tail = HEAD_TO_TAIL (Head);
710     ASSERT (Tail != NULL);
711 
712     //
713     // Debug
714     //
715     ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
716     ASSERT (Head->Size == Tail->Size);
717 
718     if (Tail->Signature != POOL_TAIL_SIGNATURE) {
719       return EFI_INVALID_PARAMETER;
720     }
721 
722     if (Head->Size != Tail->Size) {
723       return EFI_INVALID_PARAMETER;
724     }
725   }
726 
727   ASSERT_LOCKED (&mPoolMemoryLock);
728 
729   //
730   // Determine the pool type and account for it
731   //
732   Size = Head->Size;
733   Pool = LookupPoolHead (Head->Type);
734   if (Pool == NULL) {
735     return EFI_INVALID_PARAMETER;
736   }
737   Pool->Used -= Size;
738   DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64) Pool->Used));
739 
740   if  (Head->Type == EfiACPIReclaimMemory   ||
741        Head->Type == EfiACPIMemoryNVS       ||
742        Head->Type == EfiRuntimeServicesCode ||
743        Head->Type == EfiRuntimeServicesData) {
744 
745     Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
746   } else {
747     Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
748   }
749 
750   if (PoolType != NULL) {
751     *PoolType = Head->Type;
752   }
753 
754   //
755   // Determine the pool list
756   //
757   Index = SIZE_TO_LIST(Size);
758   DEBUG_CLEAR_MEMORY (Head, Size);
759 
760   //
761   // If it's not on the list, it must be pool pages
762   //
763   if (Index >= SIZE_TO_LIST (Granularity) || IsGuarded || PageAsPool) {
764 
765     //
766     // Return the memory pages back to free memory
767     //
768     NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
769     NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
770     if (IsGuarded) {
771       Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
772       CoreFreePoolPagesWithGuard (
773         Pool->MemoryType,
774         (EFI_PHYSICAL_ADDRESS)(UINTN)Head,
775         NoPages
776         );
777     } else {
778       CoreFreePoolPagesI (
779         Pool->MemoryType,
780         (EFI_PHYSICAL_ADDRESS)(UINTN)Head,
781         NoPages
782         );
783     }
784 
785   } else {
786 
787     //
788     // Put the pool entry onto the free pool list
789     //
790     Free = (POOL_FREE *) Head;
791     ASSERT(Free != NULL);
792     Free->Signature = POOL_FREE_SIGNATURE;
793     Free->Index     = (UINT32)Index;
794     InsertHeadList (&Pool->FreeList[Index], &Free->Link);
795 
796     //
797     // See if all the pool entries in the same page as Free are freed pool
798     // entries
799     //
800     NewPage = (CHAR8 *)((UINTN)Free & ~(Granularity - 1));
801     Free = (POOL_FREE *) &NewPage[0];
802     ASSERT(Free != NULL);
803 
804     if (Free->Signature == POOL_FREE_SIGNATURE) {
805 
806       AllFree = TRUE;
807       Offset = 0;
808 
809       while ((Offset < Granularity) && (AllFree)) {
810         Free = (POOL_FREE *) &NewPage[Offset];
811         ASSERT(Free != NULL);
812         if (Free->Signature != POOL_FREE_SIGNATURE) {
813           AllFree = FALSE;
814         }
815         Offset += LIST_TO_SIZE(Free->Index);
816       }
817 
818       if (AllFree) {
819 
820         //
821         // All of the pool entries in the same page as Free are free pool
822         // entries
823         // Remove all of these pool entries from the free loop lists.
824         //
825         Free = (POOL_FREE *) &NewPage[0];
826         ASSERT(Free != NULL);
827         Offset = 0;
828 
829         while (Offset < Granularity) {
830           Free = (POOL_FREE *) &NewPage[Offset];
831           ASSERT(Free != NULL);
832           RemoveEntryList (&Free->Link);
833           Offset += LIST_TO_SIZE(Free->Index);
834         }
835 
836         //
837         // Free the page
838         //
839         CoreFreePoolPagesI (Pool->MemoryType, (EFI_PHYSICAL_ADDRESS) (UINTN)NewPage,
840           EFI_SIZE_TO_PAGES (Granularity));
841       }
842     }
843   }
844 
845   //
846   // If this is an OS/OEM specific memory type, then check to see if the last
847   // portion of that memory type has been freed.  If it has, then free the
848   // list entry for that memory type
849   //
850   if (((UINT32) Pool->MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) && Pool->Used == 0) {
851     RemoveEntryList (&Pool->Link);
852     CoreFreePoolI (Pool, NULL);
853   }
854 
855   return EFI_SUCCESS;
856 }
857 
858