1 /** @file
2   UEFI Memory pool management functions.
3 
4 Copyright (c) 2006 - 2015, 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
9 
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.
12 
13 **/
14 
15 #include "DxeMain.h"
16 #include "Imem.h"
17 
18 #define POOL_FREE_SIGNATURE   SIGNATURE_32('p','f','r','0')
19 typedef struct {
20   UINT32          Signature;
21   UINT32          Index;
22   LIST_ENTRY      Link;
23 } POOL_FREE;
24 
25 
26 #define POOL_HEAD_SIGNATURE   SIGNATURE_32('p','h','d','0')
27 typedef struct {
28   UINT32          Signature;
29   UINT32          Reserved;
30   EFI_MEMORY_TYPE Type;
31   UINTN           Size;
32   CHAR8           Data[1];
33 } POOL_HEAD;
34 
35 #define SIZE_OF_POOL_HEAD OFFSET_OF(POOL_HEAD,Data)
36 
37 #define POOL_TAIL_SIGNATURE   SIGNATURE_32('p','t','a','l')
38 typedef struct {
39   UINT32      Signature;
40   UINT32      Reserved;
41   UINTN       Size;
42 } POOL_TAIL;
43 
44 #define POOL_OVERHEAD (SIZE_OF_POOL_HEAD + sizeof(POOL_TAIL))
45 
46 #define HEAD_TO_TAIL(a)   \
47   ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL)));
48 
49 //
50 // Each element is the sum of the 2 previous ones: this allows us to migrate
51 // blocks between bins by splitting them up, while not wasting too much memory
52 // as we would in a strict power-of-2 sequence
53 //
54 STATIC CONST UINT16 mPoolSizeTable[] = {
55   64, 128, 192, 320, 512, 832, 1344, 2176, 3520, 5696, 9216, 14912, 24128
56 };
57 
58 #define SIZE_TO_LIST(a)   (GetPoolIndexFromSize (a))
59 #define LIST_TO_SIZE(a)   (mPoolSizeTable [a])
60 
61 #define MAX_POOL_LIST     (sizeof (mPoolSizeTable) / sizeof (mPoolSizeTable[0]))
62 
63 #define MAX_POOL_SIZE     (MAX_ADDRESS - POOL_OVERHEAD)
64 
65 //
66 // Globals
67 //
68 
69 #define POOL_SIGNATURE  SIGNATURE_32('p','l','s','t')
70 typedef struct {
71     INTN             Signature;
72     UINTN            Used;
73     EFI_MEMORY_TYPE  MemoryType;
74     LIST_ENTRY       FreeList[MAX_POOL_LIST];
75     LIST_ENTRY       Link;
76 } POOL;
77 
78 //
79 // Pool header for each memory type.
80 //
81 POOL            mPoolHead[EfiMaxMemoryType];
82 
83 //
84 // List of pool header to search for the appropriate memory type.
85 //
86 LIST_ENTRY      mPoolHeadList = INITIALIZE_LIST_HEAD_VARIABLE (mPoolHeadList);
87 
88 /**
89   Get pool size table index from the specified size.
90 
91   @param  Size          The specified size to get index from pool table.
92 
93   @return               The index of pool size table.
94 
95 **/
96 STATIC
97 UINTN
GetPoolIndexFromSize(UINTN Size)98 GetPoolIndexFromSize (
99   UINTN   Size
100   )
101 {
102   UINTN   Index;
103 
104   for (Index = 0; Index < MAX_POOL_LIST; Index++) {
105     if (mPoolSizeTable [Index] >= Size) {
106       return Index;
107     }
108   }
109   return MAX_POOL_LIST;
110 }
111 
112 /**
113   Called to initialize the pool.
114 
115 **/
116 VOID
CoreInitializePool(VOID)117 CoreInitializePool (
118   VOID
119   )
120 {
121   UINTN  Type;
122   UINTN  Index;
123 
124   for (Type=0; Type < EfiMaxMemoryType; Type++) {
125     mPoolHead[Type].Signature  = 0;
126     mPoolHead[Type].Used       = 0;
127     mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE) Type;
128     for (Index=0; Index < MAX_POOL_LIST; Index++) {
129       InitializeListHead (&mPoolHead[Type].FreeList[Index]);
130     }
131   }
132 }
133 
134 
135 /**
136   Look up pool head for specified memory type.
137 
138   @param  MemoryType             Memory type of which pool head is looked for
139 
140   @return Pointer of Corresponding pool head.
141 
142 **/
143 POOL *
LookupPoolHead(IN EFI_MEMORY_TYPE MemoryType)144 LookupPoolHead (
145   IN EFI_MEMORY_TYPE  MemoryType
146   )
147 {
148   LIST_ENTRY      *Link;
149   POOL            *Pool;
150   UINTN           Index;
151 
152   if ((UINT32)MemoryType < EfiMaxMemoryType) {
153     return &mPoolHead[MemoryType];
154   }
155 
156   //
157   // MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI
158   // OS loaders that are provided by operating system vendors.
159   // MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use.
160   //
161   if ((UINT32) MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
162 
163     for (Link = mPoolHeadList.ForwardLink; Link != &mPoolHeadList; Link = Link->ForwardLink) {
164       Pool = CR(Link, POOL, Link, POOL_SIGNATURE);
165       if (Pool->MemoryType == MemoryType) {
166         return Pool;
167       }
168     }
169 
170     Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL));
171     if (Pool == NULL) {
172       return NULL;
173     }
174 
175     Pool->Signature = POOL_SIGNATURE;
176     Pool->Used      = 0;
177     Pool->MemoryType = MemoryType;
178     for (Index=0; Index < MAX_POOL_LIST; Index++) {
179       InitializeListHead (&Pool->FreeList[Index]);
180     }
181 
182     InsertHeadList (&mPoolHeadList, &Pool->Link);
183 
184     return Pool;
185   }
186 
187   return NULL;
188 }
189 
190 
191 
192 /**
193   Allocate pool of a particular type.
194 
195   @param  PoolType               Type of pool to allocate
196   @param  Size                   The amount of pool to allocate
197   @param  Buffer                 The address to return a pointer to the allocated
198                                  pool
199 
200   @retval EFI_INVALID_PARAMETER  PoolType not valid or Buffer is NULL.
201                                  PoolType was EfiPersistentMemory.
202   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
203   @retval EFI_SUCCESS            Pool successfully allocated.
204 
205 **/
206 EFI_STATUS
207 EFIAPI
CoreInternalAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)208 CoreInternalAllocatePool (
209   IN EFI_MEMORY_TYPE  PoolType,
210   IN UINTN            Size,
211   OUT VOID            **Buffer
212   )
213 {
214   EFI_STATUS    Status;
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   //
239   // Acquire the memory lock and make the allocation
240   //
241   Status = CoreAcquireLockOrFail (&gMemoryLock);
242   if (EFI_ERROR (Status)) {
243     return EFI_OUT_OF_RESOURCES;
244   }
245 
246   *Buffer = CoreAllocatePoolI (PoolType, Size);
247   CoreReleaseMemoryLock ();
248   return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
249 }
250 
251 /**
252   Allocate pool of a particular type.
253 
254   @param  PoolType               Type of pool to allocate
255   @param  Size                   The amount of pool to allocate
256   @param  Buffer                 The address to return a pointer to the allocated
257                                  pool
258 
259   @retval EFI_INVALID_PARAMETER  PoolType not valid or Buffer is NULL.
260   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
261   @retval EFI_SUCCESS            Pool successfully allocated.
262 
263 **/
264 EFI_STATUS
265 EFIAPI
CoreAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)266 CoreAllocatePool (
267   IN EFI_MEMORY_TYPE  PoolType,
268   IN UINTN            Size,
269   OUT VOID            **Buffer
270   )
271 {
272   EFI_STATUS  Status;
273 
274   Status = CoreInternalAllocatePool (PoolType, Size, Buffer);
275   if (!EFI_ERROR (Status)) {
276     CoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionAllocatePool, PoolType, Size, *Buffer);
277   }
278   return Status;
279 }
280 
281 /**
282   Internal function to allocate pool of a particular type.
283   Caller must have the memory lock held
284 
285   @param  PoolType               Type of pool to allocate
286   @param  Size                   The amount of pool to allocate
287 
288   @return The allocate pool, or NULL
289 
290 **/
291 VOID *
CoreAllocatePoolI(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size)292 CoreAllocatePoolI (
293   IN EFI_MEMORY_TYPE  PoolType,
294   IN UINTN            Size
295   )
296 {
297   POOL        *Pool;
298   POOL_FREE   *Free;
299   POOL_HEAD   *Head;
300   POOL_TAIL   *Tail;
301   CHAR8       *NewPage;
302   VOID        *Buffer;
303   UINTN       Index;
304   UINTN       FSize;
305   UINTN       Offset, MaxOffset;
306   UINTN       NoPages;
307   UINTN       Granularity;
308 
309   ASSERT_LOCKED (&gMemoryLock);
310 
311   if  (PoolType == EfiACPIReclaimMemory   ||
312        PoolType == EfiACPIMemoryNVS       ||
313        PoolType == EfiRuntimeServicesCode ||
314        PoolType == EfiRuntimeServicesData) {
315 
316     Granularity = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;
317   } else {
318     Granularity = DEFAULT_PAGE_ALLOCATION;
319   }
320 
321   //
322   // Adjust the size by the pool header & tail overhead
323   //
324 
325   //
326   // Adjusting the Size to be of proper alignment so that
327   // we don't get an unaligned access fault later when
328   // pool_Tail is being initialized
329   //
330   Size = ALIGN_VARIABLE (Size);
331 
332   Size += POOL_OVERHEAD;
333   Index = SIZE_TO_LIST(Size);
334   Pool = LookupPoolHead (PoolType);
335   if (Pool== NULL) {
336     return NULL;
337   }
338   Head = NULL;
339 
340   //
341   // If allocation is over max size, just allocate pages for the request
342   // (slow)
343   //
344   if (Index >= SIZE_TO_LIST (Granularity)) {
345     NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
346     NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
347     Head = CoreAllocatePoolPages (PoolType, NoPages, Granularity);
348     goto Done;
349   }
350 
351   //
352   // If there's no free pool in the proper list size, go get some more pages
353   //
354   if (IsListEmpty (&Pool->FreeList[Index])) {
355 
356     Offset = LIST_TO_SIZE (Index);
357     MaxOffset = Granularity;
358 
359     //
360     // Check the bins holding larger blocks, and carve one up if needed
361     //
362     while (++Index < SIZE_TO_LIST (Granularity)) {
363       if (!IsListEmpty (&Pool->FreeList[Index])) {
364         Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
365         RemoveEntryList (&Free->Link);
366         NewPage = (VOID *) Free;
367         MaxOffset = LIST_TO_SIZE (Index);
368         goto Carve;
369       }
370     }
371 
372     //
373     // Get another page
374     //
375     NewPage = CoreAllocatePoolPages(PoolType, EFI_SIZE_TO_PAGES (Granularity), Granularity);
376     if (NewPage == NULL) {
377       goto Done;
378     }
379 
380     //
381     // Serve the allocation request from the head of the allocated block
382     //
383 Carve:
384     Head = (POOL_HEAD *) NewPage;
385 
386     //
387     // Carve up remaining space into free pool blocks
388     //
389     Index--;
390     while (Offset < MaxOffset) {
391       ASSERT (Index < MAX_POOL_LIST);
392       FSize = LIST_TO_SIZE(Index);
393 
394       while (Offset + FSize <= MaxOffset) {
395         Free = (POOL_FREE *) &NewPage[Offset];
396         Free->Signature = POOL_FREE_SIGNATURE;
397         Free->Index     = (UINT32)Index;
398         InsertHeadList (&Pool->FreeList[Index], &Free->Link);
399         Offset += FSize;
400       }
401       Index -= 1;
402     }
403 
404     ASSERT (Offset == MaxOffset);
405     goto Done;
406   }
407 
408   //
409   // Remove entry from free pool list
410   //
411   Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
412   RemoveEntryList (&Free->Link);
413 
414   Head = (POOL_HEAD *) Free;
415 
416 Done:
417   Buffer = NULL;
418 
419   if (Head != NULL) {
420 
421     //
422     // If we have a pool buffer, fill in the header & tail info
423     //
424     Head->Signature = POOL_HEAD_SIGNATURE;
425     Head->Size      = Size;
426     Head->Type      = (EFI_MEMORY_TYPE) PoolType;
427     Tail            = HEAD_TO_TAIL (Head);
428     Tail->Signature = POOL_TAIL_SIGNATURE;
429     Tail->Size      = Size;
430     Buffer          = Head->Data;
431     DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD);
432 
433     DEBUG ((
434       DEBUG_POOL,
435       "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,
436       Buffer,
437       (UINT64)(Size - POOL_OVERHEAD),
438       (UINT64) Pool->Used
439       ));
440 
441     //
442     // Account the allocation
443     //
444     Pool->Used += Size;
445 
446   } else {
447     DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));
448   }
449 
450   return Buffer;
451 }
452 
453 
454 
455 /**
456   Frees pool.
457 
458   @param  Buffer                 The allocated pool entry to free
459 
460   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
461   @retval EFI_SUCCESS            Pool successfully freed.
462 
463 **/
464 EFI_STATUS
465 EFIAPI
CoreInternalFreePool(IN VOID * Buffer)466 CoreInternalFreePool (
467   IN VOID        *Buffer
468   )
469 {
470   EFI_STATUS Status;
471 
472   if (Buffer == NULL) {
473     return EFI_INVALID_PARAMETER;
474   }
475 
476   CoreAcquireMemoryLock ();
477   Status = CoreFreePoolI (Buffer);
478   CoreReleaseMemoryLock ();
479   return Status;
480 }
481 
482 /**
483   Frees pool.
484 
485   @param  Buffer                 The allocated pool entry to free
486 
487   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
488   @retval EFI_SUCCESS            Pool successfully freed.
489 
490 **/
491 EFI_STATUS
492 EFIAPI
CoreFreePool(IN VOID * Buffer)493 CoreFreePool (
494   IN VOID  *Buffer
495   )
496 {
497   EFI_STATUS  Status;
498 
499   Status = CoreInternalFreePool (Buffer);
500   if (!EFI_ERROR (Status)) {
501     CoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionFreePool, (EFI_MEMORY_TYPE) 0, 0, Buffer);
502   }
503   return Status;
504 }
505 
506 /**
507   Internal function to free a pool entry.
508   Caller must have the memory lock held
509 
510   @param  Buffer                 The allocated pool entry to free
511 
512   @retval EFI_INVALID_PARAMETER  Buffer not valid
513   @retval EFI_SUCCESS            Buffer successfully freed.
514 
515 **/
516 EFI_STATUS
CoreFreePoolI(IN VOID * Buffer)517 CoreFreePoolI (
518   IN VOID       *Buffer
519   )
520 {
521   POOL        *Pool;
522   POOL_HEAD   *Head;
523   POOL_TAIL   *Tail;
524   POOL_FREE   *Free;
525   UINTN       Index;
526   UINTN       NoPages;
527   UINTN       Size;
528   CHAR8       *NewPage;
529   UINTN       Offset;
530   BOOLEAN     AllFree;
531   UINTN       Granularity;
532 
533   ASSERT(Buffer != NULL);
534   //
535   // Get the head & tail of the pool entry
536   //
537   Head = CR (Buffer, POOL_HEAD, Data, POOL_HEAD_SIGNATURE);
538   ASSERT(Head != NULL);
539 
540   if (Head->Signature != POOL_HEAD_SIGNATURE) {
541     return EFI_INVALID_PARAMETER;
542   }
543 
544   Tail = HEAD_TO_TAIL (Head);
545   ASSERT(Tail != NULL);
546 
547   //
548   // Debug
549   //
550   ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
551   ASSERT (Head->Size == Tail->Size);
552   ASSERT_LOCKED (&gMemoryLock);
553 
554   if (Tail->Signature != POOL_TAIL_SIGNATURE) {
555     return EFI_INVALID_PARAMETER;
556   }
557 
558   if (Head->Size != Tail->Size) {
559     return EFI_INVALID_PARAMETER;
560   }
561 
562   //
563   // Determine the pool type and account for it
564   //
565   Size = Head->Size;
566   Pool = LookupPoolHead (Head->Type);
567   if (Pool == NULL) {
568     return EFI_INVALID_PARAMETER;
569   }
570   Pool->Used -= Size;
571   DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64) Pool->Used));
572 
573   if  (Head->Type == EfiACPIReclaimMemory   ||
574        Head->Type == EfiACPIMemoryNVS       ||
575        Head->Type == EfiRuntimeServicesCode ||
576        Head->Type == EfiRuntimeServicesData) {
577 
578     Granularity = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;
579   } else {
580     Granularity = DEFAULT_PAGE_ALLOCATION;
581   }
582 
583   //
584   // Determine the pool list
585   //
586   Index = SIZE_TO_LIST(Size);
587   DEBUG_CLEAR_MEMORY (Head, Size);
588 
589   //
590   // If it's not on the list, it must be pool pages
591   //
592   if (Index >= SIZE_TO_LIST (Granularity)) {
593 
594     //
595     // Return the memory pages back to free memory
596     //
597     NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
598     NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
599     CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN) Head, NoPages);
600 
601   } else {
602 
603     //
604     // Put the pool entry onto the free pool list
605     //
606     Free = (POOL_FREE *) Head;
607     ASSERT(Free != NULL);
608     Free->Signature = POOL_FREE_SIGNATURE;
609     Free->Index     = (UINT32)Index;
610     InsertHeadList (&Pool->FreeList[Index], &Free->Link);
611 
612     //
613     // See if all the pool entries in the same page as Free are freed pool
614     // entries
615     //
616     NewPage = (CHAR8 *)((UINTN)Free & ~(Granularity - 1));
617     Free = (POOL_FREE *) &NewPage[0];
618     ASSERT(Free != NULL);
619 
620     if (Free->Signature == POOL_FREE_SIGNATURE) {
621 
622       AllFree = TRUE;
623       Offset = 0;
624 
625       while ((Offset < Granularity) && (AllFree)) {
626         Free = (POOL_FREE *) &NewPage[Offset];
627         ASSERT(Free != NULL);
628         if (Free->Signature != POOL_FREE_SIGNATURE) {
629           AllFree = FALSE;
630         }
631         Offset += LIST_TO_SIZE(Free->Index);
632       }
633 
634       if (AllFree) {
635 
636         //
637         // All of the pool entries in the same page as Free are free pool
638         // entries
639         // Remove all of these pool entries from the free loop lists.
640         //
641         Free = (POOL_FREE *) &NewPage[0];
642         ASSERT(Free != NULL);
643         Offset = 0;
644 
645         while (Offset < Granularity) {
646           Free = (POOL_FREE *) &NewPage[Offset];
647           ASSERT(Free != NULL);
648           RemoveEntryList (&Free->Link);
649           Offset += LIST_TO_SIZE(Free->Index);
650         }
651 
652         //
653         // Free the page
654         //
655         CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN)NewPage, EFI_SIZE_TO_PAGES (Granularity));
656       }
657     }
658   }
659 
660   //
661   // If this is an OS specific memory type, then check to see if the last
662   // portion of that memory type has been freed.  If it has, then free the
663   // list entry for that memory type
664   //
665   if ((INT32)Pool->MemoryType < 0 && Pool->Used == 0) {
666     RemoveEntryList (&Pool->Link);
667     CoreFreePoolI (Pool);
668   }
669 
670   return EFI_SUCCESS;
671 }
672 
673