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

  • 源码EDK2:Tianocore
  • UEFI源码分析系列第二篇,DXE阶段的内存服务
  • 第一部分,内存服务的初始化过程
  • DXE阶段源码目录MdeModulePkg/Core/Dxe
  • 源码版本为UDK2017
  • 目前最新版2018代码在/Dxe/Mem/下多了HeapGuard.c这个文件,看名称应该是用来守护堆操作防止异常的。在UDK2017中并没有这个特性,所以我们的分析按照UDK2017的代码来。
  • 关于HeapGuard的讨论将列为单独一部分,敬请期待。

内存服务的初始化

在DxeMain中调用以下函数来初始化内存服务

/** /Dxe/DxeMain/DxeMain.c **/
269   //                                                                           
270   // Initialize Memory Services                                                
271   //                                                                           
272   CoreInitializeMemoryServices (&HobStart, &MemoryBaseAddress, &MemoryLength); 

该函数位于/Dxe/Gcd/Gcd.c

/** /Dxe/Gcd/Gcd.c **/
2054 EFI_STATUS                                     
2055 CoreInitializeMemoryServices (                 
2056   IN  VOID                  **HobStart,        
2057   OUT EFI_PHYSICAL_ADDRESS  *MemoryBaseAddress,
2058   OUT UINT64                *MemoryLength      
2059   )                                            
2060 {                                              
/** 进行内存服务初始化操作 **/
2295   *MemoryBaseAddress = BaseAddress;
2296   *MemoryLength      = Length;     
2297                                    
2298   return EFI_SUCCESS;              
2299 } 

1)转换HOB数据

1)把指向HOB数据的指针HobStart 转换成HOB格式的数据,本质就是把地址传给相应结构体的变量

HOB数据结构定义于/MdePkg/Include/Pi/PiHob.h

操作HOB数据的方法由库/MdePkg/Library/DxeHobLib 提供,该库的头文件位于/MdePkg/Include/Library/HobLib.h , 头文件中包含来几个操作HOB数据的宏定义,如GET_HOB_TYPE 宏来获取HOB类型。

/** /Dxe/Gcd/Gcd.c **/
2079   //                                                      
2080   // Point at the first HOB.  This must be the PHIT HOB.  
2081   //                                                      
2082   Hob.Raw = *HobStart;                                    
2083   ASSERT (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_HANDOFF);    

注意到变量Hob的类型是EFI_PEI_HOB_POINTERS,本质上是一个union类型的数据结构。

使用union最大的好处是提高源码的可读性和间接性。利用该类型中所有域的取值均一样的特点,在不同的使用方法下,可以使用不同的变量名字,一方面可以省去强制类型转换的麻烦,另一方面可以直接体现出当前的使用需求。

/** /MdePkg/Include/Pi/PiHob.h **/
458 ///                                                             
459 /// Union of all the possible HOB Types.                        
460 ///                                                             
461 typedef union {                                                 
462   EFI_HOB_GENERIC_HEADER              *Header;                  
463   EFI_HOB_HANDOFF_INFO_TABLE          *HandoffInformationTable; 
464   EFI_HOB_MEMORY_ALLOCATION           *MemoryAllocation;        
465   EFI_HOB_MEMORY_ALLOCATION_BSP_STORE *MemoryAllocationBspStore;
466   EFI_HOB_MEMORY_ALLOCATION_STACK     *MemoryAllocationStack;   
467   EFI_HOB_MEMORY_ALLOCATION_MODULE    *MemoryAllocationModule;  
468   EFI_HOB_RESOURCE_DESCRIPTOR         *ResourceDescriptor;      
469   EFI_HOB_GUID_TYPE                   *Guid;                    
470   EFI_HOB_FIRMWARE_VOLUME             *FirmwareVolume;          
471   EFI_HOB_FIRMWARE_VOLUME2            *FirmwareVolume2;         
472   EFI_HOB_CPU                         *Cpu;                     
473   EFI_HOB_MEMORY_POOL                 *Pool;                    
474   EFI_HOB_UEFI_CAPSULE                *Capsule;                 
475   UINT8                               *Raw;                     
476 } EFI_PEI_HOB_POINTERS;

2)初始化内存池

2)初始化内存池

