[edk2-devel] [PATCH V1 2/7] OvmfPkg/IntelTdx: Add TdxHelperLib

Min Xu min.m.xu at intel.com
Tue Jan 17 07:40:11 UTC 2023


From: Min M Xu <min.m.xu at intel.com>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4243

TdxHelperLib provides below helper functions for a td-guest.
 - TdxHelperProcessTdHob
 - TdxHelperMeasureTdHob
 - TdxHelperMeasureCfvImage
 - TdxHelperBuildGuidHobForTdxMeasurement

TdxHelperLibNull is the NULL instance of TdxHelperLib.

SecTdxHelperLib is the SEC instance of TdxHelperLib. It implements 4
functions for tdx in SEC phase:
 - TdxHelperProcessTdHob consumes TdHob to accept un-accepted memories.
   Before the TdHob is consumed, it is first validated. Most of the codes
   are copied from PlatformInitLib/IntelTdx.c. The duplicated code in
   PlatformInitLib/IntelTdx.c will be deleted later so that the build
   will not be broken.

 - TdxHelperMeasureTdHob measure/extend TdHob and store the measurement
   value in workarea.

 - TdxHelperMeasureCfvImage measure/extend the Configuration FV image and
   store the measurement value in workarea.

 - TdxHelperBuildGuidHobForTdxMeasurement is only valid in PEI-LESS
   startup mode. It builds GuidHob for tdx measurement in SEC phase.

PeiTdxHelperLib is the PEI instance of TdxHelperLib. It implements 1
function for tdx in PEI phase.
  - TdxHelperBuildGuidHobForTdxMeasurement builds GuidHob for tdx
    measurement in PEI phase.

Cc: Erdem Aktas <erdemaktas at google.com>
Cc: James Bottomley <jejb at linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao at intel.com>
Cc: Gerd Hoffmann <kraxel at redhat.com>
Cc: Tom Lendacky <thomas.lendacky at amd.com>
Cc: Michael Roth <michael.roth at amd.com>
Signed-off-by: Min Xu <min.m.xu at intel.com>
---
 OvmfPkg/Include/Library/TdxHelperLib.h        |  70 ++
 OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelper.c  |  91 ++
 .../IntelTdx/TdxHelperLib/PeiTdxHelperLib.inf |  48 +
 OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c  | 989 ++++++++++++++++++
 .../IntelTdx/TdxHelperLib/SecTdxHelperLib.inf |  53 +
 .../TdxHelperLib/TdxHelperLibNull.inf         |  32 +
 OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperNull.c |  79 ++
 .../IntelTdx/TdxHelperLib/TdxMeasurementHob.c | 266 +++++
 OvmfPkg/OvmfPkg.dec                           |   4 +
 9 files changed, 1632 insertions(+)
 create mode 100644 OvmfPkg/Include/Library/TdxHelperLib.h
 create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelper.c
 create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelperLib.inf
 create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c
 create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelperLib.inf
 create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperLibNull.inf
 create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperNull.c
 create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/TdxMeasurementHob.c

