最近在龙芯平台在调试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无效掉.

 

GitHub 加速计划 / ar / Aria
5.52 K
861
下载
下载可以很简单
最近提交(Master分支:2 个月前 )
b0d3c6dd - 4 个月前
8fd9634d - 4 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