* [PATCH v7 1/5] spdm-socket: add seperate send/recv functions
2025-09-12 2:11 [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
@ 2025-09-12 2:11 ` Wilfred Mallawa
2025-10-03 1:53 ` Alistair Francis
2025-09-12 2:11 ` [PATCH v7 2/5] spdm: add spdm storage transport virtual header Wilfred Mallawa
` (4 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Wilfred Mallawa @ 2025-09-12 2:11 UTC (permalink / raw)
To: Alistair Francis
Cc: Keith Busch, Klaus Jensen, Jesper Devantier, Stefan Hajnoczi,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
Jonathan Cameron, Wilfred Mallawa, Jonathan Cameron
From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
This is to support uni-directional transports such as SPDM over Storage.
As specified by the DMTF DSP0286.
Also update spdm_socket_rsp() to use the new send()/receive() functions. For
the case of spdm_socket_receive(), this allows us to do error checking
in one place with the addition of spdm_socket_command_valid().
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
backends/spdm-socket.c | 56 ++++++++++++++++++++++++++++--------
include/system/spdm-socket.h | 32 +++++++++++++++++++++
2 files changed, 76 insertions(+), 12 deletions(-)
diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
index 2c709c68c8..ab74a02d9c 100644
--- a/backends/spdm-socket.c
+++ b/backends/spdm-socket.c
@@ -184,29 +184,61 @@ int spdm_socket_connect(uint16_t port, Error **errp)
return client_socket;
}
-uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
- void *req, uint32_t req_len,
- void *rsp, uint32_t rsp_len)
+static bool spdm_socket_command_valid(uint32_t command)
+{
+ switch (command) {
+ case SPDM_SOCKET_COMMAND_NORMAL:
+ case SPDM_SOCKET_STORAGE_CMD_IF_SEND:
+ case SPDM_SOCKET_STORAGE_CMD_IF_RECV:
+ case SOCKET_SPDM_STORAGE_ACK_STATUS:
+ case SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE:
+ case SPDM_SOCKET_COMMAND_CONTINUE:
+ case SPDM_SOCKET_COMMAND_SHUTDOWN:
+ case SPDM_SOCKET_COMMAND_UNKOWN:
+ case SPDM_SOCKET_COMMAND_TEST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+uint32_t spdm_socket_receive(const int socket, uint32_t transport_type,
+ void *rsp, uint32_t rsp_len)
{
uint32_t command;
bool result;
- result = send_platform_data(socket, transport_type,
- SPDM_SOCKET_COMMAND_NORMAL,
- req, req_len);
- if (!result) {
+ result = receive_platform_data(socket, transport_type, &command,
+ (uint8_t *)rsp, &rsp_len);
+
+ /* we may have received some data, but check if the command is valid */
+ if (!result || !spdm_socket_command_valid(command)) {
return 0;
}
- result = receive_platform_data(socket, transport_type, &command,
- (uint8_t *)rsp, &rsp_len);
+ return rsp_len;
+}
+
+bool spdm_socket_send(const int socket, uint32_t socket_cmd,
+ uint32_t transport_type, void *req, uint32_t req_len)
+{
+ return send_platform_data(socket, transport_type, socket_cmd, req,
+ req_len);
+}
+
+uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
+ void *req, uint32_t req_len,
+ void *rsp, uint32_t rsp_len)
+{
+ bool result;
+
+ result = spdm_socket_send(socket, SPDM_SOCKET_COMMAND_NORMAL,
+ transport_type, req, req_len);
if (!result) {
return 0;
}
- assert(command != 0);
-
- return rsp_len;
+ return spdm_socket_receive(socket, transport_type, rsp, rsp_len);
}
void spdm_socket_close(const int socket, uint32_t transport_type)
diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
index 5d8bd9aa4e..29aa04fd52 100644
--- a/include/system/spdm-socket.h
+++ b/include/system/spdm-socket.h
@@ -50,6 +50,35 @@ uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
void *req, uint32_t req_len,
void *rsp, uint32_t rsp_len);
+/**
+ * spdm_socket_rsp: Receive a message from an SPDM server
+ * @socket: socket returned from spdm_socket_connect()
+ * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
+ * @rsp: response buffer
+ * @rsp_len: response buffer length
+ *
+ * Receives a message from the SPDM server and returns the number of bytes
+ * received or 0 on failure. This can be used to receive a message from the SPDM
+ * server without sending anything first.
+ */
+uint32_t spdm_socket_receive(const int socket, uint32_t transport_type,
+ void *rsp, uint32_t rsp_len);
+
+/**
+ * spdm_socket_rsp: Sends a message to an SPDM server
+ * @socket: socket returned from spdm_socket_connect()
+ * @socket_cmd: socket command type (normal/if_recv/if_send etc...)
+ * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
+ * @req: request buffer
+ * @req_len: request buffer length
+ *
+ * Sends platform data to a SPDM server on socket, returns true on success.
+ * The response from the server must then be fetched by using
+ * spdm_socket_receive().
+ */
+bool spdm_socket_send(const int socket, uint32_t socket_cmd,
+ uint32_t transport_type, void *req, uint32_t req_len);
+
/**
* spdm_socket_close: send a shutdown command to the server
* @socket: socket returned from spdm_socket_connect()
@@ -60,6 +89,9 @@ uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
void spdm_socket_close(const int socket, uint32_t transport_type);
#define SPDM_SOCKET_COMMAND_NORMAL 0x0001
+#define SPDM_SOCKET_STORAGE_CMD_IF_SEND 0x0002
+#define SPDM_SOCKET_STORAGE_CMD_IF_RECV 0x0003
+#define SOCKET_SPDM_STORAGE_ACK_STATUS 0x0004
#define SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE 0x8001
#define SPDM_SOCKET_COMMAND_CONTINUE 0xFFFD
#define SPDM_SOCKET_COMMAND_SHUTDOWN 0xFFFE
--
2.51.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v7 1/5] spdm-socket: add seperate send/recv functions
2025-09-12 2:11 ` [PATCH v7 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
@ 2025-10-03 1:53 ` Alistair Francis
0 siblings, 0 replies; 15+ messages in thread
From: Alistair Francis @ 2025-10-03 1:53 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Klaus Jensen, Jesper Devantier,
Stefan Hajnoczi, Fam Zheng, Philippe Mathieu-Daudé,
Kevin Wolf, Hanna Reitz, Michael S . Tsirkin, Marcel Apfelbaum,
qemu-devel, qemu-block, Jonathan Cameron, Wilfred Mallawa
On Fri, Sep 12, 2025 at 12:13 PM Wilfred Mallawa
<wilfred.opensource@gmail.com> wrote:
>
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> This is to support uni-directional transports such as SPDM over Storage.
> As specified by the DMTF DSP0286.
>
> Also update spdm_socket_rsp() to use the new send()/receive() functions. For
> the case of spdm_socket_receive(), this allows us to do error checking
> in one place with the addition of spdm_socket_command_valid().
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> backends/spdm-socket.c | 56 ++++++++++++++++++++++++++++--------
> include/system/spdm-socket.h | 32 +++++++++++++++++++++
> 2 files changed, 76 insertions(+), 12 deletions(-)
>
> diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
> index 2c709c68c8..ab74a02d9c 100644
> --- a/backends/spdm-socket.c
> +++ b/backends/spdm-socket.c
> @@ -184,29 +184,61 @@ int spdm_socket_connect(uint16_t port, Error **errp)
> return client_socket;
> }
>
> -uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
> - void *req, uint32_t req_len,
> - void *rsp, uint32_t rsp_len)
> +static bool spdm_socket_command_valid(uint32_t command)
> +{
> + switch (command) {
> + case SPDM_SOCKET_COMMAND_NORMAL:
> + case SPDM_SOCKET_STORAGE_CMD_IF_SEND:
> + case SPDM_SOCKET_STORAGE_CMD_IF_RECV:
> + case SOCKET_SPDM_STORAGE_ACK_STATUS:
> + case SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE:
> + case SPDM_SOCKET_COMMAND_CONTINUE:
> + case SPDM_SOCKET_COMMAND_SHUTDOWN:
> + case SPDM_SOCKET_COMMAND_UNKOWN:
> + case SPDM_SOCKET_COMMAND_TEST:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +uint32_t spdm_socket_receive(const int socket, uint32_t transport_type,
> + void *rsp, uint32_t rsp_len)
> {
> uint32_t command;
> bool result;
>
> - result = send_platform_data(socket, transport_type,
> - SPDM_SOCKET_COMMAND_NORMAL,
> - req, req_len);
> - if (!result) {
> + result = receive_platform_data(socket, transport_type, &command,
> + (uint8_t *)rsp, &rsp_len);
> +
> + /* we may have received some data, but check if the command is valid */
> + if (!result || !spdm_socket_command_valid(command)) {
> return 0;
> }
>
> - result = receive_platform_data(socket, transport_type, &command,
> - (uint8_t *)rsp, &rsp_len);
> + return rsp_len;
> +}
> +
> +bool spdm_socket_send(const int socket, uint32_t socket_cmd,
> + uint32_t transport_type, void *req, uint32_t req_len)
> +{
> + return send_platform_data(socket, transport_type, socket_cmd, req,
> + req_len);
> +}
> +
> +uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
> + void *req, uint32_t req_len,
> + void *rsp, uint32_t rsp_len)
> +{
> + bool result;
> +
> + result = spdm_socket_send(socket, SPDM_SOCKET_COMMAND_NORMAL,
> + transport_type, req, req_len);
> if (!result) {
> return 0;
> }
>
> - assert(command != 0);
> -
> - return rsp_len;
> + return spdm_socket_receive(socket, transport_type, rsp, rsp_len);
> }
>
> void spdm_socket_close(const int socket, uint32_t transport_type)
> diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
> index 5d8bd9aa4e..29aa04fd52 100644
> --- a/include/system/spdm-socket.h
> +++ b/include/system/spdm-socket.h
> @@ -50,6 +50,35 @@ uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
> void *req, uint32_t req_len,
> void *rsp, uint32_t rsp_len);
>
> +/**
> + * spdm_socket_rsp: Receive a message from an SPDM server
> + * @socket: socket returned from spdm_socket_connect()
> + * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
> + * @rsp: response buffer
> + * @rsp_len: response buffer length
> + *
> + * Receives a message from the SPDM server and returns the number of bytes
> + * received or 0 on failure. This can be used to receive a message from the SPDM
> + * server without sending anything first.
> + */
> +uint32_t spdm_socket_receive(const int socket, uint32_t transport_type,
> + void *rsp, uint32_t rsp_len);
> +
> +/**
> + * spdm_socket_rsp: Sends a message to an SPDM server
> + * @socket: socket returned from spdm_socket_connect()
> + * @socket_cmd: socket command type (normal/if_recv/if_send etc...)
> + * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
> + * @req: request buffer
> + * @req_len: request buffer length
> + *
> + * Sends platform data to a SPDM server on socket, returns true on success.
> + * The response from the server must then be fetched by using
> + * spdm_socket_receive().
> + */
> +bool spdm_socket_send(const int socket, uint32_t socket_cmd,
> + uint32_t transport_type, void *req, uint32_t req_len);
> +
> /**
> * spdm_socket_close: send a shutdown command to the server
> * @socket: socket returned from spdm_socket_connect()
> @@ -60,6 +89,9 @@ uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
> void spdm_socket_close(const int socket, uint32_t transport_type);
>
> #define SPDM_SOCKET_COMMAND_NORMAL 0x0001
> +#define SPDM_SOCKET_STORAGE_CMD_IF_SEND 0x0002
> +#define SPDM_SOCKET_STORAGE_CMD_IF_RECV 0x0003
> +#define SOCKET_SPDM_STORAGE_ACK_STATUS 0x0004
> #define SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE 0x8001
> #define SPDM_SOCKET_COMMAND_CONTINUE 0xFFFD
> #define SPDM_SOCKET_COMMAND_SHUTDOWN 0xFFFE
> --
> 2.51.0
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v7 2/5] spdm: add spdm storage transport virtual header
2025-09-12 2:11 [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
2025-09-12 2:11 ` [PATCH v7 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
@ 2025-09-12 2:11 ` Wilfred Mallawa
2025-10-01 8:46 ` Klaus Jensen
2025-10-01 8:56 ` Jonathan Cameron via
2025-09-12 2:11 ` [PATCH v7 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
` (3 subsequent siblings)
5 siblings, 2 replies; 15+ messages in thread
From: Wilfred Mallawa @ 2025-09-12 2:11 UTC (permalink / raw)
To: Alistair Francis
Cc: Keith Busch, Klaus Jensen, Jesper Devantier, Stefan Hajnoczi,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
Jonathan Cameron, Wilfred Mallawa
From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
This header contains the transport encoding for an SPDM message that
uses the SPDM over Storage transport as defined by the DMTF DSP0286.
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
---
include/system/spdm-socket.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
index 29aa04fd52..80cd7021c1 100644
--- a/include/system/spdm-socket.h
+++ b/include/system/spdm-socket.h
@@ -88,6 +88,18 @@ bool spdm_socket_send(const int socket, uint32_t socket_cmd,
*/
void spdm_socket_close(const int socket, uint32_t transport_type);
+/*
+ * Defines the transport encoding for SPDM, this information shall be passed
+ * down to the SPDM server, when conforming to the SPDM over Storage standard
+ * as defined by DSP0286.
+ */
+typedef struct {
+ uint8_t security_protocol; /* Must be 0xE8 for SPDM Commands */
+ uint16_t security_protocol_specific; /* Bit[7:2] SPDM Operation
+ Bit[0:1] Connection ID */
+ uint32_t length; /* Length of the SPDM Message*/
+} QEMU_PACKED StorageSpdmTransportHeader;
+
#define SPDM_SOCKET_COMMAND_NORMAL 0x0001
#define SPDM_SOCKET_STORAGE_CMD_IF_SEND 0x0002
#define SPDM_SOCKET_STORAGE_CMD_IF_RECV 0x0003
--
2.51.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v7 2/5] spdm: add spdm storage transport virtual header
2025-09-12 2:11 ` [PATCH v7 2/5] spdm: add spdm storage transport virtual header Wilfred Mallawa
@ 2025-10-01 8:46 ` Klaus Jensen
2025-10-01 9:44 ` Wilfred Mallawa
2025-10-01 8:56 ` Jonathan Cameron via
1 sibling, 1 reply; 15+ messages in thread
From: Klaus Jensen @ 2025-10-01 8:46 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Jesper Devantier, Stefan Hajnoczi,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
Jonathan Cameron, Wilfred Mallawa
[-- Attachment #1: Type: text/plain, Size: 1716 bytes --]
On Sep 12 12:11, Wilfred Mallawa wrote:
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> This header contains the transport encoding for an SPDM message that
> uses the SPDM over Storage transport as defined by the DMTF DSP0286.
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> ---
> include/system/spdm-socket.h | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
> index 29aa04fd52..80cd7021c1 100644
> --- a/include/system/spdm-socket.h
> +++ b/include/system/spdm-socket.h
> @@ -88,6 +88,18 @@ bool spdm_socket_send(const int socket, uint32_t socket_cmd,
> */
> void spdm_socket_close(const int socket, uint32_t transport_type);
>
> +/*
> + * Defines the transport encoding for SPDM, this information shall be passed
> + * down to the SPDM server, when conforming to the SPDM over Storage standard
> + * as defined by DSP0286.
> + */
> +typedef struct {
> + uint8_t security_protocol; /* Must be 0xE8 for SPDM Commands */
> + uint16_t security_protocol_specific; /* Bit[7:2] SPDM Operation
> + Bit[0:1] Connection ID */
> + uint32_t length; /* Length of the SPDM Message*/
> +} QEMU_PACKED StorageSpdmTransportHeader;
> +
> #define SPDM_SOCKET_COMMAND_NORMAL 0x0001
> #define SPDM_SOCKET_STORAGE_CMD_IF_SEND 0x0002
> #define SPDM_SOCKET_STORAGE_CMD_IF_RECV 0x0003
> --
> 2.51.0
>
The above struct is specific to the communication protocol with the
specific SPDM server, correct? It's not defined by DSP0286 as far as I
can tell?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v7 2/5] spdm: add spdm storage transport virtual header
2025-10-01 8:46 ` Klaus Jensen
@ 2025-10-01 9:44 ` Wilfred Mallawa
0 siblings, 0 replies; 15+ messages in thread
From: Wilfred Mallawa @ 2025-10-01 9:44 UTC (permalink / raw)
To: its@irrelevant.dk
Cc: hreitz@redhat.com, Alistair Francis, philmd@linaro.org,
stefanha@redhat.com, fam@euphon.net, qemu-devel@nongnu.org,
foss@defmacro.it, kwolf@redhat.com, qemu-block@nongnu.org,
mst@redhat.com, Jonathan.Cameron@huawei.com, kbusch@kernel.org,
marcel.apfelbaum@gmail.com
On Wed, 2025-10-01 at 10:46 +0200, Klaus Jensen wrote:
> On Sep 12 12:11, Wilfred Mallawa wrote:
> > From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> >
> > This header contains the transport encoding for an SPDM message
> > that
> > uses the SPDM over Storage transport as defined by the DMTF
> > DSP0286.
> >
> > Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> > ---
> > include/system/spdm-socket.h | 12 ++++++++++++
> > 1 file changed, 12 insertions(+)
> >
> > diff --git a/include/system/spdm-socket.h b/include/system/spdm-
> > socket.h
> > index 29aa04fd52..80cd7021c1 100644
> > --- a/include/system/spdm-socket.h
> > +++ b/include/system/spdm-socket.h
> > @@ -88,6 +88,18 @@ bool spdm_socket_send(const int socket, uint32_t
> > socket_cmd,
> > */
> > void spdm_socket_close(const int socket, uint32_t transport_type);
> >
> > +/*
> > + * Defines the transport encoding for SPDM, this information shall
> > be passed
> > + * down to the SPDM server, when conforming to the SPDM over
> > Storage standard
> > + * as defined by DSP0286.
> > + */
> > +typedef struct {
> > + uint8_t security_protocol; /* Must be 0xE8 for
> > SPDM Commands */
> > + uint16_t security_protocol_specific; /* Bit[7:2] SPDM
> > Operation
> > + Bit[0:1] Connection
> > ID */
> > + uint32_t length; /* Length of the SPDM
> > Message*/
> > +} QEMU_PACKED StorageSpdmTransportHeader;
> > +
> > #define SPDM_SOCKET_COMMAND_NORMAL 0x0001
> > #define SPDM_SOCKET_STORAGE_CMD_IF_SEND 0x0002
> > #define SPDM_SOCKET_STORAGE_CMD_IF_RECV 0x0003
> > --
> > 2.51.0
> >
>
> The above struct is specific to the communication protocol with the
> specific SPDM server, correct? It's not defined by DSP0286 as far as
> I
> can tell?
Yeah that's correct. For example, spdm-utils will use this [1]. Which
is the only server to support SPDM Storage as far as I am aware.
[1] https://github.com/westerndigitalcorporation/spdm-utils/pull/139
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v7 2/5] spdm: add spdm storage transport virtual header
2025-09-12 2:11 ` [PATCH v7 2/5] spdm: add spdm storage transport virtual header Wilfred Mallawa
2025-10-01 8:46 ` Klaus Jensen
@ 2025-10-01 8:56 ` Jonathan Cameron via
2025-10-01 9:55 ` Wilfred Mallawa
1 sibling, 1 reply; 15+ messages in thread
From: Jonathan Cameron via @ 2025-10-01 8:56 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Klaus Jensen, Jesper Devantier,
Stefan Hajnoczi, Fam Zheng, Philippe Mathieu-Daudé,
Kevin Wolf, Hanna Reitz, Michael S . Tsirkin, Marcel Apfelbaum,
qemu-devel, qemu-block, Wilfred Mallawa
On Fri, 12 Sep 2025 12:11:49 +1000
Wilfred Mallawa <wilfred.opensource@gmail.com> wrote:
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> This header contains the transport encoding for an SPDM message that
> uses the SPDM over Storage transport as defined by the DMTF DSP0286.
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
FWIW I checked the field definitions that I could find, and this looks fine.
If it's possible to give more focused reference (section etc) that might
be worth doing. For example I wasn't entirely sure where the size of
the security_protocol field is defined. This matches with what libspdm has
though.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> ---
> include/system/spdm-socket.h | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
> index 29aa04fd52..80cd7021c1 100644
> --- a/include/system/spdm-socket.h
> +++ b/include/system/spdm-socket.h
> @@ -88,6 +88,18 @@ bool spdm_socket_send(const int socket, uint32_t socket_cmd,
> */
> void spdm_socket_close(const int socket, uint32_t transport_type);
>
> +/*
> + * Defines the transport encoding for SPDM, this information shall be passed
> + * down to the SPDM server, when conforming to the SPDM over Storage standard
> + * as defined by DSP0286.
> + */
> +typedef struct {
> + uint8_t security_protocol; /* Must be 0xE8 for SPDM Commands */
> + uint16_t security_protocol_specific; /* Bit[7:2] SPDM Operation
> + Bit[0:1] Connection ID */
> + uint32_t length; /* Length of the SPDM Message*/
> +} QEMU_PACKED StorageSpdmTransportHeader;
> +
> #define SPDM_SOCKET_COMMAND_NORMAL 0x0001
> #define SPDM_SOCKET_STORAGE_CMD_IF_SEND 0x0002
> #define SPDM_SOCKET_STORAGE_CMD_IF_RECV 0x0003
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v7 2/5] spdm: add spdm storage transport virtual header
2025-10-01 8:56 ` Jonathan Cameron via
@ 2025-10-01 9:55 ` Wilfred Mallawa
0 siblings, 0 replies; 15+ messages in thread
From: Wilfred Mallawa @ 2025-10-01 9:55 UTC (permalink / raw)
To: jonathan.cameron@huawei.com
Cc: its@irrelevant.dk, hreitz@redhat.com, Alistair Francis,
philmd@linaro.org, stefanha@redhat.com, fam@euphon.net,
qemu-devel@nongnu.org, foss@defmacro.it, kwolf@redhat.com,
qemu-block@nongnu.org, mst@redhat.com, kbusch@kernel.org,
marcel.apfelbaum@gmail.com
On Wed, 2025-10-01 at 09:56 +0100, Jonathan Cameron wrote:
> On Fri, 12 Sep 2025 12:11:49 +1000
> Wilfred Mallawa <wilfred.opensource@gmail.com> wrote:
>
> > From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> >
> > This header contains the transport encoding for an SPDM message
> > that
> > uses the SPDM over Storage transport as defined by the DMTF
> > DSP0286.
> >
> > Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> FWIW I checked the field definitions that I could find, and this
> looks fine.
> If it's possible to give more focused reference (section etc) that
> might
> be worth doing. For example I wasn't entirely sure where the size of
> the security_protocol field is defined. This matches with what
> libspdm has
> though.
In regards to the security_protocol field, this is specified in the
SCSI Primary Commands - 5 (SPC-5). Note that even the NVMe spec also
refers to the SPC-5 for this definition. In which it is defined to be a
byte in length. I will add references to this for V8.
Thanks,
Wilfred
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
>
>
> > ---
> > include/system/spdm-socket.h | 12 ++++++++++++
> > 1 file changed, 12 insertions(+)
> >
> > diff --git a/include/system/spdm-socket.h b/include/system/spdm-
> > socket.h
> > index 29aa04fd52..80cd7021c1 100644
> > --- a/include/system/spdm-socket.h
> > +++ b/include/system/spdm-socket.h
> > @@ -88,6 +88,18 @@ bool spdm_socket_send(const int socket, uint32_t
> > socket_cmd,
> > */
> > void spdm_socket_close(const int socket, uint32_t transport_type);
> >
> > +/*
> > + * Defines the transport encoding for SPDM, this information shall
> > be passed
> > + * down to the SPDM server, when conforming to the SPDM over
> > Storage standard
> > + * as defined by DSP0286.
> > + */
> > +typedef struct {
> > + uint8_t security_protocol; /* Must be 0xE8 for
> > SPDM Commands */
> > + uint16_t security_protocol_specific; /* Bit[7:2] SPDM
> > Operation
> > + Bit[0:1] Connection
> > ID */
> > + uint32_t length; /* Length of the SPDM
> > Message*/
> > +} QEMU_PACKED StorageSpdmTransportHeader;
> > +
> > #define SPDM_SOCKET_COMMAND_NORMAL 0x0001
> > #define SPDM_SOCKET_STORAGE_CMD_IF_SEND 0x0002
> > #define SPDM_SOCKET_STORAGE_CMD_IF_RECV 0x0003
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v7 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-12 2:11 [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
2025-09-12 2:11 ` [PATCH v7 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
2025-09-12 2:11 ` [PATCH v7 2/5] spdm: add spdm storage transport virtual header Wilfred Mallawa
@ 2025-09-12 2:11 ` Wilfred Mallawa
2025-10-03 1:58 ` Alistair Francis
2025-09-12 2:11 ` [PATCH v7 4/5] spdm: define SPDM transport enum types Wilfred Mallawa
` (2 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Wilfred Mallawa @ 2025-09-12 2:11 UTC (permalink / raw)
To: Alistair Francis
Cc: Keith Busch, Klaus Jensen, Jesper Devantier, Stefan Hajnoczi,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
Jonathan Cameron, Wilfred Mallawa, Jonathan Cameron, Klaus Jensen
From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Adds the NVMe Admin Security Send/Receive command support with support
for DMTFs SPDM. The transport binding for SPDM is defined in the
DMTF DSP0286.
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
---
hw/nvme/ctrl.c | 212 ++++++++++++++++++++++++++++++++++-
hw/nvme/nvme.h | 5 +
include/block/nvme.h | 15 +++
include/system/spdm-socket.h | 2 +
4 files changed, 233 insertions(+), 1 deletion(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index f5ee6bf260..ad52e8f569 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -282,6 +282,8 @@ static const uint32_t nvme_cse_acs_default[256] = {
[NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC,
[NVME_ADM_CMD_DIRECTIVE_RECV] = NVME_CMD_EFF_CSUPP,
[NVME_ADM_CMD_DIRECTIVE_SEND] = NVME_CMD_EFF_CSUPP,
+ [NVME_ADM_CMD_SECURITY_SEND] = NVME_CMD_EFF_CSUPP,
+ [NVME_ADM_CMD_SECURITY_RECV] = NVME_CMD_EFF_CSUPP,
};
static const uint32_t nvme_cse_iocs_nvm_default[256] = {
@@ -7282,6 +7284,207 @@ static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req)
return NVME_SUCCESS;
}
+static uint16_t nvme_sec_prot_spdm_send(NvmeCtrl *n, NvmeRequest *req)
+{
+ StorageSpdmTransportHeader hdr = {0};
+ g_autofree uint8_t *sec_buf = NULL;
+ uint32_t transfer_len = le32_to_cpu(req->cmd.cdw11);
+ uint32_t transport_transfer_len = transfer_len;
+ uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
+ uint32_t recvd;
+ uint16_t nvme_cmd_status, ret;
+ uint8_t secp = extract32(dw10, 24, 8);
+ uint16_t spsp = extract32(dw10, 8, 16);
+ bool spdm_res;
+
+ if (transport_transfer_len > UINT32_MAX - sizeof(hdr)) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ transport_transfer_len += sizeof(hdr);
+ if (transport_transfer_len > SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ ret = nvme_check_mdts(n, transport_transfer_len);
+ if (ret != NVME_SUCCESS) {
+ return ret;
+ }
+
+ /* Generate the NVMe transport header */
+ hdr.security_protocol = secp;
+ hdr.security_protocol_specific = cpu_to_le16(spsp);
+ hdr.length = cpu_to_le32(transfer_len);
+
+ sec_buf = g_try_malloc0(transport_transfer_len);
+ if (!sec_buf) {
+ return NVME_INTERNAL_DEV_ERROR;
+ }
+
+ /* Attach the transport header */
+ memcpy(sec_buf, &hdr, sizeof(hdr));
+ ret = nvme_h2c(n, sec_buf + sizeof(hdr), transfer_len, req);
+ if (ret) {
+ return ret;
+ }
+
+ spdm_res = spdm_socket_send(n->spdm_socket, SPDM_SOCKET_STORAGE_CMD_IF_SEND,
+ SPDM_SOCKET_TRANSPORT_TYPE_NVME, sec_buf,
+ transport_transfer_len);
+ if (!spdm_res) {
+ return NVME_DATA_TRAS_ERROR | NVME_DNR;
+ }
+
+ /* The responder shall ack with message status */
+ recvd = spdm_socket_receive(n->spdm_socket, SPDM_SOCKET_TRANSPORT_TYPE_NVME,
+ &nvme_cmd_status,
+ SPDM_SOCKET_MAX_MSG_STATUS_LEN);
+
+ nvme_cmd_status = be16_to_cpu(nvme_cmd_status);
+
+ if (recvd < SPDM_SOCKET_MAX_MSG_STATUS_LEN) {
+ return NVME_DATA_TRAS_ERROR | NVME_DNR;
+ }
+
+ return nvme_cmd_status;
+}
+
+/* From host to controller */
+static uint16_t nvme_security_send(NvmeCtrl *n, NvmeRequest *req)
+{
+ uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
+ uint8_t secp = extract32(dw10, 24, 8);
+
+ switch (secp) {
+ case NVME_SEC_PROT_DMTF_SPDM:
+ if (n->spdm_socket < 0) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ return nvme_sec_prot_spdm_send(n, req);
+ default:
+ /* Unsupported Security Protocol Type */
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ return NVME_INVALID_FIELD | NVME_DNR;
+}
+
+static uint16_t nvme_sec_prot_spdm_receive(NvmeCtrl *n, NvmeRequest *req)
+{
+ StorageSpdmTransportHeader hdr;
+ g_autofree uint8_t *rsp_spdm_buf = NULL;
+ uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
+ uint32_t alloc_len = le32_to_cpu(req->cmd.cdw11);
+ uint32_t recvd, spdm_res;
+ uint16_t nvme_cmd_status, ret;
+ uint8_t secp = extract32(dw10, 24, 8);
+ uint8_t spsp = extract32(dw10, 8, 16);
+ if (!alloc_len) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ /* Generate the NVMe transport header */
+ hdr = (StorageSpdmTransportHeader) {
+ .security_protocol = secp,
+ .security_protocol_specific = cpu_to_le16(spsp),
+ .length = cpu_to_le32(alloc_len),
+ };
+
+ /* Forward if_recv to the SPDM Server with SPSP0 */
+ spdm_res = spdm_socket_send(n->spdm_socket, SPDM_SOCKET_STORAGE_CMD_IF_RECV,
+ SPDM_SOCKET_TRANSPORT_TYPE_NVME,
+ &hdr, sizeof(hdr));
+ if (!spdm_res) {
+ return NVME_DATA_TRAS_ERROR | NVME_DNR;
+ }
+
+ /* The responder shall ack with message status */
+ recvd = spdm_socket_receive(n->spdm_socket, SPDM_SOCKET_TRANSPORT_TYPE_NVME,
+ &nvme_cmd_status,
+ SPDM_SOCKET_MAX_MSG_STATUS_LEN);
+ if (recvd < SPDM_SOCKET_MAX_MSG_STATUS_LEN) {
+ return NVME_DATA_TRAS_ERROR | NVME_DNR;
+ }
+
+ nvme_cmd_status = be16_to_cpu(nvme_cmd_status);
+ /* An error here implies the prior if_recv from requester was spurious */
+ if (nvme_cmd_status != NVME_SUCCESS) {
+ return nvme_cmd_status;
+ }
+
+ /* Clear to start receiving data from the server */
+ rsp_spdm_buf = g_try_malloc0(alloc_len);
+ if (!rsp_spdm_buf) {
+ return NVME_INTERNAL_DEV_ERROR;
+ }
+
+ recvd = spdm_socket_receive(n->spdm_socket,
+ SPDM_SOCKET_TRANSPORT_TYPE_NVME,
+ rsp_spdm_buf, alloc_len);
+ if (!recvd) {
+ return NVME_DATA_TRAS_ERROR | NVME_DNR;
+ }
+
+ ret = nvme_c2h(n, rsp_spdm_buf, MIN(recvd, alloc_len), req);
+ if (ret) {
+ return ret;
+ }
+
+ return NVME_SUCCESS;
+}
+
+static uint16_t nvme_get_sec_prot_info(NvmeCtrl *n, NvmeRequest *req)
+{
+ uint32_t alloc_len = le32_to_cpu(req->cmd.cdw11);
+ uint8_t resp[10] = {
+ /* Support Security Protol List Length */
+ [6] = 0, /* MSB */
+ [7] = 2, /* LSB */
+ /* Support Security Protocol List */
+ [8] = SFSC_SECURITY_PROT_INFO,
+ [9] = 0,
+ };
+
+ if (n->spdm_socket >= 0) {
+ resp[9] = NVME_SEC_PROT_DMTF_SPDM;
+ }
+
+ if (alloc_len < 10) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ return nvme_c2h(n, resp, sizeof(resp), req);
+}
+
+/* From controller to host */
+static uint16_t nvme_security_receive(NvmeCtrl *n, NvmeRequest *req)
+{
+ uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
+ uint16_t spsp = extract32(dw10, 8, 16);
+ uint8_t secp = extract32(dw10, 24, 8);
+
+ switch (secp) {
+ case SFSC_SECURITY_PROT_INFO:
+ switch (spsp) {
+ case 0:
+ /* Supported security protocol list */
+ return nvme_get_sec_prot_info(n, req);
+ case 1:
+ /* Certificate data */
+ /* fallthrough */
+ default:
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ case NVME_SEC_PROT_DMTF_SPDM:
+ if (n->spdm_socket < 0) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ return nvme_sec_prot_spdm_receive(n, req);
+ default:
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+}
+
static uint16_t nvme_directive_send(NvmeCtrl *n, NvmeRequest *req)
{
return NVME_INVALID_FIELD | NVME_DNR;
@@ -7389,6 +7592,10 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req)
return nvme_directive_send(n, req);
case NVME_ADM_CMD_DIRECTIVE_RECV:
return nvme_directive_receive(n, req);
+ case NVME_ADM_CMD_SECURITY_SEND:
+ return nvme_security_send(n, req);
+ case NVME_ADM_CMD_SECURITY_RECV:
+ return nvme_security_receive(n, req);
default:
g_assert_not_reached();
}
@@ -8459,6 +8666,8 @@ static void nvme_init_state(NvmeCtrl *n)
sctrl->vfn = cpu_to_le16(i + 1);
}
+ n->spdm_socket = -1;
+
cap->cntlid = cpu_to_le16(n->cntlid);
cap->crt = NVME_CRT_VQ | NVME_CRT_VI;
@@ -8824,7 +9033,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
id->mdts = n->params.mdts;
id->ver = cpu_to_le32(NVME_SPEC_VER);
- oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DIRECTIVES;
+ oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DIRECTIVES |
+ NVME_OACS_SECURITY;
if (n->params.dbcs) {
oacs |= NVME_OACS_DBCS;
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index b5c9378ea4..67ed562e00 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -461,6 +461,8 @@ static inline const char *nvme_adm_opc_str(uint8_t opc)
case NVME_ADM_CMD_DIRECTIVE_RECV: return "NVME_ADM_CMD_DIRECTIVE_RECV";
case NVME_ADM_CMD_DBBUF_CONFIG: return "NVME_ADM_CMD_DBBUF_CONFIG";
case NVME_ADM_CMD_FORMAT_NVM: return "NVME_ADM_CMD_FORMAT_NVM";
+ case NVME_ADM_CMD_SECURITY_SEND: return "NVME_ADM_CMD_SECURITY_SEND";
+ case NVME_ADM_CMD_SECURITY_RECV: return "NVME_ADM_CMD_SECURITY_RECV";
default: return "NVME_ADM_CMD_UNKNOWN";
}
}
@@ -648,6 +650,9 @@ typedef struct NvmeCtrl {
} next_pri_ctrl_cap; /* These override pri_ctrl_cap after reset */
uint32_t dn; /* Disable Normal */
NvmeAtomic atomic;
+
+ /* Socket mapping to SPDM over NVMe Security In/Out commands */
+ int spdm_socket;
} NvmeCtrl;
typedef enum NvmeResetType {
diff --git a/include/block/nvme.h b/include/block/nvme.h
index 358e516e38..9fa2ecaf28 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -1779,6 +1779,21 @@ enum NvmeDirectiveOperations {
NVME_DIRECTIVE_RETURN_PARAMS = 0x1,
};
+typedef enum SfscSecurityProtocol {
+ SFSC_SECURITY_PROT_INFO = 0x00,
+} SfscSecurityProtocol;
+
+typedef enum NvmeSecurityProtocols {
+ NVME_SEC_PROT_DMTF_SPDM = 0xE8,
+} NvmeSecurityProtocols;
+
+typedef enum SpdmOperationCodes {
+ SPDM_STORAGE_DISCOVERY = 0x1, /* Mandatory */
+ SPDM_STORAGE_PENDING_INFO = 0x2, /* Optional */
+ SPDM_STORAGE_MSG = 0x5, /* Mandatory */
+ SPDM_STORAGE_SEC_MSG = 0x6, /* Optional */
+} SpdmOperationCodes;
+
typedef struct QEMU_PACKED NvmeFdpConfsHdr {
uint16_t num_confs;
uint8_t version;
diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
index 80cd7021c1..f0c8b64d1b 100644
--- a/include/system/spdm-socket.h
+++ b/include/system/spdm-socket.h
@@ -112,7 +112,9 @@ typedef struct {
#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
+#define SPDM_SOCKET_TRANSPORT_TYPE_NVME 0x04
#define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
+#define SPDM_SOCKET_MAX_MSG_STATUS_LEN 0x02
#endif
--
2.51.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v7 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-12 2:11 ` [PATCH v7 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
@ 2025-10-03 1:58 ` Alistair Francis
0 siblings, 0 replies; 15+ messages in thread
From: Alistair Francis @ 2025-10-03 1:58 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Klaus Jensen, Jesper Devantier,
Stefan Hajnoczi, Fam Zheng, Philippe Mathieu-Daudé,
Kevin Wolf, Hanna Reitz, Michael S . Tsirkin, Marcel Apfelbaum,
qemu-devel, qemu-block, Jonathan Cameron, Wilfred Mallawa,
Klaus Jensen
On Fri, Sep 12, 2025 at 12:13 PM Wilfred Mallawa
<wilfred.opensource@gmail.com> wrote:
>
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> Adds the NVMe Admin Security Send/Receive command support with support
> for DMTFs SPDM. The transport binding for SPDM is defined in the
> DMTF DSP0286.
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> hw/nvme/ctrl.c | 212 ++++++++++++++++++++++++++++++++++-
> hw/nvme/nvme.h | 5 +
> include/block/nvme.h | 15 +++
> include/system/spdm-socket.h | 2 +
> 4 files changed, 233 insertions(+), 1 deletion(-)
>
> diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
> index f5ee6bf260..ad52e8f569 100644
> --- a/hw/nvme/ctrl.c
> +++ b/hw/nvme/ctrl.c
> @@ -282,6 +282,8 @@ static const uint32_t nvme_cse_acs_default[256] = {
> [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC,
> [NVME_ADM_CMD_DIRECTIVE_RECV] = NVME_CMD_EFF_CSUPP,
> [NVME_ADM_CMD_DIRECTIVE_SEND] = NVME_CMD_EFF_CSUPP,
> + [NVME_ADM_CMD_SECURITY_SEND] = NVME_CMD_EFF_CSUPP,
> + [NVME_ADM_CMD_SECURITY_RECV] = NVME_CMD_EFF_CSUPP,
> };
>
> static const uint32_t nvme_cse_iocs_nvm_default[256] = {
> @@ -7282,6 +7284,207 @@ static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req)
> return NVME_SUCCESS;
> }
>
> +static uint16_t nvme_sec_prot_spdm_send(NvmeCtrl *n, NvmeRequest *req)
> +{
> + StorageSpdmTransportHeader hdr = {0};
> + g_autofree uint8_t *sec_buf = NULL;
> + uint32_t transfer_len = le32_to_cpu(req->cmd.cdw11);
> + uint32_t transport_transfer_len = transfer_len;
> + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
> + uint32_t recvd;
> + uint16_t nvme_cmd_status, ret;
> + uint8_t secp = extract32(dw10, 24, 8);
> + uint16_t spsp = extract32(dw10, 8, 16);
> + bool spdm_res;
> +
> + if (transport_transfer_len > UINT32_MAX - sizeof(hdr)) {
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> +
> + transport_transfer_len += sizeof(hdr);
> + if (transport_transfer_len > SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE) {
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> +
> + ret = nvme_check_mdts(n, transport_transfer_len);
> + if (ret != NVME_SUCCESS) {
> + return ret;
> + }
> +
> + /* Generate the NVMe transport header */
> + hdr.security_protocol = secp;
> + hdr.security_protocol_specific = cpu_to_le16(spsp);
> + hdr.length = cpu_to_le32(transfer_len);
> +
> + sec_buf = g_try_malloc0(transport_transfer_len);
> + if (!sec_buf) {
> + return NVME_INTERNAL_DEV_ERROR;
> + }
> +
> + /* Attach the transport header */
> + memcpy(sec_buf, &hdr, sizeof(hdr));
> + ret = nvme_h2c(n, sec_buf + sizeof(hdr), transfer_len, req);
> + if (ret) {
> + return ret;
> + }
> +
> + spdm_res = spdm_socket_send(n->spdm_socket, SPDM_SOCKET_STORAGE_CMD_IF_SEND,
> + SPDM_SOCKET_TRANSPORT_TYPE_NVME, sec_buf,
> + transport_transfer_len);
> + if (!spdm_res) {
> + return NVME_DATA_TRAS_ERROR | NVME_DNR;
> + }
> +
> + /* The responder shall ack with message status */
> + recvd = spdm_socket_receive(n->spdm_socket, SPDM_SOCKET_TRANSPORT_TYPE_NVME,
> + &nvme_cmd_status,
> + SPDM_SOCKET_MAX_MSG_STATUS_LEN);
> +
> + nvme_cmd_status = be16_to_cpu(nvme_cmd_status);
> +
> + if (recvd < SPDM_SOCKET_MAX_MSG_STATUS_LEN) {
> + return NVME_DATA_TRAS_ERROR | NVME_DNR;
> + }
> +
> + return nvme_cmd_status;
> +}
> +
> +/* From host to controller */
> +static uint16_t nvme_security_send(NvmeCtrl *n, NvmeRequest *req)
> +{
> + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
> + uint8_t secp = extract32(dw10, 24, 8);
> +
> + switch (secp) {
> + case NVME_SEC_PROT_DMTF_SPDM:
> + if (n->spdm_socket < 0) {
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> + return nvme_sec_prot_spdm_send(n, req);
> + default:
> + /* Unsupported Security Protocol Type */
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> +
> + return NVME_INVALID_FIELD | NVME_DNR;
> +}
> +
> +static uint16_t nvme_sec_prot_spdm_receive(NvmeCtrl *n, NvmeRequest *req)
> +{
> + StorageSpdmTransportHeader hdr;
> + g_autofree uint8_t *rsp_spdm_buf = NULL;
> + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
> + uint32_t alloc_len = le32_to_cpu(req->cmd.cdw11);
> + uint32_t recvd, spdm_res;
> + uint16_t nvme_cmd_status, ret;
> + uint8_t secp = extract32(dw10, 24, 8);
> + uint8_t spsp = extract32(dw10, 8, 16);
> + if (!alloc_len) {
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> +
> + /* Generate the NVMe transport header */
> + hdr = (StorageSpdmTransportHeader) {
> + .security_protocol = secp,
> + .security_protocol_specific = cpu_to_le16(spsp),
> + .length = cpu_to_le32(alloc_len),
> + };
> +
> + /* Forward if_recv to the SPDM Server with SPSP0 */
> + spdm_res = spdm_socket_send(n->spdm_socket, SPDM_SOCKET_STORAGE_CMD_IF_RECV,
> + SPDM_SOCKET_TRANSPORT_TYPE_NVME,
> + &hdr, sizeof(hdr));
> + if (!spdm_res) {
> + return NVME_DATA_TRAS_ERROR | NVME_DNR;
> + }
> +
> + /* The responder shall ack with message status */
> + recvd = spdm_socket_receive(n->spdm_socket, SPDM_SOCKET_TRANSPORT_TYPE_NVME,
> + &nvme_cmd_status,
> + SPDM_SOCKET_MAX_MSG_STATUS_LEN);
> + if (recvd < SPDM_SOCKET_MAX_MSG_STATUS_LEN) {
> + return NVME_DATA_TRAS_ERROR | NVME_DNR;
> + }
> +
> + nvme_cmd_status = be16_to_cpu(nvme_cmd_status);
> + /* An error here implies the prior if_recv from requester was spurious */
> + if (nvme_cmd_status != NVME_SUCCESS) {
> + return nvme_cmd_status;
> + }
> +
> + /* Clear to start receiving data from the server */
> + rsp_spdm_buf = g_try_malloc0(alloc_len);
> + if (!rsp_spdm_buf) {
> + return NVME_INTERNAL_DEV_ERROR;
> + }
> +
> + recvd = spdm_socket_receive(n->spdm_socket,
> + SPDM_SOCKET_TRANSPORT_TYPE_NVME,
> + rsp_spdm_buf, alloc_len);
> + if (!recvd) {
> + return NVME_DATA_TRAS_ERROR | NVME_DNR;
> + }
> +
> + ret = nvme_c2h(n, rsp_spdm_buf, MIN(recvd, alloc_len), req);
> + if (ret) {
> + return ret;
> + }
> +
> + return NVME_SUCCESS;
> +}
> +
> +static uint16_t nvme_get_sec_prot_info(NvmeCtrl *n, NvmeRequest *req)
> +{
> + uint32_t alloc_len = le32_to_cpu(req->cmd.cdw11);
> + uint8_t resp[10] = {
> + /* Support Security Protol List Length */
> + [6] = 0, /* MSB */
> + [7] = 2, /* LSB */
> + /* Support Security Protocol List */
> + [8] = SFSC_SECURITY_PROT_INFO,
> + [9] = 0,
> + };
> +
> + if (n->spdm_socket >= 0) {
> + resp[9] = NVME_SEC_PROT_DMTF_SPDM;
> + }
> +
> + if (alloc_len < 10) {
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> +
> + return nvme_c2h(n, resp, sizeof(resp), req);
> +}
> +
> +/* From controller to host */
> +static uint16_t nvme_security_receive(NvmeCtrl *n, NvmeRequest *req)
> +{
> + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
> + uint16_t spsp = extract32(dw10, 8, 16);
> + uint8_t secp = extract32(dw10, 24, 8);
> +
> + switch (secp) {
> + case SFSC_SECURITY_PROT_INFO:
> + switch (spsp) {
> + case 0:
> + /* Supported security protocol list */
> + return nvme_get_sec_prot_info(n, req);
> + case 1:
> + /* Certificate data */
> + /* fallthrough */
> + default:
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> + case NVME_SEC_PROT_DMTF_SPDM:
> + if (n->spdm_socket < 0) {
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> + return nvme_sec_prot_spdm_receive(n, req);
> + default:
> + return NVME_INVALID_FIELD | NVME_DNR;
> + }
> +}
> +
> static uint16_t nvme_directive_send(NvmeCtrl *n, NvmeRequest *req)
> {
> return NVME_INVALID_FIELD | NVME_DNR;
> @@ -7389,6 +7592,10 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req)
> return nvme_directive_send(n, req);
> case NVME_ADM_CMD_DIRECTIVE_RECV:
> return nvme_directive_receive(n, req);
> + case NVME_ADM_CMD_SECURITY_SEND:
> + return nvme_security_send(n, req);
> + case NVME_ADM_CMD_SECURITY_RECV:
> + return nvme_security_receive(n, req);
> default:
> g_assert_not_reached();
> }
> @@ -8459,6 +8666,8 @@ static void nvme_init_state(NvmeCtrl *n)
> sctrl->vfn = cpu_to_le16(i + 1);
> }
>
> + n->spdm_socket = -1;
> +
> cap->cntlid = cpu_to_le16(n->cntlid);
> cap->crt = NVME_CRT_VQ | NVME_CRT_VI;
>
> @@ -8824,7 +9033,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
> id->mdts = n->params.mdts;
> id->ver = cpu_to_le32(NVME_SPEC_VER);
>
> - oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DIRECTIVES;
> + oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DIRECTIVES |
> + NVME_OACS_SECURITY;
>
> if (n->params.dbcs) {
> oacs |= NVME_OACS_DBCS;
> diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
> index b5c9378ea4..67ed562e00 100644
> --- a/hw/nvme/nvme.h
> +++ b/hw/nvme/nvme.h
> @@ -461,6 +461,8 @@ static inline const char *nvme_adm_opc_str(uint8_t opc)
> case NVME_ADM_CMD_DIRECTIVE_RECV: return "NVME_ADM_CMD_DIRECTIVE_RECV";
> case NVME_ADM_CMD_DBBUF_CONFIG: return "NVME_ADM_CMD_DBBUF_CONFIG";
> case NVME_ADM_CMD_FORMAT_NVM: return "NVME_ADM_CMD_FORMAT_NVM";
> + case NVME_ADM_CMD_SECURITY_SEND: return "NVME_ADM_CMD_SECURITY_SEND";
> + case NVME_ADM_CMD_SECURITY_RECV: return "NVME_ADM_CMD_SECURITY_RECV";
> default: return "NVME_ADM_CMD_UNKNOWN";
> }
> }
> @@ -648,6 +650,9 @@ typedef struct NvmeCtrl {
> } next_pri_ctrl_cap; /* These override pri_ctrl_cap after reset */
> uint32_t dn; /* Disable Normal */
> NvmeAtomic atomic;
> +
> + /* Socket mapping to SPDM over NVMe Security In/Out commands */
> + int spdm_socket;
> } NvmeCtrl;
>
> typedef enum NvmeResetType {
> diff --git a/include/block/nvme.h b/include/block/nvme.h
> index 358e516e38..9fa2ecaf28 100644
> --- a/include/block/nvme.h
> +++ b/include/block/nvme.h
> @@ -1779,6 +1779,21 @@ enum NvmeDirectiveOperations {
> NVME_DIRECTIVE_RETURN_PARAMS = 0x1,
> };
>
> +typedef enum SfscSecurityProtocol {
> + SFSC_SECURITY_PROT_INFO = 0x00,
> +} SfscSecurityProtocol;
> +
> +typedef enum NvmeSecurityProtocols {
> + NVME_SEC_PROT_DMTF_SPDM = 0xE8,
> +} NvmeSecurityProtocols;
> +
> +typedef enum SpdmOperationCodes {
> + SPDM_STORAGE_DISCOVERY = 0x1, /* Mandatory */
> + SPDM_STORAGE_PENDING_INFO = 0x2, /* Optional */
> + SPDM_STORAGE_MSG = 0x5, /* Mandatory */
> + SPDM_STORAGE_SEC_MSG = 0x6, /* Optional */
> +} SpdmOperationCodes;
> +
> typedef struct QEMU_PACKED NvmeFdpConfsHdr {
> uint16_t num_confs;
> uint8_t version;
> diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
> index 80cd7021c1..f0c8b64d1b 100644
> --- a/include/system/spdm-socket.h
> +++ b/include/system/spdm-socket.h
> @@ -112,7 +112,9 @@ typedef struct {
>
> #define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
> #define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
> +#define SPDM_SOCKET_TRANSPORT_TYPE_NVME 0x04
>
> #define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
> +#define SPDM_SOCKET_MAX_MSG_STATUS_LEN 0x02
>
> #endif
> --
> 2.51.0
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v7 4/5] spdm: define SPDM transport enum types
2025-09-12 2:11 [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
` (2 preceding siblings ...)
2025-09-12 2:11 ` [PATCH v7 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
@ 2025-09-12 2:11 ` Wilfred Mallawa
2025-10-03 2:00 ` Alistair Francis
2025-09-12 2:11 ` [PATCH v7 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv Wilfred Mallawa
2025-09-30 23:52 ` [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
5 siblings, 1 reply; 15+ messages in thread
From: Wilfred Mallawa @ 2025-09-12 2:11 UTC (permalink / raw)
To: Alistair Francis
Cc: Keith Busch, Klaus Jensen, Jesper Devantier, Stefan Hajnoczi,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
Jonathan Cameron, Wilfred Mallawa, Jonathan Cameron
From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
SPDM maybe used over different transports. This patch specifies the
trasnport types as an enum with a qdev property definition such that
a user input transport type (string) can be mapped directly into the
respective SPDM transportenum for internal use.
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
backends/spdm-socket.c | 23 +++++++++++++++++++++++
include/system/spdm-socket.h | 19 +++++++++++++++----
2 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
index ab74a02d9c..6d8f02d3b9 100644
--- a/backends/spdm-socket.c
+++ b/backends/spdm-socket.c
@@ -13,6 +13,9 @@
#include "qemu/osdep.h"
#include "system/spdm-socket.h"
#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/core/qdev-prop-internal.h"
static bool read_bytes(const int socket, uint8_t *buffer,
size_t number_of_bytes)
@@ -246,3 +249,23 @@ void spdm_socket_close(const int socket, uint32_t transport_type)
send_platform_data(socket, transport_type,
SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
}
+
+const QEnumLookup SpdmTransport_lookup = {
+ .array = (const char *const[]) {
+ [SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC] = "unspecified",
+ [SPDM_SOCKET_TRANSPORT_TYPE_MCTP] = "mctp",
+ [SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE] = "doe",
+ [SPDM_SOCKET_TRANSPORT_TYPE_SCSI] = "scsi",
+ [SPDM_SOCKET_TRANSPORT_TYPE_NVME] = "nvme",
+ },
+ .size = SPDM_SOCKET_TRANSPORT_TYPE_MAX
+};
+
+const PropertyInfo qdev_prop_spdm_trans = {
+ .type = "SpdmTransportType",
+ .description = "Spdm Transport, doe/nvme/mctp/scsi/unspecified",
+ .enum_table = &SpdmTransport_lookup,
+ .get = qdev_propinfo_get_enum,
+ .set = qdev_propinfo_set_enum,
+ .set_default_value = qdev_propinfo_set_default_value_enum,
+};
diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
index f0c8b64d1b..9d0b85f8cc 100644
--- a/include/system/spdm-socket.h
+++ b/include/system/spdm-socket.h
@@ -110,11 +110,22 @@ typedef struct {
#define SPDM_SOCKET_COMMAND_UNKOWN 0xFFFF
#define SPDM_SOCKET_COMMAND_TEST 0xDEAD
-#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
-#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
-#define SPDM_SOCKET_TRANSPORT_TYPE_NVME 0x04
-
#define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
#define SPDM_SOCKET_MAX_MSG_STATUS_LEN 0x02
+typedef enum SpdmTransportType {
+ SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC = 0,
+ SPDM_SOCKET_TRANSPORT_TYPE_MCTP,
+ SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE,
+ SPDM_SOCKET_TRANSPORT_TYPE_SCSI,
+ SPDM_SOCKET_TRANSPORT_TYPE_NVME,
+ SPDM_SOCKET_TRANSPORT_TYPE_MAX
+} SpdmTransportType;
+
+extern const PropertyInfo qdev_prop_spdm_trans;
+
+#define DEFINE_PROP_SPDM_TRANS(_name, _state, _field, _default) \
+ DEFINE_PROP_UNSIGNED(_name, _state, _field, _default, \
+ qdev_prop_spdm_trans, SpdmTransportType)
+
#endif
--
2.51.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v7 4/5] spdm: define SPDM transport enum types
2025-09-12 2:11 ` [PATCH v7 4/5] spdm: define SPDM transport enum types Wilfred Mallawa
@ 2025-10-03 2:00 ` Alistair Francis
0 siblings, 0 replies; 15+ messages in thread
From: Alistair Francis @ 2025-10-03 2:00 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Klaus Jensen, Jesper Devantier,
Stefan Hajnoczi, Fam Zheng, Philippe Mathieu-Daudé,
Kevin Wolf, Hanna Reitz, Michael S . Tsirkin, Marcel Apfelbaum,
qemu-devel, qemu-block, Jonathan Cameron, Wilfred Mallawa
On Fri, Sep 12, 2025 at 12:13 PM Wilfred Mallawa
<wilfred.opensource@gmail.com> wrote:
>
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> SPDM maybe used over different transports. This patch specifies the
> trasnport types as an enum with a qdev property definition such that
> a user input transport type (string) can be mapped directly into the
> respective SPDM transportenum for internal use.
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> backends/spdm-socket.c | 23 +++++++++++++++++++++++
> include/system/spdm-socket.h | 19 +++++++++++++++----
> 2 files changed, 38 insertions(+), 4 deletions(-)
>
> diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
> index ab74a02d9c..6d8f02d3b9 100644
> --- a/backends/spdm-socket.c
> +++ b/backends/spdm-socket.c
> @@ -13,6 +13,9 @@
> #include "qemu/osdep.h"
> #include "system/spdm-socket.h"
> #include "qapi/error.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/qdev-properties-system.h"
> +#include "hw/core/qdev-prop-internal.h"
>
> static bool read_bytes(const int socket, uint8_t *buffer,
> size_t number_of_bytes)
> @@ -246,3 +249,23 @@ void spdm_socket_close(const int socket, uint32_t transport_type)
> send_platform_data(socket, transport_type,
> SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
> }
> +
> +const QEnumLookup SpdmTransport_lookup = {
> + .array = (const char *const[]) {
> + [SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC] = "unspecified",
> + [SPDM_SOCKET_TRANSPORT_TYPE_MCTP] = "mctp",
> + [SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE] = "doe",
> + [SPDM_SOCKET_TRANSPORT_TYPE_SCSI] = "scsi",
> + [SPDM_SOCKET_TRANSPORT_TYPE_NVME] = "nvme",
> + },
> + .size = SPDM_SOCKET_TRANSPORT_TYPE_MAX
> +};
> +
> +const PropertyInfo qdev_prop_spdm_trans = {
> + .type = "SpdmTransportType",
> + .description = "Spdm Transport, doe/nvme/mctp/scsi/unspecified",
> + .enum_table = &SpdmTransport_lookup,
> + .get = qdev_propinfo_get_enum,
> + .set = qdev_propinfo_set_enum,
> + .set_default_value = qdev_propinfo_set_default_value_enum,
> +};
> diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
> index f0c8b64d1b..9d0b85f8cc 100644
> --- a/include/system/spdm-socket.h
> +++ b/include/system/spdm-socket.h
> @@ -110,11 +110,22 @@ typedef struct {
> #define SPDM_SOCKET_COMMAND_UNKOWN 0xFFFF
> #define SPDM_SOCKET_COMMAND_TEST 0xDEAD
>
> -#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
> -#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
> -#define SPDM_SOCKET_TRANSPORT_TYPE_NVME 0x04
> -
> #define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
> #define SPDM_SOCKET_MAX_MSG_STATUS_LEN 0x02
>
> +typedef enum SpdmTransportType {
> + SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC = 0,
> + SPDM_SOCKET_TRANSPORT_TYPE_MCTP,
> + SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE,
> + SPDM_SOCKET_TRANSPORT_TYPE_SCSI,
> + SPDM_SOCKET_TRANSPORT_TYPE_NVME,
> + SPDM_SOCKET_TRANSPORT_TYPE_MAX
> +} SpdmTransportType;
> +
> +extern const PropertyInfo qdev_prop_spdm_trans;
> +
> +#define DEFINE_PROP_SPDM_TRANS(_name, _state, _field, _default) \
> + DEFINE_PROP_UNSIGNED(_name, _state, _field, _default, \
> + qdev_prop_spdm_trans, SpdmTransportType)
> +
> #endif
> --
> 2.51.0
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v7 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv
2025-09-12 2:11 [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
` (3 preceding siblings ...)
2025-09-12 2:11 ` [PATCH v7 4/5] spdm: define SPDM transport enum types Wilfred Mallawa
@ 2025-09-12 2:11 ` Wilfred Mallawa
2025-10-03 2:04 ` Alistair Francis
2025-09-30 23:52 ` [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
5 siblings, 1 reply; 15+ messages in thread
From: Wilfred Mallawa @ 2025-09-12 2:11 UTC (permalink / raw)
To: Alistair Francis
Cc: Keith Busch, Klaus Jensen, Jesper Devantier, Stefan Hajnoczi,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
Jonathan Cameron, Wilfred Mallawa, Jonathan Cameron, Klaus Jensen
From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
This patch extends the existing support we have for NVMe with only DoE
to also add support to SPDM over the NVMe Security Send/Recv commands.
With the new definition of the `spdm-trans` argument, users can specify
`spdm_trans=nvme` or `spdm_trans=doe`. This allows us to select the SPDM
transport respectively. SPDM over the NVMe Security Send/Recv commands
are defined in the DMTF DSP0286.
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
---
docs/specs/spdm.rst | 10 +++++++--
hw/nvme/ctrl.c | 45 ++++++++++++++++++++++++++++---------
include/hw/pci/pci_device.h | 2 ++
3 files changed, 44 insertions(+), 13 deletions(-)
diff --git a/docs/specs/spdm.rst b/docs/specs/spdm.rst
index f7de080ff0..dd6cfbbd68 100644
--- a/docs/specs/spdm.rst
+++ b/docs/specs/spdm.rst
@@ -98,7 +98,7 @@ Then you can add this to your QEMU command line:
.. code-block:: shell
-drive file=blknvme,if=none,id=mynvme,format=raw \
- -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323
+ -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323,spdm_trans=doe
At which point QEMU will try to connect to the SPDM server.
@@ -113,7 +113,13 @@ of the default. So the entire QEMU command might look like this
-append "root=/dev/vda console=ttyS0" \
-net none -nographic \
-drive file=blknvme,if=none,id=mynvme,format=raw \
- -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323
+ -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323,spdm_trans=doe
+
+The `spdm_trans` argument defines the underlying transport type that is emulated
+by QEMU. For an PCIe NVMe controller, both "doe" and "nvme" are supported. Where,
+"doe" does SPDM transport over the PCIe extended capability Data Object Exchange
+(DOE), and "nvme" uses the NVMe Admin Security Send/Receive commands to
+implement the SPDM transport.
.. _DMTF:
https://www.dmtf.org/standards/SPDM
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index ad52e8f569..7928e9b0a9 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -8947,19 +8947,31 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
pcie_cap_deverr_init(pci_dev);
- /* DOE Initialisation */
+ /* SPDM Initialisation */
if (pci_dev->spdm_port) {
- uint16_t doe_offset = n->params.sriov_max_vfs ?
- PCI_CONFIG_SPACE_SIZE + PCI_ARI_SIZEOF
- : PCI_CONFIG_SPACE_SIZE;
+ switch (pci_dev->spdm_trans) {
+ case SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE:
+ uint16_t doe_offset = n->params.sriov_max_vfs ?
+ PCI_CONFIG_SPACE_SIZE + PCI_ARI_SIZEOF
+ : PCI_CONFIG_SPACE_SIZE;
- pcie_doe_init(pci_dev, &pci_dev->doe_spdm, doe_offset,
- doe_spdm_prot, true, 0);
+ pcie_doe_init(pci_dev, &pci_dev->doe_spdm, doe_offset,
+ doe_spdm_prot, true, 0);
- pci_dev->doe_spdm.spdm_socket = spdm_socket_connect(pci_dev->spdm_port,
- errp);
+ pci_dev->doe_spdm.spdm_socket =
+ spdm_socket_connect(pci_dev->spdm_port, errp);
- if (pci_dev->doe_spdm.spdm_socket < 0) {
+ if (pci_dev->doe_spdm.spdm_socket < 0) {
+ return false;
+ }
+ break;
+ case SPDM_SOCKET_TRANSPORT_TYPE_NVME:
+ n->spdm_socket = spdm_socket_connect(pci_dev->spdm_port, errp);
+ if (n->spdm_socket < 0) {
+ return false;
+ }
+ break;
+ default:
return false;
}
}
@@ -9250,9 +9262,14 @@ static void nvme_exit(PCIDevice *pci_dev)
g_free(n->cmb.buf);
}
+ /* Only one of the `spdm_socket`s below should have been setup */
+ assert(!(pci_dev->doe_spdm.spdm_socket > 0 && n->spdm_socket >= 0));
if (pci_dev->doe_spdm.spdm_socket > 0) {
spdm_socket_close(pci_dev->doe_spdm.spdm_socket,
SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE);
+ } else if (n->spdm_socket >= 0) {
+ spdm_socket_close(pci_dev->doe_spdm.spdm_socket,
+ SPDM_SOCKET_TRANSPORT_TYPE_NVME);
}
if (n->pmr.dev) {
@@ -9307,6 +9324,8 @@ static const Property nvme_props[] = {
false),
DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff),
DEFINE_PROP_UINT16("spdm_port", PCIDevice, spdm_port, 0),
+ DEFINE_PROP_SPDM_TRANS("spdm_trans", PCIDevice, spdm_trans,
+ SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE),
DEFINE_PROP_BOOL("ctratt.mem", NvmeCtrl, params.ctratt.mem, false),
DEFINE_PROP_BOOL("atomic.dn", NvmeCtrl, params.atomic_dn, 0),
DEFINE_PROP_UINT16("atomic.awun", NvmeCtrl, params.atomic_awun, 0),
@@ -9382,7 +9401,9 @@ static void nvme_pci_write_config(PCIDevice *dev, uint32_t address,
{
uint16_t old_num_vfs = pcie_sriov_num_vfs(dev);
- if (pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) {
+ /* DOE is only initialised if SPDM over DOE is used */
+ if (pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE) &&
+ dev->spdm_trans == SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE) {
pcie_doe_write_config(&dev->doe_spdm, address, val, len);
}
pci_default_write_config(dev, address, val, len);
@@ -9393,7 +9414,9 @@ static void nvme_pci_write_config(PCIDevice *dev, uint32_t address,
static uint32_t nvme_pci_read_config(PCIDevice *dev, uint32_t address, int len)
{
uint32_t val;
- if (dev->spdm_port && pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) {
+
+ if (dev->spdm_port && pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE) &&
+ (dev->spdm_trans == SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE)) {
if (pcie_doe_read_config(&dev->doe_spdm, address, len, &val)) {
return val;
}
diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
index eee0338568..88ccea5011 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -4,6 +4,7 @@
#include "hw/pci/pci.h"
#include "hw/pci/pcie.h"
#include "hw/pci/pcie_doe.h"
+#include "system/spdm-socket.h"
#define TYPE_PCI_DEVICE "pci-device"
typedef struct PCIDeviceClass PCIDeviceClass;
@@ -166,6 +167,7 @@ struct PCIDevice {
/* SPDM */
uint16_t spdm_port;
+ SpdmTransportType spdm_trans;
/* DOE */
DOECap doe_spdm;
--
2.51.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v7 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv
2025-09-12 2:11 ` [PATCH v7 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv Wilfred Mallawa
@ 2025-10-03 2:04 ` Alistair Francis
0 siblings, 0 replies; 15+ messages in thread
From: Alistair Francis @ 2025-10-03 2:04 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Klaus Jensen, Jesper Devantier,
Stefan Hajnoczi, Fam Zheng, Philippe Mathieu-Daudé,
Kevin Wolf, Hanna Reitz, Michael S . Tsirkin, Marcel Apfelbaum,
qemu-devel, qemu-block, Jonathan Cameron, Wilfred Mallawa,
Klaus Jensen
On Fri, Sep 12, 2025 at 12:13 PM Wilfred Mallawa
<wilfred.opensource@gmail.com> wrote:
>
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> This patch extends the existing support we have for NVMe with only DoE
> to also add support to SPDM over the NVMe Security Send/Recv commands.
>
> With the new definition of the `spdm-trans` argument, users can specify
> `spdm_trans=nvme` or `spdm_trans=doe`. This allows us to select the SPDM
> transport respectively. SPDM over the NVMe Security Send/Recv commands
> are defined in the DMTF DSP0286.
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> docs/specs/spdm.rst | 10 +++++++--
> hw/nvme/ctrl.c | 45 ++++++++++++++++++++++++++++---------
> include/hw/pci/pci_device.h | 2 ++
> 3 files changed, 44 insertions(+), 13 deletions(-)
>
> diff --git a/docs/specs/spdm.rst b/docs/specs/spdm.rst
> index f7de080ff0..dd6cfbbd68 100644
> --- a/docs/specs/spdm.rst
> +++ b/docs/specs/spdm.rst
> @@ -98,7 +98,7 @@ Then you can add this to your QEMU command line:
> .. code-block:: shell
>
> -drive file=blknvme,if=none,id=mynvme,format=raw \
> - -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323
> + -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323,spdm_trans=doe
>
> At which point QEMU will try to connect to the SPDM server.
>
> @@ -113,7 +113,13 @@ of the default. So the entire QEMU command might look like this
> -append "root=/dev/vda console=ttyS0" \
> -net none -nographic \
> -drive file=blknvme,if=none,id=mynvme,format=raw \
> - -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323
> + -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323,spdm_trans=doe
> +
> +The `spdm_trans` argument defines the underlying transport type that is emulated
> +by QEMU. For an PCIe NVMe controller, both "doe" and "nvme" are supported. Where,
> +"doe" does SPDM transport over the PCIe extended capability Data Object Exchange
> +(DOE), and "nvme" uses the NVMe Admin Security Send/Receive commands to
> +implement the SPDM transport.
>
> .. _DMTF:
> https://www.dmtf.org/standards/SPDM
> diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
> index ad52e8f569..7928e9b0a9 100644
> --- a/hw/nvme/ctrl.c
> +++ b/hw/nvme/ctrl.c
> @@ -8947,19 +8947,31 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
>
> pcie_cap_deverr_init(pci_dev);
>
> - /* DOE Initialisation */
> + /* SPDM Initialisation */
> if (pci_dev->spdm_port) {
> - uint16_t doe_offset = n->params.sriov_max_vfs ?
> - PCI_CONFIG_SPACE_SIZE + PCI_ARI_SIZEOF
> - : PCI_CONFIG_SPACE_SIZE;
> + switch (pci_dev->spdm_trans) {
> + case SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE:
> + uint16_t doe_offset = n->params.sriov_max_vfs ?
> + PCI_CONFIG_SPACE_SIZE + PCI_ARI_SIZEOF
> + : PCI_CONFIG_SPACE_SIZE;
>
> - pcie_doe_init(pci_dev, &pci_dev->doe_spdm, doe_offset,
> - doe_spdm_prot, true, 0);
> + pcie_doe_init(pci_dev, &pci_dev->doe_spdm, doe_offset,
> + doe_spdm_prot, true, 0);
>
> - pci_dev->doe_spdm.spdm_socket = spdm_socket_connect(pci_dev->spdm_port,
> - errp);
> + pci_dev->doe_spdm.spdm_socket =
> + spdm_socket_connect(pci_dev->spdm_port, errp);
>
> - if (pci_dev->doe_spdm.spdm_socket < 0) {
> + if (pci_dev->doe_spdm.spdm_socket < 0) {
> + return false;
> + }
> + break;
> + case SPDM_SOCKET_TRANSPORT_TYPE_NVME:
> + n->spdm_socket = spdm_socket_connect(pci_dev->spdm_port, errp);
> + if (n->spdm_socket < 0) {
> + return false;
> + }
> + break;
> + default:
> return false;
> }
> }
> @@ -9250,9 +9262,14 @@ static void nvme_exit(PCIDevice *pci_dev)
> g_free(n->cmb.buf);
> }
>
> + /* Only one of the `spdm_socket`s below should have been setup */
> + assert(!(pci_dev->doe_spdm.spdm_socket > 0 && n->spdm_socket >= 0));
> if (pci_dev->doe_spdm.spdm_socket > 0) {
> spdm_socket_close(pci_dev->doe_spdm.spdm_socket,
> SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE);
> + } else if (n->spdm_socket >= 0) {
> + spdm_socket_close(pci_dev->doe_spdm.spdm_socket,
> + SPDM_SOCKET_TRANSPORT_TYPE_NVME);
> }
>
> if (n->pmr.dev) {
> @@ -9307,6 +9324,8 @@ static const Property nvme_props[] = {
> false),
> DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff),
> DEFINE_PROP_UINT16("spdm_port", PCIDevice, spdm_port, 0),
> + DEFINE_PROP_SPDM_TRANS("spdm_trans", PCIDevice, spdm_trans,
> + SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE),
> DEFINE_PROP_BOOL("ctratt.mem", NvmeCtrl, params.ctratt.mem, false),
> DEFINE_PROP_BOOL("atomic.dn", NvmeCtrl, params.atomic_dn, 0),
> DEFINE_PROP_UINT16("atomic.awun", NvmeCtrl, params.atomic_awun, 0),
> @@ -9382,7 +9401,9 @@ static void nvme_pci_write_config(PCIDevice *dev, uint32_t address,
> {
> uint16_t old_num_vfs = pcie_sriov_num_vfs(dev);
>
> - if (pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) {
> + /* DOE is only initialised if SPDM over DOE is used */
> + if (pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE) &&
> + dev->spdm_trans == SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE) {
> pcie_doe_write_config(&dev->doe_spdm, address, val, len);
> }
> pci_default_write_config(dev, address, val, len);
> @@ -9393,7 +9414,9 @@ static void nvme_pci_write_config(PCIDevice *dev, uint32_t address,
> static uint32_t nvme_pci_read_config(PCIDevice *dev, uint32_t address, int len)
> {
> uint32_t val;
> - if (dev->spdm_port && pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) {
> +
> + if (dev->spdm_port && pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE) &&
> + (dev->spdm_trans == SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE)) {
> if (pcie_doe_read_config(&dev->doe_spdm, address, len, &val)) {
> return val;
> }
> diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
> index eee0338568..88ccea5011 100644
> --- a/include/hw/pci/pci_device.h
> +++ b/include/hw/pci/pci_device.h
> @@ -4,6 +4,7 @@
> #include "hw/pci/pci.h"
> #include "hw/pci/pcie.h"
> #include "hw/pci/pcie_doe.h"
> +#include "system/spdm-socket.h"
>
> #define TYPE_PCI_DEVICE "pci-device"
> typedef struct PCIDeviceClass PCIDeviceClass;
> @@ -166,6 +167,7 @@ struct PCIDevice {
>
> /* SPDM */
> uint16_t spdm_port;
> + SpdmTransportType spdm_trans;
>
> /* DOE */
> DOECap doe_spdm;
> --
> 2.51.0
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support
2025-09-12 2:11 [PATCH v7 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
` (4 preceding siblings ...)
2025-09-12 2:11 ` [PATCH v7 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv Wilfred Mallawa
@ 2025-09-30 23:52 ` Wilfred Mallawa
5 siblings, 0 replies; 15+ messages in thread
From: Wilfred Mallawa @ 2025-09-30 23:52 UTC (permalink / raw)
To: Alistair Francis
Cc: its@irrelevant.dk, hreitz@redhat.com, philmd@linaro.org,
qemu-devel@nongnu.org, stefanha@redhat.com, fam@euphon.net,
qemu-block@nongnu.org, foss@defmacro.it, kwolf@redhat.com,
mst@redhat.com, Jonathan.Cameron@huawei.com, kbusch@kernel.org,
marcel.apfelbaum@gmail.com
Gentle ping :)
On Fri, 2025-09-12 at 12:11 +1000, Wilfred Mallawa wrote:
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> This series extends the existing SPDM support in QEMU to support the
> DSP0286
> SPDM Storage Transport [1] for NVMe. SPDM Storage Transport uses the
> NVMe
> Admin Security Send/Receive commands, as such, support for these
> commands have
> also been added.
>
> With the addition of a new `spdm-trans` CLI argument for NVMe
> controllers,
> users can specify `spdm_trans=nvme` or `spdm_trans=doe`. This allows
> for the
> selection of the SPDM transport. The `doe` option is the current
> default,
> `nvme` would select SPDM Storage Transport for the controller, where
> SPDM
> communication happens over the NVMe Admin Security Send/Receive
> commands.
>
> Support for DSP0286 already exists in `libspdm` [2] and support for
> the QEMU
> SPDM server is being upstreamed for `spdm-utils` [3]. This series was
> tested by
> using `spdm-utils` as the qemu SPDM server with SPDM Storage
> Transport support
> built with `libspdm` v3.8.0, and `spdm-utils` also as the SPDM
> requester.
>
> Changes V1 -> V2:
> - spdm_socket_rsp() now uses the new
> spdm_socket_send/receive()
> functions. spdm_socket_command_valid() is added to parse
> the
> command value incase some bytes were received (result =
> true) but
> with an invalid command.
>
> - Added inline comments to describe fields of
> StorageSpdmTransportHeader. Checkpatch generates warnings,
> but lots of
> existing code does this. The QEMU_PACKED attribute now
> follows the
> StorageSpdmTransportHeader struct definition.
>
> - Use extract32() instead of manual shifting/masking in
> nvme_sec_prot_spdm_send/recv().
>
> - Use g_autofree for send/recv buffer allocation
> in nvme_sec_prot_spdm_send/recv().
>
> - Added explicit fallthrough comment for checking `secp` in
> nvme_security_receive()
>
> - Added enum support for SPDM transport type, such that a
> user defined
> transport type string, can be mapped to the respective enum
> for
> internal use.
>
> Changes V2 -> V3:
> - Fixed up the incorrect use of `NVME_NO_COMPLETE` to more
> appropriate
> NVMe error codes in Patch [3/5]. Note that DSP0286 does not
> define
> error codes for transport level failures.
>
> - Removed NULL check for g_malloc0(). Should abort instead.
>
> Changes V3 -> V4:
> - Added integer overflow and MDTS checking for spdm_sends
> - Use g_try_malloc0() over g_malloc0()
> - Fixed up endian conversion for command status received from
> the server.
> - Added check to only accept SPDM send/receive if the socket
> has been setup.
> - Only show SPDM as a supported protocol if the socket
> has been setup.
>
> Changes V4 -> V5:
> - Init spdm_socket fd to -1 for NVMe. Allow 0 to be a valid file
> descriptor
> for the socket.
> - Move transport definitions to the patches they are used in.
> - Avoid splitting SPSP0/SPSP1. Use a uint16 instead.
> - Fixup up incorrect (uint8_t *) casting in calls to
> spdm_socket_receive/send().
> - Default to SPDM over DoE if transport is not specified.
> - Fixup alignment (style).
>
> Changes V5 -> V6:
> - Minor comment style fixup for the description of
> StorageSpdmTransportHeader
> - Change spdm_socket_rsp() to directly return
> spdm_socket_receive()
>
> Changes V6 -> V7:
> - Added an assert() to check that only one spdm socket was setup
> in
> nvme_exit().
> - Merged spdm_socket_close() calls into an else if for DoE/NVMe.
>
> Wilfred Mallawa (5):
> spdm-socket: add seperate send/recv functions
> spdm: add spdm storage transport virtual header
> hw/nvme: add NVMe Admin Security SPDM support
> spdm: define SPDM transport enum types
> hw/nvme: connect SPDM over NVMe Security Send/Recv
>
> backends/spdm-socket.c | 79 +++++++++--
> docs/specs/spdm.rst | 10 +-
> hw/nvme/ctrl.c | 257
> +++++++++++++++++++++++++++++++++--
> hw/nvme/nvme.h | 5 +
> include/block/nvme.h | 15 ++
> include/hw/pci/pci_device.h | 2 +
> include/system/spdm-socket.h | 63 ++++++++-
> 7 files changed, 402 insertions(+), 29 deletions(-)
^ permalink raw reply [flat|nested] 15+ messages in thread