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