/** @file This file include all BDS platform recovery flash functions. ;****************************************************************************** ;* Copyright (c) 2012 - 2021, 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 "RecoveryFlash.h" #include #include "SecureFlash.h" #include #include #include STATIC UINT8 *mAuthVarBackupDataBuffer = NULL; STATIC EFI_RESET_SYSTEM mOriginalResetSystemPtr; BOOLEAN RecoveryCapsuleIsExecutable ( IN EFI_PEI_HOB_POINTERS *RecoveryHob ); EFI_STATUS ExecuteRecoveryCapsule ( IN EFI_PEI_HOB_POINTERS *RecoveryHob ); VOID EFIAPI ResetSystemDoNothing ( IN EFI_RESET_TYPE ResetType, IN EFI_STATUS ResetStatus, IN UINTN DataSize, IN VOID *ResetData OPTIONAL ) { return; } VOID HookResetSystem ( IN BOOLEAN ToHook ) { if (ToHook) { mOriginalResetSystemPtr = gRT->ResetSystem; gRT->ResetSystem = ResetSystemDoNothing; } else { gRT->ResetSystem = mOriginalResetSystemPtr; } } /** Restore authenticated variables from the buffer mAuthVarBackupDataBuffer. **/ VOID RestoreAuthVariable ( VOID ) { EFI_FIRMWARE_VOLUME_HEADER *OnboardNvStoreageFvHeader; VARIABLE_HEADER *VariableHeader; UINTN SkipHeaderSize; UINT32 FlashSize; UINT32 FlashAddress; UINT8 GetCmdStatus; VOID *CmdBuffer; UINT32 CmdBufferSize; UINTN Index; UINTN Count; if (mAuthVarBackupDataBuffer == NULL) { return ; } OnboardNvStoreageFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid , 1); if (OnboardNvStoreageFvHeader == NULL) { return; } SkipHeaderSize = (UINTN)OnboardNvStoreageFvHeader->HeaderLength + GetVariableStoreHeaderSize (); // // The current variable region should be empty after done Crisis Recovery Flash. // If the variable region with any data, not to restore AuthVariable data to prevent variable region from breaking. // But it shall not happen. // VariableHeader = NULL; VariableHeader = (VARIABLE_HEADER *)((UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid , 1) + SkipHeaderSize); if (IsValidVariableHeader (VariableHeader)) { FreePool (mAuthVarBackupDataBuffer); return ; } // // Copy new BIOS firmware volume and variable store header information to mAuthVarBackupDataBuffer. // CopyMem (mAuthVarBackupDataBuffer, OnboardNvStoreageFvHeader, SkipHeaderSize); FlashSize = (UINT32) FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1); FlashAddress = (UINT32)(UINTN)OnboardNvStoreageFvHeader; H2OIhisiAuthLock (); GetCmdStatus = H2OIhisiGetCmdBuffer (&CmdBuffer, &CmdBufferSize, NULL, NULL); if (GetCmdStatus != 0) { H2OIhisiFbtsWrite (FlashSize, FlashAddress, mAuthVarBackupDataBuffer); } else { Count = FlashSize / SIZE_4KB; for (Index = 0; Index < Count; Index++) { CopyMem ((VOID *) (UINTN) CmdBuffer, mAuthVarBackupDataBuffer + (Index * SIZE_4KB), SIZE_4KB); H2OIhisiFbtsWrite (SIZE_4KB, (UINT32)(FlashAddress + (Index * SIZE_4KB)), (UINT8 *) (UINTN) CmdBuffer); } } H2OIhisiAuthUnlock (); } /** Backup authenticated variables to the buffer mAuthVarBackupDataBuffer. **/ VOID BackupAuthVariable ( VOID ) { EFI_FIRMWARE_VOLUME_HEADER *OnboardNvStoreageFvHeader; VARIABLE_HEADER *VariableHeader; VARIABLE_HEADER *NextVariable; UINTN VariableSize; UINTN LastVariableOffset; UINTN SkipHeaderSize; OnboardNvStoreageFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid , 1); if (OnboardNvStoreageFvHeader == NULL) { return; } SkipHeaderSize = (UINTN)OnboardNvStoreageFvHeader->HeaderLength + GetVariableStoreHeaderSize (); mAuthVarBackupDataBuffer = AllocatePool ((UINTN)FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1)); if (mAuthVarBackupDataBuffer == NULL) { return; } gBS->SetMem (mAuthVarBackupDataBuffer, (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1), 0xFF); LastVariableOffset = 0; VariableSize = 0; NextVariable = NULL; VariableHeader = (VARIABLE_HEADER *)((UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid , 1) + SkipHeaderSize); while (IsValidVariableHeader (VariableHeader)) { NextVariable = GetNextVariablePtr (VariableHeader); if (VariableHeader->State == VAR_ADDED || VariableHeader->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) { if (((VariableHeader->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) || ((VariableHeader->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)){ VariableSize = (UINTN)NextVariable - (UINTN)VariableHeader; if (LastVariableOffset + VariableSize < (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1)) { CopyMem ( &mAuthVarBackupDataBuffer[LastVariableOffset] + SkipHeaderSize, VariableHeader, VariableSize ); } LastVariableOffset += VariableSize; } } VariableHeader = NextVariable; } } VOID CreateSetupModeVariable ( VOID ) { EFI_STATUS Status; UINT8 SetupMode; UINTN Size; Size = sizeof (UINT8); Status = gRT->GetVariable ( EFI_SETUP_MODE_NAME, &gEfiGlobalVariableGuid, NULL, &Size, &SetupMode ); if (Status == EFI_NOT_FOUND) { SetupMode = 0; Status = gRT->SetVariable ( EFI_SETUP_MODE_NAME, &gEfiGlobalVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (UINT8), &SetupMode ); } } /** Prints a formatted unicode string to the default console, at the supplied cursor position @param Column The column of cursor position to print the string at @param Row The row of cursor position to print the string at @param Fmt Format string @return Length of string printed to the console **/ UINTN PrintAt ( IN UINTN Column, IN UINTN Row, IN CHAR16 *Fmt, ... ) { CHAR16 *Buffer; UINTN StrLen; VA_LIST Marker; Buffer = AllocateZeroPool (0x10000); ASSERT (Buffer); if (Column != (UINTN) - 1) { gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row); } VA_START (Marker, Fmt); StrLen = UnicodeVSPrint (Buffer, 0x10000, Fmt, Marker); VA_END (Marker); if (gST->ConOut != NULL) { // // To be extra safe make sure ConOut has been initialized // gST->ConOut->OutputString (gST->ConOut, Buffer); } FreePool (Buffer); return StrLen; } /** Display the Recovery flash user interface to user to select @param FlashMode Input flash mode (DEFAULT_FLASH_DEVICE_TYPE, SPI_FLASH_DEVICE_TYPE, OTHER_FLASH_DEVICE_TYPE) @retval EFI_SUCCESS Success @retval Other Error **/ EFI_STATUS RecoveryPopUp ( IN UINTN FlashMode ) { CHAR16 *TitleString; CHAR16 *FlashInfoStringArray[3]; UINT32 Index; EFI_INPUT_KEY Key; H2O_DIALOG_PROTOCOL *H2ODialog; EFI_STATUS Status; H2O_BDS_SERVICES_PROTOCOL *H2OBdsServices; BOOLEAN EnableHotKey; Status = gBS->LocateProtocol (&gH2OBdsServicesProtocolGuid, NULL, (VOID **)&H2OBdsServices); if (!EFI_ERROR (Status)) { EnableHotKey = FALSE; H2OBdsServices->EnableHotKeys (H2OBdsServices, &EnableHotKey); } if (IsFirmwareFailureRecovery ()) { // // If it is recovery from firmware failure, update firmware directly without user interface // RecoveryFlash (FlashMode); return EFI_SUCCESS; } Status = gBS->LocateProtocol ( &gH2ODialogProtocolGuid, NULL, (VOID **) &H2ODialog ); if (EFI_ERROR (Status)) { return Status; } // // Intialize local variable and string for use // Index = 0; TitleString = BdsLibGetStringById (STRING_TOKEN (STR_RECOVERY_FLASH_TITLE)); FlashInfoStringArray[0] = BdsLibGetStringById (STRING_TOKEN (STR_RECOVERY_FLASH_YES)); FlashInfoStringArray[1] = BdsLibGetStringById (STRING_TOKEN (STR_RECOVERY_FLASH_RESETSYSTEM)); FlashInfoStringArray[2] = 0x00; gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_WHITE, EFI_BLACK)); while (TRUE) { // // Print recovery flash message // H2ODialog->OneOfOptionDialog ( 2, FALSE, NULL, &Key, 60, TitleString, &Index, (CHAR16 **) (FlashInfoStringArray), 0 ); if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { // // Adjust the item number, there are 3 choices. // if (Index == 0) { RecoveryFlash (FlashMode); } else { ResetCommand (); break; } } } gST->ConOut->ClearScreen (gST->ConOut); // // Free allocated strings // FreePool (TitleString); FreePool (FlashInfoStringArray[0]); FreePool (FlashInfoStringArray[1]); return EFI_SUCCESS; } /** Send reset request to reset system @retval EFI_SUCCESS **/ EFI_STATUS ResetCommand ( VOID ) { UINT8 RecoveryFlag; RecoveryFlag = RESET_IN_RECOVERY; SaveLockBox (&gSecureFlashInfoGuid, &RecoveryFlag, sizeof (RecoveryFlag)); H2OIhisiAuthLock (); H2OIhisiAuth (IHISI_AUTH_POST_ONLY, NULL); H2OIhisiFbtsFlashComplete (IHISI_REBOOT, NormalFlash); H2OIhisiAuthUnlock (); return EFI_SUCCESS; } /** Update the precentage of recovery flash progress in dialog @param PercentageValue The finished percentage of flash process **/ VOID Drawpercentage ( IN UINTN PercentageValue ) { UINTN Columns; UINTN ColumnPosition; UINTN Rows; UINTN RowPosition; gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Columns, &Rows); RowPosition = Columns / 2; ColumnPosition = Rows / 2; PrintAt (RowPosition, ColumnPosition, L"%d%%", PercentageValue); } /** Drawing dialog for showing recovery flash progress **/ VOID DrawDialogBlock ( VOID ) { CHAR16 CleanLine[80]; CHAR16 *StatusString; CHAR16 SelectIndexLin[80]; UINTN Color; UINTN Columns; UINTN Index; UINTN Item; UINTN Rows; UINTN StrLenth; Color = 0; Item = 1; StrLenth = 20; // // Set the background // gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Columns, &Rows); Color = EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLUE); gST->ConOut->SetAttribute (gST->ConOut, Color); StrLenth = StrLenth / 2; for (Index = 0; Index < (StrLenth + 2) * 2 + 1; Index++) { CleanLine[Index] = 0x20; SelectIndexLin[Index] = 0x20; } CleanLine[(StrLenth + 2) * 2 + 1] = 0x0000; SelectIndexLin[1 + 1] = 0x0000; Item = Item / 2; for (Index = Rows / 2 - (Item + 3); Index <= Rows / 2 + (Item + 1); Index++) { PrintAt (Columns / 2 - (StrLenth + 2), Index, L"%s", CleanLine); } // // Next three step will draw a dialog // 1.draw three row line // 2.draw two side // 3.draw four corner // // // This is draw three row line // for (Index = Columns / 2 - (StrLenth + 1); Index < Columns / 2 + (StrLenth + 2) ; Index++) { PrintAt (Index, Rows / 2 - (Item + 3), L"%c", BOXDRAW_HORIZONTAL); PrintAt (Index, Rows / 2 - (Item + 1), L"%c", BOXDRAW_HORIZONTAL); PrintAt (Index, Rows / 2 + (Item + 1), L"%c", BOXDRAW_HORIZONTAL); } // // Draw two side // for (Index = Rows / 2 - (Item + 2); Index < Rows / 2 + (Item + 1); Index++) { PrintAt (Columns / 2 - (StrLenth + 2), Index, L"%c", BOXDRAW_VERTICAL); PrintAt (Columns / 2 + (StrLenth + 2), Index, L"%c", BOXDRAW_VERTICAL); } // // This is draw the dialog four corner // PrintAt (Columns / 2 + (StrLenth + 2) ,Rows / 2 + (Item + 1), L"%c", BOXDRAW_UP_LEFT); PrintAt (Columns / 2 + (StrLenth + 2) ,Rows / 2 - (Item + 3), L"%c", BOXDRAW_DOWN_LEFT); PrintAt (Columns / 2 - (StrLenth + 2), Rows / 2 + (Item + 1), L"%c", BOXDRAW_UP_RIGHT); PrintAt (Columns / 2 - (StrLenth + 2), Rows / 2 - (Item + 3), L"%c", BOXDRAW_DOWN_RIGHT); // // Print the title and flash status percentage // StatusString = BdsLibGetStringById (STRING_TOKEN (STR_RECOVERY_FLASH_STATUS)); if (StatusString == NULL) { return; } PrintAt (Columns / 2 - (StrLen (StatusString) / 2), Rows / 2 - (Item + 2), L"%s", StatusString); FreePool (StatusString); } /** Transfer flash complete request to the reset action of recovery complete checkpoint. @param[in] FlashCompleteRequest Flash complete request @return The reset action of recovery complete checkpoint **/ STATIC UINT8 GetCpRecoveryCompleteResetAction ( IN UINT8 FlashCompleteRequest ) { switch (FlashCompleteRequest) { case FlashCompleteDoNothing: return H2O_BDS_CP_RECOVERY_COMPLETE_DO_NOTHING; case FlashCompleteShutdown: return H2O_BDS_CP_RECOVERY_COMPLETE_SHUTDOWN; case FlashCompleteReboot: default: return H2O_BDS_CP_RECOVERY_COMPLETE_REBOOT; } } /** The entry point to doing recovery flash @param[in] FlashMode Input flash mode (DEFAULT_FLASH_DEVICE_TYPE, SPI_FLASH_DEVICE_TYPE, OTHER_FLASH_DEVICE_TYPE) **/ VOID RecoveryFlash ( IN UINTN FlashMode ) { UINT8 *BufferTmp; UINT8 FlashDevice; UINT8 *FlashTmp; UINT8 CommTmp; UINT8 *MapTmp; UINT8 Index; CHAR16 *RebootString; UINT32 FlashAddress; UINT32 FlashSize; UINTN Columns; UINTN CompareTemp; UINTN FlashPrecentage; UINTN IndexCounter; UINTN Item; UINTN PEIBaseTemp[20]; UINTN Rows; UINTN WriteSize; VOID *HobList; EFI_PEI_HOB_POINTERS RecoveryHob; EFI_STATUS Status; UINTN TotalFlashSectors; UINTN FirmwareSize; UINT8 RecoveryFlag; VOID *CmdBuffer; UINT32 CmdBufferSize; UINT8 GetCmdStatus; POST_CODE (BDS_RECOVERY_START_FLASH); // // Initial local variable // Item = 0; WriteSize = 0; CommTmp = 0xFF; Index = 0; CompareTemp = 0; FlashTmp = NULL; MapTmp = NULL; FlashDevice = (UINT8) FlashMode; BackupAuthVariable (); ZeroMem (PEIBaseTemp, 20); gST->ConOut->ClearScreen (gST->ConOut); // // Get the PEI phase .FD file memory base addess 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); } HookResetSystem (TRUE); if (RecoveryCapsuleIsExecutable (&RecoveryHob)) { // // Set this flag to hook IHISI 16h(FbtsComplete), force it to return here, // so, we can restore the authenticated variables. // RecoveryFlag = FLASH_COMPLETE_IN_RECOVERY; Status = SaveLockBox (&gSecureFlashInfoGuid, &RecoveryFlag, sizeof (RecoveryFlag)); ExecuteRecoveryCapsule (&RecoveryHob); RestoreAuthVariable (); HookResetSystem (FALSE); // // Clear firmware update flag by erase the signature in flash part for seamless recovery // SetFirmwareUpdatingFlag (FALSE); // // Use checkpoint to provide project customization interface and use PCD to control this interface is // enabled or disabled. // if (FeaturePcdGet (PcdH2OBdsCpRecoveryCompleteSupported)) { H2O_BDS_CP_RECOVERY_COMPLETE_DATA BdsRecoveryCompleteData; UINT8 RequestAction; UINTN Size; EFI_RESET_TYPE ResetType; RequestAction = 0; Size = sizeof (RequestAction); Status = RestoreLockBox (&gSecureFlashInfoGuid, &RequestAction, &Size); BdsRecoveryCompleteData.Size = sizeof (H2O_BDS_CP_RECOVERY_COMPLETE_DATA); BdsRecoveryCompleteData.Status = H2O_CP_TASK_NORMAL; BdsRecoveryCompleteData.RequestAction = GetCpRecoveryCompleteResetAction (RequestAction); BdsRecoveryCompleteData.ResetData = NULL; BdsRecoveryCompleteData.ResetDataSize = 0; DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OBdsCpRecoveryCompleteGuid)); Status = H2OCpTrigger (&gH2OBdsCpRecoveryCompleteGuid, &BdsRecoveryCompleteData); DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", BdsRecoveryCompleteData.Status)); if (BdsRecoveryCompleteData.Status == H2O_CP_TASK_SKIP) { return; } if (BdsRecoveryCompleteData.Status == H2O_CP_TASK_UPDATE) { if (BdsRecoveryCompleteData.RequestAction == H2O_BDS_CP_RECOVERY_COMPLETE_DO_NOTHING) { return; } else if (BdsRecoveryCompleteData.RequestAction == H2O_BDS_CP_RECOVERY_COMPLETE_SHUTDOWN) { ResetType = EfiResetShutdown; } else if (BdsRecoveryCompleteData.RequestAction == H2O_BDS_CP_RECOVERY_COMPLETE_REBOOT) { ResetType = EfiResetCold; } else if (BdsRecoveryCompleteData.RequestAction == H2O_BDS_CP_RECOVERY_COMPLETE_WARM_RESET) { ResetType = EfiResetWarm; } else if (BdsRecoveryCompleteData.RequestAction == H2O_BDS_CP_RECOVERY_COMPLETE_PLATFORM_SPECIFIC_RESET) { ResetType = EfiResetPlatformSpecific; } else { return; } gRT->ResetSystem (ResetType, EFI_SUCCESS, BdsRecoveryCompleteData.ResetDataSize, BdsRecoveryCompleteData.ResetData); } } gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); } BufferTmp =(UINT8 *) (UINTN)RecoveryHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryBaseAddress; FirmwareSize = (UINTN) RecoveryHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryLength; H2OIhisiAuthLock (); GetCmdStatus = H2OIhisiGetCmdBuffer (&CmdBuffer, &CmdBufferSize, NULL, NULL); if (GetCmdStatus != 0) { FlashTmp = AllocatePool (0x100); MapTmp = AllocatePool (0x100); if (FlashTmp == NULL || MapTmp == NULL) { return; } } else { FlashTmp = (UINT8 *)(UINTN) CmdBuffer; MapTmp = (UINT8 *)((UINTN) CmdBuffer + 0x100); } gBS->SetMem (FlashTmp, 0x100, 0); gBS->SetMem (MapTmp, 0x100, 0); CommTmp = H2OIhisiFbtsGetFlashPartInfo ((FBTS_FLASH_DEVICE **) &FlashTmp, (FD_BLOCK_MAP **)&MapTmp); FlashAddress = 0xFFFFFFFF - (UINT32)FirmwareSize + 1; WriteSize = *(UINT16 *)(&(MapTmp[0])); if (FLASH_BLOCK_SIZE == WriteSize * SMI_FLASH_UNIT_BYTES) { FlashSize = FLASH_BLOCK_SIZE; } else { FlashSize = FLASH_SECTOR_SIZE; } // // Each block size is 0x1000 or 0x10000 and Call IHISI to flash ROM part // DrawDialogBlock (); TotalFlashSectors = FirmwareSize / FlashSize; H2OIhisiAuth (IHISI_AUTH_POST_ONLY, NULL); for (IndexCounter = 0; IndexCounter < TotalFlashSectors; IndexCounter++) { if (GetCmdStatus != 0) { CommTmp = H2OIhisiFbtsWrite (FlashSize, FlashAddress, BufferTmp); } else { CopyMem ((VOID *) (UINTN) CmdBuffer, BufferTmp, FlashSize); CommTmp = H2OIhisiFbtsWrite (FlashSize, FlashAddress, (VOID *)(UINTN)CmdBuffer ); } FlashAddress += FlashSize; BufferTmp += FlashSize; FlashPrecentage = (IndexCounter + 1) * 100 / TotalFlashSectors; Drawpercentage (FlashPrecentage); } H2OIhisiAuthUnlock (); RestoreAuthVariable (); HookResetSystem (FALSE); gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Columns, &Rows); RebootString = BdsLibGetStringById (STRING_TOKEN (STR_RECOVERY_FLASH_REBOOT)); PrintAt (Columns / 2 - 4, Rows / 2 - Item + 0, L"%s", RebootString); FreePool (RebootString); for (IndexCounter = 0; IndexCounter < 10000; IndexCounter++) { gBS->Stall (300); } ResetCommand (); }