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