BIOS/UEFI基础——变量

来源:互联网 发布:南开大学网络 查询分数 编辑:程序博客网 时间:2024/05/16 14:50

接口

UEFI中操作变量的接口主要是下面的几个:

获取变量的接口:

/**  Returns the value of a variable.  @param[in]       VariableName  A Null-terminated string that is the name of the vendor's                                 variable.  @param[in]       VendorGuid    A unique identifier for the vendor.  @param[out]      Attributes    If not NULL, a pointer to the memory location to return the                                 attributes bitmask for the variable.  @param[in, out]  DataSize      On input, the size in bytes of the return Data buffer.                                 On output the size of data returned in Data.  @param[out]      Data          The buffer to return the contents of the variable.**/typedefEFI_STATUS(EFIAPI *EFI_GET_VARIABLE)(  IN     CHAR16                      *VariableName,  IN     EFI_GUID                    *VendorGuid,  OUT    UINT32                      *Attributes,    OPTIONAL  IN OUT UINTN                       *DataSize,  OUT    VOID                        *Data  );
设置变量的接口:

/**  Sets the value of a variable.  @param[in]  VariableName       A Null-terminated string that is the name of the vendor's variable.                                 Each VariableName is unique for each VendorGuid. VariableName must                                 contain 1 or more characters. If VariableName is an empty string,                                 then EFI_INVALID_PARAMETER is returned.  @param[in]  VendorGuid         A unique identifier for the vendor.  @param[in]  Attributes         Attributes bitmask to set for the variable.  @param[in]  DataSize           The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,                                  EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or                                  EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero                                  causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is                                  set, then a SetVariable() call with a DataSize of zero will not cause any change to                                  the variable value (the timestamp associated with the variable may be updated however                                  even if no new data value is provided,see the description of the                                  EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not                                  be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).   @param[in]  Data               The contents for the variable.**/typedefEFI_STATUS(EFIAPI *EFI_SET_VARIABLE)(  IN  CHAR16                       *VariableName,  IN  EFI_GUID                     *VendorGuid,  IN  UINT32                       Attributes,  IN  UINTN                        DataSize,  IN  VOID                         *Data  );
获取下一个变量的接口:

/**  Enumerates the current variable names.  @param[in, out]  VariableNameSize The size of the VariableName buffer.  @param[in, out]  VariableName     On input, supplies the last VariableName that was returned                                    by GetNextVariableName(). On output, returns the Nullterminated                                    string of the current variable.  @param[in, out]  VendorGuid       On input, supplies the last VendorGuid that was returned by                                    GetNextVariableName(). On output, returns the                                    VendorGuid of the current variable.**/typedefEFI_STATUS(EFIAPI *EFI_GET_NEXT_VARIABLE_NAME)(  IN OUT UINTN                    *VariableNameSize,  IN OUT CHAR16                   *VariableName,  IN OUT EFI_GUID                 *VendorGuid  );

获取系统中的变量信息:

/**  Returns information about the EFI variables.  @param[in]   Attributes                   Attributes bitmask to specify the type of variables on                                            which to return information.  @param[out]  MaximumVariableStorageSize   On output the maximum size of the storage space                                            available for the EFI variables associated with the                                            attributes specified.  @param[out]  RemainingVariableStorageSize Returns the remaining size of the storage space                                            available for the EFI variables associated with the                                            attributes specified.  @param[out]  MaximumVariableSize          Returns the maximum size of the individual EFI                                            variables associated with the attributes specified.  @retval EFI_SUCCESS                  Valid answer returned.  @retval EFI_INVALID_PARAMETER        An invalid combination of attribute bits was supplied  @retval EFI_UNSUPPORTED              The attribute is not supported on this platform, and the                                       MaximumVariableStorageSize,                                       RemainingVariableStorageSize, MaximumVariableSize                                       are undefined.**/typedefEFI_STATUS(EFIAPI *EFI_QUERY_VARIABLE_INFO)(  IN  UINT32            Attributes,  OUT UINT64            *MaximumVariableStorageSize,  OUT UINT64            *RemainingVariableStorageSize,  OUT UINT64            *MaximumVariableSize  );

