1eac8e827SWarner Losh /*- 252467047SWarner Losh * Copyright (c) 2017 Netflix, Inc. 3eac8e827SWarner Losh * 4eac8e827SWarner Losh * Redistribution and use in source and binary forms, with or without 5eac8e827SWarner Losh * modification, are permitted provided that the following conditions 6eac8e827SWarner Losh * are met: 7eac8e827SWarner Losh * 1. Redistributions of source code must retain the above copyright 8eac8e827SWarner Losh * notice, this list of conditions and the following disclaimer. 9eac8e827SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 10eac8e827SWarner Losh * notice, this list of conditions and the following disclaimer in the 11eac8e827SWarner Losh * documentation and/or other materials provided with the distribution. 12eac8e827SWarner Losh * 13eac8e827SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14eac8e827SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15eac8e827SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16eac8e827SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17eac8e827SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18eac8e827SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19eac8e827SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20eac8e827SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21eac8e827SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22eac8e827SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23eac8e827SWarner Losh * SUCH DAMAGE. 24eac8e827SWarner Losh */ 25eac8e827SWarner Losh 26eac8e827SWarner Losh #include <sys/cdefs.h> 27eac8e827SWarner Losh __FBSDID("$FreeBSD$"); 28eac8e827SWarner Losh 29eac8e827SWarner Losh #include <sys/param.h> 30eac8e827SWarner Losh #include <sys/ioccom.h> 31eac8e827SWarner Losh #include <sys/endian.h> 32eac8e827SWarner Losh 33eac8e827SWarner Losh #include <ctype.h> 34eac8e827SWarner Losh #include <err.h> 35eac8e827SWarner Losh #include <fcntl.h> 36eac8e827SWarner Losh #include <stddef.h> 37eac8e827SWarner Losh #include <stdio.h> 38eac8e827SWarner Losh #include <stdlib.h> 39eac8e827SWarner Losh #include <string.h> 405dc463f9SAlexander Motin #include <sysexits.h> 41eac8e827SWarner Losh #include <unistd.h> 42c03b1920SWarner Losh #include <stdbool.h> 43eac8e827SWarner Losh 44eac8e827SWarner Losh #include "nvmecontrol.h" 45eac8e827SWarner Losh 46f634b4c1SWarner Losh /* Tables for command line parsing */ 47eac8e827SWarner Losh 48f634b4c1SWarner Losh static cmd_fn_t wdc; 49f634b4c1SWarner Losh static cmd_fn_t wdc_cap_diag; 50f634b4c1SWarner Losh 51f634b4c1SWarner Losh #define NONE 0xffffffffu 52f634b4c1SWarner Losh #define NONE64 0xffffffffffffffffull 53f634b4c1SWarner Losh #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 54f634b4c1SWarner Losh #define OPT_END { NULL, 0, arg_none, NULL, NULL } 55f634b4c1SWarner Losh 56f634b4c1SWarner Losh static struct cmd wdc_cmd = { 57f634b4c1SWarner Losh .name = "wdc", .fn = wdc, .descr = "wdc vendor specific commands", .ctx_size = 0, .opts = NULL, .args = NULL, 58f634b4c1SWarner Losh }; 59f634b4c1SWarner Losh 60f634b4c1SWarner Losh CMD_COMMAND(wdc_cmd); 61f634b4c1SWarner Losh 62f634b4c1SWarner Losh static struct options 63f634b4c1SWarner Losh { 64f634b4c1SWarner Losh const char *template; 65f634b4c1SWarner Losh const char *dev; 66c03b1920SWarner Losh uint8_t data_area; 67f634b4c1SWarner Losh } opt = { 68f634b4c1SWarner Losh .template = NULL, 69f634b4c1SWarner Losh .dev = NULL, 70c03b1920SWarner Losh .data_area = 0, 71f634b4c1SWarner Losh }; 72f634b4c1SWarner Losh 73f634b4c1SWarner Losh static const struct opts opts[] = { 74f634b4c1SWarner Losh OPT("template", 'o', arg_string, opt, template, 75f634b4c1SWarner Losh "Template for paths to use for different logs"), 76c03b1920SWarner Losh OPT("data-area", 'd', arg_uint8, opt, data_area, 77c03b1920SWarner Losh "Data-area to retrieve up to"), 78f634b4c1SWarner Losh OPT_END 79f634b4c1SWarner Losh }; 80f634b4c1SWarner Losh 81f634b4c1SWarner Losh static const struct args args[] = { 82f634b4c1SWarner Losh { arg_string, &opt.dev, "controller-id" }, 83f634b4c1SWarner Losh { arg_none, NULL, NULL }, 84f634b4c1SWarner Losh }; 85f634b4c1SWarner Losh 86f634b4c1SWarner Losh static struct cmd cap_diag_cmd = { 87f634b4c1SWarner Losh .name = "cap-diag", 88f634b4c1SWarner Losh .fn = wdc_cap_diag, 89f634b4c1SWarner Losh .descr = "Retrieve the cap-diag logs from the drive", 90f634b4c1SWarner Losh .ctx_size = sizeof(struct options), 91f634b4c1SWarner Losh .opts = opts, 92f634b4c1SWarner Losh .args = args, 93f634b4c1SWarner Losh }; 94f634b4c1SWarner Losh 95f634b4c1SWarner Losh CMD_SUBCOMMAND(wdc_cmd, cap_diag_cmd); 96eac8e827SWarner Losh 97c03b1920SWarner Losh #define WDC_NVME_VID 0x1c58 98c03b1920SWarner Losh #define WDC_NVME_VID_2 0x1b96 99c03b1920SWarner Losh #define WDC_NVME_VID_3 0x15b7 100eac8e827SWarner Losh 101c03b1920SWarner Losh #define WDC_NVME_TOC_SIZE 0x8 102c03b1920SWarner Losh #define WDC_NVME_LOG_SIZE_HDR_LEN 0x8 103c03b1920SWarner Losh #define WDC_NVME_CAP_DIAG_OPCODE_E6 0xe6 104eac8e827SWarner Losh #define WDC_NVME_CAP_DIAG_CMD 0x0000 105c03b1920SWarner Losh #define WDC_NVME_CAP_DIAG_OPCODE_FA 0xfa 106c03b1920SWarner Losh #define WDC_NVME_DUI_MAX_SECTIONS_V0 0x3c 107c03b1920SWarner Losh #define WDC_NVME_DUI_MAX_SECTIONS_V1 0x3a 108c03b1920SWarner Losh #define WDC_NVME_DUI_MAX_SECTIONS_V2 0x26 109c03b1920SWarner Losh #define WDC_NVME_DUI_MAX_SECTIONS_V3 0x23 110c03b1920SWarner Losh 111c03b1920SWarner Losh typedef enum wdc_dui_header { 112c03b1920SWarner Losh WDC_DUI_HEADER_VER_0 = 0, 113c03b1920SWarner Losh WDC_DUI_HEADER_VER_1, 114c03b1920SWarner Losh WDC_DUI_HEADER_VER_2, 115c03b1920SWarner Losh WDC_DUI_HEADER_VER_3, 116c03b1920SWarner Losh } wdc_dui_header; 117eac8e827SWarner Losh 118eac8e827SWarner Losh static void 119eac8e827SWarner Losh wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix) 120eac8e827SWarner Losh { 121eac8e827SWarner Losh struct nvme_controller_data cdata; 122eac8e827SWarner Losh char sn[NVME_SERIAL_NUMBER_LENGTH + 1]; 123eac8e827SWarner Losh char *walker; 124eac8e827SWarner Losh 125eac8e827SWarner Losh len -= strlen(buf); 126eac8e827SWarner Losh buf += strlen(buf); 1275dc463f9SAlexander Motin if (read_controller_data(fd, &cdata)) 1285dc463f9SAlexander Motin errx(EX_IOERR, "Identify request failed"); 129eac8e827SWarner Losh memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH); 130eac8e827SWarner Losh walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1; 131eac8e827SWarner Losh while (walker > sn && *walker == ' ') 132eac8e827SWarner Losh walker--; 133eac8e827SWarner Losh *++walker = '\0'; 134c03b1920SWarner Losh snprintf(buf, len, "_%s_%s.bin", sn, suffix); 135eac8e827SWarner Losh } 136eac8e827SWarner Losh 137eac8e827SWarner Losh static void 138eac8e827SWarner Losh wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd, 139c03b1920SWarner Losh uint8_t *buffer, size_t buflen, bool e6lg_flag) 140eac8e827SWarner Losh { 141eac8e827SWarner Losh struct nvme_pt_command pt; 142eac8e827SWarner Losh 143eac8e827SWarner Losh memset(&pt, 0, sizeof(pt)); 144eac8e827SWarner Losh pt.cmd.opc = opcode; 145c03b1920SWarner Losh pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); 146eac8e827SWarner Losh pt.cmd.cdw12 = htole32(cmd); 147c03b1920SWarner Losh if (e6lg_flag) 148c03b1920SWarner Losh pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); 149c03b1920SWarner Losh else 150c03b1920SWarner Losh pt.cmd.cdw13 = htole32(off / sizeof(uint32_t)); 151eac8e827SWarner Losh pt.buf = buffer; 152eac8e827SWarner Losh pt.len = buflen; 153eac8e827SWarner Losh pt.is_read = 1; 154eac8e827SWarner Losh 155eac8e827SWarner Losh if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 1565dc463f9SAlexander Motin err(EX_IOERR, "wdc_get_data request failed"); 157eac8e827SWarner Losh if (nvme_completion_is_error(&pt.cpl)) 1585dc463f9SAlexander Motin errx(EX_IOERR, "wdc_get_data request returned error"); 159eac8e827SWarner Losh } 160eac8e827SWarner Losh 161eac8e827SWarner Losh static void 162c03b1920SWarner Losh wdc_do_dump_e6(int fd, char *tmpl, const char *suffix, uint32_t opcode, 163eac8e827SWarner Losh uint32_t cmd, int len_off) 164eac8e827SWarner Losh { 165eac8e827SWarner Losh int first; 166eac8e827SWarner Losh int fd2; 167c03b1920SWarner Losh uint8_t *buf, *hdr; 168329327e2SAlexander Motin uint64_t max_xfer_size; 169eac8e827SWarner Losh uint32_t len, offset; 170eac8e827SWarner Losh size_t resid; 171c03b1920SWarner Losh bool e6lg_flag = false; 172eac8e827SWarner Losh 173eac8e827SWarner Losh wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix); 174eac8e827SWarner Losh 175c03b1920SWarner Losh /* Read Log Dump header */ 176c03b1920SWarner Losh len = WDC_NVME_LOG_SIZE_HDR_LEN; 177c03b1920SWarner Losh offset = 0; 178c03b1920SWarner Losh hdr = malloc(len); 179c03b1920SWarner Losh if (hdr == NULL) 1805dc463f9SAlexander Motin errx(EX_OSERR, "Can't get buffer to read dump"); 181c03b1920SWarner Losh wdc_get_data(fd, opcode, len, offset, cmd, hdr, len, false); 182c03b1920SWarner Losh if (memcmp("E6LG", hdr, 4) == 0) { 183c03b1920SWarner Losh e6lg_flag = true; 184c03b1920SWarner Losh } 185c03b1920SWarner Losh 186eac8e827SWarner Losh /* XXX overwrite protection? */ 187eac8e827SWarner Losh fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644); 188eac8e827SWarner Losh if (fd2 < 0) 1895dc463f9SAlexander Motin err(EX_CANTCREAT, "open %s", tmpl); 190329327e2SAlexander Motin if (ioctl(fd, NVME_GET_MAX_XFER_SIZE, &max_xfer_size) < 0) 191329327e2SAlexander Motin err(EX_IOERR, "query max transfer size failed"); 192329327e2SAlexander Motin buf = aligned_alloc(PAGE_SIZE, max_xfer_size); 193eac8e827SWarner Losh if (buf == NULL) 1945dc463f9SAlexander Motin errx(EX_OSERR, "Can't get buffer to read dump"); 195eac8e827SWarner Losh offset = 0; 196329327e2SAlexander Motin len = max_xfer_size; 197eac8e827SWarner Losh first = 1; 198eac8e827SWarner Losh 199eac8e827SWarner Losh do { 200329327e2SAlexander Motin resid = MIN(len, max_xfer_size); 201c03b1920SWarner Losh wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid, e6lg_flag); 202eac8e827SWarner Losh 203eac8e827SWarner Losh if (first) { 204eac8e827SWarner Losh len = be32dec(buf + len_off); 205eac8e827SWarner Losh if (len == 0) 2065dc463f9SAlexander Motin errx(EX_PROTOCOL, "No data for %s", suffix); 207c03b1920SWarner Losh 208eac8e827SWarner Losh printf("Dumping %d bytes of version %d.%d log to %s\n", len, 209eac8e827SWarner Losh buf[8], buf[9], tmpl); 210eac8e827SWarner Losh /* 211eac8e827SWarner Losh * Adjust amount to dump if total dump < 1MB, 212eac8e827SWarner Losh * though it likely doesn't matter to the WDC 213eac8e827SWarner Losh * analysis tools. 214eac8e827SWarner Losh */ 215eac8e827SWarner Losh if (resid > len) 216eac8e827SWarner Losh resid = len; 217eac8e827SWarner Losh first = 0; 218eac8e827SWarner Losh } 219eac8e827SWarner Losh if (write(fd2, buf, resid) != (ssize_t)resid) 2205dc463f9SAlexander Motin err(EX_IOERR, "write"); 221eac8e827SWarner Losh offset += resid; 222eac8e827SWarner Losh len -= resid; 223eac8e827SWarner Losh } while (len > 0); 224c03b1920SWarner Losh free(hdr); 225c03b1920SWarner Losh free(buf); 226c03b1920SWarner Losh close(fd2); 227c03b1920SWarner Losh } 228c03b1920SWarner Losh 229c03b1920SWarner Losh static void 230c03b1920SWarner Losh wdc_get_data_dui(int fd, uint32_t opcode, uint32_t len, uint64_t off, 231c03b1920SWarner Losh uint8_t *buffer, size_t buflen) 232c03b1920SWarner Losh { 233c03b1920SWarner Losh struct nvme_pt_command pt; 234c03b1920SWarner Losh 235c03b1920SWarner Losh memset(&pt, 0, sizeof(pt)); 236c03b1920SWarner Losh pt.cmd.opc = opcode; 237c03b1920SWarner Losh pt.cmd.nsid = NONE; 238c03b1920SWarner Losh pt.cmd.cdw10 = htole32((len / sizeof(uint32_t)) - 1) ; 239c03b1920SWarner Losh pt.cmd.cdw12 = htole32(off & 0xFFFFFFFFu); 240c03b1920SWarner Losh pt.cmd.cdw13 = htole32(off >> 32); 241c03b1920SWarner Losh pt.buf = buffer; 242c03b1920SWarner Losh pt.len = buflen; 243c03b1920SWarner Losh pt.is_read = 1; 244c03b1920SWarner Losh 245c03b1920SWarner Losh if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 2465dc463f9SAlexander Motin err(EX_IOERR, "wdc_get_data_dui request failed"); 247c03b1920SWarner Losh if (nvme_completion_is_error(&pt.cpl)) 2485dc463f9SAlexander Motin errx(EX_IOERR, "wdc_get_data_dui request returned error"); 249c03b1920SWarner Losh } 250c03b1920SWarner Losh 251c03b1920SWarner Losh static uint8_t 252c03b1920SWarner Losh wdc_get_dui_max_sections(uint16_t header_ver) 253c03b1920SWarner Losh { 254c03b1920SWarner Losh switch (header_ver) { 255c03b1920SWarner Losh case WDC_DUI_HEADER_VER_0: 256c03b1920SWarner Losh return WDC_NVME_DUI_MAX_SECTIONS_V0; 257c03b1920SWarner Losh case WDC_DUI_HEADER_VER_1: 258c03b1920SWarner Losh return WDC_NVME_DUI_MAX_SECTIONS_V1; 259c03b1920SWarner Losh case WDC_DUI_HEADER_VER_2: 260c03b1920SWarner Losh return WDC_NVME_DUI_MAX_SECTIONS_V2; 261c03b1920SWarner Losh case WDC_DUI_HEADER_VER_3: 262c03b1920SWarner Losh return WDC_NVME_DUI_MAX_SECTIONS_V3; 263c03b1920SWarner Losh } 264c03b1920SWarner Losh return 0; 265c03b1920SWarner Losh } 266c03b1920SWarner Losh 267c03b1920SWarner Losh static void 268c03b1920SWarner Losh wdc_get_dui_log_size(int fd, uint32_t opcode, uint8_t data_area, 269c03b1920SWarner Losh uint64_t *log_size, int len_off) 270c03b1920SWarner Losh { 2711187e46dSJohn Baldwin uint8_t *hdr, *tofree; 272c03b1920SWarner Losh uint8_t max_sections; 273c03b1920SWarner Losh int i, j; 274c03b1920SWarner Losh uint16_t hdr_ver; 275c03b1920SWarner Losh uint16_t len; 276c03b1920SWarner Losh uint64_t dui_size; 277c03b1920SWarner Losh 278c03b1920SWarner Losh dui_size = 0; 279c03b1920SWarner Losh len = 1024; 2801187e46dSJohn Baldwin tofree = hdr = (uint8_t*)malloc(len); 281c03b1920SWarner Losh if (hdr == NULL) 2825dc463f9SAlexander Motin errx(EX_OSERR, "Can't get buffer to read header"); 283c03b1920SWarner Losh wdc_get_data_dui(fd, opcode, len, 0, hdr, len); 284c03b1920SWarner Losh 285c03b1920SWarner Losh hdr += len_off; 286c03b1920SWarner Losh hdr_ver = ((*hdr & 0xF) != 0)? *hdr : le16dec(hdr); 287c03b1920SWarner Losh max_sections = wdc_get_dui_max_sections(hdr_ver); 288c03b1920SWarner Losh 289c03b1920SWarner Losh if (hdr_ver == 0 || hdr_ver == 1) { 290c03b1920SWarner Losh dui_size = (uint64_t)le32dec(hdr + 4); 291c03b1920SWarner Losh if (dui_size == 0) { 292c03b1920SWarner Losh hdr += 8; 293c03b1920SWarner Losh for (i = 0, j = 0; i < (int)max_sections; i++, j+=8) 294c03b1920SWarner Losh dui_size += (uint64_t)le32dec(hdr + j + 4); 295c03b1920SWarner Losh } 296c03b1920SWarner Losh } else if (hdr_ver == 2 || hdr_ver == 3) { 297c03b1920SWarner Losh if (data_area == 0) { 298c03b1920SWarner Losh dui_size = le64dec(hdr + 4); 299c03b1920SWarner Losh if (dui_size == 0) { 300c03b1920SWarner Losh hdr += 12; 301c03b1920SWarner Losh for (i = 0, j = 0 ; i < (int)max_sections; i++, j+=12) 302c03b1920SWarner Losh dui_size += le64dec(hdr + j + 4); 303c03b1920SWarner Losh } 304c03b1920SWarner Losh } else { 305c03b1920SWarner Losh hdr += 12; 306c03b1920SWarner Losh for (i = 0, j = 0; i < (int)max_sections; i++, j+=12) { 307c03b1920SWarner Losh if (le16dec(hdr + j + 2) <= data_area) 308c03b1920SWarner Losh dui_size += le64dec(hdr + j + 4); 309c03b1920SWarner Losh else 310c03b1920SWarner Losh break; 311c03b1920SWarner Losh } 312c03b1920SWarner Losh } 313c03b1920SWarner Losh } 314c03b1920SWarner Losh else 3155dc463f9SAlexander Motin errx(EX_PROTOCOL, "ERROR : No valid header "); 316c03b1920SWarner Losh 317c03b1920SWarner Losh *log_size = dui_size; 3181187e46dSJohn Baldwin free(tofree); 319c03b1920SWarner Losh } 320c03b1920SWarner Losh 321c03b1920SWarner Losh static void 322c03b1920SWarner Losh wdc_do_dump_dui(int fd, char *tmpl, uint8_t data_area, 323c03b1920SWarner Losh const char *suffix, uint32_t opcode, int len_off) 324c03b1920SWarner Losh { 325c03b1920SWarner Losh int fd2, first; 326c03b1920SWarner Losh uint8_t *buf; 327329327e2SAlexander Motin uint64_t max_xfer_size; 328c03b1920SWarner Losh uint16_t hdr_ver; 329c03b1920SWarner Losh uint64_t log_len, offset; 330c03b1920SWarner Losh size_t resid; 331c03b1920SWarner Losh 332c03b1920SWarner Losh wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix); 333c03b1920SWarner Losh wdc_get_dui_log_size(fd, opcode, data_area, &log_len, len_off); 334c03b1920SWarner Losh if (log_len == 0) 3355dc463f9SAlexander Motin errx(EX_PROTOCOL, "No data for %s", suffix); 336c03b1920SWarner Losh fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644); 337c03b1920SWarner Losh if (fd2 < 0) 3385dc463f9SAlexander Motin err(EX_CANTCREAT, "open %s", tmpl); 339329327e2SAlexander Motin if (ioctl(fd, NVME_GET_MAX_XFER_SIZE, &max_xfer_size) < 0) 340329327e2SAlexander Motin err(EX_IOERR, "query max transfer size failed"); 341329327e2SAlexander Motin buf = aligned_alloc(PAGE_SIZE, max_xfer_size); 342c03b1920SWarner Losh if (buf == NULL) 3435dc463f9SAlexander Motin errx(EX_OSERR, "Can't get buffer to read dump"); 344c03b1920SWarner Losh offset = 0; 345c03b1920SWarner Losh first = 1; 346c03b1920SWarner Losh 347c03b1920SWarner Losh while (log_len > 0) { 348329327e2SAlexander Motin resid = MIN(log_len, max_xfer_size); 349c03b1920SWarner Losh wdc_get_data_dui(fd, opcode, resid, offset, buf, resid); 350c03b1920SWarner Losh if (first) { 351c03b1920SWarner Losh hdr_ver = ((buf[len_off] & 0xF) != 0) ? 352c03b1920SWarner Losh (buf[len_off]) : (le16dec(buf + len_off)); 353edad0330SWarner Losh printf("Dumping %jd bytes of version %d log to %s\n", 354edad0330SWarner Losh (uintmax_t)log_len, hdr_ver, tmpl); 355c03b1920SWarner Losh first = 0; 356c03b1920SWarner Losh } 357c03b1920SWarner Losh if (write(fd2, buf, resid) != (ssize_t)resid) 3585dc463f9SAlexander Motin err(EX_IOERR, "write"); 359c03b1920SWarner Losh offset += resid; 360c03b1920SWarner Losh log_len -= resid; 361c03b1920SWarner Losh } 362c03b1920SWarner Losh 363eac8e827SWarner Losh free(buf); 364eac8e827SWarner Losh close(fd2); 365eac8e827SWarner Losh } 366eac8e827SWarner Losh 367eac8e827SWarner Losh static void 368f634b4c1SWarner Losh wdc_cap_diag(const struct cmd *f, int argc, char *argv[]) 369eac8e827SWarner Losh { 370f634b4c1SWarner Losh char tmpl[MAXPATHLEN]; 371f634b4c1SWarner Losh int fd; 372c03b1920SWarner Losh struct nvme_controller_data cdata; 373c03b1920SWarner Losh uint32_t vid; 374eac8e827SWarner Losh 375f634b4c1SWarner Losh if (arg_parse(argc, argv, f)) 376f634b4c1SWarner Losh return; 377f634b4c1SWarner Losh if (opt.template == NULL) { 378f634b4c1SWarner Losh fprintf(stderr, "Missing template arg.\n"); 379f634b4c1SWarner Losh arg_help(argc, argv, f); 380eac8e827SWarner Losh } 381c03b1920SWarner Losh if (opt.data_area > 4) { 382c03b1920SWarner Losh fprintf(stderr, "Data area range 1-4, supplied %d.\n", opt.data_area); 383c03b1920SWarner Losh arg_help(argc, argv, f); 384c03b1920SWarner Losh } 385f634b4c1SWarner Losh strlcpy(tmpl, opt.template, sizeof(tmpl)); 386f634b4c1SWarner Losh open_dev(opt.dev, &fd, 1, 1); 3875dc463f9SAlexander Motin if (read_controller_data(fd, &cdata)) 3885dc463f9SAlexander Motin errx(EX_IOERR, "Identify request failed"); 389c03b1920SWarner Losh vid = cdata.vid; 390eac8e827SWarner Losh 391c03b1920SWarner Losh switch (vid) { 392c03b1920SWarner Losh case WDC_NVME_VID : 393c03b1920SWarner Losh case WDC_NVME_VID_2 : 394c03b1920SWarner Losh wdc_do_dump_e6(fd, tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE_E6, 395c03b1920SWarner Losh WDC_NVME_CAP_DIAG_CMD, 4); 396c03b1920SWarner Losh break; 397c03b1920SWarner Losh case WDC_NVME_VID_3 : 398c03b1920SWarner Losh wdc_do_dump_dui(fd, tmpl, opt.data_area, "cap_diag", 399c03b1920SWarner Losh WDC_NVME_CAP_DIAG_OPCODE_FA, 512); 400c03b1920SWarner Losh break; 401c03b1920SWarner Losh default: 4025dc463f9SAlexander Motin errx(EX_UNAVAILABLE, "ERROR : WDC: unsupported device (%#x) for this command", vid); 403c03b1920SWarner Losh } 404eac8e827SWarner Losh close(fd); 4055dc463f9SAlexander Motin exit(0); 406eac8e827SWarner Losh } 407eac8e827SWarner Losh 408eac8e827SWarner Losh static void 409f634b4c1SWarner Losh wdc(const struct cmd *nf __unused, int argc, char *argv[]) 410eac8e827SWarner Losh { 411eac8e827SWarner Losh 412f634b4c1SWarner Losh cmd_dispatch(argc, argv, &wdc_cmd); 413eac8e827SWarner Losh } 414eac8e827SWarner Losh 415eac8e827SWarner Losh /* 416eac8e827SWarner Losh * HGST's 0xc1 page. This is a grab bag of additional data. Please see 417eac8e827SWarner Losh * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf 418eac8e827SWarner Losh * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf 419eac8e827SWarner Losh * Appendix A for details 420eac8e827SWarner Losh */ 421eac8e827SWarner Losh 422eac8e827SWarner Losh typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 423eac8e827SWarner Losh 424eac8e827SWarner Losh struct subpage_print 425eac8e827SWarner Losh { 426eac8e827SWarner Losh uint16_t key; 427eac8e827SWarner Losh subprint_fn_t fn; 428eac8e827SWarner Losh }; 429eac8e827SWarner Losh 430eac8e827SWarner Losh static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 431eac8e827SWarner Losh static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 432eac8e827SWarner Losh static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 433eac8e827SWarner Losh static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 434eac8e827SWarner Losh static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 435eac8e827SWarner Losh static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 436eac8e827SWarner Losh static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 437eac8e827SWarner Losh static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 438eac8e827SWarner Losh static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 439eac8e827SWarner Losh static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 440eac8e827SWarner Losh 441eac8e827SWarner Losh static struct subpage_print hgst_subpage[] = { 442eac8e827SWarner Losh { 0x02, print_hgst_info_write_errors }, 443eac8e827SWarner Losh { 0x03, print_hgst_info_read_errors }, 444eac8e827SWarner Losh { 0x05, print_hgst_info_verify_errors }, 445eac8e827SWarner Losh { 0x10, print_hgst_info_self_test }, 446eac8e827SWarner Losh { 0x15, print_hgst_info_background_scan }, 447eac8e827SWarner Losh { 0x30, print_hgst_info_erase_errors }, 448eac8e827SWarner Losh { 0x31, print_hgst_info_erase_counts }, 449eac8e827SWarner Losh { 0x32, print_hgst_info_temp_history }, 450eac8e827SWarner Losh { 0x37, print_hgst_info_ssd_perf }, 451eac8e827SWarner Losh { 0x38, print_hgst_info_firmware_load }, 452eac8e827SWarner Losh }; 453eac8e827SWarner Losh 454eac8e827SWarner Losh /* Print a subpage that is basically just key value pairs */ 455eac8e827SWarner Losh static void 456eac8e827SWarner Losh print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, 457eac8e827SWarner Losh const struct kv_name *kv, size_t kv_count) 458eac8e827SWarner Losh { 459eac8e827SWarner Losh uint8_t *wsp, *esp; 460eac8e827SWarner Losh uint16_t ptype; 461eac8e827SWarner Losh uint8_t plen; 462eac8e827SWarner Losh uint64_t param; 463eac8e827SWarner Losh int i; 464eac8e827SWarner Losh 465eac8e827SWarner Losh wsp = buf; 466eac8e827SWarner Losh esp = wsp + size; 467eac8e827SWarner Losh while (wsp < esp) { 468eac8e827SWarner Losh ptype = le16dec(wsp); 469eac8e827SWarner Losh wsp += 2; 470eac8e827SWarner Losh wsp++; /* Flags, just ignore */ 471eac8e827SWarner Losh plen = *wsp++; 472eac8e827SWarner Losh param = 0; 4736995fb5eSDavid Bright for (i = 0; i < plen && wsp < esp; i++) 474eac8e827SWarner Losh param |= (uint64_t)*wsp++ << (i * 8); 475eac8e827SWarner Losh printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); 476eac8e827SWarner Losh } 477eac8e827SWarner Losh } 478eac8e827SWarner Losh 479eac8e827SWarner Losh static void 480eac8e827SWarner Losh print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 481eac8e827SWarner Losh { 482eac8e827SWarner Losh static struct kv_name kv[] = 483eac8e827SWarner Losh { 484eac8e827SWarner Losh { 0x0000, "Corrected Without Delay" }, 485eac8e827SWarner Losh { 0x0001, "Corrected Maybe Delayed" }, 486eac8e827SWarner Losh { 0x0002, "Re-Writes" }, 487eac8e827SWarner Losh { 0x0003, "Errors Corrected" }, 488eac8e827SWarner Losh { 0x0004, "Correct Algorithm Used" }, 489eac8e827SWarner Losh { 0x0005, "Bytes Processed" }, 490eac8e827SWarner Losh { 0x0006, "Uncorrected Errors" }, 491eac8e827SWarner Losh { 0x8000, "Flash Write Commands" }, 492eac8e827SWarner Losh { 0x8001, "HGST Special" }, 493eac8e827SWarner Losh }; 494eac8e827SWarner Losh 495eac8e827SWarner Losh printf("Write Errors Subpage:\n"); 496eac8e827SWarner Losh print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 497eac8e827SWarner Losh } 498eac8e827SWarner Losh 499eac8e827SWarner Losh static void 500eac8e827SWarner Losh print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 501eac8e827SWarner Losh { 502eac8e827SWarner Losh static struct kv_name kv[] = 503eac8e827SWarner Losh { 504eac8e827SWarner Losh { 0x0000, "Corrected Without Delay" }, 505eac8e827SWarner Losh { 0x0001, "Corrected Maybe Delayed" }, 506eac8e827SWarner Losh { 0x0002, "Re-Reads" }, 507eac8e827SWarner Losh { 0x0003, "Errors Corrected" }, 508eac8e827SWarner Losh { 0x0004, "Correct Algorithm Used" }, 509eac8e827SWarner Losh { 0x0005, "Bytes Processed" }, 510eac8e827SWarner Losh { 0x0006, "Uncorrected Errors" }, 511eac8e827SWarner Losh { 0x8000, "Flash Read Commands" }, 512eac8e827SWarner Losh { 0x8001, "XOR Recovered" }, 513eac8e827SWarner Losh { 0x8002, "Total Corrected Bits" }, 514eac8e827SWarner Losh }; 515eac8e827SWarner Losh 516eac8e827SWarner Losh printf("Read Errors Subpage:\n"); 517eac8e827SWarner Losh print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 518eac8e827SWarner Losh } 519eac8e827SWarner Losh 520eac8e827SWarner Losh static void 521eac8e827SWarner Losh print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 522eac8e827SWarner Losh { 523eac8e827SWarner Losh static struct kv_name kv[] = 524eac8e827SWarner Losh { 525eac8e827SWarner Losh { 0x0000, "Corrected Without Delay" }, 526eac8e827SWarner Losh { 0x0001, "Corrected Maybe Delayed" }, 527eac8e827SWarner Losh { 0x0002, "Re-Reads" }, 528eac8e827SWarner Losh { 0x0003, "Errors Corrected" }, 529eac8e827SWarner Losh { 0x0004, "Correct Algorithm Used" }, 530eac8e827SWarner Losh { 0x0005, "Bytes Processed" }, 531eac8e827SWarner Losh { 0x0006, "Uncorrected Errors" }, 532eac8e827SWarner Losh { 0x8000, "Commands Processed" }, 533eac8e827SWarner Losh }; 534eac8e827SWarner Losh 535eac8e827SWarner Losh printf("Verify Errors Subpage:\n"); 536eac8e827SWarner Losh print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 537eac8e827SWarner Losh } 538eac8e827SWarner Losh 539eac8e827SWarner Losh static void 540eac8e827SWarner Losh print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 541eac8e827SWarner Losh { 542eac8e827SWarner Losh size_t i; 543eac8e827SWarner Losh uint8_t *walker = buf; 544eac8e827SWarner Losh uint16_t code, hrs; 545eac8e827SWarner Losh uint32_t lba; 546eac8e827SWarner Losh 547eac8e827SWarner Losh printf("Self Test Subpage:\n"); 548eac8e827SWarner Losh for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ 549eac8e827SWarner Losh code = le16dec(walker); 550eac8e827SWarner Losh walker += 2; 551eac8e827SWarner Losh walker++; /* Ignore fixed flags */ 552eac8e827SWarner Losh if (*walker == 0) /* Last entry is zero length */ 553eac8e827SWarner Losh break; 554eac8e827SWarner Losh if (*walker++ != 0x10) { 555eac8e827SWarner Losh printf("Bad length for self test report\n"); 556eac8e827SWarner Losh return; 557eac8e827SWarner Losh } 558eac8e827SWarner Losh printf(" %-30s: %d\n", "Recent Test", code); 559eac8e827SWarner Losh printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); 560eac8e827SWarner Losh printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); 561eac8e827SWarner Losh walker++; 562eac8e827SWarner Losh printf(" %-28s: %#x\n", "Self-Test Number", *walker++); 563eac8e827SWarner Losh hrs = le16dec(walker); 564eac8e827SWarner Losh walker += 2; 565eac8e827SWarner Losh lba = le32dec(walker); 566eac8e827SWarner Losh walker += 4; 567eac8e827SWarner Losh printf(" %-28s: %u\n", "Total Power On Hrs", hrs); 568eac8e827SWarner Losh printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); 569eac8e827SWarner Losh printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); 570eac8e827SWarner Losh printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); 571eac8e827SWarner Losh printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); 572eac8e827SWarner Losh printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); 573eac8e827SWarner Losh } 574eac8e827SWarner Losh } 575eac8e827SWarner Losh 576eac8e827SWarner Losh static void 577eac8e827SWarner Losh print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 578eac8e827SWarner Losh { 579eac8e827SWarner Losh uint8_t *walker = buf; 580eac8e827SWarner Losh uint8_t status; 581eac8e827SWarner Losh uint16_t code, nscan, progress; 582eac8e827SWarner Losh uint32_t pom, nand; 583eac8e827SWarner Losh 584eac8e827SWarner Losh printf("Background Media Scan Subpage:\n"); 585eac8e827SWarner Losh /* Decode the header */ 586eac8e827SWarner Losh code = le16dec(walker); 587eac8e827SWarner Losh walker += 2; 588eac8e827SWarner Losh walker++; /* Ignore fixed flags */ 589eac8e827SWarner Losh if (*walker++ != 0x10) { 590eac8e827SWarner Losh printf("Bad length for background scan header\n"); 591eac8e827SWarner Losh return; 592eac8e827SWarner Losh } 593eac8e827SWarner Losh if (code != 0) { 594*abe10d21SAndrius V printf("Expected code 0, found code %#x\n", code); 595eac8e827SWarner Losh return; 596eac8e827SWarner Losh } 597eac8e827SWarner Losh pom = le32dec(walker); 598eac8e827SWarner Losh walker += 4; 599eac8e827SWarner Losh walker++; /* Reserved */ 600eac8e827SWarner Losh status = *walker++; 601eac8e827SWarner Losh nscan = le16dec(walker); 602eac8e827SWarner Losh walker += 2; 603eac8e827SWarner Losh progress = le16dec(walker); 604eac8e827SWarner Losh walker += 2; 605eac8e827SWarner Losh walker += 6; /* Reserved */ 606eac8e827SWarner Losh printf(" %-30s: %d\n", "Power On Minutes", pom); 607eac8e827SWarner Losh printf(" %-30s: %x (%s)\n", "BMS Status", status, 608eac8e827SWarner Losh status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); 609eac8e827SWarner Losh printf(" %-30s: %d\n", "Number of BMS", nscan); 610eac8e827SWarner Losh printf(" %-30s: %d\n", "Progress Current BMS", progress); 611eac8e827SWarner Losh /* Report retirements */ 612eac8e827SWarner Losh if (walker - (uint8_t *)buf != 20) { 613eac8e827SWarner Losh printf("Coding error, offset not 20\n"); 614eac8e827SWarner Losh return; 615eac8e827SWarner Losh } 616eac8e827SWarner Losh size -= 20; 617eac8e827SWarner Losh printf(" %-30s: %d\n", "BMS retirements", size / 0x18); 618eac8e827SWarner Losh while (size > 0) { 619eac8e827SWarner Losh code = le16dec(walker); 620eac8e827SWarner Losh walker += 2; 621eac8e827SWarner Losh walker++; 622eac8e827SWarner Losh if (*walker++ != 0x14) { 623eac8e827SWarner Losh printf("Bad length parameter\n"); 624eac8e827SWarner Losh return; 625eac8e827SWarner Losh } 626eac8e827SWarner Losh pom = le32dec(walker); 627eac8e827SWarner Losh walker += 4; 628eac8e827SWarner Losh /* 629eac8e827SWarner Losh * Spec sheet says the following are hard coded, if true, just 630eac8e827SWarner Losh * print the NAND retirement. 631eac8e827SWarner Losh */ 632eac8e827SWarner Losh if (walker[0] == 0x41 && 633eac8e827SWarner Losh walker[1] == 0x0b && 634eac8e827SWarner Losh walker[2] == 0x01 && 635eac8e827SWarner Losh walker[3] == 0x00 && 636eac8e827SWarner Losh walker[4] == 0x00 && 637eac8e827SWarner Losh walker[5] == 0x00 && 638eac8e827SWarner Losh walker[6] == 0x00 && 639eac8e827SWarner Losh walker[7] == 0x00) { 640eac8e827SWarner Losh walker += 8; 641eac8e827SWarner Losh walker += 4; /* Skip reserved */ 642eac8e827SWarner Losh nand = le32dec(walker); 643eac8e827SWarner Losh walker += 4; 644eac8e827SWarner Losh printf(" %-30s: %d\n", "Retirement number", code); 645eac8e827SWarner Losh printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); 646eac8e827SWarner Losh } else { 647eac8e827SWarner Losh printf("Parameter %#x entry corrupt\n", code); 648eac8e827SWarner Losh walker += 16; 649eac8e827SWarner Losh } 650eac8e827SWarner Losh } 651eac8e827SWarner Losh } 652eac8e827SWarner Losh 653eac8e827SWarner Losh static void 654eac8e827SWarner Losh print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 655eac8e827SWarner Losh { 656eac8e827SWarner Losh static struct kv_name kv[] = 657eac8e827SWarner Losh { 658eac8e827SWarner Losh { 0x0000, "Corrected Without Delay" }, 659eac8e827SWarner Losh { 0x0001, "Corrected Maybe Delayed" }, 660eac8e827SWarner Losh { 0x0002, "Re-Erase" }, 661eac8e827SWarner Losh { 0x0003, "Errors Corrected" }, 662eac8e827SWarner Losh { 0x0004, "Correct Algorithm Used" }, 663eac8e827SWarner Losh { 0x0005, "Bytes Processed" }, 664eac8e827SWarner Losh { 0x0006, "Uncorrected Errors" }, 665eac8e827SWarner Losh { 0x8000, "Flash Erase Commands" }, 666eac8e827SWarner Losh { 0x8001, "Mfg Defect Count" }, 667eac8e827SWarner Losh { 0x8002, "Grown Defect Count" }, 668eac8e827SWarner Losh { 0x8003, "Erase Count -- User" }, 669eac8e827SWarner Losh { 0x8004, "Erase Count -- System" }, 670eac8e827SWarner Losh }; 671eac8e827SWarner Losh 672eac8e827SWarner Losh printf("Erase Errors Subpage:\n"); 673eac8e827SWarner Losh print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 674eac8e827SWarner Losh } 675eac8e827SWarner Losh 676eac8e827SWarner Losh static void 677eac8e827SWarner Losh print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 678eac8e827SWarner Losh { 679eac8e827SWarner Losh /* My drive doesn't export this -- so not coding up */ 680eac8e827SWarner Losh printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); 681eac8e827SWarner Losh } 682eac8e827SWarner Losh 683eac8e827SWarner Losh static void 684eac8e827SWarner Losh print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 685eac8e827SWarner Losh { 686eac8e827SWarner Losh uint8_t *walker = buf; 687eac8e827SWarner Losh uint32_t min; 688eac8e827SWarner Losh 689eac8e827SWarner Losh printf("Temperature History:\n"); 690eac8e827SWarner Losh printf(" %-30s: %d C\n", "Current Temperature", *walker++); 691eac8e827SWarner Losh printf(" %-30s: %d C\n", "Reference Temperature", *walker++); 692eac8e827SWarner Losh printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); 693eac8e827SWarner Losh printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); 694eac8e827SWarner Losh min = le32dec(walker); 695eac8e827SWarner Losh walker += 4; 696eac8e827SWarner Losh printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); 697eac8e827SWarner Losh min = le32dec(walker); 698eac8e827SWarner Losh walker += 4; 699eac8e827SWarner Losh printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60); 700eac8e827SWarner Losh min = le32dec(walker); 701eac8e827SWarner Losh walker += 4; 702eac8e827SWarner Losh printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); 703eac8e827SWarner Losh } 704eac8e827SWarner Losh 705eac8e827SWarner Losh static void 706eac8e827SWarner Losh print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) 707eac8e827SWarner Losh { 708eac8e827SWarner Losh uint8_t *walker = buf; 709eac8e827SWarner Losh uint64_t val; 710eac8e827SWarner Losh 711eac8e827SWarner Losh printf("SSD Performance Subpage Type %d:\n", res); 712eac8e827SWarner Losh val = le64dec(walker); 713eac8e827SWarner Losh walker += 8; 714eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Read Commands", val); 715eac8e827SWarner Losh val = le64dec(walker); 716eac8e827SWarner Losh walker += 8; 717eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Read Blocks", val); 718eac8e827SWarner Losh val = le64dec(walker); 719eac8e827SWarner Losh walker += 8; 720eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); 721eac8e827SWarner Losh val = le64dec(walker); 722eac8e827SWarner Losh walker += 8; 723eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); 724eac8e827SWarner Losh val = le64dec(walker); 725eac8e827SWarner Losh walker += 8; 726eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); 727eac8e827SWarner Losh val = le64dec(walker); 728eac8e827SWarner Losh walker += 8; 729eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Write Commands", val); 730eac8e827SWarner Losh val = le64dec(walker); 731eac8e827SWarner Losh walker += 8; 732eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Write Blocks", val); 733eac8e827SWarner Losh val = le64dec(walker); 734eac8e827SWarner Losh walker += 8; 735eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); 736eac8e827SWarner Losh val = le64dec(walker); 737eac8e827SWarner Losh walker += 8; 738eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); 739eac8e827SWarner Losh val = le64dec(walker); 740eac8e827SWarner Losh walker += 8; 741eac8e827SWarner Losh printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); 742eac8e827SWarner Losh val = le64dec(walker); 743eac8e827SWarner Losh walker += 8; 744eac8e827SWarner Losh printf(" %-30s: %ju\n", "NAND Read Commands", val); 745eac8e827SWarner Losh val = le64dec(walker); 746eac8e827SWarner Losh walker += 8; 747eac8e827SWarner Losh printf(" %-30s: %ju\n", "NAND Read Blocks", val); 748eac8e827SWarner Losh val = le64dec(walker); 749eac8e827SWarner Losh walker += 8; 750eac8e827SWarner Losh printf(" %-30s: %ju\n", "NAND Write Commands", val); 751eac8e827SWarner Losh val = le64dec(walker); 752eac8e827SWarner Losh walker += 8; 753eac8e827SWarner Losh printf(" %-30s: %ju\n", "NAND Write Blocks", val); 754eac8e827SWarner Losh val = le64dec(walker); 755eac8e827SWarner Losh walker += 8; 756eac8e827SWarner Losh printf(" %-30s: %ju\n", "NAND Read Before Writes", val); 757eac8e827SWarner Losh } 758eac8e827SWarner Losh 759eac8e827SWarner Losh static void 760eac8e827SWarner Losh print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 761eac8e827SWarner Losh { 762eac8e827SWarner Losh uint8_t *walker = buf; 763eac8e827SWarner Losh 764eac8e827SWarner Losh printf("Firmware Load Subpage:\n"); 765eac8e827SWarner Losh printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); 766eac8e827SWarner Losh } 767eac8e827SWarner Losh 768eac8e827SWarner Losh static void 769eac8e827SWarner Losh kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) 770eac8e827SWarner Losh { 771eac8e827SWarner Losh size_t i; 772eac8e827SWarner Losh 773eac8e827SWarner Losh for (i = 0; i < nsp; i++, sp++) { 774eac8e827SWarner Losh if (sp->key == subtype) { 775eac8e827SWarner Losh sp->fn(buf, subtype, res, size); 776eac8e827SWarner Losh return; 777eac8e827SWarner Losh } 778eac8e827SWarner Losh } 779eac8e827SWarner Losh printf("No handler for page type %x\n", subtype); 780eac8e827SWarner Losh } 781eac8e827SWarner Losh 782eac8e827SWarner Losh static void 783eac8e827SWarner Losh print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 784eac8e827SWarner Losh { 785eac8e827SWarner Losh uint8_t *walker, *end, *subpage; 786eac8e827SWarner Losh uint16_t len; 787eac8e827SWarner Losh uint8_t subtype, res; 788eac8e827SWarner Losh 789eac8e827SWarner Losh printf("HGST Extra Info Log\n"); 790eac8e827SWarner Losh printf("===================\n"); 791eac8e827SWarner Losh 792eac8e827SWarner Losh walker = buf; 793d81082a7SJohn Baldwin walker += 2; /* Page count */ 794eac8e827SWarner Losh len = le16dec(walker); 795eac8e827SWarner Losh walker += 2; 796eac8e827SWarner Losh end = walker + len; /* Length is exclusive of this header */ 797eac8e827SWarner Losh 798eac8e827SWarner Losh while (walker < end) { 799eac8e827SWarner Losh subpage = walker + 4; 800eac8e827SWarner Losh subtype = *walker++ & 0x3f; /* subtype */ 801eac8e827SWarner Losh res = *walker++; /* Reserved */ 802eac8e827SWarner Losh len = le16dec(walker); 803eac8e827SWarner Losh walker += len + 2; /* Length, not incl header */ 804eac8e827SWarner Losh if (walker > end) { 805eac8e827SWarner Losh printf("Ooops! Off the end of the list\n"); 806eac8e827SWarner Losh break; 807eac8e827SWarner Losh } 808eac8e827SWarner Losh kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); 809eac8e827SWarner Losh } 810eac8e827SWarner Losh } 811eac8e827SWarner Losh 812eac8e827SWarner Losh NVME_LOGPAGE(hgst_info, 813eac8e827SWarner Losh HGST_INFO_LOG, "hgst", "Detailed Health/SMART", 814eac8e827SWarner Losh print_hgst_info_log, DEFAULT_SIZE); 815eac8e827SWarner Losh NVME_LOGPAGE(wdc_info, 816eac8e827SWarner Losh HGST_INFO_LOG, "wdc", "Detailed Health/SMART", 817eac8e827SWarner Losh print_hgst_info_log, DEFAULT_SIZE); 818