alder_lake_bios/Oem/L05/FeatureCommon/InsydeL05ModulePkg/BiosSelfHealingDxe/BiosSelfHealingDxe.c

1421 lines
42 KiB
C

/** @file
BIOS Self-Healing 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 "BiosSelfHealingDxe.h"
//#[-start-220627-dennis0018-add]//
#ifdef LCFC_SUPPORT
#include <Protocol/LenovoVariable.h>
#endif
//#[-end-220627-dennis0018-add]//
EFI_RESET_SYSTEM mOriginalResetSystemPtr = NULL;
L05_BIOS_SELF_HEALING_UI_TYPE mUiType = 0;
BOOLEAN mTimeExpired = FALSE;
BOOLEAN mSpiBackupIsUpdated = FALSE;
/**
Compares two memory buffers of a given length.
@param DestinationBuffer The first memory buffer.
@param SourceBuffer The second memory buffer.
@param Length Length of DestinationBuffer and SourceBuffer memory
regions to compare. Must be non-zero.
@return 0 All Length bytes of the two buffers are identical.
@retval Non-zero The first mismatched byte in SourceBuffer subtracted from the first
mismatched byte in DestinationBuffer.
**/
INTN
EFIAPI
InternalCompareMem64 (
IN VOID *DestinationBuffer,
IN VOID *SourceBuffer,
IN UINTN Length
)
{
UINTN CompareLength;
CompareLength = Length - sizeof (INT64);
while ((CompareLength != 0) &&
(*(INT64*)DestinationBuffer == *(INT64*)SourceBuffer)) {
DestinationBuffer = (VOID*)((UINTN)DestinationBuffer + sizeof (INT64));
SourceBuffer = (VOID*)((UINTN)SourceBuffer + sizeof (INT64));
CompareLength -= sizeof (INT64);
}
return (INTN)*(UINT64*)DestinationBuffer - (INTN)*(UINT64*)SourceBuffer;
}
/**
ResetSystem dummy function.
@param ResetType UEFI defined reset type.
@param ResetStatus The status code for the reset.
@param DataSize The size of ResetData in bytes.
@param ResetData Optional element used to introduce a platform specific reset.
The exact type of the reset is defined by the EFI_GUID that follows
the Null-terminated Unicode string.
**/
VOID
EFIAPI
DummyResetSystem (
IN EFI_RESET_TYPE ResetType,
IN EFI_STATUS ResetStatus,
IN UINTN DataSize,
IN VOID *ResetData OPTIONAL
)
{
return;
}
/**
ResetSystem hook function.
@param ToHook Point out to hook or not.
**/
VOID
HookResetSystem (
IN BOOLEAN ToHook
)
{
if (ToHook) {
mOriginalResetSystemPtr = gRT->ResetSystem;
gRT->ResetSystem = DummyResetSystem;
} else {
gRT->ResetSystem = mOriginalResetSystemPtr;
}
return;
}
/**
Update blocks on flash.
@param StartAddress Start address to be written.
@param Buffer Data buffer want to write.
@param BufferSize The requested write size.
@param UseProtectTable Need to confirm the protected area or not.
@param TotalFlashBlocks Total number of flashing blocks. If non-zero, it means that multi region are currently flashing.
@param CurrentFlashBlocks Number of blocks currently flashed. If non-zero, it means that multi region are currently flashing.
@param IsMultiRegion Multi region flashing.
@retval EFI_SUCCESS Update BIOS successfully.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
FlashUpdate (
IN UINTN StartAddress,
IN UINTN Buffer,
IN UINTN BufferSize,
IN BOOLEAN UseProtectTable,
IN UINTN TotalFlashBlocks, OPTIONAL
IN UINTN CurrentFlashBlocks, OPTIONAL
IN BOOLEAN IsMultiRegion OPTIONAL
)
{
EFI_STATUS Status;
EFI_L05_FLASH_PROTECT_REGION *L05ProtectTable;
UINTN TableCount;
UINTN BlockSize;
UINTN RecoveryBlocks;
UINTN WriteAddress;
UINTN SrcAddress;
UINTN WriteCount;
UINTN Size;
UINTN Retry;
UINTN FlashPrecentage;
UINTN Index;
UINT8 *CompareBuffer;
Status = EFI_SUCCESS;
L05ProtectTable = NULL;
TableCount = 0;
BlockSize = FLASH_SECTOR_SIZE;
RecoveryBlocks = (BufferSize / BlockSize);
TotalFlashBlocks = IsMultiRegion ? TotalFlashBlocks : RecoveryBlocks;
WriteAddress = StartAddress;
SrcAddress = Buffer;
WriteCount = 0;
Size = 0;
Retry = 0;
FlashPrecentage = 0;
CompareBuffer = NULL;
Status = GetFlashProtectTable (&L05ProtectTable, &TableCount, SelfHealing);
if (EFI_ERROR (Status) || (L05ProtectTable == NULL) || (TableCount == 0)) {
UseProtectTable = FALSE;
}
CompareBuffer = AllocatePool (BlockSize);
if (CompareBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
HookResetSystem (TRUE);
for (WriteCount = 0; WriteCount < RecoveryBlocks; WriteCount++) {
//
// Confirm whether the region needs protection
//
if (UseProtectTable) {
for (Index = 0; Index < TableCount; Index++) {
if ((WriteAddress >= L05ProtectTable[Index].LinearAddress) &&
(WriteAddress < (L05ProtectTable[Index].LinearAddress + L05ProtectTable[Index].Size))) {
if (WriteAddress == L05ProtectTable[Index].LinearAddress) {
DEBUG ((EFI_D_INFO, "BiosSelfHealingDxe: Skip protected area update => address: %x, size: %x\n", L05ProtectTable[Index].LinearAddress, L05ProtectTable[Index].Size));
}
goto Skip;
}
}
}
//
// Update region
//
for (Retry = 0; Retry < FLASH_FAILURE_RETRY_COUNT; Retry++) {
Status = FlashErase (WriteAddress, BlockSize);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Erase flash address 0x%x error %r\n", WriteAddress, Status));
continue;
}
Size = BlockSize;
Status = FlashProgram (
(UINT8 *) WriteAddress,
(UINT8 *) SrcAddress,
&Size,
WriteAddress
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Program flash address 0x%x error %r\n", WriteAddress));
continue;
}
ZeroMem (CompareBuffer, BlockSize);
Status = FlashRead (
CompareBuffer,
(UINT8 *) WriteAddress,
BlockSize
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Read flash address 0x%x error %r\n", WriteAddress, Status));
continue;
}
if (CompareMem (CompareBuffer, (VOID *) SrcAddress, BlockSize) == 0) {
break;
}
}
Skip:
FlashPrecentage = ((WriteCount + CurrentFlashBlocks + 1) * PROCESS_ACCURACY_VALUE) / TotalFlashBlocks;
BiosSelfHealingProgressBar (FlashPrecentage);
WriteAddress += BlockSize;
SrcAddress += BlockSize;
}
HookResetSystem (FALSE);
SafeFreePool ((VOID **) &CompareBuffer);
SafeFreePool ((VOID **) &L05ProtectTable);
return Status;
}
/**
Reset system event.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function!|s context, which is
implementation-dependent. Context corresponds to
NotifyContext in CreateEventEx().
**/
VOID
EFIAPI
ResetSystemTimerEvent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
mTimeExpired = TRUE;
if (mUiType == BshRecoveryDoneReset) {
gRT->ResetSystem (EfiResetPlatformSpecific, EFI_SUCCESS, 0, NULL);
CpuDeadLoop ();
}
}
/**
Trigger disable Top Swap.
@param None
@retval EFI_SUCCESS This function execute successfully.
@retval Orther An unexpected error occurred.
**/
EFI_STATUS
TriggerDisableTopSwap (
VOID
)
{
EFI_STATUS Status;
EFI_SMM_CONTROL2_PROTOCOL *SmmControl;
UINT8 SmiDataValue;
Status = EFI_SUCCESS;
SmmControl = NULL;
SmiDataValue = 0;
Status = gBS->LocateProtocol (
&gEfiSmmControl2ProtocolGuid,
NULL,
(VOID **) &SmmControl
);
if (EFI_ERROR (Status)) {
return Status;
}
SmiDataValue = PcdGet8 (PcdL05TopSwapDisableSwSmi);
if (SmiDataValue == 0xFF) {
return EFI_INVALID_PARAMETER;
}
Status = SmmControl->Trigger (
SmmControl,
&SmiDataValue,
NULL,
0,
0
);
return Status;
}
/**
Set BIOS Self-Healing Feature to enable or disable.
@param EnableFlag Enable or Disable BIOS Self-Healing.
@retval EFI_SUCCESS Update BIOS successfully.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
EFIAPI
BiosSelfHealingFunctionSwitch (
IN BOOLEAN EnableFlag
)
{
EFI_STATUS Status;
UINT8 BiosSelfHealingFlag;
UINTN NumberFileSystemHandles;
EFI_HANDLE *FileSystemHandles;
UINTN Index;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Esp;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
EFI_FILE_HANDLE RootFs;
EFI_FILE_HANDLE FileHandle;
CHAR16 *RecoveryFileFullPath;
UINTN RecoveryFileFullPathSize;
Status = EFI_SUCCESS;
BiosSelfHealingFlag = 0;
NumberFileSystemHandles = 0;
FileSystemHandles = NULL;
Index = 0;
Esp = NULL;
Volume = NULL;
RootFs = NULL;
FileHandle = NULL;
RecoveryFileFullPath = NULL;
RecoveryFileFullPathSize = 0;
//
// [Lenovo BIOS Self-Healing Design Guidance Specification v1.9]
// 2.6 BIOS Setup Option
// When "BIOS Self Healing" switch is enabled or disabled,
// BIOS should notify EC to enable or disable WDT permanently.
//
Status = OemSvcNotifyEcToSetWdtFunction (EnableFlag);
#if (FixedPcdGet32 (PcdL05ChipsetName) == L05_CHIPSET_NAME_ALDERLAKE)
return EFI_SUCCESS;
#else
//
// If switch the function to enabled, just return here
//
if (EnableFlag) {
return EFI_SUCCESS;
}
//
// If switch the function to disabled, clear BackupBlockSynced Flag
//
Status = GetBiosSelfHealingFlag (&BiosSelfHealingFlag);
if (!EFI_ERROR (Status)) {
BiosSelfHealingFlag &= ~L05_BIOS_SELF_HEALING_FLAG_BACKUP_BLOCK_SYNCED;
Status = SetBiosSelfHealingFlag (BiosSelfHealingFlag);
}
//
// Find storage backup file & delete it
//
RecoveryFileFullPathSize = StrSize ((CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFolder)) +
StrSize ((CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFile)) +
sizeof (CHAR16);
RecoveryFileFullPath = AllocateZeroPool (RecoveryFileFullPathSize);
if (RecoveryFileFullPath == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto FunctionSwitchError;
}
StrCpyS (RecoveryFileFullPath, RecoveryFileFullPathSize / sizeof (CHAR16), (CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFolder));
StrCatS (RecoveryFileFullPath, RecoveryFileFullPathSize / sizeof (CHAR16), L"\\\\");
StrCatS (RecoveryFileFullPath, RecoveryFileFullPathSize / sizeof (CHAR16), (CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFile));
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumberFileSystemHandles,
&FileSystemHandles
);
if (EFI_ERROR (Status)) {
goto FunctionSwitchError;
}
for (Index = 0; Index < NumberFileSystemHandles; Index++) {
Status = gBS->HandleProtocol (
FileSystemHandles[Index],
&gEfiPartTypeSystemPartGuid,
(VOID **) &Esp
);
if (!EFI_ERROR (Status)) {
Status = gBS->HandleProtocol (
FileSystemHandles[Index],
&gEfiSimpleFileSystemProtocolGuid,
(VOID *) &Volume
);
if (EFI_ERROR (Status)) {
continue;
}
Status = Volume->OpenVolume (
Volume,
&RootFs
);
if (EFI_ERROR (Status)) {
continue;
}
Status = RootFs->Open (
RootFs,
&FileHandle,
RecoveryFileFullPath,
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
0
);
if (!EFI_ERROR (Status)) {
//
// Delete file
//
FileHandle->Delete (FileHandle);
RootFs->Close (RootFs);
break;
}
RootFs->Close (RootFs);
}
}
FunctionSwitchError:
SafeFreePool ((VOID **) &RecoveryFileFullPath);
return Status;
#endif
}
/**
Display prompt message.
@param UiType Corresponding to the UI type.
@param DisableVendorUi A flag indicates whether to mask vendor user interfaces.
@param DisplayLogo A flag indicates whether to display logo.
@retval EFI_SUCCESS The operation completed successfully.
**/
EFI_STATUS
EFIAPI
BiosSelfHealingDisplayMessage (
IN L05_BIOS_SELF_HEALING_UI_TYPE UiType,
IN BOOLEAN DisableVendorUi,
IN BOOLEAN DisplayLogo
)
{
EFI_STATUS Status;
EFI_EVENT TimerEvent;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
UINTN EventIndex;
EFI_KEY_DATA KeyData;
Status = EFI_SUCCESS;
TimerEvent = NULL;
SimpleTextInEx = NULL;
EventIndex = 0;
if ((UiType == BshRecoveryDone) || (UiType == BshRecoveryDoneReset)) {
mUiType = UiType;
SetResolutionToTarget (FALSE);
PrintAt (0, 0, L"%s", SELF_HEALING_RECOVERY_DONE_STRING);
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
ResetSystemTimerEvent,
NULL,
&TimerEvent
);
if (!EFI_ERROR (Status)) {
gBS->SetTimer (
TimerEvent,
TimerPeriodic,
EFI_TIMER_PERIOD_SECONDS (5)
);
}
Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **) &SimpleTextInEx);
if (!EFI_ERROR (Status)) {
do {
ZeroMem (&KeyData, sizeof (EFI_KEY_DATA));
gBS->WaitForEvent (1, &(SimpleTextInEx->WaitForKeyEx), &EventIndex);
SimpleTextInEx->ReadKeyStrokeEx (SimpleTextInEx, &KeyData);
} while ((KeyData.Key.ScanCode != SCAN_ESC) && !mTimeExpired);
}
if (UiType == BshRecoveryDoneReset) {
gRT->ResetSystem (EfiResetPlatformSpecific, EFI_SUCCESS, 0, NULL);
CpuDeadLoop ();
}
return EFI_SUCCESS;
}
if (DisplayLogo) {
DisplayBootLogo ();
}
DisplayMessage (UiType, DisableVendorUi);
return EFI_SUCCESS;
}
/**
Display update progress bar.
@param Completion A value between 1 and 100 indicating the current completion progress of the update.
@retval EFI_SUCCESS The operation completed successfully.
**/
EFI_STATUS
EFIAPI
BiosSelfHealingProgressBar (
IN UINTN Completion
)
{
DrawProgressBar (Completion);
return EFI_SUCCESS;
}
#if (FixedPcdGet32 (PcdL05ChipsetName) != L05_CHIPSET_NAME_ALDERLAKE)
/**
BIOS recovery callback.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function's context.
**/
VOID
EFIAPI
BiosRecoveryCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
VOID *EndOfBdsBootSelection;
VOID *HobList;
EFI_PEI_HOB_POINTERS RecoveryHob;
UINTN Buffer;
UINTN BufferSize;
UINTN FlashAddress;
Status = EFI_SUCCESS;
EndOfBdsBootSelection = NULL;
HobList = NULL;
Buffer = 0;
BufferSize = 0;
FlashAddress = 0;
Status = gBS->LocateProtocol (
&gEndOfBdsBootSelectionProtocolGuid,
NULL,
(VOID **) &EndOfBdsBootSelection
);
if (EFI_ERROR (Status)) {
return;
}
gBS->CloseEvent (Event);
//
// Get recovery file's hob
//
HobList = GetHobList ();
RecoveryHob.Raw = HobList;
RecoveryHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, RecoveryHob.Raw);
while (RecoveryHob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION &&
!CompareGuid (&RecoveryHob.MemoryAllocationModule->MemoryAllocationHeader.Name,
&gEfiRecoveryFileAddressGuid)) {
RecoveryHob.Raw = GET_NEXT_HOB (RecoveryHob);
RecoveryHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, RecoveryHob.Raw);
}
Buffer = (UINTN) RecoveryHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryBaseAddress;
BufferSize = (UINTN) RecoveryHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryLength;
//
// Recovery BIOS
//
BiosSelfHealingDisplayMessage (BshRecovery, FALSE, TRUE);
FlashAddress = (UINTN) PcdGet32 (PcdFlashAreaBaseAddress);
Status = FlashUpdate (
FlashAddress,
Buffer,
BufferSize,
TRUE,
0,
0,
FALSE
);
DEBUG ((EFI_D_INFO, "BiosSelfHealingDxe: BIOS recovery status: %r\n", Status));
//
// [Lenovo BIOS Self-Healing Design Guidance Specification v1.9]
// 2.2 Detect
// On Top-Swap enabled boot,
// BIOS code will notify EC to disable Top-Swap pin after recovery successfully.
//
OemSvcNotifyEcToDisableTopSwap ();
#ifdef L05_BIOS_SELF_HEALING_TEST_ON_CRB
TriggerDisableTopSwap ();
#endif
BiosSelfHealingDisplayMessage (BshRecoveryDoneReset, FALSE, FALSE);
return;
}
/**
Update EEPROM/CMOS Flag callback.
@param Event Event whose notification function is being invoked.
@param Handle Checkpoint handle.
**/
VOID
EFIAPI
UpdateFlagCallback (
IN EFI_EVENT Event,
IN H2O_CP_HANDLE Handle
)
{
EFI_STATUS Status;
UINT8 BiosSelfHealingFlag;
Status = EFI_SUCCESS;
BiosSelfHealingFlag = 0;
H2OCpUnregisterHandler (Handle);
//
// Confirm whether the previous boot state is PEI Crisis Recovery
//
//[-start-220125-BAIN000092-modify]//
#ifdef LCFC_SUPPORT
if (ReadCmos8 (EfiL05BiosSelfHealingModeSwitch) == V_EFI_L05_BIOS_SELF_HEALING_MODE_CRISIS_RECOVERY) {
#else
if (ReadCmos8 (EfiL05BiosSelfHealingModeSwitch) == V_EFI_L05_BIOS_SELF_HEALING_MODE_CRISIS_RECOVERY_COMPLETED) {
#endif
//[-end-220125-BAIN000092-modify]//
Status = GetBiosSelfHealingFlag (&BiosSelfHealingFlag);
if (!EFI_ERROR (Status)) {
//
// Clear BackupBlockSynced Flag
//
BiosSelfHealingFlag &= ~L05_BIOS_SELF_HEALING_FLAG_BACKUP_BLOCK_SYNCED;
Status = SetBiosSelfHealingFlag (BiosSelfHealingFlag);
}
}
//
// Reset CMOS Flag
//
WriteCmos8 (EfiL05BiosSelfHealingModeSwitch, V_EFI_L05_BIOS_SELF_HEALING_MODE_NORMAL);
return;
}
/**
Update SPI backup callback.
@param Event Event whose notification function is being invoked.
@param Handle Checkpoint handle.
**/
VOID
EFIAPI
UpdateSpiBackupCallback (
IN EFI_EVENT Event,
IN H2O_CP_HANDLE Handle
)
{
EFI_STATUS Status;
UINT8 BiosSelfHealingFlag;
UINTN Index;
UINTN FvCount;
UINT32 *PrimaryFvBase;
UINT32 *BackupFvBase;
UINT32 *BackupFvSize;
VOID *Source;
VOID *Destination;
UINTN TotalFlashBlocks;
UINTN CurrentFlashBlocks;
BOOLEAN IsMultiRegion;
BOOLEAN *UpdateFlag;
Status = EFI_SUCCESS;
BiosSelfHealingFlag = 0;
FvCount = 0;
PrimaryFvBase = NULL;
BackupFvBase = NULL;
BackupFvSize = NULL;
Source = NULL;
Destination = NULL;
TotalFlashBlocks = 0;
CurrentFlashBlocks = 0;
IsMultiRegion = FALSE;
UpdateFlag = NULL;
H2OCpUnregisterHandler (Handle);
//
// [Lenovo BIOS Self-Healing Design Guidance Specification v1.9]
// 2.1 Overview
// The self healing (EC boot flow and BIOS top swap) flow :
// Only confirm the SPI Backup boot block in the case of the first boot after BIOS flash/recovery or
// the function is from disable to enable.
//
Status = GetBiosSelfHealingFlag (&BiosSelfHealingFlag);
if (!EFI_ERROR (Status) &&
((BiosSelfHealingFlag & L05_BIOS_SELF_HEALING_FLAG_BACKUP_BLOCK_SYNCED) == L05_BIOS_SELF_HEALING_FLAG_BACKUP_BLOCK_SYNCED)) {
//
// Backup boot block has been confirmed, so just return here
//
DEBUG ((EFI_D_INFO, "BiosSelfHealingDxe: Backup boot block has been confirmed. (BackupBlockSyncFlag is setted)\n"));
return;
}
//
// Check backup region and calculate the total number of flash blocks
//
DEBUG ((EFI_D_INFO, "BiosSelfHealingDxe: Update Backup IBB - Start\n"));
BiosSelfHealingDisplayMessage (BshBackup, FALSE, TRUE);
FvCount = RetrieveIbbFvInfo (&PrimaryFvBase, &BackupFvBase, &BackupFvSize);
UpdateFlag = AllocateZeroPool (FvCount);
if (FvCount == 0 || UpdateFlag == NULL) {
goto ExitSpiBackup;
}
for (Index = 0; Index < FvCount; Index++) {
Source = AllocatePool (BackupFvSize[Index]);
Destination = AllocatePool (BackupFvSize[Index]);
if (Source == NULL || Destination == NULL) {
goto ExitSpiBackup;
}
Status = FlashRead (
Source,
(UINT8 *)(UINTN) PrimaryFvBase[Index],
BackupFvSize[Index]
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Read flash address 0x%x error %r\n", PrimaryFvBase[Index], Status));
goto ExitSpiBackup;
}
Status = FlashRead (
Destination,
(UINT8 *)(UINTN) BackupFvBase[Index],
BackupFvSize[Index]
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Read flash address 0x%x error %r\n", BackupFvBase[Index], Status));
goto ExitSpiBackup;
}
PatchDataToBuffer (BackupFvBase[Index], Source);
if (InternalCompareMem64 (Destination, Source, BackupFvSize[Index]) != 0) {
if (!IsMultiRegion && TotalFlashBlocks > 0) {
IsMultiRegion = TRUE;
}
TotalFlashBlocks += (BackupFvSize[Index] / FLASH_SECTOR_SIZE);
UpdateFlag[Index] = TRUE;
}
SafeFreePool ((VOID **) &Source);
SafeFreePool ((VOID **) &Destination);
}
//
// Update Backup IBB
//
for (Index = 0; Index < FvCount; Index++) {
DEBUG ((EFI_D_INFO, "Update %x, size %x, Status = ", BackupFvBase[Index], BackupFvSize[Index]));
if (!UpdateFlag[Index]) {
DEBUG ((EFI_D_INFO, "%x is valid backup fv, no need to update\n", BackupFvBase[Index]));
continue;
}
Status = EFI_SUCCESS;
Source = AllocatePool (BackupFvSize[Index]);
Destination = AllocatePool (BackupFvSize[Index]);
if (Source == NULL || Destination == NULL) {
goto ExitSpiBackup;
}
Status = FlashRead (
Source,
(UINT8 *)(UINTN) PrimaryFvBase[Index],
BackupFvSize[Index]
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Read flash address 0x%x error %r\n", PrimaryFvBase[Index], Status));
goto ExitSpiBackup;
}
Status = FlashRead (
Destination,
(UINT8 *)(UINTN) BackupFvBase[Index],
BackupFvSize[Index]
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Read flash address 0x%x error %r\n", BackupFvBase[Index], Status));
goto ExitSpiBackup;
}
PatchDataToBuffer (BackupFvBase[Index], Source);
Status = FlashUpdate (
BackupFvBase[Index],
(UINTN) Source,
BackupFvSize[Index],
FALSE,
(IsMultiRegion ? TotalFlashBlocks : 0),
(IsMultiRegion ? CurrentFlashBlocks : 0),
IsMultiRegion
);
DEBUG ((EFI_D_INFO, "%r\n", Status));
CurrentFlashBlocks += (BackupFvSize[Index] / FLASH_SECTOR_SIZE);
SafeFreePool ((VOID **) &Source);
SafeFreePool ((VOID **) &Destination);
if (EFI_ERROR (Status)) {
goto ExitSpiBackup;
}
}
//
// Set BackupBlockSynced Flag
//
BiosSelfHealingFlag |= L05_BIOS_SELF_HEALING_FLAG_BACKUP_BLOCK_SYNCED;
Status = SetBiosSelfHealingFlag (BiosSelfHealingFlag);
if (TotalFlashBlocks > 0) {
mSpiBackupIsUpdated = TRUE;
}
BiosSelfHealingProgressBar (100);
ExitSpiBackup:
SafeFreePool ((VOID **) &UpdateFlag);
SafeFreePool ((VOID **) &Source);
SafeFreePool ((VOID **) &Destination);
DEBUG ((EFI_D_INFO, "BiosSelfHealingDxe: Update Backup IBB - End\n"));
return;
}
/**
Update Storage backup callback.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function's context.
**/
VOID
EFIAPI
UpdateStorageBackupCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
VOID *EndOfBdsBootSelection;
UINTN NumberFileSystemHandles;
EFI_HANDLE *FileSystemHandles;
UINTN Index;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Esp;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
EFI_FILE_HANDLE RootFs;
EFI_FILE_HANDLE FileHandle;
CHAR16 *RecoveryFolder;
CHAR16 *RecoveryFolderTemp;
CHAR16 *Walker;
CHAR16 *RecoveryFileFullPath;
UINTN FlashAreaBaseAddress;
UINTN FlashAreaSize;
UINTN BufferSize;
EFI_FILE_INFO *FileInfo;
UINT8 *Buffer;
UINT64 BvdtAddr;
CHAR8 *ImageBvdtPtr;
CHAR8 *OnboardBvdtStr;
UINTN RecoveryFileFullPathSize;
Status = EFI_SUCCESS;
EndOfBdsBootSelection = NULL;
NumberFileSystemHandles = 0;
FileSystemHandles = NULL;
Index = 0;
Esp = NULL;
Volume = NULL;
RootFs = NULL;
FileHandle = NULL;
RecoveryFolder = NULL;
RecoveryFolderTemp = NULL;
Walker = NULL;
RecoveryFileFullPath = NULL;
FlashAreaBaseAddress = (UINTN) PcdGet32 (PcdFlashAreaBaseAddress);
FlashAreaSize = (UINTN) PcdGet32 (PcdFlashAreaSize);
BufferSize = 0;
FileInfo = 0;
Buffer = NULL;
BvdtAddr = 0;
ImageBvdtPtr = NULL;
OnboardBvdtStr = NULL;
RecoveryFileFullPathSize = 0;
Status = gBS->LocateProtocol (
&gEndOfBdsBootSelectionProtocolGuid,
NULL,
(VOID **) &EndOfBdsBootSelection
);
if (EFI_ERROR (Status)) {
return;
}
gBS->CloseEvent (Event);
BufferSize = StrSize ((CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFolder)) + sizeof (CHAR16) * 2;
RecoveryFolder = AllocateZeroPool (BufferSize);
if (RecoveryFolder == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ExitStorageBackup;
}
RecoveryFolderTemp = AllocateZeroPool (BufferSize);
if (RecoveryFolderTemp == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ExitStorageBackup;
}
RecoveryFileFullPathSize = BufferSize + StrSize ((CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFile));
RecoveryFileFullPath = AllocateZeroPool (RecoveryFileFullPathSize);
if (RecoveryFileFullPath == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ExitStorageBackup;
}
StrCpyS (RecoveryFolder, BufferSize / sizeof (CHAR16), (CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFolder));
StrCatS (RecoveryFolder, BufferSize / sizeof (CHAR16), L"\\\\");
StrCpyS (RecoveryFileFullPath, RecoveryFileFullPathSize / sizeof (CHAR16), RecoveryFolder);
StrCatS (RecoveryFileFullPath, RecoveryFileFullPathSize / sizeof (CHAR16), (CHAR16 *) PcdGetPtr (PcdL05SelfRecoveryFile));
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumberFileSystemHandles,
&FileSystemHandles
);
if (EFI_ERROR (Status)) {
goto ExitStorageBackup;
}
//
// Scan ESP partition
//
for (Index = 0; Index < NumberFileSystemHandles; Index++) {
Status = gBS->HandleProtocol (
FileSystemHandles[Index],
&gEfiPartTypeSystemPartGuid,
(VOID **) &Esp
);
if (EFI_ERROR (Status)) {
continue;
}
Status = gBS->HandleProtocol (
FileSystemHandles[Index],
&gEfiSimpleFileSystemProtocolGuid,
(VOID *) &Volume
);
if (EFI_ERROR (Status)) {
continue;
}
Status = Volume->OpenVolume (
Volume,
&RootFs
);
if (EFI_ERROR (Status)) {
continue;
}
//
// 1. Try to open folder structure
// If one of the folders does not exist, a new folder will be created automatically
//
StrCpyS (RecoveryFolderTemp, BufferSize / sizeof (CHAR16), RecoveryFolder);
Walker = RecoveryFolderTemp;
while (1) {
Walker = StrStr (Walker, L"\\\\");
if (Walker == NULL) {
break;
}
ZeroMem ((VOID *) Walker, sizeof (CHAR16) * 2);
Status = RootFs->Open (
RootFs,
&FileHandle,
RecoveryFolderTemp,
EFI_FILE_MODE_READ,
EFI_FILE_DIRECTORY
);
if (EFI_ERROR (Status)) {
Status = RootFs->Open (
RootFs,
&FileHandle,
RecoveryFolderTemp,
EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
EFI_FILE_ARCHIVE | EFI_FILE_DIRECTORY
);
if (EFI_ERROR (Status)) {
RootFs->Close (RootFs);
continue;
}
}
FileHandle->Close (FileHandle);
StrCpyS (RecoveryFolderTemp, BufferSize / sizeof (CHAR16), RecoveryFolder);
Walker += 2;
}
//
// 2. Try to get file info
//
Status = RootFs->Open (
RootFs,
&FileHandle,
RecoveryFileFullPath,
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
0
);
if (EFI_ERROR (Status)) {
//
// File does not exist, so just create it directly
//
goto UpdateFile;
}
BufferSize = 0;
Status = FileHandle->GetInfo (
FileHandle,
&gEfiFileInfoGuid,
&BufferSize,
FileInfo
);
if (Status == EFI_BUFFER_TOO_SMALL) {
FileInfo = AllocateZeroPool (BufferSize);
if (FileInfo == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto CloseFileAndContinue;
}
Status = FileHandle->GetInfo (
FileHandle,
&gEfiFileInfoGuid,
&BufferSize,
FileInfo
);
}
if (EFI_ERROR (Status) || FileInfo->FileSize == 0) {
Status = EFI_SUCCESS;
goto UpdateFile;
}
//
// 3. Check storage backup file version
//
BufferSize = FileInfo->FileSize;
Buffer = AllocateZeroPool (BufferSize);
if (Buffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto CloseFileAndContinue;
}
Status = FileHandle->Read (
FileHandle,
&BufferSize,
Buffer
);
if (EFI_ERROR (Status)) {
Status = EFI_SUCCESS;
goto UpdateFile;
}
BvdtAddr = FdmGetNAtAddr (&gH2OFlashMapRegionBvdtGuid, 1);
ImageBvdtPtr = (CHAR8 *) ((UINTN) Buffer + ((UINTN) BvdtAddr - FlashAreaBaseAddress) + BIOS_VERSION_OFFSET);
OnboardBvdtStr = (CHAR8 *) (UINTN) (BvdtAddr + BIOS_VERSION_OFFSET);
if (AsciiStrCmp (OnboardBvdtStr, ImageBvdtPtr) == 0) {
//
// Storage backup file is the same as onboard BIOS, so skip updates
//
Status = EFI_ABORTED;
goto CloseFileAndContinue;
}
SafeFreePool ((VOID **) &FileInfo);
SafeFreePool ((VOID **) &Buffer);
UpdateFile:
//
// 4. Prepare backup file
//
if (!EFI_ERROR (Status)) {
FileHandle->Delete (FileHandle);
}
Status = RootFs->Open (
RootFs,
&FileHandle,
RecoveryFileFullPath,
EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
0
);
if (EFI_ERROR (Status)) {
RootFs->Close (RootFs);
continue;
}
BufferSize = FlashAreaSize;
Buffer = AllocateZeroPool (BufferSize);
if (Buffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto CloseFileAndContinue;
}
//
// 5. Write file
//
if (!mSpiBackupIsUpdated) {
BiosSelfHealingDisplayMessage (BshBackup, FALSE, TRUE);
BiosSelfHealingProgressBar (0);
}
Status = FlashRead (
Buffer,
(UINT8 *) FlashAreaBaseAddress,
FlashAreaSize
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Read flash address 0x%x error %r\n", FlashAreaBaseAddress, Status));
goto CloseFileAndContinue;
}
Status = FileHandle->Write (
FileHandle,
&BufferSize,
Buffer
);
if (!mSpiBackupIsUpdated) {
BiosSelfHealingProgressBar (100);
gBS->Stall (EFI_STALL_SECONDS (3));
}
CloseFileAndContinue:
DEBUG ((EFI_D_INFO, "BiosSelfHealingDxe: Update Storage backup file status: %r\n", Status));
SafeFreePool ((VOID **) &FileInfo);
SafeFreePool ((VOID **) &Buffer);
FileHandle->Close (FileHandle);
RootFs->Close (RootFs);
}
ExitStorageBackup:
SafeFreePool ((VOID **) &RecoveryFolder);
SafeFreePool ((VOID **) &RecoveryFolderTemp);
SafeFreePool ((VOID **) &RecoveryFileFullPath);
return;
}
/**
Install notification function.
@retval EFI_SUCCESS The operation completed successfully.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
InstallNotificationFunction (
VOID
)
{
EFI_STATUS Status;
EFI_BOOT_MODE BootMode;
VOID *Registration;
H2O_CP_HANDLE CpHandle;
Status = EFI_SUCCESS;
BootMode = GetBootModeHob ();
Registration = NULL;
CpHandle = NULL;
//
// Check if BIOS Self-Healing function is enabled or not
//
if (!PcdGetBool (PcdL05BiosSelfHealingEnable)) {
return EFI_SUCCESS;
}
//
// Register BIOS recovery callback
//
Registration = NULL;
if ((BootMode == BOOT_IN_RECOVERY_MODE) && PcdGetBool (PcdL05TopSwapEnable)) {
EfiCreateProtocolNotifyEvent (
&gEndOfBdsBootSelectionProtocolGuid,
TPL_CALLBACK,
BiosRecoveryCallback,
NULL,
(VOID **) &Registration
);
}
//
// Register EEPROM/CMOS Flag update callback on H2O_CP_MEDIUM_HIGH of gH2OBdsCpDxeSmmReadyToLockBeforeGuid to
// ensure it runs before UpdateSpiBackupCallback
//
CpHandle = NULL;
Status = H2OCpRegisterHandler (
&gH2OBdsCpDxeSmmReadyToLockBeforeGuid,
UpdateFlagCallback,
H2O_CP_MEDIUM_HIGH,
&CpHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Register SPI Backup (IBB_R) update callback on H2O_CP_MEDIUM of gH2OBdsCpDxeSmmReadyToLockBeforeGuid to
// ensure it runs before BiosProtectionStage1
//
CpHandle = NULL;
Status = H2OCpRegisterHandler (
&gH2OBdsCpDxeSmmReadyToLockBeforeGuid,
UpdateSpiBackupCallback,
H2O_CP_MEDIUM,
&CpHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Register Storage Backup (OBB_R) update callback
//
Registration = NULL;
EfiCreateProtocolNotifyEvent (
&gEndOfBdsBootSelectionProtocolGuid,
TPL_CALLBACK - 1,
UpdateStorageBackupCallback,
NULL,
(VOID **) &Registration
);
return Status;
}
#endif
/**
BIOS Self-Healing DXE Entry.
@param ImageHandle The firmware allocated handle for the UEFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The operation completed successfully.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
BiosSelfHealingDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//#[-start-220627-dennis0018-modify]//
#ifdef LCFC_SUPPORT
LENOVO_VARIABLE_PROTOCOL *LenovoVariable = NULL;
EFI_GUID MtmNumberGuid = LVAR_MTM_NUMBER_GUID;
UINT32 MtmDataSize = 0x40;
UINT8 MtmBufferPtr[0x40];
#endif
EFI_STATUS Status;
EFI_L05_BIOS_SELF_HEALING_PROTOCOL *L05BiosSelfHealingPtr;
Status = EFI_SUCCESS;
L05BiosSelfHealingPtr = NULL;
#ifdef LCFC_SUPPORT
Status = gBS->LocateProtocol (
&gLenovoVariableProtocolGuid,
NULL,
&LenovoVariable
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
Status = LenovoVariable->GetVariable (
LenovoVariable,
&MtmNumberGuid,
&MtmDataSize,
MtmBufferPtr
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
#endif
//#[-end-220627-dennis0018-modify]//
//
// Install Protocol
//
L05BiosSelfHealingPtr = AllocateZeroPool (sizeof (EFI_L05_BIOS_SELF_HEALING_PROTOCOL));
if (L05BiosSelfHealingPtr == NULL) {
return EFI_OUT_OF_RESOURCES;
}
L05BiosSelfHealingPtr->FunctionSwitch = BiosSelfHealingFunctionSwitch;
L05BiosSelfHealingPtr->DisplayMessage = BiosSelfHealingDisplayMessage;
L05BiosSelfHealingPtr->ProgressBar = BiosSelfHealingProgressBar;
Status = gBS->InstallProtocolInterface (
&ImageHandle,
&gEfiL05BiosSelfHealingProtocolGuid,
EFI_NATIVE_INTERFACE,
L05BiosSelfHealingPtr
);
if (EFI_ERROR (Status)) {
return Status;
}
#if (FixedPcdGet32 (PcdL05ChipsetName) != L05_CHIPSET_NAME_ALDERLAKE)
Status = InstallNotificationFunction ();
if (EFI_ERROR (Status)) {
return Status;
}
#endif
return Status;
}