xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm.c (revision a4955f4fa65e38d70c07d38e657a9aff43fa155f)
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 2021 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  *		get-logpage <logpage name>
25  *		get-features <feature>[,...]
26  *		format ...
27  *		secure-erase ...
28  *		detach ...
29  *		attach ...
30  *		list-firmware ...
31  *		load-firmware ...
32  *		commit-firmware ...
33  *		activate-firmware ...
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stddef.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <strings.h>
42 #include <ctype.h>
43 #include <err.h>
44 #include <sys/sunddi.h>
45 #include <libdevinfo.h>
46 
47 #include <sys/nvme.h>
48 
49 #include "nvmeadm.h"
50 
51 /*
52  * Assertions to make sure that we've properly captured various aspects of the
53  * packed structures and haven't broken them during updates.
54  */
55 CTASSERT(sizeof (nvme_identify_ctrl_t) == NVME_IDENTIFY_BUFSIZE);
56 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oacs) == 256);
57 CTASSERT(offsetof(nvme_identify_ctrl_t, id_sqes) == 512);
58 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oncs) == 520);
59 CTASSERT(offsetof(nvme_identify_ctrl_t, id_subnqn) == 768);
60 CTASSERT(offsetof(nvme_identify_ctrl_t, id_nvmof) == 1792);
61 CTASSERT(offsetof(nvme_identify_ctrl_t, id_psd) == 2048);
62 CTASSERT(offsetof(nvme_identify_ctrl_t, id_vs) == 3072);
63 
64 CTASSERT(sizeof (nvme_identify_nsid_t) == NVME_IDENTIFY_BUFSIZE);
65 CTASSERT(offsetof(nvme_identify_nsid_t, id_fpi) == 32);
66 CTASSERT(offsetof(nvme_identify_nsid_t, id_anagrpid) == 92);
67 CTASSERT(offsetof(nvme_identify_nsid_t, id_nguid) == 104);
68 CTASSERT(offsetof(nvme_identify_nsid_t, id_lbaf) == 128);
69 CTASSERT(offsetof(nvme_identify_nsid_t, id_vs) == 384);
70 
71 CTASSERT(sizeof (nvme_identify_nsid_list_t) == NVME_IDENTIFY_BUFSIZE);
72 CTASSERT(sizeof (nvme_identify_ctrl_list_t) == NVME_IDENTIFY_BUFSIZE);
73 
74 CTASSERT(sizeof (nvme_identify_primary_caps_t) == NVME_IDENTIFY_BUFSIZE);
75 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vqfrt) == 32);
76 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vifrt) == 64);
77 
78 CTASSERT(sizeof (nvme_nschange_list_t) == 4096);
79 
80 
81 struct nvme_feature {
82 	char *f_name;
83 	char *f_short;
84 	uint8_t f_feature;
85 	size_t f_bufsize;
86 	uint_t f_getflags;
87 	int (*f_get)(int, const nvme_feature_t *, const nvme_process_arg_t *);
88 	void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *,
89 	    nvme_version_t *);
90 };
91 
92 #define	NVMEADM_F_CTRL	1
93 #define	NVMEADM_F_NS	2
94 #define	NVMEADM_F_BOTH	(NVMEADM_F_CTRL | NVMEADM_F_NS)
95 
96 #define	NVMEADM_C_MULTI	1
97 #define	NVMEADM_C_EXCL	2
98 
99 struct nvmeadm_cmd {
100 	char *c_name;
101 	const char *c_desc;
102 	const char *c_flagdesc;
103 	int (*c_func)(int, const nvme_process_arg_t *);
104 	void (*c_usage)(const char *);
105 	void (*c_optparse)(nvme_process_arg_t *);
106 	int c_flags;
107 };
108 
109 
110 static void usage(const nvmeadm_cmd_t *);
111 static void nvme_walk(nvme_process_arg_t *, di_node_t);
112 static boolean_t nvme_match_ctrl(nvme_process_arg_t *);
113 static boolean_t nvme_match_ns(nvme_process_arg_t *);
114 
115 static int nvme_process(di_node_t, di_minor_t, void *);
116 
117 static int do_list(int, const nvme_process_arg_t *);
118 static int do_identify(int, const nvme_process_arg_t *);
119 static int do_identify_ctrl(int, const nvme_process_arg_t *);
120 static int do_identify_ns(int, const nvme_process_arg_t *);
121 static int do_get_logpage_error(int, const nvme_process_arg_t *);
122 static int do_get_logpage_health(int, const nvme_process_arg_t *);
123 static int do_get_logpage_fwslot(int, const nvme_process_arg_t *);
124 static int do_get_logpage(int, const nvme_process_arg_t *);
125 static int do_get_feat_common(int, const nvme_feature_t *,
126     const nvme_process_arg_t *);
127 static int do_get_feat_intr_vect(int, const nvme_feature_t *,
128     const nvme_process_arg_t *);
129 static int do_get_feat_temp_thresh(int, const nvme_feature_t *,
130     const nvme_process_arg_t *);
131 static int do_get_features(int, const nvme_process_arg_t *);
132 static int do_format(int, const nvme_process_arg_t *);
133 static int do_secure_erase(int, const nvme_process_arg_t *);
134 static int do_attach_detach(int, const nvme_process_arg_t *);
135 static int do_firmware_load(int, const nvme_process_arg_t *);
136 static int do_firmware_commit(int, const nvme_process_arg_t *);
137 static int do_firmware_activate(int, const nvme_process_arg_t *);
138 
139 static void optparse_list(nvme_process_arg_t *);
140 static void optparse_identify(nvme_process_arg_t *);
141 static void optparse_identify_ctrl(nvme_process_arg_t *);
142 static void optparse_identify_ns(nvme_process_arg_t *);
143 static void optparse_secure_erase(nvme_process_arg_t *);
144 
145 static void usage_list(const char *);
146 static void usage_identify(const char *);
147 static void usage_identify_ctrl(const char *);
148 static void usage_identify_ns(const char *);
149 static void usage_get_logpage(const char *);
150 static void usage_get_features(const char *);
151 static void usage_format(const char *);
152 static void usage_secure_erase(const char *);
153 static void usage_attach_detach(const char *);
154 static void usage_firmware_list(const char *);
155 static void usage_firmware_load(const char *);
156 static void usage_firmware_commit(const char *);
157 static void usage_firmware_activate(const char *);
158 
159 int verbose;
160 int debug;
161 
162 #define	NVMEADM_O_SE_CRYPTO	0x00000004
163 
164 #define	NVMEADM_O_ID_NSID_LIST	0x00000008
165 #define	NVMEADM_O_ID_COMMON_NS	0x00000010
166 #define	NVMEADM_O_ID_CTRL_LIST	0x00000020
167 #define	NVMEADM_O_ID_DESC_LIST	0x00000040
168 #define	NVMEADM_O_ID_ALLOC_NS	0x00000080
169 
170 static int exitcode;
171 
172 /*
173  * Nvmeadm subcommand definitons.
174  *
175  * When adding a new subcommand, please check that the commands still
176  * line up in the usage() message, and adjust the format string in
177  * usage() below if necessary.
178  */
179 static const nvmeadm_cmd_t nvmeadm_cmds[] = {
180 	{
181 		"list",
182 		"list controllers and namespaces",
183 		"  -p\t\tprint parsable output\n"
184 		    "  -o field\tselect a field for parsable output\n",
185 		do_list, usage_list, optparse_list,
186 		NVMEADM_C_MULTI
187 	},
188 	{
189 		"identify",
190 		"identify controllers and/or namespaces",
191 		"  -C\t\tget Common Namespace Identification\n"
192 		"  -a\t\tget only allocated namespace information\n"
193 		"  -c\t\tget controller identifier list\n"
194 		"  -d\t\tget namespace identification descriptors list\n"
195 		"  -n\t\tget namespaces identifier list\n",
196 		do_identify, usage_identify, optparse_identify,
197 		NVMEADM_C_MULTI
198 	},
199 	{
200 		"identify-controller",
201 		"identify controllers",
202 		"  -C\t\tget Common Namespace Identification\n"
203 		"  -a\t\tget only allocated namespace information\n"
204 		"  -c\t\tget controller identifier list\n"
205 		"  -n\t\tget namespaces identifier list\n",
206 		do_identify_ctrl, usage_identify_ctrl, optparse_identify_ctrl,
207 		NVMEADM_C_MULTI
208 	},
209 	{
210 		"identify-namespace",
211 		"identify namespaces",
212 		"  -c\t\tget attached controller identifier list\n"
213 		"  -d\t\tget namespace identification descriptors list\n",
214 		do_identify_ns, usage_identify_ns, optparse_identify_ns,
215 		NVMEADM_C_MULTI
216 	},
217 	{
218 		"get-logpage",
219 		"get a log page from controllers and/or namespaces",
220 		NULL,
221 		do_get_logpage, usage_get_logpage, NULL,
222 		NVMEADM_C_MULTI
223 	},
224 	{
225 		"get-features",
226 		"get features from controllers and/or namespaces",
227 		NULL,
228 		do_get_features, usage_get_features, NULL,
229 		NVMEADM_C_MULTI
230 	},
231 	{
232 		"format",
233 		"format namespace(s) of a controller",
234 		NULL,
235 		do_format, usage_format, NULL,
236 		NVMEADM_C_EXCL
237 	},
238 	{
239 		"secure-erase",
240 		"secure erase namespace(s) of a controller",
241 		"  -c  Do a cryptographic erase.",
242 		do_secure_erase, usage_secure_erase, optparse_secure_erase,
243 		NVMEADM_C_EXCL
244 	},
245 	{
246 		"detach",
247 		"detach blkdev(4D) from namespace(s) of a controller",
248 		NULL,
249 		do_attach_detach, usage_attach_detach, NULL,
250 		NVMEADM_C_EXCL
251 	},
252 	{
253 		"attach",
254 		"attach blkdev(4D) to namespace(s) of a controller",
255 		NULL,
256 		do_attach_detach, usage_attach_detach, NULL,
257 		NVMEADM_C_EXCL
258 	},
259 	{
260 		"list-firmware",
261 		"list firmware on a controller",
262 		NULL,
263 		do_get_logpage_fwslot, usage_firmware_list, NULL,
264 		0
265 	},
266 	{
267 		"load-firmware",
268 		"load firmware to a controller",
269 		NULL,
270 		do_firmware_load, usage_firmware_load, NULL,
271 		0
272 	},
273 	{
274 		"commit-firmware",
275 		"commit downloaded firmware to a slot of a controller",
276 		NULL,
277 		do_firmware_commit, usage_firmware_commit, NULL,
278 		0
279 	},
280 	{
281 		"activate-firmware",
282 		"activate a firmware slot of a controller",
283 		NULL,
284 		do_firmware_activate, usage_firmware_activate, NULL,
285 		0
286 	},
287 	{
288 		NULL, NULL, NULL,
289 		NULL, NULL, NULL, 0
290 	}
291 };
292 
293 static const nvme_feature_t features[] = {
294 	{ "Arbitration", "",
295 	    NVME_FEAT_ARBITRATION, 0, NVMEADM_F_CTRL,
296 	    do_get_feat_common, nvme_print_feat_arbitration },
297 	{ "Power Management", "",
298 	    NVME_FEAT_POWER_MGMT, 0, NVMEADM_F_CTRL,
299 	    do_get_feat_common, nvme_print_feat_power_mgmt },
300 	{ "LBA Range Type", "range",
301 	    NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_F_NS,
302 	    do_get_feat_common, nvme_print_feat_lba_range },
303 	{ "Temperature Threshold", "",
304 	    NVME_FEAT_TEMPERATURE, 0, NVMEADM_F_CTRL,
305 	    do_get_feat_temp_thresh, nvme_print_feat_temperature },
306 	{ "Error Recovery", "",
307 	    NVME_FEAT_ERROR, 0, NVMEADM_F_CTRL,
308 	    do_get_feat_common, nvme_print_feat_error },
309 	{ "Volatile Write Cache", "cache",
310 	    NVME_FEAT_WRITE_CACHE, 0, NVMEADM_F_CTRL,
311 	    do_get_feat_common, nvme_print_feat_write_cache },
312 	{ "Number of Queues", "queues",
313 	    NVME_FEAT_NQUEUES, 0, NVMEADM_F_CTRL,
314 	    do_get_feat_common, nvme_print_feat_nqueues },
315 	{ "Interrupt Coalescing", "coalescing",
316 	    NVME_FEAT_INTR_COAL, 0, NVMEADM_F_CTRL,
317 	    do_get_feat_common, nvme_print_feat_intr_coal },
318 	{ "Interrupt Vector Configuration", "vector",
319 	    NVME_FEAT_INTR_VECT, 0, NVMEADM_F_CTRL,
320 	    do_get_feat_intr_vect, nvme_print_feat_intr_vect },
321 	{ "Write Atomicity", "atomicity",
322 	    NVME_FEAT_WRITE_ATOM, 0, NVMEADM_F_CTRL,
323 	    do_get_feat_common, nvme_print_feat_write_atom },
324 	{ "Asynchronous Event Configuration", "event",
325 	    NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_F_CTRL,
326 	    do_get_feat_common, nvme_print_feat_async_event },
327 	{ "Autonomous Power State Transition", "",
328 	    NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_F_CTRL,
329 	    do_get_feat_common, nvme_print_feat_auto_pst },
330 	{ "Software Progress Marker", "progress",
331 	    NVME_FEAT_PROGRESS, 0, NVMEADM_F_CTRL,
332 	    do_get_feat_common, nvme_print_feat_progress },
333 	{ NULL, NULL, 0, 0, B_FALSE, NULL }
334 };
335 
336 
337 int
338 main(int argc, char **argv)
339 {
340 	int c;
341 	const nvmeadm_cmd_t *cmd;
342 	di_node_t node;
343 	nvme_process_arg_t npa = { 0 };
344 	int help = 0;
345 	char *tmp, *lasts = NULL;
346 	char *ctrl = NULL;
347 
348 	while ((c = getopt(argc, argv, "dhv")) != -1) {
349 		switch (c) {
350 		case 'd':
351 			debug++;
352 			break;
353 
354 		case 'v':
355 			verbose++;
356 			break;
357 
358 		case 'h':
359 			help++;
360 			break;
361 
362 		case '?':
363 			usage(NULL);
364 			exit(-1);
365 		}
366 	}
367 
368 	if (optind == argc) {
369 		usage(NULL);
370 		if (help)
371 			exit(0);
372 		else
373 			exit(-1);
374 	}
375 
376 	/* Look up the specified command in the command table. */
377 	for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
378 		if (strcmp(cmd->c_name, argv[optind]) == 0)
379 			break;
380 
381 	if (cmd->c_name == NULL) {
382 		usage(NULL);
383 		exit(-1);
384 	}
385 
386 	if (help) {
387 		usage(cmd);
388 		exit(0);
389 	}
390 
391 	npa.npa_cmd = cmd;
392 	npa.npa_interactive = B_TRUE;
393 	npa.npa_excl = ((cmd->c_flags & NVMEADM_C_EXCL) != 0);
394 
395 	optind++;
396 
397 	/*
398 	 * Store the remaining arguments for use by the command. Give the
399 	 * command a chance to process the options across the board before going
400 	 * into each controller.
401 	 */
402 	npa.npa_argc = argc - optind;
403 	npa.npa_argv = &argv[optind];
404 
405 	if (cmd->c_optparse != NULL) {
406 		cmd->c_optparse(&npa);
407 	}
408 
409 	/*
410 	 * All commands but "list" require a ctl/ns argument. However, this
411 	 * should not be passed through to the command in its subsequent
412 	 * arguments.
413 	 */
414 	if ((npa.npa_argc == 0 || (strncmp(npa.npa_argv[0], "nvme", 4) != 0)) &&
415 	    cmd->c_func != do_list) {
416 		warnx("missing controller/namespace name");
417 		usage(cmd);
418 		exit(-1);
419 	}
420 
421 	if (npa.npa_argc > 0) {
422 		ctrl = npa.npa_argv[0];
423 		npa.npa_argv++;
424 		npa.npa_argc--;
425 	} else {
426 		ctrl = NULL;
427 	}
428 
429 	/*
430 	 * Make sure we're not running commands on multiple controllers that
431 	 * aren't allowed to do that.
432 	 */
433 	if (ctrl != NULL && strchr(ctrl, ',') != NULL &&
434 	    (cmd->c_flags & NVMEADM_C_MULTI) == 0) {
435 		warnx("%s not allowed on multiple controllers",
436 		    cmd->c_name);
437 		usage(cmd);
438 		exit(-1);
439 	}
440 
441 	/*
442 	 * Get controller/namespace arguments and run command.
443 	 */
444 	npa.npa_name = strtok_r(ctrl, ",", &lasts);
445 	do {
446 		if (npa.npa_name != NULL) {
447 			tmp = strchr(npa.npa_name, '/');
448 			if (tmp != NULL) {
449 				*tmp++ = '\0';
450 				npa.npa_nsid = tmp;
451 				npa.npa_isns = B_TRUE;
452 			}
453 		}
454 
455 		if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL)
456 			err(-1, "failed to initialize libdevinfo");
457 		nvme_walk(&npa, node);
458 		di_fini(node);
459 
460 		if (npa.npa_found == 0) {
461 			if (npa.npa_name != NULL) {
462 				warnx("%s%.*s%.*s: no such controller or "
463 				    "namespace", npa.npa_name,
464 				    npa.npa_isns ? -1 : 0, "/",
465 				    npa.npa_isns ? -1 : 0, npa.npa_nsid);
466 			} else {
467 				warnx("no controllers found");
468 			}
469 			exitcode--;
470 		}
471 		npa.npa_found = 0;
472 		npa.npa_name = strtok_r(NULL, ",", &lasts);
473 	} while (npa.npa_name != NULL);
474 
475 	exit(exitcode);
476 }
477 
478 static void
479 nvme_oferr(const char *fmt, ...)
480 {
481 	va_list ap;
482 
483 	va_start(ap, fmt);
484 	verrx(-1, fmt, ap);
485 }
486 
487 static void
488 usage(const nvmeadm_cmd_t *cmd)
489 {
490 	const char *progname = getprogname();
491 
492 	(void) fprintf(stderr, "usage:\n");
493 	(void) fprintf(stderr, "  %s -h %s\n", progname,
494 	    cmd != NULL ? cmd->c_name : "[<command>]");
495 	(void) fprintf(stderr, "  %s [-dv] ", progname);
496 
497 	if (cmd != NULL) {
498 		cmd->c_usage(cmd->c_name);
499 	} else {
500 		(void) fprintf(stderr,
501 		    "<command> <ctl>[/<ns>][,...] [<args>]\n");
502 		(void) fprintf(stderr,
503 		    "\n  Manage NVMe controllers and namespaces.\n");
504 		(void) fprintf(stderr, "\ncommands:\n");
505 
506 		for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) {
507 			/*
508 			 * The longest nvmeadm subcommand is 19 characters long.
509 			 * The format string needs to be updated every time a
510 			 * longer subcommand is added.
511 			 */
512 			(void) fprintf(stderr, "  %-19s - %s\n",
513 			    cmd->c_name, cmd->c_desc);
514 		}
515 	}
516 	(void) fprintf(stderr, "\n%s flags:\n"
517 	    "  -h\t\tprint usage information\n"
518 	    "  -d\t\tprint information useful for debugging %s\n"
519 	    "  -v\t\tprint verbose information\n",
520 	    progname, progname);
521 
522 	if (cmd != NULL && cmd->c_flagdesc != NULL) {
523 		(void) fprintf(stderr, "\n%s %s flags:\n",
524 		    progname, cmd->c_name);
525 		(void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
526 	}
527 }
528 
529 static boolean_t
530 nvme_match_ctrl(nvme_process_arg_t *npa)
531 {
532 	char *name;
533 
534 	if (npa->npa_name == NULL)
535 		return (B_TRUE);
536 
537 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
538 	    di_instance(npa->npa_node)) < 0)
539 		err(-1, "nvme_match()");
540 
541 	if (strcmp(name, npa->npa_name) != 0) {
542 		free(name);
543 		return (B_FALSE);
544 	}
545 
546 	free(name);
547 
548 	return (B_TRUE);
549 }
550 
551 static boolean_t
552 nvme_match_ns(nvme_process_arg_t *npa)
553 {
554 	if (npa->npa_nsid == NULL)
555 		return (B_TRUE);
556 
557 	if (strcasecmp(npa->npa_nsid, di_minor_name(npa->npa_minor)) ==
558 	    0)
559 		return (B_TRUE);
560 
561 	if (npa->npa_eui64 != NULL &&
562 	    strcasecmp(npa->npa_nsid, npa->npa_eui64) == 0)
563 		return (B_TRUE);
564 
565 	if (npa->npa_nguid != NULL &&
566 	    strcasecmp(npa->npa_nsid, npa->npa_nguid) == 0)
567 		return (B_TRUE);
568 
569 	return (B_FALSE);
570 }
571 
572 char *
573 nvme_nguid(const nvme_process_arg_t *npa)
574 {
575 	char *ret = NULL;
576 
577 	if (*(uint64_t *)npa->npa_idns->id_nguid != 0 ||
578 	    *((uint64_t *)npa->npa_idns->id_nguid + 1) != 0) {
579 		uint8_t *guid = npa->npa_idns->id_nguid;
580 
581 		(void) asprintf(&ret,
582 		    "%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X"
583 		    "%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X",
584 		    guid[0], guid[1], guid[2], guid[3],
585 		    guid[4], guid[5], guid[6], guid[7],
586 		    guid[8], guid[9], guid[10], guid[11],
587 		    guid[12], guid[13], guid[14], guid[15]);
588 	}
589 
590 	return (ret);
591 }
592 
593 char *
594 nvme_eui64(const nvme_process_arg_t *npa)
595 {
596 	char *ret = NULL;
597 
598 	if (*(uint64_t *)npa->npa_idns->id_eui64 != 0) {
599 		uint8_t *eui64 = npa->npa_idns->id_eui64;
600 
601 		(void) asprintf(&ret,
602 		    "%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X",
603 		    eui64[0], eui64[1], eui64[2], eui64[3],
604 		    eui64[4], eui64[5], eui64[6], eui64[7]);
605 	}
606 
607 	return (ret);
608 }
609 
610 char *
611 nvme_dskname(const nvme_process_arg_t *npa)
612 {
613 	char *path = NULL;
614 	di_node_t child;
615 	di_dim_t dim;
616 	char *addr;
617 	char *disk_ctd;
618 	char *diskname = NULL;
619 
620 	dim = di_dim_init();
621 
622 	for (child = di_child_node(npa->npa_node);
623 	    child != DI_NODE_NIL;
624 	    child = di_sibling_node(child)) {
625 		addr = di_bus_addr(child);
626 		if (addr == NULL)
627 			continue;
628 
629 		addr = strdup(addr);
630 		if (addr == NULL)
631 			goto fail;
632 
633 		if (addr[0] == 'w')
634 			addr++;
635 
636 		/* Chop off ,... from the bus address. */
637 		*(strchrnul(addr, ',')) = '\0';
638 
639 		/*
640 		 * If there's a EUI64, that's what was used for the bus address.
641 		 * Otherwise it's just the numeric namespace id.
642 		 */
643 		if (npa->npa_eui64 != NULL &&
644 		    strcasecmp(addr, npa->npa_eui64) == 0)
645 			goto found;
646 
647 		if (strcasecmp(addr, di_minor_name(npa->npa_minor)) != 0) {
648 			free(addr);
649 			continue;
650 		}
651 
652 found:
653 		path = di_dim_path_dev(dim, di_driver_name(child),
654 		    di_instance(child), "c");
655 
656 		/*
657 		 * Error out if we didn't get a path, or if it's too short for
658 		 * the following operations to be safe.
659 		 */
660 		if (path == NULL || strlen(path) < 2)
661 			goto fail;
662 
663 		/* Chop off 's0' and get everything past the last '/' */
664 		path[strlen(path) - 2] = '\0';
665 		disk_ctd = strrchr(path, '/');
666 		if (disk_ctd == NULL)
667 			goto fail;
668 		diskname = strdup(++disk_ctd);
669 		if (diskname == NULL)
670 			goto fail;
671 
672 		free(path);
673 		free(addr);
674 		break;
675 	}
676 
677 	di_dim_fini(dim);
678 
679 	return (diskname);
680 
681 fail:
682 	free(path);
683 	free(addr);
684 	err(-1, "nvme_dskname");
685 }
686 
687 static int
688 nvme_process(di_node_t node, di_minor_t minor, void *arg)
689 {
690 	nvme_process_arg_t *npa = arg;
691 	int fd;
692 
693 	npa->npa_node = node;
694 	npa->npa_minor = minor;
695 
696 	if (!nvme_match_ctrl(npa))
697 		return (DI_WALK_CONTINUE);
698 
699 	if ((fd = nvme_open(minor, npa->npa_excl)) < 0)
700 		return (DI_WALK_CONTINUE);
701 
702 	npa->npa_version = nvme_version(fd);
703 	if (npa->npa_version == NULL)
704 		goto out;
705 
706 	npa->npa_idctl = nvme_identify(fd, NVME_IDENTIFY_CTRL);
707 	if (npa->npa_idctl == NULL)
708 		goto out;
709 
710 	if (nvme_version_check(npa->npa_version, 1, 2) &&
711 	    npa->npa_idctl->id_oacs.oa_nsmgmt != 0 &&
712 	    npa->npa_isns) {
713 		/*
714 		 * We prefer NVME_IDENTIFY_NSID_ALLOC when supported as that can
715 		 * return data on inactive namespaces, too.
716 		 */
717 		npa->npa_idns = nvme_identify(fd, NVME_IDENTIFY_NSID_ALLOC);
718 	} else {
719 		npa->npa_idns = nvme_identify(fd, NVME_IDENTIFY_NSID);
720 	}
721 
722 	if (npa->npa_idns == NULL)
723 		goto out;
724 
725 	npa->npa_eui64 = NULL;
726 	npa->npa_nguid = NULL;
727 	npa->npa_dsk = NULL;
728 
729 	if (npa->npa_isns) {
730 		npa->npa_ns_state = nvme_namespace_state(fd);
731 
732 		if ((npa->npa_ns_state & NVME_NS_STATE_ACTIVE) != 0) {
733 			npa->npa_eui64 = nvme_eui64(npa);
734 			npa->npa_nguid = nvme_nguid(npa);
735 		}
736 
737 		if ((npa->npa_ns_state & NVME_NS_STATE_ATTACHED) != 0) {
738 			npa->npa_dsk = nvme_dskname(npa);
739 		}
740 
741 		if (!nvme_match_ns(npa))
742 			goto out;
743 	}
744 
745 	npa->npa_found++;
746 
747 	exitcode += npa->npa_cmd->c_func(fd, npa);
748 
749 out:
750 	free(npa->npa_version);
751 	free(npa->npa_idctl);
752 	free(npa->npa_idns);
753 	free(npa->npa_dsk);
754 	free(npa->npa_eui64);
755 	free(npa->npa_nguid);
756 
757 	npa->npa_version = NULL;
758 	npa->npa_idctl = NULL;
759 	npa->npa_idns = NULL;
760 	npa->npa_dsk = NULL;
761 	npa->npa_eui64 = NULL;
762 	npa->npa_nguid = NULL;
763 
764 	nvme_close(fd);
765 
766 	return (DI_WALK_CONTINUE);
767 }
768 
769 static void
770 nvme_walk(nvme_process_arg_t *npa, di_node_t node)
771 {
772 	char *minor_nodetype = DDI_NT_NVME_NEXUS;
773 
774 	if (npa->npa_isns)
775 		minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT;
776 
777 	(void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process);
778 }
779 
780 static void
781 usage_list(const char *c_name)
782 {
783 	(void) fprintf(stderr, "%s "
784 	    "[-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n"
785 	    "  List NVMe controllers and their namespaces. If no "
786 	    "controllers and/or name-\n  spaces are specified, all "
787 	    "controllers and namespaces in the system will be\n  "
788 	    "listed.\n", c_name);
789 }
790 
791 static void
792 optparse_list(nvme_process_arg_t *npa)
793 {
794 	int c;
795 	uint_t oflags = 0;
796 	boolean_t parse = B_FALSE;
797 	const char *fields = NULL;
798 
799 	optind = 0;
800 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":o:p")) != -1) {
801 		switch (c) {
802 		case 'o':
803 			fields = optarg;
804 			break;
805 
806 		case 'p':
807 			parse = B_TRUE;
808 			oflags |= OFMT_PARSABLE;
809 			break;
810 
811 		case '?':
812 			errx(-1, "unknown option: -%c", optopt);
813 
814 		case ':':
815 			errx(-1, "option -%c requires an argument", optopt);
816 		}
817 	}
818 
819 	if (fields != NULL && !parse) {
820 		errx(-1, "-o can only be used when in parsable mode (-p)");
821 	}
822 
823 	if (parse && fields == NULL) {
824 		errx(-1, "parsable mode (-p) requires one to specify output "
825 		    "fields with -o");
826 	}
827 
828 	if (parse) {
829 		ofmt_status_t oferr;
830 
831 		oferr = ofmt_open(fields, nvme_list_ofmt, oflags, 0,
832 		    &npa->npa_ofmt);
833 		ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
834 	}
835 
836 	npa->npa_argc -= optind;
837 	npa->npa_argv += optind;
838 }
839 
840 static int
841 do_list_nsid(int fd, const nvme_process_arg_t *npa)
842 {
843 	_NOTE(ARGUNUSED(fd));
844 	char *dskname;
845 
846 	if (!npa->npa_interactive &&
847 	    (npa->npa_ns_state & NVME_NS_STATE_IGNORED) != 0 &&
848 	    verbose == 0)
849 		return (0);
850 
851 	if (npa->npa_ofmt != NULL) {
852 		ofmt_print(npa->npa_ofmt, (void *)npa);
853 		return (0);
854 	}
855 
856 	if (npa->npa_ns_state == NVME_NS_STATE_IGNORED) {
857 		(void) printf("  %s/%s (unallocated)\n", npa->npa_name,
858 		    di_minor_name(npa->npa_minor));
859 	} else {
860 		if ((npa->npa_ns_state & NVME_NS_STATE_ATTACHED) != 0) {
861 			dskname = npa->npa_dsk;
862 		} else if ((npa->npa_ns_state & NVME_NS_STATE_ACTIVE) != 0) {
863 			if ((npa->npa_ns_state & NVME_NS_STATE_IGNORED) != 0) {
864 				dskname = "ignored";
865 			} else {
866 				dskname = "unattached";
867 			}
868 		} else if ((npa->npa_ns_state & NVME_NS_STATE_ALLOCATED) != 0) {
869 			dskname = "inactive";
870 		} else {
871 			dskname = "invalid state";
872 		}
873 		(void) printf("  %s/%s (%s): ", npa->npa_name,
874 		    di_minor_name(npa->npa_minor), dskname);
875 		nvme_print_nsid_summary(npa->npa_idns);
876 	}
877 
878 	return (0);
879 }
880 
881 static int
882 do_list(int fd, const nvme_process_arg_t *npa)
883 {
884 	_NOTE(ARGUNUSED(fd));
885 
886 	nvme_process_arg_t ns_npa = { 0 };
887 	nvmeadm_cmd_t cmd = { 0 };
888 	char *name;
889 
890 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
891 	    di_instance(npa->npa_node)) < 0)
892 		err(-1, "do_list()");
893 
894 	if (npa->npa_ofmt == NULL) {
895 		(void) printf("%s: ", name);
896 		nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version);
897 	}
898 
899 	ns_npa.npa_name = name;
900 	ns_npa.npa_isns = B_TRUE;
901 	ns_npa.npa_nsid = npa->npa_nsid;
902 	cmd = *(npa->npa_cmd);
903 	cmd.c_func = do_list_nsid;
904 	ns_npa.npa_cmd = &cmd;
905 	ns_npa.npa_ofmt = npa->npa_ofmt;
906 	ns_npa.npa_idctl = npa->npa_idctl;
907 
908 	nvme_walk(&ns_npa, npa->npa_node);
909 
910 	free(name);
911 
912 	return (exitcode);
913 }
914 
915 static void
916 optparse_identify_ctrl(nvme_process_arg_t *npa)
917 {
918 	int c;
919 
920 	optind = 0;
921 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacn")) != -1) {
922 		switch (c) {
923 		case 'C':
924 			npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
925 			break;
926 
927 		case 'a':
928 			npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
929 			break;
930 
931 		case 'c':
932 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
933 			break;
934 
935 		case 'n':
936 			npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
937 			break;
938 
939 		case '?':
940 			errx(-1, "unknown option: -%c", optopt);
941 
942 		case ':':
943 			errx(-1, "option -%c requires an argument", optopt);
944 		}
945 	}
946 
947 	npa->npa_argc -= optind;
948 	npa->npa_argv += optind;
949 }
950 
951 static void
952 usage_identify_ctrl(const char *c_name)
953 {
954 	(void) fprintf(stderr, "%s [-C | -c | [-a] -n] <ctl>[,...]\n\n"
955 	    "  Print detailed information about the specified NVMe "
956 	    "controllers.\n", c_name);
957 }
958 
959 static int
960 do_identify_ctrl(int fd, const nvme_process_arg_t *npa)
961 {
962 	boolean_t alloc = B_FALSE;
963 
964 	if (npa->npa_isns)
965 		errx(-1, "identify-controller cannot be used on namespaces");
966 
967 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
968 	    npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
969 		errx(-1, "-C cannot be combined with other flags");
970 	}
971 
972 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
973 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
974 		errx(-1, "-c cannot be combined with other flags");
975 	}
976 
977 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
978 	    npa->npa_cmdflags !=
979 	    (NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) {
980 		errx(-1, "-a can only be used together with -n");
981 	}
982 
983 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
984 		if (!nvme_version_check(npa->npa_version, 1, 2)) {
985 			warnx("%s: -a is not supported on NVMe v%u.%u",
986 			    npa->npa_name, npa->npa_version->v_major,
987 			    npa->npa_version->v_minor);
988 			return (-1);
989 		}
990 
991 		if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) {
992 			warnx("%s: Namespace Management not supported",
993 			    npa->npa_name);
994 			return (-1);
995 		}
996 
997 		alloc = B_TRUE;
998 	}
999 
1000 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) {
1001 		if (!nvme_version_check(npa->npa_version, 1, 2)) {
1002 			warnx("%s: -C is not supported on NVMe v%u.%u",
1003 			    npa->npa_name, npa->npa_version->v_major,
1004 			    npa->npa_version->v_minor);
1005 			return (-1);
1006 		}
1007 
1008 		if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) {
1009 			warnx("%s: Namespace Management not supported",
1010 			    npa->npa_name);
1011 			return (-1);
1012 		}
1013 
1014 		(void) printf("%s: ", npa->npa_name);
1015 		nvme_print_identify_nsid(npa->npa_idns, npa->npa_version);
1016 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) {
1017 		char *caption = "Identify Active Namespace List";
1018 		nvme_identify_nsid_list_t *idnslist;
1019 
1020 		if (!nvme_version_check(npa->npa_version, 1, 1)) {
1021 			warnx("%s: -n is not supported on NVMe v%u.%u",
1022 			    npa->npa_name, npa->npa_version->v_major,
1023 			    npa->npa_version->v_minor);
1024 			return (-1);
1025 		}
1026 
1027 		idnslist = nvme_identify(fd, alloc ?
1028 		    NVME_IDENTIFY_NSID_ALLOC_LIST : NVME_IDENTIFY_NSID_LIST);
1029 
1030 		if (idnslist == NULL)
1031 			return (-1);
1032 
1033 		if (alloc)
1034 			caption = "Identify Allocated Namespace List";
1035 
1036 		(void) printf("%s: ", npa->npa_name);
1037 
1038 		nvme_print_identify_nsid_list(caption, idnslist);
1039 		free(idnslist);
1040 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1041 		nvme_identify_ctrl_list_t *ctlist;
1042 
1043 		if (!nvme_version_check(npa->npa_version, 1, 2)) {
1044 			warnx("%s: -c is not supported on NVMe v%u.%u",
1045 			    npa->npa_name, npa->npa_version->v_major,
1046 			    npa->npa_version->v_minor);
1047 			return (-1);
1048 		}
1049 
1050 		if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) {
1051 			warnx("%s: Namespace Management not supported",
1052 			    npa->npa_name);
1053 			return (-1);
1054 		}
1055 
1056 		ctlist = nvme_identify(fd, NVME_IDENTIFY_CTRL_LIST);
1057 		if (ctlist == NULL)
1058 			return (-1);
1059 
1060 		(void) printf("%s: ", npa->npa_name);
1061 		nvme_print_identify_ctrl_list("Identify Controller List",
1062 		    ctlist);
1063 		free(ctlist);
1064 	} else {
1065 		nvme_capabilities_t *cap;
1066 
1067 		cap = nvme_capabilities(fd);
1068 		if (cap == NULL)
1069 			return (-1);
1070 
1071 		(void) printf("%s: ", npa->npa_name);
1072 		nvme_print_identify_ctrl(npa->npa_idctl, cap, npa->npa_version);
1073 
1074 		free(cap);
1075 	}
1076 
1077 	return (0);
1078 }
1079 
1080 static void
1081 optparse_identify_ns(nvme_process_arg_t *npa)
1082 {
1083 	int c;
1084 
1085 	optind = 0;
1086 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":cd")) != -1) {
1087 		switch (c) {
1088 		case 'c':
1089 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1090 			break;
1091 
1092 		case 'd':
1093 			npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1094 			break;
1095 
1096 		case '?':
1097 			errx(-1, "unknown option: -%c", optopt);
1098 
1099 		case ':':
1100 			errx(-1, "option -%c requires an argument", optopt);
1101 		}
1102 	}
1103 
1104 	npa->npa_argc -= optind;
1105 	npa->npa_argv += optind;
1106 }
1107 
1108 static void
1109 usage_identify_ns(const char *c_name)
1110 {
1111 	(void) fprintf(stderr, "%s [-c | -d ] <ctl>/<ns>[,...]\n\n"
1112 	    "  Print detailed information about the specified NVMe "
1113 	    "namespaces.\n", c_name);
1114 }
1115 
1116 static int
1117 do_identify_ns(int fd, const nvme_process_arg_t *npa)
1118 {
1119 	if (!npa->npa_isns)
1120 		errx(-1, "identify-namespace cannot be used on controllers");
1121 
1122 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1123 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1124 		errx(-1, "-c cannot be combined with other flags");
1125 	}
1126 
1127 	if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1128 	    npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1129 		errx(-1, "-d cannot be combined with other flags");
1130 	}
1131 
1132 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
1133 		errx(-1, "-a cannot be used on namespaces");
1134 	}
1135 
1136 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1137 		nvme_identify_ctrl_list_t *ctlist;
1138 
1139 		if (!nvme_version_check(npa->npa_version, 1, 2)) {
1140 			warnx("%s: -c is not supported on NVMe v%u.%u",
1141 			    npa->npa_name, npa->npa_version->v_major,
1142 			    npa->npa_version->v_minor);
1143 			return (-1);
1144 		}
1145 
1146 		if (npa->npa_idctl->id_oacs.oa_nsmgmt == 0) {
1147 			warnx("%s: Namespace Management not supported",
1148 			    npa->npa_name);
1149 			return (-1);
1150 		}
1151 
1152 		ctlist = nvme_identify(fd, NVME_IDENTIFY_NSID_CTRL_LIST);
1153 		if (ctlist == NULL)
1154 			return (-1);
1155 
1156 		(void) printf("%s/%s: ", npa->npa_name,
1157 		    di_minor_name(npa->npa_minor));
1158 		nvme_print_identify_ctrl_list(
1159 		    "Identify Attached Controller List", ctlist);
1160 		free(ctlist);
1161 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) {
1162 		nvme_identify_nsid_desc_t *nsdesc;
1163 
1164 		if (!nvme_version_check(npa->npa_version, 1, 3)) {
1165 			warnx("%s: -d is not supported on NVMe v%u.%u",
1166 			    npa->npa_name, npa->npa_version->v_major,
1167 			    npa->npa_version->v_minor);
1168 			return (-1);
1169 		}
1170 
1171 		nsdesc = nvme_identify(fd, NVME_IDENTIFY_NSID_DESC);
1172 		if (nsdesc == NULL)
1173 			return (-1);
1174 
1175 		(void) printf("%s/%s: ", npa->npa_name,
1176 		    di_minor_name(npa->npa_minor));
1177 		nvme_print_identify_nsid_desc(nsdesc);
1178 		free(nsdesc);
1179 	} else {
1180 		(void) printf("%s/%s: ", npa->npa_name,
1181 		    di_minor_name(npa->npa_minor));
1182 		nvme_print_identify_nsid(npa->npa_idns, npa->npa_version);
1183 	}
1184 
1185 	return (0);
1186 }
1187 
1188 static void
1189 optparse_identify(nvme_process_arg_t *npa)
1190 {
1191 	int c;
1192 
1193 	optind = 0;
1194 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacdn")) != -1) {
1195 		switch (c) {
1196 		case 'C':
1197 			npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
1198 			break;
1199 
1200 		case 'a':
1201 			npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
1202 			break;
1203 
1204 		case 'c':
1205 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1206 			break;
1207 
1208 		case 'd':
1209 			npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1210 			break;
1211 
1212 		case 'n':
1213 			npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
1214 			break;
1215 
1216 		case '?':
1217 			errx(-1, "unknown option: -%c", optopt);
1218 
1219 		case ':':
1220 			errx(-1, "option -%c requires an argument", optopt);
1221 
1222 		}
1223 	}
1224 
1225 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
1226 	    (npa->npa_cmdflags &
1227 	    ~(NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) != 0) {
1228 		errx(-1, "-a can only be used alone or together with -n");
1229 	}
1230 
1231 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
1232 	    npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
1233 		errx(-1, "-C cannot be combined with other flags");
1234 
1235 	}
1236 
1237 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1238 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1239 		errx(-1, "-c cannot be combined with other flags");
1240 	}
1241 
1242 	if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1243 	    npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1244 		errx(-1, "-d cannot be combined with other flags");
1245 	}
1246 
1247 	npa->npa_argc -= optind;
1248 	npa->npa_argv += optind;
1249 }
1250 
1251 static void
1252 usage_identify(const char *c_name)
1253 {
1254 	(void) fprintf(stderr,
1255 	    "%s [ -C | -c | -d | [-a] -n ] <ctl>[/<ns>][,...]\n\n"
1256 	    "  Print detailed information about the specified NVMe "
1257 	    "controllers and/or name-\n  spaces.\n", c_name);
1258 }
1259 
1260 static int
1261 do_identify(int fd, const nvme_process_arg_t *npa)
1262 {
1263 	if (npa->npa_isns) {
1264 		if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0)
1265 			errx(-1, "-C cannot be used on namespaces");
1266 
1267 		if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0)
1268 			errx(-1, "-a cannot be used on namespaces");
1269 
1270 		if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0)
1271 			errx(-1, "-n cannot be used on namespaces");
1272 
1273 		return (do_identify_ns(fd, npa));
1274 	} else {
1275 		if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0)
1276 			errx(-1, "-d cannot be used on controllers");
1277 
1278 		return (do_identify_ctrl(fd, npa));
1279 	}
1280 }
1281 
1282 static void
1283 usage_get_logpage(const char *c_name)
1284 {
1285 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n"
1286 	    "  Print the specified log page of the specified NVMe "
1287 	    "controllers and/or name-\n  spaces. Supported log pages "
1288 	    "are error, health, and firmware.\n", c_name);
1289 }
1290 
1291 static void
1292 usage_firmware_list(const char *c_name)
1293 {
1294 	(void) fprintf(stderr, "%s <ctl>\n\n"
1295 	    "  Print the log page that contains the list of firmware "
1296 	    "images installed on the specified NVMe controller.\n", c_name);
1297 }
1298 
1299 static int
1300 do_get_logpage_error(int fd, const nvme_process_arg_t *npa)
1301 {
1302 	int nlog = npa->npa_idctl->id_elpe + 1;
1303 	size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog;
1304 	nvme_error_log_entry_t *elog;
1305 
1306 	if (npa->npa_isns)
1307 		errx(-1, "Error Log not available on a per-namespace basis");
1308 
1309 	elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize);
1310 
1311 	if (elog == NULL)
1312 		return (-1);
1313 
1314 	nlog = bufsize / sizeof (nvme_error_log_entry_t);
1315 
1316 	(void) printf("%s: ", npa->npa_name);
1317 	nvme_print_error_log(nlog, elog, npa->npa_version);
1318 
1319 	free(elog);
1320 
1321 	return (0);
1322 }
1323 
1324 static int
1325 do_get_logpage_health(int fd, const nvme_process_arg_t *npa)
1326 {
1327 	size_t bufsize = sizeof (nvme_health_log_t);
1328 	nvme_health_log_t *hlog;
1329 
1330 	if (npa->npa_isns) {
1331 		if (npa->npa_idctl->id_lpa.lp_smart == 0)
1332 			errx(-1, "SMART/Health information not available "
1333 			    "on a per-namespace basis on this controller");
1334 	}
1335 
1336 	hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
1337 
1338 	if (hlog == NULL)
1339 		return (-1);
1340 
1341 	(void) printf("%s: ", npa->npa_name);
1342 	nvme_print_health_log(hlog, npa->npa_idctl, npa->npa_version);
1343 
1344 	free(hlog);
1345 
1346 	return (0);
1347 }
1348 
1349 static int
1350 do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa)
1351 {
1352 	size_t bufsize = sizeof (nvme_fwslot_log_t);
1353 	nvme_fwslot_log_t *fwlog;
1354 
1355 	if (npa->npa_isns)
1356 		errx(-1, "Firmware Slot information not available on a "
1357 		    "per-namespace basis");
1358 
1359 	fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize);
1360 
1361 	if (fwlog == NULL)
1362 		return (-1);
1363 
1364 	(void) printf("%s: ", npa->npa_name);
1365 	nvme_print_fwslot_log(fwlog, npa->npa_idctl);
1366 
1367 	free(fwlog);
1368 
1369 	return (0);
1370 }
1371 
1372 static int
1373 do_get_logpage(int fd, const nvme_process_arg_t *npa)
1374 {
1375 	int ret = 0;
1376 	int (*func)(int, const nvme_process_arg_t *);
1377 
1378 	if (npa->npa_argc < 1) {
1379 		warnx("missing logpage name");
1380 		usage(npa->npa_cmd);
1381 		exit(-1);
1382 	}
1383 
1384 	if (strcmp(npa->npa_argv[0], "error") == 0)
1385 		func = do_get_logpage_error;
1386 	else if (strcmp(npa->npa_argv[0], "health") == 0)
1387 		func = do_get_logpage_health;
1388 	else if (strcmp(npa->npa_argv[0], "firmware") == 0)
1389 		func = do_get_logpage_fwslot;
1390 	else
1391 		errx(-1, "invalid log page: %s", npa->npa_argv[0]);
1392 
1393 	if (npa->npa_isns &&
1394 	    (npa->npa_ns_state & NVME_NS_STATE_ACTIVE) == 0)
1395 		errx(-1, "cannot get logpage: namespace is inactive");
1396 
1397 	ret = func(fd, npa);
1398 	return (ret);
1399 }
1400 
1401 static void
1402 usage_get_features(const char *c_name)
1403 {
1404 	const nvme_feature_t *feat;
1405 
1406 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
1407 	    "  Print the specified features of the specified NVMe controllers "
1408 	    "and/or\n  namespaces. Supported features are:\n\n", c_name);
1409 	(void) fprintf(stderr, "    %-35s %-14s %s\n",
1410 	    "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE");
1411 	for (feat = &features[0]; feat->f_feature != 0; feat++) {
1412 		char *type;
1413 
1414 		if ((feat->f_getflags & NVMEADM_F_BOTH) == NVMEADM_F_BOTH)
1415 			type = "both";
1416 		else if ((feat->f_getflags & NVMEADM_F_CTRL) != 0)
1417 			type = "controller only";
1418 		else
1419 			type = "namespace only";
1420 
1421 		(void) fprintf(stderr, "    %-35s %-14s %s\n",
1422 		    feat->f_name, feat->f_short, type);
1423 	}
1424 
1425 }
1426 
1427 static int
1428 do_get_feat_common(int fd, const nvme_feature_t *feat,
1429     const nvme_process_arg_t *npa)
1430 {
1431 	void *buf = NULL;
1432 	size_t bufsize = feat->f_bufsize;
1433 	uint64_t res;
1434 
1435 	if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf)
1436 	    == B_FALSE)
1437 		return (EINVAL);
1438 
1439 	nvme_print(2, feat->f_name, -1, NULL);
1440 	feat->f_print(res, buf, bufsize, npa->npa_idctl, npa->npa_version);
1441 	free(buf);
1442 
1443 	return (0);
1444 }
1445 
1446 static int
1447 do_get_feat_temp_thresh_one(int fd, const nvme_feature_t *feat,
1448     const char *label, uint16_t tmpsel, uint16_t thsel,
1449     const nvme_process_arg_t *npa)
1450 {
1451 	uint64_t res;
1452 	void *buf = NULL;
1453 	size_t bufsize = feat->f_bufsize;
1454 	nvme_temp_threshold_t tt;
1455 
1456 	tt.r = 0;
1457 	tt.b.tt_tmpsel = tmpsel;
1458 	tt.b.tt_thsel = thsel;
1459 
1460 	if (!nvme_get_feature(fd, feat->f_feature, tt.r, &res, &bufsize,
1461 	    &buf)) {
1462 		return (EINVAL);
1463 	}
1464 
1465 	feat->f_print(res, (void *)label, 0, npa->npa_idctl, npa->npa_version);
1466 	free(buf);
1467 	return (0);
1468 }
1469 
1470 /*
1471  * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the
1472  * device and changed the main device to have a composite temperature sensor. As
1473  * a result, there is a set of thresholds for each sensor. In addition, they
1474  * added both an over-temperature and under-temperature threshold. Since most
1475  * devices don't actually implement all the sensors, we get the health page and
1476  * see which sensors have a non-zero value to determine how to proceed.
1477  */
1478 static int
1479 do_get_feat_temp_thresh(int fd, const nvme_feature_t *feat,
1480     const nvme_process_arg_t *npa)
1481 {
1482 	int ret;
1483 	size_t bufsize = sizeof (nvme_health_log_t);
1484 	nvme_health_log_t *hlog;
1485 
1486 	nvme_print(2, feat->f_name, -1, NULL);
1487 	if ((ret = do_get_feat_temp_thresh_one(fd, feat,
1488 	    "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER,
1489 	    npa)) != 0) {
1490 		return (ret);
1491 	}
1492 
1493 	if (!nvme_version_check(npa->npa_version, 1, 2)) {
1494 		return (0);
1495 	}
1496 
1497 	if ((ret = do_get_feat_temp_thresh_one(fd, feat,
1498 	    "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER,
1499 	    npa)) != 0) {
1500 		return (ret);
1501 	}
1502 
1503 	hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
1504 	if (hlog == NULL) {
1505 		warnx("failed to get health log page, unable to get "
1506 		    "thresholds for additional sensors");
1507 		return (0);
1508 	}
1509 
1510 	if (hlog->hl_temp_sensor_1 != 0) {
1511 		(void) do_get_feat_temp_thresh_one(fd, feat,
1512 		    "Temp. Sensor 1 Over Temp. Threshold", 1,
1513 		    NVME_TEMP_THRESH_OVER, npa);
1514 		(void) do_get_feat_temp_thresh_one(fd, feat,
1515 		    "Temp. Sensor 1 Under Temp. Threshold", 1,
1516 		    NVME_TEMP_THRESH_UNDER, npa);
1517 	}
1518 
1519 	if (hlog->hl_temp_sensor_2 != 0) {
1520 		(void) do_get_feat_temp_thresh_one(fd, feat,
1521 		    "Temp. Sensor 2 Over Temp. Threshold", 2,
1522 		    NVME_TEMP_THRESH_OVER, npa);
1523 		(void) do_get_feat_temp_thresh_one(fd, feat,
1524 		    "Temp. Sensor 2 Under Temp. Threshold", 2,
1525 		    NVME_TEMP_THRESH_UNDER, npa);
1526 	}
1527 
1528 	if (hlog->hl_temp_sensor_3 != 0) {
1529 		(void) do_get_feat_temp_thresh_one(fd, feat,
1530 		    "Temp. Sensor 3 Over Temp. Threshold", 3,
1531 		    NVME_TEMP_THRESH_OVER, npa);
1532 		(void) do_get_feat_temp_thresh_one(fd, feat,
1533 		    "Temp. Sensor 3 Under Temp. Threshold", 3,
1534 		    NVME_TEMP_THRESH_UNDER, npa);
1535 	}
1536 
1537 	if (hlog->hl_temp_sensor_4 != 0) {
1538 		(void) do_get_feat_temp_thresh_one(fd, feat,
1539 		    "Temp. Sensor 4 Over Temp. Threshold", 4,
1540 		    NVME_TEMP_THRESH_OVER, npa);
1541 		(void) do_get_feat_temp_thresh_one(fd, feat,
1542 		    "Temp. Sensor 4 Under Temp. Threshold", 4,
1543 		    NVME_TEMP_THRESH_UNDER, npa);
1544 	}
1545 
1546 	if (hlog->hl_temp_sensor_5 != 0) {
1547 		(void) do_get_feat_temp_thresh_one(fd, feat,
1548 		    "Temp. Sensor 5 Over Temp. Threshold", 5,
1549 		    NVME_TEMP_THRESH_OVER, npa);
1550 		(void) do_get_feat_temp_thresh_one(fd, feat,
1551 		    "Temp. Sensor 5 Under Temp. Threshold", 5,
1552 		    NVME_TEMP_THRESH_UNDER, npa);
1553 	}
1554 
1555 	if (hlog->hl_temp_sensor_6 != 0) {
1556 		(void) do_get_feat_temp_thresh_one(fd, feat,
1557 		    "Temp. Sensor 6 Over Temp. Threshold", 6,
1558 		    NVME_TEMP_THRESH_OVER, npa);
1559 		(void) do_get_feat_temp_thresh_one(fd, feat,
1560 		    "Temp. Sensor 6 Under Temp. Threshold", 6,
1561 		    NVME_TEMP_THRESH_UNDER, npa);
1562 	}
1563 
1564 	if (hlog->hl_temp_sensor_7 != 0) {
1565 		(void) do_get_feat_temp_thresh_one(fd, feat,
1566 		    "Temp. Sensor 7 Over Temp. Threshold", 7,
1567 		    NVME_TEMP_THRESH_OVER, npa);
1568 		(void) do_get_feat_temp_thresh_one(fd, feat,
1569 		    "Temp. Sensor 7 Under Temp. Threshold", 7,
1570 		    NVME_TEMP_THRESH_UNDER, npa);
1571 	}
1572 
1573 	if (hlog->hl_temp_sensor_8 != 0) {
1574 		(void) do_get_feat_temp_thresh_one(fd, feat,
1575 		    "Temp. Sensor 8 Over Temp. Threshold", 8,
1576 		    NVME_TEMP_THRESH_OVER, npa);
1577 		(void) do_get_feat_temp_thresh_one(fd, feat,
1578 		    "Temp. Sensor 8 Under Temp. Threshold", 8,
1579 		    NVME_TEMP_THRESH_UNDER, npa);
1580 	}
1581 	free(hlog);
1582 	return (0);
1583 }
1584 
1585 static int
1586 do_get_feat_intr_vect(int fd, const nvme_feature_t *feat,
1587     const nvme_process_arg_t *npa)
1588 {
1589 	uint64_t res;
1590 	uint64_t arg;
1591 	int intr_cnt;
1592 
1593 	intr_cnt = nvme_intr_cnt(fd);
1594 
1595 	if (intr_cnt == -1)
1596 		return (EINVAL);
1597 
1598 	nvme_print(2, feat->f_name, -1, NULL);
1599 
1600 	for (arg = 0; arg < intr_cnt; arg++) {
1601 		if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL)
1602 		    == B_FALSE)
1603 			return (EINVAL);
1604 
1605 		feat->f_print(res, NULL, 0, npa->npa_idctl, npa->npa_version);
1606 	}
1607 
1608 	return (0);
1609 }
1610 
1611 static int
1612 do_get_features(int fd, const nvme_process_arg_t *npa)
1613 {
1614 	const nvme_feature_t *feat;
1615 	char *f, *flist, *lasts;
1616 	boolean_t header_printed = B_FALSE;
1617 
1618 	if (npa->npa_argc > 1)
1619 		errx(-1, "unexpected arguments");
1620 
1621 	if (npa->npa_isns &&
1622 	    (npa->npa_ns_state & NVME_NS_STATE_ACTIVE) == 0)
1623 		errx(-1, "cannot get feature: namespace is inactive");
1624 
1625 	/*
1626 	 * No feature list given, print all supported features.
1627 	 */
1628 	if (npa->npa_argc == 0) {
1629 		(void) printf("%s: Get Features\n", npa->npa_name);
1630 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
1631 			if ((npa->npa_isns &&
1632 			    (feat->f_getflags & NVMEADM_F_NS) == 0) ||
1633 			    (!npa->npa_isns &&
1634 			    (feat->f_getflags & NVMEADM_F_CTRL) == 0))
1635 				continue;
1636 
1637 			(void) feat->f_get(fd, feat, npa);
1638 		}
1639 
1640 		return (0);
1641 	}
1642 
1643 	/*
1644 	 * Process feature list.
1645 	 */
1646 	flist = strdup(npa->npa_argv[0]);
1647 	if (flist == NULL)
1648 		err(-1, "do_get_features");
1649 
1650 	for (f = strtok_r(flist, ",", &lasts);
1651 	    f != NULL;
1652 	    f = strtok_r(NULL, ",", &lasts)) {
1653 		while (isspace(*f))
1654 			f++;
1655 
1656 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
1657 			if (strncasecmp(feat->f_name, f, strlen(f)) == 0 ||
1658 			    strncasecmp(feat->f_short, f, strlen(f)) == 0)
1659 				break;
1660 		}
1661 
1662 		if (feat->f_feature == 0) {
1663 			warnx("unknown feature %s", f);
1664 			continue;
1665 		}
1666 
1667 		if ((npa->npa_isns &&
1668 		    (feat->f_getflags & NVMEADM_F_NS) == 0) ||
1669 		    (!npa->npa_isns &&
1670 		    (feat->f_getflags & NVMEADM_F_CTRL) == 0)) {
1671 			warnx("feature %s %s supported for namespaces",
1672 			    feat->f_name,
1673 			    (feat->f_getflags & NVMEADM_F_NS) != 0 ?
1674 			    "only" : "not");
1675 			continue;
1676 		}
1677 
1678 		if (!header_printed) {
1679 			(void) printf("%s: Get Features\n", npa->npa_name);
1680 			header_printed = B_TRUE;
1681 		}
1682 
1683 		if (feat->f_get(fd, feat, npa) != 0) {
1684 			warnx("unsupported feature: %s", feat->f_name);
1685 			continue;
1686 		}
1687 	}
1688 
1689 	free(flist);
1690 	return (0);
1691 }
1692 
1693 static int
1694 do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf,
1695     unsigned long ses)
1696 {
1697 	nvme_process_arg_t ns_npa = { 0 };
1698 	nvmeadm_cmd_t cmd = { 0 };
1699 
1700 	if (npa->npa_isns &&
1701 	    (npa->npa_ns_state & NVME_NS_STATE_ACTIVE) == 0) {
1702 		errx(-1, "cannot %s: namespace is inactive",
1703 		    npa->npa_cmd->c_name);
1704 	}
1705 
1706 	cmd = *(npa->npa_cmd);
1707 	cmd.c_func = do_attach_detach;
1708 	cmd.c_name = "detach";
1709 	ns_npa = *npa;
1710 	ns_npa.npa_cmd = &cmd;
1711 
1712 	if (do_attach_detach(fd, &ns_npa) != 0)
1713 		return (exitcode);
1714 	if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) {
1715 		warn("%s failed", npa->npa_cmd->c_name);
1716 		exitcode += -1;
1717 	}
1718 	cmd.c_name = "attach";
1719 	exitcode += do_attach_detach(fd, &ns_npa);
1720 
1721 	return (exitcode);
1722 }
1723 
1724 static void
1725 usage_format(const char *c_name)
1726 {
1727 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
1728 	    "  Format one or all namespaces of the specified NVMe "
1729 	    "controller. Supported LBA\n  formats can be queried with "
1730 	    "the \"%s identify\" command on the namespace\n  to be "
1731 	    "formatted.\n", c_name, getprogname());
1732 }
1733 
1734 static int
1735 do_format(int fd, const nvme_process_arg_t *npa)
1736 {
1737 	unsigned long lbaf;
1738 
1739 	if (npa->npa_idctl->id_oacs.oa_format == 0)
1740 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
1741 
1742 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0)
1743 		errx(-1, "%s not supported on individual namespace",
1744 		    npa->npa_cmd->c_name);
1745 
1746 
1747 	if (npa->npa_argc > 0) {
1748 		errno = 0;
1749 		lbaf = strtoul(npa->npa_argv[0], NULL, 10);
1750 
1751 		if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF)
1752 			errx(-1, "invalid LBA format %d", lbaf + 1);
1753 
1754 		if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0)
1755 			errx(-1, "LBA formats with metadata not supported");
1756 	} else {
1757 		lbaf = npa->npa_idns->id_flbas.lba_format;
1758 	}
1759 
1760 	return (do_format_common(fd, npa, lbaf, 0));
1761 }
1762 
1763 static void
1764 usage_secure_erase(const char *c_name)
1765 {
1766 	(void) fprintf(stderr, "%s [-c] <ctl>[/<ns>]\n\n"
1767 	    "  Secure-Erase one or all namespaces of the specified "
1768 	    "NVMe controller.\n", c_name);
1769 }
1770 
1771 static void
1772 optparse_secure_erase(nvme_process_arg_t *npa)
1773 {
1774 	int c;
1775 
1776 	optind = 0;
1777 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":c")) != -1) {
1778 		switch (c) {
1779 		case 'c':
1780 			npa->npa_cmdflags |= NVMEADM_O_SE_CRYPTO;
1781 			break;
1782 
1783 		case '?':
1784 			errx(-1, "unknown option: -%c", optopt);
1785 
1786 		case ':':
1787 			errx(-1, "option -%c requires an argument", optopt);
1788 
1789 		}
1790 	}
1791 
1792 	npa->npa_argc -= optind;
1793 	npa->npa_argv += optind;
1794 }
1795 
1796 static int
1797 do_secure_erase(int fd, const nvme_process_arg_t *npa)
1798 {
1799 	unsigned long lbaf;
1800 	uint8_t ses = NVME_FRMT_SES_USER;
1801 
1802 	if (npa->npa_argc > 0)
1803 		errx(-1, "Too many arguments");
1804 
1805 	if (npa->npa_idctl->id_oacs.oa_format == 0)
1806 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
1807 
1808 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0)
1809 		errx(-1, "%s not supported on individual namespace",
1810 		    npa->npa_cmd->c_name);
1811 
1812 	if ((npa->npa_cmdflags & NVMEADM_O_SE_CRYPTO) != 0)
1813 		ses = NVME_FRMT_SES_CRYPTO;
1814 
1815 	if (ses == NVME_FRMT_SES_CRYPTO &&
1816 	    npa->npa_idctl->id_fna.fn_crypt_erase == 0)
1817 		errx(-1, "cryptographic %s not supported",
1818 		    npa->npa_cmd->c_name);
1819 
1820 	lbaf = npa->npa_idns->id_flbas.lba_format;
1821 
1822 	return (do_format_common(fd, npa, lbaf, ses));
1823 }
1824 
1825 static void
1826 usage_attach_detach(const char *c_name)
1827 {
1828 	(void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
1829 	    "  %c%s blkdev(4D) %s one or all namespaces of the "
1830 	    "specified NVMe controller.\n",
1831 	    c_name, toupper(c_name[0]), &c_name[1],
1832 	    c_name[0] == 'd' ? "from" : "to");
1833 }
1834 
1835 static int
1836 do_attach_detach(int fd, const nvme_process_arg_t *npa)
1837 {
1838 	char *c_name = npa->npa_cmd->c_name;
1839 
1840 	if (!npa->npa_isns) {
1841 		nvme_process_arg_t ns_npa = { 0 };
1842 
1843 		ns_npa.npa_name = npa->npa_name;
1844 		ns_npa.npa_isns = B_TRUE;
1845 		ns_npa.npa_cmd = npa->npa_cmd;
1846 		ns_npa.npa_excl = npa->npa_excl;
1847 
1848 		nvme_walk(&ns_npa, npa->npa_node);
1849 
1850 		return (exitcode);
1851 	}
1852 
1853 	/*
1854 	 * Unless the user interactively requested a particular namespace to be
1855 	 * attached or detached, don't even try to attach or detach namespaces
1856 	 * that are ignored by the driver, thereby avoiding printing pointless
1857 	 * error messages.
1858 	 */
1859 	if (!npa->npa_interactive &&
1860 	    (npa->npa_ns_state & NVME_NS_STATE_IGNORED))
1861 		return (0);
1862 
1863 	if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd)
1864 	    == B_FALSE) {
1865 		warn("%s failed", c_name);
1866 		return (-1);
1867 	}
1868 
1869 	return (0);
1870 }
1871 
1872 static void
1873 usage_firmware_load(const char *c_name)
1874 {
1875 	(void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n"
1876 	    "  Load firmware <image file> to offset <offset>.\n"
1877 	    "  The firmware needs to be committed to a slot using "
1878 	    "\"nvmeadm commit-firmware\"\n  command.\n", c_name);
1879 }
1880 
1881 /*
1882  * Read exactly len bytes, or until eof.
1883  */
1884 static ssize_t
1885 read_block(int fd, char *buf, size_t len)
1886 {
1887 	size_t remain;
1888 	ssize_t bytes;
1889 
1890 	remain = len;
1891 	while (remain > 0) {
1892 		bytes = read(fd, buf, remain);
1893 		if (bytes == 0)
1894 			break;
1895 
1896 		if (bytes < 0) {
1897 			if (errno == EINTR)
1898 				continue;
1899 
1900 			return (-1);
1901 		}
1902 
1903 		buf += bytes;
1904 		remain -= bytes;
1905 	}
1906 
1907 	return (len - remain);
1908 }
1909 
1910 /*
1911  * Convert a string to a valid firmware upload offset (in bytes).
1912  */
1913 static offset_t
1914 get_fw_offsetb(char *str)
1915 {
1916 	longlong_t offsetb;
1917 	char *valend;
1918 
1919 	errno = 0;
1920 	offsetb = strtoll(str, &valend, 0);
1921 	if (errno != 0 || *valend != '\0' || offsetb < 0 ||
1922 	    offsetb > NVME_FW_OFFSETB_MAX)
1923 		errx(-1, "Offset must be numeric and in the range of 0 to %llu",
1924 		    NVME_FW_OFFSETB_MAX);
1925 
1926 	if ((offsetb & NVME_DWORD_MASK) != 0)
1927 		errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE);
1928 
1929 	return ((offset_t)offsetb);
1930 }
1931 
1932 #define	FIRMWARE_READ_BLKSIZE	(64 * 1024)		/* 64K */
1933 
1934 static int
1935 do_firmware_load(int fd, const nvme_process_arg_t *npa)
1936 {
1937 	int fw_fd;
1938 	ssize_t len;
1939 	offset_t offset = 0;
1940 	size_t size;
1941 	uint16_t sc;
1942 	char buf[FIRMWARE_READ_BLKSIZE];
1943 
1944 	if (npa->npa_argc > 2)
1945 		errx(-1, "Too many arguments");
1946 
1947 	if (npa->npa_argc == 0)
1948 		errx(-1, "Requires firmware file name, and an "
1949 		    "optional offset");
1950 
1951 	if (npa->npa_isns)
1952 		errx(-1, "Firmware loading not available on a per-namespace "
1953 		    "basis");
1954 
1955 	if (npa->npa_argc == 2)
1956 		offset = get_fw_offsetb(npa->npa_argv[1]);
1957 
1958 	fw_fd = open(npa->npa_argv[0], O_RDONLY);
1959 	if (fw_fd < 0)
1960 		errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0],
1961 		    strerror(errno));
1962 
1963 	size = 0;
1964 	do {
1965 		len = read_block(fw_fd, buf, sizeof (buf));
1966 
1967 		if (len < 0)
1968 			errx(-1, "Error reading \"%s\": %s", npa->npa_argv[0],
1969 			    strerror(errno));
1970 
1971 		if (len == 0)
1972 			break;
1973 
1974 		if (!nvme_firmware_load(fd, buf, len, offset, &sc))
1975 			errx(-1, "Error loading \"%s\": %s", npa->npa_argv[0],
1976 			    nvme_fw_error(errno, sc));
1977 
1978 		offset += len;
1979 		size += len;
1980 	} while (len == sizeof (buf));
1981 
1982 	(void) close(fw_fd);
1983 
1984 	if (verbose)
1985 		(void) printf("%zu bytes downloaded.\n", size);
1986 
1987 	return (0);
1988 }
1989 
1990 /*
1991  * Convert str to a valid firmware slot number.
1992  */
1993 static uint_t
1994 get_slot_number(char *str)
1995 {
1996 	longlong_t slot;
1997 	char *valend;
1998 
1999 	errno = 0;
2000 	slot = strtoll(str, &valend, 0);
2001 	if (errno != 0 || *valend != '\0' ||
2002 	    slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX)
2003 		errx(-1, "Slot must be numeric and in the range of %d to %d",
2004 		    NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX);
2005 
2006 	return ((uint_t)slot);
2007 }
2008 
2009 static void
2010 usage_firmware_commit(const char *c_name)
2011 {
2012 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
2013 	    "  Commit previously downloaded firmware to slot <slot>.\n"
2014 	    "  The firmware is only activated after a "
2015 	    "\"nvmeadm activate-firmware\" command.\n", c_name);
2016 }
2017 
2018 static int
2019 do_firmware_commit(int fd, const nvme_process_arg_t *npa)
2020 {
2021 	uint_t slot;
2022 	uint16_t sc;
2023 
2024 	if (npa->npa_argc > 1)
2025 		errx(-1, "Too many arguments");
2026 
2027 	if (npa->npa_argc == 0)
2028 		errx(-1, "Firmware slot number is required");
2029 
2030 	if (npa->npa_isns)
2031 		errx(-1, "Firmware committing not available on a per-namespace "
2032 		    "basis");
2033 
2034 	slot = get_slot_number(npa->npa_argv[0]);
2035 
2036 	if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly)
2037 		errx(-1, "Cannot commit firmware to slot 1: slot is read-only");
2038 
2039 	if (!nvme_firmware_commit(fd, slot, NVME_FWC_SAVE, &sc))
2040 		errx(-1, "Failed to commit firmware to slot %u: %s",
2041 		    slot, nvme_fw_error(errno, sc));
2042 
2043 	if (verbose)
2044 		(void) printf("Firmware committed to slot %u.\n", slot);
2045 
2046 	return (0);
2047 }
2048 
2049 static void
2050 usage_firmware_activate(const char *c_name)
2051 {
2052 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
2053 	    "  Activate firmware in slot <slot>.\n"
2054 	    "  The firmware will be in use after the next system reset.\n",
2055 	    c_name);
2056 }
2057 
2058 static int
2059 do_firmware_activate(int fd, const nvme_process_arg_t *npa)
2060 {
2061 	uint_t slot;
2062 	uint16_t sc;
2063 
2064 	if (npa->npa_argc > 1)
2065 		errx(-1, "Too many arguments");
2066 
2067 	if (npa->npa_argc == 0)
2068 		errx(-1, "Firmware slot number is required");
2069 
2070 	if (npa->npa_isns)
2071 		errx(-1, "Firmware activation not available on a per-namespace "
2072 		    "basis");
2073 
2074 	slot = get_slot_number(npa->npa_argv[0]);
2075 
2076 	if (!nvme_firmware_commit(fd, slot, NVME_FWC_ACTIVATE, &sc))
2077 		errx(-1, "Failed to activate slot %u: %s", slot,
2078 		    nvme_fw_error(errno, sc));
2079 
2080 	if (verbose)
2081 		printf("Slot %u activated: %s.\n", slot,
2082 		    nvme_fw_error(errno, sc));
2083 
2084 	return (0);
2085 }
2086 
2087 /*
2088  * While the NVME_VERSION_ATLEAST macro exists, specifying a version of 1.0
2089  * causes GCC to helpfully flag the -Wtype-limits warning because a uint_t is
2090  * always >= 0. In many cases it's useful to always indicate what version
2091  * something was added in to simplify code (e.g. nvmeadm_print_bit) and we'd
2092  * rather just say it's version 1.0 rather than making folks realize that a
2093  * hardcoded true is equivalent. Therefore we have this function which can't
2094  * trigger this warning today (and adds a minor amount of type safety). If GCC
2095  * or clang get smart enough to see through this, then we'll have to just
2096  * disable the warning for the single minor comparison (and reformat this a bit
2097  * to minimize the impact).
2098  */
2099 boolean_t
2100 nvme_version_check(nvme_version_t *vers, uint_t major, uint_t minor)
2101 {
2102 	if (vers->v_major > major) {
2103 		return (B_TRUE);
2104 	}
2105 
2106 	return (vers->v_major == major && vers->v_minor >= minor);
2107 }
2108