Uefi中的Variable

Variable的定义

        变量代表内存中具有特定属性的一个存储单元,它用来存放数据,也就是变量的值,在程序运行期间,这些值是可以改变的,在UEFI架构下,Variable是一个由表示信息、属性、和值构成的组合体,类似全局变量。

        "Variable" 通常指的是 EFI Variables,是一种用于存储持久性数据的机制。下面我将用生动的方式解释 EFI Variables:

        想象一下你的计算机就像一个魔法书中的魔法屋,这个屋子里有很多魔法小盒子,每个小盒子都有一个名字。这些小盒子可以用来存储各种重要的信息,比如你的名字、出生日期、计算机的配置设置等等。

  • 魔法小盒子:这些小盒子就是 EFI Variables,它们是一种用来存储数据的容器。每个变量都有一个唯一的名字,比如 "BootOrder" 或 "TimeAndDate"。

  • 名字:每个小盒子都有一个名字,这个名字用来标识和查找特定的数据。就像你的名字可以用来找到你的个人信息一样。

  • 内容:每个小盒子里可以存储一些数据,比如文本、数字、日期等等。这些数据可以是重要的系统配置信息,例如启动顺序或时间设置。

  • 持久性:与普通的计算机内存不同,这些小盒子中的数据是持久性的,它们在计算机关闭后仍然保留。这意味着你可以在下次打开计算机时找到相同的数据。

  • 魔法:在计算机开机时,它会读取这些小盒子中的数据,并根据它们的内容执行特定的操作,就像在魔法屋中使用魔法物品一样。

  • 读写:你可以随时打开这些小盒子,读取里面的数据,也可以将新的数据放入其中。这允许你在计算机上存储和管理各种重要信息。

        总之,EFI Variables 就像魔法小盒子,它们允许计算机存储和访问重要的持久性数据,这些数据可以影响计算机的行为和配置。它们对于UEFI固件和操作系统非常重要,用于存储系统配置、启动顺序、时间设置等等信息。

        在uefi中所有的环境变量都用variable来表示,系统统一使用gRT->GetVariable和gRT->SetVariable来读和写一个Variable,注意这里gRT是uefi中的Runtime阶段的一个全局的表,表的关系为SystemTable->Runtime Services->EFI_SET_VARIABLE与EFI_GET_VARIABLE。

Variable相关函数

EFI_SET_VARIABLE

