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