UEFI.源码分析.DXE的内存服务.第二部分.函数接口

Posted 木艮氵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UEFI.源码分析.DXE的内存服务.第二部分.函数接口相关的知识,希望对你有一定的参考价值。

  • 源码EDK2:Tianocore
  • UEFI源码分析系列第二篇,DXE阶段的内存服务
  • 第二部分,函数接口
  • DXE阶段源码目录MdeModulePkg/Core/Dxe
  • 首先我们来分析上一节的遗留问题,即函数CoreAddRange
  • 之后我们分析一下其余的接口,如申请内存、释放内存等
  • 最后我们从一个整体的视角来看整个内存服务

初始化流程

首先我们回顾一下整个内存初始化的过程。

DxeMain中调用如下函数开始初始化内存服务。

/** /Dxe/DxeMain/DxeMain.c **/
272   CoreInitializeMemoryServices (&HobStart, &MemoryBaseAddress, &MemoryLength);

这个函数好一顿找,最后找到一块可用的内存空间,然后调用如下函数。

/** /Dxe/Gcd/Gcd.c **/
2288   CoreAddMemoryDescriptor (
2289     EfiConventionalMemory,
2290     BaseAddress,
2291     RShiftU64 (Length, EFI_PAGE_SHIFT),
2292     Capabilities
2293     );

最后该函数有是检查、转换和测试,最后把工作扔给来下面这个函数

/** /Dxe/Mem/Page.c **/
 550   CoreAcquireMemoryLock ();                                    
 551   End = Start + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) - 1; 
 552   CoreAddRange (Type, Start, End, Attribute);                  
 553   CoreFreeMemoryMapStack ();                                   
 554   CoreReleaseMemoryLock ();

函数CoreFreeMemoryMapStack及其涉及到的变量与整个初始化过程息息相关,后续分析中会看到。


内存初始化CoreAddRange

此函数接收四个参数:

  • Type,类型为EFI_MEMORY_TYPE,根据传参来看值是EfiConventionalMemory
  • Start,内存空间起始地址
  • End,内存空间的终止地址,注意到其值为Start + Length - 1
  • Attribute,内存属性掩码,这部分不作重点,只在遇到的时候指明,感兴趣的还请自行挖掘吧~
/** /Dxe/Mem/Page.c **/
 163 VOID                                   
 164 CoreAddRange (                         
 165   IN EFI_MEMORY_TYPE          Type,    
 166   IN EFI_PHYSICAL_ADDRESS     Start,   
 167   IN EFI_PHYSICAL_ADDRESS     End,     
 168   IN UINT64                   Attribute
 169   )                                    
 170                                       
 /** 添加一块可用的内存区域 **/
 259   return ; 
 260           

额外看一下对Type域的描述,即空闲的内存区域

/** /MdePkg/Include/Uefi/UefiMultiPhase.h **/
 22 typedef enum 
 ......
 55   ///                                                                                 
 56   /// Free (unallocated) memory.                                                      
 57   ///                                                                                 
 58   EfiConventionalMemory, 
 ......
  91  EFI_MEMORY_TYPE;

1)一些基础的检查工作

是否页对齐,高地址应当大于低地址,锁有没有加上

/** /Dxe/Mem/Page.c **/
 174   ASSERT ((Start & EFI_PAGE_MASK) == 0);                              
 175   ASSERT (End > Start) ;                                              
 176                                                                       
 177   ASSERT_LOCKED (&gMemoryLock);                                       
 178                                                                       
 179   DEBUG ((DEBUG_PAGE, "AddRange: %lx-%lx to %d\\n", Start, End, Type));

2) 若包含了从地址零开始的一页,则置为零

/** /Dxe/Mem/Page.c **/
 190   if (Type == EfiConventionalMemory && Start == 0 && (End >= EFI_PAGE_SIZE - 1)) 
 191     SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0);                              
 192                                                                                  

3) 全局变量mMemoryMapKey增加1

/** /Dxe/Mem/Page.c **/
  31 //                                   
  32 // MemoryMap - The current memory map
  33 //                                   
  34 UINTN     mMemoryMapKey = 0;         

 197   mMemoryMapKey += 1;

4)触发有关Memory Map Change的事件

