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