xref: /freebsd-src/sbin/nvmecontrol/logpage.c (revision 98ab7d0a30f8d0fc9c51e1e97ac29f87750b526c)
175871362SJim Harris /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
475871362SJim Harris  * Copyright (c) 2013 EMC Corp.
575871362SJim Harris  * All rights reserved.
675871362SJim Harris  *
775871362SJim Harris  * Copyright (C) 2012-2013 Intel Corporation
875871362SJim Harris  * All rights reserved.
9fe83abacSWarner Losh  * Copyright (C) 2016-2023 Warner Losh <imp@FreeBSD.org>
106c99d132SAlexander Motin  * Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
1175871362SJim Harris  *
1275871362SJim Harris  * Redistribution and use in source and binary forms, with or without
1375871362SJim Harris  * modification, are permitted provided that the following conditions
1475871362SJim Harris  * are met:
1575871362SJim Harris  * 1. Redistributions of source code must retain the above copyright
1675871362SJim Harris  *    notice, this list of conditions and the following disclaimer.
1775871362SJim Harris  * 2. Redistributions in binary form must reproduce the above copyright
1875871362SJim Harris  *    notice, this list of conditions and the following disclaimer in the
1975871362SJim Harris  *    documentation and/or other materials provided with the distribution.
2075871362SJim Harris  *
2175871362SJim Harris  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2275871362SJim Harris  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2375871362SJim Harris  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2475871362SJim Harris  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2575871362SJim Harris  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2675871362SJim Harris  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2775871362SJim Harris  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2875871362SJim Harris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2975871362SJim Harris  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3075871362SJim Harris  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3175871362SJim Harris  * SUCH DAMAGE.
3275871362SJim Harris  */
3375871362SJim Harris 
3475871362SJim Harris #include <sys/param.h>
3575871362SJim Harris #include <sys/ioccom.h>
3675871362SJim Harris 
3775871362SJim Harris #include <ctype.h>
38821ef73cSJim Harris #include <err.h>
3975871362SJim Harris #include <fcntl.h>
4075871362SJim Harris #include <stdbool.h>
4175871362SJim Harris #include <stddef.h>
4275871362SJim Harris #include <stdio.h>
4375871362SJim Harris #include <stdlib.h>
4475871362SJim Harris #include <string.h>
455dc463f9SAlexander Motin #include <sysexits.h>
4675871362SJim Harris #include <unistd.h>
4733a099d2SWarner Losh #include <sys/endian.h>
4833a099d2SWarner Losh 
4975871362SJim Harris #include "nvmecontrol.h"
5075871362SJim Harris 
51f634b4c1SWarner Losh /* Tables for command line parsing */
52f634b4c1SWarner Losh 
53f634b4c1SWarner Losh static cmd_fn_t logpage;
54f634b4c1SWarner Losh 
55f634b4c1SWarner Losh #define NONE 0xffffffffu
56f634b4c1SWarner Losh static struct options {
57f634b4c1SWarner Losh 	bool		binary;
58f634b4c1SWarner Losh 	bool		hex;
59f634b4c1SWarner Losh 	uint32_t	page;
606c99d132SAlexander Motin 	uint8_t		lsp;
616c99d132SAlexander Motin 	uint16_t	lsi;
626c99d132SAlexander Motin 	bool		rae;
63f634b4c1SWarner Losh 	const char	*vendor;
64f634b4c1SWarner Losh 	const char	*dev;
65f634b4c1SWarner Losh } opt = {
66f634b4c1SWarner Losh 	.binary = false,
67f634b4c1SWarner Losh 	.hex = false,
68f634b4c1SWarner Losh 	.page = NONE,
696c99d132SAlexander Motin 	.lsp = 0,
706c99d132SAlexander Motin 	.lsi = 0,
716c99d132SAlexander Motin 	.rae = false,
72f634b4c1SWarner Losh 	.vendor = NULL,
73f634b4c1SWarner Losh 	.dev = NULL,
74f634b4c1SWarner Losh };
75f634b4c1SWarner Losh 
76f634b4c1SWarner Losh static const struct opts logpage_opts[] = {
77f634b4c1SWarner Losh #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
78f634b4c1SWarner Losh 	OPT("binary", 'b', arg_none, opt, binary,
79f634b4c1SWarner Losh 	    "Dump the log page as binary"),
80f634b4c1SWarner Losh 	OPT("hex", 'x', arg_none, opt, hex,
81f634b4c1SWarner Losh 	    "Dump the log page as hex"),
82f634b4c1SWarner Losh 	OPT("page", 'p', arg_uint32, opt, page,
83f634b4c1SWarner Losh 	    "Page to dump"),
846c99d132SAlexander Motin 	OPT("lsp", 'f', arg_uint8, opt, lsp,
856c99d132SAlexander Motin 	    "Log Specific Field"),
8697db0313SAlexander Motin 	OPT("lsi", 'i', arg_uint16, opt, lsi,
876c99d132SAlexander Motin 	    "Log Specific Identifier"),
886c99d132SAlexander Motin 	OPT("rae", 'r', arg_none, opt, rae,
896c99d132SAlexander Motin 	    "Retain Asynchronous Event"),
90f634b4c1SWarner Losh 	OPT("vendor", 'v', arg_string, opt, vendor,
91f634b4c1SWarner Losh 	    "Vendor specific formatting"),
92f634b4c1SWarner Losh 	{ NULL, 0, arg_none, NULL, NULL }
93f634b4c1SWarner Losh };
94f634b4c1SWarner Losh #undef OPT
95f634b4c1SWarner Losh 
96f634b4c1SWarner Losh static const struct args logpage_args[] = {
97f634b4c1SWarner Losh 	{ arg_string, &opt.dev, "<controller id|namespace id>" },
98f634b4c1SWarner Losh 	{ arg_none, NULL, NULL },
99f634b4c1SWarner Losh };
100f634b4c1SWarner Losh 
101f634b4c1SWarner Losh static struct cmd logpage_cmd = {
102f634b4c1SWarner Losh 	.name = "logpage",
103f634b4c1SWarner Losh 	.fn = logpage,
104f634b4c1SWarner Losh 	.descr = "Print logpages in human-readable form",
105f634b4c1SWarner Losh 	.ctx_size = sizeof(opt),
106f634b4c1SWarner Losh 	.opts = logpage_opts,
107f634b4c1SWarner Losh 	.args = logpage_args,
108f634b4c1SWarner Losh };
109f634b4c1SWarner Losh 
110f634b4c1SWarner Losh CMD_COMMAND(logpage_cmd);
111f634b4c1SWarner Losh 
112f634b4c1SWarner Losh /* End of tables for command line parsing */
113a13a291aSWarner Losh 
11475871362SJim Harris #define MAX_FW_SLOTS	(7)
11575871362SJim Harris 
116f428a90aSWarner Losh static SLIST_HEAD(,logpage_function) logpages;
117f428a90aSWarner Losh 
1186c99d132SAlexander Motin static int
logpage_compare(struct logpage_function * a,struct logpage_function * b)1196c99d132SAlexander Motin logpage_compare(struct logpage_function *a, struct logpage_function *b)
1206c99d132SAlexander Motin {
1216c99d132SAlexander Motin 	int c;
1226c99d132SAlexander Motin 
1236c99d132SAlexander Motin 	if ((a->vendor == NULL) != (b->vendor == NULL))
1246c99d132SAlexander Motin 		return (a->vendor == NULL ? -1 : 1);
1256c99d132SAlexander Motin 	if (a->vendor != NULL) {
1266c99d132SAlexander Motin 		c = strcmp(a->vendor, b->vendor);
1276c99d132SAlexander Motin 		if (c != 0)
1286c99d132SAlexander Motin 			return (c);
1296c99d132SAlexander Motin 	}
1306c99d132SAlexander Motin 	return ((int)a->log_page - (int)b->log_page);
1316c99d132SAlexander Motin }
1326c99d132SAlexander Motin 
133f428a90aSWarner Losh void
logpage_register(struct logpage_function * p)134f428a90aSWarner Losh logpage_register(struct logpage_function *p)
135f428a90aSWarner Losh {
1366c99d132SAlexander Motin 	struct logpage_function *l, *a;
137f428a90aSWarner Losh 
1386c99d132SAlexander Motin 	a = NULL;
1396c99d132SAlexander Motin 	l = SLIST_FIRST(&logpages);
1406c99d132SAlexander Motin 	while (l != NULL) {
1416c99d132SAlexander Motin 		if (logpage_compare(l, p) > 0)
1426c99d132SAlexander Motin 			break;
1436c99d132SAlexander Motin 		a = l;
1446c99d132SAlexander Motin 		l = SLIST_NEXT(l, link);
1456c99d132SAlexander Motin 	}
1466c99d132SAlexander Motin 	if (a == NULL)
147f428a90aSWarner Losh 		SLIST_INSERT_HEAD(&logpages, p, link);
1486c99d132SAlexander Motin 	else
1496c99d132SAlexander Motin 		SLIST_INSERT_AFTER(a, p, link);
150f428a90aSWarner Losh }
151228c4255SWarner Losh 
152a773b08bSWarner Losh const char *
kv_lookup(const struct kv_name * kv,size_t kv_count,uint32_t key)1530cf14228SWarner Losh kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
1540cf14228SWarner Losh {
1550cf14228SWarner Losh 	static char bad[32];
1560cf14228SWarner Losh 	size_t i;
1570cf14228SWarner Losh 
1580cf14228SWarner Losh 	for (i = 0; i < kv_count; i++, kv++)
1590cf14228SWarner Losh 		if (kv->key == key)
1600cf14228SWarner Losh 			return kv->name;
1610cf14228SWarner Losh 	snprintf(bad, sizeof(bad), "Attribute %#x", key);
1620cf14228SWarner Losh 	return bad;
1630cf14228SWarner Losh }
1640cf14228SWarner Losh 
165ba6da686SWarner Losh static void
print_log_hex(const struct nvme_controller_data * cdata __unused,void * data,uint32_t length)1668ce85adfSWarner Losh print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
167ba6da686SWarner Losh {
1688ce85adfSWarner Losh 
1698ce85adfSWarner Losh 	print_hex(data, length);
1708ce85adfSWarner Losh }
1718ce85adfSWarner Losh 
1728ce85adfSWarner Losh static void
print_bin(const struct nvme_controller_data * cdata __unused,void * data,uint32_t length)1738ce85adfSWarner Losh print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
1748ce85adfSWarner Losh {
1758ce85adfSWarner Losh 
176ba6da686SWarner Losh 	write(STDOUT_FILENO, data, length);
177ba6da686SWarner Losh }
178ba6da686SWarner Losh 
17975871362SJim Harris static void *
get_log_buffer(uint32_t size)180821ef73cSJim Harris get_log_buffer(uint32_t size)
18175871362SJim Harris {
18275871362SJim Harris 	void	*buf;
18375871362SJim Harris 
184821ef73cSJim Harris 	if ((buf = malloc(size)) == NULL)
1855dc463f9SAlexander Motin 		errx(EX_OSERR, "unable to malloc %u bytes", size);
186821ef73cSJim Harris 
18775871362SJim Harris 	memset(buf, 0, size);
18875871362SJim Harris 	return (buf);
18975871362SJim Harris }
19075871362SJim Harris 
19175871362SJim Harris void
read_logpage(int fd,uint8_t log_page,uint32_t nsid,uint8_t lsp,uint16_t lsi,uint8_t rae,uint64_t lpo,uint8_t csi,uint8_t ot,uint16_t uuid_index,void * payload,uint32_t payload_size)1926c99d132SAlexander Motin read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp,
193*98ab7d0aSWarner Losh     uint16_t lsi, uint8_t rae, uint64_t lpo, uint8_t csi, uint8_t ot,
194*98ab7d0aSWarner Losh     uint16_t uuid_index, void *payload, uint32_t payload_size)
19575871362SJim Harris {
19675871362SJim Harris 	struct nvme_pt_command	pt;
19785656a9aSWarner Losh 	u_int numd;
19875871362SJim Harris 
1996c99d132SAlexander Motin 	numd = payload_size / sizeof(uint32_t) - 1;
20075871362SJim Harris 	memset(&pt, 0, sizeof(pt));
2019544e6dcSChuck Tuffli 	pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
2020d787e9bSWojciech Macek 	pt.cmd.nsid = htole32(nsid);
2036c99d132SAlexander Motin 	pt.cmd.cdw10 = htole32(
2046c99d132SAlexander Motin 	    (numd << 16) |			/* NUMDL */
2056c99d132SAlexander Motin 	    (rae << 15) |			/* RAE */
2066c99d132SAlexander Motin 	    (lsp << 8) |			/* LSP */
2076c99d132SAlexander Motin 	    log_page);				/* LID */
2086c99d132SAlexander Motin 	pt.cmd.cdw11 = htole32(
2096c99d132SAlexander Motin 	    ((uint32_t)lsi << 16) |		/* LSI */
2106c99d132SAlexander Motin 	    (numd >> 16));			/* NUMDU */
211*98ab7d0aSWarner Losh 	pt.cmd.cdw12 = htole32(lpo & 0xffffffff); /* LPOL */
212*98ab7d0aSWarner Losh 	pt.cmd.cdw13 = htole32(lpo >> 32);	/* LPOU */
213*98ab7d0aSWarner Losh 	pt.cmd.cdw14 = htole32(
214*98ab7d0aSWarner Losh 	    (csi << 24) | 			/* CSI */
215*98ab7d0aSWarner Losh 	    (ot << 23) |			/* OT */
216*98ab7d0aSWarner Losh 	    uuid_index);			/* UUID Index */
21775871362SJim Harris 	pt.buf = payload;
21875871362SJim Harris 	pt.len = payload_size;
21975871362SJim Harris 	pt.is_read = 1;
22075871362SJim Harris 
221821ef73cSJim Harris 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
2225dc463f9SAlexander Motin 		err(EX_IOERR, "get log page request failed");
22375871362SJim Harris 
224821ef73cSJim Harris 	if (nvme_completion_is_error(&pt.cpl))
2255dc463f9SAlexander Motin 		errx(EX_IOERR, "get log page request returned error");
22675871362SJim Harris }
22775871362SJim Harris 
22875871362SJim Harris static void
print_log_error(const struct nvme_controller_data * cdata __unused,void * buf,uint32_t size)2298ce85adfSWarner Losh print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
23075871362SJim Harris {
23175871362SJim Harris 	int					i, nentries;
2320d787e9bSWojciech Macek 	uint16_t				status;
2330d787e9bSWojciech Macek 	uint8_t					p, sc, sct, m, dnr;
23475871362SJim Harris 	struct nvme_error_information_entry	*entry = buf;
23575871362SJim Harris 
23675871362SJim Harris 	printf("Error Information Log\n");
23775871362SJim Harris 	printf("=====================\n");
23875871362SJim Harris 
239729ee4c8SWarner Losh 	if (letoh(entry->error_count) == 0) {
24075871362SJim Harris 		printf("No error entries found\n");
24175871362SJim Harris 		return;
24275871362SJim Harris 	}
24375871362SJim Harris 
24475871362SJim Harris 	nentries = size / sizeof(struct nvme_error_information_entry);
24575871362SJim Harris 	for (i = 0; i < nentries; i++, entry++) {
246729ee4c8SWarner Losh 		if (letoh(entry->error_count) == 0)
24775871362SJim Harris 			break;
24875871362SJim Harris 
249729ee4c8SWarner Losh 		status = letoh(entry->status);
2500d787e9bSWojciech Macek 
2510d787e9bSWojciech Macek 		p = NVME_STATUS_GET_P(status);
2520d787e9bSWojciech Macek 		sc = NVME_STATUS_GET_SC(status);
2530d787e9bSWojciech Macek 		sct = NVME_STATUS_GET_SCT(status);
2540d787e9bSWojciech Macek 		m = NVME_STATUS_GET_M(status);
2550d787e9bSWojciech Macek 		dnr = NVME_STATUS_GET_DNR(status);
2560d787e9bSWojciech Macek 
25775871362SJim Harris 		printf("Entry %02d\n", i + 1);
25875871362SJim Harris 		printf("=========\n");
259729ee4c8SWarner Losh 		printf(" Error count:          %ju\n", letoh(entry->error_count));
260729ee4c8SWarner Losh 		printf(" Submission queue ID:  %u\n", letoh(entry->sqid));
261729ee4c8SWarner Losh 		printf(" Command ID:           %u\n", letoh(entry->cid));
26275871362SJim Harris 		/* TODO: Export nvme_status_string structures from kernel? */
26375871362SJim Harris 		printf(" Status:\n");
2640d787e9bSWojciech Macek 		printf("  Phase tag:           %d\n", p);
2650d787e9bSWojciech Macek 		printf("  Status code:         %d\n", sc);
2660d787e9bSWojciech Macek 		printf("  Status code type:    %d\n", sct);
2670d787e9bSWojciech Macek 		printf("  More:                %d\n", m);
2680d787e9bSWojciech Macek 		printf("  DNR:                 %d\n", dnr);
269729ee4c8SWarner Losh 		printf(" Error location:       %u\n", letoh(entry->error_location));
270729ee4c8SWarner Losh 		printf(" LBA:                  %ju\n", letoh(entry->lba));
271729ee4c8SWarner Losh 		printf(" Namespace ID:         %u\n", letoh(entry->nsid));
272729ee4c8SWarner Losh 		printf(" Vendor specific info: %u\n", letoh(entry->vendor_specific));
273729ee4c8SWarner Losh 		printf(" Transport type:       %u\n", letoh(entry->trtype));
274729ee4c8SWarner Losh 		printf(" Command specific info:%ju\n", letoh(entry->csi));
275729ee4c8SWarner Losh 		printf(" Transport specific:   %u\n", letoh(entry->ttsi));
27675871362SJim Harris 	}
27775871362SJim Harris }
27875871362SJim Harris 
2792da383a5SWarner Losh void
print_temp_K(uint16_t t)2803a194eacSWanpeng Qian print_temp_K(uint16_t t)
281dc58cdf9SWarner Losh {
282dc58cdf9SWarner Losh 	printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
283dc58cdf9SWarner Losh }
284dc58cdf9SWarner Losh 
2853a194eacSWanpeng Qian void
print_temp_C(uint16_t t)2863a194eacSWanpeng Qian print_temp_C(uint16_t t)
2873a194eacSWanpeng Qian {
2883a194eacSWanpeng Qian 	printf("%2.2f K, %u C, %3.2f F\n", (float)t + 273.15, t, (float)t * 9 / 5 + 32);
2893a194eacSWanpeng Qian }
290dc58cdf9SWarner Losh 
291dc58cdf9SWarner Losh static void
print_log_health(const struct nvme_controller_data * cdata __unused,void * buf,uint32_t size __unused)2928ce85adfSWarner Losh print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
29375871362SJim Harris {
29475871362SJim Harris 	struct nvme_health_information_page *health = buf;
29533a099d2SWarner Losh 	char cbuf[UINT128_DIG + 1];
2960d787e9bSWojciech Macek 	uint8_t	warning;
297dc58cdf9SWarner Losh 	int i;
29875871362SJim Harris 
299729ee4c8SWarner Losh 	warning = letoh(health->critical_warning);
3000d787e9bSWojciech Macek 
30175871362SJim Harris 	printf("SMART/Health Information Log\n");
30275871362SJim Harris 	printf("============================\n");
30375871362SJim Harris 
3040d787e9bSWojciech Macek 	printf("Critical Warning State:         0x%02x\n", warning);
30575871362SJim Harris 	printf(" Available spare:               %d\n",
3060d787e9bSWojciech Macek 	    !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE));
30775871362SJim Harris 	printf(" Temperature:                   %d\n",
3080d787e9bSWojciech Macek 	    !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE));
30975871362SJim Harris 	printf(" Device reliability:            %d\n",
3100d787e9bSWojciech Macek 	    !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY));
31175871362SJim Harris 	printf(" Read only:                     %d\n",
3120d787e9bSWojciech Macek 	    !!(warning & NVME_CRIT_WARN_ST_READ_ONLY));
31375871362SJim Harris 	printf(" Volatile memory backup:        %d\n",
3140d787e9bSWojciech Macek 	    !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP));
315dc58cdf9SWarner Losh 	printf("Temperature:                    ");
316729ee4c8SWarner Losh 	print_temp_K(letoh(health->temperature));
31775871362SJim Harris 	printf("Available spare:                %u\n",
318729ee4c8SWarner Losh 	    letoh(health->available_spare));
31975871362SJim Harris 	printf("Available spare threshold:      %u\n",
320729ee4c8SWarner Losh 	    letoh(health->available_spare_threshold));
32175871362SJim Harris 	printf("Percentage used:                %u\n",
322729ee4c8SWarner Losh 	    letoh(health->percentage_used));
32375871362SJim Harris 
32433a099d2SWarner Losh 	printf("Data units (512,000 byte) read: %s\n",
32533a099d2SWarner Losh 	    uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
326dc58cdf9SWarner Losh 	printf("Data units written:             %s\n",
32733a099d2SWarner Losh 	    uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
32833a099d2SWarner Losh 	printf("Host read commands:             %s\n",
32933a099d2SWarner Losh 	    uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
33033a099d2SWarner Losh 	printf("Host write commands:            %s\n",
33133a099d2SWarner Losh 	    uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
33233a099d2SWarner Losh 	printf("Controller busy time (minutes): %s\n",
33333a099d2SWarner Losh 	    uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
33433a099d2SWarner Losh 	printf("Power cycles:                   %s\n",
33533a099d2SWarner Losh 	    uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
33633a099d2SWarner Losh 	printf("Power on hours:                 %s\n",
33733a099d2SWarner Losh 	    uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
33833a099d2SWarner Losh 	printf("Unsafe shutdowns:               %s\n",
33933a099d2SWarner Losh 	    uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
34033a099d2SWarner Losh 	printf("Media errors:                   %s\n",
34133a099d2SWarner Losh 	    uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
34233a099d2SWarner Losh 	printf("No. error info log entries:     %s\n",
34333a099d2SWarner Losh 	    uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
344dc58cdf9SWarner Losh 
345729ee4c8SWarner Losh 	printf("Warning Temp Composite Time:    %d\n", letoh(health->warning_temp_time));
346729ee4c8SWarner Losh 	printf("Error Temp Composite Time:      %d\n", letoh(health->error_temp_time));
3470d787e9bSWojciech Macek 	for (i = 0; i < 8; i++) {
348729ee4c8SWarner Losh 		if (letoh(health->temp_sensor[i]) == 0)
349dc58cdf9SWarner Losh 			continue;
350dc58cdf9SWarner Losh 		printf("Temperature Sensor %d:           ", i + 1);
351729ee4c8SWarner Losh 		print_temp_K(letoh(health->temp_sensor[i]));
352dc58cdf9SWarner Losh 	}
353729ee4c8SWarner Losh 	printf("Temperature 1 Transition Count: %d\n", letoh(health->tmt1tc));
354729ee4c8SWarner Losh 	printf("Temperature 2 Transition Count: %d\n", letoh(health->tmt2tc));
355729ee4c8SWarner Losh 	printf("Total Time For Temperature 1:   %d\n", letoh(health->ttftmt1));
356729ee4c8SWarner Losh 	printf("Total Time For Temperature 2:   %d\n", letoh(health->ttftmt2));
35775871362SJim Harris }
35875871362SJim Harris 
35975871362SJim Harris static void
print_log_firmware(const struct nvme_controller_data * cdata,void * buf,uint32_t size __unused)3600d787e9bSWojciech Macek print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
36175871362SJim Harris {
36224e99dabSWarner Losh 	int				i, slots;
36375871362SJim Harris 	const char			*status;
36475871362SJim Harris 	struct nvme_firmware_page	*fw = buf;
3650d787e9bSWojciech Macek 	uint8_t				afi_slot;
3660d787e9bSWojciech Macek 	uint16_t			oacs_fw;
3670d787e9bSWojciech Macek 	uint8_t				fw_num_slots;
3680d787e9bSWojciech Macek 
369fba73a40SJohn Baldwin 	afi_slot = NVMEV(NVME_FIRMWARE_PAGE_AFI_SLOT, fw->afi);
3700d787e9bSWojciech Macek 
371fba73a40SJohn Baldwin 	oacs_fw = NVMEV(NVME_CTRLR_DATA_OACS_FIRMWARE, cdata->oacs);
372fba73a40SJohn Baldwin 	fw_num_slots = NVMEV(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, cdata->frmw);
37375871362SJim Harris 
37475871362SJim Harris 	printf("Firmware Slot Log\n");
37575871362SJim Harris 	printf("=================\n");
37675871362SJim Harris 
3770d787e9bSWojciech Macek 	if (oacs_fw == 0)
37824e99dabSWarner Losh 		slots = 1;
37924e99dabSWarner Losh 	else
3800d787e9bSWojciech Macek 		slots = MIN(fw_num_slots, MAX_FW_SLOTS);
38124e99dabSWarner Losh 
38224e99dabSWarner Losh 	for (i = 0; i < slots; i++) {
38375871362SJim Harris 		printf("Slot %d: ", i + 1);
3840d787e9bSWojciech Macek 		if (afi_slot == i + 1)
38575871362SJim Harris 			status = "  Active";
38675871362SJim Harris 		else
38775871362SJim Harris 			status = "Inactive";
38875871362SJim Harris 
3897485926eSJohn Baldwin 		if (fw->revision[i][0] == '\0')
39075871362SJim Harris 			printf("Empty\n");
39175871362SJim Harris 		else
3927485926eSJohn Baldwin 			printf("[%s] %.8s\n", status, fw->revision[i]);
39375871362SJim Harris 	}
39475871362SJim Harris }
39575871362SJim Harris 
3966c99d132SAlexander Motin static void
print_log_ns(const struct nvme_controller_data * cdata __unused,void * buf,uint32_t size __unused)3976c99d132SAlexander Motin print_log_ns(const struct nvme_controller_data *cdata __unused, void *buf,
3986c99d132SAlexander Motin     uint32_t size __unused)
3996c99d132SAlexander Motin {
4006c99d132SAlexander Motin 	struct nvme_ns_list *nsl;
4016c99d132SAlexander Motin 	u_int i;
4026c99d132SAlexander Motin 
4036c99d132SAlexander Motin 	nsl = (struct nvme_ns_list *)buf;
4046c99d132SAlexander Motin 	printf("Changed Namespace List\n");
4056c99d132SAlexander Motin 	printf("======================\n");
4066c99d132SAlexander Motin 
407acdf72f7SWarner Losh 	for (i = 0; i < nitems(nsl->ns) && letoh(nsl->ns[i]) != 0; i++) {
408acdf72f7SWarner Losh 		printf("%08x\n", letoh(nsl->ns[i]));
4096c99d132SAlexander Motin 	}
4106c99d132SAlexander Motin }
4116c99d132SAlexander Motin 
4126c99d132SAlexander Motin static void
print_log_command_effects(const struct nvme_controller_data * cdata __unused,void * buf,uint32_t size __unused)4136c99d132SAlexander Motin print_log_command_effects(const struct nvme_controller_data *cdata __unused,
4146c99d132SAlexander Motin     void *buf, uint32_t size __unused)
4156c99d132SAlexander Motin {
4166c99d132SAlexander Motin 	struct nvme_command_effects_page *ce;
4176c99d132SAlexander Motin 	u_int i;
4186c99d132SAlexander Motin 	uint32_t s;
4196c99d132SAlexander Motin 
4206c99d132SAlexander Motin 	ce = (struct nvme_command_effects_page *)buf;
4216c99d132SAlexander Motin 	printf("Commands Supported and Effects\n");
4226c99d132SAlexander Motin 	printf("==============================\n");
4236c99d132SAlexander Motin 	printf("  Command\tLBCC\tNCC\tNIC\tCCC\tCSE\tUUID\n");
4246c99d132SAlexander Motin 
4256c99d132SAlexander Motin 	for (i = 0; i < 255; i++) {
426b850caf7SWarner Losh 		s = letoh(ce->acs[i]);
427fba73a40SJohn Baldwin 		if (NVMEV(NVME_CE_PAGE_CSUP, s) == 0)
4286c99d132SAlexander Motin 			continue;
4296c99d132SAlexander Motin 		printf("Admin\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i,
430fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_LBCC, s) != 0 ? "Yes" : "No",
431fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_NCC, s) != 0 ? "Yes" : "No",
432fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_NIC, s) != 0 ? "Yes" : "No",
433fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_CCC, s) != 0 ? "Yes" : "No",
434fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_CSE, s),
435fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_UUID, s) != 0 ? "Yes" : "No");
4366c99d132SAlexander Motin 	}
4376c99d132SAlexander Motin 	for (i = 0; i < 255; i++) {
438b850caf7SWarner Losh 		s = letoh(ce->iocs[i]);
439fba73a40SJohn Baldwin 		if (NVMEV(NVME_CE_PAGE_CSUP, s) == 0)
4406c99d132SAlexander Motin 			continue;
4416c99d132SAlexander Motin 		printf("I/O\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i,
442fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_LBCC, s) != 0 ? "Yes" : "No",
443fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_NCC, s) != 0 ? "Yes" : "No",
444fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_NIC, s) != 0 ? "Yes" : "No",
445fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_CCC, s) != 0 ? "Yes" : "No",
446fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_CSE, s),
447fba73a40SJohn Baldwin 		    NVMEV(NVME_CE_PAGE_UUID, s) != 0 ? "Yes" : "No");
4486c99d132SAlexander Motin 	}
4496c99d132SAlexander Motin }
4506c99d132SAlexander Motin 
4516c99d132SAlexander Motin static void
print_log_res_notification(const struct nvme_controller_data * cdata __unused,void * buf,uint32_t size __unused)4526c99d132SAlexander Motin print_log_res_notification(const struct nvme_controller_data *cdata __unused,
4536c99d132SAlexander Motin     void *buf, uint32_t size __unused)
4546c99d132SAlexander Motin {
4556c99d132SAlexander Motin 	struct nvme_res_notification_page *rn;
4566c99d132SAlexander Motin 
4576c99d132SAlexander Motin 	rn = (struct nvme_res_notification_page *)buf;
4586c99d132SAlexander Motin 	printf("Reservation Notification\n");
4596c99d132SAlexander Motin 	printf("========================\n");
4606c99d132SAlexander Motin 
4613d28a9c6SWarner Losh 	printf("Log Page Count:                %ju\n",
4623d28a9c6SWarner Losh 	    (uintmax_t)letoh(rn->log_page_count));
4636c99d132SAlexander Motin 	printf("Log Page Type:                 ");
4643d28a9c6SWarner Losh 	switch (letoh(rn->log_page_type)) {
4656c99d132SAlexander Motin 	case 0:
4666c99d132SAlexander Motin 		printf("Empty Log Page\n");
4676c99d132SAlexander Motin 		break;
4686c99d132SAlexander Motin 	case 1:
4696c99d132SAlexander Motin 		printf("Registration Preempted\n");
4706c99d132SAlexander Motin 		break;
4716c99d132SAlexander Motin 	case 2:
4726c99d132SAlexander Motin 		printf("Reservation Released\n");
4736c99d132SAlexander Motin 		break;
4746c99d132SAlexander Motin 	case 3:
4756c99d132SAlexander Motin 		printf("Reservation Preempted\n");
4766c99d132SAlexander Motin 		break;
4776c99d132SAlexander Motin 	default:
4783d28a9c6SWarner Losh 		printf("Unknown %x\n", letoh(rn->log_page_type));
4796c99d132SAlexander Motin 		break;
4806c99d132SAlexander Motin 	};
4813d28a9c6SWarner Losh 	printf("Number of Available Log Pages: %d\n", letoh(rn->available_log_pages));
4823d28a9c6SWarner Losh 	printf("Namespace ID:                  0x%x\n", letoh(rn->nsid));
4836c99d132SAlexander Motin }
4846c99d132SAlexander Motin 
4856c99d132SAlexander Motin static void
print_log_sanitize_status(const struct nvme_controller_data * cdata __unused,void * buf,uint32_t size __unused)4866c99d132SAlexander Motin print_log_sanitize_status(const struct nvme_controller_data *cdata __unused,
4876c99d132SAlexander Motin     void *buf, uint32_t size __unused)
4886c99d132SAlexander Motin {
4896c99d132SAlexander Motin 	struct nvme_sanitize_status_page *ss;
4906c99d132SAlexander Motin 	u_int p;
49155a1679eSWarner Losh 	uint16_t sprog, sstat;
4926c99d132SAlexander Motin 
4936c99d132SAlexander Motin 	ss = (struct nvme_sanitize_status_page *)buf;
4946c99d132SAlexander Motin 	printf("Sanitize Status\n");
4956c99d132SAlexander Motin 	printf("===============\n");
4966c99d132SAlexander Motin 
49755a1679eSWarner Losh 	sprog = letoh(ss->sprog);
4986c99d132SAlexander Motin 	printf("Sanitize Progress:                   %u%% (%u/65535)\n",
49955a1679eSWarner Losh 	    (sprog * 100 + 32768) / 65536, sprog);
5006c99d132SAlexander Motin 	printf("Sanitize Status:                     ");
50155a1679eSWarner Losh 	sstat = letoh(ss->sstat);
50255a1679eSWarner Losh 	switch (NVMEV(NVME_SS_PAGE_SSTAT_STATUS, sstat)) {
5036c99d132SAlexander Motin 	case NVME_SS_PAGE_SSTAT_STATUS_NEVER:
5046c99d132SAlexander Motin 		printf("Never sanitized");
5056c99d132SAlexander Motin 		break;
5066c99d132SAlexander Motin 	case NVME_SS_PAGE_SSTAT_STATUS_COMPLETED:
5076c99d132SAlexander Motin 		printf("Completed");
5086c99d132SAlexander Motin 		break;
5096c99d132SAlexander Motin 	case NVME_SS_PAGE_SSTAT_STATUS_INPROG:
5106c99d132SAlexander Motin 		printf("In Progress");
5116c99d132SAlexander Motin 		break;
5126c99d132SAlexander Motin 	case NVME_SS_PAGE_SSTAT_STATUS_FAILED:
5136c99d132SAlexander Motin 		printf("Failed");
5146c99d132SAlexander Motin 		break;
5156c99d132SAlexander Motin 	case NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD:
5166c99d132SAlexander Motin 		printf("Completed with deallocation");
5176c99d132SAlexander Motin 		break;
5186c99d132SAlexander Motin 	default:
51955a1679eSWarner Losh 		printf("Unknown 0x%x", sstat);
5206c99d132SAlexander Motin 		break;
5216c99d132SAlexander Motin 	}
52255a1679eSWarner Losh 	p = NVMEV(NVME_SS_PAGE_SSTAT_PASSES, sstat);
5236c99d132SAlexander Motin 	if (p > 0)
5246c99d132SAlexander Motin 		printf(", %d passes", p);
52555a1679eSWarner Losh 	if (NVMEV(NVME_SS_PAGE_SSTAT_GDE, sstat) != 0)
5266c99d132SAlexander Motin 		printf(", Global Data Erased");
5276c99d132SAlexander Motin 	printf("\n");
52855a1679eSWarner Losh 	printf("Sanitize Command Dword 10:           0x%x\n", letoh(ss->scdw10));
52955a1679eSWarner Losh 	printf("Time For Overwrite:                  %u sec\n", letoh(ss->etfo));
53055a1679eSWarner Losh 	printf("Time For Block Erase:                %u sec\n", letoh(ss->etfbe));
53155a1679eSWarner Losh 	printf("Time For Crypto Erase:               %u sec\n", letoh(ss->etfce));
53255a1679eSWarner Losh 	printf("Time For Overwrite No-Deallocate:    %u sec\n", letoh(ss->etfownd));
53355a1679eSWarner Losh 	printf("Time For Block Erase No-Deallocate:  %u sec\n", letoh(ss->etfbewnd));
53455a1679eSWarner Losh 	printf("Time For Crypto Erase No-Deallocate: %u sec\n", letoh(ss->etfcewnd));
5356c99d132SAlexander Motin }
5366c99d132SAlexander Motin 
53767334019SChuck Tuffli static const char *
53867334019SChuck Tuffli self_test_res[] = {
53967334019SChuck Tuffli 	[0] = "completed without error",
54067334019SChuck Tuffli 	[1] = "aborted by a Device Self-test command",
54167334019SChuck Tuffli 	[2] = "aborted by a Controller Level Reset",
54267334019SChuck Tuffli 	[3] = "aborted due to namespace removal",
54367334019SChuck Tuffli 	[4] = "aborted due to Format NVM command",
54467334019SChuck Tuffli 	[5] = "failed due to fatal or unknown test error",
54567334019SChuck Tuffli 	[6] = "completed with an unknown segment that failed",
54667334019SChuck Tuffli 	[7] = "completed with one or more failed segments",
54767334019SChuck Tuffli 	[8] = "aborted for unknown reason",
54867334019SChuck Tuffli 	[9] = "aborted due to a sanitize operation",
54967334019SChuck Tuffli };
55067334019SChuck Tuffli static uint32_t self_test_res_max = nitems(self_test_res);
55167334019SChuck Tuffli 
55267334019SChuck Tuffli static void
print_log_self_test_status(const struct nvme_controller_data * cdata __unused,void * buf,uint32_t size __unused)55367334019SChuck Tuffli print_log_self_test_status(const struct nvme_controller_data *cdata __unused,
55467334019SChuck Tuffli     void *buf, uint32_t size __unused)
55567334019SChuck Tuffli {
55667334019SChuck Tuffli 	struct nvme_device_self_test_page *dst;
55767334019SChuck Tuffli 	uint32_t r;
55898f841efSWarner Losh 	uint16_t vs;
55967334019SChuck Tuffli 
56067334019SChuck Tuffli 	dst = buf;
56167334019SChuck Tuffli 	printf("Device Self-test Status\n");
56267334019SChuck Tuffli 	printf("=======================\n");
56367334019SChuck Tuffli 
56467334019SChuck Tuffli 	printf("Current Operation: ");
56598f841efSWarner Losh 	switch (letoh(dst->curr_operation)) {
56667334019SChuck Tuffli 	case 0x0:
56767334019SChuck Tuffli 		printf("No device self-test operation in progress\n");
56867334019SChuck Tuffli 		break;
56967334019SChuck Tuffli 	case 0x1:
57067334019SChuck Tuffli 		printf("Short device self-test operation in progress\n");
57167334019SChuck Tuffli 		break;
57267334019SChuck Tuffli 	case 0x2:
57367334019SChuck Tuffli 		printf("Extended device self-test operation in progress\n");
57467334019SChuck Tuffli 		break;
57567334019SChuck Tuffli 	case 0xe:
57667334019SChuck Tuffli 		printf("Vendor specific\n");
57767334019SChuck Tuffli 		break;
57867334019SChuck Tuffli 	default:
57998f841efSWarner Losh 		printf("Reserved (0x%x)\n", letoh(dst->curr_operation));
58067334019SChuck Tuffli 	}
58167334019SChuck Tuffli 
58298f841efSWarner Losh 	if (letoh(dst->curr_operation) != 0)
58398f841efSWarner Losh 		printf("Current Completion: %u%%\n", letoh(dst->curr_compl) & 0x7f);
58467334019SChuck Tuffli 
58567334019SChuck Tuffli 	printf("Results\n");
58667334019SChuck Tuffli 	for (r = 0; r < 20; r++) {
58767334019SChuck Tuffli 		uint64_t failing_lba;
58898f841efSWarner Losh 		uint8_t code, res, status;
58967334019SChuck Tuffli 
59098f841efSWarner Losh 		status = letoh(dst->result[r].status);
59198f841efSWarner Losh 		code = (status >> 4) & 0xf;
59298f841efSWarner Losh 		res  = status & 0xf;
59367334019SChuck Tuffli 
59467334019SChuck Tuffli 		if (res == 0xf)
59567334019SChuck Tuffli 			continue;
59667334019SChuck Tuffli 
59767334019SChuck Tuffli 		printf("[%2u] ", r);
59867334019SChuck Tuffli 		switch (code) {
59967334019SChuck Tuffli 		case 0x1:
60067334019SChuck Tuffli 			printf("Short device self-test");
60167334019SChuck Tuffli 			break;
60267334019SChuck Tuffli 		case 0x2:
60367334019SChuck Tuffli 			printf("Extended device self-test");
60467334019SChuck Tuffli 			break;
60567334019SChuck Tuffli 		case 0xe:
60667334019SChuck Tuffli 			printf("Vendor specific");
60767334019SChuck Tuffli 			break;
60867334019SChuck Tuffli 		default:
60967334019SChuck Tuffli 			printf("Reserved (0x%x)", code);
61067334019SChuck Tuffli 		}
61167334019SChuck Tuffli 		if (res < self_test_res_max)
61267334019SChuck Tuffli 			printf(" %s", self_test_res[res]);
61367334019SChuck Tuffli 		else
61467334019SChuck Tuffli 			printf(" Reserved status 0x%x", res);
61567334019SChuck Tuffli 
61667334019SChuck Tuffli 		if (res == 7)
61798f841efSWarner Losh 			printf(" starting in segment %u",
61898f841efSWarner Losh 			    letoh(dst->result[r].segment_num));
61967334019SChuck Tuffli 
62067334019SChuck Tuffli #define BIT(b) (1 << (b))
62198f841efSWarner Losh 		if (letoh(dst->result[r].valid_diag_info) & BIT(0))
62298f841efSWarner Losh 			printf(" NSID=0x%x", letoh(dst->result[r].nsid));
62398f841efSWarner Losh 		if (letoh(dst->result[r].valid_diag_info) & BIT(1)) {
62467334019SChuck Tuffli 			memcpy(&failing_lba, dst->result[r].failing_lba,
62567334019SChuck Tuffli 			    sizeof(failing_lba));
62698f841efSWarner Losh 			printf(" FLBA=0x%jx", (uintmax_t)letoh(failing_lba));
62767334019SChuck Tuffli 		}
62898f841efSWarner Losh 		if (letoh(dst->result[r].valid_diag_info) & BIT(2))
62998f841efSWarner Losh 			printf(" SCT=0x%x", letoh(dst->result[r].status_code_type));
63098f841efSWarner Losh 		if (letoh(dst->result[r].valid_diag_info) & BIT(3))
63198f841efSWarner Losh 			printf(" SC=0x%x", letoh(dst->result[r].status_code));
63267334019SChuck Tuffli #undef BIT
63398f841efSWarner Losh 		memcpy(&vs, dst->result[r].vendor_specific, sizeof(vs));
63498f841efSWarner Losh 		printf(" VENDOR_SPECIFIC=0x%x", letoh(vs));
63567334019SChuck Tuffli 		printf("\n");
63667334019SChuck Tuffli 	}
63767334019SChuck Tuffli }
63867334019SChuck Tuffli 
6399caeb430SWarner Losh /*
640ab1dd091SWarner Losh  * Table of log page printer / sizing.
641ab1dd091SWarner Losh  *
642d8fab838SWarner Losh  * Make sure you keep all the pages of one vendor together so -v help
643d8fab838SWarner Losh  * lists all the vendors pages.
644ab1dd091SWarner Losh  */
645aecd1901SWarner Losh NVME_LOGPAGE(error,
646aecd1901SWarner Losh     NVME_LOG_ERROR,			NULL,	"Drive Error Log",
647aecd1901SWarner Losh     print_log_error, 			0);
648aecd1901SWarner Losh NVME_LOGPAGE(health,
649aecd1901SWarner Losh     NVME_LOG_HEALTH_INFORMATION,	NULL,	"Health/SMART Data",
650aecd1901SWarner Losh     print_log_health, 			sizeof(struct nvme_health_information_page));
651aecd1901SWarner Losh NVME_LOGPAGE(fw,
652aecd1901SWarner Losh     NVME_LOG_FIRMWARE_SLOT,		NULL,	"Firmware Information",
653aecd1901SWarner Losh     print_log_firmware,			sizeof(struct nvme_firmware_page));
6546c99d132SAlexander Motin NVME_LOGPAGE(ns,
6556c99d132SAlexander Motin     NVME_LOG_CHANGED_NAMESPACE,		NULL,	"Changed Namespace List",
6566c99d132SAlexander Motin     print_log_ns,			sizeof(struct nvme_ns_list));
6576c99d132SAlexander Motin NVME_LOGPAGE(ce,
6586c99d132SAlexander Motin     NVME_LOG_COMMAND_EFFECT,		NULL,	"Commands Supported and Effects",
6596c99d132SAlexander Motin     print_log_command_effects,		sizeof(struct nvme_command_effects_page));
6606c99d132SAlexander Motin NVME_LOGPAGE(dst,
6616c99d132SAlexander Motin     NVME_LOG_DEVICE_SELF_TEST,		NULL,	"Device Self-test",
66267334019SChuck Tuffli     print_log_self_test_status,		sizeof(struct nvme_device_self_test_page));
6636c99d132SAlexander Motin NVME_LOGPAGE(thi,
6646c99d132SAlexander Motin     NVME_LOG_TELEMETRY_HOST_INITIATED,	NULL,	"Telemetry Host-Initiated",
6656c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6666c99d132SAlexander Motin NVME_LOGPAGE(tci,
6676c99d132SAlexander Motin     NVME_LOG_TELEMETRY_CONTROLLER_INITIATED,	NULL,	"Telemetry Controller-Initiated",
6686c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6696c99d132SAlexander Motin NVME_LOGPAGE(egi,
6706c99d132SAlexander Motin     NVME_LOG_ENDURANCE_GROUP_INFORMATION,	NULL,	"Endurance Group Information",
6716c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6726c99d132SAlexander Motin NVME_LOGPAGE(plpns,
6736c99d132SAlexander Motin     NVME_LOG_PREDICTABLE_LATENCY_PER_NVM_SET,	NULL,	"Predictable Latency Per NVM Set",
6746c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6756c99d132SAlexander Motin NVME_LOGPAGE(ple,
6766c99d132SAlexander Motin     NVME_LOG_PREDICTABLE_LATENCY_EVENT_AGGREGATE,	NULL,	"Predictable Latency Event Aggregate",
6776c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6786c99d132SAlexander Motin NVME_LOGPAGE(ana,
679c2318cf8SChuck Tuffli     NVME_LOG_ASYMMETRIC_NAMESPACE_ACCESS,	NULL,	"Asymmetric Namespace Access",
6806c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6816c99d132SAlexander Motin NVME_LOGPAGE(pel,
6826c99d132SAlexander Motin     NVME_LOG_PERSISTENT_EVENT_LOG,	NULL,	"Persistent Event Log",
6836c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6846c99d132SAlexander Motin NVME_LOGPAGE(lbasi,
6856c99d132SAlexander Motin     NVME_LOG_LBA_STATUS_INFORMATION,	NULL,	"LBA Status Information",
6866c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6876c99d132SAlexander Motin NVME_LOGPAGE(egea,
6886c99d132SAlexander Motin     NVME_LOG_ENDURANCE_GROUP_EVENT_AGGREGATE,	NULL,	"Endurance Group Event Aggregate",
6896c99d132SAlexander Motin     NULL,				DEFAULT_SIZE);
6906c99d132SAlexander Motin NVME_LOGPAGE(res_notification,
6916c99d132SAlexander Motin     NVME_LOG_RES_NOTIFICATION,		NULL,	"Reservation Notification",
6926c99d132SAlexander Motin     print_log_res_notification,		sizeof(struct nvme_res_notification_page));
6936c99d132SAlexander Motin NVME_LOGPAGE(sanitize_status,
6946c99d132SAlexander Motin     NVME_LOG_SANITIZE_STATUS,		NULL,	"Sanitize Status",
6956c99d132SAlexander Motin     print_log_sanitize_status,		sizeof(struct nvme_sanitize_status_page));
69675871362SJim Harris 
69775871362SJim Harris static void
logpage_help(void)698d8fab838SWarner Losh logpage_help(void)
699d8fab838SWarner Losh {
700f428a90aSWarner Losh 	const struct logpage_function	*f;
701d8fab838SWarner Losh 	const char 			*v;
702d8fab838SWarner Losh 
703d8fab838SWarner Losh 	fprintf(stderr, "\n");
704d8fab838SWarner Losh 	fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
705d8fab838SWarner Losh 	fprintf(stderr, "-------- ---------- ----------\n");
706f428a90aSWarner Losh 	SLIST_FOREACH(f, &logpages, link) {
707f428a90aSWarner Losh 		v = f->vendor == NULL ? "-" : f->vendor;
708f428a90aSWarner Losh 		fprintf(stderr, "0x%02x     %-10s %s\n", f->log_page, v, f->name);
709d8fab838SWarner Losh 	}
710d8fab838SWarner Losh 
7115dc463f9SAlexander Motin 	exit(EX_USAGE);
712d8fab838SWarner Losh }
713d8fab838SWarner Losh 
714a13a291aSWarner Losh static void
logpage(const struct cmd * f,int argc,char * argv[])715f634b4c1SWarner Losh logpage(const struct cmd *f, int argc, char *argv[])
71675871362SJim Harris {
717635c517aSAlexander Motin 	int				fd;
718a7bf63beSAlexander Motin 	char				*path;
719635c517aSAlexander Motin 	uint32_t			nsid, size;
72075871362SJim Harris 	void				*buf;
721f634b4c1SWarner Losh 	const struct logpage_function	*lpf;
72275871362SJim Harris 	struct nvme_controller_data	cdata;
72375871362SJim Harris 	print_fn_t			print_fn;
7240d787e9bSWojciech Macek 	uint8_t				ns_smart;
72575871362SJim Harris 
726f634b4c1SWarner Losh 	if (arg_parse(argc, argv, f))
727f634b4c1SWarner Losh 		return;
728f634b4c1SWarner Losh 	if (opt.hex && opt.binary) {
72975871362SJim Harris 		fprintf(stderr,
730f634b4c1SWarner Losh 		    "Can't specify both binary and hex\n");
731f634b4c1SWarner Losh 		arg_help(argc, argv, f);
73275871362SJim Harris 	}
733f634b4c1SWarner Losh 	if (opt.vendor != NULL && strcmp(opt.vendor, "help") == 0)
734d8fab838SWarner Losh 		logpage_help();
735f634b4c1SWarner Losh 	if (opt.page == NONE) {
736f634b4c1SWarner Losh 		fprintf(stderr, "Missing page_id (-p).\n");
737f634b4c1SWarner Losh 		arg_help(argc, argv, f);
73875871362SJim Harris 	}
7391f15d49eSAlexander Motin 	open_dev(opt.dev, &fd, 0, 1);
740a7bf63beSAlexander Motin 	get_nsid(fd, &path, &nsid);
741a7bf63beSAlexander Motin 	if (nsid == 0) {
742a7bf63beSAlexander Motin 		nsid = NVME_GLOBAL_NAMESPACE_TAG;
743a7bf63beSAlexander Motin 	} else {
744a7bf63beSAlexander Motin 		close(fd);
7451f15d49eSAlexander Motin 		open_dev(path, &fd, 0, 1);
7462528d6a3SJim Harris 	}
747a7bf63beSAlexander Motin 	free(path);
7482528d6a3SJim Harris 
7495dc463f9SAlexander Motin 	if (read_controller_data(fd, &cdata))
7505dc463f9SAlexander Motin 		errx(EX_IOERR, "Identify request failed");
751628683cbSJim Harris 
752fba73a40SJohn Baldwin 	ns_smart = NVMEV(NVME_CTRLR_DATA_LPA_NS_SMART, cdata.lpa);
7530d787e9bSWojciech Macek 
75475871362SJim Harris 	/*
755abe10d21SAndrius V 	 * The log page attributes indicate whether or not the controller
75675871362SJim Harris 	 * supports the SMART/Health information log page on a per
75775871362SJim Harris 	 * namespace basis.
75875871362SJim Harris 	 */
759a7bf63beSAlexander Motin 	if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
760f634b4c1SWarner Losh 		if (opt.page != NVME_LOG_HEALTH_INFORMATION)
7615dc463f9SAlexander Motin 			errx(EX_USAGE, "log page %d valid only at controller level",
762f634b4c1SWarner Losh 			    opt.page);
7630d787e9bSWojciech Macek 		if (ns_smart == 0)
7645dc463f9SAlexander Motin 			errx(EX_UNAVAILABLE,
7652528d6a3SJim Harris 			    "controller does not support per namespace "
7662528d6a3SJim Harris 			    "smart/health information");
76775871362SJim Harris 	}
76875871362SJim Harris 
7698ce85adfSWarner Losh 	print_fn = print_log_hex;
770cc63e8e6SWarner Losh 	size = DEFAULT_SIZE;
771f634b4c1SWarner Losh 	if (opt.binary)
772ba6da686SWarner Losh 		print_fn = print_bin;
773f634b4c1SWarner Losh 	if (!opt.binary && !opt.hex) {
77475871362SJim Harris 		/*
7755619c99fSWarner Losh 		 * See if there is a pretty print function for the specified log
7765619c99fSWarner Losh 		 * page.  If one isn't found, we just revert to the default
777e8604394SWarner Losh 		 * (print_hex). If there was a vendor specified by the user, and
7785619c99fSWarner Losh 		 * the page is vendor specific, don't match the print function
7795619c99fSWarner Losh 		 * unless the vendors match.
78075871362SJim Harris 		 */
781f634b4c1SWarner Losh 		SLIST_FOREACH(lpf, &logpages, link) {
78216091536SWarner Losh 			if (lpf->vendor != NULL && opt.vendor != NULL &&
78316091536SWarner Losh 			    strcmp(lpf->vendor, opt.vendor) != 0)
7845619c99fSWarner Losh 				continue;
785f634b4c1SWarner Losh 			if (opt.page != lpf->log_page)
7865619c99fSWarner Losh 				continue;
7876c99d132SAlexander Motin 			if (lpf->print_fn != NULL)
788f634b4c1SWarner Losh 				print_fn = lpf->print_fn;
789f634b4c1SWarner Losh 			size = lpf->size;
79075871362SJim Harris 			break;
79175871362SJim Harris 		}
79275871362SJim Harris 	}
79375871362SJim Harris 
794f634b4c1SWarner Losh 	if (opt.page == NVME_LOG_ERROR) {
79575871362SJim Harris 		size = sizeof(struct nvme_error_information_entry);
79675871362SJim Harris 		size *= (cdata.elpe + 1);
79775871362SJim Harris 	}
79875871362SJim Harris 
799cc63e8e6SWarner Losh 	/* Read the log page */
80075871362SJim Harris 	buf = get_log_buffer(size);
801*98ab7d0aSWarner Losh 	read_logpage(fd, opt.page, nsid, opt.lsp, opt.lsi, opt.rae,
802*98ab7d0aSWarner Losh 	    0, 0, 0, 0, buf, size);
8038ce85adfSWarner Losh 	print_fn(&cdata, buf, size);
80475871362SJim Harris 
80575871362SJim Harris 	close(fd);
806821ef73cSJim Harris 	exit(0);
80775871362SJim Harris }
808