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