mirror of https://github.com/acidanthera/audk.git
557 lines
19 KiB
C
557 lines
19 KiB
C
/** @file
|
|
Capsule Library instance to process capsule images.
|
|
|
|
Copyright (c) 2007 - 2016, 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 <PiDxe.h>
|
|
|
|
#include <Guid/Capsule.h>
|
|
#include <Guid/FmpCapsule.h>
|
|
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DxeServicesTableLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/CapsuleLib.h>
|
|
#include <Library/GenericBdsLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
|
|
#include <Protocol/FirmwareManagement.h>
|
|
#include <Protocol/DevicePath.h>
|
|
|
|
|
|
/**
|
|
Function indicate the current completion progress of the firmware
|
|
update. Platform may override with own specific progress function.
|
|
|
|
@param Completion A value between 1 and 100 indicating the current completion progress of the firmware update
|
|
|
|
@retval EFI_SUCESS Input capsule is a correct FMP capsule.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Update_Image_Progress (
|
|
IN UINTN Completion
|
|
)
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Validate Fmp capsules layout.
|
|
|
|
@param CapsuleHeader Points to a capsule header.
|
|
|
|
@retval EFI_SUCESS Input capsule is a correct FMP capsule.
|
|
@retval EFI_INVALID_PARAMETER Input capsule is not a correct FMP capsule.
|
|
**/
|
|
EFI_STATUS
|
|
ValidateFmpCapsule (
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
|
)
|
|
{
|
|
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;
|
|
UINT8 *EndOfCapsule;
|
|
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;
|
|
UINT8 *EndOfPayload;
|
|
UINT64 *ItemOffsetList;
|
|
UINT32 ItemNum;
|
|
UINTN Index;
|
|
|
|
FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);
|
|
EndOfCapsule = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize;
|
|
|
|
if (FmpCapsuleHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);
|
|
|
|
ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;
|
|
|
|
if (ItemNum == FmpCapsuleHeader->EmbeddedDriverCount) {
|
|
//
|
|
// No payload element
|
|
//
|
|
if (((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemNum - 1]) < EndOfCapsule) {
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (FmpCapsuleHeader->PayloadItemCount != 0) {
|
|
//
|
|
// Check if the last payload is within capsule image range
|
|
//
|
|
ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemNum - 1]);
|
|
EndOfPayload = (UINT8 *)(ImageHeader + 1) + ImageHeader->UpdateImageSize + ImageHeader->UpdateVendorCodeSize;
|
|
} else {
|
|
//
|
|
// No driver & payload element in FMP
|
|
//
|
|
EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1);
|
|
}
|
|
|
|
if (EndOfPayload != EndOfCapsule) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// All the address in ItemOffsetList must be stored in ascending order
|
|
//
|
|
if (ItemNum >= 2) {
|
|
for (Index = 0; Index < ItemNum - 1; Index++) {
|
|
if (ItemOffsetList[Index] >= ItemOffsetList[Index + 1]) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Process Firmware management protocol data capsule.
|
|
|
|
@param CapsuleHeader Points to a capsule header.
|
|
|
|
@retval EFI_SUCESS Process Capsule Image successfully.
|
|
@retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.
|
|
@retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.
|
|
@retval EFI_OUT_OF_RESOURCES Not enough memory.
|
|
**/
|
|
EFI_STATUS
|
|
ProcessFmpCapsuleImage (
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;
|
|
EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;
|
|
EFI_HANDLE ImageHandle;
|
|
UINT64 *ItemOffsetList;
|
|
UINT32 ItemNum;
|
|
UINTN Index;
|
|
UINTN ExitDataSize;
|
|
EFI_HANDLE *HandleBuffer;
|
|
EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
|
|
UINTN NumberOfHandles;
|
|
UINTN DescriptorSize;
|
|
UINT8 FmpImageInfoCount;
|
|
UINT32 FmpImageInfoDescriptorVer;
|
|
UINTN ImageInfoSize;
|
|
UINT32 PackageVersion;
|
|
CHAR16 *PackageVersionName;
|
|
CHAR16 *AbortReason;
|
|
EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
|
|
EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo;
|
|
UINTN DriverLen;
|
|
UINTN Index1;
|
|
UINTN Index2;
|
|
MEMMAP_DEVICE_PATH MemMapNode;
|
|
EFI_DEVICE_PATH_PROTOCOL *DriverDevicePath;
|
|
|
|
Status = EFI_SUCCESS;
|
|
HandleBuffer = NULL;
|
|
ExitDataSize = 0;
|
|
DriverDevicePath = NULL;
|
|
|
|
FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);
|
|
|
|
if (FmpCapsuleHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);
|
|
|
|
ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;
|
|
|
|
//
|
|
// capsule in which driver count and payload count are both zero is not processed.
|
|
//
|
|
if (ItemNum == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// 1. ConnectAll to ensure
|
|
// All the communication protocol required by driver in capsule installed
|
|
// All FMP protocols are installed
|
|
//
|
|
BdsLibConnectAll();
|
|
|
|
|
|
//
|
|
// 2. Try to load & start all the drivers within capsule
|
|
//
|
|
SetDevicePathNodeLength (&MemMapNode.Header, sizeof (MemMapNode));
|
|
MemMapNode.Header.Type = HARDWARE_DEVICE_PATH;
|
|
MemMapNode.Header.SubType = HW_MEMMAP_DP;
|
|
MemMapNode.MemoryType = EfiBootServicesCode;
|
|
MemMapNode.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CapsuleHeader;
|
|
MemMapNode.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)((UINT8 *)CapsuleHeader + CapsuleHeader->CapsuleImageSize - 1);
|
|
|
|
DriverDevicePath = AppendDevicePathNode (NULL, &MemMapNode.Header);
|
|
if (DriverDevicePath == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (Index = 0; Index < FmpCapsuleHeader->EmbeddedDriverCount; Index++) {
|
|
if (FmpCapsuleHeader->PayloadItemCount == 0 && Index == (UINTN)FmpCapsuleHeader->EmbeddedDriverCount - 1) {
|
|
//
|
|
// When driver is last element in the ItemOffsetList array, the driver size is calculated by reference CapsuleImageSize in EFI_CAPSULE_HEADER
|
|
//
|
|
DriverLen = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize - (UINTN)ItemOffsetList[Index];
|
|
} else {
|
|
DriverLen = (UINTN)ItemOffsetList[Index + 1] - (UINTN)ItemOffsetList[Index];
|
|
}
|
|
|
|
Status = gBS->LoadImage(
|
|
FALSE,
|
|
gImageHandle,
|
|
DriverDevicePath,
|
|
(UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index],
|
|
DriverLen,
|
|
&ImageHandle
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
Status = gBS->StartImage(
|
|
ImageHandle,
|
|
&ExitDataSize,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Driver Return Status = %r\n", Status));
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Connnect all again to connect drivers within capsule
|
|
//
|
|
if (FmpCapsuleHeader->EmbeddedDriverCount > 0) {
|
|
BdsLibConnectAll();
|
|
}
|
|
|
|
//
|
|
// 3. Route payload to right FMP instance
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiFirmwareManagementProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
for(Index1 = 0; Index1 < NumberOfHandles; Index1++) {
|
|
Status = gBS->HandleProtocol(
|
|
HandleBuffer[Index1],
|
|
&gEfiFirmwareManagementProtocolGuid,
|
|
(VOID **)&Fmp
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
ImageInfoSize = 0;
|
|
Status = Fmp->GetImageInfo (
|
|
Fmp,
|
|
&ImageInfoSize,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
continue;
|
|
}
|
|
|
|
FmpImageInfoBuf = NULL;
|
|
FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
|
|
if (FmpImageInfoBuf == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
PackageVersionName = NULL;
|
|
Status = Fmp->GetImageInfo (
|
|
Fmp,
|
|
&ImageInfoSize, // ImageInfoSize
|
|
FmpImageInfoBuf, // ImageInfo
|
|
&FmpImageInfoDescriptorVer, // DescriptorVersion
|
|
&FmpImageInfoCount, // DescriptorCount
|
|
&DescriptorSize, // DescriptorSize
|
|
&PackageVersion, // PackageVersion
|
|
&PackageVersionName // PackageVersionName
|
|
);
|
|
|
|
//
|
|
// If FMP GetInformation interface failed, skip this resource
|
|
//
|
|
if (EFI_ERROR(Status)) {
|
|
FreePool(FmpImageInfoBuf);
|
|
continue;
|
|
}
|
|
|
|
if (PackageVersionName != NULL) {
|
|
FreePool(PackageVersionName);
|
|
}
|
|
|
|
TempFmpImageInfo = FmpImageInfoBuf;
|
|
for (Index2 = 0; Index2 < FmpImageInfoCount; Index2++) {
|
|
//
|
|
// Check all the payload entry in capsule payload list
|
|
//
|
|
for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) {
|
|
ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);
|
|
if (CompareGuid(&ImageHeader->UpdateImageTypeId, &TempFmpImageInfo->ImageTypeId) &&
|
|
ImageHeader->UpdateImageIndex == TempFmpImageInfo->ImageIndex) {
|
|
AbortReason = NULL;
|
|
if (ImageHeader->UpdateVendorCodeSize == 0) {
|
|
Status = Fmp->SetImage(
|
|
Fmp,
|
|
TempFmpImageInfo->ImageIndex, // ImageIndex
|
|
(UINT8 *)(ImageHeader + 1), // Image
|
|
ImageHeader->UpdateImageSize, // ImageSize
|
|
NULL, // VendorCode
|
|
Update_Image_Progress, // Progress
|
|
&AbortReason // AbortReason
|
|
);
|
|
} else {
|
|
Status = Fmp->SetImage(
|
|
Fmp,
|
|
TempFmpImageInfo->ImageIndex, // ImageIndex
|
|
(UINT8 *)(ImageHeader + 1), // Image
|
|
ImageHeader->UpdateImageSize, // ImageSize
|
|
(UINT8 *)((UINT8 *) (ImageHeader + 1) + ImageHeader->UpdateImageSize), // VendorCode
|
|
Update_Image_Progress, // Progress
|
|
&AbortReason // AbortReason
|
|
);
|
|
}
|
|
if (AbortReason != NULL) {
|
|
DEBUG ((EFI_D_ERROR, "%s\n", AbortReason));
|
|
FreePool(AbortReason);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version
|
|
//
|
|
TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSize);
|
|
}
|
|
FreePool(FmpImageInfoBuf);
|
|
}
|
|
}
|
|
|
|
EXIT:
|
|
|
|
if (HandleBuffer != NULL) {
|
|
FreePool(HandleBuffer);
|
|
}
|
|
|
|
if (DriverDevicePath != NULL) {
|
|
FreePool(DriverDevicePath);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Those capsules supported by the firmwares.
|
|
|
|
@param CapsuleHeader Points to a capsule header.
|
|
|
|
@retval EFI_SUCESS Input capsule is supported by firmware.
|
|
@retval EFI_UNSUPPORTED Input capsule is not supported by the firmware.
|
|
@retval EFI_INVALID_PARAMETER Input capsule layout is not correct
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SupportCapsuleImage (
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
|
)
|
|
{
|
|
if (CompareGuid (&gEfiCapsuleGuid, &CapsuleHeader->CapsuleGuid)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid)) {
|
|
//
|
|
// Check layout of FMP capsule
|
|
//
|
|
return ValidateFmpCapsule(CapsuleHeader);
|
|
}
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/**
|
|
The firmware implements to process the capsule image.
|
|
|
|
@param CapsuleHeader Points to a capsule header.
|
|
|
|
@retval EFI_SUCESS Process Capsule Image successfully.
|
|
@retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.
|
|
@retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.
|
|
@retval EFI_OUT_OF_RESOURCES Not enough memory.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ProcessCapsuleImage (
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
|
)
|
|
{
|
|
UINT32 Length;
|
|
EFI_FIRMWARE_VOLUME_HEADER *FvImage;
|
|
EFI_FIRMWARE_VOLUME_HEADER *ProcessedFvImage;
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE FvProtocolHandle;
|
|
UINT32 FvAlignment;
|
|
|
|
FvImage = NULL;
|
|
ProcessedFvImage = NULL;
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Check FMP capsule layout
|
|
//
|
|
if (CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid)){
|
|
Status = ValidateFmpCapsule(CapsuleHeader);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Press EFI FMP Capsule
|
|
//
|
|
return ProcessFmpCapsuleImage(CapsuleHeader);
|
|
}
|
|
|
|
//
|
|
// Skip the capsule header, move to the Firware Volume
|
|
//
|
|
FvImage = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);
|
|
Length = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;
|
|
|
|
while (Length != 0) {
|
|
//
|
|
// Point to the next firmware volume header, and then
|
|
// call the DXE service to process it.
|
|
//
|
|
if (FvImage->FvLength > (UINTN) Length) {
|
|
//
|
|
// Notes: need to stuff this status somewhere so that the
|
|
// error can be detected at OS runtime
|
|
//
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
break;
|
|
}
|
|
|
|
FvAlignment = 1 << ((FvImage->Attributes & EFI_FVB2_ALIGNMENT) >> 16);
|
|
//
|
|
// FvAlignment must be more than 8 bytes required by FvHeader structure.
|
|
//
|
|
if (FvAlignment < 8) {
|
|
FvAlignment = 8;
|
|
}
|
|
//
|
|
// Check FvImage Align is required.
|
|
//
|
|
if (((UINTN) FvImage % FvAlignment) == 0) {
|
|
ProcessedFvImage = FvImage;
|
|
} else {
|
|
//
|
|
// Allocate new aligned buffer to store FvImage.
|
|
//
|
|
ProcessedFvImage = (EFI_FIRMWARE_VOLUME_HEADER *) AllocateAlignedPages ((UINTN) EFI_SIZE_TO_PAGES ((UINTN) FvImage->FvLength), (UINTN) FvAlignment);
|
|
if (ProcessedFvImage == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
CopyMem (ProcessedFvImage, FvImage, (UINTN) FvImage->FvLength);
|
|
}
|
|
|
|
Status = gDS->ProcessFirmwareVolume (
|
|
(VOID *) ProcessedFvImage,
|
|
(UINTN) ProcessedFvImage->FvLength,
|
|
&FvProtocolHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
//
|
|
// Call the dispatcher to dispatch any drivers from the produced firmware volume
|
|
//
|
|
gDS->Dispatch ();
|
|
//
|
|
// On to the next FV in the capsule
|
|
//
|
|
Length -= (UINT32) FvImage->FvLength;
|
|
FvImage = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINT8 *) FvImage + FvImage->FvLength);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
This routine is called to process capsules.
|
|
|
|
Caution: This function may receive untrusted input.
|
|
|
|
The capsules reported in EFI_HOB_UEFI_CAPSULE are processed.
|
|
If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing.
|
|
|
|
This routine should be called twice in BDS.
|
|
1) The first call must be before EndOfDxe. The system capsules is processed.
|
|
If device capsule FMP protocols are exposted at this time and device FMP
|
|
capsule has zero EmbeddedDriverCount, the device capsules are processed.
|
|
Each individual capsule result is recorded in capsule record variable.
|
|
System may reset in this function, if reset is required by capsule and
|
|
all capsules are processed.
|
|
If not all capsules are processed, reset will be defered to second call.
|
|
|
|
2) The second call must be after EndOfDxe and after ConnectAll, so that all
|
|
device capsule FMP protocols are exposed.
|
|
The system capsules are skipped. If the device capsules are NOT processed
|
|
in first call, they are processed here.
|
|
Each individual capsule result is recorded in capsule record variable.
|
|
System may reset in this function, if reset is required by capsule
|
|
processed in first call and second call.
|
|
|
|
@retval EFI_SUCCESS There is no error when processing capsules.
|
|
@retval EFI_OUT_OF_RESOURCES No enough resource to process capsules.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ProcessCapsules (
|
|
VOID
|
|
)
|
|
{
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|