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