From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by mx.groups.io with SMTP id smtpd.web12.3579.1660200849685231102 for ; Wed, 10 Aug 2022 23:54:13 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="unable to parse pub key" header.i=@intel.com header.s=intel header.b=BESScDJh; spf=pass (domain: intel.com, ip: 192.55.52.43, mailfrom: judah.vang@intel.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1660200853; x=1691736853; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=1FrTSsdYdi9gMS/lTjmziKShxLpLTUcq6peOocGOQdw=; b=BESScDJhpowJ6IttQSkPwd0a3WTeDUQb/evvM66GeBgG3leFxRs1a/Rp 0CdbBY9J1Qn+R1ZqA54rgsMa88sNWqQfq35BIwJrVr5XIR2TbRHEcqSni aiv2/fzo3vUTifN8jKrxSFJ7ADxikhKdTvQYxo2x1ChIp8cWH2XjFe9m7 4om9+PAKuyBCBCZ3nDut9bmMHKYWcB8uhXDkWPpCeCZi+9sA9hdmE2zmf MVGIdSG45IO65RvwgqH9VzpRWemizPXI5bNKIodlRWmtEnQUAAaHRvv5N tEUoBYS+xm1nRRZPn5R2Ox4MxJjBHAXs1QGIQ1uIz09594ZY1dea/3+90 A==; X-IronPort-AV: E=McAfee;i="6400,9594,10435"; a="377565777" X-IronPort-AV: E=Sophos;i="5.93,228,1654585200"; d="scan'208";a="377565777" Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 Aug 2022 23:54:12 -0700 X-IronPort-AV: E=Sophos;i="5.93,228,1654585200"; d="scan'208";a="673586326" Received: from moisespe-mobl.amr.corp.intel.com (HELO jvang-mobl.amr.corp.intel.com) ([10.212.171.64]) by fmsmga004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 Aug 2022 23:54:11 -0700 From: "Judah Vang" To: devel@edk2.groups.io Cc: Jian J Wang , Liming Gao , Hao A Wu , Nishant C Mistry Subject: [PATCH v4 08/28] MdeModulePkg: Add support for Protected Variables Date: Wed, 10 Aug 2022 23:53:17 -0700 Message-Id: <20220811065337.2068-9-judah.vang@intel.com> X-Mailer: git-send-email 2.35.1.windows.2 In-Reply-To: <20220811065337.2068-1-judah.vang@intel.com> References: <20220811065337.2068-1-judah.vang@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594 v4: Applied code review - remove unreferenced library from .inf. Updated some function description and parameters. V3: Fix 'NextVariableStore' parameter for CopyMem. It was causing an exception. Need to correctly cast 'NextVariableStore' so all platforms build. Add code to initialize 'ContextIn' structure in SmmVariableReay() to fix issue with NULL function pointer. V1: Add support for Protected Variables. Add new API to retrieve Variable Infomation and data. Add new API to update variable in non-volatile storage or cached copy. Cc: Jian J Wang Cc: Liming Gao Cc: Hao A Wu Cc: Nishant C Mistry Signed-off-by: Jian J Wang Signed-off-by: Nishant C Mistry Signed-off-by: Judah Vang --- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf | 3 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf | 3 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf | 3 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf | 3 +- MdeModulePkg/Universal/Variable/Pei/VariableParsing.h | 41 +- MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h | 127 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h | 81 +- MdeModulePkg/Universal/Variable/Pei/VariableParsing.c | 8 +- MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c | 349 +++- MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c | 2142 +++++++++++--------- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c | 26 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c | 167 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c | 194 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c | 314 ++- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c | 39 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c | 66 +- 16 files changed, 2484 insertions(+), 1082 deletions(-) diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf index 3858adf6739d..b7a3d3942ba7 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf @@ -9,7 +9,7 @@ # This external input must be validated carefully to avoid security issues such as # buffer overflow or integer overflow. # -# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.
# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -75,6 +75,7 @@ [LibraryClasses] VariablePolicyLib VariablePolicyHelperLib SafeIntLib + ProtectedVariableLib [Protocols] gEfiFirmwareVolumeBlockProtocolGuid ## CONSUMES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf index 8c552b87e080..4ba467a73255 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf @@ -18,7 +18,7 @@ # may not be modified without authorization. If platform fails to protect these resources, # the authentication service provided in this driver will be broken, and the behavior is undefined. # -# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.
# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -84,6 +84,7 @@ [LibraryClasses] VariablePolicyLib VariablePolicyHelperLib SafeIntLib + ProtectedVariableLib [Protocols] gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf index a0d8b2267e92..3b773a68dcc4 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf @@ -13,7 +13,7 @@ # may not be modified without authorization. If platform fails to protect these resources, # the authentication service provided in this driver will be broken, and the behavior is undefined. # -# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -61,6 +61,7 @@ [LibraryClasses] SafeIntLib PcdLib MmUnblockMemoryLib + ProtectedVariableLib [Protocols] gEfiVariableWriteArchProtocolGuid ## PRODUCES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf index f09bed40cf51..956a872cda09 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf @@ -18,7 +18,7 @@ # may not be modified without authorization. If platform fails to protect these resources, # the authentication service provided in this driver will be broken, and the behavior is undefined. # -# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.
# Copyright (c) 2018, Linaro, Ltd. All rights reserved.
# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent @@ -80,6 +80,7 @@ [LibraryClasses] VariableFlashInfoLib VariablePolicyLib VariablePolicyHelperLib + ProtectedVariableLib [Protocols] gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES diff --git a/MdeModulePkg/Universal/Variable/Pei/VariableParsing.h b/MdeModulePkg/Universal/Variable/Pei/VariableParsing.h index d7af6cb6e8be..2503b72461ab 100644 --- a/MdeModulePkg/Universal/Variable/Pei/VariableParsing.h +++ b/MdeModulePkg/Universal/Variable/Pei/VariableParsing.h @@ -227,13 +227,7 @@ CompareWithValidVariable ( Retrieve details of the variable next to given variable within VariableStore. - If VarInfo->Address is NULL, the first one in VariableStore is returned. - - VariableStart and/or VariableEnd can be given optionally for the situation - in which the valid storage space is smaller than the VariableStore->Size. - This usually happens when PEI variable services make a compact variable - cache to save memory, which cannot make use VariableStore->Size to determine - the correct variable storage range. + If VariableInfo->StoreIndex is invalid, the first one in VariableStore is returned. @param[in,out] VariableInfo Pointer to variable information. @@ -250,28 +244,29 @@ GetNextVariableInfo ( /** - Retrieve details about a variable and return them in VariableInfo->Header. + Retrieve details about a variable, given by VariableInfo->Buffer or + VariableInfo->Index, and pass the details back in VariableInfo->Header. - If VariableInfo->Address is given, this function will calculate its offset - relative to given variable storage via VariableStore; Otherwise, it will try - other internal variable storages or cached copies. It's assumed that, for all - copies of NV variable storage, all variables are stored in the same relative - position. If VariableInfo->Address is found in the range of any storage copies, - its offset relative to that storage should be the same in other copies. + This function is used to resolve the variable data structure into + VariableInfo->Header, for easier access later without revisiting the variable + data in variable store. If pointers in the structure of VariableInfo->Header + are not NULL, it's supposed that they are buffers passed in to hold a copy of + data of corresponding data fields in variable data structure. Otherwise, this + function simply returns pointers pointing to address of those data fields. - If VariableInfo->Offset is given (non-zero) but not VariableInfo->Address, - this function will return the variable memory address inside VariableStore, - if given, via VariableInfo->Address; Otherwise, the address of other storage - copies will be returned, if any. + The variable is specified by either VariableInfo->Index or VariableInfo->Buffer. + If VariableInfo->Index is given, this function finds the corresponding variable + first from variable storage according to the Index. - For a new variable whose offset has not been determined, a value of -1 as - VariableInfo->Offset should be passed to skip the offset calculation. + If both VariableInfo->Index and VariableInfo->Buffer are given, it's supposed + that VariableInfo->Buffer is a buffer passed in to hold a whole copy of + requested variable data to be returned. @param[in,out] VariableInfo Pointer to variable information. - @retval EFI_INVALID_PARAMETER VariableInfo is NULL or both VariableInfo->Address - and VariableInfo->Offset are NULL (0). - @retval EFI_NOT_FOUND If given Address or Offset is out of range of + @retval EFI_INVALID_PARAMETER VariableInfo is NULL or both VariableInfo->Buffer + and VariableInfo->Index are NULL (0). + @retval EFI_NOT_FOUND If given Buffer or Index is out of range of any given or internal storage copies. @retval EFI_SUCCESS Variable details are retrieved successfully. diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h index a668abb82b15..a2065f4bc6e0 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h @@ -2,7 +2,7 @@ The internal header file includes the common header files, defines internal structure and functions used by Variable modules. -Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -33,6 +33,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include #include #include @@ -820,4 +821,128 @@ VariableExLibAtRuntime ( VOID ); +/** + Is user variable? + + @param[in] Variable Pointer to variable header. + + @retval TRUE User variable. + @retval FALSE System variable. + +**/ +BOOLEAN +IsUserVariable ( + IN VARIABLE_HEADER *Variable + ); + +/** + + Variable store garbage collection and reclaim operation. + + @param[in] VariableBase Base address of variable store. + @param[out] LastVariableOffset Offset of last variable. + @param[in] IsVolatile The variable store is volatile or not; + if it is non-volatile, need FTW. + @param[in, out] UpdatingPtrTrack Pointer to updating variable pointer track structure. + @param[in] NewVariable Pointer to new variable. + @param[in] NewVariableSize New variable size. + + @return EFI_SUCCESS Reclaim operation has finished successfully. + @return EFI_OUT_OF_RESOURCES No enough memory resources or variable space. + @return Others Unexpect error happened during reclaim operation. + +**/ +EFI_STATUS +Reclaim ( + IN EFI_PHYSICAL_ADDRESS VariableBase, + OUT UINTN *LastVariableOffset, + IN BOOLEAN IsVolatile, + IN OUT VARIABLE_POINTER_TRACK *UpdatingPtrTrack, + IN VARIABLE_HEADER *NewVariable, + IN UINTN NewVariableSize + ); + +/** + + This function writes data to the FWH at the correct LBA even if the LBAs + are fragmented. + + @param Global Pointer to VARIABLE_GLOBAL structure. + @param Volatile Point out the Variable is Volatile or Non-Volatile. + @param SetByIndex TRUE if target pointer is given as index. + FALSE if target pointer is absolute. + @param Fvb Pointer to the writable FVB protocol. + @param DataPtrIndex Pointer to the Data from the end of VARIABLE_STORE_HEADER + structure. + @param DataSize Size of data to be written. + @param Buffer Pointer to the buffer from which data is written. + + @retval EFI_INVALID_PARAMETER Parameters not valid. + @retval EFI_UNSUPPORTED Fvb is a NULL for Non-Volatile variable update. + @retval EFI_OUT_OF_RESOURCES The remaining size is not enough. + @retval EFI_SUCCESS Variable store successfully updated. + +**/ +EFI_STATUS +UpdateVariableStore ( + IN VARIABLE_GLOBAL *Global, + IN BOOLEAN Volatile, + IN BOOLEAN SetByIndex, + IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb, + IN UINTN DataPtrIndex, + IN UINT32 DataSize, + IN UINT8 *Buffer + ); + +/** + Update partial data of a variable on NV storage and/or cached copy. + + @param[in] VariableInfo Pointer to a variable with detailed information. + @param[in] Offset Offset to write from. + @param[in] Size Size of data Buffer to update. + @param[in] Buffer Pointer to data buffer to update. + + @retval EFI_SUCCESS The variable data was updated successfully. + @retval EFI_UNSUPPORTED If this function is called directly in runtime. + @retval EFI_INVALID_PARAMETER If VariableInfo, Buffer or Size are not valid. + @retval Others Failed to update NV storage or variable cache. + +**/ +EFI_STATUS +EFIAPI +VariableExLibUpdateNvVariable ( + IN PROTECTED_VARIABLE_INFO *VariableInfo, + IN UINTN Offset, + IN UINT32 Size, + IN UINT8 *Buffer + ); + +/** + Finds the given variable in a variable store in SMM. + + Caution: This function may receive untrusted input. + The data size is external input, so this function will validate it carefully to avoid buffer overflow. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + @param[out] Attributes Attribute value of the variable found. + @param[in, out] DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param[out] Data Data pointer. + + @retval EFI_SUCCESS Found the specified variable. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND The specified variable could not be found. + +**/ +EFI_STATUS +EFIAPI +FindVariableInSmm ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data OPTIONAL + ); + #endif diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h index 951e8a089e34..f2ae42ad011e 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h @@ -2,7 +2,7 @@ Functions in this module are associated with variable parsing operations and are intended to be usable across variable driver source files. -Copyright (c) 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -19,6 +19,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent @param[in] Variable Pointer to the Variable Header. @param[in] VariableStoreEnd Pointer to the Variable Store End. + @param[in] AuthFormat Auth-variable indicator. @retval TRUE Variable header is valid. @retval FALSE Variable header is not valid. @@ -27,7 +28,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent BOOLEAN IsValidVariableHeader ( IN VARIABLE_HEADER *Variable, - IN VARIABLE_HEADER *VariableStoreEnd + IN VARIABLE_HEADER *VariableStoreEnd, + IN BOOLEAN AuthFormat ); /** @@ -192,6 +194,28 @@ GetVariableDataOffset ( IN BOOLEAN AuthFormat ); +/** + Get variable data payload. + + @param[in] Variable Pointer to the Variable Header. + @param[out] Data Pointer to buffer used to store the variable data. + @param[in] DataSize Size of buffer passed by Data. + @param[out] DataSize Size of data copied into Data buffer. + @param[in] AuthFlag Auth-variable indicator. + + @return EFI_SUCCESS Data was fetched. + @return EFI_INVALID_PARAMETER DataSize is NULL. + @return EFI_BUFFER_TOO_SMALL DataSize is smaller than size of variable data. + +**/ +EFI_STATUS +GetVariableData ( + IN VARIABLE_HEADER *Variable, + IN OUT VOID *Data, + IN OUT UINT32 *DataSize, + IN BOOLEAN AuthFlag + ); + /** This code gets the pointer to the next variable header. @@ -344,4 +368,57 @@ UpdateVariableInfo ( IN OUT VARIABLE_INFO_ENTRY **VariableInfo ); +/** + + Retrieve details of the variable next to given variable within VariableStore. + + If VariableInfo->StoreIndex is invalid, the first one in VariableStore is returned. + + @param[in,out] VariableInfo Pointer to variable information. + + @retval EFI_INVALID_PARAMETER VariableInfo or VariableStore is NULL. + @retval EFI_NOT_FOUND If the end of VariableStore is reached. + @retval EFI_SUCCESS The next variable is retrieved successfully. + +**/ +EFI_STATUS +EFIAPI +GetNextVariableInfo ( + IN OUT PROTECTED_VARIABLE_INFO *VariableInfo + ); + +/** + + Retrieve details about a variable and return them in VariableInfo->Header. + + If VariableInfo->Buffer is given, this function will calculate its offset + relative to given variable storage via VariableStore; Otherwise, it will try + other internal variable storages or cached copies. It's assumed that, for all + copies of NV variable storage, all variables are stored in the same relative + position. If VariableInfo->Buffer is found in the range of any storage copies, + its offset relative to that storage should be the same in other copies. + + If VariableInfo->Offset is given (non-zero) but not VariableInfo->Buffer, + this function will return the variable memory address inside VariableStore, + if given, via VariableInfo->Address; Otherwise, the address of other storage + copies will be returned, if any. + + For a new variable whose offset has not been determined, a value of -1 as + VariableInfo->Offset should be passed to skip the offset calculation. + + @param VariableInfo Pointer to variable information. + + @retval EFI_INVALID_PARAMETER VariableInfo is NULL or both VariableInfo->Address + and VariableInfo->Offset are NULL (0). + @retval EFI_NOT_FOUND If given Address or Offset is out of range of + any given or internal storage copies. + @retval EFI_SUCCESS Variable details are retrieved successfully. + +**/ +EFI_STATUS +EFIAPI +GetVariableInfo ( + IN OUT PROTECTED_VARIABLE_INFO *VariableInfo + ); + #endif diff --git a/MdeModulePkg/Universal/Variable/Pei/VariableParsing.c b/MdeModulePkg/Universal/Variable/Pei/VariableParsing.c index 2d605d39cbb6..1f164713b041 100644 --- a/MdeModulePkg/Universal/Variable/Pei/VariableParsing.c +++ b/MdeModulePkg/Universal/Variable/Pei/VariableParsing.c @@ -834,13 +834,7 @@ GetVariableInfo ( Retrieve details of the variable next to given variable within VariableStore. - If VarInfo->Buffer is NULL, the first one in VariableStore is returned. - - VariableStart and/or VariableEnd can be given optionally for the situation - in which the valid storage space is smaller than the VariableStore->Size. - This usually happens when PEI variable services make a compact variable - cache to save memory, which cannot make use VariableStore->Size to determine - the correct variable storage range. + If VariableInfo->StoreIndex is invalid, the first one in VariableStore is returned. @param[in,out] VariableInfo Pointer to variable information. diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c index fe64d0a2b3dd..a5b7f8a1fbe2 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c @@ -2,12 +2,15 @@ Handles non-volatile variable store garbage collection, using FTW (Fault Tolerant Write) protocol. -Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Variable.h" +#include "VariableNonVolatile.h" +#include "VariableParsing.h" +#include "VariableRuntimeCache.h" /** Gets LBA of block and offset by given address. @@ -155,3 +158,347 @@ FtwVariableSpace ( return Status; } + +/** + + Variable store garbage collection and reclaim operation. + + @param[in] VariableBase Base address of variable store. + @param[out] LastVariableOffset Offset of last variable. + @param[in] IsVolatile The variable store is volatile or not; + if it is non-volatile, need FTW. + @param[in, out] UpdatingPtrTrack Pointer to updating variable pointer track structure. + @param[in] NewVariable Pointer to new variable. + @param[in] NewVariableSize New variable size. + + @return EFI_SUCCESS Reclaim operation has finished successfully. + @return EFI_OUT_OF_RESOURCES No enough memory resources or variable space. + @return Others Unexpect error happened during reclaim operation. + +**/ +EFI_STATUS +Reclaim ( + IN EFI_PHYSICAL_ADDRESS VariableBase, + OUT UINTN *LastVariableOffset, + IN BOOLEAN IsVolatile, + IN OUT VARIABLE_POINTER_TRACK *UpdatingPtrTrack, + IN VARIABLE_HEADER *NewVariable, + IN UINTN NewVariableSize + ) +{ + VARIABLE_HEADER *Variable; + VARIABLE_HEADER *AddedVariable; + VARIABLE_HEADER *NextVariable; + VARIABLE_HEADER *NextAddedVariable; + VARIABLE_STORE_HEADER *VariableStoreHeader; + UINT8 *ValidBuffer; + UINTN MaximumBufferSize; + UINTN VariableSize; + UINTN NameSize; + UINT8 *CurrPtr; + VOID *Point0; + VOID *Point1; + BOOLEAN FoundAdded; + EFI_STATUS Status; + EFI_STATUS DoneStatus; + UINTN CommonVariableTotalSize; + UINTN CommonUserVariableTotalSize; + UINTN HwErrVariableTotalSize; + VARIABLE_HEADER *UpdatingVariable; + VARIABLE_HEADER *UpdatingInDeletedTransition; + BOOLEAN AuthFormat; + + AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat; + UpdatingVariable = NULL; + UpdatingInDeletedTransition = NULL; + if (UpdatingPtrTrack != NULL) { + UpdatingVariable = UpdatingPtrTrack->CurrPtr; + UpdatingInDeletedTransition = UpdatingPtrTrack->InDeletedTransitionPtr; + } + + VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)VariableBase); + + CommonVariableTotalSize = 0; + CommonUserVariableTotalSize = 0; + HwErrVariableTotalSize = 0; + + if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) { + // + // Start Pointers for the variable. + // + Variable = GetStartPointer (VariableStoreHeader); + MaximumBufferSize = sizeof (VARIABLE_STORE_HEADER); + + while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat)) { + NextVariable = GetNextVariablePtr (Variable, AuthFormat); + if (((Variable->State == VAR_ADDED) || (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED))) && + (Variable != UpdatingVariable) && + (Variable != UpdatingInDeletedTransition) + ) + { + VariableSize = (UINTN)NextVariable - (UINTN)Variable; + MaximumBufferSize += VariableSize; + } + + Variable = NextVariable; + } + + if (NewVariable != NULL) { + // + // Add the new variable size. + // + MaximumBufferSize += NewVariableSize; + } + + // + // Reserve the 1 Bytes with Oxff to identify the + // end of the variable buffer. + // + MaximumBufferSize += 1; + ValidBuffer = AllocatePool (MaximumBufferSize); + if (ValidBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + // + // For NV variable reclaim, don't allocate pool here and just use mNvVariableCache + // as the buffer to reduce SMRAM consumption for SMM variable driver. + // + MaximumBufferSize = mNvVariableCache->Size; + ValidBuffer = (UINT8 *)mNvVariableCache; + } + + SetMem (ValidBuffer, MaximumBufferSize, 0xff); + + // + // Copy variable store header. + // + CopyMem (ValidBuffer, VariableStoreHeader, sizeof (VARIABLE_STORE_HEADER)); + CurrPtr = (UINT8 *)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer); + + // + // Reinstall all ADDED variables as long as they are not identical to Updating Variable. + // + Variable = GetStartPointer (VariableStoreHeader); + while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat)) { + NextVariable = GetNextVariablePtr (Variable, AuthFormat); + if ((Variable != UpdatingVariable) && (Variable->State == VAR_ADDED)) { + VariableSize = (UINTN)NextVariable - (UINTN)Variable; + CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize); + if (!IsVolatile) { + (VOID)ProtectedVariableLibRefresh ( + (VARIABLE_HEADER *)CurrPtr, + VariableSize, + (UINTN)CurrPtr - (UINTN)ValidBuffer, + FALSE + ); + + if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) + == EFI_VARIABLE_HARDWARE_ERROR_RECORD) + { + HwErrVariableTotalSize += VariableSize; + } else { + CommonVariableTotalSize += VariableSize; + if (IsUserVariable (Variable)) { + CommonUserVariableTotalSize += VariableSize; + } + } + } + + CurrPtr += VariableSize; + } + + Variable = NextVariable; + } + + // + // Reinstall all in delete transition variables. + // + Variable = GetStartPointer (VariableStoreHeader); + while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat)) { + NextVariable = GetNextVariablePtr (Variable, AuthFormat); + if ((Variable != UpdatingVariable) && (Variable != UpdatingInDeletedTransition) && (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) && + (ProtectedVariableLibIsHmac (GetVariableNamePtr (Variable, AuthFormat)) == FALSE)) + { + FoundAdded = FALSE; + AddedVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer); + while (IsValidVariableHeader (AddedVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)ValidBuffer), AuthFormat)) { + NextAddedVariable = GetNextVariablePtr (AddedVariable, AuthFormat); + NameSize = NameSizeOfVariable (AddedVariable, AuthFormat); + if (CompareGuid ( + GetVendorGuidPtr (AddedVariable, AuthFormat), + GetVendorGuidPtr (Variable, AuthFormat) + ) && (NameSize == NameSizeOfVariable (Variable, AuthFormat))) + { + Point0 = (VOID *)GetVariableNamePtr (AddedVariable, AuthFormat); + Point1 = (VOID *)GetVariableNamePtr (Variable, AuthFormat); + if (CompareMem (Point0, Point1, NameSize) == 0) { + FoundAdded = TRUE; + break; + } + } + + AddedVariable = NextAddedVariable; + } + + if (!FoundAdded) { + // + // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED. + // + VariableSize = (UINTN)NextVariable - (UINTN)Variable; + CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize); + ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED; + if (!IsVolatile) { + (VOID)ProtectedVariableLibRefresh ( + (VARIABLE_HEADER *)CurrPtr, + VariableSize, + (UINTN)CurrPtr - (UINTN)ValidBuffer, + FALSE + ); + + if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) + == EFI_VARIABLE_HARDWARE_ERROR_RECORD) + { + HwErrVariableTotalSize += VariableSize; + } else { + CommonVariableTotalSize += VariableSize; + if (IsUserVariable (Variable)) { + CommonUserVariableTotalSize += VariableSize; + } + } + } + + CurrPtr += VariableSize; + } + } + + Variable = NextVariable; + } + + // + // Install the new variable if it is not NULL. + // + if (NewVariable != NULL) { + if (((UINTN)CurrPtr - (UINTN)ValidBuffer) + NewVariableSize > VariableStoreHeader->Size) { + // + // No enough space to store the new variable. + // + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + if (!IsVolatile) { + if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + HwErrVariableTotalSize += NewVariableSize; + } else if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + CommonVariableTotalSize += NewVariableSize; + if (IsUserVariable (NewVariable)) { + CommonUserVariableTotalSize += NewVariableSize; + } + } + + if ((HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize)) || + (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace) || + (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace)) + { + // + // No enough space to store the new variable by NV or NV+HR attribute. + // + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + } + + CopyMem (CurrPtr, (UINT8 *)NewVariable, NewVariableSize); + ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED; + if (UpdatingVariable != NULL) { + UpdatingPtrTrack->CurrPtr = (VARIABLE_HEADER *)((UINTN)UpdatingPtrTrack->StartPtr + ((UINTN)CurrPtr - (UINTN)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer))); + UpdatingPtrTrack->InDeletedTransitionPtr = NULL; + } + + CurrPtr += NewVariableSize; + } + + if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) { + // + // If volatile/emulated non-volatile variable store, just copy valid buffer. + // + SetMem ((UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size, 0xff); + CopyMem ((UINT8 *)(UINTN)VariableBase, ValidBuffer, (UINTN)CurrPtr - (UINTN)ValidBuffer); + *LastVariableOffset = (UINTN)CurrPtr - (UINTN)ValidBuffer; + if (!IsVolatile) { + // + // Emulated non-volatile variable mode. + // + mVariableModuleGlobal->HwErrVariableTotalSize = HwErrVariableTotalSize; + mVariableModuleGlobal->CommonVariableTotalSize = CommonVariableTotalSize; + mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize; + } + + Status = EFI_SUCCESS; + } else { + // + // If non-volatile variable store, perform FTW here. + // + Status = FtwVariableSpace ( + VariableBase, + (VARIABLE_STORE_HEADER *)ValidBuffer + ); + if (!EFI_ERROR (Status)) { + *LastVariableOffset = (UINTN)CurrPtr - (UINTN)ValidBuffer; + mVariableModuleGlobal->HwErrVariableTotalSize = HwErrVariableTotalSize; + mVariableModuleGlobal->CommonVariableTotalSize = CommonVariableTotalSize; + mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize; + } else { + mVariableModuleGlobal->HwErrVariableTotalSize = 0; + mVariableModuleGlobal->CommonVariableTotalSize = 0; + mVariableModuleGlobal->CommonUserVariableTotalSize = 0; + Variable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase); + while (IsValidVariableHeader (Variable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase), AuthFormat)) { + NextVariable = GetNextVariablePtr (Variable, AuthFormat); + VariableSize = (UINTN)NextVariable - (UINTN)Variable; + if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize; + } else if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + mVariableModuleGlobal->CommonVariableTotalSize += VariableSize; + if (IsUserVariable (Variable)) { + mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize; + } + } + + Variable = NextVariable; + } + + *LastVariableOffset = (UINTN)Variable - (UINTN)VariableBase; + } + } + +Done: + DoneStatus = EFI_SUCCESS; + if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) { + DoneStatus = SynchronizeRuntimeVariableCache ( + &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache, + 0, + VariableStoreHeader->Size + ); + ASSERT_EFI_ERROR (DoneStatus); + FreePool (ValidBuffer); + } else { + // + // For NV variable reclaim, we use mNvVariableCache as the buffer, so copy the data back. + // + CopyMem (mNvVariableCache, (UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size); + DoneStatus = SynchronizeRuntimeVariableCache ( + &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache, + 0, + VariableStoreHeader->Size + ); + ASSERT_EFI_ERROR (DoneStatus); + } + + if (!EFI_ERROR (Status) && EFI_ERROR (DoneStatus)) { + Status = DoneStatus; + } + + return Status; +} diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c index 6c1a3440ac8c..19b432b772d7 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c @@ -16,7 +16,7 @@ VariableServiceSetVariable() should also check authenticate data to avoid buffer overflow, integer overflow. It should also check attribute to avoid authentication bypass. -Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.
(C) Copyright 2015-2018 Hewlett Packard Enterprise Development LP
Copyright (c) Microsoft Corporation.
Copyright (c) 2022, ARM Limited. All rights reserved.
@@ -30,7 +30,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "VariableParsing.h" #include "VariableRuntimeCache.h" -VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; +VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal = NULL; /// /// Define a memory cache that improves the search performance for a variable. @@ -458,7 +458,7 @@ CalculateCommonUserVariableTotalSize ( // if (mEndOfDxe && (mVariableModuleGlobal->CommonMaxUserVariableSpace != mVariableModuleGlobal->CommonVariableSpace)) { Variable = GetStartPointer (mNvVariableCache); - while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) { + while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache), mVariableModuleGlobal->VariableGlobal.AuthFormat)) { NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat); VariableSize = (UINTN)NextVariable - (UINTN)Variable; if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { @@ -497,330 +497,6 @@ InitializeVariableQuota ( CalculateCommonUserVariableTotalSize (); } -/** - - Variable store garbage collection and reclaim operation. - - @param[in] VariableBase Base address of variable store. - @param[out] LastVariableOffset Offset of last variable. - @param[in] IsVolatile The variable store is volatile or not; - if it is non-volatile, need FTW. - @param[in, out] UpdatingPtrTrack Pointer to updating variable pointer track structure. - @param[in] NewVariable Pointer to new variable. - @param[in] NewVariableSize New variable size. - - @return EFI_SUCCESS Reclaim operation has finished successfully. - @return EFI_OUT_OF_RESOURCES No enough memory resources or variable space. - @return Others Unexpect error happened during reclaim operation. - -**/ -EFI_STATUS -Reclaim ( - IN EFI_PHYSICAL_ADDRESS VariableBase, - OUT UINTN *LastVariableOffset, - IN BOOLEAN IsVolatile, - IN OUT VARIABLE_POINTER_TRACK *UpdatingPtrTrack, - IN VARIABLE_HEADER *NewVariable, - IN UINTN NewVariableSize - ) -{ - VARIABLE_HEADER *Variable; - VARIABLE_HEADER *AddedVariable; - VARIABLE_HEADER *NextVariable; - VARIABLE_HEADER *NextAddedVariable; - VARIABLE_STORE_HEADER *VariableStoreHeader; - UINT8 *ValidBuffer; - UINTN MaximumBufferSize; - UINTN VariableSize; - UINTN NameSize; - UINT8 *CurrPtr; - VOID *Point0; - VOID *Point1; - BOOLEAN FoundAdded; - EFI_STATUS Status; - EFI_STATUS DoneStatus; - UINTN CommonVariableTotalSize; - UINTN CommonUserVariableTotalSize; - UINTN HwErrVariableTotalSize; - VARIABLE_HEADER *UpdatingVariable; - VARIABLE_HEADER *UpdatingInDeletedTransition; - BOOLEAN AuthFormat; - - AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat; - UpdatingVariable = NULL; - UpdatingInDeletedTransition = NULL; - if (UpdatingPtrTrack != NULL) { - UpdatingVariable = UpdatingPtrTrack->CurrPtr; - UpdatingInDeletedTransition = UpdatingPtrTrack->InDeletedTransitionPtr; - } - - VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)VariableBase); - - CommonVariableTotalSize = 0; - CommonUserVariableTotalSize = 0; - HwErrVariableTotalSize = 0; - - if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) { - // - // Start Pointers for the variable. - // - Variable = GetStartPointer (VariableStoreHeader); - MaximumBufferSize = sizeof (VARIABLE_STORE_HEADER); - - while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { - NextVariable = GetNextVariablePtr (Variable, AuthFormat); - if (((Variable->State == VAR_ADDED) || (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED))) && - (Variable != UpdatingVariable) && - (Variable != UpdatingInDeletedTransition) - ) - { - VariableSize = (UINTN)NextVariable - (UINTN)Variable; - MaximumBufferSize += VariableSize; - } - - Variable = NextVariable; - } - - if (NewVariable != NULL) { - // - // Add the new variable size. - // - MaximumBufferSize += NewVariableSize; - } - - // - // Reserve the 1 Bytes with Oxff to identify the - // end of the variable buffer. - // - MaximumBufferSize += 1; - ValidBuffer = AllocatePool (MaximumBufferSize); - if (ValidBuffer == NULL) { - return EFI_OUT_OF_RESOURCES; - } - } else { - // - // For NV variable reclaim, don't allocate pool here and just use mNvVariableCache - // as the buffer to reduce SMRAM consumption for SMM variable driver. - // - MaximumBufferSize = mNvVariableCache->Size; - ValidBuffer = (UINT8 *)mNvVariableCache; - } - - SetMem (ValidBuffer, MaximumBufferSize, 0xff); - - // - // Copy variable store header. - // - CopyMem (ValidBuffer, VariableStoreHeader, sizeof (VARIABLE_STORE_HEADER)); - CurrPtr = (UINT8 *)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer); - - // - // Reinstall all ADDED variables as long as they are not identical to Updating Variable. - // - Variable = GetStartPointer (VariableStoreHeader); - while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { - NextVariable = GetNextVariablePtr (Variable, AuthFormat); - if ((Variable != UpdatingVariable) && (Variable->State == VAR_ADDED)) { - VariableSize = (UINTN)NextVariable - (UINTN)Variable; - CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize); - CurrPtr += VariableSize; - if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { - HwErrVariableTotalSize += VariableSize; - } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { - CommonVariableTotalSize += VariableSize; - if (IsUserVariable (Variable)) { - CommonUserVariableTotalSize += VariableSize; - } - } - } - - Variable = NextVariable; - } - - // - // Reinstall all in delete transition variables. - // - Variable = GetStartPointer (VariableStoreHeader); - while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { - NextVariable = GetNextVariablePtr (Variable, AuthFormat); - if ((Variable != UpdatingVariable) && (Variable != UpdatingInDeletedTransition) && (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED))) { - // - // Buffer has cached all ADDED variable. - // Per IN_DELETED variable, we have to guarantee that - // no ADDED one in previous buffer. - // - - FoundAdded = FALSE; - AddedVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer); - while (IsValidVariableHeader (AddedVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)ValidBuffer))) { - NextAddedVariable = GetNextVariablePtr (AddedVariable, AuthFormat); - NameSize = NameSizeOfVariable (AddedVariable, AuthFormat); - if (CompareGuid ( - GetVendorGuidPtr (AddedVariable, AuthFormat), - GetVendorGuidPtr (Variable, AuthFormat) - ) && (NameSize == NameSizeOfVariable (Variable, AuthFormat))) - { - Point0 = (VOID *)GetVariableNamePtr (AddedVariable, AuthFormat); - Point1 = (VOID *)GetVariableNamePtr (Variable, AuthFormat); - if (CompareMem (Point0, Point1, NameSize) == 0) { - FoundAdded = TRUE; - break; - } - } - - AddedVariable = NextAddedVariable; - } - - if (!FoundAdded) { - // - // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED. - // - VariableSize = (UINTN)NextVariable - (UINTN)Variable; - CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize); - ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED; - CurrPtr += VariableSize; - if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { - HwErrVariableTotalSize += VariableSize; - } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { - CommonVariableTotalSize += VariableSize; - if (IsUserVariable (Variable)) { - CommonUserVariableTotalSize += VariableSize; - } - } - } - } - - Variable = NextVariable; - } - - // - // Install the new variable if it is not NULL. - // - if (NewVariable != NULL) { - if (((UINTN)CurrPtr - (UINTN)ValidBuffer) + NewVariableSize > VariableStoreHeader->Size) { - // - // No enough space to store the new variable. - // - Status = EFI_OUT_OF_RESOURCES; - goto Done; - } - - if (!IsVolatile) { - if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { - HwErrVariableTotalSize += NewVariableSize; - } else if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { - CommonVariableTotalSize += NewVariableSize; - if (IsUserVariable (NewVariable)) { - CommonUserVariableTotalSize += NewVariableSize; - } - } - - if ((HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize)) || - (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace) || - (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace)) - { - // - // No enough space to store the new variable by NV or NV+HR attribute. - // - Status = EFI_OUT_OF_RESOURCES; - goto Done; - } - } - - CopyMem (CurrPtr, (UINT8 *)NewVariable, NewVariableSize); - ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED; - if (UpdatingVariable != NULL) { - UpdatingPtrTrack->CurrPtr = (VARIABLE_HEADER *)((UINTN)UpdatingPtrTrack->StartPtr + ((UINTN)CurrPtr - (UINTN)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer))); - UpdatingPtrTrack->InDeletedTransitionPtr = NULL; - } - - CurrPtr += NewVariableSize; - } - - if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) { - // - // If volatile/emulated non-volatile variable store, just copy valid buffer. - // - SetMem ((UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size, 0xff); - CopyMem ((UINT8 *)(UINTN)VariableBase, ValidBuffer, (UINTN)CurrPtr - (UINTN)ValidBuffer); - *LastVariableOffset = (UINTN)CurrPtr - (UINTN)ValidBuffer; - if (!IsVolatile) { - // - // Emulated non-volatile variable mode. - // - mVariableModuleGlobal->HwErrVariableTotalSize = HwErrVariableTotalSize; - mVariableModuleGlobal->CommonVariableTotalSize = CommonVariableTotalSize; - mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize; - } - - Status = EFI_SUCCESS; - } else { - // - // If non-volatile variable store, perform FTW here. - // - Status = FtwVariableSpace ( - VariableBase, - (VARIABLE_STORE_HEADER *)ValidBuffer - ); - if (!EFI_ERROR (Status)) { - *LastVariableOffset = (UINTN)CurrPtr - (UINTN)ValidBuffer; - mVariableModuleGlobal->HwErrVariableTotalSize = HwErrVariableTotalSize; - mVariableModuleGlobal->CommonVariableTotalSize = CommonVariableTotalSize; - mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize; - } else { - mVariableModuleGlobal->HwErrVariableTotalSize = 0; - mVariableModuleGlobal->CommonVariableTotalSize = 0; - mVariableModuleGlobal->CommonUserVariableTotalSize = 0; - Variable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase); - while (IsValidVariableHeader (Variable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase))) { - NextVariable = GetNextVariablePtr (Variable, AuthFormat); - VariableSize = (UINTN)NextVariable - (UINTN)Variable; - if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { - mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize; - } else if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { - mVariableModuleGlobal->CommonVariableTotalSize += VariableSize; - if (IsUserVariable (Variable)) { - mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize; - } - } - - Variable = NextVariable; - } - - *LastVariableOffset = (UINTN)Variable - (UINTN)VariableBase; - } - } - -Done: - DoneStatus = EFI_SUCCESS; - if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) { - DoneStatus = SynchronizeRuntimeVariableCache ( - &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache, - 0, - VariableStoreHeader->Size - ); - ASSERT_EFI_ERROR (DoneStatus); - FreePool (ValidBuffer); - } else { - // - // For NV variable reclaim, we use mNvVariableCache as the buffer, so copy the data back. - // - CopyMem (mNvVariableCache, (UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size); - DoneStatus = SynchronizeRuntimeVariableCache ( - &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache, - 0, - VariableStoreHeader->Size - ); - ASSERT_EFI_ERROR (DoneStatus); - } - - if (!EFI_ERROR (Status) && EFI_ERROR (DoneStatus)) { - Status = DoneStatus; - } - - return Status; -} - /** Finds variable in storage blocks of volatile and non-volatile storage areas. @@ -1657,9 +1333,665 @@ AutoUpdateLangVariable ( } /** - Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set, - index of associated public key is needed. + Check if there's enough free space in storage to write the new variable. + @param[in] NewVariable Pointer to buffer of new variable. + @param[in] VariableSize Size of new variable. + @param[in] VariableName Name of variable. + @param[in] VendorGuid Guid of variable. + @param[in] Attributes Attributes of the variable. + @param[in] VolatileFlag Volatile/non-volatile variable indicator. + + @retval EFI_SUCCESS Enough free space on variable storage. + @retval EFI_BUFFER_TOO_SMALL There's not enough continuous free space. + @retval EFI_OUT_OF_RESOURCES There's not enough free space in total. +**/ +EFI_STATUS +CheckVariableStoreSpace ( + IN VARIABLE_HEADER *NewVariable, + IN UINTN VariableSize, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN BOOLEAN VolatileFlag + ) +{ + BOOLEAN IsCommonVariable; + BOOLEAN IsCommonUserVariable; + UINTN CommonVariableTotalSize; + UINTN CommonUserVariableTotalSize; + UINTN HwErrVariableTotalSize; + VARIABLE_STORE_HEADER *VarStore; + + if ((NewVariable == NULL) || (VariableSize == 0)) { + return EFI_SUCCESS; + } + + if (VolatileFlag) { + VarStore = (VARIABLE_STORE_HEADER *)(UINTN) + mVariableModuleGlobal->VariableGlobal.VolatileVariableBase; + if ((UINT32)(VariableSize + mVariableModuleGlobal->VolatileLastVariableOffset) + > VarStore->Size) + { + return EFI_BUFFER_TOO_SMALL; + } + } else { + if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) { + IsCommonVariable = TRUE; + IsCommonUserVariable = IsUserVariable (NewVariable); + } else { + IsCommonVariable = FALSE; + IsCommonUserVariable = FALSE; + } + + CommonVariableTotalSize = mVariableModuleGlobal->CommonVariableTotalSize + VariableSize; + CommonUserVariableTotalSize = mVariableModuleGlobal->CommonUserVariableTotalSize + VariableSize; + HwErrVariableTotalSize = mVariableModuleGlobal->HwErrVariableTotalSize + VariableSize; + + if ( (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) && + (HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize))) + || (IsCommonVariable && (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace)) + || (IsCommonVariable && + AtRuntime () && + (CommonVariableTotalSize > mVariableModuleGlobal->CommonRuntimeVariableSpace)) + || (IsCommonUserVariable && + (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace))) + { + if (AtRuntime ()) { + if (IsCommonUserVariable && + ((VariableSize + mVariableModuleGlobal->CommonUserVariableTotalSize) + > mVariableModuleGlobal->CommonMaxUserVariableSpace)) + { + RecordVarErrorFlag ( + VAR_ERROR_FLAG_USER_ERROR, + VariableName, + VendorGuid, + Attributes, + VariableSize + ); + } + + if (IsCommonVariable && + ((VariableSize + mVariableModuleGlobal->CommonVariableTotalSize) + > mVariableModuleGlobal->CommonRuntimeVariableSpace)) + { + RecordVarErrorFlag ( + VAR_ERROR_FLAG_SYSTEM_ERROR, + VariableName, + VendorGuid, + Attributes, + VariableSize + ); + } + + return EFI_OUT_OF_RESOURCES; + } + + return EFI_BUFFER_TOO_SMALL; + } + } + + return EFI_SUCCESS; +} + +/** + Fill specific data of auth-variable in buffer. + + @param[in,out] NewVariable Pointer to buffer of new variable. + @param[in] OldVariable Pointer to buffer of old copy of the variable. + @param[in] Attributes Attributes of the variable. + @param[in] KeyIndex Index of associated public key. + @param[in] MonotonicCount Value of associated monotonic count. + @param[in] TimeStamp Value of associated TimeStamp. + +**/ +VOID +SetVariableAuthData ( + IN OUT AUTHENTICATED_VARIABLE_HEADER *NewVariable, + IN AUTHENTICATED_VARIABLE_HEADER *OldVariable, + IN UINT32 Attributes, + IN UINT32 KeyIndex, + IN UINT64 MonotonicCount, + IN EFI_TIME *TimeStamp + ) +{ + NewVariable->PubKeyIndex = KeyIndex; + NewVariable->MonotonicCount = MonotonicCount; + + if ((TimeStamp != NULL) && + ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0)) + { + // + // In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only + // when the new TimeStamp value is later than the current timestamp associated + // with the variable, we need associate the new timestamp with the updated value. + // + if (((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) && + (OldVariable != NULL) && + !VariableCompareTimeStampInternal (&OldVariable->TimeStamp, TimeStamp)) + { + TimeStamp = &OldVariable->TimeStamp; + } + + CopyMem (&NewVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME)); + } else { + ZeroMem (&NewVariable->TimeStamp, sizeof (EFI_TIME)); + } +} + +/** + Fill the variable data buffer according to variable format on storage. + + @param[in,out] NewVariable Pointer to buffer of new variable. + @param[in] OldVariable Pointer to buffer of old copy of the variable. + @param[in] VariableName Name of variable. + @param[in] VendorGuid Guid of variable. + @param[in] Data Variable data. + @param[in] DataSize Size of data. 0 means delete. + @param[in] Attributes Attributes of the variable. + @param[in] KeyIndex Index of associated public key. + @param[in] MonotonicCount Value of associated monotonic count. + @param[in] TimeStamp Value of associated TimeStamp. + + @retval Size of the new variable. + +**/ +UINTN +SetVariableData ( + IN OUT VARIABLE_HEADER *NewVariable, + IN VARIABLE_HEADER *OldVariable, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes, + IN UINT32 KeyIndex, + IN UINT64 MonotonicCount, + IN EFI_TIME *TimeStamp + ) +{ + EFI_STATUS Status; + BOOLEAN AuthFormat; + UINT8 *DataPtr; + UINTN NameSize; + UINTN OldDataSize; + + AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat; + + if (AuthFormat) { + SetVariableAuthData ( + (AUTHENTICATED_VARIABLE_HEADER *)NewVariable, + (AUTHENTICATED_VARIABLE_HEADER *)OldVariable, + Attributes, + KeyIndex, + MonotonicCount, + TimeStamp + ); + } + + NewVariable->StartId = VARIABLE_DATA; + NewVariable->State = VAR_ADDED; + NewVariable->Reserved = 0; + NewVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE); + + CopyMem ( + GetVendorGuidPtr (NewVariable, AuthFormat), + VendorGuid, + sizeof (EFI_GUID) + ); + + NameSize = StrSize (VariableName); + SetNameSizeOfVariable (NewVariable, NameSize, AuthFormat); + CopyMem ( + (UINT8 *)GetVariableNamePtr (NewVariable, AuthFormat), + VariableName, + NameSize + ); + + // + // Set data size first otherwise we can't get correct data pointer in the + // buffer of new variable. + // + SetDataSizeOfVariable (NewVariable, DataSize, AuthFormat); + DataPtr = GetVariableDataPtr (NewVariable, AuthFormat); + if (((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) && + (OldVariable != NULL) && + ((OldVariable->State == VAR_ADDED) || + (OldVariable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)))) + { + // + // Get old data, which might be encrypted. + // + OldDataSize = mVariableModuleGlobal->ScratchBufferSize + - ((UINTN)DataPtr - (UINTN)NewVariable); + Status = ProtectedVariableLibGetByBuffer ( + OldVariable, + DataPtr, + (UINT32 *)&OldDataSize, + AuthFormat + ); + if (Status == EFI_UNSUPPORTED) { + OldDataSize = DataSizeOfVariable (OldVariable, AuthFormat); + CopyMem (DataPtr, GetVariableDataPtr (OldVariable, AuthFormat), OldDataSize); + } else if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return 0; + } + + DataPtr += OldDataSize; + // + // Update data size. + // + SetDataSizeOfVariable (NewVariable, DataSize + OldDataSize, AuthFormat); + } + + CopyMem (DataPtr, Data, DataSize); + + // + // The actual size of the variable stored in storage should include padding. + // + return ((UINTN)GetNextVariablePtr (NewVariable, AuthFormat) - (UINTN)NewVariable); +} + +/** + Update state of given variable as well as its cached copy. + + @param[in,out] Variable Pointer to the buffer of the variable. + @param[in,out] CacheVariable Cache copy of the variable. + @param[in] NewState New state value. + @param[in] Volatile Volatile/non-volatile variable indicator. + + @retval EFI_SUCCESS Variable state was updated successfully. + @retval Others Failed to update the variable state. + +**/ +EFI_STATUS +UpdateVariableState ( + IN OUT VARIABLE_HEADER *Variable, + IN OUT VARIABLE_HEADER *CacheVariable, + IN UINT8 NewState, + IN BOOLEAN Volatile + ) +{ + EFI_STATUS Status; + + Status = UpdateVariableStore ( + &mVariableModuleGlobal->VariableGlobal, + Volatile, + FALSE, + mVariableModuleGlobal->FvbInstance, + (UINTN)&Variable->State, + sizeof (NewState), + &NewState + ); + if (!EFI_ERROR (Status) && (CacheVariable != NULL)) { + CacheVariable->State = NewState; + } + + return Status; +} + +/** + Flush variable data to variable storage. + + @param[in] VarStoreBase Base address of variable storage. + @param[in,out] Offset Offset to write the variable from. + Offset from where next variable can be written. + @param[in,out] NewVariable Pointer to the buffer of new variable. + @param[in] VariableSize Size of new variable. + @param[in] Volatile Volatile/non-volatile variable indicator. + @param[in] AuthFormat Auth-variable indicator. + + @retval EFI_SUCCESS Variable(s) were written successfully. + @retval Others Failed to write the variable data. + +**/ +EFI_STATUS +WriteVariable ( + IN EFI_PHYSICAL_ADDRESS VarStoreBase, + IN OUT UINTN *Offset, + IN OUT VARIABLE_HEADER **NewVariable, + IN UINT32 VariableSize, + IN BOOLEAN Volatile, + IN BOOLEAN AuthFormat + ) +{ + EFI_STATUS Status; + + struct { + UINTN Offset; + UINT8 *Buffer; + UINT32 Size; + UINT8 State; + } WriteSteps[4]; + UINTN Index; + UINTN Steps; + VARIABLE_HEADER *Variable; + + Variable = *NewVariable; + if (Volatile) { + // + // For non-volatile variable, one step only : + // + WriteSteps[0].Offset = *Offset; + WriteSteps[0].Buffer = (UINT8 *)Variable; + WriteSteps[0].Size = VariableSize; + + Steps = 1; + } else { + // + // Four steps for non-volatile variable: + // + // 1. Write variable header + // 2. Set variable state to header valid + // 3. Write variable name and data + // 4. Set variable state to valid + // + Variable->State = 0xff; + WriteSteps[0].Offset = *Offset; + WriteSteps[0].Buffer = (UINT8 *)Variable; + WriteSteps[0].Size = (UINT32)GetVariableHeaderSize (AuthFormat); + + WriteSteps[1].State = VAR_HEADER_VALID_ONLY; + WriteSteps[1].Offset = *Offset + OFFSET_OF (VARIABLE_HEADER, State); + WriteSteps[1].Buffer = &WriteSteps[1].State; + WriteSteps[1].Size = sizeof (Variable->State); + + WriteSteps[2].Offset = *Offset + GetVariableHeaderSize (AuthFormat); + WriteSteps[2].Buffer = (UINT8 *)Variable + GetVariableHeaderSize (AuthFormat); + WriteSteps[2].Size = VariableSize - (UINT32)GetVariableHeaderSize (AuthFormat); + + WriteSteps[3].State = VAR_ADDED; + WriteSteps[3].Offset = *Offset + OFFSET_OF (VARIABLE_HEADER, State); + WriteSteps[3].Buffer = &WriteSteps[3].State; + WriteSteps[3].Size = sizeof (Variable->State); + + Steps = ARRAY_SIZE (WriteSteps); + } + + for (Index = 0; Index < Steps; ++Index) { + Status = UpdateVariableStore ( + &mVariableModuleGlobal->VariableGlobal, + Volatile, + TRUE, + mVariableModuleGlobal->FvbInstance, + WriteSteps[Index].Offset, + WriteSteps[Index].Size, + WriteSteps[Index].Buffer + ); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + } + + Variable->State = VAR_ADDED; + if (!Volatile) { + CopyMem ((UINT8 *)mNvVariableCache + *Offset, Variable, VariableSize); + } + + *NewVariable = (VARIABLE_HEADER *)((UINTN)VarStoreBase + *Offset); + *Offset += HEADER_ALIGN (VariableSize); + + return EFI_SUCCESS; +} + +/** + Rebase the given variable pointer(s) to the equivalent one in given variable + storage via VarStore. + + @param[in] InVarTrackPtr Pointer to current variable in cache. + @param[out] OutVarTrackPtr Pointer to rebased variable against VarStore. + @param[in] VarStore Start of variable storage to rebase against. + @param[in] VariableName Name of variable. + @param[in] VendorGuid Guid of variable. + @param[in] ByOffset If TRUE, don't do variable search in VarStore. + + @retval EFI_SUCCESS Variable(s) were deleted successfully. + @retval EFI_INVALID_PARAMETER Invalid parameters passed. + @retval EFI_NOT_FOUND Given variable (VariableName & VendorGuid) was + not found in VarStore, if ByOffset is FALSE. + +**/ +EFI_STATUS +RebaseVariablePtr ( + IN VARIABLE_POINTER_TRACK *InVarTrackPtr, + OUT VARIABLE_POINTER_TRACK *OutVarTrackPtr, + IN VARIABLE_STORE_HEADER *VarStore, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN BOOLEAN ByOffset + ) +{ + EFI_STATUS Status; + BOOLEAN AuthFormat; + VARIABLE_HEADER *NewStart; + + if ((InVarTrackPtr == NULL) || (OutVarTrackPtr == NULL) || (VarStore == NULL)) { + ASSERT (InVarTrackPtr != NULL); + ASSERT (OutVarTrackPtr != NULL); + ASSERT (VarStore != NULL); + return EFI_INVALID_PARAMETER; + } + + AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat; + + if ( (InVarTrackPtr->CurrPtr == NULL) + || (InVarTrackPtr->StartPtr == GetStartPointer (VarStore))) + { + CopyMem (OutVarTrackPtr, InVarTrackPtr, sizeof (VARIABLE_POINTER_TRACK)); + return EFI_SUCCESS; + } + + NewStart = GetStartPointer (VarStore); + if (ByOffset) { + OutVarTrackPtr->CurrPtr = (VARIABLE_HEADER *) + ((UINTN)NewStart + ((UINTN)InVarTrackPtr->CurrPtr - + (UINTN)InVarTrackPtr->StartPtr)); + + if (InVarTrackPtr->InDeletedTransitionPtr != NULL) { + OutVarTrackPtr->InDeletedTransitionPtr = + (VARIABLE_HEADER *)((UINTN)NewStart + + ((UINTN)InVarTrackPtr->InDeletedTransitionPtr - + (UINTN)InVarTrackPtr->StartPtr)); + } else { + OutVarTrackPtr->InDeletedTransitionPtr = NULL; + } + + OutVarTrackPtr->StartPtr = NewStart; + OutVarTrackPtr->EndPtr = GetEndPointer (VarStore); + } else { + OutVarTrackPtr->StartPtr = NewStart; + OutVarTrackPtr->EndPtr = GetEndPointer (VarStore); + + Status = FindVariableEx (VariableName, VendorGuid, FALSE, OutVarTrackPtr, AuthFormat); + if ((OutVarTrackPtr->CurrPtr == NULL) || EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + } + + if ( (VarStore == mNvVariableCache) + || ((UINTN)VarStore == (UINTN)mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase)) + { + OutVarTrackPtr->Volatile = FALSE; + } + + return EFI_SUCCESS; +} + +/** + Check if the given variable is from HOB. + + @param[in] CacheVariable Pointer to current variable in cache. + + @retval TRUE The variable is from HOB. + @retval FALSE The variable is NOT from HOB. + +**/ +BOOLEAN +IsHobVariable ( + IN VARIABLE_POINTER_TRACK *CacheVariable + ) +{ + VARIABLE_STORE_HEADER *HobVarStore; + + HobVarStore = (VARIABLE_STORE_HEADER *)(UINTN) + mVariableModuleGlobal->VariableGlobal.HobVariableBase; + return (CacheVariable->CurrPtr != NULL && + HobVarStore != NULL && + CacheVariable->StartPtr == GetStartPointer (HobVarStore)); +} + +/** + Get temporary buffer for a new variable data. + + @retval Pointer to the buffer address. + +**/ +VARIABLE_HEADER * +GetNewVariableBuffer ( + VOID + ) +{ + VARIABLE_HEADER *NewVariable; + VARIABLE_STORE_HEADER *VarStore; + + // + // Tricky part: Use scratch data area at the end of volatile variable store + // as a temporary storage. + // + VarStore = (VARIABLE_STORE_HEADER *)(UINTN) + mVariableModuleGlobal->VariableGlobal.VolatileVariableBase; + NewVariable = GetEndPointer (VarStore); + + SetMem (NewVariable, mVariableModuleGlobal->ScratchBufferSize, 0xff); + + return NewVariable; +} + +/** + Delete old copies of variable completely. + + @param[in] VariableName Name of variable. + @param[in] VendorGuid Guid of variable. + @param[in] Variable Pointer to current variable on storage. + @param[in,out] CacheVariable Pointer to current variable in cache. + @param[in] VolatileFlag Auth-variable indicator. + + @retval EFI_SUCCESS Variable(s) were deleted successfully. + @retval Others Failed to update variable state. + +**/ +EFI_STATUS +DeleteVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VARIABLE_POINTER_TRACK *Variable, + IN OUT VARIABLE_POINTER_TRACK *CacheVariable, + IN BOOLEAN VolatileFlag + ) +{ + EFI_STATUS Status; + + if (Variable->InDeletedTransitionPtr != NULL) { + ASSERT (CacheVariable->InDeletedTransitionPtr != NULL); + // + // Both ADDED and IN_DELETED_TRANSITION variable are present, + // set IN_DELETED_TRANSITION one to DELETED state first. + // + Status = UpdateVariableState ( + Variable->InDeletedTransitionPtr, + CacheVariable->InDeletedTransitionPtr, + CacheVariable->InDeletedTransitionPtr->State & VAR_DELETED, + VolatileFlag + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + ASSERT (CacheVariable->CurrPtr != NULL); + Status = UpdateVariableState ( + Variable->CurrPtr, + CacheVariable->CurrPtr, + CacheVariable->CurrPtr->State & VAR_DELETED, + VolatileFlag + ); + + if (!EFI_ERROR (Status)) { + UpdateVariableInfo ( + VariableName, + VendorGuid, + Variable->Volatile, + FALSE, + FALSE, + TRUE, + FALSE, + &gVariableInfo + ); + if (!Variable->Volatile) { + FlushHobVariableToFlash (VariableName, VendorGuid); + } + } + + return Status; +} + +/** + Check if it's the right time to update a variable. + + @param[in] Attributes Attributes of a variable. + + @retval TRUE It's ready for variable update. + @retval FALSE It's NOT ready for variable update. + +**/ +BOOLEAN +ReadyForUpdate ( + IN UINT32 Attributes + ) +{ + if ((mVariableModuleGlobal->FvbInstance == NULL) && + !mVariableModuleGlobal->VariableGlobal.EmuNvMode) + { + // + // The FVB protocol is not ready, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL + // is not installed. + // + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { + // + // Trying to update NV variable prior to the installation of + // EFI_VARIABLE_WRITE_ARCH_PROTOCOL + // + DEBUG (( + DEBUG_ERROR, + "Update NV variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", + EFI_NOT_AVAILABLE_YET + )); + return FALSE; + } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { + // + // Trying to update volatile authenticated variable prior to the + // installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL. The authenticated + // variable perhaps is not initialized, just return here. + // + DEBUG (( + DEBUG_ERROR, + "Update AUTH variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", + EFI_NOT_AVAILABLE_YET + )); + return FALSE; + } + } + + return TRUE; +} + +/** + Check parameters associated with the variable to update. + + @param[in] Variable Pointer to current variable on storage. + @param[in] CacheVariable Pointer to current variable in cache. @param[in] VariableName Name of variable. @param[in] VendorGuid Guid of variable. @param[in] Data Variable data. @@ -1667,9 +1999,130 @@ AutoUpdateLangVariable ( @param[in] Attributes Attributes of the variable. @param[in] KeyIndex Index of associated public key. @param[in] MonotonicCount Value of associated monotonic count. - @param[in, out] CacheVariable The variable information which is used to keep track of variable usage. @param[in] TimeStamp Value of associated TimeStamp. + @retval EFI_SUCCESS The variable is ok to be updated. + @retval EFI_ALREADY_STARTED No need to update the variable. + @retval EFI_WRITE_PROTECTED The variable cannot be updated. + @retval EFI_INVALID_PARAMETER The variable attributes are not valid. + @retval EFI_NOT_FOUND Trying to delete non-existing variable. + +**/ +EFI_STATUS +ValidateVariableParameters ( + IN VARIABLE_POINTER_TRACK *Variable, + IN VARIABLE_POINTER_TRACK *CacheVariable, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes, + IN UINT32 KeyIndex, + IN UINT64 MonotonicCount, + IN EFI_TIME *TimeStamp + ) +{ + BOOLEAN AuthFlag; + + AuthFlag = mVariableModuleGlobal->VariableGlobal.AuthFormat; + + if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) { + return EFI_ALREADY_STARTED; + } + + if (Variable->CurrPtr != NULL) { + // + // Update/Delete existing variable. + // + if (AtRuntime ()) { + // + // If AtRuntime and the variable is Volatile and Runtime Access, + // the volatile is ReadOnly, and SetVariable should be aborted and + // return EFI_WRITE_PROTECTED. + // + if (Variable->Volatile) { + return EFI_WRITE_PROTECTED; + } + + // + // Only variable that have NV attributes can be updated/deleted in Runtime. + // + if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Only variable that have RT attributes can be updated/deleted in Runtime. + // + if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Variable content unchanged and no need to update timestamp, just return. + // + if ( ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) + && (TimeStamp == NULL) + && (DataSizeOfVariable (CacheVariable->CurrPtr, AuthFlag) == DataSize) + && (CompareMem (Data, GetVariableDataPtr (CacheVariable->CurrPtr, AuthFlag), DataSize) == 0)) + { + UpdateVariableInfo ( + VariableName, + VendorGuid, + Variable->Volatile, + FALSE, + TRUE, + FALSE, + FALSE, + &gVariableInfo + ); + return EFI_ALREADY_STARTED; + } + } else { + // + // Create a new variable. + // + + // + // Make sure we are trying to create a new variable. You cannot delete a new + // variable. + // + if ((DataSize == 0) || + ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS|EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) + { + return EFI_NOT_FOUND; + } + + // + // Only variable have NV|RT attribute can be created in Runtime. + // + if ( AtRuntime () + && ( ((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) + || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) + { + return EFI_INVALID_PARAMETER; + } + } + + return EFI_SUCCESS; +} + +/** + Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set, + index of associated public key is needed. + + @param[in] VariableName Name of variable. + @param[in] VendorGuid Guid of variable. + @param[in] Data Variable data. + @param[in] DataSize Size of data. 0 means delete. + @param[in] Attributes Attributes of the variable. + @param[in] KeyIndex Index of associated public key. + @param[in] MonotonicCount Value of associated monotonic count. + @param[in,out] CacheVariable The variable information which is used + to keep track of variable usage. + @param[in] TimeStamp Value of associated TimeStamp. + @retval EFI_SUCCESS The update operation is success. @retval EFI_OUT_OF_RESOURCES Variable region is full, can not write other data into this region. @@ -1687,710 +2140,386 @@ UpdateVariable ( IN EFI_TIME *TimeStamp OPTIONAL ) { - EFI_STATUS Status; - VARIABLE_HEADER *NextVariable; - UINTN ScratchSize; - UINTN MaxDataSize; - UINTN VarNameOffset; - UINTN VarDataOffset; - UINTN VarNameSize; - UINTN VarSize; - BOOLEAN Volatile; - EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; - UINT8 State; - VARIABLE_POINTER_TRACK *Variable; - VARIABLE_POINTER_TRACK NvVariable; - VARIABLE_STORE_HEADER *VariableStoreHeader; - VARIABLE_RUNTIME_CACHE *VolatileCacheInstance; - UINT8 *BufferForMerge; - UINTN MergedBufSize; - BOOLEAN DataReady; - UINTN DataOffset; - BOOLEAN IsCommonVariable; - BOOLEAN IsCommonUserVariable; - AUTHENTICATED_VARIABLE_HEADER *AuthVariable; - BOOLEAN AuthFormat; + EFI_STATUS Status; + VARIABLE_GLOBAL *VarGlobal; + VARIABLE_HEADER *NewVariable; + VARIABLE_HEADER *NextVariable; + VARIABLE_HEADER *UpdatingVariable; + UINTN VarSize; + UINTN UpdateSize; + UINTN Offset; + VARIABLE_POINTER_TRACK *Variable; + VARIABLE_POINTER_TRACK NvVariable; + VARIABLE_STORE_HEADER *VariableStoreHeader; + VARIABLE_RUNTIME_CACHE *VolatileCacheInstance; + BOOLEAN IsCommonVariable; + BOOLEAN IsCommonUserVariable; + BOOLEAN DeleteFlag; + BOOLEAN VolatileFlag; + BOOLEAN HobVarOnlyFlag; + EFI_PHYSICAL_ADDRESS VarStoreBase; + UINTN *LastVariableOffset; - if ((mVariableModuleGlobal->FvbInstance == NULL) && !mVariableModuleGlobal->VariableGlobal.EmuNvMode) { - // - // The FVB protocol is not ready, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL is not installed. - // - if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { - // - // Trying to update NV variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL - // - DEBUG ((DEBUG_ERROR, "Update NV variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET)); - return EFI_NOT_AVAILABLE_YET; - } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { - // - // Trying to update volatile authenticated variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL - // The authenticated variable perhaps is not initialized, just return here. - // - DEBUG ((DEBUG_ERROR, "Update AUTH variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET)); - return EFI_NOT_AVAILABLE_YET; - } + if (!ReadyForUpdate (Attributes)) { + return EFI_NOT_AVAILABLE_YET; } - AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat; + VarGlobal = &mVariableModuleGlobal->VariableGlobal; + + if ( (((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) + || (Attributes == 0) + || (AtRuntime () && ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS + |EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0))) + { + DeleteFlag = TRUE; + } else { + DeleteFlag = FALSE; + } + + if ( ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) + || ((CacheVariable->CurrPtr != NULL) && + ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) != 0))) + { + VolatileFlag = FALSE; + } else { + VolatileFlag = TRUE; + } // // Check if CacheVariable points to the variable in variable HOB. // If yes, let CacheVariable points to the variable in NV variable cache. // - if ((CacheVariable->CurrPtr != NULL) && - (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) && - (CacheVariable->StartPtr == GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.HobVariableBase)) - ) - { - CacheVariable->StartPtr = GetStartPointer (mNvVariableCache); - CacheVariable->EndPtr = GetEndPointer (mNvVariableCache); - CacheVariable->Volatile = FALSE; - Status = FindVariableEx (VariableName, VendorGuid, FALSE, CacheVariable, AuthFormat); + HobVarOnlyFlag = FALSE; + if (IsHobVariable (CacheVariable)) { + Status = RebaseVariablePtr ( + CacheVariable, + CacheVariable, + mNvVariableCache, + VariableName, + VendorGuid, + FALSE + ); if ((CacheVariable->CurrPtr == NULL) || EFI_ERROR (Status)) { // // There is no matched variable in NV variable cache. // - if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0)) { + if (DeleteFlag) { // - // It is to delete variable, - // go to delete this variable in variable HOB and - // try to flush other variables from HOB to flash. + // Leave the deletion to FlushHobVariableToFlash() before return. // - UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, FALSE, TRUE, FALSE, &gVariableInfo); - FlushHobVariableToFlash (VariableName, VendorGuid); - return EFI_SUCCESS; + HobVarOnlyFlag = TRUE; + Status = EFI_SUCCESS; + goto Done; } } } + // + // Determine the physical position of variable store to update, due to cache + // mechanims of variable service. + // if ((CacheVariable->CurrPtr == NULL) || CacheVariable->Volatile) { + // + // - Add new variable (volatile or non-volatile); Or + // - Update/delete volatile variable in place. + // Variable = CacheVariable; } else { // - // Update/Delete existing NV variable. - // CacheVariable points to the variable in the memory copy of Flash area - // Now let Variable points to the same variable in Flash area. + // - Update/Delete existing NV variable. + // CacheVariable points to the variable in the memory copy of Flash area. + // Now let Variable points to the same variable in Flash area. // - VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase); - Variable = &NvVariable; - Variable->StartPtr = GetStartPointer (VariableStoreHeader); - Variable->EndPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->EndPtr - (UINTN)CacheVariable->StartPtr)); - - Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr)); - if (CacheVariable->InDeletedTransitionPtr != NULL) { - Variable->InDeletedTransitionPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->InDeletedTransitionPtr - (UINTN)CacheVariable->StartPtr)); - } else { - Variable->InDeletedTransitionPtr = NULL; - } - - Variable->Volatile = FALSE; + VariableStoreHeader = (VARIABLE_STORE_HEADER *)(UINTN) + VarGlobal->NonVolatileVariableBase; + Variable = &NvVariable; + Status = RebaseVariablePtr ( + CacheVariable, + Variable, + VariableStoreHeader, + VariableName, + VendorGuid, + TRUE + ); + ASSERT_EFI_ERROR (Status); } - Fvb = mVariableModuleGlobal->FvbInstance; - // - // Tricky part: Use scratch data area at the end of volatile variable store - // as a temporary storage. + // Validate variable parameters. // - NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *)((UINTN)mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)); - ScratchSize = mVariableModuleGlobal->ScratchBufferSize; - SetMem (NextVariable, ScratchSize, 0xff); - DataReady = FALSE; - - if (Variable->CurrPtr != NULL) { - // - // Update/Delete existing variable. - // - if (AtRuntime ()) { - // - // If AtRuntime and the variable is Volatile and Runtime Access, - // the volatile is ReadOnly, and SetVariable should be aborted and - // return EFI_WRITE_PROTECTED. - // - if (Variable->Volatile) { - Status = EFI_WRITE_PROTECTED; - goto Done; - } - - // - // Only variable that have NV attributes can be updated/deleted in Runtime. - // - if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { - Status = EFI_INVALID_PARAMETER; - goto Done; - } - - // - // Only variable that have RT attributes can be updated/deleted in Runtime. - // - if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) { - Status = EFI_INVALID_PARAMETER; - goto Done; - } - } - - // - // Setting a data variable with no access, or zero DataSize attributes - // causes it to be deleted. - // When the EFI_VARIABLE_APPEND_WRITE attribute is set, DataSize of zero will - // not delete the variable. - // - if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) { - if (Variable->InDeletedTransitionPtr != NULL) { - // - // Both ADDED and IN_DELETED_TRANSITION variable are present, - // set IN_DELETED_TRANSITION one to DELETED state first. - // - ASSERT (CacheVariable->InDeletedTransitionPtr != NULL); - State = CacheVariable->InDeletedTransitionPtr->State; - State &= VAR_DELETED; - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - Variable->Volatile, - FALSE, - Fvb, - (UINTN)&Variable->InDeletedTransitionPtr->State, - sizeof (UINT8), - &State - ); - if (!EFI_ERROR (Status)) { - if (!Variable->Volatile) { - CacheVariable->InDeletedTransitionPtr->State = State; - } - } else { - goto Done; - } - } - - State = CacheVariable->CurrPtr->State; - State &= VAR_DELETED; - - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - Variable->Volatile, - FALSE, - Fvb, - (UINTN)&Variable->CurrPtr->State, - sizeof (UINT8), - &State - ); - if (!EFI_ERROR (Status)) { - UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE, &gVariableInfo); - if (!Variable->Volatile) { - CacheVariable->CurrPtr->State = State; - FlushHobVariableToFlash (VariableName, VendorGuid); - } - } + Status = ValidateVariableParameters ( + Variable, + CacheVariable, + VariableName, + VendorGuid, + Data, + DataSize, + Attributes, + KeyIndex, + MonotonicCount, + TimeStamp + ); + if (EFI_ERROR (Status)) { + goto Done; + } - goto Done; - } + // + // Add or update a variable. Allocate a buffer to hold it temporarily. + // + NewVariable = GetNewVariableBuffer (); + // + // Fill-up variable data first, if necessary. + // + IsCommonVariable = FALSE; + IsCommonUserVariable = FALSE; + if (DeleteFlag) { // - // If the variable is marked valid, and the same data has been passed in, - // then return to the caller immediately. + // No need to fill up variable buffer when deleting a variable. But the + // buffer is still needed if variable protection is employed. // - if ((DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) == DataSize) && - (CompareMem (Data, GetVariableDataPtr (CacheVariable->CurrPtr, AuthFormat), DataSize) == 0) && - ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && - (TimeStamp == NULL)) - { - // - // Variable content unchanged and no need to update timestamp, just return. - // - UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE, &gVariableInfo); - Status = EFI_SUCCESS; - goto Done; - } else if ((CacheVariable->CurrPtr->State == VAR_ADDED) || - (CacheVariable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) - { - // - // EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable. - // - if ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) { - // - // NOTE: From 0 to DataOffset of NextVariable is reserved for Variable Header and Name. - // From DataOffset of NextVariable is to save the existing variable data. - // - DataOffset = GetVariableDataOffset (CacheVariable->CurrPtr, AuthFormat); - BufferForMerge = (UINT8 *)((UINTN)NextVariable + DataOffset); - CopyMem ( - BufferForMerge, - (UINT8 *)((UINTN)CacheVariable->CurrPtr + DataOffset), - DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) - ); - - // - // Set Max Auth/Non-Volatile/Volatile Variable Data Size as default MaxDataSize. - // - if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { - MaxDataSize = mVariableModuleGlobal->MaxAuthVariableSize - DataOffset; - } else if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { - MaxDataSize = mVariableModuleGlobal->MaxVariableSize - DataOffset; - } else { - MaxDataSize = mVariableModuleGlobal->MaxVolatileVariableSize - DataOffset; - } - - // - // Append the new data to the end of existing data. - // Max Harware error record variable data size is different from common/auth variable. - // - if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { - MaxDataSize = PcdGet32 (PcdMaxHardwareErrorVariableSize) - DataOffset; - } - - if (DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) + DataSize > MaxDataSize) { - // - // Existing data size + new data size exceed maximum variable size limitation. - // - Status = EFI_INVALID_PARAMETER; - goto Done; - } - - CopyMem ( - (UINT8 *)( - (UINTN)BufferForMerge + DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) - ), - Data, - DataSize - ); - MergedBufSize = DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) + - DataSize; - - // - // BufferForMerge(from DataOffset of NextVariable) has included the merged existing and new data. - // - Data = BufferForMerge; - DataSize = MergedBufSize; - DataReady = TRUE; - } - - // - // Mark the old variable as in delete transition. - // - State = CacheVariable->CurrPtr->State; - State &= VAR_IN_DELETED_TRANSITION; - - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - Variable->Volatile, - FALSE, - Fvb, - (UINTN)&Variable->CurrPtr->State, - sizeof (UINT8), - &State - ); - if (EFI_ERROR (Status)) { - goto Done; - } - - if (!Variable->Volatile) { - CacheVariable->CurrPtr->State = State; - } - } + VarSize = 0; } else { - // - // Not found existing variable. Create a new variable. - // + VarSize = SetVariableData ( + NewVariable, + CacheVariable->CurrPtr, + VariableName, + VendorGuid, + Data, + DataSize, + Attributes, + KeyIndex, + MonotonicCount, + TimeStamp + ); - if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) { - Status = EFI_SUCCESS; - goto Done; - } - - // - // Make sure we are trying to create a new variable. - // Setting a data variable with zero DataSize or no access attributes means to delete it. - // - if ((DataSize == 0) || ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) { - Status = EFI_NOT_FOUND; - goto Done; - } - - // - // Only variable have NV|RT attribute can be created in Runtime. - // - if (AtRuntime () && - (((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) - { - Status = EFI_INVALID_PARAMETER; - goto Done; - } - } - - // - // Function part - create a new variable and copy the data. - // Both update a variable and create a variable will come here. - // - NextVariable->StartId = VARIABLE_DATA; - // - // NextVariable->State = VAR_ADDED; - // - NextVariable->Reserved = 0; - if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { - AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)NextVariable; - AuthVariable->PubKeyIndex = KeyIndex; - AuthVariable->MonotonicCount = MonotonicCount; - ZeroMem (&AuthVariable->TimeStamp, sizeof (EFI_TIME)); - - if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) && - (TimeStamp != NULL)) - { - if ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) { - CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME)); - } else { - // - // In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only - // when the new TimeStamp value is later than the current timestamp associated - // with the variable, we need associate the new timestamp with the updated value. - // - if (Variable->CurrPtr != NULL) { - if (VariableCompareTimeStampInternal (&(((AUTHENTICATED_VARIABLE_HEADER *)CacheVariable->CurrPtr)->TimeStamp), TimeStamp)) { - CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME)); - } else { - CopyMem (&AuthVariable->TimeStamp, &(((AUTHENTICATED_VARIABLE_HEADER *)CacheVariable->CurrPtr)->TimeStamp), sizeof (EFI_TIME)); - } - } - } - } - } - - // - // The EFI_VARIABLE_APPEND_WRITE attribute will never be set in the returned - // Attributes bitmask parameter of a GetVariable() call. - // - NextVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE); - - VarNameOffset = GetVariableHeaderSize (AuthFormat); - VarNameSize = StrSize (VariableName); - CopyMem ( - (UINT8 *)((UINTN)NextVariable + VarNameOffset), - VariableName, - VarNameSize - ); - VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize); - - // - // If DataReady is TRUE, it means the variable data has been saved into - // NextVariable during EFI_VARIABLE_APPEND_WRITE operation preparation. - // - if (!DataReady) { - CopyMem ( - (UINT8 *)((UINTN)NextVariable + VarDataOffset), - Data, - DataSize - ); - } - - CopyMem ( - GetVendorGuidPtr (NextVariable, AuthFormat), - VendorGuid, - sizeof (EFI_GUID) - ); - // - // There will be pad bytes after Data, the NextVariable->NameSize and - // NextVariable->DataSize should not include pad size so that variable - // service can get actual size in GetVariable. - // - SetNameSizeOfVariable (NextVariable, VarNameSize, AuthFormat); - SetDataSizeOfVariable (NextVariable, DataSize, AuthFormat); - - // - // The actual size of the variable that stores in storage should - // include pad size. - // - VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize); - if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { - // - // Create a nonvolatile variable. - // - Volatile = FALSE; - - IsCommonVariable = FALSE; - IsCommonUserVariable = FALSE; if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) { IsCommonVariable = TRUE; - IsCommonUserVariable = IsUserVariable (NextVariable); + IsCommonUserVariable = IsUserVariable (NewVariable); } + } - if ( ( ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) - && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize))) - || (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace)) - || (IsCommonVariable && AtRuntime () && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace)) - || (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace))) - { - if (AtRuntime ()) { - if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) { - RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize); - } - - if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace)) { - RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize); - } - - Status = EFI_OUT_OF_RESOURCES; - goto Done; - } - - // - // Perform garbage collection & reclaim operation, and integrate the new variable at the same time. - // - Status = Reclaim ( - mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, - &mVariableModuleGlobal->NonVolatileLastVariableOffset, - FALSE, - Variable, - NextVariable, - HEADER_ALIGN (VarSize) - ); - if (!EFI_ERROR (Status)) { - // - // The new variable has been integrated successfully during reclaiming. - // - if (Variable->CurrPtr != NULL) { - CacheVariable->CurrPtr = (VARIABLE_HEADER *)((UINTN)CacheVariable->StartPtr + ((UINTN)Variable->CurrPtr - (UINTN)Variable->StartPtr)); - CacheVariable->InDeletedTransitionPtr = NULL; - } - - UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, TRUE, FALSE, FALSE, &gVariableInfo); - FlushHobVariableToFlash (VariableName, VendorGuid); - } else { - if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) { - RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize); - } + // + // We might need to do protection for non-volatile variable before flushing + // the data to storage. A null version (meaning no protection) of following + // APIs should simply return EFI_SUCCESS or EFI_UNSUPPORTED without any + // changes to original data. + // + if (!VolatileFlag) { + Status = ProtectedVariableLibUpdate ( + Variable->CurrPtr, + Variable->InDeletedTransitionPtr, + NewVariable, + &VarSize + ); + if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) { + return Status; + } - if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace)) { - RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize); - } - } + Status = EFI_SUCCESS; + } + // + // Mark the old variable as in delete transition first. There's no such need + // for deleting a variable, even if variable protection is employed. + // + if ( !DeleteFlag + && (CacheVariable->CurrPtr != NULL) + && ( (CacheVariable->CurrPtr->State == VAR_ADDED) + || (CacheVariable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)))) + { + Status = UpdateVariableState ( + Variable->CurrPtr, + CacheVariable->CurrPtr, + CacheVariable->CurrPtr->State & VAR_IN_DELETED_TRANSITION, + Variable->Volatile + ); + if (EFI_ERROR (Status)) { goto Done; } - - if (!mVariableModuleGlobal->VariableGlobal.EmuNvMode) { - // - // Four steps - // 1. Write variable header - // 2. Set variable state to header valid - // 3. Write variable data - // 4. Set variable state to valid - // - // - // Step 1: - // - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - FALSE, - TRUE, - Fvb, - mVariableModuleGlobal->NonVolatileLastVariableOffset, - (UINT32)GetVariableHeaderSize (AuthFormat), - (UINT8 *)NextVariable - ); - - if (EFI_ERROR (Status)) { - goto Done; - } - - // - // Step 2: - // - NextVariable->State = VAR_HEADER_VALID_ONLY; - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - FALSE, - TRUE, - Fvb, - mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State), - sizeof (UINT8), - &NextVariable->State - ); - - if (EFI_ERROR (Status)) { - goto Done; - } - - // - // Step 3: - // - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - FALSE, - TRUE, - Fvb, - mVariableModuleGlobal->NonVolatileLastVariableOffset + GetVariableHeaderSize (AuthFormat), - (UINT32)(VarSize - GetVariableHeaderSize (AuthFormat)), - (UINT8 *)NextVariable + GetVariableHeaderSize (AuthFormat) + } + + // + // Have enough space to store the variable? + // + Status = CheckVariableStoreSpace ( + NewVariable, + VarSize, + VariableName, + VendorGuid, + Attributes, + VolatileFlag + ); + if (Status == EFI_OUT_OF_RESOURCES) { + // + // Not a chance. + // + goto Done; + } + + // + // Maybe not... + // + VarStoreBase = (VolatileFlag) ? VarGlobal->VolatileVariableBase + : VarGlobal->NonVolatileVariableBase; + LastVariableOffset = (VolatileFlag) + ? &mVariableModuleGlobal->VolatileLastVariableOffset + : &mVariableModuleGlobal->NonVolatileLastVariableOffset; + if (!EFI_ERROR (Status)) { + // + // There's enough free space at the tail of variable storage. + // + + // + // If non-volatile variable is protected, a separate variable (MetaDataHmacVar) + // is always updated along with current updating variable. The buffer pointed + // by NewVariable must have two variables. They should be written at this + // time orderly. + // + NextVariable = NewVariable; + UpdatingVariable = NULL; + UpdateSize = 0; + while ( !EFI_ERROR (Status) + && ((UINTN)NextVariable - (UINTN)NewVariable) < VarSize) + { + UpdatingVariable = NextVariable; + NextVariable = GetNextVariablePtr (UpdatingVariable, VarGlobal->AuthFormat); + UpdateSize = (UINTN)NextVariable - (UINTN)UpdatingVariable; + + Status = WriteVariable ( + VarStoreBase, + LastVariableOffset, + &UpdatingVariable, + (UINT32)UpdateSize, + VolatileFlag, + VarGlobal->AuthFormat ); - - if (EFI_ERROR (Status)) { - goto Done; - } - - // - // Step 4: - // - NextVariable->State = VAR_ADDED; - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - FALSE, - TRUE, - Fvb, - mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State), - sizeof (UINT8), - &NextVariable->State - ); - - if (EFI_ERROR (Status)) { - goto Done; - } - - // - // Update the memory copy of Flash region. - // - CopyMem ((UINT8 *)mNvVariableCache + mVariableModuleGlobal->NonVolatileLastVariableOffset, (UINT8 *)NextVariable, VarSize); - } else { - // - // Emulated non-volatile variable mode. - // - NextVariable->State = VAR_ADDED; - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - FALSE, - TRUE, - Fvb, - mVariableModuleGlobal->NonVolatileLastVariableOffset, - (UINT32)VarSize, - (UINT8 *)NextVariable - ); - - if (EFI_ERROR (Status)) { - goto Done; - } } - mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize); - + // + // UpdatingVariable must point to the last written variable. Restore it to + // the first one so that we can calculate the offset in variable storage. + // + UpdatingVariable = (VARIABLE_HEADER *)((UINTN)UpdatingVariable + UpdateSize + - VarSize); if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) { - mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VarSize); + mVariableModuleGlobal->HwErrVariableTotalSize += VarSize; } else { - mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize); + mVariableModuleGlobal->CommonVariableTotalSize += VarSize; if (IsCommonUserVariable) { - mVariableModuleGlobal->CommonUserVariableTotalSize += HEADER_ALIGN (VarSize); + mVariableModuleGlobal->CommonUserVariableTotalSize += VarSize; } } - } else { - // - // Create a volatile variable. - // - Volatile = TRUE; - if ((UINT32)(VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) > - ((VARIABLE_STORE_HEADER *)((UINTN)(mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size) - { - // - // Perform garbage collection & reclaim operation, and integrate the new variable at the same time. - // - Status = Reclaim ( - mVariableModuleGlobal->VariableGlobal.VolatileVariableBase, - &mVariableModuleGlobal->VolatileLastVariableOffset, - TRUE, + // + // Mark the old variable(s) as deleted. + // + if (!EFI_ERROR (Status) && (Variable->CurrPtr != NULL)) { + Status = DeleteVariable ( + VariableName, + VendorGuid, Variable, - NextVariable, - HEADER_ALIGN (VarSize) + CacheVariable, + VolatileFlag ); - if (!EFI_ERROR (Status)) { - // - // The new variable has been integrated successfully during reclaiming. - // - if (Variable->CurrPtr != NULL) { - CacheVariable->CurrPtr = (VARIABLE_HEADER *)((UINTN)CacheVariable->StartPtr + ((UINTN)Variable->CurrPtr - (UINTN)Variable->StartPtr)); - CacheVariable->InDeletedTransitionPtr = NULL; - } - - UpdateVariableInfo (VariableName, VendorGuid, TRUE, FALSE, TRUE, FALSE, FALSE, &gVariableInfo); - } - - goto Done; } - - NextVariable->State = VAR_ADDED; - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - TRUE, - TRUE, - Fvb, - mVariableModuleGlobal->VolatileLastVariableOffset, - (UINT32)VarSize, - (UINT8 *)NextVariable - ); - - if (EFI_ERROR (Status)) { - goto Done; - } - - mVariableModuleGlobal->VolatileLastVariableOffset += HEADER_ALIGN (VarSize); - } - - // - // Mark the old variable as deleted. - // - if (!EFI_ERROR (Status) && (Variable->CurrPtr != NULL)) { - if (Variable->InDeletedTransitionPtr != NULL) { - // - // Both ADDED and IN_DELETED_TRANSITION old variable are present, - // set IN_DELETED_TRANSITION one to DELETED state first. - // - ASSERT (CacheVariable->InDeletedTransitionPtr != NULL); - State = CacheVariable->InDeletedTransitionPtr->State; - State &= VAR_DELETED; - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - Variable->Volatile, - FALSE, - Fvb, - (UINTN)&Variable->InDeletedTransitionPtr->State, - sizeof (UINT8), - &State - ); - if (!EFI_ERROR (Status)) { - if (!Variable->Volatile) { - CacheVariable->InDeletedTransitionPtr->State = State; - } - } else { - goto Done; - } - } - - State = CacheVariable->CurrPtr->State; - State &= VAR_DELETED; - - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - Variable->Volatile, - FALSE, - Fvb, - (UINTN)&Variable->CurrPtr->State, - sizeof (UINT8), - &State + } else { + // + // There's not enough space at the tail of variable storage but there's + // enough free space holes in the whole storage. Perform garbage collection + // & reclaim operation, and integrate the new variable at the same time. + // + Status = Reclaim ( + VarStoreBase, + LastVariableOffset, + VolatileFlag, + Variable, + NewVariable, + VarSize ); - if (!EFI_ERROR (Status) && !Variable->Volatile) { - CacheVariable->CurrPtr->State = State; + + if (Variable->CurrPtr != NULL) { + UpdatingVariable = Variable->CurrPtr; + } else { + UpdatingVariable = (VARIABLE_HEADER *)(((UINTN)VarStoreBase + *LastVariableOffset) - VarSize); } - } - if (!EFI_ERROR (Status)) { - UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE, &gVariableInfo); - if (!Volatile) { - FlushHobVariableToFlash (VariableName, VendorGuid); + if (EFI_ERROR (Status) && + ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0)) + { + // + // Out of space. + // + IsCommonUserVariable = IsUserVariable (NewVariable); + IsCommonVariable = TRUE; + + if (IsCommonUserVariable && + ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) + > mVariableModuleGlobal->CommonMaxUserVariableSpace)) + { + RecordVarErrorFlag ( + VAR_ERROR_FLAG_USER_ERROR, + VariableName, + VendorGuid, + Attributes, + VarSize + ); + } + + if (IsCommonVariable && + ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) + > mVariableModuleGlobal->CommonVariableSpace)) + { + RecordVarErrorFlag ( + VAR_ERROR_FLAG_SYSTEM_ERROR, + VariableName, + VendorGuid, + Attributes, + VarSize + ); + } } } Done: if (!EFI_ERROR (Status)) { - if (((Variable->CurrPtr != NULL) && !Variable->Volatile) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)) { - VolatileCacheInstance = &(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache); + if (!VolatileFlag) { + Offset = (UpdatingVariable != NULL) ? (UINTN)UpdatingVariable - (UINTN)VarStoreBase + : 0; + Status = ProtectedVariableLibWriteFinal ( + NewVariable, + VarSize, + Offset + ); + if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) { + return Status; + } + + Status = EFI_SUCCESS; + } + + UpdateVariableInfo ( + VariableName, + VendorGuid, + VolatileFlag, + FALSE, + TRUE, + FALSE, + FALSE, + &gVariableInfo + ); + // + // HOB copy of the same variable is no longer needed, no matter it has + // been deleted, updated or added from/to real variable storage. + // + if (HobVarOnlyFlag || !VolatileFlag) { + FlushHobVariableToFlash (VariableName, VendorGuid); + } + + if (!VolatileFlag) { + VolatileCacheInstance = &(VarGlobal->VariableRuntimeCacheContext.VariableRuntimeNvCache); } else { - VolatileCacheInstance = &(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache); + VolatileCacheInstance = &(VarGlobal->VariableRuntimeCacheContext.VariableRuntimeVolatileCache); } if (VolatileCacheInstance->Store != NULL) { @@ -2401,6 +2530,11 @@ Done: ); ASSERT_EFI_ERROR (Status); } + } else if (Status == EFI_ALREADY_STARTED) { + // + // Meaning nothing needs to be done. Just return success. + // + Status = EFI_SUCCESS; } return Status; @@ -2440,7 +2574,6 @@ VariableServiceGetVariable ( { EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; - UINTN VarDataSize; if ((VariableName == NULL) || (VendorGuid == NULL) || (DataSize == NULL)) { return EFI_INVALID_PARAMETER; @@ -2458,28 +2591,26 @@ VariableServiceGetVariable ( } // - // Get data size + // Get data and its size // - VarDataSize = DataSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); - ASSERT (VarDataSize != 0); + if (!Variable.Volatile) { + // + // Currently only non-volatile variable needs protection. + // + Status = ProtectedVariableLibGetByBuffer ( + Variable.CurrPtr, + Data, + (UINT32 *)DataSize, + mVariableModuleGlobal->VariableGlobal.AuthFormat + ); + } - if (*DataSize >= VarDataSize) { - if (Data == NULL) { - Status = EFI_INVALID_PARAMETER; - goto Done; - } + if (Variable.Volatile || (Status == EFI_UNSUPPORTED)) { + Status = GetVariableData (Variable.CurrPtr, Data, (UINT32 *)DataSize, mVariableModuleGlobal->VariableGlobal.AuthFormat); + } - CopyMem (Data, GetVariableDataPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat), VarDataSize); - - *DataSize = VarDataSize; + if (!EFI_ERROR (Status)) { UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE, &gVariableInfo); - - Status = EFI_SUCCESS; - goto Done; - } else { - *DataSize = VarDataSize; - Status = EFI_BUFFER_TOO_SMALL; - goto Done; } Done: @@ -2860,7 +2991,7 @@ VariableServiceSetVariable ( // Parse non-volatile variable data and get last variable offset. // NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point); - while (IsValidVariableHeader (NextVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point))) { + while (IsValidVariableHeader (NextVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point), AuthFormat)) { NextVariable = GetNextVariablePtr (NextVariable, AuthFormat); } @@ -3022,7 +3153,12 @@ VariableServiceQueryVariableInfoInternal ( // // Now walk through the related variable store. // - while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { + while (IsValidVariableHeader ( + Variable, + GetEndPointer (VariableStoreHeader), + mVariableModuleGlobal->VariableGlobal.AuthFormat + )) + { NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat); VariableSize = (UINT64)(UINTN)NextVariable - (UINT64)(UINTN)Variable; @@ -3315,7 +3451,7 @@ FlushHobVariableToFlash ( // mVariableModuleGlobal->VariableGlobal.HobVariableBase = 0; for ( Variable = GetStartPointer (VariableStoreHeader) - ; IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader)) + ; IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat) ; Variable = GetNextVariablePtr (Variable, AuthFormat) ) { @@ -3525,11 +3661,11 @@ ConvertNormalVarStorageToAuthVarStorage ( VARIABLE_HEADER *StartPtr; UINT8 *NextPtr; VARIABLE_HEADER *EndPtr; - UINTN AuthVarStroageSize; + UINTN AuthVarStorageSize; AUTHENTICATED_VARIABLE_HEADER *AuthStartPtr; VARIABLE_STORE_HEADER *AuthVarStorage; - AuthVarStroageSize = sizeof (VARIABLE_STORE_HEADER); + AuthVarStorageSize = sizeof (VARIABLE_STORE_HEADER); // // Set AuthFormat as FALSE for normal variable storage // @@ -3542,10 +3678,10 @@ ConvertNormalVarStorageToAuthVarStorage ( EndPtr = GetEndPointer (NormalVarStorage); while (StartPtr < EndPtr) { if (StartPtr->State == VAR_ADDED) { - AuthVarStroageSize = HEADER_ALIGN (AuthVarStroageSize); - AuthVarStroageSize += sizeof (AUTHENTICATED_VARIABLE_HEADER); - AuthVarStroageSize += StartPtr->NameSize + GET_PAD_SIZE (StartPtr->NameSize); - AuthVarStroageSize += StartPtr->DataSize + GET_PAD_SIZE (StartPtr->DataSize); + AuthVarStorageSize = HEADER_ALIGN (AuthVarStorageSize); + AuthVarStorageSize += sizeof (AUTHENTICATED_VARIABLE_HEADER); + AuthVarStorageSize += StartPtr->NameSize + GET_PAD_SIZE (StartPtr->NameSize); + AuthVarStorageSize += StartPtr->DataSize + GET_PAD_SIZE (StartPtr->DataSize); } StartPtr = GetNextVariablePtr (StartPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); @@ -3554,7 +3690,7 @@ ConvertNormalVarStorageToAuthVarStorage ( // // Allocate Runtime memory for Auth Variable Storage // - AuthVarStorage = AllocateRuntimeZeroPool (AuthVarStroageSize); + AuthVarStorage = AllocateRuntimeZeroPool (AuthVarStorageSize); ASSERT (AuthVarStorage != NULL); if (AuthVarStorage == NULL) { return NULL; @@ -3608,7 +3744,7 @@ ConvertNormalVarStorageToAuthVarStorage ( AuthVarStorage->State = NormalVarStorage->State; AuthVarStorage->Size = (UINT32)((UINTN)AuthStartPtr - (UINTN)AuthVarStorage); CopyGuid (&AuthVarStorage->Signature, &gEfiAuthenticatedVariableGuid); - ASSERT (AuthVarStorage->Size <= AuthVarStroageSize); + ASSERT (AuthVarStorage->Size <= AuthVarStorageSize); // // Restore AuthFormat @@ -3774,7 +3910,7 @@ VariableCommonInitialize ( // // Allocate memory for volatile variable store, note that there is a scratch space to store scratch data. // - ScratchSize = GetMaxVariableSize (); + ScratchSize = GetMaxVariableSize () * 2; mVariableModuleGlobal->ScratchBufferSize = ScratchSize; VolatileVariableStore = AllocateRuntimePool (PcdGet32 (PcdVariableStoreSize) + ScratchSize); if (VolatileVariableStore == NULL) { diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c index d5c409c914d1..4595bf8c9d02 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c @@ -3,7 +3,7 @@ and volatile storage space and install variable architecture protocol. Copyright (C) 2013, Red Hat, Inc. -Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent @@ -14,6 +14,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include +#include "VariableParsing.h" EFI_STATUS EFIAPI @@ -542,6 +543,29 @@ VariableServiceInitialize ( EFI_EVENT ReadyToBootEvent; EFI_EVENT EndOfDxeEvent; + PROTECTED_VARIABLE_CONTEXT_IN ContextIn; + + // + // Initialze protected variable service, if enabled. + // + ContextIn.StructSize = sizeof (ContextIn); + ContextIn.StructVersion = PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION; + + ContextIn.FindVariableSmm = NULL; + ContextIn.GetVariableInfo = GetVariableInfo; + ContextIn.GetNextVariableInfo = GetNextVariableInfo; + ContextIn.UpdateVariableStore = VariableExLibUpdateNvVariable; + ContextIn.UpdateVariable = VariableExLibUpdateVariable; + + ContextIn.MaxVariableSize = (UINT32)GetMaxVariableSize (); + ContextIn.VariableServiceUser = FromSmmModule; + + Status = ProtectedVariableLibInitialize (&ContextIn); + if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + Status = VariableCommonInitialize (); ASSERT_EFI_ERROR (Status); diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c index 62cde0335512..5904bcbff78a 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c @@ -1,13 +1,14 @@ /** @file Provides variable driver extended services. -Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2015 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Variable.h" #include "VariableParsing.h" +#include "VariableRuntimeCache.h" /** Finds variable in storage blocks of volatile and non-volatile storage areas. @@ -38,6 +39,7 @@ VariableExLibFindVariable ( EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + PROTECTED_VARIABLE_INFO VarInfo; Status = FindVariable ( VariableName, @@ -56,9 +58,12 @@ VariableExLibFindVariable ( return Status; } - AuthVariableInfo->DataSize = DataSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); - AuthVariableInfo->Data = GetVariableDataPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); - AuthVariableInfo->Attributes = Variable.CurrPtr->Attributes; + AuthVariableInfo->NameSize = NameSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); + AuthVariableInfo->VariableName = GetVariableNamePtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); + AuthVariableInfo->VendorGuid = GetVendorGuidPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); + AuthVariableInfo->DataSize = DataSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); + AuthVariableInfo->Data = GetVariableDataPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); + AuthVariableInfo->Attributes = Variable.CurrPtr->Attributes; if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable.CurrPtr; AuthVariableInfo->PubKeyIndex = AuthVariable->PubKeyIndex; @@ -66,6 +71,24 @@ VariableExLibFindVariable ( AuthVariableInfo->TimeStamp = &AuthVariable->TimeStamp; } + CopyMem (&VarInfo.Header, AuthVariableInfo, sizeof (VarInfo.Header)); + + VarInfo.Buffer = Variable.CurrPtr; + VarInfo.PlainData = NULL; + VarInfo.PlainDataSize = 0; + VarInfo.Flags.Auth = mVariableModuleGlobal->VariableGlobal.AuthFormat; + + // + // In case the variable is encrypted. + // + Status = ProtectedVariableLibGetByInfo (&VarInfo); + if (!EFI_ERROR (Status)) { + if (VarInfo.PlainData != NULL) { + AuthVariableInfo->Data = VarInfo.PlainData; + AuthVariableInfo->DataSize = VarInfo.PlainDataSize; + } + } + return EFI_SUCCESS; } @@ -99,6 +122,7 @@ VariableExLibFindNextVariable ( VARIABLE_HEADER *VariablePtr; AUTHENTICATED_VARIABLE_HEADER *AuthVariablePtr; VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax]; + PROTECTED_VARIABLE_INFO VarInfo; VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.VolatileVariableBase; VariableStoreHeader[VariableStoreTypeHob] = (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.HobVariableBase; @@ -123,6 +147,7 @@ VariableExLibFindNextVariable ( return Status; } + AuthVariableInfo->NameSize = NameSizeOfVariable (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); AuthVariableInfo->VariableName = GetVariableNamePtr (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); AuthVariableInfo->VendorGuid = GetVendorGuidPtr (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); AuthVariableInfo->DataSize = DataSizeOfVariable (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat); @@ -135,6 +160,20 @@ VariableExLibFindNextVariable ( AuthVariableInfo->TimeStamp = &AuthVariablePtr->TimeStamp; } + CopyMem (&VarInfo.Header, AuthVariableInfo, sizeof (VarInfo.Header)); + + VarInfo.Buffer = VariablePtr; + VarInfo.PlainData = NULL; + VarInfo.PlainDataSize = 0; + + Status = ProtectedVariableLibGetByInfo (&VarInfo); + if (!EFI_ERROR (Status)) { + if (VarInfo.PlainData != NULL) { + AuthVariableInfo->Data = VarInfo.PlainData; + AuthVariableInfo->DataSize = VarInfo.PlainDataSize; + } + } + return EFI_SUCCESS; } @@ -256,3 +295,123 @@ VariableExLibAtRuntime ( { return AtRuntime (); } + +/** + Update partial data of a variable on NV storage and/or cached copy. + + @param[in] VariableInfo Pointer to a variable with detailed information. + @param[in] Offset Offset to write from. + @param[in] Size Size of data Buffer to update. + @param[in] Buffer Pointer to data buffer to update. + + @retval EFI_SUCCESS The variable data was updated successfully. + @retval EFI_UNSUPPORTED If this function is called directly in runtime. + @retval EFI_INVALID_PARAMETER If VariableInfo, Buffer or Size are not valid. + @retval Others Failed to update NV storage or variable cache. + +**/ +EFI_STATUS +EFIAPI +VariableExLibUpdateNvVariable ( + IN PROTECTED_VARIABLE_INFO *VariableInfo, + IN UINTN Offset, + IN UINT32 Size, + IN UINT8 *Buffer + ) +{ + EFI_STATUS Status; + VARIABLE_GLOBAL *Global; + VARIABLE_RUNTIME_CACHE *CacheInstance; + VARIABLE_HEADER *VariableCache; + + if ((mVariableModuleGlobal == NULL) || (mNvVariableCache == NULL)) { + return EFI_UNSUPPORTED; + } + + // + // Flush the cache to store. + // + if (Size == (UINT32)-1) { + Status = FtwVariableSpace ( + mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, + mNvVariableCache + ); + if ( !EFI_ERROR (Status) + && (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0)) + { + FlushHobVariableToFlash (NULL, NULL); + if (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) { + FreePool ((VOID *)(UINTN)mVariableModuleGlobal->VariableGlobal.HobVariableBase); + mVariableModuleGlobal->VariableGlobal.HobVariableBase = 0; + } + } + + return Status; + } + + if ( (VariableInfo == NULL) + || (VariableInfo->StoreIndex == VAR_INDEX_INVALID) + || (Buffer == NULL) + || (Size == 0)) + { + ASSERT (VariableInfo != NULL); + ASSERT (VariableInfo->StoreIndex != VAR_INDEX_INVALID); + ASSERT (Buffer != NULL); + ASSERT (Size != 0); + return EFI_INVALID_PARAMETER; + } + + Global = &mVariableModuleGlobal->VariableGlobal; + + VariableCache = (VARIABLE_HEADER *)((UINTN)mNvVariableCache + (UINTN)VariableInfo->StoreIndex); + + ASSERT ( + StrCmp ( + VariableInfo->Header.VariableName, + GetVariableNamePtr (VariableCache, Global->AuthFormat) + ) == 0 + ); + ASSERT ( + CompareGuid ( + VariableInfo->Header.VendorGuid, + GetVendorGuidPtr (VariableCache, Global->AuthFormat) + ) + ); + + // + // Forcibly update part data of flash copy of the variable ... + // + Status = UpdateVariableStore ( + Global, + FALSE, + FALSE, + mVariableModuleGlobal->FvbInstance, + (UINTN)(Global->NonVolatileVariableBase + VariableInfo->StoreIndex + Offset), + Size, + Buffer + ); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // + // ... as well as the local cached copy. + // + CopyMem ((VOID *)((UINTN)VariableCache + Offset), Buffer, Size); + + // + // Sync remote cached copy. + // + CacheInstance = &Global->VariableRuntimeCacheContext.VariableRuntimeNvCache; + if (CacheInstance->Store != NULL) { + Status = SynchronizeRuntimeVariableCache ( + CacheInstance, + (UINTN)VariableInfo->StoreIndex + Offset, + Size + ); + ASSERT_EFI_ERROR (Status); + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c index 9e2d8fe0fe0c..32dd9901b260 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c @@ -1,7 +1,7 @@ /** @file Common variable non-volatile store routines. -Copyright (c) 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -120,6 +120,181 @@ InitEmuNonVolatileVariableStore ( return EFI_SUCCESS; } +/** + + Create a dummy variable used to fill the gap in NV variable storage caused by + the invalid variables found in HMAC verification phase. + + @param[out] Variable Variable buffer. + @param[in] Name Variable Name. + @param[in] Guid Vendor GUID of the variable. + @param[in] Size Whole size of the variable requested. + @param[in] AuthFlag Variable format flag. + +**/ +STATIC +VOID +CreateDummyVariable ( + OUT VARIABLE_HEADER *Variable, + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN UINT32 Size, + IN BOOLEAN AuthFlag + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + ASSERT (Variable != NULL); + + if (Name == NULL) { + Name = L"Dummy"; + } + + if (AuthFlag) { + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable; + + AuthVariable->StartId = VARIABLE_DATA; + AuthVariable->State = VAR_ADDED & VAR_DELETED; + AuthVariable->Attributes = EFI_VARIABLE_NON_VOLATILE; + AuthVariable->NameSize = (UINT32)StrSize (Name); + AuthVariable->DataSize = Size - sizeof (AUTHENTICATED_VARIABLE_HEADER) + - AuthVariable->NameSize; + if (Guid != NULL) { + CopyMem ((VOID *)&AuthVariable->VendorGuid, (VOID *)Guid, sizeof (EFI_GUID)); + } + + CopyMem (GetVariableNamePtr (Variable, AuthFlag), (VOID *)Name, AuthVariable->NameSize); + } else { + Variable->StartId = VARIABLE_DATA; + Variable->State = VAR_ADDED & VAR_DELETED; + Variable->Attributes = EFI_VARIABLE_NON_VOLATILE; + Variable->NameSize = (UINT32)StrSize (Name); + Variable->DataSize = Size - sizeof (VARIABLE_HEADER) - Variable->NameSize; + if (Guid != NULL) { + CopyMem ((VOID *)&Variable->VendorGuid, (VOID *)Guid, sizeof (EFI_GUID)); + } + + CopyMem (GetVariableNamePtr (Variable, AuthFlag), (VOID *)Name, Variable->NameSize); + } +} + +/** + + Init protected variable store. + + @param[in, out] VariableStore Pointer to real protected variable store base. + +**/ +EFI_STATUS +InitProtectedVariableStore ( + IN OUT VARIABLE_STORE_HEADER *VariableStore + ) +{ + EFI_STATUS Status; + PROTECTED_VARIABLE_INFO VarInfo; + UINTN Size; + UINTN Index; + BOOLEAN AuthFlag; + EFI_PHYSICAL_ADDRESS NextVariableStore; + EFI_PHYSICAL_ADDRESS *VarList; + UINTN NumVars; + UINTN CurrVar; + + SetMem ( + (UINT8 *)VariableStore + sizeof (VARIABLE_STORE_HEADER), + VariableStore->Size - sizeof (VARIABLE_STORE_HEADER), + 0xFF + ); + Index = sizeof (VARIABLE_STORE_HEADER); + + VarList = NULL; + NumVars = 0; + ProtectedVariableLibGetSortedList (&VarList, &NumVars); + + // + // Search variable in the order of StoreIndex + // + ZeroMem (&VarInfo, sizeof (VarInfo)); + + for (CurrVar = 0; CurrVar < NumVars; CurrVar++) { + VarInfo.Buffer = NULL; + VarInfo.StoreIndex = VarList[CurrVar]; + if (VarInfo.StoreIndex == VAR_INDEX_INVALID) { + break; + } + + Status = ProtectedVariableLibFind (&VarInfo); + if (EFI_ERROR (Status)) { + break; + } + + ASSERT (VarInfo.Buffer != NULL); + + AuthFlag = VarInfo.Flags.Auth; + if (VarInfo.StoreIndex == VAR_INDEX_INVALID) { + continue; + } else { + ASSERT (VarInfo.StoreIndex == HEADER_ALIGN (VarInfo.StoreIndex)); + ASSERT (VarInfo.StoreIndex < (VariableStore->Size - sizeof (VARIABLE_STORE_HEADER))); + ASSERT ((VariableStore->Size - VarInfo.StoreIndex) > GetVariableHeaderSize (AuthFlag)); + } + + // + // Fill gap caused by invalid variable. + // + if (VarInfo.StoreIndex > Index) { + Size = (UINTN)VarInfo.StoreIndex - Index; + CreateDummyVariable ( + (VARIABLE_HEADER *)((UINT8 *)VariableStore + Index), + NULL, + NULL, + (UINT32)Size, + AuthFlag + ); + Index += Size; + } + + Size = (UINTN)GetNextVariablePtr (VarInfo.Buffer, AuthFlag) + - (UINTN)VarInfo.Buffer; + CopyMem ((UINT8 *)VariableStore + VarInfo.StoreIndex, VarInfo.Buffer, Size); + + Index += Size; + Index = HEADER_ALIGN (Index); + + NextVariableStore = (EFI_PHYSICAL_ADDRESS)((UINTN)VariableStore + VarInfo.StoreIndex + Size); + } + + // + // Search variable in the order of StoreIndex + // + ZeroMem (&VarInfo, sizeof (VarInfo)); + for ( ; CurrVar < NumVars; CurrVar++) { + VarInfo.Buffer = NULL; + VarInfo.StoreIndex = VarList[CurrVar]; + Status = ProtectedVariableLibFind (&VarInfo); + if (EFI_ERROR (Status)) { + break; + } + + ASSERT (VarInfo.Buffer != NULL); + + AuthFlag = VarInfo.Flags.Auth; + if (VarInfo.StoreIndex == VAR_INDEX_INVALID) { + Size = (UINTN)GetNextVariablePtr (VarInfo.Buffer, AuthFlag) + - (UINTN)VarInfo.Buffer; + CopyMem ((VOID *)(UINTN)NextVariableStore, VarInfo.Buffer, Size); + Status = ProtectedVariableLibRefresh (VarInfo.Buffer, 0, NextVariableStore - (UINTN)VariableStore, FALSE); + NextVariableStore = NextVariableStore + Size; + } + } + + if (Status == EFI_UNSUPPORTED) { + return Status; + } + + return EFI_SUCCESS; +} + /** Init real non-volatile variable store. @@ -236,6 +411,16 @@ InitRealNonVolatileVariableStore ( return EFI_VOLUME_CORRUPTED; } + // + // Overwrite the store with verified copy of protected variables, if enabled. + // + Status = InitProtectedVariableStore (VariableStore); + if ((Status != EFI_SUCCESS) && (Status != EFI_UNSUPPORTED)) { + FreePool (NvStorageData); + DEBUG ((DEBUG_ERROR, "Variable integrity might have been compromised\n")); + return Status; + } + mNvFvHeaderCache = FvHeader; *VariableStoreBase = (EFI_PHYSICAL_ADDRESS)(UINTN)VariableStore; @@ -329,7 +514,12 @@ InitNonVolatileVariableStore ( // Parse non-volatile variable data and get last variable offset. // Variable = GetStartPointer (mNvVariableCache); - while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) { + while (IsValidVariableHeader ( + Variable, + GetEndPointer (mNvVariableCache), + mVariableModuleGlobal->VariableGlobal.AuthFormat + )) + { NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat); VariableSize = (UINTN)NextVariable - (UINTN)Variable; if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c index 39060ed405b8..be3f59341c1e 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c @@ -2,11 +2,12 @@ Functions in this module are associated with variable parsing operations and are intended to be usable across variable driver source files. -Copyright (c) 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ +#include "Variable.h" #include "VariableParsing.h" /** @@ -15,6 +16,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent @param[in] Variable Pointer to the Variable Header. @param[in] VariableStoreEnd Pointer to the Variable Store End. + @param[in] AuthFormat TRUE indicates authenticated variables are used. + FALSE indicates authenticated variables are not used. @retval TRUE Variable header is valid. @retval FALSE Variable header is not valid. @@ -23,10 +26,14 @@ SPDX-License-Identifier: BSD-2-Clause-Patent BOOLEAN IsValidVariableHeader ( IN VARIABLE_HEADER *Variable, - IN VARIABLE_HEADER *VariableStoreEnd + IN VARIABLE_HEADER *VariableStoreEnd, + IN BOOLEAN AuthFormat ) { - if ((Variable == NULL) || (Variable >= VariableStoreEnd) || (Variable->StartId != VARIABLE_DATA)) { + if ( (Variable == NULL) + || (((UINTN)Variable + GetVariableHeaderSize (AuthFormat)) >= (UINTN)VariableStoreEnd) + || (Variable->StartId != VARIABLE_DATA)) + { // // Variable is NULL or has reached the end of variable store, // or the StartId is not correct. @@ -341,6 +348,52 @@ GetVariableDataOffset ( return Value; } +/** + Get variable data payload. + + @param[in] Variable Pointer to the Variable Header. + @param[in,out] Data Pointer to buffer used to store the variable data. + @param[in,out] DataSize Size of buffer passed by Data. + Size of data copied into Data buffer. + @param[in] AuthFlag Auth-variable indicator. + + @return EFI_SUCCESS Data was fetched. + @return EFI_INVALID_PARAMETER DataSize is NULL. + @return EFI_BUFFER_TOO_SMALL DataSize is smaller than size of variable data. + +**/ +EFI_STATUS +GetVariableData ( + IN VARIABLE_HEADER *Variable, + IN OUT VOID *Data, + IN OUT UINT32 *DataSize, + IN BOOLEAN AuthFlag + ) +{ + UINT32 Size; + + if (DataSize == NULL) { + ASSERT (DataSize != NULL); + return EFI_INVALID_PARAMETER; + } + + Size = (UINT32)DataSizeOfVariable (Variable, AuthFlag); + if (*DataSize < Size) { + *DataSize = Size; + return EFI_BUFFER_TOO_SMALL; + } + + if (Data == NULL) { + ASSERT (Data != NULL); + return EFI_INVALID_PARAMETER; + } + + CopyMem (Data, GetVariableDataPtr (Variable, AuthFlag), Size); + *DataSize = Size; + + return EFI_SUCCESS; +} + /** This code gets the pointer to the next variable header. @@ -479,7 +532,7 @@ FindVariableEx ( InDeletedVariable = NULL; for ( PtrTrack->CurrPtr = PtrTrack->StartPtr - ; IsValidVariableHeader (PtrTrack->CurrPtr, PtrTrack->EndPtr) + ; IsValidVariableHeader (PtrTrack->CurrPtr, PtrTrack->EndPtr, AuthFormat) ; PtrTrack->CurrPtr = GetNextVariablePtr (PtrTrack->CurrPtr, AuthFormat) ) { @@ -608,7 +661,7 @@ VariableServiceGetNextVariableInternal ( // // Switch to the next variable store if needed // - while (!IsValidVariableHeader (Variable.CurrPtr, Variable.EndPtr)) { + while (!IsValidVariableHeader (Variable.CurrPtr, Variable.EndPtr, AuthFormat)) { // // Find current storage index // @@ -804,3 +857,254 @@ UpdateVariableInfo ( } } } + +/** + + Retrieve details about a variable and return them in VariableInfo->Header. + + If VariableInfo->Buffer is given, this function will calculate its offset + relative to given variable storage via VariableStore; Otherwise, it will try + other internal variable storages or cached copies. It's assumed that, for all + copies of NV variable storage, all variables are stored in the same relative + position. If VariableInfo->Buffer is found in the range of any storage copies, + its offset relative to that storage should be the same in other copies. + + If VariableInfo->Offset is given (non-zero) but not VariableInfo->Buffer, + this function will return the variable memory address inside VariableStore, + if given, via VariableInfo->Address; Otherwise, the address of other storage + copies will be returned, if any. + + For a new variable whose offset has not been determined, a value of -1 as + VariableInfo->Offset should be passed to skip the offset calculation. + + @param[in,out] VariableInfo Pointer to variable information. + + @retval EFI_INVALID_PARAMETER VariableInfo is NULL or both VariableInfo->Address + and VariableInfo->Offset are NULL (0). + @retval EFI_NOT_FOUND If given Address or Offset is out of range of + any given or internal storage copies. + @retval EFI_SUCCESS Variable details are retrieved successfully. + +**/ +EFI_STATUS +EFIAPI +GetVariableInfo ( + IN OUT PROTECTED_VARIABLE_INFO *VariableInfo + ) +{ + VARIABLE_STORE_HEADER *Stores[2]; + UINTN Index; + VARIABLE_HEADER *VariablePtr; + VARIABLE_HEADER *VariableBuffer; + AUTHENTICATED_VARIABLE_HEADER *AuthVariablePtr; + BOOLEAN AuthFlag; + UINTN NameSize; + UINTN DataSize; + UINTN VariableSize; + + if ((VariableInfo == NULL) || ( (VariableInfo->Buffer == NULL) + && (VariableInfo->StoreIndex == VAR_INDEX_INVALID))) + { + ASSERT (VariableInfo != NULL); + ASSERT (VariableInfo->Buffer != NULL || VariableInfo->StoreIndex != VAR_INDEX_INVALID); + return EFI_INVALID_PARAMETER; + } + + Stores[0] = mNvVariableCache; + Stores[1] = (mVariableModuleGlobal != NULL) + ? (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase + : NULL; + + VariableBuffer = VariableInfo->Buffer; + VariablePtr = NULL; + if (VariableInfo->StoreIndex != VAR_INDEX_INVALID) { + for (Index = 0; Index < ARRAY_SIZE (Stores); ++Index) { + if (Stores[Index] == NULL) { + continue; + } + + if ((UINTN)VariableInfo->StoreIndex + < ((UINTN)GetEndPointer (Stores[Index]) - (UINTN)Stores[Index])) + { + VariablePtr = (VARIABLE_HEADER *)((UINTN)Stores[Index] + (UINTN)VariableInfo->StoreIndex); + VariableInfo->Buffer = VariablePtr; + break; + } + } + } else { + VariablePtr = VariableInfo->Buffer; + } + + if (VariablePtr == NULL) { + return EFI_NOT_FOUND; + } + + AuthFlag = VariableInfo->Flags.Auth; + ASSERT (AuthFlag == TRUE || AuthFlag == FALSE); + + // + // Make a copy of the whole variable if a buffer is passed in. + // + if ((VariableBuffer != NULL) && (VariableBuffer != VariablePtr)) { + VariableSize = (UINTN)GetNextVariablePtr (VariablePtr, AuthFlag) + - (UINTN)VariablePtr; + CopyMem (VariableBuffer, VariablePtr, VariableSize); + } + + // + // AuthVariable header + // + if (AuthFlag) { + AuthVariablePtr = (AUTHENTICATED_VARIABLE_HEADER *)VariablePtr; + + VariableInfo->Header.State = AuthVariablePtr->State; + VariableInfo->Header.Attributes = AuthVariablePtr->Attributes; + VariableInfo->Header.PubKeyIndex = AuthVariablePtr->PubKeyIndex; + VariableInfo->Header.MonotonicCount = ReadUnaligned64 ( + &(AuthVariablePtr->MonotonicCount) + ); + if (VariableInfo->Header.TimeStamp != NULL) { + CopyMem ( + VariableInfo->Header.TimeStamp, + &AuthVariablePtr->TimeStamp, + sizeof (EFI_TIME) + ); + } else if (VariableBuffer != NULL) { + AuthVariablePtr = (AUTHENTICATED_VARIABLE_HEADER *)VariableBuffer; + VariableInfo->Header.TimeStamp = &AuthVariablePtr->TimeStamp; + } + } else { + VariableInfo->Header.State = VariablePtr->State; + VariableInfo->Header.Attributes = VariablePtr->Attributes; + VariableInfo->Header.PubKeyIndex = 0; + VariableInfo->Header.MonotonicCount = 0; + VariableInfo->Header.TimeStamp = NULL; + } + + // + // VendorGuid + // + if (VariableInfo->Header.VendorGuid != NULL) { + CopyGuid ( + VariableInfo->Header.VendorGuid, + GetVendorGuidPtr (VariablePtr, AuthFlag) + ); + } else { + VariableInfo->Header.VendorGuid = GetVendorGuidPtr (VariablePtr, AuthFlag); + } + + // + // VariableName + // + NameSize = NameSizeOfVariable (VariablePtr, AuthFlag); + if ( (VariableInfo->Header.VariableName != NULL) + && (VariableInfo->Header.NameSize >= NameSize)) + { + CopyMem ( + VariableInfo->Header.VariableName, + GetVariableNamePtr (VariablePtr, AuthFlag), + NameSize + ); + } else if (VariableInfo->Header.VariableName != NULL) { + return EFI_BUFFER_TOO_SMALL; + } else { + VariableInfo->Header.VariableName = GetVariableNamePtr (VariablePtr, AuthFlag); + } + + // + // Data + // + DataSize = DataSizeOfVariable (VariablePtr, AuthFlag); + if ( (VariableInfo->Header.Data != NULL) + && (VariableInfo->Header.DataSize >= DataSize)) + { + CopyMem ( + VariableInfo->Header.Data, + GetVariableDataPtr (VariablePtr, AuthFlag), + NameSize + ); + } else if (VariableInfo->Header.Data != NULL) { + return EFI_BUFFER_TOO_SMALL; + } else { + VariableInfo->Header.Data = GetVariableDataPtr (VariablePtr, AuthFlag); + } + + // + // Update size information about name & data. + // + VariableInfo->Header.NameSize = NameSize; + VariableInfo->Header.DataSize = DataSize; + + return EFI_SUCCESS; +} + +/** + + Retrieve details of the variable next to given variable within VariableStore. + + If VariableInfo->StoreIndex is invalid, the first one in VariableStore is returned. + + @param[in,out] VariableInfo Pointer to variable information. + + @retval EFI_INVALID_PARAMETER VariableInfo or VariableStore is NULL. + @retval EFI_NOT_FOUND If the end of VariableStore is reached. + @retval EFI_SUCCESS The next variable is retrieved successfully. + +**/ +EFI_STATUS +EFIAPI +GetNextVariableInfo ( + IN OUT PROTECTED_VARIABLE_INFO *VariableInfo + ) +{ + VARIABLE_STORE_HEADER *VarStore; + VARIABLE_HEADER *VariablePtr; + VARIABLE_HEADER *VariableStart; + VARIABLE_HEADER *VariableEnd; + BOOLEAN AuthFlag; + + if (VariableInfo == NULL) { + ASSERT (VariableInfo != NULL); + return EFI_INVALID_PARAMETER; + } + + if (mNvVariableCache != NULL) { + VarStore = mNvVariableCache; + } else if (mVariableModuleGlobal != NULL) { + VarStore = (VARIABLE_STORE_HEADER *)(UINTN) + mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase; + } else { + return EFI_NOT_FOUND; + } + + VariableStart = GetStartPointer (VarStore); + VariableEnd = GetEndPointer (VarStore); + + if ((VariableInfo->Flags.Auth != TRUE) && (VariableInfo->Flags.Auth != FALSE)) { + VariableInfo->Flags.Auth = CompareGuid ( + &VarStore->Signature, + &gEfiAuthenticatedVariableGuid + ); + } + + AuthFlag = VariableInfo->Flags.Auth; + + if (VariableInfo->StoreIndex == VAR_INDEX_INVALID) { + VariablePtr = VariableStart; + } else { + VariablePtr = (VARIABLE_HEADER *) + ((UINTN)VarStore + (UINTN)VariableInfo->StoreIndex); + if (VariablePtr >= VariableEnd) { + return EFI_NOT_FOUND; + } + + VariablePtr = GetNextVariablePtr (VariablePtr, AuthFlag); + } + + if (!IsValidVariableHeader (VariablePtr, VariableEnd, AuthFlag)) { + return EFI_NOT_FOUND; + } + + VariableInfo->StoreIndex = (UINTN)VariablePtr - (UINTN)VarStore; + return GetVariableInfo (VariableInfo); +} diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c index 5253c328dcd9..f7bac0227577 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c @@ -14,7 +14,7 @@ VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(), SmmVariableGetStatistics() should also do validation based on its own knowledge. -Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.
Copyright (c) 2018, Linaro, Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -1045,6 +1045,13 @@ VariableWriteServiceInitializeSmm ( { EFI_STATUS Status; + Status = ProtectedVariableLibWriteInit (); + if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) { + DEBUG ((DEBUG_ERROR, "Variable protection service: write-init failed. Status = %r\n", Status)); + ASSERT_EFI_ERROR (Status); + return; + } + Status = VariableWriteServiceInitialize (); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status)); @@ -1152,10 +1159,32 @@ MmVariableServiceInitialize ( VOID ) { - EFI_STATUS Status; - EFI_HANDLE VariableHandle; - VOID *SmmFtwRegistration; - VOID *SmmEndOfDxeRegistration; + EFI_STATUS Status; + EFI_HANDLE VariableHandle; + VOID *SmmFtwRegistration; + VOID *SmmEndOfDxeRegistration; + PROTECTED_VARIABLE_CONTEXT_IN ContextIn; + + // + // Initialize protected variable service, if enabled. + // + ContextIn.StructSize = sizeof (ContextIn); + ContextIn.StructVersion = PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION; + + ContextIn.FindVariableSmm = NULL; + ContextIn.GetVariableInfo = GetVariableInfo; + ContextIn.GetNextVariableInfo = GetNextVariableInfo; + ContextIn.UpdateVariableStore = VariableExLibUpdateNvVariable; + ContextIn.UpdateVariable = VariableExLibUpdateVariable; + + ContextIn.MaxVariableSize = (UINT32)GetMaxVariableSize (); + ContextIn.VariableServiceUser = FromSmmModule; + + Status = ProtectedVariableLibInitialize (&ContextIn); + if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) { + ASSERT_EFI_ERROR (Status); + return Status; + } // // Variable initialize. diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c index 4aaeb5ba8806..b88f75370ad8 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -13,7 +13,7 @@ InitCommunicateBuffer() is really function to check the variable data size. -Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -41,6 +41,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include "PrivilegePolymorphic.h" +#include "Variable.h" #include "VariableParsing.h" EFI_HANDLE mHandle = NULL; @@ -53,6 +54,8 @@ VARIABLE_INFO_ENTRY *mVariableInfo = NULL; VARIABLE_STORE_HEADER *mVariableRuntimeHobCacheBuffer = NULL; VARIABLE_STORE_HEADER *mVariableRuntimeNvCacheBuffer = NULL; VARIABLE_STORE_HEADER *mVariableRuntimeVolatileCacheBuffer = NULL; +VARIABLE_STORE_HEADER *mNvVariableCache = NULL; +VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal = NULL; UINTN mVariableBufferSize; UINTN mVariableRuntimeHobCacheBufferSize; UINTN mVariableRuntimeNvCacheBufferSize; @@ -616,7 +619,6 @@ FindVariableInRuntimeCache ( ) { EFI_STATUS Status; - UINTN TempDataSize; VARIABLE_POINTER_TRACK RtPtrTrack; VARIABLE_STORE_TYPE StoreType; VARIABLE_STORE_HEADER *VariableStoreList[VariableStoreTypeMax]; @@ -669,31 +671,23 @@ FindVariableInRuntimeCache ( // // Get data size // - TempDataSize = DataSizeOfVariable (RtPtrTrack.CurrPtr, mVariableAuthFormat); - ASSERT (TempDataSize != 0); - - if (*DataSize >= TempDataSize) { - if (Data == NULL) { - Status = EFI_INVALID_PARAMETER; - goto Done; - } - - CopyMem (Data, GetVariableDataPtr (RtPtrTrack.CurrPtr, mVariableAuthFormat), TempDataSize); - *DataSize = TempDataSize; - - UpdateVariableInfo (VariableName, VendorGuid, RtPtrTrack.Volatile, TRUE, FALSE, FALSE, TRUE, &mVariableInfo); - - Status = EFI_SUCCESS; - goto Done; - } else { - *DataSize = TempDataSize; - Status = EFI_BUFFER_TOO_SMALL; - goto Done; + if (!RtPtrTrack.Volatile) { + // + // Currently only non-volatile variable needs protection. + // + Status = ProtectedVariableLibGetByBuffer (RtPtrTrack.CurrPtr, Data, (UINT32 *)DataSize, mVariableAuthFormat); + } + + if (RtPtrTrack.Volatile || (Status == EFI_UNSUPPORTED)) { + Status = GetVariableData (RtPtrTrack.CurrPtr, Data, (UINT32 *)DataSize, mVariableAuthFormat); + } + + if (!EFI_ERROR (Status)) { + UpdateVariableInfo (VariableName, VendorGuid, RtPtrTrack.Volatile, TRUE, FALSE, FALSE, FALSE, &mVariableInfo); } } } -Done: if ((Status == EFI_SUCCESS) || (Status == EFI_BUFFER_TOO_SMALL)) { if ((Attributes != NULL) && (RtPtrTrack.CurrPtr != NULL)) { *Attributes = RtPtrTrack.CurrPtr->Attributes; @@ -724,6 +718,7 @@ Done: **/ EFI_STATUS +EFIAPI FindVariableInSmm ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, @@ -1633,7 +1628,8 @@ SmmVariableReady ( IN VOID *Context ) { - EFI_STATUS Status; + EFI_STATUS Status; + PROTECTED_VARIABLE_CONTEXT_IN ContextIn; Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **)&mSmmVariable); if (EFI_ERROR (Status)) { @@ -1731,6 +1727,28 @@ SmmVariableReady ( ); ASSERT_EFI_ERROR (Status); + ContextIn.StructSize = sizeof (ContextIn); + ContextIn.StructVersion = PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION; + + ContextIn.FindVariableSmm = FindVariableInSmm; + ContextIn.GetVariableInfo = GetVariableInfo; + ContextIn.GetNextVariableInfo = GetNextVariableInfo; + ContextIn.VariableServiceUser = FromRuntimeModule; + ContextIn.MaxVariableSize = 0; + ContextIn.UpdateVariableStore = NULL; + ContextIn.UpdateVariable = NULL; + + Status = ProtectedVariableLibInitialize (&ContextIn); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "%a: %d ProtectedVariableLibInitialize() return status: %r\n", + __FUNCTION__, + __LINE__, + Status + )); + } + gBS->CloseEvent (Event); } -- 2.35.1.windows.2