xref: /freebsd/sys/dev/nvmf/controller/nvmft_subr.c (revision 2f7b0de1)
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