public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Min Xu" <min.m.xu@intel.com>
To: devel@edk2.groups.io
Cc: Min Xu <min.m.xu@intel.com>,
	Ard Biesheuvel <ardb+tianocore@kernel.org>,
	Jordan Justen <jordan.l.justen@intel.com>,
	Brijesh Singh <brijesh.singh@amd.com>,
	Erdem Aktas <erdemaktas@google.com>,
	James Bottomley <jejb@linux.ibm.com>,
	Jiewen Yao <jiewen.yao@intel.com>,
	Tom Lendacky <thomas.lendacky@amd.com>,
	Gerd Hoffmann <kraxel@redhat.com>
Subject: [PATCH V3 19/29] OvmfPkg: Enable Tdx in SecMain.c
Date: Mon,  1 Nov 2021 21:16:08 +0800	[thread overview]
Message-ID: <a4233a25a63f2e3c67512eaf83094f448ad259e0.1635769996.git.min.m.xu@intel.com> (raw)
In-Reply-To: <cover.1635769996.git.min.m.xu@intel.com>

RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429

When host VMM create the Td guest, the system memory informations are
stored in TdHob, which is a memory region described in Tdx metadata.
The system memory region in TdHob should be accepted before it can be
accessed. So the major task of this patch set is to process the TdHobList
to accept the memory. After that TDVF follow the standard OVMF flow
and jump to PEI phase.

PcdTdxAcceptChunkSize & PcdTdxAcceptPageSize are added for page accepting.
They're the default settings of the chunk size and the Accept page size.

PcdUse1GPageTable is set to FALSE by default in OvmfPkgX64.dsc. It gives
no chance for Intel TDX to support 1G page table. To support 1G page
table this PCD is set to TRUE in OvmfPkgX64.dsc.

3 Tdx specific libs are used by OvmfPkgX64:
  - TdxLib
  - TdxMailboxLib

TDX only works on X64, so the code is only valid in X64 arch.

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
 OvmfPkg/OvmfPkg.dec        |   6 +
 OvmfPkg/OvmfPkgIa32.dsc    |   2 +
 OvmfPkg/OvmfPkgIa32X64.dsc |   2 +
 OvmfPkg/OvmfPkgX64.dsc     |   6 +
 OvmfPkg/Sec/IntelTdx.c     | 597 +++++++++++++++++++++++++++++++++++++
 OvmfPkg/Sec/IntelTdx.h     |  33 ++
 OvmfPkg/Sec/SecMain.c      |  60 +++-
 OvmfPkg/Sec/SecMain.inf    |   6 +
 8 files changed, 706 insertions(+), 6 deletions(-)
 create mode 100644 OvmfPkg/Sec/IntelTdx.c
 create mode 100644 OvmfPkg/Sec/IntelTdx.h

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 145943532a74..ccf8fc33ce27 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -357,6 +357,12 @@
   ## Ignore the VE halt in Tdx
   gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x58
 
+  ## The chunk size of Tdx accept page
+  gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize|0x2000000|UINT64|0x59
+
+  ## The Tdx accept page size. 0x1000(4k),0x200000(2M)
+  gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize|0x1000|UINT32|0x5a
+
 [PcdsDynamic, PcdsDynamicEx]
   gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 6a5be97c059d..056055c8d799 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -243,6 +243,8 @@
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
   VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+  TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
+  TdxMailboxLib|OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 71227d1b709a..27887da8f2a2 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -247,6 +247,8 @@
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
   VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
+  TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
+  TdxMailboxLib|OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 52f7598cf1c7..3eb29811d822 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -247,6 +247,8 @@
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
   VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
+  TdxLib|MdePkg/Library/TdxLib/TdxLib.inf
+  TdxMailboxLib|OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
 
 [LibraryClasses.common.SEC]
   TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
@@ -572,6 +574,10 @@
   gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|0x100
   gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData|0x100
 
+  #
+  # TDX need 1G PageTable support
+  gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable|TRUE
+
   #
   # Network Pcds
   #