diff --git a/OvmfPkg/Include/Library/TdxHelperLib.h b/OvmfPkg/Include/Library/TdxHelperLib.h
new file mode 100644
index 000000000000..199aade42f8e
--- /dev/null
+++ b/OvmfPkg/Include/Library/TdxHelperLib.h
@@ -0,0 +1,70 @@
+/** @file
+  TdxHelperLib header file
+
+  Copyright (c) 2021 - 2023, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TDX_HELPER_LIB_H
+#define TDX_HELPER_LIB_H
+
+#include <PiPei.h>
+
+/**
+  In Tdx guest, some information need to be passed from host VMM to guest
+  firmware. For example, the memory resource, etc. These information are
+  prepared by host VMM and put in TdHob which is described in TdxMetadata.
+  TDVF processes the TdHob to accept memories.
+
+  @retval   EFI_SUCCESS   Successfully process the TdHob
+  @retval   Others        Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperProcessTdHob (
+  VOID
+  );
+
+/**
+  In Tdx guest, TdHob is passed from host VMM to guest firmware and it contains
+  the information of the memory resource. From the security perspective before
+  it is consumed, it should be measured and extended.
+ *
+ * @retval EFI_SUCCESS Successfully measure the TdHob
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureTdHob (
+  VOID
+  );
+
+/**
+ * In Tdx guest, Configuration FV (CFV) is treated as external input because it
+ * may contain the data provided by VMM. From the sucurity perspective Cfv image
+ * should be measured before it is consumed.
+ *
+ * @retval EFI_SUCCESS Successfully measure the CFV image
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureCfvImage (
+  VOID
+  );
+
+/**
+  Build the GuidHob for tdx measurements which were done in SEC phase.
+  The measurement values are stored in WorkArea.
+
+  @retval EFI_SUCCESS  The GuidHob is built successfully
+  @retval Others       Other errors as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperBuildGuidHobForTdxMeasurement (
+  VOID
+  );
+
+#endif
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelper.c b/OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelper.c
new file mode 100644
index 000000000000..91ab53ed14ad
--- /dev/null
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelper.c
@@ -0,0 +1,91 @@
+/** @file
+  TdxHelper Functions which are used in PEI phase
+
+  Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <PiPei.h>
+
+/**
+  Build the GuidHob for tdx measurements which were done in SEC phase.
+  The measurement values are stored in WorkArea.
+
+  @retval EFI_SUCCESS  The GuidHob is built successfully
+  @retval Others       Other errors as indicated
+**/
+EFI_STATUS
+InternalBuildGuidHobForTdxMeasurement (
+  VOID
+  );
+
+/**
+  In Tdx guest, some information need to be passed from host VMM to guest
+  firmware. For example, the memory resource, etc. These information are
+  prepared by host VMM and put in TdHob which is described in TdxMetadata.
+  TDVF processes the TdHob to accept memories.
+
+  @retval   EFI_SUCCESS   Successfully process the TdHob
+  @retval   Others        Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperProcessTdHob (
+  VOID
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  In Tdx guest, TdHob is passed from host VMM to guest firmware and it contains
+  the information of the memory resource. From the security perspective before
+  it is consumed, it should be measured and extended.
+ *
+ * @retval EFI_SUCCESS Successfully measure the TdHob
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureTdHob (
+  VOID
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+ * In Tdx guest, Configuration FV (CFV) is treated as external input because it
+ * may contain the data provided by VMM. From the sucurity perspective Cfv image
+ * should be measured before it is consumed.
+ *
+ * @retval EFI_SUCCESS Successfully measure the CFV image
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureCfvImage (
+  VOID
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Build the GuidHob for tdx measurements which were done in SEC phase.
+  The measurement values are stored in WorkArea.
+
+  @retval EFI_SUCCESS  The GuidHob is built successfully
+  @retval Others       Other errors as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperBuildGuidHobForTdxMeasurement (
+  VOID
+  )
+{
+  return InternalBuildGuidHobForTdxMeasurement ();
+}
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelperLib.inf b/OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelperLib.inf
new file mode 100644
index 000000000000..ad3b6c1da62b
--- /dev/null
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/PeiTdxHelperLib.inf
@@ -0,0 +1,48 @@
+## @file
+#  TdxHelperLib PEI instance
+#
+#  This module provides Tdx helper functions in PEI phase.
+#  Copyright (c) 2021 - 2023, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PeiTdxHelperLib
+  FILE_GUID                      = 4d22289d-3bde-4501-a737-7719f3215065
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = TdxHelperLib|PEIM
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources]
+  PeiTdxHelper.c
+  TdxMeasurementHob.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  SecurityPkg/SecurityPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  HobLib
+  PcdLib
+
+[FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize
+
+[Guids]
+  gCcEventEntryHobGuid
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c b/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c
new file mode 100644
index 000000000000..67fc02e09292
--- /dev/null
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c
@@ -0,0 +1,989 @@
+/** @file
+  TdxHelper Functions which are used in SEC phase
+
+  Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseCryptLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <IndustryStandard/IntelTdx.h>
+#include <IndustryStandard/Tpm20.h>
+#include <Library/TdxLib.h>
+#include <Library/TdxMailboxLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Pi/PrePiHob.h>
+#include <WorkArea.h>
+#include <ConfidentialComputingGuestAttr.h>
+#include <Library/TdxHelperLib.h>
+
+#define ALIGNED_2MB_MASK  0x1fffff
+#define MEGABYTE_SHIFT    20
+
+#define ACCEPT_CHUNK_SIZE  SIZE_32MB
+#define AP_STACK_SIZE      SIZE_16KB
+#define APS_STACK_SIZE(CpusNum)  (ALIGN_VALUE(CpusNum*AP_STACK_SIZE, SIZE_2MB))
+
+/**
+  Build the GuidHob for tdx measurements which were done in SEC phase.
+  The measurement values are stored in WorkArea.
+
+  @retval EFI_SUCCESS  The GuidHob is built successfully
+  @retval Others       Other errors as indicated
+**/
+EFI_STATUS
+InternalBuildGuidHobForTdxMeasurement (
+  VOID
+  );
+
+/**
+  This function will be called to accept pages. Only BSP accepts pages.
+
+  TDCALL(ACCEPT_PAGE) supports the accept page size of 4k and 2M. To
+  simplify the implementation, the Memory to be accpeted is splitted
+  into 3 parts:
+  -----------------  <-- StartAddress1 (not 2M aligned)
+  |  part 1       |      Length1 < 2M
+  |---------------|  <-- StartAddress2 (2M aligned)
+  |               |      Length2 = Integer multiples of 2M
+  |  part 2       |
+  |               |
+  |---------------|  <-- StartAddress3
+  |  part 3       |      Length3 < 2M
+  |---------------|
+
+  @param[in] PhysicalAddress   Start physical adress
+  @param[in] PhysicalEnd       End physical address
+
+  @retval    EFI_SUCCESS       Accept memory successfully
+  @retval    Others            Other errors as indicated
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+BspAcceptMemoryResourceRange (
+  IN EFI_PHYSICAL_ADDRESS  PhysicalAddress,
+  IN EFI_PHYSICAL_ADDRESS  PhysicalEnd
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      AcceptPageSize;
+  UINT64      StartAddress1;
+  UINT64      StartAddress2;
+  UINT64      StartAddress3;
+  UINT64      TotalLength;
+  UINT64      Length1;
+  UINT64      Length2;
+  UINT64      Length3;
+  UINT64      Pages;
+
+  AcceptPageSize = FixedPcdGet32 (PcdTdxAcceptPageSize);
+  TotalLength    = PhysicalEnd - PhysicalAddress;
+  StartAddress1  = 0;
+  StartAddress2  = 0;
+  StartAddress3  = 0;
+  Length1        = 0;
+  Length2        = 0;
+  Length3        = 0;
+
+  if (TotalLength == 0) {
+    return EFI_SUCCESS;
+  }
+
+  if (ALIGN_VALUE (PhysicalAddress, SIZE_2MB) != PhysicalAddress) {
+    StartAddress1 = PhysicalAddress;
+    Length1       = ALIGN_VALUE (PhysicalAddress, SIZE_2MB) - PhysicalAddress;
+    if (Length1 >= TotalLength) {
+      Length1 = TotalLength;
+    }
+
+    PhysicalAddress += Length1;
+    TotalLength     -= Length1;
+  }
+
+  if (TotalLength > SIZE_2MB) {
+    StartAddress2    = PhysicalAddress;
+    Length2          = TotalLength & ~(UINT64)ALIGNED_2MB_MASK;
+    PhysicalAddress += Length2;
+    TotalLength     -= Length2;
+  }
+
+  if (TotalLength) {
+    StartAddress3 = PhysicalAddress;
+    Length3       = TotalLength;
+  }
+
+  Status = EFI_SUCCESS;
+  if (Length1 > 0) {
+    Pages  = Length1 / SIZE_4KB;
+    Status = TdAcceptPages (StartAddress1, Pages, SIZE_4KB);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  if (Length2 > 0) {
+    Pages  = Length2 / AcceptPageSize;
+    Status = TdAcceptPages (StartAddress2, Pages, AcceptPageSize);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  if (Length3 > 0) {
+    Pages  = Length3 / SIZE_4KB;
+    Status = TdAcceptPages (StartAddress3, Pages, SIZE_4KB);
+    ASSERT (!EFI_ERROR (Status));
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  return Status;
+}
+
+/**
+ * This function is called by BSP and APs to accept memory.
+ * Note:
+ * The input PhysicalStart/PhysicalEnd indicates the whole memory region
+ * to be accepted. BSP or AP only accepts one piece in the whole memory region.
+ *
+ * @param CpuIndex        vCPU index
+ * @param CpusNum         Total vCPU number of a Tdx guest
+ * @param PhysicalStart   Start address of a memory region which is to be accepted
+ * @param PhysicalEnd     End address of a memory region which is to be accepted
+ *
+ * @retval EFI_SUCCESS    Successfully accept the memory
+ * @retval Other          Other errors as indicated
+ */
+STATIC
+EFI_STATUS
+EFIAPI
+BspApAcceptMemoryResourceRange (
+  UINT32                CpuIndex,
+  UINT32                CpusNum,
+  EFI_PHYSICAL_ADDRESS  PhysicalStart,
+  EFI_PHYSICAL_ADDRESS  PhysicalEnd
+  )
+{
+  UINT64                Status;
+  UINT64                Pages;
+  UINT64                Stride;
+  UINT64                AcceptPageSize;
+  EFI_PHYSICAL_ADDRESS  PhysicalAddress;
+
+  AcceptPageSize = (UINT64)(UINTN)FixedPcdGet32 (PcdTdxAcceptPageSize);
+
+  Status          = EFI_SUCCESS;
+  Stride          = (UINTN)CpusNum * ACCEPT_CHUNK_SIZE;
+  PhysicalAddress = PhysicalStart + ACCEPT_CHUNK_SIZE * (UINTN)CpuIndex;
+
+  while (!EFI_ERROR (Status) && PhysicalAddress < PhysicalEnd) {
+    Pages  = MIN (ACCEPT_CHUNK_SIZE, PhysicalEnd - PhysicalAddress) / AcceptPageSize;
+    Status = TdAcceptPages (PhysicalAddress, Pages, (UINT32)(UINTN)AcceptPageSize);
+    ASSERT (!EFI_ERROR (Status));
+    PhysicalAddress += Stride;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+ * This function is called by APs to accept memory.
+ *
+ * @param CpuIndex        vCPU index of an AP
+ * @param PhysicalStart   Start address of a memory region which is to be accepted
+ * @param PhysicalEnd     End address of a memory region which is to be accepted
+ *
+ * @retval EFI_SUCCESS    Successfully accept the memory
+ * @retval Others         Other errors as indicated
+ */
+STATIC
+EFI_STATUS
+EFIAPI
+ApAcceptMemoryResourceRange (
+  UINT32                CpuIndex,
+  EFI_PHYSICAL_ADDRESS  PhysicalStart,
+  EFI_PHYSICAL_ADDRESS  PhysicalEnd
+  )
+{
+  UINT64          Status;
+  TD_RETURN_DATA  TdReturnData;
+
+  Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+  if (Status != TDX_EXIT_REASON_SUCCESS) {
+    ASSERT (FALSE);
+    return EFI_ABORTED;
+  }
+
+  if ((CpuIndex == 0) || (CpuIndex >= TdReturnData.TdInfo.NumVcpus)) {
+    ASSERT (FALSE);
+    return EFI_ABORTED;
+  }
+
+  return BspApAcceptMemoryResourceRange (CpuIndex, TdReturnData.TdInfo.NumVcpus, PhysicalStart, PhysicalEnd);
+}
+
+/**
+ * This function is called by BSP. It coordinates BSP/APs to accept memory together.
+ *
+ * @param PhysicalStart     Start address of a memory region which is to be accepted
+ * @param PhysicalEnd       End address of a memory region which is to be accepted
+ * @param APsStackAddress   APs stack address
+ * @param CpusNum           Total vCPU number of the Tdx guest
+ *
+ * @retval EFI_SUCCESS      Successfully accept the memory
+ * @retval Others           Other errors as indicated
+ */
+STATIC
+EFI_STATUS
+EFIAPI
+MpAcceptMemoryResourceRange (
+  IN EFI_PHYSICAL_ADDRESS      PhysicalStart,
+  IN EFI_PHYSICAL_ADDRESS      PhysicalEnd,
+  IN OUT EFI_PHYSICAL_ADDRESS  APsStackAddress,
+  IN UINT32                    CpusNum
+  )
+{
+  UINT64      Length;
+  EFI_STATUS  Status;
+
+  Length = PhysicalEnd - PhysicalStart;
+
+  DEBUG ((DEBUG_INFO, "MpAccept : 0x%llx - 0x%llx (0x%llx)\n", PhysicalStart, PhysicalEnd, Length));
+
+  if (Length == 0) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // The start address is not 2M aligned. BSP first accept the part which is not 2M aligned.
+  //
+  if (ALIGN_VALUE (PhysicalStart, SIZE_2MB) != PhysicalStart) {
+    Length = MIN (ALIGN_VALUE (PhysicalStart, SIZE_2MB) - PhysicalStart, Length);
+    Status = BspAcceptMemoryResourceRange (PhysicalStart, PhysicalStart + Length);
+    ASSERT (Status == EFI_SUCCESS);
+
+    PhysicalStart += Length;
+    Length         = PhysicalEnd - PhysicalStart;
+  }
+
+  if (Length == 0) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // BSP will accept the memory by itself if the memory is not big enough compared with a chunk.
+  //
+  if (Length <= ACCEPT_CHUNK_SIZE) {
+    return BspAcceptMemoryResourceRange (PhysicalStart, PhysicalEnd);
+  }
+
+  //
+  // Now APs are asked to accept the memory together.
+  //
+  MpSerializeStart ();
+
+  MpSendWakeupCommand (
+    MpProtectedModeWakeupCommandAcceptPages,
+    (UINT64)(UINTN)ApAcceptMemoryResourceRange,
+    PhysicalStart,
+    PhysicalEnd,
+    APsStackAddress,
+    AP_STACK_SIZE
+    );
+
+  //
+  // Now BSP does its job.
+  //
+  BspApAcceptMemoryResourceRange (0, CpusNum, PhysicalStart, PhysicalEnd);
+
+  MpSerializeEnd ();
+
+  return EFI_SUCCESS;
+}
+
+/**
+  BSP accept a small piece of memory which will be used as APs stack.
+
+  @param[in] VmmHobList    The Hoblist pass the firmware
+  @param[in] APsStackSize  APs stack size
+  @param[out] PhysicalAddressEnd    The physical end address of accepted memory in phase-1
+
+  @retval  EFI_SUCCESS     Process the HobList successfully
+  @retval  Others          Other errors as indicated
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AcceptMemoryForAPsStack (
+  IN CONST VOID             *VmmHobList,
+  IN UINT32                 APsStackSize,
+  OUT EFI_PHYSICAL_ADDRESS  *PhysicalAddressEnd
+  )
+{
+  EFI_STATUS            Status;
+  EFI_PEI_HOB_POINTERS  Hob;
+  EFI_PHYSICAL_ADDRESS  PhysicalEnd;
+  EFI_PHYSICAL_ADDRESS  PhysicalStart;
+  UINT64                ResourceLength;
+  BOOLEAN               MemoryRegionFound;
+
+  ASSERT (VmmHobList != NULL);
+
+  Status            = EFI_SUCCESS;
+  Hob.Raw           = (UINT8 *)VmmHobList;
+  MemoryRegionFound = FALSE;
+
+  DEBUG ((DEBUG_INFO, "AcceptMemoryForAPsStack with APsStackSize=0x%x\n", APsStackSize));
+
+  //
+  // Parse the HOB list until end of list or matching type is found.
+  //
+  while (!END_OF_HOB_LIST (Hob) && !MemoryRegionFound) {
+    if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
+      DEBUG ((DEBUG_INFO, "\nResourceType: 0x%x\n", Hob.ResourceDescriptor->ResourceType));
+
+      if (Hob.ResourceDescriptor->ResourceType == BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED) {
+        ResourceLength = Hob.ResourceDescriptor->ResourceLength;
+        PhysicalStart  = Hob.ResourceDescriptor->PhysicalStart;
+        PhysicalEnd    = PhysicalStart + ResourceLength;
+
+        DEBUG ((DEBUG_INFO, "ResourceAttribute: 0x%x\n", Hob.ResourceDescriptor->ResourceAttribute));
+        DEBUG ((DEBUG_INFO, "PhysicalStart: 0x%llx\n", PhysicalStart));
+        DEBUG ((DEBUG_INFO, "ResourceLength: 0x%llx\n", ResourceLength));
+        DEBUG ((DEBUG_INFO, "Owner: %g\n\n", &Hob.ResourceDescriptor->Owner));
+
+        if (ResourceLength >= APsStackSize) {
+          MemoryRegionFound = TRUE;
+          if (ResourceLength > ACCEPT_CHUNK_SIZE) {
+            PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + APsStackSize;
+          }
+        }
+
+        Status = BspAcceptMemoryResourceRange (
+                   Hob.ResourceDescriptor->PhysicalStart,
+                   PhysicalEnd
+                   );
+        if (EFI_ERROR (Status)) {
+          break;
+        }
+      }
+    }
+
+    Hob.Raw = GET_NEXT_HOB (Hob);
+  }
+
+  ASSERT (MemoryRegionFound);
+  *PhysicalAddressEnd = PhysicalEnd;
+
+  return Status;
+}
+
+/**
+  BSP and APs work togeter to accept memory which is under the address of 4G.
+
+  @param[in] VmmHobList           The Hoblist pass the firmware
+  @param[in] CpusNum              Number of vCPUs
+  @param[in] APsStackStartAddres  Start address of APs stack
+  @param[in] PhysicalAddressStart Start physical address which to be accepted
+
+  @retval  EFI_SUCCESS     Process the HobList successfully
+  @retval  Others          Other errors as indicated
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AcceptMemory (
+  IN CONST VOID            *VmmHobList,
+  IN UINT32                CpusNum,
+  IN EFI_PHYSICAL_ADDRESS  APsStackStartAddress,
+  IN EFI_PHYSICAL_ADDRESS  PhysicalAddressStart
+  )
+{
+  EFI_STATUS            Status;
+  EFI_PEI_HOB_POINTERS  Hob;
+  EFI_PHYSICAL_ADDRESS  PhysicalStart;
+  EFI_PHYSICAL_ADDRESS  PhysicalEnd;
+  EFI_PHYSICAL_ADDRESS  AcceptMemoryEndAddress;
+
+  Status                 = EFI_SUCCESS;
+  AcceptMemoryEndAddress = BASE_4GB;
+
+  ASSERT (VmmHobList != NULL);
+  Hob.Raw = (UINT8 *)VmmHobList;
+
+  DEBUG ((DEBUG_INFO, "AcceptMemory under address of 4G\n"));
+
+  //
+  // Parse the HOB list until end of list or matching type is found.
+  //
+  while (!END_OF_HOB_LIST (Hob)) {
+    if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
+      if (Hob.ResourceDescriptor->ResourceType == BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED) {
+        PhysicalStart = Hob.ResourceDescriptor->PhysicalStart;
+        PhysicalEnd   = PhysicalStart + Hob.ResourceDescriptor->ResourceLength;
+
+        if (PhysicalEnd <= PhysicalAddressStart) {
+          // this memory region has been accepted. Skipped it.
+          Hob.Raw = GET_NEXT_HOB (Hob);
+          continue;
+        }
+
+        if (PhysicalStart >= AcceptMemoryEndAddress) {
+          // this memory region is not to be accepted. And we're done.
+          break;
+        }
+
+        if (PhysicalStart >= PhysicalAddressStart) {
+          // this memory region has not been acceted.
+        } else if ((PhysicalStart < PhysicalAddressStart) && (PhysicalEnd > PhysicalAddressStart)) {
+          // part of the memory region has been accepted.
+          PhysicalStart = PhysicalAddressStart;
+        }
+
+        // then compare the PhysicalEnd with AcceptMemoryEndAddress
+        if (PhysicalEnd >= AcceptMemoryEndAddress) {
+          PhysicalEnd = AcceptMemoryEndAddress;
+        }
+
+        DEBUG ((DEBUG_INFO, "ResourceAttribute: 0x%x\n", Hob.ResourceDescriptor->ResourceAttribute));
+        DEBUG ((DEBUG_INFO, "PhysicalStart: 0x%llx\n", Hob.ResourceDescriptor->PhysicalStart));
+        DEBUG ((DEBUG_INFO, "ResourceLength: 0x%llx\n", Hob.ResourceDescriptor->ResourceLength));
+        DEBUG ((DEBUG_INFO, "Owner: %g\n\n", &Hob.ResourceDescriptor->Owner));
+
+        // Now we're ready to accept memory [PhysicalStart, PhysicalEnd)
+        if (CpusNum == 1) {
+          Status = BspAcceptMemoryResourceRange (PhysicalStart, PhysicalEnd);
+        } else {
+          Status = MpAcceptMemoryResourceRange (
+                     PhysicalStart,
+                     PhysicalEnd,
+                     APsStackStartAddress,
+                     CpusNum
+                     );
+        }
+
+        if (EFI_ERROR (Status)) {
+          ASSERT (FALSE);
+          break;
+        }
+
+        if (PhysicalEnd == AcceptMemoryEndAddress) {
+          break;
+        }
+      }
+    }
+
+    Hob.Raw = GET_NEXT_HOB (Hob);
+  }
+
+  return Status;
+}
+
+/**
+  Check the value whether in the valid list.
+
+  @param[in] Value             A value
+  @param[in] ValidList         A pointer to valid list
+  @param[in] ValidListLength   Length of valid list
+
+  @retval  TRUE   The value is in valid list.
+  @retval  FALSE  The value is not in valid list.
+
+**/
+STATIC
+BOOLEAN
+EFIAPI
+IsInValidList (
+  IN UINT32  Value,
+  IN UINT32  *ValidList,
+  IN UINT32  ValidListLength
+  )
+{
+  UINT32  index;
+
+  if (ValidList == NULL) {
+    return FALSE;
+  }
+
+  for (index = 0; index < ValidListLength; index++) {
+    if (ValidList[index] == Value) {
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+  Check the integrity of VMM Hob List.
+
+  @param[in] VmmHobList   A pointer to Hob List
+
+  @retval  TRUE     The Hob List is valid.
+  @retval  FALSE    The Hob List is invalid.
+
+**/
+STATIC
+BOOLEAN
+EFIAPI
+ValidateHobList (
+  IN CONST VOID  *VmmHobList
+  )
+{
+  EFI_PEI_HOB_POINTERS  Hob;
+  UINT32                EFI_BOOT_MODE_LIST[] = {
+    BOOT_WITH_FULL_CONFIGURATION,
+    BOOT_WITH_MINIMAL_CONFIGURATION,
+    BOOT_ASSUMING_NO_CONFIGURATION_CHANGES,
+    BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS,
+    BOOT_WITH_DEFAULT_SETTINGS,
+    BOOT_ON_S4_RESUME,
+    BOOT_ON_S5_RESUME,
+    BOOT_WITH_MFG_MODE_SETTINGS,
+    BOOT_ON_S2_RESUME,
+    BOOT_ON_S3_RESUME,
+    BOOT_ON_FLASH_UPDATE,
+    BOOT_IN_RECOVERY_MODE
+  };
+
+  UINT32  EFI_RESOURCE_TYPE_LIST[] = {
+    EFI_RESOURCE_SYSTEM_MEMORY,
+    EFI_RESOURCE_MEMORY_MAPPED_IO,
+    EFI_RESOURCE_IO,
+    EFI_RESOURCE_FIRMWARE_DEVICE,
+    EFI_RESOURCE_MEMORY_MAPPED_IO_PORT,
+    EFI_RESOURCE_MEMORY_RESERVED,
+    EFI_RESOURCE_IO_RESERVED,
+    BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED
+  };
+
+  if (VmmHobList == NULL) {
+    DEBUG ((DEBUG_ERROR, "HOB: HOB data pointer is NULL\n"));
+    return FALSE;
+  }
+
+  Hob.Raw = (UINT8 *)VmmHobList;
+
+  //
+  // Parse the HOB list until end of list or matching type is found.
+  //
+  while (!END_OF_HOB_LIST (Hob)) {
+    if (Hob.Header->Reserved != (UINT32)0) {
+      DEBUG ((DEBUG_ERROR, "HOB: Hob header Reserved filed should be zero\n"));
+      return FALSE;
+    }
+
+    if (Hob.Header->HobLength == 0) {
+      DEBUG ((DEBUG_ERROR, "HOB: Hob header LEANGTH should not be zero\n"));
+      return FALSE;
+    }
+
+    switch (Hob.Header->HobType) {
+      case EFI_HOB_TYPE_HANDOFF:
+        if (Hob.Header->HobLength != sizeof (EFI_HOB_HANDOFF_INFO_TABLE)) {
+          DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_HANDOFF));
+          return FALSE;
+        }
+
+        if (IsInValidList (Hob.HandoffInformationTable->BootMode, EFI_BOOT_MODE_LIST, ARRAY_SIZE (EFI_BOOT_MODE_LIST)) == FALSE) {
+          DEBUG ((DEBUG_ERROR, "HOB: Unknow HandoffInformationTable BootMode type. Type: 0x%08x\n", Hob.HandoffInformationTable->BootMode));
+          return FALSE;
+        }
+
+        if ((Hob.HandoffInformationTable->EfiFreeMemoryTop % 4096) != 0) {
+          DEBUG ((DEBUG_ERROR, "HOB: HandoffInformationTable EfiFreeMemoryTop address must be 4-KB aligned to meet page restrictions of UEFI.\
+                               Address: 0x%016lx\n", Hob.HandoffInformationTable->EfiFreeMemoryTop));
+          return FALSE;
+        }
+
+        break;
+
+      case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:
+        if (Hob.Header->HobLength != sizeof (EFI_HOB_RESOURCE_DESCRIPTOR)) {
+          DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_RESOURCE_DESCRIPTOR));
+          return FALSE;
+        }
+
+        if (IsInValidList (Hob.ResourceDescriptor->ResourceType, EFI_RESOURCE_TYPE_LIST, ARRAY_SIZE (EFI_RESOURCE_TYPE_LIST)) == FALSE) {
+          DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceType type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceType));
+          return FALSE;
+        }
+
+        if ((Hob.ResourceDescriptor->ResourceAttribute & (~(EFI_RESOURCE_ATTRIBUTE_PRESENT |
+                                                            EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+                                                            EFI_RESOURCE_ATTRIBUTE_TESTED |
+                                                            EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |
+                                                            EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |
+                                                            EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED |
+                                                            EFI_RESOURCE_ATTRIBUTE_PERSISTENT |
+                                                            EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC |
+                                                            EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC |
+                                                            EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 |
+                                                            EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 |
+                                                            EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_16_BIT_IO |
+                                                            EFI_RESOURCE_ATTRIBUTE_32_BIT_IO |
+                                                            EFI_RESOURCE_ATTRIBUTE_64_BIT_IO |
+                                                            EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED |
+                                                            EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_PERSISTABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED |
+                                                            EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE |
+                                                            EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE))) != 0)
+        {
+          DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceAttribute type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceAttribute));
+          return FALSE;
+        }
+
+        break;
+
+      // EFI_HOB_GUID_TYPE is variable length data, so skip check
+      case EFI_HOB_TYPE_GUID_EXTENSION:
+        break;
+
+      case EFI_HOB_TYPE_FV:
+        if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME)) {
+          DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV));
+          return FALSE;
+        }
+
+        break;
+
+      case EFI_HOB_TYPE_FV2:
+        if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME2)) {
+          DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV2));
+          return FALSE;
+        }
+
+        break;
+
+      case EFI_HOB_TYPE_FV3:
+        if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME3)) {
+          DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV3));
+          return FALSE;
+        }
+
+        break;
+
+      case EFI_HOB_TYPE_CPU:
+        if (Hob.Header->HobLength != sizeof (EFI_HOB_CPU)) {
+          DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_CPU));
+          return FALSE;
+        }
+
+        for (UINT32 index = 0; index < 6; index++) {
+          if (Hob.Cpu->Reserved[index] != 0) {
+            DEBUG ((DEBUG_ERROR, "HOB: Cpu Reserved field will always be set to zero.\n"));
+            return FALSE;
+          }
+        }
+
+        break;
+
+      default:
+        DEBUG ((DEBUG_ERROR, "HOB: Hob type is not know. Type: 0x%04x\n", Hob.Header->HobType));
+        return FALSE;
+    }
+
+    // Get next HOB
+    Hob.Raw = (UINT8 *)(Hob.Raw + Hob.Header->HobLength);
+  }
+
+  return TRUE;
+}
+
+/**
+  Processing the incoming HobList for the TDX
+
+  Firmware must parse list, and accept the pages of memory before their can be
+  use by the guest.
+
+  @param[in] VmmHobList    The Hoblist pass the firmware
+
+  @retval  EFI_SUCCESS     Process the HobList successfully
+  @retval  Others          Other errors as indicated
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ProcessHobList (
+  IN CONST VOID  *VmmHobList
+  )
+{
+  EFI_STATUS            Status;
+  UINT32                CpusNum;
+  EFI_PHYSICAL_ADDRESS  PhysicalEnd;
+  EFI_PHYSICAL_ADDRESS  APsStackStartAddress;
+
+  CpusNum = GetCpusNum ();
+
+  //
+  // If there are mutli-vCPU in a TDX guest, accept memory is split into 2 phases.
+  // Phase-1 accepts a small piece of memory by BSP. This piece of memory
+  // is used to setup AP's stack.
+  // After that phase-2 accepts a big piece of memory by BSP/APs.
+  //
+  // TDVF supports 4K and 2M accept-page-size. The memory which can be accpeted
+  // in 2M accept-page-size must be 2M aligned and multiple 2M. So we align
+  // APsStackSize to 2M size aligned.
+  //
+  if (CpusNum > 1) {
+    Status = AcceptMemoryForAPsStack (VmmHobList, APS_STACK_SIZE (CpusNum), &PhysicalEnd);
+    ASSERT (Status == EFI_SUCCESS);
+    APsStackStartAddress = PhysicalEnd - APS_STACK_SIZE (CpusNum);
+  } else {
+    PhysicalEnd          = 0;
+    APsStackStartAddress = 0;
+  }
+
+  Status = AcceptMemory (VmmHobList, CpusNum, APsStackStartAddress, PhysicalEnd);
+  ASSERT (Status == EFI_SUCCESS);
+
+  return Status;
+}
+
+//
+// SHA512_CTX is defined in <openssl/sha.h> and its size is 216 bytes.
+// It can be built successfully with GCC5 compiler but failed with VS2019.
+// The error code showed in VS2019 is that "openssl/sha.h" cannot be found.
+// To overcome this error SHA512_CTX_SIZE is defined.
+//
+#define SHA512_CTX_SIZ  216
+
+/**
+ * Calculate the sha384 of input Data and extend it to RTMR register.
+ *
+ * @param RtmrIndex       Index of the RTMR register
+ * @param DataToHash      Data to be hashed
+ * @param DataToHashLen   Length of the data
+ * @param Digest          Hash value of the input data
+ * @param DigestLen       Length of the hash value
+ *
+ * @retval EFI_SUCCESS    Successfully hash and extend to RTMR
+ * @retval Others         Other errors as indicated
+ */
+STATIC
+EFI_STATUS
+HashAndExtendToRtmr (
+  IN UINT32  RtmrIndex,
+  IN VOID    *DataToHash,
+  IN UINTN   DataToHashLen,
+  OUT UINT8  *Digest,
+  IN  UINTN  DigestLen
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       Sha384Ctx[SHA512_CTX_SIZ];
+
+  if ((DataToHash == NULL) || (DataToHashLen == 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Digest == NULL) || (DigestLen != SHA384_DIGEST_SIZE)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Calculate the sha384 of the data
+  //
+  Sha384Init (Sha384Ctx);
+  Sha384Update (Sha384Ctx, DataToHash, DataToHashLen);
+  Sha384Final (Sha384Ctx, Digest);
+
+  //
+  // Extend to RTMR
+  //
+  Status = TdExtendRtmr (
+             (UINT32 *)Digest,
+             SHA384_DIGEST_SIZE,
+             (UINT8)RtmrIndex
+             );
+
+  ASSERT (!EFI_ERROR (Status));
+  return Status;
+}
+
+/**
+  In Tdx guest, TdHob is passed from host VMM to guest firmware and it contains
+  the information of the memory resource. From the security perspective before
+  it is consumed, it should be measured and extended.
+ *
+ * @retval EFI_SUCCESS Successfully measure the TdHob
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureTdHob (
+  VOID
+  )
+{
+  EFI_PEI_HOB_POINTERS  Hob;
+  EFI_STATUS            Status;
+  UINT8                 Digest[SHA384_DIGEST_SIZE];
+  OVMF_WORK_AREA        *WorkArea;
+  VOID                  *TdHob;
+
+  TdHob   = (VOID *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);
+  Hob.Raw = (UINT8 *)TdHob;
+
+  //
+  // Walk thru the TdHob list until end of list.
+  //
+  while (!END_OF_HOB_LIST (Hob)) {
+    Hob.Raw = GET_NEXT_HOB (Hob);
+  }
+
+  Status = HashAndExtendToRtmr (
+             0,
+             (UINT8 *)TdHob,
+             (UINTN)((UINT8 *)Hob.Raw - (UINT8 *)TdHob),
+             Digest,
+             SHA384_DIGEST_SIZE
+             );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // This function is called in SEC phase and at that moment the Hob service
+  // is not available. So the TdHob measurement value is stored in workarea.
+  //
+  WorkArea = (OVMF_WORK_AREA *)FixedPcdGet32 (PcdOvmfWorkAreaBase);
+  if (WorkArea == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.MeasurementsBitmap |= TDX_MEASUREMENT_TDHOB_BITMASK;
+  CopyMem (WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.TdHobHashValue, Digest, SHA384_DIGEST_SIZE);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  In Tdx guest, some information need to be passed from host VMM to guest
+  firmware. For example, the memory resource, etc. These information are
+  prepared by host VMM and put in TdHob which is described in TdxMetadata.
+  TDVF processes the TdHob to accept memories.
+
+  @retval   EFI_SUCCESS   Successfully process the TdHob
+  @retval   Others        Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperProcessTdHob (
+  VOID
+  )
+{
+  EFI_STATUS      Status;
+  VOID            *TdHob;
+  TD_RETURN_DATA  TdReturnData;
+
+  TdHob  = (VOID *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);
+  Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  DEBUG ((
+    DEBUG_INFO,
+    "Intel Tdx Started with (GPAW: %d, Cpus: %d)\n",
+    TdReturnData.TdInfo.Gpaw,
+    TdReturnData.TdInfo.NumVcpus
+    ));
+
+  //
+  // Validate HobList
+  //
+  if (ValidateHobList (TdHob) == FALSE) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Process Hoblist to accept memory
+  //
+  Status = ProcessHobList (TdHob);
+
+  return Status;
+}
+
+/**
+ * In Tdx guest, Configuration FV (CFV) is treated as external input because it
+ * may contain the data provided by VMM. From the sucurity perspective Cfv image
+ * should be measured before it is consumed.
+ *
+ * @retval EFI_SUCCESS Successfully measure the CFV image
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureCfvImage (
+  VOID
+  )
+{
+  EFI_STATUS      Status;
+  UINT8           Digest[SHA384_DIGEST_SIZE];
+  OVMF_WORK_AREA  *WorkArea;
+
+  Status = HashAndExtendToRtmr (
+             0,
+             (UINT8 *)(UINTN)PcdGet32 (PcdOvmfFlashNvStorageVariableBase),
+             (UINT64)PcdGet32 (PcdCfvRawDataSize),
+             Digest,
+             SHA384_DIGEST_SIZE
+             );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // This function is called in SEC phase and at that moment the Hob service
+  // is not available. So CfvImage measurement value is stored in workarea.
+  //
+  WorkArea = (OVMF_WORK_AREA *)FixedPcdGet32 (PcdOvmfWorkAreaBase);
+  if (WorkArea == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.MeasurementsBitmap |= TDX_MEASUREMENT_CFVIMG_BITMASK;
+  CopyMem (WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.CfvImgHashValue, Digest, SHA384_DIGEST_SIZE);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Build the GuidHob for tdx measurements which were done in SEC phase.
+  The measurement values are stored in WorkArea.
+
+  @retval EFI_SUCCESS  The GuidHob is built successfully
+  @retval Others       Other errors as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperBuildGuidHobForTdxMeasurement (
+  VOID
+  )
+{
+ #ifdef TDX_PEI_LESS_BOOT
+  return InternalBuildGuidHobForTdxMeasurement ();
+ #else
+  return EFI_UNSUPPORTED;
+ #endif
+}
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelperLib.inf b/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelperLib.inf
new file mode 100644
index 000000000000..d17b84c01f20
--- /dev/null
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelperLib.inf
@@ -0,0 +1,53 @@
+## @file
+#  TdxHelperLib SEC instance
+#
+#  This module provides Tdx helper functions in SEC phase.
+#  Copyright (c) 2021 - 2023, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SecTdxHelperLib
+  FILE_GUID                      = ba69ac6b-0c59-4472-899d-b684590ec1e9
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = TdxHelperLib|SEC
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources]
+  SecTdxHelper.c
+  TdxMeasurementHob.c
+
+[Packages]
+  CryptoPkg/CryptoPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  SecurityPkg/SecurityPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseCryptLib
+  DebugLib
+  HobLib
+  PcdLib
+  TdxMailboxLib
+  TdxLib
+
+[FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize
+
+[Guids]
+  gCcEventEntryHobGuid
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperLibNull.inf b/OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperLibNull.inf
new file mode 100644
index 000000000000..27d07b3886cf
--- /dev/null
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperLibNull.inf
@@ -0,0 +1,32 @@
+## @file
+#  TdxHelperLib NULL instance
+#
+#  Copyright (c) 2021 - 2023, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = TdxHelperLibNull
+  FILE_GUID                      = 853603b2-53ea-463d-93e6-35d09a79e358
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = TdxHelperLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources]
+  TdxHelperNull.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperNull.c b/OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperNull.c
new file mode 100644
index 000000000000..a2125190d6aa
--- /dev/null
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/TdxHelperNull.c
@@ -0,0 +1,79 @@
+/** @file
+  NULL instance of TdxHelperLib
+
+  Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <PiPei.h>
+
+/**
+  In Tdx guest, some information need to be passed from host VMM to guest
+  firmware. For example, the memory resource, etc. These information are
+  prepared by host VMM and put in TdHob which is described in TdxMetadata.
+  TDVF processes the TdHob to accept memories.
+
+  @retval   EFI_SUCCESS   Successfully process the TdHob
+  @retval   Others        Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperProcessTdHob (
+  VOID
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  In Tdx guest, TdHob is passed from host VMM to guest firmware and it contains
+  the information of the memory resource. From the security perspective before
+  it is consumed, it should be measured and extended.
+ *
+ * @retval EFI_SUCCESS Successfully measure the TdHob
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureTdHob (
+  VOID
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+ * In Tdx guest, Configuration FV (CFV) is treated as external input because it
+ * may contain the data provided by VMM. From the sucurity perspective Cfv image
+ * should be measured before it is consumed.
+ *
+ * @retval EFI_SUCCESS Successfully measure the CFV image
+ * @retval Others      Other error as indicated
+ */
+EFI_STATUS
+EFIAPI
+TdxHelperMeasureCfvImage (
+  VOID
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Build the GuidHob for tdx measurements which were done in SEC phase.
+  The measurement values are stored in WorkArea.
+
+  @retval EFI_SUCCESS  The GuidHob is built successfully
+  @retval Others       Other errors as indicated
+**/
+EFI_STATUS
+EFIAPI
+TdxHelperBuildGuidHobForTdxMeasurement (
+  VOID
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/TdxMeasurementHob.c b/OvmfPkg/IntelTdx/TdxHelperLib/TdxMeasurementHob.c
new file mode 100644
index 000000000000..906d4df485b8
--- /dev/null
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/TdxMeasurementHob.c
@@ -0,0 +1,266 @@
+/** @file
+  Build GuidHob for tdx measurement.
+
+  Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <PiPei.h>
+#include <IndustryStandard/UefiTcgPlatform.h>
+#include <IndustryStandard/Tpm20.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/TpmMeasurementLib.h>
+#include <Pi/PrePiHob.h>
+#include <WorkArea.h>
+#include <ConfidentialComputingGuestAttr.h>
+
+#pragma pack(1)
+
+#define HANDOFF_TABLE_DESC  "TdxTable"
+typedef struct {
+  UINT8                      TableDescriptionSize;
+  UINT8                      TableDescription[sizeof (HANDOFF_TABLE_DESC)];
+  UINT64                     NumberOfTables;
+  EFI_CONFIGURATION_TABLE    TableEntry[1];
+} TDX_HANDOFF_TABLE_POINTERS2;
+
+#define FV_HANDOFF_TABLE_DESC  "Fv(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)"
+typedef struct {
+  UINT8                   BlobDescriptionSize;
+  UINT8                   BlobDescription[sizeof (FV_HANDOFF_TABLE_DESC)];
+  EFI_PHYSICAL_ADDRESS    BlobBase;
+  UINT64                  BlobLength;
+} FV_HANDOFF_TABLE_POINTERS2;
+
+#pragma pack()
+
+/**
+ * Build GuidHob for Tdx measurement.
+ *
+ * Tdx measurement includes the measurement of TdHob and CFV. They're measured
+ * and extended to RTMR registers in SEC phase. Because at that moment the Hob
+ * service are not available. So the values of the measurement are saved in
+ * workarea and will be built into GuidHob after the Hob service is ready.
+ *
+ * @param RtmrIndex     RTMR index
+ * @param EventType     Event type
+ * @param EventData     Event data
+ * @param EventSize     Size of event data
+ * @param HashValue     Hash value
+ * @param HashSize      Size of hash
+ *
+ * @retval EFI_SUCCESS  Successfully build the GuidHobs
+ * @retval Others       Other error as indicated
+ */
+STATIC
+EFI_STATUS
+BuildTdxMeasurementGuidHob (
+  UINT32  RtmrIndex,
+  UINT32  EventType,
+  UINT8   *EventData,
+  UINT32  EventSize,
+  UINT8   *HashValue,
+  UINT32  HashSize
+  )
+{
+  VOID                *EventHobData;
+  UINT8               *Ptr;
+  TPML_DIGEST_VALUES  *TdxDigest;
+
+  if (HashSize != SHA384_DIGEST_SIZE) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  #define TDX_DIGEST_VALUE_LEN  (sizeof (UINT32) + sizeof (TPMI_ALG_HASH) + SHA384_DIGEST_SIZE)
+
+  EventHobData = BuildGuidHob (
+                   &gCcEventEntryHobGuid,
+                   sizeof (TCG_PCRINDEX) + sizeof (TCG_EVENTTYPE) +
+                   TDX_DIGEST_VALUE_LEN +
+                   sizeof (UINT32) + EventSize
+                   );
+
+  if (EventHobData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Ptr = (UINT8 *)EventHobData;
+
+  //
+  // There are 2 types of measurement registers in TDX: MRTD and RTMR[0-3].
+  // According to UEFI Spec 2.10 Section 38.4.1, RTMR[0-3] is mapped to MrIndex[1-4].
+  // So RtmrIndex must be increased by 1 before the event log is created.
+  //
+  RtmrIndex++;
+  CopyMem (Ptr, &RtmrIndex, sizeof (UINT32));
+  Ptr += sizeof (UINT32);
+
+  CopyMem (Ptr, &EventType, sizeof (TCG_EVENTTYPE));
+  Ptr += sizeof (TCG_EVENTTYPE);
+
+  TdxDigest                     = (TPML_DIGEST_VALUES *)Ptr;
+  TdxDigest->count              = 1;
+  TdxDigest->digests[0].hashAlg = TPM_ALG_SHA384;
+  CopyMem (
+    TdxDigest->digests[0].digest.sha384,
+    HashValue,
+    SHA384_DIGEST_SIZE
+    );
+  Ptr += TDX_DIGEST_VALUE_LEN;
+
+  CopyMem (Ptr, &EventSize, sizeof (UINT32));
+  Ptr += sizeof (UINT32);
+
+  CopyMem (Ptr, (VOID *)EventData, EventSize);
+  Ptr += EventSize;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Get the FvName from the FV header.
+  Causion: The FV is untrusted input.
+  @param[in]  FvBase            Base address of FV image.
+  @param[in]  FvLength          Length of FV image.
+  @return FvName pointer
+  @retval NULL   FvName is NOT found
+**/
+STATIC
+VOID *
+GetFvName (
+  IN EFI_PHYSICAL_ADDRESS  FvBase,
+  IN UINT64                FvLength
+  )
+{
+  EFI_FIRMWARE_VOLUME_HEADER      *FvHeader;
+  EFI_FIRMWARE_VOLUME_EXT_HEADER  *FvExtHeader;
+
+  if (FvBase >= MAX_ADDRESS) {
+    return NULL;
+  }
+
+  if (FvLength >= MAX_ADDRESS - FvBase) {
+    return NULL;
+  }
+
+  if (FvLength < sizeof (EFI_FIRMWARE_VOLUME_HEADER)) {
+    return NULL;
+  }
+
+  FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvBase;
+  if (FvHeader->ExtHeaderOffset < sizeof (EFI_FIRMWARE_VOLUME_HEADER)) {
+    return NULL;
+  }
+
+  if (FvHeader->ExtHeaderOffset + sizeof (EFI_FIRMWARE_VOLUME_EXT_HEADER) > FvLength) {
+    return NULL;
+  }
+
+  FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)(UINTN)(FvBase + FvHeader->ExtHeaderOffset);
+
+  return &FvExtHeader->FvName;
+}
+
+/**
+  Build the GuidHob for tdx measurements which were done in SEC phase.
+  The measurement values are stored in WorkArea.
+
+  @retval EFI_SUCCESS  The GuidHob is built successfully
+  @retval Others       Other errors as indicated
+**/
+EFI_STATUS
+InternalBuildGuidHobForTdxMeasurement (
+  VOID
+  )
+{
+  EFI_STATUS                   Status;
+  OVMF_WORK_AREA               *WorkArea;
+  VOID                         *TdHobList;
+  TDX_HANDOFF_TABLE_POINTERS2  HandoffTables;
+  VOID                         *FvName;
+  FV_HANDOFF_TABLE_POINTERS2   FvBlob2;
+  EFI_PHYSICAL_ADDRESS         FvBase;
+  UINT64                       FvLength;
+  UINT8                        *HashValue;
+
+  if (!TdIsEnabled ()) {
+    ASSERT (FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  WorkArea = (OVMF_WORK_AREA *)FixedPcdGet32 (PcdOvmfWorkAreaBase);
+  if (WorkArea == NULL) {
+    return EFI_ABORTED;
+  }
+
+  Status = EFI_SUCCESS;
+
+  //
+  // Build the GuidHob for TdHob measurement
+  //
+  TdHobList = (VOID *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);
+  if (WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.MeasurementsBitmap & TDX_MEASUREMENT_TDHOB_BITMASK) {
+    HashValue                          = WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.TdHobHashValue;
+    HandoffTables.TableDescriptionSize = sizeof (HandoffTables.TableDescription);
+    CopyMem (HandoffTables.TableDescription, HANDOFF_TABLE_DESC, sizeof (HandoffTables.TableDescription));
+    HandoffTables.NumberOfTables = 1;
+    CopyGuid (&(HandoffTables.TableEntry[0].VendorGuid), &gUefiOvmfPkgTokenSpaceGuid);
+    HandoffTables.TableEntry[0].VendorTable = TdHobList;
+
+    Status = BuildTdxMeasurementGuidHob (
+               0,                               // RtmrIndex
+               EV_EFI_HANDOFF_TABLES2,          // EventType
+               (UINT8 *)(UINTN)&HandoffTables,  // EventData
+               sizeof (HandoffTables),          // EventSize
+               HashValue,                       // HashValue
+               SHA384_DIGEST_SIZE               // HashSize
+               );
+  }
+
+  if (EFI_ERROR (Status)) {
+    ASSERT (FALSE);
+    return Status;
+  }
+
+  //
+  // Build the GuidHob for Cfv measurement
+  //
+  if (WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.MeasurementsBitmap & TDX_MEASUREMENT_CFVIMG_BITMASK) {
+    HashValue                   = WorkArea->TdxWorkArea.SecTdxWorkArea.TdxMeasurementsData.CfvImgHashValue;
+    FvBase                      = (UINT64)PcdGet32 (PcdOvmfFlashNvStorageVariableBase);
+    FvLength                    = (UINT64)PcdGet32 (PcdCfvRawDataSize);
+    FvBlob2.BlobDescriptionSize = sizeof (FvBlob2.BlobDescription);
+    CopyMem (FvBlob2.BlobDescription, FV_HANDOFF_TABLE_DESC, sizeof (FvBlob2.BlobDescription));
+    FvName = GetFvName (FvBase, FvLength);
+    if (FvName != NULL) {
+      AsciiSPrint ((CHAR8 *)FvBlob2.BlobDescription, sizeof (FvBlob2.BlobDescription), "Fv(%g)", FvName);
+    }
+
+    FvBlob2.BlobBase   = FvBase;
+    FvBlob2.BlobLength = FvLength;
+
+    Status = BuildTdxMeasurementGuidHob (
+               0,                              // RtmrIndex
+               EV_EFI_PLATFORM_FIRMWARE_BLOB2, // EventType
+               (VOID *)&FvBlob2,               // EventData
+               sizeof (FvBlob2),               // EventSize
+               HashValue,                      // HashValue
+               SHA384_DIGEST_SIZE              // HashSize
+               );
+  }
+
+  if (EFI_ERROR (Status)) {
+    ASSERT (FALSE);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index e07546f4a701..7b91580f8e3a 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -98,6 +98,10 @@
   #
   SerializeVariablesLib|Include/Library/SerializeVariablesLib.h
 
+  ##  @libraryclass  TdxHelper
+  #
+  TdxHelperLib|Include/Library/TdxHelperLib.h
+
   ##  @libraryclass  Declares utility functions for virtio device drivers.
   VirtioLib|Include/Library/VirtioLib.h
 
-- 
2.29.2.windows.2



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#98643): https://edk2.groups.io/g/devel/message/98643
Mute This Topic: https://groups.io/mt/96325909/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list