xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_ctrl_info.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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
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
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
70 nvme_ctrl_info_err(nvme_ctrl_info_t *ci)
71 {
72 	return (ci->nci_err);
73 }
74 
75 int32_t
76 nvme_ctrl_info_syserr(nvme_ctrl_info_t *ci)
77 {
78 	return (ci->nci_syserr);
79 }
80 
81 const char *
82 nvme_ctrl_info_errmsg(nvme_ctrl_info_t *ci)
83 {
84 	return (ci->nci_errmsg);
85 }
86 
87 size_t
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 *
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
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
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
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
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
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
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
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
485 nvme_ctrl_info_vendor(nvme_ctrl_info_t *ci)
486 {
487 	return (ci->nci_info.id_vid);
488 }
489 
490 const char *
491 nvme_ctrl_info_model(nvme_ctrl_info_t *ci)
492 {
493 	return (ci->nci_model);
494 }
495 
496 const char *
497 nvme_ctrl_info_serial(nvme_ctrl_info_t *ci)
498 {
499 	return (ci->nci_serial);
500 }
501 
502 uint32_t
503 nvme_ctrl_info_fwgran(nvme_ctrl_info_t *ci)
504 {
505 	nvme_valid_ctrl_data_t data;
506 
507 	data.vcd_vers = &ci->nci_vers;
508 	data.vcd_id = &ci->nci_info;
509 	return (nvme_fw_load_granularity(&data));
510 }
511 
512 const char *
513 nvme_ctrl_info_fwrev(nvme_ctrl_info_t *ci)
514 {
515 	return (ci->nci_fwrev);
516 }
517 
518 const nvme_identify_ctrl_t *
519 nvme_ctrl_info_identify(nvme_ctrl_info_t *ci)
520 {
521 	return (&ci->nci_info);
522 }
523 
524 const nvme_version_t *
525 nvme_ctrl_info_version(nvme_ctrl_info_t *ci)
526 {
527 	return (&ci->nci_vers);
528 }
529 
530 nvme_ctrl_transport_t
531 nvme_ctrl_info_transport(nvme_ctrl_info_t *ci)
532 {
533 	return (ci->nci_tport);
534 }
535 
536 nvme_ctrl_type_t
537 nvme_ctrl_info_type(nvme_ctrl_info_t *ci)
538 {
539 	if (nvme_vers_ctrl_info_atleast(ci, &nvme_vers_1v4)) {
540 		switch (ci->nci_info.id_cntrltype) {
541 		case NVME_CNTRLTYPE_IO:
542 			return (NVME_CTRL_TYPE_IO);
543 		case NVME_CNTRLTYPE_DISC:
544 			return (NVME_CTRL_TYPE_DISCOVERY);
545 		case NVME_CNTRLTYPE_ADMIN:
546 			return (NVME_CTRL_TYPE_ADMIN);
547 		default:
548 			return (NVME_CTRL_TYPE_UNKNOWN);
549 		}
550 	} else {
551 		return (NVME_CTRL_TYPE_IO);
552 	}
553 }
554 
555 static bool
556 nvme_ctrl_info_pci_tport(nvme_ctrl_info_t *ci)
557 {
558 	if (ci->nci_tport != NVME_CTRL_TRANSPORT_PCI) {
559 		return (nvme_info_error(ci, NVME_INFO_ERR_TRANSPORT, 0,
560 		    "cannot get PCI data from device with type %s (0x%x)",
561 		    nvme_tporttostr(ci->nci_tport), ci->nci_tport));
562 	}
563 
564 	return (true);
565 }
566 
567 bool
568 nvme_ctrl_info_pci_vid(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_vid;
574 	return (nvme_info_success(ci));
575 }
576 
577 bool
578 nvme_ctrl_info_pci_did(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_did;
584 	return (nvme_info_success(ci));
585 }
586 
587 bool
588 nvme_ctrl_info_pci_subvid(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_subvid;
594 	return (nvme_info_success(ci));
595 }
596 
597 bool
598 nvme_ctrl_info_pci_subsys(nvme_ctrl_info_t *ci, uint16_t *u16p)
599 {
600 	if (!nvme_ctrl_info_pci_tport(ci))
601 		return (false);
602 
603 	*u16p = ci->nci_subsys;
604 	return (nvme_info_success(ci));
605 }
606 
607 bool
608 nvme_ctrl_info_pci_rev(nvme_ctrl_info_t *ci, uint8_t *u8p)
609 {
610 	if (!nvme_ctrl_info_pci_tport(ci))
611 		return (false);
612 
613 	*u8p = ci->nci_rev;
614 	return (nvme_info_success(ci));
615 }
616 
617 bool
618 nvme_ctrl_info_pci_mps_min(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_min;
624 	return (nvme_info_success(ci));
625 }
626 
627 bool
628 nvme_ctrl_info_pci_mps_max(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_mps_max;
634 	return (nvme_info_success(ci));
635 }
636 
637 bool
638 nvme_ctrl_info_pci_nintrs(nvme_ctrl_info_t *ci, uint32_t *u32p)
639 {
640 	if (!nvme_ctrl_info_pci_tport(ci))
641 		return (false);
642 
643 	*u32p = ci->nci_nintrs;
644 	return (nvme_info_success(ci));
645 }
646 
647 static bool
648 nvme_ctrl_info_nsmgmt(nvme_ctrl_info_t *ci)
649 {
650 	if (!nvme_vers_ctrl_info_atleast(ci, &nvme_vers_1v2)) {
651 		return (nvme_info_error(ci, NVME_INFO_ERR_VERSION, 0,
652 		    "cannot provide information, device must be at least "
653 		    "version 1.2, but is %u.%u", ci->nci_vers.v_major,
654 		    ci->nci_vers.v_minor));
655 	}
656 
657 	if (ci->nci_info.id_oacs.oa_nsmgmt == 0) {
658 		return (nvme_info_error(ci, NVME_INFO_ERR_MISSING_CAP, 0,
659 		    "cannot provide information, device does not support "
660 		    "namespace management"));
661 
662 	}
663 
664 	return (true);
665 }
666 
667 bool
668 nvme_ctrl_info_cap(nvme_ctrl_info_t *ci, nvme_uint128_t *u128p)
669 {
670 	if (!nvme_ctrl_info_nsmgmt(ci)) {
671 		return (false);
672 	}
673 
674 	(void) memcpy(u128p, &ci->nci_info.ap_tnvmcap, sizeof (nvme_uint128_t));
675 	return (nvme_info_success(ci));
676 }
677 
678 bool
679 nvme_ctrl_info_unalloc_cap(nvme_ctrl_info_t *ci, nvme_uint128_t *u128p)
680 {
681 	if (!nvme_ctrl_info_nsmgmt(ci)) {
682 		return (false);
683 	}
684 
685 	(void) memcpy(u128p, &ci->nci_info.ap_unvmcap, sizeof (nvme_uint128_t));
686 	return (nvme_info_success(ci));
687 }
688 
689 bool
690 nvme_ctrl_info_common_ns(nvme_ctrl_info_t *ci, const nvme_identify_nsid_t **idp)
691 {
692 	if (!nvme_ctrl_info_nsmgmt(ci)) {
693 		return (false);
694 	}
695 
696 	*idp = &ci->nci_ns;
697 	return (nvme_info_success(ci));
698 }
699 
700 uint32_t
701 nvme_ctrl_info_nformats(nvme_ctrl_info_t *ci)
702 {
703 	return (MIN(ci->nci_ns.id_nlbaf + 1, NVME_MAX_LBAF));
704 }
705 
706 uint32_t
707 nvme_ctrl_info_nns(nvme_ctrl_info_t *ci)
708 {
709 	return (ci->nci_info.id_nn);
710 }
711 
712 bool
713 nvme_ctrl_info_format(nvme_ctrl_info_t *ci, uint32_t idx,
714     const nvme_nvm_lba_fmt_t **outp)
715 {
716 	const uint32_t max = nvme_ctrl_info_nformats(ci);
717 	if (idx >= max) {
718 		return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT, 0,
719 		    "requested index %u is invalid: valid range is [0, %u]",
720 		    idx, max - 1));
721 	}
722 
723 	if (!ci->nci_lbaf_valid[idx]) {
724 		uint8_t lbads = ci->nci_ns.id_lbaf[idx].lbaf_lbads;
725 
726 		if (lbads == 0) {
727 			return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT, 0,
728 			    "format %u is not actually valid due to 0 LBA "
729 			    "data size even though it is considered a valid "
730 			    "LBA format by NLBAF", lbads));
731 		}
732 
733 		if (lbads < 9) {
734 			return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT_DATA,
735 			    0, "NVMe devices are not allowed to have a LBA "
736 			    "data size of less than 512 bytes, found raw "
737 			    "shift value of %u for format %u", lbads, idx));
738 		}
739 
740 		if (lbads >= 64) {
741 			return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT_DATA,
742 			    0, "LBA format %u has LBA data size greater "
743 			    "than 64 (%u), cannot be represented as a byte "
744 			    "size", idx, lbads));
745 		}
746 
747 		ci->nci_lbaf[idx].nnlf_id = idx;
748 		ci->nci_lbaf[idx].nnlf_ms = ci->nci_ns.id_lbaf[idx].lbaf_ms;
749 		ci->nci_lbaf[idx].nnlf_lbasz = 1ULL << lbads;
750 		ci->nci_lbaf[idx].nnlf_rel = ci->nci_ns.id_lbaf[idx].lbaf_rp;
751 		ci->nci_lbaf_valid[idx] = true;
752 	}
753 
754 	*outp = &ci->nci_lbaf[idx];
755 	return (nvme_info_success(ci));
756 }
757 
758 uint32_t
759 nvme_nvm_lba_fmt_id(const nvme_nvm_lba_fmt_t *lbaf)
760 {
761 	return (lbaf->nnlf_id);
762 }
763 
764 uint32_t
765 nvme_nvm_lba_fmt_meta_size(const nvme_nvm_lba_fmt_t *lbaf)
766 {
767 	return (lbaf->nnlf_ms);
768 }
769 
770 uint64_t
771 nvme_nvm_lba_fmt_data_size(const nvme_nvm_lba_fmt_t *lbaf)
772 {
773 	return (lbaf->nnlf_lbasz);
774 }
775 
776 uint32_t
777 nvme_nvm_lba_fmt_rel_perf(const nvme_nvm_lba_fmt_t *lbaf)
778 {
779 	return (lbaf->nnlf_rel);
780 }
781