这里本质上是根据EventGroupGUID来唤醒所有有关的事件,由于异步事件机制上基于时钟中断的,而现在已经拿到了内存锁(即禁中断),故这些事件只是从未触发状态转换到了触发状态,而并不会执行。

真正的执行会在锁释放后(开中断),时钟中断处理程序来执行这些被唤醒的事件。不过也有例外的可能,就是其他地方调用了TPL的提升和降低。具体机制参阅异步事件处理一节。

/** /Dxe/Mem/Page.c **/
 208   CoreNotifySignalList (&gEfiEventMemoryMapChangeGuid);

顺便提一下这一步是UEFI 2.0规范才有的,UEFI2.0规范是Intel把EFI 1.0规范交给UEFI Form后出的规范,原本的1.0规范里并没有这一步。


5) 修正全局变量gMemoryMap

这里出现了一个很关键的全局变量gMemoryMap

/** /Dxe/Mem/MemData.c **/
 23 //                                                                         
 24 // MemoryMap - the current memory map                                      
 25 //                                                                         
 26 LIST_ENTRY        gMemoryMap  = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap);

不过先不着急看它的详细定义,先看来看这里做了什么事情

遍历gMemoryMap这个链表里的每一个Entry,类型为MEMORY_MAP。宏CR用于从链表结构获得到数据结构,并包含一个签名检查的操作。

/** /Dxe/Mem/Page.c **/
 218   Link = gMemoryMap.ForwardLink;                              
 219   while (Link != &gMemoryMap)                                
 220     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
 221     Link  = Link->ForwardLink;                                

跳过不一致的内存,包括类型不一致、属性不一致

 223     if (Entry->Type != Type)           
 224       continue;                         
 225                                        
 226                                         
 227     if (Entry->Attribute != Attribute) 
 228       continue;                         
 229                                        

若存在Entry与新加入的StartEnd相连接,则修改StartEnd将二者合二为一,并把Entry从链表中删除

 231     if (Entry->End + 1 == Start)        
 232                                          
 233       Start = Entry->Start;              
 234       RemoveMemoryMapEntry (Entry);      
 235                                          
 236      else if (Entry->Start == End + 1) 
 237                                          
 238       End = Entry->End;                  
 239       RemoveMemoryMapEntry (Entry);      
 240                                         

可以看到gMemoryMap保存来所有内存区域的信息,如果新加入的区域与某个区域相连,则合并。

类型MEMORY_MAP定义于/Dxe/Mem/Imem.h

/** /Dxe/Mem/Imem.h **/
 38 #define MEMORY_MAP_SIGNATURE   SIGNATURE_32('m','m','a','p')
 39 typedef struct                                             
 40   UINTN           Signature;                                
 41   LIST_ENTRY      Link;                                     
 42   BOOLEAN         FromPages;                                
 43                                                             
 44   EFI_MEMORY_TYPE Type;                                     
 45   UINT64          Start;                                    
 46   UINT64          End;                                      
 47                                                             
 48   UINT64          VirtualStart;                             
 49   UINT64          Attribute;                                
 50  MEMORY_MAP;                                               

包含必要的TypeStartEnd、Attribute

FromPages表示该区域是否来自某个页内,在后面的函数AllocateMemoryMapEntry中可以看到相关的处理。

VirtualStart还不知道干嘛的,后续看看能不能分析到。


6)通过临时内存区域mMapStack转换到gMemoryMap

 247   mMapStack[mMapDepth].Signature     = MEMORY_MAP_SIGNATURE;
 248   mMapStack[mMapDepth].FromPages     = FALSE;              
 249   mMapStack[mMapDepth].Type          = Type;                
 250   mMapStack[mMapDepth].Start         = Start;               
 251   mMapStack[mMapDepth].End           = End;                 
 252   mMapStack[mMapDepth].VirtualStart  = 0;                   
 253   mMapStack[mMapDepth].Attribute     = Attribute;           
 254   InsertTailList (&gMemoryMap, &mMapStack[mMapDepth].Link); 
 255                                       
 256   mMapDepth += 1;                     
 257   ASSERT (mMapDepth < MAX_MAP_DEPTH); 
 259   return ; 
 260           

之所以要使用临时内存区域mMapStack,是因为,我们的内存服务还没有初始化好呢。mMapStack本质上是全局变量,在编译时期分配到数据段中。

