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 uint32_t
nvme_ctrl_info_fwgran(nvme_ctrl_info_t * ci)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 *
nvme_ctrl_info_fwrev(nvme_ctrl_info_t * ci)513 nvme_ctrl_info_fwrev(nvme_ctrl_info_t *ci)
514 {
515 return (ci->nci_fwrev);
516 }
517
518 const nvme_identify_ctrl_t *
nvme_ctrl_info_identify(nvme_ctrl_info_t * ci)519 nvme_ctrl_info_identify(nvme_ctrl_info_t *ci)
520 {
521 return (&ci->nci_info);
522 }
523
524 const nvme_version_t *
nvme_ctrl_info_version(nvme_ctrl_info_t * ci)525 nvme_ctrl_info_version(nvme_ctrl_info_t *ci)
526 {
527 return (&ci->nci_vers);
528 }
529
530 nvme_ctrl_transport_t
nvme_ctrl_info_transport(nvme_ctrl_info_t * ci)531 nvme_ctrl_info_transport(nvme_ctrl_info_t *ci)
532 {
533 return (ci->nci_tport);
534 }
535
536 nvme_ctrl_type_t
nvme_ctrl_info_type(nvme_ctrl_info_t * ci)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
nvme_ctrl_info_pci_tport(nvme_ctrl_info_t * ci)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
nvme_ctrl_info_pci_vid(nvme_ctrl_info_t * ci,uint16_t * u16p)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
nvme_ctrl_info_pci_did(nvme_ctrl_info_t * ci,uint16_t * u16p)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
nvme_ctrl_info_pci_subvid(nvme_ctrl_info_t * ci,uint16_t * u16p)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
nvme_ctrl_info_pci_subsys(nvme_ctrl_info_t * ci,uint16_t * u16p)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
nvme_ctrl_info_pci_rev(nvme_ctrl_info_t * ci,uint8_t * u8p)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
nvme_ctrl_info_pci_mps_min(nvme_ctrl_info_t * ci,uint32_t * u32p)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
nvme_ctrl_info_pci_mps_max(nvme_ctrl_info_t * ci,uint32_t * u32p)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
nvme_ctrl_info_pci_nintrs(nvme_ctrl_info_t * ci,uint32_t * u32p)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
nvme_ctrl_info_nsmgmt(nvme_ctrl_info_t * ci)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
nvme_ctrl_info_cap(nvme_ctrl_info_t * ci,nvme_uint128_t * u128p)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
nvme_ctrl_info_unalloc_cap(nvme_ctrl_info_t * ci,nvme_uint128_t * u128p)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
nvme_ctrl_info_common_ns(nvme_ctrl_info_t * ci,const nvme_identify_nsid_t ** idp)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
nvme_ctrl_info_nformats(nvme_ctrl_info_t * ci)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
nvme_ctrl_info_nns(nvme_ctrl_info_t * ci)707 nvme_ctrl_info_nns(nvme_ctrl_info_t *ci)
708 {
709 return (ci->nci_info.id_nn);
710 }
711
712 bool
nvme_ctrl_info_format(nvme_ctrl_info_t * ci,uint32_t idx,const nvme_nvm_lba_fmt_t ** outp)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
nvme_nvm_lba_fmt_id(const nvme_nvm_lba_fmt_t * lbaf)759 nvme_nvm_lba_fmt_id(const nvme_nvm_lba_fmt_t *lbaf)
760 {
761 return (lbaf->nnlf_id);
762 }
763
764 uint32_t
nvme_nvm_lba_fmt_meta_size(const nvme_nvm_lba_fmt_t * lbaf)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
nvme_nvm_lba_fmt_data_size(const nvme_nvm_lba_fmt_t * lbaf)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
nvme_nvm_lba_fmt_rel_perf(const nvme_nvm_lba_fmt_t * lbaf)777 nvme_nvm_lba_fmt_rel_perf(const nvme_nvm_lba_fmt_t *lbaf)
778 {
779 return (lbaf->nnlf_rel);
780 }
781