From: "Min Xu" <min.m.xu@intel.com>
To: devel@edk2.groups.io
Cc: Min M Xu <min.m.xu@intel.com>,
Erdem Aktas <erdemaktas@google.com>,
James Bottomley <jejb@linux.ibm.com>,
Jiewen Yao <jiewen.yao@intel.com>,
Gerd Hoffmann <kraxel@redhat.com>,
Tom Lendacky <thomas.lendacky@amd.com>,
Michael Roth <michael.roth@amd.com>
Subject: [PATCH V2 04/10] OvmfPkg/IntelTdx: Implement other helper functions in SecTdxHelperLib
Date: Thu, 19 Jan 2023 11:28:16 +0800 [thread overview]
Message-ID: <20230119032822.1406-5-min.m.xu@intel.com> (raw)
In-Reply-To: <20230119032822.1406-1-min.m.xu@intel.com>
From: Min M Xu <min.m.xu@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
This patch implements below helper functions in SecTdxHelperLib:
- 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.
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Michael Roth <michael.roth@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c | 150 +++++++++-
.../IntelTdx/TdxHelperLib/SecTdxHelperLib.inf | 1 +
.../IntelTdx/TdxHelperLib/TdxMeasurementHob.c | 266 ++++++++++++++++++
3 files changed, 414 insertions(+), 3 deletions(-)
create mode 100644 OvmfPkg/IntelTdx/TdxHelperLib/TdxMeasurementHob.c
diff --git a/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c b/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c
index 4fb2d2a8acad..82242d37d1ed 100644
--- a/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelper.c
@@ -32,6 +32,18 @@
#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.
@@ -793,6 +805,67 @@ TdxHelperProcessTdHob (
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
@@ -807,7 +880,47 @@ TdxHelperMeasureTdHob (
VOID
)
{
- return EFI_UNSUPPORTED;
+ 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;
}
/**
@@ -824,10 +937,37 @@ TdxHelperMeasureCfvImage (
VOID
)
{
- return EFI_UNSUPPORTED;
+ 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.
@@ -841,5 +981,9 @@ 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
index 3c6b96f7759a..d17b84c01f20 100644
--- a/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelperLib.inf
+++ b/OvmfPkg/IntelTdx/TdxHelperLib/SecTdxHelperLib.inf
@@ -24,6 +24,7 @@
[Sources]
SecTdxHelper.c
+ TdxMeasurementHob.c
[Packages]
CryptoPkg/CryptoPkg.dec
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;
+}
--
2.29.2.windows.2
next prev parent reply other threads:[~2023-01-19 3:28 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-01-19 3:28 [PATCH V2 00/10] Enable Tdx measurement in OvmfPkgX64 Min Xu
2023-01-19 3:28 ` [PATCH V2 01/10] OvmfPkg: Add Tdx measurement data structure in WorkArea Min Xu
2023-01-19 9:33 ` Gerd Hoffmann
2023-01-19 3:28 ` [PATCH V2 02/10] OvmfPkg/IntelTdx: Add TdxHelperLibNull Min Xu
2023-01-19 9:33 ` Gerd Hoffmann
2023-01-19 3:28 ` [PATCH V2 03/10] OvmfPkg/IntelTdx: Add SecTdxHelperLib Min Xu
2023-01-19 9:33 ` Gerd Hoffmann
2023-01-19 3:28 ` Min Xu [this message]
2023-01-19 9:54 ` [PATCH V2 04/10] OvmfPkg/IntelTdx: Implement other helper functions in SecTdxHelperLib Gerd Hoffmann
2023-01-19 23:44 ` Min Xu
2023-01-20 7:40 ` Min Xu
2023-01-20 8:10 ` Yao, Jiewen
2023-01-20 10:18 ` Gerd Hoffmann
2023-01-20 11:42 ` Min Xu
2023-01-20 13:50 ` Gerd Hoffmann
2023-01-21 0:02 ` Min Xu
2023-01-19 3:28 ` [PATCH V2 05/10] OvmfPkg/IntelTdx: Add PeiTdxHelperLib Min Xu
2023-01-19 9:54 ` Gerd Hoffmann
2023-01-19 3:28 ` [PATCH V2 06/10] OvmfPkg/PeilessStartupLib: Build GuidHob for Tdx measurements Min Xu
2023-01-19 9:57 ` Gerd Hoffmann
2023-01-19 3:28 ` [PATCH V2 07/10] OvmfPkg/IntelTdx: Update tdx measurement in SEC phase Min Xu
2023-01-19 9:57 ` Gerd Hoffmann
2023-01-19 3:28 ` [PATCH V2 08/10] OvmfPkg: Enable Tdx measurement in OvmfPkgX64 Min Xu
2023-01-19 3:28 ` [PATCH V2 09/10] OvmfPkg/PlatformPei: Build GuidHob for Tdx measurement Min Xu
2023-01-19 9:58 ` Gerd Hoffmann
2023-01-19 3:28 ` [PATCH V2 10/10] OvmfPkg/PlatformInitLib: Delete the ProcessTdxHobList() 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=20230119032822.1406-5-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