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