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