1a8089ea5SJohn Baldwin /*-
2a8089ea5SJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause
3a8089ea5SJohn Baldwin *
4a8089ea5SJohn Baldwin * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5a8089ea5SJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org>
6a8089ea5SJohn Baldwin */
7a8089ea5SJohn Baldwin
8a8089ea5SJohn Baldwin #include <err.h>
9a8089ea5SJohn Baldwin #include <errno.h>
10a8089ea5SJohn Baldwin #include <libnvmf.h>
11a8089ea5SJohn Baldwin #include <stdlib.h>
12a8089ea5SJohn Baldwin
13a8089ea5SJohn Baldwin #include "internal.h"
14a8089ea5SJohn Baldwin
15a8089ea5SJohn Baldwin struct controller {
16a8089ea5SJohn Baldwin struct nvmf_qpair *qp;
17a8089ea5SJohn Baldwin
18a8089ea5SJohn Baldwin uint64_t cap;
19a8089ea5SJohn Baldwin uint32_t vs;
20a8089ea5SJohn Baldwin uint32_t cc;
21a8089ea5SJohn Baldwin uint32_t csts;
22a8089ea5SJohn Baldwin
23a8089ea5SJohn Baldwin bool shutdown;
24a8089ea5SJohn Baldwin
25a8089ea5SJohn Baldwin struct nvme_controller_data cdata;
26a8089ea5SJohn Baldwin };
27a8089ea5SJohn Baldwin
28a8089ea5SJohn Baldwin static bool
update_cc(struct controller * c,uint32_t new_cc)29a8089ea5SJohn Baldwin update_cc(struct controller *c, uint32_t new_cc)
30a8089ea5SJohn Baldwin {
31a8089ea5SJohn Baldwin uint32_t changes;
32a8089ea5SJohn Baldwin
33a8089ea5SJohn Baldwin if (c->shutdown)
34a8089ea5SJohn Baldwin return (false);
35a8089ea5SJohn Baldwin if (!nvmf_validate_cc(c->qp, c->cap, c->cc, new_cc))
36a8089ea5SJohn Baldwin return (false);
37a8089ea5SJohn Baldwin
38a8089ea5SJohn Baldwin changes = c->cc ^ new_cc;
39a8089ea5SJohn Baldwin c->cc = new_cc;
40a8089ea5SJohn Baldwin
41a8089ea5SJohn Baldwin /* Handle shutdown requests. */
42a8089ea5SJohn Baldwin if (NVMEV(NVME_CC_REG_SHN, changes) != 0 &&
43a8089ea5SJohn Baldwin NVMEV(NVME_CC_REG_SHN, new_cc) != 0) {
44a8089ea5SJohn Baldwin c->csts &= ~NVMEM(NVME_CSTS_REG_SHST);
45a8089ea5SJohn Baldwin c->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE);
46a8089ea5SJohn Baldwin c->shutdown = true;
47a8089ea5SJohn Baldwin }
48a8089ea5SJohn Baldwin
49a8089ea5SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, changes) != 0) {
50a8089ea5SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) {
51a8089ea5SJohn Baldwin /* Controller reset. */
52a8089ea5SJohn Baldwin c->csts = 0;
53a8089ea5SJohn Baldwin c->shutdown = true;
54a8089ea5SJohn Baldwin } else
55a8089ea5SJohn Baldwin c->csts |= NVMEF(NVME_CSTS_REG_RDY, 1);
56a8089ea5SJohn Baldwin }
57a8089ea5SJohn Baldwin return (true);
58a8089ea5SJohn Baldwin }
59a8089ea5SJohn Baldwin
60a8089ea5SJohn Baldwin static void
handle_property_get(const struct controller * c,const struct nvmf_capsule * nc,const struct nvmf_fabric_prop_get_cmd * pget)61a8089ea5SJohn Baldwin handle_property_get(const struct controller *c, const struct nvmf_capsule *nc,
62a8089ea5SJohn Baldwin const struct nvmf_fabric_prop_get_cmd *pget)
63a8089ea5SJohn Baldwin {
64a8089ea5SJohn Baldwin struct nvmf_fabric_prop_get_rsp rsp;
65a8089ea5SJohn Baldwin
66a8089ea5SJohn Baldwin nvmf_init_cqe(&rsp, nc, 0);
67a8089ea5SJohn Baldwin
68a8089ea5SJohn Baldwin switch (le32toh(pget->ofst)) {
69a8089ea5SJohn Baldwin case NVMF_PROP_CAP:
70a8089ea5SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_8)
71a8089ea5SJohn Baldwin goto error;
72a8089ea5SJohn Baldwin rsp.value.u64 = htole64(c->cap);
73a8089ea5SJohn Baldwin break;
74a8089ea5SJohn Baldwin case NVMF_PROP_VS:
75a8089ea5SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4)
76a8089ea5SJohn Baldwin goto error;
77a8089ea5SJohn Baldwin rsp.value.u32.low = htole32(c->vs);
78a8089ea5SJohn Baldwin break;
79a8089ea5SJohn Baldwin case NVMF_PROP_CC:
80a8089ea5SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4)
81a8089ea5SJohn Baldwin goto error;
82a8089ea5SJohn Baldwin rsp.value.u32.low = htole32(c->cc);
83a8089ea5SJohn Baldwin break;
84a8089ea5SJohn Baldwin case NVMF_PROP_CSTS:
85a8089ea5SJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4)
86a8089ea5SJohn Baldwin goto error;
87a8089ea5SJohn Baldwin rsp.value.u32.low = htole32(c->csts);
88a8089ea5SJohn Baldwin break;
89a8089ea5SJohn Baldwin default:
90a8089ea5SJohn Baldwin goto error;
91a8089ea5SJohn Baldwin }
92a8089ea5SJohn Baldwin
93a8089ea5SJohn Baldwin nvmf_send_response(nc, &rsp);
94a8089ea5SJohn Baldwin return;
95a8089ea5SJohn Baldwin error:
96a8089ea5SJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
97a8089ea5SJohn Baldwin }
98a8089ea5SJohn Baldwin
99a8089ea5SJohn Baldwin static void
handle_property_set(struct controller * c,const struct nvmf_capsule * nc,const struct nvmf_fabric_prop_set_cmd * pset)100a8089ea5SJohn Baldwin handle_property_set(struct controller *c, const struct nvmf_capsule *nc,
101a8089ea5SJohn Baldwin const struct nvmf_fabric_prop_set_cmd *pset)
102a8089ea5SJohn Baldwin {
103a8089ea5SJohn Baldwin switch (le32toh(pset->ofst)) {
104a8089ea5SJohn Baldwin case NVMF_PROP_CC:
105a8089ea5SJohn Baldwin if (pset->attrib.size != NVMF_PROP_SIZE_4)
106a8089ea5SJohn Baldwin goto error;
107a8089ea5SJohn Baldwin if (!update_cc(c, le32toh(pset->value.u32.low)))
108a8089ea5SJohn Baldwin goto error;
109a8089ea5SJohn Baldwin break;
110a8089ea5SJohn Baldwin default:
111a8089ea5SJohn Baldwin goto error;
112a8089ea5SJohn Baldwin }
113a8089ea5SJohn Baldwin
114a8089ea5SJohn Baldwin nvmf_send_success(nc);
115a8089ea5SJohn Baldwin return;
116a8089ea5SJohn Baldwin error:
117a8089ea5SJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
118a8089ea5SJohn Baldwin }
119a8089ea5SJohn Baldwin
120a8089ea5SJohn Baldwin static void
handle_fabrics_command(struct controller * c,const struct nvmf_capsule * nc,const struct nvmf_fabric_cmd * fc)121a8089ea5SJohn Baldwin handle_fabrics_command(struct controller *c,
122a8089ea5SJohn Baldwin const struct nvmf_capsule *nc, const struct nvmf_fabric_cmd *fc)
123a8089ea5SJohn Baldwin {
124a8089ea5SJohn Baldwin switch (fc->fctype) {
125a8089ea5SJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_GET:
126a8089ea5SJohn Baldwin handle_property_get(c, nc,
127a8089ea5SJohn Baldwin (const struct nvmf_fabric_prop_get_cmd *)fc);
128a8089ea5SJohn Baldwin break;
129a8089ea5SJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_SET:
130a8089ea5SJohn Baldwin handle_property_set(c, nc,
131a8089ea5SJohn Baldwin (const struct nvmf_fabric_prop_set_cmd *)fc);
132a8089ea5SJohn Baldwin break;
133a8089ea5SJohn Baldwin case NVMF_FABRIC_COMMAND_CONNECT:
134a8089ea5SJohn Baldwin warnx("CONNECT command on connected queue");
135a8089ea5SJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR);
136a8089ea5SJohn Baldwin break;
137a8089ea5SJohn Baldwin case NVMF_FABRIC_COMMAND_DISCONNECT:
138a8089ea5SJohn Baldwin warnx("DISCONNECT command on admin queue");
139a8089ea5SJohn Baldwin nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC,
140a8089ea5SJohn Baldwin NVMF_FABRIC_SC_INVALID_QUEUE_TYPE);
141a8089ea5SJohn Baldwin break;
142a8089ea5SJohn Baldwin default:
143a8089ea5SJohn Baldwin warnx("Unsupported fabrics command %#x", fc->fctype);
144a8089ea5SJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
145a8089ea5SJohn Baldwin break;
146a8089ea5SJohn Baldwin }
147a8089ea5SJohn Baldwin }
148a8089ea5SJohn Baldwin
149a8089ea5SJohn Baldwin static void
handle_identify_command(const struct controller * c,const struct nvmf_capsule * nc,const struct nvme_command * cmd)150a8089ea5SJohn Baldwin handle_identify_command(const struct controller *c,
151a8089ea5SJohn Baldwin const struct nvmf_capsule *nc, const struct nvme_command *cmd)
152a8089ea5SJohn Baldwin {
153a8089ea5SJohn Baldwin uint8_t cns;
154a8089ea5SJohn Baldwin
155a8089ea5SJohn Baldwin cns = le32toh(cmd->cdw10) & 0xFF;
156a8089ea5SJohn Baldwin switch (cns) {
157a8089ea5SJohn Baldwin case 1:
158a8089ea5SJohn Baldwin break;
159a8089ea5SJohn Baldwin default:
160a8089ea5SJohn Baldwin warnx("Unsupported CNS %#x for IDENTIFY", cns);
161a8089ea5SJohn Baldwin goto error;
162a8089ea5SJohn Baldwin }
163a8089ea5SJohn Baldwin
164a8089ea5SJohn Baldwin nvmf_send_controller_data(nc, &c->cdata, sizeof(c->cdata));
165a8089ea5SJohn Baldwin return;
166a8089ea5SJohn Baldwin error:
167a8089ea5SJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
168a8089ea5SJohn Baldwin }
169a8089ea5SJohn Baldwin
170a8089ea5SJohn Baldwin void
controller_handle_admin_commands(struct controller * c,handle_command * cb,void * cb_arg)171a8089ea5SJohn Baldwin controller_handle_admin_commands(struct controller *c, handle_command *cb,
172a8089ea5SJohn Baldwin void *cb_arg)
173a8089ea5SJohn Baldwin {
174a8089ea5SJohn Baldwin struct nvmf_qpair *qp = c->qp;
175a8089ea5SJohn Baldwin const struct nvme_command *cmd;
176a8089ea5SJohn Baldwin struct nvmf_capsule *nc;
177a8089ea5SJohn Baldwin int error;
178a8089ea5SJohn Baldwin
179a8089ea5SJohn Baldwin for (;;) {
180a8089ea5SJohn Baldwin error = nvmf_controller_receive_capsule(qp, &nc);
181a8089ea5SJohn Baldwin if (error != 0) {
182a8089ea5SJohn Baldwin if (error != ECONNRESET)
183a8089ea5SJohn Baldwin warnc(error, "Failed to read command capsule");
184a8089ea5SJohn Baldwin break;
185a8089ea5SJohn Baldwin }
186a8089ea5SJohn Baldwin
187a8089ea5SJohn Baldwin cmd = nvmf_capsule_sqe(nc);
188a8089ea5SJohn Baldwin
189a8089ea5SJohn Baldwin /*
190a8089ea5SJohn Baldwin * Only permit Fabrics commands while a controller is
191a8089ea5SJohn Baldwin * disabled.
192a8089ea5SJohn Baldwin */
193a8089ea5SJohn Baldwin if (NVMEV(NVME_CC_REG_EN, c->cc) == 0 &&
194a8089ea5SJohn Baldwin cmd->opc != NVME_OPC_FABRICS_COMMANDS) {
195*f5541f9fSJohn Baldwin warnx("Unsupported admin opcode %#x while disabled\n",
196a8089ea5SJohn Baldwin cmd->opc);
197a8089ea5SJohn Baldwin nvmf_send_generic_error(nc,
198a8089ea5SJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR);
199a8089ea5SJohn Baldwin nvmf_free_capsule(nc);
200a8089ea5SJohn Baldwin continue;
201a8089ea5SJohn Baldwin }
202a8089ea5SJohn Baldwin
203a8089ea5SJohn Baldwin if (cb(nc, cmd, cb_arg)) {
204a8089ea5SJohn Baldwin nvmf_free_capsule(nc);
205a8089ea5SJohn Baldwin continue;
206a8089ea5SJohn Baldwin }
207a8089ea5SJohn Baldwin
208a8089ea5SJohn Baldwin switch (cmd->opc) {
209a8089ea5SJohn Baldwin case NVME_OPC_FABRICS_COMMANDS:
210a8089ea5SJohn Baldwin handle_fabrics_command(c, nc,
211a8089ea5SJohn Baldwin (const struct nvmf_fabric_cmd *)cmd);
212a8089ea5SJohn Baldwin break;
213a8089ea5SJohn Baldwin case NVME_OPC_IDENTIFY:
214a8089ea5SJohn Baldwin handle_identify_command(c, nc, cmd);
215a8089ea5SJohn Baldwin break;
216a8089ea5SJohn Baldwin default:
217a8089ea5SJohn Baldwin warnx("Unsupported admin opcode %#x", cmd->opc);
218a8089ea5SJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
219a8089ea5SJohn Baldwin break;
220a8089ea5SJohn Baldwin }
221a8089ea5SJohn Baldwin nvmf_free_capsule(nc);
222a8089ea5SJohn Baldwin }
223a8089ea5SJohn Baldwin }
224a8089ea5SJohn Baldwin
225a8089ea5SJohn Baldwin struct controller *
init_controller(struct nvmf_qpair * qp,const struct nvme_controller_data * cdata)226a8089ea5SJohn Baldwin init_controller(struct nvmf_qpair *qp,
227a8089ea5SJohn Baldwin const struct nvme_controller_data *cdata)
228a8089ea5SJohn Baldwin {
229a8089ea5SJohn Baldwin struct controller *c;
230a8089ea5SJohn Baldwin
231a8089ea5SJohn Baldwin c = calloc(1, sizeof(*c));
232a8089ea5SJohn Baldwin c->qp = qp;
233a8089ea5SJohn Baldwin c->cap = nvmf_controller_cap(c->qp);
234a8089ea5SJohn Baldwin c->vs = cdata->ver;
235a8089ea5SJohn Baldwin c->cdata = *cdata;
236a8089ea5SJohn Baldwin
237a8089ea5SJohn Baldwin return (c);
238a8089ea5SJohn Baldwin }
239a8089ea5SJohn Baldwin
240a8089ea5SJohn Baldwin void
free_controller(struct controller * c)241a8089ea5SJohn Baldwin free_controller(struct controller *c)
242a8089ea5SJohn Baldwin {
243a8089ea5SJohn Baldwin free(c);
244a8089ea5SJohn Baldwin }
245