Home | History | Annotate | Download | only in Mem
      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
     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
    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 *
    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
    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
    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 *
    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
    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
    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
    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