]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/sanitize.c
Document the passthru commands.
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / sanitize.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2019 Alexander Motin <mav@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/ioccom.h>
33
34 #include <ctype.h>
35 #include <err.h>
36 #include <fcntl.h>
37 #include <stdbool.h>
38 #include <stddef.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include "nvmecontrol.h"
45
46 /* Tables for command line parsing */
47
48 static cmd_fn_t sanitize;
49
50 static struct options {
51         bool            ause;
52         bool            ndas;
53         bool            oipbp;
54         bool            reportonly;
55         uint8_t         owpass;
56         uint32_t        ovrpat;
57         const char      *sanact;
58         const char      *dev;
59 } opt = {
60         .ause = false,
61         .ndas = false,
62         .oipbp = false,
63         .reportonly = false,
64         .owpass = 1,
65         .ovrpat = 0,
66         .sanact = NULL,
67         .dev = NULL,
68 };
69
70 static const struct opts sanitize_opts[] = {
71 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
72         OPT("ause", 'U', arg_none, opt, ause,
73             "Allow Unrestricted Sanitize Exit"),
74         OPT("ndas", 'd', arg_none, opt, ndas,
75             "No Deallocate After Sanitize"),
76         OPT("oipbp", 'I', arg_none, opt, oipbp,
77             "Overwrite Invert Pattern Between Passes"),
78         OPT("reportonly", 'r', arg_none, opt, reportonly,
79             "Report previous sanitize status"),
80         OPT("owpass", 'c', arg_uint8, opt, owpass,
81             "Overwrite Pass Count"),
82         OPT("ovrpat", 'p', arg_uint32, opt, ovrpat,
83             "Overwrite Pattern"),
84         OPT("sanact", 'a', arg_string, opt, sanact,
85             "Sanitize Action (block, overwrite, crypto)"),
86         { NULL, 0, arg_none, NULL, NULL }
87 };
88 #undef OPT
89
90 static const struct args sanitize_args[] = {
91         { arg_string, &opt.dev, "controller-id" },
92         { arg_none, NULL, NULL },
93 };
94
95 static struct cmd sanitize_cmd = {
96         .name = "sanitize",
97         .fn = sanitize,
98         .descr = "Sanitize NVM subsystem",
99         .ctx_size = sizeof(opt),
100         .opts = sanitize_opts,
101         .args = sanitize_args,
102 };
103
104 CMD_COMMAND(sanitize_cmd);
105
106 /* End of tables for command line parsing */
107
108 static void
109 sanitize(const struct cmd *f, int argc, char *argv[])
110 {
111         struct nvme_controller_data     cd;
112         struct nvme_pt_command          pt;
113         struct nvme_sanitize_status_page ss;
114         char                            *path;
115         uint32_t                        nsid;
116         int                             sanact = 0, fd, delay = 1;
117
118         if (arg_parse(argc, argv, f))
119                 return;
120
121         if (opt.sanact == NULL) {
122                 if (!opt.reportonly) {
123                         fprintf(stderr, "Sanitize Action is not specified\n");
124                         arg_help(argc, argv, f);
125                 }
126         } else {
127                 if (strcmp(opt.sanact, "exitfailure") == 0)
128                         sanact = 1;
129                 else if (strcmp(opt.sanact, "block") == 0)
130                         sanact = 2;
131                 else if (strcmp(opt.sanact, "overwrite") == 0)
132                         sanact = 3;
133                 else if (strcmp(opt.sanact, "crypto") == 0)
134                         sanact = 4;
135                 else {
136                         fprintf(stderr, "Incorrect Sanitize Action value\n");
137                         arg_help(argc, argv, f);
138                 }
139         }
140         if (opt.owpass == 0 || opt.owpass > 16) {
141                 fprintf(stderr, "Incorrect Overwrite Pass Count value\n");
142                 arg_help(argc, argv, f);
143         }
144
145         open_dev(opt.dev, &fd, 1, 1);
146         get_nsid(fd, &path, &nsid);
147         if (nsid != 0) {
148                 close(fd);
149                 open_dev(path, &fd, 1, 1);
150         }
151         free(path);
152
153         if (opt.reportonly)
154                 goto wait;
155
156         /* Check that controller can execute this command. */
157         read_controller_data(fd, &cd);
158         if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) &
159              NVME_CTRLR_DATA_SANICAP_BES_MASK) == 0 && sanact == 2)
160                 errx(1, "controller does not support Block Erase");
161         if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) &
162              NVME_CTRLR_DATA_SANICAP_OWS_MASK) == 0 && sanact == 3)
163                 errx(1, "controller does not support Overwrite");
164         if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) &
165              NVME_CTRLR_DATA_SANICAP_CES_MASK) == 0 && sanact == 4)
166                 errx(1, "controller does not support Crypto Erase");
167
168         /*
169          * If controller supports only one namespace, we may sanitize it.
170          * If there can be more, make user explicit in his commands.
171          */
172         if (nsid != 0 && cd.nn > 1)
173                 errx(1, "can't sanitize one of namespaces, specify controller");
174
175         memset(&pt, 0, sizeof(pt));
176         pt.cmd.opc = NVME_OPC_SANITIZE;
177         pt.cmd.cdw10 = htole32((opt.ndas << 9) | (opt.oipbp << 8) |
178             ((opt.owpass & 0xf) << 4) | (opt.ause << 3) | sanact);
179         pt.cmd.cdw11 = htole32(opt.ovrpat);
180
181         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
182                 err(1, "sanitize request failed");
183
184         if (nvme_completion_is_error(&pt.cpl))
185                 errx(1, "sanitize request returned error");
186
187 wait:
188         read_logpage(fd, NVME_LOG_SANITIZE_STATUS,
189             NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &ss, sizeof(ss));
190         switch ((ss.sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) &
191             NVME_SS_PAGE_SSTAT_STATUS_MASK) {
192         case NVME_SS_PAGE_SSTAT_STATUS_NEVER:
193                 printf("Never sanitized");
194                 break;
195         case NVME_SS_PAGE_SSTAT_STATUS_COMPLETED:
196                 printf("Sanitize completed");
197                 break;
198         case NVME_SS_PAGE_SSTAT_STATUS_INPROG:
199                 printf("Sanitize in progress: %u%% (%u/65535)\r",
200                     (ss.sprog * 100 + 32768) / 65536, ss.sprog);
201                 fflush(stdout);
202                 if (delay < 16)
203                         delay++;
204                 sleep(delay);
205                 goto wait;
206         case NVME_SS_PAGE_SSTAT_STATUS_FAILED:
207                 printf("Sanitize failed");
208                 break;
209         case NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD:
210                 printf("Sanitize completed with deallocation");
211                 break;
212         default:
213                 printf("Sanitize status unknown");
214                 break;
215         }
216         if (delay > 1)
217                 printf("                       ");
218         printf("\n");
219
220         close(fd);
221         exit(0);
222 }