From: "Ni, Ray" <ray.ni@intel.com>
To: "Wu, Hao A" <hao.a.wu@intel.com>,
"edk2-devel@lists.01.org" <edk2-devel@lists.01.org>
Cc: "Wang, Jian J" <jian.j.wang@intel.com>,
"Dong, Eric" <eric.dong@intel.com>
Subject: Re: [PATCH v2 06/12] MdeModulePkg/NvmExpressPei: Add logic to produce SSC PPI
Date: Thu, 31 Jan 2019 03:35:03 +0000 [thread overview]
Message-ID: <734D49CCEBEEF84792F5B80ED585239D5BFFFFE7@SHSMSX104.ccr.corp.intel.com> (raw)
In-Reply-To: <20190131024854.4880-7-hao.a.wu@intel.com>
> -----Original Message-----
> From: Wu, Hao A <hao.a.wu@intel.com>
> Sent: Thursday, January 31, 2019 10:49 AM
> To: edk2-devel@lists.01.org
> Cc: Wu, Hao A <hao.a.wu@intel.com>; Wang, Jian J <jian.j.wang@intel.com>;
> Ni, Ray <ray.ni@intel.com>; Dong, Eric <eric.dong@intel.com>
> Subject: [PATCH v2 06/12] MdeModulePkg/NvmExpressPei: Add logic to
> produce SSC PPI
>
> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1409
>
> For the NvmExpressPei driver, this commit will add codes to produce the
> Storage Security Command PPI if the underlying NVM Express controller
> supports the Security Send and Security Receive commands.
>
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Hao Wu <hao.a.wu@intel.com>
> ---
> MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf | 10 +-
> MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h | 81
> +++-
> MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h | 20 +-
> MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.h
> | 247 ++++++++++++
> MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c | 317
> +++++++++++++++
> MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c | 143
> +++++--
> MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c | 32 +-
> MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.c
> | 423 ++++++++++++++++++++
> 8 files changed, 1184 insertions(+), 89 deletions(-)
>
> diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
> index 9591572fec..0666e5892b 100644
> --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
> +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
> @@ -2,7 +2,7 @@
> # The NvmExpressPei driver is used to manage non-volatile memory
> subsystem
> # which follows NVM Express specification at PEI phase.
> #
> -# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
> #
> # This program and the accompanying materials
> # are licensed and made available under the terms and conditions of the
> BSD License
> @@ -30,6 +30,7 @@
> #
>
> [Sources]
> + DevicePath.c
> DmaMem.c
> NvmExpressPei.c
> NvmExpressPei.h
> @@ -39,6 +40,8 @@
> NvmExpressPeiHci.h
> NvmExpressPeiPassThru.c
> NvmExpressPeiPassThru.h
> + NvmExpressPeiStorageSecurity.c
> + NvmExpressPeiStorageSecurity.h
>
> [Packages]
> MdePkg/MdePkg.dec
> @@ -54,11 +57,12 @@
> PeimEntryPoint
>
> [Ppis]
> - gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES
> - gEfiPeiVirtualBlockIo2PpiGuid ## PRODUCES
> gEdkiiPeiNvmExpressHostControllerPpiGuid ## CONSUMES
> gEdkiiIoMmuPpiGuid ## CONSUMES
> gEfiEndOfPeiSignalPpiGuid ## CONSUMES
> + gEfiPeiVirtualBlockIoPpiGuid ## SOMETIMES_PRODUCES
> + gEfiPeiVirtualBlockIo2PpiGuid ## SOMETIMES_PRODUCES
> + gEdkiiPeiStorageSecurityCommandPpiGuid ##
> SOMETIMES_PRODUCES
>
> [Depex]
> gEfiPeiMemoryDiscoveredPpiGuid AND
> diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h
> index 0135eca6f0..7047c4f3ff 100644
> --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h
> +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.h
> @@ -25,6 +25,7 @@
> #include <Ppi/NvmExpressHostController.h>
> #include <Ppi/BlockIo.h>
> #include <Ppi/BlockIo2.h>
> +#include <Ppi/StorageSecurityCommand.h>
> #include <Ppi/IoMmu.h>
> #include <Ppi/EndOfPeiPhase.h>
>
> @@ -44,6 +45,7 @@ typedef struct
> _PEI_NVME_CONTROLLER_PRIVATE_DATA
> PEI_NVME_CONTROLLER_PRIVATE_DA
> #include "NvmExpressPeiHci.h"
> #include "NvmExpressPeiPassThru.h"
> #include "NvmExpressPeiBlockIo.h"
> +#include "NvmExpressPeiStorageSecurity.h"
>
> //
> // NVME PEI driver implementation related definitions
> @@ -90,10 +92,15 @@ struct _PEI_NVME_NAMESPACE_INFO {
> struct _PEI_NVME_CONTROLLER_PRIVATE_DATA {
> UINT32 Signature;
> UINTN MmioBase;
> + UINTN DevicePathLength;
> + EFI_DEVICE_PATH_PROTOCOL *DevicePath;
> +
> EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi;
> EFI_PEI_RECOVERY_BLOCK_IO2_PPI BlkIo2Ppi;
> + EDKII_PEI_STORAGE_SECURITY_CMD_PPI StorageSecurityPpi;
> EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList;
> EFI_PEI_PPI_DESCRIPTOR BlkIo2PpiList;
> + EFI_PEI_PPI_DESCRIPTOR StorageSecurityPpiList;
> EFI_PEI_NOTIFY_DESCRIPTOR EndOfPeiNotifyList;
>
> //
> @@ -139,11 +146,13 @@ struct _PEI_NVME_CONTROLLER_PRIVATE_DATA {
> PEI_NVME_NAMESPACE_INFO *NamespaceInfo;
> };
>
> -#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a) \
> +#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a)
> \
> CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIoPpi,
> NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
> -#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2(a) \
> +#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2(a)
> \
> CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, BlkIo2Ppi,
> NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
> -#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a) \
> +#define
> GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY(a)
> \
> + CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, StorageSecurityPpi,
> NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
> +#define GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a)
> \
> CR (a, PEI_NVME_CONTROLLER_PRIVATE_DATA, EndOfPeiNotifyList,
> NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)
>
>
> @@ -257,4 +266,70 @@ NvmePeimEndOfPei (
> IN VOID *Ppi
> );
>
> +/**
> + Creates a copy of the current device path instance and returns a pointer to
> the
> + next device path instance.
> +
> + @param DevicePath On input, this holds the pointer to the current
> + device path instance. On output, this holds
> + the pointer to the next device path instance
> + or NULL if there are no more device path
> + instances in the device path pointer to a
> + device path data structure.
> + @param Size On output, this holds the size of the device
> + path instance, in bytes or zero, if DevicePath
> + is NULL.
> +
> + @return A pointer to the current device path instance.
> +
> +**/
> +EFI_DEVICE_PATH_PROTOCOL *
> +GetNextDevicePathInstance (
> + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
> + OUT UINTN *Size
> + );
> +
> +/**
> + Check the validity of the device path of a NVM Express host controller.
> +
> + @param[in] DevicePath A pointer to the
> EFI_DEVICE_PATH_PROTOCOL
> + structure.
> + @param[in] DevicePathLength The length of the device path.
> +
> + @retval EFI_SUCCESS The device path is valid.
> + @retval EFI_INVALID_PARAMETER The device path is invalid.
> +
> +**/
> +EFI_STATUS
> +NvmeCheckHcDevicePath (
> + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
> + IN UINTN DevicePathLength
> + );
> +
> +/**
> + Build the device path for an Nvm Express device with given namespace
> identifier
> + and namespace extended unique identifier.
> +
> + @param[in] Private A pointer to the
> PEI_NVME_CONTROLLER_PRIVATE_DATA
> + data structure.
> + @param[in] NamespaceId The given namespace identifier.
> + @param[in] NamespaceUuid The given namespace extended unique
> identifier.
> + @param[out] DevicePathLength The length of the device path in bytes
> specified
> + by DevicePath.
> + @param[out] DevicePath The device path of Nvm Express device.
> +
> + @retval EFI_SUCCESS The operation succeeds.
> + @retval EFI_INVALID_PARAMETER The parameters are invalid.
> + @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of
> resources.
> +
> +**/
> +EFI_STATUS
> +NvmeBuildDevicePath (
> + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
> + IN UINT32 NamespaceId,
> + IN UINT64 NamespaceUuid,
> + OUT UINTN *DevicePathLength,
> + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
> + );
> +
> #endif
> diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h
> index ff334e3e17..ad1d5d0d8a 100644
> --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h
> +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.h
> @@ -2,7 +2,7 @@
> The NvmExpressPei driver is used to manage non-volatile memory
> subsystem
> which follows NVM Express specification at PEI phase.
>
> - Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> + Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
>
> This program and the accompanying materials
> are licensed and made available under the terms and conditions
> @@ -107,20 +107,6 @@ NvmeBaseMemPageOffset (
> );
>
> /**
> - Disable the Nvm Express controller.
> -
> - @param[in] Private The pointer to the
> PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
> -
> - @return EFI_SUCCESS Successfully disable the controller.
> - @return others Fail to disable the controller.
> -
> -**/
> -EFI_STATUS
> -NvmeDisableController (
> - IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
> - );
> -
> -/**
> Initialize the Nvm Express controller.
>
> @param[in] Private The pointer to the
> PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
> @@ -153,13 +139,13 @@ NvmeIdentifyNamespace (
> );
>
> /**
> - Free the resources allocated by an NVME controller.
> + Free the DMA resources allocated by an NVME controller.
>
> @param[in] Private The pointer to the
> PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
>
> **/
> VOID
> -NvmeFreeControllerResource (
> +NvmeFreeDmaResource (
> IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
> );
>
> diff --git
> a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.
> h
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.
> h
> new file mode 100644
> index 0000000000..8ccfb425e7
> --- /dev/null
> +++
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.
> h
> @@ -0,0 +1,247 @@
> +/** @file
> + The NvmExpressPei driver is used to manage non-volatile memory
> subsystem
> + which follows NVM Express specification at PEI phase.
> +
> + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
> +
> + 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 _NVM_EXPRESS_PEI_STORAGE_SECURITY_H_
> +#define _NVM_EXPRESS_PEI_STORAGE_SECURITY_H_
> +
> +/**
> + Gets the count of storage security devices that one specific driver detects.
> +
> + @param[in] This The PPI instance pointer.
> + @param[out] NumberofDevices The number of storage security devices
> discovered.
> +
> + @retval EFI_SUCCESS The operation performed successfully.
> + @retval EFI_INVALID_PARAMETER The parameters are invalid.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecurityGetDeviceNo (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + OUT UINTN *NumberofDevices
> + );
> +
> +/**
> + Gets the device path of a specific storage security device.
> +
> + @param[in] This The PPI instance pointer.
> + @param[in] DeviceIndex Specifies the storage security device to
> which
> + the function wants to talk. Because the driver
> + that implements Storage Security Command PPIs
> + will manage multiple storage devices, the PPIs
> + that want to talk to a single device must specify
> + the device index that was assigned during the
> + enumeration process. This index is a number from
> + one to NumberofDevices.
> + @param[out] DevicePathLength The length of the device path in bytes
> specified
> + by DevicePath.
> + @param[out] DevicePath The device path of storage security device.
> + This field re-uses EFI Device Path Protocol as
> + defined by Section 10.2 EFI Device Path Protocol
> + of UEFI 2.7 Specification.
> +
> + @retval EFI_SUCCESS The operation succeeds.
> + @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is
> NULL.
> + @retval EFI_NOT_FOUND The specified storage security device not
> found.
> + @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of
> resources.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecurityGetDevicePath (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + IN UINTN DeviceIndex,
> + OUT UINTN *DevicePathLength,
> + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
> + );
> +
> +/**
> + Send a security protocol command to a device that receives data and/or
> the result
> + of one or more commands sent by SendData.
> +
> + The ReceiveData function sends a security protocol command to the given
> DeviceIndex.
> + The security protocol command sent is defined by SecurityProtocolId and
> contains
> + the security protocol specific data SecurityProtocolSpecificData. The
> function
> + returns the data from the security protocol command in PayloadBuffer.
> +
> + For devices supporting the SCSI command set, the security protocol
> command is sent
> + using the SECURITY PROTOCOL IN command defined in SPC-4.
> +
> + For devices supporting the ATA command set, the security protocol
> command is sent
> + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if
> PayloadBufferSize
> + is non-zero.
> +
> + If the PayloadBufferSize is zero, the security protocol command is sent
> using the
> + Trusted Non-Data command defined in ATA8-ACS.
> +
> + If PayloadBufferSize is too small to store the available data from the
> security
> + protocol command, the function shall copy PayloadBufferSize bytes into
> the
> + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
> +
> + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is
> non-zero,
> + the function shall return EFI_INVALID_PARAMETER.
> +
> + If the given DeviceIndex does not support security protocol commands,
> the function
> + shall return EFI_UNSUPPORTED.
> +
> + If the security protocol fails to complete within the Timeout period, the
> function
> + shall return EFI_TIMEOUT.
> +
> + If the security protocol command completes without an error, the function
> shall
> + return EFI_SUCCESS. If the security protocol command completes with an
> error, the
> + function shall return EFI_DEVICE_ERROR.
> +
> + @param[in] This The PPI instance pointer.
> + @param[in] DeviceIndex Specifies the storage security device to which
> the
> + function wants to talk. Because the driver that
> + implements Storage Security Command PPIs will manage
> + multiple storage devices, the PPIs that want to talk
> + to a single device must specify the device index
> + that was assigned during the enumeration process.
> + This index is a number from one to NumberofDevices.
> + @param[in] Timeout The timeout, in 100ns units, to use for the
> execution
> + of the security protocol command. A Timeout value
> + of 0 means that this function will wait indefinitely
> + for the security protocol command to execute. If
> + Timeout is greater than zero, then this function
> + will return EFI_TIMEOUT if the time required to
> + execute the receive data command is greater than
> + Timeout.
> + @param[in] SecurityProtocolId
> + The value of the "Security Protocol" parameter of
> + the security protocol command to be sent.
> + @param[in] SecurityProtocolSpecificData
> + The value of the "Security Protocol Specific"
> + parameter of the security protocol command to be
> + sent.
> + @param[in] PayloadBufferSize
> + Size in bytes of the payload data buffer.
> + @param[out] PayloadBuffer A pointer to a destination buffer to store
> the
> + security protocol command specific payload data
> + for the security protocol command. The caller is
> + responsible for having either implicit or explicit
> + ownership of the buffer.
> + @param[out] PayloadTransferSize
> + A pointer to a buffer to store the size in bytes
> + of the data written to the payload data buffer.
> +
> + @retval EFI_SUCCESS The security protocol command completed
> + successfully.
> + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too
> small to
> + store the available data from the device.
> + The PayloadBuffer contains the truncated
> + data.
> + @retval EFI_UNSUPPORTED The given DeviceIndex does not
> support
> + security protocol commands.
> + @retval EFI_DEVICE_ERROR The security protocol command
> completed
> + with an error.
> + @retval EFI_INVALID_PARAMETER The PayloadBuffer or
> PayloadTransferSize
> + is NULL and PayloadBufferSize is non-zero.
> + @retval EFI_TIMEOUT A timeout occurred while waiting for the
> + security protocol command to execute.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecurityReceiveData (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + IN UINTN DeviceIndex,
> + IN UINT64 Timeout,
> + IN UINT8 SecurityProtocolId,
> + IN UINT16 SecurityProtocolSpecificData,
> + IN UINTN PayloadBufferSize,
> + OUT VOID *PayloadBuffer,
> + OUT UINTN *PayloadTransferSize
> + );
> +
> +/**
> + Send a security protocol command to a device.
> +
> + The SendData function sends a security protocol command containing the
> payload
> + PayloadBuffer to the given DeviceIndex. The security protocol command
> sent is
> + defined by SecurityProtocolId and contains the security protocol specific
> data
> + SecurityProtocolSpecificData. If the underlying protocol command requires
> a
> + specific padding for the command payload, the SendData function shall
> add padding
> + bytes to the command payload to satisfy the padding requirements.
> +
> + For devices supporting the SCSI command set, the security protocol
> command is
> + sent using the SECURITY PROTOCOL OUT command defined in SPC-4.
> +
> + For devices supporting the ATA command set, the security protocol
> command is
> + sent using one of the TRUSTED SEND commands defined in ATA8-ACS if
> PayloadBufferSize
> + is non-zero. If the PayloadBufferSize is zero, the security protocol
> command
> + is sent using the Trusted Non-Data command defined in ATA8-ACS.
> +
> + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function
> shall
> + return EFI_INVALID_PARAMETER.
> +
> + If the given DeviceIndex does not support security protocol commands,
> the function
> + shall return EFI_UNSUPPORTED.
> +
> + If the security protocol fails to complete within the Timeout period, the
> function
> + shall return EFI_TIMEOUT.
> +
> + If the security protocol command completes without an error, the function
> shall
> + return EFI_SUCCESS. If the security protocol command completes with an
> error,
> + the functio shall return EFI_DEVICE_ERROR.
> +
> + @param[in] This The PPI instance pointer.
> + @param[in] DeviceIndex The ID of the device.
> + @param[in] Timeout The timeout, in 100ns units, to use for the
> execution
> + of the security protocol command. A Timeout value
> + of 0 means that this function will wait indefinitely
> + for the security protocol command to execute. If
> + Timeout is greater than zero, then this function
> + will return EFI_TIMEOUT if the time required to
> + execute the receive data command is greater than
> + Timeout.
> + @param[in] SecurityProtocolId
> + The value of the "Security Protocol" parameter of
> + the security protocol command to be sent.
> + @param[in] SecurityProtocolSpecificData
> + The value of the "Security Protocol Specific"
> + parameter of the security protocol command to be
> + sent.
> + @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
> + @param[in] PayloadBuffer A pointer to a destination buffer to store the
> + security protocol command specific payload data
> + for the security protocol command.
> +
> + @retval EFI_SUCCESS The security protocol command completed
> successfully.
> + @retval EFI_UNSUPPORTED The given DeviceIndex does not support
> security
> + protocol commands.
> + @retval EFI_DEVICE_ERROR The security protocol command
> completed with
> + an error.
> + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and
> PayloadBufferSize
> + is non-zero.
> + @retval EFI_TIMEOUT A timeout occurred while waiting for the
> security
> + protocol command to execute.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecuritySendData (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + IN UINTN DeviceIndex,
> + IN UINT64 Timeout,
> + IN UINT8 SecurityProtocolId,
> + IN UINT16 SecurityProtocolSpecificData,
> + IN UINTN PayloadBufferSize,
> + IN VOID *PayloadBuffer
> + );
> +
> +#endif
> diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c
> new file mode 100644
> index 0000000000..56004400bc
> --- /dev/null
> +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/DevicePath.c
> @@ -0,0 +1,317 @@
> +/** @file
> + The device path help function.
> +
> + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
> +
> + 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 "NvmExpressPei.h"
> +
> +//
> +// Template for an Nvm Express Device Path node
> +//
> +NVME_NAMESPACE_DEVICE_PATH mNvmeDevicePathNodeTemplate = {
> + { // Header
> + MESSAGING_DEVICE_PATH,
> + MSG_NVME_NAMESPACE_DP,
> + {
> + (UINT8) (sizeof (NVME_NAMESPACE_DEVICE_PATH)),
> + (UINT8) ((sizeof (NVME_NAMESPACE_DEVICE_PATH)) >> 8)
> + }
> + },
> + 0x0, // NamespaceId
> + 0x0 // NamespaceUuid
> +};
> +
> +//
> +// Template for an End of entire Device Path node
> +//
> +EFI_DEVICE_PATH_PROTOCOL mNvmeEndDevicePathNodeTemplate = {
> + END_DEVICE_PATH_TYPE,
> + END_ENTIRE_DEVICE_PATH_SUBTYPE,
> + {
> + (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
> + (UINT8) ((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8)
> + }
> +};
> +
> +/**
> + Returns the 16-bit Length field of a device path node.
> +
> + Returns the 16-bit Length field of the device path node specified by Node.
> + Node is not required to be aligned on a 16-bit boundary, so it is
> recommended
> + that a function such as ReadUnaligned16() be used to extract the contents
> of
> + the Length field.
> +
> + If Node is NULL, then ASSERT().
> +
> + @param Node A pointer to a device path node data structure.
> +
> + @return The 16-bit Length field of the device path node specified by Node.
> +
> +**/
> +UINTN
> +DevicePathNodeLength (
> + IN CONST VOID *Node
> + )
> +{
> + ASSERT (Node != NULL);
> + return ReadUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL
> *)(Node))->Length[0]);
> +}
> +
> +/**
> + Returns a pointer to the next node in a device path.
> +
> + If Node is NULL, then ASSERT().
> +
> + @param Node A pointer to a device path node data structure.
> +
> + @return a pointer to the device path node that follows the device path
> node
> + specified by Node.
> +
> +**/
> +EFI_DEVICE_PATH_PROTOCOL *
> +NextDevicePathNode (
> + IN CONST VOID *Node
> + )
> +{
> + ASSERT (Node != NULL);
> + return (EFI_DEVICE_PATH_PROTOCOL *)((UINT8 *)(Node) +
> DevicePathNodeLength(Node));
> +}
> +
> +/**
> + Returns the size of a device path in bytes.
> +
> + This function returns the size, in bytes, of the device path data structure
> + specified by DevicePath including the end of device path node.
> + If DevicePath is NULL or invalid, then 0 is returned.
> +
> + @param DevicePath A pointer to a device path data structure.
> +
> + @retval 0 If DevicePath is NULL or invalid.
> + @retval Others The size of a device path in bytes.
> +
> +**/
> +UINTN
> +GetDevicePathSize (
> + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
> + )
> +{
> + CONST EFI_DEVICE_PATH_PROTOCOL *Start;
> +
> + if (DevicePath == NULL) {
> + return 0;
> + }
> +
> + //
> + // Search for the end of the device path structure
> + //
> + Start = DevicePath;
> + while (!(DevicePath->Type == END_DEVICE_PATH_TYPE &&
> + DevicePath->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) {
> + DevicePath = NextDevicePathNode (DevicePath);
> + }
> +
> + //
> + // Compute the size and add back in the size of the end device path
> structure
> + //
> + return ((UINTN) DevicePath - (UINTN) Start) + DevicePathNodeLength
> (DevicePath);
> +}
> +
> +/**
> + Creates a copy of the current device path instance and returns a pointer to
> the
> + next device path instance.
> +
> + @param DevicePath On input, this holds the pointer to the current
> + device path instance. On output, this holds
> + the pointer to the next device path instance
> + or NULL if there are no more device path
> + instances in the device path pointer to a
> + device path data structure.
> + @param Size On output, this holds the size of the device
> + path instance, in bytes or zero, if DevicePath
> + is NULL.
> +
> + @return A pointer to the current device path instance.
> +
> +**/
> +EFI_DEVICE_PATH_PROTOCOL *
> +GetNextDevicePathInstance (
> + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
> + OUT UINTN *Size
> + )
> +{
> + EFI_DEVICE_PATH_PROTOCOL *DevPath;
> + EFI_DEVICE_PATH_PROTOCOL *ReturnValue;
> + UINTN DevicePathSize;
> + UINT8 Temp;
> +
> + ASSERT (Size != NULL);
> +
> + if (DevicePath == NULL || *DevicePath == NULL) {
> + *Size = 0;
> + return NULL;
> + }
> +
> + //
> + // Find the end of the device path instance
> + //
> + DevPath = *DevicePath;
> + while (DevPath->Type != END_DEVICE_PATH_TYPE) {
> + DevPath = NextDevicePathNode (DevPath);
> + }
> +
> + //
> + // Compute the size of the device path instance
> + //
> + *Size = ((UINTN) DevPath - (UINTN) (*DevicePath)) + sizeof
> (EFI_DEVICE_PATH_PROTOCOL);
> +
> + //
> + // Make a copy and return the device path instance
> + //
> + Temp = DevPath->SubType;
> + DevPath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
> + DevicePathSize = GetDevicePathSize (*DevicePath);
> + ReturnValue = (DevicePathSize == 0) ? NULL : AllocateCopyPool
> (DevicePathSize, *DevicePath);
> + DevPath->SubType = Temp;
> +
> + //
> + // If DevPath is the end of an entire device path, then another instance
> + // does not follow, so *DevicePath is set to NULL.
> + //
> + if (DevPath->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
> + *DevicePath = NULL;
> + } else {
> + *DevicePath = NextDevicePathNode (DevPath);
> + }
> +
> + return ReturnValue;
> +}
1. Why duplicate the UefiDevicePathLib implementation here?
> +
> +/**
> + Check the validity of the device path of a NVM Express host controller.
> +
> + @param[in] DevicePath A pointer to the
> EFI_DEVICE_PATH_PROTOCOL
> + structure.
> + @param[in] DevicePathLength The length of the device path.
> +
> + @retval EFI_SUCCESS The device path is valid.
> + @retval EFI_INVALID_PARAMETER The device path is invalid.
> +
> +**/
> +EFI_STATUS
> +NvmeCheckHcDevicePath (
> + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
> + IN UINTN DevicePathLength
> + )
> +{
> + EFI_DEVICE_PATH_PROTOCOL *Start;
> + UINTN Size;
> +
> + if (DevicePath == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Start = DevicePath;
> + while (!(DevicePath->Type == END_DEVICE_PATH_TYPE &&
> + DevicePath->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) {
> + DevicePath = NextDevicePathNode (DevicePath);
> + }
> +
> + //
> + // Check if the device path and its size match each other.
> + //
> + Size = ((UINTN) DevicePath - (UINTN) Start) + sizeof
> (EFI_DEVICE_PATH_PROTOCOL);
> + if (Size != DevicePathLength) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + return EFI_SUCCESS;
> +}
2. Can we make use of UefiDevicePathLib API (IsDevicePathValide + GetDevicePathSize)
to do the HC device path check?
> +
> +/**
> + Build the device path for an Nvm Express device with given namespace
> identifier
> + and namespace extended unique identifier.
> +
> + @param[in] Private A pointer to the
> PEI_NVME_CONTROLLER_PRIVATE_DATA
> + data structure.
> + @param[in] NamespaceId The given namespace identifier.
> + @param[in] NamespaceUuid The given namespace extended unique
> identifier.
> + @param[out] DevicePathLength The length of the device path in bytes
> specified
> + by DevicePath.
> + @param[out] DevicePath The device path of Nvm Express device.
> +
> + @retval EFI_SUCCESS The operation succeeds.
> + @retval EFI_INVALID_PARAMETER The parameters are invalid.
> + @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of
> resources.
> +
> +**/
> +EFI_STATUS
> +NvmeBuildDevicePath (
> + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
> + IN UINT32 NamespaceId,
> + IN UINT64 NamespaceUuid,
> + OUT UINTN *DevicePathLength,
> + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
> + )
> +{
> + EFI_DEVICE_PATH_PROTOCOL *DevicePathWalker;
> + NVME_NAMESPACE_DEVICE_PATH *NvmeDeviceNode;
> +
> + if (DevicePathLength == NULL || DevicePath == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + *DevicePathLength = Private->DevicePathLength + sizeof
> (NVME_NAMESPACE_DEVICE_PATH);
> + *DevicePath = AllocatePool (*DevicePathLength);
> + if (*DevicePath == NULL) {
> + *DevicePathLength = 0;
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Construct the host controller part device nodes
> + //
> + DevicePathWalker = *DevicePath;
> + CopyMem (
> + DevicePathWalker,
> + Private->DevicePath,
> + Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)
> + );
> +
> + //
> + // Construct the Nvm Express device node
> + //
> + DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8
> *)DevicePathWalker +
> + (Private->DevicePathLength - sizeof
> (EFI_DEVICE_PATH_PROTOCOL)));
> + CopyMem (
> + DevicePathWalker,
> + &mNvmeDevicePathNodeTemplate,
> + sizeof (mNvmeDevicePathNodeTemplate)
> + );
> + NvmeDeviceNode = (NVME_NAMESPACE_DEVICE_PATH
> *)DevicePathWalker;
> + NvmeDeviceNode->NamespaceId = NamespaceId;
> + NvmeDeviceNode->NamespaceUuid = NamespaceUuid;
> +
> + //
> + // Construct the end device node
> + //
> + DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8
> *)DevicePathWalker +
> + sizeof (NVME_NAMESPACE_DEVICE_PATH));
> + CopyMem (
> + DevicePathWalker,
> + &mNvmeEndDevicePathNodeTemplate,
> + sizeof (mNvmeEndDevicePathNodeTemplate)
> + );
> +
> + return EFI_SUCCESS;
> +}
> diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
> index 2fe73e942c..96622e6fd5 100644
> --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
> +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
> @@ -24,11 +24,17 @@ EFI_PEI_PPI_DESCRIPTOR
> mNvmeBlkIoPpiListTemplate = {
> };
>
> EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIo2PpiListTemplate = {
> - EFI_PEI_PPI_DESCRIPTOR_PPI |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
> + (EFI_PEI_PPI_DESCRIPTOR_PPI |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> &gEfiPeiVirtualBlockIo2PpiGuid,
> NULL
> };
>
> +EFI_PEI_PPI_DESCRIPTOR mNvmeStorageSecurityPpiListTemplate = {
> + (EFI_PEI_PPI_DESCRIPTOR_PPI |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> + &gEdkiiPeiStorageSecurityCommandPpiGuid,
> + NULL
> +};
> +
> EFI_PEI_NOTIFY_DESCRIPTOR mNvmeEndOfPeiNotifyListTemplate = {
> (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> &gEfiEndOfPeiSignalPpiGuid,
> @@ -185,8 +191,7 @@ NvmePeimEndOfPei (
> PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
>
> Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY
> (NotifyDescriptor);
> - NvmeDisableController (Private);
> - NvmeFreeControllerResource (Private);
> + NvmeFreeDmaResource (Private);
>
> return EFI_SUCCESS;
> }
> @@ -211,9 +216,13 @@ NvmExpressPeimEntry (
> EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi;
> UINT8 Controller;
> UINTN MmioBase;
> + UINTN DevicePathLength;
> + EFI_DEVICE_PATH_PROTOCOL *DevicePath;
> PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
> EFI_PHYSICAL_ADDRESS DeviceAddress;
>
> + DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));
> +
> //
> // Locate the NVME host controller PPI
> //
> @@ -243,16 +252,41 @@ NvmExpressPeimEntry (
> break;
> }
>
> + Status = NvmeHcPpi->GetNvmeHcDevicePath (
> + NvmeHcPpi,
> + Controller,
> + &DevicePathLength,
> + &DevicePath
> + );
> + if (EFI_ERROR (Status)) {
> + DEBUG ((
> + DEBUG_ERROR, "%a: Fail to allocate get the device path for
> Controller %d.\n",
> + __FUNCTION__, Controller
> + ));
> + return Status;
> + }
> +
> + //
> + // Check validity of the device path of the NVM Express controller.
> + //
> + Status = NvmeCheckHcDevicePath (DevicePath, DevicePathLength);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((
> + DEBUG_ERROR, "%a: The device path is invalid for Controller %d.\n",
> + __FUNCTION__, Controller
> + ));
> + Controller++;
> + continue;
> + }
> +
> //
> // Memory allocation for controller private data
> //
> Private = AllocateZeroPool (sizeof
> (PEI_NVME_CONTROLLER_PRIVATE_DATA));
> if (Private == NULL) {
> DEBUG ((
> - DEBUG_ERROR,
> - "%a: Fail to allocate private data for Controller %d.\n",
> - __FUNCTION__,
> - Controller
> + DEBUG_ERROR, "%a: Fail to allocate private data for Controller %d.\n",
> + __FUNCTION__, Controller
> ));
> return EFI_OUT_OF_RESOURCES;
> }
> @@ -268,12 +302,9 @@ NvmExpressPeimEntry (
> );
> if (EFI_ERROR (Status)) {
> DEBUG ((
> - DEBUG_ERROR,
> - "%a: Fail to allocate DMA buffers for Controller %d.\n",
> - __FUNCTION__,
> - Controller
> + DEBUG_ERROR, "%a: Fail to allocate DMA buffers for Controller %d.\n",
> + __FUNCTION__, Controller
> ));
> - NvmeFreeControllerResource (Private);
> return Status;
> }
> ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Private-
> >Buffer));
> @@ -282,20 +313,10 @@ NvmExpressPeimEntry (
> //
> // Initialize controller private data
> //
> - Private->Signature =
> NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
> - Private->MmioBase = MmioBase;
> - Private->BlkIoPpi.GetNumberOfBlockDevices =
> NvmeBlockIoPeimGetDeviceNo;
> - Private->BlkIoPpi.GetBlockDeviceMediaInfo =
> NvmeBlockIoPeimGetMediaInfo;
> - Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
> - Private->BlkIo2Ppi.Revision =
> EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
> - Private->BlkIo2Ppi.GetNumberOfBlockDevices =
> NvmeBlockIoPeimGetDeviceNo2;
> - Private->BlkIo2Ppi.GetBlockDeviceMediaInfo =
> NvmeBlockIoPeimGetMediaInfo2;
> - Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
> - CopyMem (&Private->BlkIoPpiList, &mNvmeBlkIoPpiListTemplate, sizeof
> (EFI_PEI_PPI_DESCRIPTOR));
> - CopyMem (&Private->BlkIo2PpiList, &mNvmeBlkIo2PpiListTemplate,
> sizeof (EFI_PEI_PPI_DESCRIPTOR));
> - CopyMem (&Private->EndOfPeiNotifyList,
> &mNvmeEndOfPeiNotifyListTemplate, sizeof
> (EFI_PEI_NOTIFY_DESCRIPTOR));
> - Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
> - Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
> + Private->Signature =
> NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
> + Private->MmioBase = MmioBase;
> + Private->DevicePathLength = DevicePathLength;
> + Private->DevicePath = DevicePath;
>
> //
> // Initialize the NVME controller
> @@ -305,11 +326,9 @@ NvmExpressPeimEntry (
> DEBUG ((
> DEBUG_ERROR,
> "%a: Controller initialization fail for Controller %d with Status - %r.\n",
> - __FUNCTION__,
> - Controller,
> - Status
> + __FUNCTION__, Controller, Status
> ));
> - NvmeFreeControllerResource (Private);
> + NvmeFreeDmaResource (Private);
> Controller++;
> continue;
> }
> @@ -325,22 +344,68 @@ NvmExpressPeimEntry (
> DEBUG ((
> DEBUG_ERROR,
> "%a: Namespaces discovery fail for Controller %d with Status - %r.\n",
> - __FUNCTION__,
> - Controller,
> - Status
> + __FUNCTION__, Controller, Status
> ));
> - NvmeFreeControllerResource (Private);
> + NvmeFreeDmaResource (Private);
> Controller++;
> continue;
> }
>
> + Private->BlkIoPpi.GetNumberOfBlockDevices =
> NvmeBlockIoPeimGetDeviceNo;
> + Private->BlkIoPpi.GetBlockDeviceMediaInfo =
> NvmeBlockIoPeimGetMediaInfo;
> + Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
> + CopyMem (
> + &Private->BlkIoPpiList,
> + &mNvmeBlkIoPpiListTemplate,
> + sizeof (EFI_PEI_PPI_DESCRIPTOR)
> + );
> + Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
> +
> + Private->BlkIo2Ppi.Revision =
> EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
> + Private->BlkIo2Ppi.GetNumberOfBlockDevices =
> NvmeBlockIoPeimGetDeviceNo2;
> + Private->BlkIo2Ppi.GetBlockDeviceMediaInfo =
> NvmeBlockIoPeimGetMediaInfo2;
> + Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
> + CopyMem (
> + &Private->BlkIo2PpiList,
> + &mNvmeBlkIo2PpiListTemplate,
> + sizeof (EFI_PEI_PPI_DESCRIPTOR)
> + );
> + Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
> PeiServicesInstallPpi (&Private->BlkIoPpiList);
> - PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
> +
> + //
> + // Check if the NVME controller supports the Security Receive/Send
> commands
> + //
> + if ((Private->ControllerData->Oacs &
> SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {
> + DEBUG ((
> + DEBUG_INFO,
> + "%a: Security Security Command PPI will be produced for
> Controller %d.\n",
> + __FUNCTION__, Controller
> + ));
> + Private->StorageSecurityPpi.Revision =
> EDKII_STORAGE_SECURITY_PPI_REVISION;
> + Private->StorageSecurityPpi.GetNumberofDevices =
> NvmeStorageSecurityGetDeviceNo;
> + Private->StorageSecurityPpi.GetDevicePath =
> NvmeStorageSecurityGetDevicePath;
> + Private->StorageSecurityPpi.ReceiveData =
> NvmeStorageSecurityReceiveData;
> + Private->StorageSecurityPpi.SendData =
> NvmeStorageSecuritySendData;
> + CopyMem (
> + &Private->StorageSecurityPpiList,
> + &mNvmeStorageSecurityPpiListTemplate,
> + sizeof (EFI_PEI_PPI_DESCRIPTOR)
> + );
> + Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi;
> + PeiServicesInstallPpi (&Private->StorageSecurityPpiList);
> + }
> +
> + CopyMem (
> + &Private->EndOfPeiNotifyList,
> + &mNvmeEndOfPeiNotifyListTemplate,
> + sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)
> + );
> + PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
> +
> DEBUG ((
> - DEBUG_INFO,
> - "%a: BlockIO PPI has been installed on Controller %d.\n",
> - __FUNCTION__,
> - Controller
> + DEBUG_INFO, "%a: Controller %d has been successfully initialized.\n",
> + __FUNCTION__, Controller
> ));
> Controller++;
> }
> diff --git a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c
> index d4056a2a5b..b9fa3230f8 100644
> --- a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c
> +++ b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiHci.c
> @@ -2,7 +2,7 @@
> The NvmExpressPei driver is used to manage non-volatile memory
> subsystem
> which follows NVM Express specification at PEI phase.
>
> - Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> + Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
>
> This program and the accompanying materials
> are licensed and made available under the terms and conditions
> @@ -702,47 +702,25 @@ NvmeControllerInit (
> }
>
> /**
> - Free the resources allocated by an NVME controller.
> + Free the DMA resources allocated by an NVME controller.
>
> @param[in] Private The pointer to the
> PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
>
> **/
> VOID
> -NvmeFreeControllerResource (
> +NvmeFreeDmaResource (
> IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
> )
> {
> - //
> - // Free the controller data buffer
> - //
> - if (Private->ControllerData != NULL) {
> - FreePool (Private->ControllerData);
> - Private->ControllerData = NULL;
> - }
> + ASSERT (Private != NULL);
>
> - //
> - // Free the DMA buffers
> - //
> - if (Private->Buffer != NULL) {
> + if (Private->BufferMapping != NULL) {
> IoMmuFreeBuffer (
> NVME_MEM_MAX_PAGES,
> Private->Buffer,
> Private->BufferMapping
> );
> - Private->Buffer = NULL;
> }
>
> - //
> - // Free the namespaces information buffer
> - //
> - if (Private->NamespaceInfo != NULL) {
> - FreePool (Private->NamespaceInfo);
> - Private->NamespaceInfo = NULL;
> - }
> -
> - //
> - // Free the controller private data structure
> - //
> - FreePool (Private);
> return;
> }
> diff --git
> a/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.
> c
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.
> c
> new file mode 100644
> index 0000000000..e5a2cef3d6
> --- /dev/null
> +++
> b/MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiStorageSecurity.
> c
> @@ -0,0 +1,423 @@
> +/** @file
> + The NvmExpressPei driver is used to manage non-volatile memory
> subsystem
> + which follows NVM Express specification at PEI phase.
> +
> + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
> +
> + 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 "NvmExpressPei.h"
> +
> +/**
> + Trust transfer data from/to NVM Express device.
> +
> + This function performs one NVMe transaction to do a trust transfer
> from/to NVM
> + Express device.
> +
> + @param[in] Private The pointer to the
> PEI_NVME_CONTROLLER_PRIVATE_DATA
> + data structure.
> + @param[in,out] Buffer The pointer to the current transaction buffer.
> + @param[in] SecurityProtocolId
> + The value of the "Security Protocol" parameter
> + of the security protocol command to be sent.
> + @param[in] SecurityProtocolSpecificData
> + The value of the "Security Protocol Specific"
> + parameter of the security protocol command to
> + be sent.
> + @param[in] TransferLength The block number or sector count of the
> transfer.
> + @param[in] IsTrustSend Indicates whether it is a trust send operation
> + or not.
> + @param[in] Timeout The timeout, in 100ns units, to use for the
> + execution of the security protocol command.
> + A Timeout value of 0 means that this function
> + will wait indefinitely for the security protocol
> + command to execute. If Timeout is greater than
> + zero, then this function will return EFI_TIMEOUT
> + if the time required to execute the receive
> + data command is greater than Timeout.
> + @param[out] TransferLengthOut A pointer to a buffer to store the size in
> bytes
> + of the data written to the buffer. Ignore it
> + when IsTrustSend is TRUE.
> +
> + @retval EFI_SUCCESS The data transfer is complete successfully.
> + @return others Some error occurs when transferring data.
> +
> +**/
> +EFI_STATUS
> +TrustTransferNvmeDevice (
> + IN PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
> + IN OUT VOID *Buffer,
> + IN UINT8 SecurityProtocolId,
> + IN UINT16 SecurityProtocolSpecificData,
> + IN UINTN TransferLength,
> + IN BOOLEAN IsTrustSend,
> + IN UINT64 Timeout,
> + OUT UINTN *TransferLengthOut
> + )
> +{
> + EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
> CommandPacket;
> + EDKII_PEI_NVM_EXPRESS_COMMAND Command;
> + EDKII_PEI_NVM_EXPRESS_COMPLETION Completion;
> + EFI_STATUS Status;
> + UINT16 SpecificData;
> +
> + ZeroMem (&CommandPacket,
> sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
> + ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND));
> + ZeroMem (&Completion,
> sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION));
> +
> + CommandPacket.NvmeCmd = &Command;
> + CommandPacket.NvmeCompletion = &Completion;
> +
> + //
> + // Change Endianness of SecurityProtocolSpecificData
> + //
> + SpecificData = (((SecurityProtocolSpecificData << 8) & 0xFF00) |
> (SecurityProtocolSpecificData >> 8));
> +
> + if (IsTrustSend) {
> + Command.Cdw0.Opcode = NVME_ADMIN_SECURITY_SEND_CMD;
> + CommandPacket.TransferBuffer = Buffer;
> + CommandPacket.TransferLength = (UINT32)TransferLength;
> + CommandPacket.NvmeCmd->Cdw10 = (UINT32)((SecurityProtocolId <<
> 24) | (SpecificData << 8));
> + CommandPacket.NvmeCmd->Cdw11 = (UINT32)TransferLength;
> + } else {
> + Command.Cdw0.Opcode = NVME_ADMIN_SECURITY_RECEIVE_CMD;
> + CommandPacket.TransferBuffer = Buffer;
> + CommandPacket.TransferLength = (UINT32)TransferLength;
> + CommandPacket.NvmeCmd->Cdw10 = (UINT32)((SecurityProtocolId <<
> 24) | (SpecificData << 8));
> + CommandPacket.NvmeCmd->Cdw11 = (UINT32)TransferLength;
> + }
> +
> + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
> + CommandPacket.NvmeCmd->Nsid = NVME_CONTROLLER_NSID;
> + CommandPacket.CommandTimeout = Timeout;
> + CommandPacket.QueueType = NVME_ADMIN_QUEUE;
> +
> + Status = NvmePassThru (
> + Private,
> + NVME_CONTROLLER_NSID,
> + &CommandPacket
> + );
> +
> + if (!IsTrustSend) {
> + if (EFI_ERROR (Status)) {
> + *TransferLengthOut = 0;
> + } else {
> + *TransferLengthOut = (UINTN) TransferLength;
> + }
> + }
> +
> + return Status;
> +}
> +
> +/**
> + Gets the count of storage security devices that one specific driver detects.
> +
> + @param[in] This The PPI instance pointer.
> + @param[out] NumberofDevices The number of storage security devices
> discovered.
> +
> + @retval EFI_SUCCESS The operation performed successfully.
> + @retval EFI_INVALID_PARAMETER The parameters are invalid.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecurityGetDeviceNo (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + OUT UINTN *NumberofDevices
> + )
> +{
> + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
> +
> + if (This == NULL || NumberofDevices == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Private =
> GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY
> (This);
> + *NumberofDevices = Private->ActiveNamespaceNum;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Gets the device path of a specific storage security device.
> +
> + @param[in] This The PPI instance pointer.
> + @param[in] DeviceIndex Specifies the storage security device to
> which
> + the function wants to talk. Because the driver
> + that implements Storage Security Command PPIs
> + will manage multiple storage devices, the PPIs
> + that want to talk to a single device must specify
> + the device index that was assigned during the
> + enumeration process. This index is a number from
> + one to NumberofDevices.
> + @param[out] DevicePathLength The length of the device path in bytes
> specified
> + by DevicePath.
> + @param[out] DevicePath The device path of storage security device.
> + This field re-uses EFI Device Path Protocol as
> + defined by Section 10.2 EFI Device Path Protocol
> + of UEFI 2.7 Specification.
> +
> + @retval EFI_SUCCESS The operation succeeds.
> + @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is
> NULL.
> + @retval EFI_NOT_FOUND The specified storage security device not
> found.
> + @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of
> resources.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecurityGetDevicePath (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + IN UINTN DeviceIndex,
> + OUT UINTN *DevicePathLength,
> + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
> + )
> +{
> + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
> +
> + if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Private =
> GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY
> (This);
> + if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum))
> {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + return NvmeBuildDevicePath (
> + Private,
> + Private->NamespaceInfo[DeviceIndex-1].NamespaceId,
> + Private->NamespaceInfo[DeviceIndex-1].NamespaceUuid,
> + DevicePathLength,
> + DevicePath
> + );
> +}
> +
> +/**
> + Send a security protocol command to a device that receives data and/or
> the result
> + of one or more commands sent by SendData.
> +
> + The ReceiveData function sends a security protocol command to the given
> DeviceIndex.
> + The security protocol command sent is defined by SecurityProtocolId and
> contains
> + the security protocol specific data SecurityProtocolSpecificData. The
> function
> + returns the data from the security protocol command in PayloadBuffer.
> +
> + For devices supporting the SCSI command set, the security protocol
> command is sent
> + using the SECURITY PROTOCOL IN command defined in SPC-4.
> +
> + For devices supporting the ATA command set, the security protocol
> command is sent
> + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if
> PayloadBufferSize
> + is non-zero.
> +
> + If the PayloadBufferSize is zero, the security protocol command is sent
> using the
> + Trusted Non-Data command defined in ATA8-ACS.
> +
> + If PayloadBufferSize is too small to store the available data from the
> security
> + protocol command, the function shall copy PayloadBufferSize bytes into
> the
> + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
> +
> + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is
> non-zero,
> + the function shall return EFI_INVALID_PARAMETER.
> +
> + If the given DeviceIndex does not support security protocol commands,
> the function
> + shall return EFI_UNSUPPORTED.
> +
> + If the security protocol fails to complete within the Timeout period, the
> function
> + shall return EFI_TIMEOUT.
> +
> + If the security protocol command completes without an error, the function
> shall
> + return EFI_SUCCESS. If the security protocol command completes with an
> error, the
> + function shall return EFI_DEVICE_ERROR.
> +
> + @param[in] This The PPI instance pointer.
> + @param[in] DeviceIndex Specifies the storage security device to which
> the
> + function wants to talk. Because the driver that
> + implements Storage Security Command PPIs will manage
> + multiple storage devices, the PPIs that want to talk
> + to a single device must specify the device index
> + that was assigned during the enumeration process.
> + This index is a number from one to NumberofDevices.
> + @param[in] Timeout The timeout, in 100ns units, to use for the
> execution
> + of the security protocol command. A Timeout value
> + of 0 means that this function will wait indefinitely
> + for the security protocol command to execute. If
> + Timeout is greater than zero, then this function
> + will return EFI_TIMEOUT if the time required to
> + execute the receive data command is greater than
> + Timeout.
> + @param[in] SecurityProtocolId
> + The value of the "Security Protocol" parameter of
> + the security protocol command to be sent.
> + @param[in] SecurityProtocolSpecificData
> + The value of the "Security Protocol Specific"
> + parameter of the security protocol command to be
> + sent.
> + @param[in] PayloadBufferSize
> + Size in bytes of the payload data buffer.
> + @param[out] PayloadBuffer A pointer to a destination buffer to store
> the
> + security protocol command specific payload data
> + for the security protocol command. The caller is
> + responsible for having either implicit or explicit
> + ownership of the buffer.
> + @param[out] PayloadTransferSize
> + A pointer to a buffer to store the size in bytes
> + of the data written to the payload data buffer.
> +
> + @retval EFI_SUCCESS The security protocol command completed
> + successfully.
> + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too
> small to
> + store the available data from the device.
> + The PayloadBuffer contains the truncated
> + data.
> + @retval EFI_UNSUPPORTED The given DeviceIndex does not
> support
> + security protocol commands.
> + @retval EFI_DEVICE_ERROR The security protocol command
> completed
> + with an error.
> + @retval EFI_INVALID_PARAMETER The PayloadBuffer or
> PayloadTransferSize
> + is NULL and PayloadBufferSize is non-zero.
> + @retval EFI_TIMEOUT A timeout occurred while waiting for the
> + security protocol command to execute.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecurityReceiveData (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + IN UINTN DeviceIndex,
> + IN UINT64 Timeout,
> + IN UINT8 SecurityProtocolId,
> + IN UINT16 SecurityProtocolSpecificData,
> + IN UINTN PayloadBufferSize,
> + OUT VOID *PayloadBuffer,
> + OUT UINTN *PayloadTransferSize
> + )
> +{
> + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
> + EFI_STATUS Status;
> +
> + if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL) ||
> (PayloadBufferSize == 0)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Private =
> GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY
> (This);
> +
> + Status = TrustTransferNvmeDevice (
> + Private,
> + PayloadBuffer,
> + SecurityProtocolId,
> + SecurityProtocolSpecificData,
> + PayloadBufferSize,
> + FALSE,
> + Timeout,
> + PayloadTransferSize
> + );
> +
> + return Status;
> +}
> +
> +/**
> + Send a security protocol command to a device.
> +
> + The SendData function sends a security protocol command containing the
> payload
> + PayloadBuffer to the given DeviceIndex. The security protocol command
> sent is
> + defined by SecurityProtocolId and contains the security protocol specific
> data
> + SecurityProtocolSpecificData. If the underlying protocol command requires
> a
> + specific padding for the command payload, the SendData function shall
> add padding
> + bytes to the command payload to satisfy the padding requirements.
> +
> + For devices supporting the SCSI command set, the security protocol
> command is
> + sent using the SECURITY PROTOCOL OUT command defined in SPC-4.
> +
> + For devices supporting the ATA command set, the security protocol
> command is
> + sent using one of the TRUSTED SEND commands defined in ATA8-ACS if
> PayloadBufferSize
> + is non-zero. If the PayloadBufferSize is zero, the security protocol
> command
> + is sent using the Trusted Non-Data command defined in ATA8-ACS.
> +
> + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function
> shall
> + return EFI_INVALID_PARAMETER.
> +
> + If the given DeviceIndex does not support security protocol commands,
> the function
> + shall return EFI_UNSUPPORTED.
> +
> + If the security protocol fails to complete within the Timeout period, the
> function
> + shall return EFI_TIMEOUT.
> +
> + If the security protocol command completes without an error, the function
> shall
> + return EFI_SUCCESS. If the security protocol command completes with an
> error,
> + the functio shall return EFI_DEVICE_ERROR.
> +
> + @param[in] This The PPI instance pointer.
> + @param[in] DeviceIndex The ID of the device.
> + @param[in] Timeout The timeout, in 100ns units, to use for the
> execution
> + of the security protocol command. A Timeout value
> + of 0 means that this function will wait indefinitely
> + for the security protocol command to execute. If
> + Timeout is greater than zero, then this function
> + will return EFI_TIMEOUT if the time required to
> + execute the receive data command is greater than
> + Timeout.
> + @param[in] SecurityProtocolId
> + The value of the "Security Protocol" parameter of
> + the security protocol command to be sent.
> + @param[in] SecurityProtocolSpecificData
> + The value of the "Security Protocol Specific"
> + parameter of the security protocol command to be
> + sent.
> + @param[in] PayloadBufferSize Size in bytes of the payload data buffer.
> + @param[in] PayloadBuffer A pointer to a destination buffer to store the
> + security protocol command specific payload data
> + for the security protocol command.
> +
> + @retval EFI_SUCCESS The security protocol command completed
> successfully.
> + @retval EFI_UNSUPPORTED The given DeviceIndex does not support
> security
> + protocol commands.
> + @retval EFI_DEVICE_ERROR The security protocol command
> completed with
> + an error.
> + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and
> PayloadBufferSize
> + is non-zero.
> + @retval EFI_TIMEOUT A timeout occurred while waiting for the
> security
> + protocol command to execute.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +NvmeStorageSecuritySendData (
> + IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,
> + IN UINTN DeviceIndex,
> + IN UINT64 Timeout,
> + IN UINT8 SecurityProtocolId,
> + IN UINT16 SecurityProtocolSpecificData,
> + IN UINTN PayloadBufferSize,
> + IN VOID *PayloadBuffer
> + )
> +{
> + PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
> + EFI_STATUS Status;
> +
> + if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Private =
> GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY
> (This);
> +
> + Status = TrustTransferNvmeDevice (
> + Private,
> + PayloadBuffer,
> + SecurityProtocolId,
> + SecurityProtocolSpecificData,
> + PayloadBufferSize,
> + TRUE,
> + Timeout,
> + NULL
> + );
> +
> + return Status;
> +}
> --
> 2.12.0.windows.1
next prev parent reply other threads:[~2019-01-31 3:35 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-31 2:48 [PATCH v2 00/12] Split the S3 PEI phase HW init codes from Opal driver Hao Wu
2019-01-31 2:48 ` [PATCH v2 01/12] MdeModulePkg: Add definitions for ATA AHCI host controller PPI Hao Wu
2019-01-31 3:25 ` Ni, Ray
2019-01-31 2:48 ` [PATCH v2 02/12] MdeModulePkg: Add definitions for EDKII PEI ATA PassThru PPI Hao Wu
2019-01-31 3:22 ` Ni, Ray
2019-01-31 5:28 ` Wu, Hao A
2019-01-31 2:48 ` [PATCH v2 03/12] MdeModulePkg: Add definitions for Storage Security Command PPI Hao Wu
2019-01-31 3:26 ` Ni, Ray
2019-01-31 2:48 ` [PATCH v2 04/12] MdeModulePkg: Add GUID for LockBox to save storage dev to init in S3 Hao Wu
2019-01-31 3:27 ` Ni, Ray
2019-01-31 5:30 ` Wu, Hao A
2019-01-31 2:48 ` [PATCH v2 05/12] MdeModulePkg/NvmExpressPei: Avoid updating the module-level variable Hao Wu
2019-01-31 3:28 ` Ni, Ray
2019-01-31 2:48 ` [PATCH v2 06/12] MdeModulePkg/NvmExpressPei: Add logic to produce SSC PPI Hao Wu
2019-01-31 3:35 ` Ni, Ray [this message]
2019-01-31 5:40 ` Wu, Hao A
2019-01-31 2:48 ` [PATCH v2 07/12] MdeModulePkg/NvmExpressPei: Consume S3StorageDeviceInitList LockBox Hao Wu
2019-01-31 3:45 ` Ni, Ray
2019-01-31 5:45 ` Wu, Hao A
2019-01-31 2:48 ` [PATCH v2 08/12] MdeModulePkg/AhciPei: Add AHCI mode ATA device support in PEI Hao Wu
2019-01-31 5:49 ` Ni, Ruiyu
2019-01-31 2:48 ` [PATCH v2 09/12] MdeModulePkg/SmmLockBoxLib: Use 'DEBUG_' prefix instead of 'EFI_D_' Hao Wu
2019-01-31 5:49 ` Ni, Ruiyu
2019-01-31 2:48 ` [PATCH v2 10/12] MdeModulePkg/SmmLockBox(PEI): Remove an ASSERT in RestoreLockBox() Hao Wu
2019-01-31 5:50 ` Ni, Ruiyu
2019-01-31 5:53 ` Wu, Hao A
2019-01-31 2:48 ` [PATCH v2 11/12] MdeModulePkg/SmmLockBoxLib: Support LockBox enlarge in UpdateLockBox() Hao Wu
2019-01-31 6:00 ` Ni, Ruiyu
2019-01-31 2:48 ` [PATCH v2 12/12] SecurityPkg/OpalPassword: Remove HW init codes and consume SSC PPI Hao Wu
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=734D49CCEBEEF84792F5B80ED585239D5BFFFFE7@SHSMSX104.ccr.corp.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