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
passthru(const struct cmd * f,int argc,char * argv[])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
admin_passthru(const struct cmd * nf,int argc,char * argv[])266 admin_passthru(const struct cmd *nf, int argc, char *argv[])
267 {
268
269 passthru(nf, argc, argv);
270 }
271
272 static void
io_passthru(const struct cmd * nf,int argc,char * argv[])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);
299