/** /Dxe/Gcd/Gcd.c **/
2077   //                                                                   
2078   // Initialize the spin locks and maps in the memory services.        
2079   // Also fill in the memory services into the EFI Boot Services Table 
2080   //                                                                   
2081   CoreInitializePool ();

该函数的实现位于/Dxe/Mem/Pool.c

/** /Dxe/Mem/Pool.c **/
118 VOID                                                        
119 CoreInitializePool (                                        
120   VOID                                                      
121   )                                                         
122 {                                                           
123   UINTN  Type;                                              
124   UINTN  Index;                                             
125                                                             
126   for (Type=0; Type < EfiMaxMemoryType; Type++) {           
127     mPoolHead[Type].Signature  = 0;                         
128     mPoolHead[Type].Used       = 0;                         
129     mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE) Type;    
130     for (Index=0; Index < MAX_POOL_LIST; Index++) {         
131       InitializeListHead (&mPoolHead[Type].FreeList[Index]);
132     }                                                       
133   }                                                         
134 }                                                                                                                      

做的事情很简单,就是把全局变量mPoolHead 初始化,Pool 数据结构的定义也在Pool.c文件中。

/** /Dxe/Mem/Pool.c **/
 71 #define POOL_SIGNATURE  SIGNATURE_32('p','l','s','t') 
 72 typedef struct {                                      
 73     INTN             Signature;                       
 74     UINTN            Used;                            
 75     EFI_MEMORY_TYPE  MemoryType;                      
 76     LIST_ENTRY       FreeList[MAX_POOL_LIST];         
 77     LIST_ENTRY       Link;                            
 78 } POOL;                                               
 79                                                       
 80 //                                                    
 81 // Pool header for each memory type.                  
 82 //                                                    
 83 POOL            mPoolHead[EfiMaxMemoryType];          

全局变量mPoolHead对每种内存类型EFI_MEMORY_TYPE均有一组FreeList来维护可用的内存空间。

内存类型是以枚举类型定义于/MdePkg/Include/Uefi/UefiMultiPhase.h中。

/** /MdePkg/Include/Uefi/UefiMultiPhase.h **/
 19 ///                                                                                   
 20 /// Enumeration of memory types introduced in UEFI.                                   
 21 ///                                                                                   
 22 typedef enum {                                                                        
 23   ///                                                                                 
 24   /// Not used.                                                                       
 25   ///                                                                                 
 26   EfiReservedMemoryType,                                                              
 27   ///                                                                                 
 28   /// The code portions of a loaded application.                                      
 29   /// (Note that UEFI OS loaders are UEFI applications.)                              
 30   ///                                                                                 
 31   EfiLoaderCode,                                                                      
 32   ///                                                                                 
 33   /// The data portions of a loaded application and the default data allocation       
 34   /// type used by an application to allocate pool memory.                            
 35   ///                                                                                 
 36   EfiLoaderData,                                                                      
 37   ///                                                                                 
 38   /// The code portions of a loaded Boot Services Driver.                             
 39   ///                                                                                 
 40   EfiBootServicesCode,                                                                
 41   ///                                                                                 
 42   /// The data portions of a loaded Boot Serves Driver, and the default data          
 43   /// allocation type used by a Boot Services Driver to allocate pool memory.         
 44   ///                                                                                 
 45   EfiBootServicesData,                                                                
 46   ///                                                                                 
 47   /// The code portions of a loaded Runtime Services Driver.                          
 48   ///                                                                                 
 49   EfiRuntimeServicesCode,                                                             
 50   ///                                                                                 
 51   /// The data portions of a loaded Runtime Services Driver and the default           
 52   /// data allocation type used by a Runtime Services Driver to allocate pool memory. 
 53   ///                                                                                 
 54   EfiRuntimeServicesData,                                                             
 55   ///                                                                                 
 56   /// Free (unallocated) memory.                                                      
 57   ///                                                                                 
 58   EfiConventionalMemory,                                                              
 59   ///                                                                                 
 60   /// Memory in which errors have been detected.                                      
 61   ///                                                                                          
 62   EfiUnusableMemory,                                                                           
 63   ///                                                                                          
 64   /// Memory that holds the ACPI tables.                                                       
 65   ///                                                                                          
 66   EfiACPIReclaimMemory,                                                                        
 67   ///                                                                                          
 68   /// Address space reserved for use by the firmware.                                          
 69   ///                                                                                          
 70   EfiACPIMemoryNVS,                                                                            
 71   ///                                                                                          
 72   /// Used by system firmware to request that a memory-mapped IO region                        
 73   /// be mapped by the OS to a virtual address so it can be accessed by EFI runtime services.  
 74   ///                                                                                          
 75   EfiMemoryMappedIO,                                                                           
 76   ///                                                                                          
 77   /// System memory-mapped IO region that is used to translate memory                          
 78   /// cycles to IO cycles by the processor.                                                    
 79   ///                                                                                          
 80   EfiMemoryMappedIOPortSpace,                                                                  
 81   ///                                                                                          
 82   /// Address space reserved by the firmware for code that is part of the processor.           
 83   ///                                                                                          
 84   EfiPalCode,                                                                                  
 85   ///                                                                                          
 86   /// A memory region that operates as EfiConventionalMemory,                                  
 87   /// however it happens to also support byte-addressable non-volatility.                      
 88   ///                                                                                          
 89   EfiPersistentMemory,                                                                         
 90   EfiMaxMemoryType                                                                             
 91 } EFI_MEMORY_TYPE;                                                                             

