xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_ofmt.c (revision 73eca582ee6fee5639a2c8286c86afc906764392)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2026 Oxide Computer Company
14  * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
15  */
16 
17 /*
18  * nvmeadm output formatting for ofmt based rendering
19  */
20 
21 #include <strings.h>
22 #include <sys/sysmacros.h>
23 #include <err.h>
24 
25 #include "nvmeadm.h"
26 
27 typedef struct {
28 	uint32_t	nb_flag;
29 	const char	*nb_str;
30 } nvmeadm_bitstr_t;
31 
32 static boolean_t
nvmeadm_bits_to_str(uint32_t val,const nvmeadm_bitstr_t * strs,size_t nstrs,char * buf,size_t buflen)33 nvmeadm_bits_to_str(uint32_t val, const nvmeadm_bitstr_t *strs, size_t nstrs,
34     char *buf, size_t buflen)
35 {
36 	boolean_t comma = B_FALSE;
37 
38 	buf[0] = '\0';
39 	for (size_t i = 0; i < nstrs; i++) {
40 		if ((val & strs[i].nb_flag) != strs[i].nb_flag)
41 			continue;
42 		if (comma && strlcat(buf, ",", buflen) >= buflen)
43 			return (B_FALSE);
44 		if (strlcat(buf, strs[i].nb_str, buflen) >= buflen)
45 			return (B_FALSE);
46 		comma = true;
47 	}
48 
49 	if (buf[0] == '\0') {
50 		if (strlcat(buf, "--", buflen) >= buflen)
51 			return (B_FALSE);
52 	}
53 
54 	return (B_TRUE);
55 }
56 
57 typedef enum nvme_list_ofmt_field {
58 	NVME_LIST_MODEL,
59 	NVME_LIST_SERIAL,
60 	NVME_LIST_FWREV,
61 	NVME_LIST_VERSION,
62 	NVME_LIST_SIZE,
63 	NVME_LIST_CAPACITY,
64 	NVME_LIST_USED,
65 	NVME_LIST_INSTANCE,
66 	NVME_LIST_NAMESPACE,
67 	NVME_LIST_DISK,
68 	NVME_LIST_UNALLOC,
69 	NVME_LIST_NS_STATE,
70 	NVME_LIST_CTRLPATH,
71 	NVME_LIST_NS_FORMAT,
72 	NVME_LIST_NS_FMTID,
73 	NVME_LIST_NS_FMTDS,
74 	NVME_LIST_NS_FMTMS,
75 	NVME_LIST_LOC_LOC,
76 	NVME_LIST_LOC_CTLAP
77 } nvme_list_ofmt_field_t;
78 
79 static boolean_t
nvmeadm_list_common_ofmt_cb(ofmt_arg_t * ofmt_arg,char * buf,uint_t buflen)80 nvmeadm_list_common_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen)
81 {
82 	nvmeadm_list_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg;
83 	nvme_ctrl_info_t *ctrl = list->nloa_ctrl;
84 	const nvme_version_t *vers;
85 	char *path;
86 	size_t ret;
87 
88 	switch (ofmt_arg->ofmt_id) {
89 	case NVME_LIST_MODEL:
90 		ret = strlcpy(buf, nvme_ctrl_info_model(ctrl), buflen);
91 		break;
92 	case NVME_LIST_SERIAL:
93 		ret = strlcpy(buf, nvme_ctrl_info_serial(ctrl), buflen);
94 		break;
95 	case NVME_LIST_FWREV:
96 		ret = strlcpy(buf, nvme_ctrl_info_fwrev(ctrl), buflen);
97 		break;
98 	case NVME_LIST_VERSION:
99 		vers = nvme_ctrl_info_version(ctrl);
100 		ret = snprintf(buf, buflen, "%u.%u", vers->v_major,
101 		    vers->v_minor);
102 		break;
103 	case NVME_LIST_INSTANCE:
104 		ret = strlcpy(buf, list->nloa_name, buflen);
105 		break;
106 	case NVME_LIST_CTRLPATH:
107 		if (list->nloa_dip == DI_NODE_NIL) {
108 			return (B_FALSE);
109 		}
110 
111 		path = di_devfs_path(list->nloa_dip);
112 		if (path == NULL) {
113 			return (B_FALSE);
114 		}
115 		ret = strlcat(buf, path, buflen);
116 		di_devfs_path_free(path);
117 		break;
118 	default:
119 		warnx("internal programmer error: encountered unknown ofmt "
120 		    "argument id 0x%x", ofmt_arg->ofmt_id);
121 		abort();
122 	}
123 	if (ret >= buflen) {
124 		return (B_FALSE);
125 	}
126 	return (B_TRUE);
127 }
128 
129 static boolean_t
nvmeadm_list_ctrl_ofmt_cb(ofmt_arg_t * ofmt_arg,char * buf,uint_t buflen)130 nvmeadm_list_ctrl_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen)
131 {
132 	nvmeadm_list_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg;
133 	nvme_ctrl_info_t *ctrl = list->nloa_ctrl;
134 	nvme_uint128_t u128;
135 	size_t ret;
136 
137 	switch (ofmt_arg->ofmt_id) {
138 	case NVME_LIST_CAPACITY:
139 		if (nvme_ctrl_info_cap(ctrl, &u128)) {
140 			ret = nvme_snprint_uint128(buf, buflen, u128, 0, 0);
141 		} else {
142 			return (B_FALSE);
143 		}
144 		break;
145 	case NVME_LIST_UNALLOC:
146 		if (nvme_ctrl_info_unalloc_cap(ctrl, &u128)) {
147 			ret = nvme_snprint_uint128(buf, buflen, u128, 0, 0);
148 		} else {
149 			return (B_FALSE);
150 		}
151 		break;
152 	case NVME_LIST_LOC_LOC:
153 		if (list->nloa_loc != NULL) {
154 			ret = strlcpy(buf, list->nloa_loc, buflen);
155 		} else {
156 			ret = strlcpy(buf, "-", buflen);
157 		}
158 		break;
159 	case NVME_LIST_LOC_CTLAP:
160 		if (list->nloa_ap != NULL) {
161 			ret = strlcpy(buf, list->nloa_ap, buflen);
162 		} else {
163 			ret = strlcpy(buf, "-", buflen);
164 		}
165 		break;
166 	default:
167 		warnx("internal programmer error: encountered unknown ofmt "
168 		    "argument id 0x%x", ofmt_arg->ofmt_id);
169 		abort();
170 	}
171 
172 	if (ret >= buflen) {
173 		return (B_FALSE);
174 	}
175 	return (B_TRUE);
176 }
177 
178 static boolean_t
nvmeadm_list_nsid_ofmt_cb(ofmt_arg_t * ofmt_arg,char * buf,uint_t buflen)179 nvmeadm_list_nsid_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen)
180 {
181 	nvmeadm_list_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg;
182 	nvme_ns_info_t *ns = list->nloa_ns;
183 	const nvme_nvm_lba_fmt_t *fmt = NULL;
184 	const nvme_ns_disc_level_t level = nvme_ns_info_level(ns);
185 	uint64_t val;
186 	size_t ret;
187 
188 	(void) nvme_ns_info_curformat(ns, &fmt);
189 
190 	switch (ofmt_arg->ofmt_id) {
191 	case NVME_LIST_NAMESPACE:
192 		ret = snprintf(buf, buflen, "%u", nvme_ns_info_nsid(ns));
193 		break;
194 	case NVME_LIST_DISK:
195 		if (list->nloa_disk != NULL) {
196 			ret = strlcpy(buf, list->nloa_disk, buflen);
197 		} else {
198 			return (B_FALSE);
199 		}
200 		break;
201 	case NVME_LIST_SIZE:
202 		if (nvme_ns_info_size(ns, &val) && fmt != NULL) {
203 			val *= nvme_nvm_lba_fmt_data_size(fmt);
204 			ret = snprintf(buf, buflen, "%" PRIu64, val);
205 		} else {
206 			return (B_FALSE);
207 		}
208 		break;
209 	case NVME_LIST_CAPACITY:
210 		if (nvme_ns_info_size(ns, &val) && fmt != NULL) {
211 			val *= nvme_nvm_lba_fmt_data_size(fmt);
212 			ret = snprintf(buf, buflen, "%" PRIu64, val);
213 		} else {
214 			return (B_FALSE);
215 		}
216 		break;
217 	case NVME_LIST_USED:
218 		if (nvme_ns_info_size(ns, &val) && fmt != NULL) {
219 			val *= nvme_nvm_lba_fmt_data_size(fmt);
220 			ret = snprintf(buf, buflen, "%" PRIu64, val);
221 		} else {
222 			return (B_FALSE);
223 		}
224 		break;
225 	case NVME_LIST_NS_STATE:
226 		ret = strlcpy(buf, list->nloa_state, buflen);
227 		break;
228 	case NVME_LIST_NS_FORMAT:
229 		if (fmt != NULL) {
230 			ret = snprintf(buf, buflen, "%u+%u",
231 			    nvme_nvm_lba_fmt_data_size(fmt),
232 			    nvme_nvm_lba_fmt_meta_size(fmt));
233 		} else if (level < NVME_NS_DISC_F_ACTIVE) {
234 			ret = strlcpy(buf, "-", buflen);
235 		} else {
236 			return (B_FALSE);
237 		}
238 		break;
239 	case NVME_LIST_NS_FMTID:
240 		if (fmt != NULL) {
241 			ret = snprintf(buf, buflen, "%u",
242 			    nvme_nvm_lba_fmt_id(fmt));
243 		} else if (level < NVME_NS_DISC_F_ACTIVE) {
244 			ret = strlcpy(buf, "-", buflen);
245 		} else {
246 			return (B_FALSE);
247 		}
248 		break;
249 	case NVME_LIST_NS_FMTDS:
250 		if (fmt != NULL) {
251 			ret = snprintf(buf, buflen, "%u",
252 			    nvme_nvm_lba_fmt_data_size(fmt));
253 		} else if (level < NVME_NS_DISC_F_ACTIVE) {
254 			ret = strlcpy(buf, "-", buflen);
255 		} else {
256 			return (B_FALSE);
257 		}
258 		break;
259 	case NVME_LIST_NS_FMTMS:
260 		if (fmt != NULL) {
261 			ret = snprintf(buf, buflen, "%u",
262 			    nvme_nvm_lba_fmt_meta_size(fmt));
263 		} else if (level < NVME_NS_DISC_F_ACTIVE) {
264 			ret = strlcpy(buf, "-", buflen);
265 		} else {
266 			return (B_FALSE);
267 		}
268 		break;
269 	default:
270 		warnx("internal programmer error: encountered unknown ofmt "
271 		    "argument id 0x%x", ofmt_arg->ofmt_id);
272 		abort();
273 	}
274 
275 	if (ret >= buflen) {
276 		return (B_FALSE);
277 	}
278 	return (B_TRUE);
279 }
280 
281 const ofmt_field_t nvmeadm_list_ctrl_ofmt[] = {
282 	{ "MODEL", 30, NVME_LIST_MODEL, nvmeadm_list_common_ofmt_cb },
283 	{ "SERIAL", 30, NVME_LIST_SERIAL, nvmeadm_list_common_ofmt_cb },
284 	{ "FWREV", 10, NVME_LIST_FWREV, nvmeadm_list_common_ofmt_cb },
285 	{ "VERSION", 10, NVME_LIST_VERSION, nvmeadm_list_common_ofmt_cb },
286 	{ "CAPACITY", 15, NVME_LIST_CAPACITY, nvmeadm_list_ctrl_ofmt_cb },
287 	{ "INSTANCE", 10, NVME_LIST_INSTANCE, nvmeadm_list_common_ofmt_cb },
288 	{ "UNALLOCATED", 15, NVME_LIST_UNALLOC, nvmeadm_list_ctrl_ofmt_cb },
289 	{ "CTRLPATH", 30, NVME_LIST_CTRLPATH, nvmeadm_list_common_ofmt_cb },
290 	{ NULL, 0, 0, NULL }
291 };
292 
293 const ofmt_field_t nvmeadm_list_loc_ofmt[] = {
294 	{ "MODEL", 30, NVME_LIST_MODEL, nvmeadm_list_common_ofmt_cb },
295 	{ "SERIAL", 20, NVME_LIST_SERIAL, nvmeadm_list_common_ofmt_cb },
296 	{ "FWREV", 10, NVME_LIST_FWREV, nvmeadm_list_common_ofmt_cb },
297 	{ "VERSION", 10, NVME_LIST_VERSION, nvmeadm_list_common_ofmt_cb },
298 	{ "CAPACITY", 15, NVME_LIST_CAPACITY, nvmeadm_list_ctrl_ofmt_cb },
299 	{ "INSTANCE", 10, NVME_LIST_INSTANCE, nvmeadm_list_common_ofmt_cb },
300 	{ "UNALLOCATED", 15, NVME_LIST_UNALLOC, nvmeadm_list_ctrl_ofmt_cb },
301 	{ "CTRLPATH", 30, NVME_LIST_CTRLPATH, nvmeadm_list_common_ofmt_cb },
302 	{ "LOCATION", 15, NVME_LIST_LOC_LOC, nvmeadm_list_ctrl_ofmt_cb },
303 	{ "CTLAP", 10, NVME_LIST_LOC_CTLAP, nvmeadm_list_ctrl_ofmt_cb },
304 	{ NULL, 0, 0, NULL }
305 };
306 
307 const ofmt_field_t nvmeadm_list_nsid_ofmt[] = {
308 	{ "MODEL", 30, NVME_LIST_MODEL, nvmeadm_list_common_ofmt_cb },
309 	{ "SERIAL", 30, NVME_LIST_SERIAL, nvmeadm_list_common_ofmt_cb },
310 	{ "FWREV", 10, NVME_LIST_FWREV, nvmeadm_list_common_ofmt_cb },
311 	{ "VERSION", 10, NVME_LIST_VERSION, nvmeadm_list_common_ofmt_cb },
312 	{ "SIZE", 15, NVME_LIST_SIZE, nvmeadm_list_nsid_ofmt_cb },
313 	{ "CAPACITY", 15, NVME_LIST_CAPACITY, nvmeadm_list_nsid_ofmt_cb },
314 	{ "USED", 15, NVME_LIST_USED, nvmeadm_list_nsid_ofmt_cb },
315 	{ "INSTANCE", 10, NVME_LIST_INSTANCE, nvmeadm_list_common_ofmt_cb },
316 	{ "NAMESPACE", 10, NVME_LIST_NAMESPACE, nvmeadm_list_nsid_ofmt_cb },
317 	{ "DISK", 15, NVME_LIST_DISK, nvmeadm_list_nsid_ofmt_cb },
318 	{ "NS-STATE", 10, NVME_LIST_NS_STATE, nvmeadm_list_nsid_ofmt_cb },
319 	{ "CTRLPATH", 30, NVME_LIST_CTRLPATH, nvmeadm_list_common_ofmt_cb },
320 	{ "FORMAT", 12, NVME_LIST_NS_FORMAT, nvmeadm_list_nsid_ofmt_cb },
321 	{ "FMTID", 8, NVME_LIST_NS_FMTID, nvmeadm_list_nsid_ofmt_cb },
322 	{ "FMTDS", 8, NVME_LIST_NS_FMTDS, nvmeadm_list_nsid_ofmt_cb },
323 	{ "FMTMS", 8, NVME_LIST_NS_FMTMS, nvmeadm_list_nsid_ofmt_cb },
324 	{ NULL, 0, 0, NULL }
325 };
326 
327 typedef enum {
328 	NVME_LIST_LOGS_DEVICE,
329 	NVME_LIST_LOGS_NAME,
330 	NVME_LIST_LOGS_DESC,
331 	NVME_LIST_LOGS_SCOPE,
332 	NVME_LIST_LOGS_FIELDS,
333 	NVME_LIST_LOGS_CSI,
334 	NVME_LIST_LOGS_LID,
335 	NVME_LIST_LOGS_SIZE,
336 	NVME_LIST_LOGS_MINSIZE,
337 	NVME_LIST_LOGS_IMPL,
338 	NVME_LIST_LOGS_SOURCES,
339 	NVME_LIST_LOGS_KIND
340 } nvme_list_logs_ofmt_field_t;
341 
342 static const nvmeadm_bitstr_t nvmeadm_log_scopes[] = {
343 	{ NVME_LOG_SCOPE_CTRL, "controller" },
344 	{ NVME_LOG_SCOPE_NVM, "nvm" },
345 	{ NVME_LOG_SCOPE_NS, "namespace" }
346 };
347 
348 static const nvmeadm_bitstr_t nvmeadm_log_fields[] = {
349 	{ NVME_LOG_DISC_F_NEED_LSP, "lsp" },
350 	{ NVME_LOG_DISC_F_NEED_LSI, "lsi" },
351 	{ NVME_LOG_DISC_F_NEED_RAE, "rae" }
352 };
353 
354 static const nvmeadm_bitstr_t nvmeadm_log_sources[] = {
355 	{ NVME_LOG_DISC_S_SPEC, "spec" },
356 	{ NVME_LOG_DISC_S_ID_CTRL, "identify-controller" },
357 	{ NVME_LOG_DISC_S_DB, "internal-db" },
358 	{ NVME_LOG_DISC_S_CMD, "command" }
359 };
360 
361 static boolean_t
nvmeadm_list_logs_ofmt_cb(ofmt_arg_t * ofmt_arg,char * buf,uint_t buflen)362 nvmeadm_list_logs_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen)
363 {
364 	const nvmeadm_list_logs_ofmt_arg_t *list = ofmt_arg->ofmt_cbarg;
365 	const nvme_log_disc_t *disc = list->nlloa_disc;
366 	uint64_t alloc;
367 	size_t ret;
368 	nvme_log_size_kind_t kind;
369 
370 	switch (ofmt_arg->ofmt_id) {
371 	case NVME_LIST_LOGS_DEVICE:
372 		ret = strlcpy(buf, list->nlloa_name, buflen);
373 		break;
374 	case NVME_LIST_LOGS_NAME:
375 		ret = strlcpy(buf, nvme_log_disc_name(disc), buflen);
376 		break;
377 	case NVME_LIST_LOGS_DESC:
378 		ret = strlcpy(buf, nvme_log_disc_desc(disc), buflen);
379 		break;
380 	case NVME_LIST_LOGS_SCOPE:
381 		return (nvmeadm_bits_to_str(nvme_log_disc_scopes(disc),
382 		    nvmeadm_log_scopes, ARRAY_SIZE(nvmeadm_log_scopes), buf,
383 		    buflen));
384 	case NVME_LIST_LOGS_FIELDS:
385 		return (nvmeadm_bits_to_str(nvme_log_disc_fields(disc),
386 		    nvmeadm_log_fields, ARRAY_SIZE(nvmeadm_log_fields), buf,
387 		    buflen));
388 		break;
389 	case NVME_LIST_LOGS_CSI:
390 		switch (nvme_log_disc_csi(disc)) {
391 		case NVME_CSI_NVM:
392 			ret = strlcpy(buf, "nvm", buflen);
393 			break;
394 		case NVME_CSI_KV:
395 			ret = strlcpy(buf, "kv", buflen);
396 			break;
397 		case NVME_CSI_ZNS:
398 			ret = strlcpy(buf, "zns", buflen);
399 			break;
400 		default:
401 			ret = snprintf(buf, buflen, "unknown (0x%x)",
402 			    nvme_log_disc_csi(disc));
403 			break;
404 		}
405 		break;
406 	case NVME_LIST_LOGS_LID:
407 		ret = snprintf(buf, buflen, "0x%x", nvme_log_disc_lid(disc));
408 		break;
409 	case NVME_LIST_LOGS_SIZE:
410 	case NVME_LIST_LOGS_MINSIZE:
411 		kind = nvme_log_disc_size(disc, &alloc);
412 
413 		if (kind == NVME_LOG_SIZE_K_UNKNOWN) {
414 			return (B_FALSE);
415 		}
416 
417 		if (kind == NVME_LOG_SIZE_K_VAR &&
418 		    ofmt_arg->ofmt_id == NVME_LIST_LOGS_SIZE) {
419 			return (B_FALSE);
420 		}
421 
422 		ret = snprintf(buf, buflen, "%" PRIu64, alloc);
423 		break;
424 	case NVME_LIST_LOGS_IMPL:
425 		ret = strlcpy(buf, nvme_log_disc_impl(disc) ? "yes" : "no",
426 		    buflen);
427 		break;
428 	case NVME_LIST_LOGS_SOURCES:
429 		return (nvmeadm_bits_to_str(nvme_log_disc_sources(disc),
430 		    nvmeadm_log_sources, ARRAY_SIZE(nvmeadm_log_sources), buf,
431 		    buflen));
432 		break;
433 	case NVME_LIST_LOGS_KIND:
434 		switch (nvme_log_disc_kind(disc)) {
435 		case NVME_LOG_ID_MANDATORY:
436 			ret = strlcpy(buf, "mandatory", buflen);
437 			break;
438 		case NVME_LOG_ID_OPTIONAL:
439 			ret = strlcpy(buf, "optional", buflen);
440 			break;
441 		case NVME_LOG_ID_VENDOR_SPECIFIC:
442 			ret = strlcpy(buf, "vendor-specific", buflen);
443 			break;
444 		default:
445 			ret = snprintf(buf, buflen, "unknown (0x%x)",
446 			    nvme_log_disc_kind(disc));
447 			break;
448 		}
449 		break;
450 	default:
451 		warnx("internal programmer error: encountered unknown ofmt "
452 		    "argument id 0x%x", ofmt_arg->ofmt_id);
453 		abort();
454 	}
455 
456 	return (ret < buflen);
457 }
458 
459 const char *nvmeadm_list_logs_fields = "device,name,scope,fields,desc";
460 const char *nvmeadm_list_logs_fields_impl = "device,name,scope,impl,fields,"
461 	"desc";
462 const ofmt_field_t nvmeadm_list_logs_ofmt[] = {
463 	{ "DEVICE", 8, NVME_LIST_LOGS_DEVICE, nvmeadm_list_logs_ofmt_cb },
464 	{ "NAME", 18, NVME_LIST_LOGS_NAME, nvmeadm_list_logs_ofmt_cb },
465 	{ "DESC", 30, NVME_LIST_LOGS_DESC, nvmeadm_list_logs_ofmt_cb },
466 	{ "SCOPE", 14, NVME_LIST_LOGS_SCOPE, nvmeadm_list_logs_ofmt_cb },
467 	{ "FIELDS", 10, NVME_LIST_LOGS_FIELDS, nvmeadm_list_logs_ofmt_cb },
468 	{ "CSI", 6, NVME_LIST_LOGS_CSI, nvmeadm_list_logs_ofmt_cb },
469 	{ "LID", 6, NVME_LIST_LOGS_LID, nvmeadm_list_logs_ofmt_cb },
470 	{ "SIZE", 10, NVME_LIST_LOGS_SIZE, nvmeadm_list_logs_ofmt_cb },
471 	{ "MINSIZE", 10, NVME_LIST_LOGS_MINSIZE, nvmeadm_list_logs_ofmt_cb },
472 	{ "IMPL", 6, NVME_LIST_LOGS_IMPL, nvmeadm_list_logs_ofmt_cb },
473 	{ "SOURCES", 20, NVME_LIST_LOGS_SOURCES, nvmeadm_list_logs_ofmt_cb },
474 	{ "KIND", 16, NVME_LIST_LOGS_KIND, nvmeadm_list_logs_ofmt_cb },
475 	{ NULL, 0, 0, NULL }
476 };
477 
478 typedef enum {
479 	NVME_LIST_FEATS_DEVICE,
480 	NVME_LIST_FEATS_SHORT,
481 	NVME_LIST_FEATS_SPEC,
482 	NVME_LIST_FEATS_FID,
483 	NVME_LIST_FEATS_SCOPE,
484 	NVME_LIST_FEATS_KIND,
485 	NVME_LIST_FEATS_CSI,
486 	NVME_LIST_FEATS_FLAGS,
487 	NVME_LIST_FEATS_GET_IN,
488 	NVME_LIST_FEATS_SET_IN,
489 	NVME_LIST_FEATS_GET_OUT,
490 	NVME_LIST_FEATS_SET_OUT,
491 	NVME_LIST_FEATS_DATA_LEN,
492 	NVME_LIST_FEATS_IMPL
493 } nvme_list_features_ofmt_field_t;
494 
495 static const nvmeadm_bitstr_t nvmeadm_feat_scopes[] = {
496 	{ NVME_FEAT_SCOPE_CTRL, "controller" },
497 	{ NVME_FEAT_SCOPE_NS, "namespace" }
498 };
499 
500 static const nvmeadm_bitstr_t nvmeadm_feat_get_in[] = {
501 	{ NVME_GET_FEAT_F_CDW11, "cdw11" },
502 	{ NVME_GET_FEAT_F_DATA, "data" },
503 	{ NVME_GET_FEAT_F_NSID, "nsid" }
504 };
505 
506 static const nvmeadm_bitstr_t nvmeadm_feat_set_in[] = {
507 	{ NVME_SET_FEAT_F_CDW11, "cdw11" },
508 	{ NVME_SET_FEAT_F_CDW12, "cdw12" },
509 	{ NVME_SET_FEAT_F_CDW13, "cdw13" },
510 	{ NVME_SET_FEAT_F_CDW14, "cdw14" },
511 	{ NVME_SET_FEAT_F_CDW15, "cdw15" },
512 	{ NVME_SET_FEAT_F_DATA, "data" },
513 	{ NVME_SET_FEAT_F_NSID, "nsid" }
514 };
515 
516 static const nvmeadm_bitstr_t nvmeadm_feat_output[] = {
517 	{ NVME_FEAT_OUTPUT_CDW0, "cdw0" },
518 	{ NVME_FEAT_OUTPUT_DATA, "data" }
519 };
520 
521 static const nvmeadm_bitstr_t nvmeadm_feat_flags[] = {
522 	{ NVME_FEAT_F_GET_BCAST_NSID, "get-bcastns" },
523 	{ NVME_FEAT_F_SET_BCAST_NSID, "set-bcastns" }
524 };
525 
526 static const nvmeadm_bitstr_t nvmeadm_feat_csi[] = {
527 	{ NVME_FEAT_CSI_NVM, "nvm" }
528 };
529 
530 static boolean_t
nvmeadm_list_features_ofmt_cb(ofmt_arg_t * ofmt_arg,char * buf,uint_t buflen)531 nvmeadm_list_features_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen)
532 {
533 	const nvmeadm_list_features_ofmt_arg_t *nlfo = ofmt_arg->ofmt_cbarg;
534 	const nvme_feat_disc_t *feat = nlfo->nlfoa_feat;
535 	size_t ret;
536 
537 	switch (ofmt_arg->ofmt_id) {
538 	case NVME_LIST_FEATS_DEVICE:
539 		ret = strlcpy(buf, nlfo->nlfoa_name, buflen);
540 		break;
541 	case NVME_LIST_FEATS_SHORT:
542 		ret = strlcpy(buf, nvme_feat_disc_short(feat), buflen);
543 		break;
544 	case NVME_LIST_FEATS_SPEC:
545 		ret = strlcpy(buf, nvme_feat_disc_spec(feat), buflen);
546 		break;
547 	case NVME_LIST_FEATS_FID:
548 		ret = snprintf(buf, buflen, "0x%x", nvme_feat_disc_fid(feat));
549 		break;
550 	case NVME_LIST_FEATS_SCOPE:
551 		return (nvmeadm_bits_to_str(nvme_feat_disc_scope(feat),
552 		    nvmeadm_feat_scopes, ARRAY_SIZE(nvmeadm_feat_scopes), buf,
553 		    buflen));
554 	case NVME_LIST_FEATS_KIND:
555 		switch (nvme_feat_disc_kind(feat)) {
556 		case NVME_FEAT_MANDATORY:
557 			ret = strlcpy(buf, "mandatory", buflen);
558 			break;
559 		case NVME_FEAT_OPTIONAL:
560 			ret = strlcpy(buf, "optional", buflen);
561 			break;
562 		case NVME_FEAT_VENDOR_SPECIFIC:
563 			ret = strlcpy(buf, "vendor-specific", buflen);
564 			break;
565 		default:
566 			ret = snprintf(buf, buflen, "unknown (0x%x)",
567 			    nvme_feat_disc_kind(feat));
568 			break;
569 		}
570 		break;
571 	case NVME_LIST_FEATS_CSI:
572 		if (nvme_feat_disc_csi(feat) == NVME_FEAT_CSI_NONE) {
573 			ret = strlcpy(buf, "none", buflen);
574 			break;
575 		}
576 
577 		return (nvmeadm_bits_to_str(nvme_feat_disc_csi(feat),
578 		    nvmeadm_feat_csi, ARRAY_SIZE(nvmeadm_feat_csi), buf,
579 		    buflen));
580 	case NVME_LIST_FEATS_FLAGS:
581 		return (nvmeadm_bits_to_str(nvme_feat_disc_flags(feat),
582 		    nvmeadm_feat_flags, ARRAY_SIZE(nvmeadm_feat_flags), buf,
583 		    buflen));
584 	case NVME_LIST_FEATS_GET_IN:
585 		return (nvmeadm_bits_to_str(nvme_feat_disc_fields_get(feat),
586 		    nvmeadm_feat_get_in, ARRAY_SIZE(nvmeadm_feat_get_in), buf,
587 		    buflen));
588 	case NVME_LIST_FEATS_SET_IN:
589 		return (nvmeadm_bits_to_str(nvme_feat_disc_fields_set(feat),
590 		    nvmeadm_feat_set_in, ARRAY_SIZE(nvmeadm_feat_set_in), buf,
591 		    buflen));
592 	case NVME_LIST_FEATS_GET_OUT:
593 		return (nvmeadm_bits_to_str(nvme_feat_disc_output_get(feat),
594 		    nvmeadm_feat_output, ARRAY_SIZE(nvmeadm_feat_output), buf,
595 		    buflen));
596 	case NVME_LIST_FEATS_SET_OUT:
597 		return (nvmeadm_bits_to_str(nvme_feat_disc_output_set(feat),
598 		    nvmeadm_feat_output, ARRAY_SIZE(nvmeadm_feat_output), buf,
599 		    buflen));
600 	case NVME_LIST_FEATS_DATA_LEN:
601 		if (nvme_feat_disc_data_size(feat) == 0) {
602 			ret = strlcpy(buf, "-", buflen);
603 		} else {
604 			ret = snprintf(buf, buflen, "%" PRIu64,
605 			    nvme_feat_disc_data_size(feat));
606 		}
607 		break;
608 	case NVME_LIST_FEATS_IMPL:
609 		switch (nvme_feat_disc_impl(feat)) {
610 		case NVME_FEAT_IMPL_UNKNOWN:
611 			ret = strlcpy(buf, "unknown", buflen);
612 			break;
613 		case NVME_FEAT_IMPL_UNSUPPORTED:
614 			ret = strlcpy(buf, "no", buflen);
615 			break;
616 		case NVME_FEAT_IMPL_SUPPORTED:
617 			ret = strlcpy(buf, "yes", buflen);
618 			break;
619 		default:
620 			ret = snprintf(buf, buflen, "unknown (0x%x)",
621 			    nvme_feat_disc_impl(feat));
622 			break;
623 		}
624 		break;
625 	default:
626 		warnx("internal programmer error: encountered unknown ofmt "
627 		    "argument id 0x%x", ofmt_arg->ofmt_id);
628 		abort();
629 	}
630 
631 	return (ret < buflen);
632 }
633 
634 const char *nvmeadm_list_features_fields = "device,short,scope,impl,spec";
635 const ofmt_field_t nvmeadm_list_features_ofmt[] = {
636 	{ "DEVICE", 8, NVME_LIST_FEATS_DEVICE, nvmeadm_list_features_ofmt_cb },
637 	{ "SHORT", 14, NVME_LIST_FEATS_SHORT, nvmeadm_list_features_ofmt_cb },
638 	{ "SPEC", 30, NVME_LIST_FEATS_SPEC, nvmeadm_list_features_ofmt_cb },
639 	{ "FID", 6, NVME_LIST_FEATS_FID, nvmeadm_list_features_ofmt_cb },
640 	{ "SCOPE", 14, NVME_LIST_FEATS_SCOPE, nvmeadm_list_features_ofmt_cb },
641 	{ "KIND", 16, NVME_LIST_FEATS_KIND, nvmeadm_list_features_ofmt_cb },
642 	{ "CSI", 6, NVME_LIST_FEATS_CSI, nvmeadm_list_features_ofmt_cb },
643 	{ "FLAGS", 14, NVME_LIST_FEATS_FLAGS, nvmeadm_list_features_ofmt_cb },
644 	{ "GET-IN", 14, NVME_LIST_FEATS_GET_IN, nvmeadm_list_features_ofmt_cb },
645 	{ "SET-IN", 14, NVME_LIST_FEATS_SET_IN, nvmeadm_list_features_ofmt_cb },
646 	{ "GET-OUT", 14, NVME_LIST_FEATS_GET_OUT,
647 	    nvmeadm_list_features_ofmt_cb },
648 	{ "SET-OUT", 14, NVME_LIST_FEATS_SET_OUT,
649 	    nvmeadm_list_features_ofmt_cb },
650 	{ "DATALEN", 8, NVME_LIST_FEATS_DATA_LEN,
651 	    nvmeadm_list_features_ofmt_cb },
652 	{ "IMPL", 8, NVME_LIST_FEATS_IMPL, nvmeadm_list_features_ofmt_cb },
653 	{ NULL, 0, 0, NULL }
654 };
655