1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2024 Oxide Computer Company
14  */
15 
16 /*
17  * This file implements the logic behind the NVMe controller information
18  * interface.
19  *
20  * The idea behind controller information is to gather all of the information
21  * related to controller information in one structure that can then be
22  * interrogated. This data should have its own lifetime and represents a point
23  * in time snapshot. This then allows this information to be saved and restored
24  * across systems.
25  */
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <sys/sysmacros.h>
31 
32 #include "libnvme_impl.h"
33 
34 bool
nvme_info_error(nvme_ctrl_info_t * ci,nvme_info_err_t err,int32_t sys,const char * fmt,...)35 nvme_info_error(nvme_ctrl_info_t *ci, nvme_info_err_t err, int32_t sys,
36     const char *fmt, ...)
37 {
38 	int ret;
39 	va_list ap;
40 
41 	ci->nci_err = err;
42 	ci->nci_syserr = sys;
43 	va_start(ap, fmt);
44 	ret = vsnprintf(ci->nci_errmsg, sizeof (ci->nci_errmsg), fmt, ap);
45 	va_end(ap);
46 	if (ret >= sizeof (ci->nci_errmsg)) {
47 		ci->nci_errlen = sizeof (ci->nci_errmsg) - 1;
48 	} else if (ret <= 0) {
49 		ci->nci_errlen = 0;
50 		ci->nci_errmsg[0] = '\0';
51 	} else {
52 		ci->nci_errlen = (size_t)ret;
53 	}
54 
55 	return (false);
56 }
57 
58 bool
nvme_info_success(nvme_ctrl_info_t * ci)59 nvme_info_success(nvme_ctrl_info_t *ci)
60 {
61 	ci->nci_err = NVME_INFO_ERR_OK;
62 	ci->nci_syserr = 0;
63 	ci->nci_errmsg[0] = '\0';
64 	ci->nci_errlen = 0;
65 
66 	return (true);
67 }
68 
69 nvme_info_err_t
nvme_ctrl_info_err(nvme_ctrl_info_t * ci)70 nvme_ctrl_info_err(nvme_ctrl_info_t *ci)
71 {
72 	return (ci->nci_err);
73 }
74 
75 int32_t
nvme_ctrl_info_syserr(nvme_ctrl_info_t * ci)76 nvme_ctrl_info_syserr(nvme_ctrl_info_t *ci)
77 {
78 	return (ci->nci_syserr);
79 }
80 
81 const char *
nvme_ctrl_info_errmsg(nvme_ctrl_info_t * ci)82 nvme_ctrl_info_errmsg(nvme_ctrl_info_t *ci)
83 {
84 	return (ci->nci_errmsg);
85 }
86 
87 size_t
nvme_ctrl_info_errlen(nvme_ctrl_info_t * ci)88 nvme_ctrl_info_errlen(nvme_ctrl_info_t *ci)
89 {
90 	return (ci->nci_errlen);
91 }
92 
93 /*
94  * These errors are shared with the nvme_ns_info_t structures. While they both
95  * allow for us to pass in their respective information objects, that's mostly
96  * for future API changes. The namespace information variant of this just calls
97  * this function with the control information set to NULL.
98  */
99 const char *
nvme_ctrl_info_errtostr(nvme_ctrl_info_t * ci,nvme_info_err_t err)100 nvme_ctrl_info_errtostr(nvme_ctrl_info_t *ci, nvme_info_err_t err)
101 {
102 	switch (err) {
103 	case NVME_INFO_ERR_OK:
104 		return ("NVME_INFO_ERR_OK");
105 	case NVME_INFO_ERR_TRANSPORT:
106 		return ("NVME_INFO_ERR_TRANSPORT");
107 	case NVME_INFO_ERR_VERSION:
108 		return ("NVME_INFO_ERR_VERSION");
109 	case NVME_INFO_ERR_MISSING_CAP:
110 		return ("NVME_INFO_ERR_MISSING_CAP");
111 	case NVME_INFO_ERR_BAD_LBA_FMT:
112 		return ("NVME_INFO_ERR_BAD_LBA_FMT");
113 	case NVME_INFO_ERR_PERSIST_NVL:
114 		return ("NVME_INFO_ERR_PERSIST_NVL");
115 	case NVME_INFO_ERR_BAD_FMT:
116 		return ("NVME_INFO_ERR_BAD_FMT");
117 	case NVME_INFO_ERR_BAD_FMT_DATA:
118 		return ("NVME_INFO_ERR_BAD_FMT_DATA");
119 	case NVME_INFO_ERR_NS_INACTIVE:
120 		return ("NVME_INFO_ERR_NS_INACTIVE");
121 	case NVME_INFO_ERR_NS_NO_BLKDEV:
122 		return ("NVME_INFO_ERR_NS_NO_BLKDEV");
123 	default:
124 		return ("unknown error");
125 	}
126 }
127 
128 void
nvme_ctrl_info_free(nvme_ctrl_info_t * ci)129 nvme_ctrl_info_free(nvme_ctrl_info_t *ci)
130 {
131 	free(ci);
132 }
133 
134 /*
135  * The caller is required to ensure that out is at least max_src + 1 bytes long.
136  */
137 static void
nvme_ctrl_info_init_ident_str(const char * src,size_t max_src,char * out)138 nvme_ctrl_info_init_ident_str(const char *src, size_t max_src,
139     char *out)
140 {
141 	while (max_src > 0 && src[max_src - 1] == ' ') {
142 		max_src--;
143 	}
144 
145 	if (max_src == 0) {
146 		*out = '\0';
147 		return;
148 	}
149 
150 	(void) memcpy(out, src, max_src);
151 	out[max_src] = '\0';
152 }
153 
154 static void
nvme_ctrl_info_init_ident(nvme_ctrl_info_t * ci)155 nvme_ctrl_info_init_ident(nvme_ctrl_info_t *ci)
156 {
157 	nvme_ctrl_info_init_ident_str(ci->nci_info.id_serial,
158 	    sizeof (ci->nci_info.id_serial), ci->nci_serial);
159 	nvme_ctrl_info_init_ident_str(ci->nci_info.id_model,
160 	    sizeof (ci->nci_info.id_model), ci->nci_model);
161 	nvme_ctrl_info_init_ident_str(ci->nci_info.id_fwrev,
162 	    sizeof (ci->nci_info.id_fwrev), ci->nci_fwrev);
163 }
164 
165 bool
nvme_ctrl_info_restore(nvme_t * nvme,nvlist_t * nvl,nvme_ctrl_info_t ** outp)166 nvme_ctrl_info_restore(nvme_t *nvme, nvlist_t *nvl, nvme_ctrl_info_t **outp)
167 {
168 	int ret;
169 	uint32_t vers;
170 	nvme_ctrl_info_t *ci;
171 	char *path;
172 	uchar_t *ctrl, *ns;
173 	uint_t ctrl_len, ns_len;
174 
175 	if (nvl == NULL) {
176 		return (nvme_error(nvme, NVME_ERR_BAD_PTR, 0, "encountered "
177 		    "invalid nvlist_t input pointer: %p", nvl));
178 	}
179 
180 	if (outp == NULL) {
181 		return (nvme_error(nvme, NVME_ERR_BAD_PTR, 0, "encountered "
182 		    "invalid nvme_ctrl_info_t output pointer: %p", outp));
183 	}
184 	*outp = NULL;
185 
186 	ci = calloc(1, sizeof (nvme_ctrl_info_t));
187 	if (ci == NULL) {
188 		int e = errno;
189 		return (nvme_error(nvme, NVME_ERR_NO_MEM, e, "failed to "
190 		    "allocate memory for a new nvme_ctrl_info: %s",
191 		    strerror(e)));
192 	}
193 
194 	if ((ret = nvlist_lookup_uint32(nvl, NVME_NVL_CI_VERS, &vers)) != 0) {
195 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, ret, "failed "
196 		    "to get version key: %s", strerror(ret));
197 		goto err;
198 	}
199 
200 	if (vers != NVME_NVL_CI_VERS_0) {
201 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0,
202 		    "found unsupported version key: 0x%x", vers);
203 		goto err;
204 	}
205 
206 	ret = nvlist_lookup_pairs(nvl, 0,
207 	    NVME_NVL_CI_MAJOR, DATA_TYPE_UINT16, &ci->nci_vers.v_major,
208 	    NVME_NVL_CI_MINOR, DATA_TYPE_UINT16, &ci->nci_vers.v_minor,
209 	    NVME_NVL_CI_INST, DATA_TYPE_INT32, &ci->nci_inst,
210 	    NVME_NVL_CI_DEV_PATH, DATA_TYPE_STRING, &path,
211 	    NVME_NVL_CI_ID_CTRL, DATA_TYPE_BYTE_ARRAY, &ctrl, &ctrl_len,
212 	    NVME_NVL_CI_ID_NS, DATA_TYPE_BYTE_ARRAY, &ns, &ns_len,
213 	    NVME_NVL_CI_TPORT, DATA_TYPE_UINT32, &ci->nci_tport, NULL);
214 	if (ret != 0) {
215 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, ret,
216 		    "failed to retrieve required keys: %s", strerror(ret));
217 		goto err;
218 	}
219 
220 	if (ci->nci_inst < 0) {
221 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0,
222 		    "instance data is negative");
223 		goto err;
224 	}
225 
226 	if (ctrl_len != sizeof (ci->nci_info)) {
227 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "identify "
228 		    "controller information is the wrong length, expected "
229 		    "0x%zx bytes, found 0x%x", sizeof (ci->nci_info), ctrl_len);
230 		goto err;
231 	}
232 
233 	if (ns_len != sizeof (ci->nci_ns)) {
234 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "identify "
235 		    "namespace information is the wrong length, expected "
236 		    "0x%zx bytes, found 0x%x", sizeof (ci->nci_info), ctrl_len);
237 		goto err;
238 	}
239 
240 	(void) memcpy(&ci->nci_info, ctrl, ctrl_len);
241 	(void) memcpy(&ci->nci_ns, ns, ns_len);
242 
243 	if (strlcpy(ci->nci_dev_path, path, sizeof (ci->nci_dev_path)) >=
244 	    sizeof (ci->nci_dev_path)) {
245 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "device "
246 		    "path would have overflowed");
247 		goto err;
248 	}
249 
250 	if (ci->nci_tport != NVME_CTRL_TRANSPORT_PCI) {
251 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "found "
252 		    "unknown transport type: 0x%x", ci->nci_tport);
253 		goto err;
254 	}
255 
256 	ret = nvlist_lookup_pairs(nvl, 0,
257 	    NVME_NVL_CI_PCI_VID, DATA_TYPE_UINT16, &ci->nci_vid,
258 	    NVME_NVL_CI_PCI_DID, DATA_TYPE_UINT16, &ci->nci_did,
259 	    NVME_NVL_CI_PCI_SUBVID, DATA_TYPE_UINT16, &ci->nci_subvid,
260 	    NVME_NVL_CI_PCI_SUBSYS, DATA_TYPE_UINT16, &ci->nci_subsys,
261 	    NVME_NVL_CI_PCI_REV, DATA_TYPE_UINT8, &ci->nci_rev,
262 	    NVME_NVL_CI_PCI_MPSMIN, DATA_TYPE_UINT32, &ci->nci_mps_min,
263 	    NVME_NVL_CI_PCI_MPSMAX, DATA_TYPE_UINT32, &ci->nci_mps_max,
264 	    NVME_NVL_CI_PCI_NINTRS, DATA_TYPE_UINT32, &ci->nci_nintrs, NULL);
265 	if (ret != 0) {
266 		(void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, ret,
267 		    "failed to retrieve required PCI-specific keys: %s",
268 		    strerror(ret));
269 		goto err;
270 	}
271 
272 	nvme_ctrl_info_init_ident(ci);
273 	*outp = ci;
274 	return (true);
275 
276 err:
277 	nvme_ctrl_info_free(ci);
278 	return (false);
279 }
280 
281 bool
nvme_ctrl_info_persist(nvme_ctrl_info_t * ci,nvlist_t ** nvlp)282 nvme_ctrl_info_persist(nvme_ctrl_info_t *ci, nvlist_t **nvlp)
283 {
284 	int ret;
285 	nvlist_t *nvl;
286 
287 	if ((ret = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) {
288 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL,
289 		    ret, "failed to create initial nvlist_t *: %s",
290 		    strerror(ret)));
291 	}
292 
293 	if ((ret = nvlist_add_uint32(nvl, NVME_NVL_CI_VERS,
294 	    NVME_NVL_CI_VERS_0)) != 0) {
295 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
296 		    "failed to persist %s to nvlist: %s", NVME_NVL_CI_VERS,
297 		    strerror(ret)));
298 	}
299 
300 	if ((ret = nvlist_add_int32(nvl, NVME_NVL_CI_INST, ci->nci_inst)) !=
301 	    0) {
302 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
303 		    "failed to persist %s to nvlist: %s", NVME_NVL_CI_INST,
304 		    strerror(ret)));
305 	}
306 
307 	if ((ret = nvlist_add_uint16(nvl, NVME_NVL_CI_MAJOR,
308 	    ci->nci_vers.v_major)) != 0 ||
309 	    (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_MINOR,
310 	    ci->nci_vers.v_minor)) != 0) {
311 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
312 		    "failed to persist version data to nvlist: %s",
313 		    strerror(ret)));
314 	}
315 
316 	if ((ret = nvlist_add_string(nvl, NVME_NVL_CI_DEV_PATH,
317 	    ci->nci_dev_path)) != 0) {
318 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
319 		    "failed to persist %s to nvlist: %s", NVME_NVL_CI_DEV_PATH,
320 		    strerror(ret)));
321 	}
322 
323 	if ((ret = nvlist_add_byte_array(nvl, NVME_NVL_CI_ID_CTRL,
324 	    (void *)&ci->nci_info, sizeof (ci->nci_info))) != 0) {
325 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
326 		    "failed to persist %s to nvlist: %s", NVME_NVL_CI_ID_CTRL,
327 		    strerror(ret)));
328 	}
329 
330 	if ((ret = nvlist_add_byte_array(nvl, NVME_NVL_CI_ID_NS,
331 	    (void *)&ci->nci_ns, sizeof (ci->nci_ns))) != 0) {
332 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
333 		    "failed to persist %s to nvlist: %s", NVME_NVL_CI_ID_NS,
334 		    strerror(ret)));
335 	}
336 
337 	if ((ret = nvlist_add_uint32(nvl, NVME_NVL_CI_TPORT, ci->nci_tport)) !=
338 	    0) {
339 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
340 		    "failed to persist %s to nvlist: %s", NVME_NVL_CI_TPORT,
341 		    strerror(ret)));
342 	}
343 
344 	if ((ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_VID,
345 	    ci->nci_vid)) != 0 ||
346 	    (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_DID,
347 	    ci->nci_did)) != 0 ||
348 	    (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_SUBVID,
349 	    ci->nci_subvid)) != 0 ||
350 	    (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_SUBSYS,
351 	    ci->nci_subsys)) != 0 ||
352 	    (ret = nvlist_add_uint8(nvl, NVME_NVL_CI_PCI_REV,
353 	    ci->nci_rev)) != 0 ||
354 	    (ret = nvlist_add_uint32(nvl, NVME_NVL_CI_PCI_MPSMIN,
355 	    ci->nci_mps_min)) != 0 ||
356 	    (ret = nvlist_add_uint32(nvl, NVME_NVL_CI_PCI_MPSMAX,
357 	    ci->nci_mps_max)) != 0 ||
358 	    (ret = nvlist_add_uint32(nvl, NVME_NVL_CI_PCI_NINTRS,
359 	    ci->nci_nintrs)) != 0) {
360 		return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret,
361 		    "failed to persist PCI data to nvlist: %s", strerror(ret)));
362 	}
363 
364 	*nvlp = nvl;
365 	return (true);
366 }
367 
368 static bool
nvme_ctrl_get_udi(nvme_ctrl_t * ctrl,di_node_t di,const char * prop,uint32_t * outp,uint32_t max)369 nvme_ctrl_get_udi(nvme_ctrl_t *ctrl, di_node_t di, const char *prop,
370     uint32_t *outp, uint32_t max)
371 {
372 	int *vals, nvals;
373 
374 	nvals = di_prop_lookup_ints(DDI_DEV_T_ANY, di, prop, &vals);
375 	if (nvals < 0) {
376 		int e = errno;
377 		return (nvme_ctrl_error(ctrl, NVME_ERR_LIBDEVINFO, e, "failed "
378 		    "to get property %s while constructing controller "
379 		    "information: %s", prop, strerror(e)));
380 	} else if (nvals != 1) {
381 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_DEVI_PROP, 0,
382 		    "found unexpected number of property values for %s while "
383 		    "constructing controller information, expected 1, found %d",
384 		    prop, nvals));
385 	}
386 
387 	if (vals[0] < 0 || vals[0] > max) {
388 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_DEVI_PROP, 0,
389 		    "property %s has value 0x%x outside the allowed range "
390 		    "[0x0, 0x%x]", prop, vals[0], max));
391 	}
392 
393 	*outp = (uint32_t)vals[0];
394 	return (true);
395 }
396 
397 bool
nvme_ctrl_info_snap(nvme_ctrl_t * ctrl,nvme_ctrl_info_t ** outp)398 nvme_ctrl_info_snap(nvme_ctrl_t *ctrl, nvme_ctrl_info_t **outp)
399 {
400 	nvme_ctrl_info_t *ci;
401 	uint32_t val;
402 	nvme_ioctl_ctrl_info_t info;
403 
404 	if (outp == NULL) {
405 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
406 		    "encountered invalid nvme_ctrl_info_t output pointer: %p",
407 		    outp));
408 	}
409 	*outp = NULL;
410 
411 	ci = calloc(1, sizeof (nvme_ctrl_info_t));
412 	if (ci == NULL) {
413 		int e = errno;
414 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
415 		    "allocate memory for a new nvme_ctrl_info: %s",
416 		    strerror(e)));
417 	}
418 
419 	if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "vendor-id", &val,
420 	    UINT16_MAX)) {
421 		goto err;
422 	}
423 	ci->nci_vid = (uint16_t)val;
424 
425 	if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "device-id", &val,
426 	    UINT16_MAX)) {
427 		goto err;
428 	}
429 	ci->nci_did = (uint16_t)val;
430 
431 	/*
432 	 * The system will not create a subsystem-vendor-id or a subsystem-id if
433 	 * the subsytem vendor is zero. This should not be a fatal error.
434 	 * However, if a subsytem-vendor-id is present then we should expect a
435 	 * subsystem-id.
436 	 */
437 	if (nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "subsystem-vendor-id", &val,
438 	    UINT16_MAX)) {
439 		ci->nci_subvid = (uint16_t)val;
440 
441 		if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "subsystem-id",
442 		    &val, UINT16_MAX)) {
443 			goto err;
444 		}
445 	} else {
446 		ci->nci_subvid = 0;
447 		ci->nci_subsys = 0;
448 	}
449 
450 	if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "revision-id", &val,
451 	    UINT8_MAX)) {
452 		goto err;
453 	}
454 	ci->nci_rev = (uint8_t)val;
455 
456 	/*
457 	 * As we only support PCI based NVMe devices right now, we simply always
458 	 * identify everything as PCI based. In the future, this would be
459 	 * something we'd want to get from either an ioctl or a devinfo
460 	 * property.
461 	 */
462 	ci->nci_tport = NVME_CTRL_TRANSPORT_PCI;
463 
464 	if (!nvme_ioc_ctrl_info(ctrl, &info)) {
465 		goto err;
466 	}
467 
468 	ci->nci_vers = info.nci_vers;
469 	ci->nci_info = info.nci_ctrl_id;
470 	ci->nci_ns = info.nci_common_ns;
471 	ci->nci_mps_min = info.nci_caps.cap_mpsmin;
472 	ci->nci_mps_max = info.nci_caps.cap_mpsmax;
473 	ci->nci_nintrs = info.nci_nintrs;
474 
475 	nvme_ctrl_info_init_ident(ci);
476 	*outp = ci;
477 	return (nvme_ctrl_success(ctrl));
478 
479 err:
480 	nvme_ctrl_info_free(ci);
481 	return (false);
482 }
483 
484 uint16_t
nvme_ctrl_info_vendor(nvme_ctrl_info_t * ci)485 nvme_ctrl_info_vendor(nvme_ctrl_info_t *ci)
486 {
487 	return (ci->nci_info.id_vid);
488 }
489 
490 const char *
nvme_ctrl_info_model(nvme_ctrl_info_t * ci)491 nvme_ctrl_info_model(nvme_ctrl_info_t *ci)
492 {
493 	return (ci->nci_model);
494 }
495 
496 const char *
nvme_ctrl_info_serial(nvme_ctrl_info_t * ci)497 nvme_ctrl_info_serial(nvme_ctrl_info_t *ci)
498 {
499 	return (ci->nci_serial);
500 }
501 
502 const char *
nvme_ctrl_info_fwrev(nvme_ctrl_info_t * ci)503 nvme_ctrl_info_fwrev(nvme_ctrl_info_t *ci)
504 {
505 	return (ci->nci_fwrev);
506 }
507 
508 const nvme_identify_ctrl_t *
nvme_ctrl_info_identify(nvme_ctrl_info_t * ci)509 nvme_ctrl_info_identify(nvme_ctrl_info_t *ci)
510 {
511 	return (&ci->nci_info);
512 }
513 
514 const nvme_version_t *
nvme_ctrl_info_version(nvme_ctrl_info_t * ci)515 nvme_ctrl_info_version(nvme_ctrl_info_t *ci)
516 {
517 	return (&ci->nci_vers);
518 }
519 
520 nvme_ctrl_transport_t
nvme_ctrl_info_transport(nvme_ctrl_info_t * ci)521 nvme_ctrl_info_transport(nvme_ctrl_info_t *ci)
522 {
523 	return (ci->nci_tport);
524 }
525 
526 nvme_ctrl_type_t
nvme_ctrl_info_type(nvme_ctrl_info_t * ci)527 nvme_ctrl_info_type(nvme_ctrl_info_t *ci)
528 {
529 	if (nvme_vers_ctrl_info_atleast(ci, &nvme_vers_1v4)) {
530 		switch (ci->nci_info.id_cntrltype) {
531 		case NVME_CNTRLTYPE_IO:
532 			return (NVME_CTRL_TYPE_IO);
533 		case NVME_CNTRLTYPE_DISC:
534 			return (NVME_CTRL_TYPE_DISCOVERY);
535 		case NVME_CNTRLTYPE_ADMIN:
536 			return (NVME_CTRL_TYPE_ADMIN);
537 		default:
538 			return (NVME_CTRL_TYPE_UNKNOWN);
539 		}
540 	} else {
541 		return (NVME_CTRL_TYPE_IO);
542 	}
543 }
544 
545 static bool
nvme_ctrl_info_pci_tport(nvme_ctrl_info_t * ci)546 nvme_ctrl_info_pci_tport(nvme_ctrl_info_t *ci)
547 {
548 	if (ci->nci_tport != NVME_CTRL_TRANSPORT_PCI) {
549 		return (nvme_info_error(ci, NVME_INFO_ERR_TRANSPORT, 0,
550 		    "cannot get PCI data from device with type %s (0x%x)",
551 		    nvme_tporttostr(ci->nci_tport), ci->nci_tport));
552 	}
553 
554 	return (true);
555 }
556 
557 bool
nvme_ctrl_info_pci_vid(nvme_ctrl_info_t * ci,uint16_t * u16p)558 nvme_ctrl_info_pci_vid(nvme_ctrl_info_t *ci, uint16_t *u16p)
559 {
560 	if (!nvme_ctrl_info_pci_tport(ci))
561 		return (false);
562 
563 	*u16p = ci->nci_vid;
564 	return (nvme_info_success(ci));
565 }
566 
567 bool
nvme_ctrl_info_pci_did(nvme_ctrl_info_t * ci,uint16_t * u16p)568 nvme_ctrl_info_pci_did(nvme_ctrl_info_t *ci, uint16_t *u16p)
569 {
570 	if (!nvme_ctrl_info_pci_tport(ci))
571 		return (false);
572 
573 	*u16p = ci->nci_did;
574 	return (nvme_info_success(ci));
575 }
576 
577 bool
nvme_ctrl_info_pci_subvid(nvme_ctrl_info_t * ci,uint16_t * u16p)578 nvme_ctrl_info_pci_subvid(nvme_ctrl_info_t *ci, uint16_t *u16p)
579 {
580 	if (!nvme_ctrl_info_pci_tport(ci))
581 		return (false);
582 
583 	*u16p = ci->nci_subvid;
584 	return (nvme_info_success(ci));
585 }
586 
587 bool
nvme_ctrl_info_pci_subsys(nvme_ctrl_info_t * ci,uint16_t * u16p)588 nvme_ctrl_info_pci_subsys(nvme_ctrl_info_t *ci, uint16_t *u16p)
589 {
590 	if (!nvme_ctrl_info_pci_tport(ci))
591 		return (false);
592 
593 	*u16p = ci->nci_subsys;
594 	return (nvme_info_success(ci));
595 }
596 
597 bool
nvme_ctrl_info_pci_rev(nvme_ctrl_info_t * ci,uint8_t * u8p)598 nvme_ctrl_info_pci_rev(nvme_ctrl_info_t *ci, uint8_t *u8p)
599 {
600 	if (!nvme_ctrl_info_pci_tport(ci))
601 		return (false);
602 
603 	*u8p = ci->nci_rev;
604 	return (nvme_info_success(ci));
605 }
606 
607 bool
nvme_ctrl_info_pci_mps_min(nvme_ctrl_info_t * ci,uint32_t * u32p)608 nvme_ctrl_info_pci_mps_min(nvme_ctrl_info_t *ci, uint32_t *u32p)
609 {
610 	if (!nvme_ctrl_info_pci_tport(ci))
611 		return (false);
612 
613 	*u32p = ci->nci_mps_min;
614 	return (nvme_info_success(ci));
615 }
616 
617 bool
nvme_ctrl_info_pci_mps_max(nvme_ctrl_info_t * ci,uint32_t * u32p)618 nvme_ctrl_info_pci_mps_max(nvme_ctrl_info_t *ci, uint32_t *u32p)
619 {
620 	if (!nvme_ctrl_info_pci_tport(ci))
621 		return (false);
622 
623 	*u32p = ci->nci_mps_max;
624 	return (nvme_info_success(ci));
625 }
626 
627 bool
nvme_ctrl_info_pci_nintrs(nvme_ctrl_info_t * ci,uint32_t * u32p)628 nvme_ctrl_info_pci_nintrs(nvme_ctrl_info_t *ci, uint32_t *u32p)
629 {
630 	if (!nvme_ctrl_info_pci_tport(ci))
631 		return (false);
632 
633 	*u32p = ci->nci_nintrs;
634 	return (nvme_info_success(ci));
635 }
636 
637 static bool
nvme_ctrl_info_nsmgmt(nvme_ctrl_info_t * ci)638 nvme_ctrl_info_nsmgmt(nvme_ctrl_info_t *ci)
639 {
640 	if (!nvme_vers_ctrl_info_atleast(ci, &nvme_vers_1v2)) {
641 		return (nvme_info_error(ci, NVME_INFO_ERR_VERSION, 0,
642 		    "cannot provide information, device must be at least "
643 		    "version 1.2, but is %u.%u", ci->nci_vers.v_major,
644 		    ci->nci_vers.v_minor));
645 	}
646 
647 	if (ci->nci_info.id_oacs.oa_nsmgmt == 0) {
648 		return (nvme_info_error(ci, NVME_INFO_ERR_MISSING_CAP, 0,
649 		    "cannot provide information, device does not support "
650 		    "namespace management"));
651 
652 	}
653 
654 	return (true);
655 }
656 
657 bool
nvme_ctrl_info_cap(nvme_ctrl_info_t * ci,nvme_uint128_t * u128p)658 nvme_ctrl_info_cap(nvme_ctrl_info_t *ci, nvme_uint128_t *u128p)
659 {
660 	if (!nvme_ctrl_info_nsmgmt(ci)) {
661 		return (false);
662 	}
663 
664 	(void) memcpy(u128p, &ci->nci_info.ap_tnvmcap, sizeof (nvme_uint128_t));
665 	return (nvme_info_success(ci));
666 }
667 
668 bool
nvme_ctrl_info_unalloc_cap(nvme_ctrl_info_t * ci,nvme_uint128_t * u128p)669 nvme_ctrl_info_unalloc_cap(nvme_ctrl_info_t *ci, nvme_uint128_t *u128p)
670 {
671 	if (!nvme_ctrl_info_nsmgmt(ci)) {
672 		return (false);
673 	}
674 
675 	(void) memcpy(u128p, &ci->nci_info.ap_unvmcap, sizeof (nvme_uint128_t));
676 	return (nvme_info_success(ci));
677 }
678 
679 bool
nvme_ctrl_info_common_ns(nvme_ctrl_info_t * ci,const nvme_identify_nsid_t ** idp)680 nvme_ctrl_info_common_ns(nvme_ctrl_info_t *ci, const nvme_identify_nsid_t **idp)
681 {
682 	if (!nvme_ctrl_info_nsmgmt(ci)) {
683 		return (false);
684 	}
685 
686 	*idp = &ci->nci_ns;
687 	return (nvme_info_success(ci));
688 }
689 
690 uint32_t
nvme_ctrl_info_nformats(nvme_ctrl_info_t * ci)691 nvme_ctrl_info_nformats(nvme_ctrl_info_t *ci)
692 {
693 	return (MIN(ci->nci_ns.id_nlbaf + 1, NVME_MAX_LBAF));
694 }
695 
696 uint32_t
nvme_ctrl_info_nns(nvme_ctrl_info_t * ci)697 nvme_ctrl_info_nns(nvme_ctrl_info_t *ci)
698 {
699 	return (ci->nci_info.id_nn);
700 }
701 
702 bool
nvme_ctrl_info_format(nvme_ctrl_info_t * ci,uint32_t idx,const nvme_nvm_lba_fmt_t ** outp)703 nvme_ctrl_info_format(nvme_ctrl_info_t *ci, uint32_t idx,
704     const nvme_nvm_lba_fmt_t **outp)
705 {
706 	const uint32_t max = nvme_ctrl_info_nformats(ci);
707 	if (idx >= max) {
708 		return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT, 0,
709 		    "requested index %u is invalid: valid range is [0, %u]",
710 		    idx, max - 1));
711 	}
712 
713 	if (!ci->nci_lbaf_valid[idx]) {
714 		uint8_t lbads = ci->nci_ns.id_lbaf[idx].lbaf_lbads;
715 
716 		if (lbads == 0) {
717 			return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT, 0,
718 			    "format %u is not actually valid due to 0 LBA "
719 			    "data size even though it is considered a valid "
720 			    "LBA format by NLBAF", lbads));
721 		}
722 
723 		if (lbads < 9) {
724 			return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT_DATA,
725 			    0, "NVMe devices are not allowed to have a LBA "
726 			    "data size of less than 512 bytes, found raw "
727 			    "shift value of %u for format %u", lbads, idx));
728 		}
729 
730 		if (lbads >= 64) {
731 			return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT_DATA,
732 			    0, "LBA format %u has LBA data size greater "
733 			    "than 64 (%u), cannot be represented as a byte "
734 			    "size", idx, lbads));
735 		}
736 
737 		ci->nci_lbaf[idx].nnlf_id = idx;
738 		ci->nci_lbaf[idx].nnlf_ms = ci->nci_ns.id_lbaf[idx].lbaf_ms;
739 		ci->nci_lbaf[idx].nnlf_lbasz = 1ULL << lbads;
740 		ci->nci_lbaf[idx].nnlf_rel = ci->nci_ns.id_lbaf[idx].lbaf_rp;
741 		ci->nci_lbaf_valid[idx] = true;
742 	}
743 
744 	*outp = &ci->nci_lbaf[idx];
745 	return (nvme_info_success(ci));
746 }
747 
748 uint32_t
nvme_nvm_lba_fmt_id(const nvme_nvm_lba_fmt_t * lbaf)749 nvme_nvm_lba_fmt_id(const nvme_nvm_lba_fmt_t *lbaf)
750 {
751 	return (lbaf->nnlf_id);
752 }
753 
754 uint32_t
nvme_nvm_lba_fmt_meta_size(const nvme_nvm_lba_fmt_t * lbaf)755 nvme_nvm_lba_fmt_meta_size(const nvme_nvm_lba_fmt_t *lbaf)
756 {
757 	return (lbaf->nnlf_ms);
758 }
759 
760 uint64_t
nvme_nvm_lba_fmt_data_size(const nvme_nvm_lba_fmt_t * lbaf)761 nvme_nvm_lba_fmt_data_size(const nvme_nvm_lba_fmt_t *lbaf)
762 {
763 	return (lbaf->nnlf_lbasz);
764 }
765 
766 uint32_t
nvme_nvm_lba_fmt_rel_perf(const nvme_nvm_lba_fmt_t * lbaf)767 nvme_nvm_lba_fmt_rel_perf(const nvme_nvm_lba_fmt_t *lbaf)
768 {
769 	return (lbaf->nnlf_rel);
770 }
771