3)获取内存类型信息

3) 获取HOB中关于内存类型的数据信息,如果存在,则拷贝到全局变量gMemoryTypeInformation中。

/** /Dxe/Gcd/Gcd.c **/
2115   //                                                                                                   
2116   // See if a Memory Type Information HOB is available                                                 
2117   //                                                                                                   
2118   GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid);                                          
2119   if (GuidHob != NULL) {                                                                               
2120     EfiMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob);                                            
2121     DataSize                 = GET_GUID_HOB_DATA_SIZE (GuidHob);                                       
2122     if (EfiMemoryTypeInformation != NULL && DataSize > 0 && DataSize <= (EfiMaxMemoryType + 1) * sizeof (EFI_MEMORY_TYPE_INFORMATION)) {
2123       CopyMem (&gMemoryTypeInformation, EfiMemoryTypeInformation, DataSize);                           
2124     }                              
2125   }                                

全局变量gMemoryTypeInformation定义于/Dxe/Mem/Page.c中。

/** /Dxe/Mem/Page.c **/
  75 EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = {
  76   { EfiReservedMemoryType,      0 },
  77   { EfiLoaderCode,              0 },
  78   { EfiLoaderData,              0 },
  79   { EfiBootServicesCode,        0 },
  80   { EfiBootServicesData,        0 },
  81   { EfiRuntimeServicesCode,     0 },
  82   { EfiRuntimeServicesData,     0 },
  83   { EfiConventionalMemory,      0 },
  84   { EfiUnusableMemory,          0 },
  85   { EfiACPIReclaimMemory,       0 },
  86   { EfiACPIMemoryNVS,           0 },
  87   { EfiMemoryMappedIO,          0 },
  88   { EfiMemoryMappedIOPortSpace, 0 },
  89   { EfiPalCode,                 0 },
  90   { EfiPersistentMemory,        0 },
  91   { EfiMaxMemoryType,           0 } 
  92 }; 

类型EFI_MEMORY_TYPE_INFORMATION则定义于/MdeModulePkg/Include/Guid/MemoryTpyeInformation.h中。

该类型EFI_MEMORY_TYPE_INFORMATION是对内存类型所占用页数的一个统计。注意到HOB数据是由PEI阶段传递而来,所有PEI阶段应该把每种内存类型所需的页数算好后存入HOB传递给DXE。

DXE找到该HOB后便拷贝出来到全局变量gMemoryTypeInformation中。

/* /MdeModulePkg/Include/Guid/MemoryTpyeInformation.h **/
 21 #ifndef __MEMORY_TYPE_INFORMATION_GUID_H__
 22 #define __MEMORY_TYPE_INFORMATION_GUID_H__
 23 
 24 #define EFI_MEMORY_TYPE_INFORMATION_GUID \
 25   { 0x4c19049f,0x4137,0x4dd3, { 0x9c,0x10,0x8b,0x97,0xa8,0x3f,0xfd,0xfa } }
 26 
 27 #define EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME L"MemoryTypeInformation"
 28 
 29 extern EFI_GUID gEfiMemoryTypeInformationGuid;
 30 
 31 typedef struct {
 32   UINT32  Type;             ///< EFI memory type defined in UEFI specification.
 33   UINT32  NumberOfPages;    ///< The pages of this type memory.
 34 } EFI_MEMORY_TYPE_INFORMATION;

