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