1 /* $NetBSD: wdc.c,v 1.1 2017/04/29 00:08:46 nonaka Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Netflix, Inc 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 #ifndef lint 31 __RCSID("$NetBSD: wdc.c,v 1.1 2017/04/29 00:08:46 nonaka Exp $"); 32 #if 0 33 __FBSDID("$FreeBSD: head/sbin/nvmecontrol/wdc.c 316105 2017-03-28 20:34:02Z ngie $"); 34 #endif 35 #endif 36 37 #include <sys/param.h> 38 #include <sys/ioccom.h> 39 #include <sys/endian.h> 40 41 #include <ctype.h> 42 #include <err.h> 43 #include <fcntl.h> 44 #include <stddef.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include "nvmectl.h" 51 52 #define WDC_NVME_TOC_SIZE 8 53 54 #define WDC_NVME_CAP_DIAG_OPCODE 0xe6 55 #define WDC_NVME_CAP_DIAG_CMD 0x0000 56 57 #define WDC_NVME_DIAG_OPCODE 0xc6 58 #define WDC_NVME_DRIVE_LOG_SIZE_CMD 0x0120 59 #define WDC_NVME_DRIVE_LOG_CMD 0x0020 60 #define WDC_NVME_CRASH_DUMP_SIZE_CMD 0x0320 61 #define WDC_NVME_CRASH_DUMP_CMD 0x0420 62 #define WDC_NVME_PFAIL_DUMP_SIZE_CMD 0x0520 63 #define WDC_NVME_PFAIL_DUMP_CMD 0x0620 64 65 #define WDC_NVME_CLEAR_DUMP_OPCODE 0xff 66 #define WDC_NVME_CLEAR_CRASH_DUMP_CMD 0x0503 67 #define WDC_NVME_CLEAR_PFAIL_DUMP_CMD 0x0603 68 69 static void wdc_cap_diag(int argc, char *argv[]); 70 static void wdc_drive_log(int argc, char *argv[]); 71 static void wdc_get_crash_dump(int argc, char *argv[]); 72 static void wdc_purge(int argc, char *argv[]); 73 static void wdc_purge_monitor(int argc, char *argv[]); 74 75 #define WDC_CAP_DIAG_USAGE "\tnvmecontrol wdc cap-diag [-o path-template]\n" 76 #define WDC_DRIVE_LOG_USAGE "\tnvmecontrol wdc drive-log [-o path-template]\n" 77 #define WDC_GET_CRASH_DUMP_USAGE "\tnvmecontrol wdc get-crash-dump [-o path-template]\n" 78 #define WDC_PURGE_USAGE "\tnvmecontrol wdc purge [-o path-template]\n" 79 #define WDC_PURGE_MONITOR_USAGE "\tnvmecontrol wdc purge-monitor\n" 80 81 static struct nvme_function wdc_funcs[] = { 82 {"cap-diag", wdc_cap_diag, WDC_CAP_DIAG_USAGE}, 83 {"drive-log", wdc_drive_log, WDC_DRIVE_LOG_USAGE}, 84 {"get-crash-dump", wdc_get_crash_dump, WDC_GET_CRASH_DUMP_USAGE}, 85 {"purge", wdc_purge, WDC_PURGE_USAGE}, 86 {"purge_monitor", wdc_purge_monitor, WDC_PURGE_MONITOR_USAGE}, 87 {NULL, NULL, NULL}, 88 }; 89 90 static void 91 wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix) 92 { 93 struct nvm_identify_controller cdata; 94 char sn[sizeof(cdata.sn) + 1]; 95 char *walker; 96 97 len -= strlen(buf); 98 buf += strlen(buf); 99 read_controller_data(fd, &cdata); 100 memcpy(sn, cdata.sn, sizeof(cdata.sn)); 101 walker = sn + sizeof(cdata.sn) - 1; 102 while (walker > sn && *walker == ' ') 103 walker--; 104 *++walker = '\0'; 105 snprintf(buf, len, "%s%s.bin", sn, suffix); 106 } 107 108 static void 109 wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd, 110 uint8_t *buffer, size_t buflen) 111 { 112 struct nvme_pt_command pt; 113 114 memset(&pt, 0, sizeof(pt)); 115 pt.cmd.opcode = opcode; 116 pt.cmd.cdw10 = len / sizeof(uint32_t); /* - 1 like all the others ??? */ 117 pt.cmd.cdw11 = off / sizeof(uint32_t); 118 pt.cmd.cdw12 = cmd; 119 pt.buf = buffer; 120 pt.len = buflen; 121 pt.is_read = 1; 122 // printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n", 123 // (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen); 124 125 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 126 err(1, "wdc_get_data request failed"); 127 if (nvme_completion_is_error(&pt.cpl)) 128 errx(1, "wdc_get_data request returned error"); 129 } 130 131 static void 132 wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode, 133 uint32_t size_cmd, uint32_t cmd, int len_off) 134 { 135 int fd2; 136 uint8_t *buf; 137 uint32_t len, offset; 138 ssize_t resid; 139 long page_size; 140 141 page_size = sysconf(_SC_PAGESIZE); 142 if (page_size <= 0) 143 page_size = 4096; 144 145 wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix); 146 147 buf = aligned_alloc(page_size, WDC_NVME_TOC_SIZE); 148 if (buf == NULL) 149 errx(1, "Can't get buffer to get size"); 150 wdc_get_data(fd, opcode, WDC_NVME_TOC_SIZE, 151 0, size_cmd, buf, WDC_NVME_TOC_SIZE); 152 len = be32dec(buf + len_off); 153 154 if (len == 0) 155 errx(1, "No data for %s", suffix); 156 157 printf("Dumping %d bytes to %s\n", len, tmpl); 158 /* XXX overwrite protection? */ 159 fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC); 160 if (fd2 < 0) 161 err(1, "open %s", tmpl); 162 offset = 0; 163 buf = aligned_alloc(page_size, NVME_MAX_XFER_SIZE); 164 if (buf == NULL) 165 errx(1, "Can't get buffer to read dump"); 166 while (len > 0) { 167 resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len; 168 wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid); 169 if (write(fd2, buf, resid) != resid) 170 err(1, "write"); 171 offset += resid; 172 len -= resid; 173 } 174 free(buf); 175 close(fd2); 176 } 177 178 static void 179 wdc_do_clear_dump(int fd, uint32_t opcode, uint32_t cmd) 180 { 181 struct nvme_pt_command pt; 182 183 memset(&pt, 0, sizeof(pt)); 184 pt.cmd.opcode = opcode; 185 pt.cmd.cdw12 = cmd; 186 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 187 err(1, "wdc_do_clear_dump request failed"); 188 if (nvme_completion_is_error(&pt.cpl)) 189 errx(1, "wdc_do_clear_dump request returned error"); 190 } 191 192 __dead static void 193 wdc_cap_diag_usage(void) 194 { 195 fprintf(stderr, "usage:\n"); 196 fprintf(stderr, WDC_CAP_DIAG_USAGE); 197 exit(1); 198 } 199 200 __dead static void 201 wdc_cap_diag(int argc, char *argv[]) 202 { 203 char path_tmpl[MAXPATHLEN]; 204 int ch, fd; 205 206 path_tmpl[0] = '\0'; 207 while ((ch = getopt(argc, argv, "o:")) != -1) { 208 switch ((char)ch) { 209 case 'o': 210 strlcpy(path_tmpl, optarg, MAXPATHLEN); 211 break; 212 default: 213 wdc_cap_diag_usage(); 214 } 215 } 216 /* Check that a controller was specified. */ 217 if (optind >= argc) 218 wdc_cap_diag_usage(); 219 open_dev(argv[optind], &fd, 1, 1); 220 221 wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE, 222 WDC_NVME_CAP_DIAG_CMD, WDC_NVME_CAP_DIAG_CMD, 4); 223 224 close(fd); 225 226 exit(1); 227 } 228 229 __dead static void 230 wdc_drive_log_usage(void) 231 { 232 fprintf(stderr, "usage:\n"); 233 fprintf(stderr, WDC_DRIVE_LOG_USAGE); 234 exit(1); 235 } 236 237 __dead static void 238 wdc_drive_log(int argc, char *argv[]) 239 { 240 char path_tmpl[MAXPATHLEN]; 241 int ch, fd; 242 243 path_tmpl[0] = '\0'; 244 while ((ch = getopt(argc, argv, "o:")) != -1) { 245 switch ((char)ch) { 246 case 'o': 247 strlcpy(path_tmpl, optarg, MAXPATHLEN); 248 break; 249 default: 250 wdc_drive_log_usage(); 251 } 252 } 253 /* Check that a controller was specified. */ 254 if (optind >= argc) 255 wdc_drive_log_usage(); 256 open_dev(argv[optind], &fd, 1, 1); 257 258 wdc_do_dump(fd, path_tmpl, "drive_log", WDC_NVME_DIAG_OPCODE, 259 WDC_NVME_DRIVE_LOG_SIZE_CMD, WDC_NVME_DRIVE_LOG_CMD, 0); 260 261 close(fd); 262 263 exit(1); 264 } 265 266 __dead static void 267 wdc_get_crash_dump_usage(void) 268 { 269 fprintf(stderr, "usage:\n"); 270 fprintf(stderr, WDC_CAP_DIAG_USAGE); 271 exit(1); 272 } 273 274 __dead static void 275 wdc_get_crash_dump(int argc, char *argv[]) 276 { 277 char path_tmpl[MAXPATHLEN]; 278 int ch, fd; 279 280 while ((ch = getopt(argc, argv, "o:")) != -1) { 281 switch ((char)ch) { 282 case 'o': 283 strlcpy(path_tmpl, optarg, MAXPATHLEN); 284 break; 285 default: 286 wdc_get_crash_dump_usage(); 287 } 288 } 289 /* Check that a controller was specified. */ 290 if (optind >= argc) 291 wdc_get_crash_dump_usage(); 292 open_dev(argv[optind], &fd, 1, 1); 293 294 wdc_do_dump(fd, path_tmpl, "crash_dump", WDC_NVME_DIAG_OPCODE, 295 WDC_NVME_CRASH_DUMP_SIZE_CMD, WDC_NVME_CRASH_DUMP_CMD, 0); 296 wdc_do_clear_dump(fd, WDC_NVME_CLEAR_DUMP_OPCODE, 297 WDC_NVME_CLEAR_CRASH_DUMP_CMD); 298 // wdc_led_beacon_disable(fd); 299 wdc_do_dump(fd, path_tmpl, "pfail_dump", WDC_NVME_DIAG_OPCODE, 300 WDC_NVME_PFAIL_DUMP_SIZE_CMD, WDC_NVME_PFAIL_DUMP_CMD, 0); 301 wdc_do_clear_dump(fd, WDC_NVME_CLEAR_DUMP_OPCODE, 302 WDC_NVME_CLEAR_PFAIL_DUMP_CMD); 303 304 close(fd); 305 306 exit(1); 307 } 308 309 __dead static void 310 wdc_purge(int argc, char *argv[]) 311 { 312 char path_tmpl[MAXPATHLEN]; 313 int ch; 314 315 while ((ch = getopt(argc, argv, "o:")) != -1) { 316 switch ((char)ch) { 317 case 'o': 318 strlcpy(path_tmpl, optarg, MAXPATHLEN); 319 break; 320 default: 321 wdc_cap_diag_usage(); 322 } 323 } 324 325 printf("purge has not been implemented.\n"); 326 exit(1); 327 } 328 329 __dead static void 330 wdc_purge_monitor(int argc, char *argv[]) 331 { 332 char path_tmpl[MAXPATHLEN]; 333 int ch; 334 335 while ((ch = getopt(argc, argv, "o:")) != -1) { 336 switch ((char)ch) { 337 case 'o': 338 strlcpy(path_tmpl, optarg, MAXPATHLEN); 339 break; 340 default: 341 wdc_cap_diag_usage(); 342 } 343 } 344 345 printf("purge has not been implemented.\n"); 346 exit(1); 347 } 348 349 void 350 wdc(int argc, char *argv[]) 351 { 352 353 dispatch(argc, argv, wdc_funcs); 354 } 355