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