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

    技术2026-02-11  35

    (请保留-> 作者: 罗冰   https://blog.csdn.net/luobing4365)

    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-exolorer项目代码位于:/FF RobinPkg/RobinPkg/Drivers/ MyWizardDrv01               /FF RobinPkg/RobinPkg/Applications/ListVariable

     

    Processed: 0.009, SQL: 9