diff options
| author | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
|---|---|---|
| committer | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
| commit | 7e2ccccace24636f29ddc210b94606abd4c7e42b (patch) | |
| tree | 545d43ea12671048956cafeb12303e84d601e571 /MdeModulePkg/Library/DxeCapsuleLibFmp | |
| parent | 27b044605cd5f6b33a3d231576003850b3fe305b (diff) | |
| download | edk2-trunk.tar.xz | |
Renormalized end-of-lines from master@27b044605cd5f6b33a3d231576003850b3fe305btrunk
Diffstat (limited to 'MdeModulePkg/Library/DxeCapsuleLibFmp')
12 files changed, 5521 insertions, 5521 deletions
diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c index 41b3282d29..00e975803a 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c @@ -1,2013 +1,2013 @@ -/** @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_BEGIN ();
- 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"));
- }
-
- DEBUG_CODE_END ();
-
- 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_BEGIN ();
- CHAR16 *DevicePathStr1;
-
- DevicePathStr1 = ConvertDevicePathToText (CurFullPath, TRUE, TRUE);
- if (DevicePathStr1 != NULL) {
- DEBUG ((DEBUG_INFO, "Full device path %s\n", DevicePathStr1));
- FreePool (DevicePathStr1);
- }
-
- DEBUG_CODE_END ();
-
- //
- // 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_BEGIN ();
- 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"));
- }
-
- DEBUG_CODE_END ();
-
- 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_BEGIN ();
- 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));
- }
-
- DEBUG_CODE_END ();
-
-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;
- UINTN CapsulesToProcess;
-
- 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);
- }
-
- // If Persist Across Reset isn't supported, skip the file name strings capsule
- if (!FeaturePcdGet (PcdSupportUpdateCapsuleReset)) {
- CapsulesToProcess = CapsuleOnDiskNum;
- goto BuildGather;
- }
-
- CapsulesToProcess = CapsuleOnDiskNum + 1;
-
- 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
- //
-BuildGather:
- Status = BuildGatherList (CapsuleBuffer, CapsuleSize, CapsulesToProcess, &BlockDescriptors);
- if (EFI_ERROR (Status) || (BlockDescriptors == NULL)) {
- FreePool (CapsuleBuffer);
- FreePool (CapsuleSize);
- if (FileNameCapsule != NULL) {
- FreePool (FileNameCapsule);
- }
-
- return EFI_OUT_OF_RESOURCES;
- }
-
- //
- // 4. Call UpdateCapsule() service
- //
- Status = gRT->UpdateCapsule (
- (EFI_CAPSULE_HEADER **)CapsuleBuffer,
- CapsulesToProcess,
- (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;
-}
+/** @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_BEGIN (); + 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")); + } + + DEBUG_CODE_END (); + + 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_BEGIN (); + CHAR16 *DevicePathStr1; + + DevicePathStr1 = ConvertDevicePathToText (CurFullPath, TRUE, TRUE); + if (DevicePathStr1 != NULL) { + DEBUG ((DEBUG_INFO, "Full device path %s\n", DevicePathStr1)); + FreePool (DevicePathStr1); + } + + DEBUG_CODE_END (); + + // + // 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_BEGIN (); + 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")); + } + + DEBUG_CODE_END (); + + 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_BEGIN (); + 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)); + } + + DEBUG_CODE_END (); + +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; + UINTN CapsulesToProcess; + + 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); + } + + // If Persist Across Reset isn't supported, skip the file name strings capsule + if (!FeaturePcdGet (PcdSupportUpdateCapsuleReset)) { + CapsulesToProcess = CapsuleOnDiskNum; + goto BuildGather; + } + + CapsulesToProcess = CapsuleOnDiskNum + 1; + + 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 + // +BuildGather: + Status = BuildGatherList (CapsuleBuffer, CapsuleSize, CapsulesToProcess, &BlockDescriptors); + if (EFI_ERROR (Status) || (BlockDescriptors == NULL)) { + FreePool (CapsuleBuffer); + FreePool (CapsuleSize); + if (FileNameCapsule != NULL) { + FreePool (FileNameCapsule); + } + + return EFI_OUT_OF_RESOURCES; + } + + // + // 4. Call UpdateCapsule() service + // + Status = gRT->UpdateCapsule ( + (EFI_CAPSULE_HEADER **)CapsuleBuffer, + CapsulesToProcess, + (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/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h b/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h index 39f9596d50..ef52199e9b 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h @@ -1,75 +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_
+/** @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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c index 2433c76a8c..c17231d8b1 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c @@ -1,1691 +1,1691 @@ -/** @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 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 (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;
-}
+/** @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 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 (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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf index 095e062a6e..1dd8b9a6fe 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf @@ -1,102 +1,102 @@ -## @file
-# Capsule library instance for DXE_DRIVER.
-#
-# Capsule library instance for DXE_DRIVER module types.
-#
-# Copyright (c) 2016 - 2021, 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
- VariablePolicyHelperLib
-
-[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
-
-[FeaturePcd]
- gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset ## CONSUMES
-
-[Protocols]
- gEsrtManagementProtocolGuid ## CONSUMES
- gEfiFirmwareManagementProtocolGuid ## CONSUMES
- gEdkiiFirmwareManagementProgressProtocolGuid ## SOMETIMES_CONSUMES
- gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES
- gEfiBlockIoProtocolGuid ## CONSUMES
- gEfiDiskIoProtocolGuid ## CONSUMES
- gEdkiiVariablePolicyProtocolGuid ## 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
+## @file +# Capsule library instance for DXE_DRIVER. +# +# Capsule library instance for DXE_DRIVER module types. +# +# Copyright (c) 2016 - 2021, 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 + VariablePolicyHelperLib + +[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 + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset ## CONSUMES + +[Protocols] + gEsrtManagementProtocolGuid ## CONSUMES + gEfiFirmwareManagementProtocolGuid ## CONSUMES + gEdkiiFirmwareManagementProgressProtocolGuid ## SOMETIMES_CONSUMES + gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES + gEfiBlockIoProtocolGuid ## CONSUMES + gEfiDiskIoProtocolGuid ## CONSUMES + gEdkiiVariablePolicyProtocolGuid ## 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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni index 03c61c55c6..cf82e3b306 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni @@ -1,16 +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."
-
+// /** @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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c index 2a38a3d95b..2ee59e4c68 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c @@ -1,703 +1,703 @@ -/** @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;
-}
+/** @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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c index 9054ae446e..33f99e7433 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c @@ -1,70 +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;
-}
+/** @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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c index a1523428cd..74b6726cef 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c @@ -1,518 +1,518 @@ -/** @file
- DXE capsule report related function.
-
- Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <PiDxe.h>
-#include <Protocol/FirmwareManagement.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 <Library/VariablePolicyHelperLib.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;
-}
-
-/**
- Lock Variable by variable policy.
-
- @param[in] VariableGuid The Guid of the variable to be locked
- @param[in] VariableName The name of the variable to be locked
- @param[in] VariablePolicy The pointer of variable lock policy
-**/
-VOID
-LockVariable (
- IN CONST EFI_GUID VariableGuid,
- IN CHAR16 *VariableName,
- IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy
- )
-{
- EFI_STATUS Status;
-
- // Set the policies to protect the target variables
- Status = RegisterBasicVariablePolicy (
- VariablePolicy,
- &VariableGuid,
- VariableName,
- VARIABLE_POLICY_NO_MIN_SIZE,
- VARIABLE_POLICY_NO_MAX_SIZE,
- VARIABLE_POLICY_NO_MUST_ATTR,
- VARIABLE_POLICY_NO_CANT_ATTR,
- VARIABLE_POLICY_TYPE_LOCK_NOW
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((
- DEBUG_ERROR,
- "DxeCapsuleLibFmp: Failed to lock variable %g %s. Status = %r\n",
- &VariableGuid,
- VariableName,
- Status
- ));
- ASSERT_EFI_ERROR (Status);
- }
-}
-
-/**
- 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.
-
- @param[in] VariablePolicy The pointer of variable lock policy
-**/
-VOID
-InitCapsuleMaxVariable (
- EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy
- )
-{
- EFI_STATUS Status;
- UINTN Size;
- CHAR16 CapsuleMaxStr[sizeof ("Capsule####")];
-
- 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.
- LockVariable (gEfiCapsuleReportGuid, L"CapsuleMax", VariablePolicy);
- }
-}
-
-/**
- Initialize CapsuleLast variables.
-
- @param[in] VariablePolicy The pointer of variable lock policy
-**/
-VOID
-InitCapsuleLastVariable (
- EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy
- )
-{
- EFI_STATUS Status;
- EFI_BOOT_MODE BootMode;
- 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.
- LockVariable (gEfiCapsuleReportGuid, L"CapsuleLast", VariablePolicy);
- }
-}
-
-/**
- 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.
-
- @param[in] VariablePolicy The pointer of variable lock policy
-**/
-VOID
-InitCapsuleRelocationInfo (
- EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy
- )
-{
- CoDClearCapsuleRelocationInfo ();
-
- //
- // Unlock Capsule On Disk relocation Info variable only when Capsule On Disk flag is enabled
- //
- if (!CoDCheckCapsuleOnDiskFlag ()) {
- LockVariable (gEfiCapsuleVendorGuid, COD_RELOCATION_INFO_VAR_NAME, VariablePolicy);
- }
-}
-
-/**
- Initialize capsule related variables.
-**/
-VOID
-InitCapsuleVariable (
- VOID
- )
-{
- EFI_STATUS Status;
- EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy;
-
- // Locate the VariablePolicy protocol
- Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy);
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "DxeCapsuleReportLib %a - Could not locate VariablePolicy protocol! %r\n", __func__, Status));
- ASSERT_EFI_ERROR (Status);
- }
-
- InitCapsuleUpdateVariable ();
- InitCapsuleMaxVariable (VariablePolicy);
- InitCapsuleLastVariable (VariablePolicy);
- InitCapsuleRelocationInfo (VariablePolicy);
-
- //
- // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"
- // to check status and delete them.
- //
-}
+/** @file + DXE capsule report related function. + + Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> +#include <Protocol/FirmwareManagement.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 <Library/VariablePolicyHelperLib.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; +} + +/** + Lock Variable by variable policy. + + @param[in] VariableGuid The Guid of the variable to be locked + @param[in] VariableName The name of the variable to be locked + @param[in] VariablePolicy The pointer of variable lock policy +**/ +VOID +LockVariable ( + IN CONST EFI_GUID VariableGuid, + IN CHAR16 *VariableName, + IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy + ) +{ + EFI_STATUS Status; + + // Set the policies to protect the target variables + Status = RegisterBasicVariablePolicy ( + VariablePolicy, + &VariableGuid, + VariableName, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "DxeCapsuleLibFmp: Failed to lock variable %g %s. Status = %r\n", + &VariableGuid, + VariableName, + Status + )); + ASSERT_EFI_ERROR (Status); + } +} + +/** + 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. + + @param[in] VariablePolicy The pointer of variable lock policy +**/ +VOID +InitCapsuleMaxVariable ( + EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy + ) +{ + EFI_STATUS Status; + UINTN Size; + CHAR16 CapsuleMaxStr[sizeof ("Capsule####")]; + + 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. + LockVariable (gEfiCapsuleReportGuid, L"CapsuleMax", VariablePolicy); + } +} + +/** + Initialize CapsuleLast variables. + + @param[in] VariablePolicy The pointer of variable lock policy +**/ +VOID +InitCapsuleLastVariable ( + EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + 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. + LockVariable (gEfiCapsuleReportGuid, L"CapsuleLast", VariablePolicy); + } +} + +/** + 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. + + @param[in] VariablePolicy The pointer of variable lock policy +**/ +VOID +InitCapsuleRelocationInfo ( + EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy + ) +{ + CoDClearCapsuleRelocationInfo (); + + // + // Unlock Capsule On Disk relocation Info variable only when Capsule On Disk flag is enabled + // + if (!CoDCheckCapsuleOnDiskFlag ()) { + LockVariable (gEfiCapsuleVendorGuid, COD_RELOCATION_INFO_VAR_NAME, VariablePolicy); + } +} + +/** + Initialize capsule related variables. +**/ +VOID +InitCapsuleVariable ( + VOID + ) +{ + EFI_STATUS Status; + EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy; + + // Locate the VariablePolicy protocol + Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DxeCapsuleReportLib %a - Could not locate VariablePolicy protocol! %r\n", __func__, Status)); + ASSERT_EFI_ERROR (Status); + } + + InitCapsuleUpdateVariable (); + InitCapsuleMaxVariable (VariablePolicy); + InitCapsuleLastVariable (VariablePolicy); + InitCapsuleRelocationInfo (VariablePolicy); + + // + // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast" + // to check status and delete them. + // +} diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c index b425e99e67..7a94afbbbd 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c @@ -1,69 +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;
-}
+/** @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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c index 44f30c16c2..6b847a5c44 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c @@ -1,174 +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;
-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);
-}
-
-/**
- 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;
-}
+/** @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; +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); +} + +/** + 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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf index bf56f4623f..f9e0a97b57 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf @@ -1,74 +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
+## @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/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni index 0e90cc3d0e..dac12f1878 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni @@ -1,16 +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."
-
+// /** @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." + |
