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