1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #include <sys/param.h> 30 #include <sys/ioccom.h> 31 32 #include <err.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <stdbool.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <sysexits.h> 40 #include <unistd.h> 41 42 #include "nvmecontrol.h" 43 #include "comnd.h" 44 45 static struct options { 46 uint8_t opcode; 47 uint8_t flags; 48 uint16_t rsvd; 49 uint32_t nsid; 50 uint32_t data_len; 51 uint32_t metadata_len; 52 uint32_t timeout; 53 uint32_t cdw2; 54 uint32_t cdw3; 55 uint32_t cdw10; 56 uint32_t cdw11; 57 uint32_t cdw12; 58 uint32_t cdw13; 59 uint32_t cdw14; 60 uint32_t cdw15; 61 const char *ifn; 62 bool binary; 63 bool show_command; 64 bool dry_run; 65 bool read; 66 bool write; 67 uint8_t prefill; 68 const char *dev; 69 } opt = { 70 .binary = false, 71 .cdw10 = 0, 72 .cdw11 = 0, 73 .cdw12 = 0, 74 .cdw13 = 0, 75 .cdw14 = 0, 76 .cdw15 = 0, 77 .cdw2 = 0, 78 .cdw3 = 0, 79 .data_len = 0, 80 .dry_run = false, 81 .flags = 0, 82 .ifn = "", 83 .metadata_len = 0, 84 .nsid = 0, 85 .opcode = 0, 86 .prefill = 0, 87 .read = false, 88 .rsvd = 0, 89 .show_command = false, 90 .timeout = 0, 91 .write = false, 92 .dev = NULL, 93 }; 94 95 /* 96 * Argument names and short names selected to match the nvme-cli program 97 * so vendor-siupplied formulas work out of the box on FreeBSD with a simple 98 * s/nvme/nvmecontrol/. 99 */ 100 #define ARG(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 101 102 static struct opts opts[] = { 103 ARG("opcode", 'o', arg_uint8, opt, opcode, 104 "NVMe command opcode (required)"), 105 ARG("cdw2", '2', arg_uint32, opt, cdw2, 106 "Command dword 2 value"), 107 ARG("cdw3", '3', arg_uint32, opt, cdw3, 108 "Command dword 3 value"), 109 ARG("cdw10", '4', arg_uint32, opt, cdw10, 110 "Command dword 10 value"), 111 ARG("cdw11", '5', arg_uint32, opt, cdw11, 112 "Command dword 11 value"), 113 ARG("cdw12", '6', arg_uint32, opt, cdw12, 114 "Command dword 12 value"), 115 ARG("cdw13", '7', arg_uint32, opt, cdw13, 116 "Command dword 13 value"), 117 ARG("cdw14", '8', arg_uint32, opt, cdw14, 118 "Command dword 14 value"), 119 ARG("cdw15", '9', arg_uint32, opt, cdw15, 120 "Command dword 15 value"), 121 ARG("data-len", 'l', arg_uint32, opt, data_len, 122 "Length of data for I/O (bytes)"), 123 ARG("metadata-len", 'm', arg_uint32, opt, metadata_len, 124 "Length of metadata segment (bytes) (ignored)"), 125 ARG("flags", 'f', arg_uint8, opt, flags, 126 "NVMe command flags"), 127 ARG("input-file", 'i', arg_path, opt, ifn, 128 "Input file to send (default stdin)"), 129 ARG("namespace-id", 'n', arg_uint32, opt, nsid, 130 "Namespace id (ignored on FreeBSD)"), 131 ARG("prefill", 'p', arg_uint8, opt, prefill, 132 "Value to prefill payload with"), 133 ARG("rsvd", 'R', arg_uint16, opt, rsvd, 134 "Reserved field value"), 135 ARG("timeout", 't', arg_uint32, opt, timeout, 136 "Command timeout (ms)"), 137 ARG("raw-binary", 'b', arg_none, opt, binary, 138 "Output in binary format"), 139 ARG("dry-run", 'd', arg_none, opt, dry_run, 140 "Don't actually execute the command"), 141 ARG("read", 'r', arg_none, opt, read, 142 "Command reads data from device"), 143 ARG("show-command", 's', arg_none, opt, show_command, 144 "Show all the command values on stdout"), 145 ARG("write", 'w', arg_none, opt, write, 146 "Command writes data to device"), 147 { NULL, 0, arg_none, NULL, NULL } 148 }; 149 150 static const struct args args[] = { 151 { arg_string, &opt.dev, "controller-id|namespace-id" }, 152 { arg_none, NULL, NULL }, 153 }; 154 155 static void 156 passthru(const struct cmd *f, int argc, char *argv[]) 157 { 158 int fd = -1, ifd = -1; 159 size_t bytes_read; 160 void *data = NULL, *metadata = NULL; 161 struct nvme_pt_command pt; 162 163 if (arg_parse(argc, argv, f)) 164 return; 165 open_dev(opt.dev, &fd, 1, 1); 166 167 if (opt.read && opt.write) 168 errx(EX_USAGE, "need exactly one of --read or --write"); 169 if (opt.data_len != 0 && !opt.read && !opt.write) 170 errx(EX_USAGE, "need exactly one of --read or --write"); 171 if (*opt.ifn && (ifd = open(opt.ifn, O_RDONLY)) == -1) { 172 warn("open %s", opt.ifn); 173 goto cleanup; 174 } 175 #if notyet /* No support in kernel for this */ 176 if (opt.metadata_len != 0) { 177 if (posix_memalign(&metadata, getpagesize(), opt.metadata_len)) { 178 warn("can't allocate %d bytes for metadata", metadata_len); 179 goto cleanup; 180 } 181 } 182 #else 183 if (opt.metadata_len != 0) 184 errx(EX_UNAVAILABLE, "metadata not supported on FreeBSD"); 185 #endif 186 if (opt.data_len) { 187 if (posix_memalign(&data, getpagesize(), opt.data_len)) { 188 warn("can't allocate %d bytes for data", opt.data_len); 189 goto cleanup; 190 } 191 memset(data, opt.prefill, opt.data_len); 192 if (opt.write && 193 (bytes_read = read(ifd, data, opt.data_len)) != 194 opt.data_len) { 195 warn("read %s; expected %u bytes; got %zd", 196 *opt.ifn ? opt.ifn : "stdin", 197 opt.data_len, bytes_read); 198 goto cleanup; 199 } 200 } 201 if (opt.show_command) { 202 fprintf(stderr, "opcode : %#02x\n", opt.opcode); 203 fprintf(stderr, "flags : %#02x\n", opt.flags); 204 fprintf(stderr, "rsvd1 : %#04x\n", opt.rsvd); 205 fprintf(stderr, "nsid : %#04x\n", opt.nsid); 206 fprintf(stderr, "cdw2 : %#08x\n", opt.cdw2); 207 fprintf(stderr, "cdw3 : %#08x\n", opt.cdw3); 208 fprintf(stderr, "data_len : %#08x\n", opt.data_len); 209 fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len); 210 fprintf(stderr, "data : %p\n", data); 211 fprintf(stderr, "metadata : %p\n", metadata); 212 fprintf(stderr, "cdw10 : %#08x\n", opt.cdw10); 213 fprintf(stderr, "cdw11 : %#08x\n", opt.cdw11); 214 fprintf(stderr, "cdw12 : %#08x\n", opt.cdw12); 215 fprintf(stderr, "cdw13 : %#08x\n", opt.cdw13); 216 fprintf(stderr, "cdw14 : %#08x\n", opt.cdw14); 217 fprintf(stderr, "cdw15 : %#08x\n", opt.cdw15); 218 fprintf(stderr, "timeout_ms : %d\n", opt.timeout); 219 } 220 if (opt.dry_run) { 221 errno = 0; 222 warn("Doing a dry-run, no actual I/O"); 223 goto cleanup; 224 } 225 226 memset(&pt, 0, sizeof(pt)); 227 pt.cmd.opc = opt.opcode; 228 pt.cmd.fuse = opt.flags; 229 pt.cmd.cid = htole16(opt.rsvd); 230 pt.cmd.nsid = opt.nsid; /* XXX note: kernel overrides this */ 231 pt.cmd.rsvd2 = htole32(opt.cdw2); 232 pt.cmd.rsvd3 = htole32(opt.cdw3); 233 pt.cmd.cdw10 = htole32(opt.cdw10); 234 pt.cmd.cdw11 = htole32(opt.cdw11); 235 pt.cmd.cdw12 = htole32(opt.cdw12); 236 pt.cmd.cdw13 = htole32(opt.cdw13); 237 pt.cmd.cdw14 = htole32(opt.cdw14); 238 pt.cmd.cdw15 = htole32(opt.cdw15); 239 pt.buf = data; 240 pt.len = opt.data_len; 241 pt.is_read = opt.read; 242 243 errno = 0; 244 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 245 err(EX_IOERR, "passthrough request failed"); 246 if (!opt.binary) 247 printf("DWORD0 status= %#x\n", pt.cpl.cdw0); 248 if (opt.read) { 249 if (opt.binary) 250 write(STDOUT_FILENO, data, opt.data_len); 251 else { 252 /* print status here */ 253 print_hex(data, opt.data_len); 254 } 255 } 256 cleanup: 257 free(data); 258 close(fd); 259 if (ifd > -1) 260 close(ifd); 261 if (errno) 262 exit(EX_IOERR); 263 } 264 265 static void 266 admin_passthru(const struct cmd *nf, int argc, char *argv[]) 267 { 268 269 passthru(nf, argc, argv); 270 } 271 272 static void 273 io_passthru(const struct cmd *nf, int argc, char *argv[]) 274 { 275 276 passthru(nf, argc, argv); 277 } 278 279 static struct cmd admin_pass_cmd = { 280 .name = "admin-passthru", 281 .fn = admin_passthru, 282 .ctx_size = sizeof(struct options), 283 .opts = opts, 284 .args = args, 285 .descr = "Send a pass through Admin command to the specified device", 286 }; 287 288 static struct cmd io_pass_cmd = { 289 .name = "io-passthru", 290 .fn = io_passthru, 291 .ctx_size = sizeof(struct options), 292 .opts = opts, 293 .args = args, 294 .descr = "Send a pass through I/O command to the specified device", 295 }; 296 297 CMD_COMMAND(admin_pass_cmd); 298 CMD_COMMAND(io_pass_cmd); 299