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