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