/**
  Sets the value of a variable.
  @param[in]  VariableName       A Null-terminated string that is the name of the vendor's variable.
                                 Each VariableName is unique for each VendorGuid. VariableName must
                                 contain 1 or more characters. If VariableName is an empty string,
                                 then EFI_INVALID_PARAMETER is returned.
  @param[in]  VendorGuid         A unique identifier for the vendor.
  @param[in]  Attributes         Attributes bitmask to set for the variable.
  @param[in]  DataSize           The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, 
                                 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or 
                                 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero 
                                 causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is 
                                 set, then a SetVariable() call with a DataSize of zero will not cause any change to 
                                 the variable value (the timestamp associated with the variable may be updated however 
                                 even if no new data value is provided,see the description of the 
                                 EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not 
                                 be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). 
  @param[in]  Data               The contents for the variable.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_SET_VARIABLE)(
  IN  CHAR16                       *VariableName,
  IN  EFI_GUID                     *VendorGuid,
  IN  UINT32                       Attributes,
  IN  UINTN                        DataSize,
  IN  VOID                         *Data
  );

EFI_GET_VARIABLE

/**
  Returns the value of a variable.
  @param[in]       VariableName  A Null-terminated string that is the name of the vendor's
                                 variable.
  @param[in]       VendorGuid    A unique identifier for the vendor.
  @param[out]      Attributes    If not NULL, a pointer to the memory location to return the
                                 attributes bitmask for the variable.
  @param[in, out]  DataSize      On input, the size in bytes of the return Data buffer.
                                 On output the size of data returned in Data.
  @param[out]      Data          The buffer to return the contents of the variable.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_GET_VARIABLE)(
  IN     CHAR16                      *VariableName,
  IN     EFI_GUID                    *VendorGuid,
  OUT    UINT32                      *Attributes,    OPTIONAL
  IN OUT UINTN                       *DataSize,
  OUT    VOID                        *Data
  );

EFI_GET_NEXT_VARIABLE_NAME

/**
  Enumerates the current variable names.
  @param[in, out]  VariableNameSize The size of the VariableName buffer.
  @param[in, out]  VariableName     On input, supplies the last VariableName that was returned
                                    by GetNextVariableName(). On output, returns the Nullterminated
                                    string of the current variable.
  @param[in, out]  VendorGuid       On input, supplies the last VendorGuid that was returned by
                                    GetNextVariableName(). On output, returns the
                                    VendorGuid of the current variable.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_GET_NEXT_VARIABLE_NAME)(
  IN OUT UINTN                    *VariableNameSize,
  IN OUT CHAR16                   *VariableName,
  IN OUT EFI_GUID                 *VendorGuid
  );

EFI_QUERY_VARIABLE_INFO

/**
  Returns information about the EFI variables.
  @param[in]   Attributes                   Attributes bitmask to specify the type of variables on
                                            which to return information.
  @param[out]  MaximumVariableStorageSize   On output the maximum size of the storage space
                                            available for the EFI variables associated with the
                                            attributes specified.
  @param[out]  RemainingVariableStorageSize Returns the remaining size of the storage space
                                            available for the EFI variables associated with the
                                            attributes specified.
  @param[out]  MaximumVariableSize          Returns the maximum size of the individual EFI
                                            variables associated with the attributes specified.
  @retval EFI_SUCCESS                  Valid answer returned.
  @retval EFI_INVALID_PARAMETER        An invalid combination of attribute bits was supplied
  @retval EFI_UNSUPPORTED              The attribute is not supported on this platform, and the
                                       MaximumVariableStorageSize,
                                       RemainingVariableStorageSize, MaximumVariableSize
                                       are undefined.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_QUERY_VARIABLE_INFO)(
  IN  UINT32            Attributes,
  OUT UINT64            *MaximumVariableStorageSize,
  OUT UINT64            *RemainingVariableStorageSize,
  OUT UINT64            *MaximumVariableSize
  );

Variable模块--VariableRuntimeDxe

        根据UEFI提供的gRT->SetVariable与gRT->GetVariable服务,查找其在源码中是如何定义的,可以发现,最终定位MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf模块下,根据该模块的定义,可以找到模块的入口函数VariableServiceInitialize。

VariableServiceInitialize

        根据VariableRuntimeDxe的inf文件找到其入口函数VariableServiceInitialize,根据函数注释可以看到,这个驱动入口函数主要做的就是将四个和Variable有关的服务放入系统表内,即Get、Set、GetNext、Query四个接口函数,并且为variable的读写服务安装了架构protocol,使得variable的读写服务可以被调用,最后注册了一个EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE事件。

        根据函数内容可以看到,函数首先调用VariableCommonInitialize对存放non-volatile与volatile变量的区域进行初始化,随后便将VariableServiceGetVariable等接口函数放入SystemTable->RuntimeServices->GetVariable等系统表内,接着安装gEfiVariableArchProtocolGuid架构,以实现variable的读写服务,最后这个注册虚拟地址事件应该是为了供os使用。

/**
  Variable Driver main entry point. The Variable driver places the 4 EFI
  runtime services in the EFI System Table and installs arch protocols
  for variable read and write services being available. It also registers
  a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.

  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
  @param[in] SystemTable    A pointer to the EFI System Table.

  @retval EFI_SUCCESS       Variable service successfully initialized.

**/
EFI_STATUS
EFIAPI
VariableServiceInitialize (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_EVENT   ReadyToBootEvent;
  EFI_EVENT   EndOfDxeEvent;

  Status = VariableCommonInitialize ();
  ASSERT_EFI_ERROR (Status);

  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mHandle,
                  &gEdkiiVariableLockProtocolGuid,
                  &mVariableLock,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mHandle,
                  &gEdkiiVarCheckProtocolGuid,
                  &mVarCheck,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  SystemTable->RuntimeServices->GetVariable         = VariableServiceGetVariable;
  SystemTable->RuntimeServices->GetNextVariableName = VariableServiceGetNextVariableName;
  SystemTable->RuntimeServices->SetVariable         = VariableServiceSetVariable;
  SystemTable->RuntimeServices->QueryVariableInfo   = VariableServiceQueryVariableInfo;

  //
  // Now install the Variable Runtime Architectural protocol on a new handle.
  //
  Status = gBS->InstallProtocolInterface (
                  &mHandle,
                  &gEfiVariableArchProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  if (!PcdGetBool (PcdEmuVariableNvModeEnable)) {
    //
    // Register FtwNotificationEvent () notify function.
    //
    EfiCreateProtocolNotifyEvent (
      &gEfiFaultTolerantWriteProtocolGuid,
      TPL_CALLBACK,
      FtwNotificationEvent,
      (VOID *)SystemTable,
      &mFtwRegistration
      );
  } else {
    //
    // Emulated non-volatile variable mode does not depend on FVB and FTW.
    //
    VariableWriteServiceInitializeDxe ();
  }

  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  VariableClassAddressChangeEvent,
                  NULL,
                  &gEfiEventVirtualAddressChangeGuid,
                  &mVirtualAddressChangeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  //
  // Register the event handling function to reclaim variable for OS usage.
  //
  Status = EfiCreateEventReadyToBootEx (
             TPL_NOTIFY,
             OnReadyToBoot,
             NULL,
             &ReadyToBootEvent
             );
  ASSERT_EFI_ERROR (Status);

  //
  // Register the event handling function to set the End Of DXE flag.
  //
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  OnEndOfDxe,
                  NULL,
                  &gEfiEndOfDxeEventGroupGuid,
                  &EndOfDxeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  // Register and initialize the VariablePolicy engine.
  Status = InitVariablePolicyLib (VariableServiceGetVariable);
  ASSERT_EFI_ERROR (Status);
  Status = VarCheckRegisterSetVariableCheckHandler (ValidateSetVariable);
  ASSERT_EFI_ERROR (Status);
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mHandle,
                  &gEdkiiVariablePolicyProtocolGuid,
                  &mVariablePolicyProtocol,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  return EFI_SUCCESS;
}

Variable.c

        根据之前提到的入口函数VariableServiceInitialize中对UEFI提供的四个接口函数的定义可以观察到,四个接口函数的具体实现其实是在Variable.c文件夹下的VariableServiceInitialize、VariableServiceInitialize、VariableServiceInitialize、VariableServiceInitialize。

        其中着重观察Set与Get。

VariableServiceSetVariable

        从函数源码来看,该函数有比较繁琐的检查机制,检查入口参数是否合规,检查是否有溢出情况,可以看到,直到调用加锁函数AcquireLockOnlyAtBootTime()之前都是在做检查,具体先不深究,随后便是调用FindVariable()函数去查找要设置的variable是否已经存在,具体要看FindVariable函数返回的CurrPtr值,紧接着便调用UpdateVariable进行创建新variable,该函数本身即是一些内存的拷贝,只不过需要做一系列的检查。

        UpdateVariable中有创建新variable的操作,该操作主要分4步:

        1、写Variable_Header

        2、将Variable_Header->state设置为0x7f即有效

        3、写variable数据

        4、将Variable_Header->state设置为0x3f即已添加

        操作主要通过UpdateVariableStore函数实现,该函数的主要作用就是向FWH写入数据,利用FVB的Write向指定地址写入数据,最后将内存更新到flash区域。

/**

  This code sets variable in storage blocks (Volatile or Non-Volatile).

  Caution: This function may receive untrusted input.
  This function may be invoked in SMM mode, and datasize and data are external input.
  This function will do basic validation, before parse the data.
  This function will parse the authentication carefully to avoid security issues, like
  buffer overflow, integer overflow.
  This function will check attribute carefully to avoid authentication bypass.

  @param VariableName                     Name of Variable to be found.
  @param VendorGuid                       Variable vendor GUID.
  @param Attributes                       Attribute value of the variable found
  @param DataSize                         Size of Data found. If size is less than the
                                          data, this value contains the required size.
  @param Data                             Data pointer.

  @return EFI_INVALID_PARAMETER           Invalid parameter.
  @return EFI_SUCCESS                     Set successfully.
  @return EFI_OUT_OF_RESOURCES            Resource not enough to set variable.
  @return EFI_NOT_FOUND                   Not found.
  @return EFI_WRITE_PROTECTED             Variable is read-only.

**/
EFI_STATUS
EFIAPI
VariableServiceSetVariable (
  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;
  UINTN                   PayloadSize;
  BOOLEAN                 AuthFormat;

  AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat;

  //
  // Check input parameters.
  //
  if ((VariableName == NULL) || (VariableName[0] == 0) || (VendorGuid == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((DataSize != 0) && (Data == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check for reserverd bit in variable attribute.
  // EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated but we still allow
  // the delete operation of common authenticated variable at user physical presence.
  // So leave EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute check to AuthVariableLib
  //
  if ((Attributes & (~(EFI_VARIABLE_ATTRIBUTES_MASK | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS))) != 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check if the combination of attribute bits is valid.
  //
  if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {
    //
    // Make sure if runtime bit is set, boot service bit is set also.
    //
    if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
      return EFI_UNSUPPORTED;
    } else {
      return EFI_INVALID_PARAMETER;
    }
  } else if ((Attributes & EFI_VARIABLE_ATTRIBUTES_MASK) == EFI_VARIABLE_NON_VOLATILE) {
    //
    // Only EFI_VARIABLE_NON_VOLATILE attribute is invalid
    //
    if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
      return EFI_UNSUPPORTED;
    } else {
      return EFI_INVALID_PARAMETER;
    }
  } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {
    if (!mVariableModuleGlobal->VariableGlobal.AuthSupport) {
      //
      // Not support authenticated variable write.
      //
      return EFI_INVALID_PARAMETER;
    }
  } else if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {
    if (PcdGet32 (PcdHwErrStorageSize) == 0) {
      //
      // Not support harware error record variable variable.
      //
      return EFI_INVALID_PARAMETER;
    }
  }

  //
  // EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS and EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute
  // cannot be set both.
  //
  if (  ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
     && ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS))
  {
    return EFI_UNSUPPORTED;
  }

  if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) {
    //
    //  If DataSize == AUTHINFO_SIZE and then PayloadSize is 0.
    //  Maybe it's the delete operation of common authenticated variable at user physical presence.
    //
    if (DataSize != AUTHINFO_SIZE) {
      return EFI_UNSUPPORTED;
    }

    PayloadSize = DataSize - AUTHINFO_SIZE;
  } else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
    //
    // Sanity check for EFI_VARIABLE_AUTHENTICATION_2 descriptor.
    //
    if ((DataSize < OFFSET_OF_AUTHINFO2_CERT_DATA) ||
        (((EFI_VARIABLE_AUTHENTICATION_2 *)Data)->AuthInfo.Hdr.dwLength > DataSize - (OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo))) ||
        (((EFI_VARIABLE_AUTHENTICATION_2 *)Data)->AuthInfo.Hdr.dwLength < OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)))
    {
      return EFI_SECURITY_VIOLATION;
    }

    //
    // The VariableSpeculationBarrier() call here is to ensure the above sanity
    // check for the EFI_VARIABLE_AUTHENTICATION_2 descriptor has been completed
    // before the execution of subsequent codes.
    //
    VariableSpeculationBarrier ();
    PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
  } else {
    PayloadSize = DataSize;
  }

  if ((UINTN)(~0) - PayloadSize < StrSize (VariableName)) {
    //
    // Prevent whole variable size overflow
    //
    return EFI_INVALID_PARAMETER;
  }

  //
  //  The size of the VariableName, including the Unicode Null in bytes plus
  //  the DataSize is limited to maximum size of PcdGet32 (PcdMaxHardwareErrorVariableSize)
  //  bytes for HwErrRec#### variable.
  //
  if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
    if (StrSize (VariableName) + PayloadSize >
        PcdGet32 (PcdMaxHardwareErrorVariableSize) - GetVariableHeaderSize (AuthFormat))
    {
      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 Max(Auth|Volatile)VariableSize bytes.
    //
    if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {
      if (StrSize (VariableName) + PayloadSize >
          mVariableModuleGlobal->MaxAuthVariableSize -
          GetVariableHeaderSize (AuthFormat))
      {
        DEBUG ((
          DEBUG_ERROR,
          "%a: Failed to set variable '%s' with Guid %g\n",
          __func__,
          VariableName,
          VendorGuid
          ));
        DEBUG ((
          DEBUG_ERROR,
          "NameSize(0x%x) + PayloadSize(0x%x) > "
          "MaxAuthVariableSize(0x%x) - HeaderSize(0x%x)\n",
          StrSize (VariableName),
          PayloadSize,
          mVariableModuleGlobal->MaxAuthVariableSize,
          GetVariableHeaderSize (AuthFormat)
          ));
        return EFI_INVALID_PARAMETER;
      }
    } else if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
      if (StrSize (VariableName) + PayloadSize >
          mVariableModuleGlobal->MaxVariableSize - GetVariableHeaderSize (AuthFormat))
      {
        DEBUG ((
          DEBUG_ERROR,
          "%a: Failed to set variable '%s' with Guid %g\n",
          __func__,
          VariableName,
          VendorGuid
          ));
        DEBUG ((
          DEBUG_ERROR,
          "NameSize(0x%x) + PayloadSize(0x%x) > "
          "MaxVariableSize(0x%x) - HeaderSize(0x%x)\n",
          StrSize (VariableName),
          PayloadSize,
          mVariableModuleGlobal->MaxVariableSize,
          GetVariableHeaderSize (AuthFormat)
          ));
        return EFI_INVALID_PARAMETER;
      }
    } else {
      if (StrSize (VariableName) + PayloadSize >
          mVariableModuleGlobal->MaxVolatileVariableSize - GetVariableHeaderSize (AuthFormat))
      {
        DEBUG ((
          DEBUG_ERROR,
          "%a: Failed to set variable '%s' with Guid %g\n",
          __func__,
          VariableName,
          VendorGuid
          ));
        DEBUG ((
          DEBUG_ERROR,
          "NameSize(0x%x) + PayloadSize(0x%x) > "
          "MaxVolatileVariableSize(0x%x) - HeaderSize(0x%x)\n",
          StrSize (VariableName),
          PayloadSize,
          mVariableModuleGlobal->MaxVolatileVariableSize,
          GetVariableHeaderSize (AuthFormat)
          ));
        return EFI_INVALID_PARAMETER;
      }
    }
  }

  //
  // Special Handling for MOR Lock variable.
  //
  Status = SetVariableCheckHandlerMor (VariableName, VendorGuid, Attributes, PayloadSize, (VOID *)((UINTN)Data + DataSize - PayloadSize));
  if (Status == EFI_ALREADY_STARTED) {
    //
    // EFI_ALREADY_STARTED means the SetVariable() action is handled inside of SetVariableCheckHandlerMor().
    // Variable driver can just return SUCCESS.
    //
    return EFI_SUCCESS;
  }

  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = VarCheckLibSetVariableCheck (VariableName, VendorGuid, Attributes, PayloadSize, (VOID *)((UINTN)Data + DataSize - PayloadSize), mRequestSource);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);

  //
  // Consider reentrant in MCA/INIT/NMI. It needs be reupdated.
  //
  if (1 < InterlockedIncrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState)) {
    Point = mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase;
    //
    // Parse non-volatile variable data and get last variable offset.
    //
    NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point);
    while (IsValidVariableHeader (NextVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point))) {
      NextVariable = GetNextVariablePtr (NextVariable, AuthFormat);
    }

    mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN)NextVariable - (UINTN)Point;
  }

  //
  // Check whether the input variable is already existed.
  //
  Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, TRUE);
  if (!EFI_ERROR (Status)) {
    if (((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) && AtRuntime ()) {
      Status = EFI_WRITE_PROTECTED;
      goto Done;
    }

    if ((Attributes != 0) && ((Attributes & (~EFI_VARIABLE_APPEND_WRITE)) != Variable.CurrPtr->Attributes)) {
      //
      // If a preexisting variable is rewritten with different attributes, SetVariable() shall not
      // modify the variable and shall return EFI_INVALID_PARAMETER. Two exceptions to this rule:
      // 1. No access attributes specified
      // 2. The only attribute differing is EFI_VARIABLE_APPEND_WRITE
      //
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((DEBUG_INFO, "[Variable]: Rewritten a preexisting variable(0x%08x) with different attributes(0x%08x) - %g:%s\n", Variable.CurrPtr->Attributes, Attributes, VendorGuid, VariableName));
      goto Done;
    }
  }

  if (!FeaturePcdGet (PcdUefiVariableDefaultLangDeprecate)) {
    //
    // Hook the operation of setting PlatformLangCodes/PlatformLang and LangCodes/Lang.
    //
    Status = AutoUpdateLangVariable (VariableName, Data, DataSize);
    if (EFI_ERROR (Status)) {
      //
      // The auto update operation failed, directly return to avoid inconsistency between PlatformLang and Lang.
      //
      goto Done;
    }
  }

  if (mVariableModuleGlobal->VariableGlobal.AuthSupport) {
    Status = AuthVariableLibProcessVariable (VariableName, VendorGuid, Data, DataSize, Attributes);
  } else {
    Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, &Variable, NULL);
  }

