1 /** @file
2   UEFI Memory pool management functions.
3 
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
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   128, 256, 384, 640, 1024, 1664, 2688, 4352, 7040, 11392, 18432, 29824
56 };
57 
58 #define SIZE_TO_LIST(a)   (GetPoolIndexFromSize (a))
59 #define LIST_TO_SIZE(a)   (mPoolSizeTable [a])
60 
61 #define MAX_POOL_LIST     (ARRAY_SIZE (mPoolSizeTable))
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  Buffer is NULL.
201                                  PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
202                                  PoolType is EfiPersistentMemory.
203   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
204   @retval EFI_SUCCESS            Pool successfully allocated.
205 
206 **/
207 EFI_STATUS
208 EFIAPI
CoreInternalAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)209 CoreInternalAllocatePool (
210   IN EFI_MEMORY_TYPE  PoolType,
211   IN UINTN            Size,
212   OUT VOID            **Buffer
213   )
214 {
215   EFI_STATUS    Status;
216 
217   //
218   // If it's not a valid type, fail it
219   //
220   if ((PoolType >= EfiMaxMemoryType && PoolType < MEMORY_TYPE_OEM_RESERVED_MIN) ||
221        (PoolType == EfiConventionalMemory) || (PoolType == EfiPersistentMemory)) {
222     return EFI_INVALID_PARAMETER;
223   }
224 
225   if (Buffer == NULL) {
226     return EFI_INVALID_PARAMETER;
227   }
228 
229   *Buffer = NULL;
230 
231   //
232   // If size is too large, fail it
233   // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES
234   //
235   if (Size > MAX_POOL_SIZE) {
236     return EFI_OUT_OF_RESOURCES;
237   }
238 
239   //
240   // Acquire the memory lock and make the allocation
241   //
242   Status = CoreAcquireLockOrFail (&gMemoryLock);
243   if (EFI_ERROR (Status)) {
244     return EFI_OUT_OF_RESOURCES;
245   }
246 
247   *Buffer = CoreAllocatePoolI (PoolType, Size);
248   CoreReleaseMemoryLock ();
249   return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
250 }
251 
252 /**
253   Allocate pool of a particular type.
254 
255   @param  PoolType               Type of pool to allocate
256   @param  Size                   The amount of pool to allocate
257   @param  Buffer                 The address to return a pointer to the allocated
258                                  pool
259 
260   @retval EFI_INVALID_PARAMETER  Buffer is NULL.
261                                  PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.
262                                  PoolType is EfiPersistentMemory.
263   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
264   @retval EFI_SUCCESS            Pool successfully allocated.
265 
266 **/
267 EFI_STATUS
268 EFIAPI
CoreAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)269 CoreAllocatePool (
270   IN EFI_MEMORY_TYPE  PoolType,
271   IN UINTN            Size,
272   OUT VOID            **Buffer
273   )
274 {
275   EFI_STATUS  Status;
276 
277   Status = CoreInternalAllocatePool (PoolType, Size, Buffer);
278   if (!EFI_ERROR (Status)) {
279     CoreUpdateProfile (
280       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
281       MemoryProfileActionAllocatePool,
282       PoolType,
283       Size,
284       *Buffer,
285       NULL
286       );
287     InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
288   }
289   return Status;
290 }
291 
292 /**
293   Internal function to allocate pool of a particular type.
294   Caller must have the memory lock held
295 
296   @param  PoolType               Type of pool to allocate
297   @param  Size                   The amount of pool to allocate
298 
299   @return The allocate pool, or NULL
300 
301 **/
302 VOID *
CoreAllocatePoolI(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size)303 CoreAllocatePoolI (
304   IN EFI_MEMORY_TYPE  PoolType,
305   IN UINTN            Size
306   )
307 {
308   POOL        *Pool;
309   POOL_FREE   *Free;
310   POOL_HEAD   *Head;
311   POOL_TAIL   *Tail;
312   CHAR8       *NewPage;
313   VOID        *Buffer;
314   UINTN       Index;
315   UINTN       FSize;
316   UINTN       Offset, MaxOffset;
317   UINTN       NoPages;
318   UINTN       Granularity;
319 
320   ASSERT_LOCKED (&gMemoryLock);
321 
322   if  (PoolType == EfiACPIReclaimMemory   ||
323        PoolType == EfiACPIMemoryNVS       ||
324        PoolType == EfiRuntimeServicesCode ||
325        PoolType == EfiRuntimeServicesData) {
326 
327     Granularity = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;
328   } else {
329     Granularity = DEFAULT_PAGE_ALLOCATION;
330   }
331 
332   //
333   // Adjust the size by the pool header & tail overhead
334   //
335 
336   //
337   // Adjusting the Size to be of proper alignment so that
338   // we don't get an unaligned access fault later when
339   // pool_Tail is being initialized
340   //
341   Size = ALIGN_VARIABLE (Size);
342 
343   Size += POOL_OVERHEAD;
344   Index = SIZE_TO_LIST(Size);
345   Pool = LookupPoolHead (PoolType);
346   if (Pool== NULL) {
347     return NULL;
348   }
349   Head = NULL;
350 
351   //
352   // If allocation is over max size, just allocate pages for the request
353   // (slow)
354   //
355   if (Index >= SIZE_TO_LIST (Granularity)) {
356     NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
357     NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
358     Head = CoreAllocatePoolPages (PoolType, NoPages, Granularity);
359     goto Done;
360   }
361 
362   //
363   // If there's no free pool in the proper list size, go get some more pages
364   //
365   if (IsListEmpty (&Pool->FreeList[Index])) {
366 
367     Offset = LIST_TO_SIZE (Index);
368     MaxOffset = Granularity;
369 
370     //
371     // Check the bins holding larger blocks, and carve one up if needed
372     //
373     while (++Index < SIZE_TO_LIST (Granularity)) {
374       if (!IsListEmpty (&Pool->FreeList[Index])) {
375         Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
376         RemoveEntryList (&Free->Link);
377         NewPage = (VOID *) Free;
378         MaxOffset = LIST_TO_SIZE (Index);
379         goto Carve;
380       }
381     }
382 
383     //
384     // Get another page
385     //
386     NewPage = CoreAllocatePoolPages(PoolType, EFI_SIZE_TO_PAGES (Granularity), Granularity);
387     if (NewPage == NULL) {
388       goto Done;
389     }
390 
391     //
392     // Serve the allocation request from the head of the allocated block
393     //
394 Carve:
395     Head = (POOL_HEAD *) NewPage;
396 
397     //
398     // Carve up remaining space into free pool blocks
399     //
400     Index--;
401     while (Offset < MaxOffset) {
402       ASSERT (Index < MAX_POOL_LIST);
403       FSize = LIST_TO_SIZE(Index);
404 
405       while (Offset + FSize <= MaxOffset) {
406         Free = (POOL_FREE *) &NewPage[Offset];
407         Free->Signature = POOL_FREE_SIGNATURE;
408         Free->Index     = (UINT32)Index;
409         InsertHeadList (&Pool->FreeList[Index], &Free->Link);
410         Offset += FSize;
411       }
412       Index -= 1;
413     }
414 
415     ASSERT (Offset == MaxOffset);
416     goto Done;
417   }
418 
419   //
420   // Remove entry from free pool list
421   //
422   Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);
423   RemoveEntryList (&Free->Link);
424 
425   Head = (POOL_HEAD *) Free;
426 
427 Done:
428   Buffer = NULL;
429 
430   if (Head != NULL) {
431 
432     //
433     // If we have a pool buffer, fill in the header & tail info
434     //
435     Head->Signature = POOL_HEAD_SIGNATURE;
436     Head->Size      = Size;
437     Head->Type      = (EFI_MEMORY_TYPE) PoolType;
438     Tail            = HEAD_TO_TAIL (Head);
439     Tail->Signature = POOL_TAIL_SIGNATURE;
440     Tail->Size      = Size;
441     Buffer          = Head->Data;
442     DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD);
443 
444     DEBUG ((
445       DEBUG_POOL,
446       "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,
447       Buffer,
448       (UINT64)(Size - POOL_OVERHEAD),
449       (UINT64) Pool->Used
450       ));
451 
452     //
453     // Account the allocation
454     //
455     Pool->Used += Size;
456 
457   } else {
458     DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));
459   }
460 
461   return Buffer;
462 }
463 
464 
465 
466 /**
467   Frees pool.
468 
469   @param  Buffer                 The allocated pool entry to free
470   @param  PoolType               Pointer to pool type
471 
472   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
473   @retval EFI_SUCCESS            Pool successfully freed.
474 
475 **/
476 EFI_STATUS
477 EFIAPI
CoreInternalFreePool(IN VOID * Buffer,OUT EFI_MEMORY_TYPE * PoolType OPTIONAL)478 CoreInternalFreePool (
479   IN VOID               *Buffer,
480   OUT EFI_MEMORY_TYPE   *PoolType OPTIONAL
481   )
482 {
483   EFI_STATUS Status;
484 
485   if (Buffer == NULL) {
486     return EFI_INVALID_PARAMETER;
487   }
488 
489   CoreAcquireMemoryLock ();
490   Status = CoreFreePoolI (Buffer, PoolType);
491   CoreReleaseMemoryLock ();
492   return Status;
493 }
494 
495 /**
496   Frees pool.
497 
498   @param  Buffer                 The allocated pool entry to free
499 
500   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
501   @retval EFI_SUCCESS            Pool successfully freed.
502 
503 **/
504 EFI_STATUS
505 EFIAPI
CoreFreePool(IN VOID * Buffer)506 CoreFreePool (
507   IN VOID  *Buffer
508   )
509 {
510   EFI_STATUS        Status;
511   EFI_MEMORY_TYPE   PoolType;
512 
513   Status = CoreInternalFreePool (Buffer, &PoolType);
514   if (!EFI_ERROR (Status)) {
515     CoreUpdateProfile (
516       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
517       MemoryProfileActionFreePool,
518       PoolType,
519       0,
520       Buffer,
521       NULL
522       );
523     InstallMemoryAttributesTableOnMemoryAllocation (PoolType);
524   }
525   return Status;
526 }
527 
528 /**
529   Internal function to free a pool entry.
530   Caller must have the memory lock held
531 
532   @param  Buffer                 The allocated pool entry to free
533   @param  PoolType               Pointer to pool type
534 
535   @retval EFI_INVALID_PARAMETER  Buffer not valid
536   @retval EFI_SUCCESS            Buffer successfully freed.
537 
538 **/
539 EFI_STATUS
CoreFreePoolI(IN VOID * Buffer,OUT EFI_MEMORY_TYPE * PoolType OPTIONAL)540 CoreFreePoolI (
541   IN VOID               *Buffer,
542   OUT EFI_MEMORY_TYPE   *PoolType OPTIONAL
543   )
544 {
545   POOL        *Pool;
546   POOL_HEAD   *Head;
547   POOL_TAIL   *Tail;
548   POOL_FREE   *Free;
549   UINTN       Index;
550   UINTN       NoPages;
551   UINTN       Size;
552   CHAR8       *NewPage;
553   UINTN       Offset;
554   BOOLEAN     AllFree;
555   UINTN       Granularity;
556 
557   ASSERT(Buffer != NULL);
558   //
559   // Get the head & tail of the pool entry
560   //
561   Head = CR (Buffer, POOL_HEAD, Data, POOL_HEAD_SIGNATURE);
562   ASSERT(Head != NULL);
563 
564   if (Head->Signature != POOL_HEAD_SIGNATURE) {
565     return EFI_INVALID_PARAMETER;
566   }
567 
568   Tail = HEAD_TO_TAIL (Head);
569   ASSERT(Tail != NULL);
570 
571   //
572   // Debug
573   //
574   ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
575   ASSERT (Head->Size == Tail->Size);
576   ASSERT_LOCKED (&gMemoryLock);
577 
578   if (Tail->Signature != POOL_TAIL_SIGNATURE) {
579     return EFI_INVALID_PARAMETER;
580   }
581 
582   if (Head->Size != Tail->Size) {
583     return EFI_INVALID_PARAMETER;
584   }
585 
586   //
587   // Determine the pool type and account for it
588   //
589   Size = Head->Size;
590   Pool = LookupPoolHead (Head->Type);
591   if (Pool == NULL) {
592     return EFI_INVALID_PARAMETER;
593   }
594   Pool->Used -= Size;
595   DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64) Pool->Used));
596 
597   if  (Head->Type == EfiACPIReclaimMemory   ||
598        Head->Type == EfiACPIMemoryNVS       ||
599        Head->Type == EfiRuntimeServicesCode ||
600        Head->Type == EfiRuntimeServicesData) {
601 
602     Granularity = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;
603   } else {
604     Granularity = DEFAULT_PAGE_ALLOCATION;
605   }
606 
607   if (PoolType != NULL) {
608     *PoolType = Head->Type;
609   }
610 
611   //
612   // Determine the pool list
613   //
614   Index = SIZE_TO_LIST(Size);
615   DEBUG_CLEAR_MEMORY (Head, Size);
616 
617   //
618   // If it's not on the list, it must be pool pages
619   //
620   if (Index >= SIZE_TO_LIST (Granularity)) {
621 
622     //
623     // Return the memory pages back to free memory
624     //
625     NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
626     NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
627     CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN) Head, NoPages);
628 
629   } else {
630 
631     //
632     // Put the pool entry onto the free pool list
633     //
634     Free = (POOL_FREE *) Head;
635     ASSERT(Free != NULL);
636     Free->Signature = POOL_FREE_SIGNATURE;
637     Free->Index     = (UINT32)Index;
638     InsertHeadList (&Pool->FreeList[Index], &Free->Link);
639 
640     //
641     // See if all the pool entries in the same page as Free are freed pool
642     // entries
643     //
644     NewPage = (CHAR8 *)((UINTN)Free & ~(Granularity - 1));
645     Free = (POOL_FREE *) &NewPage[0];
646     ASSERT(Free != NULL);
647 
648     if (Free->Signature == POOL_FREE_SIGNATURE) {
649 
650       AllFree = TRUE;
651       Offset = 0;
652 
653       while ((Offset < Granularity) && (AllFree)) {
654         Free = (POOL_FREE *) &NewPage[Offset];
655         ASSERT(Free != NULL);
656         if (Free->Signature != POOL_FREE_SIGNATURE) {
657           AllFree = FALSE;
658         }
659         Offset += LIST_TO_SIZE(Free->Index);
660       }
661 
662       if (AllFree) {
663 
664         //
665         // All of the pool entries in the same page as Free are free pool
666         // entries
667         // Remove all of these pool entries from the free loop lists.
668         //
669         Free = (POOL_FREE *) &NewPage[0];
670         ASSERT(Free != NULL);
671         Offset = 0;
672 
673         while (Offset < Granularity) {
674           Free = (POOL_FREE *) &NewPage[Offset];
675           ASSERT(Free != NULL);
676           RemoveEntryList (&Free->Link);
677           Offset += LIST_TO_SIZE(Free->Index);
678         }
679 
680         //
681         // Free the page
682         //
683         CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN)NewPage, EFI_SIZE_TO_PAGES (Granularity));
684       }
685     }
686   }
687 
688   //
689   // If this is an OS/OEM specific memory type, then check to see if the last
690   // portion of that memory type has been freed.  If it has, then free the
691   // list entry for that memory type
692   //
693   if (((UINT32) Pool->MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) && Pool->Used == 0) {
694     RemoveEntryList (&Pool->Link);
695     CoreFreePoolI (Pool, NULL);
696   }
697 
698   return EFI_SUCCESS;
699 }
700 
701