4)计算所需资源大小

4) 计算所需的内存资源

/** /Dxe/Gcd/Gcd.c **/
2127   //
2128   // Include the total memory bin size needed to make sure memory bin could be allocated successfully.
2129   //
2130   MinimalMemorySizeNeeded = MINIMUM_INITIAL_MEMORY_SIZE + CalculateTotalMemoryBinSizeNeeded ();

其中MINIMUM_INITIAL_MEMORY_SIZE定义于本文件

/** /Dxe/Gcd/Gcd.c **/
  20 #define MINIMUM_INITIAL_MEMORY_SIZE 0x10000

函数CalculateTotalMemoryBinSizeNeeded也定义于本文件,其遍历全局变量gMemoryTypeInformation来计算所需的内存资源数。

/** /Dxe/Gcd/Gcd.c **/
2020 UINT64
2021 CalculateTotalMemoryBinSizeNeeded (
2022   VOID
2023   )
2024 {
2025   UINTN     Index;
2026   UINT64    TotalSize;
2027
2028   //
2029   // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array
2030   //
2031   TotalSize = 0;
2032   for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
2033     TotalSize += LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT);
2034   }
2035
2036   return TotalSize;
2037 }

5)根据HOB寻找可用内存资源

5) 遍历所有的HOB数据,来获取可用的内存资源信息

/** /Dxe/Gcd/Gcd.c **/
2135   Found  = FALSE;
2136   for (Hob.Raw = *HobStart; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {

判断该HOB的类型,若不是EFI_HOB_TYPE_RESOURCE_DESCRIPTOR则跳过。

2140     if (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
2141       continue;                                                  
2142     }                                                            

如果是资源描述的HOB数据,则进一步判断是否是系统内存的资源描述,并且保证该内存区域是测试过的内存区域。

2147     ResourceHob = Hob.ResourceDescriptor;                                                      
2148     if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) {                             
2149       continue;                                                                                
2150     }                                                                                          
2151     if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) {
2152       continue;                                                                                
2153     }                                                                                          

进一步判断该内存区域是否包含了HOB信息头所描述的空闲内存区域。

HOB数据的最开始是一个信息头数据结构,即EFI_HOB_HANDOFF_INFO_TABLE,其指明了PEI阶段堆的内存空间的地址范围。

本次循环所寻找的HOB资源,其描述的内存空间需要包含HOB信息头所描述的可用内存资源

2103   PhitHob = Hob.HandoffInformationTable;

2158     if (PhitHob->EfiFreeMemoryBottom < ResourceHob->PhysicalStart) {                             
2159       continue;                                                                                  
2160     }                                                                                            
2161     if (PhitHob->EfiFreeMemoryTop > (ResourceHob->PhysicalStart + ResourceHob->ResourceLength)) {
2162       continue;                                                                                  
2163     }                                                                                            

找到则保存该HOB数据

2168     PhitResourceHob = ResourceHob;                                                      
2169     Found = TRUE;                                                                       

计算并比较不同可用区域的大小是否满足4)中的最小需求

2171     //                                                                                                      
2172     // Compute range between PHIT EfiMemoryTop and the end of the Resource Descriptor HOB                   
2173     //                                                                                                      
2174     Attributes  = PhitResourceHob->ResourceAttribute;                                                       
2175     BaseAddress = PageAlignAddress (PhitHob->EfiMemoryTop);                                                 
2176     Length      = PageAlignLength  (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - BaseAddress);
2177     if (Length < MinimalMemorySizeNeeded) {                                                                 
2178       //                                                                                                    
2179       // If that range is not large enough to intialize the DXE Core, then                                  
2180       // Compute range between PHIT EfiFreeMemoryBottom and PHIT EfiFreeMemoryTop                           
2181       //                                                                                                    
2182       BaseAddress = PageAlignAddress (PhitHob->EfiFreeMemoryBottom);                                        
2183       Length      = PageAlignLength  (PhitHob->EfiFreeMemoryTop - BaseAddress);                             
2184       if (Length < MinimalMemorySizeNeeded) {                                                               
2185         //                                                                                                  
2186         // If that range is not large enough to intialize the DXE Core, then                                
2187         // Compute range between the start of the Resource Descriptor HOB and the start of the HOB List     
2188         //                                                                                                  
2189         BaseAddress = PageAlignAddress (ResourceHob->PhysicalStart);                                        
2190         Length      = PageAlignLength  ((UINT64)((UINTN)*HobStart - BaseAddress));                          
2191       }                                                                                                     
2192     }                                                                                                       
2193     break;                                                                                                  

