From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by mx.groups.io with SMTP id smtpd.web08.13430.1651255518273693340 for ; Fri, 29 Apr 2022 11:05:20 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="unable to parse pub key" header.i=@intel.com header.s=intel header.b=SsVTuNZ4; spf=pass (domain: intel.com, ip: 134.134.136.126, 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=1651255520; x=1682791520; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=SYFFbyYGRtNkq69bbEwbA/l8rO1smcXp0y4QQ21WrDo=; b=SsVTuNZ4B8CYLE37HibTLcMgIW8FFcZ7esTtZH6ceKYbZtfHK4O05QJm gVCSzeMzx5g911Cse7H92F8CSOPf7G+J+HOUQYCDBsDfnOkN7LB1J2MXl Cw7AdAgs7Ux7AVrJCdMFjd7m/fCz9GR1qBeS5EnR9j0NTcfsMFGBcQjTV li8VVCBM7c4b0y2o/dLFUH0OPAb6k+lmYMFkxWosjHK1b2CMVosp5cQjv k/2qH1XyuGP2yHXszIBe65EiJWFGjuevK5+fGEcexZPgyQ8Ie5fLS0LWR mRsH/hJP7Y6SrnCjBWqu4GwLGOaujO7VFc8l8ABQaCvoni4Zbo41LUpRv A==; X-IronPort-AV: E=McAfee;i="6400,9594,10332"; a="248659205" X-IronPort-AV: E=Sophos;i="5.91,186,1647327600"; d="scan'208";a="248659205" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Apr 2022 11:05:17 -0700 X-IronPort-AV: E=Sophos;i="5.91,186,1647327600"; d="scan'208";a="514955685" Received: from jvang-mobl.amr.corp.intel.com ([10.212.198.238]) by orsmga003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 29 Apr 2022 11:05:16 -0700 From: "Judah Vang" To: devel@edk2.groups.io Cc: Jian J Wang , Liming Gao , Nishant C Mistry Subject: [Patch v2 09/28] MdeModulePkg: Add support for Protected Variables Date: Fri, 29 Apr 2022 11:04:11 -0700 Message-Id: <20220429180430.3292-10-judah.vang@intel.com> X-Mailer: git-send-email 2.35.1.windows.2 In-Reply-To: <20220429180430.3292-1-judah.vang@intel.com> References: <20220429180430.3292-1-judah.vang@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594 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: 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 | 4 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf | 3 +- MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h | 126 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h | 91 +- MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c | 349 +++- MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c | 2139 +++++++++++--------- 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 | 320 ++- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c | 2 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c | 39 +- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c | 41 +- 15 files changed, 2454 insertions(+), 1053 deletions(-) diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf index c9434df631ee..c0b90e6ca066 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 # @@ -73,6 +73,7 @@ [LibraryClasses] VarCheckLib VariablePolicyLib VariablePolicyHelperLib + ProtectedVariableLib [Protocols] gEfiFirmwareVolumeBlockProtocolGuid ## CONSUMES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf index eaa97a01c6e5..6f9f027fbb0f 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 # @@ -82,6 +82,7 @@ [LibraryClasses] UefiBootServicesTableLib VariablePolicyLib VariablePolicyHelperLib + ProtectedVariableLib [Protocols] gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf index a0d8b2267e92..5d2fc78ea917 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,8 @@ [LibraryClasses] SafeIntLib PcdLib MmUnblockMemoryLib + ProtectedVariableLib + IoLib [Protocols] gEfiVariableWriteArchProtocolGuid ## PRODUCES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf index d8c4f77e7f1f..6ea7b8d4293e 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 @@ -78,6 +78,7 @@ [LibraryClasses] VarCheckLib VariablePolicyLib VariablePolicyHelperLib + ProtectedVariableLib [Protocols] gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h index 31e408976a35..97b4f9c906ff 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 **/ @@ -31,6 +31,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include #include #include @@ -823,4 +824,127 @@ 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 +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..3a4e8019aaf9 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,67 @@ UpdateVariableInfo ( IN OUT VARIABLE_INFO_ENTRY **VariableInfo ); +/** + + Retrieve details of the variable next to given variable within VariableStore. + + If VarInfo->Address is NULL, the first one in VariableStore is returned. + + VariableStart and/or VariableEnd can be given optionally for the situation + in which the valid storage space is smaller than the VariableStore->Size. + This usually happens when PEI variable services make a compact variable + cache to save memory, which cannot make use VariableStore->Size to determine + the correct variable storage range. + + @param VariableStore Pointer to a variable storage. It's optional. + @param VariableStart Start point of valid range in VariableStore. + @param VariableEnd End point of valid range in VariableStore. + @param VariableInfo Pointer to variable information. + + @retval EFI_INVALID_PARAMETER VariableInfo or VariableStore is NULL. + @retval EFI_NOT_FOUND If the end of VariableStore is reached. + @retval EFI_SUCCESS The next variable is retrieved successfully. + +**/ +EFI_STATUS +EFIAPI +GetNextVariableInfo ( + IN OUT PROTECTED_VARIABLE_INFO *VarInfo + ); + +/** + + Retrieve details about a variable and return them in VariableInfo->Header. + + If VariableInfo->Address is given, this function will calculate its offset + relative to given variable storage via VariableStore; Otherwise, it will try + other internal variable storages or cached copies. It's assumed that, for all + copies of NV variable storage, all variables are stored in the same relative + position. If VariableInfo->Address is found in the range of any storage copies, + its offset relative to that storage should be the same in other copies. + + If VariableInfo->Offset is given (non-zero) but not VariableInfo->Address, + this function will return the variable memory address inside VariableStore, + if given, via VariableInfo->Address; Otherwise, the address of other storage + copies will be returned, if any. + + For a new variable whose offset has not been determined, a value of -1 as + VariableInfo->Offset should be passed to skip the offset calculation. + + @param VariableStore Pointer to a variable storage. It's optional. + @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 *VarInfo + ); + #endif 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..6e86099eb72b 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,381 @@ 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; - } - } + UpdatingVariable = Variable->CurrPtr; - 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 +2525,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 +2569,6 @@ VariableServiceGetVariable ( { EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; - UINTN VarDataSize; if ((VariableName == NULL) || (VendorGuid == NULL) || (DataSize == NULL)) { return EFI_INVALID_PARAMETER; @@ -2458,28 +2586,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 +2986,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 +3148,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 +3446,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 +3656,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 +3673,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 +3685,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 +3739,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 +3905,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 03fec3048dc4..52bf29ec4c5c 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 @@ -534,6 +535,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 5e9d40b67ac2..e8b8f0b7f705 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 ((UINT8 *)&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. @@ -230,6 +405,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; @@ -323,7 +508,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..000e4b546888 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,260 @@ UpdateVariableInfo ( } } } + +/** + + Retrieve details about a variable and return them in VariableInfo->Header. + + If VariableInfo->Address is given, this function will calculate its offset + relative to given variable storage via VariableStore; Otherwise, it will try + other internal variable storages or cached copies. It's assumed that, for all + copies of NV variable storage, all variables are stored in the same relative + position. If VariableInfo->Address is found in the range of any storage copies, + its offset relative to that storage should be the same in other copies. + + If VariableInfo->Offset is given (non-zero) but not VariableInfo->Address, + this function will return the variable memory address inside VariableStore, + if given, via VariableInfo->Address; Otherwise, the address of other storage + copies will be returned, if any. + + For a new variable whose offset has not been determined, a value of -1 as + VariableInfo->Offset should be passed to skip the offset calculation. + + @param[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 VarInfo->Address is NULL, the first one in VariableStore is returned. + + VariableStart and/or VariableEnd can be given optionally for the situation + in which the valid storage space is smaller than the VariableStore->Size. + This usually happens when PEI variable services make a compact variable + cache to save memory, which cannot make use VariableStore->Size to determine + the correct variable storage range. + + @param[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/VariableRuntimeCache.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c index 9bb30bc1e804..dc319feee727 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c @@ -7,7 +7,7 @@ This external input must be validated carefully to avoid security issue like buffer overflow, integer overflow. -Copyright (c) 2019, Intel Corporation. All rights reserved.
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c index 517cae7b00f8..c2b689f6202d 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)); @@ -1143,10 +1150,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..8fb8679671ad 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 @@ -36,11 +36,13 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include #include #include "PrivilegePolymorphic.h" +#include "Variable.h" #include "VariableParsing.h" EFI_HANDLE mHandle = NULL; @@ -53,6 +55,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 +620,6 @@ FindVariableInRuntimeCache ( ) { EFI_STATUS Status; - UINTN TempDataSize; VARIABLE_POINTER_TRACK RtPtrTrack; VARIABLE_STORE_TYPE StoreType; VARIABLE_STORE_HEADER *VariableStoreList[VariableStoreTypeMax]; @@ -669,31 +672,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; -- 2.35.1.windows.2