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 2016 Nexenta Systems, Inc.
14 * Copyright 2017 Joyent, Inc.
15 */
16
17 /*
18 * nvmeadm -- NVMe administration utility
19 *
20 * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args]
21 * commands: list
22 * identify
23 * get-logpage <logpage name>
24 * get-features <feature>[,...]
25 * format ...
26 * secure-erase ...
27 * detach ...
28 * attach ...
29 * get-param ...
30 * set-param ...
31 * load-firmware ...
32 * activate-firmware ...
33 * write-uncorrectable ...
34 * compare ...
35 * compare-and-write ...
36 */
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <strings.h>
41 #include <ctype.h>
42 #include <err.h>
43 #include <sys/sunddi.h>
44 #include <libdevinfo.h>
45
46 #include <sys/nvme.h>
47
48 #include "nvmeadm.h"
49
50 typedef struct nvme_process_arg nvme_process_arg_t;
51 typedef struct nvme_feature nvme_feature_t;
52 typedef struct nvmeadm_cmd nvmeadm_cmd_t;
53
54 struct nvme_process_arg {
55 int npa_argc;
56 char **npa_argv;
57 char *npa_name;
58 char *npa_nsid;
59 int npa_found;
60 boolean_t npa_isns;
61 const nvmeadm_cmd_t *npa_cmd;
62 di_node_t npa_node;
63 di_minor_t npa_minor;
64 char *npa_path;
65 char *npa_dsk;
66 nvme_identify_ctrl_t *npa_idctl;
67 nvme_identify_nsid_t *npa_idns;
68 nvme_version_t *npa_version;
69 };
70
71 struct nvme_feature {
72 char *f_name;
73 char *f_short;
74 uint8_t f_feature;
75 size_t f_bufsize;
76 uint_t f_getflags;
77 int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *);
78 void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *);
79 };
80
81 #define NVMEADM_CTRL 1
82 #define NVMEADM_NS 2
83 #define NVMEADM_BOTH (NVMEADM_CTRL | NVMEADM_NS)
84
85 struct nvmeadm_cmd {
86 char *c_name;
87 char *c_desc;
88 char *c_flagdesc;
89 int (*c_func)(int, const nvme_process_arg_t *);
90 void (*c_usage)(const char *);
91 boolean_t c_multi;
92 };
93
94
95 static void usage(const nvmeadm_cmd_t *);
96 static void nvme_walk(nvme_process_arg_t *, di_node_t);
97 static boolean_t nvme_match(nvme_process_arg_t *);
98
99 static int nvme_process(di_node_t, di_minor_t, void *);
100
101 static int do_list(int, const nvme_process_arg_t *);
102 static int do_identify(int, const nvme_process_arg_t *);
103 static int do_get_logpage_error(int, const nvme_process_arg_t *);
104 static int do_get_logpage_health(int, const nvme_process_arg_t *);
105 static int do_get_logpage_fwslot(int, const nvme_process_arg_t *);
106 static int do_get_logpage(int, const nvme_process_arg_t *);
107 static int do_get_feat_common(int, const nvme_feature_t *,
108 nvme_identify_ctrl_t *);
109 static int do_get_feat_intr_vect(int, const nvme_feature_t *,
110 nvme_identify_ctrl_t *);
111 static int do_get_features(int, const nvme_process_arg_t *);
112 static int do_format(int, const nvme_process_arg_t *);
113 static int do_secure_erase(int, const nvme_process_arg_t *);
114 static int do_attach_detach(int, const nvme_process_arg_t *);
115
116 static void usage_list(const char *);
117 static void usage_identify(const char *);
118 static void usage_get_logpage(const char *);
119 static void usage_get_features(const char *);
120 static void usage_format(const char *);
121 static void usage_secure_erase(const char *);
122 static void usage_attach_detach(const char *);
123
124 int verbose;
125 int debug;
126 static int exitcode;
127
128 static const nvmeadm_cmd_t nvmeadm_cmds[] = {
129 {
130 "list",
131 "list controllers and namespaces",
132 NULL,
133 do_list, usage_list, B_TRUE
134 },
135 {
136 "identify",
137 "identify controllers and/or namespaces",
138 NULL,
139 do_identify, usage_identify, B_TRUE
140 },
141 {
142 "get-logpage",
143 "get a log page from controllers and/or namespaces",
144 NULL,
145 do_get_logpage, usage_get_logpage, B_TRUE
146 },
147 {
148 "get-features",
149 "get features from controllers and/or namespaces",
150 NULL,
151 do_get_features, usage_get_features, B_TRUE
152 },
153 {
154 "format",
155 "format namespace(s) of a controller",
156 NULL,
157 do_format, usage_format, B_FALSE
158 },
159 {
160 "secure-erase",
161 "secure erase namespace(s) of a controller",
162 " -c Do a cryptographic erase.",
163 do_secure_erase, usage_secure_erase, B_FALSE
164 },
165 {
166 "detach",
167 "detach blkdev(7d) from namespace(s) of a controller",
168 NULL,
169 do_attach_detach, usage_attach_detach, B_FALSE
170 },
171 {
172 "attach",
173 "attach blkdev(7d) to namespace(s) of a controller",
174 NULL,
175 do_attach_detach, usage_attach_detach, B_FALSE
176 },
177 {
178 NULL, NULL, NULL,
179 NULL, NULL, B_FALSE
180 }
181 };
182
183 static const nvme_feature_t features[] = {
184 { "Arbitration", "",
185 NVME_FEAT_ARBITRATION, 0, NVMEADM_CTRL,
186 do_get_feat_common, nvme_print_feat_arbitration },
187 { "Power Management", "",
188 NVME_FEAT_POWER_MGMT, 0, NVMEADM_CTRL,
189 do_get_feat_common, nvme_print_feat_power_mgmt },
190 { "LBA Range Type", "range",
191 NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_NS,
192 do_get_feat_common, nvme_print_feat_lba_range },
193 { "Temperature Threshold", "",
194 NVME_FEAT_TEMPERATURE, 0, NVMEADM_CTRL,
195 do_get_feat_common, nvme_print_feat_temperature },
196 { "Error Recovery", "",
197 NVME_FEAT_ERROR, 0, NVMEADM_CTRL,
198 do_get_feat_common, nvme_print_feat_error },
199 { "Volatile Write Cache", "cache",
200 NVME_FEAT_WRITE_CACHE, 0, NVMEADM_CTRL,
201 do_get_feat_common, nvme_print_feat_write_cache },
202 { "Number of Queues", "queues",
203 NVME_FEAT_NQUEUES, 0, NVMEADM_CTRL,
204 do_get_feat_common, nvme_print_feat_nqueues },
205 { "Interrupt Coalescing", "coalescing",
206 NVME_FEAT_INTR_COAL, 0, NVMEADM_CTRL,
207 do_get_feat_common, nvme_print_feat_intr_coal },
208 { "Interrupt Vector Configuration", "vector",
209 NVME_FEAT_INTR_VECT, 0, NVMEADM_CTRL,
210 do_get_feat_intr_vect, nvme_print_feat_intr_vect },
211 { "Write Atomicity", "atomicity",
212 NVME_FEAT_WRITE_ATOM, 0, NVMEADM_CTRL,
213 do_get_feat_common, nvme_print_feat_write_atom },
214 { "Asynchronous Event Configuration", "event",
215 NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_CTRL,
216 do_get_feat_common, nvme_print_feat_async_event },
217 { "Autonomous Power State Transition", "",
218 NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_CTRL,
219 do_get_feat_common, nvme_print_feat_auto_pst },
220 { "Software Progress Marker", "progress",
221 NVME_FEAT_PROGRESS, 0, NVMEADM_CTRL,
222 do_get_feat_common, nvme_print_feat_progress },
223 { NULL, NULL, 0, 0, B_FALSE, NULL }
224 };
225
226
227 int
main(int argc,char ** argv)228 main(int argc, char **argv)
229 {
230 int c;
231 extern int optind;
232 const nvmeadm_cmd_t *cmd;
233 di_node_t node;
234 nvme_process_arg_t npa = { 0 };
235 int help = 0;
236 char *tmp, *lasts = NULL;
237
238 while ((c = getopt(argc, argv, "dhv")) != -1) {
239 switch (c) {
240 case 'd':
241 debug++;
242 break;
243 case 'v':
244 verbose++;
245 break;
246 case 'h':
247 help++;
248 break;
249 case '?':
250 usage(NULL);
251 exit(-1);
252 }
253 }
254
255 if (optind == argc) {
256 usage(NULL);
257 if (help)
258 exit(0);
259 else
260 exit(-1);
261 }
262
263 /* Look up the specified command in the command table. */
264 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
265 if (strcmp(cmd->c_name, argv[optind]) == 0)
266 break;
267
268 if (cmd->c_name == NULL) {
269 usage(NULL);
270 exit(-1);
271 }
272
273 if (help) {
274 usage(cmd);
275 exit(0);
276 }
277
278 npa.npa_cmd = cmd;
279
280 optind++;
281
282 /*
283 * All commands but "list" require a ctl/ns argument.
284 */
285 if ((optind == argc || (strncmp(argv[optind], "nvme", 4) != 0)) &&
286 cmd->c_func != do_list) {
287 warnx("missing controller/namespace name");
288 usage(cmd);
289 exit(-1);
290 }
291
292
293 /* Store the remaining arguments for use by the command. */
294 npa.npa_argc = argc - optind - 1;
295 npa.npa_argv = &argv[optind + 1];
296
297 /*
298 * Make sure we're not running commands on multiple controllers that
299 * aren't allowed to do that.
300 */
301 if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL &&
302 cmd->c_multi == B_FALSE) {
303 warnx("%s not allowed on multiple controllers",
304 cmd->c_name);
305 usage(cmd);
306 exit(-1);
307 }
308
309 /*
310 * Get controller/namespace arguments and run command.
311 */
312 npa.npa_name = strtok_r(argv[optind], ",", &lasts);
313 do {
314 if (npa.npa_name != NULL) {
315 tmp = strchr(npa.npa_name, '/');
316 if (tmp != NULL) {
317 *tmp++ = '\0';
318 npa.npa_nsid = tmp;
319 npa.npa_isns = B_TRUE;
320 }
321 }
322
323 if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL)
324 err(-1, "failed to initialize libdevinfo");
325 nvme_walk(&npa, node);
326 di_fini(node);
327
328 if (npa.npa_found == 0) {
329 if (npa.npa_name != NULL) {
330 warnx("%s%.*s%.*s: no such controller or "
331 "namespace", npa.npa_name,
332 npa.npa_isns ? -1 : 0, "/",
333 npa.npa_isns ? -1 : 0, npa.npa_nsid);
334 } else {
335 warnx("no controllers found");
336 }
337 exitcode--;
338 }
339 npa.npa_found = 0;
340 npa.npa_name = strtok_r(NULL, ",", &lasts);
341 } while (npa.npa_name != NULL);
342
343 exit(exitcode);
344 }
345
346 static void
usage(const nvmeadm_cmd_t * cmd)347 usage(const nvmeadm_cmd_t *cmd)
348 {
349 (void) fprintf(stderr, "usage:\n");
350 (void) fprintf(stderr, " %s -h %s\n", getprogname(),
351 cmd != NULL ? cmd->c_name : "[<command>]");
352 (void) fprintf(stderr, " %s [-dv] ", getprogname());
353
354 if (cmd != NULL) {
355 cmd->c_usage(cmd->c_name);
356 } else {
357 (void) fprintf(stderr,
358 "<command> <ctl>[/<ns>][,...] [<args>]\n");
359 (void) fprintf(stderr,
360 "\n Manage NVMe controllers and namespaces.\n");
361 (void) fprintf(stderr, "\ncommands:\n");
362
363 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
364 (void) fprintf(stderr, " %-15s - %s\n",
365 cmd->c_name, cmd->c_desc);
366 }
367 (void) fprintf(stderr, "\nflags:\n"
368 " -h print usage information\n"
369 " -d print information useful for debugging %s\n"
370 " -v print verbose information\n", getprogname());
371 if (cmd != NULL && cmd->c_flagdesc != NULL)
372 (void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
373 }
374
375 static boolean_t
nvme_match(nvme_process_arg_t * npa)376 nvme_match(nvme_process_arg_t *npa)
377 {
378 char *name;
379 char *nsid = NULL;
380
381 if (npa->npa_name == NULL)
382 return (B_TRUE);
383
384 if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
385 di_instance(npa->npa_node)) < 0)
386 err(-1, "nvme_match()");
387
388 if (strcmp(name, npa->npa_name) != 0) {
389 free(name);
390 return (B_FALSE);
391 }
392
393 free(name);
394
395 if (npa->npa_isns) {
396 if (npa->npa_nsid == NULL)
397 return (B_TRUE);
398
399 nsid = di_minor_name(npa->npa_minor);
400
401 if (nsid == NULL || strcmp(npa->npa_nsid, nsid) != 0)
402 return (B_FALSE);
403 }
404
405 return (B_TRUE);
406 }
407
408 char *
nvme_dskname(const nvme_process_arg_t * npa)409 nvme_dskname(const nvme_process_arg_t *npa)
410 {
411 char *path = NULL;
412 di_node_t child;
413 di_dim_t dim;
414 char *addr;
415
416 dim = di_dim_init();
417
418 for (child = di_child_node(npa->npa_node);
419 child != DI_NODE_NIL;
420 child = di_sibling_node(child)) {
421 addr = di_bus_addr(child);
422 if (addr == NULL)
423 continue;
424
425 if (addr[0] == 'w')
426 addr++;
427
428 if (strncasecmp(addr, di_minor_name(npa->npa_minor),
429 strchrnul(addr, ',') - addr) != 0)
430 continue;
431
432 path = di_dim_path_dev(dim, di_driver_name(child),
433 di_instance(child), "c");
434
435 /*
436 * Error out if we didn't get a path, or if it's too short for
437 * the following operations to be safe.
438 */
439 if (path == NULL || strlen(path) < 2)
440 goto fail;
441
442 /* Chop off 's0' and get everything past the last '/' */
443 path[strlen(path) - 2] = '\0';
444 path = strrchr(path, '/');
445 if (path == NULL)
446 goto fail;
447 path++;
448
449 break;
450 }
451
452 di_dim_fini(dim);
453
454 return (path);
455
456 fail:
457 err(-1, "nvme_dskname");
458 }
459
460 static int
nvme_process(di_node_t node,di_minor_t minor,void * arg)461 nvme_process(di_node_t node, di_minor_t minor, void *arg)
462 {
463 nvme_process_arg_t *npa = arg;
464 int fd;
465
466 npa->npa_node = node;
467 npa->npa_minor = minor;
468
469 if (!nvme_match(npa))
470 return (DI_WALK_CONTINUE);
471
472 if ((fd = nvme_open(minor)) < 0)
473 return (DI_WALK_CONTINUE);
474
475 npa->npa_found++;
476
477 npa->npa_path = di_devfs_path(node);
478 if (npa->npa_path == NULL)
479 goto out;
480
481 npa->npa_version = nvme_version(fd);
482 if (npa->npa_version == NULL)
483 goto out;
484
485 npa->npa_idctl = nvme_identify_ctrl(fd);
486 if (npa->npa_idctl == NULL)
487 goto out;
488
489 npa->npa_idns = nvme_identify_nsid(fd);
490 if (npa->npa_idns == NULL)
491 goto out;
492
493 if (npa->npa_isns)
494 npa->npa_dsk = nvme_dskname(npa);
495
496 exitcode += npa->npa_cmd->c_func(fd, npa);
497
498 out:
499 di_devfs_path_free(npa->npa_path);
500 free(npa->npa_dsk);
501 free(npa->npa_version);
502 free(npa->npa_idctl);
503 free(npa->npa_idns);
504
505 npa->npa_version = NULL;
506 npa->npa_idctl = NULL;
507 npa->npa_idns = NULL;
508
509 nvme_close(fd);
510
511 return (DI_WALK_CONTINUE);
512 }
513
514 static void
nvme_walk(nvme_process_arg_t * npa,di_node_t node)515 nvme_walk(nvme_process_arg_t *npa, di_node_t node)
516 {
517 char *minor_nodetype = DDI_NT_NVME_NEXUS;
518
519 if (npa->npa_isns)
520 minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT;
521
522 (void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process);
523 }
524
525 static void
usage_list(const char * c_name)526 usage_list(const char *c_name)
527 {
528 (void) fprintf(stderr, "%s [<ctl>[/<ns>][,...]\n\n"
529 " List NVMe controllers and their namespaces. If no "
530 "controllers and/or name-\n spaces are specified, all "
531 "controllers and namespaces in the system will be\n "
532 "listed.\n", c_name);
533 }
534
535 static int
do_list_nsid(int fd,const nvme_process_arg_t * npa)536 do_list_nsid(int fd, const nvme_process_arg_t *npa)
537 {
538 _NOTE(ARGUNUSED(fd));
539 const uint_t format = npa->npa_idns->id_flbas.lba_format;
540 const uint_t bshift = npa->npa_idns->id_lbaf[format].lbaf_lbads;
541
542 /*
543 * Some devices have extra namespaces with illegal block sizes and
544 * zero blocks. Don't list them when verbose operation isn't requested.
545 */
546 if ((bshift < 9 || npa->npa_idns->id_nsize == 0) && verbose == 0)
547 return (0);
548
549 (void) printf(" %s/%s (%s): ", npa->npa_name,
550 di_minor_name(npa->npa_minor),
551 npa->npa_dsk != NULL ? npa->npa_dsk : "unattached");
552 nvme_print_nsid_summary(npa->npa_idns);
553
554 return (0);
555 }
556
557 static int
do_list(int fd,const nvme_process_arg_t * npa)558 do_list(int fd, const nvme_process_arg_t *npa)
559 {
560 _NOTE(ARGUNUSED(fd));
561
562 nvme_process_arg_t ns_npa = { 0 };
563 nvmeadm_cmd_t cmd = { 0 };
564 char *name;
565
566 if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
567 di_instance(npa->npa_node)) < 0)
568 err(-1, "do_list()");
569
570 (void) printf("%s: ", name);
571 nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version);
572
573 ns_npa.npa_name = name;
574 ns_npa.npa_isns = B_TRUE;
575 ns_npa.npa_nsid = npa->npa_nsid;
576 cmd = *(npa->npa_cmd);
577 cmd.c_func = do_list_nsid;
578 ns_npa.npa_cmd = &cmd;
579
580 nvme_walk(&ns_npa, npa->npa_node);
581
582 free(name);
583
584 return (exitcode);
585 }
586
587 static void
usage_identify(const char * c_name)588 usage_identify(const char *c_name)
589 {
590 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n"
591 " Print detailed information about the specified NVMe "
592 "controllers and/or name-\n spaces.\n", c_name);
593 }
594
595 static int
do_identify(int fd,const nvme_process_arg_t * npa)596 do_identify(int fd, const nvme_process_arg_t *npa)
597 {
598 if (!npa->npa_isns) {
599 nvme_capabilities_t *cap;
600
601 cap = nvme_capabilities(fd);
602 if (cap == NULL)
603 return (-1);
604
605 (void) printf("%s: ", npa->npa_name);
606 nvme_print_identify_ctrl(npa->npa_idctl, cap,
607 npa->npa_version);
608
609 free(cap);
610 } else {
611 (void) printf("%s/%s: ", npa->npa_name,
612 di_minor_name(npa->npa_minor));
613 nvme_print_identify_nsid(npa->npa_idns,
614 npa->npa_version);
615 }
616
617 return (0);
618 }
619
620 static void
usage_get_logpage(const char * c_name)621 usage_get_logpage(const char *c_name)
622 {
623 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n"
624 " Print the specified log page of the specified NVMe "
625 "controllers and/or name-\n spaces. Supported log pages "
626 "are error, health, and firmware.\n", c_name);
627 }
628
629 static int
do_get_logpage_error(int fd,const nvme_process_arg_t * npa)630 do_get_logpage_error(int fd, const nvme_process_arg_t *npa)
631 {
632 int nlog = npa->npa_idctl->id_elpe + 1;
633 size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog;
634 nvme_error_log_entry_t *elog;
635
636 if (npa->npa_isns)
637 errx(-1, "Error Log not available on a per-namespace basis");
638
639 elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize);
640
641 if (elog == NULL)
642 return (-1);
643
644 nlog = bufsize / sizeof (nvme_error_log_entry_t);
645
646 (void) printf("%s: ", npa->npa_name);
647 nvme_print_error_log(nlog, elog);
648
649 free(elog);
650
651 return (0);
652 }
653
654 static int
do_get_logpage_health(int fd,const nvme_process_arg_t * npa)655 do_get_logpage_health(int fd, const nvme_process_arg_t *npa)
656 {
657 size_t bufsize = sizeof (nvme_health_log_t);
658 nvme_health_log_t *hlog;
659
660 if (npa->npa_isns) {
661 if (npa->npa_idctl->id_lpa.lp_smart == 0)
662 errx(-1, "SMART/Health information not available "
663 "on a per-namespace basis on this controller");
664 }
665
666 hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
667
668 if (hlog == NULL)
669 return (-1);
670
671 (void) printf("%s: ", npa->npa_name);
672 nvme_print_health_log(hlog, npa->npa_idctl);
673
674 free(hlog);
675
676 return (0);
677 }
678
679 static int
do_get_logpage_fwslot(int fd,const nvme_process_arg_t * npa)680 do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa)
681 {
682 size_t bufsize = sizeof (nvme_fwslot_log_t);
683 nvme_fwslot_log_t *fwlog;
684
685 if (npa->npa_isns)
686 errx(-1, "Firmware Slot information not available on a "
687 "per-namespace basis");
688
689 fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize);
690
691 if (fwlog == NULL)
692 return (-1);
693
694 (void) printf("%s: ", npa->npa_name);
695 nvme_print_fwslot_log(fwlog);
696
697 free(fwlog);
698
699 return (0);
700 }
701
702 static int
do_get_logpage(int fd,const nvme_process_arg_t * npa)703 do_get_logpage(int fd, const nvme_process_arg_t *npa)
704 {
705 int ret = 0;
706 int (*func)(int, const nvme_process_arg_t *);
707
708 if (npa->npa_argc < 1) {
709 warnx("missing logpage name");
710 usage(npa->npa_cmd);
711 exit(-1);
712 }
713
714 if (strcmp(npa->npa_argv[0], "error") == 0)
715 func = do_get_logpage_error;
716 else if (strcmp(npa->npa_argv[0], "health") == 0)
717 func = do_get_logpage_health;
718 else if (strcmp(npa->npa_argv[0], "firmware") == 0)
719 func = do_get_logpage_fwslot;
720 else
721 errx(-1, "invalid log page: %s", npa->npa_argv[0]);
722
723 ret = func(fd, npa);
724 return (ret);
725 }
726
727 static void
usage_get_features(const char * c_name)728 usage_get_features(const char *c_name)
729 {
730 const nvme_feature_t *feat;
731
732 (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n"
733 " Print the specified features of the specified NVMe controllers "
734 "and/or\n namespaces. Supported features are:\n\n", c_name);
735 (void) fprintf(stderr, " %-35s %-14s %s\n",
736 "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE");
737 for (feat = &features[0]; feat->f_feature != 0; feat++) {
738 char *type;
739
740 if ((feat->f_getflags & NVMEADM_BOTH) == NVMEADM_BOTH)
741 type = "both";
742 else if ((feat->f_getflags & NVMEADM_CTRL) != 0)
743 type = "controller only";
744 else
745 type = "namespace only";
746
747 (void) fprintf(stderr, " %-35s %-14s %s\n",
748 feat->f_name, feat->f_short, type);
749 }
750
751 }
752
753 static int
do_get_feat_common(int fd,const nvme_feature_t * feat,nvme_identify_ctrl_t * idctl)754 do_get_feat_common(int fd, const nvme_feature_t *feat,
755 nvme_identify_ctrl_t *idctl)
756 {
757 void *buf = NULL;
758 size_t bufsize = feat->f_bufsize;
759 uint64_t res;
760
761 if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf)
762 == B_FALSE)
763 return (EINVAL);
764
765 nvme_print(2, feat->f_name, -1, NULL);
766 feat->f_print(res, buf, bufsize, idctl);
767 free(buf);
768
769 return (0);
770 }
771
772 static int
do_get_feat_intr_vect(int fd,const nvme_feature_t * feat,nvme_identify_ctrl_t * idctl)773 do_get_feat_intr_vect(int fd, const nvme_feature_t *feat,
774 nvme_identify_ctrl_t *idctl)
775 {
776 uint64_t res;
777 uint64_t arg;
778 int intr_cnt;
779
780 intr_cnt = nvme_intr_cnt(fd);
781
782 if (intr_cnt == -1)
783 return (EINVAL);
784
785 nvme_print(2, feat->f_name, -1, NULL);
786
787 for (arg = 0; arg < intr_cnt; arg++) {
788 if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL)
789 == B_FALSE)
790 return (EINVAL);
791
792 feat->f_print(res, NULL, 0, idctl);
793 }
794
795 return (0);
796 }
797
798 static int
do_get_features(int fd,const nvme_process_arg_t * npa)799 do_get_features(int fd, const nvme_process_arg_t *npa)
800 {
801 const nvme_feature_t *feat;
802 char *f, *flist, *lasts;
803 boolean_t header_printed = B_FALSE;
804
805 if (npa->npa_argc > 1)
806 errx(-1, "unexpected arguments");
807
808 /*
809 * No feature list given, print all supported features.
810 */
811 if (npa->npa_argc == 0) {
812 (void) printf("%s: Get Features\n", npa->npa_name);
813 for (feat = &features[0]; feat->f_feature != 0; feat++) {
814 if ((npa->npa_isns &&
815 (feat->f_getflags & NVMEADM_NS) == 0) ||
816 (!npa->npa_isns &&
817 (feat->f_getflags & NVMEADM_CTRL) == 0))
818 continue;
819
820 (void) feat->f_get(fd, feat, npa->npa_idctl);
821 }
822
823 return (0);
824 }
825
826 /*
827 * Process feature list.
828 */
829 flist = strdup(npa->npa_argv[0]);
830 if (flist == NULL)
831 err(-1, "do_get_features");
832
833 for (f = strtok_r(flist, ",", &lasts);
834 f != NULL;
835 f = strtok_r(NULL, ",", &lasts)) {
836 while (isspace(*f))
837 f++;
838
839 for (feat = &features[0]; feat->f_feature != 0; feat++) {
840 if (strncasecmp(feat->f_name, f, strlen(f)) == 0 ||
841 strncasecmp(feat->f_short, f, strlen(f)) == 0)
842 break;
843 }
844
845 if (feat->f_feature == 0) {
846 warnx("unknown feature %s", f);
847 continue;
848 }
849
850 if ((npa->npa_isns &&
851 (feat->f_getflags & NVMEADM_NS) == 0) ||
852 (!npa->npa_isns &&
853 (feat->f_getflags & NVMEADM_CTRL) == 0)) {
854 warnx("feature %s %s supported for namespaces",
855 feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ?
856 "only" : "not");
857 continue;
858 }
859
860 if (!header_printed) {
861 (void) printf("%s: Get Features\n", npa->npa_name);
862 header_printed = B_TRUE;
863 }
864
865 if (feat->f_get(fd, feat, npa->npa_idctl) != 0) {
866 warnx("unsupported feature: %s", feat->f_name);
867 continue;
868 }
869 }
870
871 free(flist);
872 return (0);
873 }
874
875 static int
do_format_common(int fd,const nvme_process_arg_t * npa,unsigned long lbaf,unsigned long ses)876 do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf,
877 unsigned long ses)
878 {
879 nvme_process_arg_t ns_npa = { 0 };
880 nvmeadm_cmd_t cmd = { 0 };
881
882 cmd = *(npa->npa_cmd);
883 cmd.c_func = do_attach_detach;
884 cmd.c_name = "detach";
885 ns_npa = *npa;
886 ns_npa.npa_cmd = &cmd;
887
888 if (do_attach_detach(fd, &ns_npa) != 0)
889 return (exitcode);
890 if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) {
891 warn("%s failed", npa->npa_cmd->c_name);
892 exitcode += -1;
893 }
894 cmd.c_name = "attach";
895 exitcode += do_attach_detach(fd, &ns_npa);
896
897 return (exitcode);
898 }
899
900 static void
usage_format(const char * c_name)901 usage_format(const char *c_name)
902 {
903 (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n"
904 " Format one or all namespaces of the specified NVMe "
905 "controller. Supported LBA\n formats can be queried with "
906 "the \"%s identify\" command on the namespace\n to be "
907 "formatted.\n", c_name, getprogname());
908 }
909
910 static int
do_format(int fd,const nvme_process_arg_t * npa)911 do_format(int fd, const nvme_process_arg_t *npa)
912 {
913 unsigned long lbaf;
914
915 if (npa->npa_idctl->id_oacs.oa_format == 0)
916 errx(-1, "%s not supported", npa->npa_cmd->c_name);
917
918 if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0)
919 errx(-1, "%s not supported on individual namespace",
920 npa->npa_cmd->c_name);
921
922
923 if (npa->npa_argc > 0) {
924 errno = 0;
925 lbaf = strtoul(npa->npa_argv[0], NULL, 10);
926
927 if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF)
928 errx(-1, "invalid LBA format %d", lbaf + 1);
929
930 if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0)
931 errx(-1, "LBA formats with metadata not supported");
932 } else {
933 lbaf = npa->npa_idns->id_flbas.lba_format;
934 }
935
936 return (do_format_common(fd, npa, lbaf, 0));
937 }
938
939 static void
usage_secure_erase(const char * c_name)940 usage_secure_erase(const char *c_name)
941 {
942 (void) fprintf(stderr, "%s <ctl>[/<ns>] [-c]\n\n"
943 " Secure-Erase one or all namespaces of the specified "
944 "NVMe controller.\n", c_name);
945 }
946
947 static int
do_secure_erase(int fd,const nvme_process_arg_t * npa)948 do_secure_erase(int fd, const nvme_process_arg_t *npa)
949 {
950 unsigned long lbaf;
951 uint8_t ses = NVME_FRMT_SES_USER;
952
953 if (npa->npa_idctl->id_oacs.oa_format == 0)
954 errx(-1, "%s not supported", npa->npa_cmd->c_name);
955
956 if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0)
957 errx(-1, "%s not supported on individual namespace",
958 npa->npa_cmd->c_name);
959
960 if (npa->npa_argc > 0) {
961 if (strcmp(npa->npa_argv[0], "-c") == 0)
962 ses = NVME_FRMT_SES_CRYPTO;
963 else
964 usage(npa->npa_cmd);
965 }
966
967 if (ses == NVME_FRMT_SES_CRYPTO &&
968 npa->npa_idctl->id_fna.fn_crypt_erase == 0)
969 errx(-1, "cryptographic %s not supported",
970 npa->npa_cmd->c_name);
971
972 lbaf = npa->npa_idns->id_flbas.lba_format;
973
974 return (do_format_common(fd, npa, lbaf, ses));
975 }
976
977 static void
usage_attach_detach(const char * c_name)978 usage_attach_detach(const char *c_name)
979 {
980 (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n"
981 " %c%s blkdev(7d) %s one or all namespaces of the "
982 "specified NVMe controller.\n",
983 c_name, toupper(c_name[0]), &c_name[1],
984 c_name[0] == 'd' ? "from" : "to");
985 }
986
987 static int
do_attach_detach(int fd,const nvme_process_arg_t * npa)988 do_attach_detach(int fd, const nvme_process_arg_t *npa)
989 {
990 char *c_name = npa->npa_cmd->c_name;
991
992 if (!npa->npa_isns) {
993 nvme_process_arg_t ns_npa = { 0 };
994
995 ns_npa.npa_name = npa->npa_name;
996 ns_npa.npa_isns = B_TRUE;
997 ns_npa.npa_cmd = npa->npa_cmd;
998
999 nvme_walk(&ns_npa, npa->npa_node);
1000
1001 return (exitcode);
1002 } else {
1003 if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd)
1004 == B_FALSE) {
1005 warn("%s failed", c_name);
1006 return (-1);
1007 }
1008 }
1009
1010 return (0);
1011 }
1012