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