From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../SystemFirmwareUpdate/SystemFirmwareUpdateDxe.c | 887 +++++++++++++++++++++ 1 file changed, 887 insertions(+) create mode 100644 roms/edk2/SignedCapsulePkg/Universal/SystemFirmwareUpdate/SystemFirmwareUpdateDxe.c (limited to 'roms/edk2/SignedCapsulePkg/Universal/SystemFirmwareUpdate/SystemFirmwareUpdateDxe.c') diff --git a/roms/edk2/SignedCapsulePkg/Universal/SystemFirmwareUpdate/SystemFirmwareUpdateDxe.c b/roms/edk2/SignedCapsulePkg/Universal/SystemFirmwareUpdate/SystemFirmwareUpdateDxe.c new file mode 100644 index 000000000..ea795cd7d --- /dev/null +++ b/roms/edk2/SignedCapsulePkg/Universal/SystemFirmwareUpdate/SystemFirmwareUpdateDxe.c @@ -0,0 +1,887 @@ +/** @file + SetImage instance to update system firmware. + + Caution: This module requires additional review when modified. + This module will have external input - capsule image. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + FmpSetImage() will receive untrusted input and do basic validation. + + Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SystemFirmwareDxe.h" + +// +// SystemFmp driver private data +// +SYSTEM_FMP_PRIVATE_DATA *mSystemFmpPrivate = NULL; + +EFI_GUID mCurrentImageTypeId; + +BOOLEAN mNvRamUpdated = FALSE; + +/** + Parse Config data file to get the updated data array. + + @param[in] DataBuffer Config raw file buffer. + @param[in] BufferSize Size of raw buffer. + @param[in, out] ConfigHeader Pointer to the config header. + @param[in, out] UpdateArray Pointer to the config of update data. + + @retval EFI_NOT_FOUND No config data is found. + @retval EFI_OUT_OF_RESOURCES No enough memory is allocated. + @retval EFI_SUCCESS Parse the config file successfully. + +**/ +EFI_STATUS +ParseUpdateDataFile ( + IN UINT8 *DataBuffer, + IN UINTN BufferSize, + IN OUT CONFIG_HEADER *ConfigHeader, + IN OUT UPDATE_CONFIG_DATA **UpdateArray + ); + +/** + Update System Firmware image component. + + @param[in] SystemFirmwareImage Points to the System Firmware image. + @param[in] SystemFirmwareImageSize The length of the System Firmware image in bytes. + @param[in] ConfigData Points to the component configuration structure. + @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] Progress A function used by the driver to report the progress of the firmware update. + @param[in] StartPercentage The start completion percentage value that may be used to report progress during the flash write operation. + @param[in] EndPercentage The end completion percentage value that may be used to report progress during the flash write operation. + + @retval EFI_SUCCESS The System Firmware image is updated. + @retval EFI_WRITE_PROTECTED The flash device is read only. +**/ +EFI_STATUS +PerformUpdate ( + IN VOID *SystemFirmwareImage, + IN UINTN SystemFirmwareImageSize, + IN UPDATE_CONFIG_DATA *ConfigData, + OUT UINT32 *LastAttemptVersion, + OUT UINT32 *LastAttemptStatus, + IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress, + IN UINTN StartPercentage, + IN UINTN EndPercentage + ) +{ + EFI_STATUS Status; + + DEBUG((DEBUG_INFO, "PlatformUpdate:")); + DEBUG((DEBUG_INFO, " BaseAddress - 0x%lx,", ConfigData->BaseAddress)); + DEBUG((DEBUG_INFO, " ImageOffset - 0x%x,", ConfigData->ImageOffset)); + DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ConfigData->Length)); + if (Progress != NULL) { + Progress (StartPercentage); + } + Status = PerformFlashWriteWithProgress ( + ConfigData->FirmwareType, + ConfigData->BaseAddress, + ConfigData->AddressType, + (VOID *)((UINTN)SystemFirmwareImage + (UINTN)ConfigData->ImageOffset), + ConfigData->Length, + Progress, + StartPercentage, + EndPercentage + ); + if (Progress != NULL) { + Progress (EndPercentage); + } + if (!EFI_ERROR(Status)) { + *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; + if (ConfigData->FirmwareType == PlatformFirmwareTypeNvRam) { + mNvRamUpdated = TRUE; + } + } else { + *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; + } + return Status; +} + +/** + Update System Firmware image. + + @param[in] SystemFirmwareImage Points to the System Firmware image. + @param[in] SystemFirmwareImageSize The length of the System Firmware image in bytes. + @param[in] ConfigImage Points to the config file image. + @param[in] ConfigImageSize The length of the config file image in bytes. + @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] Progress A function used by the driver to report the progress of the firmware update. + + @retval EFI_SUCCESS The System Firmware image is updated. + @retval EFI_WRITE_PROTECTED The flash device is read only. +**/ +EFI_STATUS +UpdateImage ( + IN VOID *SystemFirmwareImage, + IN UINTN SystemFirmwareImageSize, + IN VOID *ConfigImage, + IN UINTN ConfigImageSize, + OUT UINT32 *LastAttemptVersion, + OUT UINT32 *LastAttemptStatus, + IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress + ) +{ + EFI_STATUS Status; + UPDATE_CONFIG_DATA *ConfigData; + UPDATE_CONFIG_DATA *UpdateConfigData; + CONFIG_HEADER ConfigHeader; + UINTN Index; + UINTN TotalSize; + UINTN BytesWritten; + UINTN StartPercentage; + UINTN EndPercentage; + + if (ConfigImage == NULL) { + DEBUG((DEBUG_INFO, "PlatformUpdate (NoConfig):")); + DEBUG((DEBUG_INFO, " BaseAddress - 0x%x,", 0)); + DEBUG((DEBUG_INFO, " Length - 0x%x\n", SystemFirmwareImageSize)); + // ASSUME the whole System Firmware include NVRAM region. + StartPercentage = 0; + EndPercentage = 100; + if (Progress != NULL) { + Progress (StartPercentage); + } + Status = PerformFlashWriteWithProgress ( + PlatformFirmwareTypeNvRam, + 0, + FlashAddressTypeRelativeAddress, + SystemFirmwareImage, + SystemFirmwareImageSize, + Progress, + StartPercentage, + EndPercentage + ); + if (Progress != NULL) { + Progress (EndPercentage); + } + if (!EFI_ERROR(Status)) { + *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; + mNvRamUpdated = TRUE; + } else { + *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; + } + return Status; + } + + DEBUG((DEBUG_INFO, "PlatformUpdate (With Config):\n")); + ConfigData = NULL; + ZeroMem (&ConfigHeader, sizeof(ConfigHeader)); + Status = ParseUpdateDataFile ( + ConfigImage, + ConfigImageSize, + &ConfigHeader, + &ConfigData + ); + DEBUG((DEBUG_INFO, "ParseUpdateDataFile - %r\n", Status)); + if (EFI_ERROR(Status)) { + *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; + return EFI_INVALID_PARAMETER; + } + DEBUG((DEBUG_INFO, "ConfigHeader.NumOfUpdates - 0x%x\n", ConfigHeader.NumOfUpdates)); + DEBUG((DEBUG_INFO, "PcdEdkiiSystemFirmwareFileGuid - %g\n", PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))); + + TotalSize = 0; + for (Index = 0; Index < ConfigHeader.NumOfUpdates; Index++) { + if (CompareGuid(&ConfigData[Index].FileGuid, PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))) { + TotalSize = TotalSize + ConfigData[Index].Length; + } + } + + BytesWritten = 0; + Index = 0; + UpdateConfigData = ConfigData; + while (Index < ConfigHeader.NumOfUpdates) { + if (CompareGuid(&UpdateConfigData->FileGuid, PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))) { + DEBUG((DEBUG_INFO, "FileGuid - %g (processing)\n", &UpdateConfigData->FileGuid)); + StartPercentage = (BytesWritten * 100) / TotalSize; + EndPercentage = ((BytesWritten + UpdateConfigData->Length) * 100) / TotalSize; + Status = PerformUpdate ( + SystemFirmwareImage, + SystemFirmwareImageSize, + UpdateConfigData, + LastAttemptVersion, + LastAttemptStatus, + Progress, + StartPercentage, + EndPercentage + ); + // + // Shall updates be serialized so that if an update is not successfully completed, + // the remaining updates won't be performed. + // + if (EFI_ERROR (Status)) { + break; + } + } else { + DEBUG((DEBUG_INFO, "FileGuid - %g (ignored)\n", &UpdateConfigData->FileGuid)); + } + + BytesWritten += UpdateConfigData->Length; + + Index++; + UpdateConfigData++; + } + + return Status; +} + +/** + Authenticate and update System Firmware image. + + Caution: This function may receive untrusted input. + + @param[in] Image The EDKII system FMP capsule image. + @param[in] ImageSize The size of the EDKII system FMP capsule image in bytes. + @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] Progress A function used by the driver to report the progress of the firmware update. + + @retval EFI_SUCCESS EDKII system FMP capsule passes authentication and the System Firmware image is updated. + @retval EFI_SECURITY_VIOLATION EDKII system FMP capsule fails authentication and the System Firmware image is not updated. + @retval EFI_WRITE_PROTECTED The flash device is read only. +**/ +EFI_STATUS +SystemFirmwareAuthenticatedUpdate ( + IN VOID *Image, + IN UINTN ImageSize, + OUT UINT32 *LastAttemptVersion, + OUT UINT32 *LastAttemptStatus, + IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress + ) +{ + EFI_STATUS Status; + VOID *SystemFirmwareImage; + UINTN SystemFirmwareImageSize; + VOID *ConfigImage; + UINTN ConfigImageSize; + VOID *AuthenticatedImage; + UINTN AuthenticatedImageSize; + + AuthenticatedImage = NULL; + AuthenticatedImageSize = 0; + + DEBUG((DEBUG_INFO, "SystemFirmwareAuthenticatedUpdate...\n")); + + Status = CapsuleAuthenticateSystemFirmware(Image, ImageSize, FALSE, LastAttemptVersion, LastAttemptStatus, &AuthenticatedImage, &AuthenticatedImageSize); + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_INFO, "SystemFirmwareAuthenticateImage - %r\n", Status)); + return Status; + } + + DEBUG((DEBUG_INFO, "ExtractSystemFirmwareImage ...\n")); + ExtractSystemFirmwareImage(AuthenticatedImage, AuthenticatedImageSize, &SystemFirmwareImage, &SystemFirmwareImageSize); + DEBUG((DEBUG_INFO, "ExtractConfigImage ...\n")); + ExtractConfigImage(AuthenticatedImage, AuthenticatedImageSize, &ConfigImage, &ConfigImageSize); + + DEBUG((DEBUG_INFO, "UpdateImage ...\n")); + Status = UpdateImage(SystemFirmwareImage, SystemFirmwareImageSize, ConfigImage, ConfigImageSize, LastAttemptVersion, LastAttemptStatus, Progress); + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_INFO, "UpdateImage - %r\n", Status)); + return Status; + } + + DEBUG((DEBUG_INFO, "SystemFirmwareAuthenticatedUpdate Done\n")); + + return EFI_SUCCESS; +} + +/** + + This code finds variable in storage blocks (Volatile or Non-Volatile). + + @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. + + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + @return EFI_BUFFER_TO_SMALL DataSize is too small for the result. + +**/ +EFI_STATUS +EFIAPI +GetVariableHook ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + DEBUG((DEBUG_INFO, "GetVariableHook - %S, %g\n", VariableName, VendorGuid)); + return EFI_NOT_AVAILABLE_YET; +} + +/** + + This code Finds the Next available variable. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] VendorGuid Variable Vendor Guid. + + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + @return EFI_BUFFER_TO_SMALL DataSize is too small for the result. + +**/ +EFI_STATUS +EFIAPI +GetNextVariableNameHook ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid + ) +{ + DEBUG((DEBUG_INFO, "GetNextVariableNameHook - %S, %g\n", VariableName, VendorGuid)); + return EFI_NOT_AVAILABLE_YET; +} + +/** + + This code sets variable in storage blocks (Volatile or Non-Volatile). + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + @param[in] Attributes Attribute value of the variable found + @param[in] DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param[in] Data Data pointer. + + @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 +SetVariableHook ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + DEBUG((DEBUG_INFO, "SetVariableHook - %S, %g, 0x%x (0x%x)\n", VariableName, VendorGuid, Attributes, DataSize)); + return EFI_NOT_AVAILABLE_YET; +} + +/** + + This code returns information about the EFI variables. + + @param[in] Attributes Attributes bitmask to specify the type of variables + on which to return information. + @param[out] MaximumVariableStorageSize Pointer to the maximum size of the storage space available + for the EFI variables associated with the attributes specified. + @param[out] RemainingVariableStorageSize Pointer to the remaining size of the storage space available + for EFI variables associated with the attributes specified. + @param[out] MaximumVariableSize Pointer to the maximum size of an individual EFI variables + associated with the attributes specified. + + @return EFI_SUCCESS Query successfully. + +**/ +EFI_STATUS +EFIAPI +QueryVariableInfoHook ( + IN UINT32 Attributes, + OUT UINT64 *MaximumVariableStorageSize, + OUT UINT64 *RemainingVariableStorageSize, + OUT UINT64 *MaximumVariableSize + ) +{ + DEBUG((DEBUG_INFO, "QueryVariableInfoHook - 0x%x\n", Attributes)); + return EFI_NOT_AVAILABLE_YET; +} + +/** + Updates the firmware image of the device. + + This function updates the hardware with the new firmware image. + This function returns EFI_UNSUPPORTED if the firmware image is not updatable. + If the firmware image is updatable, the function should perform the following minimal validations + before proceeding to do the firmware image update. + - Validate the image authentication if image has attribute + IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. The function returns + EFI_SECURITY_VIOLATION if the validation fails. + - Validate the image is a supported image for this device. The function returns EFI_ABORTED if + the image is unsupported. The function can optionally provide more detailed information on + why the image is not a supported image. + - Validate the data from VendorCode if not null. Image validation must be performed before + VendorCode data validation. VendorCode data is ignored or considered invalid if image + validation failed. The function returns EFI_ABORTED if the data is invalid. + + VendorCode enables vendor to implement vendor-specific firmware image update policy. Null if + the caller did not specify the policy or use the default policy. As an example, vendor can implement + a policy to allow an option to force a firmware image update when the abort reason is due to the new + firmware image version is older than the current firmware image version or bad image checksum. + Sensitive operations such as those wiping the entire firmware image and render the device to be + non-functional should be encoded in the image itself rather than passed with the VendorCode. + AbortReason enables vendor to have the option to provide a more detailed description of the abort + reason to the caller. + + @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. + @param[in] ImageIndex A unique number identifying the firmware image(s) within the device. + The number is between 1 and DescriptorCount. + @param[in] Image Points to the new image. + @param[in] ImageSize Size of the new image in bytes. + @param[in] VendorCode This enables vendor to implement vendor-specific firmware image update policy. + Null indicates the caller did not specify the policy or use the default policy. + @param[in] Progress A function used by the driver to report the progress of the firmware update. + @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more + details for the aborted operation. The buffer is allocated by this function + with AllocatePool(), and it is the caller's responsibility to free it with a + call to FreePool(). + + @retval EFI_SUCCESS The device was successfully updated with the new image. + @retval EFI_ABORTED The operation is aborted. + @retval EFI_INVALID_PARAMETER The Image was NULL. + @retval EFI_UNSUPPORTED The operation is not supported. + @retval EFI_SECURITY_VIOLATION The operation could not be performed due to an authentication failure. + +**/ +EFI_STATUS +EFIAPI +FmpSetImage ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, + IN UINT8 ImageIndex, + IN CONST VOID *Image, + IN UINTN ImageSize, + IN CONST VOID *VendorCode, + IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress, + OUT CHAR16 **AbortReason + ) +{ + EFI_STATUS Status; + EFI_STATUS VarStatus; + SYSTEM_FMP_PRIVATE_DATA *SystemFmpPrivate; + + if (Image == NULL || ImageSize == 0 || AbortReason == NULL) { + return EFI_INVALID_PARAMETER; + } + + SystemFmpPrivate = SYSTEM_FMP_PRIVATE_DATA_FROM_FMP(This); + *AbortReason = NULL; + + if (ImageIndex == 0 || ImageIndex > SystemFmpPrivate->DescriptorCount) { + return EFI_INVALID_PARAMETER; + } + + Status = SystemFirmwareAuthenticatedUpdate((VOID *)Image, ImageSize, &SystemFmpPrivate->LastAttempt.LastAttemptVersion, &SystemFmpPrivate->LastAttempt.LastAttemptStatus, Progress); + DEBUG((DEBUG_INFO, "SetImage - LastAttempt Version - 0x%x, State - 0x%x\n", SystemFmpPrivate->LastAttempt.LastAttemptVersion, SystemFmpPrivate->LastAttempt.LastAttemptStatus)); + + // + // If NVRAM is updated, we should no longer touch variable services, because + // the current variable driver may not manage the new NVRAM region. + // + if (mNvRamUpdated) { + DEBUG ((DEBUG_INFO, "NvRamUpdated, Update Variable Services\n")); + gRT->GetVariable = GetVariableHook; + gRT->GetNextVariableName = GetNextVariableNameHook; + gRT->SetVariable = SetVariableHook; + gRT->QueryVariableInfo = QueryVariableInfoHook; + + gRT->Hdr.CRC32 = 0; + gBS->CalculateCrc32 ( + (UINT8 *) &gRT->Hdr, + gRT->Hdr.HeaderSize, + &gRT->Hdr.CRC32 + ); + } + + VarStatus = gRT->SetVariable( + SYSTEM_FMP_LAST_ATTEMPT_VARIABLE_NAME, + &gSystemFmpLastAttemptVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof(SystemFmpPrivate->LastAttempt), + &SystemFmpPrivate->LastAttempt + ); + DEBUG((DEBUG_INFO, "SetLastAttempt - %r\n", VarStatus)); + + return Status; +} + +/** + Get the set of EFI_FIRMWARE_IMAGE_DESCRIPTOR structures from an FMP Protocol. + + @param[in] Handle Handle with an FMP Protocol or a System FMP + Protocol. + @param[in] ProtocolGuid Pointer to the FMP Protocol GUID or System FMP + Protocol GUID. + @param[out] FmpImageInfoCount Pointer to the number of + EFI_FIRMWARE_IMAGE_DESCRIPTOR structures. + @param[out] DescriptorSize Pointer to the size, in bytes, of each + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure. + + @return NULL No EFI_FIRMWARE_IMAGE_DESCRIPTOR structures found. + @return !NULL Pointer to a buffer of EFI_FIRMWARE_IMAGE_DESCRIPTOR structures + allocated using AllocatePool(). Caller must free buffer with + FreePool(). +**/ +EFI_FIRMWARE_IMAGE_DESCRIPTOR * +GetFmpImageDescriptors ( + IN EFI_HANDLE Handle, + IN EFI_GUID *ProtocolGuid, + OUT UINT8 *FmpImageInfoCount, + OUT UINTN *DescriptorSize + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + UINTN ImageInfoSize; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + UINT32 FmpImageInfoDescriptorVer; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + + *FmpImageInfoCount = 0; + *DescriptorSize = 0; + + Status = gBS->HandleProtocol ( + Handle, + ProtocolGuid, + (VOID **)&Fmp + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + // + // Determine the size required for the set of EFI_FIRMWARE_IMAGE_DESCRIPTORs. + // + ImageInfoSize = 0; + Status = Fmp->GetImageInfo ( + Fmp, // FMP Pointer + &ImageInfoSize, // Buffer Size (in this case 0) + NULL, // NULL so we can get size + &FmpImageInfoDescriptorVer, // DescriptorVersion + FmpImageInfoCount, // DescriptorCount + DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_ERROR, "SystemFirmwareUpdateDxe: Unexpected Failure. Status = %r\n", Status)); + return NULL; + } + + // + // Allocate buffer for the set of EFI_FIRMWARE_IMAGE_DESCRIPTORs. + // + FmpImageInfoBuf = NULL; + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + DEBUG ((DEBUG_ERROR, "SystemFirmwareUpdateDxe: Failed to allocate memory for descriptors.\n")); + return NULL; + } + + // + // Retrieve the set of EFI_FIRMWARE_IMAGE_DESCRIPTORs. + // + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + FmpImageInfoCount, // DescriptorCount + DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + + // + // Free unused PackageVersionName return buffer + // + if (PackageVersionName != NULL) { + FreePool (PackageVersionName); + PackageVersionName = NULL; + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "SystemFirmwareUpdateDxe: Failure in GetImageInfo. Status = %r\n", Status)); + if (FmpImageInfoBuf != NULL) { + FreePool (FmpImageInfoBuf); + } + return NULL; + } + + return FmpImageInfoBuf; +} + +/** + Search for handles with an FMP protocol whose EFI_FIRMWARE_IMAGE_DESCRIPTOR + ImageTypeId matches the ImageTypeId produced by this module. + + @param[in] ProtocolGuid Pointer to the GUID of the protocol to search. + @param[out] HandleCount Pointer to the number of returned handles. + + @return NULL No matching handles found. + @return !NULL Pointer to a buffer of handles allocated using AllocatePool(). + Caller must free buffer with FreePool(). +**/ +EFI_HANDLE * +FindMatchingFmpHandles ( + IN EFI_GUID *ProtocolGuid, + OUT UINTN *HandleCount + ) +{ + EFI_STATUS Status; + UINTN TempHandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + UINTN Index2; + UINTN Index3; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *OriginalFmpImageInfoBuf; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + BOOLEAN MatchFound; + + *HandleCount = 0; + TempHandleCount = 0; + HandleBuffer = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + ProtocolGuid, + NULL, + &TempHandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + for (Index = 0; Index < TempHandleCount; Index++) { + OriginalFmpImageInfoBuf = GetFmpImageDescriptors ( + HandleBuffer[Index], + ProtocolGuid, + &FmpImageInfoCount, + &DescriptorSize + ); + + // + // Loop through the set of EFI_FIRMWARE_IMAGE_DESCRIPTORs. + // + MatchFound = FALSE; + if (OriginalFmpImageInfoBuf != NULL) { + FmpImageInfoBuf = OriginalFmpImageInfoBuf; + + for (Index2 = 0; Index2 < FmpImageInfoCount; Index2++) { + for (Index3 = 0; Index3 < mSystemFmpPrivate->DescriptorCount; Index3++) { + MatchFound = CompareGuid ( + &FmpImageInfoBuf->ImageTypeId, + &mSystemFmpPrivate->ImageDescriptor[Index3].ImageTypeId + ); + if (MatchFound) { + break; + } + } + if (MatchFound) { + break; + } + // + // Increment the buffer pointer ahead by the size of the descriptor + // + FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize); + } + if (MatchFound) { + HandleBuffer[*HandleCount] = HandleBuffer[Index]; + (*HandleCount)++; + } + + FreePool (OriginalFmpImageInfoBuf); + } + } + + if ((*HandleCount) == 0) { + // + // No any matching handle. + // + FreePool (HandleBuffer); + return NULL; + } + return HandleBuffer; +} + +/** + Uninstall System FMP Protocol instances that may have been installed by + SystemFirmwareUpdateDxe drivers dispatches by other capsules. + + @retval EFI_SUCCESS All System FMP Protocols found were uninstalled. + @return Other One or more System FMP Protocols could not be uninstalled. + +**/ +EFI_STATUS +UninstallMatchingSystemFmpProtocols ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *SystemFmp; + + // + // Uninstall SystemFmpProtocol instances that may have been produced by + // the SystemFirmwareUpdate drivers in FVs dispatched by other capsules. + // + HandleBuffer = FindMatchingFmpHandles ( + &gSystemFmpProtocolGuid, + &HandleCount + ); + DEBUG ((DEBUG_INFO, "SystemFirmwareUpdateDxe: Found %d matching System FMP instances\n", HandleCount)); + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol( + HandleBuffer[Index], + &gSystemFmpProtocolGuid, + (VOID **)&SystemFmp + ); + if (EFI_ERROR (Status)) { + continue; + } + DEBUG ((DEBUG_INFO, "SystemFirmwareUpdateDxe: Uninstall SystemFmp produced by another capsule\n")); + Status = gBS->UninstallProtocolInterface ( + HandleBuffer[Index], + &gSystemFmpProtocolGuid, + SystemFmp + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "SystemFirmwareUpdateDxe: Failed to uninstall SystemFmp %r. Exiting.\n", Status)); + FreePool (HandleBuffer); + return Status; + } + } + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + return EFI_SUCCESS; +} + +/** + System FMP module entrypoint + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS System FMP module is initialized. + @retval EFI_OUT_OF_RESOURCES There are not enough resources avaulable to + initialize this module. + @retval Other System FMP Protocols could not be uninstalled. + @retval Other System FMP Protocol could not be installed. + @retval Other FMP Protocol could not be installed. +**/ +EFI_STATUS +EFIAPI +SystemFirmwareUpdateMainDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + + // + // Initialize SystemFmpPrivateData + // + mSystemFmpPrivate = AllocateZeroPool (sizeof (SYSTEM_FMP_PRIVATE_DATA)); + if (mSystemFmpPrivate == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = InitializePrivateData (mSystemFmpPrivate); + if (EFI_ERROR (Status)) { + FreePool (mSystemFmpPrivate); + mSystemFmpPrivate = NULL; + return Status; + } + + // + // Uninstall SystemFmpProtocol instances that may have been produced by + // the SystemFirmwareUpdate drivers in FVs dispatched by other capsules. + // + Status = UninstallMatchingSystemFmpProtocols (); + if (EFI_ERROR (Status)) { + FreePool (mSystemFmpPrivate); + mSystemFmpPrivate = NULL; + return Status; + } + + // + // Look for a handle with matching Firmware Management Protocol + // + HandleCount = 0; + HandleBuffer = FindMatchingFmpHandles ( + &gEfiFirmwareManagementProtocolGuid, + &HandleCount + ); + DEBUG ((DEBUG_INFO, "SystemFirmwareUpdateDxe: Found %d matching FMP instances\n", HandleCount)); + + switch (HandleCount) { + case 0: + // + // Install FMP protocol onto a new handle. + // + DEBUG ((DEBUG_INFO, "SystemFirmwareUpdateDxe: Install FMP onto a new handle\n")); + Status = gBS->InstallMultipleProtocolInterfaces ( + &mSystemFmpPrivate->Handle, + &gEfiFirmwareManagementProtocolGuid, + &mSystemFmpPrivate->Fmp, + NULL + ); + break; + case 1: + // + // Install System FMP protocol onto handle with matching FMP Protocol + // + DEBUG ((DEBUG_INFO, "SystemFirmwareUpdateDxe: Install System FMP onto matching FMP handle\n")); + mSystemFmpPrivate->Handle = HandleBuffer[0]; + Status = gBS->InstallMultipleProtocolInterfaces ( + &HandleBuffer[0], + &gSystemFmpProtocolGuid, + &mSystemFmpPrivate->Fmp, + NULL + ); + break; + default: + // + // More than one matching handle is not expected. Unload driver. + // + DEBUG ((DEBUG_ERROR, "SystemFirmwareUpdateDxe: More than one matching FMP handle. Unload driver.\n")); + Status = EFI_DEVICE_ERROR; + break; + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + if (EFI_ERROR (Status)) { + FreePool (mSystemFmpPrivate); + mSystemFmpPrivate = NULL; + } + + return Status; +} -- cgit