public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Zurcher, Christopher J" <christopher.j.zurcher@intel.com>
To: devel@edk2.groups.io
Cc: Laszlo Ersek <lersek@redhat.com>,
	Jiewen Yao <jiewen.yao@intel.com>,
	Jian J Wang <jian.j.wang@intel.com>,
	Xiaoyu Lu <xiaoyux.lu@intel.com>
Subject: [PATCH v2 2/3] CryptoPkg: Add EVP to Crypto Service driver interface
Date: Mon, 14 Sep 2020 17:57:48 -0700	[thread overview]
Message-ID: <20200915005749.5331-3-christopher.j.zurcher@intel.com> (raw)
In-Reply-To: <20200915005749.5331-1-christopher.j.zurcher@intel.com>

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

Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Xiaoyu Lu <xiaoyux.lu@intel.com>
Signed-off-by: Christopher J Zurcher <christopher.j.zurcher@intel.com>
---
 CryptoPkg/CryptoPkg.dsc                                |   3 +
 CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h   |  10 ++
 CryptoPkg/Private/Protocol/Crypto.h                    | 127 +++++++++++++++++
 CryptoPkg/Driver/Crypto.c                              | 148 +++++++++++++++++++-
 CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c | 140 ++++++++++++++++++
 5 files changed, 427 insertions(+), 1 deletion(-)

diff --git a/CryptoPkg/CryptoPkg.dsc b/CryptoPkg/CryptoPkg.dsc
index 1af78468a1..af3fceb99f 100644
--- a/CryptoPkg/CryptoPkg.dsc
+++ b/CryptoPkg/CryptoPkg.dsc
@@ -159,6 +159,7 @@
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Tls.Family                               | PCD_CRYPTO_SERVICE_ENABLE_FAMILY
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.TlsSet.Family                            | PCD_CRYPTO_SERVICE_ENABLE_FAMILY
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.TlsGet.Family                            | PCD_CRYPTO_SERVICE_ENABLE_FAMILY
+  gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.EvpMd.Family                             | PCD_CRYPTO_SERVICE_ENABLE_FAMILY
 !endif
 
 !if $(CRYPTO_SERVICES) == MIN_PEI
@@ -173,6 +174,7 @@
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Rsa.Services.Free               | TRUE
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Rsa.Services.SetKey             | TRUE
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Pkcs.Services.Pkcs5HashPassword | TRUE
+  gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.EvpMd.Family                    | PCD_CRYPTO_SERVICE_ENABLE_FAMILY
 !endif
 
 !if $(CRYPTO_SERVICES) == MIN_DXE_MIN_SMM
@@ -203,6 +205,7 @@
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Aes.Services.Init                        | TRUE
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Aes.Services.CbcEncrypt                  | TRUE
   gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.Aes.Services.CbcDecrypt                  | TRUE
+  gEfiCryptoPkgTokenSpaceGuid.PcdCryptoServiceFamilyEnable.EvpMd.Family                             | PCD_CRYPTO_SERVICE_ENABLE_FAMILY
 !endif
 
 ###################################################################################################
diff --git a/CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h b/CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h
index 44fb0262f4..b79c98d679 100644
--- a/CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h
+++ b/CryptoPkg/Include/Pcd/PcdCryptoServiceFamilyEnable.h
@@ -288,6 +288,16 @@ typedef struct {
     } Services;
     UINT32    Family;
   } TlsGet;
+  union {
+    struct {
+      UINT8  Init:1;
+      UINT8  Duplicate:1;
+      UINT8  Update:1;
+      UINT8  Final:1;
+      UINT8  HashAll:1;
+    } Services;
+    UINT32    Family;
+  } EvpMd;
 } PCD_CRYPTO_SERVICE_FAMILY_ENABLE;
 
 #endif
diff --git a/CryptoPkg/Private/Protocol/Crypto.h b/CryptoPkg/Private/Protocol/Crypto.h
index c399e0d67a..84f1bc3f50 100644
--- a/CryptoPkg/Private/Protocol/Crypto.h
+++ b/CryptoPkg/Private/Protocol/Crypto.h
@@ -3434,6 +3434,127 @@ EFI_STATUS
   IN OUT UINTN                    *DataSize
   );
 