参数说明已在注释中,这里需要关心的是Attributes这个参数,它具体有以下的取值:

////// Attributes of variable.///#define EFI_VARIABLE_NON_VOLATILE                            0x00000001#define EFI_VARIABLE_BOOTSERVICE_ACCESS                      0x00000002#define EFI_VARIABLE_RUNTIME_ACCESS                          0x00000004////// This attribute is identified by the mnemonic 'HR'/// elsewhere in this specification.///#define EFI_VARIABLE_HARDWARE_ERROR_RECORD                   0x00000008////// Attributes of Authenticated Variable///#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS              0x00000010#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS   0x00000020#define EFI_VARIABLE_APPEND_WRITE                            0x00000040
其中常用的就是前面的三种:

EFI_VARIABLE_NON_VOLATILE:表示设置变量时将变量写入到非易失介质中,比如SpiFlash;

EFI_VARIABLE_BOOTSERVICE_ACCESS:表示设置的变量在UEFI阶段都可以访问,更具体的说是在调用gBS->ExitBootServices()之前都可以访问;

EFI_VARIABLE_RUNTIME_ACCESS:表示设置的变量在OS下都可以访问,当然前提是OS是UEFI兼容的。


接口初始化

变量的接口是在gRT中的,它们并非一开始就可以使用,必须要到UEFI的DXE阶段才可以使用。

并且,也不是DXE阶段一开始就可以使用,从DxeMain.c中可以看到,最初的时候gRT表实际上是空的。

在DXE的运行过程中,会加载一个个的模块,来填满整个表。

对于变量,大致就是像下面这样的代码:

  SystemTable->RuntimeServices->GetVariable         = RuntimeServiceGetVariable;  SystemTable->RuntimeServices->GetNextVariableName = RuntimeServiceGetNextVariableName;  SystemTable->RuntimeServices->SetVariable         = RuntimeServiceSetVariable;  SystemTable->RuntimeServices->QueryVariableInfo   = RuntimeServiceQueryVariableInfo;  //  // Now install the Variable Runtime Architectural Protocol on a new handle  //  NewHandle = NULL;  Status = gBS->InstallMultipleProtocolInterfaces (                  &NewHandle,                  &gEfiVariableArchProtocolGuid,                  NULL,                  &gEfiVariableWriteArchProtocolGuid,                  NULL,                  NULL                  );
变量的初始化有不同的分类,分别在不同的模块中实现,并且只需要其中的一个模块运行就可以了。

下面是一个大致的模块分类(名称类似,但又可能不同):

1. EmuVariableRuntimeDxe.inf

这种形式的变量初始化模块的特点是,所有变量都存放在内存中,相当于前面提到的属性EFI_VARIABLE_NON_VOLATILE并没有什么作用。

2. VariableRuntimeDxe.inf

这里就是最一般的形式。

3. VariableSmmRuntimeDxe.inf

这种形式是一般形式的安全加强版,变量存放在非易失介质时需要通过SMM模式来完成,因此变量的有效性和可靠性会更高。


下面以EmuVariableRuntimeDxe.inf模块为例,说明变量的初始化。

简单来说,初始化分为三个部分:

1.    初始化mVariableModuleGlobal;

2.    为全局变量gRT赋值;

3.    安装相关的Protocol和事件(该事件用于物理地址到虚拟地址的转变)。

mVariableModuleGlobal的类型是ESAL_VARIABLE_GLOBAL,结构如下:

typedef struct {  VARIABLE_GLOBAL VariableGlobal[2];  UINTN           VolatileLastVariableOffset;  UINTN           NonVolatileLastVariableOffset;  UINTN           CommonVariableTotalSize;  UINTN           HwErrVariableTotalSize;  CHAR8           *PlatformLangCodes;  CHAR8           *LangCodes;  CHAR8           *PlatformLang;  CHAR8           Lang[ISO_639_2_ENTRY_SIZE + 1];} ESAL_VARIABLE_GLOBAL;

VariableGlobal是一个数组,它有两个元素,表示的分别是物理内存和虚拟内存,因为需要又对OS的支持,所以才会又虚拟内存这一块。

