From: "Wang, Jian J" <jian.j.wang@intel.com>
To: "Vang, Judah" <judah.vang@intel.com>,
"devel@edk2.groups.io" <devel@edk2.groups.io>
Cc: "Yao, Jiewen" <jiewen.yao@intel.com>,
"Xu, Min M" <min.m.xu@intel.com>,
"Mistry, Nishant C" <nishant.c.mistry@intel.com>
Subject: Re: [PATCH v5 18/19] SecurityPkg: Add Protected Variable Services
Date: Tue, 22 Nov 2022 07:59:52 +0000 [thread overview]
Message-ID: <MW4PR11MB6763CFA4ED41217F261D42E9B60D9@MW4PR11MB6763.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20221106073509.3071-19-judah.vang@intel.com>
Judah,
Several format issues. See inline comments starting with "[JianJW]".
With them addressed,
Reviewed-by: Jian J Wang <jian.j.wang@intel.com>
Regards,
Jian
> -----Original Message-----
> From: Vang, Judah <judah.vang@intel.com>
> Sent: Sunday, November 06, 2022 3:35 PM
> To: devel@edk2.groups.io
> Cc: Wang, Jian J <jian.j.wang@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>;
> Xu, Min M <min.m.xu@intel.com>; Mistry, Nishant C
> <nishant.c.mistry@intel.com>
> Subject: [PATCH v5 18/19] SecurityPkg: Add Protected Variable Services
>
> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594
>
> V5: Applied code review comments. Remove unused API.
> V3: Change placement of buffer used for confidentiality crypto
> operation to fix an issue when enabling confidentiality. Remove
> un-needed increment of monotonic counter.
>
> V1: Add Protected Variable Services across the different UEFI phases.
> Functions includes creating variable digest, performing integrity
> check, initializing protected variables, updating protected
> variables, and verifying the MetaDataHmacVar variable.
> This module prevents UEFI variable tampering. It provides
> variable integrity and confidentiality.
>
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Min Xu <min.m.xu@intel.com>
> 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>
> ---
> SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf | 64
> +
> SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf | 68
> +
> SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf |
> 67 +
> SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.inf
> | 62 +
> SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h |
> 589 ++++++
> SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c |
> 2103 ++++++++++++++++++++
> SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c | 163
> ++
> SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c | 1327
> ++++++++++++
> SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c | 209
> ++
> SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon.c
> | 967 +++++++++
> SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c |
> 233 +++
> 11 files changed, 5852 insertions(+)
>
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
> b/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
> new file mode 100644
> index 000000000000..74a0285af7ef
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
> @@ -0,0 +1,64 @@
> +## @file
> +# Provides protected variable services for EmulatorPkg.
> +#
> +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010029
> + BASE_NAME = DxeProtectedVariableLib
> + MODULE_UNI_FILE = ProtectedVariableLib.uni
> + FILE_GUID = 6F424E10-0F75-4716-9F97-58C2E1C643AD
> + MODULE_TYPE = DXE_RUNTIME_DRIVER
> + VERSION_STRING = 0.1
> + LIBRARY_CLASS = ProtectedVariableLib|DXE_RUNTIME_DRIVER
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +# VALID_ARCHITECTURES = IA32 X64
> +#
> +
> +[Sources]
> + ProtectedVariableDxe.c
> + ProtectedVariableCommon.c
> + ProtectedVariableSmmDxeCommon.c
> + ProtectedVariableInternal.h
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + SecurityPkg/SecurityPkg.dec
> + CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + DebugLib
> + MemoryAllocationLib
> + HobLib
> + BaseCryptLib
> + EncryptionVariableLib
> + RpmcLib
> + HashApiLib
> + SortLib
> +
> +[Protocols]
> + gEfiVariableWriteArchProtocolGuid
> +
> +[Guids]
> + gEdkiiMetaDataHmacVariableGuid
> + gEdkiiProtectedVariableGlobalGuid
> + gEdkiiVarErrorFlagGuid
> + gEdkiiProtectedVariableContextGuid
> +
> +[Pcd]
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
> b/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
> new file mode 100644
> index 000000000000..44c959a94ca3
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
> @@ -0,0 +1,68 @@
> +## @file
> +# Provides protected variable services.
> +#
> +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010029
> + BASE_NAME = PeiProtectedVariableLib
> + MODULE_UNI_FILE = ProtectedVariableLib.uni
> + FILE_GUID = 76FBFBCE-ACBB-4084-A348-8FCC97AAEB9D
> + MODULE_TYPE = PEIM
> + VERSION_STRING = 0.1
> + LIBRARY_CLASS = ProtectedVariableLib|PEIM
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +# VALID_ARCHITECTURES = IA32 X64 AARCH64
> +#
> +
> +[Sources]
> + ProtectedVariablePei.c
> + ProtectedVariableCommon.c
> + ProtectedVariableInternal.h
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + SecurityPkg/SecurityPkg.dec
> + CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + DebugLib
> + MemoryAllocationLib
> + HobLib
> + BaseCryptLib
> + RpmcLib
> + VariableKeyLib
> + EncryptionVariableLib
> + ReportStatusCodeLib
> + PeiServicesLib
> + HashApiLib
> + SortLib
> +
> +[Guids]
> + gEdkiiMetaDataHmacVariableGuid
> + gEdkiiProtectedVariableGlobalGuid
> + gEdkiiVarErrorFlagGuid
> + gEdkiiProtectedVariableContextGuid
> +
> +[Ppis]
> + gEfiPeiMemoryDiscoveredPpiGuid
> + gEfiPeiVariableStoreDiscoveredPpiGuid
> +
> +[Pcd]
> + gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeVariableIntegrity
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
> b/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
> new file mode 100644
> index 000000000000..ecf0b1a43d30
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
> @@ -0,0 +1,67 @@
> +## @file
> +# Provides protected variable services.
> +#
> +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010029
> + BASE_NAME = SmmProtectedVariableLib
> + MODULE_UNI_FILE = ProtectedVariableLib.uni
> + FILE_GUID = 2BEE71E5-259B-4057-A2C1-2115DF43C76A
> + MODULE_TYPE = DXE_SMM_DRIVER
> + VERSION_STRING = 0.1
> + LIBRARY_CLASS = ProtectedVariableLib|DXE_SMM_DRIVER
> MM_STANDALONE
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +# VALID_ARCHITECTURES = IA32 X64
> +#
> +
> +[Sources]
> + ProtectedVariableSmm.c
> + ProtectedVariableCommon.c
> + ProtectedVariableSmmDxeCommon.c
> + ProtectedVariableInternal.h
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + SecurityPkg/SecurityPkg.dec
> + CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + DebugLib
> + MemoryAllocationLib
> + HobLib
> + BaseCryptLib
> + EncryptionVariableLib
> + RpmcLib
> + VariableKeyLib
> + HashApiLib
> + SortLib
> +
> +[Protocols]
> + gEfiMmEndOfDxeProtocolGuid
> +
> +[Guids]
> + gSmmVariableWriteGuid
> + gEdkiiMetaDataHmacVariableGuid
> + gEdkiiProtectedVariableGlobalGuid
> + gEdkiiVarErrorFlagGuid
> + gEdkiiProtectedVariableContextGuid
> +
> +[Pcd]
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> +
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
> nf
> b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
> nf
> new file mode 100644
> index 000000000000..011ccdce2db8
> --- /dev/null
> +++
> b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
> nf
> @@ -0,0 +1,62 @@
> +## @file
> +# Provides protected variable services.
> +#
> +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010029
> + BASE_NAME = SmmRuntimeProtectedVariableLib
> + MODULE_UNI_FILE = ProtectedVariableLib.uni
> + FILE_GUID = 99A623DE-1AD3-4AB3-909D-E3AADD7845EF
> + MODULE_TYPE = DXE_RUNTIME_DRIVER
> + VERSION_STRING = 0.1
> + LIBRARY_CLASS = ProtectedVariableLib|DXE_DRIVER
> DXE_RUNTIME_DRIVER
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +# VALID_ARCHITECTURES = IA32 X64
> +#
> +
> +[Sources]
> + ProtectedVariableSmmRuntime.c
> + ProtectedVariableCommon.c
> + ProtectedVariableInternal.h
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + SecurityPkg/SecurityPkg.dec
> + CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + DebugLib
> + MemoryAllocationLib
> + HobLib
> + BaseCryptLib
> + EncryptionVariableLib
> + RpmcLib
> + HashApiLib
> + SortLib
> +
> +[Guids]
> + gEfiEventVirtualAddressChangeGuid
> + gEdkiiMetaDataHmacVariableGuid
> + gEdkiiProtectedVariableGlobalGuid
> + gEdkiiVarErrorFlagGuid
> + gEdkiiProtectedVariableContextGuid
> +
> +[Pcd]
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> + gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> + gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> +
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
> new file mode 100644
> index 000000000000..7948649b21bd
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
> @@ -0,0 +1,589 @@
> +/** @file
> + Definitions shared among different implementation of ProtectedVariableLib.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef PROTECTED_VARIABLE_INTERNAL_H_
> +#define PROTECTED_VARIABLE_INTERNAL_H_
> +
> +#include <Guid/VariableFormat.h>
> +#include <Guid/ProtectedVariable.h>
> +
> +#include <Library/DebugLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/BaseCryptLib.h>
> +#include <Library/RpmcLib.h>
> +#include <Library/VariableKeyLib.h>
> +#include <Library/EncryptionVariableLib.h>
> +#include <Library/ProtectedVariableLib.h>
> +#include <Library/HashApiLib.h>
> +
> +#define VARIABLE_KEY_SIZE (256/8)
> +
> +#define METADATA_HMAC_SIZE (256/8)
> +#define METADATA_HMAC_KEY_NAME L"HMAC_KEY"
> +#define METADATA_HMAC_KEY_NAME_SIZE 0x10
> +
> +#define METADATA_HMAC_SEP L":"
> +#define METADATA_HMAC_SEP_SIZE 2
> +
> +#define METADATA_HMAC_VARIABLE_NAME L"MetaDataHmacVar"
> +#define METADATA_HMAC_VARIABLE_NAME_SIZE sizeof
> (METADATA_HMAC_VARIABLE_NAME)
> +#define METADATA_HMAC_VARIABLE_GUID
> gEdkiiMetaDataHmacVariableGuid
> +#define METADATA_HMAC_VARIABLE_ATTR
> VARIABLE_ATTRIBUTE_NV_BS_RT
> +
> +#define DIGEST_CONTEXT_SIZE (HashApiGetContextSize())
> +
> +#define MAX_VARIABLE_SIZE \
> + MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32
> (PcdMaxAuthVariableSize)), \
> + PcdGet32 (PcdMaxHardwareErrorVariableSize))
> +
> +#define IS_VARIABLE(Var, Name, Guid) \
> + (StrCmp ((Var)->VariableName, (Name)) == 0 \
> + && CompareGuid ((CONST EFI_GUID *)(Var)->VendorGuid, (CONST EFI_GUID
> *)(Guid)))
> +
> +#define VARIABLE_SIZE(VarInfo) \
> + (((UINTN)(VarInfo)->Header.Data - (UINTN)(VarInfo)->Buffer) \
> + + (VarInfo)->Header.DataSize \
> + + GET_PAD_SIZE ((VarInfo)->Header.DataSize))
> +
> +#define VARIABLE_HEADER_SIZE(AuthFlag) \
> + ((AuthFlag) ? sizeof (AUTHENTICATED_VARIABLE_HEADER) \
> + : sizeof (VARIABLE_HEADER))
> +
> +#define VARIABLE_NAME(Var, AuthFlag) \
> + ((CHAR16 *)((UINTN)(Var) + VARIABLE_HEADER_SIZE(AuthFlag)))
> +
> +#define VARIABLE_START(VarStore) \
> + ((VARIABLE_HEADER *)HEADER_ALIGN ((VARIABLE_STORE_HEADER
> *)(VarStore) + 1))
> +
> +#define VARIABLE_END(VarStore) \
> + ((VARIABLE_HEADER *)HEADER_ALIGN ((UINTN)(VarStore) \
> + + ((VARIABLE_STORE_HEADER *)(VarStore))->Size))
> +
> +#define SET_VARIABLE_DATA_SIZE(VarInfo, Size) \
> + if ((VarInfo)->Flags.Auth) { \
> + ((AUTHENTICATED_VARIABLE_HEADER *)((VarInfo)->Buffer))->DataSize =
> Size; \
> + (VarInfo)->Header.DataSize = Size; \
> + } else { \
> + ((VARIABLE_HEADER *)((VarInfo)->Buffer))->DataSize = Size; \
> + (VarInfo)->Header.DataSize = Size; \
> + }
> +
> +#define IS_KNOWN_UNPROTECTED_VARIABLE(Global, VarInfo) \
> + (CheckKnownUnprotectedVariable ((Global), (VarInfo)) <
> UnprotectedVarIndexMax)
> +
> +#define GET_CNTX(Global) ((PROTECTED_VARIABLE_CONTEXT_IN
> *)(UINTN)((Global)->ContextIn))
> +#define GET_BUFR(Address) ((VOID *)(UINTN)(Address))
> +#define GET_ADRS(Buffer) ((EFI_PHYSICAL_ADDRESS)(UINTN)(Buffer))
> +
> +typedef struct _VARIABLE_IDENTIFIER {
> + CHAR16 *VariableName;
> + EFI_GUID *VendorGuid;
> + UINT8 State;
> +} VARIABLE_IDENTIFIER;
> +
> +typedef enum {
> + IndexHmacInDel = 0, /// MetaDataHmacVar with state
> VAR_IN_DELETED_TRANSITION
> + IndexHmacAdded, /// MetaDataHmacVar with state VAR_ADDED
> + IndexErrorFlag, /// VarErrorFlag
> + IndexPlatformVar, /// Platform Variable
> + UnprotectedVarIndexMax
> +} UNPROTECTED_VARIABLE_INDEX;
> +
> +#pragma pack(1)
> +
> +#define PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION 0x02
> +
> +typedef struct _PROTECTED_VARIABLE_FLAG {
> + BOOLEAN Auth; // Authenticated variable format
> + BOOLEAN WriteInit; // Write-init-done
> + BOOLEAN WriteReady; // Ready-to-write
> + BOOLEAN RecoveryMode; // Variable storage recovery or provisioning
> + BOOLEAN CacheReady; // Indicates Cache is available
> + BOOLEAN Reserved; // reserved
> +} PROTECTED_VARIABLE_FLAG;
> +
> +typedef struct _PROTECTED_VARIABLE_GLOBAL {
> + UINT32 StructVersion;
> + UINT32 StructSize;
> +
> + ///
> + /// Variable root key used to derive Encryption key and HMAC key.
> + ///
> + UINT8 RootKey[VARIABLE_KEY_SIZE];
> + ///
> + /// HMAC key derived from RootKey.
> + ///
> + UINT8 MetaDataHmacKey[VARIABLE_KEY_SIZE];
> + ///
> + /// Number of variables in linked list pointed by VariableDigests.
> + ///
> + UINT32 VariableNumber;
> + ///
> + /// Size of memory reserved by VariableCache.
> + ///
> + UINT32 VariableCacheSize;
> + ///
> + /// Memory reserved to temporarily hold data of one variable, for integrity
> + /// validation purpose.
> + ///
> + EFI_PHYSICAL_ADDRESS VariableCache;
> + ///
> + /// Pointer to linked list, in which each node holds the digest value of each
> + /// variable.
> + ///
> + EFI_PHYSICAL_ADDRESS VariableDigests;
> + ///
> + /// Memory reserved for Context used in hash API to avoid repeat alloc/free.
> + ///
> + EFI_PHYSICAL_ADDRESS DigestContext;
> + ///
> + /// Pointer to one of node in linked list pointed by VariableDigests, which
> + /// has been just accessed. This is mainly used to facilitate the two calls
> + /// use case of GetVariable().
> + ///
> + EFI_PHYSICAL_ADDRESS LastAccessedVariable;
> + ///
> + /// Cached copy of pointers to nodes of unprotected variables in the linked
> + /// list pointed by VariableDigests.
> + ///
> + EFI_PHYSICAL_ADDRESS Unprotected[UnprotectedVarIndexMax];
> + ///
> + /// Pointer to data structure holding helper functions passed by user of
> + /// ProtectedVariableLib, most of which are used to complete operations on
> + /// variable storage.
> + ///
> + EFI_PHYSICAL_ADDRESS ContextIn;
> +
> + ///
> + /// Pointer to Global data structure. This is to hold pre-mem address value.
> + /// Later to be used to identify pre-mem to post-mem transition.
> + ///
> + EFI_PHYSICAL_ADDRESS GlobalSelf;
> +
> + PROTECTED_VARIABLE_FLAG Flags;
> +} PROTECTED_VARIABLE_GLOBAL;
> +
> +#pragma pack()
> +
> +/* Sort method function pointer taking two parameters */
> +typedef
> +INTN
> +(*SORT_METHOD) (
> + IN VARIABLE_DIGEST *Variable1,
> + IN VARIABLE_DIGEST *Variable2
> + );
> +
> +/* Update variable digest data function pointer */
> +typedef
> +BOOLEAN
> +(*DIGEST_UPDATE) (
> + IN OUT VOID *Context,
> + IN VOID *Data,
> + IN UINTN DataSize
> + );
> +
> +/**
> +
> + Print variable information
> +
> + @param[in] Data8 Pointer to data
> + @param[out] DataSize Size of data
> +
> +**/
> +VOID
> +PrintVariableData (
> + IN UINT8 *Data8,
> + IN UINTN DataSize
> + );
> +
> +/**
> +
> + Derive HMAC key from given variable root key.
> +
> + @param[in] RootKey Pointer to root key to derive from.
> + @param[in] RootKeySize Size of root key.
> + @param[out] HmacKey Pointer to generated HMAC key.
> + @param[in] HmacKeySize Size of HMAC key.
> +
> + @retval TRUE The HMAC key is derived successfully.
> + @retval FALSE Failed to generate HMAC key from given root key.
> +
> +**/
> +BOOLEAN
> +EFIAPI
> +GenerateMetaDataHmacKey (
> + IN CONST UINT8 *RootKey,
> + IN UINTN RootKeySize,
> + OUT UINT8 *HmacKey,
> + IN UINTN HmacKeySize
> + );
> +
> +/**
> +
> + Digests the given variable data and updates HMAC context.
> +
> + @param[in,out] Context Pointer to initialized HMAC context.
> + @param[in] VarInfo Pointer to variable data.
> +
> + @retval TRUE HMAC context was updated successfully.
> + @retval FALSE Failed to update HMAC context.
> +
> +**/
> +BOOLEAN
> +UpdateVariableMetadataHmac (
> + IN VOID *Context,
> + IN PROTECTED_VARIABLE_INFO *VarInfo
> + );
> +
> +/**
> +
> + Re-calculate HMAC based on new variable data and re-generate
> MetaDataHmacVar.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] NewVarInfo Pointer to buffer of new variable data.
> + @param[in,out] NewHmacVarInfo Pointer to buffer of new
> MetaDataHmacVar.
> +
> + @return EFI_SUCCESS The HMAC value was updated successfully.
> + @return EFI_ABORTED Failed to calculate the HMAC value.
> + @return EFI_OUT_OF_RESOURCES Not enough resource to calculate HMC
> value.
> + @return EFI_NOT_FOUND The MetaDataHmacVar was not found in
> storage.
> +
> +**/
> +EFI_STATUS
> +RefreshVariableMetadataHmac (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *NewVarInfo,
> + IN OUT PROTECTED_VARIABLE_INFO *NewHmacVarInfo
> + );
> +
> +/**
> +
> + Retrieve the context and global configuration data structure from HOB.
> +
> + Once protected NV variable storage is cached and verified in PEI phase,
> + all related information are stored in a HOB which can be used by PEI variable
> + service itself and passed to SMM along with the boot flow, which can avoid
> + many duplicate works, like generating HMAC key, verifying NV variable
> storage,
> + etc.
> +
> + The HOB can be identified by gEdkiiProtectedVariableGlobalGuid.
> +
> + @param[out] Global Pointer to global configuration data from PEI phase.
> +
> + @retval EFI_SUCCESS The HOB was found, and Context and Global are
> retrieved.
> + @retval EFI_NOT_FOUND The HOB was not found.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobalFromHob (
> + OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
> + );
> +
> +/**
> +
> + Get context and/or global data structure used to process protected variable.
> +
> + @param[out] Global Pointer to global configuration data.
> +
> + @retval EFI_SUCCESS Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> + OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
> + );
> +
> +/**
> +
> + Get context data structure used to process protected variable.
> +
> + @param[out] ContextIn Pointer to context provided by variable runtime
> services.
> +
> + @retval EFI_SUCCESS Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableContext (
> + PROTECTED_VARIABLE_CONTEXT_IN **ContextIn OPTIONAL
> + );
> +
> +/**
> +
> + Check if a given variable is unprotected variable specified in advance
> + and return its index ID.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to variable information data.
> +
> + @retval IndexHmacInDel Variable is MetaDataHmacVar in delete-transition
> state.
> + @retval IndexHmacAdded Variable is MetaDataHmacVar in valid state.
> + @retval IndexErrorFlag Variable is VarErrorLog.
> + @retval Others Variable is not any known unprotected variables.
> +
> +**/
> +UNPROTECTED_VARIABLE_INDEX
> +CheckKnownUnprotectedVariable (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo
> + );
> +
> +/**
> +
> + Return the size of variable MetaDataHmacVar.
> +
> + @param[in] AuthFlag Auth-variable indicator.
> +
> + @retval size of variable MetaDataHmacVar.
> +
> +**/
> +UINTN
> +GetMetaDataHmacVarSize (
> + IN BOOLEAN AuthFlag
> + );
> +
> +/**
> +
> + Fix state of MetaDataHmacVar on NV variable storage, if there's failure at
> + last boot during updating variable.
> +
> + This must be done before the first writing of variable in current boot,
> + including storage reclaim.
> +
> + @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 was successfully updated.
> +
> +**/
> +EFI_STATUS
> +FixupHmacVariable (
> + VOID
> + );
> +
> +/**
> +
> + Verify the variable digest.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to verified copy of protected variables.
> + @param[in] VarDig Pointer to variable digest data.
> +
> + @retval EFI_INVALID_PARAMETER Invalid parameter was passed in.
> + @retval EFI_OUT_OF_RESOURCES Not enough resource to calculate hash.
> + @retval EFI_ABORTED An error was encountered.
> + @retval EFI_COMPROMISED_DATA The data was compromised.
> + @retval EFI_SUCCESS Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +VerifyVariableDigest (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo,
> + IN VARIABLE_DIGEST *VarDig
> + );
> +
> +/**
> +
> + Get the variable digest.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to verified copy of protected variables.
> + @param[in,out] DigestValue Pointer to variable digest value.
> +
> + @retval EFI_INVALID_PARAMETER Invalid parameter was passed in.
> + @retval EFI_OUT_OF_RESOURCES Not enough resource to calculate hash.
> + @retval EFI_ABORTED An error was encountered.
> + @retval EFI_COMPROMISED_DATA The data was compromised.
> + @retval EFI_SUCCESS Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +GetVariableDigest (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo,
> + IN OUT UINT8 *DigestValue
> + );
> +
> +/**
> +
> + Compare variable name and Guid
> +
> + @param[in] Name1 Name of first variable.
> + @param[in] Name1Size Size of first variable.
> + @param[in] Name2 Name of second variable.
> + @param[in] Name2Size Size of second variable.
> + @param[in] Guid1 Guid for first variable.
> + @param[in] Guid2 Guid for second variable.
> +
> + @retval 0 First name is identical to Second name.
> + @return others First name is not identical to Second name.
> +
> +**/
> +INTN
> +CompareVariableNameAndGuid (
> + IN CONST CHAR16 *Name1,
> + IN UINTN Name1Size,
> + IN CONST CHAR16 *Name2,
> + IN UINTN Name2Size,
> + IN EFI_GUID *Guid1,
> + IN EFI_GUID *Guid2
> + );
> +
> +/**
> +
> + Compare variable digest.
> +
> + @param[in] Variable1 Pointer to first variable digest.
> + @param[in] Variable2 Pointer to second variable digest.
> +
> + @retval 0 Variables are identical.
> + @return others Variables are not identical.
> +
> +**/
> +INTN
> +CompareVariableDigestInfo (
> + IN VARIABLE_DIGEST *Variable1,
> + IN VARIABLE_DIGEST *Variable2
> + );
> +
> +/**
> +
> + Move a node backward in the order controlled by SortMethod.
> +
> + @param[in] Node Pointer to node to be moved.
> + @param[in] SortMethod Method used to compare node in list.
> +
> +**/
> +VOID
> +MoveNodeBackward (
> + IN OUT VARIABLE_DIGEST *Node,
> + IN SORT_METHOD SortMethod
> + );
> +
> +/**
> +
> + Remove variable digest node.
> +
> + @param[in,out] Global Pointer to global configuration data.
> + @param[in,out] VarDig Pointer to variable digest value.
> + @param[in] FreeResource Flag to indicate whether to free resource.
> +
> +**/
> +VOID
> +RemoveVariableDigestNode (
> + IN OUT PROTECTED_VARIABLE_GLOBAL *Global,
> + IN OUT VARIABLE_DIGEST *VarDig,
> + IN BOOLEAN FreeResource
> + );
> +
> +/**
> +
> + Insert variable digest node.
> +
> + @param[in,out] Global Pointer to global configuration data.
> + @param[in] VarDig Pointer to variable digest value.
> + @param[in] SortMethod Method for sorting.
> +
> +**/
> +VOID
> +InsertVariableDigestNode (
> + IN OUT PROTECTED_VARIABLE_GLOBAL *Global,
> + IN VARIABLE_DIGEST *VarDig,
> + IN SORT_METHOD SortMethod
> + );
> +
> +/**
> +
> + Create variable digest node.
> +
> + @param[in] VariableName Name of variable.
> + @param[in] VendorGuid Guid of variable.
> + @param[in] NameSize Size of variable name.
> + @param[in] DataSize Size of variable data.
> + @param[in] AuthVar Authenticated variable flag.
> + @param[in] Global Pointer to global configuration data.
> +
> + @retval Ptr Pointer to variable digest
> +
> +**/
> +VARIABLE_DIGEST *
> +CreateVariableDigestNode (
> + IN CHAR16 *VariableName,
> + IN EFI_GUID *VendorGuid,
> + IN UINT16 NameSize,
> + IN UINT32 DataSize,
> + IN BOOLEAN AuthVar,
> + IN PROTECTED_VARIABLE_GLOBAL *Global
> + );
> +
> +/**
> +
> + Find the specified variable digest
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to variable data.
> + @param[in] FindNext Flag to continue looking for variable.
> +
> +**/
> +VARIABLE_DIGEST *
> +FindVariableInternal (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo,
> + IN BOOLEAN FindNext
> + );
> +
> +/**
> +
> + Synchronize the RPMC counters
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to variable data.
> + @param[in] FindNext Flag to continue looking for variable.
> +
> + @retval EFI_SUCCESS Successfully sync RPMC counters.
> + @return others Failed to sync RPMC counters.
> +
> +**/
> +EFI_STATUS
> +SyncRpmcCounter (
> + VOID
> + );
> +
> +/**
> +
> + Perform for protected variable integrity check.
> +
> + 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
> +PerformVariableIntegrityCheck (
> + IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
> + );
> +
> +extern EFI_TIME mDefaultTimeStamp;
> +extern VARIABLE_IDENTIFIER
> mUnprotectedVariables[UnprotectedVarIndexMax];
> +extern PROTECTED_VARIABLE_CONTEXT_IN mVariableContextIn;
> +extern PROTECTED_VARIABLE_GLOBAL mProtectedVariableGlobal;
> +
> +#endif
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
> new file mode 100644
> index 000000000000..456c871a4561
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
> @@ -0,0 +1,2103 @@
> +/** @file
> + The common protected variable operation routines.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Base.h>
> +#include <Uefi.h>
> +#include <PiPei.h>
> +
> +#include <Guid/VariableFormat.h>
> +#include <Guid/VarErrorFlag.h>
> +
> +#include <Library/HobLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/HashApiLib.h>
> +#include <Library/SortLib.h>
> +
> +#include "ProtectedVariableInternal.h"
> +
> +EFI_TIME mDefaultTimeStamp = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
> +VARIABLE_IDENTIFIER mUnprotectedVariables[] = {
> + {
> + METADATA_HMAC_VARIABLE_NAME,
> + &METADATA_HMAC_VARIABLE_GUID,
> + VAR_ADDED & VAR_IN_DELETED_TRANSITION
> + },
> + {
> + METADATA_HMAC_VARIABLE_NAME,
> + &METADATA_HMAC_VARIABLE_GUID,
> + VAR_ADDED
> + },
> + {
> + VAR_ERROR_FLAG_NAME,
> + &gEdkiiVarErrorFlagGuid,
> + VAR_ADDED
> + },
> + {
> + (CHAR16 *)PcdGetPtr (PcdPlatformVariableName),
> + (EFI_GUID *)PcdGetPtr (PcdPlatformVariableGuid),
> + VAR_ADDED
> + }
> +};
> +
> +/**
> + Print variable information.
> +
> + @param[in] Data8 Pointer to data.
> + @param[in] DataSize Size of data.
> +
> +**/
> +VOID
> +PrintVariableData (
> + IN UINT8 *Data8,
> + IN UINTN DataSize
> + )
> +{
> + UINTN Index;
> +
> + for (Index = 0; Index < DataSize; Index++) {
> + if (Index % 0x10 == 0) {
> + DEBUG ((DEBUG_INFO, "\n%08X:", Index));
> + }
> +
> + DEBUG ((DEBUG_INFO, " %02X", *Data8++));
> + }
> +
> + DEBUG ((DEBUG_INFO, "\n"));
> +}
> +
> +/**
> +
> + Retrieve the context and global configuration data structure from HOB.
> +
> + Once protected NV variable storage is cached and verified in PEI phase,
> + all related information are stored in a HOB which can be used by PEI variable
> + service itself and passed to SMM along with the boot flow, which can avoid
> + many duplicate works, like generating HMAC key, verifying NV variable
> storage,
> + etc.
> +
> + The HOB can be identified by gEdkiiProtectedVariableGlobalGuid.
> +
> + @param[out] Global Pointer to global configuration data from PEI phase.
> +
> + @retval EFI_SUCCESS The HOB was found, and Context and Global are
> retrieved.
> + @retval EFI_NOT_FOUND The HOB was not found.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobalFromHob (
> + OUT PROTECTED_VARIABLE_GLOBAL **Global
> + )
> +{
> + VOID *Data;
> + EFI_PEI_HOB_POINTERS Hob;
> + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + EFI_PHYSICAL_ADDRESS OldStart;
> + VARIABLE_DIGEST *VarDig;
> + EFI_HOB_GUID_TYPE *GuidHob;
> + UINTN Index;
> +
> + Hob.Raw = GetFirstGuidHob (&gEdkiiProtectedVariableGlobalGuid);
> + if (Hob.Raw != NULL) {
> + Data = GET_GUID_HOB_DATA (Hob);
> + } else {
> + //
> + // Search the global from allocated memory blob.
> + //
> + Data = NULL;
> + MemoryAllocationHob = NULL;
> +
> + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
> + while (Hob.Raw != NULL) {
> + MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw;
> + if (CompareGuid (
> + &MemoryAllocationHob->AllocDescriptor.Name,
> + &gEdkiiProtectedVariableGlobalGuid
> + ))
> + {
> + Data = (VOID *)(UINTN)
> + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress;
> + break;
> + }
> +
> + Hob.Raw = GET_NEXT_HOB (Hob);
> + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
> Hob.Raw);
> + }
> + }
> +
> + if (Data == NULL) {
> + return EFI_NOT_FOUND;
> + }
> +
> + if (Global != NULL) {
> + GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
> + if (GuidHob != NULL) {
> + ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
> (GuidHob);
> + } else {
> + ASSERT (GuidHob == NULL);
> + }
> +
> + *Global = (PROTECTED_VARIABLE_GLOBAL *)((UINT8 *)Data);
> + //
> + // Fix pointers in the HOB (due to physical memory readiness)
> + //
> + if ((*Global)->GlobalSelf != (EFI_PHYSICAL_ADDRESS)(UINTN)(*Global)) {
> + OldStart = (*Global)->GlobalSelf;
> + (*Global)->ContextIn = GET_ADRS (ContextIn);
> +
> + //
> + // Mark Memory caching is available
> + //
> + (*Global)->Flags.CacheReady = TRUE;
> +
> + //
> + // Re-allocate new minimum cache
> + //
> + (*Global)->VariableCache = GET_ADRS (Data)
> + + ((*Global)->VariableCache - OldStart);
> +
> + (*Global)->DigestContext = GET_ADRS (((*Global) + 1));
> + for (Index = 0; Index < UnprotectedVarIndexMax; Index++) {
> + if ((*Global)->Unprotected[Index] != VAR_INDEX_INVALID) {
> + (*Global)->Unprotected[Index] = GET_ADRS (Data)
> + + ((*Global)->Unprotected[Index] - OldStart);
> + }
> + }
> +
> + (*Global)->LastAccessedVariable = GET_ADRS (Data)
> + + ((*Global)->LastAccessedVariable - OldStart);
> +
> + //
> + // Fix all linked-list pointers inside VARIABLE_SIGNATURE.
> + //
> + (*Global)->VariableDigests = GET_ADRS (Data)
> + + ((*Global)->VariableDigests - OldStart);
> + VarDig = VAR_DIG_PTR ((*Global)->VariableDigests);
> + while (VarDig != NULL) {
> + if (VarDig->Prev != 0) {
> + VarDig->Prev = GET_ADRS (Data) + (VarDig->Prev - OldStart);
> + }
> +
> + if (VarDig->Next != 0) {
> + VarDig->Next = GET_ADRS (Data) + (VarDig->Next - OldStart);
> + }
> +
> + VarDig = VAR_DIG_NEXT (VarDig);
> + }
> +
> + (*Global)->GlobalSelf = (EFI_PHYSICAL_ADDRESS)(UINTN)(*Global);
> + }
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Derive HMAC key from given variable root key.
> +
> + @param[in] RootKey Pointer to root key to derive from.
> + @param[in] RootKeySize Size of root key.
> + @param[out] HmacKey Pointer to generated HMAC key.
> + @param[in] HmacKeySize Size of HMAC key.
> +
> + @retval TRUE The HMAC key is derived successfully.
> + @retval FALSE Failed to generate HMAC key from given root key.
> +
> +**/
> +BOOLEAN
> +EFIAPI
> +GenerateMetaDataHmacKey (
> + IN CONST UINT8 *RootKey,
> + IN UINTN RootKeySize,
> + OUT UINT8 *HmacKey,
> + IN UINTN HmacKeySize
> + )
> +{
> + UINT8 Salt[AES_BLOCK_SIZE];
> +
> + return HkdfSha256ExtractAndExpand (
> + RootKey,
> + RootKeySize,
> + Salt,
> + 0,
> + (UINT8 *)METADATA_HMAC_KEY_NAME,
> + METADATA_HMAC_KEY_NAME_SIZE,
> + HmacKey,
> + HmacKeySize
> + );
> +}
> +
> +/**
> +
> + Return the size of variable MetaDataHmacVar.
> +
> + @param[in] AuthFlag Auth-variable indicator.
> +
> + @retval size of variable MetaDataHmacVar.
> +
> +**/
> +UINTN
> +GetMetaDataHmacVarSize (
> + IN BOOLEAN AuthFlag
> + )
> +{
> + UINTN Size;
> +
> + if (AuthFlag) {
> + Size = sizeof (AUTHENTICATED_VARIABLE_HEADER);
> + } else {
> + Size = sizeof (VARIABLE_HEADER);
> + }
> +
> + Size += METADATA_HMAC_VARIABLE_NAME_SIZE;
> + Size += GET_PAD_SIZE (Size);
> + Size += METADATA_HMAC_SIZE;
> + Size += GET_PAD_SIZE (Size);
> +
> + return Size;
> +}
> +
> +/**
> +
> + Digests the given variable data and updates HMAC context.
> +
> + @param[in] Context Pointer to initialized HMAC context.
> + @param[in] VarInfo Pointer to variable data.
> + @param[in] UpdateMethod Function to run when updating variable digest.
> +
> + @retval TRUE HMAC context was updated successfully.
> + @retval FALSE Failed to update HMAC context.
> +
> +**/
> +STATIC
> +BOOLEAN
> +UpdateVariableDigestData (
> + IN VOID *Context,
> + IN PROTECTED_VARIABLE_INFO *VarInfo,
> + IN DIGEST_UPDATE UpdateMethod
> + )
> +{
> + VOID *Buffer[12];
> + UINT32 BufferSize[12];
> + UINTN Index;
> + BOOLEAN Status;
> +
> + //
> + // Empty variable is legal here (e.g. variable deletion case or write-init case).
> + //
> + if ((VarInfo == NULL) ||
> + (VarInfo->CipherData == NULL) ||
> + (VarInfo->CipherDataSize == 0))
> + {
> + return TRUE;
> + }
> +
> + //
> + // HMAC (":" || VariableName)
> + //
> + Buffer[0] = METADATA_HMAC_SEP;
> + BufferSize[0] = METADATA_HMAC_SEP_SIZE;
> +
> + Buffer[1] = VarInfo->Header.VariableName;
> + BufferSize[1] = (UINT32)VarInfo->Header.NameSize;
> +
> + //
> + // HMAC (":" || VendorGuid || Attributes || DataSize)
> + //
> + Buffer[2] = METADATA_HMAC_SEP;
> + BufferSize[2] = METADATA_HMAC_SEP_SIZE;
> +
> + Buffer[3] = VarInfo->Header.VendorGuid;
> + BufferSize[3] = sizeof (EFI_GUID);
> +
> + Buffer[4] = &VarInfo->Header.Attributes;
> + BufferSize[4] = sizeof (VarInfo->Header.Attributes);
> +
> + Buffer[5] = &VarInfo->CipherDataSize;
> + BufferSize[5] = sizeof (VarInfo->CipherDataSize);
> +
> + //
> + // HMAC (":" || CipherData)
> + //
> + Buffer[6] = METADATA_HMAC_SEP;
> + BufferSize[6] = METADATA_HMAC_SEP_SIZE;
> +
> + Buffer[7] = VarInfo->CipherData;
> + BufferSize[7] = VarInfo->CipherDataSize;
> +
> + //
> + // HMAC (":" || PubKeyIndex || AuthMonotonicCount || TimeStamp)
> + //
> + Buffer[8] = METADATA_HMAC_SEP;
> + BufferSize[8] = METADATA_HMAC_SEP_SIZE;
> +
> + Buffer[9] = &VarInfo->Header.PubKeyIndex;
> + BufferSize[9] = sizeof (VarInfo->Header.PubKeyIndex);
> +
> + Buffer[10] = &VarInfo->Header.MonotonicCount;
> + BufferSize[10] = sizeof (VarInfo->Header.MonotonicCount);
> +
> + Buffer[11] = (VarInfo->Header.TimeStamp != NULL) ?
> + VarInfo->Header.TimeStamp : &mDefaultTimeStamp;
> + BufferSize[11] = sizeof (EFI_TIME);
> +
> + for (Index = 0; Index < ARRAY_SIZE (Buffer); ++Index) {
> + Status = UpdateMethod (Context, Buffer[Index], BufferSize[Index]);
> + if (!Status) {
> + ASSERT (FALSE);
> + return FALSE;
> + }
> + }
> +
> + return TRUE;
> +}
> +
> +/**
> +
> + Digests the given variable data and updates HMAC context.
> +
> + @param[in] Context Pointer to initialized HMAC context.
> + @param[in] VarInfo Pointer to variable data.
> +
> + @retval TRUE HMAC context was updated successfully.
> + @retval FALSE Failed to update HMAC context.
> +
> +**/
> +BOOLEAN
> +UpdateVariableMetadataHmac (
> + IN VOID *Context,
> + IN PROTECTED_VARIABLE_INFO *VarInfo
> + )
> +{
> + return UpdateVariableDigestData (Context, VarInfo,
> (DIGEST_UPDATE)HmacSha256Update);
> +}
> +
> +/**
> +
> + Get the variable digest.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to verified copy of protected variables.
> + @param[in,out] DigestValue Pointer to variable digest value.
> +
> + @retval EFI_INVALID_PARAMETER Invalid parameter was passed in.
> + @retval EFI_OUT_OF_RESOURCES Not enough resource to calculate hash.
> + @retval EFI_ABORTED An error was encountered.
> + @retval EFI_COMPROMISED_DATA The data was compromised.
> + @retval EFI_SUCCESS Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +GetVariableDigest (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo,
> + IN OUT UINT8 *DigestValue
> + )
> +{
> + EFI_STATUS Status;
> + VOID *Context;
> +
> + if ((Global == NULL) || (VarInfo == NULL) || (DigestValue == NULL)) {
> + ASSERT (Global != NULL);
> + ASSERT (VarInfo != NULL);
> + ASSERT (DigestValue != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Context = GET_BUFR (Global->DigestContext);
> + if (!HashApiInit (Context)) {
> + ASSERT (Context != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + if (VarInfo->CipherData == NULL) {
> + VarInfo->CipherData = VarInfo->Header.Data;
> + VarInfo->CipherDataSize = (UINT32)VarInfo->Header.DataSize;
> + }
> +
> + if ( !UpdateVariableDigestData (Context, VarInfo,
> (DIGEST_UPDATE)HashApiUpdate)
> + || !HashApiFinal (Context, DigestValue))
> + {
> + ASSERT (FALSE);
> + Status = EFI_ABORTED;
> + } else {
> + Status = EFI_SUCCESS;
> + }
> +
> + return Status;
> +}
> +
> +/**
> +
> + Verify the variable digest.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to verified copy of protected variables.
> + @param[in] VarDig Pointer to variable digest data.
> +
> + @retval EFI_INVALID_PARAMETER Invalid parameter was passed in.
> + @retval EFI_OUT_OF_RESOURCES Not enough resource to calculate hash.
> + @retval EFI_ABORTED An error was encountered.
> + @retval EFI_COMPROMISED_DATA The data was compromised.
> + @retval EFI_SUCCESS Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +VerifyVariableDigest (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo,
> + IN VARIABLE_DIGEST *VarDig
> + )
> +{
> + EFI_STATUS Status;
> + UINT8 NewDigest[METADATA_HMAC_SIZE];
> +
> + if (Global->Flags.RecoveryMode || !VarDig->Flags.Protected) {
> + return EFI_SUCCESS;
> + }
> +
> + ASSERT (VarDig->DigestSize == sizeof (NewDigest));
> +
> + Status = GetVariableDigest (Global, VarInfo, NewDigest);
> + if (!EFI_ERROR (Status)) {
> + if (CompareMem (VAR_DIG_VALUE (VarDig), NewDigest, VarDig-
> >DigestSize) != 0) {
> + Status = EFI_COMPROMISED_DATA;
> + }
> + }
> +
> + return Status;
> +}
> +
> +/**
> + Initialize variable MetaDataHmacVar.
> +
> + @param[in,out] Variable Pointer to buffer of MetaDataHmacVar.
> + @param[in] AuthFlag Variable format flag.
> +
> +**/
> +VOID
> +InitMetadataHmacVariable (
> + IN OUT VARIABLE_HEADER *Variable,
> + IN BOOLEAN AuthFlag
> + )
> +{
> + UINT8 *NamePtr;
> + AUTHENTICATED_VARIABLE_HEADER *AuthVariable;
> +
> + Variable->StartId = VARIABLE_DATA;
> + Variable->State = VAR_ADDED;
> + Variable->Reserved = 0;
> + Variable->Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT;
> +
> + if (AuthFlag) {
> + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable;
> +
> + AuthVariable->NameSize = METADATA_HMAC_VARIABLE_NAME_SIZE;
> + AuthVariable->DataSize = METADATA_HMAC_SIZE;
> + AuthVariable->PubKeyIndex = 0;
> + AuthVariable->MonotonicCount = 0;
> +
> + ZeroMem (&AuthVariable->TimeStamp, sizeof (EFI_TIME));
> + CopyMem (&AuthVariable->VendorGuid,
> &METADATA_HMAC_VARIABLE_GUID, sizeof (EFI_GUID));
> +
> + NamePtr = (UINT8 *)AuthVariable + sizeof
> (AUTHENTICATED_VARIABLE_HEADER);
> + } else {
> + Variable->NameSize = METADATA_HMAC_VARIABLE_NAME_SIZE;
> + Variable->DataSize = METADATA_HMAC_SIZE;
> +
> + CopyMem (&Variable->VendorGuid, &METADATA_HMAC_VARIABLE_GUID,
> sizeof (EFI_GUID));
> +
> + NamePtr = (UINT8 *)Variable + sizeof (VARIABLE_HEADER);
> + }
> +
> + CopyMem (NamePtr, METADATA_HMAC_VARIABLE_NAME,
> METADATA_HMAC_VARIABLE_NAME_SIZE);
> +}
> +
> +/**
> + Re-calculate HMAC based on new variable data and re-generate
> MetaDataHmacVar.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] NewVarInfo Pointer to buffer of new variable data.
> + @param[in,out] NewHmacVarInfo Pointer to buffer of new
> MetaDataHmacVar.
> +
> + @return EFI_SUCCESS The HMAC value was updated successfully.
> + @return EFI_ABORTED Failed to calculate the HMAC value.
> + @return EFI_OUT_OF_RESOURCES Not enough resource to calculate HMC
> value.
> + @return EFI_NOT_FOUND The MetaDataHmacVar was not found in
> storage.
> +
> +**/
> +EFI_STATUS
> +RefreshVariableMetadataHmac (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *NewVarInfo,
> + IN OUT PROTECTED_VARIABLE_INFO *NewHmacVarInfo
> + )
> +{
> + EFI_STATUS Status;
> + VOID *Context;
> + UINT32 Counter;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + PROTECTED_VARIABLE_INFO CurrHmacVarInfo;
> + UINT8 *HmacValue;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + VARIABLE_DIGEST *VarDig;
> + VARIABLE_DIGEST *HmacVarDig;
> +
> + ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> + ZeroMem ((VOID *)&CurrHmacVarInfo, sizeof (CurrHmacVarInfo));
> +
> + Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + Counter += 1;
> + ContextIn = GET_CNTX (Global);
> +
> + //
> + // Delete current MetaDataHmacVariable first, if any.
> + //
> + if (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID) {
> + HmacVarDig = VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded]);
> +
> + CurrHmacVarInfo.Header.NameSize = HmacVarDig->NameSize;
> + CurrHmacVarInfo.Header.VariableName = VAR_DIG_NAME (HmacVarDig);
> + CurrHmacVarInfo.Header.VendorGuid = VAR_DIG_GUID (HmacVarDig);
> +
> + CurrHmacVarInfo.Buffer = VAR_HDR_PTR (HmacVarDig->CacheIndex);
> + CurrHmacVarInfo.StoreIndex = HmacVarDig->StoreIndex;
> + CurrHmacVarInfo.Flags.Auth = HmacVarDig->Flags.Auth;
> + //
> + // Force marking current MetaDataHmacVariable as
> VAR_IN_DELETED_TRANSITION.
> + //
> + CurrHmacVarInfo.Buffer->State &= VAR_IN_DELETED_TRANSITION;
> + HmacVarDig->State &= VAR_IN_DELETED_TRANSITION;
> + Status = ContextIn->UpdateVariableStore (
> + &CurrHmacVarInfo,
> + OFFSET_OF (VARIABLE_HEADER, State),
> + sizeof (CurrHmacVarInfo.Buffer->State),
> + &CurrHmacVarInfo.Buffer->State
> + );
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> + } else if (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID) {
> + HmacVarDig = VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel]);
> + } else {
> + //
> + // No MetaDataHmacVar. Allocate space to cache its value.
> + //
> + HmacVarDig = CreateVariableDigestNode (
> + METADATA_HMAC_VARIABLE_NAME,
> + &METADATA_HMAC_VARIABLE_GUID,
> + METADATA_HMAC_VARIABLE_NAME_SIZE,
> + METADATA_HMAC_SIZE,
> + Global->Flags.Auth,
> + Global
> + );
> + if (HmacVarDig == NULL) {
> + ASSERT (HmacVarDig != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + HmacVarDig->Flags.Protected = FALSE;
> + }
> +
> + if (HmacVarDig->CacheIndex == VAR_INDEX_INVALID) {
> + HmacVarDig->CacheIndex = (GET_ADRS (Global)) + (Global->StructSize -
> GetMetaDataHmacVarSize (Global->Flags.Auth));
> + }
> +
> + //
> + // Construct new MetaDataHmacVar.
> + //
> + if (NewHmacVarInfo == NULL) {
> + NewHmacVarInfo = &VarInfo;
> + NewHmacVarInfo->Buffer = GET_BUFR (HmacVarDig->CacheIndex);
> + }
> +
> + InitMetadataHmacVariable (NewHmacVarInfo->Buffer, Global->Flags.Auth);
> +
> + NewHmacVarInfo->StoreIndex = VAR_INDEX_INVALID; // Skip calculating
> offset
> + NewHmacVarInfo->Flags.Auth = Global->Flags.Auth;
> + Status = ContextIn->GetVariableInfo (NewHmacVarInfo);
> + ASSERT_EFI_ERROR (Status);
> + HmacValue = NewHmacVarInfo->Header.Data;
> +
> + //
> + // Re-calculate HMAC for all valid variables
> + //
> + Context = HmacSha256New ();
> + if (Context == NULL) {
> + ASSERT (Context != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + Status = EFI_ABORTED;
> + if (!HmacSha256SetKey (
> + Context,
> + Global->MetaDataHmacKey,
> + sizeof (Global->MetaDataHmacKey)
> + ))
> + {
> + ASSERT (FALSE);
> + goto Done;
> + }
> +
> + //
> + // HMAC (|| hash(Var1) || hash(Var2) || ... || hash(VarN))
> + //
> + VarDig = VAR_DIG_PTR (Global->VariableDigests);
> + while (VarDig != NULL) {
> + if (VarDig->Flags.Valid && VarDig->Flags.Protected) {
> + HmacSha256Update (Context, VAR_DIG_VALUE (VarDig), VarDig-
> >DigestSize);
> + }
> +
> + VarDig = VAR_DIG_NEXT (VarDig);
> + }
> +
> + //
> + // HMAC (RpmcMonotonicCounter)
> + //
> + if (!HmacSha256Update (Context, &Counter, sizeof (Counter))) {
> + ASSERT (FALSE);
> + goto Done;
> + }
> +
> + if (!HmacSha256Final (Context, HmacValue)) {
> + ASSERT (FALSE);
> + goto Done;
> + }
> +
> + //
> + // Update HMAC value in cache.
> + //
> + CopyMem (VAR_DIG_VALUE (HmacVarDig), HmacValue, HmacVarDig-
> >DataSize);
> + if ((HmacVarDig->Prev == 0) && (HmacVarDig->Next == 0)) {
> + InsertVariableDigestNode (Global, HmacVarDig, NULL);
> + }
> +
> + //
> + // Just one MetaDataHmacVar is needed for normal operation.
> + //
> + Global->Unprotected[IndexHmacAdded] = VAR_DIG_ADR (HmacVarDig);
> + Global->Unprotected[IndexHmacInDel] = VAR_INDEX_INVALID;
> +
> + Status = EFI_SUCCESS;
> +
> +Done:
> + if (Context != NULL) {
> + HmacSha256Free (Context);
> + }
> +
> + return Status;
> +}
> +
> +/**
> +
> + Check if a given variable is unprotected variable specified in advance
> + and return its index ID.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to variable information data.
> +
> + @retval IndexHmacInDel Variable is MetaDataHmacVar in delete-transition
> state.
> + @retval IndexHmacAdded Variable is MetaDataHmacVar in valid state.
> + @retval IndexErrorFlag Variable is VarErrorLog.
> + @retval Others Variable is not any known unprotected variables.
> +
> +**/
> +UNPROTECTED_VARIABLE_INDEX
> +CheckKnownUnprotectedVariable (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo
> + )
> +{
> + UNPROTECTED_VARIABLE_INDEX Index;
> +
> + if ((VarInfo == NULL) || ( (VarInfo->StoreIndex == VAR_INDEX_INVALID)
> + && ( (VarInfo->Header.VariableName == NULL)
> + || (VarInfo->Header.VendorGuid == NULL))))
> + {
> + ASSERT (VarInfo != NULL);
> + ASSERT (VarInfo->StoreIndex != VAR_INDEX_INVALID);
> + ASSERT (VarInfo->Header.VariableName != NULL);
> + ASSERT (VarInfo->Header.VendorGuid != NULL);
> + return UnprotectedVarIndexMax;
> + }
> +
> + for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
> + if ( (Global->Unprotected[Index] != VAR_INDEX_INVALID)
> + && (VarInfo->StoreIndex != VAR_INDEX_INVALID))
> + {
> + if (VarInfo->StoreIndex == VAR_DIG_PTR (Global->Unprotected[Index])-
> >StoreIndex) {
> + break;
> + }
> + } else if (IS_VARIABLE (
> + &VarInfo->Header,
> + mUnprotectedVariables[Index].VariableName,
> + mUnprotectedVariables[Index].VendorGuid
> + ) && (VarInfo->Header.State == mUnprotectedVariables[Index].State))
> + {
> + break;
> + }
> + }
> +
> + return Index;
> +}
> +
> +/**
> +
> + Compare variable name and Guid
> +
> + @param[in] Name1 Name of first variable.
> + @param[in] Name1Size Size of first variable.
> + @param[in] Name2 Name of second variable.
> + @param[in] Name2Size Size of second variable.
> + @param[in] Guid1 Guid for first variable.
> + @param[in] Guid2 Guid for second variable.
> +
> + @retval 0 First name is identical to Second name.
> + @return others First name is not identical to Second name.
> +
> +**/
> +INTN
> +CompareVariableNameAndGuid (
> + IN CONST CHAR16 *Name1,
> + IN UINTN Name1Size,
> + IN CONST CHAR16 *Name2,
> + IN UINTN Name2Size,
> + IN EFI_GUID *Guid1,
> + IN EFI_GUID *Guid2
> + )
> +{
> + INTN Result;
> +
> + Result = StrnCmp (
> + Name1,
> + Name2,
> + MIN (Name1Size, Name2Size) / sizeof (CHAR16)
> + );
> + if (Result == 0) {
> + if (Name1Size != Name2Size) {
> + //
> + // Longer name is 'bigger' than shorter one.
> + //
> + Result = (INTN)Name1Size - (INTN)Name2Size;
> + } else {
> + //
> + // The variable name is the same. Compare the GUID.
> + //
> + Result = CompareMem ((VOID *)Guid1, (VOID *)Guid2, sizeof (EFI_GUID));
> + }
> + }
> +
> + return Result;
> +}
> +
> +/**
> +
> + Compare variable digest.
> +
> + @param[in] Variable1 Pointer to first variable digest.
> + @param[in] Variable2 Pointer to second variable digest.
> +
> + @retval 0 Variables are identical.
> + @return others Variables are not identical.
> +
> +**/
> +INTN
> +CompareVariableDigestInfo (
> + IN VARIABLE_DIGEST *Variable1,
> + IN VARIABLE_DIGEST *Variable2
> + )
> +{
> + return CompareVariableNameAndGuid (
> + VAR_DIG_NAME (Variable1),
> + Variable1->NameSize,
> + VAR_DIG_NAME (Variable2),
> + Variable2->NameSize,
> + &Variable1->VendorGuid,
> + &Variable2->VendorGuid
> + );
> +}
> +
> +/**
> +
> + Move a node backward in the order controlled by SortMethod.
> +
> + @param[in,out] Node Pointer to node to be moved.
> + @param[in] SortMethod Method used to compare node in list.
> +
> +**/
> +VOID
> +MoveNodeBackward (
> + IN OUT VARIABLE_DIGEST *Node,
> + IN SORT_METHOD SortMethod
> + )
> +{
> + VARIABLE_DIGEST *Curr;
> + VARIABLE_DIGEST *Prev;
> + INTN Result;
> +
> + Curr = Node;
> + while (Curr != NULL) {
> + Prev = VAR_DIG_PREV (Curr);
> + if (Prev == NULL) {
> + Result = -1;
> + } else {
> + Result = SortMethod (Prev, Node);
> + }
> +
> + //
> + // 'Result > 0' means the 'Prev' is 'bigger' than 'Node'. Continue to check
> + // previous node til a node 'smaller' than 'Node' found.
> + //
> + if (Result > 0) {
> + Curr = Prev;
> + continue;
> + }
> +
> + if (Curr != Node) {
> + //
> + // Remove Node first
> + //
> + if (VAR_DIG_PREV (Node) != NULL) {
> + VAR_DIG_PREV (Node)->Next = Node->Next;
> + }
> +
> + if (VAR_DIG_NEXT (Node) != NULL) {
> + VAR_DIG_NEXT (Node)->Prev = Node->Prev;
> + }
> +
> + //
> + // Insert Node before Curr.
> + //
> + Node->Prev = Curr->Prev;
> + Node->Next = VAR_DIG_ADR (Curr);
> +
> + if (Curr->Prev != 0) {
> + VAR_DIG_PREV (Curr)->Next = VAR_DIG_ADR (Node);
> + }
> +
> + Curr->Prev = VAR_DIG_ADR (Node);
> + }
> +
> + //
> + // If there're two identical variables in storage, one of them must be
> + // "in-delete-transition" state. Mark it as "deleted" anyway.
> + //
> + if (Result == 0) {
> + if (Curr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
> + Curr->State &= VAR_DELETED;
> + }
> +
> + if (Prev->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
> + Prev->State &= VAR_DELETED;
> + }
> + }
> +
> + break;
> + }
> +}
> +
> +/**
> +
> + Create variable digest node.
> +
> + @param[in] VariableName Name of variable.
> + @param[in] VendorGuid Guid of variable.
> + @param[in] NameSize Size of variable name.
> + @param[in] DataSize Size of variable data.
> + @param[in] AuthVar Authenticated variable flag.
> + @param[in] Global Pointer to global configuration data.
> +
> + @retval Ptr Pointer to variable digest
> +
> +**/
> +VARIABLE_DIGEST *
> +CreateVariableDigestNode (
> + IN CHAR16 *VariableName,
> + IN EFI_GUID *VendorGuid,
> + IN UINT16 NameSize,
> + IN UINT32 DataSize,
> + IN BOOLEAN AuthVar,
> + IN PROTECTED_VARIABLE_GLOBAL *Global
> + )
> +{
> + VARIABLE_DIGEST *VarDig;
> + VOID *Buffer;
> + UINTN VarSize;
> +
> + VarDig = (VARIABLE_DIGEST *)AllocateZeroPool (
> + sizeof (VARIABLE_DIGEST) + NameSize +
> METADATA_HMAC_SIZE
> + );
> + if ((VarDig == NULL) || (Global == NULL)) {
> + ASSERT (VarDig != NULL);
> + ASSERT (Global != NULL);
> + return NULL;
> + }
> +
> + VarDig->DataSize = DataSize;
> + VarDig->NameSize = NameSize;
> + VarDig->DigestSize = METADATA_HMAC_SIZE;
> + VarDig->State = VAR_ADDED;
> + VarDig->Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT;
> + VarDig->Flags.Auth = AuthVar;
> + VarDig->Flags.Valid = TRUE;
> + VarDig->Flags.Freeable = TRUE;
> + VarDig->Flags.Protected = PcdGetBool (PcdProtectedVariableIntegrity);
> + VarDig->Flags.Encrypted = PcdGetBool (PcdProtectedVariableConfidentiality);
> + VarDig->StoreIndex = VAR_INDEX_INVALID;
> + VarDig->CacheIndex = VAR_INDEX_INVALID;
> +
> + if (Global->Flags.CacheReady == TRUE) {
> + VarSize = VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
> + VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> + VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> +
> + Buffer = AllocateZeroPool (VarSize);
> + if (Buffer != NULL) {
> + VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> + }
> + }
> +
> + CopyMem (VAR_DIG_NAME (VarDig), VariableName, NameSize);
> + CopyMem (VAR_DIG_GUID (VarDig), VendorGuid, sizeof (EFI_GUID));
> +
> + return VarDig;
> +}
> +
> +/**
> +
> + Remove variable digest node.
> +
> + @param[in,out] Global Pointer to global configuration data.
> + @param[in,out] VarDig Pointer to variable digest value.
> + @param[in] FreeResource Flag to indicate whether to free resource.
> +
> +**/
> +VOID
> +RemoveVariableDigestNode (
> + IN OUT PROTECTED_VARIABLE_GLOBAL *Global,
> + IN OUT VARIABLE_DIGEST *VarDig,
> + IN BOOLEAN FreeResource
> + )
> +{
> + VARIABLE_DIGEST *Prev;
> + VARIABLE_DIGEST *Next;
> +
> + Prev = VAR_DIG_PREV (VarDig);
> + Next = VAR_DIG_NEXT (VarDig);
> +
> + if (Global->VariableDigests == VAR_DIG_ADR (VarDig)) {
> + Global->VariableDigests = VAR_DIG_ADR (Next);
> + }
> +
> + if (Prev != NULL) {
> + Prev->Next = VAR_DIG_ADR (Next);
> + }
> +
> + if (Next != NULL) {
> + Next->Prev = VAR_DIG_ADR (Prev);
> + }
> +
> + VarDig->Prev = 0;
> + VarDig->Next = 0;
> + VarDig->Flags.Valid = FALSE;
> +
> + if (FreeResource && VarDig->Flags.Freeable) {
> + if ((VarDig->CacheIndex != 0) && (VarDig->CacheIndex !=
> VAR_INDEX_INVALID)) {
> + VarDig->CacheIndex = VAR_INDEX_INVALID;
> + }
> + }
> +}
> +
> +/**
> +
> + Insert variable digest node.
> +
> + @param[in,out] Global Pointer to global configuration data.
> + @param[in] VarDig Pointer to variable digest value.
> + @param[in] SortMethod Method for sorting.
> +
> +**/
> +VOID
> +InsertVariableDigestNode (
> + IN OUT PROTECTED_VARIABLE_GLOBAL *Global,
> + IN VARIABLE_DIGEST *VarDig,
> + IN SORT_METHOD SortMethod
> + )
> +{
> + VARIABLE_DIGEST *Curr;
> + VARIABLE_DIGEST *Prev;
> + BOOLEAN DoReplace;
> + INTN Result;
> +
> + if (SortMethod == NULL) {
> + SortMethod = CompareVariableDigestInfo;
> + }
> +
> + DoReplace = FALSE;
> + Curr = VAR_DIG_PTR (Global->VariableDigests);
> + if (Curr == NULL) {
> + //
> + // First one.
> + //
> + VarDig->Prev = 0;
> + VarDig->Next = 0;
> + Global->VariableDigests = VAR_DIG_ADR (VarDig);
> + return;
> + }
> +
> + while (Curr != NULL && Curr != VarDig) {
> + Result = SortMethod (VarDig, Curr);
> +
> + if (Result <= 0) {
> + ASSERT (VarDig->StoreIndex != Curr->StoreIndex);
> +
> + //
> + // The same variable already in list?
> + //
> + if (Result == 0) {
> + //
> + // Keep only the same new one, unless states are different. In such
> + // situation, the one with no VAR_ADDED will be deleted.
> + //
> + if (VarDig->State >= Curr->State) {
> + DoReplace = TRUE;
> + Curr->Flags.Valid = FALSE; // to-be-deleted
> + } else {
> + DoReplace = FALSE;
> + VarDig->Flags.Valid = FALSE; // to-be-deleted
> + }
> + }
> +
> + //
> + // Put VarDig before Curr
> + //
> + VarDig->Next = VAR_DIG_ADR (Curr);
> + VarDig->Prev = Curr->Prev;
> +
> + if (VAR_DIG_PREV (Curr) != NULL) {
> + VAR_DIG_PREV (Curr)->Next = VAR_DIG_ADR (VarDig);
> + }
> +
> + Curr->Prev = VAR_DIG_ADR (VarDig);
> +
> + if (DoReplace) {
> + RemoveVariableDigestNode (Global, Curr, TRUE);
> + }
> +
> + break;
> + }
> +
> + Prev = Curr;
> + Curr = VAR_DIG_NEXT (Curr);
> + if (Curr == NULL) {
> + Prev->Next = VAR_DIG_ADR (VarDig);
> +
> + VarDig->Prev = VAR_DIG_ADR (Prev);
> + VarDig->Next = 0;
> + }
> + }
> +
> + //
> + // Update the head node if necessary.
> + //
> + if (VAR_DIG_PTR (VarDig->Prev) == NULL) {
> + Global->VariableDigests = VAR_DIG_ADR (VarDig);
> + }
> +}
> +
> +/**
> +
> + Find the specified variable digest
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to variable data.
> + @param[in] FindNext Flag to continue looking for variable.
> +
> +**/
> +VARIABLE_DIGEST *
> +FindVariableInternal (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_INFO *VarInfo,
> + IN BOOLEAN FindNext
> + )
> +{
> + VARIABLE_DIGEST *VarDig;
> + VARIABLE_DIGEST *Found;
> + VARIABLE_DIGEST *FirstStoreIndexVar;
> + BOOLEAN ByIndex;
> + INTN FwdOrBwd;
> +
> + //
> + // If VarInfo->StoreIndex is valid, use it to find the variable. Otherwise,
> + // use the variable name and guid instead, if given. If no clue at all, return
> + // the variable with lowest StoreIndex.
> + //
> + if ( (VarInfo->StoreIndex != VAR_INDEX_INVALID)
> + || (VarInfo->Header.VariableName == NULL)
> + || (VarInfo->Header.VendorGuid == NULL))
> + {
> + ByIndex = TRUE;
> + } else {
> + ByIndex = FALSE;
> + }
> +
> + Found = NULL;
> + VarDig = VAR_DIG_PTR (Global->VariableDigests);
> + FirstStoreIndexVar = VarDig;
> + FwdOrBwd = 1;
> +
> + //
> + // Discover variable with first/smallest store index
> + //
> + while (VarDig != NULL) {
> + if (VarDig->StoreIndex < FirstStoreIndexVar->StoreIndex) {
> + FirstStoreIndexVar = VAR_DIG_PTR (VarDig);
> + }
> +
> + VarDig = VAR_DIG_NEXT (VarDig);
> + }
> +
> + //
> + // Input variable is NULL than return first variable
> + // with smallest store index from the variable digest list.
> + //
> + if (((VarInfo->Header.VariableName == NULL) ||
> + (VarInfo->Header.VendorGuid == NULL)) &&
> + (ByIndex == FALSE))
> + {
> + return FirstStoreIndexVar;
> + }
> +
> + //
> + // Start with first entry
> + //
> + VarDig = VAR_DIG_PTR (Global->VariableDigests);
> + while (VarDig != NULL) {
> + if (ByIndex) {
> + if (FindNext) {
> + if (VarDig->StoreIndex == VarInfo->StoreIndex) {
> + Found = VarDig = VAR_DIG_NEXT (VarDig);
> + break;
> + }
> + } else if (VarDig->StoreIndex == VarInfo->StoreIndex) {
> + Found = VarDig;
> + break;
> + }
> + } else {
> + //
> + // Match given variable name and vendor guid.
> + //
> + if (IS_VARIABLE (&VarInfo->Header, VAR_DIG_NAME (VarDig),
> VAR_DIG_GUID (VarDig))) {
> + Found = (FindNext) ? VAR_DIG_NEXT (VarDig) : VarDig;
> + break;
> + }
> + }
> +
> + VarDig = (FwdOrBwd > 0) ? VAR_DIG_NEXT (VarDig) : VAR_DIG_PREV
> (VarDig);
> + if (VarDig == NULL) {
> + }
> + }
> +
> + return Found;
> +}
> +
> +/**
> +
> + Synchronize the RPMC counters
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] VarInfo Pointer to variable data.
> + @param[in] FindNext Flag to continue looking for variable.
> +
> + @retval EFI_SUCCESS Successfully sync RPMC counters.
> + @return others Failed to sync RPMC counters.
> +
> +**/
> +EFI_STATUS
> +SyncRpmcCounter (
> + VOID
> + )
> +{
> + UINT32 Counter1;
> + UINT32 Counter2;
> + EFI_STATUS Status;
> +
> + //
> + // Sync RPMC1 & RPMC2.
> + //
> + Status = RequestMonotonicCounter (RPMC_COUNTER_1, &Counter1);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter2);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + while (Counter1 < Counter2) {
> + Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + ++Counter1;
> + }
> +
> + while (Counter2 < Counter1) {
> + Status = IncrementMonotonicCounter (RPMC_COUNTER_2);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + ++Counter2;
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + An alternative version of ProtectedVariableLibGetData to get plain data from
> + given variable, if encrypted.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in,out] VarInfo Pointer to structure containing variable
> + information. VarInfo->Header.Data must point
> + to the original variable data.
> +
> + @retval EFI_SUCCESS Found the specified variable.
> + @retval EFI_INVALID_PARAMETER VarInfo is NULL or both VarInfo->Buffer
> and
> + VarInfo->Offset are invalid.
> + @retval EFI_NOT_FOUND The specified variable could not be found.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +ProtectedVariableLibGetDataInternal (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN OUT PROTECTED_VARIABLE_INFO *VarInfo
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + VOID *Buffer;
> + UINTN BufferSize;
> +
> + if ((Global == NULL) || (VarInfo == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + ContextIn = GET_CNTX (Global);
> +
> + //
> + // Check if the data has been decrypted or not.
> + //
> + BufferSize = VarInfo->PlainDataSize;
> + VarInfo->CipherData = NULL;
> + VarInfo->CipherDataSize = 0;
> + VarInfo->PlainData = NULL;
> + VarInfo->PlainDataSize = 0;
> + Status = GetCipherDataInfo (VarInfo);
> +
> + if ((Status == EFI_UNSUPPORTED) || (Status == EFI_NOT_FOUND)) {
> + VarInfo->Flags.DecryptInPlace = TRUE;
> + VarInfo->PlainDataSize = (UINT32)VarInfo->Header.DataSize;
> + VarInfo->PlainData = VarInfo->Header.Data;
> + VarInfo->CipherDataType = 0;
> + VarInfo->CipherHeaderSize = 0;
> + Status = EFI_SUCCESS;
> + } else if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + //
> + // Don't do decryption if the caller provided buffer is too small
> + // Simply return the real Plain Data Size via VarInfo->PlainDataSize
> + //
> + if (BufferSize < VarInfo->PlainDataSize) {
> + return EFI_BUFFER_TOO_SMALL;
> + }
> +
> + //
> + // If the variable data is cipher data, decrypt it inplace if possible.
> + //
> + if ((VarInfo->PlainData == NULL) && (VarInfo->CipherData != NULL)) {
> + VarInfo->Key = Global->RootKey;
> + VarInfo->KeySize = sizeof (Global->RootKey);
> +
> + switch (ContextIn->VariableServiceUser) {
> + case FromPeiModule:
> + VarInfo->Flags.DecryptInPlace = FALSE;
> + //
> + // In PEI VariableCache holds Cipher header + Cipher data
> + // Do not override Cipher header data during decrypt operation
> + //
> + VarInfo->PlainData = GET_BUFR (Global->VariableCache + VarInfo-
> >CipherHeaderSize);
> +
> + Status = DecryptVariable (VarInfo);
> + if (Status == EFI_UNSUPPORTED) {
> + VarInfo->PlainData = VarInfo->Header.Data;
> + VarInfo->PlainDataSize = (UINT32)VarInfo->Header.DataSize;
> + VarInfo->CipherDataType = 0;
> + VarInfo->CipherHeaderSize = 0;
> +
> + Status = EFI_SUCCESS;
> + }
> +
> + break;
> +
> + case FromSmmModule:
> + VarInfo->Flags.DecryptInPlace = FALSE;
> + VarInfo->PlainData = GET_BUFR (Global->VariableCache);
> +
> + Status = DecryptVariable (VarInfo);
> + if (Status == EFI_UNSUPPORTED) {
> + VarInfo->PlainData = VarInfo->Header.Data;
> + VarInfo->PlainDataSize = (UINT32)VarInfo->Header.DataSize;
> + VarInfo->CipherDataType = 0;
> + VarInfo->CipherHeaderSize = 0;
> +
> + Status = EFI_SUCCESS;
> + }
> +
> + break;
> +
> + case FromBootServiceModule:
> + case FromRuntimeModule:
> + //
> + // The SMM passes back only decrypted data. We re-use the original cipher
> + // data buffer to keep the plain data along with the cipher header.
> + //
> + VarInfo->Flags.DecryptInPlace = TRUE;
> + Buffer = (VOID *)((UINTN)VarInfo->CipherData + VarInfo-
> >CipherHeaderSize);
> + BufferSize = VarInfo->PlainDataSize;
> + Status = ContextIn->FindVariableSmm (
> + VarInfo->Header.VariableName,
> + VarInfo->Header.VendorGuid,
> + &VarInfo->Header.Attributes,
> + &BufferSize,
> + Buffer
> + );
> + if (!EFI_ERROR (Status)) {
> + //
> + // Flag the payload as plain data to avoid re-decrypting.
> + //
> + VarInfo->CipherDataType = ENC_TYPE_NULL;
> + VarInfo->PlainDataSize = (UINT32)BufferSize;
> + VarInfo->PlainData = Buffer;
> +
> + Status = SetCipherDataInfo (VarInfo);
> + if (Status == EFI_UNSUPPORTED) {
> + Status = EFI_SUCCESS;
> + }
> + }
> +
> + break;
> +
> + default:
> + Status = EFI_UNSUPPORTED;
> + break;
> + }
> +
> + VarInfo->CipherData = NULL;
> + VarInfo->CipherDataSize = 0;
> + }
> +
> + return Status;
> +}
> +
> +/**
> +
> + 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->Buffer
> 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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VOID **Buffer;
> + UINT32 BufferSize;
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + //
> + // Save the output data buffer because below call
> + // call will use this struct field internally.
[JianJW] "call" here is duplicated
> + //
> + Buffer = VarInfo->PlainData;
> + BufferSize = VarInfo->PlainDataSize;
> +
> + Status = ProtectedVariableLibGetDataInternal (Global, VarInfo);
> + if (EFI_ERROR (Status) || ((BufferSize) < VarInfo->PlainDataSize)) {
> + //
> + // Return with caller provided buffer with zero DataSize
> + //
> + VarInfo->PlainData = Buffer;
> + return Status;
> + }
> +
> + //
> + // Copy Plain data to ouput data buffer
[JianJW] typo: "ouput" -> "output", "Plain" -> "plain"
> + //
> + CopyMem (Buffer, VarInfo->PlainData, VarInfo->PlainDataSize);
> + VarInfo->PlainData = Buffer;
> +
> + return Status;
> +}
> +
> +/**
> +
> + 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[in,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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + AUTHENTICATED_VARIABLE_HEADER *AuthVariable;
> + VOID *Buffer;
> +
> + if ((Variable == NULL) || (DataSize == NULL)) {
> + ASSERT (Variable != NULL);
> + ASSERT (DataSize != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> +
> + VarInfo.Buffer = Variable;
> + VarInfo.Flags.Auth = AuthFlag;
> + VarInfo.PlainDataSize = *DataSize;
> +
> + if (VarInfo.Flags.Auth == TRUE) {
> + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable;
> +
> + VarInfo.Header.VariableName = (CHAR16 *)((UINTN)Variable + sizeof
> (AUTHENTICATED_VARIABLE_HEADER));
> + VarInfo.Header.NameSize = AuthVariable->NameSize;
> + VarInfo.Header.VendorGuid = &AuthVariable->VendorGuid;
> + VarInfo.Header.Attributes = AuthVariable->Attributes;
> + VarInfo.Header.DataSize = AuthVariable->DataSize;
> + } else {
> + VarInfo.Header.VariableName = (CHAR16 *)((UINTN)Variable + sizeof
> (VARIABLE_HEADER));
> + VarInfo.Header.NameSize = Variable->NameSize;
> + VarInfo.Header.VendorGuid = &Variable->VendorGuid;
> + VarInfo.Header.Attributes = Variable->Attributes;
> + VarInfo.Header.DataSize = Variable->DataSize;
> + }
> +
> + Buffer = VARIABLE_NAME (VarInfo.Buffer, VarInfo.Flags.Auth);
> + Buffer = GET_BUFR (GET_ADRS (Buffer) + VarInfo.Header.NameSize);
> + Buffer = GET_BUFR (GET_ADRS (Buffer) + GET_PAD_SIZE
> (VarInfo.Header.NameSize));
> + VarInfo.Header.Data = Buffer;
> +
> + Status = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
> + *DataSize = VarInfo.PlainDataSize;
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + CopyMem (Data, VarInfo.PlainData, VarInfo.PlainDataSize);
> +
> + return Status;
> +}
> +
> +/**
> + 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.
[JianJW] missing [in/out] for parameters
> +
> + @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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *VarDig;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + EFI_TIME TimeStamp;
> + VOID *DataBuffer;
> +
> + if ((VariableName == NULL) || (VariableGuid == NULL) || (DataSize == NULL)) {
> + ASSERT (VariableName != NULL);
> + ASSERT (VariableGuid != NULL);
> + ASSERT (DataSize != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Status = GetProtectedVariableGlobal (&Global);
> +
> + if (EFI_ERROR (Status)) {
> + return EFI_UNSUPPORTED;
> + }
> +
> + ContextIn = GET_CNTX (Global);
> +
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarInfo.Header.VariableName = (CHAR16 *)VariableName;
> + VarInfo.Header.NameSize = StrSize (VariableName);
> + VarInfo.Header.VendorGuid = (EFI_GUID *)VariableGuid;
> +
> + VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> + if (VarDig == NULL) {
> + return EFI_NOT_FOUND;
> + }
> +
> + if (Attributes != NULL) {
> + *Attributes = VarDig->Attributes;
> + }
> +
> + if ((Data == NULL) || (*DataSize < VarDig->PlainDataSize)) {
> + *DataSize = VarDig->PlainDataSize;
> + return EFI_BUFFER_TOO_SMALL;
> + }
> +
> + VarInfo.Flags.Auth = VarDig->Flags.Auth;
> + VarInfo.Flags.Protected = VarDig->Flags.Protected;
> +
> + //
> + // Verify digest before copy the data back, if the variable is not in cache.
> + //
> + if (VarDig->CacheIndex != VAR_INDEX_INVALID) {
> + VarInfo.Header.VariableName = NULL;
> + VarInfo.Header.VendorGuid = NULL;
> + VarInfo.Buffer = GET_BUFR (VarDig->CacheIndex);
> +
> + Status = ContextIn->GetVariableInfo (&VarInfo);
> + ASSERT_EFI_ERROR (Status);
> + } else {
> + //
> + // A buffer for at least one variable data (<=PcdMax(Auth)VariableSize)
> + // must be reserved in advance.
> + //
> + ASSERT (
> + Global->VariableCache != 0
> + && Global->VariableCacheSize >= VarDig->DataSize
> + );
> + DataBuffer = GET_BUFR (Global->VariableCache);
> + //
> + // Note name and GUID are already there.
> + //
> + VarInfo.StoreIndex = VarDig->StoreIndex;
> +
> + VarInfo.Header.VariableName = NULL; // Prevent name from being retrieved
> again.
> + VarInfo.Header.NameSize = 0;
> + VarInfo.Header.VendorGuid = NULL; // Prevent guid from being retrieved
> again.
> + VarInfo.Header.TimeStamp = &TimeStamp;
> + VarInfo.Header.Data = DataBuffer;
> + VarInfo.Header.DataSize = VarDig->DataSize;
> +
> + //
> + // Get detailed information about the variable.
> + //
> + Status = ContextIn->GetVariableInfo (&VarInfo);
> + ASSERT_EFI_ERROR (Status);
> +
> + //
> + // The variable must be validated its digest value to avoid TOCTOU, if it's
> + // not been cached yet.
> + //
> + VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> + VarInfo.Header.NameSize = VarDig->NameSize;
> + VarInfo.Header.VendorGuid = &VarDig->VendorGuid;
> + Status = VerifyVariableDigest (Global, &VarInfo, VarDig);
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> + }
> +
> + VarInfo.PlainDataSize = (UINT32)*DataSize;
> +
> + //
> + // Decrypt the data, if necessary.
> + //
> + Status = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
> + *DataSize = VarInfo.PlainDataSize;
> + if (EFI_ERROR (Status)) {
> + DEBUG ((
> + DEBUG_INFO,
> + "%a: %d Exit(). VariableName = %s, VariableGuid = 0x%g, DataSize = 0x%X,
> Data Buffer = 0x%lX, Status = %r\n",
> + __FUNCTION__,
> + __LINE__,
> + VariableName,
> + VariableGuid,
> + *DataSize,
> + Data,
> + Status
> + ));
> + return Status;
> + }
> +
> + CopyMem (Data, VarInfo.PlainData, VarInfo.PlainDataSize);
> +
> + return Status;
> +}
> +
> +/**
> +
> + This function is used to enumerate the variables managed by current
> + ProtectedVariableLib.
> +
> + If the VarInfo->StoreIndex is invalid (VAR_INDEX_INVALID), the first variable
> + with the smallest StoreIndex will be returned. Otherwise, the variable with
> + StoreIndex just after than the VarInfo->StoreIndex will be returned.
> +
> + @param[in,out] VarInfo Pointer to structure containing variable
> information.
> +
> + @retval EFI_SUCCESS Found the specified variable.
> + @retval EFI_INVALID_PARAMETER VarInfo is NULL.
> + @retval EFI_NOT_FOUND The specified variable could not be found.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +GetNextVariableInternal (
> + IN OUT PROTECTED_VARIABLE_INFO *VarInfo
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *Found;
> +
> + if (VarInfo == NULL) {
> + ASSERT (VarInfo != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + Found = FindVariableInternal (Global, VarInfo, TRUE);
> + if (Found == NULL) {
> + return EFI_NOT_FOUND;
> + }
> +
> + //
> + // Return all cached data.
> + //
> + VarInfo->Header.VariableName = VAR_DIG_NAME (Found);
> + VarInfo->Header.VendorGuid = VAR_DIG_GUID (Found);
> + VarInfo->Header.NameSize = Found->NameSize;
> + VarInfo->Header.DataSize = Found->DataSize;
> + VarInfo->Header.Attributes = Found->Attributes;
> +
> + VarInfo->PlainDataSize = Found->PlainDataSize;
> + VarInfo->StoreIndex = Found->StoreIndex;
> + if (Found->CacheIndex != VAR_INDEX_INVALID) {
> + VarInfo->Buffer = GET_BUFR (Found->CacheIndex);
> + }
> +
> + VarInfo->Flags.Auth = Found->Flags.Auth;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Find the request variable.
> +
> + @param[in, out] VarInfo Pointer to variable data.
> +
> + @retval EFI_SUCCESS The variable was read successfully.
> + @retval EFI_NOT_FOUND The variable could not be found.
> + @retval EFI_INVALID_PARAMETER Variable info is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibFind (
> + IN OUT PROTECTED_VARIABLE_INFO *VarInfo
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *Found;
> +
> + if (VarInfo == NULL) {
> + ASSERT (VarInfo != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + Found = FindVariableInternal (Global, VarInfo, FALSE);
> + if (Found == NULL) {
> + return EFI_NOT_FOUND;
> + }
> +
> + //
> + // Return all cached data.
> + //
> + VarInfo->Header.VariableName = VAR_DIG_NAME (Found);
> + VarInfo->Header.VendorGuid = VAR_DIG_GUID (Found);
> + VarInfo->Header.NameSize = Found->NameSize;
> + VarInfo->Header.DataSize = Found->DataSize;
> + VarInfo->Header.Attributes = Found->Attributes;
> +
> + VarInfo->PlainDataSize = Found->PlainDataSize;
> + VarInfo->StoreIndex = Found->StoreIndex;
> + if (Found->CacheIndex != VAR_INDEX_INVALID) {
> + VarInfo->Buffer = GET_BUFR (Found->CacheIndex);
> + }
> +
> + VarInfo->Flags.Auth = Found->Flags.Auth;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + 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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *VarDig;
> + UINTN Size;
> +
> + if ((VariableNameSize == NULL) || (VariableName == NULL) || (VariableGuid
> == NULL)) {
> + ASSERT (VariableNameSize != NULL);
> + ASSERT (VariableName != NULL);
> + ASSERT (VariableGuid != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + SetMem (&VarInfo, sizeof (VarInfo), 0);
> + Size = StrSize (VariableName);
> +
> + if (Size <= 2) {
> + VarDig = VAR_DIG_PTR (Global->VariableDigests);
> + } else {
> + VarInfo.Header.VariableName = VariableName;
> + VarInfo.Header.NameSize = Size;
> + VarInfo.Header.VendorGuid = VariableGuid;
> +
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +
> + VarDig = FindVariableInternal (Global, &VarInfo, TRUE);
> + }
> +
> + if (VarDig == NULL) {
> + return EFI_NOT_FOUND;
> + }
> +
> + if (VarDig->NameSize > *VariableNameSize) {
> + return EFI_BUFFER_TOO_SMALL;
> + }
> +
> + CopyMem (VariableName, VAR_DIG_NAME (VarDig), VarDig->NameSize);
> + CopyGuid (VariableGuid, &VarDig->VendorGuid);
> + *VariableNameSize = VarInfo.Header.NameSize;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Return the next variable name and GUID.
> +
> + @param[in, out] VarInfo Pointer to variable data.
> +
> + @retval EFI_SUCCESS The variable was read successfully.
> + @retval EFI_INVALID_PARAMETER VarInfo is NULL.
> + @retval EFI_NOT_FOUND The specified variable could not be found.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibFindNextEx (
> + IN OUT PROTECTED_VARIABLE_INFO *VarInfo
> + )
> +{
> + return GetNextVariableInternal (VarInfo);
> +}
> +
> +/**
> +
> + Return the max count of a variable.
> +
> + @return max count of a variable.
> +
> +**/
> +UINTN
> +ProtectedVariableLibGetMaxVariablesCount (
> + VOID
> + )
> +{
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + VARIABLE_DIGEST *VarDig;
> + EFI_STATUS Status;
> + UINTN Count;
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return 0;
> + }
> +
> + Count = 0;
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> +
> + //
> + // Start with first entry
> + //
> + VarDig = VAR_DIG_PTR (Global->VariableDigests);
> + VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> + VarInfo.Header.VendorGuid = VAR_DIG_GUID (VarDig);
> + VarInfo.StoreIndex = VarDig->StoreIndex;
> +
> + do {
> + VarInfo.Buffer = NULL;
> + Status = ProtectedVariableLibFindNextEx (&VarInfo);
> + if (EFI_ERROR (Status)) {
> + return Count;
> + }
> +
> + Count++;
> + } while (TRUE);
> +}
> +
> +/**
> + The function is called by PerformQuickSort to sort.
> +
> + @param[in] Left The pointer to first buffer.
> + @param[in] Right The pointer to second buffer.
> +
> + @retval 0 Buffer1 equal to Buffer2.
> + @return < 0 Buffer1 is less than Buffer2.
> + @return > 0 Buffer1 is greater than Buffer2.
> +
> +**/
> +INTN
> +EFIAPI
> +CompareStoreIndex (
> + IN CONST VOID *Left,
> + IN CONST VOID *Right
> + )
> +{
> + EFI_PHYSICAL_ADDRESS StoreIndex1;
> + EFI_PHYSICAL_ADDRESS StoreIndex2;
> +
> + StoreIndex1 = (*(EFI_PHYSICAL_ADDRESS *)Left);
> + StoreIndex2 = (*(EFI_PHYSICAL_ADDRESS *)Right);
> +
> + if (StoreIndex1 == StoreIndex2) {
> + return (0);
> + }
> +
> + if (StoreIndex1 < StoreIndex2) {
> + return (-1);
> + }
> +
> + return (1);
> +}
> +
> +/**
> + 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
> + )
> +{
> + EFI_STATUS Status;
> + UINTN Count;
> + UINTN StoreIndexTableSize;
> + EFI_PHYSICAL_ADDRESS *StoreIndexTable;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *VarDig;
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + Count = 0;
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> + StoreIndexTableSize = ProtectedVariableLibGetMaxVariablesCount ();
> + StoreIndexTable = AllocateZeroPool (sizeof (EFI_PHYSICAL_ADDRESS) *
> StoreIndexTableSize);
> +
> + //
> + // Start with first entry
> + //
> + VarDig = VAR_DIG_PTR (Global->VariableDigests);
> + VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> + VarInfo.Header.VendorGuid = VAR_DIG_GUID (VarDig);
> + VarInfo.StoreIndex = VarDig->StoreIndex;
> + StoreIndexTable[Count] = VarInfo.StoreIndex;
> + Count++;
> +
> + //
> + // Populate the un-sorted table
> + //
> + do {
> + VarInfo.Buffer = NULL;
> + Status = ProtectedVariableLibFindNextEx (&VarInfo);
> + if (EFI_ERROR (Status)) {
> + break;
> + }
> +
> + StoreIndexTable[Count] = VarInfo.StoreIndex;
> + Count++;
> + } while (TRUE);
> +
> + PerformQuickSort (
> + StoreIndexTable,
> + Count,
> + sizeof (EFI_PHYSICAL_ADDRESS),
> + (SORT_COMPARE)CompareStoreIndex
> + );
> +
> + *Buffer = StoreIndexTable;
> + *NumElements = Count;
> +
> + return EFI_SUCCESS;
> +}
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
> new file mode 100644
> index 000000000000..94df21eacf25
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
> @@ -0,0 +1,163 @@
> +/** @file
> + Implemention of ProtectedVariableLib for SMM variable services.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Library/UefiBootServicesTableLib.h"
> +#include "Library/MemoryAllocationLib.h"
> +
> +#include "ProtectedVariableInternal.h"
> +
> +PROTECTED_VARIABLE_CONTEXT_IN mVariableContextIn = {
> + PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
> + sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
> + 0,
> + FromSmmModule,
> + NULL,
> + NULL,
> + NULL,
> + NULL,
> + NULL
> +};
> +
> +PROTECTED_VARIABLE_GLOBAL mProtectedVariableGlobal = {
> + PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
> + sizeof (PROTECTED_VARIABLE_GLOBAL),
> + { 0 },
> + { 0 },
> + 0,
> + 0,
> + 0,
> + 0,
> + 0,
> + 0,
> + { 0, 0, 0 },
> + 0,
> + 0,
> + { 0, 0, 0, 0, 0, 0}
> +};
> +
> +/**
> + Fix incorrect state of MetaDataHmacVariable before any variable update.
> +
> + @param[in] Event The event that occurred
> + @param[in] Context For EFI compatibility. Not used.
> +
> +**/
> +VOID
> +EFIAPI
> +VariableWriteProtocolCallback (
> + IN EFI_EVENT Event,
> + IN VOID *Context
> + )
> +{
> + EFI_STATUS Status;
> +
> + //
> + // Fix incorrect state of MetaDataHmacVariable before any variable update.
> + // This has to be done here due to the fact that this operation needs to
> + // update NV storage but the FVB and FTW protocol might not be ready during
> + // ProtectedVariableLibInitialize().
> + //
> + Status = FixupHmacVariable ();
> + ASSERT_EFI_ERROR (Status);
> +
> + Status = ProtectedVariableLibWriteInit ();
> + ASSERT_EFI_ERROR (Status);
> +
> + gBS->CloseEvent (Event);
> +}
> +
> +/**
> +
> + 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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_CONTEXT_IN *ProtectedVarContext;
> + PROTECTED_VARIABLE_GLOBAL *OldGlobal;
> + PROTECTED_VARIABLE_GLOBAL *NewGlobal;
> + VOID *VarWriteReg;
> +
> + if ( (ContextIn == NULL)
> + || (ContextIn->StructVersion !=
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
> + || (ContextIn->StructSize != sizeof (PROTECTED_VARIABLE_CONTEXT_IN))
> + || (ContextIn->GetVariableInfo == NULL)
> + || (ContextIn->GetNextVariableInfo == NULL)
> + || (ContextIn->UpdateVariableStore == NULL)
> + || (ContextIn->UpdateVariable == NULL))
> + {
> + ASSERT (ContextIn != NULL);
> + ASSERT (ContextIn->StructVersion ==
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION);
> + ASSERT (ContextIn->StructSize == sizeof
> (PROTECTED_VARIABLE_CONTEXT_IN));
> + ASSERT (ContextIn->GetVariableInfo != NULL);
> + ASSERT (ContextIn->GetNextVariableInfo != NULL);
> + ASSERT (ContextIn->UpdateVariableStore != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + GetProtectedVariableGlobal (&NewGlobal);
> + ProtectedVarContext = GET_CNTX (NewGlobal);
> + CopyMem (ProtectedVarContext, ContextIn, sizeof (mVariableContextIn));
> + ProtectedVarContext->VariableServiceUser = FromSmmModule;
> +
> + //
> + // Get root key and HMAC key from HOB created by PEI variable driver.
> + //
> + Status = GetProtectedVariableGlobalFromHob (&OldGlobal);
> + ASSERT_EFI_ERROR (Status);
> +
> + CopyMem ((VOID *)NewGlobal, (CONST VOID *)OldGlobal, sizeof
> (*OldGlobal));
> +
> + //
> + // The keys must not be available outside SMM.
> + //
> + if (ProtectedVarContext->VariableServiceUser == FromSmmModule) {
> + ZeroMem (OldGlobal->RootKey, sizeof (OldGlobal->RootKey));
> + ZeroMem (OldGlobal->MetaDataHmacKey, sizeof (OldGlobal-
> >MetaDataHmacKey));
> + }
> +
> + //
> + // Register variable write protocol notify function used to fix any
> + // inconsistency in MetaDataHmacVariable before the first variable write
> + // operation.
> + //
> + NewGlobal->Flags.WriteInit = FALSE;
> + NewGlobal->Flags.WriteReady = FALSE;
> +
> + EfiCreateProtocolNotifyEvent (
> + &gEfiVariableWriteArchProtocolGuid,
> + TPL_CALLBACK,
> + VariableWriteProtocolCallback,
> + NULL,
> + &VarWriteReg
> + );
> +
> + return EFI_SUCCESS;
> +}
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
> new file mode 100644
> index 000000000000..8b5ccb83e32d
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
> @@ -0,0 +1,1327 @@
> +/** @file
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Base.h>
> +#include <Uefi.h>
> +#include <PiPei.h>
> +
> +#include <Guid/VariableFormat.h>
> +#include <Ppi/MemoryDiscovered.h>
> +
> +#include <Library/HobLib.h>
> +#include <Library/PeiServicesLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/ReportStatusCodeLib.h>
> +
> +#include "ProtectedVariableInternal.h"
> +
> +/**
> + Function allocates a global buffer.
> +
> + This function allocates a buffer with the specified size.
> +
> + @param[in] Size Size of buffer to allocate.
> + @param[in] AllocatePage Whether to allocate pages.
> +
> + @retval Buffer Pointer to the Buffer allocated.
> + @retval NULL if no Buffer was found.
> +
> +**/
> +VOID *
> +AllocateGlobalBuffer (
> + IN UINT32 Size,
> + IN BOOLEAN AllocatePage
> + )
> +{
> + VOID *Buffer;
> + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
> + EFI_PEI_HOB_POINTERS Hob;
> +
> + Buffer = NULL;
> + if (!AllocatePage) {
> + Buffer = BuildGuidHob (&gEdkiiProtectedVariableGlobalGuid, Size);
> + }
> +
> + if (Buffer == NULL) {
> + //
> + // Use the AllocatePages() to get over size limit of general GUID-ed HOB.
> + //
> + Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
> + if (Buffer == NULL) {
> + ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
> + return NULL;
> + }
> +
> + //
> + // Mark the HOB holding the pages just allocated so that it can be
> + // identified later.
> + //
> + MemoryAllocationHob = NULL;
> + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
> + while (Hob.Raw != NULL) {
> + MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw;
> + if ((UINTN)Buffer == (UINTN)MemoryAllocationHob-
> >AllocDescriptor.MemoryBaseAddress) {
> + CopyGuid (
> + &MemoryAllocationHob->AllocDescriptor.Name,
> + &gEdkiiProtectedVariableGlobalGuid
> + );
> + break;
> + }
> +
> + Hob.Raw = GET_NEXT_HOB (Hob);
> + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
> Hob.Raw);
> + }
> + }
> +
> + return Buffer;
> +}
> +
> +/**
> + Callback use to re-verify all variables and cache them in memory.
> +
> + @param[in] PeiServices General purpose services available to every PEIM.
> + @param[in] NotifyDescriptor The notification structure this PEIM registered
> on install.
> + @param[in] Ppi The memory discovered PPI. Not used.
> +
> + @retval EFI_SUCCESS The function completed successfully.
> + @retval others There's error in MP initialization.
> +**/
> +EFI_STATUS
> +EFIAPI
> +MemoryDiscoveredPpiNotifyCallback (
> + IN EFI_PEI_SERVICES **PeiServices,
> + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
> + IN VOID *Ppi
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *VarDig;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + VOID *Buffer;
> + UINT32 VarSize;
> + INTN Result;
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + ContextIn = GET_CNTX (Global);
> +
> + //
> + // Allocate last Var buffer for confidentiality crypto operation
> + //
> + VarSize = (Global->VariableNumber + 1) * MAX_VARIABLE_SIZE;
> + Buffer = AllocateGlobalBuffer (VarSize, TRUE);
> +
> + //
> + // Traverse all valid variables.
> + //
> + VarDig = VAR_DIG_PTR (Global->VariableDigests);
> + while (VarDig != NULL) {
> + if (VarDig->CacheIndex == VAR_INDEX_INVALID) {
> + ASSERT (VarDig->StoreIndex != VAR_INDEX_INVALID);
> +
> + VarSize = VARIABLE_HEADER_SIZE (Global->Flags.Auth);
> + VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> + VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> +
> + //
> + // Note the variable might be in unconsecutive space.
> + //
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> + VarInfo.StoreIndex = VarDig->StoreIndex;
> + VarInfo.Buffer = Buffer;
> + VarInfo.Flags.Auth = VarDig->Flags.Auth;
> +
> + Status = ContextIn->GetVariableInfo (&VarInfo);
> + ASSERT_EFI_ERROR (Status);
> + //
> + // VerifyVariableDigest() refers to CipherData for raw data.
> + //
> + VarInfo.CipherData = VarInfo.Header.Data;
> + VarInfo.CipherDataSize = (UINT32)VarInfo.Header.DataSize;
> +
> + //
> + // Make sure that the cached copy is not compromised.
> + //
> + Status = VerifyVariableDigest (Global, &VarInfo, VarDig);
> + if (EFI_ERROR (Status)) {
> + REPORT_STATUS_CODE (
> + EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
> + (PcdGet32 (PcdStatusCodeVariableIntegrity) | (Status & 0xFF))
> + );
> + ASSERT_EFI_ERROR (Status);
> + CpuDeadLoop ();
> + }
> +
> + //
> + // Simply use the cache address as CacheIndex of the variable.
> + //
> + VarDig->CacheIndex = GET_ADRS (Buffer);
> + Buffer = (UINT8 *)Buffer + MAX_VARIABLE_SIZE;
> + } else {
> + Result = StrnCmp (
> + VAR_DIG_NAME (VarDig),
> + METADATA_HMAC_VARIABLE_NAME,
> + METADATA_HMAC_VARIABLE_NAME_SIZE
> + );
> + if (Result == 0) {
> + CopyMem (
> + Buffer,
> + GET_BUFR (Global->GlobalSelf + (Global->StructSize -
> GetMetaDataHmacVarSize (Global->Flags.Auth))),
> + GetMetaDataHmacVarSize (Global->Flags.Auth)
> + );
> +
> + //
> + // Simply use the cache address as CacheIndex of the variable.
> + //
> + VarDig->CacheIndex = GET_ADRS (Buffer);
> + Buffer = (UINT8 *)Buffer + MAX_VARIABLE_SIZE;
> + }
> + }
> +
> + VarDig = VAR_DIG_NEXT (VarDig);
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Callback use to perform variable integrity check.
> +
> + @param[in] PeiServices General purpose services available to every PEIM.
> + @param[in] NotifyDescriptor The notification structure this PEIM registered
> on install.
> + @param[in] Ppi The memory discovered PPI. Not used.
> +
> + @retval EFI_SUCCESS The function completed successfully.
> + @retval others There's error in MP initialization.
> +**/
> +EFI_STATUS
> +EFIAPI
> +VariableStoreDiscoveredPpiNotifyCallback (
> + IN EFI_PEI_SERVICES **PeiServices,
> + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
> + IN VOID *Ppi
> + )
> +{
> + EFI_STATUS Status;
> + EFI_HOB_GUID_TYPE *GuidHob;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> +
> + GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
> + if (GuidHob != NULL) {
> + ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
> (GuidHob);
> + } else {
> + ASSERT (GuidHob == NULL);
> + }
> +
> + Status = ContextIn->IsHobVariableStoreAvailable ();
> +
> + if (Status == EFI_NOT_READY) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + Status = PerformVariableIntegrityCheck (ContextIn);
> +
> + return Status;
> +}
> +
> +EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[] = {
> + {
> + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> + &gEfiPeiMemoryDiscoveredPpiGuid,
> + MemoryDiscoveredPpiNotifyCallback
> + }
> +};
> +
> +EFI_PEI_NOTIFY_DESCRIPTOR mVariableStoreNotifyList[] = {
> + {
> + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> + &gEfiPeiVariableStoreDiscoveredPpiGuid,
> + VariableStoreDiscoveredPpiNotifyCallback
> + }
> +};
> +
> +/**
> +
> + Get global data structure used to process protected variable.
> +
> + @param[out] Global Pointer to global configuration data.
> +
> + @retval EFI_SUCCESS Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> + OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
> + )
> +{
> + return GetProtectedVariableGlobalFromHob (Global);
> +}
> +
> +/**
> +
> + Get context data structure used to process protected variable.
> +
> + @param[out] ContextIn Pointer to context provided by variable runtime
> services.
> +
> + @retval EFI_SUCCESS Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableContext (
> + PROTECTED_VARIABLE_CONTEXT_IN **ContextIn OPTIONAL
> + )
> +{
> + EFI_HOB_GUID_TYPE *GuidHob;
> +
> + GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
> + if (GuidHob != NULL) {
> + *ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
> (GuidHob);
> + return EFI_SUCCESS;
> + }
> +
> + return EFI_NOT_FOUND;
> +}
> +
> +/**
> + Verify the HMAC value stored in MetaDataHmacVar against all valid and
> + protected variables in storage.
> +
> + @param[in,out] Global Pointer to global configuration data.
> +
> + @retval EFI_SUCCESS The HMAC value matches.
> + @retval EFI_ABORTED Error in HMAC value calculation.
> + @retval EFI_VOLUME_CORRUPTED Inconsistency found in NV variable
> storage.
> + @retval EFI_COMPROMISED_DATA The HMAC value doesn't match.
> +
> +**/
> +EFI_STATUS
> +VerifyMetaDataHmac (
> + IN OUT PROTECTED_VARIABLE_GLOBAL *Global
> + )
> +{
> + EFI_STATUS Status;
> + VARIABLE_DIGEST *VariableDig;
> + UINT32 Counter1;
> + UINT32 Counter2;
> + VOID *Hmac1;
> + VOID *Hmac2;
> + UINT8 HmacVal1[METADATA_HMAC_SIZE];
> + UINT8 HmacVal2[METADATA_HMAC_SIZE];
> +
> + Hmac1 = NULL;
> + Hmac2 = HmacSha256New ();
> + if (Hmac2 == NULL) {
> + ASSERT (Hmac2 != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + if (!HmacSha256SetKey (Hmac2, Global->MetaDataHmacKey, sizeof (Global-
> >MetaDataHmacKey))) {
> + ASSERT (FALSE);
> + Status = EFI_ABORTED;
> + goto Done;
> + }
> +
> + //
> + // Retrieve the RPMC counter value.
> + //
> + Status = RequestMonotonicCounter (RPMC_COUNTER_1, &Counter1);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + goto Done;
> + }
> +
> + Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter2);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + goto Done;
> + }
> +
> + //
> + // Counter1 must be either equal to Counter2 or just one step ahead of
> Counter2.
> + //
> + if ((Counter1 > Counter2) && ((Counter1 - Counter2) > 1)) {
> + Status = EFI_COMPROMISED_DATA;
> + goto Done;
> + }
> +
> + VariableDig = VAR_DIG_PTR (Global->VariableDigests);
> + while (VariableDig != NULL) {
> + //
> + // Only take valid protected variables into account.
> + //
> + if (VariableDig->Flags.Protected && VariableDig->Flags.Valid) {
> + if (!HmacSha256Update (
> + Hmac2,
> + VAR_DIG_VALUE (VariableDig),
> + VariableDig->DigestSize
> + ))
> + {
> + ASSERT (FALSE);
> + Status = EFI_ABORTED;
> + goto Done;
> + }
> + }
> +
> + VariableDig = VAR_DIG_NEXT (VariableDig);
> + }
> +
> + //
> + // If two MetaDataHmacVariable were found, check which one is valid. We
> might
> + // need two HMAC values to check against: one for Counter1, one for
> Counter2.
> + //
> + if ( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> + && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID)
> + && (Counter1 != Counter2))
> + {
> + //
> + // Might need to check Counter1. There must be something wrong in last
> boot.
> + //
> + Hmac1 = HmacSha256New ();
> + if ((Hmac1 == NULL) || !HmacSha256Duplicate (Hmac2, Hmac1)) {
> + ASSERT (FALSE);
> + Status = EFI_OUT_OF_RESOURCES;
> + goto Done;
> + }
> +
> + if ( !HmacSha256Update (Hmac1, &Counter1, sizeof (Counter1))
> + || !HmacSha256Final (Hmac1, HmacVal1))
> + {
> + ASSERT (FALSE);
> + Status = EFI_ABORTED;
> + goto Done;
> + }
> + }
> +
> + //
> + // Always check Counter2.
> + //
> + if ( !HmacSha256Update (Hmac2, &Counter2, sizeof (Counter2))
> + || !HmacSha256Final (Hmac2, HmacVal2))
> + {
> + ASSERT (FALSE);
> + Status = EFI_ABORTED;
> + goto Done;
> + }
> +
> + //
> + // When writing (update or add) a variable, there must be following steps
> + // performed:
> + //
> + // A - Increment Counter1
> + // B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> + // C - Calculate new HMAC value against Counter2+1,
> + // and force-add a new MetaDataHmacVar with state of VAR_ADDED
> + // D - Write the new protected variable
> + // E - Increment Counter2
> + // F - Mark old MetaDataHmacVar as VAR_DELETED
> + //
> + Status = EFI_COMPROMISED_DATA;
> + if ( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> + && (Global->Unprotected[IndexHmacInDel] == VAR_INDEX_INVALID))
> + {
> + if (CompareMem (
> + VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacAdded])),
> + HmacVal2,
> + METADATA_HMAC_SIZE
> + ) == 0)
> + {
> + //
> + //
> + // + A - Increment Counter1
> + // B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> + // C - Calculate new HMAC value against Counter2+1,
> + // and force-add a new MetaDataHmacVar with state of VAR_ADDED
> + // D - Write the new protected variable
> + // E - Increment Counter2
> + // F - Mark old MetaDataHmacVar as VAR_DELETED
> + //
> + // or,
> + //
> + // + A - Increment Counter1
> + // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> + // + C - Calculate new HMAC value against Counter2+1,
> + // and force-add a new MetaDataHmacVar with state of VAR_ADDED
> + // + D - Write the new protected variable
> + // + E - Increment Counter2
> + // + F - Mark old MetaDataHmacVar as VAR_DELETED
> + //
> + Status = EFI_SUCCESS;
> +
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> TRUE;
> + }
> + } else if ( (Global->Unprotected[IndexHmacAdded] == VAR_INDEX_INVALID)
> + && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID))
> + {
> + if (CompareMem (
> + VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacInDel])),
> + HmacVal2,
> + METADATA_HMAC_SIZE
> + ) == 0)
> + {
> + //
> + // + A - Increment Counter1
> + // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> + // C - Calculate new HMAC value against Counter2+1,
> + // and force-add a new MetaDataHmacVar with state of VAR_ADDED
> + // D - Write the new protected variable
> + // E - Increment Counter2
> + // F - Mark old MetaDataHmacVar as VAR_DELETED
> + //
> + Status = EFI_SUCCESS;
> +
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid = TRUE;
> + }
> + } else if ( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> + && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID))
> + {
> + if (Counter1 > Counter2) {
> + if (CompareMem (
> + VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacInDel])),
> + HmacVal2,
> + METADATA_HMAC_SIZE
> + ) == 0)
> + {
> + //
> + // + A - Increment Counter1
> + // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> + // + C - Calculate new HMAC value against Counter2+1,
> + // and force-add a new MetaDataHmacVar with state VAR_ADDED
> + // D - Write the new protected variable
> + // E - Increment Counter2
> + // F - Mark old MetaDataHmacVar as VAR_DELETED
> + //
> + Status = EFI_SUCCESS;
> +
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> FALSE;
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
> TRUE;
> + } else if (CompareMem (
> + VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacAdded])),
> + HmacVal1,
> + METADATA_HMAC_SIZE
> + ) == 0)
> + {
> + //
> + // + A - Increment Counter1
> + // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> + // + C - Calculate new HMAC value against Counter2+1,
> + // and force-add a new MetaDataHmacVar with state of VAR_ADDED
> + // + D - Write the new protected variable
> + // E - Increment Counter2
> + // F - Mark old MetaDataHmacVar as VAR_DELETED
> + //
> + Status = EFI_SUCCESS;
> +
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> TRUE;
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
> FALSE;
> + }
> + } else {
> + if (CompareMem (
> + VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacAdded])),
> + HmacVal2,
> + METADATA_HMAC_SIZE
> + ) == 0)
> + {
> + //
> + // + A - Increment Counter1
> + // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> + // + C - Calculate new HMAC value against Counter2+1,
> + // and force-add a new MetaDataHmacVar with state of VAR_ADDED
> + // + D - Write the new protected variable
> + // + E - Increment Counter2
> + // F - Mark old MetaDataHmacVar as VAR_DELETED
> + //
> + Status = EFI_SUCCESS;
> +
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> TRUE;
> + VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
> FALSE;
> + }
> + }
> + } else {
> + //
> + // There must be logic error or variable written to storage skipped
> + // the protected variable service, if code reaches here.
> + //
> + ASSERT (FALSE);
> + }
> +
> +Done:
> + if (Hmac1 != NULL) {
> + HmacSha256Free (Hmac1);
> + }
> +
> + if (Hmac2 != NULL) {
> + HmacSha256Free (Hmac2);
> + }
> +
> + return Status;
> +}
> +
> +/**
> + Collect variable digest information.
> +
> + This information is collected to be used to for integrity check.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in] ContextIn Pointer to variable service context needed by
> + protected variable.
> + @param[in, out] DigestBuffer Base address of digest of each variable.
> + @param[out] DigestBufferSize Digest size of one variable if DigestBuffer
> is NULL.
> + Size of DigestBuffer if DigestBuffer is NOT NULL.
> + @param[out] VariableNumber Number of valid variables.
> +
> + @retval EFI_SUCCESS Successfully retreived variable digest.
> + @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.
> + @retval EFI_OUT_OF_RESOURCES Unable to allocate memory.
> + @retval EFI_BUFFER_TOO_SMALL The DigestBufferSize pass in is too small.
> +
> +**/
> +EFI_STATUS
> +CollectVariableDigestInfo (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn,
> + IN OUT VOID *DigestBuffer OPTIONAL,
> + OUT UINT32 *DigestBufferSize OPTIONAL,
> + OUT UINT32 *VariableNumber OPTIONAL
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + UINT32 VarNum;
> + UINT32 DigSize;
> + VARIABLE_DIGEST *VarDig;
> + EFI_TIME TimeStamp;
> + UNPROTECTED_VARIABLE_INDEX VarIndex;
> +
> + //
> + // This function might be called before Global is initialized. In that case,
> + // Global must be NULL but not ContextIn.
> + //
> + if ((Global == NULL) && (ContextIn == NULL)) {
> + ASSERT (Global != NULL || ContextIn != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if ((Global == NULL) && (DigestBuffer != NULL)) {
> + ASSERT (Global != NULL && DigestBuffer != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if ( (DigestBuffer != NULL)
> + && ((DigestBufferSize == NULL) || (*DigestBufferSize == 0)))
> + {
> + ASSERT (
> + DigestBuffer != NULL
> + && DigestBufferSize != NULL && *DigestBufferSize > 0
> + );
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if ((Global != NULL) && (ContextIn == NULL)) {
> + ContextIn = GET_CNTX (Global);
> + }
> +
> + DigSize = 0;
> + VarNum = 0;
> + VarDig = NULL;
> +
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> + VarInfo.StoreIndex = VAR_INDEX_INVALID; // To get the first variable.
> +
> + if ((Global != NULL) &&
> + (Global->VariableCache != 0) &&
> + (Global->VariableCacheSize > 0))
> + {
> + //
> + // Use the variable cache to hold a copy of one variable.
> + //
> + VarInfo.Buffer = GET_BUFR (Global->VariableCache);
> + } else {
> + //
> + // Allocate a buffer to hold a copy of one variable
> + //
> + VarInfo.Buffer = AllocatePages (EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
> + if (VarInfo.Buffer == NULL) {
> + ASSERT (VarInfo.Buffer != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> + }
> +
> + if ((DigestBuffer != NULL) && (*DigestBufferSize > 0)) {
> + VarDig = DigestBuffer;
> + }
> +
> + while (TRUE) {
> + if (VarDig != NULL) {
> + if (DigSize >= (*DigestBufferSize)) {
> + //
> + // Out of buffer.
> + //
> + break;
> + }
> +
> + VarInfo.Header.VendorGuid = &VarDig->VendorGuid;
> + VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> + VarInfo.Header.NameSize = (UINTN)DigestBuffer +
> (UINTN)*DigestBufferSize
> + - (UINTN)VarInfo.Header.VariableName;
> + VarInfo.Header.TimeStamp = &TimeStamp;
> + VarInfo.Header.Data = NULL;
> + } else {
> + ZeroMem ((VOID *)&VarInfo.Header, sizeof (VarInfo.Header));
> + }
> +
> + Status = ContextIn->GetNextVariableInfo (&VarInfo);
> + if (EFI_ERROR (Status)) {
> + break;
> + }
> +
> + //
> + // Skip deleted variables.
> + //
> + if ( (VarInfo.Header.State != VAR_ADDED)
> + && (VarInfo.Header.State != (VAR_ADDED &
> VAR_IN_DELETED_TRANSITION)))
> + {
> + continue;
> + }
> +
> + if (Global != NULL) {
> + Global->Flags.Auth &= VarInfo.Flags.Auth;
> + }
> +
> + VarNum += 1;
> + DigSize += (UINT32)(sizeof (VARIABLE_DIGEST)
> + + VarInfo.Header.NameSize
> + + METADATA_HMAC_SIZE);
> + if ((DigestBuffer != NULL) && (DigSize > *DigestBufferSize)) {
> + ASSERT (DigSize <= *DigestBufferSize);
> + return EFI_BUFFER_TOO_SMALL;
> + }
> +
> + if (VarDig != NULL) {
> + VarDig->Prev = 0;
> + VarDig->Next = 0;
> + VarDig->State = VarInfo.Header.State;
> + VarDig->Attributes = VarInfo.Header.Attributes;
> + VarDig->DataSize = (UINT32)VarInfo.Header.DataSize;
> + VarDig->NameSize = (UINT16)VarInfo.Header.NameSize;
> + VarDig->DigestSize = METADATA_HMAC_SIZE;
> + VarDig->StoreIndex = VarInfo.StoreIndex;
> +
> + if ((VarInfo.Buffer != NULL) && ((UINTN)VarInfo.Buffer != Global-
> >VariableCache)) {
> + VarDig->CacheIndex = GET_ADRS (VarInfo.Buffer);
> + } else {
> + VarDig->CacheIndex = VAR_INDEX_INVALID;
> + }
> +
> + VarDig->Flags.Auth = VarInfo.Flags.Auth;
> + VarDig->Flags.Valid = TRUE;
> +
> + VarIndex = CheckKnownUnprotectedVariable (Global, &VarInfo);
> + if (VarIndex >= UnprotectedVarIndexMax) {
> + //
> + // Check information relating to encryption, if enabled.
> + //
> + VarDig->Flags.Encrypted = FALSE;
> + if ((VarInfo.Header.Data != NULL) && (VarInfo.Header.DataSize > 0)) {
> + VarInfo.CipherData = NULL;
> + VarInfo.CipherDataSize = 0;
> + VarInfo.PlainData = NULL;
> + VarInfo.PlainDataSize = 0;
> + Status = GetCipherDataInfo (&VarInfo);
> + if (!EFI_ERROR (Status)) {
> + //
> + // Discovered encrypted variable mark variable to be
> + // encrypted on the next SetVariable() operation
> + //
> + VarDig->Flags.Encrypted = PcdGetBool
> (PcdProtectedVariableConfidentiality);
> + } else {
> + VarInfo.PlainData = VarInfo.Header.Data;
> + VarInfo.PlainDataSize = (UINT32)VarInfo.Header.DataSize;
> + VarInfo.CipherDataType = 0;
> + VarInfo.CipherHeaderSize = 0;
> + if (Status == EFI_NOT_FOUND) {
> + //
> + // Found variable that is not encrypted mark variable to be
> + // encrypted on the next SetVariable() operation
> + //
> + VarDig->Flags.Encrypted = PcdGetBool
> (PcdProtectedVariableConfidentiality);
> + }
> + }
> + }
> +
> + //
> + // Variable is protected
> + //
> + VarDig->Flags.Protected = PcdGetBool (PcdProtectedVariableIntegrity);
> + VarDig->PlainDataSize = VarInfo.PlainDataSize;
> +
> + //
> + // Calculate digest only for protected variable.
> + //
> + Status = GetVariableDigest (Global, &VarInfo, VAR_DIG_VALUE (VarDig));
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + //
> + // Keep the VarDig in an ordered list.
> + //
> + InsertVariableDigestNode (Global, VarDig, CompareVariableDigestInfo);
> + } else {
> + VarDig->Flags.Protected = FALSE;
> + VarDig->Flags.Encrypted = FALSE;
> + VarDig->PlainDataSize = VarDig->DataSize;
> +
> + //
> + // Make use of VARIABLE_DIGEST->DigestValue to cache HMAC value
> from
> + // MetaDataHmacVar, which doesn't need a digest value (only protected
> + // variables need it for integrity check).
> + //
> + if ((VarIndex == IndexHmacInDel) || (VarIndex == IndexHmacAdded)) {
> + if (VarDig->State == VAR_ADDED) {
> + VarIndex = IndexHmacAdded;
> + } else {
> + VarIndex = IndexHmacInDel;
> + }
> + }
> +
> + Global->Unprotected[VarIndex] = VAR_DIG_ADR (VarDig);
> +
> + if ((VarInfo.Header.Data != NULL) && (VarDig->DataSize <= VarDig-
> >DigestSize)) {
> + CopyMem (VAR_DIG_VALUE (VarDig), VarInfo.Header.Data, VarDig-
> >DataSize);
> + }
> +
> + //
> + // Don't add the VarDig for MetaDataHmacVar into the linked list now.
> + // Do it after the HMAC has been validated.
> + //
> + if ((VarIndex != IndexHmacInDel) || (VarIndex != IndexHmacAdded)) {
> + InsertVariableDigestNode (Global, VarDig, CompareVariableDigestInfo);
> + }
> + }
> +
> + VarDig = (VARIABLE_DIGEST *)((UINTN)VarDig + VAR_DIG_END (VarDig));
> + }
> + }
> +
> + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
> + return Status;
> + }
> +
> + if (DigestBufferSize != NULL) {
> + *DigestBufferSize = DigSize;
> + }
> +
> + if (VariableNumber != NULL) {
> + *VariableNumber = VarNum;
> + }
> +
> + if ((Global == NULL) && (VarInfo.Buffer != NULL)) {
> + //
> + // Free Buffer
> + //
> + FreePages (VarInfo.Buffer, EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Perform for protected variable integrity check.
> +
> + 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
> +PerformVariableIntegrityCheck (
> + IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
> + )
> +{
> + EFI_STATUS Status;
> + UINT32 HobDataSize;
> + UINT32 VarNumber;
> + VOID *Buffer;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *DigBuffer;
> + UINT32 DigBufferSize;
> + UINT32 HmacMetaDataSize;
> + UINTN Index;
> + BOOLEAN PreviousKey;
> + EFI_HOB_GUID_TYPE *GuidHob;
> +
> + if ((ContextIn == NULL) || (ContextIn->GetNextVariableInfo == NULL)) {
> + ASSERT (ContextIn != NULL);
> + ASSERT (ContextIn->GetNextVariableInfo != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + ContextIn->StructSize = (ContextIn->StructSize == 0) ? sizeof (*ContextIn)
> + : ContextIn->StructSize;
> +
> + //
> + // Enumerate all variables first to collect info for resource allocation.
> + //
> + DigBufferSize = 0;
> + Status = CollectVariableDigestInfo (
> + NULL,
> + ContextIn,
> + NULL,
> + &DigBufferSize,
> + &VarNumber
> + );
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return EFI_VOLUME_CORRUPTED;
> + }
> +
> + //
> + // Allocate buffer for Global. Memory layout:
> + //
> + // Global
> + // Digest context
> + // Variable Digest List
> + // HmacMetaData
> + //
> + // To save precious NEM space of processor, variable cache will not be
> + // allocated at this point until physical memory is ready for use.
> + //
> + HmacMetaDataSize = (UINT32)GetMetaDataHmacVarSize (TRUE);
> + HobDataSize = sizeof (PROTECTED_VARIABLE_GLOBAL)
> + + (UINT32)DIGEST_CONTEXT_SIZE
> + + DigBufferSize
> + + HmacMetaDataSize;
> + Buffer = AllocateGlobalBuffer (HobDataSize, FALSE);
> + if (Buffer == NULL) {
> + ASSERT (Buffer != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + Global = (PROTECTED_VARIABLE_GLOBAL *)((UINTN)Buffer);
> + Global->DigestContext = GET_ADRS (Global + 1);
> +
> + if (DigBufferSize > 0) {
> + DigBuffer = (VARIABLE_DIGEST *)(UINTN)(Global->DigestContext +
> DIGEST_CONTEXT_SIZE);
> + ZeroMem (DigBuffer, DigBufferSize);
> + } else {
> + DigBuffer = NULL;
> + }
> +
> + //
> + // Keep a copy of ContextIn in HOB for later uses.
> + //
> + Global->GlobalSelf = GET_ADRS (Global);
> + Global->ContextIn = GET_ADRS (ContextIn);
> +
> + Global->StructVersion =
> PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION;
> + Global->StructSize = HobDataSize;
> +
> + Global->VariableNumber = VarNumber;
> + Global->VariableDigests = 0;
> +
> + Global->Flags.Auth = TRUE;
> + Global->Flags.WriteInit = FALSE;
> + Global->Flags.WriteReady = FALSE;
> +
> + GuidHob = GetFirstGuidHob (&gEfiAuthenticatedVariableGuid);
> + if (GuidHob == NULL) {
> + GuidHob = GetFirstGuidHob (&gEfiVariableGuid);
> + if (GuidHob != NULL) {
> + Global->Flags.Auth = FALSE;
> + }
> + }
> +
> + Global->Flags.RecoveryMode = (GuidHob != NULL);
> +
> + //
> + // Before physical memory is ready, we cannot cache all variables in the very
> + // limited NEM space. But we still need to reserve buffer to hold data of
> + // one variable as well as context for integrity check (HMAC calculation).
> + //
> + Global->VariableCacheSize = MAX_VARIABLE_SIZE;
> + Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Global-
> >VariableCacheSize));
> + if (Buffer == NULL) {
> + ASSERT (Buffer != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + Global->VariableCache = GET_ADRS (Buffer);
> + Global->LastAccessedVariable = VAR_INDEX_INVALID;
> +
> + for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
> + Global->Unprotected[Index] = VAR_INDEX_INVALID;
> + }
> +
> + //
> + // Re-enumerate all NV variables and build digest list.
> + //
> + Status = CollectVariableDigestInfo (
> + Global,
> + ContextIn,
> + DigBuffer,
> + &DigBufferSize,
> + &VarNumber
> + );
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + ASSERT (Global->VariableNumber == VarNumber);
> +
> + //
> + // Fix-up number of valid protected variables (i.e. exclude unprotected ones)
> + //
> + for (Index = 0; VarNumber != 0 && Index < UnprotectedVarIndexMax; ++Index)
> {
> + if (Global->Unprotected[Index] != VAR_INDEX_INVALID) {
> + --VarNumber;
> + }
> + }
> +
> + //
> + // Get root key and generate HMAC key.
> + //
> + PreviousKey = FALSE;
> + Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global-
> >RootKey));
> + if (EFI_ERROR (Status)) {
> + ASSERT (FALSE);
> + Status = EFI_COMPROMISED_DATA;
> + }
> +
> + //
> + // Derive the MetaDataHmacKey from root key
> + //
> + if (!GenerateMetaDataHmacKey (
> + Global->RootKey,
> + sizeof (Global->RootKey),
> + Global->MetaDataHmacKey,
> + sizeof (Global->MetaDataHmacKey)
> + ))
> + {
> + ASSERT (FALSE);
> + Status = EFI_COMPROMISED_DATA;
> + }
> +
> + //
> + // Check the integrity of all NV variables, if any.
> + //
> + if (( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> + || (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID)))
> + {
> + //
> + // Validate the HMAC stored in variable MetaDataHmacVar.
> + //
> + Status = VerifyMetaDataHmac (Global);
> + if (EFI_ERROR (Status)) {
> + //
> + // Try again with the previous root key if the latest key failed the HMAC
> validation.
> + //
> + Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global-
> >RootKey));
> + if (!EFI_ERROR (Status)) {
> + //
> + // Derive the MetaDataHmacKey from previous root key
> + //
> + if (GenerateMetaDataHmacKey (
> + Global->RootKey,
> + sizeof (Global->RootKey),
> + Global->MetaDataHmacKey,
> + sizeof (Global->MetaDataHmacKey)
> + ) == TRUE)
> + {
> + //
> + // Validate the HMAC stored in variable MetaDataHmacVar.
> + //
> + Status = VerifyMetaDataHmac (Global);
> + if (!EFI_ERROR (Status)) {
> + Status = EFI_COMPROMISED_DATA;
> + }
> + } else {
> + Status = EFI_COMPROMISED_DATA;
> + }
> + }
> + }
> + } else if (Global->Flags.RecoveryMode) {
> + //
> + // Generate the first version of MetaDataHmacVar.
> + //
> + Status = SyncRpmcCounter ();
> + if (!EFI_ERROR (Status)) {
> + Status = RefreshVariableMetadataHmac (Global, NULL, NULL);
> + if (!EFI_ERROR (Status)) {
> + //
> + // MetaDataHmacVar is always calculated against Counter2+1. Updating
> + // RPMCs to match it.
> + //
> + (VOID)IncrementMonotonicCounter (RPMC_COUNTER_1);
> + (VOID)IncrementMonotonicCounter (RPMC_COUNTER_2);
> + }
> + }
> + } else if ((VarNumber > 0) && !Global->Flags.RecoveryMode) {
> + //
> + // There's no MetaDataHmacVar found for protected variables. Suppose
> + // the variable storage is compromised.
> + //
> + Status = EFI_COMPROMISED_DATA;
> + }
> +
> + if (EFI_ERROR (Status)) {
> + //
> + // The integrity of variables have been compromised. The platform has to do
> + // something to recover the variable store. But the boot should not go on
> + // anyway this time.
> + //
> + DEBUG ((DEBUG_ERROR, "%a: %d Integrity check Status = %r\n",
> __FUNCTION__, __LINE__, Status));
> + REPORT_STATUS_CODE (
> + EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
> + (PcdGet32 (PcdStatusCodeVariableIntegrity) |
> EFI_SW_PEI_PC_RECOVERY_BEGIN)
> + );
> + #if defined (EDKII_UNIT_TEST_FRAMEWORK_ENABLED) // Avoid test
> malfunctioning.
> + return Status;
> + #else
> + ASSERT_EFI_ERROR (Status);
> + CpuDeadLoop ();
> + #endif
> + }
> +
> + //
> + // Everything's OK.
> + //
> + REPORT_STATUS_CODE (
> + EFI_PROGRESS_CODE,
> + PcdGet32 (PcdStatusCodeVariableIntegrity)
> + );
> +
> + if (GET_BUFR (Global->VariableCacheSize) != NULL) {
> + //
> + // Free Buffer
> + //
> + FreePages (Buffer, EFI_SIZE_TO_PAGES (Global->VariableCacheSize));
> + }
> +
> + //
> + // Keep the valid MetaDataHmacVar in the list.
> + //
> + for (Index = 0; Index < IndexPlatformVar; ++Index) {
> + if ( (Global->Unprotected[Index] != VAR_INDEX_INVALID)
> + && VAR_DIG_PTR (Global->Unprotected[Index])->Flags.Valid)
> + {
> + InsertVariableDigestNode (
> + Global,
> + VAR_DIG_PTR (Global->Unprotected[Index]),
> + NULL
> + );
> + }
> + }
> +
> + //
> + // Restore the key to the latest one.
> + //
> + if (PreviousKey) {
> + Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global->RootKey));
> + ASSERT_EFI_ERROR (Status);
> +
> + //
> + // Derive the MetaDataHmacKey from root key
> + //
> + if (!GenerateMetaDataHmacKey (
> + Global->RootKey,
> + sizeof (Global->RootKey),
> + Global->MetaDataHmacKey,
> + sizeof (Global->MetaDataHmacKey)
> + ))
> + {
> + ASSERT (FALSE);
> + }
> + }
> +
> + //
> + // Make sure that the RPMC counter is in-sync.
> + //
> + Status = SyncRpmcCounter ();
> +
> + //
> + // Setup a hook to migrate data in Global once physical memory is ready.
> + //
> + Status = PeiServicesNotifyPpi (mPostMemNotifyList);
> +
> + return Status;
> +}
> +
> +/**
> +
> + Initialization for protected variable services.
> +
> + If the variable store is available than perform integrity check.
> + Otherwise, defer integrity check until variable store is available.
> +
> +
> + @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
> + )
> +{
> + EFI_STATUS Status;
> + VOID *ContextInHob;
> + PROTECTED_VARIABLE_INFO VarInfo;
> +
> + if ((ContextIn == NULL) || (ContextIn->GetNextVariableInfo == NULL)) {
> + ASSERT (ContextIn != NULL);
> + ASSERT (ContextIn->GetNextVariableInfo != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // Keep a copy of ContextIn in HOB for later uses.
> + //
> + ContextIn->StructSize = (ContextIn->StructSize == 0) ? sizeof (*ContextIn)
> + : ContextIn->StructSize;
> + ContextInHob = BuildGuidHob (&gEdkiiProtectedVariableContextGuid,
> ContextIn->StructSize);
> + CopyMem (ContextInHob, ContextIn, ContextIn->StructSize);
> +
> + //
> + // Discover if Variable Store Info Hob has been published by platform driver.
> + // It contains information regards to HOB or NV Variable Store availability
> + //
> + ZeroMem ((VOID *)&VarInfo.Header, sizeof (VarInfo.Header));
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarInfo.Buffer = AllocatePages (EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
> + if (VarInfo.Buffer == NULL) {
> + ASSERT (VarInfo.Buffer != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + FreePages (VarInfo.Buffer, EFI_SIZE_TO_PAGES ((MAX_VARIABLE_SIZE)));
> +
> + Status = ContextIn->GetNextVariableInfo (&VarInfo);
> + if (EFI_ERROR (Status)) {
> + //
> + // Register for platform driver callback when Variable Store is available.
> + //
> + DEBUG ((DEBUG_INFO, "Variable Store is not available. Register for a
> integrity check callback\n"));
> + Status = PeiServicesNotifyPpi (mVariableStoreNotifyList);
> + return Status;
> + }
> +
> + //
> + // HOB Variable store is not available
> + // Assume NV Variable store is available instead
> + // Perform integrity check on NV Variable Store
> + //
> + DEBUG ((DEBUG_INFO, "NV Variable Store is available. Perform integrity
> check\n"));
> + Status = PerformVariableIntegrityCheck (ContextInHob);
> + return Status;
> +}
> +
> +/**
> +
> + Prepare for variable update.
> +
> + (Not suppported in PEI phase.)
> +
> + @retval EFI_UNSUPPORTED Updating variable is not supported.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteInit (
> + VOID
> + )
> +{
> + ASSERT (FALSE);
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> + Update a variable with protection provided by this library.
> +
> + Not supported in PEI phase.
> +
> + @param[in,out] CurrVariable Variable to be updated. It's NULL if
> + adding a new variable.
> + @param[in] CurrVariableInDel In-delete-transition copy of updating
> variable.
> + @param[in,out] NewVariable Buffer of new variable data.
> + Buffer of "MetaDataHmacVar" and new
> + variable (encrypted).
> + @param[in,out] NewVariableSize Size of NewVariable.
> + Size of (encrypted) NewVariable and
> + "MetaDataHmacVar".
> +
> + @retval EFI_UNSUPPORTED Not support updating variable in PEI phase.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibUpdate (
> + IN OUT VARIABLE_HEADER *CurrVariable,
> + IN VARIABLE_HEADER *CurrVariableInDel,
> + IN OUT VARIABLE_HEADER *NewVariable,
> + IN OUT UINTN *NewVariableSize
> + )
> +{
> + ASSERT (FALSE);
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> + Finalize a variable updating after it's written to NV variable storage
> + successfully.
> +
> + @param[in] NewVariable Buffer of new variables and
> MetaDataHmacVar.
> + @param[in] VariableSize Size of buffer pointed by NewVariable.
> + @param[in] StoreIndex StoreIndex to NV variable storage from where
> the new
> + variable and MetaDataHmacVar have been written.
> +
> + @retval EFI_UNSUPPORTED Not support updating variable in PEI phase.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteFinal (
> + IN VARIABLE_HEADER *NewVariable,
> + IN UINTN VariableSize,
> + IN UINT64 StoreIndex
> + )
> +{
> + ASSERT (FALSE);
> + return EFI_UNSUPPORTED;
> +}
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
> new file mode 100644
> index 000000000000..8e964f4cd28d
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
> @@ -0,0 +1,209 @@
> +/** @file
> + Implemention of ProtectedVariableLib for SMM variable services.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Guid/SmmVariableCommon.h"
> +
> +#include "Library/MmServicesTableLib.h"
> +#include "Library/MemoryAllocationLib.h"
> +
> +#include "ProtectedVariableInternal.h"
> +
> +PROTECTED_VARIABLE_CONTEXT_IN mVariableContextIn = {
> + PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
> + sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
> + 0,
> + FromSmmModule,
> + NULL,
> + NULL,
> + NULL,
> + NULL,
> + NULL
> +};
> +
> +PROTECTED_VARIABLE_GLOBAL mProtectedVariableGlobal = {
> + PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
> + sizeof (PROTECTED_VARIABLE_GLOBAL),
> + { 0 },
> + { 0 },
> + 0,
> + 0,
> + 0,
> + 0,
> + 0,
> + 0,
> + { 0, 0, 0 },
> + 0,
> + 0,
> + { 0, 0, 0, 0}
> +};
> +
> +/**
> +
> + Callback function to call variable write.
> +
> + @param[in] Protocol Not Used.
> + @param[in] Interface Not Used.
> + @param[in] Handle Not Used.
> +
> + @retval EFI_SUCCESS Protected variable write successful.
> + @retval others Protected variable write failed.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +VariableWriteProtocolCallback (
> + IN CONST EFI_GUID *Protocol,
> + IN VOID *Interface,
> + IN EFI_HANDLE Handle
> + )
> +{
> + EFI_STATUS Status;
> +
> + Status = ProtectedVariableLibWriteInit ();
> + ASSERT_EFI_ERROR (Status);
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + 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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_CONTEXT_IN *ProtectedVarContext;
> + PROTECTED_VARIABLE_GLOBAL *OldGlobal;
> + PROTECTED_VARIABLE_GLOBAL *NewGlobal;
> + VARIABLE_DIGEST *VarDig;
> + VARIABLE_DIGEST *NewVarDig;
> + EFI_PHYSICAL_ADDRESS NewCacheIndex;
> + UINTN VarSize;
> + UNPROTECTED_VARIABLE_INDEX Index;
> +
> + if ( (ContextIn == NULL)
> + || (ContextIn->StructVersion !=
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
> + || (ContextIn->StructSize != sizeof (PROTECTED_VARIABLE_CONTEXT_IN))
> + || (ContextIn->GetVariableInfo == NULL)
> + || (ContextIn->GetNextVariableInfo == NULL)
> + || (ContextIn->UpdateVariableStore == NULL)
> + || (ContextIn->UpdateVariable == NULL))
> + {
> + ASSERT (ContextIn != NULL);
> + ASSERT (ContextIn->StructVersion ==
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION);
> + ASSERT (ContextIn->StructSize == sizeof
> (PROTECTED_VARIABLE_CONTEXT_IN));
> + ASSERT (ContextIn->GetVariableInfo != NULL);
> + ASSERT (ContextIn->GetNextVariableInfo != NULL);
> + ASSERT (ContextIn->UpdateVariableStore != NULL);
> + ASSERT (ContextIn->UpdateVariable != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + GetProtectedVariableGlobal (&NewGlobal);
> + ProtectedVarContext = GET_CNTX (NewGlobal);
> + CopyMem (ProtectedVarContext, ContextIn, sizeof (mVariableContextIn));
> + ProtectedVarContext->VariableServiceUser = FromSmmModule;
> +
> + //
> + // Get root key and HMAC key from HOB created by PEI variable driver.
> + //
> + Status = GetProtectedVariableGlobalFromHob (&OldGlobal);
> + ASSERT_EFI_ERROR (Status);
> +
> + CopyMem ((VOID *)NewGlobal, (CONST VOID *)OldGlobal, sizeof
> (*OldGlobal));
> +
> + //
> + // The keys must not be available outside SMM.
> + //
> + if (ProtectedVarContext->VariableServiceUser == FromSmmModule) {
> + ZeroMem (OldGlobal->RootKey, sizeof (OldGlobal->RootKey));
> + ZeroMem (OldGlobal->MetaDataHmacKey, sizeof (OldGlobal-
> >MetaDataHmacKey));
> + }
> +
> + NewGlobal->Flags.WriteInit = FALSE;
> + NewGlobal->Flags.WriteReady = FALSE;
> + NewGlobal->LastAccessedVariable = 0;
> + NewGlobal->VariableCache = GET_ADRS (AllocateZeroPool
> (MAX_VARIABLE_SIZE));
> + NewGlobal->DigestContext = GET_ADRS (AllocateZeroPool
> (DIGEST_CONTEXT_SIZE));
> + if (NewGlobal->DigestContext == 0) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Copy over variable from HOB to SMM memory
> + //
> + NewGlobal->VariableDigests = 0;
> + VarDig = VAR_DIG_PTR (OldGlobal->VariableDigests);
> + while (VarDig != NULL) {
> + //
> + // Allocate new Var Digest in SMM memory
> + //
> + NewVarDig = (VARIABLE_DIGEST *)AllocateZeroPool (
> + sizeof (VARIABLE_DIGEST) + VarDig->NameSize +
> METADATA_HMAC_SIZE
> + );
> + if (NewVarDig == NULL) {
> + ASSERT (NewVarDig != NULL);
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + CopyMem (NewVarDig, VarDig, sizeof (VARIABLE_DIGEST));
> + NewVarDig->Prev = 0;
> + NewVarDig->Next = 0;
> +
> + CopyMem (VAR_DIG_NAME (NewVarDig), VAR_DIG_NAME (VarDig), VarDig-
> >NameSize);
> + CopyMem (VAR_DIG_VALUE (NewVarDig), VAR_DIG_VALUE (VarDig),
> VarDig->DigestSize);
> +
> + VarSize = VARIABLE_HEADER_SIZE (NewGlobal->Flags.Auth);
> + VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> + VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> +
> + NewCacheIndex = GET_ADRS (AllocateZeroPool (VarSize));
> + if (GET_BUFR (NewCacheIndex) == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + CopyMem (GET_BUFR (NewCacheIndex), GET_BUFR (VarDig->CacheIndex),
> VarSize);
> + NewVarDig->CacheIndex = NewCacheIndex;
> + NewVarDig->Flags.Freeable = TRUE;
> +
> + for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
> + if (OldGlobal->Unprotected[Index] == VAR_DIG_ADR (VarDig)) {
> + NewGlobal->Unprotected[Index] = VAR_DIG_ADR (NewVarDig);
> + }
> + }
> +
> + InsertVariableDigestNode (NewGlobal, NewVarDig, NULL);
> +
> + VarDig = VAR_DIG_NEXT (VarDig);
> + }
> +
> + return Status;
> +}
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
> .c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
> .c
> new file mode 100644
> index 000000000000..8472fc8a33c7
> --- /dev/null
> +++
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
> .c
> @@ -0,0 +1,967 @@
> +/** @file
> + Implemention of ProtectedVariableLib for SMM variable services.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Guid/SmmVariableCommon.h"
> +
> +#include "Library/MmServicesTableLib.h"
> +#include "Library/MemoryAllocationLib.h"
> +#include <Library/HobLib.h>
> +
> +#include "ProtectedVariableInternal.h"
> +
> +/**
> +
> + Get context and/or global data structure used to process protected variable.
> +
> + @param[out] Global Pointer to global configuration data.
> +
> + @retval EFI_SUCCESS Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> + OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
> + )
> +{
> + if (Global != NULL) {
> + mProtectedVariableGlobal.ContextIn = GET_ADRS (&mVariableContextIn);
> + *Global = &mProtectedVariableGlobal;
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Encrypt given variable data and generate new HMAC value against it.
> +
> + @param[in] Global Pointer to global configuration data.
> + @param[in,out] NewVarInfo Pointer to buffer of new variable data.
> + @param[in,out] NewVarDig Pointer to buffer of new variable digest.
> +
> + @retval EFI_SUCCESS No error occurred during the encryption and HMC
> calculation.
> + @retval EFI_ABORTED Failed to do HMC calculation.
> + @return EFI_OUT_OF_RESOURCES Not enough resource to calculate HMC
> value.
> + @return EFI_NOT_FOUND The MetaDataHmacVar was not found in
> storage.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +UpdateVariableInternal (
> + IN PROTECTED_VARIABLE_GLOBAL *Global,
> + IN OUT PROTECTED_VARIABLE_INFO *NewVarInfo,
> + IN OUT VARIABLE_DIGEST *NewVarDig
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_INFO CachedVarInfo;
> + VOID *Buffer;
> + UINTN VarSize;
> +
> + if ((NewVarInfo == NULL) || (NewVarDig == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // If Add or update variable, encrypt new data first.
> + //
> + if (NewVarInfo->Buffer != NULL) {
> + Status = EFI_UNSUPPORTED;
> +
> + if (NewVarDig->Flags.Encrypted) {
> + NewVarInfo->PlainData = NULL;
> + NewVarInfo->PlainDataSize = 0;
> + NewVarInfo->CipherData = NULL;
> + NewVarInfo->CipherDataSize = 0;
> + NewVarInfo->Key = Global->RootKey;
> + NewVarInfo->KeySize = sizeof (Global->RootKey);
> + NewVarInfo->Header.Attributes &= (~EFI_VARIABLE_APPEND_WRITE);
> + Status = EncryptVariable (NewVarInfo);
> + if (!EFI_ERROR (Status)) {
> + //
> + // Update new data size in variable header.
> + //
> + SET_VARIABLE_DATA_SIZE (NewVarInfo, NewVarInfo->CipherDataSize);
> + } else if (Status != EFI_UNSUPPORTED) {
> + ASSERT (FALSE);
> + return Status;
> + }
> + }
> +
> + if (Status == EFI_UNSUPPORTED) {
> + NewVarInfo->CipherData = NewVarInfo->Header.Data;
> + NewVarInfo->CipherDataSize = (UINT32)NewVarInfo->Header.DataSize;
> + NewVarInfo->PlainData = NULL;
> + NewVarInfo->PlainDataSize = 0;
> + }
> + } else {
> + NewVarInfo->CipherData = NULL;
> + NewVarInfo->CipherDataSize = 0;
> + NewVarInfo->PlainData = NULL;
> + NewVarInfo->PlainDataSize = 0;
> + }
> +
> + if (NewVarDig->CacheIndex != 0) {
> + //
> + // Update the cached copy.
> + //
> + ZeroMem ((VOID *)&CachedVarInfo, sizeof (CachedVarInfo));
> + CachedVarInfo.Buffer = GET_BUFR (NewVarDig->CacheIndex);
> + CachedVarInfo.StoreIndex = VAR_INDEX_INVALID;
> + CachedVarInfo.Flags.Auth = NewVarInfo->Flags.Auth;
> +
> + Status = GET_CNTX (Global)->GetVariableInfo (&CachedVarInfo);
> + ASSERT_EFI_ERROR (Status);
> +
> + if ((CachedVarInfo.Header.DataSize != 0) && (NewVarInfo->CipherDataSize >
> CachedVarInfo.Header.DataSize)) {
> + //
> + // allocate new VarInfo buffer that is of greater CipherDataSize
> + //
> + VarSize = VARIABLE_HEADER_SIZE (NewVarDig->Flags.Auth);
> + VarSize += NewVarInfo->Header.NameSize + GET_PAD_SIZE (NewVarInfo-
> >Header.NameSize);
> + VarSize += NewVarInfo->CipherDataSize + GET_PAD_SIZE (NewVarInfo-
> >CipherDataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> + Buffer = AllocateZeroPool (VarSize);
> + if (Buffer != NULL) {
> + VarSize = VARIABLE_HEADER_SIZE (NewVarDig->Flags.Auth);
> + VarSize += CachedVarInfo.Header.NameSize + GET_PAD_SIZE
> (CachedVarInfo.Header.NameSize);
> + VarSize += CachedVarInfo.Header.DataSize + GET_PAD_SIZE
> (CachedVarInfo.DataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> +
> + CopyMem (
> + Buffer,
> + CachedVarInfo.Buffer,
> + VarSize
> + );
> +
> + FreePool (CachedVarInfo.Buffer);
> +
> + //
> + // Update the cached copy.
> + //
> + ZeroMem ((VOID *)&CachedVarInfo, sizeof (CachedVarInfo));
> + CachedVarInfo.Buffer = Buffer;
> + CachedVarInfo.StoreIndex = VAR_INDEX_INVALID;
> + CachedVarInfo.Flags.Auth = NewVarInfo->Flags.Auth;
> + Status = GET_CNTX (Global)->GetVariableInfo (&CachedVarInfo);
> + ASSERT_EFI_ERROR (Status);
> + NewVarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> + }
> + }
> +
> + CopyMem (
> + CachedVarInfo.Header.Data,
> + NewVarInfo->CipherData,
> + NewVarInfo->CipherDataSize
> + );
> + SET_VARIABLE_DATA_SIZE (&CachedVarInfo, NewVarInfo->CipherDataSize);
> +
> + NewVarDig->State = VAR_ADDED;
> + NewVarDig->DataSize = NewVarInfo->CipherDataSize;
> +
> + if (NewVarInfo->PlainDataSize > 0) {
> + NewVarDig->PlainDataSize = NewVarInfo->PlainDataSize;
> + } else {
> + NewVarDig->PlainDataSize = NewVarDig->DataSize;
> + }
> +
> + //
> + // (Re-)Calculate the hash of the variable.
> + //
> + if (NewVarDig->Flags.Protected) {
> + GetVariableDigest (Global, NewVarInfo, VAR_DIG_VALUE (NewVarDig));
> + }
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Fix state of MetaDataHmacVar on NV variable storage, if there's failure at
> + last boot during updating variable.
> +
> + This must be done before the first writing of variable in current boot,
> + including storage reclaim.
> +
> + @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 was successfully updated.
> +
> +**/
> +EFI_STATUS
> +FixupHmacVariable (
> + VOID
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_INFO HmacVarInfo;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + VARIABLE_DIGEST *VarDig;
> + UINTN Index;
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + ASSERT_EFI_ERROR (Status);
> +
> + if (Global->Flags.WriteReady) {
> + return EFI_SUCCESS;
> + }
> +
> + ContextIn = GET_CNTX (Global);
> +
> + //
> + // Delete invalid MetaDataHmacVar.
> + //
> + for (Index = 0; Index <= IndexHmacAdded; ++Index) {
> + if (Global->Unprotected[Index] == VAR_INDEX_INVALID) {
> + continue;
> + }
> +
> + VarDig = VAR_DIG_PTR (Global->Unprotected[Index]);
> + if (VarDig->Flags.Valid) {
> + continue;
> + }
> +
> + ZeroMem ((VOID *)&HmacVarInfo, sizeof (HmacVarInfo));
> + HmacVarInfo.StoreIndex = VarDig->StoreIndex;
> + HmacVarInfo.Flags.Auth = VarDig->Flags.Auth;
> +
> + Status = ContextIn->GetVariableInfo (&HmacVarInfo);
> + if (!EFI_ERROR (Status) && (HmacVarInfo.Buffer != NULL)) {
> + HmacVarInfo.Buffer->State &= VAR_DELETED;
> + Status = ContextIn->UpdateVariableStore (
> + &HmacVarInfo,
> + OFFSET_OF (VARIABLE_HEADER, State),
> + sizeof (HmacVarInfo.Buffer->State),
> + &HmacVarInfo.Buffer->State
> + );
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> + }
> +
> + //
> + // Release the resource and update related states.
> + //
> + VarDig->State &= VAR_DELETED;
> + RemoveVariableDigestNode (Global, VarDig, FALSE);
> + Global->Unprotected[Index] = VAR_INDEX_INVALID;
> + }
> +
> + //
> + // There should be no MetaDataHmacVar if in variable storage recovery mode.
> + //
> + if (Global->Flags.RecoveryMode) {
> + ASSERT (Global->Unprotected[IndexHmacAdded] == VAR_INDEX_INVALID);
> + ASSERT (Global->Unprotected[IndexHmacInDel] == VAR_INDEX_INVALID);
> + }
> +
> + Global->Flags.WriteReady = TRUE;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + 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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + PROTECTED_VARIABLE_INFO VarInfo;
> +
> + (VOID)GetProtectedVariableGlobal (&Global);
> + ContextIn = GET_CNTX (Global);
> +
> + //
> + // HmacVarInfo should be here
> + //
> + if (Global->Flags.RecoveryMode) {
> + //
> + // Flush default variables to variable storage if in variable recovery mode.
> + //
> + Status = ContextIn->UpdateVariableStore (NULL, 0, (UINT32)-1, NULL);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> + } else {
> + ContextIn = GET_CNTX (Global);
> +
> + //
> + // Fix any wrong MetaDataHmacVar information before adding new one.
> + //
> + Status = FixupHmacVariable ();
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + if (!Global->Flags.WriteReady) {
> + return EFI_NOT_READY;
> + }
> +
> + //
> + // Refresh MetaDataHmacVar with RPMC2 by 1 in each boot before any
> variable
> + // update, by deleting (attr == 0 && datasize == 0) the old one.
> + //
> + ZeroMem (&VarInfo, sizeof (PROTECTED_VARIABLE_INFO)); // Zero attr &
> datasize
> +
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> + VarInfo.Header.VariableName = METADATA_HMAC_VARIABLE_NAME;
> + VarInfo.Header.NameSize = METADATA_HMAC_VARIABLE_NAME_SIZE;
> + VarInfo.Header.VendorGuid = &METADATA_HMAC_VARIABLE_GUID;
> +
> + //
> + // Pretend to delete MetaDataHmacVar.
> + //
> + Status = ContextIn->UpdateVariable (&VarInfo.Header);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> + }
> +
> + mProtectedVariableGlobal.Flags.WriteInit = TRUE;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + 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, is variable
> + 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] CurrVariableInDel In-delete-transition copy of updating
> variable.
> + @param[in,out] NewVariable Buffer of new variable data.
> + Buffer of "MetaDataHmacVar" and new
> + variable (encrypted).
> + @param[in,out] NewVariableSize Size of NewVariable.
> + 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,
> + IN VARIABLE_HEADER *CurrVariableInDel,
> + IN OUT VARIABLE_HEADER *NewVariable,
> + IN OUT UINTN *NewVariableSize
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + VARIABLE_DIGEST *VarDig;
> + VARIABLE_DIGEST *CurrVarDig;
> + VARIABLE_DIGEST *NewVarDig;
> + PROTECTED_VARIABLE_INFO NewVarInfo;
> + PROTECTED_VARIABLE_INFO NewHmacVarInfo;
> + UINTN VarSize;
> + UINT64 UnprotectedVarIndex;
> +
> + //
> + // Advance RPMC
> + //
> + Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
> + ASSERT_EFI_ERROR (Status);
> +
> + //
> + // Buffer for new variable is always needed, even this function is called to
> + // delete an existing one, because we need to pass the MetaDataHmacVar
> back
> + // which will be updated upon each variable addition or deletion.
> + //
> + if ((NewVariable == NULL) || (NewVariableSize == NULL)) {
> + ASSERT (NewVariable != NULL);
> + ASSERT (NewVariableSize != NULL);
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + ASSERT_EFI_ERROR (Status);
> + ContextIn = GET_CNTX (Global);
> +
> + if (!Global->Flags.WriteReady && !Global->Flags.WriteInit) {
> + return EFI_NOT_READY;
> + }
> +
> + VarSize = 0;
> + CurrVarDig = NULL;
> + NewVarDig = NULL;
> + UnprotectedVarIndex = VAR_INDEX_INVALID;
> +
> + //
> + // Check existing copy of the same variable.
> + //
> + if (CurrVariable != NULL) {
> + //
> + // Find local cached copy, if possible.
> + //
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> + VarInfo.Buffer = CurrVariable;
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> + Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
> + ASSERT_EFI_ERROR (Status);
> +
> + UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global, &VarInfo);
> + if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
> + CurrVarDig = VAR_DIG_PTR (Global->Unprotected[UnprotectedVarIndex]);
> + } else {
> + CurrVarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> + }
> +
> + ASSERT (CurrVarDig != NULL);
> + CurrVarDig->State &= VAR_DELETED;
> + }
> +
> + //
> + // The old copy of the variable might haven't been deleted completely.
> + //
> + if (CurrVariableInDel != NULL) {
> + //
> + // Find local cached copy, if possible.
> + //
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> + VarInfo.Buffer = CurrVariableInDel;
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> + Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
> + ASSERT_EFI_ERROR (Status);
> +
> + if (UnprotectedVarIndex == VAR_INDEX_INVALID) {
> + UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global,
> &VarInfo);
> + }
> +
> + if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
> + VarDig = VAR_DIG_PTR (Global->Unprotected[UnprotectedVarIndex]);
> + } else {
> + VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> + }
> +
> + if ((VarDig != NULL) && (VAR_DIG_ADR (VarDig) != VAR_INDEX_INVALID)) {
> + VarDig->State &= VAR_DELETED;
> +
> + //
> + // Just need one node for the same variable. So remove the one
> + // in-del-transition.
> + //
> + if ((CurrVarDig != NULL) && (VarDig != CurrVarDig)) {
> + RemoveVariableDigestNode (Global, VarDig, TRUE);
> + } else {
> + CurrVarDig = VarDig; // Reuse the one in-del-transition.
> + }
> + }
> + }
> +
> + //
> + // New data of the variable or new variable to be added.
> + //
> + if (NewVariable != NULL) {
> + //
> + // Completely new variable?
> + //
> + if (UnprotectedVarIndex == VAR_INDEX_INVALID) {
> + ZeroMem (&VarInfo, sizeof (VarInfo));
> + VarInfo.Buffer = NewVariable;
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> + Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
> + ASSERT_EFI_ERROR (Status);
> +
> + UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global,
> &VarInfo);
> + }
> + }
> +
> + //
> + // Reserve space for MetaDataHmacVar (before the new variable so
> + // that it can be written first).
> + //
> + ZeroMem (&NewVarInfo, sizeof (NewVarInfo));
> + ZeroMem (&NewHmacVarInfo, sizeof (NewHmacVarInfo));
> +
> + //
> + // Put the MetaDataHmacVar at the beginning of buffer.
> + //
> + NewHmacVarInfo.Buffer = NewVariable;
> +
> + if (*NewVariableSize == 0) {
> + //
> + // Delete variable (but not MetaDataHmacVar)
> + //
> + if ( (UnprotectedVarIndex != IndexHmacAdded)
> + && (UnprotectedVarIndex != IndexHmacInDel))
> + {
> + RemoveVariableDigestNode (Global, CurrVarDig, TRUE);
> + }
> +
> + NewVarInfo.Buffer = NULL;
> + } else if (UnprotectedVarIndex >= IndexPlatformVar) {
> + //
> + // Add/update variable. Move new variable data to be after
> MetaDataHmacVar.
> + //
> + // TRICK: New MetaDataHmacVar will be put at the beginning of buffer
> + // for new variable so that they can be written into non-volatile
> + // variable storage in one call. This can avoid writing one variable
> + // (NewHmacVarInfo) in the middle of writing another variable
> + // (NewVarInfo), which will need two calls and introduce extra
> + // complexities (from temp variable buffer reservation to variable
> + // space reclaim, etc.) in current implementation of variable
> + // services. The caller must make sure there's enough space in
> + // variable buffer (i.e. at least 2 * MaxVariableSize).
> + //
> + NewVarInfo.Buffer = (VARIABLE_HEADER *)((UINTN)NewVariable
> + + GetMetaDataHmacVarSize (Global->Flags.Auth));
> + CopyMem ((VOID *)NewVarInfo.Buffer, (VOID *)NewVariable,
> *NewVariableSize);
> +
> + NewVarInfo.StoreIndex = VAR_INDEX_INVALID; // Skip offset calculation
> (it's new one)
> + NewVarInfo.Flags.Auth = Global->Flags.Auth;
> +
> + Status = ContextIn->GetVariableInfo (&NewVarInfo);
> + ASSERT_EFI_ERROR (Status);
> +
> + if (CurrVarDig != NULL) {
> + //
> + // Update existing variable. Re-use the node.
> + //
> + NewVarDig = CurrVarDig;
> + } else {
> + //
> + // Add new variable.
> + //
> + NewVarDig = CreateVariableDigestNode (
> + NewVarInfo.Header.VariableName,
> + NewVarInfo.Header.VendorGuid,
> + (UINT16)NewVarInfo.Header.NameSize,
> + (UINT32)NewVarInfo.Header.DataSize,
> + NewVarInfo.Flags.Auth,
> + Global
> + );
> + if (NewVarDig == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + NewVarDig->Attributes = NewVarInfo.Header.Attributes;
> + if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
> + NewVarDig->Flags.Protected = FALSE;
> + NewVarDig->Flags.Encrypted = FALSE;
> + Global->Unprotected[UnprotectedVarIndex] = VAR_DIG_ADR (NewVarDig);
> + }
> +
> + //
> + // copy new variable to CacheIndex
> + //
> + VarSize = VARIABLE_HEADER_SIZE (NewVarInfo.Flags.Auth);
> + VarSize += NewVarInfo.Header.NameSize + GET_PAD_SIZE
> (NewVarInfo.Header.NameSize);
> + VarSize += NewVarInfo.Header.DataSize + GET_PAD_SIZE
> (NewVarInfo.Header.DataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> + CopyMem (GET_BUFR (NewVarDig->CacheIndex), GET_BUFR
> (NewVarInfo.Buffer), VarSize);
> + InsertVariableDigestNode (Global, NewVarDig, NULL);
> + }
> + }
> +
> + if ( (UnprotectedVarIndex == IndexHmacAdded)
> + || (UnprotectedVarIndex == IndexHmacInDel))
> + {
> + //
> + // MetaDataHmacVar should be managed only by this library. It's not
> + // supposed to be updated by external users of variable service. The only
> + // exception is that deleting it (not really delete but refresh the HMAC
> + // value against RPMC+1) is allowed before WriteInit, as a way to always
> + // increment RPMC once in current boot before any variable updates.
> + //
> + if ((NewVarInfo.Buffer != NULL) || Global->Flags.WriteInit) {
> + return EFI_ACCESS_DENIED;
> + }
> + } else {
> + //
> + // Do encryption, if enabled.
> + //
> + if ((NewVarDig != NULL) && (NewVarInfo.Buffer != NULL)) {
> + Status = UpdateVariableInternal (Global, &NewVarInfo, NewVarDig);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> + }
> + }
> +
> + //
> + // Refresh MetaDataHmacVar.
> + //
> + Status = RefreshVariableMetadataHmac (Global, NULL, &NewHmacVarInfo);
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> +
> + //
> + // Return size for both MetaDataHmacVar and added/updated one.
> + //
> + VarSize = VARIABLE_SIZE (&NewHmacVarInfo);
> + *NewVariableSize = HEADER_ALIGN (VarSize);
> + if (NewVarInfo.Buffer != NULL) {
> + VarSize = VARIABLE_SIZE (&NewVarInfo);
> + VarSize = HEADER_ALIGN (VarSize);
> +
> + if (VarSize > GET_CNTX (Global)->MaxVariableSize) {
> + return EFI_BAD_BUFFER_SIZE;
> + }
> +
> + *NewVariableSize += VarSize;
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + 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] StoreIndex StoreIndex 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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + UNPROTECTED_VARIABLE_INDEX Index;
> + VARIABLE_DIGEST *VarDig;
> + VOID *Buffer;
> + UINTN VarSize;
> + UINTN NewVarSize;
> +
> + Status = GetProtectedVariableGlobal (&Global);
> + ASSERT_EFI_ERROR (Status);
> + ContextIn = GET_CNTX (Global);
> +
> + ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> + VarInfo.Buffer = NewVariable;
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> + Status = ContextIn->GetVariableInfo (&VarInfo);
> + ASSERT_EFI_ERROR (Status);
> +
> + Index = CheckKnownUnprotectedVariable (Global, &VarInfo);
> + if (Index < UnprotectedVarIndexMax) {
> + VarDig = VAR_DIG_PTR (Global->Unprotected[Index]);
> + } else {
> + VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> + }
> +
> + if (Index == IndexHmacAdded) {
> + //
> + // Advance the RPMC to let it match new MetaDataHmacVar.
> + //
> + Status = IncrementMonotonicCounter (RPMC_COUNTER_2);
> + ASSERT_EFI_ERROR (Status);
> +
> + if ((VarDig->StoreIndex != VAR_INDEX_INVALID) && (VarDig->State !=
> VAR_ADDED)) {
> + ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> + VarInfo.StoreIndex = VarDig->StoreIndex; // Still point to old copy
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> + //
> + // Delete variable completely.
> + //
> + Status = ContextIn->GetVariableInfo (&VarInfo);
> + ASSERT_EFI_ERROR (Status);
> +
> + if ( (VarInfo.Buffer->State == VAR_ADDED)
> + || (VarInfo.Buffer->State == (VAR_ADDED &
> VAR_IN_DELETED_TRANSITION)))
> + {
> + VarInfo.Buffer->State &= VAR_DELETED;
> + Status = ContextIn->UpdateVariableStore (
> + &VarInfo,
> + OFFSET_OF (VARIABLE_HEADER, State),
> + sizeof (VarInfo.Buffer->State),
> + &VarInfo.Buffer->State
> + );
> + if (EFI_ERROR (Status)) {
> + ASSERT_EFI_ERROR (Status);
> + return Status;
> + }
> + }
> + }
> + }
> +
> + VarDig->StoreIndex = StoreIndex;
> + VarDig->State = VAR_ADDED;
> +
> + ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> + VarInfo.Buffer = NULL;
> + VarInfo.StoreIndex = VarDig->StoreIndex;
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> + Status = ContextIn->GetVariableInfo (&VarInfo);
> +
> + //
> + // Check if cache pool need re-allocation due to variable size increase
> + //
> + VarSize = VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
> + VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> + VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> +
> + NewVarSize = VARIABLE_HEADER_SIZE (VarInfo.Flags.Auth);
> + NewVarSize += VarInfo.Header.NameSize + GET_PAD_SIZE
> (VarInfo.Header.NameSize);
> + NewVarSize += VarInfo.Header.DataSize + GET_PAD_SIZE
> (VarInfo.Header.DataSize);
> + NewVarSize = HEADER_ALIGN (NewVarSize);
> +
> + if (VarSize < NewVarSize) {
> + if (VarDig->Flags.Freeable == TRUE) {
> + FreePool (GET_BUFR (VarDig->CacheIndex));
> + }
> +
> + Buffer = AllocatePool (NewVarSize);
> + if (Buffer != NULL) {
> + VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> + } else {
> + ASSERT (FALSE);
> + return EFI_ABORTED;
> + }
> + }
> +
> + //
> + // Update cached copy.
> + //
> + CopyMem (GET_BUFR (VarDig->CacheIndex), NewVariable, NewVarSize);
> +
> + //
> + // Check if there is consecutive variable as part of the write or
> + // is it just the MetaDataHmacVar variable
> + //
> + if (NewVarSize < VariableSize) {
> + //
> + // Advance to consecutive Variable
> + //
> + NewVariable = GET_BUFR (GET_ADRS (NewVariable) + NewVarSize);
> +
> + //
> + // Update the StoreIndex of consecutive Variable
> + //
> + ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> + VarInfo.Buffer = NULL;
> + VarInfo.StoreIndex = VarDig->StoreIndex;
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> + Status = ContextIn->GetNextVariableInfo (&VarInfo);
> + StoreIndex = VarInfo.StoreIndex;
> +
> + //
> + // The new StoreIndex does not exist in the variable digest.
> + // It is yet to be updated.
> + // Therefore, find variable by Name & Guid instead.
> + //
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> +
> + //
> + // Check if cache pool need re-allocation due to variable size increase
> + //
> + VarSize = VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
> + VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> + VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> + VarSize = HEADER_ALIGN (VarSize);
> +
> + NewVarSize = VARIABLE_HEADER_SIZE (VarInfo.Flags.Auth);
> + NewVarSize += VarInfo.Header.NameSize + GET_PAD_SIZE
> (VarInfo.Header.NameSize);
> + NewVarSize += VarInfo.Header.DataSize + GET_PAD_SIZE
> (VarInfo.Header.DataSize);
> + NewVarSize = HEADER_ALIGN (NewVarSize);
> +
> + if (VarSize < NewVarSize) {
> + if (VarDig->Flags.Freeable == TRUE) {
> + FreePool (GET_BUFR (VarDig->CacheIndex));
> + }
> +
> + Buffer = AllocatePool (NewVarSize);
> + if (Buffer != NULL) {
> + VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> + } else {
> + ASSERT (FALSE);
> + return EFI_ABORTED;
> + }
> + }
> +
> + //
> + // Update cached copy.
> + //
> + CopyMem (GET_BUFR (VarDig->CacheIndex), NewVariable, NewVarSize);
> + VarDig->StoreIndex = StoreIndex;
> + }
> +
> + return Status;
> +}
> +
> +/**
> + Refresh variable information changed by variable service.
> +
> + @param[in] Variable Pointer to buffer of the updated variable.
> + @param[in] VariableSize Size of variable pointed by Variable.
> + @param[in] StoreIndex New index of the variable in store.
> + @param[in] 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
> + )
> +{
> + EFI_STATUS Status;
> + PROTECTED_VARIABLE_INFO VarInfo;
> + PROTECTED_VARIABLE_GLOBAL *Global;
> + VARIABLE_DIGEST *VarDig;
> +
> + (VOID)GetProtectedVariableGlobal (&Global);
> +
> + ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> + VarInfo.Buffer = Variable;
> + VarInfo.StoreIndex = VAR_INDEX_INVALID;
> + VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> + Status = GET_CNTX (Global)->GetVariableInfo (&VarInfo);
> + ASSERT_EFI_ERROR (Status);
> +
> + VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> + if (VarDig == NULL) {
> + ASSERT (VarDig != NULL);
> + return EFI_NOT_FOUND;
> + }
> +
> + if (StoreIndex != VAR_INDEX_INVALID) {
> + VarDig->StoreIndex = StoreIndex;
> + }
> +
> + if (RefreshData) {
> + if (VarDig->CacheIndex == VAR_INDEX_INVALID) {
> + VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)
> + AllocatePool (MAX_VARIABLE_SIZE);
> + }
> +
> + CopyMem (GET_BUFR (VarDig->CacheIndex), Variable, VariableSize);
> + }
> +
> + //
> + // Information should stay the same other than following ones.
> + //
> + VarDig->State = VarInfo.Header.State;
> + VarDig->DataSize = (UINT32)VarInfo.Header.DataSize;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + 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
> + )
> +{
> + INTN Result;
> +
> + Result = StrnCmp (
> + METADATA_HMAC_VARIABLE_NAME,
> + VariableName,
> + METADATA_HMAC_VARIABLE_NAME_SIZE
> + );
> +
> + if (Result == 0) {
> + return TRUE;
> + }
> +
> + return FALSE;
> +}
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
> new file mode 100644
> index 000000000000..4591d1cd59e5
> --- /dev/null
> +++
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
> @@ -0,0 +1,233 @@
> +/** @file
> + Implemention of ProtectedVariableLib for BootService/Runtime use cases.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Library/MemoryAllocationLib.h"
> +#include "Library/UefiBootServicesTableLib.h"
> +#include "Library/UefiRuntimeLib.h"
> +#include "ProtectedVariableInternal.h"
> +
> +EFI_EVENT mVaChangeEvent = NULL;
> +
> +PROTECTED_VARIABLE_CONTEXT_IN mRtVariableContextIn = {
> + PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
> + sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
> + 0,
> + FromRuntimeModule,
> + NULL,
> + NULL,
> + NULL,
> + NULL,
> + NULL
> +};
> +
> +PROTECTED_VARIABLE_GLOBAL mRtProtectedVariableGlobal = {
> + PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
> + sizeof (PROTECTED_VARIABLE_GLOBAL),
> + { 0 },
> + { 0 },
> + 0,
> + 0,
> + 0,
> + 0,
> + 0,
> + 0,
> + { 0, 0, 0 },
> + 0,
> + 0,
> + { 0, 0, 0, 0, 0, 0}
> +};
> +
> +/**
> +
> + Get global data structure used to process protected variable.
> +
> + @param[out] Global Pointer to global configuration data.
> +
> + @retval EFI_SUCCESS Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> + OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
> + )
> +{
> + if (Global != NULL) {
> + mRtProtectedVariableGlobal.ContextIn = GET_ADRS
> (&mRtVariableContextIn);
> + *Global = &mRtProtectedVariableGlobal;
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
> +
> + This is a notification function registered on
> EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
> + It convers pointer to new virtual address.
> +
> + @param[in] Event Event whose notification function is being invoked.
> + @param[in] Context Pointer to the notification function's context.
> +
> +**/
> +VOID
> +EFIAPI
> +VirtualAddressChangeEvent (
> + IN EFI_EVENT Event,
> + IN VOID *Context
> + )
> +{
> + if (mRtVariableContextIn.FindVariableSmm != NULL) {
> + EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn.FindVariableSmm);
> + }
> +
> + if (mRtVariableContextIn.GetVariableInfo != NULL) {
> + EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn.GetVariableInfo);
> + }
> +
> + if (mRtVariableContextIn.GetNextVariableInfo != NULL) {
> + EfiConvertPointer (0x0, (VOID
> **)&mRtVariableContextIn.GetNextVariableInfo);
> + }
> +
> + if (mRtVariableContextIn.UpdateVariableStore != NULL) {
> + EfiConvertPointer (0x0, (VOID
> **)&mRtVariableContextIn.UpdateVariableStore);
> + }
> +
> + EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn);
> + if (mRtProtectedVariableGlobal.VariableCache != 0) {
> + EfiConvertPointer (0x0, (VOID
> **)&mRtProtectedVariableGlobal.VariableCache);
> + }
> +
> + EfiConvertPointer (0x0, (VOID **)&mRtProtectedVariableGlobal);
> +}
> +
> +/**
> +
> + 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.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibInitialize (
> + IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
> + )
> +{
> + if ( (ContextIn == NULL)
> + || (ContextIn->StructVersion !=
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
> + || (ContextIn->FindVariableSmm == NULL)
> + || (ContextIn->GetVariableInfo == NULL))
> + {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + CopyMem (&mRtVariableContextIn, ContextIn, sizeof
> (mRtVariableContextIn));
> +
> + //
> + // Register the event to convert the pointer for runtime.
> + //
> + gBS->CreateEventEx (
> + EVT_NOTIFY_SIGNAL,
> + TPL_NOTIFY,
> + VirtualAddressChangeEvent,
> + NULL,
> + &gEfiEventVirtualAddressChangeGuid,
> + &mVaChangeEvent
> + );
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Prepare for variable update.
> +
> + Not supported in DXE phase.
> +
> + @retval EFI_UNSUPPORTED Updating variable is not supported.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteInit (
> + VOID
> + )
> +{
> + ASSERT (FALSE);
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> + Update a variable with protection provided by this library.
> +
> + Not supported in DXE phase.
> +
> + @param[in,out] CurrVariable Variable to be updated. It's NULL if
> + adding a new variable.
> + @param[in] CurrVariableInDel In-delete-transition copy of updating
> variable.
> + @param[in,out] NewVariable Buffer of new variable data or
> + Buffer of "MetaDataHmacVar" and new variable
> (encrypted).
> + @param[in,out] NewVariableSize Size of NewVariable or
> + Size of (encrypted) NewVariable and "MetaDataHmacVar".
> +
> + @retval EFI_UNSUPPORTED Not support updating variable.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibUpdate (
> + IN OUT VARIABLE_HEADER *CurrVariable,
> + IN VARIABLE_HEADER *CurrVariableInDel,
> + IN OUT VARIABLE_HEADER *NewVariable,
> + IN OUT UINTN *NewVariableSize
> + )
> +{
> + ASSERT (FALSE);
> + return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> + Finalize a variable updating after it's written to NV variable storage
> + successfully.
> +
> + (Not supported for BootService/Runtime use cases.)
> +
> + @param[in] NewVariable Buffer of new variables and
> MetaDataHmacVar.
> + @param[in] VariableSize Size of buffer pointed by NewVariable.
> + @param[in] StoreIndex StoreIndex to NV variable storage from where
> the new
> + variable and MetaDataHmacVar have been written.
> +
> + @retval EFI_UNSUPPORTED Not supported for BootService/Runtime use
> cases.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteFinal (
> + IN VARIABLE_HEADER *NewVariable,
> + IN UINTN VariableSize,
> + IN UINT64 StoreIndex
> + )
> +{
> + ASSERT (FALSE);
> + return EFI_UNSUPPORTED;
> +}
> --
> 2.35.1.windows.2
next prev parent reply other threads:[~2022-11-22 8:00 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-06 7:34 [PATCH v5 00/19] UEFI variable protection Judah Vang
2022-11-06 7:34 ` [PATCH v5 01/19] MdePkg: Add reference to new Ppi Guid Judah Vang
2022-11-06 7:34 ` [PATCH v5 02/19] MdeModulePkg: Update AUTH_VARIABLE_INFO struct Judah Vang
2022-11-06 7:34 ` [PATCH v5 03/19] MdeModulePkg: Add new ProtectedVariable GUIDs Judah Vang
2022-11-06 7:34 ` [PATCH v5 04/19] MdeModulePkg: Add new include files Judah Vang
2022-11-22 6:31 ` Wang, Jian J
2022-11-06 7:34 ` [PATCH v5 05/19] MdeModulePkg: Add new GUID for Variable Store Info Judah Vang
2022-11-06 7:34 ` [PATCH v5 06/19] MdeModulePkg: Add Null ProtectedVariable Library Judah Vang
2022-11-22 6:39 ` Wang, Jian J
2022-11-06 7:34 ` [PATCH v5 07/19] MdeModulePkg: Add new Variable functionality Judah Vang
2022-11-14 3:43 ` Wang, Jian J
[not found] ` <1727569A8ECB6F9D.19699@groups.io>
2022-11-14 4:27 ` [edk2-devel] " Wang, Jian J
2022-11-06 7:34 ` [PATCH v5 08/19] MdeModulePkg: Add support for Protected Variables Judah Vang
2022-11-14 7:14 ` Wang, Jian J
2022-11-14 17:19 ` Judah Vang
2022-11-15 8:49 ` [edk2-devel] " Sami Mujawar
2022-11-22 6:26 ` Wang, Jian J
[not found] ` <1729D430BF77E016.5511@groups.io>
2022-11-22 6:42 ` Wang, Jian J
2022-11-06 7:34 ` [PATCH v5 09/19] MdeModulePkg: Reference Null ProtectedVariableLib Judah Vang
2022-11-22 6:44 ` Wang, Jian J
2022-11-06 7:35 ` [PATCH v5 10/19] SecurityPkg: Add new GUIDs for Judah Vang
2022-11-06 7:35 ` [PATCH v5 11/19] SecurityPkg: Add new KeyService types and defines Judah Vang
2022-11-22 6:46 ` Wang, Jian J
2022-11-06 7:35 ` [PATCH v5 12/19] SecurityPkg: Add new variable types and functions Judah Vang
2022-11-06 7:35 ` [PATCH v5 13/19] SecurityPkg: Update RPMC APIs with index Judah Vang
2022-11-06 7:35 ` [PATCH v5 14/19] SecurityPkg: Fix GetVariableKey API Judah Vang
2022-11-06 7:35 ` [PATCH v5 15/19] SecurityPkg: Add null encryption variable libs Judah Vang
2022-11-22 6:55 ` Wang, Jian J
2022-11-06 7:35 ` [PATCH v5 16/19] SecurityPkg: Add VariableKey library function Judah Vang
2022-11-06 7:35 ` [PATCH v5 17/19] SecurityPkg: Add EncryptionVariable lib with AES Judah Vang
2022-11-22 7:15 ` Wang, Jian J
2022-11-06 7:35 ` [PATCH v5 18/19] SecurityPkg: Add Protected Variable Services Judah Vang
2022-11-22 7:59 ` Wang, Jian J [this message]
2022-11-06 7:35 ` [PATCH v5 19/19] SecurityPkg: Add references to new *.inf files Judah Vang
2022-11-22 8:05 ` Wang, Jian J
2022-12-09 8:03 ` [edk2-devel] [PATCH v5 00/19] UEFI variable protection Yao, Jiewen
[not found] ` <172F11512E3044E7.1612@groups.io>
2022-12-09 9:41 ` Yao, Jiewen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=MW4PR11MB6763CFA4ED41217F261D42E9B60D9@MW4PR11MB6763.namprd11.prod.outlook.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox