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