这边需要详细解释HOB数据的位置以及PEI阶段的堆空间,才有可能完全理解这段代码的含义。


6)若找不到则继续寻找

6) 若5)中找到的可用内存空间仍旧不满足最小的内存需求,则继续寻找其他空间

2207   if (Length < MinimalMemorySizeNeeded) {  
/** 寻找其他内存空间 **/
2266   } 

首先获取到地址上限

2074   EFI_PHYSICAL_ADDRESS               HighAddress;

2213     HighAddress = MAX_ADDRESS;

MAX_ADDRESS定义于MdePkg/Include/${arch}/ProcessorBind.h文件中。

搜索可以采用grep命令快速定位到寻找的内容,不过如果对源码结构比较熟悉,或想凭自己的理解摸索一下,建议直接到文件目录中寻找。往往我是没什么别的办法来才使用这个命令。

~/edk2 (UDK2017*) $ grep "#define MAX_ADDRESS" -Rn
MdePkg/Include/AArch64/ProcessorBind.h:94:#define MAX_ADDRESS   0xFFFFFFFFFFFFFFFFULL
MdePkg/Include/Arm/ProcessorBind.h:100:#define MAX_ADDRESS  0xFFFFFFFF
MdePkg/Include/Ipf/ProcessorBind.h:237:#define MAX_ADDRESS   0xFFFFFFFFFFFFFFFFULL
MdePkg/Include/Ia32/ProcessorBind.h:247:#define MAX_ADDRESS   0xFFFFFFFF
MdePkg/Include/Ebc/ProcessorBind.h:104:#define MAX_ADDRESS   ((UINTN) ~0)
MdePkg/Include/X64/ProcessorBind.h:261:#define MAX_ADDRESS   0xFFFFFFFFFFFFFFFFULL
grep: EmulatorPkg/Unix/Host/X11IncludeHack: 没有那个文件或目录
BaseTools/Source/C/Include/AArch64/ProcessorBind.h:96:#define MAX_ADDRESS  0xFFFFFFFFFFFFFFFF
BaseTools/Source/C/Include/Arm/ProcessorBind.h:94:#define MAX_ADDRESS  0xFFFFFFFF
BaseTools/Source/C/Include/Ia32/ProcessorBind.h:137:#define MAX_ADDRESS   0xFFFFFFFF
BaseTools/Source/C/Include/X64/ProcessorBind.h:156:#define MAX_ADDRESS   0xFFFFFFFFFFFFFFFFULL

遍历HOB数据,跳过5)中所找到的HOB,跳过不是资源描述的,跳过不是描述系统内存资源的,跳过内存资源是没有经过测试的

但是不要求包含HOB信息头描述的可用内存区域了

2214     for (Hob.Raw = *HobStart; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {

2218       if (Hob.ResourceDescriptor == PhitResourceHob) {
2219         continue;                                     
2220       }                                               

2224       if (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
2225         continue;                                                  
2226       }                                                            

2231       ResourceHob = Hob.ResourceDescriptor;                                                                
2232       if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) {                                       
2233         continue;                                                                                          
2234       }                                                                                                    
2235       if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) {          
2236         continue;                                                                                          
2237       }       

跳过最高地址超过了上限的HOB数据

2238       if ((ResourceHob->PhysicalStart + ResourceHob->ResourceLength) > (EFI_PHYSICAL_ADDRESS)MAX_ADDRESS) {
2239         continue;                                                                                          
2240       }                                                                                                    

跳过所描述地址空间低于 上一个遍历的HOB数据的地址空间 的HOB数据,for循环的最后会修改HighAddress的值。

2238       if ((ResourceHob->PhysicalStart + ResourceHob->ResourceLength) > (EFI_PHYSICAL_ADDRESS)MAX_ADDRESS) {
2239         continue;                                                                                          
2240       }

