]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/nvmecontrol.c
stand: TARGET_ARCH is spelled MACHINE_ARCH in Makefiles
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / nvmecontrol.c
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 #include <sys/stat.h>
35
36 #include <ctype.h>
37 #include <dlfcn.h>
38 #include <dirent.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <paths.h>
43 #include <stdbool.h>
44 #include <stddef.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "nvmecontrol.h"
51
52 SET_CONCAT_DEF(top, struct nvme_function);
53
54 static void
55 print_usage(const struct nvme_function *f)
56 {
57         const char *cp;
58         char ch;
59         bool need_prefix = true;
60
61         cp = f->usage;
62         while (*cp) {
63                 ch = *cp++;
64                 if (need_prefix) {
65                         if (ch != ' ')
66                                 fputs("        nvmecontrol ", stderr);
67                         else
68                                 fputs("                    ", stderr);
69                 }
70                 fputc(ch, stderr);
71                 need_prefix = (ch == '\n');
72         }
73         if (!need_prefix)
74                 fputc('\n', stderr);
75 }
76
77 static void
78 gen_usage_set(const struct nvme_function * const *f, const struct nvme_function * const *flimit)
79 {
80
81         fprintf(stderr, "usage:\n");
82         while (f < flimit) {
83                 print_usage(*f);
84                 f++;
85         }
86         exit(1);
87 }
88
89 void
90 usage(const struct nvme_function *f)
91 {
92
93         fprintf(stderr, "usage:\n");
94         print_usage(f);
95         exit(1);
96 }
97
98 void
99 dispatch_set(int argc, char *argv[], const struct nvme_function * const *tbl,
100     const struct nvme_function * const *tbl_limit)
101 {
102         const struct nvme_function * const *f = tbl;
103
104         if (argv[1] == NULL) {
105                 gen_usage_set(tbl, tbl_limit);
106                 return;
107         }
108
109         while (f < tbl_limit) {
110                 if (strcmp(argv[1], (*f)->name) == 0) {
111                         (*f)->fn(*f, argc-1, &argv[1]);
112                         return;
113                 }
114                 f++;
115         }
116
117         fprintf(stderr, "Unknown command: %s\n", argv[1]);
118         gen_usage_set(tbl, tbl_limit);
119 }
120
121 void
122 set_concat_add(struct set_concat *m, void *b, void *e)
123 {
124         void **bp, **ep;
125         int add_n, cur_n;
126
127         if (b == NULL)
128                 return;
129         /*
130          * Args are really pointers to arrays of pointers, but C's
131          * casting rules kinda suck since you can't directly cast
132          * struct foo ** to a void **.
133          */
134         bp = (void **)b;
135         ep = (void **)e;
136         add_n = ep - bp;
137         cur_n = 0;
138         if (m->begin != NULL)
139                 cur_n = m->limit - m->begin;
140         m->begin = reallocarray(m->begin, cur_n + add_n, sizeof(void *));
141         if (m->begin == NULL)
142                 err(1, "expanding concat set");
143         memcpy(m->begin + cur_n, bp, add_n * sizeof(void *));
144         m->limit = m->begin + cur_n + add_n;
145 }
146
147 static void
148 print_bytes(void *data, uint32_t length)
149 {
150         uint32_t        i, j;
151         uint8_t         *p, *end;
152
153         end = (uint8_t *)data + length;
154
155         for (i = 0; i < length; i++) {
156                 p = (uint8_t *)data + (i*16);
157                 printf("%03x: ", i*16);
158                 for (j = 0; j < 16 && p < end; j++)
159                         printf("%02x ", *p++);
160                 if (p >= end)
161                         break;
162                 printf("\n");
163         }
164         printf("\n");
165 }
166
167 static void
168 print_dwords(void *data, uint32_t length)
169 {
170         uint32_t        *p;
171         uint32_t        i, j;
172
173         p = (uint32_t *)data;
174         length /= sizeof(uint32_t);
175
176         for (i = 0; i < length; i+=8) {
177                 printf("%03x: ", i*4);
178                 for (j = 0; j < 8; j++)
179                         printf("%08x ", p[i+j]);
180                 printf("\n");
181         }
182
183         printf("\n");
184 }
185
186 void
187 print_hex(void *data, uint32_t length)
188 {
189         if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0)
190                 print_dwords(data, length);
191         else
192                 print_bytes(data, length);
193 }
194
195 void
196 read_controller_data(int fd, struct nvme_controller_data *cdata)
197 {
198         struct nvme_pt_command  pt;
199
200         memset(&pt, 0, sizeof(pt));
201         pt.cmd.opc = NVME_OPC_IDENTIFY;
202         pt.cmd.cdw10 = htole32(1);
203         pt.buf = cdata;
204         pt.len = sizeof(*cdata);
205         pt.is_read = 1;
206
207         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
208                 err(1, "identify request failed");
209
210         /* Convert data to host endian */
211         nvme_controller_data_swapbytes(cdata);
212
213         if (nvme_completion_is_error(&pt.cpl))
214                 errx(1, "identify request returned error");
215 }
216
217 void
218 read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata)
219 {
220         struct nvme_pt_command  pt;
221
222         memset(&pt, 0, sizeof(pt));
223         pt.cmd.opc = NVME_OPC_IDENTIFY;
224         pt.cmd.nsid = htole32(nsid);
225         pt.buf = nsdata;
226         pt.len = sizeof(*nsdata);
227         pt.is_read = 1;
228
229         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
230                 err(1, "identify request failed");
231
232         /* Convert data to host endian */
233         nvme_namespace_data_swapbytes(nsdata);
234
235         if (nvme_completion_is_error(&pt.cpl))
236                 errx(1, "identify request returned error");
237 }
238
239 int
240 open_dev(const char *str, int *fd, int show_error, int exit_on_error)
241 {
242         char            full_path[64];
243
244         if (!strnstr(str, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) {
245                 if (show_error)
246                         warnx("controller/namespace ids must begin with '%s'",
247                             NVME_CTRLR_PREFIX);
248                 if (exit_on_error)
249                         exit(1);
250                 else
251                         return (EINVAL);
252         }
253
254         snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str);
255         *fd = open(full_path, O_RDWR);
256         if (*fd < 0) {
257                 if (show_error)
258                         warn("could not open %s", full_path);
259                 if (exit_on_error)
260                         exit(1);
261                 else
262                         return (errno);
263         }
264
265         return (0);
266 }
267
268 void
269 parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid)
270 {
271         char    *nsloc;
272
273         /*
274          * Pull the namespace id from the string. +2 skips past the "ns" part
275          *  of the string.  Don't search past 10 characters into the string,
276          *  otherwise we know it is malformed.
277          */
278         nsloc = strnstr(ns_str, NVME_NS_PREFIX, 10);
279         if (nsloc != NULL)
280                 *nsid = strtol(nsloc + 2, NULL, 10);
281         if (nsloc == NULL || (*nsid == 0 && errno != 0))
282                 errx(1, "invalid namespace ID '%s'", ns_str);
283
284         /*
285          * The controller string will include only the nvmX part of the
286          *  nvmeXnsY string.
287          */
288         snprintf(ctrlr_str, nsloc - ns_str + 1, "%s", ns_str);
289 }
290
291 /*
292  * Loads all the .so's from the specified directory.
293  */
294 static void
295 load_dir(const char *dir)
296 {
297         DIR *d;
298         struct dirent *dent;
299         char *path = NULL;
300         void *h;
301
302         d = opendir(dir);
303         if (d == NULL)
304                 return;
305         for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
306                 if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0)
307                         continue;
308                 asprintf(&path, "%s/%s", dir, dent->d_name);
309                 if (path == NULL)
310                         err(1, "Can't malloc for path, giving up.");
311                 if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL)
312                         warnx("Can't load %s: %s", path, dlerror());
313                 else {
314                         /*
315                          * Add in the top (for cli commands)linker sets. We have
316                          * to do this by hand because linker sets aren't
317                          * automatically merged.
318                          */
319                         void *begin, *limit;
320
321                         begin = dlsym(h, "__start_set_top");
322                         limit = dlsym(h, "__stop_set_top");
323                         if (begin)
324                                 add_to_top(begin, limit);
325                         /* log pages use constructors so are done on load */
326                 }
327                 free(path);
328                 path = NULL;
329         }
330         closedir(d);
331 }
332
333 int
334 main(int argc, char *argv[])
335 {
336
337         add_to_top(NVME_CMD_BEGIN(top), NVME_CMD_LIMIT(top));
338
339         load_dir("/lib/nvmecontrol");
340         load_dir("/usr/local/lib/nvmecontrol");
341
342         if (argc < 2)
343                 gen_usage_set(top_begin(), top_limit());
344
345         dispatch_set(argc, argv, top_begin(), top_limit());
346
347         return (0);
348 }