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
47static 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
104static 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
152static const struct args args[] = {
153	{ arg_string, &opt.dev, "controller-id|namespace-id" },
154	{ arg_none, NULL, NULL },
155};
156
157static void
158passthru(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(argv[optind], &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	}
257cleanup:
258	free(data);
259	close(fd);
260	if (ifd > -1)
261		close(ifd);
262	if (errno)
263		exit(1);
264}
265
266static void
267admin_passthru(const struct cmd *nf, int argc, char *argv[])
268{
269
270	passthru(nf, argc, argv);
271}
272
273static void
274io_passthru(const struct cmd *nf, int argc, char *argv[])
275{
276
277	passthru(nf, argc, argv);
278}
279
280static 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
289static 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
298CMD_COMMAND(admin_pass_cmd);
299CMD_COMMAND(io_pass_cmd);
300