* [PATCH v4 0/5] NVMe: Add SPDM over the storage transport support
@ 2025-09-04 3:10 Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
` (4 more replies)
0 siblings, 5 replies; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-04 3:10 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,
dlemoal, Jonathan Cameron, Wilfred Mallawa
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.
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 | 258 +++++++++++++++++++++++++++++++++--
hw/nvme/nvme.h | 5 +
include/block/nvme.h | 15 ++
include/hw/pci/pci_device.h | 2 +
include/system/spdm-socket.h | 66 ++++++++-
7 files changed, 407 insertions(+), 28 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4 1/5] spdm-socket: add seperate send/recv functions
2025-09-04 3:10 [PATCH v4 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
@ 2025-09-04 3:10 ` Wilfred Mallawa
2025-09-04 10:10 ` Jonathan Cameron via
2025-09-04 3:10 ` [PATCH v4 2/5] spdm: add spdm storage transport virtual header Wilfred Mallawa
` (3 subsequent siblings)
4 siblings, 1 reply; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-04 3:10 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,
dlemoal, Jonathan Cameron, Wilfred Mallawa
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>
---
backends/spdm-socket.c | 56 +++++++++++++++++++++++++++++-------
include/system/spdm-socket.h | 35 ++++++++++++++++++++++
2 files changed, 80 insertions(+), 11 deletions(-)
diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
index 2c709c68c8..3d264814df 100644
--- a/backends/spdm-socket.c
+++ b/backends/spdm-socket.c
@@ -184,28 +184,62 @@ 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);
-
+ rsp_len = spdm_socket_receive(socket, transport_type, (uint8_t *)rsp,
+ rsp_len);
return rsp_len;
}
diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
index 5d8bd9aa4e..2b7d03f82d 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
@@ -68,7 +100,10 @@ void spdm_socket_close(const int socket, uint32_t transport_type);
#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
+#define SPDM_SOCKET_TRANSPORT_TYPE_SCSI 0x03
+#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] 16+ messages in thread
* [PATCH v4 2/5] spdm: add spdm storage transport virtual header
2025-09-04 3:10 [PATCH v4 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
@ 2025-09-04 3:10 ` Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
` (2 subsequent siblings)
4 siblings, 0 replies; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-04 3:10 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,
dlemoal, 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 2b7d03f82d..6c2cb7b926 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] 16+ messages in thread
* [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-04 3:10 [PATCH v4 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 2/5] spdm: add spdm storage transport virtual header Wilfred Mallawa
@ 2025-09-04 3:10 ` Wilfred Mallawa
2025-09-04 10:22 ` Jonathan Cameron via
` (2 more replies)
2025-09-04 3:10 ` [PATCH v4 4/5] spdm: define SPDM transport enum types Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv Wilfred Mallawa
4 siblings, 3 replies; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-04 3:10 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,
dlemoal, Jonathan Cameron, Wilfred Mallawa
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>
---
hw/nvme/ctrl.c | 213 ++++++++++++++++++++++++++++++++++++++++++-
hw/nvme/nvme.h | 5 +
include/block/nvme.h | 15 +++
3 files changed, 232 insertions(+), 1 deletion(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index f5ee6bf260..df72599bcc 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,210 @@ 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);
+ uint8_t spsp1 = extract32(dw10, 16, 8);
+ uint8_t spsp0 = extract32(dw10, 8, 8);
+ 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((spsp1 << 8) | spsp0);
+ 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,
+ (uint8_t *)&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 = (dw10 >> 24) & 0xff;
+
+ 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 spsp1 = extract32(dw10, 16, 8);
+ uint8_t spsp0 = extract32(dw10, 8, 8);
+
+ 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((spsp1 << 8) | spsp0),
+ .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,
+ (uint8_t *)&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,
+ (uint8_t *)&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 +7595,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();
}
@@ -8824,7 +9034,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;
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v4 4/5] spdm: define SPDM transport enum types
2025-09-04 3:10 [PATCH v4 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
` (2 preceding siblings ...)
2025-09-04 3:10 ` [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
@ 2025-09-04 3:10 ` Wilfred Mallawa
2025-09-04 10:24 ` Jonathan Cameron via
2025-09-04 3:10 ` [PATCH v4 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv Wilfred Mallawa
4 siblings, 1 reply; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-04 3:10 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,
dlemoal, Jonathan Cameron, Wilfred Mallawa
From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
SPDM maybe used over different transports, such as PCIe Data Object
Exchange (DoE) or Storage amongst others. This patch
specifies such 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 transport enum for internal use.
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
---
backends/spdm-socket.c | 23 +++++++++++++++++++++++
include/system/spdm-socket.h | 23 ++++++++++++++++++-----
2 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
index 3d264814df..6943246372 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)
@@ -248,3 +251,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 6c2cb7b926..8fb5f7cf40 100644
--- a/include/system/spdm-socket.h
+++ b/include/system/spdm-socket.h
@@ -110,12 +110,25 @@ 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_SCSI 0x03
-#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)
+#define DEFINE_PROP_SPDM_TRANS_NODEFAULT(_name, _state, _field) \
+ DEFINE_PROP_SPDM_TRANS(_name, _state, _field, \
+ SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC)
+
#endif
--
2.51.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v4 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv
2025-09-04 3:10 [PATCH v4 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
` (3 preceding siblings ...)
2025-09-04 3:10 ` [PATCH v4 4/5] spdm: define SPDM transport enum types Wilfred Mallawa
@ 2025-09-04 3:10 ` Wilfred Mallawa
2025-09-04 10:31 ` Jonathan Cameron via
4 siblings, 1 reply; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-04 3:10 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,
dlemoal, Jonathan Cameron, Wilfred Mallawa
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>
---
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 df72599bcc..e485e0584e 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -8948,19 +8948,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;
}
}
@@ -9251,11 +9263,17 @@ static void nvme_exit(PCIDevice *pci_dev)
g_free(n->cmb.buf);
}
+ /* Only one of the `spdm_socket` below would have been setup */
if (pci_dev->doe_spdm.spdm_socket > 0) {
spdm_socket_close(pci_dev->doe_spdm.spdm_socket,
SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE);
}
+ if (n->spdm_socket > 0) {
+ spdm_socket_close(pci_dev->doe_spdm.spdm_socket,
+ SPDM_SOCKET_TRANSPORT_TYPE_NVME);
+ }
+
if (n->pmr.dev) {
host_memory_backend_set_mapped(n->pmr.dev, false);
}
@@ -9308,6 +9326,7 @@ 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_NODEFAULT("spdm_trans", PCIDevice, spdm_trans),
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),
@@ -9383,7 +9402,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);
@@ -9394,7 +9415,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] 16+ messages in thread
* Re: [PATCH v4 1/5] spdm-socket: add seperate send/recv functions
2025-09-04 3:10 ` [PATCH v4 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
@ 2025-09-04 10:10 ` Jonathan Cameron via
2025-09-09 0:41 ` Wilfred Mallawa
0 siblings, 1 reply; 16+ messages in thread
From: Jonathan Cameron via @ 2025-09-04 10:10 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, dlemoal, Wilfred Mallawa
On Thu, 4 Sep 2025 13:10:55 +1000
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>
Hi Wilfred,
Main comments are focused on patch break up and a few definitions that
are here but not used yet. Move those to the patch that first uses them.
Jonathan
> ---
> backends/spdm-socket.c | 56 +++++++++++++++++++++++++++++-------
> include/system/spdm-socket.h | 35 ++++++++++++++++++++++
> 2 files changed, 80 insertions(+), 11 deletions(-)
>
> diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
> index 2c709c68c8..3d264814df 100644
> --- a/backends/spdm-socket.c
> +++ b/backends/spdm-socket.c
> @@ -184,28 +184,62 @@ 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)
As below - perhaps this sanity check belongs in a precursor patch?
> +{
> + 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)) {
Is this change related to the separate send/recv part? Perhaps it
is a useful bit of hardening to do as a precursor patch?
> 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);
I'd wrap that closer to 80 chars.
> +}
> +
> +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);
> -
> + rsp_len = spdm_socket_receive(socket, transport_type, (uint8_t *)rsp,
Why casting to a uint8_t * ? It is a void * and this function takes a void *.
> + rsp_len);
> return rsp_len;
> }
>
> diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
> index 5d8bd9aa4e..2b7d03f82d 100644
> --- a/include/system/spdm-socket.h
> +++ b/include/system/spdm-socket.h
> /**
> * 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
> @@ -68,7 +100,10 @@ void spdm_socket_close(const int socket, uint32_t transport_type);
>
> #define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
> #define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
> +#define SPDM_SOCKET_TRANSPORT_TYPE_SCSI 0x03
> +#define SPDM_SOCKET_TRANSPORT_TYPE_NVME 0x04
Not used in this patch. Move it to where it is first used.
>
> #define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
> +#define SPDM_SOCKET_MAX_MSG_STATUS_LEN 0x02
Not used in this patch.
>
> #endif
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-04 3:10 ` [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
@ 2025-09-04 10:22 ` Jonathan Cameron via
2025-09-09 1:16 ` Wilfred Mallawa
2025-09-04 19:47 ` Stefan Hajnoczi
2025-09-04 19:50 ` Stefan Hajnoczi
2 siblings, 1 reply; 16+ messages in thread
From: Jonathan Cameron via @ 2025-09-04 10:22 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, dlemoal, Wilfred Mallawa
On Thu, 4 Sep 2025 13:10:57 +1000
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>
Hi Wilfred,
I haven't even opened the nvme spec on basis others are covering that
part well. So this is just a review based on the code in this patch
so mostly style stuff.
Jonathan
> ---
> hw/nvme/ctrl.c | 213 ++++++++++++++++++++++++++++++++++++++++++-
> hw/nvme/nvme.h | 5 +
> include/block/nvme.h | 15 +++
> 3 files changed, 232 insertions(+), 1 deletion(-)
>
> diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
> index f5ee6bf260..df72599bcc 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,
Maybe it's an email thing but that alignment of = looks off by one space.
> };
>
> static const uint32_t nvme_cse_iocs_nvm_default[256] = {
> @@ -7282,6 +7284,210 @@ 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);
> + uint8_t spsp1 = extract32(dw10, 16, 8);
> + uint8_t spsp0 = extract32(dw10, 8, 8);
See below. 16 bit field seems more logical to me.
> + 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((spsp1 << 8) | spsp0);
> + 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,
> + (uint8_t *)&nvme_cmd_status,
As in earlier patch, spdm_socket_receive() seems to take a void * so no cast
should be needed and definitely not to a uint8_t *!
> + 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 = (dw10 >> 24) & 0xff;
Used extract32() below. Why not for this one?
> +
> + 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 spsp1 = extract32(dw10, 16, 8);
> + uint8_t spsp0 = extract32(dw10, 8, 8);
This is a little odd. You break out two 8 bit fields here just
to combine them again below. Why not a 16bit field?
If its about spec alignment maybe call that field spsp0_1
> +
> + 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((spsp1 << 8) | spsp0),
> + .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,
> + (uint8_t *)&hdr, sizeof(hdr));
As above.
> + 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,
> + (uint8_t *)&nvme_cmd_status,
As above - seems to be a spurious cast.
> + SPDM_SOCKET_MAX_MSG_STATUS_LEN);
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 4/5] spdm: define SPDM transport enum types
2025-09-04 3:10 ` [PATCH v4 4/5] spdm: define SPDM transport enum types Wilfred Mallawa
@ 2025-09-04 10:24 ` Jonathan Cameron via
0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron via @ 2025-09-04 10:24 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, dlemoal, Wilfred Mallawa
On Thu, 4 Sep 2025 13:10:58 +1000
Wilfred Mallawa <wilfred.opensource@gmail.com> wrote:
> From: Wilfred Mallawa <wilfred.mallawa@wdc.com>
>
> SPDM maybe used over different transports, such as PCIe Data Object
> Exchange (DoE) or Storage amongst others. This patch
Odd line wrap. I'd also drop the 'amongst others' as 'such as' already
suggests there are others so t those extra words add no meaning.
> specifies such 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 transport enum for internal use.
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Trivial comment below.
> diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h
> index 6c2cb7b926..8fb5f7cf40 100644
> --- a/include/system/spdm-socket.h
> +++ b/include/system/spdm-socket.h
> @@ -110,12 +110,25 @@ 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_SCSI 0x03
> -#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,
Given it will always be last element and I assume isn't a spec thing as such,
but just a useful terminating entry, I'd drop that trailing comma.
> +} 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)
> +#define DEFINE_PROP_SPDM_TRANS_NODEFAULT(_name, _state, _field) \
> + DEFINE_PROP_SPDM_TRANS(_name, _state, _field, \
> + SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC)
> +
> #endif
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv
2025-09-04 3:10 ` [PATCH v4 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv Wilfred Mallawa
@ 2025-09-04 10:31 ` Jonathan Cameron via
2025-09-09 1:38 ` Wilfred Mallawa
0 siblings, 1 reply; 16+ messages in thread
From: Jonathan Cameron via @ 2025-09-04 10:31 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, dlemoal, Wilfred Mallawa
On Thu, 4 Sep 2025 13:10:59 +1000
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.
Question on lack of default inline.
Jonathan
>
> Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.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
I wonder if, for command line backwards compatibility we should have a default
of doe if no spdm_trans parameter is provided?
> +
> +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 df72599bcc..e485e0584e 100644
> --- a/hw/nvme/ctrl.c
> +++ b/hw/nvme/ctrl.c
> @@ -9308,6 +9326,7 @@ 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_NODEFAULT("spdm_trans", PCIDevice, spdm_trans),
As above. I think a default is appropriate here.
> 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),
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-04 3:10 ` [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
2025-09-04 10:22 ` Jonathan Cameron via
@ 2025-09-04 19:47 ` Stefan Hajnoczi
2025-09-04 19:50 ` Stefan Hajnoczi
2 siblings, 0 replies; 16+ messages in thread
From: Stefan Hajnoczi @ 2025-09-04 19:47 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Klaus Jensen, Jesper Devantier,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
dlemoal, Jonathan Cameron, Wilfred Mallawa
[-- Attachment #1: Type: text/plain, Size: 771 bytes --]
On Thu, Sep 04, 2025 at 01:10:57PM +1000, Wilfred Mallawa wrote:
> +/* 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 = (dw10 >> 24) & 0xff;
> +
> + switch (secp) {
> + case NVME_SEC_PROT_DMTF_SPDM:
> + if (n->spdm_socket <= 0) {
0 is a valid file descriptor number. There are additional instances in
this patch series where 0 is treated as an error.
Please initialize the spdm_socket field to -1 and update the if
statements. Although you can probably get away with relying on never
getting fd 0 here in practice, making this assumption makes the code odd
because the reader doesn't know if this is a bug or intentional.
Stefan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-04 3:10 ` [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
2025-09-04 10:22 ` Jonathan Cameron via
2025-09-04 19:47 ` Stefan Hajnoczi
@ 2025-09-04 19:50 ` Stefan Hajnoczi
2025-09-09 4:31 ` Wilfred Mallawa
2 siblings, 1 reply; 16+ messages in thread
From: Stefan Hajnoczi @ 2025-09-04 19:50 UTC (permalink / raw)
To: Wilfred Mallawa
Cc: Alistair Francis, Keith Busch, Klaus Jensen, Jesper Devantier,
Fam Zheng, Philippe Mathieu-Daudé, Kevin Wolf, Hanna Reitz,
Michael S . Tsirkin, Marcel Apfelbaum, qemu-devel, qemu-block,
dlemoal, Jonathan Cameron, Wilfred Mallawa
[-- Attachment #1: Type: text/plain, Size: 752 bytes --]
On Thu, Sep 04, 2025 at 01:10:57PM +1000, Wilfred Mallawa 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>
> ---
> hw/nvme/ctrl.c | 213 ++++++++++++++++++++++++++++++++++++++++++-
> hw/nvme/nvme.h | 5 +
> include/block/nvme.h | 15 +++
> 3 files changed, 232 insertions(+), 1 deletion(-)
Aside from my comment about spdm_socket fd numbers:
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
I did not review the NVMe or SPDM specifics, just general device
emulation coding aspects.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 1/5] spdm-socket: add seperate send/recv functions
2025-09-04 10:10 ` Jonathan Cameron via
@ 2025-09-09 0:41 ` Wilfred Mallawa
0 siblings, 0 replies; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-09 0:41 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, dlemoal@kernel.org
On Thu, 2025-09-04 at 11:10 +0100, Jonathan Cameron wrote:
> >
[...]
> > diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c
> > index 2c709c68c8..3d264814df 100644
> > --- a/backends/spdm-socket.c
> > +++ b/backends/spdm-socket.c
> > @@ -184,28 +184,62 @@ 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)
> As below - perhaps this sanity check belongs in a precursor patch?
> > +{
> > + 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)) {
>
> Is this change related to the separate send/recv part? Perhaps it
> is a useful bit of hardening to do as a precursor patch?
Hey Jonathan,
I think it makes sense to integrate this directly into
spdm_socket_receive() in this patch. As in a precursor patch, it would
need to be changed here again.
>
> > 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);
>
> I'd wrap that closer to 80 chars.
Ah yep!
>
> > +}
> > +
> > +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);
> > -
> > + rsp_len = spdm_socket_receive(socket, transport_type, (uint8_t
> > *)rsp,
>
> Why casting to a uint8_t * ? It is a void * and this function takes
> a void *.
Okay yeah, will fixup.
>
> > + rsp_len);
> > return rsp_len;
> > }
> >
> > diff --git a/include/system/spdm-socket.h b/include/system/spdm-
> > socket.h
> > index 5d8bd9aa4e..2b7d03f82d 100644
> > --- a/include/system/spdm-socket.h
> > +++ b/include/system/spdm-socket.h
>
> > /**
> > * 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
> > @@ -68,7 +100,10 @@ void spdm_socket_close(const int socket,
> > uint32_t transport_type);
> >
> > #define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
> > #define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
> > +#define SPDM_SOCKET_TRANSPORT_TYPE_SCSI 0x03
> > +#define SPDM_SOCKET_TRANSPORT_TYPE_NVME 0x04
>
> Not used in this patch. Move it to where it is first used.
>
> >
> > #define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
> > +#define SPDM_SOCKET_MAX_MSG_STATUS_LEN 0x02
>
> Not used in this patch.
Sounds good, I will move them up.
Thanks!
Wilfred
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-04 10:22 ` Jonathan Cameron via
@ 2025-09-09 1:16 ` Wilfred Mallawa
0 siblings, 0 replies; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-09 1:16 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, dlemoal@kernel.org
On Thu, 2025-09-04 at 11:22 +0100, Jonathan Cameron wrote:
> On Thu, 4 Sep 2025 13:10:57 +1000
> 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>
> Hi Wilfred,
>
> I haven't even opened the nvme spec on basis others are covering that
> part well. So this is just a review based on the code in this patch
> so mostly style stuff.
>
> Jonathan
>
Hey Jonathan,
Sounds good! Thanks for the review. Comments inline.
Regards,
Wilfred
> > ---
> > hw/nvme/ctrl.c | 213
> > ++++++++++++++++++++++++++++++++++++++++++-
> > hw/nvme/nvme.h | 5 +
> > include/block/nvme.h | 15 +++
> > 3 files changed, 232 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
> > index f5ee6bf260..df72599bcc 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,
>
> Maybe it's an email thing but that alignment of = looks off by one
> space.
Not email, will fix!
>
> > };
> >
> > static const uint32_t nvme_cse_iocs_nvm_default[256] = {
> > @@ -7282,6 +7284,210 @@ 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);
> > + uint8_t spsp1 = extract32(dw10, 16, 8);
> > + uint8_t spsp0 = extract32(dw10, 8, 8);
>
> See below. 16 bit field seems more logical to me.
>
> > + 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((spsp1 << 8) |
> > spsp0);
> > + 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,
> > + (uint8_t *)&nvme_cmd_status,
>
> As in earlier patch, spdm_socket_receive() seems to take a void * so
> no cast
> should be needed and definitely not to a uint8_t *!
Ah yeah, will fixup.
>
> > + 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 = (dw10 >> 24) & 0xff;
>
> Used extract32() below. Why not for this one?
Missed it! Will fixup :).
>
> > +
> > + 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 spsp1 = extract32(dw10, 16, 8);
> > + uint8_t spsp0 = extract32(dw10, 8, 8);
>
> This is a little odd. You break out two 8 bit fields here just
> to combine them again below. Why not a 16bit field?
> If its about spec alignment maybe call that field spsp0_1
>
Ah yeah, I think I split it out from when DSP0286 was WIP and it seemed
like we may need to parse SPSP here. But doesn't seem to be case, at-
least for now.
>
> > +
> > + 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((spsp1 << 8) |
> > spsp0),
> > + .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,
> > + (uint8_t *)&hdr, sizeof(hdr));
>
> As above.
>
> > + 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,
> > + (uint8_t *)&nvme_cmd_status,
>
> As above - seems to be a spurious cast.
>
Will fixup for next version.
Cheers,
Wilfred
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv
2025-09-04 10:31 ` Jonathan Cameron via
@ 2025-09-09 1:38 ` Wilfred Mallawa
0 siblings, 0 replies; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-09 1:38 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, dlemoal@kernel.org
On Thu, 2025-09-04 at 11:31 +0100, Jonathan Cameron wrote:
[...]
>
> > 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
>
> I wonder if, for command line backwards compatibility we should have
> a default
> of doe if no spdm_trans parameter is provided?
Yeah good point, I will add that.
Regards,
Wilfred
>
> > +
> > +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 df72599bcc..e485e0584e 100644
> > --- a/hw/nvme/ctrl.c
> > +++ b/hw/nvme/ctrl.c
>
> > @@ -9308,6 +9326,7 @@ 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_NODEFAULT("spdm_trans", PCIDevice,
> > spdm_trans),
>
> As above. I think a default is appropriate here.
>
> > 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),
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support
2025-09-04 19:50 ` Stefan Hajnoczi
@ 2025-09-09 4:31 ` Wilfred Mallawa
0 siblings, 0 replies; 16+ messages in thread
From: Wilfred Mallawa @ 2025-09-09 4:31 UTC (permalink / raw)
To: stefanha@redhat.com
Cc: its@irrelevant.dk, hreitz@redhat.com, Alistair Francis,
philmd@linaro.org, marcel.apfelbaum@gmail.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,
dlemoal@kernel.org
On Thu, 2025-09-04 at 21:50 +0200, Stefan Hajnoczi wrote:
> On Thu, Sep 04, 2025 at 01:10:57PM +1000, Wilfred Mallawa 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>
> > ---
> > hw/nvme/ctrl.c | 213
> > ++++++++++++++++++++++++++++++++++++++++++-
> > hw/nvme/nvme.h | 5 +
> > include/block/nvme.h | 15 +++
> > 3 files changed, 232 insertions(+), 1 deletion(-)
>
> Aside from my comment about spdm_socket fd numbers:
>
> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
>
> I did not review the NVMe or SPDM specifics, just general device
> emulation coding aspects.
Hey Stefan,
Thanks for the review! I have now addressed this for V5.
Regards,
Wilfred
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2025-09-09 4:32 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-04 3:10 [PATCH v4 0/5] NVMe: Add SPDM over the storage transport support Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 1/5] spdm-socket: add seperate send/recv functions Wilfred Mallawa
2025-09-04 10:10 ` Jonathan Cameron via
2025-09-09 0:41 ` Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 2/5] spdm: add spdm storage transport virtual header Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 3/5] hw/nvme: add NVMe Admin Security SPDM support Wilfred Mallawa
2025-09-04 10:22 ` Jonathan Cameron via
2025-09-09 1:16 ` Wilfred Mallawa
2025-09-04 19:47 ` Stefan Hajnoczi
2025-09-04 19:50 ` Stefan Hajnoczi
2025-09-09 4:31 ` Wilfred Mallawa
2025-09-04 3:10 ` [PATCH v4 4/5] spdm: define SPDM transport enum types Wilfred Mallawa
2025-09-04 10:24 ` Jonathan Cameron via
2025-09-04 3:10 ` [PATCH v4 5/5] hw/nvme: connect SPDM over NVMe Security Send/Recv Wilfred Mallawa
2025-09-04 10:31 ` Jonathan Cameron via
2025-09-09 1:38 ` Wilfred Mallawa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).