diff --git a/OvmfPkg/Sec/IntelTdx.c b/OvmfPkg/Sec/IntelTdx.c
new file mode 100644
index 000000000000..a70bb61e8ee8
--- /dev/null
+++ b/OvmfPkg/Sec/IntelTdx.c
@@ -0,0 +1,597 @@
+/** @file
+
+  Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+  (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Uefi/UefiSpec.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <IndustryStandard/UefiTcgPlatform.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/TdxLib.h>
+#include <Library/TdxMailboxLib.h>
+#include <IndustryStandard/Tdx.h>
+#include <IndustryStandard/IntelTdx.h>
+#include "IntelTdx.h"
+
+#define ALIGNED_2MB_MASK 0x1fffff
+
+/**
+  BSP call this function to accept memory in a range.
+
+  @param[in]  StartAddress      Start address of the memory region
+  @param[in]  Length            Length of the memory region
+  @param[in]  AcceptChunkSize   Accept chunk size
+  @param[in]  AcceptPageSize    Accept page size
+  @retval     EFI_SUCCESS       Successfully accept the memory region
+  @retval     Others            Indicate the other errors
+**/
+EFI_STATUS
+EFIAPI
+BspAcceptMemoryResourceRange (
+  IN EFI_PHYSICAL_ADDRESS   StartAddress,
+  IN UINT64                 Length,
+  IN UINT64                 AcceptChunkSize,
+  IN UINT32                 AcceptPageSize
+  )
+{
+  EFI_STATUS                  Status;
+  UINT64                      Pages;
+  UINT64                      Stride;
+  EFI_PHYSICAL_ADDRESS        PhysicalAddress;
+  volatile MP_WAKEUP_MAILBOX  *MailBox;
+
+  Status = EFI_SUCCESS;
+  PhysicalAddress = StartAddress;
+  Stride = GetCpusNum () * AcceptChunkSize;
+  MailBox = (volatile MP_WAKEUP_MAILBOX *) GetTdxMailBox ();
+
+  while (!EFI_ERROR(Status) && PhysicalAddress < StartAddress + Length) {
+    //
+    // Decrease size of near end of resource if needed.
+    //
+    Pages = MIN (AcceptChunkSize, StartAddress + Length - PhysicalAddress) / AcceptPageSize;
+
+    MailBox->Tallies[0] += (UINT32)Pages;
+
+    Status = TdAcceptPages (PhysicalAddress, Pages, AcceptPageSize);
+    //
+    // Bump address to next chunk this cpu is responisble for
+    //
+    PhysicalAddress += Stride;
+  }
+
+  return Status;
+}
+
+/**
+  This function will be called to accept pages. BSP and APs are invokded
+  to do the task together.
+
+  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
+  |---------------|
+
+  part 1) will be accepted in 4k and by BSP.
+  Part 2) will be accepted in 2M and by BSP/AP.
+  Part 3) will be accepted in 4k and by BSP.
+
+  @param[in] PhysicalAddress   Start physical adress
+  @param[in] PhysicalEnd       End physical address
+
+  @retval    EFI_SUCCESS       Accept memory successfully
+  @retval    Others            Other errors as indicated
+**/
+EFI_STATUS
+EFIAPI
+MpAcceptMemoryResourceRange (
+  IN EFI_PHYSICAL_ADDRESS        PhysicalAddress,
+  IN EFI_PHYSICAL_ADDRESS        PhysicalEnd
+  )
+{
+  EFI_STATUS                  Status;
+  UINT64                      AcceptChunkSize;
+  UINT32                      AcceptPageSize;
+  UINT64                      StartAddress1;
+  UINT64                      StartAddress2;
+  UINT64                      StartAddress3;
+  UINT64                      TotalLength;
+  UINT64                      Length1;
+  UINT64                      Length2;
+  UINT64                      Length3;
+  UINT32                      Index;
+  UINT32                      CpusNum;
+  volatile MP_WAKEUP_MAILBOX  *MailBox;
+
+  AcceptChunkSize = FixedPcdGet64 (PcdTdxAcceptChunkSize);
+  AcceptPageSize = FixedPcdGet32 (PcdTdxAcceptPageSize);
+  TotalLength = PhysicalEnd - PhysicalAddress;
+  StartAddress1 = 0;
+  StartAddress2 = 0;
+  StartAddress3 = 0;
+  Length1 = 0;
+  Length2 = 0;
+  Length3 = 0;
+
+  if (AcceptPageSize == SIZE_4KB || TotalLength <= SIZE_2MB) {
+    //
+    // if total length is less than 2M, then we accept pages in 4k
+    //
+    StartAddress1 = 0;
+    Length1 = 0;
+    StartAddress2 = PhysicalAddress;
+    Length2 = PhysicalEnd - PhysicalAddress;
+    StartAddress3 = 0;
+    Length3 = 0;
+    AcceptPageSize = SIZE_4KB;
+  } else if (AcceptPageSize == SIZE_2MB) {
+    //
+    // Total length is bigger than 2M and Page Accept size 2M is supported.
+    //
+    if ((PhysicalAddress & ALIGNED_2MB_MASK) == 0) {
+      //
+      // Start address is 2M aligned
+      //
+      StartAddress1 = 0;
+      Length1 = 0;
+      StartAddress2 = PhysicalAddress;
+      Length2 = TotalLength & ~(UINT64)ALIGNED_2MB_MASK;
+
+      if (TotalLength > Length2) {
+        //
+        // There is remaining part 3)
+        //
+        StartAddress3 = StartAddress2 + Length2;
+        Length3 = TotalLength - Length2;
+        ASSERT (Length3 < SIZE_2MB);
+      }
+    } else {
+      //
+      // Start address is not 2M aligned and total length is bigger than 2M.
+      //
+      StartAddress1 = PhysicalAddress;
+      ASSERT (TotalLength > SIZE_2MB);
+      Length1 = SIZE_2MB - (PhysicalAddress & ALIGNED_2MB_MASK);
+      if (TotalLength - Length1 < SIZE_2MB) {
+        //
+        // The Part 2) length is less than 2MB, so let's accept all the
+        // memory in 4K
+        //
+        Length1 = TotalLength;
+
+      } else {
+        StartAddress2 = PhysicalAddress + Length1;
+        Length2 = (TotalLength - Length1) & ~(UINT64)ALIGNED_2MB_MASK;
+        Length3 = TotalLength - Length1 - Length2;
+        StartAddress3 = Length3 > 0 ? StartAddress2 + Length2 : 0;
+        ASSERT (Length3 < SIZE_2MB);
+      }
+    }
+  }
+
+  DEBUG ((DEBUG_INFO, "TdAccept: 0x%llx - 0x%llx\n", PhysicalAddress, TotalLength));
+  DEBUG ((DEBUG_INFO, "   Part1: 0x%llx - 0x%llx\n", StartAddress1, Length1));
+  DEBUG ((DEBUG_INFO, "   Part2: 0x%llx - 0x%llx\n", StartAddress2, Length2));
+  DEBUG ((DEBUG_INFO, "   Part3: 0x%llx - 0x%llx\n", StartAddress3, Length3));
+  DEBUG ((DEBUG_INFO, "   Chunk: 0x%llx, Page : 0x%llx\n", AcceptChunkSize, AcceptPageSize));
+
+  MpSerializeStart ();
+
+  if (Length2 > 0) {
+    MpSendWakeupCommand (
+      MpProtectedModeWakeupCommandAcceptPages,
+      0,
+      StartAddress2,
+      StartAddress2 + Length2,
+      AcceptChunkSize,
+      AcceptPageSize);
+
+    Status = BspAcceptMemoryResourceRange (
+                StartAddress2,
+                Length2,
+                AcceptChunkSize,
+                AcceptPageSize);
+    ASSERT (!EFI_ERROR (Status));
+  }
+
+  if (Length1 > 0) {
+    Status = BspAcceptMemoryResourceRange (
+                StartAddress1,
+                Length1,
+                AcceptChunkSize,
+                SIZE_4KB);
+    ASSERT (!EFI_ERROR (Status));
+  }
+
+  if (Length3 > 0) {
+    Status = BspAcceptMemoryResourceRange (
+                StartAddress3,
+                Length3,
+                AcceptChunkSize,
+                SIZE_4KB);
+    ASSERT (!EFI_ERROR (Status));
+  }
+
+  MpSerializeEnd ();
+
+  CpusNum = GetCpusNum ();
+  MailBox = (volatile MP_WAKEUP_MAILBOX *) GetTdxMailBox ();
+
+  for (Index = 0; Index < CpusNum; Index++) {
+    if (MailBox->Errors[Index] > 0) {
+      Status = EFI_DEVICE_ERROR;
+      DEBUG ((DEBUG_ERROR, "Error(%d) of CPU-%d when accepting memory\n",
+        MailBox->Errors[Index], Index));
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Dump out the hob list
+
+  @param[in]  HobStart    Start address of the hob list
+**/
+VOID
+EFIAPI
+DEBUG_HOBLIST (
+  IN CONST VOID             *HobStart
+  )
+{
+  EFI_PEI_HOB_POINTERS  Hob;
+  Hob.Raw = (UINT8 *) HobStart;
+  //
+  // Parse the HOB list until end of list or matching type is found.
+  //
+  while (!END_OF_HOB_LIST (Hob)) {
+    DEBUG ((DEBUG_INFO, "HOB(%p) : %x %x\n", Hob, Hob.Header->HobType, Hob.Header->HobLength));
+    switch (Hob.Header->HobType) {
+    case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:
+      DEBUG ((DEBUG_INFO, "\t: %x %x %llx %llx\n",
+        Hob.ResourceDescriptor->ResourceType,
+        Hob.ResourceDescriptor->ResourceAttribute,
+        Hob.ResourceDescriptor->PhysicalStart,
+        Hob.ResourceDescriptor->ResourceLength));
+
+      break;
+    case EFI_HOB_TYPE_MEMORY_ALLOCATION:
+      DEBUG ((DEBUG_INFO, "\t: %llx %llx %x\n",
+        Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress,
+        Hob.MemoryAllocation->AllocDescriptor.MemoryLength,
+        Hob.MemoryAllocation->AllocDescriptor.MemoryType));
+      break;
+    default:
+      break;
+    }
+    Hob.Raw = GET_NEXT_HOB (Hob);
+  }
+}
+
+/**
+  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.
+
+**/
+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.
+
+**/
+BOOLEAN
+EFIAPI
+ValidateHobList (
+  IN CONST VOID             *VmmHobList
+  )
+{
+  EFI_PEI_HOB_POINTERS  Hob;
+  UINT32 EFI_BOOT_MODE_LIST[12] = { 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[8] = { 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,
+                                       EFI_RESOURCE_MAX_MEMORY_TYPE
+                                     };
+
+  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, 12) == 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, 8) == 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 |
+                                                          EFI_RESOURCE_ATTRIBUTE_ENCRYPTED))) != 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
+
+**/
+EFI_STATUS
+EFIAPI
+ProcessHobList (
+  IN CONST VOID             *VmmHobList
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_PEI_HOB_POINTERS        Hob;
+  EFI_PHYSICAL_ADDRESS        PhysicalEnd;
+
+  Status = EFI_SUCCESS;
+  ASSERT (VmmHobList != NULL);
+  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->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
+      DEBUG ((DEBUG_INFO, "\nResourceType: 0x%x\n", Hob.ResourceDescriptor->ResourceType));
+
+      if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+        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));
+
+        PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength;
+
+        Status = MpAcceptMemoryResourceRange (
+            Hob.ResourceDescriptor->PhysicalStart,
+            PhysicalEnd);
+        if (EFI_ERROR (Status)) {
+          break;
+        }
+      }
+    }
+    Hob.Raw = GET_NEXT_HOB (Hob);
+  }
+
+  return Status;
+}
+
+/**
+  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 HobList which is described in TdxMetadata.
+
+  Information in HobList is treated as external input. From the security
+  perspective before it is consumed, it should be validated.
+
+  @retval   EFI_SUCCESS   Successfully process the hoblist
+  @retval   Others        Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+ProcessTdxHobList (
+  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;
+}
diff --git a/OvmfPkg/Sec/IntelTdx.h b/OvmfPkg/Sec/IntelTdx.h
new file mode 100644
index 000000000000..9420f586b176
--- /dev/null
+++ b/OvmfPkg/Sec/IntelTdx.h
@@ -0,0 +1,33 @@
+/** @file
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef __INTEL_TDX_H__
+#define __INTEL_TDX_H__
+
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Uefi/UefiSpec.h>
+#include <Uefi/UefiBaseType.h>
+#include <IndustryStandard/UefiTcgPlatform.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 HobList which is described in TdxMetadata.
+
+  Information in HobList is treated as external input. From the security
+  perspective before it is consumed, it should be validated.
+
+  @retval   EFI_SUCCESS   Successfully process the hoblist
+  @retval   Others        Other error as indicated
+**/
+EFI_STATUS
+EFIAPI
+ProcessTdxHobList (
+  VOID
+  );
+#endif
diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index 707b0d4bbff4..56ee8c4a230c 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -29,8 +29,10 @@
 #include <Library/MemEncryptSevLib.h>
 #include <Register/Amd/Ghcb.h>
 #include <Register/Amd/Msr.h>
