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

TianoCore、UEFI、EDK II TianoCore是支持统一可扩展固件接口(UEFI)的开源实现的社区。 EDK II是现代的,功能丰富的跨平台固件开发环境,适用于UEFI和UEFI平台初始化(PI)规范。 EDK2---“UEFI Development Kit”(UDK) UEFI Platform Initialization---UEFI平台初始化; Unified Extensible Firmware Interface (UEFI) TianoCore社区: https://www.tianocor... 阅读详情
  • 源码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 
DXE-------Driver Execution Environment (执行驱动环境)详解 DXE是在PEI阶段之后的下一个阶段。它实现了驱动环境的搭建(UEFI第三阶段)。DXE阶段执行了大量的系统初始化工作,在PEI的基础上,进入此阶段时,内存已经可以被完全使用,因此此阶段可以进行大量复杂的工作。DXE驱动之间通过Protocol通信。当所有的Driver都执行完毕后,系统完成初始化,接着会执行一个特殊的DXE Driver 的Protocol进而进入BDS阶段。从本质上讲,BDS是一种特殊的DXE阶段的应用程序。整体流程DxeMain是DXE阶段执行的主函数,入口函数。 阅读详情

相关推荐

UEFI原理与编程实践--UEFI系统的启动过程

安全验证阶段,这个阶段,需要用Assembly做一些 C 无法处理的工作,C语言无法处理CPU的特殊寄存器。让CPU进入Protected Mode(Flat Mode)的环境,会使用到CPU内部的临时ram,其实也就是缓存,这个阶段主要还是使用的汇编,也就是说为后面的阶段建立一个C语言和硬件通信的编译环境,最后将控制权交给PEI Phase。

Anthony的博客 4618

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

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

随便写写 5101

UEFI.源码分析.DXE阶段的执行

源码EDK2 : Tianocore UEFI源码分析系列第一篇,DXE阶段的初始化与执行流程 源码结构 MdeModulePkg/ Core/ Dxe/ Dispatcher/ DXE调度器 DxeMain/ DXE阶段入口函数 Event/ 异步事件机制 FwVol/ 闪存文件系统FFS FwVolBloc...

随便写写 6788

UEFI.源码分析.DXE的内存服务.第三部分.HeapGuard

源码EDK2:Tianocore UEFI源码分析系列第二篇,DXE阶段的内存服务 第三部分,HeapGuard DXE阶段源码目录MdeModulePkg/Core/Dxe 基于开篇日(2018/04/18:16:30)时最新的EDk2版本,commit为255101471918ed8840f2be347916b90eef0e9c08 从初始化过程入手 直到CoreAddMemo...

随便写写 1345

高通Android12启动流程分析

请注意,这个流程可能会根据不同的硬件平台、Android版本和制造商的定制有所变化。上述步骤提供了一个高通Android设备从上电到操作系统完全启动的一般概述。

hyg55555的博客 2786

unity中基于NGUI实现一张图片上在某个地方挖个洞

按照策划的要求,引导时只有被引导的图片是高亮的,其余地方都是黑色半透明,如下图所示: 对应的实现shader如下: Shader "Unlit/TutorialBg" { Properties { _MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {} _ClipRange0 ("Clip Vector

手机游戏开发 3698

第 3 天:Actor 是什么?创建你的第一个游戏对象!

本文详细介绍 Actor 在 Unreal Engine 5 中的作用,并通过 C++ 创建你的第一个游戏对象。你将学习 Actor 的生命周期(BeginPlay、Tick)、如何添加组件(Static Mesh),并让 Actor 在游戏世界中运作。同时提供 常见问题解决方案,助你快速上手 UE5 C++ 开发!🎮 跟随本教程,构建属于你的第一个游戏对象!

m0_37643873的博客 2629

定制PYNQ的overlay

一、用HLS生成一个定制IP(加速器)(1)首先打开vivado hls,本人的版本是2017.2,点击创建一个新工程。(2)设置工程名和路径,顶层函数设置为add,创建一个新的C++程序,名字叫做adder.cpp,不用在testbench中添加文件。选择板子的时候搜索xc7z020clg400-1,这是PYNQ的板子号,选择完成。             (3)在左侧的菜单栏里选择Sourc...

bramblewalls的博客 1万+

ADF4002分频器避坑指南:常见配置错误及解决方案

本文深入解析了ADF4002分频器在实战应用中的常见陷阱与解决方案。内容涵盖寄存器配置逻辑、硬件PCB布局要点、软件驱动时序细节及系统化调试方法,重点阐述了如何避免因配置错误导致芯片无法正常工作的问题,帮助工程师高效完成高精度频率合成系统设计。

weixin_29192141的博客 96

下载Python3.10的相关方法及问题解决

下载Python3.10的相关方法及问题解决 报错unable to locate package python3.10-dev以及subprocess.CalledProcessError: Command ‘(‘lsb_release‘, ‘-a‘)‘ returned non-zero exit status 1问题解决

Istil 1555

Android Audio - 支持多应用同时录音_Android8.1修改方法

支持多应用同时录音_Android8.1修改方法 源码路径: sdk\frameworks\av\services\audiopolicy\managerdefault\AudioPolicyManager.cpp 需要修改两个位置: audio_io_handle_t AudioPolicyManager::getInputForDevice() 在mpClientInterface-&gt...

iFinelio Tower 的博客 5758

【肌电处理】肌电信号EMG积分肌电值+均方根值+中值频率+平均功率频率【含Matlab源码 3386期】

肌电信号EMG积分肌电值+均方根值+中值频率+平均功率频率 完整的代码,方可运行;可提供运行操作视频!适合小白!

订阅付费专栏Matlab(奶茶价版),可赠送奶茶价版付费专栏指定代码1份; 821

Android 15.0 添加自定义服务,并生成jar给第三方app调用

在15.0系统ROM产品定制化开发中,由于需要新增加自定义的功能,所以要增加自定义服务,而app上层通过调用自定义服务,来调用相应的功能,所以系统需要先生成jar,然后生成jar 给上层app调用,接下来就来分析实现的步骤,然后来实现相关的功能 从而来实现所需要的功能

安卓兼职framework和app工程师的博客 668

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

上一篇: UEFI.源码分析.DXE的内存服务.第一部分.初始化
下一篇: UEFI.源码分析.DXE的内存服务.第三部分.HeapGuard
木艮氵
博客等级 码龄11年 136粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值