X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FUniversal%2FVariable%2FRuntimeDxe%2FVariableSmmRuntimeDxe.c;h=2cf0ed32ae55bea2094e7bb578a60810c242dbc1;hp=982eea0f145ae40ef33166936edff14335e33d89;hb=bd85bf54c268204c7a698a96f3ccd96cd77952cd;hpb=d00ed85e0ab212b988458b2fe98f20cb295637ad diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c index 982eea0f14..2cf0ed32ae 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -1,17 +1,20 @@ /** @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. -Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php + 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. -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + InitCommunicateBuffer() is really function to check the variable data size. + +Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include @@ -19,6 +22,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include +#include #include #include @@ -27,22 +32,158 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include -#include #include #include #include -#include #include -EFI_HANDLE mHandle = NULL; +#include "PrivilegePolymorphic.h" +#include "VariableParsing.h" + +EFI_HANDLE mHandle = NULL; EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable = NULL; 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; + +/** + 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. @@ -50,10 +191,13 @@ UINTN mVariableBufferSize; 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. @@ -65,10 +209,10 @@ InitCommunicateBuffer ( IN UINTN Function ) { - EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; - SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + - if (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE > mVariableBufferSize) { return EFI_INVALID_PARAMETER; } @@ -76,7 +220,7 @@ InitCommunicateBuffer ( SmmCommunicateHeader = (EFI_SMM_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) { @@ -93,8 +237,8 @@ InitCommunicateBuffer ( @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. - + @retval Others Failure is returned from the function in SMM. + **/ EFI_STATUS SendCommunicateBuffer ( @@ -103,9 +247,9 @@ SendCommunicateBuffer ( { EFI_STATUS Status; UINTN CommSize; - EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; - + CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; Status = mSmmCommunication->Communicate (mSmmCommunication, mVariableBufferPhysical, &CommSize); ASSERT_EFI_ERROR (Status); @@ -115,17 +259,652 @@ SendCommunicateBuffer ( 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); + 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; + 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; +} /** - This code finds variable in storage blocks (Volatile or Non-Volatile). + 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. - @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. @@ -133,71 +912,90 @@ SendCommunicateBuffer ( **/ EFI_STATUS -EFIAPI -RuntimeServiceGetVariable ( - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - OUT UINT32 *Attributes OPTIONAL, - IN OUT UINTN *DataSize, - OUT VOID *Data +GetNextVariableNameInSmm ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid ) { - EFI_STATUS Status; - UINTN PayloadSize; - SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *SmmGetNextVariableName; + UINTN OutVariableNameSize; + UINTN InVariableNameSize; - if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { - return EFI_INVALID_PARAMETER; - } + OutVariableNameSize = *VariableNameSize; + InVariableNameSize = StrSize (VariableName); + SmmGetNextVariableName = NULL; - if ((*DataSize != 0) && (Data == 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. // - PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + StrSize (VariableName); - Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); + 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)) { - return Status; + goto Done; } - ASSERT (SmmVariableHeader != NULL); + ASSERT (SmmGetNextVariableName != NULL); - CopyGuid (&SmmVariableHeader->Guid, VendorGuid); - SmmVariableHeader->DataSize = *DataSize; - SmmVariableHeader->NameSize = StrSize (VariableName); - if (Attributes == NULL) { - SmmVariableHeader->Attributes = 0; - } else { - SmmVariableHeader->Attributes = *Attributes; + // + // 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); } - CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); // - // Send data to SMM. + // Send data to SMM // Status = SendCommunicateBuffer (PayloadSize); // // Get data from SMM. // - *DataSize = SmmVariableHeader->DataSize; - if (Attributes != NULL) { - *Attributes = SmmVariableHeader->Attributes; + 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)) { - return Status; + goto Done; } - CopyMem (Data, (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize, SmmVariableHeader->DataSize); + CopyGuid (VendorGuid, &SmmGetNextVariableName->Guid); + CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize); +Done: return Status; } - /** This code Finds the Next available variable. @@ -219,44 +1017,34 @@ RuntimeServiceGetNextVariableName ( IN OUT EFI_GUID *VendorGuid ) { - EFI_STATUS Status; - UINTN PayloadSize; - SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *SmmGetNextVariableName; + EFI_STATUS Status; + UINTN MaxLen; + + Status = EFI_NOT_FOUND; if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) { return EFI_INVALID_PARAMETER; } - - // - // 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_GET_NEXT_VARIABLE_NAME, Name) + *VariableNameSize; - Status = InitCommunicateBuffer ((VOID **)&SmmGetNextVariableName, PayloadSize, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME); - if (EFI_ERROR (Status)) { - return Status; - } - ASSERT (SmmGetNextVariableName != NULL); - - SmmGetNextVariableName->NameSize = *VariableNameSize; - CopyGuid (&SmmGetNextVariableName->Guid, VendorGuid); - CopyMem (SmmGetNextVariableName->Name, VariableName, *VariableNameSize); // - // Send data to SMM + // Calculate the possible maximum length of name string, including the Null terminator. // - Status = SendCommunicateBuffer (PayloadSize); + 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; + } - // - // Get data from SMM. - // - *VariableNameSize = SmmGetNextVariableName->NameSize; - if (EFI_ERROR (Status)) { - return Status; + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) { + Status = GetNextVariableNameInRuntimeCache (VariableNameSize, VariableName, VendorGuid); + } else { + Status = GetNextVariableNameInSmm (VariableNameSize, VariableName, VendorGuid); } - - CopyGuid (VendorGuid, &SmmGetNextVariableName->Guid); - CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize); + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); return Status; } @@ -264,6 +1052,9 @@ RuntimeServiceGetNextVariableName ( /** 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 @@ -289,34 +1080,48 @@ RuntimeServiceSetVariable ( ) { EFI_STATUS Status; - UINTN PayloadSize; + 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) + StrSize (VariableName) + DataSize; + 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)) { - return Status; + goto Done; } ASSERT (SmmVariableHeader != NULL); CopyGuid ((EFI_GUID *) &SmmVariableHeader->Guid, VendorGuid); SmmVariableHeader->DataSize = DataSize; - SmmVariableHeader->NameSize = StrSize (VariableName); + SmmVariableHeader->NameSize = VariableNameSize; SmmVariableHeader->Attributes = Attributes; CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); CopyMem ((UINT8 *) SmmVariableHeader->Name + SmmVariableHeader->NameSize, Data, DataSize); @@ -325,7 +1130,18 @@ RuntimeServiceSetVariable ( // Send data to SMM. // Status = SendCommunicateBuffer (PayloadSize); - + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + + if (!EfiAtRuntime ()) { + if (!EFI_ERROR (Status)) { + SecureBootHook ( + VariableName, + VendorGuid + ); + } + } return Status; } @@ -360,10 +1176,14 @@ RuntimeServiceQueryVariableInfo ( 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; @@ -371,7 +1191,7 @@ RuntimeServiceQueryVariableInfo ( PayloadSize = sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); Status = InitCommunicateBuffer ((VOID **)&SmmQueryVariableInfo, PayloadSize, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO); if (EFI_ERROR (Status)) { - return Status; + goto Done; } ASSERT (SmmQueryVariableInfo != NULL); @@ -382,7 +1202,7 @@ RuntimeServiceQueryVariableInfo ( // Status = SendCommunicateBuffer (PayloadSize); if (EFI_ERROR (Status)) { - return Status; + goto Done; } // @@ -390,9 +1210,11 @@ RuntimeServiceQueryVariableInfo ( // *MaximumVariableSize = SmmQueryVariableInfo->MaximumVariableSize; *MaximumVariableStorageSize = SmmQueryVariableInfo->MaximumVariableStorageSize; - *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize; - - return EFI_SUCCESS; + *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize; + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; } @@ -416,7 +1238,7 @@ OnExitBootServices ( // 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); + InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE); // // Send data to SMM. @@ -446,11 +1268,24 @@ OnReadyToBoot ( // 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); } @@ -473,15 +1308,244 @@ 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); +} + +/** + 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_SMM_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_SMM_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 = mSmmCommunication->Communicate (mSmmCommunication, 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_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. @param[in] Event Event whose notification function is being invoked. @param[in] Context Pointer to the notification function's context. - + **/ VOID EFIAPI @@ -492,19 +1556,20 @@ SmmVariableReady ( { EFI_STATUS Status; - Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **)&mSmmVariable); + Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **) &mSmmVariable); if (EFI_ERROR (Status)) { return; } - + Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication); ASSERT_EFI_ERROR (Status); - + // - // Allocate memory for variable store. + // Allocate memory for variable communicate buffer. // - mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; - mVariableBufferSize += MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)); + Status = GetVariablePayloadSize (&mVariableBufferPayloadSize); + ASSERT_EFI_ERROR (Status); + mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + mVariableBufferPayloadSize; mVariableBuffer = AllocateRuntimePool (mVariableBufferSize); ASSERT (mVariableBuffer != NULL); @@ -513,21 +1578,79 @@ 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; gRT->QueryVariableInfo = RuntimeServiceQueryVariableInfo; - + // // Install the Variable Architectural Protocol on a new handle. // Status = gBS->InstallProtocolInterface ( &mHandle, - &gEfiVariableArchProtocolGuid, + &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); } @@ -536,7 +1659,7 @@ SmmVariableReady ( @param[in] Event Event whose notification function is being invoked. @param[in] Context Pointer to the notification function's context. - + **/ VOID EFIAPI @@ -555,26 +1678,34 @@ SmmVariableWriteReady ( 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, + &gEfiVariableWriteArchProtocolGuid, EFI_NATIVE_INTERFACE, NULL ); - ASSERT_EFI_ERROR (Status); + 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 + 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] 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. **/ @@ -589,15 +1720,18 @@ VariableSmmRuntimeInitialize ( 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, + &gEfiSmmVariableProtocolGuid, + TPL_CALLBACK, + SmmVariableReady, + NULL, &SmmVariableRegistration ); @@ -605,10 +1739,10 @@ VariableSmmRuntimeInitialize ( // Smm Non-Volatile variable write service is ready // EfiCreateProtocolNotifyEvent ( - &gSmmVariableWriteGuid, - TPL_CALLBACK, - SmmVariableWriteReady, - NULL, + &gSmmVariableWriteGuid, + TPL_CALLBACK, + SmmVariableWriteReady, + NULL, &SmmVariableWriteRegistration ); @@ -616,11 +1750,11 @@ VariableSmmRuntimeInitialize ( // Register the event to reclaim variable for OS usage. // EfiCreateEventReadyToBootEx ( - TPL_NOTIFY, - OnReadyToBoot, - NULL, + TPL_NOTIFY, + OnReadyToBoot, + NULL, &OnReadyToBootEvent - ); + ); // // Register the event to inform SMM variable that it is at runtime. @@ -632,7 +1766,18 @@ VariableSmmRuntimeInitialize ( 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. @@ -645,7 +1790,7 @@ VariableSmmRuntimeInitialize ( &gEfiEventVirtualAddressChangeGuid, &mVirtualAddressChangeEvent ); - + return EFI_SUCCESS; }