2264       HighAddress = ResourceHob->PhysicalStart; 
2265     } // end for

跳过不满足空间需求的

2252       TestedMemoryBaseAddress = PageAlignAddress (ResourceHob->PhysicalStart);                                            
2253       TestedMemoryLength      = PageAlignLength  (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - TestedMemoryBaseAddress);
2254       if (TestedMemoryLength < MinimalMemorySizeNeeded) {                                                                 
2255         continue;                                                                                                         
2256       }

好的,我们找到了

2261       BaseAddress = TestedMemoryBaseAddress;        
2262       Length      = TestedMemoryLength;             
2263       Attributes  = ResourceHob->ResourceAttribute; 

输出到DEBUG信息中,表示很开心 :)

2268   DEBUG ((EFI_D_INFO, "CoreInitializeMemoryServices:\n"));
2269   DEBUG ((EFI_D_INFO, "  BaseAddress - 0x%lx Length - 0x%lx MinimalMemorySizeNeeded - 0x%lx\n", BaseAddress, Length, MinimalMemorySizeNeeded));

assert一下,表示很有信心:)如果到这里还找不到,那就没法继续了

2274   ASSERT (Length >= MinimalMemorySizeNeeded);

7)转换内存属性到掩码形式

7) 把内存资源的属性Atrributes转换成Memory Capabilities掩码

2276   //                                                                                                                   
2277   // Convert the Resource HOB Attributes to an EFI Memory Capabilities mask                                            
2278   //                                                                                                                   
2279   if ((Attributes & EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE) == EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE) {                   
2280     Capabilities = CoreConvertResourceDescriptorHobAttributesToCapabilities (EfiGcdMemoryTypeMoreReliable, Attributes);
2281   } else {                                                                                                             
2282     Capabilities = CoreConvertResourceDescriptorHobAttributesToCapabilities (EfiGcdMemoryTypeSystemMemory, Attributes);
2283   }

8)添加内存资源描述符,建立内存服务

8) 使用该内存区域建立最初的内存资源,之后内存服务已经完全可用了

2285   //
2286   // Declare the very first memory region, so the EFI Memory Services are available.
2287   //
2288   CoreAddMemoryDescriptor (
2289     EfiConventionalMemory,
2290     BaseAddress,
2291     RShiftU64 (Length, EFI_PAGE_SHIFT),
2292     Capabilities
2293     );

函数CoreAddMemoryDescriptor位于/Dxe/Mem/Page.c中。

/** /Dxe/Mem/Page.c **/
 530 VOID                                     
 531 CoreAddMemoryDescriptor (                
 532   IN EFI_MEMORY_TYPE       Type,         
 533   IN EFI_PHYSICAL_ADDRESS  Start,        
 534   IN UINT64                NumberOfPages,
 535   IN UINT64                Attribute     
 536   )                                      
 537 { 
 /** 建立内存服务 **/
 679 } 

检查是否页对齐

/** /Dxe/Mem/Page.c **/
 543   if ((Start & EFI_PAGE_MASK) != 0) {
 544     return; 
 545   }  

检查内存类型的合法性

/** /Dxe/Mem/Page.c **/
 547   if (Type >= EfiMaxMemoryType && Type < MEMORY_TYPE_OEM_RESERVED_MIN) {
 548     return; 
 549   }  

内存类型的范围描述在/Dxe/Mem/Imem.h

/** /Dxe/Mem/Imem.h **/
 18 //                                                            
 19 // +---------------------------------------------------+      
 20 // | 0..(EfiMaxMemoryType - 1)    - Normal memory type |      
 21 // +---------------------------------------------------+      
 22 // | EfiMaxMemoryType..0x6FFFFFFF - Invalid            |      
 23 // +---------------------------------------------------+      
 24 // | 0x70000000..0x7FFFFFFF       - OEM reserved       |      
 25 // +---------------------------------------------------+      
 26 // | 0x80000000..0xFFFFFFFF       - OS reserved        |      
 27 // +---------------------------------------------------+      
 28 //                                                            
 29 #define MEMORY_TYPE_OS_RESERVED_MIN                 0x80000000
 30 #define MEMORY_TYPE_OS_RESERVED_MAX                 0xFFFFFFFF
 31 #define MEMORY_TYPE_OEM_RESERVED_MIN                0x70000000
 32 #define MEMORY_TYPE_OEM_RESERVED_MAX                0x7FFFFFFF

