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