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