Home | History | Annotate | Download | only in PiSmmCore
      1 /** @file
      2   SMM Memory page 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 #include <Library/SmmServicesTableLib.h>
     17 
     18 #define TRUNCATE_TO_PAGES(a)  ((a) >> EFI_PAGE_SHIFT)
     19 
     20 LIST_ENTRY  mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
     21 
     22 //
     23 // For GetMemoryMap()
     24 //
     25 
     26 #define MEMORY_MAP_SIGNATURE   SIGNATURE_32('m','m','a','p')
     27 typedef struct {
     28   UINTN           Signature;
     29   LIST_ENTRY      Link;
     30 
     31   BOOLEAN         FromStack;
     32   EFI_MEMORY_TYPE Type;
     33   UINT64          Start;
     34   UINT64          End;
     35 
     36 } MEMORY_MAP;
     37 
     38 LIST_ENTRY        gMemoryMap  = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap);
     39 
     40 
     41 #define MAX_MAP_DEPTH 6
     42 
     43 ///
     44 /// mMapDepth - depth of new descriptor stack
     45 ///
     46 UINTN         mMapDepth = 0;
     47 ///
     48 /// mMapStack - space to use as temp storage to build new map descriptors
     49 ///
     50 MEMORY_MAP    mMapStack[MAX_MAP_DEPTH];
     51 UINTN         mFreeMapStack = 0;
     52 ///
     53 /// This list maintain the free memory map list
     54 ///
     55 LIST_ENTRY   mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);
     56 
     57 /**
     58   Allocates pages from the memory map.
     59 
     60   @param[in]   Type                   The type of allocation to perform.
     61   @param[in]   MemoryType             The type of memory to turn the allocated pages
     62                                       into.
     63   @param[in]   NumberOfPages          The number of pages to allocate.
     64   @param[out]  Memory                 A pointer to receive the base allocated memory
     65                                       address.
     66   @param[in]   AddRegion              If this memory is new added region.
     67 
     68   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
     69   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
     70   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
     71   @retval EFI_SUCCESS            Pages successfully allocated.
     72 
     73 **/
     74 EFI_STATUS
     75 SmmInternalAllocatePagesEx (
     76   IN  EFI_ALLOCATE_TYPE     Type,
     77   IN  EFI_MEMORY_TYPE       MemoryType,
     78   IN  UINTN                 NumberOfPages,
     79   OUT EFI_PHYSICAL_ADDRESS  *Memory,
     80   IN  BOOLEAN               AddRegion
     81   );
     82 
     83 /**
     84   Internal function.  Deque a descriptor entry from the mFreeMemoryMapEntryList.
     85   If the list is emtry, then allocate a new page to refuel the list.
     86   Please Note this algorithm to allocate the memory map descriptor has a property
     87   that the memory allocated for memory entries always grows, and will never really be freed.
     88 
     89   @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList
     90 
     91 **/
     92 MEMORY_MAP *
     93 AllocateMemoryMapEntry (
     94   VOID
     95   )
     96 {
     97   EFI_PHYSICAL_ADDRESS   Mem;
     98   EFI_STATUS             Status;
     99   MEMORY_MAP*            FreeDescriptorEntries;
    100   MEMORY_MAP*            Entry;
    101   UINTN                  Index;
    102 
    103   //DEBUG((DEBUG_INFO, "AllocateMemoryMapEntry\n"));
    104 
    105   if (IsListEmpty (&mFreeMemoryMapEntryList)) {
    106     //DEBUG((DEBUG_INFO, "mFreeMemoryMapEntryList is empty\n"));
    107     //
    108     // The list is empty, to allocate one page to refuel the list
    109     //
    110     Status = SmmInternalAllocatePagesEx (
    111                AllocateAnyPages,
    112                EfiRuntimeServicesData,
    113                EFI_SIZE_TO_PAGES(DEFAULT_PAGE_ALLOCATION),
    114                &Mem,
    115                TRUE
    116                );
    117     ASSERT_EFI_ERROR (Status);
    118     if(!EFI_ERROR (Status)) {
    119       FreeDescriptorEntries = (MEMORY_MAP *)(UINTN)Mem;
    120       //DEBUG((DEBUG_INFO, "New FreeDescriptorEntries - 0x%x\n", FreeDescriptorEntries));
    121       //
    122       // Enque the free memmory map entries into the list
    123       //
    124       for (Index = 0; Index< DEFAULT_PAGE_ALLOCATION / sizeof(MEMORY_MAP); Index++) {
    125         FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;
    126         InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);
    127       }
    128     } else {
    129       return NULL;
    130     }
    131   }
    132   //
    133   // dequeue the first descriptor from the list
    134   //
    135   Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    136   RemoveEntryList (&Entry->Link);
    137 
    138   return Entry;
    139 }
    140 
    141 
    142 /**
    143   Internal function.  Moves any memory descriptors that are on the
    144   temporary descriptor stack to heap.
    145 
    146 **/
    147 VOID
    148 CoreFreeMemoryMapStack (
    149   VOID
    150   )
    151 {
    152   MEMORY_MAP      *Entry;
    153 
    154   //
    155   // If already freeing the map stack, then return
    156   //
    157   if (mFreeMapStack != 0) {
    158     ASSERT (FALSE);
    159     return ;
    160   }
    161 
    162   //
    163   // Move the temporary memory descriptor stack into pool
    164   //
    165   mFreeMapStack += 1;
    166 
    167   while (mMapDepth != 0) {
    168     //
    169     // Deque an memory map entry from mFreeMemoryMapEntryList
    170     //
    171     Entry = AllocateMemoryMapEntry ();
    172     ASSERT (Entry);
    173 
    174     //
    175     // Update to proper entry
    176     //
    177     mMapDepth -= 1;
    178 
    179     if (mMapStack[mMapDepth].Link.ForwardLink != NULL) {
    180 
    181       CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP));
    182       Entry->FromStack = FALSE;
    183 
    184       //
    185       // Move this entry to general memory
    186       //
    187       InsertTailList (&mMapStack[mMapDepth].Link, &Entry->Link);
    188       RemoveEntryList (&mMapStack[mMapDepth].Link);
    189       mMapStack[mMapDepth].Link.ForwardLink = NULL;
    190     }
    191   }
    192 
    193   mFreeMapStack -= 1;
    194 }
    195 
    196 /**
    197   Insert new entry from memory map.
    198 
    199   @param[in]  Link       The old memory map entry to be linked.
    200   @param[in]  Start      The start address of new memory map entry.
    201   @param[in]  End        The end address of new memory map entry.
    202   @param[in]  Type       The type of new memory map entry.
    203   @param[in]  Next       If new entry is inserted to the next of old entry.
    204   @param[in]  AddRegion  If this memory is new added region.
    205 **/
    206 VOID
    207 InsertNewEntry (
    208   IN LIST_ENTRY      *Link,
    209   IN UINT64          Start,
    210   IN UINT64          End,
    211   IN EFI_MEMORY_TYPE Type,
    212   IN BOOLEAN         Next,
    213   IN BOOLEAN         AddRegion
    214   )
    215 {
    216   MEMORY_MAP  *Entry;
    217 
    218   Entry = &mMapStack[mMapDepth];
    219   mMapDepth += 1;
    220   ASSERT (mMapDepth < MAX_MAP_DEPTH);
    221   Entry->FromStack = TRUE;
    222 
    223   Entry->Signature = MEMORY_MAP_SIGNATURE;
    224   Entry->Type = Type;
    225   Entry->Start = Start;
    226   Entry->End = End;
    227   if (Next) {
    228     InsertHeadList (Link, &Entry->Link);
    229   } else {
    230     InsertTailList (Link, &Entry->Link);
    231   }
    232 }
    233 
    234 /**
    235   Remove old entry from memory map.
    236 
    237   @param[in] Entry Memory map entry to be removed.
    238 **/
    239 VOID
    240 RemoveOldEntry (
    241   IN MEMORY_MAP  *Entry
    242   )
    243 {
    244   RemoveEntryList (&Entry->Link);
    245   if (!Entry->FromStack) {
    246     InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);
    247   }
    248 }
    249 
    250 /**
    251   Update SMM memory map entry.
    252 
    253   @param[in]  Type                   The type of allocation to perform.
    254   @param[in]  Memory                 The base of memory address.
    255   @param[in]  NumberOfPages          The number of pages to allocate.
    256   @param[in]  AddRegion              If this memory is new added region.
    257 **/
    258 VOID
    259 ConvertSmmMemoryMapEntry (
    260   IN EFI_MEMORY_TYPE       Type,
    261   IN EFI_PHYSICAL_ADDRESS  Memory,
    262   IN UINTN                 NumberOfPages,
    263   IN BOOLEAN               AddRegion
    264   )
    265 {
    266   LIST_ENTRY               *Link;
    267   MEMORY_MAP               *Entry;
    268   MEMORY_MAP               *NextEntry;
    269   LIST_ENTRY               *NextLink;
    270   MEMORY_MAP               *PreviousEntry;
    271   LIST_ENTRY               *PreviousLink;
    272   EFI_PHYSICAL_ADDRESS     Start;
    273   EFI_PHYSICAL_ADDRESS     End;
    274 
    275   Start = Memory;
    276   End = Memory + EFI_PAGES_TO_SIZE(NumberOfPages) - 1;
    277 
    278   //
    279   // Exclude memory region
    280   //
    281   Link = gMemoryMap.ForwardLink;
    282   while (Link != &gMemoryMap) {
    283     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    284     Link  = Link->ForwardLink;
    285 
    286     //
    287     // ---------------------------------------------------
    288     // |  +----------+   +------+   +------+   +------+  |
    289     // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|---
    290     //    +----------+ ^ +------+   +------+   +------+
    291     //                 |
    292     //              +------+
    293     //              |EntryX|
    294     //              +------+
    295     //
    296     if (Entry->Start > End) {
    297       if ((Entry->Start == End + 1) && (Entry->Type == Type)) {
    298         Entry->Start = Start;
    299         return ;
    300       }
    301       InsertNewEntry (
    302         &Entry->Link,
    303         Start,
    304         End,
    305         Type,
    306         FALSE,
    307         AddRegion
    308         );
    309       return ;
    310     }
    311 
    312     if ((Entry->Start <= Start) && (Entry->End >= End)) {
    313       if (Entry->Type != Type) {
    314         if (Entry->Start < Start) {
    315           //
    316           // ---------------------------------------------------
    317           // |  +----------+   +------+   +------+   +------+  |
    318           // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|---
    319           //    +----------+   +------+ ^ +------+   +------+
    320           //                            |
    321           //                         +------+
    322           //                         |EntryA|
    323           //                         +------+
    324           //
    325           InsertNewEntry (
    326             &Entry->Link,
    327             Entry->Start,
    328             Start - 1,
    329             Entry->Type,
    330             FALSE,
    331             AddRegion
    332             );
    333         }
    334         if (Entry->End > End) {
    335           //
    336           // ---------------------------------------------------
    337           // |  +----------+   +------+   +------+   +------+  |
    338           // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|---
    339           //    +----------+   +------+   +------+ ^ +------+
    340           //                                       |
    341           //                                    +------+
    342           //                                    |EntryZ|
    343           //                                    +------+
    344           //
    345           InsertNewEntry (
    346             &Entry->Link,
    347             End + 1,
    348             Entry->End,
    349             Entry->Type,
    350             TRUE,
    351             AddRegion
    352             );
    353         }
    354         //
    355         // Update this node
    356         //
    357         Entry->Start = Start;
    358         Entry->End = End;
    359         Entry->Type = Type;
    360 
    361         //
    362         // Check adjacent
    363         //
    364         NextLink = Entry->Link.ForwardLink;
    365         if (NextLink != &gMemoryMap) {
    366           NextEntry = CR (NextLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    367           //
    368           // ---------------------------------------------------
    369           // |  +----------+   +------+   +-----------------+  |
    370           // ---|gMemoryMep|---|Entry1|---|EntryX     Entry3|---
    371           //    +----------+   +------+   +-----------------+
    372           //
    373           if ((Entry->Type == NextEntry->Type) && (Entry->End + 1 == NextEntry->Start)) {
    374             Entry->End = NextEntry->End;
    375             RemoveOldEntry (NextEntry);
    376           }
    377         }
    378         PreviousLink = Entry->Link.BackLink;
    379         if (PreviousLink != &gMemoryMap) {
    380           PreviousEntry = CR (PreviousLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    381           //
    382           // ---------------------------------------------------
    383           // |  +----------+   +-----------------+   +------+  |
    384           // ---|gMemoryMep|---|Entry1     EntryX|---|Entry3|---
    385           //    +----------+   +-----------------+   +------+
    386           //
    387           if ((PreviousEntry->Type == Entry->Type) && (PreviousEntry->End + 1 == Entry->Start)) {
    388             PreviousEntry->End = Entry->End;
    389             RemoveOldEntry (Entry);
    390           }
    391         }
    392       }
    393       return ;
    394     }
    395   }
    396 
    397   //
    398   // ---------------------------------------------------
    399   // |  +----------+   +------+   +------+   +------+  |
    400   // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|---
    401   //    +----------+   +------+   +------+   +------+ ^
    402   //                                                  |
    403   //                                               +------+
    404   //                                               |EntryX|
    405   //                                               +------+
    406   //
    407   Link = gMemoryMap.BackLink;
    408   if (Link != &gMemoryMap) {
    409     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    410     if ((Entry->End + 1 == Start) && (Entry->Type == Type)) {
    411       Entry->End = End;
    412       return ;
    413     }
    414   }
    415   InsertNewEntry (
    416     &gMemoryMap,
    417     Start,
    418     End,
    419     Type,
    420     FALSE,
    421     AddRegion
    422     );
    423   return ;
    424 }
    425 
    426 /**
    427   Return the count of Smm memory map entry.
    428 
    429   @return The count of Smm memory map entry.
    430 **/
    431 UINTN
    432 GetSmmMemoryMapEntryCount (
    433   VOID
    434   )
    435 {
    436   LIST_ENTRY               *Link;
    437   UINTN                    Count;
    438 
    439   Count = 0;
    440   Link = gMemoryMap.ForwardLink;
    441   while (Link != &gMemoryMap) {
    442     Link  = Link->ForwardLink;
    443     Count++;
    444   }
    445   return Count;
    446 }
    447 
    448 /**
    449   Dump Smm memory map entry.
    450 **/
    451 VOID
    452 DumpSmmMemoryMapEntry (
    453   VOID
    454   )
    455 {
    456   LIST_ENTRY               *Link;
    457   MEMORY_MAP               *Entry;
    458   EFI_PHYSICAL_ADDRESS     Last;
    459 
    460   Last = 0;
    461   DEBUG ((DEBUG_INFO, "DumpSmmMemoryMapEntry:\n"));
    462   Link = gMemoryMap.ForwardLink;
    463   while (Link != &gMemoryMap) {
    464     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    465     Link  = Link->ForwardLink;
    466 
    467     if ((Last != 0) && (Last != (UINT64)-1)) {
    468       if (Last + 1 != Entry->Start) {
    469         Last = (UINT64)-1;
    470       } else {
    471         Last = Entry->End;
    472       }
    473     } else if (Last == 0) {
    474       Last = Entry->End;
    475     }
    476 
    477     DEBUG ((DEBUG_INFO, "Entry (Link - 0x%x)\n", &Entry->Link));
    478     DEBUG ((DEBUG_INFO, "  Signature         - 0x%x\n", Entry->Signature));
    479     DEBUG ((DEBUG_INFO, "  Link.ForwardLink  - 0x%x\n", Entry->Link.ForwardLink));
    480     DEBUG ((DEBUG_INFO, "  Link.BackLink     - 0x%x\n", Entry->Link.BackLink));
    481     DEBUG ((DEBUG_INFO, "  Type              - 0x%x\n", Entry->Type));
    482     DEBUG ((DEBUG_INFO, "  Start             - 0x%016lx\n", Entry->Start));
    483     DEBUG ((DEBUG_INFO, "  End               - 0x%016lx\n", Entry->End));
    484   }
    485 
    486   ASSERT (Last != (UINT64)-1);
    487 }
    488 
    489 /**
    490   Dump Smm memory map.
    491 **/
    492 VOID
    493 DumpSmmMemoryMap (
    494   VOID
    495   )
    496 {
    497   LIST_ENTRY      *Node;
    498   FREE_PAGE_LIST  *Pages;
    499 
    500   DEBUG ((DEBUG_INFO, "DumpSmmMemoryMap\n"));
    501 
    502   Pages = NULL;
    503   Node = mSmmMemoryMap.ForwardLink;
    504   while (Node != &mSmmMemoryMap) {
    505     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
    506     DEBUG ((DEBUG_INFO, "Pages - 0x%x\n", Pages));
    507     DEBUG ((DEBUG_INFO, "Pages->NumberOfPages - 0x%x\n", Pages->NumberOfPages));
    508     Node = Node->ForwardLink;
    509   }
    510 }
    511 
    512 /**
    513   Check if a Smm base~length is in Smm memory map.
    514 
    515   @param[in] Base   The base address of Smm memory to be checked.
    516   @param[in] Length THe length of Smm memory to be checked.
    517 
    518   @retval TRUE  Smm base~length is in smm memory map.
    519   @retval FALSE Smm base~length is in smm memory map.
    520 **/
    521 BOOLEAN
    522 SmmMemoryMapConsistencyCheckRange (
    523   IN EFI_PHYSICAL_ADDRESS Base,
    524   IN UINTN                Length
    525   )
    526 {
    527   LIST_ENTRY               *Link;
    528   MEMORY_MAP               *Entry;
    529   BOOLEAN                  Result;
    530 
    531   Result = FALSE;
    532   Link = gMemoryMap.ForwardLink;
    533   while (Link != &gMemoryMap) {
    534     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
    535     Link  = Link->ForwardLink;
    536 
    537     if (Entry->Type != EfiConventionalMemory) {
    538       continue;
    539     }
    540     if (Entry->Start == Base && Entry->End == Base + Length - 1) {
    541       Result = TRUE;
    542       break;
    543     }
    544   }
    545 
    546   return Result;
    547 }
    548 
    549 /**
    550   Check the consistency of Smm memory map.
    551 **/
    552 VOID
    553 SmmMemoryMapConsistencyCheck (
    554   VOID
    555   )
    556 {
    557   LIST_ENTRY      *Node;
    558   FREE_PAGE_LIST  *Pages;
    559   BOOLEAN         Result;
    560 
    561   Pages = NULL;
    562   Node = mSmmMemoryMap.ForwardLink;
    563   while (Node != &mSmmMemoryMap) {
    564     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
    565     Result = SmmMemoryMapConsistencyCheckRange ((EFI_PHYSICAL_ADDRESS)(UINTN)Pages, (UINTN)EFI_PAGES_TO_SIZE(Pages->NumberOfPages));
    566     ASSERT (Result);
    567     Node = Node->ForwardLink;
    568   }
    569 }
    570 
    571 /**
    572   Internal Function. Allocate n pages from given free page node.
    573 
    574   @param  Pages                  The free page node.
    575   @param  NumberOfPages          Number of pages to be allocated.
    576   @param  MaxAddress             Request to allocate memory below this address.
    577 
    578   @return Memory address of allocated pages.
    579 
    580 **/
    581 UINTN
    582 InternalAllocPagesOnOneNode (
    583   IN OUT FREE_PAGE_LIST  *Pages,
    584   IN     UINTN           NumberOfPages,
    585   IN     UINTN           MaxAddress
    586   )
    587 {
    588   UINTN           Top;
    589   UINTN           Bottom;
    590   FREE_PAGE_LIST  *Node;
    591 
    592   Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
    593   if (Top > Pages->NumberOfPages) {
    594     Top = Pages->NumberOfPages;
    595   }
    596   Bottom = Top - NumberOfPages;
    597 
    598   if (Top < Pages->NumberOfPages) {
    599     Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
    600     Node->NumberOfPages = Pages->NumberOfPages - Top;
    601     InsertHeadList (&Pages->Link, &Node->Link);
    602   }
    603 
    604   if (Bottom > 0) {
    605     Pages->NumberOfPages = Bottom;
    606   } else {
    607     RemoveEntryList (&Pages->Link);
    608   }
    609 
    610   return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
    611 }
    612 
    613 /**
    614   Internal Function. Allocate n pages from free page list below MaxAddress.
    615 
    616   @param  FreePageList           The free page node.
    617   @param  NumberOfPages          Number of pages to be allocated.
    618   @param  MaxAddress             Request to allocate memory below this address.
    619 
    620   @return Memory address of allocated pages.
    621 
    622 **/
    623 UINTN
    624 InternalAllocMaxAddress (
    625   IN OUT LIST_ENTRY  *FreePageList,
    626   IN     UINTN       NumberOfPages,
    627   IN     UINTN       MaxAddress
    628   )
    629 {
    630   LIST_ENTRY      *Node;
    631   FREE_PAGE_LIST  *Pages;
    632 
    633   for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
    634     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
    635     if (Pages->NumberOfPages >= NumberOfPages &&
    636         (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
    637       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
    638     }
    639   }
    640   return (UINTN)(-1);
    641 }
    642 
    643 /**
    644   Internal Function. Allocate n pages from free page list at given address.
    645 
    646   @param  FreePageList           The free page node.
    647   @param  NumberOfPages          Number of pages to be allocated.
    648   @param  MaxAddress             Request to allocate memory below this address.
    649 
    650   @return Memory address of allocated pages.
    651 
    652 **/
    653 UINTN
    654 InternalAllocAddress (
    655   IN OUT LIST_ENTRY  *FreePageList,
    656   IN     UINTN       NumberOfPages,
    657   IN     UINTN       Address
    658   )
    659 {
    660   UINTN           EndAddress;
    661   LIST_ENTRY      *Node;
    662   FREE_PAGE_LIST  *Pages;
    663 
    664   if ((Address & EFI_PAGE_MASK) != 0) {
    665     return ~Address;
    666   }
    667 
    668   EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
    669   for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
    670     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
    671     if ((UINTN)Pages <= Address) {
    672       if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
    673         break;
    674       }
    675       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
    676     }
    677   }
    678   return ~Address;
    679 }
    680 
    681 /**
    682   Allocates pages from the memory map.
    683 
    684   @param[in]   Type                   The type of allocation to perform.
    685   @param[in]   MemoryType             The type of memory to turn the allocated pages
    686                                       into.
    687   @param[in]   NumberOfPages          The number of pages to allocate.
    688   @param[out]  Memory                 A pointer to receive the base allocated memory
    689                                       address.
    690   @param[in]   AddRegion              If this memory is new added region.
    691 
    692   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
    693   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
    694   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
    695   @retval EFI_SUCCESS            Pages successfully allocated.
    696 
    697 **/
    698 EFI_STATUS
    699 SmmInternalAllocatePagesEx (
    700   IN  EFI_ALLOCATE_TYPE     Type,
    701   IN  EFI_MEMORY_TYPE       MemoryType,
    702   IN  UINTN                 NumberOfPages,
    703   OUT EFI_PHYSICAL_ADDRESS  *Memory,
    704   IN  BOOLEAN               AddRegion
    705   )
    706 {
    707   UINTN  RequestedAddress;
    708 
    709   if (MemoryType != EfiRuntimeServicesCode &&
    710       MemoryType != EfiRuntimeServicesData) {
    711     return EFI_INVALID_PARAMETER;
    712   }
    713 
    714   if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
    715     return EFI_OUT_OF_RESOURCES;
    716   }
    717 
    718   //
    719   // We don't track memory type in SMM
    720   //
    721   RequestedAddress = (UINTN)*Memory;
    722   switch (Type) {
    723     case AllocateAnyPages:
    724       RequestedAddress = (UINTN)(-1);
    725     case AllocateMaxAddress:
    726       *Memory = InternalAllocMaxAddress (
    727                   &mSmmMemoryMap,
    728                   NumberOfPages,
    729                   RequestedAddress
    730                   );
    731       if (*Memory == (UINTN)-1) {
    732         return EFI_OUT_OF_RESOURCES;
    733       }
    734       break;
    735     case AllocateAddress:
    736       *Memory = InternalAllocAddress (
    737                   &mSmmMemoryMap,
    738                   NumberOfPages,
    739                   RequestedAddress
    740                   );
    741       if (*Memory != RequestedAddress) {
    742         return EFI_NOT_FOUND;
    743       }
    744       break;
    745     default:
    746       return EFI_INVALID_PARAMETER;
    747   }
    748 
    749   //
    750   // Update SmmMemoryMap here.
    751   //
    752   ConvertSmmMemoryMapEntry (MemoryType, *Memory, NumberOfPages, AddRegion);
    753   if (!AddRegion) {
    754     CoreFreeMemoryMapStack();
    755   }
    756 
    757   return EFI_SUCCESS;
    758 }
    759 
    760 /**
    761   Allocates pages from the memory map.
    762 
    763   @param[in]   Type                   The type of allocation to perform.
    764   @param[in]   MemoryType             The type of memory to turn the allocated pages
    765                                       into.
    766   @param[in]   NumberOfPages          The number of pages to allocate.
    767   @param[out]  Memory                 A pointer to receive the base allocated memory
    768                                       address.
    769 
    770   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
    771   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
    772   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
    773   @retval EFI_SUCCESS            Pages successfully allocated.
    774 
    775 **/
    776 EFI_STATUS
    777 EFIAPI
    778 SmmInternalAllocatePages (
    779   IN  EFI_ALLOCATE_TYPE     Type,
    780   IN  EFI_MEMORY_TYPE       MemoryType,
    781   IN  UINTN                 NumberOfPages,
    782   OUT EFI_PHYSICAL_ADDRESS  *Memory
    783   )
    784 {
    785   return SmmInternalAllocatePagesEx (Type, MemoryType, NumberOfPages, Memory, FALSE);
    786 }
    787 
    788 /**
    789   Allocates pages from the memory map.
    790 
    791   @param  Type                   The type of allocation to perform.
    792   @param  MemoryType             The type of memory to turn the allocated pages
    793                                  into.
    794   @param  NumberOfPages          The number of pages to allocate.
    795   @param  Memory                 A pointer to receive the base allocated memory
    796                                  address.
    797 
    798   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
    799   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
    800   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
    801   @retval EFI_SUCCESS            Pages successfully allocated.
    802 
    803 **/
    804 EFI_STATUS
    805 EFIAPI
    806 SmmAllocatePages (
    807   IN  EFI_ALLOCATE_TYPE     Type,
    808   IN  EFI_MEMORY_TYPE       MemoryType,
    809   IN  UINTN                 NumberOfPages,
    810   OUT EFI_PHYSICAL_ADDRESS  *Memory
    811   )
    812 {
    813   EFI_STATUS  Status;
    814 
    815   Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory);
    816   if (!EFI_ERROR (Status)) {
    817     SmmCoreUpdateProfile (
    818       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
    819       MemoryProfileActionAllocatePages,
    820       MemoryType,
    821       EFI_PAGES_TO_SIZE (NumberOfPages),
    822       (VOID *) (UINTN) *Memory,
    823       NULL
    824       );
    825   }
    826   return Status;
    827 }
    828 
    829 /**
    830   Internal Function. Merge two adjacent nodes.
    831 
    832   @param  First             The first of two nodes to merge.
    833 
    834   @return Pointer to node after merge (if success) or pointer to next node (if fail).
    835 
    836 **/
    837 FREE_PAGE_LIST *
    838 InternalMergeNodes (
    839   IN FREE_PAGE_LIST  *First
    840   )
    841 {
    842   FREE_PAGE_LIST  *Next;
    843 
    844   Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
    845   ASSERT (
    846     TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
    847 
    848   if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
    849     First->NumberOfPages += Next->NumberOfPages;
    850     RemoveEntryList (&Next->Link);
    851     Next = First;
    852   }
    853   return Next;
    854 }
    855 
    856 /**
    857   Frees previous allocated pages.
    858 
    859   @param[in]  Memory                 Base address of memory being freed.
    860   @param[in]  NumberOfPages          The number of pages to free.
    861   @param[in]  AddRegion              If this memory is new added region.
    862 
    863   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
    864   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
    865   @return EFI_SUCCESS            Pages successfully freed.
    866 
    867 **/
    868 EFI_STATUS
    869 SmmInternalFreePagesEx (
    870   IN EFI_PHYSICAL_ADDRESS  Memory,
    871   IN UINTN                 NumberOfPages,
    872   IN BOOLEAN               AddRegion
    873   )
    874 {
    875   LIST_ENTRY      *Node;
    876   FREE_PAGE_LIST  *Pages;
    877 
    878   if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) {
    879     return EFI_INVALID_PARAMETER;
    880   }
    881 
    882   Pages = NULL;
    883   Node = mSmmMemoryMap.ForwardLink;
    884   while (Node != &mSmmMemoryMap) {
    885     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
    886     if (Memory < (UINTN)Pages) {
    887       break;
    888     }
    889     Node = Node->ForwardLink;
    890   }
    891 
    892   if (Node != &mSmmMemoryMap &&
    893       Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
    894     return EFI_INVALID_PARAMETER;
    895   }
    896 
    897   if (Node->BackLink != &mSmmMemoryMap) {
    898     Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
    899     if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
    900       return EFI_INVALID_PARAMETER;
    901     }
    902   }
    903 
    904   Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
    905   Pages->NumberOfPages = NumberOfPages;
    906   InsertTailList (Node, &Pages->Link);
    907 
    908   if (Pages->Link.BackLink != &mSmmMemoryMap) {
    909     Pages = InternalMergeNodes (
    910               BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
    911               );
    912   }
    913 
    914   if (Node != &mSmmMemoryMap) {
    915     InternalMergeNodes (Pages);
    916   }
    917 
    918   //
    919   // Update SmmMemoryMap here.
    920   //
    921   ConvertSmmMemoryMapEntry (EfiConventionalMemory, Memory, NumberOfPages, AddRegion);
    922   if (!AddRegion) {
    923     CoreFreeMemoryMapStack();
    924   }
    925 
    926   return EFI_SUCCESS;
    927 }
    928 
    929 /**
    930   Frees previous allocated pages.
    931 
    932   @param[in]  Memory                 Base address of memory being freed.
    933   @param[in]  NumberOfPages          The number of pages to free.
    934 
    935   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
    936   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
    937   @return EFI_SUCCESS            Pages successfully freed.
    938 
    939 **/
    940 EFI_STATUS
    941 EFIAPI
    942 SmmInternalFreePages (
    943   IN EFI_PHYSICAL_ADDRESS  Memory,
    944   IN UINTN                 NumberOfPages
    945   )
    946 {
    947   return SmmInternalFreePagesEx (Memory, NumberOfPages, FALSE);
    948 }
    949 
    950 /**
    951   Frees previous allocated pages.
    952 
    953   @param  Memory                 Base address of memory being freed.
    954   @param  NumberOfPages          The number of pages to free.
    955 
    956   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
    957   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
    958   @return EFI_SUCCESS            Pages successfully freed.
    959 
    960 **/
    961 EFI_STATUS
    962 EFIAPI
    963 SmmFreePages (
    964   IN EFI_PHYSICAL_ADDRESS  Memory,
    965   IN UINTN                 NumberOfPages
    966   )
    967 {
    968   EFI_STATUS  Status;
    969 
    970   Status = SmmInternalFreePages (Memory, NumberOfPages);
    971   if (!EFI_ERROR (Status)) {
    972     SmmCoreUpdateProfile (
    973       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
    974       MemoryProfileActionFreePages,
    975       EfiMaxMemoryType,
    976       EFI_PAGES_TO_SIZE (NumberOfPages),
    977       (VOID *) (UINTN) Memory,
    978       NULL
    979       );
    980   }
    981   return Status;
    982 }
    983 
    984 /**
    985   Add free SMRAM region for use by memory service.
    986 
    987   @param  MemBase                Base address of memory region.
    988   @param  MemLength              Length of the memory region.
    989   @param  Type                   Memory type.
    990   @param  Attributes             Memory region state.
    991 
    992 **/
    993 VOID
    994 SmmAddMemoryRegion (
    995   IN  EFI_PHYSICAL_ADDRESS  MemBase,
    996   IN  UINT64                MemLength,
    997   IN  EFI_MEMORY_TYPE       Type,
    998   IN  UINT64                Attributes
    999   )
   1000 {
   1001   UINTN  AlignedMemBase;
   1002 
   1003   //
   1004   // Add EfiRuntimeServicesData for memory regions that is already allocated, needs testing, or needs ECC initialization
   1005   //
   1006   if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
   1007     Type = EfiRuntimeServicesData;
   1008   } else {
   1009     Type = EfiConventionalMemory;
   1010   }
   1011 
   1012   DEBUG ((DEBUG_INFO, "SmmAddMemoryRegion\n"));
   1013   DEBUG ((DEBUG_INFO, "  MemBase    - 0x%lx\n", MemBase));
   1014   DEBUG ((DEBUG_INFO, "  MemLength  - 0x%lx\n", MemLength));
   1015   DEBUG ((DEBUG_INFO, "  Type       - 0x%x\n", Type));
   1016   DEBUG ((DEBUG_INFO, "  Attributes - 0x%lx\n", Attributes));
   1017 
   1018   //
   1019   // Align range on an EFI_PAGE_SIZE boundary
   1020   //
   1021   AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
   1022   MemLength -= AlignedMemBase - MemBase;
   1023   if (Type == EfiConventionalMemory) {
   1024     SmmInternalFreePagesEx (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
   1025   } else {
   1026     ConvertSmmMemoryMapEntry (EfiRuntimeServicesData, AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
   1027   }
   1028 
   1029   CoreFreeMemoryMapStack ();
   1030 }
   1031 
   1032 /**
   1033   This function returns a copy of the current memory map. The map is an array of
   1034   memory descriptors, each of which describes a contiguous block of memory.
   1035 
   1036   @param[in, out]  MemoryMapSize          A pointer to the size, in bytes, of the
   1037                                           MemoryMap buffer. On input, this is the size of
   1038                                           the buffer allocated by the caller.  On output,
   1039                                           it is the size of the buffer returned by the
   1040                                           firmware  if the buffer was large enough, or the
   1041                                           size of the buffer needed  to contain the map if
   1042                                           the buffer was too small.
   1043   @param[in, out]  MemoryMap              A pointer to the buffer in which firmware places
   1044                                           the current memory map.
   1045   @param[out]      MapKey                 A pointer to the location in which firmware
   1046                                           returns the key for the current memory map.
   1047   @param[out]      DescriptorSize         A pointer to the location in which firmware
   1048                                           returns the size, in bytes, of an individual
   1049                                           EFI_MEMORY_DESCRIPTOR.
   1050   @param[out]      DescriptorVersion      A pointer to the location in which firmware
   1051                                           returns the version number associated with the
   1052                                           EFI_MEMORY_DESCRIPTOR.
   1053 
   1054   @retval EFI_SUCCESS            The memory map was returned in the MemoryMap
   1055                                  buffer.
   1056   @retval EFI_BUFFER_TOO_SMALL   The MemoryMap buffer was too small. The current
   1057                                  buffer size needed to hold the memory map is
   1058                                  returned in MemoryMapSize.
   1059   @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.
   1060 
   1061 **/
   1062 EFI_STATUS
   1063 EFIAPI
   1064 SmmCoreGetMemoryMap (
   1065   IN OUT UINTN                  *MemoryMapSize,
   1066   IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,
   1067   OUT UINTN                     *MapKey,
   1068   OUT UINTN                     *DescriptorSize,
   1069   OUT UINT32                    *DescriptorVersion
   1070   )
   1071 {
   1072   UINTN                    Count;
   1073   LIST_ENTRY               *Link;
   1074   MEMORY_MAP               *Entry;
   1075   UINTN                    Size;
   1076   UINTN                    BufferSize;
   1077 
   1078   Size = sizeof (EFI_MEMORY_DESCRIPTOR);
   1079 
   1080   //
   1081   // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will
   1082   // prevent people from having pointer math bugs in their code.
   1083   // now you have to use *DescriptorSize to make things work.
   1084   //
   1085   Size += sizeof(UINT64) - (Size % sizeof (UINT64));
   1086 
   1087   if (DescriptorSize != NULL) {
   1088     *DescriptorSize = Size;
   1089   }
   1090 
   1091   if (DescriptorVersion != NULL) {
   1092     *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;
   1093   }
   1094 
   1095   Count = GetSmmMemoryMapEntryCount ();
   1096   BufferSize = Size * Count;
   1097   if (*MemoryMapSize < BufferSize) {
   1098     *MemoryMapSize = BufferSize;
   1099     return EFI_BUFFER_TOO_SMALL;
   1100   }
   1101 
   1102   *MemoryMapSize = BufferSize;
   1103   if (MemoryMap == NULL) {
   1104     return EFI_INVALID_PARAMETER;
   1105   }
   1106 
   1107   ZeroMem (MemoryMap, BufferSize);
   1108   Link = gMemoryMap.ForwardLink;
   1109   while (Link != &gMemoryMap) {
   1110     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
   1111     Link  = Link->ForwardLink;
   1112 
   1113     MemoryMap->Type           = Entry->Type;
   1114     MemoryMap->PhysicalStart  = Entry->Start;
   1115     MemoryMap->NumberOfPages  = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);
   1116 
   1117     MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
   1118   }
   1119 
   1120   return EFI_SUCCESS;
   1121 }
   1122