/** /Dxe/Mem/Page.c **/
  36 #define MAX_MAP_DEPTH 6                                                  
  37                                                                          
  38 ///                                                                      
  39 /// mMapDepth - depth of new descriptor stack                            
  40 ///                                                                      
  41 UINTN         mMapDepth = 0;                                             
  42 ///                                                                      
  43 /// mMapStack - space to use as temp storage to build new map descriptors
  44 ///                                                                      
  45 MEMORY_MAP    mMapStack[MAX_MAP_DEPTH];                                  
  46 UINTN         mFreeMapStack = 0;                                         

全局变量mMapDepth相当于栈顶指针,全局变量mFreeMapStack标记是否经过了CoreFreeMemoryMapStack的操作。


7)CoreFreeMemoryMapStack

此时轮到我们这个息息相关的函数出场了,同样需要在锁的持有下操作

/** /Dxe/Mem/Page.c **/
 318 VOID                            
 319 CoreFreeMemoryMapStack (        
 320   VOID                          
 321   )                             
 322                                
 323   MEMORY_MAP      *Entry;       
 324   MEMORY_MAP      *Entry2;      
 325   LIST_ENTRY      *Link2;       
 326                                 
 327   ASSERT_LOCKED (&gMemoryLock); 

检查并标记全局变量mFreeMapStack

 332   if (mFreeMapStack != 0) 
 333     return ;               
 334                           
 339   mFreeMapStack += 1;

依次处理保存在栈中的MEMORY_MAP

 341   while (mMapDepth != 0)             
 342     //                                
 343     // Deque an memory map entry from 
 344     //                                
 345     Entry = AllocateMemoryMapEntry ();
 346                                       
 347     ASSERT (Entry);                   
 348                                       
 349     //                                
 350     // Update to proper entry         
 351     //                                
 352     mMapDepth -= 1;                   

这里调用了AllocateMemoryMapEntry来获取一个Entry,这个函数需要使用gMemoryMap里的内存区域来申请到一个Entry。注意到6)中入栈的时候,也把入栈的MemoryMap链接到了gMemoryMap中,所以此时AllocateMemoryMapEntry是可用的。

后面会详细解释一下此时AllocateMemoryMapEntry的执行情况。

 354     if (mMapStack[mMapDepth].Link.ForwardLink != NULL)                                       
 355                                                                                               
 356       //                                                                                      
 357       // Move this entry to general memory                                                    
 358       //                                                                                      
 359       RemoveEntryList (&mMapStack[mMapDepth].Link);                                           
 360       mMapStack[mMapDepth].Link.ForwardLink = NULL;                                           
 361                                                                                               
 362       CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP));                           
 363       Entry->FromPages = TRUE;                                                                
 364                                                                                               
 365       //                                                                                      
 366       // Find insertion location                                                              
 367       //                                                                                      
 368       for (Link2 = gMemoryMap.ForwardLink; Link2 != &gMemoryMap; Link2 = Link2->ForwardLink) 
 369         Entry2 = CR (Link2, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);                          
 370         if (Entry2->FromPages && Entry2->Start > Entry->Start)                               
 371           break;                                                                              
 372                                                                                              
 373                                                                                              
 374                                                                                               
 375       InsertTailList (Link2, &Entry->Link);                                                   
 376                                                                                               
 377      else                                                                                   
 378       //                                                                                      
 379       // This item of mMapStack[mMapDepth] has already been dequeued from gMemoryMap list,    
 380       // so here no need to move it to memory.                                                
 381       //                                                                                      
 382       InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);                                
 383      // end if else
 384    // end while
 385                      
 386   mFreeMapStack -= 1;
 387  // end function CoreFreeMemoryMapStack

对于栈中mMapStack[mMapDepth]Link不为空的情况(对于当前情况,该mMapStack[mMapDepth]是被链接到gMemoryMap中),首先断开Link,然后把数据拷贝到申请到的Entry中,随后把Entry加入到gMemoryMap里。

(这一步是否多此一举呢,其实并没有,由于mMapStack[mMapDepth]被链接到gMemoryMap中,所以AllocateMemoryMapEntry实际上会修改mMapStack

对于Link为空的情况,则将Entry直接插入全局链表mFreeMemoryMapEntryList,显然该全局变量是用来维护可用于MEMORY_MAP的内存区域的,也就是说这个链表中的元素都是一个一个的空的MEMORY_MAP结构体,可以直接拿来用。

即在mMapStack[mMapDepth]Link 为空的情况下,通过AllocateMemoryMapEntry申请的Entry又插回了mFreeMemoryMapEntryList

  50 LIST_ENTRY   mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);

