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

本文深入解析UEFI DXE阶段的内存服务,包括初始化流程、内存初始化及常用函数接口,如CoreAllocatePages、CoreFreePages、内存池管理。文章详细介绍了内存分配与释放的实现机制,并探讨了内存服务的整体视角。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 源码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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值