diff --git a/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.c b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.c new file mode 100644 index 0000000000..762ca159ff --- /dev/null +++ b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.c @@ -0,0 +1,281 @@ +/** @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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 %a!\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; +} diff --git a/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.inf b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.inf new file mode 100644 index 0000000000..649fb9403f --- /dev/null +++ b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.inf @@ -0,0 +1,56 @@ +## @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.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MicrocodeMeasurementDxe + MODULE_UNI_FILE = MicrocodeMeasurementDxe.uni + FILE_GUID = 0A32A803-ACDF-4C89-8293-91011548CD91 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = MicrocodeMeasurementDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + MicrocodeMeasurementDxe.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + HobLib + MicrocodeLib + TpmMeasurementLib + +[Guids] + gEdkiiMicrocodePatchHobGuid ## CONSUMES ## HOB + +[UserExtensions.TianoCore."ExtraFiles"] + MicrocodeMeasurementDxeExtra.uni + +[Depex] + TRUE diff --git a/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.uni b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.uni new file mode 100644 index 0000000000..5a21e955fb --- /dev/null +++ b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.uni @@ -0,0 +1,15 @@ +// /** @file +// This driver measures microcode patches to TPM. +// +// This driver consumes gEdkiiMicrocodePatchHobGuid, packs all uniquemicrocode patch found in gEdkiiMicrocodePatchHobGuid to a binary blob, and measures the binary blob to TPM. +// +// Copyright (c) 2021, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "This driver measures Microcode Patches to TPM." + +#string STR_MODULE_DESCRIPTION #language en-US "This driver consumes gEdkiiMicrocodePatchHobGuid, packs all microcode patch found in gEdkiiMicrocodePatchHobGuid to a binary blob, and measure the binary blob to TPM." diff --git a/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxeExtra.uni b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxeExtra.uni new file mode 100644 index 0000000000..6990cee8c6 --- /dev/null +++ b/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxeExtra.uni @@ -0,0 +1,12 @@ +// /** @file +// MicrocodeMeasurementDxe Localized Strings and Content +// +// Copyright (c) 2021, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Microcode Patches Measurement DXE Driver" diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc index 870b452840..d1d61dd6a0 100644 --- a/UefiCpuPkg/UefiCpuPkg.dsc +++ b/UefiCpuPkg/UefiCpuPkg.dsc @@ -119,6 +119,7 @@ UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.inf UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.inf + UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.inf [Components.IA32, Components.X64] UefiCpuPkg/CpuDxe/CpuDxe.inf