]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c
Add TPM2 implementation.
[mirror_edk2.git] / SecurityPkg / Library / DxeTpm2MeasureBootLib / DxeTpm2MeasureBootLib.c
diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c
new file mode 100644 (file)
index 0000000..3c81b5a
--- /dev/null
@@ -0,0 +1,700 @@
+/** @file\r
+  The library instance provides security service of TPM2 measure boot.\r
+\r
+  Caution: This file requires additional review when modified.\r
+  This library will have external input - PE/COFF image and GPT partition.\r
+  This external input must be validated carefully to avoid security issue like\r
+  buffer overflow, integer overflow.\r
+\r
+  DxeTpm2MeasureBootLibImageRead() function will make sure the PE/COFF image content\r
+  read is within the image buffer.\r
+\r
+  TrEEMeasurePeImage() function will accept untrusted PE/COFF image and validate its\r
+  data structure within this image buffer before use.\r
+\r
+  TrEEMeasureGptTable() function will receive untrusted GPT partition table, and parse\r
+  partition data carefully.\r
+\r
+Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials \r
+are licensed and made available under the terms and conditions of the BSD License \r
+which accompanies this distribution.  The full text of the license may be found at \r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Protocol/TrEEProtocol.h>\r
+#include <Protocol/BlockIo.h>\r
+#include <Protocol/DiskIo.h>\r
+#include <Protocol/DevicePathToText.h>\r
+#include <Protocol/FirmwareVolumeBlock.h>\r
+\r
+#include <Guid/MeasuredFvHob.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseCryptLib.h>\r
+#include <Library/PeCoffLib.h>\r
+#include <Library/SecurityManagementLib.h>\r
+#include <Library/HobLib.h>\r
+\r
+//\r
+// Flag to check GPT partition. It only need be measured once.\r
+//\r
+BOOLEAN                           mTrEEMeasureGptTableFlag = FALSE;\r
+EFI_GUID                          mTrEEZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};\r
+UINTN                             mTrEEMeasureGptCount = 0;\r
+VOID                              *mTrEEFileBuffer;\r
+UINTN                             mTrEEImageSize;\r
+//\r
+// Measured FV handle cache\r
+//\r
+EFI_HANDLE                        mTrEECacheMeasuredHandle  = NULL;\r
+MEASURED_HOB_DATA                 *mTrEEMeasuredHobData     = NULL;\r
+\r
+/**\r
+  Reads contents of a PE/COFF image in memory buffer.\r
+\r
+  Caution: This function may receive untrusted input.\r
+  PE/COFF image is external input, so this function will make sure the PE/COFF image content\r
+  read is within the image buffer.\r
+\r
+  @param  FileHandle      Pointer to the file handle to read the PE/COFF image.\r
+  @param  FileOffset      Offset into the PE/COFF image to begin the read operation.\r
+  @param  ReadSize        On input, the size in bytes of the requested read operation.  \r
+                          On output, the number of bytes actually read.\r
+  @param  Buffer          Output buffer that contains the data read from the PE/COFF image.\r
+  \r
+  @retval EFI_SUCCESS     The specified portion of the PE/COFF image was read and the size \r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DxeTpm2MeasureBootLibImageRead (\r
+  IN     VOID    *FileHandle,\r
+  IN     UINTN   FileOffset,\r
+  IN OUT UINTN   *ReadSize,\r
+  OUT    VOID    *Buffer\r
+  )\r
+{\r
+  UINTN               EndPosition;\r
+\r
+  if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (MAX_ADDRESS - FileOffset < *ReadSize) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  EndPosition = FileOffset + *ReadSize;\r
+  if (EndPosition > mTrEEImageSize) {\r
+    *ReadSize = (UINT32)(mTrEEImageSize - FileOffset);\r
+  }\r
+\r
+  if (FileOffset >= mTrEEImageSize) {\r
+    *ReadSize = 0;\r
+  }\r
+\r
+  CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Measure GPT table data into TPM log.\r
+\r
+  Caution: This function may receive untrusted input.\r
+  The GPT partition table is external input, so this function should parse partition data carefully.\r
+\r
+  @param TreeProtocol            Pointer to the located TREE protocol instance.\r
+  @param GptHandle               Handle that GPT partition was installed.\r
+\r
+  @retval EFI_SUCCESS            Successfully measure GPT table.\r
+  @retval EFI_UNSUPPORTED        Not support GPT table on the given handle.\r
+  @retval EFI_DEVICE_ERROR       Can't get GPT table because device error.\r
+  @retval EFI_OUT_OF_RESOURCES   No enough resource to measure GPT table.\r
+  @retval other error value\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TrEEMeasureGptTable (\r
+  IN  EFI_TREE_PROTOCOL  *TreeProtocol,\r
+  IN  EFI_HANDLE         GptHandle\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_BLOCK_IO_PROTOCOL             *BlockIo;\r
+  EFI_DISK_IO_PROTOCOL              *DiskIo;\r
+  EFI_PARTITION_TABLE_HEADER        *PrimaryHeader;\r
+  EFI_PARTITION_ENTRY               *PartitionEntry;\r
+  UINT8                             *EntryPtr;\r
+  UINTN                             NumberOfPartition;\r
+  UINT32                            Index;\r
+  TrEE_EVENT                        *TreeEvent;\r
+  EFI_GPT_DATA                      *GptData;\r
+  UINT32                            EventSize;\r
+\r
+  if (mTrEEMeasureGptCount > 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol (GptHandle, &gEfiBlockIoProtocolGuid, (VOID**)&BlockIo);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  Status = gBS->HandleProtocol (GptHandle, &gEfiDiskIoProtocolGuid, (VOID**)&DiskIo);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  //\r
+  // Read the EFI Partition Table Header\r
+  //  \r
+  PrimaryHeader = (EFI_PARTITION_TABLE_HEADER *) AllocatePool (BlockIo->Media->BlockSize);\r
+  if (PrimaryHeader == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }  \r
+  Status = DiskIo->ReadDisk (\r
+                     DiskIo,\r
+                     BlockIo->Media->MediaId,\r
+                     1 * BlockIo->Media->BlockSize,\r
+                     BlockIo->Media->BlockSize,\r
+                     (UINT8 *)PrimaryHeader\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Failed to Read Partition Table Header!\n"));\r
+    FreePool (PrimaryHeader);\r
+    return EFI_DEVICE_ERROR;\r
+  }  \r
+  //\r
+  // Read the partition entry.\r
+  //\r
+  EntryPtr = (UINT8 *)AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry);\r
+  if (EntryPtr == NULL) {\r
+    FreePool (PrimaryHeader);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  Status = DiskIo->ReadDisk (\r
+                     DiskIo,\r
+                     BlockIo->Media->MediaId,\r
+                     MultU64x32(PrimaryHeader->PartitionEntryLBA, BlockIo->Media->BlockSize),\r
+                     PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry,\r
+                     EntryPtr\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (PrimaryHeader);\r
+    FreePool (EntryPtr);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  \r
+  //\r
+  // Count the valid partition\r
+  //\r
+  PartitionEntry    = (EFI_PARTITION_ENTRY *)EntryPtr;\r
+  NumberOfPartition = 0;\r
+  for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) {\r
+    if (!CompareGuid (&PartitionEntry->PartitionTypeGUID, &mTrEEZeroGuid)) {\r
+      NumberOfPartition++;  \r
+    }\r
+    PartitionEntry = (EFI_PARTITION_ENTRY *)((UINT8 *)PartitionEntry + PrimaryHeader->SizeOfPartitionEntry);\r
+  }\r
+\r
+  //\r
+  // Prepare Data for Measurement\r
+  // \r
+  EventSize = (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) \r
+                        + NumberOfPartition * PrimaryHeader->SizeOfPartitionEntry);\r
+  TreeEvent = (TrEE_EVENT *) AllocateZeroPool (EventSize + sizeof (TrEE_EVENT) - sizeof(TreeEvent->Event));\r
+  if (TreeEvent == NULL) {\r
+    FreePool (PrimaryHeader);\r
+    FreePool (EntryPtr);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TreeEvent->Size = EventSize + sizeof (TrEE_EVENT) - sizeof(TreeEvent->Event);\r
+  TreeEvent->Header.HeaderSize    = sizeof(TrEE_EVENT_HEADER);\r
+  TreeEvent->Header.HeaderVersion = TREE_EVENT_HEADER_VERSION;\r
+  TreeEvent->Header.PCRIndex      = 5;\r
+  TreeEvent->Header.EventType     = EV_EFI_GPT_EVENT;\r
+  GptData = (EFI_GPT_DATA *) TreeEvent->Event;  \r
+\r
+  //\r
+  // Copy the EFI_PARTITION_TABLE_HEADER and NumberOfPartition\r
+  //  \r
+  CopyMem ((UINT8 *)GptData, (UINT8*)PrimaryHeader, sizeof (EFI_PARTITION_TABLE_HEADER));\r
+  GptData->NumberOfPartitions = NumberOfPartition;\r
+  //\r
+  // Copy the valid partition entry\r
+  //\r
+  PartitionEntry    = (EFI_PARTITION_ENTRY*)EntryPtr;\r
+  NumberOfPartition = 0;\r
+  for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) {\r
+    if (!CompareGuid (&PartitionEntry->PartitionTypeGUID, &mTrEEZeroGuid)) {\r
+      CopyMem (\r
+        (UINT8 *)&GptData->Partitions + NumberOfPartition * PrimaryHeader->SizeOfPartitionEntry,\r
+        (UINT8 *)PartitionEntry,\r
+        PrimaryHeader->SizeOfPartitionEntry\r
+        );\r
+      NumberOfPartition++;\r
+    }\r
+    PartitionEntry =(EFI_PARTITION_ENTRY *)((UINT8 *)PartitionEntry + PrimaryHeader->SizeOfPartitionEntry);\r
+  }\r
+\r
+  //\r
+  // Measure the GPT data\r
+  //\r
+  Status = TreeProtocol->HashLogExtendEvent (\r
+             TreeProtocol,\r
+             0,\r
+             (EFI_PHYSICAL_ADDRESS) (UINTN) (VOID *) GptData,\r
+             (UINT64) EventSize,\r
+             TreeEvent\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    mTrEEMeasureGptCount++;\r
+  }\r
+\r
+  FreePool (PrimaryHeader);\r
+  FreePool (EntryPtr);\r
+  FreePool (TreeEvent);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Measure PE image into TPM log based on the authenticode image hashing in\r
+  PE/COFF Specification 8.0 Appendix A.\r
+\r
+  Caution: This function may receive untrusted input.\r
+  PE/COFF image is external input, so this function will validate its data structure\r
+  within this image buffer before use.\r
+\r
+  @param[in] TreeProtocol   Pointer to the located TREE protocol instance.\r
+  @param[in] ImageAddress   Start address of image buffer.\r
+  @param[in] ImageSize      Image size\r
+  @param[in] LinkTimeBase   Address that the image is loaded into memory.\r
+  @param[in] ImageType      Image subsystem type.\r
+  @param[in] FilePath       File path is corresponding to the input image.\r
+\r
+  @retval EFI_SUCCESS            Successfully measure image.\r
+  @retval EFI_OUT_OF_RESOURCES   No enough resource to measure image.\r
+  @retval EFI_UNSUPPORTED        ImageType is unsupported or PE image is mal-format.  \r
+  @retval other error value\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TrEEMeasurePeImage (\r
+  IN  EFI_TREE_PROTOCOL         *TreeProtocol,\r
+  IN  EFI_PHYSICAL_ADDRESS      ImageAddress,\r
+  IN  UINTN                     ImageSize,\r
+  IN  UINTN                     LinkTimeBase,\r
+  IN  UINT16                    ImageType,\r
+  IN  EFI_DEVICE_PATH_PROTOCOL  *FilePath\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  TrEE_EVENT                        *TreeEvent;\r
+  EFI_IMAGE_LOAD_EVENT              *ImageLoad;\r
+  UINT32                            FilePathSize;\r
+  UINT32                            EventSize;\r
+\r
+  Status        = EFI_UNSUPPORTED;\r
+  ImageLoad     = NULL;\r
+  FilePathSize  = (UINT32) GetDevicePathSize (FilePath);\r
+\r
+  //\r
+  // Determine destination PCR by BootPolicy\r
+  //\r
+  EventSize = sizeof (*ImageLoad) - sizeof (ImageLoad->DevicePath) + FilePathSize;\r
+  TreeEvent = AllocateZeroPool (EventSize + sizeof (TrEE_EVENT) - sizeof(TreeEvent->Event));\r
+  if (TreeEvent == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TreeEvent->Size = EventSize + sizeof (TrEE_EVENT) - sizeof(TreeEvent->Event);\r
+  TreeEvent->Header.HeaderSize    = sizeof(TrEE_EVENT_HEADER);\r
+  TreeEvent->Header.HeaderVersion = TREE_EVENT_HEADER_VERSION;\r
+  ImageLoad           = (EFI_IMAGE_LOAD_EVENT *) TreeEvent->Event;\r
+\r
+  switch (ImageType) {\r
+    case EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION:\r
+      TreeEvent->Header.EventType = EV_EFI_BOOT_SERVICES_APPLICATION;\r
+      TreeEvent->Header.PCRIndex  = 4;\r
+      break;\r
+    case EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:\r
+      TreeEvent->Header.EventType = EV_EFI_BOOT_SERVICES_DRIVER;\r
+      TreeEvent->Header.PCRIndex  = 2;\r
+      break;\r
+    case EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:\r
+      TreeEvent->Header.EventType = EV_EFI_RUNTIME_SERVICES_DRIVER;\r
+      TreeEvent->Header.PCRIndex  = 2;\r
+      break;\r
+    default:\r
+      DEBUG ((\r
+        EFI_D_ERROR,\r
+        "TrEEMeasurePeImage: Unknown subsystem type %d",\r
+        ImageType\r
+        ));\r
+      goto Finish;\r
+  }\r
+\r
+  ImageLoad->ImageLocationInMemory = ImageAddress;\r
+  ImageLoad->ImageLengthInMemory   = ImageSize;\r
+  ImageLoad->ImageLinkTimeAddress  = LinkTimeBase;\r
+  ImageLoad->LengthOfDevicePath    = FilePathSize;\r
+  CopyMem (ImageLoad->DevicePath, FilePath, FilePathSize);\r
+\r
+  //\r
+  // Log the PE data\r
+  //\r
+  Status = TreeProtocol->HashLogExtendEvent (\r
+             TreeProtocol,\r
+             PE_COFF_IMAGE,\r
+             ImageAddress,\r
+             ImageSize,\r
+             TreeEvent\r
+             );\r
+  if (Status == EFI_VOLUME_FULL) {\r
+    //\r
+    // Volume full here means the image is hashed and its result is extended to PCR.\r
+    // But the event log cann't be saved since log area is full.\r
+    // Just return EFI_SUCCESS in order not to block the image load.\r
+    //\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+Finish:\r
+  FreePool (TreeEvent);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The security handler is used to abstract platform-specific policy \r
+  from the DXE core response to an attempt to use a file that returns a \r
+  given status for the authentication check from the section extraction protocol.  \r
+\r
+  The possible responses in a given SAP implementation may include locking \r
+  flash upon failure to authenticate, attestation logging for all signed drivers, \r
+  and other exception operations.  The File parameter allows for possible logging \r
+  within the SAP of the driver.\r
+\r
+  If File is NULL, then EFI_INVALID_PARAMETER is returned.\r
+\r
+  If the file specified by File with an authentication status specified by \r
+  AuthenticationStatus is safe for the DXE Core to use, then EFI_SUCCESS is returned.\r
+\r
+  If the file specified by File with an authentication status specified by \r
+  AuthenticationStatus is not safe for the DXE Core to use under any circumstances, \r
+  then EFI_ACCESS_DENIED is returned.\r
+\r
+  If the file specified by File with an authentication status specified by \r
+  AuthenticationStatus is not safe for the DXE Core to use right now, but it \r
+  might be possible to use it at a future time, then EFI_SECURITY_VIOLATION is \r
+  returned.\r
+\r
+  @param[in]      AuthenticationStatus  This is the authentication status returned\r
+                                        from the securitymeasurement services for the\r
+                                        input file.\r
+  @param[in]      File       This is a pointer to the device path of the file that is\r
+                             being dispatched. This will optionally be used for logging.\r
+  @param[in]      FileBuffer File buffer matches the input file device path.\r
+  @param[in]      FileSize   Size of File buffer matches the input file device path.\r
+  @param[in]      BootPolicy A boot policy that was used to call LoadImage() UEFI service.\r
+\r
+  @retval EFI_SUCCESS             The file specified by DevicePath and non-NULL\r
+                                  FileBuffer did authenticate, and the platform policy dictates\r
+                                  that the DXE Foundation may use the file.\r
+  @retval other error value\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DxeTpm2MeasureBootHandler (\r
+  IN  UINT32                           AuthenticationStatus,\r
+  IN  CONST EFI_DEVICE_PATH_PROTOCOL   *File,\r
+  IN  VOID                             *FileBuffer,\r
+  IN  UINTN                            FileSize,\r
+  IN  BOOLEAN                          BootPolicy\r
+  )\r
+{\r
+  EFI_TREE_PROTOCOL                   *TreeProtocol;\r
+  EFI_STATUS                          Status;\r
+  TREE_BOOT_SERVICE_CAPABILITY        ProtocolCapability;\r
+  EFI_DEVICE_PATH_PROTOCOL            *DevicePathNode;\r
+  EFI_DEVICE_PATH_PROTOCOL            *OrigDevicePathNode;\r
+  EFI_HANDLE                          Handle;\r
+  EFI_HANDLE                          TempHandle;\r
+  BOOLEAN                             ApplicationRequired;\r
+  PE_COFF_LOADER_IMAGE_CONTEXT        ImageContext;\r
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvbProtocol;\r
+  EFI_PHYSICAL_ADDRESS                FvAddress;\r
+  UINT32                              Index;\r
+\r
+  Status = gBS->LocateProtocol (&gEfiTrEEProtocolGuid, NULL, (VOID **) &TreeProtocol);\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // TrEE protocol is not installed. So, TPM2 is not present.\r
+    // Don't do any measurement, and directly return EFI_SUCCESS.\r
+    //\r
+    DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler - TrEE - %r\n", Status));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  ProtocolCapability.Size = (UINT8) sizeof (ProtocolCapability);\r
+  Status = TreeProtocol->GetCapability (\r
+                           TreeProtocol, \r
+                           &ProtocolCapability\r
+                           );\r
+  if (EFI_ERROR (Status) || !ProtocolCapability.TrEEPresentFlag) {\r
+    //\r
+    // TPM device doesn't work or activate.\r
+    //\r
+    DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler (%r) - TrEEPresentFlag - %x\n", Status, ProtocolCapability.TrEEPresentFlag));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Copy File Device Path\r
+  //\r
+  OrigDevicePathNode = DuplicateDevicePath (File);\r
+  \r
+  //\r
+  // 1. Check whether this device path support BlockIo protocol.\r
+  // Is so, this device path may be a GPT device path.\r
+  //\r
+  DevicePathNode = OrigDevicePathNode;\r
+  Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &DevicePathNode, &Handle);\r
+  if (!EFI_ERROR (Status) && !mTrEEMeasureGptTableFlag) {\r
+    //\r
+    // Find the gpt partion on the given devicepath\r
+    //\r
+    DevicePathNode = OrigDevicePathNode;\r
+    ASSERT (DevicePathNode != NULL);\r
+    while (!IsDevicePathEnd (DevicePathNode)) {\r
+      //\r
+      // Find the Gpt partition\r
+      //\r
+      if (DevicePathType (DevicePathNode) == MEDIA_DEVICE_PATH &&\r
+            DevicePathSubType (DevicePathNode) == MEDIA_HARDDRIVE_DP) {\r
+        //\r
+        // Check whether it is a gpt partition or not\r
+        //                           \r
+        if (((HARDDRIVE_DEVICE_PATH *) DevicePathNode)->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER && \r
+            ((HARDDRIVE_DEVICE_PATH *) DevicePathNode)->SignatureType == SIGNATURE_TYPE_GUID) {\r
+\r
+          //\r
+          // Change the partition device path to its parent device path (disk) and get the handle.\r
+          //\r
+          DevicePathNode->Type    = END_DEVICE_PATH_TYPE;\r
+          DevicePathNode->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;\r
+          DevicePathNode          = OrigDevicePathNode;\r
+          Status = gBS->LocateDevicePath (\r
+                         &gEfiDiskIoProtocolGuid,\r
+                         &DevicePathNode,\r
+                         &Handle\r
+                         );\r
+          if (!EFI_ERROR (Status)) {\r
+            //\r
+            // Measure GPT disk.\r
+            //\r
+            Status = TrEEMeasureGptTable (TreeProtocol, Handle);\r
+            DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler - TrEEMeasureGptTable - %r\n", Status));\r
+            if (!EFI_ERROR (Status)) {\r
+              //\r
+              // GPT disk check done.\r
+              //\r
+              mTrEEMeasureGptTableFlag = TRUE;\r
+            }\r
+          }\r
+          FreePool (OrigDevicePathNode);\r
+          OrigDevicePathNode = DuplicateDevicePath (File);\r
+          ASSERT (OrigDevicePathNode != NULL);\r
+          break;\r
+        }\r
+      }\r
+      DevicePathNode    = NextDevicePathNode (DevicePathNode);\r
+    }\r
+  }\r
+  \r
+  //\r
+  // 2. Measure PE image.\r
+  //\r
+  ApplicationRequired = FALSE;\r
+\r
+  //\r
+  // Check whether this device path support FVB protocol.\r
+  //\r
+  DevicePathNode = OrigDevicePathNode;\r
+  Status = gBS->LocateDevicePath (&gEfiFirmwareVolumeBlockProtocolGuid, &DevicePathNode, &Handle);\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Don't check FV image, and directly return EFI_SUCCESS.\r
+    // It can be extended to the specific FV authentication according to the different requirement.\r
+    //\r
+    if (IsDevicePathEnd (DevicePathNode)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+    //\r
+    // The PE image from unmeasured Firmware volume need be measured\r
+    // The PE image from measured Firmware volume will be mearsured according to policy below.\r
+    //   If it is driver, do not measure\r
+    //   If it is application, still measure.\r
+    //\r
+    ApplicationRequired = TRUE;\r
+\r
+    if (mTrEECacheMeasuredHandle != Handle && mTrEEMeasuredHobData != NULL) {\r
+      //\r
+      // Search for Root FV of this PE image\r
+      //\r
+      TempHandle = Handle;\r
+      do {\r
+        Status = gBS->HandleProtocol(\r
+                        TempHandle, \r
+                        &gEfiFirmwareVolumeBlockProtocolGuid,\r
+                        (VOID**)&FvbProtocol\r
+                        );\r
+        TempHandle = FvbProtocol->ParentHandle;\r
+      } while (!EFI_ERROR(Status) && FvbProtocol->ParentHandle != NULL);\r
+\r
+      //\r
+      // Search in measured FV Hob\r
+      //\r
+      Status = FvbProtocol->GetPhysicalAddress(FvbProtocol, &FvAddress);\r
+      if (EFI_ERROR(Status)){\r
+        return Status;\r
+      }\r
+\r
+      ApplicationRequired = FALSE;\r
+\r
+      for (Index = 0; Index < mTrEEMeasuredHobData->Num; Index++) {\r
+        if(mTrEEMeasuredHobData->MeasuredFvBuf[Index].BlobBase == FvAddress) {\r
+          //\r
+          // Cache measured FV for next measurement\r
+          //\r
+          mTrEECacheMeasuredHandle = Handle;\r
+          ApplicationRequired  = TRUE;\r
+          break;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // File is not found.\r
+  //\r
+  if (FileBuffer == NULL) {\r
+    Status = EFI_SECURITY_VIOLATION;\r
+    goto Finish;\r
+  }\r
+\r
+  mTrEEImageSize  = FileSize;\r
+  mTrEEFileBuffer = FileBuffer;\r
+\r
+  //\r
+  // Measure PE Image\r
+  //\r
+  DevicePathNode = OrigDevicePathNode;\r
+  ZeroMem (&ImageContext, sizeof (ImageContext));\r
+  ImageContext.Handle    = (VOID *) FileBuffer;\r
+  ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) DxeTpm2MeasureBootLibImageRead;\r
+\r
+  //\r
+  // Get information about the image being loaded\r
+  //\r
+  Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // The information can't be got from the invalid PeImage\r
+    //\r
+    goto Finish;\r
+  }\r
+  \r
+  //\r
+  // Measure only application if Application flag is set\r
+  // Measure drivers and applications if Application flag is not set\r
+  //\r
+  if ((!ApplicationRequired) || \r
+        (ApplicationRequired && ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) {  \r
+    //\r
+    // Print the image path to be measured.\r
+    //    \r
+    DEBUG_CODE_BEGIN ();\r
+      CHAR16                            *ToText;\r
+      ToText = ConvertDevicePathToText (\r
+                 DevicePathNode,\r
+                 FALSE,\r
+                 TRUE\r
+                 );\r
+      if (ToText != NULL) {\r
+        DEBUG ((DEBUG_INFO, "The measured image path is %s.\n", ToText));\r
+        FreePool (ToText);\r
+      }\r
+    DEBUG_CODE_END ();\r
+\r
+    //\r
+    // Measure PE image into TPM log.\r
+    //\r
+    Status = TrEEMeasurePeImage (\r
+               TreeProtocol,\r
+               (EFI_PHYSICAL_ADDRESS) (UINTN) FileBuffer, \r
+               FileSize, \r
+               (UINTN) ImageContext.ImageAddress, \r
+               ImageContext.ImageType, \r
+               DevicePathNode\r
+               );\r
+    DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler - TrEEMeasurePeImage - %r\n", Status));\r
+  }\r
+\r
+  //\r
+  // Done, free the allocated resource.\r
+  //\r
+Finish:\r
+  if (OrigDevicePathNode != NULL) {\r
+    FreePool (OrigDevicePathNode);\r
+  }\r
+\r
+  DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler - %r\n", Status));\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Register the security handler to provide TPM measure boot service.\r
+\r
+  @param  ImageHandle  ImageHandle of the loaded driver.\r
+  @param  SystemTable  Pointer to the EFI System Table.\r
+\r
+  @retval  EFI_SUCCESS            Register successfully.\r
+  @retval  EFI_OUT_OF_RESOURCES   No enough memory to register this handler.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DxeTpm2MeasureBootLibConstructor (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_HOB_GUID_TYPE  *GuidHob;\r
+\r
+  GuidHob = NULL;\r
+\r
+  GuidHob = GetFirstGuidHob (&gMeasuredFvHobGuid);\r
+\r
+  if (GuidHob != NULL) {\r
+    mTrEEMeasuredHobData = GET_GUID_HOB_DATA (GuidHob);\r
+  }\r
+\r
+  return RegisterSecurity2Handler (\r
+          DxeTpm2MeasureBootHandler,\r
+          EFI_AUTH_OPERATION_MEASURE_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED\r
+          );\r
+}\r