1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/ioccom.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <stdbool.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <sysexits.h> 42 #include <unistd.h> 43 44 #include "nvmecontrol.h" 45 #include "comnd.h" 46 47 static 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 104 static 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 152 static const struct args args[] = { 153 { arg_string, &opt.dev, "controller-id|namespace-id" }, 154 { arg_none, NULL, NULL }, 155 }; 156 157 static void 158 passthru(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(opt.dev, &fd, 1, 1); 168 169 if (opt.read && opt.write) 170 errx(EX_USAGE, "need exactly one of --read or --write"); 171 if (opt.data_len != 0 && !opt.read && !opt.write) 172 errx(EX_USAGE, "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(EX_UNAVAILABLE, "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(EX_IOERR, "passthrough request failed"); 248 if (!opt.binary) 249 printf("DWORD0 status= %#x\n", pt.cpl.cdw0); 250 if (opt.read) { 251 if (opt.binary) 252 write(STDOUT_FILENO, data, opt.data_len); 253 else { 254 /* print status here */ 255 print_hex(data, opt.data_len); 256 } 257 } 258 cleanup: 259 free(data); 260 close(fd); 261 if (ifd > -1) 262 close(ifd); 263 if (errno) 264 exit(EX_IOERR); 265 } 266 267 static void 268 admin_passthru(const struct cmd *nf, int argc, char *argv[]) 269 { 270 271 passthru(nf, argc, argv); 272 } 273 274 static void 275 io_passthru(const struct cmd *nf, int argc, char *argv[]) 276 { 277 278 passthru(nf, argc, argv); 279 } 280 281 static struct cmd admin_pass_cmd = { 282 .name = "admin-passthru", 283 .fn = admin_passthru, 284 .ctx_size = sizeof(struct options), 285 .opts = opts, 286 .args = args, 287 .descr = "Send a pass through Admin command to the specified device", 288 }; 289 290 static struct cmd io_pass_cmd = { 291 .name = "io-passthru", 292 .fn = io_passthru, 293 .ctx_size = sizeof(struct options), 294 .opts = opts, 295 .args = args, 296 .descr = "Send a pass through I/O command to the specified device", 297 }; 298 299 CMD_COMMAND(admin_pass_cmd); 300 CMD_COMMAND(io_pass_cmd); 301