xref: /freebsd/sbin/nvmecontrol/logpage.c (revision 4f52dfbb)
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 #define DEFAULT_SIZE	(4096)
52 #define MAX_FW_SLOTS	(7)
53 
54 typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
55 
56 struct kv_name
57 {
58 	uint32_t key;
59 	const char *name;
60 };
61 
62 static const char *
63 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
64 {
65 	static char bad[32];
66 	size_t i;
67 
68 	for (i = 0; i < kv_count; i++, kv++)
69 		if (kv->key == key)
70 			return kv->name;
71 	snprintf(bad, sizeof(bad), "Attribute %#x", key);
72 	return bad;
73 }
74 
75 static void
76 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
77 {
78 
79 	print_hex(data, length);
80 }
81 
82 static void
83 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
84 {
85 
86 	write(STDOUT_FILENO, data, length);
87 }
88 
89 static void *
90 get_log_buffer(uint32_t size)
91 {
92 	void	*buf;
93 
94 	if ((buf = malloc(size)) == NULL)
95 		errx(1, "unable to malloc %u bytes", size);
96 
97 	memset(buf, 0, size);
98 	return (buf);
99 }
100 
101 void
102 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
103     uint32_t payload_size)
104 {
105 	struct nvme_pt_command	pt;
106 	struct nvme_error_information_entry	*err_entry;
107 	int i, err_pages;
108 
109 	memset(&pt, 0, sizeof(pt));
110 	pt.cmd.opc_fuse = NVME_CMD_SET_OPC(NVME_OPC_GET_LOG_PAGE);
111 	pt.cmd.nsid = htole32(nsid);
112 	pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
113 	pt.cmd.cdw10 |= log_page;
114 	pt.cmd.cdw10 = htole32(pt.cmd.cdw10);
115 	pt.buf = payload;
116 	pt.len = payload_size;
117 	pt.is_read = 1;
118 
119 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
120 		err(1, "get log page request failed");
121 
122 	/* Convert data to host endian */
123 	switch (log_page) {
124 	case NVME_LOG_ERROR:
125 		err_entry = (struct nvme_error_information_entry *)payload;
126 		err_pages = payload_size / sizeof(struct nvme_error_information_entry);
127 		for (i = 0; i < err_pages; i++)
128 			nvme_error_information_entry_swapbytes(err_entry++);
129 		break;
130 	case NVME_LOG_HEALTH_INFORMATION:
131 		nvme_health_information_page_swapbytes(
132 		    (struct nvme_health_information_page *)payload);
133 		break;
134 	case NVME_LOG_FIRMWARE_SLOT:
135 		nvme_firmware_page_swapbytes(
136 		    (struct nvme_firmware_page *)payload);
137 		break;
138 	case INTEL_LOG_TEMP_STATS:
139 		intel_log_temp_stats_swapbytes(
140 		    (struct intel_log_temp_stats *)payload);
141 		break;
142 	default:
143 		break;
144 	}
145 
146 	if (nvme_completion_is_error(&pt.cpl))
147 		errx(1, "get log page request returned error");
148 }
149 
150 static void
151 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
152 {
153 	int					i, nentries;
154 	uint16_t				status;
155 	uint8_t					p, sc, sct, m, dnr;
156 	struct nvme_error_information_entry	*entry = buf;
157 
158 	printf("Error Information Log\n");
159 	printf("=====================\n");
160 
161 	if (entry->error_count == 0) {
162 		printf("No error entries found\n");
163 		return;
164 	}
165 
166 	nentries = size/sizeof(struct nvme_error_information_entry);
167 	for (i = 0; i < nentries; i++, entry++) {
168 		if (entry->error_count == 0)
169 			break;
170 
171 		status = entry->status;
172 
173 		p = NVME_STATUS_GET_P(status);
174 		sc = NVME_STATUS_GET_SC(status);
175 		sct = NVME_STATUS_GET_SCT(status);
176 		m = NVME_STATUS_GET_M(status);
177 		dnr = NVME_STATUS_GET_DNR(status);
178 
179 		printf("Entry %02d\n", i + 1);
180 		printf("=========\n");
181 		printf(" Error count:          %ju\n", entry->error_count);
182 		printf(" Submission queue ID:  %u\n", entry->sqid);
183 		printf(" Command ID:           %u\n", entry->cid);
184 		/* TODO: Export nvme_status_string structures from kernel? */
185 		printf(" Status:\n");
186 		printf("  Phase tag:           %d\n", p);
187 		printf("  Status code:         %d\n", sc);
188 		printf("  Status code type:    %d\n", sct);
189 		printf("  More:                %d\n", m);
190 		printf("  DNR:                 %d\n", dnr);
191 		printf(" Error location:       %u\n", entry->error_location);
192 		printf(" LBA:                  %ju\n", entry->lba);
193 		printf(" Namespace ID:         %u\n", entry->nsid);
194 		printf(" Vendor specific info: %u\n", entry->vendor_specific);
195 	}
196 }
197 
198 static void
199 print_temp(uint16_t t)
200 {
201 	printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
202 }
203 
204 
205 static void
206 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
207 {
208 	struct nvme_health_information_page *health = buf;
209 	char cbuf[UINT128_DIG + 1];
210 	uint8_t	warning;
211 	int i;
212 
213 	warning = health->critical_warning;
214 
215 	printf("SMART/Health Information Log\n");
216 	printf("============================\n");
217 
218 	printf("Critical Warning State:         0x%02x\n", warning);
219 	printf(" Available spare:               %d\n",
220 	    !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE));
221 	printf(" Temperature:                   %d\n",
222 	    !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE));
223 	printf(" Device reliability:            %d\n",
224 	    !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY));
225 	printf(" Read only:                     %d\n",
226 	    !!(warning & NVME_CRIT_WARN_ST_READ_ONLY));
227 	printf(" Volatile memory backup:        %d\n",
228 	    !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP));
229 	printf("Temperature:                    ");
230 	print_temp(health->temperature);
231 	printf("Available spare:                %u\n",
232 	    health->available_spare);
233 	printf("Available spare threshold:      %u\n",
234 	    health->available_spare_threshold);
235 	printf("Percentage used:                %u\n",
236 	    health->percentage_used);
237 
238 	printf("Data units (512,000 byte) read: %s\n",
239 	    uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
240 	printf("Data units written:             %s\n",
241 	    uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
242 	printf("Host read commands:             %s\n",
243 	    uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
244 	printf("Host write commands:            %s\n",
245 	    uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
246 	printf("Controller busy time (minutes): %s\n",
247 	    uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
248 	printf("Power cycles:                   %s\n",
249 	    uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
250 	printf("Power on hours:                 %s\n",
251 	    uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
252 	printf("Unsafe shutdowns:               %s\n",
253 	    uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
254 	printf("Media errors:                   %s\n",
255 	    uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
256 	printf("No. error info log entries:     %s\n",
257 	    uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
258 
259 	printf("Warning Temp Composite Time:    %d\n", health->warning_temp_time);
260 	printf("Error Temp Composite Time:      %d\n", health->error_temp_time);
261 	for (i = 0; i < 8; i++) {
262 		if (health->temp_sensor[i] == 0)
263 			continue;
264 		printf("Temperature Sensor %d:           ", i + 1);
265 		print_temp(health->temp_sensor[i]);
266 	}
267 }
268 
269 static void
270 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
271 {
272 	int				i, slots;
273 	const char			*status;
274 	struct nvme_firmware_page	*fw = buf;
275 	uint8_t				afi_slot;
276 	uint16_t			oacs_fw;
277 	uint8_t				fw_num_slots;
278 
279 	afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT;
280 	afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK;
281 
282 	oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
283 		NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
284 	fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
285 		NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
286 
287 	printf("Firmware Slot Log\n");
288 	printf("=================\n");
289 
290 	if (oacs_fw == 0)
291 		slots = 1;
292 	else
293 		slots = MIN(fw_num_slots, MAX_FW_SLOTS);
294 
295 	for (i = 0; i < slots; i++) {
296 		printf("Slot %d: ", i + 1);
297 		if (afi_slot == i + 1)
298 			status = "  Active";
299 		else
300 			status = "Inactive";
301 
302 		if (fw->revision[i] == 0LLU)
303 			printf("Empty\n");
304 		else
305 			if (isprint(*(char *)&fw->revision[i]))
306 				printf("[%s] %.8s\n", status,
307 				    (char *)&fw->revision[i]);
308 			else
309 				printf("[%s] %016jx\n", status,
310 				    fw->revision[i]);
311 	}
312 }
313 
314 /*
315  * Intel specific log pages from
316  * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
317  *
318  * Though the version as of this date has a typo for the size of log page 0xca,
319  * offset 147: it is only 1 byte, not 6.
320  */
321 static void
322 print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
323 {
324 	struct intel_log_temp_stats	*temp = buf;
325 
326 	printf("Intel Temperature Log\n");
327 	printf("=====================\n");
328 
329 	printf("Current:                        ");
330 	print_temp(temp->current);
331 	printf("Overtemp Last Flags             %#jx\n", (uintmax_t)temp->overtemp_flag_last);
332 	printf("Overtemp Lifetime Flags         %#jx\n", (uintmax_t)temp->overtemp_flag_life);
333 	printf("Max Temperature                 ");
334 	print_temp(temp->max_temp);
335 	printf("Min Temperature                 ");
336 	print_temp(temp->min_temp);
337 	printf("Max Operating Temperature       ");
338 	print_temp(temp->max_oper_temp);
339 	printf("Min Operating Temperature       ");
340 	print_temp(temp->min_oper_temp);
341 	printf("Estimated Temperature Offset:   %ju C/K\n", (uintmax_t)temp->est_offset);
342 }
343 
344 /*
345  * Format from Table 22, section 5.7 IO Command Latency Statistics.
346  * Read and write stats pages have identical encoding.
347  */
348 static void
349 print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
350 {
351 	const char *walker = buf;
352 	int i;
353 
354 	printf("Major:                         %d\n", le16dec(walker + 0));
355 	printf("Minor:                         %d\n", le16dec(walker + 2));
356 	for (i = 0; i < 32; i++)
357 		printf("%4dus-%4dus:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4));
358 	for (i = 1; i < 32; i++)
359 		printf("%4dms-%4dms:                 %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4));
360 	for (i = 1; i < 32; i++)
361 		printf("%4dms-%4dms:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4));
362 }
363 
364 static void
365 print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
366 {
367 
368 	printf("Intel Read Latency Log\n");
369 	printf("======================\n");
370 	print_intel_read_write_lat_log(cdata, buf, size);
371 }
372 
373 static void
374 print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
375 {
376 
377 	printf("Intel Write Latency Log\n");
378 	printf("=======================\n");
379 	print_intel_read_write_lat_log(cdata, buf, size);
380 }
381 
382 /*
383  * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented.
384  */
385 static void
386 print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
387 {
388 	uint8_t *walker = buf;
389 	uint8_t *end = walker + 150;
390 	const char *name;
391 	uint64_t raw;
392 	uint8_t normalized;
393 
394 	static struct kv_name kv[] =
395 	{
396 		{ 0xab, "Program Fail Count" },
397 		{ 0xac, "Erase Fail Count" },
398 		{ 0xad, "Wear Leveling Count" },
399 		{ 0xb8, "End to End Error Count" },
400 		{ 0xc7, "CRC Error Count" },
401 		{ 0xe2, "Timed: Media Wear" },
402 		{ 0xe3, "Timed: Host Read %" },
403 		{ 0xe4, "Timed: Elapsed Time" },
404 		{ 0xea, "Thermal Throttle Status" },
405 		{ 0xf0, "Retry Buffer Overflows" },
406 		{ 0xf3, "PLL Lock Loss Count" },
407 		{ 0xf4, "NAND Bytes Written" },
408 		{ 0xf5, "Host Bytes Written" },
409 	};
410 
411 	printf("Additional SMART Data Log\n");
412 	printf("=========================\n");
413 	/*
414 	 * walker[0] = Key
415 	 * walker[1,2] = reserved
416 	 * walker[3] = Normalized Value
417 	 * walker[4] = reserved
418 	 * walker[5..10] = Little Endian Raw value
419 	 *	(or other represenations)
420 	 * walker[11] = reserved
421 	 */
422 	while (walker < end) {
423 		name = kv_lookup(kv, nitems(kv), *walker);
424 		normalized = walker[3];
425 		raw = le48dec(walker + 5);
426 		switch (*walker){
427 		case 0:
428 			break;
429 		case 0xad:
430 			printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized,
431 			    le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
432 			break;
433 		case 0xe2:
434 			printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
435 			break;
436 		case 0xea:
437 			printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6));
438 			break;
439 		default:
440 			printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
441 			break;
442 		}
443 		walker += 12;
444 	}
445 }
446 
447 /*
448  * HGST's 0xc1 page. This is a grab bag of additional data. Please see
449  * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
450  * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
451  * Appendix A for details
452  */
453 
454 typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
455 
456 struct subpage_print
457 {
458 	uint16_t key;
459 	subprint_fn_t fn;
460 };
461 
462 static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
463 static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
464 static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
465 static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
466 static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
467 static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
468 static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
469 static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
470 static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
471 static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
472 
473 static struct subpage_print hgst_subpage[] = {
474 	{ 0x02, print_hgst_info_write_errors },
475 	{ 0x03, print_hgst_info_read_errors },
476 	{ 0x05, print_hgst_info_verify_errors },
477 	{ 0x10, print_hgst_info_self_test },
478 	{ 0x15, print_hgst_info_background_scan },
479 	{ 0x30, print_hgst_info_erase_errors },
480 	{ 0x31, print_hgst_info_erase_counts },
481 	{ 0x32, print_hgst_info_temp_history },
482 	{ 0x37, print_hgst_info_ssd_perf },
483 	{ 0x38, print_hgst_info_firmware_load },
484 };
485 
486 /* Print a subpage that is basically just key value pairs */
487 static void
488 print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
489     const struct kv_name *kv, size_t kv_count)
490 {
491 	uint8_t *wsp, *esp;
492 	uint16_t ptype;
493 	uint8_t plen;
494 	uint64_t param;
495 	int i;
496 
497 	wsp = buf;
498 	esp = wsp + size;
499 	while (wsp < esp) {
500 		ptype = le16dec(wsp);
501 		wsp += 2;
502 		wsp++;			/* Flags, just ignore */
503 		plen = *wsp++;
504 		param = 0;
505 		for (i = 0; i < plen; i++)
506 			param |= (uint64_t)*wsp++ << (i * 8);
507 		printf("  %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param);
508 	}
509 }
510 
511 static void
512 print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
513 {
514 	static struct kv_name kv[] =
515 	{
516 		{ 0x0000, "Corrected Without Delay" },
517 		{ 0x0001, "Corrected Maybe Delayed" },
518 		{ 0x0002, "Re-Writes" },
519 		{ 0x0003, "Errors Corrected" },
520 		{ 0x0004, "Correct Algorithm Used" },
521 		{ 0x0005, "Bytes Processed" },
522 		{ 0x0006, "Uncorrected Errors" },
523 		{ 0x8000, "Flash Write Commands" },
524 		{ 0x8001, "HGST Special" },
525 	};
526 
527 	printf("Write Errors Subpage:\n");
528 	print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
529 }
530 
531 static void
532 print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
533 {
534 	static struct kv_name kv[] =
535 	{
536 		{ 0x0000, "Corrected Without Delay" },
537 		{ 0x0001, "Corrected Maybe Delayed" },
538 		{ 0x0002, "Re-Reads" },
539 		{ 0x0003, "Errors Corrected" },
540 		{ 0x0004, "Correct Algorithm Used" },
541 		{ 0x0005, "Bytes Processed" },
542 		{ 0x0006, "Uncorrected Errors" },
543 		{ 0x8000, "Flash Read Commands" },
544 		{ 0x8001, "XOR Recovered" },
545 		{ 0x8002, "Total Corrected Bits" },
546 	};
547 
548 	printf("Read Errors Subpage:\n");
549 	print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
550 }
551 
552 static void
553 print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
554 {
555 	static struct kv_name kv[] =
556 	{
557 		{ 0x0000, "Corrected Without Delay" },
558 		{ 0x0001, "Corrected Maybe Delayed" },
559 		{ 0x0002, "Re-Reads" },
560 		{ 0x0003, "Errors Corrected" },
561 		{ 0x0004, "Correct Algorithm Used" },
562 		{ 0x0005, "Bytes Processed" },
563 		{ 0x0006, "Uncorrected Errors" },
564 		{ 0x8000, "Commands Processed" },
565 	};
566 
567 	printf("Verify Errors Subpage:\n");
568 	print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
569 }
570 
571 static void
572 print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
573 {
574 	size_t i;
575 	uint8_t *walker = buf;
576 	uint16_t code, hrs;
577 	uint32_t lba;
578 
579 	printf("Self Test Subpage:\n");
580 	for (i = 0; i < size / 20; i++) {	/* Each entry is 20 bytes */
581 		code = le16dec(walker);
582 		walker += 2;
583 		walker++;			/* Ignore fixed flags */
584 		if (*walker == 0)		/* Last entry is zero length */
585 			break;
586 		if (*walker++ != 0x10) {
587 			printf("Bad length for self test report\n");
588 			return;
589 		}
590 		printf("  %-30s: %d\n", "Recent Test", code);
591 		printf("    %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
592 		printf("    %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
593 		walker++;
594 		printf("    %-28s: %#x\n", "Self-Test Number", *walker++);
595 		hrs = le16dec(walker);
596 		walker += 2;
597 		lba = le32dec(walker);
598 		walker += 4;
599 		printf("    %-28s: %u\n", "Total Power On Hrs", hrs);
600 		printf("    %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba);
601 		printf("    %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
602 		printf("    %-28s: %#x\n", "Additional Sense Code", *walker++);
603 		printf("    %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
604 		printf("    %-28s: %#x\n", "Vendor Specific Detail", *walker++);
605 	}
606 }
607 
608 static void
609 print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
610 {
611 	uint8_t *walker = buf;
612 	uint8_t status;
613 	uint16_t code, nscan, progress;
614 	uint32_t pom, nand;
615 
616 	printf("Background Media Scan Subpage:\n");
617 	/* Decode the header */
618 	code = le16dec(walker);
619 	walker += 2;
620 	walker++;			/* Ignore fixed flags */
621 	if (*walker++ != 0x10) {
622 		printf("Bad length for background scan header\n");
623 		return;
624 	}
625 	if (code != 0) {
626 		printf("Expceted code 0, found code %#x\n", code);
627 		return;
628 	}
629 	pom = le32dec(walker);
630 	walker += 4;
631 	walker++;			/* Reserved */
632 	status = *walker++;
633 	nscan = le16dec(walker);
634 	walker += 2;
635 	progress = le16dec(walker);
636 	walker += 2;
637 	walker += 6;			/* Reserved */
638 	printf("  %-30s: %d\n", "Power On Minutes", pom);
639 	printf("  %-30s: %x (%s)\n", "BMS Status", status,
640 	    status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown")));
641 	printf("  %-30s: %d\n", "Number of BMS", nscan);
642 	printf("  %-30s: %d\n", "Progress Current BMS", progress);
643 	/* Report retirements */
644 	if (walker - (uint8_t *)buf != 20) {
645 		printf("Coding error, offset not 20\n");
646 		return;
647 	}
648 	size -= 20;
649 	printf("  %-30s: %d\n", "BMS retirements", size / 0x18);
650 	while (size > 0) {
651 		code = le16dec(walker);
652 		walker += 2;
653 		walker++;
654 		if (*walker++ != 0x14) {
655 			printf("Bad length parameter\n");
656 			return;
657 		}
658 		pom = le32dec(walker);
659 		walker += 4;
660 		/*
661 		 * Spec sheet says the following are hard coded, if true, just
662 		 * print the NAND retirement.
663 		 */
664 		if (walker[0] == 0x41 &&
665 		    walker[1] == 0x0b &&
666 		    walker[2] == 0x01 &&
667 		    walker[3] == 0x00 &&
668 		    walker[4] == 0x00 &&
669 		    walker[5] == 0x00 &&
670 		    walker[6] == 0x00 &&
671 		    walker[7] == 0x00) {
672 			walker += 8;
673 			walker += 4;	/* Skip reserved */
674 			nand = le32dec(walker);
675 			walker += 4;
676 			printf("  %-30s: %d\n", "Retirement number", code);
677 			printf("    %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
678 		} else {
679 			printf("Parameter %#x entry corrupt\n", code);
680 			walker += 16;
681 		}
682 	}
683 }
684 
685 static void
686 print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
687 {
688 	static struct kv_name kv[] =
689 	{
690 		{ 0x0000, "Corrected Without Delay" },
691 		{ 0x0001, "Corrected Maybe Delayed" },
692 		{ 0x0002, "Re-Erase" },
693 		{ 0x0003, "Errors Corrected" },
694 		{ 0x0004, "Correct Algorithm Used" },
695 		{ 0x0005, "Bytes Processed" },
696 		{ 0x0006, "Uncorrected Errors" },
697 		{ 0x8000, "Flash Erase Commands" },
698 		{ 0x8001, "Mfg Defect Count" },
699 		{ 0x8002, "Grown Defect Count" },
700 		{ 0x8003, "Erase Count -- User" },
701 		{ 0x8004, "Erase Count -- System" },
702 	};
703 
704 	printf("Erase Errors Subpage:\n");
705 	print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
706 }
707 
708 static void
709 print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
710 {
711 	/* My drive doesn't export this -- so not coding up */
712 	printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
713 }
714 
715 static void
716 print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
717 {
718 	uint8_t *walker = buf;
719 	uint32_t min;
720 
721 	printf("Temperature History:\n");
722 	printf("  %-30s: %d C\n", "Current Temperature", *walker++);
723 	printf("  %-30s: %d C\n", "Reference Temperature", *walker++);
724 	printf("  %-30s: %d C\n", "Maximum Temperature", *walker++);
725 	printf("  %-30s: %d C\n", "Minimum Temperature", *walker++);
726 	min = le32dec(walker);
727 	walker += 4;
728 	printf("  %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
729 	min = le32dec(walker);
730 	walker += 4;
731 	printf("  %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60);
732 	min = le32dec(walker);
733 	walker += 4;
734 	printf("  %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
735 }
736 
737 static void
738 print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused)
739 {
740 	uint8_t *walker = buf;
741 	uint64_t val;
742 
743 	printf("SSD Performance Subpage Type %d:\n", res);
744 	val = le64dec(walker);
745 	walker += 8;
746 	printf("  %-30s: %ju\n", "Host Read Commands", val);
747 	val = le64dec(walker);
748 	walker += 8;
749 	printf("  %-30s: %ju\n", "Host Read Blocks", val);
750 	val = le64dec(walker);
751 	walker += 8;
752 	printf("  %-30s: %ju\n", "Host Cache Read Hits Commands", val);
753 	val = le64dec(walker);
754 	walker += 8;
755 	printf("  %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
756 	val = le64dec(walker);
757 	walker += 8;
758 	printf("  %-30s: %ju\n", "Host Read Commands Stalled", val);
759 	val = le64dec(walker);
760 	walker += 8;
761 	printf("  %-30s: %ju\n", "Host Write Commands", val);
762 	val = le64dec(walker);
763 	walker += 8;
764 	printf("  %-30s: %ju\n", "Host Write Blocks", val);
765 	val = le64dec(walker);
766 	walker += 8;
767 	printf("  %-30s: %ju\n", "Host Write Odd Start Commands", val);
768 	val = le64dec(walker);
769 	walker += 8;
770 	printf("  %-30s: %ju\n", "Host Write Odd End Commands", val);
771 	val = le64dec(walker);
772 	walker += 8;
773 	printf("  %-30s: %ju\n", "Host Write Commands Stalled", val);
774 	val = le64dec(walker);
775 	walker += 8;
776 	printf("  %-30s: %ju\n", "NAND Read Commands", val);
777 	val = le64dec(walker);
778 	walker += 8;
779 	printf("  %-30s: %ju\n", "NAND Read Blocks", val);
780 	val = le64dec(walker);
781 	walker += 8;
782 	printf("  %-30s: %ju\n", "NAND Write Commands", val);
783 	val = le64dec(walker);
784 	walker += 8;
785 	printf("  %-30s: %ju\n", "NAND Write Blocks", val);
786 	val = le64dec(walker);
787 	walker += 8;
788 	printf("  %-30s: %ju\n", "NAND Read Before Writes", val);
789 }
790 
791 static void
792 print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
793 {
794 	uint8_t *walker = buf;
795 
796 	printf("Firmware Load Subpage:\n");
797 	printf("  %-30s: %d\n", "Firmware Downloads", le32dec(walker));
798 }
799 
800 static void
801 kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp)
802 {
803 	size_t i;
804 
805 	for (i = 0; i < nsp; i++, sp++) {
806 		if (sp->key == subtype) {
807 			sp->fn(buf, subtype, res, size);
808 			return;
809 		}
810 	}
811 	printf("No handler for page type %x\n", subtype);
812 }
813 
814 static void
815 print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
816 {
817 	uint8_t	*walker, *end, *subpage;
818 	int pages;
819 	uint16_t len;
820 	uint8_t subtype, res;
821 
822 	printf("HGST Extra Info Log\n");
823 	printf("===================\n");
824 
825 	walker = buf;
826 	pages = *walker++;
827 	walker++;
828 	len = le16dec(walker);
829 	walker += 2;
830 	end = walker + len;		/* Length is exclusive of this header */
831 
832 	while (walker < end) {
833 		subpage = walker + 4;
834 		subtype = *walker++ & 0x3f;	/* subtype */
835 		res = *walker++;		/* Reserved */
836 		len = le16dec(walker);
837 		walker += len + 2;		/* Length, not incl header */
838 		if (walker > end) {
839 			printf("Ooops! Off the end of the list\n");
840 			break;
841 		}
842 		kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage));
843 	}
844 }
845 
846 /*
847  * Table of log page printer / sizing.
848  *
849  * This includes Intel specific pages that are widely implemented.
850  * Make sure you keep all the pages of one vendor together so -v help
851  * lists all the vendors pages.
852  */
853 static struct logpage_function {
854 	uint8_t		log_page;
855 	const char     *vendor;
856 	const char     *name;
857 	print_fn_t	print_fn;
858 	size_t		size;
859 } logfuncs[] = {
860 	{NVME_LOG_ERROR,		NULL,	"Drive Error Log",
861 	 print_log_error,		0},
862 	{NVME_LOG_HEALTH_INFORMATION,	NULL,	"Health/SMART Data",
863 	 print_log_health,		sizeof(struct nvme_health_information_page)},
864 	{NVME_LOG_FIRMWARE_SLOT,	NULL,	"Firmware Information",
865 	 print_log_firmware,		sizeof(struct nvme_firmware_page)},
866 	{HGST_INFO_LOG,			"hgst",	"Detailed Health/SMART",
867 	 print_hgst_info_log,		DEFAULT_SIZE},
868 	{HGST_INFO_LOG,			"wds",	"Detailed Health/SMART",
869 	 print_hgst_info_log,		DEFAULT_SIZE},
870 	{INTEL_LOG_TEMP_STATS,		"intel", "Temperature Stats",
871 	 print_intel_temp_stats,	sizeof(struct intel_log_temp_stats)},
872 	{INTEL_LOG_READ_LAT_LOG,	"intel", "Read Latencies",
873 	 print_intel_read_lat_log,	DEFAULT_SIZE},
874 	{INTEL_LOG_WRITE_LAT_LOG,	"intel", "Write Latencies",
875 	 print_intel_write_lat_log,	DEFAULT_SIZE},
876 	{INTEL_LOG_ADD_SMART,		"intel", "Extra Health/SMART Data",
877 	 print_intel_add_smart,		DEFAULT_SIZE},
878 	{INTEL_LOG_ADD_SMART,		"samsung", "Extra Health/SMART Data",
879 	 print_intel_add_smart,		DEFAULT_SIZE},
880 
881 	{0, NULL, NULL, NULL, 0},
882 };
883 
884 static void
885 logpage_usage(void)
886 {
887 	fprintf(stderr, "usage:\n");
888 	fprintf(stderr, LOGPAGE_USAGE);
889 	exit(1);
890 }
891 
892 static void
893 logpage_help(void)
894 {
895 	struct logpage_function		*f;
896 	const char 			*v;
897 
898 	fprintf(stderr, "\n");
899 	fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
900 	fprintf(stderr, "-------- ---------- ----------\n");
901 	for (f = logfuncs; f->log_page > 0; f++) {
902 		v = f->vendor == NULL ? "-" : f->vendor;
903 		fprintf(stderr, "0x%02x     %-10s %s\n", f->log_page, v, f->name);
904 	}
905 
906 	exit(1);
907 }
908 
909 void
910 logpage(int argc, char *argv[])
911 {
912 	int				fd;
913 	int				log_page = 0, pageflag = false;
914 	int				binflag = false, hexflag = false, ns_specified;
915 	int				opt;
916 	char				*p;
917 	char				cname[64];
918 	uint32_t			nsid, size;
919 	void				*buf;
920 	const char			*vendor = NULL;
921 	struct logpage_function		*f;
922 	struct nvme_controller_data	cdata;
923 	print_fn_t			print_fn;
924 	uint8_t				ns_smart;
925 
926 	while ((opt = getopt(argc, argv, "bp:xv:")) != -1) {
927 		switch (opt) {
928 		case 'b':
929 			binflag = true;
930 			break;
931 		case 'p':
932 			if (strcmp(optarg, "help") == 0)
933 				logpage_help();
934 
935 			/* TODO: Add human-readable ASCII page IDs */
936 			log_page = strtol(optarg, &p, 0);
937 			if (p != NULL && *p != '\0') {
938 				fprintf(stderr,
939 				    "\"%s\" not valid log page id.\n",
940 				    optarg);
941 				logpage_usage();
942 			}
943 			pageflag = true;
944 			break;
945 		case 'x':
946 			hexflag = true;
947 			break;
948 		case 'v':
949 			if (strcmp(optarg, "help") == 0)
950 				logpage_help();
951 			vendor = optarg;
952 			break;
953 		}
954 	}
955 
956 	if (!pageflag) {
957 		printf("Missing page_id (-p).\n");
958 		logpage_usage();
959 	}
960 
961 	/* Check that a controller and/or namespace was specified. */
962 	if (optind >= argc)
963 		logpage_usage();
964 
965 	if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
966 		ns_specified = true;
967 		parse_ns_str(argv[optind], cname, &nsid);
968 		open_dev(cname, &fd, 1, 1);
969 	} else {
970 		ns_specified = false;
971 		nsid = NVME_GLOBAL_NAMESPACE_TAG;
972 		open_dev(argv[optind], &fd, 1, 1);
973 	}
974 
975 	read_controller_data(fd, &cdata);
976 
977 	ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
978 		NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
979 
980 	/*
981 	 * The log page attribtues indicate whether or not the controller
982 	 * supports the SMART/Health information log page on a per
983 	 * namespace basis.
984 	 */
985 	if (ns_specified) {
986 		if (log_page != NVME_LOG_HEALTH_INFORMATION)
987 			errx(1, "log page %d valid only at controller level",
988 			    log_page);
989 		if (ns_smart == 0)
990 			errx(1,
991 			    "controller does not support per namespace "
992 			    "smart/health information");
993 	}
994 
995 	print_fn = print_log_hex;
996 	size = DEFAULT_SIZE;
997 	if (binflag)
998 		print_fn = print_bin;
999 	if (!binflag && !hexflag) {
1000 		/*
1001 		 * See if there is a pretty print function for the specified log
1002 		 * page.  If one isn't found, we just revert to the default
1003 		 * (print_hex). If there was a vendor specified bt the user, and
1004 		 * the page is vendor specific, don't match the print function
1005 		 * unless the vendors match.
1006 		 */
1007 		for (f = logfuncs; f->log_page > 0; f++) {
1008 			if (f->vendor != NULL && vendor != NULL &&
1009 			    strcmp(f->vendor, vendor) != 0)
1010 				continue;
1011 			if (log_page != f->log_page)
1012 				continue;
1013 			print_fn = f->print_fn;
1014 			size = f->size;
1015 			break;
1016 		}
1017 	}
1018 
1019 	if (log_page == NVME_LOG_ERROR) {
1020 		size = sizeof(struct nvme_error_information_entry);
1021 		size *= (cdata.elpe + 1);
1022 	}
1023 
1024 	/* Read the log page */
1025 	buf = get_log_buffer(size);
1026 	read_logpage(fd, log_page, nsid, buf, size);
1027 	print_fn(&cdata, buf, size);
1028 
1029 	close(fd);
1030 	exit(0);
1031 }
1032