Date   

Event: Tools, CI, Code base construction meeting series - 08/08/2022 #cal-reminder

Group Notification <noreply@...>
 

Reminder: Tools, CI, Code base construction meeting series

When:
08/08/2022
4:30pm to 5:30pm
(UTC-07:00) America/Los Angeles

Where:
https://github.com/tianocore/edk2/discussions/2614

View Event

Description:

TianoCore community,

Microsoft and Intel will be hosting a series of open meetings to discuss build, CI, tools, and other related topics. If you are interested, have ideas/opinions please join us. These meetings will be Monday 4:30pm Pacific Time on Microsoft Teams.

MS Teams Link in following discussion: * https://github.com/tianocore/edk2/discussions/2614

Anyone is welcome to join.

MS Teams Browser Clients * https://docs.microsoft.com/en-us/microsoftteams/get-clients?tabs=Windows#browser-client


Re: [PATCH v3 17/28] SecurityPkg: Add Protected Variable Services

Wang, Jian J
 

Hi Judah,

Just one minor issue. In ProtectedVariableInternal.h, following prototype is never implemented.
ProtectedVariableLibGetNextInternal

The logic in this patch is very complicated. It's hard to find out any logic issue by reading code.
Please make sure that code has been passed enough tests. I can only give ack for it.

Acked-by: Jian J Wang <jian.j.wang@...>

-----Original Message-----
From: Vang, Judah <judah.vang@...>
Sent: Thursday, June 09, 2022 2:03 PM
To: devel@edk2.groups.io
Cc: Wang, Jian J <jian.j.wang@...>; Yao, Jiewen <jiewen.yao@...>;
Xu, Min M <min.m.xu@...>; Mistry, Nishant C
<nishant.c.mistry@...>
Subject: [PATCH v3 17/28] SecurityPkg: Add Protected Variable Services

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594

V3: Change placement of buffer used for confidentiality crypto
operation to fix an issue when enabling confidentiality. Remove
un-needed increment of monotonic counter.

V1: Add Protected Variable Services across the different UEFI phases.
Functions includes creating variable digest, performing integrity
check, initializing protected variables, updating protected
variables, and verifying the MetaDataHmacVar variable.
This module prevents UEFI variable tampering. It provides
variable integrity and confidentiality.

Cc: Jian J Wang <jian.j.wang@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Min Xu <min.m.xu@...>
Cc: Nishant C Mistry <nishant.c.mistry@...>
Signed-off-by: Jian J Wang <jian.j.wang@...>
Signed-off-by: Nishant C Mistry <nishant.c.mistry@...>
Signed-off-by: Judah Vang <judah.vang@...>
---
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 |
2103 ++++++++++++++++++++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c | 163
++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c | 1327
++++++++++++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c | 209
++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon.c
| 967 +++++++++
SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c |
233 +++
11 files changed, 5874 insertions(+)