VARIABLE_GLOBAL的结构体如下:

typedef struct {  EFI_PHYSICAL_ADDRESS  VolatileVariableBase;  EFI_PHYSICAL_ADDRESS  NonVolatileVariableBase;  EFI_LOCK              VariableServicesLock;} VARIABLE_GLOBAL;

EFI_PHYSICAL_ADDRESS就是UINT64。

VariableServicesLock是一个用于变量的锁,它的类型EFI_LOCK的结构体如下:

typedef struct {  EFI_TPL         Tpl;  EFI_TPL         OwnerTpl;  EFI_LOCK_STATE  Lock;} EFI_LOCK;

EFI_TPL是UINTN,

EFI_LOCK_STATE有以下的几种状态:

typedef enum {  EfiLockUninitialized = 0,  EfiLockReleased      = 1,  EfiLockAcquired      = 2} EFI_LOCK_STATE;

不过关于锁的东西并不是这里需要关注的重点。

mVariableModuleGlobal的初始化位于VariableCommonInitialize(),具体的流程如下:


其中需要注意两个结构体:

////// Variable Store region header.///typedef struct {  ///  /// Variable store region signature.  ///  EFI_GUID  Signature;  ///  /// Size of entire variable store,  /// including size of variable store header but not including the size of FvHeader.  ///  UINT32  Size;  ///  /// Variable region format state.  ///  UINT8   Format;  ///  /// Variable region healthy state.  ///  UINT8   State;  UINT16  Reserved;  UINT32  Reserved1;} VARIABLE_STORE_HEADER;

这个是整个变量存储空间的头部。

然后每个变量都有一个自己的头部:

////// Single Variable Data Header Structure.///typedef struct {  ///  /// Variable Data Start Flag.  ///  UINT16      StartId;  ///  /// Variable State defined above.  ///  UINT8       State;  UINT8       Reserved;  ///  /// Attributes of variable defined in UEFI specification.  ///  UINT32      Attributes;  ///  /// Size of variable null-terminated Unicode string name.  ///  UINT32      NameSize;  ///  /// Size of the variable data without this header.  ///  UINT32      DataSize;  ///  /// A unique identifier for the vendor that produces and consumes this varaible.  ///  EFI_GUID    VendorGuid;} VARIABLE_HEADER;
初始化的内容就是这些,完成初始化后就可以使用变量的存取了。

之后介绍变量读写的实现。

GetVariable

GetVariable()的流程大致如下:


有几点需要注意:

1. 从RuntimeServiceGetVariable()到EmuGetVariable()多了一个参数:

EFI_STATUSEFIAPIRuntimeServiceGetVariable (  IN CHAR16        *VariableName,  IN EFI_GUID      *VendorGuid,  OUT UINT32       *Attributes OPTIONAL,  IN OUT UINTN     *DataSize,  OUT VOID         *Data  ){  return EmuGetVariable (          VariableName,          VendorGuid,          Attributes OPTIONAL,          DataSize,          Data,          &mVariableModuleGlobal->VariableGlobal[Physical]          );}

mVariableModuleGlobal->VariableGlobal[Physical],表示的是在UEFI下的物理地址。

2. FindVariable (VariableName, VendorGuid,&Variable, Global);函数中最重要的是第三个参数Variable,它也是函数的输出,它的结构如下:

typedef struct {  VARIABLE_HEADER *CurrPtr;  VARIABLE_HEADER *EndPtr;  VARIABLE_HEADER *StartPtr;  BOOLEAN         Volatile;} VARIABLE_POINTER_TRACK;

这个输出的结构反映了当前的变量保存区域。

当找到变量后(没找到关系也不大,下图以找到为例),上述的值如图中所示:


上述的立方体区域实际上有两个,一个存放易失变量一个存放非易失变量,Volatile根据在那块区域找到的来确定。

3. FindVariable()函数出入的VariableName参数如果是空的,而VendorGuid非空,则直接返回下一个非易失变量。

4. UpdateVariableInfo()部分是用来统计UEFI下的变量总体信息的,并不是变量实现的一部分,这里先不关注。


SetVariable

具体的流程如下:



0 0
原创粉丝点击