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