Skip to content

Commit

Permalink
MdeModulePkg/Variable: Add RT GetVariable() cache support
Browse files Browse the repository at this point in the history
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
makubacki committed Nov 6, 2019
1 parent 1747ab6 commit aab3b9b
Show file tree
Hide file tree
Showing 12 changed files with 1,011 additions and 41 deletions.
29 changes: 28 additions & 1 deletion MdeModulePkg/Include/Guid/SmmVariableCommon.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
/** @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.<BR>
Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#ifndef _SMM_VARIABLE_COMMON_H_
#define _SMM_VARIABLE_COMMON_H_

#include <Guid/VariableFormat.h>
#include <Protocol/VarCheck.h>

#define EFI_SMM_VARIABLE_WRITE_GUID \
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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_
12 changes: 12 additions & 0 deletions MdeModulePkg/MdeModulePkg.dec
Original file line number Diff line number Diff line change
Expand Up @@ -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.<BR><BR>
# TRUE - The UEFI variable runtime cache is enabled.<BR>
# FALSE - The UEFI variable runtime cache is disabled.<BR>
# @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
Expand Down
50 changes: 49 additions & 1 deletion MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.
Expand All @@ -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);
}
Expand Down
32 changes: 24 additions & 8 deletions MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
//
Expand All @@ -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 {
Expand Down
153 changes: 153 additions & 0 deletions MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c
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;
}
Loading

0 comments on commit aab3b9b

Please sign in to comment.