Done:
  InterlockedDecrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState);
  ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);

  if (!AtRuntime ()) {
    if (!EFI_ERROR (Status)) {
      SecureBootHook (
        VariableName,
        VendorGuid
        );
    }
  }

  return Status;
}

  FindVariable

        该函数在Set和Get中都有用到,该函数有五个传入参数,可以看到4个输入,1个输出。

FindVariable (
  IN  CHAR16                  *VariableName,
  IN  EFI_GUID                *VendorGuid,
  OUT VARIABLE_POINTER_TRACK  *PtrTrack,
  IN  VARIABLE_GLOBAL         *Global,
  IN  BOOLEAN                 IgnoreRtCheck
  )

        其中第三个输出参数的结构体类型如下

typedef struct {
  VARIABLE_HEADER    *CurrPtr;
  //
  // If both ADDED and IN_DELETED_TRANSITION variable are present,
  // InDeletedTransitionPtr will point to the IN_DELETED_TRANSITION one.
  // Otherwise, CurrPtr will point to the ADDED or IN_DELETED_TRANSITION one,
  // and InDeletedTransitionPtr will be NULL at the same time.
  //
  VARIABLE_HEADER    *InDeletedTransitionPtr;
  VARIABLE_HEADER    *EndPtr;
  VARIABLE_HEADER    *StartPtr;
  BOOLEAN            Volatile;
} VARIABLE_POINTER_TRACK;

        这个输出的结构反映了当前的变量保存区域。立方体区域实际上有两个,一个存放易失变量一个存放非易失变量,可以看到整个结构其实就是两部分:

