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 2017 Joyent, Inc. 14 * Copyright 2021 Oxide Computer Company 15 * Copyright 2022 Tintri by DDN, Inc. All rights reserved. 16 */ 17 18 /* 19 * nvmeadm -- NVMe administration utility 20 * 21 * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args] 22 * commands: list 23 * identify 24 * get-logpage <logpage name> 25 * get-features <feature>[,...] 26 * format ... 27 * secure-erase ... 28 * detach ... 29 * attach ... 30 * list-firmware ... 31 * load-firmware ... 32 * commit-firmware ... 33 * activate-firmware ... 34 */ 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <stddef.h> 39 #include <unistd.h> 40 #include <fcntl.h> 41 #include <strings.h> 42 #include <ctype.h> 43 #include <err.h> 44 #include <sys/sunddi.h> 45 #include <libdevinfo.h> 46 47 #include <sys/nvme.h> 48 49 #include "nvmeadm.h" 50 51 /* 52 * Assertions to make sure that we've properly captured various aspects of the 53 * packed structures and haven't broken them during updates. 54 */ 55 CTASSERT(sizeof (nvme_identify_ctrl_t) == NVME_IDENTIFY_BUFSIZE); 56 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oacs) == 256); 57 CTASSERT(offsetof(nvme_identify_ctrl_t, id_sqes) == 512); 58 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oncs) == 520); 59 CTASSERT(offsetof(nvme_identify_ctrl_t, id_subnqn) == 768); 60 CTASSERT(offsetof(nvme_identify_ctrl_t, id_nvmof) == 1792); 61 CTASSERT(offsetof(nvme_identify_ctrl_t, id_psd) == 2048); 62 CTASSERT(offsetof(nvme_identify_ctrl_t, id_vs) == 3072); 63 64 CTASSERT(sizeof (nvme_identify_nsid_t) == NVME_IDENTIFY_BUFSIZE); 65 CTASSERT(offsetof(nvme_identify_nsid_t, id_fpi) == 32); 66 CTASSERT(offsetof(nvme_identify_nsid_t, id_anagrpid) == 92); 67 CTASSERT(offsetof(nvme_identify_nsid_t, id_nguid) == 104); 68 CTASSERT(offsetof(nvme_identify_nsid_t, id_lbaf) == 128); 69 CTASSERT(offsetof(nvme_identify_nsid_t, id_vs) == 384); 70 71 CTASSERT(sizeof (nvme_identify_nsid_list_t) == NVME_IDENTIFY_BUFSIZE); 72 CTASSERT(sizeof (nvme_identify_ctrl_list_t) == NVME_IDENTIFY_BUFSIZE); 73 74 CTASSERT(sizeof (nvme_identify_primary_caps_t) == NVME_IDENTIFY_BUFSIZE); 75 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vqfrt) == 32); 76 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vifrt) == 64); 77 78 CTASSERT(sizeof (nvme_nschange_list_t) == 4096); 79 80 81 struct nvme_feature { 82 char *f_name; 83 char *f_short; 84 uint8_t f_feature; 85 size_t f_bufsize; 86 uint_t f_getflags; 87 int (*f_get)(int, const nvme_feature_t *, const nvme_process_arg_t *); 88 void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *, 89 nvme_version_t *); 90 }; 91 92 #define NVMEADM_F_CTRL 1 93 #define NVMEADM_F_NS 2 94 #define NVMEADM_F_BOTH (NVMEADM_F_CTRL | NVMEADM_F_NS) 95 96 #define NVMEADM_C_MULTI 1 97 #define NVMEADM_C_EXCL 2 98 99 struct nvmeadm_cmd { 100 char *c_name; 101 const char *c_desc; 102 const char *c_flagdesc; 103 int (*c_func)(int, const nvme_process_arg_t *); 104 void (*c_usage)(const char *); 105 void (*c_optparse)(nvme_process_arg_t *); 106 int c_flags; 107 }; 108 109 110 static void usage(const nvmeadm_cmd_t *); 111 static void nvme_walk(nvme_process_arg_t *, di_node_t); 112 static boolean_t nvme_match(nvme_process_arg_t *); 113 114 static int nvme_process(di_node_t, di_minor_t, void *); 115 116 static int do_list(int, const nvme_process_arg_t *); 117 static int do_identify(int, const nvme_process_arg_t *); 118 static int do_identify_ctrl(int, const nvme_process_arg_t *); 119 static int do_identify_ns(int, const nvme_process_arg_t *); 120 static int do_get_logpage_error(int, const nvme_process_arg_t *); 121 static int do_get_logpage_health(int, const nvme_process_arg_t *); 122 static int do_get_logpage_fwslot(int, const nvme_process_arg_t *); 123 static int do_get_logpage(int, const nvme_process_arg_t *); 124 static int do_get_feat_common(int, const nvme_feature_t *, 125 const nvme_process_arg_t *); 126 static int do_get_feat_intr_vect(int, const nvme_feature_t *, 127 const nvme_process_arg_t *); 128 static int do_get_feat_temp_thresh(int, const nvme_feature_t *, 129 const nvme_process_arg_t *); 130 static int do_get_features(int, const nvme_process_arg_t *); 131 static int do_format(int, const nvme_process_arg_t *); 132 static int do_secure_erase(int, const nvme_process_arg_t *); 133 static int do_attach_detach(int, const nvme_process_arg_t *); 134 static int do_firmware_load(int, const nvme_process_arg_t *); 135 static int do_firmware_commit(int, const nvme_process_arg_t *); 136 static int do_firmware_activate(int, const nvme_process_arg_t *); 137 138 static void optparse_list(nvme_process_arg_t *); 139 static void optparse_identify(nvme_process_arg_t *); 140 static void optparse_identify_ctrl(nvme_process_arg_t *); 141 static void optparse_identify_ns(nvme_process_arg_t *); 142 static void optparse_secure_erase(nvme_process_arg_t *); 143 144 static void usage_list(const char *); 145 static void usage_identify(const char *); 146 static void usage_identify_ctrl(const char *); 147 static void usage_identify_ns(const char *); 148 static void usage_get_logpage(const char *); 149 static void usage_get_features(const char *); 150 static void usage_format(const char *); 151 static void usage_secure_erase(const char *); 152 static void usage_attach_detach(const char *); 153 static void usage_firmware_list(const char *); 154 static void usage_firmware_load(const char *); 155 static void usage_firmware_commit(const char *); 156 static void usage_firmware_activate(const char *); 157 158 int verbose; 159 int debug; 160 161 #define NVMEADM_O_SE_CRYPTO 0x00000004 162 163 #define NVMEADM_O_ID_NSID_LIST 0x00000008 164 #define NVMEADM_O_ID_COMMON_NS 0x00000010 165 #define NVMEADM_O_ID_CTRL_LIST 0x00000020 166 #define NVMEADM_O_ID_DESC_LIST 0x00000040 167 #define NVMEADM_O_ID_ALLOC_NS 0x00000080 168 169 static int exitcode; 170 171 /* 172 * Nvmeadm subcommand definitons. 173 * 174 * When adding a new subcommand, please check that the commands still 175 * line up in the usage() message, and adjust the format string in 176 * usage() below if necessary. 177 */ 178 static const nvmeadm_cmd_t nvmeadm_cmds[] = { 179 { 180 "list", 181 "list controllers and namespaces", 182 " -p\t\tprint parsable output\n" 183 " -o field\tselect a field for parsable output\n", 184 do_list, usage_list, optparse_list, 185 NVMEADM_C_MULTI 186 }, 187 { 188 "identify", 189 "identify controllers and/or namespaces", 190 " -C\t\tget Common Namespace Identification\n" 191 " -a\t\tget only allocated namespace information\n" 192 " -c\t\tget controller identifier list\n" 193 " -d\t\tget namespace identification descriptors list\n" 194 " -n\t\tget namespaces identifier list\n", 195 do_identify, usage_identify, optparse_identify, 196 NVMEADM_C_MULTI 197 }, 198 { 199 "identify-controller", 200 "identify controllers", 201 " -C\t\tget Common Namespace Identification\n" 202 " -a\t\tget only allocated namespace information\n" 203 " -c\t\tget controller identifier list\n" 204 " -n\t\tget namespaces identifier list\n", 205 do_identify_ctrl, usage_identify_ctrl, optparse_identify_ctrl, 206 NVMEADM_C_MULTI 207 }, 208 { 209 "identify-namespace", 210 "identify namespaces", 211 " -c\t\tget attached controller identifier list\n" 212 " -d\t\tget namespace identification descriptors list\n", 213 do_identify_ns, usage_identify_ns, optparse_identify_ns, 214 NVMEADM_C_MULTI 215 }, 216 { 217 "get-logpage", 218 "get a log page from controllers and/or namespaces", 219 NULL, 220 do_get_logpage, usage_get_logpage, NULL, 221 NVMEADM_C_MULTI 222 }, 223 { 224 "get-features", 225 "get features from controllers and/or namespaces", 226 NULL, 227 do_get_features, usage_get_features, NULL, 228 NVMEADM_C_MULTI 229 }, 230 { 231 "format", 232 "format namespace(s) of a controller", 233 NULL, 234 do_format, usage_format, NULL, 235 NVMEADM_C_EXCL 236 }, 237 { 238 "secure-erase", 239 "secure erase namespace(s) of a controller", 240 " -c Do a cryptographic erase.", 241 do_secure_erase, usage_secure_erase, optparse_secure_erase, 242 NVMEADM_C_EXCL 243 }, 244 { 245 "detach", 246 "detach blkdev(4D) from namespace(s) of a controller", 247 NULL, 248 do_attach_detach, usage_attach_detach, NULL, 249 NVMEADM_C_EXCL 250 }, 251 { 252 "attach", 253 "attach blkdev(4D) to namespace(s) of a controller", 254 NULL, 255 do_attach_detach, usage_attach_detach, NULL, 256 NVMEADM_C_EXCL 257 }, 258 { 259 "list-firmware", 260 "list firmware on a controller", 261 NULL, 262 do_get_logpage_fwslot, usage_firmware_list, NULL, 263 0 264 }, 265 { 266 "load-firmware", 267 "load firmware to a controller", 268 NULL, 269 do_firmware_load, usage_firmware_load, NULL, 270 0 271 }, 272 { 273 "commit-firmware", 274 "commit downloaded firmware to a slot of a controller", 275 NULL, 276 do_firmware_commit, usage_firmware_commit, NULL, 277 0 278 }, 279 { 280 "activate-firmware", 281 "activate a firmware slot of a controller", 282 NULL, 283 do_firmware_activate, usage_firmware_activate, NULL, 284 0 285 }, 286 { 287 NULL, NULL, NULL, 288 NULL, NULL, NULL, 0 289 } 290 }; 291 292 static const nvme_feature_t features[] = { 293 { "Arbitration", "", 294 NVME_FEAT_ARBITRATION, 0, NVMEADM_F_CTRL, 295 do_get_feat_common, nvme_print_feat_arbitration }, 296 { "Power Management", "", 297 NVME_FEAT_POWER_MGMT, 0, NVMEADM_F_CTRL, 298 do_get_feat_common, nvme_print_feat_power_mgmt }, 299 { "LBA Range Type", "range", 300 NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_F_NS, 301 do_get_feat_common, nvme_print_feat_lba_range }, 302 { "Temperature Threshold", "", 303 NVME_FEAT_TEMPERATURE, 0, NVMEADM_F_CTRL, 304 do_get_feat_temp_thresh, nvme_print_feat_temperature }, 305 { "Error Recovery", "", 306 NVME_FEAT_ERROR, 0, NVMEADM_F_CTRL, 307 do_get_feat_common, nvme_print_feat_error }, 308 { "Volatile Write Cache", "cache", 309 NVME_FEAT_WRITE_CACHE, 0, NVMEADM_F_CTRL, 310 do_get_feat_common, nvme_print_feat_write_cache }, 311 { "Number of Queues", "queues", 312 NVME_FEAT_NQUEUES, 0, NVMEADM_F_CTRL, 313 do_get_feat_common, nvme_print_feat_nqueues }, 314 { "Interrupt Coalescing", "coalescing", 315 NVME_FEAT_INTR_COAL, 0, NVMEADM_F_CTRL, 316 do_get_feat_common, nvme_print_feat_intr_coal }, 317 { "Interrupt Vector Configuration", "vector", 318 NVME_FEAT_INTR_VECT, 0, NVMEADM_F_CTRL, 319 do_get_feat_intr_vect, nvme_print_feat_intr_vect }, 320 { "Write Atomicity", "atomicity", 321 NVME_FEAT_WRITE_ATOM, 0, NVMEADM_F_CTRL, 322 do_get_feat_common, nvme_print_feat_write_atom }, 323 { "Asynchronous Event Configuration", "event", 324 NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_F_CTRL, 325 do_get_feat_common, nvme_print_feat_async_event }, 326 { "Autonomous Power State Transition", "", 327 NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_F_CTRL, 328 do_get_feat_common, nvme_print_feat_auto_pst }, 329 { "Software Progress Marker", "progress", 330 NVME_FEAT_PROGRESS, 0, NVMEADM_F_CTRL, 331 do_get_feat_common, nvme_print_feat_progress }, 332 { NULL, NULL, 0, 0, B_FALSE, NULL } 333 }; 334 335 336 int 337 main(int argc, char **argv) 338 { 339 int c; 340 const nvmeadm_cmd_t *cmd; 341 di_node_t node; 342 nvme_process_arg_t npa = { 0 }; 343 int help = 0; 344 char *tmp, *lasts = NULL; 345 char *ctrl = NULL; 346 347 while ((c = getopt(argc, argv, "dhv")) != -1) { 348 switch (c) { 349 case 'd': 350 debug++; 351 break; 352 353 case 'v': 354 verbose++; 355 break; 356 357 case 'h': 358 help++; 359 break; 360 361 case '?': 362 usage(NULL); 363 exit(-1); 364 } 365 } 366 367 if (optind == argc) { 368 usage(NULL); 369 if (help) 370 exit(0); 371 else 372 exit(-1); 373 } 374 375 /* Look up the specified command in the command table. */ 376 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) 377 if (strcmp(cmd->c_name, argv[optind]) == 0) 378 break; 379 380 if (cmd->c_name == NULL) { 381 usage(NULL); 382 exit(-1); 383 } 384 385 if (help) { 386 usage(cmd); 387 exit(0); 388 } 389 390 npa.npa_cmd = cmd; 391 npa.npa_interactive = B_TRUE; 392 npa.npa_excl = ((cmd->c_flags & NVMEADM_C_EXCL) != 0); 393 394 optind++; 395 396 /* 397 * Store the remaining arguments for use by the command. Give the 398 * command a chance to process the options across the board before going 399 * into each controller. 400 */ 401 npa.npa_argc = argc - optind; 402 npa.npa_argv = &argv[optind]; 403 404 if (cmd->c_optparse != NULL) { 405 cmd->c_optparse(&npa); 406 } 407 408 /* 409 * All commands but "list" require a ctl/ns argument. However, this 410 * should not be passed through to the command in its subsequent 411 * arguments. 412 */ 413 if ((npa.npa_argc == 0 || (strncmp(npa.npa_argv[0], "nvme", 4) != 0)) && 414 cmd->c_func != do_list) { 415 warnx("missing controller/namespace name"); 416 usage(cmd); 417 exit(-1); 418 } 419 420 if (npa.npa_argc > 0) { 421 ctrl = npa.npa_argv[0]; 422 npa.npa_argv++; 423 npa.npa_argc--; 424 } else { 425 ctrl = NULL; 426 } 427 428 /* 429 * Make sure we're not running commands on multiple controllers that 430 * aren't allowed to do that. 431 */ 432 if (ctrl != NULL && strchr(ctrl, ',') != NULL && 433 (cmd->c_flags & NVMEADM_C_MULTI) == 0) { 434 warnx("%s not allowed on multiple controllers", 435 cmd->c_name); 436 usage(cmd); 437 exit(-1); 438 } 439 440 /* 441 * Get controller/namespace arguments and run command. 442 */ 443 npa.npa_name = strtok_r(ctrl, ",", &lasts); 444 do { 445 if (npa.npa_name != NULL) { 446 tmp = strchr(npa.npa_name, '/'); 447 if (tmp != NULL) { 448 *tmp++ = '\0'; 449 npa.npa_nsid = tmp; 450 npa.npa_isns = B_TRUE; 451 } 452 } 453 454 if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL) 455 err(-1, "failed to initialize libdevinfo"); 456 nvme_walk(&npa, node); 457 di_fini(node); 458 459 if (npa.npa_found == 0) { 460 if (npa.npa_name != NULL) { 461 warnx("%s%.*s%.*s: no such controller or " 462 "namespace", npa.npa_name, 463 npa.npa_isns ? -1 : 0, "/", 464 npa.npa_isns ? -1 : 0, npa.npa_nsid); 465 } else { 466 warnx("no controllers found"); 467 } 468 exitcode--; 469 } 470 npa.npa_found = 0; 471 npa.npa_name = strtok_r(NULL, ",", &lasts); 472 } while (npa.npa_name != NULL); 473 474 exit(exitcode); 475 } 476 477 static void 478 nvme_oferr(const char *fmt, ...) 479 { 480 va_list ap; 481 482 va_start(ap, fmt); 483 verrx(-1, fmt, ap); 484 } 485 486 static void 487 usage(const nvmeadm_cmd_t *cmd) 488 { 489 const char *progname = getprogname(); 490 491 (void) fprintf(stderr, "usage:\n"); 492 (void) fprintf(stderr, " %s -h %s\n", progname, 493 cmd != NULL ? cmd->c_name : "[<command>]"); 494 (void) fprintf(stderr, " %s [-dv] ", progname); 495 496 if (cmd != NULL) { 497 cmd->c_usage(cmd->c_name); 498 } else { 499 (void) fprintf(stderr, 500 "<command> <ctl>[/<ns>][,...] [<args>]\n"); 501 (void) fprintf(stderr, 502 "\n Manage NVMe controllers and namespaces.\n"); 503 (void) fprintf(stderr, "\ncommands:\n"); 504 505 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) { 506 /* 507 * The longest nvmeadm subcommand is 19 characters long. 508 * The format string needs to be updated every time a 509 * longer subcommand is added. 510 */ 511 (void) fprintf(stderr, " %-19s - %s\n", 512 cmd->c_name, cmd->c_desc); 513 } 514 } 515 (void) fprintf(stderr, "\n%s flags:\n" 516 " -h\t\tprint usage information\n" 517 " -d\t\tprint information useful for debugging %s\n" 518 " -v\t\tprint verbose information\n", 519 progname, progname); 520 521 if (cmd != NULL && cmd->c_flagdesc != NULL) { 522 (void) fprintf(stderr, "\n%s %s flags:\n", 523 progname, cmd->c_name); 524 (void) fprintf(stderr, "%s\n", cmd->c_flagdesc); 525 } 526 } 527 528 static boolean_t 529 nvme_match(nvme_process_arg_t *npa) 530 { 531 char *name; 532 char *nsid = NULL; 533 534 if (npa->npa_name == NULL) 535 return (B_TRUE); 536 537 if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 538 di_instance(npa->npa_node)) < 0) 539 err(-1, "nvme_match()"); 540 541 if (strcmp(name, npa->npa_name) != 0) { 542 free(name); 543 return (B_FALSE); 544 } 545 546 free(name); 547 548 if (npa->npa_isns) { 549 if (npa->npa_nsid == NULL) 550 return (B_TRUE); 551 552 nsid = di_minor_name(npa->npa_minor); 553 554 if (nsid == NULL || strcmp(npa->npa_nsid, nsid) != 0) 555 return (B_FALSE); 556 } 557 558 return (B_TRUE); 559 } 560 561 char * 562 nvme_dskname(const nvme_process_arg_t *npa) 563 { 564 char *path = NULL; 565 di_node_t child; 566 di_dim_t dim; 567 char *addr; 568 char *disk_ctd; 569 char *diskname = NULL; 570 571 dim = di_dim_init(); 572 573 for (child = di_child_node(npa->npa_node); 574 child != DI_NODE_NIL; 575 child = di_sibling_node(child)) { 576 addr = di_bus_addr(child); 577 if (addr == NULL) 578 continue; 579 580 if (addr[0] == 'w') 581 addr++; 582 583 if (strncasecmp(addr, di_minor_name(npa->npa_minor), 584 strchrnul(addr, ',') - addr) != 0) 585 continue; 586 587 path = di_dim_path_dev(dim, di_driver_name(child), 588 di_instance(child), "c"); 589 590 /* 591 * Error out if we didn't get a path, or if it's too short for 592 * the following operations to be safe. 593 */ 594 if (path == NULL || strlen(path) < 2) 595 goto fail; 596 597 /* Chop off 's0' and get everything past the last '/' */ 598 path[strlen(path) - 2] = '\0'; 599 disk_ctd = strrchr(path, '/'); 600 if (disk_ctd == NULL) 601 goto fail; 602 diskname = strdup(++disk_ctd); 603 if (diskname == NULL) 604 goto fail; 605 606 free(path); 607 break; 608 } 609 610 di_dim_fini(dim); 611 612 return (diskname); 613 614 fail: 615 free(path); 616 err(-1, "nvme_dskname"); 617 } 618 619 static int 620 nvme_process(di_node_t node, di_minor_t minor, void *arg) 621 { 622 nvme_process_arg_t *npa = arg; 623 int fd; 624 625 npa->npa_node = node; 626 npa->npa_minor = minor; 627 628 if (!nvme_match(npa)) 629 return (DI_WALK_CONTINUE); 630 631 if ((fd = nvme_open(minor, npa->npa_excl)) < 0) 632 return (DI_WALK_CONTINUE); 633 634 npa->npa_found++; 635 636 npa->npa_path = di_devfs_path(node); 637 if (npa->npa_path == NULL) 638 goto out; 639 640 npa->npa_version = nvme_version(fd); 641 if (npa->npa_version == NULL) 642 goto out; 643 644 npa->npa_idctl = nvme_identify(fd, NVME_IDENTIFY_CTRL); 645 if (npa->npa_idctl == NULL) 646 goto out; 647 648 if (nvme_version_check(npa->npa_version, 1, 2) && 649 npa->npa_idctl->id_oacs.oa_nsmgmt != 0 && 650 npa->npa_isns) { 651 /* 652 * We prefer NVME_IDENTIFY_NSID_ALLOC when supported as that can 653 * return data on inactive namespaces, too. 654 */ 655 npa->npa_idns = nvme_identify(fd, NVME_IDENTIFY_NSID_ALLOC); 656 } else { 657 npa->npa_idns = nvme_identify(fd, NVME_IDENTIFY_NSID); 658 } 659 660 if (npa->npa_idns == NULL) 661 goto out; 662 663 npa->npa_dsk = NULL; 664 if (npa->npa_isns) { 665 npa->npa_ns_state = nvme_namespace_state(fd); 666 if ((npa->npa_ns_state & NVME_NS_STATE_ATTACHED) != 0) 667 npa->npa_dsk = nvme_dskname(npa); 668 } 669 670 671 exitcode += npa->npa_cmd->c_func(fd, npa); 672 673 out: 674 di_devfs_path_free(npa->npa_path); 675 free(npa->npa_version); 676 free(npa->npa_idctl); 677 free(npa->npa_idns); 678 free(npa->npa_dsk); 679 680 npa->npa_version = NULL; 681 npa->npa_idctl = NULL; 682 npa->npa_idns = NULL; 683 npa->npa_dsk = NULL; 684 685 nvme_close(fd); 686 687 return (DI_WALK_CONTINUE); 688 } 689 690 static void 691 nvme_walk(nvme_process_arg_t *npa, di_node_t node) 692 { 693 char *minor_nodetype = DDI_NT_NVME_NEXUS; 694 695 if (npa->npa_isns) 696 minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT; 697 698 (void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process); 699 } 700 701 static void 702 usage_list(const char *c_name) 703 { 704 (void) fprintf(stderr, "%s " 705 "[-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n" 706 " List NVMe controllers and their namespaces. If no " 707 "controllers and/or name-\n spaces are specified, all " 708 "controllers and namespaces in the system will be\n " 709 "listed.\n", c_name); 710 } 711 712 static void 713 optparse_list(nvme_process_arg_t *npa) 714 { 715 int c; 716 uint_t oflags = 0; 717 boolean_t parse = B_FALSE; 718 const char *fields = NULL; 719 720 optind = 0; 721 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":o:p")) != -1) { 722 switch (c) { 723 case 'o': 724 fields = optarg; 725 break; 726 727 case 'p': 728 parse = B_TRUE; 729 oflags |= OFMT_PARSABLE; 730 break; 731 732 case '?': 733 errx(-1, "unknown option: -%c", optopt); 734 735 case ':': 736 errx(-1, "option -%c requires an argument", optopt); 737 } 738 } 739 740 if (fields != NULL && !parse) { 741 errx(-1, "-o can only be used when in parsable mode (-p)"); 742 } 743 744 if (parse && fields == NULL) { 745 errx(-1, "parsable mode (-p) requires one to specify output " 746 "fields with -o"); 747 } 748 749 if (parse) { 750 ofmt_status_t oferr; 751 752 oferr = ofmt_open(fields, nvme_list_ofmt, oflags, 0, 753 &npa->npa_ofmt); 754 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx); 755 } 756 757 npa->npa_argc -= optind; 758 npa->npa_argv += optind; 759 } 760 761 static int 762 do_list_nsid(int fd, const nvme_process_arg_t *npa) 763 { 764 _NOTE(ARGUNUSED(fd)); 765 char *dskname; 766 767 if (!npa->npa_interactive && 768 (npa->npa_ns_state & NVME_NS_STATE_IGNORED) != 0 && 769 verbose == 0) 770 return (0); 771 772 if (npa->npa_ofmt != NULL) { 773 ofmt_print(npa->npa_ofmt, (void *)npa); 774 return (0); 775 } 776 777 if (npa->npa_ns_state == NVME_NS_STATE_IGNORED) { 778 (void) printf(" %s/%s (unallocated)\n", npa->npa_name, 779 di_minor_name(npa->npa_minor)); 780 } else { 781 if ((npa->npa_ns_state & NVME_NS_STATE_ATTACHED) != 0) { 782 dskname = npa->npa_dsk; 783 } else if ((npa->npa_ns_state & NVME_NS_STATE_ACTIVE) != 0) { 784 if ((npa->npa_ns_state & NVME_NS_STATE_IGNORED) != 0) { 785 dskname = "ignored"; 786 } else { 787 dskname = "unattached"; 788 } 789 } else if ((npa->npa_ns_state & NVME_NS_STATE_ALLOCATED) != 0) { 790 dskname = "inactive"; 791 } else { 792 dskname = "invalid state"; 793 } 794 (void) printf(" %s/%s (%s): ", npa->npa_name, 795 di_minor_name(npa->npa_minor), dskname); 796 nvme_print_nsid_summary(npa->npa_idns); 797 } 798 799 return (0); 800 } 801 802 static int 803 do_list(int fd, const nvme_process_arg_t *npa) 804 { 805 _NOTE(ARGUNUSED(fd)); 806 807 nvme_process_arg_t ns_npa = { 0 }; 808 nvmeadm_cmd_t cmd = { 0 }; 809 char *name; 810 811 if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 812 di_instance(npa->npa_node)) < 0) 813 err(-1, "do_list()"); 814 815 if (npa->npa_ofmt == NULL) { 816 (void) printf("%s: ", name); 817 nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version); 818 } 819 820 ns_npa.npa_name = name; 821 ns_npa.npa_isns = B_TRUE; 822 ns_npa.npa_nsid = npa->npa_nsid; 823 cmd = *(npa->npa_cmd); 824 cmd.c_func = do_list_nsid; 825 ns_npa.npa_cmd = &cmd; 826 ns_npa.npa_ofmt = npa->npa_ofmt; 827 ns_npa.npa_idctl = npa->npa_idctl; 828 829 nvme_walk(&ns_npa, npa->npa_node); 830 831 free(name); 832 833 return (exitcode); 834 } 835 836 static void 837 optparse_identify_ctrl(nvme_process_arg_t *npa) 838 { 839 int c; 840 841 optind = 0; 842 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacn")) != -1) { 843 switch (c) { 844 case 'C': 845 npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS; 846 break; 847 848 case 'a': 849 npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS; 850 break; 851 852 case 'c': 853 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST; 854 break; 855 856 case 'n': 857 npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST; 858 break; 859 860 case '?': 861 errx(-1, "unknown option: -%c", optopt); 862 863 case ':': 864 errx(-1, "option -%c requires an argument", optopt); 865 } 866 } 867 868 npa->npa_argc -= optind; 869 npa->npa_argv += optind; 870 } 871 872 static void 873 usage_identify_ctrl(const char *c_name) 874 { 875 (void) fprintf(stderr, "%s [-C | -c | [-a] -n] <ctl>[,...]\n\n" 876 " Print detailed information about the specified NVMe " 877 "controllers.\n", c_name); 878 } 879 880 static int 881 do_identify_ctrl(int fd, const nvme_process_arg_t *npa) 882 { 883 boolean_t alloc = B_FALSE; 884 885 if (npa->npa_isns) 886 errx(-1, "identify-controller cannot be used on namespaces"); 887 888 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 && 889 npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) { 890 errx(-1, "-C cannot be combined with other flags"); 891 } 892 893 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 && 894 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) { 895 errx(-1, "-c cannot be combined with other flags"); 896 } 897 898 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 && 899 npa->npa_cmdflags != 900 (NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) { 901 errx(-1, "-a can only be used together with -n"); 902 } 903 904 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) { 905 if (!nvme_version_check(npa->npa_version, 1, 2)) { 906 warnx("%s: -a is not supported on NVMe v%u.%u", 907 npa->npa_name, npa->npa_version->v_major, 908 npa->npa_version->v_minor); 909 return (-1); 910 } 911 912 if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) { 913 warnx("%s: Namespace Management not supported", 914 npa->npa_name); 915 return (-1); 916 } 917 918 alloc = B_TRUE; 919 } 920 921 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) { 922 if (!nvme_version_check(npa->npa_version, 1, 2)) { 923 warnx("%s: -C is not supported on NVMe v%u.%u", 924 npa->npa_name, npa->npa_version->v_major, 925 npa->npa_version->v_minor); 926 return (-1); 927 } 928 929 if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) { 930 warnx("%s: Namespace Management not supported", 931 npa->npa_name); 932 return (-1); 933 } 934 935 (void) printf("%s: ", npa->npa_name); 936 nvme_print_identify_nsid(npa->npa_idns, npa->npa_version); 937 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) { 938 char *caption = "Identify Active Namespace List"; 939 nvme_identify_nsid_list_t *idnslist; 940 941 if (!nvme_version_check(npa->npa_version, 1, 1)) { 942 warnx("%s: -n is not supported on NVMe v%u.%u", 943 npa->npa_name, npa->npa_version->v_major, 944 npa->npa_version->v_minor); 945 return (-1); 946 } 947 948 idnslist = nvme_identify(fd, alloc ? 949 NVME_IDENTIFY_NSID_ALLOC_LIST : NVME_IDENTIFY_NSID_LIST); 950 951 if (idnslist == NULL) 952 return (-1); 953 954 if (alloc) 955 caption = "Identify Allocated Namespace List"; 956 957 (void) printf("%s: ", npa->npa_name); 958 959 nvme_print_identify_nsid_list(caption, idnslist); 960 free(idnslist); 961 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) { 962 nvme_identify_ctrl_list_t *ctlist; 963 964 if (!nvme_version_check(npa->npa_version, 1, 2)) { 965 warnx("%s: -c is not supported on NVMe v%u.%u", 966 npa->npa_name, npa->npa_version->v_major, 967 npa->npa_version->v_minor); 968 return (-1); 969 } 970 971 if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) { 972 warnx("%s: Namespace Management not supported", 973 npa->npa_name); 974 return (-1); 975 } 976 977 ctlist = nvme_identify(fd, NVME_IDENTIFY_CTRL_LIST); 978 if (ctlist == NULL) 979 return (-1); 980 981 (void) printf("%s: ", npa->npa_name); 982 nvme_print_identify_ctrl_list("Identify Controller List", 983 ctlist); 984 free(ctlist); 985 } else { 986 nvme_capabilities_t *cap; 987 988 cap = nvme_capabilities(fd); 989 if (cap == NULL) 990 return (-1); 991 992 (void) printf("%s: ", npa->npa_name); 993 nvme_print_identify_ctrl(npa->npa_idctl, cap, npa->npa_version); 994 995 free(cap); 996 } 997 998 return (0); 999 } 1000 1001 static void 1002 optparse_identify_ns(nvme_process_arg_t *npa) 1003 { 1004 int c; 1005 1006 optind = 0; 1007 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":cd")) != -1) { 1008 switch (c) { 1009 case 'c': 1010 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST; 1011 break; 1012 1013 case 'd': 1014 npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST; 1015 break; 1016 1017 case '?': 1018 errx(-1, "unknown option: -%c", optopt); 1019 1020 case ':': 1021 errx(-1, "option -%c requires an argument", optopt); 1022 } 1023 } 1024 1025 npa->npa_argc -= optind; 1026 npa->npa_argv += optind; 1027 } 1028 1029 static void 1030 usage_identify_ns(const char *c_name) 1031 { 1032 (void) fprintf(stderr, "%s [-c | -d ] <ctl>/<ns>[,...]\n\n" 1033 " Print detailed information about the specified NVMe " 1034 "namespaces.\n", c_name); 1035 } 1036 1037 static int 1038 do_identify_ns(int fd, const nvme_process_arg_t *npa) 1039 { 1040 if (!npa->npa_isns) 1041 errx(-1, "identify-namespace cannot be used on controllers"); 1042 1043 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 && 1044 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) { 1045 errx(-1, "-c cannot be combined with other flags"); 1046 } 1047 1048 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 && 1049 npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) { 1050 errx(-1, "-d cannot be combined with other flags"); 1051 } 1052 1053 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) { 1054 errx(-1, "-a cannot be used on namespaces"); 1055 } 1056 1057 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) { 1058 nvme_identify_ctrl_list_t *ctlist; 1059 1060 if (!nvme_version_check(npa->npa_version, 1, 2)) { 1061 warnx("%s: -c is not supported on NVMe v%u.%u", 1062 npa->npa_name, npa->npa_version->v_major, 1063 npa->npa_version->v_minor); 1064 return (-1); 1065 } 1066 1067 if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) { 1068 warnx("%s: Namespace Management not supported", 1069 npa->npa_name); 1070 return (-1); 1071 } 1072 1073 ctlist = nvme_identify(fd, NVME_IDENTIFY_NSID_CTRL_LIST); 1074 if (ctlist == NULL) 1075 return (-1); 1076 1077 (void) printf("%s/%s: ", npa->npa_name, 1078 di_minor_name(npa->npa_minor)); 1079 nvme_print_identify_ctrl_list( 1080 "Identify Attached Controller List", ctlist); 1081 free(ctlist); 1082 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) { 1083 nvme_identify_nsid_desc_t *nsdesc; 1084 1085 if (!nvme_version_check(npa->npa_version, 1, 3)) { 1086 warnx("%s: -d is not supported on NVMe v%u.%u", 1087 npa->npa_name, npa->npa_version->v_major, 1088 npa->npa_version->v_minor); 1089 return (-1); 1090 } 1091 1092 nsdesc = nvme_identify(fd, NVME_IDENTIFY_NSID_DESC); 1093 if (nsdesc == NULL) 1094 return (-1); 1095 1096 (void) printf("%s/%s: ", npa->npa_name, 1097 di_minor_name(npa->npa_minor)); 1098 nvme_print_identify_nsid_desc(nsdesc); 1099 free(nsdesc); 1100 } else { 1101 (void) printf("%s/%s: ", npa->npa_name, 1102 di_minor_name(npa->npa_minor)); 1103 nvme_print_identify_nsid(npa->npa_idns, npa->npa_version); 1104 } 1105 1106 return (0); 1107 } 1108 1109 static void 1110 optparse_identify(nvme_process_arg_t *npa) 1111 { 1112 int c; 1113 1114 optind = 0; 1115 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacdn")) != -1) { 1116 switch (c) { 1117 case 'C': 1118 npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS; 1119 break; 1120 1121 case 'a': 1122 npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS; 1123 break; 1124 1125 case 'c': 1126 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST; 1127 break; 1128 1129 case 'd': 1130 npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST; 1131 break; 1132 1133 case 'n': 1134 npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST; 1135 break; 1136 1137 case '?': 1138 errx(-1, "unknown option: -%c", optopt); 1139 1140 case ':': 1141 errx(-1, "option -%c requires an argument", optopt); 1142 1143 } 1144 } 1145 1146 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 && 1147 (npa->npa_cmdflags & 1148 ~(NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) != 0) { 1149 errx(-1, "-a can only be used alone or together with -n"); 1150 } 1151 1152 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 && 1153 npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) { 1154 errx(-1, "-C cannot be combined with other flags"); 1155 1156 } 1157 1158 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 && 1159 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) { 1160 errx(-1, "-c cannot be combined with other flags"); 1161 } 1162 1163 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 && 1164 npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) { 1165 errx(-1, "-d cannot be combined with other flags"); 1166 } 1167 1168 npa->npa_argc -= optind; 1169 npa->npa_argv += optind; 1170 } 1171 1172 static void 1173 usage_identify(const char *c_name) 1174 { 1175 (void) fprintf(stderr, 1176 "%s [ -C | -c | -d | [-a] -n ] <ctl>[/<ns>][,...]\n\n" 1177 " Print detailed information about the specified NVMe " 1178 "controllers and/or name-\n spaces.\n", c_name); 1179 } 1180 1181 static int 1182 do_identify(int fd, const nvme_process_arg_t *npa) 1183 { 1184 if (npa->npa_isns) { 1185 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) 1186 errx(-1, "-C cannot be used on namespaces"); 1187 1188 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) 1189 errx(-1, "-a cannot be used on namespaces"); 1190 1191 if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) 1192 errx(-1, "-n cannot be used on namespaces"); 1193 1194 return (do_identify_ns(fd, npa)); 1195 } else { 1196 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) 1197 errx(-1, "-d cannot be used on controllers"); 1198 1199 return (do_identify_ctrl(fd, npa)); 1200 } 1201 } 1202 1203 static void 1204 usage_get_logpage(const char *c_name) 1205 { 1206 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n" 1207 " Print the specified log page of the specified NVMe " 1208 "controllers and/or name-\n spaces. Supported log pages " 1209 "are error, health, and firmware.\n", c_name); 1210 } 1211 1212 static void 1213 usage_firmware_list(const char *c_name) 1214 { 1215 (void) fprintf(stderr, "%s <ctl>\n\n" 1216 " Print the log page that contains the list of firmware " 1217 "images installed on the specified NVMe controller.\n", c_name); 1218 } 1219 1220 static int 1221 do_get_logpage_error(int fd, const nvme_process_arg_t *npa) 1222 { 1223 int nlog = npa->npa_idctl->id_elpe + 1; 1224 size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog; 1225 nvme_error_log_entry_t *elog; 1226 1227 if (npa->npa_isns) 1228 errx(-1, "Error Log not available on a per-namespace basis"); 1229 1230 elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize); 1231 1232 if (elog == NULL) 1233 return (-1); 1234 1235 nlog = bufsize / sizeof (nvme_error_log_entry_t); 1236 1237 (void) printf("%s: ", npa->npa_name); 1238 nvme_print_error_log(nlog, elog, npa->npa_version); 1239 1240 free(elog); 1241 1242 return (0); 1243 } 1244 1245 static int 1246 do_get_logpage_health(int fd, const nvme_process_arg_t *npa) 1247 { 1248 size_t bufsize = sizeof (nvme_health_log_t); 1249 nvme_health_log_t *hlog; 1250 1251 if (npa->npa_isns) { 1252 if (npa->npa_idctl->id_lpa.lp_smart == 0) 1253 errx(-1, "SMART/Health information not available " 1254 "on a per-namespace basis on this controller"); 1255 } 1256 1257 hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize); 1258 1259 if (hlog == NULL) 1260 return (-1); 1261 1262 (void) printf("%s: ", npa->npa_name); 1263 nvme_print_health_log(hlog, npa->npa_idctl, npa->npa_version); 1264 1265 free(hlog); 1266 1267 return (0); 1268 } 1269 1270 static int 1271 do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa) 1272 { 1273 size_t bufsize = sizeof (nvme_fwslot_log_t); 1274 nvme_fwslot_log_t *fwlog; 1275 1276 if (npa->npa_isns) 1277 errx(-1, "Firmware Slot information not available on a " 1278 "per-namespace basis"); 1279 1280 fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize); 1281 1282 if (fwlog == NULL) 1283 return (-1); 1284 1285 (void) printf("%s: ", npa->npa_name); 1286 nvme_print_fwslot_log(fwlog, npa->npa_idctl); 1287 1288 free(fwlog); 1289 1290 return (0); 1291 } 1292 1293 static int 1294 do_get_logpage(int fd, const nvme_process_arg_t *npa) 1295 { 1296 int ret = 0; 1297 int (*func)(int, const nvme_process_arg_t *); 1298 1299 if (npa->npa_argc < 1) { 1300 warnx("missing logpage name"); 1301 usage(npa->npa_cmd); 1302 exit(-1); 1303 } 1304 1305 if (strcmp(npa->npa_argv[0], "error") == 0) 1306 func = do_get_logpage_error; 1307 else if (strcmp(npa->npa_argv[0], "health") == 0) 1308 func = do_get_logpage_health; 1309 else if (strcmp(npa->npa_argv[0], "firmware") == 0) 1310 func = do_get_logpage_fwslot; 1311 else 1312 errx(-1, "invalid log page: %s", npa->npa_argv[0]); 1313 1314 if (npa->npa_isns && 1315 (npa->npa_ns_state & NVME_NS_STATE_ACTIVE) == 0) 1316 errx(-1, "cannot get logpage: namespace is inactive"); 1317 1318 ret = func(fd, npa); 1319 return (ret); 1320 } 1321 1322 static void 1323 usage_get_features(const char *c_name) 1324 { 1325 const nvme_feature_t *feat; 1326 1327 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n" 1328 " Print the specified features of the specified NVMe controllers " 1329 "and/or\n namespaces. Supported features are:\n\n", c_name); 1330 (void) fprintf(stderr, " %-35s %-14s %s\n", 1331 "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE"); 1332 for (feat = &features[0]; feat->f_feature != 0; feat++) { 1333 char *type; 1334 1335 if ((feat->f_getflags & NVMEADM_F_BOTH) == NVMEADM_F_BOTH) 1336 type = "both"; 1337 else if ((feat->f_getflags & NVMEADM_F_CTRL) != 0) 1338 type = "controller only"; 1339 else 1340 type = "namespace only"; 1341 1342 (void) fprintf(stderr, " %-35s %-14s %s\n", 1343 feat->f_name, feat->f_short, type); 1344 } 1345 1346 } 1347 1348 static int 1349 do_get_feat_common(int fd, const nvme_feature_t *feat, 1350 const nvme_process_arg_t *npa) 1351 { 1352 void *buf = NULL; 1353 size_t bufsize = feat->f_bufsize; 1354 uint64_t res; 1355 1356 if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf) 1357 == B_FALSE) 1358 return (EINVAL); 1359 1360 nvme_print(2, feat->f_name, -1, NULL); 1361 feat->f_print(res, buf, bufsize, npa->npa_idctl, npa->npa_version); 1362 free(buf); 1363 1364 return (0); 1365 } 1366 1367 static int 1368 do_get_feat_temp_thresh_one(int fd, const nvme_feature_t *feat, 1369 const char *label, uint16_t tmpsel, uint16_t thsel, 1370 const nvme_process_arg_t *npa) 1371 { 1372 uint64_t res; 1373 void *buf = NULL; 1374 size_t bufsize = feat->f_bufsize; 1375 nvme_temp_threshold_t tt; 1376 1377 tt.r = 0; 1378 tt.b.tt_tmpsel = tmpsel; 1379 tt.b.tt_thsel = thsel; 1380 1381 if (!nvme_get_feature(fd, feat->f_feature, tt.r, &res, &bufsize, 1382 &buf)) { 1383 return (EINVAL); 1384 } 1385 1386 feat->f_print(res, (void *)label, 0, npa->npa_idctl, npa->npa_version); 1387 free(buf); 1388 return (0); 1389 } 1390 1391 /* 1392 * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the 1393 * device and changed the main device to have a composite temperature sensor. As 1394 * a result, there is a set of thresholds for each sensor. In addition, they 1395 * added both an over-temperature and under-temperature threshold. Since most 1396 * devices don't actually implement all the sensors, we get the health page and 1397 * see which sensors have a non-zero value to determine how to proceed. 1398 */ 1399 static int 1400 do_get_feat_temp_thresh(int fd, const nvme_feature_t *feat, 1401 const nvme_process_arg_t *npa) 1402 { 1403 int ret; 1404 size_t bufsize = sizeof (nvme_health_log_t); 1405 nvme_health_log_t *hlog; 1406 1407 nvme_print(2, feat->f_name, -1, NULL); 1408 if ((ret = do_get_feat_temp_thresh_one(fd, feat, 1409 "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER, 1410 npa)) != 0) { 1411 return (ret); 1412 } 1413 1414 if (!nvme_version_check(npa->npa_version, 1, 2)) { 1415 return (0); 1416 } 1417 1418 if ((ret = do_get_feat_temp_thresh_one(fd, feat, 1419 "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER, 1420 npa)) != 0) { 1421 return (ret); 1422 } 1423 1424 hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize); 1425 if (hlog == NULL) { 1426 warnx("failed to get health log page, unable to get " 1427 "thresholds for additional sensors"); 1428 return (0); 1429 } 1430 1431 if (hlog->hl_temp_sensor_1 != 0) { 1432 (void) do_get_feat_temp_thresh_one(fd, feat, 1433 "Temp. Sensor 1 Over Temp. Threshold", 1, 1434 NVME_TEMP_THRESH_OVER, npa); 1435 (void) do_get_feat_temp_thresh_one(fd, feat, 1436 "Temp. Sensor 1 Under Temp. Threshold", 1, 1437 NVME_TEMP_THRESH_UNDER, npa); 1438 } 1439 1440 if (hlog->hl_temp_sensor_2 != 0) { 1441 (void) do_get_feat_temp_thresh_one(fd, feat, 1442 "Temp. Sensor 2 Over Temp. Threshold", 2, 1443 NVME_TEMP_THRESH_OVER, npa); 1444 (void) do_get_feat_temp_thresh_one(fd, feat, 1445 "Temp. Sensor 2 Under Temp. Threshold", 2, 1446 NVME_TEMP_THRESH_UNDER, npa); 1447 } 1448 1449 if (hlog->hl_temp_sensor_3 != 0) { 1450 (void) do_get_feat_temp_thresh_one(fd, feat, 1451 "Temp. Sensor 3 Over Temp. Threshold", 3, 1452 NVME_TEMP_THRESH_OVER, npa); 1453 (void) do_get_feat_temp_thresh_one(fd, feat, 1454 "Temp. Sensor 3 Under Temp. Threshold", 3, 1455 NVME_TEMP_THRESH_UNDER, npa); 1456 } 1457 1458 if (hlog->hl_temp_sensor_4 != 0) { 1459 (void) do_get_feat_temp_thresh_one(fd, feat, 1460 "Temp. Sensor 4 Over Temp. Threshold", 4, 1461 NVME_TEMP_THRESH_OVER, npa); 1462 (void) do_get_feat_temp_thresh_one(fd, feat, 1463 "Temp. Sensor 4 Under Temp. Threshold", 4, 1464 NVME_TEMP_THRESH_UNDER, npa); 1465 } 1466 1467 if (hlog->hl_temp_sensor_5 != 0) { 1468 (void) do_get_feat_temp_thresh_one(fd, feat, 1469 "Temp. Sensor 5 Over Temp. Threshold", 5, 1470 NVME_TEMP_THRESH_OVER, npa); 1471 (void) do_get_feat_temp_thresh_one(fd, feat, 1472 "Temp. Sensor 5 Under Temp. Threshold", 5, 1473 NVME_TEMP_THRESH_UNDER, npa); 1474 } 1475 1476 if (hlog->hl_temp_sensor_6 != 0) { 1477 (void) do_get_feat_temp_thresh_one(fd, feat, 1478 "Temp. Sensor 6 Over Temp. Threshold", 6, 1479 NVME_TEMP_THRESH_OVER, npa); 1480 (void) do_get_feat_temp_thresh_one(fd, feat, 1481 "Temp. Sensor 6 Under Temp. Threshold", 6, 1482 NVME_TEMP_THRESH_UNDER, npa); 1483 } 1484 1485 if (hlog->hl_temp_sensor_7 != 0) { 1486 (void) do_get_feat_temp_thresh_one(fd, feat, 1487 "Temp. Sensor 7 Over Temp. Threshold", 7, 1488 NVME_TEMP_THRESH_OVER, npa); 1489 (void) do_get_feat_temp_thresh_one(fd, feat, 1490 "Temp. Sensor 7 Under Temp. Threshold", 7, 1491 NVME_TEMP_THRESH_UNDER, npa); 1492 } 1493 1494 if (hlog->hl_temp_sensor_8 != 0) { 1495 (void) do_get_feat_temp_thresh_one(fd, feat, 1496 "Temp. Sensor 8 Over Temp. Threshold", 8, 1497 NVME_TEMP_THRESH_OVER, npa); 1498 (void) do_get_feat_temp_thresh_one(fd, feat, 1499 "Temp. Sensor 8 Under Temp. Threshold", 8, 1500 NVME_TEMP_THRESH_UNDER, npa); 1501 } 1502 free(hlog); 1503 return (0); 1504 } 1505 1506 static int 1507 do_get_feat_intr_vect(int fd, const nvme_feature_t *feat, 1508 const nvme_process_arg_t *npa) 1509 { 1510 uint64_t res; 1511 uint64_t arg; 1512 int intr_cnt; 1513 1514 intr_cnt = nvme_intr_cnt(fd); 1515 1516 if (intr_cnt == -1) 1517 return (EINVAL); 1518 1519 nvme_print(2, feat->f_name, -1, NULL); 1520 1521 for (arg = 0; arg < intr_cnt; arg++) { 1522 if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL) 1523 == B_FALSE) 1524 return (EINVAL); 1525 1526 feat->f_print(res, NULL, 0, npa->npa_idctl, npa->npa_version); 1527 } 1528 1529 return (0); 1530 } 1531 1532 static int 1533 do_get_features(int fd, const nvme_process_arg_t *npa) 1534 { 1535 const nvme_feature_t *feat; 1536 char *f, *flist, *lasts; 1537 boolean_t header_printed = B_FALSE; 1538 1539 if (npa->npa_argc > 1) 1540 errx(-1, "unexpected arguments"); 1541 1542 if (npa->npa_isns && 1543 (npa->npa_ns_state & NVME_NS_STATE_ACTIVE) == 0) 1544 errx(-1, "cannot get feature: namespace is inactive"); 1545 1546 /* 1547 * No feature list given, print all supported features. 1548 */ 1549 if (npa->npa_argc == 0) { 1550 (void) printf("%s: Get Features\n", npa->npa_name); 1551 for (feat = &features[0]; feat->f_feature != 0; feat++) { 1552 if ((npa->npa_isns && 1553 (feat->f_getflags & NVMEADM_F_NS) == 0) || 1554 (!npa->npa_isns && 1555 (feat->f_getflags & NVMEADM_F_CTRL) == 0)) 1556 continue; 1557 1558 (void) feat->f_get(fd, feat, npa); 1559 } 1560 1561 return (0); 1562 } 1563 1564 /* 1565 * Process feature list. 1566 */ 1567 flist = strdup(npa->npa_argv[0]); 1568 if (flist == NULL) 1569 err(-1, "do_get_features"); 1570 1571 for (f = strtok_r(flist, ",", &lasts); 1572 f != NULL; 1573 f = strtok_r(NULL, ",", &lasts)) { 1574 while (isspace(*f)) 1575 f++; 1576 1577 for (feat = &features[0]; feat->f_feature != 0; feat++) { 1578 if (strncasecmp(feat->f_name, f, strlen(f)) == 0 || 1579 strncasecmp(feat->f_short, f, strlen(f)) == 0) 1580 break; 1581 } 1582 1583 if (feat->f_feature == 0) { 1584 warnx("unknown feature %s", f); 1585 continue; 1586 } 1587 1588 if ((npa->npa_isns && 1589 (feat->f_getflags & NVMEADM_F_NS) == 0) || 1590 (!npa->npa_isns && 1591 (feat->f_getflags & NVMEADM_F_CTRL) == 0)) { 1592 warnx("feature %s %s supported for namespaces", 1593 feat->f_name, 1594 (feat->f_getflags & NVMEADM_F_NS) != 0 ? 1595 "only" : "not"); 1596 continue; 1597 } 1598 1599 if (!header_printed) { 1600 (void) printf("%s: Get Features\n", npa->npa_name); 1601 header_printed = B_TRUE; 1602 } 1603 1604 if (feat->f_get(fd, feat, npa) != 0) { 1605 warnx("unsupported feature: %s", feat->f_name); 1606 continue; 1607 } 1608 } 1609 1610 free(flist); 1611 return (0); 1612 } 1613 1614 static int 1615 do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf, 1616 unsigned long ses) 1617 { 1618 nvme_process_arg_t ns_npa = { 0 }; 1619 nvmeadm_cmd_t cmd = { 0 }; 1620 1621 if (npa->npa_isns && 1622 (npa->npa_ns_state & NVME_NS_STATE_ACTIVE) == 0) { 1623 errx(-1, "cannot %s: namespace is inactive", 1624 npa->npa_cmd->c_name); 1625 } 1626 1627 cmd = *(npa->npa_cmd); 1628 cmd.c_func = do_attach_detach; 1629 cmd.c_name = "detach"; 1630 ns_npa = *npa; 1631 ns_npa.npa_cmd = &cmd; 1632 1633 if (do_attach_detach(fd, &ns_npa) != 0) 1634 return (exitcode); 1635 if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) { 1636 warn("%s failed", npa->npa_cmd->c_name); 1637 exitcode += -1; 1638 } 1639 cmd.c_name = "attach"; 1640 exitcode += do_attach_detach(fd, &ns_npa); 1641 1642 return (exitcode); 1643 } 1644 1645 static void 1646 usage_format(const char *c_name) 1647 { 1648 (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n" 1649 " Format one or all namespaces of the specified NVMe " 1650 "controller. Supported LBA\n formats can be queried with " 1651 "the \"%s identify\" command on the namespace\n to be " 1652 "formatted.\n", c_name, getprogname()); 1653 } 1654 1655 static int 1656 do_format(int fd, const nvme_process_arg_t *npa) 1657 { 1658 unsigned long lbaf; 1659 1660 if (npa->npa_idctl->id_oacs.oa_format == 0) 1661 errx(-1, "%s not supported", npa->npa_cmd->c_name); 1662 1663 if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0) 1664 errx(-1, "%s not supported on individual namespace", 1665 npa->npa_cmd->c_name); 1666 1667 1668 if (npa->npa_argc > 0) { 1669 errno = 0; 1670 lbaf = strtoul(npa->npa_argv[0], NULL, 10); 1671 1672 if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF) 1673 errx(-1, "invalid LBA format %d", lbaf + 1); 1674 1675 if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0) 1676 errx(-1, "LBA formats with metadata not supported"); 1677 } else { 1678 lbaf = npa->npa_idns->id_flbas.lba_format; 1679 } 1680 1681 return (do_format_common(fd, npa, lbaf, 0)); 1682 } 1683 1684 static void 1685 usage_secure_erase(const char *c_name) 1686 { 1687 (void) fprintf(stderr, "%s [-c] <ctl>[/<ns>]\n\n" 1688 " Secure-Erase one or all namespaces of the specified " 1689 "NVMe controller.\n", c_name); 1690 } 1691 1692 static void 1693 optparse_secure_erase(nvme_process_arg_t *npa) 1694 { 1695 int c; 1696 1697 optind = 0; 1698 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":c")) != -1) { 1699 switch (c) { 1700 case 'c': 1701 npa->npa_cmdflags |= NVMEADM_O_SE_CRYPTO; 1702 break; 1703 1704 case '?': 1705 errx(-1, "unknown option: -%c", optopt); 1706 1707 case ':': 1708 errx(-1, "option -%c requires an argument", optopt); 1709 1710 } 1711 } 1712 1713 npa->npa_argc -= optind; 1714 npa->npa_argv += optind; 1715 } 1716 1717 static int 1718 do_secure_erase(int fd, const nvme_process_arg_t *npa) 1719 { 1720 unsigned long lbaf; 1721 uint8_t ses = NVME_FRMT_SES_USER; 1722 1723 if (npa->npa_argc > 0) 1724 errx(-1, "Too many arguments"); 1725 1726 if (npa->npa_idctl->id_oacs.oa_format == 0) 1727 errx(-1, "%s not supported", npa->npa_cmd->c_name); 1728 1729 if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0) 1730 errx(-1, "%s not supported on individual namespace", 1731 npa->npa_cmd->c_name); 1732 1733 if ((npa->npa_cmdflags & NVMEADM_O_SE_CRYPTO) != 0) 1734 ses = NVME_FRMT_SES_CRYPTO; 1735 1736 if (ses == NVME_FRMT_SES_CRYPTO && 1737 npa->npa_idctl->id_fna.fn_crypt_erase == 0) 1738 errx(-1, "cryptographic %s not supported", 1739 npa->npa_cmd->c_name); 1740 1741 lbaf = npa->npa_idns->id_flbas.lba_format; 1742 1743 return (do_format_common(fd, npa, lbaf, ses)); 1744 } 1745 1746 static void 1747 usage_attach_detach(const char *c_name) 1748 { 1749 (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n" 1750 " %c%s blkdev(4D) %s one or all namespaces of the " 1751 "specified NVMe controller.\n", 1752 c_name, toupper(c_name[0]), &c_name[1], 1753 c_name[0] == 'd' ? "from" : "to"); 1754 } 1755 1756 static int 1757 do_attach_detach(int fd, const nvme_process_arg_t *npa) 1758 { 1759 char *c_name = npa->npa_cmd->c_name; 1760 1761 if (!npa->npa_isns) { 1762 nvme_process_arg_t ns_npa = { 0 }; 1763 1764 ns_npa.npa_name = npa->npa_name; 1765 ns_npa.npa_isns = B_TRUE; 1766 ns_npa.npa_cmd = npa->npa_cmd; 1767 ns_npa.npa_excl = npa->npa_excl; 1768 1769 nvme_walk(&ns_npa, npa->npa_node); 1770 1771 return (exitcode); 1772 } 1773 1774 /* 1775 * Unless the user interactively requested a particular namespace to be 1776 * attached or detached, don't even try to attach or detach namespaces 1777 * that are ignored by the driver, thereby avoiding printing pointless 1778 * error messages. 1779 */ 1780 if (!npa->npa_interactive && 1781 (npa->npa_ns_state & NVME_NS_STATE_IGNORED)) 1782 return (0); 1783 1784 if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd) 1785 == B_FALSE) { 1786 warn("%s failed", c_name); 1787 return (-1); 1788 } 1789 1790 return (0); 1791 } 1792 1793 static void 1794 usage_firmware_load(const char *c_name) 1795 { 1796 (void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n" 1797 " Load firmware <image file> to offset <offset>.\n" 1798 " The firmware needs to be committed to a slot using " 1799 "\"nvmeadm commit-firmware\"\n command.\n", c_name); 1800 } 1801 1802 /* 1803 * Read exactly len bytes, or until eof. 1804 */ 1805 static ssize_t 1806 read_block(int fd, char *buf, size_t len) 1807 { 1808 size_t remain; 1809 ssize_t bytes; 1810 1811 remain = len; 1812 while (remain > 0) { 1813 bytes = read(fd, buf, remain); 1814 if (bytes == 0) 1815 break; 1816 1817 if (bytes < 0) { 1818 if (errno == EINTR) 1819 continue; 1820 1821 return (-1); 1822 } 1823 1824 buf += bytes; 1825 remain -= bytes; 1826 } 1827 1828 return (len - remain); 1829 } 1830 1831 /* 1832 * Convert a string to a valid firmware upload offset (in bytes). 1833 */ 1834 static offset_t 1835 get_fw_offsetb(char *str) 1836 { 1837 longlong_t offsetb; 1838 char *valend; 1839 1840 errno = 0; 1841 offsetb = strtoll(str, &valend, 0); 1842 if (errno != 0 || *valend != '\0' || offsetb < 0 || 1843 offsetb > NVME_FW_OFFSETB_MAX) 1844 errx(-1, "Offset must be numeric and in the range of 0 to %llu", 1845 NVME_FW_OFFSETB_MAX); 1846 1847 if ((offsetb & NVME_DWORD_MASK) != 0) 1848 errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE); 1849 1850 return ((offset_t)offsetb); 1851 } 1852 1853 #define FIRMWARE_READ_BLKSIZE (64 * 1024) /* 64K */ 1854 1855 static int 1856 do_firmware_load(int fd, const nvme_process_arg_t *npa) 1857 { 1858 int fw_fd; 1859 ssize_t len; 1860 offset_t offset = 0; 1861 size_t size; 1862 uint16_t sc; 1863 char buf[FIRMWARE_READ_BLKSIZE]; 1864 1865 if (npa->npa_argc > 2) 1866 errx(-1, "Too many arguments"); 1867 1868 if (npa->npa_argc == 0) 1869 errx(-1, "Requires firmware file name, and an " 1870 "optional offset"); 1871 1872 if (npa->npa_isns) 1873 errx(-1, "Firmware loading not available on a per-namespace " 1874 "basis"); 1875 1876 if (npa->npa_argc == 2) 1877 offset = get_fw_offsetb(npa->npa_argv[1]); 1878 1879 fw_fd = open(npa->npa_argv[0], O_RDONLY); 1880 if (fw_fd < 0) 1881 errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0], 1882 strerror(errno)); 1883 1884 size = 0; 1885 do { 1886 len = read_block(fw_fd, buf, sizeof (buf)); 1887 1888 if (len < 0) 1889 errx(-1, "Error reading \"%s\": %s", npa->npa_argv[0], 1890 strerror(errno)); 1891 1892 if (len == 0) 1893 break; 1894 1895 if (!nvme_firmware_load(fd, buf, len, offset, &sc)) 1896 errx(-1, "Error loading \"%s\": %s", npa->npa_argv[0], 1897 nvme_fw_error(errno, sc)); 1898 1899 offset += len; 1900 size += len; 1901 } while (len == sizeof (buf)); 1902 1903 (void) close(fw_fd); 1904 1905 if (verbose) 1906 (void) printf("%zu bytes downloaded.\n", size); 1907 1908 return (0); 1909 } 1910 1911 /* 1912 * Convert str to a valid firmware slot number. 1913 */ 1914 static uint_t 1915 get_slot_number(char *str) 1916 { 1917 longlong_t slot; 1918 char *valend; 1919 1920 errno = 0; 1921 slot = strtoll(str, &valend, 0); 1922 if (errno != 0 || *valend != '\0' || 1923 slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX) 1924 errx(-1, "Slot must be numeric and in the range of %d to %d", 1925 NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX); 1926 1927 return ((uint_t)slot); 1928 } 1929 1930 static void 1931 usage_firmware_commit(const char *c_name) 1932 { 1933 (void) fprintf(stderr, "%s <ctl> <slot>\n\n" 1934 " Commit previously downloaded firmware to slot <slot>.\n" 1935 " The firmware is only activated after a " 1936 "\"nvmeadm activate-firmware\" command.\n", c_name); 1937 } 1938 1939 static int 1940 do_firmware_commit(int fd, const nvme_process_arg_t *npa) 1941 { 1942 uint_t slot; 1943 uint16_t sc; 1944 1945 if (npa->npa_argc > 1) 1946 errx(-1, "Too many arguments"); 1947 1948 if (npa->npa_argc == 0) 1949 errx(-1, "Firmware slot number is required"); 1950 1951 if (npa->npa_isns) 1952 errx(-1, "Firmware committing not available on a per-namespace " 1953 "basis"); 1954 1955 slot = get_slot_number(npa->npa_argv[0]); 1956 1957 if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly) 1958 errx(-1, "Cannot commit firmware to slot 1: slot is read-only"); 1959 1960 if (!nvme_firmware_commit(fd, slot, NVME_FWC_SAVE, &sc)) 1961 errx(-1, "Failed to commit firmware to slot %u: %s", 1962 slot, nvme_fw_error(errno, sc)); 1963 1964 if (verbose) 1965 (void) printf("Firmware committed to slot %u.\n", slot); 1966 1967 return (0); 1968 } 1969 1970 static void 1971 usage_firmware_activate(const char *c_name) 1972 { 1973 (void) fprintf(stderr, "%s <ctl> <slot>\n\n" 1974 " Activate firmware in slot <slot>.\n" 1975 " The firmware will be in use after the next system reset.\n", 1976 c_name); 1977 } 1978 1979 static int 1980 do_firmware_activate(int fd, const nvme_process_arg_t *npa) 1981 { 1982 uint_t slot; 1983 uint16_t sc; 1984 1985 if (npa->npa_argc > 1) 1986 errx(-1, "Too many arguments"); 1987 1988 if (npa->npa_argc == 0) 1989 errx(-1, "Firmware slot number is required"); 1990 1991 if (npa->npa_isns) 1992 errx(-1, "Firmware activation not available on a per-namespace " 1993 "basis"); 1994 1995 slot = get_slot_number(npa->npa_argv[0]); 1996 1997 if (!nvme_firmware_commit(fd, slot, NVME_FWC_ACTIVATE, &sc)) 1998 errx(-1, "Failed to activate slot %u: %s", slot, 1999 nvme_fw_error(errno, sc)); 2000 2001 if (verbose) 2002 printf("Slot %u activated: %s.\n", slot, 2003 nvme_fw_error(errno, sc)); 2004 2005 return (0); 2006 } 2007 2008 /* 2009 * While the NVME_VERSION_ATLEAST macro exists, specifying a version of 1.0 2010 * causes GCC to helpfully flag the -Wtype-limits warning because a uint_t is 2011 * always >= 0. In many cases it's useful to always indicate what version 2012 * something was added in to simplify code (e.g. nvmeadm_print_bit) and we'd 2013 * rather just say it's version 1.0 rather than making folks realize that a 2014 * hardcoded true is equivalent. Therefore we have this function which can't 2015 * trigger this warning today (and adds a minor amount of type safety). If GCC 2016 * or clang get smart enough to see through this, then we'll have to just 2017 * disable the warning for the single minor comparison (and reformat this a bit 2018 * to minimize the impact). 2019 */ 2020 boolean_t 2021 nvme_version_check(nvme_version_t *vers, uint_t major, uint_t minor) 2022 { 2023 if (vers->v_major > major) { 2024 return (B_TRUE); 2025 } 2026 2027 return (vers->v_major == major && vers->v_minor >= minor); 2028 } 2029