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