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