xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_field.c (revision 0439b35b4c5b977fedef1ab5cbeff2c08150aba5)
1c96729eaSRobert Mustacchi /*
2c96729eaSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3c96729eaSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4c96729eaSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5c96729eaSRobert Mustacchi  * 1.0 of the CDDL.
6c96729eaSRobert Mustacchi  *
7c96729eaSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8c96729eaSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9c96729eaSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10c96729eaSRobert Mustacchi  */
11c96729eaSRobert Mustacchi 
12c96729eaSRobert Mustacchi /*
13c96729eaSRobert Mustacchi  * Copyright 2026 Oxide Computer Company
14c96729eaSRobert Mustacchi  */
15c96729eaSRobert Mustacchi 
16c96729eaSRobert Mustacchi /*
17c96729eaSRobert Mustacchi  * Logic to slice, dice, and print structured data from log pages and related
18c96729eaSRobert Mustacchi  * NVMe structures.
19c96729eaSRobert Mustacchi  */
20c96729eaSRobert Mustacchi 
21c96729eaSRobert Mustacchi #include <err.h>
22c96729eaSRobert Mustacchi #include <string.h>
23c96729eaSRobert Mustacchi #include <sys/sysmacros.h>
24c96729eaSRobert Mustacchi #include <sys/bitext.h>
25c96729eaSRobert Mustacchi #include <libcmdutils.h>
26c96729eaSRobert Mustacchi #include <sys/ilstr.h>
27c96729eaSRobert Mustacchi #include <ctype.h>
28c96729eaSRobert Mustacchi 
29c96729eaSRobert Mustacchi #include "nvmeadm.h"
30c96729eaSRobert Mustacchi 
31c96729eaSRobert Mustacchi static const nvmeadm_log_field_info_t *field_log_map[] = {
32ffd3f184SRobert Mustacchi 	&suplog_field_info,
33ffd3f184SRobert Mustacchi 	&supcmd_field_info,
34ffd3f184SRobert Mustacchi 	&supmicmd_field_info,
35ffd3f184SRobert Mustacchi 	&supfeat_field_info,
3656566d3aSRobert Mustacchi 	&phyeye_field_info,
37f9747139SRobert Mustacchi 	&kioxia_vul_extsmart_field_info,
38a3f161aeSRobert Mustacchi 	&micron_vul_extsmart_field_info,
3956566d3aSRobert Mustacchi 	&ocp_vul_smart_field_info,
4056566d3aSRobert Mustacchi 	&ocp_vul_errrec_field_info,
4156566d3aSRobert Mustacchi 	&ocp_vul_devcap_field_info,
4256566d3aSRobert Mustacchi 	&ocp_vul_unsup_field_info,
4356566d3aSRobert Mustacchi 	&ocp_vul_telstr_field_info,
44*0439b35bSRobert Mustacchi 	&solidigm_vul_power_field_info,
45*0439b35bSRobert Mustacchi 	&solidigm_vul_temp_field_info,
46f9747139SRobert Mustacchi 	&wdc_vul_cusmart_field_info,
47f7dd6a46SRobert Mustacchi 	&wdc_vul_eol_field_info,
48f7dd6a46SRobert Mustacchi 	&wdc_vul_power_field_info
49c96729eaSRobert Mustacchi };
50c96729eaSRobert Mustacchi 
51c96729eaSRobert Mustacchi typedef struct {
52c96729eaSRobert Mustacchi 	const char *fo_base;
53c96729eaSRobert Mustacchi 	const char *fo_short;
54c96729eaSRobert Mustacchi 	const char *fo_desc;
55c96729eaSRobert Mustacchi 	char fo_val[256];
56c96729eaSRobert Mustacchi 	char fo_hval[256];
57c96729eaSRobert Mustacchi 	uint32_t fo_off;
58c96729eaSRobert Mustacchi 	uint32_t fo_bitoff;
59c96729eaSRobert Mustacchi 	uint32_t fo_len;
60c96729eaSRobert Mustacchi 	uint32_t fo_bitlen;
61c96729eaSRobert Mustacchi } field_ofmt_t;
62c96729eaSRobert Mustacchi 
63c96729eaSRobert Mustacchi typedef enum {
64c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_SHORT,
65c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_DESC,
66c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_VALUE,
67c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_HUMAN,
68c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_BYTEOFF,
69c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_BITOFF,
70c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_BYTELEN,
71c96729eaSRobert Mustacchi 	NVMEADM_FIELD_OT_BITLEN
72c96729eaSRobert Mustacchi } phyeye_otype_t;
73c96729eaSRobert Mustacchi 
74c96729eaSRobert Mustacchi static boolean_t
nvmeadm_field_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)75c96729eaSRobert Mustacchi nvmeadm_field_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
76c96729eaSRobert Mustacchi {
77c96729eaSRobert Mustacchi 	size_t ret;
78c96729eaSRobert Mustacchi 	field_ofmt_t *fo = ofarg->ofmt_cbarg;
79c96729eaSRobert Mustacchi 
80c96729eaSRobert Mustacchi 	switch (ofarg->ofmt_id) {
81c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_SHORT:
82c96729eaSRobert Mustacchi 		if (fo->fo_base == NULL) {
83c96729eaSRobert Mustacchi 			ret = strlcat(buf, fo->fo_short, buflen);
84c96729eaSRobert Mustacchi 		} else {
85c96729eaSRobert Mustacchi 			ret = snprintf(buf, buflen, "%s.%s", fo->fo_base,
86c96729eaSRobert Mustacchi 			    fo->fo_short);
87c96729eaSRobert Mustacchi 		}
88c96729eaSRobert Mustacchi 		break;
89c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_DESC:
90c96729eaSRobert Mustacchi 		ret = strlcat(buf, fo->fo_desc, buflen);
91c96729eaSRobert Mustacchi 		break;
92c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_VALUE:
93c96729eaSRobert Mustacchi 		if (fo->fo_val[0] == '\0')
94c96729eaSRobert Mustacchi 			return (B_FALSE);
95c96729eaSRobert Mustacchi 
96c96729eaSRobert Mustacchi 		ret = strlcat(buf, fo->fo_val, buflen);
97c96729eaSRobert Mustacchi 		break;
98c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_HUMAN:
99c96729eaSRobert Mustacchi 		if (fo->fo_hval[0] != '\0') {
100c96729eaSRobert Mustacchi 			ret = strlcat(buf, fo->fo_hval, buflen);
101c96729eaSRobert Mustacchi 		} else {
102c96729eaSRobert Mustacchi 			ret = strlcat(buf, fo->fo_val, buflen);
103c96729eaSRobert Mustacchi 		}
104c96729eaSRobert Mustacchi 		break;
105c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_BYTEOFF:
106c96729eaSRobert Mustacchi 		ret = snprintf(buf, buflen, "%u", fo->fo_off);
107c96729eaSRobert Mustacchi 		break;
108c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_BITOFF:
109c96729eaSRobert Mustacchi 		ret = snprintf(buf, buflen, "%u", fo->fo_bitoff);
110c96729eaSRobert Mustacchi 		break;
111c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_BYTELEN:
112c96729eaSRobert Mustacchi 		ret = snprintf(buf, buflen, "%u", fo->fo_len);
113c96729eaSRobert Mustacchi 		break;
114c96729eaSRobert Mustacchi 	case NVMEADM_FIELD_OT_BITLEN:
115c96729eaSRobert Mustacchi 		ret = snprintf(buf, buflen, "%u", fo->fo_bitlen);
116c96729eaSRobert Mustacchi 		break;
117c96729eaSRobert Mustacchi 	default:
118c96729eaSRobert Mustacchi 		abort();
119c96729eaSRobert Mustacchi 	}
120c96729eaSRobert Mustacchi 
121c96729eaSRobert Mustacchi 	return (ret < buflen);
122c96729eaSRobert Mustacchi }
123c96729eaSRobert Mustacchi 
124c96729eaSRobert Mustacchi const ofmt_field_t nvmeadm_field_ofmt[] = {
125c96729eaSRobert Mustacchi 	{ "SHORT", 30, NVMEADM_FIELD_OT_SHORT, nvmeadm_field_ofmt_cb },
126c96729eaSRobert Mustacchi 	{ "DESC", 30, NVMEADM_FIELD_OT_DESC, nvmeadm_field_ofmt_cb },
127c96729eaSRobert Mustacchi 	{ "VALUE", 20, NVMEADM_FIELD_OT_VALUE, nvmeadm_field_ofmt_cb },
128c96729eaSRobert Mustacchi 	{ "HUMAN", 20, NVMEADM_FIELD_OT_HUMAN, nvmeadm_field_ofmt_cb },
129c96729eaSRobert Mustacchi 	{ "OFFSET", 8, NVMEADM_FIELD_OT_BYTEOFF, nvmeadm_field_ofmt_cb },
130c96729eaSRobert Mustacchi 	{ "BITOFF", 8, NVMEADM_FIELD_OT_BITOFF, nvmeadm_field_ofmt_cb },
131c96729eaSRobert Mustacchi 	{ "LENGTH", 8, NVMEADM_FIELD_OT_BYTELEN, nvmeadm_field_ofmt_cb },
132c96729eaSRobert Mustacchi 	{ "BITLEN", 8, NVMEADM_FIELD_OT_BITLEN, nvmeadm_field_ofmt_cb },
133c96729eaSRobert Mustacchi 	{ NULL, 0, 0, NULL }
134c96729eaSRobert Mustacchi };
135c96729eaSRobert Mustacchi 
136c96729eaSRobert Mustacchi /*
137c96729eaSRobert Mustacchi  * We've been asked to apply a filter that matches on a field which may be a
138c96729eaSRobert Mustacchi  * top-level field or a nested one. For example, consider eom.odp.pefp. When
139c96729eaSRobert Mustacchi  * we're in parsable mode we only ever allow for absolute matches. This ensures
140c96729eaSRobert Mustacchi  * that if we add more fields to something that it doesn't end up changing the
141c96729eaSRobert Mustacchi  * output the user gets. However, if we're in a non-parsable mode then we'll
142c96729eaSRobert Mustacchi  * allow partial matches with a section. That is, 'eom' will match anything
143c96729eaSRobert Mustacchi  * starting with 'eom'. 'eom.odp' will match all fields with 'eom.odp'. Partial
144c96729eaSRobert Mustacchi  * matches within a field will not work, e.g. 'eom.o' would not match 'eom.odp'.
145c96729eaSRobert Mustacchi  *
146c96729eaSRobert Mustacchi  * However, a more specific match should match its parent. So, we want
147c96729eaSRobert Mustacchi  * 'eom.odp.pefp' to match 'eom' and 'eom.odp'. Even if we match those, we don't
148c96729eaSRobert Mustacchi  * count them as a use of the filter. Only exact matches count. This ensures
149c96729eaSRobert Mustacchi  * that if someone makes a typo or uses a non-existent field, say 'eom.foobar',
150c96729eaSRobert Mustacchi  * which does match 'eom', it still will generate an error.
151c96729eaSRobert Mustacchi  */
152c96729eaSRobert Mustacchi bool
nvmeadm_field_filter(nvmeadm_field_print_t * print,const char * base,const char * shrt)153c96729eaSRobert Mustacchi nvmeadm_field_filter(nvmeadm_field_print_t *print, const char *base,
154c96729eaSRobert Mustacchi     const char *shrt)
155c96729eaSRobert Mustacchi {
156c96729eaSRobert Mustacchi 	char buf[PATH_MAX];
157c96729eaSRobert Mustacchi 	const char *check;
158c96729eaSRobert Mustacchi 	bool match = false;
159c96729eaSRobert Mustacchi 
160c96729eaSRobert Mustacchi 	if (print->fp_nfilts == 0) {
161c96729eaSRobert Mustacchi 		return (true);
162c96729eaSRobert Mustacchi 	}
163c96729eaSRobert Mustacchi 
164c96729eaSRobert Mustacchi 	if (base != NULL && shrt != NULL) {
165c96729eaSRobert Mustacchi 		(void) snprintf(buf, sizeof (buf), "%s.%s", base, shrt);
166c96729eaSRobert Mustacchi 		check = buf;
167c96729eaSRobert Mustacchi 	} else if (base == NULL) {
168c96729eaSRobert Mustacchi 		VERIFY3P(shrt, !=, NULL);
169c96729eaSRobert Mustacchi 		check = shrt;
170c96729eaSRobert Mustacchi 	} else if (shrt == NULL) {
171c96729eaSRobert Mustacchi 		VERIFY3P(base, !=, NULL);
172c96729eaSRobert Mustacchi 		check = base;
173c96729eaSRobert Mustacchi 	} else {
174c96729eaSRobert Mustacchi 		abort();
175c96729eaSRobert Mustacchi 	}
176c96729eaSRobert Mustacchi 
177c96729eaSRobert Mustacchi 	/*
178c96729eaSRobert Mustacchi 	 * Always check all filters so that way a user specifying the same thing
179c96729eaSRobert Mustacchi 	 * multiple times doesn't end up in trouble.
180c96729eaSRobert Mustacchi 	 */
181c96729eaSRobert Mustacchi 	for (int i = 0; i < print->fp_nfilts; i++) {
182c96729eaSRobert Mustacchi 		nvmeadm_field_filt_t *f = &print->fp_filts[i];
183c96729eaSRobert Mustacchi 
184c96729eaSRobert Mustacchi 		if (strcmp(check, f->nff_str) == 0) {
185c96729eaSRobert Mustacchi 			f->nff_used = true;
186c96729eaSRobert Mustacchi 			match = true;
187c96729eaSRobert Mustacchi 			continue;
188c96729eaSRobert Mustacchi 		}
189c96729eaSRobert Mustacchi 
190c96729eaSRobert Mustacchi 		if (print->fp_ofmt != NULL) {
191c96729eaSRobert Mustacchi 			continue;
192c96729eaSRobert Mustacchi 		}
193c96729eaSRobert Mustacchi 
194c96729eaSRobert Mustacchi 		size_t len = strlen(check);
195c96729eaSRobert Mustacchi 		if (len >= f->nff_len) {
196c96729eaSRobert Mustacchi 			if (strncmp(check, f->nff_str, f->nff_len) == 0 &&
197c96729eaSRobert Mustacchi 			    check[f->nff_len] == '.') {
198c96729eaSRobert Mustacchi 				match = true;
199c96729eaSRobert Mustacchi 				continue;
200c96729eaSRobert Mustacchi 			}
201c96729eaSRobert Mustacchi 		} else {
202c96729eaSRobert Mustacchi 			if (strncmp(check, f->nff_str, len) == 0 &&
203c96729eaSRobert Mustacchi 			    f->nff_str[len] == '.') {
204c96729eaSRobert Mustacchi 				match = true;
205c96729eaSRobert Mustacchi 				continue;
206c96729eaSRobert Mustacchi 			}
207c96729eaSRobert Mustacchi 		}
208c96729eaSRobert Mustacchi 	}
209c96729eaSRobert Mustacchi 
210c96729eaSRobert Mustacchi 	return (match);
211c96729eaSRobert Mustacchi }
212c96729eaSRobert Mustacchi 
213c96729eaSRobert Mustacchi static void
field_print_one_bit(nvmeadm_field_print_t * print,field_ofmt_t * ofarg,nvmeadm_field_type_t type,uint32_t level)214c96729eaSRobert Mustacchi field_print_one_bit(nvmeadm_field_print_t *print, field_ofmt_t *ofarg,
215c96729eaSRobert Mustacchi     nvmeadm_field_type_t type, uint32_t level)
216c96729eaSRobert Mustacchi {
217c96729eaSRobert Mustacchi 	uint32_t indent;
218c96729eaSRobert Mustacchi 
219c96729eaSRobert Mustacchi 	if (!nvmeadm_field_filter(print, ofarg->fo_base, ofarg->fo_short)) {
220c96729eaSRobert Mustacchi 		return;
221c96729eaSRobert Mustacchi 	}
222c96729eaSRobert Mustacchi 
223c96729eaSRobert Mustacchi 	if (print->fp_ofmt != NULL) {
224c96729eaSRobert Mustacchi 		ofmt_print(print->fp_ofmt, ofarg);
225c96729eaSRobert Mustacchi 		return;
226c96729eaSRobert Mustacchi 	}
227c96729eaSRobert Mustacchi 
228c96729eaSRobert Mustacchi 	indent = 4 + print->fp_indent * 2;
229c96729eaSRobert Mustacchi 	if (level > 1) {
230c96729eaSRobert Mustacchi 		indent += (level - 1) * 7;
231c96729eaSRobert Mustacchi 	}
232c96729eaSRobert Mustacchi 
233c96729eaSRobert Mustacchi 	(void) printf("%*s|--> %s: ", indent, "", ofarg->fo_desc);
234c96729eaSRobert Mustacchi 	switch (type) {
235c96729eaSRobert Mustacchi 	case NVMEADM_FT_STRMAP:
236c96729eaSRobert Mustacchi 		(void) printf("%s (%s)\n", ofarg->fo_hval,
237c96729eaSRobert Mustacchi 		    ofarg->fo_val);
238c96729eaSRobert Mustacchi 		break;
239c96729eaSRobert Mustacchi 	case NVMEADM_FT_BITS:
240c96729eaSRobert Mustacchi 		(void) printf("%s\n", ofarg->fo_val);
241c96729eaSRobert Mustacchi 		break;
242c96729eaSRobert Mustacchi 	case NVMEADM_FT_HEX:
243c96729eaSRobert Mustacchi 	case NVMEADM_FT_PERCENT:
244c96729eaSRobert Mustacchi 		(void) printf("%s\n", ofarg->fo_hval);
245c96729eaSRobert Mustacchi 		break;
246c96729eaSRobert Mustacchi 	default:
247c96729eaSRobert Mustacchi 		abort();
248c96729eaSRobert Mustacchi 	}
249c96729eaSRobert Mustacchi }
250c96729eaSRobert Mustacchi 
251c96729eaSRobert Mustacchi /*
252c96729eaSRobert Mustacchi  * Extract what should be a series of printable ASCII bytes, but don't assume
253c96729eaSRobert Mustacchi  * that they are. Similarly, assume we need to trim any trailing spaces in the
254c96729eaSRobert Mustacchi  * field. If anything in here is not ASCII, we'll escape it.
255c96729eaSRobert Mustacchi  */
256c96729eaSRobert Mustacchi static void
field_extract_ascii(const void * data,nvmeadm_field_type_t type,size_t len,size_t off,field_ofmt_t * ofarg)257c96729eaSRobert Mustacchi field_extract_ascii(const void *data, nvmeadm_field_type_t type, size_t len,
258c96729eaSRobert Mustacchi     size_t off, field_ofmt_t *ofarg)
259c96729eaSRobert Mustacchi {
260c96729eaSRobert Mustacchi 	bool zpad = type == NVMEADM_FT_ASCIIZ;
261c96729eaSRobert Mustacchi 	const uint8_t *u8p = data + off;
262c96729eaSRobert Mustacchi 
263c96729eaSRobert Mustacchi 	while (len > 0) {
264c96729eaSRobert Mustacchi 		if ((zpad && u8p[len - 1] == '\0') ||
265c96729eaSRobert Mustacchi 		    (!zpad && u8p[len - 1] == ' ')) {
266c96729eaSRobert Mustacchi 			len--;
267c96729eaSRobert Mustacchi 		} else {
268c96729eaSRobert Mustacchi 			break;
269c96729eaSRobert Mustacchi 		}
270c96729eaSRobert Mustacchi 	}
271c96729eaSRobert Mustacchi 
272c96729eaSRobert Mustacchi 	if (len == 0)
273c96729eaSRobert Mustacchi 		return;
274c96729eaSRobert Mustacchi 
275c96729eaSRobert Mustacchi 	ilstr_t ilstr;
276c96729eaSRobert Mustacchi 	ilstr_init_prealloc(&ilstr, ofarg->fo_val, sizeof (ofarg->fo_val));
277c96729eaSRobert Mustacchi 
278c96729eaSRobert Mustacchi 	for (size_t i = 0; i < len; i++) {
279c96729eaSRobert Mustacchi 		if (isascii(u8p[i]) && isprint(u8p[i])) {
280c96729eaSRobert Mustacchi 			ilstr_append_char(&ilstr, u8p[i]);
281c96729eaSRobert Mustacchi 		} else {
282c96729eaSRobert Mustacchi 			ilstr_aprintf(&ilstr, "\\x%02x", u8p[i]);
283c96729eaSRobert Mustacchi 		}
284c96729eaSRobert Mustacchi 	}
285c96729eaSRobert Mustacchi 
286c96729eaSRobert Mustacchi 	if (ilstr_errno(&ilstr) != ILSTR_ERROR_OK) {
287c96729eaSRobert Mustacchi 		errx(-1, "failed to construct internal string for field %s: "
288c96729eaSRobert Mustacchi 		    "0x%x", ofarg->fo_desc, ilstr_errno(&ilstr));
289c96729eaSRobert Mustacchi 	}
290c96729eaSRobert Mustacchi 
291c96729eaSRobert Mustacchi 	(void) memcpy(ofarg->fo_hval, ofarg->fo_val, ilstr_len(&ilstr) + 1);
292c96729eaSRobert Mustacchi 	ilstr_fini(&ilstr);
293c96729eaSRobert Mustacchi }
294c96729eaSRobert Mustacchi 
295c96729eaSRobert Mustacchi static uint64_t
nvmeadm_apply_addend(uint64_t val,const nvmeadm_field_addend_t * add)296c96729eaSRobert Mustacchi nvmeadm_apply_addend(uint64_t val, const nvmeadm_field_addend_t *add)
297c96729eaSRobert Mustacchi {
298c96729eaSRobert Mustacchi 	if (add->nfa_shift > 0) {
299c96729eaSRobert Mustacchi 		val <<= add->nfa_shift;
300c96729eaSRobert Mustacchi 	}
301c96729eaSRobert Mustacchi 
302c96729eaSRobert Mustacchi 	val += add->nfa_addend;
303c96729eaSRobert Mustacchi 	return (val);
304c96729eaSRobert Mustacchi }
305c96729eaSRobert Mustacchi 
306c96729eaSRobert Mustacchi static void
nvmeadm_field_bit_extract(const nvmeadm_field_bit_t * bit,uint64_t fval,field_ofmt_t * ofarg,uint64_t * bp)307c96729eaSRobert Mustacchi nvmeadm_field_bit_extract(const nvmeadm_field_bit_t *bit, uint64_t fval,
308c96729eaSRobert Mustacchi     field_ofmt_t *ofarg, uint64_t *bp)
309c96729eaSRobert Mustacchi {
310c96729eaSRobert Mustacchi 	VERIFY3U(bit->nfb_hibit, <, 64);
311c96729eaSRobert Mustacchi 	uint64_t bval = bitx64(fval, bit->nfb_hibit, bit->nfb_lowbit);
312c96729eaSRobert Mustacchi 	if (bp != NULL)
313c96729eaSRobert Mustacchi 		*bp = bval;
314c96729eaSRobert Mustacchi 
315c96729eaSRobert Mustacchi 	(void) snprintf(ofarg->fo_val, sizeof (ofarg->fo_val), "0x%" PRIx64,
316c96729eaSRobert Mustacchi 	    bval);
317c96729eaSRobert Mustacchi 	switch (bit->nfb_type) {
318c96729eaSRobert Mustacchi 	case NVMEADM_FT_HEX:
319c96729eaSRobert Mustacchi 		/*
320c96729eaSRobert Mustacchi 		 * The "human" string is the version with the addend applied.
321c96729eaSRobert Mustacchi 		 */
322c96729eaSRobert Mustacchi 		bval = nvmeadm_apply_addend(bval, &bit->nfb_addend);
323c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_hval, sizeof (ofarg->fo_hval),
324c96729eaSRobert Mustacchi 		    "0x%" PRIx64, bval);
325c96729eaSRobert Mustacchi 		break;
326c96729eaSRobert Mustacchi 	case NVMEADM_FT_UNIT:
327c96729eaSRobert Mustacchi 		bval = nvmeadm_apply_addend(bval, &bit->nfb_addend);
328c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_hval, sizeof (ofarg->fo_hval),
329c96729eaSRobert Mustacchi 		    "%" PRIu64 " %s", bval, bit->nfb_addend.nfa_unit);
330c96729eaSRobert Mustacchi 		break;
331c96729eaSRobert Mustacchi 	case NVMEADM_FT_BITS:
332c96729eaSRobert Mustacchi 		/* No human string for this. */
333c96729eaSRobert Mustacchi 		break;
334c96729eaSRobert Mustacchi 	case NVMEADM_FT_STRMAP:
335c96729eaSRobert Mustacchi 		if (bval < ARRAY_SIZE(bit->nfb_strs) &&
336c96729eaSRobert Mustacchi 		    bit->nfb_strs[bval] != NULL) {
337c96729eaSRobert Mustacchi 			(void) strlcpy(ofarg->fo_hval, bit->nfb_strs[bval],
338c96729eaSRobert Mustacchi 			    sizeof (ofarg->fo_hval));
339c96729eaSRobert Mustacchi 		} else {
340c96729eaSRobert Mustacchi 			(void) strlcpy(ofarg->fo_hval, "reserved",
341c96729eaSRobert Mustacchi 			    sizeof (ofarg->fo_hval));
342c96729eaSRobert Mustacchi 		}
343c96729eaSRobert Mustacchi 		break;
344c96729eaSRobert Mustacchi 	case NVMEADM_FT_PERCENT:
345c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_hval, sizeof (ofarg->fo_hval), "%u%%",
346c96729eaSRobert Mustacchi 		    bval);
347c96729eaSRobert Mustacchi 		break;
348c96729eaSRobert Mustacchi 	case NVMEADM_FT_BYTES:
349c96729eaSRobert Mustacchi 		bval = nvmeadm_apply_addend(bval, &bit->nfb_addend);
350c96729eaSRobert Mustacchi 		nicenum(bval, ofarg->fo_hval, sizeof (ofarg->fo_hval));
351c96729eaSRobert Mustacchi 		break;
352c96729eaSRobert Mustacchi 	case NVMEADM_FT_GUID:
353c96729eaSRobert Mustacchi 		/* GUIDs don't fit inside the 8 byte limit we have */
354c96729eaSRobert Mustacchi 		abort();
355c96729eaSRobert Mustacchi 	case NVMEADM_FT_ASCII:
356c96729eaSRobert Mustacchi 	case NVMEADM_FT_ASCIIZ:
357c96729eaSRobert Mustacchi 		/* We should handle this once it shows up here */
358c96729eaSRobert Mustacchi 		abort();
359c96729eaSRobert Mustacchi 	case NVMEADM_FT_CONTAINER:
360c96729eaSRobert Mustacchi 		/* Containers are only used at the field level right now. */
361c96729eaSRobert Mustacchi 		abort();
362c96729eaSRobert Mustacchi 	}
363c96729eaSRobert Mustacchi }
364c96729eaSRobert Mustacchi 
365c96729eaSRobert Mustacchi static void
field_print_bits(nvmeadm_field_print_t * print,const nvmeadm_field_bit_t * bits,size_t nbits,uint64_t val,const char * base,size_t off,size_t bitoff,uint32_t level)366c96729eaSRobert Mustacchi field_print_bits(nvmeadm_field_print_t *print, const nvmeadm_field_bit_t *bits,
367c96729eaSRobert Mustacchi     size_t nbits, uint64_t val, const char *base, size_t off, size_t bitoff,
368c96729eaSRobert Mustacchi     uint32_t level)
369c96729eaSRobert Mustacchi {
370c96729eaSRobert Mustacchi 	for (size_t i = 0; i < nbits; i++) {
371c96729eaSRobert Mustacchi 		uint8_t blen = bits[i].nfb_hibit - bits[i].nfb_lowbit + 1;
372c96729eaSRobert Mustacchi 		field_ofmt_t ofarg = { 0 };
373c96729eaSRobert Mustacchi 
374c96729eaSRobert Mustacchi 		/*
375c96729eaSRobert Mustacchi 		 * See if this field is one that is meaningful to this revision
376c96729eaSRobert Mustacchi 		 * of the log page or controller. If the version is NULL or the
377c96729eaSRobert Mustacchi 		 * revision is 0 in the field, then there is nothing to check.
378c96729eaSRobert Mustacchi 		 * While most fields add something in a new revision, a few also
379c96729eaSRobert Mustacchi 		 * change things, so we also check for a max revision as well.
380c96729eaSRobert Mustacchi 		 */
381c96729eaSRobert Mustacchi 		if (bits[i].nfb_rev != 0 && bits[i].nfb_rev > print->fp_rev) {
382c96729eaSRobert Mustacchi 			continue;
383c96729eaSRobert Mustacchi 		}
384c96729eaSRobert Mustacchi 
385c96729eaSRobert Mustacchi 		if (bits[i].nfb_maxrev != 0 && print->fp_rev >
386c96729eaSRobert Mustacchi 		    bits[i].nfb_maxrev) {
387c96729eaSRobert Mustacchi 			continue;
388c96729eaSRobert Mustacchi 		}
389c96729eaSRobert Mustacchi 
390c96729eaSRobert Mustacchi 		if (bits[i].nfb_vers != NULL && print->fp_vers != NULL &&
391c96729eaSRobert Mustacchi 		    !nvme_vers_atleast(print->fp_vers, bits[i].nfb_vers)) {
392c96729eaSRobert Mustacchi 			continue;
393c96729eaSRobert Mustacchi 		}
394c96729eaSRobert Mustacchi 
395c96729eaSRobert Mustacchi 		ofarg.fo_base = base;
396c96729eaSRobert Mustacchi 		ofarg.fo_short = bits[i].nfb_short;
397c96729eaSRobert Mustacchi 		ofarg.fo_desc = bits[i].nfb_desc;
398c96729eaSRobert Mustacchi 		ofarg.fo_off = off + (bitoff + bits[i].nfb_lowbit) / NBBY;
399c96729eaSRobert Mustacchi 		ofarg.fo_bitoff = (bitoff + bits[i].nfb_lowbit) % NBBY;
400c96729eaSRobert Mustacchi 		ofarg.fo_len = blen / NBBY;
401c96729eaSRobert Mustacchi 		ofarg.fo_bitlen = blen % NBBY;
402c96729eaSRobert Mustacchi 
403c96729eaSRobert Mustacchi 		uint64_t bit_val;
404c96729eaSRobert Mustacchi 		nvmeadm_field_bit_extract(&bits[i], val, &ofarg, &bit_val);
405c96729eaSRobert Mustacchi 
406c96729eaSRobert Mustacchi 		field_print_one_bit(print, &ofarg, bits[i].nfb_type, level);
407c96729eaSRobert Mustacchi 
408c96729eaSRobert Mustacchi 		if (bits[i].nfb_type == NVMEADM_FT_BITS) {
409c96729eaSRobert Mustacchi 			char buf[256];
410c96729eaSRobert Mustacchi 
411c96729eaSRobert Mustacchi 			(void) snprintf(buf, sizeof (buf), "%s.%s", base,
412c96729eaSRobert Mustacchi 			    bits[i].nfb_short);
413c96729eaSRobert Mustacchi 			field_print_bits(print, bits[i].nfb_bits,
414c96729eaSRobert Mustacchi 			    bits[i].nfb_nbits, bit_val, buf, ofarg.fo_off,
415c96729eaSRobert Mustacchi 			    ofarg.fo_bitoff, level + 1);
416c96729eaSRobert Mustacchi 		}
417c96729eaSRobert Mustacchi 	}
418c96729eaSRobert Mustacchi }
419c96729eaSRobert Mustacchi 
420c96729eaSRobert Mustacchi static void
field_print_one(nvmeadm_field_print_t * print,field_ofmt_t * ofarg,nvmeadm_field_type_t type)421c96729eaSRobert Mustacchi field_print_one(nvmeadm_field_print_t *print, field_ofmt_t *ofarg,
422c96729eaSRobert Mustacchi     nvmeadm_field_type_t type)
423c96729eaSRobert Mustacchi {
424c96729eaSRobert Mustacchi 	if (!nvmeadm_field_filter(print, ofarg->fo_base, ofarg->fo_short)) {
425c96729eaSRobert Mustacchi 		return;
426c96729eaSRobert Mustacchi 	}
427c96729eaSRobert Mustacchi 
428c96729eaSRobert Mustacchi 	if (print->fp_ofmt != NULL) {
429c96729eaSRobert Mustacchi 		if (type == NVMEADM_FT_CONTAINER)
430c96729eaSRobert Mustacchi 			return;
431c96729eaSRobert Mustacchi 		ofmt_print(print->fp_ofmt, ofarg);
432c96729eaSRobert Mustacchi 		return;
433c96729eaSRobert Mustacchi 	}
434c96729eaSRobert Mustacchi 
435c96729eaSRobert Mustacchi 	uint_t indent = 2 + print->fp_indent * 2;
436c96729eaSRobert Mustacchi 	(void) printf("%*s%s:", indent, "", ofarg->fo_desc);
437c96729eaSRobert Mustacchi 	switch (type) {
438c96729eaSRobert Mustacchi 	case NVMEADM_FT_BITS:
439c96729eaSRobert Mustacchi 		(void) printf(" %s\n", ofarg->fo_val);
440c96729eaSRobert Mustacchi 		break;
441c96729eaSRobert Mustacchi 	case NVMEADM_FT_STRMAP:
442c96729eaSRobert Mustacchi 		(void) printf(" %s (%s)\n", ofarg->fo_hval, ofarg->fo_val);
443c96729eaSRobert Mustacchi 		break;
444c96729eaSRobert Mustacchi 	case NVMEADM_FT_HEX:
445c96729eaSRobert Mustacchi 	case NVMEADM_FT_UNIT:
446c96729eaSRobert Mustacchi 	case NVMEADM_FT_BYTES:
447c96729eaSRobert Mustacchi 	case NVMEADM_FT_PERCENT:
448c96729eaSRobert Mustacchi 	case NVMEADM_FT_GUID:
449c96729eaSRobert Mustacchi 	case NVMEADM_FT_ASCII:
450c96729eaSRobert Mustacchi 	case NVMEADM_FT_ASCIIZ:
451c96729eaSRobert Mustacchi 		(void) printf(" %s\n", ofarg->fo_hval);
452c96729eaSRobert Mustacchi 		break;
453c96729eaSRobert Mustacchi 	case NVMEADM_FT_CONTAINER:
454c96729eaSRobert Mustacchi 		(void) printf("\n");
455c96729eaSRobert Mustacchi 		break;
456c96729eaSRobert Mustacchi 	}
457c96729eaSRobert Mustacchi }
458c96729eaSRobert Mustacchi 
459c96729eaSRobert Mustacchi /*
460c96729eaSRobert Mustacchi  * Extract the u128 from where we are right now.
461c96729eaSRobert Mustacchi  */
462c96729eaSRobert Mustacchi static void
nvmeadm_field_extract_u128(const nvmeadm_field_t * field,const void * data,field_ofmt_t * ofarg)463c96729eaSRobert Mustacchi nvmeadm_field_extract_u128(const nvmeadm_field_t *field, const void *data,
464c96729eaSRobert Mustacchi     field_ofmt_t *ofarg)
465c96729eaSRobert Mustacchi {
466c96729eaSRobert Mustacchi 	nvme_uint128_t u128;
467c96729eaSRobert Mustacchi 	const uint8_t *u8p;
468c96729eaSRobert Mustacchi 
469c96729eaSRobert Mustacchi 	(void) memcpy(&u128, data + field->nf_off, sizeof (u128));
470c96729eaSRobert Mustacchi 
471c96729eaSRobert Mustacchi 	if (u128.hi == 0) {
472c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_val, sizeof (ofarg->fo_val), "0x%x",
473c96729eaSRobert Mustacchi 		    u128.lo);
474c96729eaSRobert Mustacchi 	} else {
475c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_val, sizeof (ofarg->fo_val),
476c96729eaSRobert Mustacchi 		    "0x%x%016x", u128.hi, u128.lo);
477c96729eaSRobert Mustacchi 	}
478c96729eaSRobert Mustacchi 
479c96729eaSRobert Mustacchi 	switch (field->nf_type) {
480c96729eaSRobert Mustacchi 	case NVMEADM_FT_BYTES:
481c96729eaSRobert Mustacchi 		/*
482c96729eaSRobert Mustacchi 		 * Right now we a 64-bit byte value is 16 EiB. If we have more
483c96729eaSRobert Mustacchi 		 * than that, error so we do something more clever, but
484c96729eaSRobert Mustacchi 		 * otherwise punt for the time being.
485c96729eaSRobert Mustacchi 		 */
486c96729eaSRobert Mustacchi 		if (u128.hi != 0) {
487c96729eaSRobert Mustacchi 			warnx("encountered 128-bit size with upper bits set "
488c96729eaSRobert Mustacchi 			    "for field %s, cannot accurately convert",
489c96729eaSRobert Mustacchi 			    field->nf_desc);
490c96729eaSRobert Mustacchi 			u128.lo = UINT64_MAX;
491c96729eaSRobert Mustacchi 		}
492c96729eaSRobert Mustacchi 
493c96729eaSRobert Mustacchi 		if (field->nf_addend.nfa_shift != 0 ||
494c96729eaSRobert Mustacchi 		    field->nf_addend.nfa_addend != 0) {
495c96729eaSRobert Mustacchi 			warnx("encountered 128-bit size with addend request "
496c96729eaSRobert Mustacchi 			    "for field %s, but conversion not implemented",
497c96729eaSRobert Mustacchi 			    field->nf_desc);
498c96729eaSRobert Mustacchi 		}
499c96729eaSRobert Mustacchi 
500c96729eaSRobert Mustacchi 		nicenum(u128.lo, ofarg->fo_hval, sizeof (ofarg->fo_hval));
501c96729eaSRobert Mustacchi 		break;
502c96729eaSRobert Mustacchi 	case NVMEADM_FT_GUID:
503c96729eaSRobert Mustacchi 		u8p = data + field->nf_off;
504c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_hval, sizeof (ofarg->fo_hval),
505c96729eaSRobert Mustacchi 		    "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
506c96729eaSRobert Mustacchi 		    "%02x%02x%02x%02x%02x%02x",
507c96729eaSRobert Mustacchi 		    u8p[15], u8p[14], u8p[13], u8p[12],
508c96729eaSRobert Mustacchi 		    u8p[11], u8p[10], u8p[9], u8p[8],
509c96729eaSRobert Mustacchi 		    u8p[7], u8p[6], u8p[5], u8p[4],
510c96729eaSRobert Mustacchi 		    u8p[3], u8p[2], u8p[1], u8p[0]);
511c96729eaSRobert Mustacchi 		break;
512c96729eaSRobert Mustacchi 	case NVMEADM_FT_HEX:
513c96729eaSRobert Mustacchi 		if (field->nf_addend.nfa_shift != 0 ||
514c96729eaSRobert Mustacchi 		    field->nf_addend.nfa_addend != 0) {
515c96729eaSRobert Mustacchi 			warnx("encountered 128-bit field with addend, but "
516c96729eaSRobert Mustacchi 			    "cannot apply it");
517c96729eaSRobert Mustacchi 		}
518c96729eaSRobert Mustacchi 		(void) memcpy(ofarg->fo_hval, ofarg->fo_val,
519c96729eaSRobert Mustacchi 		    sizeof (ofarg->fo_hval));
520c96729eaSRobert Mustacchi 		break;
521c96729eaSRobert Mustacchi 	default:
522c96729eaSRobert Mustacchi 		break;
523c96729eaSRobert Mustacchi 	}
524c96729eaSRobert Mustacchi }
525c96729eaSRobert Mustacchi 
526c96729eaSRobert Mustacchi static void
nvmeadm_field_extract(const nvmeadm_field_t * field,const void * data,field_ofmt_t * ofarg,uint64_t * bp)527c96729eaSRobert Mustacchi nvmeadm_field_extract(const nvmeadm_field_t *field, const void *data,
528c96729eaSRobert Mustacchi     field_ofmt_t *ofarg, uint64_t *bp)
529c96729eaSRobert Mustacchi {
530c96729eaSRobert Mustacchi 	uint64_t val;
531c96729eaSRobert Mustacchi 
532c96729eaSRobert Mustacchi 	/*
533c96729eaSRobert Mustacchi 	 * Don't touch containers. There is nothing to extract.
534c96729eaSRobert Mustacchi 	 */
535c96729eaSRobert Mustacchi 	if (field->nf_type == NVMEADM_FT_CONTAINER)
536c96729eaSRobert Mustacchi 		return;
537c96729eaSRobert Mustacchi 
538c96729eaSRobert Mustacchi 	/*
539c96729eaSRobert Mustacchi 	 * If this is an ASCII field, then we just handle this immediately.
540c96729eaSRobert Mustacchi 	 */
541c96729eaSRobert Mustacchi 	if (field->nf_type == NVMEADM_FT_ASCII ||
542c96729eaSRobert Mustacchi 	    field->nf_type == NVMEADM_FT_ASCIIZ) {
543c96729eaSRobert Mustacchi 		field_extract_ascii(data, field->nf_type, field->nf_len,
544c96729eaSRobert Mustacchi 		    field->nf_off, ofarg);
545c96729eaSRobert Mustacchi 		return;
546c96729eaSRobert Mustacchi 	}
547c96729eaSRobert Mustacchi 
548c96729eaSRobert Mustacchi 	/*
549c96729eaSRobert Mustacchi 	 * Next look at the type and size and see if this is something that we
550c96729eaSRobert Mustacchi 	 * can deal with simply. Items that are less than a u64 are easy. If we
551c96729eaSRobert Mustacchi 	 * had better C23 support and therefore could deal with a u128, then
552c96729eaSRobert Mustacchi 	 * this would be simpler too. Until then, we split this based on things
553c96729eaSRobert Mustacchi 	 * larger than a u64 and those not.
554c96729eaSRobert Mustacchi 	 */
555c96729eaSRobert Mustacchi 	if (field->nf_len > sizeof (uint64_t)) {
556c96729eaSRobert Mustacchi 		switch (field->nf_type) {
557c96729eaSRobert Mustacchi 		case NVMEADM_FT_HEX:
558c96729eaSRobert Mustacchi 		case NVMEADM_FT_BYTES:
559c96729eaSRobert Mustacchi 		case NVMEADM_FT_GUID:
560c96729eaSRobert Mustacchi 			VERIFY3U(field->nf_len, ==, 16);
561c96729eaSRobert Mustacchi 			nvmeadm_field_extract_u128(field, data, ofarg);
562c96729eaSRobert Mustacchi 			break;
563c96729eaSRobert Mustacchi 		default:
564c96729eaSRobert Mustacchi 			abort();
565c96729eaSRobert Mustacchi 		}
566c96729eaSRobert Mustacchi 		return;
567c96729eaSRobert Mustacchi 	}
568c96729eaSRobert Mustacchi 
569c96729eaSRobert Mustacchi 	/*
570c96729eaSRobert Mustacchi 	 * NVMe integers are defined as being encoded in little endian.
571c96729eaSRobert Mustacchi 	 */
572c96729eaSRobert Mustacchi 	val = 0;
573c96729eaSRobert Mustacchi 	const uint8_t *u8p = data + field->nf_off;
574c96729eaSRobert Mustacchi 	for (size_t i = 0; i < field->nf_len; i++) {
575c96729eaSRobert Mustacchi 		uint8_t shift = i * NBBY;
576c96729eaSRobert Mustacchi 		val |= (uint64_t)u8p[i] << shift;
577c96729eaSRobert Mustacchi 	}
578c96729eaSRobert Mustacchi 
579c96729eaSRobert Mustacchi 	if (bp != NULL)
580c96729eaSRobert Mustacchi 		*bp = val;
581c96729eaSRobert Mustacchi 	(void) snprintf(ofarg->fo_val, sizeof (ofarg->fo_val), "0x%" PRIx64,
582c96729eaSRobert Mustacchi 	    val);
583c96729eaSRobert Mustacchi 
584c96729eaSRobert Mustacchi 	switch (field->nf_type) {
585c96729eaSRobert Mustacchi 	case NVMEADM_FT_HEX:
586c96729eaSRobert Mustacchi 		/*
587c96729eaSRobert Mustacchi 		 * The "human" string is the version with the addend applied.
588c96729eaSRobert Mustacchi 		 */
589c96729eaSRobert Mustacchi 		val = nvmeadm_apply_addend(val, &field->nf_addend);
590c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_hval, sizeof (ofarg->fo_hval),
591c96729eaSRobert Mustacchi 		    "0x%" PRIx64, val);
592c96729eaSRobert Mustacchi 		break;
593c96729eaSRobert Mustacchi 	case NVMEADM_FT_UNIT:
594c96729eaSRobert Mustacchi 		val = nvmeadm_apply_addend(val, &field->nf_addend);
595c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_hval, sizeof (ofarg->fo_hval),
596c96729eaSRobert Mustacchi 		    "%" PRIu64 " %s", val, field->nf_addend.nfa_unit);
597c96729eaSRobert Mustacchi 		break;
598c96729eaSRobert Mustacchi 	case NVMEADM_FT_BITS:
599c96729eaSRobert Mustacchi 		/* No human string for these */
600c96729eaSRobert Mustacchi 		break;
601c96729eaSRobert Mustacchi 	case NVMEADM_FT_STRMAP:
602c96729eaSRobert Mustacchi 		if (val < ARRAY_SIZE(field->nf_strs) &&
603c96729eaSRobert Mustacchi 		    field->nf_strs[val] != NULL) {
604c96729eaSRobert Mustacchi 			(void) strlcpy(ofarg->fo_hval, field->nf_strs[val],
605c96729eaSRobert Mustacchi 			    sizeof (ofarg->fo_hval));
606c96729eaSRobert Mustacchi 		} else {
607c96729eaSRobert Mustacchi 			(void) strlcpy(ofarg->fo_hval, "reserved",
608c96729eaSRobert Mustacchi 			    sizeof (ofarg->fo_hval));
609c96729eaSRobert Mustacchi 		}
610c96729eaSRobert Mustacchi 		break;
611c96729eaSRobert Mustacchi 	case NVMEADM_FT_BYTES:
612c96729eaSRobert Mustacchi 		val = nvmeadm_apply_addend(val, &field->nf_addend);
613c96729eaSRobert Mustacchi 		nicenum(val, ofarg->fo_hval, sizeof (ofarg->fo_hval));
614c96729eaSRobert Mustacchi 		break;
615c96729eaSRobert Mustacchi 	case NVMEADM_FT_PERCENT:
616c96729eaSRobert Mustacchi 		(void) snprintf(ofarg->fo_hval, sizeof (ofarg->fo_hval), "%u%%",
617c96729eaSRobert Mustacchi 		    val);
618c96729eaSRobert Mustacchi 		break;
619c96729eaSRobert Mustacchi 	case NVMEADM_FT_GUID:
620c96729eaSRobert Mustacchi 		/*
621c96729eaSRobert Mustacchi 		 * GUIDs are larger than 8 bytes and so we should never hit
622c96729eaSRobert Mustacchi 		 * this.
623c96729eaSRobert Mustacchi 		 */
624c96729eaSRobert Mustacchi 		abort();
625c96729eaSRobert Mustacchi 	case NVMEADM_FT_ASCII:
626c96729eaSRobert Mustacchi 	case NVMEADM_FT_ASCIIZ:
627c96729eaSRobert Mustacchi 	case NVMEADM_FT_CONTAINER:
628c96729eaSRobert Mustacchi 		/* Should already be handled above */
629c96729eaSRobert Mustacchi 		abort();
630c96729eaSRobert Mustacchi 	}
631c96729eaSRobert Mustacchi }
632c96729eaSRobert Mustacchi 
633c96729eaSRobert Mustacchi void
nvmeadm_field_print(nvmeadm_field_print_t * print)634c96729eaSRobert Mustacchi nvmeadm_field_print(nvmeadm_field_print_t *print)
635c96729eaSRobert Mustacchi {
636c96729eaSRobert Mustacchi 	if (print->fp_ofmt == NULL && print->fp_header != NULL &&
637c96729eaSRobert Mustacchi 	    nvmeadm_field_filter(print, print->fp_base, NULL)) {
638c96729eaSRobert Mustacchi 		(void) printf("%s\n", print->fp_header);
639c96729eaSRobert Mustacchi 	}
640c96729eaSRobert Mustacchi 
641c96729eaSRobert Mustacchi 	for (size_t i = 0; i < print->fp_nfields; i++) {
642c96729eaSRobert Mustacchi 		const nvmeadm_field_t *field = &print->fp_fields[i];
643c96729eaSRobert Mustacchi 		field_ofmt_t ofarg = { 0 };
644c96729eaSRobert Mustacchi 
645c96729eaSRobert Mustacchi 		/*
646c96729eaSRobert Mustacchi 		 * See if this field is one that is meaningful to this revision
647c96729eaSRobert Mustacchi 		 * of the log page or controller. If the version is NULL or the
648c96729eaSRobert Mustacchi 		 * revision is 0 in the field, then there is nothing to check.
649c96729eaSRobert Mustacchi 		 */
650c96729eaSRobert Mustacchi 		if (field->nf_rev != 0 && field->nf_rev > print->fp_rev) {
651c96729eaSRobert Mustacchi 			continue;
652c96729eaSRobert Mustacchi 		}
653c96729eaSRobert Mustacchi 
654c96729eaSRobert Mustacchi 		if (field->nf_maxrev != 0 && print->fp_rev > field->nf_maxrev) {
655c96729eaSRobert Mustacchi 			continue;
656c96729eaSRobert Mustacchi 		}
657c96729eaSRobert Mustacchi 
658c96729eaSRobert Mustacchi 		if (field->nf_vers != NULL && print->fp_vers != NULL &&
659c96729eaSRobert Mustacchi 		    !nvme_vers_atleast(print->fp_vers, field->nf_vers)) {
660c96729eaSRobert Mustacchi 			continue;
661c96729eaSRobert Mustacchi 		}
662c96729eaSRobert Mustacchi 
663c96729eaSRobert Mustacchi 		ofarg.fo_base = print->fp_base;
664c96729eaSRobert Mustacchi 		ofarg.fo_short = field->nf_short;
665c96729eaSRobert Mustacchi 		ofarg.fo_desc = field->nf_desc;
666c96729eaSRobert Mustacchi 		ofarg.fo_off = print->fp_off + field->nf_off;
667c96729eaSRobert Mustacchi 		ofarg.fo_bitoff = 0;
668c96729eaSRobert Mustacchi 		ofarg.fo_len = field->nf_len;
669c96729eaSRobert Mustacchi 		ofarg.fo_bitlen = 0;
670c96729eaSRobert Mustacchi 
671c96729eaSRobert Mustacchi 		/*
672c96729eaSRobert Mustacchi 		 * Extract the value from the field and perform any conversions
673c96729eaSRobert Mustacchi 		 * to a human value where appropriate.
674c96729eaSRobert Mustacchi 		 */
675c96729eaSRobert Mustacchi 		uint64_t bit_val;
676c96729eaSRobert Mustacchi 		nvmeadm_field_extract(field, print->fp_data, &ofarg, &bit_val);
677c96729eaSRobert Mustacchi 
678c96729eaSRobert Mustacchi 		field_print_one(print, &ofarg, field->nf_type);
679c96729eaSRobert Mustacchi 
680c96729eaSRobert Mustacchi 		/*
681c96729eaSRobert Mustacchi 		 * Now that we've dealt with this, handle anything that's
682c96729eaSRobert Mustacchi 		 * somewhat recursive in nature.
683c96729eaSRobert Mustacchi 		 */
684c96729eaSRobert Mustacchi 		if (field->nf_type == NVMEADM_FT_CONTAINER) {
685c96729eaSRobert Mustacchi 			char buf[256];
686c96729eaSRobert Mustacchi 			nvmeadm_field_print_t copy = *print;
687c96729eaSRobert Mustacchi 
688c96729eaSRobert Mustacchi 			if (print->fp_base == NULL) {
689c96729eaSRobert Mustacchi 				(void) strlcpy(buf, field->nf_short,
690c96729eaSRobert Mustacchi 				    sizeof (buf));
691c96729eaSRobert Mustacchi 			} else {
692c96729eaSRobert Mustacchi 				(void) snprintf(buf, sizeof (buf), "%s.%s",
693c96729eaSRobert Mustacchi 				    print->fp_base, field->nf_short);
694c96729eaSRobert Mustacchi 			}
695c96729eaSRobert Mustacchi 
696c96729eaSRobert Mustacchi 			copy.fp_header = NULL;
697c96729eaSRobert Mustacchi 			copy.fp_base = buf;
698c96729eaSRobert Mustacchi 			copy.fp_fields = field->nf_fields;
699c96729eaSRobert Mustacchi 			copy.fp_nfields = field->nf_nfields;
700c96729eaSRobert Mustacchi 			copy.fp_data += field->nf_off;
701c96729eaSRobert Mustacchi 			copy.fp_dlen = field->nf_len;
702c96729eaSRobert Mustacchi 			copy.fp_off += field->nf_off;
703c96729eaSRobert Mustacchi 			copy.fp_indent++;
704c96729eaSRobert Mustacchi 
705c96729eaSRobert Mustacchi 			nvmeadm_field_print(&copy);
706c96729eaSRobert Mustacchi 		} else if (field->nf_type == NVMEADM_FT_BITS) {
707c96729eaSRobert Mustacchi 			char buf[256];
708c96729eaSRobert Mustacchi 
709c96729eaSRobert Mustacchi 			if (print->fp_base == NULL) {
710c96729eaSRobert Mustacchi 				(void) strlcpy(buf, field->nf_short,
711c96729eaSRobert Mustacchi 				    sizeof (buf));
712c96729eaSRobert Mustacchi 			} else {
713c96729eaSRobert Mustacchi 				(void) snprintf(buf, sizeof (buf), "%s.%s",
714c96729eaSRobert Mustacchi 				    print->fp_base, field->nf_short);
715c96729eaSRobert Mustacchi 			}
716c96729eaSRobert Mustacchi 
717c96729eaSRobert Mustacchi 			field_print_bits(print, field->nf_bits, field->nf_nbits,
718c96729eaSRobert Mustacchi 			    bit_val, buf, ofarg.fo_off, 0, 1);
719c96729eaSRobert Mustacchi 		}
720c96729eaSRobert Mustacchi 	}
721c96729eaSRobert Mustacchi }
722c96729eaSRobert Mustacchi 
723c96729eaSRobert Mustacchi bool
nvmeadm_log_page_fields(const nvme_process_arg_t * npa,const char * name,const void * data,size_t len,nvmeadm_field_filt_t * filts,size_t nfilts,nvmeadm_log_field_flag_t flags)724c96729eaSRobert Mustacchi nvmeadm_log_page_fields(const nvme_process_arg_t *npa, const char *name,
725c96729eaSRobert Mustacchi     const void *data, size_t len, nvmeadm_field_filt_t *filts, size_t nfilts,
726c96729eaSRobert Mustacchi     nvmeadm_log_field_flag_t flags)
727c96729eaSRobert Mustacchi {
728c96729eaSRobert Mustacchi 	bool ret = true, found = false;
729c96729eaSRobert Mustacchi 	VERIFY0(flags & ~NVMEADM_LFF_CHECK_NAME);
730c96729eaSRobert Mustacchi 
731c96729eaSRobert Mustacchi 	/*
732c96729eaSRobert Mustacchi 	 * If we don't have a log page name, that's an indication that we're
733c96729eaSRobert Mustacchi 	 * being asked to just do our hex print.
734c96729eaSRobert Mustacchi 	 */
735c96729eaSRobert Mustacchi 	if (name == NULL) {
736c96729eaSRobert Mustacchi 		nvmeadm_dump_hex(data, len);
737c96729eaSRobert Mustacchi 		return (ret);
738c96729eaSRobert Mustacchi 	}
739c96729eaSRobert Mustacchi 
740c96729eaSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(field_log_map); i++) {
741c96729eaSRobert Mustacchi 		if (strcmp(name, field_log_map[i]->nlfi_log) != 0) {
742c96729eaSRobert Mustacchi 			continue;
743c96729eaSRobert Mustacchi 		}
744c96729eaSRobert Mustacchi 
745c96729eaSRobert Mustacchi 		/*
746c96729eaSRobert Mustacchi 		 * We've found a match for this log page. Ensure we don't hex
747c96729eaSRobert Mustacchi 		 * dump it at the end.
748c96729eaSRobert Mustacchi 		 */
749c96729eaSRobert Mustacchi 		found = true;
750c96729eaSRobert Mustacchi 
751c96729eaSRobert Mustacchi 		if (len < field_log_map[i]->nlfi_min) {
752c96729eaSRobert Mustacchi 			errx(-1, "cannot print log %s: log requires "
753c96729eaSRobert Mustacchi 			    "0x%zx bytes of data but only have 0x%zx",
754c96729eaSRobert Mustacchi 			    name, field_log_map[i]->nlfi_min, len);
755c96729eaSRobert Mustacchi 		}
756c96729eaSRobert Mustacchi 
757c96729eaSRobert Mustacchi 		nvmeadm_field_print_t print = { 0 };
758c96729eaSRobert Mustacchi 		if (field_log_map[i]->nlfi_getrev != NULL) {
759c96729eaSRobert Mustacchi 			print.fp_rev = field_log_map[i]->nlfi_getrev(data,
760c96729eaSRobert Mustacchi 			    len);
761c96729eaSRobert Mustacchi 		}
762c96729eaSRobert Mustacchi 
763c96729eaSRobert Mustacchi 		/*
764c96729eaSRobert Mustacchi 		 * This may be NULL if we're not getting this fresh from a
765c96729eaSRobert Mustacchi 		 * controller.
766c96729eaSRobert Mustacchi 		 */
767c96729eaSRobert Mustacchi 		print.fp_vers = npa->npa_version;
768c96729eaSRobert Mustacchi 
769c96729eaSRobert Mustacchi 		print.fp_filts = filts;
770c96729eaSRobert Mustacchi 		print.fp_nfilts = nfilts;
771c96729eaSRobert Mustacchi 		print.fp_ofmt = npa->npa_ofmt;
772c96729eaSRobert Mustacchi 
773c96729eaSRobert Mustacchi 		/*
774c96729eaSRobert Mustacchi 		 * A registered log page may instead ask to drive the process as
775c96729eaSRobert Mustacchi 		 * there is variable data or similar present.
776c96729eaSRobert Mustacchi 		 */
777c96729eaSRobert Mustacchi 		if (field_log_map[i]->nlfi_drive != NULL) {
778c96729eaSRobert Mustacchi 			if (!field_log_map[i]->nlfi_drive(&print, data, len)) {
779c96729eaSRobert Mustacchi 				ret = false;
780c96729eaSRobert Mustacchi 			}
781c96729eaSRobert Mustacchi 		} else {
782c96729eaSRobert Mustacchi 			print.fp_fields = field_log_map[i]->nlfi_fields;
783c96729eaSRobert Mustacchi 			print.fp_nfields = field_log_map[i]->nlfi_nfields;
784c96729eaSRobert Mustacchi 
785c96729eaSRobert Mustacchi 			/*
786c96729eaSRobert Mustacchi 			 * We set the base string to NULL for most log pages
787c96729eaSRobert Mustacchi 			 * that aren't comprised of variable components by
788c96729eaSRobert Mustacchi 			 * default. Ones that are variable instead will set this
789c96729eaSRobert Mustacchi 			 * as part of their drive function callback.
790c96729eaSRobert Mustacchi 			 */
791c96729eaSRobert Mustacchi 			print.fp_base = NULL;
792c96729eaSRobert Mustacchi 
793c96729eaSRobert Mustacchi 			print.fp_data = data;
794c96729eaSRobert Mustacchi 			print.fp_dlen = len;
795c96729eaSRobert Mustacchi 
796c96729eaSRobert Mustacchi 			nvmeadm_field_print(&print);
797c96729eaSRobert Mustacchi 			break;
798c96729eaSRobert Mustacchi 		}
799c96729eaSRobert Mustacchi 	}
800c96729eaSRobert Mustacchi 
801c96729eaSRobert Mustacchi 	if (!found) {
802c96729eaSRobert Mustacchi 		if ((flags & NVMEADM_LFF_CHECK_NAME) != 0) {
803c96729eaSRobert Mustacchi 			warnx("unable to print log page %s: the log page is "
804c96729eaSRobert Mustacchi 			    "either unknown or printing information is missing",
805c96729eaSRobert Mustacchi 			    name);
806c96729eaSRobert Mustacchi 			ret = false;
807c96729eaSRobert Mustacchi 		}
808c96729eaSRobert Mustacchi 
809c96729eaSRobert Mustacchi 		if (npa->npa_ofmt != NULL) {
810c96729eaSRobert Mustacchi 			errx(-1, "parsable mode requested, but unable to print "
811c96729eaSRobert Mustacchi 			    "parsable output");
812c96729eaSRobert Mustacchi 		}
813c96729eaSRobert Mustacchi 
814c96729eaSRobert Mustacchi 		nvmeadm_dump_hex(data, len);
815c96729eaSRobert Mustacchi 	}
816c96729eaSRobert Mustacchi 
817c96729eaSRobert Mustacchi 	for (size_t i = 0; i < nfilts; i++) {
818c96729eaSRobert Mustacchi 		if (!filts[i].nff_used) {
819c96729eaSRobert Mustacchi 			warnx("filter '%s' did not match any fields",
820c96729eaSRobert Mustacchi 			    filts[i].nff_str);
821c96729eaSRobert Mustacchi 			ret = false;
822c96729eaSRobert Mustacchi 		}
823c96729eaSRobert Mustacchi 	}
824c96729eaSRobert Mustacchi 
825c96729eaSRobert Mustacchi 	return (ret);
826c96729eaSRobert Mustacchi }
827