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_unimpl && !nvme_log_disc_impl(disc)) { 1693 return (B_FALSE); 1694 } 1695 1696 if (nll->nll_nfilts <= 0) { 1697 return (B_TRUE); 1698 } 1699 1700 for (int i = 0; i < nll->nll_nfilts; i++) { 1701 if (strcmp(nvme_log_disc_name(disc), nll->nll_filts[i]) == 0) { 1702 nll->nll_used[i] = B_TRUE; 1703 return (B_TRUE); 1704 } 1705 } 1706 1707 return (B_FALSE); 1708 } 1709 1710 static int 1711 do_list_logs(const nvme_process_arg_t *npa) 1712 { 1713 nvme_log_disc_scope_t scope; 1714 nvme_log_iter_t *iter; 1715 nvme_iter_t ret; 1716 const nvme_log_disc_t *disc; 1717 nvmeadm_list_logs_t *nll = npa->npa_cmd_arg; 1718 1719 if (nll->nll_scope != 0) { 1720 scope = nll->nll_scope; 1721 } else if (npa->npa_ns != NULL) { 1722 scope = NVME_LOG_SCOPE_NS; 1723 } else { 1724 scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM; 1725 } 1726 1727 if (!nvme_log_discover_init(npa->npa_ctrl, scope, 0, &iter)) { 1728 nvmeadm_warn(npa, "failed to iterate logs on %s", 1729 npa->npa_ctrl_name); 1730 return (-1); 1731 } 1732 1733 while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) { 1734 if (do_list_logs_match(disc, nll)) { 1735 nvmeadm_list_logs_ofmt_arg_t print; 1736 1737 print.nlloa_name = npa->npa_name; 1738 print.nlloa_disc = disc; 1739 ofmt_print(npa->npa_ofmt, &print); 1740 nll->nll_nprint++; 1741 } 1742 } 1743 1744 nvme_log_discover_fini(iter); 1745 if (ret == NVME_ITER_ERROR) { 1746 nvmeadm_warn(npa, "failed to iterate logs on %s", 1747 npa->npa_ctrl_name); 1748 return (-1); 1749 } 1750 1751 for (int i = 0; i < nll->nll_nfilts; i++) { 1752 if (!nll->nll_used[i]) { 1753 warnx("log page filter '%s' did match any log pages", 1754 nll->nll_filts[i]); 1755 exitcode = -1; 1756 } 1757 } 1758 1759 if (nll->nll_nprint == 0) { 1760 if (nll->nll_nfilts == 0) { 1761 warnx("no log pages found for %s", npa->npa_name); 1762 } 1763 exitcode = -1; 1764 } 1765 1766 return (exitcode); 1767 } 1768 1769 static void 1770 usage_get_logpage(const char *c_name) 1771 { 1772 (void) fprintf(stderr, "%s [-O file] <ctl>[/<ns>][,...] <logpage>\n\n" 1773 " Print the specified log page of the specified NVMe " 1774 "controllers and/or name-\n spaces. Run nvmeadm list-logpages " 1775 "for supported log pages. All devices\n support error, health, " 1776 "and firmware.\n", c_name); 1777 } 1778 1779 static void 1780 usage_firmware_list(const char *c_name) 1781 { 1782 (void) fprintf(stderr, "%s <ctl>\n\n" 1783 " Print the log page that contains the list of firmware " 1784 "images installed on the specified NVMe controller.\n", c_name); 1785 } 1786 1787 static uint64_t 1788 do_get_logpage_size(const nvme_process_arg_t *npa, nvme_log_disc_t *disc, 1789 nvme_log_req_t *req) 1790 { 1791 uint64_t len, ret; 1792 void *buf; 1793 nvme_log_size_kind_t kind; 1794 1795 kind = nvme_log_disc_size(disc, &len); 1796 if (kind != NVME_LOG_SIZE_K_VAR) { 1797 return (len); 1798 } 1799 1800 /* 1801 * We have a log with a variable length size. To determine the actual 1802 * size we must actually determine the full length of this. 1803 */ 1804 if ((buf = malloc(len)) == NULL) { 1805 errx(-1, "failed to allocate %zu byte buffer to get log " 1806 "page size", len); 1807 } 1808 1809 if (!nvme_log_req_set_output(req, buf, len)) { 1810 nvmeadm_fatal(npa, "failed to set output parameters to " 1811 "determine log length"); 1812 } 1813 1814 if (!nvme_log_req_exec(req)) { 1815 nvmeadm_fatal(npa, "failed to execute log request %s to " 1816 "determine log length", npa->npa_argv[0]); 1817 } 1818 1819 if (!nvme_log_disc_calc_size(disc, &ret, buf, len)) { 1820 errx(-1, "failed to determine full %s log length", 1821 npa->npa_argv[0]); 1822 } 1823 1824 free(buf); 1825 return (ret); 1826 } 1827 1828 static void 1829 do_get_logpage_dump(const void *buf, size_t len, const char *file) 1830 { 1831 size_t off = 0; 1832 int fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0644); 1833 1834 if (fd < 0) { 1835 err(-1, "failed to create output file %s", file); 1836 } 1837 1838 while (len > 0) { 1839 ssize_t ret = write(fd, buf + off, len - off); 1840 if (ret < 0) { 1841 err(EXIT_FAILURE, "failed to write log data to file %s " 1842 "at offset %zu", file, off); 1843 } 1844 1845 off += (size_t)ret; 1846 len -= (size_t)ret; 1847 } 1848 1849 (void) close(fd); 1850 } 1851 1852 static int 1853 do_get_logpage_common(const nvme_process_arg_t *npa, const char *page) 1854 { 1855 int ret = 0; 1856 nvme_log_disc_t *disc; 1857 nvme_log_req_t *req; 1858 nvme_log_disc_scope_t scope; 1859 void *buf; 1860 size_t toalloc; 1861 nvmeadm_get_logpage_t *log = npa->npa_cmd_arg; 1862 1863 /* 1864 * If we have enough information to identify a log-page via libnvme (or 1865 * in the future take enough options to allow us to actually do this 1866 * manually), then we will fetch it. If we don't know how to print it, 1867 * then we'll just hex dump it for now. 1868 */ 1869 if (!nvme_log_req_init_by_name(npa->npa_ctrl, page, 0, &disc, &req)) { 1870 nvmeadm_fatal(npa, "could not initialize log request for %s", 1871 page); 1872 } 1873 1874 if (npa->npa_ns != NULL) { 1875 scope = NVME_LOG_SCOPE_NS; 1876 } else { 1877 scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM; 1878 } 1879 1880 if ((scope & nvme_log_disc_scopes(disc)) == 0) { 1881 errx(-1, "log page %s does not support operating on %s", page, 1882 npa->npa_ns != NULL ? "namespaces" : "controllers"); 1883 } 1884 1885 /* 1886 * In the future we should add options to allow one to specify and set 1887 * the fields for the lsp, lsi, etc. and set them here. 1888 */ 1889 1890 if (npa->npa_ns != NULL) { 1891 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info); 1892 1893 if (!nvme_log_req_set_nsid(req, nsid)) { 1894 nvmeadm_fatal(npa, "failed to set log request " 1895 "namespace ID to 0x%x", nsid); 1896 } 1897 } 1898 1899 /* 1900 * The output size should be the last thing that we determine as we may 1901 * need to issue a log request to figure out how much data we should 1902 * actually be reading. 1903 */ 1904 toalloc = do_get_logpage_size(npa, disc, req); 1905 buf = malloc(toalloc); 1906 if (buf == NULL) { 1907 err(-1, "failed to allocate %zu bytes for log " 1908 "request %s", toalloc, page); 1909 } 1910 1911 if (!nvme_log_req_set_output(req, buf, toalloc)) { 1912 nvmeadm_fatal(npa, "failed to set output parameters"); 1913 } 1914 1915 if (!nvme_log_req_exec(req)) { 1916 nvmeadm_fatal(npa, "failed to execute log request %s", 1917 npa->npa_argv[0]); 1918 } 1919 1920 if (log != NULL && log->ngl_output != NULL) { 1921 do_get_logpage_dump(buf, toalloc, log->ngl_output); 1922 goto done; 1923 } 1924 1925 (void) printf("%s: ", npa->npa_name); 1926 if (strcmp(page, "error") == 0) { 1927 size_t nlog = toalloc / sizeof (nvme_error_log_entry_t); 1928 nvme_print_error_log(nlog, buf, npa->npa_version); 1929 } else if (strcmp(page, "health") == 0) { 1930 nvme_print_health_log(buf, npa->npa_idctl, npa->npa_version); 1931 } else if (strcmp(page, "firmware") == 0) { 1932 nvme_print_fwslot_log(buf, npa->npa_idctl); 1933 } else { 1934 (void) printf("%s (%s)\n", nvme_log_disc_desc(disc), page); 1935 nvmeadm_dump_hex(buf, toalloc); 1936 } 1937 1938 done: 1939 free(buf); 1940 nvme_log_disc_free(disc); 1941 nvme_log_req_fini(req); 1942 1943 return (ret); 1944 } 1945 1946 static int 1947 do_get_logpage_fwslot(const nvme_process_arg_t *npa) 1948 { 1949 if (npa->npa_argc >= 1) { 1950 warnx("no additional arguments may be specified to %s", 1951 npa->npa_cmd->c_name); 1952 usage(npa->npa_cmd); 1953 exit(-1); 1954 } 1955 1956 return (do_get_logpage_common(npa, "firmware")); 1957 } 1958 1959 static void 1960 optparse_get_logpage(nvme_process_arg_t *npa) 1961 { 1962 int c; 1963 const char *output = NULL; 1964 nvmeadm_get_logpage_t *log; 1965 1966 if ((log = calloc(1, sizeof (nvmeadm_get_logpage_t))) == NULL) { 1967 err(-1, "failed to allocate memory to track log page " 1968 "information"); 1969 } 1970 1971 npa->npa_cmd_arg = log; 1972 1973 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":O:")) != -1) { 1974 switch (c) { 1975 case 'O': 1976 output = optarg; 1977 break; 1978 case '?': 1979 errx(-1, "unknown option: -%c", optopt); 1980 case ':': 1981 errx(-1, "option -%c requires an argument", optopt); 1982 } 1983 } 1984 1985 log->ngl_output = output; 1986 } 1987 1988 static int 1989 do_get_logpage(const nvme_process_arg_t *npa) 1990 { 1991 1992 if (npa->npa_argc < 1) { 1993 warnx("missing log page name"); 1994 usage(npa->npa_cmd); 1995 exit(-1); 1996 } 1997 1998 if (npa->npa_argc > 1) { 1999 warnx("only a single log page may be specified at a time"); 2000 usage(npa->npa_cmd); 2001 exit(-1); 2002 } 2003 2004 return (do_get_logpage_common(npa, npa->npa_argv[0])); 2005 } 2006 2007 static void 2008 optparse_list_features(nvme_process_arg_t *npa) 2009 { 2010 int c; 2011 uint_t oflags = 0; 2012 boolean_t parse = B_FALSE; 2013 const char *fields = NULL; 2014 nvmeadm_features_t *feat; 2015 ofmt_status_t oferr; 2016 2017 if ((feat = calloc(1, sizeof (nvmeadm_features_t))) == NULL) { 2018 err(-1, "failed to allocate memory to track feature " 2019 "information"); 2020 } 2021 2022 npa->npa_cmd_arg = feat; 2023 2024 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:p")) != -1) { 2025 switch (c) { 2026 case 'a': 2027 feat->nf_unimpl = B_TRUE; 2028 break; 2029 case 'H': 2030 oflags |= OFMT_NOHEADER; 2031 break; 2032 case 'o': 2033 fields = optarg; 2034 break; 2035 case 'p': 2036 parse = B_TRUE; 2037 oflags |= OFMT_PARSABLE; 2038 break; 2039 case '?': 2040 errx(-1, "unknown option: -%c", optopt); 2041 case ':': 2042 errx(-1, "option -%c requires an argument", optopt); 2043 } 2044 } 2045 2046 if (!parse) { 2047 oflags |= OFMT_WRAP; 2048 } 2049 2050 if (parse && fields == NULL) { 2051 errx(-1, "parsable mode (-p) requires fields specified with " 2052 "-o"); 2053 } 2054 2055 if (fields == NULL) { 2056 fields = nvmeadm_list_features_fields; 2057 } 2058 2059 oferr = ofmt_open(fields, nvmeadm_list_features_ofmt, oflags, 0, 2060 &npa->npa_ofmt); 2061 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx); 2062 2063 if (npa->npa_argc - optind > 1) { 2064 feat->nf_nfilts = (uint32_t)(npa->npa_argc - optind - 1); 2065 feat->nf_filts = npa->npa_argv + optind + 1; 2066 feat->nf_used = calloc(feat->nf_nfilts, sizeof (boolean_t)); 2067 if (feat->nf_used == NULL) { 2068 err(-1, "failed to allocate memory for tracking " 2069 "feature filters"); 2070 } 2071 } 2072 } 2073 2074 static void 2075 usage_list_features(const char *c_name) 2076 { 2077 (void) fprintf(stderr, "%s [-a] [-H] [-o field,[...] [-p]] " 2078 "<ctl>[/<ns>][,...]\n\t [feature...]\n\n" 2079 " List features supported by controllers or namespaces.\n", 2080 c_name); 2081 } 2082 2083 static boolean_t 2084 do_features_match(const nvme_feat_disc_t *disc, nvmeadm_features_t *nf) 2085 { 2086 if (nf->nf_nfilts == 0) { 2087 return (B_TRUE); 2088 } 2089 2090 for (uint32_t i = 0; i < nf->nf_nfilts; i++) { 2091 const char *match = nf->nf_filts[i]; 2092 long long fid; 2093 const char *err; 2094 2095 if (strcmp(nvme_feat_disc_short(disc), match) == 0 || 2096 strcasecmp(nvme_feat_disc_spec(disc), match) == 0) { 2097 nf->nf_used[i] = B_TRUE; 2098 return (B_TRUE); 2099 } 2100 2101 fid = strtonumx(match, 0, UINT32_MAX, &err, 0); 2102 if (err == NULL && fid == nvme_feat_disc_fid(disc)) { 2103 nf->nf_used[i] = B_TRUE; 2104 return (B_TRUE); 2105 } 2106 } 2107 2108 return (B_FALSE); 2109 } 2110 2111 2112 /* 2113 * This is a common entry point for both list-features and get-features, which 2114 * iterate over all features and take action for each one. 2115 */ 2116 typedef void (*do_features_cb_f)(const nvme_process_arg_t *, 2117 const nvme_feat_disc_t *); 2118 static int 2119 do_features(const nvme_process_arg_t *npa, nvmeadm_features_t *nf, 2120 do_features_cb_f func) 2121 { 2122 nvme_feat_scope_t scope; 2123 nvme_feat_iter_t *iter; 2124 nvme_iter_t ret; 2125 const nvme_feat_disc_t *disc; 2126 2127 if (npa->npa_ns != NULL) { 2128 scope = NVME_FEAT_SCOPE_NS; 2129 } else { 2130 scope = NVME_FEAT_SCOPE_CTRL; 2131 } 2132 2133 if (!nvme_feat_discover_init(npa->npa_ctrl, scope, 0, &iter)) { 2134 nvmeadm_warn(npa, "failed to iterate features on %s", 2135 npa->npa_ctrl_name); 2136 return (-1); 2137 } 2138 2139 while ((ret = nvme_feat_discover_step(iter, &disc)) == 2140 NVME_ITER_VALID) { 2141 if (do_features_match(disc, nf)) { 2142 if (!nf->nf_unimpl && nvme_feat_disc_impl(disc) == 2143 NVME_FEAT_IMPL_UNSUPPORTED) { 2144 continue; 2145 } 2146 2147 func(npa, disc); 2148 nf->nf_nprint++; 2149 } 2150 } 2151 2152 nvme_feat_discover_fini(iter); 2153 if (ret == NVME_ITER_ERROR) { 2154 nvmeadm_warn(npa, "failed to iterate features on %s", 2155 npa->npa_ctrl_name); 2156 return (-1); 2157 } 2158 2159 for (uint32_t i = 0; i < nf->nf_nfilts; i++) { 2160 if (!nf->nf_used[i]) { 2161 warnx("feature filter '%s' did match any features", 2162 nf->nf_filts[i]); 2163 exitcode = -1; 2164 } 2165 } 2166 2167 if (nf->nf_nprint == 0) { 2168 if (nf->nf_nfilts == 0) { 2169 warnx("no features found for %s", npa->npa_name); 2170 } 2171 exitcode = -1; 2172 } 2173 2174 return (exitcode); 2175 } 2176 2177 static void 2178 do_list_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc) 2179 { 2180 nvmeadm_list_features_ofmt_arg_t print; 2181 2182 print.nlfoa_name = npa->npa_name; 2183 print.nlfoa_feat = disc; 2184 ofmt_print(npa->npa_ofmt, &print); 2185 } 2186 2187 static int 2188 do_list_features(const nvme_process_arg_t *npa) 2189 { 2190 nvmeadm_features_t *nf = npa->npa_cmd_arg; 2191 2192 return (do_features(npa, nf, do_list_features_cb)); 2193 } 2194 2195 static void 2196 usage_get_features(const char *c_name) 2197 { 2198 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n" 2199 " Print the specified features of the specified NVMe controllers " 2200 "and/or\n namespaces. Feature support varies on the controller.\n" 2201 "Run 'nvmeadm list-features <ctl>' to see supported features.\n", 2202 c_name); 2203 } 2204 2205 /* 2206 * The nvmeadm(8) get-features output has traditionally swallowed certain errors 2207 * for features that it considers unimplemented in tandem with the kernel. With 2208 * the introduction of libnvme and ioctl interface changes, the kernel no longer 2209 * caches information about features that are unimplemented. 2210 * 2211 * There are two cases that we currently swallow errors on and the following 2212 * must all be true: 2213 * 2214 * 1) We have a controller error. 2215 * 2) The system doesn't know whether the feature is implemented or not. 2216 * 3) The controller error indicates that we have an invalid field. 2217 * 2218 * There is one additional wrinkle that we are currently papering over due to 2219 * the history of nvmeadm swallowing errors. The error recovery feature was made 2220 * explicitly namespace-specific in NVMe 1.4. However, various NVMe 1.3 devices 2221 * will error if we ask for it without specifying a namespace. Conversely, older 2222 * devices will be upset if you do ask for a namespace. This case can be removed 2223 * once we better survey devices and come up with a heuristic for how to handle 2224 * this across older generations. 2225 * 2226 * If we add a single feature endpoint that gives flexibility over how the 2227 * feature are listed, then we should not swallow errors. 2228 */ 2229 static boolean_t 2230 swallow_get_feat_err(const nvme_process_arg_t *npa, 2231 const nvme_feat_disc_t *disc) 2232 { 2233 uint32_t sct, sc; 2234 2235 if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_CONTROLLER) { 2236 return (B_FALSE); 2237 } 2238 2239 nvme_ctrl_deverr(npa->npa_ctrl, &sct, &sc); 2240 if (nvme_feat_disc_impl(disc) == NVME_FEAT_IMPL_UNKNOWN && 2241 sct == NVME_CQE_SCT_GENERIC && sc == NVME_CQE_SC_GEN_INV_FLD) { 2242 return (B_TRUE); 2243 } 2244 2245 if (nvme_feat_disc_fid(disc) == NVME_FEAT_ERROR && 2246 sct == NVME_CQE_SCT_GENERIC && (sc == NVME_CQE_SC_GEN_INV_FLD || 2247 sc == NVME_CQE_SC_GEN_INV_NS)) { 2248 return (B_TRUE); 2249 } 2250 2251 return (B_FALSE); 2252 } 2253 2254 static boolean_t 2255 do_get_feat_common(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc, 2256 uint32_t cdw11, uint32_t *cdw0, void **datap, size_t *lenp) 2257 { 2258 nvme_get_feat_req_t *req = NULL; 2259 void *data = NULL; 2260 uint64_t datalen = 0; 2261 nvme_get_feat_fields_t fields = nvme_feat_disc_fields_get(disc); 2262 2263 if (!nvme_get_feat_req_init_by_disc(npa->npa_ctrl, disc, &req)) { 2264 nvmeadm_warn(npa, "failed to initialize get feature request " 2265 "for feature %s", nvme_feat_disc_short(disc)); 2266 exitcode = -1; 2267 goto err; 2268 } 2269 2270 if ((fields & NVME_GET_FEAT_F_CDW11) != 0 && 2271 !nvme_get_feat_req_set_cdw11(req, cdw11)) { 2272 nvmeadm_warn(npa, "failed to set cdw11 to 0x%x for feature %s", 2273 cdw11, nvme_feat_disc_short(disc)); 2274 exitcode = -1; 2275 goto err; 2276 } 2277 2278 if ((fields & NVME_GET_FEAT_F_DATA) != 0) { 2279 datalen = nvme_feat_disc_data_size(disc); 2280 VERIFY3U(datalen, !=, 0); 2281 data = malloc(datalen); 2282 if (data == NULL) { 2283 err(-1, "failed to allocate %zu bytes for feature %s " 2284 "data buffer", datalen, nvme_feat_disc_short(disc)); 2285 } 2286 2287 if (!nvme_get_feat_req_set_output(req, data, datalen)) { 2288 nvmeadm_warn(npa, "failed to set output data for " 2289 "feature %s", nvme_feat_disc_short(disc)); 2290 exitcode = -1; 2291 goto err; 2292 } 2293 } 2294 2295 if ((fields & NVME_GET_FEAT_F_NSID) != 0) { 2296 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info); 2297 2298 if (!nvme_get_feat_req_set_nsid(req, nsid)) { 2299 nvmeadm_warn(npa, "failed to set nsid to 0x%x for " 2300 "feature %s", nsid, nvme_feat_disc_spec(disc)); 2301 exitcode = -1; 2302 goto err; 2303 } 2304 } 2305 2306 if (!nvme_get_feat_req_exec(req)) { 2307 if (!swallow_get_feat_err(npa, disc)) { 2308 nvmeadm_warn(npa, "failed to get feature %s", 2309 nvme_feat_disc_spec(disc)); 2310 exitcode = -1; 2311 } 2312 2313 goto err; 2314 } 2315 2316 if (!nvme_get_feat_req_get_cdw0(req, cdw0)) { 2317 nvmeadm_warn(npa, "failed to get cdw0 result data for %s", 2318 nvme_feat_disc_spec(disc)); 2319 goto err; 2320 } 2321 2322 *datap = data; 2323 *lenp = datalen; 2324 nvme_get_feat_req_fini(req); 2325 return (B_TRUE); 2326 2327 err: 2328 free(data); 2329 nvme_get_feat_req_fini(req); 2330 return (B_FALSE); 2331 } 2332 2333 static void 2334 do_get_feat_temp_thresh_one(const nvme_process_arg_t *npa, 2335 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat, 2336 const char *label, uint16_t tmpsel, uint16_t thsel) 2337 { 2338 uint32_t cdw0; 2339 void *buf = NULL; 2340 size_t buflen; 2341 nvme_temp_threshold_t tt; 2342 2343 tt.r = 0; 2344 tt.b.tt_tmpsel = tmpsel; 2345 tt.b.tt_thsel = thsel; 2346 2347 /* 2348 * The printing function treats the buffer argument as the label to 2349 * print for this threshold. 2350 */ 2351 if (!do_get_feat_common(npa, disc, tt.r, &cdw0, &buf, &buflen)) { 2352 return; 2353 } 2354 2355 feat->f_print(cdw0, (void *)label, 0, npa->npa_idctl, 2356 npa->npa_version); 2357 free(buf); 2358 } 2359 2360 /* 2361 * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the 2362 * device and changed the main device to have a composite temperature sensor. As 2363 * a result, there is a set of thresholds for each sensor. In addition, they 2364 * added both an over-temperature and under-temperature threshold. Since most 2365 * devices don't actually implement all the sensors, we get the health page and 2366 * see which sensors have a non-zero value to determine how to proceed. 2367 */ 2368 static boolean_t 2369 do_get_feat_temp_thresh(const nvme_process_arg_t *npa, 2370 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat) 2371 { 2372 nvme_log_req_t *req = NULL; 2373 nvme_log_disc_t *log_disc = NULL; 2374 size_t toalloc; 2375 void *buf = NULL; 2376 boolean_t ret = B_FALSE; 2377 const nvme_health_log_t *hlog; 2378 2379 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL); 2380 do_get_feat_temp_thresh_one(npa, disc, feat, 2381 "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER); 2382 2383 if (!nvme_version_check(npa, &nvme_vers_1v2)) { 2384 return (B_TRUE); 2385 } 2386 2387 if (!nvme_log_req_init_by_name(npa->npa_ctrl, "health", 0, &log_disc, 2388 &req)) { 2389 nvmeadm_warn(npa, "failed to initialize health log page " 2390 "request"); 2391 return (B_FALSE); 2392 } 2393 2394 toalloc = do_get_logpage_size(npa, log_disc, req); 2395 buf = malloc(toalloc); 2396 if (buf == NULL) { 2397 err(-1, "failed to allocate %zu bytes for health log page", 2398 toalloc); 2399 } 2400 2401 if (!nvme_log_req_set_output(req, buf, toalloc)) { 2402 nvmeadm_warn(npa, "failed to set output parameters for health " 2403 "log page"); 2404 goto out; 2405 } 2406 2407 if (!nvme_log_req_exec(req)) { 2408 nvmeadm_warn(npa, "failed to retrieve the health log page"); 2409 goto out; 2410 } 2411 2412 /* cast required to prove our intentionality to smatch */ 2413 hlog = (const nvme_health_log_t *)buf; 2414 2415 do_get_feat_temp_thresh_one(npa, disc, feat, 2416 "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER); 2417 if (hlog->hl_temp_sensor_1 != 0) { 2418 do_get_feat_temp_thresh_one(npa, disc, feat, 2419 "Temp. Sensor 1 Over Temp. Threshold", 1, 2420 NVME_TEMP_THRESH_OVER); 2421 do_get_feat_temp_thresh_one(npa, disc, feat, 2422 "Temp. Sensor 1 Under Temp. Threshold", 1, 2423 NVME_TEMP_THRESH_UNDER); 2424 } 2425 2426 if (hlog->hl_temp_sensor_2 != 0) { 2427 do_get_feat_temp_thresh_one(npa, disc, feat, 2428 "Temp. Sensor 2 Over Temp. Threshold", 2, 2429 NVME_TEMP_THRESH_OVER); 2430 do_get_feat_temp_thresh_one(npa, disc, feat, 2431 "Temp. Sensor 2 Under Temp. Threshold", 2, 2432 NVME_TEMP_THRESH_UNDER); 2433 } 2434 2435 if (hlog->hl_temp_sensor_3 != 0) { 2436 do_get_feat_temp_thresh_one(npa, disc, feat, 2437 "Temp. Sensor 3 Over Temp. Threshold", 3, 2438 NVME_TEMP_THRESH_OVER); 2439 do_get_feat_temp_thresh_one(npa, disc, feat, 2440 "Temp. Sensor 3 Under Temp. Threshold", 3, 2441 NVME_TEMP_THRESH_UNDER); 2442 } 2443 2444 if (hlog->hl_temp_sensor_4 != 0) { 2445 do_get_feat_temp_thresh_one(npa, disc, feat, 2446 "Temp. Sensor 4 Over Temp. Threshold", 4, 2447 NVME_TEMP_THRESH_OVER); 2448 do_get_feat_temp_thresh_one(npa, disc, feat, 2449 "Temp. Sensor 4 Under Temp. Threshold", 4, 2450 NVME_TEMP_THRESH_UNDER); 2451 } 2452 2453 if (hlog->hl_temp_sensor_5 != 0) { 2454 do_get_feat_temp_thresh_one(npa, disc, feat, 2455 "Temp. Sensor 5 Over Temp. Threshold", 5, 2456 NVME_TEMP_THRESH_OVER); 2457 do_get_feat_temp_thresh_one(npa, disc, feat, 2458 "Temp. Sensor 5 Under Temp. Threshold", 5, 2459 NVME_TEMP_THRESH_UNDER); 2460 } 2461 2462 if (hlog->hl_temp_sensor_6 != 0) { 2463 do_get_feat_temp_thresh_one(npa, disc, feat, 2464 "Temp. Sensor 6 Over Temp. Threshold", 6, 2465 NVME_TEMP_THRESH_OVER); 2466 do_get_feat_temp_thresh_one(npa, disc, feat, 2467 "Temp. Sensor 6 Under Temp. Threshold", 6, 2468 NVME_TEMP_THRESH_UNDER); 2469 } 2470 2471 if (hlog->hl_temp_sensor_7 != 0) { 2472 do_get_feat_temp_thresh_one(npa, disc, feat, 2473 "Temp. Sensor 7 Over Temp. Threshold", 7, 2474 NVME_TEMP_THRESH_OVER); 2475 do_get_feat_temp_thresh_one(npa, disc, feat, 2476 "Temp. Sensor 7 Under Temp. Threshold", 7, 2477 NVME_TEMP_THRESH_UNDER); 2478 } 2479 2480 if (hlog->hl_temp_sensor_8 != 0) { 2481 do_get_feat_temp_thresh_one(npa, disc, feat, 2482 "Temp. Sensor 8 Over Temp. Threshold", 8, 2483 NVME_TEMP_THRESH_OVER); 2484 do_get_feat_temp_thresh_one(npa, disc, feat, 2485 "Temp. Sensor 8 Under Temp. Threshold", 8, 2486 NVME_TEMP_THRESH_UNDER); 2487 } 2488 2489 ret = B_TRUE; 2490 out: 2491 nvme_log_req_fini(req); 2492 free(buf); 2493 return (ret); 2494 } 2495 2496 static boolean_t 2497 do_get_feat_intr_vect(const nvme_process_arg_t *npa, 2498 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat) 2499 { 2500 uint32_t nintrs; 2501 boolean_t ret = B_TRUE; 2502 2503 if (!nvme_ctrl_info_pci_nintrs(npa->npa_ctrl_info, &nintrs)) { 2504 nvmeadm_ctrl_info_warn(npa, "failed to get interrupt count " 2505 "from controller %s information snapshot", npa->npa_name); 2506 return (B_FALSE); 2507 } 2508 2509 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL); 2510 for (uint32_t i = 0; i < nintrs; i++) { 2511 uint32_t cdw0; 2512 void *buf; 2513 size_t buflen; 2514 nvme_intr_vect_t vect; 2515 2516 vect.r = 0; 2517 vect.b.iv_iv = i; 2518 2519 if (!do_get_feat_common(npa, disc, vect.r, &cdw0, &buf, 2520 &buflen)) { 2521 ret = B_FALSE; 2522 continue; 2523 } 2524 2525 feat->f_print(cdw0, buf, buflen, npa->npa_idctl, 2526 npa->npa_version); 2527 free(buf); 2528 } 2529 2530 return (ret); 2531 } 2532 2533 /* 2534 * We've been asked to print the following feature that the controller probably 2535 * supports. Find our internal feature information for this to see if we know 2536 * how to deal with it. 2537 */ 2538 static void 2539 do_get_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc) 2540 { 2541 const nvmeadm_feature_t *feat = NULL; 2542 uint32_t fid = nvme_feat_disc_fid(disc); 2543 nvme_get_feat_fields_t fields; 2544 void *data = NULL; 2545 size_t datalen = 0; 2546 uint32_t cdw0; 2547 2548 for (size_t i = 0; i < ARRAY_SIZE(features); i++) { 2549 if (features[i].f_feature == fid) { 2550 feat = &features[i]; 2551 break; 2552 } 2553 } 2554 2555 /* 2556 * Determine if we have enough logic in here to get and print the 2557 * feature. The vast majority of NVMe features only output a single 2558 * uint32_t in cdw0 and potentially a data buffer. As long as no input 2559 * arguments are required, then we can go ahead and get this and print 2560 * the data. If there is, then we will refuse unless we have a 2561 * particular function. If we have a specific get function, we expect it 2562 * to do all the printing. 2563 */ 2564 if (feat != NULL && feat->f_get != NULL) { 2565 if (!feat->f_get(npa, disc, feat)) { 2566 exitcode = -1; 2567 } 2568 return; 2569 } 2570 2571 fields = nvme_feat_disc_fields_get(disc); 2572 if ((fields & NVME_GET_FEAT_F_CDW11) != 0) { 2573 warnx("unable to get feature %s due to missing nvmeadm(8) " 2574 "implementation logic", nvme_feat_disc_spec(disc)); 2575 exitcode = -1; 2576 return; 2577 } 2578 2579 /* 2580 * We do not set exitcode on failure here so that way we can swallow 2581 * errors from unimplemented features. 2582 */ 2583 if (!do_get_feat_common(npa, disc, 0, &cdw0, &data, &datalen)) { 2584 return; 2585 } 2586 2587 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL); 2588 if (feat != NULL && feat->f_print != NULL) { 2589 feat->f_print(cdw0, data, datalen, npa->npa_idctl, 2590 npa->npa_version); 2591 } else { 2592 nvme_feat_output_t output = nvme_feat_disc_output_get(disc); 2593 nvme_print_feat_unknown(output, cdw0, data, datalen); 2594 } 2595 2596 free(data); 2597 } 2598 2599 /* 2600 * This is an entry point which prints every feature that we know about. We 2601 * often go to lengths to discover all the variable inputs that can be used for 2602 * a given feature that requires an argument in cdw11. Due to the semantics of 2603 * filtering being used for features and the need to print each feature, this is 2604 * not the place to add general field filtering or a means to request a specific 2605 * cdw11 argument or similar. Instead, a new get-feature which requires someone 2606 * to specify the short name for a feature and then allows particular fields to 2607 * be grabbed and arguments should be created instead. 2608 * 2609 * This uses the same general feature logic that underpins do_list_features() 2610 * and therefore we transform filter arguments into the same style used there. 2611 */ 2612 static int 2613 do_get_features(const nvme_process_arg_t *npa) 2614 { 2615 char *fstr = NULL; 2616 char **filts = NULL; 2617 boolean_t *used = NULL; 2618 nvmeadm_features_t nf; 2619 int ret; 2620 2621 if (npa->npa_argc > 1) 2622 errx(-1, "unexpected arguments"); 2623 2624 if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) < 2625 NVME_NS_DISC_F_ACTIVE) { 2626 errx(-1, "cannot get feature: namespace is inactive"); 2627 } 2628 2629 /* 2630 * We always leave nf_unimpl set to false as we don't want to bother 2631 * trying to print a feature that we know the device doesn't support. 2632 */ 2633 (void) memset(&nf, 0, sizeof (nvmeadm_features_t)); 2634 2635 /* 2636 * If we've been given a series of features to print, treat those as 2637 * filters on the features as we're walking them to determine which to 2638 * print or not. 2639 */ 2640 if (npa->npa_argc == 1) { 2641 char *f; 2642 uint32_t i; 2643 2644 nf.nf_nfilts = 1; 2645 fstr = strdup(npa->npa_argv[0]); 2646 2647 if (fstr == NULL) { 2648 err(-1, "failed to allocate memory to duplicate " 2649 "feature string"); 2650 } 2651 2652 for (const char *c = strchr(fstr, ','); c != NULL; 2653 c = strchr(c + 1, ',')) { 2654 nf.nf_nfilts++; 2655 } 2656 2657 filts = calloc(nf.nf_nfilts, sizeof (char *)); 2658 if (filts == NULL) { 2659 err(-1, "failed to allocate memory for filter list"); 2660 } 2661 2662 i = 0; 2663 while ((f = strsep(&fstr, ",")) != NULL) { 2664 filts[i] = f; 2665 i++; 2666 } 2667 VERIFY3U(i, ==, nf.nf_nfilts); 2668 nf.nf_filts = filts; 2669 2670 used = calloc(nf.nf_nfilts, sizeof (boolean_t)); 2671 if (used == NULL) { 2672 err(-1, "failed to allocate memory for filter use " 2673 "tracking"); 2674 } 2675 nf.nf_used = used; 2676 } 2677 2678 (void) printf("%s: Get Features\n", npa->npa_name); 2679 ret = do_features(npa, &nf, do_get_features_cb); 2680 2681 free(fstr); 2682 free(filts); 2683 free(used); 2684 return (ret); 2685 } 2686 2687 static int 2688 do_format_common(const nvme_process_arg_t *npa, uint32_t lbaf, 2689 uint32_t ses) 2690 { 2691 int ret = 0; 2692 nvme_format_req_t *req; 2693 2694 if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) < 2695 NVME_NS_DISC_F_ACTIVE) { 2696 errx(-1, "cannot %s: namespace is inactive", 2697 npa->npa_cmd->c_name); 2698 } 2699 2700 if (!nvme_format_req_init(npa->npa_ctrl, &req)) { 2701 nvmeadm_fatal(npa, "failed to initialize format request for " 2702 "%s", npa->npa_name); 2703 } 2704 2705 if (npa->npa_ns != NULL) { 2706 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info); 2707 2708 if (!nvme_format_req_set_nsid(req, nsid)) { 2709 nvmeadm_fatal(npa, "failed to set format request " 2710 "namespace ID to 0x%x", nsid); 2711 } 2712 } 2713 2714 if (!nvme_format_req_set_lbaf(req, lbaf) || 2715 !nvme_format_req_set_ses(req, ses)) { 2716 nvmeadm_fatal(npa, "failed to set format request fields for %s", 2717 npa->npa_name); 2718 } 2719 2720 if (do_detach(npa) != 0) { 2721 errx(-1, "cannot %s %s due to namespace detach failure", 2722 npa->npa_cmd->c_name, npa->npa_name); 2723 } 2724 2725 if (!nvme_format_req_exec(req)) { 2726 nvmeadm_warn(npa, "failed to %s %s", npa->npa_cmd->c_name, 2727 npa->npa_name); 2728 ret = -1; 2729 } 2730 2731 if (do_attach(npa) != 0) 2732 ret = -1; 2733 2734 return (ret); 2735 } 2736 2737 static void 2738 usage_format(const char *c_name) 2739 { 2740 (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n" 2741 " Format one or all namespaces of the specified NVMe " 2742 "controller. Supported LBA\n formats can be queried with " 2743 "the \"%s identify\" command on the namespace\n to be " 2744 "formatted.\n", c_name, getprogname()); 2745 } 2746 2747 static uint32_t 2748 do_format_determine_lbaf(const nvme_process_arg_t *npa) 2749 { 2750 const nvme_nvm_lba_fmt_t *fmt; 2751 nvme_ns_info_t *ns_info = NULL; 2752 uint32_t lbaf; 2753 2754 if (npa->npa_argc > 0) { 2755 unsigned long lba; 2756 uint32_t nlbaf = nvme_ctrl_info_nformats(npa->npa_ctrl_info); 2757 2758 errno = 0; 2759 lba = strtoul(npa->npa_argv[0], NULL, 10); 2760 if (errno != 0 || lba >= nlbaf) 2761 errx(-1, "invalid LBA format %s", npa->npa_argv[0]); 2762 2763 if (!nvme_ctrl_info_format(npa->npa_ctrl_info, (uint32_t)lba, 2764 &fmt)) { 2765 nvmeadm_fatal(npa, "failed to get LBA format %lu " 2766 "information", lba); 2767 } 2768 } else { 2769 /* 2770 * If we have a namespace then we use the current namespace's 2771 * LBA format. If we don't have a namespace, then we promised 2772 * we'd look at namespace 1 in the manual page. 2773 */ 2774 if (npa->npa_ns_info == NULL) { 2775 if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, 1, 2776 &ns_info)) { 2777 nvmeadm_fatal(npa, "failed to get namespace 1 " 2778 "information, please explicitly specify an " 2779 "LBA format"); 2780 } 2781 2782 if (!nvme_ns_info_curformat(ns_info, &fmt)) { 2783 nvmeadm_fatal(npa, "failed to retrieve current " 2784 "namespace format from namespace 1"); 2785 } 2786 } else { 2787 if (!nvme_ns_info_curformat(npa->npa_ns_info, &fmt)) { 2788 nvmeadm_fatal(npa, "failed to get the current " 2789 "format information from %s", 2790 npa->npa_name); 2791 } 2792 } 2793 } 2794 2795 if (nvme_nvm_lba_fmt_meta_size(fmt) != 0) { 2796 errx(-1, "LBA formats with metadata are not supported"); 2797 } 2798 2799 lbaf = nvme_nvm_lba_fmt_id(fmt); 2800 nvme_ns_info_free(ns_info); 2801 return (lbaf); 2802 } 2803 2804 static int 2805 do_format(const nvme_process_arg_t *npa) 2806 { 2807 uint32_t lbaf; 2808 2809 if (npa->npa_argc > 1) { 2810 errx(-1, "%s passed extraneous arguments starting with %s", 2811 npa->npa_cmd->c_name, npa->npa_argv[1]); 2812 } 2813 2814 lbaf = do_format_determine_lbaf(npa); 2815 return (do_format_common(npa, lbaf, 0)); 2816 } 2817 2818 static void 2819 usage_secure_erase(const char *c_name) 2820 { 2821 (void) fprintf(stderr, "%s [-c] <ctl>[/<ns>]\n\n" 2822 " Secure-Erase one or all namespaces of the specified " 2823 "NVMe controller.\n", c_name); 2824 } 2825 2826 static void 2827 optparse_secure_erase(nvme_process_arg_t *npa) 2828 { 2829 int c; 2830 2831 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":c")) != -1) { 2832 switch (c) { 2833 case 'c': 2834 npa->npa_cmdflags |= NVMEADM_O_SE_CRYPTO; 2835 break; 2836 2837 case '?': 2838 errx(-1, "unknown option: -%c", optopt); 2839 2840 case ':': 2841 errx(-1, "option -%c requires an argument", optopt); 2842 2843 } 2844 } 2845 } 2846 2847 static int 2848 do_secure_erase(const nvme_process_arg_t *npa) 2849 { 2850 unsigned long lbaf; 2851 uint8_t ses = NVME_FRMT_SES_USER; 2852 2853 if (npa->npa_argc > 0) { 2854 errx(-1, "%s passed extraneous arguments starting with %s", 2855 npa->npa_cmd->c_name, npa->npa_argv[0]); 2856 } 2857 2858 if ((npa->npa_cmdflags & NVMEADM_O_SE_CRYPTO) != 0) 2859 ses = NVME_FRMT_SES_CRYPTO; 2860 2861 lbaf = do_format_determine_lbaf(npa); 2862 return (do_format_common(npa, lbaf, ses)); 2863 } 2864 2865 static void 2866 usage_attach_detach(const char *c_name) 2867 { 2868 (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n" 2869 " %c%s blkdev(4D) %s one or all namespaces of the " 2870 "specified NVMe controller.\n", 2871 c_name, toupper(c_name[0]), &c_name[1], 2872 c_name[0] == 'd' ? "from" : "to"); 2873 } 2874 2875 static int 2876 do_attach(const nvme_process_arg_t *npa) 2877 { 2878 int rv; 2879 nvme_ns_iter_t *iter = NULL; 2880 nvme_iter_t ret; 2881 const nvme_ns_disc_t *disc; 2882 2883 if (npa->npa_ns != NULL) { 2884 if (!nvme_ns_bd_attach(npa->npa_ns)) { 2885 nvmeadm_warn(npa, "faild to attach %s", npa->npa_name); 2886 return (-1); 2887 } 2888 return (0); 2889 } 2890 2891 if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_NOT_IGNORED, 2892 &iter)) { 2893 nvmeadm_fatal(npa, "failed to initialize namespace discovery " 2894 "on %s", npa->npa_name); 2895 } 2896 2897 rv = 0; 2898 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) { 2899 nvme_ns_t *ns; 2900 uint32_t nsid; 2901 2902 if (nvme_ns_disc_level(disc) == NVME_NS_DISC_F_BLKDEV) 2903 continue; 2904 2905 nsid = nvme_ns_disc_nsid(disc); 2906 if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) { 2907 nvmeadm_warn(npa, "failed to open namespace %s/%u " 2908 "handle", npa->npa_name, nsid); 2909 rv = -1; 2910 continue; 2911 } 2912 2913 if (!nvme_ns_bd_attach(ns)) { 2914 nvmeadm_warn(npa, "failed to attach namespace " 2915 "%s/%u", npa->npa_name, nsid); 2916 rv = -1; 2917 } 2918 nvme_ns_fini(ns); 2919 } 2920 2921 nvme_ns_discover_fini(iter); 2922 if (ret == NVME_ITER_ERROR) { 2923 nvmeadm_warn(npa, "failed to iterate namespaces on %s", 2924 npa->npa_name); 2925 rv = -1; 2926 } 2927 2928 return (rv); 2929 } 2930 2931 static int 2932 do_detach(const nvme_process_arg_t *npa) 2933 { 2934 int rv; 2935 nvme_ns_iter_t *iter = NULL; 2936 nvme_iter_t ret; 2937 const nvme_ns_disc_t *disc; 2938 2939 if (npa->npa_ns != NULL) { 2940 if (!nvme_ns_bd_detach(npa->npa_ns)) { 2941 nvmeadm_warn(npa, "failed to detach %s", npa->npa_name); 2942 return (-1); 2943 } 2944 return (0); 2945 } 2946 2947 if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_BLKDEV, 2948 &iter)) { 2949 nvmeadm_fatal(npa, "failed to initialize namespace discovery " 2950 "on %s", npa->npa_name); 2951 } 2952 2953 rv = 0; 2954 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) { 2955 nvme_ns_t *ns; 2956 uint32_t nsid = nvme_ns_disc_nsid(disc); 2957 2958 if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) { 2959 nvmeadm_warn(npa, "failed to open namespace %s/%u " 2960 "handle", npa->npa_name, nsid); 2961 rv = -1; 2962 continue; 2963 } 2964 2965 if (!nvme_ns_bd_detach(ns)) { 2966 nvmeadm_warn(npa, "failed to detach namespace " 2967 "%s/%u", npa->npa_name, nsid); 2968 rv = -1; 2969 } 2970 nvme_ns_fini(ns); 2971 } 2972 2973 nvme_ns_discover_fini(iter); 2974 if (ret == NVME_ITER_ERROR) { 2975 nvmeadm_warn(npa, "failed to iterate namespaces on %s", 2976 npa->npa_name); 2977 rv = -1; 2978 } 2979 2980 return (rv); 2981 } 2982 2983 static void 2984 usage_firmware_load(const char *c_name) 2985 { 2986 (void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n" 2987 " Load firmware <image file> to offset <offset>.\n" 2988 " The firmware needs to be committed to a slot using " 2989 "\"nvmeadm commit-firmware\"\n command.\n", c_name); 2990 } 2991 2992 /* 2993 * Read exactly len bytes, or until eof. 2994 */ 2995 static size_t 2996 read_block(const nvme_process_arg_t *npa, int fd, char *buf, size_t len) 2997 { 2998 size_t remain; 2999 3000 remain = len; 3001 while (remain > 0) { 3002 ssize_t bytes = read(fd, buf, remain); 3003 if (bytes == 0) 3004 break; 3005 3006 if (bytes < 0) { 3007 if (errno == EINTR) 3008 continue; 3009 3010 err(-1, "Error reading \"%s\"", npa->npa_argv[0]); 3011 } 3012 3013 buf += (size_t)bytes; 3014 remain -= (size_t)bytes; 3015 } 3016 3017 return (len - remain); 3018 } 3019 3020 /* 3021 * Convert a string to a valid firmware upload offset (in bytes). 3022 */ 3023 static uint64_t 3024 get_fw_offsetb(char *str) 3025 { 3026 longlong_t offsetb; 3027 char *valend; 3028 3029 errno = 0; 3030 offsetb = strtoll(str, &valend, 0); 3031 if (errno != 0 || *valend != '\0' || offsetb < 0 || 3032 offsetb > NVME_FW_OFFSETB_MAX) 3033 errx(-1, "Offset must be numeric and in the range of 0 to %llu", 3034 NVME_FW_OFFSETB_MAX); 3035 3036 if ((offsetb & NVME_DWORD_MASK) != 0) 3037 errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE); 3038 3039 return ((uint64_t)offsetb); 3040 } 3041 3042 #define FIRMWARE_READ_BLKSIZE (64 * 1024) /* 64K */ 3043 3044 static int 3045 do_firmware_load(const nvme_process_arg_t *npa) 3046 { 3047 int fw_fd; 3048 uint64_t offset = 0; 3049 size_t size, len; 3050 char buf[FIRMWARE_READ_BLKSIZE]; 3051 3052 if (npa->npa_argc > 2) 3053 errx(-1, "%s passed extraneous arguments starting with %s", 3054 npa->npa_cmd->c_name, npa->npa_argv[2]); 3055 3056 if (npa->npa_argc == 0) 3057 errx(-1, "Requires firmware file name, and an " 3058 "optional offset"); 3059 3060 if (npa->npa_ns != NULL) 3061 errx(-1, "Firmware loading not available on a per-namespace " 3062 "basis"); 3063 3064 if (npa->npa_argc == 2) 3065 offset = get_fw_offsetb(npa->npa_argv[1]); 3066 3067 fw_fd = open(npa->npa_argv[0], O_RDONLY); 3068 if (fw_fd < 0) 3069 errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0], 3070 strerror(errno)); 3071 3072 size = 0; 3073 do { 3074 len = read_block(npa, fw_fd, buf, sizeof (buf)); 3075 3076 if (len == 0) 3077 break; 3078 3079 if (!nvme_fw_load(npa->npa_ctrl, buf, len, offset)) { 3080 nvmeadm_fatal(npa, "failed to load firmware image " 3081 "\"%s\" at offset %" PRIu64, npa->npa_argv[0], 3082 offset); 3083 } 3084 3085 offset += len; 3086 size += len; 3087 } while (len == sizeof (buf)); 3088 3089 (void) close(fw_fd); 3090 3091 if (verbose) 3092 (void) printf("%zu bytes downloaded.\n", size); 3093 3094 return (0); 3095 } 3096 3097 /* 3098 * Common firmware commit for nvmeadm commit-firmware and activate-firmware. 3099 */ 3100 static void 3101 nvmeadm_firmware_commit(const nvme_process_arg_t *npa, uint32_t slot, 3102 uint32_t act) 3103 { 3104 nvme_fw_commit_req_t *req; 3105 3106 if (!nvme_fw_commit_req_init(npa->npa_ctrl, &req)) { 3107 nvmeadm_fatal(npa, "failed to initialize firmware commit " 3108 "request for %s", npa->npa_name); 3109 } 3110 3111 if (!nvme_fw_commit_req_set_slot(req, slot) || 3112 !nvme_fw_commit_req_set_action(req, act)) { 3113 nvmeadm_fatal(npa, "failed to set firmware commit fields for " 3114 "%s", npa->npa_name); 3115 } 3116 3117 if (!nvme_fw_commit_req_exec(req)) { 3118 nvmeadm_fatal(npa, "failed to %s firmware on %s", 3119 npa->npa_cmd->c_name, npa->npa_name); 3120 } 3121 3122 nvme_fw_commit_req_fini(req); 3123 } 3124 3125 /* 3126 * Convert str to a valid firmware slot number. 3127 */ 3128 static uint32_t 3129 get_slot_number(char *str) 3130 { 3131 longlong_t slot; 3132 char *valend; 3133 3134 errno = 0; 3135 slot = strtoll(str, &valend, 0); 3136 if (errno != 0 || *valend != '\0' || 3137 slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX) 3138 errx(-1, "Slot must be numeric and in the range of %u to %u", 3139 NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX); 3140 3141 return ((uint32_t)slot); 3142 } 3143 3144 static void 3145 usage_firmware_commit(const char *c_name) 3146 { 3147 (void) fprintf(stderr, "%s <ctl> <slot>\n\n" 3148 " Commit previously downloaded firmware to slot <slot>.\n" 3149 " The firmware is only activated after a " 3150 "\"nvmeadm activate-firmware\" command.\n", c_name); 3151 } 3152 3153 static int 3154 do_firmware_commit(const nvme_process_arg_t *npa) 3155 { 3156 uint32_t slot; 3157 3158 if (npa->npa_argc > 1) 3159 errx(-1, "%s passed extraneous arguments starting with %s", 3160 npa->npa_cmd->c_name, npa->npa_argv[1]); 3161 3162 if (npa->npa_argc == 0) 3163 errx(-1, "Firmware slot number is required"); 3164 3165 if (npa->npa_ns != NULL) 3166 errx(-1, "Firmware committing not available on a per-namespace " 3167 "basis"); 3168 3169 slot = get_slot_number(npa->npa_argv[0]); 3170 3171 if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly) 3172 errx(-1, "Cannot commit firmware to slot 1: slot is read-only"); 3173 3174 nvmeadm_firmware_commit(npa, slot, NVME_FWC_SAVE); 3175 3176 if (verbose) 3177 (void) printf("Firmware committed to slot %u.\n", slot); 3178 3179 return (0); 3180 } 3181 3182 static void 3183 usage_firmware_activate(const char *c_name) 3184 { 3185 (void) fprintf(stderr, "%s <ctl> <slot>\n\n" 3186 " Activate firmware in slot <slot>.\n" 3187 " The firmware will be in use after the next system reset.\n", 3188 c_name); 3189 } 3190 3191 static int 3192 do_firmware_activate(const nvme_process_arg_t *npa) 3193 { 3194 uint32_t slot; 3195 3196 if (npa->npa_argc > 1) 3197 errx(-1, "%s passed extraneous arguments starting with %s", 3198 npa->npa_cmd->c_name, npa->npa_argv[1]); 3199 3200 if (npa->npa_argc == 0) 3201 errx(-1, "Firmware slot number is required"); 3202 3203 if (npa->npa_ns != NULL) 3204 errx(-1, "Firmware activation not available on a per-namespace " 3205 "basis"); 3206 3207 slot = get_slot_number(npa->npa_argv[0]); 3208 3209 nvmeadm_firmware_commit(npa, slot, NVME_FWC_ACTIVATE); 3210 3211 if (verbose) 3212 (void) printf("Slot %u successfully activated.\n", slot); 3213 3214 return (0); 3215 } 3216 3217 nvme_vuc_disc_t * 3218 nvmeadm_vuc_init(const nvme_process_arg_t *npa, const char *name) 3219 { 3220 nvme_vuc_disc_t *vuc; 3221 nvme_vuc_disc_lock_t lock; 3222 3223 if (!nvme_vuc_discover_by_name(npa->npa_ctrl, name, 0, &vuc)) { 3224 nvmeadm_fatal(npa, "%s does not support operation %s: device " 3225 "does not support vendor unique command %s", npa->npa_name, 3226 npa->npa_cmd->c_name, name); 3227 } 3228 3229 lock = nvme_vuc_disc_lock(vuc); 3230 switch (lock) { 3231 case NVME_VUC_DISC_LOCK_NONE: 3232 break; 3233 case NVME_VUC_DISC_LOCK_READ: 3234 nvmeadm_excl(npa, NVME_LOCK_L_READ); 3235 break; 3236 case NVME_VUC_DISC_LOCK_WRITE: 3237 nvmeadm_excl(npa, NVME_LOCK_L_WRITE); 3238 break; 3239 } 3240 3241 return (vuc); 3242 } 3243 3244 void 3245 nvmeadm_vuc_fini(const nvme_process_arg_t *npa, nvme_vuc_disc_t *vuc) 3246 { 3247 if (nvme_vuc_disc_lock(vuc) != NVME_VUC_DISC_LOCK_NONE) { 3248 if (npa->npa_ns != NULL) { 3249 nvme_ns_unlock(npa->npa_ns); 3250 } else if (npa->npa_ctrl != NULL) { 3251 nvme_ctrl_unlock(npa->npa_ctrl); 3252 } 3253 } 3254 3255 nvme_vuc_disc_free(vuc); 3256 } 3257