/** @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. | |
// | |
mCapsulePtr = (VOID **)AllocateZeroPool (sizeof (VOID *) * mCapsuleTotalNumber); | |
if (mCapsulePtr == NULL) { | |
DEBUG ((DEBUG_ERROR, "Allocate mCapsulePtr fail!\n")); | |
mCapsuleTotalNumber = 0; | |
return; | |
} | |
mCapsuleStatusArray = (EFI_STATUS *)AllocateZeroPool (sizeof (EFI_STATUS) * mCapsuleTotalNumber); | |
if (mCapsuleStatusArray == NULL) { | |
DEBUG ((DEBUG_ERROR, "Allocate mCapsuleStatusArray fail!\n")); | |
FreePool (mCapsulePtr); | |
mCapsulePtr = NULL; | |
mCapsuleTotalNumber = 0; | |
return; | |
} | |
SetMemN (mCapsuleStatusArray, sizeof (EFI_STATUS) * mCapsuleTotalNumber, EFI_NOT_READY); | |
CapsuleNameCapsulePtr = (VOID **)AllocateZeroPool (sizeof (VOID *) * CapsuleNameCapsuleTotalNumber); | |
if (CapsuleNameCapsulePtr == NULL) { | |
DEBUG ((DEBUG_ERROR, "Allocate CapsuleNameCapsulePtr fail!\n")); | |
FreePool (mCapsulePtr); | |
FreePool (mCapsuleStatusArray); | |
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) { | |
mCapsuleNamePtr = (CHAR16 **)AllocateZeroPool (sizeof (CHAR16 *) * mCapsuleTotalNumber); | |
if (mCapsuleNamePtr == NULL) { | |
DEBUG ((DEBUG_ERROR, "Allocate mCapsuleNamePtr fail!\n")); | |
FreePool (mCapsulePtr); | |
FreePool (mCapsuleStatusArray); | |
FreePool (CapsuleNameCapsulePtr); | |
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; | |
} | |
FreePool (CapsuleNameCapsulePtr); | |
} | |
/** | |
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; | |
CapsulePtrCache = (VOID **)AllocateZeroPool (sizeof (VOID *) * mCapsuleTotalNumber); | |
if (CapsulePtrCache == NULL) { | |
DEBUG ((DEBUG_ERROR, "Allocate CapsulePtrCache fail!\n")); | |
return; | |
} | |
CapsuleGuidCache = (EFI_GUID *)AllocateZeroPool (sizeof (EFI_GUID) * mCapsuleTotalNumber); | |
if (CapsuleGuidCache == NULL) { | |
DEBUG ((DEBUG_ERROR, "Allocate CapsuleGuidCache fail!\n")); | |
FreePool (CapsulePtrCache); | |
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); | |
if (EFI_ERROR (Status)) { | |
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; | |
} |