From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: Laszlo Ersek <lersek@redhat.com>
Cc: edk2-devel-01 <edk2-devel@lists.01.org>,
Jordan Justen <jordan.l.justen@intel.com>
Subject: Re: [PATCH v2 2/7] OvmfPkg: introduce PciCapPciSegmentLib
Date: Thu, 24 May 2018 10:08:56 +0200 [thread overview]
Message-ID: <CAKv+Gu8QKo0da=sUWBB5LeMMFZ-TfSF0JiayTUPs+iybhNrPUg@mail.gmail.com> (raw)
In-Reply-To: <20180523202121.8125-3-lersek@redhat.com>
On 23 May 2018 at 22:21, Laszlo Ersek <lersek@redhat.com> wrote:
> Add a library class, and a BASE lib instance, that are layered on top of
> PciCapLib, and allow clients to plug a PciSegmentLib backend into
> PciCapLib, for config space access.
>
> (Side note:
>
For the record: I am fine with keeping this side note in the commit log.
> The "MaxDomain" parameter is provided because, in practice, platforms
> exist where a PCI Express device may show up on a root bridge such that
> the root bridge doesn't support access to extended config space. Earlier
> the same issue was handled for MdeModulePkg/PciHostBridgeDxe in commit
> 014b472053ae3. However, that solution does not apply to the PciSegmentLib
> class, because:
>
> (1) The config space accessor functions of the PciSegmentLib class, such
> as PciSegmentReadBuffer(), have no way of informing the caller whether
> access to extended config space actually succeeds.
>
> (For example, in the UefiPciSegmentLibPciRootBridgeIo instace, which
> could in theory benefit from commit 014b472053ae3, the
> EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.Pci.Read() status code is explicitly
> ignored, because there's no way for the lib instance to propagate it
> to the PciSegmentLib caller. If the
> EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.Pci.Read() call fails, then
> DxePciSegmentLibPciRootBridgeIoReadWorker() returns Data with
> indeterminate value.)
>
> (2) There is no *general* way for any firmware platform to provide, or
> use, a PciSegmentLib instance in which access to extended config space
> always succeeds.
>
> In brief, on a platform where config space may be limited to 256 bytes,
> access to extended config space through PciSegmentLib may invoke undefined
> behavior; therefore PciCapPciSegmentLib must give platforms a way to
> prevent such access.)
>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
>
> Notes:
> v2:
> - move library from MdePkg to OvmfPkg, for initial introduction
>
> OvmfPkg/OvmfPkg.dec | 5 +
> OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf | 35 +++
> OvmfPkg/Include/Library/PciCapPciSegmentLib.h | 82 +++++++
> OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h | 47 ++++
> OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c | 226 ++++++++++++++++++++
> 5 files changed, 395 insertions(+)
>
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 74818a2e2a19..fbec1cfe4a8e 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -35,6 +35,11 @@ [LibraryClasses]
> # config space.
> PciCapLib|Include/Library/PciCapLib.h
>
> + ## @libraryclass Layered on top of PciCapLib, allows clients to plug a
> + # PciSegmentLib backend into PciCapLib, for config space
> + # access.
> + PciCapPciSegmentLib|Include/Library/PciCapPciSegmentLib.h
> +
> ## @libraryclass Access QEMU's firmware configuration interface
> #
> QemuFwCfgLib|Include/Library/QemuFwCfgLib.h
> diff --git a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf
> new file mode 100644
> index 000000000000..e3cf5de49b15
> --- /dev/null
> +++ b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf
> @@ -0,0 +1,35 @@
> +## @file
> +# Plug a PciSegmentLib backend into PciCapLib, for config space access.
> +#
> +# Copyright (C) 2018, Red Hat, Inc.
> +#
> +# This program and the accompanying materials are licensed and made available
> +# under the terms and conditions of the BSD License which accompanies this
> +# distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
> +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +##
> +
> +[Defines]
> + INF_VERSION = 1.27
> + BASE_NAME = BasePciCapPciSegmentLib
> + FILE_GUID = ED011855-AA31-43B9-ACC0-BF45A05C5985
> + MODULE_TYPE = BASE
> + VERSION_STRING = 1.0
> + LIBRARY_CLASS = PciCapPciSegmentLib
> +
> +[Sources]
> + BasePciCapPciSegmentLib.h
> + BasePciCapPciSegmentLib.c
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + OvmfPkg/OvmfPkg.dec
> +
> +[LibraryClasses]
> + BaseLib
> + DebugLib
> + MemoryAllocationLib
> + PciSegmentLib
> diff --git a/OvmfPkg/Include/Library/PciCapPciSegmentLib.h b/OvmfPkg/Include/Library/PciCapPciSegmentLib.h
> new file mode 100644
> index 000000000000..6b6930288d16
> --- /dev/null
> +++ b/OvmfPkg/Include/Library/PciCapPciSegmentLib.h
> @@ -0,0 +1,82 @@
> +/** @file
> + Library class layered on top of PciCapLib that allows clients to plug a
> + PciSegmentLib backend into PciCapLib, for config space access.
> +
> + Copyright (C) 2018, Red Hat, Inc.
> +
> + This program and the accompanying materials are licensed and made available
> + under the terms and conditions of the BSD License which accompanies this
> + distribution. The full text of the license may be found at
> + http://opensource.org/licenses/bsd-license.php
> +
> + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
> + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +**/
> +
> +#ifndef __PCI_CAP_PCI_SEGMENT_LIB_H__
> +#define __PCI_CAP_PCI_SEGMENT_LIB_H__
> +
> +#include <Library/PciCapLib.h>
> +
> +
> +/**
> + Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function
> + quadruplet. The config space accessors are based upon PciSegmentLib.
> +
> + @param[in] MaxDomain If MaxDomain is PciCapExtended, then
> + PciDevice->ReadConfig() and PciDevice->WriteConfig()
> + will delegate extended config space accesses too to
> + PciSegmentReadBuffer() and PciSegmentWriteBuffer(),
> + respectively. Otherwise, PciDevice->ReadConfig() and
> + PciDevice->WriteConfig() will reject accesses to
> + extended config space with RETURN_UNSUPPORTED, without
> + calling PciSegmentReadBuffer() or
> + PciSegmentWriteBuffer(). By setting MaxDomain to
> + PciCapNormal, the platform can prevent undefined
> + PciSegmentLib behavior when the PCI root bridge under
> + the PCI device at Segment:Bus:Device.Function doesn't
> + support extended config space.
> +
> + @param[in] Segment 16-bit wide segment number.
> +
> + @param[in] Bus 8-bit wide bus number.
> +
> + @param[in] Device 5-bit wide device number.
> +
> + @param[in] Function 3-bit wide function number.
> +
> + @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.
> + PciDevice can be passed to the PciCapLib APIs.
> +
> + @retval RETURN_SUCCESS PciDevice has been constructed and output.
> +
> + @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the
> + permitted number of bits.
> +
> + @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +PciCapPciSegmentDeviceInit (
> + IN PCI_CAP_DOMAIN MaxDomain,
> + IN UINT16 Segment,
> + IN UINT8 Bus,
> + IN UINT8 Device,
> + IN UINT8 Function,
> + OUT PCI_CAP_DEV **PciDevice
> + );
> +
> +
> +/**
> + Free the resources used by PciDevice.
> +
> + @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by
> + PciCapPciSegmentDeviceInit().
> +**/
> +VOID
> +EFIAPI
> +PciCapPciSegmentDeviceUninit (
> + IN PCI_CAP_DEV *PciDevice
> + );
> +
> +#endif // __PCI_CAP_PCI_SEGMENT_LIB_H__
> diff --git a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h
> new file mode 100644
> index 000000000000..3ce15fe0fb57
> --- /dev/null
> +++ b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h
> @@ -0,0 +1,47 @@
> +/** @file
> + Plug a PciSegmentLib backend into PciCapLib, for config space access --
> + internal macro and type definitions.
> +
> + Copyright (C) 2018, Red Hat, Inc.
> +
> + This program and the accompanying materials are licensed and made available
> + under the terms and conditions of the BSD License which accompanies this
> + distribution. The full text of the license may be found at
> + http://opensource.org/licenses/bsd-license.php
> +
> + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
> + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +**/
> +
> +#ifndef __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__
> +#define __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__
> +
> +#include <Library/DebugLib.h>
> +
> +#include <Library/PciCapPciSegmentLib.h>
> +
> +#define SEGMENT_DEV_SIG SIGNATURE_64 ('P', 'C', 'P', 'S', 'G', 'M', 'N', 'T')
> +
> +typedef struct {
> + //
> + // Signature identifying the derived class.
> + //
> + UINT64 Signature;
> + //
> + // Members added by the derived class, specific to the use of PciSegmentLib.
> + //
> + PCI_CAP_DOMAIN MaxDomain;
> + UINT16 SegmentNr;
> + UINT8 BusNr;
> + UINT8 DeviceNr;
> + UINT8 FunctionNr;
> + //
> + // Base class.
> + //
> + PCI_CAP_DEV BaseDevice;
> +} SEGMENT_DEV;
> +
> +#define SEGMENT_DEV_FROM_PCI_CAP_DEV(PciDevice) \
> + CR (PciDevice, SEGMENT_DEV, BaseDevice, SEGMENT_DEV_SIG)
> +
> +#endif // __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__
> diff --git a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c
> new file mode 100644
> index 000000000000..57eb6b625b56
> --- /dev/null
> +++ b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c
> @@ -0,0 +1,226 @@
> +/** @file
> + Plug a PciSegmentLib backend into PciCapLib, for config space access.
> +
> + Copyright (C) 2018, Red Hat, Inc.
> +
> + This program and the accompanying materials are licensed and made available
> + under the terms and conditions of the BSD License which accompanies this
> + distribution. The full text of the license may be found at
> + http://opensource.org/licenses/bsd-license.php
> +
> + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
> + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +**/
> +
> +#include <IndustryStandard/Pci23.h>
> +
> +#include <Library/BaseLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PciSegmentLib.h>
> +
> +#include "BasePciCapPciSegmentLib.h"
> +
> +
> +/**
> + Read the config space of a given PCI device (both normal and extended).
> +
> + SegmentDevReadConfig() performs as few config space accesses as possible
> + (without attempting 64-bit wide accesses).
> +
> + @param[in] PciDevice Implementation-specific unique representation
> + of the PCI device in the PCI hierarchy.
> +
> + @param[in] SourceOffset Source offset in the config space of the PCI
> + device to start reading from.
> +
> + @param[out] DestinationBuffer Buffer to store the read data to.
> +
> + @param[in] Size The number of bytes to transfer.
> +
> + @retval RETURN_SUCCESS Size bytes have been transferred from config
> + space to DestinationBuffer.
> +
> + @retval RETURN_UNSUPPORTED Accessing Size bytes from SourceOffset exceeds
> + the config space limit of the PCI device.
> + Although PCI_CAP_DEV_READ_CONFIG allows reading
> + fewer than Size bytes in this case,
> + SegmentDevReadConfig() will read none.
> +**/
> +STATIC
> +RETURN_STATUS
> +EFIAPI
> +SegmentDevReadConfig (
> + IN PCI_CAP_DEV *PciDevice,
> + IN UINT16 SourceOffset,
> + OUT VOID *DestinationBuffer,
> + IN UINT16 Size
> + )
> +{
> + SEGMENT_DEV *SegmentDev;
> + UINT16 ConfigSpaceSize;
> + UINT64 SourceAddress;
> +
> + SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
> + ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
> + PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
> + //
> + // Note that all UINT16 variables below are promoted to INT32, and the
> + // addition and the comparison is carried out in INT32.
> + //
> + if (SourceOffset + Size > ConfigSpaceSize) {
> + return RETURN_UNSUPPORTED;
> + }
> + SourceAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,
> + SegmentDev->BusNr, SegmentDev->DeviceNr,
> + SegmentDev->FunctionNr, SourceOffset);
> + PciSegmentReadBuffer (SourceAddress, Size, DestinationBuffer);
> + return RETURN_SUCCESS;
> +}
> +
> +
> +/**
> + Write the config space of a given PCI device (both normal and extended).
> +
> + SegmentDevWriteConfig() performs as few config space accesses as possible
> + (without attempting 64-bit wide accesses).
> +
> + @param[in] PciDevice Implementation-specific unique representation
> + of the PCI device in the PCI hierarchy.
> +
> + @param[in] DestinationOffset Destination offset in the config space of the
> + PCI device to start writing at.
> +
> + @param[in] SourceBuffer Buffer to read the data to be stored from.
> +
> + @param[in] Size The number of bytes to transfer.
> +
> + @retval RETURN_SUCCESS Size bytes have been transferred from
> + SourceBuffer to config space.
> +
> + @retval RETURN_UNSUPPORTED Accessing Size bytes at DestinationOffset exceeds
> + the config space limit of the PCI device.
> + Although PCI_CAP_DEV_WRITE_CONFIG allows writing
> + fewer than Size bytes in this case,
> + SegmentDevWriteConfig() will write none.
> +**/
> +STATIC
> +RETURN_STATUS
> +EFIAPI
> +SegmentDevWriteConfig (
> + IN PCI_CAP_DEV *PciDevice,
> + IN UINT16 DestinationOffset,
> + IN VOID *SourceBuffer,
> + IN UINT16 Size
> + )
> +{
> + SEGMENT_DEV *SegmentDev;
> + UINT16 ConfigSpaceSize;
> + UINT64 DestinationAddress;
> +
> + SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
> + ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
> + PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
> + //
> + // Note that all UINT16 variables below are promoted to INT32, and the
> + // addition and the comparison is carried out in INT32.
> + //
> + if (DestinationOffset + Size > ConfigSpaceSize) {
> + return RETURN_UNSUPPORTED;
> + }
> + DestinationAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,
> + SegmentDev->BusNr, SegmentDev->DeviceNr,
> + SegmentDev->FunctionNr, DestinationOffset);
> + PciSegmentWriteBuffer (DestinationAddress, Size, SourceBuffer);
> + return RETURN_SUCCESS;
> +}
> +
> +
> +/**
> + Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function
> + quadruplet. The config space accessors are based upon PciSegmentLib.
> +
> + @param[in] MaxDomain If MaxDomain is PciCapExtended, then
> + PciDevice->ReadConfig() and PciDevice->WriteConfig()
> + will delegate extended config space accesses too to
> + PciSegmentReadBuffer() and PciSegmentWriteBuffer(),
> + respectively. Otherwise, PciDevice->ReadConfig() and
> + PciDevice->WriteConfig() will reject accesses to
> + extended config space with RETURN_UNSUPPORTED, without
> + calling PciSegmentReadBuffer() or
> + PciSegmentWriteBuffer(). By setting MaxDomain to
> + PciCapNormal, the platform can prevent undefined
> + PciSegmentLib behavior when the PCI root bridge under
> + the PCI device at Segment:Bus:Device.Function doesn't
> + support extended config space.
> +
> + @param[in] Segment 16-bit wide segment number.
> +
> + @param[in] Bus 8-bit wide bus number.
> +
> + @param[in] Device 5-bit wide device number.
> +
> + @param[in] Function 3-bit wide function number.
> +
> + @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.
> + PciDevice can be passed to the PciCapLib APIs.
> +
> + @retval RETURN_SUCCESS PciDevice has been constructed and output.
> +
> + @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the
> + permitted number of bits.
> +
> + @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
> +**/
> +RETURN_STATUS
> +EFIAPI
> +PciCapPciSegmentDeviceInit (
> + IN PCI_CAP_DOMAIN MaxDomain,
> + IN UINT16 Segment,
> + IN UINT8 Bus,
> + IN UINT8 Device,
> + IN UINT8 Function,
> + OUT PCI_CAP_DEV **PciDevice
> + )
> +{
> + SEGMENT_DEV *SegmentDev;
> +
> + if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) {
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + SegmentDev = AllocatePool (sizeof *SegmentDev);
> + if (SegmentDev == NULL) {
> + return RETURN_OUT_OF_RESOURCES;
> + }
> +
> + SegmentDev->Signature = SEGMENT_DEV_SIG;
> + SegmentDev->MaxDomain = MaxDomain;
> + SegmentDev->SegmentNr = Segment;
> + SegmentDev->BusNr = Bus;
> + SegmentDev->DeviceNr = Device;
> + SegmentDev->FunctionNr = Function;
> + SegmentDev->BaseDevice.ReadConfig = SegmentDevReadConfig;
> + SegmentDev->BaseDevice.WriteConfig = SegmentDevWriteConfig;
> +
> + *PciDevice = &SegmentDev->BaseDevice;
> + return RETURN_SUCCESS;
> +}
> +
> +
> +/**
> + Free the resources used by PciDevice.
> +
> + @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by
> + PciCapPciSegmentDeviceInit().
> +**/
> +VOID
> +EFIAPI
> +PciCapPciSegmentDeviceUninit (
> + IN PCI_CAP_DEV *PciDevice
> + )
> +{
> + SEGMENT_DEV *SegmentDev;
> +
> + SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
> + FreePool (SegmentDev);
> +}
> --
> 2.14.1.3.gb7cf6e02401b
>
>
next prev parent reply other threads:[~2018-05-24 8:08 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-05-23 20:21 [PATCH v2 0/7] OvmfPkg, ArmVirtPkg: add and use PCI(E) Capabilities Library Laszlo Ersek
2018-05-23 20:21 ` [PATCH v2 1/7] OvmfPkg: introduce PciCapLib Laszlo Ersek
2018-05-24 7:53 ` Ard Biesheuvel
2018-05-24 14:39 ` Laszlo Ersek
2018-05-24 14:41 ` Ard Biesheuvel
2018-05-24 17:25 ` Laszlo Ersek
2018-05-23 20:21 ` [PATCH v2 2/7] OvmfPkg: introduce PciCapPciSegmentLib Laszlo Ersek
2018-05-24 8:08 ` Ard Biesheuvel [this message]
2018-05-24 14:43 ` Laszlo Ersek
2018-05-24 14:55 ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 3/7] OvmfPkg: introduce PciCapPciIoLib Laszlo Ersek
2018-05-24 8:13 ` Ard Biesheuvel
2018-05-24 14:50 ` Laszlo Ersek
2018-05-24 14:54 ` Ard Biesheuvel
2018-05-24 14:54 ` Ard Biesheuvel
2018-05-24 17:22 ` Laszlo Ersek
2018-05-23 20:21 ` [PATCH v2 4/7] OvmfPkg: resolve PciCapLib, PciCapPciSegmentLib, PciCapPciIoLib Laszlo Ersek
2018-05-24 8:14 ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 5/7] ArmVirtPkg: " Laszlo Ersek
2018-05-24 8:14 ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 6/7] OvmfPkg/PciHotPlugInitDxe: convert to PciCapLib Laszlo Ersek
2018-05-24 8:15 ` Ard Biesheuvel
2018-05-23 20:21 ` [PATCH v2 7/7] OvmfPkg/Virtio10Dxe: " Laszlo Ersek
2018-05-24 8:16 ` Ard Biesheuvel
2018-05-24 14:55 ` Laszlo Ersek
2018-05-24 20:04 ` [PATCH v2 0/7] OvmfPkg, ArmVirtPkg: add and use PCI(E) Capabilities Library 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='CAKv+Gu8QKo0da=sUWBB5LeMMFZ-TfSF0JiayTUPs+iybhNrPUg@mail.gmail.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