请保留-> 【原文: 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所示。
在上一篇博客代码的基础上,进行部分修改,将用户的选择存储于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太多,只能显示最后一屏:
实际上,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工具,测试结果如下:
加载完驱动后,进入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