/** @file
Initialize TPM2 device and measure FVs before handing off control to DXE.
Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.
Copyright (c) 2017, Microsoft Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PERF_ID_TCG2_PEI 0x3080
typedef struct {
EFI_GUID *EventGuid;
EFI_TCG2_EVENT_LOG_FORMAT LogFormat;
} TCG2_EVENT_INFO_STRUCT;
TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = {
{ &gTcgEventEntryHobGuid, EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 },
{ &gTcgEvent2EntryHobGuid, EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 },
};
BOOLEAN mImageInMemory = FALSE;
EFI_PEI_FILE_HANDLE mFileHandle;
EFI_PEI_PPI_DESCRIPTOR mTpmInitializedPpiList = {
EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
&gPeiTpmInitializedPpiGuid,
NULL
};
EFI_PEI_PPI_DESCRIPTOR mTpmInitializationDonePpiList = {
EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
&gPeiTpmInitializationDonePpiGuid,
NULL
};
/**
Do a hash operation on a data buffer, extend a specific TPM PCR with the hash result,
and build a GUIDed HOB recording the event which will be passed to the DXE phase and
added into the Event Log.
@param[in] This Indicates the calling context
@param[in] Flags Bitmap providing additional information.
@param[in] HashData If BIT0 of Flags is 0, it is physical address of the
start of the data buffer to be hashed, extended, and logged.
If BIT0 of Flags is 1, it is physical address of the
start of the pre-hash data buffter to be extended, and logged.
The pre-hash data format is TPML_DIGEST_VALUES.
@param[in] HashDataLen The length, in bytes, of the buffer referenced by HashData.
@param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure.
@param[in] NewEventData Pointer to the new event data.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
@retval EFI_DEVICE_ERROR The command was unsuccessful.
**/
EFI_STATUS
EFIAPI
HashLogExtendEvent (
IN EDKII_TCG_PPI *This,
IN UINT64 Flags,
IN UINT8 *HashData,
IN UINTN HashDataLen,
IN TCG_PCR_EVENT_HDR *NewEventHdr,
IN UINT8 *NewEventData
);
EDKII_TCG_PPI mEdkiiTcgPpi = {
HashLogExtendEvent
};
EFI_PEI_PPI_DESCRIPTOR mTcgPpiList = {
EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
&gEdkiiTcgPpiGuid,
&mEdkiiTcgPpi
};
//
// Number of firmware blobs to grow by each time we run out of room
//
#define FIRMWARE_BLOB_GROWTH_STEP 4
EFI_PLATFORM_FIRMWARE_BLOB *mMeasuredBaseFvInfo;
UINT32 mMeasuredMaxBaseFvIndex = 0;
UINT32 mMeasuredBaseFvIndex = 0;
EFI_PLATFORM_FIRMWARE_BLOB *mMeasuredChildFvInfo;
UINT32 mMeasuredMaxChildFvIndex = 0;
UINT32 mMeasuredChildFvIndex = 0;
#pragma pack (1)
#define FV_HANDOFF_TABLE_DESC "Fv(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)"
typedef struct {
UINT8 BlobDescriptionSize;
UINT8 BlobDescription[sizeof (FV_HANDOFF_TABLE_DESC)];
EFI_PHYSICAL_ADDRESS BlobBase;
UINT64 BlobLength;
} FV_HANDOFF_TABLE_POINTERS2;
#pragma pack ()
/**
Measure and record the Firmware Volume Information once FvInfoPPI install.
@param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
@param[in] NotifyDescriptor Address of the notification descriptor data structure.
@param[in] Ppi Address of the PPI that was installed.
@retval EFI_SUCCESS The FV Info is measured and recorded to TPM.
@return Others Fail to measure FV.
**/
EFI_STATUS
EFIAPI
FirmwareVolumeInfoPpiNotifyCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
);
/**
Record all measured Firmware Volume Information into a Guid Hob
@param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
@param[in] NotifyDescriptor Address of the notification descriptor data structure.
@param[in] Ppi Address of the PPI that was installed.
@retval EFI_SUCCESS The FV Info is measured and recorded to TPM.
@return Others Fail to measure FV.
**/
EFI_STATUS
EFIAPI
EndofPeiSignalNotifyCallBack (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
);
EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList[] = {
{
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK,
&gEfiPeiFirmwareVolumeInfoPpiGuid,
FirmwareVolumeInfoPpiNotifyCallback
},
{
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK,
&gEfiPeiFirmwareVolumeInfo2PpiGuid,
FirmwareVolumeInfoPpiNotifyCallback
},
{
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiEndOfPeiSignalPpiGuid,
EndofPeiSignalNotifyCallBack
}
};
/**
Record all measured Firmware Volume Information into a Guid Hob
Guid Hob payload layout is
UINT32 *************************** FIRMWARE_BLOB number
EFI_PLATFORM_FIRMWARE_BLOB******** BLOB Array
@param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
@param[in] NotifyDescriptor Address of the notification descriptor data structure.
@param[in] Ppi Address of the PPI that was installed.
@retval EFI_SUCCESS The FV Info is measured and recorded to TPM.
@return Others Fail to measure FV.
**/
EFI_STATUS
EFIAPI
EndofPeiSignalNotifyCallBack (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
MEASURED_HOB_DATA *MeasuredHobData;
MeasuredHobData = NULL;
PERF_CALLBACK_BEGIN (&gEfiEndOfPeiSignalPpiGuid);
//
// Create a Guid hob to save all measured Fv
//
MeasuredHobData = BuildGuidHob (
&gMeasuredFvHobGuid,
sizeof (UINTN) + sizeof (EFI_PLATFORM_FIRMWARE_BLOB) * (mMeasuredBaseFvIndex + mMeasuredChildFvIndex)
);
if (MeasuredHobData != NULL) {
//
// Save measured FV info enty number
//
MeasuredHobData->Num = mMeasuredBaseFvIndex + mMeasuredChildFvIndex;
//
// Save measured base Fv info
//
CopyMem (MeasuredHobData->MeasuredFvBuf, mMeasuredBaseFvInfo, sizeof (EFI_PLATFORM_FIRMWARE_BLOB) * (mMeasuredBaseFvIndex));
//
// Save measured child Fv info
//
CopyMem (&MeasuredHobData->MeasuredFvBuf[mMeasuredBaseFvIndex], mMeasuredChildFvInfo, sizeof (EFI_PLATFORM_FIRMWARE_BLOB) * (mMeasuredChildFvIndex));
}
PERF_CALLBACK_END (&gEfiEndOfPeiSignalPpiGuid);
return EFI_SUCCESS;
}
/**
Make sure that the current PCR allocations, the TPM supported PCRs,
PcdTcg2HashAlgorithmBitmap and the PcdTpm2HashMask are all in agreement.
**/
VOID
SyncPcrAllocationsAndPcrMask (
VOID
)
{
EFI_STATUS Status;
EFI_TCG2_EVENT_ALGORITHM_BITMAP TpmHashAlgorithmBitmap;
EFI_TCG2_EVENT_ALGORITHM_BITMAP BiosHashAlgorithmBitmap;
UINT32 TpmActivePcrBanks;
UINT32 NewTpmActivePcrBanks;
UINT32 Tpm2PcrMask;
UINT32 NewTpm2PcrMask;
DEBUG ((DEBUG_ERROR, "SyncPcrAllocationsAndPcrMask!\n"));
//
// Determine the current TPM support and the Platform PCR mask.
//
Status = Tpm2GetCapabilitySupportedAndActivePcrs (&TpmHashAlgorithmBitmap, &TpmActivePcrBanks);
ASSERT_EFI_ERROR (Status);
DEBUG ((DEBUG_INFO, "Tpm2GetCapabilitySupportedAndActivePcrs - TpmHashAlgorithmBitmap: 0x%08x\n", TpmHashAlgorithmBitmap));
DEBUG ((DEBUG_INFO, "Tpm2GetCapabilitySupportedAndActivePcrs - TpmActivePcrBanks 0x%08x\n", TpmActivePcrBanks));
Tpm2PcrMask = PcdGet32 (PcdTpm2HashMask);
if (Tpm2PcrMask == 0) {
//
// If PcdTpm2HashMask is zero, use ActivePcr setting.
// Only when PcdTpm2HashMask is initialized to 0, will it be updated to current Active Pcrs.
//
PcdSet32S (PcdTpm2HashMask, TpmActivePcrBanks);
Tpm2PcrMask = TpmActivePcrBanks;
}
DEBUG ((DEBUG_INFO, "Tpm2PcrMask 0x%08x\n", Tpm2PcrMask));
//
// The Active PCRs in the TPM need to be a strict subset of the hashing algorithms supported by BIOS.
//
// * Find the intersection of Pcd support and TPM active PCRs. If banks are missing from the TPM support
// that are in the PCD, update the PCD.
// * Find intersection of TPM Active PCRs and BIOS supported algorithms. If there are active PCR banks
// that are not supported by the platform, update the TPM allocations and reboot.
// Note: When the HashLibBaseCryptoRouter solution is used, the hash algorithm support from BIOS is reported
// by Tcg2HashAlgorithmBitmap, which is populated by HashLib instances at runtime.
BiosHashAlgorithmBitmap = PcdGet32 (PcdTcg2HashAlgorithmBitmap);
DEBUG ((DEBUG_INFO, "Tcg2HashAlgorithmBitmap: 0x%08x\n", BiosHashAlgorithmBitmap));
if (((TpmActivePcrBanks & Tpm2PcrMask) != TpmActivePcrBanks) ||
((TpmActivePcrBanks & BiosHashAlgorithmBitmap) != TpmActivePcrBanks))
{
DEBUG ((DEBUG_INFO, "TpmActivePcrBanks & Tpm2PcrMask = 0x%08x\n", (TpmActivePcrBanks & Tpm2PcrMask)));
DEBUG ((DEBUG_INFO, "TpmActivePcrBanks & BiosHashAlgorithmBitmap = 0x%08x\n", (TpmActivePcrBanks & BiosHashAlgorithmBitmap)));
NewTpmActivePcrBanks = TpmActivePcrBanks;
NewTpmActivePcrBanks &= Tpm2PcrMask;
NewTpmActivePcrBanks &= BiosHashAlgorithmBitmap;
DEBUG ((DEBUG_INFO, "NewTpmActivePcrBanks 0x%08x\n", NewTpmActivePcrBanks));
DEBUG ((DEBUG_INFO, "%a - Reallocating PCR banks from 0x%X to 0x%X.\n", __func__, TpmActivePcrBanks, NewTpmActivePcrBanks));
if (NewTpmActivePcrBanks == 0) {
DEBUG ((DEBUG_ERROR, "%a - No viable PCRs active! Please set a less restrictive value for PcdTpm2HashMask!\n", __func__));
ASSERT (FALSE);
} else {
DEBUG ((DEBUG_ERROR, "Tpm2PcrAllocateBanks (TpmHashAlgorithmBitmap: 0x%08x, NewTpmActivePcrBanks: 0x%08x)\n", TpmHashAlgorithmBitmap, NewTpmActivePcrBanks));
Status = Tpm2PcrAllocateBanks (NULL, (UINT32)TpmHashAlgorithmBitmap, NewTpmActivePcrBanks);
if (EFI_ERROR (Status)) {
//
// We can't do much here, but we hope that this doesn't happen.
//
DEBUG ((DEBUG_ERROR, "%a - Failed to reallocate PCRs!\n", __func__));
ASSERT_EFI_ERROR (Status);
}
//
// Need reset system, since we just called Tpm2PcrAllocateBanks().
//
ResetCold ();
}
}
//
// If there are any PCRs that claim support in the Platform mask that are
// not supported by the TPM, update the mask.
//
if ((Tpm2PcrMask & TpmHashAlgorithmBitmap) != Tpm2PcrMask) {
NewTpm2PcrMask = Tpm2PcrMask & TpmHashAlgorithmBitmap;
DEBUG ((DEBUG_INFO, "%a - Updating PcdTpm2HashMask from 0x%X to 0x%X.\n", __func__, Tpm2PcrMask, NewTpm2PcrMask));
if (NewTpm2PcrMask == 0) {
DEBUG ((DEBUG_ERROR, "%a - No viable PCRs supported! Please set a less restrictive value for PcdTpm2HashMask!\n", __func__));
ASSERT (FALSE);
}
Status = PcdSet32S (PcdTpm2HashMask, NewTpm2PcrMask);
DEBUG ((DEBUG_ERROR, "Set PcdTpm2Hash Mask to 0x%08x\n", NewTpm2PcrMask));
ASSERT_EFI_ERROR (Status);
}
}
/**
Add a new entry to the Event Log.
@param[in] DigestList A list of digest.
@param[in,out] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure.
@param[in] NewEventData Pointer to the new event data.
@retval EFI_SUCCESS The new event log entry was added.
@retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
**/
EFI_STATUS
LogHashEvent (
IN TPML_DIGEST_VALUES *DigestList,
IN OUT TCG_PCR_EVENT_HDR *NewEventHdr,
IN UINT8 *NewEventData
)
{
VOID *HobData;
EFI_STATUS Status;
UINTN Index;
EFI_STATUS RetStatus;
UINT32 SupportedEventLogs;
TCG_PCR_EVENT2 *TcgPcrEvent2;
UINT8 *DigestBuffer;
SupportedEventLogs = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 | EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
RetStatus = EFI_SUCCESS;
for (Index = 0; Index < sizeof (mTcg2EventInfo)/sizeof (mTcg2EventInfo[0]); Index++) {
if ((SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) {
DEBUG ((DEBUG_INFO, " LogFormat - 0x%08x\n", mTcg2EventInfo[Index].LogFormat));
switch (mTcg2EventInfo[Index].LogFormat) {
case EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2:
Status = GetDigestFromDigestList (TPM_ALG_SHA1, DigestList, &NewEventHdr->Digest);
if (!EFI_ERROR (Status)) {
HobData = BuildGuidHob (
&gTcgEventEntryHobGuid,
sizeof (*NewEventHdr) + NewEventHdr->EventSize
);
if (HobData == NULL) {
RetStatus = EFI_OUT_OF_RESOURCES;
break;
}
CopyMem (HobData, NewEventHdr, sizeof (*NewEventHdr));
HobData = (VOID *)((UINT8 *)HobData + sizeof (*NewEventHdr));
CopyMem (HobData, NewEventData, NewEventHdr->EventSize);
}
break;
case EFI_TCG2_EVENT_LOG_FORMAT_TCG_2:
//
// Use GetDigestListSize (DigestList) in the GUID HOB DataLength calculation
// to reserve enough buffer to hold TPML_DIGEST_VALUES compact binary.
//
HobData = BuildGuidHob (
&gTcgEvent2EntryHobGuid,
sizeof (TcgPcrEvent2->PCRIndex) + sizeof (TcgPcrEvent2->EventType) + GetDigestListSize (DigestList) + sizeof (TcgPcrEvent2->EventSize) + NewEventHdr->EventSize
);
if (HobData == NULL) {
RetStatus = EFI_OUT_OF_RESOURCES;
break;
}
TcgPcrEvent2 = HobData;
TcgPcrEvent2->PCRIndex = NewEventHdr->PCRIndex;
TcgPcrEvent2->EventType = NewEventHdr->EventType;
DigestBuffer = (UINT8 *)&TcgPcrEvent2->Digest;
DigestBuffer = CopyDigestListToBuffer (DigestBuffer, DigestList, PcdGet32 (PcdTpm2HashMask));
CopyMem (DigestBuffer, &NewEventHdr->EventSize, sizeof (TcgPcrEvent2->EventSize));
DigestBuffer = DigestBuffer + sizeof (TcgPcrEvent2->EventSize);
CopyMem (DigestBuffer, NewEventData, NewEventHdr->EventSize);
break;
}
}
}
return RetStatus;
}
/**
Do a hash operation on a data buffer, extend a specific TPM PCR with the hash result,
and build a GUIDed HOB recording the event which will be passed to the DXE phase and
added into the Event Log.
@param[in] This Indicates the calling context
@param[in] Flags Bitmap providing additional information.
@param[in] HashData If BIT0 of Flags is 0, it is physical address of the
start of the data buffer to be hashed, extended, and logged.
If BIT0 of Flags is 1, it is physical address of the
start of the pre-hash data buffter to be extended, and logged.
The pre-hash data format is TPML_DIGEST_VALUES.
@param[in] HashDataLen The length, in bytes, of the buffer referenced by HashData.
@param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure.
@param[in] NewEventData Pointer to the new event data.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
@retval EFI_DEVICE_ERROR The command was unsuccessful.
**/
EFI_STATUS
EFIAPI
HashLogExtendEvent (
IN EDKII_TCG_PPI *This,
IN UINT64 Flags,
IN UINT8 *HashData,
IN UINTN HashDataLen,
IN TCG_PCR_EVENT_HDR *NewEventHdr,
IN UINT8 *NewEventData
)
{
EFI_STATUS Status;
TPML_DIGEST_VALUES DigestList;
if (GetFirstGuidHob (&gTpmErrorHobGuid) != NULL) {
return EFI_DEVICE_ERROR;
}
if (((Flags & EDKII_TCG_PRE_HASH) != 0) || ((Flags & EDKII_TCG_PRE_HASH_LOG_ONLY) != 0)) {
ZeroMem (&DigestList, sizeof (DigestList));
CopyMem (&DigestList, HashData, sizeof (DigestList));
Status = EFI_SUCCESS;
if ((Flags & EDKII_TCG_PRE_HASH) != 0 ) {
Status = Tpm2PcrExtend (
NewEventHdr->PCRIndex,
&DigestList
);
}
} else {
Status = HashAndExtend (
NewEventHdr->PCRIndex,
HashData,
HashDataLen,
&DigestList
);
}
if (!EFI_ERROR (Status)) {
Status = LogHashEvent (&DigestList, NewEventHdr, NewEventData);
}
if (Status == EFI_DEVICE_ERROR) {
DEBUG ((DEBUG_ERROR, "HashLogExtendEvent - %r. Disable TPM.\n", Status));
BuildGuidHob (&gTpmErrorHobGuid, 0);
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(PcdGet32 (PcdStatusCodeSubClassTpmDevice) | EFI_P_EC_INTERFACE_ERROR)
);
}
return Status;
}
/**
Measure CRTM version.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
@retval EFI_DEVICE_ERROR The command was unsuccessful.
**/
EFI_STATUS
MeasureCRTMVersion (
VOID
)
{
TCG_PCR_EVENT_HDR TcgEventHdr;
//
// Use FirmwareVersion string to represent CRTM version.
// OEMs should get real CRTM version string and measure it.
//
TcgEventHdr.PCRIndex = 0;
TcgEventHdr.EventType = EV_S_CRTM_VERSION;
TcgEventHdr.EventSize = (UINT32)StrSize ((CHAR16 *)PcdGetPtr (PcdFirmwareVersionString));
return HashLogExtendEvent (
&mEdkiiTcgPpi,
0,
(UINT8 *)PcdGetPtr (PcdFirmwareVersionString),
TcgEventHdr.EventSize,
&TcgEventHdr,
(UINT8 *)PcdGetPtr (PcdFirmwareVersionString)
);
}
/**
Get the FvName from the FV header.
Causion: The FV is untrusted input.
@param[in] FvBase Base address of FV image.
@param[in] FvLength Length of FV image.
@return FvName pointer
@retval NULL FvName is NOT found
**/
VOID *
GetFvName (
IN EFI_PHYSICAL_ADDRESS FvBase,
IN UINT64 FvLength
)
{
EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader;
if (FvBase >= MAX_ADDRESS) {
return NULL;
}
if (FvLength >= MAX_ADDRESS - FvBase) {
return NULL;
}
if (FvLength < sizeof (EFI_FIRMWARE_VOLUME_HEADER)) {
return NULL;
}
FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvBase;
if (FvHeader->ExtHeaderOffset < sizeof (EFI_FIRMWARE_VOLUME_HEADER)) {
return NULL;
}
if (FvHeader->ExtHeaderOffset + sizeof (EFI_FIRMWARE_VOLUME_EXT_HEADER) > FvLength) {
return NULL;
}
FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)(UINTN)(FvBase + FvHeader->ExtHeaderOffset);
return &FvExtHeader->FvName;
}
/**
Measure FV image.
Add it into the measured FV list after the FV is measured successfully.
@param[in] FvBase Base address of FV image.
@param[in] FvLength Length of FV image.
@retval EFI_SUCCESS Fv image is measured successfully
or it has been already measured.
@retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
@retval EFI_DEVICE_ERROR The command was unsuccessful.
**/
EFI_STATUS
MeasureFvImage (
IN EFI_PHYSICAL_ADDRESS FvBase,
IN UINT64 FvLength
)
{
UINT32 Index;
EFI_STATUS Status;
EFI_PLATFORM_FIRMWARE_BLOB FvBlob;
FV_HANDOFF_TABLE_POINTERS2 FvBlob2;
VOID *EventData;
VOID *FvName;
TCG_PCR_EVENT_HDR TcgEventHdr;
UINT32 Instance;
UINT32 Tpm2HashMask;
TPML_DIGEST_VALUES DigestList;
UINT32 DigestCount;
EFI_PEI_FIRMWARE_VOLUME_INFO_MEASUREMENT_EXCLUDED_PPI *MeasurementExcludedFvPpi;
EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI *PrehashedFvPpi;
HASH_INFO *PreHashInfo;
UINT32 HashAlgoMask;
EFI_PHYSICAL_ADDRESS FvOrgBase;
EFI_PHYSICAL_ADDRESS FvDataBase;
EFI_PEI_HOB_POINTERS Hob;
EDKII_MIGRATED_FV_INFO *MigratedFvInfo;
//
// Check Excluded FV list
//
Instance = 0;
do {
Status = PeiServicesLocatePpi (
&gEfiPeiFirmwareVolumeInfoMeasurementExcludedPpiGuid,
Instance,
NULL,
(VOID **)&MeasurementExcludedFvPpi
);
if (!EFI_ERROR (Status)) {
for (Index = 0; Index < MeasurementExcludedFvPpi->Count; Index++) {
if ( (MeasurementExcludedFvPpi->Fv[Index].FvBase == FvBase)
&& (MeasurementExcludedFvPpi->Fv[Index].FvLength == FvLength))
{
DEBUG ((DEBUG_INFO, "The FV which is excluded by Tcg2Pei starts at: 0x%x\n", FvBase));
DEBUG ((DEBUG_INFO, "The FV which is excluded by Tcg2Pei has the size: 0x%x\n", FvLength));
return EFI_SUCCESS;
}
}
Instance++;
}
} while (!EFI_ERROR (Status));
//
// Check measured FV list
//
for (Index = 0; Index < mMeasuredBaseFvIndex; Index++) {
if ((mMeasuredBaseFvInfo[Index].BlobBase == FvBase) && (mMeasuredBaseFvInfo[Index].BlobLength == FvLength)) {
DEBUG ((DEBUG_INFO, "The FV which is already measured by Tcg2Pei starts at: 0x%x\n", FvBase));
DEBUG ((DEBUG_INFO, "The FV which is already measured by Tcg2Pei has the size: 0x%x\n", FvLength));
return EFI_SUCCESS;
}
}
//
// Check pre-hashed FV list
//
Instance = 0;
Tpm2HashMask = PcdGet32 (PcdTpm2HashMask);
do {
Status = PeiServicesLocatePpi (
&gEdkiiPeiFirmwareVolumeInfoPrehashedFvPpiGuid,
Instance,
NULL,
(VOID **)&PrehashedFvPpi
);
if (!EFI_ERROR (Status) && (PrehashedFvPpi->FvBase == FvBase) && (PrehashedFvPpi->FvLength == FvLength)) {
ZeroMem (&DigestList, sizeof (TPML_DIGEST_VALUES));
//
// The FV is prehashed, check against TPM hash mask
//
PreHashInfo = (HASH_INFO *)(PrehashedFvPpi + 1);
for (Index = 0, DigestCount = 0; Index < PrehashedFvPpi->Count; Index++) {
DEBUG ((DEBUG_INFO, "Hash Algo ID in PrehashedFvPpi=0x%x\n", PreHashInfo->HashAlgoId));
HashAlgoMask = GetHashMaskFromAlgo (PreHashInfo->HashAlgoId);
if ((Tpm2HashMask & HashAlgoMask) != 0 ) {
//
// Hash is required, copy it to DigestList
//
WriteUnaligned16 (&(DigestList.digests[DigestCount].hashAlg), PreHashInfo->HashAlgoId);
CopyMem (
&DigestList.digests[DigestCount].digest,
PreHashInfo + 1,
PreHashInfo->HashSize
);
DigestCount++;
//
// Clean the corresponding Hash Algo mask bit
//
Tpm2HashMask &= ~HashAlgoMask;
}
PreHashInfo = (HASH_INFO *)((UINT8 *)(PreHashInfo + 1) + PreHashInfo->HashSize);
}
WriteUnaligned32 (&DigestList.count, DigestCount);
break;
}
Instance++;
} while (!EFI_ERROR (Status));
//
// Search the matched migration FV info
//
FvOrgBase = FvBase;
FvDataBase = FvBase;
Hob.Raw = GetFirstGuidHob (&gEdkiiMigratedFvInfoGuid);
while (Hob.Raw != NULL) {
MigratedFvInfo = GET_GUID_HOB_DATA (Hob);
if ((MigratedFvInfo->FvNewBase == (UINT32)FvBase) && (MigratedFvInfo->FvLength == (UINT32)FvLength)) {
//
// Found the migrated FV info
//
FvOrgBase = (EFI_PHYSICAL_ADDRESS)(UINTN)MigratedFvInfo->FvOrgBase;
FvDataBase = (EFI_PHYSICAL_ADDRESS)(UINTN)MigratedFvInfo->FvDataBase;
break;
}
Hob.Raw = GET_NEXT_HOB (Hob);
Hob.Raw = GetNextGuidHob (&gEdkiiMigratedFvInfoGuid, Hob.Raw);
}
//
// Init the log event for FV measurement
//
if (PcdGet32 (PcdTcgPfpMeasurementRevision) >= TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2_REV_105) {
FvBlob2.BlobDescriptionSize = sizeof (FvBlob2.BlobDescription);
CopyMem (FvBlob2.BlobDescription, FV_HANDOFF_TABLE_DESC, sizeof (FvBlob2.BlobDescription));
FvName = GetFvName (FvBase, FvLength);
if (FvName != NULL) {
AsciiSPrint ((CHAR8 *)FvBlob2.BlobDescription, sizeof (FvBlob2.BlobDescription), "Fv(%g)", FvName);
}
FvBlob2.BlobBase = FvOrgBase;
FvBlob2.BlobLength = FvLength;
TcgEventHdr.PCRIndex = 0;
TcgEventHdr.EventType = EV_EFI_PLATFORM_FIRMWARE_BLOB2;
TcgEventHdr.EventSize = sizeof (FvBlob2);
EventData = &FvBlob2;
} else {
FvBlob.BlobBase = FvOrgBase;
FvBlob.BlobLength = FvLength;
TcgEventHdr.PCRIndex = 0;
TcgEventHdr.EventType = EV_EFI_PLATFORM_FIRMWARE_BLOB;
TcgEventHdr.EventSize = sizeof (FvBlob);
EventData = &FvBlob;
}
if (Tpm2HashMask == 0) {
//
// FV pre-hash algos comply with current TPM hash requirement
// Skip hashing step in measure, only extend DigestList to PCR and log event
//
Status = HashLogExtendEvent (
&mEdkiiTcgPpi,
EDKII_TCG_PRE_HASH,
(UINT8 *)&DigestList, // HashData
(UINTN)sizeof (DigestList), // HashDataLen
&TcgEventHdr, // EventHdr
EventData // EventData
);
DEBUG ((DEBUG_INFO, "The pre-hashed FV which is extended & logged by Tcg2Pei starts at: 0x%x\n", FvBase));
DEBUG ((DEBUG_INFO, "The pre-hashed FV which is extended & logged by Tcg2Pei has the size: 0x%x\n", FvLength));
} else {
//
// Hash the FV, extend digest to the TPM and log TCG event
//
Status = HashLogExtendEvent (
&mEdkiiTcgPpi,
0,
(UINT8 *)(UINTN)FvDataBase, // HashData
(UINTN)FvLength, // HashDataLen
&TcgEventHdr, // EventHdr
EventData // EventData
);
DEBUG ((DEBUG_INFO, "The FV which is measured by Tcg2Pei starts at: 0x%x\n", FvBase));
DEBUG ((DEBUG_INFO, "The FV which is measured by Tcg2Pei has the size: 0x%x\n", FvLength));
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "The FV which failed to be measured starts at: 0x%x\n", FvBase));
return Status;
}
//
// Add new FV into the measured FV list.
//
if (mMeasuredBaseFvIndex >= mMeasuredMaxBaseFvIndex) {
mMeasuredBaseFvInfo = ReallocatePool (
sizeof (EFI_PLATFORM_FIRMWARE_BLOB) * mMeasuredMaxBaseFvIndex,
sizeof (EFI_PLATFORM_FIRMWARE_BLOB) * (mMeasuredMaxBaseFvIndex + FIRMWARE_BLOB_GROWTH_STEP),
mMeasuredBaseFvInfo
);
ASSERT (mMeasuredBaseFvInfo != NULL);
mMeasuredMaxBaseFvIndex = mMeasuredMaxBaseFvIndex + FIRMWARE_BLOB_GROWTH_STEP;
}
mMeasuredBaseFvInfo[mMeasuredBaseFvIndex].BlobBase = FvBase;
mMeasuredBaseFvInfo[mMeasuredBaseFvIndex].BlobLength = FvLength;
mMeasuredBaseFvIndex++;
return Status;
}
/**
Measure main BIOS.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
@retval EFI_DEVICE_ERROR The command was unsuccessful.
**/
EFI_STATUS
MeasureMainBios (
VOID
)
{
EFI_STATUS Status;
EFI_PEI_FV_HANDLE VolumeHandle;
EFI_FV_INFO VolumeInfo;
EFI_PEI_FIRMWARE_VOLUME_PPI *FvPpi;
PERF_START_EX (mFileHandle, "EventRec", "Tcg2Pei", 0, PERF_ID_TCG2_PEI);
//
// Only measure BFV at the very beginning. Other parts of Static Core Root of
// Trust for Measurement(S-CRTM) will be measured later on FvInfoNotify.
// BFV is processed without installing FV Info Ppi. Other FVs either inside BFV or
// reported by platform will be installed with Fv Info Ppi
// This firmware volume measure policy can be modified/enhanced by special
// platform for special CRTM TPM measuring.
//
Status = PeiServicesFfsFindNextVolume (0, &VolumeHandle);
ASSERT_EFI_ERROR (Status);
//
// Measure and record the firmware volume that is dispatched by PeiCore
//
Status = PeiServicesFfsGetVolumeInfo (VolumeHandle, &VolumeInfo);
ASSERT_EFI_ERROR (Status);
//
// Locate the corresponding FV_PPI according to founded FV's format guid
//
Status = PeiServicesLocatePpi (
&VolumeInfo.FvFormat,
0,
NULL,
(VOID **)&FvPpi
);
ASSERT_EFI_ERROR (Status);
Status = MeasureFvImage ((EFI_PHYSICAL_ADDRESS)(UINTN)VolumeInfo.FvStart, VolumeInfo.FvSize);
PERF_END_EX (mFileHandle, "EventRec", "Tcg2Pei", 0, PERF_ID_TCG2_PEI + 1);
return Status;
}
/**
Measure and record the Firmware Volume Information once FvInfoPPI install.
@param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
@param[in] NotifyDescriptor Address of the notification descriptor data structure.
@param[in] Ppi Address of the PPI that was installed.
@retval EFI_SUCCESS The FV Info is measured and recorded to TPM.
@return Others Fail to measure FV.
**/
EFI_STATUS
EFIAPI
FirmwareVolumeInfoPpiNotifyCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
EFI_PEI_FIRMWARE_VOLUME_INFO_PPI *Fv;
EFI_STATUS Status;
EFI_PEI_FIRMWARE_VOLUME_PPI *FvPpi;
UINTN Index;
Fv = (EFI_PEI_FIRMWARE_VOLUME_INFO_PPI *)Ppi;
//
// The PEI Core can not dispatch or load files from memory mapped FVs that do not support FvPpi.
//
Status = PeiServicesLocatePpi (
&Fv->FvFormat,
0,
NULL,
(VOID **)&FvPpi
);
if (EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
//
// This is an FV from an FFS file, and the parent FV must have already been measured,
// No need to measure twice, so just record the FV and return
//
if ((Fv->ParentFvName != NULL) || (Fv->ParentFileName != NULL)) {
if (mMeasuredChildFvIndex >= mMeasuredMaxChildFvIndex) {
mMeasuredChildFvInfo = ReallocatePool (
sizeof (EFI_PLATFORM_FIRMWARE_BLOB) * mMeasuredMaxChildFvIndex,
sizeof (EFI_PLATFORM_FIRMWARE_BLOB) * (mMeasuredMaxChildFvIndex + FIRMWARE_BLOB_GROWTH_STEP),
mMeasuredChildFvInfo
);
ASSERT (mMeasuredChildFvInfo != NULL);
mMeasuredMaxChildFvIndex = mMeasuredMaxChildFvIndex + FIRMWARE_BLOB_GROWTH_STEP;
}
//
// Check whether FV is in the measured child FV list.
//
for (Index = 0; Index < mMeasuredChildFvIndex; Index++) {
if (mMeasuredChildFvInfo[Index].BlobBase == (EFI_PHYSICAL_ADDRESS)(UINTN)Fv->FvInfo) {
return EFI_SUCCESS;
}
}
mMeasuredChildFvInfo[mMeasuredChildFvIndex].BlobBase = (EFI_PHYSICAL_ADDRESS)(UINTN)Fv->FvInfo;
mMeasuredChildFvInfo[mMeasuredChildFvIndex].BlobLength = Fv->FvInfoSize;
mMeasuredChildFvIndex++;
return EFI_SUCCESS;
}
return MeasureFvImage ((EFI_PHYSICAL_ADDRESS)(UINTN)Fv->FvInfo, Fv->FvInfoSize);
}
/**
Do measurement after memory is ready.
@param[in] PeiServices Describes the list of possible PEI Services.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
@retval EFI_DEVICE_ERROR The command was unsuccessful.
**/
EFI_STATUS
PeimEntryMP (
IN EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
//
// install Tcg Services
//
Status = PeiServicesInstallPpi (&mTcgPpiList);
ASSERT_EFI_ERROR (Status);
if (PcdGet8 (PcdTpm2ScrtmPolicy) == 1) {
Status = MeasureCRTMVersion ();
}
Status = MeasureMainBios ();
if (EFI_ERROR (Status)) {
return Status;
}
//
// Post callbacks:
// for the FvInfoPpi services to measure and record
// the additional Fvs to TPM
//
Status = PeiServicesNotifyPpi (&mNotifyList[0]);
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
Measure and log Separator event with error, and extend the measurement result into a specific PCR.
@param[in] PCRIndex PCR index.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_DEVICE_ERROR The operation was unsuccessful.
**/
EFI_STATUS
MeasureSeparatorEventWithError (
IN TPM_PCRINDEX PCRIndex
)
{
TCG_PCR_EVENT_HDR TcgEvent;
UINT32 EventData;
//
// Use EventData 0x1 to indicate there is error.
//
EventData = 0x1;
TcgEvent.PCRIndex = PCRIndex;
TcgEvent.EventType = EV_SEPARATOR;
TcgEvent.EventSize = (UINT32)sizeof (EventData);
return HashLogExtendEvent (&mEdkiiTcgPpi, 0, (UINT8 *)&EventData, TcgEvent.EventSize, &TcgEvent, (UINT8 *)&EventData);
}
/**
Entry point of this module.
@param[in] FileHandle Handle of the file being invoked.
@param[in] PeiServices Describes the list of possible PEI Services.
@return Status.
**/
EFI_STATUS
EFIAPI
PeimEntryMA (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
EFI_STATUS Status2;
EFI_BOOT_MODE BootMode;
TPM_PCRINDEX PcrIndex;
BOOLEAN S3ErrorReport;
if (CompareGuid (PcdGetPtr (PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceNoneGuid) ||
CompareGuid (PcdGetPtr (PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceTpm12Guid))
{
DEBUG ((DEBUG_INFO, "No TPM2 instance required!\n"));
return EFI_UNSUPPORTED;
}
if (GetFirstGuidHob (&gTpmErrorHobGuid) != NULL) {
DEBUG ((DEBUG_ERROR, "TPM2 error!\n"));
return EFI_DEVICE_ERROR;
}
Status = PeiServicesGetBootMode (&BootMode);
ASSERT_EFI_ERROR (Status);
//
// In S3 path, skip shadow logic. no measurement is required
//
if (BootMode != BOOT_ON_S3_RESUME) {
Status = (**PeiServices).RegisterForShadow (FileHandle);
if (Status == EFI_ALREADY_STARTED) {
mImageInMemory = TRUE;
mFileHandle = FileHandle;
} else if (Status == EFI_NOT_FOUND) {
ASSERT_EFI_ERROR (Status);
}
}
if (!mImageInMemory) {
//
// Initialize TPM device
//
Status = Tpm2RequestUseTpm ();
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "TPM2 not detected!\n"));
goto Done;
}
S3ErrorReport = FALSE;
if (PcdGet8 (PcdTpm2InitializationPolicy) == 1) {
if (BootMode == BOOT_ON_S3_RESUME) {
Status = Tpm2Startup (TPM_SU_STATE);
if (EFI_ERROR (Status)) {
Status = Tpm2Startup (TPM_SU_CLEAR);
if (!EFI_ERROR (Status)) {
S3ErrorReport = TRUE;
}
}
} else {
Status = Tpm2Startup (TPM_SU_CLEAR);
}
if (EFI_ERROR (Status)) {
goto Done;
}
}
//
// Update Tpm2HashMask according to PCR bank.
//
SyncPcrAllocationsAndPcrMask ();
if (S3ErrorReport) {
//
// The system firmware that resumes from S3 MUST deal with a
// TPM2_Startup error appropriately.
// For example, issue a TPM2_Startup(TPM_SU_CLEAR) command and
// configuring the device securely by taking actions like extending a
// separator with an error digest (0x01) into PCRs 0 through 7.
//
for (PcrIndex = 0; PcrIndex < 8; PcrIndex++) {
Status = MeasureSeparatorEventWithError (PcrIndex);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Separator Event with Error not Measured. Error!\n"));
}
}
}
//
// TpmSelfTest is optional on S3 path, skip it to save S3 time
//
if (BootMode != BOOT_ON_S3_RESUME) {
if (PcdGet8 (PcdTpm2SelfTestPolicy) == 1) {
Status = Tpm2SelfTest (NO);
if (EFI_ERROR (Status)) {
goto Done;
}
}
}
DEBUG_CODE_BEGIN ();
//
// Peek into TPM PCR 00 before any BIOS measurement.
//
Tpm2PcrReadForActiveBank (00, NULL);
DEBUG_CODE_END ();
//
// Only install TpmInitializedPpi on success
//
Status = PeiServicesInstallPpi (&mTpmInitializedPpiList);
ASSERT_EFI_ERROR (Status);
}
if (mImageInMemory) {
Status = PeimEntryMP ((EFI_PEI_SERVICES **)PeiServices);
return Status;
}
Done:
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "TPM2 error! Build Hob\n"));
BuildGuidHob (&gTpmErrorHobGuid, 0);
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(PcdGet32 (PcdStatusCodeSubClassTpmDevice) | EFI_P_EC_INTERFACE_ERROR)
);
}
//
// Always install TpmInitializationDonePpi no matter success or fail.
// Other driver can know TPM initialization state by TpmInitializedPpi.
//
Status2 = PeiServicesInstallPpi (&mTpmInitializationDonePpiList);
ASSERT_EFI_ERROR (Status2);
return Status;
}