Home | History | Annotate | Download | only in PiSmmCore
      1 /** @file
      2   SMM Memory page management functions.
      3 
      4   Copyright (c) 2009 - 2014, 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 #define TRUNCATE_TO_PAGES(a)  ((a) >> EFI_PAGE_SHIFT)
     18 
     19 LIST_ENTRY  mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
     20 
     21 /**
     22   Internal Function. Allocate n pages from given free page node.
     23 
     24   @param  Pages                  The free page node.
     25   @param  NumberOfPages          Number of pages to be allocated.
     26   @param  MaxAddress             Request to allocate memory below this address.
     27 
     28   @return Memory address of allocated pages.
     29 
     30 **/
     31 UINTN
     32 InternalAllocPagesOnOneNode (
     33   IN OUT FREE_PAGE_LIST  *Pages,
     34   IN     UINTN           NumberOfPages,
     35   IN     UINTN           MaxAddress
     36   )
     37 {
     38   UINTN           Top;
     39   UINTN           Bottom;
     40   FREE_PAGE_LIST  *Node;
     41 
     42   Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
     43   if (Top > Pages->NumberOfPages) {
     44     Top = Pages->NumberOfPages;
     45   }
     46   Bottom = Top - NumberOfPages;
     47 
     48   if (Top < Pages->NumberOfPages) {
     49     Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
     50     Node->NumberOfPages = Pages->NumberOfPages - Top;
     51     InsertHeadList (&Pages->Link, &Node->Link);
     52   }
     53 
     54   if (Bottom > 0) {
     55     Pages->NumberOfPages = Bottom;
     56   } else {
     57     RemoveEntryList (&Pages->Link);
     58   }
     59 
     60   return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
     61 }
     62 
     63 /**
     64   Internal Function. Allocate n pages from free page list below MaxAddress.
     65 
     66   @param  FreePageList           The free page node.
     67   @param  NumberOfPages          Number of pages to be allocated.
     68   @param  MaxAddress             Request to allocate memory below this address.
     69 
     70   @return Memory address of allocated pages.
     71 
     72 **/
     73 UINTN
     74 InternalAllocMaxAddress (
     75   IN OUT LIST_ENTRY  *FreePageList,
     76   IN     UINTN       NumberOfPages,
     77   IN     UINTN       MaxAddress
     78   )
     79 {
     80   LIST_ENTRY      *Node;
     81   FREE_PAGE_LIST  *Pages;
     82 
     83   for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
     84     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
     85     if (Pages->NumberOfPages >= NumberOfPages &&
     86         (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
     87       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
     88     }
     89   }
     90   return (UINTN)(-1);
     91 }
     92 
     93 /**
     94   Internal Function. Allocate n pages from free page list at given address.
     95 
     96   @param  FreePageList           The free page node.
     97   @param  NumberOfPages          Number of pages to be allocated.
     98   @param  MaxAddress             Request to allocate memory below this address.
     99 
    100   @return Memory address of allocated pages.
    101 
    102 **/
    103 UINTN
    104 InternalAllocAddress (
    105   IN OUT LIST_ENTRY  *FreePageList,
    106   IN     UINTN       NumberOfPages,
    107   IN     UINTN       Address
    108   )
    109 {
    110   UINTN           EndAddress;
    111   LIST_ENTRY      *Node;
    112   FREE_PAGE_LIST  *Pages;
    113 
    114   if ((Address & EFI_PAGE_MASK) != 0) {
    115     return ~Address;
    116   }
    117 
    118   EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
    119   for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
    120     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
    121     if ((UINTN)Pages <= Address) {
    122       if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
    123         break;
    124       }
    125       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
    126     }
    127   }
    128   return ~Address;
    129 }
    130 
    131 /**
    132   Allocates pages from the memory map.
    133 
    134   @param  Type                   The type of allocation to perform.
    135   @param  MemoryType             The type of memory to turn the allocated pages
    136                                  into.
    137   @param  NumberOfPages          The number of pages to allocate.
    138   @param  Memory                 A pointer to receive the base allocated memory
    139                                  address.
    140 
    141   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
    142   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
    143   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
    144   @retval EFI_SUCCESS            Pages successfully allocated.
    145 
    146 **/
    147 EFI_STATUS
    148 EFIAPI
    149 SmmInternalAllocatePages (
    150   IN  EFI_ALLOCATE_TYPE     Type,
    151   IN  EFI_MEMORY_TYPE       MemoryType,
    152   IN  UINTN                 NumberOfPages,
    153   OUT EFI_PHYSICAL_ADDRESS  *Memory
    154   )
    155 {
    156   UINTN  RequestedAddress;
    157 
    158   if (MemoryType != EfiRuntimeServicesCode &&
    159       MemoryType != EfiRuntimeServicesData) {
    160     return EFI_INVALID_PARAMETER;
    161   }
    162 
    163   if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
    164     return EFI_OUT_OF_RESOURCES;
    165   }
    166 
    167   //
    168   // We don't track memory type in SMM
    169   //
    170   RequestedAddress = (UINTN)*Memory;
    171   switch (Type) {
    172     case AllocateAnyPages:
    173       RequestedAddress = (UINTN)(-1);
    174     case AllocateMaxAddress:
    175       *Memory = InternalAllocMaxAddress (
    176                   &mSmmMemoryMap,
    177                   NumberOfPages,
    178                   RequestedAddress
    179                   );
    180       if (*Memory == (UINTN)-1) {
    181         return EFI_OUT_OF_RESOURCES;
    182       }
    183       break;
    184     case AllocateAddress:
    185       *Memory = InternalAllocAddress (
    186                   &mSmmMemoryMap,
    187                   NumberOfPages,
    188                   RequestedAddress
    189                   );
    190       if (*Memory != RequestedAddress) {
    191         return EFI_NOT_FOUND;
    192       }
    193       break;
    194     default:
    195       return EFI_INVALID_PARAMETER;
    196   }
    197   return EFI_SUCCESS;
    198 }
    199 
    200 /**
    201   Allocates pages from the memory map.
    202 
    203   @param  Type                   The type of allocation to perform.
    204   @param  MemoryType             The type of memory to turn the allocated pages
    205                                  into.
    206   @param  NumberOfPages          The number of pages to allocate.
    207   @param  Memory                 A pointer to receive the base allocated memory
    208                                  address.
    209 
    210   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
    211   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
    212   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
    213   @retval EFI_SUCCESS            Pages successfully allocated.
    214 
    215 **/
    216 EFI_STATUS
    217 EFIAPI
    218 SmmAllocatePages (
    219   IN  EFI_ALLOCATE_TYPE     Type,
    220   IN  EFI_MEMORY_TYPE       MemoryType,
    221   IN  UINTN                 NumberOfPages,
    222   OUT EFI_PHYSICAL_ADDRESS  *Memory
    223   )
    224 {
    225   EFI_STATUS  Status;
    226 
    227   Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory);
    228   if (!EFI_ERROR (Status)) {
    229     SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionAllocatePages, MemoryType, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) *Memory);
    230   }
    231   return Status;
    232 }
    233 
    234 /**
    235   Internal Function. Merge two adjacent nodes.
    236 
    237   @param  First             The first of two nodes to merge.
    238 
    239   @return Pointer to node after merge (if success) or pointer to next node (if fail).
    240 
    241 **/
    242 FREE_PAGE_LIST *
    243 InternalMergeNodes (
    244   IN FREE_PAGE_LIST  *First
    245   )
    246 {
    247   FREE_PAGE_LIST  *Next;
    248 
    249   Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
    250   ASSERT (
    251     TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
    252 
    253   if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
    254     First->NumberOfPages += Next->NumberOfPages;
    255     RemoveEntryList (&Next->Link);
    256     Next = First;
    257   }
    258   return Next;
    259 }
    260 
    261 /**
    262   Frees previous allocated pages.
    263 
    264   @param  Memory                 Base address of memory being freed.
    265   @param  NumberOfPages          The number of pages to free.
    266 
    267   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
    268   @retval EFI_INVALID_PARAMETER  Address not aligned.
    269   @return EFI_SUCCESS            Pages successfully freed.
    270 
    271 **/
    272 EFI_STATUS
    273 EFIAPI
    274 SmmInternalFreePages (
    275   IN EFI_PHYSICAL_ADDRESS  Memory,
    276   IN UINTN                 NumberOfPages
    277   )
    278 {
    279   LIST_ENTRY      *Node;
    280   FREE_PAGE_LIST  *Pages;
    281 
    282   if ((Memory & EFI_PAGE_MASK) != 0) {
    283     return EFI_INVALID_PARAMETER;
    284   }
    285 
    286   Pages = NULL;
    287   Node = mSmmMemoryMap.ForwardLink;
    288   while (Node != &mSmmMemoryMap) {
    289     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
    290     if (Memory < (UINTN)Pages) {
    291       break;
    292     }
    293     Node = Node->ForwardLink;
    294   }
    295 
    296   if (Node != &mSmmMemoryMap &&
    297       Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
    298     return EFI_INVALID_PARAMETER;
    299   }
    300 
    301   if (Node->BackLink != &mSmmMemoryMap) {
    302     Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
    303     if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
    304       return EFI_INVALID_PARAMETER;
    305     }
    306   }
    307 
    308   Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
    309   Pages->NumberOfPages = NumberOfPages;
    310   InsertTailList (Node, &Pages->Link);
    311 
    312   if (Pages->Link.BackLink != &mSmmMemoryMap) {
    313     Pages = InternalMergeNodes (
    314               BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
    315               );
    316   }
    317 
    318   if (Node != &mSmmMemoryMap) {
    319     InternalMergeNodes (Pages);
    320   }
    321 
    322   return EFI_SUCCESS;
    323 }
    324 
    325 /**
    326   Frees previous allocated pages.
    327 
    328   @param  Memory                 Base address of memory being freed.
    329   @param  NumberOfPages          The number of pages to free.
    330 
    331   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
    332   @retval EFI_INVALID_PARAMETER  Address not aligned.
    333   @return EFI_SUCCESS            Pages successfully freed.
    334 
    335 **/
    336 EFI_STATUS
    337 EFIAPI
    338 SmmFreePages (
    339   IN EFI_PHYSICAL_ADDRESS  Memory,
    340   IN UINTN                 NumberOfPages
    341   )
    342 {
    343   EFI_STATUS  Status;
    344 
    345   Status = SmmInternalFreePages (Memory, NumberOfPages);
    346   if (!EFI_ERROR (Status)) {
    347     SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionFreePages, 0, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) Memory);
    348   }
    349   return Status;
    350 }
    351 
    352 /**
    353   Add free SMRAM region for use by memory service.
    354 
    355   @param  MemBase                Base address of memory region.
    356   @param  MemLength              Length of the memory region.
    357   @param  Type                   Memory type.
    358   @param  Attributes             Memory region state.
    359 
    360 **/
    361 VOID
    362 SmmAddMemoryRegion (
    363   IN  EFI_PHYSICAL_ADDRESS  MemBase,
    364   IN  UINT64                MemLength,
    365   IN  EFI_MEMORY_TYPE       Type,
    366   IN  UINT64                Attributes
    367   )
    368 {
    369   UINTN  AlignedMemBase;
    370 
    371   //
    372   // Do not add memory regions that is already allocated, needs testing, or needs ECC initialization
    373   //
    374   if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
    375     return;
    376   }
    377 
    378   //
    379   // Align range on an EFI_PAGE_SIZE boundary
    380   //
    381   AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
    382   MemLength -= AlignedMemBase - MemBase;
    383   SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));
    384 }
    385