/** @file
Initialize TPM2 device and measure FVs before handing off control to DXE.
;******************************************************************************
;* Copyright (c) 2015 - 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.
;*
;******************************************************************************
*/
/**
Copyright (c) 2015 - 2018, 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
#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
};
//
// 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 ()
#define DMA_PROTECTION_DISABLED_EVENT_STRING "DMA Protection Disabled"
/**
Measure DMA protection policy, and extend the measurement result into a specific PCR.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_DEVICE_ERROR The operation was unsuccessful.
**/
EFI_STATUS
MeasureDmaProtectionDisabled (
VOID
);
/**
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
);
/**
Clear memory below 1MB if MOR is set.
@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 Operation completed successfully.
@retval Others Operation failed.
**/
STATIC
EFI_STATUS
EFIAPI
MemoryOverwriteCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
);
/**
Process TPM initialization once PEI_TPM2_INTERFACE_PPI installed.
@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 Operation completed successfully.
@retval Others Operation failed.
**/
STATIC
EFI_STATUS
EFIAPI
Tpm2InterfacePpiNotifyCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
);
/**
Reset PPI callback.
@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 Operation completed successfully.
@retval Others Operation failed.
**/
EFI_STATUS
EFIAPI
ResetSystemCallback (
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
}
};
EFI_PEI_FIRMWARE_VOLUME_INFO_MEASUREMENT_EXCLUDED_PPI *mMeasurementExcludedFvPpi;
STATIC EFI_PEI_NOTIFY_DESCRIPTOR mMorCallbackDescriptor =
{
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiEndOfPeiSignalPpiGuid,
MemoryOverwriteCallback
};
STATIC EFI_PEI_NOTIFY_DESCRIPTOR mTpm2InterfacePpiNotifyDescriptor =
{
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gPeiTpm2InterfacePpiGuid,
Tpm2InterfacePpiNotifyCallback
};
STATIC EFI_PEI_NOTIFY_DESCRIPTOR mResetCallbackDescriptor =
{
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiPeiResetPpiGuid,
ResetSystemCallback
};
/**
Reset PPI callback.
@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 Operation completed successfully.
@retval Others Operation failed.
**/
EFI_STATUS
EFIAPI
ResetSystemCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
EFI_STATUS Status;
EFI_PEI_RESET_PPI *ResetPpi;
DEBUG ((DEBUG_INFO, "[TPM2] ResetsystemCallback\n"));
Status = PeiServicesLocatePpi (
&gEfiPeiResetPpiGuid,
0,
NULL,
(VOID **)&ResetPpi
);
if (!EFI_ERROR (Status)) {
return ResetPpi->ResetSystem ((CONST EFI_PEI_SERVICES **)PeiServices);
}
return Status;
}
/**
Get TPM property.
This funtion get the value of TPM_PT_PERMANENT got from TPM2_GetCapability.
@param[out] TpmOwnerProperty A value of TRUE indicates that the TPM has successfully installed an owner.
@retval EFI_SUCCESS Operation completed successfully.
@retval Others Operation failed.
**/
STATIC
EFI_STATUS
Tpm2GetProperty (
OUT BOOLEAN *TpmProperty
)
{
TPMS_CAPABILITY_DATA TpmCap;
TPMI_YES_NO MoreData;
EFI_STATUS Status;
UINT32 Permanent;
Status = Tpm2GetCapability (
TPM_CAP_TPM_PROPERTIES,
TPM_PT_PERMANENT,
1,
&MoreData,
&TpmCap
);
if (EFI_ERROR (Status)) {
return Status;
}
Permanent = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value);
//
// bit0: ownerAuthSet
// bit2: lockoutAuthSet
//
*TpmProperty = ((Permanent & (BIT0 | BIT2)) == 0) ? FALSE : TRUE;
return EFI_SUCCESS;
}
/**
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;
EFI_STATUS Status;
EDKII_IOMMU_PPI *IoMmu;
if ((PcdGet32 (PcdPeiTpmPolicy) & DMA_PROTECTION_MEASUREMENT) != 0) {
//
// DMA protection measurement
//
Status = PeiServicesLocatePpi (
&gEdkiiIoMmuPpiGuid,
0,
NULL,
(VOID **)&IoMmu
);
if (EFI_ERROR (Status)) {
//
// DMA protection is disabled, extend an EV_EFI_ACTION event into PCR[7]
//
Status = MeasureDmaProtectionDisabled ();
DEBUG ((EFI_D_INFO, "MeasureDmaProtectionDisabled - %r\n", Status));
}
}
MeasuredHobData = NULL;
//
// 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));
}
return EFI_SUCCESS;
}
/**
Set Tpm2HashMask PCD value accroding to TPM2 PCR bank.
**/
VOID
SetTpm2HashMask (
VOID
)
{
EFI_STATUS Status;
UINT32 ActivePcrBanks;
TPML_PCR_SELECTION Pcrs;
UINTN Index;
DEBUG ((EFI_D_ERROR, "SetTpm2HashMask!\n"));
Status = Tpm2GetCapabilityPcrs (&Pcrs);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Tpm2GetCapabilityPcrs fail!\n"));
ActivePcrBanks = EFI_TCG2_BOOT_HASH_ALG_SHA1;
} else {
DEBUG ((EFI_D_INFO, "Tpm2GetCapabilityPcrs Count - %08x\n", Pcrs.count));
ActivePcrBanks = 0;
for (Index = 0; Index < Pcrs.count; Index++) {
DEBUG ((EFI_D_INFO, "hash - %x\n", Pcrs.pcrSelections[Index].hash));
switch (Pcrs.pcrSelections[Index].hash) {
case TPM_ALG_SHA1:
if (!IsZeroBuffer (Pcrs.pcrSelections[Index].pcrSelect, Pcrs.pcrSelections[Index].sizeofSelect)) {
ActivePcrBanks |= EFI_TCG2_BOOT_HASH_ALG_SHA1;
}
break;
case TPM_ALG_SHA256:
if (!IsZeroBuffer (Pcrs.pcrSelections[Index].pcrSelect, Pcrs.pcrSelections[Index].sizeofSelect)) {
ActivePcrBanks |= EFI_TCG2_BOOT_HASH_ALG_SHA256;
}
break;
case TPM_ALG_SHA384:
if (!IsZeroBuffer (Pcrs.pcrSelections[Index].pcrSelect, Pcrs.pcrSelections[Index].sizeofSelect)) {
ActivePcrBanks |= EFI_TCG2_BOOT_HASH_ALG_SHA384;
}
break;
case TPM_ALG_SHA512:
if (!IsZeroBuffer (Pcrs.pcrSelections[Index].pcrSelect, Pcrs.pcrSelections[Index].sizeofSelect)) {
ActivePcrBanks |= EFI_TCG2_BOOT_HASH_ALG_SHA512;
}
break;
case TPM_ALG_SM3_256:
if (!IsZeroBuffer (Pcrs.pcrSelections[Index].pcrSelect, Pcrs.pcrSelections[Index].sizeofSelect)) {
ActivePcrBanks |= EFI_TCG2_BOOT_HASH_ALG_SM3_256;
}
break;
}
}
}
Status = PcdSet32S (PcdTpm2HashMask, ActivePcrBanks);
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 ((EFI_D_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] Flags Bitmap providing additional information.
@param[in] HashData Physical address of the start of the data buffer
to be hashed, extended, and logged.
@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
HashLogExtendEvent (
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;
}
Status = HashAndExtend (
NewEventHdr->PCRIndex,
HashData,
HashDataLen,
&DigestList
);
if (!EFI_ERROR (Status)) {
if ((Flags & EFI_TCG2_EXTEND_ONLY) == 0) {
Status = LogHashEvent (&DigestList, NewEventHdr, NewEventData);
}
}
if (Status == EFI_DEVICE_ERROR) {
DEBUG ((EFI_D_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 DMA protection policy, and extend the measurement result into a specific PCR.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_DEVICE_ERROR The operation was unsuccessful.
**/
EFI_STATUS
MeasureDmaProtectionDisabled (
VOID
)
{
TCG_PCR_EVENT_HDR TcgEventHdr;
TcgEventHdr.PCRIndex = 7;
TcgEventHdr.EventType = EV_EFI_ACTION;
TcgEventHdr.EventSize = sizeof (DMA_PROTECTION_DISABLED_EVENT_STRING) - 1;
return HashLogExtendEvent (
0,
(UINT8 *)DMA_PROTECTION_DISABLED_EVENT_STRING,
sizeof (DMA_PROTECTION_DISABLED_EVENT_STRING) - 1,
&TcgEventHdr,
(UINT8 *)DMA_PROTECTION_DISABLED_EVENT_STRING
);
}
/**
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;
if ((PcdGet32 (PcdPeiTpmPolicy) & SKIP_MEASURE_CRTM_VERSION) != 0) {
return EFI_SUCCESS;
}
//
// 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 (
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 + 1) {
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;
EFI_PHYSICAL_ADDRESS FvOrgBase;
EFI_PHYSICAL_ADDRESS FvDataBase;
EFI_PEI_HOB_POINTERS Hob;
EDKII_MIGRATED_FV_INFO *MigratedFvInfo;
if ((PcdGet32 (PcdPeiTpmPolicy) & SKIP_MEASURE_FV_IMAGE) != 0) {
return EFI_SUCCESS;
}
//
// Check if it is in Excluded FV list
//
if (mMeasurementExcludedFvPpi != NULL) {
for (Index = 0; Index < mMeasurementExcludedFvPpi->Count; Index ++) {
if (mMeasurementExcludedFvPpi->Fv[Index].FvBase == FvBase) {
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;
}
}
}
//
// Check whether FV is in the measured FV list.
//
for (Index = 0; Index < mMeasuredBaseFvIndex; Index ++) {
if (mMeasuredBaseFvInfo[Index].BlobBase == FvBase) {
return EFI_SUCCESS;
}
}
//
// 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;
}
//
// Hash the FV, extend digest to the TPM and log TCG event
//
Status = HashLogExtendEvent (
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);
if (mMeasuredBaseFvInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
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;
if ((PcdGet32 (PcdPeiTpmPolicy) & SKIP_MEASURE_MAIN_BIOS) != 0) {
return EFI_SUCCESS;
}
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);
if (mMeasuredChildFvInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
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);
}
/**
Clear memory below 1MB if MOR is set.
@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 Operation completed successfully.
@retval Others Operation failed.
**/
STATIC
EFI_STATUS
EFIAPI
MemoryOverwriteCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
EFI_STATUS Status;
EFI_PEI_READ_ONLY_VARIABLE2_PPI *Variable;
UINTN VariableSize ;
UINT8 MorVariable;
BOOLEAN TpmProperty;
Status = PeiServicesLocatePpi (
&gEfiPeiReadOnlyVariable2PpiGuid,
0,
NULL,
(VOID **)&Variable
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Check MemoryOverwriteRequestControl variable
//
VariableSize = sizeof (MorVariable);
Status = Variable->GetVariable (
Variable,
MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
&gEfiMemoryOverwriteControlDataGuid,
NULL,
&VariableSize,
&MorVariable
);
if (!EFI_ERROR (Status) && MOR_CLEAR_MEMORY_VALUE (MorVariable) == 0x0) {
//
// No need to clear memory.
//
return EFI_SUCCESS;
}
//
// Get property is moved from driver's entry to here,
// since some TPM may fail in the command execution in MA.
//
TpmProperty = FALSE;
if ((PcdGet32 (PcdPeiTpmPolicy) & SKIP_PEI_TPM_PROPERTY_CHECK) == 0) {
Status = Tpm2GetProperty (&TpmProperty);
if (EFI_ERROR (Status)) {
TpmProperty = TRUE;
}
} else {
TpmProperty = TRUE;
}
if (TpmProperty) {
//
// To clear the memory region below 1M here before CSM use it.
// The other memory regions will be cleared in DXE phase.
//
Status = MemoryOverwriteBelow1M ();
}
return Status;
}
/**
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;
Status = PeiServicesLocatePpi (
&gEfiPeiFirmwareVolumeInfoMeasurementExcludedPpiGuid,
0,
NULL,
(VOID**)&mMeasurementExcludedFvPpi
);
// Do not check status, because it is optional
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 (0,(UINT8 *)&EventData, TcgEvent.EventSize, &TcgEvent,(UINT8 *)&EventData);
}
/**
TPM initialization entry.
@param[in] PeiServices Describes the list of possible PEI Services.
@return Status.
**/
EFI_STATUS
InitializationEntry (
IN EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
EFI_STATUS Status2;
EFI_BOOT_MODE BootMode;
TPM_PCRINDEX PcrIndex;
BOOLEAN S3ErrorReport;
Status = PeiServicesGetBootMode (&BootMode);
ASSERT_EFI_ERROR (Status);
if (!mImageInMemory) {
S3ErrorReport = FALSE;
if ((PcdGet32 (PcdPeiTpmPolicy) & SKIP_TPM_STARTUP) == 0) {
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.
//
SetTpm2HashMask ();
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 ((EFI_D_ERROR, "Separator Event with Error not Measured. Error!\n"));
}
}
//
// Disable EH & SH
//
Status = Tpm2HierarchyControl (TPM_RH_PLATFORM, NULL, TPM_RH_ENDORSEMENT, NO);
DEBUG ((EFI_D_INFO, "[TPM2] Disable Endorsement Hierarchy %r\n", Status));
Status = Tpm2HierarchyControl (TPM_RH_PLATFORM, NULL, TPM_RH_OWNER, NO);
DEBUG ((EFI_D_INFO, "[TPM2] Disable Storage Hierarchy %r\n", Status));
if (FeaturePcdGet (PcdH2OTpmS3StartupFailureReboot)) {
//
// Need reset system
//
DEBUG ((EFI_D_INFO, "[TPM2] TPM S3 Startup Failed, Reset System\n"));
Status = PeiServicesNotifyPpi (&mResetCallbackDescriptor);
}
}
//
// TpmSelfTest is optional on S3 path, skip it to save S3 time
//
if (BootMode != BOOT_ON_S3_RESUME) {
if ((PcdGet32 (PcdPeiTpmPolicy) & SKIP_TPM_SELF_TEST) == 0) {
Status = Tpm2SelfTest (NO);
if (EFI_ERROR (Status)) {
goto Done;
}
}
if (BootMode != BOOT_ON_FLASH_UPDATE) {
Status = PeiServicesNotifyPpi (&mMorCallbackDescriptor);
ASSERT_EFI_ERROR (Status);
}
}
//
// Only intall 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 ((EFI_D_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;
}
/**
Process TPM initialization once PEI_TPM2_INTERFACE_PPI installed.
@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 Operation completed successfully.
@retval Others Operation failed.
**/
EFI_STATUS
EFIAPI
Tpm2InterfacePpiNotifyCallback (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
EFI_STATUS Status;
VOID *Interface;
Status = PeiServicesLocatePpi (
&gPeiTpm2InterfacePpiGuid,
0,
NULL,
(VOID **)&Interface
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = PeiServicesLocatePpi (
&gPeiTpmInitializationDonePpiGuid,
0,
NULL,
(VOID **)&Interface
);
if (!EFI_ERROR (Status)) {
//
// TPM initialization is done already
//
return Status;
}
Status = Tpm2RequestUseTpm ();
if (EFI_ERROR (Status)) {
return Status;
}
return InitializationEntry (PeiServices);
}
/**
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_BOOT_MODE BootMode;
VOID *Interface;
if (CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceNoneGuid) ||
CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceTpm12Guid) ||
CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceTcmGuid)) {
DEBUG ((DEBUG_INFO, "No TPM2 instance required!\n"));
return EFI_UNSUPPORTED;
}
if (GetFirstGuidHob (&gTpmErrorHobGuid) != NULL) {
DEBUG ((EFI_D_ERROR, "TPM2 error!\n"));
return EFI_DEVICE_ERROR;
}
if (PcdGetBool (PcdTpmHide)) {
DEBUG((EFI_D_INFO, "TPM is hidden\n"));
return EFI_UNSUPPORTED;
}
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 (Status == EFI_UNSUPPORTED) {
DEBUG ((DEBUG_ERROR, "TPM2 not detected!\n"));
return PeiServicesNotifyPpi (&mTpm2InterfacePpiNotifyDescriptor);
}
} else {
Status = PeiServicesLocatePpi (
&gPeiTpmInitializationDonePpiGuid,
0,
NULL,
(VOID **)&Interface
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "TPM2 error! Build Hob\n"));
BuildGuidHob (&gTpmErrorHobGuid,0);
return EFI_DEVICE_ERROR;
}
}
return InitializationEntry ((EFI_PEI_SERVICES **)PeiServices);
}