1、Variable_Store_Header

        这是变量存储区的头部,用来声明变量存储区属性。

2、Variable_Header+Data

        (实际上应该是Variable_Header+Name+Data),Variable_Header结构体中就声明了变量的属性、名称大小、数据大小、GUID,随后的Name便是变量名,Data则是变量数据。

 Attributes

        该属性一般为下列三种:

        EFI_VARIABLE_NON_VOLATILE:表示设置变量时将变量写入到非易失介质中,比如SpiFlash;

        EFI_VARIABLE_BOOTSERVICE_ACCESS:表示设置的变量在UEFI阶段都可以访问,更具体的说是在调用gBS->ExitBootServices()之前都可以访问;

        EFI_VARIABLE_RUNTIME_ACCESS:表示设置的变量在OS下都可以访问,当然前提是OS是UEFI兼容的。

        有时可以看到该项属性为0x07,其实实际代表着EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,4+2+1。

///
/// Attributes of variable.
///
#define EFI_VARIABLE_NON_VOLATILE                            0x00000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS                      0x00000002
#define EFI_VARIABLE_RUNTIME_ACCESS                          0x00000004

        根据查看FindVariable函数内部以及前面的变量存放结构,可以看到PtrTrack的StartPtr与EndPtr的指向位置就是第一个Variable的起始位置、以及最后一个Variable的截止位置,而Volatile应该就是决定了到底是去Volatile区找还是Non-Volatile区找。

         可以看到在函数内最后调用了FindVariableEx函数,这个函数其实不用看就已经知道里面具体是写什么了,利用CurrPtr指针从StartPtr遍历到EndPtr,在这期间不停对比VendorGuid与VariableName,如果一致,就意味着找到了,将CurrPtr返回,如果直到EndPtr都没有,说明没找到。

 UpdateVariable

        前面FindVariable后,就已经知道变量存储区到底有没有要找的变量了,接下来便是调用UpdateVariable进行下一步操作,该函数操作比较繁琐,因此采取比较简洁的概括如图

 VariableServiceGetVariable

        相较于Set长篇大论,Get明显精简了不少,没有了繁琐的检查,只是简单的检查了一下入口参数的判定,紧接着便是用FindVariable查找是否存在该变量,如果不存在就直接退出,如果存在,则获取一下variable data的大小,判断Buffer size与variable data size相比是否足够大,如果足够,则使用CopyMem函数将GetVariableDataPtr,也就是指向variable data的指针,赋给了指向Data的指针,随后UpdateVariableInfo。

