0
点赞
收藏
分享

微信扫一扫

UEFI开发学习 - Variable Services


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
 );
 


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
 );
 


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
 );
 


2.4 QueryVariableInfo
这个函数的作用是返回关于EFI变量的信息。
QueryVariableInfo()函数允许调用者获得关于可用于存储EFI变量的最大存储空间,可用于存储EFI变量的最大剩余存储空间的大小和每个EFI变量的最大空间,与指定的属性相关联。
函数原型为:

typedef
 EFI_STATUS
 QueryVariableInfo (
 IN UINT32 Attributes,
 OUT UINT64 *MaximumVariableStorageSize,
 OUT UINT64 *RemainingVariableStorageSize,
 OUT UINT64 *MaximumVariableSize
 );
 


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


其中常用的就是前面三种:
(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 <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/PcdLib.h>


//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 = 0x00010006
  BASE_NAME = MyVariable
  FILE_GUID = 69A6DE6D-FA9F-485E-9A3E-EA70FDCFC82A
  MODULE_TYPE = UEFI_APPLICATION
  VERSION_STRING = 1.0
  ENTRY_POINT = MyHVariableAppEntry

[Sources]
  MyVariable.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiShellCEntryLib
  BaseLib
  BaseMemoryLib
  DebugLib
  PrintLib
  DevicePathLib
  UefiBootServicesTableLib
  MemoryAllocationLib
  UefiLib
  PcdLib


举报

相关推荐

0 条评论