xref: /freebsd/sbin/nvmecontrol/passthru.c (revision 5dc463f9a5abf311e886e0000e5df1428415bbbf)
17d88a2f8SWarner Losh /*-
27d88a2f8SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37d88a2f8SWarner Losh  *
47d88a2f8SWarner Losh  * Copyright (C) 2012-2013 Intel Corporation
57d88a2f8SWarner Losh  * All rights reserved.
67d88a2f8SWarner Losh  *
77d88a2f8SWarner Losh  * Redistribution and use in source and binary forms, with or without
87d88a2f8SWarner Losh  * modification, are permitted provided that the following conditions
97d88a2f8SWarner Losh  * are met:
107d88a2f8SWarner Losh  * 1. Redistributions of source code must retain the above copyright
117d88a2f8SWarner Losh  *    notice, this list of conditions and the following disclaimer.
127d88a2f8SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
137d88a2f8SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
147d88a2f8SWarner Losh  *    documentation and/or other materials provided with the distribution.
157d88a2f8SWarner Losh  *
167d88a2f8SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177d88a2f8SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187d88a2f8SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197d88a2f8SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207d88a2f8SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217d88a2f8SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227d88a2f8SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237d88a2f8SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247d88a2f8SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257d88a2f8SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267d88a2f8SWarner Losh  * SUCH DAMAGE.
277d88a2f8SWarner Losh  */
287d88a2f8SWarner Losh 
297d88a2f8SWarner Losh #include <sys/cdefs.h>
307d88a2f8SWarner Losh __FBSDID("$FreeBSD$");
317d88a2f8SWarner Losh 
327d88a2f8SWarner Losh #include <sys/param.h>
337d88a2f8SWarner Losh #include <sys/ioccom.h>
347d88a2f8SWarner Losh 
357d88a2f8SWarner Losh #include <err.h>
367d88a2f8SWarner Losh #include <errno.h>
377d88a2f8SWarner Losh #include <fcntl.h>
387d88a2f8SWarner Losh #include <stdbool.h>
397d88a2f8SWarner Losh #include <stdio.h>
407d88a2f8SWarner Losh #include <stdlib.h>
417d88a2f8SWarner Losh #include <string.h>
42*5dc463f9SAlexander Motin #include <sysexits.h>
437d88a2f8SWarner Losh #include <unistd.h>
447d88a2f8SWarner Losh 
457d88a2f8SWarner Losh #include "nvmecontrol.h"
467d88a2f8SWarner Losh #include "comnd.h"
477d88a2f8SWarner Losh 
487d88a2f8SWarner Losh static struct options {
497d88a2f8SWarner Losh 	uint8_t		opcode;
507d88a2f8SWarner Losh 	uint8_t		flags;
517d88a2f8SWarner Losh 	uint16_t	rsvd;
527d88a2f8SWarner Losh 	uint32_t	nsid;
537d88a2f8SWarner Losh 	uint32_t	data_len;
547d88a2f8SWarner Losh 	uint32_t	metadata_len;
557d88a2f8SWarner Losh 	uint32_t	timeout;
567d88a2f8SWarner Losh 	uint32_t	cdw2;
577d88a2f8SWarner Losh 	uint32_t	cdw3;
587d88a2f8SWarner Losh 	uint32_t	cdw10;
597d88a2f8SWarner Losh 	uint32_t	cdw11;
607d88a2f8SWarner Losh 	uint32_t	cdw12;
617d88a2f8SWarner Losh 	uint32_t	cdw13;
627d88a2f8SWarner Losh 	uint32_t	cdw14;
637d88a2f8SWarner Losh 	uint32_t	cdw15;
647d88a2f8SWarner Losh 	const char	*ifn;
657d88a2f8SWarner Losh 	bool		binary;
667d88a2f8SWarner Losh 	bool		show_command;
677d88a2f8SWarner Losh 	bool		dry_run;
687d88a2f8SWarner Losh 	bool		read;
697d88a2f8SWarner Losh 	bool		write;
707d88a2f8SWarner Losh 	uint8_t		prefill;
717d88a2f8SWarner Losh 	const char	*dev;
727d88a2f8SWarner Losh } opt = {
737d88a2f8SWarner Losh 	.binary = false,
747d88a2f8SWarner Losh 	.cdw10 = 0,
757d88a2f8SWarner Losh 	.cdw11 = 0,
767d88a2f8SWarner Losh 	.cdw12 = 0,
777d88a2f8SWarner Losh 	.cdw13 = 0,
787d88a2f8SWarner Losh 	.cdw14 = 0,
797d88a2f8SWarner Losh 	.cdw15 = 0,
807d88a2f8SWarner Losh 	.cdw2 = 0,
817d88a2f8SWarner Losh 	.cdw3 = 0,
827d88a2f8SWarner Losh 	.data_len = 0,
837d88a2f8SWarner Losh 	.dry_run = false,
847d88a2f8SWarner Losh 	.flags = 0,
857d88a2f8SWarner Losh 	.ifn = "",
867d88a2f8SWarner Losh 	.metadata_len = 0,
877d88a2f8SWarner Losh 	.nsid = 0,
887d88a2f8SWarner Losh 	.opcode = 0,
897d88a2f8SWarner Losh 	.prefill = 0,
907d88a2f8SWarner Losh 	.read = false,
917d88a2f8SWarner Losh 	.rsvd = 0,
927d88a2f8SWarner Losh 	.show_command = false,
937d88a2f8SWarner Losh 	.timeout = 0,
947d88a2f8SWarner Losh 	.write = false,
957d88a2f8SWarner Losh 	.dev = NULL,
967d88a2f8SWarner Losh };
977d88a2f8SWarner Losh 
987d88a2f8SWarner Losh /*
997d88a2f8SWarner Losh  * Argument names and short names selected to match the nvme-cli program
1007d88a2f8SWarner Losh  * so vendor-siupplied formulas work out of the box on FreeBSD with a simple
1017d88a2f8SWarner Losh  * s/nvme/nvmecontrol/.
1027d88a2f8SWarner Losh  */
1037d88a2f8SWarner Losh #define ARG(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
1047d88a2f8SWarner Losh 
105ef7a2eadSWarner Losh static struct opts opts[] = {
1067d88a2f8SWarner Losh 	ARG("opcode",		'o',	arg_uint8,	opt, opcode,
1077d88a2f8SWarner Losh 	    "NVMe command opcode (required)"),
1087d88a2f8SWarner Losh 	ARG("cdw2",		'2',	arg_uint32,	opt, cdw2,
1097d88a2f8SWarner Losh 	    "Command dword 2 value"),
1107d88a2f8SWarner Losh 	ARG("cdw3",		'3',	arg_uint32,	opt, cdw3,
1117d88a2f8SWarner Losh 	    "Command dword 3 value"),
1127d88a2f8SWarner Losh 	ARG("cdw10",		'4',	arg_uint32,	opt, cdw10,
1137d88a2f8SWarner Losh 	    "Command dword 10 value"),
1147d88a2f8SWarner Losh 	ARG("cdw11",		'5',	arg_uint32,	opt, cdw11,
1157d88a2f8SWarner Losh 	    "Command dword 11 value"),
1167d88a2f8SWarner Losh 	ARG("cdw12",		'6',	arg_uint32,	opt, cdw12,
1177d88a2f8SWarner Losh 	    "Command dword 12 value"),
1187d88a2f8SWarner Losh 	ARG("cdw13",		'7',	arg_uint32,	opt, cdw13,
1197d88a2f8SWarner Losh 	    "Command dword 13 value"),
1207d88a2f8SWarner Losh 	ARG("cdw14",		'8',	arg_uint32,	opt, cdw14,
1217d88a2f8SWarner Losh 	    "Command dword 14 value"),
1227d88a2f8SWarner Losh 	ARG("cdw15",		'9',	arg_uint32,	opt, cdw15,
1237d88a2f8SWarner Losh 	    "Command dword 15 value"),
1247d88a2f8SWarner Losh 	ARG("data-len",		'l',	arg_uint32,	opt, data_len,
1257d88a2f8SWarner Losh 	    "Length of data for I/O (bytes)"),
1267d88a2f8SWarner Losh 	ARG("metadata-len",	'm',	arg_uint32,	opt, metadata_len,
1277d88a2f8SWarner Losh 	    "Length of metadata segment (bytes) (igored)"),
1287d88a2f8SWarner Losh 	ARG("flags",		'f',	arg_uint8,	opt, flags,
1297d88a2f8SWarner Losh 	    "NVMe command flags"),
1307d88a2f8SWarner Losh 	ARG("input-file",	'i',	arg_path,	opt, ifn,
1317d88a2f8SWarner Losh 	    "Input file to send (default stdin)"),
1327d88a2f8SWarner Losh 	ARG("namespace-id",	'n',	arg_uint32,	opt, nsid,
1337d88a2f8SWarner Losh 	    "Namespace id (ignored on FreeBSD)"),
1347d88a2f8SWarner Losh 	ARG("prefill",		'p',	arg_uint8,	opt, prefill,
1357d88a2f8SWarner Losh 	    "Value to prefill payload with"),
1367d88a2f8SWarner Losh 	ARG("rsvd",		'R',	arg_uint16,	opt, rsvd,
1377d88a2f8SWarner Losh 	    "Reserved field value"),
1387d88a2f8SWarner Losh 	ARG("timeout",		't',	arg_uint32,	opt, timeout,
1397d88a2f8SWarner Losh 	    "Command timeout (ms)"),
1407d88a2f8SWarner Losh 	ARG("raw-binary",	'b',	arg_none,	opt, binary,
1417d88a2f8SWarner Losh 	    "Output in binary format"),
1427d88a2f8SWarner Losh 	ARG("dry-run",		'd',	arg_none,	opt, dry_run,
1437d88a2f8SWarner Losh 	    "Don't actually execute the command"),
1447d88a2f8SWarner Losh 	ARG("read",		'r',	arg_none,	opt, read,
1457d88a2f8SWarner Losh 	    "Command reads data from device"),
1467d88a2f8SWarner Losh 	ARG("show-command",	's',	arg_none,	opt, show_command,
1477d88a2f8SWarner Losh 	    "Show all the command values on stdout"),
1487d88a2f8SWarner Losh 	ARG("write",		'w',	arg_none,	opt, write,
1497d88a2f8SWarner Losh 	    "Command writes data to device"),
1507d88a2f8SWarner Losh 	{ NULL, 0, arg_none, NULL, NULL }
1517d88a2f8SWarner Losh };
1527d88a2f8SWarner Losh 
1537d88a2f8SWarner Losh static const struct args args[] = {
1547d88a2f8SWarner Losh 	{ arg_string, &opt.dev, "controller-id|namespace-id" },
1557d88a2f8SWarner Losh 	{ arg_none, NULL, NULL },
1567d88a2f8SWarner Losh };
1577d88a2f8SWarner Losh 
1587d88a2f8SWarner Losh static void
1597d88a2f8SWarner Losh passthru(const struct cmd *f, int argc, char *argv[])
1607d88a2f8SWarner Losh {
1617d88a2f8SWarner Losh 	int	fd = -1, ifd = -1;
1626995fb5eSDavid Bright 	size_t	bytes_read;
1637d88a2f8SWarner Losh 	void	*data = NULL, *metadata = NULL;
1647d88a2f8SWarner Losh 	struct nvme_pt_command	pt;
1657d88a2f8SWarner Losh 
1666995fb5eSDavid Bright 	if (arg_parse(argc, argv, f))
1676995fb5eSDavid Bright 		return;
1688f92938fSAlexander Motin 	open_dev(opt.dev, &fd, 1, 1);
1697d88a2f8SWarner Losh 
1707d88a2f8SWarner Losh 	if (opt.read && opt.write)
171*5dc463f9SAlexander Motin 		errx(EX_USAGE, "need exactly one of --read or --write");
1727d88a2f8SWarner Losh 	if (opt.data_len != 0 && !opt.read && !opt.write)
173*5dc463f9SAlexander Motin 		errx(EX_USAGE, "need exactly one of --read or --write");
1747d88a2f8SWarner Losh 	if (*opt.ifn && (ifd = open(opt.ifn, O_RDONLY)) == -1) {
1757d88a2f8SWarner Losh 		warn("open %s", opt.ifn);
1767d88a2f8SWarner Losh 		goto cleanup;
1777d88a2f8SWarner Losh 	}
1787d88a2f8SWarner Losh #if notyet	/* No support in kernel for this */
1797d88a2f8SWarner Losh 	if (opt.metadata_len != 0) {
1807d88a2f8SWarner Losh 		if (posix_memalign(&metadata, getpagesize(), opt.metadata_len)) {
1817d88a2f8SWarner Losh 			warn("can't allocate %d bytes for metadata", metadata_len);
1827d88a2f8SWarner Losh 			goto cleanup;
1837d88a2f8SWarner Losh 		}
1847d88a2f8SWarner Losh 	}
1857d88a2f8SWarner Losh #else
1867d88a2f8SWarner Losh 	if (opt.metadata_len != 0)
187*5dc463f9SAlexander Motin 		errx(EX_UNAVAILABLE, "metadata not supported on FreeBSD");
1887d88a2f8SWarner Losh #endif
1897d88a2f8SWarner Losh 	if (opt.data_len) {
1907d88a2f8SWarner Losh 		if (posix_memalign(&data, getpagesize(), opt.data_len)) {
1917d88a2f8SWarner Losh 			warn("can't allocate %d bytes for data", opt.data_len);
1927d88a2f8SWarner Losh 			goto cleanup;
1937d88a2f8SWarner Losh 		}
1947d88a2f8SWarner Losh 		memset(data, opt.prefill, opt.data_len);
1956995fb5eSDavid Bright 		if (opt.write &&
1966995fb5eSDavid Bright 		    (bytes_read = read(ifd, data, opt.data_len)) !=
1976995fb5eSDavid Bright 		    opt.data_len) {
1986995fb5eSDavid Bright 			warn("read %s; expected %u bytes; got %zd",
1996995fb5eSDavid Bright 			     *opt.ifn ? opt.ifn : "stdin",
2006995fb5eSDavid Bright 			     opt.data_len, bytes_read);
2017d88a2f8SWarner Losh 			goto cleanup;
2027d88a2f8SWarner Losh 		}
2037d88a2f8SWarner Losh 	}
2047d88a2f8SWarner Losh 	if (opt.show_command) {
2057d88a2f8SWarner Losh 		fprintf(stderr, "opcode       : %#02x\n", opt.opcode);
2067d88a2f8SWarner Losh 		fprintf(stderr, "flags        : %#02x\n", opt.flags);
2077d88a2f8SWarner Losh 		fprintf(stderr, "rsvd1        : %#04x\n", opt.rsvd);
2087d88a2f8SWarner Losh 		fprintf(stderr, "nsid         : %#04x\n", opt.nsid);
2097d88a2f8SWarner Losh 		fprintf(stderr, "cdw2         : %#08x\n", opt.cdw2);
2107d88a2f8SWarner Losh 		fprintf(stderr, "cdw3         : %#08x\n", opt.cdw3);
2117d88a2f8SWarner Losh 		fprintf(stderr, "data_len     : %#08x\n", opt.data_len);
2127d88a2f8SWarner Losh 		fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len);
2137d88a2f8SWarner Losh 		fprintf(stderr, "data         : %p\n", data);
2147d88a2f8SWarner Losh 		fprintf(stderr, "metadata     : %p\n", metadata);
2157d88a2f8SWarner Losh 		fprintf(stderr, "cdw10        : %#08x\n", opt.cdw10);
2167d88a2f8SWarner Losh 		fprintf(stderr, "cdw11        : %#08x\n", opt.cdw11);
2177d88a2f8SWarner Losh 		fprintf(stderr, "cdw12        : %#08x\n", opt.cdw12);
2187d88a2f8SWarner Losh 		fprintf(stderr, "cdw13        : %#08x\n", opt.cdw13);
2197d88a2f8SWarner Losh 		fprintf(stderr, "cdw14        : %#08x\n", opt.cdw14);
2207d88a2f8SWarner Losh 		fprintf(stderr, "cdw15        : %#08x\n", opt.cdw15);
2217d88a2f8SWarner Losh 		fprintf(stderr, "timeout_ms   : %d\n", opt.timeout);
2227d88a2f8SWarner Losh 	}
2237d88a2f8SWarner Losh 	if (opt.dry_run) {
2247d88a2f8SWarner Losh 		errno = 0;
2257d88a2f8SWarner Losh 		warn("Doing a dry-run, no actual I/O");
2267d88a2f8SWarner Losh 		goto cleanup;
2277d88a2f8SWarner Losh 	}
2287d88a2f8SWarner Losh 
2297d88a2f8SWarner Losh 	memset(&pt, 0, sizeof(pt));
2307d88a2f8SWarner Losh 	pt.cmd.opc = opt.opcode;
2317d88a2f8SWarner Losh 	pt.cmd.fuse = opt.flags;
2327d88a2f8SWarner Losh 	pt.cmd.cid = htole16(opt.rsvd);
2337d88a2f8SWarner Losh 	pt.cmd.nsid = opt.nsid;				/* XXX note: kernel overrides this */
2347d88a2f8SWarner Losh 	pt.cmd.rsvd2 = htole32(opt.cdw2);
2357d88a2f8SWarner Losh 	pt.cmd.rsvd3 = htole32(opt.cdw3);
2367d88a2f8SWarner Losh 	pt.cmd.cdw10 = htole32(opt.cdw10);
2377d88a2f8SWarner Losh 	pt.cmd.cdw11 = htole32(opt.cdw11);
2387d88a2f8SWarner Losh 	pt.cmd.cdw12 = htole32(opt.cdw12);
2397d88a2f8SWarner Losh 	pt.cmd.cdw13 = htole32(opt.cdw13);
2407d88a2f8SWarner Losh 	pt.cmd.cdw14 = htole32(opt.cdw14);
2417d88a2f8SWarner Losh 	pt.cmd.cdw15 = htole32(opt.cdw15);
2427d88a2f8SWarner Losh 	pt.buf = data;
2437d88a2f8SWarner Losh 	pt.len = opt.data_len;
2447d88a2f8SWarner Losh 	pt.is_read = opt.read;
2457d88a2f8SWarner Losh 
2467d88a2f8SWarner Losh 	errno = 0;
2477d88a2f8SWarner Losh 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
248*5dc463f9SAlexander Motin 		err(EX_IOERR, "passthrough request failed");
2497d88a2f8SWarner Losh 	/* XXX report status */
2507d88a2f8SWarner Losh 	if (opt.read) {
2517d88a2f8SWarner Losh 		if (opt.binary)
2527d88a2f8SWarner Losh 			write(STDOUT_FILENO, data, opt.data_len);
2537d88a2f8SWarner Losh 		else {
2547d88a2f8SWarner Losh 			/* print status here */
2557d88a2f8SWarner Losh 			print_hex(data, opt.data_len);
2567d88a2f8SWarner Losh 		}
2577d88a2f8SWarner Losh 	}
2587d88a2f8SWarner Losh cleanup:
2596995fb5eSDavid Bright 	free(data);
2606995fb5eSDavid Bright 	close(fd);
2616995fb5eSDavid Bright 	if (ifd > -1)
2626995fb5eSDavid Bright 		close(ifd);
2637d88a2f8SWarner Losh 	if (errno)
264*5dc463f9SAlexander Motin 		exit(EX_IOERR);
2657d88a2f8SWarner Losh }
2667d88a2f8SWarner Losh 
2677d88a2f8SWarner Losh static void
2687d88a2f8SWarner Losh admin_passthru(const struct cmd *nf, int argc, char *argv[])
2697d88a2f8SWarner Losh {
2707d88a2f8SWarner Losh 
2717d88a2f8SWarner Losh 	passthru(nf, argc, argv);
2727d88a2f8SWarner Losh }
2737d88a2f8SWarner Losh 
2747d88a2f8SWarner Losh static void
2757d88a2f8SWarner Losh io_passthru(const struct cmd *nf, int argc, char *argv[])
2767d88a2f8SWarner Losh {
2777d88a2f8SWarner Losh 
2787d88a2f8SWarner Losh 	passthru(nf, argc, argv);
2797d88a2f8SWarner Losh }
2807d88a2f8SWarner Losh 
281ef7a2eadSWarner Losh static struct cmd admin_pass_cmd = {
282ef7a2eadSWarner Losh 	.name = "admin-passthru",
283ef7a2eadSWarner Losh 	.fn = admin_passthru,
284ef7a2eadSWarner Losh 	.ctx_size = sizeof(struct options),
285ef7a2eadSWarner Losh 	.opts = opts,
286ef7a2eadSWarner Losh 	.args = args,
287ef7a2eadSWarner Losh 	.descr = "Send a pass through Admin command to the specified device",
288ef7a2eadSWarner Losh };
289ef7a2eadSWarner Losh 
290ef7a2eadSWarner Losh static struct cmd io_pass_cmd = {
291ef7a2eadSWarner Losh 	.name = "io-passthru",
292ef7a2eadSWarner Losh 	.fn = io_passthru,
293ef7a2eadSWarner Losh 	.ctx_size = sizeof(struct options),
294ef7a2eadSWarner Losh 	.opts = opts,
295ef7a2eadSWarner Losh 	.args = args,
296e2d6a6e9SWarner Losh 	.descr = "Send a pass through I/O command to the specified device",
297ef7a2eadSWarner Losh };
298ef7a2eadSWarner Losh 
299ef7a2eadSWarner Losh CMD_COMMAND(admin_pass_cmd);
300ef7a2eadSWarner Losh CMD_COMMAND(io_pass_cmd);
301