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 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 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 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 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 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 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 * 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 241 free_controller(struct controller *c) 242 { 243 free(c); 244 } 245