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 <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 void *data = NULL, *metadata = NULL; 162 struct nvme_pt_command pt; 163 164 arg_parse(argc, argv, f); 165 open_dev(argv[optind], &fd, 1, 1); 166 167 if (opt.read && opt.write) 168 errx(1, "need exactly one of --read or --write"); 169 if (opt.data_len != 0 && !opt.read && !opt.write) 170 errx(1, "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(1, "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 && read(ifd, data, opt.data_len) < 0) { 193 warn("read %s", *opt.ifn ? opt.ifn : "stdin"); 194 goto cleanup; 195 } 196 } 197 if (opt.show_command) { 198 fprintf(stderr, "opcode : %#02x\n", opt.opcode); 199 fprintf(stderr, "flags : %#02x\n", opt.flags); 200 fprintf(stderr, "rsvd1 : %#04x\n", opt.rsvd); 201 fprintf(stderr, "nsid : %#04x\n", opt.nsid); 202 fprintf(stderr, "cdw2 : %#08x\n", opt.cdw2); 203 fprintf(stderr, "cdw3 : %#08x\n", opt.cdw3); 204 fprintf(stderr, "data_len : %#08x\n", opt.data_len); 205 fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len); 206 fprintf(stderr, "data : %p\n", data); 207 fprintf(stderr, "metadata : %p\n", metadata); 208 fprintf(stderr, "cdw10 : %#08x\n", opt.cdw10); 209 fprintf(stderr, "cdw11 : %#08x\n", opt.cdw11); 210 fprintf(stderr, "cdw12 : %#08x\n", opt.cdw12); 211 fprintf(stderr, "cdw13 : %#08x\n", opt.cdw13); 212 fprintf(stderr, "cdw14 : %#08x\n", opt.cdw14); 213 fprintf(stderr, "cdw15 : %#08x\n", opt.cdw15); 214 fprintf(stderr, "timeout_ms : %d\n", opt.timeout); 215 } 216 if (opt.dry_run) { 217 errno = 0; 218 warn("Doing a dry-run, no actual I/O"); 219 goto cleanup; 220 } 221 222 memset(&pt, 0, sizeof(pt)); 223 pt.cmd.opc = opt.opcode; 224 pt.cmd.fuse = opt.flags; 225 pt.cmd.cid = htole16(opt.rsvd); 226 pt.cmd.nsid = opt.nsid; /* XXX note: kernel overrides this */ 227 pt.cmd.rsvd2 = htole32(opt.cdw2); 228 pt.cmd.rsvd3 = htole32(opt.cdw3); 229 pt.cmd.cdw10 = htole32(opt.cdw10); 230 pt.cmd.cdw11 = htole32(opt.cdw11); 231 pt.cmd.cdw12 = htole32(opt.cdw12); 232 pt.cmd.cdw13 = htole32(opt.cdw13); 233 pt.cmd.cdw14 = htole32(opt.cdw14); 234 pt.cmd.cdw15 = htole32(opt.cdw15); 235 pt.buf = data; 236 pt.len = opt.data_len; 237 pt.is_read = opt.read; 238 239 errno = 0; 240 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 241 err(1, "passthrough request failed"); 242 /* XXX report status */ 243 if (opt.read) { 244 if (opt.binary) 245 write(STDOUT_FILENO, data, opt.data_len); 246 else { 247 /* print status here */ 248 print_hex(data, opt.data_len); 249 } 250 } 251 cleanup: 252 if (errno) 253 exit(1); 254 } 255 256 static void 257 admin_passthru(const struct cmd *nf, int argc, char *argv[]) 258 { 259 260 passthru(nf, argc, argv); 261 } 262 263 static void 264 io_passthru(const struct cmd *nf, int argc, char *argv[]) 265 { 266 267 passthru(nf, argc, argv); 268 } 269 270 static struct cmd admin_pass_cmd = { 271 .name = "admin-passthru", 272 .fn = admin_passthru, 273 .ctx_size = sizeof(struct options), 274 .opts = opts, 275 .args = args, 276 .descr = "Send a pass through Admin command to the specified device", 277 }; 278 279 static struct cmd io_pass_cmd = { 280 .name = "io-passthru", 281 .fn = io_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 CMD_COMMAND(admin_pass_cmd); 289 CMD_COMMAND(io_pass_cmd); 290