/** @file | |
This driver measures microcode patches to TPM. | |
This driver consumes gEdkiiMicrocodePatchHobGuid, packs all unique microcode patch found in gEdkiiMicrocodePatchHobGuid to a binary blob, and measures the binary blob to TPM. | |
Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <IndustryStandard/UefiTcgPlatform.h> | |
#include <Guid/EventGroup.h> | |
#include <Guid/MicrocodePatchHob.h> | |
#include <Library/DebugLib.h> | |
#include <Library/UefiDriverEntryPoint.h> | |
#include <Library/UefiLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/HobLib.h> | |
#include <Library/MicrocodeLib.h> | |
#include <Library/TpmMeasurementLib.h> | |
#define CPU_MICROCODE_MEASUREMENT_DESCRIPTION "Microcode Measurement" | |
#define CPU_MICROCODE_MEASUREMENT_EVENT_LOG_DESCRIPTION_LEN sizeof (CPU_MICROCODE_MEASUREMENT_DESCRIPTION) | |
#pragma pack(1) | |
typedef struct { | |
UINT8 Description[CPU_MICROCODE_MEASUREMENT_EVENT_LOG_DESCRIPTION_LEN]; | |
UINTN NumberOfMicrocodePatchesMeasured; | |
UINTN SizeOfMicrocodePatchesMeasured; | |
} CPU_MICROCODE_MEASUREMENT_EVENT_LOG; | |
#pragma pack() | |
/** | |
Helping function. | |
The function is called by QuickSort to compare the order of offsets of | |
two microcode patches in RAM relative to their base address. Elements | |
will be in ascending order. | |
@param[in] Offset1 The pointer to the offset of first microcode patch. | |
@param[in] Offset2 The pointer to the offset of second microcode patch. | |
@retval 1 The offset of first microcode patch is bigger than that of the second. | |
@retval -1 The offset of first microcode patch is smaller than that of the second. | |
@retval 0 The offset of first microcode patch equals to that of the second. | |
**/ | |
INTN | |
EFIAPI | |
MicrocodePatchOffsetCompareFunction ( | |
IN CONST VOID *Offset1, | |
IN CONST VOID *Offset2 | |
) | |
{ | |
if (*(UINT64 *)(Offset1) > *(UINT64 *)(Offset2)) { | |
return 1; | |
} else if (*(UINT64 *)(Offset1) < *(UINT64 *)(Offset2)) { | |
return -1; | |
} else { | |
return 0; | |
} | |
} | |
/** | |
This function remove duplicate and invalid offsets in Offsets. | |
This function remove duplicate and invalid offsets in Offsets. Invalid offset means MAX_UINT64 in Offsets. | |
@param[in] Offsets Microcode offset list. | |
@param[in, out] Count On call as the count of raw microcode offset list; On return as count of the clean microcode offset list. | |
**/ | |
VOID | |
RemoveDuplicateAndInvalidOffset ( | |
IN UINT64 *Offsets, | |
IN OUT UINTN *Count | |
) | |
{ | |
UINTN Index; | |
UINTN NewCount; | |
UINT64 LastOffset; | |
UINT64 QuickSortBuffer; | |
// | |
// The order matters when packing all applied microcode patches to a single binary blob. | |
// Therefore it is a must to do sorting before packing. | |
// NOTE: Since microcode patches are sorted by their addresses in memory, the order of | |
// addresses in memory of all the microcode patches before sorting is required to be the | |
// same in every boot flow. If any future updates made this assumption untenable, then | |
// there needs a new solution to measure microcode patches. | |
// | |
QuickSort ( | |
Offsets, | |
*Count, | |
sizeof (UINT64), | |
MicrocodePatchOffsetCompareFunction, | |
(VOID *)&QuickSortBuffer | |
); | |
NewCount = 0; | |
LastOffset = MAX_UINT64; | |
for (Index = 0; Index < *Count; Index++) { | |
// | |
// When MAX_UINT64 element is met, all following elements are MAX_UINT64. | |
// | |
if (Offsets[Index] == MAX_UINT64) { | |
break; | |
} | |
// | |
// Remove duplicated offsets | |
// | |
if (Offsets[Index] != LastOffset) { | |
LastOffset = Offsets[Index]; | |
Offsets[NewCount] = Offsets[Index]; | |
NewCount++; | |
} | |
} | |
*Count = NewCount; | |
} | |
/** | |
Callback function. | |
Called after signaling of the Ready to Boot Event. Measure microcode patches binary blob with event type EV_CPU_MICROCODE to PCR[1] in TPM. | |
@param[in] Event Event whose notification function is being invoked. | |
@param[in] Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
MeasureMicrocodePatches ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 PCRIndex; | |
UINT32 EventType; | |
CPU_MICROCODE_MEASUREMENT_EVENT_LOG EventLog; | |
UINT32 EventLogSize; | |
EFI_HOB_GUID_TYPE *GuidHob; | |
EDKII_MICROCODE_PATCH_HOB *MicrocodePatchHob; | |
UINT64 *Offsets; | |
UINTN Count; | |
UINTN Index; | |
UINTN TotalMicrocodeSize; | |
UINT8 *MicrocodePatchesBlob; | |
PCRIndex = 1; | |
EventType = EV_CPU_MICROCODE; | |
AsciiStrCpyS ( | |
(CHAR8 *)(EventLog.Description), | |
CPU_MICROCODE_MEASUREMENT_EVENT_LOG_DESCRIPTION_LEN, | |
CPU_MICROCODE_MEASUREMENT_DESCRIPTION | |
); | |
EventLog.NumberOfMicrocodePatchesMeasured = 0; | |
EventLog.SizeOfMicrocodePatchesMeasured = 0; | |
EventLogSize = sizeof (CPU_MICROCODE_MEASUREMENT_EVENT_LOG); | |
Offsets = NULL; | |
TotalMicrocodeSize = 0; | |
Count = 0; | |
GuidHob = GetFirstGuidHob (&gEdkiiMicrocodePatchHobGuid); | |
if (NULL == GuidHob) { | |
DEBUG ((DEBUG_ERROR, "ERROR: GetFirstGuidHob (&gEdkiiMicrocodePatchHobGuid) failed.\n")); | |
return; | |
} | |
MicrocodePatchHob = GET_GUID_HOB_DATA (GuidHob); | |
DEBUG ( | |
(DEBUG_INFO, | |
"INFO: Got MicrocodePatchHob with microcode patches starting address:0x%x, microcode patches region size:0x%x, processor count:0x%x\n", | |
MicrocodePatchHob->MicrocodePatchAddress, MicrocodePatchHob->MicrocodePatchRegionSize, | |
MicrocodePatchHob->ProcessorCount) | |
); | |
Offsets = AllocateCopyPool ( | |
MicrocodePatchHob->ProcessorCount * sizeof (UINT64), | |
MicrocodePatchHob->ProcessorSpecificPatchOffset | |
); | |
Count = MicrocodePatchHob->ProcessorCount; | |
RemoveDuplicateAndInvalidOffset (Offsets, &Count); | |
if (0 == Count) { | |
DEBUG ((DEBUG_INFO, "INFO: No microcode patch is ever applied, skip the measurement of microcode!\n")); | |
FreePool (Offsets); | |
return; | |
} | |
for (Index = 0; Index < Count; Index++) { | |
TotalMicrocodeSize += | |
GetMicrocodeLength ((CPU_MICROCODE_HEADER *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress + Offsets[Index]))); | |
} | |
EventLog.NumberOfMicrocodePatchesMeasured = Count; | |
EventLog.SizeOfMicrocodePatchesMeasured = TotalMicrocodeSize; | |
MicrocodePatchesBlob = AllocateZeroPool (TotalMicrocodeSize); | |
if (NULL == MicrocodePatchesBlob) { | |
DEBUG ((DEBUG_ERROR, "ERROR: AllocateZeroPool to MicrocodePatchesBlob failed!\n")); | |
FreePool (Offsets); | |
return; | |
} | |
TotalMicrocodeSize = 0; | |
for (Index = 0; Index < Count; Index++) { | |
CopyMem ( | |
(VOID *)(MicrocodePatchesBlob + TotalMicrocodeSize), | |
(VOID *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress + Offsets[Index])), | |
(UINTN)(GetMicrocodeLength ( | |
(CPU_MICROCODE_HEADER *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress + | |
Offsets[Index])) | |
)) | |
); | |
TotalMicrocodeSize += | |
GetMicrocodeLength ((CPU_MICROCODE_HEADER *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress + Offsets[Index]))); | |
} | |
Status = TpmMeasureAndLogData ( | |
PCRIndex, // PCRIndex | |
EventType, // EventType | |
&EventLog, // EventLog | |
EventLogSize, // LogLen | |
MicrocodePatchesBlob, // HashData | |
TotalMicrocodeSize // HashDataLen | |
); | |
if (!EFI_ERROR (Status)) { | |
gBS->CloseEvent (Event); | |
DEBUG ( | |
(DEBUG_INFO, | |
"INFO: %d Microcode patches are successfully extended to TPM! The total size measured to TPM is 0x%x\n", | |
Count, | |
TotalMicrocodeSize) | |
); | |
} else { | |
DEBUG ((DEBUG_ERROR, "ERROR: TpmMeasureAndLogData failed with status %r!\n", Status)); | |
} | |
FreePool (Offsets); | |
FreePool (MicrocodePatchesBlob); | |
return; | |
} | |
/** | |
Driver to produce microcode measurement. | |
Driver to produce microcode measurement. Which install a callback function on ready to boot event. | |
@param ImageHandle Module's image handle | |
@param SystemTable Pointer of EFI_SYSTEM_TABLE | |
@return EFI_SUCCESS This function always complete successfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MicrocodeMeasurementDriverEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_EVENT Event; | |
// | |
// Measure Microcode patches | |
// | |
EfiCreateEventReadyToBootEx ( | |
TPL_CALLBACK, | |
MeasureMicrocodePatches, | |
NULL, | |
&Event | |
); | |
return EFI_SUCCESS; | |
} |