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
nvmf_nqn_valid(const char * nqn)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
_nvmf_controller_cap(uint32_t max_io_qsize,uint8_t enable_timeout)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
_nvmf_validate_cc(uint32_t max_io_qsize __unused,uint64_t cap,uint32_t old_cc,uint32_t new_cc)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
nvmf_controller_serial(char * buf,size_t len,u_long hostid)166 nvmf_controller_serial(char *buf, size_t len, u_long hostid)
167 {
168 snprintf(buf, len, "HI:%lu", hostid);
169 }
170
171 void
nvmf_strpad(char * dst,const char * src,size_t len)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
_nvmf_init_io_controller_data(uint16_t cntlid,uint32_t max_io_qsize,const char * serial,const char * model,const char * firmware_version,const char * subnqn,int nn,uint32_t ioccsz,uint32_t iorcsz,struct nvme_controller_data * cdata)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