]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/passthru.c
MFC r350058 (by imp): Implement {io,admin}-passthru commands.
[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 const 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         void    *data = NULL, *metadata = NULL;
162         struct nvme_pt_command  pt;
163
164         arg_parse(argc, argv, f);
165         open_dev(argv[optind], &fd, 1, 1);
166
167         if (opt.read && opt.write)
168                 errx(1, "need exactly one of --read or --write");
169         if (opt.data_len != 0 && !opt.read && !opt.write)
170                 errx(1, "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(1, "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 && read(ifd, data, opt.data_len) < 0) {
193                         warn("read %s", *opt.ifn ? opt.ifn : "stdin");
194                         goto cleanup;
195                 }
196         }
197         if (opt.show_command) {
198                 fprintf(stderr, "opcode       : %#02x\n", opt.opcode);
199                 fprintf(stderr, "flags        : %#02x\n", opt.flags);
200                 fprintf(stderr, "rsvd1        : %#04x\n", opt.rsvd);
201                 fprintf(stderr, "nsid         : %#04x\n", opt.nsid);
202                 fprintf(stderr, "cdw2         : %#08x\n", opt.cdw2);
203                 fprintf(stderr, "cdw3         : %#08x\n", opt.cdw3);
204                 fprintf(stderr, "data_len     : %#08x\n", opt.data_len);
205                 fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len);
206                 fprintf(stderr, "data         : %p\n", data);
207                 fprintf(stderr, "metadata     : %p\n", metadata);
208                 fprintf(stderr, "cdw10        : %#08x\n", opt.cdw10);
209                 fprintf(stderr, "cdw11        : %#08x\n", opt.cdw11);
210                 fprintf(stderr, "cdw12        : %#08x\n", opt.cdw12);
211                 fprintf(stderr, "cdw13        : %#08x\n", opt.cdw13);
212                 fprintf(stderr, "cdw14        : %#08x\n", opt.cdw14);
213                 fprintf(stderr, "cdw15        : %#08x\n", opt.cdw15);
214                 fprintf(stderr, "timeout_ms   : %d\n", opt.timeout);
215         }
216         if (opt.dry_run) {
217                 errno = 0;
218                 warn("Doing a dry-run, no actual I/O");
219                 goto cleanup;
220         }
221
222         memset(&pt, 0, sizeof(pt));
223         pt.cmd.opc = opt.opcode;
224         pt.cmd.fuse = opt.flags;
225         pt.cmd.cid = htole16(opt.rsvd);
226         pt.cmd.nsid = opt.nsid;                         /* XXX note: kernel overrides this */
227         pt.cmd.rsvd2 = htole32(opt.cdw2);
228         pt.cmd.rsvd3 = htole32(opt.cdw3);
229         pt.cmd.cdw10 = htole32(opt.cdw10);
230         pt.cmd.cdw11 = htole32(opt.cdw11);
231         pt.cmd.cdw12 = htole32(opt.cdw12);
232         pt.cmd.cdw13 = htole32(opt.cdw13);
233         pt.cmd.cdw14 = htole32(opt.cdw14);
234         pt.cmd.cdw15 = htole32(opt.cdw15);
235         pt.buf = data;
236         pt.len = opt.data_len;
237         pt.is_read = opt.read;
238
239         errno = 0;
240         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
241                 err(1, "passthrough request failed");
242         /* XXX report status */
243         if (opt.read) {
244                 if (opt.binary)
245                         write(STDOUT_FILENO, data, opt.data_len);
246                 else {
247                         /* print status here */
248                         print_hex(data, opt.data_len);
249                 }
250         }
251 cleanup:
252         if (errno)
253                 exit(1);
254 }
255
256 static void
257 admin_passthru(const struct cmd *nf, int argc, char *argv[])
258 {
259
260         passthru(nf, argc, argv);
261 }
262
263 static void
264 io_passthru(const struct cmd *nf, int argc, char *argv[])
265 {
266
267         passthru(nf, argc, argv);
268 }
269
270 CMD_COMMAND(top, admin-passthru, admin_passthru, sizeof(struct options), opts, args,
271     "Send a pass through Admin command to the specified device");
272 CMD_COMMAND(top, io-passthru, io_passthru, sizeof(struct options), opts, args,
273     "Send a pass through I/O command to the specified device");