forked from tianocore/edk2-staging
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MdeModulePkg/Variable: Add RT GetVariable() cache support
REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2220 This change reduces SMIs for GetVariable () by maintaining a UEFI variable cache in Runtime DXE in addition to the pre- existing cache in SMRAM. When the Runtime Service GetVariable() is invoked, a Runtime DXE cache is used instead of triggering an SMI to VariableSmm. This can improve overall system performance by servicing variable read requests without rendezvousing all cores into SMM. The runtime cache can be disabled with by setting the FeaturePCD gEfiMdeModulePkgTokenSpaceGuid.PcdEnableVariableRuntimeCache to FALSE. If the PCD is set to FALSE, the runtime cache will not be used and an SMI will be triggered for Runtime Service GetVariable () and GetNextVariableName () invocations. The following are important points regarding the behavior of the variable drivers when the variable runtime cache is enabled. 1. All of the non-volatile storage contents are loaded into the cache upon driver load. This one time load operation from storage is preferred as opposed to building the cache on demand. An on- demand cache would require a fallback SMI to load data into the cache as variables are requested. 2. SetVariable () requests will continue to always trigger an SMI. This occurs regardless of whether the variable is volatile or non-volatile. 3. Both volatile and non-volatile variables are cached in a runtime buffer. As is the case in the current EDK II variable driver, they continue to be cached in separate buffers. 4. The cache in Runtime DXE and SMM are intended to be exact copies of one another. All SMM variable accesses only return data from the SMM cache. The runtime caches are only updated after the variable I/O operation is successful in SMM. The runtime caches are only updated from SMM. 5. Synchronization mechanisms are in place to ensure the runtime cache content integrity with the SMM cache. These may result in updates to runtime cache that are the same in content but different in offset and size from updates to the SMM cache. When using SMM variables with runtime cache enabled, two caches will now be present. 1. "Runtime Cache" - Maintained in VariableSmmRuntimeDxe. Used to service Runtime Services GetVariable () and GetNextVariableName () callers. 2. "SMM Cache" - Maintained in VariableSmm to service SMM GetVariable () and GetNextVariableName () callers. a. This cache is retained so SMM modules do not operate on data outside SMRAM. Because a race condition can occur if an SMI occurs during the execution of runtime code reading from the runtime cache, a runtime cache read lock is introduced that explicitly moves pending updates from SMM to the runtime cache if an SMM update occurs while the runtime cache is locked. Note that it is not expected a Runtime services call will interrupt SMM processing since all CPU cores rendezvous in SMM. It is possible to view UEFI variable read and write statistics by setting the gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics FeaturePcd to TRUE and using the VariableInfo UEFI application in MdeModulePkg to dump variable statistics to the console. By doing so, a user can view the number of GetVariable () hits from the Runtime DXE variable driver (Runtime Cache hits) and the SMM variable driver (SMM Cache hits). SMM Cache hits for GetVariable () will occur when SMM modules invoke GetVariable (). Cc: Dandan Bi <dandan.bi@intel.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Eric Dong <eric.dong@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Michael Kubacki <michael.a.kubacki@intel.com> Reviewed-by: Jian J Wang <jian.j.wang@intel.com>
- Loading branch information
Showing
12 changed files
with
1,011 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
153 changes: 153 additions & 0 deletions
153
MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.<BR> | ||
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; | ||
} |
Oops, something went wrong.