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