Variable Services是Runtime Services的一部分,提供关于Variable的一些服务,Variable被定义为键值对,由标识信息加上属性(键)和任意数据(值)组成。Variable用于存储在平台实现的EFI环境和EFI OS加载器以及在EFI环境中的其他应用程序之间传递数据。(1、一个Guid可以对应多个Variable,一个Variable中包含多种不同数据类型的数据(结构体)?;2、Setup界面中的一些值就是Variable,通过这种方式来存储。)
UEFI规范为Variable Services定义了四个接口,如下表所示:
Name Type Description
GetVariable Runtime Returns the value of a variable.
GetNextVariableName Runtime Enumerates the current variable names.
SetVariable Runtime Sets the value of a variable.
QueryVariableInfo Runtime Returns information about the EFI variables.
2 接口
2.1 GetVariable这个接口的作用是返回一个指定Variable的值。
每个供应商可以使用唯一的VendorGuid创建和管理自己的变量,而不会有名称冲突的风险。当一个变量被设置时,它的Attributes被提供来指示系统应该如何存储和维护这个数据变量。属性会影响何时可以访问变量以及数据的波动性。如果EFI_BOOT_SERVICES.ExitBootServices()已经被执行,没有设置EFI_VARIABLE_RUNTIME_ACCESS属性的变量将不可见,GetVariable()将会返回一个EFI_NOT_FOUND错误。
如果Data缓冲区太小,无法保存变量的内容,则返回错误EFI_BUFFER_TOO_SMALL,并将DataSize设置为获取数据所需的缓冲区大小。
函数原型为:
typedef
EFI_STATUS
GetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
OUT UINT32 *Attributes OPTIONAL,
IN OUT UINTN *DataSize,
OUT VOID *Data OPTIONAL
);
1
2
3
4
5
6
7
8
9
2.2 GetNextVariableName
这个函数的作用是枚举当前变量名。
GetNextVariableName()被调用多次以检索系统中当前可用的所有变量的VariableName和VendorGuid。在每次调用GetNextVariableName()时,前面的结果都被传递到接口中,在输出时,接口返回下一个变量名数据。当返回整个变量列表时,返回错误EFI_NOT_FOUND。
注意,如果返回EFI_BUFFER_TOO_SMALL,则返回Variable Name缓冲区对于下一个变量来说太小了。当出现这种错误时,VariableNameSize将被更新以反映所需的缓冲区大小。在调用GetNextVariableName()的所有情况下,VariableName分配的实际缓冲区大小。变量名不能小于在变量名缓冲区输入时传递给GetNextVariableName()变量名字符串的大小。
一旦执行EFI_BOOT_SERVICES.ExitBootServices(),仅在引导服务期间可见的变量将不再返回。要获取GetNextVariableName()返回的变量的数据内容或属性,需要使用GetVariable()接口。
函数原型为:
typedef
EFI_STATUS
GetNextVariableName (
IN OUT UINTN *VariableNameSize,
IN OUT CHAR16 *VariableName,
IN OUT EFI_GUID *VendorGuid
);
1
2
3
4
5
6
7
2.3 SetVariable
这个函数的作用是设置变量的值。此服务可用于创建新变量、修改现有变量的值或删除现有变量。
变量存储在固件中,并且可以在整个电源周期中保持它们的值。每个供应商都可以创建和管理自己的变量,而不用冒使用唯一变量VendorGuid而产生的命名冲突的风险。VendorGuid已经被UEFI规范用来定义一些Global Variable,可以在第三章Boot Manager下的Table3-1看到相关定义。
每个变量都具有定义固件如何存储和维护数据值的Attributes。如果EFI_VARIABLE_NON_VOLATILE属性没有设置,固件将变量存储在普通内存中,在整个电源周期不维护。这些变量用于将信息从一个组件传递到另一个组件。这方面的一个例子是固件的语言代码支持变量。它是在固件初始化时创建的,以供可能需要该信息的EFI组件访问,但不需要备份到非易失性存储。
EFI_VARIABLE_NON_VOLATILE变量存储在存储容量有限的固定硬件中;有时是严重有限的容量。软件应该只在绝对必要时使用非易失性变量。此外,如果软件使用非易失性变量,则应尽可能使用仅在引导服务时可访问的变量。
函数原型为:
typedef
EFI_STATUS
SetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
);
1
2
3
4
5
6
7
8
9
2.4 QueryVariableInfo
这个函数的作用是返回关于EFI变量的信息。
QueryVariableInfo()函数允许调用者获得关于可用于存储EFI变量的最大存储空间,可用于存储EFI变量的最大剩余存储空间的大小和每个EFI变量的最大空间,与指定的属性相关联。
函数原型为:
typedef
EFI_STATUS
QueryVariableInfo (
IN UINT32 Attributes,
OUT UINT64 *MaximumVariableStorageSize,
OUT UINT64 *RemainingVariableStorageSize,
OUT UINT64 *MaximumVariableSize
);
1
2
3
4
5
6
7
8
2.5 相关定义
2.5.1 Attribute
上述有几个接口函数有Attribute这个参数,这个参数是比较重要的,它具体有以下取值:
//*******************************************************
// Variable Attributes
//*******************************************************
#define EFI_VARIABLE_NON_VOLATILE 0x00000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008
//This attribute is identified by the mnemonic ‘HR’ elsewhere
//in this specification.
#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010
//NOTE: EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated
//and should be considered reserved.
#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS \ 0x00000020
#define EFI_VARIABLE_APPEND_WRITE 0x00000040
#define EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS 0x00000080
//This attribute indicates that the variable payload begins
//with an EFI_VARIABLE_AUTHENTICATION_3 structure, and
//potentially more structures as indicated by fields of this
//structure. See definition below and in SetVariable().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
其中常用的就是前面三种:
(1)EFI_VARIABLE_NON_VOLATILE(NV):表示设置变量时将变量写入到非易失性介质中,比如SPI Flash;
(2)EFI_VARIABLE_BOOTSERVICE_ACCESS(BS):表示设置的变量在UEFI阶段都可以访问,更具体地说是在调用gBS->ExitBootServices()之前都可以访问,它对于操作系统是不可见的;
(3)EFI_VARIABLE_RUNTIME_ACCESS(RT):表示设置的变量在OS下都可以访问,当然前提是OS是UEFI兼容的。
UEFI规范定义了一些Global Variable,可以在第三章Boot Manager下的Table3-1看到相关定义。
3 相关Protocol
在PI规范中定义了两个Protocol,这两个Protocol与上述的接口初始化有关。详细信息在PI规范的DXE Architectural Protocols章节。
3.1 Variable Architectural Protocol
EFI_VARIABLE_ARCH_PROTOCOL
提供获取和设置环境变量所需的服务。该协议必须由DXE驱动程序生成,并且只能由DXE Foundation使用。
生成此协议的DXE驱动程序必须是运行时驱动程序。这个驱动程序负责初始化UEFI Runtime Services表的GetVariable(), GetNextVariableName(), SetVariable()和QueryVariableInfo()字段。在UEFI Runtime Services表的三个字段初始化后,驱动程序必须安装EFI_VARIABLE_ARCH_PROTOCOL_GUID的新句柄与NULL接口指针。该协议的安装会通知DXE Foundation,只读和易变环境变量相关的服务现在可用,DXE Founation必须更新UEFI Runtime Services表的32位CRC。环境变量服务的完整实现是在此协议和EFI_VARIABLE_ARCH_PROTOCOL都安装后。**需要对volatile环境变量进行只读访问或读/写访问的DXE驱动程序必须在其依赖表达式中具有此架构协议。**需要对非易失性(nonvolatile)环境变量进行写访问的DXE驱动程序必须在依赖项表达式中包含EFI_VARIABLE_WRITE_ARCH_PROTOCOL。
3.2 Variable Write Architectural Protocol
EFI_VARIABLE_WRITE_ARCH_PROTOCOL
提供设置非易失性环境变量所需的服务。该协议必须由运行时DXE驱动程序生成,并且只能由DXE Foundation使用。
生成此协议的DXE驱动程序必须是运行时驱动程序。这个驱动程序可能会更新UEFI Runtime Services表的SetVariable()字段。初始化UEFI Runtime Services表后,驱动程序必须将EFI_VARIABLE_WRITE_ARCH_PROTOCOL_GUID安装到一个新的句柄上,该句柄的接口指针为NULL。环境变量的完整实现是在此协议和EFI_VARIABLE_ARCH_PROTOCOL都安装后。**需要对volatile环境变量进行只读访问或读写访问的DXE驱动程序必须在依赖表达式包含此架构协议:EFI_VARIABLE_WRITE_ARCH_PROTOCOL。**需要对非易失性(nonvolatile)环境变量进行访问的DXE驱动程序必须在其依赖项表达式中具有这种架构协议。
4 接口初始化
上述接口是在gRT中的,它们并非一开始就可以使用,必须要到UEFI的DXE阶段才可以使用。并且,也不是DXE阶段一开始就可以使用。在DXE的运行过程中,会加载一个个的模块,来填满整个表。
上述接口的初始化有不同的分类,分别在不同的模块中实现,并且只需要其中的一个模块运行就可以了。
(关于接口的初始化也有点模糊,为什么有不同的分类,而且还要在不同的模块中实现)。
MyVariable.c
#include
#include
#include
#include
#include
#include
#include
#include //ShellCEntryLib call user interface ShellAppMain
EFI_STATUS
EFIAPI
MyHVariableAppEntry(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable
)
{EFI_STATUS Status = EFI_SUCCESS;UINTN DataSize;UINT64 CSDNEnable = 1;EFI_GUID gEfiCsdnEnableGuid = { 0xb71604d2, 0xb8ba, 0x4d9c, { 0x78, 0x88, 0xac, 0xcf, 0x1d, 0xa2, 0x26, 0xc3 }};DataSize = sizeof (CSDNEnable);Status = gRT->SetVariable (L"CSDNEnable",&gEfiCsdnEnableGuid,EFI_VARIABLE_NON_VOLATILE |EFI_VARIABLE_BOOTSERVICE_ACCESS |EFI_VARIABLE_RUNTIME_ACCESS,DataSize,&CSDNEnable);ASSERT(Status);DEBUG ((EFI_D_ERROR," [CSDN]: Setvariable CSDNEnable 0x%x.\n", CSDNEnable));CSDNEnable = 0;DataSize = sizeof (CSDNEnable);Status = gRT->GetVariable (L"CSDNEnable",&gEfiCsdnEnableGuid,NULL,&DataSize,&CSDNEnable);ASSERT(Status);if (CSDNEnable) {DEBUG ((EFI_D_ERROR, "[CSDN] CSDNEnable\n"));}return EFI_SUCCESS;
}
MyVariable.inf
[Defines]INF_VERSION = 0x00010006BASE_NAME = MyVariableFILE_GUID = 69A6DE6D-FA9F-485E-9A3E-EA70FDCFC82AMODULE_TYPE = UEFI_APPLICATIONVERSION_STRING = 1.0ENTRY_POINT = MyHVariableAppEntry[Sources]MyVariable.c[Packages]MdePkg/MdePkg.decShellPkg/ShellPkg.decMdeModulePkg/MdeModulePkg.dec[LibraryClasses]UefiShellCEntryLibBaseLibBaseMemoryLibDebugLibPrintLibDevicePathLibUefiBootServicesTableLibMemoryAllocationLibUefiLibPcdLib
上一篇:MyBatis注解开发
下一篇:python:数据结构内容(1)