--- /dev/null
+/** @file\r
+ This driver verifies and reports OBB FVs.\r
+\r
+Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "FvReportPei.h"\r
+\r
+STATIC CONST HASH_ALG_INFO mHashAlgInfo[] = {\r
+ {TPM_ALG_SHA256, SHA256_DIGEST_SIZE, Sha256Init, Sha256Update, Sha256Final, Sha256HashAll}, // 000B\r
+ {TPM_ALG_SHA384, SHA384_DIGEST_SIZE, Sha384Init, Sha384Update, Sha384Final, Sha384HashAll}, // 000C\r
+ {TPM_ALG_SHA512, SHA512_DIGEST_SIZE, Sha512Init, Sha512Update, Sha512Final, Sha512HashAll}, // 000D\r
+};\r
+\r
+/**\r
+ Find hash algorithm information from mHashAlgInfo according to given ID.\r
+\r
+ @param[in] HashAlgoId Hash algorithm type id.\r
+\r
+ @retval Pointer to HASH_ALG_INFO if given hash algorithm is supported.\r
+ @retval NULL if given algorithm is not supported.\r
+**/\r
+STATIC\r
+CONST\r
+HASH_ALG_INFO *\r
+FindHashAlgInfo (\r
+ IN UINT16 HashAlgId\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < ARRAY_SIZE (mHashAlgInfo); ++Index) {\r
+ if (mHashAlgInfo[Index].HashAlgId == HashAlgId) {\r
+ return &mHashAlgInfo[Index];\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Install a EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI instance so that\r
+ TCG driver may use to extend PCRs.\r
+\r
+ @param[in] FvBuffer Buffer containing the whole FV.\r
+ @param[in] FvLength Length of the FV.\r
+ @param[in] HashAlgoId Hash algorithm type id.\r
+ @param[in] HashSize Hash size.\r
+ @param[in] HashValue Hash value buffer.\r
+**/\r
+STATIC\r
+VOID\r
+InstallPreHashFvPpi (\r
+ IN VOID *FvBuffer,\r
+ IN UINTN FvLength,\r
+ IN UINT16 HashAlgoId,\r
+ IN UINT16 HashSize,\r
+ IN UINT8 *HashValue\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PEI_PPI_DESCRIPTOR *FvInfoPpiDescriptor;\r
+ EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI *PreHashedFvPpi;\r
+ UINTN PpiSize;\r
+ HASH_INFO *HashInfo;\r
+\r
+ PpiSize = sizeof (EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI)\r
+ + sizeof (sizeof (HASH_INFO))\r
+ + HashSize;\r
+\r
+ PreHashedFvPpi = AllocatePool (PpiSize);\r
+ ASSERT (PreHashedFvPpi != NULL);\r
+\r
+ PreHashedFvPpi->FvBase = (UINT32)(UINTN)FvBuffer;\r
+ PreHashedFvPpi->FvLength = (UINT32)FvLength;\r
+ PreHashedFvPpi->Count = 1;\r
+\r
+ HashInfo = HASH_INFO_PTR (PreHashedFvPpi);\r
+ HashInfo->HashAlgoId = HashAlgoId;\r
+ HashInfo->HashSize = HashSize;\r
+ CopyMem (HASH_VALUE_PTR (HashInfo), HashValue, HashSize);\r
+\r
+ FvInfoPpiDescriptor = AllocatePool (sizeof (EFI_PEI_PPI_DESCRIPTOR));\r
+ ASSERT (FvInfoPpiDescriptor != NULL);\r
+\r
+ FvInfoPpiDescriptor->Guid = &gEdkiiPeiFirmwareVolumeInfoPrehashedFvPpiGuid;\r
+ FvInfoPpiDescriptor->Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST;\r
+ FvInfoPpiDescriptor->Ppi = (VOID *) PreHashedFvPpi;\r
+\r
+ Status = PeiServicesInstallPpi (FvInfoPpiDescriptor);\r
+ ASSERT_EFI_ERROR (Status);\r
+}\r
+\r
+/**\r
+ Calculate and verify hash value for given FV.\r
+\r
+ @param[in] HashInfo Hash information of the FV.\r
+ @param[in] FvInfo Information of FV used for verification.\r
+ @param[in] FvNumber Length of the FV.\r
+ @param[in] BootMode Length of the FV.\r
+\r
+ @retval EFI_SUCCESS The given FV is integrate.\r
+ @retval EFI_VOLUME_CORRUPTED The given FV is corrupted (hash mismatch).\r
+ @retval EFI_UNSUPPORTED The hash algorithm is not supported.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+VerifyHashedFv (\r
+ IN FV_HASH_INFO *HashInfo,\r
+ IN HASHED_FV_INFO *FvInfo,\r
+ IN UINTN FvNumber,\r
+ IN EFI_BOOT_MODE BootMode\r
+ )\r
+{\r
+ UINTN FvIndex;\r
+ CONST HASH_ALG_INFO *AlgInfo;\r
+ UINT8 *HashValue;\r
+ UINT8 *FvHashValue;\r
+ VOID *FvBuffer;\r
+ EFI_STATUS Status;\r
+\r
+ if (HashInfo == NULL ||\r
+ HashInfo->HashSize == 0 ||\r
+ HashInfo->HashAlgoId == TPM_ALG_NULL) {\r
+ DEBUG ((DEBUG_INFO, "Bypass FV hash verification\r\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ AlgInfo = FindHashAlgInfo (HashInfo->HashAlgoId);\r
+ if (AlgInfo == NULL || AlgInfo->HashSize != HashInfo->HashSize) {\r
+ DEBUG ((DEBUG_ERROR, "Unsupported or wrong hash algorithm: %04X (size=%d)\r\n",\r
+ HashInfo->HashAlgoId, HashInfo->HashSize));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ ASSERT (FvInfo != NULL);\r
+ ASSERT (FvNumber > 0);\r
+\r
+ //\r
+ // We need a hash value for each FV as well as one for all FVs.\r
+ //\r
+ HashValue = AllocateZeroPool (AlgInfo->HashSize * (FvNumber + 1));\r
+ ASSERT (HashValue != NULL);\r
+\r
+ //\r
+ // Calcuate hash value for each FV first.\r
+ //\r
+ FvHashValue = HashValue;\r
+ for (FvIndex = 0; FvIndex < FvNumber; ++FvIndex) {\r
+ //\r
+ // FV must be meant for verified boot and/or measured boot.\r
+ //\r
+ ASSERT ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_VERIFIED_BOOT) != 0 ||\r
+ (FvInfo[FvIndex].Flag & HASHED_FV_FLAG_MEASURED_BOOT) != 0);\r
+\r
+ //\r
+ // Skip any FV not meant for current boot mode.\r
+ //\r
+ if ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_SKIP_BOOT_MODE (BootMode)) != 0) {\r
+ DEBUG ((DEBUG_INFO, "Skip FV[%016lX] for boot mode[%d]\r\n",\r
+ FvInfo[FvIndex].Base, BootMode));\r
+ continue;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "Pre-hashed[alg=%04X,size=%d,flag=%016lX] FV: 0x%016lX (%08lX) (Flag=%016lX)\r\n",\r
+ HashInfo->HashAlgoId,\r
+ HashInfo->HashSize,\r
+ HashInfo->HashFlag,\r
+ FvInfo[FvIndex].Base,\r
+ FvInfo[FvIndex].Length,\r
+ FvInfo[FvIndex].Flag\r
+ ));\r
+\r
+ //\r
+ // Copy FV to permanent memory to avoid potential TOC/TOU.\r
+ //\r
+ FvBuffer = AllocatePages (EFI_SIZE_TO_PAGES((UINTN)FvInfo[FvIndex].Length));\r
+ ASSERT (FvBuffer != NULL);\r
+ CopyMem (FvBuffer, (CONST VOID *)(UINTN)FvInfo[FvIndex].Base, (UINTN)FvInfo[FvIndex].Length);\r
+\r
+ if (!AlgInfo->HashAll (FvBuffer, (UINTN)FvInfo[FvIndex].Length, FvHashValue)) {\r
+ Status = EFI_ABORTED;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Report the FV measurement.\r
+ //\r
+ if ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_MEASURED_BOOT) != 0) {\r
+ InstallPreHashFvPpi (\r
+ FvBuffer,\r
+ (UINTN)FvInfo[FvIndex].Length,\r
+ HashInfo->HashAlgoId,\r
+ HashInfo->HashSize,\r
+ FvHashValue\r
+ );\r
+ }\r
+\r
+ //\r
+ // Don't keep the hash value of current FV if we don't need to verify it.\r
+ //\r
+ if ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_VERIFIED_BOOT) != 0) {\r
+ FvHashValue += AlgInfo->HashSize;\r
+ }\r
+\r
+ //\r
+ // Use memory copy of the FV from now on.\r
+ //\r
+ FvInfo[FvIndex].Base = (UINT64)(UINTN)FvBuffer;\r
+ }\r
+\r
+ //\r
+ // Check final hash for all FVs.\r
+ //\r
+ if (FvHashValue == HashValue ||\r
+ (AlgInfo->HashAll (HashValue, FvHashValue - HashValue, FvHashValue) &&\r
+ CompareMem (HashInfo->Hash, FvHashValue, AlgInfo->HashSize) == 0)) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_VOLUME_CORRUPTED;\r
+ }\r
+\r
+Done:\r
+ FreePool (HashValue);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Report FV to PEI and/or DXE core for dispatch.\r
+\r
+ @param[in] FvInfo Information of a FV.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+ReportHashedFv (\r
+ IN HASHED_FV_INFO *FvInfo\r
+ )\r
+{\r
+ CONST EFI_GUID *FvFormat;\r
+\r
+ if ((FvInfo->Flag & HASHED_FV_FLAG_REPORT_FV_HOB) != 0) {\r
+ //\r
+ // Require DXE core to process this FV.\r
+ //\r
+ BuildFvHob (\r
+ (EFI_PHYSICAL_ADDRESS)FvInfo->Base,\r
+ FvInfo->Length\r
+ );\r
+ DEBUG ((DEBUG_INFO, "Reported FV HOB: %016lX (%08lX)\r\n", FvInfo->Base, FvInfo->Length));\r
+ }\r
+\r
+ if ((FvInfo->Flag & HASHED_FV_FLAG_REPORT_FV_INFO_PPI) != 0) {\r
+ //\r
+ // Require PEI core to process this FV.\r
+ //\r
+ FvFormat = &((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvInfo->Base)->FileSystemGuid;\r
+ PeiServicesInstallFvInfoPpi (\r
+ FvFormat,\r
+ (VOID *)(UINTN)FvInfo->Base,\r
+ (UINT32)FvInfo->Length,\r
+ NULL,\r
+ NULL\r
+ );\r
+ DEBUG ((DEBUG_INFO, "Reported FV PPI: %016lX (%08lX)\r\n", FvInfo->Base, FvInfo->Length));\r
+ }\r
+}\r
+\r
+/**\r
+ Verify and report pre-hashed FVs.\r
+\r
+ Doing this must be at post-memory to make sure there's enough memory to hold\r
+ all FVs to be verified. This is necessary for mitigating TOCTOU issue.\r
+\r
+ This function will never return if the verification is failed.\r
+\r
+ @param[in] StoredHashFvPpi Pointer to PPI containing hash information.\r
+ @param[in] BootMode Current boot mode.\r
+\r
+ @retval Pointer to structure containning valid hash information for current boot mode.\r
+ @retval NULL if there's no hash associated with current boot mode.\r
+**/\r
+STATIC\r
+FV_HASH_INFO *\r
+GetHashInfo (\r
+ IN EDKII_PEI_FIRMWARE_VOLUME_INFO_STORED_HASH_FV_PPI *StoredHashFvPpi,\r
+ IN EFI_BOOT_MODE BootMode\r
+ )\r
+{\r
+ FV_HASH_INFO *HashInfo;\r
+\r
+ if ((StoredHashFvPpi->HashInfo.HashFlag & FV_HASH_FLAG_BOOT_MODE (BootMode)) != 0) {\r
+ HashInfo = &StoredHashFvPpi->HashInfo;\r
+ } else {\r
+ HashInfo = NULL;\r
+ }\r
+\r
+ return HashInfo;\r
+}\r
+\r
+/**\r
+ Verify and report pre-hashed FVs.\r
+\r
+ Doing this must be at post-memory to make sure there's enough memory to hold\r
+ all FVs to be verified. This is necessary for mitigating TOCTOU issue.\r
+\r
+ This function will never return if the verification is failed.\r
+\r
+ @param[in] PeiServices General purpose services available to every PEIM.\r
+ @param[in] BootMode Current boot mode.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+CheckStoredHashFv (\r
+ IN CONST EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_BOOT_MODE BootMode\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EDKII_PEI_FIRMWARE_VOLUME_INFO_STORED_HASH_FV_PPI *StoredHashFvPpi;\r
+ FV_HASH_INFO *HashInfo;\r
+ UINTN FvIndex;\r
+\r
+ //\r
+ // Check pre-hashed FV list\r
+ //\r
+ StoredHashFvPpi = NULL;\r
+ Status = PeiServicesLocatePpi (\r
+ &gEdkiiPeiFirmwareVolumeInfoStoredHashFvPpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID**)&StoredHashFvPpi\r
+ );\r
+ if (!EFI_ERROR(Status) && StoredHashFvPpi != NULL && StoredHashFvPpi->FvNumber > 0) {\r
+\r
+ HashInfo = GetHashInfo (StoredHashFvPpi, BootMode);\r
+ Status = VerifyHashedFv (HashInfo, StoredHashFvPpi->FvInfo,\r
+ StoredHashFvPpi->FvNumber, BootMode);\r
+ if (!EFI_ERROR (Status)) {\r
+\r
+ //\r
+ // Report the FVs to PEI core and/or DXE core.\r
+ //\r
+ for (FvIndex = 0; FvIndex < StoredHashFvPpi->FvNumber; ++FvIndex) {\r
+ if ((StoredHashFvPpi->FvInfo[FvIndex].Flag\r
+ & HASHED_FV_FLAG_SKIP_BOOT_MODE (BootMode)) == 0) {\r
+ ReportHashedFv (&StoredHashFvPpi->FvInfo[FvIndex]);\r
+ }\r
+ }\r
+\r
+ REPORT_STATUS_CODE (\r
+ EFI_PROGRESS_CODE,\r
+ PcdGet32 (PcdStatusCodeFvVerificationPass)\r
+ );\r
+\r
+ } else {\r
+\r
+ DEBUG ((DEBUG_ERROR, "ERROR: Failed to verify OBB FVs (%r)\r\n", Status));\r
+\r
+ REPORT_STATUS_CODE_EX (\r
+ EFI_PROGRESS_CODE,\r
+ PcdGet32 (PcdStatusCodeFvVerificationFail),\r
+ 0,\r
+ NULL,\r
+ &gEdkiiPeiFirmwareVolumeInfoStoredHashFvPpiGuid,\r
+ StoredHashFvPpi,\r
+ sizeof (*StoredHashFvPpi)\r
+ );\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ DEBUG ((DEBUG_ERROR, "ERROR: No/invalid StoredHashFvPpi located\r\n"));\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+ ASSERT (StoredHashFvPpi != NULL && StoredHashFvPpi->FvNumber > 0);\r
+\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Main entry for FvReport PEIM.\r
+\r
+ @param[in] FileHandle Handle of the file being invoked.\r
+ @param[in] PeiServices Pointer to PEI Services table.\r
+\r
+ @retval EFI_SUCCESS If all FVs reported by StoredHashFvPpi are verified.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvReportEntryPoint (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MODE BootMode;\r
+\r
+ Status = PeiServicesGetBootMode (&BootMode);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = CheckStoredHashFv (PeiServices, BootMode);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Never pass control to left part of BIOS if any error.\r
+ //\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+ Definitions for OBB FVs verification.\r
+\r
+Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#ifndef __FV_REPORT_PEI_H__\r
+#define __FV_REPORT_PEI_H__\r
+\r
+#include <PiPei.h>\r
+\r
+#include <IndustryStandard/Tpm20.h>\r
+\r
+#include <Ppi/FirmwareVolumeInfoStoredHashFv.h>\r
+\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/BaseCryptLib.h>\r
+#include <Library/ReportStatusCodeLib.h>\r
+\r
+#define HASH_INFO_PTR(PreHashedFvPpi) \\r
+ (HASH_INFO *)((UINT8 *)(PreHashedFvPpi) + sizeof (EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI))\r
+\r
+#define HASH_VALUE_PTR(HashInfo) \\r
+ (VOID *)((UINT8 *)(HashInfo) + sizeof (HASH_INFO))\r
+\r
+/**\r
+ Computes the message digest of a input data buffer.\r
+\r
+ This function performs message digest of a given data buffer, and places\r
+ the digest value into the specified memory.\r
+\r
+ If this interface is not supported, then return FALSE.\r
+\r
+ @param[in] Data Pointer to the buffer containing the data to be hashed.\r
+ @param[in] DataSize Size of Data buffer in bytes.\r
+ @param[out] HashValue Pointer to a buffer that receives digest value.\r
+\r
+ @retval TRUE The digest computation succeeded.\r
+ @retval FALSE The digest computation failed.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *HASH_ALL_METHOD) (\r
+ IN CONST VOID *Data,\r
+ IN UINTN DataSize,\r
+ OUT UINT8 *HashValue\r
+ );\r
+\r
+/**\r
+ Initializes user-supplied memory as hash context for subsequent use.\r
+\r
+ @param[out] HashContext Pointer to hash context being initialized.\r
+\r
+ @retval TRUE Hash context initialization succeeded.\r
+ @retval FALSE Hash context initialization failed.\r
+ @retval FALSE This interface is not supported.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *HASH_INIT_METHOD) (\r
+ OUT VOID *HashContext\r
+ );\r
+\r
+/**\r
+ Digests the input data and updates hash context.\r
+\r
+ @param[in, out] HashContext Pointer to the hash context.\r
+ @param[in] Data Pointer to the buffer containing the data to be hashed.\r
+ @param[in] DataSize Size of Data buffer in bytes.\r
+\r
+ @retval TRUE Hash data digest succeeded.\r
+ @retval FALSE Hash data digest failed.\r
+ @retval FALSE This interface is not supported.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *HASH_UPDATE_METHOD) (\r
+ IN OUT VOID *HashContext,\r
+ IN CONST VOID *Data,\r
+ IN UINTN DataSize\r
+ );\r
+\r
+/**\r
+ Completes computation of the hash digest value.\r
+\r
+ @param[in, out] HashContext Pointer to the hash context.\r
+ @param[out] HashValue Pointer to a buffer that receives the hash digest\r
+ value.\r
+\r
+ @retval TRUE Hash digest computation succeeded.\r
+ @retval FALSE Hash digest computation failed.\r
+ @retval FALSE This interface is not supported.\r
+\r
+**/\r
+typedef\r
+BOOLEAN\r
+(EFIAPI *HASH_FINAL_METHOD) (\r
+ IN OUT VOID *HashContext,\r
+ OUT UINT8 *HashValue\r
+ );\r
+\r
+typedef struct {\r
+ UINT16 HashAlgId;\r
+ UINTN HashSize;\r
+ HASH_INIT_METHOD HashInit;\r
+ HASH_UPDATE_METHOD HashUpdate;\r
+ HASH_FINAL_METHOD HashFinal;\r
+ HASH_ALL_METHOD HashAll;\r
+} HASH_ALG_INFO;\r
+\r
+#endif //__FV_REPORT_PEI_H__\r
+\r