1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (C) 2012-2013 Intel Corporation 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/ioccom.h> 34 35 #include <err.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <stdbool.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sysexits.h> 43 #include <unistd.h> 44 45 #include "nvmecontrol.h" 46 #include "comnd.h" 47 48 static struct options { 49 uint8_t opcode; 50 uint8_t flags; 51 uint16_t rsvd; 52 uint32_t nsid; 53 uint32_t data_len; 54 uint32_t metadata_len; 55 uint32_t timeout; 56 uint32_t cdw2; 57 uint32_t cdw3; 58 uint32_t cdw10; 59 uint32_t cdw11; 60 uint32_t cdw12; 61 uint32_t cdw13; 62 uint32_t cdw14; 63 uint32_t cdw15; 64 const char *ifn; 65 bool binary; 66 bool show_command; 67 bool dry_run; 68 bool read; 69 bool write; 70 uint8_t prefill; 71 const char *dev; 72 } opt = { 73 .binary = false, 74 .cdw10 = 0, 75 .cdw11 = 0, 76 .cdw12 = 0, 77 .cdw13 = 0, 78 .cdw14 = 0, 79 .cdw15 = 0, 80 .cdw2 = 0, 81 .cdw3 = 0, 82 .data_len = 0, 83 .dry_run = false, 84 .flags = 0, 85 .ifn = "", 86 .metadata_len = 0, 87 .nsid = 0, 88 .opcode = 0, 89 .prefill = 0, 90 .read = false, 91 .rsvd = 0, 92 .show_command = false, 93 .timeout = 0, 94 .write = false, 95 .dev = NULL, 96 }; 97 98 /* 99 * Argument names and short names selected to match the nvme-cli program 100 * so vendor-siupplied formulas work out of the box on FreeBSD with a simple 101 * s/nvme/nvmecontrol/. 102 */ 103 #define ARG(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 104 105 static struct opts opts[] = { 106 ARG("opcode", 'o', arg_uint8, opt, opcode, 107 "NVMe command opcode (required)"), 108 ARG("cdw2", '2', arg_uint32, opt, cdw2, 109 "Command dword 2 value"), 110 ARG("cdw3", '3', arg_uint32, opt, cdw3, 111 "Command dword 3 value"), 112 ARG("cdw10", '4', arg_uint32, opt, cdw10, 113 "Command dword 10 value"), 114 ARG("cdw11", '5', arg_uint32, opt, cdw11, 115 "Command dword 11 value"), 116 ARG("cdw12", '6', arg_uint32, opt, cdw12, 117 "Command dword 12 value"), 118 ARG("cdw13", '7', arg_uint32, opt, cdw13, 119 "Command dword 13 value"), 120 ARG("cdw14", '8', arg_uint32, opt, cdw14, 121 "Command dword 14 value"), 122 ARG("cdw15", '9', arg_uint32, opt, cdw15, 123 "Command dword 15 value"), 124 ARG("data-len", 'l', arg_uint32, opt, data_len, 125 "Length of data for I/O (bytes)"), 126 ARG("metadata-len", 'm', arg_uint32, opt, metadata_len, 127 "Length of metadata segment (bytes) (igored)"), 128 ARG("flags", 'f', arg_uint8, opt, flags, 129 "NVMe command flags"), 130 ARG("input-file", 'i', arg_path, opt, ifn, 131 "Input file to send (default stdin)"), 132 ARG("namespace-id", 'n', arg_uint32, opt, nsid, 133 "Namespace id (ignored on FreeBSD)"), 134 ARG("prefill", 'p', arg_uint8, opt, prefill, 135 "Value to prefill payload with"), 136 ARG("rsvd", 'R', arg_uint16, opt, rsvd, 137 "Reserved field value"), 138 ARG("timeout", 't', arg_uint32, opt, timeout, 139 "Command timeout (ms)"), 140 ARG("raw-binary", 'b', arg_none, opt, binary, 141 "Output in binary format"), 142 ARG("dry-run", 'd', arg_none, opt, dry_run, 143 "Don't actually execute the command"), 144 ARG("read", 'r', arg_none, opt, read, 145 "Command reads data from device"), 146 ARG("show-command", 's', arg_none, opt, show_command, 147 "Show all the command values on stdout"), 148 ARG("write", 'w', arg_none, opt, write, 149 "Command writes data to device"), 150 { NULL, 0, arg_none, NULL, NULL } 151 }; 152 153 static const struct args args[] = { 154 { arg_string, &opt.dev, "controller-id|namespace-id" }, 155 { arg_none, NULL, NULL }, 156 }; 157 158 static void 159 passthru(const struct cmd *f, int argc, char *argv[]) 160 { 161 int fd = -1, ifd = -1; 162 size_t bytes_read; 163 void *data = NULL, *metadata = NULL; 164 struct nvme_pt_command pt; 165 166 if (arg_parse(argc, argv, f)) 167 return; 168 open_dev(opt.dev, &fd, 1, 1); 169 170 if (opt.read && opt.write) 171 errx(EX_USAGE, "need exactly one of --read or --write"); 172 if (opt.data_len != 0 && !opt.read && !opt.write) 173 errx(EX_USAGE, "need exactly one of --read or --write"); 174 if (*opt.ifn && (ifd = open(opt.ifn, O_RDONLY)) == -1) { 175 warn("open %s", opt.ifn); 176 goto cleanup; 177 } 178 #if notyet /* No support in kernel for this */ 179 if (opt.metadata_len != 0) { 180 if (posix_memalign(&metadata, getpagesize(), opt.metadata_len)) { 181 warn("can't allocate %d bytes for metadata", metadata_len); 182 goto cleanup; 183 } 184 } 185 #else 186 if (opt.metadata_len != 0) 187 errx(EX_UNAVAILABLE, "metadata not supported on FreeBSD"); 188 #endif 189 if (opt.data_len) { 190 if (posix_memalign(&data, getpagesize(), opt.data_len)) { 191 warn("can't allocate %d bytes for data", opt.data_len); 192 goto cleanup; 193 } 194 memset(data, opt.prefill, opt.data_len); 195 if (opt.write && 196 (bytes_read = read(ifd, data, opt.data_len)) != 197 opt.data_len) { 198 warn("read %s; expected %u bytes; got %zd", 199 *opt.ifn ? opt.ifn : "stdin", 200 opt.data_len, bytes_read); 201 goto cleanup; 202 } 203 } 204 if (opt.show_command) { 205 fprintf(stderr, "opcode : %#02x\n", opt.opcode); 206 fprintf(stderr, "flags : %#02x\n", opt.flags); 207 fprintf(stderr, "rsvd1 : %#04x\n", opt.rsvd); 208 fprintf(stderr, "nsid : %#04x\n", opt.nsid); 209 fprintf(stderr, "cdw2 : %#08x\n", opt.cdw2); 210 fprintf(stderr, "cdw3 : %#08x\n", opt.cdw3); 211 fprintf(stderr, "data_len : %#08x\n", opt.data_len); 212 fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len); 213 fprintf(stderr, "data : %p\n", data); 214 fprintf(stderr, "metadata : %p\n", metadata); 215 fprintf(stderr, "cdw10 : %#08x\n", opt.cdw10); 216 fprintf(stderr, "cdw11 : %#08x\n", opt.cdw11); 217 fprintf(stderr, "cdw12 : %#08x\n", opt.cdw12); 218 fprintf(stderr, "cdw13 : %#08x\n", opt.cdw13); 219 fprintf(stderr, "cdw14 : %#08x\n", opt.cdw14); 220 fprintf(stderr, "cdw15 : %#08x\n", opt.cdw15); 221 fprintf(stderr, "timeout_ms : %d\n", opt.timeout); 222 } 223 if (opt.dry_run) { 224 errno = 0; 225 warn("Doing a dry-run, no actual I/O"); 226 goto cleanup; 227 } 228 229 memset(&pt, 0, sizeof(pt)); 230 pt.cmd.opc = opt.opcode; 231 pt.cmd.fuse = opt.flags; 232 pt.cmd.cid = htole16(opt.rsvd); 233 pt.cmd.nsid = opt.nsid; /* XXX note: kernel overrides this */ 234 pt.cmd.rsvd2 = htole32(opt.cdw2); 235 pt.cmd.rsvd3 = htole32(opt.cdw3); 236 pt.cmd.cdw10 = htole32(opt.cdw10); 237 pt.cmd.cdw11 = htole32(opt.cdw11); 238 pt.cmd.cdw12 = htole32(opt.cdw12); 239 pt.cmd.cdw13 = htole32(opt.cdw13); 240 pt.cmd.cdw14 = htole32(opt.cdw14); 241 pt.cmd.cdw15 = htole32(opt.cdw15); 242 pt.buf = data; 243 pt.len = opt.data_len; 244 pt.is_read = opt.read; 245 246 errno = 0; 247 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 248 err(EX_IOERR, "passthrough request failed"); 249 /* XXX report status */ 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