diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp')
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."
+
|