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 等。