diff --git a/FmpDevicePkg/FmpDxe/DetectTestKey.c b/FmpDevicePkg/FmpDxe/DetectTestKey.c new file mode 100644 index 0000000000..0a6e37eded --- /dev/null +++ b/FmpDevicePkg/FmpDxe/DetectTestKey.c @@ -0,0 +1,166 @@ +/** @file + Detects if PcdFmpDevicePkcs7CertBufferXdr contains a test key. + + Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +**/ + +#include +#include +#include +#include +#include +#include +#include + +/** + Check to see if any of the keys in PcdFmpDevicePkcs7CertBufferXdr matches + the test key. PcdFmpDeviceTestKeySha256Digest contains the SHA256 hash of + the test key. For each key in PcdFmpDevicePkcs7CertBufferXdr, compute the + SHA256 hash and compare it to PcdFmpDeviceTestKeySha256Digest. If the + SHA256 hash matches or there is then error computing the SHA256 hash, then + set PcdTestKeyUsed to TRUE. Skip this check if PcdTestKeyUsed is already + TRUE or PcdFmpDeviceTestKeySha256Digest is not exactly SHA256_DIGEST_SIZE + bytes. +**/ +VOID +DetectTestKey ( + VOID + ) +{ + BOOLEAN TestKeyUsed; + UINTN PublicKeyDataLength; + UINT8 *PublicKeyDataXdr; + UINT8 *PublicKeyDataXdrEnd; + VOID *HashContext; + UINT8 Digest[SHA256_DIGEST_SIZE]; + + // + // If PcdFmpDeviceTestKeySha256Digest is not exacty SHA256_DIGEST_SIZE bytes, + // then skip the test key detection. + // + if (PcdGetSize (PcdFmpDeviceTestKeySha256Digest) != SHA256_DIGEST_SIZE) { + return; + } + + // + // If PcdTestKeyUsed is already TRUE, then skip test key detection + // + TestKeyUsed = PcdGetBool (PcdTestKeyUsed); + if (TestKeyUsed) { + return; + } + + // + // If PcdFmpDevicePkcs7CertBufferXdr is invalid, then skip test key detection + // + PublicKeyDataXdr = PcdGetPtr (PcdFmpDevicePkcs7CertBufferXdr); + PublicKeyDataXdrEnd = PublicKeyDataXdr + PcdGetSize (PcdFmpDevicePkcs7CertBufferXdr); + if (PublicKeyDataXdr == NULL || PublicKeyDataXdr == PublicKeyDataXdrEnd) { + return; + } + + // + // Allocate hash context buffer required for SHA 256 + // + HashContext = AllocatePool (Sha256GetContextSize ()); + if (HashContext == NULL) { + TestKeyUsed = TRUE; + } + + // + // Loop through all keys in PcdFmpDevicePkcs7CertBufferXdr + // + while (!TestKeyUsed && PublicKeyDataXdr < PublicKeyDataXdrEnd) { + if (PublicKeyDataXdr + sizeof (UINT32) > PublicKeyDataXdrEnd) { + // + // Key data extends beyond end of PCD + // + 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 + // + break; + } + + // + // Hash public key from PcdFmpDevicePkcs7CertBufferXdr using SHA256. + // If error occurs computing SHA256, then assume test key is in use. + // + ZeroMem (Digest, SHA256_DIGEST_SIZE); + if (!Sha256Init (HashContext)) { + TestKeyUsed = TRUE; + break; + } + if (!Sha256Update (HashContext, PublicKeyDataXdr, PublicKeyDataLength)) { + TestKeyUsed = TRUE; + break; + } + if (!Sha256Final (HashContext, Digest)) { + TestKeyUsed = TRUE; + break; + } + + // + // Check if SHA256 hash of public key matches SHA256 hash of test key + // + if (CompareMem (Digest, PcdGetPtr (PcdFmpDeviceTestKeySha256Digest), SHA256_DIGEST_SIZE) == 0) { + TestKeyUsed = TRUE; + break; + } + + // + // Point to start of next key + // + PublicKeyDataXdr += PublicKeyDataLength; + PublicKeyDataXdr = (UINT8 *)ALIGN_POINTER (PublicKeyDataXdr, sizeof (UINT32)); + } + + // + // Free hash context buffer required for SHA 256 + // + if (HashContext != NULL) { + FreePool (HashContext); + HashContext = NULL; + } + + // + // If test key detected or an error occured checking for the test key, then + // set PcdTestKeyUsed to TRUE. + // + if (TestKeyUsed) { + DEBUG ((DEBUG_INFO, "FmpDxe: Test key detected in PcdFmpDevicePkcs7CertBufferXdr.\n")); + PcdSetBoolS (PcdTestKeyUsed, TRUE); + } else { + DEBUG ((DEBUG_INFO, "FmpDxe: No test key detected in PcdFmpDevicePkcs7CertBufferXdr.\n")); + } +} diff --git a/FmpDevicePkg/FmpDxe/FmpDxe.c b/FmpDevicePkg/FmpDxe/FmpDxe.c new file mode 100644 index 0000000000..b709bc282a --- /dev/null +++ b/FmpDevicePkg/FmpDxe/FmpDxe.c @@ -0,0 +1,1452 @@ +/** @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) 2016, Microsoft Corporation. All rights reserved.
+ Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "VariableSupport.h" + +#define VERSION_STRING_NOT_SUPPORTED L"VERSION STRING NOT SUPPORTED" +#define VERSION_STRING_NOT_AVAILABLE L"VERSION STRING NOT AVAILABLE" + +/** + Check to see if any of the keys in PcdFmpDevicePkcs7CertBufferXdr matches + the test key. PcdFmpDeviceTestKeySha256Digest contains the SHA256 hash of + the test key. For each key in PcdFmpDevicePkcs7CertBufferXdr, compute the + SHA256 hash and compare it to PcdFmpDeviceTestKeySha256Digest. If the + SHA256 hash matches or there is then error computing the SHA256 hash, then + set PcdTestKeyUsed to TRUE. Skip this check if PcdTestKeyUsed is already + TRUE or PcdFmpDeviceTestKeySha256Digest is not exactly SHA256_DIGEST_SIZE + bytes. +**/ +VOID +DetectTestKey ( + VOID + ); + +/// +/// FILE_GUID from FmpDxe.inf. When FmpDxe.inf is used in a platform, the +/// FILE_GUID must always be overridden in the 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 } +}; + +EFI_FIRMWARE_IMAGE_DESCRIPTOR mDesc; +BOOLEAN mDescriptorPopulated = FALSE; +BOOLEAN mRuntimeVersionSupported = TRUE; +BOOLEAN mFmpInstalled = FALSE; + +/// +/// Function pointer to progress function +/// +EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS mProgressFunc = NULL; +BOOLEAN mProgressSupported = FALSE; + +CHAR16 *mImageIdName = NULL; +UINT64 mImageId = 0x1; +CHAR16 *mVersionName = NULL; + +EFI_EVENT mFmpDeviceLockEvent; +BOOLEAN mFmpDeviceLocked = FALSE; + +/** + 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 (!mProgressSupported) { + return Status; + } + + 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) { + mProgressSupported = FALSE; + 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 gEfiCallerIdGuid is returned. + + @return The ImageTypeId GUID + +**/ +EFI_GUID * +GetImageTypeIdGuid ( + VOID + ) +{ + EFI_STATUS Status; + EFI_GUID *FmpDeviceLibGuid; + + FmpDeviceLibGuid = NULL; + Status = FmpDeviceGetImageTypeIdGuidPtr (&FmpDeviceLibGuid); + if (EFI_ERROR (Status)) { + if (Status != EFI_UNSUPPORTED) { + DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib GetImageTypeIdGuidPtr() returned invalid error %r\n", Status)); + } + return &gEfiCallerIdGuid; + } + if (FmpDeviceLibGuid == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib GetImageTypeIdGuidPtr() returned invalid GUID\n")); + return &gEfiCallerIdGuid; + } + return FmpDeviceLibGuid; +} + +/** + Returns a pointer to the Null-terminated Unicode ImageIdName string. + + @return 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 + + Take the largest value + +**/ +UINT32 +GetLowestSupportedVersion ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 DeviceLibLowestSupportedVersion; + UINT32 VariableLowestSupportedVersion; + UINT32 ReturnLsv; + + // + // Get the LowestSupportedVersion. + // + + DeviceLibLowestSupportedVersion = DEFAULT_LOWESTSUPPORTEDVERSION; + ReturnLsv = PcdGet32 (PcdFmpDeviceBuildTimeLowestSupportedVersion); + if (!IsLowestSupportedVersionCheckRequired ()) { + return 1; + } + + // + // Check the FmpDeviceLib + // + 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(); + if (VariableLowestSupportedVersion > ReturnLsv) { + ReturnLsv = VariableLowestSupportedVersion; + } + + // + // Return the largest value + // + return ReturnLsv; +} + +/** + Populates the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure in the module global + variable mDesc. + +**/ +VOID +PopulateDescriptor ( + VOID + ) +{ + EFI_STATUS Status; + + mDesc.ImageIndex = 1; + CopyGuid (&mDesc.ImageTypeId, GetImageTypeIdGuid()); + mDesc.ImageId = mImageId; + mDesc.ImageIdName = GetImageTypeNameString(); + + // + // 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 (&mDesc.Version); + if (Status == EFI_UNSUPPORTED) { + mRuntimeVersionSupported = FALSE; + mDesc.Version = GetVersionFromVariable(); + } else if (EFI_ERROR (Status)) { + // + // Unexpected error. Use default version. + // + DEBUG ((DEBUG_ERROR, "FmpDxe: GetVersion() from FmpDeviceLib (%s) returned %r\n", GetImageTypeNameString(), Status)); + mDesc.Version = DEFAULT_VERSION; + } + + // + // Free the current version name. Shouldn't really happen but this populate + // function could be called multiple times (to refresh). + // + if (mVersionName != NULL) { + FreePool (mVersionName); + mVersionName = NULL; + } + + // + // Attempt to get the version string from the FmpDeviceLib + // + Status = FmpDeviceGetVersionString (&mVersionName); + if (Status == EFI_UNSUPPORTED) { + DEBUG ((DEBUG_INFO, "FmpDxe: GetVersionString() unsupported in FmpDeviceLib.\n")); + mVersionName = AllocateCopyPool ( + sizeof (VERSION_STRING_NOT_SUPPORTED), + VERSION_STRING_NOT_SUPPORTED + ); + } else if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "FmpDxe: GetVersionString() not available in FmpDeviceLib.\n")); + mVersionName = AllocateCopyPool ( + sizeof (VERSION_STRING_NOT_AVAILABLE), + VERSION_STRING_NOT_AVAILABLE + ); + } + + mDesc.VersionName = mVersionName; + + mDesc.LowestSupportedImageVersion = GetLowestSupportedVersion(); + + // + // Get attributes from the FmpDeviceLib + // + FmpDeviceGetAttributes (&mDesc.AttributesSupported, &mDesc.AttributesSetting); + + // + // Force set the updatable bits in the attributes; + // + mDesc.AttributesSupported |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + mDesc.AttributesSetting |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + + // + // Force set the authentication bits in the attributes; + // + mDesc.AttributesSupported |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); + mDesc.AttributesSetting |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); + + mDesc.Compatibilities = 0; + + // + // Get the size of the firmware image from the FmpDeviceLib + // + Status = FmpDeviceGetSize (&mDesc.Size); + if (EFI_ERROR (Status)) { + mDesc.Size = 0; + } + + mDesc.LastAttemptVersion = GetLastAttemptVersionFromVariable (); + mDesc.LastAttemptStatus = GetLastAttemptStatusFromVariable (); + + mDescriptorPopulated = 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; + + Status = EFI_SUCCESS; + + // + // Check for valid pointer + // + if (ImageInfoSize == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: GetImageInfo() - ImageInfoSize is NULL.\n")); + 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: GetImageInfo() - ImageInfoSize is to small.\n")); + 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: GetImageInfo() - Pointer Parameter is NULL.\n")); + Status = EFI_INVALID_PARAMETER; + goto cleanup; + } + + // + // Set the size to whatever we need + // + *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR); + + + if (!mDescriptorPopulated) { + PopulateDescriptor(); + } + + // + // Copy the image descriptor + // + CopyMem (ImageInfo, &mDesc, 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[out] Image Points to the buffer where the current image is copied to. + @param[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_VIOLATIO 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; + UINTN Size; + + Status = EFI_SUCCESS; + + if ((ImageSize == NULL)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: GetImage() - ImageSize Pointer Parameter is NULL.\n")); + 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: GetImage() - ImageSize is to small.\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto cleanup; + } + + if (Image == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: GetImage() - Image Pointer Parameter is NULL.\n")); + Status = EFI_INVALID_PARAMETER; + goto cleanup; + } + + // + // Check to make sure index is 1 (only 1 image for this device) + // + if (ImageIndex != 1) { + DEBUG ((DEBUG_ERROR, "FmpDxe: GetImage() - Image Index Invalid.\n")); + 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[out] PayloadSize + + @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, + OUT UINTN *PayloadSize + ) +{ + // + // Check to make sure that operation can be safely performed. + // + if (((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) < (UINTN)Image || \ + ((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) >= (UINTN)Image + ImageSize) { + // + // Pointer overflow. Invalid image. + // + return NULL; + } + + *PayloadSize = ImageSize - (sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength); + return (VOID *)((UINT8 *)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength); +} + +/** + 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; + + 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. + + @retval EFI_SUCCESS The image was successfully checked. + @retval EFI_INVALID_PARAMETER The Image was NULL. + @retval EFI_UNSUPPORTED The operation is not supported. + @retval EFI_SECURITY_VIOLATIO 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 *ImageUpdateable + ) +{ + EFI_STATUS Status; + UINTN RawSize; + VOID *FmpPayloadHeader; + UINTN FmpPayloadSize; + UINT32 Version; + UINT32 FmpHeaderSize; + UINTN AllHeaderSize; + UINT32 Index; + VOID *PublicKeyData; + UINTN PublicKeyDataLength; + UINT8 *PublicKeyDataXdr; + UINT8 *PublicKeyDataXdrEnd; + + Status = EFI_SUCCESS; + RawSize = 0; + FmpPayloadHeader = NULL; + FmpPayloadSize = 0; + Version = 0; + FmpHeaderSize = 0; + AllHeaderSize = 0; + + // + // make sure the descriptor has already been loaded + // + if (!mDescriptorPopulated) { + PopulateDescriptor(); + } + + if (ImageUpdateable == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: CheckImage() - ImageUpdateable Pointer Parameter is NULL.\n")); + Status = EFI_INVALID_PARAMETER; + goto cleanup; + } + + // + //Set to valid and then if any tests fail it will update this flag. + // + *ImageUpdateable = IMAGE_UPDATABLE_VALID; + + if (Image == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: CheckImage() - Image Pointer Parameter is NULL.\n")); + // + // not sure if this is needed + // + *ImageUpdateable = IMAGE_UPDATABLE_INVALID; + return EFI_INVALID_PARAMETER; + } + + PublicKeyDataXdr = PcdGetPtr (PcdFmpDevicePkcs7CertBufferXdr); + PublicKeyDataXdrEnd = PublicKeyDataXdr + PcdGetSize (PcdFmpDevicePkcs7CertBufferXdr); + + if (PublicKeyDataXdr == NULL || (PublicKeyDataXdr == PublicKeyDataXdrEnd)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: Invalid certificate, skipping it.\n")); + Status = EFI_ABORTED; + } else { + // + // Try each key from PcdFmpDevicePkcs7CertBufferXdr + // + for (Index = 1; PublicKeyDataXdr < PublicKeyDataXdrEnd; Index++) { + Index++; + DEBUG ( + (DEBUG_INFO, + "FmpDxe: Certificate #%d [%p..%p].\n", + Index, + PublicKeyDataXdr, + PublicKeyDataXdrEnd + ) + ); + + if ((PublicKeyDataXdr + sizeof (UINT32)) > PublicKeyDataXdrEnd) { + // + // Key data extends beyond end of PCD + // + DEBUG ((DEBUG_ERROR, "FmpDxe: Certificate size extends beyond end of PCD, skipping it.\n")); + Status = EFI_ABORTED; + 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: Certificate extends beyond end of PCD, skipping it.\n")); + Status = EFI_ABORTED; + 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: CheckTheImage() - Authentication Failed %r.\n", Status)); + goto cleanup; + } + + // + // Check to make sure index is 1 + // + if (ImageIndex != 1) { + DEBUG ((DEBUG_ERROR, "FmpDxe: CheckImage() - Image Index Invalid.\n")); + *ImageUpdateable = IMAGE_UPDATABLE_INVALID_TYPE; + Status = EFI_SUCCESS; + goto cleanup; + } + + + // + // Check the FmpPayloadHeader + // + FmpPayloadHeader = GetFmpHeader ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &FmpPayloadSize ); + if (FmpPayloadHeader == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetFmpHeader failed.\n")); + Status = EFI_ABORTED; + goto cleanup; + } + Status = GetFmpPayloadHeaderVersion (FmpPayloadHeader, FmpPayloadSize, &Version); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetFmpPayloadHeaderVersion failed %r.\n", Status)); + *ImageUpdateable = IMAGE_UPDATABLE_INVALID; + Status = EFI_SUCCESS; + goto cleanup; + } + + // + // Check the lowest supported version + // + if (Version < mDesc.LowestSupportedImageVersion) { + DEBUG ( + (DEBUG_ERROR, + "FmpDxe: CheckTheImage() - Version Lower than lowest supported version. 0x%08X < 0x%08X\n", + Version, mDesc.LowestSupportedImageVersion) + ); + *ImageUpdateable = IMAGE_UPDATABLE_INVALID_OLD; + Status = EFI_SUCCESS; + 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)); + *ImageUpdateable = IMAGE_UPDATABLE_INVALID; + Status = EFI_SUCCESS; + 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 ); + if (AllHeaderSize == 0) { + DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - GetAllHeaderSize failed.\n")); + Status = EFI_ABORTED; + goto cleanup; + } + RawSize = ImageSize - AllHeaderSize; + + // + // FmpDeviceLib CheckImage function to do any specific checks + // + Status = FmpDeviceCheckImage ((((UINT8 *)Image) + AllHeaderSize), RawSize, ImageUpdateable); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: CheckTheImage() - FmpDeviceLib CheckImage failed. Status = %r\n", Status)); + } + +cleanup: + return Status; +} + +/** + 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_VIOLATIO 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; + UINT32 Updateable; + BOOLEAN BooleanValue; + UINT32 FmpHeaderSize; + VOID *FmpHeader; + UINTN FmpPayloadSize; + UINT32 AllHeaderSize; + UINT32 IncommingFwVersion; + UINT32 LastAttemptStatus; + + Status = EFI_SUCCESS; + Updateable = 0; + BooleanValue = FALSE; + FmpHeaderSize = 0; + FmpHeader = NULL; + FmpPayloadSize = 0; + AllHeaderSize = 0; + IncommingFwVersion = 0; + LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; + + + SetLastAttemptVersionInVariable (IncommingFwVersion); //set to 0 to clear any previous results. + + // + // 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 (mFmpDeviceLocked) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Device is already locked. Can't update.\n")); + Status = EFI_ACCESS_DENIED; + goto cleanup; + } + + // + // Call check image to verify the image + // + Status = CheckTheImage (This, ImageIndex, Image, ImageSize, &Updateable); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Check The Image failed with %r.\n", Status)); + if (Status == EFI_SECURITY_VIOLATION) { + LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR; + } + goto cleanup; + } + + // + // No functional error in CheckTheImage. Attempt to get the Version to + // support better error reporting. + // + FmpHeader = GetFmpHeader ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &FmpPayloadSize ); + if (FmpHeader == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - GetFmpHeader failed.\n")); + Status = EFI_ABORTED; + goto cleanup; + } + Status = GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &IncommingFwVersion); + if (!EFI_ERROR (Status)) { + // + // Set to actual value + // + SetLastAttemptVersionInVariable (IncommingFwVersion); + } + + + if (Updateable != IMAGE_UPDATABLE_VALID) { + DEBUG ( + (DEBUG_ERROR, + "FmpDxed: SetTheImage() - Check The Image returned that the Image was not valid for update. Updatable value = 0x%X.\n", + Updateable) + ); + Status = EFI_ABORTED; + goto cleanup; + } + + if (Progress == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Invalid progress callback\n")); + Status = EFI_INVALID_PARAMETER; + goto cleanup; + } + + mProgressFunc = Progress; + mProgressSupported = TRUE; + + // + // Checking the image is at least 1% + // + Status = Progress (1); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Progress Callback failed with Status %r.\n", Status)); + } + + // + //Check System Power + // + Status = CheckSystemPower (&BooleanValue); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - CheckSystemPower - API call failed %r.\n", Status)); + goto cleanup; + } + if (!BooleanValue) { + Status = EFI_ABORTED; + DEBUG ( + (DEBUG_ERROR, + "FmpDxe: SetTheImage() - CheckSystemPower - returned False. Update not allowed due to System Power.\n") + ); + 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: SetTheImage() - CheckSystemThermal - API call failed %r.\n", Status)); + goto cleanup; + } + if (!BooleanValue) { + Status = EFI_ABORTED; + DEBUG ( + (DEBUG_ERROR, + "FmpDxe: SetTheImage() - CheckSystemThermal - returned False. Update not allowed due to System Thermal.\n") + ); + goto cleanup; + } + + Progress (3); + + // + //Check System Environment + // + Status = CheckSystemEnvironment (&BooleanValue); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - CheckSystemEnvironment - API call failed %r.\n", Status)); + goto cleanup; + } + if (!BooleanValue) { + Status = EFI_ABORTED; + DEBUG ( + (DEBUG_ERROR, + "FmpDxe: SetTheImage() - CheckSystemEnvironment - returned False. Update not allowed due to System Environment.\n") + ); + goto cleanup; + } + + Progress (4); + + // + // Save LastAttemptStatus as error so that if SetImage never returns the error + // state is recorded. + // + SetLastAttemptStatusInVariable (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: SetTheImage() - GetFmpPayloadHeaderSize failed %r.\n", Status)); + goto cleanup; + } + + AllHeaderSize = GetAllHeaderSize ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize ); + if (AllHeaderSize == 0) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - GetAllHeaderSize failed.\n")); + 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 = FmpDeviceSetImage ( + (((UINT8 *)Image) + AllHeaderSize), + ImageSize - AllHeaderSize, + VendorCode, + FmpDxeProgress, + IncommingFwVersion, + AbortReason + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() SetImage from FmpDeviceLib failed. Status = %r.\n", Status)); + goto cleanup; + } + + + // + // Finished the update without error + // Indicate that control has been returned from FmpDeviceLib + // + Progress (99); + + // + // Update the version stored in variable + // + if (!mRuntimeVersionSupported) { + UINT32 Version = DEFAULT_VERSION; + GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &Version); + SetVersionInVariable (Version); + } + + // + // Update lowest supported variable + // + { + UINT32 Version = DEFAULT_LOWESTSUPPORTEDVERSION; + GetFmpPayloadHeaderLowestSupportedVersion (FmpHeader, FmpPayloadSize, &Version); + SetLowestSupportedVersionInVariable (Version); + } + + LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; + + // + // Set flag so the descriptor is repopulated + // This only applied to devices that do not require system reboot + // + if (!PcdGetBool (PcdFmpDeviceSystemResetRequired)) { + mDescriptorPopulated = FALSE; + } + +cleanup: + mProgressFunc = NULL; + mProgressSupported = FALSE; + SetLastAttemptStatusInVariable (LastAttemptStatus); + + // + // Set progress to 100 after everything is done including recording Status. + // + Progress (100); + + 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_VIOLATIO 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; + + if (!mFmpDeviceLocked) { + if (IsLockFmpDeviceAtLockEventGuidRequired ()) { + // + // Lock all UEFI Variables used by this module. + // + Status = LockAllFmpVariables (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to lock variables. Status = %r.\n")); + } else { + DEBUG ((DEBUG_INFO, "FmpDxe: All variables locked\n")); + } + + // + // Lock the firmware device + // + Status = FmpDeviceLock(); + if (EFI_ERROR (Status)) { + if (Status != EFI_UNSUPPORTED) { + DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLock() returned error. Status = %r\n", Status)); + } else { + DEBUG ((DEBUG_WARN, "FmpDxe: FmpDeviceLock() returned error. Status = %r\n", Status)); + } + } + mFmpDeviceLocked = TRUE; + } else { + DEBUG ((DEBUG_VERBOSE, "FmpDxe: Not calling FmpDeviceLock() because mfg mode\n")); + } + } +} + +/** + 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; + EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL *FmpProgress; + + Status = EFI_SUCCESS; + Fmp = NULL; + FmpProgress = NULL; + + // + // Only allow a single FMP Protocol instance to be installed + // + if (mFmpInstalled) { + return EFI_ALREADY_STARTED; + } + + // + // Allocate FMP Protocol instance + // + Fmp = AllocateZeroPool (sizeof (EFI_FIRMWARE_MANAGEMENT_PROTOCOL)); + if (Fmp == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to allocate memory for FMP Protocol instance.\n")); + Status = EFI_OUT_OF_RESOURCES; + goto cleanup; + } + + // + // Allocate FMP Progress Protocol instance + // + FmpProgress = AllocateZeroPool (sizeof (EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL)); + if (FmpProgress == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to allocate memory for FMP Progress Protocol instance.\n")); + Status = EFI_OUT_OF_RESOURCES; + FreePool (Fmp); + goto cleanup; + } + + // + // Set up FMP Protocol function pointers + // + Fmp->GetImageInfo = GetTheImageInfo; + Fmp->GetImage = GetTheImage; + Fmp->SetImage = SetTheImage; + Fmp->CheckImage = CheckTheImage; + Fmp->GetPackageInfo = GetPackageInfo; + Fmp->SetPackageInfo = SetPackageInfo; + + // + // Fill in FMP Progress Protocol fields for Version 1 + // + FmpProgress->Version = 1; + FmpProgress->ProgressBarForegroundColor.Raw = PcdGet32 (PcdFmpDeviceProgressColor); + FmpProgress->WatchdogSeconds = PcdGet8 (PcdFmpDeviceProgressWatchdogTimeInSeconds); + + // + // Install FMP Protocol and FMP Progress Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiFirmwareManagementProtocolGuid, + Fmp, + &gEdkiiFirmwareManagementProgressProtocolGuid, + FmpProgress, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: FMP Protocol install error. Status = %r.\n", Status)); + FreePool (Fmp); + goto cleanup; + } + + DEBUG ((DEBUG_INFO, "FmpDxe: FMP Protocol Installed!\n")); + mFmpInstalled = TRUE; + +cleanup: + + return Status; +} + +/** + Main entry for this 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; + EFI_GUID *LockGuid; + + // + // Verify that a new FILE_GUID value has been provided in the + // 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: FmpDeviceLib PcdFmpDeviceImageIdName is an empty string.\n")); + ASSERT (FALSE); + } + + // + // Detects if PcdFmpDevicePkcs7CertBufferXdr contains a test key. + // + DetectTestKey (); + + // + // 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) { + DEBUG ((DEBUG_INFO, "FmpDxe: FmpDeviceLib registration returned EFI_UNSUPPORTED. Installing single FMP instance.\n")); + Status = InstallFmpInstance (ImageHandle); + } else if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib registration returned %r. No FMP installed.\n", Status)); + } else { + DEBUG (( + DEBUG_INFO, + "FmpDxe: FmpDeviceLib registration returned EFI_SUCCESS. Expect FMP to be installed during the BDS/Device connection phase.\n" + )); + } + + // + // Register notify function to lock the FMP device. + // The lock event GUID is retrieved from PcdFmpDeviceLockEventGuid. + // If PcdFmpDeviceLockEventGuid is not the size of an EFI_GUID, then + // gEfiEndOfDxeEventGroupGuid is used. + // + LockGuid = &gEfiEndOfDxeEventGroupGuid; + if (PcdGetSize (PcdFmpDeviceLockEventGuid) == sizeof (EFI_GUID)) { + LockGuid = (EFI_GUID *)PcdGetPtr (PcdFmpDeviceLockEventGuid); + } + DEBUG ((DEBUG_INFO, "FmpDxe: Lock GUID: %g\n", LockGuid)); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + FmpDxeLockEventNotify, + NULL, + LockGuid, + &mFmpDeviceLockEvent + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to register for ready to boot. Status = %r\n", Status)); + } + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/FmpDevicePkg/FmpDxe/FmpDxe.inf b/FmpDevicePkg/FmpDxe/FmpDxe.inf new file mode 100644 index 0000000000..256c50bf03 --- /dev/null +++ b/FmpDevicePkg/FmpDxe/FmpDxe.inf @@ -0,0 +1,93 @@ +## @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) 2016, Microsoft Corporation. All rights reserved.
+# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FmpDxe + MODULE_UNI_FILE = FmpDxe.uni + FILE_GUID = 78EF0A56-1CF0-4535-B5DA-F6FD2F405A11 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FmpDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF ARM AARCH64 +# + +[Sources] + FmpDxe.c + DetectTestKey.c + VariableSupport.h + VariableSupport.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + FmpDevicePkg/FmpDevicePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + DebugLib + BaseLib + BaseMemoryLib + UefiBootServicesTableLib + MemoryAllocationLib + UefiLib + BaseCryptLib + FmpAuthenticationLib + FmpDeviceLib + FmpPayloadHeaderLib + CapsuleUpdatePolicyLib + +[Guids] + gEfiEndOfDxeEventGroupGuid + +[Protocols] + gEdkiiVariableLockProtocolGuid ## CONSUMES + gEfiFirmwareManagementProtocolGuid ## PRODUCES + gEdkiiFirmwareManagementProgressProtocolGuid ## PRODUCES + +[Pcd] + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceSystemResetRequired ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceImageIdName ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceBuildTimeLowestSupportedVersion ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceLockEventGuid ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceProgressWatchdogTimeInSeconds ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceProgressColor ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDevicePkcs7CertBufferXdr ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceTestKeySha256Digest ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdTestKeyUsed ## SOMETIMES_PRODUCES + +[Depex] + gEfiVariableWriteArchProtocolGuid AND gEdkiiVariableLockProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + FmpDxeExtra.uni diff --git a/FmpDevicePkg/FmpDxe/FmpDxe.uni b/FmpDevicePkg/FmpDxe/FmpDxe.uni new file mode 100644 index 0000000000..502ec0660d --- /dev/null +++ b/FmpDevicePkg/FmpDxe/FmpDxe.uni @@ -0,0 +1,20 @@ +// /** @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) 2018, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Produces a Firmware Management Protocol to support firmware updates" + +#string STR_MODULE_DESCRIPTION #language en-US "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." diff --git a/FmpDevicePkg/FmpDxe/FmpDxeExtra.uni b/FmpDevicePkg/FmpDxe/FmpDxeExtra.uni new file mode 100644 index 0000000000..8f3cc4367c --- /dev/null +++ b/FmpDevicePkg/FmpDxe/FmpDxeExtra.uni @@ -0,0 +1,18 @@ +// /** @file +// FmpDxe Localized Strings and Content +// +// Copyright (c) 2018, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Firmware Management Protocol DXE DXE Driver" diff --git a/FmpDevicePkg/FmpDxe/FmpDxeLib.inf b/FmpDevicePkg/FmpDxe/FmpDxeLib.inf new file mode 100644 index 0000000000..c8fe49e0c0 --- /dev/null +++ b/FmpDevicePkg/FmpDxe/FmpDxeLib.inf @@ -0,0 +1,90 @@ +## @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) 2016, Microsoft Corporation. All rights reserved.
+# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FmpDxeLib + MODULE_UNI_FILE = FmpDxe.uni + FILE_GUID = 4B11717A-30B3-4122-8C69-8E0D5E141C32 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL + CONSTRUCTOR = FmpDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF ARM AARCH64 +# + +[Sources] + FmpDxe.c + DetectTestKey.c + VariableSupport.h + VariableSupport.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + FmpDevicePkg/FmpDevicePkg.dec + +[LibraryClasses] + DebugLib + BaseLib + BaseMemoryLib + UefiBootServicesTableLib + MemoryAllocationLib + UefiLib + BaseCryptLib + FmpAuthenticationLib + FmpDeviceLib + FmpPayloadHeaderLib + CapsuleUpdatePolicyLib + +[Guids] + gEfiEndOfDxeEventGroupGuid + +[Protocols] + gEdkiiVariableLockProtocolGuid ## CONSUMES + gEfiFirmwareManagementProtocolGuid ## PRODUCES + gEdkiiFirmwareManagementProgressProtocolGuid ## PRODUCES + +[Pcd] + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceSystemResetRequired ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceImageIdName ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceBuildTimeLowestSupportedVersion ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceLockEventGuid ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceProgressWatchdogTimeInSeconds ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceProgressColor ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDevicePkcs7CertBufferXdr ## CONSUMES + gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceTestKeySha256Digest ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdTestKeyUsed ## SOMETIMES_PRODUCES + +[Depex] + gEfiVariableWriteArchProtocolGuid AND gEdkiiVariableLockProtocolGuid diff --git a/FmpDevicePkg/FmpDxe/VariableSupport.c b/FmpDevicePkg/FmpDxe/VariableSupport.c new file mode 100644 index 0000000000..c15178d099 --- /dev/null +++ b/FmpDevicePkg/FmpDxe/VariableSupport.c @@ -0,0 +1,461 @@ +/** @file + UEFI variable support functions for Firmware Management Protocol based + firmware updates. + + Copyright (c) 2016, Microsoft Corporation. All rights reserved.
+ Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include "VariableSupport.h" + +/// +/// Array of UEFI variable names that are locked in LockAllFmpVariables(). +/// +const CHAR16 *mFmpVariableLockList[] = { + VARNAME_VERSION, + VARNAME_LSV, + VARNAME_LASTATTEMPTSTATUS, + VARNAME_LASTATTEMPTVERSION +}; + +/** + Returns the value used to fill in the Version field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default version value + is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion" + + @return The version of the firmware image in the firmware device. + +**/ +UINT32 +GetVersionFromVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 *Value; + UINTN Size; + UINT32 Version; + + Value = NULL; + Size = 0; + Version = DEFAULT_VERSION; + + Status = GetVariable2 (VARNAME_VERSION, &gEfiCallerIdGuid, (VOID **)&Value, &Size); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to get the Version from variable. Status = %r\n", Status)); + return Version; + } + + // + // No error from call + // + if (Size == sizeof (*Value)) { + // + // Successful read + // + Version = *Value; + } else { + // + // Return default since size was unknown + // + DEBUG ((DEBUG_ERROR, "Getting version Variable returned a size different than expected. Size = 0x%x\n", Size)); + } + + FreePool (Value); + + return Version; +} + +/** + Returns the value used to fill in the LowestSupportedVersion field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default lowest + supported version value is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv" + + @return The lowest supported version of the firmware image in the firmware + device. + +**/ +UINT32 +GetLowestSupportedVersionFromVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 *Value; + UINTN Size; + UINT32 Version; + + Value = NULL; + Size = 0; + Version = DEFAULT_LOWESTSUPPORTEDVERSION; + + Status = GetVariable2 (VARNAME_LSV, &gEfiCallerIdGuid, (VOID **)&Value, &Size); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "Warning: Failed to get the Lowest Supported Version from variable. Status = %r\n", Status)); + return Version; + } + + // + // No error from call + // + if (Size == sizeof (*Value)) { + // + // Successful read + // + Version = *Value; + } else { + // + // Return default since size was unknown + // + DEBUG ((DEBUG_ERROR, "Getting LSV Variable returned a size different than expected. Size = 0x%x\n", Size)); + } + + FreePool (Value); + + return Version; +} + +/** + Returns the value used to fill in the LastAttemptStatus field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default last attempt + status value is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus" + + @return The last attempt status value for the most recent capsule update. + +**/ +UINT32 +GetLastAttemptStatusFromVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 *Value; + UINTN Size; + UINT32 LastAttemptStatus; + + Value = NULL; + Size = 0; + LastAttemptStatus = DEFAULT_LASTATTEMPT; + + Status = GetVariable2 (VARNAME_LASTATTEMPTSTATUS, &gEfiCallerIdGuid, (VOID **)&Value, &Size); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "Warning: Failed to get the Last Attempt Status from variable. Status = %r\n", Status)); + return LastAttemptStatus; + } + + // + // No error from call + // + if (Size == sizeof (*Value)) { + // + // Successful read + // + LastAttemptStatus = *Value; + } else { + // + // Return default since size was unknown + // + DEBUG ( + (DEBUG_ERROR, + "Getting Last Attempt Status Variable returned a size different than expected. Size = 0x%x\n", + Size) + ); + } + + FreePool (Value); + + return LastAttemptStatus; +} + +/** + Returns the value used to fill in the LastAttemptVersion field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default last attempt + version value is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion" + + @return The last attempt version value for the most recent capsule update. + +**/ +UINT32 +GetLastAttemptVersionFromVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 *Value; + UINTN Size; + UINT32 Version; + + Value = NULL; + Size = 0; + Version = DEFAULT_LASTATTEMPT; + + Status = GetVariable2 (VARNAME_LASTATTEMPTVERSION, &gEfiCallerIdGuid, (VOID **)&Value, &Size); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "Warning: Failed to get the Last Attempt Version from variable. Status = %r\n", Status)); + return Version; + } + + // + // No error from call + // + if (Size == sizeof (*Value)) { + // + // Successful read + // + Version = *Value; + } else { + // + // Return default since size was unknown + // + DEBUG ( + (DEBUG_ERROR, + "Getting Last Attempt Version variable returned a size different than expected. Size = 0x%x\n", + Size) + ); + } + + FreePool (Value); + + return Version; +} + + +/** + Saves the version current of the firmware image in the firmware device to a + UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion" + + @param[in] Version The version of the firmware image in the firmware device. + +**/ +VOID +SetVersionInVariable ( + UINT32 Version + ) +{ + EFI_STATUS Status; + UINT32 Current; + + Status = EFI_SUCCESS; + + Current = GetVersionFromVariable(); + if (Current != Version) { + Status = gRT->SetVariable ( + VARNAME_VERSION, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (Version), + &Version + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set the Version into a variable. Status = %r\n", Status)); + } + } else { + DEBUG ((DEBUG_INFO, "Version variable doesn't need to update. Same value as before.\n")); + } +} + +/** + Saves the lowest supported version current of the firmware image in the + firmware device to a UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv" + + @param[in] LowestSupported The lowest supported version of the firmware image + in the firmware device. + +**/ +VOID +SetLowestSupportedVersionInVariable ( + UINT32 LowestSupportedVersion + ) +{ + EFI_STATUS Status; + UINT32 Current; + + Status = EFI_SUCCESS; + + Current = GetLowestSupportedVersionFromVariable(); + if (LowestSupportedVersion > Current) { + Status = gRT->SetVariable ( + VARNAME_LSV, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (LowestSupportedVersion), &LowestSupportedVersion + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set the LSV into a variable. Status = %r\n", Status)); + } + } else { + DEBUG ((DEBUG_INFO, "LSV variable doesn't need to update. Same value as before.\n")); + } +} + +/** + Saves the last attempt status value of the most recent FMP capsule update to a + UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus" + + @param[in] LastAttemptStatus The last attempt status of the most recent FMP + capsule update. + +**/ +VOID +SetLastAttemptStatusInVariable ( + UINT32 LastAttemptStatus + ) +{ + EFI_STATUS Status; + UINT32 Current; + + Status = EFI_SUCCESS; + + Current = GetLastAttemptStatusFromVariable(); + if (Current != LastAttemptStatus) { + Status = gRT->SetVariable ( + VARNAME_LASTATTEMPTSTATUS, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (LastAttemptStatus), + &LastAttemptStatus + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set the LastAttemptStatus into a variable. Status = %r\n", Status)); + } + } else { + DEBUG ((DEBUG_INFO, "LastAttemptStatus variable doesn't need to update. Same value as before.\n")); + } +} + +/** + Saves the last attempt version value of the most recent FMP capsule update to + a UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion" + + @param[in] LastAttemptVersion The last attempt version value of the most + recent FMP capsule update. + +**/ +VOID +SetLastAttemptVersionInVariable ( + UINT32 LastAttemptVersion + ) +{ + EFI_STATUS Status; + UINT32 Current; + + Status = EFI_SUCCESS; + + Current = GetLastAttemptVersionFromVariable(); + if (Current != LastAttemptVersion) { + Status = gRT->SetVariable ( + VARNAME_LASTATTEMPTVERSION, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (LastAttemptVersion), + &LastAttemptVersion + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set the LastAttemptVersion into a variable. Status = %r\n", Status)); + } + } else { + DEBUG ((DEBUG_INFO, "LastAttemptVersion variable doesn't need to update. Same value as before.\n")); + } +} + +/** + Locks all the UEFI Variables used by this module. + + @retval EFI_SUCCESS All UEFI variables are locked. + @retval EFI_UNSUPPORTED Variable Lock Protocol not found. + @retval Other One of the UEFI variables could not be locked. + +**/ +EFI_STATUS +LockAllFmpVariables ( + VOID + ) +{ + EFI_STATUS Status; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + EFI_STATUS ReturnStatus; + UINTN Index; + + VariableLock = NULL; + Status = gBS->LocateProtocol ( + &gEdkiiVariableLockProtocolGuid, + NULL, + (VOID **)&VariableLock + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to locate Variable Lock Protocol (%r).\n", Status)); + return EFI_UNSUPPORTED; + } + + ReturnStatus = EFI_SUCCESS; + for (Index = 0; Index < ARRAY_SIZE (mFmpVariableLockList); Index++) { + Status = VariableLock->RequestToLock ( + VariableLock, + (CHAR16 *)mFmpVariableLockList[Index], + &gEfiCallerIdGuid + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to lock variable %g %s. Status = %r\n", + &gEfiCallerIdGuid, + mFmpVariableLockList[Index], + Status + )); + if (!EFI_ERROR (ReturnStatus)) { + ReturnStatus = Status; + } + } + } + + return ReturnStatus; +} diff --git a/FmpDevicePkg/FmpDxe/VariableSupport.h b/FmpDevicePkg/FmpDxe/VariableSupport.h new file mode 100644 index 0000000000..e7e34f5d8f --- /dev/null +++ b/FmpDevicePkg/FmpDxe/VariableSupport.h @@ -0,0 +1,180 @@ +/** @file + UEFI variable support functions for Firmware Management Protocol based + firmware updates. + + Copyright (c) 2016, Microsoft Corporation. All rights reserved.
+ Copyright (c) 2018, Intel Corporation. All rights reserved.
+ + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +**/ + +#ifndef __VARIABLE_SUPPORT_H__ +#define __VARIABLE_SUPPORT_H__ + +#define DEFAULT_VERSION 0x1 +#define DEFAULT_LOWESTSUPPORTEDVERSION 0x0 +#define DEFAULT_LASTATTEMPT 0x0 + +#define VARNAME_VERSION L"FmpVersion" +#define VARNAME_LSV L"FmpLsv" + +#define VARNAME_LASTATTEMPTSTATUS L"LastAttemptStatus" +#define VARNAME_LASTATTEMPTVERSION L"LastAttemptVersion" + +/** + Returns the value used to fill in the Version field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default version value + is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion" + + @return The version of the firmware image in the firmware device. + +**/ +UINT32 +GetVersionFromVariable ( + VOID + ); + +/** + Returns the value used to fill in the LowestSupportedVersion field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default lowest + supported version value is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv" + + @return The lowest supported version of the firmware image in the firmware + device. + +**/ +UINT32 +GetLowestSupportedVersionFromVariable ( + VOID + ); + +/** + Returns the value used to fill in the LastAttemptStatus field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default last attempt + status value is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus" + + @return The last attempt status value for the most recent capsule update. + +**/ +UINT32 +GetLastAttemptStatusFromVariable ( + VOID + ); + +/** + Returns the value used to fill in the LastAttemptVersion field of the + EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo() + service of the Firmware Management Protocol. The value is read from a UEFI + variable. If the UEFI variables does not exist, then a default last attempt + version value is returned. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion" + + @return The last attempt version value for the most recent capsule update. + +**/ +UINT32 +GetLastAttemptVersionFromVariable ( + VOID + ); + +/** + Saves the version current of the firmware image in the firmware device to a + UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion" + + @param[in] Version The version of the firmware image in the firmware device. + +**/ +VOID +SetVersionInVariable ( + UINT32 Version + ); + +/** + Saves the lowest supported version current of the firmware image in the + firmware device to a UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv" + + @param[in] LowestSupported The lowest supported version of the firmware image + in the firmware device. + +**/ +VOID +SetLowestSupportedVersionInVariable ( + UINT32 LowestSupportedVersion + ); + +/** + Saves the last attempt status value of the most recent FMP capsule update to a + UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus" + + @param[in] LastAttemptStatus The last attempt status of the most recent FMP + capsule update. + +**/ +VOID +SetLastAttemptStatusInVariable ( + UINT32 LastAttemptStatus + ); + +/** + Saves the last attempt version value of the most recent FMP capsule update to + a UEFI variable. + + UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion" + + @param[in] LastAttemptVersion The last attempt version value of the most + recent FMP capsule update. + +**/ +VOID +SetLastAttemptVersionInVariable ( + UINT32 LastAttemptVersion + ); + +/** + Locks all the UEFI Variables that use gEfiCallerIdGuid of the currently + executing module. + +**/ +EFI_STATUS +LockAllFmpVariables ( + VOID + ); + +#endif