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