1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2013 EMC Corp.
5 * All rights reserved.
6 *
7 * Copyright (C) 2012-2013 Intel Corporation
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include <sys/param.h>
36#include <sys/ioccom.h>
37
38#include <ctype.h>
39#include <err.h>
40#include <fcntl.h>
41#include <stdbool.h>
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <sys/endian.h>
48
49#include "nvmecontrol.h"
50
51/*
52 * Intel specific log pages from
53 * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
54 *
55 * Though the version as of this date has a typo for the size of log page 0xca,
56 * offset 147: it is only 1 byte, not 6.
57 */
58static void
59print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
60{
61	struct intel_log_temp_stats	*temp = buf;
62
63	printf("Intel Temperature Log\n");
64	printf("=====================\n");
65
66	printf("Current:                        ");
67	print_temp(temp->current);
68	printf("Overtemp Last Flags             %#jx\n", (uintmax_t)temp->overtemp_flag_last);
69	printf("Overtemp Lifetime Flags         %#jx\n", (uintmax_t)temp->overtemp_flag_life);
70	printf("Max Temperature                 ");
71	print_temp(temp->max_temp);
72	printf("Min Temperature                 ");
73	print_temp(temp->min_temp);
74	printf("Max Operating Temperature       ");
75	print_temp(temp->max_oper_temp);
76	printf("Min Operating Temperature       ");
77	print_temp(temp->min_oper_temp);
78	printf("Estimated Temperature Offset:   %ju C/K\n", (uintmax_t)temp->est_offset);
79}
80
81/*
82 * Format from Table 22, section 5.7 IO Command Latency Statistics.
83 * Read and write stats pages have identical encoding.
84 */
85static void
86print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
87{
88	const char *walker = buf;
89	int i;
90
91	printf("Major:                         %d\n", le16dec(walker + 0));
92	printf("Minor:                         %d\n", le16dec(walker + 2));
93	for (i = 0; i < 32; i++)
94		printf("%4dus-%4dus:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4));
95	for (i = 1; i < 32; i++)
96		printf("%4dms-%4dms:                 %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4));
97	for (i = 1; i < 32; i++)
98		printf("%4dms-%4dms:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4));
99}
100
101static void
102print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
103{
104
105	printf("Intel Read Latency Log\n");
106	printf("======================\n");
107	print_intel_read_write_lat_log(cdata, buf, size);
108}
109
110static void
111print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
112{
113
114	printf("Intel Write Latency Log\n");
115	printf("=======================\n");
116	print_intel_read_write_lat_log(cdata, buf, size);
117}
118
119/*
120 * Table 19. 5.4 SMART Attributes. Others also implement this and some extra data not documented.
121 */
122void
123print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
124{
125	uint8_t *walker = buf;
126	uint8_t *end = walker + 150;
127	const char *name;
128	uint64_t raw;
129	uint8_t normalized;
130
131	static struct kv_name kv[] =
132	{
133		{ 0xab, "Program Fail Count" },
134		{ 0xac, "Erase Fail Count" },
135		{ 0xad, "Wear Leveling Count" },
136		{ 0xb8, "End to End Error Count" },
137		{ 0xc7, "CRC Error Count" },
138		{ 0xe2, "Timed: Media Wear" },
139		{ 0xe3, "Timed: Host Read %" },
140		{ 0xe4, "Timed: Elapsed Time" },
141		{ 0xea, "Thermal Throttle Status" },
142		{ 0xf0, "Retry Buffer Overflows" },
143		{ 0xf3, "PLL Lock Loss Count" },
144		{ 0xf4, "NAND Bytes Written" },
145		{ 0xf5, "Host Bytes Written" },
146	};
147
148	printf("Additional SMART Data Log\n");
149	printf("=========================\n");
150	/*
151	 * walker[0] = Key
152	 * walker[1,2] = reserved
153	 * walker[3] = Normalized Value
154	 * walker[4] = reserved
155	 * walker[5..10] = Little Endian Raw value
156	 *	(or other represenations)
157	 * walker[11] = reserved
158	 */
159	while (walker < end) {
160		name = kv_lookup(kv, nitems(kv), *walker);
161		normalized = walker[3];
162		raw = le48dec(walker + 5);
163		switch (*walker){
164		case 0:
165			break;
166		case 0xad:
167			printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized,
168			    le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
169			break;
170		case 0xe2:
171			printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
172			break;
173		case 0xea:
174			printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6));
175			break;
176		default:
177			printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
178			break;
179		}
180		walker += 12;
181	}
182}
183
184NVME_LOGPAGE(intel_temp,
185    INTEL_LOG_TEMP_STATS,		"intel", "Temperature Stats",
186    print_intel_temp_stats,		sizeof(struct intel_log_temp_stats));
187NVME_LOGPAGE(intel_rlat,
188    INTEL_LOG_READ_LAT_LOG,		"intel", "Read Latencies",
189    print_intel_read_lat_log,		DEFAULT_SIZE);
190NVME_LOGPAGE(intel_wlat,
191    INTEL_LOG_WRITE_LAT_LOG,		"intel", "Write Latencies",
192    print_intel_write_lat_log,		DEFAULT_SIZE);
193NVME_LOGPAGE(intel_smart,
194    INTEL_LOG_ADD_SMART,		"intel", "Extra Health/SMART Data",
195    print_intel_add_smart,		DEFAULT_SIZE);
196