Home | History | Annotate | Download | only in PiSmmCore
      1 /** @file
      2   SMM Memory pool management functions.
      3 
      4   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
      5   This program and the accompanying materials are licensed and made available
      6   under the terms and conditions of the BSD License which accompanies this
      7   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 "PiSmmCore.h"
     16 
     17 LIST_ENTRY  mSmmPoolLists[SmmPoolTypeMax][MAX_POOL_INDEX];
     18 //
     19 // To cache the SMRAM base since when Loading modules At fixed address feature is enabled,
     20 // all module is assigned an offset relative the SMRAM base in build time.
     21 //
     22 GLOBAL_REMOVE_IF_UNREFERENCED  EFI_PHYSICAL_ADDRESS       gLoadModuleAtFixAddressSmramBase = 0;
     23 
     24 /**
     25   Convert a UEFI memory type to SMM pool type.
     26 
     27   @param[in]  MemoryType              Type of pool to allocate.
     28 
     29   @return SMM pool type
     30 **/
     31 SMM_POOL_TYPE
     32 UefiMemoryTypeToSmmPoolType (
     33   IN  EFI_MEMORY_TYPE   MemoryType
     34   )
     35 {
     36   ASSERT ((MemoryType == EfiRuntimeServicesCode) || (MemoryType == EfiRuntimeServicesData));
     37   switch (MemoryType) {
     38   case EfiRuntimeServicesCode:
     39     return SmmPoolTypeCode;
     40   case EfiRuntimeServicesData:
     41     return SmmPoolTypeData;
     42   default:
     43     return SmmPoolTypeMax;
     44   }
     45 }
     46 
     47 
     48 /**
     49   Called to initialize the memory service.
     50 
     51   @param   SmramRangeCount       Number of SMRAM Regions
     52   @param   SmramRanges           Pointer to SMRAM Descriptors
     53 
     54 **/
     55 VOID
     56 SmmInitializeMemoryServices (
     57   IN UINTN                 SmramRangeCount,
     58   IN EFI_SMRAM_DESCRIPTOR  *SmramRanges
     59   )
     60 {
     61   UINTN                  Index;
     62   EFI_STATUS             Status;
     63   UINTN                  SmmPoolTypeIndex;
     64   EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *LMFAConfigurationTable;
     65 
     66   //
     67   // Initialize Pool list
     68   //
     69   for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) {
     70     for (Index = 0; Index < ARRAY_SIZE (mSmmPoolLists[SmmPoolTypeIndex]); Index++) {
     71       InitializeListHead (&mSmmPoolLists[SmmPoolTypeIndex][Index]);
     72     }
     73   }
     74 
     75   Status = EfiGetSystemConfigurationTable (
     76             &gLoadFixedAddressConfigurationTableGuid,
     77            (VOID **) &LMFAConfigurationTable
     78            );
     79   if (!EFI_ERROR (Status) && LMFAConfigurationTable != NULL) {
     80     gLoadModuleAtFixAddressSmramBase = LMFAConfigurationTable->SmramBase;
     81   }
     82 
     83   //
     84   // Add Free SMRAM regions
     85   // Need add Free memory at first, to let gSmmMemoryMap record data
     86   //
     87   for (Index = 0; Index < SmramRangeCount; Index++) {
     88     if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
     89       continue;
     90     }
     91     SmmAddMemoryRegion (
     92       SmramRanges[Index].CpuStart,
     93       SmramRanges[Index].PhysicalSize,
     94       EfiConventionalMemory,
     95       SmramRanges[Index].RegionState
     96       );
     97   }
     98 
     99   //
    100   // Add the allocated SMRAM regions
    101   //
    102   for (Index = 0; Index < SmramRangeCount; Index++) {
    103     if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) == 0) {
    104       continue;
    105     }
    106     SmmAddMemoryRegion (
    107       SmramRanges[Index].CpuStart,
    108       SmramRanges[Index].PhysicalSize,
    109       EfiConventionalMemory,
    110       SmramRanges[Index].RegionState
    111       );
    112   }
    113 
    114 }
    115 
    116 /**
    117   Internal Function. Allocate a pool by specified PoolIndex.
    118 
    119   @param  PoolType              Type of pool to allocate.
    120   @param  PoolIndex             Index which indicate the Pool size.
    121   @param  FreePoolHdr           The returned Free pool.
    122 
    123   @retval EFI_OUT_OF_RESOURCES   Allocation failed.
    124   @retval EFI_SUCCESS            Pool successfully allocated.
    125 
    126 **/
    127 EFI_STATUS
    128 InternalAllocPoolByIndex (
    129   IN  EFI_MEMORY_TYPE   PoolType,
    130   IN  UINTN             PoolIndex,
    131   OUT FREE_POOL_HEADER  **FreePoolHdr
    132   )
    133 {
    134   EFI_STATUS            Status;
    135   FREE_POOL_HEADER      *Hdr;
    136   EFI_PHYSICAL_ADDRESS  Address;
    137   SMM_POOL_TYPE         SmmPoolType;
    138 
    139   SmmPoolType = UefiMemoryTypeToSmmPoolType(PoolType);
    140 
    141   ASSERT (PoolIndex <= MAX_POOL_INDEX);
    142   Status = EFI_SUCCESS;
    143   Hdr = NULL;
    144   if (PoolIndex == MAX_POOL_INDEX) {
    145     Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), &Address);
    146     if (EFI_ERROR (Status)) {
    147       return EFI_OUT_OF_RESOURCES;
    148     }
    149     Hdr = (FREE_POOL_HEADER *) (UINTN) Address;
    150   } else if (!IsListEmpty (&mSmmPoolLists[SmmPoolType][PoolIndex])) {
    151     Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[SmmPoolType][PoolIndex]), FREE_POOL_HEADER, Link);
    152     RemoveEntryList (&Hdr->Link);
    153   } else {
    154     Status = InternalAllocPoolByIndex (PoolType, PoolIndex + 1, &Hdr);
    155     if (!EFI_ERROR (Status)) {
    156       Hdr->Header.Size >>= 1;
    157       Hdr->Header.Available = TRUE;
    158       Hdr->Header.Type = PoolType;
    159       InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &Hdr->Link);
    160       Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size);
    161     }
    162   }
    163 
    164   if (!EFI_ERROR (Status)) {
    165     Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex;
    166     Hdr->Header.Available = FALSE;
    167     Hdr->Header.Type = PoolType;
    168   }
    169 
    170   *FreePoolHdr = Hdr;
    171   return Status;
    172 }
    173 
    174 /**
    175   Internal Function. Free a pool by specified PoolIndex.
    176 
    177   @param  FreePoolHdr           The pool to free.
    178 
    179   @retval EFI_SUCCESS           Pool successfully freed.
    180 
    181 **/
    182 EFI_STATUS
    183 InternalFreePoolByIndex (
    184   IN FREE_POOL_HEADER  *FreePoolHdr
    185   )
    186 {
    187   UINTN                 PoolIndex;
    188   SMM_POOL_TYPE         SmmPoolType;
    189 
    190   ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0);
    191   ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0);
    192   ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE);
    193 
    194   SmmPoolType = UefiMemoryTypeToSmmPoolType(FreePoolHdr->Header.Type);
    195 
    196   PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT);
    197   FreePoolHdr->Header.Available = TRUE;
    198   ASSERT (PoolIndex < MAX_POOL_INDEX);
    199   InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &FreePoolHdr->Link);
    200   return EFI_SUCCESS;
    201 }
    202 
    203 /**
    204   Allocate pool of a particular type.
    205 
    206   @param  PoolType               Type of pool to allocate.
    207   @param  Size                   The amount of pool to allocate.
    208   @param  Buffer                 The address to return a pointer to the allocated
    209                                  pool.
    210 
    211   @retval EFI_INVALID_PARAMETER  PoolType not valid.
    212   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
    213   @retval EFI_SUCCESS            Pool successfully allocated.
    214 
    215 **/
    216 EFI_STATUS
    217 EFIAPI
    218 SmmInternalAllocatePool (
    219   IN   EFI_MEMORY_TYPE  PoolType,
    220   IN   UINTN            Size,
    221   OUT  VOID             **Buffer
    222   )
    223 {
    224   POOL_HEADER           *PoolHdr;
    225   FREE_POOL_HEADER      *FreePoolHdr;
    226   EFI_STATUS            Status;
    227   EFI_PHYSICAL_ADDRESS  Address;
    228   UINTN                 PoolIndex;
    229 
    230   if (PoolType != EfiRuntimeServicesCode &&
    231       PoolType != EfiRuntimeServicesData) {
    232     return EFI_INVALID_PARAMETER;
    233   }
    234 
    235   Size += sizeof (*PoolHdr);
    236   if (Size > MAX_POOL_SIZE) {
    237     Size = EFI_SIZE_TO_PAGES (Size);
    238     Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, Size, &Address);
    239     if (EFI_ERROR (Status)) {
    240       return Status;
    241     }
    242 
    243     PoolHdr = (POOL_HEADER*)(UINTN)Address;
    244     PoolHdr->Size = EFI_PAGES_TO_SIZE (Size);
    245     PoolHdr->Available = FALSE;
    246     PoolHdr->Type = PoolType;
    247     *Buffer = PoolHdr + 1;
    248     return Status;
    249   }
    250 
    251   Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT;
    252   PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size);
    253   if ((Size & (Size - 1)) != 0) {
    254     PoolIndex++;
    255   }
    256 
    257   Status = InternalAllocPoolByIndex (PoolType, PoolIndex, &FreePoolHdr);
    258   if (!EFI_ERROR(Status)) {
    259     *Buffer = &FreePoolHdr->Header + 1;
    260   }
    261   return Status;
    262 }
    263 
    264 /**
    265   Allocate pool of a particular type.
    266 
    267   @param  PoolType               Type of pool to allocate.
    268   @param  Size                   The amount of pool to allocate.
    269   @param  Buffer                 The address to return a pointer to the allocated
    270                                  pool.
    271 
    272   @retval EFI_INVALID_PARAMETER  PoolType not valid.
    273   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
    274   @retval EFI_SUCCESS            Pool successfully allocated.
    275 
    276 **/
    277 EFI_STATUS
    278 EFIAPI
    279 SmmAllocatePool (
    280   IN   EFI_MEMORY_TYPE  PoolType,
    281   IN   UINTN            Size,
    282   OUT  VOID             **Buffer
    283   )
    284 {
    285   EFI_STATUS  Status;
    286 
    287   Status = SmmInternalAllocatePool (PoolType, Size, Buffer);
    288   if (!EFI_ERROR (Status)) {
    289     SmmCoreUpdateProfile (
    290       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
    291       MemoryProfileActionAllocatePool,
    292       PoolType,
    293       Size,
    294       *Buffer,
    295       NULL
    296       );
    297   }
    298   return Status;
    299 }
    300 
    301 /**
    302   Frees pool.
    303 
    304   @param  Buffer                 The allocated pool entry to free.
    305 
    306   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
    307   @retval EFI_SUCCESS            Pool successfully freed.
    308 
    309 **/
    310 EFI_STATUS
    311 EFIAPI
    312 SmmInternalFreePool (
    313   IN VOID  *Buffer
    314   )
    315 {
    316   FREE_POOL_HEADER  *FreePoolHdr;
    317 
    318   if (Buffer == NULL) {
    319     return EFI_INVALID_PARAMETER;
    320   }
    321 
    322   FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1);
    323   ASSERT (!FreePoolHdr->Header.Available);
    324 
    325   if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) {
    326     ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0);
    327     ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0);
    328     return SmmInternalFreePages (
    329              (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr,
    330              EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size)
    331              );
    332   }
    333   return InternalFreePoolByIndex (FreePoolHdr);
    334 }
    335 
    336 /**
    337   Frees pool.
    338 
    339   @param  Buffer                 The allocated pool entry to free.
    340 
    341   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
    342   @retval EFI_SUCCESS            Pool successfully freed.
    343 
    344 **/
    345 EFI_STATUS
    346 EFIAPI
    347 SmmFreePool (
    348   IN VOID  *Buffer
    349   )
    350 {
    351   EFI_STATUS  Status;
    352 
    353   Status = SmmInternalFreePool (Buffer);
    354   if (!EFI_ERROR (Status)) {
    355     SmmCoreUpdateProfile (
    356       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
    357       MemoryProfileActionFreePool,
    358       EfiMaxMemoryType,
    359       0,
    360       Buffer,
    361       NULL
    362       );
    363   }
    364   return Status;
    365 }
    366