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