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