UEFI开发探索61-VFR文件和其他资源文件2(NVRAM上存储数据)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

NVRAM全称为Non-Volatile Ram(非易失性内存) ,Legacy BIOS下这块是使用CMOS来实现的,UEFI下则可以直接在ROM中分出一部分来实现。(问题来了,在UEFI下CMOS到底怎么用的呢? 有机会再研究一下)

UEFI下,对NVRAM的使用如图1所示。

图 1 配置参数存储于NVRAM

在上一篇博客代码的基础上,进行部分修改,将用户的选择存储于NVRAM,修改步骤如下。

1 更新MyWizardDriver.c

获取HII相关的几个Protocol指针:

  EFI_HII_STRING_PROTOCOL         *HiiString;
  EFI_FORM_BROWSER2_PROTOCOL      *FormBrowser2;
  EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting;

添加相应的代码(Line228~Line261):

  // Locate Hii Database protocol
  //
  Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **) &HiiDatabase);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  PrivateData->HiiDatabase = HiiDatabase;

PrivateData->HiiConfigRouting = HiiConfigRouting;

后续的代码中,获取Hii Database protocol的代码注释掉。(Line289~Line298以及Line310)

2 更新HiiConfigAccess.c

添加外部变量的声明(Line14 and Line15):

extern EFI_GUID   mMyWizardDriverFormSetGuid;
extern CHAR16     mIfrVariableName[];

然后对三个函数进行改造,之前这三个函数都没有实现具体功能:

MyWizardDriverHiiConfigAccessExtractConfig()、MyWizardDriverHiiConfigAccessRouteConfig()和MyWizardDriverHiiConfigAccessCallback()。

具体的代码就不贴出来了,到文末给的地址将代码下载下来查看即可。

3 几个重要Porotocol

(1)gEfiHiiDatabaseProtocolGuid。 EFI_HII_DATABASE_PROTOCOL的GUID,定义于EdkCompatibilityPkg\Foundation\Efi\Protocol\HiiDatabase\HiiDatabase中 。访问HII仓库的Protocol,UEFI Spec 2.8 34.8节详细描述其用法。

(2) gEfiHiiStringProtocolGuid。EFI_HII_STRING_PROTOCOL的GUID,定义于EdkCompatibilityPkg\Foundation\Efi\Protocol\HiiString\HiiString.c中。访问字符串资源的Protocol,UEFI Spec 2.8 的34.3节描述其用法,在之前的博客中使用过它。

(3)  gEfiFormBrowser2ProtocolGuid。EFI_FORM_BROWSER2_PROTOCOL的GUID,定义于EdkCompatibilityPkg\Foundation\Efi\Protocol\FormBrowser2\FormBrowser2.c中,是Setup界面的基础引擎。UEFI Spec 2.8 的35.6节描述其用法。

(4) gEfiHiiConfigRoutingProtocolGuid。EFI_HII_CONFIG_ROUTING_PROTOCOL的GUID,定义于EdkCompatibilityPkg\Foundation\Efi\Protocol\HiiConfigRouting\HiiConfigRouting.c中,这是一个全局处理Setup界面交互的protocol。UEFI Spec 2.8 的35.4节描述其用法。

Variable是UEFI环境中的Key/Value对(想起了离散数学的二元组),用于在平台上存储数据,允许EFI执行环境中和EFI OS Loader中使用,在操作系统下也可以通过API访问。在大多数情况下,Variable是持续存在的(不因掉电而消失)。在本篇中,用到了Rutime Services中处理Variable的相关Protocol,正好趁机会整理下。

(5)  typedef  EFI_STATUS GetVariable (
IN CHAR16 *VariableName,  //Variable的字符串名
IN EFI_GUID *VendorGuid,   //Variable的唯一GUID
OUT UINT32 *Attributes OPTIONAL, //属性,如果定义非空,则返回其相关性质,
               //见MdePkg\Include\Uefi\UefiMultiPhase.h中相关宏定义
IN OUT UINTN *DataSize, //指明输入或者输出的数据缓冲区长度
OUT VOID *Data OPTIONAL //数据缓冲区
);

每个厂商都会创建并维护自己的Variable,为了与其他Variable不冲突,使用VendorGuid来唯一标志这些Variable。各种属性中,EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS比较特殊,其返回的数据接口有标准定义,可以在UEFI标准中查看。

(6)  typedef EFI_STATUS GetNextVariableName (
IN OUT UINTN *VariableNameSize, //VariableName的字节长度,必须保证足够容纳字

  // 符串(包含NULL字符)
IN OUT CHAR16 *VariableName, //作为输入时,需提供由此函数返回最后一个Variable

  // 字符串名;作为输出时,返回Variable字符串
IN OUT EFI_GUID *VendorGuid //作为输入时,需提供由此函数返回的最后一个

  // VendorGuid; 作为输出时,返回当前Variable的GUID
);

为了熟悉这个函数的用法,我参照ShellProtocol中的函数,自己写了个ListVariable的工程例子。具体见篇末的链接。

这是个粗糙的例子,屏幕显示都没有考虑,Variable太多,只能显示最后一屏:

图2 显示Variable

实际上,Shell命令dmpstore可以列出所有的Variable,以及其中的内容,在后面的实验中我们会用到。

(7)  typedef EFI_STATUS SetVariable (
IN CHAR16 *VariableName,  // VariableName的字节长度,必须保证足够容纳字符串(包//含NULL字符)
IN EFI_GUID *VendorGuid,  //Variable的唯一GUID
IN UINT32 Attributes,     //属性,见函数GetVariable中的说明
IN UINTN DataSize,  //一般用作数据缓冲区的长度,不过,当Attributes为下列值时,作用

  //不同 (具体查看UEFI规格书):
//EFI_VARIABLE_APPEND_WRITE,
//EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS,
//EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS,
//EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
IN VOID *Data      // Variable的内容
);

这是个非常复杂的函数,UEFI规范中用了足足7页来介绍(UEFI Spec 2.8 p235-p241),这里没法展开讨论,还是通过实例的学习,稍微体会其用法吧。

4 编译及测试

为演示Variable的用法,我在HiiConfigAccess.c的Line271开始,添加了几行代码:

//robin add 2020-07-01 22:46:51
  PrivateData->Configuration.MyWizardDriverStringData[0]=0x31;
  PrivateData->Configuration.MyWizardDriverStringData[1]=0x32;
  PrivateData->Configuration.MyWizardDriverStringData[2]=0x33;
  PrivateData->Configuration.MyWizardDriverStringData[3]=0x34;
  PrivateData->Configuration.MyWizardDriverStringData[4]=0x35;

将工程拷贝到RobinPkg的Driver文件夹下,编译:

C:\MyWorkspace>build -a X64 -p RobinPkg\RobinPkg.dsc
-m RobinPkg\Drivers\MyWizardDrv01\MyWizardDriver.inf

使用上一篇中的调试环境,配合dmpstore工具,测试结果如下:

图3 测试过程

加载完驱动后,进入BIOS Setup,修改自定义的选项,然后退出。再次进入UEFI Shell后,可以查看Variable,可以看出所修改的部分已经改变了。

Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/RobinPkg/Drivers/ MyWizardDrv01
              /RobinPkg/Applications/ListVariable

4,285 total views, 1 views today

发表评论

电子邮件地址不会被公开。