最近在龙芯平台在调试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_DEBUGDbgPrint(DEBUG_INFO,"Enter RuntimeServiceGetVariable()\n");DbgPrint(DEBUG_INFO,"GetVariable:Name=%sx\n",VariableName);
#endifif (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_DEBUGDbgPrint(DEBUG_INFO,"Return RuntimeServiceGetVariable() (%r)\n",Status);
#endifreturn 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_DEBUGDbgPrint(DEBUG_INFO,"Enter RuntimeServiceSetVariable()\n");                                                                                                                            DbgPrint(DEBUG_INFO,"SetVariable Name:%s,DataSize=0x%lx,Data=0x%lx\n",VariableName,DataSize,Data);
#endifif (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_DEBUGDbgPrint(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_DEBUGDbgPrint(DEBUG_INFO,"Return RuntimeServiceSetVariable() (%r)\n",Status);
#endifreturn 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_DEBUGDbgPrint(DEBUG_INFO,"Enter UpdateVariable ().\n");
#endifif ((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_DEBUGDEBUG((EFI_D_ERROR, "Updatealready exist Variable %s.\n",VariableName));
#endifif (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_DEBUGDEBUG((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_DEBUGDbgPrint(DEBUG_INFO,"NextVariable->DataSize=0x%llx\n",NextVariable->DataSize);DbgPrint(DEBUG_INFO,"NextVariable->NameSize=0x%llx\n",NextVariable->NameSize);
#endifVarSize = 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_DEBUGDbgPrint(DEBUG_INFO,"mVariableModuleGlobal->NonVolatileLastVariableOffset=0x%llx (%a)[%d]\n",mVariableModuleGlobal->NonVolatileLastVariableOffset,__FUNCTION__,__LINE__);
#endifStatus = 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_DEBUGDbgPrint(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无效掉.

UEFI下Variable的实现相关推荐

  1. UEFI下windows启动过程

    引导文件  在UEFI安装完操作系统后,Windows至少使用两个分区,一个叫做ESP分区(EFI SYSTEM PARTITION),用于存放启动文件,另一个则是BIOS下正常的系统分区,不同的是, ...

  2. UEFI下Windows引导过程

    引导文件 在UEFI安装完操作系统后,Windows至少使用两个分区,一个叫做ESP分区(EFI SYSTEM PARTITION),用于存放启动文件,另一个则是BIOS下正常的系统分区,不同的是,B ...

  3. UEFI下的Gmac驱动实现

    1.mac的分类: mac按照传输的速率可以分为 emac 和 gmac. mac: 它是一个controller,它的主要的作用有两个方面:帧发送: 接受来自协议层的数据,加上控制信息,然后以位数据 ...

  4. uefi下的开机顺序_科普贴:BIOS和UEFI的启动项

    先插一句话,现在很多人用UEFI BIOS这个称呼.这里为了区分:BIOS一律指传统BIOS, UEFI BIOS一律称呼为UEFI. UEFI下的BIOS设置,一律称为UEFI设置. 写这篇的原因 ...

  5. 电脑引导那些事(2)--UEFI下win8.1咋改win7,咋装双系统,咋不能激活?

    前言 上篇已经说明了电脑引导两种方式,本篇详细说说我在帮别人装系统中得到的一些经验.对于现在UEFI主板的电脑装系统我们不能总是格了硬盘换成MBR分区表,然后按照传统的方式安装系统.我们要学习新的技术 ...

  6. linux 分区 GUID 8304,UEFI下安装Archlinux

    一.无线网线配置 1.检查无线网卡驱动是否已加载 #lspci -k//PCI网卡 #lsusb -v//USB网卡 #ip link//查看无线设备名称(以下使用设备名为:wlp7s0) #ip l ...

  7. 使用老毛桃进行uefi下的win10备份

    使用老毛桃进行uefi下的win10备份 1 支持UEFI的启动U盘 2 进入老毛桃PE 3 调用ghost11选择C盘所在分区备份 5 以后使用 老的习惯,装完系统,依赖ghost对装完的win10 ...

  8. linux系统用uefi启动安装win7,uefi下安装win7系统有什么不同

    UEFI 模式下安装Windows 7操作系统(暂不涉及Linux等其它操作系统): 1.硬盘必须是GPT格式,系统必须是64位,UEFI不支持32位系统. 备注:严谨地说,UEFI也可以引导MBR磁 ...

  9. UEFI下用rufus安装ubuntu16.04 LTS

    最近的mint系统经常崩,出现很多问题,对Ubuntu的心还是有点不甘,对我这种没事就折腾 当然换回原来的Ubuntu, 先刻录镜像到U盘中,用的是rufus这个工具,如果不知道怎么使用的话可以百度一 ...

最新文章

  1. Linux系统日志分析与管理(14)
  2. 青龙羊毛——东方头条(搬砖,非原创)
  3. 正则 ?= 和 ?= 用法 以及零宽断言等概念
  4. 金三银四产品人跳槽指南:这9本书帮你搞定升职加薪
  5. vue的this.$set的作用
  6. 获取指定年月的全部日期放入数组
  7. 如何设计一道优雅的白名单策略
  8. [转]Cuda笔记【1】GPU计算DEMO
  9. ipc.Client: Retrying connect to server: h1/192.168.1.61:9000. Already tried 0 time(s);解决方法
  10. C语言结构体初始化(转载)
  11. APP开发难吗?澳大利亚10岁儿童已开发5个手机App
  12. Linux编辑器介绍
  13. 史上最全 IT 类学习资源
  14. Java(22):Java连接Mysql数据库的操作说明
  15. ubuntu hashcat 安装
  16. Java微信公众号开发之微信素材管理工具类
  17. 特步软件测试员工资,特步集团全面预算管理系统——管理员手册V1.1
  18. 使用html5 canvas 绘制Android机器人
  19. MACBOOK强制退出程序的方法
  20. 程序员的好代码长什么模样?

热门文章

  1. 线性表--链栈(十一)
  2. [转载]WPF控件拖动
  3. HR 必须了解的绩效评估
  4. 【GRUB】GRUB2基本操作
  5. 策略验证_指标买点分析技法_运用boll布林线指标选择买点
  6. java which valid identifier_JAVA程序员认证模拟题及分析(2)
  7. 改进StyleGAN的人脸融合系统
  8. 广州华锐互动:元宇宙二次元虚拟人物助力品牌实现数字化营销
  9. 移动购物正流行 你准备好了么?
  10. 多款较经典的JS下拉菜单 - Javascript/Ajax - 网海拾贝 | 电脑教程软件资讯网