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