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