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 2026 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 #include <sys/stat.h>
49 #include <sys/mman.h>
50 #include <sys/fm/protocol.h>
51 #include <fm/topo_hc.h>
52 #include <getopt.h>
53
54 #include <sys/nvme.h>
55
56 #include "nvmeadm.h"
57
58 /*
59 * Assertions to make sure that we've properly captured various aspects of the
60 * packed structures and haven't broken them during updates.
61 */
62 CTASSERT(sizeof (nvme_identify_ctrl_t) == NVME_IDENTIFY_BUFSIZE);
63 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oacs) == 256);
64 CTASSERT(offsetof(nvme_identify_ctrl_t, id_sqes) == 512);
65 CTASSERT(offsetof(nvme_identify_ctrl_t, id_oncs) == 520);
66 CTASSERT(offsetof(nvme_identify_ctrl_t, id_subnqn) == 768);
67 CTASSERT(offsetof(nvme_identify_ctrl_t, id_nvmof) == 1792);
68 CTASSERT(offsetof(nvme_identify_ctrl_t, id_psd) == 2048);
69 CTASSERT(offsetof(nvme_identify_ctrl_t, id_vs) == 3072);
70
71 CTASSERT(sizeof (nvme_identify_nsid_t) == NVME_IDENTIFY_BUFSIZE);
72 CTASSERT(offsetof(nvme_identify_nsid_t, id_fpi) == 32);
73 CTASSERT(offsetof(nvme_identify_nsid_t, id_anagrpid) == 92);
74 CTASSERT(offsetof(nvme_identify_nsid_t, id_nguid) == 104);
75 CTASSERT(offsetof(nvme_identify_nsid_t, id_lbaf) == 128);
76 CTASSERT(offsetof(nvme_identify_nsid_t, id_vs) == 384);
77
78 CTASSERT(sizeof (nvme_identify_nsid_list_t) == NVME_IDENTIFY_BUFSIZE);
79 CTASSERT(sizeof (nvme_identify_ctrl_list_t) == NVME_IDENTIFY_BUFSIZE);
80
81 CTASSERT(sizeof (nvme_identify_primary_caps_t) == NVME_IDENTIFY_BUFSIZE);
82 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vqfrt) == 32);
83 CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vifrt) == 64);
84
85 CTASSERT(sizeof (nvme_nschange_list_t) == 4096);
86
87 static void usage(const nvmeadm_cmd_t *);
88 static bool nvmeadm_ctrl_disc_cb(nvme_t *, const nvme_ctrl_disc_t *, void *);
89
90 static int do_list(const nvme_process_arg_t *);
91 static int do_identify(const nvme_process_arg_t *);
92 static int do_identify_ctrl(const nvme_process_arg_t *);
93 static int do_identify_ns(const nvme_process_arg_t *);
94 static int do_list_logs(const nvme_process_arg_t *);
95 static int do_get_logpage_fwslot(const nvme_process_arg_t *);
96 static int do_get_logpage(const nvme_process_arg_t *);
97 static int do_print_logpage(const nvme_process_arg_t *);
98 static int do_list_features(const nvme_process_arg_t *);
99 static boolean_t do_get_feat_intr_vect(const nvme_process_arg_t *,
100 const nvme_feat_disc_t *, const nvmeadm_feature_t *);
101 static boolean_t do_get_feat_temp_thresh(const nvme_process_arg_t *,
102 const nvme_feat_disc_t *, const nvmeadm_feature_t *);
103 static int do_get_features(const nvme_process_arg_t *);
104 static int do_set_feature(const nvme_process_arg_t *);
105 static int do_format(const nvme_process_arg_t *);
106 static int do_secure_erase(const nvme_process_arg_t *);
107 static int do_attach_bd(const nvme_process_arg_t *);
108 static int do_detach_bd(const nvme_process_arg_t *);
109 static int do_firmware_load(const nvme_process_arg_t *);
110 static int do_firmware_commit(const nvme_process_arg_t *);
111 static int do_firmware_activate(const nvme_process_arg_t *);
112
113 static void optparse_list(nvme_process_arg_t *);
114 static void optparse_identify(nvme_process_arg_t *);
115 static void optparse_identify_ctrl(nvme_process_arg_t *);
116 static void optparse_identify_ns(nvme_process_arg_t *);
117 static void optparse_list_logs(nvme_process_arg_t *);
118 static void optparse_get_logpage(nvme_process_arg_t *);
119 static void optparse_print_logpage(nvme_process_arg_t *);
120 static void optparse_list_features(nvme_process_arg_t *);
121 static void optparse_set_feature(nvme_process_arg_t *);
122 static void optparse_secure_erase(nvme_process_arg_t *);
123
124 static void usage_list(const char *);
125 static void usage_identify(const char *);
126 static void usage_identify_ctrl(const char *);
127 static void usage_identify_ns(const char *);
128 static void usage_list_logs(const char *);
129 static void usage_get_logpage(const char *);
130 static void usage_print_logpage(const char *);
131 static void usage_list_features(const char *);
132 static void usage_get_features(const char *);
133 static void usage_set_feature(const char *);
134 static void usage_format(const char *);
135 static void usage_secure_erase(const char *);
136 static void usage_attach_detach_bd(const char *);
137 static void usage_firmware_list(const char *);
138 static void usage_firmware_load(const char *);
139 static void usage_firmware_commit(const char *);
140 static void usage_firmware_activate(const char *);
141
142 int verbose;
143 int debug;
144
145 /*
146 * nvmeadm Secure-erase specific options
147 */
148 #define NVMEADM_O_SE_CRYPTO 0x00000004
149
150 /*
151 * nvmeadm identify specific options
152 */
153 #define NVMEADM_O_ID_NSID_LIST 0x00000008
154 #define NVMEADM_O_ID_COMMON_NS 0x00000010
155 #define NVMEADM_O_ID_CTRL_LIST 0x00000020
156 #define NVMEADM_O_ID_DESC_LIST 0x00000040
157 #define NVMEADM_O_ID_ALLOC_NS 0x00000080
158
159 static int exitcode;
160
161 /*
162 * Nvmeadm subcommand definitons.
163 *
164 * When adding a new subcommand, please check that the commands still
165 * line up in the usage() message, and adjust the format string in
166 * usage() below if necessary.
167 */
168 static const nvmeadm_cmd_t nvmeadm_cmds[] = {
169 {
170 "list",
171 "list controllers and namespaces",
172 " -c\t\tlist only controllers\n"
173 " -L\t\tlist controller location\n"
174 " -p\t\tprint parsable output\n"
175 " -o field\tselect a field for parsable output\n",
176 " model\t\tthe controller's model name\n"
177 " serial\tthe controller's serial number\n"
178 " fwrev\t\tthe controller's current firmware revision\n"
179 " version\tthe controller's NVMe specification version\n"
180 " capacity\tthe controller or namespace's capacity in bytes\n"
181 " instance\tthe device driver instance (e.g. nvme3)\n"
182 " ctrlpath\tthe controller's /devices path\n"
183 " unallocated\tthe amount of unallocated NVM in bytes\n"
184 " size\t\tthe namespace's logical size in bytes\n"
185 " used\t\tthe namespace's bytes used\n"
186 " disk\t\tthe name of the namespace's disk device\n"
187 " namespace\tthe namespace's numerical value\n"
188 " ns-state\tthe namespace's current state\n"
189 " format\ta description of the namespace's format\n"
190 " fmtid\t\tthe numerical id of the namespace's format\n"
191 " fmtds\t\tthe data size of the namespace's format\n"
192 " fmtms\t\tthe metadata size of the namespace's format\n"
193 " location\tcontroller location (-L)\n"
194 " ctlap\t\tcontrolling attachment point (-L)\n",
195 do_list, usage_list, optparse_list,
196 NVMEADM_C_MULTI
197 },
198 {
199 "identify",
200 "identify controllers and/or namespaces",
201 " -C\t\tget Common Namespace Identification\n"
202 " -a\t\tget only allocated namespace information\n"
203 " -c\t\tget controller identifier list\n"
204 " -d\t\tget namespace identification descriptors list\n"
205 " -n\t\tget namespaces identifier list",
206 NULL,
207 do_identify, usage_identify, optparse_identify,
208 NVMEADM_C_MULTI
209 },
210 {
211 "identify-controller",
212 "identify controllers",
213 " -C\t\tget Common Namespace Identification\n"
214 " -a\t\tget only allocated namespace information\n"
215 " -c\t\tget controller identifier list\n"
216 " -n\t\tget namespaces identifier list",
217 NULL,
218 do_identify_ctrl, usage_identify_ctrl, optparse_identify_ctrl,
219 NVMEADM_C_MULTI
220 },
221 {
222 "identify-namespace",
223 "identify namespaces",
224 " -c\t\tget attached controller identifier list\n"
225 " -d\t\tget namespace identification descriptors list",
226 NULL,
227 do_identify_ns, usage_identify_ns, optparse_identify_ns,
228 NVMEADM_C_MULTI
229 },
230 {
231 "list-logpages",
232 "list a device's supported log pages",
233 " -a\t\tprint all log pages, including unimplemented ones\n"
234 " -H\t\tomit column headers\n"
235 " -o field\tselect a field for parsable output\n"
236 " -p\t\tprint parsable output\n"
237 " -s scope\tprint logs that match the specified scopes "
238 "(default is based on\n\t\tdevice)\n",
239 " device\tthe name of the controller or namespace\n"
240 " name\t\tthe name of the log page\n"
241 " desc\t\ta description of the loage page\n"
242 " scope\t\tthe valid device scopes for the log page\n"
243 " fields\tthe list of fields in the get log request that may "
244 "be set or required\n\t\t(e.g. lsi, lsp, rae, etc.)\n"
245 " csi\t\tthe command set interface the log page belongs to\n"
246 " lid\t\tthe log page's numeric ID\n"
247 " impl\t\tindicates whether the device implements the log "
248 "page\n"
249 " size\t\tthe size of the log page for fixed size logs\n"
250 " minsize\tthe minimum size required to determine the full "
251 "log page size\n\t\tfor variable-length pages\n"
252 " sources\twhere information for this log page came from\n"
253 " kind\t\tindicates the kind of log page e.g. standard, "
254 "vendor-specific,\n\t\tetc.",
255 do_list_logs, usage_list_logs, optparse_list_logs,
256 NVMEADM_C_MULTI
257 },
258 {
259 .c_name = "get-logpage",
260 .c_desc = "get a log page from controllers and/or namespaces",
261 .c_flagdesc = " -H\t\tomit column headers\n"
262 " -o field\tselect a field for parsable output\n"
263 " -O file\toutput log raw binary data to a file\n"
264 " -p\t\tprint parsable output\n"
265 " -x\t\tforce hex dump output\n",
266 .c_fielddesc = " short\t\tfield's short name\n"
267 " desc\t\tdescription of the field\n"
268 " value\t\tthe raw field value\n"
269 " human\t\tthe human printable form of the value\n"
270 " length\tthe length of the field in bytes\n"
271 " bitlen\tthe number of additional bits long the field "
272 "is\n"
273 " offset\tthe offset in bytes to the start of the field\n"
274 " bitoff\tthe additional number of bits to the start of "
275 "the field\n",
276 .c_func = do_get_logpage,
277 .c_usage = usage_get_logpage,
278 .c_optparse = optparse_get_logpage,
279 .c_flags = NVMEADM_C_MULTI
280 },
281 {
282 .c_name = "print-logpage",
283 .c_desc = "print and filter a log page from disk",
284 .c_flagdesc = " -f file\tfile that contains log data\n"
285 " -H\t\tomit column headers\n"
286 " -o field\tselect a field for parsable output\n"
287 " -p\t\tprint parsable output\n"
288 " -x\t\tforce hex dump output\n",
289 .c_fielddesc = " short\t\tfield's short name\n"
290 " desc\t\tdescription of the field\n"
291 " value\t\tthe raw field value\n"
292 " human\t\tthe human printable form of the value\n"
293 " offset\tthe length of the field in bytes\n"
294 " bitlen\tthe number of additional bits long the field "
295 "is\n"
296 " offset\tthe offset in bytes to the start of the field\n"
297 " bitoff\tthe additional number of bits to the start of "
298 "the field\n",
299 .c_func = do_print_logpage,
300 .c_usage = usage_print_logpage,
301 .c_optparse = optparse_print_logpage,
302 .c_flags = NVMEADM_C_NOCTRL
303 },
304 {
305 "list-features",
306 "list a device's supported features",
307 " -a\t\tprint all features, including unsupported\n"
308 " -H\t\tomit column headers\n"
309 " -o field\tselect a field for parsable output\n"
310 " -p\t\tprint parsable output",
311 " device\tthe name of the controller or namespace\n"
312 " short\t\tthe short name of the feature\n"
313 " spec\t\tthe longer feature description from the NVMe spec\n"
314 " fid\t\tthe numeric feature ID\n"
315 " scope\t\tthe valid device scopes for the feature\n"
316 " kind\t\tindicates the kind of feature e.g. standard, "
317 "vendor-specific,\n\t\tetc.\n"
318 " csi\t\tindicates the features command set interface\n"
319 " flags\t\tindicates additional properties of the feature\n"
320 " get-in\tindicates the fields that are required to get the "
321 "feature\n"
322 " set-in\tindicates the fields that are required to set the "
323 "feature\n"
324 " get-out\tindicates the fields the feature outputs\n"
325 " set-out\tindicates the fields the feature outputs when "
326 "setting the feature\n"
327 " datalen\tindicates the length of the feature's data "
328 "payload\n"
329 " impl\t\tindicates whether the device implements the "
330 "feature",
331 do_list_features, usage_list_features, optparse_list_features,
332 NVMEADM_C_MULTI
333 },
334 {
335 "get-features",
336 "get features from controllers and/or namespaces",
337 NULL,
338 NULL,
339 do_get_features, usage_get_features, NULL,
340 NVMEADM_C_MULTI
341 },
342 {
343 .c_name = "set-feature",
344 .c_desc = "set a feature on a controller and/or namespace",
345 .c_flagdesc = " -s\t\tsave the setting persistently\n"
346 " -v value\tset the primary (cdw11) feature value\n"
347 " --cdw12 cdw12\tset the cdw12 32-bit value\n"
348 " --cdw13 cdw13\tset the cdw13 32-bit value\n"
349 " --cdw15 cdw15\tset the cdw15 32-bit value\n"
350 " -i input\tthe command input data file\n"
351 " -l length\tthe command input data size in bytes\n"
352 " -I impact\trequest impact on the system\n",
353 .c_func = do_set_feature,
354 .c_usage = usage_set_feature,
355 .c_optparse = optparse_set_feature,
356 .c_flags = NVMEADM_C_EXCL
357 },
358 {
359 "format",
360 "format namespace(s) of a controller",
361 NULL,
362 NULL,
363 do_format, usage_format, NULL,
364 NVMEADM_C_EXCL
365 },
366 {
367 "secure-erase",
368 "secure erase namespace(s) of a controller",
369 " -c Do a cryptographic erase.",
370 NULL,
371 do_secure_erase, usage_secure_erase, optparse_secure_erase,
372 NVMEADM_C_EXCL
373 },
374 {
375 "create-namespace",
376 "create a new namespace",
377 " -b block-size\tNamespace format chosen to match the "
378 "requested block-size\n"
379 " -c cap\tSpecifies the namespace capacity in bytes, defaults "
380 "to the\n\t\tnamespace's size. When the size is greater than "
381 "the\n\t\tcapacity, the namespace is thin provisioned.\n"
382 " -f flbas\tformatted LBA block size index\n"
383 " -n nmic\tmulti-path I/O and namespace sharing capabilities, "
384 "valid values:\n"
385 "\t\tnone\tno namespace sharing\n"
386 "\t\tshared\tthe namespace may be attached by two or more "
387 "controllers\n"
388 " -t csi\tspecifies the namespace's command set interface, "
389 "defaults to\n\t\tnvm\n",
390 NULL,
391 do_create_ns, usage_create_ns, optparse_create_ns,
392 NVMEADM_C_EXCL
393 },
394 {
395 "delete-namespace",
396 "delete a namespace",
397 NULL, NULL,
398 do_delete_ns, usage_delete_ns, NULL,
399 NVMEADM_C_EXCL
400 },
401 {
402 "attach-namespace",
403 "attach a namespace to a controller",
404 NULL, NULL,
405 do_attach_ns, usage_attach_ns, NULL,
406 NVMEADM_C_EXCL
407 },
408 {
409 "detach-namespace",
410 "detach a namespace from a controller",
411 NULL, NULL,
412 do_detach_ns, usage_detach_ns, NULL,
413 NVMEADM_C_EXCL
414 },
415
416 {
417 "detach",
418 "detach blkdev(4D) from namespace(s) of a controller",
419 NULL,
420 NULL,
421 do_detach_bd, usage_attach_detach_bd, NULL,
422 NVMEADM_C_EXCL
423 },
424 {
425 "attach",
426 "attach blkdev(4D) to namespace(s) of a controller",
427 NULL,
428 NULL,
429 do_attach_bd, usage_attach_detach_bd, NULL,
430 NVMEADM_C_EXCL
431 },
432 {
433 "list-firmware",
434 "list firmware on a controller",
435 NULL,
436 NULL,
437 do_get_logpage_fwslot, usage_firmware_list, NULL,
438 0
439 },
440 {
441 "load-firmware",
442 "load firmware to a controller",
443 NULL,
444 NULL,
445 do_firmware_load, usage_firmware_load, NULL,
446 NVMEADM_C_EXCL
447 },
448 {
449 "commit-firmware",
450 "commit downloaded firmware to a slot of a controller",
451 NULL,
452 NULL,
453 do_firmware_commit, usage_firmware_commit, NULL,
454 NVMEADM_C_EXCL
455 },
456 {
457 "activate-firmware",
458 "activate a firmware slot of a controller",
459 NULL,
460 NULL,
461 do_firmware_activate, usage_firmware_activate, NULL,
462 NVMEADM_C_EXCL
463 },
464 {
465 .c_name = "measure-phyeye",
466 .c_desc = "measure the physical interface eye",
467 .c_flagdesc = " -o output\tthe data output file\n"
468 " -Q quality\tthe quality of the eye measurement (good, "
469 "better, or best)\n",
470 .c_func = do_measure_phyeye_cmd,
471 .c_usage = usage_measure_phyeye_cmd,
472 .c_optparse = optparse_measure_phyeye_cmd,
473 .c_flags = NVMEADM_C_EXCL
474 },
475 {
476 .c_name = "report-phyeye",
477 .c_desc = "report information about the measured eye",
478 .c_flagdesc = " -e eye\toptional eye index to report\n"
479 " -l lane\tprint information only about the specific "
480 "lane\n"
481 " -m mode\tdata report mode, either 'print-eye' or "
482 "'eye-data'\n",
483 .c_func = do_report_phyeye_cmd,
484 .c_usage = usage_report_phyeye_cmd,
485 .c_optparse = optparse_report_phyeye_cmd,
486 .c_flags = NVMEADM_C_NOCTRL
487 },
488 {
489 .c_name = "vendor-cmd",
490 .c_desc = "run an arbitrary vendor-specific command",
491 .c_flagdesc = " -O opcode\tcommand opcode\n"
492 " -n nsid\tcommand namespace identifier\n"
493 " --cdw12 cdw12\tcdw12 32-bit value\n"
494 " --cdw13 cdw13\tcdw13 32-bit value\n"
495 " --cdw14 cdw14\tcdw14 32-bit value\n"
496 " --cdw15 cdw15\tcdw15 32-bit value\n"
497 " -l length\tthe command data size in bytes\n"
498 " -i input\tthe command input data file\n"
499 " -o output\tthe command output data file\n"
500 " -L lock\trequest a controller or namespace lock\n"
501 " -I impact\trequest impact on the system\n"
502 " -t timeout\trequest timeout in seconds\n",
503 .c_func = do_vendor_cmd,
504 .c_usage = usage_vendor_cmd,
505 .c_optparse = optparse_vendor_cmd
506 },
507 {
508 .c_name = "ocp/error-inject",
509 .c_desc = "inject errors onto an NVMe controller",
510 .c_flagdesc = " -l lat_us\tset latency duration to lat_us "
511 "microseconds\n"
512 " -n nreads\tdelay error injection until nreads read "
513 "commands complete\n",
514 .c_func = do_ocp_errinj,
515 .c_usage = usage_ocp_errinj,
516 .c_optparse = optparse_ocp_errinj,
517 .c_flags = NVMEADM_C_EXCL
518 },
519 {
520 .c_name = "sandisk/hwrev",
521 .c_desc = "obtain device hardware revision",
522 .c_func = do_sandisk_hwrev,
523 .c_usage = usage_sandisk_hwrev,
524 .c_flags = 0
525 },
526 {
527 .c_name = "sandisk/pci-eye",
528 .c_desc = "get PCIe receiver eye diagram",
529 .c_flagdesc = " -l lane\t\tspecify the PCIe lane (0-3)\n"
530 " -o output\tspecify output file destination\n",
531 .c_func = do_sandisk_pcieye,
532 .c_usage = usage_sandisk_pcieye,
533 .c_optparse = optparse_sandisk_pcieye
534 },
535 {
536 "wdc/e6dump",
537 "dump WDC e6 diagnostic log",
538 " -o output\tspecify output file destination\n",
539 NULL,
540 do_wdc_e6dump, usage_wdc_e6dump, optparse_wdc_e6dump,
541 0
542 },
543 {
544 "wdc/resize",
545 "change a WDC device's capacity",
546 " -g\t\tquery the device's current resized capacity\n"
547 " -s size\tset the size of a device to the specified in gb",
548 NULL,
549 do_wdc_resize, usage_wdc_resize, optparse_wdc_resize,
550 /*
551 * We do not set NVMEADM_C_EXCL here as that is handled by the
552 * vendor unique command logic and operates based on the
553 * information we get from vuc discovery.
554 */
555 0
556 },
557 {
558 "wdc/clear-assert",
559 "clear internal device assertion",
560 NULL,
561 NULL,
562 do_wdc_clear_assert, usage_wdc_clear_assert, NULL
563 },
564 {
565 "wdc/inject-assert",
566 "inject internal device assertion",
567 NULL,
568 NULL,
569 do_wdc_inject_assert, usage_wdc_inject_assert, NULL
570 },
571 {
572 NULL, NULL, NULL,
573 NULL, NULL, NULL, 0
574 }
575 };
576
577 static const nvmeadm_feature_t features[] = {
578 {
579 .f_feature = "arb",
580 .f_print = nvme_print_feat_arbitration
581 }, {
582 .f_feature = "pm",
583 .f_print = nvme_print_feat_power_mgmt
584 }, {
585 .f_feature = "range",
586 .f_print = nvme_print_feat_lba_range
587 }, {
588 .f_feature = "temp",
589 .f_get = do_get_feat_temp_thresh,
590 .f_print = nvme_print_feat_temperature
591 }, {
592 .f_feature = "errrec",
593 .f_print = nvme_print_feat_error
594 }, {
595 .f_feature = "cache",
596 .f_print = nvme_print_feat_write_cache
597 }, {
598 .f_feature = "queues",
599 .f_print = nvme_print_feat_nqueues
600 }, {
601 .f_feature = "coalescing",
602 .f_print = nvme_print_feat_intr_coal
603 }, {
604 .f_feature = "vector",
605 .f_get = do_get_feat_intr_vect,
606 .f_print = nvme_print_feat_intr_vect
607 }, {
608 .f_feature = "atomicity",
609 .f_print = nvme_print_feat_write_atom
610 }, {
611 .f_feature = "event",
612 .f_print = nvme_print_feat_async_event
613 }, {
614 .f_feature = "apst",
615 .f_print = nvme_print_feat_auto_pst
616 }, {
617 .f_feature = "progress",
618 .f_print = nvme_print_feat_progress
619 }, {
620 .f_feature = "hostsup",
621 .f_print = nvme_print_feat_host_behavior
622 }, {
623 .f_feature = "ocp/errinj",
624 .f_print = nvme_print_feat_ocp_err_inj
625 }, {
626 .f_feature = "ocp/plpfail",
627 .f_print = nvme_print_feat_ocp_plp_fail
628 }, {
629 .f_feature = "ocp/plphealth",
630 .f_print = nvme_print_feat_ocp_plp_health
631 }
632 };
633
634 static void
nvmeadm_ctrl_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)635 nvmeadm_ctrl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap)
636 {
637 nvme_ctrl_t *ctrl = npa->npa_ctrl;
638
639 (void) fprintf(stderr, "nvmeadm: ");
640 (void) vfprintf(stderr, fmt, ap);
641 (void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n",
642 nvme_ctrl_errmsg(ctrl), nvme_ctrl_errtostr(npa->npa_ctrl,
643 nvme_ctrl_err(ctrl)), nvme_ctrl_err(ctrl), nvme_ctrl_syserr(ctrl));
644 }
645
646 static void
nvmeadm_hdl_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)647 nvmeadm_hdl_vwarn(const nvme_process_arg_t *npa, const char *fmt, va_list ap)
648 {
649 nvme_t *nvme = npa->npa_nvme;
650
651 (void) fprintf(stderr, "nvmeadm: ");
652 (void) vfprintf(stderr, fmt, ap);
653 (void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n",
654 nvme_errmsg(nvme), nvme_errtostr(nvme, nvme_err(nvme)),
655 nvme_err(nvme), nvme_syserr(nvme));
656 }
657
658 static void
nvmeadm_ctrl_info_vwarn(const nvme_process_arg_t * npa,const char * fmt,va_list ap)659 nvmeadm_ctrl_info_vwarn(const nvme_process_arg_t *npa, const char *fmt,
660 va_list ap)
661 {
662 nvme_ctrl_info_t *info = npa->npa_ctrl_info;
663
664 (void) fprintf(stderr, "nvmeadm: ");
665 (void) vfprintf(stderr, fmt, ap);
666 (void) fprintf(stderr, ": %s: %s (libnvme info: 0x%x, sys: %d)\n",
667 nvme_ctrl_info_errmsg(info), nvme_ctrl_info_errtostr(info,
668 nvme_ctrl_info_err(info)), nvme_ctrl_info_err(info),
669 nvme_ctrl_info_syserr(info));
670 }
671
672 void
nvmeadm_warn(const nvme_process_arg_t * npa,const char * fmt,...)673 nvmeadm_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
674 {
675 va_list ap;
676
677 va_start(ap, fmt);
678 nvmeadm_ctrl_vwarn(npa, fmt, ap);
679 va_end(ap);
680 }
681
682 void __NORETURN
nvmeadm_fatal(const nvme_process_arg_t * npa,const char * fmt,...)683 nvmeadm_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
684 {
685 va_list ap;
686
687 va_start(ap, fmt);
688 nvmeadm_ctrl_vwarn(npa, fmt, ap);
689 va_end(ap);
690
691 exit(-1);
692 }
693
694 void
nvmeadm_hdl_warn(const nvme_process_arg_t * npa,const char * fmt,...)695 nvmeadm_hdl_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
696 {
697 va_list ap;
698
699 va_start(ap, fmt);
700 nvmeadm_hdl_vwarn(npa, fmt, ap);
701 va_end(ap);
702 }
703
704 void __NORETURN
nvmeadm_hdl_fatal(const nvme_process_arg_t * npa,const char * fmt,...)705 nvmeadm_hdl_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
706 {
707 va_list ap;
708
709 va_start(ap, fmt);
710 nvmeadm_hdl_vwarn(npa, fmt, ap);
711 va_end(ap);
712
713 exit(-1);
714 }
715
716 static void
nvmeadm_ctrl_info_warn(const nvme_process_arg_t * npa,const char * fmt,...)717 nvmeadm_ctrl_info_warn(const nvme_process_arg_t *npa, const char *fmt, ...)
718 {
719 va_list ap;
720
721 va_start(ap, fmt);
722 nvmeadm_ctrl_info_vwarn(npa, fmt, ap);
723 va_end(ap);
724 }
725
726 static void
nvmeadm_ctrl_info_fatal(const nvme_process_arg_t * npa,const char * fmt,...)727 nvmeadm_ctrl_info_fatal(const nvme_process_arg_t *npa, const char *fmt, ...)
728 {
729 va_list ap;
730
731 va_start(ap, fmt);
732 nvmeadm_ctrl_info_vwarn(npa, fmt, ap);
733 va_end(ap);
734
735 exit(-1);
736 }
737
738 boolean_t
nvme_version_check(const nvme_process_arg_t * npa,const nvme_version_t * vers)739 nvme_version_check(const nvme_process_arg_t *npa, const nvme_version_t *vers)
740 {
741 return (nvme_vers_atleast(npa->npa_version, vers) ? B_TRUE : B_FALSE);
742 }
743
744 long long
optparse_ui_range(const char * raw,const char * field,uint64_t min,uint64_t max)745 optparse_ui_range(const char *raw, const char *field, uint64_t min,
746 uint64_t max)
747 {
748 const char *errstr;
749 long long l;
750
751 l = strtonumx(raw, min, max, &errstr, 0);
752 if (errstr != NULL) {
753 errx(-1, "failed to parse %s: value %s is %s: valid values "
754 "are in the range [0x%" PRIx64 ", 0x%" PRIx64 "]", field,
755 raw, errstr, min, max);
756 }
757
758 return (l);
759 }
760
761 nvme_disc_impact_t
optparse_impact(char * raw)762 optparse_impact(char *raw)
763 {
764 nvme_disc_impact_t impact = 0;
765 char *last;
766
767 for (char *s = strtok_r(raw, ",", &last); s != NULL;
768 s = strtok_r(NULL, ",", &last)) {
769 if (strcmp(s, "data") == 0) {
770 impact |= NVME_DISC_IMPACT_DATA;
771 } else if (strcmp(s, "namespace") == 0) {
772 impact |= NVME_DISC_IMPACT_NS;
773 } else {
774 errx(-1, "invalid impact string: %s", s);
775 }
776 }
777
778 return (impact);
779 }
780
781 /*
782 * Because nvmeadm operates on a series of NVMe devices for several commands,
783 * here we need to clean up everything that we allocated for this device so we
784 * can prepare for the next.
785 */
786 static void
nvmeadm_cleanup_npa(nvme_process_arg_t * npa)787 nvmeadm_cleanup_npa(nvme_process_arg_t *npa)
788 {
789 npa->npa_idctl = NULL;
790 npa->npa_version = NULL;
791
792 if (npa->npa_excl) {
793 if (npa->npa_ns != NULL) {
794 nvme_ns_unlock(npa->npa_ns);
795 } else if (npa->npa_ctrl != NULL) {
796 nvme_ctrl_unlock(npa->npa_ctrl);
797 }
798 }
799
800 if (npa->npa_ns_info != NULL) {
801 nvme_ns_info_free(npa->npa_ns_info);
802 npa->npa_ns_info = NULL;
803 }
804
805 if (npa->npa_ctrl_info != NULL) {
806 nvme_ctrl_info_free(npa->npa_ctrl_info);
807 npa->npa_ctrl_info = NULL;
808 }
809
810 if (npa->npa_ns != NULL) {
811 nvme_ns_fini(npa->npa_ns);
812 npa->npa_ns = NULL;
813 }
814
815 if (npa->npa_ctrl != NULL) {
816 nvme_ctrl_fini(npa->npa_ctrl);
817 npa->npa_ctrl = NULL;
818 }
819 }
820
821 /*
822 * Determine if a command requires a controller or namespace write lock. If so
823 * we first attempt to grab it non-blocking and then if that fails, we'll warn
824 * that we may be blocking for the lock so that way the user has a chance to do
825 * something and can cancel it.
826 */
827 void
nvmeadm_excl(const nvme_process_arg_t * npa,nvme_lock_level_t level)828 nvmeadm_excl(const nvme_process_arg_t *npa, nvme_lock_level_t level)
829 {
830 bool ret;
831 nvme_lock_flags_t flags = NVME_LOCK_F_DONT_BLOCK;
832
833 if (npa->npa_ns != NULL) {
834 ret = nvme_ns_lock(npa->npa_ns, level, flags);
835 } else {
836 ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags);
837 }
838
839 if (ret) {
840 return;
841 }
842
843 if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_LOCK_WOULD_BLOCK) {
844 nvmeadm_fatal(npa, "failed to acquire lock on %s",
845 npa->npa_name);
846 }
847
848 (void) fprintf(stderr, "Waiting on contended %s lock on %s...",
849 npa->npa_ns != NULL ? "namespace": "controller", npa->npa_name);
850 (void) fflush(stderr);
851
852 flags &= ~NVME_LOCK_F_DONT_BLOCK;
853 if (npa->npa_ns != NULL) {
854 ret = nvme_ns_lock(npa->npa_ns, level, flags);
855 } else {
856 ret = nvme_ctrl_lock(npa->npa_ctrl, level, flags);
857 }
858
859 if (!ret) {
860 nvmeadm_fatal(npa, "failed to acquire lock on %s",
861 npa->npa_name);
862 }
863
864 (void) fprintf(stderr, " acquired\n");
865 }
866
867 /*
868 * Most of nvmeadm was written before the existence of libnvme and always had
869 * things like the identify controller or namespace information sitting around.
870 * As such we try to grab all this in one place for it. Note, regardless if this
871 * succeeds or fails, our callers will still call nvmeadm_cleanup_npa() so we
872 * don't need to clean up the various libnvme objects.
873 */
874 static boolean_t
nvmeadm_open_dev(nvme_process_arg_t * npa)875 nvmeadm_open_dev(nvme_process_arg_t *npa)
876 {
877 if (!nvme_ctrl_ns_init(npa->npa_nvme, npa->npa_name, &npa->npa_ctrl,
878 &npa->npa_ns)) {
879 nvmeadm_hdl_warn(npa, "failed to open '%s'", npa->npa_name);
880 exitcode = -1;
881 return (B_FALSE);
882 }
883
884 /*
885 * Several commands expect to be able to access the controller's
886 * information snapshot. Grab that now for it and the namespace if it
887 * exists.
888 */
889 if (!nvme_ctrl_info_snap(npa->npa_ctrl, &npa->npa_ctrl_info)) {
890 nvmeadm_warn(npa, "failed to get controller info for %s",
891 npa->npa_ctrl_name);
892 exitcode = -1;
893 return (B_FALSE);
894 }
895
896 if (npa->npa_ns != NULL && !nvme_ns_info_snap(npa->npa_ns,
897 &npa->npa_ns_info)) {
898 nvmeadm_warn(npa, "failed to get namespace info for %s",
899 npa->npa_name);
900 exitcode = -1;
901 return (B_FALSE);
902 }
903
904 /*
905 * Snapshot data the rest of the command has fairly ingrained.
906 */
907 npa->npa_version = nvme_ctrl_info_version(npa->npa_ctrl_info);
908 npa->npa_idctl = nvme_ctrl_info_identify(npa->npa_ctrl_info);
909
910 /*
911 * If this command has requested exclusive access, proceed to grab that
912 * before we continue.
913 */
914 if (npa->npa_excl) {
915 nvmeadm_excl(npa, NVME_LOCK_L_WRITE);
916 }
917
918 return (B_TRUE);
919 }
920
921 static bool
nvmeadm_ctrl_disc_cb(nvme_t * nvme,const nvme_ctrl_disc_t * disc,void * arg)922 nvmeadm_ctrl_disc_cb(nvme_t *nvme, const nvme_ctrl_disc_t *disc, void *arg)
923 {
924 nvme_process_arg_t *npa = arg;
925 di_node_t di = nvme_ctrl_disc_devi(disc);
926 char name[128];
927
928 (void) snprintf(name, sizeof (name), "%s%d", di_driver_name(di),
929 di_instance(di));
930 npa->npa_name = name;
931 npa->npa_ctrl_name = name;
932
933 if (nvmeadm_open_dev(npa)) {
934 if (npa->npa_cmd->c_func(npa) != 0) {
935 exitcode = -1;
936 }
937 }
938
939 nvmeadm_cleanup_npa(npa);
940 return (true);
941 }
942
943 int
main(int argc,char ** argv)944 main(int argc, char **argv)
945 {
946 int c;
947 const nvmeadm_cmd_t *cmd;
948 nvme_process_arg_t npa = { 0 };
949 int help = 0;
950 char *ctrl = NULL;
951
952 while ((c = getopt(argc, argv, "dhv")) != -1) {
953 switch (c) {
954 case 'd':
955 debug++;
956 break;
957
958 case 'v':
959 verbose++;
960 break;
961
962 case 'h':
963 help++;
964 break;
965
966 case '?':
967 usage(NULL);
968 exit(-1);
969 }
970 }
971
972 if (optind == argc) {
973 usage(NULL);
974 if (help)
975 exit(0);
976 else
977 exit(-1);
978 }
979
980 /* Look up the specified command in the command table. */
981 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
982 if (strcmp(cmd->c_name, argv[optind]) == 0)
983 break;
984
985 if (cmd->c_name == NULL) {
986 usage(NULL);
987 exit(-1);
988 }
989
990 if (help) {
991 usage(cmd);
992 exit(0);
993 }
994
995 npa.npa_nvme = nvme_init();
996 if (npa.npa_nvme == NULL) {
997 err(-1, "failed to initialize libnvme");
998 }
999 npa.npa_cmd = cmd;
1000 npa.npa_excl = ((npa.npa_cmd->c_flags & NVMEADM_C_EXCL) != 0);
1001
1002 optind++;
1003
1004 /*
1005 * Store the remaining arguments for use by the command. Give the
1006 * command a chance to process the options across the board before going
1007 * into each controller.
1008 */
1009 npa.npa_argc = argc - optind;
1010 npa.npa_argv = &argv[optind];
1011
1012 if (npa.npa_cmd->c_optparse != NULL) {
1013 optind = 0;
1014 npa.npa_cmd->c_optparse(&npa);
1015 npa.npa_argc -= optind;
1016 npa.npa_argv += optind;
1017 }
1018
1019 /*
1020 * If this command indicates that it does not require a controller, then
1021 * don't bother with the rest and just call it immediately.
1022 */
1023 if ((npa.npa_cmd->c_flags & NVMEADM_C_NOCTRL) != 0) {
1024 if (npa.npa_cmd->c_func(&npa) != 0) {
1025 exitcode = -1;
1026 }
1027 exit(exitcode);
1028 }
1029
1030 /*
1031 * All commands but "list" require a ctl/ns argument. However, this
1032 * should not be passed through to the command in its subsequent
1033 * arguments.
1034 */
1035 if (npa.npa_argc == 0 && cmd->c_func != do_list) {
1036 warnx("missing controller/namespace name");
1037 usage(cmd);
1038 exit(-1);
1039 }
1040
1041 if (npa.npa_argc > 0) {
1042 ctrl = npa.npa_argv[0];
1043 npa.npa_argv++;
1044 npa.npa_argc--;
1045 } else {
1046 if (!nvme_ctrl_discover(npa.npa_nvme, nvmeadm_ctrl_disc_cb,
1047 &npa)) {
1048 nvmeadm_hdl_fatal(&npa, "failed to walk controllers");
1049 }
1050 exit(exitcode);
1051 }
1052
1053 /*
1054 * Make sure we're not running commands on multiple controllers that
1055 * aren't allowed to do that.
1056 */
1057 if (ctrl != NULL && strchr(ctrl, ',') != NULL &&
1058 (cmd->c_flags & NVMEADM_C_MULTI) == 0) {
1059 warnx("%s not allowed on multiple controllers",
1060 cmd->c_name);
1061 usage(cmd);
1062 exit(-1);
1063 }
1064
1065 /*
1066 * Get controller/namespace arguments and run command.
1067 */
1068 while ((npa.npa_name = strsep(&ctrl, ",")) != NULL) {
1069 char *ctrl_name, *slash;
1070
1071 /*
1072 * We may be given just a controller as an argument or a
1073 * controller and a namespace as an argument. Parts of the
1074 * commands want to know what controller they're referring to
1075 * even if the overall argument was for a namespace. So we
1076 * always dup the argument and try to make the controller out of
1077 * it.
1078 */
1079 ctrl_name = strdup(npa.npa_name);
1080 if (ctrl_name == NULL) {
1081 err(-1, "failed to duplicate NVMe controller/namespace "
1082 "name");
1083 }
1084 if ((slash = strchr(ctrl_name, '/')) != NULL)
1085 *slash = '\0';
1086 npa.npa_ctrl_name = ctrl_name;
1087
1088 if (nvmeadm_open_dev(&npa)) {
1089 if (npa.npa_cmd->c_func(&npa) != 0) {
1090 exitcode = -1;
1091 }
1092 }
1093
1094 nvmeadm_cleanup_npa(&npa);
1095 free(ctrl_name);
1096 }
1097
1098 exit(exitcode);
1099 }
1100
1101 static void
nvme_oferr(const char * fmt,...)1102 nvme_oferr(const char *fmt, ...)
1103 {
1104 va_list ap;
1105
1106 va_start(ap, fmt);
1107 verrx(-1, fmt, ap);
1108 }
1109
1110 static void
usage(const nvmeadm_cmd_t * cmd)1111 usage(const nvmeadm_cmd_t *cmd)
1112 {
1113 const char *progname = getprogname();
1114
1115 (void) fprintf(stderr, "usage:\n");
1116 (void) fprintf(stderr, " %s -h %s\n", progname,
1117 cmd != NULL ? cmd->c_name : "[<command>]");
1118 (void) fprintf(stderr, " %s [-dv] ", progname);
1119
1120 if (cmd != NULL) {
1121 cmd->c_usage(cmd->c_name);
1122 } else {
1123 (void) fprintf(stderr,
1124 "<command> <ctl>[/<ns>][,...] [<args>]\n");
1125 (void) fprintf(stderr,
1126 "\n Manage NVMe controllers and namespaces.\n");
1127 (void) fprintf(stderr, "\ncommands:\n");
1128
1129 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) {
1130 /*
1131 * The longest nvmeadm subcommand is 19 characters long.
1132 * The format string needs to be updated every time a
1133 * longer subcommand is added.
1134 */
1135 (void) fprintf(stderr, " %-19s - %s\n",
1136 cmd->c_name, cmd->c_desc);
1137 }
1138 }
1139 (void) fprintf(stderr, "\n%s flags:\n"
1140 " -h\t\tprint usage information\n"
1141 " -d\t\tprint information useful for debugging %s\n"
1142 " -v\t\tprint verbose information\n",
1143 progname, progname);
1144
1145 if (cmd != NULL && cmd->c_flagdesc != NULL) {
1146 (void) fprintf(stderr, "\n%s %s flags:\n",
1147 progname, cmd->c_name);
1148 (void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
1149 }
1150
1151 if (cmd != NULL && cmd->c_fielddesc != NULL) {
1152 (void) fprintf(stderr, "\n%s %s valid fields:\n",
1153 progname, cmd->c_name);
1154 (void) fprintf(stderr, "%s\n", cmd->c_fielddesc);
1155 }
1156 }
1157
1158 char *
nvme_dskname(di_node_t ctrl,const char * bd_addr)1159 nvme_dskname(di_node_t ctrl, const char *bd_addr)
1160 {
1161 di_dim_t dim;
1162 char *diskname = NULL;
1163
1164 dim = di_dim_init();
1165 if (dim == NULL) {
1166 err(-1, "failed to initialize devinfo minor translation");
1167 }
1168
1169 for (di_node_t child = di_child_node(ctrl); child != DI_NODE_NIL;
1170 child = di_sibling_node(child)) {
1171 char *disk_ctd, *path = NULL;
1172 const char *addr = di_bus_addr(child);
1173 if (addr == NULL)
1174 continue;
1175
1176 if (strcmp(addr, bd_addr) != 0)
1177 continue;
1178
1179 path = di_dim_path_dev(dim, di_driver_name(child),
1180 di_instance(child), "c");
1181
1182 /*
1183 * Error out if we didn't get a path, or if it's too short for
1184 * the following operations to be safe.
1185 */
1186 if (path == NULL || strlen(path) < 2) {
1187 errx(-1, "failed to get a valid minor path");
1188 }
1189
1190 /* Chop off 's0' and get everything past the last '/' */
1191 path[strlen(path) - 2] = '\0';
1192 disk_ctd = strrchr(path, '/');
1193 if (disk_ctd == NULL) {
1194 errx(-1, "encountered malformed minor path: %s", path);
1195 }
1196
1197 diskname = strdup(++disk_ctd);
1198 if (diskname == NULL) {
1199 err(-1, "failed to duplicate disk path");
1200 }
1201
1202 free(path);
1203 break;
1204 }
1205
1206 di_dim_fini(dim);
1207 return (diskname);
1208 }
1209
1210 static void
usage_list(const char * c_name)1211 usage_list(const char *c_name)
1212 {
1213 (void) fprintf(stderr, "%s "
1214 "[-c | -L] [-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n"
1215 " List NVMe controllers and their namespaces. If no "
1216 "controllers and/or name-\n spaces are specified, all "
1217 "controllers and namespaces in the system will be\n "
1218 "listed.\n", c_name);
1219 }
1220
1221 static void
optparse_list(nvme_process_arg_t * npa)1222 optparse_list(nvme_process_arg_t *npa)
1223 {
1224 int c;
1225 uint_t oflags = 0;
1226 boolean_t parse = B_FALSE, cflag = B_FALSE, Lflag = B_FALSE;
1227 const char *fields = NULL;
1228 const ofmt_field_t *ofmt = nvmeadm_list_nsid_ofmt;
1229 nvmeadm_list_t *list = NULL;
1230
1231 if ((list = calloc(1, sizeof (nvmeadm_list_t))) == NULL) {
1232 err(-1, "failed to allocate memory for argument tracking");
1233 }
1234 list->list_mode = NVMEADM_LIST_DEFAULT;
1235
1236 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":cLo:p")) != -1) {
1237 switch (c) {
1238 case 'c':
1239 cflag = B_TRUE;
1240 list->list_mode = NVMEADM_LIST_CTRL;
1241 ofmt = nvmeadm_list_ctrl_ofmt;
1242 break;
1243 case 'L':
1244 Lflag = B_TRUE;
1245 list->list_mode = NVMEADM_LIST_LOC;
1246 ofmt = nvmeadm_list_loc_ofmt;
1247 break;
1248 case 'o':
1249 fields = optarg;
1250 break;
1251
1252 case 'p':
1253 parse = B_TRUE;
1254 oflags |= OFMT_PARSABLE;
1255 break;
1256
1257 case '?':
1258 errx(-1, "unknown option: -%c", optopt);
1259
1260 case ':':
1261 errx(-1, "option -%c requires an argument", optopt);
1262 }
1263 }
1264
1265 if (cflag && Lflag) {
1266 errx(-1, "only one of -c and -L may be selected");
1267 }
1268
1269 if (fields != NULL && !parse) {
1270 errx(-1, "-o can only be used when in parsable mode (-p)");
1271 }
1272
1273 /*
1274 * We use ofmt by default with -L.
1275 */
1276 if (fields == NULL && Lflag) {
1277 fields = "instance,model,location,ctlap";
1278 }
1279
1280 if (parse && fields == NULL) {
1281 errx(-1, "parsable mode (-p) requires one to specify output "
1282 "fields with -o");
1283 }
1284
1285 if (parse || Lflag) {
1286 ofmt_status_t oferr;
1287
1288 oferr = ofmt_open(fields, ofmt, oflags, 0,
1289 &npa->npa_ofmt);
1290 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
1291 }
1292
1293 if (Lflag) {
1294 int e;
1295
1296 list->list_topo = topo_open(TOPO_VERSION, NULL, &e);
1297 if (list->list_topo == NULL) {
1298 errx(-1, "location information unavailable: unable to "
1299 "obtain topo handle: %s", topo_strerror(e));
1300 }
1301
1302 (void) topo_snap_hold(list->list_topo, NULL, &e);
1303 if (e != 0) {
1304 errx(-1, "location information unavailable: unable to "
1305 "obtain topo snapshot: %s", topo_strerror(e));
1306 }
1307 }
1308
1309 npa->npa_cmd_arg = list;
1310 }
1311
1312 static void
do_list_nsid(const nvme_process_arg_t * npa,nvme_ctrl_info_t * ctrl,nvme_ns_info_t * ns)1313 do_list_nsid(const nvme_process_arg_t *npa, nvme_ctrl_info_t *ctrl,
1314 nvme_ns_info_t *ns)
1315 {
1316 const char *bd_addr, *disk = NULL, *state = NULL;
1317 char *disk_path = NULL;
1318 di_node_t ctrl_devi;
1319
1320 switch (nvme_ns_info_level(ns)) {
1321 case NVME_NS_DISC_F_ALL:
1322 disk = "unallocated";
1323 state = "unallocated";
1324 break;
1325 case NVME_NS_DISC_F_ALLOCATED:
1326 disk = "inactive";
1327 state = "allocated";
1328 break;
1329 case NVME_NS_DISC_F_ACTIVE:
1330 disk = "ignored";
1331 state = "active";
1332 break;
1333 case NVME_NS_DISC_F_NOT_IGNORED:
1334 disk = "unattached";
1335 state = "active-usable";
1336 break;
1337 case NVME_NS_DISC_F_BLKDEV:
1338 disk = "unknown";
1339 state = "blkdev";
1340 if (nvme_ns_info_bd_addr(ns, &bd_addr) &&
1341 nvme_ctrl_devi(npa->npa_ctrl, &ctrl_devi)) {
1342 disk_path = nvme_dskname(ctrl_devi, bd_addr);
1343 disk = disk_path;
1344 }
1345 break;
1346 }
1347
1348 if (npa->npa_ofmt != NULL) {
1349 nvmeadm_list_ofmt_arg_t oarg = { 0 };
1350
1351 oarg.nloa_name = npa->npa_ctrl_name;
1352 oarg.nloa_ctrl = ctrl;
1353 oarg.nloa_ns = ns;
1354 oarg.nloa_disk = disk_path;
1355 oarg.nloa_state = state;
1356 if (!nvme_ctrl_devi(npa->npa_ctrl, &oarg.nloa_dip))
1357 oarg.nloa_dip = DI_NODE_NIL;
1358
1359 ofmt_print(npa->npa_ofmt, &oarg);
1360 } else {
1361 (void) printf(" %s/%u (%s)", npa->npa_ctrl_name,
1362 nvme_ns_info_nsid(ns), disk);
1363 if (nvme_ns_info_level(ns) >= NVME_NS_DISC_F_ACTIVE) {
1364 (void) printf(": ");
1365 nvme_print_nsid_summary(ns);
1366 } else {
1367 (void) printf("\n");
1368 }
1369 }
1370
1371 free(disk_path);
1372 }
1373
1374 /*
1375 * Determine a device's controlling AP. As most NVMe devices are PCIe devices
1376 * (except when virtualized) we look at the parent pcieb instance and look for
1377 * its ap-names and slot-names properties.
1378 */
1379 static void
do_list_phys_map_ctlap(nvmeadm_list_ofmt_arg_t * oarg)1380 do_list_phys_map_ctlap(nvmeadm_list_ofmt_arg_t *oarg)
1381 {
1382 di_node_t pdip;
1383 int *ap_names, *slot_names;
1384
1385 pdip = di_parent_node(oarg->nloa_dip);
1386 if (pdip == DI_NODE_NIL) {
1387 return;
1388 }
1389
1390 /*
1391 * This probably will need to change a bit when we understand the exact
1392 * shape AP's take when ACPI-based PCI hotplug is supported for virtual
1393 * machines. In that world, it's not clear where the AP will live.
1394 */
1395 const char *drv = di_driver_name(pdip);
1396 if (drv == NULL || strcmp(drv, "pcieb") != 0) {
1397 return;
1398 }
1399
1400 /*
1401 * The AP names property is a bitfield that indicates which entry in the
1402 * slot-names. A given device can in theory support multiple slots, but
1403 * PCIe is always going to be a value of 1.
1404 */
1405 if (di_prop_lookup_ints(DDI_DEV_T_ANY, pdip, "ap-names", &ap_names) !=
1406 1 || *ap_names != 1) {
1407 return;
1408 }
1409
1410 /*
1411 * The slot-names property is structured with the first word as a
1412 * bitfield mask and then a series of names which are guaranteed to be
1413 * NULL terminated.
1414 */
1415 int nu32 = di_prop_lookup_ints(DDI_DEV_T_ANY, pdip, "slot-names",
1416 &slot_names);
1417 if (nu32 <= 1 || slot_names[0] != 1) {
1418 return;
1419 }
1420
1421 oarg->nloa_ap = calloc(nu32 - 1, sizeof (uint32_t));
1422 if (oarg->nloa_ap == NULL) {
1423 err(-1, "failed to allocate memory for attachment point name");
1424 }
1425
1426 (void) memcpy(oarg->nloa_ap, &slot_names[1], sizeof (uint32_t) *
1427 (nu32 - 1));
1428 }
1429
1430 /*
1431 * NVMe devices are always enumerated with an 'nvme' node. See if we can find
1432 * that.
1433 */
1434 static int
do_list_phys_map_loc_cb(topo_hdl_t * hp,tnode_t * tn,void * arg)1435 do_list_phys_map_loc_cb(topo_hdl_t *hp, tnode_t *tn, void *arg)
1436 {
1437 nvmeadm_list_ofmt_arg_t *oarg = arg;
1438 char *label = NULL;
1439 uint32_t tinst;
1440 int err;
1441
1442 if (strcmp(topo_node_name(tn), NVME) != 0) {
1443 return (TOPO_WALK_NEXT);
1444 }
1445
1446 if (topo_prop_get_uint32(tn, TOPO_PGROUP_IO, TOPO_IO_INSTANCE, &tinst,
1447 &err) != 0) {
1448 return (TOPO_WALK_NEXT);
1449 }
1450
1451 if (tinst != (uint32_t)di_instance(oarg->nloa_dip)) {
1452 return (TOPO_WALK_NEXT);
1453 }
1454
1455 /*
1456 * Walk the node hierarchy looking for something that has a label. We
1457 * basically only consider ourselves and our immediate parent if that
1458 * parent is a bay or slot. If we go too far then we may end up finding
1459 * a label for a device that has no relationship to us.
1460 */
1461 if (topo_prop_get_string(tn, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL,
1462 &label, &err) != 0) {
1463 tnode_t *pn = topo_node_parent(tn);
1464 const char *pname = topo_node_name(pn);
1465 if (strcmp(pname, BAY) == 0 || strcmp(pname, SLOT) == 0) {
1466 if (topo_prop_get_string(pn, TOPO_PGROUP_PROTOCOL,
1467 TOPO_PROP_LABEL, &label, &err) != 0) {
1468 label = NULL;
1469 }
1470 }
1471 }
1472
1473 oarg->nloa_loc = label;
1474 return (TOPO_WALK_TERMINATE);
1475 }
1476
1477 static void
do_list_phys_map_loc(nvmeadm_list_t * list,nvmeadm_list_ofmt_arg_t * oarg)1478 do_list_phys_map_loc(nvmeadm_list_t *list, nvmeadm_list_ofmt_arg_t *oarg)
1479 {
1480 topo_walk_t *wp;
1481 int err;
1482
1483 wp = topo_walk_init(list->list_topo, FM_FMRI_SCHEME_HC,
1484 do_list_phys_map_loc_cb, oarg, &err);
1485 if (wp == NULL) {
1486 warnx("location information not available: failed to "
1487 "initialize topo walk: %s", topo_strerror(err));
1488 exitcode = -1;
1489 return;
1490 }
1491
1492 err = topo_walk_step(wp, TOPO_WALK_SIBLING);
1493 if (err == TOPO_WALK_ERR) {
1494 warnx("location information not available: topo walk failed");
1495 exitcode = -1;
1496 }
1497
1498 topo_walk_fini(wp);
1499 }
1500
1501 static int
do_list(const nvme_process_arg_t * npa)1502 do_list(const nvme_process_arg_t *npa)
1503 {
1504 nvme_ctrl_info_t *info = NULL;
1505 nvme_ns_iter_t *iter = NULL;
1506 nvme_iter_t ret;
1507 const nvme_ns_disc_t *disc;
1508 nvme_ns_disc_level_t level;
1509 int rv = -1;
1510 nvmeadm_list_t *list = npa->npa_cmd_arg;
1511
1512 if (npa->npa_argc > 0) {
1513 errx(-1, "%s passed extraneous arguments starting with %s",
1514 npa->npa_cmd->c_name, npa->npa_argv[0]);
1515 }
1516
1517 if (!nvme_ctrl_info_snap(npa->npa_ctrl, &info)) {
1518 nvmeadm_warn(npa, "failed to get controller information for %s",
1519 npa->npa_ctrl_name);
1520 return (-1);
1521 }
1522
1523 if (npa->npa_ofmt == NULL) {
1524 (void) printf("%s: ", npa->npa_ctrl_name);
1525 nvme_print_ctrl_summary(info);
1526 } else if (list->list_mode != NVMEADM_LIST_DEFAULT) {
1527 nvmeadm_list_ofmt_arg_t oarg = { 0 };
1528 oarg.nloa_name = npa->npa_ctrl_name;
1529 oarg.nloa_ctrl = info;
1530 if (!nvme_ctrl_devi(npa->npa_ctrl, &oarg.nloa_dip))
1531 oarg.nloa_dip = DI_NODE_NIL;
1532
1533 if (list->list_mode == NVMEADM_LIST_LOC) {
1534 do_list_phys_map_ctlap(&oarg);
1535 do_list_phys_map_loc(list, &oarg);
1536 }
1537
1538 ofmt_print(npa->npa_ofmt, &oarg);
1539 free(oarg.nloa_ap);
1540 if (list->list_topo != NULL) {
1541 topo_hdl_strfree(list->list_topo, oarg.nloa_loc);
1542 }
1543 }
1544
1545 if (list->list_mode != NVMEADM_LIST_DEFAULT) {
1546 rv = 0;
1547 goto out;
1548 }
1549
1550 /*
1551 * Check if we were given an explicit namespace as an argument. If so,
1552 * we always list it and don't need to do discovery.
1553 */
1554 if (npa->npa_ns != NULL) {
1555 nvme_ns_info_t *ns_info;
1556
1557 if (!nvme_ns_info_snap(npa->npa_ns, &ns_info)) {
1558 nvmeadm_warn(npa, "failed to get namespace "
1559 "information for %s", npa->npa_name);
1560 goto out;
1561 }
1562
1563 do_list_nsid(npa, info, ns_info);
1564 nvme_ns_info_free(ns_info);
1565 rv = 0;
1566 goto out;
1567 }
1568
1569 if (verbose) {
1570 level = NVME_NS_DISC_F_ALL;
1571 } else {
1572 level = NVME_NS_DISC_F_NOT_IGNORED;
1573 }
1574
1575 if (!nvme_ns_discover_init(npa->npa_ctrl, level, &iter)) {
1576 nvmeadm_warn(npa, "failed to iterate namespaces on %s",
1577 npa->npa_ctrl_name);
1578 goto out;
1579 }
1580
1581 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
1582 nvme_ns_info_t *ns_info;
1583 uint32_t nsid = nvme_ns_disc_nsid(disc);
1584
1585 if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, nsid, &ns_info)) {
1586 nvmeadm_warn(npa, "failed to get namespace "
1587 "information for %s/%u", npa->npa_ctrl_name, nsid);
1588 exitcode = -1;
1589 continue;
1590 }
1591
1592 do_list_nsid(npa, info, ns_info);
1593 nvme_ns_info_free(ns_info);
1594 }
1595
1596 nvme_ns_discover_fini(iter);
1597 if (ret == NVME_ITER_ERROR) {
1598 nvmeadm_warn(npa, "failed to iterate all namespaces on %s",
1599 npa->npa_ctrl_name);
1600 } else {
1601 rv = 0;
1602 }
1603
1604 out:
1605 nvme_ctrl_info_free(info);
1606 return (rv);
1607 }
1608
1609 static void
optparse_identify_ctrl(nvme_process_arg_t * npa)1610 optparse_identify_ctrl(nvme_process_arg_t *npa)
1611 {
1612 int c;
1613
1614 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacn")) != -1) {
1615 switch (c) {
1616 case 'C':
1617 npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
1618 break;
1619
1620 case 'a':
1621 npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
1622 break;
1623
1624 case 'c':
1625 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1626 break;
1627
1628 case 'n':
1629 npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
1630 break;
1631
1632 case '?':
1633 errx(-1, "unknown option: -%c", optopt);
1634
1635 case ':':
1636 errx(-1, "option -%c requires an argument", optopt);
1637 }
1638 }
1639 }
1640
1641 static void
usage_identify_ctrl(const char * c_name)1642 usage_identify_ctrl(const char *c_name)
1643 {
1644 (void) fprintf(stderr, "%s [-C | -c | [-a] -n] <ctl>[,...]\n\n"
1645 " Print detailed information about the specified NVMe "
1646 "controllers.\n", c_name);
1647 }
1648
1649 static int
do_identify_ctrl(const nvme_process_arg_t * npa)1650 do_identify_ctrl(const nvme_process_arg_t *npa)
1651 {
1652 boolean_t alloc = B_FALSE;
1653
1654 if (npa->npa_ns != NULL)
1655 errx(-1, "identify-controller cannot be used on namespaces");
1656
1657 if (npa->npa_argc > 0) {
1658 errx(-1, "%s passed extraneous arguments starting with %s",
1659 npa->npa_cmd->c_name, npa->npa_argv[0]);
1660 }
1661
1662 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
1663 npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
1664 errx(-1, "-C cannot be combined with other flags");
1665 }
1666
1667 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1668 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1669 errx(-1, "-c cannot be combined with other flags");
1670 }
1671
1672 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
1673 npa->npa_cmdflags !=
1674 (NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) {
1675 errx(-1, "-a can only be used together with -n");
1676 }
1677
1678 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
1679 alloc = B_TRUE;
1680 }
1681
1682 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0) {
1683 const nvme_identify_nsid_t *idns;
1684
1685 if (!nvme_ctrl_info_common_ns(npa->npa_ctrl_info, &idns)) {
1686 nvmeadm_ctrl_info_warn(npa, "failed to get common "
1687 "namespace information for %s", npa->npa_name);
1688 return (-1);
1689 }
1690
1691 (void) printf("%s: ", npa->npa_name);
1692 nvme_print_identify_nsid(idns, npa->npa_version);
1693 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0) {
1694 const char *caption;
1695 uint32_t cns;
1696 nvme_identify_nsid_list_t *idnslist;
1697 nvme_id_req_t *req;
1698
1699 if (alloc) {
1700 caption = "Identify Allocated Namespace List";
1701 cns = NVME_IDENTIFY_NSID_ALLOC_LIST;
1702 } else {
1703 caption = "Identify Active Namespace List";
1704 cns = NVME_IDENTIFY_NSID_LIST;
1705 }
1706
1707 if ((idnslist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1708 err(-1, "failed to allocate identify buffer size");
1709 }
1710
1711 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM, cns,
1712 &req)) {
1713 nvmeadm_fatal(npa, "failed to initialize %s request",
1714 caption);
1715 }
1716
1717 /*
1718 * Always set the NSID for these requests to NSID 0 so that way
1719 * we can start the list at the beginning. When we encounter
1720 * devices with more than 1024 NSIDs then we'll need to issue
1721 * additional requests.
1722 */
1723 if (!nvme_id_req_set_nsid(req, 0) ||
1724 !nvme_id_req_set_output(req, idnslist,
1725 NVME_IDENTIFY_BUFSIZE)) {
1726 nvmeadm_fatal(npa, "failed to set required fields for "
1727 "identify request");
1728 }
1729
1730 if (!nvme_id_req_exec(req)) {
1731 nvmeadm_fatal(npa, "failed to execute identify "
1732 "request");
1733 }
1734 nvme_id_req_fini(req);
1735
1736 (void) printf("%s: ", npa->npa_name);
1737
1738 nvme_print_identify_nsid_list(caption, idnslist);
1739 free(idnslist);
1740 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1741 nvme_identify_ctrl_list_t *ctlist;
1742 nvme_id_req_t *req;
1743
1744 if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1745 err(-1, "failed to allocate identify buffer size");
1746 }
1747
1748 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1749 NVME_IDENTIFY_CTRL_LIST, &req)) {
1750 nvmeadm_fatal(npa, "failed to initialize identify "
1751 "request");
1752 }
1753
1754 if (!nvme_id_req_set_ctrlid(req, 0) ||
1755 !nvme_id_req_set_output(req, ctlist,
1756 NVME_IDENTIFY_BUFSIZE)) {
1757 nvmeadm_fatal(npa, "failed to set required fields for "
1758 "identify request");
1759 }
1760 if (!nvme_id_req_exec(req)) {
1761 nvmeadm_fatal(npa, "failed to execute identify "
1762 "request");
1763 }
1764 nvme_id_req_fini(req);
1765
1766 (void) printf("%s: ", npa->npa_name);
1767 nvme_print_identify_ctrl_list("Identify Controller List",
1768 ctlist);
1769 free(ctlist);
1770 } else {
1771 uint32_t mpsmin;
1772
1773 if (!nvme_ctrl_info_pci_mps_min(npa->npa_ctrl_info,
1774 &mpsmin)) {
1775 nvmeadm_ctrl_info_fatal(npa, "failed to get minimum "
1776 "memory page size");
1777 }
1778
1779 (void) printf("%s: ", npa->npa_name);
1780 nvme_print_identify_ctrl(npa->npa_idctl, mpsmin,
1781 npa->npa_version);
1782 }
1783
1784 return (0);
1785 }
1786
1787 static void
optparse_identify_ns(nvme_process_arg_t * npa)1788 optparse_identify_ns(nvme_process_arg_t *npa)
1789 {
1790 int c;
1791
1792 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":cd")) != -1) {
1793 switch (c) {
1794 case 'c':
1795 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1796 break;
1797
1798 case 'd':
1799 npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1800 break;
1801
1802 case '?':
1803 errx(-1, "unknown option: -%c", optopt);
1804
1805 case ':':
1806 errx(-1, "option -%c requires an argument", optopt);
1807 }
1808 }
1809 }
1810
1811 static void
usage_identify_ns(const char * c_name)1812 usage_identify_ns(const char *c_name)
1813 {
1814 (void) fprintf(stderr, "%s [-c | -d ] <ctl>/<ns>[,...]\n\n"
1815 " Print detailed information about the specified NVMe "
1816 "namespaces.\n", c_name);
1817 }
1818
1819 static int
do_identify_ns(const nvme_process_arg_t * npa)1820 do_identify_ns(const nvme_process_arg_t *npa)
1821 {
1822 uint32_t nsid;
1823
1824 if (npa->npa_ns == NULL)
1825 errx(-1, "identify-namespace cannot be used on controllers");
1826
1827 if (npa->npa_argc > 0) {
1828 errx(-1, "%s passed extraneous arguments starting with %s",
1829 npa->npa_cmd->c_name, npa->npa_argv[0]);
1830 }
1831
1832 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1833 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1834 errx(-1, "-c cannot be combined with other flags");
1835 }
1836
1837 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1838 npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1839 errx(-1, "-d cannot be combined with other flags");
1840 }
1841
1842 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0) {
1843 errx(-1, "-a cannot be used on namespaces");
1844 }
1845
1846 nsid = nvme_ns_info_nsid(npa->npa_ns_info);
1847
1848 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0) {
1849 nvme_identify_ctrl_list_t *ctlist;
1850 nvme_id_req_t *req;
1851
1852 if ((ctlist = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1853 err(-1, "failed to allocate identify buffer size");
1854 }
1855
1856 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1857 NVME_IDENTIFY_NSID_CTRL_LIST, &req)) {
1858 nvmeadm_fatal(npa, "failed to initialize identify "
1859 "request");
1860 }
1861
1862 if (!nvme_id_req_set_nsid(req, nsid) ||
1863 !nvme_id_req_set_ctrlid(req, 0) ||
1864 !nvme_id_req_set_output(req, ctlist,
1865 NVME_IDENTIFY_BUFSIZE)) {
1866 nvmeadm_fatal(npa, "failed to set required fields for "
1867 "identify request");
1868 }
1869
1870 if (!nvme_id_req_exec(req)) {
1871 nvmeadm_fatal(npa, "failed to execute identify "
1872 "request");
1873 }
1874 nvme_id_req_fini(req);
1875
1876 (void) printf("%s: ", npa->npa_name);
1877 nvme_print_identify_ctrl_list(
1878 "Identify Attached Controller List", ctlist);
1879 free(ctlist);
1880 } else if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0) {
1881 nvme_identify_nsid_desc_t *nsdesc;
1882 nvme_id_req_t *req;
1883
1884 if ((nsdesc = malloc(NVME_IDENTIFY_BUFSIZE)) == NULL) {
1885 err(-1, "failed to allocate identify buffer size");
1886 }
1887
1888 if (!nvme_id_req_init_by_cns(npa->npa_ctrl, NVME_CSI_NVM,
1889 NVME_IDENTIFY_NSID_DESC, &req)) {
1890 nvmeadm_fatal(npa, "failed to initialize identify "
1891 "request");
1892 }
1893
1894 if (!nvme_id_req_set_nsid(req, nsid) ||
1895 !nvme_id_req_set_output(req, nsdesc,
1896 NVME_IDENTIFY_BUFSIZE)) {
1897 nvmeadm_fatal(npa, "failed to set required fields for "
1898 "identify request");
1899 }
1900
1901 if (!nvme_id_req_exec(req)) {
1902 nvmeadm_fatal(npa, "failed to execute identify "
1903 "request");
1904 }
1905 nvme_id_req_fini(req);
1906
1907 (void) printf("%s: ", npa->npa_name);
1908 nvme_print_identify_nsid_desc(nsdesc);
1909 free(nsdesc);
1910 } else {
1911 const nvme_identify_nsid_t *idns;
1912
1913 (void) printf("%s: ", npa->npa_name);
1914 idns = nvme_ns_info_identify(npa->npa_ns_info);
1915 nvme_print_identify_nsid(idns, npa->npa_version);
1916 }
1917
1918 return (0);
1919 }
1920
1921 static void
optparse_identify(nvme_process_arg_t * npa)1922 optparse_identify(nvme_process_arg_t *npa)
1923 {
1924 int c;
1925
1926 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Cacdn")) != -1) {
1927 switch (c) {
1928 case 'C':
1929 npa->npa_cmdflags |= NVMEADM_O_ID_COMMON_NS;
1930 break;
1931
1932 case 'a':
1933 npa->npa_cmdflags |= NVMEADM_O_ID_ALLOC_NS;
1934 break;
1935
1936 case 'c':
1937 npa->npa_cmdflags |= NVMEADM_O_ID_CTRL_LIST;
1938 break;
1939
1940 case 'd':
1941 npa->npa_cmdflags |= NVMEADM_O_ID_DESC_LIST;
1942 break;
1943
1944 case 'n':
1945 npa->npa_cmdflags |= NVMEADM_O_ID_NSID_LIST;
1946 break;
1947
1948 case '?':
1949 errx(-1, "unknown option: -%c", optopt);
1950
1951 case ':':
1952 errx(-1, "option -%c requires an argument", optopt);
1953
1954 }
1955 }
1956
1957 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0 &&
1958 (npa->npa_cmdflags &
1959 ~(NVMEADM_O_ID_ALLOC_NS | NVMEADM_O_ID_NSID_LIST)) != 0) {
1960 errx(-1, "-a can only be used alone or together with -n");
1961 }
1962
1963 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0 &&
1964 npa->npa_cmdflags != NVMEADM_O_ID_COMMON_NS) {
1965 errx(-1, "-C cannot be combined with other flags");
1966
1967 }
1968
1969 if ((npa->npa_cmdflags & NVMEADM_O_ID_CTRL_LIST) != 0 &&
1970 npa->npa_cmdflags != NVMEADM_O_ID_CTRL_LIST) {
1971 errx(-1, "-c cannot be combined with other flags");
1972 }
1973
1974 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0 &&
1975 npa->npa_cmdflags != NVMEADM_O_ID_DESC_LIST) {
1976 errx(-1, "-d cannot be combined with other flags");
1977 }
1978 }
1979
1980 static void
usage_identify(const char * c_name)1981 usage_identify(const char *c_name)
1982 {
1983 (void) fprintf(stderr,
1984 "%s [ -C | -c | -d | [-a] -n ] <ctl>[/<ns>][,...]\n\n"
1985 " Print detailed information about the specified NVMe "
1986 "controllers and/or name-\n spaces.\n", c_name);
1987 }
1988
1989 static int
do_identify(const nvme_process_arg_t * npa)1990 do_identify(const nvme_process_arg_t *npa)
1991 {
1992 if (npa->npa_argc > 0) {
1993 errx(-1, "%s passed extraneous arguments starting with %s",
1994 npa->npa_cmd->c_name, npa->npa_argv[0]);
1995 }
1996
1997 if (npa->npa_ns != NULL) {
1998 if ((npa->npa_cmdflags & NVMEADM_O_ID_COMMON_NS) != 0)
1999 errx(-1, "-C cannot be used on namespaces");
2000
2001 if ((npa->npa_cmdflags & NVMEADM_O_ID_ALLOC_NS) != 0)
2002 errx(-1, "-a cannot be used on namespaces");
2003
2004 if ((npa->npa_cmdflags & NVMEADM_O_ID_NSID_LIST) != 0)
2005 errx(-1, "-n cannot be used on namespaces");
2006
2007 return (do_identify_ns(npa));
2008 } else {
2009 if ((npa->npa_cmdflags & NVMEADM_O_ID_DESC_LIST) != 0)
2010 errx(-1, "-d cannot be used on controllers");
2011
2012 return (do_identify_ctrl(npa));
2013 }
2014 }
2015
2016 static void
optparse_list_logs(nvme_process_arg_t * npa)2017 optparse_list_logs(nvme_process_arg_t *npa)
2018 {
2019 int c;
2020 uint_t oflags = 0;
2021 boolean_t parse = B_FALSE;
2022 const char *fields = NULL;
2023 char *scope = NULL;
2024 ofmt_status_t oferr;
2025 nvmeadm_list_logs_t *nll;
2026
2027 if ((nll = calloc(1, sizeof (nvmeadm_list_logs_t))) == NULL) {
2028 err(-1, "failed to allocate memory to track log information");
2029 }
2030
2031 npa->npa_cmd_arg = nll;
2032
2033 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:ps:")) != -1) {
2034 switch (c) {
2035 case 'a':
2036 nll->nll_unimpl = B_TRUE;
2037 break;
2038 case 'H':
2039 oflags |= OFMT_NOHEADER;
2040 break;
2041 case 'o':
2042 fields = optarg;
2043 break;
2044 case 'p':
2045 parse = B_TRUE;
2046 oflags |= OFMT_PARSABLE;
2047 break;
2048 case 's':
2049 scope = optarg;
2050 break;
2051 case '?':
2052 errx(-1, "unknown option: -%c", optopt);
2053 case ':':
2054 errx(-1, "option -%c requires an argument", optopt);
2055 }
2056 }
2057
2058 if (!parse) {
2059 oflags |= OFMT_WRAP;
2060 }
2061
2062 if (parse && fields == NULL) {
2063 errx(-1, "parsable mode (-p) requires fields specified with "
2064 "-o");
2065 }
2066
2067 if (fields == NULL) {
2068 if (nll->nll_unimpl) {
2069 fields = nvmeadm_list_logs_fields_impl;
2070 } else {
2071 fields = nvmeadm_list_logs_fields;
2072 }
2073 }
2074
2075 if (scope != NULL) {
2076 const char *str;
2077
2078 while ((str = strsep(&scope, ",")) != NULL) {
2079 if (strcasecmp(str, "nvm") == 0) {
2080 nll->nll_scope |= NVME_LOG_SCOPE_NVM;
2081 } else if (strcasecmp(str, "ns") == 0 ||
2082 strcasecmp(str, "namespace") == 0) {
2083 nll->nll_scope |= NVME_LOG_SCOPE_NS;
2084 } else if (strcasecmp(str, "ctrl") == 0 ||
2085 strcasecmp(str, "controller") == 0) {
2086 nll->nll_scope |= NVME_LOG_SCOPE_CTRL;
2087 } else {
2088 errx(-1, "unknown scope string: '%s'; valid "
2089 "values are 'nvm', 'namespace', and "
2090 "'controller'", str);
2091 }
2092 }
2093 }
2094
2095 oferr = ofmt_open(fields, nvmeadm_list_logs_ofmt, oflags, 0,
2096 &npa->npa_ofmt);
2097 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
2098
2099 if (npa->npa_argc - optind > 1) {
2100 nll->nll_nfilts = npa->npa_argc - optind - 1;
2101 nll->nll_filts = npa->npa_argv + optind + 1;
2102 nll->nll_used = calloc(nll->nll_nfilts, sizeof (boolean_t));
2103 if (nll->nll_used == NULL) {
2104 err(-1, "failed to allocate memory for tracking log "
2105 "page filters");
2106 }
2107 }
2108 }
2109
2110 static void
usage_list_logs(const char * c_name)2111 usage_list_logs(const char *c_name)
2112 {
2113 (void) fprintf(stderr, "%s [-H] [-o field,[...] [-p]] [-s scope,[...]] "
2114 "[-a]\n\t <ctl>[/<ns>][,...] [logpage...]\n\n"
2115 " List log pages supported by controllers or namespaces.\n",
2116 c_name);
2117 }
2118
2119 static boolean_t
do_list_logs_match(const nvme_log_disc_t * disc,nvmeadm_list_logs_t * nll)2120 do_list_logs_match(const nvme_log_disc_t *disc, nvmeadm_list_logs_t *nll)
2121 {
2122 if (!nll->nll_unimpl && !nvme_log_disc_impl(disc)) {
2123 return (B_FALSE);
2124 }
2125
2126 if (nll->nll_nfilts <= 0) {
2127 return (B_TRUE);
2128 }
2129
2130 /*
2131 * Check all filters. If we have multiple specified that would match we
2132 * want that to count.
2133 */
2134 boolean_t match = B_FALSE;
2135 for (int i = 0; i < nll->nll_nfilts; i++) {
2136 if (strcmp(nvme_log_disc_name(disc), nll->nll_filts[i]) == 0) {
2137 nll->nll_used[i] = B_TRUE;
2138 match = B_TRUE;
2139 continue;
2140 }
2141
2142 size_t naliases = nvme_log_disc_naliases(disc);
2143 const char *const *aliases = nvme_log_disc_aliases(disc);
2144 for (size_t a = 0; a < naliases; a++) {
2145 if (strcmp(aliases[a], nll->nll_filts[i]) == 0) {
2146 nll->nll_used[i] = B_TRUE;
2147 match = B_TRUE;
2148 break;
2149 }
2150 }
2151 }
2152
2153 return (match);
2154 }
2155
2156 static int
do_list_logs(const nvme_process_arg_t * npa)2157 do_list_logs(const nvme_process_arg_t *npa)
2158 {
2159 nvme_log_disc_scope_t scope;
2160 nvme_log_iter_t *iter;
2161 nvme_iter_t ret;
2162 const nvme_log_disc_t *disc;
2163 nvmeadm_list_logs_t *nll = npa->npa_cmd_arg;
2164
2165 if (nll->nll_scope != 0) {
2166 scope = nll->nll_scope;
2167 } else if (npa->npa_ns != NULL) {
2168 scope = NVME_LOG_SCOPE_NS;
2169 } else {
2170 scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM;
2171 }
2172
2173 if (!nvme_log_discover_init(npa->npa_ctrl, scope, 0, &iter)) {
2174 nvmeadm_warn(npa, "failed to iterate logs on %s",
2175 npa->npa_ctrl_name);
2176 return (-1);
2177 }
2178
2179 while ((ret = nvme_log_discover_step(iter, &disc)) == NVME_ITER_VALID) {
2180 if (do_list_logs_match(disc, nll)) {
2181 nvmeadm_list_logs_ofmt_arg_t print;
2182
2183 print.nlloa_name = npa->npa_name;
2184 print.nlloa_disc = disc;
2185 ofmt_print(npa->npa_ofmt, &print);
2186 nll->nll_nprint++;
2187 }
2188 }
2189
2190 nvme_log_discover_fini(iter);
2191 if (ret == NVME_ITER_ERROR) {
2192 nvmeadm_warn(npa, "failed to iterate logs on %s",
2193 npa->npa_ctrl_name);
2194 return (-1);
2195 }
2196
2197 for (int i = 0; i < nll->nll_nfilts; i++) {
2198 if (!nll->nll_used[i]) {
2199 warnx("log page filter '%s' did match any log pages",
2200 nll->nll_filts[i]);
2201 exitcode = -1;
2202 }
2203 }
2204
2205 if (nll->nll_nprint == 0) {
2206 if (nll->nll_nfilts == 0) {
2207 warnx("no log pages found for %s", npa->npa_name);
2208 }
2209 exitcode = -1;
2210 }
2211
2212 return (exitcode);
2213 }
2214
2215 static void
usage_get_logpage(const char * c_name)2216 usage_get_logpage(const char *c_name)
2217 {
2218 (void) fprintf(stderr, "%s [-O file | -x | -p -o field,[...] [-H]]\n"
2219 "\t <ctl>[/<ns>][,...] <logpage> [filter...]\n\n"
2220 " Print the specified log page of the specified NVMe "
2221 "controllers and/or name-\n spaces. Run \"nvmeadm list-logpages\" "
2222 "for supported log pages. All devices\n support error, health, "
2223 "and firmware.\n", c_name);
2224 }
2225
2226 static void
usage_firmware_list(const char * c_name)2227 usage_firmware_list(const char *c_name)
2228 {
2229 (void) fprintf(stderr, "%s <ctl>\n\n"
2230 " Print the log page that contains the list of firmware "
2231 "images installed on the specified NVMe controller.\n", c_name);
2232 }
2233
2234 static uint64_t
do_get_logpage_size(const nvme_process_arg_t * npa,nvme_log_disc_t * disc,nvme_log_req_t * req)2235 do_get_logpage_size(const nvme_process_arg_t *npa, nvme_log_disc_t *disc,
2236 nvme_log_req_t *req)
2237 {
2238 uint64_t len, ret;
2239 void *buf;
2240 nvme_log_size_kind_t kind;
2241
2242 kind = nvme_log_disc_size(disc, &len);
2243 if (kind != NVME_LOG_SIZE_K_VAR) {
2244 return (len);
2245 }
2246
2247 /*
2248 * We have a log with a variable length size. To determine the actual
2249 * size we must actually determine the full length of this.
2250 */
2251 if ((buf = malloc(len)) == NULL) {
2252 errx(-1, "failed to allocate %zu byte buffer to get log "
2253 "page size", len);
2254 }
2255
2256 if (!nvme_log_req_set_output(req, buf, len)) {
2257 nvmeadm_fatal(npa, "failed to set output parameters to "
2258 "determine log length");
2259 }
2260
2261 if (!nvme_log_req_exec(req)) {
2262 nvmeadm_fatal(npa, "failed to execute log request %s to "
2263 "determine log length", npa->npa_argv[0]);
2264 }
2265
2266 if (!nvme_log_disc_calc_size(disc, &ret, buf, len)) {
2267 errx(-1, "failed to determine full %s log length",
2268 npa->npa_argv[0]);
2269 }
2270
2271 free(buf);
2272 return (ret);
2273 }
2274
2275 static void
do_get_logpage_dump(const void * buf,size_t len,const char * file)2276 do_get_logpage_dump(const void *buf, size_t len, const char *file)
2277 {
2278 size_t off = 0;
2279 int fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
2280
2281 if (fd < 0) {
2282 err(-1, "failed to create output file %s", file);
2283 }
2284
2285 while (len > 0) {
2286 ssize_t ret = write(fd, buf + off, len - off);
2287 if (ret < 0) {
2288 err(EXIT_FAILURE, "failed to write log data to file %s "
2289 "at offset %zu", file, off);
2290 }
2291
2292 off += (size_t)ret;
2293 len -= (size_t)ret;
2294 }
2295
2296 (void) close(fd);
2297 }
2298
2299 /*
2300 * Here we need to explicitly attempt to release any context that has previously
2301 * existed for the persistent event log. It is fine if none exists as the
2302 * controller is required not to error. However, if we don't do this and attempt
2303 * to establish a new context, then it will generate an error.
2304 *
2305 * We'll use our existing request, which doesn't ask for data yet and issue the
2306 * get log page request with the LSP in question. After it is completed, we'll
2307 * reset the LSP to establish a context.
2308 */
2309 static void
do_get_logpage_pev_relctx(const nvme_process_arg_t * npa,nvme_log_req_t * req)2310 do_get_logpage_pev_relctx(const nvme_process_arg_t *npa, nvme_log_req_t *req)
2311 {
2312 uint32_t buf;
2313
2314 if (!nvme_log_req_set_lsp(req, NVME_PEV_LSP_REL_CTX)) {
2315 nvmeadm_fatal(npa, "failed to set lsp to release the "
2316 "persistent event log context");
2317 }
2318
2319 /*
2320 * In NVMe 2.0 the spec made it explicit that the controller was
2321 * supposed to ignore the length and offset for a request to release the
2322 * context; however, that wasn't present in NVMe 1.4. The number of
2323 * dwords part of the get log page command is a zeros based value,
2324 * meaning there is no explicit way to request zero bytes. Rather than
2325 * trust that all controllers get this right (especially when it wasn't
2326 * exactly specified in NVMe 1.4), we just toss a throwaway buffer here.
2327 */
2328 if (!nvme_log_req_set_output(req, &buf, sizeof (buf))) {
2329 nvmeadm_fatal(npa, "failed to set zero log length for "
2330 "persistent event log release context");
2331 }
2332
2333 if (!nvme_log_req_exec(req)) {
2334 nvmeadm_fatal(npa, "failed to execute log request %s to "
2335 "release the event log context", npa->npa_argv[0]);
2336 }
2337
2338 if (!nvme_log_req_set_lsp(req, NVME_PEV_LSP_EST_CTX_READ)) {
2339 nvmeadm_fatal(npa, "failed to set lsp to establish the "
2340 "persistent event log context");
2341 }
2342
2343 /*
2344 * Make sure that our stack buffer is no longer part of the log request.
2345 */
2346 if (!nvme_log_req_clear_output(req)) {
2347 nvmeadm_fatal(npa, "failed to clear output from persistent "
2348 "event log release context");
2349 }
2350 }
2351
2352 /*
2353 * Fill in the baseline context for a phyeye request. In this case we always
2354 * perform a normal read command and set the quality to "good" (the
2355 * lowest quality). Configurable use of this is driven through the phyeye
2356 * commands in nvmeadm_phyeye.c. In addition, we must always set the controller
2357 * ID in this request to our own.
2358 */
2359 static void
do_get_logpage_phyeye_ctx(const nvme_process_arg_t * npa,nvme_log_req_t * req)2360 do_get_logpage_phyeye_ctx(const nvme_process_arg_t *npa, nvme_log_req_t *req)
2361 {
2362 nvme_eom_lsp_t lsp;
2363
2364 (void) memset(&lsp, 0, sizeof (lsp));
2365 lsp.nel_mqual = NVME_EOM_LSP_MQUAL_GOOD;
2366 lsp.nel_act = NVME_EOM_LSP_READ;
2367
2368 if (!nvme_log_req_set_lsp(req, lsp.r)) {
2369 nvmeadm_fatal(npa, "failed to set lsp for host phyeye");
2370 }
2371
2372 if (!nvme_log_req_set_lsi(req, npa->npa_idctl->id_cntlid)) {
2373 nvmeadm_fatal(npa, "failed to set lsi for host phyeye");
2374 }
2375 }
2376
2377 static int
do_get_logpage_common(const nvme_process_arg_t * npa,const char * page,nvmeadm_field_filt_t * filts,size_t nfilts)2378 do_get_logpage_common(const nvme_process_arg_t *npa, const char *page,
2379 nvmeadm_field_filt_t *filts, size_t nfilts)
2380 {
2381 int ret = 0;
2382 nvme_log_disc_t *disc;
2383 nvme_log_req_t *req;
2384 nvme_log_disc_scope_t scope;
2385 void *buf;
2386 size_t toalloc;
2387 nvmeadm_get_logpage_t *log = npa->npa_cmd_arg;
2388
2389 /*
2390 * If we have enough information to identify a log-page via libnvme (or
2391 * in the future take enough options to allow us to actually do this
2392 * manually), then we will fetch it. If we don't know how to print it,
2393 * then we'll just hex dump it for now.
2394 */
2395 if (!nvme_log_req_init_by_name(npa->npa_ctrl, page, 0, &disc, &req)) {
2396 nvmeadm_fatal(npa, "could not initialize log request for %s",
2397 page);
2398 }
2399
2400 if (npa->npa_ns != NULL) {
2401 scope = NVME_LOG_SCOPE_NS;
2402 } else {
2403 scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NVM;
2404 }
2405
2406 if ((scope & nvme_log_disc_scopes(disc)) == 0) {
2407 errx(-1, "log page %s does not support operating on %s", page,
2408 npa->npa_ns != NULL ? "namespaces" : "controllers");
2409 }
2410
2411 /*
2412 * In the future we should add options to allow one to specify and set
2413 * the fields for the lsp, lsi, etc. and set them here. Some log pages
2414 * need a specific lsp set and special handling related to contexts. Do
2415 * that now.
2416 */
2417 switch (nvme_log_disc_lid(disc)) {
2418 case NVME_LOGPAGE_PEV:
2419 do_get_logpage_pev_relctx(npa, req);
2420 break;
2421 case NVME_LOGPAGE_TELMHOST:
2422 return (do_get_logpage_telemetry(npa, disc, req));
2423 case NVME_LOGPAGE_PHYEYE:
2424 do_get_logpage_phyeye_ctx(npa, req);
2425 break;
2426 default:
2427 break;
2428 }
2429
2430 if (npa->npa_ns != NULL) {
2431 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
2432
2433 if (!nvme_log_req_set_nsid(req, nsid)) {
2434 nvmeadm_fatal(npa, "failed to set log request "
2435 "namespace ID to 0x%x", nsid);
2436 }
2437 }
2438
2439 /*
2440 * The output size should be the last thing that we determine as we may
2441 * need to issue a log request to figure out how much data we should
2442 * actually be reading.
2443 */
2444 toalloc = do_get_logpage_size(npa, disc, req);
2445 buf = malloc(toalloc);
2446 if (buf == NULL) {
2447 err(-1, "failed to allocate %zu bytes for log "
2448 "request %s", toalloc, page);
2449 }
2450
2451 if (!nvme_log_req_set_output(req, buf, toalloc)) {
2452 nvmeadm_fatal(npa, "failed to set output parameters");
2453 }
2454
2455 /*
2456 * Again, we need to potentially adjust specific LSP values here for the
2457 * various contexts that exist. Note that we are reusing the existing
2458 * request so if the prior values for the initial request are fine, then
2459 * we can just leave it there.
2460 */
2461 switch (nvme_log_disc_lid(disc)) {
2462 case NVME_LOGPAGE_PEV:
2463 if (!nvme_log_req_set_lsp(req, NVME_PEV_LSP_READ)) {
2464 nvmeadm_fatal(npa, "failed to set lsp to read the "
2465 "persistent event log");
2466 }
2467 break;
2468 default:
2469 break;
2470 }
2471
2472 if (!nvme_log_req_exec(req)) {
2473 nvmeadm_fatal(npa, "failed to execute log request %s",
2474 npa->npa_argv[0]);
2475 }
2476
2477 if (log != NULL && log->ngl_output != NULL) {
2478 do_get_logpage_dump(buf, toalloc, log->ngl_output);
2479 goto done;
2480 } else if (log != NULL && log->ngl_hex) {
2481 nvmeadm_dump_hex(buf, toalloc);
2482 goto done;
2483 }
2484
2485 if (npa->npa_ofmt == NULL) {
2486 (void) printf("%s: ", npa->npa_name);
2487 }
2488 if (strcmp(page, "error") == 0) {
2489 size_t nlog = toalloc / sizeof (nvme_error_log_entry_t);
2490 if (nfilts > 0) {
2491 warnx("log filters are not currently supported with "
2492 "the %s log page", page);
2493 ret = -1;
2494 }
2495
2496 nvme_print_error_log(nlog, buf, npa->npa_version);
2497 } else if (strcmp(page, "health") == 0) {
2498 if (nfilts > 0) {
2499 warnx("log filters are not currently supported with "
2500 "the %s log page", page);
2501 ret = -1;
2502 }
2503
2504 nvme_print_health_log(buf, npa->npa_idctl, npa->npa_version);
2505 } else if (strcmp(page, "firmware") == 0) {
2506 if (nfilts > 0) {
2507 warnx("log filters are not currently supported with "
2508 "the %s log page", page);
2509 ret = -1;
2510 }
2511
2512 nvme_print_fwslot_log(buf, npa->npa_idctl);
2513 } else {
2514 if (npa->npa_ofmt == NULL) {
2515 (void) printf("%s (%s)\n", nvme_log_disc_desc(disc),
2516 page);
2517 }
2518 if (!nvmeadm_log_page_fields(npa, page, buf, toalloc, filts,
2519 nfilts, 0)) {
2520 ret = -1;
2521 }
2522 }
2523
2524 done:
2525 free(buf);
2526 nvme_log_disc_free(disc);
2527 nvme_log_req_fini(req);
2528
2529 return (ret);
2530 }
2531
2532 static int
do_get_logpage_fwslot(const nvme_process_arg_t * npa)2533 do_get_logpage_fwslot(const nvme_process_arg_t *npa)
2534 {
2535 if (npa->npa_argc >= 1) {
2536 warnx("no additional arguments may be specified to %s",
2537 npa->npa_cmd->c_name);
2538 usage(npa->npa_cmd);
2539 exit(-1);
2540 }
2541
2542 return (do_get_logpage_common(npa, "firmware", NULL, 0));
2543 }
2544
2545 static void
optparse_get_logpage(nvme_process_arg_t * npa)2546 optparse_get_logpage(nvme_process_arg_t *npa)
2547 {
2548 int c;
2549 const char *fields = NULL;
2550 bool parse = false;
2551 uint_t oflags = 0;
2552 nvmeadm_get_logpage_t *log;
2553 uint_t count = 0;
2554
2555 if ((log = calloc(1, sizeof (nvmeadm_get_logpage_t))) == NULL) {
2556 err(-1, "failed to allocate memory to track log page "
2557 "information");
2558 }
2559
2560 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":Ho:O:px")) != -1) {
2561 switch (c) {
2562 case 'H':
2563 oflags |= OFMT_NOHEADER;
2564 break;
2565 case 'o':
2566 fields = optarg;
2567 break;
2568 case 'O':
2569 log->ngl_output = optarg;
2570 count++;
2571 break;
2572 case 'p':
2573 parse = true;
2574 oflags |= OFMT_PARSABLE;
2575 count++;
2576 break;
2577 case 'x':
2578 log->ngl_hex = true;
2579 count++;
2580 break;
2581 case '?':
2582 errx(-1, "unknown option: -%c", optopt);
2583 case ':':
2584 errx(-1, "option -%c requires an argument", optopt);
2585 }
2586 }
2587
2588 if (parse && fields == NULL) {
2589 errx(-1, "parsable mode (-p) requires fields specified with "
2590 "-o");
2591 }
2592
2593 if (!parse && fields != NULL) {
2594 errx(-1, "output field selection (-o) only usable in "
2595 "parsable mode (-p)");
2596 }
2597
2598 if (!parse && (oflags & OFMT_NOHEADER) != 0) {
2599 errx(-1, "omitting headers (-H) only usable in parsable mode "
2600 "(-p)");
2601 }
2602
2603 if (count > 1) {
2604 errx(-1, "only one of parsable output (-p), outputing to a "
2605 "file (-O), and hexadecimal output (-x) may be requested");
2606 }
2607
2608 if (parse) {
2609 ofmt_status_t oferr = ofmt_open(fields, nvmeadm_field_ofmt,
2610 oflags, 0, &npa->npa_ofmt);
2611 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
2612 }
2613
2614 npa->npa_cmd_arg = log;
2615 }
2616
2617 static void
do_logpage_filts(const nvme_process_arg_t * npa,nvmeadm_field_filt_t ** filtsp,size_t * nfiltsp)2618 do_logpage_filts(const nvme_process_arg_t *npa, nvmeadm_field_filt_t **filtsp,
2619 size_t *nfiltsp)
2620 {
2621 size_t nfilts;
2622 nvmeadm_field_filt_t *filts;
2623
2624 *filtsp = NULL;
2625 *nfiltsp = 0;
2626
2627 if (npa->npa_argc <= 1)
2628 return;
2629
2630 nfilts = (size_t)npa->npa_argc - 1;
2631 filts = calloc(nfilts, sizeof (nvmeadm_field_filt_t));
2632 if (filts == NULL) {
2633 err(-1, "failed to allocate memory for filter "
2634 "tracking");
2635 }
2636
2637 for (size_t i = 0; i < nfilts; i++) {
2638 filts[i].nff_len = strlen(npa->npa_argv[i + 1]);
2639 filts[i].nff_str = npa->npa_argv[i + 1];
2640 }
2641
2642 *filtsp = filts;
2643 *nfiltsp = nfilts;
2644 }
2645
2646 static int
do_get_logpage(const nvme_process_arg_t * npa)2647 do_get_logpage(const nvme_process_arg_t *npa)
2648 {
2649 size_t nfilts = 0;
2650 nvmeadm_field_filt_t *filts = NULL;
2651 nvmeadm_get_logpage_t *log = npa->npa_cmd_arg;
2652
2653 if (npa->npa_argc < 1) {
2654 warnx("missing log page name");
2655 usage(npa->npa_cmd);
2656 exit(-1);
2657 }
2658
2659 do_logpage_filts(npa, &filts, &nfilts);
2660
2661 if (log != NULL && log->ngl_hex && nfilts > 0) {
2662 errx(-1, "hexadecimal output (-x) cannot be used with "
2663 "log filter operands");
2664 }
2665
2666 return (do_get_logpage_common(npa, npa->npa_argv[0], filts, nfilts));
2667 }
2668
2669 static void
usage_print_logpage(const char * c_name)2670 usage_print_logpage(const char *c_name)
2671 {
2672 (void) fprintf(stderr, "%s -f file [-x | -p -o field,[...] [-H]] "
2673 "<logpage>\n\t [filter...]\n\n"
2674 " Print the specified log page from a file. Optional filters "
2675 "may be used to\n restrict the log fields printed. See nvmeadm(8) "
2676 "for a list of all log\n pages. Run \"nvmeadm list-logpages\" to "
2677 "see log pages a specific controller\n supports.\n", c_name);
2678 }
2679
2680 typedef struct {
2681 const char *pl_input;
2682 bool pl_hex;
2683 } print_logpages_t;
2684
2685 static void
optparse_print_logpage(nvme_process_arg_t * npa)2686 optparse_print_logpage(nvme_process_arg_t *npa)
2687 {
2688 int c;
2689 const char *fields = NULL;
2690 bool parse = false;
2691 uint_t oflags = 0;
2692 print_logpages_t *pl;
2693
2694 if ((pl = calloc(1, sizeof (print_logpages_t))) == NULL) {
2695 err(-1, "failed to allocate memory for option tracking");
2696 }
2697
2698 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":f:Ho:px")) != -1) {
2699 switch (c) {
2700 case 'f':
2701 pl->pl_input = optarg;
2702 break;
2703 case 'H':
2704 oflags |= OFMT_NOHEADER;
2705 break;
2706 case 'o':
2707 fields = optarg;
2708 break;
2709 case 'p':
2710 parse = true;
2711 oflags |= OFMT_PARSABLE;
2712 break;
2713 case 'x':
2714 pl->pl_hex = true;
2715 break;
2716 case '?':
2717 errx(-1, "unknown option: -%c", optopt);
2718 case ':':
2719 errx(-1, "option -%c requires an argument", optopt);
2720 }
2721 }
2722
2723 if (pl->pl_input == NULL) {
2724 errx(-1, "missing required input file to process (-f)");
2725 }
2726
2727 if (parse && fields == NULL) {
2728 errx(-1, "parsable mode (-p) requires fields specified with "
2729 "-o");
2730 }
2731
2732 if (!parse && fields != NULL) {
2733 errx(-1, "output field selection (-o) only usable in "
2734 "parsable mode (-p)");
2735 }
2736
2737 if (!parse && (oflags & OFMT_NOHEADER) != 0) {
2738 errx(-1, "omitting headers (-H) only usable in parsable mode "
2739 "(-p)");
2740 }
2741
2742 if (pl->pl_hex && parse) {
2743 errx(-1, "only one of parsable output (-p) and heaxdecimal "
2744 "output (-x) may be requested");
2745 }
2746
2747 if (parse) {
2748 ofmt_status_t oferr = ofmt_open(fields, nvmeadm_field_ofmt,
2749 oflags, 0, &npa->npa_ofmt);
2750 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
2751 }
2752
2753 npa->npa_cmd_arg = pl;
2754 }
2755
2756 static int
do_print_logpage(const nvme_process_arg_t * npa)2757 do_print_logpage(const nvme_process_arg_t *npa)
2758 {
2759 int fd = -1, ret = 0;
2760 struct stat st;
2761 void *data;
2762 const char *logpage = NULL;
2763 size_t nfilts = 0;
2764 nvmeadm_field_filt_t *filts = NULL;
2765 print_logpages_t *pl = npa->npa_cmd_arg;
2766 nvmeadm_log_field_flag_t flag = 0;
2767
2768 if (pl->pl_hex) {
2769 if (npa->npa_argc != 0) {
2770 errx(-1, "log page and filters not supported with -x");
2771 }
2772 } else {
2773 if (npa->npa_argc == 0) {
2774 errx(-1, "missing required log page");
2775 }
2776
2777 logpage = npa->npa_argv[0];
2778 do_logpage_filts(npa, &filts, &nfilts);
2779 flag |= NVMEADM_LFF_CHECK_NAME;
2780 }
2781
2782 if ((fd = open(pl->pl_input, O_RDONLY)) < 0) {
2783 err(-1, "failed to open input file %s", pl->pl_input);
2784 }
2785
2786 if (fstat(fd, &st) != 0) {
2787 err(-1, "failed to stat %s", pl->pl_input);
2788 }
2789
2790 if (st.st_size > NVMEADM_MAX_MMAP) {
2791 errx(-1, "%s file size of 0x%lx exceeds maximum allowed size "
2792 "of 0x%llx", pl->pl_input, st.st_size, NVMEADM_MAX_MMAP);
2793 }
2794
2795 data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
2796 if (data == MAP_FAILED) {
2797 errx(-1, "failed to mmap %s", pl->pl_input);
2798 }
2799
2800 if (!nvmeadm_log_page_fields(npa, logpage, data, st.st_size,
2801 filts, nfilts, flag)) {
2802 ret = -1;
2803 }
2804
2805 VERIFY0(munmap(data, st.st_size));
2806 VERIFY0(close(fd));
2807 free(filts);
2808
2809 return (ret);
2810 }
2811
2812 static void
optparse_list_features(nvme_process_arg_t * npa)2813 optparse_list_features(nvme_process_arg_t *npa)
2814 {
2815 int c;
2816 uint_t oflags = 0;
2817 boolean_t parse = B_FALSE;
2818 const char *fields = NULL;
2819 nvmeadm_features_t *feat;
2820 ofmt_status_t oferr;
2821
2822 if ((feat = calloc(1, sizeof (nvmeadm_features_t))) == NULL) {
2823 err(-1, "failed to allocate memory to track feature "
2824 "information");
2825 }
2826
2827 npa->npa_cmd_arg = feat;
2828
2829 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":aHo:p")) != -1) {
2830 switch (c) {
2831 case 'a':
2832 feat->nf_unimpl = B_TRUE;
2833 break;
2834 case 'H':
2835 oflags |= OFMT_NOHEADER;
2836 break;
2837 case 'o':
2838 fields = optarg;
2839 break;
2840 case 'p':
2841 parse = B_TRUE;
2842 oflags |= OFMT_PARSABLE;
2843 break;
2844 case '?':
2845 errx(-1, "unknown option: -%c", optopt);
2846 case ':':
2847 errx(-1, "option -%c requires an argument", optopt);
2848 }
2849 }
2850
2851 if (!parse) {
2852 oflags |= OFMT_WRAP;
2853 }
2854
2855 if (parse && fields == NULL) {
2856 errx(-1, "parsable mode (-p) requires fields specified with "
2857 "-o");
2858 }
2859
2860 if (fields == NULL) {
2861 fields = nvmeadm_list_features_fields;
2862 }
2863
2864 oferr = ofmt_open(fields, nvmeadm_list_features_ofmt, oflags, 0,
2865 &npa->npa_ofmt);
2866 ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx);
2867
2868 if (npa->npa_argc - optind > 1) {
2869 feat->nf_nfilts = (uint32_t)(npa->npa_argc - optind - 1);
2870 feat->nf_filts = npa->npa_argv + optind + 1;
2871 feat->nf_used = calloc(feat->nf_nfilts, sizeof (boolean_t));
2872 if (feat->nf_used == NULL) {
2873 err(-1, "failed to allocate memory for tracking "
2874 "feature filters");
2875 }
2876 }
2877 }
2878
2879 static void
usage_list_features(const char * c_name)2880 usage_list_features(const char *c_name)
2881 {
2882 (void) fprintf(stderr, "%s [-a] [-H] [-o field,[...] [-p]] "
2883 "<ctl>[/<ns>][,...]\n\t [feature...]\n\n"
2884 " List features supported by controllers or namespaces.\n",
2885 c_name);
2886 }
2887
2888 static boolean_t
do_features_match(const nvme_feat_disc_t * disc,nvmeadm_features_t * nf)2889 do_features_match(const nvme_feat_disc_t *disc, nvmeadm_features_t *nf)
2890 {
2891 if (nf->nf_nfilts == 0) {
2892 return (B_TRUE);
2893 }
2894
2895 for (uint32_t i = 0; i < nf->nf_nfilts; i++) {
2896 const char *match = nf->nf_filts[i];
2897 long long fid;
2898 const char *err;
2899
2900 if (strcmp(nvme_feat_disc_short(disc), match) == 0 ||
2901 strcasecmp(nvme_feat_disc_spec(disc), match) == 0) {
2902 nf->nf_used[i] = B_TRUE;
2903 return (B_TRUE);
2904 }
2905
2906 fid = strtonumx(match, 0, UINT32_MAX, &err, 0);
2907 if (err == NULL && fid == nvme_feat_disc_fid(disc)) {
2908 nf->nf_used[i] = B_TRUE;
2909 return (B_TRUE);
2910 }
2911 }
2912
2913 return (B_FALSE);
2914 }
2915
2916
2917 /*
2918 * This is a common entry point for both list-features and get-features, which
2919 * iterate over all features and take action for each one.
2920 */
2921 typedef void (*do_features_cb_f)(const nvme_process_arg_t *,
2922 const nvme_feat_disc_t *);
2923 static int
do_features(const nvme_process_arg_t * npa,nvmeadm_features_t * nf,do_features_cb_f func)2924 do_features(const nvme_process_arg_t *npa, nvmeadm_features_t *nf,
2925 do_features_cb_f func)
2926 {
2927 nvme_feat_scope_t scope;
2928 nvme_feat_iter_t *iter;
2929 nvme_iter_t ret;
2930 const nvme_feat_disc_t *disc;
2931
2932 if (npa->npa_ns != NULL) {
2933 scope = NVME_FEAT_SCOPE_NS;
2934 } else {
2935 scope = NVME_FEAT_SCOPE_CTRL | NVME_FEAT_SCOPE_NVM;
2936 }
2937
2938 if (!nvme_feat_discover_init(npa->npa_ctrl, scope, 0, &iter)) {
2939 nvmeadm_warn(npa, "failed to iterate features on %s",
2940 npa->npa_ctrl_name);
2941 return (-1);
2942 }
2943
2944 while ((ret = nvme_feat_discover_step(iter, &disc)) ==
2945 NVME_ITER_VALID) {
2946 if (do_features_match(disc, nf)) {
2947 if (!nf->nf_unimpl && nvme_feat_disc_impl(disc) ==
2948 NVME_FEAT_IMPL_UNSUPPORTED) {
2949 continue;
2950 }
2951
2952 func(npa, disc);
2953 nf->nf_nprint++;
2954 }
2955 }
2956
2957 nvme_feat_discover_fini(iter);
2958 if (ret == NVME_ITER_ERROR) {
2959 nvmeadm_warn(npa, "failed to iterate features on %s",
2960 npa->npa_ctrl_name);
2961 return (-1);
2962 }
2963
2964 for (uint32_t i = 0; i < nf->nf_nfilts; i++) {
2965 if (!nf->nf_used[i]) {
2966 warnx("feature filter '%s' did match any features",
2967 nf->nf_filts[i]);
2968 exitcode = -1;
2969 }
2970 }
2971
2972 if (nf->nf_nprint == 0) {
2973 if (nf->nf_nfilts == 0) {
2974 warnx("no features found for %s", npa->npa_name);
2975 }
2976 exitcode = -1;
2977 }
2978
2979 return (exitcode);
2980 }
2981
2982 static void
do_list_features_cb(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)2983 do_list_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc)
2984 {
2985 nvmeadm_list_features_ofmt_arg_t print;
2986
2987 print.nlfoa_name = npa->npa_name;
2988 print.nlfoa_feat = disc;
2989 ofmt_print(npa->npa_ofmt, &print);
2990 }
2991
2992 static int
do_list_features(const nvme_process_arg_t * npa)2993 do_list_features(const nvme_process_arg_t *npa)
2994 {
2995 nvmeadm_features_t *nf = npa->npa_cmd_arg;
2996
2997 return (do_features(npa, nf, do_list_features_cb));
2998 }
2999
3000 static void
usage_get_features(const char * c_name)3001 usage_get_features(const char *c_name)
3002 {
3003 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
3004 " Print the specified features of the specified NVMe controllers "
3005 "and/or\n namespaces. Feature support varies on the controller.\n"
3006 "Run \"nvmeadm list-features <ctl>\" to see supported features.\n",
3007 c_name);
3008 }
3009
3010 /*
3011 * The nvmeadm(8) get-features output has traditionally swallowed certain errors
3012 * for features that it considers unimplemented in tandem with the kernel. With
3013 * the introduction of libnvme and ioctl interface changes, the kernel no longer
3014 * caches information about features that are unimplemented.
3015 *
3016 * There are two cases that we currently swallow errors on and the following
3017 * must all be true:
3018 *
3019 * 1) We have a controller error.
3020 * 2) The system doesn't know whether the feature is implemented or not.
3021 * 3) The controller error indicates that we have an invalid field.
3022 *
3023 * There is one additional wrinkle that we are currently papering over due to
3024 * the history of nvmeadm swallowing errors. The error recovery feature was made
3025 * explicitly namespace-specific in NVMe 1.4. However, various NVMe 1.3 devices
3026 * will error if we ask for it without specifying a namespace. Conversely, older
3027 * devices will be upset if you do ask for a namespace. This case can be removed
3028 * once we better survey devices and come up with a heuristic for how to handle
3029 * this across older generations.
3030 *
3031 * If we add a single feature endpoint that gives flexibility over how the
3032 * feature are listed, then we should not swallow errors.
3033 */
3034 static boolean_t
swallow_get_feat_err(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)3035 swallow_get_feat_err(const nvme_process_arg_t *npa,
3036 const nvme_feat_disc_t *disc)
3037 {
3038 uint32_t sct, sc;
3039
3040 if (nvme_ctrl_err(npa->npa_ctrl) != NVME_ERR_CONTROLLER) {
3041 return (B_FALSE);
3042 }
3043
3044 nvme_ctrl_deverr(npa->npa_ctrl, &sct, &sc);
3045 if (nvme_feat_disc_impl(disc) == NVME_FEAT_IMPL_UNKNOWN &&
3046 sct == NVME_CQE_SCT_GENERIC && sc == NVME_CQE_SC_GEN_INV_FLD) {
3047 return (B_TRUE);
3048 }
3049
3050 if (nvme_feat_disc_fid(disc) == NVME_FEAT_ERROR &&
3051 sct == NVME_CQE_SCT_GENERIC && (sc == NVME_CQE_SC_GEN_INV_FLD ||
3052 sc == NVME_CQE_SC_GEN_INV_NS)) {
3053 return (B_TRUE);
3054 }
3055
3056 return (B_FALSE);
3057 }
3058
3059 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)3060 do_get_feat_common(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc,
3061 uint32_t cdw11, uint32_t *cdw0, void **datap, size_t *lenp)
3062 {
3063 nvme_get_feat_req_t *req = NULL;
3064 void *data = NULL;
3065 uint64_t datalen = 0;
3066 nvme_get_feat_fields_t fields = nvme_feat_disc_fields_get(disc);
3067
3068 if (!nvme_get_feat_req_init_by_disc(npa->npa_ctrl, disc, &req)) {
3069 nvmeadm_warn(npa, "failed to initialize get feature request "
3070 "for feature %s", nvme_feat_disc_short(disc));
3071 exitcode = -1;
3072 goto err;
3073 }
3074
3075 if ((fields & NVME_GET_FEAT_F_CDW11) != 0 &&
3076 !nvme_get_feat_req_set_cdw11(req, cdw11)) {
3077 nvmeadm_warn(npa, "failed to set cdw11 to 0x%x for feature %s",
3078 cdw11, nvme_feat_disc_short(disc));
3079 exitcode = -1;
3080 goto err;
3081 }
3082
3083 if ((fields & NVME_GET_FEAT_F_DATA) != 0) {
3084 datalen = nvme_feat_disc_data_size(disc);
3085 VERIFY3U(datalen, !=, 0);
3086 data = malloc(datalen);
3087 if (data == NULL) {
3088 err(-1, "failed to allocate %zu bytes for feature %s "
3089 "data buffer", datalen, nvme_feat_disc_short(disc));
3090 }
3091
3092 if (!nvme_get_feat_req_set_output(req, data, datalen)) {
3093 nvmeadm_warn(npa, "failed to set output data for "
3094 "feature %s", nvme_feat_disc_short(disc));
3095 exitcode = -1;
3096 goto err;
3097 }
3098 }
3099
3100 if ((fields & NVME_GET_FEAT_F_NSID) != 0) {
3101 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
3102
3103 if (!nvme_get_feat_req_set_nsid(req, nsid)) {
3104 nvmeadm_warn(npa, "failed to set nsid to 0x%x for "
3105 "feature %s", nsid, nvme_feat_disc_spec(disc));
3106 exitcode = -1;
3107 goto err;
3108 }
3109 }
3110
3111 if (!nvme_get_feat_req_exec(req)) {
3112 if (!swallow_get_feat_err(npa, disc)) {
3113 nvmeadm_warn(npa, "failed to get feature %s",
3114 nvme_feat_disc_spec(disc));
3115 exitcode = -1;
3116 }
3117
3118 goto err;
3119 }
3120
3121 if (!nvme_get_feat_req_get_cdw0(req, cdw0)) {
3122 nvmeadm_warn(npa, "failed to get cdw0 result data for %s",
3123 nvme_feat_disc_spec(disc));
3124 goto err;
3125 }
3126
3127 *datap = data;
3128 *lenp = datalen;
3129 nvme_get_feat_req_fini(req);
3130 return (B_TRUE);
3131
3132 err:
3133 free(data);
3134 nvme_get_feat_req_fini(req);
3135 return (B_FALSE);
3136 }
3137
3138 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)3139 do_get_feat_temp_thresh_one(const nvme_process_arg_t *npa,
3140 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat,
3141 const char *label, uint16_t tmpsel, uint16_t thsel)
3142 {
3143 uint32_t cdw0;
3144 void *buf = NULL;
3145 size_t buflen;
3146 nvme_temp_threshold_t tt;
3147
3148 tt.r = 0;
3149 tt.b.tt_tmpsel = tmpsel;
3150 tt.b.tt_thsel = thsel;
3151
3152 /*
3153 * The printing function treats the buffer argument as the label to
3154 * print for this threshold.
3155 */
3156 if (!do_get_feat_common(npa, disc, tt.r, &cdw0, &buf, &buflen)) {
3157 return;
3158 }
3159
3160 feat->f_print(cdw0, (void *)label, 0, npa->npa_idctl,
3161 npa->npa_version);
3162 free(buf);
3163 }
3164
3165 /*
3166 * In NVMe 1.2, the specification allowed for up to 8 sensors to be on the
3167 * device and changed the main device to have a composite temperature sensor. As
3168 * a result, there is a set of thresholds for each sensor. In addition, they
3169 * added both an over-temperature and under-temperature threshold. Since most
3170 * devices don't actually implement all the sensors, we get the health page and
3171 * see which sensors have a non-zero value to determine how to proceed.
3172 */
3173 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)3174 do_get_feat_temp_thresh(const nvme_process_arg_t *npa,
3175 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat)
3176 {
3177 nvme_log_req_t *req = NULL;
3178 nvme_log_disc_t *log_disc = NULL;
3179 size_t toalloc;
3180 void *buf = NULL;
3181 boolean_t ret = B_FALSE;
3182 const nvme_health_log_t *hlog;
3183
3184 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
3185 do_get_feat_temp_thresh_one(npa, disc, feat,
3186 "Composite Over Temp. Threshold", 0, NVME_TEMP_THRESH_OVER);
3187
3188 if (!nvme_version_check(npa, &nvme_vers_1v2)) {
3189 return (B_TRUE);
3190 }
3191
3192 if (!nvme_log_req_init_by_name(npa->npa_ctrl, "health", 0, &log_disc,
3193 &req)) {
3194 nvmeadm_warn(npa, "failed to initialize health log page "
3195 "request");
3196 return (B_FALSE);
3197 }
3198
3199 toalloc = do_get_logpage_size(npa, log_disc, req);
3200 buf = malloc(toalloc);
3201 if (buf == NULL) {
3202 err(-1, "failed to allocate %zu bytes for health log page",
3203 toalloc);
3204 }
3205
3206 if (!nvme_log_req_set_output(req, buf, toalloc)) {
3207 nvmeadm_warn(npa, "failed to set output parameters for health "
3208 "log page");
3209 goto out;
3210 }
3211
3212 if (!nvme_log_req_exec(req)) {
3213 nvmeadm_warn(npa, "failed to retrieve the health log page");
3214 goto out;
3215 }
3216
3217 /* cast required to prove our intentionality to smatch */
3218 hlog = (const nvme_health_log_t *)buf;
3219
3220 do_get_feat_temp_thresh_one(npa, disc, feat,
3221 "Composite Under Temp. Threshold", 0, NVME_TEMP_THRESH_UNDER);
3222 if (hlog->hl_temp_sensor_1 != 0) {
3223 do_get_feat_temp_thresh_one(npa, disc, feat,
3224 "Temp. Sensor 1 Over Temp. Threshold", 1,
3225 NVME_TEMP_THRESH_OVER);
3226 do_get_feat_temp_thresh_one(npa, disc, feat,
3227 "Temp. Sensor 1 Under Temp. Threshold", 1,
3228 NVME_TEMP_THRESH_UNDER);
3229 }
3230
3231 if (hlog->hl_temp_sensor_2 != 0) {
3232 do_get_feat_temp_thresh_one(npa, disc, feat,
3233 "Temp. Sensor 2 Over Temp. Threshold", 2,
3234 NVME_TEMP_THRESH_OVER);
3235 do_get_feat_temp_thresh_one(npa, disc, feat,
3236 "Temp. Sensor 2 Under Temp. Threshold", 2,
3237 NVME_TEMP_THRESH_UNDER);
3238 }
3239
3240 if (hlog->hl_temp_sensor_3 != 0) {
3241 do_get_feat_temp_thresh_one(npa, disc, feat,
3242 "Temp. Sensor 3 Over Temp. Threshold", 3,
3243 NVME_TEMP_THRESH_OVER);
3244 do_get_feat_temp_thresh_one(npa, disc, feat,
3245 "Temp. Sensor 3 Under Temp. Threshold", 3,
3246 NVME_TEMP_THRESH_UNDER);
3247 }
3248
3249 if (hlog->hl_temp_sensor_4 != 0) {
3250 do_get_feat_temp_thresh_one(npa, disc, feat,
3251 "Temp. Sensor 4 Over Temp. Threshold", 4,
3252 NVME_TEMP_THRESH_OVER);
3253 do_get_feat_temp_thresh_one(npa, disc, feat,
3254 "Temp. Sensor 4 Under Temp. Threshold", 4,
3255 NVME_TEMP_THRESH_UNDER);
3256 }
3257
3258 if (hlog->hl_temp_sensor_5 != 0) {
3259 do_get_feat_temp_thresh_one(npa, disc, feat,
3260 "Temp. Sensor 5 Over Temp. Threshold", 5,
3261 NVME_TEMP_THRESH_OVER);
3262 do_get_feat_temp_thresh_one(npa, disc, feat,
3263 "Temp. Sensor 5 Under Temp. Threshold", 5,
3264 NVME_TEMP_THRESH_UNDER);
3265 }
3266
3267 if (hlog->hl_temp_sensor_6 != 0) {
3268 do_get_feat_temp_thresh_one(npa, disc, feat,
3269 "Temp. Sensor 6 Over Temp. Threshold", 6,
3270 NVME_TEMP_THRESH_OVER);
3271 do_get_feat_temp_thresh_one(npa, disc, feat,
3272 "Temp. Sensor 6 Under Temp. Threshold", 6,
3273 NVME_TEMP_THRESH_UNDER);
3274 }
3275
3276 if (hlog->hl_temp_sensor_7 != 0) {
3277 do_get_feat_temp_thresh_one(npa, disc, feat,
3278 "Temp. Sensor 7 Over Temp. Threshold", 7,
3279 NVME_TEMP_THRESH_OVER);
3280 do_get_feat_temp_thresh_one(npa, disc, feat,
3281 "Temp. Sensor 7 Under Temp. Threshold", 7,
3282 NVME_TEMP_THRESH_UNDER);
3283 }
3284
3285 if (hlog->hl_temp_sensor_8 != 0) {
3286 do_get_feat_temp_thresh_one(npa, disc, feat,
3287 "Temp. Sensor 8 Over Temp. Threshold", 8,
3288 NVME_TEMP_THRESH_OVER);
3289 do_get_feat_temp_thresh_one(npa, disc, feat,
3290 "Temp. Sensor 8 Under Temp. Threshold", 8,
3291 NVME_TEMP_THRESH_UNDER);
3292 }
3293
3294 ret = B_TRUE;
3295 out:
3296 nvme_log_req_fini(req);
3297 free(buf);
3298 return (ret);
3299 }
3300
3301 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)3302 do_get_feat_intr_vect(const nvme_process_arg_t *npa,
3303 const nvme_feat_disc_t *disc, const nvmeadm_feature_t *feat)
3304 {
3305 uint32_t nintrs;
3306 boolean_t ret = B_TRUE;
3307
3308 if (!nvme_ctrl_info_pci_nintrs(npa->npa_ctrl_info, &nintrs)) {
3309 nvmeadm_ctrl_info_warn(npa, "failed to get interrupt count "
3310 "from controller %s information snapshot", npa->npa_name);
3311 return (B_FALSE);
3312 }
3313
3314 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
3315 for (uint32_t i = 0; i < nintrs; i++) {
3316 uint32_t cdw0;
3317 void *buf;
3318 size_t buflen;
3319 nvme_intr_vect_t vect;
3320
3321 vect.r = 0;
3322 vect.b.iv_iv = i;
3323
3324 if (!do_get_feat_common(npa, disc, vect.r, &cdw0, &buf,
3325 &buflen)) {
3326 ret = B_FALSE;
3327 continue;
3328 }
3329
3330 feat->f_print(cdw0, buf, buflen, npa->npa_idctl,
3331 npa->npa_version);
3332 free(buf);
3333 }
3334
3335 return (ret);
3336 }
3337
3338 /*
3339 * We've been asked to print the following feature that the controller probably
3340 * supports. Find our internal feature information for this to see if we know
3341 * how to deal with it.
3342 */
3343 static void
do_get_features_cb(const nvme_process_arg_t * npa,const nvme_feat_disc_t * disc)3344 do_get_features_cb(const nvme_process_arg_t *npa, const nvme_feat_disc_t *disc)
3345 {
3346 const nvmeadm_feature_t *feat = NULL;
3347 const char *name = nvme_feat_disc_short(disc);
3348 nvme_get_feat_fields_t fields;
3349 void *data = NULL;
3350 size_t datalen = 0;
3351 uint32_t cdw0;
3352
3353 for (size_t i = 0; i < ARRAY_SIZE(features); i++) {
3354 if (strcmp(features[i].f_feature, name) == 0) {
3355 feat = &features[i];
3356 break;
3357 }
3358 }
3359
3360 /*
3361 * Determine if we have enough logic in here to get and print the
3362 * feature. The vast majority of NVMe features only output a single
3363 * uint32_t in cdw0 and potentially a data buffer. As long as no input
3364 * arguments are required, then we can go ahead and get this and print
3365 * the data. If there is, then we will refuse unless we have a
3366 * particular function. If we have a specific get function, we expect it
3367 * to do all the printing.
3368 */
3369 if (feat != NULL && feat->f_get != NULL) {
3370 if (!feat->f_get(npa, disc, feat)) {
3371 exitcode = -1;
3372 }
3373 return;
3374 }
3375
3376 fields = nvme_feat_disc_fields_get(disc);
3377 if ((fields & NVME_GET_FEAT_F_CDW11) != 0) {
3378 warnx("unable to get feature %s due to missing nvmeadm(8) "
3379 "implementation logic", nvme_feat_disc_spec(disc));
3380 exitcode = -1;
3381 return;
3382 }
3383
3384 /*
3385 * We do not set exitcode on failure here so that way we can swallow
3386 * errors from unimplemented features.
3387 */
3388 if (!do_get_feat_common(npa, disc, 0, &cdw0, &data, &datalen)) {
3389 return;
3390 }
3391
3392 nvme_print(2, nvme_feat_disc_spec(disc), -1, NULL);
3393 if (feat != NULL && feat->f_print != NULL) {
3394 feat->f_print(cdw0, data, datalen, npa->npa_idctl,
3395 npa->npa_version);
3396 } else {
3397 nvme_feat_output_t output = nvme_feat_disc_output_get(disc);
3398 nvme_print_feat_unknown(output, cdw0, data, datalen);
3399 }
3400
3401 free(data);
3402 }
3403
3404 /*
3405 * This is an entry point which prints every feature that we know about. We
3406 * often go to lengths to discover all the variable inputs that can be used for
3407 * a given feature that requires an argument in cdw11. Due to the semantics of
3408 * filtering being used for features and the need to print each feature, this is
3409 * not the place to add general field filtering or a means to request a specific
3410 * cdw11 argument or similar. Instead, a new get-feature which requires someone
3411 * to specify the short name for a feature and then allows particular fields to
3412 * be grabbed and arguments should be created instead.
3413 *
3414 * This uses the same general feature logic that underpins do_list_features()
3415 * and therefore we transform filter arguments into the same style used there.
3416 */
3417 static int
do_get_features(const nvme_process_arg_t * npa)3418 do_get_features(const nvme_process_arg_t *npa)
3419 {
3420 char *fstr = NULL;
3421 char **filts = NULL;
3422 boolean_t *used = NULL;
3423 nvmeadm_features_t nf;
3424 int ret;
3425
3426 if (npa->npa_argc > 1)
3427 errx(-1, "unexpected arguments");
3428
3429 if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) <
3430 NVME_NS_DISC_F_ACTIVE) {
3431 errx(-1, "cannot get feature: namespace is inactive");
3432 }
3433
3434 /*
3435 * We always leave nf_unimpl set to false as we don't want to bother
3436 * trying to print a feature that we know the device doesn't support.
3437 */
3438 (void) memset(&nf, 0, sizeof (nvmeadm_features_t));
3439
3440 /*
3441 * If we've been given a series of features to print, treat those as
3442 * filters on the features as we're walking them to determine which to
3443 * print or not.
3444 */
3445 if (npa->npa_argc == 1) {
3446 char *f;
3447 uint32_t i;
3448
3449 nf.nf_nfilts = 1;
3450 fstr = strdup(npa->npa_argv[0]);
3451
3452 if (fstr == NULL) {
3453 err(-1, "failed to allocate memory to duplicate "
3454 "feature string");
3455 }
3456
3457 for (const char *c = strchr(fstr, ','); c != NULL;
3458 c = strchr(c + 1, ',')) {
3459 nf.nf_nfilts++;
3460 }
3461
3462 filts = calloc(nf.nf_nfilts, sizeof (char *));
3463 if (filts == NULL) {
3464 err(-1, "failed to allocate memory for filter list");
3465 }
3466
3467 i = 0;
3468 while ((f = strsep(&fstr, ",")) != NULL) {
3469 filts[i] = f;
3470 i++;
3471 }
3472 VERIFY3U(i, ==, nf.nf_nfilts);
3473 nf.nf_filts = filts;
3474
3475 used = calloc(nf.nf_nfilts, sizeof (boolean_t));
3476 if (used == NULL) {
3477 err(-1, "failed to allocate memory for filter use "
3478 "tracking");
3479 }
3480 nf.nf_used = used;
3481 }
3482
3483 (void) printf("%s: Get Features\n", npa->npa_name);
3484 ret = do_features(npa, &nf, do_get_features_cb);
3485
3486 free(fstr);
3487 free(filts);
3488 free(used);
3489 return (ret);
3490 }
3491
3492 typedef struct nvmeadm_set_feat {
3493 bool nsf_save;
3494 bool nsf_hcdw11;
3495 bool nsf_hcdw12;
3496 bool nsf_hcdw13;
3497 bool nsf_hcdw15;
3498 uint32_t nsf_cdw11;
3499 uint32_t nsf_cdw12;
3500 uint32_t nsf_cdw13;
3501 uint32_t nsf_cdw15;
3502 uint32_t nsf_dlen;
3503 const char *nsf_input;
3504 nvme_disc_impact_t nsf_impact;
3505 } nvmeadm_set_feat_t;
3506
3507 /*
3508 * Like with vendor-specific commands, we opt to allow long options for this
3509 * sub-command so that way the --cdwXX arguments are a little more manageable.
3510 * Traditionally cdw11 was the only field set for a feature aside from data. As
3511 * such, we use -v as the short value here to allow folks that are used to other
3512 * tooling.
3513 */
3514 static const struct option set_feat_lopts[] = {
3515 { "save", no_argument, NULL, 's' },
3516 { "cdw11", required_argument, NULL, 'v' },
3517 { "cdw12", required_argument, NULL, '2' },
3518 { "cdw13", required_argument, NULL, '3' },
3519 { "cdw15", required_argument, NULL, '5' },
3520 { "input", required_argument, NULL, 'i' },
3521 { "length", required_argument, NULL, 'l' },
3522 { "impact", required_argument, NULL, 'I' },
3523 { NULL, 0, NULL, 0 }
3524 };
3525
3526 /*
3527 * Use a limit that's a bit larger than the kernels in case someone tries to
3528 * adjust the tunable or we increase it in the future. This would be a good
3529 * thing to ask the kernel about instead.
3530 */
3531 #define NVMEADM_SET_FEAT_LEN_MAX (4 * 1024 * 1024)
3532
3533 static void
optparse_set_feature(nvme_process_arg_t * npa)3534 optparse_set_feature(nvme_process_arg_t *npa)
3535 {
3536 int c;
3537 nvmeadm_set_feat_t *feat;
3538
3539 if ((feat = calloc(1, sizeof (nvmeadm_set_feat_t))) == NULL) {
3540 err(-1, "failed to allocate memory for option tracking");
3541 }
3542
3543 /*
3544 * Work around getopt_long treating optind=0 as a request to restart
3545 * option processing and setting optind = optreset = 1.
3546 */
3547 optind++;
3548 while ((c = getopt_long(npa->npa_argc + 1, npa->npa_argv - 1,
3549 ":sv:2:3:5:i:l:I:", set_feat_lopts, NULL)) != -1) {
3550 switch (c) {
3551 case 's':
3552 feat->nsf_save = true;
3553 break;
3554 case 'v':
3555 feat->nsf_cdw11 = (uint32_t)optparse_ui_range(optarg,
3556 "cdw11", 0, UINT32_MAX);
3557 feat->nsf_hcdw11 = true;
3558 break;
3559 case '2':
3560 feat->nsf_cdw12 = (uint32_t)optparse_ui_range(optarg,
3561 "cdw12", 0, UINT32_MAX);
3562 feat->nsf_hcdw12 = true;
3563 break;
3564 case '3':
3565 feat->nsf_cdw13 = (uint32_t)optparse_ui_range(optarg,
3566 "cdw13", 0, UINT32_MAX);
3567 feat->nsf_hcdw13 = true;
3568 break;
3569 case '5':
3570 feat->nsf_cdw15 = (uint32_t)optparse_ui_range(optarg,
3571 "cdw15", 0, UINT32_MAX);
3572 feat->nsf_hcdw15 = true;
3573 break;
3574 case 'i':
3575 feat->nsf_input = optarg;
3576 break;
3577 case 'l':
3578 feat->nsf_dlen = (uint32_t)optparse_ui_range(optarg,
3579 "length", 0, NVMEADM_SET_FEAT_LEN_MAX);
3580 break;
3581 case 'I':
3582 feat->nsf_impact = optparse_impact(optarg);
3583 break;
3584 case '?':
3585 errx(-1, "unknown option: -%c", optopt);
3586 case ':':
3587 errx(-1, "option -%c requires an argument", optopt);
3588 }
3589 }
3590
3591 /*
3592 * Undo our lies.
3593 */
3594 optind--;
3595
3596 if (feat->nsf_input != NULL && feat->nsf_dlen == 0) {
3597 errx(-1, "specified data input file (-i) but missing required "
3598 "data length (-l)");
3599 }
3600
3601 if (feat->nsf_input == NULL && feat->nsf_dlen != 0) {
3602 errx(-1, "%u bytes of data transfer requested (-l), but no "
3603 "input (-i) specified", feat->nsf_dlen);
3604 }
3605
3606 npa->npa_cmd_arg = feat;
3607 }
3608
3609 static void
usage_set_feature(const char * c_name)3610 usage_set_feature(const char *c_name)
3611 {
3612 (void) fprintf(stderr, "%s [-s] [-v value] [--cdw12 cdw12] "
3613 "[--cdw13 cdw13]\n\t [--cdw15 cdw15] [-l length -i input] "
3614 "[-I impact] <ctl>[/<ns>]\n\n", c_name);
3615 (void) fprintf(stderr, " set a feature on a controller and/or "
3616 "namespace\n");
3617
3618 }
3619
3620 /*
3621 * Read up to len bytes from file. If file hits EOF before len bytes are read,
3622 * len will be zero-filled.
3623 */
3624 void *
nvmeadm_fill_from_file(const char * file,size_t len)3625 nvmeadm_fill_from_file(const char *file, size_t len)
3626 {
3627 void *buf = calloc(len, sizeof (uint8_t));
3628 if (buf == NULL) {
3629 err(-1, "failed to allocate 0x%zx byte request data buffer",
3630 len);
3631 }
3632
3633 int ifd = open(file, O_RDONLY);
3634 if (ifd < 0) {
3635 err(-1, "failed to open input file %s", file);
3636 }
3637
3638 size_t rem = len, off = 0;
3639 while (rem > 0) {
3640 size_t toread = MIN(16 * 1024, rem);
3641 ssize_t ret = read(ifd, buf + off, toread);
3642 if (ret < 0) {
3643 errx(-1, "failed to read %zu bytes at offset %zu from "
3644 "%s", toread, off, file);
3645 } else if (ret == 0) {
3646 break;
3647 }
3648
3649 rem -= (size_t)ret;
3650 off += (size_t)ret;
3651 }
3652
3653 VERIFY0(close(ifd));
3654 return (buf);
3655 }
3656
3657 static int
do_set_feature(const nvme_process_arg_t * npa)3658 do_set_feature(const nvme_process_arg_t *npa)
3659 {
3660 nvmeadm_set_feat_t *feat = npa->npa_cmd_arg;
3661 nvme_feat_disc_t *disc = NULL;
3662 nvme_set_feat_req_t *req;
3663 nvme_ctrl_t *ctrl = npa->npa_ctrl;
3664 uint8_t *buf = NULL;
3665
3666 if (npa->npa_argc < 1) {
3667 errx(-1, "missing required feature to set");
3668 } else if (npa->npa_argc > 1) {
3669 errx(-1, "%s passed extraneous arguments starting with %s",
3670 npa->npa_cmd->c_name, npa->npa_argv[1]);
3671 }
3672
3673 if (npa->npa_ns != NULL) {
3674 errx(-1, "features may only bet set on a controller, not a "
3675 "namespace");
3676 }
3677
3678 const char *name = npa->npa_argv[0];
3679
3680 /*
3681 * First try to parse by name. Otherwise, try to see if this is a
3682 * feature identifier we can parse. If it's not a known name, see if
3683 * it's a valid feature identifier. If it isn't, we use the original
3684 * error message from libnvme.
3685 */
3686 if (!nvme_set_feat_req_init_by_name(ctrl, name, 0, &disc,
3687 &req)) {
3688 if (nvme_ctrl_err(ctrl) != NVME_ERR_FEAT_NAME_UNKNOWN) {
3689 nvmeadm_fatal(npa, "failed to initialize set feature "
3690 "request for %s", name);
3691 }
3692
3693 const char *errstr;
3694 uint8_t fid = strtonumx(name, 0, NVME_FEAT_MAX_FID, &errstr, 0);
3695 if (errstr != NULL) {
3696 nvmeadm_fatal(npa, "failed to initialize set feature "
3697 "request for %s", name);
3698 }
3699
3700 if (!nvme_set_feat_req_init(ctrl, &req)) {
3701 nvmeadm_fatal(npa, "failed to initialize set feature "
3702 "request for %s", name);
3703 }
3704
3705 if (!nvme_set_feat_req_set_fid(req, fid)) {
3706 nvmeadm_fatal(npa, "failed to set feature id to 0x%x",
3707 fid);
3708 }
3709 }
3710
3711 if (npa->npa_ns != NULL) {
3712 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
3713
3714 if (!nvme_set_feat_req_set_nsid(req, nsid)) {
3715 nvmeadm_fatal(npa, "failed to set feature request "
3716 "namespace ID to 0x%x", nsid);
3717 }
3718 }
3719
3720 if (!nvme_set_feat_req_set_save(req, feat->nsf_save) ||
3721 (feat->nsf_hcdw11 && !nvme_set_feat_req_set_cdw11(req,
3722 feat->nsf_cdw11)) ||
3723 (feat->nsf_hcdw12 && !nvme_set_feat_req_set_cdw12(req,
3724 feat->nsf_cdw12)) ||
3725 (feat->nsf_hcdw13 && !nvme_set_feat_req_set_cdw13(req,
3726 feat->nsf_cdw13)) ||
3727 (feat->nsf_hcdw15 && !nvme_set_feat_req_set_cdw15(req,
3728 feat->nsf_cdw15)) ||
3729 !nvme_set_feat_req_set_impact(req, feat->nsf_impact)) {
3730 nvmeadm_fatal(npa, "failed to set request fields");
3731 }
3732
3733 if (feat->nsf_dlen > 0) {
3734 buf = nvmeadm_fill_from_file(feat->nsf_input, feat->nsf_dlen);
3735 if (!nvme_set_feat_req_set_input(req, buf, feat->nsf_dlen)) {
3736 nvmeadm_fatal(npa, "failed to set input data buffer");
3737 }
3738 }
3739
3740 if (!nvme_set_feat_req_exec(req)) {
3741 nvmeadm_fatal(npa, "failed to execute set feature request");
3742 }
3743
3744 uint32_t cdw0;
3745 if (nvme_set_feat_req_get_cdw0(req, &cdw0)) {
3746 (void) printf("Set Feature cdw0: 0x%x\n", cdw0);
3747 }
3748
3749 nvme_set_feat_req_fini(req);
3750 free(buf);
3751
3752 return (0);
3753 }
3754
3755 static int
do_format_common(const nvme_process_arg_t * npa,uint32_t lbaf,uint32_t ses)3756 do_format_common(const nvme_process_arg_t *npa, uint32_t lbaf,
3757 uint32_t ses)
3758 {
3759 int ret = 0;
3760 nvme_format_req_t *req;
3761
3762 if (npa->npa_ns != NULL && nvme_ns_info_level(npa->npa_ns_info) <
3763 NVME_NS_DISC_F_ACTIVE) {
3764 errx(-1, "cannot %s: namespace is inactive",
3765 npa->npa_cmd->c_name);
3766 }
3767
3768 if (!nvme_format_req_init(npa->npa_ctrl, &req)) {
3769 nvmeadm_fatal(npa, "failed to initialize format request for "
3770 "%s", npa->npa_name);
3771 }
3772
3773 if (npa->npa_ns != NULL) {
3774 uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
3775
3776 if (!nvme_format_req_set_nsid(req, nsid)) {
3777 nvmeadm_fatal(npa, "failed to set format request "
3778 "namespace ID to 0x%x", nsid);
3779 }
3780 }
3781
3782 if (!nvme_format_req_set_lbaf(req, lbaf) ||
3783 !nvme_format_req_set_ses(req, ses)) {
3784 nvmeadm_fatal(npa, "failed to set format request fields for %s",
3785 npa->npa_name);
3786 }
3787
3788 if (do_detach_bd(npa) != 0) {
3789 errx(-1, "cannot %s %s due to namespace detach failure",
3790 npa->npa_cmd->c_name, npa->npa_name);
3791 }
3792
3793 if (!nvme_format_req_exec(req)) {
3794 nvmeadm_warn(npa, "failed to %s %s", npa->npa_cmd->c_name,
3795 npa->npa_name);
3796 ret = -1;
3797 }
3798
3799 if (do_attach_bd(npa) != 0)
3800 ret = -1;
3801
3802 return (ret);
3803 }
3804
3805 static void
usage_format(const char * c_name)3806 usage_format(const char *c_name)
3807 {
3808 (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
3809 " Format one or all namespaces of the specified NVMe "
3810 "controller. Supported LBA\n formats can be queried with "
3811 "the \"%s identify\" command on the namespace\n to be "
3812 "formatted.\n", c_name, getprogname());
3813 }
3814
3815 static uint32_t
do_format_determine_lbaf(const nvme_process_arg_t * npa)3816 do_format_determine_lbaf(const nvme_process_arg_t *npa)
3817 {
3818 const nvme_nvm_lba_fmt_t *fmt;
3819 nvme_ns_info_t *ns_info = NULL;
3820 uint32_t lbaf;
3821
3822 if (npa->npa_argc > 0) {
3823 unsigned long lba;
3824 uint32_t nlbaf = nvme_ctrl_info_nformats(npa->npa_ctrl_info);
3825
3826 errno = 0;
3827 lba = strtoul(npa->npa_argv[0], NULL, 10);
3828 if (errno != 0 || lba >= nlbaf)
3829 errx(-1, "invalid LBA format %s", npa->npa_argv[0]);
3830
3831 if (!nvme_ctrl_info_format(npa->npa_ctrl_info, (uint32_t)lba,
3832 &fmt)) {
3833 nvmeadm_fatal(npa, "failed to get LBA format %lu "
3834 "information", lba);
3835 }
3836 } else {
3837 /*
3838 * If we have a namespace then we use the current namespace's
3839 * LBA format. If we don't have a namespace, then we promised
3840 * we'd look at namespace 1 in the manual page.
3841 */
3842 if (npa->npa_ns_info == NULL) {
3843 if (!nvme_ctrl_ns_info_snap(npa->npa_ctrl, 1,
3844 &ns_info)) {
3845 nvmeadm_fatal(npa, "failed to get namespace 1 "
3846 "information, please explicitly specify an "
3847 "LBA format");
3848 }
3849
3850 if (!nvme_ns_info_curformat(ns_info, &fmt)) {
3851 nvmeadm_fatal(npa, "failed to retrieve current "
3852 "namespace format from namespace 1");
3853 }
3854 } else {
3855 if (!nvme_ns_info_curformat(npa->npa_ns_info, &fmt)) {
3856 nvmeadm_fatal(npa, "failed to get the current "
3857 "format information from %s",
3858 npa->npa_name);
3859 }
3860 }
3861 }
3862
3863 if (nvme_nvm_lba_fmt_meta_size(fmt) != 0) {
3864 errx(-1, "LBA formats with metadata are not supported");
3865 }
3866
3867 lbaf = nvme_nvm_lba_fmt_id(fmt);
3868 nvme_ns_info_free(ns_info);
3869 return (lbaf);
3870 }
3871
3872 static int
do_format(const nvme_process_arg_t * npa)3873 do_format(const nvme_process_arg_t *npa)
3874 {
3875 uint32_t lbaf;
3876
3877 if (npa->npa_argc > 1) {
3878 errx(-1, "%s passed extraneous arguments starting with %s",
3879 npa->npa_cmd->c_name, npa->npa_argv[1]);
3880 }
3881
3882 lbaf = do_format_determine_lbaf(npa);
3883 return (do_format_common(npa, lbaf, 0));
3884 }
3885
3886 static void
usage_secure_erase(const char * c_name)3887 usage_secure_erase(const char *c_name)
3888 {
3889 (void) fprintf(stderr, "%s [-c] <ctl>[/<ns>]\n\n"
3890 " Secure-Erase one or all namespaces of the specified "
3891 "NVMe controller.\n", c_name);
3892 }
3893
3894 static void
optparse_secure_erase(nvme_process_arg_t * npa)3895 optparse_secure_erase(nvme_process_arg_t *npa)
3896 {
3897 int c;
3898
3899 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":c")) != -1) {
3900 switch (c) {
3901 case 'c':
3902 npa->npa_cmdflags |= NVMEADM_O_SE_CRYPTO;
3903 break;
3904
3905 case '?':
3906 errx(-1, "unknown option: -%c", optopt);
3907
3908 case ':':
3909 errx(-1, "option -%c requires an argument", optopt);
3910
3911 }
3912 }
3913 }
3914
3915 static int
do_secure_erase(const nvme_process_arg_t * npa)3916 do_secure_erase(const nvme_process_arg_t *npa)
3917 {
3918 unsigned long lbaf;
3919 uint8_t ses = NVME_FRMT_SES_USER;
3920
3921 if (npa->npa_argc > 0) {
3922 errx(-1, "%s passed extraneous arguments starting with %s",
3923 npa->npa_cmd->c_name, npa->npa_argv[0]);
3924 }
3925
3926 if ((npa->npa_cmdflags & NVMEADM_O_SE_CRYPTO) != 0)
3927 ses = NVME_FRMT_SES_CRYPTO;
3928
3929 lbaf = do_format_determine_lbaf(npa);
3930 return (do_format_common(npa, lbaf, ses));
3931 }
3932
3933 static void
usage_attach_detach_bd(const char * c_name)3934 usage_attach_detach_bd(const char *c_name)
3935 {
3936 (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
3937 " %c%s blkdev(4D) %s one or all namespaces of the "
3938 "specified NVMe controller.\n",
3939 c_name, toupper(c_name[0]), &c_name[1],
3940 c_name[0] == 'd' ? "from" : "to");
3941 }
3942
3943 /*
3944 * nvmeadm does not generate an error when trying to attach blkdev to something
3945 * that already has it attached. Swallow that here.
3946 */
3947 static boolean_t
swallow_attach_bd_err(const nvme_process_arg_t * npa)3948 swallow_attach_bd_err(const nvme_process_arg_t *npa)
3949 {
3950 return (nvme_ctrl_err(npa->npa_ctrl) == NVME_ERR_NS_BLKDEV_ATTACH);
3951 }
3952
3953 static int
do_attach_bd(const nvme_process_arg_t * npa)3954 do_attach_bd(const nvme_process_arg_t *npa)
3955 {
3956 int rv;
3957 nvme_ns_iter_t *iter = NULL;
3958 nvme_iter_t ret;
3959 const nvme_ns_disc_t *disc;
3960
3961 if (npa->npa_ns != NULL) {
3962 if (!nvme_ns_bd_attach(npa->npa_ns) &&
3963 !swallow_attach_bd_err(npa)) {
3964 nvmeadm_warn(npa, "faild to attach %s", npa->npa_name);
3965 return (-1);
3966 }
3967 return (0);
3968 }
3969
3970 if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_NOT_IGNORED,
3971 &iter)) {
3972 nvmeadm_fatal(npa, "failed to initialize namespace discovery "
3973 "on %s", npa->npa_name);
3974 }
3975
3976 rv = 0;
3977 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
3978 nvme_ns_t *ns;
3979 uint32_t nsid;
3980
3981 if (nvme_ns_disc_level(disc) == NVME_NS_DISC_F_BLKDEV)
3982 continue;
3983
3984 nsid = nvme_ns_disc_nsid(disc);
3985 if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) {
3986 nvmeadm_warn(npa, "failed to open namespace %s/%u "
3987 "handle", npa->npa_name, nsid);
3988 rv = -1;
3989 continue;
3990 }
3991
3992 /*
3993 * nvmeadm has historically swallowed the case where you ask to
3994 * attach an already attached namespace.
3995 */
3996 if (!nvme_ns_bd_attach(ns) && !swallow_attach_bd_err(npa)) {
3997 nvmeadm_warn(npa, "failed to attach namespace "
3998 "%s/%u", npa->npa_name, nsid);
3999 rv = -1;
4000 }
4001 nvme_ns_fini(ns);
4002 }
4003
4004 nvme_ns_discover_fini(iter);
4005 if (ret == NVME_ITER_ERROR) {
4006 nvmeadm_warn(npa, "failed to iterate namespaces on %s",
4007 npa->npa_name);
4008 rv = -1;
4009 }
4010
4011 return (rv);
4012 }
4013
4014 /*
4015 * nvmeadm does not generate an error when trying to attach blkdev to something
4016 * that already has it attached. Swallow that here.
4017 */
4018 static boolean_t
swallow_detach_bd_err(const nvme_process_arg_t * npa)4019 swallow_detach_bd_err(const nvme_process_arg_t *npa)
4020 {
4021 switch (nvme_ctrl_err(npa->npa_ctrl)) {
4022 case NVME_ERR_NS_UNALLOC:
4023 case NVME_ERR_NS_CTRL_NOT_ATTACHED:
4024 case NVME_ERR_NS_CTRL_ATTACHED:
4025 return (B_TRUE);
4026 default:
4027 return (B_FALSE);
4028 }
4029 }
4030
4031 static int
do_detach_bd(const nvme_process_arg_t * npa)4032 do_detach_bd(const nvme_process_arg_t *npa)
4033 {
4034 int rv;
4035 nvme_ns_iter_t *iter = NULL;
4036 nvme_iter_t ret;
4037 const nvme_ns_disc_t *disc;
4038
4039 if (npa->npa_ns != NULL) {
4040 if (!nvme_ns_bd_detach(npa->npa_ns) &&
4041 !swallow_detach_bd_err(npa)) {
4042 nvmeadm_warn(npa, "failed to detach %s", npa->npa_name);
4043 return (-1);
4044 }
4045 return (0);
4046 }
4047
4048 if (!nvme_ns_discover_init(npa->npa_ctrl, NVME_NS_DISC_F_BLKDEV,
4049 &iter)) {
4050 nvmeadm_fatal(npa, "failed to initialize namespace discovery "
4051 "on %s", npa->npa_name);
4052 }
4053
4054 rv = 0;
4055 while ((ret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
4056 nvme_ns_t *ns;
4057 uint32_t nsid = nvme_ns_disc_nsid(disc);
4058
4059 if (!nvme_ns_init(npa->npa_ctrl, nsid, &ns)) {
4060 nvmeadm_warn(npa, "failed to open namespace %s/%u "
4061 "handle", npa->npa_name, nsid);
4062 rv = -1;
4063 continue;
4064 }
4065
4066 if (!nvme_ns_bd_detach(ns) && !swallow_detach_bd_err(npa)) {
4067 nvmeadm_warn(npa, "failed to detach namespace %s/%u",
4068 npa->npa_name, nsid);
4069 rv = -1;
4070 }
4071 nvme_ns_fini(ns);
4072 }
4073
4074 nvme_ns_discover_fini(iter);
4075 if (ret == NVME_ITER_ERROR) {
4076 nvmeadm_warn(npa, "failed to iterate namespaces on %s",
4077 npa->npa_name);
4078 rv = -1;
4079 }
4080
4081 return (rv);
4082 }
4083
4084 static void
usage_firmware_load(const char * c_name)4085 usage_firmware_load(const char *c_name)
4086 {
4087 (void) fprintf(stderr, "%s <ctl> <image file> [<offset>]\n\n"
4088 " Load firmware <image file> to offset <offset>.\n"
4089 " The firmware needs to be committed to a slot using "
4090 "\"nvmeadm commit-firmware\"\n command.\n", c_name);
4091 }
4092
4093 /*
4094 * Read exactly len bytes, or until eof.
4095 */
4096 static size_t
read_block(const nvme_process_arg_t * npa,int fd,char * buf,size_t len)4097 read_block(const nvme_process_arg_t *npa, int fd, char *buf, size_t len)
4098 {
4099 size_t remain;
4100
4101 remain = len;
4102 while (remain > 0) {
4103 ssize_t bytes = read(fd, buf, remain);
4104 if (bytes == 0)
4105 break;
4106
4107 if (bytes < 0) {
4108 if (errno == EINTR)
4109 continue;
4110
4111 err(-1, "Error reading \"%s\"", npa->npa_argv[0]);
4112 }
4113
4114 buf += (size_t)bytes;
4115 remain -= (size_t)bytes;
4116 }
4117
4118 return (len - remain);
4119 }
4120
4121 /*
4122 * Convert a string to a valid firmware upload offset (in bytes).
4123 */
4124 static uint64_t
get_fw_offsetb(char * str)4125 get_fw_offsetb(char *str)
4126 {
4127 longlong_t offsetb;
4128 char *valend;
4129
4130 errno = 0;
4131 offsetb = strtoll(str, &valend, 0);
4132 if (errno != 0 || *valend != '\0' || offsetb < 0 ||
4133 offsetb > NVME_FW_OFFSETB_MAX)
4134 errx(-1, "Offset must be numeric and in the range of 0 to %llu",
4135 NVME_FW_OFFSETB_MAX);
4136
4137 if ((offsetb & NVME_DWORD_MASK) != 0)
4138 errx(-1, "Offset must be multiple of %d", NVME_DWORD_SIZE);
4139
4140 return ((uint64_t)offsetb);
4141 }
4142
4143 #define FIRMWARE_READ_BLKSIZE (64 * 1024) /* 64K */
4144
4145 static int
do_firmware_load(const nvme_process_arg_t * npa)4146 do_firmware_load(const nvme_process_arg_t *npa)
4147 {
4148 int fw_fd;
4149 uint64_t offset = 0;
4150 size_t size, len;
4151 char buf[FIRMWARE_READ_BLKSIZE];
4152
4153 if (npa->npa_argc > 2)
4154 errx(-1, "%s passed extraneous arguments starting with %s",
4155 npa->npa_cmd->c_name, npa->npa_argv[2]);
4156
4157 if (npa->npa_argc == 0)
4158 errx(-1, "Requires firmware file name, and an "
4159 "optional offset");
4160
4161 if (npa->npa_ns != NULL)
4162 errx(-1, "Firmware loading not available on a per-namespace "
4163 "basis");
4164
4165 if (npa->npa_argc == 2)
4166 offset = get_fw_offsetb(npa->npa_argv[1]);
4167
4168 fw_fd = open(npa->npa_argv[0], O_RDONLY);
4169 if (fw_fd < 0)
4170 errx(-1, "Failed to open \"%s\": %s", npa->npa_argv[0],
4171 strerror(errno));
4172
4173 size = 0;
4174 do {
4175 len = read_block(npa, fw_fd, buf, sizeof (buf));
4176
4177 if (len == 0)
4178 break;
4179
4180 if (!nvme_fw_load(npa->npa_ctrl, buf, len, offset)) {
4181 nvmeadm_fatal(npa, "failed to load firmware image "
4182 "\"%s\" at offset %" PRIu64, npa->npa_argv[0],
4183 offset);
4184 }
4185
4186 offset += len;
4187 size += len;
4188 } while (len == sizeof (buf));
4189
4190 (void) close(fw_fd);
4191
4192 if (verbose)
4193 (void) printf("%zu bytes downloaded.\n", size);
4194
4195 return (0);
4196 }
4197
4198 /*
4199 * Common firmware commit for nvmeadm commit-firmware and activate-firmware.
4200 */
4201 static void
nvmeadm_firmware_commit(const nvme_process_arg_t * npa,uint32_t slot,uint32_t act)4202 nvmeadm_firmware_commit(const nvme_process_arg_t *npa, uint32_t slot,
4203 uint32_t act)
4204 {
4205 nvme_fw_commit_req_t *req;
4206
4207 if (!nvme_fw_commit_req_init(npa->npa_ctrl, &req)) {
4208 nvmeadm_fatal(npa, "failed to initialize firmware commit "
4209 "request for %s", npa->npa_name);
4210 }
4211
4212 if (!nvme_fw_commit_req_set_slot(req, slot) ||
4213 !nvme_fw_commit_req_set_action(req, act)) {
4214 nvmeadm_fatal(npa, "failed to set firmware commit fields for "
4215 "%s", npa->npa_name);
4216 }
4217
4218 if (!nvme_fw_commit_req_exec(req)) {
4219 /*
4220 * A number of command specific status values are informational
4221 * and indicate that the operation was successful but that
4222 * something else, such as a device reset, is still required
4223 * before the new firmware is active.
4224 * We distinguish those here and report them as a note rather
4225 * than a fatal error.
4226 */
4227 if (nvme_ctrl_err(npa->npa_ctrl) == NVME_ERR_CONTROLLER) {
4228 uint32_t sct, sc;
4229
4230 nvme_ctrl_deverr(npa->npa_ctrl, &sct, &sc);
4231 if (sct == NVME_CQE_SCT_SPECIFIC && (
4232 sc == NVME_CQE_SC_SPC_FW_RESET ||
4233 sc == NVME_CQE_SC_SPC_FW_NSSR ||
4234 sc == NVME_CQE_SC_SPC_FW_NEXT_RESET)) {
4235 fprintf(stderr,
4236 "nvmeadm: commit successful but %s\n",
4237 nvme_sctostr(npa->npa_ctrl, NVME_CSI_NVM,
4238 sct, sc));
4239 } else {
4240 nvmeadm_fatal(npa, "failed to %s on %s",
4241 npa->npa_cmd->c_name, npa->npa_name);
4242 }
4243 } else {
4244 nvmeadm_fatal(npa, "failed to %s on %s",
4245 npa->npa_cmd->c_name, npa->npa_name);
4246 }
4247 }
4248
4249 nvme_fw_commit_req_fini(req);
4250 }
4251
4252 /*
4253 * Convert str to a valid firmware slot number.
4254 */
4255 static uint32_t
get_slot_number(char * str)4256 get_slot_number(char *str)
4257 {
4258 longlong_t slot;
4259 char *valend;
4260
4261 errno = 0;
4262 slot = strtoll(str, &valend, 0);
4263 if (errno != 0 || *valend != '\0' ||
4264 slot < NVME_FW_SLOT_MIN || slot > NVME_FW_SLOT_MAX)
4265 errx(-1, "Slot must be numeric and in the range of %u to %u",
4266 NVME_FW_SLOT_MIN, NVME_FW_SLOT_MAX);
4267
4268 return ((uint32_t)slot);
4269 }
4270
4271 static void
usage_firmware_commit(const char * c_name)4272 usage_firmware_commit(const char *c_name)
4273 {
4274 (void) fprintf(stderr, "%s <ctl> <slot>\n\n"
4275 " Commit previously downloaded firmware to slot <slot>.\n"
4276 " The firmware is only activated after a "
4277 "\"nvmeadm activate-firmware\" command.\n", c_name);
4278 }
4279
4280 static int
do_firmware_commit(const nvme_process_arg_t * npa)4281 do_firmware_commit(const nvme_process_arg_t *npa)
4282 {
4283 uint32_t slot;
4284
4285 if (npa->npa_argc > 1)
4286 errx(-1, "%s passed extraneous arguments starting with %s",
4287 npa->npa_cmd->c_name, npa->npa_argv[1]);
4288
4289 if (npa->npa_argc == 0)
4290 errx(-1, "Firmware slot number is required");
4291
4292 if (npa->npa_ns != NULL)
4293 errx(-1, "Firmware committing not available on a per-namespace "
4294 "basis");
4295
4296 slot = get_slot_number(npa->npa_argv[0]);
4297
4298 if (slot == 1 && npa->npa_idctl->id_frmw.fw_readonly)
4299 errx(-1, "Cannot commit firmware to slot 1: slot is read-only");
4300
4301 nvmeadm_firmware_commit(npa, slot, NVME_FWC_SAVE);
4302
4303 if (verbose)
4304 (void) printf("Firmware committed to slot %u.\n", slot);
4305
4306 return (0);
4307 }
4308
4309 static void
usage_firmware_activate(const char * c_name)4310 usage_firmware_activate(const char *c_name)
4311 {
4312 (void) fprintf(stderr, "%s <ctl> <slot>\n\n"
4313 " Activate firmware in slot <slot>.\n"
4314 " The firmware will be in use after the next system reset.\n",
4315 c_name);
4316 }
4317
4318 static int
do_firmware_activate(const nvme_process_arg_t * npa)4319 do_firmware_activate(const nvme_process_arg_t *npa)
4320 {
4321 uint32_t slot;
4322
4323 if (npa->npa_argc > 1)
4324 errx(-1, "%s passed extraneous arguments starting with %s",
4325 npa->npa_cmd->c_name, npa->npa_argv[1]);
4326
4327 if (npa->npa_argc == 0)
4328 errx(-1, "Firmware slot number is required");
4329
4330 if (npa->npa_ns != NULL)
4331 errx(-1, "Firmware activation not available on a per-namespace "
4332 "basis");
4333
4334 slot = get_slot_number(npa->npa_argv[0]);
4335
4336 nvmeadm_firmware_commit(npa, slot, NVME_FWC_ACTIVATE);
4337
4338 if (verbose)
4339 (void) printf("Slot %u successfully activated.\n", slot);
4340
4341 return (0);
4342 }
4343