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 2017 Joyent, Inc. 14 * Copyright 2024 Oxide Computer Company 15 * Copyright 2022 Tintri by DDN, Inc. All rights reserved. 16 */ 17 18 /* 19 * nvmeadm -- NVMe administration utility 20 * 21 * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args] 22 * commands: list 23 * identify 24 * list-logpages [logpage name],... 25 * get-logpage <logpage name> 26 * get-features <feature>[,...] 27 * format ... 28 * secure-erase ... 29 * detach ... 30 * attach ... 31 * list-firmware ... 32 * load-firmware ... 33 * commit-firmware ... 34 * activate-firmware ... 35 */ 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <stddef.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 #include <sys/sysmacros.h> 48 49 #include <sys/nvme.h> 50 51 #include "nvmeadm.h" 52 53 /* 54 * Assertions to make sure that we've properly captured various aspects of the 55 * packed structures and haven't broken them during updates. 56 */ 57 CTASSERT(sizeof (nvme_identify_ctrl_t) == NVME_IDENTIFY_BUFSIZE); 58 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oacs) == 256); 59 CTASSERT(offsetof(nvme_identify_ctrl_t, id_sqes) == 512); 60 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oncs) == 520); 61 CTASSERT(offsetof(nvme_identify_ctrl_t, id_subnqn) == 768); 62 CTASSERT(offsetof(nvme_identify_ctrl_t, id_nvmof) == 1792); 63 CTASSERT(offsetof(nvme_identify_ctrl_t, id_psd) == 2048); 64 CTASSERT(offsetof(nvme_identify_ctrl_t, id_vs) == 3072); 65 66 CTASSERT(sizeof (nvme_identify_nsid_t) == NVME_IDENTIFY_BUFSIZE); 67 CTASSERT(offsetof(nvme_identify_nsid_t, id_fpi) == 32); 68 CTASSERT(offsetof(nvme_identify_nsid_t, id_anagrpid) == 92); 69 CTASSERT(offsetof(nvme_identify_nsid_t, id_nguid) == 104); 70 CTASSERT(offsetof(nvme_identify_nsid_t, id_lbaf) == 128); 71 CTASSERT(offsetof(nvme_identify_nsid_t, id_vs) == 384); 72 73 CTASSERT(sizeof (nvme_identify_nsid_list_t) == NVME_IDENTIFY_BUFSIZE); 74 CTASSERT(sizeof (nvme_identify_ctrl_list_t) == NVME_IDENTIFY_BUFSIZE); 75 76 CTASSERT(sizeof (nvme_identify_primary_caps_t) == NVME_IDENTIFY_BUFSIZE); 77 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vqfrt) == 32); 78 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vifrt) == 64); 79 80 CTASSERT(sizeof (nvme_nschange_list_t) == 4096); 81 82 #define NVMEADM_F_CTRL 1 83 #define NVMEADM_F_NS 2 84 #define NVMEADM_F_BOTH (NVMEADM_F_CTRL | NVMEADM_F_NS) 85 86 static void usage(const nvmeadm_cmd_t *); 87 static bool nvmeadm_ctrl_disc_cb(nvme_t *, const nvme_ctrl_disc_t *, void *); 88 89 static int do_list(const nvme_process_arg_t *); 90 static int do_identify(const nvme_process_arg_t *); 91 static int do_identify_ctrl(const nvme_process_arg_t *); 92 static int do_identify_ns(const nvme_process_arg_t *); 93 static int do_list_logs(const nvme_process_arg_t *); 94 static int do_get_logpage_fwslot(const nvme_process_arg_t *); 95 static int do_get_logpage(const nvme_process_arg_t *); 96 static int do_list_features(const nvme_process_arg_t *); 97 static boolean_t do_get_feat_intr_vect(const nvme_process_arg_t *, 98 const nvme_feat_disc_t *, const nvmeadm_feature_t *); 99 static boolean_t do_get_feat_temp_thresh(const nvme_process_arg_t *, 100 const nvme_feat_disc_t *, const nvmeadm_feature_t *); 101 static int do_get_features(const nvme_process_arg_t *); 102 static int do_format(const nvme_process_arg_t *); 103 static int do_secure_erase(const nvme_process_arg_t *); 104 static int do_attach(const nvme_process_arg_t *); 105 static int do_detach(const nvme_process_arg_t *); 106 static int do_firmware_load(const nvme_process_arg_t *); 107 static int do_firmware_commit(const nvme_process_arg_t *); 108 static int do_firmware_activate(const nvme_process_arg_t *); 109 110 static void optparse_list(nvme_process_arg_t *); 111 static void optparse_identify(nvme_process_arg_t *); 112 static void optparse_identify_ctrl(nvme_process_arg_t *); 113 static void optparse_identify_ns(nvme_process_arg_t *); 114 static void optparse_list_logs(nvme_process_arg_t *); 115 static void optparse_get_logpage(nvme_process_arg_t *); 116 static void optparse_list_features(nvme_process_arg_t *); 117 static void optparse_secure_erase(nvme_process_arg_t *); 118 119 static void usage_list(const char *); 120 static void usage_identify(const char *); 121 static void usage_identify_ctrl(const char *); 122 static void usage_identify_ns(const char *); 123 static void usage_list_logs(const char *); 124 static void usage_get_logpage(const char *); 125 static void usage_list_features(const char *); 126 static void usage_get_features(const char *); 127 static void usage_format(const char *); 128 static void usage_secure_erase(const char *); 129 static void usage_attach_detach(const char *); 130 static void usage_firmware_list(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 138 /* 139 * nvmeadm Secure-erase specific options 140 */ 141 #define NVMEADM_O_SE_CRYPTO 0x00000004 142 143 /* 144 * nvmeadm identify specific options 145 */ 146 #define NVMEADM_O_ID_NSID_LIST 0x00000008 147 #define NVMEADM_O_ID_COMMON_NS 0x00000010 148 #define NVMEADM_O_ID_CTRL_LIST 0x00000020 149 #define NVMEADM_O_ID_DESC_LIST 0x00000040 150 #define NVMEADM_O_ID_ALLOC_NS 0x00000080 151 152 /* 153 * nvmeadm List specific options 154 */ 155 #define NVMEADM_O_LS_CTRL 0x00000100 156 157 static int exitcode; 158 159 /* 160 * Nvmeadm subcommand definitons. 161 * 162 * When adding a new subcommand, please check that the commands still 163 * line up in the usage() message, and adjust the format string in 164 * usage() below if necessary. 165 */ 166 static const nvmeadm_cmd_t nvmeadm_cmds[] = { 167 { 168 "list", 169 "list controllers and namespaces", 170 " -c\t\tlist only controllers\n" 171 " -p\t\tprint parsable output\n" 172 " -o field\tselect a field for parsable output\n", 173 " model\t\tthe model name of the device\n" 174 " serial\tthe serial number of the device\n" 175 " fwrev\t\tthe device's current firmware revision\n" 176 " version\tthe device's NVMe specification version\n" 177 " capacity\tthe capacity of the device in bytes\n" 178 " instance\tthe device driver instance (e.g. nvme3)\n" 179 " unallocated\tthe amount of unallocated NVM in bytes", 180 do_list, usage_list, optparse_list, 181 NVMEADM_C_MULTI 182 }, 183 { 184 "identify", 185 "identify controllers and/or namespaces", 186 " -C\t\tget Common Namespace Identification\n" 187 " -a\t\tget only allocated namespace information\n" 188 " -c\t\tget controller identifier list\n" 189 " -d\t\tget namespace identification descriptors list\n" 190 " -n\t\tget namespaces identifier list", 191 NULL, 192 do_identify, usage_identify, optparse_identify, 193 NVMEADM_C_MULTI 194 }, 195 { 196 "identify-controller", 197 "identify controllers", 198 " -C\t\tget Common Namespace Identification\n" 199 " -a\t\tget only allocated namespace information\n" 200 " -c\t\tget controller identifier list\n" 201 " -n\t\tget namespaces identifier list", 202 NULL, 203 do_identify_ctrl, usage_identify_ctrl, optparse_identify_ctrl, 204 NVMEADM_C_MULTI 205 }, 206 { 207 "identify-namespace", 208 "identify namespaces", 209 " -c\t\tget attached controller identifier list\n" 210 " -d\t\tget namespace identification descriptors list", 211 NULL, 212 do_identify_ns, usage_identify_ns, optparse_identify_ns, 213 NVMEADM_C_MULTI 214 }, 215 { 216 "list-logpages", 217 "list a device's supported log pages", 218 " -a\t\tprint all log pages, including unimplemented ones\n" 219 " -H\t\tomit column headers\n" 220 " -o field\tselect a field for parsable output\n" 221 " -p\t\tprint parsable output\n" 222 " -s scope\tprint logs that match the specified scopes " 223 "(default is based on\n\t\tdevice)\n", 224 " device\tthe name of the controller or namespace\n" 225 " name\t\tthe name of the log page\n" 226 " desc\t\ta description of the loage page\n" 227 " scope\t\tthe valid device scopes for the log page\n" 228 " fields\tthe list of fields in the get log request that may " 229 "be set or required\n\t\t(e.g. lsi, lsp, rae, etc.)\n" 230 " csi\t\tthe command set interface the log page belongs to\n" 231 " lid\t\tthe log page's numeric ID\n" 232 " impl\t\tindicates whether the device implements the log " 233 "page\n" 234 " size\t\tthe size of the log page for fixed size logs\n" 235 " minsize\tthe minimum size required to determine the full " 236 "log page size\n\t\tfor variable-length pages\n" 237 " sources\twhere information for this log page came from\n" 238 " kind\t\tindicates the kind of log page e.g. standard, " 239 "vendor-specific,\n\t\tetc.", 240 do_list_logs, usage_list_logs, optparse_list_logs, 241 NVMEADM_C_MULTI 242 }, 243 { 244 "get-logpage", 245 "get a log page from controllers and/or namespaces", 246 " -O file\toutput log raw binary data to a file\n", 247 NULL, 248 do_get_logpage, usage_get_logpage, optparse_get_logpage, 249 NVMEADM_C_MULTI 250 }, 251 { 252 "list-features", 253 "list a device's supported features", 254 " -a\t\tprint all features, including unsupported\n" 255 " -H\t\tomit column headers\n" 256 " -o field\tselect a field for parsable output\n" 257 " -p\t\tprint parsable output", 258 " device\tthe name of the controller or namespace\n" 259 " short\t\tthe short name of the feature\n" 260 " spec\t\tthe longer feature description from the NVMe spec\n" 261 " fid\t\tthe numeric feature ID\n" 262 " scope\t\tthe valid device scopes for the feature\n" 263 " kind\t\tindicates the kind of feature e.g. standard, " 264 "vendor-specific,\n\t\tetc.\n" 265 " csi\t\tindicates the features command set interface\n" 266 " flags\t\tindicates additional properties of the feature\n" 267 " get-in\tindicates the fields that are required to get the " 268 "feature\n" 269 " set-in\tindicates the fields that are required to set the " 270 "feature\n" 271 " get-out\tindicates the fields the feature outputs\n" 272 " set-out\tindicates the fields the feature outputs when " 273 "setting the feature\n" 274 " datalen\tindicates the length of the feature's data " 275 "payload\n" 276 " impl\t\tindicates whether the device implements the " 277 "feature", 278 do_list_features, usage_list_features, optparse_list_features, 279 NVMEADM_C_MULTI 280 }, 281 { 282 "get-features", 283 "get features from controllers and/or namespaces", 284 NULL, 285 NULL, 286 do_get_features, usage_get_features, NULL, 287 NVMEADM_C_MULTI 288 }, 289 { 290 "format", 291 "format namespace(s) of a controller", 292 NULL, 293 NULL, 294 do_format, usage_format, NULL, 295 NVMEADM_C_EXCL 296 }, 297 { 298 "secure-erase", 299 "secure erase namespace(s) of a controller", 300 " -c Do a cryptographic erase.", 301 NULL, 302 do_secure_erase, usage_secure_erase, optparse_secure_erase, 303 NVMEADM_C_EXCL 304 }, 305 { 306 "detach", 307 "detach blkdev(4D) from namespace(s) of a controller", 308 NULL, 309 NULL, 310 do_detach, usage_attach_detach, NULL, 311 NVMEADM_C_EXCL 312 }, 313 { 314 "attach", 315 "attach blkdev(4D) to namespace(s) of a controller", 316 NULL, 317 NULL, 318 do_attach, usage_attach_detach, NULL, 319 NVMEADM_C_EXCL 320 }, 321 { 322 "list-firmware", 323 "list firmware on a controller", 324 NULL, 325 NULL, 326 do_get_logpage_fwslot, usage_firmware_list, NULL, 327 0 328 }, 329 { 330 "load-firmware", 331 "load firmware to a controller", 332 NULL, 333 NULL, 334 do_firmware_load, usage_firmware_load, NULL, 335 NVMEADM_C_EXCL 336 }, 337 { 338 "commit-firmware", 339 "commit downloaded firmware to a slot of a controller", 340 NULL, 341 NULL, 342 do_firmware_commit, usage_firmware_commit, NULL, 343 NVMEADM_C_EXCL 344 }, 345 { 346 "activate-firmware", 347 "activate a firmware slot of a controller", 348 NULL, 349 NULL, 350 do_firmware_activate, usage_firmware_activate, NULL, 351 NVMEADM_C_EXCL 352 }, 353 { 354 "wdc/e6dump", 355 "dump WDC e6 diagnostic log", 356 " -o output\tspecify output file destination\n", 357 NULL, 358 do_wdc_e6dump, usage_wdc_e6dump, optparse_wdc_e6dump, 359 0 360 }, 361 { 362 "wdc/resize", 363 "change a WDC device's capacity", 364 " -g\t\tquery the device's current resized capacity\n" 365 " -s size\tset the size of a device to the specified in gb", 366 NULL, 367 do_wdc_resize, usage_wdc_resize, optparse_wdc_resize, 368 /* 369 * We do not set NVMEADM_C_EXCL here as that is handled by the 370 * vendor unique command logic and operates based on the 371 * information we get from vuc discovery. 372 */ 373 0 374 }, 375 { 376 "wdc/clear-assert", 377 "clear internal device assertion", 378 NULL, 379 NULL, 380 do_wdc_clear_assert, usage_wdc_clear_assert, NULL 381 }, 382 { 383 "wdc/inject-assert", 384 "inject internal device assertion", 385 NULL, 386 NULL, 387 do_wdc_inject_assert, usage_wdc_inject_assert, NULL 388 }, 389 { 390 NULL, NULL, NULL, 391 NULL, NULL, NULL, 0 392 } 393 }; 394 395 static const nvmeadm_feature_t features[] = { 396 { 397 .f_feature = NVME_FEAT_ARBITRATION, 398 .f_print = nvme_print_feat_arbitration 399 }, { 400 .f_feature = NVME_FEAT_POWER_MGMT, 401 .f_print = nvme_print_feat_power_mgmt 402 }, { 403 .f_feature = NVME_FEAT_LBA_RANGE, 404 .f_print = nvme_print_feat_lba_range 405 }, { 406 .f_feature = NVME_FEAT_TEMPERATURE, 407 .f_get = do_get_feat_temp_thresh, 408 .f_print = nvme_print_feat_temperature 409 }, { 410 .f_feature = NVME_FEAT_ERROR, 411 .f_print = nvme_print_feat_error 412 }, { 413 .f_feature = NVME_FEAT_WRITE_CACHE, 414 .f_print = nvme_print_feat_write_cache 415 }, { 416 .f_feature = NVME_FEAT_NQUEUES, 417 .f_print = nvme_print_feat_nqueues 418 }, { 419 .f_feature = NVME_FEAT_INTR_COAL, 420 .f_print = nvme_print_feat_intr_coal 421 }, { 422 .f_feature = NVME_FEAT_INTR_VECT, 423 .f_get = do_get_feat_intr_vect, 424 .f_print = nvme_print_feat_intr_vect 425 }, { 426 .f_feature = NVME_FEAT_WRITE_ATOM, 427 .f_print = nvme_print_feat_write_atom 428 }, { 429 .f_feature = NVME_FEAT_ASYNC_EVENT, 430 .f_print = nvme_print_feat_async_event 431 }, { 432 .f_feature = NVME_FEAT_AUTO_PST, 433 .f_print = nvme_print_feat_auto_pst 434 }, { 435 .f_feature = NVME_FEAT_PROGRESS, 436 .f_print = nvme_print_feat_progress 437 } 438 }; 439 440 static void 441 nvmeadm_ctrl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap) 442 { 443 nvme_ctrl_t *ctrl = npa->npa_ctrl; 444 445 (void) fprintf(stderr, "nvmeadm: "); 446 (void) vfprintf(stderr, fmt, ap); 447 (void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n", 448 nvme_ctrl_errmsg(ctrl), nvme_ctrl_errtostr(npa->npa_ctrl, 449 nvme_ctrl_err(ctrl)), nvme_ctrl_err(ctrl), nvme_ctrl_syserr(ctrl)); 450 } 451 452 static void 453 nvmeadm_hdl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap) 454 { 455 nvme_t *nvme = npa->npa_nvme; 456 457 (void) fprintf(stderr, "nvmeadm: "); 458 (void) vfprintf(stderr, fmt, ap); 459 (void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n", 460 nvme_errmsg(nvme), nvme_errtostr(nvme, nvme_err(nvme)), 461 nvme_err(nvme), nvme_syserr(nvme)); 462 } 463 464 static void 465 nvmeadm_ctrl_info_vwarn(const nvme_process_arg_t *npa, const char *fmt, 466 va_list ap) 467 { 468 nvme_ctrl_info_t *info = npa->npa_ctrl_info; 469 470 (void) fprintf(stderr, "nvmeadm: "); 471 (void) vfprintf(stderr, fmt, ap); 472 (void) fprintf(stderr, ": %s: %s (libnvme info: 0x%x, sys: %d)\n", 473 nvme_ctrl_info_errmsg(info), nvme_ctrl_info_errtostr(info, 474 nvme_ctrl_info_err(info)), nvme_ctrl_info_err(info), 475 nvme_ctrl_info_syserr(info)); 476 } 477 478 void 479 nvmeadm_warn(const nvme_process_arg_t *npa, const char *fmt, ...) 480 { 481 va_list ap; 482 483 va_start(ap, fmt); 484 nvmeadm_ctrl_vwarn(npa, fmt, ap); 485 va_end(ap); 486 } 487 488 void __NORETURN 489 nvmeadm_fatal(const nvme_process_arg_t *npa, const char *fmt, ...) 490 { 491 va_list ap; 492 493 va_start(ap, fmt); 494 nvmeadm_ctrl_vwarn(npa, fmt, ap); 495 va_end(ap); 496 497 exit(-1); 498 } 499 500 void 501 nvmeadm_hdl_warn(const nvme_process_arg_t *npa, const char *fmt, ...) 502 { 503 va_list ap; 504 505 va_start(ap, fmt); 506 nvmeadm_hdl_vwarn(npa, fmt, ap); 507 va_end(ap); 508 } 509 510 void __NORETURN 511 nvmeadm_hdl_fatal(const nvme_process_arg_t *npa, const char *fmt, ...) 512 { 513 va_list ap; 514 515 va_start(ap, fmt); 516 nvmeadm_hdl_vwarn(npa, fmt, ap); 517 va_end(ap); 518 519 exit(-1); 520 } 521 522 static void 523 nvmeadm_ctrl_info_warn(const nvme_process_arg_t *npa, const char *fmt, ...) 524 { 525 va_list ap; 526 527 va_start(ap, fmt); 528 nvmeadm_ctrl_info_vwarn(npa, fmt, ap); 529 va_end(ap); 530 } 531 532 static void 533 nvmeadm_ctrl_info_fatal(const nvme_process_arg_t *npa, const char *fmt, ...) 534 { 535 va_list ap; 536 537 va_start(ap, fmt); 538 nvmeadm_ctrl_info_vwarn(npa, fmt, ap); 539 va_end(ap); 540 541 exit(-1); 542 } 543 544 boolean_t 545 nvme_version_check(const nvme_process_arg_t *npa, const nvme_version_t *vers) 546 { 547 return (nvme_vers_atleast(npa->npa_version, vers) ? B_TRUE : B_FALSE); 548 } 549 550 /* 551 * Because nvmeadm operates on a series of NVMe devices for several commands, 552 * here we need to clean up everything that we allocated for this device so we 553 * can prepare for the next. 554 */ 555 static void 556 nvmeadm_cleanup_npa(nvme_process_arg_t *npa) 557 { 558 npa->npa_idctl = NULL; 559 npa->npa_version = NULL; 560 561 if (npa->npa_excl) { 562 if (npa->npa_ns != NULL) { 563 nvme_ns_unlock(npa->npa_ns); 564 } else if (npa->npa_ctrl != NULL) { 565 nvme_ctrl_unlock(npa->npa_ctrl); 566 } 567 } 568 569 if (npa->npa_ns_info != NULL) { 570 nvme_ns_info_free(npa->npa_ns_info); 571 npa->npa_ns_info = NULL; 572 } 573 574 if (npa->npa_ctrl_info != NULL) { 575 nvme_ctrl_info_free(npa->npa_ctrl_info); 576 npa->npa_ctrl_info = NULL; 577 } 578 579 if (npa->npa_ns != NULL) { 580 nvme_ns_fini(npa->npa_ns); 581 npa->npa_ns = NULL; 582 } 583 584 if (npa->npa_ctrl != NULL) { 585 nvme_ctrl_fini(npa->npa_ctrl); 586 npa->npa_ctrl = NULL; 587 } 588 } 589 590 /* 591 * Determine if a command requires a controller or namespace write lock. If so 592 * we first attempt to grab it non-blocking and then if that fails, we'll warn 593 * that we may be blocking for the lock so that way the user has a chance to do 594 * something and can cancel it. 595 */ 596 static void 597 nvmeadm_excl(const nvme_process_arg_t *npa, nvme_lock_level_t level) 598 { 599 bool ret; 600 nvme_lock_flags_t flags = NVME_LOCK_F_DONT_BLOCK; 601 602 if (npa->npa_ns != NULL) { 603 ret = nvme_ns_lock(npa->npa_ns, level, flags); 604 } else { 605 ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags); 606 } 607 608 if (ret) { 609 return; 610 } 611 612 if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_LOCK_WOULD_BLOCK) { 613 nvmeadm_fatal(npa, "failed to acquire lock on %s", 614 npa->npa_name); 615 } 616 617 (void) fprintf(stderr, "Waiting on contended %s lock on %s...", 618 npa->npa_ns != NULL ? "namespace": "controller", npa->npa_name); 619 (void) fflush(stderr); 620 621 flags &= ~NVME_LOCK_F_DONT_BLOCK; 622 if (npa->npa_ns != NULL) { 623 ret = nvme_ns_lock(npa->npa_ns, level, flags); 624 } else { 625 ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags); 626 } 627 628 if (!ret) { 629 nvmeadm_fatal(npa, "failed to acquire lock on %s", 630 npa->npa_name); 631 } 632 633 (void) fprintf(stderr, " acquired\n"); 634 } 635 636 /* 637 * Most of nvmeadm was written before the existence of libnvme and always had 638 * things like the identify controller or namespace information sitting around. 639 * As such we try to grab all this in one place for it. Note, regardless if this 640 * succeeds or fails, our callers will still call nvmeadm_cleanup_npa() so we 641 * don't need to clean up the various libnvme objects. 642 */ 643 static boolean_t 644 nvmeadm_open_dev(nvme_process_arg_t *npa) 645 { 646 if (!nvme_ctrl_ns_init(npa->npa_nvme, npa->npa_name, &npa->npa_ctrl, 647 &npa->npa_ns)) { 648 nvmeadm_hdl_warn(npa, "failed to open '%s'", npa->npa_name); 649 exitcode = -1; 650 return (B_FALSE); 651 } 652 653 /* 654 * Several commands expect to be able to access the controller's 655 * information snapshot. Grab that now for it and the namespace if it 656 * exists. 657 */ 658 if (!nvme_ctrl_info_snap(npa->npa_ctrl, &npa->npa_ctrl_info)) { 659 nvmeadm_warn(npa, "failed to get controller info for %s", 660 npa->npa_ctrl_name); 661 exitcode = -1; 662 return (B_FALSE); 663 } 664 665 if (npa->npa_ns != NULL && !nvme_ns_info_snap(npa->npa_ns, 666 &npa->npa_ns_info)) { 667 nvmeadm_warn(npa, "failed to get namespace info for %s", 668 npa->npa_name); 669 exitcode = -1; 670 return (B_FALSE); 671 } 672 673 /* 674 * Snapshot data the rest of the command has fairly ingrained. 675 */ 676 npa->npa_version = nvme_ctrl_info_version(npa->npa_ctrl_info); 677 npa->npa_idctl = nvme_ctrl_info_identify(npa->npa_ctrl_info); 678 679 /* 680 * If this command has requested exclusive access, proceed to grab that 681 * before we continue. 682 */ 683 if (npa->npa_excl) { 684 nvmeadm_excl(npa, NVME_LOCK_L_WRITE); 685 } 686 687 return (B_TRUE); 688 } 689 690 static bool 691 nvmeadm_ctrl_disc_cb(nvme_t *nvme, const nvme_ctrl_disc_t *disc, void *arg) 692 { 693 nvme_process_arg_t *npa = arg; 694 di_node_t di = nvme_ctrl_disc_devi(disc); 695 char name[128]; 696 697 (void) snprintf(name, sizeof (name), "%s%d", di_driver_name(di), 698 di_instance(di)); 699 npa->npa_name = name; 700 npa->npa_ctrl_name = name; 701 702 if (nvmeadm_open_dev(npa)) { 703 if (npa->npa_cmd->c_func(npa) != 0) { 704 exitcode = -1; 705 } 706 } 707 708 nvmeadm_cleanup_npa(npa); 709 return (true); 710 } 711 712 int 713 main(int argc, char **argv) 714 { 715 int c; 716 const nvmeadm_cmd_t *cmd; 717 nvme_process_arg_t npa = { 0 }; 718 int help = 0; 719 char *ctrl = NULL; 720 721 while ((c = getopt(argc, argv, "dhv")) != -1) { 722 switch (c) { 723 case 'd': 724 debug++; 725 break; 726 727 case 'v': 728 verbose++; 729 break; 730 731 case 'h': 732 help++; 733 break; 734 735 case '?': 736 usage(NULL); 737 exit(-1); 738 } 739 } 740 741 if (optind == argc) { 742 usage(NULL); 743 if (help) 744 exit(0); 745 else 746 exit(-1); 747 } 748 749 /* Look up the specified command in the command table. */ 750 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) 751 if (strcmp(cmd->c_name, argv[optind]) == 0) 752 break; 753 754 if (cmd->c_name == NULL) { 755 usage(NULL); 756 exit(-1); 757 } 758 759 if (help) { 760 usage(cmd); 761 exit(0); 762 } 763 764 npa.npa_nvme = nvme_init(); 765 if (npa.npa_nvme == NULL) { 766 err(-1, "failed to initialize libnvme"); 767 } 768 npa.npa_cmd = cmd; 769 npa.npa_excl = ((cmd->c_flags & NVMEADM_C_EXCL) != 0); 770 771 optind++; 772 773 /* 774 * Store the remaining arguments for use by the command. Give the 775 * command a chance to process the options across the board before going 776 * into each controller. 777 */ 778 npa.npa_argc = argc - optind; 779 npa.npa_argv = &argv[optind]; 780 781 if (cmd->c_optparse != NULL) { 782 optind = 0; 783 cmd->c_optparse(&npa); 784 npa.npa_argc -= optind; 785 npa.npa_argv += optind; 786 } 787 788 /* 789 * All commands but "list" require a ctl/ns argument. However, this 790 * should not be passed through to the command in its subsequent 791 * arguments. 792 */ 793 if (npa.npa_argc == 0 && cmd->c_func != do_list) { 794 warnx("missing controller/namespace name"); 795 usage(cmd); 796 exit(-1); 797 } 798 799 if (npa.npa_argc > 0) { 800 ctrl = npa.npa_argv[0]; 801 npa.npa_argv++; 802 npa.npa_argc--; 803 } else { 804 if (!nvme_ctrl_discover(npa.npa_nvme, nvmeadm_ctrl_disc_cb, 805 &npa)) { 806 nvmeadm_hdl_fatal(&npa, "failed to walk controllers"); 807 } 808 exit(exitcode); 809 } 810 811 /* 812 * Make sure we're not running commands on multiple controllers that 813 * aren't allowed to do that. 814 */ 815 if (ctrl != NULL && strchr(ctrl, ',') != NULL && 816 (cmd->c_flags & NVMEADM_C_MULTI) == 0) { 817 warnx("%s not allowed on multiple controllers", 818 cmd->c_name); 819 usage(cmd); 820 exit(-1); 821 } 822 823 /* 824 * Get controller/namespace arguments and run command. 825 */ 826 while ((npa.npa_name = strsep(&ctrl, ",")) != NULL) { 827 char *ctrl_name, *slash; 828 829 /* 830 * We may be given just a controller as an argument or a 831 * controller and a namespace as an argument. Parts of the 832 * commands want to know what controller they're referring to 833 * even if the overall argument was for a namespace. So we 834 * always dup the argument and try to make the controller out of 835 * it. 836 */ 837 ctrl_name = strdup(npa.npa_name); 838 if (ctrl_name == NULL) { 839 err(-1, "failed to duplicate NVMe controller/namespace " 840 "name"); 841 } 842 if ((slash = strchr(ctrl_name, '/')) != NULL) 843 *slash = '\0'; 844 npa.npa_ctrl_name = ctrl_name; 845 846 if (nvmeadm_open_dev(&npa)) { 847 if (npa.npa_cmd->c_func(&npa) != 0) { 848 exitcode = -1; 849 } 850 } 851 852 nvmeadm_cleanup_npa(&npa); 853 free(ctrl_name); 854 } 855 856 exit(exitcode); 857 } 858 859 static void 860 nvme_oferr(const char *fmt, ...) 861 { 862 va_list ap; 863 864 va_start(ap, fmt); 865 verrx(-1, fmt, ap); 866 } 867 868 static void 869 usage(const nvmeadm_cmd_t *cmd) 870 { 871 const char *progname = getprogname(); 872 873 (void) fprintf(stderr, "usage:\n"); 874 (void) fprintf(stderr, " %s -h %s\n", progname, 875 cmd != NULL ? cmd->c_name : "[<command>]"); 876 (void) fprintf(stderr, " %s [-dv] ", progname); 877 878 if (cmd != NULL) { 879 cmd->c_usage(cmd->c_name); 880 } else { 881 (void) fprintf(stderr, 882 "<command> <ctl>[/<ns>][,...] [<args>]\n"); 883 (void) fprintf(stderr, 884 "\n Manage NVMe controllers and namespaces.\n"); 885 (void) fprintf(stderr, "\ncommands:\n"); 886 887 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) { 888 /* 889 * The longest nvmeadm subcommand is 19 characters long. 890 * The format string needs to be updated every time a 891 * longer subcommand is added. 892 */ 893 (void) fprintf(stderr, " %-19s - %s\n", 894 cmd->c_name, cmd->c_desc); 895 } 896 } 897 (void) fprintf(stderr, "\n%s flags:\n" 898 " -h\t\tprint usage information\n" 899 " -d\t\tprint information useful for debugging %s\n" 900 " -v\t\tprint verbose information\n", 901 progname, progname); 902 903 if (cmd != NULL && cmd->c_flagdesc != NULL) { 904 (void) fprintf(stderr, "\n%s %s flags:\n", 905 progname, cmd->c_name); 906 (void) fprintf(stderr, "%s\n", cmd->c_flagdesc); 907 } 908 909 if (cmd != NULL && cmd->c_fielddesc != NULL) { 910 (void) fprintf(stderr, "\n%s %s valid fields:\n", 911 progname, cmd->c_name); 912 (void) fprintf(stderr, "%s\n", cmd->c_fielddesc); 913 } 914 } 915 916 char * 917 nvme_dskname(di_node_t ctrl, const char *bd_addr) 918 { 919 di_dim_t dim; 920 char *diskname = NULL; 921 922 dim = di_dim_init(); 923 if (dim == NULL) { 924 err(-1, "failed to initialize devinfo minor translation"); 925 } 926 927 for (di_node_t child = di_child_node(ctrl); child != DI_NODE_NIL; 928 child = di_sibling_node(child)) { 929 char *disk_ctd, *path = NULL; 930 const char *addr = di_bus_addr(child); 931 if (addr == NULL) 932 continue; 933 934 if (strcmp(addr, bd_addr) != 0) 935 continue; 936 937 path = di_dim_path_dev(dim, di_driver_name(child), 938 di_instance(child), "c"); 939 940 /* 941 * Error out if we didn't get a path, or if it's too short for 942 * the following operations to be safe. 943 */ 944 if (path == NULL || strlen(path) < 2) { 945 errx(-1, "failed to get a valid minor path"); 946 } 947 948 /* Chop off 's0' and get everything past the last '/' */ 949 path[strlen(path) - 2] = '\0'; 950 disk_ctd = strrchr(path, '/'); 951 if (disk_ctd == NULL) { 952 errx(-1, "encountered malformed minor path: %s", path); 953 } 954 955 diskname = strdup(++disk_ctd); 956 if (diskname == NULL) { 957 err(-1, "failed to duplicate disk path"); 958 } 959 960 free(path); 961 break; 962 } 963 964 di_dim_fini(dim); 965 return (diskname); 966 } 967 968 static void 969 usage_list(const char *c_name) 970 { 971 (void) fprintf(stderr, "%s " 972 "[-c] [-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n" 973 " List NVMe controllers and their namespaces. If no " 974 "controllers and/or name-\n spaces are specified, all " 975 "controllers and namespaces in the system will be\n " 976 "listed.\n", c_name); 977 } 978 979 static void 980 optparse_list(nvme_process_arg_t *npa) 981 { 982 int c; 983 uint_t oflags = 0; 984 boolean_t parse = B_FALSE; 985 const char *fields = NULL; 986 const ofmt_field_t *ofmt = nvmeadm_list_nsid_ofmt; 987 988 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":co:p")) != -1) { 989 switch (c) { 990 case 'c': 991 npa->npa_cmdflags |= NVMEADM_O_LS_CTRL; 992 ofmt = nvmeadm_list_ctrl_ofmt; 993 break; 994 case 'o': 995 fields = optarg; 996 break; 997 998 case 'p': 999 parse = B_TRUE; 1000 oflags |= OFMT_PARSABLE; 1001 break; 1002 1003 case '?': 1004 errx(-1, "unknown option: -%c", optopt); 1005 1006 case ':': 1007 errx(-1, "option -%c requires an argument", optopt); 1008 } 1009 } 1010 1011 if (fields != NULL && !parse) { 1012 errx(-1, "-o can only be used when in parsable mode (-p)"); 1013 } 1014 1015 if (parse && fields == NULL) { 1016 errx(-1, "parsable mode (-p) requires one to specify output " 1017 "fields with -o"); 1018 } 1019 1020 if (parse) { 1021 ofmt_status_t oferr; 1022 1023 oferr = ofmt_open(fields, ofmt, oflags, 0, 1024 &npa->npa_ofmt); 1025 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx); 1026 } 1027 } 1028 1029 static void 1030 do_list_nsid(const nvme_process_arg_t *npa, nvme_ctrl_info_t *ctrl, 1031 nvme_ns_info_t *ns) 1032 { 1033 const char *bd_addr, *disk = NULL; 1034 char *disk_path = NULL; 1035 di_node_t ctrl_devi; 1036 1037 switch (nvme_ns_info_level(ns)) { 1038 case NVME_NS_DISC_F_ALL: 1039 disk = "unallocated"; 1040 break; 1041 case NVME_NS_DISC_F_ALLOCATED: 1042 disk = "inactive"; 1043 break; 1044 case NVME_NS_DISC_F_ACTIVE: 1045 disk = "ignored"; 1046 break; 1047 case NVME_NS_DISC_F_NOT_IGNORED: 1048 disk = "unattached"; 1049 break; 1050 case NVME_NS_DISC_F_BLKDEV: 1051 disk = "unknown"; 1052 if (nvme_ns_info_bd_addr(ns, &bd_addr) && 1053 nvme_ctrl_devi(npa->npa_ctrl, &ctrl_devi)) { 1054 disk_path = nvme_dskname(ctrl_devi, bd_addr); 1055 disk = disk_path; 1056 } 1057 break; 1058 } 1059 1060 if (npa->npa_ofmt != NULL) { 1061 nvmeadm_list_ofmt_arg_t oarg = { 0 }; 1062 1063 oarg.nloa_name = npa->npa_ctrl_name; 1064 oarg.nloa_ctrl = ctrl; 1065 oarg.nloa_ns = ns; 1066 oarg.nloa_disk = disk_path; 1067 1068 ofmt_print(npa->npa_ofmt, &oarg); 1069 } else { 1070 (void) printf(" %s/%u (%s)", npa->npa_ctrl_name, 1071 nvme_ns_info_nsid(ns), disk); 1072 if (nvme_ns_info_level(ns) >= NVME_NS_DISC_F_ACTIVE) { 1073 (void) printf(": "); 1074 nvme_print_nsid_summary(ns); 1075 } else { 1076 (void) printf("\n"); 1077 } 1078 } 1079 1080 free(disk_path); 1081 } 1082 1083 static int 1084 do_list(const nvme_process_arg_t *npa) 1085 { 1086 nvme_ctrl_info_t *info = NULL; 1087 nvme_ns_iter_t *iter = NULL; 1088 nvme_iter_t ret; 1089 const nvme_ns_disc_t *disc; 1090 nvme_ns_disc_level_t level; 1091 int rv = -1; 1092 1093 if (npa->npa_argc > 0) { 1094 errx(-1, "%s passed extraneous arguments starting with %s", 1095 npa->npa_cmd->c_name, npa->npa_argv[0]); 1096 } 1097 1098 if (!nvme_ctrl_info_snap(npa->npa_ctrl, &info)) { 1099 nvmeadm_warn(npa, "failed to get controller information for %s", 1100 npa->npa_ctrl_name); 1101 return (-1); 1102 } 1103 1104 if (npa->npa_ofmt == NULL) { 1105 (void) printf("%s: ", npa->npa_ctrl_name); 1106 nvme_print_ctrl_summary(info); 1107 } else if ((npa->npa_cmdflags & NVMEADM_O_LS_CTRL) != 0) { 1108 nvmeadm_list_ofmt_arg_t oarg = { 0 }; 1109 oarg.nloa_name = npa->npa_ctrl_name; 1110 oarg.nloa_ctrl = info; 1111 1112 ofmt_print(npa->npa_ofmt, &oarg); 1113 } 1114 1115 if ((npa->npa_cmdflags & NVMEADM_O_LS_CTRL) != 0) { 1116 rv = 0; 1117 goto out; 1118 } 1119 1120 /* 1121 * Check if we were given an explicit namespace as an argument. If so, 1122 * we always list it and don't need to do discovery. 1123 */ 1124 if (npa->npa_ns != NULL) { 1125 nvme_ns_info_t *ns_info; 1126 1127 if (!nvme_ns_info_snap(npa->npa_ns, &ns_info)) { 1128 nvmeadm_warn(npa, "failed to get namespace " 1129 "information for %s", npa->npa_name); 1130 goto out; 1131 } 1132 1133 do_list_nsid(npa, info, ns_info); 1134 nvme_ns_info_free(ns_info); 1135 rv = 0; 1136 goto out; 1137 } 1138 1139 if (verbose) { 1140 level = NVME_NS_DISC_F_ALL; 1141 } else { 1142 level = NVME_NS_DISC_F_NOT_IGNORED; 1143 } 1144 1145 if (!nvme_ns_discover_init(npa->npa_ctrl, level, &iter)) { 1146 nvmeadm_warn(npa, "failed to iterate namespaces on %s", 1147 npa->npa_ctrl_name); 1148 goto out; 1149 } 1150 1151 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) { 1152 nvme_ns_info_t *ns_info; 1153 uint32_t nsid = nvme_ns_disc_nsid(disc); 1154 1155 if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, nsid, &ns_info)) { 1156 nvmeadm_warn(npa, "failed to get namespace " 1157 "information for %s/%u", npa->npa_ctrl_name, nsid); 1158 exitcode = -1; 1159 continue; 1160 } 1161 1162 do_list_nsid(npa, info, ns_info); 1163 nvme_ns_info_free(ns_info); 1164 } 1165 1166 nvme_ns_discover_fini(iter); 1167 if (ret == NVME_ITER_ERROR) { 1168 nvmeadm_warn(npa, "failed to iterate all namespaces on %s", 1169 npa->npa_ctrl_name); 1170 } else { 1171 rv = 0; 1172 } 1173 1174 out: 1175 nvme_ctrl_info_free(info); 1176 return (rv); 1177 } 1178 1179 static void 1180 optparse_identify_ctrl(nvme_process_arg_t *npa) 1181 { 1182 int c; 1183 1184 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacn")) != -1) { 1185 switch (c) { 1186 case 'C': 1187 npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS; 1188 break; 1189 1190 case 'a': 1191 npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS; 1192 break; 1193 1194 case 'c': 1195 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST; 1196 break; 1197 1198 case 'n': 1199 npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST; 1200 break; 1201 1202 case '?': 1203 errx(-1, "unknown option: -%c", optopt); 1204 1205 case ':': 1206 errx(-1, "option -%c requires an argument", optopt); 1207 } 1208 } 1209 } 1210 1211 static void 1212 usage_identify_ctrl(const char *c_name) 1213 { 1214 (void) fprintf(stderr, "%s [-C | -c | [-a] -n] <ctl>[,...]\n\n" 1215 " Print detailed information about the specified NVMe " 1216 "controllers.\n", c_name); 1217 } 1218 1219 static int 1220 do_identify_ctrl(const nvme_process_arg_t *npa) 1221 { 1222 boolean_t alloc = B_FALSE; 1223 1224 if (npa->npa_ns != NULL) 1225 errx(-1, "identify-controller cannot be used on namespaces"); 1226 1227 if (npa->npa_argc > 0) { 1228 errx(-1, "%s passed extraneous arguments starting with %s", 1229 npa->npa_cmd->c_name, npa->npa_argv[0]); 1230 } 1231 1232 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 && 1233 npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) { 1234 errx(-1, "-C cannot be combined with other flags"); 1235 } 1236 1237 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 && 1238 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) { 1239 errx(-1, "-c cannot be combined with other flags"); 1240 } 1241 1242 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 && 1243 npa->npa_cmdflags != 1244 (NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) { 1245 errx(-1, "-a can only be used together with -n"); 1246 } 1247 1248 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) { 1249 alloc = B_TRUE; 1250 } 1251 1252 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) { 1253 const nvme_identify_nsid_t *idns; 1254 1255 if (!nvme_ctrl_info_common_ns(npa->npa_ctrl_info, &idns)) { 1256 nvmeadm_ctrl_info_warn(npa, "failed to get common " 1257 "namespace information for %s", npa->npa_name); 1258 return (-1); 1259 } 1260 1261 (void) printf("%s: ", npa->npa_name); 1262 nvme_print_identify_nsid(idns, npa->npa_version); 1263 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) { 1264 const char *caption; 1265 uint32_t cns; 1266 nvme_identify_nsid_list_t *idnslist; 1267 nvme_id_req_t *req; 1268 1269 if (alloc) { 1270 caption = "Identify Allocated Namespace List"; 1271 cns = NVME_IDENTIFY_NSID_ALLOC_LIST; 1272 } else { 1273 caption = "Identify Active Namespace List"; 1274 cns = NVME_IDENTIFY_NSID_LIST; 1275 } 1276 1277 if ((idnslist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) { 1278 err(-1, "failed to allocate identify buffer size"); 1279 } 1280 1281 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM, cns, 1282 &req)) { 1283 nvmeadm_fatal(npa, "failed to initialize %s request", 1284 caption); 1285 } 1286 1287 /* 1288 * Always set the NSID for these requests to NSID 0 so that way 1289 * we can start the list at the beginning. When we encounter 1290 * devices with more than 1024 NSIDs then we'll need to issue 1291 * additional requests. 1292 */ 1293 if (!nvme_id_req_set_nsid(req, 0) || 1294 !nvme_id_req_set_output(req, idnslist, 1295 NVME_IDENTIFY_BUFSIZE)) { 1296 nvmeadm_fatal(npa, "failed to set required fields for " 1297 "identify request"); 1298 } 1299 1300 if (!nvme_id_req_exec(req)) { 1301 nvmeadm_fatal(npa, "failed to execute identify " 1302 "request"); 1303 } 1304 nvme_id_req_fini(req); 1305 1306 (void) printf("%s: ", npa->npa_name); 1307 1308 nvme_print_identify_nsid_list(caption, idnslist); 1309 free(idnslist); 1310 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) { 1311 nvme_identify_ctrl_list_t *ctlist; 1312 nvme_id_req_t *req; 1313 1314 if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) { 1315 err(-1, "failed to allocate identify buffer size"); 1316 } 1317 1318 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM, 1319 NVME_IDENTIFY_CTRL_LIST, &req)) { 1320 nvmeadm_fatal(npa, "failed to initialize identify " 1321 "request"); 1322 } 1323 1324 if (!nvme_id_req_set_ctrlid(req, 0) || 1325 !nvme_id_req_set_output(req, ctlist, 1326 NVME_IDENTIFY_BUFSIZE)) { 1327 nvmeadm_fatal(npa, "failed to set required fields for " 1328 "identify request"); 1329 } 1330 if (!nvme_id_req_exec(req)) { 1331 nvmeadm_fatal(npa, "failed to execute identify " 1332 "request"); 1333 } 1334 nvme_id_req_fini(req); 1335 1336 (void) printf("%s: ", npa->npa_name); 1337 nvme_print_identify_ctrl_list("Identify Controller List", 1338 ctlist); 1339 free(ctlist); 1340 } else { 1341 uint32_t mpsmin; 1342 1343 if (!nvme_ctrl_info_pci_mps_min(npa->npa_ctrl_info, 1344 &mpsmin)) { 1345 nvmeadm_ctrl_info_fatal(npa, "failed to get minimum " 1346 "memory page size"); 1347 } 1348 1349 (void) printf("%s: ", npa->npa_name); 1350 nvme_print_identify_ctrl(npa->npa_idctl, mpsmin, 1351 npa->npa_version); 1352 } 1353 1354 return (0); 1355 } 1356 1357 static void 1358 optparse_identify_ns(nvme_process_arg_t *npa) 1359 { 1360 int c; 1361 1362 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":cd")) != -1) { 1363 switch (c) { 1364 case 'c': 1365 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST; 1366 break; 1367 1368 case 'd': 1369 npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST; 1370 break; 1371 1372 case '?': 1373 errx(-1, "unknown option: -%c", optopt); 1374 1375 case ':': 1376 errx(-1, "option -%c requires an argument", optopt); 1377 } 1378 } 1379 } 1380 1381 static void 1382 usage_identify_ns(const char *c_name) 1383 { 1384 (void) fprintf(stderr, "%s [-c | -d ] <ctl>/<ns>[,...]\n\n" 1385 " Print detailed information about the specified NVMe " 1386 "namespaces.\n", c_name); 1387 } 1388 1389 static int 1390 do_identify_ns(const nvme_process_arg_t *npa) 1391 { 1392 uint32_t nsid; 1393 1394 if (npa->npa_ns == NULL) 1395 errx(-1, "identify-namespace cannot be used on controllers"); 1396 1397 if (npa->npa_argc > 0) { 1398 errx(-1, "%s passed extraneous arguments starting with %s", 1399 npa->npa_cmd->c_name, npa->npa_argv[0]); 1400 } 1401 1402 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 && 1403 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) { 1404 errx(-1, "-c cannot be combined with other flags"); 1405 } 1406 1407 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 && 1408 npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) { 1409 errx(-1, "-d cannot be combined with other flags"); 1410 } 1411 1412 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) { 1413 errx(-1, "-a cannot be used on namespaces"); 1414 } 1415 1416 nsid = nvme_ns_info_nsid(npa->npa_ns_info); 1417 1418 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) { 1419 nvme_identify_ctrl_list_t *ctlist; 1420 nvme_id_req_t *req; 1421 1422 if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) { 1423 err(-1, "failed to allocate identify buffer size"); 1424 } 1425 1426 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM, 1427 NVME_IDENTIFY_NSID_CTRL_LIST, &req)) { 1428 nvmeadm_fatal(npa, "failed to initialize identify " 1429 "request"); 1430 } 1431 1432 if (!nvme_id_req_set_nsid(req, nsid) || 1433 !nvme_id_req_set_ctrlid(req, 0) || 1434 !nvme_id_req_set_output(req, ctlist, 1435 NVME_IDENTIFY_BUFSIZE)) { 1436 nvmeadm_fatal(npa, "failed to set required fields for " 1437 "identify request"); 1438 } 1439 1440 if (!nvme_id_req_exec(req)) { 1441 nvmeadm_fatal(npa, "failed to execute identify " 1442 "request"); 1443 } 1444 nvme_id_req_fini(req); 1445 1446 (void) printf("%s: ", npa->npa_name); 1447 nvme_print_identify_ctrl_list( 1448 "Identify Attached Controller List", ctlist); 1449 free(ctlist); 1450 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) { 1451 nvme_identify_nsid_desc_t *nsdesc; 1452 nvme_id_req_t *req; 1453 1454 if ((nsdesc = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) { 1455 err(-1, "failed to allocate identify buffer size"); 1456 } 1457 1458 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM, 1459 NVME_IDENTIFY_NSID_DESC, &req)) { 1460 nvmeadm_fatal(npa, "failed to initialize identify " 1461 "request"); 1462 } 1463 1464 if (!nvme_id_req_set_nsid(req, nsid) || 1465 !nvme_id_req_set_output(req, nsdesc, 1466 NVME_IDENTIFY_BUFSIZE)) { 1467 nvmeadm_fatal(npa, "failed to set required fields for " 1468 "identify request"); 1469 } 1470 1471 if (!nvme_id_req_exec(req)) { 1472 nvmeadm_fatal(npa, "failed to execute identify " 1473 "request"); 1474 } 1475 nvme_id_req_fini(req); 1476 1477 (void) printf("%s: ", npa->npa_name); 1478 nvme_print_identify_nsid_desc(nsdesc); 1479 free(nsdesc); 1480 } else { 1481 const nvme_identify_nsid_t *idns; 1482 1483 (void) printf("%s: ", npa->npa_name); 1484 idns = nvme_ns_info_identify(npa->npa_ns_info); 1485 nvme_print_identify_nsid(idns, npa->npa_version); 1486 } 1487 1488 return (0); 1489 } 1490 1491 static void 1492 optparse_identify(nvme_process_arg_t *npa) 1493 { 1494 int c; 1495 1496 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacdn")) != -1) { 1497 switch (c) { 1498 case 'C': 1499 npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS; 1500 break; 1501 1502 case 'a': 1503 npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS; 1504 break; 1505 1506 case 'c': 1507 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST; 1508 break; 1509 1510 case 'd': 1511 npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST; 1512 break; 1513 1514 case 'n': 1515 npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST; 1516 break; 1517 1518 case '?': 1519 errx(-1, "unknown option: -%c", optopt); 1520 1521 case ':': 1522 errx(-1, "option -%c requires an argument", optopt); 1523 1524 } 1525 } 1526 1527 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 && 1528 (npa->npa_cmdflags & 1529 ~(NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) != 0) { 1530 errx(-1, "-a can only be used alone or together with -n"); 1531 } 1532 1533 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 && 1534 npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) { 1535 errx(-1, "-C cannot be combined with other flags"); 1536 1537 } 1538 1539 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 && 1540 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) { 1541 errx(-1, "-c cannot be combined with other flags"); 1542 } 1543 1544 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 && 1545 npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) { 1546 errx(-1, "-d cannot be combined with other flags"); 1547 } 1548 } 1549 1550 static void 1551 usage_identify(const char *c_name) 1552 { 1553 (void) fprintf(stderr, 1554 "%s [ -C | -c | -d | [-a] -n ] <ctl>[/<ns>][,...]\n\n" 1555 " Print detailed information about the specified NVMe " 1556 "controllers and/or name-\n spaces.\n", c_name); 1557 } 1558 1559 static int 1560 do_identify(const nvme_process_arg_t *npa) 1561 { 1562 if (npa->npa_argc > 0) { 1563 errx(-1, "%s passed extraneous arguments starting with %s", 1564 npa->npa_cmd->c_name, npa->npa_argv[0]); 1565 } 1566 1567 if (npa->npa_ns != NULL) { 1568 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) 1569 errx(-1, "-C cannot be used on namespaces"); 1570 1571 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) 1572 errx(-1, "-a cannot be used on namespaces"); 1573 1574 if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) 1575 errx(-1, "-n cannot be used on namespaces"); 1576 1577 return (do_identify_ns(npa)); 1578 } else { 1579 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) 1580 errx(-1, "-d cannot be used on controllers"); 1581 1582 return (do_identify_ctrl(npa)); 1583 } 1584 } 1585 1586 static void 1587 optparse_list_logs(nvme_process_arg_t *npa) 1588 { 1589 int c; 1590 uint_t oflags = 0; 1591 boolean_t parse = B_FALSE; 1592 const char *fields = NULL; 1593 char *scope = NULL; 1594 ofmt_status_t oferr; 1595 nvmeadm_list_logs_t *nll; 1596 1597 if ((nll = calloc(1, sizeof (nvmeadm_list_logs_t))) == NULL) { 1598 err(-1, "failed to allocate memory to track log information"); 1599 } 1600 1601 npa->npa_cmd_arg = nll; 1602 1603 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:ps:")) != -1) { 1604 switch (c) { 1605 case 'a': 1606 nll->nll_unimpl = B_TRUE; 1607 break; 1608 case 'H': 1609 oflags |= OFMT_NOHEADER; 1610 break; 1611 case 'o': 1612 fields = optarg; 1613 break; 1614 case 'p': 1615 parse = B_TRUE; 1616 oflags |= OFMT_PARSABLE; 1617 break; 1618 case 's': 1619 scope = optarg; 1620 break; 1621 case '?': 1622 errx(-1, "unknown option: -%c", optopt); 1623 case ':': 1624 errx(-1, "option -%c requires an argument", optopt); 1625 } 1626 } 1627 1628 if (!parse) { 1629 oflags |= OFMT_WRAP; 1630 } 1631 1632 if (parse && fields == NULL) { 1633 errx(-1, "parsable mode (-p) requires fields specified with " 1634 "-o"); 1635 } 1636 1637 if (fields == NULL) { 1638 if (nll->nll_unimpl) { 1639 fields = nvmeadm_list_logs_fields_impl; 1640 } else { 1641 fields = nvmeadm_list_logs_fields; 1642 } 1643 } 1644 1645 if (scope != NULL) { 1646 const char *str; 1647 1648 while ((str = strsep(&scope, ",")) != NULL) { 1649 if (strcasecmp(str, "nvm") == 0) { 1650 nll->nll_scope |= NVME_LOG_SCOPE_NVM; 1651 } else if (strcasecmp(str, "ns") == 0 || 1652 strcasecmp(str, "namespace") == 0) { 1653 nll->nll_scope |= NVME_LOG_SCOPE_NS; 1654 } else if (strcasecmp(str, "ctrl") == 0 || 1655 strcasecmp(str, "controller") == 0) { 1656 nll->nll_scope |= NVME_LOG_SCOPE_CTRL; 1657 } else { 1658 errx(-1, "unknown scope string: '%s'; valid " 1659 "values are 'nvm', 'namespace', and " 1660 "'controller'", str); 1661 } 1662 } 1663 } 1664 1665 oferr = ofmt_open(fields, nvmeadm_list_logs_ofmt, oflags, 0, 1666 &npa->npa_ofmt); 1667 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx); 1668 1669 if (npa->npa_argc - optind > 1) { 1670 nll->nll_nfilts = npa->npa_argc - optind - 1; 1671 nll->nll_filts = npa->npa_argv + optind + 1; 1672 nll->nll_used = calloc(nll->nll_nfilts, sizeof (boolean_t)); 1673 if (nll->nll_used == NULL) { 1674 err(-1, "failed to allocate memory for tracking log " 1675 "page filters"); 1676 } 1677 } 1678 } 1679 1680 static void 1681 usage_list_logs(const char *c_name) 1682 { 1683 (void) fprintf(stderr, "%s [-H] [-o field,[...] [-p]] [-s scope,[...]] " 1684 "[-a]\n\t [<ctl>[/<ns>][,...] [logpage...]\n\n" 1685 " List log pages supported by controllers or namespaces.\n", 1686 c_name); 1687 } 1688 1689 static boolean_t 1690 do_list_logs_match(const nvme_log_disc_t *disc, nvmeadm_list_logs_t *nll) 1691 { 1692 if (nll->nll_nfilts <= 0) { 1693 return (B_TRUE); 1694 } 1695 1696 for (int i = 0; i < nll->nll_nfilts; i++) { 1697 if (strcmp(nvme_log_disc_name(disc), nll->nll_filts[i]) == 0) { 1698 nll->nll_used[i] = B_TRUE; 1699 return (B_TRUE); 1700 } 1701 } 1702 1703 return (B_FALSE); 1704 } 1705 1706 static int 1707 do_list_logs(const nvme_process_arg_t *npa) 1708 { 1709 nvme_log_disc_scope_t scope; 1710 nvme_log_iter_t *iter; 1711 nvme_iter_t ret; 1712 const nvme_log_disc_t *disc; 1713 nvmeadm_list_logs_t *nll = npa->npa_cmd_arg; 1714 1715 if (nll->nll_scope != 0) { 1716 scope = nll->nll_scope; 1717 } else if (npa->npa_ns != NULL) { 1718 scope = NVME_LOG_SCOPE_NS; 1719 } else { 1720 scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM; 1721 } 1722 1723 if (!nvme_log_discover_init(npa->npa_ctrl, scope, 0, &iter)) { 1724 nvmeadm_warn(npa, "failed to iterate logs on %s", 1725 npa->npa_ctrl_name); 1726 return (-1); 1727 } 1728 1729 while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) { 1730 if (do_list_logs_match(disc, nll)) { 1731 nvmeadm_list_logs_ofmt_arg_t print; 1732 1733 print.nlloa_name = npa->npa_name; 1734 print.nlloa_disc = disc; 1735 ofmt_print(npa->npa_ofmt, &print); 1736 nll->nll_nprint++; 1737 } 1738 } 1739 1740 nvme_log_discover_fini(iter); 1741 if (ret == NVME_ITER_ERROR) { 1742 nvmeadm_warn(npa, "failed to iterate logs on %s", 1743 npa->npa_ctrl_name); 1744 return (-1); 1745 } 1746 1747 for (int i = 0; i < nll->nll_nfilts; i++) { 1748 if (!nll->nll_used[i]) { 1749 warnx("log page filter '%s' did match any log pages", 1750 nll->nll_filts[i]); 1751 exitcode = -1; 1752 } 1753 } 1754 1755 if (nll->nll_nprint == 0) { 1756 if (nll->nll_nfilts == 0) { 1757 warnx("no log pages found for %s", npa->npa_name); 1758 } 1759 exitcode = -1; 1760 } 1761 1762 return (exitcode); 1763 } 1764 1765 static void 1766 usage_get_logpage(const char *c_name) 1767 { 1768 (void) fprintf(stderr, "%s [-O file] <ctl>[/<ns>][,...] <logpage>\n\n" 1769 " Print the specified log page of the specified NVMe " 1770 "controllers and/or name-\n spaces. Run nvmeadm list-logpages " 1771 "for supported log pages. All devices\n support error, health, " 1772 "and firmware.\n", c_name); 1773 } 1774 1775 static void 1776 usage_firmware_list(const char *c_name) 1777 { 1778 (void) fprintf(stderr, "%s <ctl>\n\n" 1779 " Print the log page that contains the list of firmware " 1780 "images installed on the specified NVMe controller.\n", c_name); 1781 } 1782 1783 static uint64_t 1784 do_get_logpage_size(const nvme_process_arg_t *npa, nvme_log_disc_t *disc, 1785 nvme_log_req_t *req) 1786 { 1787 uint64_t len, ret; 1788 void *buf; 1789 nvme_log_size_kind_t kind; 1790 1791 kind = nvme_log_disc_size(disc, &len); 1792 if (kind != NVME_LOG_SIZE_K_VAR) { 1793 return (len); 1794 } 1795 1796 /* 1797 * We have a log with a variable length size. To determine the actual 1798 * size we must actually determine the full length of this. 1799 */ 1800 if ((buf = malloc(len)) == NULL) { 1801 errx(-1, "failed to allocate %zu byte buffer to get log " 1802 "page size", len); 1803 } 1804 1805 if (!nvme_log_req_set_output(req, buf, len)) { 1806 nvmeadm_fatal(npa, "failed to set output parameters to " 1807 "determine log length"); 1808 } 1809 1810 if (!nvme_log_req_exec(req)) { 1811 nvmeadm_fatal(npa, "failed to execute log request %s to " 1812 "determine log length", npa->npa_argv[0]); 1813 } 1814 1815 if (!nvme_log_disc_calc_size(disc, &ret, buf, len)) { 1816 errx(-1, "failed to determine full %s log length", 1817 npa->npa_argv[0]); 1818 } 1819 1820 free(buf); 1821 return (ret); 1822 } 1823 1824 static void 1825 do_get_logpage_dump(const void *buf, size_t len, const char *file) 1826 { 1827 size_t off = 0; 1828 int fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0644); 1829 1830 if (fd < 0) { 1831 err(-1, "failed to create output file %s", file); 1832 } 1833 1834 while (len > 0) { 1835 ssize_t ret = write(fd, buf + off, len - off); 1836 if (ret < 0) { 1837 err(EXIT_FAILURE, "failed to write log data to file %s " 1838 "at offset %zu", file, off); 1839 } 1840 1841 off += (size_t)ret; 1842 len -= (size_t)ret; 1843 } 1844 1845 (void) close(fd); 1846 } 1847 1848 static int 1849 do_get_logpage_common(const nvme_process_arg_t *npa, const char *page) 1850 { 1851 int ret = 0; 1852 nvme_log_disc_t *disc; 1853 nvme_log_req_t *req; 1854 nvme_log_disc_scope_t scope; 1855 void *buf; 1856 size_t toalloc; 1857 nvmeadm_get_logpage_t *log = npa->npa_cmd_arg; 1858 1859 /* 1860 * If we have enough information to identify a log-page via libnvme (or 1861 * in the future take enough options to allow us to actually do this 1862 * manually), then we will fetch it. If we don't know how to print it, 1863 * then we'll just hex dump it for now. 1864 */ 1865 if (!nvme_log_req_init_by_name(npa->npa_ctrl, page, 0, &disc, &req)) { 1866 nvmeadm_fatal(npa, "could not initialize log request for %s", 1867 page); 1868 } 1869 1870 if (npa->npa_ns != NULL) { 1871 scope = NVME_LOG_SCOPE_NS; 1872 } else { 1873 scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM; 1874 } 1875 1876 if ((scope & nvme_log_disc_scopes(disc)) == 0) { 1877 errx(-1, "log page %s does not support operating on %s", page, 1878 npa->npa_ns != NULL ? "namespaces" : "controllers"); 1879 } 1880 1881 /* 1882 * In the future we should add options to allow one to specify and set 1883 * the fields for the lsp, lsi, etc. and set them here. 1884 */ 1885 1886 if (npa->npa_ns != NULL) { 1887 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info); 1888 1889 if (!nvme_log_req_set_nsid(req, nsid)) { 1890 nvmeadm_fatal(npa, "failed to set log request " 1891 "namespace ID to 0x%x", nsid); 1892 } 1893 } 1894 1895 /* 1896 * The output size should be the last thing that we determine as we may 1897 * need to issue a log request to figure out how much data we should 1898 * actually be reading. 1899 */ 1900 toalloc = do_get_logpage_size(npa, disc, req); 1901 buf = malloc(toalloc); 1902 if (buf == NULL) { 1903 err(-1, "failed to allocate %zu bytes for log " 1904 "request %s", toalloc, page); 1905 } 1906 1907 if (!nvme_log_req_set_output(req, buf, toalloc)) { 1908 nvmeadm_fatal(npa, "failed to set output parameters"); 1909 } 1910 1911 if (!nvme_log_req_exec(req)) { 1912 nvmeadm_fatal(npa, "failed to execute log request %s", 1913 npa->npa_argv[0]); 1914 } 1915 1916 if (log != NULL && log->ngl_output != NULL) { 1917 do_get_logpage_dump(buf, toalloc, log->ngl_output); 1918 goto done; 1919 } 1920 1921 (void) printf("%s: ", npa->npa_name); 1922 if (strcmp(page, "error") == 0) { 1923 size_t nlog = toalloc / sizeof (nvme_error_log_entry_t); 1924 nvme_print_error_log(nlog, buf, npa->npa_version); 1925 } else if (strcmp(page, "health") == 0) { 1926 nvme_print_health_log(buf, npa->npa_idctl, npa->npa_version); 1927 } else if (strcmp(page, "firmware") == 0) { 1928 nvme_print_fwslot_log(buf, npa->npa_idctl); 1929 } else { 1930 (void) printf("%s (%s)\n", nvme_log_disc_desc(disc), page); 1931 nvmeadm_dump_hex(buf, toalloc); 1932 } 1933 1934 done: 1935 free(buf); 1936 nvme_log_disc_free(disc); 1937 nvme_log_req_fini(req); 1938 1939 return (ret); 1940 } 1941 1942 static int 1943 do_get_logpage_fwslot(const nvme_process_arg_t *npa) 1944 { 1945 if (npa->npa_argc >= 1) { 1946 warnx("no additional arguments may be specified to %s", 1947 npa->npa_cmd->c_name); 1948 usage(npa->npa_cmd); 1949 exit(-1); 1950 } 1951 1952 return (do_get_logpage_common(npa, "firmware")); 1953 } 1954 1955 static void 1956 optparse_get_logpage(nvme_process_arg_t *npa) 1957 { 1958 int c; 1959 const char *output = NULL; 1960 nvmeadm_get_logpage_t *log; 1961 1962 if ((log = calloc(1, sizeof (nvmeadm_get_logpage_t))) == NULL) { 1963 err(-1, "failed to allocate memory to track log page " 1964 "information"); 1965 } 1966 1967 npa->npa_cmd_arg = log; 1968 1969 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":O:")) != -1) { 1970 switch (c) { 1971 case 'O': 1972 output = optarg; 1973 break; 1974 case '?': 1975 errx(-1, "unknown option: -%c", optopt); 1976 case ':': 1977 errx(-1, "option -%c requires an argument", optopt); 1978 } 1979 } 1980 1981 log->ngl_output = output; 1982 } 1983 1984 static int 1985 do_get_logpage(const nvme_process_arg_t *npa) 1986 { 1987 1988 if (npa->npa_argc < 1) { 1989 warnx("missing log page name"); 1990 usage(npa->npa_cmd); 1991 exit(-1); 1992 } 1993 1994 if (npa->npa_argc > 1) { 1995 warnx("only a single log page may be specified at a time"); 1996 usage(npa->npa_cmd); 1997 exit(-1); 1998 } 1999 2000 return (do_get_logpage_common(npa, npa->npa_argv[0])); 2001 } 2002 2003 static void 2004 optparse_list_features(nvme_process_arg_t *npa) 2005 { 2006 int c; 2007 uint_t oflags = 0; 2008 boolean_t parse = B_FALSE; 2009 const char *fields = NULL; 2010 nvmeadm_features_t *feat; 2011 ofmt_status_t oferr; 2012 2013 if ((feat = calloc(1, sizeof (nvmeadm_features_t))) == NULL) { 2014 err(-1, "failed to allocate memory to track feature " 2015 "information"); 2016 } 2017 2018 npa->npa_cmd_arg = feat; 2019 2020 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:p")) != -1) { 2021 switch (c) { 2022 case 'a': 2023 feat->nf_unimpl = B_TRUE; 2024 break; 2025 case 'H': 2026 oflags |= OFMT_NOHEADER; 2027 break; 2028 case 'o': 2029 fields = optarg; 2030 break; 2031 case 'p': 2032 parse = B_TRUE; 2033 oflags |= OFMT_PARSABLE; 2034 break; 2035 case '?': 2036 errx(-1, "unknown option: -%c", optopt); 2037 case ':': 2038 errx(-1, "option -%c requires an argument", optopt); 2039 } 2040 } 2041 2042 if (!parse) { 2043 oflags |= OFMT_WRAP; 2044 } 2045 2046 if (parse && fields == NULL) { 2047 errx(-1, "parsable mode (-p) requires fields specified with " 2048 "-o"); 2049 } 2050 2051 if (fields == NULL) { 2052 fields = nvmeadm_list_features_fields; 2053 } 2054 2055 oferr = ofmt_open(fields, nvmeadm_list_features_ofmt, oflags, 0, 2056 &npa->npa_ofmt); 2057 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx); 2058 2059 if (npa->npa_argc - optind > 1) { 2060 feat->nf_nfilts = (uint32_t)(npa->npa_argc - optind - 1); 2061 feat->nf_filts = npa->npa_argv + optind + 1; 2062 feat->nf_used = calloc(feat->nf_nfilts, sizeof (boolean_t)); 2063 if (feat->nf_used == NULL) { 2064 err(-1, "failed to allocate memory for tracking " 2065 "feature filters"); 2066 } 2067 } 2068 } 2069 2070 static void 2071 usage_list_features(const char *c_name) 2072 { 2073 (void) fprintf(stderr, "%s [-a] [-H] [-o field,[...] [-p]] " 2074 "<ctl>[/<ns>][,...]\n\t [feature...]\n\n" 2075 " List features supported by controllers or namespaces.\n", 2076 c_name); 2077 } 2078 2079 static boolean_t 2080 do_features_match(const nvme_feat_disc_t *disc, nvmeadm_features_t *nf) 2081 { 2082 if (nf->nf_nfilts == 0) { 2083 return (B_TRUE); 2084 } 2085 2086 for (uint32_t i = 0; i < nf->nf_nfilts; i++) { 2087 const char *match = nf->nf_filts[i]; 2088 long long fid; 2089 const char *err; 2090 2091 if (strcmp(nvme_feat_disc_short(disc), match) == 0 || 2092 strcasecmp(nvme_feat_disc_spec(disc), match) == 0) { 2093 nf->nf_used[i] = B_TRUE; 2094 return (B_TRUE); 2095 } 2096 2097 fid = strtonumx(match, 0, UINT32_MAX, &err, 0); 2098 if (err == NULL && fid == nvme_feat_disc_fid(disc)) { 2099 nf->nf_used[i] = B_TRUE; 2100 return (B_TRUE); 2101 } 2102 } 2103 2104 return (B_FALSE); 2105 } 2106 2107 2108 /* 2109 * This is a common entry point for both list-features and get-features, which 2110 * iterate over all features and take action for each one. 2111 */ 2112 typedef void (*do_features_cb_f)(const nvme_process_arg_t *, 2113 const nvme_feat_disc_t *); 2114 static int 2115 do_features(const nvme_process_arg_t *npa, nvmeadm_features_t *nf, 2116 do_features_cb_f func) 2117 { 2118 nvme_feat_scope_t scope; 2119 nvme_feat_iter_t *iter; 2120 nvme_iter_t ret; 2121 const nvme_feat_disc_t *disc; 2122 2123 if (npa->npa_ns != NULL) { 2124 scope = NVME_FEAT_SCOPE_NS; 2125 } else { 2126 scope = NVME_FEAT_SCOPE_CTRL; 2127 } 2128 2129 if (!nvme_feat_discover_init(npa->npa_ctrl, scope, 0, &iter)) { 2130 nvmeadm_warn(npa, "failed to iterate features on %s", 2131 npa->npa_ctrl_name); 2132 return (-1); 2133 } 2134 2135 while ((ret = nvme_feat_discover_step(iter, &disc)) == 2136 NVME_ITER_VALID) { 2137 if (do_features_match(disc, nf)) { 2138 if (!nf->nf_unimpl && nvme_feat_disc_impl(disc) == 2139 NVME_FEAT_IMPL_UNSUPPORTED) { 2140 continue; 2141 } 2142 2143 func(npa, disc); 2144 nf->nf_nprint++; 2145 } 2146 } 2147 2148 nvme_feat_discover_fini(iter); 2149 if (ret == NVME_ITER_ERROR) { 2150 nvmeadm_warn(npa, "failed to iterate features on %s", 2151 npa->npa_ctrl_name); 2152 return (-1); 2153 } 2154 2155 for (uint32_t i = 0; i < nf->nf_nfilts; i++) { 2156 if (!nf->nf_used[i]) { 2157 warnx("feature filter '%s' did match any features", 2158 nf->nf_filts[i]); 2159 exitcode = -1; 2160 } 2161 } 2162 2163 if (nf->nf_nprint == 0) { 2164 if (nf->nf_nfilts == 0) { 2165 warnx("no features found for %s", npa->npa_name); 2166 } 2167 exitcode = -1; 2168 } 2169 2170 return (exitcode); 2171 } 2172 2173 static void 2174 do_list_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc) 2175 { 2176 nvmeadm_list_features_ofmt_arg_t print; 2177 2178 print.nlfoa_name = npa->npa_name; 2179 print.nlfoa_feat = disc; 2180 ofmt_print(npa->npa_ofmt, &print); 2181 } 2182 2183 static int 2184 do_list_features(const nvme_process_arg_t *npa) 2185 { 2186 nvmeadm_features_t *nf = npa->npa_cmd_arg; 2187 2188 return (do_features(npa, nf, do_list_features_cb)); 2189 } 2190 2191 static void 2192 usage_get_features(const char *c_name) 2193 { 2194 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n" 2195 " Print the specified features of the specified NVMe controllers " 2196 "and/or\n namespaces. Feature support varies on the controller.\n" 2197 "Run 'nvmeadm list-features <ctl>' to see supported features.\n", 2198 c_name); 2199 } 2200 2201 /* 2202 * The nvmeadm(8) get-features output has traditionally swallowed certain errors 2203 * for features that it considers unimplemented in tandem with the kernel. With 2204 * the introduction of libnvme and ioctl interface changes, the kernel no longer 2205 * caches information about features that are unimplemented. 2206 * 2207 * There are two cases that we currently swallow errors on and the following 2208 * must all be true: 2209 * 2210 * 1) We have a controller error. 2211 * 2) The system doesn't know whether the feature is implemented or not. 2212 * 3) The controller error indicates that we have an invalid field. 2213 * 2214 * There is one additional wrinkle that we are currently papering over due to 2215 * the history of nvmeadm swallowing errors. The error recovery feature was made 2216 * explicitly namespace-specific in NVMe 1.4. However, various NVMe 1.3 devices 2217 * will error if we ask for it without specifying a namespace. Conversely, older 2218 * devices will be upset if you do ask for a namespace. This case can be removed 2219 * once we better survey devices and come up with a heuristic for how to handle 2220 * this across older generations. 2221 * 2222 * If we add a single feature endpoint that gives flexibility over how the 2223 * feature are listed, then we should not swallow errors. 2224 */ 2225 static boolean_t 2226 swallow_get_feat_err(const nvme_process_arg_t *npa, 2227 const nvme_feat_disc_t *disc) 2228 { 2229 uint32_t sct, sc; 2230 2231 if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_CONTROLLER) { 2232 return (B_FALSE); 2233 } 2234 2235 nvme_ctrl_deverr(npa->npa_ctrl, &sct, &sc); 2236 if (nvme_feat_disc_impl(disc) == NVME_FEAT_IMPL_UNKNOWN && 2237 sct == NVME_CQE_SCT_GENERIC && sc == NVME_CQE_SC_GEN_INV_FLD) { 2238 return (B_TRUE); 2239 } 2240 2241 if (nvme_feat_disc_fid(disc) == NVME_FEAT_ERROR && 2242 sct == NVME_CQE_SCT_GENERIC && (sc == NVME_CQE_SC_GEN_INV_FLD || 2243 sc == NVME_CQE_SC_GEN_INV_NS)) { 2244 return (B_TRUE); 2245 } 2246 2247 return (B_FALSE); 2248 } 2249 2250 static boolean_t 2251 do_get_feat_common(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc, 2252 uint32_t cdw11, uint32_t *cdw0, void **datap, size_t *lenp) 2253 { 2254 nvme_get_feat_req_t *req = NULL; 2255 void *data = NULL; 2256 uint64_t datalen = 0; 2257 nvme_get_feat_fields_t fields = nvme_feat_disc_fields_get(disc); 2258 2259 if (!nvme_get_feat_req_init_by_disc(npa->npa_ctrl, disc, &req)) { 2260 nvmeadm_warn(npa, "failed to initialize get feature request " 2261 "for feature %s", nvme_feat_disc_short(disc)); 2262 exitcode = -1; 2263 goto err; 2264 } 2265 2266 if ((fields & NVME_GET_FEAT_F_CDW11) != 0 && 2267 !nvme_get_feat_req_set_cdw11(req, cdw11)) { 2268 nvmeadm_warn(npa, "failed to set cdw11 to 0x%x for feature %s", 2269 cdw11, nvme_feat_disc_short(disc)); 2270 exitcode = -1; 2271 goto err; 2272 } 2273 2274 if ((fields & NVME_GET_FEAT_F_DATA) != 0) { 2275 datalen = nvme_feat_disc_data_size(disc); 2276 VERIFY3U(datalen, !=, 0); 2277 data = malloc(datalen); 2278 if (data == NULL) { 2279 err(-1, "failed to allocate %zu bytes for feature %s " 2280 "data buffer", datalen, nvme_feat_disc_short(disc)); 2281 } 2282 2283 if (!nvme_get_feat_req_set_output(req, data, datalen)) { 2284 nvmeadm_warn(npa, "failed to set output data for " 2285 "feature %s", nvme_feat_disc_short(disc)); 2286 exitcode = -1; 2287 goto err; 2288 } 2289 } 2290 2291 if ((fields & NVME_GET_FEAT_F_NSID) != 0) { 2292 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info); 2293 2294 if (!nvme_get_feat_req_set_nsid(req, nsid)) { 2295 nvmeadm_warn(npa, "failed to set nsid to 0x%x for " 2296 "feature %s", nsid, nvme_feat_disc_spec(disc)); 2297 exitcode = -1; 2298 goto err; 2299 } 2300 } 2301 2302 if (!nvme_get_feat_req_exec(req)) { 2303 if (!swallow_get_feat_err(npa, disc)) { 2304 nvmeadm_warn(npa, "failed to get feature %s", 2305 nvme_feat_disc_spec(disc)); 2306 exitcode = -1; 2307 } 2308 2309 goto err; 2310 } 2311 2312 if (!nvme_get_feat_req_get_cdw0(req, cdw0)) { 2313 nvmeadm_warn(npa, "failed to get cdw0 result data for %s", 2314 nvme_feat_disc_spec(disc)); 2315 goto err; 2316 } 2317 2318 *datap = data; 2319 *lenp = datalen; 2320 nvme_get_feat_req_fini(req); 2321 return (B_TRUE); 2322 2323 err: 2324 free(data); 2325 nvme_get_feat_req_fini(req); 2326 return (B_FALSE); 2327 } 2328 2329 static void 2330 do_get_feat_temp_thresh_one(const nvme_process_arg_t *npa, 2331 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat, 2332 const char *label, uint16_t tmpsel, uint16_t thsel) 2333 { 2334 uint32_t cdw0; 2335 void *buf = NULL; 2336 size_t buflen; 2337 nvme_temp_threshold_t tt; 2338 2339 tt.r = 0; 2340 tt.b.tt_tmpsel = tmpsel; 2341 tt.b.tt_thsel = thsel; 2342 2343 /* 2344 * The printing function treats the buffer argument as the label to 2345 * print for this threshold. 2346 */ 2347 if (!do_get_feat_common(npa, disc, tt.r, &cdw0, &buf, &buflen)) { 2348 return; 2349 } 2350 2351 feat->f_print(cdw0, (void *)label, 0, npa->npa_idctl, 2352 npa->npa_version); 2353 free(buf); 2354 } 2355 2356 /* 2357 * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the 2358 * device and changed the main device to have a composite temperature sensor. As 2359 * a result, there is a set of thresholds for each sensor. In addition, they 2360 * added both an over-temperature and under-temperature threshold. Since most 2361 * devices don't actually implement all the sensors, we get the health page and 2362 * see which sensors have a non-zero value to determine how to proceed. 2363 */ 2364 static boolean_t 2365 do_get_feat_temp_thresh(const nvme_process_arg_t *npa, 2366 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat) 2367 { 2368 nvme_log_req_t *req = NULL; 2369 nvme_log_disc_t *log_disc = NULL; 2370 size_t toalloc; 2371 void *buf = NULL; 2372 boolean_t ret = B_FALSE; 2373 const nvme_health_log_t *hlog; 2374 2375 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL); 2376 do_get_feat_temp_thresh_one(npa, disc, feat, 2377 "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER); 2378 2379 if (!nvme_version_check(npa, &nvme_vers_1v2)) { 2380 return (B_TRUE); 2381 } 2382 2383 if (!nvme_log_req_init_by_name(npa->npa_ctrl, "health", 0, &log_disc, 2384 &req)) { 2385 nvmeadm_warn(npa, "failed to initialize health log page " 2386 "request"); 2387 return (B_FALSE); 2388 } 2389 2390 toalloc = do_get_logpage_size(npa, log_disc, req); 2391 buf = malloc(toalloc); 2392 if (buf == NULL) { 2393 err(-1, "failed to allocate %zu bytes for health log page", 2394 toalloc); 2395 } 2396 2397 if (!nvme_log_req_set_output(req, buf, toalloc)) { 2398 nvmeadm_warn(npa, "failed to set output parameters for health " 2399 "log page"); 2400 goto out; 2401 } 2402 2403 if (!nvme_log_req_exec(req)) { 2404 nvmeadm_warn(npa, "failed to retrieve the health log page"); 2405 goto out; 2406 } 2407 2408 /* cast required to prove our intentionality to smatch */ 2409 hlog = (const nvme_health_log_t *)buf; 2410 2411 do_get_feat_temp_thresh_one(npa, disc, feat, 2412 "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER); 2413 if (hlog->hl_temp_sensor_1 != 0) { 2414 do_get_feat_temp_thresh_one(npa, disc, feat, 2415 "Temp. Sensor 1 Over Temp. Threshold", 1, 2416 NVME_TEMP_THRESH_OVER); 2417 do_get_feat_temp_thresh_one(npa, disc, feat, 2418 "Temp. Sensor 1 Under Temp. Threshold", 1, 2419 NVME_TEMP_THRESH_UNDER); 2420 } 2421 2422 if (hlog->hl_temp_sensor_2 != 0) { 2423 do_get_feat_temp_thresh_one(npa, disc, feat, 2424 "Temp. Sensor 2 Over Temp. Threshold", 2, 2425 NVME_TEMP_THRESH_OVER); 2426 do_get_feat_temp_thresh_one(npa, disc, feat, 2427 "Temp. Sensor 2 Under Temp. Threshold", 2, 2428 NVME_TEMP_THRESH_UNDER); 2429 } 2430 2431 if (hlog->hl_temp_sensor_3 != 0) { 2432 do_get_feat_temp_thresh_one(npa, disc, feat, 2433 "Temp. Sensor 3 Over Temp. Threshold", 3, 2434 NVME_TEMP_THRESH_OVER); 2435 do_get_feat_temp_thresh_one(npa, disc, feat, 2436 "Temp. Sensor 3 Under Temp. Threshold", 3, 2437 NVME_TEMP_THRESH_UNDER); 2438 } 2439 2440 if (hlog->hl_temp_sensor_4 != 0) { 2441 do_get_feat_temp_thresh_one(npa, disc, feat, 2442 "Temp. Sensor 4 Over Temp. Threshold", 4, 2443 NVME_TEMP_THRESH_OVER); 2444 do_get_feat_temp_thresh_one(npa, disc, feat, 2445 "Temp. Sensor 4 Under Temp. Threshold", 4, 2446 NVME_TEMP_THRESH_UNDER); 2447 } 2448 2449 if (hlog->hl_temp_sensor_5 != 0) { 2450 do_get_feat_temp_thresh_one(npa, disc, feat, 2451 "Temp. Sensor 5 Over Temp. Threshold", 5, 2452 NVME_TEMP_THRESH_OVER); 2453 do_get_feat_temp_thresh_one(npa, disc, feat, 2454 "Temp. Sensor 5 Under Temp. Threshold", 5, 2455 NVME_TEMP_THRESH_UNDER); 2456 } 2457 2458 if (hlog->hl_temp_sensor_6 != 0) { 2459 do_get_feat_temp_thresh_one(npa, disc, feat, 2460 "Temp. Sensor 6 Over Temp. Threshold", 6, 2461 NVME_TEMP_THRESH_OVER); 2462 do_get_feat_temp_thresh_one(npa, disc, feat, 2463 "Temp. Sensor 6 Under Temp. Threshold", 6, 2464 NVME_TEMP_THRESH_UNDER); 2465 } 2466 2467 if (hlog->hl_temp_sensor_7 != 0) { 2468 do_get_feat_temp_thresh_one(npa, disc, feat, 2469 "Temp. Sensor 7 Over Temp. Threshold", 7, 2470 NVME_TEMP_THRESH_OVER); 2471 do_get_feat_temp_thresh_one(npa, disc, feat, 2472 "Temp. Sensor 7 Under Temp. Threshold", 7, 2473 NVME_TEMP_THRESH_UNDER); 2474 } 2475 2476 if (hlog->hl_temp_sensor_8 != 0) { 2477 do_get_feat_temp_thresh_one(npa, disc, feat, 2478 "Temp. Sensor 8 Over Temp. Threshold", 8, 2479 NVME_TEMP_THRESH_OVER); 2480 do_get_feat_temp_thresh_one(npa, disc, feat, 2481 "Temp. Sensor 8 Under Temp. Threshold", 8, 2482 NVME_TEMP_THRESH_UNDER); 2483 } 2484 2485 ret = B_TRUE; 2486 out: 2487 nvme_log_req_fini(req); 2488 free(buf); 2489 return (ret); 2490 } 2491 2492 static boolean_t 2493 do_get_feat_intr_vect(const nvme_process_arg_t *npa, 2494 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat) 2495 { 2496 uint32_t nintrs; 2497 boolean_t ret = B_TRUE; 2498 2499 if (!nvme_ctrl_info_pci_nintrs(npa->npa_ctrl_info, &nintrs)) { 2500 nvmeadm_ctrl_info_warn(npa, "failed to get interrupt count " 2501 "from controller %s information snapshot", npa->npa_name); 2502 return (B_FALSE); 2503 } 2504 2505 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL); 2506 for (uint32_t i = 0; i < nintrs; i++) { 2507 uint32_t cdw0; 2508 void *buf; 2509 size_t buflen; 2510 nvme_intr_vect_t vect; 2511 2512 vect.r = 0; 2513 vect.b.iv_iv = i; 2514 2515 if (!do_get_feat_common(npa, disc, vect.r, &cdw0, &buf, 2516 &buflen)) { 2517 ret = B_FALSE; 2518 continue; 2519 } 2520 2521 feat->f_print(cdw0, buf, buflen, npa->npa_idctl, 2522 npa->npa_version); 2523 free(buf); 2524 } 2525 2526 return (ret); 2527 } 2528 2529 /* 2530 * We've been asked to print the following feature that the controller probably 2531 * supports. Find our internal feature information for this to see if we know 2532 * how to deal with it. 2533 */ 2534 static void 2535 do_get_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc) 2536 { 2537 const nvmeadm_feature_t *feat = NULL; 2538 uint32_t fid = nvme_feat_disc_fid(disc); 2539 nvme_get_feat_fields_t fields; 2540 void *data = NULL; 2541 size_t datalen = 0; 2542 uint32_t cdw0; 2543 2544 for (size_t i = 0; i < ARRAY_SIZE(features); i++) { 2545 if (features[i].f_feature == fid) { 2546 feat = &features[i]; 2547 break; 2548 } 2549 } 2550 2551 /* 2552 * Determine if we have enough logic in here to get and print the 2553 * feature. The vast majority of NVMe features only output a single 2554 * uint32_t in cdw0 and potentially a data buffer. As long as no input 2555 * arguments are required, then we can go ahead and get this and print 2556 * the data. If there is, then we will refuse unless we have a 2557 * particular function. If we have a specific get function, we expect it 2558 * to do all the printing. 2559 */ 2560 if (feat != NULL && feat->f_get != NULL) { 2561 if (!feat->f_get(npa, disc, feat)) { 2562 exitcode = -1; 2563 } 2564 return; 2565 } 2566 2567 fields = nvme_feat_disc_fields_get(disc); 2568 if ((fields & NVME_GET_FEAT_F_CDW11) != 0) { 2569 warnx("unable to get feature %s due to missing nvmeadm(8) " 2570 "implementation logic", nvme_feat_disc_spec(disc)); 2571 exitcode = -1; 2572 return; 2573 } 2574 2575 /* 2576 * We do not set exitcode on failure here so that way we can swallow 2577 * errors from unimplemented features. 2578 */ 2579 if (!do_get_feat_common(npa, disc, 0, &cdw0, &data, &datalen)) { 2580 return; 2581 } 2582 2583 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL); 2584 if (feat != NULL && feat->f_print != NULL) { 2585 feat->f_print(cdw0, data, datalen, npa->npa_idctl, 2586 npa->npa_version); 2587 } else { 2588 nvme_feat_output_t output = nvme_feat_disc_output_get(disc); 2589 nvme_print_feat_unknown(output, cdw0, data, datalen); 2590 } 2591 2592 free(data); 2593 } 2594 2595 /* 2596 * This is an entry point which prints every feature that we know about. We 2597 * often go to lengths to discover all the variable inputs that can be used for 2598 * a given feature that requires an argument in cdw11. Due to the semantics of 2599 * filtering being used for features and the need to print each feature, this is 2600 * not the place to add general field filtering or a means to request a specific 2601 * cdw11 argument or similar. Instead, a new get-feature which requires someone 2602 * to specify the short name for a feature and then allows particular fields to 2603 * be grabbed and arguments should be created instead. 2604 * 2605 * This uses the same general feature logic that underpins do_list_features() 2606 * and therefore we transform filter arguments into the same style used there. 2607 */ 2608 static int 2609 do_get_features(const nvme_process_arg_t *npa) 2610 { 2611 char *fstr = NULL; 2612 char **filts = NULL; 2613 boolean_t *used = NULL; 2614 nvmeadm_features_t nf; 2615 int ret; 2616 2617 if (npa->npa_argc > 1) 2618 errx(-1, "unexpected arguments"); 2619 2620 if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) < 2621 NVME_NS_DISC_F_ACTIVE) { 2622 errx(-1, "cannot get feature: namespace is inactive"); 2623 } 2624 2625 /* 2626 * We always leave nf_unimpl set to false as we don't want to bother 2627 * trying to print a feature that we know the device doesn't support. 2628 */ 2629 (void) memset(&nf, 0, sizeof (nvmeadm_features_t)); 2630 2631 /* 2632 * If we've been given a series of features to print, treat those as 2633 * filters on the features as we're walking them to determine which to 2634 * print or not. 2635 */ 2636 if (npa->npa_argc == 1) { 2637 char *f; 2638 uint32_t i; 2639 2640 nf.nf_nfilts = 1; 2641 fstr = strdup(npa->npa_argv[0]); 2642 2643 if (fstr == NULL) { 2644 err(-1, "failed to allocate memory to duplicate " 2645 "feature string"); 2646 } 2647 2648 for (const char *c = strchr(fstr, ','); c != NULL; 2649 c = strchr(c + 1, ',')) { 2650 nf.nf_nfilts++; 2651 } 2652 2653 filts = calloc(nf.nf_nfilts, sizeof (char *)); 2654 if (filts == NULL) { 2655 err(-1, "failed to allocate memory for filter list"); 2656 } 2657 2658 i = 0; 2659 while ((f = strsep(&fstr, ",")) != NULL) { 2660 filts[i] = f; 2661 i++; 2662 } 2663 VERIFY3U(i, ==, nf.nf_nfilts); 2664 nf.nf_filts = filts; 2665 2666 used = calloc(nf.nf_nfilts, sizeof (boolean_t)); 2667 if (used == NULL) { 2668 err(-1, "failed to allocate memory for filter use " 2669 "tracking"); 2670 } 2671 nf.nf_used = used; 2672 } 2673 2674 (void) printf("%s: Get Features\n", npa->npa_name); 2675 ret = do_features(npa, &nf, do_get_features_cb); 2676 2677 free(fstr); 2678 free(filts); 2679 free(used); 2680 return (ret); 2681 } 2682 2683 static int 2684 do_format_common(const nvme_process_arg_t *npa, uint32_t lbaf, 2685 uint32_t ses) 2686 { 2687 int ret = 0; 2688 nvme_format_req_t *req; 2689 2690 if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) < 2691 NVME_NS_DISC_F_ACTIVE) { 2692 errx(-1, "cannot %s: namespace is inactive", 2693 npa->npa_cmd->c_name); 2694 } 2695 2696 if (!nvme_format_req_init(npa->npa_ctrl, &req)) { 2697 nvmeadm_fatal(npa, "failed to initialize format request for " 2698 "%s", npa->npa_name); 2699 } 2700 2701 if (npa->npa_ns != NULL) { 2702 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info); 2703 2704 if (!nvme_format_req_set_nsid(req, nsid)) { 2705 nvmeadm_fatal(npa, "failed to set format request " 2706 "namespace ID to 0x%x", nsid); 2707 } 2708 } 2709 2710 if (!nvme_format_req_set_lbaf(req, lbaf) || 2711 !nvme_format_req_set_ses(req, ses)) { 2712 nvmeadm_fatal(npa, "failed to set format request fields for %s", 2713 npa->npa_name); 2714 } 2715 2716 if (do_detach(npa) != 0) { 2717 errx(-1, "cannot %s %s due to namespace detach failure", 2718 npa->npa_cmd->c_name, npa->npa_name); 2719 } 2720 2721 if (!nvme_format_req_exec(req)) { 2722 nvmeadm_warn(npa, "failed to %s %s", npa->npa_cmd->c_name, 2723 npa->npa_name); 2724 ret = -1; 2725 } 2726 2727 if (do_attach(npa) != 0) 2728 ret = -1; 2729 2730 return (ret); 2731 } 2732 2733 static void 2734 usage_format(const char *c_name) 2735 { 2736 (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n" 2737 " Format one or all namespaces of the specified NVMe " 2738 "controller. Supported LBA\n formats can be queried with " 2739 "the \"%s identify\" command on the namespace\n to be " 2740 "formatted.\n", c_name, getprogname()); 2741 } 2742 2743 static uint32_t 2744 do_format_determine_lbaf(const nvme_process_arg_t *npa) 2745 { 2746 const nvme_nvm_lba_fmt_t *fmt; 2747 nvme_ns_info_t *ns_info = NULL; 2748 uint32_t lbaf; 2749 2750 if (npa->npa_argc > 0) { 2751 unsigned long lba; 2752 uint32_t nlbaf = nvme_ctrl_info_nformats(npa->npa_ctrl_info); 2753 2754 errno = 0; 2755 lba = strtoul(npa->npa_argv[0], NULL, 10); 2756 if (errno != 0 || lba >= nlbaf) 2757 errx(-1, "invalid LBA format %s", npa->npa_argv[0]); 2758 2759 if (!nvme_ctrl_info_format(npa->npa_ctrl_info, (uint32_t)lba, 2760 &fmt)) { 2761 nvmeadm_fatal(npa, "failed to get LBA format %lu " 2762 "information", lba); 2763 } 2764 } else { 2765 /* 2766 * If we have a namespace then we use the current namespace's 2767 * LBA format. If we don't have a namespace, then we promised 2768 * we'd look at namespace 1 in the manual page. 2769 */ 2770 if (npa->npa_ns_info == NULL) { 2771 if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, 1, 2772 &ns_info)) { 2773 nvmeadm_fatal(npa, "failed to get namespace 1 " 2774 "information, please explicitly specify an " 2775 "LBA format"); 2776 } 2777 2778 if (!nvme_ns_info_curformat(ns_info, &fmt)) { 2779 nvmeadm_fatal(npa, "failed to retrieve current " 2780 "namespace format from namespace 1"); 2781 } 2782 } else { 2783 if (!nvme_ns_info_curformat(npa->npa_ns_info, &fmt)) { 2784 nvmeadm_fatal(npa, "failed to get the current " 2785 "format information from %s", 2786 npa->npa_name); 2787 } 2788 } 2789 } 2790 2791 if (nvme_nvm_lba_fmt_meta_size(fmt) != 0) { 2792 errx(-1, "LBA formats with metadata are not supported"); 2793 } 2794 2795 lbaf = nvme_nvm_lba_fmt_id(fmt); 2796 nvme_ns_info_free(ns_info); 2797 return (lbaf); 2798 } 2799 2800 static int 2801 do_format(const nvme_process_arg_t *npa) 2802 { 2803 uint32_t lbaf; 2804 2805 if (npa->npa_argc > 1) { 2806 errx(-1, "%s passed extraneous arguments starting with %s", 2807 npa->npa_cmd->c_name, npa->npa_argv[1]); 2808 } 2809 2810 lbaf = do_format_determine_lbaf(npa); 2811 return (do_format_common(npa, lbaf, 0)); 2812 } 2813 2814 static void 2815 usage_secure_erase(const char *c_name) 2816 { 2817 (void) fprintf(stderr, "%s [-c] <ctl>[/<ns>]\n\n" 2818 " Secure-Erase one or all namespaces of the specified " 2819 "NVMe controller.\n", c_name); 2820 } 2821 2822 static void 2823 optparse_secure_erase(nvme_process_arg_t *npa) 2824 { 2825 int c; 2826 2827 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":c")) != -1) { 2828 switch (c) { 2829 case 'c': 2830 npa->npa_cmdflags |= NVMEADM_O_SE_CRYPTO; 2831 break; 2832 2833 case '?': 2834 errx(-1, "unknown option: -%c", optopt); 2835 2836 case ':': 2837 errx(-1, "option -%c requires an argument", optopt); 2838 2839 } 2840 } 2841 } 2842 2843 static int 2844 do_secure_erase(const nvme_process_arg_t *npa) 2845 { 2846 unsigned long lbaf; 2847 uint8_t ses = NVME_FRMT_SES_USER; 2848 2849 if (npa->npa_argc > 0) { 2850 errx(-1, "%s passed extraneous arguments starting with %s", 2851 npa->npa_cmd->c_name, npa->npa_argv[0]); 2852 } 2853 2854 if ((npa->npa_cmdflags & NVMEADM_O_SE_CRYPTO) != 0) 2855 ses = NVME_FRMT_SES_CRYPTO; 2856 2857 lbaf = do_format_determine_lbaf(npa); 2858 return (do_format_common(npa, lbaf, ses)); 2859 } 2860 2861 static void 2862 usage_attach_detach(const char *c_name) 2863 { 2864 (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n" 2865 " %c%s blkdev(4D) %s one or all namespaces of the " 2866 "specified NVMe controller.\n", 2867 c_name, toupper(c_name[0]), &c_name[1], 2868 c_name[0] == 'd' ? "from" : "to"); 2869 } 2870 2871 static int 2872 do_attach(const nvme_process_arg_t *npa) 2873 { 2874 int rv; 2875 nvme_ns_iter_t *iter = NULL; 2876 nvme_iter_t ret; 2877 const nvme_ns_disc_t *disc; 2878 2879 if (npa->npa_ns != NULL) { 2880 if (!nvme_ns_bd_attach(npa->npa_ns)) { 2881 nvmeadm_warn(npa, "faild to attach %s", npa->npa_name); 2882 return (-1); 2883 } 2884 return (0); 2885 } 2886 2887 if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_NOT_IGNORED, 2888 &iter)) { 2889 nvmeadm_fatal(npa, "failed to initialize namespace discovery " 2890 "on %s", npa->npa_name); 2891 } 2892 2893 rv = 0; 2894 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) { 2895 nvme_ns_t *ns; 2896 uint32_t nsid; 2897 2898 if (nvme_ns_disc_level(disc) == NVME_NS_DISC_F_BLKDEV) 2899 continue; 2900 2901 nsid = nvme_ns_disc_nsid(disc); 2902 if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) { 2903 nvmeadm_warn(npa, "failed to open namespace %s/%u " 2904 "handle", npa->npa_name, nsid); 2905 rv = -1; 2906 continue; 2907 } 2908 2909 if (!nvme_ns_bd_attach(ns)) { 2910 nvmeadm_warn(npa, "failed to attach namespace " 2911 "%s/%u", npa->npa_name, nsid); 2912 rv = -1; 2913 } 2914 nvme_ns_fini(ns); 2915 } 2916 2917 nvme_ns_discover_fini(iter); 2918 if (ret == NVME_ITER_ERROR) { 2919 nvmeadm_warn(npa, "failed to iterate namespaces on %s", 2920 npa->npa_name); 2921 rv = -1; 2922 } 2923 2924 return (rv); 2925 } 2926 2927 static int 2928 do_detach(const nvme_process_arg_t *npa) 2929 { 2930 int rv; 2931 nvme_ns_iter_t *iter = NULL; 2932 nvme_iter_t ret; 2933 const nvme_ns_disc_t *disc; 2934 2935 if (npa->npa_ns != NULL) { 2936 if (!nvme_ns_bd_detach(npa->npa_ns)) { 2937 nvmeadm_warn(npa, "failed to detach %s", npa->npa_name); 2938 return (-1); 2939 } 2940 return (0); 2941 } 2942 2943 if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_BLKDEV, 2944 &iter)) { 2945 nvmeadm_fatal(npa, "failed to initialize namespace discovery " 2946 "on %s", npa->npa_name); 2947 } 2948 2949 rv = 0; 2950 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) { 2951 nvme_ns_t *ns; 2952 uint32_t nsid = nvme_ns_disc_nsid(disc); 2953 2954 if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) { 2955 nvmeadm_warn(npa, "failed to open namespace %s/%u " 2956 "handle", npa->npa_name, nsid); 2957 rv = -1; 2958 continue; 2959 } 2960 2961 if (!nvme_ns_bd_detach(ns)) { 2962 nvmeadm_warn(npa, "failed to detach namespace " 2963 "%s/%u", npa->npa_name, nsid); 2964 rv = -1; 2965 } 2966 nvme_ns_fini(ns); 2967 } 2968 2969 nvme_ns_discover_fini(iter); 2970 if (ret == NVME_ITER_ERROR) { 2971 nvmeadm_warn(npa, "failed to iterate namespaces on %s", 2972 npa->npa_name); 2973 rv = -1; 2974 } 2975 2976 return (rv); 2977 } 2978 2979 static void 2980 usage_firmware_load(const char *c_name) 2981 { 2982 (void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n" 2983 " Load firmware <image file> to offset <offset>.\n" 2984 " The firmware needs to be committed to a slot using " 2985 "\"nvmeadm commit-firmware\"\n command.\n", c_name); 2986 } 2987 2988 /* 2989 * Read exactly len bytes, or until eof. 2990 */ 2991 static size_t 2992 read_block(const nvme_process_arg_t *npa, int fd, char *buf, size_t len) 2993 { 2994 size_t remain; 2995 2996 remain = len; 2997 while (remain > 0) { 2998 ssize_t bytes = read(fd, buf, remain); 2999 if (bytes == 0) 3000 break; 3001 3002 if (bytes < 0) { 3003 if (errno == EINTR) 3004 continue; 3005 3006 err(-1, "Error reading \"%s\"", npa->npa_argv[0]); 3007 } 3008 3009 buf += (size_t)bytes; 3010 remain -= (size_t)bytes; 3011 } 3012 3013 return (len - remain); 3014 } 3015 3016 /* 3017 * Convert a string to a valid firmware upload offset (in bytes). 3018 */ 3019 static uint64_t 3020 get_fw_offsetb(char *str) 3021 { 3022 longlong_t offsetb; 3023 char *valend; 3024 3025 errno = 0; 3026 offsetb = strtoll(str, &valend, 0); 3027 if (errno != 0 || *valend != '\0' || offsetb < 0 || 3028 offsetb > NVME_FW_OFFSETB_MAX) 3029 errx(-1, "Offset must be numeric and in the range of 0 to %llu", 3030 NVME_FW_OFFSETB_MAX); 3031 3032 if ((offsetb & NVME_DWORD_MASK) != 0) 3033 errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE); 3034 3035 return ((uint64_t)offsetb); 3036 } 3037 3038 #define FIRMWARE_READ_BLKSIZE (64 * 1024) /* 64K */ 3039 3040 static int 3041 do_firmware_load(const nvme_process_arg_t *npa) 3042 { 3043 int fw_fd; 3044 uint64_t offset = 0; 3045 size_t size, len; 3046 char buf[FIRMWARE_READ_BLKSIZE]; 3047 3048 if (npa->npa_argc > 2) 3049 errx(-1, "%s passed extraneous arguments starting with %s", 3050 npa->npa_cmd->c_name, npa->npa_argv[2]); 3051 3052 if (npa->npa_argc == 0) 3053 errx(-1, "Requires firmware file name, and an " 3054 "optional offset"); 3055 3056 if (npa->npa_ns != NULL) 3057 errx(-1, "Firmware loading not available on a per-namespace " 3058 "basis"); 3059 3060 if (npa->npa_argc == 2) 3061 offset = get_fw_offsetb(npa->npa_argv[1]); 3062 3063 fw_fd = open(npa->npa_argv[0], O_RDONLY); 3064 if (fw_fd < 0) 3065 errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0], 3066 strerror(errno)); 3067 3068 size = 0; 3069 do { 3070 len = read_block(npa, fw_fd, buf, sizeof (buf)); 3071 3072 if (len == 0) 3073 break; 3074 3075 if (!nvme_fw_load(npa->npa_ctrl, buf, len, offset)) { 3076 nvmeadm_fatal(npa, "failed to load firmware image " 3077 "\"%s\" at offset %" PRIu64, npa->npa_argv[0], 3078 offset); 3079 } 3080 3081 offset += len; 3082 size += len; 3083 } while (len == sizeof (buf)); 3084 3085 (void) close(fw_fd); 3086 3087 if (verbose) 3088 (void) printf("%zu bytes downloaded.\n", size); 3089 3090 return (0); 3091 } 3092 3093 /* 3094 * Common firmware commit for nvmeadm commit-firmware and activate-firmware. 3095 */ 3096 static void 3097 nvmeadm_firmware_commit(const nvme_process_arg_t *npa, uint32_t slot, 3098 uint32_t act) 3099 { 3100 nvme_fw_commit_req_t *req; 3101 3102 if (!nvme_fw_commit_req_init(npa->npa_ctrl, &req)) { 3103 nvmeadm_fatal(npa, "failed to initialize firmware commit " 3104 "request for %s", npa->npa_name); 3105 } 3106 3107 if (!nvme_fw_commit_req_set_slot(req, slot) || 3108 !nvme_fw_commit_req_set_action(req, act)) { 3109 nvmeadm_fatal(npa, "failed to set firmware commit fields for " 3110 "%s", npa->npa_name); 3111 } 3112 3113 if (!nvme_fw_commit_req_exec(req)) { 3114 nvmeadm_fatal(npa, "failed to %s firmware on %s", 3115 npa->npa_cmd->c_name, npa->npa_name); 3116 } 3117 3118 nvme_fw_commit_req_fini(req); 3119 } 3120 3121 /* 3122 * Convert str to a valid firmware slot number. 3123 */ 3124 static uint32_t 3125 get_slot_number(char *str) 3126 { 3127 longlong_t slot; 3128 char *valend; 3129 3130 errno = 0; 3131 slot = strtoll(str, &valend, 0); 3132 if (errno != 0 || *valend != '\0' || 3133 slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX) 3134 errx(-1, "Slot must be numeric and in the range of %u to %u", 3135 NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX); 3136 3137 return ((uint32_t)slot); 3138 } 3139 3140 static void 3141 usage_firmware_commit(const char *c_name) 3142 { 3143 (void) fprintf(stderr, "%s <ctl> <slot>\n\n" 3144 " Commit previously downloaded firmware to slot <slot>.\n" 3145 " The firmware is only activated after a " 3146 "\"nvmeadm activate-firmware\" command.\n", c_name); 3147 } 3148 3149 static int 3150 do_firmware_commit(const nvme_process_arg_t *npa) 3151 { 3152 uint32_t slot; 3153 3154 if (npa->npa_argc > 1) 3155 errx(-1, "%s passed extraneous arguments starting with %s", 3156 npa->npa_cmd->c_name, npa->npa_argv[1]); 3157 3158 if (npa->npa_argc == 0) 3159 errx(-1, "Firmware slot number is required"); 3160 3161 if (npa->npa_ns != NULL) 3162 errx(-1, "Firmware committing not available on a per-namespace " 3163 "basis"); 3164 3165 slot = get_slot_number(npa->npa_argv[0]); 3166 3167 if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly) 3168 errx(-1, "Cannot commit firmware to slot 1: slot is read-only"); 3169 3170 nvmeadm_firmware_commit(npa, slot, NVME_FWC_SAVE); 3171 3172 if (verbose) 3173 (void) printf("Firmware committed to slot %u.\n", slot); 3174 3175 return (0); 3176 } 3177 3178 static void 3179 usage_firmware_activate(const char *c_name) 3180 { 3181 (void) fprintf(stderr, "%s <ctl> <slot>\n\n" 3182 " Activate firmware in slot <slot>.\n" 3183 " The firmware will be in use after the next system reset.\n", 3184 c_name); 3185 } 3186 3187 static int 3188 do_firmware_activate(const nvme_process_arg_t *npa) 3189 { 3190 uint32_t slot; 3191 3192 if (npa->npa_argc > 1) 3193 errx(-1, "%s passed extraneous arguments starting with %s", 3194 npa->npa_cmd->c_name, npa->npa_argv[1]); 3195 3196 if (npa->npa_argc == 0) 3197 errx(-1, "Firmware slot number is required"); 3198 3199 if (npa->npa_ns != NULL) 3200 errx(-1, "Firmware activation not available on a per-namespace " 3201 "basis"); 3202 3203 slot = get_slot_number(npa->npa_argv[0]); 3204 3205 nvmeadm_firmware_commit(npa, slot, NVME_FWC_ACTIVATE); 3206 3207 if (verbose) 3208 (void) printf("Slot %u successfully activated.\n", slot); 3209 3210 return (0); 3211 } 3212 3213 nvme_vuc_disc_t * 3214 nvmeadm_vuc_init(const nvme_process_arg_t *npa, const char *name) 3215 { 3216 nvme_vuc_disc_t *vuc; 3217 nvme_vuc_disc_lock_t lock; 3218 3219 if (!nvme_vuc_discover_by_name(npa->npa_ctrl, name, 0, &vuc)) { 3220 nvmeadm_fatal(npa, "%s does not support operation %s: device " 3221 "does not support vendor unique command %s", npa->npa_name, 3222 npa->npa_cmd->c_name, name); 3223 } 3224 3225 lock = nvme_vuc_disc_lock(vuc); 3226 switch (lock) { 3227 case NVME_VUC_DISC_LOCK_NONE: 3228 break; 3229 case NVME_VUC_DISC_LOCK_READ: 3230 nvmeadm_excl(npa, NVME_LOCK_L_READ); 3231 break; 3232 case NVME_VUC_DISC_LOCK_WRITE: 3233 nvmeadm_excl(npa, NVME_LOCK_L_WRITE); 3234 break; 3235 } 3236 3237 return (vuc); 3238 } 3239 3240 void 3241 nvmeadm_vuc_fini(const nvme_process_arg_t *npa, nvme_vuc_disc_t *vuc) 3242 { 3243 if (nvme_vuc_disc_lock(vuc) != NVME_VUC_DISC_LOCK_NONE) { 3244 if (npa->npa_ns != NULL) { 3245 nvme_ns_unlock(npa->npa_ns); 3246 } else if (npa->npa_ctrl != NULL) { 3247 nvme_ctrl_unlock(npa->npa_ctrl); 3248 } 3249 } 3250 3251 nvme_vuc_disc_free(vuc); 3252 } 3253