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