]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/wdc.c
MFC r320522 (by imp):
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / wdc.c
1 /*-
2  * Copyright (c) 2017 Netflix, Inc
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/ioccom.h>
32 #include <sys/endian.h>
33
34 #include <ctype.h>
35 #include <err.h>
36 #include <fcntl.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include "nvmecontrol.h"
44
45 #define WDC_NVME_TOC_SIZE       8
46
47 #define WDC_NVME_CAP_DIAG_OPCODE        0xe6
48 #define WDC_NVME_CAP_DIAG_CMD           0x0000
49
50 static void wdc_cap_diag(int argc, char *argv[]);
51
52 #define WDC_CAP_DIAG_USAGE      "\tnvmecontrol wdc cap-diag [-o path-template]\n"
53
54 static struct nvme_function wdc_funcs[] = {
55         {"cap-diag",            wdc_cap_diag,           WDC_CAP_DIAG_USAGE},
56         {NULL,                  NULL,                   NULL},
57 };
58
59 static void
60 wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix)
61 {
62         struct nvme_controller_data     cdata;
63         char sn[NVME_SERIAL_NUMBER_LENGTH + 1];
64         char *walker;
65
66         len -= strlen(buf);
67         buf += strlen(buf);
68         read_controller_data(fd, &cdata);
69         memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH);
70         walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1;
71         while (walker > sn && *walker == ' ')
72                 walker--;
73         *++walker = '\0';
74         snprintf(buf, len, "%s%s.bin", sn, suffix);
75 }
76
77 static void
78 wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd,
79     uint8_t *buffer, size_t buflen)
80 {
81         struct nvme_pt_command  pt;
82
83         memset(&pt, 0, sizeof(pt));
84         pt.cmd.opc = opcode;
85         pt.cmd.cdw10 = len / sizeof(uint32_t);  /* - 1 like all the others ??? */
86         pt.cmd.cdw11 = off / sizeof(uint32_t);
87         pt.cmd.cdw12 = cmd;
88         pt.buf = buffer;
89         pt.len = buflen;
90         pt.is_read = 1;
91 //      printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n",
92 //          (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen);
93
94         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
95                 err(1, "wdc_get_data request failed");
96         if (nvme_completion_is_error(&pt.cpl))
97                 errx(1, "wdc_get_data request returned error");
98 }
99
100 static void
101 wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode,
102     uint32_t cmd, int len_off)
103 {
104         int first;
105         int fd2;
106         uint8_t *buf;
107         uint32_t len, offset;
108         size_t resid;
109
110         wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix);
111
112         /* XXX overwrite protection? */
113         fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644);
114         if (fd2 < 0)
115                 err(1, "open %s", tmpl);
116         buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE);
117         if (buf == NULL)
118                 errx(1, "Can't get buffer to read dump");
119         offset = 0;
120         len = NVME_MAX_XFER_SIZE;
121         first = 1;
122
123         do {
124                 resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len;
125                 wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid);
126
127                 if (first) {
128                         len = be32dec(buf + len_off);
129                         if (len == 0)
130                                 errx(1, "No data for %s", suffix);
131                         if (memcmp("E6LG", buf, 4) != 0)
132                                 printf("Expected header of E6LG, found '%4.4s' instead\n",
133                                     buf);
134                         printf("Dumping %d bytes of version %d.%d log to %s\n", len,
135                             buf[8], buf[9], tmpl);
136                         /*
137                          * Adjust amount to dump if total dump < 1MB,
138                          * though it likely doesn't matter to the WDC
139                          * analysis tools.
140                          */
141                         if (resid > len)
142                                 resid = len;
143                         first = 0;
144                 }
145                 if (write(fd2, buf, resid) != (ssize_t)resid)
146                         err(1, "write");
147                 offset += resid;
148                 len -= resid;
149         } while (len > 0);
150         free(buf);
151         close(fd2);
152 }
153
154 static void
155 wdc_cap_diag_usage(void)
156 {
157         fprintf(stderr, "usage:\n");
158         fprintf(stderr, WDC_CAP_DIAG_USAGE);
159         exit(1);
160 }
161
162 static void
163 wdc_cap_diag(int argc, char *argv[])
164 {
165         char path_tmpl[MAXPATHLEN];
166         int ch, fd;
167
168         path_tmpl[0] = '\0';
169         while ((ch = getopt(argc, argv, "o:")) != -1) {
170                 switch ((char)ch) {
171                 case 'o':
172                         strlcpy(path_tmpl, optarg, MAXPATHLEN);
173                         break;
174                 default:
175                         wdc_cap_diag_usage();
176                 }
177         }
178         /* Check that a controller was specified. */
179         if (optind >= argc)
180                 wdc_cap_diag_usage();
181         open_dev(argv[optind], &fd, 1, 1);
182
183         wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE,
184             WDC_NVME_CAP_DIAG_CMD, 4);
185
186         close(fd);
187
188         exit(1);        
189 }
190
191 void
192 wdc(int argc, char *argv[])
193 {
194
195         dispatch(argc, argv, wdc_funcs);
196 }