2019-09-03 11:58:42 +02:00
|
|
|
/** @file
|
|
|
|
DXE capsule process.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
ProcessCapsules(), ProcessTheseCapsules() will receive untrusted
|
|
|
|
input and do basic validation.
|
|
|
|
|
|
|
|
Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
#include <PiDxe.h>
|
|
|
|
#include <Protocol/EsrtManagement.h>
|
|
|
|
#include <Protocol/FirmwareManagementProgress.h>
|
|
|
|
|
|
|
|
#include <Library/BaseLib.h>
|
|
|
|
#include <Library/DebugLib.h>
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
|
|
#include <Library/MemoryAllocationLib.h>
|
|
|
|
#include <Library/UefiLib.h>
|
|
|
|
#include <Library/PcdLib.h>
|
|
|
|
#include <Library/HobLib.h>
|
|
|
|
#include <Library/ReportStatusCodeLib.h>
|
|
|
|
#include <Library/CapsuleLib.h>
|
|
|
|
#include <Library/DisplayUpdateProgressLib.h>
|
|
|
|
|
|
|
|
#include <IndustryStandard/WindowsUxCapsule.h>
|
|
|
|
|
|
|
|
extern EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL *mFmpProgress;
|
|
|
|
|
|
|
|
/**
|
|
|
|
Return if this FMP is a system FMP or a device FMP, based upon CapsuleHeader.
|
|
|
|
|
|
|
|
@param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
|
|
|
|
|
|
|
|
@retval TRUE It is a system FMP.
|
|
|
|
@retval FALSE It is a device FMP.
|
|
|
|
**/
|
|
|
|
BOOLEAN
|
|
|
|
IsFmpCapsule (
|
|
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
Validate Fmp capsules layout.
|
|
|
|
|
|
|
|
Caution: This function may receive untrusted input.
|
|
|
|
|
|
|
|
This function assumes the caller validated the capsule by using
|
|
|
|
IsValidCapsuleHeader(), so that all fields in EFI_CAPSULE_HEADER are correct.
|
|
|
|
The capsule buffer size is CapsuleHeader->CapsuleImageSize.
|
|
|
|
|
|
|
|
This function validates the fields in EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
|
|
|
|
and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.
|
|
|
|
|
|
|
|
This function need support nested FMP capsule.
|
|
|
|
|
|
|
|
@param[in] CapsuleHeader Points to a capsule header.
|
|
|
|
@param[out] EmbeddedDriverCount The EmbeddedDriverCount in the FMP capsule.
|
|
|
|
|
|
|
|
@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,
|
|
|
|
OUT UINT16 *EmbeddedDriverCount OPTIONAL
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
Validate if it is valid capsule header
|
|
|
|
|
|
|
|
This function assumes the caller provided correct CapsuleHeader pointer
|
|
|
|
and CapsuleSize.
|
|
|
|
|
|
|
|
This function validates the fields in EFI_CAPSULE_HEADER.
|
|
|
|
|
|
|
|
@param[in] CapsuleHeader Points to a capsule header.
|
|
|
|
@param[in] CapsuleSize Size of the whole capsule image.
|
|
|
|
|
|
|
|
**/
|
|
|
|
BOOLEAN
|
|
|
|
IsValidCapsuleHeader (
|
|
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader,
|
|
|
|
IN UINT64 CapsuleSize
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
Return if this capsule is a capsule name capsule, based upon CapsuleHeader.
|
|
|
|
|
|
|
|
@param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
|
|
|
|
|
|
|
|
@retval TRUE It is a capsule name capsule.
|
|
|
|
@retval FALSE It is not a capsule name capsule.
|
|
|
|
**/
|
|
|
|
BOOLEAN
|
|
|
|
IsCapsuleNameCapsule (
|
|
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
Check the integrity of the capsule name capsule.
|
|
|
|
If the capsule is vaild, return the physical address of each capsule name string.
|
|
|
|
|
|
|
|
@param[in] CapsuleHeader Pointer to the capsule header of a capsule name capsule.
|
|
|
|
@param[out] CapsuleNameNum Number of capsule name.
|
|
|
|
|
|
|
|
@retval NULL Capsule name capsule is not valid.
|
|
|
|
@retval CapsuleNameBuf Array of capsule name physical address.
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_PHYSICAL_ADDRESS *
|
|
|
|
ValidateCapsuleNameCapsuleIntegrity (
|
|
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader,
|
|
|
|
OUT UINTN *CapsuleNameNum
|
|
|
|
);
|
|
|
|
|
|
|
|
extern BOOLEAN mDxeCapsuleLibEndOfDxe;
|
|
|
|
BOOLEAN mNeedReset = FALSE;
|
|
|
|
|
|
|
|
VOID **mCapsulePtr;
|
|
|
|
CHAR16 **mCapsuleNamePtr;
|
|
|
|
EFI_STATUS *mCapsuleStatusArray;
|
|
|
|
UINT32 mCapsuleTotalNumber;
|
|
|
|
|
|
|
|
/**
|
|
|
|
The firmware implements to process the capsule image.
|
|
|
|
|
|
|
|
Caution: This function may receive untrusted input.
|
|
|
|
|
|
|
|
@param[in] CapsuleHeader Points to a capsule header.
|
|
|
|
@param[in] CapFileName Capsule file name.
|
|
|
|
@param[out] ResetRequired Indicates whether reset is required or not.
|
|
|
|
|
|
|
|
@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
|
|
|
|
ProcessThisCapsuleImage (
|
|
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader,
|
|
|
|
IN CHAR16 *CapFileName, OPTIONAL
|
|
|
|
OUT BOOLEAN *ResetRequired OPTIONAL
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
Function indicate the current completion progress of the firmware
|
|
|
|
update. Platform may override with own specific progress function.
|
|
|
|
|
|
|
|
@param[in] Completion A value between 1 and 100 indicating the current
|
|
|
|
completion progress of the firmware update
|
|
|
|
|
|
|
|
@retval EFI_SUCESS The capsule update progress was updated.
|
|
|
|
@retval EFI_INVALID_PARAMETER Completion is greater than 100%.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
UpdateImageProgress (
|
|
|
|
IN UINTN Completion
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
UINTN Seconds;
|
|
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *Color;
|
|
|
|
|
|
|
|
DEBUG((DEBUG_INFO, "Update Progress - %d%%\n", Completion));
|
|
|
|
|
|
|
|
if (Completion > 100) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Use a default timeout of 5 minutes if there is not FMP Progress Protocol.
|
|
|
|
//
|
|
|
|
Seconds = 5 * 60;
|
|
|
|
Color = NULL;
|
|
|
|
if (mFmpProgress != NULL) {
|
|
|
|
Seconds = mFmpProgress->WatchdogSeconds;
|
|
|
|
Color = &mFmpProgress->ProgressBarForegroundColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Cancel the watchdog timer
|
|
|
|
//
|
|
|
|
gBS->SetWatchdogTimer (0, 0x0000, 0, NULL);
|
|
|
|
|
|
|
|
if (Completion != 100) {
|
|
|
|
//
|
|
|
|
// Arm the watchdog timer from PCD setting
|
|
|
|
//
|
|
|
|
if (Seconds != 0) {
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "Arm watchdog timer %d seconds\n", Seconds));
|
|
|
|
gBS->SetWatchdogTimer (Seconds, 0x0000, 0, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = DisplayUpdateProgress (Completion, Color);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
This function initializes the mCapsulePtr, mCapsuleStatusArray and mCapsuleTotalNumber.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
InitCapsulePtr (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_PEI_HOB_POINTERS HobPointer;
|
|
|
|
UINTN Index;
|
|
|
|
UINTN Index2;
|
|
|
|
UINTN Index3;
|
|
|
|
UINTN CapsuleNameNumber;
|
|
|
|
UINTN CapsuleNameTotalNumber;
|
|
|
|
UINTN CapsuleNameCapsuleTotalNumber;
|
|
|
|
VOID **CapsuleNameCapsulePtr;
|
|
|
|
EFI_PHYSICAL_ADDRESS *CapsuleNameAddress;
|
|
|
|
|
|
|
|
CapsuleNameNumber = 0;
|
|
|
|
CapsuleNameTotalNumber = 0;
|
|
|
|
CapsuleNameCapsuleTotalNumber = 0;
|
|
|
|
CapsuleNameCapsulePtr = NULL;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Find all capsule images from hob
|
|
|
|
//
|
|
|
|
HobPointer.Raw = GetHobList ();
|
|
|
|
while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) {
|
|
|
|
if (!IsValidCapsuleHeader((VOID *)(UINTN)HobPointer.Capsule->BaseAddress, HobPointer.Capsule->Length)) {
|
|
|
|
HobPointer.Header->HobType = EFI_HOB_TYPE_UNUSED; // Mark this hob as invalid
|
|
|
|
} else {
|
|
|
|
if (IsCapsuleNameCapsule((VOID *)(UINTN)HobPointer.Capsule->BaseAddress)) {
|
|
|
|
CapsuleNameCapsuleTotalNumber++;
|
|
|
|
} else {
|
|
|
|
mCapsuleTotalNumber++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HobPointer.Raw = GET_NEXT_HOB (HobPointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG ((DEBUG_INFO, "mCapsuleTotalNumber - 0x%x\n", mCapsuleTotalNumber));
|
|
|
|
|
|
|
|
if (mCapsuleTotalNumber == 0) {
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Init temp Capsule Data table.
|
|
|
|
//
|
2020-04-28 12:49:24 +02:00
|
|
|
mCapsulePtr = (VOID **) AllocateZeroPool(sizeof (VOID *) * mCapsuleTotalNumber);
|
2019-09-03 11:58:42 +02:00
|
|
|
if (mCapsulePtr == NULL) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Allocate mCapsulePtr fail!\n"));
|
|
|
|
mCapsuleTotalNumber = 0;
|
|
|
|
return ;
|
|
|
|
}
|
2020-04-28 12:49:24 +02:00
|
|
|
mCapsuleStatusArray = (EFI_STATUS *) AllocateZeroPool(sizeof (EFI_STATUS) * mCapsuleTotalNumber);
|
2019-09-03 11:58:42 +02:00
|
|
|
if (mCapsuleStatusArray == NULL) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Allocate mCapsuleStatusArray fail!\n"));
|
2020-04-23 11:08:10 +02:00
|
|
|
FreePool(mCapsulePtr);
|
2019-09-03 11:58:42 +02:00
|
|
|
mCapsulePtr = NULL;
|
|
|
|
mCapsuleTotalNumber = 0;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
SetMemN (mCapsuleStatusArray, sizeof (EFI_STATUS) * mCapsuleTotalNumber, EFI_NOT_READY);
|
|
|
|
|
2020-04-28 12:49:24 +02:00
|
|
|
CapsuleNameCapsulePtr = (VOID **) AllocateZeroPool(sizeof (VOID *) * CapsuleNameCapsuleTotalNumber);
|
2019-09-03 11:58:42 +02:00
|
|
|
if (CapsuleNameCapsulePtr == NULL) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Allocate CapsuleNameCapsulePtr fail!\n"));
|
2020-04-23 11:08:10 +02:00
|
|
|
FreePool(mCapsulePtr);
|
|
|
|
FreePool(mCapsuleStatusArray);
|
2019-09-03 11:58:42 +02:00
|
|
|
mCapsulePtr = NULL;
|
|
|
|
mCapsuleStatusArray = NULL;
|
|
|
|
mCapsuleTotalNumber = 0;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Find all capsule images from hob
|
|
|
|
//
|
|
|
|
HobPointer.Raw = GetHobList ();
|
|
|
|
Index = 0;
|
|
|
|
Index2 = 0;
|
|
|
|
while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) {
|
|
|
|
if (IsCapsuleNameCapsule ((VOID *) (UINTN) HobPointer.Capsule->BaseAddress)) {
|
|
|
|
CapsuleNameCapsulePtr [Index2++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress;
|
|
|
|
} else {
|
|
|
|
mCapsulePtr [Index++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress;
|
|
|
|
}
|
|
|
|
HobPointer.Raw = GET_NEXT_HOB (HobPointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Find Capsule On Disk Names
|
|
|
|
//
|
|
|
|
for (Index = 0; Index < CapsuleNameCapsuleTotalNumber; Index ++) {
|
|
|
|
CapsuleNameAddress = ValidateCapsuleNameCapsuleIntegrity (CapsuleNameCapsulePtr[Index], &CapsuleNameNumber);
|
|
|
|
if (CapsuleNameAddress != NULL ) {
|
|
|
|
CapsuleNameTotalNumber += CapsuleNameNumber;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CapsuleNameTotalNumber == mCapsuleTotalNumber) {
|
2020-04-28 12:49:24 +02:00
|
|
|
mCapsuleNamePtr = (CHAR16 **) AllocateZeroPool(sizeof (CHAR16 *) * mCapsuleTotalNumber);
|
2019-09-03 11:58:42 +02:00
|
|
|
if (mCapsuleNamePtr == NULL) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Allocate mCapsuleNamePtr fail!\n"));
|
2020-04-23 11:08:10 +02:00
|
|
|
FreePool(mCapsulePtr);
|
|
|
|
FreePool(mCapsuleStatusArray);
|
|
|
|
FreePool(CapsuleNameCapsulePtr);
|
2019-09-03 11:58:42 +02:00
|
|
|
mCapsulePtr = NULL;
|
|
|
|
mCapsuleStatusArray = NULL;
|
|
|
|
mCapsuleTotalNumber = 0;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Index = 0, Index3 = 0; Index < CapsuleNameCapsuleTotalNumber; Index ++) {
|
|
|
|
CapsuleNameAddress = ValidateCapsuleNameCapsuleIntegrity (CapsuleNameCapsulePtr[Index], &CapsuleNameNumber);
|
|
|
|
if (CapsuleNameAddress != NULL ) {
|
|
|
|
for (Index2 = 0; Index2 < CapsuleNameNumber; Index2 ++) {
|
|
|
|
mCapsuleNamePtr[Index3 ++] = (CHAR16 *)(UINTN) CapsuleNameAddress[Index2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mCapsuleNamePtr = NULL;
|
|
|
|
}
|
|
|
|
|
2020-04-23 11:08:10 +02:00
|
|
|
FreePool(CapsuleNameCapsulePtr);
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
This function returns if all capsule images are processed.
|
|
|
|
|
|
|
|
@retval TRUE All capsule images are processed.
|
|
|
|
@retval FALSE Not all capsule images are processed.
|
|
|
|
**/
|
|
|
|
BOOLEAN
|
|
|
|
AreAllImagesProcessed (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN Index;
|
|
|
|
|
|
|
|
for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
|
|
|
|
if (mCapsuleStatusArray[Index] == EFI_NOT_READY) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
This function populates capsule in the configuration table.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
PopulateCapsuleInConfigurationTable (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
VOID **CapsulePtrCache;
|
|
|
|
EFI_GUID *CapsuleGuidCache;
|
|
|
|
EFI_CAPSULE_HEADER *CapsuleHeader;
|
|
|
|
EFI_CAPSULE_TABLE *CapsuleTable;
|
|
|
|
UINT32 CacheIndex;
|
|
|
|
UINT32 CacheNumber;
|
|
|
|
UINT32 CapsuleNumber;
|
|
|
|
UINTN Index;
|
|
|
|
UINTN Size;
|
|
|
|
EFI_STATUS Status;
|
|
|
|
|
|
|
|
if (mCapsuleTotalNumber == 0) {
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
CapsulePtrCache = NULL;
|
|
|
|
CapsuleGuidCache = NULL;
|
|
|
|
CacheIndex = 0;
|
|
|
|
CacheNumber = 0;
|
|
|
|
|
2020-04-28 12:49:24 +02:00
|
|
|
CapsulePtrCache = (VOID **) AllocateZeroPool(sizeof (VOID *) * mCapsuleTotalNumber);
|
2019-09-03 11:58:42 +02:00
|
|
|
if (CapsulePtrCache == NULL) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Allocate CapsulePtrCache fail!\n"));
|
|
|
|
return ;
|
|
|
|
}
|
2020-04-28 12:49:24 +02:00
|
|
|
CapsuleGuidCache = (EFI_GUID *) AllocateZeroPool(sizeof (EFI_GUID) * mCapsuleTotalNumber);
|
2019-09-03 11:58:42 +02:00
|
|
|
if (CapsuleGuidCache == NULL) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Allocate CapsuleGuidCache fail!\n"));
|
2020-04-23 11:08:10 +02:00
|
|
|
FreePool(CapsulePtrCache);
|
2019-09-03 11:58:42 +02:00
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Capsules who have CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE always are used for operating
|
|
|
|
// System to have information persist across a system reset. EFI System Table must
|
|
|
|
// point to an array of capsules that contains the same CapsuleGuid value. And agents
|
|
|
|
// searching for this type capsule will look in EFI System Table and search for the
|
|
|
|
// capsule's Guid and associated pointer to retrieve the data. Two steps below describes
|
|
|
|
// how to sorting the capsules by the unique guid and install the array to EFI System Table.
|
|
|
|
// Firstly, Loop for all coalesced capsules, record unique CapsuleGuids and cache them in an
|
|
|
|
// array for later sorting capsules by CapsuleGuid.
|
|
|
|
//
|
|
|
|
for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
|
|
|
|
CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
|
|
|
|
if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) {
|
|
|
|
//
|
|
|
|
// For each capsule, we compare it with known CapsuleGuid in the CacheArray.
|
|
|
|
// If already has the Guid, skip it. Whereas, record it in the CacheArray as
|
|
|
|
// an additional one.
|
|
|
|
//
|
|
|
|
CacheIndex = 0;
|
|
|
|
while (CacheIndex < CacheNumber) {
|
|
|
|
if (CompareGuid(&CapsuleGuidCache[CacheIndex],&CapsuleHeader->CapsuleGuid)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CacheIndex++;
|
|
|
|
}
|
|
|
|
if (CacheIndex == CacheNumber) {
|
|
|
|
CopyMem(&CapsuleGuidCache[CacheNumber++],&CapsuleHeader->CapsuleGuid,sizeof(EFI_GUID));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Secondly, for each unique CapsuleGuid in CacheArray, gather all coalesced capsules
|
|
|
|
// whose guid is the same as it, and malloc memory for an array which preceding
|
|
|
|
// with UINT32. The array fills with entry point of capsules that have the same
|
|
|
|
// CapsuleGuid, and UINT32 represents the size of the array of capsules. Then install
|
|
|
|
// this array into EFI System Table, so that agents searching for this type capsule
|
|
|
|
// will look in EFI System Table and search for the capsule's Guid and associated
|
|
|
|
// pointer to retrieve the data.
|
|
|
|
//
|
|
|
|
for (CacheIndex = 0; CacheIndex < CacheNumber; CacheIndex++) {
|
|
|
|
CapsuleNumber = 0;
|
|
|
|
for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
|
|
|
|
CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
|
|
|
|
if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) {
|
|
|
|
if (CompareGuid (&CapsuleGuidCache[CacheIndex], &CapsuleHeader->CapsuleGuid)) {
|
|
|
|
//
|
|
|
|
// Cache Caspuleheader to the array, this array is uniqued with certain CapsuleGuid.
|
|
|
|
//
|
|
|
|
CapsulePtrCache[CapsuleNumber++] = (VOID*)CapsuleHeader;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (CapsuleNumber != 0) {
|
|
|
|
Size = sizeof(EFI_CAPSULE_TABLE) + (CapsuleNumber - 1) * sizeof(VOID*);
|
|
|
|
CapsuleTable = AllocateRuntimePool (Size);
|
|
|
|
if (CapsuleTable == NULL) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "Allocate CapsuleTable (%g) fail!\n", &CapsuleGuidCache[CacheIndex]));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CapsuleTable->CapsuleArrayNumber = CapsuleNumber;
|
|
|
|
CopyMem(&CapsuleTable->CapsulePtr[0], CapsulePtrCache, CapsuleNumber * sizeof(VOID*));
|
|
|
|
Status = gBS->InstallConfigurationTable (&CapsuleGuidCache[CacheIndex], (VOID*)CapsuleTable);
|
2020-04-23 11:08:10 +02:00
|
|
|
if (EFI_ERROR(Status)) {
|
2019-09-03 11:58:42 +02:00
|
|
|
DEBUG ((DEBUG_ERROR, "InstallConfigurationTable (%g) fail!\n", &CapsuleGuidCache[CacheIndex]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FreePool(CapsuleGuidCache);
|
|
|
|
FreePool(CapsulePtrCache);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
This routine is called to process capsules.
|
|
|
|
|
|
|
|
Caution: This function may receive untrusted input.
|
|
|
|
|
|
|
|
Each individual capsule result is recorded in capsule record variable.
|
|
|
|
|
|
|
|
@param[in] FirstRound TRUE: First round. Need skip the FMP capsules with non zero EmbeddedDriverCount.
|
|
|
|
FALSE: Process rest FMP capsules.
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS There is no error when processing capsules.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES No enough resource to process capsules.
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
ProcessTheseCapsules (
|
|
|
|
IN BOOLEAN FirstRound
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_CAPSULE_HEADER *CapsuleHeader;
|
|
|
|
UINT32 Index;
|
|
|
|
ESRT_MANAGEMENT_PROTOCOL *EsrtManagement;
|
|
|
|
UINT16 EmbeddedDriverCount;
|
|
|
|
BOOLEAN ResetRequired;
|
|
|
|
CHAR16 *CapsuleName;
|
|
|
|
|
|
|
|
REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesBegin)));
|
|
|
|
|
|
|
|
if (FirstRound) {
|
|
|
|
InitCapsulePtr ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mCapsuleTotalNumber == 0) {
|
|
|
|
//
|
|
|
|
// We didn't find a hob, so had no errors.
|
|
|
|
//
|
|
|
|
DEBUG ((DEBUG_ERROR, "We can not find capsule data in capsule update boot mode.\n"));
|
|
|
|
mNeedReset = TRUE;
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AreAllImagesProcessed ()) {
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check the capsule flags,if contains CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE, install
|
|
|
|
// capsuleTable to configure table with EFI_CAPSULE_GUID
|
|
|
|
//
|
|
|
|
if (FirstRound) {
|
|
|
|
PopulateCapsuleInConfigurationTable ();
|
|
|
|
}
|
|
|
|
|
|
|
|
REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdatingFirmware)));
|
|
|
|
|
|
|
|
//
|
|
|
|
// If Windows UX capsule exist, process it first
|
|
|
|
//
|
|
|
|
for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
|
|
|
|
CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
|
|
|
|
CapsuleName = (mCapsuleNamePtr == NULL) ? NULL : mCapsuleNamePtr[Index];
|
|
|
|
if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {
|
|
|
|
DEBUG ((DEBUG_INFO, "ProcessThisCapsuleImage (Ux) - 0x%x\n", CapsuleHeader));
|
|
|
|
DEBUG ((DEBUG_INFO, "Display logo capsule is found.\n"));
|
|
|
|
Status = ProcessThisCapsuleImage (CapsuleHeader, CapsuleName, NULL);
|
|
|
|
mCapsuleStatusArray [Index] = EFI_SUCCESS;
|
|
|
|
DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage (Ux) - %r\n", Status));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG ((DEBUG_INFO, "Updating the firmware ......\n"));
|
|
|
|
|
|
|
|
//
|
|
|
|
// All capsules left are recognized by platform.
|
|
|
|
//
|
|
|
|
for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
|
|
|
|
if (mCapsuleStatusArray [Index] != EFI_NOT_READY) {
|
|
|
|
// already processed
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
|
|
|
|
CapsuleName = (mCapsuleNamePtr == NULL) ? NULL : mCapsuleNamePtr[Index];
|
|
|
|
if (!CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {
|
|
|
|
//
|
|
|
|
// Call capsule library to process capsule image.
|
|
|
|
//
|
|
|
|
EmbeddedDriverCount = 0;
|
|
|
|
if (IsFmpCapsule(CapsuleHeader)) {
|
|
|
|
Status = ValidateFmpCapsule (CapsuleHeader, &EmbeddedDriverCount);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
|
|
DEBUG((DEBUG_ERROR, "ValidateFmpCapsule failed. Ignore!\n"));
|
|
|
|
mCapsuleStatusArray [Index] = EFI_ABORTED;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mCapsuleStatusArray [Index] = EFI_ABORTED;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!FirstRound) || (EmbeddedDriverCount == 0)) {
|
|
|
|
DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage - 0x%x\n", CapsuleHeader));
|
|
|
|
ResetRequired = FALSE;
|
|
|
|
Status = ProcessThisCapsuleImage (CapsuleHeader, CapsuleName, &ResetRequired);
|
|
|
|
mCapsuleStatusArray [Index] = Status;
|
|
|
|
DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage - %r\n", Status));
|
|
|
|
|
|
|
|
if (Status != EFI_NOT_READY) {
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
|
|
REPORT_STATUS_CODE(EFI_ERROR_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareFailed)));
|
|
|
|
DEBUG ((DEBUG_ERROR, "Capsule process failed!\n"));
|
|
|
|
} else {
|
|
|
|
REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareSuccess)));
|
|
|
|
}
|
|
|
|
|
|
|
|
mNeedReset |= ResetRequired;
|
|
|
|
if ((CapsuleHeader->Flags & PcdGet16(PcdSystemRebootAfterCapsuleProcessFlag)) != 0) {
|
|
|
|
mNeedReset = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtManagement);
|
|
|
|
//
|
|
|
|
// Always sync ESRT Cache from FMP Instance
|
|
|
|
//
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
|
|
EsrtManagement->SyncEsrtFmp();
|
|
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
|
|
|
|
REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesEnd)));
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Do reset system.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
DoResetSystem (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
DEBUG((DEBUG_INFO, "Capsule Request Cold Reboot."));
|
|
|
|
|
|
|
|
REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeResettingSystem)));
|
|
|
|
|
|
|
|
gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
|
|
|
|
|
|
|
|
CpuDeadLoop();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
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, it means error occurs, force reset to
|
|
|
|
normal boot path.
|
|
|
|
|
|
|
|
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
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
|
|
|
|
if (!mDxeCapsuleLibEndOfDxe) {
|
|
|
|
Status = ProcessTheseCapsules(TRUE);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Reboot System if and only if all capsule processed.
|
|
|
|
// If not, defer reset to 2nd process.
|
|
|
|
//
|
|
|
|
if (mNeedReset && AreAllImagesProcessed()) {
|
|
|
|
DoResetSystem();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = ProcessTheseCapsules(FALSE);
|
|
|
|
//
|
|
|
|
// Reboot System if required after all capsule processed
|
|
|
|
//
|
|
|
|
if (mNeedReset) {
|
|
|
|
DoResetSystem();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|