/**

  This code finds variable in storage blocks (Volatile or Non-Volatile).

  Caution: This function may receive untrusted input.
  This function may be invoked in SMM mode, and datasize is external input.
  This function will do basic validation, before parse the data.

  @param VariableName               Name of Variable to be found.
  @param VendorGuid                 Variable vendor GUID.
  @param Attributes                 Attribute value of the variable found.
  @param DataSize                   Size of Data found. If size is less than the
                                    data, this value contains the required size.
  @param Data                       The buffer to return the contents of the variable. May be NULL
                                    with a zero DataSize in order to determine the size buffer needed.

  @return EFI_INVALID_PARAMETER     Invalid parameter.
  @return EFI_SUCCESS               Find the specified variable.
  @return EFI_NOT_FOUND             Not found.
  @return EFI_BUFFER_TO_SMALL       DataSize is too small for the result.

**/
EFI_STATUS
EFIAPI
VariableServiceGetVariable (
  IN      CHAR16    *VariableName,
  IN      EFI_GUID  *VendorGuid,
  OUT     UINT32    *Attributes OPTIONAL,
  IN OUT  UINTN     *DataSize,
  OUT     VOID      *Data OPTIONAL
  )
{
  EFI_STATUS              Status;
  VARIABLE_POINTER_TRACK  Variable;
  UINTN                   VarDataSize;

  if ((VariableName == NULL) || (VendorGuid == NULL) || (DataSize == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (VariableName[0] == 0) {
    return EFI_NOT_FOUND;
  }

  AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);

  Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE);
  if (EFI_ERROR (Status) || (Variable.CurrPtr == NULL)) {
    goto Done;
  }

  //
  // Get data size
  //
  VarDataSize = DataSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
  ASSERT (VarDataSize != 0);

  if (*DataSize >= VarDataSize) {
    if (Data == NULL) {
      Status = EFI_INVALID_PARAMETER;
      goto Done;
    }

    CopyMem (Data, GetVariableDataPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat), VarDataSize);

    *DataSize = VarDataSize;
    UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE, &gVariableInfo);

    Status = EFI_SUCCESS;
    goto Done;
  } else {
    *DataSize = VarDataSize;
    Status    = EFI_BUFFER_TOO_SMALL;
    goto Done;
  }

