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