xref: /titanic_53/usr/src/cmd/nvmeadm/nvmeadm.c (revision ecee5a1fb22b5ba5e3b9bf76dde2c12d7c15632a)
1*ecee5a1fSHans Rosenfeld /*
2*ecee5a1fSHans Rosenfeld  * This file and its contents are supplied under the terms of the
3*ecee5a1fSHans Rosenfeld  * Common Development and Distribution License ("CDDL"), version 1.0.
4*ecee5a1fSHans Rosenfeld  * You may only use this file in accordance with the terms of version
5*ecee5a1fSHans Rosenfeld  * 1.0 of the CDDL.
6*ecee5a1fSHans Rosenfeld  *
7*ecee5a1fSHans Rosenfeld  * A full copy of the text of the CDDL should have accompanied this
8*ecee5a1fSHans Rosenfeld  * source.  A copy of the CDDL is also available via the Internet at
9*ecee5a1fSHans Rosenfeld  * http://www.illumos.org/license/CDDL.
10*ecee5a1fSHans Rosenfeld  */
11*ecee5a1fSHans Rosenfeld 
12*ecee5a1fSHans Rosenfeld /*
13*ecee5a1fSHans Rosenfeld  * Copyright 2016 Nexenta Systems, Inc.
14*ecee5a1fSHans Rosenfeld  */
15*ecee5a1fSHans Rosenfeld 
16*ecee5a1fSHans Rosenfeld /*
17*ecee5a1fSHans Rosenfeld  * nvmeadm -- NVMe administration utility
18*ecee5a1fSHans Rosenfeld  *
19*ecee5a1fSHans Rosenfeld  * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args]
20*ecee5a1fSHans Rosenfeld  * commands:	list
21*ecee5a1fSHans Rosenfeld  *		identify
22*ecee5a1fSHans Rosenfeld  *		get-logpage <logpage name>
23*ecee5a1fSHans Rosenfeld  *		get-features <feature>[,...]
24*ecee5a1fSHans Rosenfeld  *		format ...
25*ecee5a1fSHans Rosenfeld  *		secure-erase ...
26*ecee5a1fSHans Rosenfeld  *		detach ...
27*ecee5a1fSHans Rosenfeld  *		attach ...
28*ecee5a1fSHans Rosenfeld  *		get-param ...
29*ecee5a1fSHans Rosenfeld  *		set-param ...
30*ecee5a1fSHans Rosenfeld  *		load-firmware ...
31*ecee5a1fSHans Rosenfeld  *		activate-firmware ...
32*ecee5a1fSHans Rosenfeld  *		write-uncorrectable ...
33*ecee5a1fSHans Rosenfeld  *		compare ...
34*ecee5a1fSHans Rosenfeld  *		compare-and-write ...
35*ecee5a1fSHans Rosenfeld  */
36*ecee5a1fSHans Rosenfeld 
37*ecee5a1fSHans Rosenfeld #include <stdio.h>
38*ecee5a1fSHans Rosenfeld #include <stdlib.h>
39*ecee5a1fSHans Rosenfeld #include <strings.h>
40*ecee5a1fSHans Rosenfeld #include <ctype.h>
41*ecee5a1fSHans Rosenfeld #include <err.h>
42*ecee5a1fSHans Rosenfeld #include <sys/sunddi.h>
43*ecee5a1fSHans Rosenfeld #include <libdevinfo.h>
44*ecee5a1fSHans Rosenfeld 
45*ecee5a1fSHans Rosenfeld #include <sys/nvme.h>
46*ecee5a1fSHans Rosenfeld 
47*ecee5a1fSHans Rosenfeld #include "nvmeadm.h"
48*ecee5a1fSHans Rosenfeld 
49*ecee5a1fSHans Rosenfeld typedef struct nvme_process_arg nvme_process_arg_t;
50*ecee5a1fSHans Rosenfeld typedef struct nvme_feature nvme_feature_t;
51*ecee5a1fSHans Rosenfeld typedef struct nvmeadm_cmd nvmeadm_cmd_t;
52*ecee5a1fSHans Rosenfeld 
53*ecee5a1fSHans Rosenfeld struct nvme_process_arg {
54*ecee5a1fSHans Rosenfeld 	int npa_argc;
55*ecee5a1fSHans Rosenfeld 	char **npa_argv;
56*ecee5a1fSHans Rosenfeld 	char *npa_name;
57*ecee5a1fSHans Rosenfeld 	uint32_t npa_nsid;
58*ecee5a1fSHans Rosenfeld 	boolean_t npa_isns;
59*ecee5a1fSHans Rosenfeld 	const nvmeadm_cmd_t *npa_cmd;
60*ecee5a1fSHans Rosenfeld 	di_node_t npa_node;
61*ecee5a1fSHans Rosenfeld 	di_minor_t npa_minor;
62*ecee5a1fSHans Rosenfeld 	char *npa_path;
63*ecee5a1fSHans Rosenfeld 	char *npa_dsk;
64*ecee5a1fSHans Rosenfeld 	nvme_identify_ctrl_t *npa_idctl;
65*ecee5a1fSHans Rosenfeld 	nvme_identify_nsid_t *npa_idns;
66*ecee5a1fSHans Rosenfeld 	nvme_version_t *npa_version;
67*ecee5a1fSHans Rosenfeld };
68*ecee5a1fSHans Rosenfeld 
69*ecee5a1fSHans Rosenfeld struct nvme_feature {
70*ecee5a1fSHans Rosenfeld 	char *f_name;
71*ecee5a1fSHans Rosenfeld 	char *f_short;
72*ecee5a1fSHans Rosenfeld 	uint8_t f_feature;
73*ecee5a1fSHans Rosenfeld 	size_t f_bufsize;
74*ecee5a1fSHans Rosenfeld 	uint_t f_getflags;
75*ecee5a1fSHans Rosenfeld 	int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *);
76*ecee5a1fSHans Rosenfeld 	void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *);
77*ecee5a1fSHans Rosenfeld };
78*ecee5a1fSHans Rosenfeld 
79*ecee5a1fSHans Rosenfeld #define	NVMEADM_CTRL	1
80*ecee5a1fSHans Rosenfeld #define	NVMEADM_NS	2
81*ecee5a1fSHans Rosenfeld #define	NVMEADM_BOTH	(NVMEADM_CTRL | NVMEADM_NS)
82*ecee5a1fSHans Rosenfeld 
83*ecee5a1fSHans Rosenfeld struct nvmeadm_cmd {
84*ecee5a1fSHans Rosenfeld 	char *c_name;
85*ecee5a1fSHans Rosenfeld 	char *c_desc;
86*ecee5a1fSHans Rosenfeld 	char *c_flagdesc;
87*ecee5a1fSHans Rosenfeld 	int (*c_func)(int, const nvme_process_arg_t *);
88*ecee5a1fSHans Rosenfeld 	void (*c_usage)(const char *);
89*ecee5a1fSHans Rosenfeld 	boolean_t c_multi;
90*ecee5a1fSHans Rosenfeld };
91*ecee5a1fSHans Rosenfeld 
92*ecee5a1fSHans Rosenfeld 
93*ecee5a1fSHans Rosenfeld static void usage(const nvmeadm_cmd_t *);
94*ecee5a1fSHans Rosenfeld static void nvme_walk(nvme_process_arg_t *, di_node_t);
95*ecee5a1fSHans Rosenfeld static boolean_t nvme_match(nvme_process_arg_t *);
96*ecee5a1fSHans Rosenfeld 
97*ecee5a1fSHans Rosenfeld static int nvme_process(di_node_t, di_minor_t, void *);
98*ecee5a1fSHans Rosenfeld 
99*ecee5a1fSHans Rosenfeld static int do_list(int, const nvme_process_arg_t *);
100*ecee5a1fSHans Rosenfeld static int do_identify(int, const nvme_process_arg_t *);
101*ecee5a1fSHans Rosenfeld static int do_get_logpage_error(int, const nvme_process_arg_t *);
102*ecee5a1fSHans Rosenfeld static int do_get_logpage_health(int, const nvme_process_arg_t *);
103*ecee5a1fSHans Rosenfeld static int do_get_logpage_fwslot(int, const nvme_process_arg_t *);
104*ecee5a1fSHans Rosenfeld static int do_get_logpage(int, const nvme_process_arg_t *);
105*ecee5a1fSHans Rosenfeld static int do_get_feat_common(int, const nvme_feature_t *,
106*ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *);
107*ecee5a1fSHans Rosenfeld static int do_get_feat_intr_vect(int, const nvme_feature_t *,
108*ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *);
109*ecee5a1fSHans Rosenfeld static int do_get_features(int, const nvme_process_arg_t *);
110*ecee5a1fSHans Rosenfeld static int do_format(int, const nvme_process_arg_t *);
111*ecee5a1fSHans Rosenfeld static int do_secure_erase(int, const nvme_process_arg_t *);
112*ecee5a1fSHans Rosenfeld static int do_attach_detach(int, const nvme_process_arg_t *);
113*ecee5a1fSHans Rosenfeld 
114*ecee5a1fSHans Rosenfeld static void usage_list(const char *);
115*ecee5a1fSHans Rosenfeld static void usage_identify(const char *);
116*ecee5a1fSHans Rosenfeld static void usage_get_logpage(const char *);
117*ecee5a1fSHans Rosenfeld static void usage_get_features(const char *);
118*ecee5a1fSHans Rosenfeld static void usage_format(const char *);
119*ecee5a1fSHans Rosenfeld static void usage_secure_erase(const char *);
120*ecee5a1fSHans Rosenfeld static void usage_attach_detach(const char *);
121*ecee5a1fSHans Rosenfeld 
122*ecee5a1fSHans Rosenfeld int verbose;
123*ecee5a1fSHans Rosenfeld int debug;
124*ecee5a1fSHans Rosenfeld int found;
125*ecee5a1fSHans Rosenfeld static int exitcode;
126*ecee5a1fSHans Rosenfeld 
127*ecee5a1fSHans Rosenfeld static const nvmeadm_cmd_t nvmeadm_cmds[] = {
128*ecee5a1fSHans Rosenfeld 	{
129*ecee5a1fSHans Rosenfeld 		"list",
130*ecee5a1fSHans Rosenfeld 		"list controllers and namespaces",
131*ecee5a1fSHans Rosenfeld 		NULL,
132*ecee5a1fSHans Rosenfeld 		do_list, usage_list, B_TRUE
133*ecee5a1fSHans Rosenfeld 	},
134*ecee5a1fSHans Rosenfeld 	{
135*ecee5a1fSHans Rosenfeld 		"identify",
136*ecee5a1fSHans Rosenfeld 		"identify controllers and/or namespaces",
137*ecee5a1fSHans Rosenfeld 		NULL,
138*ecee5a1fSHans Rosenfeld 		do_identify, usage_identify, B_TRUE
139*ecee5a1fSHans Rosenfeld 	},
140*ecee5a1fSHans Rosenfeld 	{
141*ecee5a1fSHans Rosenfeld 		"get-logpage",
142*ecee5a1fSHans Rosenfeld 		"get a log page from controllers and/or namespaces",
143*ecee5a1fSHans Rosenfeld 		NULL,
144*ecee5a1fSHans Rosenfeld 		do_get_logpage, usage_get_logpage, B_TRUE
145*ecee5a1fSHans Rosenfeld 	},
146*ecee5a1fSHans Rosenfeld 	{
147*ecee5a1fSHans Rosenfeld 		"get-features",
148*ecee5a1fSHans Rosenfeld 		"get features from controllers and/or namespaces",
149*ecee5a1fSHans Rosenfeld 		NULL,
150*ecee5a1fSHans Rosenfeld 		do_get_features, usage_get_features, B_TRUE
151*ecee5a1fSHans Rosenfeld 	},
152*ecee5a1fSHans Rosenfeld 	{
153*ecee5a1fSHans Rosenfeld 		"format",
154*ecee5a1fSHans Rosenfeld 		"format namespace(s) of a controller",
155*ecee5a1fSHans Rosenfeld 		NULL,
156*ecee5a1fSHans Rosenfeld 		do_format, usage_format, B_FALSE
157*ecee5a1fSHans Rosenfeld 	},
158*ecee5a1fSHans Rosenfeld 	{
159*ecee5a1fSHans Rosenfeld 		"secure-erase",
160*ecee5a1fSHans Rosenfeld 		"secure erase namespace(s) of a controller",
161*ecee5a1fSHans Rosenfeld 		"  -c  Do a cryptographic erase.",
162*ecee5a1fSHans Rosenfeld 		do_secure_erase, usage_secure_erase, B_FALSE
163*ecee5a1fSHans Rosenfeld 	},
164*ecee5a1fSHans Rosenfeld 	{
165*ecee5a1fSHans Rosenfeld 		"detach",
166*ecee5a1fSHans Rosenfeld 		"detach blkdev(7d) from namespace(s) of a controller",
167*ecee5a1fSHans Rosenfeld 		NULL,
168*ecee5a1fSHans Rosenfeld 		do_attach_detach, usage_attach_detach, B_FALSE
169*ecee5a1fSHans Rosenfeld 	},
170*ecee5a1fSHans Rosenfeld 	{
171*ecee5a1fSHans Rosenfeld 		"attach",
172*ecee5a1fSHans Rosenfeld 		"attach blkdev(7d) to namespace(s) of a controller",
173*ecee5a1fSHans Rosenfeld 		NULL,
174*ecee5a1fSHans Rosenfeld 		do_attach_detach, usage_attach_detach, B_FALSE
175*ecee5a1fSHans Rosenfeld 	},
176*ecee5a1fSHans Rosenfeld 	{
177*ecee5a1fSHans Rosenfeld 		NULL, NULL, NULL,
178*ecee5a1fSHans Rosenfeld 		NULL, NULL, B_FALSE
179*ecee5a1fSHans Rosenfeld 	}
180*ecee5a1fSHans Rosenfeld };
181*ecee5a1fSHans Rosenfeld 
182*ecee5a1fSHans Rosenfeld static const nvme_feature_t features[] = {
183*ecee5a1fSHans Rosenfeld 	{ "Arbitration", "",
184*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_ARBITRATION, 0, NVMEADM_CTRL,
185*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_arbitration },
186*ecee5a1fSHans Rosenfeld 	{ "Power Management", "",
187*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_POWER_MGMT, 0, NVMEADM_CTRL,
188*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_power_mgmt },
189*ecee5a1fSHans Rosenfeld 	{ "LBA Range Type", "range",
190*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_NS,
191*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_lba_range },
192*ecee5a1fSHans Rosenfeld 	{ "Temperature Threshold", "",
193*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_TEMPERATURE, 0, NVMEADM_CTRL,
194*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_temperature },
195*ecee5a1fSHans Rosenfeld 	{ "Error Recovery", "",
196*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_ERROR, 0, NVMEADM_CTRL,
197*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_error },
198*ecee5a1fSHans Rosenfeld 	{ "Volatile Write Cache", "cache",
199*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_WRITE_CACHE, 0, NVMEADM_CTRL,
200*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_write_cache },
201*ecee5a1fSHans Rosenfeld 	{ "Number of Queues", "queues",
202*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_NQUEUES, 0, NVMEADM_CTRL,
203*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_nqueues },
204*ecee5a1fSHans Rosenfeld 	{ "Interrupt Coalescing", "coalescing",
205*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_INTR_COAL, 0, NVMEADM_CTRL,
206*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_intr_coal },
207*ecee5a1fSHans Rosenfeld 	{ "Interrupt Vector Configuration", "vector",
208*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_INTR_VECT, 0, NVMEADM_CTRL,
209*ecee5a1fSHans Rosenfeld 	    do_get_feat_intr_vect, nvme_print_feat_intr_vect },
210*ecee5a1fSHans Rosenfeld 	{ "Write Atomicity", "atomicity",
211*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_WRITE_ATOM, 0, NVMEADM_CTRL,
212*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_write_atom },
213*ecee5a1fSHans Rosenfeld 	{ "Asynchronous Event Configuration", "event",
214*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_CTRL,
215*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_async_event },
216*ecee5a1fSHans Rosenfeld 	{ "Autonomous Power State Transition", "",
217*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_CTRL,
218*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_auto_pst },
219*ecee5a1fSHans Rosenfeld 	{ "Software Progress Marker", "progress",
220*ecee5a1fSHans Rosenfeld 	    NVME_FEAT_PROGRESS, 0, NVMEADM_CTRL,
221*ecee5a1fSHans Rosenfeld 	    do_get_feat_common, nvme_print_feat_progress },
222*ecee5a1fSHans Rosenfeld 	{ NULL, NULL, 0, 0, B_FALSE, NULL }
223*ecee5a1fSHans Rosenfeld };
224*ecee5a1fSHans Rosenfeld 
225*ecee5a1fSHans Rosenfeld 
226*ecee5a1fSHans Rosenfeld int
227*ecee5a1fSHans Rosenfeld main(int argc, char **argv)
228*ecee5a1fSHans Rosenfeld {
229*ecee5a1fSHans Rosenfeld 	int c;
230*ecee5a1fSHans Rosenfeld 	extern int optind;
231*ecee5a1fSHans Rosenfeld 	const nvmeadm_cmd_t *cmd;
232*ecee5a1fSHans Rosenfeld 	di_node_t node;
233*ecee5a1fSHans Rosenfeld 	nvme_process_arg_t npa = { 0 };
234*ecee5a1fSHans Rosenfeld 	int help = 0;
235*ecee5a1fSHans Rosenfeld 	char *tmp, *lasts = NULL;
236*ecee5a1fSHans Rosenfeld 
237*ecee5a1fSHans Rosenfeld 	while ((c = getopt(argc, argv, "dhv")) != -1) {
238*ecee5a1fSHans Rosenfeld 		switch (c) {
239*ecee5a1fSHans Rosenfeld 		case 'd':
240*ecee5a1fSHans Rosenfeld 			debug++;
241*ecee5a1fSHans Rosenfeld 			break;
242*ecee5a1fSHans Rosenfeld 		case 'v':
243*ecee5a1fSHans Rosenfeld 			verbose++;
244*ecee5a1fSHans Rosenfeld 			break;
245*ecee5a1fSHans Rosenfeld 		case 'h':
246*ecee5a1fSHans Rosenfeld 			help++;
247*ecee5a1fSHans Rosenfeld 			break;
248*ecee5a1fSHans Rosenfeld 		case '?':
249*ecee5a1fSHans Rosenfeld 			usage(NULL);
250*ecee5a1fSHans Rosenfeld 			exit(-1);
251*ecee5a1fSHans Rosenfeld 		}
252*ecee5a1fSHans Rosenfeld 	}
253*ecee5a1fSHans Rosenfeld 
254*ecee5a1fSHans Rosenfeld 	if (optind == argc) {
255*ecee5a1fSHans Rosenfeld 		usage(NULL);
256*ecee5a1fSHans Rosenfeld 		if (help)
257*ecee5a1fSHans Rosenfeld 			exit(0);
258*ecee5a1fSHans Rosenfeld 		else
259*ecee5a1fSHans Rosenfeld 			exit(-1);
260*ecee5a1fSHans Rosenfeld 	}
261*ecee5a1fSHans Rosenfeld 
262*ecee5a1fSHans Rosenfeld 	/* Look up the specified command in the command table. */
263*ecee5a1fSHans Rosenfeld 	for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
264*ecee5a1fSHans Rosenfeld 		if (strcmp(cmd->c_name, argv[optind]) == 0)
265*ecee5a1fSHans Rosenfeld 			break;
266*ecee5a1fSHans Rosenfeld 
267*ecee5a1fSHans Rosenfeld 	if (cmd->c_name == NULL) {
268*ecee5a1fSHans Rosenfeld 		usage(NULL);
269*ecee5a1fSHans Rosenfeld 		exit(-1);
270*ecee5a1fSHans Rosenfeld 	}
271*ecee5a1fSHans Rosenfeld 
272*ecee5a1fSHans Rosenfeld 	if (help) {
273*ecee5a1fSHans Rosenfeld 		usage(cmd);
274*ecee5a1fSHans Rosenfeld 		exit(0);
275*ecee5a1fSHans Rosenfeld 	}
276*ecee5a1fSHans Rosenfeld 
277*ecee5a1fSHans Rosenfeld 	npa.npa_cmd = cmd;
278*ecee5a1fSHans Rosenfeld 
279*ecee5a1fSHans Rosenfeld 	optind++;
280*ecee5a1fSHans Rosenfeld 
281*ecee5a1fSHans Rosenfeld 	/*
282*ecee5a1fSHans Rosenfeld 	 * All commands but "list" require a ctl/ns argument.
283*ecee5a1fSHans Rosenfeld 	 */
284*ecee5a1fSHans Rosenfeld 	if ((optind == argc || (strncmp(argv[optind], "nvme", 4) != 0)) &&
285*ecee5a1fSHans Rosenfeld 	    cmd->c_func != do_list) {
286*ecee5a1fSHans Rosenfeld 		warnx("missing controller/namespace name");
287*ecee5a1fSHans Rosenfeld 		usage(cmd);
288*ecee5a1fSHans Rosenfeld 		exit(-1);
289*ecee5a1fSHans Rosenfeld 	}
290*ecee5a1fSHans Rosenfeld 
291*ecee5a1fSHans Rosenfeld 
292*ecee5a1fSHans Rosenfeld 	/* Store the remaining arguments for use by the command. */
293*ecee5a1fSHans Rosenfeld 	npa.npa_argc = argc - optind - 1;
294*ecee5a1fSHans Rosenfeld 	npa.npa_argv = &argv[optind + 1];
295*ecee5a1fSHans Rosenfeld 
296*ecee5a1fSHans Rosenfeld 	/*
297*ecee5a1fSHans Rosenfeld 	 * Make sure we're not running commands on multiple controllers that
298*ecee5a1fSHans Rosenfeld 	 * aren't allowed to do that.
299*ecee5a1fSHans Rosenfeld 	 */
300*ecee5a1fSHans Rosenfeld 	if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL &&
301*ecee5a1fSHans Rosenfeld 	    cmd->c_multi == B_FALSE) {
302*ecee5a1fSHans Rosenfeld 		warnx("%s not allowed on multiple controllers",
303*ecee5a1fSHans Rosenfeld 		    cmd->c_name);
304*ecee5a1fSHans Rosenfeld 		usage(cmd);
305*ecee5a1fSHans Rosenfeld 		exit(-1);
306*ecee5a1fSHans Rosenfeld 	}
307*ecee5a1fSHans Rosenfeld 
308*ecee5a1fSHans Rosenfeld 	/*
309*ecee5a1fSHans Rosenfeld 	 * Get controller/namespace arguments and run command.
310*ecee5a1fSHans Rosenfeld 	 */
311*ecee5a1fSHans Rosenfeld 	npa.npa_name = strtok_r(argv[optind], ",", &lasts);
312*ecee5a1fSHans Rosenfeld 	do {
313*ecee5a1fSHans Rosenfeld 		if (npa.npa_name != NULL) {
314*ecee5a1fSHans Rosenfeld 			tmp = strchr(npa.npa_name, '/');
315*ecee5a1fSHans Rosenfeld 			if (tmp != NULL) {
316*ecee5a1fSHans Rosenfeld 				unsigned long nsid;
317*ecee5a1fSHans Rosenfeld 				*tmp++ = '\0';
318*ecee5a1fSHans Rosenfeld 				errno = 0;
319*ecee5a1fSHans Rosenfeld 				nsid = strtoul(tmp, NULL, 10);
320*ecee5a1fSHans Rosenfeld 				if (nsid >= UINT32_MAX || errno != 0) {
321*ecee5a1fSHans Rosenfeld 					warn("invalid namespace %s", tmp);
322*ecee5a1fSHans Rosenfeld 					exitcode--;
323*ecee5a1fSHans Rosenfeld 					continue;
324*ecee5a1fSHans Rosenfeld 				}
325*ecee5a1fSHans Rosenfeld 				if (nsid == 0) {
326*ecee5a1fSHans Rosenfeld 					warnx("invalid namespace %s", tmp);
327*ecee5a1fSHans Rosenfeld 					exitcode--;
328*ecee5a1fSHans Rosenfeld 					continue;
329*ecee5a1fSHans Rosenfeld 				}
330*ecee5a1fSHans Rosenfeld 				npa.npa_nsid = nsid;
331*ecee5a1fSHans Rosenfeld 				npa.npa_isns = B_TRUE;
332*ecee5a1fSHans Rosenfeld 			}
333*ecee5a1fSHans Rosenfeld 		}
334*ecee5a1fSHans Rosenfeld 
335*ecee5a1fSHans Rosenfeld 		if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL)
336*ecee5a1fSHans Rosenfeld 			err(-1, "failed to initialize libdevinfo");
337*ecee5a1fSHans Rosenfeld 		nvme_walk(&npa, node);
338*ecee5a1fSHans Rosenfeld 		di_fini(node);
339*ecee5a1fSHans Rosenfeld 
340*ecee5a1fSHans Rosenfeld 		if (found == 0) {
341*ecee5a1fSHans Rosenfeld 			if (npa.npa_name != NULL) {
342*ecee5a1fSHans Rosenfeld 				warnx("%s%.*s%.*d: no such controller or "
343*ecee5a1fSHans Rosenfeld 				    "namespace", npa.npa_name,
344*ecee5a1fSHans Rosenfeld 				    npa.npa_nsid > 0 ? -1 : 0, "/",
345*ecee5a1fSHans Rosenfeld 				    npa.npa_nsid > 0 ? -1 : 0, npa.npa_nsid);
346*ecee5a1fSHans Rosenfeld 			} else {
347*ecee5a1fSHans Rosenfeld 				warnx("no controllers found");
348*ecee5a1fSHans Rosenfeld 			}
349*ecee5a1fSHans Rosenfeld 			exitcode--;
350*ecee5a1fSHans Rosenfeld 		}
351*ecee5a1fSHans Rosenfeld 		found = 0;
352*ecee5a1fSHans Rosenfeld 		npa.npa_name = strtok_r(NULL, ",", &lasts);
353*ecee5a1fSHans Rosenfeld 	} while (npa.npa_name != NULL);
354*ecee5a1fSHans Rosenfeld 
355*ecee5a1fSHans Rosenfeld 	exit(exitcode);
356*ecee5a1fSHans Rosenfeld }
357*ecee5a1fSHans Rosenfeld 
358*ecee5a1fSHans Rosenfeld static void
359*ecee5a1fSHans Rosenfeld usage(const nvmeadm_cmd_t *cmd)
360*ecee5a1fSHans Rosenfeld {
361*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "usage:\n");
362*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "  %s -h %s\n", getprogname(),
363*ecee5a1fSHans Rosenfeld 	    cmd != NULL ? cmd->c_name : "[<command>]");
364*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "  %s [-dv] ", getprogname());
365*ecee5a1fSHans Rosenfeld 
366*ecee5a1fSHans Rosenfeld 	if (cmd != NULL) {
367*ecee5a1fSHans Rosenfeld 		cmd->c_usage(cmd->c_name);
368*ecee5a1fSHans Rosenfeld 	} else {
369*ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr,
370*ecee5a1fSHans Rosenfeld 		    "<command> <ctl>[/<ns>][,...] [<args>]\n");
371*ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr,
372*ecee5a1fSHans Rosenfeld 		    "\n  Manage NVMe controllers and namespaces.\n");
373*ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr, "\ncommands:\n");
374*ecee5a1fSHans Rosenfeld 
375*ecee5a1fSHans Rosenfeld 		for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
376*ecee5a1fSHans Rosenfeld 			(void) fprintf(stderr, "  %-15s - %s\n",
377*ecee5a1fSHans Rosenfeld 			    cmd->c_name, cmd->c_desc);
378*ecee5a1fSHans Rosenfeld 	}
379*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "\nflags:\n"
380*ecee5a1fSHans Rosenfeld 	    "  -h  print usage information\n"
381*ecee5a1fSHans Rosenfeld 	    "  -d  print information useful for debugging %s\n"
382*ecee5a1fSHans Rosenfeld 	    "  -v  print verbose information\n", getprogname());
383*ecee5a1fSHans Rosenfeld 	if (cmd != NULL && cmd->c_flagdesc != NULL)
384*ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
385*ecee5a1fSHans Rosenfeld }
386*ecee5a1fSHans Rosenfeld 
387*ecee5a1fSHans Rosenfeld static boolean_t
388*ecee5a1fSHans Rosenfeld nvme_match(nvme_process_arg_t *npa)
389*ecee5a1fSHans Rosenfeld {
390*ecee5a1fSHans Rosenfeld 	char *name;
391*ecee5a1fSHans Rosenfeld 	uint32_t nsid = 0;
392*ecee5a1fSHans Rosenfeld 
393*ecee5a1fSHans Rosenfeld 	if (npa->npa_name == NULL)
394*ecee5a1fSHans Rosenfeld 		return (B_TRUE);
395*ecee5a1fSHans Rosenfeld 
396*ecee5a1fSHans Rosenfeld 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
397*ecee5a1fSHans Rosenfeld 	    di_instance(npa->npa_node)) < 0)
398*ecee5a1fSHans Rosenfeld 		err(-1, "nvme_match()");
399*ecee5a1fSHans Rosenfeld 
400*ecee5a1fSHans Rosenfeld 	if (strcmp(name, npa->npa_name) != 0) {
401*ecee5a1fSHans Rosenfeld 		free(name);
402*ecee5a1fSHans Rosenfeld 		return (B_FALSE);
403*ecee5a1fSHans Rosenfeld 	}
404*ecee5a1fSHans Rosenfeld 
405*ecee5a1fSHans Rosenfeld 	free(name);
406*ecee5a1fSHans Rosenfeld 
407*ecee5a1fSHans Rosenfeld 	if (npa->npa_isns) {
408*ecee5a1fSHans Rosenfeld 		if (npa->npa_nsid == 0)
409*ecee5a1fSHans Rosenfeld 			return (B_TRUE);
410*ecee5a1fSHans Rosenfeld 		nsid = strtoul(di_minor_name(npa->npa_minor), NULL, 10);
411*ecee5a1fSHans Rosenfeld 	}
412*ecee5a1fSHans Rosenfeld 
413*ecee5a1fSHans Rosenfeld 	if (npa->npa_isns && npa->npa_nsid != nsid)
414*ecee5a1fSHans Rosenfeld 		return (B_FALSE);
415*ecee5a1fSHans Rosenfeld 
416*ecee5a1fSHans Rosenfeld 	return (B_TRUE);
417*ecee5a1fSHans Rosenfeld }
418*ecee5a1fSHans Rosenfeld 
419*ecee5a1fSHans Rosenfeld char *
420*ecee5a1fSHans Rosenfeld nvme_dskname(const nvme_process_arg_t *npa)
421*ecee5a1fSHans Rosenfeld {
422*ecee5a1fSHans Rosenfeld 	char *path = NULL;
423*ecee5a1fSHans Rosenfeld 	di_node_t child;
424*ecee5a1fSHans Rosenfeld 	di_dim_t dim;
425*ecee5a1fSHans Rosenfeld 	char *addr;
426*ecee5a1fSHans Rosenfeld 
427*ecee5a1fSHans Rosenfeld 	dim = di_dim_init();
428*ecee5a1fSHans Rosenfeld 
429*ecee5a1fSHans Rosenfeld 	for (child = di_child_node(npa->npa_node);
430*ecee5a1fSHans Rosenfeld 	    child != DI_NODE_NIL;
431*ecee5a1fSHans Rosenfeld 	    child = di_sibling_node(child)) {
432*ecee5a1fSHans Rosenfeld 		addr = di_bus_addr(child);
433*ecee5a1fSHans Rosenfeld 		if (addr == NULL)
434*ecee5a1fSHans Rosenfeld 			continue;
435*ecee5a1fSHans Rosenfeld 
436*ecee5a1fSHans Rosenfeld 		if (addr[0] == 'w')
437*ecee5a1fSHans Rosenfeld 			addr++;
438*ecee5a1fSHans Rosenfeld 
439*ecee5a1fSHans Rosenfeld 		if (strncasecmp(addr, di_minor_name(npa->npa_minor),
440*ecee5a1fSHans Rosenfeld 		    strchrnul(addr, ',') - addr) != 0)
441*ecee5a1fSHans Rosenfeld 			continue;
442*ecee5a1fSHans Rosenfeld 
443*ecee5a1fSHans Rosenfeld 		path = di_dim_path_dev(dim, di_driver_name(child),
444*ecee5a1fSHans Rosenfeld 		    di_instance(child), "c");
445*ecee5a1fSHans Rosenfeld 
446*ecee5a1fSHans Rosenfeld 		if (path != NULL) {
447*ecee5a1fSHans Rosenfeld 			path[strlen(path) - 2] = '\0';
448*ecee5a1fSHans Rosenfeld 			path = strrchr(path, '/') + 1;
449*ecee5a1fSHans Rosenfeld 			if (path != NULL) {
450*ecee5a1fSHans Rosenfeld 				path = strdup(path);
451*ecee5a1fSHans Rosenfeld 				if (path == NULL)
452*ecee5a1fSHans Rosenfeld 					err(-1, "nvme_dskname");
453*ecee5a1fSHans Rosenfeld 			}
454*ecee5a1fSHans Rosenfeld 		}
455*ecee5a1fSHans Rosenfeld 
456*ecee5a1fSHans Rosenfeld 		break;
457*ecee5a1fSHans Rosenfeld 	}
458*ecee5a1fSHans Rosenfeld 
459*ecee5a1fSHans Rosenfeld 	di_dim_fini(dim);
460*ecee5a1fSHans Rosenfeld 	return (path);
461*ecee5a1fSHans Rosenfeld }
462*ecee5a1fSHans Rosenfeld 
463*ecee5a1fSHans Rosenfeld static int
464*ecee5a1fSHans Rosenfeld nvme_process(di_node_t node, di_minor_t minor, void *arg)
465*ecee5a1fSHans Rosenfeld {
466*ecee5a1fSHans Rosenfeld 	nvme_process_arg_t *npa = arg;
467*ecee5a1fSHans Rosenfeld 	int fd;
468*ecee5a1fSHans Rosenfeld 
469*ecee5a1fSHans Rosenfeld 	npa->npa_node = node;
470*ecee5a1fSHans Rosenfeld 	npa->npa_minor = minor;
471*ecee5a1fSHans Rosenfeld 
472*ecee5a1fSHans Rosenfeld 	if (!nvme_match(npa))
473*ecee5a1fSHans Rosenfeld 		return (DI_WALK_CONTINUE);
474*ecee5a1fSHans Rosenfeld 
475*ecee5a1fSHans Rosenfeld 	if ((fd = nvme_open(minor)) < 0)
476*ecee5a1fSHans Rosenfeld 		return (DI_WALK_CONTINUE);
477*ecee5a1fSHans Rosenfeld 
478*ecee5a1fSHans Rosenfeld 	found++;
479*ecee5a1fSHans Rosenfeld 
480*ecee5a1fSHans Rosenfeld 	npa->npa_path = di_devfs_path(node);
481*ecee5a1fSHans Rosenfeld 	if (npa->npa_path == NULL)
482*ecee5a1fSHans Rosenfeld 		goto out;
483*ecee5a1fSHans Rosenfeld 
484*ecee5a1fSHans Rosenfeld 	npa->npa_version = nvme_version(fd);
485*ecee5a1fSHans Rosenfeld 	if (npa->npa_version == NULL)
486*ecee5a1fSHans Rosenfeld 		goto out;
487*ecee5a1fSHans Rosenfeld 
488*ecee5a1fSHans Rosenfeld 	npa->npa_idctl = nvme_identify_ctrl(fd);
489*ecee5a1fSHans Rosenfeld 	if (npa->npa_idctl == NULL)
490*ecee5a1fSHans Rosenfeld 		goto out;
491*ecee5a1fSHans Rosenfeld 
492*ecee5a1fSHans Rosenfeld 	npa->npa_idns = nvme_identify_nsid(fd);
493*ecee5a1fSHans Rosenfeld 	if (npa->npa_idns == NULL)
494*ecee5a1fSHans Rosenfeld 		goto out;
495*ecee5a1fSHans Rosenfeld 
496*ecee5a1fSHans Rosenfeld 	if (npa->npa_isns)
497*ecee5a1fSHans Rosenfeld 		npa->npa_dsk = nvme_dskname(npa);
498*ecee5a1fSHans Rosenfeld 
499*ecee5a1fSHans Rosenfeld 	exitcode += npa->npa_cmd->c_func(fd, npa);
500*ecee5a1fSHans Rosenfeld 
501*ecee5a1fSHans Rosenfeld out:
502*ecee5a1fSHans Rosenfeld 	di_devfs_path_free(npa->npa_path);
503*ecee5a1fSHans Rosenfeld 	free(npa->npa_dsk);
504*ecee5a1fSHans Rosenfeld 	free(npa->npa_version);
505*ecee5a1fSHans Rosenfeld 	free(npa->npa_idctl);
506*ecee5a1fSHans Rosenfeld 	free(npa->npa_idns);
507*ecee5a1fSHans Rosenfeld 
508*ecee5a1fSHans Rosenfeld 	npa->npa_version = NULL;
509*ecee5a1fSHans Rosenfeld 	npa->npa_idctl = NULL;
510*ecee5a1fSHans Rosenfeld 	npa->npa_idns = NULL;
511*ecee5a1fSHans Rosenfeld 
512*ecee5a1fSHans Rosenfeld 	nvme_close(fd);
513*ecee5a1fSHans Rosenfeld 
514*ecee5a1fSHans Rosenfeld 	return (DI_WALK_CONTINUE);
515*ecee5a1fSHans Rosenfeld }
516*ecee5a1fSHans Rosenfeld 
517*ecee5a1fSHans Rosenfeld static void
518*ecee5a1fSHans Rosenfeld nvme_walk(nvme_process_arg_t *npa, di_node_t node)
519*ecee5a1fSHans Rosenfeld {
520*ecee5a1fSHans Rosenfeld 	char *minor_nodetype = DDI_NT_NVME_NEXUS;
521*ecee5a1fSHans Rosenfeld 
522*ecee5a1fSHans Rosenfeld 	if (npa->npa_isns)
523*ecee5a1fSHans Rosenfeld 		minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT;
524*ecee5a1fSHans Rosenfeld 
525*ecee5a1fSHans Rosenfeld 	(void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process);
526*ecee5a1fSHans Rosenfeld }
527*ecee5a1fSHans Rosenfeld 
528*ecee5a1fSHans Rosenfeld static void
529*ecee5a1fSHans Rosenfeld usage_list(const char *c_name)
530*ecee5a1fSHans Rosenfeld {
531*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s [<ctl>[/<ns>][,...]\n\n"
532*ecee5a1fSHans Rosenfeld 	    "  List NVMe controllers and their namespaces. If no "
533*ecee5a1fSHans Rosenfeld 	    "controllers and/or name-\n  spaces are specified, all "
534*ecee5a1fSHans Rosenfeld 	    "controllers and namespaces in the system will be\n  "
535*ecee5a1fSHans Rosenfeld 	    "listed.\n", c_name);
536*ecee5a1fSHans Rosenfeld }
537*ecee5a1fSHans Rosenfeld 
538*ecee5a1fSHans Rosenfeld static int
539*ecee5a1fSHans Rosenfeld do_list_nsid(int fd, const nvme_process_arg_t *npa)
540*ecee5a1fSHans Rosenfeld {
541*ecee5a1fSHans Rosenfeld 	_NOTE(ARGUNUSED(fd));
542*ecee5a1fSHans Rosenfeld 
543*ecee5a1fSHans Rosenfeld 	(void) printf("  %s/%s (%s): ", npa->npa_name,
544*ecee5a1fSHans Rosenfeld 	    di_minor_name(npa->npa_minor),
545*ecee5a1fSHans Rosenfeld 	    npa->npa_dsk != NULL ? npa->npa_dsk : "unattached");
546*ecee5a1fSHans Rosenfeld 	nvme_print_nsid_summary(npa->npa_idns);
547*ecee5a1fSHans Rosenfeld 
548*ecee5a1fSHans Rosenfeld 	return (0);
549*ecee5a1fSHans Rosenfeld }
550*ecee5a1fSHans Rosenfeld 
551*ecee5a1fSHans Rosenfeld static int
552*ecee5a1fSHans Rosenfeld do_list(int fd, const nvme_process_arg_t *npa)
553*ecee5a1fSHans Rosenfeld {
554*ecee5a1fSHans Rosenfeld 	_NOTE(ARGUNUSED(fd));
555*ecee5a1fSHans Rosenfeld 
556*ecee5a1fSHans Rosenfeld 	nvme_process_arg_t ns_npa = { 0 };
557*ecee5a1fSHans Rosenfeld 	nvmeadm_cmd_t cmd = { 0 };
558*ecee5a1fSHans Rosenfeld 	char *name;
559*ecee5a1fSHans Rosenfeld 
560*ecee5a1fSHans Rosenfeld 	if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
561*ecee5a1fSHans Rosenfeld 	    di_instance(npa->npa_node)) < 0)
562*ecee5a1fSHans Rosenfeld 		err(-1, "do_list()");
563*ecee5a1fSHans Rosenfeld 
564*ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", name);
565*ecee5a1fSHans Rosenfeld 	nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version);
566*ecee5a1fSHans Rosenfeld 
567*ecee5a1fSHans Rosenfeld 	ns_npa.npa_name = name;
568*ecee5a1fSHans Rosenfeld 	ns_npa.npa_isns = B_TRUE;
569*ecee5a1fSHans Rosenfeld 	ns_npa.npa_nsid = npa->npa_nsid;
570*ecee5a1fSHans Rosenfeld 	cmd = *(npa->npa_cmd);
571*ecee5a1fSHans Rosenfeld 	cmd.c_func = do_list_nsid;
572*ecee5a1fSHans Rosenfeld 	ns_npa.npa_cmd = &cmd;
573*ecee5a1fSHans Rosenfeld 
574*ecee5a1fSHans Rosenfeld 	nvme_walk(&ns_npa, npa->npa_node);
575*ecee5a1fSHans Rosenfeld 
576*ecee5a1fSHans Rosenfeld 	free(name);
577*ecee5a1fSHans Rosenfeld 
578*ecee5a1fSHans Rosenfeld 	return (exitcode);
579*ecee5a1fSHans Rosenfeld }
580*ecee5a1fSHans Rosenfeld 
581*ecee5a1fSHans Rosenfeld static void
582*ecee5a1fSHans Rosenfeld usage_identify(const char *c_name)
583*ecee5a1fSHans Rosenfeld {
584*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n"
585*ecee5a1fSHans Rosenfeld 	    "  Print detailed information about the specified NVMe "
586*ecee5a1fSHans Rosenfeld 	    "controllers and/or name-\n  spaces.\n", c_name);
587*ecee5a1fSHans Rosenfeld }
588*ecee5a1fSHans Rosenfeld 
589*ecee5a1fSHans Rosenfeld static int
590*ecee5a1fSHans Rosenfeld do_identify(int fd, const nvme_process_arg_t *npa)
591*ecee5a1fSHans Rosenfeld {
592*ecee5a1fSHans Rosenfeld 	if (npa->npa_nsid == 0) {
593*ecee5a1fSHans Rosenfeld 		nvme_capabilities_t *cap;
594*ecee5a1fSHans Rosenfeld 
595*ecee5a1fSHans Rosenfeld 		cap = nvme_capabilities(fd);
596*ecee5a1fSHans Rosenfeld 		if (cap == NULL)
597*ecee5a1fSHans Rosenfeld 			return (-1);
598*ecee5a1fSHans Rosenfeld 
599*ecee5a1fSHans Rosenfeld 		(void) printf("%s: ", npa->npa_name);
600*ecee5a1fSHans Rosenfeld 		nvme_print_identify_ctrl(npa->npa_idctl, cap,
601*ecee5a1fSHans Rosenfeld 		    npa->npa_version);
602*ecee5a1fSHans Rosenfeld 
603*ecee5a1fSHans Rosenfeld 		free(cap);
604*ecee5a1fSHans Rosenfeld 	} else {
605*ecee5a1fSHans Rosenfeld 		(void) printf("%s/%s: ", npa->npa_name,
606*ecee5a1fSHans Rosenfeld 		    di_minor_name(npa->npa_minor));
607*ecee5a1fSHans Rosenfeld 		nvme_print_identify_nsid(npa->npa_idns,
608*ecee5a1fSHans Rosenfeld 		    npa->npa_version);
609*ecee5a1fSHans Rosenfeld 	}
610*ecee5a1fSHans Rosenfeld 
611*ecee5a1fSHans Rosenfeld 	return (0);
612*ecee5a1fSHans Rosenfeld }
613*ecee5a1fSHans Rosenfeld 
614*ecee5a1fSHans Rosenfeld static void
615*ecee5a1fSHans Rosenfeld usage_get_logpage(const char *c_name)
616*ecee5a1fSHans Rosenfeld {
617*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n"
618*ecee5a1fSHans Rosenfeld 	    "  Print the specified log page of the specified NVMe "
619*ecee5a1fSHans Rosenfeld 	    "controllers and/or name-\n  spaces. Supported log pages "
620*ecee5a1fSHans Rosenfeld 	    "are error, health, and firmware.\n", c_name);
621*ecee5a1fSHans Rosenfeld }
622*ecee5a1fSHans Rosenfeld 
623*ecee5a1fSHans Rosenfeld static int
624*ecee5a1fSHans Rosenfeld do_get_logpage_error(int fd, const nvme_process_arg_t *npa)
625*ecee5a1fSHans Rosenfeld {
626*ecee5a1fSHans Rosenfeld 	int nlog = npa->npa_idctl->id_elpe + 1;
627*ecee5a1fSHans Rosenfeld 	size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog;
628*ecee5a1fSHans Rosenfeld 	nvme_error_log_entry_t *elog;
629*ecee5a1fSHans Rosenfeld 
630*ecee5a1fSHans Rosenfeld 	if (npa->npa_nsid != 0)
631*ecee5a1fSHans Rosenfeld 		errx(-1, "Error Log not available on a per-namespace basis");
632*ecee5a1fSHans Rosenfeld 
633*ecee5a1fSHans Rosenfeld 	elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize);
634*ecee5a1fSHans Rosenfeld 
635*ecee5a1fSHans Rosenfeld 	if (elog == NULL)
636*ecee5a1fSHans Rosenfeld 		return (-1);
637*ecee5a1fSHans Rosenfeld 
638*ecee5a1fSHans Rosenfeld 	nlog = bufsize / sizeof (nvme_error_log_entry_t);
639*ecee5a1fSHans Rosenfeld 
640*ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", npa->npa_name);
641*ecee5a1fSHans Rosenfeld 	nvme_print_error_log(nlog, elog);
642*ecee5a1fSHans Rosenfeld 
643*ecee5a1fSHans Rosenfeld 	free(elog);
644*ecee5a1fSHans Rosenfeld 
645*ecee5a1fSHans Rosenfeld 	return (0);
646*ecee5a1fSHans Rosenfeld }
647*ecee5a1fSHans Rosenfeld 
648*ecee5a1fSHans Rosenfeld static int
649*ecee5a1fSHans Rosenfeld do_get_logpage_health(int fd, const nvme_process_arg_t *npa)
650*ecee5a1fSHans Rosenfeld {
651*ecee5a1fSHans Rosenfeld 	size_t bufsize = sizeof (nvme_health_log_t);
652*ecee5a1fSHans Rosenfeld 	nvme_health_log_t *hlog;
653*ecee5a1fSHans Rosenfeld 
654*ecee5a1fSHans Rosenfeld 	if (npa->npa_nsid != 0) {
655*ecee5a1fSHans Rosenfeld 		if (npa->npa_idctl->id_lpa.lp_smart == 0)
656*ecee5a1fSHans Rosenfeld 			errx(-1, "SMART/Health information not available "
657*ecee5a1fSHans Rosenfeld 			    "on a per-namespace basis on this controller");
658*ecee5a1fSHans Rosenfeld 	}
659*ecee5a1fSHans Rosenfeld 
660*ecee5a1fSHans Rosenfeld 	hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
661*ecee5a1fSHans Rosenfeld 
662*ecee5a1fSHans Rosenfeld 	if (hlog == NULL)
663*ecee5a1fSHans Rosenfeld 		return (-1);
664*ecee5a1fSHans Rosenfeld 
665*ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", npa->npa_name);
666*ecee5a1fSHans Rosenfeld 	nvme_print_health_log(hlog, npa->npa_idctl);
667*ecee5a1fSHans Rosenfeld 
668*ecee5a1fSHans Rosenfeld 	free(hlog);
669*ecee5a1fSHans Rosenfeld 
670*ecee5a1fSHans Rosenfeld 	return (0);
671*ecee5a1fSHans Rosenfeld }
672*ecee5a1fSHans Rosenfeld 
673*ecee5a1fSHans Rosenfeld static int
674*ecee5a1fSHans Rosenfeld do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa)
675*ecee5a1fSHans Rosenfeld {
676*ecee5a1fSHans Rosenfeld 	size_t bufsize = sizeof (nvme_fwslot_log_t);
677*ecee5a1fSHans Rosenfeld 	nvme_fwslot_log_t *fwlog;
678*ecee5a1fSHans Rosenfeld 
679*ecee5a1fSHans Rosenfeld 	if (npa->npa_nsid != 0)
680*ecee5a1fSHans Rosenfeld 		errx(-1, "Firmware Slot information not available on a "
681*ecee5a1fSHans Rosenfeld 		    "per-namespace basis");
682*ecee5a1fSHans Rosenfeld 
683*ecee5a1fSHans Rosenfeld 	fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize);
684*ecee5a1fSHans Rosenfeld 
685*ecee5a1fSHans Rosenfeld 	if (fwlog == NULL)
686*ecee5a1fSHans Rosenfeld 		return (-1);
687*ecee5a1fSHans Rosenfeld 
688*ecee5a1fSHans Rosenfeld 	(void) printf("%s: ", npa->npa_name);
689*ecee5a1fSHans Rosenfeld 	nvme_print_fwslot_log(fwlog);
690*ecee5a1fSHans Rosenfeld 
691*ecee5a1fSHans Rosenfeld 	free(fwlog);
692*ecee5a1fSHans Rosenfeld 
693*ecee5a1fSHans Rosenfeld 	return (0);
694*ecee5a1fSHans Rosenfeld }
695*ecee5a1fSHans Rosenfeld 
696*ecee5a1fSHans Rosenfeld static int
697*ecee5a1fSHans Rosenfeld do_get_logpage(int fd, const nvme_process_arg_t *npa)
698*ecee5a1fSHans Rosenfeld {
699*ecee5a1fSHans Rosenfeld 	int ret = 0;
700*ecee5a1fSHans Rosenfeld 	int (*func)(int, const nvme_process_arg_t *);
701*ecee5a1fSHans Rosenfeld 
702*ecee5a1fSHans Rosenfeld 	if (npa->npa_argc < 1) {
703*ecee5a1fSHans Rosenfeld 		warnx("missing logpage name");
704*ecee5a1fSHans Rosenfeld 		usage(npa->npa_cmd);
705*ecee5a1fSHans Rosenfeld 		exit(-1);
706*ecee5a1fSHans Rosenfeld 	}
707*ecee5a1fSHans Rosenfeld 
708*ecee5a1fSHans Rosenfeld 	if (strcmp(npa->npa_argv[0], "error") == 0)
709*ecee5a1fSHans Rosenfeld 		func = do_get_logpage_error;
710*ecee5a1fSHans Rosenfeld 	else if (strcmp(npa->npa_argv[0], "health") == 0)
711*ecee5a1fSHans Rosenfeld 		func = do_get_logpage_health;
712*ecee5a1fSHans Rosenfeld 	else if (strcmp(npa->npa_argv[0], "firmware") == 0)
713*ecee5a1fSHans Rosenfeld 		func = do_get_logpage_fwslot;
714*ecee5a1fSHans Rosenfeld 	else
715*ecee5a1fSHans Rosenfeld 		errx(-1, "invalid log page: %s", npa->npa_argv[0]);
716*ecee5a1fSHans Rosenfeld 
717*ecee5a1fSHans Rosenfeld 	ret = func(fd, npa);
718*ecee5a1fSHans Rosenfeld 	return (ret);
719*ecee5a1fSHans Rosenfeld }
720*ecee5a1fSHans Rosenfeld 
721*ecee5a1fSHans Rosenfeld static void
722*ecee5a1fSHans Rosenfeld usage_get_features(const char *c_name)
723*ecee5a1fSHans Rosenfeld {
724*ecee5a1fSHans Rosenfeld 	const nvme_feature_t *feat;
725*ecee5a1fSHans Rosenfeld 
726*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
727*ecee5a1fSHans Rosenfeld 	    "  Print the specified features of the specified NVMe controllers "
728*ecee5a1fSHans Rosenfeld 	    "and/or\n  namespaces. Supported features are:\n\n", c_name);
729*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "    %-35s %-14s %s\n",
730*ecee5a1fSHans Rosenfeld 	    "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE");
731*ecee5a1fSHans Rosenfeld 	for (feat = &features[0]; feat->f_feature != 0; feat++) {
732*ecee5a1fSHans Rosenfeld 		char *type;
733*ecee5a1fSHans Rosenfeld 
734*ecee5a1fSHans Rosenfeld 		if ((feat->f_getflags & NVMEADM_BOTH) == NVMEADM_BOTH)
735*ecee5a1fSHans Rosenfeld 			type = "both";
736*ecee5a1fSHans Rosenfeld 		else if ((feat->f_getflags & NVMEADM_CTRL) != 0)
737*ecee5a1fSHans Rosenfeld 			type = "controller only";
738*ecee5a1fSHans Rosenfeld 		else
739*ecee5a1fSHans Rosenfeld 			type = "namespace only";
740*ecee5a1fSHans Rosenfeld 
741*ecee5a1fSHans Rosenfeld 		(void) fprintf(stderr, "    %-35s %-14s %s\n",
742*ecee5a1fSHans Rosenfeld 		    feat->f_name, feat->f_short, type);
743*ecee5a1fSHans Rosenfeld 	}
744*ecee5a1fSHans Rosenfeld 
745*ecee5a1fSHans Rosenfeld }
746*ecee5a1fSHans Rosenfeld 
747*ecee5a1fSHans Rosenfeld static int
748*ecee5a1fSHans Rosenfeld do_get_feat_common(int fd, const nvme_feature_t *feat,
749*ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *idctl)
750*ecee5a1fSHans Rosenfeld {
751*ecee5a1fSHans Rosenfeld 	void *buf = NULL;
752*ecee5a1fSHans Rosenfeld 	size_t bufsize = feat->f_bufsize;
753*ecee5a1fSHans Rosenfeld 	uint64_t res;
754*ecee5a1fSHans Rosenfeld 
755*ecee5a1fSHans Rosenfeld 	if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf)
756*ecee5a1fSHans Rosenfeld 	    == B_FALSE)
757*ecee5a1fSHans Rosenfeld 		return (EINVAL);
758*ecee5a1fSHans Rosenfeld 
759*ecee5a1fSHans Rosenfeld 	nvme_print(2, feat->f_name, -1, NULL);
760*ecee5a1fSHans Rosenfeld 	feat->f_print(res, buf, bufsize, idctl);
761*ecee5a1fSHans Rosenfeld 	free(buf);
762*ecee5a1fSHans Rosenfeld 
763*ecee5a1fSHans Rosenfeld 	return (0);
764*ecee5a1fSHans Rosenfeld }
765*ecee5a1fSHans Rosenfeld 
766*ecee5a1fSHans Rosenfeld static int
767*ecee5a1fSHans Rosenfeld do_get_feat_intr_vect(int fd, const nvme_feature_t *feat,
768*ecee5a1fSHans Rosenfeld     nvme_identify_ctrl_t *idctl)
769*ecee5a1fSHans Rosenfeld {
770*ecee5a1fSHans Rosenfeld 	uint64_t res;
771*ecee5a1fSHans Rosenfeld 	uint64_t arg;
772*ecee5a1fSHans Rosenfeld 	int intr_cnt;
773*ecee5a1fSHans Rosenfeld 
774*ecee5a1fSHans Rosenfeld 	intr_cnt = nvme_intr_cnt(fd);
775*ecee5a1fSHans Rosenfeld 
776*ecee5a1fSHans Rosenfeld 	if (intr_cnt == -1)
777*ecee5a1fSHans Rosenfeld 		return (EINVAL);
778*ecee5a1fSHans Rosenfeld 
779*ecee5a1fSHans Rosenfeld 	nvme_print(2, feat->f_name, -1, NULL);
780*ecee5a1fSHans Rosenfeld 
781*ecee5a1fSHans Rosenfeld 	for (arg = 0; arg < intr_cnt; arg++) {
782*ecee5a1fSHans Rosenfeld 		if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL)
783*ecee5a1fSHans Rosenfeld 		    == B_FALSE)
784*ecee5a1fSHans Rosenfeld 			return (EINVAL);
785*ecee5a1fSHans Rosenfeld 
786*ecee5a1fSHans Rosenfeld 		feat->f_print(res, NULL, 0, idctl);
787*ecee5a1fSHans Rosenfeld 	}
788*ecee5a1fSHans Rosenfeld 
789*ecee5a1fSHans Rosenfeld 	return (0);
790*ecee5a1fSHans Rosenfeld }
791*ecee5a1fSHans Rosenfeld 
792*ecee5a1fSHans Rosenfeld static int
793*ecee5a1fSHans Rosenfeld do_get_features(int fd, const nvme_process_arg_t *npa)
794*ecee5a1fSHans Rosenfeld {
795*ecee5a1fSHans Rosenfeld 	const nvme_feature_t *feat;
796*ecee5a1fSHans Rosenfeld 	char *f, *flist, *lasts;
797*ecee5a1fSHans Rosenfeld 	boolean_t header_printed = B_FALSE;
798*ecee5a1fSHans Rosenfeld 
799*ecee5a1fSHans Rosenfeld 	if (npa->npa_argc > 1)
800*ecee5a1fSHans Rosenfeld 		errx(-1, "unexpected arguments");
801*ecee5a1fSHans Rosenfeld 
802*ecee5a1fSHans Rosenfeld 	/*
803*ecee5a1fSHans Rosenfeld 	 * No feature list given, print all supported features.
804*ecee5a1fSHans Rosenfeld 	 */
805*ecee5a1fSHans Rosenfeld 	if (npa->npa_argc == 0) {
806*ecee5a1fSHans Rosenfeld 		(void) printf("%s: Get Features\n", npa->npa_name);
807*ecee5a1fSHans Rosenfeld 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
808*ecee5a1fSHans Rosenfeld 			if ((npa->npa_nsid != 0 &&
809*ecee5a1fSHans Rosenfeld 			    (feat->f_getflags & NVMEADM_NS) == 0) ||
810*ecee5a1fSHans Rosenfeld 			    (npa->npa_nsid == 0 &&
811*ecee5a1fSHans Rosenfeld 			    (feat->f_getflags & NVMEADM_CTRL) == 0))
812*ecee5a1fSHans Rosenfeld 				continue;
813*ecee5a1fSHans Rosenfeld 
814*ecee5a1fSHans Rosenfeld 			(void) feat->f_get(fd, feat, npa->npa_idctl);
815*ecee5a1fSHans Rosenfeld 		}
816*ecee5a1fSHans Rosenfeld 
817*ecee5a1fSHans Rosenfeld 		return (0);
818*ecee5a1fSHans Rosenfeld 	}
819*ecee5a1fSHans Rosenfeld 
820*ecee5a1fSHans Rosenfeld 	/*
821*ecee5a1fSHans Rosenfeld 	 * Process feature list.
822*ecee5a1fSHans Rosenfeld 	 */
823*ecee5a1fSHans Rosenfeld 	flist = strdup(npa->npa_argv[0]);
824*ecee5a1fSHans Rosenfeld 	if (flist == NULL)
825*ecee5a1fSHans Rosenfeld 		err(-1, "do_get_features");
826*ecee5a1fSHans Rosenfeld 
827*ecee5a1fSHans Rosenfeld 	for (f = strtok_r(flist, ",", &lasts);
828*ecee5a1fSHans Rosenfeld 	    f != NULL;
829*ecee5a1fSHans Rosenfeld 	    f = strtok_r(NULL, ",", &lasts)) {
830*ecee5a1fSHans Rosenfeld 		while (isspace(*f))
831*ecee5a1fSHans Rosenfeld 			f++;
832*ecee5a1fSHans Rosenfeld 
833*ecee5a1fSHans Rosenfeld 		for (feat = &features[0]; feat->f_feature != 0; feat++) {
834*ecee5a1fSHans Rosenfeld 			if (strncasecmp(feat->f_name, f, strlen(f)) == 0 ||
835*ecee5a1fSHans Rosenfeld 			    strncasecmp(feat->f_short, f, strlen(f)) == 0)
836*ecee5a1fSHans Rosenfeld 				break;
837*ecee5a1fSHans Rosenfeld 		}
838*ecee5a1fSHans Rosenfeld 
839*ecee5a1fSHans Rosenfeld 		if (feat->f_feature == 0) {
840*ecee5a1fSHans Rosenfeld 			warnx("unknown feature %s", f);
841*ecee5a1fSHans Rosenfeld 			continue;
842*ecee5a1fSHans Rosenfeld 		}
843*ecee5a1fSHans Rosenfeld 
844*ecee5a1fSHans Rosenfeld 		if ((npa->npa_nsid != 0 &&
845*ecee5a1fSHans Rosenfeld 		    (feat->f_getflags & NVMEADM_NS) == 0) ||
846*ecee5a1fSHans Rosenfeld 		    (npa->npa_nsid == 0 &&
847*ecee5a1fSHans Rosenfeld 		    (feat->f_getflags & NVMEADM_CTRL) == 0)) {
848*ecee5a1fSHans Rosenfeld 			warnx("feature %s %s supported for namespaces",
849*ecee5a1fSHans Rosenfeld 			    feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ?
850*ecee5a1fSHans Rosenfeld 			    "only" : "not");
851*ecee5a1fSHans Rosenfeld 			continue;
852*ecee5a1fSHans Rosenfeld 		}
853*ecee5a1fSHans Rosenfeld 
854*ecee5a1fSHans Rosenfeld 		if (!header_printed) {
855*ecee5a1fSHans Rosenfeld 			(void) printf("%s: Get Features\n", npa->npa_name);
856*ecee5a1fSHans Rosenfeld 			header_printed = B_TRUE;
857*ecee5a1fSHans Rosenfeld 		}
858*ecee5a1fSHans Rosenfeld 
859*ecee5a1fSHans Rosenfeld 		if (feat->f_get(fd, feat, npa->npa_idctl) != 0) {
860*ecee5a1fSHans Rosenfeld 			warnx("unsupported feature: %s", feat->f_name);
861*ecee5a1fSHans Rosenfeld 			continue;
862*ecee5a1fSHans Rosenfeld 		}
863*ecee5a1fSHans Rosenfeld 	}
864*ecee5a1fSHans Rosenfeld 
865*ecee5a1fSHans Rosenfeld 	free(flist);
866*ecee5a1fSHans Rosenfeld 	return (0);
867*ecee5a1fSHans Rosenfeld }
868*ecee5a1fSHans Rosenfeld 
869*ecee5a1fSHans Rosenfeld static int
870*ecee5a1fSHans Rosenfeld do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf,
871*ecee5a1fSHans Rosenfeld     unsigned long ses)
872*ecee5a1fSHans Rosenfeld {
873*ecee5a1fSHans Rosenfeld 	nvme_process_arg_t ns_npa = { 0 };
874*ecee5a1fSHans Rosenfeld 	nvmeadm_cmd_t cmd = { 0 };
875*ecee5a1fSHans Rosenfeld 
876*ecee5a1fSHans Rosenfeld 	cmd = *(npa->npa_cmd);
877*ecee5a1fSHans Rosenfeld 	cmd.c_func = do_attach_detach;
878*ecee5a1fSHans Rosenfeld 	cmd.c_name = "detach";
879*ecee5a1fSHans Rosenfeld 	ns_npa = *npa;
880*ecee5a1fSHans Rosenfeld 	ns_npa.npa_cmd = &cmd;
881*ecee5a1fSHans Rosenfeld 
882*ecee5a1fSHans Rosenfeld 	if (do_attach_detach(fd, &ns_npa) != 0)
883*ecee5a1fSHans Rosenfeld 		return (exitcode);
884*ecee5a1fSHans Rosenfeld 	if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) {
885*ecee5a1fSHans Rosenfeld 		warn("%s failed", npa->npa_cmd->c_name);
886*ecee5a1fSHans Rosenfeld 		exitcode += -1;
887*ecee5a1fSHans Rosenfeld 	}
888*ecee5a1fSHans Rosenfeld 	cmd.c_name = "attach";
889*ecee5a1fSHans Rosenfeld 	exitcode += do_attach_detach(fd, &ns_npa);
890*ecee5a1fSHans Rosenfeld 
891*ecee5a1fSHans Rosenfeld 	return (exitcode);
892*ecee5a1fSHans Rosenfeld }
893*ecee5a1fSHans Rosenfeld 
894*ecee5a1fSHans Rosenfeld static void
895*ecee5a1fSHans Rosenfeld usage_format(const char *c_name)
896*ecee5a1fSHans Rosenfeld {
897*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
898*ecee5a1fSHans Rosenfeld 	    "  Format one or all namespaces of the specified NVMe "
899*ecee5a1fSHans Rosenfeld 	    "controller. Supported LBA\n  formats can be queried with "
900*ecee5a1fSHans Rosenfeld 	    "the \"%s identify\" command on the namespace\n  to be "
901*ecee5a1fSHans Rosenfeld 	    "formatted.\n", c_name, getprogname());
902*ecee5a1fSHans Rosenfeld }
903*ecee5a1fSHans Rosenfeld 
904*ecee5a1fSHans Rosenfeld static int
905*ecee5a1fSHans Rosenfeld do_format(int fd, const nvme_process_arg_t *npa)
906*ecee5a1fSHans Rosenfeld {
907*ecee5a1fSHans Rosenfeld 	unsigned long lbaf;
908*ecee5a1fSHans Rosenfeld 
909*ecee5a1fSHans Rosenfeld 	if (npa->npa_idctl->id_oacs.oa_format == 0)
910*ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
911*ecee5a1fSHans Rosenfeld 
912*ecee5a1fSHans Rosenfeld 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0)
913*ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported on individual namespace",
914*ecee5a1fSHans Rosenfeld 		    npa->npa_cmd->c_name);
915*ecee5a1fSHans Rosenfeld 
916*ecee5a1fSHans Rosenfeld 
917*ecee5a1fSHans Rosenfeld 	if (npa->npa_argc > 0) {
918*ecee5a1fSHans Rosenfeld 		errno = 0;
919*ecee5a1fSHans Rosenfeld 		lbaf = strtoul(npa->npa_argv[0], NULL, 10);
920*ecee5a1fSHans Rosenfeld 
921*ecee5a1fSHans Rosenfeld 		if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF)
922*ecee5a1fSHans Rosenfeld 			errx(-1, "invalid LBA format %d", lbaf + 1);
923*ecee5a1fSHans Rosenfeld 
924*ecee5a1fSHans Rosenfeld 		if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0)
925*ecee5a1fSHans Rosenfeld 			errx(-1, "LBA formats with metadata not supported");
926*ecee5a1fSHans Rosenfeld 	} else {
927*ecee5a1fSHans Rosenfeld 		lbaf = npa->npa_idns->id_flbas.lba_format;
928*ecee5a1fSHans Rosenfeld 	}
929*ecee5a1fSHans Rosenfeld 
930*ecee5a1fSHans Rosenfeld 	return (do_format_common(fd, npa, lbaf, 0));
931*ecee5a1fSHans Rosenfeld }
932*ecee5a1fSHans Rosenfeld 
933*ecee5a1fSHans Rosenfeld static void
934*ecee5a1fSHans Rosenfeld usage_secure_erase(const char *c_name)
935*ecee5a1fSHans Rosenfeld {
936*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [-c]\n\n"
937*ecee5a1fSHans Rosenfeld 	    "  Secure-Erase one or all namespaces of the specified "
938*ecee5a1fSHans Rosenfeld 	    "NVMe controller.\n", c_name);
939*ecee5a1fSHans Rosenfeld }
940*ecee5a1fSHans Rosenfeld 
941*ecee5a1fSHans Rosenfeld static int
942*ecee5a1fSHans Rosenfeld do_secure_erase(int fd, const nvme_process_arg_t *npa)
943*ecee5a1fSHans Rosenfeld {
944*ecee5a1fSHans Rosenfeld 	unsigned long lbaf;
945*ecee5a1fSHans Rosenfeld 	uint8_t ses = NVME_FRMT_SES_USER;
946*ecee5a1fSHans Rosenfeld 
947*ecee5a1fSHans Rosenfeld 	if (npa->npa_idctl->id_oacs.oa_format == 0)
948*ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported", npa->npa_cmd->c_name);
949*ecee5a1fSHans Rosenfeld 
950*ecee5a1fSHans Rosenfeld 	if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0)
951*ecee5a1fSHans Rosenfeld 		errx(-1, "%s not supported on individual namespace",
952*ecee5a1fSHans Rosenfeld 		    npa->npa_cmd->c_name);
953*ecee5a1fSHans Rosenfeld 
954*ecee5a1fSHans Rosenfeld 	if (npa->npa_argc > 0) {
955*ecee5a1fSHans Rosenfeld 		if (strcmp(npa->npa_argv[0], "-c") == 0)
956*ecee5a1fSHans Rosenfeld 			ses = NVME_FRMT_SES_CRYPTO;
957*ecee5a1fSHans Rosenfeld 		else
958*ecee5a1fSHans Rosenfeld 			usage(npa->npa_cmd);
959*ecee5a1fSHans Rosenfeld 	}
960*ecee5a1fSHans Rosenfeld 
961*ecee5a1fSHans Rosenfeld 	if (ses == NVME_FRMT_SES_CRYPTO &&
962*ecee5a1fSHans Rosenfeld 	    npa->npa_idctl->id_fna.fn_crypt_erase == 0)
963*ecee5a1fSHans Rosenfeld 		errx(-1, "cryptographic %s not supported",
964*ecee5a1fSHans Rosenfeld 		    npa->npa_cmd->c_name);
965*ecee5a1fSHans Rosenfeld 
966*ecee5a1fSHans Rosenfeld 	lbaf = npa->npa_idns->id_flbas.lba_format;
967*ecee5a1fSHans Rosenfeld 
968*ecee5a1fSHans Rosenfeld 	return (do_format_common(fd, npa, lbaf, ses));
969*ecee5a1fSHans Rosenfeld }
970*ecee5a1fSHans Rosenfeld 
971*ecee5a1fSHans Rosenfeld static void
972*ecee5a1fSHans Rosenfeld usage_attach_detach(const char *c_name)
973*ecee5a1fSHans Rosenfeld {
974*ecee5a1fSHans Rosenfeld 	(void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
975*ecee5a1fSHans Rosenfeld 	    "  %c%s blkdev(7d) %s one or all namespaces of the "
976*ecee5a1fSHans Rosenfeld 	    "specified NVMe controller.\n",
977*ecee5a1fSHans Rosenfeld 	    c_name, toupper(c_name[0]), &c_name[1],
978*ecee5a1fSHans Rosenfeld 	    c_name[0] == 'd' ? "from" : "to");
979*ecee5a1fSHans Rosenfeld }
980*ecee5a1fSHans Rosenfeld 
981*ecee5a1fSHans Rosenfeld static int
982*ecee5a1fSHans Rosenfeld do_attach_detach(int fd, const nvme_process_arg_t *npa)
983*ecee5a1fSHans Rosenfeld {
984*ecee5a1fSHans Rosenfeld 	char *c_name = npa->npa_cmd->c_name;
985*ecee5a1fSHans Rosenfeld 
986*ecee5a1fSHans Rosenfeld 	if (!npa->npa_isns) {
987*ecee5a1fSHans Rosenfeld 		nvme_process_arg_t ns_npa = { 0 };
988*ecee5a1fSHans Rosenfeld 
989*ecee5a1fSHans Rosenfeld 		ns_npa.npa_name = npa->npa_name;
990*ecee5a1fSHans Rosenfeld 		ns_npa.npa_isns = B_TRUE;
991*ecee5a1fSHans Rosenfeld 		ns_npa.npa_cmd = npa->npa_cmd;
992*ecee5a1fSHans Rosenfeld 
993*ecee5a1fSHans Rosenfeld 		nvme_walk(&ns_npa, npa->npa_node);
994*ecee5a1fSHans Rosenfeld 
995*ecee5a1fSHans Rosenfeld 		return (exitcode);
996*ecee5a1fSHans Rosenfeld 	} else {
997*ecee5a1fSHans Rosenfeld 		if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd)
998*ecee5a1fSHans Rosenfeld 		    == B_FALSE) {
999*ecee5a1fSHans Rosenfeld 			warn("%s failed", c_name);
1000*ecee5a1fSHans Rosenfeld 			return (-1);
1001*ecee5a1fSHans Rosenfeld 		}
1002*ecee5a1fSHans Rosenfeld 	}
1003*ecee5a1fSHans Rosenfeld 
1004*ecee5a1fSHans Rosenfeld 	return (0);
1005*ecee5a1fSHans Rosenfeld }
1006