/** @file This driver provides IHISI interface in SMM mode ;****************************************************************************** ;* Copyright (c) 2018 - 2022, 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 "IhisiSmm.h" #include H2O_IHISI_PROTOCOL *mH2OIhisi = NULL; EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *mSmmFwBlockService = NULL; STATIC CONST BOOLEAN mPcdIhisiRegisterTableValid = (BOOLEAN)(FixedPcdGetPtrSize (PcdIhisiRegisterTable) % sizeof (PCD_IHISI_REGISTER_TABLE) == 0); /** Get Bootguard svn info from CSME @param[out] RevocationFromMe Pointer to Arb SVN info data @retval EFI_SUCCESS Function succeeded. @return Other Error occurred in this function. **/ EFI_STATUS GetArbSvnSaveInfoFromME ( REVOCATION_VALUE *RevocationFromMe ) { EFI_STATUS Status; UINT32 MeStatus; ARB_SVN_INFO_ENTRY *ArbSvnInfo; UINT32 NumOfEntries; UINT8 KmId; UINT32 Index; BOOT_GUARD_BOOT_STATE BtgStatus; MeStatus = ME_NOT_READY; BtgStatus = BootGuardBootStateLegacyBoot; ArbSvnInfo = NULL; NumOfEntries = 1; KmId = 0; Status = BootGuardPlatformLibGetMeStatus (&MeStatus); if (!EFI_ERROR (Status)) { if (((MeStatus & 0x0f) == ME_READY)){ BtgStatus = BootGuardPlatformLibDetermineBootState (); RevocationFromMe->BootGuardStatus = BtgStatus; if (BtgStatus != BootGuardBootStateLegacyBoot) { Status = gSmst->SmmAllocatePool ( EfiRuntimeServicesData, (NumOfEntries * sizeof (ARB_SVN_INFO_ENTRY)), (VOID**)&ArbSvnInfo ); if (!EFI_ERROR(Status) && ArbSvnInfo != NULL) { ArbSvnInfo = ZeroMem (ArbSvnInfo, (NumOfEntries * sizeof (ARB_SVN_INFO_ENTRY))); // // Get the current SVN value of ACM, BPM and KM through HECI command. // Status = HeciArbSvnGetInfoMsg (&NumOfEntries, ArbSvnInfo); if (EFI_ERROR (Status) && (Status == EFI_BUFFER_TOO_SMALL)) { Status = gSmst->SmmFreePool (ArbSvnInfo); ASSERT_EFI_ERROR (Status); Status = gSmst->SmmAllocatePool ( EfiRuntimeServicesData, (NumOfEntries * sizeof (ARB_SVN_INFO_ENTRY)), (VOID**)&ArbSvnInfo ); if (!EFI_ERROR(Status) && ArbSvnInfo != NULL) { // // Get the current SVN value of ACM, BPM and KM through HECI command. // Status = HeciArbSvnGetInfoMsg (&NumOfEntries, ArbSvnInfo); } } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to get ArbSvnInfo through HECI command, Status = %r\n", Status)); goto ArbExit; } } else { DEBUG ((DEBUG_ERROR, "Failed to get ArbSvnInfo, error in Allocate Pool\n")); Status = EFI_UNSUPPORTED; goto ArbExit; } // // Get current KMID through HECI command. // Status = BootGuardPlatformLibGetKMID (&KmId); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to get KMID through HECI command, Status = %r\n", Status)); goto ArbExit; } } else { DEBUG ((DEBUG_ERROR, "NOT Bootguard\n")); //BtgStatus = BootGuardBootStateLegacyBoot RevocationFromMe->ACMSVN = 0; RevocationFromMe->BPMSVN = 0; RevocationFromMe->KMSVN = 0; RevocationFromMe->KMID = 0; Status = EFI_SUCCESS; goto ArbExit; } } else { DEBUG ((DEBUG_ERROR, "ME NOT READY\n")); Status = EFI_UNSUPPORTED; goto ArbExit; } } else { DEBUG ((DEBUG_ERROR, "Get ME Status fail\n")); goto ArbExit; } for (Index = 0; Index < NumOfEntries; Index++) { if (ArbSvnInfo[Index].UsageId == MFT_KEY_USAGE_INDEX_OEM_ACM_MANIFEST) { RevocationFromMe->ACMSVN = ArbSvnInfo[Index].ExecutingSvn; } if (ArbSvnInfo[Index].UsageId == MFT_KEY_USAGE_INDEX_BOOT_POLICY_MANIFEST) { RevocationFromMe->BPMSVN = ArbSvnInfo[Index].ExecutingSvn; } if (ArbSvnInfo[Index].UsageId == MFT_KEY_USAGE_INDEX_OEM_BTG_KEY_MANIFEST) { RevocationFromMe->KMSVN = ArbSvnInfo[Index].ExecutingSvn; } } RevocationFromMe->KMID = KmId; ArbExit: if (ArbSvnInfo != NULL) { Status = gSmst->SmmFreePool (ArbSvnInfo); ASSERT_EFI_ERROR (Status); ArbSvnInfo = NULL; } return Status; } /** Get Bootguard svn info from Variable @param[out] Revocation Pointer to Arb SVN info data @retval EFI_SUCCESS Function succeeded. @return Other Get variable fail. **/ EFI_STATUS GetArbSvnSaveInfoFromVariable ( REVOCATION_VALUE *Revocation ) { EFI_STATUS Status; UINTN Size; Size = sizeof (REVOCATION_VALUE); Status = CommonGetVariable ( L"ArbSvnInfo", &gArbSvnInfoGuid, &Size, Revocation ); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_ERROR, "Failed to get ArbSvnInfo variable, Status = %r\n", Status)); } return Status; } /** Ihisi function to initialize all of Bootguard SVN relative information */ STATIC VOID ArbSvnSaveInfo ( ) { EFI_STATUS Status; REVOCATION_VALUE RevocationCurrent; BOOLEAN GetFromMe; REVOCATION_VALUE Revocation; BOOLEAN GetFromVariable; EFI_BOOT_MODE BootMode; BOOLEAN UpdateInfo; GetFromMe = FALSE; GetFromVariable = FALSE; UpdateInfo = FALSE; Revocation.ACMSVN = 0; Revocation.BPMSVN = 0; Revocation.KMSVN = 0; Revocation.KMID = 0; Revocation.BootGuardStatus = BootGuardBootStateLegacyBoot; // // Try to get the Revocation value from ME // Status = GetArbSvnSaveInfoFromME (&RevocationCurrent); if (!EFI_ERROR(Status)) { GetFromMe = TRUE; Revocation.ACMSVN = RevocationCurrent.ACMSVN; Revocation.BPMSVN = RevocationCurrent.BPMSVN; Revocation.KMSVN = RevocationCurrent.KMSVN; Revocation.KMID = RevocationCurrent.KMID; Revocation.BootGuardStatus = RevocationCurrent.BootGuardStatus; } BootMode = GetBootModeHob (); // // The content of NV storage for variable is not reliable in recovery boot mode. // if (BootMode != BOOT_IN_RECOVERY_MODE) { // // Try to get the Revocation value from ME // Status = GetArbSvnSaveInfoFromVariable (&Revocation); if (!EFI_ERROR(Status)) { GetFromVariable = TRUE; } if (GetFromMe == TRUE) { if (GetFromVariable == FALSE) { // // Prepare the Variable data // Revocation.ACMSVN = RevocationCurrent.ACMSVN; Revocation.BPMSVN = RevocationCurrent.BPMSVN; Revocation.KMSVN = RevocationCurrent.KMSVN; Revocation.KMID = RevocationCurrent.KMID; Revocation.BootGuardStatus = RevocationCurrent.BootGuardStatus; UpdateInfo = TRUE; } else { // // Update variable if necessary // if (Revocation.ACMSVN < RevocationCurrent.ACMSVN) { Revocation.ACMSVN = RevocationCurrent.ACMSVN; UpdateInfo = TRUE; } if (Revocation.BPMSVN < RevocationCurrent.BPMSVN) { Revocation.BPMSVN = RevocationCurrent.BPMSVN; UpdateInfo = TRUE; } if (Revocation.KMSVN < RevocationCurrent.KMSVN) { Revocation.KMSVN = RevocationCurrent.KMSVN; UpdateInfo = TRUE; } if (Revocation.KMID < RevocationCurrent.KMID) { Revocation.KMID = RevocationCurrent.KMID; UpdateInfo = TRUE; } if (Revocation.BootGuardStatus != RevocationCurrent.BootGuardStatus) { Revocation.BootGuardStatus = RevocationCurrent.BootGuardStatus; UpdateInfo = TRUE; } } } if (UpdateInfo == TRUE){ // // Save related SVN values into variable, so we can get these values in PEI or SMM. // Status = SetVariableToSensitiveVariable ( L"ArbSvnInfo", &gArbSvnInfoGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, sizeof (Revocation), &Revocation ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to set ArbSvnInfo variable, Status = %r\n", Status)); } } } if ((GetFromMe == TRUE) || (GetFromVariable == TRUE)) { // // Save BootMode Info to LockBox, so we can restore these values in SMM. // Status = SaveLockBox (&gArbSvnInfoGuid, &Revocation, sizeof (Revocation)); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "SaveLockBox BootMode %r\n", Status)); } } } /** Ihisi driver entry point to initialize all of IHISI relative services @param[in] ImageHandle The firmware allocated handle for the UEFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The operation completed successfully. @retval Others An unexpected error occurred. */ EFI_STATUS EFIAPI IhisiEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; // // Prepare Bootguard Arb Svn info for BIOS update // ArbSvnSaveInfo (); Status = gSmst->SmmLocateProtocol ( &gH2OIhisiProtocolGuid, NULL, (VOID **) &mH2OIhisi ); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); } if (mSmmFwBlockService == NULL) { Status = gSmst->SmmLocateProtocol ( &gEfiSmmFwBlockServiceProtocolGuid, NULL, (VOID **) &mSmmFwBlockService ); if (EFI_ERROR (Status)) { return Status; } } if (FeaturePcdGet (PcdH2OIhisiFbtsSupported)) { Status = FbtsInit (); if (EFI_ERROR (Status)) { return Status; } } if (FeaturePcdGet (PcdH2OIhisiFbts2Supported)) { Status = Fbts2Init (); if (EFI_ERROR (Status)) { return Status; } } Status = BiosGCIInit (); if (EFI_ERROR (Status)) { return Status; } // // Due to OEM can register their own IHISI command by locate H2OIhisi protocol, // Remove this OEM service. // // IhisiOemHook = IhisiChipsetHook; // // SEG Feature - Remove H2OUVE relevant source codes // // Status = H2oUveSmiServiceInit (); // if (EFI_ERROR (Status)) { // return Status; // } return EFI_SUCCESS; } /** Register IHISI sub function if SubFuncTable CmdNumber/AsciiFuncGuid define in PcdIhisiRegisterTable list. @param[out] SubFuncTable Pointer to ihisi register table. @param[out] TableCount SubFuncTable count @retval EFI_SUCCESS Function succeeded. @return Other Error occurred in this function. **/ EFI_STATUS RegisterIhisiSubFunction ( IHISI_REGISTER_TABLE *SubFuncTable, UINT16 TableCount ) { EFI_STATUS Status; UINT8 *PcdTable; UINT8 PcdPriority; UINT8 EndChar; UINTN Index; UINTN PcdCount; UINTN PcdMaxCount; UINTN SignatureSize; BOOLEAN PcdFoundRegistered; Status = EFI_SUCCESS; EndChar = 0; PcdFoundRegistered = FALSE; PcdTable = (UINT8 *)PcdGetPtr (PcdIhisiRegisterTable); PcdMaxCount = FixedPcdGetPtrSize (PcdIhisiRegisterTable) / sizeof (UINT8); if (!mPcdIhisiRegisterTableValid) { DEBUG ((DEBUG_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); // // Caculate 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 ((DEBUG_ERROR, "IHISI command :0x%X, priority : 0x%X, that already has a registered function\n", SubFuncTable[Index].CmdNumber, PcdPriority)); ASSERT (FALSE); } } } return Status; }