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