mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-27 16:58:09 +01:00
6b33696c93
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
1976 lines
61 KiB
C
1976 lines
61 KiB
C
/** @file
|
|
The implementation supports Capusle on Disk.
|
|
|
|
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "CapsuleOnDisk.h"
|
|
|
|
/**
|
|
Return if this capsule is a capsule name capsule, based upon CapsuleHeader.
|
|
|
|
@param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
|
|
|
|
@retval TRUE It is a capsule name capsule.
|
|
@retval FALSE It is not a capsule name capsule.
|
|
**/
|
|
BOOLEAN
|
|
IsCapsuleNameCapsule (
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
|
);
|
|
|
|
/**
|
|
Check the integrity of the capsule name capsule.
|
|
If the capsule is vaild, return the physical address of each capsule name string.
|
|
|
|
This routine assumes the capsule has been validated by IsValidCapsuleHeader(), so
|
|
capsule memory overflow is not going to happen in this routine.
|
|
|
|
@param[in] CapsuleHeader Pointer to the capsule header of a capsule name capsule.
|
|
@param[out] CapsuleNameNum Number of capsule name.
|
|
|
|
@retval NULL Capsule name capsule is not valid.
|
|
@retval CapsuleNameBuf Array of capsule name physical address.
|
|
|
|
**/
|
|
EFI_PHYSICAL_ADDRESS *
|
|
ValidateCapsuleNameCapsuleIntegrity (
|
|
IN EFI_CAPSULE_HEADER *CapsuleHeader,
|
|
OUT UINTN *CapsuleNameNum
|
|
)
|
|
{
|
|
UINT8 *CapsuleNamePtr;
|
|
UINT8 *CapsuleNameBufStart;
|
|
UINT8 *CapsuleNameBufEnd;
|
|
UINTN Index;
|
|
UINTN StringSize;
|
|
EFI_PHYSICAL_ADDRESS *CapsuleNameBuf;
|
|
|
|
if (!IsCapsuleNameCapsule (CapsuleHeader)) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Total string size must be even.
|
|
//
|
|
if (((CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize) & BIT0) != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
*CapsuleNameNum = 0;
|
|
Index = 0;
|
|
CapsuleNameBufStart = (UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize;
|
|
|
|
//
|
|
// If strings are not aligned on a 16-bit boundary, reallocate memory for it.
|
|
//
|
|
if (((UINTN) CapsuleNameBufStart & BIT0) != 0) {
|
|
CapsuleNameBufStart = AllocateCopyPool(CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize, CapsuleNameBufStart);
|
|
if (CapsuleNameBufStart == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
CapsuleNameBufEnd = CapsuleNameBufStart + CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;
|
|
|
|
CapsuleNamePtr = CapsuleNameBufStart;
|
|
while (CapsuleNamePtr < CapsuleNameBufEnd) {
|
|
StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));
|
|
CapsuleNamePtr += StringSize;
|
|
(*CapsuleNameNum) ++;
|
|
}
|
|
|
|
//
|
|
// Integrity check.
|
|
//
|
|
if (CapsuleNamePtr != CapsuleNameBufEnd) {
|
|
if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {
|
|
FreePool(CapsuleNameBufStart);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CapsuleNameBuf = AllocatePool (*CapsuleNameNum * sizeof (EFI_PHYSICAL_ADDRESS));
|
|
if (CapsuleNameBuf == NULL) {
|
|
if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {
|
|
FreePool(CapsuleNameBufStart);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CapsuleNamePtr = CapsuleNameBufStart;
|
|
while (CapsuleNamePtr < CapsuleNameBufEnd) {
|
|
StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));
|
|
CapsuleNameBuf[Index] = (EFI_PHYSICAL_ADDRESS)(UINTN) CapsuleNamePtr;
|
|
CapsuleNamePtr += StringSize;
|
|
Index ++;
|
|
}
|
|
|
|
return CapsuleNameBuf;
|
|
}
|
|
|
|
/**
|
|
This routine is called to upper case given unicode string.
|
|
|
|
@param[in] Str String to upper case
|
|
|
|
@retval upper cased string after process
|
|
|
|
**/
|
|
static
|
|
CHAR16 *
|
|
UpperCaseString (
|
|
IN CHAR16 *Str
|
|
)
|
|
{
|
|
CHAR16 *Cptr;
|
|
|
|
for (Cptr = Str; *Cptr != L'\0'; Cptr++) {
|
|
if (L'a' <= *Cptr && *Cptr <= L'z') {
|
|
*Cptr = *Cptr - L'a' + L'A';
|
|
}
|
|
}
|
|
|
|
return Str;
|
|
}
|
|
|
|
/**
|
|
This routine is used to return substring before period '.' or '\0'
|
|
Caller should respsonsible of substr space allocation & free
|
|
|
|
@param[in] Str String to check
|
|
@param[out] SubStr First part of string before period or '\0'
|
|
@param[out] SubStrLen Length of first part of string
|
|
|
|
**/
|
|
static
|
|
VOID
|
|
GetSubStringBeforePeriod (
|
|
IN CHAR16 *Str,
|
|
OUT CHAR16 *SubStr,
|
|
OUT UINTN *SubStrLen
|
|
)
|
|
{
|
|
UINTN Index;
|
|
for (Index = 0; Str[Index] != L'.' && Str[Index] != L'\0'; Index++) {
|
|
SubStr[Index] = Str[Index];
|
|
}
|
|
|
|
SubStr[Index] = L'\0';
|
|
*SubStrLen = Index;
|
|
}
|
|
|
|
/**
|
|
This routine pad the string in tail with input character.
|
|
|
|
@param[in] StrBuf Str buffer to be padded, should be enough room for
|
|
@param[in] PadLen Expected padding length
|
|
@param[in] Character Character used to pad
|
|
|
|
**/
|
|
static
|
|
VOID
|
|
PadStrInTail (
|
|
IN CHAR16 *StrBuf,
|
|
IN UINTN PadLen,
|
|
IN CHAR16 Character
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; StrBuf[Index] != L'\0'; Index++);
|
|
|
|
while(PadLen != 0) {
|
|
StrBuf[Index] = Character;
|
|
Index++;
|
|
PadLen--;
|
|
}
|
|
|
|
StrBuf[Index] = L'\0';
|
|
}
|
|
|
|
/**
|
|
This routine find the offset of the last period '.' of string. If No period exists
|
|
function FileNameExtension is set to L'\0'
|
|
|
|
@param[in] FileName File name to split between last period
|
|
@param[out] FileNameFirst First FileName before last period
|
|
@param[out] FileNameExtension FileName after last period
|
|
|
|
**/
|
|
static
|
|
VOID
|
|
SplitFileNameExtension (
|
|
IN CHAR16 *FileName,
|
|
OUT CHAR16 *FileNameFirst,
|
|
OUT CHAR16 *FileNameExtension
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN StringLen;
|
|
|
|
StringLen = StrnLenS(FileName, MAX_FILE_NAME_SIZE);
|
|
for (Index = StringLen; Index > 0 && FileName[Index] != L'.'; Index--);
|
|
|
|
//
|
|
// No period exists. No FileName Extension
|
|
//
|
|
if (Index == 0 && FileName[Index] != L'.') {
|
|
FileNameExtension[0] = L'\0';
|
|
Index = StringLen;
|
|
} else {
|
|
StrCpyS(FileNameExtension, MAX_FILE_NAME_SIZE, &FileName[Index+1]);
|
|
}
|
|
|
|
//
|
|
// Copy First file name
|
|
//
|
|
StrnCpyS(FileNameFirst, MAX_FILE_NAME_SIZE, FileName, Index);
|
|
FileNameFirst[Index] = L'\0';
|
|
}
|
|
|
|
/**
|
|
This routine is called to get all boot options in the order determnined by:
|
|
1. "OptionBuf"
|
|
2. "BootOrder"
|
|
|
|
@param[out] OptionBuf BootList buffer to all boot options returned
|
|
@param[out] OptionCount BootList count of all boot options returned
|
|
|
|
@retval EFI_SUCCESS There is no error when processing capsule
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetBootOptionInOrder(
|
|
OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf,
|
|
OUT UINTN *OptionCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN DataSize;
|
|
UINT16 BootNext;
|
|
CHAR16 BootOptionName[20];
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOrderOptionBuf;
|
|
UINTN BootOrderCount;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry;
|
|
UINTN BootNextCount;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *TempBuf;
|
|
|
|
BootOrderOptionBuf = NULL;
|
|
TempBuf = NULL;
|
|
BootNextCount = 0;
|
|
BootOrderCount = 0;
|
|
*OptionBuf = NULL;
|
|
*OptionCount = 0;
|
|
|
|
//
|
|
// First Get BootOption from "BootNext"
|
|
//
|
|
DataSize = sizeof(BootNext);
|
|
Status = gRT->GetVariable (
|
|
EFI_BOOT_NEXT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&DataSize,
|
|
(VOID *)&BootNext
|
|
);
|
|
//
|
|
// BootNext variable is a single UINT16
|
|
//
|
|
if (!EFI_ERROR(Status) && DataSize == sizeof(UINT16)) {
|
|
//
|
|
// Add the boot next boot option
|
|
//
|
|
UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootNext);
|
|
ZeroMem(&BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));
|
|
Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
BootNextCount = 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Second get BootOption from "BootOrder"
|
|
//
|
|
BootOrderOptionBuf = EfiBootManagerGetLoadOptions (&BootOrderCount, LoadOptionTypeBoot);
|
|
if (BootNextCount == 0 && BootOrderCount == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// At least one BootOption is found
|
|
//
|
|
TempBuf = AllocatePool(sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * (BootNextCount + BootOrderCount));
|
|
if (TempBuf != NULL) {
|
|
if (BootNextCount == 1) {
|
|
CopyMem(TempBuf, &BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));
|
|
}
|
|
|
|
if (BootOrderCount > 0) {
|
|
CopyMem(TempBuf + BootNextCount, BootOrderOptionBuf, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * BootOrderCount);
|
|
}
|
|
|
|
*OptionBuf = TempBuf;
|
|
*OptionCount = BootNextCount + BootOrderCount;
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
FreePool(BootOrderOptionBuf);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This routine is called to get boot option by OptionNumber.
|
|
|
|
@param[in] Number The OptionNumber of boot option
|
|
@param[out] OptionBuf BootList buffer to all boot options returned
|
|
|
|
@retval EFI_SUCCESS There is no error when getting boot option
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetBootOptionByNumber(
|
|
IN UINT16 Number,
|
|
OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 BootOptionName[20];
|
|
EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
|
|
|
|
UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", Number);
|
|
ZeroMem (&BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
|
|
Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootOption);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
*OptionBuf = AllocatePool (sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
|
|
if (*OptionBuf != NULL) {
|
|
CopyMem(*OptionBuf, &BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get Active EFI System Partition within GPT based on device path.
|
|
|
|
@param[in] DevicePath Device path to find a active EFI System Partition
|
|
@param[out] FsHandle BootList points to all boot options returned
|
|
|
|
@retval EFI_SUCCESS Active EFI System Partition is succesfully found
|
|
@retval EFI_NOT_FOUND No Active EFI System Partition is found
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetEfiSysPartitionFromDevPath(
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
OUT EFI_HANDLE *FsHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
HARDDRIVE_DEVICE_PATH *Hd;
|
|
EFI_HANDLE Handle;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
|
|
|
|
//
|
|
// Check if the device path contains GPT node
|
|
//
|
|
TempDevicePath = DevicePath;
|
|
while (!IsDevicePathEnd (TempDevicePath)) {
|
|
if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&
|
|
(DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {
|
|
Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;
|
|
if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {
|
|
break;
|
|
}
|
|
}
|
|
TempDevicePath = NextDevicePathNode (TempDevicePath);
|
|
}
|
|
|
|
if (!IsDevicePathEnd (TempDevicePath)) {
|
|
//
|
|
// Search for EFI system partition protocol on full device path in Boot Option
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);
|
|
|
|
//
|
|
// Search for simple file system on this handler
|
|
//
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
|
|
if (!EFI_ERROR(Status)) {
|
|
*FsHandle = Handle;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
This routine is called to get Simple File System protocol on the first EFI system partition found in
|
|
active boot option. The boot option list is detemined in order by
|
|
1. "BootNext"
|
|
2. "BootOrder"
|
|
|
|
@param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
|
|
device like USB can get enumerated.
|
|
@param[in, out] LoadOptionNumber On input, specify the boot option to get EFI system partition.
|
|
On output, return the OptionNumber of the boot option where EFI
|
|
system partition is got from.
|
|
@param[out] FsFsHandle Simple File System Protocol found on first active EFI system partition
|
|
|
|
@retval EFI_SUCCESS Simple File System protocol found for EFI system partition
|
|
@retval EFI_NOT_FOUND No Simple File System protocol found for EFI system partition
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetEfiSysPartitionFromActiveBootOption(
|
|
IN UINTN MaxRetry,
|
|
IN OUT UINT16 **LoadOptionNumber,
|
|
OUT EFI_HANDLE *FsHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuf;
|
|
UINTN BootOptionNum;
|
|
UINTN Index;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
|
|
EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
|
|
|
|
*FsHandle = NULL;
|
|
CurFullPath = NULL;
|
|
|
|
if (*LoadOptionNumber != NULL) {
|
|
BootOptionNum = 1;
|
|
Status = GetBootOptionByNumber(**LoadOptionNumber, &BootOptionBuf);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status));
|
|
return Status;
|
|
}
|
|
} else {
|
|
Status = GetBootOptionInOrder(&BootOptionBuf, &BootOptionNum);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status));
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Search BootOptionList to check if it is an active boot option with EFI system partition
|
|
// 1. Connect device path
|
|
// 2. expend short/plug in devicepath
|
|
// 3. LoadImage
|
|
//
|
|
for (Index = 0; Index < BootOptionNum; Index++) {
|
|
//
|
|
// Get the boot option from the link list
|
|
//
|
|
DevicePath = BootOptionBuf[Index].FilePath;
|
|
|
|
//
|
|
// Skip inactive or legacy boot options
|
|
//
|
|
if ((BootOptionBuf[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 ||
|
|
DevicePathType (DevicePath) == BBS_DEVICE_PATH) {
|
|
continue;
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
CHAR16 *DevicePathStr;
|
|
|
|
DevicePathStr = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
|
|
if (DevicePathStr != NULL){
|
|
DEBUG((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr));
|
|
FreePool(DevicePathStr);
|
|
} else {
|
|
DEBUG((DEBUG_INFO, "DevicePathToStr failed\n"));
|
|
}
|
|
);
|
|
|
|
CurFullPath = NULL;
|
|
//
|
|
// Try every full device Path generated from bootoption
|
|
//
|
|
do {
|
|
PreFullPath = CurFullPath;
|
|
CurFullPath = EfiBootManagerGetNextLoadOptionDevicePath(DevicePath, CurFullPath);
|
|
|
|
if (PreFullPath != NULL) {
|
|
FreePool(PreFullPath);
|
|
}
|
|
|
|
if (CurFullPath == NULL) {
|
|
//
|
|
// No Active EFI system partition is found in BootOption device path
|
|
//
|
|
Status = EFI_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
CHAR16 *DevicePathStr1;
|
|
|
|
DevicePathStr1 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);
|
|
if (DevicePathStr1 != NULL){
|
|
DEBUG((DEBUG_INFO, "Full device path %s\n", DevicePathStr1));
|
|
FreePool(DevicePathStr1);
|
|
}
|
|
);
|
|
|
|
//
|
|
// Make sure the boot option device path connected.
|
|
// Only handle first device in boot option. Other optional device paths are described as OSV specific
|
|
// FullDevice could contain extra directory & file info. So don't check connection status here.
|
|
//
|
|
EfiBootManagerConnectDevicePath (CurFullPath, NULL);
|
|
Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);
|
|
|
|
//
|
|
// Some relocation device like USB need more time to get enumerated
|
|
//
|
|
while (EFI_ERROR(Status) && MaxRetry > 0) {
|
|
EfiBootManagerConnectDevicePath(CurFullPath, NULL);
|
|
|
|
//
|
|
// Search for EFI system partition protocol on full device path in Boot Option
|
|
//
|
|
Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);
|
|
if (!EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
DEBUG((DEBUG_ERROR, "GetEfiSysPartitionFromDevPath Loop %x\n", Status));
|
|
//
|
|
// Stall 100ms if connection failed to ensure USB stack is ready
|
|
//
|
|
gBS->Stall(100000);
|
|
MaxRetry --;
|
|
}
|
|
} while(EFI_ERROR(Status));
|
|
|
|
//
|
|
// Find a qualified Simple File System
|
|
//
|
|
if (!EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Return the OptionNumber of the boot option where EFI system partition is got from
|
|
//
|
|
if (*LoadOptionNumber == NULL) {
|
|
*LoadOptionNumber = AllocateCopyPool(sizeof(UINT16), (UINT16 *) &BootOptionBuf[Index].OptionNumber);
|
|
if (*LoadOptionNumber == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No qualified EFI system partition found
|
|
//
|
|
if (*FsHandle == NULL) {
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
CHAR16 *DevicePathStr2;
|
|
if (*FsHandle != NULL) {
|
|
DevicePathStr2 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);
|
|
if (DevicePathStr2 != NULL){
|
|
DEBUG((DEBUG_INFO, "Found Active EFI System Partion on %s\n", DevicePathStr2));
|
|
FreePool(DevicePathStr2);
|
|
}
|
|
} else {
|
|
DEBUG((DEBUG_INFO, "Failed to found Active EFI System Partion\n"));
|
|
}
|
|
);
|
|
|
|
if (CurFullPath != NULL) {
|
|
FreePool(CurFullPath);
|
|
}
|
|
|
|
//
|
|
// Free BootOption Buffer
|
|
//
|
|
for (Index = 0; Index < BootOptionNum; Index++) {
|
|
if (BootOptionBuf[Index].Description != NULL) {
|
|
FreePool(BootOptionBuf[Index].Description);
|
|
}
|
|
|
|
if (BootOptionBuf[Index].FilePath != NULL) {
|
|
FreePool(BootOptionBuf[Index].FilePath);
|
|
}
|
|
|
|
if (BootOptionBuf[Index].OptionalData != NULL) {
|
|
FreePool(BootOptionBuf[Index].OptionalData);
|
|
}
|
|
}
|
|
|
|
FreePool(BootOptionBuf);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in
|
|
alphabetical order described in UEFI spec.
|
|
|
|
@param[in] Dir Directory file handler
|
|
@param[in] FileAttr Attribute of file to be red from directory
|
|
@param[out] FileInfoList File images info list red from directory
|
|
@param[out] FileNum File images number red from directory
|
|
|
|
@retval EFI_SUCCESS File FileInfo list in the given
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetFileInfoListInAlphabetFromDir(
|
|
IN EFI_FILE_HANDLE Dir,
|
|
IN UINT64 FileAttr,
|
|
OUT LIST_ENTRY *FileInfoList,
|
|
OUT UINTN *FileNum
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
FILE_INFO_ENTRY *NewFileInfoEntry;
|
|
FILE_INFO_ENTRY *TempFileInfoEntry;
|
|
EFI_FILE_INFO *FileInfo;
|
|
CHAR16 *NewFileName;
|
|
CHAR16 *ListedFileName;
|
|
CHAR16 *NewFileNameExtension;
|
|
CHAR16 *ListedFileNameExtension;
|
|
CHAR16 *TempNewSubStr;
|
|
CHAR16 *TempListedSubStr;
|
|
LIST_ENTRY *Link;
|
|
BOOLEAN NoFile;
|
|
UINTN FileCount;
|
|
UINTN IndexNew;
|
|
UINTN IndexListed;
|
|
UINTN NewSubStrLen;
|
|
UINTN ListedSubStrLen;
|
|
INTN SubStrCmpResult;
|
|
|
|
Status = EFI_SUCCESS;
|
|
NewFileName = NULL;
|
|
ListedFileName = NULL;
|
|
NewFileNameExtension = NULL;
|
|
ListedFileNameExtension = NULL;
|
|
TempNewSubStr = NULL;
|
|
TempListedSubStr = NULL;
|
|
FileInfo = NULL;
|
|
NoFile = FALSE;
|
|
FileCount = 0;
|
|
|
|
InitializeListHead(FileInfoList);
|
|
|
|
TempNewSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
|
|
TempListedSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
|
|
|
|
if (TempNewSubStr == NULL || TempListedSubStr == NULL ) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
for ( Status = FileHandleFindFirstFile(Dir, &FileInfo)
|
|
; !EFI_ERROR(Status) && !NoFile
|
|
; Status = FileHandleFindNextFile(Dir, FileInfo, &NoFile)
|
|
){
|
|
if (FileInfo == NULL) {
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Skip file with mismatching File attribute
|
|
//
|
|
if ((FileInfo->Attribute & (FileAttr)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
NewFileInfoEntry = NULL;
|
|
NewFileInfoEntry = (FILE_INFO_ENTRY*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY));
|
|
if (NewFileInfoEntry == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
NewFileInfoEntry->Signature = FILE_INFO_SIGNATURE;
|
|
NewFileInfoEntry->FileInfo = AllocateCopyPool((UINTN) FileInfo->Size, FileInfo);
|
|
if (NewFileInfoEntry->FileInfo == NULL) {
|
|
FreePool(NewFileInfoEntry);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
NewFileInfoEntry->FileNameFirstPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
|
|
if (NewFileInfoEntry->FileNameFirstPart == NULL) {
|
|
FreePool(NewFileInfoEntry->FileInfo);
|
|
FreePool(NewFileInfoEntry);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
NewFileInfoEntry->FileNameSecondPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);
|
|
if (NewFileInfoEntry->FileNameSecondPart == NULL) {
|
|
FreePool(NewFileInfoEntry->FileInfo);
|
|
FreePool(NewFileInfoEntry->FileNameFirstPart);
|
|
FreePool(NewFileInfoEntry);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension
|
|
// If no period in the whole file name. NewFileExtension is set to L'\0'
|
|
//
|
|
NewFileName = NewFileInfoEntry->FileNameFirstPart;
|
|
NewFileNameExtension = NewFileInfoEntry->FileNameSecondPart;
|
|
SplitFileNameExtension(FileInfo->FileName, NewFileName, NewFileNameExtension);
|
|
UpperCaseString(NewFileName);
|
|
UpperCaseString(NewFileNameExtension);
|
|
|
|
//
|
|
// Insert capsule file in alphabetical ordered list
|
|
//
|
|
for (Link = FileInfoList->ForwardLink; Link != FileInfoList; Link = Link->ForwardLink) {
|
|
//
|
|
// Get the FileInfo from the link list
|
|
//
|
|
TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
|
|
ListedFileName = TempFileInfoEntry->FileNameFirstPart;
|
|
ListedFileNameExtension = TempFileInfoEntry->FileNameSecondPart;
|
|
|
|
//
|
|
// Follow rule in UEFI spec 8.5.5 to compare file name
|
|
//
|
|
IndexListed = 0;
|
|
IndexNew = 0;
|
|
while (TRUE){
|
|
//
|
|
// First compare each substrings in NewFileName & ListedFileName between periods
|
|
//
|
|
GetSubStringBeforePeriod(&NewFileName[IndexNew], TempNewSubStr, &NewSubStrLen);
|
|
GetSubStringBeforePeriod(&ListedFileName[IndexListed], TempListedSubStr, &ListedSubStrLen);
|
|
if (NewSubStrLen > ListedSubStrLen) {
|
|
//
|
|
// Substr in NewFileName is longer. Pad tail with SPACE
|
|
//
|
|
PadStrInTail(TempListedSubStr, NewSubStrLen - ListedSubStrLen, L' ');
|
|
} else if (NewSubStrLen < ListedSubStrLen){
|
|
//
|
|
// Substr in ListedFileName is longer. Pad tail with SPACE
|
|
//
|
|
PadStrInTail(TempNewSubStr, ListedSubStrLen - NewSubStrLen, L' ');
|
|
}
|
|
|
|
SubStrCmpResult = StrnCmp(TempNewSubStr, TempListedSubStr, MAX_FILE_NAME_LEN);
|
|
if (SubStrCmpResult != 0) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Move to skip this substring
|
|
//
|
|
IndexNew += NewSubStrLen;
|
|
IndexListed += ListedSubStrLen;
|
|
//
|
|
// Reach File First Name end
|
|
//
|
|
if (NewFileName[IndexNew] == L'\0' || ListedFileName[IndexListed] == L'\0') {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Skip the period L'.'
|
|
//
|
|
IndexNew++;
|
|
IndexListed++;
|
|
}
|
|
|
|
if (SubStrCmpResult < 0) {
|
|
//
|
|
// NewFileName is smaller. Find the right place to insert New file
|
|
//
|
|
break;
|
|
} else if (SubStrCmpResult == 0) {
|
|
//
|
|
// 2 cases whole NewFileName is smaller than ListedFileName
|
|
// 1. if NewFileName == ListedFileName. Continue to compare FileNameExtension
|
|
// 2. if NewFileName is shorter than ListedFileName
|
|
//
|
|
if (NewFileName[IndexNew] == L'\0') {
|
|
if (ListedFileName[IndexListed] != L'\0' || (StrnCmp(NewFileNameExtension, ListedFileNameExtension, MAX_FILE_NAME_LEN) < 0)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Other case, ListedFileName is smaller. Continue to compare the next file in the list
|
|
//
|
|
}
|
|
|
|
//
|
|
// If Find an entry in the list whose name is bigger than new FileInfo in alphabet order
|
|
// Insert it before this entry
|
|
// else
|
|
// Insert at the tail of this list (Link = FileInfoList)
|
|
//
|
|
InsertTailList(Link, &NewFileInfoEntry->Link);
|
|
|
|
FileCount++;
|
|
}
|
|
|
|
*FileNum = FileCount;
|
|
|
|
EXIT:
|
|
|
|
if (TempNewSubStr != NULL) {
|
|
FreePool(TempNewSubStr);
|
|
}
|
|
|
|
if (TempListedSubStr != NULL) {
|
|
FreePool(TempListedSubStr);
|
|
}
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
while(!IsListEmpty(FileInfoList)) {
|
|
Link = FileInfoList->ForwardLink;
|
|
RemoveEntryList(Link);
|
|
|
|
TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
|
|
|
|
FreePool(TempFileInfoEntry->FileInfo);
|
|
FreePool(TempFileInfoEntry->FileNameFirstPart);
|
|
FreePool(TempFileInfoEntry->FileNameSecondPart);
|
|
FreePool(TempFileInfoEntry);
|
|
}
|
|
*FileNum = 0;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
This routine is called to get all qualified image from file from an given directory
|
|
in alphabetic order. All the file image is copied to allocated boottime memory.
|
|
Caller should free these memory
|
|
|
|
@param[in] Dir Directory file handler
|
|
@param[in] FileAttr Attribute of file to be red from directory
|
|
@param[out] FilePtr File images Info buffer red from directory
|
|
@param[out] FileNum File images number red from directory
|
|
|
|
@retval EFI_SUCCESS Succeed to get all capsules in alphabetic order.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetFileImageInAlphabetFromDir(
|
|
IN EFI_FILE_HANDLE Dir,
|
|
IN UINT64 FileAttr,
|
|
OUT IMAGE_INFO **FilePtr,
|
|
OUT UINTN *FileNum
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY *Link;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
FILE_INFO_ENTRY *FileInfoEntry;
|
|
EFI_FILE_INFO *FileInfo;
|
|
UINTN FileCount;
|
|
IMAGE_INFO *TempFilePtrBuf;
|
|
UINTN Size;
|
|
LIST_ENTRY FileInfoList;
|
|
|
|
FileHandle = NULL;
|
|
FileCount = 0;
|
|
TempFilePtrBuf = NULL;
|
|
*FilePtr = NULL;
|
|
|
|
//
|
|
// Get file list in Dir in alphabetical order
|
|
//
|
|
Status = GetFileInfoListInAlphabetFromDir(
|
|
Dir,
|
|
FileAttr,
|
|
&FileInfoList,
|
|
&FileCount
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));
|
|
goto EXIT;
|
|
}
|
|
|
|
if (FileCount == 0) {
|
|
DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));
|
|
Status = EFI_NOT_FOUND;
|
|
goto EXIT;
|
|
}
|
|
|
|
TempFilePtrBuf = (IMAGE_INFO *)AllocateZeroPool(sizeof(IMAGE_INFO) * FileCount);
|
|
if (TempFilePtrBuf == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Read all files from FileInfoList to BS memory
|
|
//
|
|
FileCount = 0;
|
|
for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {
|
|
//
|
|
// Get FileInfo from the link list
|
|
//
|
|
FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
|
|
FileInfo = FileInfoEntry->FileInfo;
|
|
|
|
Status = Dir->Open(
|
|
Dir,
|
|
&FileHandle,
|
|
FileInfo->FileName,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
if (EFI_ERROR(Status)){
|
|
continue;
|
|
}
|
|
|
|
Size = (UINTN)FileInfo->FileSize;
|
|
TempFilePtrBuf[FileCount].ImageAddress = AllocateZeroPool(Size);
|
|
if (TempFilePtrBuf[FileCount].ImageAddress == NULL) {
|
|
DEBUG((DEBUG_ERROR, "Fail to allocate memory for capsule. Stop processing the rest.\n"));
|
|
break;
|
|
}
|
|
|
|
Status = FileHandle->Read(
|
|
FileHandle,
|
|
&Size,
|
|
TempFilePtrBuf[FileCount].ImageAddress
|
|
);
|
|
|
|
FileHandle->Close(FileHandle);
|
|
|
|
//
|
|
// Skip read error file
|
|
//
|
|
if (EFI_ERROR(Status) || Size != (UINTN)FileInfo->FileSize) {
|
|
//
|
|
// Remove this error file info accordingly
|
|
// & move Link to BackLink
|
|
//
|
|
Link = RemoveEntryList(Link);
|
|
Link = Link->BackLink;
|
|
|
|
FreePool(FileInfoEntry->FileInfo);
|
|
FreePool(FileInfoEntry->FileNameFirstPart);
|
|
FreePool(FileInfoEntry->FileNameSecondPart);
|
|
FreePool(FileInfoEntry);
|
|
|
|
FreePool(TempFilePtrBuf[FileCount].ImageAddress);
|
|
TempFilePtrBuf[FileCount].ImageAddress = NULL;
|
|
TempFilePtrBuf[FileCount].FileInfo = NULL;
|
|
|
|
continue;
|
|
}
|
|
TempFilePtrBuf[FileCount].FileInfo = FileInfo;
|
|
FileCount++;
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {
|
|
FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
|
|
FileInfo = FileInfoEntry->FileInfo;
|
|
DEBUG((DEBUG_INFO, "Successfully read capsule file %s from disk.\n", FileInfo->FileName));
|
|
}
|
|
);
|
|
|
|
EXIT:
|
|
|
|
*FilePtr = TempFilePtrBuf;
|
|
*FileNum = FileCount;
|
|
|
|
//
|
|
// FileInfo will be freed by Calller
|
|
//
|
|
while(!IsListEmpty(&FileInfoList)) {
|
|
Link = FileInfoList.ForwardLink;
|
|
RemoveEntryList(Link);
|
|
|
|
FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
|
|
|
|
FreePool(FileInfoEntry->FileNameFirstPart);
|
|
FreePool(FileInfoEntry->FileNameSecondPart);
|
|
FreePool(FileInfoEntry);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This routine is called to remove all qualified image from file from an given directory.
|
|
|
|
@param[in] Dir Directory file handler
|
|
@param[in] FileAttr Attribute of files to be deleted
|
|
|
|
@retval EFI_SUCCESS Succeed to remove all files from an given directory.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
RemoveFileFromDir(
|
|
IN EFI_FILE_HANDLE Dir,
|
|
IN UINT64 FileAttr
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY FileInfoList;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
FILE_INFO_ENTRY *FileInfoEntry;
|
|
EFI_FILE_INFO *FileInfo;
|
|
UINTN FileCount;
|
|
|
|
FileHandle = NULL;
|
|
|
|
//
|
|
// Get file list in Dir in alphabetical order
|
|
//
|
|
Status = GetFileInfoListInAlphabetFromDir(
|
|
Dir,
|
|
FileAttr,
|
|
&FileInfoList,
|
|
&FileCount
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));
|
|
goto EXIT;
|
|
}
|
|
|
|
if (FileCount == 0) {
|
|
DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));
|
|
Status = EFI_NOT_FOUND;
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Delete all files with given attribute in Dir
|
|
//
|
|
for (Link = FileInfoList.ForwardLink; Link != &(FileInfoList); Link = Link->ForwardLink) {
|
|
//
|
|
// Get FileInfo from the link list
|
|
//
|
|
FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
|
|
FileInfo = FileInfoEntry->FileInfo;
|
|
|
|
Status = Dir->Open(
|
|
Dir,
|
|
&FileHandle,
|
|
FileInfo->FileName,
|
|
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
|
|
0
|
|
);
|
|
if (EFI_ERROR(Status)){
|
|
continue;
|
|
}
|
|
|
|
Status = FileHandle->Delete(FileHandle);
|
|
}
|
|
|
|
EXIT:
|
|
|
|
while(!IsListEmpty(&FileInfoList)) {
|
|
Link = FileInfoList.ForwardLink;
|
|
RemoveEntryList(Link);
|
|
|
|
FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);
|
|
|
|
FreePool(FileInfoEntry->FileInfo);
|
|
FreePool(FileInfoEntry);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This routine is called to get all caspules from file. The capsule file image is
|
|
copied to BS memory. Caller is responsible to free them.
|
|
|
|
@param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
|
|
devices like USB can get enumerated.
|
|
@param[out] CapsulePtr Copied Capsule file Image Info buffer
|
|
@param[out] CapsuleNum CapsuleNumber
|
|
@param[out] FsHandle File system handle
|
|
@param[out] LoadOptionNumber OptionNumber of boot option
|
|
|
|
@retval EFI_SUCCESS Succeed to get all capsules.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetAllCapsuleOnDisk(
|
|
IN UINTN MaxRetry,
|
|
OUT IMAGE_INFO **CapsulePtr,
|
|
OUT UINTN *CapsuleNum,
|
|
OUT EFI_HANDLE *FsHandle,
|
|
OUT UINT16 *LoadOptionNumber
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
|
|
EFI_FILE_HANDLE RootDir;
|
|
EFI_FILE_HANDLE FileDir;
|
|
UINT16 *TempOptionNumber;
|
|
|
|
TempOptionNumber = NULL;
|
|
*CapsuleNum = 0;
|
|
|
|
Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &TempOptionNumber, FsHandle);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol(*FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = Fs->OpenVolume(Fs, &RootDir);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = RootDir->Open(
|
|
RootDir,
|
|
&FileDir,
|
|
EFI_CAPSULE_FILE_DIRECTORY,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));
|
|
RootDir->Close (RootDir);
|
|
return Status;
|
|
}
|
|
RootDir->Close (RootDir);
|
|
|
|
//
|
|
// Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute
|
|
// ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY
|
|
//
|
|
Status = GetFileImageInAlphabetFromDir(
|
|
FileDir,
|
|
EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE,
|
|
CapsulePtr,
|
|
CapsuleNum
|
|
);
|
|
DEBUG((DEBUG_INFO, "GetFileImageInAlphabetFromDir status %x\n", Status));
|
|
|
|
//
|
|
// Always remove file to avoid deadloop in capsule process
|
|
//
|
|
Status = RemoveFileFromDir(FileDir, EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE);
|
|
DEBUG((DEBUG_INFO, "RemoveFileFromDir status %x\n", Status));
|
|
|
|
FileDir->Close (FileDir);
|
|
|
|
if (LoadOptionNumber != NULL) {
|
|
*LoadOptionNumber = *TempOptionNumber;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Build Gather list for a list of capsule images.
|
|
|
|
@param[in] CapsuleBuffer An array of pointer to capsule images
|
|
@param[in] CapsuleSize An array of UINTN to capsule images size
|
|
@param[in] CapsuleNum The count of capsule images
|
|
@param[out] BlockDescriptors The block descriptors for the capsule images
|
|
|
|
@retval EFI_SUCCESS The block descriptors for the capsule images are constructed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
BuildGatherList (
|
|
IN VOID **CapsuleBuffer,
|
|
IN UINTN *CapsuleSize,
|
|
IN UINTN CapsuleNum,
|
|
OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader;
|
|
UINTN Index;
|
|
|
|
BlockDescriptors1 = NULL;
|
|
BlockDescriptorPre = NULL;
|
|
BlockDescriptorsHeader = NULL;
|
|
|
|
for (Index = 0; Index < CapsuleNum; Index++) {
|
|
//
|
|
// Allocate memory for the descriptors.
|
|
//
|
|
BlockDescriptors1 = AllocateZeroPool(2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR));
|
|
if (BlockDescriptors1 == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "BuildGatherList: failed to allocate memory for descriptors\n"));
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ERREXIT;
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1));
|
|
}
|
|
|
|
//
|
|
// Record descirptor header
|
|
//
|
|
if (Index == 0) {
|
|
BlockDescriptorsHeader = BlockDescriptors1;
|
|
}
|
|
|
|
if (BlockDescriptorPre != NULL) {
|
|
BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;
|
|
BlockDescriptorPre->Length = 0;
|
|
}
|
|
|
|
BlockDescriptors1->Union.DataBlock = (UINTN) CapsuleBuffer[Index];
|
|
BlockDescriptors1->Length = CapsuleSize[Index];
|
|
|
|
BlockDescriptorPre = BlockDescriptors1 + 1;
|
|
BlockDescriptors1 = NULL;
|
|
}
|
|
|
|
//
|
|
// Null-terminate.
|
|
//
|
|
if (BlockDescriptorPre != NULL) {
|
|
BlockDescriptorPre->Union.ContinuationPointer = (UINTN)NULL;
|
|
BlockDescriptorPre->Length = 0;
|
|
*BlockDescriptors = BlockDescriptorsHeader;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
ERREXIT:
|
|
if (BlockDescriptors1 != NULL) {
|
|
FreePool(BlockDescriptors1);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This routine is called to check if CapsuleOnDisk flag in OsIndications Variable
|
|
is enabled.
|
|
|
|
@retval TRUE Flag is enabled
|
|
@retval FALSE Flag is not enabled
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
CoDCheckCapsuleOnDiskFlag(
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 OsIndication;
|
|
UINTN DataSize;
|
|
|
|
//
|
|
// Check File Capsule Delivery Supported Flag in OsIndication variable
|
|
//
|
|
OsIndication = 0;
|
|
DataSize = sizeof(UINT64);
|
|
Status = gRT->GetVariable (
|
|
EFI_OS_INDICATIONS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&DataSize,
|
|
&OsIndication
|
|
);
|
|
if (!EFI_ERROR(Status) &&
|
|
(OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.
|
|
|
|
@retval EFI_SUCCESS All Capsule On Disk flags are cleared
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CoDClearCapsuleOnDiskFlag(
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 OsIndication;
|
|
UINTN DataSize;
|
|
|
|
//
|
|
// Reset File Capsule Delivery Supported Flag in OsIndication variable
|
|
//
|
|
OsIndication = 0;
|
|
DataSize = sizeof(UINT64);
|
|
Status = gRT->GetVariable (
|
|
EFI_OS_INDICATIONS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&DataSize,
|
|
&OsIndication
|
|
);
|
|
if (EFI_ERROR(Status) ||
|
|
(OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) {
|
|
return Status;
|
|
}
|
|
|
|
OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);
|
|
Status = gRT->SetVariable (
|
|
EFI_OS_INDICATIONS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
sizeof(UINT64),
|
|
&OsIndication
|
|
);
|
|
ASSERT(!EFI_ERROR(Status));
|
|
|
|
//
|
|
// Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable
|
|
//
|
|
Status = gRT->SetVariable (
|
|
EFI_BOOT_NEXT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This routine is called to clear CapsuleOnDisk Relocation Info variable.
|
|
Total Capsule On Disk length is recorded in this variable
|
|
|
|
@retval EFI_SUCCESS Capsule On Disk flags are cleared
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CoDClearCapsuleRelocationInfo(
|
|
VOID
|
|
)
|
|
{
|
|
return gRT->SetVariable (
|
|
COD_RELOCATION_INFO_VAR_NAME,
|
|
&gEfiCapsuleVendorGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
/**
|
|
Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device
|
|
with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must
|
|
be a full device path.
|
|
Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
|
|
Function will stall 100ms between each retry.
|
|
|
|
Side Effects:
|
|
Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems
|
|
of the relocation device will be corrupted.
|
|
|
|
@param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
|
|
devices like USB can get enumerated.
|
|
|
|
@retval EFI_SUCCESS Capsule on Disk images are sucessfully relocated to the platform-specific device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
RelocateCapsuleToDisk(
|
|
UINTN MaxRetry
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN CapsuleOnDiskNum;
|
|
UINTN Index;
|
|
UINTN DataSize;
|
|
UINT64 TotalImageSize;
|
|
UINT64 TotalImageNameSize;
|
|
IMAGE_INFO *CapsuleOnDiskBuf;
|
|
EFI_HANDLE Handle;
|
|
EFI_HANDLE TempHandle;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN NumberOfHandles;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
UINT8 *CapsuleDataBuf;
|
|
UINT8 *CapsulePtr;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
|
|
EFI_FILE_HANDLE RootDir;
|
|
EFI_FILE_HANDLE TempCodFile;
|
|
UINT64 TempCodFileSize;
|
|
EFI_DEVICE_PATH *TempDevicePath;
|
|
BOOLEAN RelocationInfo;
|
|
UINT16 LoadOptionNumber;
|
|
EFI_CAPSULE_HEADER FileNameCapsuleHeader;
|
|
|
|
RootDir = NULL;
|
|
TempCodFile = NULL;
|
|
HandleBuffer = NULL;
|
|
CapsuleDataBuf = NULL;
|
|
CapsuleOnDiskBuf = NULL;
|
|
NumberOfHandles = 0;
|
|
|
|
DEBUG ((DEBUG_INFO, "CapsuleOnDisk RelocateCapsule Enter\n"));
|
|
|
|
//
|
|
// 1. Load all Capsule On Disks in to memory
|
|
//
|
|
Status = GetAllCapsuleOnDisk(MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, &LoadOptionNumber);
|
|
if (EFI_ERROR(Status) || CapsuleOnDiskNum == 0 || CapsuleOnDiskBuf == NULL) {
|
|
DEBUG ((DEBUG_INFO, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// 2. Connect platform special device path as relocation device.
|
|
// If no platform special device path specified or the device path is invalid, use the EFI system partition where
|
|
// stores the capsules as relocation device.
|
|
//
|
|
if (IsDevicePathValid ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), PcdGetSize(PcdCodRelocationDevPath))) {
|
|
Status = EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), &TempHandle);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status));
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Connect all the child handle. Partition & FAT drivers are allowed in this case
|
|
//
|
|
gBS->ConnectController (TempHandle, NULL, NULL, TRUE);
|
|
Status = gBS->LocateHandleBuffer(
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_ERROR, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status));
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Find first Simple File System Handle which can match PcdCodRelocationDevPath
|
|
//
|
|
for (Index = 0; Index < NumberOfHandles; Index++) {
|
|
Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&TempDevicePath);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
DataSize = GetDevicePathSize((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath)) - sizeof(EFI_DEVICE_PATH);
|
|
if (0 == CompareMem((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), TempDevicePath, DataSize)) {
|
|
Handle = HandleBuffer[Index];
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreePool(HandleBuffer);
|
|
|
|
if (Index == NumberOfHandles) {
|
|
DEBUG ((DEBUG_ERROR, "RelocateCapsule: No simple file system protocol found.\n"));
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
Status = gBS->HandleProtocol(Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
|
|
if (EFI_ERROR(Status) || BlockIo->Media->ReadOnly) {
|
|
DEBUG((DEBUG_ERROR, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));
|
|
goto EXIT;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Check if device used to relocate Capsule On Disk is big enough
|
|
//
|
|
TotalImageSize = 0;
|
|
TotalImageNameSize = 0;
|
|
for (Index = 0; Index < CapsuleOnDiskNum; Index++) {
|
|
//
|
|
// Overflow check
|
|
//
|
|
if (MAX_ADDRESS - (UINTN)TotalImageSize <= CapsuleOnDiskBuf[Index].FileInfo->FileSize) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto EXIT;
|
|
}
|
|
|
|
if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto EXIT;
|
|
}
|
|
|
|
TotalImageSize += CapsuleOnDiskBuf[Index].FileInfo->FileSize;
|
|
TotalImageNameSize += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);
|
|
DEBUG((DEBUG_INFO, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf[Index].FileInfo->FileName, CapsuleOnDiskBuf[Index].FileInfo->FileSize));
|
|
}
|
|
|
|
DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize));
|
|
DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize));
|
|
|
|
if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= sizeof(UINT64) * 2 ||
|
|
MAX_ADDRESS - (UINTN)TotalImageSize <= (UINTN)TotalImageNameSize + sizeof(UINT64) * 2) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto EXIT;
|
|
}
|
|
|
|
TempCodFileSize = sizeof(UINT64) + TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;
|
|
|
|
//
|
|
// Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly
|
|
//
|
|
if (DivU64x32(TempCodFileSize, BlockIo->Media->BlockSize) > BlockIo->Media->LastBlock) {
|
|
DEBUG((DEBUG_ERROR, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));
|
|
DEBUG((DEBUG_ERROR, "TotalImageSize = %x\n", TotalImageSize));
|
|
DEBUG((DEBUG_ERROR, "TotalImageNameSize = %x\n", TotalImageNameSize));
|
|
DEBUG((DEBUG_ERROR, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo->Media->BlockSize, BlockIo->Media->LastBlock));
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
CapsuleDataBuf = AllocatePool((UINTN) TempCodFileSize);
|
|
if (CapsuleDataBuf == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// First UINT64 reserved for total image size, including capsule name capsule.
|
|
//
|
|
*(UINT64 *) CapsuleDataBuf = TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;
|
|
|
|
//
|
|
// Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write
|
|
//
|
|
for (Index = 0, CapsulePtr = CapsuleDataBuf + sizeof(UINT64); Index < CapsuleOnDiskNum; Index++) {
|
|
CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].ImageAddress, (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize);
|
|
CapsulePtr += CapsuleOnDiskBuf[Index].FileInfo->FileSize;
|
|
}
|
|
|
|
//
|
|
// Line the capsule header for capsule name capsule.
|
|
//
|
|
CopyGuid(&FileNameCapsuleHeader.CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid);
|
|
FileNameCapsuleHeader.CapsuleImageSize = (UINT32) TotalImageNameSize + sizeof(EFI_CAPSULE_HEADER);
|
|
FileNameCapsuleHeader.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
|
|
FileNameCapsuleHeader.HeaderSize = sizeof(EFI_CAPSULE_HEADER);
|
|
CopyMem(CapsulePtr, &FileNameCapsuleHeader, FileNameCapsuleHeader.HeaderSize);
|
|
CapsulePtr += FileNameCapsuleHeader.HeaderSize;
|
|
|
|
//
|
|
// Line up all the Capsule file names.
|
|
//
|
|
for (Index = 0; Index < CapsuleOnDiskNum; Index++) {
|
|
CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].FileInfo->FileName, StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName));
|
|
CapsulePtr += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);
|
|
}
|
|
|
|
//
|
|
// 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir
|
|
//
|
|
Status = Fs->OpenVolume(Fs, &RootDir);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR, "RelocateCapsule: OpenVolume error. %x\n", Status));
|
|
goto EXIT;
|
|
}
|
|
|
|
Status = RootDir->Open(
|
|
RootDir,
|
|
&TempCodFile,
|
|
(CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),
|
|
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
|
|
0
|
|
);
|
|
if (!EFI_ERROR(Status)) {
|
|
//
|
|
// Error handling code to prevent malicious code to hold this file to block capsule on disk
|
|
//
|
|
TempCodFile->Delete(TempCodFile);
|
|
}
|
|
Status = RootDir->Open(
|
|
RootDir,
|
|
&TempCodFile,
|
|
(CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),
|
|
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
|
|
0
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status));
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Always write at the begining of TempCap file
|
|
//
|
|
DataSize = (UINTN) TempCodFileSize;
|
|
Status = TempCodFile->Write(
|
|
TempCodFile,
|
|
&DataSize,
|
|
CapsuleDataBuf
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status));
|
|
goto EXIT;
|
|
}
|
|
|
|
if (DataSize != TempCodFileSize) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Save Capsule On Disk relocation info to "CodRelocationInfo" Var
|
|
// It is used in next reboot by TCB
|
|
//
|
|
RelocationInfo = TRUE;
|
|
Status = gRT->SetVariable(
|
|
COD_RELOCATION_INFO_VAR_NAME,
|
|
&gEfiCapsuleVendorGuid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
sizeof (BOOLEAN),
|
|
&RelocationInfo
|
|
);
|
|
//
|
|
// Save the LoadOptionNumber of the boot option, where the capsule is relocated,
|
|
// into "CodRelocationLoadOption" var. It is used in next reboot after capsule is
|
|
// updated out of TCB to remove the TempCoDFile.
|
|
//
|
|
Status = gRT->SetVariable(
|
|
COD_RELOCATION_LOAD_OPTION_VAR_NAME,
|
|
&gEfiCapsuleVendorGuid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
sizeof (UINT16),
|
|
&LoadOptionNumber
|
|
);
|
|
|
|
EXIT:
|
|
|
|
if (CapsuleDataBuf != NULL) {
|
|
FreePool(CapsuleDataBuf);
|
|
}
|
|
|
|
if (CapsuleOnDiskBuf != NULL) {
|
|
//
|
|
// Free resources allocated by CodLibGetAllCapsuleOnDisk
|
|
//
|
|
for (Index = 0; Index < CapsuleOnDiskNum; Index++ ) {
|
|
FreePool(CapsuleOnDiskBuf[Index].ImageAddress);
|
|
FreePool(CapsuleOnDiskBuf[Index].FileInfo);
|
|
}
|
|
FreePool(CapsuleOnDiskBuf);
|
|
}
|
|
|
|
if (TempCodFile != NULL) {
|
|
if (EFI_ERROR(Status)) {
|
|
TempCodFile->Delete (TempCodFile);
|
|
} else {
|
|
TempCodFile->Close (TempCodFile);
|
|
}
|
|
}
|
|
|
|
if (RootDir != NULL) {
|
|
RootDir->Close (RootDir);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.
|
|
Relocate Capsule On Disk to memory and call UpdateCapsule().
|
|
Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
|
|
Function will stall 100ms between each retry.
|
|
|
|
@param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
|
|
devices like USB can get enumerated.
|
|
|
|
@retval EFI_SUCCESS Deliver capsule through Capsule In Ram successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
RelocateCapsuleToRam (
|
|
UINTN MaxRetry
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN CapsuleOnDiskNum;
|
|
IMAGE_INFO *CapsuleOnDiskBuf;
|
|
EFI_HANDLE Handle;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;
|
|
VOID **CapsuleBuffer;
|
|
UINTN *CapsuleSize;
|
|
EFI_CAPSULE_HEADER *FileNameCapsule;
|
|
UINTN Index;
|
|
UINT8 *StringBuf;
|
|
UINTN StringSize;
|
|
UINTN TotalStringSize;
|
|
|
|
CapsuleOnDiskBuf = NULL;
|
|
BlockDescriptors = NULL;
|
|
CapsuleBuffer = NULL;
|
|
CapsuleSize = NULL;
|
|
FileNameCapsule = NULL;
|
|
TotalStringSize = 0;
|
|
|
|
//
|
|
// 1. Load all Capsule On Disks into memory
|
|
//
|
|
Status = GetAllCapsuleOnDisk (MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, NULL);
|
|
if (EFI_ERROR(Status) || CapsuleOnDiskNum == 0 || CapsuleOnDiskBuf == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "GetAllCapsuleOnDisk Status - 0x%x\n", Status));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// 2. Add a capsule for Capsule file name strings
|
|
//
|
|
CapsuleBuffer = AllocateZeroPool((CapsuleOnDiskNum + 1) * sizeof (VOID *));
|
|
if (CapsuleBuffer == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CapsuleSize = AllocateZeroPool((CapsuleOnDiskNum + 1) * sizeof (UINTN));
|
|
if (CapsuleSize == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));
|
|
FreePool(CapsuleBuffer);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (Index = 0; Index < CapsuleOnDiskNum; Index++) {
|
|
CapsuleBuffer[Index] = (VOID *)(UINTN) CapsuleOnDiskBuf[Index].ImageAddress;
|
|
CapsuleSize[Index] = (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize;
|
|
TotalStringSize += StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);
|
|
}
|
|
|
|
FileNameCapsule = AllocateZeroPool(sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);
|
|
if (FileNameCapsule == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Fail to allocate memory for name capsule.\n"));
|
|
FreePool(CapsuleBuffer);
|
|
FreePool(CapsuleSize);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
FileNameCapsule->CapsuleImageSize = (UINT32) (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);
|
|
FileNameCapsule->Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
|
|
FileNameCapsule->HeaderSize = sizeof (EFI_CAPSULE_HEADER);
|
|
CopyGuid (&(FileNameCapsule->CapsuleGuid), &gEdkiiCapsuleOnDiskNameGuid);
|
|
|
|
StringBuf = (UINT8 *)FileNameCapsule + FileNameCapsule->HeaderSize;
|
|
for (Index = 0; Index < CapsuleOnDiskNum; Index ++) {
|
|
StringSize = StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);
|
|
CopyMem(StringBuf, CapsuleOnDiskBuf[Index].FileInfo->FileName, StringSize);
|
|
StringBuf += StringSize;
|
|
}
|
|
|
|
CapsuleBuffer[CapsuleOnDiskNum] = FileNameCapsule;
|
|
CapsuleSize[CapsuleOnDiskNum] = TotalStringSize + sizeof (EFI_CAPSULE_HEADER);
|
|
|
|
//
|
|
// 3. Build Gather list for the capsules
|
|
//
|
|
Status = BuildGatherList (CapsuleBuffer, CapsuleSize, CapsuleOnDiskNum + 1, &BlockDescriptors);
|
|
if (EFI_ERROR(Status) || BlockDescriptors == NULL) {
|
|
FreePool(CapsuleBuffer);
|
|
FreePool(CapsuleSize);
|
|
FreePool(FileNameCapsule);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// 4. Call UpdateCapsule() service
|
|
//
|
|
Status = gRT->UpdateCapsule((EFI_CAPSULE_HEADER **) CapsuleBuffer, CapsuleOnDiskNum + 1, (UINTN) BlockDescriptors);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Relocate Capsule on Disk from EFI system partition.
|
|
|
|
Two solution to deliver Capsule On Disk:
|
|
Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().
|
|
Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage
|
|
device with BlockIo protocol.
|
|
|
|
Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
|
|
Function will stall 100ms between each retry.
|
|
|
|
Side Effects:
|
|
Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.
|
|
Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file
|
|
systems of the relocation device will be corrupted.
|
|
|
|
@param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
|
|
devices like USB can get enumerated. Input 0 means no retry.
|
|
|
|
@retval EFI_SUCCESS Capsule on Disk images are successfully relocated.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CoDRelocateCapsule(
|
|
UINTN MaxRetry
|
|
)
|
|
{
|
|
if (!PcdGetBool (PcdCapsuleOnDiskSupport)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Clear CapsuleOnDisk Flag firstly.
|
|
//
|
|
CoDClearCapsuleOnDiskFlag ();
|
|
|
|
//
|
|
// If Capsule In Ram is supported, delivery capsules through memory
|
|
//
|
|
if (PcdGetBool (PcdCapsuleInRamSupport)) {
|
|
DEBUG ((DEBUG_INFO, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));
|
|
return RelocateCapsuleToRam (MaxRetry);
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName)));
|
|
return RelocateCapsuleToDisk (MaxRetry);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Remove the temp file from the root of EFI System Partition.
|
|
Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.
|
|
Function will stall 100ms between each retry.
|
|
|
|
@param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure
|
|
devices like USB can get enumerated. Input 0 means no retry.
|
|
|
|
@retval EFI_SUCCESS Remove the temp file successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CoDRemoveTempFile (
|
|
UINTN MaxRetry
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN DataSize;
|
|
UINT16 *LoadOptionNumber;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
|
|
EFI_HANDLE FsHandle;
|
|
EFI_FILE_HANDLE RootDir;
|
|
EFI_FILE_HANDLE TempCodFile;
|
|
|
|
RootDir = NULL;
|
|
TempCodFile = NULL;
|
|
DataSize = sizeof(UINT16);
|
|
|
|
LoadOptionNumber = AllocatePool (sizeof(UINT16));
|
|
if (LoadOptionNumber == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Check if capsule files are relocated
|
|
//
|
|
Status = gRT->GetVariable (
|
|
COD_RELOCATION_LOAD_OPTION_VAR_NAME,
|
|
&gEfiCapsuleVendorGuid,
|
|
NULL,
|
|
&DataSize,
|
|
(VOID *)LoadOptionNumber
|
|
);
|
|
if (EFI_ERROR(Status) || DataSize != sizeof(UINT16)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Get the EFI file system from the boot option where the capsules are relocated
|
|
//
|
|
Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &LoadOptionNumber, &FsHandle);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol(FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
Status = Fs->OpenVolume(Fs, &RootDir);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Delete the TempCoDFile
|
|
//
|
|
Status = RootDir->Open(
|
|
RootDir,
|
|
&TempCodFile,
|
|
(CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),
|
|
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
|
|
0
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
TempCodFile->Delete(TempCodFile);
|
|
|
|
//
|
|
// Clear "CoDRelocationLoadOption" variable
|
|
//
|
|
Status = gRT->SetVariable (
|
|
COD_RELOCATION_LOAD_OPTION_VAR_NAME,
|
|
&gEfiCapsuleVendorGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
EXIT:
|
|
if (LoadOptionNumber != NULL) {
|
|
FreePool(LoadOptionNumber);
|
|
}
|
|
|
|
if (RootDir != NULL) {
|
|
RootDir->Close(RootDir);
|
|
}
|
|
|
|
return Status;
|
|
}
|