/** @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; | |
} |