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