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