-
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
 #include <Ppi/TemporaryRamSupport.h>
+#include "IntelTdx.h"
 
 #define SEC_IDT_ENTRY_COUNT  34
 
@@ -865,6 +867,24 @@ SevEsIsEnabled (
   return (SevEsWorkArea->SevEsEnabled != 0);
 }
 
+/**
+  Check TDX is enabled.
+
+  @retval    TRUE   TDX is enabled
+  @retval    FALSE  TDX is not enabled
+**/
+BOOLEAN
+SecTdxIsEnabled (
+  VOID
+  )
+{
+  CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER   *CcWorkAreaHeader;
+
+  CcWorkAreaHeader = (CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *) FixedPcdGet32 (PcdOvmfWorkAreaBase);
+  return (CcWorkAreaHeader != NULL && CcWorkAreaHeader->GuestType == GUEST_TYPE_INTEL_TDX);
+}
+
+
 VOID
 EFIAPI
 SecCoreStartupWithStack (
@@ -878,6 +898,19 @@ SecCoreStartupWithStack (
   UINT32                      Index;
   volatile UINT8              *Table;
 
+#if defined (MDE_CPU_X64)
+  if (SecTdxIsEnabled ()) {
+    //
+    // For Td guests, the memory map info is in TdHobLib. It should be processed
+    // first so that the memory is accepted. Otherwise access to the unaccepted
+    // memory will trigger tripple fault.
+    //
+    if (ProcessTdxHobList () != EFI_SUCCESS) {
+      CpuDeadLoop ();
+    }
+  }
+#endif
+
   //
   // To ensure SMM can't be compromised on S3 resume, we must force re-init of
   // the BaseExtractGuidedSectionLib. Since this is before library contructors
@@ -895,13 +928,20 @@ SecCoreStartupWithStack (
   // we use a loop rather than CopyMem.
   //
   IdtTableInStack.PeiService = NULL;
+
   for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {
-    UINT8  *Src;
-    UINT8  *Dst;
-    UINTN  Byte;
+    //
+    // Declare the local variables that actually move the data elements as
+    // volatile to prevent the optimizer from replacing this function with
+    // the intrinsic memcpy()
+    //
+    CONST UINT8     *Src;
+    volatile UINT8  *Dst;
+    UINTN           Byte;
+
+    Src = (CONST UINT8 *) &mIdtEntryTemplate;
+    Dst = (volatile UINT8 *) &IdtTableInStack.IdtTable[Index];
 
-    Src = (UINT8 *) &mIdtEntryTemplate;
-    Dst = (UINT8 *) &IdtTableInStack.IdtTable[Index];
     for (Byte = 0; Byte < sizeof (mIdtEntryTemplate); Byte++) {
       Dst[Byte] = Src[Byte];
     }
@@ -947,6 +987,14 @@ SecCoreStartupWithStack (
     AsmEnableCache ();
   }
 
+  if (SecTdxIsEnabled ()) {
+    //
+    // InitializeCpuExceptionHandlers () should be called in Td guests so that
+    // #VE exceptions can be handled correctly.
+    //
+    InitializeCpuExceptionHandlers (NULL);
+  }
+
   DEBUG ((DEBUG_INFO,
     "SecCoreStartupWithStack(0x%x, 0x%x)\n",
     (UINT32)(UINTN)BootFv,
diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf
index 6083fa21a433..4f16ff5d8330 100644
--- a/OvmfPkg/Sec/SecMain.inf
+++ b/OvmfPkg/Sec/SecMain.inf
@@ -28,6 +28,7 @@
   Ia32/SecEntry.nasm
 
 [Sources.X64]
+  IntelTdx.c
   X64/SecEntry.nasm
 
 [Packages]
@@ -51,6 +52,8 @@
   ExtractGuidedSectionLib
   LocalApicLib
   CpuExceptionHandlerLib
+  TdxLib
+  TdxMailboxLib
 
 [Ppis]
   gEfiTemporaryRamSupportPpiGuid                # PPI ALWAYS_PRODUCED
@@ -73,6 +76,9 @@
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfConfidentialComputingWorkAreaHeader
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfWorkAreaBase
 
 [FeaturePcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
-- 
2.29.2.windows.2


  parent reply	other threads:[~2021-11-01 13:18 UTC|newest]

Thread overview: 107+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-01 13:15 [PATCH V3 00/29] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
2021-11-01 13:15 ` [PATCH V3 01/29] MdePkg: Add Tdx.h Min Xu
2021-11-01 13:15 ` [PATCH V3 02/29] MdePkg: Add TdxLib to wrap Tdx operations Min Xu
2021-11-02 14:06   ` Gerd Hoffmann
2021-11-10  4:58     ` [edk2-devel] " Min Xu
2021-11-10 10:38   ` Erdem Aktas
2021-11-12  2:38     ` Min Xu
2021-11-12  2:42       ` Yao, Jiewen
2021-11-12  5:29         ` Min Xu
2021-11-12  5:33           ` Yao, Jiewen
2021-11-01 13:15 ` [PATCH V3 03/29] UefiCpuPkg: Extend VmgExitLibNull to handle #VE exception Min Xu
2021-11-02 14:11   ` Gerd Hoffmann
2021-11-01 13:15 ` [PATCH V3 04/29] OvmfPkg: Extend VmgExitLib " Min Xu
2021-11-02 14:23   ` Gerd Hoffmann
2021-11-10  6:46     ` Min Xu
2021-11-17  0:32   ` Erdem Aktas
2021-11-01 13:15 ` [PATCH V3 05/29] UefiCpuPkg/CpuExceptionHandler: Add base support for the " Min Xu
2021-11-02 14:24   ` Gerd Hoffmann
2021-11-01 13:15 ` [PATCH V3 06/29] MdePkg: Add helper functions for Tdx guest in BaseIoLibIntrinsic Min Xu
2021-11-01 13:15 ` [PATCH V3 07/29] MdePkg: Support mmio " Min Xu
2021-11-01 13:15 ` [PATCH V3 08/29] MdePkg: Support IoFifo " Min Xu
2021-11-01 13:15 ` [PATCH V3 09/29] MdePkg: Support IoRead/IoWrite " Min Xu
2021-11-01 13:15 ` [PATCH V3 10/29] UefiPayloadPkg: PreparePrepare UefiPayloadPkg to use TdxLib Min Xu
2021-11-01 15:31   ` Guo Dong
2021-11-01 15:58   ` Ma, Maurice
2021-11-02  0:07     ` Min Xu
2021-11-02 14:32   ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 11/29] UefiCpuPkg: Support TDX in BaseXApicX2ApicLib Min Xu
2021-11-02 14:33   ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 12/29] UefiCpuPkg: Define ConfidentialComputingGuestAttr Min Xu
2021-11-02 14:36   ` Gerd Hoffmann
2021-11-03  8:32     ` [edk2-devel] " Min Xu
2021-11-01 13:16 ` [PATCH V3 13/29] MdePkg: Add macro to check SEV/TDX guest Min Xu
2021-11-02 14:36   ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 14/29] UefiCpuPkg: Enable Tdx support in MpInitLib Min Xu
2021-11-03  6:09   ` Gerd Hoffmann
2021-11-03 12:57     ` Min Xu
2021-11-04  8:10       ` Gerd Hoffmann
2021-11-04 15:21         ` Lendacky, Thomas
2021-11-04 23:24           ` Min Xu
2021-11-05  6:46             ` [edk2-devel] " Gerd Hoffmann
2021-11-05  6:53               ` Min Xu
2021-11-09  2:44               ` Min Xu
2021-11-01 13:16 ` [PATCH V3 15/29] OvmfPkg: Update SecEntry.nasm to support Tdx Min Xu
2021-11-03  6:30   ` Gerd Hoffmann
2021-11-16 12:11     ` Min Xu
2021-11-17 15:19       ` Gerd Hoffmann
2021-11-18  9:59         ` Yao, Jiewen
2021-11-19 15:11           ` Gerd Hoffmann
2021-11-20  3:18             ` Yao, Jiewen
2021-11-23 12:38               ` Gerd Hoffmann
2021-11-23 13:07                 ` Yao, Jiewen
2021-11-23 14:26                   ` James Bottomley
2021-11-23 14:36                     ` Yao, Jiewen
2021-11-23 14:51                       ` James Bottomley
2021-11-23 15:10                         ` Yao, Jiewen
2021-11-23 15:37                           ` [edk2-devel] " James Bottomley
2021-11-24  3:15                             ` Yao, Jiewen
2021-11-24  8:12                               ` Gerd Hoffmann
2021-11-24 11:08                                 ` Yao, Jiewen
2021-11-24 13:35                                   ` James Bottomley
2021-11-24 14:03                                     ` Yao, Jiewen
2021-11-24 14:07                                       ` James Bottomley
2021-11-24 14:59                                         ` Yao, Jiewen
2021-11-25  8:32                                           ` Gerd Hoffmann
2021-11-26  6:29                                             ` Yao, Jiewen
2021-12-01 13:55                                               ` Gerd Hoffmann
2021-12-02 13:22                                                 ` Yao, Jiewen
2021-12-06 14:57                                                   ` Gerd Hoffmann
2021-12-07  2:28                                                     ` Yao, Jiewen
2021-12-07  8:04                                                       ` Gerd Hoffmann
2021-12-08  5:13                                                         ` Min Xu
     [not found]                                         ` <16BA8381113E7B1B.22735@groups.io>
2021-11-24 15:30                                           ` Yao, Jiewen
     [not found]                             ` <16BA5D1709524394.9880@groups.io>
2021-11-24  3:21                               ` Yao, Jiewen
2021-11-01 13:16 ` [PATCH V3 16/29] OvmfPkg: Add IntelTdx.h in OvmfPkg/Include/IndustryStandard Min Xu
2021-11-01 13:16 ` [PATCH V3 17/29] OvmfPkg: Add TdxMailboxLib Min Xu
2021-11-01 13:16 ` [PATCH V3 18/29] MdePkg: Add EFI_RESOURCE_ATTRIBUTE_ENCRYPTED in PiHob.h Min Xu
2021-11-01 13:16 ` Min Xu [this message]
2021-11-01 13:16 ` [PATCH V3 20/29] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation Min Xu
2021-11-03  6:50   ` Gerd Hoffmann
2021-11-03 13:07     ` Min Xu
2021-11-03 13:35     ` Min Xu
2021-11-04 14:36       ` Brijesh Singh
2021-11-01 13:16 ` [PATCH V3 21/29] MdeModulePkg: EFER should not be changed in TDX Min Xu
2021-11-03  6:51   ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 22/29] MdeModulePkg: Set shared bit in Mmio region for Tdx guest Min Xu
2021-11-03  6:57   ` Gerd Hoffmann
2021-11-04  7:03     ` [edk2-devel] " Min Xu
2021-11-01 13:16 ` [PATCH V3 23/29] UefiCpuPkg: Update AddressEncMask in CpuPageTable Min Xu
2021-11-03  7:00   ` Gerd Hoffmann
2021-11-22  3:09     ` [edk2-devel] " Ni, Ray
2021-12-07  3:50       ` Min Xu
2021-12-07  7:15         ` Gerd Hoffmann
2021-11-01 13:16 ` [PATCH V3 24/29] OvmfPkg: Update PlatformPei to support TDX Min Xu
2021-11-01 13:16 ` [PATCH V3 25/29] OvmfPkg: Update AcpiPlatformDxe to alter MADT table Min Xu
2021-11-01 13:16 ` [PATCH V3 26/29] OvmfPkg: Add TdxDxe driver Min Xu
2021-11-01 13:16 ` [PATCH V3 27/29] OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library Min Xu
2021-11-03  7:10   ` Gerd Hoffmann
2021-12-08  8:37     ` [edk2-devel] " Min Xu
2021-11-01 13:16 ` [PATCH V3 28/29] OvmfPkg/QemuFwCfgLib: Support Tdx in QemuFwCfgDxe Min Xu
2021-11-03  7:12   ` Gerd Hoffmann
2021-12-13  2:06     ` Min Xu
2021-11-01 13:16 ` [PATCH V3 29/29] OvmfPkg: Update IoMmuDxe to support TDX Min Xu
2021-11-03  7:17   ` Gerd Hoffmann
2021-12-13  2:39     ` [edk2-devel] " Min Xu
2021-12-13  6:42       ` Gerd Hoffmann
2021-12-13  7:33         ` Min Xu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a4233a25a63f2e3c67512eaf83094f448ad259e0.1635769996.git.min.m.xu@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox