xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm.c (revision f5f0964ce91892f7482efc86903b0ec7c7b6ba66)
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 2025 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  *		list-logpages [logpage name],...
25  *		get-logpage <logpage name>
26  *		get-features <feature>[,...]
27  *		format ...
28  *		secure-erase ...
29  *		detach ...
30  *		attach ...
31  *		list-firmware ...
32  *		load-firmware ...
33  *		commit-firmware ...
34  *		activate-firmware ...
35  */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stddef.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 #include <sys/sysmacros.h>
48 
49 #include <sys/nvme.h>
50 
51 #include "nvmeadm.h"
52 
53 /*
54  * Assertions to make sure that we've properly captured various aspects of the
55  * packed structures and haven't broken them during updates.
56  */
57 CTASSERT(sizeof (nvme_identify_ctrl_t) == NVME_IDENTIFY_BUFSIZE);
58 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oacs) == 256);
59 CTASSERT(offsetof(nvme_identify_ctrl_t, id_sqes) == 512);
60 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oncs) == 520);
61 CTASSERT(offsetof(nvme_identify_ctrl_t, id_subnqn) == 768);
62 CTASSERT(offsetof(nvme_identify_ctrl_t, id_nvmof) == 1792);
63 CTASSERT(offsetof(nvme_identify_ctrl_t, id_psd) == 2048);
64 CTASSERT(offsetof(nvme_identify_ctrl_t, id_vs) == 3072);
65 
66 CTASSERT(sizeof (nvme_identify_nsid_t) == NVME_IDENTIFY_BUFSIZE);
67 CTASSERT(offsetof(nvme_identify_nsid_t, id_fpi) == 32);
68 CTASSERT(offsetof(nvme_identify_nsid_t, id_anagrpid) == 92);
69 CTASSERT(offsetof(nvme_identify_nsid_t, id_nguid) == 104);
70 CTASSERT(offsetof(nvme_identify_nsid_t, id_lbaf) == 128);
71 CTASSERT(offsetof(nvme_identify_nsid_t, id_vs) == 384);
72 
73 CTASSERT(sizeof (nvme_identify_nsid_list_t) == NVME_IDENTIFY_BUFSIZE);
74 CTASSERT(sizeof (nvme_identify_ctrl_list_t) == NVME_IDENTIFY_BUFSIZE);
75 
76 CTASSERT(sizeof (nvme_identify_primary_caps_t) == NVME_IDENTIFY_BUFSIZE);
77 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vqfrt) == 32);
78 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vifrt) == 64);
79 
80 CTASSERT(sizeof (nvme_nschange_list_t) == 4096);
81 
82 #define	NVMEADM_F_CTRL	1
83 #define	NVMEADM_F_NS	2
84 #define	NVMEADM_F_BOTH	(NVMEADM_F_CTRL | NVMEADM_F_NS)
85 
86 static void usage(const nvmeadm_cmd_t *);
87 static bool nvmeadm_ctrl_disc_cb(nvme_t *, const nvme_ctrl_disc_t *, void *);
88 
89 static int do_list(const nvme_process_arg_t *);
90 static int do_identify(const nvme_process_arg_t *);
91 static int do_identify_ctrl(const nvme_process_arg_t *);
92 static int do_identify_ns(const nvme_process_arg_t *);
93 static int do_list_logs(const nvme_process_arg_t *);
94 static int do_get_logpage_fwslot(const nvme_process_arg_t *);
95 static int do_get_logpage(const nvme_process_arg_t *);
96 static int do_list_features(const nvme_process_arg_t *);
97 static boolean_t do_get_feat_intr_vect(const nvme_process_arg_t *,
98     const nvme_feat_disc_t *, const nvmeadm_feature_t *);
99 static boolean_t do_get_feat_temp_thresh(const nvme_process_arg_t *,
100     const nvme_feat_disc_t *, const nvmeadm_feature_t *);
101 static int do_get_features(const nvme_process_arg_t *);
102 static int do_format(const nvme_process_arg_t *);
103 static int do_secure_erase(const nvme_process_arg_t *);
104 static int do_attach_bd(const nvme_process_arg_t *);
105 static int do_detach_bd(const nvme_process_arg_t *);
106 static int do_firmware_load(const nvme_process_arg_t *);
107 static int do_firmware_commit(const nvme_process_arg_t *);
108 static int do_firmware_activate(const nvme_process_arg_t *);
109 
110 static void optparse_list(nvme_process_arg_t *);
111 static void optparse_identify(nvme_process_arg_t *);
112 static void optparse_identify_ctrl(nvme_process_arg_t *);
113 static void optparse_identify_ns(nvme_process_arg_t *);
114 static void optparse_list_logs(nvme_process_arg_t *);
115 static void optparse_get_logpage(nvme_process_arg_t *);
116 static void optparse_list_features(nvme_process_arg_t *);
117 static void optparse_secure_erase(nvme_process_arg_t *);
118 
119 static void usage_list(const char *);
120 static void usage_identify(const char *);
121 static void usage_identify_ctrl(const char *);
122 static void usage_identify_ns(const char *);
123 static void usage_list_logs(const char *);
124 static void usage_get_logpage(const char *);
125 static void usage_list_features(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_bd(const char *);
130 static void usage_firmware_list(const char *);
131 static void usage_firmware_load(const char *);
132 static void usage_firmware_commit(const char *);
133 static void usage_firmware_activate(const char *);
134 
135 int verbose;
136 int debug;
137 
138 /*
139  * nvmeadm Secure-erase specific options
140  */
141 #define	NVMEADM_O_SE_CRYPTO	0x00000004
142 
143 /*
144  * nvmeadm identify specific options
145  */
146 #define	NVMEADM_O_ID_NSID_LIST	0x00000008
147 #define	NVMEADM_O_ID_COMMON_NS	0x00000010
148 #define	NVMEADM_O_ID_CTRL_LIST	0x00000020
149 #define	NVMEADM_O_ID_DESC_LIST	0x00000040
150 #define	NVMEADM_O_ID_ALLOC_NS	0x00000080
151 
152 /*
153  * nvmeadm List specific options
154  */
155 #define	NVMEADM_O_LS_CTRL	0x00000100
156 
157 static int exitcode;
158 
159 /*
160  * Nvmeadm subcommand definitons.
161  *
162  * When adding a new subcommand, please check that the commands still
163  * line up in the usage() message, and adjust the format string in
164  * usage() below if necessary.
165  */
166 static const nvmeadm_cmd_t nvmeadm_cmds[] = {
167 	{
168 		"list",
169 		"list controllers and namespaces",
170 		"  -c\t\tlist only controllers\n"
171 		"  -p\t\tprint parsable output\n"
172 		"  -o field\tselect a field for parsable output\n",
173 		"  model\t\tthe model name of the device\n"
174 		"  serial\tthe serial number of the device\n"
175 		"  fwrev\t\tthe device's current firmware revision\n"
176 		"  version\tthe device's NVMe specification version\n"
177 		"  capacity\tthe capacity of the device in bytes\n"
178 		"  instance\tthe device driver instance (e.g. nvme3)\n"
179 		"  unallocated\tthe amount of unallocated NVM in bytes",
180 		do_list, usage_list, optparse_list,
181 		NVMEADM_C_MULTI
182 	},
183 	{
184 		"identify",
185 		"identify controllers and/or namespaces",
186 		"  -C\t\tget Common Namespace Identification\n"
187 		"  -a\t\tget only allocated namespace information\n"
188 		"  -c\t\tget controller identifier list\n"
189 		"  -d\t\tget namespace identification descriptors list\n"
190 		"  -n\t\tget namespaces identifier list",
191 		NULL,
192 		do_identify, usage_identify, optparse_identify,
193 		NVMEADM_C_MULTI
194 	},
195 	{
196 		"identify-controller",
197 		"identify controllers",
198 		"  -C\t\tget Common Namespace Identification\n"
199 		"  -a\t\tget only allocated namespace information\n"
200 		"  -c\t\tget controller identifier list\n"
201 		"  -n\t\tget namespaces identifier list",
202 		NULL,
203 		do_identify_ctrl, usage_identify_ctrl, optparse_identify_ctrl,
204 		NVMEADM_C_MULTI
205 	},
206 	{
207 		"identify-namespace",
208 		"identify namespaces",
209 		"  -c\t\tget attached controller identifier list\n"
210 		"  -d\t\tget namespace identification descriptors list",
211 		NULL,
212 		do_identify_ns, usage_identify_ns, optparse_identify_ns,
213 		NVMEADM_C_MULTI
214 	},
215 	{
216 		"list-logpages",
217 		"list a device's supported log pages",
218 		"  -a\t\tprint all log pages, including unimplemented ones\n"
219 		"  -H\t\tomit column headers\n"
220 		"  -o field\tselect a field for parsable output\n"
221 		"  -p\t\tprint parsable output\n"
222 		"  -s scope\tprint logs that match the specified scopes "
223 		"(default is based on\n\t\tdevice)\n",
224 		"  device\tthe name of the controller or namespace\n"
225 		"  name\t\tthe name of the log page\n"
226 		"  desc\t\ta description of the loage page\n"
227 		"  scope\t\tthe valid device scopes for the log page\n"
228 		"  fields\tthe list of fields in the get log request that may "
229 		"be set or required\n\t\t(e.g. lsi, lsp, rae, etc.)\n"
230 		"  csi\t\tthe command set interface the log page belongs to\n"
231 		"  lid\t\tthe log page's numeric ID\n"
232 		"  impl\t\tindicates whether the device implements the log "
233 		"page\n"
234 		"  size\t\tthe size of the log page for fixed size logs\n"
235 		"  minsize\tthe minimum size required to determine the full "
236 		"log page size\n\t\tfor variable-length pages\n"
237 		"  sources\twhere information for this log page came from\n"
238 		"  kind\t\tindicates the kind of log page e.g. standard, "
239 		"vendor-specific,\n\t\tetc.",
240 		do_list_logs, usage_list_logs, optparse_list_logs,
241 		NVMEADM_C_MULTI
242 	},
243 	{
244 		"get-logpage",
245 		"get a log page from controllers and/or namespaces",
246 		"  -O file\toutput log raw binary data to a file\n",
247 		NULL,
248 		do_get_logpage, usage_get_logpage, optparse_get_logpage,
249 		NVMEADM_C_MULTI
250 	},
251 	{
252 		"list-features",
253 		"list a device's supported features",
254 		"  -a\t\tprint all features, including unsupported\n"
255 		"  -H\t\tomit column headers\n"
256 		"  -o field\tselect a field for parsable output\n"
257 		"  -p\t\tprint parsable output",
258 		"  device\tthe name of the controller or namespace\n"
259 		"  short\t\tthe short name of the feature\n"
260 		"  spec\t\tthe longer feature description from the NVMe spec\n"
261 		"  fid\t\tthe numeric feature ID\n"
262 		"  scope\t\tthe valid device scopes for the feature\n"
263 		"  kind\t\tindicates the kind of feature e.g. standard, "
264 		"vendor-specific,\n\t\tetc.\n"
265 		"  csi\t\tindicates the features command set interface\n"
266 		"  flags\t\tindicates additional properties of the feature\n"
267 		"  get-in\tindicates the fields that are required to get the "
268 		"feature\n"
269 		"  set-in\tindicates the fields that are required to set the "
270 		"feature\n"
271 		"  get-out\tindicates the fields the feature outputs\n"
272 		"  set-out\tindicates the fields the feature outputs when "
273 		"setting the feature\n"
274 		"  datalen\tindicates the length of the feature's data "
275 		"payload\n"
276 		"  impl\t\tindicates whether the device implements the "
277 		"feature",
278 		do_list_features, usage_list_features, optparse_list_features,
279 		NVMEADM_C_MULTI
280 	},
281 	{
282 		"get-features",
283 		"get features from controllers and/or namespaces",
284 		NULL,
285 		NULL,
286 		do_get_features, usage_get_features, NULL,
287 		NVMEADM_C_MULTI
288 	},
289 	{
290 		"format",
291 		"format namespace(s) of a controller",
292 		NULL,
293 		NULL,
294 		do_format, usage_format, NULL,
295 		NVMEADM_C_EXCL
296 	},
297 	{
298 		"secure-erase",
299 		"secure erase namespace(s) of a controller",
300 		"  -c  Do a cryptographic erase.",
301 		NULL,
302 		do_secure_erase, usage_secure_erase, optparse_secure_erase,
303 		NVMEADM_C_EXCL
304 	},
305 	{
306 		"create-namespace",
307 		"create a new namespace",
308 		"  -b block-size\tNamespace format chosen to match the "
309 		"requested block-size\n"
310 		"  -c cap\tSpecifies the namespace capacity in bytes, defaults "
311 		"to the\n\t\tnamespace's size. When the size is greater than "
312 		"the\n\t\tcapacity, the namespace is thin provisioned.\n"
313 		"  -f flbas\tformatted LBA block size index\n"
314 		"  -n nmic\tmulti-path I/O and namespace sharing capabilities, "
315 		"valid values:\n"
316 		"\t\tnone\tno namespace sharing\n"
317 		"\t\tshared\tthe namespace may be attached by two or more "
318 		"controllers\n"
319 		"  -t csi\tspecifies the namespace's command set interface, "
320 		"defaults to\n\t\tnvm\n",
321 		NULL,
322 		do_create_ns, usage_create_ns, optparse_create_ns,
323 		NVMEADM_C_EXCL
324 	},
325 	{
326 		"delete-namespace",
327 		"delete a namespace",
328 		NULL, NULL,
329 		do_delete_ns, usage_delete_ns, NULL,
330 		NVMEADM_C_EXCL
331 	},
332 	{
333 		"attach-namespace",
334 		"attach a namespace to a controller",
335 		NULL, NULL,
336 		do_attach_ns, usage_attach_ns, NULL,
337 		NVMEADM_C_EXCL
338 	},
339 	{
340 		"detach-namespace",
341 		"detach a namespace from a controller",
342 		NULL, NULL,
343 		do_detach_ns, usage_detach_ns, NULL,
344 		NVMEADM_C_EXCL
345 	},
346 
347 	{
348 		"detach",
349 		"detach blkdev(4D) from namespace(s) of a controller",
350 		NULL,
351 		NULL,
352 		do_detach_bd, usage_attach_detach_bd, NULL,
353 		NVMEADM_C_EXCL
354 	},
355 	{
356 		"attach",
357 		"attach blkdev(4D) to namespace(s) of a controller",
358 		NULL,
359 		NULL,
360 		do_attach_bd, usage_attach_detach_bd, NULL,
361 		NVMEADM_C_EXCL
362 	},
363 	{
364 		"list-firmware",
365 		"list firmware on a controller",
366 		NULL,
367 		NULL,
368 		do_get_logpage_fwslot, usage_firmware_list, NULL,
369 		0
370 	},
371 	{
372 		"load-firmware",
373 		"load firmware to a controller",
374 		NULL,
375 		NULL,
376 		do_firmware_load, usage_firmware_load, NULL,
377 		NVMEADM_C_EXCL
378 	},
379 	{
380 		"commit-firmware",
381 		"commit downloaded firmware to a slot of a controller",
382 		NULL,
383 		NULL,
384 		do_firmware_commit, usage_firmware_commit, NULL,
385 		NVMEADM_C_EXCL
386 	},
387 	{
388 		"activate-firmware",
389 		"activate a firmware slot of a controller",
390 		NULL,
391 		NULL,
392 		do_firmware_activate, usage_firmware_activate, NULL,
393 		NVMEADM_C_EXCL
394 	},
395 	{
396 		"wdc/e6dump",
397 		"dump WDC e6 diagnostic log",
398 		"  -o output\tspecify output file destination\n",
399 		NULL,
400 		do_wdc_e6dump, usage_wdc_e6dump, optparse_wdc_e6dump,
401 		0
402 	},
403 	{
404 		"wdc/resize",
405 		"change a WDC device's capacity",
406 		"  -g\t\tquery the device's current resized capacity\n"
407 		"  -s size\tset the size of a device to the specified in gb",
408 		NULL,
409 		do_wdc_resize, usage_wdc_resize, optparse_wdc_resize,
410 		/*
411 		 * We do not set NVMEADM_C_EXCL here as that is handled by the
412 		 * vendor unique command logic and operates based on the
413 		 * information we get from vuc discovery.
414 		 */
415 		0
416 	},
417 	{
418 		"wdc/clear-assert",
419 		"clear internal device assertion",
420 		NULL,
421 		NULL,
422 		do_wdc_clear_assert, usage_wdc_clear_assert, NULL
423 	},
424 	{
425 		"wdc/inject-assert",
426 		"inject internal device assertion",
427 		NULL,
428 		NULL,
429 		do_wdc_inject_assert, usage_wdc_inject_assert, NULL
430 	},
431 	{
432 		NULL, NULL, NULL,
433 		NULL, NULL, NULL, 0
434 	}
435 };
436 
437 static const nvmeadm_feature_t features[] = {
438 	{
439 		.f_feature = NVME_FEAT_ARBITRATION,
440 		.f_print = nvme_print_feat_arbitration
441 	}, {
442 		.f_feature = NVME_FEAT_POWER_MGMT,
443 		.f_print = nvme_print_feat_power_mgmt
444 	}, {
445 		.f_feature = NVME_FEAT_LBA_RANGE,
446 		.f_print = nvme_print_feat_lba_range
447 	}, {
448 		.f_feature = NVME_FEAT_TEMPERATURE,
449 		.f_get = do_get_feat_temp_thresh,
450 		.f_print = nvme_print_feat_temperature
451 	}, {
452 		.f_feature = NVME_FEAT_ERROR,
453 		.f_print = nvme_print_feat_error
454 	}, {
455 		.f_feature = NVME_FEAT_WRITE_CACHE,
456 		.f_print = nvme_print_feat_write_cache
457 	}, {
458 		.f_feature = NVME_FEAT_NQUEUES,
459 		.f_print = nvme_print_feat_nqueues
460 	}, {
461 		.f_feature = NVME_FEAT_INTR_COAL,
462 		.f_print = nvme_print_feat_intr_coal
463 	}, {
464 		.f_feature = NVME_FEAT_INTR_VECT,
465 		.f_get = do_get_feat_intr_vect,
466 		.f_print = nvme_print_feat_intr_vect
467 	}, {
468 		.f_feature = NVME_FEAT_WRITE_ATOM,
469 		.f_print = nvme_print_feat_write_atom
470 	}, {
471 		.f_feature = NVME_FEAT_ASYNC_EVENT,
472 		.f_print = nvme_print_feat_async_event
473 	}, {
474 		.f_feature = NVME_FEAT_AUTO_PST,
475 		.f_print = nvme_print_feat_auto_pst
476 	}, {
477 		.f_feature = NVME_FEAT_PROGRESS,
478 		.f_print = nvme_print_feat_progress
479 	}
480 };
481 
482 static void
nvmeadm_ctrl_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)483 nvmeadm_ctrl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap)
484 {
485 	nvme_ctrl_t *ctrl = npa->npa_ctrl;
486 
487 	(void) fprintf(stderr, "nvmeadm: ");
488 	(void) vfprintf(stderr, fmt, ap);
489 	(void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n",
490 	    nvme_ctrl_errmsg(ctrl), nvme_ctrl_errtostr(npa->npa_ctrl,
491 	    nvme_ctrl_err(ctrl)), nvme_ctrl_err(ctrl), nvme_ctrl_syserr(ctrl));
492 }
493 
494 static void
nvmeadm_hdl_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)495 nvmeadm_hdl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap)
496 {
497 	nvme_t *nvme = npa->npa_nvme;
498 
499 	(void) fprintf(stderr, "nvmeadm: ");
500 	(void) vfprintf(stderr, fmt, ap);
501 	(void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n",
502 	    nvme_errmsg(nvme), nvme_errtostr(nvme, nvme_err(nvme)),
503 	    nvme_err(nvme), nvme_syserr(nvme));
504 }
505 
506 static void
nvmeadm_ctrl_info_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)507 nvmeadm_ctrl_info_vwarn(const nvme_process_arg_t *npa, const char *fmt,
508     va_list ap)
509 {
510 	nvme_ctrl_info_t *info = npa->npa_ctrl_info;
511 
512 	(void) fprintf(stderr, "nvmeadm: ");
513 	(void) vfprintf(stderr, fmt, ap);
514 	(void) fprintf(stderr, ": %s: %s (libnvme info: 0x%x, sys: %d)\n",
515 	    nvme_ctrl_info_errmsg(info), nvme_ctrl_info_errtostr(info,
516 	    nvme_ctrl_info_err(info)), nvme_ctrl_info_err(info),
517 	    nvme_ctrl_info_syserr(info));
518 }
519 
520 void
nvmeadm_warn(const nvme_process_arg_t * npa,const char * fmt,...)521 nvmeadm_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
522 {
523 	va_list ap;
524 
525 	va_start(ap, fmt);
526 	nvmeadm_ctrl_vwarn(npa, fmt, ap);
527 	va_end(ap);
528 }
529 
530 void __NORETURN
nvmeadm_fatal(const nvme_process_arg_t * npa,const char * fmt,...)531 nvmeadm_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
532 {
533 	va_list ap;
534 
535 	va_start(ap, fmt);
536 	nvmeadm_ctrl_vwarn(npa, fmt, ap);
537 	va_end(ap);
538 
539 	exit(-1);
540 }
541 
542 void
nvmeadm_hdl_warn(const nvme_process_arg_t * npa,const char * fmt,...)543 nvmeadm_hdl_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
544 {
545 	va_list ap;
546 
547 	va_start(ap, fmt);
548 	nvmeadm_hdl_vwarn(npa, fmt, ap);
549 	va_end(ap);
550 }
551 
552 void __NORETURN
nvmeadm_hdl_fatal(const nvme_process_arg_t * npa,const char * fmt,...)553 nvmeadm_hdl_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
554 {
555 	va_list ap;
556 
557 	va_start(ap, fmt);
558 	nvmeadm_hdl_vwarn(npa, fmt, ap);
559 	va_end(ap);
560 
561 	exit(-1);
562 }
563 
564 static void
nvmeadm_ctrl_info_warn(const nvme_process_arg_t * npa,const char * fmt,...)565 nvmeadm_ctrl_info_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
566 {
567 	va_list ap;
568 
569 	va_start(ap, fmt);
570 	nvmeadm_ctrl_info_vwarn(npa, fmt, ap);
571 	va_end(ap);
572 }
573 
574 static void
nvmeadm_ctrl_info_fatal(const nvme_process_arg_t * npa,const char * fmt,...)575 nvmeadm_ctrl_info_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
576 {
577 	va_list ap;
578 
579 	va_start(ap, fmt);
580 	nvmeadm_ctrl_info_vwarn(npa, fmt, ap);
581 	va_end(ap);
582 
583 	exit(-1);
584 }
585 
586 boolean_t
nvme_version_check(const nvme_process_arg_t * npa,const nvme_version_t * vers)587 nvme_version_check(const nvme_process_arg_t *npa, const nvme_version_t *vers)
588 {
589 	return (nvme_vers_atleast(npa->npa_version, vers) ? B_TRUE : B_FALSE);
590 }
591 
592 /*
593  * Because nvmeadm operates on a series of NVMe devices for several commands,
594  * here we need to clean up everything that we allocated for this device so we
595  * can prepare for the next.
596  */
597 static void
nvmeadm_cleanup_npa(nvme_process_arg_t * npa)598 nvmeadm_cleanup_npa(nvme_process_arg_t *npa)
599 {
600 	npa->npa_idctl = NULL;
601 	npa->npa_version = NULL;
602 
603 	if (npa->npa_excl) {
604 		if (npa->npa_ns != NULL) {
605 			nvme_ns_unlock(npa->npa_ns);
606 		} else if (npa->npa_ctrl != NULL) {
607 			nvme_ctrl_unlock(npa->npa_ctrl);
608 		}
609 	}
610 
611 	if (npa->npa_ns_info != NULL) {
612 		nvme_ns_info_free(npa->npa_ns_info);
613 		npa->npa_ns_info = NULL;
614 	}
615 
616 	if (npa->npa_ctrl_info != NULL) {
617 		nvme_ctrl_info_free(npa->npa_ctrl_info);
618 		npa->npa_ctrl_info = NULL;
619 	}
620 
621 	if (npa->npa_ns != NULL) {
622 		nvme_ns_fini(npa->npa_ns);
623 		npa->npa_ns = NULL;
624 	}
625 
626 	if (npa->npa_ctrl != NULL) {
627 		nvme_ctrl_fini(npa->npa_ctrl);
628 		npa->npa_ctrl = NULL;
629 	}
630 }
631 
632 /*
633  * Determine if a command requires a controller or namespace write lock. If so
634  * we first attempt to grab it non-blocking and then if that fails, we'll warn
635  * that we may be blocking for the lock so that way the user has a chance to do
636  * something and can cancel it.
637  */
638 static void
nvmeadm_excl(const nvme_process_arg_t * npa,nvme_lock_level_t level)639 nvmeadm_excl(const nvme_process_arg_t *npa, nvme_lock_level_t level)
640 {
641 	bool ret;
642 	nvme_lock_flags_t flags = NVME_LOCK_F_DONT_BLOCK;
643 
644 	if (npa->npa_ns != NULL) {
645 		ret = nvme_ns_lock(npa->npa_ns, level, flags);
646 	} else {
647 		ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags);
648 	}
649 
650 	if (ret) {
651 		return;
652 	}
653 
654 	if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_LOCK_WOULD_BLOCK) {
655 		nvmeadm_fatal(npa, "failed to acquire lock on %s",
656 		    npa->npa_name);
657 	}
658 
659 	(void) fprintf(stderr, "Waiting on contended %s lock on %s...",
660 	    npa->npa_ns != NULL ? "namespace": "controller", npa->npa_name);
661 	(void) fflush(stderr);
662 
663 	flags &= ~NVME_LOCK_F_DONT_BLOCK;
664 	if (npa->npa_ns != NULL) {
665 		ret = nvme_ns_lock(npa->npa_ns, level, flags);
666 	} else {
667 		ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags);
668 	}
669 
670 	if (!ret) {
671 		nvmeadm_fatal(npa, "failed to acquire lock on %s",
672 		    npa->npa_name);
673 	}
674 
675 	(void) fprintf(stderr, " acquired\n");
676 }
677 
678 /*
679  * Most of nvmeadm was written before the existence of libnvme and always had
680  * things like the identify controller or namespace information sitting around.
681  * As such we try to grab all this in one place for it. Note, regardless if this
682  * succeeds or fails, our callers will still call nvmeadm_cleanup_npa() so we
683  * don't need to clean up the various libnvme objects.
684  */
685 static boolean_t
nvmeadm_open_dev(nvme_process_arg_t * npa)686 nvmeadm_open_dev(nvme_process_arg_t *npa)
687 {
688 	if (!nvme_ctrl_ns_init(npa->npa_nvme, npa->npa_name, &npa->npa_ctrl,
689 	    &npa->npa_ns)) {
690 		nvmeadm_hdl_warn(npa, "failed to open '%s'", npa->npa_name);
691 		exitcode = -1;
692 		return (B_FALSE);
693 	}
694 
695 	/*
696 	 * Several commands expect to be able to access the controller's
697 	 * information snapshot. Grab that now for it and the namespace if it
698 	 * exists.
699 	 */
700 	if (!nvme_ctrl_info_snap(npa->npa_ctrl, &npa->npa_ctrl_info)) {
701 		nvmeadm_warn(npa, "failed to get controller info for %s",
702 		    npa->npa_ctrl_name);
703 		exitcode = -1;
704 		return (B_FALSE);
705 	}
706 
707 	if (npa->npa_ns != NULL && !nvme_ns_info_snap(npa->npa_ns,
708 	    &npa->npa_ns_info)) {
709 		nvmeadm_warn(npa, "failed to get namespace info for %s",
710 		    npa->npa_name);
711 		exitcode = -1;
712 		return (B_FALSE);
713 	}
714 
715 	/*
716 	 * Snapshot data the rest of the command has fairly ingrained.
717 	 */
718 	npa->npa_version = nvme_ctrl_info_version(npa->npa_ctrl_info);
719 	npa->npa_idctl = nvme_ctrl_info_identify(npa->npa_ctrl_info);
720 
721 	/*
722 	 * If this command has requested exclusive access, proceed to grab that
723 	 * before we continue.
724 	 */
725 	if (npa->npa_excl) {
726 		nvmeadm_excl(npa, NVME_LOCK_L_WRITE);
727 	}
728 
729 	return (B_TRUE);
730 }
731 
732 static bool
nvmeadm_ctrl_disc_cb(nvme_t * nvme,const nvme_ctrl_disc_t * disc,void * arg)733 nvmeadm_ctrl_disc_cb(nvme_t *nvme, const nvme_ctrl_disc_t *disc, void *arg)
734 {
735 	nvme_process_arg_t *npa = arg;
736 	di_node_t di = nvme_ctrl_disc_devi(disc);
737 	char name[128];
738 
739 	(void) snprintf(name, sizeof (name), "%s%d", di_driver_name(di),
740 	    di_instance(di));
741 	npa->npa_name = name;
742 	npa->npa_ctrl_name = name;
743 
744 	if (nvmeadm_open_dev(npa)) {
745 		if (npa->npa_cmd->c_func(npa) != 0) {
746 			exitcode = -1;
747 		}
748 	}
749 
750 	nvmeadm_cleanup_npa(npa);
751 	return (true);
752 }
753 
754 int
main(int argc,char ** argv)755 main(int argc, char **argv)
756 {
757 	int c;
758 	const nvmeadm_cmd_t *cmd;
759 	nvme_process_arg_t npa = { 0 };
760 	int help = 0;
761 	char *ctrl = NULL;
762 
763 	while ((c = getopt(argc, argv, "dhv")) != -1) {
764 		switch (c) {
765 		case 'd':
766 			debug++;
767 			break;
768 
769 		case 'v':
770 			verbose++;
771 			break;
772 
773 		case 'h':
774 			help++;
775 			break;
776 
777 		case '?':
778 			usage(NULL);
779 			exit(-1);
780 		}
781 	}
782 
783 	if (optind == argc) {
784 		usage(NULL);
785 		if (help)
786 			exit(0);
787 		else
788 			exit(-1);
789 	}
790 
791 	/* Look up the specified command in the command table. */
792 	for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
793 		if (strcmp(cmd->c_name, argv[optind]) == 0)
794 			break;
795 
796 	if (cmd->c_name == NULL) {
797 		usage(NULL);
798 		exit(-1);
799 	}
800 
801 	if (help) {
802 		usage(cmd);
803 		exit(0);
804 	}
805 
806 	npa.npa_nvme = nvme_init();
807 	if (npa.npa_nvme == NULL) {
808 		err(-1, "failed to initialize libnvme");
809 	}
810 	npa.npa_cmd = cmd;
811 	npa.npa_excl = ((cmd->c_flags & NVMEADM_C_EXCL) != 0);
812 
813 	optind++;
814 
815 	/*
816 	 * Store the remaining arguments for use by the command. Give the
817 	 * command a chance to process the options across the board before going
818 	 * into each controller.
819 	 */
820 	npa.npa_argc = argc - optind;
821 	npa.npa_argv = &argv[optind];
822 
823 	if (cmd->c_optparse != NULL) {
824 		optind = 0;
825 		cmd->c_optparse(&npa);
826 		npa.npa_argc -= optind;
827 		npa.npa_argv += optind;
828 	}
829 
830 	/*
831 	 * All commands but "list" require a ctl/ns argument. However, this
832 	 * should not be passed through to the command in its subsequent
833 	 * arguments.
834 	 */
835 	if (npa.npa_argc == 0 && cmd->c_func != do_list) {
836 		warnx("missing controller/namespace name");
837 		usage(cmd);
838 		exit(-1);
839 	}
840 
841 	if (npa.npa_argc > 0) {
842 		ctrl = npa.npa_argv[0];
843 		npa.npa_argv++;
844 		npa.npa_argc--;
845 	} else {
846 		if (!nvme_ctrl_discover(npa.npa_nvme, nvmeadm_ctrl_disc_cb,
847 		    &npa)) {
848 			nvmeadm_hdl_fatal(&npa, "failed to walk controllers");
849 		}
850 		exit(exitcode);
851 	}
852 
853 	/*
854 	 * Make sure we're not running commands on multiple controllers that
855 	 * aren't allowed to do that.
856 	 */
857 	if (ctrl != NULL && strchr(ctrl, ',') != NULL &&
858 	    (cmd->c_flags & NVMEADM_C_MULTI) == 0) {
859 		warnx("%s not allowed on multiple controllers",
860 		    cmd->c_name);
861 		usage(cmd);
862 		exit(-1);
863 	}
864 
865 	/*
866 	 * Get controller/namespace arguments and run command.
867 	 */
868 	while ((npa.npa_name = strsep(&ctrl, ",")) != NULL) {
869 		char *ctrl_name, *slash;
870 
871 		/*
872 		 * We may be given just a controller as an argument or a
873 		 * controller and a namespace as an argument. Parts of the
874 		 * commands want to know what controller they're referring to
875 		 * even if the overall argument was for a namespace. So we
876 		 * always dup the argument and try to make the controller out of
877 		 * it.
878 		 */
879 		ctrl_name = strdup(npa.npa_name);
880 		if (ctrl_name == NULL) {
881 			err(-1, "failed to duplicate NVMe controller/namespace "
882 			    "name");
883 		}
884 		if ((slash = strchr(ctrl_name, '/')) != NULL)
885 			*slash = '\0';
886 		npa.npa_ctrl_name = ctrl_name;
887 
888 		if (nvmeadm_open_dev(&npa)) {
889 			if (npa.npa_cmd->c_func(&npa) != 0) {
890 				exitcode = -1;
891 			}
892 		}
893 
894 		nvmeadm_cleanup_npa(&npa);
895 		free(ctrl_name);
896 	}
897 
898 	exit(exitcode);
899 }
900 
901 static void
nvme_oferr(const char * fmt,...)902 nvme_oferr(const char *fmt, ...)
903 {
904 	va_list ap;
905 
906 	va_start(ap, fmt);
907 	verrx(-1, fmt, ap);
908 }
909 
910 static void
usage(const nvmeadm_cmd_t * cmd)911 usage(const nvmeadm_cmd_t *cmd)
912 {
913 	const char *progname = getprogname();
914 
915 	(void) fprintf(stderr, "usage:\n");
916 	(void) fprintf(stderr, "  %s -h %s\n", progname,
917 	    cmd != NULL ? cmd->c_name : "[<command>]");
918 	(void) fprintf(stderr, "  %s [-dv] ", progname);
919 
920 	if (cmd != NULL) {
921 		cmd->c_usage(cmd->c_name);
922 	} else {
923 		(void) fprintf(stderr,
924 		    "<command> <ctl>[/<ns>][,...] [<args>]\n");
925 		(void) fprintf(stderr,
926 		    "\n  Manage NVMe controllers and namespaces.\n");
927 		(void) fprintf(stderr, "\ncommands:\n");
928 
929 		for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) {
930 			/*
931 			 * The longest nvmeadm subcommand is 19 characters long.
932 			 * The format string needs to be updated every time a
933 			 * longer subcommand is added.
934 			 */
935 			(void) fprintf(stderr, "  %-19s - %s\n",
936 			    cmd->c_name, cmd->c_desc);
937 		}
938 	}
939 	(void) fprintf(stderr, "\n%s flags:\n"
940 	    "  -h\t\tprint usage information\n"
941 	    "  -d\t\tprint information useful for debugging %s\n"
942 	    "  -v\t\tprint verbose information\n",
943 	    progname, progname);
944 
945 	if (cmd != NULL && cmd->c_flagdesc != NULL) {
946 		(void) fprintf(stderr, "\n%s %s flags:\n",
947 		    progname, cmd->c_name);
948 		(void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
949 	}
950 
951 	if (cmd != NULL && cmd->c_fielddesc != NULL) {
952 		(void) fprintf(stderr, "\n%s %s valid fields:\n",
953 		    progname, cmd->c_name);
954 		(void) fprintf(stderr, "%s\n", cmd->c_fielddesc);
955 	}
956 }
957 
958 char *
nvme_dskname(di_node_t ctrl,const char * bd_addr)959 nvme_dskname(di_node_t ctrl, const char *bd_addr)
960 {
961 	di_dim_t dim;
962 	char *diskname = NULL;
963 
964 	dim = di_dim_init();
965 	if (dim == NULL) {
966 		err(-1, "failed to initialize devinfo minor translation");
967 	}
968 
969 	for (di_node_t child = di_child_node(ctrl); child != DI_NODE_NIL;
970 	    child = di_sibling_node(child)) {
971 		char *disk_ctd, *path = NULL;
972 		const char *addr = di_bus_addr(child);
973 		if (addr == NULL)
974 			continue;
975 
976 		if (strcmp(addr, bd_addr) != 0)
977 			continue;
978 
979 		path = di_dim_path_dev(dim, di_driver_name(child),
980 		    di_instance(child), "c");
981 
982 		/*
983 		 * Error out if we didn't get a path, or if it's too short for
984 		 * the following operations to be safe.
985 		 */
986 		if (path == NULL || strlen(path) < 2) {
987 			errx(-1, "failed to get a valid minor path");
988 		}
989 
990 		/* Chop off 's0' and get everything past the last '/' */
991 		path[strlen(path) - 2] = '\0';
992 		disk_ctd = strrchr(path, '/');
993 		if (disk_ctd == NULL) {
994 			errx(-1, "encountered malformed minor path: %s", path);
995 		}
996 
997 		diskname = strdup(++disk_ctd);
998 		if (diskname == NULL) {
999 			err(-1, "failed to duplicate disk path");
1000 		}
1001 
1002 		free(path);
1003 		break;
1004 	}
1005 
1006 	di_dim_fini(dim);
1007 	return (diskname);
1008 }
1009 
1010 static void
usage_list(const char * c_name)1011 usage_list(const char *c_name)
1012 {
1013 	(void) fprintf(stderr, "%s "
1014 	    "[-c] [-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n"
1015 	    "  List NVMe controllers and their namespaces. If no "
1016 	    "controllers and/or name-\n  spaces are specified, all "
1017 	    "controllers and namespaces in the system will be\n  "
1018 	    "listed.\n", c_name);
1019 }
1020 
1021 static void
optparse_list(nvme_process_arg_t * npa)1022 optparse_list(nvme_process_arg_t *npa)
1023 {
1024 	int c;
1025 	uint_t oflags = 0;
1026 	boolean_t parse = B_FALSE;
1027 	const char *fields = NULL;
1028 	const ofmt_field_t *ofmt = nvmeadm_list_nsid_ofmt;
1029 
1030 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":co:p")) != -1) {
1031 		switch (c) {
1032 		case 'c':
1033 			npa->npa_cmdflags |= NVMEADM_O_LS_CTRL;
1034 			ofmt = nvmeadm_list_ctrl_ofmt;
1035 			break;
1036 		case 'o':
1037 			fields = optarg;
1038 			break;
1039 
1040 		case 'p':
1041 			parse = B_TRUE;
1042 			oflags |= OFMT_PARSABLE;
1043 			break;
1044 
1045 		case '?':
1046 			errx(-1, "unknown option: -%c", optopt);
1047 
1048 		case ':':
1049 			errx(-1, "option -%c requires an argument", optopt);
1050 		}
1051 	}
1052 
1053 	if (fields != NULL && !parse) {
1054 		errx(-1, "-o can only be used when in parsable mode (-p)");
1055 	}
1056 
1057 	if (parse && fields == NULL) {
1058 		errx(-1, "parsable mode (-p) requires one to specify output "
1059 		    "fields with -o");
1060 	}
1061 
1062 	if (parse) {
1063 		ofmt_status_t oferr;
1064 
1065 		oferr = ofmt_open(fields, ofmt, oflags, 0,
1066 		    &npa->npa_ofmt);
1067 		ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
1068 	}
1069 }
1070 
1071 static void
do_list_nsid(const nvme_process_arg_t * npa,nvme_ctrl_info_t * ctrl,nvme_ns_info_t * ns)1072 do_list_nsid(const nvme_process_arg_t *npa, nvme_ctrl_info_t *ctrl,
1073     nvme_ns_info_t *ns)
1074 {
1075 	const char *bd_addr, *disk = NULL, *state = NULL;
1076 	char *disk_path = NULL;
1077 	di_node_t ctrl_devi;
1078 
1079 	switch (nvme_ns_info_level(ns)) {
1080 	case NVME_NS_DISC_F_ALL:
1081 		disk = "unallocated";
1082 		state = "unallocated";
1083 		break;
1084 	case NVME_NS_DISC_F_ALLOCATED:
1085 		disk = "inactive";
1086 		state = "allocated";
1087 		break;
1088 	case NVME_NS_DISC_F_ACTIVE:
1089 		disk = "ignored";
1090 		state = "active";
1091 		break;
1092 	case NVME_NS_DISC_F_NOT_IGNORED:
1093 		disk = "unattached";
1094 		state = "active-usable";
1095 		break;
1096 	case NVME_NS_DISC_F_BLKDEV:
1097 		disk = "unknown";
1098 		state = "blkdev";
1099 		if (nvme_ns_info_bd_addr(ns, &bd_addr) &&
1100 		    nvme_ctrl_devi(npa->npa_ctrl, &ctrl_devi)) {
1101 			disk_path = nvme_dskname(ctrl_devi, bd_addr);
1102 			disk = disk_path;
1103 		}
1104 		break;
1105 	}
1106 
1107 	if (npa->npa_ofmt != NULL) {
1108 		nvmeadm_list_ofmt_arg_t oarg = { 0 };
1109 
1110 		oarg.nloa_name = npa->npa_ctrl_name;
1111 		oarg.nloa_ctrl = ctrl;
1112 		oarg.nloa_ns = ns;
1113 		oarg.nloa_disk = disk_path;
1114 		oarg.nloa_state = state;
1115 
1116 		ofmt_print(npa->npa_ofmt, &oarg);
1117 	} else {
1118 		(void) printf("  %s/%u (%s)", npa->npa_ctrl_name,
1119 		    nvme_ns_info_nsid(ns), disk);
1120 		if (nvme_ns_info_level(ns) >= NVME_NS_DISC_F_ACTIVE) {
1121 			(void) printf(": ");
1122 			nvme_print_nsid_summary(ns);
1123 		} else {
1124 			(void) printf("\n");
1125 		}
1126 	}
1127 
1128 	free(disk_path);
1129 }
1130 
1131 static int
do_list(const nvme_process_arg_t * npa)1132 do_list(const nvme_process_arg_t *npa)
1133 {
1134 	nvme_ctrl_info_t *info = NULL;
1135 	nvme_ns_iter_t *iter = NULL;
1136 	nvme_iter_t ret;
1137 	const nvme_ns_disc_t *disc;
1138 	nvme_ns_disc_level_t level;
1139 	int rv = -1;
1140 
1141 	if (npa->npa_argc > 0) {
1142 		errx(-1, "%s passed extraneous arguments starting with %s",
1143 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1144 	}
1145 
1146 	if (!nvme_ctrl_info_snap(npa->npa_ctrl, &info)) {
1147 		nvmeadm_warn(npa, "failed to get controller information for %s",
1148 		    npa->npa_ctrl_name);
1149 		return (-1);
1150 	}
1151 
1152 	if (npa->npa_ofmt == NULL) {
1153 		(void) printf("%s: ", npa->npa_ctrl_name);
1154 		nvme_print_ctrl_summary(info);
1155 	} else if ((npa->npa_cmdflags & NVMEADM_O_LS_CTRL) != 0) {
1156 		nvmeadm_list_ofmt_arg_t oarg = { 0 };
1157 		oarg.nloa_name = npa->npa_ctrl_name;
1158 		oarg.nloa_ctrl = info;
1159 
1160 		ofmt_print(npa->npa_ofmt, &oarg);
1161 	}
1162 
1163 	if ((npa->npa_cmdflags & NVMEADM_O_LS_CTRL) != 0) {
1164 		rv = 0;
1165 		goto out;
1166 	}
1167 
1168 	/*
1169 	 * Check if we were given an explicit namespace as an argument. If so,
1170 	 * we always list it and don't need to do discovery.
1171 	 */
1172 	if (npa->npa_ns != NULL) {
1173 		nvme_ns_info_t *ns_info;
1174 
1175 		if (!nvme_ns_info_snap(npa->npa_ns, &ns_info)) {
1176 			nvmeadm_warn(npa, "failed to get namespace "
1177 			    "information for %s", npa->npa_name);
1178 			goto out;
1179 		}
1180 
1181 		do_list_nsid(npa, info, ns_info);
1182 		nvme_ns_info_free(ns_info);
1183 		rv = 0;
1184 		goto out;
1185 	}
1186 
1187 	if (verbose) {
1188 		level = NVME_NS_DISC_F_ALL;
1189 	} else {
1190 		level = NVME_NS_DISC_F_NOT_IGNORED;
1191 	}
1192 
1193 	if (!nvme_ns_discover_init(npa->npa_ctrl, level, &iter)) {
1194 		nvmeadm_warn(npa, "failed to iterate namespaces on %s",
1195 		    npa->npa_ctrl_name);
1196 		goto out;
1197 	}
1198 
1199 	while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
1200 		nvme_ns_info_t *ns_info;
1201 		uint32_t nsid = nvme_ns_disc_nsid(disc);
1202 
1203 		if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, nsid, &ns_info)) {
1204 			nvmeadm_warn(npa, "failed to get namespace "
1205 			    "information for %s/%u", npa->npa_ctrl_name, nsid);
1206 			exitcode = -1;
1207 			continue;
1208 		}
1209 
1210 		do_list_nsid(npa, info, ns_info);
1211 		nvme_ns_info_free(ns_info);
1212 	}
1213 
1214 	nvme_ns_discover_fini(iter);
1215 	if (ret == NVME_ITER_ERROR) {
1216 		nvmeadm_warn(npa, "failed to iterate all namespaces on %s",
1217 		    npa->npa_ctrl_name);
1218 	} else {
1219 		rv = 0;
1220 	}
1221 
1222 out:
1223 	nvme_ctrl_info_free(info);
1224 	return (rv);
1225 }
1226 
1227 static void
optparse_identify_ctrl(nvme_process_arg_t * npa)1228 optparse_identify_ctrl(nvme_process_arg_t *npa)
1229 {
1230 	int c;
1231 
1232 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacn")) != -1) {
1233 		switch (c) {
1234 		case 'C':
1235 			npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
1236 			break;
1237 
1238 		case 'a':
1239 			npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
1240 			break;
1241 
1242 		case 'c':
1243 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1244 			break;
1245 
1246 		case 'n':
1247 			npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
1248 			break;
1249 
1250 		case '?':
1251 			errx(-1, "unknown option: -%c", optopt);
1252 
1253 		case ':':
1254 			errx(-1, "option -%c requires an argument", optopt);
1255 		}
1256 	}
1257 }
1258 
1259 static void
usage_identify_ctrl(const char * c_name)1260 usage_identify_ctrl(const char *c_name)
1261 {
1262 	(void) fprintf(stderr, "%s [-C | -c | [-a] -n] <ctl>[,...]\n\n"
1263 	    "  Print detailed information about the specified NVMe "
1264 	    "controllers.\n", c_name);
1265 }
1266 
1267 static int
do_identify_ctrl(const nvme_process_arg_t * npa)1268 do_identify_ctrl(const nvme_process_arg_t *npa)
1269 {
1270 	boolean_t alloc = B_FALSE;
1271 
1272 	if (npa->npa_ns != NULL)
1273 		errx(-1, "identify-controller cannot be used on namespaces");
1274 
1275 	if (npa->npa_argc > 0) {
1276 		errx(-1, "%s passed extraneous arguments starting with %s",
1277 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1278 	}
1279 
1280 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
1281 	    npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
1282 		errx(-1, "-C cannot be combined with other flags");
1283 	}
1284 
1285 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1286 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1287 		errx(-1, "-c cannot be combined with other flags");
1288 	}
1289 
1290 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
1291 	    npa->npa_cmdflags !=
1292 	    (NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) {
1293 		errx(-1, "-a can only be used together with -n");
1294 	}
1295 
1296 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
1297 		alloc = B_TRUE;
1298 	}
1299 
1300 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) {
1301 		const nvme_identify_nsid_t *idns;
1302 
1303 		if (!nvme_ctrl_info_common_ns(npa->npa_ctrl_info, &idns)) {
1304 			nvmeadm_ctrl_info_warn(npa, "failed to get common "
1305 			    "namespace information for %s", npa->npa_name);
1306 			return (-1);
1307 		}
1308 
1309 		(void) printf("%s: ", npa->npa_name);
1310 		nvme_print_identify_nsid(idns, npa->npa_version);
1311 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) {
1312 		const char *caption;
1313 		uint32_t cns;
1314 		nvme_identify_nsid_list_t *idnslist;
1315 		nvme_id_req_t *req;
1316 
1317 		if (alloc) {
1318 			caption = "Identify Allocated Namespace List";
1319 			cns = NVME_IDENTIFY_NSID_ALLOC_LIST;
1320 		} else {
1321 			caption = "Identify Active Namespace List";
1322 			cns = NVME_IDENTIFY_NSID_LIST;
1323 		}
1324 
1325 		if ((idnslist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1326 			err(-1, "failed to allocate identify buffer size");
1327 		}
1328 
1329 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM, cns,
1330 		    &req)) {
1331 			nvmeadm_fatal(npa, "failed to initialize %s request",
1332 			    caption);
1333 		}
1334 
1335 		/*
1336 		 * Always set the NSID for these requests to NSID 0 so that way
1337 		 * we can start the list at the beginning. When we encounter
1338 		 * devices with more than 1024 NSIDs then we'll need to issue
1339 		 * additional requests.
1340 		 */
1341 		if (!nvme_id_req_set_nsid(req, 0) ||
1342 		    !nvme_id_req_set_output(req, idnslist,
1343 		    NVME_IDENTIFY_BUFSIZE)) {
1344 			nvmeadm_fatal(npa, "failed to set required fields for "
1345 			    "identify request");
1346 		}
1347 
1348 		if (!nvme_id_req_exec(req)) {
1349 			nvmeadm_fatal(npa, "failed to execute identify "
1350 			    "request");
1351 		}
1352 		nvme_id_req_fini(req);
1353 
1354 		(void) printf("%s: ", npa->npa_name);
1355 
1356 		nvme_print_identify_nsid_list(caption, idnslist);
1357 		free(idnslist);
1358 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1359 		nvme_identify_ctrl_list_t *ctlist;
1360 		nvme_id_req_t *req;
1361 
1362 		if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1363 			err(-1, "failed to allocate identify buffer size");
1364 		}
1365 
1366 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1367 		    NVME_IDENTIFY_CTRL_LIST, &req)) {
1368 			nvmeadm_fatal(npa, "failed to initialize identify "
1369 			    "request");
1370 		}
1371 
1372 		if (!nvme_id_req_set_ctrlid(req, 0) ||
1373 		    !nvme_id_req_set_output(req, ctlist,
1374 		    NVME_IDENTIFY_BUFSIZE)) {
1375 			nvmeadm_fatal(npa, "failed to set required fields for "
1376 			    "identify request");
1377 		}
1378 		if (!nvme_id_req_exec(req)) {
1379 			nvmeadm_fatal(npa, "failed to execute identify "
1380 			    "request");
1381 		}
1382 		nvme_id_req_fini(req);
1383 
1384 		(void) printf("%s: ", npa->npa_name);
1385 		nvme_print_identify_ctrl_list("Identify Controller List",
1386 		    ctlist);
1387 		free(ctlist);
1388 	} else {
1389 		uint32_t mpsmin;
1390 
1391 		if (!nvme_ctrl_info_pci_mps_min(npa->npa_ctrl_info,
1392 		    &mpsmin)) {
1393 			nvmeadm_ctrl_info_fatal(npa, "failed to get minimum "
1394 			    "memory page size");
1395 		}
1396 
1397 		(void) printf("%s: ", npa->npa_name);
1398 		nvme_print_identify_ctrl(npa->npa_idctl, mpsmin,
1399 		    npa->npa_version);
1400 	}
1401 
1402 	return (0);
1403 }
1404 
1405 static void
optparse_identify_ns(nvme_process_arg_t * npa)1406 optparse_identify_ns(nvme_process_arg_t *npa)
1407 {
1408 	int c;
1409 
1410 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":cd")) != -1) {
1411 		switch (c) {
1412 		case 'c':
1413 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1414 			break;
1415 
1416 		case 'd':
1417 			npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1418 			break;
1419 
1420 		case '?':
1421 			errx(-1, "unknown option: -%c", optopt);
1422 
1423 		case ':':
1424 			errx(-1, "option -%c requires an argument", optopt);
1425 		}
1426 	}
1427 }
1428 
1429 static void
usage_identify_ns(const char * c_name)1430 usage_identify_ns(const char *c_name)
1431 {
1432 	(void) fprintf(stderr, "%s [-c | -d ] <ctl>/<ns>[,...]\n\n"
1433 	    "  Print detailed information about the specified NVMe "
1434 	    "namespaces.\n", c_name);
1435 }
1436 
1437 static int
do_identify_ns(const nvme_process_arg_t * npa)1438 do_identify_ns(const nvme_process_arg_t *npa)
1439 {
1440 	uint32_t nsid;
1441 
1442 	if (npa->npa_ns == NULL)
1443 		errx(-1, "identify-namespace cannot be used on controllers");
1444 
1445 	if (npa->npa_argc > 0) {
1446 		errx(-1, "%s passed extraneous arguments starting with %s",
1447 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1448 	}
1449 
1450 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1451 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1452 		errx(-1, "-c cannot be combined with other flags");
1453 	}
1454 
1455 	if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1456 	    npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1457 		errx(-1, "-d cannot be combined with other flags");
1458 	}
1459 
1460 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
1461 		errx(-1, "-a cannot be used on namespaces");
1462 	}
1463 
1464 	nsid = nvme_ns_info_nsid(npa->npa_ns_info);
1465 
1466 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1467 		nvme_identify_ctrl_list_t *ctlist;
1468 		nvme_id_req_t *req;
1469 
1470 		if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1471 			err(-1, "failed to allocate identify buffer size");
1472 		}
1473 
1474 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1475 		    NVME_IDENTIFY_NSID_CTRL_LIST, &req)) {
1476 			nvmeadm_fatal(npa, "failed to initialize identify "
1477 			    "request");
1478 		}
1479 
1480 		if (!nvme_id_req_set_nsid(req, nsid) ||
1481 		    !nvme_id_req_set_ctrlid(req, 0) ||
1482 		    !nvme_id_req_set_output(req, ctlist,
1483 		    NVME_IDENTIFY_BUFSIZE)) {
1484 			nvmeadm_fatal(npa, "failed to set required fields for "
1485 			    "identify request");
1486 		}
1487 
1488 		if (!nvme_id_req_exec(req)) {
1489 			nvmeadm_fatal(npa, "failed to execute identify "
1490 			    "request");
1491 		}
1492 		nvme_id_req_fini(req);
1493 
1494 		(void) printf("%s: ", npa->npa_name);
1495 		nvme_print_identify_ctrl_list(
1496 		    "Identify Attached Controller List", ctlist);
1497 		free(ctlist);
1498 	} else if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) {
1499 		nvme_identify_nsid_desc_t *nsdesc;
1500 		nvme_id_req_t *req;
1501 
1502 		if ((nsdesc = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1503 			err(-1, "failed to allocate identify buffer size");
1504 		}
1505 
1506 		if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1507 		    NVME_IDENTIFY_NSID_DESC, &req)) {
1508 			nvmeadm_fatal(npa, "failed to initialize identify "
1509 			    "request");
1510 		}
1511 
1512 		if (!nvme_id_req_set_nsid(req, nsid) ||
1513 		    !nvme_id_req_set_output(req, nsdesc,
1514 		    NVME_IDENTIFY_BUFSIZE)) {
1515 			nvmeadm_fatal(npa, "failed to set required fields for "
1516 			    "identify request");
1517 		}
1518 
1519 		if (!nvme_id_req_exec(req)) {
1520 			nvmeadm_fatal(npa, "failed to execute identify "
1521 			    "request");
1522 		}
1523 		nvme_id_req_fini(req);
1524 
1525 		(void) printf("%s: ", npa->npa_name);
1526 		nvme_print_identify_nsid_desc(nsdesc);
1527 		free(nsdesc);
1528 	} else {
1529 		const nvme_identify_nsid_t *idns;
1530 
1531 		(void) printf("%s: ", npa->npa_name);
1532 		idns = nvme_ns_info_identify(npa->npa_ns_info);
1533 		nvme_print_identify_nsid(idns, npa->npa_version);
1534 	}
1535 
1536 	return (0);
1537 }
1538 
1539 static void
optparse_identify(nvme_process_arg_t * npa)1540 optparse_identify(nvme_process_arg_t *npa)
1541 {
1542 	int c;
1543 
1544 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacdn")) != -1) {
1545 		switch (c) {
1546 		case 'C':
1547 			npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
1548 			break;
1549 
1550 		case 'a':
1551 			npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
1552 			break;
1553 
1554 		case 'c':
1555 			npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1556 			break;
1557 
1558 		case 'd':
1559 			npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1560 			break;
1561 
1562 		case 'n':
1563 			npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
1564 			break;
1565 
1566 		case '?':
1567 			errx(-1, "unknown option: -%c", optopt);
1568 
1569 		case ':':
1570 			errx(-1, "option -%c requires an argument", optopt);
1571 
1572 		}
1573 	}
1574 
1575 	if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
1576 	    (npa->npa_cmdflags &
1577 	    ~(NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) != 0) {
1578 		errx(-1, "-a can only be used alone or together with -n");
1579 	}
1580 
1581 	if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
1582 	    npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
1583 		errx(-1, "-C cannot be combined with other flags");
1584 
1585 	}
1586 
1587 	if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1588 	    npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1589 		errx(-1, "-c cannot be combined with other flags");
1590 	}
1591 
1592 	if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1593 	    npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1594 		errx(-1, "-d cannot be combined with other flags");
1595 	}
1596 }
1597 
1598 static void
usage_identify(const char * c_name)1599 usage_identify(const char *c_name)
1600 {
1601 	(void) fprintf(stderr,
1602 	    "%s [ -C | -c | -d | [-a] -n ] <ctl>[/<ns>][,...]\n\n"
1603 	    "  Print detailed information about the specified NVMe "
1604 	    "controllers and/or name-\n  spaces.\n", c_name);
1605 }
1606 
1607 static int
do_identify(const nvme_process_arg_t * npa)1608 do_identify(const nvme_process_arg_t *npa)
1609 {
1610 	if (npa->npa_argc > 0) {
1611 		errx(-1, "%s passed extraneous arguments starting with %s",
1612 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
1613 	}
1614 
1615 	if (npa->npa_ns != NULL) {
1616 		if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0)
1617 			errx(-1, "-C cannot be used on namespaces");
1618 
1619 		if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0)
1620 			errx(-1, "-a cannot be used on namespaces");
1621 
1622 		if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0)
1623 			errx(-1, "-n cannot be used on namespaces");
1624 
1625 		return (do_identify_ns(npa));
1626 	} else {
1627 		if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0)
1628 			errx(-1, "-d cannot be used on controllers");
1629 
1630 		return (do_identify_ctrl(npa));
1631 	}
1632 }
1633 
1634 static void
optparse_list_logs(nvme_process_arg_t * npa)1635 optparse_list_logs(nvme_process_arg_t *npa)
1636 {
1637 	int c;
1638 	uint_t oflags = 0;
1639 	boolean_t parse = B_FALSE;
1640 	const char *fields = NULL;
1641 	char *scope = NULL;
1642 	ofmt_status_t oferr;
1643 	nvmeadm_list_logs_t *nll;
1644 
1645 	if ((nll = calloc(1, sizeof (nvmeadm_list_logs_t))) == NULL) {
1646 		err(-1, "failed to allocate memory to track log information");
1647 	}
1648 
1649 	npa->npa_cmd_arg = nll;
1650 
1651 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:ps:")) != -1) {
1652 		switch (c) {
1653 		case 'a':
1654 			nll->nll_unimpl = B_TRUE;
1655 			break;
1656 		case 'H':
1657 			oflags |= OFMT_NOHEADER;
1658 			break;
1659 		case 'o':
1660 			fields = optarg;
1661 			break;
1662 		case 'p':
1663 			parse = B_TRUE;
1664 			oflags |= OFMT_PARSABLE;
1665 			break;
1666 		case 's':
1667 			scope = optarg;
1668 			break;
1669 		case '?':
1670 			errx(-1, "unknown option: -%c", optopt);
1671 		case ':':
1672 			errx(-1, "option -%c requires an argument", optopt);
1673 		}
1674 	}
1675 
1676 	if (!parse) {
1677 		oflags |= OFMT_WRAP;
1678 	}
1679 
1680 	if (parse && fields == NULL) {
1681 		errx(-1, "parsable mode (-p) requires fields specified with "
1682 		    "-o");
1683 	}
1684 
1685 	if (fields == NULL) {
1686 		if (nll->nll_unimpl) {
1687 			fields = nvmeadm_list_logs_fields_impl;
1688 		} else {
1689 			fields = nvmeadm_list_logs_fields;
1690 		}
1691 	}
1692 
1693 	if (scope != NULL) {
1694 		const char *str;
1695 
1696 		while ((str = strsep(&scope, ",")) != NULL) {
1697 			if (strcasecmp(str, "nvm") == 0) {
1698 				nll->nll_scope |= NVME_LOG_SCOPE_NVM;
1699 			} else if (strcasecmp(str, "ns") == 0 ||
1700 			    strcasecmp(str, "namespace") == 0) {
1701 				nll->nll_scope |= NVME_LOG_SCOPE_NS;
1702 			} else if (strcasecmp(str, "ctrl") == 0 ||
1703 			    strcasecmp(str, "controller") == 0) {
1704 				nll->nll_scope |= NVME_LOG_SCOPE_CTRL;
1705 			} else {
1706 				errx(-1, "unknown scope string: '%s'; valid "
1707 				    "values are 'nvm', 'namespace', and "
1708 				    "'controller'", str);
1709 			}
1710 		}
1711 	}
1712 
1713 	oferr = ofmt_open(fields, nvmeadm_list_logs_ofmt, oflags, 0,
1714 	    &npa->npa_ofmt);
1715 	ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
1716 
1717 	if (npa->npa_argc - optind > 1) {
1718 		nll->nll_nfilts = npa->npa_argc - optind - 1;
1719 		nll->nll_filts = npa->npa_argv + optind + 1;
1720 		nll->nll_used = calloc(nll->nll_nfilts, sizeof (boolean_t));
1721 		if (nll->nll_used == NULL) {
1722 			err(-1, "failed to allocate memory for tracking log "
1723 			    "page filters");
1724 		}
1725 	}
1726 }
1727 
1728 static void
usage_list_logs(const char * c_name)1729 usage_list_logs(const char *c_name)
1730 {
1731 	(void) fprintf(stderr, "%s [-H] [-o field,[...] [-p]] [-s scope,[...]] "
1732 	    "[-a]\n\t  [<ctl>[/<ns>][,...] [logpage...]\n\n"
1733 	    "  List log pages supported by controllers or namespaces.\n",
1734 	    c_name);
1735 }
1736 
1737 static boolean_t
do_list_logs_match(const nvme_log_disc_t * disc,nvmeadm_list_logs_t * nll)1738 do_list_logs_match(const nvme_log_disc_t *disc, nvmeadm_list_logs_t *nll)
1739 {
1740 	if (!nll->nll_unimpl && !nvme_log_disc_impl(disc)) {
1741 		return (B_FALSE);
1742 	}
1743 
1744 	if (nll->nll_nfilts <= 0) {
1745 		return (B_TRUE);
1746 	}
1747 
1748 	for (int i = 0; i < nll->nll_nfilts; i++) {
1749 		if (strcmp(nvme_log_disc_name(disc), nll->nll_filts[i]) == 0) {
1750 			nll->nll_used[i] = B_TRUE;
1751 			return (B_TRUE);
1752 		}
1753 	}
1754 
1755 	return (B_FALSE);
1756 }
1757 
1758 static int
do_list_logs(const nvme_process_arg_t * npa)1759 do_list_logs(const nvme_process_arg_t *npa)
1760 {
1761 	nvme_log_disc_scope_t scope;
1762 	nvme_log_iter_t *iter;
1763 	nvme_iter_t ret;
1764 	const nvme_log_disc_t *disc;
1765 	nvmeadm_list_logs_t *nll = npa->npa_cmd_arg;
1766 
1767 	if (nll->nll_scope != 0) {
1768 		scope = nll->nll_scope;
1769 	} else if (npa->npa_ns != NULL) {
1770 		scope = NVME_LOG_SCOPE_NS;
1771 	} else {
1772 		scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM;
1773 	}
1774 
1775 	if (!nvme_log_discover_init(npa->npa_ctrl, scope, 0, &iter)) {
1776 		nvmeadm_warn(npa, "failed to iterate logs on %s",
1777 		    npa->npa_ctrl_name);
1778 		return (-1);
1779 	}
1780 
1781 	while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) {
1782 		if (do_list_logs_match(disc, nll)) {
1783 			nvmeadm_list_logs_ofmt_arg_t print;
1784 
1785 			print.nlloa_name = npa->npa_name;
1786 			print.nlloa_disc = disc;
1787 			ofmt_print(npa->npa_ofmt, &print);
1788 			nll->nll_nprint++;
1789 		}
1790 	}
1791 
1792 	nvme_log_discover_fini(iter);
1793 	if (ret == NVME_ITER_ERROR) {
1794 		nvmeadm_warn(npa, "failed to iterate logs on %s",
1795 		    npa->npa_ctrl_name);
1796 		return (-1);
1797 	}
1798 
1799 	for (int i = 0; i < nll->nll_nfilts; i++) {
1800 		if (!nll->nll_used[i]) {
1801 			warnx("log page filter '%s' did match any log pages",
1802 			    nll->nll_filts[i]);
1803 			exitcode = -1;
1804 		}
1805 	}
1806 
1807 	if (nll->nll_nprint == 0) {
1808 		if (nll->nll_nfilts == 0) {
1809 			warnx("no log pages found for %s", npa->npa_name);
1810 		}
1811 		exitcode = -1;
1812 	}
1813 
1814 	return (exitcode);
1815 }
1816 
1817 static void
usage_get_logpage(const char * c_name)1818 usage_get_logpage(const char *c_name)
1819 {
1820 	(void) fprintf(stderr, "%s [-O file] <ctl>[/<ns>][,...] <logpage>\n\n"
1821 	    "  Print the specified log page of the specified NVMe "
1822 	    "controllers and/or name-\n  spaces. Run nvmeadm list-logpages "
1823 	    "for supported log pages. All devices\n support error, health, "
1824 	    "and firmware.\n", c_name);
1825 }
1826 
1827 static void
usage_firmware_list(const char * c_name)1828 usage_firmware_list(const char *c_name)
1829 {
1830 	(void) fprintf(stderr, "%s <ctl>\n\n"
1831 	    "  Print the log page that contains the list of firmware "
1832 	    "images installed on the specified NVMe controller.\n", c_name);
1833 }
1834 
1835 static uint64_t
do_get_logpage_size(const nvme_process_arg_t * npa,nvme_log_disc_t * disc,nvme_log_req_t * req)1836 do_get_logpage_size(const nvme_process_arg_t *npa, nvme_log_disc_t *disc,
1837     nvme_log_req_t *req)
1838 {
1839 	uint64_t len, ret;
1840 	void *buf;
1841 	nvme_log_size_kind_t kind;
1842 
1843 	kind = nvme_log_disc_size(disc, &len);
1844 	if (kind != NVME_LOG_SIZE_K_VAR) {
1845 		return (len);
1846 	}
1847 
1848 	/*
1849 	 * We have a log with a variable length size. To determine the actual
1850 	 * size we must actually determine the full length of this.
1851 	 */
1852 	if ((buf = malloc(len)) == NULL) {
1853 		errx(-1, "failed to allocate %zu byte buffer to get log "
1854 		    "page size", len);
1855 	}
1856 
1857 	if (!nvme_log_req_set_output(req, buf, len)) {
1858 		nvmeadm_fatal(npa, "failed to set output parameters to "
1859 		    "determine log length");
1860 	}
1861 
1862 	if (!nvme_log_req_exec(req)) {
1863 		nvmeadm_fatal(npa, "failed to execute log request %s to "
1864 		    "determine log length", npa->npa_argv[0]);
1865 	}
1866 
1867 	if (!nvme_log_disc_calc_size(disc, &ret, buf, len)) {
1868 		errx(-1, "failed to determine full %s log length",
1869 		    npa->npa_argv[0]);
1870 	}
1871 
1872 	free(buf);
1873 	return (ret);
1874 }
1875 
1876 static void
do_get_logpage_dump(const void * buf,size_t len,const char * file)1877 do_get_logpage_dump(const void *buf, size_t len, const char *file)
1878 {
1879 	size_t off = 0;
1880 	int fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
1881 
1882 	if (fd < 0) {
1883 		err(-1, "failed to create output file %s", file);
1884 	}
1885 
1886 	while (len > 0) {
1887 		ssize_t ret = write(fd, buf + off, len - off);
1888 		if (ret < 0) {
1889 			err(EXIT_FAILURE, "failed to write log data to file %s "
1890 			    "at offset %zu", file, off);
1891 		}
1892 
1893 		off += (size_t)ret;
1894 		len -= (size_t)ret;
1895 	}
1896 
1897 	(void) close(fd);
1898 }
1899 
1900 /*
1901  * Here we need to explicitly attempt to release any context that has previously
1902  * existed for the persistent event log. It is fine if none exists as the
1903  * controller is required not to error. However, if we don't do this and attempt
1904  * to establish a new context, then it will generate an error.
1905  *
1906  * We'll use our existing request, which doesn't ask for data yet and issue the
1907  * get log page request with the LSP in question. After it is completed, we'll
1908  * reset the LSP to establish a context.
1909  */
1910 static void
do_get_logpage_pev_relctx(const nvme_process_arg_t * npa,nvme_log_req_t * req)1911 do_get_logpage_pev_relctx(const nvme_process_arg_t *npa, nvme_log_req_t *req)
1912 {
1913 	uint32_t buf;
1914 
1915 	if (!nvme_log_req_set_lsp(req, NVME_PEV_LSP_REL_CTX)) {
1916 		nvmeadm_fatal(npa, "failed to set lsp to release the "
1917 		    "persistent event log context");
1918 	}
1919 
1920 	/*
1921 	 * In NVMe 2.0 the spec made it explicit that the controller was
1922 	 * supposed to ignore the length and offset for a request to release the
1923 	 * context; however, that wasn't present in NVMe 1.4. The number of
1924 	 * dwords part of the get log page command is a zeros based value,
1925 	 * meaning there is no explicit way to request zero bytes. Rather than
1926 	 * trust that all controllers get this right (especially when it wasn't
1927 	 * exactly specified in NVMe 1.4), we just toss a throwaway buffer here.
1928 	 */
1929 	if (!nvme_log_req_set_output(req, &buf, sizeof (buf))) {
1930 		nvmeadm_fatal(npa, "failed to set zero log length for "
1931 		    "persistent event log release context");
1932 	}
1933 
1934 	if (!nvme_log_req_exec(req)) {
1935 		nvmeadm_fatal(npa, "failed to execute log request %s to "
1936 		    "release the event log context", npa->npa_argv[0]);
1937 	}
1938 
1939 	if (!nvme_log_req_set_lsp(req, NVME_PEV_LSP_EST_CTX_READ)) {
1940 		nvmeadm_fatal(npa, "failed to set lsp to establish the "
1941 		    "persistent event log context");
1942 	}
1943 
1944 	/*
1945 	 * Make sure that our stack buffer is no longer part of the log request.
1946 	 */
1947 	if (!nvme_log_req_clear_output(req)) {
1948 		nvmeadm_fatal(npa, "failed to clear output from persistent "
1949 		    "event log release context");
1950 	}
1951 }
1952 
1953 static int
do_get_logpage_common(const nvme_process_arg_t * npa,const char * page)1954 do_get_logpage_common(const nvme_process_arg_t *npa, const char *page)
1955 {
1956 	int ret = 0;
1957 	nvme_log_disc_t *disc;
1958 	nvme_log_req_t *req;
1959 	nvme_log_disc_scope_t scope;
1960 	void *buf;
1961 	size_t toalloc;
1962 	nvmeadm_get_logpage_t *log = npa->npa_cmd_arg;
1963 
1964 	/*
1965 	 * If we have enough information to identify a log-page via libnvme (or
1966 	 * in the future take enough options to allow us to actually do this
1967 	 * manually), then we will fetch it. If we don't know how to print it,
1968 	 * then we'll just hex dump it for now.
1969 	 */
1970 	if (!nvme_log_req_init_by_name(npa->npa_ctrl, page, 0, &disc, &req)) {
1971 		nvmeadm_fatal(npa, "could not initialize log request for %s",
1972 		    page);
1973 	}
1974 
1975 	if (npa->npa_ns != NULL) {
1976 		scope = NVME_LOG_SCOPE_NS;
1977 	} else {
1978 		scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM;
1979 	}
1980 
1981 	if ((scope & nvme_log_disc_scopes(disc)) == 0) {
1982 		errx(-1, "log page %s does not support operating on %s", page,
1983 		    npa->npa_ns != NULL ? "namespaces" : "controllers");
1984 	}
1985 
1986 	/*
1987 	 * In the future we should add options to allow one to specify and set
1988 	 * the fields for the lsp, lsi, etc. and set them here. Some log pages
1989 	 * need a specific lsp set and special handling related to contexts. Do
1990 	 * that now.
1991 	 */
1992 	switch (nvme_log_disc_lid(disc)) {
1993 	case NVME_LOGPAGE_PEV:
1994 		do_get_logpage_pev_relctx(npa, req);
1995 		break;
1996 	default:
1997 		break;
1998 	}
1999 
2000 	if (npa->npa_ns != NULL) {
2001 		uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
2002 
2003 		if (!nvme_log_req_set_nsid(req, nsid)) {
2004 			nvmeadm_fatal(npa, "failed to set log request "
2005 			    "namespace ID to 0x%x", nsid);
2006 		}
2007 	}
2008 
2009 	/*
2010 	 * The output size should be the last thing that we determine as we may
2011 	 * need to issue a log request to figure out how much data we should
2012 	 * actually be reading.
2013 	 */
2014 	toalloc = do_get_logpage_size(npa, disc, req);
2015 	buf = malloc(toalloc);
2016 	if (buf == NULL) {
2017 		err(-1, "failed to allocate %zu bytes for log "
2018 		    "request %s", toalloc, page);
2019 	}
2020 
2021 	if (!nvme_log_req_set_output(req, buf, toalloc)) {
2022 		nvmeadm_fatal(npa, "failed to set output parameters");
2023 	}
2024 
2025 
2026 	/*
2027 	 * Again, we need to potentially adjust specific LSP values here for the
2028 	 * various contexts that exist.
2029 	 */
2030 	switch (nvme_log_disc_lid(disc)) {
2031 	case NVME_LOGPAGE_PEV:
2032 		if (!nvme_log_req_set_lsp(req, NVME_PEV_LSP_READ)) {
2033 			nvmeadm_fatal(npa, "failed to set lsp to read the "
2034 			    "persistent event log");
2035 		}
2036 		break;
2037 	default:
2038 		break;
2039 	}
2040 
2041 	if (!nvme_log_req_exec(req)) {
2042 		nvmeadm_fatal(npa, "failed to execute log request %s",
2043 		    npa->npa_argv[0]);
2044 	}
2045 
2046 	if (log != NULL && log->ngl_output != NULL) {
2047 		do_get_logpage_dump(buf, toalloc, log->ngl_output);
2048 		goto done;
2049 	}
2050 
2051 	(void) printf("%s: ", npa->npa_name);
2052 	if (strcmp(page, "error") == 0) {
2053 		size_t nlog = toalloc / sizeof (nvme_error_log_entry_t);
2054 		nvme_print_error_log(nlog, buf, npa->npa_version);
2055 	} else if (strcmp(page, "health") == 0) {
2056 		nvme_print_health_log(buf, npa->npa_idctl, npa->npa_version);
2057 	} else if (strcmp(page, "firmware") == 0) {
2058 		nvme_print_fwslot_log(buf, npa->npa_idctl);
2059 	} else {
2060 		(void) printf("%s (%s)\n", nvme_log_disc_desc(disc), page);
2061 		nvmeadm_dump_hex(buf, toalloc);
2062 	}
2063 
2064 done:
2065 	free(buf);
2066 	nvme_log_disc_free(disc);
2067 	nvme_log_req_fini(req);
2068 
2069 	return (ret);
2070 }
2071 
2072 static int
do_get_logpage_fwslot(const nvme_process_arg_t * npa)2073 do_get_logpage_fwslot(const nvme_process_arg_t *npa)
2074 {
2075 	if (npa->npa_argc >= 1) {
2076 		warnx("no additional arguments may be specified to %s",
2077 		    npa->npa_cmd->c_name);
2078 		usage(npa->npa_cmd);
2079 		exit(-1);
2080 	}
2081 
2082 	return (do_get_logpage_common(npa, "firmware"));
2083 }
2084 
2085 static void
optparse_get_logpage(nvme_process_arg_t * npa)2086 optparse_get_logpage(nvme_process_arg_t *npa)
2087 {
2088 	int c;
2089 	const char *output = NULL;
2090 	nvmeadm_get_logpage_t *log;
2091 
2092 	if ((log = calloc(1, sizeof (nvmeadm_get_logpage_t))) == NULL) {
2093 		err(-1, "failed to allocate memory to track log page "
2094 		    "information");
2095 	}
2096 
2097 	npa->npa_cmd_arg = log;
2098 
2099 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":O:")) != -1) {
2100 		switch (c) {
2101 		case 'O':
2102 			output = optarg;
2103 			break;
2104 		case '?':
2105 			errx(-1, "unknown option: -%c", optopt);
2106 		case ':':
2107 			errx(-1, "option -%c requires an argument", optopt);
2108 		}
2109 	}
2110 
2111 	log->ngl_output = output;
2112 }
2113 
2114 static int
do_get_logpage(const nvme_process_arg_t * npa)2115 do_get_logpage(const nvme_process_arg_t *npa)
2116 {
2117 
2118 	if (npa->npa_argc < 1) {
2119 		warnx("missing log page name");
2120 		usage(npa->npa_cmd);
2121 		exit(-1);
2122 	}
2123 
2124 	if (npa->npa_argc > 1) {
2125 		warnx("only a single log page may be specified at a time");
2126 		usage(npa->npa_cmd);
2127 		exit(-1);
2128 	}
2129 
2130 	return (do_get_logpage_common(npa, npa->npa_argv[0]));
2131 }
2132 
2133 static void
optparse_list_features(nvme_process_arg_t * npa)2134 optparse_list_features(nvme_process_arg_t *npa)
2135 {
2136 	int c;
2137 	uint_t oflags = 0;
2138 	boolean_t parse = B_FALSE;
2139 	const char *fields = NULL;
2140 	nvmeadm_features_t *feat;
2141 	ofmt_status_t oferr;
2142 
2143 	if ((feat = calloc(1, sizeof (nvmeadm_features_t))) == NULL) {
2144 		err(-1, "failed to allocate memory to track feature "
2145 		    "information");
2146 	}
2147 
2148 	npa->npa_cmd_arg = feat;
2149 
2150 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:p")) != -1) {
2151 		switch (c) {
2152 		case 'a':
2153 			feat->nf_unimpl = B_TRUE;
2154 			break;
2155 		case 'H':
2156 			oflags |= OFMT_NOHEADER;
2157 			break;
2158 		case 'o':
2159 			fields = optarg;
2160 			break;
2161 		case 'p':
2162 			parse = B_TRUE;
2163 			oflags |= OFMT_PARSABLE;
2164 			break;
2165 		case '?':
2166 			errx(-1, "unknown option: -%c", optopt);
2167 		case ':':
2168 			errx(-1, "option -%c requires an argument", optopt);
2169 		}
2170 	}
2171 
2172 	if (!parse) {
2173 		oflags |= OFMT_WRAP;
2174 	}
2175 
2176 	if (parse && fields == NULL) {
2177 		errx(-1, "parsable mode (-p) requires fields specified with "
2178 		    "-o");
2179 	}
2180 
2181 	if (fields == NULL) {
2182 		fields = nvmeadm_list_features_fields;
2183 	}
2184 
2185 	oferr = ofmt_open(fields, nvmeadm_list_features_ofmt, oflags, 0,
2186 	    &npa->npa_ofmt);
2187 	ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
2188 
2189 	if (npa->npa_argc - optind > 1) {
2190 		feat->nf_nfilts = (uint32_t)(npa->npa_argc - optind - 1);
2191 		feat->nf_filts = npa->npa_argv + optind + 1;
2192 		feat->nf_used = calloc(feat->nf_nfilts, sizeof (boolean_t));
2193 		if (feat->nf_used == NULL) {
2194 			err(-1, "failed to allocate memory for tracking "
2195 			    "feature filters");
2196 		}
2197 	}
2198 }
2199 
2200 static void
usage_list_features(const char * c_name)2201 usage_list_features(const char *c_name)
2202 {
2203 	(void) fprintf(stderr, "%s [-a] [-H] [-o field,[...] [-p]] "
2204 	    "<ctl>[/<ns>][,...]\n\t  [feature...]\n\n"
2205 	    "  List features supported by controllers or namespaces.\n",
2206 	    c_name);
2207 }
2208 
2209 static boolean_t
do_features_match(const nvme_feat_disc_t * disc,nvmeadm_features_t * nf)2210 do_features_match(const nvme_feat_disc_t *disc, nvmeadm_features_t *nf)
2211 {
2212 	if (nf->nf_nfilts == 0) {
2213 		return (B_TRUE);
2214 	}
2215 
2216 	for (uint32_t i = 0; i < nf->nf_nfilts; i++) {
2217 		const char *match = nf->nf_filts[i];
2218 		long long fid;
2219 		const char *err;
2220 
2221 		if (strcmp(nvme_feat_disc_short(disc), match) == 0 ||
2222 		    strcasecmp(nvme_feat_disc_spec(disc), match) == 0) {
2223 			nf->nf_used[i] = B_TRUE;
2224 			return (B_TRUE);
2225 		}
2226 
2227 		fid = strtonumx(match, 0, UINT32_MAX, &err, 0);
2228 		if (err == NULL && fid == nvme_feat_disc_fid(disc)) {
2229 			nf->nf_used[i] = B_TRUE;
2230 			return (B_TRUE);
2231 		}
2232 	}
2233 
2234 	return (B_FALSE);
2235 }
2236 
2237 
2238 /*
2239  * This is a common entry point for both list-features and get-features, which
2240  * iterate over all features and take action for each one.
2241  */
2242 typedef void (*do_features_cb_f)(const nvme_process_arg_t *,
2243     const nvme_feat_disc_t *);
2244 static int
do_features(const nvme_process_arg_t * npa,nvmeadm_features_t * nf,do_features_cb_f func)2245 do_features(const nvme_process_arg_t *npa, nvmeadm_features_t *nf,
2246     do_features_cb_f func)
2247 {
2248 	nvme_feat_scope_t scope;
2249 	nvme_feat_iter_t *iter;
2250 	nvme_iter_t ret;
2251 	const nvme_feat_disc_t *disc;
2252 
2253 	if (npa->npa_ns != NULL) {
2254 		scope = NVME_FEAT_SCOPE_NS;
2255 	} else {
2256 		scope = NVME_FEAT_SCOPE_CTRL;
2257 	}
2258 
2259 	if (!nvme_feat_discover_init(npa->npa_ctrl, scope, 0, &iter)) {
2260 		nvmeadm_warn(npa, "failed to iterate features on %s",
2261 		    npa->npa_ctrl_name);
2262 		return (-1);
2263 	}
2264 
2265 	while ((ret = nvme_feat_discover_step(iter, &disc)) ==
2266 	    NVME_ITER_VALID) {
2267 		if (do_features_match(disc, nf)) {
2268 			if (!nf->nf_unimpl && nvme_feat_disc_impl(disc) ==
2269 			    NVME_FEAT_IMPL_UNSUPPORTED) {
2270 				continue;
2271 			}
2272 
2273 			func(npa, disc);
2274 			nf->nf_nprint++;
2275 		}
2276 	}
2277 
2278 	nvme_feat_discover_fini(iter);
2279 	if (ret == NVME_ITER_ERROR) {
2280 		nvmeadm_warn(npa, "failed to iterate features on %s",
2281 		    npa->npa_ctrl_name);
2282 		return (-1);
2283 	}
2284 
2285 	for (uint32_t i = 0; i < nf->nf_nfilts; i++) {
2286 		if (!nf->nf_used[i]) {
2287 			warnx("feature filter '%s' did match any features",
2288 			    nf->nf_filts[i]);
2289 			exitcode = -1;
2290 		}
2291 	}
2292 
2293 	if (nf->nf_nprint == 0) {
2294 		if (nf->nf_nfilts == 0) {
2295 			warnx("no features found for %s", npa->npa_name);
2296 		}
2297 		exitcode = -1;
2298 	}
2299 
2300 	return (exitcode);
2301 }
2302 
2303 static void
do_list_features_cb(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)2304 do_list_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc)
2305 {
2306 	nvmeadm_list_features_ofmt_arg_t print;
2307 
2308 	print.nlfoa_name = npa->npa_name;
2309 	print.nlfoa_feat = disc;
2310 	ofmt_print(npa->npa_ofmt, &print);
2311 }
2312 
2313 static int
do_list_features(const nvme_process_arg_t * npa)2314 do_list_features(const nvme_process_arg_t *npa)
2315 {
2316 	nvmeadm_features_t *nf = npa->npa_cmd_arg;
2317 
2318 	return (do_features(npa, nf, do_list_features_cb));
2319 }
2320 
2321 static void
usage_get_features(const char * c_name)2322 usage_get_features(const char *c_name)
2323 {
2324 	(void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
2325 	    "  Print the specified features of the specified NVMe controllers "
2326 	    "and/or\n  namespaces. Feature support varies on the controller.\n"
2327 	    "Run 'nvmeadm list-features <ctl>' to see supported features.\n",
2328 	    c_name);
2329 }
2330 
2331 /*
2332  * The nvmeadm(8) get-features output has traditionally swallowed certain errors
2333  * for features that it considers unimplemented in tandem with the kernel. With
2334  * the introduction of libnvme and ioctl interface changes, the kernel no longer
2335  * caches information about features that are unimplemented.
2336  *
2337  * There are two cases that we currently swallow errors on and the following
2338  * must all be true:
2339  *
2340  * 1) We have a controller error.
2341  * 2) The system doesn't know whether the feature is implemented or not.
2342  * 3) The controller error indicates that we have an invalid field.
2343  *
2344  * There is one additional wrinkle that we are currently papering over due to
2345  * the history of nvmeadm swallowing errors. The error recovery feature was made
2346  * explicitly namespace-specific in NVMe 1.4. However, various NVMe 1.3 devices
2347  * will error if we ask for it without specifying a namespace. Conversely, older
2348  * devices will be upset if you do ask for a namespace. This case can be removed
2349  * once we better survey devices and come up with a heuristic for how to handle
2350  * this across older generations.
2351  *
2352  * If we add a single feature endpoint that gives flexibility over how the
2353  * feature are listed, then we should not swallow errors.
2354  */
2355 static boolean_t
swallow_get_feat_err(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)2356 swallow_get_feat_err(const nvme_process_arg_t *npa,
2357     const nvme_feat_disc_t *disc)
2358 {
2359 	uint32_t sct, sc;
2360 
2361 	if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_CONTROLLER) {
2362 		return (B_FALSE);
2363 	}
2364 
2365 	nvme_ctrl_deverr(npa->npa_ctrl, &sct, &sc);
2366 	if (nvme_feat_disc_impl(disc) == NVME_FEAT_IMPL_UNKNOWN &&
2367 	    sct == NVME_CQE_SCT_GENERIC && sc == NVME_CQE_SC_GEN_INV_FLD) {
2368 		return (B_TRUE);
2369 	}
2370 
2371 	if (nvme_feat_disc_fid(disc) == NVME_FEAT_ERROR &&
2372 	    sct == NVME_CQE_SCT_GENERIC && (sc == NVME_CQE_SC_GEN_INV_FLD ||
2373 	    sc == NVME_CQE_SC_GEN_INV_NS)) {
2374 		return (B_TRUE);
2375 	}
2376 
2377 	return (B_FALSE);
2378 }
2379 
2380 static boolean_t
do_get_feat_common(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,uint32_t cdw11,uint32_t * cdw0,void ** datap,size_t * lenp)2381 do_get_feat_common(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc,
2382     uint32_t cdw11, uint32_t *cdw0, void **datap, size_t *lenp)
2383 {
2384 	nvme_get_feat_req_t *req = NULL;
2385 	void *data = NULL;
2386 	uint64_t datalen = 0;
2387 	nvme_get_feat_fields_t fields = nvme_feat_disc_fields_get(disc);
2388 
2389 	if (!nvme_get_feat_req_init_by_disc(npa->npa_ctrl, disc, &req)) {
2390 		nvmeadm_warn(npa, "failed to initialize get feature request "
2391 		    "for feature %s", nvme_feat_disc_short(disc));
2392 		exitcode = -1;
2393 		goto err;
2394 	}
2395 
2396 	if ((fields & NVME_GET_FEAT_F_CDW11) != 0 &&
2397 	    !nvme_get_feat_req_set_cdw11(req, cdw11)) {
2398 		nvmeadm_warn(npa, "failed to set cdw11 to 0x%x for feature %s",
2399 		    cdw11, nvme_feat_disc_short(disc));
2400 		exitcode = -1;
2401 		goto err;
2402 	}
2403 
2404 	if ((fields & NVME_GET_FEAT_F_DATA) != 0) {
2405 		datalen = nvme_feat_disc_data_size(disc);
2406 		VERIFY3U(datalen, !=, 0);
2407 		data = malloc(datalen);
2408 		if (data == NULL) {
2409 			err(-1, "failed to allocate %zu bytes for feature %s "
2410 			    "data buffer", datalen, nvme_feat_disc_short(disc));
2411 		}
2412 
2413 		if (!nvme_get_feat_req_set_output(req, data, datalen)) {
2414 			nvmeadm_warn(npa, "failed to set output data for "
2415 			    "feature %s", nvme_feat_disc_short(disc));
2416 			exitcode = -1;
2417 			goto err;
2418 		}
2419 	}
2420 
2421 	if ((fields & NVME_GET_FEAT_F_NSID) != 0) {
2422 		uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
2423 
2424 		if (!nvme_get_feat_req_set_nsid(req, nsid)) {
2425 			nvmeadm_warn(npa, "failed to set nsid to 0x%x for "
2426 			    "feature %s", nsid, nvme_feat_disc_spec(disc));
2427 			exitcode = -1;
2428 			goto err;
2429 		}
2430 	}
2431 
2432 	if (!nvme_get_feat_req_exec(req)) {
2433 		if (!swallow_get_feat_err(npa, disc)) {
2434 			nvmeadm_warn(npa, "failed to get feature %s",
2435 			    nvme_feat_disc_spec(disc));
2436 			exitcode = -1;
2437 		}
2438 
2439 		goto err;
2440 	}
2441 
2442 	if (!nvme_get_feat_req_get_cdw0(req, cdw0)) {
2443 		nvmeadm_warn(npa, "failed to get cdw0 result data for %s",
2444 		    nvme_feat_disc_spec(disc));
2445 		goto err;
2446 	}
2447 
2448 	*datap = data;
2449 	*lenp = datalen;
2450 	nvme_get_feat_req_fini(req);
2451 	return (B_TRUE);
2452 
2453 err:
2454 	free(data);
2455 	nvme_get_feat_req_fini(req);
2456 	return (B_FALSE);
2457 }
2458 
2459 static void
do_get_feat_temp_thresh_one(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,const nvmeadm_feature_t * feat,const char * label,uint16_t tmpsel,uint16_t thsel)2460 do_get_feat_temp_thresh_one(const nvme_process_arg_t *npa,
2461     const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat,
2462     const char *label, uint16_t tmpsel, uint16_t thsel)
2463 {
2464 	uint32_t cdw0;
2465 	void *buf = NULL;
2466 	size_t buflen;
2467 	nvme_temp_threshold_t tt;
2468 
2469 	tt.r = 0;
2470 	tt.b.tt_tmpsel = tmpsel;
2471 	tt.b.tt_thsel = thsel;
2472 
2473 	/*
2474 	 * The printing function treats the buffer argument as the label to
2475 	 * print for this threshold.
2476 	 */
2477 	if (!do_get_feat_common(npa, disc, tt.r, &cdw0, &buf, &buflen)) {
2478 		return;
2479 	}
2480 
2481 	feat->f_print(cdw0, (void *)label, 0, npa->npa_idctl,
2482 	    npa->npa_version);
2483 	free(buf);
2484 }
2485 
2486 /*
2487  * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the
2488  * device and changed the main device to have a composite temperature sensor. As
2489  * a result, there is a set of thresholds for each sensor. In addition, they
2490  * added both an over-temperature and under-temperature threshold. Since most
2491  * devices don't actually implement all the sensors, we get the health page and
2492  * see which sensors have a non-zero value to determine how to proceed.
2493  */
2494 static boolean_t
do_get_feat_temp_thresh(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,const nvmeadm_feature_t * feat)2495 do_get_feat_temp_thresh(const nvme_process_arg_t *npa,
2496     const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat)
2497 {
2498 	nvme_log_req_t *req = NULL;
2499 	nvme_log_disc_t *log_disc = NULL;
2500 	size_t toalloc;
2501 	void *buf = NULL;
2502 	boolean_t ret = B_FALSE;
2503 	const nvme_health_log_t *hlog;
2504 
2505 	nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
2506 	do_get_feat_temp_thresh_one(npa, disc, feat,
2507 	    "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER);
2508 
2509 	if (!nvme_version_check(npa, &nvme_vers_1v2)) {
2510 		return (B_TRUE);
2511 	}
2512 
2513 	if (!nvme_log_req_init_by_name(npa->npa_ctrl, "health", 0, &log_disc,
2514 	    &req)) {
2515 		nvmeadm_warn(npa, "failed to initialize health log page "
2516 		    "request");
2517 		return (B_FALSE);
2518 	}
2519 
2520 	toalloc = do_get_logpage_size(npa, log_disc, req);
2521 	buf = malloc(toalloc);
2522 	if (buf == NULL) {
2523 		err(-1, "failed to allocate %zu bytes for health log page",
2524 		    toalloc);
2525 	}
2526 
2527 	if (!nvme_log_req_set_output(req, buf, toalloc)) {
2528 		nvmeadm_warn(npa, "failed to set output parameters for health "
2529 		    "log page");
2530 		goto out;
2531 	}
2532 
2533 	if (!nvme_log_req_exec(req)) {
2534 		nvmeadm_warn(npa, "failed to retrieve the health log page");
2535 		goto out;
2536 	}
2537 
2538 	/* cast required to prove our intentionality to smatch */
2539 	hlog = (const nvme_health_log_t *)buf;
2540 
2541 	do_get_feat_temp_thresh_one(npa, disc, feat,
2542 	    "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER);
2543 	if (hlog->hl_temp_sensor_1 != 0) {
2544 		do_get_feat_temp_thresh_one(npa, disc, feat,
2545 		    "Temp. Sensor 1 Over Temp. Threshold", 1,
2546 		    NVME_TEMP_THRESH_OVER);
2547 		do_get_feat_temp_thresh_one(npa, disc, feat,
2548 		    "Temp. Sensor 1 Under Temp. Threshold", 1,
2549 		    NVME_TEMP_THRESH_UNDER);
2550 	}
2551 
2552 	if (hlog->hl_temp_sensor_2 != 0) {
2553 		do_get_feat_temp_thresh_one(npa, disc, feat,
2554 		    "Temp. Sensor 2 Over Temp. Threshold", 2,
2555 		    NVME_TEMP_THRESH_OVER);
2556 		do_get_feat_temp_thresh_one(npa, disc, feat,
2557 		    "Temp. Sensor 2 Under Temp. Threshold", 2,
2558 		    NVME_TEMP_THRESH_UNDER);
2559 	}
2560 
2561 	if (hlog->hl_temp_sensor_3 != 0) {
2562 		do_get_feat_temp_thresh_one(npa, disc, feat,
2563 		    "Temp. Sensor 3 Over Temp. Threshold", 3,
2564 		    NVME_TEMP_THRESH_OVER);
2565 		do_get_feat_temp_thresh_one(npa, disc, feat,
2566 		    "Temp. Sensor 3 Under Temp. Threshold", 3,
2567 		    NVME_TEMP_THRESH_UNDER);
2568 	}
2569 
2570 	if (hlog->hl_temp_sensor_4 != 0) {
2571 		do_get_feat_temp_thresh_one(npa, disc, feat,
2572 		    "Temp. Sensor 4 Over Temp. Threshold", 4,
2573 		    NVME_TEMP_THRESH_OVER);
2574 		do_get_feat_temp_thresh_one(npa, disc, feat,
2575 		    "Temp. Sensor 4 Under Temp. Threshold", 4,
2576 		    NVME_TEMP_THRESH_UNDER);
2577 	}
2578 
2579 	if (hlog->hl_temp_sensor_5 != 0) {
2580 		do_get_feat_temp_thresh_one(npa, disc, feat,
2581 		    "Temp. Sensor 5 Over Temp. Threshold", 5,
2582 		    NVME_TEMP_THRESH_OVER);
2583 		do_get_feat_temp_thresh_one(npa, disc, feat,
2584 		    "Temp. Sensor 5 Under Temp. Threshold", 5,
2585 		    NVME_TEMP_THRESH_UNDER);
2586 	}
2587 
2588 	if (hlog->hl_temp_sensor_6 != 0) {
2589 		do_get_feat_temp_thresh_one(npa, disc, feat,
2590 		    "Temp. Sensor 6 Over Temp. Threshold", 6,
2591 		    NVME_TEMP_THRESH_OVER);
2592 		do_get_feat_temp_thresh_one(npa, disc, feat,
2593 		    "Temp. Sensor 6 Under Temp. Threshold", 6,
2594 		    NVME_TEMP_THRESH_UNDER);
2595 	}
2596 
2597 	if (hlog->hl_temp_sensor_7 != 0) {
2598 		do_get_feat_temp_thresh_one(npa, disc, feat,
2599 		    "Temp. Sensor 7 Over Temp. Threshold", 7,
2600 		    NVME_TEMP_THRESH_OVER);
2601 		do_get_feat_temp_thresh_one(npa, disc, feat,
2602 		    "Temp. Sensor 7 Under Temp. Threshold", 7,
2603 		    NVME_TEMP_THRESH_UNDER);
2604 	}
2605 
2606 	if (hlog->hl_temp_sensor_8 != 0) {
2607 		do_get_feat_temp_thresh_one(npa, disc, feat,
2608 		    "Temp. Sensor 8 Over Temp. Threshold", 8,
2609 		    NVME_TEMP_THRESH_OVER);
2610 		do_get_feat_temp_thresh_one(npa, disc, feat,
2611 		    "Temp. Sensor 8 Under Temp. Threshold", 8,
2612 		    NVME_TEMP_THRESH_UNDER);
2613 	}
2614 
2615 	ret = B_TRUE;
2616 out:
2617 	nvme_log_req_fini(req);
2618 	free(buf);
2619 	return (ret);
2620 }
2621 
2622 static boolean_t
do_get_feat_intr_vect(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc,const nvmeadm_feature_t * feat)2623 do_get_feat_intr_vect(const nvme_process_arg_t *npa,
2624     const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat)
2625 {
2626 	uint32_t nintrs;
2627 	boolean_t ret = B_TRUE;
2628 
2629 	if (!nvme_ctrl_info_pci_nintrs(npa->npa_ctrl_info, &nintrs)) {
2630 		nvmeadm_ctrl_info_warn(npa, "failed to get interrupt count "
2631 		    "from controller %s information snapshot", npa->npa_name);
2632 		return (B_FALSE);
2633 	}
2634 
2635 	nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
2636 	for (uint32_t i = 0; i < nintrs; i++) {
2637 		uint32_t cdw0;
2638 		void *buf;
2639 		size_t buflen;
2640 		nvme_intr_vect_t vect;
2641 
2642 		vect.r = 0;
2643 		vect.b.iv_iv = i;
2644 
2645 		if (!do_get_feat_common(npa, disc, vect.r, &cdw0, &buf,
2646 		    &buflen)) {
2647 			ret = B_FALSE;
2648 			continue;
2649 		}
2650 
2651 		feat->f_print(cdw0, buf, buflen, npa->npa_idctl,
2652 		    npa->npa_version);
2653 		free(buf);
2654 	}
2655 
2656 	return (ret);
2657 }
2658 
2659 /*
2660  * We've been asked to print the following feature that the controller probably
2661  * supports. Find our internal feature information for this to see if we know
2662  * how to deal with it.
2663  */
2664 static void
do_get_features_cb(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)2665 do_get_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc)
2666 {
2667 	const nvmeadm_feature_t *feat = NULL;
2668 	uint32_t fid = nvme_feat_disc_fid(disc);
2669 	nvme_get_feat_fields_t fields;
2670 	void *data = NULL;
2671 	size_t datalen = 0;
2672 	uint32_t cdw0;
2673 
2674 	for (size_t i = 0; i < ARRAY_SIZE(features); i++) {
2675 		if (features[i].f_feature == fid) {
2676 			feat = &features[i];
2677 			break;
2678 		}
2679 	}
2680 
2681 	/*
2682 	 * Determine if we have enough logic in here to get and print the
2683 	 * feature. The vast majority of NVMe features only output a single
2684 	 * uint32_t in cdw0 and potentially a data buffer. As long as no input
2685 	 * arguments are required, then we can go ahead and get this and print
2686 	 * the data. If there is, then we will refuse unless we have a
2687 	 * particular function. If we have a specific get function, we expect it
2688 	 * to do all the printing.
2689 	 */
2690 	if (feat != NULL && feat->f_get != NULL) {
2691 		if (!feat->f_get(npa, disc, feat)) {
2692 			exitcode = -1;
2693 		}
2694 		return;
2695 	}
2696 
2697 	fields = nvme_feat_disc_fields_get(disc);
2698 	if ((fields & NVME_GET_FEAT_F_CDW11) != 0) {
2699 		warnx("unable to get feature %s due to missing nvmeadm(8) "
2700 		    "implementation logic", nvme_feat_disc_spec(disc));
2701 		exitcode = -1;
2702 		return;
2703 	}
2704 
2705 	/*
2706 	 * We do not set exitcode on failure here so that way we can swallow
2707 	 * errors from unimplemented features.
2708 	 */
2709 	if (!do_get_feat_common(npa, disc, 0, &cdw0, &data, &datalen)) {
2710 		return;
2711 	}
2712 
2713 	nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
2714 	if (feat != NULL && feat->f_print != NULL) {
2715 		feat->f_print(cdw0, data, datalen, npa->npa_idctl,
2716 		    npa->npa_version);
2717 	} else {
2718 		nvme_feat_output_t output = nvme_feat_disc_output_get(disc);
2719 		nvme_print_feat_unknown(output, cdw0, data, datalen);
2720 	}
2721 
2722 	free(data);
2723 }
2724 
2725 /*
2726  * This is an entry point which prints every feature that we know about. We
2727  * often go to lengths to discover all the variable inputs that can be used for
2728  * a given feature that requires an argument in cdw11. Due to the semantics of
2729  * filtering being used for features and the need to print each feature, this is
2730  * not the place to add general field filtering or a means to request a specific
2731  * cdw11 argument or similar. Instead, a new get-feature which requires someone
2732  * to specify the short name for a feature and then allows particular fields to
2733  * be grabbed and arguments should be created instead.
2734  *
2735  * This uses the same general feature logic that underpins do_list_features()
2736  * and therefore we transform filter arguments into the same style used there.
2737  */
2738 static int
do_get_features(const nvme_process_arg_t * npa)2739 do_get_features(const nvme_process_arg_t *npa)
2740 {
2741 	char *fstr = NULL;
2742 	char **filts = NULL;
2743 	boolean_t *used = NULL;
2744 	nvmeadm_features_t nf;
2745 	int ret;
2746 
2747 	if (npa->npa_argc > 1)
2748 		errx(-1, "unexpected arguments");
2749 
2750 	if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) <
2751 	    NVME_NS_DISC_F_ACTIVE) {
2752 		errx(-1, "cannot get feature: namespace is inactive");
2753 	}
2754 
2755 	/*
2756 	 * We always leave nf_unimpl set to false as we don't want to bother
2757 	 * trying to print a feature that we know the device doesn't support.
2758 	 */
2759 	(void) memset(&nf, 0, sizeof (nvmeadm_features_t));
2760 
2761 	/*
2762 	 * If we've been given a series of features to print, treat those as
2763 	 * filters on the features as we're walking them to determine which to
2764 	 * print or not.
2765 	 */
2766 	if (npa->npa_argc == 1) {
2767 		char *f;
2768 		uint32_t i;
2769 
2770 		nf.nf_nfilts = 1;
2771 		fstr = strdup(npa->npa_argv[0]);
2772 
2773 		if (fstr == NULL) {
2774 			err(-1, "failed to allocate memory to duplicate "
2775 			    "feature string");
2776 		}
2777 
2778 		for (const char *c = strchr(fstr, ','); c != NULL;
2779 		    c = strchr(c + 1, ',')) {
2780 			nf.nf_nfilts++;
2781 		}
2782 
2783 		filts = calloc(nf.nf_nfilts, sizeof (char *));
2784 		if (filts == NULL) {
2785 			err(-1, "failed to allocate memory for filter list");
2786 		}
2787 
2788 		i = 0;
2789 		while ((f = strsep(&fstr, ",")) != NULL) {
2790 			filts[i] = f;
2791 			i++;
2792 		}
2793 		VERIFY3U(i, ==, nf.nf_nfilts);
2794 		nf.nf_filts = filts;
2795 
2796 		used = calloc(nf.nf_nfilts, sizeof (boolean_t));
2797 		if (used == NULL) {
2798 			err(-1, "failed to allocate memory for filter use "
2799 			    "tracking");
2800 		}
2801 		nf.nf_used = used;
2802 	}
2803 
2804 	(void) printf("%s: Get Features\n", npa->npa_name);
2805 	ret = do_features(npa, &nf, do_get_features_cb);
2806 
2807 	free(fstr);
2808 	free(filts);
2809 	free(used);
2810 	return (ret);
2811 }
2812 
2813 static int
do_format_common(const nvme_process_arg_t * npa,uint32_t lbaf,uint32_t ses)2814 do_format_common(const nvme_process_arg_t *npa, uint32_t lbaf,
2815     uint32_t ses)
2816 {
2817 	int ret = 0;
2818 	nvme_format_req_t *req;
2819 
2820 	if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) <
2821 	    NVME_NS_DISC_F_ACTIVE) {
2822 		errx(-1, "cannot %s: namespace is inactive",
2823 		    npa->npa_cmd->c_name);
2824 	}
2825 
2826 	if (!nvme_format_req_init(npa->npa_ctrl, &req)) {
2827 		nvmeadm_fatal(npa, "failed to initialize format request for "
2828 		    "%s", npa->npa_name);
2829 	}
2830 
2831 	if (npa->npa_ns != NULL) {
2832 		uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
2833 
2834 		if (!nvme_format_req_set_nsid(req, nsid)) {
2835 			nvmeadm_fatal(npa, "failed to set format request "
2836 			    "namespace ID to 0x%x", nsid);
2837 		}
2838 	}
2839 
2840 	if (!nvme_format_req_set_lbaf(req, lbaf) ||
2841 	    !nvme_format_req_set_ses(req, ses)) {
2842 		nvmeadm_fatal(npa, "failed to set format request fields for %s",
2843 		    npa->npa_name);
2844 	}
2845 
2846 	if (do_detach_bd(npa) != 0) {
2847 		errx(-1, "cannot %s %s due to namespace detach failure",
2848 		    npa->npa_cmd->c_name, npa->npa_name);
2849 	}
2850 
2851 	if (!nvme_format_req_exec(req)) {
2852 		nvmeadm_warn(npa, "failed to %s %s", npa->npa_cmd->c_name,
2853 		    npa->npa_name);
2854 		ret = -1;
2855 	}
2856 
2857 	if (do_attach_bd(npa) != 0)
2858 		ret = -1;
2859 
2860 	return (ret);
2861 }
2862 
2863 static void
usage_format(const char * c_name)2864 usage_format(const char *c_name)
2865 {
2866 	(void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
2867 	    "  Format one or all namespaces of the specified NVMe "
2868 	    "controller. Supported LBA\n  formats can be queried with "
2869 	    "the \"%s identify\" command on the namespace\n  to be "
2870 	    "formatted.\n", c_name, getprogname());
2871 }
2872 
2873 static uint32_t
do_format_determine_lbaf(const nvme_process_arg_t * npa)2874 do_format_determine_lbaf(const nvme_process_arg_t *npa)
2875 {
2876 	const nvme_nvm_lba_fmt_t *fmt;
2877 	nvme_ns_info_t *ns_info = NULL;
2878 	uint32_t lbaf;
2879 
2880 	if (npa->npa_argc > 0) {
2881 		unsigned long lba;
2882 		uint32_t nlbaf = nvme_ctrl_info_nformats(npa->npa_ctrl_info);
2883 
2884 		errno = 0;
2885 		lba = strtoul(npa->npa_argv[0], NULL, 10);
2886 		if (errno != 0 || lba >= nlbaf)
2887 			errx(-1, "invalid LBA format %s", npa->npa_argv[0]);
2888 
2889 		if (!nvme_ctrl_info_format(npa->npa_ctrl_info, (uint32_t)lba,
2890 		    &fmt)) {
2891 			nvmeadm_fatal(npa, "failed to get LBA format %lu "
2892 			    "information", lba);
2893 		}
2894 	} else {
2895 		/*
2896 		 * If we have a namespace then we use the current namespace's
2897 		 * LBA format. If we don't have a namespace, then we promised
2898 		 * we'd look at namespace 1 in the manual page.
2899 		 */
2900 		if (npa->npa_ns_info == NULL) {
2901 			if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, 1,
2902 			    &ns_info)) {
2903 				nvmeadm_fatal(npa, "failed to get namespace 1 "
2904 				    "information, please explicitly specify an "
2905 				    "LBA format");
2906 			}
2907 
2908 			if (!nvme_ns_info_curformat(ns_info, &fmt)) {
2909 				nvmeadm_fatal(npa, "failed to retrieve current "
2910 				    "namespace format from namespace 1");
2911 			}
2912 		} else {
2913 			if (!nvme_ns_info_curformat(npa->npa_ns_info, &fmt)) {
2914 				nvmeadm_fatal(npa, "failed to get the current "
2915 				    "format information from %s",
2916 				    npa->npa_name);
2917 			}
2918 		}
2919 	}
2920 
2921 	if (nvme_nvm_lba_fmt_meta_size(fmt) != 0) {
2922 		errx(-1, "LBA formats with metadata are not supported");
2923 	}
2924 
2925 	lbaf = nvme_nvm_lba_fmt_id(fmt);
2926 	nvme_ns_info_free(ns_info);
2927 	return (lbaf);
2928 }
2929 
2930 static int
do_format(const nvme_process_arg_t * npa)2931 do_format(const nvme_process_arg_t *npa)
2932 {
2933 	uint32_t lbaf;
2934 
2935 	if (npa->npa_argc > 1) {
2936 		errx(-1, "%s passed extraneous arguments starting with %s",
2937 		    npa->npa_cmd->c_name, npa->npa_argv[1]);
2938 	}
2939 
2940 	lbaf = do_format_determine_lbaf(npa);
2941 	return (do_format_common(npa, lbaf, 0));
2942 }
2943 
2944 static void
usage_secure_erase(const char * c_name)2945 usage_secure_erase(const char *c_name)
2946 {
2947 	(void) fprintf(stderr, "%s [-c] <ctl>[/<ns>]\n\n"
2948 	    "  Secure-Erase one or all namespaces of the specified "
2949 	    "NVMe controller.\n", c_name);
2950 }
2951 
2952 static void
optparse_secure_erase(nvme_process_arg_t * npa)2953 optparse_secure_erase(nvme_process_arg_t *npa)
2954 {
2955 	int c;
2956 
2957 	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":c")) != -1) {
2958 		switch (c) {
2959 		case 'c':
2960 			npa->npa_cmdflags |= NVMEADM_O_SE_CRYPTO;
2961 			break;
2962 
2963 		case '?':
2964 			errx(-1, "unknown option: -%c", optopt);
2965 
2966 		case ':':
2967 			errx(-1, "option -%c requires an argument", optopt);
2968 
2969 		}
2970 	}
2971 }
2972 
2973 static int
do_secure_erase(const nvme_process_arg_t * npa)2974 do_secure_erase(const nvme_process_arg_t *npa)
2975 {
2976 	unsigned long lbaf;
2977 	uint8_t ses = NVME_FRMT_SES_USER;
2978 
2979 	if (npa->npa_argc > 0) {
2980 		errx(-1, "%s passed extraneous arguments starting with %s",
2981 		    npa->npa_cmd->c_name, npa->npa_argv[0]);
2982 	}
2983 
2984 	if ((npa->npa_cmdflags & NVMEADM_O_SE_CRYPTO) != 0)
2985 		ses = NVME_FRMT_SES_CRYPTO;
2986 
2987 	lbaf = do_format_determine_lbaf(npa);
2988 	return (do_format_common(npa, lbaf, ses));
2989 }
2990 
2991 static void
usage_attach_detach_bd(const char * c_name)2992 usage_attach_detach_bd(const char *c_name)
2993 {
2994 	(void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
2995 	    "  %c%s blkdev(4D) %s one or all namespaces of the "
2996 	    "specified NVMe controller.\n",
2997 	    c_name, toupper(c_name[0]), &c_name[1],
2998 	    c_name[0] == 'd' ? "from" : "to");
2999 }
3000 
3001 /*
3002  * nvmeadm does not generate an error when trying to attach blkdev to something
3003  * that already has it attached. Swallow that here.
3004  */
3005 static boolean_t
swallow_attach_bd_err(const nvme_process_arg_t * npa)3006 swallow_attach_bd_err(const nvme_process_arg_t *npa)
3007 {
3008 	return (nvme_ctrl_err(npa->npa_ctrl) == NVME_ERR_NS_BLKDEV_ATTACH);
3009 }
3010 
3011 static int
do_attach_bd(const nvme_process_arg_t * npa)3012 do_attach_bd(const nvme_process_arg_t *npa)
3013 {
3014 	int rv;
3015 	nvme_ns_iter_t *iter = NULL;
3016 	nvme_iter_t ret;
3017 	const nvme_ns_disc_t *disc;
3018 
3019 	if (npa->npa_ns != NULL) {
3020 		if (!nvme_ns_bd_attach(npa->npa_ns) &&
3021 		    !swallow_attach_bd_err(npa)) {
3022 			nvmeadm_warn(npa, "faild to attach %s", npa->npa_name);
3023 			return (-1);
3024 		}
3025 		return (0);
3026 	}
3027 
3028 	if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_NOT_IGNORED,
3029 	    &iter))  {
3030 		nvmeadm_fatal(npa, "failed to initialize namespace discovery "
3031 		    "on %s", npa->npa_name);
3032 	}
3033 
3034 	rv = 0;
3035 	while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
3036 		nvme_ns_t *ns;
3037 		uint32_t nsid;
3038 
3039 		if (nvme_ns_disc_level(disc) == NVME_NS_DISC_F_BLKDEV)
3040 			continue;
3041 
3042 		nsid = nvme_ns_disc_nsid(disc);
3043 		if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) {
3044 			nvmeadm_warn(npa, "failed to open namespace %s/%u "
3045 			    "handle", npa->npa_name, nsid);
3046 			rv = -1;
3047 			continue;
3048 		}
3049 
3050 		/*
3051 		 * nvmeadm has historically swallowed the case where you ask to
3052 		 * attach an already attached namespace.
3053 		 */
3054 		if (!nvme_ns_bd_attach(ns) && !swallow_attach_bd_err(npa)) {
3055 			nvmeadm_warn(npa, "failed to attach namespace "
3056 			    "%s/%u", npa->npa_name, nsid);
3057 			rv = -1;
3058 		}
3059 		nvme_ns_fini(ns);
3060 	}
3061 
3062 	nvme_ns_discover_fini(iter);
3063 	if (ret == NVME_ITER_ERROR) {
3064 		nvmeadm_warn(npa, "failed to iterate namespaces on %s",
3065 		    npa->npa_name);
3066 		rv = -1;
3067 	}
3068 
3069 	return (rv);
3070 }
3071 
3072 /*
3073  * nvmeadm does not generate an error when trying to attach blkdev to something
3074  * that already has it attached. Swallow that here.
3075  */
3076 static boolean_t
swallow_detach_bd_err(const nvme_process_arg_t * npa)3077 swallow_detach_bd_err(const nvme_process_arg_t *npa)
3078 {
3079 	switch (nvme_ctrl_err(npa->npa_ctrl)) {
3080 	case NVME_ERR_NS_UNALLOC:
3081 	case NVME_ERR_NS_CTRL_NOT_ATTACHED:
3082 	case NVME_ERR_NS_CTRL_ATTACHED:
3083 		return (B_TRUE);
3084 	default:
3085 		return (B_FALSE);
3086 	}
3087 }
3088 
3089 static int
do_detach_bd(const nvme_process_arg_t * npa)3090 do_detach_bd(const nvme_process_arg_t *npa)
3091 {
3092 	int rv;
3093 	nvme_ns_iter_t *iter = NULL;
3094 	nvme_iter_t ret;
3095 	const nvme_ns_disc_t *disc;
3096 
3097 	if (npa->npa_ns != NULL) {
3098 		if (!nvme_ns_bd_detach(npa->npa_ns) &&
3099 		    !swallow_detach_bd_err(npa)) {
3100 			nvmeadm_warn(npa, "failed to detach %s", npa->npa_name);
3101 			return (-1);
3102 		}
3103 		return (0);
3104 	}
3105 
3106 	if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_BLKDEV,
3107 	    &iter))  {
3108 		nvmeadm_fatal(npa, "failed to initialize namespace discovery "
3109 		    "on %s", npa->npa_name);
3110 	}
3111 
3112 	rv = 0;
3113 	while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
3114 		nvme_ns_t *ns;
3115 		uint32_t nsid = nvme_ns_disc_nsid(disc);
3116 
3117 		if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) {
3118 			nvmeadm_warn(npa, "failed to open namespace %s/%u "
3119 			    "handle", npa->npa_name, nsid);
3120 			rv = -1;
3121 			continue;
3122 		}
3123 
3124 		if (!nvme_ns_bd_detach(ns) && !swallow_detach_bd_err(npa)) {
3125 			nvmeadm_warn(npa, "failed to detach namespace %s/%u",
3126 			    npa->npa_name, nsid);
3127 			rv = -1;
3128 		}
3129 		nvme_ns_fini(ns);
3130 	}
3131 
3132 	nvme_ns_discover_fini(iter);
3133 	if (ret == NVME_ITER_ERROR) {
3134 		nvmeadm_warn(npa, "failed to iterate namespaces on %s",
3135 		    npa->npa_name);
3136 		rv = -1;
3137 	}
3138 
3139 	return (rv);
3140 }
3141 
3142 static void
usage_firmware_load(const char * c_name)3143 usage_firmware_load(const char *c_name)
3144 {
3145 	(void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n"
3146 	    "  Load firmware <image file> to offset <offset>.\n"
3147 	    "  The firmware needs to be committed to a slot using "
3148 	    "\"nvmeadm commit-firmware\"\n  command.\n", c_name);
3149 }
3150 
3151 /*
3152  * Read exactly len bytes, or until eof.
3153  */
3154 static size_t
read_block(const nvme_process_arg_t * npa,int fd,char * buf,size_t len)3155 read_block(const nvme_process_arg_t *npa, int fd, char *buf, size_t len)
3156 {
3157 	size_t remain;
3158 
3159 	remain = len;
3160 	while (remain > 0) {
3161 		ssize_t bytes = read(fd, buf, remain);
3162 		if (bytes == 0)
3163 			break;
3164 
3165 		if (bytes < 0) {
3166 			if (errno == EINTR)
3167 				continue;
3168 
3169 			err(-1, "Error reading \"%s\"", npa->npa_argv[0]);
3170 		}
3171 
3172 		buf += (size_t)bytes;
3173 		remain -= (size_t)bytes;
3174 	}
3175 
3176 	return (len - remain);
3177 }
3178 
3179 /*
3180  * Convert a string to a valid firmware upload offset (in bytes).
3181  */
3182 static uint64_t
get_fw_offsetb(char * str)3183 get_fw_offsetb(char *str)
3184 {
3185 	longlong_t offsetb;
3186 	char *valend;
3187 
3188 	errno = 0;
3189 	offsetb = strtoll(str, &valend, 0);
3190 	if (errno != 0 || *valend != '\0' || offsetb < 0 ||
3191 	    offsetb > NVME_FW_OFFSETB_MAX)
3192 		errx(-1, "Offset must be numeric and in the range of 0 to %llu",
3193 		    NVME_FW_OFFSETB_MAX);
3194 
3195 	if ((offsetb & NVME_DWORD_MASK) != 0)
3196 		errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE);
3197 
3198 	return ((uint64_t)offsetb);
3199 }
3200 
3201 #define	FIRMWARE_READ_BLKSIZE	(64 * 1024)		/* 64K */
3202 
3203 static int
do_firmware_load(const nvme_process_arg_t * npa)3204 do_firmware_load(const nvme_process_arg_t *npa)
3205 {
3206 	int fw_fd;
3207 	uint64_t offset = 0;
3208 	size_t size, len;
3209 	char buf[FIRMWARE_READ_BLKSIZE];
3210 
3211 	if (npa->npa_argc > 2)
3212 		errx(-1, "%s passed extraneous arguments starting with %s",
3213 		    npa->npa_cmd->c_name, npa->npa_argv[2]);
3214 
3215 	if (npa->npa_argc == 0)
3216 		errx(-1, "Requires firmware file name, and an "
3217 		    "optional offset");
3218 
3219 	if (npa->npa_ns != NULL)
3220 		errx(-1, "Firmware loading not available on a per-namespace "
3221 		    "basis");
3222 
3223 	if (npa->npa_argc == 2)
3224 		offset = get_fw_offsetb(npa->npa_argv[1]);
3225 
3226 	fw_fd = open(npa->npa_argv[0], O_RDONLY);
3227 	if (fw_fd < 0)
3228 		errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0],
3229 		    strerror(errno));
3230 
3231 	size = 0;
3232 	do {
3233 		len = read_block(npa, fw_fd, buf, sizeof (buf));
3234 
3235 		if (len == 0)
3236 			break;
3237 
3238 		if (!nvme_fw_load(npa->npa_ctrl, buf, len, offset)) {
3239 			nvmeadm_fatal(npa, "failed to load firmware image "
3240 			    "\"%s\" at offset %" PRIu64, npa->npa_argv[0],
3241 			    offset);
3242 		}
3243 
3244 		offset += len;
3245 		size += len;
3246 	} while (len == sizeof (buf));
3247 
3248 	(void) close(fw_fd);
3249 
3250 	if (verbose)
3251 		(void) printf("%zu bytes downloaded.\n", size);
3252 
3253 	return (0);
3254 }
3255 
3256 /*
3257  * Common firmware commit for nvmeadm commit-firmware and activate-firmware.
3258  */
3259 static void
nvmeadm_firmware_commit(const nvme_process_arg_t * npa,uint32_t slot,uint32_t act)3260 nvmeadm_firmware_commit(const nvme_process_arg_t *npa, uint32_t slot,
3261     uint32_t act)
3262 {
3263 	nvme_fw_commit_req_t *req;
3264 
3265 	if (!nvme_fw_commit_req_init(npa->npa_ctrl, &req)) {
3266 		nvmeadm_fatal(npa, "failed to initialize firmware commit "
3267 		    "request for %s", npa->npa_name);
3268 	}
3269 
3270 	if (!nvme_fw_commit_req_set_slot(req, slot) ||
3271 	    !nvme_fw_commit_req_set_action(req, act)) {
3272 		nvmeadm_fatal(npa, "failed to set firmware commit fields for "
3273 		    "%s", npa->npa_name);
3274 	}
3275 
3276 	if (!nvme_fw_commit_req_exec(req)) {
3277 		/*
3278 		 * A number of command specific status values are informational
3279 		 * and indicate that the operation was successful but that
3280 		 * something else, such as a device reset, is still required
3281 		 * before the new firmware is active.
3282 		 * We distinguish those here and report them as a note rather
3283 		 * than a fatal error.
3284 		 */
3285 		if (nvme_ctrl_err(npa->npa_ctrl) == NVME_ERR_CONTROLLER) {
3286 			uint32_t sct, sc;
3287 
3288 			nvme_ctrl_deverr(npa->npa_ctrl, &sct, &sc);
3289 			if (sct == NVME_CQE_SCT_SPECIFIC && (
3290 			    sc == NVME_CQE_SC_SPC_FW_RESET ||
3291 			    sc == NVME_CQE_SC_SPC_FW_NSSR ||
3292 			    sc == NVME_CQE_SC_SPC_FW_NEXT_RESET)) {
3293 				fprintf(stderr,
3294 				    "nvmeadm: commit successful but %s\n",
3295 				    nvme_sctostr(npa->npa_ctrl, NVME_CSI_NVM,
3296 				    sct, sc));
3297 			} else {
3298 				nvmeadm_fatal(npa, "failed to %s on %s",
3299 				    npa->npa_cmd->c_name, npa->npa_name);
3300 			}
3301 		} else {
3302 			nvmeadm_fatal(npa, "failed to %s on %s",
3303 			    npa->npa_cmd->c_name, npa->npa_name);
3304 		}
3305 	}
3306 
3307 	nvme_fw_commit_req_fini(req);
3308 }
3309 
3310 /*
3311  * Convert str to a valid firmware slot number.
3312  */
3313 static uint32_t
get_slot_number(char * str)3314 get_slot_number(char *str)
3315 {
3316 	longlong_t slot;
3317 	char *valend;
3318 
3319 	errno = 0;
3320 	slot = strtoll(str, &valend, 0);
3321 	if (errno != 0 || *valend != '\0' ||
3322 	    slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX)
3323 		errx(-1, "Slot must be numeric and in the range of %u to %u",
3324 		    NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX);
3325 
3326 	return ((uint32_t)slot);
3327 }
3328 
3329 static void
usage_firmware_commit(const char * c_name)3330 usage_firmware_commit(const char *c_name)
3331 {
3332 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
3333 	    "  Commit previously downloaded firmware to slot <slot>.\n"
3334 	    "  The firmware is only activated after a "
3335 	    "\"nvmeadm activate-firmware\" command.\n", c_name);
3336 }
3337 
3338 static int
do_firmware_commit(const nvme_process_arg_t * npa)3339 do_firmware_commit(const nvme_process_arg_t *npa)
3340 {
3341 	uint32_t slot;
3342 
3343 	if (npa->npa_argc > 1)
3344 		errx(-1, "%s passed extraneous arguments starting with %s",
3345 		    npa->npa_cmd->c_name, npa->npa_argv[1]);
3346 
3347 	if (npa->npa_argc == 0)
3348 		errx(-1, "Firmware slot number is required");
3349 
3350 	if (npa->npa_ns != NULL)
3351 		errx(-1, "Firmware committing not available on a per-namespace "
3352 		    "basis");
3353 
3354 	slot = get_slot_number(npa->npa_argv[0]);
3355 
3356 	if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly)
3357 		errx(-1, "Cannot commit firmware to slot 1: slot is read-only");
3358 
3359 	nvmeadm_firmware_commit(npa, slot, NVME_FWC_SAVE);
3360 
3361 	if (verbose)
3362 		(void) printf("Firmware committed to slot %u.\n", slot);
3363 
3364 	return (0);
3365 }
3366 
3367 static void
usage_firmware_activate(const char * c_name)3368 usage_firmware_activate(const char *c_name)
3369 {
3370 	(void) fprintf(stderr, "%s <ctl> <slot>\n\n"
3371 	    "  Activate firmware in slot <slot>.\n"
3372 	    "  The firmware will be in use after the next system reset.\n",
3373 	    c_name);
3374 }
3375 
3376 static int
do_firmware_activate(const nvme_process_arg_t * npa)3377 do_firmware_activate(const nvme_process_arg_t *npa)
3378 {
3379 	uint32_t slot;
3380 
3381 	if (npa->npa_argc > 1)
3382 		errx(-1, "%s passed extraneous arguments starting with %s",
3383 		    npa->npa_cmd->c_name, npa->npa_argv[1]);
3384 
3385 	if (npa->npa_argc == 0)
3386 		errx(-1, "Firmware slot number is required");
3387 
3388 	if (npa->npa_ns != NULL)
3389 		errx(-1, "Firmware activation not available on a per-namespace "
3390 		    "basis");
3391 
3392 	slot = get_slot_number(npa->npa_argv[0]);
3393 
3394 	nvmeadm_firmware_commit(npa, slot, NVME_FWC_ACTIVATE);
3395 
3396 	if (verbose)
3397 		(void) printf("Slot %u successfully activated.\n", slot);
3398 
3399 	return (0);
3400 }
3401 
3402 nvme_vuc_disc_t *
nvmeadm_vuc_init(const nvme_process_arg_t * npa,const char * name)3403 nvmeadm_vuc_init(const nvme_process_arg_t *npa, const char *name)
3404 {
3405 	nvme_vuc_disc_t *vuc;
3406 	nvme_vuc_disc_lock_t lock;
3407 
3408 	if (!nvme_vuc_discover_by_name(npa->npa_ctrl, name, 0, &vuc)) {
3409 		nvmeadm_fatal(npa, "%s does not support operation %s: device "
3410 		    "does not support vendor unique command %s", npa->npa_name,
3411 		    npa->npa_cmd->c_name, name);
3412 	}
3413 
3414 	lock = nvme_vuc_disc_lock(vuc);
3415 	switch (lock) {
3416 	case NVME_VUC_DISC_LOCK_NONE:
3417 		break;
3418 	case NVME_VUC_DISC_LOCK_READ:
3419 		nvmeadm_excl(npa, NVME_LOCK_L_READ);
3420 		break;
3421 	case NVME_VUC_DISC_LOCK_WRITE:
3422 		nvmeadm_excl(npa, NVME_LOCK_L_WRITE);
3423 		break;
3424 	}
3425 
3426 	return (vuc);
3427 }
3428 
3429 void
nvmeadm_vuc_fini(const nvme_process_arg_t * npa,nvme_vuc_disc_t * vuc)3430 nvmeadm_vuc_fini(const nvme_process_arg_t *npa, nvme_vuc_disc_t *vuc)
3431 {
3432 	if (nvme_vuc_disc_lock(vuc) != NVME_VUC_DISC_LOCK_NONE) {
3433 		if (npa->npa_ns != NULL) {
3434 			nvme_ns_unlock(npa->npa_ns);
3435 		} else if (npa->npa_ctrl != NULL) {
3436 			nvme_ctrl_unlock(npa->npa_ctrl);
3437 		}
3438 	}
3439 
3440 	nvme_vuc_disc_free(vuc);
3441 }
3442