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的事件
这里本质上是根据EventGroup
的GUID
来唤醒所有有关的事件,由于异步事件机制上基于时钟中断的,而现在已经拿到了内存锁(即禁中断),故这些事件只是从未触发状态转换到了触发状态,而并不会执行。
真正的执行会在锁释放后(开中断),时钟中断处理程序来执行这些被唤醒的事件。不过也有例外的可能,就是其他地方调用了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
与新加入的Start
和End
相连接,则修改Start
或End
将二者合二为一,并把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;
包含必要的Type
、Start
、End、
和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_MAP
的Entry
/** /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
哎,有点晕了吗。到底mMapStack
、gMemoryMap
和CoreAllocatePoolPages()
之间是如何作用的呢,当mMapStack
中的一个元素同时存在于gMemoryMap
中,申请内存的时候又是如何操作的呢。
在这里给一个简略的描述的话,大概是这样的一个过程,但首先给一个定义以帮助描述,这里称动态内存空间
为先前通过HOB数据搜索所得到的内存空间,称静态内存空间
为mMapStack
全局变量在编译时期所确定的数据段的一段内存空间。
- 初始条件是
mMapStack
中存在一项MEMORY_MAP
,该项同时也链接到了gMemoryMap
。此项描述了一块可用的内存空间,在整个初始化过程中来说,也就是动态内存空间
。 - 函数
CoreFreeMemoryMapStack
的意图是把mMapStack
中的项转移到gMemoryMap
中,而mMapStack
本质上是临时内存,所以CoreFreeMemoryMapStack
本质上也就是把保存在静态内存空间
中的Entry
转移到动态内存空间
。 - 函数
AllocateMemoryMapEntry
需要检查全局变量mFreeMemoryMapEntryList
来获取可用的Entry
,第一次运行时该全局变量必然为空,所以需要通过CoreAllocatePoolPages
来获取一段动态内存空间
。 - 函数
CoreAllocatePoolPages
本质上是通过gMemoryMap
来获取可用的内存的。由于在第1步中gMemoryMap
已经链接到来一项Entry
,而这项Entry
是位于静态内存空间
,该Entry
的FromPages
标记为FALSE
,于是在根据gMemoryMap
中的这一项Entry
来分配动态内存的时候,最终会使得这项Entry
分裂成两项,一项Entry0
描述剩余的动态内存空间,一项Entry1
描述分配出的一页大小的动态内存空间。最终的情况就是,mMapStack
中存在两项即Entry0
和Entry1
,这两项也都链接到gMemoryMap
中。CoreAllocatePoolPages
最终返回一个地址,该地址是Entry1
描述的动态内存空间的基地址。 - 回到函数
AllocateMemoryMapEntry
,在通过CoreAllocatePoolPages
获得可用的一页动态内存后,会按照sizeof(MEMORY_MAP)
的大小将这一页空间划分成若干个Entry
保存到mFreeMemoryMapEntryList
。此时这些Entry
本身的数据实际是位于动态内存空间
,其属性FromPages
被标记为TRUE
,不过这些Entry
并没有描述某一段内存空间,其本身的数据都是空值。此时`mMapStack
中的Entry0
和Entry1
自身的数据仍然位于静态内存空间
中,但这两项所描述的内存空间是动态内存空间
。 - 回到函数
CoreFreeMemoryMapStack
,在通过AllocateMemoryMapEntry
获取到一个位于动态内存空间
的Entry
后,便把静态内存空间
(即mMapStack
)中的一项数据拷贝进去。此时的Entry
本身位于动态内存空间
,而且描述了一段动态内存。 - 把
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_GRANULARITY
和RUNTIME_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 )
这边会首先根据NewType
在mMemoryTypeStatistics
中所指定的位置寻找,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的异步事件服务.第一部分.事件驱动