diff --git
a/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
b/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
new file mode 100644
index 000000000000..74a0285af7ef
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
@@ -0,0 +1,64 @@
+## @file
+# Provides protected variable services for EmulatorPkg.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010029
+ BASE_NAME = DxeProtectedVariableLib
+ MODULE_UNI_FILE = ProtectedVariableLib.uni
+ FILE_GUID = 6F424E10-0F75-4716-9F97-58C2E1C643AD
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 0.1
+ LIBRARY_CLASS = ProtectedVariableLib|DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build
tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ ProtectedVariableDxe.c
+ ProtectedVariableCommon.c
+ ProtectedVariableSmmDxeCommon.c
+ ProtectedVariableInternal.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ HobLib
+ BaseCryptLib
+ EncryptionVariableLib
+ RpmcLib
+ HashApiLib
+ SortLib
+
+[Protocols]
+ gEfiVariableWriteArchProtocolGuid
+
+[Guids]
+ gEdkiiMetaDataHmacVariableGuid
+ gEdkiiProtectedVariableGlobalGuid
+ gEdkiiVarErrorFlagGuid
+ gEdkiiProtectedVariableContextGuid
+
+[Pcd]
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
diff --git a/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
b/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
new file mode 100644
index 000000000000..44c959a94ca3
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
@@ -0,0 +1,68 @@
+## @file
+# Provides protected variable services.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010029
+ BASE_NAME = PeiProtectedVariableLib
+ MODULE_UNI_FILE = ProtectedVariableLib.uni
+ FILE_GUID = 76FBFBCE-ACBB-4084-A348-8FCC97AAEB9D
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 0.1
+ LIBRARY_CLASS = ProtectedVariableLib|PEIM
+
+#
+# The following information is for reference only and not required by the build
tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 AARCH64
+#
+
+[Sources]
+ ProtectedVariablePei.c
+ ProtectedVariableCommon.c
+ ProtectedVariableInternal.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ HobLib
+ BaseCryptLib
+ RpmcLib
+ VariableKeyLib
+ EncryptionVariableLib
+ ReportStatusCodeLib
+ PeiServicesLib
+ HashApiLib
+ SortLib
+
+[Guids]
+ gEdkiiMetaDataHmacVariableGuid
+ gEdkiiProtectedVariableGlobalGuid
+ gEdkiiVarErrorFlagGuid
+ gEdkiiProtectedVariableContextGuid
+
+[Ppis]
+ gEfiPeiMemoryDiscoveredPpiGuid
+ gEfiPeiVariableStoreDiscoveredPpiGuid
+
+[Pcd]
+ gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeVariableIntegrity
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
diff --git
a/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
b/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
new file mode 100644
index 000000000000..ecf0b1a43d30
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
@@ -0,0 +1,67 @@
+## @file
+# Provides protected variable services.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010029
+ BASE_NAME = SmmProtectedVariableLib
+ MODULE_UNI_FILE = ProtectedVariableLib.uni
+ FILE_GUID = 2BEE71E5-259B-4057-A2C1-2115DF43C76A
+ MODULE_TYPE = DXE_SMM_DRIVER
+ VERSION_STRING = 0.1
+ LIBRARY_CLASS = ProtectedVariableLib|DXE_SMM_DRIVER
MM_STANDALONE
+
+#
+# The following information is for reference only and not required by the build
tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ ProtectedVariableSmm.c
+ ProtectedVariableCommon.c
+ ProtectedVariableSmmDxeCommon.c
+ ProtectedVariableInternal.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ HobLib
+ BaseCryptLib
+ EncryptionVariableLib
+ RpmcLib
+ VariableKeyLib
+ HashApiLib
+ SortLib
+
+[Protocols]
+ gEfiMmEndOfDxeProtocolGuid
+
+[Guids]
+ gSmmVariableWriteGuid
+ gEdkiiMetaDataHmacVariableGuid
+ gEdkiiProtectedVariableGlobalGuid
+ gEdkiiVarErrorFlagGuid
+ gEdkiiProtectedVariableContextGuid
+
+[Pcd]
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
+
diff --git
a/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
nf
b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
nf
new file mode 100644
index 000000000000..011ccdce2db8
--- /dev/null
+++
b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
nf
@@ -0,0 +1,62 @@
+## @file
+# Provides protected variable services.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010029
+ BASE_NAME = SmmRuntimeProtectedVariableLib
+ MODULE_UNI_FILE = ProtectedVariableLib.uni
+ FILE_GUID = 99A623DE-1AD3-4AB3-909D-E3AADD7845EF
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 0.1
+ LIBRARY_CLASS = ProtectedVariableLib|DXE_DRIVER
DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build
tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ ProtectedVariableSmmRuntime.c
+ ProtectedVariableCommon.c
+ ProtectedVariableInternal.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ HobLib
+ BaseCryptLib
+ EncryptionVariableLib
+ RpmcLib
+ HashApiLib
+ SortLib
+
+[Guids]
+ gEfiEventVirtualAddressChangeGuid
+ gEdkiiMetaDataHmacVariableGuid
+ gEdkiiProtectedVariableGlobalGuid
+ gEdkiiVarErrorFlagGuid
+ gEdkiiProtectedVariableContextGuid
+
+[Pcd]
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
+ gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
+ gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
+
diff --git
a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
new file mode 100644
index 000000000000..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..456c871a4561
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
@@ -0,0 +1,2103 @@
+/** @file
+ The common protected variable operation routines.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <PiPei.h>
+
+#include <Guid/VariableFormat.h>
+#include <Guid/VarErrorFlag.h>
+
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/HashApiLib.h>
+#include <Library/SortLib.h>
+
+#include "ProtectedVariableInternal.h"
+
+EFI_TIME mDefaultTimeStamp = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+VARIABLE_IDENTIFIER mUnprotectedVariables[] = {
+ {
+ METADATA_HMAC_VARIABLE_NAME,
+ &METADATA_HMAC_VARIABLE_GUID,
+ VAR_ADDED & VAR_IN_DELETED_TRANSITION
+ },
+ {
+ METADATA_HMAC_VARIABLE_NAME,
+ &METADATA_HMAC_VARIABLE_GUID,
+ VAR_ADDED
+ },
+ {
+ VAR_ERROR_FLAG_NAME,
+ &gEdkiiVarErrorFlagGuid,
+ VAR_ADDED
+ },
+ {
+ (CHAR16 *)PcdGetPtr (PcdPlatformVariableName),
+ (EFI_GUID *)PcdGetPtr (PcdPlatformVariableGuid),
+ VAR_ADDED
+ }
+};
+
+/**
+ Print variable information.
+
+ @param[in] Data8 Pointer to data.
+ @param[in] DataSize Size of data.
+
+**/
+VOID
+PrintVariableData (
+ IN UINT8 *Data8,
+ IN UINTN DataSize
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < DataSize; Index++) {
+ if (Index % 0x10 == 0) {
+ DEBUG ((DEBUG_INFO, "\n%08X:", Index));
+ }
+
+ DEBUG ((DEBUG_INFO, " %02X", *Data8++));
+ }
+
+ DEBUG ((DEBUG_INFO, "\n"));
+}
+
+/**
+
+ Retrieve the context and global configuration data structure from HOB.
+
+ Once protected NV variable storage is cached and verified in PEI phase,
+ all related information are stored in a HOB which can be used by PEI variable
+ service itself and passed to SMM along with the boot flow, which can avoid
+ many duplicate works, like generating HMAC key, verifying NV variable
storage,
+ etc.
+
+ The HOB can be identified by gEdkiiProtectedVariableGlobalGuid.
+
+ @param[out] Global Pointer to global configuration data from PEI phase.
+
+ @retval EFI_SUCCESS The HOB was found, and Context and Global are
retrieved.
+ @retval EFI_NOT_FOUND The HOB was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProtectedVariableGlobalFromHob (
+ OUT PROTECTED_VARIABLE_GLOBAL **Global
+ )
+{
+ VOID *Data;
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ EFI_PHYSICAL_ADDRESS OldStart;
+ VARIABLE_DIGEST *VarDig;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ UINTN Index;
+
+ Hob.Raw = GetFirstGuidHob (&gEdkiiProtectedVariableGlobalGuid);
+ if (Hob.Raw != NULL) {
+ Data = GET_GUID_HOB_DATA (Hob);
+ } else {
+ //
+ // Search the global from allocated memory blob.
+ //
+ Data = NULL;
+ MemoryAllocationHob = NULL;
+
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
+ while (Hob.Raw != NULL) {
+ MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw;
+ if (CompareGuid (
+ &MemoryAllocationHob->AllocDescriptor.Name,
+ &gEdkiiProtectedVariableGlobalGuid
+ ))
+ {
+ Data = (VOID *)(UINTN)
+ MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress;
+ break;
+ }
+
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
Hob.Raw);
+ }
+ }
+
+ if (Data == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (Global != NULL) {
+ GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
+ if (GuidHob != NULL) {
+ ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
(GuidHob);
+ } else {
+ ASSERT (GuidHob == NULL);
+ }
+
+ *Global = (PROTECTED_VARIABLE_GLOBAL *)((UINT8 *)Data);
+ //
+ // Fix pointers in the HOB (due to physical memory readiness)
+ //
+ if ((*Global)->GlobalSelf != (EFI_PHYSICAL_ADDRESS)(UINTN)(*Global)) {
+ OldStart = (*Global)->GlobalSelf;
+ (*Global)->ContextIn = GET_ADRS (ContextIn);
+
+ //
+ // Mark Memory caching is available
+ //
+ (*Global)->Flags.CacheReady = TRUE;
+
+ //
+ // Re-allocate new minimum cache
+ //
+ (*Global)->VariableCache = GET_ADRS (Data)
+ + ((*Global)->VariableCache - OldStart);
+
+ (*Global)->DigestContext = GET_ADRS (((*Global) + 1));
+ for (Index = 0; Index < UnprotectedVarIndexMax; Index++) {
+ if ((*Global)->Unprotected[Index] != VAR_INDEX_INVALID) {
+ (*Global)->Unprotected[Index] = GET_ADRS (Data)
+ + ((*Global)->Unprotected[Index] - OldStart);
+ }
+ }
+
+ (*Global)->LastAccessedVariable = GET_ADRS (Data)
+ + ((*Global)->LastAccessedVariable - OldStart);
+
+ //
+ // Fix all linked-list pointers inside VARIABLE_SIGNATURE.
+ //
+ (*Global)->VariableDigests = GET_ADRS (Data)
+ + ((*Global)->VariableDigests - OldStart);
+ VarDig = VAR_DIG_PTR ((*Global)->VariableDigests);
+ while (VarDig != NULL) {
+ if (VarDig->Prev != 0) {
+ VarDig->Prev = GET_ADRS (Data) + (VarDig->Prev - OldStart);
+ }
+
+ if (VarDig->Next != 0) {
+ VarDig->Next = GET_ADRS (Data) + (VarDig->Next - OldStart);
+ }
+
+ VarDig = VAR_DIG_NEXT (VarDig);
+ }
+
+ (*Global)->GlobalSelf = (EFI_PHYSICAL_ADDRESS)(UINTN)(*Global);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Derive HMAC key from given variable root key.
+
+ @param[in] RootKey Pointer to root key to derive from.
+ @param[in] RootKeySize Size of root key.
+ @param[out] HmacKey Pointer to generated HMAC key.
+ @param[in] HmacKeySize Size of HMAC key.
+
+ @retval TRUE The HMAC key is derived successfully.
+ @retval FALSE Failed to generate HMAC key from given root key.
+
+**/
+BOOLEAN
+EFIAPI
+GenerateMetaDataHmacKey (
+ IN CONST UINT8 *RootKey,
+ IN UINTN RootKeySize,
+ OUT UINT8 *HmacKey,
+ IN UINTN HmacKeySize
+ )
+{
+ UINT8 Salt[AES_BLOCK_SIZE];
+
+ return HkdfSha256ExtractAndExpand (
+ RootKey,
+ RootKeySize,
+ Salt,
+ 0,
+ (UINT8 *)METADATA_HMAC_KEY_NAME,
+ METADATA_HMAC_KEY_NAME_SIZE,
+ HmacKey,
+ HmacKeySize
+ );
+}
+
+/**
+
+ Return the size of variable MetaDataHmacVar.
+
+ @param[in] AuthFlag Auth-variable indicator.
+
+ @retval size of variable MetaDataHmacVar.
+
+**/
+UINTN
+GetMetaDataHmacVarSize (
+ IN BOOLEAN AuthFlag
+ )
+{
+ UINTN Size;
+
+ if (AuthFlag) {
+ Size = sizeof (AUTHENTICATED_VARIABLE_HEADER);
+ } else {
+ Size = sizeof (VARIABLE_HEADER);
+ }
+
+ Size += METADATA_HMAC_VARIABLE_NAME_SIZE;
+ Size += GET_PAD_SIZE (Size);
+ Size += METADATA_HMAC_SIZE;
+ Size += GET_PAD_SIZE (Size);
+
+ return Size;
+}
+
+/**
+
+ Digests the given variable data and updates HMAC context.
+
+ @param[in] Context Pointer to initialized HMAC context.
+ @param[in] VarInfo Pointer to variable data.
+ @param[in] UpdateMethod Function to run when updating variable digest.
+
+ @retval TRUE HMAC context was updated successfully.
+ @retval FALSE Failed to update HMAC context.
+
+**/
+STATIC
+BOOLEAN
+UpdateVariableDigestData (
+ IN VOID *Context,
+ IN PROTECTED_VARIABLE_INFO *VarInfo,
+ IN DIGEST_UPDATE UpdateMethod
+ )
+{
+ VOID *Buffer[12];
+ UINT32 BufferSize[12];
+ UINTN Index;
+ BOOLEAN Status;
+
+ //
+ // Empty variable is legal here (e.g. variable deletion case or write-init case).
+ //
+ if ((VarInfo == NULL) ||
+ (VarInfo->CipherData == NULL) ||
+ (VarInfo->CipherDataSize == 0))
+ {
+ return TRUE;
+ }
+
+ //
+ // HMAC (":" || VariableName)
+ //
+ Buffer[0] = METADATA_HMAC_SEP;
+ BufferSize[0] = METADATA_HMAC_SEP_SIZE;
+
+ Buffer[1] = VarInfo->Header.VariableName;
+ BufferSize[1] = (UINT32)VarInfo->Header.NameSize;
+
+ //
+ // HMAC (":" || VendorGuid || Attributes || DataSize)
+ //
+ Buffer[2] = METADATA_HMAC_SEP;
+ BufferSize[2] = METADATA_HMAC_SEP_SIZE;
+
+ Buffer[3] = VarInfo->Header.VendorGuid;
+ BufferSize[3] = sizeof (EFI_GUID);
+
+ Buffer[4] = &VarInfo->Header.Attributes;
+ BufferSize[4] = sizeof (VarInfo->Header.Attributes);
+
+ Buffer[5] = &VarInfo->CipherDataSize;
+ BufferSize[5] = sizeof (VarInfo->CipherDataSize);
+
+ //
+ // HMAC (":" || CipherData)
+ //
+ Buffer[6] = METADATA_HMAC_SEP;
+ BufferSize[6] = METADATA_HMAC_SEP_SIZE;
+
+ Buffer[7] = VarInfo->CipherData;
+ BufferSize[7] = VarInfo->CipherDataSize;
+
+ //
+ // HMAC (":" || PubKeyIndex || AuthMonotonicCount || TimeStamp)
+ //
+ Buffer[8] = METADATA_HMAC_SEP;
+ BufferSize[8] = METADATA_HMAC_SEP_SIZE;
+
+ Buffer[9] = &VarInfo->Header.PubKeyIndex;
+ BufferSize[9] = sizeof (VarInfo->Header.PubKeyIndex);
+
+ Buffer[10] = &VarInfo->Header.MonotonicCount;
+ BufferSize[10] = sizeof (VarInfo->Header.MonotonicCount);
+
+ Buffer[11] = (VarInfo->Header.TimeStamp != NULL) ?
+ VarInfo->Header.TimeStamp : &mDefaultTimeStamp;
+ BufferSize[11] = sizeof (EFI_TIME);
+
+ for (Index = 0; Index < ARRAY_SIZE (Buffer); ++Index) {
+ Status = UpdateMethod (Context, Buffer[Index], BufferSize[Index]);
+ if (!Status) {
+ ASSERT (FALSE);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+
+ Digests the given variable data and updates HMAC context.
+
+ @param[in] Context Pointer to initialized HMAC context.
+ @param[in] VarInfo Pointer to variable data.
+
+ @retval TRUE HMAC context was updated successfully.
+ @retval FALSE Failed to update HMAC context.
+
+**/
+BOOLEAN
+UpdateVariableMetadataHmac (
+ IN VOID *Context,
+ IN PROTECTED_VARIABLE_INFO *VarInfo
+ )
+{
+ return UpdateVariableDigestData (Context, VarInfo,
(DIGEST_UPDATE)HmacSha256Update);
+}
+
+/**
+
+ Get the variable digest.
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in] VarInfo Pointer to verified copy of protected variables.
+ @param[in,out] DigestValue Pointer to variable digest value.
+
+ @retval EFI_INVALID_PARAMETER Invalid parameter was passed in.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to calculate hash.
+ @retval EFI_ABORTED An error was encountered.
+ @retval EFI_COMPROMISED_DATA The data was compromised.
+ @retval EFI_SUCCESS Variable digest was successfully verified.
+
+**/
+EFI_STATUS
+GetVariableDigest (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN PROTECTED_VARIABLE_INFO *VarInfo,
+ IN OUT UINT8 *DigestValue
+ )
+{
+ EFI_STATUS Status;
+ VOID *Context;
+
+ if ((Global == NULL) || (VarInfo == NULL) || (DigestValue == NULL)) {
+ ASSERT (Global != NULL);
+ ASSERT (VarInfo != NULL);
+ ASSERT (DigestValue != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Context = GET_BUFR (Global->DigestContext);
+ if (!HashApiInit (Context)) {
+ ASSERT (Context != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (VarInfo->CipherData == NULL) {
+ VarInfo->CipherData = VarInfo->Header.Data;
+ VarInfo->CipherDataSize = (UINT32)VarInfo->Header.DataSize;
+ }
+
+ if ( !UpdateVariableDigestData (Context, VarInfo,
(DIGEST_UPDATE)HashApiUpdate)
+ || !HashApiFinal (Context, DigestValue))
+ {
+ ASSERT (FALSE);
+ Status = EFI_ABORTED;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+
+ Verify the variable digest.
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in] VarInfo Pointer to verified copy of protected variables.
+ @param[in] VarDig Pointer to variable digest data.
+
+ @retval EFI_INVALID_PARAMETER Invalid parameter was passed in.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to calculate hash.
+ @retval EFI_ABORTED An error was encountered.
+ @retval EFI_COMPROMISED_DATA The data was compromised.
+ @retval EFI_SUCCESS Variable digest was successfully verified.
+
+**/
+EFI_STATUS
+VerifyVariableDigest (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN PROTECTED_VARIABLE_INFO *VarInfo,
+ IN VARIABLE_DIGEST *VarDig
+ )
+{
+ EFI_STATUS Status;
+ UINT8 NewDigest[METADATA_HMAC_SIZE];
+
+ if (Global->Flags.RecoveryMode || !VarDig->Flags.Protected) {
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (VarDig->DigestSize == sizeof (NewDigest));
+
+ Status = GetVariableDigest (Global, VarInfo, NewDigest);
+ if (!EFI_ERROR (Status)) {
+ if (CompareMem (VAR_DIG_VALUE (VarDig), NewDigest, VarDig-
DigestSize) != 0) {
+ Status = EFI_COMPROMISED_DATA;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Initialize variable MetaDataHmacVar.
+
+ @param[in,out] Variable Pointer to buffer of MetaDataHmacVar.
+ @param[in] AuthFlag Variable format flag.
+
+**/
+VOID
+InitMetadataHmacVariable (
+ IN OUT VARIABLE_HEADER *Variable,
+ IN BOOLEAN AuthFlag
+ )
+{
+ UINT8 *NamePtr;
+ AUTHENTICATED_VARIABLE_HEADER *AuthVariable;
+
+ Variable->StartId = VARIABLE_DATA;
+ Variable->State = VAR_ADDED;
+ Variable->Reserved = 0;
+ Variable->Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT;
+
+ if (AuthFlag) {
+ AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable;
+
+ AuthVariable->NameSize = METADATA_HMAC_VARIABLE_NAME_SIZE;
+ AuthVariable->DataSize = METADATA_HMAC_SIZE;
+ AuthVariable->PubKeyIndex = 0;
+ AuthVariable->MonotonicCount = 0;
+
+ ZeroMem (&AuthVariable->TimeStamp, sizeof (EFI_TIME));
+ CopyMem (&AuthVariable->VendorGuid,
&METADATA_HMAC_VARIABLE_GUID, sizeof (EFI_GUID));
+
+ NamePtr = (UINT8 *)AuthVariable + sizeof
(AUTHENTICATED_VARIABLE_HEADER);
+ } else {
+ Variable->NameSize = METADATA_HMAC_VARIABLE_NAME_SIZE;
+ Variable->DataSize = METADATA_HMAC_SIZE;
+
+ CopyMem (&Variable->VendorGuid, &METADATA_HMAC_VARIABLE_GUID,
sizeof (EFI_GUID));
+
+ NamePtr = (UINT8 *)Variable + sizeof (VARIABLE_HEADER);
+ }
+
+ CopyMem (NamePtr, METADATA_HMAC_VARIABLE_NAME,
METADATA_HMAC_VARIABLE_NAME_SIZE);
+}
+
+/**
+ Re-calculate HMAC based on new variable data and re-generate
MetaDataHmacVar.
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in] NewVarInfo Pointer to buffer of new variable data.
+ @param[in,out] NewHmacVarInfo Pointer to buffer of new
MetaDataHmacVar.
+
+ @return EFI_SUCCESS The HMAC value was updated successfully.
+ @return EFI_ABORTED Failed to calculate the HMAC value.
+ @return EFI_OUT_OF_RESOURCES Not enough resource to calculate HMC
value.
+ @return EFI_NOT_FOUND The MetaDataHmacVar was not found in
storage.
+
+**/
+EFI_STATUS
+RefreshVariableMetadataHmac (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN PROTECTED_VARIABLE_INFO *NewVarInfo,
+ IN OUT PROTECTED_VARIABLE_INFO *NewHmacVarInfo
+ )
+{
+ EFI_STATUS Status;
+ VOID *Context;
+ UINT32 Counter;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ PROTECTED_VARIABLE_INFO CurrHmacVarInfo;
+ UINT8 *HmacValue;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ VARIABLE_DIGEST *VarDig;
+ VARIABLE_DIGEST *HmacVarDig;
+
+ ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
+ ZeroMem ((VOID *)&CurrHmacVarInfo, sizeof (CurrHmacVarInfo));
+
+ Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ Counter += 1;
+ ContextIn = GET_CNTX (Global);
+
+ //
+ // Delete current MetaDataHmacVariable first, if any.
+ //
+ if (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID) {
+ HmacVarDig = VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded]);
+
+ CurrHmacVarInfo.Header.NameSize = HmacVarDig->NameSize;
+ CurrHmacVarInfo.Header.VariableName = VAR_DIG_NAME (HmacVarDig);
+ CurrHmacVarInfo.Header.VendorGuid = VAR_DIG_GUID (HmacVarDig);
+
+ CurrHmacVarInfo.Buffer = VAR_HDR_PTR (HmacVarDig->CacheIndex);
+ CurrHmacVarInfo.StoreIndex = HmacVarDig->StoreIndex;
+ CurrHmacVarInfo.Flags.Auth = HmacVarDig->Flags.Auth;
+ //
+ // Force marking current MetaDataHmacVariable as
VAR_IN_DELETED_TRANSITION.
+ //
+ CurrHmacVarInfo.Buffer->State &= VAR_IN_DELETED_TRANSITION;
+ HmacVarDig->State &= VAR_IN_DELETED_TRANSITION;
+ Status = ContextIn->UpdateVariableStore (
+ &CurrHmacVarInfo,
+ OFFSET_OF (VARIABLE_HEADER, State),
+ sizeof (CurrHmacVarInfo.Buffer->State),
+ &CurrHmacVarInfo.Buffer->State
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ } else if (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID) {
+ HmacVarDig = VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel]);
+ } else {
+ //
+ // No MetaDataHmacVar. Allocate space to cache its value.
+ //
+ HmacVarDig = CreateVariableDigestNode (
+ METADATA_HMAC_VARIABLE_NAME,
+ &METADATA_HMAC_VARIABLE_GUID,
+ METADATA_HMAC_VARIABLE_NAME_SIZE,
+ METADATA_HMAC_SIZE,
+ Global->Flags.Auth,
+ Global
+ );
+ if (HmacVarDig == NULL) {
+ ASSERT (HmacVarDig != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ HmacVarDig->Flags.Protected = FALSE;
+ }
+
+ if (HmacVarDig->CacheIndex == VAR_INDEX_INVALID) {
+ HmacVarDig->CacheIndex = (GET_ADRS (Global)) + (Global->StructSize -
GetMetaDataHmacVarSize (Global->Flags.Auth));
+ }
+
+ //
+ // Construct new MetaDataHmacVar.
+ //
+ if (NewHmacVarInfo == NULL) {
+ NewHmacVarInfo = &VarInfo;
+ NewHmacVarInfo->Buffer = GET_BUFR (HmacVarDig->CacheIndex);
+ }
+
+ InitMetadataHmacVariable (NewHmacVarInfo->Buffer, Global->Flags.Auth);
+
+ NewHmacVarInfo->StoreIndex = VAR_INDEX_INVALID; // Skip calculating
offset
+ NewHmacVarInfo->Flags.Auth = Global->Flags.Auth;
+ Status = ContextIn->GetVariableInfo (NewHmacVarInfo);
+ ASSERT_EFI_ERROR (Status);
+ HmacValue = NewHmacVarInfo->Header.Data;
+
+ //
+ // Re-calculate HMAC for all valid variables
+ //
+ Context = HmacSha256New ();
+ if (Context == NULL) {
+ ASSERT (Context != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_ABORTED;
+ if (!HmacSha256SetKey (
+ Context,
+ Global->MetaDataHmacKey,
+ sizeof (Global->MetaDataHmacKey)
+ ))
+ {
+ ASSERT (FALSE);
+ goto Done;
+ }
+
+ //
+ // HMAC (|| hash(Var1) || hash(Var2) || ... || hash(VarN))
+ //
+ VarDig = VAR_DIG_PTR (Global->VariableDigests);
+ while (VarDig != NULL) {
+ if (VarDig->Flags.Valid && VarDig->Flags.Protected) {
+ HmacSha256Update (Context, VAR_DIG_VALUE (VarDig), VarDig-
DigestSize);
+ }
+
+ VarDig = VAR_DIG_NEXT (VarDig);
+ }
+
+ //
+ // HMAC (RpmcMonotonicCounter)
+ //
+ if (!HmacSha256Update (Context, &Counter, sizeof (Counter))) {
+ ASSERT (FALSE);
+ goto Done;
+ }
+
+ if (!HmacSha256Final (Context, HmacValue)) {
+ ASSERT (FALSE);
+ goto Done;
+ }
+
+ //
+ // Update HMAC value in cache.
+ //
+ CopyMem (VAR_DIG_VALUE (HmacVarDig), HmacValue, HmacVarDig-
DataSize);
+ if ((HmacVarDig->Prev == 0) && (HmacVarDig->Next == 0)) {
+ InsertVariableDigestNode (Global, HmacVarDig, NULL);
+ }
+
+ //
+ // Just one MetaDataHmacVar is needed for normal operation.
+ //
+ Global->Unprotected[IndexHmacAdded] = VAR_DIG_ADR (HmacVarDig);
+ Global->Unprotected[IndexHmacInDel] = VAR_INDEX_INVALID;
+
+ Status = EFI_SUCCESS;
+
+Done:
+ if (Context != NULL) {
+ HmacSha256Free (Context);
+ }
+
+ return Status;
+}
+
+/**
+
+ Check if a given variable is unprotected variable specified in advance
+ and return its index ID.
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in] VarInfo Pointer to variable information data.
+
+ @retval IndexHmacInDel Variable is MetaDataHmacVar in delete-transition
state.
+ @retval IndexHmacAdded Variable is MetaDataHmacVar in valid state.
+ @retval IndexErrorFlag Variable is VarErrorLog.
+ @retval Others Variable is not any known unprotected variables.
+
+**/
+UNPROTECTED_VARIABLE_INDEX
+CheckKnownUnprotectedVariable (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN PROTECTED_VARIABLE_INFO *VarInfo
+ )
+{
+ UNPROTECTED_VARIABLE_INDEX Index;
+
+ if ((VarInfo == NULL) || ( (VarInfo->StoreIndex == VAR_INDEX_INVALID)
+ && ( (VarInfo->Header.VariableName == NULL)
+ || (VarInfo->Header.VendorGuid == NULL))))
+ {
+ ASSERT (VarInfo != NULL);
+ ASSERT (VarInfo->StoreIndex != VAR_INDEX_INVALID);
+ ASSERT (VarInfo->Header.VariableName != NULL);
+ ASSERT (VarInfo->Header.VendorGuid != NULL);
+ return UnprotectedVarIndexMax;
+ }
+
+ for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
+ if ( (Global->Unprotected[Index] != VAR_INDEX_INVALID)
+ && (VarInfo->StoreIndex != VAR_INDEX_INVALID))
+ {
+ if (VarInfo->StoreIndex == VAR_DIG_PTR (Global->Unprotected[Index])-
StoreIndex) {
+ break;
+ }
+ } else if (IS_VARIABLE (
+ &VarInfo->Header,
+ mUnprotectedVariables[Index].VariableName,
+ mUnprotectedVariables[Index].VendorGuid
+ ) && (VarInfo->Header.State == mUnprotectedVariables[Index].State))
+ {
+ break;
+ }
+ }
+
+ return Index;
+}
+
+/**
+
+ Compare variable name and Guid
+
+ @param[in] Name1 Name of first variable.
+ @param[in] Name1Size Size of first variable.
+ @param[in] Name2 Name of second variable.
+ @param[in] Name2Size Size of second variable.
+ @param[in] Guid1 Guid for first variable.
+ @param[in] Guid2 Guid for second variable.
+
+ @retval 0 First name is identical to Second name.
+ @return others First name is not identical to Second name.
+
+**/
+INTN
+CompareVariableNameAndGuid (
+ IN CONST CHAR16 *Name1,
+ IN UINTN Name1Size,
+ IN CONST CHAR16 *Name2,
+ IN UINTN Name2Size,
+ IN EFI_GUID *Guid1,
+ IN EFI_GUID *Guid2
+ )
+{
+ INTN Result;
+
+ Result = StrnCmp (
+ Name1,
+ Name2,
+ MIN (Name1Size, Name2Size) / sizeof (CHAR16)
+ );
+ if (Result == 0) {
+ if (Name1Size != Name2Size) {
+ //
+ // Longer name is 'bigger' than shorter one.
+ //
+ Result = (INTN)Name1Size - (INTN)Name2Size;
+ } else {
+ //
+ // The variable name is the same. Compare the GUID.
+ //
+ Result = CompareMem ((VOID *)Guid1, (VOID *)Guid2, sizeof (EFI_GUID));
+ }
+ }
+
+ return Result;
+}
+
+/**
+
+ Compare variable digest.
+
+ @param[in] Variable1 Pointer to first variable digest.
+ @param[in] Variable2 Pointer to second variable digest.
+
+ @retval 0 Variables are identical.
+ @return others Variables are not identical.
+
+**/
+INTN
+CompareVariableDigestInfo (
+ IN VARIABLE_DIGEST *Variable1,
+ IN VARIABLE_DIGEST *Variable2
+ )
+{
+ return CompareVariableNameAndGuid (
+ VAR_DIG_NAME (Variable1),
+ Variable1->NameSize,
+ VAR_DIG_NAME (Variable2),
+ Variable2->NameSize,
+ &Variable1->VendorGuid,
+ &Variable2->VendorGuid
+ );
+}
+
+/**
+
+ Move a node backward in the order controlled by SortMethod.
+
+ @param[in,out] Node Pointer to node to be moved.
+ @param[in] SortMethod Method used to compare node in list.
+
+**/
+VOID
+MoveNodeBackward (
+ IN OUT VARIABLE_DIGEST *Node,
+ IN SORT_METHOD SortMethod
+ )
+{
+ VARIABLE_DIGEST *Curr;
+ VARIABLE_DIGEST *Prev;
+ INTN Result;
+
+ Curr = Node;
+ while (Curr != NULL) {
+ Prev = VAR_DIG_PREV (Curr);
+ if (Prev == NULL) {
+ Result = -1;
+ } else {
+ Result = SortMethod (Prev, Node);
+ }
+
+ //
+ // 'Result > 0' means the 'Prev' is 'bigger' than 'Node'. Continue to check
+ // previous node til a node 'smaller' than 'Node' found.
+ //
+ if (Result > 0) {
+ Curr = Prev;
+ continue;
+ }
+
+ if (Curr != Node) {
+ //
+ // Remove Node first
+ //
+ if (VAR_DIG_PREV (Node) != NULL) {
+ VAR_DIG_PREV (Node)->Next = Node->Next;
+ }
+
+ if (VAR_DIG_NEXT (Node) != NULL) {
+ VAR_DIG_NEXT (Node)->Prev = Node->Prev;
+ }
+
+ //
+ // Insert Node before Curr.
+ //
+ Node->Prev = Curr->Prev;
+ Node->Next = VAR_DIG_ADR (Curr);
+
+ if (Curr->Prev != 0) {
+ VAR_DIG_PREV (Curr)->Next = VAR_DIG_ADR (Node);
+ }
+
+ Curr->Prev = VAR_DIG_ADR (Node);
+ }
+
+ //
+ // If there're two identical variables in storage, one of them must be
+ // "in-delete-transition" state. Mark it as "deleted" anyway.
+ //
+ if (Result == 0) {
+ if (Curr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
+ Curr->State &= VAR_DELETED;
+ }
+
+ if (Prev->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
+ Prev->State &= VAR_DELETED;
+ }
+ }
+
+ break;
+ }
+}
+
+/**
+
+ Create variable digest node.
+
+ @param[in] VariableName Name of variable.
+ @param[in] VendorGuid Guid of variable.
+ @param[in] NameSize Size of variable name.
+ @param[in] DataSize Size of variable data.
+ @param[in] AuthVar Authenticated variable flag.
+ @param[in] Global Pointer to global configuration data.
+
+ @retval Ptr Pointer to variable digest
+
+**/
+VARIABLE_DIGEST *
+CreateVariableDigestNode (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT16 NameSize,
+ IN UINT32 DataSize,
+ IN BOOLEAN AuthVar,
+ IN PROTECTED_VARIABLE_GLOBAL *Global
+ )
+{
+ VARIABLE_DIGEST *VarDig;
+ VOID *Buffer;
+ UINTN VarSize;
+
+ VarDig = (VARIABLE_DIGEST *)AllocateZeroPool (
+ sizeof (VARIABLE_DIGEST) + NameSize +
METADATA_HMAC_SIZE
+ );
+ if ((VarDig == NULL) || (Global == NULL)) {
+ ASSERT (VarDig != NULL);
+ ASSERT (Global != NULL);
+ return NULL;
+ }
+
+ VarDig->DataSize = DataSize;
+ VarDig->NameSize = NameSize;
+ VarDig->DigestSize = METADATA_HMAC_SIZE;
+ VarDig->State = VAR_ADDED;
+ VarDig->Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT;
+ VarDig->Flags.Auth = AuthVar;
+ VarDig->Flags.Valid = TRUE;
+ VarDig->Flags.Freeable = TRUE;
+ VarDig->Flags.Protected = PcdGetBool (PcdProtectedVariableIntegrity);
+ VarDig->Flags.Encrypted = PcdGetBool (PcdProtectedVariableConfidentiality);
+ VarDig->StoreIndex = VAR_INDEX_INVALID;
+ VarDig->CacheIndex = VAR_INDEX_INVALID;
+
+ if (Global->Flags.CacheReady == TRUE) {
+ VarSize = VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
+ VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
+ VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+
+ Buffer = AllocateZeroPool (VarSize);
+ if (Buffer != NULL) {
+ VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
+ }
+ }
+
+ CopyMem (VAR_DIG_NAME (VarDig), VariableName, NameSize);
+ CopyMem (VAR_DIG_GUID (VarDig), VendorGuid, sizeof (EFI_GUID));
+
+ return VarDig;
+}
+
+/**
+
+ Remove variable digest node.
+
+ @param[in,out] Global Pointer to global configuration data.
+ @param[in,out] VarDig Pointer to variable digest value.
+ @param[in] FreeResource Flag to indicate whether to free resource.
+
+**/
+VOID
+RemoveVariableDigestNode (
+ IN OUT PROTECTED_VARIABLE_GLOBAL *Global,
+ IN OUT VARIABLE_DIGEST *VarDig,
+ IN BOOLEAN FreeResource
+ )
+{
+ VARIABLE_DIGEST *Prev;
+ VARIABLE_DIGEST *Next;
+
+ Prev = VAR_DIG_PREV (VarDig);
+ Next = VAR_DIG_NEXT (VarDig);
+
+ if (Global->VariableDigests == VAR_DIG_ADR (VarDig)) {
+ Global->VariableDigests = VAR_DIG_ADR (Next);
+ }
+
+ if (Prev != NULL) {
+ Prev->Next = VAR_DIG_ADR (Next);
+ }
+
+ if (Next != NULL) {
+ Next->Prev = VAR_DIG_ADR (Prev);
+ }
+
+ VarDig->Prev = 0;
+ VarDig->Next = 0;
+ VarDig->Flags.Valid = FALSE;
+
+ if (FreeResource && VarDig->Flags.Freeable) {
+ if ((VarDig->CacheIndex != 0) && (VarDig->CacheIndex !=
VAR_INDEX_INVALID)) {
+ VarDig->CacheIndex = VAR_INDEX_INVALID;
+ }
+ }
+}
+
+/**
+
+ Insert variable digest node.
+
+ @param[in,out] Global Pointer to global configuration data.
+ @param[in] VarDig Pointer to variable digest value.
+ @param[in] SortMethod Method for sorting.
+
+**/
+VOID
+InsertVariableDigestNode (
+ IN OUT PROTECTED_VARIABLE_GLOBAL *Global,
+ IN VARIABLE_DIGEST *VarDig,
+ IN SORT_METHOD SortMethod
+ )
+{
+ VARIABLE_DIGEST *Curr;
+ VARIABLE_DIGEST *Prev;
+ BOOLEAN DoReplace;
+ INTN Result;
+
+ if (SortMethod == NULL) {
+ SortMethod = CompareVariableDigestInfo;
+ }
+
+ DoReplace = FALSE;
+ Curr = VAR_DIG_PTR (Global->VariableDigests);
+ if (Curr == NULL) {
+ //
+ // First one.
+ //
+ VarDig->Prev = 0;
+ VarDig->Next = 0;
+ Global->VariableDigests = VAR_DIG_ADR (VarDig);
+ return;
+ }
+
+ while (Curr != NULL && Curr != VarDig) {
+ Result = SortMethod (VarDig, Curr);
+
+ if (Result <= 0) {
+ ASSERT (VarDig->StoreIndex != Curr->StoreIndex);
+
+ //
+ // The same variable already in list?
+ //
+ if (Result == 0) {
+ //
+ // Keep only the same new one, unless states are different. In such
+ // situation, the one with no VAR_ADDED will be deleted.
+ //
+ if (VarDig->State >= Curr->State) {
+ DoReplace = TRUE;
+ Curr->Flags.Valid = FALSE; // to-be-deleted
+ } else {
+ DoReplace = FALSE;
+ VarDig->Flags.Valid = FALSE; // to-be-deleted
+ }
+ }
+
+ //
+ // Put VarDig before Curr
+ //
+ VarDig->Next = VAR_DIG_ADR (Curr);
+ VarDig->Prev = Curr->Prev;
+
+ if (VAR_DIG_PREV (Curr) != NULL) {
+ VAR_DIG_PREV (Curr)->Next = VAR_DIG_ADR (VarDig);
+ }
+
+ Curr->Prev = VAR_DIG_ADR (VarDig);
+
+ if (DoReplace) {
+ RemoveVariableDigestNode (Global, Curr, TRUE);
+ }
+
+ break;
+ }
+
+ Prev = Curr;
+ Curr = VAR_DIG_NEXT (Curr);
+ if (Curr == NULL) {
+ Prev->Next = VAR_DIG_ADR (VarDig);
+
+ VarDig->Prev = VAR_DIG_ADR (Prev);
+ VarDig->Next = 0;
+ }
+ }
+
+ //
+ // Update the head node if necessary.
+ //
+ if (VAR_DIG_PTR (VarDig->Prev) == NULL) {
+ Global->VariableDigests = VAR_DIG_ADR (VarDig);
+ }
+}
+
+/**
+
+ Find the specified variable digest
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in] VarInfo Pointer to variable data.
+ @param[in] FindNext Flag to continue looking for variable.
+
+**/
+VARIABLE_DIGEST *
+FindVariableInternal (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN PROTECTED_VARIABLE_INFO *VarInfo,
+ IN BOOLEAN FindNext
+ )
+{
+ VARIABLE_DIGEST *VarDig;
+ VARIABLE_DIGEST *Found;
+ VARIABLE_DIGEST *FirstStoreIndexVar;
+ BOOLEAN ByIndex;
+ INTN FwdOrBwd;
+
+ //
+ // If VarInfo->StoreIndex is valid, use it to find the variable. Otherwise,
+ // use the variable name and guid instead, if given. If no clue at all, return
+ // the variable with lowest StoreIndex.
+ //
+ if ( (VarInfo->StoreIndex != VAR_INDEX_INVALID)
+ || (VarInfo->Header.VariableName == NULL)
+ || (VarInfo->Header.VendorGuid == NULL))
+ {
+ ByIndex = TRUE;
+ } else {
+ ByIndex = FALSE;
+ }
+
+ Found = NULL;
+ VarDig = VAR_DIG_PTR (Global->VariableDigests);
+ FirstStoreIndexVar = VarDig;
+ FwdOrBwd = 1;
+
+ //
+ // Discover variable with first/smallest store index
+ //
+ while (VarDig != NULL) {
+ if (VarDig->StoreIndex < FirstStoreIndexVar->StoreIndex) {
+ FirstStoreIndexVar = VAR_DIG_PTR (VarDig);
+ }
+
+ VarDig = VAR_DIG_NEXT (VarDig);
+ }
+
+ //
+ // Input variable is NULL than return first variable
+ // with smallest store index from the variable digest list.
+ //
+ if (((VarInfo->Header.VariableName == NULL) ||
+ (VarInfo->Header.VendorGuid == NULL)) &&
+ (ByIndex == FALSE))
+ {
+ return FirstStoreIndexVar;
+ }
+
+ //
+ // Start with first entry
+ //
+ VarDig = VAR_DIG_PTR (Global->VariableDigests);
+ while (VarDig != NULL) {
+ if (ByIndex) {
+ if (FindNext) {
+ if (VarDig->StoreIndex == VarInfo->StoreIndex) {
+ Found = VarDig = VAR_DIG_NEXT (VarDig);
+ break;
+ }
+ } else if (VarDig->StoreIndex == VarInfo->StoreIndex) {
+ Found = VarDig;
+ break;
+ }
+ } else {
+ //
+ // Match given variable name and vendor guid.
+ //
+ if (IS_VARIABLE (&VarInfo->Header, VAR_DIG_NAME (VarDig),
VAR_DIG_GUID (VarDig))) {
+ Found = (FindNext) ? VAR_DIG_NEXT (VarDig) : VarDig;
+ break;
+ }
+ }
+
+ VarDig = (FwdOrBwd > 0) ? VAR_DIG_NEXT (VarDig) : VAR_DIG_PREV
(VarDig);
+ if (VarDig == NULL) {
+ }
+ }
+
+ return Found;
+}
+
+/**
+
+ Synchronize the RPMC counters
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in] VarInfo Pointer to variable data.
+ @param[in] FindNext Flag to continue looking for variable.
+
+ @retval EFI_SUCCESS Successfully sync RPMC counters.
+ @return others Failed to sync RPMC counters.
+
+**/
+EFI_STATUS
+SyncRpmcCounter (
+ VOID
+ )
+{
+ UINT32 Counter1;
+ UINT32 Counter2;
+ EFI_STATUS Status;
+
+ //
+ // Sync RPMC1 & RPMC2.
+ //
+ Status = RequestMonotonicCounter (RPMC_COUNTER_1, &Counter1);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter2);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ while (Counter1 < Counter2) {
+ Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ ++Counter1;
+ }
+
+ while (Counter2 < Counter1) {
+ Status = IncrementMonotonicCounter (RPMC_COUNTER_2);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ ++Counter2;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ An alternative version of ProtectedVariableLibGetData to get plain data from
+ given variable, if encrypted.
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in,out] VarInfo Pointer to structure containing variable
+ information. VarInfo->Header.Data must point
+ to the original variable data.
+
+ @retval EFI_SUCCESS Found the specified variable.
+ @retval EFI_INVALID_PARAMETER VarInfo is NULL or both VarInfo->Buffer
and
+ VarInfo->Offset are invalid.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+
+**/
+STATIC
+EFI_STATUS
+ProtectedVariableLibGetDataInternal (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ VOID *Buffer;
+ UINTN BufferSize;
+
+ if ((Global == NULL) || (VarInfo == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ContextIn = GET_CNTX (Global);
+
+ //
+ // Check if the data has been decrypted or not.
+ //
+ BufferSize = VarInfo->PlainDataSize;
+ VarInfo->CipherData = NULL;
+ VarInfo->CipherDataSize = 0;
+ VarInfo->PlainData = NULL;
+ VarInfo->PlainDataSize = 0;
+ Status = GetCipherDataInfo (VarInfo);
+
+ if ((Status == EFI_UNSUPPORTED) || (Status == EFI_NOT_FOUND)) {
+ VarInfo->Flags.DecryptInPlace = TRUE;
+ VarInfo->PlainDataSize = (UINT32)VarInfo->Header.DataSize;
+ VarInfo->PlainData = VarInfo->Header.Data;
+ VarInfo->CipherDataType = 0;
+ VarInfo->CipherHeaderSize = 0;
+ Status = EFI_SUCCESS;
+ } else if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ //
+ // Don't do decryption if the caller provided buffer is too small
+ // Simply return the real Plain Data Size via VarInfo->PlainDataSize
+ //
+ if (BufferSize < VarInfo->PlainDataSize) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // If the variable data is cipher data, decrypt it inplace if possible.
+ //
+ if ((VarInfo->PlainData == NULL) && (VarInfo->CipherData != NULL)) {
+ VarInfo->Key = Global->RootKey;
+ VarInfo->KeySize = sizeof (Global->RootKey);
+
+ switch (ContextIn->VariableServiceUser) {
+ case FromPeiModule:
+ VarInfo->Flags.DecryptInPlace = FALSE;
+ //
+ // In PEI VariableCache holds Cipher header + Cipher data
+ // Do not override Cipher header data during decrypt operation
+ //
+ VarInfo->PlainData = GET_BUFR (Global->VariableCache + VarInfo-
CipherHeaderSize);
+
+ Status = DecryptVariable (VarInfo);
+ if (Status == EFI_UNSUPPORTED) {
+ VarInfo->PlainData = VarInfo->Header.Data;
+ VarInfo->PlainDataSize = (UINT32)VarInfo->Header.DataSize;
+ VarInfo->CipherDataType = 0;
+ VarInfo->CipherHeaderSize = 0;
+
+ Status = EFI_SUCCESS;
+ }
+
+ break;
+
+ case FromSmmModule:
+ VarInfo->Flags.DecryptInPlace = FALSE;
+ VarInfo->PlainData = GET_BUFR (Global->VariableCache);
+
+ Status = DecryptVariable (VarInfo);
+ if (Status == EFI_UNSUPPORTED) {
+ VarInfo->PlainData = VarInfo->Header.Data;
+ VarInfo->PlainDataSize = (UINT32)VarInfo->Header.DataSize;
+ VarInfo->CipherDataType = 0;
+ VarInfo->CipherHeaderSize = 0;
+
+ Status = EFI_SUCCESS;
+ }
+
+ break;
+
+ case FromBootServiceModule:
+ case FromRuntimeModule:
+ //
+ // The SMM passes back only decrypted data. We re-use the original cipher
+ // data buffer to keep the plain data along with the cipher header.
+ //
+ VarInfo->Flags.DecryptInPlace = TRUE;
+ Buffer = (VOID *)((UINTN)VarInfo->CipherData + VarInfo-
CipherHeaderSize);
+ BufferSize = VarInfo->PlainDataSize;
+ Status = ContextIn->FindVariableSmm (
+ VarInfo->Header.VariableName,
+ VarInfo->Header.VendorGuid,
+ &VarInfo->Header.Attributes,
+ &BufferSize,
+ Buffer
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Flag the payload as plain data to avoid re-decrypting.
+ //
+ VarInfo->CipherDataType = ENC_TYPE_NULL;
+ VarInfo->PlainDataSize = (UINT32)BufferSize;
+ VarInfo->PlainData = Buffer;
+
+ Status = SetCipherDataInfo (VarInfo);
+ if (Status == EFI_UNSUPPORTED) {
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ break;
+
+ default:
+ Status = EFI_UNSUPPORTED;
+ break;
+ }
+
+ VarInfo->CipherData = NULL;
+ VarInfo->CipherDataSize = 0;
+ }
+
+ return Status;
+}
+
+/**
+
+ An alternative version of ProtectedVariableLibGetData to get plain data, if
+ encrypted, from given variable, for different use cases.
+
+ @param[in,out] VarInfo Pointer to structure containing variable
information.
+
+ @retval EFI_SUCCESS Found the specified variable.
+ @retval EFI_INVALID_PARAMETER VarInfo is NULL or both VarInfo->Buffer
and
+ VarInfo->Offset are invalid.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetByInfo (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VOID **Buffer;
+ UINT32 BufferSize;
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ //
+ // Save the output data buffer because below call
+ // call will use this struct field internally.
+ //
+ Buffer = VarInfo->PlainData;
+ BufferSize = VarInfo->PlainDataSize;
+
+ Status = ProtectedVariableLibGetDataInternal (Global, VarInfo);
+ if (EFI_ERROR (Status) || ((BufferSize) < VarInfo->PlainDataSize)) {
+ //
+ // Return with caller provided buffer with zero DataSize
+ //
+ VarInfo->PlainData = Buffer;
+ return Status;
+ }
+
+ //
+ // Copy Plain data to ouput data buffer
+ //
+ CopyMem (Buffer, VarInfo->PlainData, VarInfo->PlainDataSize);
+ VarInfo->PlainData = Buffer;
+
+ return Status;
+}
+
+/**
+
+ Retrieve plain data, if encrypted, of given variable.
+
+ If variable encryption is employed, this function will initiate a SMM request
+ to get the plain data. Due to security consideration, the decryption can only
+ be done in SMM environment.
+
+ @param[in] Variable Pointer to header of a Variable.
+ @param[in,out] Data Pointer to plain data of the given variable.
+ @param[in,out] DataSize Size of data returned or data buffer needed.
+ @param[in] AuthFlag Auth-variable indicator.
+
+ @retval EFI_SUCCESS Found the specified variable.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+ @retval EFI_BUFFER_TOO_SMALL If *DataSize is smaller than needed.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetByBuffer (
+ IN VARIABLE_HEADER *Variable,
+ IN OUT VOID *Data,
+ IN OUT UINT32 *DataSize,
+ IN BOOLEAN AuthFlag
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ AUTHENTICATED_VARIABLE_HEADER *AuthVariable;
+ VOID *Buffer;
+
+ if ((Variable == NULL) || (DataSize == NULL)) {
+ ASSERT (Variable != NULL);
+ ASSERT (DataSize != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+
+ VarInfo.Buffer = Variable;
+ VarInfo.Flags.Auth = AuthFlag;
+ VarInfo.PlainDataSize = *DataSize;
+
+ if (VarInfo.Flags.Auth == TRUE) {
+ AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable;
+
+ VarInfo.Header.VariableName = (CHAR16 *)((UINTN)Variable + sizeof
(AUTHENTICATED_VARIABLE_HEADER));
+ VarInfo.Header.NameSize = AuthVariable->NameSize;
+ VarInfo.Header.VendorGuid = &AuthVariable->VendorGuid;
+ VarInfo.Header.Attributes = AuthVariable->Attributes;
+ VarInfo.Header.DataSize = AuthVariable->DataSize;
+ } else {
+ VarInfo.Header.VariableName = (CHAR16 *)((UINTN)Variable + sizeof
(VARIABLE_HEADER));
+ VarInfo.Header.NameSize = Variable->NameSize;
+ VarInfo.Header.VendorGuid = &Variable->VendorGuid;
+ VarInfo.Header.Attributes = Variable->Attributes;
+ VarInfo.Header.DataSize = Variable->DataSize;
+ }
+
+ Buffer = VARIABLE_NAME (VarInfo.Buffer, VarInfo.Flags.Auth);
+ Buffer = GET_BUFR (GET_ADRS (Buffer) + VarInfo.Header.NameSize);
+ Buffer = GET_BUFR (GET_ADRS (Buffer) + GET_PAD_SIZE
(VarInfo.Header.NameSize));
+ VarInfo.Header.Data = Buffer;
+
+ Status = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
+ *DataSize = VarInfo.PlainDataSize;
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ CopyMem (Data, VarInfo.PlainData, VarInfo.PlainDataSize);
+
+ return Status;
+}
+
+/**
+ This service retrieves a variable's value using its name and GUID.
+
+ Read the specified variable from the UEFI variable store. If the Data
+ buffer is too small to hold the contents of the variable, the error
+ EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer
+ size to obtain the data.
+
+ @param VariableName A pointer to a null-terminated string that is the
variable's name.
+ @param VariableGuid A pointer to an EFI_GUID that is the variable's
GUID. The combination of
+ VariableGuid and VariableName must be unique.
+ @param Attributes If non-NULL, on return, points to the variable's
attributes.
+ @param DataSize On entry, points to the size in bytes of the Data
buffer.
+ On return, points to the size of the data returned in Data.
+ @param Data Points to the buffer which will hold the returned
variable value.
+ May be NULL with a zero DataSize in order to determine the
size of the buffer needed.
+
+ @retval EFI_SUCCESS The variable was read successfully.
+ @retval EFI_NOT_FOUND The variable was be found.
+ @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting
data.
+ DataSize is updated with the size required for
+ the specified variable.
+ @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or
Data is NULL.
+ @retval EFI_DEVICE_ERROR The variable could not be retrieved because of
a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetByName (
+ IN CONST CHAR16 *VariableName,
+ IN CONST EFI_GUID *VariableGuid,
+ OUT UINT32 *Attributes,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *VarDig;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ EFI_TIME TimeStamp;
+ VOID *DataBuffer;
+
+ if ((VariableName == NULL) || (VariableGuid == NULL) || (DataSize == NULL)) {
+ ASSERT (VariableName != NULL);
+ ASSERT (VariableGuid != NULL);
+ ASSERT (DataSize != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetProtectedVariableGlobal (&Global);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ ContextIn = GET_CNTX (Global);
+
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarInfo.Header.VariableName = (CHAR16 *)VariableName;
+ VarInfo.Header.NameSize = StrSize (VariableName);
+ VarInfo.Header.VendorGuid = (EFI_GUID *)VariableGuid;
+
+ VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
+ if (VarDig == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (Attributes != NULL) {
+ *Attributes = VarDig->Attributes;
+ }
+
+ if ((Data == NULL) || (*DataSize < VarDig->PlainDataSize)) {
+ *DataSize = VarDig->PlainDataSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ VarInfo.Flags.Auth = VarDig->Flags.Auth;
+ VarInfo.Flags.Protected = VarDig->Flags.Protected;
+
+ //
+ // Verify digest before copy the data back, if the variable is not in cache.
+ //
+ if (VarDig->CacheIndex != VAR_INDEX_INVALID) {
+ VarInfo.Header.VariableName = NULL;
+ VarInfo.Header.VendorGuid = NULL;
+ VarInfo.Buffer = GET_BUFR (VarDig->CacheIndex);
+
+ Status = ContextIn->GetVariableInfo (&VarInfo);
+ ASSERT_EFI_ERROR (Status);
+ } else {
+ //
+ // A buffer for at least one variable data (<=PcdMax(Auth)VariableSize)
+ // must be reserved in advance.
+ //
+ ASSERT (
+ Global->VariableCache != 0
+ && Global->VariableCacheSize >= VarDig->DataSize
+ );
+ DataBuffer = GET_BUFR (Global->VariableCache);
+ //
+ // Note name and GUID are already there.
+ //
+ VarInfo.StoreIndex = VarDig->StoreIndex;
+
+ VarInfo.Header.VariableName = NULL; // Prevent name from being retrieved
again.
+ VarInfo.Header.NameSize = 0;
+ VarInfo.Header.VendorGuid = NULL; // Prevent guid from being retrieved
again.
+ VarInfo.Header.TimeStamp = &TimeStamp;
+ VarInfo.Header.Data = DataBuffer;
+ VarInfo.Header.DataSize = VarDig->DataSize;
+
+ //
+ // Get detailed information about the variable.
+ //
+ Status = ContextIn->GetVariableInfo (&VarInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // The variable must be validated its digest value to avoid TOCTOU, if it's
+ // not been cached yet.
+ //
+ VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
+ VarInfo.Header.NameSize = VarDig->NameSize;
+ VarInfo.Header.VendorGuid = &VarDig->VendorGuid;
+ Status = VerifyVariableDigest (Global, &VarInfo, VarDig);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ VarInfo.PlainDataSize = (UINT32)*DataSize;
+
+ //
+ // Decrypt the data, if necessary.
+ //
+ Status = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
+ *DataSize = VarInfo.PlainDataSize;
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: %d Exit(). VariableName = %s, VariableGuid = 0x%g, DataSize = 0x%X,
Data Buffer = 0x%lX, Status = %r\n",
+ __FUNCTION__,
+ __LINE__,
+ VariableName,
+ VariableGuid,
+ *DataSize,
+ Data,
+ Status
+ ));
+ return Status;
+ }
+
+ CopyMem (Data, VarInfo.PlainData, VarInfo.PlainDataSize);
+
+ return Status;
+}
+
+/**
+
+ This function is used to enumerate the variables managed by current
+ ProtectedVariableLib.
+
+ If the VarInfo->StoreIndex is invalid (VAR_INDEX_INVALID), the first variable
+ with the smallest StoreIndex will be returned. Otherwise, the variable with
+ StoreIndex just after than the VarInfo->StoreIndex will be returned.
+
+ @param[in,out] VarInfo Pointer to structure containing variable
information.
+
+ @retval EFI_SUCCESS Found the specified variable.
+ @retval EFI_INVALID_PARAMETER VarInfo is NULL.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+
+**/
+STATIC
+EFI_STATUS
+GetNextVariableInternal (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *Found;
+
+ if (VarInfo == NULL) {
+ ASSERT (VarInfo != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ Found = FindVariableInternal (Global, VarInfo, TRUE);
+ if (Found == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Return all cached data.
+ //
+ VarInfo->Header.VariableName = VAR_DIG_NAME (Found);
+ VarInfo->Header.VendorGuid = VAR_DIG_GUID (Found);
+ VarInfo->Header.NameSize = Found->NameSize;
+ VarInfo->Header.DataSize = Found->DataSize;
+ VarInfo->Header.Attributes = Found->Attributes;
+
+ VarInfo->PlainDataSize = Found->PlainDataSize;
+ VarInfo->StoreIndex = Found->StoreIndex;
+ if (Found->CacheIndex != VAR_INDEX_INVALID) {
+ VarInfo->Buffer = GET_BUFR (Found->CacheIndex);
+ }
+
+ VarInfo->Flags.Auth = Found->Flags.Auth;
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Find the request variable.
+
+ @param[in, out] VarInfo Pointer to variable data.
+
+ @retval EFI_SUCCESS The variable was read successfully.
+ @retval EFI_NOT_FOUND The variable could not be found.
+ @retval EFI_INVALID_PARAMETER Variable info is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibFind (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *Found;
+
+ if (VarInfo == NULL) {
+ ASSERT (VarInfo != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ Found = FindVariableInternal (Global, VarInfo, FALSE);
+ if (Found == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Return all cached data.
+ //
+ VarInfo->Header.VariableName = VAR_DIG_NAME (Found);
+ VarInfo->Header.VendorGuid = VAR_DIG_GUID (Found);
+ VarInfo->Header.NameSize = Found->NameSize;
+ VarInfo->Header.DataSize = Found->DataSize;
+ VarInfo->Header.Attributes = Found->Attributes;
+
+ VarInfo->PlainDataSize = Found->PlainDataSize;
+ VarInfo->StoreIndex = Found->StoreIndex;
+ if (Found->CacheIndex != VAR_INDEX_INVALID) {
+ VarInfo->Buffer = GET_BUFR (Found->CacheIndex);
+ }
+
+ VarInfo->Flags.Auth = Found->Flags.Auth;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Return the next variable name and GUID.
+
+ This function is called multiple times to retrieve the VariableName
+ and VariableGuid of all variables currently available in the system.
+ On each call, the previous results are passed into the interface,
+ and, on return, the interface returns the data for the next
+ interface. When the entire variable list has been returned,
+ EFI_NOT_FOUND is returned.
+
+ @param VariableNameSize On entry, points to the size of the buffer pointed
to by VariableName.
+ On return, the size of the variable name buffer.
+ @param VariableName On entry, a pointer to a null-terminated string that
is the variable's name.
+ On return, points to the next variable's null-terminated name
string.
+ @param VariableGuid On entry, a pointer to an EFI_GUID that is the
variable's GUID.
+ On return, a pointer to the next variable's GUID.
+
+ @retval EFI_SUCCESS The variable was read successfully.
+ @retval EFI_NOT_FOUND The variable could not be found.
+ @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the
resulting
+ data. VariableNameSize is updated with the size
+ required for the specified variable.
+ @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or
+ VariableNameSize is NULL.
+ @retval EFI_DEVICE_ERROR The variable could not be retrieved because of
a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibFindNext (
+ IN OUT UINTN *VariableNameSize,
+ IN OUT CHAR16 *VariableName,
+ IN OUT EFI_GUID *VariableGuid
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *VarDig;
+ UINTN Size;
+
+ if ((VariableNameSize == NULL) || (VariableName == NULL) || (VariableGuid
== NULL)) {
+ ASSERT (VariableNameSize != NULL);
+ ASSERT (VariableName != NULL);
+ ASSERT (VariableGuid != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SetMem (&VarInfo, sizeof (VarInfo), 0);
+ Size = StrSize (VariableName);
+
+ if (Size <= 2) {
+ VarDig = VAR_DIG_PTR (Global->VariableDigests);
+ } else {
+ VarInfo.Header.VariableName = VariableName;
+ VarInfo.Header.NameSize = Size;
+ VarInfo.Header.VendorGuid = VariableGuid;
+
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+
+ VarDig = FindVariableInternal (Global, &VarInfo, TRUE);
+ }
+
+ if (VarDig == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (VarDig->NameSize > *VariableNameSize) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (VariableName, VAR_DIG_NAME (VarDig), VarDig->NameSize);
+ CopyGuid (VariableGuid, &VarDig->VendorGuid);
+ *VariableNameSize = VarInfo.Header.NameSize;
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Return the next variable name and GUID.
+
+ @param[in, out] VarInfo Pointer to variable data.
+
+ @retval EFI_SUCCESS The variable was read successfully.
+ @retval EFI_INVALID_PARAMETER VarInfo is NULL.
+ @retval EFI_NOT_FOUND The specified variable could not be found.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibFindNextEx (
+ IN OUT PROTECTED_VARIABLE_INFO *VarInfo
+ )
+{
+ return GetNextVariableInternal (VarInfo);
+}
+
+/**
+
+ Return the max count of a variable.
+
+ @return max count of a variable.
+
+**/
+UINTN
+ProtectedVariableLibGetMaxVariablesCount (
+ VOID
+ )
+{
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ VARIABLE_DIGEST *VarDig;
+ EFI_STATUS Status;
+ UINTN Count;
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return 0;
+ }
+
+ Count = 0;
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+
+ //
+ // Start with first entry
+ //
+ VarDig = VAR_DIG_PTR (Global->VariableDigests);
+ VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
+ VarInfo.Header.VendorGuid = VAR_DIG_GUID (VarDig);
+ VarInfo.StoreIndex = VarDig->StoreIndex;
+
+ do {
+ VarInfo.Buffer = NULL;
+ Status = ProtectedVariableLibFindNextEx (&VarInfo);
+ if (EFI_ERROR (Status)) {
+ return Count;
+ }
+
+ Count++;
+ } while (TRUE);
+}
+
+/**
+ The function is called by PerformQuickSort to sort.
+
+ @param[in] Left The pointer to first buffer.
+ @param[in] Right The pointer to second buffer.
+
+ @retval 0 Buffer1 equal to Buffer2.
+ @return < 0 Buffer1 is less than Buffer2.
+ @return > 0 Buffer1 is greater than Buffer2.
+
+**/
+INTN
+EFIAPI
+CompareStoreIndex (
+ IN CONST VOID *Left,
+ IN CONST VOID *Right
+ )
+{
+ EFI_PHYSICAL_ADDRESS StoreIndex1;
+ EFI_PHYSICAL_ADDRESS StoreIndex2;
+
+ StoreIndex1 = (*(EFI_PHYSICAL_ADDRESS *)Left);
+ StoreIndex2 = (*(EFI_PHYSICAL_ADDRESS *)Right);
+
+ if (StoreIndex1 == StoreIndex2) {
+ return (0);
+ }
+
+ if (StoreIndex1 < StoreIndex2) {
+ return (-1);
+ }
+
+ return (1);
+}
+
+/**
+ Refresh variable information changed by variable service.
+
+ @param Buffer Pointer to a pointer of buffer.
+ @param NumElements Pointer to number of elements in list.
+
+
+ @return EFI_SUCCESS Successfully retrieved sorted list.
+ @return others Unsuccessful.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetSortedList (
+ IN OUT EFI_PHYSICAL_ADDRESS **Buffer,
+ IN OUT UINTN *NumElements
+ )
+{
+ EFI_STATUS Status;
+ UINTN Count;
+ UINTN StoreIndexTableSize;
+ EFI_PHYSICAL_ADDRESS *StoreIndexTable;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *VarDig;
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ Count = 0;
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+ StoreIndexTableSize = ProtectedVariableLibGetMaxVariablesCount ();
+ StoreIndexTable = AllocateZeroPool (sizeof (EFI_PHYSICAL_ADDRESS) *
StoreIndexTableSize);
+
+ //
+ // Start with first entry
+ //
+ VarDig = VAR_DIG_PTR (Global->VariableDigests);
+ VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
+ VarInfo.Header.VendorGuid = VAR_DIG_GUID (VarDig);
+ VarInfo.StoreIndex = VarDig->StoreIndex;
+ StoreIndexTable[Count] = VarInfo.StoreIndex;
+ Count++;
+
+ //
+ // Populate the un-sorted table
+ //
+ do {
+ VarInfo.Buffer = NULL;
+ Status = ProtectedVariableLibFindNextEx (&VarInfo);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ StoreIndexTable[Count] = VarInfo.StoreIndex;
+ Count++;
+ } while (TRUE);
+
+ PerformQuickSort (
+ StoreIndexTable,
+ Count,
+ sizeof (EFI_PHYSICAL_ADDRESS),
+ (SORT_COMPARE)CompareStoreIndex
+ );
+
+ *Buffer = StoreIndexTable;
+ *NumElements = Count;
+
+ return EFI_SUCCESS;
+}
diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
new file mode 100644
index 000000000000..94df21eacf25
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
@@ -0,0 +1,163 @@
+/** @file
+ Implemention of ProtectedVariableLib for SMM variable services.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Uefi.h>
+
+#include "Library/UefiBootServicesTableLib.h"
+#include "Library/MemoryAllocationLib.h"
+
+#include "ProtectedVariableInternal.h"
+
+PROTECTED_VARIABLE_CONTEXT_IN mVariableContextIn = {
+ PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
+ sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
+ 0,
+ FromSmmModule,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PROTECTED_VARIABLE_GLOBAL mProtectedVariableGlobal = {
+ PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
+ sizeof (PROTECTED_VARIABLE_GLOBAL),
+ { 0 },
+ { 0 },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ { 0, 0, 0 },
+ 0,
+ 0,
+ { 0, 0, 0, 0, 0, 0}
+};
+
+/**
+ Fix incorrect state of MetaDataHmacVariable before any variable update.
+
+ @param[in] Event The event that occurred
+ @param[in] Context For EFI compatibility. Not used.
+
+**/
+VOID
+EFIAPI
+VariableWriteProtocolCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Fix incorrect state of MetaDataHmacVariable before any variable update.
+ // This has to be done here due to the fact that this operation needs to
+ // update NV storage but the FVB and FTW protocol might not be ready during
+ // ProtectedVariableLibInitialize().
+ //
+ Status = FixupHmacVariable ();
+ ASSERT_EFI_ERROR (Status);
+
+ Status = ProtectedVariableLibWriteInit ();
+ ASSERT_EFI_ERROR (Status);
+
+ gBS->CloseEvent (Event);
+}
+
+/**
+
+ Initialization for protected variable services.
+
+ If this initialization failed upon any error, the whole variable services
+ should not be used. A system reset might be needed to re-construct NV
+ variable storage to be the default state.
+
+ @param[in] ContextIn Pointer to variable service context needed by
+ protected variable.
+
+ @retval EFI_SUCCESS Protected variable services are ready.
+ @retval EFI_INVALID_PARAMETER If ContextIn == NULL or something
missing or
+ mismatching in the content in ContextIn.
+ @retval EFI_COMPROMISED_DATA If failed to check integrity of protected
variables.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_UNSUPPORTED Unsupported to process protected variable.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibInitialize (
+ IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_CONTEXT_IN *ProtectedVarContext;
+ PROTECTED_VARIABLE_GLOBAL *OldGlobal;
+ PROTECTED_VARIABLE_GLOBAL *NewGlobal;
+ VOID *VarWriteReg;
+
+ if ( (ContextIn == NULL)
+ || (ContextIn->StructVersion !=
PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
+ || (ContextIn->StructSize != sizeof (PROTECTED_VARIABLE_CONTEXT_IN))
+ || (ContextIn->GetVariableInfo == NULL)
+ || (ContextIn->GetNextVariableInfo == NULL)
+ || (ContextIn->UpdateVariableStore == NULL)
+ || (ContextIn->UpdateVariable == NULL))
+ {
+ ASSERT (ContextIn != NULL);
+ ASSERT (ContextIn->StructVersion ==
PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION);
+ ASSERT (ContextIn->StructSize == sizeof
(PROTECTED_VARIABLE_CONTEXT_IN));
+ ASSERT (ContextIn->GetVariableInfo != NULL);
+ ASSERT (ContextIn->GetNextVariableInfo != NULL);
+ ASSERT (ContextIn->UpdateVariableStore != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ GetProtectedVariableGlobal (&NewGlobal);
+ ProtectedVarContext = GET_CNTX (NewGlobal);
+ CopyMem (ProtectedVarContext, ContextIn, sizeof (mVariableContextIn));
+ ProtectedVarContext->VariableServiceUser = FromSmmModule;
+
+ //
+ // Get root key and HMAC key from HOB created by PEI variable driver.
+ //
+ Status = GetProtectedVariableGlobalFromHob (&OldGlobal);
+ ASSERT_EFI_ERROR (Status);
+
+ CopyMem ((VOID *)NewGlobal, (CONST VOID *)OldGlobal, sizeof
(*OldGlobal));
+
+ //
+ // The keys must not be available outside SMM.
+ //
+ if (ProtectedVarContext->VariableServiceUser == FromSmmModule) {
+ ZeroMem (OldGlobal->RootKey, sizeof (OldGlobal->RootKey));
+ ZeroMem (OldGlobal->MetaDataHmacKey, sizeof (OldGlobal-
MetaDataHmacKey));
+ }
+
+ //
+ // Register variable write protocol notify function used to fix any
+ // inconsistency in MetaDataHmacVariable before the first variable write
+ // operation.
+ //
+ NewGlobal->Flags.WriteInit = FALSE;
+ NewGlobal->Flags.WriteReady = FALSE;
+
+ EfiCreateProtocolNotifyEvent (
+ &gEfiVariableWriteArchProtocolGuid,
+ TPL_CALLBACK,
+ VariableWriteProtocolCallback,
+ NULL,
+ &VarWriteReg
+ );
+
+ return EFI_SUCCESS;
+}
diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
new file mode 100644
index 000000000000..8b5ccb83e32d
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
@@ -0,0 +1,1327 @@
+/** @file
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <PiPei.h>
+
+#include <Guid/VariableFormat.h>
+#include <Ppi/MemoryDiscovered.h>
+
+#include <Library/HobLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/ReportStatusCodeLib.h>
+
+#include "ProtectedVariableInternal.h"
+
+/**
+ Function allocates a global buffer.
+
+ This function allocates a buffer with the specified size.
+
+ @param[in] Size Size of buffer to allocate.
+ @param[in] AllocatePage Whether to allocate pages.
+
+ @retval Buffer Pointer to the Buffer allocated.
+ @retval NULL if no Buffer was found.
+
+**/
+VOID *
+AllocateGlobalBuffer (
+ IN UINT32 Size,
+ IN BOOLEAN AllocatePage
+ )
+{
+ VOID *Buffer;
+ EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
+ EFI_PEI_HOB_POINTERS Hob;
+
+ Buffer = NULL;
+ if (!AllocatePage) {
+ Buffer = BuildGuidHob (&gEdkiiProtectedVariableGlobalGuid, Size);
+ }
+
+ if (Buffer == NULL) {
+ //
+ // Use the AllocatePages() to get over size limit of general GUID-ed HOB.
+ //
+ Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
+ if (Buffer == NULL) {
+ ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
+ return NULL;
+ }
+
+ //
+ // Mark the HOB holding the pages just allocated so that it can be
+ // identified later.
+ //
+ MemoryAllocationHob = NULL;
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
+ while (Hob.Raw != NULL) {
+ MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw;
+ if ((UINTN)Buffer == (UINTN)MemoryAllocationHob-
AllocDescriptor.MemoryBaseAddress) {
+ CopyGuid (
+ &MemoryAllocationHob->AllocDescriptor.Name,
+ &gEdkiiProtectedVariableGlobalGuid
+ );
+ break;
+ }
+
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
Hob.Raw);
+ }
+ }
+
+ return Buffer;
+}
+
+/**
+ Callback use to re-verify all variables and cache them in memory.
+
+ @param[in] PeiServices General purpose services available to every PEIM.
+ @param[in] NotifyDescriptor The notification structure this PEIM registered
on install.
+ @param[in] Ppi The memory discovered PPI. Not used.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval others There's error in MP initialization.
+**/
+EFI_STATUS
+EFIAPI
+MemoryDiscoveredPpiNotifyCallback (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *VarDig;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ VOID *Buffer;
+ UINT32 VarSize;
+ INTN Result;
+
+ Status = GetProtectedVariableGlobal (&Global);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ ContextIn = GET_CNTX (Global);
+
+ //
+ // Allocate last Var buffer for confidentiality crypto operation
+ //
+ VarSize = (Global->VariableNumber + 1) * MAX_VARIABLE_SIZE;
+ Buffer = AllocateGlobalBuffer (VarSize, TRUE);
+
+ //
+ // Traverse all valid variables.
+ //
+ VarDig = VAR_DIG_PTR (Global->VariableDigests);
+ while (VarDig != NULL) {
+ if (VarDig->CacheIndex == VAR_INDEX_INVALID) {
+ ASSERT (VarDig->StoreIndex != VAR_INDEX_INVALID);
+
+ VarSize = VARIABLE_HEADER_SIZE (Global->Flags.Auth);
+ VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
+ VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+
+ //
+ // Note the variable might be in unconsecutive space.
+ //
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+ VarInfo.StoreIndex = VarDig->StoreIndex;
+ VarInfo.Buffer = Buffer;
+ VarInfo.Flags.Auth = VarDig->Flags.Auth;
+
+ Status = ContextIn->GetVariableInfo (&VarInfo);
+ ASSERT_EFI_ERROR (Status);
+ //
+ // VerifyVariableDigest() refers to CipherData for raw data.
+ //
+ VarInfo.CipherData = VarInfo.Header.Data;
+ VarInfo.CipherDataSize = (UINT32)VarInfo.Header.DataSize;
+
+ //
+ // Make sure that the cached copy is not compromised.
+ //
+ Status = VerifyVariableDigest (Global, &VarInfo, VarDig);
+ if (EFI_ERROR (Status)) {
+ REPORT_STATUS_CODE (
+ EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
+ (PcdGet32 (PcdStatusCodeVariableIntegrity) | (Status & 0xFF))
+ );
+ ASSERT_EFI_ERROR (Status);
+ CpuDeadLoop ();
+ }
+
+ //
+ // Simply use the cache address as CacheIndex of the variable.
+ //
+ VarDig->CacheIndex = GET_ADRS (Buffer);
+ Buffer = (UINT8 *)Buffer + MAX_VARIABLE_SIZE;
+ } else {
+ Result = StrnCmp (
+ VAR_DIG_NAME (VarDig),
+ METADATA_HMAC_VARIABLE_NAME,
+ METADATA_HMAC_VARIABLE_NAME_SIZE
+ );
+ if (Result == 0) {
+ CopyMem (
+ Buffer,
+ GET_BUFR (Global->GlobalSelf + (Global->StructSize -
GetMetaDataHmacVarSize (Global->Flags.Auth))),
+ GetMetaDataHmacVarSize (Global->Flags.Auth)
+ );
+
+ //
+ // Simply use the cache address as CacheIndex of the variable.
+ //
+ VarDig->CacheIndex = GET_ADRS (Buffer);
+ Buffer = (UINT8 *)Buffer + MAX_VARIABLE_SIZE;
+ }
+ }
+
+ VarDig = VAR_DIG_NEXT (VarDig);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Callback use to perform variable integrity check.
+
+ @param[in] PeiServices General purpose services available to every PEIM.
+ @param[in] NotifyDescriptor The notification structure this PEIM registered
on install.
+ @param[in] Ppi The memory discovered PPI. Not used.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval others There's error in MP initialization.
+**/
+EFI_STATUS
+EFIAPI
+VariableStoreDiscoveredPpiNotifyCallback (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ )
+{
+ EFI_STATUS Status;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+
+ GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
+ if (GuidHob != NULL) {
+ ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
(GuidHob);
+ } else {
+ ASSERT (GuidHob == NULL);
+ }
+
+ Status = ContextIn->IsHobVariableStoreAvailable ();
+
+ if (Status == EFI_NOT_READY) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ Status = PerformVariableIntegrityCheck (ContextIn);
+
+ return Status;
+}
+
+EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[] = {
+ {
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiMemoryDiscoveredPpiGuid,
+ MemoryDiscoveredPpiNotifyCallback
+ }
+};
+
+EFI_PEI_NOTIFY_DESCRIPTOR mVariableStoreNotifyList[] = {
+ {
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiVariableStoreDiscoveredPpiGuid,
+ VariableStoreDiscoveredPpiNotifyCallback
+ }
+};
+
+/**
+
+ Get global data structure used to process protected variable.
+
+ @param[out] Global Pointer to global configuration data.
+
+ @retval EFI_SUCCESS Get requested structure successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProtectedVariableGlobal (
+ OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
+ )
+{
+ return GetProtectedVariableGlobalFromHob (Global);
+}
+
+/**
+
+ Get context data structure used to process protected variable.
+
+ @param[out] ContextIn Pointer to context provided by variable runtime
services.
+
+ @retval EFI_SUCCESS Get requested structure successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProtectedVariableContext (
+ PROTECTED_VARIABLE_CONTEXT_IN **ContextIn OPTIONAL
+ )
+{
+ EFI_HOB_GUID_TYPE *GuidHob;
+
+ GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
+ if (GuidHob != NULL) {
+ *ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
(GuidHob);
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Verify the HMAC value stored in MetaDataHmacVar against all valid and
+ protected variables in storage.
+
+ @param[in,out] Global Pointer to global configuration data.
+
+ @retval EFI_SUCCESS The HMAC value matches.
+ @retval EFI_ABORTED Error in HMAC value calculation.
+ @retval EFI_VOLUME_CORRUPTED Inconsistency found in NV variable
storage.
+ @retval EFI_COMPROMISED_DATA The HMAC value doesn't match.
+
+**/
+EFI_STATUS
+VerifyMetaDataHmac (
+ IN OUT PROTECTED_VARIABLE_GLOBAL *Global
+ )
+{
+ EFI_STATUS Status;
+ VARIABLE_DIGEST *VariableDig;
+ UINT32 Counter1;
+ UINT32 Counter2;
+ VOID *Hmac1;
+ VOID *Hmac2;
+ UINT8 HmacVal1[METADATA_HMAC_SIZE];
+ UINT8 HmacVal2[METADATA_HMAC_SIZE];
+
+ Hmac1 = NULL;
+ Hmac2 = HmacSha256New ();
+ if (Hmac2 == NULL) {
+ ASSERT (Hmac2 != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!HmacSha256SetKey (Hmac2, Global->MetaDataHmacKey, sizeof (Global-
MetaDataHmacKey))) {
+ ASSERT (FALSE);
+ Status = EFI_ABORTED;
+ goto Done;
+ }
+
+ //
+ // Retrieve the RPMC counter value.
+ //
+ Status = RequestMonotonicCounter (RPMC_COUNTER_1, &Counter1);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto Done;
+ }
+
+ Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter2);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto Done;
+ }
+
+ //
+ // Counter1 must be either equal to Counter2 or just one step ahead of
Counter2.
+ //
+ if ((Counter1 > Counter2) && ((Counter1 - Counter2) > 1)) {
+ Status = EFI_COMPROMISED_DATA;
+ goto Done;
+ }
+
+ VariableDig = VAR_DIG_PTR (Global->VariableDigests);
+ while (VariableDig != NULL) {
+ //
+ // Only take valid protected variables into account.
+ //
+ if (VariableDig->Flags.Protected && VariableDig->Flags.Valid) {
+ if (!HmacSha256Update (
+ Hmac2,
+ VAR_DIG_VALUE (VariableDig),
+ VariableDig->DigestSize
+ ))
+ {
+ ASSERT (FALSE);
+ Status = EFI_ABORTED;
+ goto Done;
+ }
+ }
+
+ VariableDig = VAR_DIG_NEXT (VariableDig);
+ }
+
+ //
+ // If two MetaDataHmacVariable were found, check which one is valid. We
might
+ // need two HMAC values to check against: one for Counter1, one for
Counter2.
+ //
+ if ( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
+ && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID)
+ && (Counter1 != Counter2))
+ {
+ //
+ // Might need to check Counter1. There must be something wrong in last
boot.
+ //
+ Hmac1 = HmacSha256New ();
+ if ((Hmac1 == NULL) || !HmacSha256Duplicate (Hmac2, Hmac1)) {
+ ASSERT (FALSE);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ if ( !HmacSha256Update (Hmac1, &Counter1, sizeof (Counter1))
+ || !HmacSha256Final (Hmac1, HmacVal1))
+ {
+ ASSERT (FALSE);
+ Status = EFI_ABORTED;
+ goto Done;
+ }
+ }
+
+ //
+ // Always check Counter2.
+ //
+ if ( !HmacSha256Update (Hmac2, &Counter2, sizeof (Counter2))
+ || !HmacSha256Final (Hmac2, HmacVal2))
+ {
+ ASSERT (FALSE);
+ Status = EFI_ABORTED;
+ goto Done;
+ }
+
+ //
+ // When writing (update or add) a variable, there must be following steps
+ // performed:
+ //
+ // A - Increment Counter1
+ // B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
+ // C - Calculate new HMAC value against Counter2+1,
+ // and force-add a new MetaDataHmacVar with state of VAR_ADDED
+ // D - Write the new protected variable
+ // E - Increment Counter2
+ // F - Mark old MetaDataHmacVar as VAR_DELETED
+ //
+ Status = EFI_COMPROMISED_DATA;
+ if ( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
+ && (Global->Unprotected[IndexHmacInDel] == VAR_INDEX_INVALID))
+ {
+ if (CompareMem (
+ VAR_DIG_VALUE (VAR_DIG_PTR (Global-
Unprotected[IndexHmacAdded])),
+ HmacVal2,
+ METADATA_HMAC_SIZE
+ ) == 0)
+ {
+ //
+ //
+ // + A - Increment Counter1
+ // B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
+ // C - Calculate new HMAC value against Counter2+1,
+ // and force-add a new MetaDataHmacVar with state of VAR_ADDED
+ // D - Write the new protected variable
+ // E - Increment Counter2
+ // F - Mark old MetaDataHmacVar as VAR_DELETED
+ //
+ // or,
+ //
+ // + A - Increment Counter1
+ // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
+ // + C - Calculate new HMAC value against Counter2+1,
+ // and force-add a new MetaDataHmacVar with state of VAR_ADDED
+ // + D - Write the new protected variable
+ // + E - Increment Counter2
+ // + F - Mark old MetaDataHmacVar as VAR_DELETED
+ //
+ Status = EFI_SUCCESS;
+
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
TRUE;
+ }
+ } else if ( (Global->Unprotected[IndexHmacAdded] == VAR_INDEX_INVALID)
+ && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID))
+ {
+ if (CompareMem (
+ VAR_DIG_VALUE (VAR_DIG_PTR (Global-
Unprotected[IndexHmacInDel])),
+ HmacVal2,
+ METADATA_HMAC_SIZE
+ ) == 0)
+ {
+ //
+ // + A - Increment Counter1
+ // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
+ // C - Calculate new HMAC value against Counter2+1,
+ // and force-add a new MetaDataHmacVar with state of VAR_ADDED
+ // D - Write the new protected variable
+ // E - Increment Counter2
+ // F - Mark old MetaDataHmacVar as VAR_DELETED
+ //
+ Status = EFI_SUCCESS;
+
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid = TRUE;
+ }
+ } else if ( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
+ && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID))
+ {
+ if (Counter1 > Counter2) {
+ if (CompareMem (
+ VAR_DIG_VALUE (VAR_DIG_PTR (Global-
Unprotected[IndexHmacInDel])),
+ HmacVal2,
+ METADATA_HMAC_SIZE
+ ) == 0)
+ {
+ //
+ // + A - Increment Counter1
+ // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
+ // + C - Calculate new HMAC value against Counter2+1,
+ // and force-add a new MetaDataHmacVar with state VAR_ADDED
+ // D - Write the new protected variable
+ // E - Increment Counter2
+ // F - Mark old MetaDataHmacVar as VAR_DELETED
+ //
+ Status = EFI_SUCCESS;
+
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
FALSE;
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
TRUE;
+ } else if (CompareMem (
+ VAR_DIG_VALUE (VAR_DIG_PTR (Global-
Unprotected[IndexHmacAdded])),
+ HmacVal1,
+ METADATA_HMAC_SIZE
+ ) == 0)
+ {
+ //
+ // + A - Increment Counter1
+ // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
+ // + C - Calculate new HMAC value against Counter2+1,
+ // and force-add a new MetaDataHmacVar with state of VAR_ADDED
+ // + D - Write the new protected variable
+ // E - Increment Counter2
+ // F - Mark old MetaDataHmacVar as VAR_DELETED
+ //
+ Status = EFI_SUCCESS;
+
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
TRUE;
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
FALSE;
+ }
+ } else {
+ if (CompareMem (
+ VAR_DIG_VALUE (VAR_DIG_PTR (Global-
Unprotected[IndexHmacAdded])),
+ HmacVal2,
+ METADATA_HMAC_SIZE
+ ) == 0)
+ {
+ //
+ // + A - Increment Counter1
+ // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
+ // + C - Calculate new HMAC value against Counter2+1,
+ // and force-add a new MetaDataHmacVar with state of VAR_ADDED
+ // + D - Write the new protected variable
+ // + E - Increment Counter2
+ // F - Mark old MetaDataHmacVar as VAR_DELETED
+ //
+ Status = EFI_SUCCESS;
+
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
TRUE;
+ VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
FALSE;
+ }
+ }
+ } else {
+ //
+ // There must be logic error or variable written to storage skipped
+ // the protected variable service, if code reaches here.
+ //
+ ASSERT (FALSE);
+ }
+
+Done:
+ if (Hmac1 != NULL) {
+ HmacSha256Free (Hmac1);
+ }
+
+ if (Hmac2 != NULL) {
+ HmacSha256Free (Hmac2);
+ }
+
+ return Status;
+}
+
+/**
+ Collect variable digest information.
+
+ This information is collected to be used to for integrity check.
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in] ContextIn Pointer to variable service context needed by
+ protected variable.
+ @param[in, out] DigestBuffer Base address of digest of each variable.
+ @param[out] DigestBufferSize Digest size of one variable if DigestBuffer
is NULL.
+ Size of DigestBuffer if DigestBuffer is NOT NULL.
+ @param[out] VariableNumber Number of valid variables.
+
+ @retval EFI_SUCCESS Successfully retreived variable digest.
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Unable to allocate memory.
+ @retval EFI_BUFFER_TOO_SMALL The DigestBufferSize pass in is too small.
+
+**/
+EFI_STATUS
+CollectVariableDigestInfo (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn,
+ IN OUT VOID *DigestBuffer OPTIONAL,
+ OUT UINT32 *DigestBufferSize OPTIONAL,
+ OUT UINT32 *VariableNumber OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ UINT32 VarNum;
+ UINT32 DigSize;
+ VARIABLE_DIGEST *VarDig;
+ EFI_TIME TimeStamp;
+ UNPROTECTED_VARIABLE_INDEX VarIndex;
+
+ //
+ // This function might be called before Global is initialized. In that case,
+ // Global must be NULL but not ContextIn.
+ //
+ if ((Global == NULL) && (ContextIn == NULL)) {
+ ASSERT (Global != NULL || ContextIn != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Global == NULL) && (DigestBuffer != NULL)) {
+ ASSERT (Global != NULL && DigestBuffer != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ( (DigestBuffer != NULL)
+ && ((DigestBufferSize == NULL) || (*DigestBufferSize == 0)))
+ {
+ ASSERT (
+ DigestBuffer != NULL
+ && DigestBufferSize != NULL && *DigestBufferSize > 0
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Global != NULL) && (ContextIn == NULL)) {
+ ContextIn = GET_CNTX (Global);
+ }
+
+ DigSize = 0;
+ VarNum = 0;
+ VarDig = NULL;
+
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+ VarInfo.StoreIndex = VAR_INDEX_INVALID; // To get the first variable.
+
+ if ((Global != NULL) &&
+ (Global->VariableCache != 0) &&
+ (Global->VariableCacheSize > 0))
+ {
+ //
+ // Use the variable cache to hold a copy of one variable.
+ //
+ VarInfo.Buffer = GET_BUFR (Global->VariableCache);
+ } else {
+ //
+ // Allocate a buffer to hold a copy of one variable
+ //
+ VarInfo.Buffer = AllocatePages (EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
+ if (VarInfo.Buffer == NULL) {
+ ASSERT (VarInfo.Buffer != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ if ((DigestBuffer != NULL) && (*DigestBufferSize > 0)) {
+ VarDig = DigestBuffer;
+ }
+
+ while (TRUE) {
+ if (VarDig != NULL) {
+ if (DigSize >= (*DigestBufferSize)) {
+ //
+ // Out of buffer.
+ //
+ break;
+ }
+
+ VarInfo.Header.VendorGuid = &VarDig->VendorGuid;
+ VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
+ VarInfo.Header.NameSize = (UINTN)DigestBuffer +
(UINTN)*DigestBufferSize
+ - (UINTN)VarInfo.Header.VariableName;
+ VarInfo.Header.TimeStamp = &TimeStamp;
+ VarInfo.Header.Data = NULL;
+ } else {
+ ZeroMem ((VOID *)&VarInfo.Header, sizeof (VarInfo.Header));
+ }
+
+ Status = ContextIn->GetNextVariableInfo (&VarInfo);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // Skip deleted variables.
+ //
+ if ( (VarInfo.Header.State != VAR_ADDED)
+ && (VarInfo.Header.State != (VAR_ADDED &
VAR_IN_DELETED_TRANSITION)))
+ {
+ continue;
+ }
+
+ if (Global != NULL) {
+ Global->Flags.Auth &= VarInfo.Flags.Auth;
+ }
+
+ VarNum += 1;
+ DigSize += (UINT32)(sizeof (VARIABLE_DIGEST)
+ + VarInfo.Header.NameSize
+ + METADATA_HMAC_SIZE);
+ if ((DigestBuffer != NULL) && (DigSize > *DigestBufferSize)) {
+ ASSERT (DigSize <= *DigestBufferSize);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ if (VarDig != NULL) {
+ VarDig->Prev = 0;
+ VarDig->Next = 0;
+ VarDig->State = VarInfo.Header.State;
+ VarDig->Attributes = VarInfo.Header.Attributes;
+ VarDig->DataSize = (UINT32)VarInfo.Header.DataSize;
+ VarDig->NameSize = (UINT16)VarInfo.Header.NameSize;
+ VarDig->DigestSize = METADATA_HMAC_SIZE;
+ VarDig->StoreIndex = VarInfo.StoreIndex;
+
+ if ((VarInfo.Buffer != NULL) && ((UINTN)VarInfo.Buffer != Global-
VariableCache)) {
+ VarDig->CacheIndex = GET_ADRS (VarInfo.Buffer);
+ } else {
+ VarDig->CacheIndex = VAR_INDEX_INVALID;
+ }
+
+ VarDig->Flags.Auth = VarInfo.Flags.Auth;
+ VarDig->Flags.Valid = TRUE;
+
+ VarIndex = CheckKnownUnprotectedVariable (Global, &VarInfo);
+ if (VarIndex >= UnprotectedVarIndexMax) {
+ //
+ // Check information relating to encryption, if enabled.
+ //
+ VarDig->Flags.Encrypted = FALSE;
+ if ((VarInfo.Header.Data != NULL) && (VarInfo.Header.DataSize > 0)) {
+ VarInfo.CipherData = NULL;
+ VarInfo.CipherDataSize = 0;
+ VarInfo.PlainData = NULL;
+ VarInfo.PlainDataSize = 0;
+ Status = GetCipherDataInfo (&VarInfo);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Discovered encrypted variable mark variable to be
+ // encrypted on the next SetVariable() operation
+ //
+ VarDig->Flags.Encrypted = PcdGetBool
(PcdProtectedVariableConfidentiality);
+ } else {
+ VarInfo.PlainData = VarInfo.Header.Data;
+ VarInfo.PlainDataSize = (UINT32)VarInfo.Header.DataSize;
+ VarInfo.CipherDataType = 0;
+ VarInfo.CipherHeaderSize = 0;
+ if (Status == EFI_NOT_FOUND) {
+ //
+ // Found variable that is not encrypted mark variable to be
+ // encrypted on the next SetVariable() operation
+ //
+ VarDig->Flags.Encrypted = PcdGetBool
(PcdProtectedVariableConfidentiality);
+ }
+ }
+ }
+
+ //
+ // Variable is protected
+ //
+ VarDig->Flags.Protected = PcdGetBool (PcdProtectedVariableIntegrity);
+ VarDig->PlainDataSize = VarInfo.PlainDataSize;
+
+ //
+ // Calculate digest only for protected variable.
+ //
+ Status = GetVariableDigest (Global, &VarInfo, VAR_DIG_VALUE (VarDig));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Keep the VarDig in an ordered list.
+ //
+ InsertVariableDigestNode (Global, VarDig, CompareVariableDigestInfo);
+ } else {
+ VarDig->Flags.Protected = FALSE;
+ VarDig->Flags.Encrypted = FALSE;
+ VarDig->PlainDataSize = VarDig->DataSize;
+
+ //
+ // Make use of VARIABLE_DIGEST->DigestValue to cache HMAC value
from
+ // MetaDataHmacVar, which doesn't need a digest value (only protected
+ // variables need it for integrity check).
+ //
+ if ((VarIndex == IndexHmacInDel) || (VarIndex == IndexHmacAdded)) {
+ if (VarDig->State == VAR_ADDED) {
+ VarIndex = IndexHmacAdded;
+ } else {
+ VarIndex = IndexHmacInDel;
+ }
+ }
+
+ Global->Unprotected[VarIndex] = VAR_DIG_ADR (VarDig);
+
+ if ((VarInfo.Header.Data != NULL) && (VarDig->DataSize <= VarDig-
DigestSize)) {
+ CopyMem (VAR_DIG_VALUE (VarDig), VarInfo.Header.Data, VarDig-
DataSize);
+ }
+
+ //
+ // Don't add the VarDig for MetaDataHmacVar into the linked list now.
+ // Do it after the HMAC has been validated.
+ //
+ if ((VarIndex != IndexHmacInDel) || (VarIndex != IndexHmacAdded)) {
+ InsertVariableDigestNode (Global, VarDig, CompareVariableDigestInfo);
+ }
+ }
+
+ VarDig = (VARIABLE_DIGEST *)((UINTN)VarDig + VAR_DIG_END (VarDig));
+ }
+ }
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ return Status;
+ }
+
+ if (DigestBufferSize != NULL) {
+ *DigestBufferSize = DigSize;
+ }
+
+ if (VariableNumber != NULL) {
+ *VariableNumber = VarNum;
+ }
+
+ if ((Global == NULL) && (VarInfo.Buffer != NULL)) {
+ //
+ // Free Buffer
+ //
+ FreePages (VarInfo.Buffer, EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Perform for protected variable integrity check.
+
+ If this initialization failed upon any error, the whole variable services
+ should not be used. A system reset might be needed to re-construct NV
+ variable storage to be the default state.
+
+ @param[in] ContextIn Pointer to variable service context needed by
+ protected variable.
+
+ @retval EFI_SUCCESS Protected variable services are ready.
+ @retval EFI_INVALID_PARAMETER If ContextIn == NULL or something
missing or
+ mismatching in the content in ContextIn.
+ @retval EFI_COMPROMISED_DATA If failed to check integrity of protected
variables.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_UNSUPPORTED Unsupported to process protected variable.
+
+**/
+EFI_STATUS
+EFIAPI
+PerformVariableIntegrityCheck (
+ IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
+ )
+{
+ EFI_STATUS Status;
+ UINT32 HobDataSize;
+ UINT32 VarNumber;
+ VOID *Buffer;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *DigBuffer;
+ UINT32 DigBufferSize;
+ UINT32 HmacMetaDataSize;
+ UINTN Index;
+ BOOLEAN PreviousKey;
+ EFI_HOB_GUID_TYPE *GuidHob;
+
+ if ((ContextIn == NULL) || (ContextIn->GetNextVariableInfo == NULL)) {
+ ASSERT (ContextIn != NULL);
+ ASSERT (ContextIn->GetNextVariableInfo != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ContextIn->StructSize = (ContextIn->StructSize == 0) ? sizeof (*ContextIn)
+ : ContextIn->StructSize;
+
+ //
+ // Enumerate all variables first to collect info for resource allocation.
+ //
+ DigBufferSize = 0;
+ Status = CollectVariableDigestInfo (
+ NULL,
+ ContextIn,
+ NULL,
+ &DigBufferSize,
+ &VarNumber
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ //
+ // Allocate buffer for Global. Memory layout:
+ //
+ // Global
+ // Digest context
+ // Variable Digest List
+ // HmacMetaData
+ //
+ // To save precious NEM space of processor, variable cache will not be
+ // allocated at this point until physical memory is ready for use.
+ //
+ HmacMetaDataSize = (UINT32)GetMetaDataHmacVarSize (TRUE);
+ HobDataSize = sizeof (PROTECTED_VARIABLE_GLOBAL)
+ + (UINT32)DIGEST_CONTEXT_SIZE
+ + DigBufferSize
+ + HmacMetaDataSize;
+ Buffer = AllocateGlobalBuffer (HobDataSize, FALSE);
+ if (Buffer == NULL) {
+ ASSERT (Buffer != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Global = (PROTECTED_VARIABLE_GLOBAL *)((UINTN)Buffer);
+ Global->DigestContext = GET_ADRS (Global + 1);
+
+ if (DigBufferSize > 0) {
+ DigBuffer = (VARIABLE_DIGEST *)(UINTN)(Global->DigestContext +
DIGEST_CONTEXT_SIZE);
+ ZeroMem (DigBuffer, DigBufferSize);
+ } else {
+ DigBuffer = NULL;
+ }
+
+ //
+ // Keep a copy of ContextIn in HOB for later uses.
+ //
+ Global->GlobalSelf = GET_ADRS (Global);
+ Global->ContextIn = GET_ADRS (ContextIn);
+
+ Global->StructVersion =
PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION;
+ Global->StructSize = HobDataSize;
+
+ Global->VariableNumber = VarNumber;
+ Global->VariableDigests = 0;
+
+ Global->Flags.Auth = TRUE;
+ Global->Flags.WriteInit = FALSE;
+ Global->Flags.WriteReady = FALSE;
+
+ GuidHob = GetFirstGuidHob (&gEfiAuthenticatedVariableGuid);
+ if (GuidHob == NULL) {
+ GuidHob = GetFirstGuidHob (&gEfiVariableGuid);
+ if (GuidHob != NULL) {
+ Global->Flags.Auth = FALSE;
+ }
+ }
+
+ Global->Flags.RecoveryMode = (GuidHob != NULL);
+
+ //
+ // Before physical memory is ready, we cannot cache all variables in the very
+ // limited NEM space. But we still need to reserve buffer to hold data of
+ // one variable as well as context for integrity check (HMAC calculation).
+ //
+ Global->VariableCacheSize = MAX_VARIABLE_SIZE;
+ Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Global-
VariableCacheSize));
+ if (Buffer == NULL) {
+ ASSERT (Buffer != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Global->VariableCache = GET_ADRS (Buffer);
+ Global->LastAccessedVariable = VAR_INDEX_INVALID;
+
+ for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
+ Global->Unprotected[Index] = VAR_INDEX_INVALID;
+ }
+
+ //
+ // Re-enumerate all NV variables and build digest list.
+ //
+ Status = CollectVariableDigestInfo (
+ Global,
+ ContextIn,
+ DigBuffer,
+ &DigBufferSize,
+ &VarNumber
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ ASSERT (Global->VariableNumber == VarNumber);
+
+ //
+ // Fix-up number of valid protected variables (i.e. exclude unprotected ones)
+ //
+ for (Index = 0; VarNumber != 0 && Index < UnprotectedVarIndexMax; ++Index)
{
+ if (Global->Unprotected[Index] != VAR_INDEX_INVALID) {
+ --VarNumber;
+ }
+ }
+
+ //
+ // Get root key and generate HMAC key.
+ //
+ PreviousKey = FALSE;
+ Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global-
RootKey));
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ Status = EFI_COMPROMISED_DATA;
+ }
+
+ //
+ // Derive the MetaDataHmacKey from root key
+ //
+ if (!GenerateMetaDataHmacKey (
+ Global->RootKey,
+ sizeof (Global->RootKey),
+ Global->MetaDataHmacKey,
+ sizeof (Global->MetaDataHmacKey)
+ ))
+ {
+ ASSERT (FALSE);
+ Status = EFI_COMPROMISED_DATA;
+ }
+
+ //
+ // Check the integrity of all NV variables, if any.
+ //
+ if (( (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
+ || (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID)))
+ {
+ //
+ // Validate the HMAC stored in variable MetaDataHmacVar.
+ //
+ Status = VerifyMetaDataHmac (Global);
+ if (EFI_ERROR (Status)) {
+ //
+ // Try again with the previous root key if the latest key failed the HMAC
validation.
+ //
+ Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global-
RootKey));
+ if (!EFI_ERROR (Status)) {
+ //
+ // Derive the MetaDataHmacKey from previous root key
+ //
+ if (GenerateMetaDataHmacKey (
+ Global->RootKey,
+ sizeof (Global->RootKey),
+ Global->MetaDataHmacKey,
+ sizeof (Global->MetaDataHmacKey)
+ ) == TRUE)
+ {
+ //
+ // Validate the HMAC stored in variable MetaDataHmacVar.
+ //
+ Status = VerifyMetaDataHmac (Global);
+ if (!EFI_ERROR (Status)) {
+ Status = EFI_COMPROMISED_DATA;
+ }
+ } else {
+ Status = EFI_COMPROMISED_DATA;
+ }
+ }
+ }
+ } else if (Global->Flags.RecoveryMode) {
+ //
+ // Generate the first version of MetaDataHmacVar.
+ //
+ Status = SyncRpmcCounter ();
+ if (!EFI_ERROR (Status)) {
+ Status = RefreshVariableMetadataHmac (Global, NULL, NULL);
+ if (!EFI_ERROR (Status)) {
+ //
+ // MetaDataHmacVar is always calculated against Counter2+1. Updating
+ // RPMCs to match it.
+ //
+ (VOID)IncrementMonotonicCounter (RPMC_COUNTER_1);
+ (VOID)IncrementMonotonicCounter (RPMC_COUNTER_2);
+ }
+ }
+ } else if ((VarNumber > 0) && !Global->Flags.RecoveryMode) {
+ //
+ // There's no MetaDataHmacVar found for protected variables. Suppose
+ // the variable storage is compromised.
+ //
+ Status = EFI_COMPROMISED_DATA;
+ }
+
+ if (EFI_ERROR (Status)) {
+ //
+ // The integrity of variables have been compromised. The platform has to do
+ // something to recover the variable store. But the boot should not go on
+ // anyway this time.
+ //
+ DEBUG ((DEBUG_ERROR, "%a: %d Integrity check Status = %r\n",
__FUNCTION__, __LINE__, Status));
+ REPORT_STATUS_CODE (
+ EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
+ (PcdGet32 (PcdStatusCodeVariableIntegrity) |
EFI_SW_PEI_PC_RECOVERY_BEGIN)
+ );
+ #if defined (EDKII_UNIT_TEST_FRAMEWORK_ENABLED) // Avoid test
malfunctioning.
+ return Status;
+ #else
+ ASSERT_EFI_ERROR (Status);
+ CpuDeadLoop ();
+ #endif
+ }
+
+ //
+ // Everything's OK.
+ //
+ REPORT_STATUS_CODE (
+ EFI_PROGRESS_CODE,
+ PcdGet32 (PcdStatusCodeVariableIntegrity)
+ );
+
+ if (GET_BUFR (Global->VariableCacheSize) != NULL) {
+ //
+ // Free Buffer
+ //
+ FreePages (Buffer, EFI_SIZE_TO_PAGES (Global->VariableCacheSize));
+ }
+
+ //
+ // Keep the valid MetaDataHmacVar in the list.
+ //
+ for (Index = 0; Index < IndexPlatformVar; ++Index) {
+ if ( (Global->Unprotected[Index] != VAR_INDEX_INVALID)
+ && VAR_DIG_PTR (Global->Unprotected[Index])->Flags.Valid)
+ {
+ InsertVariableDigestNode (
+ Global,
+ VAR_DIG_PTR (Global->Unprotected[Index]),
+ NULL
+ );
+ }
+ }
+
+ //
+ // Restore the key to the latest one.
+ //
+ if (PreviousKey) {
+ Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global->RootKey));
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Derive the MetaDataHmacKey from root key
+ //
+ if (!GenerateMetaDataHmacKey (
+ Global->RootKey,
+ sizeof (Global->RootKey),
+ Global->MetaDataHmacKey,
+ sizeof (Global->MetaDataHmacKey)
+ ))
+ {
+ ASSERT (FALSE);
+ }
+ }
+
+ //
+ // Make sure that the RPMC counter is in-sync.
+ //
+ Status = SyncRpmcCounter ();
+
+ //
+ // Setup a hook to migrate data in Global once physical memory is ready.
+ //
+ Status = PeiServicesNotifyPpi (mPostMemNotifyList);
+
+ return Status;
+}
+
+/**
+
+ Initialization for protected variable services.
+
+ If the variable store is available than perform integrity check.
+ Otherwise, defer integrity check until variable store is available.
+
+
+ @param[in] ContextIn Pointer to variable service context needed by
+ protected variable.
+
+ @retval EFI_SUCCESS Protected variable services are ready.
+ @retval EFI_INVALID_PARAMETER If ContextIn == NULL or something
missing or
+ mismatching in the content in ContextIn.
+ @retval EFI_COMPROMISED_DATA If failed to check integrity of protected
variables.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_UNSUPPORTED Unsupported to process protected variable.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibInitialize (
+ IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
+ )
+{
+ EFI_STATUS Status;
+ VOID *ContextInHob;
+ PROTECTED_VARIABLE_INFO VarInfo;
+
+ if ((ContextIn == NULL) || (ContextIn->GetNextVariableInfo == NULL)) {
+ ASSERT (ContextIn != NULL);
+ ASSERT (ContextIn->GetNextVariableInfo != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Keep a copy of ContextIn in HOB for later uses.
+ //
+ ContextIn->StructSize = (ContextIn->StructSize == 0) ? sizeof (*ContextIn)
+ : ContextIn->StructSize;
+ ContextInHob = BuildGuidHob (&gEdkiiProtectedVariableContextGuid,
ContextIn->StructSize);
+ CopyMem (ContextInHob, ContextIn, ContextIn->StructSize);
+
+ //
+ // Discover if Variable Store Info Hob has been published by platform driver.
+ // It contains information regards to HOB or NV Variable Store availability
+ //
+ ZeroMem ((VOID *)&VarInfo.Header, sizeof (VarInfo.Header));
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarInfo.Buffer = AllocatePages (EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
+ if (VarInfo.Buffer == NULL) {
+ ASSERT (VarInfo.Buffer != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ FreePages (VarInfo.Buffer, EFI_SIZE_TO_PAGES ((MAX_VARIABLE_SIZE)));
+
+ Status = ContextIn->GetNextVariableInfo (&VarInfo);
+ if (EFI_ERROR (Status)) {
+ //
+ // Register for platform driver callback when Variable Store is available.
+ //
+ DEBUG ((DEBUG_INFO, "Variable Store is not available. Register for a
integrity check callback\n"));
+ Status = PeiServicesNotifyPpi (mVariableStoreNotifyList);
+ return Status;
+ }
+
+ //
+ // HOB Variable store is not available
+ // Assume NV Variable store is available instead
+ // Perform integrity check on NV Variable Store
+ //
+ DEBUG ((DEBUG_INFO, "NV Variable Store is available. Perform integrity
check\n"));
+ Status = PerformVariableIntegrityCheck (ContextInHob);
+ return Status;
+}
+
+/**
+
+ Prepare for variable update.
+
+ (Not suppported in PEI phase.)
+
+ @retval EFI_UNSUPPORTED Updating variable is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteInit (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+
+ Update a variable with protection provided by this library.
+
+ Not supported in PEI phase.
+
+ @param[in,out] CurrVariable Variable to be updated. It's NULL if
+ adding a new variable.
+ @param[in] CurrVariableInDel In-delete-transition copy of updating
variable.
+ @param[in,out] NewVariable Buffer of new variable data.
+ Buffer of "MetaDataHmacVar" and new
+ variable (encrypted).
+ @param[in,out] NewVariableSize Size of NewVariable.
+ Size of (encrypted) NewVariable and
+ "MetaDataHmacVar".
+
+ @retval EFI_UNSUPPORTED Not support updating variable in PEI phase.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibUpdate (
+ IN OUT VARIABLE_HEADER *CurrVariable,
+ IN VARIABLE_HEADER *CurrVariableInDel,
+ IN OUT VARIABLE_HEADER *NewVariable,
+ IN OUT UINTN *NewVariableSize
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+
+ Finalize a variable updating after it's written to NV variable storage
+ successfully.
+
+ @param[in] NewVariable Buffer of new variables and
MetaDataHmacVar.
+ @param[in] VariableSize Size of buffer pointed by NewVariable.
+ @param[in] StoreIndex StoreIndex to NV variable storage from where
the new
+ variable and MetaDataHmacVar have been written.
+
+ @retval EFI_UNSUPPORTED Not support updating variable in PEI phase.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteFinal (
+ IN VARIABLE_HEADER *NewVariable,
+ IN UINTN VariableSize,
+ IN UINT64 StoreIndex
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
new file mode 100644
index 000000000000..8e964f4cd28d
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
@@ -0,0 +1,209 @@
+/** @file
+ Implemention of ProtectedVariableLib for SMM variable services.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Uefi.h>
+
+#include "Guid/SmmVariableCommon.h"
+
+#include "Library/MmServicesTableLib.h"
+#include "Library/MemoryAllocationLib.h"
+
+#include "ProtectedVariableInternal.h"
+
+PROTECTED_VARIABLE_CONTEXT_IN mVariableContextIn = {
+ PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
+ sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
+ 0,
+ FromSmmModule,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PROTECTED_VARIABLE_GLOBAL mProtectedVariableGlobal = {
+ PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
+ sizeof (PROTECTED_VARIABLE_GLOBAL),
+ { 0 },
+ { 0 },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ { 0, 0, 0 },
+ 0,
+ 0,
+ { 0, 0, 0, 0}
+};
+
+/**
+
+ Callback function to call variable write.
+
+ @param[in] Protocol Not Used.
+ @param[in] Interface Not Used.
+ @param[in] Handle Not Used.
+
+ @retval EFI_SUCCESS Protected variable write successful.
+ @retval others Protected variable write failed.
+
+**/
+EFI_STATUS
+EFIAPI
+VariableWriteProtocolCallback (
+ IN CONST EFI_GUID *Protocol,
+ IN VOID *Interface,
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+
+ Status = ProtectedVariableLibWriteInit ();
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Initialization for protected variable services.
+
+ If this initialization failed upon any error, the whole variable services
+ should not be used. A system reset might be needed to re-construct NV
+ variable storage to be the default state.
+
+ @param[in] ContextIn Pointer to variable service context needed by
+ protected variable.
+
+ @retval EFI_SUCCESS Protected variable services are ready.
+ @retval EFI_INVALID_PARAMETER If ContextIn == NULL or something
missing or
+ mismatching in the content in ContextIn.
+ @retval EFI_COMPROMISED_DATA If failed to check integrity of protected
variables.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.
+ @retval EFI_UNSUPPORTED Unsupported to process protected variable.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibInitialize (
+ IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_CONTEXT_IN *ProtectedVarContext;
+ PROTECTED_VARIABLE_GLOBAL *OldGlobal;
+ PROTECTED_VARIABLE_GLOBAL *NewGlobal;
+ VARIABLE_DIGEST *VarDig;
+ VARIABLE_DIGEST *NewVarDig;
+ EFI_PHYSICAL_ADDRESS NewCacheIndex;
+ UINTN VarSize;
+ UNPROTECTED_VARIABLE_INDEX Index;
+
+ if ( (ContextIn == NULL)
+ || (ContextIn->StructVersion !=
PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
+ || (ContextIn->StructSize != sizeof (PROTECTED_VARIABLE_CONTEXT_IN))
+ || (ContextIn->GetVariableInfo == NULL)
+ || (ContextIn->GetNextVariableInfo == NULL)
+ || (ContextIn->UpdateVariableStore == NULL)
+ || (ContextIn->UpdateVariable == NULL))
+ {
+ ASSERT (ContextIn != NULL);
+ ASSERT (ContextIn->StructVersion ==
PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION);
+ ASSERT (ContextIn->StructSize == sizeof
(PROTECTED_VARIABLE_CONTEXT_IN));
+ ASSERT (ContextIn->GetVariableInfo != NULL);
+ ASSERT (ContextIn->GetNextVariableInfo != NULL);
+ ASSERT (ContextIn->UpdateVariableStore != NULL);
+ ASSERT (ContextIn->UpdateVariable != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ GetProtectedVariableGlobal (&NewGlobal);
+ ProtectedVarContext = GET_CNTX (NewGlobal);
+ CopyMem (ProtectedVarContext, ContextIn, sizeof (mVariableContextIn));
+ ProtectedVarContext->VariableServiceUser = FromSmmModule;
+
+ //
+ // Get root key and HMAC key from HOB created by PEI variable driver.
+ //
+ Status = GetProtectedVariableGlobalFromHob (&OldGlobal);
+ ASSERT_EFI_ERROR (Status);
+
+ CopyMem ((VOID *)NewGlobal, (CONST VOID *)OldGlobal, sizeof
(*OldGlobal));
+
+ //
+ // The keys must not be available outside SMM.
+ //
+ if (ProtectedVarContext->VariableServiceUser == FromSmmModule) {
+ ZeroMem (OldGlobal->RootKey, sizeof (OldGlobal->RootKey));
+ ZeroMem (OldGlobal->MetaDataHmacKey, sizeof (OldGlobal-
MetaDataHmacKey));
+ }
+
+ NewGlobal->Flags.WriteInit = FALSE;
+ NewGlobal->Flags.WriteReady = FALSE;
+ NewGlobal->LastAccessedVariable = 0;
+ NewGlobal->VariableCache = GET_ADRS (AllocateZeroPool
(MAX_VARIABLE_SIZE));
+ NewGlobal->DigestContext = GET_ADRS (AllocateZeroPool
(DIGEST_CONTEXT_SIZE));
+ if (NewGlobal->DigestContext == 0) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Copy over variable from HOB to SMM memory
+ //
+ NewGlobal->VariableDigests = 0;
+ VarDig = VAR_DIG_PTR (OldGlobal->VariableDigests);
+ while (VarDig != NULL) {
+ //
+ // Allocate new Var Digest in SMM memory
+ //
+ NewVarDig = (VARIABLE_DIGEST *)AllocateZeroPool (
+ sizeof (VARIABLE_DIGEST) + VarDig->NameSize +
METADATA_HMAC_SIZE
+ );
+ if (NewVarDig == NULL) {
+ ASSERT (NewVarDig != NULL);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (NewVarDig, VarDig, sizeof (VARIABLE_DIGEST));
+ NewVarDig->Prev = 0;
+ NewVarDig->Next = 0;
+
+ CopyMem (VAR_DIG_NAME (NewVarDig), VAR_DIG_NAME (VarDig), VarDig-
NameSize);
+ CopyMem (VAR_DIG_VALUE (NewVarDig), VAR_DIG_VALUE (VarDig),
VarDig->DigestSize);
+
+ VarSize = VARIABLE_HEADER_SIZE (NewGlobal->Flags.Auth);
+ VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
+ VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+
+ NewCacheIndex = GET_ADRS (AllocateZeroPool (VarSize));
+ if (GET_BUFR (NewCacheIndex) == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (GET_BUFR (NewCacheIndex), GET_BUFR (VarDig->CacheIndex),
VarSize);
+ NewVarDig->CacheIndex = NewCacheIndex;
+ NewVarDig->Flags.Freeable = TRUE;
+
+ for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
+ if (OldGlobal->Unprotected[Index] == VAR_DIG_ADR (VarDig)) {
+ NewGlobal->Unprotected[Index] = VAR_DIG_ADR (NewVarDig);
+ }
+ }
+
+ InsertVariableDigestNode (NewGlobal, NewVarDig, NULL);
+
+ VarDig = VAR_DIG_NEXT (VarDig);
+ }
+
+ return Status;
+}
diff --git
a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
.c
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
.c
new file mode 100644
index 000000000000..8472fc8a33c7
--- /dev/null
+++
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
.c
@@ -0,0 +1,967 @@
+/** @file
+ Implemention of ProtectedVariableLib for SMM variable services.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Uefi.h>
+
+#include "Guid/SmmVariableCommon.h"
+
+#include "Library/MmServicesTableLib.h"
+#include "Library/MemoryAllocationLib.h"
+#include <Library/HobLib.h>
+
+#include "ProtectedVariableInternal.h"
+
+/**
+
+ Get context and/or global data structure used to process protected variable.
+
+ @param[out] Global Pointer to global configuration data.
+
+ @retval EFI_SUCCESS Get requested structure successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProtectedVariableGlobal (
+ OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
+ )
+{
+ if (Global != NULL) {
+ mProtectedVariableGlobal.ContextIn = GET_ADRS (&mVariableContextIn);
+ *Global = &mProtectedVariableGlobal;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Encrypt given variable data and generate new HMAC value against it.
+
+ @param[in] Global Pointer to global configuration data.
+ @param[in,out] NewVarInfo Pointer to buffer of new variable data.
+ @param[in,out] NewVarDig Pointer to buffer of new variable digest.
+
+ @retval EFI_SUCCESS No error occurred during the encryption and HMC
calculation.
+ @retval EFI_ABORTED Failed to do HMC calculation.
+ @return EFI_OUT_OF_RESOURCES Not enough resource to calculate HMC
value.
+ @return EFI_NOT_FOUND The MetaDataHmacVar was not found in
storage.
+
+**/
+STATIC
+EFI_STATUS
+UpdateVariableInternal (
+ IN PROTECTED_VARIABLE_GLOBAL *Global,
+ IN OUT PROTECTED_VARIABLE_INFO *NewVarInfo,
+ IN OUT VARIABLE_DIGEST *NewVarDig
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_INFO CachedVarInfo;
+ VOID *Buffer;
+ UINTN VarSize;
+
+ if ((NewVarInfo == NULL) || (NewVarDig == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If Add or update variable, encrypt new data first.
+ //
+ if (NewVarInfo->Buffer != NULL) {
+ Status = EFI_UNSUPPORTED;
+
+ if (NewVarDig->Flags.Encrypted) {
+ NewVarInfo->PlainData = NULL;
+ NewVarInfo->PlainDataSize = 0;
+ NewVarInfo->CipherData = NULL;
+ NewVarInfo->CipherDataSize = 0;
+ NewVarInfo->Key = Global->RootKey;
+ NewVarInfo->KeySize = sizeof (Global->RootKey);
+ NewVarInfo->Header.Attributes &= (~EFI_VARIABLE_APPEND_WRITE);
+ Status = EncryptVariable (NewVarInfo);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Update new data size in variable header.
+ //
+ SET_VARIABLE_DATA_SIZE (NewVarInfo, NewVarInfo->CipherDataSize);
+ } else if (Status != EFI_UNSUPPORTED) {
+ ASSERT (FALSE);
+ return Status;
+ }
+ }
+
+ if (Status == EFI_UNSUPPORTED) {
+ NewVarInfo->CipherData = NewVarInfo->Header.Data;
+ NewVarInfo->CipherDataSize = (UINT32)NewVarInfo->Header.DataSize;
+ NewVarInfo->PlainData = NULL;
+ NewVarInfo->PlainDataSize = 0;
+ }
+ } else {
+ NewVarInfo->CipherData = NULL;
+ NewVarInfo->CipherDataSize = 0;
+ NewVarInfo->PlainData = NULL;
+ NewVarInfo->PlainDataSize = 0;
+ }
+
+ if (NewVarDig->CacheIndex != 0) {
+ //
+ // Update the cached copy.
+ //
+ ZeroMem ((VOID *)&CachedVarInfo, sizeof (CachedVarInfo));
+ CachedVarInfo.Buffer = GET_BUFR (NewVarDig->CacheIndex);
+ CachedVarInfo.StoreIndex = VAR_INDEX_INVALID;
+ CachedVarInfo.Flags.Auth = NewVarInfo->Flags.Auth;
+
+ Status = GET_CNTX (Global)->GetVariableInfo (&CachedVarInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ if ((CachedVarInfo.Header.DataSize != 0) && (NewVarInfo->CipherDataSize >
CachedVarInfo.Header.DataSize)) {
+ //
+ // allocate new VarInfo buffer that is of greater CipherDataSize
+ //
+ VarSize = VARIABLE_HEADER_SIZE (NewVarDig->Flags.Auth);
+ VarSize += NewVarInfo->Header.NameSize + GET_PAD_SIZE (NewVarInfo-
Header.NameSize);
+ VarSize += NewVarInfo->CipherDataSize + GET_PAD_SIZE (NewVarInfo-
CipherDataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+ Buffer = AllocateZeroPool (VarSize);
+ if (Buffer != NULL) {
+ VarSize = VARIABLE_HEADER_SIZE (NewVarDig->Flags.Auth);
+ VarSize += CachedVarInfo.Header.NameSize + GET_PAD_SIZE
(CachedVarInfo.Header.NameSize);
+ VarSize += CachedVarInfo.Header.DataSize + GET_PAD_SIZE
(CachedVarInfo.DataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+
+ CopyMem (
+ Buffer,
+ CachedVarInfo.Buffer,
+ VarSize
+ );
+
+ FreePool (CachedVarInfo.Buffer);
+
+ //
+ // Update the cached copy.
+ //
+ ZeroMem ((VOID *)&CachedVarInfo, sizeof (CachedVarInfo));
+ CachedVarInfo.Buffer = Buffer;
+ CachedVarInfo.StoreIndex = VAR_INDEX_INVALID;
+ CachedVarInfo.Flags.Auth = NewVarInfo->Flags.Auth;
+ Status = GET_CNTX (Global)->GetVariableInfo (&CachedVarInfo);
+ ASSERT_EFI_ERROR (Status);
+ NewVarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
+ }
+ }
+
+ CopyMem (
+ CachedVarInfo.Header.Data,
+ NewVarInfo->CipherData,
+ NewVarInfo->CipherDataSize
+ );
+ SET_VARIABLE_DATA_SIZE (&CachedVarInfo, NewVarInfo->CipherDataSize);
+
+ NewVarDig->State = VAR_ADDED;
+ NewVarDig->DataSize = NewVarInfo->CipherDataSize;
+
+ if (NewVarInfo->PlainDataSize > 0) {
+ NewVarDig->PlainDataSize = NewVarInfo->PlainDataSize;
+ } else {
+ NewVarDig->PlainDataSize = NewVarDig->DataSize;
+ }
+
+ //
+ // (Re-)Calculate the hash of the variable.
+ //
+ if (NewVarDig->Flags.Protected) {
+ GetVariableDigest (Global, NewVarInfo, VAR_DIG_VALUE (NewVarDig));
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Fix state of MetaDataHmacVar on NV variable storage, if there's failure at
+ last boot during updating variable.
+
+ This must be done before the first writing of variable in current boot,
+ including storage reclaim.
+
+ @retval EFI_UNSUPPORTED Updating NV variable storage is not
supported.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to complete the
operation.
+ @retval EFI_SUCCESS Variable store was successfully updated.
+
+**/
+EFI_STATUS
+FixupHmacVariable (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_INFO HmacVarInfo;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ VARIABLE_DIGEST *VarDig;
+ UINTN Index;
+
+ Status = GetProtectedVariableGlobal (&Global);
+ ASSERT_EFI_ERROR (Status);
+
+ if (Global->Flags.WriteReady) {
+ return EFI_SUCCESS;
+ }
+
+ ContextIn = GET_CNTX (Global);
+
+ //
+ // Delete invalid MetaDataHmacVar.
+ //
+ for (Index = 0; Index <= IndexHmacAdded; ++Index) {
+ if (Global->Unprotected[Index] == VAR_INDEX_INVALID) {
+ continue;
+ }
+
+ VarDig = VAR_DIG_PTR (Global->Unprotected[Index]);
+ if (VarDig->Flags.Valid) {
+ continue;
+ }
+
+ ZeroMem ((VOID *)&HmacVarInfo, sizeof (HmacVarInfo));
+ HmacVarInfo.StoreIndex = VarDig->StoreIndex;
+ HmacVarInfo.Flags.Auth = VarDig->Flags.Auth;
+
+ Status = ContextIn->GetVariableInfo (&HmacVarInfo);
+ if (!EFI_ERROR (Status) && (HmacVarInfo.Buffer != NULL)) {
+ HmacVarInfo.Buffer->State &= VAR_DELETED;
+ Status = ContextIn->UpdateVariableStore (
+ &HmacVarInfo,
+ OFFSET_OF (VARIABLE_HEADER, State),
+ sizeof (HmacVarInfo.Buffer->State),
+ &HmacVarInfo.Buffer->State
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ //
+ // Release the resource and update related states.
+ //
+ VarDig->State &= VAR_DELETED;
+ RemoveVariableDigestNode (Global, VarDig, FALSE);
+ Global->Unprotected[Index] = VAR_INDEX_INVALID;
+ }
+
+ //
+ // There should be no MetaDataHmacVar if in variable storage recovery mode.
+ //
+ if (Global->Flags.RecoveryMode) {
+ ASSERT (Global->Unprotected[IndexHmacAdded] == VAR_INDEX_INVALID);
+ ASSERT (Global->Unprotected[IndexHmacInDel] == VAR_INDEX_INVALID);
+ }
+
+ Global->Flags.WriteReady = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Prepare for variable update.
+
+ This is needed only once during current boot to mitigate replay attack. Its
+ major job is to advance RPMC (Replay Protected Monotonic Counter).
+
+ @retval EFI_SUCCESS Variable is ready to update hereafter.
+ @retval EFI_UNSUPPORTED Updating variable is not supported.
+ @retval EFI_DEVICE_ERROR Error in advancing RPMC.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteInit (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ PROTECTED_VARIABLE_INFO VarInfo;
+
+ (VOID)GetProtectedVariableGlobal (&Global);
+ ContextIn = GET_CNTX (Global);
+
+ //
+ // HmacVarInfo should be here
+ //
+ if (Global->Flags.RecoveryMode) {
+ //
+ // Flush default variables to variable storage if in variable recovery mode.
+ //
+ Status = ContextIn->UpdateVariableStore (NULL, 0, (UINT32)-1, NULL);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ } else {
+ ContextIn = GET_CNTX (Global);
+
+ //
+ // Fix any wrong MetaDataHmacVar information before adding new one.
+ //
+ Status = FixupHmacVariable ();
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ if (!Global->Flags.WriteReady) {
+ return EFI_NOT_READY;
+ }
+
+ //
+ // Refresh MetaDataHmacVar with RPMC2 by 1 in each boot before any
variable
+ // update, by deleting (attr == 0 && datasize == 0) the old one.
+ //
+ ZeroMem (&VarInfo, sizeof (PROTECTED_VARIABLE_INFO)); // Zero attr &
datasize
+
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+ VarInfo.Header.VariableName = METADATA_HMAC_VARIABLE_NAME;
+ VarInfo.Header.NameSize = METADATA_HMAC_VARIABLE_NAME_SIZE;
+ VarInfo.Header.VendorGuid = &METADATA_HMAC_VARIABLE_GUID;
+
+ //
+ // Pretend to delete MetaDataHmacVar.
+ //
+ Status = ContextIn->UpdateVariable (&VarInfo.Header);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ mProtectedVariableGlobal.Flags.WriteInit = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Update a variable with protection provided by this library.
+
+ If variable encryption is employed, the new variable data will be encrypted
+ before being written to NV variable storage.
+
+ A special variable, called "MetaDataHmacVar", will always be updated along
+ with variable being updated to reflect the changes (HMAC value) of all
+ protected valid variables. The only exceptions, currently, is variable
+ variable "VarErrorLog".
+
+ The buffer passed by NewVariable must be double of maximum variable size,
+ which allows to pass the "MetaDataHmacVar" back to caller along with
encrypted
+ new variable data, if any. This can make sure the new variable data and
+ "MetaDataHmacVar" can be written at almost the same time to reduce the
chance
+ of compromising the integrity.
+
+ If *NewVariableSize is zero, it means to delete variable passed by CurrVariable
+ and/or CurrVariableInDel. "MetaDataHmacVar" will be updated as well in such
+ case because of less variables in storage. NewVariable should be always
passed
+ in to convey new "MetaDataHmacVar" back.
+
+ @param[in,out] CurrVariable Variable to be updated. It's NULL if
+ adding a new variable.
+ @param[in] CurrVariableInDel In-delete-transition copy of updating
variable.
+ @param[in,out] NewVariable Buffer of new variable data.
+ Buffer of "MetaDataHmacVar" and new
+ variable (encrypted).
+ @param[in,out] NewVariableSize Size of NewVariable.
+ Size of (encrypted) NewVariable and
+ "MetaDataHmacVar".
+
+ @retval EFI_SUCCESS The variable is updated with protection
successfully.
+ @retval EFI_INVALID_PARAMETER NewVariable is NULL.
+ @retval EFI_NOT_FOUND Information missing to finish the operation.
+ @retval EFI_ABORTED Failed to encrypt variable or calculate HMAC.
+ @retval EFI_NOT_READY The RPMC device is not yet initialized.
+ @retval EFI_DEVICE_ERROR The RPMC device has error in updating.
+ @retval EFI_ACCESS_DENIED The given variable is not allowed to update.
+ Currently this only happens on updating
+ "MetaDataHmacVar" from code outside of this
+ library.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibUpdate (
+ IN OUT VARIABLE_HEADER *CurrVariable,
+ IN VARIABLE_HEADER *CurrVariableInDel,
+ IN OUT VARIABLE_HEADER *NewVariable,
+ IN OUT UINTN *NewVariableSize
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ VARIABLE_DIGEST *VarDig;
+ VARIABLE_DIGEST *CurrVarDig;
+ VARIABLE_DIGEST *NewVarDig;
+ PROTECTED_VARIABLE_INFO NewVarInfo;
+ PROTECTED_VARIABLE_INFO NewHmacVarInfo;
+ UINTN VarSize;
+ UINT64 UnprotectedVarIndex;
+
+ //
+ // Advance RPMC
+ //
+ Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Buffer for new variable is always needed, even this function is called to
+ // delete an existing one, because we need to pass the MetaDataHmacVar
back
+ // which will be updated upon each variable addition or deletion.
+ //
+ if ((NewVariable == NULL) || (NewVariableSize == NULL)) {
+ ASSERT (NewVariable != NULL);
+ ASSERT (NewVariableSize != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetProtectedVariableGlobal (&Global);
+ ASSERT_EFI_ERROR (Status);
+ ContextIn = GET_CNTX (Global);
+
+ if (!Global->Flags.WriteReady && !Global->Flags.WriteInit) {
+ return EFI_NOT_READY;
+ }
+
+ VarSize = 0;
+ CurrVarDig = NULL;
+ NewVarDig = NULL;
+ UnprotectedVarIndex = VAR_INDEX_INVALID;
+
+ //
+ // Check existing copy of the same variable.
+ //
+ if (CurrVariable != NULL) {
+ //
+ // Find local cached copy, if possible.
+ //
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+ VarInfo.Buffer = CurrVariable;
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+
+ Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
+ ASSERT_EFI_ERROR (Status);
+
+ UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global, &VarInfo);
+ if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
+ CurrVarDig = VAR_DIG_PTR (Global->Unprotected[UnprotectedVarIndex]);
+ } else {
+ CurrVarDig = FindVariableInternal (Global, &VarInfo, FALSE);
+ }
+
+ ASSERT (CurrVarDig != NULL);
+ CurrVarDig->State &= VAR_DELETED;
+ }
+
+ //
+ // The old copy of the variable might haven't been deleted completely.
+ //
+ if (CurrVariableInDel != NULL) {
+ //
+ // Find local cached copy, if possible.
+ //
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+ VarInfo.Buffer = CurrVariableInDel;
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+
+ Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
+ ASSERT_EFI_ERROR (Status);
+
+ if (UnprotectedVarIndex == VAR_INDEX_INVALID) {
+ UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global,
&VarInfo);
+ }
+
+ if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
+ VarDig = VAR_DIG_PTR (Global->Unprotected[UnprotectedVarIndex]);
+ } else {
+ VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
+ }
+
+ if ((VarDig != NULL) && (VAR_DIG_ADR (VarDig) != VAR_INDEX_INVALID)) {
+ VarDig->State &= VAR_DELETED;
+
+ //
+ // Just need one node for the same variable. So remove the one
+ // in-del-transition.
+ //
+ if ((CurrVarDig != NULL) && (VarDig != CurrVarDig)) {
+ RemoveVariableDigestNode (Global, VarDig, TRUE);
+ } else {
+ CurrVarDig = VarDig; // Reuse the one in-del-transition.
+ }
+ }
+ }
+
+ //
+ // New data of the variable or new variable to be added.
+ //
+ if (NewVariable != NULL) {
+ //
+ // Completely new variable?
+ //
+ if (UnprotectedVarIndex == VAR_INDEX_INVALID) {
+ ZeroMem (&VarInfo, sizeof (VarInfo));
+ VarInfo.Buffer = NewVariable;
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+
+ Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
+ ASSERT_EFI_ERROR (Status);
+
+ UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global,
&VarInfo);
+ }
+ }
+
+ //
+ // Reserve space for MetaDataHmacVar (before the new variable so
+ // that it can be written first).
+ //
+ ZeroMem (&NewVarInfo, sizeof (NewVarInfo));
+ ZeroMem (&NewHmacVarInfo, sizeof (NewHmacVarInfo));
+
+ //
+ // Put the MetaDataHmacVar at the beginning of buffer.
+ //
+ NewHmacVarInfo.Buffer = NewVariable;
+
+ if (*NewVariableSize == 0) {
+ //
+ // Delete variable (but not MetaDataHmacVar)
+ //
+ if ( (UnprotectedVarIndex != IndexHmacAdded)
+ && (UnprotectedVarIndex != IndexHmacInDel))
+ {
+ RemoveVariableDigestNode (Global, CurrVarDig, TRUE);
+ }
+
+ NewVarInfo.Buffer = NULL;
+ } else if (UnprotectedVarIndex >= IndexPlatformVar) {
+ //
+ // Add/update variable. Move new variable data to be after
MetaDataHmacVar.
+ //
+ // TRICK: New MetaDataHmacVar will be put at the beginning of buffer
+ // for new variable so that they can be written into non-volatile
+ // variable storage in one call. This can avoid writing one variable
+ // (NewHmacVarInfo) in the middle of writing another variable
+ // (NewVarInfo), which will need two calls and introduce extra
+ // complexities (from temp variable buffer reservation to variable
+ // space reclaim, etc.) in current implementation of variable
+ // services. The caller must make sure there's enough space in
+ // variable buffer (i.e. at least 2 * MaxVariableSize).
+ //
+ NewVarInfo.Buffer = (VARIABLE_HEADER *)((UINTN)NewVariable
+ + GetMetaDataHmacVarSize (Global->Flags.Auth));
+ CopyMem ((VOID *)NewVarInfo.Buffer, (VOID *)NewVariable,
*NewVariableSize);
+
+ NewVarInfo.StoreIndex = VAR_INDEX_INVALID; // Skip offset calculation
(it's new one)
+ NewVarInfo.Flags.Auth = Global->Flags.Auth;
+
+ Status = ContextIn->GetVariableInfo (&NewVarInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ if (CurrVarDig != NULL) {
+ //
+ // Update existing variable. Re-use the node.
+ //
+ NewVarDig = CurrVarDig;
+ } else {
+ //
+ // Add new variable.
+ //
+ NewVarDig = CreateVariableDigestNode (
+ NewVarInfo.Header.VariableName,
+ NewVarInfo.Header.VendorGuid,
+ (UINT16)NewVarInfo.Header.NameSize,
+ (UINT32)NewVarInfo.Header.DataSize,
+ NewVarInfo.Flags.Auth,
+ Global
+ );
+ if (NewVarDig == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NewVarDig->Attributes = NewVarInfo.Header.Attributes;
+ if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
+ NewVarDig->Flags.Protected = FALSE;
+ NewVarDig->Flags.Encrypted = FALSE;
+ Global->Unprotected[UnprotectedVarIndex] = VAR_DIG_ADR (NewVarDig);
+ }
+
+ //
+ // copy new variable to CacheIndex
+ //
+ VarSize = VARIABLE_HEADER_SIZE (NewVarInfo.Flags.Auth);
+ VarSize += NewVarInfo.Header.NameSize + GET_PAD_SIZE
(NewVarInfo.Header.NameSize);
+ VarSize += NewVarInfo.Header.DataSize + GET_PAD_SIZE
(NewVarInfo.Header.DataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+ CopyMem (GET_BUFR (NewVarDig->CacheIndex), GET_BUFR
(NewVarInfo.Buffer), VarSize);
+ InsertVariableDigestNode (Global, NewVarDig, NULL);
+ }
+ }
+
+ if ( (UnprotectedVarIndex == IndexHmacAdded)
+ || (UnprotectedVarIndex == IndexHmacInDel))
+ {
+ //
+ // MetaDataHmacVar should be managed only by this library. It's not
+ // supposed to be updated by external users of variable service. The only
+ // exception is that deleting it (not really delete but refresh the HMAC
+ // value against RPMC+1) is allowed before WriteInit, as a way to always
+ // increment RPMC once in current boot before any variable updates.
+ //
+ if ((NewVarInfo.Buffer != NULL) || Global->Flags.WriteInit) {
+ return EFI_ACCESS_DENIED;
+ }
+ } else {
+ //
+ // Do encryption, if enabled.
+ //
+ if ((NewVarDig != NULL) && (NewVarInfo.Buffer != NULL)) {
+ Status = UpdateVariableInternal (Global, &NewVarInfo, NewVarDig);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+ }
+
+ //
+ // Refresh MetaDataHmacVar.
+ //
+ Status = RefreshVariableMetadataHmac (Global, NULL, &NewHmacVarInfo);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ //
+ // Return size for both MetaDataHmacVar and added/updated one.
+ //
+ VarSize = VARIABLE_SIZE (&NewHmacVarInfo);
+ *NewVariableSize = HEADER_ALIGN (VarSize);
+ if (NewVarInfo.Buffer != NULL) {
+ VarSize = VARIABLE_SIZE (&NewVarInfo);
+ VarSize = HEADER_ALIGN (VarSize);
+
+ if (VarSize > GET_CNTX (Global)->MaxVariableSize) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ *NewVariableSize += VarSize;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Finalize a variable updating after it's written to NV variable storage
+ successfully.
+
+ This usually includes works like increasing RPMC, synchronizing local cache,
+ updating new position of "MetaDataHmacVar", deleting old copy of
"MetaDataHmacVar"
+ completely, etc.
+
+ @param[in] NewVariable Buffer of new variables and
MetaDataHmacVar.
+ @param[in] VariableSize Size of buffer pointed by NewVariable.
+ @param[in] StoreIndex StoreIndex to NV variable storage from where
the new
+ variable and MetaDataHmacVar have been written.
+
+ @retval EFI_SUCCESS No problem in winding up the variable write
operation.
+ @retval Others Failed to updating state of old copy of updated
+ variable, or failed to increase RPMC, etc.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteFinal (
+ IN VARIABLE_HEADER *NewVariable,
+ IN UINTN VariableSize,
+ IN UINT64 StoreIndex
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ PROTECTED_VARIABLE_CONTEXT_IN *ContextIn;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ UNPROTECTED_VARIABLE_INDEX Index;
+ VARIABLE_DIGEST *VarDig;
+ VOID *Buffer;
+ UINTN VarSize;
+ UINTN NewVarSize;
+
+ Status = GetProtectedVariableGlobal (&Global);
+ ASSERT_EFI_ERROR (Status);
+ ContextIn = GET_CNTX (Global);
+
+ ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
+ VarInfo.Buffer = NewVariable;
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+
+ Status = ContextIn->GetVariableInfo (&VarInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ Index = CheckKnownUnprotectedVariable (Global, &VarInfo);
+ if (Index < UnprotectedVarIndexMax) {
+ VarDig = VAR_DIG_PTR (Global->Unprotected[Index]);
+ } else {
+ VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
+ }
+
+ if (Index == IndexHmacAdded) {
+ //
+ // Advance the RPMC to let it match new MetaDataHmacVar.
+ //
+ Status = IncrementMonotonicCounter (RPMC_COUNTER_2);
+ ASSERT_EFI_ERROR (Status);
+
+ if ((VarDig->StoreIndex != VAR_INDEX_INVALID) && (VarDig->State !=
VAR_ADDED)) {
+ ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
+ VarInfo.StoreIndex = VarDig->StoreIndex; // Still point to old copy
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+
+ //
+ // Delete variable completely.
+ //
+ Status = ContextIn->GetVariableInfo (&VarInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ if ( (VarInfo.Buffer->State == VAR_ADDED)
+ || (VarInfo.Buffer->State == (VAR_ADDED &
VAR_IN_DELETED_TRANSITION)))
+ {
+ VarInfo.Buffer->State &= VAR_DELETED;
+ Status = ContextIn->UpdateVariableStore (
+ &VarInfo,
+ OFFSET_OF (VARIABLE_HEADER, State),
+ sizeof (VarInfo.Buffer->State),
+ &VarInfo.Buffer->State
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+ }
+ }
+
+ VarDig->StoreIndex = StoreIndex;
+ VarDig->State = VAR_ADDED;
+
+ ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
+ VarInfo.Buffer = NULL;
+ VarInfo.StoreIndex = VarDig->StoreIndex;
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+ Status = ContextIn->GetVariableInfo (&VarInfo);
+
+ //
+ // Check if cache pool need re-allocation due to variable size increase
+ //
+ VarSize = VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
+ VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
+ VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+
+ NewVarSize = VARIABLE_HEADER_SIZE (VarInfo.Flags.Auth);
+ NewVarSize += VarInfo.Header.NameSize + GET_PAD_SIZE
(VarInfo.Header.NameSize);
+ NewVarSize += VarInfo.Header.DataSize + GET_PAD_SIZE
(VarInfo.Header.DataSize);
+ NewVarSize = HEADER_ALIGN (NewVarSize);
+
+ if (VarSize < NewVarSize) {
+ if (VarDig->Flags.Freeable == TRUE) {
+ FreePool (GET_BUFR (VarDig->CacheIndex));
+ }
+
+ Buffer = AllocatePool (NewVarSize);
+ if (Buffer != NULL) {
+ VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
+ } else {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ }
+
+ //
+ // Update cached copy.
+ //
+ CopyMem (GET_BUFR (VarDig->CacheIndex), NewVariable, NewVarSize);
+
+ //
+ // Check if there is consecutive variable as part of the write or
+ // is it just the MetaDataHmacVar variable
+ //
+ if (NewVarSize < VariableSize) {
+ //
+ // Advance to consecutive Variable
+ //
+ NewVariable = GET_BUFR (GET_ADRS (NewVariable) + NewVarSize);
+
+ //
+ // Update the StoreIndex of consecutive Variable
+ //
+ ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
+ VarInfo.Buffer = NULL;
+ VarInfo.StoreIndex = VarDig->StoreIndex;
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+ Status = ContextIn->GetNextVariableInfo (&VarInfo);
+ StoreIndex = VarInfo.StoreIndex;
+
+ //
+ // The new StoreIndex does not exist in the variable digest.
+ // It is yet to be updated.
+ // Therefore, find variable by Name & Guid instead.
+ //
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
+
+ //
+ // Check if cache pool need re-allocation due to variable size increase
+ //
+ VarSize = VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
+ VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
+ VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
+ VarSize = HEADER_ALIGN (VarSize);
+
+ NewVarSize = VARIABLE_HEADER_SIZE (VarInfo.Flags.Auth);
+ NewVarSize += VarInfo.Header.NameSize + GET_PAD_SIZE
(VarInfo.Header.NameSize);
+ NewVarSize += VarInfo.Header.DataSize + GET_PAD_SIZE
(VarInfo.Header.DataSize);
+ NewVarSize = HEADER_ALIGN (NewVarSize);
+
+ if (VarSize < NewVarSize) {
+ if (VarDig->Flags.Freeable == TRUE) {
+ FreePool (GET_BUFR (VarDig->CacheIndex));
+ }
+
+ Buffer = AllocatePool (NewVarSize);
+ if (Buffer != NULL) {
+ VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
+ } else {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ }
+
+ //
+ // Update cached copy.
+ //
+ CopyMem (GET_BUFR (VarDig->CacheIndex), NewVariable, NewVarSize);
+ VarDig->StoreIndex = StoreIndex;
+ }
+
+ return Status;
+}
+
+/**
+ Refresh variable information changed by variable service.
+
+ @param[in] Variable Pointer to buffer of the updated variable.
+ @param[in] VariableSize Size of variable pointed by Variable.
+ @param[in] StoreIndex New index of the variable in store.
+ @param[in] RefreshData Flag to indicate if the variable has been updated.
+
+ @return EFI_SUCCESS No error occurred in updating.
+ @return EFI_NOT_FOUND The given variable was not found in
+ ProtectedVariableLib.
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibRefresh (
+ IN VARIABLE_HEADER *Variable,
+ IN UINTN VariableSize,
+ IN UINT64 StoreIndex,
+ IN BOOLEAN RefreshData
+ )
+{
+ EFI_STATUS Status;
+ PROTECTED_VARIABLE_INFO VarInfo;
+ PROTECTED_VARIABLE_GLOBAL *Global;
+ VARIABLE_DIGEST *VarDig;
+
+ (VOID)GetProtectedVariableGlobal (&Global);
+
+ ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
+ VarInfo.Buffer = Variable;
+ VarInfo.StoreIndex = VAR_INDEX_INVALID;
+ VarInfo.Flags.Auth = Global->Flags.Auth;
+
+ Status = GET_CNTX (Global)->GetVariableInfo (&VarInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
+ if (VarDig == NULL) {
+ ASSERT (VarDig != NULL);
+ return EFI_NOT_FOUND;
+ }
+
+ if (StoreIndex != VAR_INDEX_INVALID) {
+ VarDig->StoreIndex = StoreIndex;
+ }
+
+ if (RefreshData) {
+ if (VarDig->CacheIndex == VAR_INDEX_INVALID) {
+ VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)
+ AllocatePool (MAX_VARIABLE_SIZE);
+ }
+
+ CopyMem (GET_BUFR (VarDig->CacheIndex), Variable, VariableSize);
+ }
+
+ //
+ // Information should stay the same other than following ones.
+ //
+ VarDig->State = VarInfo.Header.State;
+ VarDig->DataSize = (UINT32)VarInfo.Header.DataSize;
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Determine if the variable is the HMAC variable
+
+ @param VariableName Pointer to variable name.
+
+ @return TRUE Variable is HMAC variable
+ @return FALSE Variable is not HMAC variable
+
+**/
+BOOLEAN
+ProtectedVariableLibIsHmac (
+ IN CHAR16 *VariableName
+ )
+{
+ INTN Result;
+
+ Result = StrnCmp (
+ METADATA_HMAC_VARIABLE_NAME,
+ VariableName,
+ METADATA_HMAC_VARIABLE_NAME_SIZE
+ );
+
+ if (Result == 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git
a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
new file mode 100644
index 000000000000..4591d1cd59e5
--- /dev/null
+++
b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
@@ -0,0 +1,233 @@
+/** @file
+ Implemention of ProtectedVariableLib for BootService/Runtime use cases.
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Uefi.h>
+
+#include "Library/MemoryAllocationLib.h"
+#include "Library/UefiBootServicesTableLib.h"
+#include "Library/UefiRuntimeLib.h"
+#include "ProtectedVariableInternal.h"
+
+EFI_EVENT mVaChangeEvent = NULL;
+
+PROTECTED_VARIABLE_CONTEXT_IN mRtVariableContextIn = {
+ PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
+ sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
+ 0,
+ FromRuntimeModule,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PROTECTED_VARIABLE_GLOBAL mRtProtectedVariableGlobal = {
+ PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
+ sizeof (PROTECTED_VARIABLE_GLOBAL),
+ { 0 },
+ { 0 },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ { 0, 0, 0 },
+ 0,
+ 0,
+ { 0, 0, 0, 0, 0, 0}
+};
+
+/**
+
+ Get global data structure used to process protected variable.
+
+ @param[out] Global Pointer to global configuration data.
+
+ @retval EFI_SUCCESS Get requested structure successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProtectedVariableGlobal (
+ OUT PROTECTED_VARIABLE_GLOBAL **Global OPTIONAL
+ )
+{
+ if (Global != NULL) {
+ mRtProtectedVariableGlobal.ContextIn = GET_ADRS
(&mRtVariableContextIn);
+ *Global = &mRtProtectedVariableGlobal;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
+
+ This is a notification function registered on
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
+ It convers pointer to new virtual address.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context Pointer to the notification function's context.
+
+**/
+VOID
+EFIAPI
+VirtualAddressChangeEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ if (mRtVariableContextIn.FindVariableSmm != NULL) {
+ EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn.FindVariableSmm);
+ }
+
+ if (mRtVariableContextIn.GetVariableInfo != NULL) {
+ EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn.GetVariableInfo);
+ }
+
+ if (mRtVariableContextIn.GetNextVariableInfo != NULL) {
+ EfiConvertPointer (0x0, (VOID
**)&mRtVariableContextIn.GetNextVariableInfo);
+ }
+
+ if (mRtVariableContextIn.UpdateVariableStore != NULL) {
+ EfiConvertPointer (0x0, (VOID
**)&mRtVariableContextIn.UpdateVariableStore);
+ }
+
+ EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn);
+ if (mRtProtectedVariableGlobal.VariableCache != 0) {
+ EfiConvertPointer (0x0, (VOID
**)&mRtProtectedVariableGlobal.VariableCache);
+ }
+
+ EfiConvertPointer (0x0, (VOID **)&mRtProtectedVariableGlobal);
+}
+
+/**
+
+ Initialization for protected variable services.
+
+ If this initialization failed upon any error, the whole variable services
+ should not be used. A system reset might be needed to re-construct NV
+ variable storage to be the default state.
+
+ @param[in] ContextIn Pointer to variable service context needed by
+ protected variable.
+
+ @retval EFI_SUCCESS Protected variable services are ready.
+ @retval EFI_INVALID_PARAMETER If ContextIn == NULL or something
missing or
+ mismatching in the content in ContextIn.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibInitialize (
+ IN PROTECTED_VARIABLE_CONTEXT_IN *ContextIn
+ )
+{
+ if ( (ContextIn == NULL)
+ || (ContextIn->StructVersion !=
PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
+ || (ContextIn->FindVariableSmm == NULL)
+ || (ContextIn->GetVariableInfo == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (&mRtVariableContextIn, ContextIn, sizeof
(mRtVariableContextIn));
+
+ //
+ // Register the event to convert the pointer for runtime.
+ //
+ gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ VirtualAddressChangeEvent,
+ NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &mVaChangeEvent
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Prepare for variable update.
+
+ Not supported in DXE phase.
+
+ @retval EFI_UNSUPPORTED Updating variable is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteInit (
+ VOID
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+
+ Update a variable with protection provided by this library.
+
+ Not supported in DXE phase.
+
+ @param[in,out] CurrVariable Variable to be updated. It's NULL if
+ adding a new variable.
+ @param[in] CurrVariableInDel In-delete-transition copy of updating
variable.
+ @param[in,out] NewVariable Buffer of new variable data or
+ Buffer of "MetaDataHmacVar" and new variable
(encrypted).
+ @param[in,out] NewVariableSize Size of NewVariable or
+ Size of (encrypted) NewVariable and "MetaDataHmacVar".
+
+ @retval EFI_UNSUPPORTED Not support updating variable.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibUpdate (
+ IN OUT VARIABLE_HEADER *CurrVariable,
+ IN VARIABLE_HEADER *CurrVariableInDel,
+ IN OUT VARIABLE_HEADER *NewVariable,
+ IN OUT UINTN *NewVariableSize
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+
+ Finalize a variable updating after it's written to NV variable storage
+ successfully.
+
+ (Not supported for BootService/Runtime use cases.)
+
+ @param[in] NewVariable Buffer of new variables and
MetaDataHmacVar.
+ @param[in] VariableSize Size of buffer pointed by NewVariable.
+ @param[in] StoreIndex StoreIndex to NV variable storage from where
the new
+ variable and MetaDataHmacVar have been written.
+
+ @retval EFI_UNSUPPORTED Not supported for BootService/Runtime use
cases.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibWriteFinal (
+ IN VARIABLE_HEADER *NewVariable,
+ IN UINTN VariableSize,
+ IN UINT64 StoreIndex
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
--
2.35.1.windows.2


Re: [PATCH 1/1] Ext4Pkg: Sanity check the inode size

Pedro Falcato
 

Pushed as 436a861


On Sun, Aug 7, 2022 at 7:27 AM Marvin Häuser <mhaeuser@...> wrote:
Reviewed-by: Marvin Häuser <mhaeuser@...>

> On 7. Aug 2022, at 01:39, Pedro Falcato <pedro.falcato@...> wrote:
>
> Check its alignment and value for possible bad values.
>
> Cc: Marvin Häuser <mhaeuser@...>
> Signed-off-by: Pedro Falcato <pedro.falcato@...>
> ---
> Features/Ext4Pkg/Ext4Dxe/Superblock.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/Features/Ext4Pkg/Ext4Dxe/Superblock.c b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
> index c22155ba11b4..edee051c41e8 100644
> --- a/Features/Ext4Pkg/Ext4Dxe/Superblock.c
> +++ b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
> @@ -189,6 +189,12 @@ Ext4OpenSuperblock (
>     Partition->FeaturesIncompat = Sb->s_feature_incompat;
>     Partition->FeaturesRoCompat = Sb->s_feature_ro_compat;
>     Partition->InodeSize        = Sb->s_inode_size;
> +
> +    // Check for proper alignment of InodeSize and that InodeSize is indeed larger than
> +    // the minimum size, 128 bytes.
> +    if (((Partition->InodeSize % 4) != 0) || (Partition->InodeSize < EXT4_GOOD_OLD_INODE_SIZE)) {
> +      return EFI_VOLUME_CORRUPTED;
> +    }
>   } else {
>     // GOOD_OLD_REV
>     Partition->FeaturesCompat = Partition->FeaturesIncompat = Partition->FeaturesRoCompat = 0;
> --
> 2.37.1
>



--
Pedro Falcato


[PATCH] RedfishPkg: Redfish library functions to send POST and DELETE requests do not allow to use all spec defined parameters

Igor Kulchytskyy
 

There is no function to send POST requets with the ContetntType different
from "application/json".
There is no function to send DELETE request with the body.


Cc: Abner Chang <Abner.Chang@...>
Cc: Nickle Wang <nickle.wang@...>
Signed-off-by: Igor Kulchytskyy <igork@...>
---
RedfishPkg/Include/Library/RedfishLib.h | 696 ++++++++++++++++++++
RedfishPkg/PrivateInclude/Library/RedfishLib.h | 616 -----------------
RedfishPkg/PrivateLibrary/RedfishLib/RedfishLib.c | 189 ++++++
RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfishService.h | 8 +
RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c | 43 +-
RedfishPkg/RedfishPkg.dec | 2 +-
6 files changed, 934 insertions(+), 620 deletions(-)

diff --git a/RedfishPkg/Include/Library/RedfishLib.h b/RedfishPkg/Include/Library/RedfishLib.h
new file mode 100644
index 0000000..d4b3246
--- /dev/null
+++ b/RedfishPkg/Include/Library/RedfishLib.h
@@ -0,0 +1,696 @@
+/** @file
+ This library provides a set of utility APIs that allow to create/read/update/delete
+ (CRUD) Redfish resources and provide basic query abilities by using [URI/RedPath]
+ (https://github.com/DMTF/libredfish).
+
+ The query language is based on XPath (https://www.w3.org/TR/1999/REC-xpath-19991116/).
+ This library and query language essentially treat the entire Redfish Service like it
+ was a single JSON document. In other words whenever it encounters an odata.id in JSON
+ document, it will retrieve the new JSON document (if needed). We name the path as
+ RedPath:
+
+ Expression Description
+
+ nodename Selects the JSON entity with the name "nodename".
+ If the value of nodename is an object with "@odata.id",
+ it will continue get the value from "@odata.id".
+
+ / Selects from the root node
+
+ [index] Selects the index number JSON entity from an array or
+ object. If the JSON entity is one collection (has
+ Members & Members@...), means to get the index
+ member in "Members". Index number >=1, 1 means to return
+ the first instance.
+
+ [XXX] Operation on JSON entity.
+ If the JSON entity is one collection (has Members &
+ Members@...), means to get all elements in
+ "Members". If the JSON entity is one array, means to
+ get all elements in array. Others will match the nodename
+ directly (e.g. JSON_OBJECT, JSON_STRING, JSON_TRUE,
+ JSON_FALSE, JSON_INTEGER).
+
+ [nodename] Selects all the elements from an JSON entity that
+ contain a property named "nodename"
+
+ [name=value] Selects all the elements from an JSON entity where
+ the property "name" is equal to "value"
+
+ [name~value] Selects all the elements from an JSON entity where
+ the string property "name" is equal to "value" using
+ case insensitive comparison.
+
+ [name<value] Selects all the elements from an JSON entity where
+ the property "name" is less than "value"
+
+ [name<=value] Selects all the elements from an JSON entity where
+ the property "name" is less than or equal to "value"
+
+ [name>value] Selects all the elements from an JSON entity where
+ the property "name" is greater than "value"
+
+ [name>=value] Selects all the elements from an JSON entity where
+ the property "name" is greater than or equal to "value"
+
+ Some examples:
+
+ /v1/Chassis[1] - Will return the first Chassis instance.
+ /v1/Chassis[SKU=1234] - Will return all Chassis instances with a SKU field equal to 1234.
+ /v1/Systems[Storage] - Will return all the System instances that have Storage field populated.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2021 Hewlett Packard Enterprise Development LP<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef REDFISH_LIB_H_
+#define REDFISH_LIB_H_
+
+#include <Library/JsonLib.h>
+
+#include <Protocol/Http.h>
+#include <Protocol/EdkIIRedfishConfigHandler.h>
+
+#define ODATA_TYPE_NAME_MAX_SIZE 128
+#define ODATA_TYPE_MAX_SIZE 128
+
+///
+/// Library class public defines
+///
+typedef VOID *REDFISH_SERVICE;
+typedef VOID *REDFISH_PAYLOAD;
+
+///
+/// Library class public structures/unions
+///
+typedef struct {
+ EFI_HTTP_STATUS_CODE *StatusCode;
+ UINTN HeaderCount;
+ EFI_HTTP_HEADER *Headers;
+ REDFISH_PAYLOAD Payload;
+} REDFISH_RESPONSE;
+
+///
+/// Odata type-name mapping structure.
+///
+typedef struct {
+ CONST CHAR8 OdataTypeName[ODATA_TYPE_NAME_MAX_SIZE];
+ CONST CHAR8 OdataType[ODATA_TYPE_MAX_SIZE];
+} REDFISH_ODATA_TYPE_MAPPING;
+
+/**
+ This function uses REST EX protocol provided in RedfishConfigServiceInfo.
+ The service enumerator will also handle the authentication flow automatically
+ if HTTP basic auth or Redfish session login is configured to use.
+
+ Callers are responsible for freeing the returned service by RedfishCleanupService().
+
+ @param[in] RedfishConfigServiceInfo Redfish service information the EFI Redfish
+ feature driver communicates with.
+
+ @return New created Redfish Service, or NULL if error happens.
+
+**/
+REDFISH_SERVICE
+EFIAPI
+RedfishCreateService (
+ IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo
+ );
+
+/**
+ Free the Service and all its related resources.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+
+**/
+VOID
+EFIAPI
+RedfishCleanupService (
+ IN REDFISH_SERVICE RedfishService
+ );
+
+/**
+ Create REDFISH_PAYLOAD instance in local with JSON represented resource value and
+ the Redfish Service.
+
+ The returned REDFISH_PAYLOAD can be used to create or update Redfish resource in
+ server side.
+
+ Callers are responsible for freeing the returned payload by RedfishCleanupPayload().
+
+ @param[in] Value JSON Value of the redfish resource.
+ @param[in] RedfishService The Service to access the Redfish resources.
+
+ @return REDFISH_PAYLOAD instance of the resource, or NULL if error happens.
+
+**/
+REDFISH_PAYLOAD
+EFIAPI
+RedfishCreatePayload (
+ IN EDKII_JSON_VALUE Value,
+ IN REDFISH_SERVICE RedfishService
+ );
+
+/**
+ Free the RedfishPayload and all its related resources.
+
+ @param[in] Payload Payload to be freed.
+
+**/
+VOID
+EFIAPI
+RedfishCleanupPayload (
+ IN REDFISH_PAYLOAD Payload
+ );
+
+/**
+ This function returns the decoded JSON value of a REDFISH_PAYLOAD.
+
+ Caller doesn't need to free the returned JSON value because it will be released
+ in corresponding RedfishCleanupPayload() function.
+
+ @param[in] Payload A REDFISH_PAYLOAD instance.
+
+ @return Decoded JSON value of the payload.
+
+**/
+EDKII_JSON_VALUE
+EFIAPI
+RedfishJsonInPayload (
+ IN REDFISH_PAYLOAD Payload
+ );
+
+/**
+ Fill the input RedPath string with system UUID from SMBIOS table or use the customized
+ ID if FromSmbios == FALSE.
+
+ This is a helper function to build a RedPath string which can be used to address
+ a Redfish resource for this computer system. The input PathString must have a Systems
+ note in format of "Systems[UUID=%g]" or "Systems[UUID~%g]" to fill the UUID value.
+
+ Example:
+ Use "/v1/Systems[UUID=%g]/Bios" to build a RedPath to address the "Bios" resource
+ for this computer system.
+
+ @param[in] RedPath RedPath format to be build.
+ @param[in] FromSmbios Get system UUID from SMBIOS as computer system instance ID.
+ @param[in] IdString The computer system instance ID.
+
+ @return Full RedPath with system UUID inside, or NULL if error happens.
+
+**/
+CHAR8 *
+EFIAPI
+RedfishBuildPathWithSystemUuid (
+ IN CONST CHAR8 *RedPath,
+ IN BOOLEAN FromSmbios,
+ IN CHAR8 *IdString OPTIONAL
+ );
+
+/**
+ Get a redfish response addressed by a RedPath string, including HTTP StatusCode, Headers
+ and Payload which record any HTTP response messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+ @param[in] RedPath RedPath string to address a resource, must start
+ from the root node.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX. The corresponding redfish resource has
+ been returned in Payload within RedResponse.
+ @retval EFI_INVALID_PARAMETER RedfishService, RedPath, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned Payload is NULL, indicates any error happen.
+ 2. If the returned StatusCode is NULL, indicates any error happen.
+ 3. If the returned StatusCode is not 2XX, indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishGetByService (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *RedPath,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+/**
+ Get a redfish response addressed by URI, including HTTP StatusCode, Headers
+ and Payload which record any HTTP response messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the URI resources.
+ @param[in] URI String to address a resource.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX. The corresponding redfish resource has
+ been returned in Payload within RedResponse.
+ @retval EFI_INVALID_PARAMETER RedfishService, RedPath, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned Payload is NULL, indicates any error happen.
+ 2. If the returned StatusCode is NULL, indicates any error happen.
+ 3. If the returned StatusCode is not 2XX, indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishGetByUri (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *Uri,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+/**
+ Get a redfish response addressed by the input Payload and relative RedPath string,
+ including HTTP StatusCode, Headers and Payload which record any HTTP response messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] Payload A existing REDFISH_PAYLOAD instance.
+ @param[in] RedPath Relative RedPath string to address a resource inside Payload.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful:
+ 1. The HTTP StatusCode is NULL and the returned Payload in
+ RedResponse is not NULL, indicates the Redfish resource has
+ been parsed from the input payload directly.
+ 2. The HTTP StatusCode is not NULL and the value is 2XX,
+ indicates the corresponding redfish resource has been returned
+ in Payload within RedResponse.
+ @retval EFI_INVALID_PARAMETER Payload, RedPath, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned Payload is NULL, indicates any error happen.
+ 2. If StatusCode is not NULL and the returned value of StatusCode
+ is not 2XX, indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishGetByPayload (
+ IN REDFISH_PAYLOAD Payload,
+ IN CONST CHAR8 *RedPath,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+/**
+ Use HTTP PATCH to perform updates on pre-existing Redfish resource.
+
+ This function uses the RedfishService to patch a Redfish resource addressed by
+ Uri (only the relative path is required). Changes to one or more properties within
+ the target resource are represented in the input Content, properties not specified
+ in Content won't be changed by this request. The corresponding redfish response will
+ returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
+ messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+ @param[in] Uri Relative path to address the resource.
+ @param[in] Content JSON represented properties to be update.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX. The Redfish resource will be returned
+ in Payload within RedResponse if server send it back in the HTTP
+ response message body.
+ @retval EFI_INVALID_PARAMETER RedfishService, Uri, Content, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishPatchToUri (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *Uri,
+ IN CONST CHAR8 *Content,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+/**
+ Use HTTP PATCH to perform updates on target payload. Patch to odata.id in Payload directly.
+
+ This function uses the Payload to patch the Target. Changes to one or more properties
+ within the target resource are represented in the input Payload, properties not specified
+ in Payload won't be changed by this request. The corresponding redfish response will
+ returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
+ messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] Target The target payload to be updated.
+ @param[in] Payload Palyoad with properties to be changed.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX. The Redfish resource will be returned
+ in Payload within RedResponse if server send it back in the HTTP
+ response message body.
+ @retval EFI_INVALID_PARAMETER Target, Payload, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishPatchToPayload (
+ IN REDFISH_PAYLOAD Target,
+ IN REDFISH_PAYLOAD Payload,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+
+/**
+ Use HTTP POST to create new Redfish resource in the Resource Collection.
+
+ The POST request should be submitted to the Resource Collection in which the new resource
+ is to belong. The Resource Collection is addressed by URI. The Redfish may
+ ignore any service controlled properties. The corresponding redfish response will returned,
+ including HTTP StatusCode, Headers and Payload which record any HTTP response messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+ @param[in] Uri Relative path to address the resource.
+ @param[in] Content JSON represented properties to be update.
+ @param[in] ContentSize Size of the Content to be send to Redfish service
+ @param[in] ContentType Type of the Content to be send to Redfish service
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX. The Redfish resource will be returned
+ in Payload within RedResponse if server send it back in the HTTP
+ response message body.
+ @retval EFI_INVALID_PARAMETER RedfishService, Uri, Content, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishPostToUriEx (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *Uri,
+ IN CONST CHAR8 *Content,
+ IN UINTN ContentSize,
+ IN CONST CHAR8 *ContentType,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+
+/**
+ Use HTTP POST to create a new resource in target payload.
+
+ The POST request should be submitted to the Resource Collection in which the new resource
+ is to belong. The Resource Collection is addressed by Target payload. The Redfish may
+ ignore any service controlled properties. The corresponding redfish response will returned,
+ including HTTP StatusCode, Headers and Payload which record any HTTP response messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] Target Target payload of the Resource Collection.
+ @param[in] Payload The new resource to be created.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX. The Redfish resource will be returned
+ in Payload within RedResponse if server send it back in the HTTP
+ response message body.
+ @retval EFI_INVALID_PARAMETER Target, Payload, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishPostToPayload (
+ IN REDFISH_PAYLOAD Target,
+ IN REDFISH_PAYLOAD Payload,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+/**
+ Use HTTP DELETE to remove a resource.
+
+ This function uses the RedfishService to remove a Redfish resource which is addressed
+ by input Uri (only the relative path is required). The corresponding redfish response will
+ returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
+ messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+ @param[in] Uri Relative path to address the resource.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX, the Redfish resource has been removed.
+ If there is any message returned from server, it will be returned
+ in Payload within RedResponse.
+ @retval EFI_INVALID_PARAMETER RedfishService, Uri, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishDeleteByUri (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *Uri,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+/**
+ Use HTTP DELETE to remove a resource.
+
+ This function uses the RedfishService to remove a Redfish resource which is addressed
+ by input Uri (only the relative path is required). The corresponding redfish response will
+ returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
+ messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+ @param[in] Uri Relative path to address the resource.
+ @param[in] Content JSON represented properties to be deleted.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX, the Redfish resource has been removed.
+ If there is any message returned from server, it will be returned
+ in Payload within RedResponse.
+ @retval EFI_INVALID_PARAMETER RedfishService, Uri, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishDeleteByUriEx (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *Uri,
+ IN CONST CHAR8 *Content,
+ OUT REDFISH_RESPONSE *RedResponse
+ );
+
+/**
+ Dump text in fractions.
+
+ @param[in] String ASCII string to dump.
+
+**/
+VOID
+RedfishDumpJsonStringFractions (
+ IN CHAR8 *String
+ );
+
+/**
+ Extract the JSON text content from REDFISH_PAYLOAD and dump to debug console.
+
+ @param[in] Payload The Redfish payload to dump.
+
+**/
+VOID
+RedfishDumpPayload (
+ IN REDFISH_PAYLOAD Payload
+ );
+
+/**
+ Dump text in JSON value.
+
+ @param[in] JsonValue The Redfish JSON value to dump.
+
+**/
+VOID
+RedfishDumpJson (
+ IN EDKII_JSON_VALUE JsonValue
+ );
+
+/**
+ This function will cleanup the HTTP header and Redfish payload resources.
+
+ @param[in] StatusCode The status code in HTTP response message.
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[in] Payload The Redfish payload to dump.
+
+**/
+VOID
+RedfishFreeResponse (
+ IN EFI_HTTP_STATUS_CODE *StatusCode,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ IN REDFISH_PAYLOAD Payload
+ );
+
+/**
+ Check if the "@odata.type" in Payload is valid or not.
+
+ @param[in] Payload The Redfish payload to be checked.
+ @param[in] OdataTypeName OdataType will be retrived from mapping list.
+ @param[in] OdataTypeMappingList The list of OdataType.
+ @param[in] OdataTypeMappingListSize The number of mapping list
+
+ @return TRUE if the "@odata.type" in Payload is valid, otherwise FALSE.
+
+**/
+BOOLEAN
+RedfishIsValidOdataType (
+ IN REDFISH_PAYLOAD Payload,
+ IN CONST CHAR8 *OdataTypeName,
+ IN REDFISH_ODATA_TYPE_MAPPING *OdataTypeMappingList,
+ IN UINTN OdataTypeMappingListSize
+ );
+
+/**
+ Check if the payload is collection
+
+ @param[in] Payload The Redfish payload to be checked.
+
+ @return TRUE if the payload is collection.
+
+**/
+BOOLEAN
+RedfishIsPayloadCollection (
+ IN REDFISH_PAYLOAD Payload
+ );
+
+/**
+ Get collection size.
+
+ @param[in] Payload The Redfish collection payload
+ @param[in] CollectionSize Size of this collection
+
+ @return EFI_SUCCESS Coolection size is returned in CollectionSize
+ @return EFI_INVALID_PARAMETER The payload is not a collection.
+**/
+EFI_STATUS
+RedfishGetCollectionSize (
+ IN REDFISH_PAYLOAD Payload,
+ IN UINTN *CollectionSize
+ );
+
+/**
+ Get Redfish payload of collection member
+
+ @param[in] Payload The Redfish collection payload
+ @param[in] Index Index of collection member
+
+ @return NULL Fail to get collection member.
+ @return Non NULL Payload is returned.
+**/
+REDFISH_PAYLOAD
+RedfishGetPayloadByIndex (
+ IN REDFISH_PAYLOAD Payload,
+ IN UINTN Index
+ );
+
+/**
+ Check and return Redfish resource of the given Redpath.
+
+ @param[in] RedfishService Pointer to REDFISH_SERVICE
+ @param[in] Redpath Redpath of the resource.
+ @param[in] Response Optional return the resource.
+
+ @return EFI_STATUS
+**/
+EFI_STATUS
+RedfishCheckIfRedpathExist (
+ IN REDFISH_SERVICE RedfishService,
+ IN CHAR8 *Redpath,
+ IN REDFISH_RESPONSE *Response OPTIONAL
+ );
+
+/**
+ This function returns the string of Redfish service version.
+
+ @param[in] RedfishService Redfish service instance.
+ @param[out] ServiceVersionStr Redfish service string.
+
+ @return EFI_STATUS
+
+**/
+EFI_STATUS
+RedfishGetServiceVersion (
+ IN REDFISH_SERVICE RedfishService,
+ OUT CHAR8 **ServiceVersionStr
+ );
+
+/**
+ This function returns the string of Redfish service version.
+
+ @param[in] ServiceVerisonStr The string of Redfish service version.
+ @param[in] Url The URL to build Redpath with ID.
+ Start with "/", for example "/Registries"
+ @param[in] Id ID string
+ @param[out] Redpath Pointer to retrive Redpath, caller has to free
+ the memory allocated for this string.
+ @return EFI_STATUS
+
+**/
+EFI_STATUS
+RedfishBuildRedpathUseId (
+ IN CHAR8 *ServiceVerisonStr,
+ IN CHAR8 *Url,
+ IN CHAR8 *Id,
+ OUT CHAR8 **Redpath
+ );
+
+#endif
diff --git a/RedfishPkg/PrivateInclude/Library/RedfishLib.h b/RedfishPkg/PrivateInclude/Library/RedfishLib.h
deleted file mode 100644
index b2488ab..0000000
--- a/RedfishPkg/PrivateInclude/Library/RedfishLib.h
+++ /dev/null
@@ -1,616 +0,0 @@
-/** @file
- This library provides a set of utility APIs that allow to create/read/update/delete
- (CRUD) Redfish resources and provide basic query abilities by using [URI/RedPath]
- (https://github.com/DMTF/libredfish).
-
- The query language is based on XPath (https://www.w3.org/TR/1999/REC-xpath-19991116/).
- This library and query language essentially treat the entire Redfish Service like it
- was a single JSON document. In other words whenever it encounters an odata.id in JSON
- document, it will retrieve the new JSON document (if needed). We name the path as
- RedPath:
-
- Expression Description
-
- nodename Selects the JSON entity with the name "nodename".
- If the value of nodename is an object with "@odata.id",
- it will continue get the value from "@odata.id".
-
- / Selects from the root node
-
- [index] Selects the index number JSON entity from an array or
- object. If the JSON entity is one collection (has
- Members & Members@...), means to get the index
- member in "Members". Index number >=1, 1 means to return
- the first instance.
-
- [XXX] Operation on JSON entity.
- If the JSON entity is one collection (has Members &
- Members@...), means to get all elements in
- "Members". If the JSON entity is one array, means to
- get all elements in array. Others will match the nodename
- directly (e.g. JSON_OBJECT, JSON_STRING, JSON_TRUE,
- JSON_FALSE, JSON_INTEGER).
-
- [nodename] Selects all the elements from an JSON entity that
- contain a property named "nodename"
-
- [name=value] Selects all the elements from an JSON entity where
- the property "name" is equal to "value"
-
- [name~value] Selects all the elements from an JSON entity where
- the string property "name" is equal to "value" using
- case insensitive comparison.
-
- [name<value] Selects all the elements from an JSON entity where
- the property "name" is less than "value"
-
- [name<=value] Selects all the elements from an JSON entity where
- the property "name" is less than or equal to "value"
-
- [name>value] Selects all the elements from an JSON entity where
- the property "name" is greater than "value"
-
- [name>=value] Selects all the elements from an JSON entity where
- the property "name" is greater than or equal to "value"
-
- Some examples:
-
- /v1/Chassis[1] - Will return the first Chassis instance.
- /v1/Chassis[SKU=1234] - Will return all Chassis instances with a SKU field equal to 1234.
- /v1/Systems[Storage] - Will return all the System instances that have Storage field populated.
-
- Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2021 Hewlett Packard Enterprise Development LP<BR>
-
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef REDFISH_LIB_H_
-#define REDFISH_LIB_H_
-
-#include <Library/JsonLib.h>
-
-#include <Protocol/Http.h>
-#include <Protocol/EdkIIRedfishConfigHandler.h>
-
-#define ODATA_TYPE_NAME_MAX_SIZE 128
-#define ODATA_TYPE_MAX_SIZE 128
-
-///
-/// Library class public defines
-///
-typedef VOID *REDFISH_SERVICE;
-typedef VOID *REDFISH_PAYLOAD;
-
-///
-/// Library class public structures/unions
-///
-typedef struct {
- EFI_HTTP_STATUS_CODE *StatusCode;
- UINTN HeaderCount;
- EFI_HTTP_HEADER *Headers;
- REDFISH_PAYLOAD Payload;
-} REDFISH_RESPONSE;
-
-///
-/// Odata type-name mapping structure.
-///
-typedef struct {
- CONST CHAR8 OdataTypeName[ODATA_TYPE_NAME_MAX_SIZE];
- CONST CHAR8 OdataType[ODATA_TYPE_MAX_SIZE];
-} REDFISH_ODATA_TYPE_MAPPING;
-
-/**
- This function uses REST EX protocol provided in RedfishConfigServiceInfo.
- The service enumerator will also handle the authentication flow automatically
- if HTTP basic auth or Redfish session login is configured to use.
-
- Callers are responsible for freeing the returned service by RedfishCleanupService().
-
- @param[in] RedfishConfigServiceInfo Redfish service information the EFI Redfish
- feature driver communicates with.
-
- @return New created Redfish Service, or NULL if error happens.
-
-**/
-REDFISH_SERVICE
-EFIAPI
-RedfishCreateService (
- IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo
- );
-
-/**
- Free the Service and all its related resources.
-
- @param[in] RedfishService The Service to access the Redfish resources.
-
-**/
-VOID
-EFIAPI
-RedfishCleanupService (
- IN REDFISH_SERVICE RedfishService
- );
-
-/**
- Create REDFISH_PAYLOAD instance in local with JSON represented resource value and
- the Redfish Service.
-
- The returned REDFISH_PAYLOAD can be used to create or update Redfish resource in
- server side.
-
- Callers are responsible for freeing the returned payload by RedfishCleanupPayload().
-
- @param[in] Value JSON Value of the redfish resource.
- @param[in] RedfishService The Service to access the Redfish resources.
-
- @return REDFISH_PAYLOAD instance of the resource, or NULL if error happens.
-
-**/
-REDFISH_PAYLOAD
-EFIAPI
-RedfishCreatePayload (
- IN EDKII_JSON_VALUE Value,
- IN REDFISH_SERVICE RedfishService
- );
-
-/**
- Free the RedfishPayload and all its related resources.
-
- @param[in] Payload Payload to be freed.
-
-**/
-VOID
-EFIAPI
-RedfishCleanupPayload (
- IN REDFISH_PAYLOAD Payload
- );
-
-/**
- This function returns the decoded JSON value of a REDFISH_PAYLOAD.
-
- Caller doesn't need to free the returned JSON value because it will be released
- in corresponding RedfishCleanupPayload() function.
-
- @param[in] Payload A REDFISH_PAYLOAD instance.
-
- @return Decoded JSON value of the payload.
-
-**/
-EDKII_JSON_VALUE
-EFIAPI
-RedfishJsonInPayload (
- IN REDFISH_PAYLOAD Payload
- );
-
-/**
- Fill the input RedPath string with system UUID from SMBIOS table or use the customized
- ID if FromSmbios == FALSE.
-
- This is a helper function to build a RedPath string which can be used to address
- a Redfish resource for this computer system. The input PathString must have a Systems
- note in format of "Systems[UUID=%g]" or "Systems[UUID~%g]" to fill the UUID value.
-
- Example:
- Use "/v1/Systems[UUID=%g]/Bios" to build a RedPath to address the "Bios" resource
- for this computer system.
-
- @param[in] RedPath RedPath format to be build.
- @param[in] FromSmbios Get system UUID from SMBIOS as computer system instance ID.
- @param[in] IdString The computer system instance ID.
-
- @return Full RedPath with system UUID inside, or NULL if error happens.
-
-**/
-CHAR8 *
-EFIAPI
-RedfishBuildPathWithSystemUuid (
- IN CONST CHAR8 *RedPath,
- IN BOOLEAN FromSmbios,
- IN CHAR8 *IdString OPTIONAL
- );
-
-/**
- Get a redfish response addressed by a RedPath string, including HTTP StatusCode, Headers
- and Payload which record any HTTP response messages.
-
- Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
- redfish response data.
-
- @param[in] RedfishService The Service to access the Redfish resources.
- @param[in] RedPath RedPath string to address a resource, must start
- from the root node.
- @param[out] RedResponse Pointer to the Redfish response data.
-
- @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
- NULL and the value is 2XX. The corresponding redfish resource has
- been returned in Payload within RedResponse.
- @retval EFI_INVALID_PARAMETER RedfishService, RedPath, or RedResponse is NULL.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
- more error info from returned HTTP StatusCode, Headers and Payload
- within RedResponse:
- 1. If the returned Payload is NULL, indicates any error happen.
- 2. If the returned StatusCode is NULL, indicates any error happen.
- 3. If the returned StatusCode is not 2XX, indicates any error happen.
-**/
-EFI_STATUS
-EFIAPI
-RedfishGetByService (
- IN REDFISH_SERVICE RedfishService,
- IN CONST CHAR8 *RedPath,
- OUT REDFISH_RESPONSE *RedResponse
- );
-
-/**
- Get a redfish response addressed by URI, including HTTP StatusCode, Headers
- and Payload which record any HTTP response messages.
-
- Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
- redfish response data.
-
- @param[in] RedfishService The Service to access the URI resources.
- @param[in] URI String to address a resource.
- @param[out] RedResponse Pointer to the Redfish response data.
-
- @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
- NULL and the value is 2XX. The corresponding redfish resource has
- been returned in Payload within RedResponse.
- @retval EFI_INVALID_PARAMETER RedfishService, RedPath, or RedResponse is NULL.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
- more error info from returned HTTP StatusCode, Headers and Payload
- within RedResponse:
- 1. If the returned Payload is NULL, indicates any error happen.
- 2. If the returned StatusCode is NULL, indicates any error happen.
- 3. If the returned StatusCode is not 2XX, indicates any error happen.
-**/
-EFI_STATUS
-EFIAPI
-RedfishGetByUri (
- IN REDFISH_SERVICE RedfishService,
- IN CONST CHAR8 *Uri,
- OUT REDFISH_RESPONSE *RedResponse
- );
-
-/**
- Get a redfish response addressed by the input Payload and relative RedPath string,
- including HTTP StatusCode, Headers and Payload which record any HTTP response messages.
-
- Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
- redfish response data.
-
- @param[in] Payload A existing REDFISH_PAYLOAD instance.
- @param[in] RedPath Relative RedPath string to address a resource inside Payload.
- @param[out] RedResponse Pointer to the Redfish response data.
-
- @retval EFI_SUCCESS The opeartion is successful:
- 1. The HTTP StatusCode is NULL and the returned Payload in
- RedResponse is not NULL, indicates the Redfish resource has
- been parsed from the input payload directly.
- 2. The HTTP StatusCode is not NULL and the value is 2XX,
- indicates the corresponding redfish resource has been returned
- in Payload within RedResponse.
- @retval EFI_INVALID_PARAMETER Payload, RedPath, or RedResponse is NULL.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
- more error info from returned HTTP StatusCode, Headers and Payload
- within RedResponse:
- 1. If the returned Payload is NULL, indicates any error happen.
- 2. If StatusCode is not NULL and the returned value of StatusCode
- is not 2XX, indicates any error happen.
-**/
-EFI_STATUS
-EFIAPI
-RedfishGetByPayload (
- IN REDFISH_PAYLOAD Payload,
- IN CONST CHAR8 *RedPath,
- OUT REDFISH_RESPONSE *RedResponse
- );
-
-/**
- Use HTTP PATCH to perform updates on pre-existing Redfish resource.
-
- This function uses the RedfishService to patch a Redfish resource addressed by
- Uri (only the relative path is required). Changes to one or more properties within
- the target resource are represented in the input Content, properties not specified
- in Content won't be changed by this request. The corresponding redfish response will
- returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
- messages.
-
- Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
- redfish response data.
-
- @param[in] RedfishService The Service to access the Redfish resources.
- @param[in] Uri Relative path to address the resource.
- @param[in] Content JSON represented properties to be update.
- @param[out] RedResponse Pointer to the Redfish response data.
-
- @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
- NULL and the value is 2XX. The Redfish resource will be returned
- in Payload within RedResponse if server send it back in the HTTP
- response message body.
- @retval EFI_INVALID_PARAMETER RedfishService, Uri, Content, or RedResponse is NULL.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
- more error info from returned HTTP StatusCode, Headers and Payload
- within RedResponse:
- 1. If the returned StatusCode is NULL, indicates any error happen.
- 2. If the returned StatusCode is not NULL and the value is not 2XX,
- indicates any error happen.
-**/
-EFI_STATUS
-EFIAPI
-RedfishPatchToUri (
- IN REDFISH_SERVICE RedfishService,
- IN CONST CHAR8 *Uri,
- IN CONST CHAR8 *Content,
- OUT REDFISH_RESPONSE *RedResponse
- );
-
-/**
- Use HTTP PATCH to perform updates on target payload. Patch to odata.id in Payload directly.
-
- This function uses the Payload to patch the Target. Changes to one or more properties
- within the target resource are represented in the input Payload, properties not specified
- in Payload won't be changed by this request. The corresponding redfish response will
- returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
- messages.
-
- Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
- redfish response data.
-
- @param[in] Target The target payload to be updated.
- @param[in] Payload Palyoad with properties to be changed.
- @param[out] RedResponse Pointer to the Redfish response data.
-
- @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
- NULL and the value is 2XX. The Redfish resource will be returned
- in Payload within RedResponse if server send it back in the HTTP
- response message body.
- @retval EFI_INVALID_PARAMETER Target, Payload, or RedResponse is NULL.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
- more error info from returned HTTP StatusCode, Headers and Payload
- within RedResponse:
- 1. If the returned StatusCode is NULL, indicates any error happen.
- 2. If the returned StatusCode is not NULL and the value is not 2XX,
- indicates any error happen.
-**/
-EFI_STATUS
-EFIAPI
-RedfishPatchToPayload (
- IN REDFISH_PAYLOAD Target,
- IN REDFISH_PAYLOAD Payload,
- OUT REDFISH_RESPONSE *RedResponse
- );
-
-/**
- Use HTTP POST to create a new resource in target payload.
-
- The POST request should be submitted to the Resource Collection in which the new resource
- is to belong. The Resource Collection is addressed by Target payload. The Redfish may
- ignore any service controlled properties. The corresponding redfish response will returned,
- including HTTP StatusCode, Headers and Payload which record any HTTP response messages.
-
- Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
- redfish response data.
-
- @param[in] Target Target payload of the Resource Collection.
- @param[in] Payload The new resource to be created.
- @param[out] RedResponse Pointer to the Redfish response data.
-
- @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
- NULL and the value is 2XX. The Redfish resource will be returned
- in Payload within RedResponse if server send it back in the HTTP
- response message body.
- @retval EFI_INVALID_PARAMETER Target, Payload, or RedResponse is NULL.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
- more error info from returned HTTP StatusCode, Headers and Payload
- within RedResponse:
- 1. If the returned StatusCode is NULL, indicates any error happen.
- 2. If the returned StatusCode is not NULL and the value is not 2XX,
- indicates any error happen.
-**/
-EFI_STATUS
-EFIAPI
-RedfishPostToPayload (
- IN REDFISH_PAYLOAD Target,
- IN REDFISH_PAYLOAD Payload,
- OUT REDFISH_RESPONSE *RedResponse
- );
-
-/**
- Use HTTP DELETE to remove a resource.
-
- This function uses the RedfishService to remove a Redfish resource which is addressed
- by input Uri (only the relative path is required). The corresponding redfish response will
- returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
- messages.
-
- Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
- redfish response data.
-
- @param[in] RedfishService The Service to access the Redfish resources.
- @param[in] Uri Relative path to address the resource.
- @param[out] RedResponse Pointer to the Redfish response data.
-
- @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
- NULL and the value is 2XX, the Redfish resource has been removed.
- If there is any message returned from server, it will be returned
- in Payload within RedResponse.
- @retval EFI_INVALID_PARAMETER RedfishService, Uri, or RedResponse is NULL.
- @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
- more error info from returned HTTP StatusCode, Headers and Payload
- within RedResponse:
- 1. If the returned StatusCode is NULL, indicates any error happen.
- 2. If the returned StatusCode is not NULL and the value is not 2XX,
- indicates any error happen.
-**/
-EFI_STATUS
-EFIAPI
-RedfishDeleteByUri (
- IN REDFISH_SERVICE RedfishService,
- IN CONST CHAR8 *Uri,
- OUT REDFISH_RESPONSE *RedResponse
- );
-
-/**
- Dump text in fractions.
-
- @param[in] String ASCII string to dump.
-
-**/
-VOID
-RedfishDumpJsonStringFractions (
- IN CHAR8 *String
- );
-
-/**
- Extract the JSON text content from REDFISH_PAYLOAD and dump to debug console.
-
- @param[in] Payload The Redfish payload to dump.
-
-**/
-VOID
-RedfishDumpPayload (
- IN REDFISH_PAYLOAD Payload
- );
-
-/**
- Dump text in JSON value.
-
- @param[in] JsonValue The Redfish JSON value to dump.
-
-**/
-VOID
-RedfishDumpJson (
- IN EDKII_JSON_VALUE JsonValue
- );
-
-/**
- This function will cleanup the HTTP header and Redfish payload resources.
-
- @param[in] StatusCode The status code in HTTP response message.
- @param[in] HeaderCount Number of HTTP header structures in Headers list.
- @param[in] Headers Array containing list of HTTP headers.
- @param[in] Payload The Redfish payload to dump.
-
-**/
-VOID
-RedfishFreeResponse (
- IN EFI_HTTP_STATUS_CODE *StatusCode,
- IN UINTN HeaderCount,
- IN EFI_HTTP_HEADER *Headers,
- IN REDFISH_PAYLOAD Payload
- );
-
-/**
- Check if the "@odata.type" in Payload is valid or not.
-
- @param[in] Payload The Redfish payload to be checked.
- @param[in] OdataTypeName OdataType will be retrived from mapping list.
- @param[in] OdataTypeMappingList The list of OdataType.
- @param[in] OdataTypeMappingListSize The number of mapping list
-
- @return TRUE if the "@odata.type" in Payload is valid, otherwise FALSE.
-
-**/
-BOOLEAN
-RedfishIsValidOdataType (
- IN REDFISH_PAYLOAD Payload,
- IN CONST CHAR8 *OdataTypeName,
- IN REDFISH_ODATA_TYPE_MAPPING *OdataTypeMappingList,
- IN UINTN OdataTypeMappingListSize
- );
-
-/**
- Check if the payload is collection
-
- @param[in] Payload The Redfish payload to be checked.
-
- @return TRUE if the payload is collection.
-
-**/
-BOOLEAN
-RedfishIsPayloadCollection (
- IN REDFISH_PAYLOAD Payload
- );
-
-/**
- Get collection size.
-
- @param[in] Payload The Redfish collection payload
- @param[in] CollectionSize Size of this collection
-
- @return EFI_SUCCESS Coolection size is returned in CollectionSize
- @return EFI_INVALID_PARAMETER The payload is not a collection.
-**/
-EFI_STATUS
-RedfishGetCollectionSize (
- IN REDFISH_PAYLOAD Payload,
- IN UINTN *CollectionSize
- );
-
-/**
- Get Redfish payload of collection member
-
- @param[in] Payload The Redfish collection payload
- @param[in] Index Index of collection member
-
- @return NULL Fail to get collection member.
- @return Non NULL Payload is returned.
-**/
-REDFISH_PAYLOAD
-RedfishGetPayloadByIndex (
- IN REDFISH_PAYLOAD Payload,
- IN UINTN Index
- );
-
-/**
- Check and return Redfish resource of the given Redpath.
-
- @param[in] RedfishService Pointer to REDFISH_SERVICE
- @param[in] Redpath Redpath of the resource.
- @param[in] Response Optional return the resource.
-
- @return EFI_STATUS
-**/
-EFI_STATUS
-RedfishCheckIfRedpathExist (
- IN REDFISH_SERVICE RedfishService,
- IN CHAR8 *Redpath,
- IN REDFISH_RESPONSE *Response OPTIONAL
- );
-
-/**
- This function returns the string of Redfish service version.
-
- @param[in] RedfishService Redfish service instance.
- @param[out] ServiceVersionStr Redfish service string.
-
- @return EFI_STATUS
-
-**/
-EFI_STATUS
-RedfishGetServiceVersion (
- IN REDFISH_SERVICE RedfishService,
- OUT CHAR8 **ServiceVersionStr
- );
-
-/**
- This function returns the string of Redfish service version.
-
- @param[in] ServiceVerisonStr The string of Redfish service version.
- @param[in] Url The URL to build Redpath with ID.
- Start with "/", for example "/Registries"
- @param[in] Id ID string
- @param[out] Redpath Pointer to retrive Redpath, caller has to free
- the memory allocated for this string.
- @return EFI_STATUS
-
-**/
-EFI_STATUS
-RedfishBuildRedpathUseId (
- IN CHAR8 *ServiceVerisonStr,
- IN CHAR8 *Url,
- IN CHAR8 *Id,
- OUT CHAR8 **Redpath
- );
-
-#endif
diff --git a/RedfishPkg/PrivateLibrary/RedfishLib/RedfishLib.c b/RedfishPkg/PrivateLibrary/RedfishLib/RedfishLib.c
index 9f9d377..c52a518 100644
--- a/RedfishPkg/PrivateLibrary/RedfishLib/RedfishLib.c
+++ b/RedfishPkg/PrivateLibrary/RedfishLib/RedfishLib.c
@@ -583,6 +583,104 @@ RedfishPatchToPayload (
return EFI_SUCCESS;
}

+
+/**
+ Use HTTP POST to create new Redfish resource in the Resource Collection.
+
+ The POST request should be submitted to the Resource Collection in which the new resource
+ is to belong. The Resource Collection is addressed by URI. The Redfish may
+ ignore any service controlled properties. The corresponding redfish response will returned,
+ including HTTP StatusCode, Headers and Payload which record any HTTP response messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+ @param[in] Uri Relative path to address the resource.
+ @param[in] Content JSON represented properties to be update.
+ @param[in] ContentSize Size of the Content to be send to Redfish service
+ @param[in] ContentType Type of the Content to be send to Redfish service
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX. The Redfish resource will be returned
+ in Payload within RedResponse if server send it back in the HTTP
+ response message body.
+ @retval EFI_INVALID_PARAMETER RedfishService, Uri, Content, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishPostToUriEx (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *Uri,
+ IN CONST CHAR8 *Content,
+ IN UINTN ContentSize,
+ IN CONST CHAR8 *ContentType,
+ OUT REDFISH_RESPONSE *RedResponse
+ )
+{
+ EFI_STATUS Status;
+ EDKII_JSON_VALUE JsonValue;
+
+ Status = EFI_SUCCESS;
+ JsonValue = NULL;
+
+ if ((RedfishService == NULL) || (Uri == NULL) || (Content == NULL) || (RedResponse == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE));
+
+ JsonValue = (EDKII_JSON_VALUE)postUriFromService (
+ RedfishService,
+ Uri,
+ Content,
+ ContentSize,
+ ContentType,
+ &(RedResponse->StatusCode)
+ );
+
+ //
+ // 1. If the returned StatusCode is NULL, indicates any error happen.
+ //
+ if (RedResponse->StatusCode == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ //
+ // 2. If the returned StatusCode is not NULL and the value is not 2XX, indicates any error happen.
+ // NOTE: If there is any error message returned from server, it will be returned in
+ // Payload within RedResponse.
+ //
+ if ((*(RedResponse->StatusCode) < HTTP_STATUS_200_OK) || \
+ (*(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT))
+ {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ON_EXIT:
+ if (JsonValue != NULL) {
+ RedResponse->Payload = createRedfishPayload (JsonValue, RedfishService);
+ if (RedResponse->Payload == NULL) {
+ //
+ // Ignore the error when create RedfishPayload, just free the JsonValue since it's not what
+ // we care about if the returned StatusCode is 2XX.
+ //
+ JsonValueFree (JsonValue);
+ }
+ }
+
+ return Status;
+}
+
+
/**
Use HTTP POST to create a new resource in target payload.

@@ -738,6 +836,97 @@ ON_EXIT:
return Status;
}

+
+/**
+ Use HTTP DELETE to remove a resource.
+
+ This function uses the RedfishService to remove a Redfish resource which is addressed
+ by input Uri (only the relative path is required). The corresponding redfish response will
+ returned, including HTTP StatusCode, Headers and Payload which record any HTTP response
+ messages.
+
+ Callers are responsible for freeing the HTTP StatusCode, Headers and Payload returned in
+ redfish response data.
+
+ @param[in] RedfishService The Service to access the Redfish resources.
+ @param[in] Uri Relative path to address the resource.
+ @param[in] Content JSON represented properties to be deleted.
+ @param[out] RedResponse Pointer to the Redfish response data.
+
+ @retval EFI_SUCCESS The opeartion is successful, indicates the HTTP StatusCode is not
+ NULL and the value is 2XX, the Redfish resource has been removed.
+ If there is any message returned from server, it will be returned
+ in Payload within RedResponse.
+ @retval EFI_INVALID_PARAMETER RedfishService, Uri, or RedResponse is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. Callers can get
+ more error info from returned HTTP StatusCode, Headers and Payload
+ within RedResponse:
+ 1. If the returned StatusCode is NULL, indicates any error happen.
+ 2. If the returned StatusCode is not NULL and the value is not 2XX,
+ indicates any error happen.
+**/
+EFI_STATUS
+EFIAPI
+RedfishDeleteByUriEx (
+ IN REDFISH_SERVICE RedfishService,
+ IN CONST CHAR8 *Uri,
+ IN CONST CHAR8 *Content,
+ OUT REDFISH_RESPONSE *RedResponse
+ )
+{
+ EFI_STATUS Status;
+ EDKII_JSON_VALUE JsonValue;
+
+ Status = EFI_SUCCESS;
+ JsonValue = NULL;
+
+ if ((RedfishService == NULL) || (Content == NULL) || (Uri == NULL) || (RedResponse == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (RedResponse, sizeof (REDFISH_RESPONSE));
+
+ JsonValue = (EDKII_JSON_VALUE)deleteUriFromServiceEx (
+ RedfishService,
+ Uri,
+ Content,
+ &(RedResponse->StatusCode)
+ );
+
+ //
+ // 1. If the returned StatusCode is NULL, indicates any error happen.
+ //
+ if (RedResponse->StatusCode == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ //
+ // 2. If the returned StatusCode is not NULL and the value is not 2XX, indicates any error happen.
+ // NOTE: If there is any error message returned from server, it will be returned in
+ // Payload within RedResponse.
+ //
+ if ((*(RedResponse->StatusCode) < HTTP_STATUS_200_OK) || \
+ (*(RedResponse->StatusCode) > HTTP_STATUS_206_PARTIAL_CONTENT))
+ {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ON_EXIT:
+ if (JsonValue != NULL) {
+ RedResponse->Payload = createRedfishPayload (JsonValue, RedfishService);
+ if (RedResponse->Payload == NULL) {
+ //
+ // Ignore the error when create RedfishPayload, just free the JsonValue since it's not what
+ // we care about if the returned StatusCode is 2XX.
+ //
+ JsonValueFree (JsonValue);
+ }
+ }
+
+ return Status;
+}
+
/**
Dump text in fractions.

diff --git a/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfishService.h b/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfishService.h
index 5c13b68..75afadc 100644
--- a/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfishService.h
+++ b/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/include/redfishService.h
@@ -129,6 +129,14 @@ deleteUriFromService (
EFI_HTTP_STATUS_CODE **StatusCode
);

+json_t *
+deleteUriFromServiceEx (
+ redfishService *service,
+ const char *uri,
+ const char *content,
+ EFI_HTTP_STATUS_CODE **StatusCode
+ );
+
redfishPayload *
getRedfishServiceRoot (
redfishService *service,
diff --git a/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c b/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c
index afa172b..b8bfabe 100644
--- a/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c
+++ b/RedfishPkg/PrivateLibrary/RedfishLib/edk2libredfish/src/service.c
@@ -923,10 +923,12 @@ ON_EXIT:
return ret;
}

+
json_t *
-deleteUriFromService (
+deleteUriFromServiceEx (
redfishService *service,
const char *uri,
+ const char *content,
EFI_HTTP_STATUS_CODE **StatusCode
)
{
@@ -937,6 +939,8 @@ deleteUriFromService (
EFI_HTTP_REQUEST_DATA *RequestData = NULL;
EFI_HTTP_MESSAGE *RequestMsg = NULL;
EFI_HTTP_MESSAGE ResponseMsg;
+ CHAR8 ContentLengthStr[80];
+ size_t contentLength;

ret = NULL;

@@ -956,7 +960,7 @@ deleteUriFromService (
//
// Step 1: Create HTTP request message with 4 headers:
//
- HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service->basicAuthStr) ? 5 : 4);
+ HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service->basicAuthStr) ? 8 : 7);
if (HttpIoHeader == NULL) {
ret = NULL;
goto ON_EXIT;
@@ -979,6 +983,23 @@ deleteUriFromService (
Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive");
ASSERT_EFI_ERROR (Status);

+ Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", "application/json");
+ ASSERT_EFI_ERROR (Status);
+
+ if(content != NULL){
+ contentLength = strlen (content);
+ AsciiSPrint (
+ ContentLengthStr,
+ sizeof (ContentLengthStr),
+ "%lu",
+ (UINT64)contentLength
+ );
+ Status = HttpIoSetHeader (HttpIoHeader, "Content-Length", ContentLengthStr);
+ ASSERT_EFI_ERROR (Status);
+ Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0");
+ ASSERT_EFI_ERROR (Status);
+ }
+
//
// Step 2: build the rest of HTTP request info.
//
@@ -1003,7 +1024,12 @@ deleteUriFromService (
RequestMsg->Data.Request = RequestData;
RequestMsg->HeaderCount = HttpIoHeader->HeaderCount;
RequestMsg->Headers = HttpIoHeader->Headers;
-
+
+ if(content != NULL){
+ RequestMsg->BodyLength = contentLength;
+ RequestMsg->Body = (VOID *)content;
+ }
+
ZeroMem (&ResponseMsg, sizeof (ResponseMsg));

//
@@ -1057,6 +1083,17 @@ ON_EXIT:
return ret;
}

+json_t *
+deleteUriFromService (
+ redfishService *service,
+ const char *uri,
+ EFI_HTTP_STATUS_CODE **StatusCode
+ )
+{
+ return deleteUriFromServiceEx(service, uri, NULL, StatusCode);
+}
+
+
redfishPayload *
getRedfishServiceRoot (
redfishService *service,
diff --git a/RedfishPkg/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec
index 9886502..0aa2688 100644
--- a/RedfishPkg/RedfishPkg.dec
+++ b/RedfishPkg/RedfishPkg.dec
@@ -64,7 +64,7 @@

## @libraryclass Redfish Helper Library
# Library provides Redfish helper functions.
- RedfishLib|PrivateInclude/Library/RedfishLib.h
+ RedfishLib|Include/Library/RedfishLib.h

[Protocols]
## Include/Protocol/EdkIIRedfishCredential.h
--
2.6.1.windows.1
-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.


[PATCH] RedfishPkg: RedfishDiscoverDxe: USB Redfish host interface is not supported

Igor Kulchytskyy
 

Host Interface details are discribed by the SMBIOS Type 42 table.
The table is published by the RedfishHostInterfaceDxe driver.
That driver supports PCI-E and USB host interface types.
The table is consumed by the RedfishGetHostInterfaceProtocolData function in the RedfishDiscoverDxe driver.
That function only supports PCI-E interface.


Cc: Abner Chang <Abner.Chang@...>
Cc: Nickle Wang <nickle.wang@...>
Signed-off-by: Igor Kulchytskyy <igork@...>
---
RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c b/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c
index d79750b..a3b977f 100644
--- a/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c
+++ b/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c
@@ -65,10 +65,15 @@ RedfishGetHostInterfaceProtocolData (
RecordTmp = (UINT8 *)Record + Offset;

//
- // Check Device Type, only PCI/PCIe Network Interface v2 is supported now.
+ // Check Device Type, PCI/PCIe and USB Network Interface v2 is supported.
//
- if (*RecordTmp == REDFISH_HOST_INTERFACE_DEVICE_TYPE_PCI_PCIE_V2) {
- ASSERT (SpecificDataLen == sizeof (PCI_OR_PCIE_INTERFACE_DEVICE_DESCRIPTOR_V2) + 1);
+ if ((*RecordTmp == REDFISH_HOST_INTERFACE_DEVICE_TYPE_PCI_PCIE_V2) || (*RecordTmp == REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2)) {
+ if (*RecordTmp == REDFISH_HOST_INTERFACE_DEVICE_TYPE_PCI_PCIE_V2){
+ ASSERT (SpecificDataLen == sizeof (PCI_OR_PCIE_INTERFACE_DEVICE_DESCRIPTOR_V2) + 1);
+ }
+ if (*RecordTmp == REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2){
+ ASSERT (SpecificDataLen > sizeof (REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2) + 1);
+ }
*DeviceDescriptor = (REDFISH_INTERFACE_DATA *)RecordTmp;
Offset = Offset + SpecificDataLen;
RecordTmp = (UINT8 *)Record + Offset;
--
2.6.1.windows.1
-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.


[PATCH 1/1] Ext4Pkg: Sanity check the inode size

Pedro Falcato
 

Check its alignment and value for possible bad values.

Cc: Marvin Häuser <mhaeuser@...>
Signed-off-by: Pedro Falcato <pedro.falcato@...>
---
Features/Ext4Pkg/Ext4Dxe/Superblock.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/Features/Ext4Pkg/Ext4Dxe/Superblock.c b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
index c22155ba11b4..edee051c41e8 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Superblock.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
@@ -189,6 +189,12 @@ Ext4OpenSuperblock (
Partition->FeaturesIncompat = Sb->s_feature_incompat;
Partition->FeaturesRoCompat = Sb->s_feature_ro_compat;
Partition->InodeSize = Sb->s_inode_size;
+
+ // Check for proper alignment of InodeSize and that InodeSize is indeed larger than
+ // the minimum size, 128 bytes.
+ if (((Partition->InodeSize % 4) != 0) || (Partition->InodeSize < EXT4_GOOD_OLD_INODE_SIZE)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
} else {
// GOOD_OLD_REV
Partition->FeaturesCompat = Partition->FeaturesIncompat = Partition->FeaturesRoCompat = 0;
--
2.37.1


Re: [edk2-platforms][PATCH v3 1/1] Ext4Pkg: Code correctness and security improvements

Pedro Falcato
 

Pushed as c367ec5


On Sun, Aug 7, 2022 at 12:21 AM Pedro Falcato via groups.io <pedro.falcato=gmail.com@groups.io> wrote:


On Fri, Jul 29, 2022 at 4:54 PM Savva Mitrofanov <savvamtr@...> wrote:
This changes tends to improve security of code sections by fixing
integer overflows, missing alignment checks, unsafe casts, also
simplified some routines, fixed compiler warnings and corrected some
code mistakes.

- Set HoleLen to UINT64 to prevent truncation in Ext4Read function
- Replace EXT4_BLOCK_NR with 32-bit EXT2_BLOCK_NR in BlockMap, because
by specification files using block maps must be placed within the first
2^32 blocks of a filesystem
- Replace UNREACHABLE with ASSERT (FALSE) in case of new checksum
algorithms, due to it is an invariant violation rather than unreachable
path
- Solve compiler warnings. Initialize all fields in gExt4BindingProtocol
Fix comparison of integer expressions of different signedness
- Field name_len has type CHAR8, while filename limit is 255
(EXT4_NAME_MAX), so because structure EXT4_DIR_ENTRY would be
unchangeable in future, we could drop this check without any
assertions
- Simplify Ext4RemoveDentry logic by using IsNodeInList
- Fix possible int overflow in Ext4ExtentsMapKeyCompare
- Return bad block type in Ext4GetBlockpath
- Adds 4-byte aligned check for superblock group descriptor size field

Cc: Marvin Häuser <mhaeuser@...>
Cc: Pedro Falcato <pedro.falcato@...>
Cc: Vitaly Cheptsov <vit9696@...>
Signed-off-by: Savva Mitrofanov <savvamtr@...>
---
 Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h   |  3 +-
 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h    |  2 +-
 Features/Ext4Pkg/Ext4Dxe/BlockGroup.c |  4 +--
 Features/Ext4Pkg/Ext4Dxe/BlockMap.c   | 18 ++++++++----
 Features/Ext4Pkg/Ext4Dxe/Directory.c  | 29 ++------------------
 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c    | 10 ++++---
 Features/Ext4Pkg/Ext4Dxe/Extents.c    |  8 ++++--
 Features/Ext4Pkg/Ext4Dxe/Inode.c      | 10 +++----
 Features/Ext4Pkg/Ext4Dxe/Superblock.c | 15 +++++-----
 9 files changed, 44 insertions(+), 55 deletions(-)

diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
index a55cd2fa68ad..7a19d2f79d53 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
@@ -338,7 +338,7 @@ STATIC_ASSERT (
 #define EXT4_TIND_BLOCK  14
 #define EXT4_NR_BLOCKS   15

-#define EXT4_GOOD_OLD_INODE_SIZE  128
+#define EXT4_GOOD_OLD_INODE_SIZE  128U

 typedef struct _Ext4_I_OSD2_Linux {
   UINT16    l_i_blocks_high;
@@ -463,6 +463,7 @@ typedef struct {
 #define EXT4_EXTENT_MAX_INITIALIZED  (1 << 15)

 typedef UINT64  EXT4_BLOCK_NR;
+typedef UINT32  EXT2_BLOCK_NR;
 typedef UINT32  EXT4_INO_NR;

 // 2 is always the root inode number in ext4
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
index b1508482b0a7..b446488b2112 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
@@ -1165,7 +1165,7 @@ EFI_STATUS
 Ext4GetBlocks (
   IN  EXT4_PARTITION  *Partition,
   IN  EXT4_FILE       *File,
-  IN  EXT4_BLOCK_NR   LogicalBlock,
+  IN  EXT2_BLOCK_NR   LogicalBlock,
   OUT EXT4_EXTENT     *Extent
   );

diff --git a/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c b/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
index 9a1a41901f36..572e8f60ab92 100644
--- a/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
+++ b/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
@@ -218,9 +218,9 @@ Ext4CalculateBlockGroupDescChecksum (
   IN UINT32                       BlockGroupNum
   )
 {
-  if (Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+  if ((Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) != 0) {
     return Ext4CalculateBlockGroupDescChecksumMetadataCsum (Partition, BlockGroupDesc, BlockGroupNum);
-  } else if (Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
+  } else if ((Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) != 0) {
     return Ext4CalculateBlockGroupDescChecksumGdtCsum (Partition, BlockGroupDesc, BlockGroupNum);
   }

diff --git a/Features/Ext4Pkg/Ext4Dxe/BlockMap.c b/Features/Ext4Pkg/Ext4Dxe/BlockMap.c
index 1a06ac9fbf86..2bc629fe9d38 100644
--- a/Features/Ext4Pkg/Ext4Dxe/BlockMap.c
+++ b/Features/Ext4Pkg/Ext4Dxe/BlockMap.c
@@ -70,7 +70,7 @@ UINTN
 Ext4GetBlockPath (
   IN  CONST EXT4_PARTITION  *Partition,
   IN  UINT32                LogicalBlock,
-  OUT EXT4_BLOCK_NR         BlockPath[EXT4_MAX_BLOCK_PATH]
+  OUT EXT2_BLOCK_NR         BlockPath[EXT4_MAX_BLOCK_PATH]
   )
 {
   // The logic behind the block map is very much like a page table
@@ -123,7 +123,7 @@ Ext4GetBlockPath (
       break;
     default:
       // EXT4_TYPE_BAD_BLOCK
-      return -1;
+      break;
   }

   return Type + 1;
@@ -213,12 +213,12 @@ EFI_STATUS
 Ext4GetBlocks (
   IN  EXT4_PARTITION  *Partition,
   IN  EXT4_FILE       *File,
-  IN  EXT4_BLOCK_NR   LogicalBlock,
+  IN  EXT2_BLOCK_NR   LogicalBlock,
   OUT EXT4_EXTENT     *Extent
   )
 {
   EXT4_INODE     *Inode;
-  EXT4_BLOCK_NR  BlockPath[EXT4_MAX_BLOCK_PATH];
+  EXT2_BLOCK_NR  BlockPath[EXT4_MAX_BLOCK_PATH];
   UINTN          BlockPathLength;
   UINTN          Index;
   UINT32         *Buffer;
@@ -230,7 +230,7 @@ Ext4GetBlocks (

   BlockPathLength = Ext4GetBlockPath (Partition, LogicalBlock, BlockPath);

-  if (BlockPathLength == (UINTN)-1) {
+  if (BlockPathLength - 1 == EXT4_TYPE_BAD_BLOCK) {
     // Bad logical block (out of range)
     return EFI_NO_MAPPING;
   }
@@ -272,7 +272,13 @@ Ext4GetBlocks (
     }
   }

-  Ext4GetExtentInBlockMap (Buffer, Partition->BlockSize / sizeof (UINT32), BlockPath[BlockPathLength - 1], Extent);
+  Ext4GetExtentInBlockMap (
+    Buffer,
+    Partition->BlockSize / sizeof (UINT32),
+    BlockPath[BlockPathLength - 1],
+    Extent
+    );
+
   FreePool (Buffer);

   return EFI_SUCCESS;
diff --git a/Features/Ext4Pkg/Ext4Dxe/Directory.c b/Features/Ext4Pkg/Ext4Dxe/Directory.c
index 682f66ad5525..4441e6d192b6 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Directory.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Directory.c
@@ -74,7 +74,7 @@ Ext4ValidDirent (
   }

   // Dirent sizes need to be 4 byte aligned
-  if (Dirent->rec_len % 4) {
+  if ((Dirent->rec_len % 4) != 0) {
     return FALSE;
   }

@@ -160,17 +160,6 @@ Ext4RetrieveDirent (
         return EFI_VOLUME_CORRUPTED;
       }

-      // Ignore names bigger than our limit.
-
-      /* Note: I think having a limit is sane because:
-        1) It's nicer to work with.
-        2) Linux and a number of BSDs also have a filename limit of 255.
-      */
-      if (Entry->name_len > EXT4_NAME_MAX) {
-        BlockOffset += Entry->rec_len;
-        continue;
-      }
-
       // Unused entry
       if (Entry->inode == 0) {
         BlockOffset += Entry->rec_len;
@@ -548,20 +537,8 @@ Ext4RemoveDentry (
   IN OUT EXT4_DENTRY  *ToBeRemoved
   )
 {
-  EXT4_DENTRY  *D;
-  LIST_ENTRY   *Entry;
-  LIST_ENTRY   *NextEntry;
-
-  BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Parent->Children) {
-    D = EXT4_DENTRY_FROM_DENTRY_LIST (Entry);
-
-    if (D == ToBeRemoved) {
-      RemoveEntryList (Entry);
-      return;
-    }
-  }
-
-  DEBUG ((DEBUG_ERROR, "[ext4] Ext4RemoveDentry did not find the asked-for dentry\n"));
+  ASSERT (IsNodeInList (&ToBeRemoved->ListNode, &Parent->Children));
+  RemoveEntryList (&ToBeRemoved->ListNode);
 }

 /**
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
index 43b9340d3956..2a4f5a7bd0ef 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
@@ -260,10 +260,12 @@ Ext4Stop (

 EFI_DRIVER_BINDING_PROTOCOL  gExt4BindingProtocol =
 {
-  Ext4IsBindingSupported,
-  Ext4Bind,
-  Ext4Stop,
-  EXT4_DRIVER_VERSION
+  .Supported           = Ext4IsBindingSupported,
+  .Start               = Ext4Bind,
+  .Stop                = Ext4Stop,
+  .Version             = EXT4_DRIVER_VERSION,
+  .ImageHandle         = NULL,
+  .DriverBindingHandle = NULL
 };

 /**
diff --git a/Features/Ext4Pkg/Ext4Dxe/Extents.c b/Features/Ext4Pkg/Ext4Dxe/Extents.c
index c3874df71751..369879e07fe7 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Extents.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Extents.c
@@ -257,9 +257,11 @@ Ext4GetExtent (
     return EFI_SUCCESS;
   }

-  if (!(Inode->i_flags & EXT4_EXTENTS_FL)) {
+  if ((Inode->i_flags & EXT4_EXTENTS_FL) == 0) {
     // If this is an older ext2/ext3 filesystem, emulate Ext4GetExtent using the block map
-    Status = Ext4GetBlocks (Partition, File, LogicalBlock, Extent);
+    // By specification files using block maps must be placed within the first 2^32 blocks
+    // of a filesystem, so we can safely cast LogicalBlock to uint32
+    Status = Ext4GetBlocks (Partition, File, (UINT32)LogicalBlock, Extent);
This comment is wrong. It should read something like "Files using block maps are limited to 2^32 blocks, so we can safely...". I'll fix this up myself since this is minor and the rest LGTM.

     if (!EFI_ERROR (Status)) {
       Ext4CacheExtents (File, Extent, 1);
@@ -420,7 +422,7 @@ Ext4ExtentsMapKeyCompare (
   Extent = UserStruct;
   Block  = (UINT32)(UINTN)StandaloneKey;

-  if ((Block >= Extent->ee_block) && (Block < Extent->ee_block + Ext4GetExtentLength (Extent))) {
+  if ((Block >= Extent->ee_block) && (Block - Extent->ee_block < Ext4GetExtentLength (Extent))) {
     return 0;
   }

diff --git a/Features/Ext4Pkg/Ext4Dxe/Inode.c b/Features/Ext4Pkg/Ext4Dxe/Inode.c
index 831f5946e870..7f8be2f02643 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Inode.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Inode.c
@@ -100,7 +100,7 @@ Ext4Read (
   EFI_STATUS   Status;
   BOOLEAN      HasBackingExtent;
   UINT32       HoleOff;
-  UINTN        HoleLen;
+  UINT64       HoleLen;
   UINT64       ExtentStartBytes;
   UINT64       ExtentLengthBytes;
   UINT64       ExtentLogicalBytes;
@@ -155,10 +155,10 @@ Ext4Read (
         HoleLen = (Ext4GetExtentLength (&Extent) * Partition->BlockSize) - HoleOff;
       }

-      WasRead = HoleLen > RemainingRead ? RemainingRead : HoleLen;
+      WasRead = HoleLen > RemainingRead ? RemainingRead : (UINTN)HoleLen;
       // Potential improvement: In the future, we could get the file hole's total
       // size and memset all that
-      SetMem (Buffer, WasRead, 0);
+      ZeroMem (Buffer, WasRead);
     } else {
       ExtentStartBytes = MultU64x32 (
                            LShiftU64 (Extent.ee_start_hi, 32) |
@@ -291,7 +291,7 @@ Ext4FilePhysicalSpace (

     // If HUGE_FILE is enabled and EXT4_HUGE_FILE_FL is set in the inode's flags, each unit
     // in i_blocks corresponds to an actual filesystem block
-    if (File->Inode->i_flags & EXT4_HUGE_FILE_FL) {
+    if ((File->Inode->i_flags & EXT4_HUGE_FILE_FL) != 0) {
       return MultU64x32 (Blocks, File->Partition->BlockSize);
     }
   }
@@ -431,7 +431,7 @@ Ext4FileCreateTime (
   Inode = File->Inode;

   if (!EXT4_INODE_HAS_FIELD (Inode, i_crtime)) {
-    SetMem (Time, sizeof (EFI_TIME), 0);
+    ZeroMem (Time, sizeof (EFI_TIME));
     return;
   }

diff --git a/Features/Ext4Pkg/Ext4Dxe/Superblock.c b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
index 47fc3a65507a..c22155ba11b4 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Superblock.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
@@ -220,7 +220,7 @@ Ext4OpenSuperblock (
     return EFI_UNSUPPORTED;
   }

-  if (Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_CSUM_SEED) {
+  if ((Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_CSUM_SEED) != 0) {
     Partition->InitialSeed = Sb->s_checksum_seed;
   } else {
     Partition->InitialSeed = Ext4CalculateChecksum (Partition, Sb->s_uuid, 16, ~0U);
@@ -257,16 +257,17 @@ Ext4OpenSuperblock (
     ));

   if (EXT4_IS_64_BIT (Partition)) {
+    // s_desc_size should be 4 byte aligned and
+    // 64 bit filesystems need DescSize to be 64 bytes
+    if (((Sb->s_desc_size % 4) != 0) || (Sb->s_desc_size < EXT4_64BIT_BLOCK_DESC_SIZE)) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
     Partition->DescSize = Sb->s_desc_size;
   } else {
     Partition->DescSize = EXT4_OLD_BLOCK_DESC_SIZE;
   }

-  if ((Partition->DescSize < EXT4_64BIT_BLOCK_DESC_SIZE) && EXT4_IS_64_BIT (Partition)) {
-    // 64 bit filesystems need DescSize to be 64 bytes
-    return EFI_VOLUME_CORRUPTED;
-  }
-
   if (!Ext4VerifySuperblockChecksum (Partition, Sb)) {
     DEBUG ((DEBUG_ERROR, "[ext4] Bad superblock checksum %lx\n", Ext4CalculateSuperblockChecksum (Partition, Sb)));
     return EFI_VOLUME_CORRUPTED;
@@ -342,7 +343,7 @@ Ext4CalculateChecksum (
       // For some reason, EXT4 really likes non-inverted CRC32C checksums, so we stick to that here.
       return ~CalculateCrc32c(Buffer, Length, ~InitialValue);
     default:
-      UNREACHABLE ();
+      ASSERT (FALSE);
       return 0;
   }
 }
--
2.37.1

Overall, LGTM.

Reviewed-by: Pedro Falcato <pedro.falcato@...>

--
Pedro Falcato



--
Pedro Falcato


Re: [edk2-platforms][PATCH v3 1/1] Ext4Pkg: Code correctness and security improvements

Pedro Falcato
 



On Fri, Jul 29, 2022 at 4:54 PM Savva Mitrofanov <savvamtr@...> wrote:
This changes tends to improve security of code sections by fixing
integer overflows, missing alignment checks, unsafe casts, also
simplified some routines, fixed compiler warnings and corrected some
code mistakes.

- Set HoleLen to UINT64 to prevent truncation in Ext4Read function
- Replace EXT4_BLOCK_NR with 32-bit EXT2_BLOCK_NR in BlockMap, because
by specification files using block maps must be placed within the first
2^32 blocks of a filesystem
- Replace UNREACHABLE with ASSERT (FALSE) in case of new checksum
algorithms, due to it is an invariant violation rather than unreachable
path
- Solve compiler warnings. Initialize all fields in gExt4BindingProtocol
Fix comparison of integer expressions of different signedness
- Field name_len has type CHAR8, while filename limit is 255
(EXT4_NAME_MAX), so because structure EXT4_DIR_ENTRY would be
unchangeable in future, we could drop this check without any
assertions
- Simplify Ext4RemoveDentry logic by using IsNodeInList
- Fix possible int overflow in Ext4ExtentsMapKeyCompare
- Return bad block type in Ext4GetBlockpath
- Adds 4-byte aligned check for superblock group descriptor size field

Cc: Marvin Häuser <mhaeuser@...>
Cc: Pedro Falcato <pedro.falcato@...>
Cc: Vitaly Cheptsov <vit9696@...>
Signed-off-by: Savva Mitrofanov <savvamtr@...>
---
 Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h   |  3 +-
 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h    |  2 +-
 Features/Ext4Pkg/Ext4Dxe/BlockGroup.c |  4 +--
 Features/Ext4Pkg/Ext4Dxe/BlockMap.c   | 18 ++++++++----
 Features/Ext4Pkg/Ext4Dxe/Directory.c  | 29 ++------------------
 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c    | 10 ++++---
 Features/Ext4Pkg/Ext4Dxe/Extents.c    |  8 ++++--
 Features/Ext4Pkg/Ext4Dxe/Inode.c      | 10 +++----
 Features/Ext4Pkg/Ext4Dxe/Superblock.c | 15 +++++-----
 9 files changed, 44 insertions(+), 55 deletions(-)

diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
index a55cd2fa68ad..7a19d2f79d53 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
@@ -338,7 +338,7 @@ STATIC_ASSERT (
 #define EXT4_TIND_BLOCK  14
 #define EXT4_NR_BLOCKS   15

-#define EXT4_GOOD_OLD_INODE_SIZE  128
+#define EXT4_GOOD_OLD_INODE_SIZE  128U

 typedef struct _Ext4_I_OSD2_Linux {
   UINT16    l_i_blocks_high;
@@ -463,6 +463,7 @@ typedef struct {
 #define EXT4_EXTENT_MAX_INITIALIZED  (1 << 15)

 typedef UINT64  EXT4_BLOCK_NR;
+typedef UINT32  EXT2_BLOCK_NR;
 typedef UINT32  EXT4_INO_NR;

 // 2 is always the root inode number in ext4
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
index b1508482b0a7..b446488b2112 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
@@ -1165,7 +1165,7 @@ EFI_STATUS
 Ext4GetBlocks (
   IN  EXT4_PARTITION  *Partition,
   IN  EXT4_FILE       *File,
-  IN  EXT4_BLOCK_NR   LogicalBlock,
+  IN  EXT2_BLOCK_NR   LogicalBlock,
   OUT EXT4_EXTENT     *Extent
   );

diff --git a/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c b/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
index 9a1a41901f36..572e8f60ab92 100644
--- a/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
+++ b/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
@@ -218,9 +218,9 @@ Ext4CalculateBlockGroupDescChecksum (
   IN UINT32                       BlockGroupNum
   )
 {
-  if (Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+  if ((Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) != 0) {
     return Ext4CalculateBlockGroupDescChecksumMetadataCsum (Partition, BlockGroupDesc, BlockGroupNum);
-  } else if (Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
+  } else if ((Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) != 0) {
     return Ext4CalculateBlockGroupDescChecksumGdtCsum (Partition, BlockGroupDesc, BlockGroupNum);
   }

diff --git a/Features/Ext4Pkg/Ext4Dxe/BlockMap.c b/Features/Ext4Pkg/Ext4Dxe/BlockMap.c
index 1a06ac9fbf86..2bc629fe9d38 100644
--- a/Features/Ext4Pkg/Ext4Dxe/BlockMap.c
+++ b/Features/Ext4Pkg/Ext4Dxe/BlockMap.c
@@ -70,7 +70,7 @@ UINTN
 Ext4GetBlockPath (
   IN  CONST EXT4_PARTITION  *Partition,
   IN  UINT32                LogicalBlock,
-  OUT EXT4_BLOCK_NR         BlockPath[EXT4_MAX_BLOCK_PATH]
+  OUT EXT2_BLOCK_NR         BlockPath[EXT4_MAX_BLOCK_PATH]
   )
 {
   // The logic behind the block map is very much like a page table
@@ -123,7 +123,7 @@ Ext4GetBlockPath (
       break;
     default:
       // EXT4_TYPE_BAD_BLOCK
-      return -1;
+      break;
   }

   return Type + 1;
@@ -213,12 +213,12 @@ EFI_STATUS
 Ext4GetBlocks (
   IN  EXT4_PARTITION  *Partition,
   IN  EXT4_FILE       *File,
-  IN  EXT4_BLOCK_NR   LogicalBlock,
+  IN  EXT2_BLOCK_NR   LogicalBlock,
   OUT EXT4_EXTENT     *Extent
   )
 {
   EXT4_INODE     *Inode;
-  EXT4_BLOCK_NR  BlockPath[EXT4_MAX_BLOCK_PATH];
+  EXT2_BLOCK_NR  BlockPath[EXT4_MAX_BLOCK_PATH];
   UINTN          BlockPathLength;
   UINTN          Index;
   UINT32         *Buffer;
@@ -230,7 +230,7 @@ Ext4GetBlocks (

   BlockPathLength = Ext4GetBlockPath (Partition, LogicalBlock, BlockPath);

-  if (BlockPathLength == (UINTN)-1) {
+  if (BlockPathLength - 1 == EXT4_TYPE_BAD_BLOCK) {
     // Bad logical block (out of range)
     return EFI_NO_MAPPING;
   }
@@ -272,7 +272,13 @@ Ext4GetBlocks (
     }
   }

-  Ext4GetExtentInBlockMap (Buffer, Partition->BlockSize / sizeof (UINT32), BlockPath[BlockPathLength - 1], Extent);
+  Ext4GetExtentInBlockMap (
+    Buffer,
+    Partition->BlockSize / sizeof (UINT32),
+    BlockPath[BlockPathLength - 1],
+    Extent
+    );
+
   FreePool (Buffer);

   return EFI_SUCCESS;
diff --git a/Features/Ext4Pkg/Ext4Dxe/Directory.c b/Features/Ext4Pkg/Ext4Dxe/Directory.c
index 682f66ad5525..4441e6d192b6 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Directory.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Directory.c
@@ -74,7 +74,7 @@ Ext4ValidDirent (
   }

   // Dirent sizes need to be 4 byte aligned
-  if (Dirent->rec_len % 4) {
+  if ((Dirent->rec_len % 4) != 0) {
     return FALSE;
   }

@@ -160,17 +160,6 @@ Ext4RetrieveDirent (
         return EFI_VOLUME_CORRUPTED;
       }

-      // Ignore names bigger than our limit.
-
-      /* Note: I think having a limit is sane because:
-        1) It's nicer to work with.
-        2) Linux and a number of BSDs also have a filename limit of 255.
-      */
-      if (Entry->name_len > EXT4_NAME_MAX) {
-        BlockOffset += Entry->rec_len;
-        continue;
-      }
-
       // Unused entry
       if (Entry->inode == 0) {
         BlockOffset += Entry->rec_len;
@@ -548,20 +537,8 @@ Ext4RemoveDentry (
   IN OUT EXT4_DENTRY  *ToBeRemoved
   )
 {
-  EXT4_DENTRY  *D;
-  LIST_ENTRY   *Entry;
-  LIST_ENTRY   *NextEntry;
-
-  BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Parent->Children) {
-    D = EXT4_DENTRY_FROM_DENTRY_LIST (Entry);
-
-    if (D == ToBeRemoved) {
-      RemoveEntryList (Entry);
-      return;
-    }
-  }
-
-  DEBUG ((DEBUG_ERROR, "[ext4] Ext4RemoveDentry did not find the asked-for dentry\n"));
+  ASSERT (IsNodeInList (&ToBeRemoved->ListNode, &Parent->Children));
+  RemoveEntryList (&ToBeRemoved->ListNode);
 }

 /**
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
index 43b9340d3956..2a4f5a7bd0ef 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
@@ -260,10 +260,12 @@ Ext4Stop (

 EFI_DRIVER_BINDING_PROTOCOL  gExt4BindingProtocol =
 {
-  Ext4IsBindingSupported,
-  Ext4Bind,
-  Ext4Stop,
-  EXT4_DRIVER_VERSION
+  .Supported           = Ext4IsBindingSupported,
+  .Start               = Ext4Bind,
+  .Stop                = Ext4Stop,
+  .Version             = EXT4_DRIVER_VERSION,
+  .ImageHandle         = NULL,
+  .DriverBindingHandle = NULL
 };

 /**
diff --git a/Features/Ext4Pkg/Ext4Dxe/Extents.c b/Features/Ext4Pkg/Ext4Dxe/Extents.c
index c3874df71751..369879e07fe7 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Extents.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Extents.c
@@ -257,9 +257,11 @@ Ext4GetExtent (
     return EFI_SUCCESS;
   }

-  if (!(Inode->i_flags & EXT4_EXTENTS_FL)) {
+  if ((Inode->i_flags & EXT4_EXTENTS_FL) == 0) {
     // If this is an older ext2/ext3 filesystem, emulate Ext4GetExtent using the block map
-    Status = Ext4GetBlocks (Partition, File, LogicalBlock, Extent);
+    // By specification files using block maps must be placed within the first 2^32 blocks
+    // of a filesystem, so we can safely cast LogicalBlock to uint32
+    Status = Ext4GetBlocks (Partition, File, (UINT32)LogicalBlock, Extent);
This comment is wrong. It should read something like "Files using block maps are limited to 2^32 blocks, so we can safely...". I'll fix this up myself since this is minor and the rest LGTM.

     if (!EFI_ERROR (Status)) {
       Ext4CacheExtents (File, Extent, 1);
@@ -420,7 +422,7 @@ Ext4ExtentsMapKeyCompare (
   Extent = UserStruct;
   Block  = (UINT32)(UINTN)StandaloneKey;

-  if ((Block >= Extent->ee_block) && (Block < Extent->ee_block + Ext4GetExtentLength (Extent))) {
+  if ((Block >= Extent->ee_block) && (Block - Extent->ee_block < Ext4GetExtentLength (Extent))) {
     return 0;
   }

diff --git a/Features/Ext4Pkg/Ext4Dxe/Inode.c b/Features/Ext4Pkg/Ext4Dxe/Inode.c
index 831f5946e870..7f8be2f02643 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Inode.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Inode.c
@@ -100,7 +100,7 @@ Ext4Read (
   EFI_STATUS   Status;
   BOOLEAN      HasBackingExtent;
   UINT32       HoleOff;
-  UINTN        HoleLen;
+  UINT64       HoleLen;
   UINT64       ExtentStartBytes;
   UINT64       ExtentLengthBytes;
   UINT64       ExtentLogicalBytes;
@@ -155,10 +155,10 @@ Ext4Read (
         HoleLen = (Ext4GetExtentLength (&Extent) * Partition->BlockSize) - HoleOff;
       }

-      WasRead = HoleLen > RemainingRead ? RemainingRead : HoleLen;
+      WasRead = HoleLen > RemainingRead ? RemainingRead : (UINTN)HoleLen;
       // Potential improvement: In the future, we could get the file hole's total
       // size and memset all that
-      SetMem (Buffer, WasRead, 0);
+      ZeroMem (Buffer, WasRead);
     } else {
       ExtentStartBytes = MultU64x32 (
                            LShiftU64 (Extent.ee_start_hi, 32) |
@@ -291,7 +291,7 @@ Ext4FilePhysicalSpace (

     // If HUGE_FILE is enabled and EXT4_HUGE_FILE_FL is set in the inode's flags, each unit
     // in i_blocks corresponds to an actual filesystem block
-    if (File->Inode->i_flags & EXT4_HUGE_FILE_FL) {
+    if ((File->Inode->i_flags & EXT4_HUGE_FILE_FL) != 0) {
       return MultU64x32 (Blocks, File->Partition->BlockSize);
     }
   }
@@ -431,7 +431,7 @@ Ext4FileCreateTime (
   Inode = File->Inode;

   if (!EXT4_INODE_HAS_FIELD (Inode, i_crtime)) {
-    SetMem (Time, sizeof (EFI_TIME), 0);
+    ZeroMem (Time, sizeof (EFI_TIME));
     return;
   }

diff --git a/Features/Ext4Pkg/Ext4Dxe/Superblock.c b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
index 47fc3a65507a..c22155ba11b4 100644
--- a/Features/Ext4Pkg/Ext4Dxe/Superblock.c
+++ b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
@@ -220,7 +220,7 @@ Ext4OpenSuperblock (
     return EFI_UNSUPPORTED;
   }

-  if (Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_CSUM_SEED) {
+  if ((Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_CSUM_SEED) != 0) {
     Partition->InitialSeed = Sb->s_checksum_seed;
   } else {
     Partition->InitialSeed = Ext4CalculateChecksum (Partition, Sb->s_uuid, 16, ~0U);
@@ -257,16 +257,17 @@ Ext4OpenSuperblock (
     ));

   if (EXT4_IS_64_BIT (Partition)) {
+    // s_desc_size should be 4 byte aligned and
+    // 64 bit filesystems need DescSize to be 64 bytes
+    if (((Sb->s_desc_size % 4) != 0) || (Sb->s_desc_size < EXT4_64BIT_BLOCK_DESC_SIZE)) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
     Partition->DescSize = Sb->s_desc_size;
   } else {
     Partition->DescSize = EXT4_OLD_BLOCK_DESC_SIZE;
   }

-  if ((Partition->DescSize < EXT4_64BIT_BLOCK_DESC_SIZE) && EXT4_IS_64_BIT (Partition)) {
-    // 64 bit filesystems need DescSize to be 64 bytes
-    return EFI_VOLUME_CORRUPTED;
-  }
-
   if (!Ext4VerifySuperblockChecksum (Partition, Sb)) {
     DEBUG ((DEBUG_ERROR, "[ext4] Bad superblock checksum %lx\n", Ext4CalculateSuperblockChecksum (Partition, Sb)));
     return EFI_VOLUME_CORRUPTED;
@@ -342,7 +343,7 @@ Ext4CalculateChecksum (
       // For some reason, EXT4 really likes non-inverted CRC32C checksums, so we stick to that here.
       return ~CalculateCrc32c(Buffer, Length, ~InitialValue);
     default:
-      UNREACHABLE ();
+      ASSERT (FALSE);
       return 0;
   }
 }
--
2.37.1

Overall, LGTM.

Reviewed-by: Pedro Falcato <pedro.falcato@...>

--
Pedro Falcato


Re: [PATCH v1] DynamicTablesPkg: Fix using RmrNodeCount unitlitialised

Sami Mujawar
 

Hi Edward,

Thank you for this patch.

This fix looks good to me.

Reviewed-by: Sami Mujawar <sami.mujawar@...>

Regards,

Sami Mujawar

On 04/08/2022 10:20 am, Edward Pickup wrote:
Fix using RmrNodeCount uninitliased by initliasing it to zero. Also, add
an additional check for ACPI version. This fixes a crash running on
kvmtool.

Signed-off-by: Edward Pickup <edward.pickup@...>
---

Change can be seen at https://github.com/edpickup/edk2/tree/2322_rmrnodecount_uninitialised_v1

DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c b/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c
index 40d99162cc610de2d0c2f0a5fff6e457c08d07fb..f28973c1a8620aa5b756e85af1b46ebfaf018839 100644
--- a/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c
+++ b/DynamicTablesPkg/Library/Acpi/Arm/AcpiIortLibArm/IortGenerator.c
@@ -2107,6 +2107,8 @@ BuildIortTable (
ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);

ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);


+ RmrNodeCount = 0;

+

if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||

(AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision))

{

@@ -2714,7 +2716,10 @@ BuildIortTable (
}

}


- if (RmrNodeCount > 0) {

+ if ((AcpiTableInfo->AcpiTableRevision >=

+ EFI_ACPI_IO_REMAPPING_TABLE_REVISION_05) &&

+ (RmrNodeCount > 0))

+ {

Status = AddRmrNodes (

This,

CfgMgrProtocol,


Re: [edk2-platforms][PATCH v1 1/1] MinPlatformPkg/TestPointCheckLib: Remove unnecessary GetVariable2() call

Isaac Oram
 

Reviewed-by: Isaac Oram <isaac.w.oram@...>

-----Original Message-----
From: mikuback@... <mikuback@...>
Sent: Friday, August 5, 2022 11:02 AM
To: devel@edk2.groups.io
Cc: Chiu, Chasel <chasel.chiu@...>; Desimone, Nathaniel L <nathaniel.l.desimone@...>; Oram, Isaac W <isaac.w.oram@...>; Gao, Liming <gaoliming@...>; Dong, Eric <eric.dong@...>
Subject: [edk2-platforms][PATCH v1 1/1] MinPlatformPkg/TestPointCheckLib: Remove unnecessary GetVariable2() call

From: Michael Kubacki <michael.kubacki@...>

The data buffer returned from the GetVariable2() call in
TestPointCheckMemoryTypeInformation() is not actually used or freed.

This change removes the unnecessary function call.

Cc: Chasel Chiu <chasel.chiu@...>
Cc: Nate DeSimone <nathaniel.l.desimone@...>
Cc: Isaac Oram <isaac.w.oram@...>
Cc: Liming Gao <gaoliming@...>
Cc: Eric Dong <eric.dong@...>
Signed-off-by: Michael Kubacki <michael.kubacki@...>
---
Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c | 8 --------
1 file changed, 8 deletions(-)

diff --git a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c b/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c
index 9ee9dd252c7e..0ff6789ac621 100644
--- a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c
+++ b/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c
@@ -109,7 +109,6 @@ TestPointCheckMemoryTypeInformation (
EFI_HOB_GUID_TYPE *GuidHob;
VOID *CurrentMemoryTypeInformation;
VOID *PreviousMemoryTypeInformation;
- VOID *VariableMemoryTypeInformation;

DEBUG ((DEBUG_INFO, "==== TestPointCheckMemoryTypeInformation - Enter\n"));
CurrentMemoryTypeInformation = NULL;
@@ -128,13 +127,6 @@ TestPointCheckMemoryTypeInformation (
goto Done;
}

- GetVariable2 (
- EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
- &gEfiMemoryTypeInformationGuid,
- &VariableMemoryTypeInformation,
- NULL
- );
-
if ((CurrentMemoryTypeInformation != NULL) && (PreviousMemoryTypeInformation != NULL)) {
DumpMemoryTypeInfoSummary(CurrentMemoryTypeInformation, PreviousMemoryTypeInformation);
}
--
2.28.0.windows.1


Re: [edk2-platforms][PATCH v1 1/1] MinPlatformPkg/TestPointCheckLib: Prevent modification of HOB data

Isaac Oram
 

Reviewed-by: Isaac Oram <isaac.w.oram@...>

-----Original Message-----
From: mikuback@... <mikuback@...>
Sent: Friday, August 5, 2022 10:16 AM
To: devel@edk2.groups.io
Cc: Chiu, Chasel <chasel.chiu@...>; Desimone, Nathaniel L <nathaniel.l.desimone@...>; Oram, Isaac W <isaac.w.oram@...>; Gao, Liming <gaoliming@...>; Dong, Eric <eric.dong@...>
Subject: [edk2-platforms][PATCH v1 1/1] MinPlatformPkg/TestPointCheckLib: Prevent modification of HOB data

From: Michael Kubacki <michael.kubacki@...>

DumpMemoryTypeInfoSummary() is used to dump information about the MemoryTypeInformation HOB. The dump function currently modifies the data which can corrupt the data for later HOB consumers in the DXE phase.

This change makes DumpMemoryTypeInfoSummary() treat the data as read-only.

Cc: Chasel Chiu <chasel.chiu@...>
Cc: Nate DeSimone <nathaniel.l.desimone@...>
Cc: Isaac Oram <isaac.w.oram@...>
Cc: Liming Gao <gaoliming@...>
Cc: Eric Dong <eric.dong@...>
Signed-off-by: Michael Kubacki <michael.kubacki@...>
---
Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c b/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c
index 9ee9dd252c7e..a30b69dd787c 100644
--- a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMemoryTypeInformation.c
+++ b/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCh
+++ eckMemoryTypeInformation.c
@@ -22,8 +22,8 @@ ShortNameOfMemoryType(

VOID
DumpMemoryTypeInfoSummary (
- IN EFI_MEMORY_TYPE_INFORMATION *CurrentMemoryTypeInformation,
- IN EFI_MEMORY_TYPE_INFORMATION *PreviousMemoryTypeInformation
+ IN CONST EFI_MEMORY_TYPE_INFORMATION *CurrentMemoryTypeInformation,
+ IN CONST EFI_MEMORY_TYPE_INFORMATION *PreviousMemoryTypeInformation
)
{
UINTN Index;
@@ -83,7 +83,6 @@ DumpMemoryTypeInfoSummary (
}

if (Next != Previous) {
- PreviousMemoryTypeInformation[Index].NumberOfPages = Next;
MemoryTypeInformationModified = TRUE;
}

@@ -110,7 +109,7 @@ TestPointCheckMemoryTypeInformation (
VOID *CurrentMemoryTypeInformation;
VOID *PreviousMemoryTypeInformation;
VOID *VariableMemoryTypeInformation;
-
+
DEBUG ((DEBUG_INFO, "==== TestPointCheckMemoryTypeInformation - Enter\n"));
CurrentMemoryTypeInformation = NULL;
PreviousMemoryTypeInformation = NULL;
--
2.28.0.windows.1


[edk2-platforms][PATCH v1 1/1] MinPlatformPkg: Add FspNvsBuffer compression option

Michael Kubacki
 

From: Michael Kubacki <michael.kubacki@...>

Adds a PCD called "PcdEnableCompressedFspNvsBuffer" that allows the
"FspNvsBuffer" UEFI variable data to be saved as compressed data.

Especially due to the nature of the data saved in this variable, it
compresses well. For example, it has been found to reduce ~63KB
of data to ~13KB. Boot time impact has been found to be negligible.

The default value is FALSE to keep default behavior consistent.

Decompression can be performed on the variable data using the
standard UefiDecompressLib.

Cc: Chasel Chiu <chasel.chiu@...>
Cc: Nate DeSimone <nathaniel.l.desimone@...>
Cc: Isaac Oram <isaac.w.oram@...>
Cc: Liming Gao <gaoliming@...>
Cc: Eric Dong <eric.dong@...>
Signed-off-by: Michael Kubacki <michael.kubacki@...>
---
Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/SaveMemoryConf=
ig.c | 62 ++++++++++++++++----
Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/SaveMemoryConf=
ig.inf | 4 ++
Platform/Intel/MinPlatformPkg/MinPlatformPkg.dec =
| 6 ++
Platform/Intel/MinPlatformPkg/MinPlatformPkg.dsc =
| 1 +
4 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/Sa=
veMemoryConfig.c b/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryCon=
fig/SaveMemoryConfig.c
index 0215e8eeddfb..95b8cef8b32b 100644
--- a/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/SaveMemor=
yConfig.c
+++ b/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/SaveMemor=
yConfig.c
@@ -3,6 +3,7 @@
exists, and saves the data to nvRAM.
=20
Copyright (c) 2017 - 2022, Intel Corporation. All rights reserved.<BR>
+Copyright (c) Microsoft Corporation.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
=20
**/
@@ -10,6 +11,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include <Base.h>
#include <Uefi.h>
#include <Library/BaseLib.h>
+#include <Library/CompressLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/HobLib.h>
@@ -19,6 +21,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include <Library/BaseMemoryLib.h>
#include <Library/LargeVariableReadLib.h>
#include <Library/LargeVariableWriteLib.h>
+#include <Library/PcdLib.h>
#include <Library/VariableWriteLib.h>
#include <Guid/FspNonVolatileStorageHob2.h>
=20
@@ -38,20 +41,26 @@ SaveMemoryConfigEntryPoint (
IN EFI_SYSTEM_TABLE *SystemTable
)
{
- EFI_STATUS Status;
- EFI_HOB_GUID_TYPE *GuidHob;
- VOID *HobData;
- VOID *VariableData;
- UINTN DataSize;
- UINTN BufferSize;
- BOOLEAN DataIsIdentical;
+ EFI_STATUS Status;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ VOID *HobData;
+ VOID *VariableData;
+ UINTN DataSize;
+ UINTN BufferSize;
+ BOOLEAN DataIsIdentical;
+ VOID *CompressedData;
+ UINT64 CompressedSize;
+ UINTN CompressedAllocationPages;
=20
- DataSize =3D 0;
- BufferSize =3D 0;
- VariableData =3D NULL;
- GuidHob =3D NULL;
- HobData =3D NULL;
- DataIsIdentical =3D FALSE;
+ DataSize =3D 0;
+ BufferSize =3D 0;
+ VariableData =3D NULL;
+ GuidHob =3D NULL;
+ HobData =3D NULL;
+ DataIsIdentical =3D FALSE;
+ CompressedData =3D NULL;
+ CompressedSize =3D 0;
+ CompressedAllocationPages =3D 0;
=20
//
// Search for the Memory Configuration GUID HOB. If it is not present=
, then
@@ -73,6 +82,29 @@ SaveMemoryConfigEntryPoint (
}
}
=20
+ if (PcdGetBool (PcdEnableCompressedFspNvsBuffer)) {
+ if (DataSize > 0) {
+ CompressedAllocationPages =3D EFI_SIZE_TO_PAGES (DataSize);
+ CompressedData =3D AllocatePages (CompressedAllocationP=
ages);
+ if (CompressedData =3D=3D NULL) {
+ DEBUG ((DEBUG_ERROR, "[%a] - Failed to allocate compressed data =
buffer.\n", __FUNCTION__));
+ ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CompressedSize =3D EFI_PAGES_TO_SIZE (CompressedAllocationPages);
+ Status =3D Compress (HobData, DataSize, CompressedData, &C=
ompressedSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "[%a] - failed to compress data. Status =3D=
%r\n", __FUNCTION__, Status));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+ }
+
+ HobData =3D CompressedData;
+ DataSize =3D (UINTN)CompressedSize;
+ }
+
if (HobData !=3D NULL) {
DEBUG ((DEBUG_INFO, "FspNvsHob.NvsDataLength:%d\n", DataSize));
DEBUG ((DEBUG_INFO, "FspNvsHob.NvsDataPtr : 0x%x\n", HobData));
@@ -136,6 +168,10 @@ SaveMemoryConfigEntryPoint (
DEBUG((DEBUG_ERROR, "Memory S3 Data HOB was not found\n"));
}
=20
+ if (CompressedData !=3D NULL) {
+ FreePages (CompressedData, CompressedAllocationPages);
+ }
+
//
// This driver does not produce any protocol services, so always unloa=
d it.
//
diff --git a/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/Sa=
veMemoryConfig.inf b/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryC=
onfig/SaveMemoryConfig.inf
index 61e85a658693..0f12deb131ca 100644
--- a/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/SaveMemor=
yConfig.inf
+++ b/Platform/Intel/MinPlatformPkg/FspWrapper/SaveMemoryConfig/SaveMemor=
yConfig.inf
@@ -26,6 +26,7 @@ [LibraryClasses]
LargeVariableReadLib
LargeVariableWriteLib
BaseLib
+ CompressLib
=20
[Packages]
MdePkg/MdePkg.dec
@@ -45,6 +46,9 @@ [Guids]
gFspNonVolatileStorageHob2Guid ## CONSUMES
gFspNvsBufferVariableGuid ## PRODUCES
=20
+[Pcd]
+ gMinPlatformPkgTokenSpaceGuid.PcdEnableCompressedFspNvsBuffer
+
[Depex]
gEfiVariableArchProtocolGuid AND
gEfiVariableWriteArchProtocolGuid
\ No newline at end of file
diff --git a/Platform/Intel/MinPlatformPkg/MinPlatformPkg.dec b/Platform/=
Intel/MinPlatformPkg/MinPlatformPkg.dec
index db0a19066f11..49e1f070f9ab 100644
--- a/Platform/Intel/MinPlatformPkg/MinPlatformPkg.dec
+++ b/Platform/Intel/MinPlatformPkg/MinPlatformPkg.dec
@@ -305,6 +305,12 @@ [PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynami=
c, PcdsDynamicEx]
=20
gMinPlatformPkgTokenSpaceGuid.PcdPlatformMemoryCheckLevel|0|UINT32|0x3=
0000009
=20
+ ## Controls whether the FSP NVS buffer is saved as compressed data.
+ # Data compression can significantly reduce variable storage usage for=
FSP NVS buffer data.
+ # Platforms that choose to compress the data will need to decompress t=
he variable data upon
+ # extraction.
+ gMinPlatformPkgTokenSpaceGuid.PcdEnableCompressedFspNvsBuffer|FALSE|BO=
OLEAN|0x30000010
+
## This PCD is to control which device is the potential trusted consol=
e input device.<BR><BR>
# For example:<BR>
# USB Short Form: UsbHID(0xFFFF,0xFFFF,0x1,0x1)<BR>
diff --git a/Platform/Intel/MinPlatformPkg/MinPlatformPkg.dsc b/Platform/=
Intel/MinPlatformPkg/MinPlatformPkg.dsc
index 09aa6fe4d51c..ae170f87d548 100644
--- a/Platform/Intel/MinPlatformPkg/MinPlatformPkg.dsc
+++ b/Platform/Intel/MinPlatformPkg/MinPlatformPkg.dsc
@@ -106,6 +106,7 @@ [LibraryClasses.common.DXE_DRIVER]
FspWrapperPlatformLib|MinPlatformPkg/FspWrapper/Library/DxeFspWrapperP=
latformLib/DxeFspWrapperPlatformLib.inf
TestPointCheckLib|MinPlatformPkg/Test/Library/TestPointCheckLib/DxeTes=
tPointCheckLib.inf
TestPointLib|MinPlatformPkg/Test/Library/TestPointLib/DxeTestPointLib.=
inf
+ CompressLib|MinPlatformPkg/Library/CompressLib/CompressLib.inf
=20
[LibraryClasses.common.DXE_SMM_DRIVER]
TestPointCheckLib|MinPlatformPkg/Test/Library/TestPointCheckLib/SmmTes=
tPointCheckLib.inf
--=20
2.28.0.windows.1


[edk2-platforms][PATCH v1 1/1] MinPlatformPkg/TestPointCheckLib: Remove unnecessary GetVariable2() call

Michael Kubacki
 

From: Michael Kubacki <michael.kubacki@...>

The data buffer returned from the GetVariable2() call in
TestPointCheckMemoryTypeInformation() is not actually used or freed.

This change removes the unnecessary function call.

Cc: Chasel Chiu <chasel.chiu@...>
Cc: Nate DeSimone <nathaniel.l.desimone@...>
Cc: Isaac Oram <isaac.w.oram@...>
Cc: Liming Gao <gaoliming@...>
Cc: Eric Dong <eric.dong@...>
Signed-off-by: Michael Kubacki <michael.kubacki@...>
---
Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMem=
oryTypeInformation.c | 8 --------
1 file changed, 8 deletions(-)

diff --git a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib=
/DxeCheckMemoryTypeInformation.c b/Platform/Intel/MinPlatformPkg/Test/Lib=
rary/TestPointCheckLib/DxeCheckMemoryTypeInformation.c
index 9ee9dd252c7e..0ff6789ac621 100644
--- a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeChe=
ckMemoryTypeInformation.c
+++ b/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeChe=
ckMemoryTypeInformation.c
@@ -109,7 +109,6 @@ TestPointCheckMemoryTypeInformation (
EFI_HOB_GUID_TYPE *GuidHob;
VOID *CurrentMemoryTypeInformation;
VOID *PreviousMemoryTypeInformation;
- VOID *VariableMemoryTypeInformation;
=20
DEBUG ((DEBUG_INFO, "=3D=3D=3D=3D TestPointCheckMemoryTypeInformation =
- Enter\n"));
CurrentMemoryTypeInformation =3D NULL;
@@ -128,13 +127,6 @@ TestPointCheckMemoryTypeInformation (
goto Done;
}
=20
- GetVariable2 (
- EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
- &gEfiMemoryTypeInformationGuid,
- &VariableMemoryTypeInformation,
- NULL
- );
-
if ((CurrentMemoryTypeInformation !=3D NULL) && (PreviousMemoryTypeInf=
ormation !=3D NULL)) {
DumpMemoryTypeInfoSummary(CurrentMemoryTypeInformation, PreviousMemo=
ryTypeInformation);
}
--=20
2.28.0.windows.1


[edk2-platforms][PATCH v1 1/1] MinPlatformPkg/TestPointCheckLib: Prevent modification of HOB data

Michael Kubacki
 

From: Michael Kubacki <michael.kubacki@...>

DumpMemoryTypeInfoSummary() is used to dump information about the
MemoryTypeInformation HOB. The dump function currently modifies the
data which can corrupt the data for later HOB consumers in the DXE
phase.

This change makes DumpMemoryTypeInfoSummary() treat the data as
read-only.

Cc: Chasel Chiu <chasel.chiu@...>
Cc: Nate DeSimone <nathaniel.l.desimone@...>
Cc: Isaac Oram <isaac.w.oram@...>
Cc: Liming Gao <gaoliming@...>
Cc: Eric Dong <eric.dong@...>
Signed-off-by: Michael Kubacki <michael.kubacki@...>
---
Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeCheckMem=
oryTypeInformation.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib=
/DxeCheckMemoryTypeInformation.c b/Platform/Intel/MinPlatformPkg/Test/Lib=
rary/TestPointCheckLib/DxeCheckMemoryTypeInformation.c
index 9ee9dd252c7e..a30b69dd787c 100644
--- a/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeChe=
ckMemoryTypeInformation.c
+++ b/Platform/Intel/MinPlatformPkg/Test/Library/TestPointCheckLib/DxeChe=
ckMemoryTypeInformation.c
@@ -22,8 +22,8 @@ ShortNameOfMemoryType(
=20
VOID
DumpMemoryTypeInfoSummary (
- IN EFI_MEMORY_TYPE_INFORMATION *CurrentMemoryTypeInformation,
- IN EFI_MEMORY_TYPE_INFORMATION *PreviousMemoryTypeInformation
+ IN CONST EFI_MEMORY_TYPE_INFORMATION *CurrentMemoryTypeInformation,
+ IN CONST EFI_MEMORY_TYPE_INFORMATION *PreviousMemoryTypeInformation
)
{
UINTN Index;
@@ -83,7 +83,6 @@ DumpMemoryTypeInfoSummary (
}
=20
if (Next !=3D Previous) {
- PreviousMemoryTypeInformation[Index].NumberOfPages =3D Next;
MemoryTypeInformationModified =3D TRUE;
}
=20
@@ -110,7 +109,7 @@ TestPointCheckMemoryTypeInformation (
VOID *CurrentMemoryTypeInformation;
VOID *PreviousMemoryTypeInformation;
VOID *VariableMemoryTypeInformation;
- =20
+
DEBUG ((DEBUG_INFO, "=3D=3D=3D=3D TestPointCheckMemoryTypeInformation =
- Enter\n"));
CurrentMemoryTypeInformation =3D NULL;
PreviousMemoryTypeInformation =3D NULL;
--=20
2.28.0.windows.1


Re: [PATCH v2] MdeModulePkg/NonDiscoverablePciDeviceDxe: Allow partial FreeBuffer

Jeff Brasen
 

-----Original Message-----
From: Ard Biesheuvel <ardb@...>
Sent: Tuesday, August 2, 2022 10:51 AM
To: Jeff Brasen <jbrasen@...>
Cc: devel@edk2.groups.io; hao.a.wu@...; ray.ni@...;
quic_llindhol@...; ardb+tianocore@...
Subject: Re: [PATCH v2] MdeModulePkg/NonDiscoverablePciDeviceDxe:
Allow partial FreeBuffer

External email: Use caution opening links or attachments


On Tue, 2 Aug 2022 at 17:32, Jeff Brasen <jbrasen@...> wrote:



-----Original Message-----
From: Ard Biesheuvel <ardb@...>
Sent: Friday, July 29, 2022 9:48 AM
To: Jeff Brasen <jbrasen@...>
Cc: devel@edk2.groups.io; hao.a.wu@...; ray.ni@...;
quic_llindhol@...; ardb+tianocore@...
Subject: Re: [PATCH v2] MdeModulePkg/NonDiscoverablePciDeviceDxe:
Allow partial FreeBuffer

External email: Use caution opening links or attachments


On Thu, 28 Jul 2022 at 13:25, Jeff Brasen <jbrasen@...> wrote:


Adding Leif/Ard to CC incase they have any comments on this patch.
This generally looks ok to me. I just wonder if it wouldn't be
simpler to reuse the existing allocation descriptor if it is not
being freed entirely. Given the [presumably] the most common case is
to allocate and then free some pages at the end, lowering the page
count on the existing descriptor would cover most cases, and we'd
only need to allocate new ones if pages are being freed at the start or in
the middle.

There is often freeing at the beginning as well as this is being used to create
a 64K aligned section of memory in the case. So it over allocates and the
free's some at the beginning and the end. I could probably make it detect
and use that but figured this code would support all cases and required less
case specific detection.
Ah interesting. Would it help if the allocate routine aligned allocations to their
size?
The PciIo->AllocateBuffer function doesn't support passing the request in so we would need to know that info beforehand. The current calling in the XHCI driver does a free at the beginning and then the end of the buffer so we could the existing allocation tracker but figured it would be better to correct the function just in case someone called it to free in the middle.


Re: [PATCH] RedfishPkg: RedfishDiscoverDxe: USB Redfish host interface is not supported

Chang, Abner
 

[AMD Official Use Only - General]

Hi Igor,
Just found that you don't have the CC for maintainers and your Signed-off-by tag in the commit message.
You can refer to below link for the correct commit message format for the patch.
https://github.com/tianocore/tianocore.github.io/wiki/Laszlo's-unkempt-git-guide-for-edk2-contributors-and-maintainers

Thanks
Abner

-----Original Message-----
From: Igor Kulchytskyy <igork@...>
Sent: Thursday, August 4, 2022 11:25 PM
To: Chang, Abner <Abner.Chang@...>
Cc: nickle.wang@...; Igor Kulchytskyy <igork@...>;
devel@edk2.groups.io
Subject: [PATCH] RedfishPkg: RedfishDiscoverDxe: USB Redfish host
interface is not supported

[CAUTION: External Email]

Host Interface details are discribed by the SMBIOS Type 42 table.
The table is published by the RedfishHostInterfaceDxe driver.
That driver supports PCI-E and USB host interface types.
The table is consumed by the RedfishGetHostInterfaceProtocolData function
in the RedfishDiscoverDxe driver.
That function only supports PCI-E interface.
---
RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c | 11
++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c
b/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c
index d79750b..a3b977f 100644
--- a/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c
+++ b/RedfishPkg/RedfishDiscoverDxe/RedfishSmbiosHostInterface.c
@@ -65,10 +65,15 @@ RedfishGetHostInterfaceProtocolData (
RecordTmp = (UINT8 *)Record + Offset;

//
- // Check Device Type, only PCI/PCIe Network Interface v2 is supported
now.
+ // Check Device Type, PCI/PCIe and USB Network Interface v2 is
supported.
//
- if (*RecordTmp ==
REDFISH_HOST_INTERFACE_DEVICE_TYPE_PCI_PCIE_V2) {
- ASSERT (SpecificDataLen == sizeof
(PCI_OR_PCIE_INTERFACE_DEVICE_DESCRIPTOR_V2) + 1);
+ if ((*RecordTmp ==
REDFISH_HOST_INTERFACE_DEVICE_TYPE_PCI_PCIE_V2) || (*RecordTmp ==
REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2)) {
+ if (*RecordTmp ==
REDFISH_HOST_INTERFACE_DEVICE_TYPE_PCI_PCIE_V2){
+ ASSERT (SpecificDataLen == sizeof
(PCI_OR_PCIE_INTERFACE_DEVICE_DESCRIPTOR_V2) + 1);
+ }
+ if (*RecordTmp ==
REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2){
+ ASSERT (SpecificDataLen > sizeof
(REDFISH_HOST_INTERFACE_DEVICE_TYPE_USB_V2) + 1);
+ }
*DeviceDescriptor = (REDFISH_INTERFACE_DATA *)RecordTmp;
Offset = Offset + SpecificDataLen;
RecordTmp = (UINT8 *)Record + Offset;
--
2.6.1.windows.1
-The information contained in this message may be confidential and
proprietary to American Megatrends (AMI). This communication is intended
to be read only by the individual or entity to whom it is addressed or by their
designee. If the reader of this message is not the intended recipient, you are
on notice that any distribution of this message, in any form, is strictly
prohibited. Please promptly notify the sender by reply e-mail or by
telephone at 770-246-8600, and then delete or destroy all copies of the
transmission.


Re: [PATCH] MdePkg/UefiDevicePathLib: reback the DevicePathUtilitiesStandaloneMm

Michael D Kinney
 

Liming,

Adding a new INF that is type BASE was my original suggestion. Not the name change.

In that thread, I suggested we get input from MM owners before changing the name.

If there are many downstream platforms that are breaking from the name change,
then we should add the old INF back to provide time for all platform to update to
use the new INF name.

Mike

-----Original Message-----
From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of gaoliming via groups.io
Sent: Thursday, August 4, 2022 11:11 PM
To: Huang, Yanbo <yanbo.huang@...>; devel@edk2.groups.io
Cc: Kinney, Michael D <michael.d.kinney@...>; Liu, Zhiguang <zhiguang.liu@...>; Bi, Dandan <dandan.bi@...>
Subject: 回复: [edk2-devel] [PATCH] MdePkg/UefiDevicePathLib: reback the DevicePathUtilitiesStandaloneMm

Yanbo:
If this patch is temporarily added, you can handle it in your downstream code base.

If you request to add DevicePathUtilitiesStandaloneMm for long term compatibility, this topic has been discussed in
https://edk2.groups.io/g/devel/message/91799.

Thanks
Liming
-----邮件原件-----
发件人: Huang, Yanbo <yanbo.huang@...>
发送时间: 2022年8月5日 13:34
收件人: Gao, Liming <gaoliming@...>; devel@edk2.groups.io
抄送: Kinney, Michael D <michael.d.kinney@...>; Liu, Zhiguang
<zhiguang.liu@...>; Bi, Dandan <dandan.bi@...>
主题: RE: [edk2-devel] [PATCH] MdePkg/UefiDevicePathLib: reback the
DevicePathUtilitiesStandaloneMm

Hi Liming,

You mentioned patch rename the DevicePathUtilitiesStandaloneMm to
UefiDevicePathLibBase, but there are some consumer in intel platform still
use the DevicePathUtilitiesStandaloneMm, so downstream will failed in CI
because it cannot find DevicePathUtilitiesStandaloneMm. So the
DevicePathUtilitiesStandaloneMm and UefiDevicePathLibBase must exist at
the same time for a period of time. After downstream finished and platform
change to use UefiDevicePathLibBase, then
DevicePathUtilitiesStandaloneMm can be deleted.

Best Regards,
Yanbo Huang

-----Original Message-----
From: gaoliming <gaoliming@...>
Sent: Friday, August 5, 2022 11:16 AM
To: devel@edk2.groups.io; Huang, Yanbo <yanbo.huang@...>
Cc: Kinney, Michael D <michael.d.kinney@...>; Liu, Zhiguang
<zhiguang.liu@...>
Subject: 回复: [edk2-devel] [PATCH] MdePkg/UefiDevicePathLib: reback the
DevicePathUtilitiesStandaloneMm

Yanbo:
Previous change has been reviewed and merged. Please see the detail
https://edk2.groups.io/g/devel/message/91799

Thanks
Liming
-----邮件原件-----
发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 Huang,
Yanbo
发送时间: 2022年8月5日 10:42
收件人: devel@edk2.groups.io
抄送: Yanbo Huang <yanbo.huang@...>; Michael D Kinney
<michael.d.kinney@...>; Liming Gao <gaoliming@...>;
Zhiguang Liu <zhiguang.liu@...>
主题: [edk2-devel] [PATCH] MdePkg/UefiDevicePathLib: reback the
DevicePathUtilitiesStandaloneMm

From: Yanbo Huang <yanbo.huang@...>

reback the DevicePathUtilitiesStandaloneMm to unblock the downstream
sync

Signed-off-by: Yanbo Huang <yanbo.huang@...>
CC: Michael D Kinney <michael.d.kinney@...>
CC: Liming Gao <gaoliming@...>
CC: Zhiguang Liu <zhiguang.liu@...>

---
.../DevicePathUtilitiesStandaloneMm.c | 39 ++++++++++
.../UefiDevicePathLibStandaloneMm.inf | 75
+++++++++++++++++++
2 files changed, 114 insertions(+)
create mode 100644
MdePkg/Library/UefiDevicePathLib/DevicePathUtilitiesStandaloneMm.c
create mode 100644
MdePkg/Library/UefiDevicePathLib/UefiDevicePathLibStandaloneMm.inf

diff --git
a/MdePkg/Library/UefiDevicePathLib/DevicePathUtilitiesStandaloneMm.c
b/MdePkg/Library/UefiDevicePathLib/DevicePathUtilitiesStandaloneMm.c
new file mode 100644
index 0000000000..096f835b90
--- /dev/null
+++
b/MdePkg/Library/UefiDevicePathLib/DevicePathUtilitiesStandaloneMm.c
@@ -0,0 +1,39 @@
+/** @file
+ Device Path services. The thing to remember is device paths are
+built
out
of
+ nodes. The device path is terminated by an end node that is length
+ sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is
sizeof(EFI_DEVICE_PATH_PROTOCOL)
+ all over this file.
+
+ The only place where multi-instance device paths are supported is
+ in environment varibles. Multi-instance device paths should never
+ be
placed
+ on a Handle.
+
+ Copyright (c) 2006 - 2018, Intel Corporation. All rights
+ reserved.<BR> Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "UefiDevicePathLib.h"
+
+/**
+ Retrieves the device path protocol from a handle.
+
+ This function returns the device path protocol from the handle
specified by
Handle.
+ If Handle is NULL or Handle does not contain a device path
+ protocol,
then
NULL
+ is returned.
+
+ @param Handle The handle from which to
retrieve the device
+ path protocol.
+
+ @return The device path protocol from the handle specified by Handle.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+DevicePathFromHandle (
+ IN EFI_HANDLE Handle
+ )
+{
+ return NULL;
+}
diff --git
a/MdePkg/Library/UefiDevicePathLib/UefiDevicePathLibStandaloneMm.inf
b/MdePkg/Library/UefiDevicePathLib/UefiDevicePathLibStandaloneMm.inf
new file mode 100644
index 0000000000..23fedf38b7
--- /dev/null
+++
b/MdePkg/Library/UefiDevicePathLib/UefiDevicePathLibStandaloneMm.inf
@@ -0,0 +1,75 @@
+## @file
+# Instance of Device Path Library based on Memory Allocation Library.
+#
+# Device Path Library that layers on top of the Memory Allocation
Library.
+#
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights
+reserved.<BR> # Copyright (c) Microsoft Corporation.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent # # ##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = UefiDevicePathLib
+ MODULE_UNI_FILE = UefiDevicePathLib.uni
+ FILE_GUID =
D8E58437-44D3-4154-B7A7-EB794923EF12
+ MODULE_TYPE = MM_STANDALONE
+ PI_SPECIFICATION_VERSION = 0x00010032
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DevicePathLib |
MM_STANDALONE MM_CORE_STANDALONE
+
+
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DevicePathUtilities.c
+ DevicePathUtilitiesStandaloneMm.c
+ DevicePathToText.c
+ DevicePathFromText.c
+ UefiDevicePathLib.c
+ UefiDevicePathLib.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ MemoryAllocationLib
+ DebugLib
+ BaseMemoryLib
+ PcdLib
+ PrintLib
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiVTUTF8Guid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiVT100Guid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiVT100PlusGuid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiPcAnsiGuid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiUartDevicePathGuid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiSasDevicePathGuid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiVirtualDiskGuid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiVirtualCdGuid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiPersistentVirtualDiskGuid
+ ## SOMETIMES_CONSUMES ## GUID
+ gEfiPersistentVirtualCdGuid
+
+[Protocols]
+ gEfiDevicePathProtocolGuid ##
SOMETIMES_CONSUMES
+ gEfiDebugPortProtocolGuid ## UNDEFINED
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdMaximumDevicePathNodeCount ##
SOMETIMES_CONSUMES
--
2.31.1.windows.1










[PATCH] DynamicTablesPkg: Add support to build _DSD

Jeff Brasen
 

Add APIs needed to build _DSD with different UUIDs.
This is per ACPI specification 6.4 s6.2.5.

Adds support for building data packages with format
Package {"Name", Integer}

Signed-off-by: Jeff Brasen <jbrasen@...>
---
.../Include/Library/AmlLib/AmlLib.h | 50 ++++
.../Common/AmlLib/CodeGen/AmlCodeGen.c | 236 ++++++++++++++++++
2 files changed, 286 insertions(+)

diff --git a/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h b/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h
index 6f214c0dfa..30cab3f6bb 100644
--- a/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h
+++ b/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h
@@ -1280,6 +1280,56 @@ AmlAddLpiState (
IN AML_OBJECT_NODE_HANDLE LpiNode
);

+/** AML code generation for a _DSD device data object.
+
+ AmlAddDeviceDataDescriptorPackage (Uuid, ParentNode, NewObjectNode) is
+ equivalent of the following ASL code:
+ ToUUID(Uuid),
+ Package () {}
+
+ @ingroup CodeGenApis
+
+ @param [in] Uuid The Uuid of the descriptor to be created
+ @param [in] DsdNode Node of the DSD Package.
+ @param [out] PackageNode If success, contains the created package node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddDeviceDataDescriptorPackage (
+ IN CONST EFI_GUID *Uuid,
+ IN AML_OBJECT_NODE_HANDLE DsdNode,
+ OUT AML_OBJECT_NODE_HANDLE *PackageNode
+ );
+
+/** AML code generation to add a package with a name and value,
+ * to a parent package
+
+ AmlAddNameValuePackage ("Name", Value, ParentNode) is
+ equivalent of the following ASL code:
+ Package (2) {"Name", Value}
+
+ @ingroup CodeGenApis
+
+ @param [in] Name String to place in first entry of package
+ @param [in] Value Integer to place in second entry of package
+ @param [in] PackageNode Package to add new sub package to.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddNameIntegerPackage (
+ IN CHAR8 *Name,
+ IN UINT64 Value,
+ IN AML_OBJECT_NODE_HANDLE PackageNode
+ );
+
// DEPRECATED APIS
#ifndef DISABLE_NEW_DEPRECATED_INTERFACES

diff --git a/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c
index e51d2dd7f0..80e43d0e62 100644
--- a/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c
+++ b/DynamicTablesPkg/Library/Common/AmlLib/CodeGen/AmlCodeGen.c
@@ -2600,3 +2600,239 @@ error_handler:

return Status;
}
+
+/** AML code generation for a _DSD device data object.
+
+ AmlAddDeviceDataDescriptorPackage (Uuid, ParentNode, NewObjectNode) is
+ equivalent of the following ASL code:
+ ToUUID(Uuid),
+ Package () {}
+
+ @ingroup CodeGenApis
+
+ @param [in] Uuid The Uuid of the descriptor to be created
+ @param [in] DsdNode Node of the DSD Package.
+ @param [out] PackageNode If success, contains the created package node.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddDeviceDataDescriptorPackage (
+ IN CONST EFI_GUID *Uuid,
+ IN AML_OBJECT_NODE_HANDLE DsdNode,
+ OUT AML_OBJECT_NODE_HANDLE *PackageNode
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE *UuidNode;
+ AML_DATA_NODE *UuidDataNode;
+ AML_OBJECT_NODE_HANDLE DsdEntryList;
+
+ if ((Uuid == NULL) ||
+ (PackageNode == NULL) ||
+ (AmlGetNodeType ((AML_NODE_HANDLE)DsdNode) != EAmlNodeObject) ||
+ (!AmlNodeHasOpCode (DsdNode, AML_NAME_OP, 0)) ||
+ !AmlNameOpCompareName (DsdNode, "_DSD"))
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Get the Package object node of the _DSD node,
+ // which is the 2nd fixed argument (i.e. index 1).
+ DsdEntryList = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument (
+ DsdNode,
+ EAmlParseIndexTerm1
+ );
+ if ((DsdEntryList == NULL) ||
+ (AmlGetNodeType ((AML_NODE_HANDLE)DsdEntryList) != EAmlNodeObject) ||
+ (!AmlNodeHasOpCode (DsdEntryList, AML_PACKAGE_OP, 0)))
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *PackageNode = NULL;
+ UuidNode = NULL;
+
+ Status = AmlCodeGenBuffer (NULL, 0, &UuidNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ Status = AmlCreateDataNode (
+ EAmlNodeDataTypeRaw,
+ (CONST UINT8 *)Uuid,
+ sizeof (EFI_GUID),
+ &UuidDataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ Status = AmlVarListAddTail (
+ (AML_NODE_HEADER *)UuidNode,
+ (AML_NODE_HEADER *)UuidDataNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+ UuidDataNode = NULL;
+
+ // Append to the the list of _DSD entries.
+ Status = AmlVarListAddTail (
+ (AML_NODE_HANDLE)DsdEntryList,
+ (AML_NODE_HANDLE)UuidNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ Status = AmlCodeGenPackage (PackageNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ // Append to the the list of _DSD entries.
+ Status = AmlVarListAddTail (
+ (AML_NODE_HANDLE)DsdEntryList,
+ (AML_NODE_HANDLE)*PackageNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ return Status;
+
+error_handler:
+ if (UuidNode != NULL) {
+ //Need to check to detach as there is an error path that have
+ //this attached to the DsdEntry
+ if (AML_NODE_HAS_PARENT (UuidNode)) {
+ AmlDetachNode ((AML_NODE_HANDLE)UuidNode);
+ }
+ AmlDeleteTree ((AML_NODE_HANDLE)UuidNode);
+ }
+
+ if (*PackageNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HANDLE)*PackageNode);
+ *PackageNode = NULL;
+ }
+ if (UuidDataNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HANDLE)UuidDataNode);
+ }
+
+ return Status;
+}
+
+/** AML code generation to add a package with a name and value,
+ * to a parent package
+
+ AmlAddNameValuePackage ("Name", Value, ParentNode) is
+ equivalent of the following ASL code:
+ Package (2) {"Name", Value}
+
+ @ingroup CodeGenApis
+
+ @param [in] Name String to place in first entry of package
+ @param [in] Value Integer to place in second entry of package
+ @param [in] PackageNode Package to add new sub package to.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlAddNameIntegerPackage (
+ IN CHAR8 *Name,
+ IN UINT64 Value,
+ IN AML_OBJECT_NODE_HANDLE PackageNode
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE *NameNode;
+ AML_OBJECT_NODE *ValueNode;
+ AML_OBJECT_NODE *NewPackageNode;
+
+ if ((Name == NULL) ||
+ (AmlGetNodeType ((AML_NODE_HANDLE)PackageNode) != EAmlNodeObject) ||
+ (!AmlNodeHasOpCode (PackageNode, AML_PACKAGE_OP, 0)))
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NewPackageNode = NULL;
+ // The new package entry.
+ Status = AmlCodeGenPackage (&NewPackageNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = AmlCodeGenString (Name, &NameNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (Status);
+ goto error_handler;
+ }
+
+ Status = AmlVarListAddTail (
+ (AML_NODE_HANDLE)NewPackageNode,
+ (AML_NODE_HANDLE)NameNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+ NameNode = NULL;
+
+ Status = AmlCodeGenInteger (Value, &ValueNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ Status = AmlVarListAddTail (
+ (AML_NODE_HANDLE)NewPackageNode,
+ (AML_NODE_HANDLE)ValueNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+ ValueNode = NULL;
+
+ Status = AmlVarListAddTail (
+ (AML_NODE_HANDLE)PackageNode,
+ (AML_NODE_HANDLE)NewPackageNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ return Status;
+
+error_handler:
+ if (NewPackageNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HANDLE)NewPackageNode);
+ }
+ if (NameNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HANDLE)NameNode);
+ }
+ if (ValueNode != NULL) {
+ AmlDeleteTree ((AML_NODE_HANDLE)ValueNode);
+ }
+
+ return Status;
+}
--
2.25.1


Re: [PATCH v1 0/5] Add support to build PRM for AARCH64 using GCC

Michael Kubacki
 

Hi Sami,

I see you created a PR that is marked as draft with these changes:
https://github.com/tianocore/edk2/pull/3167

When you're ready for them to be submitted, if you could please create a non-draft PR with the review tags added, I'd be happy to push it.

Thanks,
Michael

On 8/3/2022 10:35 AM, Sami Mujawar wrote:
This patch series adds support to build PRM for AARCH64 using GCC.
The changes can be seen at:
https://github.com/samimujawar/edk2/tree/2238_aarch64_prm_support_v1
Jose Marinho (5):
Basetools/GenFw: Allow AARCH64 builds to use the --prm flag
PrmPkg: Enable external visibility on PRM symbols
PrmPkg: Build Prm Samples with GCC for AARCH64
PrmPkg: Support AArch64 builds using GCC
PrmPkg: Add details on AArch64 build to the Readme.
BaseTools/Source/C/GenFw/Elf64Convert.c | 6 ++---
PrmPkg/Include/Prm.h | 3 +++
PrmPkg/PrmPkg.ci.yaml | 1 +
PrmPkg/PrmPkg.dsc | 24 +++++++++++++++++---
PrmPkg/Readme.md | 11 +++++++++
PrmPkg/Samples/PrmSampleAcpiParameterBufferModule/PrmSampleAcpiParameterBufferModule.inf | 4 ++++
PrmPkg/Samples/PrmSampleContextBufferModule/PrmSampleContextBufferModule.inf | 4 ++++
7 files changed, 47 insertions(+), 6 deletions(-)


[PATCH] MdeModulePkg XhciPei: Fix dead loop issue in UsbHcFreeMemPool()

Zeng, Star
 

Use Block->Next instead of Pool->Head->Next, otherwise the for loop
will be not able to come out.
It will also match with the UsbHcFreeMemPool() in EhciPei.

Cc: Hao A Wu <hao.a.wu@...>
Cc: Ray Ni <ray.ni@...>
Cc: Zhikai Sun <zhikai.sun@...>
Signed-off-by: Star Zeng <star.zeng@...>
---
MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c b/MdeModulePkg/Bus/Pci=
/XhciPei/UsbHcMem.c
index c64b38fcfc89..148425ae844e 100644
--- a/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c
+++ b/MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c
@@ -385,7 +385,7 @@ UsbHcFreeMemPool (
// UsbHcUnlinkMemBlock can't be used to unlink and free the=0D
// first block.=0D
//=0D
- for (Block =3D Pool->Head->Next; Block !=3D NULL; Block =3D Pool->Head->=
Next) {=0D
+ for (Block =3D Pool->Head->Next; Block !=3D NULL; Block =3D Block->Next)=
{=0D
// UsbHcUnlinkMemBlock (Pool->Head, Block);=0D
UsbHcFreeMemBlock (Pool, Block);=0D
}=0D
--=20
2.33.1.windows.1

2481 - 2500 of 94574