Index: head/sbin/nvmecontrol/Makefile =================================================================== --- head/sbin/nvmecontrol/Makefile +++ head/sbin/nvmecontrol/Makefile @@ -4,10 +4,11 @@ PROG= nvmecontrol SRCS= nvmecontrol.c devlist.c firmware.c format.c identify.c identify_ext.c logpage.c \ perftest.c reset.c ns.c nvme_util.c power.c nc_util.c -SRCS+= wdc.c intel.c MAN= nvmecontrol.8 LDFLAGS+= -rdynamic +SUBDIR= modules .PATH: ${SRCTOP}/sys/dev/nvme .include +.include Index: head/sbin/nvmecontrol/intel.c =================================================================== --- head/sbin/nvmecontrol/intel.c +++ head/sbin/nvmecontrol/intel.c @@ -1,198 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2013 EMC Corp. - * All rights reserved. - * - * Copyright (C) 2012-2013 Intel Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nvmecontrol.h" - -/* - * Intel specific log pages from - * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf - * - * Though the version as of this date has a typo for the size of log page 0xca, - * offset 147: it is only 1 byte, not 6. - */ -static void -print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) -{ - struct intel_log_temp_stats *temp = buf; - - printf("Intel Temperature Log\n"); - printf("=====================\n"); - - printf("Current: "); - print_temp(temp->current); - printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); - printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); - printf("Max Temperature "); - print_temp(temp->max_temp); - printf("Min Temperature "); - print_temp(temp->min_temp); - printf("Max Operating Temperature "); - print_temp(temp->max_oper_temp); - printf("Min Operating Temperature "); - print_temp(temp->min_oper_temp); - printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); -} - -/* - * Format from Table 22, section 5.7 IO Command Latency Statistics. - * Read and write stats pages have identical encoding. - */ -static void -print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) -{ - const char *walker = buf; - int i; - - printf("Major: %d\n", le16dec(walker + 0)); - printf("Minor: %d\n", le16dec(walker + 2)); - for (i = 0; i < 32; i++) - printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4)); - for (i = 1; i < 32; i++) - printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4)); - for (i = 1; i < 32; i++) - printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4)); -} - -static void -print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) -{ - - printf("Intel Read Latency Log\n"); - printf("======================\n"); - print_intel_read_write_lat_log(cdata, buf, size); -} - -static void -print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) -{ - - printf("Intel Write Latency Log\n"); - printf("=======================\n"); - print_intel_read_write_lat_log(cdata, buf, size); -} - -/* - * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented. - */ -static void -print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) -{ - uint8_t *walker = buf; - uint8_t *end = walker + 150; - const char *name; - uint64_t raw; - uint8_t normalized; - - static struct kv_name kv[] = - { - { 0xab, "Program Fail Count" }, - { 0xac, "Erase Fail Count" }, - { 0xad, "Wear Leveling Count" }, - { 0xb8, "End to End Error Count" }, - { 0xc7, "CRC Error Count" }, - { 0xe2, "Timed: Media Wear" }, - { 0xe3, "Timed: Host Read %" }, - { 0xe4, "Timed: Elapsed Time" }, - { 0xea, "Thermal Throttle Status" }, - { 0xf0, "Retry Buffer Overflows" }, - { 0xf3, "PLL Lock Loss Count" }, - { 0xf4, "NAND Bytes Written" }, - { 0xf5, "Host Bytes Written" }, - }; - - printf("Additional SMART Data Log\n"); - printf("=========================\n"); - /* - * walker[0] = Key - * walker[1,2] = reserved - * walker[3] = Normalized Value - * walker[4] = reserved - * walker[5..10] = Little Endian Raw value - * (or other represenations) - * walker[11] = reserved - */ - while (walker < end) { - name = kv_lookup(kv, nitems(kv), *walker); - normalized = walker[3]; - raw = le48dec(walker + 5); - switch (*walker){ - case 0: - break; - case 0xad: - printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized, - le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); - break; - case 0xe2: - printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); - break; - case 0xea: - printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); - break; - default: - printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); - break; - } - walker += 12; - } -} - -NVME_LOGPAGE(intel_temp, - INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats", - print_intel_temp_stats, sizeof(struct intel_log_temp_stats)); -NVME_LOGPAGE(intel_rlat, - INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies", - print_intel_read_lat_log, DEFAULT_SIZE); -NVME_LOGPAGE(intel_wlat, - INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies", - print_intel_write_lat_log, DEFAULT_SIZE); -NVME_LOGPAGE(intel_smart, - INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data", - print_intel_add_smart, DEFAULT_SIZE); -NVME_LOGPAGE(samsung_smart, /* NOTE, this will be deleted before 13.0 */ - INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data", - print_intel_add_smart, DEFAULT_SIZE); Index: head/sbin/nvmecontrol/modules/Makefile =================================================================== --- head/sbin/nvmecontrol/modules/Makefile +++ head/sbin/nvmecontrol/modules/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= intel wdc + +.include Index: head/sbin/nvmecontrol/modules/Makefile.inc =================================================================== --- head/sbin/nvmecontrol/modules/Makefile.inc +++ head/sbin/nvmecontrol/modules/Makefile.inc @@ -0,0 +1,12 @@ +# $FreeBSD$ + +PACKAGE=runtime +NVMECONTROLDIR= ${SRCTOP}/sbin/nvmecontrol + +MK_INSTALLLIB= no +MK_PROFILE= no + +CFLAGS+= -I${NVMECONTROLDIR} + +SHLIB_NAME?= ${LIB}.so +LIBDIR= /lib/nvmecontrol Index: head/sbin/nvmecontrol/modules/intel/Makefile =================================================================== --- head/sbin/nvmecontrol/modules/intel/Makefile +++ head/sbin/nvmecontrol/modules/intel/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +LIB= intel +SRCS= intel.c + +.include Index: head/sbin/nvmecontrol/modules/intel/intel.c =================================================================== --- head/sbin/nvmecontrol/modules/intel/intel.c +++ head/sbin/nvmecontrol/modules/intel/intel.c @@ -0,0 +1,198 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2013 EMC Corp. + * All rights reserved. + * + * Copyright (C) 2012-2013 Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +/* + * Intel specific log pages from + * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf + * + * Though the version as of this date has a typo for the size of log page 0xca, + * offset 147: it is only 1 byte, not 6. + */ +static void +print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) +{ + struct intel_log_temp_stats *temp = buf; + + printf("Intel Temperature Log\n"); + printf("=====================\n"); + + printf("Current: "); + print_temp(temp->current); + printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); + printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); + printf("Max Temperature "); + print_temp(temp->max_temp); + printf("Min Temperature "); + print_temp(temp->min_temp); + printf("Max Operating Temperature "); + print_temp(temp->max_oper_temp); + printf("Min Operating Temperature "); + print_temp(temp->min_oper_temp); + printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); +} + +/* + * Format from Table 22, section 5.7 IO Command Latency Statistics. + * Read and write stats pages have identical encoding. + */ +static void +print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) +{ + const char *walker = buf; + int i; + + printf("Major: %d\n", le16dec(walker + 0)); + printf("Minor: %d\n", le16dec(walker + 2)); + for (i = 0; i < 32; i++) + printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4)); + for (i = 1; i < 32; i++) + printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4)); + for (i = 1; i < 32; i++) + printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4)); +} + +static void +print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) +{ + + printf("Intel Read Latency Log\n"); + printf("======================\n"); + print_intel_read_write_lat_log(cdata, buf, size); +} + +static void +print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) +{ + + printf("Intel Write Latency Log\n"); + printf("=======================\n"); + print_intel_read_write_lat_log(cdata, buf, size); +} + +/* + * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented. + */ +static void +print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) +{ + uint8_t *walker = buf; + uint8_t *end = walker + 150; + const char *name; + uint64_t raw; + uint8_t normalized; + + static struct kv_name kv[] = + { + { 0xab, "Program Fail Count" }, + { 0xac, "Erase Fail Count" }, + { 0xad, "Wear Leveling Count" }, + { 0xb8, "End to End Error Count" }, + { 0xc7, "CRC Error Count" }, + { 0xe2, "Timed: Media Wear" }, + { 0xe3, "Timed: Host Read %" }, + { 0xe4, "Timed: Elapsed Time" }, + { 0xea, "Thermal Throttle Status" }, + { 0xf0, "Retry Buffer Overflows" }, + { 0xf3, "PLL Lock Loss Count" }, + { 0xf4, "NAND Bytes Written" }, + { 0xf5, "Host Bytes Written" }, + }; + + printf("Additional SMART Data Log\n"); + printf("=========================\n"); + /* + * walker[0] = Key + * walker[1,2] = reserved + * walker[3] = Normalized Value + * walker[4] = reserved + * walker[5..10] = Little Endian Raw value + * (or other represenations) + * walker[11] = reserved + */ + while (walker < end) { + name = kv_lookup(kv, nitems(kv), *walker); + normalized = walker[3]; + raw = le48dec(walker + 5); + switch (*walker){ + case 0: + break; + case 0xad: + printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized, + le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); + break; + case 0xe2: + printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); + break; + case 0xea: + printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); + break; + default: + printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); + break; + } + walker += 12; + } +} + +NVME_LOGPAGE(intel_temp, + INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats", + print_intel_temp_stats, sizeof(struct intel_log_temp_stats)); +NVME_LOGPAGE(intel_rlat, + INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies", + print_intel_read_lat_log, DEFAULT_SIZE); +NVME_LOGPAGE(intel_wlat, + INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies", + print_intel_write_lat_log, DEFAULT_SIZE); +NVME_LOGPAGE(intel_smart, + INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data", + print_intel_add_smart, DEFAULT_SIZE); +NVME_LOGPAGE(samsung_smart, /* NOTE, this will be deleted before 13.0 */ + INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data", + print_intel_add_smart, DEFAULT_SIZE); Index: head/sbin/nvmecontrol/modules/wdc/Makefile =================================================================== --- head/sbin/nvmecontrol/modules/wdc/Makefile +++ head/sbin/nvmecontrol/modules/wdc/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +LIB= wdc +SRCS= wdc.c + +.include Index: head/sbin/nvmecontrol/modules/wdc/wdc.c =================================================================== --- head/sbin/nvmecontrol/modules/wdc/wdc.c +++ head/sbin/nvmecontrol/modules/wdc/wdc.c @@ -0,0 +1,597 @@ +/*- + * Copyright (c) 2017 Netflix, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +#define WDC_USAGE \ + "wdc (cap-diag)\n" + +NVME_CMD_DECLARE(wdc, struct nvme_function); + +#define WDC_NVME_TOC_SIZE 8 + +#define WDC_NVME_CAP_DIAG_OPCODE 0xe6 +#define WDC_NVME_CAP_DIAG_CMD 0x0000 + +static void wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]); + +#define WDC_CAP_DIAG_USAGE "wdc cap-diag [-o path-template]\n" + +NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE); + +static void +wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix) +{ + struct nvme_controller_data cdata; + char sn[NVME_SERIAL_NUMBER_LENGTH + 1]; + char *walker; + + len -= strlen(buf); + buf += strlen(buf); + read_controller_data(fd, &cdata); + memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH); + walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1; + while (walker > sn && *walker == ' ') + walker--; + *++walker = '\0'; + snprintf(buf, len, "%s%s.bin", sn, suffix); +} + +static void +wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd, + uint8_t *buffer, size_t buflen) +{ + struct nvme_pt_command pt; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = opcode; + pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); /* - 1 like all the others ??? */ + pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); + pt.cmd.cdw12 = htole32(cmd); + pt.buf = buffer; + pt.len = buflen; + pt.is_read = 1; +// printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n", +// (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen); + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(1, "wdc_get_data request failed"); + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "wdc_get_data request returned error"); +} + +static void +wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode, + uint32_t cmd, int len_off) +{ + int first; + int fd2; + uint8_t *buf; + uint32_t len, offset; + size_t resid; + + wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix); + + /* XXX overwrite protection? */ + fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd2 < 0) + err(1, "open %s", tmpl); + buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE); + if (buf == NULL) + errx(1, "Can't get buffer to read dump"); + offset = 0; + len = NVME_MAX_XFER_SIZE; + first = 1; + + do { + resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len; + wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid); + + if (first) { + len = be32dec(buf + len_off); + if (len == 0) + errx(1, "No data for %s", suffix); + if (memcmp("E6LG", buf, 4) != 0) + printf("Expected header of E6LG, found '%4.4s' instead\n", + buf); + printf("Dumping %d bytes of version %d.%d log to %s\n", len, + buf[8], buf[9], tmpl); + /* + * Adjust amount to dump if total dump < 1MB, + * though it likely doesn't matter to the WDC + * analysis tools. + */ + if (resid > len) + resid = len; + first = 0; + } + if (write(fd2, buf, resid) != (ssize_t)resid) + err(1, "write"); + offset += resid; + len -= resid; + } while (len > 0); + free(buf); + close(fd2); +} + +static void +wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]) +{ + char path_tmpl[MAXPATHLEN]; + int ch, fd; + + path_tmpl[0] = '\0'; + while ((ch = getopt(argc, argv, "o:")) != -1) { + switch ((char)ch) { + case 'o': + strlcpy(path_tmpl, optarg, MAXPATHLEN); + break; + default: + usage(nf); + } + } + /* Check that a controller was specified. */ + if (optind >= argc) + usage(nf); + open_dev(argv[optind], &fd, 1, 1); + + wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE, + WDC_NVME_CAP_DIAG_CMD, 4); + + close(fd); + + exit(1); +} + +static void +wdc(const struct nvme_function *nf __unused, int argc, char *argv[]) +{ + + DISPATCH(argc, argv, wdc); +} + +/* + * HGST's 0xc1 page. This is a grab bag of additional data. Please see + * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf + * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf + * Appendix A for details + */ + +typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); + +struct subpage_print +{ + uint16_t key; + subprint_fn_t fn; +}; + +static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); + +static struct subpage_print hgst_subpage[] = { + { 0x02, print_hgst_info_write_errors }, + { 0x03, print_hgst_info_read_errors }, + { 0x05, print_hgst_info_verify_errors }, + { 0x10, print_hgst_info_self_test }, + { 0x15, print_hgst_info_background_scan }, + { 0x30, print_hgst_info_erase_errors }, + { 0x31, print_hgst_info_erase_counts }, + { 0x32, print_hgst_info_temp_history }, + { 0x37, print_hgst_info_ssd_perf }, + { 0x38, print_hgst_info_firmware_load }, +}; + +/* Print a subpage that is basically just key value pairs */ +static void +print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, + const struct kv_name *kv, size_t kv_count) +{ + uint8_t *wsp, *esp; + uint16_t ptype; + uint8_t plen; + uint64_t param; + int i; + + wsp = buf; + esp = wsp + size; + while (wsp < esp) { + ptype = le16dec(wsp); + wsp += 2; + wsp++; /* Flags, just ignore */ + plen = *wsp++; + param = 0; + for (i = 0; i < plen; i++) + param |= (uint64_t)*wsp++ << (i * 8); + printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); + } +} + +static void +print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Writes" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Flash Write Commands" }, + { 0x8001, "HGST Special" }, + }; + + printf("Write Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Reads" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Flash Read Commands" }, + { 0x8001, "XOR Recovered" }, + { 0x8002, "Total Corrected Bits" }, + }; + + printf("Read Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Reads" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Commands Processed" }, + }; + + printf("Verify Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) +{ + size_t i; + uint8_t *walker = buf; + uint16_t code, hrs; + uint32_t lba; + + printf("Self Test Subpage:\n"); + for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ + code = le16dec(walker); + walker += 2; + walker++; /* Ignore fixed flags */ + if (*walker == 0) /* Last entry is zero length */ + break; + if (*walker++ != 0x10) { + printf("Bad length for self test report\n"); + return; + } + printf(" %-30s: %d\n", "Recent Test", code); + printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); + printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); + walker++; + printf(" %-28s: %#x\n", "Self-Test Number", *walker++); + hrs = le16dec(walker); + walker += 2; + lba = le32dec(walker); + walker += 4; + printf(" %-28s: %u\n", "Total Power On Hrs", hrs); + printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); + printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); + printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); + printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); + printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); + } +} + +static void +print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) +{ + uint8_t *walker = buf; + uint8_t status; + uint16_t code, nscan, progress; + uint32_t pom, nand; + + printf("Background Media Scan Subpage:\n"); + /* Decode the header */ + code = le16dec(walker); + walker += 2; + walker++; /* Ignore fixed flags */ + if (*walker++ != 0x10) { + printf("Bad length for background scan header\n"); + return; + } + if (code != 0) { + printf("Expceted code 0, found code %#x\n", code); + return; + } + pom = le32dec(walker); + walker += 4; + walker++; /* Reserved */ + status = *walker++; + nscan = le16dec(walker); + walker += 2; + progress = le16dec(walker); + walker += 2; + walker += 6; /* Reserved */ + printf(" %-30s: %d\n", "Power On Minutes", pom); + printf(" %-30s: %x (%s)\n", "BMS Status", status, + status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); + printf(" %-30s: %d\n", "Number of BMS", nscan); + printf(" %-30s: %d\n", "Progress Current BMS", progress); + /* Report retirements */ + if (walker - (uint8_t *)buf != 20) { + printf("Coding error, offset not 20\n"); + return; + } + size -= 20; + printf(" %-30s: %d\n", "BMS retirements", size / 0x18); + while (size > 0) { + code = le16dec(walker); + walker += 2; + walker++; + if (*walker++ != 0x14) { + printf("Bad length parameter\n"); + return; + } + pom = le32dec(walker); + walker += 4; + /* + * Spec sheet says the following are hard coded, if true, just + * print the NAND retirement. + */ + if (walker[0] == 0x41 && + walker[1] == 0x0b && + walker[2] == 0x01 && + walker[3] == 0x00 && + walker[4] == 0x00 && + walker[5] == 0x00 && + walker[6] == 0x00 && + walker[7] == 0x00) { + walker += 8; + walker += 4; /* Skip reserved */ + nand = le32dec(walker); + walker += 4; + printf(" %-30s: %d\n", "Retirement number", code); + printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); + } else { + printf("Parameter %#x entry corrupt\n", code); + walker += 16; + } + } +} + +static void +print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Erase" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Flash Erase Commands" }, + { 0x8001, "Mfg Defect Count" }, + { 0x8002, "Grown Defect Count" }, + { 0x8003, "Erase Count -- User" }, + { 0x8004, "Erase Count -- System" }, + }; + + printf("Erase Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + /* My drive doesn't export this -- so not coding up */ + printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); +} + +static void +print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) +{ + uint8_t *walker = buf; + uint32_t min; + + printf("Temperature History:\n"); + printf(" %-30s: %d C\n", "Current Temperature", *walker++); + printf(" %-30s: %d C\n", "Reference Temperature", *walker++); + printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); + printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); + min = le32dec(walker); + walker += 4; + printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); + min = le32dec(walker); + walker += 4; + printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60); + min = le32dec(walker); + walker += 4; + printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); +} + +static void +print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) +{ + uint8_t *walker = buf; + uint64_t val; + + printf("SSD Performance Subpage Type %d:\n", res); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Read Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Read Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Read Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Read Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Write Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Write Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Read Before Writes", val); +} + +static void +print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) +{ + uint8_t *walker = buf; + + printf("Firmware Load Subpage:\n"); + printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); +} + +static void +kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) +{ + size_t i; + + for (i = 0; i < nsp; i++, sp++) { + if (sp->key == subtype) { + sp->fn(buf, subtype, res, size); + return; + } + } + printf("No handler for page type %x\n", subtype); +} + +static void +print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) +{ + uint8_t *walker, *end, *subpage; + int pages; + uint16_t len; + uint8_t subtype, res; + + printf("HGST Extra Info Log\n"); + printf("===================\n"); + + walker = buf; + pages = *walker++; + walker++; + len = le16dec(walker); + walker += 2; + end = walker + len; /* Length is exclusive of this header */ + + while (walker < end) { + subpage = walker + 4; + subtype = *walker++ & 0x3f; /* subtype */ + res = *walker++; /* Reserved */ + len = le16dec(walker); + walker += len + 2; /* Length, not incl header */ + if (walker > end) { + printf("Ooops! Off the end of the list\n"); + break; + } + kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); + } +} + +NVME_LOGPAGE(hgst_info, + HGST_INFO_LOG, "hgst", "Detailed Health/SMART", + print_hgst_info_log, DEFAULT_SIZE); +NVME_LOGPAGE(wdc_info, + HGST_INFO_LOG, "wdc", "Detailed Health/SMART", + print_hgst_info_log, DEFAULT_SIZE); +NVME_COMMAND(top, wdc, wdc, WDC_USAGE); Index: head/sbin/nvmecontrol/wdc.c =================================================================== --- head/sbin/nvmecontrol/wdc.c +++ head/sbin/nvmecontrol/wdc.c @@ -1,597 +0,0 @@ -/*- - * Copyright (c) 2017 Netflix, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nvmecontrol.h" - -#define WDC_USAGE \ - "wdc (cap-diag)\n" - -NVME_CMD_DECLARE(wdc, struct nvme_function); - -#define WDC_NVME_TOC_SIZE 8 - -#define WDC_NVME_CAP_DIAG_OPCODE 0xe6 -#define WDC_NVME_CAP_DIAG_CMD 0x0000 - -static void wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]); - -#define WDC_CAP_DIAG_USAGE "wdc cap-diag [-o path-template]\n" - -NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE); - -static void -wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix) -{ - struct nvme_controller_data cdata; - char sn[NVME_SERIAL_NUMBER_LENGTH + 1]; - char *walker; - - len -= strlen(buf); - buf += strlen(buf); - read_controller_data(fd, &cdata); - memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH); - walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1; - while (walker > sn && *walker == ' ') - walker--; - *++walker = '\0'; - snprintf(buf, len, "%s%s.bin", sn, suffix); -} - -static void -wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd, - uint8_t *buffer, size_t buflen) -{ - struct nvme_pt_command pt; - - memset(&pt, 0, sizeof(pt)); - pt.cmd.opc = opcode; - pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); /* - 1 like all the others ??? */ - pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); - pt.cmd.cdw12 = htole32(cmd); - pt.buf = buffer; - pt.len = buflen; - pt.is_read = 1; -// printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n", -// (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen); - - if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) - err(1, "wdc_get_data request failed"); - if (nvme_completion_is_error(&pt.cpl)) - errx(1, "wdc_get_data request returned error"); -} - -static void -wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode, - uint32_t cmd, int len_off) -{ - int first; - int fd2; - uint8_t *buf; - uint32_t len, offset; - size_t resid; - - wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix); - - /* XXX overwrite protection? */ - fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd2 < 0) - err(1, "open %s", tmpl); - buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE); - if (buf == NULL) - errx(1, "Can't get buffer to read dump"); - offset = 0; - len = NVME_MAX_XFER_SIZE; - first = 1; - - do { - resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len; - wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid); - - if (first) { - len = be32dec(buf + len_off); - if (len == 0) - errx(1, "No data for %s", suffix); - if (memcmp("E6LG", buf, 4) != 0) - printf("Expected header of E6LG, found '%4.4s' instead\n", - buf); - printf("Dumping %d bytes of version %d.%d log to %s\n", len, - buf[8], buf[9], tmpl); - /* - * Adjust amount to dump if total dump < 1MB, - * though it likely doesn't matter to the WDC - * analysis tools. - */ - if (resid > len) - resid = len; - first = 0; - } - if (write(fd2, buf, resid) != (ssize_t)resid) - err(1, "write"); - offset += resid; - len -= resid; - } while (len > 0); - free(buf); - close(fd2); -} - -static void -wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]) -{ - char path_tmpl[MAXPATHLEN]; - int ch, fd; - - path_tmpl[0] = '\0'; - while ((ch = getopt(argc, argv, "o:")) != -1) { - switch ((char)ch) { - case 'o': - strlcpy(path_tmpl, optarg, MAXPATHLEN); - break; - default: - usage(nf); - } - } - /* Check that a controller was specified. */ - if (optind >= argc) - usage(nf); - open_dev(argv[optind], &fd, 1, 1); - - wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE, - WDC_NVME_CAP_DIAG_CMD, 4); - - close(fd); - - exit(1); -} - -static void -wdc(const struct nvme_function *nf __unused, int argc, char *argv[]) -{ - - DISPATCH(argc, argv, wdc); -} - -/* - * HGST's 0xc1 page. This is a grab bag of additional data. Please see - * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf - * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf - * Appendix A for details - */ - -typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); - -struct subpage_print -{ - uint16_t key; - subprint_fn_t fn; -}; - -static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); -static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); - -static struct subpage_print hgst_subpage[] = { - { 0x02, print_hgst_info_write_errors }, - { 0x03, print_hgst_info_read_errors }, - { 0x05, print_hgst_info_verify_errors }, - { 0x10, print_hgst_info_self_test }, - { 0x15, print_hgst_info_background_scan }, - { 0x30, print_hgst_info_erase_errors }, - { 0x31, print_hgst_info_erase_counts }, - { 0x32, print_hgst_info_temp_history }, - { 0x37, print_hgst_info_ssd_perf }, - { 0x38, print_hgst_info_firmware_load }, -}; - -/* Print a subpage that is basically just key value pairs */ -static void -print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, - const struct kv_name *kv, size_t kv_count) -{ - uint8_t *wsp, *esp; - uint16_t ptype; - uint8_t plen; - uint64_t param; - int i; - - wsp = buf; - esp = wsp + size; - while (wsp < esp) { - ptype = le16dec(wsp); - wsp += 2; - wsp++; /* Flags, just ignore */ - plen = *wsp++; - param = 0; - for (i = 0; i < plen; i++) - param |= (uint64_t)*wsp++ << (i * 8); - printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); - } -} - -static void -print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) -{ - static struct kv_name kv[] = - { - { 0x0000, "Corrected Without Delay" }, - { 0x0001, "Corrected Maybe Delayed" }, - { 0x0002, "Re-Writes" }, - { 0x0003, "Errors Corrected" }, - { 0x0004, "Correct Algorithm Used" }, - { 0x0005, "Bytes Processed" }, - { 0x0006, "Uncorrected Errors" }, - { 0x8000, "Flash Write Commands" }, - { 0x8001, "HGST Special" }, - }; - - printf("Write Errors Subpage:\n"); - print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); -} - -static void -print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) -{ - static struct kv_name kv[] = - { - { 0x0000, "Corrected Without Delay" }, - { 0x0001, "Corrected Maybe Delayed" }, - { 0x0002, "Re-Reads" }, - { 0x0003, "Errors Corrected" }, - { 0x0004, "Correct Algorithm Used" }, - { 0x0005, "Bytes Processed" }, - { 0x0006, "Uncorrected Errors" }, - { 0x8000, "Flash Read Commands" }, - { 0x8001, "XOR Recovered" }, - { 0x8002, "Total Corrected Bits" }, - }; - - printf("Read Errors Subpage:\n"); - print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); -} - -static void -print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) -{ - static struct kv_name kv[] = - { - { 0x0000, "Corrected Without Delay" }, - { 0x0001, "Corrected Maybe Delayed" }, - { 0x0002, "Re-Reads" }, - { 0x0003, "Errors Corrected" }, - { 0x0004, "Correct Algorithm Used" }, - { 0x0005, "Bytes Processed" }, - { 0x0006, "Uncorrected Errors" }, - { 0x8000, "Commands Processed" }, - }; - - printf("Verify Errors Subpage:\n"); - print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); -} - -static void -print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) -{ - size_t i; - uint8_t *walker = buf; - uint16_t code, hrs; - uint32_t lba; - - printf("Self Test Subpage:\n"); - for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ - code = le16dec(walker); - walker += 2; - walker++; /* Ignore fixed flags */ - if (*walker == 0) /* Last entry is zero length */ - break; - if (*walker++ != 0x10) { - printf("Bad length for self test report\n"); - return; - } - printf(" %-30s: %d\n", "Recent Test", code); - printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); - printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); - walker++; - printf(" %-28s: %#x\n", "Self-Test Number", *walker++); - hrs = le16dec(walker); - walker += 2; - lba = le32dec(walker); - walker += 4; - printf(" %-28s: %u\n", "Total Power On Hrs", hrs); - printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); - printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); - printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); - printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); - printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); - } -} - -static void -print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) -{ - uint8_t *walker = buf; - uint8_t status; - uint16_t code, nscan, progress; - uint32_t pom, nand; - - printf("Background Media Scan Subpage:\n"); - /* Decode the header */ - code = le16dec(walker); - walker += 2; - walker++; /* Ignore fixed flags */ - if (*walker++ != 0x10) { - printf("Bad length for background scan header\n"); - return; - } - if (code != 0) { - printf("Expceted code 0, found code %#x\n", code); - return; - } - pom = le32dec(walker); - walker += 4; - walker++; /* Reserved */ - status = *walker++; - nscan = le16dec(walker); - walker += 2; - progress = le16dec(walker); - walker += 2; - walker += 6; /* Reserved */ - printf(" %-30s: %d\n", "Power On Minutes", pom); - printf(" %-30s: %x (%s)\n", "BMS Status", status, - status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); - printf(" %-30s: %d\n", "Number of BMS", nscan); - printf(" %-30s: %d\n", "Progress Current BMS", progress); - /* Report retirements */ - if (walker - (uint8_t *)buf != 20) { - printf("Coding error, offset not 20\n"); - return; - } - size -= 20; - printf(" %-30s: %d\n", "BMS retirements", size / 0x18); - while (size > 0) { - code = le16dec(walker); - walker += 2; - walker++; - if (*walker++ != 0x14) { - printf("Bad length parameter\n"); - return; - } - pom = le32dec(walker); - walker += 4; - /* - * Spec sheet says the following are hard coded, if true, just - * print the NAND retirement. - */ - if (walker[0] == 0x41 && - walker[1] == 0x0b && - walker[2] == 0x01 && - walker[3] == 0x00 && - walker[4] == 0x00 && - walker[5] == 0x00 && - walker[6] == 0x00 && - walker[7] == 0x00) { - walker += 8; - walker += 4; /* Skip reserved */ - nand = le32dec(walker); - walker += 4; - printf(" %-30s: %d\n", "Retirement number", code); - printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); - } else { - printf("Parameter %#x entry corrupt\n", code); - walker += 16; - } - } -} - -static void -print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) -{ - static struct kv_name kv[] = - { - { 0x0000, "Corrected Without Delay" }, - { 0x0001, "Corrected Maybe Delayed" }, - { 0x0002, "Re-Erase" }, - { 0x0003, "Errors Corrected" }, - { 0x0004, "Correct Algorithm Used" }, - { 0x0005, "Bytes Processed" }, - { 0x0006, "Uncorrected Errors" }, - { 0x8000, "Flash Erase Commands" }, - { 0x8001, "Mfg Defect Count" }, - { 0x8002, "Grown Defect Count" }, - { 0x8003, "Erase Count -- User" }, - { 0x8004, "Erase Count -- System" }, - }; - - printf("Erase Errors Subpage:\n"); - print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); -} - -static void -print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) -{ - /* My drive doesn't export this -- so not coding up */ - printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); -} - -static void -print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) -{ - uint8_t *walker = buf; - uint32_t min; - - printf("Temperature History:\n"); - printf(" %-30s: %d C\n", "Current Temperature", *walker++); - printf(" %-30s: %d C\n", "Reference Temperature", *walker++); - printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); - printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); - min = le32dec(walker); - walker += 4; - printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); - min = le32dec(walker); - walker += 4; - printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60); - min = le32dec(walker); - walker += 4; - printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); -} - -static void -print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) -{ - uint8_t *walker = buf; - uint64_t val; - - printf("SSD Performance Subpage Type %d:\n", res); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Read Commands", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Read Blocks", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Write Commands", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Write Blocks", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "NAND Read Commands", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "NAND Read Blocks", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "NAND Write Commands", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "NAND Write Blocks", val); - val = le64dec(walker); - walker += 8; - printf(" %-30s: %ju\n", "NAND Read Before Writes", val); -} - -static void -print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) -{ - uint8_t *walker = buf; - - printf("Firmware Load Subpage:\n"); - printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); -} - -static void -kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) -{ - size_t i; - - for (i = 0; i < nsp; i++, sp++) { - if (sp->key == subtype) { - sp->fn(buf, subtype, res, size); - return; - } - } - printf("No handler for page type %x\n", subtype); -} - -static void -print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) -{ - uint8_t *walker, *end, *subpage; - int pages; - uint16_t len; - uint8_t subtype, res; - - printf("HGST Extra Info Log\n"); - printf("===================\n"); - - walker = buf; - pages = *walker++; - walker++; - len = le16dec(walker); - walker += 2; - end = walker + len; /* Length is exclusive of this header */ - - while (walker < end) { - subpage = walker + 4; - subtype = *walker++ & 0x3f; /* subtype */ - res = *walker++; /* Reserved */ - len = le16dec(walker); - walker += len + 2; /* Length, not incl header */ - if (walker > end) { - printf("Ooops! Off the end of the list\n"); - break; - } - kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); - } -} - -NVME_LOGPAGE(hgst_info, - HGST_INFO_LOG, "hgst", "Detailed Health/SMART", - print_hgst_info_log, DEFAULT_SIZE); -NVME_LOGPAGE(wdc_info, - HGST_INFO_LOG, "wdc", "Detailed Health/SMART", - print_hgst_info_log, DEFAULT_SIZE); -NVME_COMMAND(top, wdc, wdc, WDC_USAGE);