| /** @file | |
| Produces a Firmware Management Protocol that supports updates to a firmware | |
| image stored in a firmware device with platform and firmware device specific | |
| information provided through PCDs and libraries. | |
| Copyright (c) Microsoft Corporation.<BR> | |
| Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "FmpDxe.h" | |
| #include "VariableSupport.h" | |
| /// | |
| /// FILE_GUID from FmpDxe.inf. When FmpDxe.inf is used in a platform, the | |
| /// FILE_GUID must always be overridden in the <Defines> section to provide | |
| /// the ESRT GUID value associated with the updatable firmware image. A | |
| /// check is made in this module's driver entry point to verify that a | |
| /// new FILE_GUID value has been defined. | |
| /// | |
| const EFI_GUID mDefaultModuleFileGuid = { | |
| 0x78ef0a56, 0x1cf0, 0x4535, { 0xb5, 0xda, 0xf6, 0xfd, 0x2f, 0x40, 0x5a, 0x11 } | |
| }; | |
| /// | |
| /// TRUE if FmpDeviceLib manages a single firmware storage device. | |
| /// | |
| BOOLEAN mFmpSingleInstance = FALSE; | |
| /// | |
| /// Firmware Management Protocol instance that is initialized in the entry | |
| /// point from PCD settings. | |
| /// | |
| EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL mFmpProgress; | |
| // | |
| // Template of the private context structure for the Firmware Management | |
| // Protocol instance | |
| // | |
| const FIRMWARE_MANAGEMENT_PRIVATE_DATA mFirmwareManagementPrivateDataTemplate = { | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA_SIGNATURE, // Signature | |
| NULL, // Handle | |
| { // Fmp | |
| GetTheImageInfo, | |
| GetTheImage, | |
| SetTheImage, | |
| CheckTheImage, | |
| GetPackageInfo, | |
| SetPackageInfo | |
| }, | |
| FALSE, // DescriptorPopulated | |
| { // Desc | |
| 1, // ImageIndex | |
| // | |
| // ImageTypeId | |
| // | |
| { 0x00000000, 0x0000,0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } | |
| }, | |
| 1, // ImageId | |
| NULL, // ImageIdName | |
| 0, // Version | |
| NULL, // VersionName | |
| 0, // Size | |
| 0, // AttributesSupported | |
| 0, // AttributesSetting | |
| 0, // Compatibilities | |
| 0, // LowestSupportedImageVersion | |
| 0, // LastAttemptVersion | |
| 0, // LastAttemptStatus | |
| 0 // HardwareInstance | |
| }, | |
| NULL, // ImageIdName | |
| NULL, // VersionName | |
| TRUE, // RuntimeVersionSupported | |
| NULL, // FmpDeviceLockEvent | |
| FALSE, // FmpDeviceLocked | |
| NULL, // FmpDeviceContext | |
| NULL, // VersionVariableName | |
| NULL, // LsvVariableName | |
| NULL, // LastAttemptStatusVariableName | |
| NULL, // LastAttemptVersionVariableName | |
| NULL, // FmpStateVariableName | |
| TRUE // DependenciesSatisfied | |
| }; | |
| /// | |
| /// GUID that is used to create event used to lock the firmware storage device. | |
| /// | |
| EFI_GUID *mLockGuid = NULL; | |
| /// | |
| /// Progress() function pointer passed into SetTheImage() | |
| /// | |
| EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS mProgressFunc = NULL; | |
| /// | |
| /// Null-terminated Unicode string retrieved from PcdFmpDeviceImageIdName. | |
| /// | |
| CHAR16 *mImageIdName = NULL; | |
| /** | |
| Callback function to report the process of the firmware updating. | |
| Wrap the caller's version in this so that progress from the device lib is | |
| within the expected range. Convert device lib 0% - 100% to 6% - 98%. | |
| FmpDxe 1% - 5% for validation | |
| FmpDeviceLib 6% - 98% for flashing/update | |
| FmpDxe 99% - 100% finish | |
| @param[in] Completion A value between 1 and 100 indicating the current | |
| completion progress of the firmware update. Completion | |
| progress is reported as from 1 to 100 percent. A value | |
| of 0 is used by the driver to indicate that progress | |
| reporting is not supported. | |
| @retval EFI_SUCCESS The progress was updated. | |
| @retval EFI_UNSUPPORTED Updating progress is not supported. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FmpDxeProgress ( | |
| IN UINTN Completion | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = EFI_UNSUPPORTED; | |
| if (mProgressFunc == NULL) { | |
| return Status; | |
| } | |
| // | |
| // Reserve 6% - 98% for the FmpDeviceLib. Call the real progress function. | |
| // | |
| Status = mProgressFunc (((Completion * 92) / 100) + 6); | |
| if (Status == EFI_UNSUPPORTED) { | |
| mProgressFunc = NULL; | |
| } | |
| return Status; | |
| } | |
| /** | |
| Returns a pointer to the ImageTypeId GUID value. An attempt is made to get | |
| the GUID value from the FmpDeviceLib. If the FmpDeviceLib does not provide | |
| a GUID value, then PcdFmpDeviceImageTypeIdGuid is used. If the size of | |
| PcdFmpDeviceImageTypeIdGuid is not the size of EFI_GUID, then gEfiCallerIdGuid | |
| is returned. | |
| @retval The ImageTypeId GUID | |
| **/ | |
| EFI_GUID * | |
| GetImageTypeIdGuid ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_GUID *FmpDeviceLibGuid; | |
| UINTN ImageTypeIdGuidSize; | |
| FmpDeviceLibGuid = NULL; | |
| Status = FmpDeviceGetImageTypeIdGuidPtr (&FmpDeviceLibGuid); | |
| if (EFI_ERROR (Status)) { | |
| if (Status != EFI_UNSUPPORTED) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): FmpDeviceLib GetImageTypeIdGuidPtr() returned invalid error %r\n", mImageIdName, Status)); | |
| } | |
| } else if (FmpDeviceLibGuid == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): FmpDeviceLib GetImageTypeIdGuidPtr() returned invalid GUID\n", mImageIdName)); | |
| Status = EFI_NOT_FOUND; | |
| } | |
| if (EFI_ERROR (Status)) { | |
| ImageTypeIdGuidSize = PcdGetSize (PcdFmpDeviceImageTypeIdGuid); | |
| if (ImageTypeIdGuidSize == sizeof (EFI_GUID)) { | |
| FmpDeviceLibGuid = (EFI_GUID *)PcdGetPtr (PcdFmpDeviceImageTypeIdGuid); | |
| } else { | |
| DEBUG ((DEBUG_WARN, "FmpDxe(%s): Fall back to ImageTypeIdGuid of gEfiCallerIdGuid\n", mImageIdName)); | |
| FmpDeviceLibGuid = &gEfiCallerIdGuid; | |
| } | |
| } | |
| return FmpDeviceLibGuid; | |
| } | |
| /** | |
| Returns a pointer to the Null-terminated Unicode ImageIdName string. | |
| @retval Null-terminated Unicode ImageIdName string. | |
| **/ | |
| CHAR16 * | |
| GetImageTypeNameString ( | |
| VOID | |
| ) | |
| { | |
| return mImageIdName; | |
| } | |
| /** | |
| Lowest supported version is a combo of three parts. | |
| 1. Check if the device lib has a lowest supported version | |
| 2. Check if we have a variable for lowest supported version (this will be updated with each capsule applied) | |
| 3. Check Fixed at build PCD | |
| @param[in] Private Pointer to the private context structure for the | |
| Firmware Management Protocol instance. | |
| @retval The largest value | |
| **/ | |
| UINT32 | |
| GetLowestSupportedVersion ( | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 DeviceLibLowestSupportedVersion; | |
| UINT32 VariableLowestSupportedVersion; | |
| UINT32 ReturnLsv; | |
| // | |
| // Get the LowestSupportedVersion. | |
| // | |
| if (!IsLowestSupportedVersionCheckRequired ()) { | |
| // | |
| // Any Version can pass the 0 LowestSupportedVersion check. | |
| // | |
| return 0; | |
| } | |
| ReturnLsv = PcdGet32 (PcdFmpDeviceBuildTimeLowestSupportedVersion); | |
| // | |
| // Check the FmpDeviceLib | |
| // | |
| DeviceLibLowestSupportedVersion = DEFAULT_LOWESTSUPPORTEDVERSION; | |
| Status = FmpDeviceGetLowestSupportedVersion (&DeviceLibLowestSupportedVersion); | |
| if (EFI_ERROR (Status)) { | |
| DeviceLibLowestSupportedVersion = DEFAULT_LOWESTSUPPORTEDVERSION; | |
| } | |
| if (DeviceLibLowestSupportedVersion > ReturnLsv) { | |
| ReturnLsv = DeviceLibLowestSupportedVersion; | |
| } | |
| // | |
| // Check the lowest supported version UEFI variable for this device | |
| // | |
| VariableLowestSupportedVersion = GetLowestSupportedVersionFromVariable (Private); | |
| if (VariableLowestSupportedVersion > ReturnLsv) { | |
| ReturnLsv = VariableLowestSupportedVersion; | |
| } | |
| // | |
| // Return the largest value | |
| // | |
| return ReturnLsv; | |
| } | |
| /** | |
| Populates the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure in the private | |
| context structure. | |
| @param[in] Private Pointer to the private context structure for the | |
| Firmware Management Protocol instance. | |
| **/ | |
| VOID | |
| PopulateDescriptor ( | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 DependenciesSize; | |
| if (Private == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): PopulateDescriptor() - Private is NULL.\n", mImageIdName)); | |
| return; | |
| } | |
| if (Private->DescriptorPopulated) { | |
| return; | |
| } | |
| Private->Descriptor.ImageIndex = 1; | |
| CopyGuid (&Private->Descriptor.ImageTypeId, GetImageTypeIdGuid ()); | |
| Private->Descriptor.ImageId = Private->Descriptor.ImageIndex; | |
| Private->Descriptor.ImageIdName = GetImageTypeNameString (); | |
| // | |
| // Get the hardware instance from FmpDeviceLib | |
| // | |
| Status = FmpDeviceGetHardwareInstance (&Private->Descriptor.HardwareInstance); | |
| if (Status == EFI_UNSUPPORTED) { | |
| Private->Descriptor.HardwareInstance = 0; | |
| } | |
| // | |
| // Generate UEFI Variable names used to store status information for this | |
| // FMP instance. | |
| // | |
| GenerateFmpVariableNames (Private); | |
| // | |
| // Get the version. Some devices don't support getting the firmware version | |
| // at runtime. If FmpDeviceLib does not support returning a version, then | |
| // it is stored in a UEFI variable. | |
| // | |
| Status = FmpDeviceGetVersion (&Private->Descriptor.Version); | |
| if (Status == EFI_UNSUPPORTED) { | |
| Private->RuntimeVersionSupported = FALSE; | |
| Private->Descriptor.Version = GetVersionFromVariable (Private); | |
| } else if (EFI_ERROR (Status)) { | |
| // | |
| // Unexpected error. Use default version. | |
| // | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetVersion() from FmpDeviceLib (%s) returned %r\n", mImageIdName, GetImageTypeNameString (), Status)); | |
| Private->Descriptor.Version = DEFAULT_VERSION; | |
| } | |
| // | |
| // Free the current version name. Shouldn't really happen but this populate | |
| // function could be called multiple times (to refresh). | |
| // | |
| if (Private->Descriptor.VersionName != NULL) { | |
| FreePool (Private->Descriptor.VersionName); | |
| Private->Descriptor.VersionName = NULL; | |
| } | |
| // | |
| // Attempt to get the version string from the FmpDeviceLib | |
| // | |
| Status = FmpDeviceGetVersionString (&Private->Descriptor.VersionName); | |
| if (Status == EFI_UNSUPPORTED) { | |
| DEBUG ((DEBUG_INFO, "FmpDxe(%s): GetVersionString() unsupported in FmpDeviceLib.\n", mImageIdName)); | |
| Private->Descriptor.VersionName = AllocateCopyPool ( | |
| sizeof (VERSION_STRING_NOT_SUPPORTED), | |
| VERSION_STRING_NOT_SUPPORTED | |
| ); | |
| } else if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_INFO, "FmpDxe(%s): GetVersionString() not available in FmpDeviceLib.\n", mImageIdName)); | |
| Private->Descriptor.VersionName = AllocateCopyPool ( | |
| sizeof (VERSION_STRING_NOT_AVAILABLE), | |
| VERSION_STRING_NOT_AVAILABLE | |
| ); | |
| } | |
| Private->Descriptor.LowestSupportedImageVersion = GetLowestSupportedVersion (Private); | |
| // | |
| // Get attributes from the FmpDeviceLib | |
| // | |
| FmpDeviceGetAttributes ( | |
| &Private->Descriptor.AttributesSupported, | |
| &Private->Descriptor.AttributesSetting | |
| ); | |
| // | |
| // Force set the updatable bits in the attributes; | |
| // | |
| Private->Descriptor.AttributesSupported |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; | |
| Private->Descriptor.AttributesSetting |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; | |
| // | |
| // Force set the authentication bits in the attributes; | |
| // | |
| Private->Descriptor.AttributesSupported |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); | |
| Private->Descriptor.AttributesSetting |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); | |
| Private->Descriptor.Compatibilities = 0; | |
| // | |
| // Get the size of the firmware image from the FmpDeviceLib | |
| // | |
| Status = FmpDeviceGetSize (&Private->Descriptor.Size); | |
| if (EFI_ERROR (Status)) { | |
| Private->Descriptor.Size = 0; | |
| } | |
| Private->Descriptor.LastAttemptVersion = GetLastAttemptVersionFromVariable (Private); | |
| Private->Descriptor.LastAttemptStatus = GetLastAttemptStatusFromVariable (Private); | |
| // | |
| // Get the dependency from the FmpDependencyDeviceLib. | |
| // | |
| Private->Descriptor.Dependencies = NULL; | |
| // | |
| // Check the attribute IMAGE_ATTRIBUTE_DEPENDENCY | |
| // | |
| if (Private->Descriptor.AttributesSetting & IMAGE_ATTRIBUTE_DEPENDENCY) { | |
| Private->Descriptor.Dependencies = GetFmpDependency (&DependenciesSize); | |
| } | |
| Private->DescriptorPopulated = TRUE; | |
| } | |
| /** | |
| Returns information about the current firmware image(s) of the device. | |
| This function allows a copy of the current firmware image to be created and saved. | |
| The saved copy could later been used, for example, in firmware image recovery or rollback. | |
| @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. | |
| @param[in, out] ImageInfoSize A pointer to the size, in bytes, of the ImageInfo buffer. | |
| On input, this is the size of the buffer allocated by the caller. | |
| On output, it is the size of the buffer returned by the firmware | |
| if the buffer was large enough, or the size of the buffer needed | |
| to contain the image(s) information if the buffer was too small. | |
| @param[in, out] ImageInfo A pointer to the buffer in which firmware places the current image(s) | |
| information. The information is an array of EFI_FIRMWARE_IMAGE_DESCRIPTORs. | |
| @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number | |
| associated with the EFI_FIRMWARE_IMAGE_DESCRIPTOR. | |
| @param[out] DescriptorCount A pointer to the location in which firmware returns the number of | |
| descriptors or firmware images within this device. | |
| @param[out] DescriptorSize A pointer to the location in which firmware returns the size, in bytes, | |
| of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR. | |
| @param[out] PackageVersion A version number that represents all the firmware images in the device. | |
| The format is vendor specific and new version must have a greater value | |
| than the old version. If PackageVersion is not supported, the value is | |
| 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version comparison | |
| is to be performed using PackageVersionName. A value of 0xFFFFFFFD indicates | |
| that package version update is in progress. | |
| @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the | |
| package version name. 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_BUFFER_TOO_SMALL The ImageInfo buffer was too small. The current buffer size | |
| needed to hold the image(s) information is returned in ImageInfoSize. | |
| @retval EFI_INVALID_PARAMETER ImageInfoSize is NULL. | |
| @retval EFI_DEVICE_ERROR Valid information could not be returned. Possible corrupted image. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetTheImageInfo ( | |
| IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, | |
| IN OUT UINTN *ImageInfoSize, | |
| IN OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo, | |
| OUT UINT32 *DescriptorVersion, | |
| OUT UINT8 *DescriptorCount, | |
| OUT UINTN *DescriptorSize, | |
| OUT UINT32 *PackageVersion, | |
| OUT CHAR16 **PackageVersionName | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; | |
| Status = EFI_SUCCESS; | |
| if (This == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImageInfo() - This is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| // | |
| // Retrieve the private context structure | |
| // | |
| Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This); | |
| FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext); | |
| // | |
| // Check for valid pointer | |
| // | |
| if (ImageInfoSize == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImageInfo() - ImageInfoSize is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| // | |
| // Check the buffer size | |
| // NOTE: Check this first so caller can get the necessary memory size it must allocate. | |
| // | |
| if (*ImageInfoSize < (sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR))) { | |
| *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR); | |
| DEBUG ((DEBUG_VERBOSE, "FmpDxe(%s): GetImageInfo() - ImageInfoSize is to small.\n", mImageIdName)); | |
| Status = EFI_BUFFER_TOO_SMALL; | |
| goto cleanup; | |
| } | |
| // | |
| // Confirm that buffer isn't null | |
| // | |
| if ( (ImageInfo == NULL) || (DescriptorVersion == NULL) || (DescriptorCount == NULL) || (DescriptorSize == NULL) | |
| || (PackageVersion == NULL)) | |
| { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImageInfo() - Pointer Parameter is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| // | |
| // Set the size to whatever we need | |
| // | |
| *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR); | |
| // | |
| // Make sure the descriptor has already been loaded or refreshed | |
| // | |
| PopulateDescriptor (Private); | |
| // | |
| // Copy the image descriptor | |
| // | |
| CopyMem (ImageInfo, &Private->Descriptor, sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR)); | |
| *DescriptorVersion = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; | |
| *DescriptorCount = 1; | |
| *DescriptorSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR); | |
| // | |
| // means unsupported | |
| // | |
| *PackageVersion = 0xFFFFFFFF; | |
| // | |
| // Do not update PackageVersionName since it is not supported in this instance. | |
| // | |
| cleanup: | |
| return Status; | |
| } | |
| /** | |
| Retrieves a copy of the current firmware image of the device. | |
| This function allows a copy of the current firmware image to be created and saved. | |
| The saved copy could later been used, for example, in firmware image recovery or rollback. | |
| @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, out] Image Points to the buffer where the current image is copied to. | |
| @param[in, out] ImageSize On entry, points to the size of the buffer pointed to by Image, in bytes. | |
| On return, points to the length of the image, in bytes. | |
| @retval EFI_SUCCESS The device was successfully updated with the new image. | |
| @retval EFI_BUFFER_TOO_SMALL The buffer specified by ImageSize is too small to hold the | |
| image. The current buffer size needed to hold the image is returned | |
| in ImageSize. | |
| @retval EFI_INVALID_PARAMETER The Image was NULL. | |
| @retval EFI_NOT_FOUND The current image is not copied to the buffer. | |
| @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 | |
| GetTheImage ( | |
| IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, | |
| IN UINT8 ImageIndex, | |
| IN OUT VOID *Image, | |
| IN OUT UINTN *ImageSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; | |
| UINTN Size; | |
| if (!FeaturePcdGet (PcdFmpDeviceStorageAccessEnable)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Status = EFI_SUCCESS; | |
| if (This == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImage() - This is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| // | |
| // Retrieve the private context structure | |
| // | |
| Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This); | |
| FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext); | |
| // | |
| // Check to make sure index is 1 (only 1 image for this device) | |
| // | |
| if (ImageIndex != 1) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImage() - Image Index Invalid.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| if (ImageSize == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImage() - ImageSize Pointer Parameter is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| // | |
| // Check the buffer size | |
| // | |
| Status = FmpDeviceGetSize (&Size); | |
| if (EFI_ERROR (Status)) { | |
| Size = 0; | |
| } | |
| if (*ImageSize < Size) { | |
| *ImageSize = Size; | |
| DEBUG ((DEBUG_VERBOSE, "FmpDxe(%s): GetImage() - ImageSize is to small.\n", mImageIdName)); | |
| Status = EFI_BUFFER_TOO_SMALL; | |
| goto cleanup; | |
| } | |
| if (Image == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImage() - Image Pointer Parameter is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| Status = FmpDeviceGetImage (Image, ImageSize); | |
| cleanup: | |
| return Status; | |
| } | |
| /** | |
| Helper function to safely retrieve the FMP header from | |
| within an EFI_FIRMWARE_IMAGE_AUTHENTICATION structure. | |
| @param[in] Image Pointer to the image. | |
| @param[in] ImageSize Size of the image. | |
| @param[in] AdditionalHeaderSize Size of any headers that cannot be calculated by this function. | |
| @param[out] PayloadSize An optional pointer to a UINTN that holds the size of the payload | |
| (image size minus headers) | |
| @retval !NULL Valid pointer to the header. | |
| @retval NULL Structure is bad and pointer cannot be found. | |
| **/ | |
| VOID * | |
| GetFmpHeader ( | |
| IN CONST EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image, | |
| IN CONST UINTN ImageSize, | |
| IN CONST UINTN AdditionalHeaderSize, | |
| OUT UINTN *PayloadSize OPTIONAL | |
| ) | |
| { | |
| // | |
| // Check to make sure that operation can be safely performed. | |
| // | |
| if ((((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) + AdditionalHeaderSize < (UINTN)Image) || \ | |
| (((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) + AdditionalHeaderSize >= (UINTN)Image + ImageSize)) | |
| { | |
| // | |
| // Pointer overflow. Invalid image. | |
| // | |
| return NULL; | |
| } | |
| if (PayloadSize != NULL) { | |
| *PayloadSize = ImageSize - (sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength + AdditionalHeaderSize); | |
| } | |
| return (VOID *)((UINT8 *)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength + AdditionalHeaderSize); | |
| } | |
| /** | |
| Helper function to safely calculate the size of all headers | |
| within an EFI_FIRMWARE_IMAGE_AUTHENTICATION structure. | |
| @param[in] Image Pointer to the image. | |
| @param[in] AdditionalHeaderSize Size of any headers that cannot be calculated by this function. | |
| @retval UINT32>0 Valid size of all the headers. | |
| @retval 0 Structure is bad and size cannot be found. | |
| **/ | |
| UINT32 | |
| GetAllHeaderSize ( | |
| IN CONST EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image, | |
| IN UINT32 AdditionalHeaderSize | |
| ) | |
| { | |
| UINT32 CalculatedSize; | |
| if (Image == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetAllHeaderSize() - Image is NULL.\n", mImageIdName)); | |
| return 0; | |
| } | |
| CalculatedSize = sizeof (Image->MonotonicCount) + | |
| AdditionalHeaderSize + | |
| Image->AuthInfo.Hdr.dwLength; | |
| // | |
| // Check to make sure that operation can be safely performed. | |
| // | |
| if ((CalculatedSize < sizeof (Image->MonotonicCount)) || | |
| (CalculatedSize < AdditionalHeaderSize) || | |
| (CalculatedSize < Image->AuthInfo.Hdr.dwLength)) | |
| { | |
| // | |
| // Integer overflow. Invalid image. | |
| // | |
| return 0; | |
| } | |
| return CalculatedSize; | |
| } | |
| /** | |
| Checks if the firmware image is valid for the device. | |
| This function allows firmware update application to validate the firmware image without | |
| invoking the SetImage() first. | |
| @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[out] ImageUpdatable Indicates if the new image is valid for update. It also provides, | |
| if available, additional information if the image is invalid. | |
| @param[out] LastAttemptStatus A pointer to a UINT32 that holds the last attempt status to report | |
| back to the ESRT table in case of error. If an error does not occur, | |
| this function will set the value to LAST_ATTEMPT_STATUS_SUCCESS. | |
| This function will return error codes that occur within this function | |
| implementation within a driver range of last attempt error codes from | |
| LAST_ATTEMPT_STATUS_DRIVER_MIN_ERROR_CODE_VALUE | |
| to LAST_ATTEMPT_STATUS_DRIVER_MAX_ERROR_CODE_VALUE. | |
| This function might also return error codes that occur within libraries | |
| linked against this module that return last attempt error codes such as: | |
| LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_LIB_MIN_ERROR_CODE_VALUE to | |
| LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_LIB_MAX_ERROR_CODE_VALUE | |
| LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_CHECK_LIB_MIN_ERROR_CODE_VALUE to | |
| LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_CHECK_LIB_MAX_ERROR_CODE_VALUE | |
| @retval EFI_SUCCESS The image was successfully checked. | |
| @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 | |
| CheckTheImageInternal ( | |
| IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, | |
| IN UINT8 ImageIndex, | |
| IN CONST VOID *Image, | |
| IN UINTN ImageSize, | |
| OUT UINT32 *ImageUpdatable, | |
| OUT UINT32 *LastAttemptStatus | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 LocalLastAttemptStatus; | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; | |
| UINTN RawSize; | |
| VOID *FmpPayloadHeader; | |
| UINTN FmpPayloadSize; | |
| UINT32 Version; | |
| UINT32 FmpHeaderSize; | |
| UINTN AllHeaderSize; | |
| UINT32 Index; | |
| VOID *PublicKeyData; | |
| UINTN PublicKeyDataLength; | |
| UINT8 *PublicKeyDataXdr; | |
| UINT8 *PublicKeyDataXdrEnd; | |
| EFI_FIRMWARE_IMAGE_DEP *Dependencies; | |
| UINT32 DependenciesSize; | |
| Status = EFI_SUCCESS; | |
| LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; | |
| RawSize = 0; | |
| FmpPayloadHeader = NULL; | |
| FmpPayloadSize = 0; | |
| Version = 0; | |
| FmpHeaderSize = 0; | |
| AllHeaderSize = 0; | |
| Dependencies = NULL; | |
| DependenciesSize = 0; | |
| if (!FeaturePcdGet (PcdFmpDeviceStorageAccessEnable)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| if (LastAttemptStatus == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImageInternal() - LastAttemptStatus is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| // | |
| // A last attempt status error code will always override the success | |
| // value before returning from the function | |
| // | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; | |
| if (This == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckImage() - This is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_PROTOCOL_ARG_MISSING; | |
| goto cleanup; | |
| } | |
| // | |
| // Retrieve the private context structure | |
| // | |
| Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This); | |
| FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext); | |
| // | |
| // Make sure the descriptor has already been loaded or refreshed | |
| // | |
| PopulateDescriptor (Private); | |
| if (ImageUpdatable == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckImage() - ImageUpdatable Pointer Parameter is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_IMAGE_NOT_UPDATABLE; | |
| goto cleanup; | |
| } | |
| // | |
| // Set to valid and then if any tests fail it will update this flag. | |
| // | |
| *ImageUpdatable = IMAGE_UPDATABLE_VALID; | |
| // | |
| // Set to satisfied and then if dependency evaluates to false it will update this flag. | |
| // | |
| Private->DependenciesSatisfied = TRUE; | |
| if (Image == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckImage() - Image Pointer Parameter is NULL.\n", mImageIdName)); | |
| // | |
| // not sure if this is needed | |
| // | |
| *ImageUpdatable = IMAGE_UPDATABLE_INVALID; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_IMAGE_NOT_PROVIDED; | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| PublicKeyDataXdr = PcdGetPtr (PcdFmpDevicePkcs7CertBufferXdr); | |
| PublicKeyDataXdrEnd = PublicKeyDataXdr + PcdGetSize (PcdFmpDevicePkcs7CertBufferXdr); | |
| if ((PublicKeyDataXdr == NULL) || (PublicKeyDataXdr == PublicKeyDataXdrEnd)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Invalid certificate, skipping it.\n", mImageIdName)); | |
| Status = EFI_ABORTED; | |
| LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_INVALID_CERTIFICATE; | |
| } else { | |
| // | |
| // Try each key from PcdFmpDevicePkcs7CertBufferXdr | |
| // | |
| for (Index = 1; PublicKeyDataXdr < PublicKeyDataXdrEnd; Index++) { | |
| Index++; | |
| DEBUG ( | |
| (DEBUG_INFO, | |
| "FmpDxe(%s): Certificate #%d [%p..%p].\n", | |
| mImageIdName, | |
| Index, | |
| PublicKeyDataXdr, | |
| PublicKeyDataXdrEnd | |
| ) | |
| ); | |
| if ((PublicKeyDataXdr + sizeof (UINT32)) > PublicKeyDataXdrEnd) { | |
| // | |
| // Key data extends beyond end of PCD | |
| // | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Certificate size extends beyond end of PCD, skipping it.\n", mImageIdName)); | |
| Status = EFI_ABORTED; | |
| LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_INVALID_KEY_LENGTH_VALUE; | |
| break; | |
| } | |
| // | |
| // Read key length stored in big-endian format | |
| // | |
| PublicKeyDataLength = SwapBytes32 (*(UINT32 *)(PublicKeyDataXdr)); | |
| // | |
| // Point to the start of the key data | |
| // | |
| PublicKeyDataXdr += sizeof (UINT32); | |
| if (PublicKeyDataXdr + PublicKeyDataLength > PublicKeyDataXdrEnd) { | |
| // | |
| // Key data extends beyond end of PCD | |
| // | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Certificate extends beyond end of PCD, skipping it.\n", mImageIdName)); | |
| Status = EFI_ABORTED; | |
| LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_INVALID_KEY_LENGTH; | |
| break; | |
| } | |
| PublicKeyData = PublicKeyDataXdr; | |
| Status = AuthenticateFmpImage ( | |
| (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, | |
| ImageSize, | |
| PublicKeyData, | |
| PublicKeyDataLength | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| break; | |
| } | |
| PublicKeyDataXdr += PublicKeyDataLength; | |
| PublicKeyDataXdr = (UINT8 *)ALIGN_POINTER (PublicKeyDataXdr, sizeof (UINT32)); | |
| } | |
| } | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - Authentication Failed %r.\n", mImageIdName, Status)); | |
| if (LocalLastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS) { | |
| *LastAttemptStatus = LocalLastAttemptStatus; | |
| } else { | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_IMAGE_AUTH_FAILURE; | |
| } | |
| goto cleanup; | |
| } | |
| // | |
| // Check to make sure index is 1 | |
| // | |
| if (ImageIndex != 1) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckImage() - Image Index Invalid.\n", mImageIdName)); | |
| *ImageUpdatable = IMAGE_UPDATABLE_INVALID_TYPE; | |
| Status = EFI_INVALID_PARAMETER; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_INVALID_IMAGE_INDEX; | |
| goto cleanup; | |
| } | |
| // | |
| // Get the dependency from Image. | |
| // | |
| Dependencies = GetImageDependency ( | |
| (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, | |
| ImageSize, | |
| &DependenciesSize, | |
| LastAttemptStatus | |
| ); | |
| if (*LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS) { | |
| Status = EFI_ABORTED; | |
| goto cleanup; | |
| } | |
| // | |
| // Check the FmpPayloadHeader | |
| // | |
| FmpPayloadHeader = GetFmpHeader ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, DependenciesSize, &FmpPayloadSize); | |
| if (FmpPayloadHeader == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - GetFmpHeader failed.\n", mImageIdName)); | |
| Status = EFI_ABORTED; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_GET_FMP_HEADER; | |
| goto cleanup; | |
| } | |
| Status = GetFmpPayloadHeaderVersion (FmpPayloadHeader, FmpPayloadSize, &Version); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - GetFmpPayloadHeaderVersion failed %r.\n", mImageIdName, Status)); | |
| *ImageUpdatable = IMAGE_UPDATABLE_INVALID; | |
| Status = EFI_SUCCESS; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_GET_FMP_HEADER_VERSION; | |
| goto cleanup; | |
| } | |
| // | |
| // Check the lowest supported version | |
| // | |
| if (Version < Private->Descriptor.LowestSupportedImageVersion) { | |
| DEBUG ( | |
| (DEBUG_ERROR, | |
| "FmpDxe(%s): CheckTheImage() - Version Lower than lowest supported version. 0x%08X < 0x%08X\n", | |
| mImageIdName, Version, Private->Descriptor.LowestSupportedImageVersion) | |
| ); | |
| *ImageUpdatable = IMAGE_UPDATABLE_INVALID_OLD; | |
| Status = EFI_SUCCESS; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_VERSION_TOO_LOW; | |
| goto cleanup; | |
| } | |
| // | |
| // Evaluate dependency expression | |
| // | |
| Private->DependenciesSatisfied = CheckFmpDependency ( | |
| Private->Descriptor.ImageTypeId, | |
| Version, | |
| Dependencies, | |
| DependenciesSize, | |
| &LocalLastAttemptStatus | |
| ); | |
| if (!Private->DependenciesSatisfied) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - Dependency check failed.\n", mImageIdName)); | |
| *ImageUpdatable = IMAGE_UPDATABLE_INVALID; | |
| Status = EFI_SUCCESS; | |
| *LastAttemptStatus = LocalLastAttemptStatus; | |
| goto cleanup; | |
| } | |
| // | |
| // Get the FmpHeaderSize so we can determine the real payload size | |
| // | |
| Status = GetFmpPayloadHeaderSize (FmpPayloadHeader, FmpPayloadSize, &FmpHeaderSize); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetFmpPayloadHeaderSize failed %r.\n", Status)); | |
| *ImageUpdatable = IMAGE_UPDATABLE_INVALID; | |
| Status = EFI_SUCCESS; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_GET_FMP_HEADER_SIZE; | |
| goto cleanup; | |
| } | |
| // | |
| // Call FmpDevice Lib Check Image on the | |
| // Raw payload. So all headers need stripped off | |
| // | |
| AllHeaderSize = GetAllHeaderSize ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize + DependenciesSize); | |
| if (AllHeaderSize == 0) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - GetAllHeaderSize failed.\n", mImageIdName)); | |
| Status = EFI_ABORTED; | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_GET_ALL_HEADER_SIZE; | |
| goto cleanup; | |
| } | |
| RawSize = ImageSize - AllHeaderSize; | |
| // | |
| // FmpDeviceLib CheckImage function to do any specific checks | |
| // | |
| Status = FmpDeviceCheckImageWithStatus ((((UINT8 *)Image) + AllHeaderSize), RawSize, ImageUpdatable, LastAttemptStatus); | |
| if (EFI_ERROR (Status)) { | |
| // The image cannot be valid if an error occurred checking the image | |
| if (*ImageUpdatable == IMAGE_UPDATABLE_VALID) { | |
| *ImageUpdatable = IMAGE_UPDATABLE_INVALID; | |
| } | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - FmpDeviceLib CheckImage failed. Status = %r\n", mImageIdName, Status)); | |
| } | |
| // | |
| // Only validate the library last attempt status code if the image is not updatable. | |
| // This specifically avoids converting LAST_ATTEMPT_STATUS_SUCCESS if it set for an updatable image. | |
| // | |
| if (*ImageUpdatable != IMAGE_UPDATABLE_VALID) { | |
| // | |
| // LastAttemptStatus returned from the device library should fall within the designated error range | |
| // [LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE, LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUE] | |
| // | |
| if ((*LastAttemptStatus < LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE) || | |
| (*LastAttemptStatus > LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUE)) | |
| { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "FmpDxe(%s): CheckTheImage() - LastAttemptStatus %d from FmpDeviceCheckImageWithStatus() is invalid.\n", | |
| mImageIdName, | |
| *LastAttemptStatus | |
| )); | |
| *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; | |
| } | |
| } | |
| cleanup: | |
| return Status; | |
| } | |
| /** | |
| Checks if the firmware image is valid for the device. | |
| This function allows firmware update application to validate the firmware image without | |
| invoking the SetImage() first. | |
| @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[out] ImageUpdatable Indicates if the new image is valid for update. It also provides, | |
| if available, additional information if the image is invalid. | |
| @retval EFI_SUCCESS The image was successfully checked. | |
| @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 | |
| CheckTheImage ( | |
| IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, | |
| IN UINT8 ImageIndex, | |
| IN CONST VOID *Image, | |
| IN UINTN ImageSize, | |
| OUT UINT32 *ImageUpdatable | |
| ) | |
| { | |
| UINT32 LastAttemptStatus; | |
| return CheckTheImageInternal (This, ImageIndex, Image, ImageSize, ImageUpdatable, &LastAttemptStatus); | |
| } | |
| /** | |
| 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 | |
| SetTheImage ( | |
| 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; | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; | |
| UINT32 Updateable; | |
| BOOLEAN BooleanValue; | |
| UINT32 FmpHeaderSize; | |
| VOID *FmpHeader; | |
| UINTN FmpPayloadSize; | |
| UINT32 AllHeaderSize; | |
| UINT32 IncomingFwVersion; | |
| UINT32 LastAttemptStatus; | |
| UINT32 Version; | |
| UINT32 LowestSupportedVersion; | |
| EFI_FIRMWARE_IMAGE_DEP *Dependencies; | |
| UINT32 DependenciesSize; | |
| Status = EFI_SUCCESS; | |
| Private = NULL; | |
| Updateable = 0; | |
| BooleanValue = FALSE; | |
| FmpHeaderSize = 0; | |
| FmpHeader = NULL; | |
| FmpPayloadSize = 0; | |
| AllHeaderSize = 0; | |
| IncomingFwVersion = 0; | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; | |
| Dependencies = NULL; | |
| DependenciesSize = 0; | |
| if (!FeaturePcdGet (PcdFmpDeviceStorageAccessEnable)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| if (This == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - This is NULL.\n", mImageIdName)); | |
| Status = EFI_INVALID_PARAMETER; | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_PROTOCOL_ARG_MISSING; | |
| goto cleanup; | |
| } | |
| // | |
| // Retrieve the private context structure | |
| // | |
| Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This); | |
| FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext); | |
| // | |
| // Make sure the descriptor has already been loaded or refreshed | |
| // | |
| PopulateDescriptor (Private); | |
| // | |
| // Set to 0 to clear any previous results. | |
| // | |
| SetLastAttemptVersionInVariable (Private, IncomingFwVersion); | |
| // | |
| // if we have locked the device, then skip the set operation. | |
| // it should be blocked by hardware too but we can catch here even faster | |
| // | |
| if (Private->FmpDeviceLocked) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - Device is already locked. Can't update.\n", mImageIdName)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_DEVICE_LOCKED; | |
| Status = EFI_UNSUPPORTED; | |
| goto cleanup; | |
| } | |
| // | |
| // Call check image to verify the image | |
| // | |
| Status = CheckTheImageInternal (This, ImageIndex, Image, ImageSize, &Updateable, &LastAttemptStatus); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - Check The Image failed with %r.\n", mImageIdName, Status)); | |
| goto cleanup; | |
| } | |
| // | |
| // Get the dependency from Image. | |
| // | |
| Dependencies = GetImageDependency ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &DependenciesSize, &LastAttemptStatus); | |
| // | |
| // No functional error in CheckTheImage. Attempt to get the Version to | |
| // support better error reporting. | |
| // | |
| FmpHeader = GetFmpHeader ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, DependenciesSize, &FmpPayloadSize); | |
| if (FmpHeader == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - GetFmpHeader failed.\n", mImageIdName)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_GET_FMP_HEADER; | |
| Status = EFI_ABORTED; | |
| goto cleanup; | |
| } | |
| Status = GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &IncomingFwVersion); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Set to actual value | |
| // | |
| SetLastAttemptVersionInVariable (Private, IncomingFwVersion); | |
| } | |
| if (Updateable != IMAGE_UPDATABLE_VALID) { | |
| DEBUG ( | |
| (DEBUG_ERROR, | |
| "FmpDxe(%s): SetTheImage() - Check The Image returned that the Image was not valid for update. Updatable value = 0x%X.\n", | |
| mImageIdName, Updateable) | |
| ); | |
| if (Private->DependenciesSatisfied == FALSE) { | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES; | |
| } | |
| Status = EFI_ABORTED; | |
| goto cleanup; | |
| } | |
| if (Progress == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - Invalid progress callback\n", mImageIdName)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_PROGRESS_CALLBACK_ERROR; | |
| Status = EFI_INVALID_PARAMETER; | |
| goto cleanup; | |
| } | |
| mProgressFunc = Progress; | |
| // | |
| // Checking the image is at least 1% | |
| // | |
| Status = Progress (1); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - Progress Callback failed with Status %r.\n", mImageIdName, Status)); | |
| } | |
| // | |
| // Check System Power | |
| // | |
| Status = CheckSystemPower (&BooleanValue); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - CheckSystemPower - API call failed %r.\n", mImageIdName, Status)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_CHECK_POWER_API; | |
| goto cleanup; | |
| } | |
| if (!BooleanValue) { | |
| Status = EFI_ABORTED; | |
| DEBUG ( | |
| (DEBUG_ERROR, | |
| "FmpDxe(%s): SetTheImage() - CheckSystemPower - returned False. Update not allowed due to System Power.\n", mImageIdName) | |
| ); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT; | |
| goto cleanup; | |
| } | |
| Progress (2); | |
| // | |
| // Check System Thermal | |
| // | |
| Status = CheckSystemThermal (&BooleanValue); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - CheckSystemThermal - API call failed %r.\n", mImageIdName, Status)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_CHECK_SYS_THERMAL_API; | |
| goto cleanup; | |
| } | |
| if (!BooleanValue) { | |
| Status = EFI_ABORTED; | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_THERMAL; | |
| DEBUG ( | |
| (DEBUG_ERROR, | |
| "FmpDxe(%s): SetTheImage() - CheckSystemThermal - returned False. Update not allowed due to System Thermal.\n", mImageIdName) | |
| ); | |
| goto cleanup; | |
| } | |
| Progress (3); | |
| // | |
| // Check System Environment | |
| // | |
| Status = CheckSystemEnvironment (&BooleanValue); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - CheckSystemEnvironment - API call failed %r.\n", mImageIdName, Status)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_CHECK_SYS_ENV_API; | |
| goto cleanup; | |
| } | |
| if (!BooleanValue) { | |
| Status = EFI_ABORTED; | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_SYSTEM_ENV; | |
| DEBUG ( | |
| (DEBUG_ERROR, | |
| "FmpDxe(%s): SetTheImage() - CheckSystemEnvironment - returned False. Update not allowed due to System Environment.\n", mImageIdName) | |
| ); | |
| goto cleanup; | |
| } | |
| Progress (4); | |
| // | |
| // Save LastAttemptStatus as error so that if SetImage never returns the error | |
| // state is recorded. | |
| // | |
| SetLastAttemptStatusInVariable (Private, LastAttemptStatus); | |
| // | |
| // Strip off all the headers so the device can process its firmware | |
| // | |
| Status = GetFmpPayloadHeaderSize (FmpHeader, FmpPayloadSize, &FmpHeaderSize); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - GetFmpPayloadHeaderSize failed %r.\n", mImageIdName, Status)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_GET_FMP_HEADER_SIZE; | |
| goto cleanup; | |
| } | |
| AllHeaderSize = GetAllHeaderSize ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize + DependenciesSize); | |
| if (AllHeaderSize == 0) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - GetAllHeaderSize failed.\n", mImageIdName)); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_DRIVER_ERROR_GET_ALL_HEADER_SIZE; | |
| Status = EFI_ABORTED; | |
| goto cleanup; | |
| } | |
| // | |
| // Indicate that control is handed off to FmpDeviceLib | |
| // | |
| Progress (5); | |
| // | |
| // Copy the requested image to the firmware using the FmpDeviceLib | |
| // | |
| Status = FmpDeviceSetImageWithStatus ( | |
| (((UINT8 *)Image) + AllHeaderSize), | |
| ImageSize - AllHeaderSize, | |
| VendorCode, | |
| FmpDxeProgress, | |
| IncomingFwVersion, | |
| AbortReason, | |
| &LastAttemptStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() SetImage from FmpDeviceLib failed. Status = %r.\n", mImageIdName, Status)); | |
| // | |
| // LastAttemptStatus returned from the device library should fall within the designated error range | |
| // [LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE, LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUE] | |
| // | |
| if ((LastAttemptStatus < LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE) || | |
| (LastAttemptStatus > LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUE)) | |
| { | |
| DEBUG ( | |
| (DEBUG_ERROR, | |
| "FmpDxe(%s): SetTheImage() - LastAttemptStatus %d from FmpDeviceSetImageWithStatus() is invalid.\n", | |
| mImageIdName, | |
| LastAttemptStatus) | |
| ); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; | |
| } | |
| goto cleanup; | |
| } | |
| // | |
| // Store the dependency | |
| // | |
| if (Private->Descriptor.AttributesSetting & IMAGE_ATTRIBUTE_DEPENDENCY) { | |
| Status = SaveFmpDependency (Dependencies, DependenciesSize); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() SaveFmpDependency from FmpDependencyCheckLib failed. (%r)\n", mImageIdName, Status)); | |
| } | |
| Status = EFI_SUCCESS; | |
| } | |
| // | |
| // Finished the update without error | |
| // Indicate that control has been returned from FmpDeviceLib | |
| // | |
| Progress (99); | |
| // | |
| // Update the version stored in variable | |
| // | |
| if (!Private->RuntimeVersionSupported) { | |
| Version = DEFAULT_VERSION; | |
| GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &Version); | |
| SetVersionInVariable (Private, Version); | |
| } | |
| // | |
| // Update lowest supported variable | |
| // | |
| LowestSupportedVersion = DEFAULT_LOWESTSUPPORTEDVERSION; | |
| GetFmpPayloadHeaderLowestSupportedVersion (FmpHeader, FmpPayloadSize, &LowestSupportedVersion); | |
| SetLowestSupportedVersionInVariable (Private, LowestSupportedVersion); | |
| LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; | |
| cleanup: | |
| mProgressFunc = NULL; | |
| if (Private != NULL) { | |
| DEBUG ((DEBUG_INFO, "FmpDxe(%s): SetTheImage() LastAttemptStatus: %u.\n", mImageIdName, LastAttemptStatus)); | |
| SetLastAttemptStatusInVariable (Private, LastAttemptStatus); | |
| } | |
| if (Progress != NULL) { | |
| // | |
| // Set progress to 100 after everything is done including recording Status. | |
| // | |
| Progress (100); | |
| } | |
| // | |
| // Need repopulate after SetImage is called to | |
| // update LastAttemptVersion and LastAttemptStatus. | |
| // | |
| if (Private != NULL) { | |
| Private->DescriptorPopulated = FALSE; | |
| } | |
| return Status; | |
| } | |
| /** | |
| Returns information about the firmware package. | |
| This function returns package information. | |
| @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. | |
| @param[out] PackageVersion A version number that represents all the firmware images in the device. | |
| The format is vendor specific and new version must have a greater value | |
| than the old version. If PackageVersion is not supported, the value is | |
| 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version | |
| comparison is to be performed using PackageVersionName. A value of | |
| 0xFFFFFFFD indicates that package version update is in progress. | |
| @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing | |
| the package version name. The buffer is allocated by this function with | |
| AllocatePool(), and it is the caller's responsibility to free it with a | |
| call to FreePool(). | |
| @param[out] PackageVersionNameMaxLen The maximum length of package version name if device supports update of | |
| package version name. A value of 0 indicates the device does not support | |
| update of package version name. Length is the number of Unicode characters, | |
| including the terminating null character. | |
| @param[out] AttributesSupported Package attributes that are supported by this device. See 'Package Attribute | |
| Definitions' for possible returned values of this parameter. A value of 1 | |
| indicates the attribute is supported and the current setting value is | |
| indicated in AttributesSetting. A value of 0 indicates the attribute is not | |
| supported and the current setting value in AttributesSetting is meaningless. | |
| @param[out] AttributesSetting Package attributes. See 'Package Attribute Definitions' for possible returned | |
| values of this parameter | |
| @retval EFI_SUCCESS The package information was successfully returned. | |
| @retval EFI_UNSUPPORTED The operation is not supported. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetPackageInfo ( | |
| IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, | |
| OUT UINT32 *PackageVersion, | |
| OUT CHAR16 **PackageVersionName, | |
| OUT UINT32 *PackageVersionNameMaxLen, | |
| OUT UINT64 *AttributesSupported, | |
| OUT UINT64 *AttributesSetting | |
| ) | |
| { | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Updates information about the firmware package. | |
| This function updates package information. | |
| This function returns EFI_UNSUPPORTED if the package information is not updatable. | |
| VendorCode enables vendor to implement vendor-specific package information update policy. | |
| Null if the caller did not specify this policy or use the default policy. | |
| @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. | |
| @param[in] Image Points to the authentication image. | |
| Null if authentication is not required. | |
| @param[in] ImageSize Size of the authentication image in bytes. | |
| 0 if authentication is not required. | |
| @param[in] VendorCode This enables vendor to implement vendor-specific firmware | |
| image update policy. | |
| Null indicates the caller did not specify this policy or use | |
| the default policy. | |
| @param[in] PackageVersion The new package version. | |
| @param[in] PackageVersionName A pointer to the new null-terminated Unicode string representing | |
| the package version name. | |
| The string length is equal to or less than the value returned in | |
| PackageVersionNameMaxLen. | |
| @retval EFI_SUCCESS The device was successfully updated with the new package | |
| information. | |
| @retval EFI_INVALID_PARAMETER The PackageVersionName length is longer than the value | |
| returned in PackageVersionNameMaxLen. | |
| @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 | |
| SetPackageInfo ( | |
| IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, | |
| IN CONST VOID *Image, | |
| IN UINTN ImageSize, | |
| IN CONST VOID *VendorCode, | |
| IN UINT32 PackageVersion, | |
| IN CONST CHAR16 *PackageVersionName | |
| ) | |
| { | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Event notification function that is invoked when the event GUID specified by | |
| PcdFmpDeviceLockEventGuid is signaled. | |
| @param[in] Event Event whose notification function is being invoked. | |
| @param[in] Context The pointer to the notification function's context, | |
| which is implementation-dependent. | |
| **/ | |
| VOID | |
| EFIAPI | |
| FmpDxeLockEventNotify ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; | |
| if (Context == NULL) { | |
| ASSERT (Context != NULL); | |
| return; | |
| } | |
| Private = (FIRMWARE_MANAGEMENT_PRIVATE_DATA *)Context; | |
| if (!Private->FmpDeviceLocked) { | |
| // | |
| // Lock the firmware device | |
| // | |
| FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext); | |
| Status = FmpDeviceLock (); | |
| if (EFI_ERROR (Status)) { | |
| if (Status != EFI_UNSUPPORTED) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): FmpDeviceLock() returned error. Status = %r\n", mImageIdName, Status)); | |
| } else { | |
| DEBUG ((DEBUG_WARN, "FmpDxe(%s): FmpDeviceLock() returned error. Status = %r\n", mImageIdName, Status)); | |
| } | |
| } | |
| Private->FmpDeviceLocked = TRUE; | |
| } | |
| } | |
| /** | |
| Function to install FMP instance. | |
| @param[in] Handle The device handle to install a FMP instance on. | |
| @retval EFI_SUCCESS FMP Installed | |
| @retval EFI_INVALID_PARAMETER Handle was invalid | |
| @retval other Error installing FMP | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| InstallFmpInstance ( | |
| IN EFI_HANDLE Handle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; | |
| // | |
| // Only allow a single FMP Protocol instance to be installed | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Handle, | |
| &gEfiFirmwareManagementProtocolGuid, | |
| (VOID **)&Fmp, | |
| NULL, | |
| NULL, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| return EFI_ALREADY_STARTED; | |
| } | |
| // | |
| // Allocate FMP Protocol instance | |
| // | |
| Private = AllocateCopyPool ( | |
| sizeof (mFirmwareManagementPrivateDataTemplate), | |
| &mFirmwareManagementPrivateDataTemplate | |
| ); | |
| if (Private == NULL) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to allocate memory for private structure.\n", mImageIdName)); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto cleanup; | |
| } | |
| // | |
| // Initialize private context data structure | |
| // | |
| Private->Handle = Handle; | |
| Private->FmpDeviceContext = NULL; | |
| Status = FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext); | |
| if (Status == EFI_UNSUPPORTED) { | |
| Private->FmpDeviceContext = NULL; | |
| } else if (EFI_ERROR (Status)) { | |
| goto cleanup; | |
| } | |
| // | |
| // Make sure the descriptor has already been loaded or refreshed | |
| // | |
| PopulateDescriptor (Private); | |
| if (IsLockFmpDeviceAtLockEventGuidRequired ()) { | |
| // | |
| // Register all UEFI Variables used by this module to be locked. | |
| // | |
| Status = LockAllFmpVariables (Private); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to register variables to lock. Status = %r.\n", mImageIdName, Status)); | |
| } else { | |
| DEBUG ((DEBUG_INFO, "FmpDxe(%s): All variables registered to lock\n", mImageIdName)); | |
| } | |
| // | |
| // Create and register notify function to lock the FMP device. | |
| // | |
| Status = gBS->CreateEventEx ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| FmpDxeLockEventNotify, | |
| Private, | |
| mLockGuid, | |
| &Private->FmpDeviceLockEvent | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Failed to register notification. Status = %r\n", mImageIdName, Status)); | |
| } | |
| ASSERT_EFI_ERROR (Status); | |
| } else { | |
| DEBUG ((DEBUG_VERBOSE, "FmpDxe(%s): Not registering notification to call FmpDeviceLock() because mfg mode\n", mImageIdName)); | |
| } | |
| // | |
| // Install FMP Protocol and FMP Progress Protocol | |
| // | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &Private->Handle, | |
| &gEfiFirmwareManagementProtocolGuid, | |
| &Private->Fmp, | |
| &gEdkiiFirmwareManagementProgressProtocolGuid, | |
| &mFmpProgress, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Protocol install error. Status = %r.\n", mImageIdName, Status)); | |
| goto cleanup; | |
| } | |
| cleanup: | |
| if (EFI_ERROR (Status)) { | |
| if (Private != NULL) { | |
| if (Private->FmpDeviceLockEvent != NULL) { | |
| gBS->CloseEvent (Private->FmpDeviceLockEvent); | |
| } | |
| if (Private->Descriptor.VersionName != NULL) { | |
| FreePool (Private->Descriptor.VersionName); | |
| } | |
| if (Private->FmpDeviceContext != NULL) { | |
| FmpDeviceSetContext (NULL, &Private->FmpDeviceContext); | |
| } | |
| 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); | |
| } | |
| FreePool (Private); | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Function to uninstall FMP instance. | |
| @param[in] Handle The device handle to install a FMP instance on. | |
| @retval EFI_SUCCESS FMP Installed | |
| @retval EFI_INVALID_PARAMETER Handle was invalid | |
| @retval other Error installing FMP | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| UninstallFmpInstance ( | |
| IN EFI_HANDLE Handle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; | |
| FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; | |
| Status = gBS->OpenProtocol ( | |
| Handle, | |
| &gEfiFirmwareManagementProtocolGuid, | |
| (VOID **)&Fmp, | |
| NULL, | |
| NULL, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Protocol open error. Status = %r.\n", mImageIdName, Status)); | |
| return Status; | |
| } | |
| Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (Fmp); | |
| FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext); | |
| if (Private->FmpDeviceLockEvent != NULL) { | |
| gBS->CloseEvent (Private->FmpDeviceLockEvent); | |
| } | |
| Status = gBS->UninstallMultipleProtocolInterfaces ( | |
| Private->Handle, | |
| &gEfiFirmwareManagementProtocolGuid, | |
| &Private->Fmp, | |
| &gEdkiiFirmwareManagementProgressProtocolGuid, | |
| &mFmpProgress, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): Protocol uninstall error. Status = %r.\n", mImageIdName, Status)); | |
| return Status; | |
| } | |
| if (Private->Descriptor.VersionName != NULL) { | |
| FreePool (Private->Descriptor.VersionName); | |
| } | |
| if (Private->FmpDeviceContext != NULL) { | |
| FmpDeviceSetContext (NULL, &Private->FmpDeviceContext); | |
| } | |
| 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); | |
| } | |
| FreePool (Private); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Unloads the application and its installed protocol. | |
| @param ImageHandle Handle that identifies the image to be unloaded. | |
| @param SystemTable The system table. | |
| @retval EFI_SUCCESS The image has been unloaded. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FmpDxeLibDestructor ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| if (mFmpSingleInstance) { | |
| return UninstallFmpInstance (ImageHandle); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Main entry for this driver/library. | |
| @param[in] ImageHandle Image handle this driver. | |
| @param[in] SystemTable Pointer to SystemTable. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| FmpDxeEntryPoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Verify that a new FILE_GUID value has been provided in the <Defines> | |
| // section of this module. The FILE_GUID is the ESRT GUID that must be | |
| // unique for each updatable firmware image. | |
| // | |
| if (CompareGuid (&mDefaultModuleFileGuid, &gEfiCallerIdGuid)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe: Use of default FILE_GUID detected. FILE_GUID must be set to a unique value.\n")); | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Get the ImageIdName value for the EFI_FIRMWARE_IMAGE_DESCRIPTOR from a PCD. | |
| // | |
| mImageIdName = (CHAR16 *)PcdGetPtr (PcdFmpDeviceImageIdName); | |
| if ((PcdGetSize (PcdFmpDeviceImageIdName) <= 2) || (mImageIdName[0] == 0)) { | |
| // | |
| // PcdFmpDeviceImageIdName must be set to a non-empty Unicode string | |
| // | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%g): PcdFmpDeviceImageIdName is an empty string.\n", &gEfiCallerIdGuid)); | |
| ASSERT (FALSE); | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Detects if PcdFmpDevicePkcs7CertBufferXdr contains a test key. | |
| // | |
| DetectTestKey (); | |
| // | |
| // Fill in FMP Progress Protocol fields for Version 1 | |
| // | |
| mFmpProgress.Version = 1; | |
| mFmpProgress.ProgressBarForegroundColor.Raw = PcdGet32 (PcdFmpDeviceProgressColor); | |
| mFmpProgress.WatchdogSeconds = PcdGet8 (PcdFmpDeviceProgressWatchdogTimeInSeconds); | |
| // The lock event GUID is retrieved from PcdFmpDeviceLockEventGuid. | |
| // If PcdFmpDeviceLockEventGuid is not the size of an EFI_GUID, then | |
| // gEfiEndOfDxeEventGroupGuid is used. | |
| // | |
| mLockGuid = &gEfiEndOfDxeEventGroupGuid; | |
| if (PcdGetSize (PcdFmpDeviceLockEventGuid) == sizeof (EFI_GUID)) { | |
| mLockGuid = (EFI_GUID *)PcdGetPtr (PcdFmpDeviceLockEventGuid); | |
| } | |
| DEBUG ((DEBUG_INFO, "FmpDxe(%s): Lock GUID: %g\n", mImageIdName, mLockGuid)); | |
| // | |
| // Register with library the install function so if the library uses | |
| // UEFI driver model/driver binding protocol it can install FMP on its device handle | |
| // If library is simple lib that does not use driver binding then it should return | |
| // unsupported and this will install the FMP instance on the ImageHandle | |
| // | |
| Status = RegisterFmpInstaller (InstallFmpInstance); | |
| if (Status == EFI_UNSUPPORTED) { | |
| mFmpSingleInstance = TRUE; | |
| DEBUG ((DEBUG_INFO, "FmpDxe(%s): FmpDeviceLib registration returned EFI_UNSUPPORTED. Installing single FMP instance.\n", mImageIdName)); | |
| Status = RegisterFmpUninstaller (UninstallFmpInstance); | |
| if (Status == EFI_UNSUPPORTED) { | |
| Status = InstallFmpInstance (ImageHandle); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): FmpDeviceLib RegisterFmpInstaller and RegisterFmpUninstaller do not match.\n", mImageIdName)); | |
| Status = EFI_UNSUPPORTED; | |
| } | |
| } else if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): FmpDeviceLib registration returned %r. No FMP installed.\n", mImageIdName, Status)); | |
| } else { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "FmpDxe(%s): FmpDeviceLib registration returned EFI_SUCCESS. Expect FMP to be installed during the BDS/Device connection phase.\n", | |
| mImageIdName | |
| )); | |
| Status = RegisterFmpUninstaller (UninstallFmpInstance); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "FmpDxe(%s): FmpDeviceLib RegisterFmpInstaller and RegisterFmpUninstaller do not match.\n", mImageIdName)); | |
| } | |
| } | |
| return Status; | |
| } |