variable architecture protocol 是pi 规范中定义的架构协议。安装此协议的驱动是运行时驱动。
它负责注册variable 的运行时服务(variable Runtime Service). UEFI 规范定义了4个variable 运行时服务:
GetVariable, GetNextVariableName, SetVariable 和QueryVariableInfo.
SystemTable->RuntimeServices->GetVariable = VariableServiceGetVariable;SystemTable->RuntimeServices->GetNextVariableName = VariableServiceGetNextVariableName;SystemTable->RuntimeServices->SetVariable = VariableServiceSetVariable;SystemTable->RuntimeServices->QueryVariableInfo = VariableServiceQueryVariableInfo;//// Now install the Variable Runtime Architectural protocol on a new handle.//Status = gBS->InstallProtocolInterface (&mHandle,&gEfiVariableArchProtocolGuid,EFI_NATIVE_INTERFACE,NULL);ASSERT_EFI_ERROR (Status);
Variable Write Architecture Protocol 也是pi 规范中定义的架构协议。该协议的安装意味着非易失性变量(
non-volative variable) 读定的操作已经就绪。跟易失性变量(non - volative variable ) 读写的操作不同, non-volatile variable 读写需要借助于底层接口来操作非易失性存储人质, 如flash 芯片。
MdeModulePkg\Universal\Variable\RuntimeDxe\VariableDxe.c
//// Now install the Variable Runtime Architectural protocol on a new handle.//Status = gBS->InstallProtocolInterface (&mHandle,&gEfiVariableArchProtocolGuid,EFI_NATIVE_INTERFACE,NULL);ASSERT_EFI_ERROR (Status);//// Register FtwNotificationEvent () notify function.//EfiCreateProtocolNotifyEvent (&gEfiFaultTolerantWriteProtocolGuid,TPL_CALLBACK,FtwNotificationEvent,(VOID *)SystemTable,&mFtwRegistration);
注册Notify 函数FtwNotificationEvent(), 当gEfiFaultTolerantWriteProtocolGuid 安装,执行该函数。该函数通过对应的FVB Protocol 来封装底层非易失性存储介质的读写操作,最后,该函数安装Variable Write Architecture Protocol 来表示对Non volatile variable 的读写已经读绪。
Variable 相关关键数据结构
typedef struct {EFI_PHYSICAL_ADDRESS HobVariableBase;EFI_PHYSICAL_ADDRESS VolatileVariableBase;EFI_PHYSICAL_ADDRESS NonVolatileVariableBase;EFI_LOCK VariableServicesLock;UINT32 ReentrantState;BOOLEAN AuthFormat;BOOLEAN AuthSupport;BOOLEAN EmuNvMode;
} VARIABLE_GLOBAL;typedef struct {VARIABLE_GLOBAL VariableGlobal;UINTN VolatileLastVariableOffset;UINTN NonVolatileLastVariableOffset;UINTN CommonVariableSpace;UINTN CommonMaxUserVariableSpace;UINTN CommonRuntimeVariableSpace;UINTN CommonVariableTotalSize;UINTN CommonUserVariableTotalSize;UINTN HwErrVariableTotalSize;UINTN MaxVariableSize;UINTN MaxAuthVariableSize;UINTN MaxVolatileVariableSize;UINTN ScratchBufferSize;CHAR8 *PlatformLangCodes;CHAR8 *LangCodes;CHAR8 *PlatformLang;CHAR8 Lang[ISO_639_2_ENTRY_SIZE + 1];EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbInstance;
} VARIABLE_MODULE_GLOBAL;
VARIABLE_MODULE_GLOBAL 结构描述了全局分配的资源。其中, VARIABLE_GLOBAL结构的VolatileVariableBase
指向系统为易失性变量分配的内存空间;NonVolatileVariableBase 则是指向非易失性变量存储空间。 VolatileLastVariableOffset ,NonVolatileLastVariableOffset 分别用来记录各自空间中最后一个Variable 距离起始地址的偏移量。
VARIABLE_STORE_HEADER 数据结构
///
/// 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;
整个变量存储空间是以VARIABLE_STORE_HEADER 结构开始,该结构记录整个存储空间的特性。其中Signature 域为存储格式签名,size 域为存储空间的大小, VARIABLE_STORE_HEADER 结构之后,依次存放着各个变量。
MdeModulePkg\Universal\Variable\RuntimeDxe\Variable.c
//// Initialize Variable Specific Data.//mVariableModuleGlobal->VariableGlobal.VolatileVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VolatileVariableStore;mVariableModuleGlobal->VolatileLastVariableOffset = (UINTN) GetStartPointer (VolatileVariableStore) - (UINTN) VolatileVariableStore;CopyGuid (&VolatileVariableStore->Signature, VariableGuid);VolatileVariableStore->Size = PcdGet32 (PcdVariableStoreSize);VolatileVariableStore->Format = VARIABLE_STORE_FORMATTED;VolatileVariableStore->State = VARIABLE_STORE_HEALTHY;VolatileVariableStore->Reserved = 0;VolatileVariableStore->Reserved1 = 0;
Volatile 类型存储空间的VARIABLE_STORE_HEADER 的初始化。
VARIABLE_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;
每个变量的存储是以VARIABLE_HEADER 结构开始的,该结构描述了该变量的特性。每个变量都具有Name, Data 和Guid 成员,其中VARIABLE_HEADER 结构中NameSize 记录了该变量Name 的大小, DataSize 记录变变量的Data 的大小, VendorGuid 记录了该变量的guid 内容。
Variable 关键函数解析
MdeModulePkg\Universal\Variable\RuntimeDxe\Variable.c
EFI_STATUS
EFIAPI
VariableServiceSetVariable (IN CHAR16 *VariableName,IN EFI_GUID *VendorGuid,IN UINT32 Attributes,IN UINTN DataSize,IN VOID *Data)
1. 创建一个新的varibale 时,会将该Variable 的heade.State 设置成var_add;
2 .删除一个已经存在的variable 时,仅仅将该Variable 的Header.State 域与上VAR_DELETED;
3. 更新一个已经存在的variable 时,会将该Variable 的Header.State 域与上var_in_deleted_transition & var_deleted. 同时创建一个新的variable. 它的header.state 设置成var_added;
EFI_STATUS
EFIAPI
VariableServiceGetVariable (IN CHAR16 *VariableName,IN EFI_GUID *VendorGuid,OUT UINT32 *Attributes OPTIONAL,IN OUT UINTN *DataSize,OUT VOID *Data OPTIONAL)
VariableServiceGetVariable 用来查找某一变量。
EFI_STATUS
EFIAPI
VariableServiceGetNextVariableName (IN OUT UINTN *VariableNameSize,IN OUT CHAR16 *VariableName,IN OUT EFI_GUID *VendorGuid)
VariableServiceGetNextVariableName 用来返回下一个变量。
EFI_STATUS
Reclaim (IN EFI_PHYSICAL_ADDRESS VariableBase,OUT UINTN *LastVariableOffset,IN BOOLEAN IsVolatile,IN OUT VARIABLE_POINTER_TRACK *UpdatingPtrTrack,IN VARIABLE_HEADER *NewVariable,IN UINTN NewVariableSize)
通常,删除或更新一个已经存在的variable 时,将该variable 标识成DELETE 状态,但是它所占用的存储空间并没有释放出来,这样就会造成空间的浪费,因此此类Variable 被视为垃圾。
Reclaim 负责对variable 存储空间进行垃圾整理。
Reclaim 会遍历整个variable 存储空间,依次将所有有效的variable, 即header.State 域为var_added 或者var_in_deleted_transition & var_added. 读到内存中进行整理,然后再写回到variable 存储空间。
Runtime 相关的处理
作为运行时的服务,Variable Service 的API 需要能在OS 环境下被调用。因此,相关的全局指针需要在EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 时刻转化为虚拟地址。
Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,TPL_NOTIFY,VariableClassAddressChangeEvent,NULL,&gEfiEventVirtualAddressChangeGuid,&mVirtualAddressChangeEvent);ASSERT_EFI_ERROR (Status);//// Register the event handling function to reclaim variable for OS usage.//Status = EfiCreateEventReadyToBootEx (TPL_NOTIFY,OnReadyToBoot,NULL,&ReadyToBootEvent);ASSERT_EFI_ERROR (Status);
注册一个Notify 函数,将运行时所用到的指针转化成虚拟地址。
在ReadyToBoot 的时候,将非易失性变量存储空间垃圾进行整理,为os 环境下提供更多的存储空间。
Capsule Runtime Service
Capsule Runtime Service 介绍
应用程序或操作系统可以利用Capsule Service 与EFI 系统进行灵活的数据通信。典型的应用如: 调用者可以利用capsule service 进行固件升级(flash update); 操作系统可以利用capsule service 进行系统全面诊断。
Capsule Architecture Protocol 的安装
Capsule Architecture Protocol 是PI 规范中定义的架构协议。安装此协议的驱动是个运行时驱动(runtime Driver) , 它负责capsule 的运行时服务。 uefi 规范定义了2个capsule 运行时服务。 updateCapsule 和QueryCapsuleCapabilities 。
MdeModulePkg\Universal\CapsuleRuntimeDxe\CapsuleService.c
EFI_STATUS
EFIAPI
CapsuleServiceInitialize (IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_STATUS Status;mMaxSizePopulateCapsule = PcdGet32(PcdMaxSizePopulateCapsule);mMaxSizeNonPopulateCapsule = PcdGet32(PcdMaxSizeNonPopulateCapsule);//// When PEI phase is IA32, DXE phase is X64, it is possible that capsule data are// put above 4GB, so capsule PEI will transfer to long mode to get capsule data.// The page table and stack is used to transfer processor mode from IA32 to long mode.// Create the base address of page table and stack, and save them into variable.// This is not needed when capsule with reset type is not supported.//SaveLongModeContext ();//// Install capsule runtime services into UEFI runtime service tables.//gRT->UpdateCapsule = UpdateCapsule;gRT->QueryCapsuleCapabilities = QueryCapsuleCapabilities;//// Install the Capsule Architectural Protocol on a new handle// to signify the capsule runtime services are ready.//Status = gBS->InstallMultipleProtocolInterfaces (&mNewHandle,&gEfiCapsuleArchProtocolGuid,NULL,NULL);ASSERT_EFI_ERROR (Status);return Status;
}
2个Capsule 服务接口的注册。
Capsule Architecture Protocol 的安装。
Capsule 相关关键数据结构
EFI_CAPSULE_HEADER数据结构
MdePkg\Include\Uefi\UefiSpec.h
///
/// EFI Capsule Header.
///
typedef struct {////// A GUID that defines the contents of a capsule.///EFI_GUID CapsuleGuid;////// The size of the capsule header. This may be larger than the size of/// the EFI_CAPSULE_HEADER since CapsuleGuid may imply/// extended header entries///UINT32 HeaderSize;////// Bit-mapped list describing the capsule attributes. The Flag values/// of 0x0000 - 0xFFFF are defined by CapsuleGuid. Flag values/// of 0x10000 - 0xFFFFFFFF are defined by this specification///UINT32 Flags;////// Size in bytes of the capsule.///UINT32 CapsuleImageSize;
} EFI_CAPSULE_HEADER;
每个capsule 都是capsule header 和capsule body 两部分组成。 capsule header 遵循EFI_CAPSULE_HEADER结构。
capsule body 则是平台相关的, uefi 规范并未对此定义,通常实现为FV(Firmware Volume). EFI_CAPSULE_HEADER 结构描述了capsule 的特性, 其中capsuleGuid 用于标识capsule 身份,每个平台都可以通过该域来识别各自支持的capsule. Flags
域标识capsule 类型,该域说明capsule 需要什么样的处理, Flags 有如下定义:
#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000
#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000
#define CAPSULE_FLAGS_INITIATE_RESET 0x00040000
CAPSULE_FLAGS_PERSIST_ACROSS_RESET 表明Capsule 需要在系统重启后得到处理。
CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 表时capsule 需要系统在重启后将数据碎片(虚拟地址连续面物理地址可能不连续)整理成物理地址连续的内存,并安装在EFI SYSTEM TABLE 中,以供操作系统访问。一般该特性可被操作系统用用全面的系统诊断。
CAPSULE_FLAGS_INITIATE_RESET 表明Capsule 需要系统在系统重启后得到处理,并且由系统主动重启。
此外,如果Flags 为0 则表明Capsule 需要系统立即执行,无需系统重启。
注: 对于Capsule 而言,系统重启是指那些能保留内存数据不丢失的重启方式,如S3 等。