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