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 char *npa_nsid; 58 int npa_found; 59 boolean_t npa_isns; 60 const nvmeadm_cmd_t *npa_cmd; 61 di_node_t npa_node; 62 di_minor_t npa_minor; 63 char *npa_path; 64 char *npa_dsk; 65 nvme_identify_ctrl_t *npa_idctl; 66 nvme_identify_nsid_t *npa_idns; 67 nvme_version_t *npa_version; 68 }; 69 70 struct nvme_feature { 71 char *f_name; 72 char *f_short; 73 uint8_t f_feature; 74 size_t f_bufsize; 75 uint_t f_getflags; 76 int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *); 77 void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *); 78 }; 79 80 #define NVMEADM_CTRL 1 81 #define NVMEADM_NS 2 82 #define NVMEADM_BOTH (NVMEADM_CTRL | NVMEADM_NS) 83 84 struct nvmeadm_cmd { 85 char *c_name; 86 char *c_desc; 87 char *c_flagdesc; 88 int (*c_func)(int, const nvme_process_arg_t *); 89 void (*c_usage)(const char *); 90 boolean_t c_multi; 91 }; 92 93 94 static void usage(const nvmeadm_cmd_t *); 95 static void nvme_walk(nvme_process_arg_t *, di_node_t); 96 static boolean_t nvme_match(nvme_process_arg_t *); 97 98 static int nvme_process(di_node_t, di_minor_t, void *); 99 100 static int do_list(int, const nvme_process_arg_t *); 101 static int do_identify(int, const nvme_process_arg_t *); 102 static int do_get_logpage_error(int, const nvme_process_arg_t *); 103 static int do_get_logpage_health(int, const nvme_process_arg_t *); 104 static int do_get_logpage_fwslot(int, const nvme_process_arg_t *); 105 static int do_get_logpage(int, const nvme_process_arg_t *); 106 static int do_get_feat_common(int, const nvme_feature_t *, 107 nvme_identify_ctrl_t *); 108 static int do_get_feat_intr_vect(int, const nvme_feature_t *, 109 nvme_identify_ctrl_t *); 110 static int do_get_features(int, const nvme_process_arg_t *); 111 static int do_format(int, const nvme_process_arg_t *); 112 static int do_secure_erase(int, const nvme_process_arg_t *); 113 static int do_attach_detach(int, const nvme_process_arg_t *); 114 115 static void usage_list(const char *); 116 static void usage_identify(const char *); 117 static void usage_get_logpage(const char *); 118 static void usage_get_features(const char *); 119 static void usage_format(const char *); 120 static void usage_secure_erase(const char *); 121 static void usage_attach_detach(const char *); 122 123 int verbose; 124 int debug; 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 *tmp++ = '\0'; 317 npa.npa_nsid = tmp; 318 npa.npa_isns = B_TRUE; 319 } 320 } 321 322 if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL) 323 err(-1, "failed to initialize libdevinfo"); 324 nvme_walk(&npa, node); 325 di_fini(node); 326 327 if (npa.npa_found == 0) { 328 if (npa.npa_name != NULL) { 329 warnx("%s%.*s%.*s: no such controller or " 330 "namespace", npa.npa_name, 331 npa.npa_isns ? -1 : 0, "/", 332 npa.npa_isns ? -1 : 0, npa.npa_nsid); 333 } else { 334 warnx("no controllers found"); 335 } 336 exitcode--; 337 } 338 npa.npa_found = 0; 339 npa.npa_name = strtok_r(NULL, ",", &lasts); 340 } while (npa.npa_name != NULL); 341 342 exit(exitcode); 343 } 344 345 static void 346 usage(const nvmeadm_cmd_t *cmd) 347 { 348 (void) fprintf(stderr, "usage:\n"); 349 (void) fprintf(stderr, " %s -h %s\n", getprogname(), 350 cmd != NULL ? cmd->c_name : "[<command>]"); 351 (void) fprintf(stderr, " %s [-dv] ", getprogname()); 352 353 if (cmd != NULL) { 354 cmd->c_usage(cmd->c_name); 355 } else { 356 (void) fprintf(stderr, 357 "<command> <ctl>[/<ns>][,...] [<args>]\n"); 358 (void) fprintf(stderr, 359 "\n Manage NVMe controllers and namespaces.\n"); 360 (void) fprintf(stderr, "\ncommands:\n"); 361 362 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) 363 (void) fprintf(stderr, " %-15s - %s\n", 364 cmd->c_name, cmd->c_desc); 365 } 366 (void) fprintf(stderr, "\nflags:\n" 367 " -h print usage information\n" 368 " -d print information useful for debugging %s\n" 369 " -v print verbose information\n", getprogname()); 370 if (cmd != NULL && cmd->c_flagdesc != NULL) 371 (void) fprintf(stderr, "%s\n", cmd->c_flagdesc); 372 } 373 374 static boolean_t 375 nvme_match(nvme_process_arg_t *npa) 376 { 377 char *name; 378 char *nsid = NULL; 379 380 if (npa->npa_name == NULL) 381 return (B_TRUE); 382 383 if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 384 di_instance(npa->npa_node)) < 0) 385 err(-1, "nvme_match()"); 386 387 if (strcmp(name, npa->npa_name) != 0) { 388 free(name); 389 return (B_FALSE); 390 } 391 392 free(name); 393 394 if (npa->npa_isns) { 395 if (npa->npa_nsid == NULL) 396 return (B_TRUE); 397 398 nsid = di_minor_name(npa->npa_minor); 399 400 if (nsid == NULL || strcmp(npa->npa_nsid, nsid) != 0) 401 return (B_FALSE); 402 } 403 404 return (B_TRUE); 405 } 406 407 char * 408 nvme_dskname(const nvme_process_arg_t *npa) 409 { 410 char *path = NULL; 411 di_node_t child; 412 di_dim_t dim; 413 char *addr; 414 415 dim = di_dim_init(); 416 417 for (child = di_child_node(npa->npa_node); 418 child != DI_NODE_NIL; 419 child = di_sibling_node(child)) { 420 addr = di_bus_addr(child); 421 if (addr == NULL) 422 continue; 423 424 if (addr[0] == 'w') 425 addr++; 426 427 if (strncasecmp(addr, di_minor_name(npa->npa_minor), 428 strchrnul(addr, ',') - addr) != 0) 429 continue; 430 431 path = di_dim_path_dev(dim, di_driver_name(child), 432 di_instance(child), "c"); 433 434 if (path != NULL) { 435 path[strlen(path) - 2] = '\0'; 436 path = strrchr(path, '/') + 1; 437 if (path != NULL) { 438 path = strdup(path); 439 if (path == NULL) 440 err(-1, "nvme_dskname"); 441 } 442 } 443 444 break; 445 } 446 447 di_dim_fini(dim); 448 return (path); 449 } 450 451 static int 452 nvme_process(di_node_t node, di_minor_t minor, void *arg) 453 { 454 nvme_process_arg_t *npa = arg; 455 int fd; 456 457 npa->npa_node = node; 458 npa->npa_minor = minor; 459 460 if (!nvme_match(npa)) 461 return (DI_WALK_CONTINUE); 462 463 if ((fd = nvme_open(minor)) < 0) 464 return (DI_WALK_CONTINUE); 465 466 npa->npa_found++; 467 468 npa->npa_path = di_devfs_path(node); 469 if (npa->npa_path == NULL) 470 goto out; 471 472 npa->npa_version = nvme_version(fd); 473 if (npa->npa_version == NULL) 474 goto out; 475 476 npa->npa_idctl = nvme_identify_ctrl(fd); 477 if (npa->npa_idctl == NULL) 478 goto out; 479 480 npa->npa_idns = nvme_identify_nsid(fd); 481 if (npa->npa_idns == NULL) 482 goto out; 483 484 if (npa->npa_isns) 485 npa->npa_dsk = nvme_dskname(npa); 486 487 exitcode += npa->npa_cmd->c_func(fd, npa); 488 489 out: 490 di_devfs_path_free(npa->npa_path); 491 free(npa->npa_dsk); 492 free(npa->npa_version); 493 free(npa->npa_idctl); 494 free(npa->npa_idns); 495 496 npa->npa_version = NULL; 497 npa->npa_idctl = NULL; 498 npa->npa_idns = NULL; 499 500 nvme_close(fd); 501 502 return (DI_WALK_CONTINUE); 503 } 504 505 static void 506 nvme_walk(nvme_process_arg_t *npa, di_node_t node) 507 { 508 char *minor_nodetype = DDI_NT_NVME_NEXUS; 509 510 if (npa->npa_isns) 511 minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT; 512 513 (void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process); 514 } 515 516 static void 517 usage_list(const char *c_name) 518 { 519 (void) fprintf(stderr, "%s [<ctl>[/<ns>][,...]\n\n" 520 " List NVMe controllers and their namespaces. If no " 521 "controllers and/or name-\n spaces are specified, all " 522 "controllers and namespaces in the system will be\n " 523 "listed.\n", c_name); 524 } 525 526 static int 527 do_list_nsid(int fd, const nvme_process_arg_t *npa) 528 { 529 _NOTE(ARGUNUSED(fd)); 530 531 (void) printf(" %s/%s (%s): ", npa->npa_name, 532 di_minor_name(npa->npa_minor), 533 npa->npa_dsk != NULL ? npa->npa_dsk : "unattached"); 534 nvme_print_nsid_summary(npa->npa_idns); 535 536 return (0); 537 } 538 539 static int 540 do_list(int fd, const nvme_process_arg_t *npa) 541 { 542 _NOTE(ARGUNUSED(fd)); 543 544 nvme_process_arg_t ns_npa = { 0 }; 545 nvmeadm_cmd_t cmd = { 0 }; 546 char *name; 547 548 if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 549 di_instance(npa->npa_node)) < 0) 550 err(-1, "do_list()"); 551 552 (void) printf("%s: ", name); 553 nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version); 554 555 ns_npa.npa_name = name; 556 ns_npa.npa_isns = B_TRUE; 557 ns_npa.npa_nsid = npa->npa_nsid; 558 cmd = *(npa->npa_cmd); 559 cmd.c_func = do_list_nsid; 560 ns_npa.npa_cmd = &cmd; 561 562 nvme_walk(&ns_npa, npa->npa_node); 563 564 free(name); 565 566 return (exitcode); 567 } 568 569 static void 570 usage_identify(const char *c_name) 571 { 572 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n" 573 " Print detailed information about the specified NVMe " 574 "controllers and/or name-\n spaces.\n", c_name); 575 } 576 577 static int 578 do_identify(int fd, const nvme_process_arg_t *npa) 579 { 580 if (!npa->npa_isns) { 581 nvme_capabilities_t *cap; 582 583 cap = nvme_capabilities(fd); 584 if (cap == NULL) 585 return (-1); 586 587 (void) printf("%s: ", npa->npa_name); 588 nvme_print_identify_ctrl(npa->npa_idctl, cap, 589 npa->npa_version); 590 591 free(cap); 592 } else { 593 (void) printf("%s/%s: ", npa->npa_name, 594 di_minor_name(npa->npa_minor)); 595 nvme_print_identify_nsid(npa->npa_idns, 596 npa->npa_version); 597 } 598 599 return (0); 600 } 601 602 static void 603 usage_get_logpage(const char *c_name) 604 { 605 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n" 606 " Print the specified log page of the specified NVMe " 607 "controllers and/or name-\n spaces. Supported log pages " 608 "are error, health, and firmware.\n", c_name); 609 } 610 611 static int 612 do_get_logpage_error(int fd, const nvme_process_arg_t *npa) 613 { 614 int nlog = npa->npa_idctl->id_elpe + 1; 615 size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog; 616 nvme_error_log_entry_t *elog; 617 618 if (npa->npa_isns) 619 errx(-1, "Error Log not available on a per-namespace basis"); 620 621 elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize); 622 623 if (elog == NULL) 624 return (-1); 625 626 nlog = bufsize / sizeof (nvme_error_log_entry_t); 627 628 (void) printf("%s: ", npa->npa_name); 629 nvme_print_error_log(nlog, elog); 630 631 free(elog); 632 633 return (0); 634 } 635 636 static int 637 do_get_logpage_health(int fd, const nvme_process_arg_t *npa) 638 { 639 size_t bufsize = sizeof (nvme_health_log_t); 640 nvme_health_log_t *hlog; 641 642 if (npa->npa_isns) { 643 if (npa->npa_idctl->id_lpa.lp_smart == 0) 644 errx(-1, "SMART/Health information not available " 645 "on a per-namespace basis on this controller"); 646 } 647 648 hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize); 649 650 if (hlog == NULL) 651 return (-1); 652 653 (void) printf("%s: ", npa->npa_name); 654 nvme_print_health_log(hlog, npa->npa_idctl); 655 656 free(hlog); 657 658 return (0); 659 } 660 661 static int 662 do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa) 663 { 664 size_t bufsize = sizeof (nvme_fwslot_log_t); 665 nvme_fwslot_log_t *fwlog; 666 667 if (npa->npa_isns) 668 errx(-1, "Firmware Slot information not available on a " 669 "per-namespace basis"); 670 671 fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize); 672 673 if (fwlog == NULL) 674 return (-1); 675 676 (void) printf("%s: ", npa->npa_name); 677 nvme_print_fwslot_log(fwlog); 678 679 free(fwlog); 680 681 return (0); 682 } 683 684 static int 685 do_get_logpage(int fd, const nvme_process_arg_t *npa) 686 { 687 int ret = 0; 688 int (*func)(int, const nvme_process_arg_t *); 689 690 if (npa->npa_argc < 1) { 691 warnx("missing logpage name"); 692 usage(npa->npa_cmd); 693 exit(-1); 694 } 695 696 if (strcmp(npa->npa_argv[0], "error") == 0) 697 func = do_get_logpage_error; 698 else if (strcmp(npa->npa_argv[0], "health") == 0) 699 func = do_get_logpage_health; 700 else if (strcmp(npa->npa_argv[0], "firmware") == 0) 701 func = do_get_logpage_fwslot; 702 else 703 errx(-1, "invalid log page: %s", npa->npa_argv[0]); 704 705 ret = func(fd, npa); 706 return (ret); 707 } 708 709 static void 710 usage_get_features(const char *c_name) 711 { 712 const nvme_feature_t *feat; 713 714 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n" 715 " Print the specified features of the specified NVMe controllers " 716 "and/or\n namespaces. Supported features are:\n\n", c_name); 717 (void) fprintf(stderr, " %-35s %-14s %s\n", 718 "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE"); 719 for (feat = &features[0]; feat->f_feature != 0; feat++) { 720 char *type; 721 722 if ((feat->f_getflags & NVMEADM_BOTH) == NVMEADM_BOTH) 723 type = "both"; 724 else if ((feat->f_getflags & NVMEADM_CTRL) != 0) 725 type = "controller only"; 726 else 727 type = "namespace only"; 728 729 (void) fprintf(stderr, " %-35s %-14s %s\n", 730 feat->f_name, feat->f_short, type); 731 } 732 733 } 734 735 static int 736 do_get_feat_common(int fd, const nvme_feature_t *feat, 737 nvme_identify_ctrl_t *idctl) 738 { 739 void *buf = NULL; 740 size_t bufsize = feat->f_bufsize; 741 uint64_t res; 742 743 if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf) 744 == B_FALSE) 745 return (EINVAL); 746 747 nvme_print(2, feat->f_name, -1, NULL); 748 feat->f_print(res, buf, bufsize, idctl); 749 free(buf); 750 751 return (0); 752 } 753 754 static int 755 do_get_feat_intr_vect(int fd, const nvme_feature_t *feat, 756 nvme_identify_ctrl_t *idctl) 757 { 758 uint64_t res; 759 uint64_t arg; 760 int intr_cnt; 761 762 intr_cnt = nvme_intr_cnt(fd); 763 764 if (intr_cnt == -1) 765 return (EINVAL); 766 767 nvme_print(2, feat->f_name, -1, NULL); 768 769 for (arg = 0; arg < intr_cnt; arg++) { 770 if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL) 771 == B_FALSE) 772 return (EINVAL); 773 774 feat->f_print(res, NULL, 0, idctl); 775 } 776 777 return (0); 778 } 779 780 static int 781 do_get_features(int fd, const nvme_process_arg_t *npa) 782 { 783 const nvme_feature_t *feat; 784 char *f, *flist, *lasts; 785 boolean_t header_printed = B_FALSE; 786 787 if (npa->npa_argc > 1) 788 errx(-1, "unexpected arguments"); 789 790 /* 791 * No feature list given, print all supported features. 792 */ 793 if (npa->npa_argc == 0) { 794 (void) printf("%s: Get Features\n", npa->npa_name); 795 for (feat = &features[0]; feat->f_feature != 0; feat++) { 796 if ((npa->npa_isns && 797 (feat->f_getflags & NVMEADM_NS) == 0) || 798 (!npa->npa_isns && 799 (feat->f_getflags & NVMEADM_CTRL) == 0)) 800 continue; 801 802 (void) feat->f_get(fd, feat, npa->npa_idctl); 803 } 804 805 return (0); 806 } 807 808 /* 809 * Process feature list. 810 */ 811 flist = strdup(npa->npa_argv[0]); 812 if (flist == NULL) 813 err(-1, "do_get_features"); 814 815 for (f = strtok_r(flist, ",", &lasts); 816 f != NULL; 817 f = strtok_r(NULL, ",", &lasts)) { 818 while (isspace(*f)) 819 f++; 820 821 for (feat = &features[0]; feat->f_feature != 0; feat++) { 822 if (strncasecmp(feat->f_name, f, strlen(f)) == 0 || 823 strncasecmp(feat->f_short, f, strlen(f)) == 0) 824 break; 825 } 826 827 if (feat->f_feature == 0) { 828 warnx("unknown feature %s", f); 829 continue; 830 } 831 832 if ((npa->npa_isns && 833 (feat->f_getflags & NVMEADM_NS) == 0) || 834 (!npa->npa_isns && 835 (feat->f_getflags & NVMEADM_CTRL) == 0)) { 836 warnx("feature %s %s supported for namespaces", 837 feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ? 838 "only" : "not"); 839 continue; 840 } 841 842 if (!header_printed) { 843 (void) printf("%s: Get Features\n", npa->npa_name); 844 header_printed = B_TRUE; 845 } 846 847 if (feat->f_get(fd, feat, npa->npa_idctl) != 0) { 848 warnx("unsupported feature: %s", feat->f_name); 849 continue; 850 } 851 } 852 853 free(flist); 854 return (0); 855 } 856 857 static int 858 do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf, 859 unsigned long ses) 860 { 861 nvme_process_arg_t ns_npa = { 0 }; 862 nvmeadm_cmd_t cmd = { 0 }; 863 864 cmd = *(npa->npa_cmd); 865 cmd.c_func = do_attach_detach; 866 cmd.c_name = "detach"; 867 ns_npa = *npa; 868 ns_npa.npa_cmd = &cmd; 869 870 if (do_attach_detach(fd, &ns_npa) != 0) 871 return (exitcode); 872 if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) { 873 warn("%s failed", npa->npa_cmd->c_name); 874 exitcode += -1; 875 } 876 cmd.c_name = "attach"; 877 exitcode += do_attach_detach(fd, &ns_npa); 878 879 return (exitcode); 880 } 881 882 static void 883 usage_format(const char *c_name) 884 { 885 (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n" 886 " Format one or all namespaces of the specified NVMe " 887 "controller. Supported LBA\n formats can be queried with " 888 "the \"%s identify\" command on the namespace\n to be " 889 "formatted.\n", c_name, getprogname()); 890 } 891 892 static int 893 do_format(int fd, const nvme_process_arg_t *npa) 894 { 895 unsigned long lbaf; 896 897 if (npa->npa_idctl->id_oacs.oa_format == 0) 898 errx(-1, "%s not supported", npa->npa_cmd->c_name); 899 900 if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0) 901 errx(-1, "%s not supported on individual namespace", 902 npa->npa_cmd->c_name); 903 904 905 if (npa->npa_argc > 0) { 906 errno = 0; 907 lbaf = strtoul(npa->npa_argv[0], NULL, 10); 908 909 if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF) 910 errx(-1, "invalid LBA format %d", lbaf + 1); 911 912 if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0) 913 errx(-1, "LBA formats with metadata not supported"); 914 } else { 915 lbaf = npa->npa_idns->id_flbas.lba_format; 916 } 917 918 return (do_format_common(fd, npa, lbaf, 0)); 919 } 920 921 static void 922 usage_secure_erase(const char *c_name) 923 { 924 (void) fprintf(stderr, "%s <ctl>[/<ns>] [-c]\n\n" 925 " Secure-Erase one or all namespaces of the specified " 926 "NVMe controller.\n", c_name); 927 } 928 929 static int 930 do_secure_erase(int fd, const nvme_process_arg_t *npa) 931 { 932 unsigned long lbaf; 933 uint8_t ses = NVME_FRMT_SES_USER; 934 935 if (npa->npa_idctl->id_oacs.oa_format == 0) 936 errx(-1, "%s not supported", npa->npa_cmd->c_name); 937 938 if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0) 939 errx(-1, "%s not supported on individual namespace", 940 npa->npa_cmd->c_name); 941 942 if (npa->npa_argc > 0) { 943 if (strcmp(npa->npa_argv[0], "-c") == 0) 944 ses = NVME_FRMT_SES_CRYPTO; 945 else 946 usage(npa->npa_cmd); 947 } 948 949 if (ses == NVME_FRMT_SES_CRYPTO && 950 npa->npa_idctl->id_fna.fn_crypt_erase == 0) 951 errx(-1, "cryptographic %s not supported", 952 npa->npa_cmd->c_name); 953 954 lbaf = npa->npa_idns->id_flbas.lba_format; 955 956 return (do_format_common(fd, npa, lbaf, ses)); 957 } 958 959 static void 960 usage_attach_detach(const char *c_name) 961 { 962 (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n" 963 " %c%s blkdev(7d) %s one or all namespaces of the " 964 "specified NVMe controller.\n", 965 c_name, toupper(c_name[0]), &c_name[1], 966 c_name[0] == 'd' ? "from" : "to"); 967 } 968 969 static int 970 do_attach_detach(int fd, const nvme_process_arg_t *npa) 971 { 972 char *c_name = npa->npa_cmd->c_name; 973 974 if (!npa->npa_isns) { 975 nvme_process_arg_t ns_npa = { 0 }; 976 977 ns_npa.npa_name = npa->npa_name; 978 ns_npa.npa_isns = B_TRUE; 979 ns_npa.npa_cmd = npa->npa_cmd; 980 981 nvme_walk(&ns_npa, npa->npa_node); 982 983 return (exitcode); 984 } else { 985 if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd) 986 == B_FALSE) { 987 warn("%s failed", c_name); 988 return (-1); 989 } 990 } 991 992 return (0); 993 }