随后开始真正的建立内存服务。

/** /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 ();

明明以为到这里就开始建立了,结果却又跳转到一个函数。

实际上复杂的系统中这种情况很常见,真正干实际事情的代码隐藏在层层调用之中。这是因为往往需要有大量的检查工作,而且如果所有代码都放在一个函数里实现,通常会显得很臃肿。

Lock机制在这里就不详细讲了,最底层的实现是禁中断。这是因为UEFI系统只用了一个处理器核。详细会在异步事件机制中分析。

CoreAddRange将在后面操作内存的接口函数中详细描述。

/** /Dxe/Mem/Page.c **/
 569   if (mMemoryTypeInformationInitialized) { 
 570     return;                                
 571   }                                        

内存服务建立后,我们需要使用MemoryTypeInformation信息来进行测试。全局变量mMemoryTypeInformationInitialized用于标记是否完成来内存测试工作,在本函数的结尾会标记为TRUE,若在某次申请测试时出错则会直接返回,从而该标记不会被置为真。

在前文的3)中从HOB中获取的内存类型信息,描述了各种类型的内存所需要的页数,随后在4)中我们据此计算出了最小的内存空间需求。之后的5)和6)均是在这最小的内存空间需求下寻找合适的内存空间。

首先我们遍历所有的内存类型信息

/** /Dxe/Mem/Page.c **/
 577   for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { 
 581     Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[Index].Type);                   
 582     if ((UINT32)Type > EfiMaxMemoryType) {                                           
 583       continue;
 584     }

如果某个内存类型所需要的页数不为零,则申请这么多页大小的空间。CoreAllocatePages会在后面详述。

/** /Dxe/Mem/Page.c **/
 585     if (gMemoryTypeInformation[Index].NumberOfPages != 0) {   
 589       Status = CoreAllocatePages (                            
 590                  AllocateAnyPages,                            
 591                  Type,                                        
 592                  gMemoryTypeInformation[Index].NumberOfPages, 
 593                  &mMemoryTypeStatistics[Type].BaseAddress     
 594                  );                                           

这里涉及到一个全局变量mMemoryTypeStatistics,它是对每种内存类型的一个统计变量,同样定义于本文件/Dxe/Mem/Page.c中。

/** /Dxe/Mem/Page.c **/
  21 typedef struct {                             
  22   EFI_PHYSICAL_ADDRESS  BaseAddress;         
  23   EFI_PHYSICAL_ADDRESS  MaximumAddress;      
  24   UINT64                CurrentNumberOfPages;
  25   UINT64                NumberOfPages;       
  26   UINTN                 InformationIndex;    
  27   BOOLEAN               Special;             
  28   BOOLEAN               Runtime;             
  29 } EFI_MEMORY_TYPE_STATISTICS;                

  53 EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = {
  54   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE,  FALSE },  // EfiReservedMemoryType
  55   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiLoaderCode 
  56   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiLoaderData             
  57   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiBootServicesCode       
  58   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiBootServicesData       
  59   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE,  TRUE  },  // EfiRuntimeServicesCode    
  60   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE,  TRUE  },  // EfiRuntimeServicesData    
  61   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiConventionalMemory     
  62   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiUnusableMemory         
  63   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE,  FALSE },  // EfiACPIReclaimMemory      
  64   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE,  FALSE },  // EfiACPIMemoryNVS          
  65   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiMemoryMappedIO         
  66   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiMemoryMappedIOPortSpace
  67   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE,  TRUE  },  // EfiPalCode                
  68   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE },  // EfiPersistentMemory       
  69   { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }   // EfiMaxMemoryType          
  70 };                                                                                        

每种内存类型都会有一个统计信息,统计信息有七类。

  • BaseAddress即基地址,初始为零,在前文的申请内存时会被赋值为一个有意义的地址
  • MaximumAddress即最高地址,初始即为MAX_ADDRESS
  • CurrentNumberOfPages即当前已申请的页数
  • NumberOfPages即总页数,测试成功后与gMemoryTypeInformation中记录的页数相同
  • InformationIndex记录该内存类型对应gMemoryTypeInformation的索引
  • Special特殊需求
  • Runtime记录该内存类型是否是运行时类型。运行时的内存资源在UEFI退出后不会被释放。