+/**
+  Allocates and initializes one EVP_MD_CTX context for subsequent EVP_MD use.
+
+  If DigestName is NULL, then return FALSE.
+
+  @param[in]    DigestName    Pointer to the digest name.
+
+  @return  Pointer to the EVP_MD_CTX context that has been allocated and initialized.
+           If DigestName is invalid, returns NULL.
+           If the allocations fails, returns NULL.
+           If initialization fails, returns NULL.
+
+**/
+typedef
+VOID *
+(EFIAPI* EDKII_CRYPTO_EVPMD_INIT)(
+  IN  CONST CHAR8   *DigestName
+  );
+
+/**
+  Makes a copy of an existing EVP_MD context.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If NewEvpMdContext is NULL, then return FALSE.
+
+  @param[in]  EvpMdContext     Pointer to EVP_MD context being copied.
+  @param[out] NewEvpMdContext  Pointer to new EVP_MD context.
+
+  @retval TRUE   EVP_MD context copy succeeded.
+  @retval FALSE  EVP_MD context copy failed.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI* EDKII_CRYPTO_EVPMD_DUPLICATE)(
+  IN  CONST VOID    *EvpMdContext,
+  OUT VOID          *NewEvpMdContext
+  );
+
+/**
+  Digests the input data and updates EVP_MD context.
+
+  This function performs EVP digest on a data buffer of the specified size.
+  It can be called multiple times to compute the digest of long or discontinuous data streams.
+  EVP_MD context should be already correctly initialized by EvpMdInit(), and should not
+  be finalized by EvpMdFinal(). Behavior with invalid context is undefined.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If Data is NULL and DataSize is not zero, return FALSE.
+
+  @param[in, out]  EvpMdContext       Pointer to the EVP_MD context.
+  @param[in]       Data               Pointer to the buffer containing the data to be digested.
+  @param[in]       DataSize           Size of Data buffer in bytes.
+
+  @retval TRUE   EVP data digest succeeded.
+  @retval FALSE  EVP data digest failed.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI* EDKII_CRYPTO_EVPMD_UPDATE)(
+  IN OUT  VOID        *EvpMdContext,
+  IN      CONST VOID  *Data,
+  IN      UINTN       DataSize
+  );
+
+/**
+  Completes computation of the EVP digest value.
+  Releases the specified EVP_MD_CTX context.
+
+  This function completes EVP hash computation and retrieves the digest value into
+  the specified memory. After this function has been called, the EVP context cannot
+  be used again.
+  EVP context should be already correctly initialized by EvpMdInit(), and should
+  not be finalized by EvpMdFinal(). Behavior with invalid EVP context is undefined.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If DigestValue is NULL, free the Context then return FALSE.
+
+  @param[in, out]  EvpMdContext   Pointer to the EVP context.
+  @param[out]      Digest         Pointer to a buffer that receives the EVP digest value.
+
+  @retval TRUE   EVP digest computation succeeded.
+  @retval FALSE  EVP digest computation failed.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI* EDKII_CRYPTO_EVPMD_FINAL)(
+  IN OUT  VOID   *EvpMdContext,
+  OUT     UINT8  *DigestValue
+  );
+
+/**
+  Computes the message digest of an input data buffer.
+
+  This function performs the message digest of a given data buffer, and places
+  the digest value into the specified memory.
+
+  If DigestName is NULL, return FALSE.
+  If Data is NULL and DataSize is not zero, return FALSE.
+  If HashValue is NULL, return FALSE.
+
+  @param[in]    DigestName    Pointer to the digest name.
+  @param[in]    Data          Pointer to the buffer containing the data to be hashed.
+  @param[in]    DataSize      Size of Data buffer in bytes.
+  @param[out]   HashValue     Pointer to a buffer that receives the digest value.
+
+  @retval TRUE   Digest computation succeeded.
+  @retval FALSE  Digest computation failed.
+
+**/
+typedef
+BOOLEAN
+(EFIAPI* EDKII_CRYPTO_EVPMD_HASH_ALL)(
+  IN  CONST CHAR8   *DigestName,
+  IN  CONST VOID    *Data,
+  IN  UINTN         DataSize,
+  OUT UINT8         *HashValue
+  );
+
 
 ///
 /// EDK II Crypto Protocol
@@ -3619,6 +3740,12 @@ struct _EDKII_CRYPTO_PROTOCOL {
   EDKII_CRYPTO_TLS_GET_HOST_PUBLIC_CERT           TlsGetHostPublicCert;
   EDKII_CRYPTO_TLS_GET_HOST_PRIVATE_KEY           TlsGetHostPrivateKey;
   EDKII_CRYPTO_TLS_GET_CERT_REVOCATION_LIST       TlsGetCertRevocationList;
+  /// Digest Envelope (EVP MD)
+  EDKII_CRYPTO_EVPMD_INIT                         EvpMdInit;
+  EDKII_CRYPTO_EVPMD_DUPLICATE                    EvpMdDuplicate;
+  EDKII_CRYPTO_EVPMD_UPDATE                       EvpMdUpdate;
+  EDKII_CRYPTO_EVPMD_FINAL                        EvpMdFinal;
+  EDKII_CRYPTO_EVPMD_HASH_ALL                     EvpMdHashAll;
 };
 
 extern GUID gEdkiiCryptoProtocolGuid;
