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