]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/passthru.c
zfs: merge openzfs/zfs@2e2a46e0a
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / passthru.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019-2021 Netflix, Inc
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 #include <sys/param.h>
30 #include <sys/ioccom.h>
31
32 #include <err.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sysexits.h>
40 #include <unistd.h>
41
42 #include "nvmecontrol.h"
43 #include "comnd.h"
44
45 static struct options {
46         uint8_t         opcode;
47         uint8_t         flags;
48         uint16_t        rsvd;
49         uint32_t        nsid;
50         uint32_t        data_len;
51         uint32_t        metadata_len;
52         uint32_t        timeout;
53         uint32_t        cdw2;
54         uint32_t        cdw3;
55         uint32_t        cdw10;
56         uint32_t        cdw11;
57         uint32_t        cdw12;
58         uint32_t        cdw13;
59         uint32_t        cdw14;
60         uint32_t        cdw15;
61         const char      *ifn;
62         bool            binary;
63         bool            show_command;
64         bool            dry_run;
65         bool            read;
66         bool            write;
67         uint8_t         prefill;
68         const char      *dev;
69 } opt = {
70         .binary = false,
71         .cdw10 = 0,
72         .cdw11 = 0,
73         .cdw12 = 0,
74         .cdw13 = 0,
75         .cdw14 = 0,
76         .cdw15 = 0,
77         .cdw2 = 0,
78         .cdw3 = 0,
79         .data_len = 0,
80         .dry_run = false,
81         .flags = 0,
82         .ifn = "",
83         .metadata_len = 0,
84         .nsid = 0,
85         .opcode = 0,
86         .prefill = 0,
87         .read = false,
88         .rsvd = 0,
89         .show_command = false,
90         .timeout = 0,
91         .write = false,
92         .dev = NULL,
93 };
94
95 /*
96  * Argument names and short names selected to match the nvme-cli program
97  * so vendor-siupplied formulas work out of the box on FreeBSD with a simple
98  * s/nvme/nvmecontrol/.
99  */
100 #define ARG(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
101
102 static struct opts opts[] = {
103         ARG("opcode",           'o',    arg_uint8,      opt, opcode,
104             "NVMe command opcode (required)"),
105         ARG("cdw2",             '2',    arg_uint32,     opt, cdw2,
106             "Command dword 2 value"),
107         ARG("cdw3",             '3',    arg_uint32,     opt, cdw3,
108             "Command dword 3 value"),
109         ARG("cdw10",            '4',    arg_uint32,     opt, cdw10,
110             "Command dword 10 value"),
111         ARG("cdw11",            '5',    arg_uint32,     opt, cdw11,
112             "Command dword 11 value"),
113         ARG("cdw12",            '6',    arg_uint32,     opt, cdw12,
114             "Command dword 12 value"),
115         ARG("cdw13",            '7',    arg_uint32,     opt, cdw13,
116             "Command dword 13 value"),
117         ARG("cdw14",            '8',    arg_uint32,     opt, cdw14,
118             "Command dword 14 value"),
119         ARG("cdw15",            '9',    arg_uint32,     opt, cdw15,
120             "Command dword 15 value"),
121         ARG("data-len",         'l',    arg_uint32,     opt, data_len,
122             "Length of data for I/O (bytes)"),
123         ARG("metadata-len",     'm',    arg_uint32,     opt, metadata_len,
124             "Length of metadata segment (bytes) (ignored)"),
125         ARG("flags",            'f',    arg_uint8,      opt, flags,
126             "NVMe command flags"),
127         ARG("input-file",       'i',    arg_path,       opt, ifn,
128             "Input file to send (default stdin)"),
129         ARG("namespace-id",     'n',    arg_uint32,     opt, nsid,
130             "Namespace id (ignored on FreeBSD)"),
131         ARG("prefill",          'p',    arg_uint8,      opt, prefill,
132             "Value to prefill payload with"),
133         ARG("rsvd",             'R',    arg_uint16,     opt, rsvd,
134             "Reserved field value"),
135         ARG("timeout",          't',    arg_uint32,     opt, timeout,
136             "Command timeout (ms)"),
137         ARG("raw-binary",       'b',    arg_none,       opt, binary,
138             "Output in binary format"),
139         ARG("dry-run",          'd',    arg_none,       opt, dry_run,
140             "Don't actually execute the command"),
141         ARG("read",             'r',    arg_none,       opt, read,
142             "Command reads data from device"),
143         ARG("show-command",     's',    arg_none,       opt, show_command,
144             "Show all the command values on stdout"),
145         ARG("write",            'w',    arg_none,       opt, write,
146             "Command writes data to device"),
147         { NULL, 0, arg_none, NULL, NULL }
148 };
149
150 static const struct args args[] = {
151         { arg_string, &opt.dev, "controller-id|namespace-id" },
152         { arg_none, NULL, NULL },
153 };
154
155 static void
156 passthru(const struct cmd *f, int argc, char *argv[])
157 {
158         int     fd = -1, ifd = -1;
159         size_t  bytes_read;
160         void    *data = NULL, *metadata = NULL;
161         struct nvme_pt_command  pt;
162
163         if (arg_parse(argc, argv, f))
164                 return;
165         open_dev(opt.dev, &fd, 1, 1);
166
167         if (opt.read && opt.write)
168                 errx(EX_USAGE, "need exactly one of --read or --write");
169         if (opt.data_len != 0 && !opt.read && !opt.write)
170                 errx(EX_USAGE, "need exactly one of --read or --write");
171         if (*opt.ifn && (ifd = open(opt.ifn, O_RDONLY)) == -1) {
172                 warn("open %s", opt.ifn);
173                 goto cleanup;
174         }
175 #if notyet      /* No support in kernel for this */
176         if (opt.metadata_len != 0) {
177                 if (posix_memalign(&metadata, getpagesize(), opt.metadata_len)) {
178                         warn("can't allocate %d bytes for metadata", metadata_len);
179                         goto cleanup;
180                 }
181         }
182 #else
183         if (opt.metadata_len != 0)
184                 errx(EX_UNAVAILABLE, "metadata not supported on FreeBSD");
185 #endif
186         if (opt.data_len) {
187                 if (posix_memalign(&data, getpagesize(), opt.data_len)) {
188                         warn("can't allocate %d bytes for data", opt.data_len);
189                         goto cleanup;
190                 }
191                 memset(data, opt.prefill, opt.data_len);
192                 if (opt.write &&
193                     (bytes_read = read(ifd, data, opt.data_len)) !=
194                     opt.data_len) {
195                         warn("read %s; expected %u bytes; got %zd",
196                              *opt.ifn ? opt.ifn : "stdin",
197                              opt.data_len, bytes_read);
198                         goto cleanup;
199                 }
200         }
201         if (opt.show_command) {
202                 fprintf(stderr, "opcode       : %#02x\n", opt.opcode);
203                 fprintf(stderr, "flags        : %#02x\n", opt.flags);
204                 fprintf(stderr, "rsvd1        : %#04x\n", opt.rsvd);
205                 fprintf(stderr, "nsid         : %#04x\n", opt.nsid);
206                 fprintf(stderr, "cdw2         : %#08x\n", opt.cdw2);
207                 fprintf(stderr, "cdw3         : %#08x\n", opt.cdw3);
208                 fprintf(stderr, "data_len     : %#08x\n", opt.data_len);
209                 fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len);
210                 fprintf(stderr, "data         : %p\n", data);
211                 fprintf(stderr, "metadata     : %p\n", metadata);
212                 fprintf(stderr, "cdw10        : %#08x\n", opt.cdw10);
213                 fprintf(stderr, "cdw11        : %#08x\n", opt.cdw11);
214                 fprintf(stderr, "cdw12        : %#08x\n", opt.cdw12);
215                 fprintf(stderr, "cdw13        : %#08x\n", opt.cdw13);
216                 fprintf(stderr, "cdw14        : %#08x\n", opt.cdw14);
217                 fprintf(stderr, "cdw15        : %#08x\n", opt.cdw15);
218                 fprintf(stderr, "timeout_ms   : %d\n", opt.timeout);
219         }
220         if (opt.dry_run) {
221                 errno = 0;
222                 warn("Doing a dry-run, no actual I/O");
223                 goto cleanup;
224         }
225
226         memset(&pt, 0, sizeof(pt));
227         pt.cmd.opc = opt.opcode;
228         pt.cmd.fuse = opt.flags;
229         pt.cmd.cid = htole16(opt.rsvd);
230         pt.cmd.nsid = opt.nsid;                         /* XXX note: kernel overrides this */
231         pt.cmd.rsvd2 = htole32(opt.cdw2);
232         pt.cmd.rsvd3 = htole32(opt.cdw3);
233         pt.cmd.cdw10 = htole32(opt.cdw10);
234         pt.cmd.cdw11 = htole32(opt.cdw11);
235         pt.cmd.cdw12 = htole32(opt.cdw12);
236         pt.cmd.cdw13 = htole32(opt.cdw13);
237         pt.cmd.cdw14 = htole32(opt.cdw14);
238         pt.cmd.cdw15 = htole32(opt.cdw15);
239         pt.buf = data;
240         pt.len = opt.data_len;
241         pt.is_read = opt.read;
242
243         errno = 0;
244         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
245                 err(EX_IOERR, "passthrough request failed");
246         if (!opt.binary)
247                 printf("DWORD0 status= %#x\n", pt.cpl.cdw0);
248         if (opt.read) {
249                 if (opt.binary)
250                         write(STDOUT_FILENO, data, opt.data_len);
251                 else {
252                         /* print status here */
253                         print_hex(data, opt.data_len);
254                 }
255         }
256 cleanup:
257         free(data);
258         close(fd);
259         if (ifd > -1)
260                 close(ifd);
261         if (errno)
262                 exit(EX_IOERR);
263 }
264
265 static void
266 admin_passthru(const struct cmd *nf, int argc, char *argv[])
267 {
268
269         passthru(nf, argc, argv);
270 }
271
272 static void
273 io_passthru(const struct cmd *nf, int argc, char *argv[])
274 {
275
276         passthru(nf, argc, argv);
277 }
278
279 static struct cmd admin_pass_cmd = {
280         .name = "admin-passthru",
281         .fn = admin_passthru,
282         .ctx_size = sizeof(struct options),
283         .opts = opts,
284         .args = args,
285         .descr = "Send a pass through Admin command to the specified device",
286 };
287
288 static struct cmd io_pass_cmd = {
289         .name = "io-passthru",
290         .fn = io_passthru,
291         .ctx_size = sizeof(struct options),
292         .opts = opts,
293         .args = args,
294         .descr = "Send a pass through I/O command to the specified device",
295 };
296
297 CMD_COMMAND(admin_pass_cmd);
298 CMD_COMMAND(io_pass_cmd);