MdeModulePkg/Core/Dxe
/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数据的指针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)初始化内存池
/** /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) 获取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) 计算所需的内存资源
/** /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数据,来获取可用的内存资源信息
/** /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) 若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) 把内存资源的属性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) 使用该内存区域建立最初的内存资源,之后内存服务已经完全可用了
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;