再看AllocateMemoryMapEntry()函数,它申请一个MEMORY_MAPEntry

/** /Dxe/Mem/Page.c **/
 275 MEMORY_MAP *            
 276 AllocateMemoryMapEntry (
 277   VOID                  
 278   )                     
 279                       
 280   MEMORY_MAP*            FreeDescriptorEntries; 
 281   MEMORY_MAP*            Entry;                 
 282   UINTN                  Index;                 

首先检查mFreeMemoryMapEntryList是否为空,若为空则申请一块内存,通过函数CoreAllocatePoolPages(),申请到一页大小的空间,这块空间又被划分成了数个MEMORY_MAP结构体,链接到了mFreeMemoryMapEntryList中。

哎不对啊,CoreAllocatePoolPages()难道已经可用了吗。是的没错,因为gMemoryMap中已经有了一块可用的内存区域了。不过此时的申请还与一般情况下的申请不太相同哦。

 284   if (IsListEmpty (&mFreeMemoryMapEntryList))   
 285     // 
 286     // The list is empty, to allocate one page to refuel the list                                 
 287     // 
 288     FreeDescriptorEntries = CoreAllocatePoolPages (EfiBootServicesData,                           
 289                               EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION_GRANULARITY),            
 290                               DEFAULT_PAGE_ALLOCATION_GRANULARITY);                               
 291     if (FreeDescriptorEntries != NULL)                                                           
 292       // 
 293       // Enque the free memmory map entries into the list                                         
 294       // 
 295       for (Index = 0; Index < DEFAULT_PAGE_ALLOCATION_GRANULARITY / sizeof(MEMORY_MAP); Index++) 
 296         FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;                            
 297         InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);            
 298       
 299      else  
 300       return NULL; 
 301     
 302   

最后函数AllocateMemoryMapEntry()直接返回mFreeMemoryMapEntryList中的一个Entry

 306   Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
 307   RemoveEntryList (&Entry->Link);                                                          
 308                                                                                            
 309   return Entry;                                                                            
 310 

哎,有点晕了吗。到底mMapStackgMemoryMapCoreAllocatePoolPages()之间是如何作用的呢,当mMapStack中的一个元素同时存在于gMemoryMap中,申请内存的时候又是如何操作的呢。

