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