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