当前位置: 首页 > news >正文

【UEFI实战】在库中使用全局变量

说明

本文涉及的代码都可以在vUDK2017: https://github.com/tianocore/edk2.git Tag vUDK2017.中找到。

一个不怎么好的测试代码

有两个驱动,NullDxeDriverOne.inf和NullDxeDriverTwo.inf,它们做的事情只有一件,就是调用一个库函数:

EFI_STATUS
EFIAPI
NullDxeDriverOneEntry (IN EFI_HANDLE           ImageHandle,IN EFI_SYSTEM_TABLE     *SystemTable)
{EFI_STATUS         Status;Status             = EFI_SUCCESS;DEBUG ((EFI_D_ERROR, "[beni]NullDxeDriverOneEntry Start.\n"));PrintGlobalVar ();DEBUG ((EFI_D_ERROR, "[beni]NullDxeDriverOneEntry End.\n"));return Status;
}

下面是PrintGlobalVar()函数的实现:

/**Print the address of the global variables.@param   NA@retval EFI_SUCCESS     Executed successfully.@retval Others          Error happened.
**/
EFI_STATUS
EFIAPI
PrintGlobalVar (VOID)
{if (NULL == gBuffer) {gBuffer = AllocatePool (128);}DEBUG ((EFI_D_ERROR, "[beni]gBuffer addr: 0x%p.\n", gBuffer));DEBUG ((EFI_D_ERROR, "[beni]&Data addr: 0x%p.\n", &Data));return EFI_SUCCESS;
}

这里的gBuffer和Data是两个全局的变量。

这里的本意是,我们从其它设备上获取到一部分数据,存放在gBuffer对应的缓冲区去,之后就不需要在每次调用都去访问设备。

但是实际的情况如下:

[beni]NullDxeDriverOneEntry Start.
[beni]gBuffer addr: 0x7191218.
[beni]&Data addr: 0x7922390.
[beni]NullDxeDriverOneEntry End.
Loading driver F555F2BF-E141-43B7-A5EA-635F757FC774
InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 7191340
Loading driver at 0x0000791D000 EntryPoint=0x0000791D380 NullDxeDriverTwo.efi
InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 7191698
[beni]NullDxeDriverTwoEntry Start.
[beni]gBuffer addr: 0x7191798.
[beni]&Data addr: 0x791F390.
[beni]NullDxeDriverTwoEntry End.

可以看到实际上还是有两份gBuffer(Data是一个整型,它也有两份),也就是说还是需要访问两次设备。

这是因为不同的模块是分开编译的,实际上都是独立的存放了库函数,也就分开存放了这些全局变量。

如果这个库函数被多次的调用,那么多少会影响到启动时间。

PCD数据

对应普通数据,比如说前面提到的Data整型,可以保存成PCD数据。

比如在dec里面声明一个PCD,如下所示:

[PcdsDynamic]gUefiOemPkgTokenSpaceGuid.PcdOemVersion|0xFFFFFFFF|UINT32|0x40000001

注意这里的类型是Dynamic的,这样就可以在启动过程中设置。

比如在之前的Lib中加入如下的代码:

EFI_STATUS
EFIAPI
GlobalDataTestLibConstructor (IN  EFI_HANDLE               ImageHandle,IN  EFI_SYSTEM_TABLE         *SystemTable)
{DEBUG ((EFI_D_ERROR, "[beni]CustomizedDisplayLibConstructor.\n"));if (0xFFFFFFFF == PcdGet32 (PcdOemVersion)) {DEBUG ((EFI_D_ERROR, "[beni]PcdSet.\n"));PcdSet32 (PcdOemVersion, 0x00000001);}return EFI_SUCCESS;
}

这个构造器在每个驱动第一次调用该库中的函数的时候都会调用。

之后在BIOS运行的时候上面的两个驱动的打印如下:

[beni]CustomizedDisplayLibConstructor.
[beni]PcdSet.
[beni]NullDxeDriverOneEntry Start.
[beni]SystemTable: 0x7A95018.
[beni]gBS: 0x7B26C10.
[beni]gBuffer addr: 0x7191218.
[beni]Version: 0x1.
[beni]NullDxeDriverOneEntry End.
Loading driver F555F2BF-E141-43B7-A5EA-635F757FC774
InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 7191340
Loading driver at 0x0000791D000 EntryPoint=0x0000791D380 NullDxeDriverTwo.efi
InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 7191698
[beni]CustomizedDisplayLibConstructor.
[beni]NullDxeDriverTwoEntry Start.
[beni]SystemTable: 0x7A95018.
[beni]gBS: 0x7B26C10.
[beni]gBuffer addr: 0x7191798.
[beni]Version: 0x1.
[beni]NullDxeDriverTwoEntry End.