Done:
  if ((Status == EFI_SUCCESS) || (Status == EFI_BUFFER_TOO_SMALL)) {
    if ((Attributes != NULL) && (Variable.CurrPtr != NULL)) {
      *Attributes = Variable.CurrPtr->Attributes;
    }
  }

  ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);
  return Status;
}

 VariableServiceGetNextVariableName

        该函数主要功能为找到下一个变量,从源码来看,主要先对入口参数进行检查,随后调用VariableServiceGetNextVariableInternal,调用的函数所做的事情就是检查所给定的变量存放表单中是否存在variable,其实现过程大致为通过遍历链表式的方法遍历变量存储区,直到找到对应variable后返回。在执行完VariableServiceGetNextVariableInternal函数后,若函数返回成功,则使用CopyMem将GetVariableNamePtr指针赋给VariableName,同样GetVendorGuidPtr指针赋给VendorGuid。

/**

  This code Finds the Next available variable.

  Caution: This function may receive untrusted input.
  This function may be invoked in SMM mode. This function will do basic validation, before parse the data.

  @param VariableNameSize           The size of the VariableName buffer. The size must be large
                                    enough to fit input string supplied in VariableName buffer.
  @param VariableName               Pointer to variable name.
  @param VendorGuid                 Variable Vendor Guid.

  @retval EFI_SUCCESS               The function completed successfully.
  @retval EFI_NOT_FOUND             The next variable was not found.
  @retval EFI_BUFFER_TOO_SMALL      The VariableNameSize is too small for the result.
                                    VariableNameSize has been updated with the size needed to complete the request.
  @retval EFI_INVALID_PARAMETER     VariableNameSize is NULL.
  @retval EFI_INVALID_PARAMETER     VariableName is NULL.
  @retval EFI_INVALID_PARAMETER     VendorGuid is NULL.
  @retval EFI_INVALID_PARAMETER     The input values of VariableName and VendorGuid are not a name and
                                    GUID of an existing variable.
  @retval EFI_INVALID_PARAMETER     Null-terminator is not found in the first VariableNameSize bytes of
                                    the input VariableName buffer.

**/
EFI_STATUS
EFIAPI
VariableServiceGetNextVariableName (
  IN OUT  UINTN     *VariableNameSize,
  IN OUT  CHAR16    *VariableName,
  IN OUT  EFI_GUID  *VendorGuid
  )
{
  EFI_STATUS             Status;
  UINTN                  MaxLen;
  UINTN                  VarNameSize;
  BOOLEAN                AuthFormat;
  VARIABLE_HEADER        *VariablePtr;
  VARIABLE_STORE_HEADER  *VariableStoreHeader[VariableStoreTypeMax];

  if ((VariableNameSize == NULL) || (VariableName == NULL) || (VendorGuid == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat;

  //
  // Calculate the possible maximum length of name string, including the Null terminator.
  //
  MaxLen = *VariableNameSize / sizeof (CHAR16);
  if ((MaxLen == 0) || (StrnLenS (VariableName, MaxLen) == MaxLen)) {
    //
    // Null-terminator is not found in the first VariableNameSize bytes of the input VariableName buffer,
    // follow spec to return EFI_INVALID_PARAMETER.
    //
    return EFI_INVALID_PARAMETER;
  }

  AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);

  //
  // 0: Volatile, 1: HOB, 2: Non-Volatile.
  // The index and attributes mapping must be kept in this order as FindVariable
  // makes use of this mapping to implement search algorithm.
  //
  VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;
  VariableStoreHeader[VariableStoreTypeHob]      = (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.HobVariableBase;
  VariableStoreHeader[VariableStoreTypeNv]       = mNvVariableCache;

  Status =  VariableServiceGetNextVariableInternal (
              VariableName,
              VendorGuid,
              VariableStoreHeader,
              &VariablePtr,
              AuthFormat
              );
  if (!EFI_ERROR (Status)) {
    VarNameSize = NameSizeOfVariable (VariablePtr, AuthFormat);
    ASSERT (VarNameSize != 0);
    if (VarNameSize <= *VariableNameSize) {
      CopyMem (
        VariableName,
        GetVariableNamePtr (VariablePtr, AuthFormat),
        VarNameSize
        );
      CopyMem (
        VendorGuid,
        GetVendorGuidPtr (VariablePtr, AuthFormat),
        sizeof (EFI_GUID)
        );
      Status = EFI_SUCCESS;
    } else {
      Status = EFI_BUFFER_TOO_SMALL;
    }

    *VariableNameSize = VarNameSize;
  }

  ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);
  return Status;
}

 VariableServiceQueryVariableInfo

        该函数的功能在于返回EFI variables的信息,其内部实现和前面的GetNextVariable有点像,也是调用了一个类似的函数,VariableServiceQueryVariableInfoInternal来实现其功能,

/**

  This code returns information about the EFI variables.

  Caution: This function may receive untrusted input.
  This function may be invoked in SMM mode. This function will do basic validation, before parse the data.

  @param Attributes                     Attributes bitmask to specify the type of variables
                                        on which to return information.
  @param MaximumVariableStorageSize     Pointer to the maximum size of the storage space available
                                        for the EFI variables associated with the attributes specified.
  @param RemainingVariableStorageSize   Pointer to the remaining size of the storage space available
                                        for EFI variables associated with the attributes specified.
  @param MaximumVariableSize            Pointer to the maximum size of an individual EFI variables
                                        associated with the attributes specified.

  @return EFI_INVALID_PARAMETER         An invalid combination of attribute bits was supplied.
  @return EFI_SUCCESS                   Query successfully.
  @return EFI_UNSUPPORTED               The attribute is not supported on this platform.

**/
EFI_STATUS
EFIAPI
VariableServiceQueryVariableInfo (
  IN  UINT32  Attributes,
  OUT UINT64  *MaximumVariableStorageSize,
  OUT UINT64  *RemainingVariableStorageSize,
  OUT UINT64  *MaximumVariableSize
  )
{
  EFI_STATUS  Status;

  if ((MaximumVariableStorageSize == NULL) || (RemainingVariableStorageSize == NULL) || (MaximumVariableSize == NULL) || (Attributes == 0)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
    //
    //  Deprecated attribute, make this check as highest priority.
    //
    return EFI_UNSUPPORTED;
  }

  if ((Attributes & EFI_VARIABLE_ATTRIBUTES_MASK) == 0) {
    //
    // Make sure the Attributes combination is supported by the platform.
    //
    return EFI_UNSUPPORTED;
  } else if ((Attributes & EFI_VARIABLE_ATTRIBUTES_MASK) == EFI_VARIABLE_NON_VOLATILE) {
    //
    // Only EFI_VARIABLE_NON_VOLATILE attribute is invalid
    //
    return EFI_INVALID_PARAMETER;
  } else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {
    //
    // Make sure if runtime bit is set, boot service bit is set also.
    //
    return EFI_INVALID_PARAMETER;
  } else if (AtRuntime () && ((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)) {
    //
    // Make sure RT Attribute is set if we are in Runtime phase.
    //
    return EFI_INVALID_PARAMETER;
  } else if ((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
    //
    // Make sure Hw Attribute is set with NV.
    //
    return EFI_INVALID_PARAMETER;
  } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {
    if (!mVariableModuleGlobal->VariableGlobal.AuthSupport) {
      //
      // Not support authenticated variable write.
      //
      return EFI_UNSUPPORTED;
    }
  } else if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {
    if (PcdGet32 (PcdHwErrStorageSize) == 0) {
      //
      // Not support harware error record variable variable.
      //
      return EFI_UNSUPPORTED;
    }
  }

  AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);

  Status = VariableServiceQueryVariableInfoInternal (
             Attributes,
             MaximumVariableStorageSize,
             RemainingVariableStorageSize,
             MaximumVariableSize
             );

  ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock);
  return Status;
}

Variable实践

        该实践总共调用了 SetVariable和GetVariable两个函数,去设置与获取变量PhytiumEnable。

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/PcdLib.h>


//ShellCEntryLib call user interface ShellAppMain
EFI_STATUS
EFIAPI
MyHVariableAppEntry(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
)
{
  EFI_STATUS                         Status = EFI_SUCCESS;
  UINTN                              DataSize;
  UINT64                             PhytiumEnable = 1;
  EFI_GUID  gEfiPhytiumEnableGuid = { 0xb71604d2, 0xb8ba, 0x4d9c, { 0x78, 0x88, 0xac, 0xcf, 0x1d, 0xa2, 0x26, 0xc3 }};

  DataSize = sizeof (PhytiumEnable);
  Status = gRT->SetVariable (
                  L"PhytiumEnable",
                  &gEfiPhytiumEnableGuid,
                  EFI_VARIABLE_NON_VOLATILE |
                  EFI_VARIABLE_BOOTSERVICE_ACCESS |
                  EFI_VARIABLE_RUNTIME_ACCESS,
                  DataSize,
                  &PhytiumEnable
                  );

  ASSERT(Status);
  DEBUG ((EFI_D_ERROR," [Phytium]: Setvariable PhytiumEnable 0x%x.\n", PhytiumEnable));

  PhytiumEnable = 0;
  DataSize = sizeof (PhytiumEnable);
  Status = gRT->GetVariable (
                L"PhytiumEnable",
                &gEfiPhytiumEnableGuid,
                NULL,
                &DataSize,
                &PhytiumEnable
                );
  ASSERT(Status);

  if (PhytiumEnable) {
    DEBUG ((EFI_D_ERROR, "[Phytium] PhytiumEnable\n"));
  }

  return EFI_SUCCESS;
}

         实现结果如下图。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值