mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
hw/nvme: add NVMe Admin Security SPDM support
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> Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
This commit is contained in:
committed by
Klaus Jensen
parent
64a9336a41
commit
e5534abeb4
+211
-1
@@ -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;
|
||||
|
||||
@@ -8820,7 +9029,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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -114,7 +114,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
|
||||
|
||||
Reference in New Issue
Block a user