在第一个驱动运行的时候,PcdOemVersion的值被修改成了1,第二个驱动运行的时候,PcdOemVersion的值已经被修改了,就不会再进入了,然后第二个驱动也能打印PcdOemVersion的值为1。也就是说PcdOemVersion的值再全局都能够正常使用了。

变量

对于上文提到的一般的数据类型,比如UINT32之类的,可以通过PCD来保存。

但是对于一段数据区域,就不太方便,这个时候可以使用变量。

UEFI里面的Runtime Service里面提供了变量的操作函数GetVariable()和SetVariable()。

注意它不能用来PEI阶段,因为只有在DXE阶段相关的驱动安装之后(也并不是一开始就能用,PEI阶段有一种叫做HOB的东西有类似的作用,这里先不讲)才能使用变量操作。

还是以上文的代码为例,为了保存gBuffer的数据并能够在BIOS启动的过程中都可以使用(其实也并没有都可以),这里新建了一个模块,用来初始化gBuffer对应的数据:(新模块名为GlobalDataInstall.inf)

EFI_STATUS
EFIAPI
GlobalDataInstallEntry (IN EFI_HANDLE           ImageHandle,IN EFI_SYSTEM_TABLE     *SystemTable)
{EFI_STATUS         Status;DEBUG ((EFI_D_ERROR, "[beni]GlobalDataInstallEntry start.\n"));Status = VariableMethod ();DEBUG ((EFI_D_ERROR, "[beni]GlobalDataInstallEntry end.\n"));return Status;
}

其中VariableMethod()的实现如下:

EFI_STATUS
EFIAPI
VariableMethod (VOID)
{EFI_STATUS    Status;VOID          *Buffer;Buffer = AllocateZeroPool (128);if (NULL == Buffer) {DEBUG ((EFI_D_ERROR, "[beni]AllocatePool failed.\n"));return EFI_OUT_OF_RESOURCES;}*((UINT32 *)Buffer) = OEM_DATA_MAGIC;Status = gRT->SetVariable (OEM_DATA_NAME,&gEfiOemGlobalDataGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS,128,Buffer);FreePool (Buffer);return Status;
}

其实就是一个简单的调用SetVariable()的过程。

这里因为是测试,所以没有设计到Buffer里面的具体的数据,只是申请了一个128字节的数据区,然后放了个魔术字在最前面。

当SetVariable()之后,系统会保存一份Buffer数据,原来的可以释放掉。

上述的操作其实也不需要再一个新的驱动里面,也可以放在库函数的构造函数中,这种方式已经在前面PCD数据的时候使用过,所以这里采用了新的方式。

之后在库函数中添加如下的代码来获取全局的变量:

EFI_STATUS
EFIAPI
PrintGlobalVar (VOID)
{EFI_STATUS         Status;VOID               *Buffer;UINTN              Size;Size   = 0;Buffer = NULL;Status = gRT->GetVariable (OEM_DATA_NAME,&gEfiOemGlobalDataGuid,NULL,&Size,Buffer);if (EFI_ERROR (Status)) {if (EFI_BUFFER_TOO_SMALL == Status) {DEBUG ((EFI_D_ERROR, "[beni]Size : %d.\n", Size));Buffer = AllocatePool (Size);if (NULL == Buffer) {DEBUG ((EFI_D_ERROR, "[beni]AllocatePool failed.\n"));return EFI_OUT_OF_RESOURCES;}Status = gRT->GetVariable (OEM_DATA_NAME,&gEfiOemGlobalDataGuid,NULL,&Size,Buffer);if (EFI_ERROR (Status)) {DEBUG ((EFI_D_ERROR, "[beni]GetVariable failed 0. - %r\n", Status));return Status;}} else {DEBUG ((EFI_D_ERROR, "[beni]GetVariable failed 1. - %r\n", Status));return Status;}}if (OEM_DATA_MAGIC == *((UINT32 *)Buffer)) {DEBUG ((EFI_D_ERROR, "[beni]I got the data.\n"));}DEBUG ((EFI_D_ERROR, "[beni]Version: 0x%x.\n", PcdGet32(PcdOemVersion)));if (NULL != Buffer) {FreePool (Buffer);Buffer = NULL;}return EFI_SUCCESS;
}

