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