]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/passthru.c
sysctl(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / passthru.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2012-2013 Intel Corporation
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/ioccom.h>
34
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sysexits.h>
43 #include <unistd.h>
44
45 #include "nvmecontrol.h"
46 #include "comnd.h"
47
48 static struct options {
49         uint8_t         opcode;
50         uint8_t         flags;
51         uint16_t        rsvd;
52         uint32_t        nsid;
53         uint32_t        data_len;
54         uint32_t        metadata_len;
55         uint32_t        timeout;
56         uint32_t        cdw2;
57         uint32_t        cdw3;
58         uint32_t        cdw10;
59         uint32_t        cdw11;
60         uint32_t        cdw12;
61         uint32_t        cdw13;
62         uint32_t        cdw14;
63         uint32_t        cdw15;
64         const char      *ifn;
65         bool            binary;
66         bool            show_command;
67         bool            dry_run;
68         bool            read;
69         bool            write;
70         uint8_t         prefill;
71         const char      *dev;
72 } opt = {
73         .binary = false,
74         .cdw10 = 0,
75         .cdw11 = 0,
76         .cdw12 = 0,
77         .cdw13 = 0,
78         .cdw14 = 0,
79         .cdw15 = 0,
80         .cdw2 = 0,
81         .cdw3 = 0,
82         .data_len = 0,
83         .dry_run = false,
84         .flags = 0,
85         .ifn = "",
86         .metadata_len = 0,
87         .nsid = 0,
88         .opcode = 0,
89         .prefill = 0,
90         .read = false,
91         .rsvd = 0,
92         .show_command = false,
93         .timeout = 0,
94         .write = false,
95         .dev = NULL,
96 };
97
98 /*
99  * Argument names and short names selected to match the nvme-cli program
100  * so vendor-siupplied formulas work out of the box on FreeBSD with a simple
101  * s/nvme/nvmecontrol/.
102  */
103 #define ARG(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
104
105 static struct opts opts[] = {
106         ARG("opcode",           'o',    arg_uint8,      opt, opcode,
107             "NVMe command opcode (required)"),
108         ARG("cdw2",             '2',    arg_uint32,     opt, cdw2,
109             "Command dword 2 value"),
110         ARG("cdw3",             '3',    arg_uint32,     opt, cdw3,
111             "Command dword 3 value"),
112         ARG("cdw10",            '4',    arg_uint32,     opt, cdw10,
113             "Command dword 10 value"),
114         ARG("cdw11",            '5',    arg_uint32,     opt, cdw11,
115             "Command dword 11 value"),
116         ARG("cdw12",            '6',    arg_uint32,     opt, cdw12,
117             "Command dword 12 value"),
118         ARG("cdw13",            '7',    arg_uint32,     opt, cdw13,
119             "Command dword 13 value"),
120         ARG("cdw14",            '8',    arg_uint32,     opt, cdw14,
121             "Command dword 14 value"),
122         ARG("cdw15",            '9',    arg_uint32,     opt, cdw15,
123             "Command dword 15 value"),
124         ARG("data-len",         'l',    arg_uint32,     opt, data_len,
125             "Length of data for I/O (bytes)"),
126         ARG("metadata-len",     'm',    arg_uint32,     opt, metadata_len,
127             "Length of metadata segment (bytes) (igored)"),
128         ARG("flags",            'f',    arg_uint8,      opt, flags,
129             "NVMe command flags"),
130         ARG("input-file",       'i',    arg_path,       opt, ifn,
131             "Input file to send (default stdin)"),
132         ARG("namespace-id",     'n',    arg_uint32,     opt, nsid,
133             "Namespace id (ignored on FreeBSD)"),
134         ARG("prefill",          'p',    arg_uint8,      opt, prefill,
135             "Value to prefill payload with"),
136         ARG("rsvd",             'R',    arg_uint16,     opt, rsvd,
137             "Reserved field value"),
138         ARG("timeout",          't',    arg_uint32,     opt, timeout,
139             "Command timeout (ms)"),
140         ARG("raw-binary",       'b',    arg_none,       opt, binary,
141             "Output in binary format"),
142         ARG("dry-run",          'd',    arg_none,       opt, dry_run,
143             "Don't actually execute the command"),
144         ARG("read",             'r',    arg_none,       opt, read,
145             "Command reads data from device"),
146         ARG("show-command",     's',    arg_none,       opt, show_command,
147             "Show all the command values on stdout"),
148         ARG("write",            'w',    arg_none,       opt, write,
149             "Command writes data to device"),
150         { NULL, 0, arg_none, NULL, NULL }
151 };
152
153 static const struct args args[] = {
154         { arg_string, &opt.dev, "controller-id|namespace-id" },
155         { arg_none, NULL, NULL },
156 };
157
158 static void
159 passthru(const struct cmd *f, int argc, char *argv[])
160 {
161         int     fd = -1, ifd = -1;
162         size_t  bytes_read;
163         void    *data = NULL, *metadata = NULL;
164         struct nvme_pt_command  pt;
165
166         if (arg_parse(argc, argv, f))
167                 return;
168         open_dev(opt.dev, &fd, 1, 1);
169
170         if (opt.read && opt.write)
171                 errx(EX_USAGE, "need exactly one of --read or --write");
172         if (opt.data_len != 0 && !opt.read && !opt.write)
173                 errx(EX_USAGE, "need exactly one of --read or --write");
174         if (*opt.ifn && (ifd = open(opt.ifn, O_RDONLY)) == -1) {
175                 warn("open %s", opt.ifn);
176                 goto cleanup;
177         }
178 #if notyet      /* No support in kernel for this */
179         if (opt.metadata_len != 0) {
180                 if (posix_memalign(&metadata, getpagesize(), opt.metadata_len)) {
181                         warn("can't allocate %d bytes for metadata", metadata_len);
182                         goto cleanup;
183                 }
184         }
185 #else
186         if (opt.metadata_len != 0)
187                 errx(EX_UNAVAILABLE, "metadata not supported on FreeBSD");
188 #endif
189         if (opt.data_len) {
190                 if (posix_memalign(&data, getpagesize(), opt.data_len)) {
191                         warn("can't allocate %d bytes for data", opt.data_len);
192                         goto cleanup;
193                 }
194                 memset(data, opt.prefill, opt.data_len);
195                 if (opt.write &&
196                     (bytes_read = read(ifd, data, opt.data_len)) !=
197                     opt.data_len) {
198                         warn("read %s; expected %u bytes; got %zd",
199                              *opt.ifn ? opt.ifn : "stdin",
200                              opt.data_len, bytes_read);
201                         goto cleanup;
202                 }
203         }
204         if (opt.show_command) {
205                 fprintf(stderr, "opcode       : %#02x\n", opt.opcode);
206                 fprintf(stderr, "flags        : %#02x\n", opt.flags);
207                 fprintf(stderr, "rsvd1        : %#04x\n", opt.rsvd);
208                 fprintf(stderr, "nsid         : %#04x\n", opt.nsid);
209                 fprintf(stderr, "cdw2         : %#08x\n", opt.cdw2);
210                 fprintf(stderr, "cdw3         : %#08x\n", opt.cdw3);
211                 fprintf(stderr, "data_len     : %#08x\n", opt.data_len);
212                 fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len);
213                 fprintf(stderr, "data         : %p\n", data);
214                 fprintf(stderr, "metadata     : %p\n", metadata);
215                 fprintf(stderr, "cdw10        : %#08x\n", opt.cdw10);
216                 fprintf(stderr, "cdw11        : %#08x\n", opt.cdw11);
217                 fprintf(stderr, "cdw12        : %#08x\n", opt.cdw12);
218                 fprintf(stderr, "cdw13        : %#08x\n", opt.cdw13);
219                 fprintf(stderr, "cdw14        : %#08x\n", opt.cdw14);
220                 fprintf(stderr, "cdw15        : %#08x\n", opt.cdw15);
221                 fprintf(stderr, "timeout_ms   : %d\n", opt.timeout);
222         }
223         if (opt.dry_run) {
224                 errno = 0;
225                 warn("Doing a dry-run, no actual I/O");
226                 goto cleanup;
227         }
228
229         memset(&pt, 0, sizeof(pt));
230         pt.cmd.opc = opt.opcode;
231         pt.cmd.fuse = opt.flags;
232         pt.cmd.cid = htole16(opt.rsvd);
233         pt.cmd.nsid = opt.nsid;                         /* XXX note: kernel overrides this */
234         pt.cmd.rsvd2 = htole32(opt.cdw2);
235         pt.cmd.rsvd3 = htole32(opt.cdw3);
236         pt.cmd.cdw10 = htole32(opt.cdw10);
237         pt.cmd.cdw11 = htole32(opt.cdw11);
238         pt.cmd.cdw12 = htole32(opt.cdw12);
239         pt.cmd.cdw13 = htole32(opt.cdw13);
240         pt.cmd.cdw14 = htole32(opt.cdw14);
241         pt.cmd.cdw15 = htole32(opt.cdw15);
242         pt.buf = data;
243         pt.len = opt.data_len;
244         pt.is_read = opt.read;
245
246         errno = 0;
247         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
248                 err(EX_IOERR, "passthrough request failed");
249         /* XXX report status */
250         if (opt.read) {
251                 if (opt.binary)
252                         write(STDOUT_FILENO, data, opt.data_len);
253                 else {
254                         /* print status here */
255                         print_hex(data, opt.data_len);
256                 }
257         }
258 cleanup:
259         free(data);
260         close(fd);
261         if (ifd > -1)
262                 close(ifd);
263         if (errno)
264                 exit(EX_IOERR);
265 }
266
267 static void
268 admin_passthru(const struct cmd *nf, int argc, char *argv[])
269 {
270
271         passthru(nf, argc, argv);
272 }
273
274 static void
275 io_passthru(const struct cmd *nf, int argc, char *argv[])
276 {
277
278         passthru(nf, argc, argv);
279 }
280
281 static struct cmd admin_pass_cmd = {
282         .name = "admin-passthru",
283         .fn = admin_passthru,
284         .ctx_size = sizeof(struct options),
285         .opts = opts,
286         .args = args,
287         .descr = "Send a pass through Admin command to the specified device",
288 };
289
290 static struct cmd io_pass_cmd = {
291         .name = "io-passthru",
292         .fn = io_passthru,
293         .ctx_size = sizeof(struct options),
294         .opts = opts,
295         .args = args,
296         .descr = "Send a pass through I/O command to the specified device",
297 };
298
299 CMD_COMMAND(admin_pass_cmd);
300 CMD_COMMAND(io_pass_cmd);