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 <sys/types.h> 9 #ifdef _KERNEL 10 #include <sys/libkern.h> 11 #else 12 #include <stdbool.h> 13 #include <stdio.h> 14 #include <string.h> 15 #endif 16 17 #include <dev/nvmf/nvmf_proto.h> 18 19 #include "nvmft_subr.h" 20 21 bool 22 nvmf_nqn_valid(const char *nqn) 23 { 24 size_t len; 25 26 len = strnlen(nqn, NVME_NQN_FIELD_SIZE); 27 if (len == 0 || len > NVMF_NQN_MAX_LEN) 28 return (false); 29 30 #ifdef STRICT_CHECKS 31 /* 32 * Stricter checks from the spec. Linux does not seem to 33 * require these. 34 */ 35 36 /* 37 * NVMF_NQN_MIN_LEN does not include '.', and require at least 38 * one character of a domain name. 39 */ 40 if (len < NVMF_NQN_MIN_LEN + 2) 41 return (false); 42 if (memcmp("nqn.", nqn, strlen("nqn.")) != 0) 43 return (false); 44 nqn += strlen("nqn."); 45 46 /* Next 4 digits must be a year. */ 47 for (u_int i = 0; i < 4; i++) { 48 if (!isdigit(nqn[i])) 49 return (false); 50 } 51 nqn += 4; 52 53 /* '-' between year and month. */ 54 if (nqn[0] != '-') 55 return (false); 56 nqn++; 57 58 /* 2 digit month. */ 59 for (u_int i = 0; i < 2; i++) { 60 if (!isdigit(nqn[i])) 61 return (false); 62 } 63 nqn += 2; 64 65 /* '.' between month and reverse domain name. */ 66 if (nqn[0] != '.') 67 return (false); 68 #endif 69 return (true); 70 } 71 72 uint64_t 73 _nvmf_controller_cap(uint32_t max_io_qsize, uint8_t enable_timeout) 74 { 75 uint32_t caphi, caplo; 76 u_int mps; 77 78 caphi = NVMEF(NVME_CAP_HI_REG_CMBS, 0) | 79 NVMEF(NVME_CAP_HI_REG_PMRS, 0); 80 if (max_io_qsize != 0) { 81 mps = ffs(PAGE_SIZE) - 1; 82 if (mps < NVME_MPS_SHIFT) 83 mps = 0; 84 else 85 mps -= NVME_MPS_SHIFT; 86 caphi |= NVMEF(NVME_CAP_HI_REG_MPSMAX, mps) | 87 NVMEF(NVME_CAP_HI_REG_MPSMIN, mps); 88 } 89 caphi |= NVMEF(NVME_CAP_HI_REG_BPS, 0) | 90 NVMEF(NVME_CAP_HI_REG_CSS, NVME_CAP_HI_REG_CSS_NVM_MASK) | 91 NVMEF(NVME_CAP_HI_REG_NSSRS, 0) | 92 NVMEF(NVME_CAP_HI_REG_DSTRD, 0); 93 94 caplo = NVMEF(NVME_CAP_LO_REG_TO, enable_timeout) | 95 NVMEF(NVME_CAP_LO_REG_AMS, 0) | 96 NVMEF(NVME_CAP_LO_REG_CQR, 1); 97 98 if (max_io_qsize != 0) 99 caplo |= NVMEF(NVME_CAP_LO_REG_MQES, max_io_qsize - 1); 100 101 return ((uint64_t)caphi << 32 | caplo); 102 } 103 104 bool 105 _nvmf_validate_cc(uint32_t max_io_qsize __unused, uint64_t cap, uint32_t old_cc, 106 uint32_t new_cc) 107 { 108 uint32_t caphi, changes, field; 109 110 changes = old_cc ^ new_cc; 111 field = NVMEV(NVME_CC_REG_IOCQES, new_cc); 112 if (field != 0) { 113 /* 114 * XXX: Linux's initiator writes a non-zero value to 115 * IOCQES when connecting to a discovery controller. 116 */ 117 #ifdef STRICT_CHECKS 118 if (max_io_qsize == 0) 119 return (false); 120 #endif 121 if (field != 4) 122 return (false); 123 } 124 field = NVMEV(NVME_CC_REG_IOSQES, new_cc); 125 if (field != 0) { 126 /* 127 * XXX: Linux's initiator writes a non-zero value to 128 * IOCQES when connecting to a discovery controller. 129 */ 130 #ifdef STRICT_CHECKS 131 if (max_io_qsize == 0) 132 return (false); 133 #endif 134 if (field != 6) 135 return (false); 136 } 137 field = NVMEV(NVME_CC_REG_SHN, new_cc); 138 if (field == 3) 139 return (false); 140 141 field = NVMEV(NVME_CC_REG_AMS, new_cc); 142 if (field != 0) 143 return (false); 144 145 caphi = cap >> 32; 146 field = NVMEV(NVME_CC_REG_MPS, new_cc); 147 if (field < NVMEV(NVME_CAP_HI_REG_MPSMAX, caphi) || 148 field > NVMEV(NVME_CAP_HI_REG_MPSMIN, caphi)) 149 return (false); 150 151 field = NVMEV(NVME_CC_REG_CSS, new_cc); 152 if (field != 0 && field != 0x7) 153 return (false); 154 155 /* AMS, MPS, and CSS can only be changed while CC.EN is 0. */ 156 if (NVMEV(NVME_CC_REG_EN, old_cc) != 0 && 157 (NVMEV(NVME_CC_REG_AMS, changes) != 0 || 158 NVMEV(NVME_CC_REG_MPS, changes) != 0 || 159 NVMEV(NVME_CC_REG_CSS, changes) != 0)) 160 return (false); 161 162 return (true); 163 } 164 165 void 166 nvmf_controller_serial(char *buf, size_t len, u_long hostid) 167 { 168 snprintf(buf, len, "HI:%lu", hostid); 169 } 170 171 void 172 nvmf_strpad(char *dst, const char *src, size_t len) 173 { 174 while (len > 0 && *src != '\0') { 175 *dst++ = *src++; 176 len--; 177 } 178 memset(dst, ' ', len); 179 } 180 181 void 182 _nvmf_init_io_controller_data(uint16_t cntlid, uint32_t max_io_qsize, 183 const char *serial, const char *model, const char *firmware_version, 184 const char *subnqn, int nn, uint32_t ioccsz, uint32_t iorcsz, 185 struct nvme_controller_data *cdata) 186 { 187 char *cp; 188 189 nvmf_strpad(cdata->sn, serial, sizeof(cdata->sn)); 190 nvmf_strpad(cdata->mn, model, sizeof(cdata->mn)); 191 nvmf_strpad(cdata->fr, firmware_version, sizeof(cdata->fr)); 192 cp = memchr(cdata->fr, '-', sizeof(cdata->fr)); 193 if (cp != NULL) 194 memset(cp, ' ', sizeof(cdata->fr) - (cp - (char *)cdata->fr)); 195 196 /* FreeBSD OUI */ 197 cdata->ieee[0] = 0xfc; 198 cdata->ieee[1] = 0x9c; 199 cdata->ieee[2] = 0x58; 200 201 cdata->ctrlr_id = htole16(cntlid); 202 cdata->ver = htole32(NVME_REV(1, 4)); 203 cdata->ctratt = htole32( 204 NVMEF(NVME_CTRLR_DATA_CTRATT_128BIT_HOSTID, 1) | 205 NVMEF(NVME_CTRLR_DATA_CTRATT_TBKAS, 1)); 206 cdata->cntrltype = 1; 207 cdata->acl = 3; 208 cdata->aerl = 3; 209 210 /* 1 read-only firmware slot */ 211 cdata->frmw = NVMEF(NVME_CTRLR_DATA_FRMW_SLOT1_RO, 1) | 212 NVMEF(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, 1); 213 214 cdata->lpa = NVMEF(NVME_CTRLR_DATA_LPA_EXT_DATA, 1); 215 216 /* Single power state */ 217 cdata->npss = 0; 218 219 /* 220 * 1.2+ require a non-zero value for these even though it makes 221 * no sense for Fabrics. 222 */ 223 cdata->wctemp = htole16(0x0157); 224 cdata->cctemp = cdata->wctemp; 225 226 /* 1 second granularity for KeepAlive */ 227 cdata->kas = htole16(10); 228 229 cdata->sqes = NVMEF(NVME_CTRLR_DATA_SQES_MAX, 6) | 230 NVMEF(NVME_CTRLR_DATA_SQES_MIN, 6); 231 cdata->cqes = NVMEF(NVME_CTRLR_DATA_CQES_MAX, 4) | 232 NVMEF(NVME_CTRLR_DATA_CQES_MIN, 4); 233 234 cdata->maxcmd = htole16(max_io_qsize); 235 cdata->nn = htole32(nn); 236 237 cdata->vwc = 238 NVMEF(NVME_CTRLR_DATA_VWC_ALL, NVME_CTRLR_DATA_VWC_ALL_NO) | 239 NVMEM(NVME_CTRLR_DATA_VWC_PRESENT); 240 241 /* Transport-specific? */ 242 cdata->sgls = htole32( 243 NVMEF(NVME_CTRLR_DATA_SGLS_TRANSPORT_DATA_BLOCK, 1) | 244 NVMEF(NVME_CTRLR_DATA_SGLS_ADDRESS_AS_OFFSET, 1) | 245 NVMEF(NVME_CTRLR_DATA_SGLS_NVM_COMMAND_SET, 1)); 246 247 strlcpy(cdata->subnqn, subnqn, sizeof(cdata->subnqn)); 248 249 cdata->ioccsz = htole32(ioccsz / 16); 250 cdata->iorcsz = htole32(iorcsz / 16); 251 252 /* Transport-specific? */ 253 cdata->icdoff = 0; 254 255 cdata->fcatt = 0; 256 257 /* Transport-specific? */ 258 cdata->msdbd = 1; 259 } 260