xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm.c (revision 29219719c034367724cbf77434175b3c4e681e43)
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 <unistd.h>
39 #include <fcntl.h>
40 #include <strings.h>
41 #include <ctype.h>
42 #include <err.h>
43 #include <sys/sunddi.h>
44 #include <libdevinfo.h>
45 
46 #include <sys/nvme.h>
47 
48 #include "nvmeadm.h"
49 
50 struct nvme_feature {
51 	char *f_name;
52 	char *f_short;
53 	uint8_t f_feature;
54 	size_t f_bufsize;
55 	uint_t f_getflags;
56 	int (*f_get)(int, const nvme_feature_t *, const nvme_process_arg_t *);
57 	void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *,
58 	    nvme_version_t *);
59 };
60 
61 #define	NVMEADM_F_CTRL	1
62 #define	NVMEADM_F_NS	2
63 #define	NVMEADM_F_BOTH	(NVMEADM_F_CTRL | NVMEADM_F_NS)
64 
65 #define	NVMEADM_C_MULTI	1
66 #define	NVMEADM_C_EXCL	2
67 
68 struct nvmeadm_cmd {
69 	char *c_name;
70 	const char *c_desc;
71 	const char *c_flagdesc;
72 	int (*c_func)(int, const nvme_process_arg_t *);
73 	void (*c_usage)(const char *);
74 	void (*c_optparse)(nvme_process_arg_t *);
75 	int c_flags;
76 };
77 
78 
79 static void usage(const nvmeadm_cmd_t *);
80 static void nvme_walk(nvme_process_arg_t *, di_node_t);
81 static boolean_t nvme_match(nvme_process_arg_t *);
82 
83 static int nvme_process(di_node_t, di_minor_t, void *);
84 
85 static int do_list(int, const nvme_process_arg_t *);
86 static int do_identify(int, const nvme_process_arg_t *);
87 static int do_get_logpage_error(int, const nvme_process_arg_t *);
88 static int do_get_logpage_health(int, const nvme_process_arg_t *);
89 static int do_get_logpage_fwslot(int, const nvme_process_arg_t *);
90 static int do_get_logpage(int, const nvme_process_arg_t *);
91 static int do_get_feat_common(int, const nvme_feature_t *,
92     const nvme_process_arg_t *);
93 static int do_get_feat_intr_vect(int, const nvme_feature_t *,
94     const nvme_process_arg_t *);
95 static int do_get_feat_temp_thresh(int, const nvme_feature_t *,
96     const nvme_process_arg_t *);
97 static int do_get_features(int, const nvme_process_arg_t *);
98 static int do_format(int, const nvme_process_arg_t *);
99 static int do_secure_erase(int, const nvme_process_arg_t *);
100 static int do_attach_detach(int, const nvme_process_arg_t *);
101 static int do_firmware_load(int, const nvme_process_arg_t *);
102 static int do_firmware_commit(int, const nvme_process_arg_t *);
103 static int do_firmware_activate(int, const nvme_process_arg_t *);
104 
105 static void optparse_list(nvme_process_arg_t *);
106 
107 static void usage_list(const char *);
108 static void usage_identify(const char *);
109 static void usage_get_logpage(const char *);
110 static void usage_get_features(const char *);
111 static void usage_format(const char *);
112 static void usage_secure_erase(const char *);
113 static void usage_attach_detach(const char *);
114 static void usage_firmware_list(const char *);
115 static void usage_firmware_load(const char *);
116 static void usage_firmware_commit(const char *);
117 static void usage_firmware_activate(const char *);
118 
119 int verbose;
120 int debug;
121 static int exitcode;
122 
123 static const nvmeadm_cmd_t nvmeadm_cmds[] = {
124 	{
125 		"list",
126 		"list controllers and namespaces",
127 		"  -p\t\tprint parsable output\n"
128 		    "  -o field\tselect a field for parsable output\n",
129 		do_list, usage_list, optparse_list,
130 		NVMEADM_C_MULTI
131 	},
132 	{
133 		"identify",
134 		"identify controllers and/or namespaces",
135 		NULL,
136 		do_identify, usage_identify, NULL,
137 		NVMEADM_C_MULTI
138 	},
139 	{
140 		"get-logpage",
141 		"get a log page from controllers and/or namespaces",
142 		NULL,
143 		do_get_logpage, usage_get_logpage, NULL,
144 		NVMEADM_C_MULTI
145 	},
146 	{
147 		"get-features",
148 		"get features from controllers and/or namespaces",
149 		NULL,
150 		do_get_features, usage_get_features, NULL,
151 		NVMEADM_C_MULTI
152 	},
153 	{
154 		"format",
155 		"format namespace(s) of a controller",
156 		NULL,
157 		do_format, usage_format, NULL,
158 		NVMEADM_C_EXCL
159 	},
160 	{
161 		"secure-erase",
162 		"secure erase namespace(s) of a controller",
163 		"  -c  Do a cryptographic erase.",
164 		do_secure_erase, usage_secure_erase, NULL,
165 		NVMEADM_C_EXCL
166 	},
167 	{
168 		"detach",
169 		"detach blkdev(4D) from namespace(s) of a controller",
170 		NULL,
171 		do_attach_detach, usage_attach_detach, NULL,
172 		NVMEADM_C_EXCL
173 	},
174 	{
175 		"attach",
176 		"attach blkdev(4D) to namespace(s) of a controller",
177 		NULL,
178 		do_attach_detach, usage_attach_detach, NULL,
179 		NVMEADM_C_EXCL
180 	},
181 	{
182 		"list-firmware",
183 		"list firmware on a controller",
184 		NULL,
185 		do_get_logpage_fwslot, usage_firmware_list, NULL,
186 		0
187 	},
188 	{
189 		"load-firmware",
190 		"load firmware to a controller",
191 		NULL,
192 		do_firmware_load, usage_firmware_load, NULL,
193 		0
194 	},
195 	{
196 		"commit-firmware",
197 		"commit downloaded firmware to a slot of a controller",
198 		NULL,
199 		do_firmware_commit, usage_firmware_commit, NULL,
200 		0
201 	},
202 	{
203 		"activate-firmware",
204 		"activate a firmware slot of a controller",
205 		NULL,
206 		do_firmware_activate, usage_firmware_activate, NULL,
207 		0
208 	},
209 	{
210 		NULL, NULL, NULL,
211 		NULL, NULL, NULL, 0
212 	}
213 };
214 
215 static const nvme_feature_t features[] = {
216 	{ "Arbitration", "",
217 	    NVME_FEAT_ARBITRATION, 0, NVMEADM_F_CTRL,
218 	    do_get_feat_common, nvme_print_feat_arbitration },
219 	{ "Power Management", "",
220 	    NVME_FEAT_POWER_MGMT, 0, NVMEADM_F_CTRL,
221 	    do_get_feat_common, nvme_print_feat_power_mgmt },
222 	{ "LBA Range Type", "range",
223 	    NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_F_NS,
224 	    do_get_feat_common, nvme_print_feat_lba_range },
225 	{ "Temperature Threshold", "",
226 	    NVME_FEAT_TEMPERATURE, 0, NVMEADM_F_CTRL,
227 	    do_get_feat_temp_thresh, nvme_print_feat_temperature },
228 	{ "Error Recovery", "",
229 	    NVME_FEAT_ERROR, 0, NVMEADM_F_CTRL,
230 	    do_get_feat_common, nvme_print_feat_error },
231 	{ "Volatile Write Cache", "cache",
232 	    NVME_FEAT_WRITE_CACHE, 0, NVMEADM_F_CTRL,
233 	    do_get_feat_common, nvme_print_feat_write_cache },
234 	{ "Number of Queues", "queues",
235 	    NVME_FEAT_NQUEUES, 0, NVMEADM_F_CTRL,
236 	    do_get_feat_common, nvme_print_feat_nqueues },
237 	{ "Interrupt Coalescing", "coalescing",
238 	    NVME_FEAT_INTR_COAL, 0, NVMEADM_F_CTRL,
239 	    do_get_feat_common, nvme_print_feat_intr_coal },
240 	{ "Interrupt Vector Configuration", "vector",
241 	    NVME_FEAT_INTR_VECT, 0, NVMEADM_F_CTRL,
242 	    do_get_feat_intr_vect, nvme_print_feat_intr_vect },
243 	{ "Write Atomicity", "atomicity",
244 	    NVME_FEAT_WRITE_ATOM, 0, NVMEADM_F_CTRL,
245 	    do_get_feat_common, nvme_print_feat_write_atom },
246 	{ "Asynchronous Event Configuration", "event",
247 	    NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_F_CTRL,
248 	    do_get_feat_common, nvme_print_feat_async_event },
249 	{ "Autonomous Power State Transition", "",
250 	    NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_F_CTRL,
251 	    do_get_feat_common, nvme_print_feat_auto_pst },
252 	{ "Software Progress Marker", "progress",
253 	    NVME_FEAT_PROGRESS, 0, NVMEADM_F_CTRL,
254 	    do_get_feat_common, nvme_print_feat_progress },
255 	{ NULL, NULL, 0, 0, B_FALSE, NULL }
256 };
257 
258 
259 int
260 main(int argc, char **argv)
261 {
262 	int c;
263 	const nvmeadm_cmd_t *cmd;
264 	di_node_t node;
265 	nvme_process_arg_t npa = { 0 };
266 	int help = 0;
267 	char *tmp, *lasts = NULL;
268 	char *ctrl = NULL;
269 
270 	while ((c = getopt(argc, argv, "dhv")) != -1) {
271 		switch (c) {
272 		case 'd':
273 			debug++;
274 			break;
275 		case 'v':
276 			verbose++;
277 			break;
278 		case 'h':
279 			help++;
280 			break;
281 		case '?':
282 			usage(NULL);
283 			exit(-1);
284 		}
285 	}
286 
287 	if (optind == argc) {
288 		usage(NULL);
289 		if (help)
290 			exit(0);
291 		else
292 			exit(-1);
293 	}
294 
295 	/* Look up the specified command in the command table. */
296 	for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
297 		if (strcmp(cmd->c_name, argv[optind]) == 0)
298 			break;
299 
300 	if (cmd->c_name == NULL) {
301 		usage(NULL);
302 		exit(-1);
303 	}
304 
305 	if (help) {
306 		usage(cmd);
307 		exit(0);
308 	}
309 
310 	npa.npa_cmd = cmd;
311 	npa.npa_interactive = B_TRUE;
312 	npa.npa_excl = ((cmd->c_flags & NVMEADM_C_EXCL) != 0);
313 
314 	optind++;
315 
316 	/*
317 	 * Store the remaining arguments for use by the command. Give the
318 	 * command a chance to process the options across the board before going
319 	 * into each controller.
320 	 */
321 	npa.npa_argc = argc - optind;
322 	npa.npa_argv = &argv[optind];
323 
324 	if (cmd->c_optparse != NULL) {
325 		cmd->c_optparse(&npa);
326 	}
327 
328 	/*
329 	 * All commands but "list" require a ctl/ns argument. However, this
330 	 * should not be passed through to the command in its subsequent
331 	 * arguments.
332 	 */
333 	if ((npa.npa_argc == 0 || (strncmp(npa.npa_argv[0], "nvme", 4) != 0)) &&
334 	    cmd->c_func != do_list) {
335 		warnx("missing controller/namespace name");
336 		usage(cmd);
337 		exit(-1);
338 	}
339 
340 	if (npa.npa_argc > 0) {
341 		ctrl = npa.npa_argv[0];
342 		npa.npa_argv++;
343 		npa.npa_argc--;
344 	} else {
345 		ctrl = NULL;
346 	}
347 
348 	/*
349 	 * Make sure we're not running commands on multiple controllers that
350 	 * aren't allowed to do that.
351 	 */
352 	if (ctrl != NULL && strchr(ctrl, ',') != NULL &&
353 	    (cmd->c_flags & NVMEADM_C_MULTI) == 0) {
354 		warnx("%s not allowed on multiple controllers",
355 		    cmd->c_name);
356 		usage(cmd);
357 		exit(-1);
358 	}
359 
360 	/*
361 	 * Get controller/namespace arguments and run command.
362 	 */
363 	npa.npa_name = strtok_r(ctrl, ",", &lasts);
364 	do {
365 		if (npa.npa_name != NULL) {
366 			tmp = strchr(npa.npa_name, '/');
367 			if (tmp != NULL) {
368 				*tmp++ = '\0';
369 				npa.npa_nsid = tmp;
370 				npa.npa_isns = B_TRUE;
371 			}
372 		}
373 
374 		if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL)
375 			err(-1, "failed to initialize libdevinfo");
376 		nvme_walk(&npa, node);
377 		di_fini(node);
378 
379 		if (npa.npa_found == 0) {
380 			if (npa.npa_name != NULL) {
381 				warnx("%s%.*s%.*s: no such controller or "
382 				    "namespace", npa.npa_name,
383 				    npa.npa_isns ? -1 : 0, "/",
384 				    npa.npa_isns ? -1 : 0, npa.npa_nsid);
385 			} else {
386 				warnx("no controllers found");
387 			}
388 			exitcode--;
389 		}
390 		npa.npa_found = 0;
391 		npa.npa_name = strtok_r(NULL, ",", &lasts);
392 	} while (npa.npa_name != NULL);
393 
394 	exit(exitcode);
395 }
396 
397 static void
398 nvme_oferr(const char *fmt, ...)
399 {
400 	va_list ap;
401 
402 	va_start(ap, fmt);
403 	verrx(-1, fmt, ap);
404 }
405 
406 static void
407 usage(const nvmeadm_cmd_t *cmd)
408 {
409 	(void) fprintf(stderr, "usage:\n");
410 	(void) fprintf(stderr, "  %s -h %s\n", getprogname(),
411 	    cmd != NULL ? cmd->c_name : "[<command>]");
412 	(void) fprintf(stderr, "  %s [-dv] ", getprogname());
413 
414 	if (cmd != NULL) {
415 		cmd->c_usage(cmd->c_name);
416 	} else {
417 		(void) fprintf(stderr,
418 		    "<command> <ctl>[/<ns>][,...] [<args>]\n");
419 		(void) fprintf(stderr,
420 		    "\n  Manage NVMe controllers and namespaces.\n");
421 		(void) fprintf(stderr, "\ncommands:\n");
422 
423 		for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
424 			(void) fprintf(stderr, "  %-18s - %s\n",
425 			    cmd->c_name, cmd->c_desc);
426 	}
427 	(void) fprintf(stderr, "\nflags:\n"
428 	    "  -h\t\tprint usage information\n"
429 	    "  -d\t\tprint information useful for debugging %s\n"
430 	    "  -v\t\tprint verbose information\n", getprogname());
431 	if (cmd != NULL && cmd->c_flagdesc != NULL)
432 		(void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
433 }
434 
435 static boolean_t
436 nvme_match(nvme_process_arg_t *npa)
437 {
438 	char *name;
439 	char *nsid = NULL;
440 
441 	if (npa->npa_name == NULL)
442 		return (B_TRUE);
443 
444 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
445 	    di_instance(npa->npa_node)) < 0)
446 		err(-1, "nvme_match()");
447 
448 	if (strcmp(name, npa->npa_name) != 0) {
449 		free(name);
450 		return (B_FALSE);
451 	}
452 
453 	free(name);
454 
455 	if (npa->npa_isns) {
456 		if (npa->npa_nsid == NULL)
457 			return (B_TRUE);
458 
459 		nsid = di_minor_name(npa->npa_minor);
460 
461 		if (nsid == NULL || strcmp(npa->npa_nsid, nsid) != 0)
462 			return (B_FALSE);
463 	}
464 
465 	return (B_TRUE);
466 }
467 
468 char *
469 nvme_dskname(const nvme_process_arg_t *npa)
470 {
471 	char *path = NULL;
472 	di_node_t child;
473 	di_dim_t dim;
474 	char *addr;
475 	char *disk_ctd;
476 	char *diskname = NULL;
477 
478 	dim = di_dim_init();
479 
480 	for (child = di_child_node(npa->npa_node);
481 	    child != DI_NODE_NIL;
482 	    child = di_sibling_node(child)) {
483 		addr = di_bus_addr(child);
484 		if (addr == NULL)
485 			continue;
486 
487 		if (addr[0] == 'w')
488 			addr++;
489 
490 		if (strncasecmp(addr, di_minor_name(npa->npa_minor),
491 		    strchrnul(addr, ',') - addr) != 0)
492 			continue;
493 
494 		path = di_dim_path_dev(dim, di_driver_name(child),
495 		    di_instance(child), "c");
496 
497 		/*
498 		 * Error out if we didn't get a path, or if it's too short for
499 		 * the following operations to be safe.
500 		 */
501 		if (path == NULL || strlen(path) < 2)
502 			goto fail;
503 
504 		/* Chop off 's0' and get everything past the last '/' */
505 		path[strlen(path) - 2] = '\0';
506 		disk_ctd = strrchr(path, '/');
507 		if (disk_ctd == NULL)
508 			goto fail;
509 		diskname = strdup(++disk_ctd);
510 		if (diskname == NULL)
511 			goto fail;
512 
513 		free(path);
514 		break;
515 	}
516 
517 	di_dim_fini(dim);
518 
519 	return (diskname);
520 
521 fail:
522 	free(path);
523 	err(-1, "nvme_dskname");
524 }
525 
526 static int
527 nvme_process(di_node_t node, di_minor_t minor, void *arg)
528 {
529 	nvme_process_arg_t *npa = arg;
530 	int fd;
531 
532 	npa->npa_node = node;
533 	npa->npa_minor = minor;
534 
535 	if (!nvme_match(npa))
536 		return (DI_WALK_CONTINUE);
537 
538 	if ((fd = nvme_open(minor, npa->npa_excl)) < 0)
539 		return (DI_WALK_CONTINUE);
540 
541 	npa->npa_found++;
542 
543 	npa->npa_path = di_devfs_path(node);
544 	if (npa->npa_path == NULL)
545 		goto out;
546 
547 	npa->npa_version = nvme_version(fd);
548 	if (npa->npa_version == NULL)
549 		goto out;
550 
551 	npa->npa_idctl = nvme_identify_ctrl(fd);
552 	if (npa->npa_idctl == NULL)
553 		goto out;
554 
555 	npa->npa_idns = nvme_identify_nsid(fd);
556 	if (npa->npa_idns == NULL)
557 		goto out;
558 
559 	if (npa->npa_isns) {
560 		npa->npa_ignored = nvme_is_ignored_ns(fd);
561 		if (!npa->npa_ignored)
562 			npa->npa_dsk = nvme_dskname(npa);
563 	}
564 
565 
566 	exitcode += npa->npa_cmd->c_func(fd, npa);
567 
568 out:
569 	di_devfs_path_free(npa->npa_path);
570 	free(npa->npa_version);
571 	free(npa->npa_idctl);
572 	free(npa->npa_idns);
573 	free(npa->npa_dsk);
574 
575 	npa->npa_version = NULL;
576 	npa->npa_idctl = NULL;
577 	npa->npa_idns = NULL;
578 	npa->npa_dsk = NULL;
579 
580 	nvme_close(fd);
581 
582 	return (DI_WALK_CONTINUE);
583 }
584 
585 static void
586 nvme_walk(nvme_process_arg_t *npa, di_node_t node)
587 {
588 	char *minor_nodetype = DDI_NT_NVME_NEXUS;
589 
590 	if (npa->npa_isns)
591 		minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT;
592 
593 	(void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process);
594 }
595 
596 static void
597 usage_list(const char *c_name)
598 {
599 	(void) fprintf(stderr, "%s "
600 	    "[-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n"
601 	    "  List NVMe controllers and their namespaces. If no "
602 	    "controllers and/or name-\n  spaces are specified, all "
603 	    "controllers and namespaces in the system will be\n  "
604 	    "listed.\n", c_name);
605 }
606 
607 static void
608 optparse_list(nvme_process_arg_t *npa)
609 {
610 	int c;
611 	uint_t oflags = 0;
612 	boolean_t parse = B_FALSE;
613 	const char *fields = NULL;
614 
615 	optind = 0;
616 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":o:p")) != -1) {
617 		switch (c) {
618 		case 'o':
619 			fields = optarg;
620 			break;
621 		case 'p':
622 			parse = B_TRUE;
623 			oflags |= OFMT_PARSABLE;
624 			break;
625 		case '?':
626 			errx(-1, "unknown list option: -%c", optopt);
627 			break;
628 		case ':':
629 			errx(-1, "option -%c requires an argument", optopt);
630 		default:
631 			break;
632 		}
633 	}
634 
635 	if (fields != NULL && !parse) {
636 		errx(-1, "-o can only be used when in parsable mode (-p)");
637 	}
638 
639 	if (parse && fields == NULL) {
640 		errx(-1, "parsable mode (-p) requires one to specify output "
641 		    "fields with -o");
642 	}
643 
644 	if (parse) {
645 		ofmt_status_t oferr;
646 
647 		oferr = ofmt_open(fields, nvme_list_ofmt, oflags, 0,
648 		    &npa->npa_ofmt);
649 		ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
650 	}
651 
652 	npa->npa_argc -= optind;
653 	npa->npa_argv += optind;
654 }
655 
656 static int
657 do_list_nsid(int fd, const nvme_process_arg_t *npa)
658 {
659 	_NOTE(ARGUNUSED(fd));
660 	const uint_t format = npa->npa_idns->id_flbas.lba_format;
661 	const uint_t bshift = npa->npa_idns->id_lbaf[format].lbaf_lbads;
662 
663 	/*
664 	 * Some devices have extra namespaces with illegal block sizes and
665 	 * zero blocks. Don't list them when verbose operation isn't requested.
666 	 */
667 	if ((bshift < 9 || npa->npa_idns->id_nsize == 0) && verbose == 0)
668 		return (0);
669 
670 	if (npa->npa_ofmt == NULL) {
671 		(void) printf("  %s/%s (%s): ", npa->npa_name,
672 		    di_minor_name(npa->npa_minor),
673 		    npa->npa_dsk != NULL ? npa->npa_dsk : "unattached");
674 		nvme_print_nsid_summary(npa->npa_idns);
675 	} else {
676 		ofmt_print(npa->npa_ofmt, (void *)npa);
677 	}
678 
679 	return (0);
680 }
681 
682 static int
683 do_list(int fd, const nvme_process_arg_t *npa)
684 {
685 	_NOTE(ARGUNUSED(fd));
686 
687 	nvme_process_arg_t ns_npa = { 0 };
688 	nvmeadm_cmd_t cmd = { 0 };
689 	char *name;
690 
691 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
692 	    di_instance(npa->npa_node)) < 0)
693 		err(-1, "do_list()");
694 
695 	if (npa->npa_ofmt == NULL) {
696 		(void) printf("%s: ", name);
697 		nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version);
698 	}
699 
700 	ns_npa.npa_name = name;
701 	ns_npa.npa_isns = B_TRUE;
702 	ns_npa.npa_nsid = npa->npa_nsid;
703 	cmd = *(npa->npa_cmd);
704 	cmd.c_func = do_list_nsid;
705 	ns_npa.npa_cmd = &cmd;
706 	ns_npa.npa_ofmt = npa->npa_ofmt;
707 	ns_npa.npa_idctl = npa->npa_idctl;
708 
709 	nvme_walk(&ns_npa, npa->npa_node);
710 
711 	free(name);
712 
713 	return (exitcode);
714 }
715 
716 static void
717 usage_identify(const char *c_name)
718 {
719 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n"
720 	    "  Print detailed information about the specified NVMe "
721 	    "controllers and/or name-\n  spaces.\n", c_name);
722 }
723 
724 static int
725 do_identify(int fd, const nvme_process_arg_t *npa)
726 {
727 	if (!npa->npa_isns) {
728 		nvme_capabilities_t *cap;
729 
730 		cap = nvme_capabilities(fd);
731 		if (cap == NULL)
732 			return (-1);
733 
734 		(void) printf("%s: ", npa->npa_name);
735 		nvme_print_identify_ctrl(npa->npa_idctl, cap,
736 		    npa->npa_version);
737 
738 		free(cap);
739 	} else {
740 		(void) printf("%s/%s: ", npa->npa_name,
741 		    di_minor_name(npa->npa_minor));
742 		nvme_print_identify_nsid(npa->npa_idns,
743 		    npa->npa_version);
744 	}
745 
746 	return (0);
747 }
748 
749 static void
750 usage_get_logpage(const char *c_name)
751 {
752 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n"
753 	    "  Print the specified log page of the specified NVMe "
754 	    "controllers and/or name-\n  spaces. Supported log pages "
755 	    "are error, health, and firmware.\n", c_name);
756 }
757 
758 static void
759 usage_firmware_list(const char *c_name)
760 {
761 	(void) fprintf(stderr, "%s <ctl>\n\n"
762 	    "  Print the log page that contains the list of firmware "
763 	    "images installed on the specified NVMe controller.\n", c_name);
764 }
765 
766 static int
767 do_get_logpage_error(int fd, const nvme_process_arg_t *npa)
768 {
769 	int nlog = npa->npa_idctl->id_elpe + 1;
770 	size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog;
771 	nvme_error_log_entry_t *elog;
772 
773 	if (npa->npa_isns)
774 		errx(-1, "Error Log not available on a per-namespace basis");
775 
776 	elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize);
777 
778 	if (elog == NULL)
779 		return (-1);
780 
781 	nlog = bufsize / sizeof (nvme_error_log_entry_t);
782 
783 	(void) printf("%s: ", npa->npa_name);
784 	nvme_print_error_log(nlog, elog, npa->npa_version);
785 
786 	free(elog);
787 
788 	return (0);
789 }
790 
791 static int
792 do_get_logpage_health(int fd, const nvme_process_arg_t *npa)
793 {
794 	size_t bufsize = sizeof (nvme_health_log_t);
795 	nvme_health_log_t *hlog;
796 
797 	if (npa->npa_isns) {
798 		if (npa->npa_idctl->id_lpa.lp_smart == 0)
799 			errx(-1, "SMART/Health information not available "
800 			    "on a per-namespace basis on this controller");
801 	}
802 
803 	hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
804 
805 	if (hlog == NULL)
806 		return (-1);
807 
808 	(void) printf("%s: ", npa->npa_name);
809 	nvme_print_health_log(hlog, npa->npa_idctl, npa->npa_version);
810 
811 	free(hlog);
812 
813 	return (0);
814 }
815 
816 static int
817 do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa)
818 {
819 	size_t bufsize = sizeof (nvme_fwslot_log_t);
820 	nvme_fwslot_log_t *fwlog;
821 
822 	if (npa->npa_isns)
823 		errx(-1, "Firmware Slot information not available on a "
824 		    "per-namespace basis");
825 
826 	fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize);
827 
828 	if (fwlog == NULL)
829 		return (-1);
830 
831 	(void) printf("%s: ", npa->npa_name);
832 	nvme_print_fwslot_log(fwlog, npa->npa_idctl);
833 
834 	free(fwlog);
835 
836 	return (0);
837 }
838 
839 static int
840 do_get_logpage(int fd, const nvme_process_arg_t *npa)
841 {
842 	int ret = 0;
843 	int (*func)(int, const nvme_process_arg_t *);
844 
845 	if (npa->npa_argc < 1) {
846 		warnx("missing logpage name");
847 		usage(npa->npa_cmd);
848 		exit(-1);
849 	}
850 
851 	if (strcmp(npa->npa_argv[0], "error") == 0)
852 		func = do_get_logpage_error;
853 	else if (strcmp(npa->npa_argv[0], "health") == 0)
854 		func = do_get_logpage_health;
855 	else if (strcmp(npa->npa_argv[0], "firmware") == 0)
856 		func = do_get_logpage_fwslot;
857 	else
858 		errx(-1, "invalid log page: %s", npa->npa_argv[0]);
859 
860 	ret = func(fd, npa);
861 	return (ret);
862 }
863 
864 static void
865 usage_get_features(const char *c_name)
866 {
867 	const nvme_feature_t *feat;
868 
869 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
870 	    "  Print the specified features of the specified NVMe controllers "
871 	    "and/or\n  namespaces. Supported features are:\n\n", c_name);
872 	(void) fprintf(stderr, "    %-35s %-14s %s\n",
873 	    "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE");
874 	for (feat = &features[0]; feat->f_feature != 0; feat++) {
875 		char *type;
876 
877 		if ((feat->f_getflags & NVMEADM_F_BOTH) == NVMEADM_F_BOTH)
878 			type = "both";
879 		else if ((feat->f_getflags & NVMEADM_F_CTRL) != 0)
880 			type = "controller only";
881 		else
882 			type = "namespace only";
883 
884 		(void) fprintf(stderr, "    %-35s %-14s %s\n",
885 		    feat->f_name, feat->f_short, type);
886 	}
887 
888 }
889 
890 static int
891 do_get_feat_common(int fd, const nvme_feature_t *feat,
892     const nvme_process_arg_t *npa)
893 {
894 	void *buf = NULL;
895 	size_t bufsize = feat->f_bufsize;
896 	uint64_t res;
897 
898 	if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf)
899 	    == B_FALSE)
900 		return (EINVAL);
901 
902 	nvme_print(2, feat->f_name, -1, NULL);
903 	feat->f_print(res, buf, bufsize, npa->npa_idctl, npa->npa_version);
904 	free(buf);
905 
906 	return (0);
907 }
908 
909 static int
910 do_get_feat_temp_thresh_one(int fd, const nvme_feature_t *feat,
911     const char *label, uint16_t tmpsel, uint16_t thsel,
912     const nvme_process_arg_t *npa)
913 {
914 	uint64_t res;
915 	void *buf = NULL;
916 	size_t bufsize = feat->f_bufsize;
917 	nvme_temp_threshold_t tt;
918 
919 	tt.r = 0;
920 	tt.b.tt_tmpsel = tmpsel;
921 	tt.b.tt_thsel = thsel;
922 
923 	if (!nvme_get_feature(fd, feat->f_feature, tt.r, &res, &bufsize,
924 	    &buf)) {
925 		return (EINVAL);
926 	}
927 
928 	feat->f_print(res, (void *)label, 0, npa->npa_idctl, npa->npa_version);
929 	free(buf);
930 	return (0);
931 }
932 
933 /*
934  * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the
935  * device and changed the main device to have a composite temperature sensor. As
936  * a result, there is a set of thresholds for each sensor. In addition, they
937  * added both an over-temperature and under-temperature threshold. Since most
938  * devices don't actually implement all the sensors, we get the health page and
939  * see which sensors have a non-zero value to determine how to proceed.
940  */
941 static int
942 do_get_feat_temp_thresh(int fd, const nvme_feature_t *feat,
943     const nvme_process_arg_t *npa)
944 {
945 	int ret;
946 	size_t bufsize = sizeof (nvme_health_log_t);
947 	nvme_health_log_t *hlog;
948 
949 	nvme_print(2, feat->f_name, -1, NULL);
950 	if ((ret = do_get_feat_temp_thresh_one(fd, feat,
951 	    "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER,
952 	    npa)) != 0) {
953 		return (ret);
954 	}
955 
956 	if (!nvme_version_check(npa->npa_version, 1, 2)) {
957 		return (0);
958 	}
959 
960 	if ((ret = do_get_feat_temp_thresh_one(fd, feat,
961 	    "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER,
962 	    npa)) != 0) {
963 		return (ret);
964 	}
965 
966 	hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
967 	if (hlog == NULL) {
968 		warnx("failed to get health log page, unable to get "
969 		    "thresholds for additional sensors");
970 		return (0);
971 	}
972 
973 	if (hlog->hl_temp_sensor_1 != 0) {
974 		(void) do_get_feat_temp_thresh_one(fd, feat,
975 		    "Temp. Sensor 1 Over Temp. Threshold", 1,
976 		    NVME_TEMP_THRESH_OVER, npa);
977 		(void) do_get_feat_temp_thresh_one(fd, feat,
978 		    "Temp. Sensor 1 Under Temp. Threshold", 1,
979 		    NVME_TEMP_THRESH_UNDER, npa);
980 	}
981 
982 	if (hlog->hl_temp_sensor_2 != 0) {
983 		(void) do_get_feat_temp_thresh_one(fd, feat,
984 		    "Temp. Sensor 2 Over Temp. Threshold", 2,
985 		    NVME_TEMP_THRESH_OVER, npa);
986 		(void) do_get_feat_temp_thresh_one(fd, feat,
987 		    "Temp. Sensor 2 Under Temp. Threshold", 2,
988 		    NVME_TEMP_THRESH_UNDER, npa);
989 	}
990 
991 	if (hlog->hl_temp_sensor_3 != 0) {
992 		(void) do_get_feat_temp_thresh_one(fd, feat,
993 		    "Temp. Sensor 3 Over Temp. Threshold", 3,
994 		    NVME_TEMP_THRESH_OVER, npa);
995 		(void) do_get_feat_temp_thresh_one(fd, feat,
996 		    "Temp. Sensor 3 Under Temp. Threshold", 3,
997 		    NVME_TEMP_THRESH_UNDER, npa);
998 	}
999 
1000 	if (hlog->hl_temp_sensor_4 != 0) {
1001 		(void) do_get_feat_temp_thresh_one(fd, feat,
1002 		    "Temp. Sensor 4 Over Temp. Threshold", 4,
1003 		    NVME_TEMP_THRESH_OVER, npa);
1004 		(void) do_get_feat_temp_thresh_one(fd, feat,
1005 		    "Temp. Sensor 4 Under Temp. Threshold", 4,
1006 		    NVME_TEMP_THRESH_UNDER, npa);
1007 	}
1008 
1009 	if (hlog->hl_temp_sensor_5 != 0) {
1010 		(void) do_get_feat_temp_thresh_one(fd, feat,
1011 		    "Temp. Sensor 5 Over Temp. Threshold", 5,
1012 		    NVME_TEMP_THRESH_OVER, npa);
1013 		(void) do_get_feat_temp_thresh_one(fd, feat,
1014 		    "Temp. Sensor 5 Under Temp. Threshold", 5,
1015 		    NVME_TEMP_THRESH_UNDER, npa);
1016 	}
1017 
1018 	if (hlog->hl_temp_sensor_6 != 0) {
1019 		(void) do_get_feat_temp_thresh_one(fd, feat,
1020 		    "Temp. Sensor 6 Over Temp. Threshold", 6,
1021 		    NVME_TEMP_THRESH_OVER, npa);
1022 		(void) do_get_feat_temp_thresh_one(fd, feat,
1023 		    "Temp. Sensor 6 Under Temp. Threshold", 6,
1024 		    NVME_TEMP_THRESH_UNDER, npa);
1025 	}
1026 
1027 	if (hlog->hl_temp_sensor_7 != 0) {
1028 		(void) do_get_feat_temp_thresh_one(fd, feat,
1029 		    "Temp. Sensor 7 Over Temp. Threshold", 7,
1030 		    NVME_TEMP_THRESH_OVER, npa);
1031 		(void) do_get_feat_temp_thresh_one(fd, feat,
1032 		    "Temp. Sensor 7 Under Temp. Threshold", 7,
1033 		    NVME_TEMP_THRESH_UNDER, npa);
1034 	}
1035 
1036 	if (hlog->hl_temp_sensor_8 != 0) {
1037 		(void) do_get_feat_temp_thresh_one(fd, feat,
1038 		    "Temp. Sensor 8 Over Temp. Threshold", 8,
1039 		    NVME_TEMP_THRESH_OVER, npa);
1040 		(void) do_get_feat_temp_thresh_one(fd, feat,
1041 		    "Temp. Sensor 8 Under Temp. Threshold", 8,
1042 		    NVME_TEMP_THRESH_UNDER, npa);
1043 	}
1044 	free(hlog);
1045 	return (0);
1046 }
1047 
1048 static int
1049 do_get_feat_intr_vect(int fd, const nvme_feature_t *feat,
1050     const nvme_process_arg_t *npa)
1051 {
1052 	uint64_t res;
1053 	uint64_t arg;
1054 	int intr_cnt;
1055 
1056 	intr_cnt = nvme_intr_cnt(fd);
1057 
1058 	if (intr_cnt == -1)
1059 		return (EINVAL);
1060 
1061 	nvme_print(2, feat->f_name, -1, NULL);
1062 
1063 	for (arg = 0; arg < intr_cnt; arg++) {
1064 		if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL)
1065 		    == B_FALSE)
1066 			return (EINVAL);
1067 
1068 		feat->f_print(res, NULL, 0, npa->npa_idctl, npa->npa_version);
1069 	}
1070 
1071 	return (0);
1072 }
1073 
1074 static int
1075 do_get_features(int fd, const nvme_process_arg_t *npa)
1076 {
1077 	const nvme_feature_t *feat;
1078 	char *f, *flist, *lasts;
1079 	boolean_t header_printed = B_FALSE;
1080 
1081 	if (npa->npa_argc > 1)
1082 		errx(-1, "unexpected arguments");
1083 
1084 	/*
1085 	 * No feature list given, print all supported features.
1086 	 */
1087 	if (npa->npa_argc == 0) {
1088 		(void) printf("%s: Get Features\n", npa->npa_name);
1089 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
1090 			if ((npa->npa_isns &&
1091 			    (feat->f_getflags & NVMEADM_F_NS) == 0) ||
1092 			    (!npa->npa_isns &&
1093 			    (feat->f_getflags & NVMEADM_F_CTRL) == 0))
1094 				continue;
1095 
1096 			(void) feat->f_get(fd, feat, npa);
1097 		}
1098 
1099 		return (0);
1100 	}
1101 
1102 	/*
1103 	 * Process feature list.
1104 	 */
1105 	flist = strdup(npa->npa_argv[0]);
1106 	if (flist == NULL)
1107 		err(-1, "do_get_features");
1108 
1109 	for (f = strtok_r(flist, ",", &lasts);
1110 	    f != NULL;
1111 	    f = strtok_r(NULL, ",", &lasts)) {
1112 		while (isspace(*f))
1113 			f++;
1114 
1115 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
1116 			if (strncasecmp(feat->f_name, f, strlen(f)) == 0 ||
1117 			    strncasecmp(feat->f_short, f, strlen(f)) == 0)
1118 				break;
1119 		}
1120 
1121 		if (feat->f_feature == 0) {
1122 			warnx("unknown feature %s", f);
1123 			continue;
1124 		}
1125 
1126 		if ((npa->npa_isns &&
1127 		    (feat->f_getflags & NVMEADM_F_NS) == 0) ||
1128 		    (!npa->npa_isns &&
1129 		    (feat->f_getflags & NVMEADM_F_CTRL) == 0)) {
1130 			warnx("feature %s %s supported for namespaces",
1131 			    feat->f_name,
1132 			    (feat->f_getflags & NVMEADM_F_NS) != 0 ?
1133 			    "only" : "not");
1134 			continue;
1135 		}
1136 
1137 		if (!header_printed) {
1138 			(void) printf("%s: Get Features\n", npa->npa_name);
1139 			header_printed = B_TRUE;
1140 		}
1141 
1142 		if (feat->f_get(fd, feat, npa) != 0) {
1143 			warnx("unsupported feature: %s", feat->f_name);
1144 			continue;
1145 		}
1146 	}
1147 
1148 	free(flist);
1149 	return (0);
1150 }
1151 
1152 static int
1153 do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf,
1154     unsigned long ses)
1155 {
1156 	nvme_process_arg_t ns_npa = { 0 };
1157 	nvmeadm_cmd_t cmd = { 0 };
1158 
1159 	cmd = *(npa->npa_cmd);
1160 	cmd.c_func = do_attach_detach;
1161 	cmd.c_name = "detach";
1162 	ns_npa = *npa;
1163 	ns_npa.npa_cmd = &cmd;
1164 
1165 	if (do_attach_detach(fd, &ns_npa) != 0)
1166 		return (exitcode);
1167 	if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) {
1168 		warn("%s failed", npa->npa_cmd->c_name);
1169 		exitcode += -1;
1170 	}
1171 	cmd.c_name = "attach";
1172 	exitcode += do_attach_detach(fd, &ns_npa);
1173 
1174 	return (exitcode);
1175 }
1176 
1177 static void
1178 usage_format(const char *c_name)
1179 {
1180 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
1181 	    "  Format one or all namespaces of the specified NVMe "
1182 	    "controller. Supported LBA\n  formats can be queried with "
1183 	    "the \"%s identify\" command on the namespace\n  to be "
1184 	    "formatted.\n", c_name, getprogname());
1185 }
1186 
1187 static int
1188 do_format(int fd, const nvme_process_arg_t *npa)
1189 {
1190 	unsigned long lbaf;
1191 
1192 	if (npa->npa_idctl->id_oacs.oa_format == 0)
1193 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
1194 
1195 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0)
1196 		errx(-1, "%s not supported on individual namespace",
1197 		    npa->npa_cmd->c_name);
1198 
1199 
1200 	if (npa->npa_argc > 0) {
1201 		errno = 0;
1202 		lbaf = strtoul(npa->npa_argv[0], NULL, 10);
1203 
1204 		if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF)
1205 			errx(-1, "invalid LBA format %d", lbaf + 1);
1206 
1207 		if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0)
1208 			errx(-1, "LBA formats with metadata not supported");
1209 	} else {
1210 		lbaf = npa->npa_idns->id_flbas.lba_format;
1211 	}
1212 
1213 	return (do_format_common(fd, npa, lbaf, 0));
1214 }
1215 
1216 static void
1217 usage_secure_erase(const char *c_name)
1218 {
1219 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [-c]\n\n"
1220 	    "  Secure-Erase one or all namespaces of the specified "
1221 	    "NVMe controller.\n", c_name);
1222 }
1223 
1224 static int
1225 do_secure_erase(int fd, const nvme_process_arg_t *npa)
1226 {
1227 	unsigned long lbaf;
1228 	uint8_t ses = NVME_FRMT_SES_USER;
1229 
1230 	if (npa->npa_idctl->id_oacs.oa_format == 0)
1231 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
1232 
1233 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0)
1234 		errx(-1, "%s not supported on individual namespace",
1235 		    npa->npa_cmd->c_name);
1236 
1237 	if (npa->npa_argc > 0) {
1238 		if (strcmp(npa->npa_argv[0], "-c") == 0)
1239 			ses = NVME_FRMT_SES_CRYPTO;
1240 		else
1241 			usage(npa->npa_cmd);
1242 	}
1243 
1244 	if (ses == NVME_FRMT_SES_CRYPTO &&
1245 	    npa->npa_idctl->id_fna.fn_crypt_erase == 0)
1246 		errx(-1, "cryptographic %s not supported",
1247 		    npa->npa_cmd->c_name);
1248 
1249 	lbaf = npa->npa_idns->id_flbas.lba_format;
1250 
1251 	return (do_format_common(fd, npa, lbaf, ses));
1252 }
1253 
1254 static void
1255 usage_attach_detach(const char *c_name)
1256 {
1257 	(void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
1258 	    "  %c%s blkdev(4D) %s one or all namespaces of the "
1259 	    "specified NVMe controller.\n",
1260 	    c_name, toupper(c_name[0]), &c_name[1],
1261 	    c_name[0] == 'd' ? "from" : "to");
1262 }
1263 
1264 static int
1265 do_attach_detach(int fd, const nvme_process_arg_t *npa)
1266 {
1267 	char *c_name = npa->npa_cmd->c_name;
1268 
1269 	if (!npa->npa_isns) {
1270 		nvme_process_arg_t ns_npa = { 0 };
1271 
1272 		ns_npa.npa_name = npa->npa_name;
1273 		ns_npa.npa_isns = B_TRUE;
1274 		ns_npa.npa_cmd = npa->npa_cmd;
1275 		ns_npa.npa_excl = npa->npa_excl;
1276 
1277 		nvme_walk(&ns_npa, npa->npa_node);
1278 
1279 		return (exitcode);
1280 	}
1281 
1282 	/*
1283 	 * Unless the user interactively requested a particular namespace to be
1284 	 * attached or detached, don't even try to attach or detach namespaces
1285 	 * that are ignored by the driver, thereby avoiding printing pointless
1286 	 * error messages.
1287 	 */
1288 	if (!npa->npa_interactive && npa->npa_ignored)
1289 		return (0);
1290 
1291 	if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd)
1292 	    == B_FALSE) {
1293 		warn("%s failed", c_name);
1294 		return (-1);
1295 	}
1296 
1297 	return (0);
1298 }
1299 
1300 static void
1301 usage_firmware_load(const char *c_name)
1302 {
1303 	(void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n"
1304 	    "  Load firmware <image file> to offset <offset>.\n"
1305 	    "  The firmware needs to be committed to a slot using "
1306 	    "\"nvmeadm commit-firmware\"\n  command.\n", c_name);
1307 }
1308 
1309 /*
1310  * Read exactly len bytes, or until eof.
1311  */
1312 static ssize_t
1313 read_block(int fd, char *buf, size_t len)
1314 {
1315 	size_t remain;
1316 	ssize_t bytes;
1317 
1318 	remain = len;
1319 	while (remain > 0) {
1320 		bytes = read(fd, buf, remain);
1321 		if (bytes == 0)
1322 			break;
1323 
1324 		if (bytes < 0) {
1325 			if (errno == EINTR)
1326 				continue;
1327 
1328 			return (-1);
1329 		}
1330 
1331 		buf += bytes;
1332 		remain -= bytes;
1333 	}
1334 
1335 	return (len - remain);
1336 }
1337 
1338 /*
1339  * Convert a string to a valid firmware upload offset (in bytes).
1340  */
1341 static offset_t
1342 get_fw_offsetb(char *str)
1343 {
1344 	longlong_t offsetb;
1345 	char *valend;
1346 
1347 	errno = 0;
1348 	offsetb = strtoll(str, &valend, 0);
1349 	if (errno != 0 || *valend != '\0' || offsetb < 0 ||
1350 	    offsetb > NVME_FW_OFFSETB_MAX)
1351 		errx(-1, "Offset must be numeric and in the range of 0 to %llu",
1352 		    NVME_FW_OFFSETB_MAX);
1353 
1354 	if ((offsetb & NVME_DWORD_MASK) != 0)
1355 		errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE);
1356 
1357 	return ((offset_t)offsetb);
1358 }
1359 
1360 #define	FIRMWARE_READ_BLKSIZE	(64 * 1024)		/* 64K */
1361 
1362 static int
1363 do_firmware_load(int fd, const nvme_process_arg_t *npa)
1364 {
1365 	int fw_fd;
1366 	ssize_t len;
1367 	offset_t offset = 0;
1368 	size_t size;
1369 	uint16_t sc;
1370 	char buf[FIRMWARE_READ_BLKSIZE];
1371 
1372 	if (npa->npa_argc > 2)
1373 		errx(-1, "Too many arguments");
1374 
1375 	if (npa->npa_argc == 0)
1376 		errx(-1, "Requires firmware file name, and an "
1377 		    "optional offset");
1378 
1379 	if (npa->npa_isns)
1380 		errx(-1, "Firmware loading not available on a per-namespace "
1381 		    "basis");
1382 
1383 	if (npa->npa_argc == 2)
1384 		offset = get_fw_offsetb(npa->npa_argv[1]);
1385 
1386 	fw_fd = open(npa->npa_argv[0], O_RDONLY);
1387 	if (fw_fd < 0)
1388 		errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0],
1389 		    strerror(errno));
1390 
1391 	size = 0;
1392 	do {
1393 		len = read_block(fw_fd, buf, sizeof (buf));
1394 
1395 		if (len < 0)
1396 			errx(-1, "Error reading \"%s\": %s", npa->npa_argv[0],
1397 			    strerror(errno));
1398 
1399 		if (len == 0)
1400 			break;
1401 
1402 		if (!nvme_firmware_load(fd, buf, len, offset, &sc))
1403 			errx(-1, "Error loading \"%s\": %s", npa->npa_argv[0],
1404 			    nvme_fw_error(errno, sc));
1405 
1406 		offset += len;
1407 		size += len;
1408 	} while (len == sizeof (buf));
1409 
1410 	(void) close(fw_fd);
1411 
1412 	if (verbose)
1413 		(void) printf("%zu bytes downloaded.\n", size);
1414 
1415 	return (0);
1416 }
1417 
1418 /*
1419  * Convert str to a valid firmware slot number.
1420  */
1421 static uint_t
1422 get_slot_number(char *str)
1423 {
1424 	longlong_t slot;
1425 	char *valend;
1426 
1427 	errno = 0;
1428 	slot = strtoll(str, &valend, 0);
1429 	if (errno != 0 || *valend != '\0' ||
1430 	    slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX)
1431 		errx(-1, "Slot must be numeric and in the range of %d to %d",
1432 		    NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX);
1433 
1434 	return ((uint_t)slot);
1435 }
1436 
1437 static void
1438 usage_firmware_commit(const char *c_name)
1439 {
1440 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
1441 	    "  Commit previously downloaded firmware to slot <slot>.\n"
1442 	    "  The firmware is only activated after a "
1443 	    "\"nvmeadm activate-firmware\" command.\n", c_name);
1444 }
1445 
1446 static int
1447 do_firmware_commit(int fd, const nvme_process_arg_t *npa)
1448 {
1449 	uint_t slot;
1450 	uint16_t sc;
1451 
1452 	if (npa->npa_argc > 1)
1453 		errx(-1, "Too many arguments");
1454 
1455 	if (npa->npa_argc == 0)
1456 		errx(-1, "Firmware slot number is required");
1457 
1458 	if (npa->npa_isns)
1459 		errx(-1, "Firmware committing not available on a per-namespace "
1460 		    "basis");
1461 
1462 	slot = get_slot_number(npa->npa_argv[0]);
1463 
1464 	if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly)
1465 		errx(-1, "Cannot commit firmware to slot 1: slot is read-only");
1466 
1467 	if (!nvme_firmware_commit(fd, slot, NVME_FWC_SAVE, &sc))
1468 		errx(-1, "Failed to commit firmware to slot %u: %s",
1469 		    slot, nvme_fw_error(errno, sc));
1470 
1471 	if (verbose)
1472 		(void) printf("Firmware committed to slot %u.\n", slot);
1473 
1474 	return (0);
1475 }
1476 
1477 static void
1478 usage_firmware_activate(const char *c_name)
1479 {
1480 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
1481 	    "  Activate firmware in slot <slot>.\n"
1482 	    "  The firmware will be in use after the next system reset.\n",
1483 	    c_name);
1484 }
1485 
1486 static int
1487 do_firmware_activate(int fd, const nvme_process_arg_t *npa)
1488 {
1489 	uint_t slot;
1490 	uint16_t sc;
1491 
1492 	if (npa->npa_argc > 1)
1493 		errx(-1, "Too many arguments");
1494 
1495 	if (npa->npa_argc == 0)
1496 		errx(-1, "Firmware slot number is required");
1497 
1498 	if (npa->npa_isns)
1499 		errx(-1, "Firmware activation not available on a per-namespace "
1500 		    "basis");
1501 
1502 	slot = get_slot_number(npa->npa_argv[0]);
1503 
1504 	if (!nvme_firmware_commit(fd, slot, NVME_FWC_ACTIVATE, &sc))
1505 		errx(-1, "Failed to activate slot %u: %s", slot,
1506 		    nvme_fw_error(errno, sc));
1507 
1508 	if (verbose)
1509 		printf("Slot %u activated: %s.\n", slot,
1510 		    nvme_fw_error(errno, sc));
1511 
1512 	return (0);
1513 }
1514 
1515 /*
1516  * While the NVME_VERSION_ATLEAST macro exists, specifying a version of 1.0
1517  * causes GCC to helpfully flag the -Wtype-limits warning because a uint_t is
1518  * always >= 0. In many cases it's useful to always indicate what version
1519  * something was added in to simplify code (e.g. nvmeadm_print_bit) and we'd
1520  * rather just say it's version 1.0 rather than making folks realize that a
1521  * hardcoded true is equivalent. Therefore we have this function which can't
1522  * trigger this warning today (and adds a minor amount of type safety). If GCC
1523  * or clang get smart enough to see through this, then we'll have to just
1524  * disable the warning for the single minor comparison (and reformat this a bit
1525  * to minimize the impact).
1526  */
1527 boolean_t
1528 nvme_version_check(nvme_version_t *vers, uint_t major, uint_t minor)
1529 {
1530 	if (vers->v_major > major) {
1531 		return (B_TRUE);
1532 	}
1533 
1534 	return (vers->v_major == major && vers->v_minor >= minor);
1535 }
1536