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