nvmectl.c revision 1.2.6.1 1 1.2.6.1 pgoyette /* $NetBSD: nvmectl.c,v 1.2.6.1 2017/05/02 03:19:16 pgoyette Exp $ */
2 1.1 nonaka
3 1.1 nonaka /*-
4 1.1 nonaka * Copyright (C) 2012-2013 Intel Corporation
5 1.1 nonaka * All rights reserved.
6 1.1 nonaka *
7 1.1 nonaka * Redistribution and use in source and binary forms, with or without
8 1.1 nonaka * modification, are permitted provided that the following conditions
9 1.1 nonaka * are met:
10 1.1 nonaka * 1. Redistributions of source code must retain the above copyright
11 1.1 nonaka * notice, this list of conditions and the following disclaimer.
12 1.1 nonaka * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 nonaka * notice, this list of conditions and the following disclaimer in the
14 1.1 nonaka * documentation and/or other materials provided with the distribution.
15 1.1 nonaka *
16 1.1 nonaka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 1.1 nonaka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 1.1 nonaka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.1 nonaka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 1.1 nonaka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 1.1 nonaka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 1.1 nonaka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 1.1 nonaka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 1.1 nonaka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.1 nonaka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 1.1 nonaka * SUCH DAMAGE.
27 1.1 nonaka */
28 1.1 nonaka
29 1.1 nonaka #include <sys/cdefs.h>
30 1.1 nonaka #ifndef lint
31 1.2.6.1 pgoyette __RCSID("$NetBSD: nvmectl.c,v 1.2.6.1 2017/05/02 03:19:16 pgoyette Exp $");
32 1.1 nonaka #if 0
33 1.2.6.1 pgoyette __FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 314229 2017-02-25 00:09:12Z imp $");
34 1.1 nonaka #endif
35 1.1 nonaka #endif
36 1.1 nonaka
37 1.1 nonaka #include <sys/param.h>
38 1.1 nonaka #include <sys/ioccom.h>
39 1.1 nonaka #include <sys/stat.h>
40 1.1 nonaka
41 1.1 nonaka #include <ctype.h>
42 1.1 nonaka #include <err.h>
43 1.1 nonaka #include <errno.h>
44 1.1 nonaka #include <fcntl.h>
45 1.1 nonaka #include <paths.h>
46 1.1 nonaka #include <stdbool.h>
47 1.1 nonaka #include <stddef.h>
48 1.1 nonaka #include <stdio.h>
49 1.1 nonaka #include <stdlib.h>
50 1.1 nonaka #include <string.h>
51 1.1 nonaka #include <unistd.h>
52 1.1 nonaka
53 1.1 nonaka #include "nvmectl.h"
54 1.1 nonaka
55 1.2.6.1 pgoyette static struct nvme_function funcs[] = {
56 1.1 nonaka {"devlist", devlist, DEVLIST_USAGE},
57 1.1 nonaka {"identify", identify, IDENTIFY_USAGE},
58 1.1 nonaka #ifdef PERFTEST_USAGE
59 1.1 nonaka {"perftest", perftest, PERFTEST_USAGE},
60 1.1 nonaka #endif
61 1.1 nonaka #ifdef RESET_USAGE
62 1.1 nonaka {"reset", reset, RESET_USAGE},
63 1.1 nonaka #endif
64 1.1 nonaka {"logpage", logpage, LOGPAGE_USAGE},
65 1.1 nonaka #ifdef FIRMWARE_USAGE
66 1.1 nonaka {"firmware", firmware, FIRMWARE_USAGE},
67 1.1 nonaka #endif
68 1.1 nonaka {"power", power, POWER_USAGE},
69 1.2.6.1 pgoyette {"wdc", wdc, WDC_USAGE},
70 1.1 nonaka {NULL, NULL, NULL},
71 1.1 nonaka };
72 1.1 nonaka
73 1.2.6.1 pgoyette void
74 1.2.6.1 pgoyette gen_usage(struct nvme_function *f)
75 1.1 nonaka {
76 1.1 nonaka
77 1.1 nonaka fprintf(stderr, "usage:\n");
78 1.1 nonaka while (f->name != NULL) {
79 1.1 nonaka fprintf(stderr, "%s", f->usage);
80 1.1 nonaka f++;
81 1.1 nonaka }
82 1.1 nonaka exit(1);
83 1.1 nonaka }
84 1.1 nonaka
85 1.2.6.1 pgoyette void
86 1.2.6.1 pgoyette dispatch(int argc, char *argv[], struct nvme_function *tbl)
87 1.2.6.1 pgoyette {
88 1.2.6.1 pgoyette struct nvme_function *f = tbl;
89 1.2.6.1 pgoyette
90 1.2.6.1 pgoyette if (argv[1] == NULL) {
91 1.2.6.1 pgoyette gen_usage(tbl);
92 1.2.6.1 pgoyette return;
93 1.2.6.1 pgoyette }
94 1.2.6.1 pgoyette
95 1.2.6.1 pgoyette while (f->name != NULL) {
96 1.2.6.1 pgoyette if (strcmp(argv[1], f->name) == 0)
97 1.2.6.1 pgoyette f->fn(argc-1, &argv[1]);
98 1.2.6.1 pgoyette f++;
99 1.2.6.1 pgoyette }
100 1.2.6.1 pgoyette
101 1.2.6.1 pgoyette fprintf(stderr, "Unknown command: %s\n", argv[1]);
102 1.2.6.1 pgoyette gen_usage(tbl);
103 1.2.6.1 pgoyette }
104 1.2.6.1 pgoyette
105 1.1 nonaka static void
106 1.1 nonaka print_bytes(void *data, uint32_t length)
107 1.1 nonaka {
108 1.1 nonaka uint32_t i, j;
109 1.1 nonaka uint8_t *p, *end;
110 1.1 nonaka
111 1.1 nonaka end = (uint8_t *)data + length;
112 1.1 nonaka
113 1.1 nonaka for (i = 0; i < length; i++) {
114 1.1 nonaka p = (uint8_t *)data + (i*16);
115 1.1 nonaka printf("%03x: ", i*16);
116 1.1 nonaka for (j = 0; j < 16 && p < end; j++)
117 1.1 nonaka printf("%02x ", *p++);
118 1.1 nonaka if (p >= end)
119 1.1 nonaka break;
120 1.1 nonaka printf("\n");
121 1.1 nonaka }
122 1.1 nonaka printf("\n");
123 1.1 nonaka }
124 1.1 nonaka
125 1.1 nonaka static void
126 1.1 nonaka print_dwords(void *data, uint32_t length)
127 1.1 nonaka {
128 1.1 nonaka uint32_t *p;
129 1.1 nonaka uint32_t i, j;
130 1.1 nonaka
131 1.1 nonaka p = (uint32_t *)data;
132 1.1 nonaka length /= sizeof(uint32_t);
133 1.1 nonaka
134 1.1 nonaka for (i = 0; i < length; i+=8) {
135 1.1 nonaka printf("%03x: ", i*4);
136 1.1 nonaka for (j = 0; j < 8; j++)
137 1.1 nonaka printf("%08x ", p[i+j]);
138 1.1 nonaka printf("\n");
139 1.1 nonaka }
140 1.1 nonaka
141 1.1 nonaka printf("\n");
142 1.1 nonaka }
143 1.1 nonaka
144 1.1 nonaka void
145 1.1 nonaka print_hex(void *data, uint32_t length)
146 1.1 nonaka {
147 1.1 nonaka if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0)
148 1.1 nonaka print_dwords(data, length);
149 1.1 nonaka else
150 1.1 nonaka print_bytes(data, length);
151 1.1 nonaka }
152 1.1 nonaka
153 1.1 nonaka void
154 1.1 nonaka read_controller_data(int fd, struct nvm_identify_controller *cdata)
155 1.1 nonaka {
156 1.1 nonaka struct nvme_pt_command pt;
157 1.1 nonaka
158 1.1 nonaka memset(&pt, 0, sizeof(pt));
159 1.1 nonaka pt.cmd.opcode = NVM_ADMIN_IDENTIFY;
160 1.1 nonaka pt.cmd.cdw10 = 1;
161 1.1 nonaka pt.buf = cdata;
162 1.1 nonaka pt.len = sizeof(*cdata);
163 1.1 nonaka pt.is_read = 1;
164 1.1 nonaka
165 1.1 nonaka if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
166 1.1 nonaka err(1, "identify request failed");
167 1.1 nonaka
168 1.1 nonaka if (nvme_completion_is_error(&pt.cpl))
169 1.1 nonaka errx(1, "identify request returned error");
170 1.1 nonaka }
171 1.1 nonaka
172 1.1 nonaka void
173 1.1 nonaka read_namespace_data(int fd, int nsid, struct nvm_identify_namespace *nsdata)
174 1.1 nonaka {
175 1.1 nonaka struct nvme_pt_command pt;
176 1.1 nonaka
177 1.1 nonaka memset(&pt, 0, sizeof(pt));
178 1.1 nonaka pt.cmd.opcode = NVM_ADMIN_IDENTIFY;
179 1.1 nonaka pt.cmd.nsid = nsid;
180 1.1 nonaka pt.buf = nsdata;
181 1.1 nonaka pt.len = sizeof(*nsdata);
182 1.1 nonaka pt.is_read = 1;
183 1.1 nonaka
184 1.1 nonaka if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
185 1.1 nonaka err(1, "identify request failed");
186 1.1 nonaka
187 1.1 nonaka if (nvme_completion_is_error(&pt.cpl))
188 1.1 nonaka errx(1, "identify request returned error");
189 1.1 nonaka }
190 1.1 nonaka
191 1.1 nonaka int
192 1.1 nonaka open_dev(const char *str, int *fd, int show_error, int exit_on_error)
193 1.1 nonaka {
194 1.1 nonaka char full_path[64];
195 1.1 nonaka
196 1.1 nonaka if (!strnstr(str, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) {
197 1.1 nonaka if (show_error)
198 1.1 nonaka warnx("controller/namespace ids must begin with '%s'",
199 1.1 nonaka NVME_CTRLR_PREFIX);
200 1.1 nonaka if (exit_on_error)
201 1.1 nonaka exit(1);
202 1.1 nonaka else
203 1.1 nonaka return (EINVAL);
204 1.1 nonaka }
205 1.1 nonaka
206 1.1 nonaka snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str);
207 1.1 nonaka *fd = open(full_path, O_RDWR);
208 1.1 nonaka if (*fd < 0) {
209 1.1 nonaka if (show_error)
210 1.1 nonaka warn("could not open %s", full_path);
211 1.1 nonaka if (exit_on_error)
212 1.1 nonaka exit(1);
213 1.1 nonaka else
214 1.1 nonaka return (errno);
215 1.1 nonaka }
216 1.1 nonaka
217 1.1 nonaka return (0);
218 1.1 nonaka }
219 1.1 nonaka
220 1.1 nonaka void
221 1.1 nonaka parse_ns_str(const char *ns_str, char *ctrlr_str, int *nsid)
222 1.1 nonaka {
223 1.1 nonaka char *nsloc;
224 1.1 nonaka
225 1.1 nonaka /*
226 1.1 nonaka * Pull the namespace id from the string. +2 skips past the "ns" part
227 1.1 nonaka * of the string. Don't search past 10 characters into the string,
228 1.1 nonaka * otherwise we know it is malformed.
229 1.1 nonaka */
230 1.1 nonaka nsloc = strnstr(ns_str, NVME_NS_PREFIX, 10);
231 1.1 nonaka if (nsloc != NULL)
232 1.1 nonaka *nsid = strtol(nsloc + 2, NULL, 10);
233 1.1 nonaka if (nsloc == NULL || (*nsid == 0 && errno != 0))
234 1.1 nonaka errx(1, "invalid namespace ID '%s'", ns_str);
235 1.1 nonaka
236 1.1 nonaka /*
237 1.1 nonaka * The controller string will include only the nvmX part of the
238 1.1 nonaka * nvmeXnsY string.
239 1.1 nonaka */
240 1.1 nonaka snprintf(ctrlr_str, nsloc - ns_str + 1, "%s", ns_str);
241 1.1 nonaka }
242 1.1 nonaka
243 1.1 nonaka void
244 1.1 nonaka nvme_strvis(u_char *dst, int dlen, const u_char *src, int slen)
245 1.1 nonaka {
246 1.1 nonaka #define STRVIS_ISWHITE(x) ((x) == ' ' || (x) == '\0' || (x) == (u_char)'\377')
247 1.1 nonaka /* Trim leading and trailing blanks and NULs. */
248 1.1 nonaka while (slen > 0 && STRVIS_ISWHITE(src[0]))
249 1.1 nonaka ++src, --slen;
250 1.1 nonaka while (slen > 0 && STRVIS_ISWHITE(src[slen - 1]))
251 1.1 nonaka --slen;
252 1.1 nonaka
253 1.1 nonaka while (slen > 0) {
254 1.1 nonaka if (*src < 0x20 || *src >= 0x80) {
255 1.1 nonaka /* non-printable characters */
256 1.1 nonaka dlen -= 4;
257 1.1 nonaka if (dlen < 1)
258 1.1 nonaka break;
259 1.1 nonaka *dst++ = '\\';
260 1.1 nonaka *dst++ = ((*src & 0300) >> 6) + '0';
261 1.1 nonaka *dst++ = ((*src & 0070) >> 3) + '0';
262 1.1 nonaka *dst++ = ((*src & 0007) >> 0) + '0';
263 1.1 nonaka } else if (*src == '\\') {
264 1.1 nonaka /* quote characters */
265 1.1 nonaka dlen -= 2;
266 1.1 nonaka if (dlen < 1)
267 1.1 nonaka break;
268 1.1 nonaka *dst++ = '\\';
269 1.1 nonaka *dst++ = '\\';
270 1.1 nonaka } else {
271 1.1 nonaka /* normal characters */
272 1.1 nonaka if (--dlen < 1)
273 1.1 nonaka break;
274 1.1 nonaka *dst++ = *src;
275 1.1 nonaka }
276 1.1 nonaka ++src, --slen;
277 1.1 nonaka }
278 1.1 nonaka
279 1.1 nonaka *dst++ = 0;
280 1.1 nonaka }
281 1.1 nonaka
282 1.1 nonaka int
283 1.1 nonaka main(int argc, char *argv[])
284 1.1 nonaka {
285 1.1 nonaka
286 1.1 nonaka if (argc < 2)
287 1.2.6.1 pgoyette gen_usage(funcs);
288 1.1 nonaka
289 1.2.6.1 pgoyette dispatch(argc, argv, funcs);
290 1.1 nonaka
291 1.1 nonaka return (0);
292 1.1 nonaka }
293