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