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

654 lines
20 KiB
C

/** @file
BIOS Self-Healing SMM Driver.
;******************************************************************************
;* Copyright (c) 2020, Insyde Software Corporation. 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 "BiosSelfHealingSmm.h"
EFI_SMM_CPU_PROTOCOL *mSmmCpu = NULL;
H2O_IHISI_PROTOCOL *mH2OIhisi = NULL;
STATIC IHISI_REGISTER_TABLE mFeatureFbtsRegisterTable[] = {
//
// AH=1Fh
//
{ FBTSApHookPoint, "S1FL05ApHookForBios", FbtsApHookForBios}
};
/**
Read information from the CPU save state.
@param Register Specifies the CPU register to read form the save state.
@param Width The number of bytes to read from the CPU save state.
@param CpuNum Specifies the zero-based index of the CPU save state.
@param RegisterData Upon return, this holds the CPU register value read from the save state.
@retval EFI_SUCCESS The register was read from Save State
@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
@retval EFI_INVALID_PARAMTER This or Buffer is NULL.
**/
EFI_STATUS
ReadDwordRegister (
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
IN UINTN Width,
IN UINTN CpuNum,
OUT VOID *RegisterData
)
{
EFI_STATUS Status;
Status = mSmmCpu->ReadSaveState (
mSmmCpu,
Width,
RegisterNum,
CpuNum,
RegisterData
);
return Status;
}
/**
Write value to a CPU Save State register on the target processor.
This function abstracts the differences that whether the CPU Save State register is in the
IA32 CPU Save State Map or X64 CPU Save State Map.
This function supports writing a CPU Save State register in SMBase relocation handler.
@param RegisterNum Specifies the CPU register to write to the save state.
@param Width The number of bytes to read from the CPU save state.
@param CpuNum Specifies the zero-based index of the CPU save state.
@param RegisterData Upon entry, this holds the new CPU register value.
@retval EFI_SUCCESS The register was written to Save State.
@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor.
@retval EFI_INVALID_PARAMTER ProcessorIndex or Width is not correct.
**/
EFI_STATUS
WriteDwordRegister (
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
IN UINTN Width,
IN UINTN CpuNum,
IN VOID *RegisterData
)
{
EFI_STATUS Status;
Status = mSmmCpu->WriteSaveState (
mSmmCpu,
Width,
RegisterNum,
CpuNum,
RegisterData
);
return Status;
}
/**
Check current BIOS capability of BIOS Self-Healing.
@param None
@retval ReturnStatus Current support capabilities.
**/
UINT32
CheckCapability (
VOID
)
{
UINT32 ReturnStatus;
ReturnStatus = L05_BIOS_SELF_HEALING_FUNCTION_SUPPORTED;
ReturnStatus |= (PcdGetBool (PcdL05BiosSelfHealingEnable)) ? L05_BIOS_SELF_HEALING_FUNCTION_ENABLED : L05_BIOS_SELF_HEALING_FUNCTION_DISABLED;
return ReturnStatus;
}
/**
Set specific destruction flag.
@param None
@retval L05_BIOS_SELF_HEALING_FUNCTION_SUCCESSFUL Setup successful.
@retval L05_BIOS_SELF_HEALING_FUNCTION_FAILED Setup failed.
**/
UINT32
SetDestructionFlag (
IN UINT8 Flag
)
{
EFI_STATUS Status;
UINT8 BiosSelfHealingFlag;
Status = EFI_SUCCESS;
BiosSelfHealingFlag = 0;
//
// Check destruction flag
//
Status = GetBiosSelfHealingFlag (&BiosSelfHealingFlag);
if (EFI_ERROR (Status)) {
return L05_BIOS_SELF_HEALING_FUNCTION_FAILED;
}
if ((BiosSelfHealingFlag & Flag) == Flag) {
return L05_BIOS_SELF_HEALING_FUNCTION_SUCCESSFUL;
}
//
// Set destruction flag
//
BiosSelfHealingFlag |= Flag;
Status = SetBiosSelfHealingFlag (BiosSelfHealingFlag);
if (EFI_ERROR (Status)) {
return L05_BIOS_SELF_HEALING_FUNCTION_FAILED;
}
return L05_BIOS_SELF_HEALING_FUNCTION_SUCCESSFUL;
}
/**
Software SMI callback for verification tool.
@param CpuNum CPU number.
@retval EFI_SUCCESS The operation completed successfully.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
EFIAPI
BiosSelfHealingSmmCallback (
IN UINTN CpuNum
)
{
UINT32 ReturnStatus;
UINT32 Eax;
UINT32 Ebx;
ReturnStatus = L05_BIOS_SELF_HEALING_FUNCTION_FAILED;
Eax = 0;
Ebx = 0;
//
// Check function signature is valid or not
//
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &Eax);
if (Eax != L05_BIOS_SELF_HEALING_FUNCTION) {
return EFI_UNSUPPORTED;
}
ReadDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RBX, sizeof (UINT32), CpuNum, &Ebx);
switch (Ebx) {
case L05_BIOS_SELF_HEALING_CHECK_CAPABILITY:
ReturnStatus = CheckCapability ();
break;
case L05_BIOS_SELF_HEALING_DESTROY_BOOT_BLOCK:
//
// Can only be executed in manufacturing mode and function is enabled
//
if (IsManufacturingMode () && PcdGetBool (PcdL05BiosSelfHealingEnable)) {
ReturnStatus = SetDestructionFlag (L05_BIOS_SELF_HEALING_FLAG_DESTROY_BOOT_BLOCK);
}
break;
case L05_BIOS_SELF_HEALING_DESTROY_FVMAIN:
//
// Can only be executed in manufacturing mode and function is enabled
//
if (IsManufacturingMode () && PcdGetBool (PcdL05BiosSelfHealingEnable)) {
ReturnStatus = SetDestructionFlag (L05_BIOS_SELF_HEALING_FLAG_DESTROY_FVMAIN);
}
break;
default:
ReturnStatus = L05_BIOS_SELF_HEALING_FUNCTION_FAILED;
break;
}
WriteDwordRegister (EFI_SMM_SAVE_STATE_REGISTER_RAX, sizeof (UINT32), CpuNum, &ReturnStatus);
return EFI_SUCCESS;
}
/**
Check pending flags during POST and perform related actions.
@param None
@retval EFI_SUCCESS Successful operation.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
CheckPendingFlag (
VOID
)
{
EFI_STATUS Status;
EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *SmmFwb;
UINT8 BiosSelfHealingFlag;
BOOLEAN DestroyBootBlock;
BOOLEAN DestroyFvMain;
UINTN EraseBase;
UINTN EraseSize;
Status = EFI_SUCCESS;
SmmFwb = NULL;
BiosSelfHealingFlag = 0;
DestroyBootBlock = FALSE;
DestroyFvMain = FALSE;
EraseBase = 0;
EraseSize = 0;
Status = gSmst->SmmLocateProtocol (
&gEfiSmmFwBlockServiceProtocolGuid,
NULL,
&SmmFwb
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Check destruction flag
//
Status = GetBiosSelfHealingFlag (&BiosSelfHealingFlag);
if (EFI_ERROR (Status)) {
return Status;
}
DestroyBootBlock = ((BiosSelfHealingFlag & L05_BIOS_SELF_HEALING_FLAG_DESTROY_BOOT_BLOCK) == L05_BIOS_SELF_HEALING_FLAG_DESTROY_BOOT_BLOCK) ? TRUE : FALSE;
DestroyFvMain = ((BiosSelfHealingFlag & L05_BIOS_SELF_HEALING_FLAG_DESTROY_FVMAIN) == L05_BIOS_SELF_HEALING_FLAG_DESTROY_FVMAIN) ? TRUE : FALSE;
if (!DestroyBootBlock && !DestroyFvMain) {
//
// Flags are not set, just skip
//
return EFI_SUCCESS;
}
if (DestroyBootBlock && IsManufacturingMode ()) {
#if (FixedPcdGet32 (PcdL05ChipsetName) == L05_CHIPSET_NAME_TIGERLAKE)
EraseBase = (UINTN) PcdGet32 (PcdFlashFvFspmBase);
EraseSize = (UINTN) (PcdGet32 (PcdFlashFvFspmSize) +
PcdGet32 (PcdFlashFvFsptSize) +
(UINT32)(PcdGet64 (PcdH2OFlashDeviceMapSize)) +
PcdGet32 (PcdFlashFvRecovery0Size));
#endif
#if (FixedPcdGet32 (PcdL05ChipsetName) == L05_CHIPSET_NAME_ALDERLAKE)
EraseBase = (UINTN) PcdGet32 (PcdFlashFvFspMBase);
EraseSize = (UINTN) (PcdGet32 (PcdFlashFvFspMSize) +
PcdGet32 (PcdFlashFvFspTSize) +
(UINT32)(PcdGet64 (PcdH2OFlashDeviceMapSize)) +
PcdGet32 (PcdFlashFvRecovery0Size));
#endif
#if (FixedPcdGet32 (PcdL05ChipsetName) == L05_CHIPSET_NAME_CEZANNE)
EraseBase = (UINTN) PcdGet32 (PcdFlashFvRecovery2PadBase);
EraseSize = (UINTN) PcdGet32 (PcdFlashFvRecovery2PadSize);
#endif
#if (FixedPcdGet32 (PcdL05ChipsetName) == L05_CHIPSET_NAME_REMBRANDT)
EraseBase = (UINTN) PcdGet32 (PcdFlashFvRecoveryBase);
EraseSize = (UINTN) PcdGet32 (PcdFlashFvRecoverySize);
#endif
Status = SmmFwb->EraseBlocks (
SmmFwb,
EraseBase,
&EraseSize
);
}
if (DestroyFvMain && IsManufacturingMode ()) {
EraseBase = (UINTN) PcdGet32 (PcdFlashFvMainBase);
EraseSize = (UINTN) PcdGet32 (PcdFlashFvMainSize);
Status = SmmFwb->EraseBlocks (
SmmFwb,
EraseBase,
&EraseSize
);
}
//
// Clear flags
//
if (DestroyBootBlock) {
BiosSelfHealingFlag &= ~L05_BIOS_SELF_HEALING_FLAG_DESTROY_BOOT_BLOCK;
}
if (DestroyFvMain) {
BiosSelfHealingFlag &= ~L05_BIOS_SELF_HEALING_FLAG_DESTROY_FVMAIN;
}
if (IsManufacturingMode ()) {
BiosSelfHealingFlag &= ~L05_BIOS_SELF_HEALING_FLAG_BACKUP_BLOCK_SYNCED;
}
Status = SetBiosSelfHealingFlag (BiosSelfHealingFlag);
if (!IsManufacturingMode ()) {
//
// Only clear flags if not in manufacturing mode to avoid false triggering
//
return Status;
}
//
// BIOS should notify EC to set WDT flag when using tool to simulate BootBlock crash
//
OemSvcNotifyEcToSetWdtFlag ();
#ifdef L05_BIOS_SELF_HEALING_TEST_ON_CRB
TopSwapSet (TRUE);
#endif
DEBUG ((DEBUG_INFO, "cold reset from %a()\n", __FUNCTION__));
ResetCold ();
//
// Waiting for reset
//
CpuDeadLoop ();
return Status;
}
/**
Software SMI callback to clear Top Swap bit.
@param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
@param DispatchContext Points to an optional handler context which was specified when the
handler was registered.
@param CommBuffer A pointer to a collection of data in memory that will
be conveyed from a non-SMM environment into an SMM environment.
@param CommBufferSize The size of the CommBuffer.
@retval EFI_SUCCESS The interrupt was handled successfully.
**/
EFI_STATUS
EFIAPI
DisableTopSwapCallback (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *DispatchContext,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
)
{
TopSwapSet (FALSE);
return EFI_SUCCESS;
}
/**
AH=1Fh Fbts Ap Hook For BIOS.
This callback function will be invoked several times during flash process.
@retval EFI_SUCCESS Success returns.
**/
EFI_STATUS
FbtsApHookForBios (
VOID
)
{
EFI_STATUS Status;
UINT8 BiosSelfHealingFlag;
UINT8 ApState;
Status = EFI_SUCCESS;
BiosSelfHealingFlag = 0;
ApState = (UINT8) mH2OIhisi->ReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX);
if (ApState == BeforeWriteRom) {
//
// [Lenovo BIOS Self-Healing Design Guidance Specification v1.9]
// 2.2 Detect
// When BIOS flash happened, before erase/write BIOS should notify EC to set flag (Wdt Flag should be keeped
// even EC power lost, such as stored in the EC EEPROM) to enable WDT.
//
DEBUG ((DEBUG_INFO, "BiosSelfHealingSmm: FbtsApHookForBios => Before write ROM\n"));
OemSvcNotifyEcToSetWdtFlag ();
#if (FixedPcdGet32 (PcdL05ChipsetName) != L05_CHIPSET_NAME_ALDERLAKE)
//
// Clear BackupBlockSynced Flag
//
Status = GetBiosSelfHealingFlag (&BiosSelfHealingFlag);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "BiosSelfHealingSmm: Clear BackupBlockSynced Flag\n"));
BiosSelfHealingFlag &= ~L05_BIOS_SELF_HEALING_FLAG_BACKUP_BLOCK_SYNCED;
Status = SetBiosSelfHealingFlag (BiosSelfHealingFlag);
}
#endif
}
return EFI_SUCCESS;
}
/**
Register IHISI sub function if SubFuncTable CmdNumber/AsciiFuncGuid define in PcdIhisiRegisterTable list.
@param SubFuncTable Pointer to ihisi register table.
@param TableCount SubFuncTable count
@retval EFI_SUCCESS Function succeeded.
@return Other Error occurred in this function.
**/
EFI_STATUS
RegisterIhisiSubFunction (
IN IHISI_REGISTER_TABLE *SubFuncTable,
IN UINT16 TableCount
)
{
EFI_STATUS Status;
UINT8 *PcdTable;
UINT8 PcdPriority;
UINT8 EndChar;
UINTN Index;
UINTN PcdCount;
UINTN PcdMaxCount;
UINTN SignatureSize;
BOOLEAN PcdFoundRegistered;
Status = EFI_SUCCESS;
PcdTable = NULL;
PcdPriority = 0;
EndChar = 0;
Index = 0;
PcdCount = 0;
PcdMaxCount = 0;
SignatureSize = 0;
PcdFoundRegistered = FALSE;
PcdTable = (UINT8 *) PcdGetPtr (PcdIhisiRegisterTable);
PcdMaxCount = FixedPcdGetPtrSize (PcdIhisiRegisterTable) / sizeof (UINT8);
if ((FixedPcdGetPtrSize (PcdIhisiRegisterTable) % sizeof (PCD_IHISI_REGISTER_TABLE)) != 0) {
DEBUG ((EFI_D_ERROR, "PcdIhisiRegisterTable of description not follow PCD_IHISI_REGISTER_TABLE definition, \
it may cause some of IHISI function register fail \n"));
}
for (Index = 0; Index < TableCount; Index ++) {
PcdCount = 0;
PcdPriority = 0x80;
PcdFoundRegistered = FALSE;
SignatureSize = AsciiStrLen (SubFuncTable[Index].FuncSignature);
//
// Calculate PCD of address to find 1. CmdNumber 2. FuncSignature 3. Priority
//
do {
if (SubFuncTable[Index].CmdNumber == *(PcdTable + PcdCount)) {
PcdCount++;
if (AsciiStrnCmp (SubFuncTable[Index].FuncSignature, (CHAR8 *) (PcdTable + PcdCount), SignatureSize) == 0) {
if (EndChar == *(PcdTable + PcdCount + SignatureSize)) {
PcdPriority = *(PcdTable + PcdCount + SignatureSize + 1);
PcdFoundRegistered = TRUE;
break;
}
}
}
PcdCount++;
} while (PcdCount < PcdMaxCount);
if (PcdFoundRegistered) {
Status = mH2OIhisi->RegisterCommand (
SubFuncTable[Index].CmdNumber,
SubFuncTable[Index].IhisiFunction,
PcdPriority
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "IHISI command :0x%X, priority : 0x%X, that already has a registered function\n", SubFuncTable[Index].CmdNumber, PcdPriority));
}
}
}
return Status;
}
/**
BIOS Self-Healing SMM 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
BiosSelfHealingSmmEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_L05_SMM_SW_SMI_INTERFACE_PROTOCOL *L05SwSmiPtr;
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
EFI_SMM_SW_REGISTER_CONTEXT SwContext;
EFI_HANDLE SwHandle;
IHISI_REGISTER_TABLE *SubFuncTable;
UINT16 TableCount;
Status = EFI_SUCCESS;
L05SwSmiPtr = NULL;
SwDispatch = NULL;
SwContext.SwSmiInputValue = 0;
SwHandle = NULL;
SubFuncTable = NULL;
TableCount = 0;
Status = gSmst->SmmLocateProtocol (
&gEfiSmmCpuProtocolGuid,
NULL,
&mSmmCpu
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gSmst->SmmLocateProtocol (
&gH2OIhisiProtocolGuid,
NULL,
&mH2OIhisi
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gSmst->SmmLocateProtocol (
&gEfiL05SmmSwSmiInterfaceProtocolGuid,
NULL,
&L05SwSmiPtr
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gSmst->SmmLocateProtocol (
&gEfiSmmSwDispatch2ProtocolGuid,
NULL,
&SwDispatch
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Register BIOS Self-Healing Software SMI callback
//
Status = L05SwSmiPtr->RegisterCallbackFunction (
L05SwSmiPtr,
L05_SECURITY_SW_SMI,
FeatureCallbackType,
BiosSelfHealingSmmCallback
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Check if BIOS Self-Healing function is enabled or not
//
if (!PcdGetBool (PcdL05BiosSelfHealingEnable)) {
return EFI_SUCCESS;
}
//
// Check pending flags during POST and perform related actions
//
CheckPendingFlag ();
#ifdef L05_BIOS_SELF_HEALING_TEST_ON_CRB
//
// Register disable Top Swap Software SMI callback
//
SwContext.SwSmiInputValue = (UINTN) -1;
Status = SwDispatch->Register (
SwDispatch,
DisableTopSwapCallback,
&SwContext,
&SwHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
PcdSet8S (PcdL05TopSwapDisableSwSmi, (UINT8) SwContext.SwSmiInputValue);
DEBUG ((DEBUG_INFO, "PcdL05TopSwapDisableSwSmi value: %x\n", PcdGet8 (PcdL05TopSwapDisableSwSmi)));
#endif
//
// Register IHISI Sub Function
//
SubFuncTable = mFeatureFbtsRegisterTable;
TableCount = sizeof (mFeatureFbtsRegisterTable) / sizeof (mFeatureFbtsRegisterTable[0]);
Status = RegisterIhisiSubFunction (SubFuncTable, TableCount);
if (EFI_ERROR (Status)) {
return Status;
}
return Status;
}