+/** @file\r
+ Produces a Firmware Management Protocol that supports updates to a firmware\r
+ image stored in a firmware device with platform and firmware device specific\r
+ information provided through PCDs and libraries.\r
+\r
+ Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+ Redistribution and use in source and binary forms, with or without\r
+ modification, are permitted provided that the following conditions are met:\r
+ 1. Redistributions of source code must retain the above copyright notice,\r
+ this list of conditions and the following disclaimer.\r
+ 2. Redistributions in binary form must reproduce the above copyright notice,\r
+ this list of conditions and the following disclaimer in the documentation\r
+ and/or other materials provided with the distribution.\r
+\r
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\r
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\r
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\r
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/FmpAuthenticationLib.h>\r
+#include <Library/FmpDeviceLib.h>\r
+#include <Library/FmpPayloadHeaderLib.h>\r
+#include <Library/CapsuleUpdatePolicyLib.h>\r
+#include <Protocol/FirmwareManagement.h>\r
+#include <Protocol/FirmwareManagementProgress.h>\r
+#include <Guid/SystemResourceTable.h>\r
+#include <Guid/EventGroup.h>\r
+#include "VariableSupport.h"\r
+\r
+#define VERSION_STRING_NOT_SUPPORTED L"VERSION STRING NOT SUPPORTED"\r
+#define VERSION_STRING_NOT_AVAILABLE L"VERSION STRING NOT AVAILABLE"\r
+\r
+/**\r
+ Check to see if any of the keys in PcdFmpDevicePkcs7CertBufferXdr matches\r
+ the test key. PcdFmpDeviceTestKeySha256Digest contains the SHA256 hash of\r
+ the test key. For each key in PcdFmpDevicePkcs7CertBufferXdr, compute the\r
+ SHA256 hash and compare it to PcdFmpDeviceTestKeySha256Digest. If the\r
+ SHA256 hash matches or there is then error computing the SHA256 hash, then\r
+ set PcdTestKeyUsed to TRUE. Skip this check if PcdTestKeyUsed is already\r
+ TRUE or PcdFmpDeviceTestKeySha256Digest is not exactly SHA256_DIGEST_SIZE\r
+ bytes.\r
+**/\r
+VOID\r
+DetectTestKey (\r
+ VOID\r
+ );\r
+\r
+///\r
+/// FILE_GUID from FmpDxe.inf. When FmpDxe.inf is used in a platform, the\r
+/// FILE_GUID must always be overridden in the <Defines> section to provide\r
+/// the ESRT GUID value associated with the updatable firmware image. A\r
+/// check is made in this module's driver entry point to verify that a\r
+/// new FILE_GUID value has been defined.\r
+///\r
+const EFI_GUID mDefaultModuleFileGuid = {\r
+ 0x78ef0a56, 0x1cf0, 0x4535, { 0xb5, 0xda, 0xf6, 0xfd, 0x2f, 0x40, 0x5a, 0x11 }\r
+};\r
+\r
+EFI_FIRMWARE_IMAGE_DESCRIPTOR mDesc;\r
+BOOLEAN mDescriptorPopulated = FALSE;\r
+BOOLEAN mRuntimeVersionSupported = TRUE;\r
+BOOLEAN mFmpInstalled = FALSE;\r
+\r
+///\r
+/// Function pointer to progress function\r
+///\r
+EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS mProgressFunc = NULL;\r
+BOOLEAN mProgressSupported = FALSE;\r
+\r
+CHAR16 *mImageIdName = NULL;\r
+UINT64 mImageId = 0x1;\r
+CHAR16 *mVersionName = NULL;\r
+\r
+EFI_EVENT mFmpDeviceLockEvent;\r
+BOOLEAN mFmpDeviceLocked = FALSE;\r
+\r
+/**\r
+ Callback function to report the process of the firmware updating.\r
+\r
+ Wrap the caller's version in this so that progress from the device lib is\r
+ within the expected range. Convert device lib 0% - 100% to 6% - 98%.\r
+\r
+ FmpDxe 1% - 5% for validation\r
+ FmpDeviceLib 6% - 98% for flashing/update\r
+ FmpDxe 99% - 100% finish\r
+\r
+ @param[in] Completion A value between 1 and 100 indicating the current\r
+ completion progress of the firmware update. Completion\r
+ progress is reported as from 1 to 100 percent. A value\r
+ of 0 is used by the driver to indicate that progress\r
+ reporting is not supported.\r
+\r
+ @retval EFI_SUCCESS The progress was updated.\r
+ @retval EFI_UNSUPPORTED Updating progress is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FmpDxeProgress (\r
+ IN UINTN Completion\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_UNSUPPORTED;\r
+\r
+ if (!mProgressSupported) {\r
+ return Status;\r
+ }\r
+\r
+ if (mProgressFunc == NULL) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Reserve 6% - 98% for the FmpDeviceLib. Call the real progress function.\r
+ //\r
+ Status = mProgressFunc (((Completion * 92) / 100) + 6);\r
+\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ mProgressSupported = FALSE;\r
+ mProgressFunc = NULL;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Returns a pointer to the ImageTypeId GUID value. An attempt is made to get\r
+ the GUID value from the FmpDeviceLib. If the FmpDeviceLib does not provide\r
+ a GUID value, then gEfiCallerIdGuid is returned.\r
+\r
+ @return The ImageTypeId GUID\r
+\r
+**/\r
+EFI_GUID *\r
+GetImageTypeIdGuid (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_GUID *FmpDeviceLibGuid;\r
+\r
+ FmpDeviceLibGuid = NULL;\r
+ Status = FmpDeviceGetImageTypeIdGuidPtr (&FmpDeviceLibGuid);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status != EFI_UNSUPPORTED) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib GetImageTypeIdGuidPtr() returned invalid error %r\n", Status));\r
+ }\r
+ return &gEfiCallerIdGuid;\r
+ }\r
+ if (FmpDeviceLibGuid == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib GetImageTypeIdGuidPtr() returned invalid GUID\n"));\r
+ return &gEfiCallerIdGuid;\r
+ }\r
+ return FmpDeviceLibGuid;\r
+}\r
+\r
+/**\r
+ Returns a pointer to the Null-terminated Unicode ImageIdName string.\r
+\r
+ @return Null-terminated Unicode ImageIdName string.\r
+\r
+**/\r
+CHAR16 *\r
+GetImageTypeNameString (\r
+ VOID\r
+ )\r
+{\r
+ return mImageIdName;\r
+}\r
+\r
+/**\r
+ Lowest supported version is a combo of three parts.\r
+ 1. Check if the device lib has a lowest supported version\r
+ 2. Check if we have a variable for lowest supported version (this will be updated with each capsule applied)\r
+ 3. Check Fixed at build PCD\r
+\r
+ Take the largest value\r
+\r
+**/\r
+UINT32\r
+GetLowestSupportedVersion (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 DeviceLibLowestSupportedVersion;\r
+ UINT32 VariableLowestSupportedVersion;\r
+ UINT32 ReturnLsv;\r
+\r
+ //\r
+ // Get the LowestSupportedVersion.\r
+ //\r
+\r
+ DeviceLibLowestSupportedVersion = DEFAULT_LOWESTSUPPORTEDVERSION;\r
+ ReturnLsv = PcdGet32 (PcdFmpDeviceBuildTimeLowestSupportedVersion);\r
+ if (!IsLowestSupportedVersionCheckRequired ()) {\r
+ return 1;\r
+ }\r
+\r
+ //\r
+ // Check the FmpDeviceLib\r
+ //\r
+ Status = FmpDeviceGetLowestSupportedVersion (&DeviceLibLowestSupportedVersion);\r
+ if (EFI_ERROR (Status)) {\r
+ DeviceLibLowestSupportedVersion = DEFAULT_LOWESTSUPPORTEDVERSION;\r
+ }\r
+\r
+ if (DeviceLibLowestSupportedVersion > ReturnLsv) {\r
+ ReturnLsv = DeviceLibLowestSupportedVersion;\r
+ }\r
+\r
+ //\r
+ // Check the lowest supported version UEFI variable for this device\r
+ //\r
+ VariableLowestSupportedVersion = GetLowestSupportedVersionFromVariable();\r
+ if (VariableLowestSupportedVersion > ReturnLsv) {\r
+ ReturnLsv = VariableLowestSupportedVersion;\r
+ }\r
+\r
+ //\r
+ // Return the largest value\r
+ //\r
+ return ReturnLsv;\r
+}\r
+\r
+/**\r
+ Populates the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure in the module global\r
+ variable mDesc.\r
+\r
+**/\r
+VOID\r
+PopulateDescriptor (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ mDesc.ImageIndex = 1;\r
+ CopyGuid (&mDesc.ImageTypeId, GetImageTypeIdGuid());\r
+ mDesc.ImageId = mImageId;\r
+ mDesc.ImageIdName = GetImageTypeNameString();\r
+\r
+ //\r
+ // Get the version. Some devices don't support getting the firmware version\r
+ // at runtime. If FmpDeviceLib does not support returning a version, then\r
+ // it is stored in a UEFI variable.\r
+ //\r
+ Status = FmpDeviceGetVersion (&mDesc.Version);\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ mRuntimeVersionSupported = FALSE;\r
+ mDesc.Version = GetVersionFromVariable();\r
+ } else if (EFI_ERROR (Status)) {\r
+ //\r
+ // Unexpected error. Use default version.\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: GetVersion() from FmpDeviceLib (%s) returned %r\n", GetImageTypeNameString(), Status));\r
+ mDesc.Version = DEFAULT_VERSION;\r
+ }\r
+\r
+ //\r
+ // Free the current version name. Shouldn't really happen but this populate\r
+ // function could be called multiple times (to refresh).\r
+ //\r
+ if (mVersionName != NULL) {\r
+ FreePool (mVersionName);\r
+ mVersionName = NULL;\r
+ }\r
+\r
+ //\r
+ // Attempt to get the version string from the FmpDeviceLib\r
+ //\r
+ Status = FmpDeviceGetVersionString (&mVersionName);\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ DEBUG ((DEBUG_INFO, "FmpDxe: GetVersionString() unsupported in FmpDeviceLib.\n"));\r
+ mVersionName = AllocateCopyPool (\r
+ sizeof (VERSION_STRING_NOT_SUPPORTED),\r
+ VERSION_STRING_NOT_SUPPORTED\r
+ );\r
+ } else if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO, "FmpDxe: GetVersionString() not available in FmpDeviceLib.\n"));\r
+ mVersionName = AllocateCopyPool (\r
+ sizeof (VERSION_STRING_NOT_AVAILABLE),\r
+ VERSION_STRING_NOT_AVAILABLE\r
+ );\r
+ }\r
+\r
+ mDesc.VersionName = mVersionName;\r
+\r
+ mDesc.LowestSupportedImageVersion = GetLowestSupportedVersion();\r
+\r
+ //\r
+ // Get attributes from the FmpDeviceLib\r
+ //\r
+ FmpDeviceGetAttributes (&mDesc.AttributesSupported, &mDesc.AttributesSetting);\r
+\r
+ //\r
+ // Force set the updatable bits in the attributes;\r
+ //\r
+ mDesc.AttributesSupported |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;\r
+ mDesc.AttributesSetting |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;\r
+\r
+ //\r
+ // Force set the authentication bits in the attributes;\r
+ //\r
+ mDesc.AttributesSupported |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED);\r
+ mDesc.AttributesSetting |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED);\r
+\r
+ mDesc.Compatibilities = 0;\r
+\r
+ //\r
+ // Get the size of the firmware image from the FmpDeviceLib\r
+ //\r
+ Status = FmpDeviceGetSize (&mDesc.Size);\r
+ if (EFI_ERROR (Status)) {\r
+ mDesc.Size = 0;\r
+ }\r
+\r
+ mDesc.LastAttemptVersion = GetLastAttemptVersionFromVariable ();\r
+ mDesc.LastAttemptStatus = GetLastAttemptStatusFromVariable ();\r
+\r
+ mDescriptorPopulated = TRUE;\r
+}\r
+\r
+/**\r
+ Returns information about the current firmware image(s) of the device.\r
+\r
+ This function allows a copy of the current firmware image to be created and saved.\r
+ The saved copy could later been used, for example, in firmware image recovery or rollback.\r
+\r
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.\r
+ @param[in, out] ImageInfoSize A pointer to the size, in bytes, of the ImageInfo buffer.\r
+ On input, this is the size of the buffer allocated by the caller.\r
+ On output, it is the size of the buffer returned by the firmware\r
+ if the buffer was large enough, or the size of the buffer needed\r
+ to contain the image(s) information if the buffer was too small.\r
+ @param[in, out] ImageInfo A pointer to the buffer in which firmware places the current image(s)\r
+ information. The information is an array of EFI_FIRMWARE_IMAGE_DESCRIPTORs.\r
+ @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number\r
+ associated with the EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
+ @param[out] DescriptorCount A pointer to the location in which firmware returns the number of\r
+ descriptors or firmware images within this device.\r
+ @param[out] DescriptorSize A pointer to the location in which firmware returns the size, in bytes,\r
+ of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
+ @param[out] PackageVersion A version number that represents all the firmware images in the device.\r
+ The format is vendor specific and new version must have a greater value\r
+ than the old version. If PackageVersion is not supported, the value is\r
+ 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version comparison\r
+ is to be performed using PackageVersionName. A value of 0xFFFFFFFD indicates\r
+ that package version update is in progress.\r
+ @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the\r
+ package version name. The buffer is allocated by this function with\r
+ AllocatePool(), and it is the caller's responsibility to free it with a call\r
+ to FreePool().\r
+\r
+ @retval EFI_SUCCESS The device was successfully updated with the new image.\r
+ @retval EFI_BUFFER_TOO_SMALL The ImageInfo buffer was too small. The current buffer size\r
+ needed to hold the image(s) information is returned in ImageInfoSize.\r
+ @retval EFI_INVALID_PARAMETER ImageInfoSize is NULL.\r
+ @retval EFI_DEVICE_ERROR Valid information could not be returned. Possible corrupted image.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetTheImageInfo (\r
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,\r
+ IN OUT UINTN *ImageInfoSize,\r
+ IN OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo,\r
+ OUT UINT32 *DescriptorVersion,\r
+ OUT UINT8 *DescriptorCount,\r
+ OUT UINTN *DescriptorSize,\r
+ OUT UINT32 *PackageVersion,\r
+ OUT CHAR16 **PackageVersionName\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Check for valid pointer\r
+ //\r
+ if (ImageInfoSize == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: GetImageInfo() - ImageInfoSize is NULL.\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Check the buffer size\r
+ // NOTE: Check this first so caller can get the necessary memory size it must allocate.\r
+ //\r
+ if (*ImageInfoSize < (sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR))) {\r
+ *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR);\r
+ DEBUG ((DEBUG_VERBOSE, "FmpDxe: GetImageInfo() - ImageInfoSize is to small.\n"));\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Confirm that buffer isn't null\r
+ //\r
+ if ( (ImageInfo == NULL) || (DescriptorVersion == NULL) || (DescriptorCount == NULL) || (DescriptorSize == NULL)\r
+ || (PackageVersion == NULL)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: GetImageInfo() - Pointer Parameter is NULL.\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Set the size to whatever we need\r
+ //\r
+ *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR);\r
+\r
+\r
+ if (!mDescriptorPopulated) {\r
+ PopulateDescriptor();\r
+ }\r
+\r
+ //\r
+ // Copy the image descriptor\r
+ //\r
+ CopyMem (ImageInfo, &mDesc, sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR));\r
+\r
+ *DescriptorVersion = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;\r
+ *DescriptorCount = 1;\r
+ *DescriptorSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR);\r
+ //\r
+ // means unsupported\r
+ //\r
+ *PackageVersion = 0xFFFFFFFF;\r
+\r
+ //\r
+ // Do not update PackageVersionName since it is not supported in this instance.\r
+ //\r
+\r
+cleanup:\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Retrieves a copy of the current firmware image of the device.\r
+\r
+ This function allows a copy of the current firmware image to be created and saved.\r
+ The saved copy could later been used, for example, in firmware image recovery or rollback.\r
+\r
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.\r
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.\r
+ The number is between 1 and DescriptorCount.\r
+ @param[out] Image Points to the buffer where the current image is copied to.\r
+ @param[out] ImageSize On entry, points to the size of the buffer pointed to by Image, in bytes.\r
+ On return, points to the length of the image, in bytes.\r
+\r
+ @retval EFI_SUCCESS The device was successfully updated with the new image.\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer specified by ImageSize is too small to hold the\r
+ image. The current buffer size needed to hold the image is returned\r
+ in ImageSize.\r
+ @retval EFI_INVALID_PARAMETER The Image was NULL.\r
+ @retval EFI_NOT_FOUND The current image is not copied to the buffer.\r
+ @retval EFI_UNSUPPORTED The operation is not supported.\r
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetTheImage (\r
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,\r
+ IN UINT8 ImageIndex,\r
+ IN OUT VOID *Image,\r
+ IN OUT UINTN *ImageSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Size;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ if ((ImageSize == NULL)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: GetImage() - ImageSize Pointer Parameter is NULL.\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Check the buffer size\r
+ //\r
+ Status = FmpDeviceGetSize (&Size);\r
+ if (EFI_ERROR (Status)) {\r
+ Size = 0;\r
+ }\r
+ if (*ImageSize < Size) {\r
+ *ImageSize = Size;\r
+ DEBUG ((DEBUG_VERBOSE, "FmpDxe: GetImage() - ImageSize is to small.\n"));\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto cleanup;\r
+ }\r
+\r
+ if (Image == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: GetImage() - Image Pointer Parameter is NULL.\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Check to make sure index is 1 (only 1 image for this device)\r
+ //\r
+ if (ImageIndex != 1) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: GetImage() - Image Index Invalid.\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto cleanup;\r
+ }\r
+\r
+\r
+ Status = FmpDeviceGetImage (Image, ImageSize);\r
+cleanup:\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Helper function to safely retrieve the FMP header from\r
+ within an EFI_FIRMWARE_IMAGE_AUTHENTICATION structure.\r
+\r
+ @param[in] Image Pointer to the image.\r
+ @param[in] ImageSize Size of the image.\r
+ @param[out] PayloadSize\r
+\r
+ @retval !NULL Valid pointer to the header.\r
+ @retval NULL Structure is bad and pointer cannot be found.\r
+\r
+**/\r
+VOID *\r
+GetFmpHeader (\r
+ IN CONST EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,\r
+ IN CONST UINTN ImageSize,\r
+ OUT UINTN *PayloadSize\r
+ )\r
+{\r
+ //\r
+ // Check to make sure that operation can be safely performed.\r
+ //\r
+ if (((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) < (UINTN)Image || \\r
+ ((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) >= (UINTN)Image + ImageSize) {\r
+ //\r
+ // Pointer overflow. Invalid image.\r
+ //\r
+ return NULL;\r
+ }\r
+\r
+ *PayloadSize = ImageSize - (sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength);\r
+ return (VOID *)((UINT8 *)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength);\r
+}\r
+\r
+/**\r
+ Helper function to safely calculate the size of all headers\r
+ within an EFI_FIRMWARE_IMAGE_AUTHENTICATION structure.\r
+\r
+ @param[in] Image Pointer to the image.\r
+ @param[in] AdditionalHeaderSize Size of any headers that cannot be calculated by this function.\r
+\r
+ @retval UINT32>0 Valid size of all the headers.\r
+ @retval 0 Structure is bad and size cannot be found.\r
+\r
+**/\r
+UINT32\r
+GetAllHeaderSize (\r
+ IN CONST EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,\r
+ IN UINT32 AdditionalHeaderSize\r
+ )\r
+{\r
+ UINT32 CalculatedSize;\r
+\r
+ CalculatedSize = sizeof (Image->MonotonicCount) +\r
+ AdditionalHeaderSize +\r
+ Image->AuthInfo.Hdr.dwLength;\r
+\r
+ //\r
+ // Check to make sure that operation can be safely performed.\r
+ //\r
+ if (CalculatedSize < sizeof (Image->MonotonicCount) ||\r
+ CalculatedSize < AdditionalHeaderSize ||\r
+ CalculatedSize < Image->AuthInfo.Hdr.dwLength ) {\r
+ //\r
+ // Integer overflow. Invalid image.\r
+ //\r
+ return 0;\r
+ }\r
+\r
+ return CalculatedSize;\r
+}\r
+\r
+/**\r
+ Checks if the firmware image is valid for the device.\r
+\r
+ This function allows firmware update application to validate the firmware image without\r
+ invoking the SetImage() first.\r
+\r
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.\r
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.\r
+ The number is between 1 and DescriptorCount.\r
+ @param[in] Image Points to the new image.\r
+ @param[in] ImageSize Size of the new image in bytes.\r
+ @param[out] ImageUpdatable Indicates if the new image is valid for update. It also provides,\r
+ if available, additional information if the image is invalid.\r
+\r
+ @retval EFI_SUCCESS The image was successfully checked.\r
+ @retval EFI_INVALID_PARAMETER The Image was NULL.\r
+ @retval EFI_UNSUPPORTED The operation is not supported.\r
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CheckTheImage (\r
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,\r
+ IN UINT8 ImageIndex,\r
+ IN CONST VOID *Image,\r
+ IN UINTN ImageSize,\r
+ OUT UINT32 *ImageUpdateable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN RawSize;\r
+ VOID *FmpPayloadHeader;\r
+ UINTN FmpPayloadSize;\r
+ UINT32 Version;\r
+ UINT32 FmpHeaderSize;\r
+ UINTN AllHeaderSize;\r
+ UINT32 Index;\r
+ VOID *PublicKeyData;\r
+ UINTN PublicKeyDataLength;\r
+ UINT8 *PublicKeyDataXdr;\r
+ UINT8 *PublicKeyDataXdrEnd;\r
+\r
+ Status = EFI_SUCCESS;\r
+ RawSize = 0;\r
+ FmpPayloadHeader = NULL;\r
+ FmpPayloadSize = 0;\r
+ Version = 0;\r
+ FmpHeaderSize = 0;\r
+ AllHeaderSize = 0;\r
+\r
+ //\r
+ // make sure the descriptor has already been loaded\r
+ //\r
+ if (!mDescriptorPopulated) {\r
+ PopulateDescriptor();\r
+ }\r
+\r
+ if (ImageUpdateable == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckImage() - ImageUpdateable Pointer Parameter is NULL.\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ //Set to valid and then if any tests fail it will update this flag.\r
+ //\r
+ *ImageUpdateable = IMAGE_UPDATABLE_VALID;\r
+\r
+ if (Image == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckImage() - Image Pointer Parameter is NULL.\n"));\r
+ //\r
+ // not sure if this is needed\r
+ //\r
+ *ImageUpdateable = IMAGE_UPDATABLE_INVALID;\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ PublicKeyDataXdr = PcdGetPtr (PcdFmpDevicePkcs7CertBufferXdr);\r
+ PublicKeyDataXdrEnd = PublicKeyDataXdr + PcdGetSize (PcdFmpDevicePkcs7CertBufferXdr);\r
+\r
+ if (PublicKeyDataXdr == NULL || (PublicKeyDataXdr == PublicKeyDataXdrEnd)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Invalid certificate, skipping it.\n"));\r
+ Status = EFI_ABORTED;\r
+ } else {\r
+ //\r
+ // Try each key from PcdFmpDevicePkcs7CertBufferXdr\r
+ //\r
+ for (Index = 1; PublicKeyDataXdr < PublicKeyDataXdrEnd; Index++) {\r
+ Index++;\r
+ DEBUG (\r
+ (DEBUG_INFO,\r
+ "FmpDxe: Certificate #%d [%p..%p].\n",\r
+ Index,\r
+ PublicKeyDataXdr,\r
+ PublicKeyDataXdrEnd\r
+ )\r
+ );\r
+\r
+ if ((PublicKeyDataXdr + sizeof (UINT32)) > PublicKeyDataXdrEnd) {\r
+ //\r
+ // Key data extends beyond end of PCD\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Certificate size extends beyond end of PCD, skipping it.\n"));\r
+ Status = EFI_ABORTED;\r
+ break;\r
+ }\r
+ //\r
+ // Read key length stored in big-endian format\r
+ //\r
+ PublicKeyDataLength = SwapBytes32 (*(UINT32 *)(PublicKeyDataXdr));\r
+ //\r
+ // Point to the start of the key data\r
+ //\r
+ PublicKeyDataXdr += sizeof (UINT32);\r
+ if (PublicKeyDataXdr + PublicKeyDataLength > PublicKeyDataXdrEnd) {\r
+ //\r
+ // Key data extends beyond end of PCD\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Certificate extends beyond end of PCD, skipping it.\n"));\r
+ Status = EFI_ABORTED;\r
+ break;\r
+ }\r
+ PublicKeyData = PublicKeyDataXdr;\r
+ Status = AuthenticateFmpImage (\r
+ (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image,\r
+ ImageSize,\r
+ PublicKeyData,\r
+ PublicKeyDataLength\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ PublicKeyDataXdr += PublicKeyDataLength;\r
+ PublicKeyDataXdr = (UINT8 *)ALIGN_POINTER (PublicKeyDataXdr, sizeof (UINT32));\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - Authentication Failed %r.\n", Status));\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Check to make sure index is 1\r
+ //\r
+ if (ImageIndex != 1) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckImage() - Image Index Invalid.\n"));\r
+ *ImageUpdateable = IMAGE_UPDATABLE_INVALID_TYPE;\r
+ Status = EFI_SUCCESS;\r
+ goto cleanup;\r
+ }\r
+\r
+\r
+ //\r
+ // Check the FmpPayloadHeader\r
+ //\r
+ FmpPayloadHeader = GetFmpHeader ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &FmpPayloadSize );\r
+ if (FmpPayloadHeader == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetFmpHeader failed.\n"));\r
+ Status = EFI_ABORTED;\r
+ goto cleanup;\r
+ }\r
+ Status = GetFmpPayloadHeaderVersion (FmpPayloadHeader, FmpPayloadSize, &Version);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetFmpPayloadHeaderVersion failed %r.\n", Status));\r
+ *ImageUpdateable = IMAGE_UPDATABLE_INVALID;\r
+ Status = EFI_SUCCESS;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Check the lowest supported version\r
+ //\r
+ if (Version < mDesc.LowestSupportedImageVersion) {\r
+ DEBUG (\r
+ (DEBUG_ERROR,\r
+ "FmpDxe: CheckTheImage() - Version Lower than lowest supported version. 0x%08X < 0x%08X\n",\r
+ Version, mDesc.LowestSupportedImageVersion)\r
+ );\r
+ *ImageUpdateable = IMAGE_UPDATABLE_INVALID_OLD;\r
+ Status = EFI_SUCCESS;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Get the FmpHeaderSize so we can determine the real payload size\r
+ //\r
+ Status = GetFmpPayloadHeaderSize (FmpPayloadHeader, FmpPayloadSize, &FmpHeaderSize);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetFmpPayloadHeaderSize failed %r.\n", Status));\r
+ *ImageUpdateable = IMAGE_UPDATABLE_INVALID;\r
+ Status = EFI_SUCCESS;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Call FmpDevice Lib Check Image on the\r
+ // Raw payload. So all headers need stripped off\r
+ //\r
+ AllHeaderSize = GetAllHeaderSize ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize );\r
+ if (AllHeaderSize == 0) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetAllHeaderSize failed.\n"));\r
+ Status = EFI_ABORTED;\r
+ goto cleanup;\r
+ }\r
+ RawSize = ImageSize - AllHeaderSize;\r
+\r
+ //\r
+ // FmpDeviceLib CheckImage function to do any specific checks\r
+ //\r
+ Status = FmpDeviceCheckImage ((((UINT8 *)Image) + AllHeaderSize), RawSize, ImageUpdateable);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - FmpDeviceLib CheckImage failed. Status = %r\n", Status));\r
+ }\r
+\r
+cleanup:\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Updates the firmware image of the device.\r
+\r
+ This function updates the hardware with the new firmware image.\r
+ This function returns EFI_UNSUPPORTED if the firmware image is not updatable.\r
+ If the firmware image is updatable, the function should perform the following minimal validations\r
+ before proceeding to do the firmware image update.\r
+ - Validate the image authentication if image has attribute\r
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. The function returns\r
+ EFI_SECURITY_VIOLATION if the validation fails.\r
+ - Validate the image is a supported image for this device. The function returns EFI_ABORTED if\r
+ the image is unsupported. The function can optionally provide more detailed information on\r
+ why the image is not a supported image.\r
+ - Validate the data from VendorCode if not null. Image validation must be performed before\r
+ VendorCode data validation. VendorCode data is ignored or considered invalid if image\r
+ validation failed. The function returns EFI_ABORTED if the data is invalid.\r
+\r
+ VendorCode enables vendor to implement vendor-specific firmware image update policy. Null if\r
+ the caller did not specify the policy or use the default policy. As an example, vendor can implement\r
+ a policy to allow an option to force a firmware image update when the abort reason is due to the new\r
+ firmware image version is older than the current firmware image version or bad image checksum.\r
+ Sensitive operations such as those wiping the entire firmware image and render the device to be\r
+ non-functional should be encoded in the image itself rather than passed with the VendorCode.\r
+ AbortReason enables vendor to have the option to provide a more detailed description of the abort\r
+ reason to the caller.\r
+\r
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.\r
+ @param[in] ImageIndex A unique number identifying the firmware image(s) within the device.\r
+ The number is between 1 and DescriptorCount.\r
+ @param[in] Image Points to the new image.\r
+ @param[in] ImageSize Size of the new image in bytes.\r
+ @param[in] VendorCode This enables vendor to implement vendor-specific firmware image update policy.\r
+ Null indicates the caller did not specify the policy or use the default policy.\r
+ @param[in] Progress A function used by the driver to report the progress of the firmware update.\r
+ @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more\r
+ details for the aborted operation. The buffer is allocated by this function\r
+ with AllocatePool(), and it is the caller's responsibility to free it with a\r
+ call to FreePool().\r
+\r
+ @retval EFI_SUCCESS The device was successfully updated with the new image.\r
+ @retval EFI_ABORTED The operation is aborted.\r
+ @retval EFI_INVALID_PARAMETER The Image was NULL.\r
+ @retval EFI_UNSUPPORTED The operation is not supported.\r
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SetTheImage (\r
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,\r
+ IN UINT8 ImageIndex,\r
+ IN CONST VOID *Image,\r
+ IN UINTN ImageSize,\r
+ IN CONST VOID *VendorCode,\r
+ IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress,\r
+ OUT CHAR16 **AbortReason\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 Updateable;\r
+ BOOLEAN BooleanValue;\r
+ UINT32 FmpHeaderSize;\r
+ VOID *FmpHeader;\r
+ UINTN FmpPayloadSize;\r
+ UINT32 AllHeaderSize;\r
+ UINT32 IncommingFwVersion;\r
+ UINT32 LastAttemptStatus;\r
+\r
+ Status = EFI_SUCCESS;\r
+ Updateable = 0;\r
+ BooleanValue = FALSE;\r
+ FmpHeaderSize = 0;\r
+ FmpHeader = NULL;\r
+ FmpPayloadSize = 0;\r
+ AllHeaderSize = 0;\r
+ IncommingFwVersion = 0;\r
+ LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;\r
+\r
+\r
+ SetLastAttemptVersionInVariable (IncommingFwVersion); //set to 0 to clear any previous results.\r
+\r
+ //\r
+ // if we have locked the device, then skip the set operation.\r
+ // it should be blocked by hardware too but we can catch here even faster\r
+ //\r
+ if (mFmpDeviceLocked) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Device is already locked. Can't update.\n"));\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Call check image to verify the image\r
+ //\r
+ Status = CheckTheImage (This, ImageIndex, Image, ImageSize, &Updateable);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Check The Image failed with %r.\n", Status));\r
+ if (Status == EFI_SECURITY_VIOLATION) {\r
+ LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;\r
+ }\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // No functional error in CheckTheImage. Attempt to get the Version to\r
+ // support better error reporting.\r
+ //\r
+ FmpHeader = GetFmpHeader ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &FmpPayloadSize );\r
+ if (FmpHeader == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - GetFmpHeader failed.\n"));\r
+ Status = EFI_ABORTED;\r
+ goto cleanup;\r
+ }\r
+ Status = GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &IncommingFwVersion);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Set to actual value\r
+ //\r
+ SetLastAttemptVersionInVariable (IncommingFwVersion);\r
+ }\r
+\r
+\r
+ if (Updateable != IMAGE_UPDATABLE_VALID) {\r
+ DEBUG (\r
+ (DEBUG_ERROR,\r
+ "FmpDxed: SetTheImage() - Check The Image returned that the Image was not valid for update. Updatable value = 0x%X.\n",\r
+ Updateable)\r
+ );\r
+ Status = EFI_ABORTED;\r
+ goto cleanup;\r
+ }\r
+\r
+ if (Progress == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Invalid progress callback\n"));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto cleanup;\r
+ }\r
+\r
+ mProgressFunc = Progress;\r
+ mProgressSupported = TRUE;\r
+\r
+ //\r
+ // Checking the image is at least 1%\r
+ //\r
+ Status = Progress (1);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Progress Callback failed with Status %r.\n", Status));\r
+ }\r
+\r
+ //\r
+ //Check System Power\r
+ //\r
+ Status = CheckSystemPower (&BooleanValue);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - CheckSystemPower - API call failed %r.\n", Status));\r
+ goto cleanup;\r
+ }\r
+ if (!BooleanValue) {\r
+ Status = EFI_ABORTED;\r
+ DEBUG (\r
+ (DEBUG_ERROR,\r
+ "FmpDxe: SetTheImage() - CheckSystemPower - returned False. Update not allowed due to System Power.\n")\r
+ );\r
+ LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT;\r
+ goto cleanup;\r
+ }\r
+\r
+ Progress (2);\r
+\r
+ //\r
+ //Check System Thermal\r
+ //\r
+ Status = CheckSystemThermal (&BooleanValue);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - CheckSystemThermal - API call failed %r.\n", Status));\r
+ goto cleanup;\r
+ }\r
+ if (!BooleanValue) {\r
+ Status = EFI_ABORTED;\r
+ DEBUG (\r
+ (DEBUG_ERROR,\r
+ "FmpDxe: SetTheImage() - CheckSystemThermal - returned False. Update not allowed due to System Thermal.\n")\r
+ );\r
+ goto cleanup;\r
+ }\r
+\r
+ Progress (3);\r
+\r
+ //\r
+ //Check System Environment\r
+ //\r
+ Status = CheckSystemEnvironment (&BooleanValue);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - CheckSystemEnvironment - API call failed %r.\n", Status));\r
+ goto cleanup;\r
+ }\r
+ if (!BooleanValue) {\r
+ Status = EFI_ABORTED;\r
+ DEBUG (\r
+ (DEBUG_ERROR,\r
+ "FmpDxe: SetTheImage() - CheckSystemEnvironment - returned False. Update not allowed due to System Environment.\n")\r
+ );\r
+ goto cleanup;\r
+ }\r
+\r
+ Progress (4);\r
+\r
+ //\r
+ // Save LastAttemptStatus as error so that if SetImage never returns the error\r
+ // state is recorded.\r
+ //\r
+ SetLastAttemptStatusInVariable (LastAttemptStatus);\r
+\r
+ //\r
+ // Strip off all the headers so the device can process its firmware\r
+ //\r
+ Status = GetFmpPayloadHeaderSize (FmpHeader, FmpPayloadSize, &FmpHeaderSize);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - GetFmpPayloadHeaderSize failed %r.\n", Status));\r
+ goto cleanup;\r
+ }\r
+\r
+ AllHeaderSize = GetAllHeaderSize ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize );\r
+ if (AllHeaderSize == 0) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - GetAllHeaderSize failed.\n"));\r
+ Status = EFI_ABORTED;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Indicate that control is handed off to FmpDeviceLib\r
+ //\r
+ Progress (5);\r
+\r
+ //\r
+ //Copy the requested image to the firmware using the FmpDeviceLib\r
+ //\r
+ Status = FmpDeviceSetImage (\r
+ (((UINT8 *)Image) + AllHeaderSize),\r
+ ImageSize - AllHeaderSize,\r
+ VendorCode,\r
+ FmpDxeProgress,\r
+ IncommingFwVersion,\r
+ AbortReason\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() SetImage from FmpDeviceLib failed. Status = %r.\n", Status));\r
+ goto cleanup;\r
+ }\r
+\r
+\r
+ //\r
+ // Finished the update without error\r
+ // Indicate that control has been returned from FmpDeviceLib\r
+ //\r
+ Progress (99);\r
+\r
+ //\r
+ // Update the version stored in variable\r
+ //\r
+ if (!mRuntimeVersionSupported) {\r
+ UINT32 Version = DEFAULT_VERSION;\r
+ GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &Version);\r
+ SetVersionInVariable (Version);\r
+ }\r
+\r
+ //\r
+ // Update lowest supported variable\r
+ //\r
+ {\r
+ UINT32 Version = DEFAULT_LOWESTSUPPORTEDVERSION;\r
+ GetFmpPayloadHeaderLowestSupportedVersion (FmpHeader, FmpPayloadSize, &Version);\r
+ SetLowestSupportedVersionInVariable (Version);\r
+ }\r
+\r
+ LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;\r
+\r
+ //\r
+ // Set flag so the descriptor is repopulated\r
+ // This only applied to devices that do not require system reboot\r
+ //\r
+ if (!PcdGetBool (PcdFmpDeviceSystemResetRequired)) {\r
+ mDescriptorPopulated = FALSE;\r
+ }\r
+\r
+cleanup:\r
+ mProgressFunc = NULL;\r
+ mProgressSupported = FALSE;\r
+ SetLastAttemptStatusInVariable (LastAttemptStatus);\r
+\r
+ //\r
+ // Set progress to 100 after everything is done including recording Status.\r
+ //\r
+ Progress (100);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Returns information about the firmware package.\r
+\r
+ This function returns package information.\r
+\r
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.\r
+ @param[out] PackageVersion A version number that represents all the firmware images in the device.\r
+ The format is vendor specific and new version must have a greater value\r
+ than the old version. If PackageVersion is not supported, the value is\r
+ 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version\r
+ comparison is to be performed using PackageVersionName. A value of\r
+ 0xFFFFFFFD indicates that package version update is in progress.\r
+ @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing\r
+ the package version name. The buffer is allocated by this function with\r
+ AllocatePool(), and it is the caller's responsibility to free it with a\r
+ call to FreePool().\r
+ @param[out] PackageVersionNameMaxLen The maximum length of package version name if device supports update of\r
+ package version name. A value of 0 indicates the device does not support\r
+ update of package version name. Length is the number of Unicode characters,\r
+ including the terminating null character.\r
+ @param[out] AttributesSupported Package attributes that are supported by this device. See 'Package Attribute\r
+ Definitions' for possible returned values of this parameter. A value of 1\r
+ indicates the attribute is supported and the current setting value is\r
+ indicated in AttributesSetting. A value of 0 indicates the attribute is not\r
+ supported and the current setting value in AttributesSetting is meaningless.\r
+ @param[out] AttributesSetting Package attributes. See 'Package Attribute Definitions' for possible returned\r
+ values of this parameter\r
+\r
+ @retval EFI_SUCCESS The package information was successfully returned.\r
+ @retval EFI_UNSUPPORTED The operation is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetPackageInfo (\r
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,\r
+ OUT UINT32 *PackageVersion,\r
+ OUT CHAR16 **PackageVersionName,\r
+ OUT UINT32 *PackageVersionNameMaxLen,\r
+ OUT UINT64 *AttributesSupported,\r
+ OUT UINT64 *AttributesSetting\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Updates information about the firmware package.\r
+\r
+ This function updates package information.\r
+ This function returns EFI_UNSUPPORTED if the package information is not updatable.\r
+ VendorCode enables vendor to implement vendor-specific package information update policy.\r
+ Null if the caller did not specify this policy or use the default policy.\r
+\r
+ @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance.\r
+ @param[in] Image Points to the authentication image.\r
+ Null if authentication is not required.\r
+ @param[in] ImageSize Size of the authentication image in bytes.\r
+ 0 if authentication is not required.\r
+ @param[in] VendorCode This enables vendor to implement vendor-specific firmware\r
+ image update policy.\r
+ Null indicates the caller did not specify this policy or use\r
+ the default policy.\r
+ @param[in] PackageVersion The new package version.\r
+ @param[in] PackageVersionName A pointer to the new null-terminated Unicode string representing\r
+ the package version name.\r
+ The string length is equal to or less than the value returned in\r
+ PackageVersionNameMaxLen.\r
+\r
+ @retval EFI_SUCCESS The device was successfully updated with the new package\r
+ information.\r
+ @retval EFI_INVALID_PARAMETER The PackageVersionName length is longer than the value\r
+ returned in PackageVersionNameMaxLen.\r
+ @retval EFI_UNSUPPORTED The operation is not supported.\r
+ @retval EFI_SECURITY_VIOLATIO The operation could not be performed due to an authentication failure.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SetPackageInfo (\r
+ IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This,\r
+ IN CONST VOID *Image,\r
+ IN UINTN ImageSize,\r
+ IN CONST VOID *VendorCode,\r
+ IN UINT32 PackageVersion,\r
+ IN CONST CHAR16 *PackageVersionName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Event notification function that is invoked when the event GUID specified by\r
+ PcdFmpDeviceLockEventGuid is signaled.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context The pointer to the notification function's context,\r
+ which is implementation-dependent.\r
+**/\r
+VOID\r
+EFIAPI\r
+FmpDxeLockEventNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (!mFmpDeviceLocked) {\r
+ if (IsLockFmpDeviceAtLockEventGuidRequired ()) {\r
+ //\r
+ // Lock all UEFI Variables used by this module.\r
+ //\r
+ Status = LockAllFmpVariables ();\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to lock variables. Status = %r.\n"));\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, "FmpDxe: All variables locked\n"));\r
+ }\r
+\r
+ //\r
+ // Lock the firmware device\r
+ //\r
+ Status = FmpDeviceLock();\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status != EFI_UNSUPPORTED) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLock() returned error. Status = %r\n", Status));\r
+ } else {\r
+ DEBUG ((DEBUG_WARN, "FmpDxe: FmpDeviceLock() returned error. Status = %r\n", Status));\r
+ }\r
+ }\r
+ mFmpDeviceLocked = TRUE;\r
+ } else {\r
+ DEBUG ((DEBUG_VERBOSE, "FmpDxe: Not calling FmpDeviceLock() because mfg mode\n"));\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Function to install FMP instance.\r
+\r
+ @param[in] Handle The device handle to install a FMP instance on.\r
+\r
+ @retval EFI_SUCCESS FMP Installed\r
+ @retval EFI_INVALID_PARAMETER Handle was invalid\r
+ @retval other Error installing FMP\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InstallFmpInstance (\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;\r
+ EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL *FmpProgress;\r
+\r
+ Status = EFI_SUCCESS;\r
+ Fmp = NULL;\r
+ FmpProgress = NULL;\r
+\r
+ //\r
+ // Only allow a single FMP Protocol instance to be installed\r
+ //\r
+ if (mFmpInstalled) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ //\r
+ // Allocate FMP Protocol instance\r
+ //\r
+ Fmp = AllocateZeroPool (sizeof (EFI_FIRMWARE_MANAGEMENT_PROTOCOL));\r
+ if (Fmp == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to allocate memory for FMP Protocol instance.\n"));\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Allocate FMP Progress Protocol instance\r
+ //\r
+ FmpProgress = AllocateZeroPool (sizeof (EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL));\r
+ if (FmpProgress == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to allocate memory for FMP Progress Protocol instance.\n"));\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ FreePool (Fmp);\r
+ goto cleanup;\r
+ }\r
+\r
+ //\r
+ // Set up FMP Protocol function pointers\r
+ //\r
+ Fmp->GetImageInfo = GetTheImageInfo;\r
+ Fmp->GetImage = GetTheImage;\r
+ Fmp->SetImage = SetTheImage;\r
+ Fmp->CheckImage = CheckTheImage;\r
+ Fmp->GetPackageInfo = GetPackageInfo;\r
+ Fmp->SetPackageInfo = SetPackageInfo;\r
+\r
+ //\r
+ // Fill in FMP Progress Protocol fields for Version 1\r
+ //\r
+ FmpProgress->Version = 1;\r
+ FmpProgress->ProgressBarForegroundColor.Raw = PcdGet32 (PcdFmpDeviceProgressColor);\r
+ FmpProgress->WatchdogSeconds = PcdGet8 (PcdFmpDeviceProgressWatchdogTimeInSeconds);\r
+\r
+ //\r
+ // Install FMP Protocol and FMP Progress Protocol\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Handle,\r
+ &gEfiFirmwareManagementProtocolGuid,\r
+ Fmp,\r
+ &gEdkiiFirmwareManagementProgressProtocolGuid,\r
+ FmpProgress,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: FMP Protocol install error. Status = %r.\n", Status));\r
+ FreePool (Fmp);\r
+ goto cleanup;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "FmpDxe: FMP Protocol Installed!\n"));\r
+ mFmpInstalled = TRUE;\r
+\r
+cleanup:\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Main entry for this library.\r
+\r
+ @param[in] ImageHandle Image handle this driver.\r
+ @param[in] SystemTable Pointer to SystemTable.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FmpDxeEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_GUID *LockGuid;\r
+\r
+ //\r
+ // Verify that a new FILE_GUID value has been provided in the <Defines>\r
+ // section of this module. The FILE_GUID is the ESRT GUID that must be\r
+ // unique for each updatable firmware image.\r
+ //\r
+ if (CompareGuid (&mDefaultModuleFileGuid, &gEfiCallerIdGuid)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Use of default FILE_GUID detected. FILE_GUID must be set to a unique value.\n"));\r
+ ASSERT (FALSE);\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Get the ImageIdName value for the EFI_FIRMWARE_IMAGE_DESCRIPTOR from a PCD.\r
+ //\r
+ mImageIdName = (CHAR16 *) PcdGetPtr (PcdFmpDeviceImageIdName);\r
+ if (PcdGetSize (PcdFmpDeviceImageIdName) <= 2 || mImageIdName[0] == 0) {\r
+ //\r
+ // PcdFmpDeviceImageIdName must be set to a non-empty Unicode string\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib PcdFmpDeviceImageIdName is an empty string.\n"));\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ //\r
+ // Detects if PcdFmpDevicePkcs7CertBufferXdr contains a test key.\r
+ //\r
+ DetectTestKey ();\r
+\r
+ //\r
+ // Register with library the install function so if the library uses\r
+ // UEFI driver model/driver binding protocol it can install FMP on its device handle\r
+ // If library is simple lib that does not use driver binding then it should return\r
+ // unsupported and this will install the FMP instance on the ImageHandle\r
+ //\r
+ Status = RegisterFmpInstaller (InstallFmpInstance);\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ DEBUG ((DEBUG_INFO, "FmpDxe: FmpDeviceLib registration returned EFI_UNSUPPORTED. Installing single FMP instance.\n"));\r
+ Status = InstallFmpInstance (ImageHandle);\r
+ } else if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib registration returned %r. No FMP installed.\n", Status));\r
+ } else {\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "FmpDxe: FmpDeviceLib registration returned EFI_SUCCESS. Expect FMP to be installed during the BDS/Device connection phase.\n"\r
+ ));\r
+ }\r
+\r
+ //\r
+ // Register notify function to lock the FMP device.\r
+ // The lock event GUID is retrieved from PcdFmpDeviceLockEventGuid.\r
+ // If PcdFmpDeviceLockEventGuid is not the size of an EFI_GUID, then\r
+ // gEfiEndOfDxeEventGroupGuid is used.\r
+ //\r
+ LockGuid = &gEfiEndOfDxeEventGroupGuid;\r
+ if (PcdGetSize (PcdFmpDeviceLockEventGuid) == sizeof (EFI_GUID)) {\r
+ LockGuid = (EFI_GUID *)PcdGetPtr (PcdFmpDeviceLockEventGuid);\r
+ }\r
+ DEBUG ((DEBUG_INFO, "FmpDxe: Lock GUID: %g\n", LockGuid));\r
+\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ FmpDxeLockEventNotify,\r
+ NULL,\r
+ LockGuid,\r
+ &mFmpDeviceLockEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to register for ready to boot. Status = %r\n", Status));\r
+ }\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r