REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594
Add EncryptionVariableLib.h for providing encryption and
decryption services for protected variables.
Add ProtectedVariableLib.h for providing integrity or
variables.
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Nishant C Mistry <nishant.c.mistry@intel.com>
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
Signed-off-by: Nishant C Mistry <nishant.c.mistry@intel.com>
Signed-off-by: Judah Vang <judah.vang@intel.com>
---
MdeModulePkg/Include/Library/EncryptionVariableLib.h | 165 +++++
MdeModulePkg/Include/Library/ProtectedVariableLib.h | 700 ++++++++++++++++++++
2 files changed, 865 insertions(+)
diff --git a/MdeModulePkg/Include/Library/EncryptionVariableLib.h b/MdeModulePkg/Include/Library/EncryptionVariableLib.h
new file mode 100644
index 000000000000..c7740e659dcf
--- /dev/null
+++ b/MdeModulePkg/Include/Library/EncryptionVariableLib.h
@@ -0,0 +1,165 @@
+/** @file
+ Provides services to encrypt/decrypt variables.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef ENCRYPTION_VARIABLE_LIB_H_
+#define ENCRYPTION_VARIABLE_LIB_H_
+
+#include <IndustryStandard/Tpm20.h>
+
+#include <Guid/VariableFormat.h>
+
+#include <Library/AuthVariableLib.h>
+
+#define ENC_TYPE_NULL 0
+#define ENC_TYPE_AES TPM_ALG_AES
+
+typedef struct _VARIABLE_ENCRYPTION_FLAGS {
+ BOOLEAN Auth; // Variable is authenticated or not
+ BOOLEAN DecryptInPlace; // Do decryption in place
+ BOOLEAN Protected; // Variable is protected or not
+} VARIABLE_ENCRYPTION_FLAGS;
+
+typedef struct _VARIABLE_ENCRYPTION_INFO {
+ AUTH_VARIABLE_INFO Header; // Authenticated varabile header
+ VARIABLE_HEADER *Buffer; // Pointer to variable buffer
+ UINT64 StoreIndex; // Variable store index
+ VOID *PlainData; // Pointer to plain data
+ UINT32 PlainDataSize; // Size of plain data
+ VOID *CipherData; // Pointer to cipher data
+ UINT32 CipherDataSize; // Size of cipher data
+ UINT32 CipherHeaderSize; // Size of cipher header
+ UINT32 CipherDataType; // Type of cipher data
+ VOID *Key; // Pointer to encrypt/decrypt key
+ UINT32 KeySize; // Size of key
+ VARIABLE_ENCRYPTION_FLAGS Flags; // Encryption flags
+} VARIABLE_ENCRYPTION_INFO;
+
+/**
+ Encrypt variable data.
+
+ @param[in, out] VarInfo Pointer to structure containing detailed information about a variable.
+
+ @retval EFI_SUCCESS Function successfully executed.
+ @retval EFI_INVALID_PARAMETER If ProtectedVarLibContextIn == NULL or ProtectedVarLibContextOut == NULL.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_UNSUPPORTED Unsupported to process authenticated variable.
+
+**/
+EFI_STATUS
+EFIAPI
+EncryptVariable (
+ IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo
+ );
+
+/**
+ Decrypt variable data.
+
+ If VarEncInfo->CipherData is not NULL, it must holds the cipher data to be
+ decrypted. Otherwise, assume the cipher data from variable data buffer, i.e.
+ VarEncInfo->Header.Data.
+
+ If VarEncInfo->Flags.DecryptInPlace is TRUE, the decrypted data will be put
+ back in the same buffer as cipher buffer got above, after encryption header,
+ which helps to identify later if the data in buffer is decrypted or not. This
+ can avoid repeat decryption when accessing the same variable more than once.
+
+ If VarEncInfo->Flags.DecryptInPlace is FALSE, VarEncInfo->PlainData must be
+ passed in with a valid buffer with VarEncInfo->PlainDataSize set correctly
+ with its size.
+
+ Note the VarEncInfo->PlainData is always pointing to the buffer address with
+ decrypted data without encryption header, and VarEncInfo->PlainDataSize is
+ always the size of original variable data, if this function returned
+ successfully.
+
+ @param[in, out] VarInfo Pointer to structure containing detailed
+ information about a variable.
+
+ @retval EFI_SUCCESS Variable was decrypted successfully.
+ @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is invalid.
+ @retval EFI_BUFFER_TOO_SMALL VarEncInfo->PlainData is not NULL but
+ VarEncInfo->PlainDataSize is too small.
+ @retval EFI_ABORTED Uknown error occurred during decrypting.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_COMPROMISED_DATA The cipher header is not valid.
+ @retval EFI_UNSUPPORTED Unsupported to encrypt variable.
+
+**/
+EFI_STATUS
+EFIAPI
+DecryptVariable (
+ IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo
+ );
+
+/**
+ Get cipher information about a variable, including plaindata size,
+ cipher algorithm type, etc.
+
+ For data passed in with VarEncInfo,
+
+ VarEncInfo->Header.Data
+ - The variable data in normal variable structure.
+ VarEncInfo->Header.DataSize
+ - The size of variable data.
+
+ For data passed out with VarEncInfo (valid only if EFI_SUCCESS is returned),
+
+ VarEncInfo->CipherDataType
+ - ENC_TYPE_NULL, if the variable is not encrypted or has been decrypted;
+ - ENC_TYPE_AES, if the variable is encrypted.
+ VarEncInfo->CipherHeaderSize
+ - Size of cipher header put before encrypted or decrypted data.
+ VarEncInfo->PlainData
+ - NULL, if the variable is encrypted; Or
+ - pointer to original variable data, if the variable has been decrypted.
+ VarEncInfo->PlainDataSize
+ - The size of original variable data
+ VarEncInfo->CipherData
+ - NULL, if the variable is decrypted; Or
+ - pointer to start of encrypted variable data, including encryption header;
+ VarEncInfo->CipherDataSize
+ - The size of encrypted variable data, including encryption header.
+
+ @param[in, out] VarInfo Pointer to structure containing detailed
+ information about a variable.
+
+ @retval EFI_SUCCESS The information was retrieved successfully.
+ @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is invalid.
+ @retval EFI_NOT_FOUND No cipher information recognized.
+ @retval EFI_UNSUPPORTED Unsupported interface.
+
+**/
+EFI_STATUS
+EFIAPI
+GetCipherDataInfo (
+ IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo
+ );
+
+/**
+ Force set cipher information for a variable, like plaindata size,
+ cipher algorithm type, cipher data etc.
+
+ The destination buffer must be passed via VarEncInfo->Header.Data.
+
+ This method is only used to update and/or change plain data information.
+
+ @param[in, out] VarInfo Pointer to structure containing detailed
+ information about a variable.
+
+ @retval EFI_SUCCESS The information was updated successfully.
+ @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is invalid.
+ @retval EFI_UNSUPPORTED If this method is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetCipherDataInfo (
+ IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo
+ );
+
+#endif //_ENCRYPTION_VARIABLE_LIB_H_
diff --git a/MdeModulePkg/Include/Library/ProtectedVariableLib.h b/MdeModulePkg/Include/Library/ProtectedVariableLib.h
new file mode 100644
index 000000000000..2f57b4ebbc70
--- /dev/null
+++ b/MdeModulePkg/Include/Library/ProtectedVariableLib.h
@@ -0,0 +1,700 @@
+/** @file
+ Defines interfaces of protected variable services for non-volatile variable
+ storage.
+
+Copyright (c) 2020 - 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PROTECTED_VARIABLE_LIB_H_
+#define PROTECTED_VARIABLE_LIB_H_
+
+#include <PiPei.h>
+#include <PiDxe.h>
+
+#include <Guid/VariableFormat.h>
+
+#include <Protocol/VarCheck.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+#include <Library/UefiLib.h>
+#include <Library/EncryptionVariableLib.h>
+
+#pragma pack(1)
+
+typedef struct _VARIABLE_DIGEST_FLAGS {
+ BOOLEAN Auth; // Authenticated variable format
+ BOOLEAN Valid; // Valid variable data in current variable
+ BOOLEAN Protected; // Protected variable (used in calculating HMAC)
+ BOOLEAN Encrypted; // Encrypted variable
+ BOOLEAN Freeable; // Memory reserved for current node can be freed
+ BOOLEAN CacheIndexAhead; // Indicates if CacheIndex is Ahead relative to Global structure
+ BOOLEAN Reserved[2]; // Reserved fields
+} VARIABLE_DIGEST_FLAGS;
+
+typedef struct _VARIABLE_DIGEST {
+ ///
+ /// Pointer to digest of next variable in a pre-defined rule of order for
+ /// integration verification. In other words, the final HMAC of all
+ /// protected variables is calculated by concatenating digest of each
+ /// variable in the order of this singly linked list.
+ ///
+ EFI_PHYSICAL_ADDRESS Prev;
+ EFI_PHYSICAL_ADDRESS Next;
+ ///
+ /// Index to variable in physical store, used to locate the variable directly
+ /// inside the store (Implementation dependent).
+ ///
+ EFI_PHYSICAL_ADDRESS StoreIndex;
+ ///
+ /// Index to variable in memory cache, used to locate the variable directly
+ /// inside the cache (Implementation dependent).
+ ///
+ EFI_PHYSICAL_ADDRESS CacheIndex;
+
+ ///
+ /// Pointer to Cache offset within Global Structure
+ ///
+ UINT32 CacheOffset;
+
+ ///
+ /// Frequently accessed information relating to the variable.
+ ///
+ UINT16 DigestSize; // Size of digest value
+ UINT16 NameSize; // Size of variable name
+ UINT32 DataSize; // Size of variable data
+ UINT32 PlainDataSize; // Size of plain data of current variable (if encrypted)
+ UINT32 State; // State of current variable
+ UINT32 Attributes; // Attributes of current variable
+
+ EFI_GUID VendorGuid; // GUID
+ VARIABLE_DIGEST_FLAGS Flags; // Variable digest flags
+ //
+ // Data with variable length are put at the end of this structure.
+ //
+ // CHAR16 VariableName[NameSize/2];
+ // UINT8 DigestValue[DigestSize];
+} VARIABLE_DIGEST;
+
+#pragma pack()
+
+#define VAR_DIG_NAMEOFF(VarDig) (sizeof (VARIABLE_DIGEST))
+#define VAR_DIG_DIGOFF(VarDig) (VAR_DIG_NAMEOFF (VarDig) + (VarDig)->NameSize)
+
+#define VAR_DIG_END(VarDig) (VAR_DIG_DIGOFF (VarDig) + (VarDig)->DigestSize)
+
+#define VAR_DIG_VALUE(VarDig) (VOID *)((UINTN)(VarDig) + VAR_DIG_DIGOFF (VarDig))
+#define VAR_DIG_NAME(VarDig) (CHAR16 *)((UINTN)(VarDig) + VAR_DIG_NAMEOFF (VarDig))
+#define VAR_DIG_GUID(VarDig) &(VAR_DIG_PTR (VarDig)->VendorGuid)
+
+#define VAR_DIG_PTR(Addr) ((VARIABLE_DIGEST *)(UINTN)(Addr))
+#define VAR_DIG_ADR(Ptr) ((EFI_PHYSICAL_ADDRESS)(UINTN)(Ptr))
+#define VAR_DIG_NEXT(VarDig) (VAR_DIG_PTR ((VarDig)->Next))
+#define VAR_DIG_PREV(VarDig) (VAR_DIG_PTR ((VarDig)->Prev))
+
+#define VAR_INDEX_INVALID ((UINT64)(-1))
+
+#define VAR_HDR_PTR(Addr) ((VARIABLE_HEADER *)(UINTN)(Addr))
+
+typedef VARIABLE_ENCRYPTION_INFO PROTECTED_VARIABLE_INFO;
+
+/**
+
+ This function writes data to the NV variable storage at given position.
+
+ Note: Per current variable service architecture, only SMM is allowed to
+ (directly) change NV variable storage.
+
+ @param VariableInfo Pointer to structure holding details of a variable.
+ @param Offset Offset to the given variable to write from.
+ @param Size Size of data to be written.
+ @param Buffer Pointer to the buffer from which data is written.
+
+ @retval EFI_INVALID_PARAMETER Invalid parameters passed in.
+ @retval EFI_UNSUPPORTED Updating NV variable storage is not supported.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to complete the operation.
+ @retval EFI_SUCCESS Variable store successfully updated.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *PROTECTED_VAR_LIB_UPDATE_VARIABLE_STORE)(
+ IN PROTECTED_VARIABLE_INFO *VariableInfo,
+ IN UINTN Offset,
+ IN UINT32 Size,
+ IN UINT8 *Buffer
+ );
+
+/**
+ Update the variable region with Variable information.
+
+ @param[in] AuthVariableInfo Pointer AUTH_VARIABLE_INFO structure for
+ input of the variable.
+
+ @retval EFI_SUCCESS The update operation is success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *PROTECTED_VAR_LIB_UPDATE_VARIABLE)(
+ IN AUTH_VARIABLE_INFO *AuthVariableInfo
+ );
+
+/**
+
+ Retrieve details about a variable and return them in VariableInfo->Header.
+
+ If VariableInfo->Address is given, this function will calculate its offset
+ relative to given variable storage via VariableStore; Otherwise, it will try
+ other internal variable storages or cached copies. It's assumed that, for all
+ copies of NV variable storage, all variables are stored in the same relative
+ position. If VariableInfo->Address is found in the range of any storage copies,
+ its offset relative to that storage should be the same in other copies.
+
+ If VariableInfo->Offset is given (non-zero) but not VariableInfo->Address,
+ this function will return the variable memory address inside VariableStore,
+ if given, via VariableInfo->Address; Otherwise, the address of other storage
+ copies will be returned, if any.
+
+ For a new variable whose offset has not been determined, a value of -1 as
+ VariableInfo->Offset should be passed to skip the offset calculation.
+
+ @param VariableInfo Pointer to variable information.
+
+ @retval EFI_INVALID_PARAMETER VariableInfo is NULL or both VariableInfo->Address
+ and VariableInfo->Offset are NULL (0).
+ @retval EFI_NOT_FOUND If given Address or Offset is out of range of
+ any given or internal storage copies.
+ @retval EFI_SUCCESS Variable details are retrieved successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *PROTECTED_VAR_LIB_GET_VAR_INFO)(
+ IN OUT PROTECTED_VARIABLE_INFO *VariableInfo
+ );
+
+/**
+
+ Retrieve details of the variable next to given variable within VariableStore.
+
+ If VarInfo->Address is NULL, the first one in VariableStore is returned.
+
+ VariableStart and/or VariableEnd can be given optionally for the situation
+ in which the valid storage space is smaller than the VariableStore->Size.
+ This usually happens when PEI variable services make a compact variable
+ cache to save memory, which cannot make use VariableStore->Size to determine
+ the correct variable storage range.
+
+ @param VariableStore Pointer to a variable storage. It's optional.
+ @param VariableStart Start point of valid range in VariableStore.
+ @param VariableEnd End point of valid range in VariableStore.
+ @param VariableInfo Pointer to variable information.
+
+ @retval EFI_INVALID_PARAMETER VariableInfo or VariableStore is NULL.
+ @retval EFI_NOT_FOUND If the end of VariableStore is reached.
+ @retval EFI_SUCCESS The next variable is retrieved successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *PROTECTED_VAR_LIB_GET_NEXT_VAR_INFO)(
+ IN OUT PROTECTED_VARIABLE_INFO *VariableInfo
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *DIGEST_METHOD_CALLBACK)(
+ IN VARIABLE_HEADER *Variable,
+ IN OUT VARIABLE_DIGEST *Digest
+ );
+
+/**
+
+ Initialize a memory copy of NV variable storage.
+
+ To save memory consumption (especially in PEI phase), it's allowed to cache
+ only valid variables. In such case, an index table recording offset of each
+ valid variables could be employed. The index table makes sure the cached copy
+ to be synchronized with the original copy in NV variable storage. To avoid
+ TOCTOU issue, once the variables are cached in memory and verified, NV
+ variable storage should not be used to read variable information. The cached
+ copy should be used instead.
+
+ If StoreCacheBase is not given, this function should return the required
+ cache size and valid variable number, if VariableNumber is not NULL. Then
+ the caller can prepare correct cache buffer and index table buffer before
+ next calling.
+
+ @param[in] CacheBase Base address of NV variable storage cache.
+ @param[in] CacheSize Size of CacheBuffer.
+ @param[out] CacheSize Size of required cache buffer.
+ @param[out] DigBuffer Base address of digest of each variable.
+ @param[in, out] DigBufferSize Digest size of one variable if DigestBuffer is NULL.
+ Size of DigestBuffer if DigestBuffer is NOT NULL.
+ @param[out] DigSize Required size of DigestBuffer if DigestBuffer is NULL.
+ @param[out] DigMethod Method used to generate digest for each variable.
+ @param[out] VarNumber Number of valid variables.
+ @param[out] AuthFlag Auth-variable indicator.
+
+ @retval EFI_INVALID_PARAMETER CacheSize is NULL; Or,
+ StoreCacheBase is 0 but *CacheSize it not.
+ @retval EFI_VOLUME_CORRUPTED If original NV variable storage is corrupted.
+ @retval EFI_BUFFER_TOO_SMALL If *CacheSize is smaller than required memory.
+ @retval EFI_SUCCESS The cached variable storage is initialized.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *PROTECTED_VAR_LIB_INIT_VAR_STORE)(
+ OUT VOID *CacheBase OPTIONAL,
+ IN OUT UINT32 *CacheSize OPTIONAL,
+ OUT VOID *DigBuffer OPTIONAL,
+ IN OUT UINT32 *DigBufferSize OPTIONAL,
+ IN UINT32 DigSize OPTIONAL,
+ IN DIGEST_METHOD_CALLBACK DigMethod OPTIONAL,
+ OUT UINT32 *VarNumber OPTIONAL,
+ OUT BOOLEAN *AuthFlag OPTIONAL
+ );
+
+/**
+
+ Initiate a variable retrieval in SMM environment from non-SMM environment.
+
+ This is usually required in BS/RT environment when local cached copy is in
+ encrypted form. Variable decryption can only be done in SMM environment.
+
+ @param[in] VariableName Name of Variable to be found.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[out] Attributes Attribute value of the variable found.
+ @param[in, out] DataSize Size of Data found. If size is less than the
+ data, this value contains the required size.
+ @param[out] Data Data pointer.
+
+ @retval EFI_SUCCESS Found the specified variable.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *PROTECTED_VAR_LIB_FIND_VAR_SMM)(
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ OUT UINT32 *Attributes OPTIONAL,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data OPTIONAL
+ );
+
+/**
+ Check if a variable is user variable or not.
+
+ @param[in] Variable Pointer to variable header.
+
+ @retval TRUE User variable.
+ @retval FALSE System variable.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI *PROTECTED_VAR_LIB_IS_USER_VAR)(
+ IN VARIABLE_HEADER *Variable
+ );
+
+/**
+ Check if a HOB variable store is available or not.
+
+ @retval EFI_NOT_READY HOB variable store info not available.
+ @retval EFI_NOT_FOUND HOB variable store is NOT available.
+ @retval EFI_SUCCESS HOB variable store is available.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *PROTECTED_VAR_LIB_HOB_STORE_AVAILABLE)(
+ VOID
+ );
+
+typedef enum {
+ FromPeiModule,
+ FromBootServiceModule,
+ FromRuntimeModule,
+ FromSmmModule
+} VARIABLE_SERVICE_USER;
+
+#pragma pack(1)
+
+#define PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION 0x02
+
+typedef struct _PROTECTED_VARIABLE_CONTEXT_IN {
+ UINT32 StructVersion;
+ UINT32 StructSize;
+ UINT32 MaxVariableSize;
+
+ VARIABLE_SERVICE_USER VariableServiceUser;
+
+ PROTECTED_VAR_LIB_FIND_VAR_SMM FindVariableSmm;
+ PROTECTED_VAR_LIB_GET_VAR_INFO GetVariableInfo;
+ PROTECTED_VAR_LIB_GET_NEXT_VAR_INFO GetNextVariableInfo;
+ PROTECTED_VAR_LIB_UPDATE_VARIABLE_STORE UpdateVariableStore;
+ PROTECTED_VAR_LIB_UPDATE_VARIABLE UpdateVariable;
+ PROTECTED_VAR_LIB_HOB_STORE_AVAILABLE IsHobVariableStoreAvailable;
+} PROTECTED_VARIABLE_CONTEXT_IN;
+
+#pragma pack()
+
+/**
+
+ Initialization for protected variable services.
+
+ If this initialization failed upon any error, the whole variable services
+ should not be used. A system reset might be needed to re-construct NV
+ variable storage to be the default state.
+
+ @param[in] ContextIn Pointer to variable service context needed by
+ protected variable.
+
+ @retval EFI_SUCCESS Protected variable services are ready.
+ @retval EFI_INVALID_PARAMETER If ContextIn == NULL or something missing or
+ mismatching in the content in ContextIn.
+ @retval EFI_COMPROMISED_DATA If failed to check integrity of protected variables.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_UNSUPPORTED Unsupported to process protected variable.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibInitialize (
+ IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
+ );
+
+/**
+
+ An alternative version of ProtectedVariableLibGetData to get plain data, if
+ encrypted, from given variable, for different use cases.
+
+ @param[in,out] VarInfo Pointer to structure containing variable information.
+
+ @retval EFI_SUCCESS Found the specified variable.
+ @retval EFI_INVALID_PARAMETER VarInfo is NULL or both VarInfo->Address and
+ VarInfo->Offset are invalid.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetByInfo (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ );
+
+/**
+ This service retrieves a variable's value using its name and GUID.
+
+ Read the specified variable from the UEFI variable store. If the Data
+ buffer is too small to hold the contents of the variable, the error
+ EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer
+ size to obtain the data.
+
+ @param VariableName A pointer to a null-terminated string that is the variable's name.
+ @param VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of
+ VariableGuid and VariableName must be unique.
+ @param Attributes If non-NULL, on return, points to the variable's attributes.
+ @param DataSize On entry, points to the size in bytes of the Data buffer.
+ On return, points to the size of the data returned in Data.
+ @param Data Points to the buffer which will hold the returned variable value.
+ May be NULL with a zero DataSize in order to determine the size of the buffer needed.
+
+ @retval EFI_SUCCESS The variable was read successfully.
+ @retval EFI_NOT_FOUND The variable was be found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data.
+ DataSize is updated with the size required for
+ the specified variable.
+ @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or Data is NULL.
+ @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetByName (
+ IN CONST CHAR16 *VariableName,
+ IN CONST EFI_GUID *VariableGuid,
+ OUT UINT32 *Attributes,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data OPTIONAL
+ );
+
+/**
+
+ Retrieve plain data, if encrypted, of given variable.
+
+ If variable encryption is employed, this function will initiate a SMM request
+ to get the plain data. Due to security consideration, the decryption can only
+ be done in SMM environment.
+
+ @param[in] Variable Pointer to header of a Variable.
+ @param[out] Data Pointer to plain data of the given variable.
+ @param[in, out] DataSize Size of data returned or data buffer needed.
+ @param[in] AuthFlag Auth-variable indicator.
+
+ @retval EFI_SUCCESS Found the specified variable.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+ @retval EFI_BUFFER_TOO_SMALL If *DataSize is smaller than needed.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetByBuffer (
+ IN VARIABLE_HEADER *Variable,
+ IN OUT VOID *Data,
+ IN OUT UINT32 *DataSize,
+ IN BOOLEAN AuthFlag
+ );
+
+/**
+
+ Prepare for variable update.
+
+ This is needed only once during current boot to mitigate replay attack. Its
+ major job is to advance RPMC (Replay Protected Monotonic Counter).
+
+ @retval EFI_SUCCESS Variable is ready to update hereafter.
+ @retval EFI_UNSUPPORTED Updating variable is not supported.
+ @retval EFI_DEVICE_ERROR Error in advancing RPMC.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteInit (
+ VOID
+ );
+
+/**
+
+ Update a variable with protection provided by this library.
+
+ If variable encryption is employed, the new variable data will be encrypted
+ before being written to NV variable storage.
+
+ A special variable, called "MetaDataHmacVar", will always be updated along
+ with variable being updated to reflect the changes (HMAC value) of all
+ protected valid variables. The only exceptions, currently, are variable
+ "MetaDataHmacVar" itself and variable "VarErrorLog".
+
+ The buffer passed by NewVariable must be double of maximum variable size,
+ which allows to pass the "MetaDataHmacVar" back to caller along with encrypted
+ new variable data, if any. This can make sure the new variable data and
+ "MetaDataHmacVar" can be written at almost the same time to reduce the chance
+ of compromising the integrity.
+
+ If *NewVariableSize is zero, it means to delete variable passed by CurrVariable
+ and/or CurrVariableInDel. "MetaDataHmacVar" will be updated as well in such
+ case because of less variables in storage. NewVariable should be always passed
+ in to convey new "MetaDataHmacVar" back.
+
+ @param[in,out] CurrVariable Variable to be updated. It's NULL if
+ adding a new variable.
+ @param[in,out] CurrVariableInDel In-delete-transition copy of updating variable.
+ @param[in] NewVariable Buffer of new variable data.
+ @param[out] NewVariable Buffer of "MetaDataHmacVar" and new
+ variable (encrypted).
+ @param[in] NewVariableSize Size of NewVariable.
+ @param[out] NewVariableSize Size of (encrypted) NewVariable and
+ "MetaDataHmacVar".
+
+ @retval EFI_SUCCESS The variable is updated with protection successfully.
+ @retval EFI_INVALID_PARAMETER NewVariable is NULL.
+ @retval EFI_NOT_FOUND Information missing to finish the operation.
+ @retval EFI_ABORTED Failed to encrypt variable or calculate HMAC.
+ @retval EFI_NOT_READY The RPMC device is not yet initialized.
+ @retval EFI_DEVICE_ERROR The RPMC device has error in updating.
+ @retval EFI_ACCESS_DENIED The given variable is not allowed to update.
+ Currently this only happens on updating
+ "MetaDataHmacVar" from code outside of this
+ library.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibUpdate (
+ IN OUT VARIABLE_HEADER *CurrVariable OPTIONAL,
+ IN OUT VARIABLE_HEADER *CurrVariableInDel OPTIONAL,
+ IN OUT VARIABLE_HEADER *NewVariable,
+ IN OUT UINTN *NewVariableSize
+ );
+
+/**
+
+ Finalize a variable updating after it's written to NV variable storage
+ successfully.
+
+ This usually includes works like increasing RPMC, synchronizing local cache,
+ updating new position of "MetaDataHmacVar", deleting old copy of "MetaDataHmacVar"
+ completely, etc.
+
+ @param[in] NewVariable Buffer of new variables and MetaDataHmacVar.
+ @param[in] VariableSize Size of buffer pointed by NewVariable.
+ @param[in] Offset Offset to NV variable storage from where the new
+ variable and MetaDataHmacVar have been written.
+
+ @retval EFI_SUCCESS No problem in winding up the variable write operation.
+ @retval Others Failed to updating state of old copy of updated
+ variable, or failed to increase RPMC, etc.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteFinal (
+ IN VARIABLE_HEADER *NewVariable,
+ IN UINTN VariableSize,
+ IN UINT64 StoreIndex
+ );
+
+/**
+ Return the request variable name and GUID as per StoreIndex.
+
+ This function is called multiple times to retrieve the VariableName
+ and VariableGuid of all variables currently available in the system.
+ On each call, the previous results are passed into the interface,
+ and, on return, the interface returns the data for the next
+ interface. When the entire variable list has been returned,
+ EFI_NOT_FOUND is returned.
+
+ @param This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI.
+
+ @param VariableNameSize On entry, points to the size of the buffer pointed to by VariableName.
+ On return, the size of the variable name buffer.
+ @param VariableName On entry, a pointer to a null-terminated string that is the variable's name.
+ On return, points to the next variable's null-terminated name string.
+ @param VariableGuid On entry, a pointer to an EFI_GUID that is the variable's GUID.
+ On return, a pointer to the next variable's GUID.
+
+ @retval EFI_SUCCESS The variable was read successfully.
+ @retval EFI_NOT_FOUND The variable could not be found.
+ @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the resulting
+ data. VariableNameSize is updated with the size
+ required for the specified variable.
+ @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or
+ VariableNameSize is NULL.
+ @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibFind (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ );
+
+/**
+ Return the next variable name and GUID.
+
+ This function is called multiple times to retrieve the VariableName
+ and VariableGuid of all variables currently available in the system.
+ On each call, the previous results are passed into the interface,
+ and, on return, the interface returns the data for the next
+ interface. When the entire variable list has been returned,
+ EFI_NOT_FOUND is returned.
+
+ @param VariableNameSize On entry, points to the size of the buffer pointed to by VariableName.
+ On return, the size of the variable name buffer.
+ @param VariableName On entry, a pointer to a null-terminated string that is the variable's name.
+ On return, points to the next variable's null-terminated name string.
+ @param VariableGuid On entry, a pointer to an EFI_GUID that is the variable's GUID.
+ On return, a pointer to the next variable's GUID.
+
+ @retval EFI_SUCCESS The variable was read successfully.
+ @retval EFI_NOT_FOUND The variable could not be found.
+ @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the resulting
+ data. VariableNameSize is updated with the size
+ required for the specified variable.
+ @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or
+ VariableNameSize is NULL.
+ @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibFindNext (
+ IN OUT UINTN *VariableNameSize,
+ IN OUT CHAR16 *VariableName,
+ IN OUT EFI_GUID *VariableGuid
+ );
+
+/**
+ Find variable via information in data structure PROTECTED_VARIABLE_INFO.
+
+ If VarInfo->StoreIndex is given and valid, always used it to search variable
+ in store. Otherwise, search the variable via variable name and guid pointed
+ by VarInfo->Header.VariableName and VarInfo->Header.VendorGuid.
+
+ @param VarInfo Pointer to data containing variable information.
+
+ @return EFI_SUCCESS Found the variable.
+ @return EFI_INVALID_PARAMETER No valid variable information is given.
+ @return EFI_NOT_FOUND The given variable was not found or no more
+ variables available.
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibFindNextEx (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ );
+
+/**
+ Refresh variable information changed by variable service.
+
+ @param Variable Pointer to buffer of the updated variable.
+ @param VariableSize Size of variable pointed by Variable.
+ @param StoreIndex New index of the variable in store.
+ @param RefreshData Flag to indicate if the variable has been updated.
+
+ @return EFI_SUCCESS No error occurred in updating.
+ @return EFI_NOT_FOUND The given variable was not found in
+ ProtectedVariableLib.
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibRefresh (
+ IN VARIABLE_HEADER *Variable,
+ IN UINTN VariableSize,
+ IN UINT64 StoreIndex,
+ IN BOOLEAN RefreshData
+ );
+
+/**
+ Refresh variable information changed by variable service.
+
+ @param Buffer Pointer to a pointer of buffer.
+ @param NumElements Pointer to number of elements in list.
+
+
+ @return EFI_SUCCESS Successfully retrieved sorted list.
+ @return others Unsuccessful.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetSortedList (
+ IN OUT EFI_PHYSICAL_ADDRESS **Buffer,
+ IN OUT UINTN *NumElements
+ );
+
+/**
+
+ Determine if the variable is the HMAC variable
+
+ @param VariableName Pointer to variable name.
+
+ @return TRUE Variable is HMAC variable
+ @return FALSE Variable is not HMAC variable
+
+**/
+BOOLEAN
+ProtectedVariableLibIsHmac (
+ IN CHAR16 *VariableName
+ );
+
+#endif
--
2.35.1.windows.2
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#89424): https://edk2.groups.io/g/devel/message/89424
Mute This Topic: https://groups.io/mt/90781891/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
Hi Judah, Please check my comments inlined below. Regards, Jian > -----Original Message----- > From: Vang, Judah <judah.vang@intel.com> > Sent: Saturday, April 30, 2022 2:04 AM > To: devel@edk2.groups.io > Cc: Wang, Jian J <jian.j.wang@intel.com>; Gao, Liming > <gaoliming@byosoft.com.cn>; Mistry, Nishant C <nishant.c.mistry@intel.com> > Subject: [Patch v2 06/28] MdeModulePkg: Add new include files > > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594 > > Add EncryptionVariableLib.h for providing encryption and > decryption services for protected variables. > Add ProtectedVariableLib.h for providing integrity or > variables. > > Cc: Jian J Wang <jian.j.wang@intel.com> > Cc: Liming Gao <gaoliming@byosoft.com.cn> > Cc: Nishant C Mistry <nishant.c.mistry@intel.com> > Signed-off-by: Jian J Wang <jian.j.wang@intel.com> > Signed-off-by: Nishant C Mistry <nishant.c.mistry@intel.com> > Signed-off-by: Judah Vang <judah.vang@intel.com> > --- > MdeModulePkg/Include/Library/EncryptionVariableLib.h | 165 +++++ > MdeModulePkg/Include/Library/ProtectedVariableLib.h | 700 > ++++++++++++++++++++ > 2 files changed, 865 insertions(+) > > diff --git a/MdeModulePkg/Include/Library/EncryptionVariableLib.h > b/MdeModulePkg/Include/Library/EncryptionVariableLib.h > new file mode 100644 > index 000000000000..c7740e659dcf > --- /dev/null > +++ b/MdeModulePkg/Include/Library/EncryptionVariableLib.h > @@ -0,0 +1,165 @@ > +/** @file > + Provides services to encrypt/decrypt variables. > + > +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > +SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef ENCRYPTION_VARIABLE_LIB_H_ > +#define ENCRYPTION_VARIABLE_LIB_H_ > + > +#include <IndustryStandard/Tpm20.h> > + > +#include <Guid/VariableFormat.h> > + > +#include <Library/AuthVariableLib.h> > + > +#define ENC_TYPE_NULL 0 > +#define ENC_TYPE_AES TPM_ALG_AES > + > +typedef struct _VARIABLE_ENCRYPTION_FLAGS { > + BOOLEAN Auth; // Variable is authenticated or not > + BOOLEAN DecryptInPlace; // Do decryption in place > + BOOLEAN Protected; // Variable is protected or not > +} VARIABLE_ENCRYPTION_FLAGS; > + > +typedef struct _VARIABLE_ENCRYPTION_INFO { > + AUTH_VARIABLE_INFO Header; // Authenticated varabile header > + VARIABLE_HEADER *Buffer; // Pointer to variable buffer > + UINT64 StoreIndex; // Variable store index > + VOID *PlainData; // Pointer to plain data > + UINT32 PlainDataSize; // Size of plain data > + VOID *CipherData; // Pointer to cipher data > + UINT32 CipherDataSize; // Size of cipher data > + UINT32 CipherHeaderSize; // Size of cipher header > + UINT32 CipherDataType; // Type of cipher data > + VOID *Key; // Pointer to encrypt/decrypt key > + UINT32 KeySize; // Size of key > + VARIABLE_ENCRYPTION_FLAGS Flags; // Encryption flags > +} VARIABLE_ENCRYPTION_INFO; > + > +/** > + Encrypt variable data. > + > + @param[in, out] VarInfo Pointer to structure containing detailed > information about a variable. > + > + @retval EFI_SUCCESS Function successfully executed. > + @retval EFI_INVALID_PARAMETER If ProtectedVarLibContextIn == NULL or > ProtectedVarLibContextOut == NULL. > + @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource. > + @retval EFI_UNSUPPORTED Unsupported to process authenticated > variable. [JianJW] authenticated -> encrypted > + > +**/ > +EFI_STATUS > +EFIAPI > +EncryptVariable ( > + IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo > + ); > + > +/** > + Decrypt variable data. > + > + If VarEncInfo->CipherData is not NULL, it must holds the cipher data to be > + decrypted. Otherwise, assume the cipher data from variable data buffer, i.e. > + VarEncInfo->Header.Data. > + > + If VarEncInfo->Flags.DecryptInPlace is TRUE, the decrypted data will be put > + back in the same buffer as cipher buffer got above, after encryption header, > + which helps to identify later if the data in buffer is decrypted or not. This > + can avoid repeat decryption when accessing the same variable more than > once. > + > + If VarEncInfo->Flags.DecryptInPlace is FALSE, VarEncInfo->PlainData must be > + passed in with a valid buffer with VarEncInfo->PlainDataSize set correctly > + with its size. > + > + Note the VarEncInfo->PlainData is always pointing to the buffer address with > + decrypted data without encryption header, and VarEncInfo->PlainDataSize is > + always the size of original variable data, if this function returned > + successfully. > + > + @param[in, out] VarInfo Pointer to structure containing detailed > + information about a variable. > + > + @retval EFI_SUCCESS Variable was decrypted successfully. > + @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is > invalid. > + @retval EFI_BUFFER_TOO_SMALL VarEncInfo->PlainData is not NULL but > + VarEncInfo->PlainDataSize is too small. > + @retval EFI_ABORTED Uknown error occurred during decrypting. [JianJW] Uknown -> Unknown > + @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource. > + @retval EFI_COMPROMISED_DATA The cipher header is not valid. > + @retval EFI_UNSUPPORTED Unsupported to encrypt variable. > + > +**/ > +EFI_STATUS > +EFIAPI > +DecryptVariable ( > + IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo > + ); > + > +/** > + Get cipher information about a variable, including plaindata size, > + cipher algorithm type, etc. > + > + For data passed in with VarEncInfo, > + > + VarEncInfo->Header.Data > + - The variable data in normal variable structure. > + VarEncInfo->Header.DataSize > + - The size of variable data. > + > + For data passed out with VarEncInfo (valid only if EFI_SUCCESS is returned), > + > + VarEncInfo->CipherDataType > + - ENC_TYPE_NULL, if the variable is not encrypted or has been decrypted; > + - ENC_TYPE_AES, if the variable is encrypted. > + VarEncInfo->CipherHeaderSize > + - Size of cipher header put before encrypted or decrypted data. > + VarEncInfo->PlainData > + - NULL, if the variable is encrypted; Or > + - pointer to original variable data, if the variable has been decrypted. > + VarEncInfo->PlainDataSize > + - The size of original variable data > + VarEncInfo->CipherData > + - NULL, if the variable is decrypted; Or > + - pointer to start of encrypted variable data, including encryption header; > + VarEncInfo->CipherDataSize > + - The size of encrypted variable data, including encryption header. > + > + @param[in, out] VarInfo Pointer to structure containing detailed > + information about a variable. > + > + @retval EFI_SUCCESS The information was retrieved successfully. > + @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is > invalid. > + @retval EFI_NOT_FOUND No cipher information recognized. > + @retval EFI_UNSUPPORTED Unsupported interface. > + > +**/ > +EFI_STATUS > +EFIAPI > +GetCipherDataInfo ( > + IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo > + ); > + > +/** > + Force set cipher information for a variable, like plaindata size, > + cipher algorithm type, cipher data etc. > + > + The destination buffer must be passed via VarEncInfo->Header.Data. > + > + This method is only used to update and/or change plain data information. > + > + @param[in, out] VarInfo Pointer to structure containing detailed > + information about a variable. > + > + @retval EFI_SUCCESS The information was updated successfully. > + @retval EFI_INVALID_PARAMETER Variable information in VarEncInfo is > invalid. > + @retval EFI_UNSUPPORTED If this method is not supported. > + > +**/ > +EFI_STATUS > +EFIAPI > +SetCipherDataInfo ( > + IN OUT VARIABLE_ENCRYPTION_INFO *VarInfo > + ); > + > +#endif //_ENCRYPTION_VARIABLE_LIB_H_ > diff --git a/MdeModulePkg/Include/Library/ProtectedVariableLib.h > b/MdeModulePkg/Include/Library/ProtectedVariableLib.h > new file mode 100644 > index 000000000000..2f57b4ebbc70 > --- /dev/null > +++ b/MdeModulePkg/Include/Library/ProtectedVariableLib.h > @@ -0,0 +1,700 @@ > +/** @file > + Defines interfaces of protected variable services for non-volatile variable > + storage. > + > +Copyright (c) 2020 - 2022, Intel Corporation. All rights reserved.<BR> > +SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef PROTECTED_VARIABLE_LIB_H_ > +#define PROTECTED_VARIABLE_LIB_H_ > + > +#include <PiPei.h> > +#include <PiDxe.h> > + > +#include <Guid/VariableFormat.h> > + > +#include <Protocol/VarCheck.h> > +#include <Protocol/FirmwareVolumeBlock.h> > + > +#include <Library/UefiLib.h> > +#include <Library/EncryptionVariableLib.h> > + > +#pragma pack(1) > + > +typedef struct _VARIABLE_DIGEST_FLAGS { > + BOOLEAN Auth; // Authenticated variable format > + BOOLEAN Valid; // Valid variable data in current variable > + BOOLEAN Protected; // Protected variable (used in calculating > HMAC) > + BOOLEAN Encrypted; // Encrypted variable > + BOOLEAN Freeable; // Memory reserved for current node can > be freed > + BOOLEAN CacheIndexAhead; // Indicates if CacheIndex is Ahead > relative to Global structure > + BOOLEAN Reserved[2]; // Reserved fields > +} VARIABLE_DIGEST_FLAGS; > + > +typedef struct _VARIABLE_DIGEST { > + /// > + /// Pointer to digest of next variable in a pre-defined rule of order for > + /// integration verification. In other words, the final HMAC of all > + /// protected variables is calculated by concatenating digest of each > + /// variable in the order of this singly linked list. > + /// > + EFI_PHYSICAL_ADDRESS Prev; > + EFI_PHYSICAL_ADDRESS Next; > + /// > + /// Index to variable in physical store, used to locate the variable directly > + /// inside the store (Implementation dependent). > + /// > + EFI_PHYSICAL_ADDRESS StoreIndex; > + /// > + /// Index to variable in memory cache, used to locate the variable directly > + /// inside the cache (Implementation dependent). > + /// > + EFI_PHYSICAL_ADDRESS CacheIndex; > + > + /// > + /// Pointer to Cache offset within Global Structure > + /// > + UINT32 CacheOffset; > + > + /// > + /// Frequently accessed information relating to the variable. > + /// > + UINT16 DigestSize; // Size of digest value > + UINT16 NameSize; // Size of variable name > + UINT32 DataSize; // Size of variable data > + UINT32 PlainDataSize; // Size of plain data of current variable (if > encrypted) > + UINT32 State; // State of current variable > + UINT32 Attributes; // Attributes of current variable > + > + EFI_GUID VendorGuid; // GUID > + VARIABLE_DIGEST_FLAGS Flags; // Variable digest flags > + // > + // Data with variable length are put at the end of this structure. > + // > + // CHAR16 VariableName[NameSize/2]; > + // UINT8 DigestValue[DigestSize]; > +} VARIABLE_DIGEST; > + > +#pragma pack() > + > +#define VAR_DIG_NAMEOFF(VarDig) (sizeof (VARIABLE_DIGEST)) > +#define VAR_DIG_DIGOFF(VarDig) (VAR_DIG_NAMEOFF (VarDig) + (VarDig)- > >NameSize) > + > +#define VAR_DIG_END(VarDig) (VAR_DIG_DIGOFF (VarDig) + (VarDig)- > >DigestSize) > + > +#define VAR_DIG_VALUE(VarDig) (VOID *)((UINTN)(VarDig) + > VAR_DIG_DIGOFF (VarDig)) > +#define VAR_DIG_NAME(VarDig) (CHAR16 *)((UINTN)(VarDig) + > VAR_DIG_NAMEOFF (VarDig)) > +#define VAR_DIG_GUID(VarDig) &(VAR_DIG_PTR (VarDig)->VendorGuid) > + > +#define VAR_DIG_PTR(Addr) ((VARIABLE_DIGEST *)(UINTN)(Addr)) > +#define VAR_DIG_ADR(Ptr) ((EFI_PHYSICAL_ADDRESS)(UINTN)(Ptr)) > +#define VAR_DIG_NEXT(VarDig) (VAR_DIG_PTR ((VarDig)->Next)) > +#define VAR_DIG_PREV(VarDig) (VAR_DIG_PTR ((VarDig)->Prev)) > + > +#define VAR_INDEX_INVALID ((UINT64)(-1)) > + > +#define VAR_HDR_PTR(Addr) ((VARIABLE_HEADER *)(UINTN)(Addr)) > + > +typedef VARIABLE_ENCRYPTION_INFO PROTECTED_VARIABLE_INFO; > + > +/** > + > + This function writes data to the NV variable storage at given position. > + > + Note: Per current variable service architecture, only SMM is allowed to > + (directly) change NV variable storage. > + > + @param VariableInfo Pointer to structure holding details of a variable. > + @param Offset Offset to the given variable to write from. > + @param Size Size of data to be written. > + @param Buffer Pointer to the buffer from which data is written. > + > + @retval EFI_INVALID_PARAMETER Invalid parameters passed in. > + @retval EFI_UNSUPPORTED Updating NV variable storage is not > supported. > + @retval EFI_OUT_OF_RESOURCES Not enough resource to complete the > operation. > + @retval EFI_SUCCESS Variable store successfully updated. > + > +**/ > +typedef > +EFI_STATUS > +(EFIAPI *PROTECTED_VAR_LIB_UPDATE_VARIABLE_STORE)( > + IN PROTECTED_VARIABLE_INFO *VariableInfo, > + IN UINTN Offset, > + IN UINT32 Size, > + IN UINT8 *Buffer > + ); > + > +/** > + Update the variable region with Variable information. > + > + @param[in] AuthVariableInfo Pointer AUTH_VARIABLE_INFO structure for > + input of the variable. [JianJW] "Pointer" -> "Pointer to" > + > + @retval EFI_SUCCESS The update operation is success. > + @retval EFI_INVALID_PARAMETER Invalid parameter. > + @retval EFI_WRITE_PROTECTED Variable is write-protected. > + @retval EFI_OUT_OF_RESOURCES There is not enough resource. > + > +**/ > +typedef > +EFI_STATUS > +(EFIAPI *PROTECTED_VAR_LIB_UPDATE_VARIABLE)( > + IN AUTH_VARIABLE_INFO *AuthVariableInfo > + ); > + > +/** > + > + Retrieve details about a variable and return them in VariableInfo->Header. > + > + If VariableInfo->Address is given, this function will calculate its offset > + relative to given variable storage via VariableStore; Otherwise, it will try > + other internal variable storages or cached copies. It's assumed that, for all > + copies of NV variable storage, all variables are stored in the same relative > + position. If VariableInfo->Address is found in the range of any storage copies, > + its offset relative to that storage should be the same in other copies. > + > + If VariableInfo->Offset is given (non-zero) but not VariableInfo->Address, > + this function will return the variable memory address inside VariableStore, > + if given, via VariableInfo->Address; Otherwise, the address of other storage > + copies will be returned, if any. > + > + For a new variable whose offset has not been determined, a value of -1 as > + VariableInfo->Offset should be passed to skip the offset calculation. > + > + @param VariableInfo Pointer to variable information. > + > + @retval EFI_INVALID_PARAMETER VariableInfo is NULL or both VariableInfo- > >Address > + and VariableInfo->Offset are NULL (0). > + @retval EFI_NOT_FOUND If given Address or Offset is out of range of > + any given or internal storage copies. > + @retval EFI_SUCCESS Variable details are retrieved successfully. > + > +**/ > +typedef > +EFI_STATUS > +(EFIAPI *PROTECTED_VAR_LIB_GET_VAR_INFO)( > + IN OUT PROTECTED_VARIABLE_INFO *VariableInfo > + ); > + > +/** > + > + Retrieve details of the variable next to given variable within VariableStore. > + > + If VarInfo->Address is NULL, the first one in VariableStore is returned. > + > + VariableStart and/or VariableEnd can be given optionally for the situation > + in which the valid storage space is smaller than the VariableStore->Size. > + This usually happens when PEI variable services make a compact variable > + cache to save memory, which cannot make use VariableStore->Size to > determine > + the correct variable storage range. > + > + @param VariableStore Pointer to a variable storage. It's optional. > + @param VariableStart Start point of valid range in VariableStore. > + @param VariableEnd End point of valid range in VariableStore. > + @param VariableInfo Pointer to variable information. > + > + @retval EFI_INVALID_PARAMETER VariableInfo or VariableStore is NULL. > + @retval EFI_NOT_FOUND If the end of VariableStore is reached. > + @retval EFI_SUCCESS The next variable is retrieved successfully. [JianJW] The list of @param and @retval don't match the function prototype > + > +**/ > +typedef > +EFI_STATUS > +(EFIAPI *PROTECTED_VAR_LIB_GET_NEXT_VAR_INFO)( > + IN OUT PROTECTED_VARIABLE_INFO *VariableInfo > + ); > + > +typedef > +EFI_STATUS > +(EFIAPI *DIGEST_METHOD_CALLBACK)( > + IN VARIABLE_HEADER *Variable, > + IN OUT VARIABLE_DIGEST *Digest > + ); [JianJW] Missing function header comment > + > +/** > + > + Initialize a memory copy of NV variable storage. > + > + To save memory consumption (especially in PEI phase), it's allowed to cache > + only valid variables. In such case, an index table recording offset of each > + valid variables could be employed. The index table makes sure the cached > copy > + to be synchronized with the original copy in NV variable storage. To avoid > + TOCTOU issue, once the variables are cached in memory and verified, NV > + variable storage should not be used to read variable information. The cached > + copy should be used instead. > + > + If StoreCacheBase is not given, this function should return the required > + cache size and valid variable number, if VariableNumber is not NULL. Then > + the caller can prepare correct cache buffer and index table buffer before > + next calling. > + > + @param[in] CacheBase Base address of NV variable storage cache. > + @param[in] CacheSize Size of CacheBuffer. > + @param[out] CacheSize Size of required cache buffer. > + @param[out] DigBuffer Base address of digest of each variable. > + @param[in, out] DigBufferSize Digest size of one variable if DigestBuffer is > NULL. > + Size of DigestBuffer if DigestBuffer is NOT NULL. > + @param[out] DigSize Required size of DigestBuffer if DigestBuffer is > NULL. > + @param[out] DigMethod Method used to generate digest for each > variable. > + @param[out] VarNumber Number of valid variables. > + @param[out] AuthFlag Auth-variable indicator. > + > + @retval EFI_INVALID_PARAMETER CacheSize is NULL; Or, > + StoreCacheBase is 0 but *CacheSize it not. > + @retval EFI_VOLUME_CORRUPTED If original NV variable storage is > corrupted. > + @retval EFI_BUFFER_TOO_SMALL If *CacheSize is smaller than required > memory. > + @retval EFI_SUCCESS The cached variable storage is initialized. > + > +**/ > +typedef > +EFI_STATUS > +(EFIAPI *PROTECTED_VAR_LIB_INIT_VAR_STORE)( > + OUT VOID *CacheBase OPTIONAL, > + IN OUT UINT32 *CacheSize OPTIONAL, > + OUT VOID *DigBuffer OPTIONAL, > + IN OUT UINT32 *DigBufferSize OPTIONAL, > + IN UINT32 DigSize OPTIONAL, > + IN DIGEST_METHOD_CALLBACK DigMethod OPTIONAL, > + OUT UINT32 *VarNumber OPTIONAL, > + OUT BOOLEAN *AuthFlag OPTIONAL > + ); [JianJW] This function and above one are not used at all. Please remove it. > + > +/** > + > + Initiate a variable retrieval in SMM environment from non-SMM environment. > + > + This is usually required in BS/RT environment when local cached copy is in > + encrypted form. Variable decryption can only be done in SMM environment. > + > + @param[in] VariableName Name of Variable to be found. > + @param[in] VendorGuid Variable vendor GUID. > + @param[out] Attributes Attribute value of the variable found. > + @param[in, out] DataSize Size of Data found. If size is less than the > + data, this value contains the required size. > + @param[out] Data Data pointer. > + > + @retval EFI_SUCCESS Found the specified variable. > + @retval EFI_INVALID_PARAMETER Invalid parameter. > + @retval EFI_NOT_FOUND The specified variable could not be found. > + > +**/ > +typedef > +EFI_STATUS > +(EFIAPI *PROTECTED_VAR_LIB_FIND_VAR_SMM)( > + IN CHAR16 *VariableName, > + IN EFI_GUID *VendorGuid, > + OUT UINT32 *Attributes OPTIONAL, > + IN OUT UINTN *DataSize, > + OUT VOID *Data OPTIONAL > + ); [JianJW] The parameters are not aligned. > + > +/** > + Check if a variable is user variable or not. > + > + @param[in] Variable Pointer to variable header. > + > + @retval TRUE User variable. > + @retval FALSE System variable. > + > +**/ > +typedef > +BOOLEAN > +(EFIAPI *PROTECTED_VAR_LIB_IS_USER_VAR)( > + IN VARIABLE_HEADER *Variable > + ); [JianJW] This function type is not used at all. Please remove it. > + > +/** > + Check if a HOB variable store is available or not. > + > + @retval EFI_NOT_READY HOB variable store info not available. > + @retval EFI_NOT_FOUND HOB variable store is NOT available. > + @retval EFI_SUCCESS HOB variable store is available. > +**/ > +typedef > +EFI_STATUS > +(EFIAPI *PROTECTED_VAR_LIB_HOB_STORE_AVAILABLE)( > + VOID > + ); > + > +typedef enum { > + FromPeiModule, > + FromBootServiceModule, > + FromRuntimeModule, > + FromSmmModule > +} VARIABLE_SERVICE_USER; > + > +#pragma pack(1) > + > +#define PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION 0x02 > + > +typedef struct _PROTECTED_VARIABLE_CONTEXT_IN { > + UINT32 StructVersion; > + UINT32 StructSize; > + UINT32 MaxVariableSize; > + > + VARIABLE_SERVICE_USER VariableServiceUser; > + > + PROTECTED_VAR_LIB_FIND_VAR_SMM FindVariableSmm; > + PROTECTED_VAR_LIB_GET_VAR_INFO GetVariableInfo; > + PROTECTED_VAR_LIB_GET_NEXT_VAR_INFO GetNextVariableInfo; > + PROTECTED_VAR_LIB_UPDATE_VARIABLE_STORE UpdateVariableStore; > + PROTECTED_VAR_LIB_UPDATE_VARIABLE UpdateVariable; > + PROTECTED_VAR_LIB_HOB_STORE_AVAILABLE > IsHobVariableStoreAvailable; > +} PROTECTED_VARIABLE_CONTEXT_IN; > + > +#pragma pack() > + > +/** > + > + Initialization for protected variable services. > + > + If this initialization failed upon any error, the whole variable services > + should not be used. A system reset might be needed to re-construct NV > + variable storage to be the default state. > + > + @param[in] ContextIn Pointer to variable service context needed by > + protected variable. > + > + @retval EFI_SUCCESS Protected variable services are ready. > + @retval EFI_INVALID_PARAMETER If ContextIn == NULL or something > missing or > + mismatching in the content in ContextIn. > + @retval EFI_COMPROMISED_DATA If failed to check integrity of protected > variables. > + @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource. > + @retval EFI_UNSUPPORTED Unsupported to process protected variable. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibInitialize ( > + IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn > + ); > + > +/** > + > + An alternative version of ProtectedVariableLibGetData to get plain data, if > + encrypted, from given variable, for different use cases. > + > + @param[in,out] VarInfo Pointer to structure containing variable > information. > + > + @retval EFI_SUCCESS Found the specified variable. > + @retval EFI_INVALID_PARAMETER VarInfo is NULL or both VarInfo- > >Address and > + VarInfo->Offset are invalid. > + @retval EFI_NOT_FOUND The specified variable could not be found. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibGetByInfo ( > + IN OUT PROTECTED_VARIABLE_INFO *VarInfo > + ); > + > +/** > + This service retrieves a variable's value using its name and GUID. > + > + Read the specified variable from the UEFI variable store. If the Data > + buffer is too small to hold the contents of the variable, the error > + EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer > + size to obtain the data. > + > + @param VariableName A pointer to a null-terminated string that is the > variable's name. > + @param VariableGuid A pointer to an EFI_GUID that is the variable's > GUID. The combination of > + VariableGuid and VariableName must be unique. > + @param Attributes If non-NULL, on return, points to the variable's > attributes. > + @param DataSize On entry, points to the size in bytes of the Data > buffer. > + On return, points to the size of the data returned in Data. > + @param Data Points to the buffer which will hold the returned > variable value. > + May be NULL with a zero DataSize in order to determine the > size of the buffer needed. > + > + @retval EFI_SUCCESS The variable was read successfully. > + @retval EFI_NOT_FOUND The variable was be found. > + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting > data. > + DataSize is updated with the size required for > + the specified variable. > + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or > Data is NULL. > + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of > a device error. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibGetByName ( > + IN CONST CHAR16 *VariableName, > + IN CONST EFI_GUID *VariableGuid, > + OUT UINT32 *Attributes, > + IN OUT UINTN *DataSize, > + OUT VOID *Data OPTIONAL > + ); [JianJW] The data type of parameters are not aligned. > + > +/** > + > + Retrieve plain data, if encrypted, of given variable. > + > + If variable encryption is employed, this function will initiate a SMM request > + to get the plain data. Due to security consideration, the decryption can only > + be done in SMM environment. > + > + @param[in] Variable Pointer to header of a Variable. > + @param[out] Data Pointer to plain data of the given variable. > + @param[in, out] DataSize Size of data returned or data buffer needed. > + @param[in] AuthFlag Auth-variable indicator. > + > + @retval EFI_SUCCESS Found the specified variable. > + @retval EFI_INVALID_PARAMETER Invalid parameter. > + @retval EFI_NOT_FOUND The specified variable could not be found. > + @retval EFI_BUFFER_TOO_SMALL If *DataSize is smaller than needed. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibGetByBuffer ( > + IN VARIABLE_HEADER *Variable, > + IN OUT VOID *Data, > + IN OUT UINT32 *DataSize, > + IN BOOLEAN AuthFlag > + ); > + > +/** > + > + Prepare for variable update. > + > + This is needed only once during current boot to mitigate replay attack. Its > + major job is to advance RPMC (Replay Protected Monotonic Counter). > + > + @retval EFI_SUCCESS Variable is ready to update hereafter. > + @retval EFI_UNSUPPORTED Updating variable is not supported. > + @retval EFI_DEVICE_ERROR Error in advancing RPMC. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibWriteInit ( > + VOID > + ); > + > +/** > + > + Update a variable with protection provided by this library. > + > + If variable encryption is employed, the new variable data will be encrypted > + before being written to NV variable storage. > + > + A special variable, called "MetaDataHmacVar", will always be updated along > + with variable being updated to reflect the changes (HMAC value) of all > + protected valid variables. The only exceptions, currently, are variable > + "MetaDataHmacVar" itself and variable "VarErrorLog". > + > + The buffer passed by NewVariable must be double of maximum variable size, > + which allows to pass the "MetaDataHmacVar" back to caller along with > encrypted > + new variable data, if any. This can make sure the new variable data and > + "MetaDataHmacVar" can be written at almost the same time to reduce the > chance > + of compromising the integrity. > + > + If *NewVariableSize is zero, it means to delete variable passed by CurrVariable > + and/or CurrVariableInDel. "MetaDataHmacVar" will be updated as well in such > + case because of less variables in storage. NewVariable should be always > passed > + in to convey new "MetaDataHmacVar" back. > + > + @param[in,out] CurrVariable Variable to be updated. It's NULL if > + adding a new variable. > + @param[in,out] CurrVariableInDel In-delete-transition copy of updating > variable. > + @param[in] NewVariable Buffer of new variable data. > + @param[out] NewVariable Buffer of "MetaDataHmacVar" and new > + variable (encrypted). > + @param[in] NewVariableSize Size of NewVariable. > + @param[out] NewVariableSize Size of (encrypted) NewVariable and > + "MetaDataHmacVar". > + > + @retval EFI_SUCCESS The variable is updated with protection > successfully. > + @retval EFI_INVALID_PARAMETER NewVariable is NULL. > + @retval EFI_NOT_FOUND Information missing to finish the operation. > + @retval EFI_ABORTED Failed to encrypt variable or calculate HMAC. > + @retval EFI_NOT_READY The RPMC device is not yet initialized. > + @retval EFI_DEVICE_ERROR The RPMC device has error in updating. > + @retval EFI_ACCESS_DENIED The given variable is not allowed to update. > + Currently this only happens on updating > + "MetaDataHmacVar" from code outside of this > + library. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibUpdate ( > + IN OUT VARIABLE_HEADER *CurrVariable OPTIONAL, > + IN OUT VARIABLE_HEADER *CurrVariableInDel OPTIONAL, > + IN OUT VARIABLE_HEADER *NewVariable, > + IN OUT UINTN *NewVariableSize > + ); > + > +/** > + > + Finalize a variable updating after it's written to NV variable storage > + successfully. > + > + This usually includes works like increasing RPMC, synchronizing local cache, > + updating new position of "MetaDataHmacVar", deleting old copy of > "MetaDataHmacVar" > + completely, etc. > + > + @param[in] NewVariable Buffer of new variables and > MetaDataHmacVar. > + @param[in] VariableSize Size of buffer pointed by NewVariable. > + @param[in] Offset Offset to NV variable storage from where the > new [JianJW] The "Offset" doesn't match the parameter name (StoreIndex) used in below > + variable and MetaDataHmacVar have been written. > + > + @retval EFI_SUCCESS No problem in winding up the variable write > operation. > + @retval Others Failed to updating state of old copy of updated > + variable, or failed to increase RPMC, etc. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibWriteFinal ( > + IN VARIABLE_HEADER *NewVariable, > + IN UINTN VariableSize, > + IN UINT64 StoreIndex > + ); > + > +/** > + Return the request variable name and GUID as per StoreIndex. > + > + This function is called multiple times to retrieve the VariableName > + and VariableGuid of all variables currently available in the system. > + On each call, the previous results are passed into the interface, > + and, on return, the interface returns the data for the next > + interface. When the entire variable list has been returned, > + EFI_NOT_FOUND is returned. > + > + @param This A pointer to this instance of the > EFI_PEI_READ_ONLY_VARIABLE2_PPI. > + > + @param VariableNameSize On entry, points to the size of the buffer pointed > to by VariableName. > + On return, the size of the variable name buffer. > + @param VariableName On entry, a pointer to a null-terminated string that > is the variable's name. > + On return, points to the next variable's null-terminated name > string. > + @param VariableGuid On entry, a pointer to an EFI_GUID that is the > variable's GUID. > + On return, a pointer to the next variable's GUID. [JianJW] The @param list doesn't match the real parameters declared below. > + > + @retval EFI_SUCCESS The variable was read successfully. > + @retval EFI_NOT_FOUND The variable could not be found. > + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the > resulting > + data. VariableNameSize is updated with the size > + required for the specified variable. > + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or > + VariableNameSize is NULL. > + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of > a device error. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibFind ( > + IN OUT PROTECTED_VARIABLE_INFO *VarInfo > + ); > + > +/** > + Return the next variable name and GUID. > + > + This function is called multiple times to retrieve the VariableName > + and VariableGuid of all variables currently available in the system. > + On each call, the previous results are passed into the interface, > + and, on return, the interface returns the data for the next > + interface. When the entire variable list has been returned, > + EFI_NOT_FOUND is returned. > + > + @param VariableNameSize On entry, points to the size of the buffer pointed > to by VariableName. > + On return, the size of the variable name buffer. > + @param VariableName On entry, a pointer to a null-terminated string that > is the variable's name. > + On return, points to the next variable's null-terminated name > string. > + @param VariableGuid On entry, a pointer to an EFI_GUID that is the > variable's GUID. > + On return, a pointer to the next variable's GUID. > + > + @retval EFI_SUCCESS The variable was read successfully. > + @retval EFI_NOT_FOUND The variable could not be found. > + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the > resulting > + data. VariableNameSize is updated with the size > + required for the specified variable. > + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or > + VariableNameSize is NULL. > + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of > a device error. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibFindNext ( > + IN OUT UINTN *VariableNameSize, > + IN OUT CHAR16 *VariableName, > + IN OUT EFI_GUID *VariableGuid > + ); > + > +/** > + Find variable via information in data structure PROTECTED_VARIABLE_INFO. > + > + If VarInfo->StoreIndex is given and valid, always used it to search variable > + in store. Otherwise, search the variable via variable name and guid pointed > + by VarInfo->Header.VariableName and VarInfo->Header.VendorGuid. > + > + @param VarInfo Pointer to data containing variable information. > + > + @return EFI_SUCCESS Found the variable. > + @return EFI_INVALID_PARAMETER No valid variable information is given. > + @return EFI_NOT_FOUND The given variable was not found or no more > + variables available. > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibFindNextEx ( > + IN OUT PROTECTED_VARIABLE_INFO *VarInfo > + ); > + > +/** > + Refresh variable information changed by variable service. > + > + @param Variable Pointer to buffer of the updated variable. > + @param VariableSize Size of variable pointed by Variable. > + @param StoreIndex New index of the variable in store. > + @param RefreshData Flag to indicate if the variable has been updated. > + > + @return EFI_SUCCESS No error occurred in updating. > + @return EFI_NOT_FOUND The given variable was not found in > + ProtectedVariableLib. > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibRefresh ( > + IN VARIABLE_HEADER *Variable, > + IN UINTN VariableSize, > + IN UINT64 StoreIndex, > + IN BOOLEAN RefreshData > + ); > + > +/** > + Refresh variable information changed by variable service. [JianJW] This function description looks unmatching the function name. > + > + @param Buffer Pointer to a pointer of buffer. > + @param NumElements Pointer to number of elements in list. > + > + > + @return EFI_SUCCESS Successfully retrieved sorted list. > + @return others Unsuccessful. > + > +**/ > +EFI_STATUS > +EFIAPI > +ProtectedVariableLibGetSortedList ( > + IN OUT EFI_PHYSICAL_ADDRESS **Buffer, > + IN OUT UINTN *NumElements > + ); > + > +/** > + > + Determine if the variable is the HMAC variable > + > + @param VariableName Pointer to variable name. > + > + @return TRUE Variable is HMAC variable > + @return FALSE Variable is not HMAC variable > + > +**/ > +BOOLEAN > +ProtectedVariableLibIsHmac ( > + IN CHAR16 *VariableName > + ); > + > +#endif > -- > 2.35.1.windows.2 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#89695): https://edk2.groups.io/g/devel/message/89695 Mute This Topic: https://groups.io/mt/90781891/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=-=-=-=-=-=-=-=-=-=-=-
© 2016 - 2022 Red Hat, Inc.