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