From: niruiyu Date: Fri, 17 May 2013 03:49:35 +0000 (+0000) Subject: Add EDKII_VARIABLE_LOCK_PROTOCOL and the implementation in MdeModulePkg variable... X-Git-Tag: edk2-stable201903~12529 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=ff8438477f2dcea28149514de25368ac0b2c02ee Add EDKII_VARIABLE_LOCK_PROTOCOL and the implementation in MdeModulePkg variable drivers. Add code in BdsDxe driver to call the protocol to mark the read-only variables defined in the UEFI Spec. Signed-off-by: Ruiyu Ni Reviewed-by: Guo Dong Reviewed-by: Star Zeng git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@14372 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/IntelFrameworkModulePkg/Universal/BdsDxe/Bds.h b/IntelFrameworkModulePkg/Universal/BdsDxe/Bds.h index 0929f1d27b..14996f63e6 100644 --- a/IntelFrameworkModulePkg/Universal/BdsDxe/Bds.h +++ b/IntelFrameworkModulePkg/Universal/BdsDxe/Bds.h @@ -1,7 +1,7 @@ /** @file Head file for BDS Architectural Protocol implementation -Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.
+Copyright (c) 2004 - 2013, 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 @@ -47,6 +47,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include #include diff --git a/IntelFrameworkModulePkg/Universal/BdsDxe/BdsDxe.inf b/IntelFrameworkModulePkg/Universal/BdsDxe/BdsDxe.inf index a60738e946..2424a8a842 100644 --- a/IntelFrameworkModulePkg/Universal/BdsDxe/BdsDxe.inf +++ b/IntelFrameworkModulePkg/Universal/BdsDxe/BdsDxe.inf @@ -14,7 +14,7 @@ # BDSDxe also maintain the UI for "Boot Manager, Boot Maintaince Manager, Device Manager" which # is used for user to configure boot option or maintain hardware device. # -# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
+# Copyright (c) 2008 - 2013, 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 @@ -160,6 +160,7 @@ gEfiDriverHealthProtocolGuid ## PROTOCOL SOMETIMES_CONSUMES gEfiPciIoProtocolGuid ## PROTOCOL CONSUMES gEfiBootLogoProtocolGuid ## PROTOCOL SOMETIMES_CONSUMES + gEdkiiVariableLockProtocolGuid ## PROTOCOL CONSUMES [FeaturePcd] gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangDeprecate diff --git a/IntelFrameworkModulePkg/Universal/BdsDxe/BdsEntry.c b/IntelFrameworkModulePkg/Universal/BdsDxe/BdsEntry.c index 6eaec886f2..91c6dc72dd 100644 --- a/IntelFrameworkModulePkg/Universal/BdsDxe/BdsEntry.c +++ b/IntelFrameworkModulePkg/Universal/BdsDxe/BdsEntry.c @@ -38,6 +38,17 @@ EFI_BDS_ARCH_PROTOCOL gBds = { UINT16 *mBootNext = NULL; +/// +/// The read-only variables defined in UEFI Spec. +/// +CHAR16 *mReadOnlyVariables[] = { + L"PlatformLangCodes", + L"LangCodes", + L"BootOptionSupport", + L"HwErrRecSupport", + L"OsIndicationsSupported" + }; + /** Install Boot Device Selection Protocol @@ -459,6 +470,8 @@ BdsEntry ( CHAR16 *FirmwareVendor; EFI_STATUS Status; UINT16 BootTimeOut; + UINTN Index; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; // // Insert the performance probe @@ -496,6 +509,18 @@ BdsEntry ( // BdsFormalizeEfiGlobalVariable(); + // + // Mark the read-only variables if the Variable Lock protocol exists + // + Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock); + DEBUG ((EFI_D_INFO, "[BdsDxe] Locate Variable Lock protocol - %r\n", Status)); + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < sizeof (mReadOnlyVariables) / sizeof (mReadOnlyVariables[0]); Index++) { + Status = VariableLock->RequestToLock (VariableLock, mReadOnlyVariables[Index], &gEfiGlobalVariableGuid); + ASSERT_EFI_ERROR (Status); + } + } + // // Report Status Code to indicate connecting drivers will happen // @@ -504,12 +529,6 @@ BdsEntry ( (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS) ); - // - // Do the platform init, can be customized by OEM/IBV - // - PERF_START (NULL, "PlatformBds", "BDS", 0); - PlatformBdsInit (); - InitializeHwErrRecSupport(); // @@ -539,6 +558,12 @@ BdsEntry ( InitializeLanguage (TRUE); InitializeFrontPage (TRUE); + // + // Do the platform init, can be customized by OEM/IBV + // + PERF_START (NULL, "PlatformBds", "BDS", 0); + PlatformBdsInit (); + // // Set up the device list based on EFI 1.1 variables // process Driver#### and Load the driver's in the diff --git a/MdeModulePkg/Include/Guid/SmmVariableCommon.h b/MdeModulePkg/Include/Guid/SmmVariableCommon.h index 478fd056b0..a2daae8fe3 100644 --- a/MdeModulePkg/Include/Guid/SmmVariableCommon.h +++ b/MdeModulePkg/Include/Guid/SmmVariableCommon.h @@ -60,6 +60,10 @@ typedef struct { // is gEfiSmmVariableProtocolGuid. // #define SMM_VARIABLE_FUNCTION_GET_STATISTICS 7 +// +// The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE +// +#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE 8 /// /// Size of SMM communicate header, without including the payload. @@ -101,4 +105,6 @@ typedef struct { UINT32 Attributes; } SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO; +typedef SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE; + #endif // _SMM_VARIABLE_COMMON_H_ \ No newline at end of file diff --git a/MdeModulePkg/Include/Protocol/VariableLock.h b/MdeModulePkg/Include/Protocol/VariableLock.h new file mode 100644 index 0000000000..a2a73bc161 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/VariableLock.h @@ -0,0 +1,63 @@ +/** @file + Variable Lock Protocol is related to EDK II-specific implementation of variables + and intended for use as a means to mark a variable read-only after the event + EFI_END_OF_DXE_EVENT_GUID is signaled. + + Copyright (c) 2013, 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 + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __VARIABLE_LOCK_H__ +#define __VARIABLE_LOCK_H__ + +#define EDKII_VARIABLE_LOCK_PROTOCOL_GUID \ + { \ + 0xcd3d0a05, 0x9e24, 0x437c, { 0xa8, 0x91, 0x1e, 0xe0, 0x53, 0xdb, 0x76, 0x38 } \ + } + +typedef struct _EDKII_VARIABLE_LOCK_PROTOCOL EDKII_VARIABLE_LOCK_PROTOCOL; + +/** + Mark a variable that will become read-only after leaving the DXE phase of execution. + Write request coming from SMM environment through EFI_SMM_VARIABLE_PROTOCOL is allowed. + + @param[in] This The EDKII_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. +**/ +typedef +EFI_STATUS +(EFIAPI * EDKII_VARIABLE_LOCK_PROTOCOL_REQUEST_TO_LOCK) ( + IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ); + +/// +/// Variable Lock Protocol is related to EDK II-specific implementation of variables +/// and intended for use as a means to mark a variable read-only after the event +/// EFI_END_OF_DXE_EVENT_GUID is signaled. +/// +struct _EDKII_VARIABLE_LOCK_PROTOCOL { + EDKII_VARIABLE_LOCK_PROTOCOL_REQUEST_TO_LOCK RequestToLock; +}; + +extern EFI_GUID gEdkiiVariableLockProtocolGuid; + +#endif + diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 309450792a..3c07c69a97 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -326,6 +326,10 @@ # Include/Protocol/SmmVariableProtocol.h gEfiSmmVariableProtocolGuid = { 0xed32d533, 0x99e6, 0x4209, { 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7 }} + ## This protocol is intended for use as a means to mark a variable read-only after the event EFI_END_OF_DXE_EVENT_GUID is signaled. + # Include/Protocol/VariableLock.h + gEdkiiVariableLockProtocolGuid = { 0xcd3d0a05, 0x9e24, 0x437c, { 0xa8, 0x91, 0x1e, 0xe0, 0x53, 0xdb, 0x76, 0x38 }} + ## This protocol is similar with DXE FVB protocol and used in the UEFI SMM evvironment. # Include/Protocol/SmmFirmwareVolumeBlock.h gEfiSmmFirmwareVolumeBlockProtocolGuid = { 0xd326d041, 0xbd31, 0x4c01, { 0xb5, 0xa8, 0x62, 0x8b, 0xe8, 0x7f, 0x6, 0x53 }} diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c index cd3f5ef475..9f983ffc9f 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c @@ -21,12 +21,28 @@ VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; /// /// Define a memory cache that improves the search performance for a variable. /// -VARIABLE_STORE_HEADER *mNvVariableCache = NULL; +VARIABLE_STORE_HEADER *mNvVariableCache = NULL; /// /// The memory entry used for variable statistics data. /// -VARIABLE_INFO_ENTRY *gVariableInfo = NULL; +VARIABLE_INFO_ENTRY *gVariableInfo = NULL; + +/// +/// The list to store the variables which cannot be set after the EFI_END_OF_DXE_EVENT_GROUP_GUID +/// or EVT_GROUP_READY_TO_BOOT event. +/// +LIST_ENTRY mLockedVariableList = INITIALIZE_LIST_HEAD_VARIABLE (mLockedVariableList); + +/// +/// The flag to indicate whether the platform has left the DXE phase of execution. +/// +BOOLEAN mEndOfDxe = FALSE; + +/// +/// The flag to indicate whether the variable storage locking is enabled. +/// +BOOLEAN mEnableLocking = TRUE; /** @@ -1918,6 +1934,58 @@ IsHwErrRecVariable ( return TRUE; } +/** + 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 + ) +{ + VARIABLE_ENTRY *Entry; + + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (mEndOfDxe) { + return EFI_ACCESS_DENIED; + } + + Entry = AllocateRuntimePool (sizeof (*Entry) + StrSize (VariableName)); + if (Entry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((EFI_D_INFO, "[Variable] Lock: %g:%s\n", VendorGuid, VariableName)); + + AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + Entry->Name = (CHAR16 *) (Entry + 1); + StrCpy (Entry->Name, VariableName); + CopyGuid (&Entry->Guid, VendorGuid); + InsertTailList (&mLockedVariableList, &Entry->Link); + + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + return EFI_SUCCESS; +} + /** This code finds variable in storage blocks (Volatile or Non-Volatile). @@ -2192,6 +2260,8 @@ VariableServiceSetVariable ( EFI_STATUS Status; VARIABLE_HEADER *NextVariable; EFI_PHYSICAL_ADDRESS Point; + LIST_ENTRY *Link; + VARIABLE_ENTRY *Entry; // // Check input parameters. @@ -2247,16 +2317,6 @@ VariableServiceSetVariable ( } } - if (AtRuntime ()) { - // - // HwErrRecSupport Global Variable identifies the level of hardware error record persistence - // support implemented by the platform. This variable is only modified by firmware and is read-only to the OS. - // - if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, L"HwErrRecSupport") == 0)) { - return EFI_WRITE_PROTECTED; - } - } - AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); // @@ -2275,13 +2335,31 @@ VariableServiceSetVariable ( mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) NextVariable - (UINTN) Point; } + if (mEndOfDxe && mEnableLocking) { + // + // Treat the variables listed in the forbidden variable list as read-only after leaving DXE phase. + // + for ( Link = GetFirstNode (&mLockedVariableList) + ; !IsNull (&mLockedVariableList, Link) + ; Link = GetNextNode (&mLockedVariableList, Link) + ) { + Entry = BASE_CR (Link, VARIABLE_ENTRY, Link); + if (CompareGuid (&Entry->Guid, VendorGuid) && (StrCmp (Entry->Name, VariableName) == 0)) { + Status = EFI_WRITE_PROTECTED; + DEBUG ((EFI_D_INFO, "[Variable]: Changing readonly variable after leaving DXE phase - %g:%s\n", VendorGuid, VariableName)); + goto Done; + } + } + } + // // Check whether the input variable is already existed. // Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, TRUE); if (!EFI_ERROR (Status)) { if (((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) && AtRuntime ()) { - return EFI_WRITE_PROTECTED; + Status = EFI_WRITE_PROTECTED; + goto Done; } } @@ -2292,6 +2370,7 @@ VariableServiceSetVariable ( Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, &Variable); +Done: InterlockedDecrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState); ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h index 8504ce05f7..b5e6edec93 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h @@ -22,6 +22,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include #include #include @@ -97,6 +98,12 @@ typedef struct { VOID *Data; } VARIABLE_CACHE_ENTRY; +typedef struct { + EFI_GUID Guid; + CHAR16 *Name; + LIST_ENTRY Link; +} VARIABLE_ENTRY; + /** Flush the HOB variable to flash. @@ -456,7 +463,30 @@ VariableServiceQueryVariableInfo ( OUT UINT64 *RemainingVariableStorageSize, OUT UINT64 *MaximumVariableSize ); - + +/** + 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 + ); + extern VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; #endif diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c index ba4d29ab30..9e371c0a85 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c @@ -3,7 +3,7 @@ Implement all four UEFI Runtime Variable services for the nonvolatile and volatile storage space and install variable architecture protocol. -Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2013, 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 @@ -21,6 +21,9 @@ extern VARIABLE_INFO_ENTRY *gVariableInfo; EFI_HANDLE mHandle = NULL; EFI_EVENT mVirtualAddressChangeEvent = NULL; EFI_EVENT mFtwRegistration = NULL; +extern BOOLEAN mEndOfDxe; +extern BOOLEAN mEnableLocking; +EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock = { VariableLockRequestToLock }; /** Return TRUE if ExitBootServices () has been called. @@ -255,12 +258,34 @@ OnReadyToBoot ( VOID *Context ) { + // + // Set the End Of DXE bit in case the EFI_END_OF_DXE_EVENT_GROUP_GUID event is not signaled. + // + mEndOfDxe = TRUE; ReclaimForOS (); if (FeaturePcdGet (PcdVariableCollectStatistics)) { gBS->InstallConfigurationTable (&gEfiVariableGuid, gVariableInfo); } } +/** + Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group. + + This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +OnEndOfDxe ( + EFI_EVENT Event, + VOID *Context + ) +{ + mEndOfDxe = TRUE; +} /** Fault Tolerant Write protocol notification event handler. @@ -375,11 +400,20 @@ VariableServiceInitialize ( ) { EFI_STATUS Status; - EFI_EVENT ReadyToBootEvent; + EFI_EVENT ReadyToBootEvent; + EFI_EVENT EndOfDxeEvent; Status = VariableCommonInitialize (); ASSERT_EFI_ERROR (Status); + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVariableLockProtocolGuid, + &mVariableLock, + NULL + ); + ASSERT_EFI_ERROR (Status); + SystemTable->RuntimeServices->GetVariable = VariableServiceGetVariable; SystemTable->RuntimeServices->GetNextVariableName = VariableServiceGetNextVariableName; SystemTable->RuntimeServices->SetVariable = VariableServiceSetVariable; @@ -426,6 +460,20 @@ VariableServiceInitialize ( NULL, &ReadyToBootEvent ); + ASSERT_EFI_ERROR (Status); + + // + // Register the event handling function to set the End Of DXE flag. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnEndOfDxe, + NULL, + &gEfiEndOfDxeEventGroupGuid, + &EndOfDxeEvent + ); + ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf index 28b69c34de..8cbefefa1b 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf @@ -2,7 +2,7 @@ # Component description file for Variable module. # # This module installs three EFI_RUNTIME_SERVICES: SetVariable, GetVariable, GetNextVariableName. -# Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
+# Copyright (c) 2006 - 2013, 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 @@ -59,13 +59,15 @@ gEfiVariableWriteArchProtocolGuid ## ALWAYS_PRODUCES gEfiVariableArchProtocolGuid ## ALWAYS_PRODUCES gEfiFaultTolerantWriteProtocolGuid ## SOMETIMES_CONSUMES + gEdkiiVariableLockProtocolGuid ## ALWAYS_PRODUCES [Guids] gEfiVariableGuid ## PRODUCES ## Configuration Table Guid gEfiGlobalVariableGuid ## PRODUCES ## Variable Guid - gEfiEventVirtualAddressChangeGuid ## PRODUCES ## Event + gEfiEventVirtualAddressChangeGuid ## CONSUMES ## Event gEfiSystemNvDataFvGuid ## CONSUMES gEfiHardwareErrorVariableGuid ## SOMETIMES_CONSUMES + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c index 111a6cd411..4009fcb171 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c @@ -29,6 +29,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include @@ -46,15 +47,61 @@ BOOLEAN mAtRuntime = F EFI_GUID mZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; UINT8 *mVariableBufferPayload = NULL; UINTN mVariableBufferPayloadSize; +extern BOOLEAN mEndOfDxe; +extern BOOLEAN mEnableLocking; + +/** + + This code sets variable in storage blocks (Volatile or Non-Volatile). + + @param VariableName Name of Variable to be found. + @param VendorGuid Variable vendor GUID. + @param Attributes Attribute value of the variable found + @param DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param Data Data pointer. + + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Set successfully. + @return EFI_OUT_OF_RESOURCES Resource not enough to set variable. + @return EFI_NOT_FOUND Not found. + @return EFI_WRITE_PROTECTED Variable is read-only. + +**/ +EFI_STATUS +EFIAPI +SmmVariableSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + + // + // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL. + // + mEnableLocking = FALSE; + Status = VariableServiceSetVariable ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + mEnableLocking = TRUE; + return Status; +} EFI_SMM_VARIABLE_PROTOCOL gSmmVariable = { VariableServiceGetVariable, VariableServiceGetNextVariableName, - VariableServiceSetVariable, + SmmVariableSetVariable, VariableServiceQueryVariableInfo }; - /** Return TRUE if ExitBootServices () has been called. @@ -450,6 +497,7 @@ SmmVariableHandler ( SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName; SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo; VARIABLE_INFO_ENTRY *VariableInfo; + SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; UINTN InfoSize; UINTN NameBufferSize; UINTN CommBufferPayloadSize; @@ -635,6 +683,7 @@ SmmVariableHandler ( break; case SMM_VARIABLE_FUNCTION_READY_TO_BOOT: + mEndOfDxe = TRUE; if (AtRuntime()) { Status = EFI_UNSUPPORTED; break; @@ -667,6 +716,51 @@ SmmVariableHandler ( *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; break; + case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE: + if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name)) { + DEBUG ((EFI_D_ERROR, "RequestToLock: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + // + // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload. + // + CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize); + VariableToLock = (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *) mVariableBufferPayload; + + if (VariableToLock->NameSize > MAX_ADDRESS - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name)) { + // + // Prevent InfoSize overflow happen + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + if (VariableToLock->NameSize < sizeof (CHAR16) || VariableToLock->Name[VariableToLock->NameSize/sizeof (CHAR16) - 1] != L'\0') { + // + // Make sure VariableName is A Null-terminated string. + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + InfoSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name) + VariableToLock->NameSize; + + // + // SMRAM range check already covered before + // + if (InfoSize > CommBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + Status = VariableLockRequestToLock ( + NULL, + VariableToLock->Name, + &VariableToLock->Guid + ); + break; + default: Status = EFI_UNSUPPORTED; } @@ -678,6 +772,28 @@ EXIT: return EFI_SUCCESS; } +/** + SMM END_OF_DXE protocol notification event handler. + + @param Protocol Points to the protocol's unique identifier + @param Interface Points to the interface instance + @param Handle The handle on which the interface was installed + + @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully + +**/ +EFI_STATUS +EFIAPI +SmmEndOfDxeCallback ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + DEBUG ((EFI_D_INFO, "[Variable]END_OF_DXE is signaled\n")); + mEndOfDxe = TRUE; + return EFI_SUCCESS; +} /** SMM Fault Tolerant Write protocol notification event handler. @@ -774,6 +890,7 @@ VariableServiceInitialize ( VOID *SmmFtwRegistration; EFI_SMM_ACCESS2_PROTOCOL *SmmAccess; UINTN Size; + VOID *SmmEndOfDxeRegistration; // // Variable initialize. @@ -843,6 +960,16 @@ VariableServiceInitialize ( ); ASSERT_EFI_ERROR (Status); + // + // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function. + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmEndOfDxeProtocolGuid, + SmmEndOfDxeCallback, + &SmmEndOfDxeRegistration + ); + ASSERT_EFI_ERROR (Status); + // // Register FtwNotificationEvent () notify function. // diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf index e2c5d9c40d..c229805bc9 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf @@ -14,7 +14,7 @@ # This external input must be validated carefully to avoid security issue like # buffer overflow, integer overflow. # -# Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.
+# Copyright (c) 2010 - 2013, 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 @@ -69,6 +69,7 @@ gEfiSmmVariableProtocolGuid ## ALWAYS_PRODUCES gEfiSmmFaultTolerantWriteProtocolGuid ## SOMETIMES_CONSUMES gEfiSmmAccess2ProtocolGuid ## ALWAYS_CONSUMES + gEfiSmmEndOfDxeProtocolGuid ## ALWAYS_CONSUMES [Guids] gEfiVariableGuid ## PRODUCES ## Configuration Table Guid diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c index eb67bae748..865b9ad1a4 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -19,6 +19,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include #include @@ -44,6 +45,7 @@ UINT8 *mVariableBufferPhysical = NULL; UINTN mVariableBufferSize; UINTN mVariableBufferPayloadSize; EFI_LOCK mVariableServicesLock; +EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock; /** Acquires lock only at boot time. Simply returns at runtime. @@ -160,6 +162,63 @@ 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 PayloadSize; + SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; + + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == 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 + PayloadSize. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name) + StrSize (VariableName); + 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 = StrSize (VariableName); + CopyMem (VariableToLock->Name, VariableName, VariableToLock->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} /** This code finds variable in storage blocks (Volatile or Non-Volatile). @@ -722,6 +781,7 @@ VariableSmmRuntimeInitialize ( IN EFI_SYSTEM_TABLE *SystemTable ) { + EFI_STATUS Status; VOID *SmmVariableRegistration; VOID *SmmVariableWriteRegistration; EFI_EVENT OnReadyToBootEvent; @@ -729,6 +789,15 @@ VariableSmmRuntimeInitialize ( EfiInitializeLock (&mVariableServicesLock, TPL_NOTIFY); + mVariableLock.RequestToLock = VariableLockRequestToLock; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVariableLockProtocolGuid, + &mVariableLock, + NULL + ); + ASSERT_EFI_ERROR (Status); + // // Smm variable service is ready // diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf index 05dd49d368..761ea66e6b 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf @@ -4,7 +4,7 @@ # This module is the Runtime DXE part correspond to SMM variable module. It # installs variable arch protocol and variable write arch protocol and works # with SMM variable module together. -# Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.
+# Copyright (c) 2010 - 2013, 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 @@ -54,6 +54,7 @@ gEfiVariableArchProtocolGuid ## ALWAYS_PRODUCES gEfiSmmCommunicationProtocolGuid gEfiSmmVariableProtocolGuid + gEdkiiVariableLockProtocolGuid ## ALWAYS_PRODUCES [Guids] gEfiEventVirtualAddressChangeGuid ## PRODUCES ## Event