summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c')
-rw-r--r--MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c4026
1 files changed, 2013 insertions, 2013 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;
+}