| /** @file | |
| Implement all four UEFI Runtime Variable services for the nonvolatile | |
| and volatile storage space and install variable architecture protocol. | |
| Copyright (C) 2013, Red Hat, Inc. | |
| Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> | |
| (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> | |
| Copyright (c) Microsoft Corporation. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Variable.h" | |
| #include <Protocol/VariablePolicy.h> | |
| #include <Library/VariablePolicyLib.h> | |
| EFI_STATUS | |
| EFIAPI | |
| ProtocolIsVariablePolicyEnabled ( | |
| OUT BOOLEAN *State | |
| ); | |
| EFI_HANDLE mHandle = NULL; | |
| EFI_EVENT mVirtualAddressChangeEvent = NULL; | |
| VOID *mFtwRegistration = NULL; | |
| VOID ***mVarCheckAddressPointer = NULL; | |
| UINTN mVarCheckAddressPointerCount = 0; | |
| EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock = { VariableLockRequestToLock }; | |
| EDKII_VARIABLE_POLICY_PROTOCOL mVariablePolicyProtocol = { | |
| EDKII_VARIABLE_POLICY_PROTOCOL_REVISION, | |
| DisableVariablePolicy, | |
| ProtocolIsVariablePolicyEnabled, | |
| RegisterVariablePolicy, | |
| DumpVariablePolicy, | |
| LockVariablePolicy, | |
| GetVariablePolicyInfo, | |
| GetLockOnVariableStateVariablePolicyInfo | |
| }; | |
| EDKII_VAR_CHECK_PROTOCOL mVarCheck = { | |
| VarCheckRegisterSetVariableCheckHandler, | |
| VarCheckVariablePropertySet, | |
| VarCheckVariablePropertyGet | |
| }; | |
| /** | |
| 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 | |
| ); | |
| /** | |
| Return TRUE if ExitBootServices () has been called. | |
| @retval TRUE If ExitBootServices () has been called. | |
| **/ | |
| BOOLEAN | |
| AtRuntime ( | |
| VOID | |
| ) | |
| { | |
| return EfiAtRuntime (); | |
| } | |
| /** | |
| Initializes a basic mutual exclusion lock. | |
| This function initializes a basic mutual exclusion lock to the released state | |
| and returns the lock. Each lock provides mutual exclusion access at its task | |
| priority level. Since there is no preemption or multiprocessor support in EFI, | |
| acquiring the lock only consists of raising to the locks TPL. | |
| If Lock is NULL, then ASSERT(). | |
| If Priority is not a valid TPL value, then ASSERT(). | |
| @param Lock A pointer to the lock data structure to initialize. | |
| @param Priority EFI TPL is associated with the lock. | |
| @return The lock. | |
| **/ | |
| EFI_LOCK * | |
| InitializeLock ( | |
| IN OUT EFI_LOCK *Lock, | |
| IN EFI_TPL Priority | |
| ) | |
| { | |
| return EfiInitializeLock (Lock, Priority); | |
| } | |
| /** | |
| 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 (!AtRuntime ()) { | |
| 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 (!AtRuntime ()) { | |
| EfiReleaseLock (Lock); | |
| } | |
| } | |
| /** | |
| Retrieve the Fault Tolerent Write protocol interface. | |
| @param[out] FtwProtocol The interface of Ftw protocol | |
| @retval EFI_SUCCESS The FTW protocol instance was found and returned in FtwProtocol. | |
| @retval EFI_NOT_FOUND The FTW protocol instance was not found. | |
| @retval EFI_INVALID_PARAMETER SarProtocol is NULL. | |
| **/ | |
| EFI_STATUS | |
| GetFtwProtocol ( | |
| OUT VOID **FtwProtocol | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Locate Fault Tolerent Write protocol | |
| // | |
| Status = gBS->LocateProtocol ( | |
| &gEfiFaultTolerantWriteProtocolGuid, | |
| NULL, | |
| FtwProtocol | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Retrieve the FVB protocol interface by HANDLE. | |
| @param[in] FvBlockHandle The handle of FVB protocol that provides services for | |
| reading, writing, and erasing the target block. | |
| @param[out] FvBlock The interface of FVB protocol | |
| @retval EFI_SUCCESS The interface information for the specified protocol was returned. | |
| @retval EFI_UNSUPPORTED The device does not support the FVB protocol. | |
| @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. | |
| **/ | |
| EFI_STATUS | |
| GetFvbByHandle ( | |
| IN EFI_HANDLE FvBlockHandle, | |
| OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock | |
| ) | |
| { | |
| // | |
| // To get the FVB protocol interface on the handle | |
| // | |
| return gBS->HandleProtocol ( | |
| FvBlockHandle, | |
| &gEfiFirmwareVolumeBlockProtocolGuid, | |
| (VOID **)FvBlock | |
| ); | |
| } | |
| /** | |
| Function returns an array of handles that support the FVB protocol | |
| in a buffer allocated from pool. | |
| @param[out] NumberHandles The number of handles returned in Buffer. | |
| @param[out] Buffer A pointer to the buffer to return the requested | |
| array of handles that support FVB protocol. | |
| @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of | |
| handles in Buffer was returned in NumberHandles. | |
| @retval EFI_NOT_FOUND No FVB handle was found. | |
| @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. | |
| @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. | |
| **/ | |
| EFI_STATUS | |
| GetFvbCountAndBuffer ( | |
| OUT UINTN *NumberHandles, | |
| OUT EFI_HANDLE **Buffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Locate all handles of Fvb protocol | |
| // | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiFirmwareVolumeBlockProtocolGuid, | |
| NULL, | |
| NumberHandles, | |
| Buffer | |
| ); | |
| return Status; | |
| } | |
| /** | |
| 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 Event Event whose notification function is being invoked. | |
| @param Context Pointer to the notification function's context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| VariableClassAddressChangeEvent ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| UINTN Index; | |
| if (mVariableModuleGlobal->FvbInstance != NULL) { | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->GetBlockSize); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->GetPhysicalAddress); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->GetAttributes); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->SetAttributes); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->Read); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->Write); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->EraseBlocks); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance); | |
| } | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->PlatformLangCodes); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->LangCodes); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->PlatformLang); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->VariableGlobal.HobVariableBase); | |
| EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal); | |
| EfiConvertPointer (0x0, (VOID **)&mNvVariableCache); | |
| EfiConvertPointer (0x0, (VOID **)&mNvFvHeaderCache); | |
| if (mAuthContextOut.AddressPointer != NULL) { | |
| for (Index = 0; Index < mAuthContextOut.AddressPointerCount; Index++) { | |
| EfiConvertPointer (0x0, (VOID **)mAuthContextOut.AddressPointer[Index]); | |
| } | |
| } | |
| if (mVarCheckAddressPointer != NULL) { | |
| for (Index = 0; Index < mVarCheckAddressPointerCount; Index++) { | |
| EfiConvertPointer (0x0, (VOID **)mVarCheckAddressPointer[Index]); | |
| } | |
| } | |
| } | |
| /** | |
| Notification function of EVT_GROUP_READY_TO_BOOT event group. | |
| This is a notification function registered on EVT_GROUP_READY_TO_BOOT event group. | |
| When the Boot Manager is about to load and execute a boot option, it reclaims variable | |
| storage if free size is below the threshold. | |
| @param Event Event whose notification function is being invoked. | |
| @param Context Pointer to the notification function's context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| OnReadyToBoot ( | |
| EFI_EVENT Event, | |
| VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| if (!mEndOfDxe) { | |
| MorLockInitAtEndOfDxe (); | |
| Status = LockVariablePolicy (); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Set the End Of DXE bit in case the EFI_END_OF_DXE_EVENT_GROUP_GUID event is not signaled. | |
| // | |
| mEndOfDxe = TRUE; | |
| mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount); | |
| // | |
| // The initialization for variable quota. | |
| // | |
| InitializeVariableQuota (); | |
| } | |
| ReclaimForOS (); | |
| if (FeaturePcdGet (PcdVariableCollectStatistics)) { | |
| if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { | |
| gBS->InstallConfigurationTable (&gEfiAuthenticatedVariableGuid, gVariableInfo); | |
| } else { | |
| gBS->InstallConfigurationTable (&gEfiVariableGuid, gVariableInfo); | |
| } | |
| } | |
| gBS->CloseEvent (Event); | |
| } | |
| /** | |
| 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 | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| DEBUG ((DEBUG_INFO, "[Variable]END_OF_DXE is signaled\n")); | |
| MorLockInitAtEndOfDxe (); | |
| Status = LockVariablePolicy (); | |
| ASSERT_EFI_ERROR (Status); | |
| mEndOfDxe = TRUE; | |
| mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount); | |
| // | |
| // The initialization for variable quota. | |
| // | |
| InitializeVariableQuota (); | |
| if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) { | |
| ReclaimForOS (); | |
| } | |
| gBS->CloseEvent (Event); | |
| } | |
| /** | |
| Initializes variable write service for DXE. | |
| **/ | |
| VOID | |
| VariableWriteServiceInitializeDxe ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = VariableWriteServiceInitialize (); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status)); | |
| } | |
| // | |
| // Some Secure Boot Policy Var (SecureBoot, etc) updates following other | |
| // Secure Boot Policy Variable change. Record their initial value. | |
| // | |
| RecordSecureBootPolicyVarData (); | |
| // | |
| // Install the Variable Write Architectural protocol. | |
| // | |
| Status = gBS->InstallProtocolInterface ( | |
| &mHandle, | |
| &gEfiVariableWriteArchProtocolGuid, | |
| EFI_NATIVE_INTERFACE, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| /** | |
| Fault Tolerant Write protocol notification event handler. | |
| Non-Volatile variable write may needs FTW protocol to reclaim when | |
| writting variable. | |
| @param[in] Event Event whose notification function is being invoked. | |
| @param[in] Context Pointer to the notification function's context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| FtwNotificationEvent ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol; | |
| EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; | |
| EFI_PHYSICAL_ADDRESS NvStorageVariableBase; | |
| EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; | |
| EFI_PHYSICAL_ADDRESS BaseAddress; | |
| UINT64 Length; | |
| EFI_PHYSICAL_ADDRESS VariableStoreBase; | |
| UINT64 VariableStoreLength; | |
| UINTN FtwMaxBlockSize; | |
| UINT32 NvStorageVariableSize; | |
| UINT64 NvStorageVariableSize64; | |
| // | |
| // Ensure FTW protocol is installed. | |
| // | |
| Status = GetFtwProtocol ((VOID **)&FtwProtocol); | |
| if (EFI_ERROR (Status)) { | |
| return; | |
| } | |
| Status = GetVariableFlashNvStorageInfo (&NvStorageVariableBase, &NvStorageVariableSize64); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = SafeUint64ToUint32 (NvStorageVariableSize64, &NvStorageVariableSize); | |
| // This driver currently assumes the size will be UINT32 so assert the value is safe for now. | |
| ASSERT_EFI_ERROR (Status); | |
| VariableStoreBase = NvStorageVariableBase + mNvFvHeaderCache->HeaderLength; | |
| Status = FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize); | |
| if (!EFI_ERROR (Status)) { | |
| ASSERT (NvStorageVariableSize <= FtwMaxBlockSize); | |
| } | |
| // | |
| // Let NonVolatileVariableBase point to flash variable store base directly after FTW ready. | |
| // | |
| mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase; | |
| // | |
| // Find the proper FVB protocol for variable. | |
| // | |
| Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol); | |
| if (EFI_ERROR (Status)) { | |
| return; | |
| } | |
| mVariableModuleGlobal->FvbInstance = FvbProtocol; | |
| // | |
| // Mark the variable storage region of the FLASH as RUNTIME. | |
| // | |
| VariableStoreLength = mNvVariableCache->Size; | |
| BaseAddress = VariableStoreBase & (~EFI_PAGE_MASK); | |
| Length = VariableStoreLength + (VariableStoreBase - BaseAddress); | |
| Length = (Length + EFI_PAGE_SIZE - 1) & (~EFI_PAGE_MASK); | |
| Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_WARN, "Variable driver failed to get flash memory attribute.\n")); | |
| } else { | |
| if ((GcdDescriptor.Attributes & EFI_MEMORY_RUNTIME) == 0) { | |
| Status = gDS->SetMemorySpaceAttributes ( | |
| BaseAddress, | |
| Length, | |
| GcdDescriptor.Attributes | EFI_MEMORY_RUNTIME | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_WARN, "Variable driver failed to add EFI_MEMORY_RUNTIME attribute to Flash.\n")); | |
| } | |
| } | |
| } | |
| // | |
| // Initializes variable write service after FTW was ready. | |
| // | |
| VariableWriteServiceInitializeDxe (); | |
| // | |
| // Close the notify event to avoid install gEfiVariableWriteArchProtocolGuid again. | |
| // | |
| gBS->CloseEvent (Event); | |
| } | |
| /** | |
| This API function returns whether or not the policy engine is | |
| currently being enforced. | |
| @param[out] State Pointer to a return value for whether the policy enforcement | |
| is currently enabled. | |
| @retval EFI_SUCCESS | |
| @retval Others An error has prevented this command from completing. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ProtocolIsVariablePolicyEnabled ( | |
| OUT BOOLEAN *State | |
| ) | |
| { | |
| *State = IsVariablePolicyEnabled (); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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 | |
| VariableServiceInitialize ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_EVENT ReadyToBootEvent; | |
| EFI_EVENT EndOfDxeEvent; | |
| Status = VariableCommonInitialize (); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &mHandle, | |
| &gEdkiiVariableLockProtocolGuid, | |
| &mVariableLock, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &mHandle, | |
| &gEdkiiVarCheckProtocolGuid, | |
| &mVarCheck, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| SystemTable->RuntimeServices->GetVariable = VariableServiceGetVariable; | |
| SystemTable->RuntimeServices->GetNextVariableName = VariableServiceGetNextVariableName; | |
| SystemTable->RuntimeServices->SetVariable = VariableServiceSetVariable; | |
| SystemTable->RuntimeServices->QueryVariableInfo = VariableServiceQueryVariableInfo; | |
| // | |
| // Now install the Variable Runtime Architectural protocol on a new handle. | |
| // | |
| Status = gBS->InstallProtocolInterface ( | |
| &mHandle, | |
| &gEfiVariableArchProtocolGuid, | |
| EFI_NATIVE_INTERFACE, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (!PcdGetBool (PcdEmuVariableNvModeEnable)) { | |
| // | |
| // Register FtwNotificationEvent () notify function. | |
| // | |
| EfiCreateProtocolNotifyEvent ( | |
| &gEfiFaultTolerantWriteProtocolGuid, | |
| TPL_CALLBACK, | |
| FtwNotificationEvent, | |
| (VOID *)SystemTable, | |
| &mFtwRegistration | |
| ); | |
| } else { | |
| // | |
| // Emulated non-volatile variable mode does not depend on FVB and FTW. | |
| // | |
| VariableWriteServiceInitializeDxe (); | |
| } | |
| Status = gBS->CreateEventEx ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_NOTIFY, | |
| VariableClassAddressChangeEvent, | |
| NULL, | |
| &gEfiEventVirtualAddressChangeGuid, | |
| &mVirtualAddressChangeEvent | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Register the event handling function to reclaim variable for OS usage. | |
| // | |
| Status = EfiCreateEventReadyToBootEx ( | |
| TPL_NOTIFY, | |
| OnReadyToBoot, | |
| 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_CALLBACK, | |
| OnEndOfDxe, | |
| NULL, | |
| &gEfiEndOfDxeEventGroupGuid, | |
| &EndOfDxeEvent | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // Register and initialize the VariablePolicy engine. | |
| Status = InitVariablePolicyLib (VariableServiceGetVariable); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = VarCheckRegisterSetVariableCheckHandler (ValidateSetVariable); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &mHandle, | |
| &gEdkiiVariablePolicyProtocolGuid, | |
| &mVariablePolicyProtocol, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| return EFI_SUCCESS; | |
| } |