summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c1975
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h75
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c1650
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf98
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni16
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c691
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c70
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c473
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c69
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c174
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf74
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni16
12 files changed, 5381 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c
new file mode 100644
index 00000000..6f28a92c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c
@@ -0,0 +1,1975 @@
+/** @file
+ The implementation supports Capusle on Disk.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CapsuleOnDisk.h"
+
+/**
+ 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.
+
+ This routine assumes the capsule has been validated by IsValidCapsuleHeader(), so
+ capsule memory overflow is not going to happen in this routine.
+
+ @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
+ )
+{
+ UINT8 *CapsuleNamePtr;
+ UINT8 *CapsuleNameBufStart;
+ UINT8 *CapsuleNameBufEnd;
+ UINTN Index;
+ UINTN StringSize;
+ EFI_PHYSICAL_ADDRESS *CapsuleNameBuf;
+
+ if (!IsCapsuleNameCapsule (CapsuleHeader)) {
+ return NULL;
+ }
+
+ //
+ // Total string size must be even.
+ //
+ if (((CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize) & BIT0) != 0) {
+ return NULL;
+ }
+
+ *CapsuleNameNum = 0;
+ Index = 0;
+ CapsuleNameBufStart = (UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize;
+
+ //
+ // If strings are not aligned on a 16-bit boundary, reallocate memory for it.
+ //
+ if (((UINTN) CapsuleNameBufStart & BIT0) != 0) {
+ CapsuleNameBufStart = AllocateCopyPool (CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize, CapsuleNameBufStart);
+ if (CapsuleNameBufStart == NULL) {
+ return NULL;
+ }
+ }
+
+ CapsuleNameBufEnd = CapsuleNameBufStart + CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;
+
+ CapsuleNamePtr = CapsuleNameBufStart;
+ while (CapsuleNamePtr < CapsuleNameBufEnd) {
+ StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));
+ CapsuleNamePtr += StringSize;
+ (*CapsuleNameNum) ++;
+ }
+
+ //
+ // Integrity check.
+ //
+ if (CapsuleNamePtr != CapsuleNameBufEnd) {
+ if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {
+ FreePool (CapsuleNameBufStart);
+ }
+ return NULL;
+ }
+
+ CapsuleNameBuf = AllocatePool (*CapsuleNameNum * sizeof (EFI_PHYSICAL_ADDRESS));
+ if (CapsuleNameBuf == NULL) {
+ if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {
+ FreePool (CapsuleNameBufStart);
+ }
+ return NULL;
+ }
+
+ CapsuleNamePtr = CapsuleNameBufStart;
+ while (CapsuleNamePtr < CapsuleNameBufEnd) {
+ StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));
+ CapsuleNameBuf[Index] = (EFI_PHYSICAL_ADDRESS)(UINTN) CapsuleNamePtr;
+ CapsuleNamePtr += StringSize;
+ Index ++;
+ }
+
+ return CapsuleNameBuf;
+}
+
+/**
+ This routine is called to upper case given unicode string.
+
+ @param[in] Str String to upper case
+
+ @retval upper cased string after process
+
+**/
+static
+CHAR16 *
+UpperCaseString (
+ IN CHAR16 *Str
+ )
+{
+ CHAR16 *Cptr;
+
+ for (Cptr = Str; *Cptr != L'\0'; Cptr++) {
+ if (L'a' <= *Cptr && *Cptr <= L'z') {
+ *Cptr = *Cptr - L'a' + L'A';
+ }
+ }
+
+ return Str;
+}
+
+/**
+ This routine is used to return substring before period '.' or '\0'
+ Caller should respsonsible of substr space allocation & free
+
+ @param[in] Str String to check
+ @param[out] SubStr First part of string before period or '\0'
+ @param[out] SubStrLen Length of first part of string
+
+**/
+static
+VOID
+GetSubStringBeforePeriod (
+ IN CHAR16 *Str,
+ OUT CHAR16 *SubStr,
+ OUT UINTN *SubStrLen
+ )
+{
+ UINTN Index;
+ for (Index = 0; Str[Index] != L'.' && Str[Index] != L'\0'; Index++) {
+ SubStr[Index] = Str[Index];
+ }
+
+ SubStr[Index] = L'\0';
+ *SubStrLen = Index;
+}
+
+/**
+ This routine pad the string in tail with input character.
+
+ @param[in] StrBuf Str buffer to be padded, should be enough room for
+ @param[in] PadLen Expected padding length
+ @param[in] Character Character used to pad
+
+**/
+static
+VOID
+PadStrInTail (
+ IN CHAR16 *StrBuf,
+ IN UINTN PadLen,
+ IN CHAR16 Character
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; StrBuf[Index] != L'\0'; Index++);
+
+ while(PadLen != 0) {
+ StrBuf[Index] = Character;
+ Index++;
+ PadLen--;
+ }
+
+ StrBuf[Index] = L'\0';
+}
+
+/**
+ This routine find the offset of the last period '.' of string. If No period exists
+ function FileNameExtension is set to L'\0'
+
+ @param[in] FileName File name to split between last period
+ @param[out] FileNameFirst First FileName before last period
+ @param[out] FileNameExtension FileName after last period
+
+**/
+static
+VOID
+SplitFileNameExtension (
+ IN CHAR16 *FileName,
+ OUT CHAR16 *FileNameFirst,
+ OUT CHAR16 *FileNameExtension
+ )
+{
+ UINTN Index;
+ UINTN StringLen;
+
+ StringLen = StrnLenS(FileName, MAX_FILE_NAME_SIZE);
+ for (Index = StringLen; Index > 0 && FileName[Index] != L'.'; Index--);
+
+ //
+ // No period exists. No FileName Extension
+ //
+ if (Index == 0 && FileName[Index] != L'.') {
+ FileNameExtension[0] = L'\0';
+ Index = StringLen;
+ } else {
+ StrCpyS(FileNameExtension, MAX_FILE_NAME_SIZE, &FileName[Index+1]);
+ }
+
+ //
+ // Copy First file name
+ //
+ StrnCpyS(FileNameFirst, MAX_FILE_NAME_SIZE, FileName, Index);
+ FileNameFirst[Index] = L'\0';
+}
+
+/**
+ This routine is called to get all boot options in the order determnined by:
+ 1. "OptionBuf"
+ 2. "BootOrder"
+
+ @param[out] OptionBuf BootList buffer to all boot options returned
+ @param[out] OptionCount BootList count of all boot options returned
+
+ @retval EFI_SUCCESS There is no error when processing capsule
+
+**/
+EFI_STATUS
+GetBootOptionInOrder(
+ OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf,
+ OUT UINTN *OptionCount
+ )
+{
+ EFI_STATUS Status;
+ UINTN DataSize;
+ UINT16 BootNext;
+ CHAR16 BootOptionName[20];
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOrderOptionBuf;
+ UINTN BootOrderCount;
+ EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry;
+ UINTN BootNextCount;
+ EFI_BOOT_MANAGER_LOAD_OPTION *TempBuf;
+
+ BootOrderOptionBuf = NULL;
+ TempBuf = NULL;
+ BootNextCount = 0;
+ BootOrderCount = 0;
+ *OptionBuf = NULL;
+ *OptionCount = 0;
+
+ //
+ // First Get BootOption from "BootNext"
+ //
+ DataSize = sizeof(BootNext);
+ Status = gRT->GetVariable (
+ EFI_BOOT_NEXT_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &DataSize,
+ (VOID *)&BootNext
+ );
+ //
+ // BootNext variable is a single UINT16
+ //
+ if (!EFI_ERROR(Status) && DataSize == sizeof(UINT16)) {
+ //
+ // Add the boot next boot option
+ //
+ UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootNext);
+ ZeroMem(&BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));
+ Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry);
+
+ if (!EFI_ERROR(Status)) {
+ BootNextCount = 1;
+ }
+ }
+
+ //
+ // Second get BootOption from "BootOrder"
+ //
+ BootOrderOptionBuf = EfiBootManagerGetLoadOptions (&BootOrderCount, LoadOptionTypeBoot);
+ if (BootNextCount == 0 && BootOrderCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // At least one BootOption is found
+ //
+ TempBuf = AllocatePool(sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * (BootNextCount + BootOrderCount));
+ if (TempBuf != NULL) {
+ if (BootNextCount == 1) {
+ CopyMem(TempBuf, &BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));
+ }
+
+ if (BootOrderCount > 0) {
+ CopyMem(TempBuf + BootNextCount, BootOrderOptionBuf, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * BootOrderCount);
+ }
+
+ *OptionBuf = TempBuf;
+ *OptionCount = BootNextCount + BootOrderCount;
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+ FreePool(BootOrderOptionBuf);
+
+ return Status;
+}
+
+/**
+ This routine is called to get boot option by OptionNumber.
+
+ @param[in] Number The OptionNumber of boot option
+ @param[out] OptionBuf BootList buffer to all boot options returned
+
+ @retval EFI_SUCCESS There is no error when getting boot option
+
+**/
+EFI_STATUS
+GetBootOptionByNumber(
+ IN UINT16 Number,
+ OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 BootOptionName[20];
+ EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
+
+ UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", Number);
+ ZeroMem (&BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+ Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootOption);
+
+ if (!EFI_ERROR (Status)) {
+ *OptionBuf = AllocatePool (sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+ if (*OptionBuf != NULL) {
+ CopyMem (*OptionBuf, &BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Get Active EFI System Partition within GPT based on device path.
+
+ @param[in] DevicePath Device path to find a active EFI System Partition
+ @param[out] FsHandle BootList points to all boot options returned
+
+ @retval EFI_SUCCESS Active EFI System Partition is succesfully found
+ @retval EFI_NOT_FOUND No Active EFI System Partition is found
+
+**/
+EFI_STATUS
+GetEfiSysPartitionFromDevPath(
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT EFI_HANDLE *FsHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ HARDDRIVE_DEVICE_PATH *Hd;
+ EFI_HANDLE Handle;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+
+ //
+ // Check if the device path contains GPT node
+ //
+ TempDevicePath = DevicePath;
+ while (!IsDevicePathEnd (TempDevicePath)) {
+ if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {
+ Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;
+ if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {
+ break;
+ }
+ }
+ TempDevicePath = NextDevicePathNode (TempDevicePath);
+ }
+
+ if (!IsDevicePathEnd (TempDevicePath)) {
+ //
+ // Search for EFI system partition protocol on full device path in Boot Option
+ //
+ Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);
+
+ //
+ // Search for simple file system on this handler
+ //
+ if (!EFI_ERROR(Status)) {
+ Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
+ if (!EFI_ERROR(Status)) {
+ *FsHandle = Handle;
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ This routine is called to get Simple File System protocol on the first EFI system partition found in
+ active boot option. The boot option list is detemined in order by
+ 1. "BootNext"
+ 2. "BootOrder"
+
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
+ device like USB can get enumerated.
+ @param[in, out] LoadOptionNumber On input, specify the boot option to get EFI system partition.
+ On output, return the OptionNumber of the boot option where EFI
+ system partition is got from.
+ @param[out] FsFsHandle Simple File System Protocol found on first active EFI system partition
+
+ @retval EFI_SUCCESS Simple File System protocol found for EFI system partition
+ @retval EFI_NOT_FOUND No Simple File System protocol found for EFI system partition
+
+**/
+EFI_STATUS
+GetEfiSysPartitionFromActiveBootOption(
+ IN UINTN MaxRetry,
+ IN OUT UINT16 **LoadOptionNumber,
+ OUT EFI_HANDLE *FsHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuf;
+ UINTN BootOptionNum;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
+ EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
+
+ *FsHandle = NULL;
+ CurFullPath = NULL;
+
+ if (*LoadOptionNumber != NULL) {
+ BootOptionNum = 1;
+ Status = GetBootOptionByNumber(**LoadOptionNumber, &BootOptionBuf);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status));
+ return Status;
+ }
+ } else {
+ Status = GetBootOptionInOrder(&BootOptionBuf, &BootOptionNum);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status));
+ return Status;
+ }
+ }
+
+ //
+ // Search BootOptionList to check if it is an active boot option with EFI system partition
+ // 1. Connect device path
+ // 2. expend short/plug in devicepath
+ // 3. LoadImage
+ //
+ for (Index = 0; Index < BootOptionNum; Index++) {
+ //
+ // Get the boot option from the link list
+ //
+ DevicePath = BootOptionBuf[Index].FilePath;
+
+ //
+ // Skip inactive or legacy boot options
+ //
+ if ((BootOptionBuf[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 ||
+ DevicePathType (DevicePath) == BBS_DEVICE_PATH) {
+ continue;
+ }
+
+ DEBUG_CODE (
+ CHAR16 *DevicePathStr;
+
+ DevicePathStr = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
+ if (DevicePathStr != NULL){
+ DEBUG((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr));
+ FreePool(DevicePathStr);
+ } else {
+ DEBUG((DEBUG_INFO, "DevicePathToStr failed\n"));
+ }
+ );
+
+ CurFullPath = NULL;
+ //
+ // Try every full device Path generated from bootoption
+ //
+ do {
+ PreFullPath = CurFullPath;
+ CurFullPath = EfiBootManagerGetNextLoadOptionDevicePath(DevicePath, CurFullPath);
+
+ if (PreFullPath != NULL) {
+ FreePool (PreFullPath);
+ }
+
+ if (CurFullPath == NULL) {
+ //
+ // No Active EFI system partition is found in BootOption device path
+ //
+ Status = EFI_NOT_FOUND;
+ break;
+ }
+
+ DEBUG_CODE (
+ CHAR16 *DevicePathStr1;
+
+ DevicePathStr1 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);
+ if (DevicePathStr1 != NULL){
+ DEBUG((DEBUG_INFO, "Full device path %s\n", DevicePathStr1));
+ FreePool(DevicePathStr1);
+ }
+ );
+
+ //
+ // Make sure the boot option device path connected.
+ // Only handle first device in boot option. Other optional device paths are described as OSV specific
+ // FullDevice could contain extra directory & file info. So don't check connection status here.
+ //
+ EfiBootManagerConnectDevicePath (CurFullPath, NULL);
+ Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);
+
+ //
+ // Some relocation device like USB need more time to get enumerated
+ //
+ while (EFI_ERROR(Status) && MaxRetry > 0) {
+ EfiBootManagerConnectDevicePath(CurFullPath, NULL);
+
+ //
+ // Search for EFI system partition protocol on full device path in Boot Option
+ //
+ Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);
+ if (!EFI_ERROR(Status)) {
+ break;
+ }
+ DEBUG((DEBUG_ERROR, "GetEfiSysPartitionFromDevPath Loop %x\n", Status));
+ //
+ // Stall 100ms if connection failed to ensure USB stack is ready
+ //
+ gBS->Stall(100000);
+ MaxRetry --;
+ }
+ } while(EFI_ERROR(Status));
+
+ //
+ // Find a qualified Simple File System
+ //
+ if (!EFI_ERROR(Status)) {
+ break;
+ }
+
+ }
+
+ //
+ // Return the OptionNumber of the boot option where EFI system partition is got from
+ //
+ if (*LoadOptionNumber == NULL) {
+ *LoadOptionNumber = AllocateCopyPool (sizeof(UINT16), (UINT16 *) &BootOptionBuf[Index].OptionNumber);
+ if (*LoadOptionNumber == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ //
+ // No qualified EFI system partition found
+ //
+ if (*FsHandle == NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+
+ DEBUG_CODE (
+ CHAR16 *DevicePathStr2;
+ if (*FsHandle != NULL) {
+ DevicePathStr2 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);
+ if (DevicePathStr2 != NULL){
+ DEBUG((DEBUG_INFO, "Found Active EFI System Partion on %s\n", DevicePathStr2));
+ FreePool(DevicePathStr2);
+ }
+ } else {
+ DEBUG((DEBUG_INFO, "Failed to found Active EFI System Partion\n"));
+ }
+ );
+
+ if (CurFullPath != NULL) {
+ FreePool(CurFullPath);
+ }
+
+ //
+ // Free BootOption Buffer
+ //
+ for (Index = 0; Index < BootOptionNum; Index++) {
+ if (BootOptionBuf[Index].Description != NULL) {
+ FreePool(BootOptionBuf[Index].Description);
+ }
+
+ if (BootOptionBuf[Index].FilePath != NULL) {
+ FreePool(BootOptionBuf[Index].FilePath);
+ }
+
+ if (BootOptionBuf[Index].OptionalData != NULL) {
+ FreePool(BootOptionBuf[Index].OptionalData);
+ }
+ }
+
+ FreePool(BootOptionBuf);
+
+ return Status;
+}
+
+
+/**
+ This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in
+ alphabetical order described in UEFI spec.
+
+ @param[in] Dir Directory file handler
+ @param[in] FileAttr Attribute of file to be red from directory
+ @param[out] FileInfoList File images info list red from directory
+ @param[out] FileNum File images number red from directory
+
+ @retval EFI_SUCCESS File FileInfo list in the given
+
+**/
+EFI_STATUS
+GetFileInfoListInAlphabetFromDir(
+ IN EFI_FILE_HANDLE Dir,
+ IN UINT64 FileAttr,
+ OUT LIST_ENTRY *FileInfoList,
+ OUT UINTN *FileNum
+ )
+{
+ EFI_STATUS Status;
+ FILE_INFO_ENTRY *NewFileInfoEntry;
+ FILE_INFO_ENTRY *TempFileInfoEntry;
+ EFI_FILE_INFO *FileInfo;
+ CHAR16 *NewFileName;
+ CHAR16 *ListedFileName;
+ CHAR16 *NewFileNameExtension;
+ CHAR16 *ListedFileNameExtension;
+ CHAR16 *TempNewSubStr;
+ CHAR16 *TempListedSubStr;
+ LIST_ENTRY *Link;
+ BOOLEAN NoFile;
+ UINTN FileCount;
+ UINTN IndexNew;
+ UINTN IndexListed;
+ UINTN NewSubStrLen;
+ UINTN ListedSubStrLen;
+ INTN SubStrCmpResult;
+
+ Status = EFI_SUCCESS;
+ NewFileName = NULL;
+ ListedFileName = NULL;
+ NewFileNameExtension = NULL;
+ ListedFileNameExtension = NULL;
+ TempNewSubStr = NULL;
+ TempListedSubStr = NULL;
+ FileInfo = NULL;
+ NoFile = FALSE;
+ FileCount = 0;
+
+ InitializeListHead(FileInfoList);
+
+ TempNewSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
+ TempListedSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
+
+ if (TempNewSubStr == NULL || TempListedSubStr == NULL ) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ for ( Status = FileHandleFindFirstFile(Dir, &FileInfo)
+ ; !EFI_ERROR(Status) && !NoFile
+ ; Status = FileHandleFindNextFile(Dir, FileInfo, &NoFile)
+ ){
+ if (FileInfo == NULL) {
+ goto EXIT;
+ }
+
+ //
+ // Skip file with mismatching File attribute
+ //
+ if ((FileInfo->Attribute & (FileAttr)) == 0) {
+ continue;
+ }
+
+ NewFileInfoEntry = NULL;
+ NewFileInfoEntry = (FILE_INFO_ENTRY*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY));
+ if (NewFileInfoEntry == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ NewFileInfoEntry->Signature = FILE_INFO_SIGNATURE;
+ NewFileInfoEntry->FileInfo = AllocateCopyPool((UINTN) FileInfo->Size, FileInfo);
+ if (NewFileInfoEntry->FileInfo == NULL) {
+ FreePool(NewFileInfoEntry);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ NewFileInfoEntry->FileNameFirstPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
+ if (NewFileInfoEntry->FileNameFirstPart == NULL) {
+ FreePool(NewFileInfoEntry->FileInfo);
+ FreePool(NewFileInfoEntry);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ NewFileInfoEntry->FileNameSecondPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
+ if (NewFileInfoEntry->FileNameSecondPart == NULL) {
+ FreePool(NewFileInfoEntry->FileInfo);
+ FreePool(NewFileInfoEntry->FileNameFirstPart);
+ FreePool(NewFileInfoEntry);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ //
+ // Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension
+ // If no period in the whole file name. NewFileExtension is set to L'\0'
+ //
+ NewFileName = NewFileInfoEntry->FileNameFirstPart;
+ NewFileNameExtension = NewFileInfoEntry->FileNameSecondPart;
+ SplitFileNameExtension(FileInfo->FileName, NewFileName, NewFileNameExtension);
+ UpperCaseString(NewFileName);
+ UpperCaseString(NewFileNameExtension);
+
+ //
+ // Insert capsule file in alphabetical ordered list
+ //
+ for (Link = FileInfoList->ForwardLink; Link != FileInfoList; Link = Link->ForwardLink) {
+ //
+ // Get the FileInfo from the link list
+ //
+ TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
+ ListedFileName = TempFileInfoEntry->FileNameFirstPart;
+ ListedFileNameExtension = TempFileInfoEntry->FileNameSecondPart;
+
+ //
+ // Follow rule in UEFI spec 8.5.5 to compare file name
+ //
+ IndexListed = 0;
+ IndexNew = 0;
+ while (TRUE){
+ //
+ // First compare each substrings in NewFileName & ListedFileName between periods
+ //
+ GetSubStringBeforePeriod(&NewFileName[IndexNew], TempNewSubStr, &NewSubStrLen);
+ GetSubStringBeforePeriod(&ListedFileName[IndexListed], TempListedSubStr, &ListedSubStrLen);
+ if (NewSubStrLen > ListedSubStrLen) {
+ //
+ // Substr in NewFileName is longer. Pad tail with SPACE
+ //
+ PadStrInTail(TempListedSubStr, NewSubStrLen - ListedSubStrLen, L' ');
+ } else if (NewSubStrLen < ListedSubStrLen){
+ //
+ // Substr in ListedFileName is longer. Pad tail with SPACE
+ //
+ PadStrInTail(TempNewSubStr, ListedSubStrLen - NewSubStrLen, L' ');
+ }
+
+ SubStrCmpResult = StrnCmp(TempNewSubStr, TempListedSubStr, MAX_FILE_NAME_LEN);
+ if (SubStrCmpResult != 0) {
+ break;
+ }
+
+ //
+ // Move to skip this substring
+ //
+ IndexNew += NewSubStrLen;
+ IndexListed += ListedSubStrLen;
+ //
+ // Reach File First Name end
+ //
+ if (NewFileName[IndexNew] == L'\0' || ListedFileName[IndexListed] == L'\0') {
+ break;
+ }
+
+ //
+ // Skip the period L'.'
+ //
+ IndexNew++;
+ IndexListed++;
+ }
+
+ if (SubStrCmpResult < 0) {
+ //
+ // NewFileName is smaller. Find the right place to insert New file
+ //
+ break;
+ } else if (SubStrCmpResult == 0) {
+ //
+ // 2 cases whole NewFileName is smaller than ListedFileName
+ // 1. if NewFileName == ListedFileName. Continue to compare FileNameExtension
+ // 2. if NewFileName is shorter than ListedFileName
+ //
+ if (NewFileName[IndexNew] == L'\0') {
+ if (ListedFileName[IndexListed] != L'\0' || (StrnCmp(NewFileNameExtension, ListedFileNameExtension, MAX_FILE_NAME_LEN) < 0)) {
+ break;
+ }
+ }
+ }
+
+ //
+ // Other case, ListedFileName is smaller. Continue to compare the next file in the list
+ //
+ }
+
+ //
+ // If Find an entry in the list whose name is bigger than new FileInfo in alphabet order
+ // Insert it before this entry
+ // else
+ // Insert at the tail of this list (Link = FileInfoList)
+ //
+ InsertTailList(Link, &NewFileInfoEntry->Link);
+
+ FileCount++;
+ }
+
+ *FileNum = FileCount;
+
+EXIT:
+
+ if (TempNewSubStr != NULL) {
+ FreePool(TempNewSubStr);
+ }
+
+ if (TempListedSubStr != NULL) {
+ FreePool(TempListedSubStr);
+ }
+
+ if (EFI_ERROR(Status)) {
+ while(!IsListEmpty(FileInfoList)) {
+ Link = FileInfoList->ForwardLink;
+ RemoveEntryList(Link);
+
+ TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
+
+ FreePool(TempFileInfoEntry->FileInfo);
+ FreePool(TempFileInfoEntry->FileNameFirstPart);
+ FreePool(TempFileInfoEntry->FileNameSecondPart);
+ FreePool(TempFileInfoEntry);
+ }
+ *FileNum = 0;
+ }
+
+ return Status;
+}
+
+
+/**
+ This routine is called to get all qualified image from file from an given directory
+ in alphabetic order. All the file image is copied to allocated boottime memory.
+ Caller should free these memory
+
+ @param[in] Dir Directory file handler
+ @param[in] FileAttr Attribute of file to be red from directory
+ @param[out] FilePtr File images Info buffer red from directory
+ @param[out] FileNum File images number red from directory
+
+ @retval EFI_SUCCESS Succeed to get all capsules in alphabetic order.
+
+**/
+EFI_STATUS
+GetFileImageInAlphabetFromDir(
+ IN EFI_FILE_HANDLE Dir,
+ IN UINT64 FileAttr,
+ OUT IMAGE_INFO **FilePtr,
+ OUT UINTN *FileNum
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Link;
+ EFI_FILE_HANDLE FileHandle;
+ FILE_INFO_ENTRY *FileInfoEntry;
+ EFI_FILE_INFO *FileInfo;
+ UINTN FileCount;
+ IMAGE_INFO *TempFilePtrBuf;
+ UINTN Size;
+ LIST_ENTRY FileInfoList;
+
+ FileHandle = NULL;
+ FileCount = 0;
+ TempFilePtrBuf = NULL;
+ *FilePtr = NULL;
+
+ //
+ // Get file list in Dir in alphabetical order
+ //
+ Status = GetFileInfoListInAlphabetFromDir(
+ Dir,
+ FileAttr,
+ &FileInfoList,
+ &FileCount
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));
+ goto EXIT;
+ }
+
+ if (FileCount == 0) {
+ DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));
+ Status = EFI_NOT_FOUND;
+ goto EXIT;
+ }
+
+ TempFilePtrBuf = (IMAGE_INFO *)AllocateZeroPool(sizeof(IMAGE_INFO) * FileCount);
+ if (TempFilePtrBuf == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ //
+ // Read all files from FileInfoList to BS memory
+ //
+ FileCount = 0;
+ for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {
+ //
+ // Get FileInfo from the link list
+ //
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
+ FileInfo = FileInfoEntry->FileInfo;
+
+ Status = Dir->Open(
+ Dir,
+ &FileHandle,
+ FileInfo->FileName,
+ EFI_FILE_MODE_READ,
+ 0
+ );
+ if (EFI_ERROR(Status)){
+ continue;
+ }
+
+ Size = (UINTN)FileInfo->FileSize;
+ TempFilePtrBuf[FileCount].ImageAddress = AllocateZeroPool(Size);
+ if (TempFilePtrBuf[FileCount].ImageAddress == NULL) {
+ DEBUG((DEBUG_ERROR, "Fail to allocate memory for capsule. Stop processing the rest.\n"));
+ break;
+ }
+
+ Status = FileHandle->Read(
+ FileHandle,
+ &Size,
+ TempFilePtrBuf[FileCount].ImageAddress
+ );
+
+ FileHandle->Close(FileHandle);
+
+ //
+ // Skip read error file
+ //
+ if (EFI_ERROR(Status) || Size != (UINTN)FileInfo->FileSize) {
+ //
+ // Remove this error file info accordingly
+ // & move Link to BackLink
+ //
+ Link = RemoveEntryList(Link);
+ Link = Link->BackLink;
+
+ FreePool(FileInfoEntry->FileInfo);
+ FreePool(FileInfoEntry->FileNameFirstPart);
+ FreePool(FileInfoEntry->FileNameSecondPart);
+ FreePool(FileInfoEntry);
+
+ FreePool(TempFilePtrBuf[FileCount].ImageAddress);
+ TempFilePtrBuf[FileCount].ImageAddress = NULL;
+ TempFilePtrBuf[FileCount].FileInfo = NULL;
+
+ continue;
+ }
+ TempFilePtrBuf[FileCount].FileInfo = FileInfo;
+ FileCount++;
+ }
+
+ DEBUG_CODE (
+ for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
+ FileInfo = FileInfoEntry->FileInfo;
+ DEBUG((DEBUG_INFO, "Successfully read capsule file %s from disk.\n", FileInfo->FileName));
+ }
+ );
+
+EXIT:
+
+ *FilePtr = TempFilePtrBuf;
+ *FileNum = FileCount;
+
+ //
+ // FileInfo will be freed by Calller
+ //
+ while(!IsListEmpty(&FileInfoList)) {
+ Link = FileInfoList.ForwardLink;
+ RemoveEntryList(Link);
+
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
+
+ FreePool(FileInfoEntry->FileNameFirstPart);
+ FreePool(FileInfoEntry->FileNameSecondPart);
+ FreePool(FileInfoEntry);
+ }
+
+ return Status;
+}
+
+/**
+ This routine is called to remove all qualified image from file from an given directory.
+
+ @param[in] Dir Directory file handler
+ @param[in] FileAttr Attribute of files to be deleted
+
+ @retval EFI_SUCCESS Succeed to remove all files from an given directory.
+
+**/
+EFI_STATUS
+RemoveFileFromDir(
+ IN EFI_FILE_HANDLE Dir,
+ IN UINT64 FileAttr
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Link;
+ LIST_ENTRY FileInfoList;
+ EFI_FILE_HANDLE FileHandle;
+ FILE_INFO_ENTRY *FileInfoEntry;
+ EFI_FILE_INFO *FileInfo;
+ UINTN FileCount;
+
+ FileHandle = NULL;
+
+ //
+ // Get file list in Dir in alphabetical order
+ //
+ Status = GetFileInfoListInAlphabetFromDir(
+ Dir,
+ FileAttr,
+ &FileInfoList,
+ &FileCount
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));
+ goto EXIT;
+ }
+
+ if (FileCount == 0) {
+ DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));
+ Status = EFI_NOT_FOUND;
+ goto EXIT;
+ }
+
+ //
+ // Delete all files with given attribute in Dir
+ //
+ for (Link = FileInfoList.ForwardLink; Link != &(FileInfoList); Link = Link->ForwardLink) {
+ //
+ // Get FileInfo from the link list
+ //
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
+ FileInfo = FileInfoEntry->FileInfo;
+
+ Status = Dir->Open(
+ Dir,
+ &FileHandle,
+ FileInfo->FileName,
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
+ 0
+ );
+ if (EFI_ERROR(Status)){
+ continue;
+ }
+
+ Status = FileHandle->Delete(FileHandle);
+ }
+
+EXIT:
+
+ while(!IsListEmpty(&FileInfoList)) {
+ Link = FileInfoList.ForwardLink;
+ RemoveEntryList(Link);
+
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
+
+ FreePool(FileInfoEntry->FileInfo);
+ FreePool(FileInfoEntry);
+ }
+
+ return Status;
+}
+
+/**
+ This routine is called to get all caspules from file. The capsule file image is
+ copied to BS memory. Caller is responsible to free them.
+
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
+ devices like USB can get enumerated.
+ @param[out] CapsulePtr Copied Capsule file Image Info buffer
+ @param[out] CapsuleNum CapsuleNumber
+ @param[out] FsHandle File system handle
+ @param[out] LoadOptionNumber OptionNumber of boot option
+
+ @retval EFI_SUCCESS Succeed to get all capsules.
+
+**/
+EFI_STATUS
+GetAllCapsuleOnDisk(
+ IN UINTN MaxRetry,
+ OUT IMAGE_INFO **CapsulePtr,
+ OUT UINTN *CapsuleNum,
+ OUT EFI_HANDLE *FsHandle,
+ OUT UINT16 *LoadOptionNumber
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+ EFI_FILE_HANDLE RootDir;
+ EFI_FILE_HANDLE FileDir;
+ UINT16 *TempOptionNumber;
+
+ TempOptionNumber = NULL;
+ *CapsuleNum = 0;
+
+ Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &TempOptionNumber, FsHandle);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Status = gBS->HandleProtocol(*FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Status = Fs->OpenVolume(Fs, &RootDir);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Status = RootDir->Open(
+ RootDir,
+ &FileDir,
+ EFI_CAPSULE_FILE_DIRECTORY,
+ EFI_FILE_MODE_READ,
+ 0
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG((DEBUG_ERROR, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));
+ RootDir->Close (RootDir);
+ return Status;
+ }
+ RootDir->Close (RootDir);
+
+ //
+ // Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute
+ // ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY
+ //
+ Status = GetFileImageInAlphabetFromDir(
+ FileDir,
+ EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE,
+ CapsulePtr,
+ CapsuleNum
+ );
+ DEBUG((DEBUG_INFO, "GetFileImageInAlphabetFromDir status %x\n", Status));
+
+ //
+ // Always remove file to avoid deadloop in capsule process
+ //
+ Status = RemoveFileFromDir(FileDir, EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE);
+ DEBUG((DEBUG_INFO, "RemoveFileFromDir status %x\n", Status));
+
+ FileDir->Close (FileDir);
+
+ if (LoadOptionNumber != NULL) {
+ *LoadOptionNumber = *TempOptionNumber;
+ }
+
+ return Status;
+}
+
+/**
+ Build Gather list for a list of capsule images.
+
+ @param[in] CapsuleBuffer An array of pointer to capsule images
+ @param[in] CapsuleSize An array of UINTN to capsule images size
+ @param[in] CapsuleNum The count of capsule images
+ @param[out] BlockDescriptors The block descriptors for the capsule images
+
+ @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.
+
+**/
+EFI_STATUS
+BuildGatherList (
+ IN VOID **CapsuleBuffer,
+ IN UINTN *CapsuleSize,
+ IN UINTN CapsuleNum,
+ OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors
+ )
+{
+ EFI_STATUS Status;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader;
+ UINTN Index;
+
+ BlockDescriptors1 = NULL;
+ BlockDescriptorPre = NULL;
+ BlockDescriptorsHeader = NULL;
+
+ for (Index = 0; Index < CapsuleNum; Index++) {
+ //
+ // Allocate memory for the descriptors.
+ //
+ BlockDescriptors1 = AllocateZeroPool (2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR));
+ if (BlockDescriptors1 == NULL) {
+ DEBUG ((DEBUG_ERROR, "BuildGatherList: failed to allocate memory for descriptors\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERREXIT;
+ } else {
+ DEBUG ((DEBUG_INFO, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1));
+ }
+
+ //
+ // Record descirptor header
+ //
+ if (Index == 0) {
+ BlockDescriptorsHeader = BlockDescriptors1;
+ }
+
+ if (BlockDescriptorPre != NULL) {
+ BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;
+ BlockDescriptorPre->Length = 0;
+ }
+
+ BlockDescriptors1->Union.DataBlock = (UINTN) CapsuleBuffer[Index];
+ BlockDescriptors1->Length = CapsuleSize[Index];
+
+ BlockDescriptorPre = BlockDescriptors1 + 1;
+ BlockDescriptors1 = NULL;
+ }
+
+ //
+ // Null-terminate.
+ //
+ if (BlockDescriptorPre != NULL) {
+ BlockDescriptorPre->Union.ContinuationPointer = (UINTN)NULL;
+ BlockDescriptorPre->Length = 0;
+ *BlockDescriptors = BlockDescriptorsHeader;
+ }
+
+ return EFI_SUCCESS;
+
+ERREXIT:
+ if (BlockDescriptors1 != NULL) {
+ FreePool (BlockDescriptors1);
+ }
+
+ return Status;
+}
+
+/**
+ This routine is called to check if CapsuleOnDisk flag in OsIndications Variable
+ is enabled.
+
+ @retval TRUE Flag is enabled
+ @retval FALSE Flag is not enabled
+
+**/
+BOOLEAN
+EFIAPI
+CoDCheckCapsuleOnDiskFlag(
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT64 OsIndication;
+ UINTN DataSize;
+
+ //
+ // Check File Capsule Delivery Supported Flag in OsIndication variable
+ //
+ OsIndication = 0;
+ DataSize = sizeof(UINT64);
+ Status = gRT->GetVariable (
+ EFI_OS_INDICATIONS_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &DataSize,
+ &OsIndication
+ );
+ if (!EFI_ERROR(Status) &&
+ (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.
+
+ @retval EFI_SUCCESS All Capsule On Disk flags are cleared
+
+**/
+EFI_STATUS
+EFIAPI
+CoDClearCapsuleOnDiskFlag(
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT64 OsIndication;
+ UINTN DataSize;
+
+ //
+ // Reset File Capsule Delivery Supported Flag in OsIndication variable
+ //
+ OsIndication = 0;
+ DataSize = sizeof(UINT64);
+ Status = gRT->GetVariable (
+ EFI_OS_INDICATIONS_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ NULL,
+ &DataSize,
+ &OsIndication
+ );
+ if (EFI_ERROR(Status) ||
+ (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) {
+ return Status;
+ }
+
+ OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);
+ Status = gRT->SetVariable (
+ EFI_OS_INDICATIONS_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ sizeof(UINT64),
+ &OsIndication
+ );
+ ASSERT(!EFI_ERROR(Status));
+
+ //
+ // Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable
+ //
+ Status = gRT->SetVariable (
+ EFI_BOOT_NEXT_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ 0,
+ 0,
+ NULL
+ );
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This routine is called to clear CapsuleOnDisk Relocation Info variable.
+ Total Capsule On Disk length is recorded in this variable
+
+ @retval EFI_SUCCESS Capsule On Disk flags are cleared
+
+**/
+EFI_STATUS
+CoDClearCapsuleRelocationInfo(
+ VOID
+ )
+{
+ return gRT->SetVariable (
+ COD_RELOCATION_INFO_VAR_NAME,
+ &gEfiCapsuleVendorGuid,
+ 0,
+ 0,
+ NULL
+ );
+}
+
+/**
+ Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device
+ with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must
+ be a full device path.
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
+ Function will stall 100ms between each retry.
+
+ Side Effects:
+ Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems
+ of the relocation device will be corrupted.
+
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
+ devices like USB can get enumerated.
+
+ @retval EFI_SUCCESS Capsule on Disk images are sucessfully relocated to the platform-specific device.
+
+**/
+EFI_STATUS
+RelocateCapsuleToDisk(
+ UINTN MaxRetry
+ )
+{
+ EFI_STATUS Status;
+ UINTN CapsuleOnDiskNum;
+ UINTN Index;
+ UINTN DataSize;
+ UINT64 TotalImageSize;
+ UINT64 TotalImageNameSize;
+ IMAGE_INFO *CapsuleOnDiskBuf;
+ EFI_HANDLE Handle;
+ EFI_HANDLE TempHandle;
+ EFI_HANDLE *HandleBuffer;
+ UINTN NumberOfHandles;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ UINT8 *CapsuleDataBuf;
+ UINT8 *CapsulePtr;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+ EFI_FILE_HANDLE RootDir;
+ EFI_FILE_HANDLE TempCodFile;
+ UINT64 TempCodFileSize;
+ EFI_DEVICE_PATH *TempDevicePath;
+ BOOLEAN RelocationInfo;
+ UINT16 LoadOptionNumber;
+ EFI_CAPSULE_HEADER FileNameCapsuleHeader;
+
+ RootDir = NULL;
+ TempCodFile = NULL;
+ HandleBuffer = NULL;
+ CapsuleDataBuf = NULL;
+ CapsuleOnDiskBuf = NULL;
+ NumberOfHandles = 0;
+
+ DEBUG ((DEBUG_INFO, "CapsuleOnDisk RelocateCapsule Enter\n"));
+
+ //
+ // 1. Load all Capsule On Disks in to memory
+ //
+ Status = GetAllCapsuleOnDisk(MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, &LoadOptionNumber);
+ if (EFI_ERROR(Status) || CapsuleOnDiskNum == 0 || CapsuleOnDiskBuf == NULL) {
+ DEBUG ((DEBUG_INFO, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status));
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // 2. Connect platform special device path as relocation device.
+ // If no platform special device path specified or the device path is invalid, use the EFI system partition where
+ // stores the capsules as relocation device.
+ //
+ if (IsDevicePathValid ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), PcdGetSize(PcdCodRelocationDevPath))) {
+ Status = EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), &TempHandle);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status));
+ goto EXIT;
+ }
+
+ //
+ // Connect all the child handle. Partition & FAT drivers are allowed in this case
+ //
+ gBS->ConnectController (TempHandle, NULL, NULL, TRUE);
+ Status = gBS->LocateHandleBuffer(
+ ByProtocol,
+ &gEfiSimpleFileSystemProtocolGuid,
+ NULL,
+ &NumberOfHandles,
+ &HandleBuffer
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status));
+ goto EXIT;
+ }
+
+ //
+ // Find first Simple File System Handle which can match PcdCodRelocationDevPath
+ //
+ for (Index = 0; Index < NumberOfHandles; Index++) {
+ Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&TempDevicePath);
+ if (EFI_ERROR(Status)) {
+ continue;
+ }
+
+ DataSize = GetDevicePathSize((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath)) - sizeof(EFI_DEVICE_PATH);
+ if (0 == CompareMem((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), TempDevicePath, DataSize)) {
+ Handle = HandleBuffer[Index];
+ break;
+ }
+ }
+
+ FreePool(HandleBuffer);
+
+ if (Index == NumberOfHandles) {
+ DEBUG ((DEBUG_ERROR, "RelocateCapsule: No simple file system protocol found.\n"));
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ Status = gBS->HandleProtocol(Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
+ if (EFI_ERROR(Status) || BlockIo->Media->ReadOnly) {
+ DEBUG((DEBUG_ERROR, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));
+ goto EXIT;
+ }
+
+ Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
+ if (EFI_ERROR(Status)) {
+ goto EXIT;
+ }
+
+ //
+ // Check if device used to relocate Capsule On Disk is big enough
+ //
+ TotalImageSize = 0;
+ TotalImageNameSize = 0;
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++) {
+ //
+ // Overflow check
+ //
+ if (MAX_ADDRESS - (UINTN)TotalImageSize <= CapsuleOnDiskBuf[Index].FileInfo->FileSize) {
+ Status = EFI_INVALID_PARAMETER;
+ goto EXIT;
+ }
+
+ if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto EXIT;
+ }
+
+ TotalImageSize += CapsuleOnDiskBuf[Index].FileInfo->FileSize;
+ TotalImageNameSize += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);
+ DEBUG((DEBUG_INFO, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf[Index].FileInfo->FileName, CapsuleOnDiskBuf[Index].FileInfo->FileSize));
+ }
+
+ DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize));
+ DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize));
+
+ if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= sizeof(UINT64) * 2 ||
+ MAX_ADDRESS - (UINTN)TotalImageSize <= (UINTN)TotalImageNameSize + sizeof(UINT64) * 2) {
+ Status = EFI_INVALID_PARAMETER;
+ goto EXIT;
+ }
+
+ TempCodFileSize = sizeof(UINT64) + TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;
+
+ //
+ // Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly
+ //
+ if (DivU64x32(TempCodFileSize, BlockIo->Media->BlockSize) > BlockIo->Media->LastBlock) {
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));
+ DEBUG((DEBUG_ERROR, "TotalImageSize = %x\n", TotalImageSize));
+ DEBUG((DEBUG_ERROR, "TotalImageNameSize = %x\n", TotalImageNameSize));
+ DEBUG((DEBUG_ERROR, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo->Media->BlockSize, BlockIo->Media->LastBlock));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ CapsuleDataBuf = AllocatePool((UINTN) TempCodFileSize);
+ if (CapsuleDataBuf == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ //
+ // First UINT64 reserved for total image size, including capsule name capsule.
+ //
+ *(UINT64 *) CapsuleDataBuf = TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;
+
+ //
+ // Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write
+ //
+ for (Index = 0, CapsulePtr = CapsuleDataBuf + sizeof(UINT64); Index < CapsuleOnDiskNum; Index++) {
+ CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].ImageAddress, (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize);
+ CapsulePtr += CapsuleOnDiskBuf[Index].FileInfo->FileSize;
+ }
+
+ //
+ // Line the capsule header for capsule name capsule.
+ //
+ CopyGuid(&FileNameCapsuleHeader.CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid);
+ FileNameCapsuleHeader.CapsuleImageSize = (UINT32) TotalImageNameSize + sizeof(EFI_CAPSULE_HEADER);
+ FileNameCapsuleHeader.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
+ FileNameCapsuleHeader.HeaderSize = sizeof(EFI_CAPSULE_HEADER);
+ CopyMem(CapsulePtr, &FileNameCapsuleHeader, FileNameCapsuleHeader.HeaderSize);
+ CapsulePtr += FileNameCapsuleHeader.HeaderSize;
+
+ //
+ // Line up all the Capsule file names.
+ //
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++) {
+ CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].FileInfo->FileName, StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName));
+ CapsulePtr += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);
+ }
+
+ //
+ // 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir
+ //
+ Status = Fs->OpenVolume(Fs, &RootDir);
+ if (EFI_ERROR(Status)) {
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: OpenVolume error. %x\n", Status));
+ goto EXIT;
+ }
+
+ Status = RootDir->Open(
+ RootDir,
+ &TempCodFile,
+ (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
+ 0
+ );
+ if (!EFI_ERROR(Status)) {
+ //
+ // Error handling code to prevent malicious code to hold this file to block capsule on disk
+ //
+ TempCodFile->Delete(TempCodFile);
+ }
+ Status = RootDir->Open(
+ RootDir,
+ &TempCodFile,
+ (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
+ 0
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status));
+ goto EXIT;
+ }
+
+ //
+ // Always write at the begining of TempCap file
+ //
+ DataSize = (UINTN) TempCodFileSize;
+ Status = TempCodFile->Write(
+ TempCodFile,
+ &DataSize,
+ CapsuleDataBuf
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status));
+ goto EXIT;
+ }
+
+ if (DataSize != TempCodFileSize) {
+ Status = EFI_DEVICE_ERROR;
+ goto EXIT;
+ }
+
+ //
+ // Save Capsule On Disk relocation info to "CodRelocationInfo" Var
+ // It is used in next reboot by TCB
+ //
+ RelocationInfo = TRUE;
+ Status = gRT->SetVariable(
+ COD_RELOCATION_INFO_VAR_NAME,
+ &gEfiCapsuleVendorGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ sizeof (BOOLEAN),
+ &RelocationInfo
+ );
+ //
+ // Save the LoadOptionNumber of the boot option, where the capsule is relocated,
+ // into "CodRelocationLoadOption" var. It is used in next reboot after capsule is
+ // updated out of TCB to remove the TempCoDFile.
+ //
+ Status = gRT->SetVariable(
+ COD_RELOCATION_LOAD_OPTION_VAR_NAME,
+ &gEfiCapsuleVendorGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ sizeof (UINT16),
+ &LoadOptionNumber
+ );
+
+EXIT:
+
+ if (CapsuleDataBuf != NULL) {
+ FreePool(CapsuleDataBuf);
+ }
+
+ if (CapsuleOnDiskBuf != NULL) {
+ //
+ // Free resources allocated by CodLibGetAllCapsuleOnDisk
+ //
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++ ) {
+ FreePool(CapsuleOnDiskBuf[Index].ImageAddress);
+ FreePool(CapsuleOnDiskBuf[Index].FileInfo);
+ }
+ FreePool(CapsuleOnDiskBuf);
+ }
+
+ if (TempCodFile != NULL) {
+ if (EFI_ERROR(Status)) {
+ TempCodFile->Delete (TempCodFile);
+ } else {
+ TempCodFile->Close (TempCodFile);
+ }
+ }
+
+ if (RootDir != NULL) {
+ RootDir->Close (RootDir);
+ }
+
+ return Status;
+}
+
+/**
+ For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.
+ Relocate Capsule On Disk to memory and call UpdateCapsule().
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
+ Function will stall 100ms between each retry.
+
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
+ devices like USB can get enumerated.
+
+ @retval EFI_SUCCESS Deliver capsule through Capsule In Ram successfully.
+
+**/
+EFI_STATUS
+RelocateCapsuleToRam (
+ UINTN MaxRetry
+ )
+{
+ EFI_STATUS Status;
+ UINTN CapsuleOnDiskNum;
+ IMAGE_INFO *CapsuleOnDiskBuf;
+ EFI_HANDLE Handle;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;
+ VOID **CapsuleBuffer;
+ UINTN *CapsuleSize;
+ EFI_CAPSULE_HEADER *FileNameCapsule;
+ UINTN Index;
+ UINT8 *StringBuf;
+ UINTN StringSize;
+ UINTN TotalStringSize;
+
+ CapsuleOnDiskBuf = NULL;
+ BlockDescriptors = NULL;
+ CapsuleBuffer = NULL;
+ CapsuleSize = NULL;
+ FileNameCapsule = NULL;
+ TotalStringSize = 0;
+
+ //
+ // 1. Load all Capsule On Disks into memory
+ //
+ Status = GetAllCapsuleOnDisk (MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, NULL);
+ if (EFI_ERROR (Status) || CapsuleOnDiskNum == 0 || CapsuleOnDiskBuf == NULL) {
+ DEBUG ((DEBUG_ERROR, "GetAllCapsuleOnDisk Status - 0x%x\n", Status));
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // 2. Add a capsule for Capsule file name strings
+ //
+ CapsuleBuffer = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (VOID *));
+ if (CapsuleBuffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CapsuleSize = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (UINTN));
+ if (CapsuleSize == NULL) {
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));
+ FreePool (CapsuleBuffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++) {
+ CapsuleBuffer[Index] = (VOID *)(UINTN) CapsuleOnDiskBuf[Index].ImageAddress;
+ CapsuleSize[Index] = (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize;
+ TotalStringSize += StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);
+ }
+
+ FileNameCapsule = AllocateZeroPool (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);
+ if (FileNameCapsule == NULL) {
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory for name capsule.\n"));
+ FreePool (CapsuleBuffer);
+ FreePool (CapsuleSize);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ FileNameCapsule->CapsuleImageSize = (UINT32) (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);
+ FileNameCapsule->Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
+ FileNameCapsule->HeaderSize = sizeof (EFI_CAPSULE_HEADER);
+ CopyGuid (&(FileNameCapsule->CapsuleGuid), &gEdkiiCapsuleOnDiskNameGuid);
+
+ StringBuf = (UINT8 *)FileNameCapsule + FileNameCapsule->HeaderSize;
+ for (Index = 0; Index < CapsuleOnDiskNum; Index ++) {
+ StringSize = StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);
+ CopyMem (StringBuf, CapsuleOnDiskBuf[Index].FileInfo->FileName, StringSize);
+ StringBuf += StringSize;
+ }
+
+ CapsuleBuffer[CapsuleOnDiskNum] = FileNameCapsule;
+ CapsuleSize[CapsuleOnDiskNum] = TotalStringSize + sizeof (EFI_CAPSULE_HEADER);
+
+ //
+ // 3. Build Gather list for the capsules
+ //
+ Status = BuildGatherList (CapsuleBuffer, CapsuleSize, CapsuleOnDiskNum + 1, &BlockDescriptors);
+ if (EFI_ERROR (Status) || BlockDescriptors == NULL) {
+ FreePool (CapsuleBuffer);
+ FreePool (CapsuleSize);
+ FreePool (FileNameCapsule);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // 4. Call UpdateCapsule() service
+ //
+ Status = gRT->UpdateCapsule((EFI_CAPSULE_HEADER **) CapsuleBuffer, CapsuleOnDiskNum + 1, (UINTN) BlockDescriptors);
+
+ return Status;
+}
+
+/**
+ Relocate Capsule on Disk from EFI system partition.
+
+ Two solution to deliver Capsule On Disk:
+ Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().
+ Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage
+ device with BlockIo protocol.
+
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
+ Function will stall 100ms between each retry.
+
+ Side Effects:
+ Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.
+ Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file
+ systems of the relocation device will be corrupted.
+
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
+ devices like USB can get enumerated. Input 0 means no retry.
+
+ @retval EFI_SUCCESS Capsule on Disk images are successfully relocated.
+
+**/
+EFI_STATUS
+EFIAPI
+CoDRelocateCapsule(
+ UINTN MaxRetry
+ )
+{
+ if (!PcdGetBool (PcdCapsuleOnDiskSupport)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Clear CapsuleOnDisk Flag firstly.
+ //
+ CoDClearCapsuleOnDiskFlag ();
+
+ //
+ // If Capsule In Ram is supported, delivery capsules through memory
+ //
+ if (PcdGetBool (PcdCapsuleInRamSupport)) {
+ DEBUG ((DEBUG_INFO, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));
+ return RelocateCapsuleToRam (MaxRetry);
+ } else {
+ DEBUG ((DEBUG_INFO, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName)));
+ return RelocateCapsuleToDisk (MaxRetry);
+ }
+}
+
+/**
+ Remove the temp file from the root of EFI System Partition.
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
+ Function will stall 100ms between each retry.
+
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
+ devices like USB can get enumerated. Input 0 means no retry.
+
+ @retval EFI_SUCCESS Remove the temp file successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+CoDRemoveTempFile (
+ UINTN MaxRetry
+ )
+{
+ EFI_STATUS Status;
+ UINTN DataSize;
+ UINT16 *LoadOptionNumber;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+ EFI_HANDLE FsHandle;
+ EFI_FILE_HANDLE RootDir;
+ EFI_FILE_HANDLE TempCodFile;
+
+ RootDir = NULL;
+ TempCodFile = NULL;
+ DataSize = sizeof(UINT16);
+
+ LoadOptionNumber = AllocatePool (sizeof(UINT16));
+ if (LoadOptionNumber == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Check if capsule files are relocated
+ //
+ Status = gRT->GetVariable (
+ COD_RELOCATION_LOAD_OPTION_VAR_NAME,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &DataSize,
+ (VOID *)LoadOptionNumber
+ );
+ if (EFI_ERROR(Status) || DataSize != sizeof(UINT16)) {
+ goto EXIT;
+ }
+
+ //
+ // Get the EFI file system from the boot option where the capsules are relocated
+ //
+ Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &LoadOptionNumber, &FsHandle);
+ if (EFI_ERROR(Status)) {
+ goto EXIT;
+ }
+
+ Status = gBS->HandleProtocol(FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
+ if (EFI_ERROR(Status)) {
+ goto EXIT;
+ }
+
+ Status = Fs->OpenVolume(Fs, &RootDir);
+ if (EFI_ERROR(Status)) {
+ goto EXIT;
+ }
+
+ //
+ // Delete the TempCoDFile
+ //
+ Status = RootDir->Open(
+ RootDir,
+ &TempCodFile,
+ (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
+ 0
+ );
+ if (EFI_ERROR(Status)) {
+ goto EXIT;
+ }
+
+ TempCodFile->Delete(TempCodFile);
+
+ //
+ // Clear "CoDRelocationLoadOption" variable
+ //
+ Status = gRT->SetVariable (
+ COD_RELOCATION_LOAD_OPTION_VAR_NAME,
+ &gEfiCapsuleVendorGuid,
+ 0,
+ 0,
+ NULL
+ );
+
+EXIT:
+ if (LoadOptionNumber != NULL) {
+ FreePool (LoadOptionNumber);
+ }
+
+ if (RootDir != NULL) {
+ RootDir->Close(RootDir);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h
new file mode 100644
index 00000000..9d9aa158
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h
@@ -0,0 +1,75 @@
+/** @file
+ Defines several datastructures used by Capsule On Disk feature.
+ They are mainly used for FAT files.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _CAPSULES_ON_DISK_H_
+#define _CAPSULES_ON_DISK_H_
+
+#include <Uefi.h>
+#include <Pi/PiMultiPhase.h>
+
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/CapsuleLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootManagerLib.h>
+
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/DiskIo.h>
+#include <Protocol/BlockIo.h>
+
+#include <Guid/CapsuleVendor.h>
+#include <Guid/FileInfo.h>
+#include <Guid/GlobalVariable.h>
+
+//
+// This data structure is the part of FILE_INFO_ENTRY
+//
+#define FILE_INFO_SIGNATURE SIGNATURE_32 ('F', 'L', 'I', 'F')
+
+//
+// LoadOptionNumber of the boot option where the capsules is relocated.
+//
+#define COD_RELOCATION_LOAD_OPTION_VAR_NAME L"CodRelocationLoadOption"
+
+//
+// (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes)
+//
+#define MAX_FILE_NAME_SIZE 522
+#define MAX_FILE_NAME_LEN (MAX_FILE_NAME_SIZE / sizeof(CHAR16))
+#define MAX_FILE_INFO_LEN (OFFSET_OF(EFI_FILE_INFO, FileName) + MAX_FILE_NAME_LEN)
+
+typedef struct {
+ UINTN Signature;
+ LIST_ENTRY Link; /// Linked list members.
+ EFI_FILE_INFO *FileInfo; /// Pointer to the FileInfo struct for this file or NULL.
+ CHAR16 *FileNameFirstPart; /// Text to the left of right-most period in the file name. String is capitialized
+ CHAR16 *FileNameSecondPart; /// Text to the right of right-most period in the file name.String is capitialized. Maybe NULL
+} FILE_INFO_ENTRY;
+
+typedef struct {
+ //
+ // image address.
+ //
+ VOID *ImageAddress;
+ //
+ // The file info of the image comes from.
+ // if FileInfo == NULL. means image does not come from file
+ //
+ EFI_FILE_INFO *FileInfo;
+} IMAGE_INFO;
+
+#endif // _CAPSULES_ON_DISK_H_
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c
new file mode 100644
index 00000000..68a53da6
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c
@@ -0,0 +1,1650 @@
+/** @file
+ DXE capsule library.
+
+ 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.
+
+ SupportCapsuleImage(), ProcessCapsuleImage(), IsValidCapsuleHeader(),
+ ValidateFmpCapsule(), and DisplayCapsuleImage() receives untrusted input and
+ performs basic validation.
+
+ Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <IndustryStandard/WindowsUxCapsule.h>
+
+#include <Guid/FmpCapsule.h>
+#include <Guid/SystemResourceTable.h>
+#include <Guid/EventGroup.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/CapsuleLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BmpSupportLib.h>
+
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/EsrtManagement.h>
+#include <Protocol/FirmwareManagement.h>
+#include <Protocol/FirmwareManagementProgress.h>
+#include <Protocol/DevicePath.h>
+
+EFI_SYSTEM_RESOURCE_TABLE *mEsrtTable = NULL;
+BOOLEAN mIsVirtualAddrConverted = FALSE;
+
+BOOLEAN mDxeCapsuleLibEndOfDxe = FALSE;
+EFI_EVENT mDxeCapsuleLibEndOfDxeEvent = NULL;
+
+EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL *mFmpProgress = NULL;
+
+/**
+ Initialize capsule related variables.
+**/
+VOID
+InitCapsuleVariable (
+ VOID
+ );
+
+/**
+ Record capsule status variable.
+
+ @param[in] CapsuleHeader The capsule image header
+ @param[in] CapsuleStatus The capsule process stauts
+
+ @retval EFI_SUCCESS The capsule status variable is recorded.
+ @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
+**/
+EFI_STATUS
+RecordCapsuleStatusVariable (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN EFI_STATUS CapsuleStatus
+ );
+
+/**
+ Record FMP capsule status variable.
+
+ @param[in] CapsuleHeader The capsule image header
+ @param[in] CapsuleStatus The capsule process stauts
+ @param[in] PayloadIndex FMP payload index
+ @param[in] ImageHeader FMP image header
+ @param[in] FmpDevicePath DevicePath associated with the FMP producer
+ @param[in] CapFileName Capsule file name
+
+ @retval EFI_SUCCESS The capsule status variable is recorded.
+ @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
+**/
+EFI_STATUS
+RecordFmpCapsuleStatusVariable (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN EFI_STATUS CapsuleStatus,
+ IN UINTN PayloadIndex,
+ IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
+ IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath, OPTIONAL
+ IN CHAR16 *CapFileName 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
+ );
+
+/**
+ 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
+ )
+{
+ return CompareGuid (&CapsuleHeader->CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid);
+}
+
+/**
+ Return if this CapsuleGuid is a FMP capsule GUID or not.
+
+ @param[in] CapsuleGuid A pointer to EFI_GUID
+
+ @retval TRUE It is a FMP capsule GUID.
+ @retval FALSE It is not a FMP capsule GUID.
+**/
+BOOLEAN
+IsFmpCapsuleGuid (
+ IN EFI_GUID *CapsuleGuid
+ )
+{
+ if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Validate if it is valid capsule header
+
+ Caution: This function may receive untrusted input.
+
+ 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
+ )
+{
+ if (CapsuleHeader->CapsuleImageSize != CapsuleSize) {
+ return FALSE;
+ }
+ if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ 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
+ )
+{
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;
+ UINT8 *EndOfCapsule;
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;
+ UINT8 *EndOfPayload;
+ UINT64 *ItemOffsetList;
+ UINT32 ItemNum;
+ UINTN Index;
+ UINTN FmpCapsuleSize;
+ UINTN FmpCapsuleHeaderSize;
+ UINT64 FmpImageSize;
+ UINTN FmpImageHeaderSize;
+
+ if (!IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {
+ return ValidateFmpCapsule ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), EmbeddedDriverCount);
+ }
+
+ if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {
+ DEBUG((DEBUG_ERROR, "HeaderSize(0x%x) >= CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);
+ EndOfCapsule = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize;
+ FmpCapsuleSize = (UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader;
+
+ if (FmpCapsuleSize < sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER)) {
+ DEBUG((DEBUG_ERROR, "FmpCapsuleSize(0x%x) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\n", FmpCapsuleSize));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
+ if (FmpCapsuleHeader->Version != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {
+ DEBUG((DEBUG_ERROR, "FmpCapsuleHeader->Version(0x%x) != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION\n", FmpCapsuleHeader->Version));
+ return EFI_INVALID_PARAMETER;
+ }
+ ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);
+
+ // No overflow
+ ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;
+
+ if ((FmpCapsuleSize - sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER))/sizeof(UINT64) < ItemNum) {
+ DEBUG((DEBUG_ERROR, "ItemNum(0x%x) too big\n", ItemNum));
+ return EFI_INVALID_PARAMETER;
+ }
+ FmpCapsuleHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER) + sizeof(UINT64)*ItemNum;
+
+ // Check ItemOffsetList
+ for (Index = 0; Index < ItemNum; Index++) {
+ if (ItemOffsetList[Index] >= FmpCapsuleSize) {
+ DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) >= FmpCapsuleSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleSize));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (ItemOffsetList[Index] < FmpCapsuleHeaderSize) {
+ DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < FmpCapsuleHeaderSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleHeaderSize));
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // All the address in ItemOffsetList must be stored in ascending order
+ //
+ if (Index > 0) {
+ if (ItemOffsetList[Index] <= ItemOffsetList[Index - 1]) {
+ DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < ItemOffsetList[%d](0x%x)\n", Index, ItemOffsetList[Index], Index - 1, ItemOffsetList[Index - 1]));
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER
+ for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) {
+ ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);
+ if (Index == ItemNum - 1) {
+ EndOfPayload = (UINT8 *)((UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader);
+ } else {
+ EndOfPayload = (UINT8 *)(UINTN)ItemOffsetList[Index+1];
+ }
+ FmpImageSize = (UINTN)EndOfPayload - ItemOffsetList[Index];
+
+ FmpImageHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER);
+ if ((ImageHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) ||
+ (ImageHeader->Version < 1)) {
+ DEBUG((DEBUG_ERROR, "ImageHeader->Version(0x%x) Unknown\n", ImageHeader->Version));
+ return EFI_INVALID_PARAMETER;
+ }
+ if (ImageHeader->Version == 1) {
+ FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);
+ } else if (ImageHeader->Version == 2) {
+ FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, ImageCapsuleSupport);
+ }
+ if (FmpImageSize < FmpImageHeaderSize) {
+ DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) < FmpImageHeaderSize(0x%x)\n", FmpImageSize, FmpImageHeaderSize));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // No overflow
+ if (FmpImageSize != (UINT64)FmpImageHeaderSize + (UINT64)ImageHeader->UpdateImageSize + (UINT64)ImageHeader->UpdateVendorCodeSize) {
+ DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) mismatch, UpdateImageSize(0x%x) UpdateVendorCodeSize(0x%x)\n", FmpImageSize, ImageHeader->UpdateImageSize, ImageHeader->UpdateVendorCodeSize));
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (ItemNum == 0) {
+ //
+ // No driver & payload element in FMP
+ //
+ EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1);
+ if (EndOfPayload != EndOfCapsule) {
+ DEBUG((DEBUG_ERROR, "EndOfPayload(0x%x) mismatch, EndOfCapsule(0x%x)\n", EndOfPayload, EndOfCapsule));
+ return EFI_INVALID_PARAMETER;
+ }
+ return EFI_UNSUPPORTED;
+ }
+
+ if (EmbeddedDriverCount != NULL) {
+ *EmbeddedDriverCount = FmpCapsuleHeader->EmbeddedDriverCount;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Those capsules supported by the firmwares.
+
+ Caution: This function may receive untrusted input.
+
+ @param[in] 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.
+**/
+EFI_STATUS
+DisplayCapsuleImage (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader
+ )
+{
+ DISPLAY_DISPLAY_PAYLOAD *ImagePayload;
+ UINTN PayloadSize;
+ EFI_STATUS Status;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
+ UINTN BltSize;
+ UINTN Height;
+ UINTN Width;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
+
+ //
+ // UX capsule doesn't have extended header entries.
+ //
+ if (CapsuleHeader->HeaderSize != sizeof (EFI_CAPSULE_HEADER)) {
+ return EFI_UNSUPPORTED;
+ }
+ ImagePayload = (DISPLAY_DISPLAY_PAYLOAD *)((UINTN) CapsuleHeader + CapsuleHeader->HeaderSize);
+ //
+ // (CapsuleImageSize > HeaderSize) is guaranteed by IsValidCapsuleHeader().
+ //
+ PayloadSize = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;
+
+ //
+ // Make sure the image payload at least contain the DISPLAY_DISPLAY_PAYLOAD header.
+ // Further size check is performed by the logic translating BMP to GOP BLT.
+ //
+ if (PayloadSize <= sizeof (DISPLAY_DISPLAY_PAYLOAD)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ImagePayload->Version != 1) {
+ return EFI_UNSUPPORTED;
+ }
+ if (CalculateCheckSum8((UINT8 *)CapsuleHeader, CapsuleHeader->CapsuleImageSize) != 0) {
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Only Support Bitmap by now
+ //
+ if (ImagePayload->ImageType != 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Try to open GOP
+ //
+ Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
+ if (EFI_ERROR (Status)) {
+ Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&GraphicsOutput);
+ if (EFI_ERROR(Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ if (GraphicsOutput->Mode->Mode != ImagePayload->Mode) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Blt = NULL;
+ Width = 0;
+ Height = 0;
+ Status = TranslateBmpToGopBlt (
+ ImagePayload + 1,
+ PayloadSize - sizeof(DISPLAY_DISPLAY_PAYLOAD),
+ &Blt,
+ &BltSize,
+ &Height,
+ &Width
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = GraphicsOutput->Blt (
+ GraphicsOutput,
+ Blt,
+ EfiBltBufferToVideo,
+ 0,
+ 0,
+ (UINTN) ImagePayload->OffsetX,
+ (UINTN) ImagePayload->OffsetY,
+ Width,
+ Height,
+ Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
+ );
+
+ FreePool(Blt);
+
+ return Status;
+}
+
+/**
+ Dump FMP information.
+
+ @param[in] ImageInfoSize The size of ImageInfo, in bytes.
+ @param[in] ImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[in] DescriptorVersion The version of EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[in] DescriptorCount The count of EFI_FIRMWARE_IMAGE_DESCRIPTOR.
+ @param[in] DescriptorSize The size of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR, in bytes.
+ @param[in] PackageVersion The version of package.
+ @param[in] PackageVersionName The version name of package.
+**/
+VOID
+DumpFmpImageInfo (
+ IN UINTN ImageInfoSize,
+ IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo,
+ IN UINT32 DescriptorVersion,
+ IN UINT8 DescriptorCount,
+ IN UINTN DescriptorSize,
+ IN UINT32 PackageVersion,
+ IN CHAR16 *PackageVersionName
+ )
+{
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR *CurrentImageInfo;
+ UINTN Index;
+
+ DEBUG((DEBUG_VERBOSE, " DescriptorVersion - 0x%x\n", DescriptorVersion));
+ DEBUG((DEBUG_VERBOSE, " DescriptorCount - 0x%x\n", DescriptorCount));
+ DEBUG((DEBUG_VERBOSE, " DescriptorSize - 0x%x\n", DescriptorSize));
+ DEBUG((DEBUG_VERBOSE, " PackageVersion - 0x%x\n", PackageVersion));
+ DEBUG((DEBUG_VERBOSE, " PackageVersionName - %s\n\n", PackageVersionName));
+ CurrentImageInfo = ImageInfo;
+ for (Index = 0; Index < DescriptorCount; Index++) {
+ DEBUG((DEBUG_VERBOSE, " ImageDescriptor (%d)\n", Index));
+ DEBUG((DEBUG_VERBOSE, " ImageIndex - 0x%x\n", CurrentImageInfo->ImageIndex));
+ DEBUG((DEBUG_VERBOSE, " ImageTypeId - %g\n", &CurrentImageInfo->ImageTypeId));
+ DEBUG((DEBUG_VERBOSE, " ImageId - 0x%lx\n", CurrentImageInfo->ImageId));
+ DEBUG((DEBUG_VERBOSE, " ImageIdName - %s\n", CurrentImageInfo->ImageIdName));
+ DEBUG((DEBUG_VERBOSE, " Version - 0x%x\n", CurrentImageInfo->Version));
+ DEBUG((DEBUG_VERBOSE, " VersionName - %s\n", CurrentImageInfo->VersionName));
+ DEBUG((DEBUG_VERBOSE, " Size - 0x%x\n", CurrentImageInfo->Size));
+ DEBUG((DEBUG_VERBOSE, " AttributesSupported - 0x%lx\n", CurrentImageInfo->AttributesSupported));
+ DEBUG((DEBUG_VERBOSE, " AttributesSetting - 0x%lx\n", CurrentImageInfo->AttributesSetting));
+ DEBUG((DEBUG_VERBOSE, " Compatibilities - 0x%lx\n", CurrentImageInfo->Compatibilities));
+ if (DescriptorVersion > 1) {
+ DEBUG((DEBUG_VERBOSE, " LowestSupportedImageVersion - 0x%x\n", CurrentImageInfo->LowestSupportedImageVersion));
+ if (DescriptorVersion > 2) {
+ DEBUG((DEBUG_VERBOSE, " LastAttemptVersion - 0x%x\n", CurrentImageInfo->LastAttemptVersion));
+ DEBUG((DEBUG_VERBOSE, " LastAttemptStatus - 0x%x\n", CurrentImageInfo->LastAttemptStatus));
+ DEBUG((DEBUG_VERBOSE, " HardwareInstance - 0x%lx\n", CurrentImageInfo->HardwareInstance));
+ }
+ }
+ //
+ // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version
+ //
+ CurrentImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)CurrentImageInfo + DescriptorSize);
+ }
+}
+
+/**
+ Dump a non-nested FMP capsule.
+
+ @param[in] CapsuleHeader A pointer to CapsuleHeader
+**/
+VOID
+DumpFmpCapsule (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader
+ )
+{
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;
+ UINTN Index;
+ UINT64 *ItemOffsetList;
+
+ FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize);
+
+ DEBUG((DEBUG_VERBOSE, "FmpCapsule:\n"));
+ DEBUG((DEBUG_VERBOSE, " Version - 0x%x\n", FmpCapsuleHeader->Version));
+ DEBUG((DEBUG_VERBOSE, " EmbeddedDriverCount - 0x%x\n", FmpCapsuleHeader->EmbeddedDriverCount));
+ DEBUG((DEBUG_VERBOSE, " PayloadItemCount - 0x%x\n", FmpCapsuleHeader->PayloadItemCount));
+
+ ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);
+ for (Index = 0; Index < FmpCapsuleHeader->EmbeddedDriverCount; Index++) {
+ DEBUG((DEBUG_VERBOSE, " ItemOffsetList[%d] - 0x%lx\n", Index, ItemOffsetList[Index]));
+ }
+ for (; Index < (UINT32)FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount; Index++) {
+ DEBUG((DEBUG_VERBOSE, " ItemOffsetList[%d] - 0x%lx\n", Index, ItemOffsetList[Index]));
+ ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);
+
+ DEBUG((DEBUG_VERBOSE, " ImageHeader:\n"));
+ DEBUG((DEBUG_VERBOSE, " Version - 0x%x\n", ImageHeader->Version));
+ DEBUG((DEBUG_VERBOSE, " UpdateImageTypeId - %g\n", &ImageHeader->UpdateImageTypeId));
+ DEBUG((DEBUG_VERBOSE, " UpdateImageIndex - 0x%x\n", ImageHeader->UpdateImageIndex));
+ DEBUG((DEBUG_VERBOSE, " UpdateImageSize - 0x%x\n", ImageHeader->UpdateImageSize));
+ DEBUG((DEBUG_VERBOSE, " UpdateVendorCodeSize - 0x%x\n", ImageHeader->UpdateVendorCodeSize));
+ if (ImageHeader->Version >= 2) {
+ DEBUG((DEBUG_VERBOSE, " UpdateHardwareInstance - 0x%lx\n", ImageHeader->UpdateHardwareInstance));
+ if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {
+ DEBUG((DEBUG_VERBOSE, " ImageCapsuleSupport - 0x%lx\n", ImageHeader->ImageCapsuleSupport));
+ }
+ }
+ }
+}
+
+/**
+ Dump all FMP information.
+**/
+VOID
+DumpAllFmpInfo (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN NumberOfHandles;
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
+ UINTN Index;
+ UINTN ImageInfoSize;
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
+ UINT32 FmpImageInfoDescriptorVer;
+ UINT8 FmpImageInfoCount;
+ UINTN DescriptorSize;
+ UINT32 PackageVersion;
+ CHAR16 *PackageVersionName;
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiFirmwareManagementProtocolGuid,
+ NULL,
+ &NumberOfHandles,
+ &HandleBuffer
+ );
+ if (EFI_ERROR(Status)) {
+ return ;
+ }
+
+ for (Index = 0; Index < NumberOfHandles; Index++) {
+ Status = gBS->HandleProtocol(
+ HandleBuffer[Index],
+ &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 = AllocateZeroPool (ImageInfoSize);
+ if (FmpImageInfoBuf == NULL) {
+ continue;
+ }
+
+ PackageVersionName = NULL;
+ Status = Fmp->GetImageInfo (
+ Fmp,
+ &ImageInfoSize, // ImageInfoSize
+ FmpImageInfoBuf, // ImageInfo
+ &FmpImageInfoDescriptorVer, // DescriptorVersion
+ &FmpImageInfoCount, // DescriptorCount
+ &DescriptorSize, // DescriptorSize
+ &PackageVersion, // PackageVersion
+ &PackageVersionName // PackageVersionName
+ );
+ if (EFI_ERROR(Status)) {
+ FreePool(FmpImageInfoBuf);
+ continue;
+ }
+
+ DEBUG((DEBUG_INFO, "FMP (%d) ImageInfo:\n", Index));
+ DumpFmpImageInfo(
+ ImageInfoSize, // ImageInfoSize
+ FmpImageInfoBuf, // ImageInfo
+ FmpImageInfoDescriptorVer, // DescriptorVersion
+ FmpImageInfoCount, // DescriptorCount
+ DescriptorSize, // DescriptorSize
+ PackageVersion, // PackageVersion
+ PackageVersionName // PackageVersionName
+ );
+
+ if (PackageVersionName != NULL) {
+ FreePool(PackageVersionName);
+ }
+
+ FreePool(FmpImageInfoBuf);
+ }
+
+ FreePool (HandleBuffer);
+
+ return ;
+}
+
+/**
+ Get FMP handle by ImageTypeId and HardwareInstance.
+
+ @param[in] UpdateImageTypeId Used to identify device firmware targeted by this update.
+ @param[in] UpdateHardwareInstance The HardwareInstance to target with this update.
+ @param[out] NoHandles The number of handles returned in HandleBuf.
+ @param[out] HandleBuf A pointer to the buffer to return the requested array of handles.
+ @param[out] ResetRequiredBuf A pointer to the buffer to return reset required flag for
+ the requested array of handles.
+
+ @retval EFI_SUCCESS The array of handles and their reset required flag were returned in
+ HandleBuf and ResetRequiredBuf, and the number of handles in HandleBuf
+ was returned in NoHandles.
+ @retval EFI_NOT_FOUND No handles match the search.
+ @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
+**/
+EFI_STATUS
+GetFmpHandleBufferByType (
+ IN EFI_GUID *UpdateImageTypeId,
+ IN UINT64 UpdateHardwareInstance,
+ OUT UINTN *NoHandles, OPTIONAL
+ OUT EFI_HANDLE **HandleBuf, OPTIONAL
+ OUT BOOLEAN **ResetRequiredBuf OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN NumberOfHandles;
+ EFI_HANDLE *MatchedHandleBuffer;
+ BOOLEAN *MatchedResetRequiredBuffer;
+ UINTN MatchedNumberOfHandles;
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
+ UINTN Index;
+ UINTN ImageInfoSize;
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
+ UINT32 FmpImageInfoDescriptorVer;
+ UINT8 FmpImageInfoCount;
+ UINTN DescriptorSize;
+ UINT32 PackageVersion;
+ CHAR16 *PackageVersionName;
+ UINTN Index2;
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo;
+
+ if (NoHandles != NULL) {
+ *NoHandles = 0;
+ }
+ if (HandleBuf != NULL) {
+ *HandleBuf = NULL;
+ }
+ if (ResetRequiredBuf != NULL) {
+ *ResetRequiredBuf = NULL;
+ }
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiFirmwareManagementProtocolGuid,
+ NULL,
+ &NumberOfHandles,
+ &HandleBuffer
+ );
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ MatchedNumberOfHandles = 0;
+
+ MatchedHandleBuffer = NULL;
+ if (HandleBuf != NULL) {
+ MatchedHandleBuffer = AllocateZeroPool (sizeof(EFI_HANDLE) * NumberOfHandles);
+ if (MatchedHandleBuffer == NULL) {
+ FreePool (HandleBuffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ MatchedResetRequiredBuffer = NULL;
+ if (ResetRequiredBuf != NULL) {
+ MatchedResetRequiredBuffer = AllocateZeroPool (sizeof(BOOLEAN) * NumberOfHandles);
+ if (MatchedResetRequiredBuffer == NULL) {
+ if (MatchedHandleBuffer != NULL) {
+ FreePool (MatchedHandleBuffer);
+ }
+ FreePool (HandleBuffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ for (Index = 0; Index < NumberOfHandles; Index++) {
+ Status = gBS->HandleProtocol(
+ HandleBuffer[Index],
+ &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 = AllocateZeroPool (ImageInfoSize);
+ if (FmpImageInfoBuf == NULL) {
+ continue;
+ }
+
+ PackageVersionName = NULL;
+ Status = Fmp->GetImageInfo (
+ Fmp,
+ &ImageInfoSize, // ImageInfoSize
+ FmpImageInfoBuf, // ImageInfo
+ &FmpImageInfoDescriptorVer, // DescriptorVersion
+ &FmpImageInfoCount, // DescriptorCount
+ &DescriptorSize, // DescriptorSize
+ &PackageVersion, // PackageVersion
+ &PackageVersionName // PackageVersionName
+ );
+ if (EFI_ERROR(Status)) {
+ FreePool(FmpImageInfoBuf);
+ continue;
+ }
+
+ if (PackageVersionName != NULL) {
+ FreePool(PackageVersionName);
+ }
+
+ TempFmpImageInfo = FmpImageInfoBuf;
+ for (Index2 = 0; Index2 < FmpImageInfoCount; Index2++) {
+ //
+ // Check if this FMP instance matches
+ //
+ if (CompareGuid(UpdateImageTypeId, &TempFmpImageInfo->ImageTypeId)) {
+ if ((UpdateHardwareInstance == 0) ||
+ ((FmpImageInfoDescriptorVer >= EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION) &&
+ (UpdateHardwareInstance == TempFmpImageInfo->HardwareInstance))) {
+ if (MatchedHandleBuffer != NULL) {
+ MatchedHandleBuffer[MatchedNumberOfHandles] = HandleBuffer[Index];
+ }
+ if (MatchedResetRequiredBuffer != NULL) {
+ MatchedResetRequiredBuffer[MatchedNumberOfHandles] = (((TempFmpImageInfo->AttributesSupported &
+ IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0) &&
+ ((TempFmpImageInfo->AttributesSetting &
+ IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0));
+ }
+ MatchedNumberOfHandles++;
+ break;
+ }
+ }
+ TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSize);
+ }
+ FreePool(FmpImageInfoBuf);
+ }
+
+ FreePool (HandleBuffer);
+
+ if (MatchedNumberOfHandles == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (NoHandles != NULL) {
+ *NoHandles = MatchedNumberOfHandles;
+ }
+ if (HandleBuf != NULL) {
+ *HandleBuf = MatchedHandleBuffer;
+ }
+ if (ResetRequiredBuf != NULL) {
+ *ResetRequiredBuf = MatchedResetRequiredBuffer;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Return FmpImageInfoDescriptorVer by an FMP handle.
+
+ @param[in] Handle A FMP handle.
+
+ @return FmpImageInfoDescriptorVer associated with the FMP.
+**/
+UINT32
+GetFmpImageInfoDescriptorVer (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
+ UINTN ImageInfoSize;
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
+ UINT32 FmpImageInfoDescriptorVer;
+ UINT8 FmpImageInfoCount;
+ UINTN DescriptorSize;
+ UINT32 PackageVersion;
+ CHAR16 *PackageVersionName;
+
+ Status = gBS->HandleProtocol(
+ Handle,
+ &gEfiFirmwareManagementProtocolGuid,
+ (VOID **)&Fmp
+ );
+ if (EFI_ERROR(Status)) {
+ return 0;
+ }
+
+ ImageInfoSize = 0;
+ Status = Fmp->GetImageInfo (
+ Fmp,
+ &ImageInfoSize,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return 0;
+ }
+
+ FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
+ if (FmpImageInfoBuf == NULL) {
+ return 0;
+ }
+
+ PackageVersionName = NULL;
+ Status = Fmp->GetImageInfo (
+ Fmp,
+ &ImageInfoSize, // ImageInfoSize
+ FmpImageInfoBuf, // ImageInfo
+ &FmpImageInfoDescriptorVer, // DescriptorVersion
+ &FmpImageInfoCount, // DescriptorCount
+ &DescriptorSize, // DescriptorSize
+ &PackageVersion, // PackageVersion
+ &PackageVersionName // PackageVersionName
+ );
+ if (EFI_ERROR(Status)) {
+ FreePool(FmpImageInfoBuf);
+ return 0;
+ }
+ return FmpImageInfoDescriptorVer;
+}
+
+/**
+ Set FMP image data.
+
+ @param[in] Handle A FMP handle.
+ @param[in] ImageHeader The payload image header.
+ @param[in] PayloadIndex The index of the payload.
+
+ @return The status of FMP->SetImage.
+**/
+EFI_STATUS
+SetFmpImageData (
+ IN EFI_HANDLE Handle,
+ IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
+ IN UINTN PayloadIndex
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
+ UINT8 *Image;
+ VOID *VendorCode;
+ CHAR16 *AbortReason;
+ EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS ProgressCallback;
+
+ Status = gBS->HandleProtocol(
+ Handle,
+ &gEfiFirmwareManagementProtocolGuid,
+ (VOID **)&Fmp
+ );
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Lookup Firmware Management Progress Protocol before SetImage() is called
+ // This is an optional protocol that may not be present on Handle.
+ //
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEdkiiFirmwareManagementProgressProtocolGuid,
+ (VOID **)&mFmpProgress
+ );
+ if (EFI_ERROR (Status)) {
+ mFmpProgress = NULL;
+ }
+
+ if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {
+ Image = (UINT8 *)(ImageHeader + 1);
+ } else {
+ //
+ // If the EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is version 1,
+ // Header should exclude UpdateHardwareInstance field, and
+ // ImageCapsuleSupport field if version is 2.
+ //
+ if (ImageHeader->Version == 1) {
+ Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);
+ } else {
+ Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, ImageCapsuleSupport);
+ }
+ }
+
+ if (ImageHeader->UpdateVendorCodeSize == 0) {
+ VendorCode = NULL;
+ } else {
+ VendorCode = Image + ImageHeader->UpdateImageSize;
+ }
+ AbortReason = NULL;
+ DEBUG((DEBUG_INFO, "Fmp->SetImage ...\n"));
+ DEBUG((DEBUG_INFO, "ImageTypeId - %g, ", &ImageHeader->UpdateImageTypeId));
+ DEBUG((DEBUG_INFO, "PayloadIndex - 0x%x, ", PayloadIndex));
+ DEBUG((DEBUG_INFO, "ImageIndex - 0x%x ", ImageHeader->UpdateImageIndex));
+ if (ImageHeader->Version >= 2) {
+ DEBUG((DEBUG_INFO, "(UpdateHardwareInstance - 0x%x)", ImageHeader->UpdateHardwareInstance));
+ if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {
+ DEBUG((DEBUG_INFO, "(ImageCapsuleSupport - 0x%x)", ImageHeader->ImageCapsuleSupport));
+ }
+ }
+ DEBUG((DEBUG_INFO, "\n"));
+
+ //
+ // Before calling SetImage(), reset the progress bar to 0%
+ //
+ ProgressCallback = UpdateImageProgress;
+ Status = UpdateImageProgress (0);
+ if (EFI_ERROR (Status)) {
+ ProgressCallback = NULL;
+ }
+
+ Status = Fmp->SetImage(
+ Fmp,
+ ImageHeader->UpdateImageIndex, // ImageIndex
+ Image, // Image
+ ImageHeader->UpdateImageSize, // ImageSize
+ VendorCode, // VendorCode
+ ProgressCallback, // Progress
+ &AbortReason // AbortReason
+ );
+ //
+ // Set the progress bar to 100% after returning from SetImage()
+ //
+ if (ProgressCallback != NULL) {
+ UpdateImageProgress (100);
+ }
+
+ DEBUG((DEBUG_INFO, "Fmp->SetImage - %r\n", Status));
+ if (AbortReason != NULL) {
+ DEBUG ((DEBUG_ERROR, "%s\n", AbortReason));
+ FreePool(AbortReason);
+ }
+
+ //
+ // Clear mFmpProgress after SetImage() returns
+ //
+ mFmpProgress = NULL;
+
+ return Status;
+}
+
+/**
+ Start a UEFI image in the FMP payload.
+
+ @param[in] ImageBuffer A pointer to the memory location containing a copy of the image to be loaded..
+ @param[in] ImageSize The size in bytes of ImageBuffer.
+
+ @return The status of gBS->LoadImage and gBS->StartImage.
+**/
+EFI_STATUS
+StartFmpImage (
+ IN VOID *ImageBuffer,
+ IN UINTN ImageSize
+ )
+{
+ MEMMAP_DEVICE_PATH MemMapNode;
+ EFI_STATUS Status;
+ EFI_HANDLE ImageHandle;
+ EFI_DEVICE_PATH_PROTOCOL *DriverDevicePath;
+ UINTN ExitDataSize;
+
+ 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)ImageBuffer;
+ MemMapNode.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)((UINT8 *)ImageBuffer + ImageSize - 1);
+
+ DriverDevicePath = AppendDevicePathNode (NULL, &MemMapNode.Header);
+ if (DriverDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DEBUG((DEBUG_INFO, "FmpCapsule: LoadImage ...\n"));
+ Status = gBS->LoadImage(
+ FALSE,
+ gImageHandle,
+ DriverDevicePath,
+ ImageBuffer,
+ ImageSize,
+ &ImageHandle
+ );
+ DEBUG((DEBUG_INFO, "FmpCapsule: LoadImage - %r\n", Status));
+ if (EFI_ERROR(Status)) {
+ //
+ // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
+ // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
+ // If the caller doesn't have the option to defer the execution of an image, we should
+ // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
+ //
+ if (Status == EFI_SECURITY_VIOLATION) {
+ gBS->UnloadImage (ImageHandle);
+ }
+ FreePool(DriverDevicePath);
+ return Status;
+ }
+
+ DEBUG((DEBUG_INFO, "FmpCapsule: StartImage ...\n"));
+ Status = gBS->StartImage(
+ ImageHandle,
+ &ExitDataSize,
+ NULL
+ );
+ DEBUG((DEBUG_INFO, "FmpCapsule: StartImage - %r\n", Status));
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "Driver Return Status = %r\n", Status));
+ }
+
+ FreePool(DriverDevicePath);
+ return Status;
+}
+
+/**
+ Record FMP capsule status.
+
+ @param[in] Handle A FMP handle.
+ @param[in] CapsuleHeader The capsule image header
+ @param[in] CapsuleStatus The capsule process stauts
+ @param[in] PayloadIndex FMP payload index
+ @param[in] ImageHeader FMP image header
+ @param[in] CapFileName Capsule file name
+**/
+VOID
+RecordFmpCapsuleStatus (
+ IN EFI_HANDLE Handle, OPTIONAL
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN EFI_STATUS CapsuleStatus,
+ IN UINTN PayloadIndex,
+ IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
+ IN CHAR16 *CapFileName OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath;
+ UINT32 FmpImageInfoDescriptorVer;
+ EFI_STATUS StatusEsrt;
+ ESRT_MANAGEMENT_PROTOCOL *EsrtProtocol;
+ EFI_SYSTEM_RESOURCE_ENTRY EsrtEntry;
+
+ FmpDevicePath = NULL;
+ if (Handle != NULL) {
+ gBS->HandleProtocol(
+ Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&FmpDevicePath
+ );
+ }
+
+ RecordFmpCapsuleStatusVariable (
+ CapsuleHeader,
+ CapsuleStatus,
+ PayloadIndex,
+ ImageHeader,
+ FmpDevicePath,
+ CapFileName
+ );
+
+ //
+ // Update corresponding ESRT entry LastAttemp Status
+ //
+ Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtProtocol);
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+ if (Handle == NULL) {
+ return ;
+ }
+
+ //
+ // Update EsrtEntry For V1, V2 FMP instance.
+ // V3 FMP ESRT cache will be synced up through SyncEsrtFmp interface
+ //
+ FmpImageInfoDescriptorVer = GetFmpImageInfoDescriptorVer (Handle);
+ if (FmpImageInfoDescriptorVer < EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION) {
+ StatusEsrt = EsrtProtocol->GetEsrtEntry(&ImageHeader->UpdateImageTypeId, &EsrtEntry);
+ if (!EFI_ERROR(StatusEsrt)){
+ if (!EFI_ERROR(CapsuleStatus)) {
+ EsrtEntry.LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
+ } else {
+ EsrtEntry.LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
+ }
+ EsrtEntry.LastAttemptVersion = 0;
+ EsrtProtocol->UpdateEsrtEntry(&EsrtEntry);
+ }
+ }
+}
+
+/**
+ Process Firmware management protocol data capsule.
+
+ This function assumes the caller validated the capsule by using
+ ValidateFmpCapsule(), so that all fields in EFI_CAPSULE_HEADER,
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER are correct.
+
+ This function need support nested FMP capsule.
+
+ @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.
+ @retval EFI_NOT_READY No FMP protocol to handle this FMP capsule.
+**/
+EFI_STATUS
+ProcessFmpCapsuleImage (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN CHAR16 *CapFileName, OPTIONAL
+ OUT BOOLEAN *ResetRequired OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;
+ UINT64 *ItemOffsetList;
+ UINT32 ItemNum;
+ UINTN Index;
+ EFI_HANDLE *HandleBuffer;
+ BOOLEAN *ResetRequiredBuffer;
+ UINTN NumberOfHandles;
+ UINTN DriverLen;
+ UINT64 UpdateHardwareInstance;
+ UINTN Index2;
+ BOOLEAN NotReady;
+ BOOLEAN Abort;
+
+ if (!IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {
+ return ProcessFmpCapsuleImage ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), CapFileName, ResetRequired);
+ }
+
+ NotReady = FALSE;
+ Abort = FALSE;
+
+ DumpFmpCapsule(CapsuleHeader);
+
+ 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. Try to load & start all the drivers within capsule
+ //
+ 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 = StartFmpImage (
+ (UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index],
+ DriverLen
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "Driver Return Status = %r\n", Status));
+ return Status;
+ }
+ }
+
+ //
+ // 2. Route payload to right FMP instance
+ //
+ DEBUG((DEBUG_INFO, "FmpCapsule: route payload to right FMP instance ...\n"));
+
+ DumpAllFmpInfo ();
+
+ //
+ // 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]);
+
+ UpdateHardwareInstance = 0;
+ ///
+ /// UpdateHardwareInstance field was added in Version 2
+ ///
+ if (ImageHeader->Version >= 2) {
+ UpdateHardwareInstance = ImageHeader->UpdateHardwareInstance;
+ }
+
+ Status = GetFmpHandleBufferByType (
+ &ImageHeader->UpdateImageTypeId,
+ UpdateHardwareInstance,
+ &NumberOfHandles,
+ &HandleBuffer,
+ &ResetRequiredBuffer
+ );
+ if (EFI_ERROR(Status) ||
+ (HandleBuffer == NULL) ||
+ (ResetRequiredBuffer == NULL)) {
+ NotReady = TRUE;
+ RecordFmpCapsuleStatus (
+ NULL,
+ CapsuleHeader,
+ EFI_NOT_READY,
+ Index - FmpCapsuleHeader->EmbeddedDriverCount,
+ ImageHeader,
+ CapFileName
+ );
+ continue;
+ }
+
+ for (Index2 = 0; Index2 < NumberOfHandles; Index2++) {
+ if (Abort) {
+ RecordFmpCapsuleStatus (
+ HandleBuffer[Index2],
+ CapsuleHeader,
+ EFI_ABORTED,
+ Index - FmpCapsuleHeader->EmbeddedDriverCount,
+ ImageHeader,
+ CapFileName
+ );
+ continue;
+ }
+
+ Status = SetFmpImageData (
+ HandleBuffer[Index2],
+ ImageHeader,
+ Index - FmpCapsuleHeader->EmbeddedDriverCount
+ );
+ if (Status != EFI_SUCCESS) {
+ Abort = TRUE;
+ } else {
+ if (ResetRequired != NULL) {
+ *ResetRequired |= ResetRequiredBuffer[Index2];
+ }
+ }
+
+ RecordFmpCapsuleStatus (
+ HandleBuffer[Index2],
+ CapsuleHeader,
+ Status,
+ Index - FmpCapsuleHeader->EmbeddedDriverCount,
+ ImageHeader,
+ CapFileName
+ );
+ }
+ if (HandleBuffer != NULL) {
+ FreePool(HandleBuffer);
+ }
+ if (ResetRequiredBuffer != NULL) {
+ FreePool(ResetRequiredBuffer);
+ }
+ }
+
+ if (NotReady) {
+ return EFI_NOT_READY;
+ }
+
+ //
+ // always return SUCCESS to indicate this capsule is processed.
+ // The status of SetImage is recorded in capsule result variable.
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Return if there is a FMP header below capsule header.
+
+ @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
+
+ @retval TRUE There is a FMP header below capsule header.
+ @retval FALSE There is not a FMP header below capsule header
+**/
+BOOLEAN
+IsNestedFmpCapsule (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader
+ )
+{
+ EFI_STATUS Status;
+ EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry;
+ UINTN Index;
+ BOOLEAN EsrtGuidFound;
+ EFI_CAPSULE_HEADER *NestedCapsuleHeader;
+ UINTN NestedCapsuleSize;
+ ESRT_MANAGEMENT_PROTOCOL *EsrtProtocol;
+ EFI_SYSTEM_RESOURCE_ENTRY Entry;
+
+ EsrtGuidFound = FALSE;
+ if (mIsVirtualAddrConverted) {
+ if(mEsrtTable != NULL) {
+ EsrtEntry = (EFI_SYSTEM_RESOURCE_ENTRY *)(mEsrtTable + 1);
+ for (Index = 0; Index < mEsrtTable->FwResourceCount ; Index++, EsrtEntry++) {
+ if (CompareGuid(&EsrtEntry->FwClass, &CapsuleHeader->CapsuleGuid)) {
+ EsrtGuidFound = TRUE;
+ break;
+ }
+ }
+ }
+ } else {
+ //
+ // Check ESRT protocol
+ //
+ Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtProtocol);
+ if (!EFI_ERROR(Status)) {
+ Status = EsrtProtocol->GetEsrtEntry(&CapsuleHeader->CapsuleGuid, &Entry);
+ if (!EFI_ERROR(Status)) {
+ EsrtGuidFound = TRUE;
+ }
+ }
+
+ //
+ // Check Firmware Management Protocols
+ //
+ if (!EsrtGuidFound) {
+ Status = GetFmpHandleBufferByType (
+ &CapsuleHeader->CapsuleGuid,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ );
+ if (!EFI_ERROR(Status)) {
+ EsrtGuidFound = TRUE;
+ }
+ }
+ }
+ if (!EsrtGuidFound) {
+ return FALSE;
+ }
+
+ //
+ // Check nested capsule header
+ // FMP GUID after ESRT one
+ //
+ NestedCapsuleHeader = (EFI_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize);
+ NestedCapsuleSize = (UINTN)CapsuleHeader + CapsuleHeader->CapsuleImageSize - (UINTN)NestedCapsuleHeader;
+ if (NestedCapsuleSize < sizeof(EFI_CAPSULE_HEADER)) {
+ return FALSE;
+ }
+ if (!IsValidCapsuleHeader(NestedCapsuleHeader, NestedCapsuleSize)) {
+ return FALSE;
+ }
+ if (!IsFmpCapsuleGuid(&NestedCapsuleHeader->CapsuleGuid)) {
+ return FALSE;
+ }
+ DEBUG ((DEBUG_INFO, "IsNestedFmpCapsule\n"));
+ return TRUE;
+}
+
+/**
+ 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
+ )
+{
+ if (IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {
+ return TRUE;
+ }
+ if (IsNestedFmpCapsule(CapsuleHeader)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ Those capsules supported by the firmwares.
+
+ Caution: This function may receive untrusted input.
+
+ @param[in] 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
+ )
+{
+ //
+ // check Display Capsule Guid
+ //
+ if (CompareGuid (&gWindowsUxCapsuleGuid, &CapsuleHeader->CapsuleGuid)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Check capsule file name capsule
+ //
+ if (IsCapsuleNameCapsule(CapsuleHeader)) {
+ return EFI_SUCCESS;
+ }
+
+ if (IsFmpCapsule(CapsuleHeader)) {
+ //
+ // Fake capsule header is valid case in QueryCapsuleCpapbilities().
+ //
+ if (CapsuleHeader->HeaderSize == CapsuleHeader->CapsuleImageSize) {
+ return EFI_SUCCESS;
+ }
+ //
+ // Check layout of FMP capsule
+ //
+ return ValidateFmpCapsule(CapsuleHeader, NULL);
+ }
+ DEBUG((DEBUG_ERROR, "Unknown Capsule Guid - %g\n", &CapsuleHeader->CapsuleGuid));
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ 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
+ )
+{
+ EFI_STATUS Status;
+
+ if (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS) {
+ RecordCapsuleStatusVariable(CapsuleHeader, EFI_UNSUPPORTED);
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Display image in firmware update display capsule
+ //
+ if (CompareGuid (&gWindowsUxCapsuleGuid, &CapsuleHeader->CapsuleGuid)) {
+ DEBUG((DEBUG_INFO, "ProcessCapsuleImage for WindowsUxCapsule ...\n"));
+ Status = DisplayCapsuleImage(CapsuleHeader);
+ RecordCapsuleStatusVariable(CapsuleHeader, Status);
+ return Status;
+ }
+
+ //
+ // Check FMP capsule layout
+ //
+ if (IsFmpCapsule (CapsuleHeader)) {
+ DEBUG((DEBUG_INFO, "ProcessCapsuleImage for FmpCapsule ...\n"));
+ DEBUG((DEBUG_INFO, "ValidateFmpCapsule ...\n"));
+ Status = ValidateFmpCapsule(CapsuleHeader, NULL);
+ DEBUG((DEBUG_INFO, "ValidateFmpCapsule - %r\n", Status));
+ if (EFI_ERROR(Status)) {
+ RecordCapsuleStatusVariable(CapsuleHeader, Status);
+ return Status;
+ }
+
+ //
+ // Process EFI FMP Capsule
+ //
+ DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n"));
+ Status = ProcessFmpCapsuleImage(CapsuleHeader, CapFileName, ResetRequired);
+ DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status));
+
+ return Status;
+ }
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ The firmware implements to process the capsule image.
+
+ Caution: This function may receive untrusted input.
+
+ @param[in] 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
+ )
+{
+ return ProcessThisCapsuleImage (CapsuleHeader, NULL, NULL);
+}
+
+/**
+ Callback function executed when the EndOfDxe event group is signaled.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context The pointer to the notification function's context, which
+ is implementation-dependent.
+**/
+VOID
+EFIAPI
+DxeCapsuleLibEndOfDxe (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ mDxeCapsuleLibEndOfDxe = TRUE;
+}
+
+/**
+ The constructor function.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The constructor successfully .
+**/
+EFI_STATUS
+EFIAPI
+DxeCapsuleLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ DxeCapsuleLibEndOfDxe,
+ NULL,
+ &gEfiEndOfDxeEventGroupGuid,
+ &mDxeCapsuleLibEndOfDxeEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ InitCapsuleVariable();
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The destructor function closes the End of DXE event.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The destructor completed successfully.
+**/
+EFI_STATUS
+EFIAPI
+DxeCapsuleLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Close the End of DXE event.
+ //
+ Status = gBS->CloseEvent (mDxeCapsuleLibEndOfDxeEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
new file mode 100644
index 00000000..0e3bb2bc
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
@@ -0,0 +1,98 @@
+## @file
+# Capsule library instance for DXE_DRIVER.
+#
+# Capsule library instance for DXE_DRIVER module types.
+#
+# Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeCapsuleLib
+ MODULE_UNI_FILE = DxeCapsuleLib.uni
+ FILE_GUID = 534E35DE-8EB3-47b3-A4E0-72A571E50733
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = CapsuleLib|DXE_DRIVER UEFI_APPLICATION
+ CONSTRUCTOR = DxeCapsuleLibConstructor
+ DESTRUCTOR = DxeCapsuleLibDestructor
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeCapsuleLib.c
+ DxeCapsuleProcessLib.c
+ DxeCapsuleReportLib.c
+ CapsuleOnDisk.c
+ CapsuleOnDisk.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ DxeServicesTableLib
+ UefiBootServicesTableLib
+ DevicePathLib
+ ReportStatusCodeLib
+ PrintLib
+ HobLib
+ BmpSupportLib
+ DisplayUpdateProgressLib
+ FileHandleLib
+ UefiBootManagerLib
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleMax ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSystemRebootAfterCapsuleProcessFlag ## CONSUMES
+
+ gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeSubClassCapsule ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeProcessCapsulesBegin ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeProcessCapsulesEnd ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdatingFirmware ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareSuccess ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareFailed ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeResettingSystem ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleInRamSupport ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleOnDiskSupport ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCodRelocationDevPath ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCoDRelocationFileName ## CONSUMES
+
+[Protocols]
+ gEsrtManagementProtocolGuid ## CONSUMES
+ gEfiFirmwareManagementProtocolGuid ## CONSUMES
+ gEdkiiVariableLockProtocolGuid ## SOMETIMES_CONSUMES
+ gEdkiiFirmwareManagementProgressProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiBlockIoProtocolGuid ## CONSUMES
+ gEfiDiskIoProtocolGuid ## CONSUMES
+
+[Guids]
+ gEfiFmpCapsuleGuid ## SOMETIMES_CONSUMES ## GUID
+ gWindowsUxCapsuleGuid ## SOMETIMES_CONSUMES ## GUID
+ ## SOMETIMES_CONSUMES ## Variable:L"CapsuleMax"
+ ## SOMETIMES_PRODUCES ## Variable:L"CapsuleMax"
+ gEfiCapsuleReportGuid
+ gEfiCapsuleVendorGuid ## SOMETIMES_CONSUMES ## Variable:L"CapsuleUpdateData"
+ gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event
+ gEfiPartTypeSystemPartGuid ## SOMETIMES_CONSUMES
+ gEfiCapsuleVendorGuid ## SOMETIMES_CONSUMES ## Variable:L"CodRelocationInfo"
+ ## SOMETIMES_CONSUMES ## Variable:L"OsIndications"
+ ## SOMETIMES_PRODUCES ## Variable:L"OsIndications"
+ ## SOMETIMES_CONSUMES ## Variable:L"BootNext"
+ ## SOMETIMES_PRODUCES ## Variable:L"BootNext"
+ gEfiGlobalVariableGuid
+ gEdkiiCapsuleOnDiskNameGuid ## SOMETIMES_CONSUMES ## GUID
+
+[Depex]
+ gEfiVariableWriteArchProtocolGuid
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni
new file mode 100644
index 00000000..03c61c55
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// Capsule library instance for DXE_DRIVER.
+//
+// Capsule library instance for DXE_DRIVER module types.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Capsule Support Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Capsule library instance for DXE_DRIVER module types."
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c
new file mode 100644
index 00000000..e721a64b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c
@@ -0,0 +1,691 @@
+/** @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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c
new file mode 100644
index 00000000..33f99e74
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c
@@ -0,0 +1,70 @@
+/** @file
+ DXE capsule process.
+ Dummy function for runtime module, because CapsuleDxeRuntime
+ does not need call ProcessCapsules().
+
+ Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Library/CapsuleLib.h>
+
+/**
+ 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
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+
+ 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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c
new file mode 100644
index 00000000..bfefd652
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c
@@ -0,0 +1,473 @@
+/** @file
+ DXE capsule report related function.
+
+ Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Protocol/FirmwareManagement.h>
+#include <Protocol/VariableLock.h>
+#include <Guid/CapsuleReport.h>
+#include <Guid/FmpCapsule.h>
+#include <Guid/CapsuleVendor.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/PrintLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/CapsuleLib.h>
+
+#include <IndustryStandard/WindowsUxCapsule.h>
+
+/**
+ This routine is called to clear CapsuleOnDisk Relocation Info variable.
+ Total Capsule On Disk length is recorded in this variable
+
+ @retval EFI_SUCCESS Capsule On Disk flags are cleared
+
+**/
+EFI_STATUS
+CoDClearCapsuleRelocationInfo(
+ VOID
+ );
+
+/**
+ Get current capsule last variable index.
+
+ @return Current capsule last variable index.
+ @retval -1 No current capsule last variable.
+**/
+INTN
+GetCurrentCapsuleLastIndex (
+ VOID
+ )
+{
+ UINTN Size;
+ CHAR16 CapsuleLastStr[sizeof("Capsule####")];
+ EFI_STATUS Status;
+ UINT16 CurrentIndex;
+
+ Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
+ Status = gRT->GetVariable(
+ L"CapsuleLast",
+ &gEfiCapsuleReportGuid,
+ NULL,
+ &Size,
+ CapsuleLastStr
+ );
+ if (EFI_ERROR(Status)) {
+ return -1;
+ }
+ CurrentIndex = (UINT16)StrHexToUintn(&CapsuleLastStr[sizeof("Capsule") - 1]);
+ return CurrentIndex;
+}
+
+/**
+ Get a new capsule status variable index.
+
+ @return A new capsule status variable index.
+ @retval 0 No new capsule status variable index. Rolling over.
+**/
+INTN
+GetNewCapsuleResultIndex (
+ VOID
+ )
+{
+ INTN CurrentIndex;
+
+ CurrentIndex = GetCurrentCapsuleLastIndex();
+ if (CurrentIndex >= PcdGet16(PcdCapsuleMax)) {
+ DEBUG((DEBUG_INFO, " CapsuleResult variable Rolling Over!\n"));
+ return 0;
+ }
+
+ return CurrentIndex + 1;
+}
+
+/**
+ Write a new capsule status variable.
+
+ @param[in] CapsuleResult The capsule status variable
+ @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes
+
+ @retval EFI_SUCCESS The capsule status variable is recorded.
+ @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
+**/
+EFI_STATUS
+WriteNewCapsuleResultVariable (
+ IN VOID *CapsuleResult,
+ IN UINTN CapsuleResultSize
+ )
+{
+ INTN CapsuleResultIndex;
+ CHAR16 CapsuleResultStr[sizeof("Capsule####")];
+ UINTN Size;
+ EFI_STATUS Status;
+
+ CapsuleResultIndex = GetNewCapsuleResultIndex();
+ DEBUG((DEBUG_INFO, "New CapsuleResultIndex - 0x%x\n", CapsuleResultIndex));
+
+ UnicodeSPrint(
+ CapsuleResultStr,
+ sizeof(CapsuleResultStr),
+ L"Capsule%04x",
+ CapsuleResultIndex
+ );
+
+ Status = gRT->SetVariable(
+ CapsuleResultStr,
+ &gEfiCapsuleReportGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ CapsuleResultSize,
+ CapsuleResult
+ );
+ if (!EFI_ERROR(Status)) {
+ Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
+ DEBUG((DEBUG_INFO, "Set CapsuleLast - %s\n", CapsuleResultStr));
+ Status = gRT->SetVariable(
+ L"CapsuleLast",
+ &gEfiCapsuleReportGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ Size,
+ CapsuleResultStr
+ );
+ }
+
+ return Status;
+}
+
+/**
+ Record capsule status variable and to local cache.
+
+ @param[in] CapsuleHeader The capsule image header
+ @param[in] CapsuleStatus The capsule process stauts
+
+ @retval EFI_SUCCESS The capsule status variable is recorded.
+ @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
+**/
+EFI_STATUS
+RecordCapsuleStatusVariable (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN EFI_STATUS CapsuleStatus
+ )
+{
+ EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultVariable;
+ EFI_STATUS Status;
+
+ CapsuleResultVariable.VariableTotalSize = sizeof(CapsuleResultVariable);
+ CapsuleResultVariable.Reserved = 0;
+ CopyGuid (&CapsuleResultVariable.CapsuleGuid, &CapsuleHeader->CapsuleGuid);
+ ZeroMem(&CapsuleResultVariable.CapsuleProcessed, sizeof(CapsuleResultVariable.CapsuleProcessed));
+ gRT->GetTime(&CapsuleResultVariable.CapsuleProcessed, NULL);
+ CapsuleResultVariable.CapsuleStatus = CapsuleStatus;
+
+ Status = EFI_SUCCESS;
+ if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
+ Status = WriteNewCapsuleResultVariable(&CapsuleResultVariable, sizeof(CapsuleResultVariable));
+ }
+ return Status;
+}
+
+/**
+ Record FMP capsule status variable and to local cache.
+
+ @param[in] CapsuleHeader The capsule image header
+ @param[in] CapsuleStatus The capsule process stauts
+ @param[in] PayloadIndex FMP payload index
+ @param[in] ImageHeader FMP image header
+ @param[in] FmpDevicePath DevicePath associated with the FMP producer
+ @param[in] CapFileName Capsule file name
+
+ @retval EFI_SUCCESS The capsule status variable is recorded.
+ @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
+**/
+EFI_STATUS
+RecordFmpCapsuleStatusVariable (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN EFI_STATUS CapsuleStatus,
+ IN UINTN PayloadIndex,
+ IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
+ IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath, OPTIONAL
+ IN CHAR16 *CapFileName OPTIONAL
+ )
+{
+ EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResultVariableHeader;
+ EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultVariableFmp;
+ EFI_STATUS Status;
+ UINT8 *CapsuleResultVariable;
+ UINTN CapsuleResultVariableSize;
+ CHAR16 *DevicePathStr;
+ UINTN DevicePathStrSize;
+ UINTN CapFileNameSize;
+
+ DevicePathStr = NULL;
+ CapFileNameSize = sizeof(CHAR16);
+
+ if (FmpDevicePath != NULL) {
+ DevicePathStr = ConvertDevicePathToText (FmpDevicePath, FALSE, FALSE);
+ }
+ if (DevicePathStr != NULL) {
+ DevicePathStrSize = StrSize(DevicePathStr);
+ } else {
+ DevicePathStrSize = sizeof(CHAR16);
+ }
+
+ if (CapFileName != NULL) {
+ CapFileNameSize = StrSize(CapFileName);
+ }
+
+ //
+ // Allocate room for CapsuleFileName.
+ //
+ CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + CapFileNameSize + DevicePathStrSize;
+
+ CapsuleResultVariable = AllocateZeroPool (CapsuleResultVariableSize);
+ if (CapsuleResultVariable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CapsuleResultVariableHeader = (VOID *)CapsuleResultVariable;
+ CapsuleResultVariableHeader->VariableTotalSize = (UINT32)CapsuleResultVariableSize;
+ CapsuleResultVariableHeader->Reserved = 0;
+ CopyGuid(&CapsuleResultVariableHeader->CapsuleGuid, &CapsuleHeader->CapsuleGuid);
+ ZeroMem(&CapsuleResultVariableHeader->CapsuleProcessed, sizeof(CapsuleResultVariableHeader->CapsuleProcessed));
+ gRT->GetTime(&CapsuleResultVariableHeader->CapsuleProcessed, NULL);
+ CapsuleResultVariableHeader->CapsuleStatus = CapsuleStatus;
+
+ CapsuleResultVariableFmp = (VOID *)(CapsuleResultVariable + sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER));
+ CapsuleResultVariableFmp->Version = 0x1;
+ CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex;
+ CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex;
+ CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId);
+
+ if (CapFileName != NULL) {
+ CopyMem((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP), CapFileName, CapFileNameSize);
+ }
+
+ if (DevicePathStr != NULL) {
+ CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + CapFileNameSize, DevicePathStr, DevicePathStrSize);
+ FreePool (DevicePathStr);
+ DevicePathStr = NULL;
+ }
+
+ Status = EFI_SUCCESS;
+ if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
+ Status = WriteNewCapsuleResultVariable(CapsuleResultVariable, CapsuleResultVariableSize);
+ }
+ FreePool (CapsuleResultVariable);
+ return Status;
+}
+
+/**
+ Initialize CapsuleMax variables.
+**/
+VOID
+InitCapsuleMaxVariable (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ CHAR16 CapsuleMaxStr[sizeof("Capsule####")];
+ EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
+
+ UnicodeSPrint(
+ CapsuleMaxStr,
+ sizeof(CapsuleMaxStr),
+ L"Capsule%04x",
+ PcdGet16(PcdCapsuleMax)
+ );
+
+ Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
+ Status = gRT->SetVariable(
+ L"CapsuleMax",
+ &gEfiCapsuleReportGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ Size,
+ CapsuleMaxStr
+ );
+ if (!EFI_ERROR(Status)) {
+ // Lock it per UEFI spec.
+ Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
+ if (!EFI_ERROR(Status)) {
+ Status = VariableLock->RequestToLock(VariableLock, L"CapsuleMax", &gEfiCapsuleReportGuid);
+ ASSERT_EFI_ERROR(Status);
+ }
+ }
+}
+
+/**
+ Initialize CapsuleLast variables.
+**/
+VOID
+InitCapsuleLastVariable (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MODE BootMode;
+ EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
+ VOID *CapsuleResult;
+ UINTN Size;
+ CHAR16 CapsuleLastStr[sizeof("Capsule####")];
+
+ BootMode = GetBootModeHob();
+ if (BootMode == BOOT_ON_FLASH_UPDATE) {
+ Status = gRT->SetVariable(
+ L"CapsuleLast",
+ &gEfiCapsuleReportGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ 0,
+ NULL
+ );
+ // Do not lock it because it will be updated later.
+ } else {
+ //
+ // Check if OS/APP cleared L"Capsule####"
+ //
+ ZeroMem(CapsuleLastStr, sizeof(CapsuleLastStr));
+ Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
+ Status = gRT->GetVariable(
+ L"CapsuleLast",
+ &gEfiCapsuleReportGuid,
+ NULL,
+ &Size,
+ CapsuleLastStr
+ );
+ if (!EFI_ERROR(Status)) {
+ //
+ // L"CapsuleLast" is got, check if data is there.
+ //
+ Status = GetVariable2 (
+ CapsuleLastStr,
+ &gEfiCapsuleReportGuid,
+ (VOID **) &CapsuleResult,
+ NULL
+ );
+ if (EFI_ERROR(Status)) {
+ //
+ // If no data, delete L"CapsuleLast"
+ //
+ Status = gRT->SetVariable(
+ L"CapsuleLast",
+ &gEfiCapsuleReportGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ 0,
+ NULL
+ );
+ } else {
+ if (CapsuleResult != NULL) {
+ FreePool (CapsuleResult);
+ }
+ }
+ }
+
+ // Lock it in normal boot path per UEFI spec.
+ Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
+ if (!EFI_ERROR(Status)) {
+ Status = VariableLock->RequestToLock(VariableLock, L"CapsuleLast", &gEfiCapsuleReportGuid);
+ ASSERT_EFI_ERROR(Status);
+ }
+ }
+}
+
+/**
+ Initialize capsule update variables.
+**/
+VOID
+InitCapsuleUpdateVariable (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ CHAR16 CapsuleVarName[30];
+ CHAR16 *TempVarName;
+
+ //
+ // Clear all the capsule variables CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
+ // as early as possible which will avoid the next time boot after the capsule update
+ // will still into the capsule loop
+ //
+ StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), EFI_CAPSULE_VARIABLE_NAME);
+ TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
+ Index = 0;
+ while (TRUE) {
+ if (Index > 0) {
+ UnicodeValueToStringS (
+ TempVarName,
+ sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),
+ 0,
+ Index,
+ 0
+ );
+ }
+ Status = gRT->SetVariable (
+ CapsuleVarName,
+ &gEfiCapsuleVendorGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ (VOID *)NULL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // There is no capsule variables, quit
+ //
+ break;
+ }
+ Index++;
+ }
+}
+
+/**
+ Initialize capsule relocation info variable.
+**/
+VOID
+InitCapsuleRelocationInfo (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
+
+ CoDClearCapsuleRelocationInfo();
+
+ //
+ // Unlock Capsule On Disk relocation Info variable only when Capsule On Disk flag is enabled
+ //
+ if (!CoDCheckCapsuleOnDiskFlag()) {
+ Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
+ if (!EFI_ERROR (Status)) {
+ Status = VariableLock->RequestToLock (VariableLock, COD_RELOCATION_INFO_VAR_NAME, &gEfiCapsuleVendorGuid);
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+}
+
+/**
+ Initialize capsule related variables.
+**/
+VOID
+InitCapsuleVariable (
+ VOID
+ )
+{
+ InitCapsuleUpdateVariable();
+ InitCapsuleMaxVariable();
+ InitCapsuleLastVariable();
+ InitCapsuleRelocationInfo();
+
+ //
+ // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"
+ // to check status and delete them.
+ //
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c
new file mode 100644
index 00000000..4cbc3819
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c
@@ -0,0 +1,69 @@
+/** @file
+ DXE capsule report related function.
+ Dummy function for runtime module, because CapsuleDxeRuntime
+ does not need record capsule status variable.
+
+ Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Protocol/FirmwareManagement.h>
+#include <Guid/FmpCapsule.h>
+#include <Library/CapsuleLib.h>
+
+/**
+ Record capsule status variable and to local cache.
+
+ @param[in] CapsuleHeader The capsule image header
+ @param[in] CapsuleStatus The capsule process stauts
+
+ @retval EFI_SUCCESS The capsule status variable is recorded.
+ @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
+**/
+EFI_STATUS
+RecordCapsuleStatusVariable (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN EFI_STATUS CapsuleStatus
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Record FMP capsule status variable and to local cache.
+
+ @param[in] CapsuleHeader The capsule image header
+ @param[in] CapsuleStatus The capsule process stauts
+ @param[in] PayloadIndex FMP payload index
+ @param[in] ImageHeader FMP image header
+ @param[in] FmpDevicePath DevicePath associated with the FMP producer
+ @param[in] CapFileName Capsule file name
+
+ @retval EFI_SUCCESS The capsule status variable is recorded.
+ @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
+**/
+EFI_STATUS
+RecordFmpCapsuleStatusVariable (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,
+ IN EFI_STATUS CapsuleStatus,
+ IN UINTN PayloadIndex,
+ IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
+ IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath, OPTIONAL
+ IN CHAR16 *CapFileName OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Initialize capsule related variables.
+**/
+VOID
+InitCapsuleVariable (
+ VOID
+ )
+{
+ return;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c
new file mode 100644
index 00000000..6a780352
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c
@@ -0,0 +1,174 @@
+/** @file
+ Capsule library runtime support.
+
+ Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <Guid/FmpCapsule.h>
+#include <Guid/SystemResourceTable.h>
+#include <Guid/EventGroup.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+extern EFI_SYSTEM_RESOURCE_TABLE *mEsrtTable;
+extern BOOLEAN mIsVirtualAddrConverted;
+EFI_EVENT mDxeRuntimeCapsuleLibVirtualAddressChangeEvent = NULL;
+EFI_EVENT mDxeRuntimeCapsuleLibReadyToBootEvent = NULL;
+
+/**
+ Convert EsrtTable physical address to virtual address.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context The pointer to the notification function's context, which
+ is implementation-dependent.
+**/
+VOID
+EFIAPI
+DxeCapsuleLibVirtualAddressChangeEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gRT->ConvertPointer (EFI_OPTIONAL_PTR, (VOID **)&mEsrtTable);
+ mIsVirtualAddrConverted = TRUE;
+}
+
+/**
+ Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT.
+
+ @param[in] Event The Event that is being processed.
+ @param[in] Context The Event Context.
+
+**/
+STATIC
+VOID
+EFIAPI
+DxeCapsuleLibReadyToBootEventNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UINTN Index;
+ EFI_CONFIGURATION_TABLE *ConfigEntry;
+ EFI_SYSTEM_RESOURCE_TABLE *EsrtTable;
+
+ //
+ // Get Esrt table first
+ //
+ ConfigEntry = gST->ConfigurationTable;
+ for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
+ if (CompareGuid(&gEfiSystemResourceTableGuid, &ConfigEntry->VendorGuid)) {
+ break;
+ }
+ ConfigEntry++;
+ }
+
+ //
+ // If no Esrt table installed in Configure Table
+ //
+ if (Index < gST->NumberOfTableEntries) {
+ //
+ // Search Esrt to check given capsule is qualified
+ //
+ EsrtTable = (EFI_SYSTEM_RESOURCE_TABLE *) ConfigEntry->VendorTable;
+
+ mEsrtTable = AllocateRuntimeCopyPool (
+ sizeof (EFI_SYSTEM_RESOURCE_TABLE) +
+ EsrtTable->FwResourceCount * sizeof (EFI_SYSTEM_RESOURCE_ENTRY),
+ EsrtTable);
+ ASSERT (mEsrtTable != NULL);
+
+ //
+ // Set FwResourceCountMax to a sane value.
+ //
+ mEsrtTable->FwResourceCountMax = mEsrtTable->FwResourceCount;
+ }
+}
+
+/**
+ The constructor function hook VirtualAddressChange event to use ESRT table as capsule routing table.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The constructor successfully .
+**/
+EFI_STATUS
+EFIAPI
+DxeRuntimeCapsuleLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Make sure we can handle virtual address changes.
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ DxeCapsuleLibVirtualAddressChangeEvent,
+ NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &mDxeRuntimeCapsuleLibVirtualAddressChangeEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Register notify function to cache the FMP capsule GUIDs at ReadyToBoot.
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ DxeCapsuleLibReadyToBootEventNotify,
+ NULL,
+ &gEfiEventReadyToBootGuid,
+ &mDxeRuntimeCapsuleLibReadyToBootEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The destructor function closes the VirtualAddressChange event.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The destructor completed successfully.
+**/
+EFI_STATUS
+EFIAPI
+DxeRuntimeCapsuleLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Close the VirtualAddressChange event.
+ //
+ Status = gBS->CloseEvent (mDxeRuntimeCapsuleLibVirtualAddressChangeEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Close the ReadyToBoot event.
+ //
+ Status = gBS->CloseEvent (mDxeRuntimeCapsuleLibReadyToBootEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
new file mode 100644
index 00000000..f9e0a97b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
@@ -0,0 +1,74 @@
+## @file
+# Capsule library instance for DXE_RUNTIME_DRIVER.
+#
+# Capsule library instance for DXE_RUNTIME_DRIVER module types.
+#
+# Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeRuntimeCapsuleLib
+ MODULE_UNI_FILE = DxeRuntimeCapsuleLib.uni
+ FILE_GUID = 19BE1E4B-1A9A-44c1-8F12-32DD0470516A
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = CapsuleLib|DXE_RUNTIME_DRIVER
+ CONSTRUCTOR = DxeCapsuleLibConstructor
+ CONSTRUCTOR = DxeRuntimeCapsuleLibConstructor
+ DESTRUCTOR = DxeCapsuleLibDestructor
+ DESTRUCTOR = DxeRuntimeCapsuleLibDestructor
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeCapsuleLib.c
+ DxeCapsuleProcessLibNull.c
+ DxeCapsuleReportLibNull.c
+ DxeCapsuleRuntime.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ DxeServicesTableLib
+ UefiBootServicesTableLib
+ DevicePathLib
+ ReportStatusCodeLib
+ PrintLib
+ HobLib
+ BmpSupportLib
+
+
+[Protocols]
+ gEsrtManagementProtocolGuid ## CONSUMES
+ gEfiFirmwareManagementProtocolGuid ## CONSUMES
+ gEdkiiVariableLockProtocolGuid ## SOMETIMES_CONSUMES
+ gEdkiiFirmwareManagementProgressProtocolGuid ## SOMETIMES_CONSUMES
+
+[Guids]
+ gEfiFmpCapsuleGuid ## SOMETIMES_CONSUMES ## GUID
+ gWindowsUxCapsuleGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiSystemResourceTableGuid ## SOMETIMES_CONSUMES ## GUID
+ ## SOMETIMES_CONSUMES ## Variable:L"CapsuleMax"
+ ## SOMETIMES_PRODUCES ## Variable:L"CapsuleMax"
+ gEfiCapsuleReportGuid
+ gEfiCapsuleVendorGuid ## SOMETIMES_CONSUMES ## Variable:L"CapsuleUpdateData"
+ gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event
+ gEfiEventReadyToBootGuid ## CONSUMES ## Event
+ gEfiEventVirtualAddressChangeGuid ## CONSUMES ## Event
+ gEdkiiCapsuleOnDiskNameGuid ## SOMETIMES_CONSUMES ## GUID
+
+[Depex]
+ gEfiVariableWriteArchProtocolGuid
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni
new file mode 100644
index 00000000..0e90cc3d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// Capsule library instance for DXE_RUNTIME_DRIVER.
+//
+// Capsule library instance for DXE_RUNTIME_DRIVER module types.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Capsule Support Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Capsule library instance for DXE_RUNTIME_DRIVER module types."
+