UEFI下Variable的实现
最近在龙芯平台在调试Nvrom的存储功能,设置开机的启动项,设置开机密码等功能.这部分功能在标准的uefi中都是使用Variable
这套机制实现的.熟悉uefi的人都知道Variable这套机制在uefi中使用非常的广泛.现在我们来说说Variable是如何实现的.
首先:Variable在uefi的公共源码上是有相应的代码的,代码位于目录MdeModulePkg/Universal/Variable/RuntimeDxe和
MdeModulePkg/Universal/Variable/EmuRuntimeDxe这两个目录,第一个目录是实体机需要使用的,第二个目录是为虚拟机实现
的.一开始我们使用的是第二个,现在回过头来发现,使用的代码都不对,variable所有的变量都存储在了内存中,当掉电或者重
启后所有的数据都丢失了,需要重新写入.因此是不正确的.正常的环境变量也就是variable使用存储到flash中的,这样才能够保
存内容,掉电或者重启后数据不会丢失.在uefi中所有的环境变量都用variable来表示,系统统一使用gST->GetVariable和
gST->SetVariable来读和写一个Variable.注意这里gST是uefi中的Runtime阶段的一个全局的表.这两个函数是注册到这个表的.注
册的位置是在Vaiable的驱动模块中注册的,并不是DxeCore中那个表原有的函数地址.其实gST->SetVariable的实现是需要三个驱
动来完成的,并不是在Variable这个驱动就能实现的,Variable这是最上层的.下面就详细的讲讲gST->SetVariable的函数实现.
VariableRuntimeDxe:
主要依赖三个驱动,分别为VariableRuntimeDxe和FvBlockServiceDxe还有FlashOperationDxe
VariableRuntimeDxe主要实现一些必要的服务和初始化,以及设置Volatile类型和Non-Vloatile类型的Variable存储位置.
我们这里设置Volatile的变量放在了Runtime类型的一段内存中,目前龙芯平台在uefi下还不支持Runtime,所以后续这些变量还是需
要放到我们为固件保留的内存地址上.存放Non-Volatile的变量的位置就是flash 的地址,我们这里固件的起始64位地址是0x900000001fc00000,这个地址也就是flahs的其实地址,开头的位置存放了一些固件的描述信息和一些跳转指令,以及一些固件头的信息.这部分数据就是下面这个全局变量中的EFI_FIRMWARE_VOLUME_HEADER FvbInfo信息完全一致.
EFI_FVB_MEDIA_INFO mLoongsonPlatformFvbInfo = {
0x10000,
{
{
0xc1, 0x9f, 0x1f, 0x3c, 0x08, 0x00, 0xe0, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
},
gEfiSystemNvDataFvGuid,
0x10000,
EFI_FVH_SIGNATURE,
EFI_FVB2_MEMORY_MAPPED |
EFI_FVB2_READ_ENABLED_CAP |
EFI_FVB2_READ_STATUS |
EFI_FVB2_WRITE_ENABLED_CAP |
EFI_FVB2_WRITE_STATUS |
EFI_FVB2_ERASE_POLARITY |
EFI_FVB2_ALIGNMENT_16,
0x48,//sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
0x00, // CheckSum
0, // ExtHeaderOffset
{
0,
}, // Reserved[1]
2, // Revision
{
{
(FixedPcdGet32(PcdFlashNvStorageVariableSize))/FIRMWARE_BLOCK_SIZE,
FIRMWARE_BLOCK_SIZE
}
}
},
{
{
0,
0
}
}
};
typedef struct {
UINT64 FvLength;
EFI_FIRMWARE_VOLUME_HEADER FvbInfo;
EFI_FV_BLOCK_MAP_ENTRY End[1];
} EFI_FVB_MEDIA_INFO;
下面我们看一下VariableRuntimeDxe的入口函数都做了那些工作.代码如下:
EFI_STATUS
EFIAPI
VariableServiceInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
EFI_FVB_ATTRIBUTES_2 Attributes;
EFI_PHYSICAL_ADDRESS NvStorageVariableBase;
EFI_PHYSICAL_ADDRESS FvbBaseAddress;
UINTN HandleCount;
UINTN Index;
Fvb = NULL;
Status = EFI_NOT_FOUND;
DbgPrint(DEBUG_INFO,"Enter VariableServiceInitialize()\n");
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolumeBlockProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
DbgPrint(DEBUG_INFO,"Not Support gEfiFirmwareVolumeBlockProtocol\n");
return Status ;
}
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiFirmwareVolumeBlockProtocolGuid,
(VOID **) &Fvb
);
if (EFI_ERROR (Status)) {
Status = EFI_NOT_FOUND;
break;
}
Status = Fvb->GetAttributes (Fvb, &Attributes);
if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) {
continue;
}
Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
if (EFI_ERROR (Status)) {
continue;
}
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);
NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);
NvStorageVariableBase = NvStorageVariableBase + FwVolHeader->HeaderLength;
if ((NvStorageVariableBase >= FvbBaseAddress) && (NvStorageVariableBase < (FvbBaseAddress + FwVolHeader->FvLength))) {
Status = EFI_SUCCESS;
break;
}
}
FreePool (HandleBuffer);
if (!EFI_ERROR (Status) && Fvb != NULL) {
Status = VariableCommonInitialize (Fvb);
ASSERT_EFI_ERROR (Status);
SystemTable->RuntimeServices->GetVariable = RuntimeServiceGetVariable;
SystemTable->RuntimeServices->GetNextVariableName = RuntimeServiceGetNextVariableName;
SystemTable->RuntimeServices->SetVariable = RuntimeServiceSetVariable;
SystemTable->RuntimeServices->QueryVariableInfo = RuntimeServiceQueryVariableInfo;
Status = gBS->InstallMultipleProtocolInterfaces (
&mHandle,
&gEfiVariableArchProtocolGuid, NULL,
&gEfiVariableWriteArchProtocolGuid, NULL,
NULL
);
ASSERT_EFI_ERROR (Status);
}
EfiCreateProtocolNotifyEvent (
&gEfiFirmwareVolumeBlockProtocolGuid,
TPL_CALLBACK,
FvbNotificationEvent,
(VOID *)SystemTable,
&mFvbRegistration
);
DbgPrint(DEBUG_INFO,"return VariableServiceInitialize() (%r)\n",Status);
return EFI_SUCCESS;
}
上面的代码就是VariableRuntimeDxe入口函数,根据代码可知首先要找到gEfiFirmwareVolumeBlockProtocol,这个protocol就是Variable需要依赖的一个protocol因为在SetVariable的时候需要调用的就是gEfiFirmwareVolumeBlockProtocol中的Write函数.locate和这个protocol成功之后来获取存储Variable的地址,这个地址是从fdf文件中采用pcd的方式get出来的,这个地址就是我们前面说的地址0x900000001fc00000,然而VariableStoreHeader的地址就是跳过0x48头的长度就是对应的VariableStoreHeader的地址.我们这里预留了256个字节,第一个Variable的位置就是从0x900000001fc00148的位置开始存储.最终位置到0x900000001fc06000.这部分地址就是用来存放BIOS下面的Variable的内容的地址.当写满之后,需要将有用的信息拿出来然后清楚后,再写进去.这是入口函数主要的功能.
下面我们看一下GetVariable和SetVariable的函数,这两个函数是在BIOS阶段经常使用的
gST->GetVariable();
EFI_STATUS
EFIAPI
RuntimeServiceGetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
OUT UINT32 *Attributes OPTIONAL,
IN OUT UINTN *DataSize,
OUT VOID *Data
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
UINTN VarDataSize;
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"Enter RuntimeServiceGetVariable()\n");
DbgPrint(DEBUG_INFO,"GetVariable:Name=%sx\n",VariableName);
#endif
if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) {
return EFI_INVALID_PARAMETER;
}
AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);
//
// Find existing variable
//
Status = FindVariableInCache (VariableName, VendorGuid, Attributes, DataSize, Data);
if ((Status == EFI_BUFFER_TOO_SMALL) || (Status == EFI_SUCCESS)){
UpdateVariableInfo (VariableName, VendorGuid, FALSE, TRUE, FALSE, FALSE, TRUE);
goto Done;
}
//Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal);
Status = FindNonVolatileVariable(VariableName, VendorGuid, &Variable);
if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) {
Status = FindVolatileVariable(VariableName, VendorGuid, &Variable);
}
if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) {
goto Done;
}
//
// Get data size
//
VarDataSize = DataSizeOfVariable (Variable.CurrPtr);
ASSERT (VarDataSize != 0);
if (*DataSize >= VarDataSize) {
if (Data == NULL) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
CopyMem (Data, GetVariableDataPtr (Variable.CurrPtr), VarDataSize);
if (Attributes != NULL) {
*Attributes = Variable.CurrPtr->Attributes;
}
*DataSize = VarDataSize;
UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE);
UpdateVariableMem (VariableName, VendorGuid, Variable.CurrPtr->Attributes, VarDataSize, Data);
Status = EFI_SUCCESS;
goto Done;
} else {
*DataSize = VarDataSize;
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
Done:
ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"Return RuntimeServiceGetVariable() (%r)\n",Status);
#endif
return Status;
}
上面的函数就是GetVariable的函数实现,主要就是从FindVariable函数中来找Variable,这里为了提高查找的效率将FindVariable分成了两个函数FindVolatileVariable()和FindNonVolatileVariable(),因为在Get一个Variable的时候是通过名字和Guid来寻找的,并没有属性,所以只能先去NonVolatile的存储地方去找,存储的地址可以是0x900000001fc00048的地址也可以是这段地址的数据拷贝的内存地址.在上面的VariableRuntimeDxe的入口函数中的VariableCommonInitialize()函数中,会将0x900000001fc00048为起始地址拷贝0x6000大小的数据到内存,这段内存的数据是作为flash存放Variable数据的一段映射.这里的数据需要保持一致,我们在添加一个Variable的时候flash里面的数据会写,然后将这段数据更新到对应的内存地址上.回到GetVariable的过程,在FindVariable的过程中找到之后,Variable.CurrPtr指针就不会为空,这样就拿到了这个Variable头的地址,通过头的地址就可以拿到想要的数据.然后依次获取数据,和属性.这里需要注意,GetVariable的过程由于龙芯平台整个IO空间和内存地址空间是统一编址的,所以访问IO设备像Flash这种读的时候,直接可以使用类似读内存数据的方法来读,但是写必须要调用Flash的驱动函数去写才能写入.这也算龙芯平台的一个特点.如果使用写内存数据的方式去写Flash数据写不进去,但是也不会出现其他的问题.这点需要在注意.
gST->SetVariable():
EFI_STATUS
EFIAPI
RuntimeServiceSetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
)
{
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
VARIABLE_HEADER *NextVariable;
EFI_PHYSICAL_ADDRESS Point;
//
// Check input parameters
//
if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {
return EFI_INVALID_PARAMETER;
}
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"Enter RuntimeServiceSetVariable()\n");
DbgPrint(DEBUG_INFO,"SetVariable Name:%s,DataSize=0x%lx,Data=0x%lx\n",VariableName,DataSize,Data);
#endif
if (DataSize != 0 && Data == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Not support authenticated variable write yet.
//
if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
return EFI_INVALID_PARAMETER;
}
//
// Make sure if runtime bit is set, boot service bit is set also
//
if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {
return EFI_INVALID_PARAMETER;
}
if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {
return EFI_INVALID_PARAMETER;
}
if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
if ((DataSize > PcdGet32 (PcdMaxHardwareErrorVariableSize)) ||
(sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > PcdGet32 (PcdMaxHardwareErrorVariableSize))) {
return EFI_INVALID_PARAMETER;
}
//
// According to UEFI spec, HARDWARE_ERROR_RECORD variable name convention should be L"HwErrRecXXXX"
//
if (StrnCmp(VariableName, L"HwErrRec", StrLen(L"HwErrRec")) != 0) {
return EFI_INVALID_PARAMETER;
}
} else {
//
// The size of the VariableName, including the Unicode Null in bytes plus
// the DataSize is limited to maximum size of PcdGet32 (PcdMaxVariableSize) bytes.
//
if ((DataSize > PcdGet32 (PcdMaxVariableSize)) ||
(sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > PcdGet32 (PcdMaxVariableSize))) {
return EFI_INVALID_PARAMETER;
}
}
AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);
//
// Consider reentrant in MCA/INIT/NMI. It needs be reupdated;
//
if (1 < InterlockedIncrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState)) {
Point = mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase;
//
// get last variable offset
//
NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *) (UINTN) Point);
while ((NextVariable < GetEndPointer ((VARIABLE_STORE_HEADER *) (UINTN) Point)) && IsValidVariableHeader (NextVariable)) {
NextVariable = GetNextVariablePtr (NextVariable);
}
mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) NextVariable - (UINTN) Point;
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"mVariableModuleGlobal->NonVolatileLastVariableOffset=0x%llx\n",mVariableModuleGlobal->NonVolatileLastVariableOffset);
#endif
}
//Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal);
if( (Attributes & EFI_VARIABLE_NON_VOLATILE) != 0){
Status = FindNonVolatileVariable(VariableName, VendorGuid, &Variable);
}else{
Status = FindVolatileVariable(VariableName, VendorGuid, &Variable);
}
DbgPrint(DEBUG_INFO,"Find Variable Name %s %r \n",VariableName,Status);
Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, &Variable);
InterlockedDecrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState);
ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"Return RuntimeServiceSetVariable() (%r)\n",Status);
#endif
return Status;
}
上面的代码就是SetVariabel的代码,由上面的代码可知,函数的入参为 VariableName, *VendorGuid,Attributes,DataSize, *Data这几个参数都是要使用的,首先检查函数入参的合法性,都过了之后,就根据属性去FindVariable,Volatile的Variable就去Volatile的地址去找,NonVolatile的就去Flash获取对应的映射的内存地址去找,找到之后到后面的UpdataVariable函数就会走相对应的更新一个已经存在的Variable的函数流程,如果没有找到就会走添加一个新的Variable的函数流程.下面我们来看一下UpdataVariable的函数流程.
EFI_STATUS
EFIAPI
UpdateVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VOID *Data,
IN UINTN DataSize,
IN UINT32 Attributes OPTIONAL,
IN VARIABLE_POINTER_TRACK *CacheVariable
)
{
EFI_STATUS Status;
VARIABLE_HEADER *NextVariable;
UINTN ScratchSize;
UINTN NonVolatileVarableStoreSize;
UINTN VarNameOffset;
UINTN VarDataOffset;
UINTN VarNameSize;
UINTN VarSize;
BOOLEAN Volatile;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
UINT8 State;
BOOLEAN Reclaimed;
VARIABLE_POINTER_TRACK *Variable;
VARIABLE_POINTER_TRACK NvVariable;
VARIABLE_STORE_HEADER *VariableStoreHeader;
UINTN CacheOffset;
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"Enter UpdateVariable ().\n");
#endif
if ((mVariableModuleGlobal->FvbInstance == NULL) && ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)) {
return EFI_NOT_AVAILABLE_YET;
}
if ((CacheVariable->CurrPtr == NULL) || CacheVariable->Volatile) {
Variable = CacheVariable;
} else {
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);
Variable = &NvVariable;
Variable->StartPtr = GetStartPointer (VariableStoreHeader);
Variable->EndPtr = GetEndPointer (VariableStoreHeader);
Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr));
Variable->Volatile = FALSE;
}
Fvb = mVariableModuleGlobal->FvbInstance;
Reclaimed = FALSE;
if (Variable->CurrPtr != NULL) {
//
// Update/Delete existing variable
//
#if VARIABLE_DEBUG
DEBUG((EFI_D_ERROR, "Updatealready exist Variable %s.\n",VariableName));
#endif
if (EfiAtRuntime ()) {
if (Variable->Volatile) {
Status = EFI_WRITE_PROTECTED;
goto Done;
}
if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
}
//
// Setting a data variable with no access, or zero DataSize attributes specified causes it to be deleted.
//
if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {
State = Variable->CurrPtr->State;
State &= VAR_DELETED;
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
Variable->Volatile,
FALSE,
Fvb,
(UINTN) &Variable->CurrPtr->State,
sizeof (UINT8),
&State
);
if (!EFI_ERROR (Status)) {
UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE);
if (!Variable->Volatile) {
CacheVariable->CurrPtr->State = State;
}
}
goto Done;
}
//
// If the variable is marked valid and the same data has been passed in
// then return to the caller immediately.
//
if (DataSizeOfVariable (Variable->CurrPtr) == DataSize &&
(CompareMem (Data, GetVariableDataPtr (Variable->CurrPtr), DataSize) == 0)) {
UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE);
Status = EFI_SUCCESS;
goto Done;
} else if ((Variable->CurrPtr->State == VAR_ADDED) ||
(Variable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {
//
// Mark the old variable as in delete transition
//
State = Variable->CurrPtr->State;
State &= VAR_IN_DELETED_TRANSITION;
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
Variable->Volatile,
FALSE,
Fvb,
(UINTN) &Variable->CurrPtr->State,
sizeof (UINT8),
&State
);
if (EFI_ERROR (Status)) {
goto Done;
}
if (!Variable->Volatile) {
CacheVariable->CurrPtr->State = State;
}
}
} else {
#if VARIABLE_DEBUG
DEBUG((EFI_D_ERROR, "Creat new variable name = %s\n",VariableName));
#endif
//
// Not found existing variable. Create a new variable
//
if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {
Status = EFI_NOT_FOUND;
goto Done;
}
if (EfiAtRuntime () &&
(((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
}
//
// Function part - create a new variable and copy the data.
// Both update a variable and create a variable will come here.
//
NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase));
ScratchSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize));
SetMem (NextVariable, ScratchSize, 0xff);
NextVariable->StartId = VARIABLE_DATA;
NextVariable->Attributes = Attributes;
//
// NextVariable->State = VAR_ADDED;
//
NextVariable->Reserved = 0;
VarNameOffset = sizeof (VARIABLE_HEADER);
VarNameSize = StrSize (VariableName);
CopyMem (
(UINT8 *) ((UINTN) NextVariable + VarNameOffset),
VariableName,
VarNameSize
);
VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);
CopyMem (
(UINT8 *) ((UINTN) NextVariable + VarDataOffset),
Data,
DataSize
);
CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));
//
// There will be pad bytes after Data, the NextVariable->NameSize and
// NextVariable->DataSize should not include pad size so that variable
// service can get actual size in GetVariable
//
NextVariable->NameSize = (UINT32)VarNameSize;
NextVariable->DataSize = (UINT32)DataSize;
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"NextVariable->DataSize=0x%llx\n",NextVariable->DataSize);
DbgPrint(DEBUG_INFO,"NextVariable->NameSize=0x%llx\n",NextVariable->NameSize);
#endif
VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
Volatile = FALSE;
NonVolatileVarableStoreSize = ((VARIABLE_STORE_HEADER *)(UINTN)(mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase))->Size;
if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0)
&& ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))
|| (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0)
&& ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - RECLAIM_SIZE_OFFSET - VAR_STORE_HEAD_OFFSET - PcdGet32 (PcdHwErrStorageSize)))) {
if (EfiAtRuntime ()) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
Status = Reclaim (mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,
&mVariableModuleGlobal->NonVolatileLastVariableOffset, FALSE, Variable->CurrPtr);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// If still no enough space, return out of resources
//
if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0)
&& ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))
|| (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0)
&& ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - RECLAIM_SIZE_OFFSET - VAR_STORE_HEAD_OFFSET - PcdGet32 (PcdHwErrStorageSize)))) {
Status = EFI_OUT_OF_RESOURCES;
DEBUG_SHOW_ALL_VAR (mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);
goto Done;
}
Reclaimed = TRUE;
}
//
// Three steps
// 1. Write variable header
// 2. Set variable state to header valid
// 3. Write variable data
// 4. Set variable state to valid
//
//
// Step 1:
//
CacheOffset = mVariableModuleGlobal->NonVolatileLastVariableOffset;
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"mVariableModuleGlobal->NonVolatileLastVariableOffset=0x%llx (%a)[%d]\n",mVariableModuleGlobal->NonVolatileLastVariableOffset,__FUNCTION__,__LINE__);
#endif
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
FALSE,
TRUE,
Fvb,
mVariableModuleGlobal->NonVolatileLastVariableOffset,
sizeof (VARIABLE_HEADER),
(UINT8 *) NextVariable
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Step 2:
//
NextVariable->State = VAR_HEADER_VALID_ONLY;
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
FALSE,
TRUE,
Fvb,
mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),
sizeof (UINT8),
&NextVariable->State
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Step 3:
//
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
FALSE,
TRUE,
Fvb,
mVariableModuleGlobal->NonVolatileLastVariableOffset + sizeof (VARIABLE_HEADER),
(UINT32) VarSize - sizeof (VARIABLE_HEADER),
(UINT8 *) NextVariable + sizeof (VARIABLE_HEADER)
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Step 4:
//
NextVariable->State = VAR_ADDED;
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
FALSE,
TRUE,
Fvb,
mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),
sizeof (UINT8),
&NextVariable->State
);
if (EFI_ERROR (Status)) {
goto Done;
}
mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize);
if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {
mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VarSize);
} else {
mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize);
}
//
// update the memory copy of Flash region.
//
CopyMem ((UINT8 *)mNvNonVariableMem + CacheOffset, (UINT8 *)NextVariable, VarSize);
} else {
//
// Create a volatile variable
//
Volatile = TRUE;
if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >
((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size) {
//
// Perform garbage collection & reclaim operation
//
Status = Reclaim (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase,
&mVariableModuleGlobal->VolatileLastVariableOffset, TRUE, Variable->CurrPtr);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// If still no enough space, return out of resources
//
if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >
((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size
) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
Reclaimed = TRUE;
}
NextVariable->State = VAR_ADDED;
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
TRUE,
TRUE,
Fvb,
mVariableModuleGlobal->VolatileLastVariableOffset,
(UINT32) VarSize,
(UINT8 *) NextVariable
);
if (EFI_ERROR (Status)) {
goto Done;
}
mVariableModuleGlobal->VolatileLastVariableOffset += HEADER_ALIGN (VarSize);
}
//
// Mark the old variable as deleted
//
if (!Reclaimed && !EFI_ERROR (Status) && Variable->CurrPtr != NULL) {
State = Variable->CurrPtr->State;
State &= VAR_DELETED;
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
Variable->Volatile,
FALSE,
Fvb,
(UINTN) &Variable->CurrPtr->State,
sizeof (UINT8),
&State
);
if (!EFI_ERROR (Status) && !Variable->Volatile) {
CacheVariable->CurrPtr->State = State;
}
}
if (!EFI_ERROR (Status)) {
UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE);
UpdateVariableMem (VariableName, VendorGuid, Attributes, DataSize, Data);
}
Done:
#if VARIABLE_DEBUG
DbgPrint(DEBUG_INFO,"mVariableModuleGlobal->NonVolatileLastVariableOffset=0x%llx (%a)[%d]\n",mVariableModuleGlobal->NonVolatileLastVariableOffset,__FUNCTION__,__LINE__);
DbgPrint(DEBUG_INFO,"Return UpdateVariable ().\n");
#endif
return Status;
}
从上面的代码可以,这里包含了更新一个已经存在的和不存在创建新的Variable的过程,同时也包含了Volatile和NonVolatile的过程.详细的过程这里就不介绍了,只介绍几个要点.第一,创建一个NonVolatile的过程要分四部,首先创建好Variable的头,也就是VariableHeader的结构填内容,VariableHeader就是下面的结构体:typedef struct {
UINT16 StartId;
UINT8 State;
UINT8 Reserved;
UINT32 Attributes;
UINT32 NameSize;
UINT32 DataSize;
EFI_GUID VendorGuid;
} VARIABLE_HEADER;
为这些数据初始化完后,将这部分头先写入到Flash中,然后第二步更新头的中的State位,让这位变成有效.第三步:写Variable的数据,最后再次更新投中的State位为VAR_ADDED.也就是创建一个新的NonVolatile的数据要写付flash四次,写入flash就是调用的前面locate出来的FirmwareVolumeBlockProtocol中的write函数,这个函数最底层就是FlashDeviceOperationProtocl的写函数,后面再介绍FirmwareVolumeBlockProtocol的实现.在写入的过程中最主要的就是Offset这个地址,这个地址到FlashDeviceOperationProtocl那层是基于0x900000001fc000000的偏移地址,而现在传的地址是64位的虚拟地址.头的地址就是
mVariableModuleGlobal->NonVolatileLastVariableOffset这个全局地址,每一次写入一个Variable都会更新这个地址,为下一次写入Variable的起始地址. mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize);就是在更新他的地址.重启后首先还是要获取这个值,这个值的获取是需要遍历0x900000001fc0048为起始地址的所有Variable,然后得到最后一个地址,算出来下一次要写的起始地址.这部分代码也在上面的入口函数中执行的.还有一点需要注意,VariableRuntimeDxe阶段是做了BIOS下NvRom下满后Reclaim处理的函数的,但是由于时间问题,这部分功能我们还没有调试,在前面调试的过程中主要遇到的问题就是这个VariableRuntimeDxe所依赖的驱动以及这个驱动中涉及的Variable是如何存储的,以及以什么方式存.当更新一个Variabel的时候,是需要将已经存在的Variable的State位无效掉,然后再重新写一个Variable.这也是为何会写满的原因,在启动过程中有些Variable是需要更新的,以前存在的就会无效掉.在整个VariableRuntimeDxe的驱动中,有两个很重要的全局变量,一个是VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal = NULL,这个变量中的每个数据都是有用的,下面看一下这个变量的类型
typedef struct {
VARIABLE_GLOBAL VariableGlobal;
UINTN VolatileLastVariableOffset;
UINTN NonVolatileLastVariableOffset;
UINTN CommonVariableTotalSize;
UINTN HwErrVariableTotalSize;
CHAR8 *PlatformLangCodes;//[256]; //Pre-allocate 256 bytes space to accommodate the PlatformlangCodes.
CHAR8 *LangCodes;//[256]; //Pre-allocate 256 bytes space to accommodate the langCodes.
CHAR8 *PlatformLang;//[8]; //Pre-allocate 8 bytes space to accommodate the Platformlang variable.
CHAR8 Lang[4]; //Pre-allocate 4 bytes space to accommodate the lang variable.
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbInstance;
} VARIABLE_MODULE_GLOBAL;
里面的变量都是在驱动的入口函数中初始化的.这个变量也是在入口函数中初始化的.最后所有VariableRuntimeDxe所需要的所有的服务都注册好之后,需要Install gEfiVariableArchProtocolGuid和gEfiVariableWriteArchProtocolGuid这两个Protocol,否则在后续运行的时候,需要这两个protoco但是locate不到的时候就会出现挂起的现象.
Varoable的Reclaim过程
在启动过程中,一些Variable 知识一次性有效的,机器重启后就无效了,就需要再次写入flash,这样重启的次数多了就会出现Variable区域写满的情况.我们这里为Variable区域预留了24K的空间来存放variable变量.那么在写满之后是如何处理的呢?这个逻辑是在SetVariable的函数中添加的,在写一个Variable的时候(无论是创建一个新的Variable还是更新一个已经存在的Variable)当发现满足一下条件的时候
if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0)
&& ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))
|| (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0)
&& ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - RECLAIM_SIZE_OFFSET - VAR_STORE_HEAD_OFFSET - PcdGet32 (PcdHwErrStorageSize)))) {
这个条件是Non-Volatile类型的Variable需要满足的条件,其实上面的条件中Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD始终是为0的,也就是满足VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - RECLAIM_SIZE_OFFSET - VAR_STORE_HEAD_OFFSET - PcdGet32 (PcdHwErrStorageSize)条件即可,这个条件说白了就是看是否现在要写入的Variable的长度加上已经一些的variable的长度是不是超过了我们预留的大小,如果超过了那么就需要执行Reclaim函数.如果是Volatile类型的Variable其原理是一样的,都是调用Reclaim函数.下面我们看一下这个Reclaim函数.其代码如下:
EFI_STATUS
Reclaim (
IN EFI_PHYSICAL_ADDRESS VariableBase,
OUT UINTN *LastVariableOffset,
IN BOOLEAN IsVolatile,
IN VARIABLE_HEADER *UpdatingVariable
)
{
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *AddedVariable;
VARIABLE_HEADER *NextVariable;
VARIABLE_HEADER *NextAddedVariable;
VARIABLE_STORE_HEADER *VariableStoreHeader;
UINT8 *ValidBuffer;
UINTN MaximumBufferSize;
UINTN VariableSize;
UINTN VariableNameSize;
UINTN UpdatingVariableNameSize;
UINTN NameSize;
UINT8 *CurrPtr;
VOID *Point0;
VOID *Point1;
BOOLEAN FoundAdded;
EFI_STATUS Status;
CHAR16 *VariableNamePtr;
CHAR16 *UpdatingVariableNamePtr;
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) VariableBase);
//
// recaluate the total size of Common/HwErr type variables in non-volatile area.
//
if (!IsVolatile) {
mVariableModuleGlobal->CommonVariableTotalSize = 0;
mVariableModuleGlobal->HwErrVariableTotalSize = 0;
}
//
// Start Pointers for the variable.
//
Variable = GetStartPointer (VariableStoreHeader);
MaximumBufferSize = VAR_STORE_HEAD_OFFSET;
while (IsValidVariableHeader (Variable)) {
NextVariable = GetNextVariablePtr (Variable);
if (Variable->State == VAR_ADDED ||
Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)
) {
VariableSize = (UINTN) NextVariable - (UINTN) Variable;
MaximumBufferSize += VariableSize;
}
Variable = NextVariable;
}
//
// Reserve the 1 Bytes with Oxff to identify the
// end of the variable buffer.
//
MaximumBufferSize += 1;
ValidBuffer = AllocatePool (MaximumBufferSize);
if (ValidBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
SetMem (ValidBuffer, MaximumBufferSize, 0xff);
//
// Copy variable store header
//
CopyMem (ValidBuffer, VariableStoreHeader, VAR_STORE_HEAD_OFFSET);
CurrPtr = (UINT8 *) GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer);
//
// Reinstall all ADDED variables as long as they are not identical to Updating Variable
//
Variable = GetStartPointer (VariableStoreHeader);
while (IsValidVariableHeader (Variable)) {
NextVariable = GetNextVariablePtr (Variable);
if (Variable->State == VAR_ADDED) {
if (UpdatingVariable != NULL) {
if (UpdatingVariable == Variable) {
Variable = NextVariable;
continue;
}
VariableNameSize = NameSizeOfVariable(Variable);
UpdatingVariableNameSize = NameSizeOfVariable(UpdatingVariable);
VariableNamePtr = GetVariableNamePtr (Variable);
UpdatingVariableNamePtr = GetVariableNamePtr (UpdatingVariable);
if (CompareGuid (&Variable->VendorGuid, &UpdatingVariable->VendorGuid) &&
VariableNameSize == UpdatingVariableNameSize &&
CompareMem (VariableNamePtr, UpdatingVariableNamePtr, VariableNameSize) == 0 ) {
Variable = NextVariable;
continue;
}
}
VariableSize = (UINTN) NextVariable - (UINTN) Variable;
CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize);
CurrPtr += VariableSize;
if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
} else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
}
}
Variable = NextVariable;
}
//
// Reinstall the variable being updated if it is not NULL
//
if (UpdatingVariable != NULL) {
VariableSize = (UINTN)(GetNextVariablePtr (UpdatingVariable)) - (UINTN)UpdatingVariable;
CopyMem (CurrPtr, (UINT8 *) UpdatingVariable, VariableSize);
CurrPtr += VariableSize;
if ((!IsVolatile) && ((UpdatingVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
} else if ((!IsVolatile) && ((UpdatingVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
}
}
//
// Reinstall all in delete transition variables
//
Variable = GetStartPointer (VariableStoreHeader);
while (IsValidVariableHeader (Variable)) {
NextVariable = GetNextVariablePtr (Variable);
if (Variable != UpdatingVariable && Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) {
//
// Buffer has cached all ADDED variable.
// Per IN_DELETED variable, we have to guarantee that
// no ADDED one in previous buffer.
//
FoundAdded = FALSE;
AddedVariable = GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer);
while (IsValidVariableHeader (AddedVariable)) {
NextAddedVariable = GetNextVariablePtr (AddedVariable);
NameSize = NameSizeOfVariable (AddedVariable);
if (CompareGuid (&AddedVariable->VendorGuid, &Variable->VendorGuid) &&
NameSize == NameSizeOfVariable (Variable)
) {
Point0 = (VOID *) GetVariableNamePtr (AddedVariable);
Point1 = (VOID *) GetVariableNamePtr (Variable);
if (CompareMem (Point0, Point1, NameSizeOfVariable (AddedVariable)) == 0) {
FoundAdded = TRUE;
break;
}
}
AddedVariable = NextAddedVariable;
}
if (!FoundAdded) {
//
// Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED
//
VariableSize = (UINTN) NextVariable - (UINTN) Variable;
CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize);
((VARIABLE_HEADER *) CurrPtr)->State = VAR_ADDED;
CurrPtr += VariableSize;
if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
} else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
}
}
}
Variable = NextVariable;
}
if (IsVolatile) {
//
// If volatile variable store, just copy valid buffer
//
SetMem ((UINT8 *) (UINTN) VariableBase, VariableStoreHeader->Size, 0xff);
CopyMem ((UINT8 *) (UINTN) VariableBase, ValidBuffer, (UINTN) (CurrPtr - (UINT8 *) ValidBuffer));
Status = EFI_SUCCESS;
} else {
//
// If non-volatile variable store, perform FTW here.
//
Status = ReclaimUpdateNvStore (
(UINT8 *) ValidBuffer,
(UINT8 *) GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer),
(UINT8 *) (CurrPtr) + 1
);
CopyMem (mNvNonVariableMem, (CHAR8 *)(UINTN)VariableBase, VariableStoreHeader->Size);
}
if (!EFI_ERROR (Status)) {
*LastVariableOffset = (UINTN) (CurrPtr - (UINT8 *) ValidBuffer);
} else {
ASSERT(0);
*LastVariableOffset = 0;
}
FreePool (ValidBuffer);
return Status;
}
首先函数入参:
VariableBase就是存放Variable_store_header的地址,如果是non-volatile的类型的,那么就是non-volatile的头地址,volatile的同样就是volatile variable_store-header的地址.
LastVariableOffset就是上次写入的头的起始地址,也就是全局变量mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase或者
mVariableModuleGlobal->VariableGlobal.VolatileVariableBase的值
IsVolatile表示是否是volatile的variable也就是variable的类型.
UpdatingVariable就是代码本次要写入的varibale.
根据函数第一个参数就能获取到第一个variable的头,然后就一次变量所有的variable,检测其state状态,如果是有效的,就将这个variable保留下来,这样就获取到了之前写过的variable哪些是需要再次写入的,无效的就直接不要了.然后调用函数ReclaimUpdateNvStore来删除整个variable区域,再将有效的variable再写回去.下面是函数ReclaimUpdateNvStore的代码:
EFI_STATUS
ReclaimUpdateNvStore (
IN UINT8 *StoreHeader,
IN UINT8 *VariableStart,
IN UINT8 *VariableEnd
)
{
EFI_STATUS Status;
UINTN StartOffset;
UINTN EndOffset;
UINTN Offset;
UINTN BlockSize;
EFI_LBA LbaIndex;
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
EFI_PHYSICAL_ADDRESS FvVolHdr;
//
// Get offset for Non-volatile range
//
StartOffset = (UINTN) VariableStart - (UINTN) StoreHeader;
EndOffset = ((VARIABLE_STORE_HEADER *) (UINTN) (mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase))->Size;
Offset = StartOffset;
//
// Get block size
//
Status = mVariableModuleGlobal->FvbInstance->GetPhysicalAddress(mVariableModuleGlobal->FvbInstance, &FvVolHdr);
ASSERT_EFI_ERROR (Status);
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvVolHdr);
BlockSize = FwVolHeader->BlockMap->Length;
//
// Erase flash
//
while (Offset < EndOffset) {
LbaIndex = (EFI_LBA) (Offset / BlockSize);
mVariableModuleGlobal->FvbInstance->EraseBlocks (
mVariableModuleGlobal->FvbInstance,
LbaIndex,
1,
EFI_LBA_LIST_TERMINATOR
);
Offset += ERASE_ONE_BLOCK_SIZE;
}
//
// Re-write flash
//
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableGlobal,
FALSE,
TRUE,
mVariableModuleGlobal->FvbInstance,
StartOffset,
(UINTN) VariableEnd - (UINTN) VariableStart,
(UINT8 *) VariableStart
);
ASSERT_EFI_ERROR (Status);
return Status;
}
上面的代码就包含了擦除和重新写入的过程.这里不再详细的介绍了.这里需要注意一下,无效一个variable只需要将setvariable函数的入参中的Datasize=0x0,DataBuff=NULL,即可.这样调用一次setvariable就会将这个variable无效掉.
更多推荐
所有评论(0)