X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=FmpDevicePkg%2FFmpDxe%2FVariableSupport.c;h=86dd5b203b73159718128fbc11089fe10d4d09a1;hp=57f4388df292e1e5baedb4f0dc83e224da421035;hb=HEAD;hpb=bcef758cbac5f3e136e7ec458e3690e615591ca0 diff --git a/FmpDevicePkg/FmpDxe/VariableSupport.c b/FmpDevicePkg/FmpDxe/VariableSupport.c index 57f4388df2..541e5e0f5f 100644 --- a/FmpDevicePkg/FmpDxe/VariableSupport.c +++ b/FmpDevicePkg/FmpDxe/VariableSupport.c @@ -3,81 +3,363 @@ firmware updates. Copyright (c) 2016, Microsoft Corporation. All rights reserved.
- Copyright (c) 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2018 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include -#include -#include -#include -#include -#include -#include +#include "FmpDxe.h" #include "VariableSupport.h" -/// -/// Array of UEFI variable names that are locked in LockAllFmpVariables(). -/// -const CHAR16 *mFmpVariableLockList[] = { - VARNAME_VERSION, - VARNAME_LSV, - VARNAME_LASTATTEMPTSTATUS, - VARNAME_LASTATTEMPTVERSION -}; - /** - Returns the value used to fill in the Version field of the - EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() - service of the Firmware Management Protocol. The value is read from a UEFI - variable. If the UEFI variables does not exist, then a default version value - is returned. + Retrieve the value of a 32-bit UEFI Variable specified by VariableName and + a GUID of gEfiCallerIdGuid. + + @param[in] VariableName Pointer to the UEFI Variable name to retrieve. + @param[out] Valid Set to TRUE if UEFI Variable is present and the size + of the UEFI Variable value is 32-bits. Otherwise + FALSE. + @param[out] Value If Valid is set to TRUE, then the 32-bit value of + the UEFI Variable. Otherwise 0. +**/ +static +VOID +GetFmpVariable ( + IN CHAR16 *VariableName, + OUT BOOLEAN *Valid, + OUT UINT32 *Value + ) +{ + EFI_STATUS Status; + UINTN Size; + UINT32 *Buffer; + + *Valid = FALSE; + *Value = 0; + Size = 0; + Buffer = NULL; + Status = GetVariable2 ( + VariableName, + &gEfiCallerIdGuid, + (VOID **)&Buffer, + &Size + ); + if (!EFI_ERROR (Status) && (Size == sizeof (*Value)) && (Buffer != NULL)) { + *Valid = TRUE; + *Value = *Buffer; + } - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion" + if (Buffer != NULL) { + FreePool (Buffer); + } +} - @return The version of the firmware image in the firmware device. +/** + Delete the UEFI Variable with name specified by VariableName and GUID of + gEfiCallerIdGuid. If the variable can not be deleted, then print a + DEBUG_ERROR message. + @param[in] VariableName Pointer to the UEFI Variable name to delete. **/ -UINT32 -GetVersionFromVariable ( - VOID +static +VOID +DeleteFmpVariable ( + IN CHAR16 *VariableName ) { EFI_STATUS Status; - UINT32 *Value; - UINTN Size; - UINT32 Version; + BOOLEAN Valid; + UINT32 Value; - Value = NULL; - Size = 0; - Version = DEFAULT_VERSION; + GetFmpVariable (VariableName, &Valid, &Value); + if (Valid) { + Status = gRT->SetVariable (VariableName, &gEfiCallerIdGuid, 0, 0, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to delete variable %s. Status = %r\n", mImageIdName, VariableName, Status)); + } else { + DEBUG ((DEBUG_INFO, "FmpDxe(%s): Deleted variable %s\n", mImageIdName, VariableName)); + } + } +} + +/** + Retrieve the FMP Controller State UEFI Variable value. Return NULL if + the variable does not exist or if the size of the UEFI Variable is not the + size of FMP_CONTROLLER_STATE. The buffer for the UEFI Variable value + if allocated using the UEFI Boot Service AllocatePool(). - Status = GetVariable2 (VARNAME_VERSION, &gEfiCallerIdGuid, (VOID **)&Value, &Size); - if (EFI_ERROR (Status) || (Value == NULL)) { - DEBUG ((DEBUG_ERROR, "Failed to get the Version from variable. Status = %r\n", Status)); - return Version; + @param[in] Private Private context structure for the managed controller. + + @return Pointer to the allocated FMP Controller State. Returns NULL + if the variable does not exist or is a different size than expected. +**/ +static +FMP_CONTROLLER_STATE * +GetFmpControllerState ( + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + FMP_CONTROLLER_STATE *FmpControllerState; + UINTN Size; + + FmpControllerState = NULL; + Size = 0; + Status = GetVariable2 ( + Private->FmpStateVariableName, + &gEfiCallerIdGuid, + (VOID **)&FmpControllerState, + &Size + ); + if (EFI_ERROR (Status) || (FmpControllerState == NULL)) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to get the controller state. Status = %r\n", mImageIdName, Status)); + } else { + if (Size == sizeof (*FmpControllerState)) { + return FmpControllerState; + } + + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Getting controller state returned a size different than expected. Size = 0x%x\n", mImageIdName, Size)); } + if (FmpControllerState != NULL) { + FreePool (FmpControllerState); + } + + return NULL; +} + +/** + Generates a Null-terminated Unicode string UEFI Variable name from a base name + and a hardware instance. If the hardware instance value is 0, then the base + name is returned. If the hardware instance value is non-zero, then the 64-bit + hardware instance value is converted to a 16 character hex string and appended + to base name. The UEFI Variable name returned is allocated using the UEFI + Boot Service AllocatePool(). + + @param[in] HardwareInstance 64-bit hardware instance value. + @param[in] BaseVariableName Null-terminated Unicode string that is the base + name of the UEFI Variable. + + @return Pointer to the allocated UEFI Variable name. Returns NULL if the + UEFI Variable can not be allocated. +**/ +static +CHAR16 * +GenerateFmpVariableName ( + IN UINT64 HardwareInstance, + IN CHAR16 *BaseVariableName + ) +{ + UINTN Size; + CHAR16 *VariableName; + // - // No error from call + // Allocate Unicode string with room for BaseVariableName and a 16 digit + // hexadecimal value for the HardwareInstance value. // - if (Size == sizeof (*Value)) { + Size = StrSize (BaseVariableName) + 16 * sizeof (CHAR16); + VariableName = AllocateCopyPool (Size, BaseVariableName); + if (VariableName == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to generate variable name %s.\n", mImageIdName, BaseVariableName)); + return VariableName; + } + + if (HardwareInstance == 0) { + return VariableName; + } + + UnicodeValueToStringS ( + &VariableName[StrLen (BaseVariableName)], + Size, + PREFIX_ZERO | RADIX_HEX, + HardwareInstance, + 16 + ); + return VariableName; +} + +/** + Generate the names of the UEFI Variables used to store state information for + a managed controller. The UEFI Variables names are a combination of a base + name and an optional hardware instance value as a 16 character hex value. If + the hardware instance value is 0, then the 16 character hex value is not + included. These storage for the UEFI Variable names are allocated using the + UEFI Boot Service AllocatePool() and the pointers are stored in the Private. + The following are examples of variable names produces for hardware instance + value 0 and value 0x1234567812345678. + + FmpVersion + FmpLsv + LastAttemptStatus + LastAttemptVersion + FmpState + + FmpVersion1234567812345678 + FmpLsv1234567812345678 + LastAttemptStatus1234567812345678 + LastAttemptVersion1234567812345678 + FmpState1234567812345678 + + @param[in,out] Private Private context structure for the managed controller. +**/ +VOID +GenerateFmpVariableNames ( + IN OUT FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + VOID *Buffer; + FMP_CONTROLLER_STATE FmpControllerState; + + if (Private->VersionVariableName != NULL) { + FreePool (Private->VersionVariableName); + } + + if (Private->LsvVariableName != NULL) { + FreePool (Private->LsvVariableName); + } + + if (Private->LastAttemptStatusVariableName != NULL) { + FreePool (Private->LastAttemptStatusVariableName); + } + + if (Private->LastAttemptVersionVariableName != NULL) { + FreePool (Private->LastAttemptVersionVariableName); + } + + if (Private->FmpStateVariableName != NULL) { + FreePool (Private->FmpStateVariableName); + } + + Private->VersionVariableName = GenerateFmpVariableName ( + Private->Descriptor.HardwareInstance, + VARNAME_VERSION + ); + Private->LsvVariableName = GenerateFmpVariableName ( + Private->Descriptor.HardwareInstance, + VARNAME_LSV + ); + Private->LastAttemptStatusVariableName = GenerateFmpVariableName ( + Private->Descriptor.HardwareInstance, + VARNAME_LASTATTEMPTSTATUS + ); + Private->LastAttemptVersionVariableName = GenerateFmpVariableName ( + Private->Descriptor.HardwareInstance, + VARNAME_LASTATTEMPTVERSION + ); + Private->FmpStateVariableName = GenerateFmpVariableName ( + Private->Descriptor.HardwareInstance, + VARNAME_FMPSTATE + ); + + DEBUG ((DEBUG_INFO, "FmpDxe(%s): Variable %g %s\n", mImageIdName, &gEfiCallerIdGuid, Private->VersionVariableName)); + DEBUG ((DEBUG_INFO, "FmpDxe(%s): Variable %g %s\n", mImageIdName, &gEfiCallerIdGuid, Private->LsvVariableName)); + DEBUG ((DEBUG_INFO, "FmpDxe(%s): Variable %g %s\n", mImageIdName, &gEfiCallerIdGuid, Private->LastAttemptStatusVariableName)); + DEBUG ((DEBUG_INFO, "FmpDxe(%s): Variable %g %s\n", mImageIdName, &gEfiCallerIdGuid, Private->LastAttemptVersionVariableName)); + DEBUG ((DEBUG_INFO, "FmpDxe(%s): Variable %g %s\n", mImageIdName, &gEfiCallerIdGuid, Private->FmpStateVariableName)); + + Buffer = GetFmpControllerState (Private); + if (Buffer != NULL) { // - // Successful read + // FMP Controller State was found with correct size. + // Delete old variables if they exist. // - Version = *Value; - } else { + FreePool (Buffer); + DeleteFmpVariable (Private->VersionVariableName); + DeleteFmpVariable (Private->LsvVariableName); + DeleteFmpVariable (Private->LastAttemptStatusVariableName); + DeleteFmpVariable (Private->LastAttemptVersionVariableName); + return; + } + + // + // FMP Controller State was either not found or is wrong size. + // Create a new FMP Controller State variable with the correct size. + // + DEBUG ((DEBUG_INFO, "FmpDxe(%s): Create controller state\n", mImageIdName)); + GetFmpVariable ( + Private->VersionVariableName, + &FmpControllerState.VersionValid, + &FmpControllerState.Version + ); + GetFmpVariable ( + Private->LsvVariableName, + &FmpControllerState.LsvValid, + &FmpControllerState.Lsv + ); + GetFmpVariable ( + Private->LastAttemptStatusVariableName, + &FmpControllerState.LastAttemptStatusValid, + &FmpControllerState.LastAttemptStatus + ); + GetFmpVariable ( + Private->LastAttemptVersionVariableName, + &FmpControllerState.LastAttemptVersionValid, + &FmpControllerState.LastAttemptVersion + ); + Status = gRT->SetVariable ( + Private->FmpStateVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (FmpControllerState), + &FmpControllerState + ); + if (EFI_ERROR (Status)) { // - // Return default since size was unknown + // Failed to create FMP Controller State. In this case, do not + // delete the individual variables. They can be used again on next boot + // to create the FMP Controller State. // - DEBUG ((DEBUG_ERROR, "Getting version Variable returned a size different than expected. Size = 0x%x\n", Size)); + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to create controller state. Status = %r\n", mImageIdName, Status)); + } else { + DeleteFmpVariable (Private->VersionVariableName); + DeleteFmpVariable (Private->LsvVariableName); + DeleteFmpVariable (Private->LastAttemptStatusVariableName); + DeleteFmpVariable (Private->LastAttemptVersionVariableName); } +} - FreePool (Value); +/** + Returns the value used to fill in the Version field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default version value + is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" + + @param[in] Private Private context structure for the managed controller. - return Version; + @return The version of the firmware image in the firmware device. +**/ +UINT32 +GetVersionFromVariable ( + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private + ) +{ + FMP_CONTROLLER_STATE *FmpControllerState; + UINT32 Value; + + Value = DEFAULT_VERSION; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState != NULL) { + if (FmpControllerState->VersionValid) { + Value = FmpControllerState->Version; + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Get variable %g %s Version %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + Value + )); + } + + FreePool (FmpControllerState); + } + + return Value; } /** @@ -87,50 +369,40 @@ GetVersionFromVariable ( variable. If the UEFI variables does not exist, then a default lowest supported version value is returned. - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv" + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" + + @param[in] Private Private context structure for the managed controller. @return The lowest supported version of the firmware image in the firmware device. - **/ UINT32 GetLowestSupportedVersionFromVariable ( - VOID + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private ) { - EFI_STATUS Status; - UINT32 *Value; - UINTN Size; - UINT32 Version; - - Value = NULL; - Size = 0; - Version = DEFAULT_LOWESTSUPPORTEDVERSION; + FMP_CONTROLLER_STATE *FmpControllerState; + UINT32 Value; + + Value = DEFAULT_LOWESTSUPPORTEDVERSION; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState != NULL) { + if (FmpControllerState->LsvValid) { + Value = FmpControllerState->Lsv; + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Get variable %g %s LowestSupportedVersion %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + Value + )); + } - Status = GetVariable2 (VARNAME_LSV, &gEfiCallerIdGuid, (VOID **)&Value, &Size); - if (EFI_ERROR (Status) || (Value == NULL)) { - DEBUG ((DEBUG_WARN, "Warning: Failed to get the Lowest Supported Version from variable. Status = %r\n", Status)); - return Version; + FreePool (FmpControllerState); } - // - // No error from call - // - if (Size == sizeof (*Value)) { - // - // Successful read - // - Version = *Value; - } else { - // - // Return default since size was unknown - // - DEBUG ((DEBUG_ERROR, "Getting LSV Variable returned a size different than expected. Size = 0x%x\n", Size)); - } - - FreePool (Value); - - return Version; + return Value; } /** @@ -140,53 +412,39 @@ GetLowestSupportedVersionFromVariable ( variable. If the UEFI variables does not exist, then a default last attempt status value is returned. - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus" + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" - @return The last attempt status value for the most recent capsule update. + @param[in] Private Private context structure for the managed controller. + @return The last attempt status value for the most recent capsule update. **/ UINT32 GetLastAttemptStatusFromVariable ( - VOID + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private ) { - EFI_STATUS Status; - UINT32 *Value; - UINTN Size; - UINT32 LastAttemptStatus; - - Value = NULL; - Size = 0; - LastAttemptStatus = DEFAULT_LASTATTEMPT; - - Status = GetVariable2 (VARNAME_LASTATTEMPTSTATUS, &gEfiCallerIdGuid, (VOID **)&Value, &Size); - if (EFI_ERROR (Status) || (Value == NULL)) { - DEBUG ((DEBUG_WARN, "Warning: Failed to get the Last Attempt Status from variable. Status = %r\n", Status)); - return LastAttemptStatus; - } + FMP_CONTROLLER_STATE *FmpControllerState; + UINT32 Value; + + Value = DEFAULT_LASTATTEMPTSTATUS; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState != NULL) { + if (FmpControllerState->LastAttemptStatusValid) { + Value = FmpControllerState->LastAttemptStatus; + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Get variable %g %s LastAttemptStatus %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + Value + )); + } - // - // No error from call - // - if (Size == sizeof (*Value)) { - // - // Successful read - // - LastAttemptStatus = *Value; - } else { - // - // Return default since size was unknown - // - DEBUG ( - (DEBUG_ERROR, - "Getting Last Attempt Status Variable returned a size different than expected. Size = 0x%x\n", - Size) - ); + FreePool (FmpControllerState); } - FreePool (Value); - - return LastAttemptStatus; + return Value; } /** @@ -196,249 +454,394 @@ GetLastAttemptStatusFromVariable ( variable. If the UEFI variables does not exist, then a default last attempt version value is returned. - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion" + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" - @return The last attempt version value for the most recent capsule update. + @param[in] Private Private context structure for the managed controller. + @return The last attempt version value for the most recent capsule update. **/ UINT32 GetLastAttemptVersionFromVariable ( - VOID + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private ) { - EFI_STATUS Status; - UINT32 *Value; - UINTN Size; - UINT32 Version; - - Value = NULL; - Size = 0; - Version = DEFAULT_LASTATTEMPT; + FMP_CONTROLLER_STATE *FmpControllerState; + UINT32 Value; + + Value = DEFAULT_LASTATTEMPTVERSION; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState != NULL) { + if (FmpControllerState->LastAttemptVersionValid) { + Value = FmpControllerState->LastAttemptVersion; + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Get variable %g %s LastAttemptVersion %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + Value + )); + } - Status = GetVariable2 (VARNAME_LASTATTEMPTVERSION, &gEfiCallerIdGuid, (VOID **)&Value, &Size); - if (EFI_ERROR (Status) || (Value == NULL)) { - DEBUG ((DEBUG_WARN, "Warning: Failed to get the Last Attempt Version from variable. Status = %r\n", Status)); - return Version; + FreePool (FmpControllerState); } - // - // No error from call - // - if (Size == sizeof (*Value)) { - // - // Successful read - // - Version = *Value; - } else { - // - // Return default since size was unknown - // - DEBUG ( - (DEBUG_ERROR, - "Getting Last Attempt Version variable returned a size different than expected. Size = 0x%x\n", - Size) - ); - } - - FreePool (Value); - - return Version; + return Value; } - /** Saves the version current of the firmware image in the firmware device to a UEFI variable. - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion" + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" + @param[in] Private Private context structure for the managed controller. @param[in] Version The version of the firmware image in the firmware device. - **/ VOID SetVersionInVariable ( - UINT32 Version + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private, + IN UINT32 Version ) { - EFI_STATUS Status; - UINT32 Current; + EFI_STATUS Status; + FMP_CONTROLLER_STATE *FmpControllerState; + BOOLEAN Update; - Status = EFI_SUCCESS; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState == NULL) { + // + // Can not update value if FMP Controller State does not exist. + // This variable is guaranteed to be created by GenerateFmpVariableNames(). + // + return; + } - Current = GetVersionFromVariable(); - if (Current != Version) { - Status = gRT->SetVariable ( - VARNAME_VERSION, - &gEfiCallerIdGuid, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - sizeof (Version), - &Version - ); + Update = FALSE; + if (!FmpControllerState->VersionValid) { + Update = TRUE; + } + + if (FmpControllerState->Version != Version) { + Update = TRUE; + } + + if (!Update) { + DEBUG ((DEBUG_INFO, "FmpDxe(%s): No need to update controller state. Same value as before.\n", mImageIdName)); + } else { + FmpControllerState->VersionValid = TRUE; + FmpControllerState->Version = Version; + Status = gRT->SetVariable ( + Private->FmpStateVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (*FmpControllerState), + FmpControllerState + ); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to set the Version into a variable. Status = %r\n", Status)); + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to update controller state. Status = %r\n", mImageIdName, Status)); + } else { + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Set variable %g %s Version %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + Version + )); } - } else { - DEBUG ((DEBUG_INFO, "Version variable doesn't need to update. Same value as before.\n")); } + + FreePool (FmpControllerState); } /** Saves the lowest supported version current of the firmware image in the firmware device to a UEFI variable. - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv" - - @param[in] LowestSupportedVersion The lowest supported version of the firmware image - in the firmware device. + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" + @param[in] Private Private context structure for the managed + controller. + @param[in] LowestSupportedVersion The lowest supported version of the + firmware image in the firmware device. **/ VOID SetLowestSupportedVersionInVariable ( - UINT32 LowestSupportedVersion + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private, + IN UINT32 LowestSupportedVersion ) { - EFI_STATUS Status; - UINT32 Current; + EFI_STATUS Status; + FMP_CONTROLLER_STATE *FmpControllerState; + BOOLEAN Update; - Status = EFI_SUCCESS; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState == NULL) { + // + // Can not update value if FMP Controller State does not exist. + // This variable is guaranteed to be created by GenerateFmpVariableNames(). + // + return; + } - Current = GetLowestSupportedVersionFromVariable(); - if (LowestSupportedVersion > Current) { - Status = gRT->SetVariable ( - VARNAME_LSV, - &gEfiCallerIdGuid, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - sizeof (LowestSupportedVersion), &LowestSupportedVersion - ); + Update = FALSE; + if (!FmpControllerState->LsvValid) { + Update = TRUE; + } + + if (FmpControllerState->Lsv < LowestSupportedVersion) { + Update = TRUE; + } + + if (!Update) { + DEBUG ((DEBUG_INFO, "FmpDxe(%s): No need to update controller state. Same value as before.\n", mImageIdName)); + } else { + FmpControllerState->LsvValid = TRUE; + FmpControllerState->Lsv = LowestSupportedVersion; + Status = gRT->SetVariable ( + Private->FmpStateVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (*FmpControllerState), + FmpControllerState + ); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to set the LSV into a variable. Status = %r\n", Status)); + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to update controller state. Status = %r\n", mImageIdName, Status)); + } else { + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Set variable %g %s LowestSupportedVersion %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + LowestSupportedVersion + )); } - } else { - DEBUG ((DEBUG_INFO, "LSV variable doesn't need to update. Same value as before.\n")); } + + FreePool (FmpControllerState); } /** Saves the last attempt status value of the most recent FMP capsule update to a UEFI variable. - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus" + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" + @param[in] Private Private context structure for the managed + controller. @param[in] LastAttemptStatus The last attempt status of the most recent FMP capsule update. - **/ VOID SetLastAttemptStatusInVariable ( - UINT32 LastAttemptStatus + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private, + IN UINT32 LastAttemptStatus ) { - EFI_STATUS Status; - UINT32 Current; + EFI_STATUS Status; + FMP_CONTROLLER_STATE *FmpControllerState; + BOOLEAN Update; - Status = EFI_SUCCESS; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState == NULL) { + // + // Can not update value if FMP Controller State does not exist. + // This variable is guaranteed to be created by GenerateFmpVariableNames(). + // + return; + } - Current = GetLastAttemptStatusFromVariable(); - if (Current != LastAttemptStatus) { - Status = gRT->SetVariable ( - VARNAME_LASTATTEMPTSTATUS, - &gEfiCallerIdGuid, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - sizeof (LastAttemptStatus), - &LastAttemptStatus - ); + Update = FALSE; + if (!FmpControllerState->LastAttemptStatusValid) { + Update = TRUE; + } + + if (FmpControllerState->LastAttemptStatus != LastAttemptStatus) { + Update = TRUE; + } + + if (!Update) { + DEBUG ((DEBUG_INFO, "FmpDxe(%s): No need to update controller state. Same value as before.\n", mImageIdName)); + } else { + FmpControllerState->LastAttemptStatusValid = TRUE; + FmpControllerState->LastAttemptStatus = LastAttemptStatus; + Status = gRT->SetVariable ( + Private->FmpStateVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (*FmpControllerState), + FmpControllerState + ); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to set the LastAttemptStatus into a variable. Status = %r\n", Status)); + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to update controller state. Status = %r\n", mImageIdName, Status)); + } else { + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Set variable %g %s LastAttemptStatus %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + LastAttemptStatus + )); } - } else { - DEBUG ((DEBUG_INFO, "LastAttemptStatus variable doesn't need to update. Same value as before.\n")); } + + FreePool (FmpControllerState); } /** Saves the last attempt version value of the most recent FMP capsule update to a UEFI variable. - UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion" + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpState" + @param[in] Private Private context structure for the managed + controller. @param[in] LastAttemptVersion The last attempt version value of the most recent FMP capsule update. - **/ VOID SetLastAttemptVersionInVariable ( - UINT32 LastAttemptVersion + IN FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private, + IN UINT32 LastAttemptVersion ) { - EFI_STATUS Status; - UINT32 Current; + EFI_STATUS Status; + FMP_CONTROLLER_STATE *FmpControllerState; + BOOLEAN Update; - Status = EFI_SUCCESS; + FmpControllerState = GetFmpControllerState (Private); + if (FmpControllerState == NULL) { + // + // Can not update value if FMP Controller State does not exist. + // This variable is guaranteed to be created by GenerateFmpVariableNames(). + // + return; + } + + Update = FALSE; + if (!FmpControllerState->LastAttemptVersionValid) { + Update = TRUE; + } - Current = GetLastAttemptVersionFromVariable(); - if (Current != LastAttemptVersion) { - Status = gRT->SetVariable ( - VARNAME_LASTATTEMPTVERSION, - &gEfiCallerIdGuid, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - sizeof (LastAttemptVersion), - &LastAttemptVersion - ); + if (FmpControllerState->LastAttemptVersion != LastAttemptVersion) { + Update = TRUE; + } + + if (!Update) { + DEBUG ((DEBUG_INFO, "FmpDxe(%s): No need to update controller state. Same value as before.\n", mImageIdName)); + } else { + FmpControllerState->LastAttemptVersionValid = TRUE; + FmpControllerState->LastAttemptVersion = LastAttemptVersion; + Status = gRT->SetVariable ( + Private->FmpStateVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (*FmpControllerState), + FmpControllerState + ); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to set the LastAttemptVersion into a variable. Status = %r\n", Status)); + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to update controller state. Status = %r\n", mImageIdName, Status)); + } else { + DEBUG (( + DEBUG_INFO, + "FmpDxe(%s): Set variable %g %s LastAttemptVersion %08x\n", + mImageIdName, + &gEfiCallerIdGuid, + Private->FmpStateVariableName, + LastAttemptVersion + )); } - } else { - DEBUG ((DEBUG_INFO, "LastAttemptVersion variable doesn't need to update. Same value as before.\n")); } + + FreePool (FmpControllerState); } /** - Locks all the UEFI Variables used by this module. + Attempts to lock a single UEFI Variable propagating the error state of the + first lock attempt that fails. Uses gEfiCallerIdGuid as the variable GUID. + + @param[in] PreviousStatus The previous UEFI Variable lock attempt status. + @param[in] VariableLock The EDK II Variable Lock Protocol instance. + @param[in] VariableName The name of the UEFI Variable to lock. + + @retval EFI_SUCCESS The UEFI Variable was locked and the previous variable + lock attempt also succeeded. + @retval Other The UEFI Variable could not be locked or the previous + variable lock attempt failed. +**/ +static +EFI_STATUS +LockFmpVariable ( + IN EFI_STATUS PreviousStatus, + IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy, + IN CHAR16 *VariableName + ) +{ + EFI_STATUS Status; + + // If success, go ahead and set the policies to protect the target variables. + Status = RegisterBasicVariablePolicy ( + VariablePolicy, + &gEfiCallerIdGuid, + VariableName, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "FmpDxe(%s): Failed to lock variable %g %s. Status = %r\n", + mImageIdName, + &gEfiCallerIdGuid, + VariableName, + Status + )); + } + + if (EFI_ERROR (PreviousStatus)) { + return PreviousStatus; + } + + return Status; +} + +/** + Locks all the UEFI Variables that use gEfiCallerIdGuid of the currently + executing module. + + @param[in] Private Private context structure for the managed controller. @retval EFI_SUCCESS All UEFI variables are locked. @retval EFI_UNSUPPORTED Variable Lock Protocol not found. @retval Other One of the UEFI variables could not be locked. - **/ EFI_STATUS LockAllFmpVariables ( - VOID + FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private ) { - EFI_STATUS Status; - EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; - EFI_STATUS ReturnStatus; - UINTN Index; - - VariableLock = NULL; - Status = gBS->LocateProtocol ( - &gEdkiiVariableLockProtocolGuid, - NULL, - (VOID **)&VariableLock - ); + EFI_STATUS Status; + EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy; + + // Locate the VariablePolicy protocol. + Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to locate Variable Lock Protocol (%r).\n", Status)); - return EFI_UNSUPPORTED; + DEBUG ((DEBUG_ERROR, "FmpDxe %a - Could not locate VariablePolicy protocol! %r\n", __FUNCTION__, Status)); + return Status; } - ReturnStatus = EFI_SUCCESS; - for (Index = 0; Index < ARRAY_SIZE (mFmpVariableLockList); Index++) { - Status = VariableLock->RequestToLock ( - VariableLock, - (CHAR16 *)mFmpVariableLockList[Index], - &gEfiCallerIdGuid - ); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to lock variable %g %s. Status = %r\n", - &gEfiCallerIdGuid, - mFmpVariableLockList[Index], - Status - )); - if (!EFI_ERROR (ReturnStatus)) { - ReturnStatus = Status; - } - } - } + Status = EFI_SUCCESS; + Status = LockFmpVariable (Status, VariablePolicy, Private->VersionVariableName); + Status = LockFmpVariable (Status, VariablePolicy, Private->LsvVariableName); + Status = LockFmpVariable (Status, VariablePolicy, Private->LastAttemptStatusVariableName); + Status = LockFmpVariable (Status, VariablePolicy, Private->LastAttemptVersionVariableName); + Status = LockFmpVariable (Status, VariablePolicy, Private->FmpStateVariableName); - return ReturnStatus; + return Status; }