diff --git a/MdeModulePkg/Include/Guid/SmmVariableCommon.h b/MdeModulePkg/Include/Guid/SmmVariableCommon.h index c527a59891..ceef44dfd2 100644 --- a/MdeModulePkg/Include/Guid/SmmVariableCommon.h +++ b/MdeModulePkg/Include/Guid/SmmVariableCommon.h @@ -1,7 +1,7 @@ /** @file The file defined some common structures used for communicating between SMM variable module and SMM variable wrapper module. -Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.
+Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -9,6 +9,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #ifndef _SMM_VARIABLE_COMMON_H_ #define _SMM_VARIABLE_COMMON_H_ +#include #include #define EFI_SMM_VARIABLE_WRITE_GUID \ @@ -66,6 +67,16 @@ typedef struct { #define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET 10 #define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE 11 +// +// The payload for this function is SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT +// +#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12 + +#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE 13 +// +// The payload for this function is SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO +// +#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO 14 /// /// Size of SMM communicate header, without including the payload. @@ -120,4 +131,20 @@ typedef struct { UINTN VariablePayloadSize; } SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE; +typedef struct { + BOOLEAN *ReadLock; + BOOLEAN *PendingUpdate; + BOOLEAN *HobFlushComplete; + VARIABLE_STORE_HEADER *RuntimeHobCache; + VARIABLE_STORE_HEADER *RuntimeNvCache; + VARIABLE_STORE_HEADER *RuntimeVolatileCache; +} SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT; + +typedef struct { + UINTN TotalHobStorageSize; + UINTN TotalNvStorageSize; + UINTN TotalVolatileStorageSize; + BOOLEAN AuthenticatedVariableUsage; +} SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO; + #endif // _SMM_VARIABLE_COMMON_H_ diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index d6bac974da..3ef8fe7644 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -641,6 +641,18 @@ # @Prompt Enable Device Path From Text support. gEfiMdeModulePkgTokenSpaceGuid.PcdDevicePathSupportDevicePathFromText|TRUE|BOOLEAN|0x00010038 + ## Indicates if the UEFI variable runtime cache should be enabled. + # This setting only applies if SMM variables are enabled. When enabled, all variable + # data for Runtime Service GetVariable () and GetNextVariableName () calls is retrieved + # from a runtime data buffer referred to as the "runtime cache". An SMI is not triggered + # at all for these requests. Variables writes still trigger an SMI. This can greatly + # reduce overall system SMM usage as most boots tend to issue far more variable reads + # than writes.

+ # TRUE - The UEFI variable runtime cache is enabled.
+ # FALSE - The UEFI variable runtime cache is disabled.
+ # @Prompt Enable the UEFI variable runtime cache. + gEfiMdeModulePkgTokenSpaceGuid.PcdEnableVariableRuntimeCache|FALSE|BOOLEAN|0x00010039 + ## Indicates if the statistics about variable usage will be collected. This information is # stored as a vendor configuration table into the EFI system table. # Set this PCD to TRUE to use VariableInfo application in MdeModulePkg\Application directory to get diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c index 0bd2f22e1a..29d6aca993 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c @@ -25,6 +25,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "Variable.h" #include "VariableNonVolatile.h" #include "VariableParsing.h" +#include "VariableRuntimeCache.h" VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; @@ -332,6 +333,12 @@ RecordVarErrorFlag ( // Update the data in NV cache. // *VarErrFlag = TempFlag; + Status = SynchronizeRuntimeVariableCache ( + &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache, + (UINTN) VarErrFlag - (UINTN) mNvVariableCache + (UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, + sizeof (TempFlag) + ); + ASSERT_EFI_ERROR (Status); } } } @@ -766,12 +773,24 @@ Reclaim ( Done: if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) { + Status = SynchronizeRuntimeVariableCache ( + &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache, + 0, + VariableStoreHeader->Size + ); + ASSERT_EFI_ERROR (Status); 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); + CopyMem (mNvVariableCache, (UINT8 *) (UINTN) VariableBase, VariableStoreHeader->Size); + Status = SynchronizeRuntimeVariableCache ( + &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache, + 0, + VariableStoreHeader->Size + ); + ASSERT_EFI_ERROR (Status); } return Status; @@ -1614,6 +1633,7 @@ UpdateVariable ( VARIABLE_POINTER_TRACK *Variable; VARIABLE_POINTER_TRACK NvVariable; VARIABLE_STORE_HEADER *VariableStoreHeader; + VARIABLE_RUNTIME_CACHE *VolatileCacheInstance; UINT8 *BufferForMerge; UINTN MergedBufSize; BOOLEAN DataReady; @@ -2275,6 +2295,23 @@ UpdateVariable ( } Done: + if (!EFI_ERROR (Status)) { + if (Variable->Volatile) { + VolatileCacheInstance = &(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache); + } else { + VolatileCacheInstance = &(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache); + } + + if (VolatileCacheInstance->Store != NULL) { + Status = SynchronizeRuntimeVariableCache ( + VolatileCacheInstance, + 0, + VolatileCacheInstance->Store->Size + ); + ASSERT_EFI_ERROR (Status); + } + } + return Status; } @@ -3200,6 +3237,14 @@ FlushHobVariableToFlash ( ErrorFlag = TRUE; } } + if (mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeHobCache.Store != NULL) { + Status = SynchronizeRuntimeVariableCache ( + &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeHobCache, + 0, + mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeHobCache.Store->Size + ); + ASSERT_EFI_ERROR (Status); + } if (ErrorFlag) { // // We still have HOB variable(s) not flushed in flash. @@ -3210,6 +3255,9 @@ FlushHobVariableToFlash ( // All HOB variables have been flushed in flash. // DEBUG ((EFI_D_INFO, "Variable driver: all HOB variables have been flushed in flash.\n")); + if (mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.HobFlushComplete != NULL) { + *(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.HobFlushComplete) = TRUE; + } if (!AtRuntime ()) { FreePool ((VOID *) VariableStoreHeader); } diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h index fb574b2e32..0b2bb6ae66 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h @@ -64,6 +64,21 @@ typedef enum { VariableStoreTypeMax } VARIABLE_STORE_TYPE; +typedef struct { + UINT32 PendingUpdateOffset; + UINT32 PendingUpdateLength; + VARIABLE_STORE_HEADER *Store; +} VARIABLE_RUNTIME_CACHE; + +typedef struct { + BOOLEAN *ReadLock; + BOOLEAN *PendingUpdate; + BOOLEAN *HobFlushComplete; + VARIABLE_RUNTIME_CACHE VariableRuntimeHobCache; + VARIABLE_RUNTIME_CACHE VariableRuntimeNvCache; + VARIABLE_RUNTIME_CACHE VariableRuntimeVolatileCache; +} VARIABLE_RUNTIME_CACHE_CONTEXT; + typedef struct { VARIABLE_HEADER *CurrPtr; // @@ -79,14 +94,15 @@ typedef struct { } VARIABLE_POINTER_TRACK; typedef struct { - EFI_PHYSICAL_ADDRESS HobVariableBase; - EFI_PHYSICAL_ADDRESS VolatileVariableBase; - EFI_PHYSICAL_ADDRESS NonVolatileVariableBase; - EFI_LOCK VariableServicesLock; - UINT32 ReentrantState; - BOOLEAN AuthFormat; - BOOLEAN AuthSupport; - BOOLEAN EmuNvMode; + EFI_PHYSICAL_ADDRESS HobVariableBase; + EFI_PHYSICAL_ADDRESS VolatileVariableBase; + EFI_PHYSICAL_ADDRESS NonVolatileVariableBase; + VARIABLE_RUNTIME_CACHE_CONTEXT VariableRuntimeCacheContext; + EFI_LOCK VariableServicesLock; + UINT32 ReentrantState; + BOOLEAN AuthFormat; + BOOLEAN AuthSupport; + BOOLEAN EmuNvMode; } VARIABLE_GLOBAL; typedef struct { diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c new file mode 100644 index 0000000000..bc93cc07d2 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c @@ -0,0 +1,153 @@ +/** @file + Functions related to managing the UEFI variable runtime cache. This file should only include functions + used by the SMM UEFI variable driver. + + Caution: This module requires additional review when modified. + This driver will have external input - variable data. They may be input in SMM mode. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + +Copyright (c) 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "VariableParsing.h" +#include "VariableRuntimeCache.h" + +extern VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; +extern VARIABLE_STORE_HEADER *mNvVariableCache; + +/** + Copies any pending updates to runtime variable caches. + + @retval EFI_UNSUPPORTED The volatile store to be updated is not initialized properly. + @retval EFI_SUCCESS The volatile store was updated successfully. + +**/ +EFI_STATUS +FlushPendingRuntimeVariableCacheUpdates ( + VOID + ) +{ + VARIABLE_RUNTIME_CACHE_CONTEXT *VariableRuntimeCacheContext; + + VariableRuntimeCacheContext = &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext; + + if (VariableRuntimeCacheContext->VariableRuntimeNvCache.Store == NULL || + VariableRuntimeCacheContext->VariableRuntimeVolatileCache.Store == NULL || + VariableRuntimeCacheContext->PendingUpdate == NULL) { + return EFI_UNSUPPORTED; + } + + if (*(VariableRuntimeCacheContext->PendingUpdate)) { + if (VariableRuntimeCacheContext->VariableRuntimeHobCache.Store != NULL && + mVariableModuleGlobal->VariableGlobal.HobVariableBase > 0) { + CopyMem ( + (VOID *) ( + ((UINT8 *) (UINTN) VariableRuntimeCacheContext->VariableRuntimeHobCache.Store) + + VariableRuntimeCacheContext->VariableRuntimeHobCache.PendingUpdateOffset + ), + (VOID *) ( + ((UINT8 *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase) + + VariableRuntimeCacheContext->VariableRuntimeHobCache.PendingUpdateOffset + ), + VariableRuntimeCacheContext->VariableRuntimeHobCache.PendingUpdateLength + ); + VariableRuntimeCacheContext->VariableRuntimeHobCache.PendingUpdateLength = 0; + VariableRuntimeCacheContext->VariableRuntimeHobCache.PendingUpdateOffset = 0; + } + + CopyMem ( + (VOID *) ( + ((UINT8 *) (UINTN) VariableRuntimeCacheContext->VariableRuntimeNvCache.Store) + + VariableRuntimeCacheContext->VariableRuntimeNvCache.PendingUpdateOffset + ), + (VOID *) ( + ((UINT8 *) (UINTN) mNvVariableCache) + + VariableRuntimeCacheContext->VariableRuntimeNvCache.PendingUpdateOffset + ), + VariableRuntimeCacheContext->VariableRuntimeNvCache.PendingUpdateLength + ); + VariableRuntimeCacheContext->VariableRuntimeNvCache.PendingUpdateLength = 0; + VariableRuntimeCacheContext->VariableRuntimeNvCache.PendingUpdateOffset = 0; + + CopyMem ( + (VOID *) ( + ((UINT8 *) (UINTN) VariableRuntimeCacheContext->VariableRuntimeVolatileCache.Store) + + VariableRuntimeCacheContext->VariableRuntimeVolatileCache.PendingUpdateOffset + ), + (VOID *) ( + ((UINT8 *) (UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase) + + VariableRuntimeCacheContext->VariableRuntimeVolatileCache.PendingUpdateOffset + ), + VariableRuntimeCacheContext->VariableRuntimeVolatileCache.PendingUpdateLength + ); + VariableRuntimeCacheContext->VariableRuntimeVolatileCache.PendingUpdateLength = 0; + VariableRuntimeCacheContext->VariableRuntimeVolatileCache.PendingUpdateOffset = 0; + *(VariableRuntimeCacheContext->PendingUpdate) = FALSE; + } + + return EFI_SUCCESS; +} + +/** + Synchronizes the runtime variable caches with all pending updates outside runtime. + + Ensures all conditions are met to maintain coherency for runtime cache updates. This function will attempt + to write the given update (and any other pending updates) if the ReadLock is available. Otherwise, the + update is added as a pending update for the given variable store and it will be flushed to the runtime cache + at the next opportunity the ReadLock is available. + + @param[in] VariableRuntimeCache Variable runtime cache structure for the runtime cache being synchronized. + @param[in] Offset Offset in bytes to apply the update. + @param[in] Length Length of data in bytes of the update. + + @retval EFI_SUCCESS The update was added as a pending update successfully. If the variable runtime + cache ReadLock was available, the runtime cache was updated successfully. + @retval EFI_UNSUPPORTED The volatile store to be updated is not initialized properly. + +**/ +EFI_STATUS +SynchronizeRuntimeVariableCache ( + IN VARIABLE_RUNTIME_CACHE *VariableRuntimeCache, + IN UINTN Offset, + IN UINTN Length + ) +{ + if (VariableRuntimeCache == NULL) { + return EFI_INVALID_PARAMETER; + } else if (VariableRuntimeCache->Store == NULL) { + // The runtime cache may not be active or allocated yet. + // In either case, return EFI_SUCCESS instead of EFI_NOT_AVAILABLE_YET. + return EFI_SUCCESS; + } + + if (mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.PendingUpdate == NULL || + mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.ReadLock == NULL) { + return EFI_UNSUPPORTED; + } + + if (*(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.PendingUpdate) && + VariableRuntimeCache->PendingUpdateLength > 0) { + VariableRuntimeCache->PendingUpdateLength = + (UINT32) ( + MAX ( + (UINTN) (VariableRuntimeCache->PendingUpdateOffset + VariableRuntimeCache->PendingUpdateLength), + Offset + Length + ) - MIN ((UINTN) VariableRuntimeCache->PendingUpdateOffset, Offset) + ); + VariableRuntimeCache->PendingUpdateOffset = + (UINT32) MIN ((UINTN) VariableRuntimeCache->PendingUpdateOffset, Offset); + } else { + VariableRuntimeCache->PendingUpdateLength = (UINT32) Length; + VariableRuntimeCache->PendingUpdateOffset = (UINT32) Offset; + } + *(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.PendingUpdate) = TRUE; + + if (*(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.ReadLock) == FALSE) { + return FlushPendingRuntimeVariableCacheUpdates (); + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.h new file mode 100644 index 0000000000..f9804a1d69 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.h @@ -0,0 +1,51 @@ +/** @file + The common variable volatile store routines shared by the DXE_RUNTIME variable + module and the DXE_SMM variable module. + +Copyright (c) 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _VARIABLE_RUNTIME_CACHE_H_ +#define _VARIABLE_RUNTIME_CACHE_H_ + +#include "Variable.h" + +/** + Copies any pending updates to runtime variable caches. + + @retval EFI_UNSUPPORTED The volatile store to be updated is not initialized properly. + @retval EFI_SUCCESS The volatile store was updated successfully. + +**/ +EFI_STATUS +FlushPendingRuntimeVariableCacheUpdates ( + VOID + ); + +/** + Synchronizes the runtime variable caches with all pending updates outside runtime. + + Ensures all conditions are met to maintain coherency for runtime cache updates. This function will attempt + to write the given update (and any other pending updates) if the ReadLock is available. Otherwise, the + update is added as a pending update for the given variable store and it will be flushed to the runtime cache + at the next opportunity the ReadLock is available. + + @param[in] VariableRuntimeCache Variable runtime cache structure for the runtime cache being synchronized. + @param[in] Offset Offset in bytes to apply the update. + @param[in] Length Length of data in bytes of the update. + + @retval EFI_SUCCESS The update was added as a pending update successfully. If the variable runtime + cache ReadLock was available, the runtime cache was updated successfully. + @retval EFI_UNSUPPORTED The volatile store to be updated is not initialized properly. + +**/ +EFI_STATUS +SynchronizeRuntimeVariableCache ( + IN VARIABLE_RUNTIME_CACHE *VariableRuntimeCache, + IN UINTN Offset, + IN UINTN Length + ); + +#endif diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf index 08a5490787..ceea5d1ff9 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf @@ -40,6 +40,8 @@ VariableNonVolatile.h VariableParsing.c VariableParsing.h + VariableRuntimeCache.c + VariableRuntimeCache.h PrivilegePolymorphic.h Measurement.c TcgMorLockDxe.c diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c index 5e24bc4a62..caca5c3241 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c @@ -31,6 +31,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include "Variable.h" #include "VariableParsing.h" +#include "VariableRuntimeCache.h" + +extern VARIABLE_STORE_HEADER *mNvVariableCache; BOOLEAN mAtRuntime = FALSE; UINT8 *mVariableBufferPayload = NULL; @@ -451,25 +454,29 @@ SmmVariableGetStatistics ( EFI_STATUS EFIAPI SmmVariableHandler ( - IN EFI_HANDLE DispatchHandle, - IN CONST VOID *RegisterContext, - IN OUT VOID *CommBuffer, - IN OUT UINTN *CommBufferSize + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize ) { - EFI_STATUS Status; - SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; - SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; - SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName; - SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo; - SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *GetPayloadSize; - VARIABLE_INFO_ENTRY *VariableInfo; - SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; - SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; - UINTN InfoSize; - UINTN NameBufferSize; - UINTN CommBufferPayloadSize; - UINTN TempCommBufferSize; + EFI_STATUS Status; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName; + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo; + SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *GetPayloadSize; + SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT *RuntimeVariableCacheContext; + SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO *GetRuntimeCacheInfo; + SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + VARIABLE_INFO_ENTRY *VariableInfo; + VARIABLE_RUNTIME_CACHE_CONTEXT *VariableCacheContext; + VARIABLE_STORE_HEADER *VariableCache; + UINTN InfoSize; + UINTN NameBufferSize; + UINTN CommBufferPayloadSize; + UINTN TempCommBufferSize; // // If input is invalid, stop processing this SMI @@ -789,6 +796,154 @@ SmmVariableHandler ( ); CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize); break; + case SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT: + if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT)) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: SMM communication buffer size invalid!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + if (mEndOfDxe) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Cannot init context after end of DXE!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + // + // Copy the input communicate buffer payload to the pre-allocated SMM variable payload buffer. + // + CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize); + RuntimeVariableCacheContext = (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT *) mVariableBufferPayload; + + // + // Verify required runtime cache buffers are provided. + // + if (RuntimeVariableCacheContext->RuntimeVolatileCache == NULL || + RuntimeVariableCacheContext->RuntimeNvCache == NULL || + RuntimeVariableCacheContext->PendingUpdate == NULL || + RuntimeVariableCacheContext->ReadLock == NULL || + RuntimeVariableCacheContext->HobFlushComplete == NULL) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Required runtime cache buffer is NULL!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + // + // Verify minimum size requirements for the runtime variable store buffers. + // + if ((RuntimeVariableCacheContext->RuntimeHobCache != NULL && + RuntimeVariableCacheContext->RuntimeHobCache->Size < sizeof (VARIABLE_STORE_HEADER)) || + RuntimeVariableCacheContext->RuntimeVolatileCache->Size < sizeof (VARIABLE_STORE_HEADER) || + RuntimeVariableCacheContext->RuntimeNvCache->Size < sizeof (VARIABLE_STORE_HEADER)) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: A runtime cache buffer size is invalid!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + // + // Verify runtime buffers do not overlap with SMRAM ranges. + // + if (RuntimeVariableCacheContext->RuntimeHobCache != NULL && + !VariableSmmIsBufferOutsideSmmValid ( + (UINTN) RuntimeVariableCacheContext->RuntimeHobCache, + (UINTN) RuntimeVariableCacheContext->RuntimeHobCache->Size)) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime HOB cache buffer in SMRAM or overflow!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + if (!VariableSmmIsBufferOutsideSmmValid ( + (UINTN) RuntimeVariableCacheContext->RuntimeVolatileCache, + (UINTN) RuntimeVariableCacheContext->RuntimeVolatileCache->Size)) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime volatile cache buffer in SMRAM or overflow!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + if (!VariableSmmIsBufferOutsideSmmValid ( + (UINTN) RuntimeVariableCacheContext->RuntimeNvCache, + (UINTN) RuntimeVariableCacheContext->RuntimeNvCache->Size)) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime non-volatile cache buffer in SMRAM or overflow!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + if (!VariableSmmIsBufferOutsideSmmValid ( + (UINTN) RuntimeVariableCacheContext->PendingUpdate, + sizeof (*(RuntimeVariableCacheContext->PendingUpdate)))) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime cache pending update buffer in SMRAM or overflow!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + if (!VariableSmmIsBufferOutsideSmmValid ( + (UINTN) RuntimeVariableCacheContext->ReadLock, + sizeof (*(RuntimeVariableCacheContext->ReadLock)))) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime cache read lock buffer in SMRAM or overflow!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + if (!VariableSmmIsBufferOutsideSmmValid ( + (UINTN) RuntimeVariableCacheContext->HobFlushComplete, + sizeof (*(RuntimeVariableCacheContext->HobFlushComplete)))) { + DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime cache HOB flush complete buffer in SMRAM or overflow!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + VariableCacheContext = &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext; + VariableCacheContext->VariableRuntimeHobCache.Store = RuntimeVariableCacheContext->RuntimeHobCache; + VariableCacheContext->VariableRuntimeVolatileCache.Store = RuntimeVariableCacheContext->RuntimeVolatileCache; + VariableCacheContext->VariableRuntimeNvCache.Store = RuntimeVariableCacheContext->RuntimeNvCache; + VariableCacheContext->PendingUpdate = RuntimeVariableCacheContext->PendingUpdate; + VariableCacheContext->ReadLock = RuntimeVariableCacheContext->ReadLock; + VariableCacheContext->HobFlushComplete = RuntimeVariableCacheContext->HobFlushComplete; + + // Set up the intial pending request since the RT cache needs to be in sync with SMM cache + VariableCacheContext->VariableRuntimeHobCache.PendingUpdateOffset = 0; + VariableCacheContext->VariableRuntimeHobCache.PendingUpdateLength = 0; + if (mVariableModuleGlobal->VariableGlobal.HobVariableBase > 0 && + VariableCacheContext->VariableRuntimeHobCache.Store != NULL) { + VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase; + VariableCacheContext->VariableRuntimeHobCache.PendingUpdateLength = (UINT32) ((UINTN) GetEndPointer (VariableCache) - (UINTN) VariableCache); + CopyGuid (&(VariableCacheContext->VariableRuntimeHobCache.Store->Signature), &(VariableCache->Signature)); + } + VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase; + VariableCacheContext->VariableRuntimeVolatileCache.PendingUpdateOffset = 0; + VariableCacheContext->VariableRuntimeVolatileCache.PendingUpdateLength = (UINT32) ((UINTN) GetEndPointer (VariableCache) - (UINTN) VariableCache); + CopyGuid (&(VariableCacheContext->VariableRuntimeVolatileCache.Store->Signature), &(VariableCache->Signature)); + + VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mNvVariableCache; + VariableCacheContext->VariableRuntimeNvCache.PendingUpdateOffset = 0; + VariableCacheContext->VariableRuntimeNvCache.PendingUpdateLength = (UINT32) ((UINTN) GetEndPointer (VariableCache) - (UINTN) VariableCache); + CopyGuid (&(VariableCacheContext->VariableRuntimeNvCache.Store->Signature), &(VariableCache->Signature)); + + *(VariableCacheContext->PendingUpdate) = TRUE; + *(VariableCacheContext->ReadLock) = FALSE; + *(VariableCacheContext->HobFlushComplete) = FALSE; + + Status = EFI_SUCCESS; + break; + case SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE: + Status = FlushPendingRuntimeVariableCacheUpdates (); + break; + case SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO: + if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO)) { + DEBUG ((DEBUG_ERROR, "GetRuntimeCacheInfo: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + GetRuntimeCacheInfo = (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO *) SmmVariableFunctionHeader->Data; + + if (mVariableModuleGlobal->VariableGlobal.HobVariableBase > 0) { + VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase; + GetRuntimeCacheInfo->TotalHobStorageSize = VariableCache->Size; + } else { + GetRuntimeCacheInfo->TotalHobStorageSize = 0; + } + + VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase; + GetRuntimeCacheInfo->TotalVolatileStorageSize = VariableCache->Size; + VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mNvVariableCache; + GetRuntimeCacheInfo->TotalNvStorageSize = (UINTN) VariableCache->Size; + GetRuntimeCacheInfo->AuthenticatedVariableUsage = mVariableModuleGlobal->VariableGlobal.AuthFormat; + + Status = EFI_SUCCESS; + break; default: Status = EFI_UNSUPPORTED; diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf index 6dc2721b81..bc3033588d 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf @@ -49,6 +49,8 @@ VariableNonVolatile.h VariableParsing.c VariableParsing.h + VariableRuntimeCache.c + VariableRuntimeCache.h VarCheck.c Variable.h PrivilegePolymorphic.h diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c index 0a1888e5ef..3dee05fded 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 - 2017, Intel Corporation. All rights reserved.
+Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -39,6 +39,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include "PrivilegePolymorphic.h" +#include "VariableParsing.h" EFI_HANDLE mHandle = NULL; EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable = NULL; @@ -46,8 +47,19 @@ EFI_EVENT mVirtualAddressChangeEvent = NULL; EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL; UINT8 *mVariableBuffer = NULL; UINT8 *mVariableBufferPhysical = NULL; +VARIABLE_INFO_ENTRY *mVariableInfo = NULL; +VARIABLE_STORE_HEADER *mVariableRuntimeHobCacheBuffer = NULL; +VARIABLE_STORE_HEADER *mVariableRuntimeNvCacheBuffer = NULL; +VARIABLE_STORE_HEADER *mVariableRuntimeVolatileCacheBuffer = NULL; UINTN mVariableBufferSize; +UINTN mVariableRuntimeHobCacheBufferSize; +UINTN mVariableRuntimeNvCacheBufferSize; +UINTN mVariableRuntimeVolatileCacheBufferSize; UINTN mVariableBufferPayloadSize; +BOOLEAN mVariableRuntimeCachePendingUpdate; +BOOLEAN mVariableRuntimeCacheReadLock; +BOOLEAN mVariableAuthFormat; +BOOLEAN mHobFlushComplete; EFI_LOCK mVariableServicesLock; EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock; EDKII_VAR_CHECK_PROTOCOL mVarCheck; @@ -107,6 +119,72 @@ ReleaseLockOnlyAtBootTime ( } } +/** + Return TRUE if ExitBootServices () has been called. + + @retval TRUE If ExitBootServices () has been called. FALSE if ExitBootServices () has not been called. +**/ +BOOLEAN +AtRuntime ( + VOID + ) +{ + return EfiAtRuntime (); +} + +/** + Initialize the variable cache buffer as an empty variable store. + + @param[out] VariableCacheBuffer A pointer to pointer of a cache variable store. + @param[in,out] TotalVariableCacheSize On input, the minimum size needed for the UEFI variable store cache + buffer that is allocated. On output, the actual size of the buffer allocated. + If TotalVariableCacheSize is zero, a buffer will not be allocated and the + function will return with EFI_SUCCESS. + + @retval EFI_SUCCESS The variable cache was allocated and initialized successfully. + @retval EFI_INVALID_PARAMETER A given pointer is NULL or an invalid variable store size was specified. + @retval EFI_OUT_OF_RESOURCES Insufficient resources are available to allocate the variable store cache buffer. + +**/ +EFI_STATUS +InitVariableCache ( + OUT VARIABLE_STORE_HEADER **VariableCacheBuffer, + IN OUT UINTN *TotalVariableCacheSize + ) +{ + VARIABLE_STORE_HEADER *VariableCacheStorePtr; + + if (TotalVariableCacheSize == NULL) { + return EFI_INVALID_PARAMETER; + } + if (*TotalVariableCacheSize == 0) { + return EFI_SUCCESS; + } + if (VariableCacheBuffer == NULL || *TotalVariableCacheSize < sizeof (VARIABLE_STORE_HEADER)) { + return EFI_INVALID_PARAMETER; + } + *TotalVariableCacheSize = ALIGN_VALUE (*TotalVariableCacheSize, sizeof (UINT32)); + + // + // Allocate NV Storage Cache and initialize it to all 1's (like an erased FV) + // + *VariableCacheBuffer = (VARIABLE_STORE_HEADER *) AllocateRuntimePages ( + EFI_SIZE_TO_PAGES (*TotalVariableCacheSize) + ); + if (*VariableCacheBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + VariableCacheStorePtr = *VariableCacheBuffer; + SetMem32 ((VOID *) VariableCacheStorePtr, *TotalVariableCacheSize, (UINT32) 0xFFFFFFFF); + + ZeroMem ((VOID *) VariableCacheStorePtr, sizeof (VARIABLE_STORE_HEADER)); + VariableCacheStorePtr->Size = (UINT32) *TotalVariableCacheSize; + VariableCacheStorePtr->Format = VARIABLE_STORE_FORMATTED; + VariableCacheStorePtr->State = VARIABLE_STORE_HEALTHY; + + return EFI_SUCCESS; +} + /** Initialize the communicate buffer using DataSize and Function. @@ -425,7 +503,56 @@ Done: } /** - This code finds variable in storage blocks (Volatile or Non-Volatile). + Signals SMM to synchronize any pending variable updates with the runtime cache(s). + +**/ +VOID +SyncRuntimeCache ( + VOID + ) +{ + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE. + // + InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE); + + // + // Send data to SMM. + // + SendCommunicateBuffer (0); +} + +/** + Check whether a SMI must be triggered to retrieve pending cache updates. + + If the variable HOB was finished being flushed since the last check for a runtime cache update, this function + will prevent the HOB cache from being used for future runtime cache hits. + +**/ +VOID +CheckForRuntimeCacheSync ( + VOID + ) +{ + if (mVariableRuntimeCachePendingUpdate) { + SyncRuntimeCache (); + } + ASSERT (!mVariableRuntimeCachePendingUpdate); + + // + // The HOB variable data may have finished being flushed in the runtime cache sync update + // + if (mHobFlushComplete && mVariableRuntimeHobCacheBuffer != NULL) { + if (!EfiAtRuntime ()) { + FreePages (mVariableRuntimeHobCacheBuffer, EFI_SIZE_TO_PAGES (mVariableRuntimeHobCacheBufferSize)); + } + mVariableRuntimeHobCacheBuffer = NULL; + } +} + +/** + Finds the given variable in a runtime cache variable store. Caution: This function may receive untrusted input. The data size is external input, so this function will validate it carefully to avoid buffer overflow. @@ -437,20 +564,131 @@ Done: 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_SUCCESS Find the specified variable. - @retval EFI_NOT_FOUND Not found. - @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result. + @retval EFI_NOT_FOUND The specified variable could not be found. **/ EFI_STATUS -EFIAPI -RuntimeServiceGetVariable ( +FindVariableInRuntimeCache ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, OUT UINT32 *Attributes OPTIONAL, IN OUT UINTN *DataSize, - OUT VOID *Data + OUT VOID *Data OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN TempDataSize; + VARIABLE_POINTER_TRACK RtPtrTrack; + VARIABLE_STORE_TYPE StoreType; + VARIABLE_STORE_HEADER *VariableStoreList[VariableStoreTypeMax]; + + Status = EFI_NOT_FOUND; + + if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The UEFI specification restricts Runtime Services callers from invoking the same or certain other Runtime Service + // functions prior to completion and return from a previous Runtime Service call. These restrictions prevent + // a GetVariable () or GetNextVariable () call from being issued until a prior call has returned. The runtime + // cache read lock should always be free when entering this function. + // + ASSERT (!mVariableRuntimeCacheReadLock); + + mVariableRuntimeCacheReadLock = TRUE; + CheckForRuntimeCacheSync (); + + if (!mVariableRuntimeCachePendingUpdate) { + // + // 0: Volatile, 1: HOB, 2: Non-Volatile. + // The index and attributes mapping must be kept in this order as FindVariable + // makes use of this mapping to implement search algorithm. + // + VariableStoreList[VariableStoreTypeVolatile] = mVariableRuntimeVolatileCacheBuffer; + VariableStoreList[VariableStoreTypeHob] = mVariableRuntimeHobCacheBuffer; + VariableStoreList[VariableStoreTypeNv] = mVariableRuntimeNvCacheBuffer; + + for (StoreType = (VARIABLE_STORE_TYPE) 0; StoreType < VariableStoreTypeMax; StoreType++) { + if (VariableStoreList[StoreType] == NULL) { + continue; + } + + RtPtrTrack.StartPtr = GetStartPointer (VariableStoreList[StoreType]); + RtPtrTrack.EndPtr = GetEndPointer (VariableStoreList[StoreType]); + RtPtrTrack.Volatile = (BOOLEAN) (StoreType == VariableStoreTypeVolatile); + + Status = FindVariableEx (VariableName, VendorGuid, FALSE, &RtPtrTrack, mVariableAuthFormat); + if (!EFI_ERROR (Status)) { + break; + } + } + + if (!EFI_ERROR (Status)) { + // + // 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); + if (Attributes != NULL) { + *Attributes = RtPtrTrack.CurrPtr->Attributes; + } + + *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; + } + } + } + +Done: + mVariableRuntimeCacheReadLock = FALSE; + + return Status; +} + +/** + 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 ) { EFI_STATUS Status; @@ -474,8 +712,6 @@ RuntimeServiceGetVariable ( return EFI_INVALID_PARAMETER; } - AcquireLockOnlyAtBootTime(&mVariableServicesLock); - // // Init the communicate buffer. The buffer data size is: // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. @@ -488,7 +724,7 @@ RuntimeServiceGetVariable ( } PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + TempDataSize; - Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); + Status = InitCommunicateBuffer ((VOID **) &SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); if (EFI_ERROR (Status)) { goto Done; } @@ -534,10 +770,57 @@ RuntimeServiceGetVariable ( } Done: - ReleaseLockOnlyAtBootTime (&mVariableServicesLock); return Status; } +/** + This code finds variable in storage blocks (Volatile or Non-Volatile). + + 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_INVALID_PARAMETER Invalid parameter. + @retval EFI_SUCCESS Find the specified variable. + @retval EFI_NOT_FOUND Not found. + @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result. + +**/ +EFI_STATUS +EFIAPI +RuntimeServiceGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_STATUS Status; + + if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + if (VariableName[0] == 0) { + return EFI_NOT_FOUND; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) { + Status = FindVariableInRuntimeCache (VariableName, VendorGuid, Attributes, DataSize, Data); + } else { + Status = FindVariableInSmm (VariableName, VendorGuid, Attributes, DataSize, Data); + } + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + + return Status; +} /** This code Finds the Next available variable. @@ -870,6 +1153,17 @@ OnReadyToBoot ( // SendCommunicateBuffer (0); + // + // Install the system configuration table for variable info data captured + // + if (FeaturePcdGet (PcdEnableVariableRuntimeCache) && FeaturePcdGet (PcdVariableCollectStatistics)) { + if (mVariableAuthFormat) { + gBS->InstallConfigurationTable (&gEfiAuthenticatedVariableGuid, mVariableInfo); + } else { + gBS->InstallConfigurationTable (&gEfiVariableGuid, mVariableInfo); + } + } + gBS->CloseEvent (Event); } @@ -893,6 +1187,9 @@ VariableAddressChangeEvent ( { EfiConvertPointer (0x0, (VOID **) &mVariableBuffer); EfiConvertPointer (0x0, (VOID **) &mSmmCommunication); + EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeHobCacheBuffer); + EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeNvCacheBuffer); + EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeVolatileCacheBuffer); } /** @@ -969,6 +1266,159 @@ Done: return Status; } +/** + This code gets information needed from SMM for runtime cache initialization. + + @param[out] TotalHobStorageSize Output pointer for the total HOB storage size in bytes. + @param[out] TotalNvStorageSize Output pointer for the total non-volatile storage size in bytes. + @param[out] TotalVolatileStorageSize Output pointer for the total volatile storage size in bytes. + @param[out] AuthenticatedVariableUsage Output pointer that indicates if authenticated variables are to be used. + + @retval EFI_SUCCESS Retrieved the size successfully. + @retval EFI_INVALID_PARAMETER TotalNvStorageSize parameter is NULL. + @retval EFI_OUT_OF_RESOURCES The memory resources needed for a CommBuffer are not available. + @retval Others Could not retrieve the size successfully. + +**/ +EFI_STATUS +GetRuntimeCacheInfo ( + OUT UINTN *TotalHobStorageSize, + OUT UINTN *TotalNvStorageSize, + OUT UINTN *TotalVolatileStorageSize, + OUT BOOLEAN *AuthenticatedVariableUsage + ) +{ + EFI_STATUS Status; + SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO *SmmGetRuntimeCacheInfo; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + UINTN CommSize; + UINT8 *CommBuffer; + + SmmGetRuntimeCacheInfo = NULL; + CommBuffer = mVariableBuffer; + + if (TotalHobStorageSize == NULL || TotalNvStorageSize == NULL || TotalVolatileStorageSize == NULL || AuthenticatedVariableUsage == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (CommBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO); + ZeroMem (CommBuffer, CommSize); + + SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer; + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); + SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO); + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; + SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO; + SmmGetRuntimeCacheInfo = (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO *) SmmVariableFunctionHeader->Data; + + // + // Send data to SMM. + // + Status = mSmmCommunication->Communicate (mSmmCommunication, CommBuffer, &CommSize); + ASSERT_EFI_ERROR (Status); + if (CommSize <= SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + Status = SmmVariableFunctionHeader->ReturnStatus; + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Get data from SMM. + // + *TotalHobStorageSize = SmmGetRuntimeCacheInfo->TotalHobStorageSize; + *TotalNvStorageSize = SmmGetRuntimeCacheInfo->TotalNvStorageSize; + *TotalVolatileStorageSize = SmmGetRuntimeCacheInfo->TotalVolatileStorageSize; + *AuthenticatedVariableUsage = SmmGetRuntimeCacheInfo->AuthenticatedVariableUsage; + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + +/** + Sends the runtime variable cache context information to SMM. + + @retval EFI_SUCCESS Retrieved the size successfully. + @retval EFI_INVALID_PARAMETER TotalNvStorageSize parameter is NULL. + @retval EFI_OUT_OF_RESOURCES The memory resources needed for a CommBuffer are not available. + @retval Others Could not retrieve the size successfully.; + +**/ +EFI_STATUS +SendRuntimeVariableCacheContextToSmm ( + VOID + ) +{ + EFI_STATUS Status; + SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT *SmmRuntimeVarCacheContext; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + UINTN CommSize; + UINT8 *CommBuffer; + + SmmRuntimeVarCacheContext = NULL; + CommBuffer = mVariableBuffer; + + if (CommBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT); + // + CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT); + ZeroMem (CommBuffer, CommSize); + + SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer; + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); + SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT); + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; + SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT; + SmmRuntimeVarCacheContext = (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT *) SmmVariableFunctionHeader->Data; + + SmmRuntimeVarCacheContext->RuntimeHobCache = mVariableRuntimeHobCacheBuffer; + SmmRuntimeVarCacheContext->RuntimeVolatileCache = mVariableRuntimeVolatileCacheBuffer; + SmmRuntimeVarCacheContext->RuntimeNvCache = mVariableRuntimeNvCacheBuffer; + SmmRuntimeVarCacheContext->PendingUpdate = &mVariableRuntimeCachePendingUpdate; + SmmRuntimeVarCacheContext->ReadLock = &mVariableRuntimeCacheReadLock; + SmmRuntimeVarCacheContext->HobFlushComplete = &mHobFlushComplete; + + // + // Send data to SMM. + // + Status = mSmmCommunication->Communicate (mSmmCommunication, CommBuffer, &CommSize); + ASSERT_EFI_ERROR (Status); + if (CommSize <= SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + Status = SmmVariableFunctionHeader->ReturnStatus; + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + /** Initialize variable service and install Variable Architectural protocol. @@ -985,7 +1435,7 @@ SmmVariableReady ( { EFI_STATUS Status; - Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **)&mSmmVariable); + Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **) &mSmmVariable); if (EFI_ERROR (Status)) { return; } @@ -1007,6 +1457,42 @@ SmmVariableReady ( // mVariableBufferPhysical = mVariableBuffer; + if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) { + DEBUG ((DEBUG_INFO, "Variable driver runtime cache is enabled.\n")); + // + // Allocate runtime variable cache memory buffers. + // + Status = GetRuntimeCacheInfo ( + &mVariableRuntimeHobCacheBufferSize, + &mVariableRuntimeNvCacheBufferSize, + &mVariableRuntimeVolatileCacheBufferSize, + &mVariableAuthFormat + ); + if (!EFI_ERROR (Status)) { + Status = InitVariableCache (&mVariableRuntimeHobCacheBuffer, &mVariableRuntimeHobCacheBufferSize); + if (!EFI_ERROR (Status)) { + Status = InitVariableCache (&mVariableRuntimeNvCacheBuffer, &mVariableRuntimeNvCacheBufferSize); + if (!EFI_ERROR (Status)) { + Status = InitVariableCache (&mVariableRuntimeVolatileCacheBuffer, &mVariableRuntimeVolatileCacheBufferSize); + if (!EFI_ERROR (Status)) { + Status = SendRuntimeVariableCacheContextToSmm (); + if (!EFI_ERROR (Status)) { + SyncRuntimeCache (); + } + } + } + } + if (EFI_ERROR (Status)) { + mVariableRuntimeHobCacheBuffer = NULL; + mVariableRuntimeNvCacheBuffer = NULL; + mVariableRuntimeVolatileCacheBuffer = NULL; + } + } + ASSERT_EFI_ERROR (Status); + } else { + DEBUG ((DEBUG_INFO, "Variable driver runtime cache is disabled.\n")); + } + gRT->GetVariable = RuntimeServiceGetVariable; gRT->GetNextVariableName = RuntimeServiceGetNextVariableName; gRT->SetVariable = RuntimeServiceSetVariable; diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf index 14894e6f13..a250533a53 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 - 2017, Intel Corporation. All rights reserved.
+# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # ## @@ -39,6 +39,8 @@ VariableSmmRuntimeDxe.c PrivilegePolymorphic.h Measurement.c + VariableParsing.c + VariableParsing.h [Packages] MdePkg/MdePkg.dec @@ -65,7 +67,21 @@ gEdkiiVariableLockProtocolGuid ## PRODUCES gEdkiiVarCheckProtocolGuid ## PRODUCES +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdEnableVariableRuntimeCache ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics ## CONSUMES + [Guids] + ## PRODUCES ## GUID # Signature of Variable store header + ## CONSUMES ## GUID # Signature of Variable store header + ## SOMETIMES_PRODUCES ## SystemTable + gEfiAuthenticatedVariableGuid + + ## PRODUCES ## GUID # Signature of Variable store header + ## CONSUMES ## GUID # Signature of Variable store header + ## SOMETIMES_PRODUCES ## SystemTable + gEfiVariableGuid + gEfiEventVirtualAddressChangeGuid ## CONSUMES ## Event gEfiEventExitBootServicesGuid ## CONSUMES ## Event ## CONSUMES ## GUID # Locate protocol diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf index f8a3742959..6e17f6cdf5 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf @@ -49,6 +49,8 @@ VariableNonVolatile.h VariableParsing.c VariableParsing.h + VariableRuntimeCache.c + VariableRuntimeCache.h VarCheck.c Variable.h PrivilegePolymorphic.h