若某一次申请内存出错,则释放所有已申请的内存空间并返回,全局变量mMemoryTypeInformationInitialized仍为FALSE,因此在下一次执行本函数CoreAddMemoryDescriptor的时候会继续进行测试。

/** /Dxe/Mem/Page.c **/
 595       if (EFI_ERROR (Status)) {      
 601         for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) {  
 605           Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[FreeIndex].Type); 
 606           if ((UINT32)Type > EfiMaxMemoryType) {   
 607             continue;               
 608           }     
 609                                                                               
 610           if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) {         
 611             CoreFreePages (                                                   
 612               mMemoryTypeStatistics[Type].BaseAddress,                        
 613               gMemoryTypeInformation[FreeIndex].NumberOfPages                 
 614               );                                                              
 615             mMemoryTypeStatistics[Type].BaseAddress    = 0;                   
 616             mMemoryTypeStatistics[Type].MaximumAddress = MAX_ADDRESS;         
 617           }                                                                   
 618         }                                                                     
 619         return;
 620       } 

每次申请成功的时候,则更新该内存类型对应的统计信息mMemoryTypeStatistics

/** /Dxe/Mem/Page.c **/
 625       mMemoryTypeStatistics[Type].MaximumAddress =                                     
 626         mMemoryTypeStatistics[Type].BaseAddress +                                      
 627         LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1;   

同时更新全局变量mDefaultMaximumAddress,该变量用于记录所有内存类型申请的内存空间基地址中对小的基地址减一的值。

/** /Dxe/Mem/Page.c **/
 633       if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { 
 634         mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; 
 635       }                                                                       
 636     } // end if NumberOgPages != 0
 637   } // end for each memory type

至此所有内存类型中页数不为零的类型均成功申请了一块内存,且更新了对应的统计信息,说明内存空间上足够的。接下来继续更新统计信息,同时释放申请的内存空间。

函数CoreFreePages用于释放申请的内存空间。由于测试过内存空间足够,因此可以在统计信息中更新NumberOfPages域。

/** /Dxe/Mem/Page.c **/

 644   for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {            
 648     Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[Index].Type);                              
 649     if ((UINT32)Type > EfiMaxMemoryType) {                                                      
 650       continue;                                                                                 
 651     }                                                                                           
 652     if (gMemoryTypeInformation[Index].NumberOfPages != 0) {                                     
 653       CoreFreePages (                                                                           
 654         mMemoryTypeStatistics[Type].BaseAddress,                                                
 655         gMemoryTypeInformation[Index].NumberOfPages                                             
 656         );                                                                                      
 657       mMemoryTypeStatistics[Type].NumberOfPages   = gMemoryTypeInformation[Index].NumberOfPages;
 658       gMemoryTypeInformation[Index].NumberOfPages = 0;                                          
 659     }                                                                                           
 660   }                                 

随后更新统计信息中的InformationIndex域,并设置部分内存类型的MaximumAddress域。如果某个内存类型在gMemoryTypeInformation中记录的页数不为零,则至此该类型的MaximumAddress已经在前文更新过,不会等于其初始值MAX_ADDRESS。因此若相等(673行),则说明该内存类型在gMemoryTypeInformation中的页数为0。此时统一把这些内存类型的统计信息中的MaximumAddress设置为mDefaultMaximumAddress

还记得mDefaultMaximumAddress为所有申请的内存空间的最小基地址减1的值吗,所以该全局变量实际上的描述的一个默认分配空间的最高地址。后面在分析内存的申请和释放的时候会看到这个全局变量和另一个全局变量mDefaultBaseAddress的作用。

 666   for (Type = (EFI_MEMORY_TYPE) 0; Type < EfiMaxMemoryType; Type++) {                  
 667     for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { 
 668       if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) {               
 669         mMemoryTypeStatistics[Type].InformationIndex = Index;                          
 670       }                                                                                
 671     }                                                                                  
 672     mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0;                              
 673     if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ADDRESS) {                   
 674       mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress;             
 675     }                                                                                  
 676   }                                                                                    

最后标记内存测试已完成。

 678   mMemoryTypeInformationInitialized = TRUE; 

参考文献

  • spec

你可能感兴趣的:(UEFI)