diff --git a/CryptoPkg/Driver/Crypto.c b/CryptoPkg/Driver/Crypto.c
index d9096ea603..a49af46e38 100644
--- a/CryptoPkg/Driver/Crypto.c
+++ b/CryptoPkg/Driver/Crypto.c
@@ -4463,6 +4463,146 @@ CryptoServiceTlsGetCertRevocationList (
   return CALL_BASECRYPTLIB (TlsGet.Services.CertRevocationList, TlsGetCertRevocationList, (Data, DataSize), EFI_UNSUPPORTED);
 }
 
+//=====================================================================================
+//    EVP (Envelope) Primitive
+//=====================================================================================
+
+/**
+  Allocates and initializes one EVP_MD_CTX context for subsequent EVP_MD use.
+
+  If DigestName is NULL, then return FALSE.
+
+  @param[in]    DigestName    Pointer to the digest name.
+
+  @return  Pointer to the EVP_MD_CTX context that has been allocated and initialized.
+           If DigestName is invalid, returns NULL.
+           If the allocations fails, returns NULL.
+           If initialization fails, returns NULL.
+
+**/
+VOID *
+EFIAPI
+CryptoServiceEvpMdInit (
+  IN  CONST CHAR8   *DigestName
+  )
+{
+  return CALL_BASECRYPTLIB (EvpMd.Services.Init, EvpMdInit, (DigestName), NULL);
+}
+
+/**
+  Makes a copy of an existing EVP_MD context.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If NewEvpMdContext is NULL, then return FALSE.
+
+  @param[in]  EvpMdContext     Pointer to EVP_MD context being copied.
+  @param[out] NewEvpMdContext  Pointer to new EVP_MD context.
+
+  @retval TRUE   EVP_MD context copy succeeded.
+  @retval FALSE  EVP_MD context copy failed.
+
+**/
+BOOLEAN
+EFIAPI
+CryptoServiceEvpMdDuplicate (
+  IN  CONST VOID    *EvpMdContext,
+  OUT VOID          *NewEvpMdContext
+  )
+{
+  return CALL_BASECRYPTLIB (EvpMd.Services.Duplicate, EvpMdDuplicate, (EvpMdContext, NewEvpMdContext), FALSE);
+}
+
+/**
+  Digests the input data and updates EVP_MD context.
+
+  This function performs EVP digest on a data buffer of the specified size.
+  It can be called multiple times to compute the digest of long or discontinuous data streams.
+  EVP_MD context should be already correctly initialized by EvpMdInit(), and should not
+  be finalized by EvpMdFinal(). Behavior with invalid context is undefined.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If Data is NULL and DataSize is not zero, return FALSE.
+
+  @param[in, out]  EvpMdContext       Pointer to the EVP_MD context.
+  @param[in]       Data               Pointer to the buffer containing the data to be digested.
+  @param[in]       DataSize           Size of Data buffer in bytes.
+
+  @retval TRUE   EVP data digest succeeded.
+  @retval FALSE  EVP data digest failed.
+
+**/
+BOOLEAN
+EFIAPI
+CryptoServiceEvpMdUpdate (
+  IN OUT  VOID        *EvpMdContext,
+  IN      CONST VOID  *Data,
+  IN      UINTN       DataSize
+  )
+{
+  return CALL_BASECRYPTLIB (EvpMd.Services.Update, EvpMdUpdate, (EvpMdContext, Data, DataSize), FALSE);
+}
+
+/**
+  Completes computation of the EVP digest value.
+  Releases the specified EVP_MD_CTX context.
+
+  This function completes EVP hash computation and retrieves the digest value into
+  the specified memory. After this function has been called, the EVP context cannot
+  be used again.
+  EVP context should be already correctly initialized by EvpMdInit(), and should
+  not be finalized by EvpMdFinal(). Behavior with invalid EVP context is undefined.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If DigestValue is NULL, free the Context then return FALSE.
+
+  @param[in, out]  EvpMdContext   Pointer to the EVP context.
+  @param[out]      Digest         Pointer to a buffer that receives the EVP digest value.
+
+  @retval TRUE   EVP digest computation succeeded.
+  @retval FALSE  EVP digest computation failed.
+
+**/
+BOOLEAN
+EFIAPI
+CryptoServiceEvpMdFinal (
+  IN OUT  VOID   *EvpMdContext,
+  OUT     UINT8  *DigestValue
+  )
+{
+  return CALL_BASECRYPTLIB (EvpMd.Services.Final, EvpMdFinal, (EvpMdContext, DigestValue), FALSE);
+}
+
+/**
+  Computes the message digest of an input data buffer.
+
+  This function performs the message digest of a given data buffer, and places
+  the digest value into the specified memory.
+
+  If DigestName is NULL, return FALSE.
+  If Data is NULL and DataSize is not zero, return FALSE.
+  If HashValue is NULL, return FALSE.
+
+  @param[in]    DigestName    Pointer to the digest name.
+  @param[in]    Data          Pointer to the buffer containing the data to be hashed.
+  @param[in]    DataSize      Size of Data buffer in bytes.
+  @param[out]   HashValue     Pointer to a buffer that receives the digest value.
+
+  @retval TRUE   Digest computation succeeded.
+  @retval FALSE  Digest computation failed.
+
+**/
+BOOLEAN
+EFIAPI
+CryptoServiceEvpMdHashAll (
+  IN  CONST CHAR8   *DigestName,
+  IN  CONST VOID    *Data,
+  IN  UINTN         DataSize,
+  OUT UINT8         *HashValue
+  )
+{
+  return CALL_BASECRYPTLIB (EvpMd.Services.HashAll, EvpMdHashAll, (DigestName, Data, DataSize, HashValue), FALSE);
+}
+
 const EDKII_CRYPTO_PROTOCOL mEdkiiCrypto = {
   /// Version
   CryptoServiceGetCryptoVersion,
@@ -4663,5 +4803,11 @@ const EDKII_CRYPTO_PROTOCOL mEdkiiCrypto = {
   CryptoServiceTlsGetCaCertificate,
   CryptoServiceTlsGetHostPublicCert,
   CryptoServiceTlsGetHostPrivateKey,
-  CryptoServiceTlsGetCertRevocationList
+  CryptoServiceTlsGetCertRevocationList,
+  /// Digest Envelope (EVP MD)
+  CryptoServiceEvpMdInit,
+  CryptoServiceEvpMdDuplicate,
+  CryptoServiceEvpMdUpdate,
+  CryptoServiceEvpMdFinal,
+  CryptoServiceEvpMdHashAll
 };
diff --git a/CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c b/CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c
index 3f14c6d262..0d98bcb09c 100644
--- a/CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c
+++ b/CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c
@@ -3499,3 +3499,143 @@ TlsGetCertRevocationList (
 {
   CALL_CRYPTO_SERVICE (TlsGetCertRevocationList, (Data, DataSize), EFI_UNSUPPORTED);
 }
+
+//=====================================================================================
+//    EVP (Envelope) Primitive
+//=====================================================================================
+
+/**
+  Allocates and initializes one EVP_MD_CTX context for subsequent EVP_MD use.
+
+  If DigestName is NULL, then return FALSE.
+
+  @param[in]    DigestName    Pointer to the digest name.
+
+  @return  Pointer to the EVP_MD_CTX context that has been allocated and initialized.
+           If DigestName is invalid, returns NULL.
+           If the allocations fails, returns NULL.
+           If initialization fails, returns NULL.
+
+**/
+VOID *
+EFIAPI
+EvpMdInit (
+  IN  CONST CHAR8   *DigestName
+  )
+{
+  CALL_CRYPTO_SERVICE (EvpMdInit, (DigestName), NULL);
+}
+
+/**
+  Makes a copy of an existing EVP_MD context.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If NewEvpMdContext is NULL, then return FALSE.
+
+  @param[in]  EvpMdContext     Pointer to EVP_MD context being copied.
+  @param[out] NewEvpMdContext  Pointer to new EVP_MD context.
+
+  @retval TRUE   EVP_MD context copy succeeded.
+  @retval FALSE  EVP_MD context copy failed.
+
+**/
+BOOLEAN
+EFIAPI
+EvpMdDuplicate (
+  IN  CONST VOID    *EvpMdContext,
+  OUT VOID          *NewEvpMdContext
+  )
+{
+  CALL_CRYPTO_SERVICE (EvpMdDuplicate, (EvpMdContext, NewEvpMdContext), FALSE);
+}
+
+/**
+  Digests the input data and updates EVP_MD context.
+
+  This function performs EVP digest on a data buffer of the specified size.
+  It can be called multiple times to compute the digest of long or discontinuous data streams.
+  EVP_MD context should be already correctly initialized by EvpMdInit(), and should not
+  be finalized by EvpMdFinal(). Behavior with invalid context is undefined.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If Data is NULL and DataSize is not zero, return FALSE.
+
+  @param[in, out]  EvpMdContext       Pointer to the EVP_MD context.
+  @param[in]       Data               Pointer to the buffer containing the data to be digested.
+  @param[in]       DataSize           Size of Data buffer in bytes.
+
+  @retval TRUE   EVP data digest succeeded.
+  @retval FALSE  EVP data digest failed.
+
+**/
+BOOLEAN
+EFIAPI
+EvpMdUpdate (
+  IN OUT  VOID        *EvpMdContext,
+  IN      CONST VOID  *Data,
+  IN      UINTN       DataSize
+  )
+{
+  CALL_CRYPTO_SERVICE (EvpMdUpdate, (EvpMdContext, Data, DataSize), FALSE);
+}
+
+/**
+  Completes computation of the EVP digest value.
+  Releases the specified EVP_MD_CTX context.
+
+  This function completes EVP hash computation and retrieves the digest value into
+  the specified memory. After this function has been called, the EVP context cannot
+  be used again.
+  EVP context should be already correctly initialized by EvpMdInit(), and should
+  not be finalized by EvpMdFinal(). Behavior with invalid EVP context is undefined.
+
+  If EvpMdContext is NULL, then return FALSE.
+  If DigestValue is NULL, free the Context then return FALSE.
+
+  @param[in, out]  EvpMdContext   Pointer to the EVP context.
+  @param[out]      Digest         Pointer to a buffer that receives the EVP digest value.
+
+  @retval TRUE   EVP digest computation succeeded.
+  @retval FALSE  EVP digest computation failed.
+
+**/
+BOOLEAN
+EFIAPI
+EvpMdFinal (
+  IN OUT  VOID   *EvpMdContext,
+  OUT     UINT8  *DigestValue
+  )
+{
+  CALL_CRYPTO_SERVICE (EvpMdFinal, (EvpMdContext, DigestValue), FALSE);
+}
+
+/**
+  Computes the message digest of an input data buffer.
+
+  This function performs the message digest of a given data buffer, and places
+  the digest value into the specified memory.
+
+  If DigestName is NULL, return FALSE.
+  If Data is NULL and DataSize is not zero, return FALSE.
+  If HashValue is NULL, return FALSE.
+
+  @param[in]    DigestName    Pointer to the digest name.
+  @param[in]    Data          Pointer to the buffer containing the data to be hashed.
+  @param[in]    DataSize      Size of Data buffer in bytes.
+  @param[out]   HashValue     Pointer to a buffer that receives the digest value.
+
+  @retval TRUE   Digest computation succeeded.
+  @retval FALSE  Digest computation failed.
+
+**/
+BOOLEAN
+EFIAPI
+EvpMdHashAll (
+  IN  CONST CHAR8   *DigestName,
+  IN  CONST VOID    *Data,
+  IN  UINTN         DataSize,
+  OUT UINT8         *HashValue
+  )
+{
+  CALL_CRYPTO_SERVICE (EvpMdHashAll, (DigestName, Data, DataSize, HashValue), FALSE);
+}
-- 
2.28.0.windows.1


  parent reply	other threads:[~2020-09-15  0:58 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-15  0:57 [PATCH v2 0/3] CryptoPkg/BaseCryptLib: Add EVP (Envelope) Digest interface Zurcher, Christopher J
2020-09-15  0:57 ` [PATCH v2 1/3] " Zurcher, Christopher J
2020-09-15  0:57 ` Zurcher, Christopher J [this message]
2020-09-15  0:57 ` [PATCH v2 3/3] SecurityPkg/Hash2DxeCrypto: Rebase Hash2DxeCrypto onto the EVP interface Zurcher, Christopher J
2020-09-15  1:21 ` [PATCH v2 0/3] CryptoPkg/BaseCryptLib: Add EVP (Envelope) Digest interface Yao, Jiewen
2020-09-15  2:54   ` Zurcher, Christopher J
2020-09-15  2:58     ` Yao, Jiewen
2020-09-15  8:01     ` [edk2-devel] " Laszlo Ersek

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=20200915005749.5331-3-christopher.j.zurcher@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