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