From: "Judah Vang" <judah.vang@intel.com>
To: devel@edk2.groups.io
Cc: Judah Vang <judah.vang@intel.com>,
Jian J Wang <jian.j.wang@intel.com>,
Jiewen Yao <jiewen.yao@intel.com>,
Nishant C Mistry <nishant.c.mistry@intel.com>
Subject: [PATCH v1 17/28] SecurityPkg: Add Protected Variable Services
Date: Fri, 25 Mar 2022 16:28:14 -0700 [thread overview]
Message-ID: <20220325232825.2167-13-judah.vang@intel.com> (raw)
In-Reply-To: <20220325232825.2167-1-judah.vang@intel.com>
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594
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.
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: 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 | 611 ++++++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c | 2095 ++++++++++++++++++++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c | 163 ++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c | 1331 +++++++++++++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c | 209 ++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon.c | 975 +++++++++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c | 233 +++
11 files changed, 5878 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.inf b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.inf
new file mode 100644
index 000000000000..011ccdce2db8
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.inf
@@ -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..fd38b5bd2019
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
@@ -0,0 +1,611 @@
+/** @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
+ );
+
+/**
+
+ 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.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetNextInternal (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ );
+
+/**
+
+ 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..ed1a8e1ec545
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
@@ -0,0 +1,2095 @@
+/** @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.
+ //
+ 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;
+ }
+
+ //
+ // 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.
+ //
+ Buffer = VarInfo->PlainData;
+ BufferSize = VarInfo->PlainDataSize;
+
+ Status = ProtectedVariableLibGetDataInternal (Global, VarInfo);
+ if (EFI_ERROR (Status)) {
+ //
+ // Return with caller provided buffer with zero DataSize
+ //
+ VarInfo->PlainData = Buffer;
+ VarInfo->PlainDataSize = 0;
+ return Status;
+ }
+
+ if ((Buffer == NULL) || ((BufferSize) < VarInfo->PlainDataSize)) {
+ //
+ // Return with caller provided buffer with true PlainDataSize
+ //
+ VarInfo->PlainData = Buffer;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Copy Plain data to ouput data buffer
+ //
+ 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;
+
+ 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);
+ if (!EFI_ERROR (Status)) {
+ if ((*DataSize) < VarInfo.PlainDataSize) {
+ *DataSize = VarInfo.PlainDataSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *DataSize = VarInfo.PlainDataSize;
+ 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.
+
+ @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;
+ }
+ }
+
+ //
+ // Decrypt the data, if necessary.
+ //
+ Status = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
+ if (!EFI_ERROR (Status)) {
+ if (*DataSize < VarInfo.PlainDataSize) {
+ *DataSize = VarInfo.PlainDataSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *DataSize = VarInfo.PlainDataSize;
+ 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..91d71a1e8525
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
@@ -0,0 +1,1331 @@
+/** @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);
+
+ VarSize = Global->VariableNumber * MAX_VARIABLE_SIZE;
+ Buffer = AllocateGlobalBuffer (VarSize, TRUE);
+
+ //
+ // It's safe to store the pointer in Global->VariableCache, which has been
+ // cleared by above calling of GetProtectedVariableGlobal().
+ //
+ Global->VariableCache = GET_ADRS (Buffer);
+ Global->VariableCacheSize = EFI_SIZE_TO_PAGES (VarSize);
+
+ //
+ // 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..92ba7cb12763
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon.c
@@ -0,0 +1,975 @@
+/** @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 {
+ //
+ // RPMC1 is going to be one step ahead of RPMC2 here, in order to
+ // refresh the MetaDataHmacVar in each boot before writing any other
+ // variables.
+ //
+ Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
+ ASSERT_EFI_ERROR (Status);
+
+ 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.26.2.windows.1
next prev parent reply other threads:[~2022-03-25 23:28 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-25 23:28 [PATCH v1 05/28] MdeModulePkg: Add new ProtectedVariable GUIDs Judah Vang
2022-03-25 23:28 ` [PATCH v1 06/28] MdeModulePkg: Add new include files Judah Vang
2022-03-25 23:28 ` [PATCH v1 07/28] MdeModulePkg: Add Null ProtectedVariable Library Judah Vang
2022-03-25 23:28 ` [PATCH v1 08/28] MdeModulePkg: Add new Variable functionality Judah Vang
2022-03-25 23:28 ` [PATCH v1 09/28] MdeModulePkg: Add support for Protected Variables Judah Vang
2022-03-25 23:28 ` [PATCH v1 10/28] SecurityPkg: Add new KeyService types and defines Judah Vang
2022-03-25 23:28 ` [PATCH v1 11/28] SecurityPkg: Update RPMC APIs with index Judah Vang
2022-03-25 23:28 ` [PATCH v1 12/28] SecurityPkg: Add new variable types and functions Judah Vang
2022-03-25 23:28 ` [PATCH v1 13/28] SecurityPkg: Fix GetVariableKey API Judah Vang
2022-03-25 23:28 ` [PATCH v1 14/28] SecurityPkg: Add null encryption variable libs Judah Vang
2022-03-25 23:28 ` [PATCH v1 15/28] SecurityPkg: Add VariableKey library function Judah Vang
2022-03-25 23:28 ` [PATCH v1 16/28] SecurityPkg: Add EncryptionVariable lib with AES Judah Vang
2022-03-25 23:28 ` Judah Vang [this message]
2022-03-25 23:28 ` [PATCH v1 18/28] MdeModulePkg: Reference Null ProtectedVariableLib Judah Vang
2022-03-25 23:28 ` [PATCH v1 19/28] SecurityPkg: Add references to new *.inf files Judah Vang
2022-03-25 23:28 ` [PATCH v1 20/28] ArmVirtPkg: Add reference to ProtectedVariableNull Judah Vang
2022-03-25 23:28 ` [PATCH v1 21/28] UefiPayloadPkg: Add ProtectedVariable reference Judah Vang
2022-03-28 1:31 ` Ni, Ray
2022-03-25 23:28 ` [PATCH v1 22/28] EmulatorPkg: " Judah Vang
2022-03-28 1:30 ` Ni, Ray
2022-03-25 23:28 ` [PATCH v1 23/28] OvmfPkg: " Judah Vang
2022-03-25 23:28 ` [PATCH v1 24/28] OvmfPkg: Add ProtectedVariableLib reference Judah Vang
2022-03-25 23:28 ` [PATCH v1 25/28] " Judah Vang
2022-03-25 23:28 ` [PATCH v1 26/28] " Judah Vang
2022-03-25 23:28 ` [PATCH v1 27/28] OvmfPkg: Add ProtectedVariable reference Judah Vang
2022-03-28 7:42 ` Boeuf, Sebastien
2022-03-25 23:28 ` [PATCH v1 28/28] CryptoPkg: Enable cypto HMAC KDF library Judah Vang
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=20220325232825.2167-13-judah.vang@intel.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