在这里给一个简略的描述的话,大概是这样的一个过程,但首先给一个定义以帮助描述,这里称动态内存空间为先前通过HOB数据搜索所得到的内存空间,称静态内存空间mMapStack全局变量在编译时期所确定的数据段的一段内存空间。

  1. 初始条件是mMapStack中存在一项MEMORY_MAP,该项同时也链接到了gMemoryMap。此项描述了一块可用的内存空间,在整个初始化过程中来说,也就是动态内存空间
  2. 函数CoreFreeMemoryMapStack的意图是把mMapStack中的项转移到gMemoryMap中,而mMapStack本质上是临时内存,所以CoreFreeMemoryMapStack本质上也就是把保存在静态内存空间中的Entry转移到动态内存空间
  3. 函数AllocateMemoryMapEntry需要检查全局变量mFreeMemoryMapEntryList来获取可用的Entry,第一次运行时该全局变量必然为空,所以需要通过CoreAllocatePoolPages来获取一段动态内存空间
  4. 函数CoreAllocatePoolPages本质上是通过gMemoryMap来获取可用的内存的。由于在第1步中gMemoryMap已经链接到来一项Entry,而这项Entry是位于静态内存空间,该EntryFromPages标记为FALSE,于是在根据gMemoryMap中的这一项Entry来分配动态内存的时候,最终会使得这项Entry分裂成两项,一项Entry0描述剩余的动态内存空间,一项Entry1描述分配出的一页大小的动态内存空间。最终的情况就是,mMapStack中存在两项即Entry0Entry1,这两项也都链接到gMemoryMap中。CoreAllocatePoolPages最终返回一个地址,该地址是Entry1描述的动态内存空间的基地址。
  5. 回到函数AllocateMemoryMapEntry,在通过CoreAllocatePoolPages获得可用的一页动态内存后,会按照sizeof(MEMORY_MAP)的大小将这一页空间划分成若干个Entry保存到mFreeMemoryMapEntryList。此时这些Entry本身的数据实际是位于动态内存空间,其属性FromPages被标记为TRUE,不过这些Entry并没有描述某一段内存空间,其本身的数据都是空值。此时`mMapStack中的Entry0Entry1自身的数据仍然位于静态内存空间中,但这两项所描述的内存空间是动态内存空间
  6. 回到函数CoreFreeMemoryMapStack,在通过AllocateMemoryMapEntry获取到一个位于动态内存空间Entry后,便把静态内存空间(即mMapStack)中的一项数据拷贝进去。此时的Entry本身位于动态内存空间,而且描述了一段动态内存。
  7. mMapStack中的所有项都这样复制出来后,mMapStack便为空,gMemoryMap便包含若干个Entry,这些Entry描述了一段动态内存。可见此时这段动态内存里有一页的空间是用来存放这些Entry本身的。

常用函数接口

BootServices的初始化可见,最常用的关于内存申请的函数有四个。

/** /Dxe/DxeMain/DxeMain.c **/
 57   (EFI_ALLOCATE_PAGES)                          CoreAllocatePages, 
 58   (EFI_FREE_PAGES)                              CoreFreePages,     
 60   (EFI_ALLOCATE_POOL)                           CoreAllocatePool, 
 61   (EFI_FREE_POOL)                               CoreFreePool,     

由于代码中充斥着各种状态检查,故以后会去掉大部分无关紧要的检查代码,必要的标记检查由于可能对执行流程产生影响,所有会保留。


CoreAllocatePages

函数原型

/** /Dxe/Mem/Page.c **/
1339 EFI_STATUS                                
1340 EFIAPI                                    
1341 CoreAllocatePages (                       
1342   IN  EFI_ALLOCATE_TYPE     Type,         
1343   IN  EFI_MEMORY_TYPE       MemoryType,   
1344   IN  UINTN                 NumberOfPages,
1345   OUT EFI_PHYSICAL_ADDRESS  *Memory       
1346   )                                       

直接转到另一个函数。这里额外的工作是Profile的更新。这里就不分析了。

/** /Dxe/Mem/Page.c **/
1350   Status = CoreInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory); 
1351   if (!EFI_ERROR (Status))                                                     
1352     CoreUpdateProfile (                                                         
1353       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),                        
1354       MemoryProfileActionAllocatePages,                                         
1355       MemoryType,                                                               
1356       EFI_PAGES_TO_SIZE (NumberOfPages),                                        
1357       (VOID *) (UINTN) *Memory,                                                 
1358       NULL                                                                      
1359       );                                                                        
1360     InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);                
1361     ApplyMemoryProtectionPolicy (EfiConventionalMemory, MemoryType, *Memory,    
1362       EFI_PAGES_TO_SIZE (NumberOfPages));                                       
1363                                                                                

函数CoreInternalAllocatePages

/** /Dxe/Mem/Page.c **/
1207 EFI_STATUS                                 
1208 EFIAPI                                     
1209 CoreInternalAllocatePages (                
1210   IN EFI_ALLOCATE_TYPE      Type,          
1211   IN EFI_MEMORY_TYPE        MemoryType,    
1212   IN UINTN                  NumberOfPages, 
1213   IN OUT EFI_PHYSICAL_ADDRESS  *Memory     
1214   )                                        

这里的EFI_ALLOCATE_TYPE定义在UefiSpec.h

/** /MdePkg/Include/Uefi/UefiSpec.h **/
  29 ///
  30 /// Enumeration of EFI memory allocation types.
  31 ///
  32 typedef enum 
  33   ///
  34   /// Allocate any available range of pages that satisfies the request.
  35   ///
  36   AllocateAnyPages,
  37   ///
  38   /// Allocate any available range of pages whose uppermost address is less than
  39   /// or equal to a specified maximum address.
  40   ///
  41   AllocateMaxAddress,
  42   ///
  43   /// Allocate pages at a specified address.
  44   ///
  45   AllocateAddress,
  46   ///
  47   /// Maximum enumeration value that may be used for bounds checking.
  48   ///
  49   MaxAllocateType
  50  EFI_ALLOCATE_TYPE;

常用的即AllocateAnyPages申请页,AllocateAddress在指定地址处申请内存。

设置页对齐。

/** /Dxe/Mem/Page.c **/
1236   Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;  
1237                                                     
1238   if  (MemoryType == EfiACPIReclaimMemory   ||      
1239        MemoryType == EfiACPIMemoryNVS       ||      
1240        MemoryType == EfiRuntimeServicesCode ||      
1241        MemoryType == EfiRuntimeServicesData)       
1242                                                     
1243     Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
1244                                                    

这里的宏DEFAULT_PAGE_ALLOCATION_GRANULARITYRUNTIME_PAGE_ALLOCATION_GRANULARITY均是架构相关,通过grep命令可以查到。

/edk2 (UDK2017*) $ grep "DEFAULT_PAGE_ALLOCATION_GRANULARITY" -Rn
MdePkg/Include/AArch64/ProcessorBind.h:110:#define DEFAULT_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/Arm/ProcessorBind.h:116:#define DEFAULT_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/Ipf/ProcessorBind.h:254:#define DEFAULT_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/Ia32/ProcessorBind.h:263:#define DEFAULT_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/Ebc/ProcessorBind.h:120:#define DEFAULT_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/X64/ProcessorBind.h:277:#define DEFAULT_PAGE_ALLOCATION_GRANULARITY   (0x1000)

/edk2 (UDK2017*) $ grep "RUNTIME_PAGE_ALLOCATION_GRANULARITY" -Rn
MdePkg/Include/AArch64/ProcessorBind.h:111:#define RUNTIME_PAGE_ALLOCATION_GRANULARITY   (0x10000)
MdePkg/Include/Arm/ProcessorBind.h:117:#define RUNTIME_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/Ipf/ProcessorBind.h:255:#define RUNTIME_PAGE_ALLOCATION_GRANULARITY   (0x2000)
MdePkg/Include/Ia32/ProcessorBind.h:264:#define RUNTIME_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/Ebc/ProcessorBind.h:121:#define RUNTIME_PAGE_ALLOCATION_GRANULARITY   (0x1000)
MdePkg/Include/X64/ProcessorBind.h:278:#define RUNTIME_PAGE_ALLOCATION_GRANULARITY   (0x1000)

根据页对齐,调整NumberOfPages到正确的页数

1252   NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;   
1253   NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);

在获得锁之后开始申请内存

1258   Start = *Memory;

1263   MaxAddress = MAX_ADDRESS;

1298   if (Type != AllocateAddress)                                              
1299     Start = FindFreePages (MaxAddress, NumberOfPages, MemoryType, Alignment);
1300     if (Start == 0)                                                         
1301       Status = EFI_OUT_OF_RESOURCES;                                         
1302       goto Done;                                                             
1303                                                                             
1304                                                                        

1309   Status = CoreConvertPages (Start, NumberOfPages, MemoryType);     

寻找空闲页FindFreePages只是返回所申请内存的基地址,函数CoreConvertPages则是对申请的内存空间的管理数据进行操作,使其描述申请后的内存分布。

/** /Dxe/Mem/Page.c **/
1125 UINT64                              
1126 FindFreePages (                     
1127     IN UINT64           MaxAddress, 
1128     IN UINT64           NoPages,    
1129     IN EFI_MEMORY_TYPE  NewType,    
1130     IN UINTN            Alignment   
1131     )                               

这边会首先根据NewTypemMemoryTypeStatistics中所指定的位置寻找,mMemoryTypeStatistics的设置是在初始化时候的申请内存测试的时候,理论上应当足以满足所有的申请。

1139     Start = CoreFindFreePagesI (                            
1140               mMemoryTypeStatistics[NewType].MaximumAddress,
1141               mMemoryTypeStatistics[NewType].BaseAddress,   
1142               NoPages,                                      
1143               NewType,                                      
1144               Alignment                                     
1145               );                                            

如果找不到,则到默认的区域寻找

1155     Start = CoreFindFreePagesI (mDefaultMaximumAddress, 0, NoPages, NewType, Alignment); 

若还找不到,则取任何地方寻找都行啦

1170   Start = CoreFindFreePagesI (MaxAddress, 0, NoPages, NewType, Alignment);

最后如果还找不到,则重新执行该函数,相当于无限循环直到找到

1185   return FindFreePages (MaxAddress, NoPages, NewType, Alignment); 

函数CoreFindFreePagesI在一段特定的内存空间中寻找空闲页

/** /Dxe/Mem/Page.c **/
 993 UINT64                              
 994 CoreFindFreePagesI (                
 995   IN UINT64           MaxAddress,   
 996   IN UINT64           MinAddress,   
 997   IN UINT64           NumberOfPages,
 998   IN EFI_MEMORY_TYPE  NewType,      
 999   IN UINTN            Alignment     
1000   )                                 

获取单位为byte的申请大小

1036   NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);

遍历gMemoryMap链表的每一个Entry

1039   for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink)   
1040     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);                          

只寻找空闲的内存区域

1045     if (Entry->Type != EfiConventionalMemory) 
1046       continue;                                
1047                                               

对比空闲内存区域的位置是否符合需求,并在页对齐的要求检查大小是否满足

1049     DescStart = Entry->Start;
1050     DescEnd = Entry->End;    

1055     if ((DescStart >= MaxAddress) || (DescEnd < MinAddress)) 
1056       continue;                                               
1057                                                              

1062     if (DescEnd >= MaxAddress) 
1063       DescEnd = MaxAddress;     
1064                                

1066     DescEnd = ((DescEnd + 1) & (~(Alignment - 1))) - 1; 

1069     if (DescEnd < DescStart) 
1070       continue;               
1071                              

1077     DescNumberOfBytes = DescEnd - DescStart + 1;

1079     if (DescNumberOfBytes >= NumberOfBytes)            

1083       if ((DescEnd - NumberOfBytes + 1) < MinAddress)  
1084         continue;                                       
1085                                                        

1090       if (DescEnd > Target)                            
1091         Target = DescEnd;                               
1092                                                        

1093      // end if DescNumberOfBytes >= NumberOfBytes
1094    // end for each Entry in gMemoryMap

循环退出后,Target指向空闲内存区域的高地址,修正为所申请地址空间的基地址。

1099   Target -= NumberOfBytes - 1;

1108   return Target;
1109  // end function CoreFindFreePagesI

这之后需要CoreConvertPages来转换描述内存空间的数据。

/** /Dxe/Mem/Page.c **/
 941 EFI_STATUS                                                                 
 942 CoreConvertPages (                                                         
 943   IN UINT64           Start,                                               
 944   IN UINT64           NumberOfPages,                                       
 945   IN EFI_MEMORY_TYPE  NewType                                              
 946   )                                                                        
 947                                                                           
 948   return CoreConvertPagesEx(Start, NumberOfPages, TRUE, NewType, FALSE, 0);
 949                                                                           

 702 EFI_STATUS                                
 703 CoreConvertPagesEx (                      
 704   IN UINT64           Start,              
 705   IN UINT64           NumberOfPages,      
 706   IN BOOLEAN          ChangingType,       
 707   IN EFI_MEMORY_TYPE  NewType,            
 708   IN BOOLEAN          ChangingAttributes, 
 709   IN UINT64           NewAttributes       
 710   )                                       

Start即为前文CoreFindFreePagesI所返回的Target

计算的到End的值

 721   Entry = NULL;                                             
 722   NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);
 723   End = Start + NumberOfBytes - 1;                          

展开循环,寻找gMemoryMap中包含了所申请内存空间的Entry进行处理。

 739   while (Start < End)                                                                  
 740                                                                                         
 741     //                                                                                  
 742     // Find the entry that the covers the range                                         
 743     //                                                                                  
 744     for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) 
 745       Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);                        
 746                                                                                         
 747       if (Entry->Start <= Start && Entry->End > Start)                                 
 748         break;                                                                          
 749                                  

以上是关于UEFI.源码分析.DXE的内存服务.第二部分.函数接口的主要内容,如果未能解决你的问题,请参考以下文章

UEFI.源码分析.DXE的异步事件服务.第二部分.任务优先级

UEFI.源码分析.DXE的异步事件服务.第一部分.事件驱动

UEFI.源码分析.DXE的异步事件服务.第一部分.事件驱动

UEFI.源码分析.DXE的内存服务.第一部分.初始化

UEFI.源码分析.DXE的异步事件服务.第三部分.定时器与时钟中断

UEFI.源码分析.DXE的异步事件服务.第三部分.定时器与时钟中断