1418 lines
40 KiB
C
1418 lines
40 KiB
C
/** @file
|
|
BIOS update fault tolerance DXE driver.
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2020, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
#include <Uefi/UefiSpec.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Library/HobLib.h>
|
|
#include <Library/BaseCryptLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/VariableLib.h>
|
|
#include <Library/H2OCpLib.h>
|
|
#include <Library/CapsuleUpdateCriteriaLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/SeamlessRecoveryLib.h>
|
|
#include <Library/FdSupportLib.h>
|
|
#include <Library/FlashRegionLib.h>
|
|
#include <Library/PostCodeLib.h>
|
|
#include <Library/GenericBdsLib.h>
|
|
#include <Protocol/SimpleFileSystem.h>
|
|
#include <Protocol/FirmwareVolume2.h>
|
|
#include <Protocol/DevicePath.h>
|
|
#include <Protocol/SwapAddressRange.h>
|
|
#include <Protocol/VariableLock.h>
|
|
#include <Guid/FileSystemInfo.h>
|
|
#include <Guid/FileInfo.h>
|
|
|
|
#include <PostCode.h>
|
|
|
|
#define MAX_BOOT_OPTIONS 128
|
|
|
|
/**
|
|
Get the offset of the boot loader file path from system partition for the boot
|
|
device path of the current boot option
|
|
|
|
@param[in] BootDevicePath The device path of the boot option
|
|
@param[out] BootFilePathOffset The offset from the boot device path of the boot
|
|
loader file path
|
|
|
|
@retval EFI_SUCCESS The BootFilePathOffset is correctly set
|
|
@return Others Unable to get boot file path offset
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetBootFilePathDevicePathOffset (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *BootDevicePath,
|
|
OUT UINTN *BootFilePathDevicePathOffset
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_STATUS Status;
|
|
|
|
if (BootDevicePath == NULL || BootFilePathDevicePathOffset == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
Status = EFI_NOT_FOUND;
|
|
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)BootDevicePath;
|
|
while (!IsDevicePathEnd(DevicePath)) {
|
|
if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
|
|
(DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {
|
|
*BootFilePathDevicePathOffset = (UINTN)((UINT8 *)DevicePath - (UINT8 *)BootDevicePath);
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
}
|
|
DevicePath = NextDevicePathNode (DevicePath);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Check if root file system has capsule image file or not.
|
|
|
|
@param[in] SysRootHandle A pointer to a file handle.
|
|
|
|
@retval TRUE It has the capsule image file.
|
|
@retval FALSE It does not has the capsule image file.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
HaveCapsuleImageFile (
|
|
IN EFI_FILE_HANDLE SysRootHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE_HANDLE CapsuleHandle;
|
|
UINTN FileInfoSize;
|
|
EFI_FILE_INFO *FileInfo;
|
|
UINTN FileSize;
|
|
BOOLEAN Result;
|
|
|
|
if (SysRootHandle == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = SysRootHandle->Open (
|
|
SysRootHandle,
|
|
&CapsuleHandle,
|
|
EFI_CAPSULE_FILE_PATH,
|
|
EFI_FILE_MODE_READ,
|
|
EFI_FILE_DIRECTORY
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Result = FALSE;
|
|
FileInfo = NULL;
|
|
|
|
FileInfoSize = 0;
|
|
Status = CapsuleHandle->GetInfo (CapsuleHandle, &gEfiFileInfoGuid, &FileInfoSize, NULL);
|
|
if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
goto Exit;
|
|
}
|
|
FileInfo = AllocatePool (FileInfoSize);
|
|
if (FileInfo == NULL) {
|
|
goto Exit;
|
|
}
|
|
Status = CapsuleHandle->GetInfo (CapsuleHandle,&gEfiFileInfoGuid, &FileInfoSize, (VOID *) FileInfo);
|
|
if (EFI_ERROR (Status) || FileInfo->FileSize == 0) {
|
|
goto Exit;
|
|
}
|
|
|
|
FileInfoSize = (UINTN) FileInfo->FileSize;
|
|
FreePool (FileInfo);
|
|
FileInfo = AllocatePool (FileInfoSize);
|
|
if (FileInfo == NULL) {
|
|
goto Exit;
|
|
}
|
|
|
|
while (TRUE) {
|
|
FileSize = FileInfoSize;
|
|
Status = CapsuleHandle->Read (
|
|
CapsuleHandle,
|
|
&FileSize,
|
|
(VOID *) FileInfo
|
|
);
|
|
if (EFI_ERROR (Status) || FileSize == 0) {
|
|
break;
|
|
}
|
|
|
|
if (FileInfo->FileSize > 0 &&
|
|
((FileInfo->Attribute & EFI_FILE_DIRECTORY) != EFI_FILE_DIRECTORY) &&
|
|
StrnCmp (FileInfo->FileName, EFI_CAPSULE_FILE_NAME, sizeof (EFI_CAPSULE_FILE_NAME) / sizeof (CHAR16) - 1) == 0) {
|
|
Result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
CapsuleHandle->Close (CapsuleHandle);
|
|
if (FileInfo != NULL) {
|
|
FreePool (FileInfo);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
Get the information of system drive which contain capsule image file from all simple file system instances.
|
|
|
|
@param[out] SysRootDevicePath The pointer of system root device path pointer
|
|
@param[out] SysRoorHandle The pointer of EFI_FILE_HANDLE of system drive
|
|
|
|
@retval EFI_SUCCESS The system drive information is correctly get
|
|
@return Others Unable to get system drive information
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetSystemRootInfoFromFileSystems (
|
|
OUT EFI_DEVICE_PATH **SysRootDevicePath,
|
|
OUT EFI_FILE_HANDLE *SysRootHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN NumberOfHandles;
|
|
UINTN Index;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
|
|
EFI_FILE_HANDLE RootHandle;
|
|
EFI_DEVICE_PATH *DevicePath;
|
|
|
|
if (SysRootDevicePath == NULL || SysRootHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
for (Index = 0; Index < NumberOfHandles; Index++) {
|
|
Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **) &SimpleFileSystem);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = SimpleFileSystem->OpenVolume (SimpleFileSystem, &RootHandle);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath);
|
|
if (EFI_ERROR (Status)) {
|
|
RootHandle->Close (RootHandle);
|
|
continue;
|
|
}
|
|
|
|
*SysRootDevicePath = DevicePath;
|
|
*SysRootHandle = RootHandle;
|
|
break;
|
|
}
|
|
|
|
FreePool (HandleBuffer);
|
|
return (Index < NumberOfHandles) ? EFI_SUCCESS : EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
Get the system drive information through the boot loader file path under system partition
|
|
|
|
@param[out] SysRootDevicePath The pointer of system root device path pointer
|
|
@param[out] SysRoorHandle The pointer of EFI_FILE_HANDLE of system drive
|
|
|
|
@retval EFI_SUCCESS The system drive information is correctly get
|
|
@return Others Unable to get system drive information
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetSystemRootInfoFromCurrentBootOption (
|
|
OUT EFI_DEVICE_PATH **SysRootDevicePath,
|
|
OUT EFI_FILE_HANDLE *SysRootHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN TotalBootOptions;
|
|
UINT16 BootOptions[MAX_BOOT_OPTIONS];
|
|
UINTN Size;
|
|
CHAR16 BootOption[] = L"Boot0000";
|
|
UINT8 *BootOptionData;
|
|
CHAR16 *BootOptionDesc;
|
|
EFI_DEVICE_PATH_PROTOCOL *BootDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *ExpandedBootDevicePath;
|
|
UINTN BootFilePathDevicePathOffset;
|
|
UINTN CompareSize;
|
|
BOOLEAN Found;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN NumberOfHandles;
|
|
UINTN CurrentOption;
|
|
UINTN Index;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
|
|
|
|
if (SysRootDevicePath == NULL || SysRootHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
ExpandedBootDevicePath = NULL;
|
|
TotalBootOptions = 0;
|
|
Status = EFI_NOT_FOUND;
|
|
Size = sizeof(UINT16);
|
|
Status = CommonGetVariable (
|
|
L"BootNext",
|
|
&gEfiGlobalVariableGuid,
|
|
&Size,
|
|
&BootOptions[0]
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
TotalBootOptions ++;
|
|
}
|
|
Size = sizeof(UINT16) * (MAX_BOOT_OPTIONS - TotalBootOptions);
|
|
Status = CommonGetVariable (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
&Size,
|
|
&BootOptions[TotalBootOptions]
|
|
);
|
|
if (EFI_ERROR (Status) && TotalBootOptions == 0) {
|
|
return Status;
|
|
}
|
|
TotalBootOptions += (Size / sizeof(UINT16));
|
|
for (CurrentOption = 0, Found = FALSE, Status = EFI_NOT_FOUND; CurrentOption < TotalBootOptions && !Found; CurrentOption ++) {
|
|
UnicodeSPrint (BootOption, sizeof(BootOption), L"Boot%04x", BootOptions[CurrentOption]);
|
|
BootOptionData = CommonGetVariableData (BootOption, &gEfiGlobalVariableGuid);
|
|
if (BootOptionData == NULL) {
|
|
continue;
|
|
}
|
|
//
|
|
// Get the boot loader file path from the current Boot Option data
|
|
//
|
|
BootOptionDesc = (CHAR16 *)(BootOptionData + sizeof(UINT32) + sizeof(UINT16));
|
|
BootDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)(BootOptionData + sizeof(UINT32) + sizeof(UINT16) + StrSize(BootOptionDesc));
|
|
if (IS_USB_SHORT_FORM_DEVICE_PATH (BootDevicePath)){
|
|
ExpandedBootDevicePath = BdsLibExpandUsbShortFormDevPath (BootDevicePath);
|
|
BootDevicePath = ExpandedBootDevicePath;
|
|
}
|
|
Status = GetBootFilePathDevicePathOffset (BootDevicePath, &BootFilePathDevicePathOffset);
|
|
if (Status == EFI_SUCCESS) {
|
|
CompareSize = BootFilePathDevicePathOffset;
|
|
} else {
|
|
CompareSize = GetDevicePathSize (BootDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL);
|
|
}
|
|
Status = gBS->LocateHandleBuffer(
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (BootOptionData);
|
|
continue;
|
|
}
|
|
|
|
for (Index = 0; Index < NumberOfHandles; Index++) {
|
|
Status = gBS->HandleProtocol(
|
|
HandleBuffer[Index],
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **)SysRootDevicePath
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
if (CompareMem (
|
|
((UINT8 *)*SysRootDevicePath) + GetDevicePathSize (*SysRootDevicePath) - CompareSize - sizeof(EFI_DEVICE_PATH),
|
|
BootDevicePath,
|
|
CompareSize) == 0) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (Found) {
|
|
Status = gBS->HandleProtocol(
|
|
HandleBuffer[Index],
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **)&SimpleFileSystem
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, SysRootHandle);
|
|
}
|
|
} else {
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
FreePool (HandleBuffer);
|
|
FreePool (BootOptionData);
|
|
if (ExpandedBootDevicePath != NULL){
|
|
FreePool (ExpandedBootDevicePath);
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get the information of system drive which contain capsule image file.
|
|
|
|
@param[out] SysRootDevicePath The pointer of system root device path pointer
|
|
@param[out] SysRoorHandle The pointer of EFI_FILE_HANDLE of system drive
|
|
|
|
@retval EFI_SUCCESS The system drive information is correctly get
|
|
@return Others Unable to get system drive information
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetSystemRootInfo (
|
|
OUT EFI_DEVICE_PATH **SysRootDevicePath,
|
|
OUT EFI_FILE_HANDLE *SysRootHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = GetSystemRootInfoFromCurrentBootOption (SysRootDevicePath, SysRootHandle);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (HaveCapsuleImageFile (*SysRootHandle)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
(*SysRootHandle)->Close (*SysRootHandle);
|
|
}
|
|
|
|
return GetSystemRootInfoFromFileSystems (SysRootDevicePath, SysRootHandle);
|
|
}
|
|
|
|
/**
|
|
Dump raw data.
|
|
|
|
@param[in] Data raw data
|
|
@param[in] Size raw data size
|
|
|
|
**/
|
|
VOID
|
|
InternalDumpData (
|
|
IN UINT8 *Data8,
|
|
IN UINTN DataSize
|
|
)
|
|
{
|
|
DEBUG_CODE_BEGIN();
|
|
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < DataSize; Index++) {
|
|
if (Index % 0x10 == 0) {
|
|
DEBUG ((DEBUG_INFO, "\n%08X:", Index));
|
|
}
|
|
DEBUG ((DEBUG_INFO, " %02X", *Data8++));
|
|
}
|
|
DEBUG ((DEBUG_INFO, "\n"));
|
|
|
|
DEBUG_CODE_END();
|
|
}
|
|
|
|
/**
|
|
Calculate SHA256 Hash
|
|
|
|
@param[in] Data data
|
|
@param[in] Size data size
|
|
@param[out] Digest SHA256 digest
|
|
|
|
**/
|
|
VOID
|
|
CreateSha256Hash (
|
|
IN UINT8 *Data,
|
|
IN UINTN Size,
|
|
OUT UINT8 *Digest
|
|
)
|
|
{
|
|
UINTN CtxSize;
|
|
VOID *HashCtx;
|
|
|
|
CtxSize = Sha256GetContextSize ();
|
|
HashCtx = AllocatePool (CtxSize);
|
|
ASSERT (HashCtx != NULL);
|
|
Sha256Init (HashCtx);
|
|
Sha256Update (HashCtx, Data, Size);
|
|
Sha256Final (HashCtx, Digest);
|
|
InternalDumpData (Digest, 32);
|
|
|
|
FreePool (HashCtx);
|
|
}
|
|
|
|
/**
|
|
Delete a file from root file handle.
|
|
|
|
@param[in] Root The root file handle.
|
|
@param[in] FileName Pointer to file name.
|
|
|
|
@retval EFI_SUCCESS File does not exist or deleted the file successfully.
|
|
@retval Others Failed to delete the file.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
DeleteFileFromRoot (
|
|
IN EFI_FILE_HANDLE Root,
|
|
IN CHAR16 *FileName
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE *FileHandle;
|
|
|
|
DEBUG ((DEBUG_INFO, "DeleteFileFromRoot - start\n"));
|
|
|
|
Status = Root->Open (Root, &FileHandle, FileName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
DEBUG ((DEBUG_INFO, "File %s does not exist. No need to delete\n", FileName));
|
|
return EFI_SUCCESS;
|
|
} else if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Cannot open file: %s. Status = %r\n", FileName, Status));
|
|
return Status;
|
|
}
|
|
|
|
if (FileHandle == NULL) {
|
|
Status = EFI_UNSUPPORTED;
|
|
DEBUG ((DEBUG_ERROR, "Failed to open root dir - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = FileHandle->Delete (FileHandle);
|
|
DEBUG ((DEBUG_INFO, "DeleteFileFromRoot %s %r\n", FileName, Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Write a file to an root file handle.
|
|
|
|
@param[in] SysRoot The system root file handle.
|
|
@param[in] FileName Pointer to file name.
|
|
@param[in] FileBuffer The buffer to be written into file system.
|
|
@param[in] FileSize The size of FileBuffer.
|
|
|
|
@retval EFI_SUCCESS Wrote the file successfully.
|
|
@retval Others Failed to write the file.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
WriteFileToSysRoot (
|
|
IN EFI_FILE_HANDLE SysRoot,
|
|
IN CHAR16 *FileName,
|
|
IN UINT8 *FileBuffer,
|
|
IN UINTN FileSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE *FileHandle;
|
|
UINTN WriteSize;
|
|
|
|
DEBUG ((DEBUG_INFO, "WriteRecoveryFile - start\n"));
|
|
|
|
Status = SysRoot->Open (SysRoot, &FileHandle, FileName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Open file on root: %s. error - %r\n", FileName, Status));
|
|
return Status;
|
|
}
|
|
|
|
if (FileHandle == NULL) {
|
|
Status = EFI_UNSUPPORTED;
|
|
DEBUG ((DEBUG_ERROR, "Open file on root: %s. error - %r\n", FileName, Status));
|
|
return Status;
|
|
}
|
|
|
|
do {
|
|
WriteSize = (FileSize > SIZE_4KB) ? SIZE_4KB : FileSize;
|
|
Status = FileHandle->Write (FileHandle, &WriteSize, FileBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
FileSize = FileSize - WriteSize;
|
|
FileBuffer = FileBuffer + WriteSize;
|
|
} while (FileSize > 0);
|
|
|
|
DEBUG ((DEBUG_INFO, "WriteRecoveryFile %s %r\n", FileName, Status));
|
|
FileHandle->Close (FileHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Read a file from a root file handle.
|
|
|
|
@param[in] SysRoot The system root file handle.
|
|
@param[in] FileName Pointer to file name.
|
|
@param[in] FileBuffer The buffer read from file system.
|
|
@param[in] FileSize The size of FileBuffer.
|
|
|
|
@retval EFI_SUCCESS Read the file successfully.
|
|
@retval Others Failed to read the file.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ReadFileFromSysRoot (
|
|
IN EFI_FILE_HANDLE SysRoot,
|
|
IN CHAR16 *FileName,
|
|
OUT VOID **Buffer,
|
|
OUT UINTN *BufferSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE *FileHandle;
|
|
UINTN FileInfoSize;
|
|
EFI_FILE_INFO *FileInfo;
|
|
EFI_GUID FileInfoGuid = EFI_FILE_INFO_ID;
|
|
|
|
DEBUG ((DEBUG_INFO, "ReadRecoveryFile - start\n"));
|
|
|
|
Status = SysRoot->Open (SysRoot, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Open file on root: %s. error - %r\n", FileName, Status));
|
|
return Status;
|
|
}
|
|
|
|
if (FileHandle == NULL) {
|
|
Status = EFI_UNSUPPORTED;
|
|
DEBUG ((DEBUG_ERROR, "Open file on root: %s. error - %r\n", FileName, Status));
|
|
return Status;
|
|
}
|
|
|
|
FileInfoSize = 0;
|
|
FileInfo = NULL;
|
|
|
|
Status = FileHandle->GetInfo (
|
|
FileHandle,
|
|
&FileInfoGuid,
|
|
&FileInfoSize,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
DEBUG ((DEBUG_ERROR, "GetInfo error - %r\n", Status));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "FileRead\n"));
|
|
FileInfo = AllocatePool (FileInfoSize);
|
|
if (FileInfo == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "GetInfo fail, AllocatePool(Size: %x) error\n", FileInfoSize));
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FileHandle->GetInfo (
|
|
FileHandle,
|
|
&FileInfoGuid,
|
|
&FileInfoSize,
|
|
FileInfo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "FileRead fail, GetInfo error, Status: %r\n", Status));
|
|
goto Exit;
|
|
}
|
|
|
|
*BufferSize = (UINT32) FileInfo->FileSize;
|
|
if (*BufferSize != 0) {
|
|
*Buffer = AllocateZeroPool (*BufferSize);
|
|
if (*Buffer == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Read fail, AllocatePool(Size: %x) error\n", FileInfoSize));
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FileHandle->Read (
|
|
FileHandle,
|
|
BufferSize,
|
|
*Buffer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Read fail, error - %r\n", Status));
|
|
FreePool (*Buffer);
|
|
goto Exit;
|
|
}
|
|
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "File size is 0, set return Buffer to NULL.\n"));
|
|
*Buffer = NULL;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
Exit:
|
|
if (FileInfo != NULL) {
|
|
FreePool (FileInfo);
|
|
}
|
|
|
|
FileHandle->Close (FileHandle);
|
|
|
|
DEBUG ((DEBUG_INFO, "ReadRecoveryFile %s BufferSize: %x %r\n", FileName, *BufferSize, Status));
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Update a block on flash.
|
|
|
|
@param[in] Src Number of blocks to be erased
|
|
@param[in] NumBytes Contains the total size of the buffer.
|
|
@param[in] WriteAddress Target address to be updated
|
|
|
|
@retval EFI_SUCCESS. Operation is successful.
|
|
@retval EFI_INVALID_PARAMETER Src is NULL or WriteAddress is not well aligned.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate needed memory buffer.
|
|
@retval EFI_VOLUME_CORRUPTED The block is not updated as expected.
|
|
@retval Others If there is any device errors.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
FlashUpdate (
|
|
IN UINT8 *Src,
|
|
IN UINTN NumBytes,
|
|
IN UINTN WriteAddress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *CompareBuffer;
|
|
UINTN Size;
|
|
|
|
if ((Src == NULL) || ((NumBytes % GetFlashBlockSize ()) != 0) || ((WriteAddress % SIZE_4KB) != 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
CompareBuffer = AllocateZeroPool (NumBytes);
|
|
if (CompareBuffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashRead (
|
|
CompareBuffer,
|
|
(UINT8 *)WriteAddress,
|
|
NumBytes
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Read flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
if (CompareMem (CompareBuffer, Src, NumBytes) == 0) {
|
|
//
|
|
// No need to update
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashErase (WriteAddress, NumBytes);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Erase flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
Size = NumBytes;
|
|
Status = FlashProgram (
|
|
(UINT8 *)WriteAddress,
|
|
(UINT8 *)Src,
|
|
&Size,
|
|
WriteAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Program flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Read buffer back to verify udpate status
|
|
//
|
|
ZeroMem (CompareBuffer, NumBytes);
|
|
Status = FlashRead (
|
|
CompareBuffer,
|
|
(UINT8 *)WriteAddress,
|
|
NumBytes
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Read flash address 0x%x error %r\n", WriteAddress));
|
|
goto Exit;
|
|
}
|
|
|
|
if (CompareMem (CompareBuffer, Src, NumBytes) != 0) {
|
|
//
|
|
// Block is not updated as expected.
|
|
//
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
DEBUG ((DEBUG_ERROR, "FlashUpdate: Block is not updated as expected.\n"));
|
|
}
|
|
|
|
Exit:
|
|
if (CompareBuffer != NULL) {
|
|
FreePool (CompareBuffer);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Check whether need to backup the current SBB into storge.
|
|
|
|
@param None
|
|
|
|
@retval TRUE Backup is needed.
|
|
FALSE Backup is not needed.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsSbbBackupRequired (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN DataSize;
|
|
UINT8 Sha256[SHA256_DIGEST_SIZE];
|
|
|
|
ZeroMem (Sha256, SHA256_DIGEST_SIZE);
|
|
DataSize = SHA256_DIGEST_SIZE;
|
|
Status = CommonGetVariable (
|
|
BACKUP_SBB_FILE_DIGEST_SHA256_NAME,
|
|
&gH2OSeamlessRecoveryDigestGuid,
|
|
&DataSize,
|
|
&Sha256
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Check whether the input SBB is identical with the sha256 digest.
|
|
|
|
@param [in] FileBuffer The Backup SBB file data buffer
|
|
@param [in] FileSize The Backup SBB file size
|
|
|
|
@retval TRUE The digest of FileBuffer is identical with the digest in variable.
|
|
FALSE The digest of FileBuffer is not identical with the digest in variable.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsBackupSbbValid (
|
|
IN VOID *FileBuffer,
|
|
IN UINTN FileSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 Sha256[SHA256_DIGEST_SIZE];
|
|
UINT8 Sha256Digest[SHA256_DIGEST_SIZE];
|
|
UINTN VariableSize;
|
|
|
|
ZeroMem (Sha256, SHA256_DIGEST_SIZE);
|
|
CreateSha256Hash ((UINT8 *)(UINTN)FileBuffer, FileSize, Sha256);
|
|
//
|
|
// Check SBB digest
|
|
//
|
|
ZeroMem (Sha256Digest, SHA256_DIGEST_SIZE);
|
|
VariableSize = SHA256_DIGEST_SIZE;
|
|
Status = CommonGetVariable (
|
|
BACKUP_SBB_FILE_DIGEST_SHA256_NAME,
|
|
&gH2OSeamlessRecoveryDigestGuid,
|
|
&VariableSize,
|
|
Sha256Digest
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to get message digest of the backup SBB.\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (CompareMem (Sha256Digest, Sha256, VariableSize) != 0) {
|
|
DEBUG ((DEBUG_ERROR, "SBB image loaded from media is corrupted.\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Backup the current SBB to the storage.
|
|
|
|
@retval EFI_SUCCESS Operation was successful.
|
|
@retval Others Operation was failed.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
BackupCurrentSbbToStorage (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *SysRootDevicePath;
|
|
EFI_FILE_HANDLE SysRootHandle;
|
|
UINT8 Sha256[SHA256_DIGEST_SIZE];
|
|
UINT8 *Buffer;
|
|
UINTN BufferSize;
|
|
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
|
|
|
|
Status = GetSystemRootInfo (&SysRootDevicePath, &SysRootHandle);
|
|
if (EFI_ERROR (Status) || SysRootHandle == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Read the current SBB from flash part
|
|
//
|
|
BufferSize = (UINTN)PcdGet32 (PcdFlashSbbSize);
|
|
Buffer = AllocateZeroPool (BufferSize);
|
|
if (Buffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashRead (
|
|
Buffer,
|
|
(UINT8 *)(UINTN)PcdGet32 (PcdFlashSbbBase),
|
|
BufferSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Backup SBB to the system root.
|
|
//
|
|
Status = WriteFileToSysRoot (
|
|
SysRootHandle,
|
|
BACKUP_SBB_FILE_NAME,
|
|
Buffer,
|
|
BufferSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to write backup SBB to hdd. %r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Calculate the hash
|
|
//
|
|
ZeroMem (Sha256, SHA256_DIGEST_SIZE);
|
|
DEBUG ((DEBUG_INFO, "Create recovery file digest:\n"));
|
|
CreateSha256Hash ((UINT8 *) (UINTN)Buffer, BufferSize, Sha256);
|
|
|
|
Status = CommonSetVariable (
|
|
BACKUP_SBB_FILE_DIGEST_SHA256_NAME,
|
|
&gH2OSeamlessRecoveryDigestGuid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
SHA256_DIGEST_SIZE,
|
|
Sha256
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to save sha256 digest for recovery file. %r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
Status = VariableLock->RequestToLock (
|
|
VariableLock,
|
|
BACKUP_SBB_FILE_DIGEST_SHA256_NAME,
|
|
&gH2OSeamlessRecoveryDigestGuid
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to lock variable %s, Status = %r\n", BACKUP_SBB_FILE_DIGEST_SHA256_NAME, Status));
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
if (Buffer != NULL) {
|
|
FreePool (Buffer);
|
|
}
|
|
if (SysRootHandle != NULL) {
|
|
SysRootHandle->Close (SysRootHandle);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Determine if two buffers overlap in memory.
|
|
|
|
@param[in] Buff1 pointer to first buffer
|
|
@param[in] Size1 size of Buff1
|
|
@param[in] Buff2 pointer to second buffer
|
|
@param[in] Size2 size of Buff2
|
|
|
|
@retval TRUE Buffers overlap in memory.
|
|
@retval FALSE Buffer doesn't overlap.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsOverlapped (
|
|
IN UINT8 *Buff1,
|
|
IN UINTN Size1,
|
|
IN UINT8 *Buff2,
|
|
IN UINTN Size2
|
|
)
|
|
{
|
|
//
|
|
// If buff1's end is less than the start of buff2, then it's ok.
|
|
// Also, if buff1's start is beyond buff2's end, then it's ok.
|
|
//
|
|
if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Check if fault tolerance is correctly supported.
|
|
|
|
@retval EFI_SUCCESS Sanity check pass.
|
|
@retval EFI_SECURITY_VIOLATION Sanity check fail.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
FaultToleranceSanityCheck (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS BootBlockBase;
|
|
UINTN BootBlockSize;
|
|
EFI_PHYSICAL_ADDRESS BackupBlockBase;
|
|
UINTN BackupBlockSize;
|
|
UINT64 RecoveryFvSize;
|
|
EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SwapAddressRange;
|
|
|
|
if (!FeaturePcdGet (PcdH2OBiosUpdateFaultToleranceEnabled)) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck - PcdH2OBiosUpdateFaultToleranceEnabled is FALSE\n"));
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiSwapAddressRangeProtocolGuid,
|
|
NULL,
|
|
(VOID **)&SwapAddressRange
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck - SwapAddressRange not found\n"));
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
Status = SwapAddressRange->GetRangeLocation (
|
|
SwapAddressRange,
|
|
&BootBlockBase,
|
|
&BootBlockSize,
|
|
&BackupBlockBase,
|
|
&BackupBlockSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck - SwapAddressRange->GetRangeLocation %r\n", Status));
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
if ((BootBlockSize == 0) ||
|
|
(BackupBlockSize == 0)) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck - Invalid BootBlockSize/BackupBlockSize\n"));
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
if (((UINTN)PcdGet32 (PcdFlashPbbSize) != (UINTN)BootBlockSize) ||
|
|
((UINTN)PcdGet32 (PcdFlashPbbSize) != (UINTN)BackupBlockSize) ||
|
|
((UINTN)PcdGet32 (PcdFlashPbbBase) != (UINTN)BootBlockBase) ||
|
|
((UINTN)PcdGet32 (PcdFlashPbbRBase) != (UINTN)BackupBlockBase)) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck - PBB/SBB PCDs are incorrect\n"));
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
RecoveryFvSize = FdmGetNAtSize (
|
|
&gH2OFlashMapRegionBootFvGuid,
|
|
1
|
|
);
|
|
if ((UINTN)RecoveryFvSize > (UINTN)BackupBlockSize) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck - PBB size is too small for fault tolerance\n"));
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
if (IsFirmwareUpdateResiliencySupported ()) {
|
|
if (IsOverlapped (
|
|
(UINT8 *)(UINTN)BackupBlockBase,
|
|
BackupBlockSize,
|
|
(UINT8 *)(UINTN)PcdGet32 (PcdFlashSbbBase),
|
|
PcdGet32 (PcdFlashSbbSize)
|
|
)) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck - PBBR and SBB are overlapped. Invalid flash layout\n"));
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
BIOS update fault tolerance handler on ready to boot before.
|
|
|
|
@param[in] Event - A pointer to the Event that triggered the callback.
|
|
@param[in] Context - A pointer to private data registered with the callback function.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
BiosUpdateFaultToleranceOnReadyToBootBefore (
|
|
IN EFI_EVENT Event,
|
|
IN H2O_CP_HANDLE Handle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *SysRootDevicePath;
|
|
EFI_FILE_HANDLE SysRootHandle;
|
|
|
|
H2OCpUnregisterHandler (Handle);
|
|
|
|
//
|
|
// The update has been processed before this funtion called.
|
|
// Clear the update progress at this point.
|
|
//
|
|
ClearFirmwareUpdateProgress ();
|
|
|
|
//
|
|
// Delete backup file if system doesn't support resiliency.
|
|
//
|
|
if (!IsFirmwareUpdateResiliencySupported () && !IsSbbBackupRequired ()) {
|
|
Status = GetSystemRootInfo (&SysRootDevicePath, &SysRootHandle);
|
|
if (EFI_ERROR (Status) || SysRootHandle == NULL) {
|
|
return;
|
|
}
|
|
DeleteFileFromRoot (SysRootHandle, BACKUP_SBB_FILE_NAME);
|
|
SysRootHandle->Close (SysRootHandle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
BIOS update fault tolerance handler on end of dxe before.
|
|
|
|
@param[in] Event - A pointer to the Event that triggered the callback.
|
|
@param[in] Context - A pointer to private data registered with the callback function.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
BiosUpdateFaultToleranceOnEndOfDxeBefore (
|
|
IN EFI_EVENT Event,
|
|
IN H2O_CP_HANDLE Handle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Buffer;
|
|
UINTN BufferSize;
|
|
EFI_SWAP_ADDRESS_RANGE_PROTOCOL *SwapAddressRange;
|
|
BOOLEAN SwapState;
|
|
EFI_DEVICE_PATH_PROTOCOL *SysRootDevicePath;
|
|
EFI_FILE_HANDLE SysRootHandle;
|
|
|
|
H2OCpUnregisterHandler (Handle);
|
|
|
|
SysRootHandle = NULL;
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiSwapAddressRangeProtocolGuid,
|
|
NULL,
|
|
(VOID **)&SwapAddressRange
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "Locate SwapAddressRangeProtocol %r\n", Status));
|
|
return;
|
|
}
|
|
|
|
Status = FaultToleranceSanityCheck ();
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "FaultToleranceSanityCheck %r\n", Status));
|
|
POST_CODE (DXE_FAULT_TOLERANCE_FCHECK_FAILED);
|
|
CpuDeadLoop ();
|
|
}
|
|
|
|
if (IsSbbBackupRequired ()) {
|
|
DEBUG ((DEBUG_INFO, "BackupCurrentSbbToStorage for fault tolerence update\n"));
|
|
Status = BackupCurrentSbbToStorage ();
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "BackupCurrentSbbToStorage %r\n"));
|
|
}
|
|
}
|
|
|
|
if (!IsFirmwareUpdateResiliencyEnabled ()) {
|
|
return;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "There was a firmware update completion. Start the resiliency procedure\n"));
|
|
|
|
Status = SwapAddressRange->GetSwapState (SwapAddressRange, &SwapState);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "GetSwapState %r\n", Status));
|
|
return;
|
|
}
|
|
|
|
if (!SwapState) {
|
|
//
|
|
// Update was successfully.
|
|
// Flash the current PBB content into PBBR.
|
|
//
|
|
DEBUG ((DEBUG_INFO, "PBB SyncUp. Source Base = %X\n", (UINTN) PcdGet32 (PcdFlashPbbBase)));
|
|
//
|
|
// Read the current PBB from flash part
|
|
//
|
|
BufferSize = (UINTN)PcdGet32 (PcdFlashPbbSize);
|
|
Buffer = AllocateZeroPool (BufferSize);
|
|
if (Buffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashRead (
|
|
Buffer,
|
|
(UINT8 *)(UINTN)PcdGet32 (PcdFlashPbbBase),
|
|
BufferSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Read the current PBB error %r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Flash current PBB into PBBR
|
|
//
|
|
Status = FlashUpdate (
|
|
(UINT8 *)Buffer,
|
|
BufferSize,
|
|
(UINTN)PcdGet32 (PcdFlashPbbRBase)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Flash current PBB into PBBR error %r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "SBB SyncUp in Disk\n"));
|
|
Status = BackupCurrentSbbToStorage ();
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "BackupCurrentSbbToStorage %r\n"));
|
|
}
|
|
} else {
|
|
//
|
|
// Update was failed. TopSwap is enabled.
|
|
// Rollback is processing.
|
|
//
|
|
DEBUG ((DEBUG_INFO, "Start RollBack\n"));
|
|
//
|
|
// Read the current PBBR from flash part
|
|
//
|
|
BufferSize = (UINTN)PcdGet32 (PcdFlashPbbSize);
|
|
Buffer = AllocateZeroPool (BufferSize);
|
|
if (Buffer == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashRead (
|
|
Buffer,
|
|
(UINT8 *)(UINTN)PcdGet32 (PcdFlashPbbRBase),
|
|
BufferSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Read the current PBBR error %r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "PBB RollBack. Source Base = %X\n", (UINTN)PcdGet32(PcdFlashPbbRBase)));
|
|
//
|
|
// Flash current PBBR into PBB
|
|
//
|
|
Status = FlashUpdate (
|
|
(UINT8 *)Buffer,
|
|
BufferSize,
|
|
(UINTN)PcdGet32 (PcdFlashPbbBase)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Flash PBBR %r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "SBB RollBack\n"));
|
|
Status = GetSystemRootInfo (&SysRootDevicePath, &SysRootHandle);
|
|
if (EFI_ERROR (Status) || SysRootHandle == NULL) {
|
|
goto Exit;
|
|
}
|
|
//
|
|
// Read the backup SBB from sys root.
|
|
//
|
|
Status = ReadFileFromSysRoot (
|
|
SysRootHandle,
|
|
BACKUP_SBB_FILE_NAME,
|
|
(VOID **)&Buffer,
|
|
&BufferSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Can't fetch bakcup SBB on disk %r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
if (!IsBackupSbbValid ((VOID *)Buffer, BufferSize)) {
|
|
DEBUG ((DEBUG_ERROR, "BackupSbb in disk is invalid\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
Status = FlashUpdate (
|
|
(UINT8 *)Buffer,
|
|
BufferSize,
|
|
(UINTN)PcdGet32 (PcdFlashSbbBase)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Flash PBBR %r\n"));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete progress variable
|
|
//
|
|
Status = ClearFirmwareUpdateProgress ();
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Delete progress variable %r\n"));
|
|
}
|
|
|
|
//
|
|
// Disable topswap bit
|
|
//
|
|
Status = SwapAddressRange->SetSwapState (SwapAddressRange, FALSE);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "SetSwapState %r\n", Status));
|
|
}
|
|
|
|
gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
|
|
|
|
Exit:
|
|
if (Buffer != NULL) {
|
|
FreePool (Buffer);
|
|
}
|
|
if (SysRootHandle != NULL) {
|
|
SysRootHandle->Close (SysRootHandle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Lock Progress varaible.
|
|
|
|
@param[in] Event Event whose notification function is being invoked
|
|
@param[in] Context Pointer to the notification function's context
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
LockProgressVariable (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLockProtocol;
|
|
|
|
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLockProtocol);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// This variable controls the update progress.
|
|
// It should be protected from malicious software. We set it as read-only variable here.
|
|
//
|
|
Status = VariableLockProtocol->RequestToLock (
|
|
VariableLockProtocol,
|
|
UPDATE_PROGRESS_NAME,
|
|
&gH2OSeamlessRecoveryGuid
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "Error when lock variable %s, Status = %r\n", UPDATE_PROGRESS_NAME, Status));
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
gBS->CloseEvent (Event);
|
|
}
|
|
|
|
/**
|
|
Initialize BIOS update fault tolerance support for system firmware update.
|
|
|
|
@retval EFI_SUCCESS Fault Tolerence Support is initialized successfully
|
|
@retval EFI_NOT_FOUND Fault Tolerence Support is not initialized successfully
|
|
**/
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BiosUpdateFaultToleranceDxeEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
H2O_CP_HANDLE CpHandle;
|
|
VOID *Registration;
|
|
|
|
if (FeaturePcdGet (PcdH2OBdsCpEndOfDxeBeforeSupported)) {
|
|
Status = H2OCpRegisterHandler (
|
|
&gH2OBdsCpEndOfDxeBeforeGuid,
|
|
BiosUpdateFaultToleranceOnEndOfDxeBefore,
|
|
H2O_CP_MEDIUM,
|
|
&CpHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpEndOfDxeBeforeGuid, Status));
|
|
return Status;
|
|
}
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpEndOfDxeBeforeGuid, Status));
|
|
}
|
|
|
|
if (FeaturePcdGet (PcdH2OBdsCpReadyToBootBeforeSupported)) {
|
|
Status = H2OCpRegisterHandler (
|
|
&gH2OBdsCpReadyToBootBeforeGuid,
|
|
BiosUpdateFaultToleranceOnReadyToBootBefore,
|
|
H2O_CP_LOW,
|
|
&CpHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpReadyToBootBeforeGuid, Status));
|
|
return Status;
|
|
}
|
|
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpReadyToBootBeforeGuid, Status));
|
|
}
|
|
|
|
//
|
|
// Lock the Progress variable.
|
|
//
|
|
EfiCreateProtocolNotifyEvent (
|
|
&gEdkiiVariableLockProtocolGuid,
|
|
TPL_CALLBACK,
|
|
LockProgressVariable,
|
|
NULL,
|
|
(VOID **)&Registration
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|