From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Variable/RuntimeDxe/VariableSmmRuntimeDxe.c | 1801 ++++++++++++++++++++ 1 file changed, 1801 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c (limited to 'roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c') diff --git a/roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c new file mode 100644 index 000000000..663a1aaa1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -0,0 +1,1801 @@ +/** @file + Implement all four UEFI Runtime Variable services for the nonvolatile + and volatile storage space and install variable architecture protocol + based on SMM variable module. + + Caution: This module requires additional review when modified. + This driver will have external input - variable data. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + RuntimeServiceGetVariable() and RuntimeServiceSetVariable() are external API + to receive data buffer. The size should be checked carefully. + + InitCommunicateBuffer() is really function to check the variable data size. + +Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "PrivilegePolymorphic.h" +#include "VariableParsing.h" + +EFI_HANDLE mHandle = NULL; +EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable = NULL; +EFI_EVENT mVirtualAddressChangeEvent = NULL; +EFI_MM_COMMUNICATION2_PROTOCOL *mMmCommunication2 = 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; + +/** + Some Secure Boot Policy Variable may update following other variable changes(SecureBoot follows PK change, etc). + Record their initial State when variable write service is ready. + +**/ +VOID +EFIAPI +RecordSecureBootPolicyVarData( + VOID + ); + +/** + Acquires lock only at boot time. Simply returns at runtime. + + This is a temperary function that will be removed when + EfiAcquireLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiAcquireLock() at boot time, and simply returns + at runtime. + + @param Lock A pointer to the lock to acquire. + +**/ +VOID +AcquireLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!EfiAtRuntime ()) { + EfiAcquireLock (Lock); + } +} + +/** + Releases lock only at boot time. Simply returns at runtime. + + This is a temperary function which will be removed when + EfiReleaseLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiReleaseLock() at boot time and simply returns + at runtime. + + @param Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!EfiAtRuntime ()) { + EfiReleaseLock (Lock); + } +} + +/** + 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. + + The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + + DataSize. + + Caution: This function may receive untrusted input. + The data size external input, so this function will validate it carefully to avoid buffer overflow. + + @param[out] DataPtr Points to the data in the communicate buffer. + @param[in] DataSize The data size to send to SMM. + @param[in] Function The function number to initialize the communicate header. + + @retval EFI_INVALID_PARAMETER The data size is too big. + @retval EFI_SUCCESS Find the specified variable. + +**/ +EFI_STATUS +InitCommunicateBuffer ( + OUT VOID **DataPtr OPTIONAL, + IN UINTN DataSize, + IN UINTN Function + ) +{ + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + + + if (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE > mVariableBufferSize) { + return EFI_INVALID_PARAMETER; + } + + SmmCommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *) mVariableBuffer; + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); + SmmCommunicateHeader->MessageLength = DataSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; + SmmVariableFunctionHeader->Function = Function; + if (DataPtr != NULL) { + *DataPtr = SmmVariableFunctionHeader->Data; + } + + return EFI_SUCCESS; +} + + +/** + Send the data in communicate buffer to SMM. + + @param[in] DataSize This size of the function header and the data. + + @retval EFI_SUCCESS Success is returned from the functin in SMM. + @retval Others Failure is returned from the function in SMM. + +**/ +EFI_STATUS +SendCommunicateBuffer ( + IN UINTN DataSize + ) +{ + EFI_STATUS Status; + UINTN CommSize; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + + CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + Status = mMmCommunication2->Communicate (mMmCommunication2, + mVariableBufferPhysical, + mVariableBuffer, + &CommSize); + ASSERT_EFI_ERROR (Status); + + SmmCommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *) mVariableBuffer; + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)SmmCommunicateHeader->Data; + return SmmVariableFunctionHeader->ReturnStatus; +} + +/** + Mark a variable that will become read-only after leaving the DXE phase of execution. + + @param[in] This The VARIABLE_LOCK_PROTOCOL instance. + @param[in] VariableName A pointer to the variable name that will be made read-only subsequently. + @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently. + + @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked + as pending to be read-only. + @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL. + Or VariableName is an empty string. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource to hold the lock request. +**/ +EFI_STATUS +EFIAPI +VariableLockRequestToLock ( + IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; + + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (VariableName); + VariableToLock = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name)) { + 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. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &VariableToLock, PayloadSize, SMM_VARIABLE_FUNCTION_LOCK_VARIABLE); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (VariableToLock != NULL); + + CopyGuid (&VariableToLock->Guid, VendorGuid); + VariableToLock->NameSize = VariableNameSize; + CopyMem (VariableToLock->Name, VariableName, VariableToLock->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + +/** + Register SetVariable check handler. + + @param[in] Handler Pointer to check handler. + + @retval EFI_SUCCESS The SetVariable check handler was registered successfully. + @retval EFI_INVALID_PARAMETER Handler is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request. + @retval EFI_UNSUPPORTED This interface is not implemented. + For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present. + +**/ +EFI_STATUS +EFIAPI +VarCheckRegisterSetVariableCheckHandler ( + IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Variable property set. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[in] VariableProperty Pointer to the input variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string, + or the fields of VariableProperty are not valid. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertySet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty->Revision != VAR_CHECK_VARIABLE_PROPERTY_REVISION) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (Name); + CommVariableProperty = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + 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. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (CommVariableProperty != NULL); + + CopyGuid (&CommVariableProperty->Guid, Guid); + CopyMem (&CommVariableProperty->VariableProperty, VariableProperty, sizeof (*VariableProperty)); + CommVariableProperty->NameSize = VariableNameSize; + CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + +/** + Variable property get. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[out] VariableProperty Pointer to the output variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string. + @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertyGet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (Name); + CommVariableProperty = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + 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. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (CommVariableProperty != NULL); + + CopyGuid (&CommVariableProperty->Guid, Guid); + CommVariableProperty->NameSize = VariableNameSize; + CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + if (Status == EFI_SUCCESS) { + CopyMem (VariableProperty, &CommVariableProperty->VariableProperty, sizeof (*VariableProperty)); + } + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + +/** + 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. + + @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 +FindVariableInRuntimeCache ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + 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; + } + + ZeroMem (&RtPtrTrack, sizeof (RtPtrTrack)); + + // + // 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); + *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: + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + if (Attributes != NULL && RtPtrTrack.CurrPtr != NULL) { + *Attributes = RtPtrTrack.CurrPtr->Attributes; + } + } + 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; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + UINTN TempDataSize; + UINTN VariableNameSize; + + if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + TempDataSize = *DataSize; + VariableNameSize = StrSize (VariableName); + SmmVariableHeader = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) { + return EFI_INVALID_PARAMETER; + } + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + if (TempDataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize) { + // + // If output data buffer exceed SMM payload limit. Trim output buffer to SMM payload size + // + TempDataSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize; + } + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + TempDataSize; + + Status = InitCommunicateBuffer ((VOID **) &SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (SmmVariableHeader != NULL); + + CopyGuid (&SmmVariableHeader->Guid, VendorGuid); + SmmVariableHeader->DataSize = TempDataSize; + SmmVariableHeader->NameSize = VariableNameSize; + if (Attributes == NULL) { + SmmVariableHeader->Attributes = 0; + } else { + SmmVariableHeader->Attributes = *Attributes; + } + CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + + // + // Get data from SMM. + // + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + // + // SMM CommBuffer DataSize can be a trimed value + // Only update DataSize when needed + // + *DataSize = SmmVariableHeader->DataSize; + } + if (Attributes != NULL) { + *Attributes = SmmVariableHeader->Attributes; + } + + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Data != NULL) { + CopyMem (Data, (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize, SmmVariableHeader->DataSize); + } else { + Status = EFI_INVALID_PARAMETER; + } + +Done: + 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; +} + +/** + Finds the next available variable in a runtime cache variable store. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] VendorGuid Variable Vendor Guid. + + @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 +GetNextVariableNameInRuntimeCache ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN VarNameSize; + VARIABLE_HEADER *VariablePtr; + VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax]; + + Status = EFI_NOT_FOUND; + + // + // 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); + + CheckForRuntimeCacheSync (); + + mVariableRuntimeCacheReadLock = TRUE; + 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. + // + VariableStoreHeader[VariableStoreTypeVolatile] = mVariableRuntimeVolatileCacheBuffer; + VariableStoreHeader[VariableStoreTypeHob] = mVariableRuntimeHobCacheBuffer; + VariableStoreHeader[VariableStoreTypeNv] = mVariableRuntimeNvCacheBuffer; + + Status = VariableServiceGetNextVariableInternal ( + VariableName, + VendorGuid, + VariableStoreHeader, + &VariablePtr, + mVariableAuthFormat + ); + if (!EFI_ERROR (Status)) { + VarNameSize = NameSizeOfVariable (VariablePtr, mVariableAuthFormat); + ASSERT (VarNameSize != 0); + if (VarNameSize <= *VariableNameSize) { + CopyMem (VariableName, GetVariableNamePtr (VariablePtr, mVariableAuthFormat), VarNameSize); + CopyMem (VendorGuid, GetVendorGuidPtr (VariablePtr, mVariableAuthFormat), sizeof (EFI_GUID)); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *VariableNameSize = VarNameSize; + } + } + mVariableRuntimeCacheReadLock = FALSE; + + return Status; +} + +/** + Finds the next available variable in a SMM variable store. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] VendorGuid Variable Vendor Guid. + + @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 +GetNextVariableNameInSmm ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *SmmGetNextVariableName; + UINTN OutVariableNameSize; + UINTN InVariableNameSize; + + OutVariableNameSize = *VariableNameSize; + InVariableNameSize = StrSize (VariableName); + SmmGetNextVariableName = NULL; + + // + // If input string exceeds SMM payload limit. Return failure + // + if (InVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + return EFI_INVALID_PARAMETER; + } + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + if (OutVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + // + // If output buffer exceed SMM payload limit. Trim output buffer to SMM payload size + // + OutVariableNameSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name); + } + // + // Payload should be Guid + NameSize + MAX of Input & Output buffer + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + MAX (OutVariableNameSize, InVariableNameSize); + + Status = InitCommunicateBuffer ((VOID **)&SmmGetNextVariableName, PayloadSize, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (SmmGetNextVariableName != NULL); + + // + // SMM comm buffer->NameSize is buffer size for return string + // + SmmGetNextVariableName->NameSize = OutVariableNameSize; + + CopyGuid (&SmmGetNextVariableName->Guid, VendorGuid); + // + // Copy whole string + // + CopyMem (SmmGetNextVariableName->Name, VariableName, InVariableNameSize); + if (OutVariableNameSize > InVariableNameSize) { + ZeroMem ((UINT8 *) SmmGetNextVariableName->Name + InVariableNameSize, OutVariableNameSize - InVariableNameSize); + } + + // + // Send data to SMM + // + Status = SendCommunicateBuffer (PayloadSize); + + // + // Get data from SMM. + // + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + // + // SMM CommBuffer NameSize can be a trimed value + // Only update VariableNameSize when needed + // + *VariableNameSize = SmmGetNextVariableName->NameSize; + } + if (EFI_ERROR (Status)) { + goto Done; + } + + CopyGuid (VendorGuid, &SmmGetNextVariableName->Guid); + CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize); + +Done: + return Status; +} + +/** + This code Finds the Next available variable. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] VendorGuid Variable Vendor Guid. + + @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 +RuntimeServiceGetNextVariableName ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN MaxLen; + + Status = EFI_NOT_FOUND; + + if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Calculate the possible maximum length of name string, including the Null terminator. + // + MaxLen = *VariableNameSize / sizeof (CHAR16); + if ((MaxLen == 0) || (StrnLenS (VariableName, MaxLen) == MaxLen)) { + // + // Null-terminator is not found in the first VariableNameSize bytes of the input VariableName buffer, + // follow spec to return EFI_INVALID_PARAMETER. + // + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) { + Status = GetNextVariableNameInRuntimeCache (VariableNameSize, VariableName, VendorGuid); + } else { + Status = GetNextVariableNameInSmm (VariableNameSize, VariableName, VendorGuid); + } + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + + return Status; +} + +/** + This code sets variable in storage blocks (Volatile or Non-Volatile). + + Caution: This function may receive untrusted input. + The data size and data are 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[in] Attributes Attribute value of the variable found + @param[in] DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param[in] Data Data pointer. + + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_SUCCESS Set successfully. + @retval EFI_OUT_OF_RESOURCES Resource not enough to set variable. + @retval EFI_NOT_FOUND Not found. + @retval EFI_WRITE_PROTECTED Variable is read-only. + +**/ +EFI_STATUS +EFIAPI +RuntimeServiceSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + UINTN VariableNameSize; + + // + // Check input parameters. + // + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataSize != 0 && Data == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (VariableName); + SmmVariableHeader = NULL; + + // + // If VariableName or DataSize exceeds SMM payload limit. Return failure + // + if ((VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) || + (DataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize)){ + 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. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + DataSize; + Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_SET_VARIABLE); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (SmmVariableHeader != NULL); + + CopyGuid ((EFI_GUID *) &SmmVariableHeader->Guid, VendorGuid); + SmmVariableHeader->DataSize = DataSize; + SmmVariableHeader->NameSize = VariableNameSize; + SmmVariableHeader->Attributes = Attributes; + CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); + CopyMem ((UINT8 *) SmmVariableHeader->Name + SmmVariableHeader->NameSize, Data, DataSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + + if (!EfiAtRuntime ()) { + if (!EFI_ERROR (Status)) { + SecureBootHook ( + VariableName, + VendorGuid + ); + } + } + return Status; +} + + +/** + This code returns information about the EFI variables. + + @param[in] Attributes Attributes bitmask to specify the type of variables + on which to return information. + @param[out] MaximumVariableStorageSize Pointer to the maximum size of the storage space available + for the EFI variables associated with the attributes specified. + @param[out] RemainingVariableStorageSize Pointer to the remaining size of the storage space available + for EFI variables associated with the attributes specified. + @param[out] MaximumVariableSize Pointer to the maximum size of an individual EFI variables + associated with the attributes specified. + + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied. + @retval EFI_SUCCESS Query successfully. + @retval EFI_UNSUPPORTED The attribute is not supported on this platform. + +**/ +EFI_STATUS +EFIAPI +RuntimeServiceQueryVariableInfo ( + IN UINT32 Attributes, + OUT UINT64 *MaximumVariableStorageSize, + OUT UINT64 *RemainingVariableStorageSize, + OUT UINT64 *MaximumVariableSize + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *SmmQueryVariableInfo; + + SmmQueryVariableInfo = NULL; + + if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) { + 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; + // + PayloadSize = sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); + Status = InitCommunicateBuffer ((VOID **)&SmmQueryVariableInfo, PayloadSize, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (SmmQueryVariableInfo != NULL); + + SmmQueryVariableInfo->Attributes = Attributes; + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Get data from SMM. + // + *MaximumVariableSize = SmmQueryVariableInfo->MaximumVariableSize; + *MaximumVariableStorageSize = SmmQueryVariableInfo->MaximumVariableStorageSize; + *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize; + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + + +/** + Exit Boot Services Event notification handler. + + Notify SMM variable driver about the event. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +OnExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE. + // + InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE); + + // + // Send data to SMM. + // + SendCommunicateBuffer (0); +} + + +/** + On Ready To Boot Services Event notification handler. + + Notify SMM variable driver about the event. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE. + // + InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_READY_TO_BOOT); + + // + // Send data to SMM. + // + 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); +} + + +/** + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. + + This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + It convers pointer to new virtual address. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +VariableAddressChangeEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EfiConvertPointer (0x0, (VOID **) &mVariableBuffer); + EfiConvertPointer (0x0, (VOID **) &mMmCommunication2); + EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeHobCacheBuffer); + EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeNvCacheBuffer); + EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeVolatileCacheBuffer); +} + +/** + This code gets variable payload size. + + @param[out] VariablePayloadSize Output pointer to variable payload size. + + @retval EFI_SUCCESS Get successfully. + @retval Others Get unsuccessfully. + +**/ +EFI_STATUS +EFIAPI +GetVariablePayloadSize ( + OUT UINTN *VariablePayloadSize + ) +{ + EFI_STATUS Status; + SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *SmmGetPayloadSize; + EFI_MM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + UINTN CommSize; + UINT8 *CommBuffer; + + SmmGetPayloadSize = NULL; + CommBuffer = NULL; + + if(VariablePayloadSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + // + CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + CommBuffer = AllocateZeroPool (CommSize); + if (CommBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + SmmCommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *) CommBuffer; + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); + SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; + SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE; + SmmGetPayloadSize = (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *) SmmVariableFunctionHeader->Data; + + // + // Send data to SMM. + // + Status = mMmCommunication2->Communicate (mMmCommunication2, CommBuffer, CommBuffer, &CommSize); + ASSERT_EFI_ERROR (Status); + + Status = SmmVariableFunctionHeader->ReturnStatus; + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Get data from SMM. + // + *VariablePayloadSize = SmmGetPayloadSize->VariablePayloadSize; + +Done: + if (CommBuffer != NULL) { + FreePool (CommBuffer); + } + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + 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_MM_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_MM_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 = mMmCommunication2->Communicate (mMmCommunication2, CommBuffer, 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_MM_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_MM_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 = mMmCommunication2->Communicate (mMmCommunication2, CommBuffer, 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. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmVariableReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **) &mSmmVariable); + if (EFI_ERROR (Status)) { + return; + } + + Status = gBS->LocateProtocol (&gEfiMmCommunication2ProtocolGuid, NULL, (VOID **) &mMmCommunication2); + ASSERT_EFI_ERROR (Status); + + // + // Allocate memory for variable communicate buffer. + // + Status = GetVariablePayloadSize (&mVariableBufferPayloadSize); + ASSERT_EFI_ERROR (Status); + mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + mVariableBufferPayloadSize; + mVariableBuffer = AllocateRuntimePool (mVariableBufferSize); + ASSERT (mVariableBuffer != NULL); + + // + // Save the buffer physical address used for SMM conmunication. + // + 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; + gRT->QueryVariableInfo = RuntimeServiceQueryVariableInfo; + + // + // Install the Variable Architectural Protocol on a new handle. + // + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiVariableArchProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + mVariableLock.RequestToLock = VariableLockRequestToLock; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVariableLockProtocolGuid, + &mVariableLock, + NULL + ); + ASSERT_EFI_ERROR (Status); + + mVarCheck.RegisterSetVariableCheckHandler = VarCheckRegisterSetVariableCheckHandler; + mVarCheck.VariablePropertySet = VarCheckVariablePropertySet; + mVarCheck.VariablePropertyGet = VarCheckVariablePropertyGet; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVarCheckProtocolGuid, + &mVarCheck, + NULL + ); + ASSERT_EFI_ERROR (Status); + + gBS->CloseEvent (Event); +} + + +/** + SMM Non-Volatile variable write service is ready notify event handler. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmVariableWriteReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *ProtocolOps; + + // + // Check whether the protocol is installed or not. + // + Status = gBS->LocateProtocol (&gSmmVariableWriteGuid, NULL, (VOID **) &ProtocolOps); + if (EFI_ERROR (Status)) { + return; + } + + // + // Some Secure Boot Policy Var (SecureBoot, etc) updates following other + // Secure Boot Policy Variable change. Record their initial value. + // + RecordSecureBootPolicyVarData(); + + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiVariableWriteArchProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + gBS->CloseEvent (Event); +} + + +/** + Variable Driver main entry point. The Variable driver places the 4 EFI + runtime services in the EFI System Table and installs arch protocols + for variable read and write services being available. It also registers + a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS Variable service successfully initialized. + +**/ +EFI_STATUS +EFIAPI +VariableSmmRuntimeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *SmmVariableRegistration; + VOID *SmmVariableWriteRegistration; + EFI_EVENT OnReadyToBootEvent; + EFI_EVENT ExitBootServiceEvent; + EFI_EVENT LegacyBootEvent; + + EfiInitializeLock (&mVariableServicesLock, TPL_NOTIFY); + + // + // Smm variable service is ready + // + EfiCreateProtocolNotifyEvent ( + &gEfiSmmVariableProtocolGuid, + TPL_CALLBACK, + SmmVariableReady, + NULL, + &SmmVariableRegistration + ); + + // + // Smm Non-Volatile variable write service is ready + // + EfiCreateProtocolNotifyEvent ( + &gSmmVariableWriteGuid, + TPL_CALLBACK, + SmmVariableWriteReady, + NULL, + &SmmVariableWriteRegistration + ); + + // + // Register the event to reclaim variable for OS usage. + // + EfiCreateEventReadyToBootEx ( + TPL_NOTIFY, + OnReadyToBoot, + NULL, + &OnReadyToBootEvent + ); + + // + // Register the event to inform SMM variable that it is at runtime. + // + gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnExitBootServices, + NULL, + &gEfiEventExitBootServicesGuid, + &ExitBootServiceEvent + ); + + // + // Register the event to inform SMM variable that it is at runtime for legacy boot. + // Reuse OnExitBootServices() here. + // + EfiCreateEventLegacyBootEx( + TPL_NOTIFY, + OnExitBootServices, + NULL, + &LegacyBootEvent + ); + + // + // Register the event to convert the pointer for runtime. + // + gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + VariableAddressChangeEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &mVirtualAddressChangeEvent + ); + + return EFI_SUCCESS; +} + -- cgit