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