alder_lake_bios/Insyde/InsydeModulePkg/Universal/Recovery/CrisisRecoveryPei/CrisisRecovery.c

1388 lines
44 KiB
C

/** @file
Crisis Recovery Implementation
;******************************************************************************
;* Copyright (c) 2012 - 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 "CrisisRecovery.h"
#include <Library/BaseOemSvcKernelLib.h>
#include <Library/PeiOemSvcKernelLib.h>
#include <Library/FlashRegionLib.h>
#include <Library/VariableSupportLib.h>
#include <Library/BaseLib.h>
#include <Library/PrintLib.h>
#include <Library/BaseCryptLib.h>
#include <Ppi/FileAccessPei.h>
#include <Guid/FmpCapsule.h>
#include <PostCode.h>
#define FAT_MAX_FILE_PATH_LENGTH 256
FLASH_ENTRY mProtectRegion[] = {
{
0, // RecoveryFV Address to be updated
0, // RecoveryFv Size to be updated
0 // RecoveryFv Offset to be updated
},
{0x0, 0x0, 0x0}
};
EFI_FFS_FILE_HEADER *mFfsHeader;
//
// Module globals
//
EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = {PlatformRecoveryModule};
EFI_PEI_PPI_DESCRIPTOR mRecoveryPpiList = {
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiPeiRecoveryModulePpiGuid,
&mRecoveryPpi
};
/**
Loads a file capsule from disk with a specific name.
@param[in] PeiServices General-purpose services that are available
to every PEIM
@param[in] FileName The name of the file to be loaded.
@param[in] File The file buffer.
@param[out] FileSize The file size.
@retval EFI_SUCCESS The file was loaded correctly.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_NOT_FOUND A requested file cannot be found.
**/
STATIC
EFI_STATUS
LoadFileOnDisk (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN CHAR16 *FileName,
OUT VOID **File,
OUT UINTN *FileSize
)
{
EFI_STATUS Status;
PEI_FILE_ACCESS_PPI *FileAccess;
UINTN VolumeCount;
UINTN Index;
PEI_FILE_HANDLE Handle;
UINTN FileInfoSize;
PEI_FILE_INFO *FileInfo;
UINTN BufferSize;
VOID *Buffer;
Status = (**PeiServices).LocatePpi (
PeiServices,
&gPeiFileAccessPpiGuid,
0,
NULL,
(VOID **)&FileAccess
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Search each volume for the file
//
VolumeCount = FileAccess->GetNumberOfVolumes (FileAccess);
for (Index = 0; Index < VolumeCount; Index++) {
Status = FileAccess->OpenFile (
FileAccess,
FileName,
&Handle,
EFI_FILE_MODE_READ,
EFI_FILE_ARCHIVE,
Index
);
if (EFI_ERROR (Status)) {
continue;
}
FileInfoSize = sizeof (PEI_FILE_INFO) + FAT_MAX_FILE_PATH_LENGTH * sizeof (CHAR16);
FileInfo = AllocatePages (EFI_SIZE_TO_PAGES (FileInfoSize));
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = FileAccess->GetFileInfo (FileAccess, Handle, &FileInfoSize, FileInfo);
if (EFI_ERROR (Status)) {
FileAccess->CloseFile (FileAccess, Handle);
continue;
}
BufferSize = (UINTN)FileInfo->FileSize;
Buffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
if (Buffer == NULL) {
FreePages (FileInfo, EFI_SIZE_TO_PAGES (FileInfoSize));
FileAccess->CloseFile (FileAccess, Handle);
return EFI_OUT_OF_RESOURCES;
}
Status = FileAccess->ReadFile (
FileAccess,
Handle,
ReadData,
&BufferSize,
Buffer
);
if (EFI_ERROR (Status)) {
FreePages (FileInfo, EFI_SIZE_TO_PAGES (FileInfoSize));
FreePages (Buffer, EFI_SIZE_TO_PAGES (BufferSize));
FileAccess->CloseFile (FileAccess, Handle);
return Status;
}
FreePages (FileInfo, EFI_SIZE_TO_PAGES (FileInfoSize));
FileAccess->CloseFile (FileAccess, Handle);
*FileSize = BufferSize;
*File = Buffer;
break;
}
return Status;
}
/**
Check whether loading backup SBB is needed.
@retval TRUE Load backup SBB is needed.
@retval FALSE Load backup SBB is not needed.
**/
STATIC
BOOLEAN
LoadBackupSbbIsRequired (
VOID
)
{
EFI_STATUS Status;
UINT32 Progress;
EFI_HOB_GUID_TYPE *GuidHob;
GuidHob = GetFirstGuidHob (&gH2OSeamlessRecoveryDigestGuid);
if (GuidHob == NULL) {
//
// Backup SBB not found.
//
return FALSE;
}
Status = GetFirmwareUpdateProgress (&Progress);
if (EFI_ERROR (Status)) {
//
// Load backup SBB for recovery in default.
//
return TRUE;
}
if (IsFirmwareUpdateResiliencySupported ()) {
return ((Progress == FlashPbb) || (Progress == FlashResiliency));
} else {
return (Progress == FlashPbbR);
}
}
/**
Calculate SHA256 Hash
@param[in] Data data
@param[in] Size data size
@param[out] Digest SHA256 digest
**/
STATIC
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);
FreePool (HashCtx);
}
/**
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.
**/
STATIC
BOOLEAN
IsBackupSbbValid (
IN VOID *FileBuffer,
IN UINTN FileSize
)
{
EFI_HOB_GUID_TYPE *GuidHob;
UINT8 Sha256Digest[SHA256_DIGEST_SIZE];
ZeroMem (Sha256Digest, SHA256_DIGEST_SIZE);
CreateSha256Hash ((UINT8 *)(UINTN)FileBuffer, FileSize, Sha256Digest);
//
// Check SBB digest hob
//
GuidHob = GetFirstGuidHob (&gH2OSeamlessRecoveryDigestGuid);
if (GuidHob == NULL) {
DEBUG ((DEBUG_ERROR, "Failed to get message digest of the backup SBB.\n"));
return FALSE;
}
if (CompareMem ((VOID *)Sha256Digest, GET_GUID_HOB_DATA (GuidHob), SHA256_DIGEST_SIZE) != 0) {
DEBUG ((DEBUG_ERROR, "SBB image loaded from media is corrupted.\n"));
return FALSE;
}
return TRUE;
}
/**
Loads backup SBB file from disk.
@param[in] PeiServices General-purpose services that are available
to every PEIM
@param[in] File The file buffer.
@param[out] FileSize The file size.
@retval EFI_SUCCESS The file was loaded correctly.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_NOT_FOUND A requested file cannot be found.
**/
STATIC
EFI_STATUS
LoadBackupSbbFile (
IN CONST EFI_PEI_SERVICES **PeiServices,
OUT VOID **File,
OUT UINTN *FileSize
)
{
EFI_STATUS Status;
VOID *Buffer;
UINTN BufferSize;
Buffer = NULL;
BufferSize = 0;
Status = LoadFileOnDisk (
PeiServices,
BACKUP_SBB_FILE_NAME,
&Buffer,
&BufferSize
);
if (EFI_ERROR (Status)) {
return Status;
}
if (!IsBackupSbbValid (Buffer, BufferSize)) {
return EFI_SECURITY_VIOLATION;
}
*File = Buffer;
*FileSize = BufferSize;
return EFI_SUCCESS;
}
/**
Read the recovery caspule from PeiDeviceRecoveryModulePpi.
@param [in] PeiServices General purpose services available to every PEIM.
@param [out] CapsuleSize Capsule size.
@param [out] Capsule Pointer to the capsule.
@retval EFI_SUCCESS The capsule was loaded correctly.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_NOT_FOUND A requested recovery apsule cannot be found.
**/
STATIC
EFI_STATUS
LoadRecoveryCapsule (
IN EFI_PEI_SERVICES **PeiServices,
OUT VOID **Capsule,
OUT UINTN *CapsuleSize
)
{
EFI_STATUS Status;
EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryModule;
UINTN NumberRecoveryCapsules;
UINTN RecoveryCapsuleSize;
EFI_GUID DeviceId;
VOID *Buffer;
UINT8 DeviceRecoveryModuleFound;
UINTN Index;
Status = EFI_SUCCESS;
DeviceRecoveryModule = NULL;
DeviceRecoveryModuleFound = FALSE;
Index = 0;
while (!DeviceRecoveryModuleFound) {
//
// Search the platform for some recovery capsule if the DXE IPL
// discovered a recovery condition and has requested a load.
//
Status = PeiServicesLocatePpi (
&gEfiPeiDeviceRecoveryModulePpiGuid,
Index,
NULL,
(VOID **)&DeviceRecoveryModule
);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Device Recovery PPI located\n"));
Status = DeviceRecoveryModule->GetNumberRecoveryCapsules (
PeiServices,
DeviceRecoveryModule,
&NumberRecoveryCapsules
);
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Get Number of Recovery Capsules Returns: %r\n", Status));
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Number Of Recovery Capsules: %d\n", NumberRecoveryCapsules));
if (EFI_ERROR(Status) || (NumberRecoveryCapsules == 0)) {
Index++;
continue;
}
DeviceRecoveryModuleFound = TRUE;
}
//
// If there is an image provider, get the capsule ID
//
RecoveryCapsuleSize = 0;
Status = DeviceRecoveryModule->GetRecoveryCapsuleInfo (
PeiServices,
DeviceRecoveryModule,
0,
&RecoveryCapsuleSize,
&DeviceId
);
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Get Recovery Capsule Info Returns: %r\n", Status));
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Recovery Capsule Size: %d\n", RecoveryCapsuleSize));
if (EFI_ERROR (Status)) {
return Status;
}
//
// Only support the 2 capsule types known
// Future enhancement is to rank-order the selection
//
if ((!CompareGuid (&DeviceId, &gRecoveryOnFatIdeDiskGuid)) &&
(!CompareGuid (&DeviceId, &gRecoveryOnDataCdGuid)) &&
(!CompareGuid (&DeviceId, &gRecoveryOnFatUsbDiskGuid))
) {
return EFI_UNSUPPORTED;
}
Buffer = AllocatePages (EFI_SIZE_TO_PAGES (RecoveryCapsuleSize));
if (Buffer == NULL) {
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Allocate buffer for loading recovery capsule Returns: %r\n", EFI_OUT_OF_RESOURCES));
return EFI_OUT_OF_RESOURCES;
}
Status = DeviceRecoveryModule->LoadRecoveryCapsule (
PeiServices,
DeviceRecoveryModule,
0,
Buffer
);
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "LoadRecoveryCapsule Returns: %r\n", Status));
if (EFI_ERROR(Status)) {
FreePages (Buffer, EFI_SIZE_TO_PAGES (RecoveryCapsuleSize));
return Status;
}
*Capsule = Buffer;
*CapsuleSize = RecoveryCapsuleSize;
return Status;
}
STATIC
EFI_STATUS
EFIAPI
GetDefaultProtectRegion (
OUT FLASH_ENTRY **DefaultProtectTable,
OUT UINTN *TableSize
)
{
FLASH_ENTRY *Pool;
UINT64 RecoveryFvAddr;
UINT64 RecoveryFvSize;
UINTN DefaultSize;
EFI_STATUS Status;
RecoveryFvAddr = 0;
DefaultSize = sizeof(FLASH_ENTRY) * 2;
*TableSize = DefaultSize;
Status = PeiServicesAllocatePool (DefaultSize, (VOID**)&Pool);
*DefaultProtectTable = Pool;
if (EFI_ERROR(Status)){
return EFI_OUT_OF_RESOURCES;
}
RecoveryFvAddr = FdmGetNAtAddr (
&gH2OFlashMapRegionBootFvGuid,
1
);
RecoveryFvSize = FdmGetNAtSize (
&gH2OFlashMapRegionBootFvGuid,
1
);
Pool->WriteAddress = (UINT32) RecoveryFvAddr;
Pool->WriteSize = (UINT32) RecoveryFvSize;
Pool->SourceOffset = (UINT32) (RecoveryFvAddr - FdmGetBaseAddr());
Pool++;
Pool->WriteAddress = 0;
Pool->WriteSize = 0;
Pool->SourceOffset = 0;
return Status;
}
/**
Pei crisis recovery in PEI stage
@param [in] FileHandle
@param [in] PeiServices General purpose services available to every PEIM
@retval EFI_SUCCESS Success
@retval EFI_UNSUPPORTED Some required PPIs are not available
@retval EFI_OUT_OF_RESOURCES Not enough memory to allocate
**/
EFI_STATUS
CrisisRecoveryEntry (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
EFI_PEI_PPI_DESCRIPTOR *oldRecoveryPpiDescriptor;
EFI_PEI_RECOVERY_MODULE_PPI *oldRecoveryPpi;
Status = (*PeiServices)->LocatePpi (
PeiServices,
&gEfiPeiRecoveryModulePpiGuid,
0,
&oldRecoveryPpiDescriptor,
(VOID **)&oldRecoveryPpi
);
if (Status == EFI_SUCCESS) {
Status = (*PeiServices)->ReInstallPpi (
PeiServices,
oldRecoveryPpiDescriptor,
&mRecoveryPpiList
);
} else {
Status = (*PeiServices)->InstallPpi (
PeiServices,
&mRecoveryPpiList
);
}
return Status;
}
/**
Return if the capsule is FMP capsule, based upon CapsuleHeader.
@param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
@retval TRUE It is a FMP capsule.
@retval FALSE It is not a FMP capsule.
**/
BOOLEAN
IsFmpCapsule (
IN EFI_CAPSULE_HEADER *CapsuleHeader
)
{
return CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid);
}
/**
Get BIOS image information from recovery capsule
@param [in] Capsule Recovery capsule buffer
@param [in] CapsuleSize Recovery capsule file size
@param [out] BiosImageOffset The offset of BIOS image from recovery capsule file buffer
@param [out] BiosImageSize The size of the BIOS image
@retval EFI_SUCCESS BIOS image information is successfully retrieved
@return others Failed to get BIOS image information from recovery capsule
**/
EFI_STATUS
GetBiosImageFromCapsule (
IN UINT8 *Capsule,
IN UINTN CapsuleSize,
OUT UINTN *BiosImageOffset,
OUT UINTN *BiosImageSize
)
{
UINTN Index;
ISFLASH_DATA_REGION_HEADER *DataRegion;
if ( (Capsule == NULL) || (CapsuleSize == 0) ) {
return EFI_INVALID_PARAMETER;
}
if (!((Capsule[0] == (UINT8)'M') && (Capsule[1] == (UINT8)'Z'))) {
if (IsFmpCapsule ((EFI_CAPSULE_HEADER *)Capsule)) {
*BiosImageOffset = ((EFI_CAPSULE_HEADER *)Capsule)->CapsuleImageSize - PcdGet32 (PcdFlashAreaSize);
*BiosImageSize = PcdGet32 (PcdFlashAreaSize);
} else {
*BiosImageOffset = 0;
*BiosImageSize = CapsuleSize;
if (FeaturePcdGet (PcdSecureFlashSupported)) {
*BiosImageSize -= SIGNATURE_SIZE;
}
}
return EFI_SUCCESS;
}
//
// Capsule image is PE32 image
// Search for BIOS image
//
for (Index = 0; Index < CapsuleSize - ISFLASH_TAG_SIZE; Index++) {
if (CompareMem(Capsule + Index, ISFLASH_BIOS_IMAGE_TAG_HALF_1, ISFLASH_HALF_TAG_SIZE) == 0){
if (CompareMem(Capsule + Index + ISFLASH_HALF_TAG_SIZE, ISFLASH_BIOS_IMAGE_TAG_HALF_2, ISFLASH_HALF_TAG_SIZE) == 0) {
break;
}
}
}
if (Index == CapsuleSize - ISFLASH_TAG_SIZE) {
return EFI_NOT_FOUND;
}
//
// Copy BIOS image to the start of FirmwareBin pointer
//
DataRegion = (ISFLASH_DATA_REGION_HEADER *)(Capsule + Index);
*BiosImageOffset = Index + sizeof(ISFLASH_DATA_REGION_HEADER);
*BiosImageSize = DataRegion->DataSize;
if (*BiosImageOffset + *BiosImageSize > CapsuleSize) {
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
VOID
ClearFirmwareUpdatingFlag (
VOID
)
{
EFI_STATUS Status;
if (!IsFirmwareFailureRecovery()) {
return;
}
Status = FlashErase( (UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionFtwBackupGuid ,1), GetFlashBlockSize());
if (EFI_ERROR(Status)) {
ASSERT_EFI_ERROR(Status);
return;
}
}
/**
Provide the functionality of the Ea Recovery Module.
@param [in] PeiServices General purpose services available to every PEIM.
@param [in] This
@retval Status EFI_SUCCESS if the interface could be successfully
installed
**/
EFI_STATUS
EFIAPI
PlatformRecoveryModule (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_RECOVERY_MODULE_PPI *This
)
{
EFI_STATUS Status;
UINTN RecoveryCapsuleSize;
EFI_PHYSICAL_ADDRESS Address;
VOID *Buffer;
UINTN BiosImageOffset;
UINTN BiosImageSize;
BOOLEAN FindFv;
EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
UINT32 FvAlignment;
UINTN FvHeaderAddress;
VOID *SetupData;
UINTN VariableSize;
UINT8 *SupervisorPasswordBuf;
UINT8 *UserPasswordBuf;
EFI_PEI_FV_HANDLE FvHandle;
EFI_PEI_FILE_HANDLE FileHandle;
EFI_STATUS FindDxeCoreStatus;
EFI_PEI_HOB_POINTERS Hob;
VOID *File;
UINTN FileSize;
BOOLEAN DxeFileFound;
POST_CODE (PEI_ENTER_RECOVERY_MODE);
DEBUG ((EFI_D_INFO | EFI_D_LOAD, " ############ PEI Crisis Recovery Entry ############\n\n"));
Buffer = NULL;
File = NULL;
FileSize = 0;
BiosImageOffset = 0;
DxeFileFound = FALSE;
if (FeaturePcdGet(PcdUseFastCrisisRecovery)) {
Status = LoadRecoveryCapsule (PeiServices, &Buffer, &RecoveryCapsuleSize);
if (EFI_ERROR(Status) && !LoadBackupSbbIsRequired ()) {
return Status;
}
if (LoadBackupSbbIsRequired ()) {
Status = LoadBackupSbbFile (
(CONST EFI_PEI_SERVICES **)PeiServices,
&File,
&FileSize
);
if (!EFI_ERROR (Status)) {
Buffer = File;
RecoveryCapsuleSize = FileSize;
BiosImageOffset = 0;
DxeFileFound = TRUE;
}
}
if (!DxeFileFound) {
Status = GetBiosImageFromCapsule (
(UINT8 *)Buffer,
RecoveryCapsuleSize,
&BiosImageOffset,
&BiosImageSize
);
if (EFI_ERROR(Status)) {
return Status;
}
}
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Fast Crisis to handoff to Dxe phase preparation.\n"));
if (PcdGetBool (PcdSecureSysPasswordSupported)) {
SupervisorPasswordBuf = NULL;
CommonGetVariableDataAndSize (
EFI_ADMIN_PASSWORD_NAME,
&gInsydeSecureFirmwarePasswordGuid,
&VariableSize,
(VOID **) &SupervisorPasswordBuf
);
if (SupervisorPasswordBuf != NULL) {
BuildGuidDataHob (
&gInsydeSecureFirmwarePasswordHobGuid,
SupervisorPasswordBuf,
VariableSize
);
}
} else {
SupervisorPasswordBuf = NULL;
CommonGetVariableDataAndSize (
L"SystemSupervisorPw",
&gEfiSupervisorPwGuid,
&VariableSize,
(VOID **) &SupervisorPasswordBuf
);
if (SupervisorPasswordBuf != NULL) {
BuildGuidDataHob (
&gEfiSupervisorPwHobGuid,
SupervisorPasswordBuf,
VariableSize
);
}
}
UserPasswordBuf = NULL;
CommonGetVariableDataAndSize (
L"SystemUserPw",
&gEfiUserPwGuid,
&VariableSize,
(VOID **) &UserPasswordBuf
);
if (UserPasswordBuf != NULL) {
BuildGuidDataHob (
&gEfiUserPwHobGuid,
UserPasswordBuf,
VariableSize
);
}
SetupData = NULL;
CommonGetVariableDataAndSize (
L"SetupOrg",
&gSystemConfigurationGuid,
&VariableSize,
(VOID **) &SetupData
);
if (SetupData == NULL) {
SetupData = NULL;
CommonGetVariableDataAndSize (
SETUP_VARIABLE_NAME,
&gSystemConfigurationGuid,
&VariableSize,
(VOID **) &SetupData
);
}
if (SetupData != NULL) {
BuildGuidDataHob (
&gEfiPowerOnPwSCUHobGuid,
SetupData,
VariableSize
);
}
//
// OemSvcBootModeCreateFv(); could utilize to create customer's FvHobs
//
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Checking any exist of FvHobs with DxeCore Ffs for DxeIpl handoff.\n"));
Hob.Raw = GetHobList ();
while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, Hob.Raw)) != NULL) {
FileHandle = NULL;
FvHandle = (VOID *)(UINTN) (Hob.FirmwareVolume->BaseAddress);
FindDxeCoreStatus = PeiServicesFfsFindNextFile (EFI_FV_FILETYPE_DXE_CORE, FvHandle, &FileHandle);
if (!EFI_ERROR (FindDxeCoreStatus)) {
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Finding the DxeCore existed in FV at memory address %x \n", (UINTN) (Hob.FirmwareVolume->BaseAddress)));
return FindDxeCoreStatus;
}
Hob.Raw = GET_NEXT_HOB (Hob);
}
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "No DxeCore Ffs exist at currently FvHobs.\n"));
//
// Creating FV to HOB. It make the FV visible for DxeIplFindDxeCore().
//
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Creating FvHobs for DxeIpl to handoff from image which read from storage\n"));
FvHeaderAddress = (UINTN)Buffer + BiosImageOffset;
do {
FindFv = FALSE;
FvHeader = (EFI_FIRMWARE_VOLUME_HEADER*)FvHeaderAddress;
if (FvHeader->Signature == EFI_FVH_SIGNATURE) {
//
// Find the usable of Firmware Volume
//
if ((CompareGuid (&gEfiFirmwareFileSystem2Guid, &FvHeader->FileSystemGuid)) ||
(CompareGuid (&gEfiFirmwareFileSystem3Guid, &FvHeader->FileSystemGuid))) {
if (FeaturePcdGet (PcdH2OPeiCpCrisisRecoveryPublishFvSupported)) {
H2O_PEI_CP_CRISIS_RECOVERY_PUBLISH_FV_DATA CrisisRecoveryPublishFvData;
CrisisRecoveryPublishFvData.Size = sizeof (H2O_PEI_CP_CRISIS_RECOVERY_PUBLISH_FV_DATA);
CrisisRecoveryPublishFvData.Status = H2O_CP_TASK_NORMAL;
CrisisRecoveryPublishFvData.FvHeader = FvHeader;
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OPeiCpCrisisRecoveryPublishFvGuid));
H2OCpTrigger (&gH2OPeiCpCrisisRecoveryPublishFvGuid, &CrisisRecoveryPublishFvData);
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", CrisisRecoveryPublishFvData.Status));
if (CrisisRecoveryPublishFvData.Status == H2O_CP_TASK_SKIP) {
FvHeaderAddress += (UINTN)FvHeader->FvLength;
continue;
}
}
FindFv = TRUE;
FvAlignment = 1 << ((FvHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16);
Address = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINTN)FvHeader->FvLength), (UINTN)FvAlignment);
if (Address == 0) {
DEBUG ((DEBUG_ERROR, "Allocate memory for firmware volume failed!!\n"));
break;
}
CopyMem ((VOID *) (UINTN) Address, (VOID *) FvHeaderAddress, (UINTN) FvHeader->FvLength);
BuildFvHob (Address, FvHeader->FvLength);
PeiServicesInstallFvInfoPpi (
&FvHeader->FileSystemGuid,
(VOID*) (UINTN) Address,
(UINT32) FvHeader->FvLength,
NULL,
NULL
);
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Find the valid FVs at memory address %x \n", FvHeaderAddress));
FvHeaderAddress += (UINTN)FvHeader->FvLength;
}
}
if (!FindFv) {
FvHeaderAddress += 0x10;
}
} while (((FvHeaderAddress - ((UINTN) Buffer))+ sizeof (EFI_FIRMWARE_VOLUME_HEADER)) < RecoveryCapsuleSize);
} else {
Status = LoadRecoveryCapsule (PeiServices, &Buffer, &RecoveryCapsuleSize);
if (EFI_ERROR(Status)) {
return Status;
}
Status = FlashBios (Buffer, RecoveryCapsuleSize);
}
return Status;
}
/**
This function will delete the variable if the variable size is different between variable region and variable default
region.
Some variable size may be changed in different BIOS version. Using the mismatched variable after updating BIOS
may cause unpredictable behaviors. To prevent from tis situation so delete this type variables.
@retval EFI_SUCCESS Delete all of variable which variable is different between variable region and variable default region.
@retval EFI_OUT_OF_RESOURCES There are not enough resource to store variable region data.
@retval Other The request could not be completed by other reason.
**/
STATIC
EFI_STATUS
CheckVariableConsistency (
VOID
)
{
EFI_STATUS Status;
UINTN VariableStoreHeaderSize;
UINTN VariableRegionSize;
UINT8 *VarRegionBuffer;
VARIABLE_HEADER *VariableHeader;
VARIABLE_HEADER *DefaultVariableHeader;
VARIABLE_HEADER *DefaultVariableHeaderbySkuId0;
UINTN DefaultVariableSize;
BOOLEAN FoundMismatchedVariable;
//
// Write back invalid related MMIO address to prevent from reading out-of-date data
//
WriteBackInvalidateDataCacheRange (
(VOID *)(UINTN)FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid, 1),
(UINTN)FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1)
);
WriteBackInvalidateDataCacheRange (
(VOID *)(UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionVarDefaultGuid, 1),
(UINTN)FdmGetNAtSize (&gH2OFlashMapRegionVarDefaultGuid, 1)
);
//
// Get whole variable region and put in memory buffer.
//
VariableRegionSize = (UINTN)FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1);
VarRegionBuffer = AllocatePages (EFI_SIZE_TO_PAGES (VariableRegionSize));
if (VarRegionBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = FlashRead (
(UINT8 *)VarRegionBuffer,
(UINT8 *)(UINTN)FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid, 1),
VariableRegionSize
);
if (EFI_ERROR(Status)) {
FreePages(VarRegionBuffer, EFI_SIZE_TO_PAGES(VariableRegionSize));
return Status;
}
//
// Get each variable in default variable region. If default variable isn't in current SKU, use it in SKU 0.
//
VariableStoreHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY) + GetVariableStoreHeaderSize ();
DefaultVariableHeaderbySkuId0 = (VARIABLE_HEADER *)(UINTN)FdmGetNAtAddr (&gH2OFlashMapRegionVarDefaultGuid, 1);
if (DefaultVariableHeaderbySkuId0 == NULL) {
return EFI_NOT_FOUND;
}
FoundMismatchedVariable = FALSE;
for (DefaultVariableHeader = (VARIABLE_HEADER *)((UINT8 *)DefaultVariableHeaderbySkuId0 + GetVariableStoreHeaderSize ());
IsValidVariableHeader (DefaultVariableHeader);
DefaultVariableHeader = GetNextVariablePtr (DefaultVariableHeader)) {
if (DefaultVariableHeader->State != VAR_ADDED) {
continue;
}
DefaultVariableSize = 0;
Status = CommonGetDefaultVariable (
GET_VARIABLE_NAME_PTR (DefaultVariableHeader),
&DefaultVariableHeader->VendorGuid,
(H2O_BOARD_ID)(LibPcdGetSku()),
NULL,
(UINTN *)&DefaultVariableSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
DefaultVariableSize = DefaultVariableHeader->DataSize;
}
//
// Delete variable if the variable size is different between variable region and variable default region.
//
for (VariableHeader = (VARIABLE_HEADER *)(VarRegionBuffer + VariableStoreHeaderSize);
IsValidVariableHeader (VariableHeader);
VariableHeader = GetNextVariablePtr (VariableHeader)) {
if (VariableHeader->State != VAR_ADDED && VariableHeader->State != (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
continue;
}
if (StrCmp (GET_VARIABLE_NAME_PTR (VariableHeader), GET_VARIABLE_NAME_PTR (DefaultVariableHeader)) != 0 ||
!CompareGuid(&VariableHeader->VendorGuid, &DefaultVariableHeader->VendorGuid)) {
continue;
}
if (DefaultVariableSize != DataSizeOfVariable (VariableHeader)) {
VariableHeader->State &= VAR_DELETED;
FoundMismatchedVariable = TRUE;
}
}
}
//
// Write updated variable region data to variable region if finding any mismatched variable.
//
Status = EFI_SUCCESS;
if (FoundMismatchedVariable) {
Status = FlashProgram (
(UINT8 *)(UINTN)FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid, 1),
(UINT8 *)VarRegionBuffer,
&VariableRegionSize,
(UINTN)FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid, 1)
);
}
FreePages(VarRegionBuffer, EFI_SIZE_TO_PAGES(VariableRegionSize));
return Status;
}
/**
Flash BIOS image from recovery capsule
@param [in] RecoveryCapsule Pointer to recovery capsule
@param [in] RecoveryCapsuleSize Recovery capsule file size
@retval EFI_SUCCESS Flash BIOS image successfully
@return Others Unable to flash BIOS image because BIOS image is not found in recovery capsule or
failed to allocate memory
**/
EFI_STATUS
FlashBios (
IN UINT8 *RecoveryCapsule,
IN UINTN RecoveryCapsuleSize
)
{
EFI_STATUS Status;
UINTN WriteCount;
UINT16 ProtectNumCount;
UINT32 WriteAddress;
UINT32 WriteSize;
UINT32 SrcAddress;
PEI_SPEAKER_IF_PPI *SpeakerPPI;
BOOLEAN SpeakerOn;
BOOLEAN DescriptorBIOS;
EFI_PHYSICAL_ADDRESS RomBinFile;
UINTN NumOfProtectRegion;
FLASH_ENTRY *OemProtectTable;
UINTN TableCount;
BOOLEAN UseEcIdle;
BOOLEAN UseCrisisProtectTable;
BOOLEAN SkipPeiRegion;
FLASH_ENTRY *ProtectRegion;
UINTN IsMeProtectRegion;
UINTN RecoveryBlocks;
EFI_PEI_SERVICES **PeiServices;
UINT8 *ReadBuffer;
UINT64 RecoveryFvAddr;
UINT64 RecoveryFvSize;
UINTN DefaultProtectTableSize;
FLASH_ENTRY *DefaultProtectTable;
EFI_STATUS OemSvcStatus;
UINTN BiosImageOffset;
UINT8 *BiosImage;
UINTN BiosImageSize;
UINT32 RemainingSize;
TableCount = 0;
NumOfProtectRegion = 0;
IsMeProtectRegion = 0;
RecoveryBlocks = 0;
UseEcIdle = FALSE;
OemProtectTable = NULL;
ProtectRegion = NULL;
SpeakerOn = FALSE;
DescriptorBIOS = FALSE;
UseCrisisProtectTable = TRUE;
SkipPeiRegion = FALSE;
ReadBuffer = NULL;
DefaultProtectTable = NULL;
PeiServices = (EFI_PEI_SERVICES **) GetPeiServicesTablePointer();
WriteSize = GetFlashBlockSize ();
Status = PeiServicesLocatePpi (
&gPeiSpeakerInterfacePpiGuid,
0,
NULL,
(VOID **)&SpeakerPPI
);
if (!EFI_ERROR (Status)) {
SpeakerOn = TRUE;
}
RecoveryFvAddr = FdmGetNAtAddr (
&gH2OFlashMapRegionBootFvGuid,
1
);
if (RecoveryFvAddr == 0) {
return EFI_NOT_FOUND;
}
RecoveryFvSize = FdmGetNAtSize (
&gH2OFlashMapRegionBootFvGuid,
1
);
Status = GetDefaultProtectRegion (&DefaultProtectTable, &DefaultProtectTableSize);
ASSERT(DefaultProtectTable);
Status = EFI_SUCCESS;
//
// Get OEM protect Regions
// (OemServices)
//
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcGetProtectTable \n"));
OemSvcStatus = OemSvcGetProtectTable (
&TableCount,
&UseEcIdle,
&OemProtectTable
);
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcGetProtectTable Status: %r\n", OemSvcStatus));
if (FeaturePcdGet (PcdH2OPeiCpCrisisRecoveryGetProtectTableSupported)) {
H2O_PEI_CP_CRISIS_RECOVERY_GET_PROTECT_TABLE_DATA CrisisRecoveryGetProtectTableData;
CrisisRecoveryGetProtectTableData.Size = sizeof (H2O_PEI_CP_CRISIS_RECOVERY_GET_PROTECT_TABLE_DATA);
CrisisRecoveryGetProtectTableData.Status = H2O_CP_TASK_NORMAL;
CrisisRecoveryGetProtectTableData.ProtectTable = OemProtectTable;
CrisisRecoveryGetProtectTableData.ProtectTableCount = (UINT32) TableCount;
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OPeiCpCrisisRecoveryGetProtectTableGuid));
H2OCpTrigger (&gH2OPeiCpCrisisRecoveryGetProtectTableGuid, &CrisisRecoveryGetProtectTableData);
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", CrisisRecoveryGetProtectTableData.Status));
if (CrisisRecoveryGetProtectTableData.Status == H2O_CP_TASK_UPDATE) {
OemProtectTable = CrisisRecoveryGetProtectTableData.ProtectTable;
TableCount = CrisisRecoveryGetProtectTableData.ProtectTableCount;
}
}
if (UseEcIdle) {
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcEcIdle \n"));
OemSvcStatus = OemSvcEcIdle (TRUE);
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcEcIdle Status: %r\n", OemSvcStatus));
}
PeiCsSvcEnableFdWrites (TRUE);
if (FeaturePcdGet (PcdH2OPeiCpCrisisRecoveryFlashSupported)) {
H2O_PEI_CP_CRISIS_RECOVERY_FLASH_DATA CrisisRecoveryFlashData;
CrisisRecoveryFlashData.Size = sizeof (H2O_PEI_CP_CRISIS_RECOVERY_FLASH_DATA);
CrisisRecoveryFlashData.Status = H2O_CP_TASK_NORMAL;
CrisisRecoveryFlashData.RecoveryCapsule = RecoveryCapsule;
CrisisRecoveryFlashData.RecoveryCapsuleSize = RecoveryCapsuleSize;
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OPeiCpCrisisRecoveryFlashGuid));
H2OCpTrigger (&gH2OPeiCpCrisisRecoveryFlashGuid, &CrisisRecoveryFlashData);
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", CrisisRecoveryFlashData.Status));
if (CrisisRecoveryFlashData.Status == H2O_CP_TASK_SKIP) {
goto Done;
} else if (CrisisRecoveryFlashData.Status == H2O_CP_TASK_UPDATE) {
RecoveryCapsule = CrisisRecoveryFlashData.RecoveryCapsule;
RecoveryCapsuleSize = CrisisRecoveryFlashData.RecoveryCapsuleSize;
}
}
Status = GetBiosImageFromCapsule (
RecoveryCapsule,
RecoveryCapsuleSize,
&BiosImageOffset,
&BiosImageSize
);
if (EFI_ERROR(Status)) {
goto Done;
}
BiosImage = RecoveryCapsule + BiosImageOffset;
POST_CODE (PEI_RECOVERY_LOAD_FILE_DONE);
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "\n ######### Flash BIOS ######### \n"));
//
// Get onboard BIOS base address
//
if (((*((UINT32 *)BiosImage)) == FLVALSIG) || ((*((UINT32 *)((UINT8 *)BiosImage + 0x10))) == FLVALSIG)) {
WriteAddress = 0xFFFFFFFF - BiosImageSize + 1;
DescriptorBIOS = TRUE;
} else {
WriteAddress = (UINT32 ) FdmGetBaseAddr ();
}
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Write ROM base address %x \n", WriteAddress));
SrcAddress = (UINT32) BiosImage;
MicroSecondDelay(50);
//
//OEM protect table is empty or table return whole ROM size, PEI Crisis default not protect any region
//
if ((OemProtectTable == NULL) ||
(OemProtectTable[0].WriteSize == 0xFFFFFFFF)) {
//
// Update whole FD image
//
UseCrisisProtectTable = FALSE;
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Write whole image\n"));
} else {
//
//Analyze the OEM protect table
//
for (ProtectNumCount = 0; ProtectNumCount < (TableCount - 1); ProtectNumCount++) {
if ((OemProtectTable[ProtectNumCount].WriteAddress < FdmGetBaseAddr ())) {
IsMeProtectRegion ++;
}
if (OemProtectTable[ProtectNumCount].WriteAddress == RecoveryFvAddr) {
SkipPeiRegion = TRUE;
}
}
if (DescriptorBIOS == FALSE) {
//
//All of the OEM protect region are ME region for Non-ME bin file then it will use kernel default protect region.
//
if (IsMeProtectRegion == ((TableCount - 1))) {
if (((DefaultProtectTableSize / sizeof (FLASH_ENTRY)) == 2) && (DefaultProtectTable->WriteAddress == RecoveryFvAddr)) {
UseCrisisProtectTable = FALSE;
SkipPeiRegion = TRUE;
} else {
NumOfProtectRegion = (DefaultProtectTableSize / sizeof (FLASH_ENTRY)) - 1;
ProtectRegion = DefaultProtectTable;
}
} else {
NumOfProtectRegion = TableCount - 1;
ProtectRegion = OemProtectTable;
}
} else {
NumOfProtectRegion = TableCount - 1;
ProtectRegion = OemProtectTable;
}
}
if (UseCrisisProtectTable) {
//
//Read all of the ROM part bin files
//
Status = PeiServicesAllocatePages (
EfiBootServicesCode,
EFI_SIZE_TO_PAGES (BiosImageSize),
&RomBinFile
);
if (EFI_ERROR (Status)) {
goto Done;
}
Status = FlashRead (
(UINT8 *)((UINT32)RomBinFile),
(UINT8 *)WriteAddress,
BiosImageSize
);
if (EFI_ERROR (Status)) {
goto Done;
}
for (ProtectNumCount = 0; ProtectNumCount < NumOfProtectRegion; ProtectNumCount++) {
//
//Non-ME bin file need not to reference ME protect region
//
if ((ProtectRegion[ProtectNumCount].WriteAddress < FdmGetBaseAddr ()) && (DescriptorBIOS == FALSE)) {
continue;
} else {
//
//Non-ME bin file to reference the BIOS protect region
//
if (DescriptorBIOS == FALSE) {
CopyMem (
(((UINT8 *)SrcAddress) + ProtectRegion[ProtectNumCount].SourceOffset),
(((UINT8 *)((UINT32)RomBinFile)) + ProtectRegion[ProtectNumCount].SourceOffset),
ProtectRegion[ProtectNumCount].WriteSize
);
} else {
//
//Include ME bin file to reference ME protect region
//
if (ProtectRegion[ProtectNumCount].WriteAddress < FdmGetBaseAddr ()) {
CopyMem (
(((UINT8 *)SrcAddress) + ProtectRegion[ProtectNumCount].SourceOffset),
(((UINT8 *)((UINT32)RomBinFile)) + ProtectRegion[ProtectNumCount].SourceOffset),
ProtectRegion[ProtectNumCount].WriteSize
);
} else {
//
//Include ME bin file to reference BIOS protect region
//
CopyMem (
(((UINT8 *)SrcAddress) + (ProtectRegion[ProtectNumCount].SourceOffset + (BiosImageSize - FdmGetFlashAreaSize()))),
(((UINT8 *)((UINT32)RomBinFile)) + (ProtectRegion[ProtectNumCount].SourceOffset + (BiosImageSize - FdmGetFlashAreaSize ()))),
ProtectRegion[ProtectNumCount].WriteSize
);
}
}
}
}
}
if (SkipPeiRegion) {
RemainingSize = (UINT32)(BiosImageSize - (UINTN) RecoveryFvSize);
RecoveryBlocks = ((BiosImageSize - (UINTN) RecoveryFvSize) / WriteSize);
RecoveryBlocks = ((BiosImageSize - (UINTN) RecoveryFvSize) % WriteSize) == 0 ? RecoveryBlocks: RecoveryBlocks + 1;
} else {
RemainingSize = (UINT32)BiosImageSize;
RecoveryBlocks = ((BiosImageSize) / WriteSize);
RecoveryBlocks = ((BiosImageSize) % WriteSize) == 0 ? RecoveryBlocks : RecoveryBlocks + 1;
}
ReadBuffer = AllocatePages(EFI_SIZE_TO_PAGES(WriteSize));
ASSERT(ReadBuffer != NULL);
if (ReadBuffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
for (WriteCount = 0; WriteCount < RecoveryBlocks; WriteCount ++) {
UINTN Retry;
POST_CODE (START_FLASH);
//
//Make a Beep sound
//
if (SpeakerOn && ((WriteCount % 0xF) == 0)) {
SpeakerPPI->GenerateBeep (1, 500000, 0);
}
for (Retry = 0; Retry < FLASH_FAILURE_RETRY_COUNT; Retry++) {
WriteSize = GetFlashBlockSize();
if (WriteSize > RemainingSize){
WriteSize = RemainingSize;
}
Status = FlashErase (WriteAddress, WriteSize);
if (EFI_ERROR (Status)) {
continue;
}
POST_CODE (ERASE_DONE);
MicroSecondDelay (50);
Status = FlashProgram (
(UINT8 *)WriteAddress,
(UINT8 *)SrcAddress,
(UINTN *)&WriteSize,
(WriteAddress & ~(0xFFFF))
);
if (EFI_ERROR (Status)) {
continue;
}
Status = FlashRead (
ReadBuffer,
(UINT8 *)WriteAddress,
WriteSize
);
if (EFI_ERROR (Status)) {
continue;
}
if (CompareMem (ReadBuffer, (UINT8 *)SrcAddress, WriteSize) == 0) {
break;
}
}
POST_CODE (PROGRAM_DONE);
WriteAddress += WriteSize;
SrcAddress += WriteSize;
RemainingSize -= WriteSize;
MicroSecondDelay (50);
}
MicroSecondDelay (50);
Status = EFI_SUCCESS;
DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Flash BIOS Success and Restart System\n"));
//
// The status of "EC idle" and "Flash device be writable" should be cleared, when error occur.
//
Done:
CheckVariableConsistency ();
ClearFirmwareUpdatingFlag();
if (ReadBuffer != NULL) {
FreePages(ReadBuffer, EFI_SIZE_TO_PAGES(WriteSize));
}
if (UseEcIdle) {
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcEcIdle \n"));
OemSvcStatus = OemSvcEcIdle (FALSE);
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcEcIdle Status: %r\n", OemSvcStatus));
}
PeiCsSvcEnableFdWrites (FALSE);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcPeiCrisisRecoveryReset \n"));
Status = OemSvcPeiCrisisRecoveryReset ();
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcPeiCrisisRecoveryReset Status: %r\n", Status));
if (Status == EFI_SUCCESS) {
return EFI_SUCCESS;
}
(*PeiServices)->ResetSystem ((CONST EFI_PEI_SERVICES **)PeiServices);
//
// Wait For shutdown
//
CpuDeadLoop ();
return EFI_SUCCESS;
}