同样在NullDxeDriverOne.inf和NullDxeDriverTwo.inf两个模块当中调用上述的库函数,结果如下:

[beni]CustomizedDisplayLibConstructor.
[beni]NullDxeDriverOneEntry Start.
[beni]SystemTable: 0x7A95018.
[beni]gBS: 0x7B26C10.
[beni]Size : 128.
[beni]I got the data.
[beni]Version: 0x1.
[beni]NullDxeDriverOneEntry End.
Loading driver F555F2BF-E141-43B7-A5EA-635F757FC774
InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 718C2C0
Loading driver at 0x0000791A000 EntryPoint=0x0000791A380 NullDxeDriverTwo.efi
InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 718CC98
[beni]CustomizedDisplayLibConstructor.
[beni]NullDxeDriverTwoEntry Start.
[beni]SystemTable: 0x7A95018.
[beni]gBS: 0x7B26C10.
[beni]Size : 128.
[beni]I got the data.
[beni]Version: 0x1.
[beni]NullDxeDriverTwoEntry End.

可以看到能够正常获取到数据。

这种方式可以使用到一段数据中,当然也能用在普通的数据中。

不过有个问题,可以在上面的代码中看到,需要在库函数中返回的申请内存和释放内存,当多次调用这个库函数的时候,还是会浪费一些时间(当然对于普通数据没有这种烦恼,但是普通数据显然用PCD更方便)。

其它

上述两种是最常见的方式,理论上还应该有其它的方法。

比如将数据放在Protocol中,然后安装这个Protocol,在其它地方获取这个Protocol并解析其中的数据。

http://www.jsqmd.com/news/56443/

相关文章:

  • 107 如何快速保存和恢复文件的默认打开方式?
  • 90 老牌压缩软件,性能强大,开源免费!
  • 95 为什么越来越多的人不再使用eD2k了?回顾电驴的兴与衰
  • 155 重装系统电脑循环重启?问题大概率出现在这里!
  • 89 WindowsPE系统浅谈与PE系统推荐
  • 42 winntsetup版本更新
  • 138 Windows安装程序无法将Windows配置为在此计算机的硬件上运行的解决办法
  • 手搓BIOS+UEFI双启动PE启动盘
  • 139 不用PE不用RE不用U盘不双击setup.exe:独家重装Windows系统的骚操作(全网首创)
  • 39 极限节约C盘空间:符号链接的玩法新高度
  • 2025最新郑州空调/地暖维修保养服务公司最新top5推荐!空调维修/空调清洗/空调保养/地暖清洗/地暖保养,行业专业数据+市场口碑榜+选择指南,南阳/平顶山/周口/新乡
  • 第四
  • 113 隐藏此电脑中的常用文件夹
  • 78 打破 Microsoft Office 只能安装在C盘的魔咒!
  • Centos7.9-生成自定义SSL证书-用于服务器调试、部署
  • 2025空调/地暖清洗保养机构推荐——郑州科名,专业团队护航,口碑出圈,空调维修/清洗/保养,地暖清洗/保养,专业高效,服务优良
  • 全球AI周报:军备竞赛白热化,模型战争迎新纪元,巨头战略大变局
  • test-20251130
  • 第四篇Srum冲刺博客
  • 实用指南:汽车行业SCRM:企业微信+服务商模式破解汽车服务行业痛点的案例分析
  • 实用指南:汽车行业SCRM:企业微信+服务商模式破解汽车服务行业痛点的案例分析
  • 鸿蒙分布式安全通信:跨设备身份认证与数据加密传输 - 青青子衿-
  • BipedalWalker实战:SAC算法如何让机器人学会稳定行走
  • 分布式硬件池化:跨设备摄像头、传感器能力协同 - 青青子衿-
  • 【日记】傍晚半马训练途中,我似乎快要认不出自己生活的这座小城市了(1295 字)
  • 读后感5
  • 血腥之狼:APT组织利用合法软件NetSupport的攻击链分析
  • 如何开始微信小程序渗透?
  • 读后感4
  • NOIP总结