xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_print.c (revision 1f0c5e612d8dfd7d1a8f4487cef753357b9909f5)
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 2016 Nexenta Systems, Inc.
14  */
15 
16 /*
17  * functions for printing of NVMe data structures and their members
18  */
19 
20 #include <sys/byteorder.h>
21 #include <sys/types.h>
22 #include <inttypes.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <strings.h>
26 #include <stdarg.h>
27 #include <err.h>
28 #include <assert.h>
29 
30 #include "nvmeadm.h"
31 
32 static int nvme_strlen(const char *, int);
33 
34 static void nvme_print_str(int, char *, int, const char *, int);
35 static void nvme_print_double(int, char *, double, int, char *);
36 static void nvme_print_uint64(int, char *, uint64_t, const char *, char *);
37 static void nvme_print_uint128(int, char *, nvme_uint128_t, char *, int, int);
38 static void nvme_print_bit(int, char *, int, char *, char *);
39 
40 #define	ARRAYSIZE(x)		(sizeof (x) / sizeof (*(x)))
41 
42 static const char *generic_status_codes[] = {
43 	"Successful Completion",
44 	"Invalid Command Opcode",
45 	"Invalid Field in Command",
46 	"Command ID Conflict",
47 	"Data Transfer Error",
48 	"Commands Aborted due to Power Loss Notification",
49 	"Internal Error",
50 	"Command Abort Requested",
51 	"Command Aborted due to SQ Deletion",
52 	"Command Aborted due to Failed Fused Command",
53 	"Command Aborted due to Missing Fused Command",
54 	"Invalid Namespace or Format",
55 	"Command Sequence Error",
56 	/* NVMe 1.1 */
57 	"Invalid SGL Segment Descriptor",
58 	"Invalid Number of SGL Descriptors",
59 	"Data SGL Length Invalid",
60 	"Metadata SGL Length Invalid",
61 	"SGL Descriptor Type Invalid",
62 	/* NVMe 1.2 */
63 	"Invalid Use of Controller Memory Buffer",
64 	"PRP Offset Invalid",
65 	"Atomic Write Unit Exceeded"
66 };
67 
68 static const char *specific_status_codes[] = {
69 	"Completion Queue Invalid",
70 	"Invalid Queue Identifier",
71 	"Invalid Queue Size",
72 	"Abort Command Limit Exceeded",
73 	"Reserved",
74 	"Asynchronous Event Request Limit Exceeded",
75 	"Invalid Firmware Slot",
76 	"Invalid Firmware Image",
77 	"Invalid Interrupt Vector",
78 	"Invalid Log Page",
79 	"Invalid Format",
80 	"Firmware Activation Requires Conventional Reset",
81 	"Invalid Queue Deletion",
82 	/* NVMe 1.1 */
83 	"Feature Identifier Not Saveable",
84 	"Feature Not Changeable",
85 	"Feature Not Namespace Specific",
86 	"Firmware Activation Requires NVM Subsystem Reset",
87 	/* NVMe 1.2 */
88 	"Firmware Activation Requires Reset",
89 	"Firmware Activation Requires Maximum Time Violation",
90 	"Firmware Activation Prohibited",
91 	"Overlapping Range",
92 	"Namespace Insufficient Capacity",
93 	"Namespace Identifier Unavailable",
94 	"Reserved",
95 	"Namespace Already Attached",
96 	"Namespace Is Private",
97 	"Namespace Not Attached",
98 	"Thin Provisioning Not Supported",
99 	"Controller List Invalid"
100 };
101 
102 static const char *generic_nvm_status_codes[] = {
103 	"LBA Out Of Range",
104 	"Capacity Exceeded",
105 	"Namespace Not Ready",
106 	/* NVMe 1.1 */
107 	"Reservation Conflict",
108 	/* NVMe 1.2 */
109 	"Format In Progress",
110 };
111 
112 static const char *specific_nvm_status_codes[] = {
113 	"Conflicting Attributes",
114 	"Invalid Protection Information",
115 	"Attempted Write to Read Only Range"
116 };
117 
118 static const char *media_nvm_status_codes[] = {
119 	"Write Fault",
120 	"Unrecovered Read Error",
121 	"End-to-End Guard Check Error",
122 	"End-to-End Application Tag Check Error",
123 	"End-to-End Reference Tag Check Error",
124 	"Compare Failure",
125 	"Access Denied",
126 	/* NVMe 1.2 */
127 	"Deallocated or Unwritten Logical Block"
128 };
129 
130 static const char *status_code_types[] = {
131 	"Generic Command Status",
132 	"Command Specific Status",
133 	"Media Errors",
134 	"Reserved",
135 	"Reserved",
136 	"Reserved",
137 	"Reserved",
138 	"Vendor Specific"
139 };
140 
141 static const char *lbaf_relative_performance[] = {
142 	"Best", "Better", "Good", "Degraded"
143 };
144 
145 static const char *lba_range_types[] = {
146 	"Reserved", "Filesystem", "RAID", "Cache", "Page/Swap File"
147 };
148 
149 /*
150  * nvme_print
151  *
152  * This function prints a string indented by the specified number of spaces,
153  * optionally followed by the specified index if it is >= 0. If a format string
154  * is specified, a single colon and the required number of spaces for alignment
155  * are printed before the format string and any remaining arguments are passed
156  * vprintf.
157  *
158  * NVME_PRINT_ALIGN was chosen so that all values will be lined up nicely even
159  * for the longest name at its default indentation.
160  */
161 
162 #define	NVME_PRINT_ALIGN	43
163 
164 void
165 nvme_print(int indent, char *name, int index, const char *fmt, ...)
166 {
167 	int align = NVME_PRINT_ALIGN - (indent + strlen(name) + 1);
168 	va_list ap;
169 
170 	if (index >= 0)
171 		align -= snprintf(NULL, 0, " %d", index);
172 
173 	if (align < 0)
174 		align = 0;
175 
176 	va_start(ap, fmt);
177 
178 	(void) printf("%*s%s", indent, "", name);
179 
180 	if (index >= 0)
181 		(void) printf(" %d", index);
182 
183 	if (fmt != NULL) {
184 		(void) printf(": %*s", align, "");
185 		(void) vprintf(fmt, ap);
186 	}
187 
188 	(void) printf("\n");
189 	va_end(ap);
190 }
191 
192 /*
193  * nvme_strlen -- return length of string without trailing whitespace
194  */
195 static int
196 nvme_strlen(const char *str, int len)
197 {
198 	if (len < 0)
199 		return (0);
200 
201 	while (str[--len] == ' ')
202 		;
203 
204 	return (++len);
205 }
206 
207 /*
208  * nvme_print_str -- print a string up to the specified length
209  */
210 static void
211 nvme_print_str(int indent, char *name, int index, const char *value, int len)
212 {
213 	if (len == 0)
214 		len = strlen(value);
215 
216 	nvme_print(indent, name, index, "%.*s", nvme_strlen(value, len), value);
217 }
218 
219 /*
220  * nvme_print_double -- print a double up to a specified number of places with
221  * optional unit
222  */
223 static void
224 nvme_print_double(int indent, char *name, double value, int places, char *unit)
225 {
226 	if (unit == NULL)
227 		unit = "";
228 
229 	nvme_print(indent, name, -1, "%.*g%s", places, value, unit);
230 }
231 
232 /*
233  * nvme_print_uint64 -- print uint64_t with optional unit in decimal or another
234  * format specified
235  */
236 static void
237 nvme_print_uint64(int indent, char *name, uint64_t value, const char *fmt,
238     char *unit)
239 {
240 	char *tmp_fmt;
241 
242 	if (unit == NULL)
243 		unit = "";
244 
245 	if (fmt == NULL)
246 		fmt = "%"PRId64;
247 
248 	if (asprintf(&tmp_fmt, "%s%%s", fmt) < 0)
249 		err(-1, "nvme_print_uint64()");
250 
251 	nvme_print(indent, name, -1, tmp_fmt, value, unit);
252 
253 	free(tmp_fmt);
254 }
255 
256 /*
257  * nvme_print_uint128 -- print a 128bit uint with optional unit, after applying
258  * binary and/or decimal shifting
259  */
260 static void
261 nvme_print_uint128(int indent, char *name, nvme_uint128_t value, char *unit,
262     int scale_bits, int scale_tens)
263 {
264 	const char hex[] = "0123456789abcdef";
265 	uint8_t o[(128 + scale_bits) / 3];
266 	char p[sizeof (o) * 2];
267 	char *pp = &p[0];
268 	int i, x;
269 	uint64_t rem = 0;
270 
271 	if (unit == NULL)
272 		unit = "";
273 
274 	/*
275 	 * Don't allow binary shifting by more than 64 bits to keep the
276 	 * arithmetic simple. Also limit decimal shifting based on the size
277 	 * of any possible remainder from binary shifting.
278 	 */
279 	assert(scale_bits <= 64);
280 	assert(scale_tens <= (64 - scale_bits) / 3);
281 
282 	bzero(o, sizeof (o));
283 	bzero(p, sizeof (p));
284 
285 	/*
286 	 * Convert the two 64-bit numbers into a series of BCD digits using
287 	 * a double-dabble algorithm. By using more or less iterations than
288 	 * 128 we can do a binary shift in either direction.
289 	 */
290 	for (x = 0; x != 128 - scale_bits; x++) {
291 		for (i = 0; i != sizeof (o); i++) {
292 			if ((o[i] & 0xf0) > 0x40)
293 				o[i] += 0x30;
294 
295 			if ((o[i] & 0xf) > 4)
296 				o[i] += 3;
297 		}
298 
299 		for (i = 0; i != sizeof (o) - 1; i++)
300 			o[i] = (o[i] << 1) + (o[i+1] >> 7);
301 
302 		o[i] = (o[i] << 1) + (value.hi >> 63);
303 
304 		value.hi = (value.hi << 1) + (value.lo >> 63);
305 		value.lo = (value.lo << 1);
306 	}
307 
308 	/*
309 	 * If we're supposed to do a decimal left shift (* 10^x), too,
310 	 * calculate the remainder of the previous binary shift operation.
311 	 */
312 	if (scale_tens > 0) {
313 		rem = value.hi >> (64 - scale_bits);
314 
315 		for (i = 0; i != scale_tens; i++)
316 			rem *= 10;
317 
318 		rem >>= scale_bits;
319 	}
320 
321 	/*
322 	 * Construct the decimal number for printing. Skip leading zeros.
323 	 */
324 	for (i = 0; i < sizeof (o); i++)
325 		if (o[i] != 0)
326 			break;
327 
328 	if (i == sizeof (o)) {
329 		/*
330 		 * The converted number is 0. Just print the calculated
331 		 * remainder and return.
332 		 */
333 		nvme_print(indent, name, -1, "%"PRId64"%s", rem, unit);
334 		return;
335 	} else {
336 		if (o[i] > 0xf)
337 			*pp++ = hex[o[i] >> 4];
338 
339 		*pp++ = hex[o[i] & 0xf];
340 
341 		for (i++; i < sizeof (o); i++) {
342 			*pp++ = hex[o[i] >> 4];
343 			*pp++ = hex[o[i] & 0xf];
344 		}
345 	}
346 
347 	/*
348 	 * For negative decimal scaling, use the printf precision specifier to
349 	 * truncate the results according to the requested decimal scaling. For
350 	 * positive decimal scaling we print the remainder padded with 0.
351 	 */
352 	nvme_print(indent, name, -1, "%.*s%0.*"PRId64"%s",
353 	    strlen(p) + scale_tens, p,
354 	    scale_tens > 0 ? scale_tens : 0, rem,
355 	    unit);
356 }
357 
358 /*
359  * nvme_print_bit -- print a bit with optional names for both states
360  */
361 static void
362 nvme_print_bit(int indent, char *name, int value, char *s_true, char *s_false)
363 {
364 	if (s_true == NULL)
365 		s_true = "supported";
366 	if (s_false == NULL)
367 		s_false = "unsupported";
368 
369 	nvme_print(indent, name, -1, "%s", value ? s_true : s_false);
370 }
371 
372 /*
373  * nvme_print_ctrl_summary -- print a 1-line summary of the IDENTIFY CONTROLLER
374  * data structure
375  */
376 void
377 nvme_print_ctrl_summary(nvme_identify_ctrl_t *idctl, nvme_version_t *version)
378 {
379 	(void) printf("model: %.*s, serial: %.*s, FW rev: %.*s, NVMe v%d.%d\n",
380 	    nvme_strlen(idctl->id_model, sizeof (idctl->id_model)),
381 	    idctl->id_model,
382 	    nvme_strlen(idctl->id_serial, sizeof (idctl->id_serial)),
383 	    idctl->id_serial,
384 	    nvme_strlen(idctl->id_fwrev, sizeof (idctl->id_fwrev)),
385 	    idctl->id_fwrev,
386 	    version->v_major, version->v_minor);
387 }
388 
389 /*
390  * nvme_print_nsid_summary -- print a 1-line summary of the IDENTIFY NAMESPACE
391  * data structure
392  */
393 void
394 nvme_print_nsid_summary(nvme_identify_nsid_t *idns)
395 {
396 	int bsize = 1 << idns->id_lbaf[idns->id_flbas.lba_format].lbaf_lbads;
397 
398 	(void) printf("Size = %"PRId64" MB, "
399 	    "Capacity = %"PRId64" MB, "
400 	    "Used = %"PRId64" MB\n",
401 	    idns->id_nsize * bsize / 1024 / 1024,
402 	    idns->id_ncap * bsize / 1024 / 1024,
403 	    idns->id_nuse * bsize / 1024 / 1024);
404 
405 }
406 
407 /*
408  * nvme_print_identify_ctrl
409  *
410  * This function pretty-prints the structure returned by the IDENTIFY CONTROLLER
411  * command.
412  */
413 void
414 nvme_print_identify_ctrl(nvme_identify_ctrl_t *idctl,
415     nvme_capabilities_t *cap, nvme_version_t *version)
416 {
417 	int i;
418 
419 	nvme_print(0, "Identify Controller", -1, NULL);
420 	nvme_print(2, "Controller Capabilities and Features", -1, NULL);
421 	nvme_print_str(4, "Model", -1,
422 	    idctl->id_model, sizeof (idctl->id_model));
423 	nvme_print_str(4, "Serial", -1,
424 	    idctl->id_serial, sizeof (idctl->id_serial));
425 	nvme_print_str(4, "Firmware Revision", -1,
426 	    idctl->id_fwrev, sizeof (idctl->id_fwrev));
427 	if (verbose) {
428 		nvme_print_uint64(4, "PCI vendor ID",
429 		    idctl->id_vid, "0x%0.4"PRIx64, NULL);
430 		nvme_print_uint64(4, "subsystem vendor ID",
431 		    idctl->id_ssvid, "0x%0.4"PRIx64, NULL);
432 		nvme_print_uint64(4, "Recommended Arbitration Burst",
433 		    idctl->id_rab, NULL, NULL);
434 		nvme_print(4, "Vendor IEEE OUI", -1, "%0.2X-%0.2X-%0.2X",
435 		    idctl->id_oui[0], idctl->id_oui[1], idctl->id_oui[2]);
436 	}
437 	nvme_print(4, "Multi-Interface Capabilities", -1, NULL);
438 	nvme_print_bit(6, "Multiple PCI Express ports",
439 	    idctl->id_mic.m_multi_pci, NULL, NULL);
440 
441 	if (NVME_VERSION_ATLEAST(version, 1, 1)) {
442 		nvme_print_bit(6, "Multiple Controllers",
443 		    idctl->id_mic.m_multi_ctrl, NULL, NULL);
444 		nvme_print_bit(6, "Is SR-IOV virtual function",
445 		    idctl->id_mic.m_sr_iov, "yes", "no");
446 	}
447 	if (idctl->id_mdts > 0)
448 		nvme_print_uint64(4, "Maximum Data Transfer Size",
449 		    (1 << idctl->id_mdts) * cap->mpsmin / 1024, NULL, "kB");
450 	else
451 		nvme_print_str(4, "Maximum Data Transfer Size", -1,
452 		    "unlimited", 0);
453 
454 	if (NVME_VERSION_ATLEAST(version, 1, 1)) {
455 		nvme_print_uint64(4, "Unique Controller Identifier",
456 		    idctl->id_cntlid, "0x%0.4"PRIx64, NULL);
457 	}
458 
459 	nvme_print(2, "Admin Command Set Attributes", -1, NULL);
460 	nvme_print(4, "Optional Admin Command Support", -1, NULL);
461 	nvme_print_bit(6, "Security Send & Receive",
462 	    idctl->id_oacs.oa_security, NULL, NULL);
463 	nvme_print_bit(6, "Format NVM",
464 	    idctl->id_oacs.oa_format, NULL, NULL);
465 	nvme_print_bit(6, "Firmware Activate & Download",
466 	    idctl->id_oacs.oa_firmware, NULL, NULL);
467 	if (verbose) {
468 		nvme_print_uint64(4, "Abort Command Limit",
469 		    (uint16_t)idctl->id_acl + 1, NULL, NULL);
470 		nvme_print_uint64(4, "Asynchronous Event Request Limit",
471 		    (uint16_t)idctl->id_aerl + 1, NULL, NULL);
472 	}
473 	nvme_print(4, "Firmware Updates", -1, NULL);
474 	nvme_print_bit(6, "Firmware Slot 1",
475 	    idctl->id_frmw.fw_readonly, "read-only", "writable");
476 	nvme_print_uint64(6, "No. of Firmware Slots",
477 	    idctl->id_frmw.fw_nslot, NULL, NULL);
478 	nvme_print(2, "Log Page Attributes", -1, NULL);
479 	nvme_print_bit(6, "per Namespace SMART/Health info",
480 	    idctl->id_lpa.lp_smart, NULL, NULL);
481 	nvme_print_uint64(4, "Error Log Page Entries",
482 	    (uint16_t)idctl->id_elpe + 1, NULL, NULL);
483 	nvme_print_uint64(4, "Number of Power States",
484 	    (uint16_t)idctl->id_npss + 1, NULL, NULL);
485 	if (verbose) {
486 		nvme_print_bit(4, "Admin Vendor-specific Command Format",
487 		    idctl->id_avscc.av_spec, "standard", "vendor-specific");
488 	}
489 
490 	if (NVME_VERSION_ATLEAST(version, 1, 1)) {
491 		nvme_print_bit(4, "Autonomous Power State Transitions",
492 		    idctl->id_apsta.ap_sup, NULL, NULL);
493 	}
494 
495 	nvme_print(2, "NVM Command Set Attributes", -1, NULL);
496 	if (verbose) {
497 		nvme_print(4, "Submission Queue Entry Size", -1,
498 		    "min %d, max %d",
499 		    1 << idctl->id_sqes.qes_min, 1 << idctl->id_sqes.qes_max);
500 		nvme_print(4, "Completion Queue Entry Size", -1,
501 		    "min %d, max %d",
502 		    1 << idctl->id_cqes.qes_min, 1 << idctl->id_cqes.qes_max);
503 	}
504 	nvme_print_uint64(4, "Number of Namespaces",
505 	    idctl->id_nn, NULL, NULL);
506 	nvme_print(4, "Optional NVM Command Support", -1, NULL);
507 	nvme_print_bit(6, "Compare",
508 	    idctl->id_oncs.on_compare, NULL, NULL);
509 	nvme_print_bit(6, "Write Uncorrectable",
510 	    idctl->id_oncs.on_wr_unc, NULL, NULL);
511 	nvme_print_bit(6, "Dataset Management",
512 	    idctl->id_oncs.on_dset_mgmt, NULL, NULL);
513 
514 	if (NVME_VERSION_ATLEAST(version, 1, 1)) {
515 		nvme_print_bit(6, "Write Zeros",
516 		    idctl->id_oncs.on_wr_zero, NULL, NULL);
517 		nvme_print_bit(6, "Save/Select in Get/Set Features",
518 		    idctl->id_oncs.on_save, NULL, NULL);
519 		nvme_print_bit(6, "Reservations",
520 		    idctl->id_oncs.on_reserve, NULL, NULL);
521 	}
522 
523 	nvme_print(4, "Fused Operation Support", -1, NULL);
524 	nvme_print_bit(6, "Compare and Write",
525 	    idctl->id_fuses.f_cmp_wr, NULL, NULL);
526 	nvme_print(4, "Format NVM Attributes", -1, NULL);
527 	nvme_print_bit(6, "per Namespace Format",
528 	    idctl->id_fna.fn_format == 0, NULL, NULL);
529 	nvme_print_bit(6, "per Namespace Secure Erase",
530 	    idctl->id_fna.fn_sec_erase == 0, NULL, NULL);
531 	nvme_print_bit(6, "Cryptographic Erase",
532 	    idctl->id_fna.fn_crypt_erase, NULL, NULL);
533 	nvme_print_bit(4, "Volatile Write Cache",
534 	    idctl->id_vwc.vwc_present, "present", "not present");
535 	nvme_print_uint64(4, "Atomic Write Unit Normal",
536 	    (uint32_t)idctl->id_awun + 1, NULL,
537 	    idctl->id_awun == 0 ? " block" : " blocks");
538 	nvme_print_uint64(4, "Atomic Write Unit Power Fail",
539 	    (uint32_t)idctl->id_awupf + 1, NULL,
540 	    idctl->id_awupf == 0 ? " block" : " blocks");
541 
542 	if (verbose != 0)
543 		nvme_print_bit(4, "NVM Vendor-specific Command Format",
544 		    idctl->id_nvscc.nv_spec, "standard", "vendor-specific");
545 
546 	if (NVME_VERSION_ATLEAST(version, 1, 1)) {
547 		nvme_print_uint64(4, "Atomic Compare & Write Size",
548 		    (uint32_t)idctl->id_acwu + 1, NULL,
549 		    idctl->id_acwu == 0 ? " block" : " blocks");
550 		nvme_print(4, "SGL Support", -1, NULL);
551 		nvme_print_bit(6, "SGLs in NVM commands",
552 		    idctl->id_sgls.sgl_sup, NULL, NULL);
553 		nvme_print_bit(6, "SGL Bit Bucket Descriptor",
554 		    idctl->id_sgls.sgl_bucket, NULL, NULL);
555 	}
556 
557 	for (i = 0; i != idctl->id_npss + 1; i++) {
558 		double scale = 0.01;
559 		double power = 0;
560 		int places = 2;
561 		char *unit = "W";
562 
563 		if (NVME_VERSION_ATLEAST(version, 1, 1) &&
564 		    idctl->id_psd[i].psd_mps == 1) {
565 			scale = 0.0001;
566 			places = 4;
567 		}
568 
569 		power = (double)idctl->id_psd[i].psd_mp * scale;
570 		if (power < 1.0) {
571 			power *= 1000.0;
572 			unit = "mW";
573 		}
574 
575 		nvme_print(4, "Power State Descriptor", i, NULL);
576 		nvme_print_double(6, "Maximum Power", power, places, unit);
577 		nvme_print_bit(6, "Non-Operational State",
578 		    idctl->id_psd[i].psd_nops, "yes", "no");
579 		nvme_print_uint64(6, "Entry Latency",
580 		    idctl->id_psd[i].psd_enlat, NULL, "us");
581 		nvme_print_uint64(6, "Exit Latency",
582 		    idctl->id_psd[i].psd_exlat, NULL, "us");
583 		nvme_print_uint64(6, "Relative Read Throughput (0 = best)",
584 		    idctl->id_psd[i].psd_rrt, NULL, NULL);
585 		nvme_print_uint64(6, "Relative Read Latency (0 = best)",
586 		    idctl->id_psd[i].psd_rrl, NULL, NULL);
587 		nvme_print_uint64(6, "Relative Write Throughput (0 = best)",
588 		    idctl->id_psd[i].psd_rwt, NULL, NULL);
589 		nvme_print_uint64(6, "Relative Write Latency (0 = best)",
590 		    idctl->id_psd[i].psd_rwl, NULL, NULL);
591 	}
592 }
593 
594 /*
595  * nvme_print_identify_nsid
596  *
597  * This function pretty-prints the structure returned by the IDENTIFY NAMESPACE
598  * command.
599  */
600 void
601 nvme_print_identify_nsid(nvme_identify_nsid_t *idns, nvme_version_t *version)
602 {
603 	int bsize = 1 << idns->id_lbaf[idns->id_flbas.lba_format].lbaf_lbads;
604 	int i;
605 
606 	nvme_print(0, "Identify Namespace", -1, NULL);
607 	nvme_print(2, "Namespace Capabilities and Features", -1, NULL);
608 	nvme_print_uint64(4, "Namespace Size",
609 	    idns->id_nsize * bsize / 1024 / 1024, NULL, "MB");
610 	nvme_print_uint64(4, "Namespace Capacity",
611 	    idns->id_ncap * bsize / 1024 / 1024, NULL, "MB");
612 	nvme_print_uint64(4, "Namespace Utilization",
613 	    idns->id_nuse * bsize / 1024 / 1024, NULL, "MB");
614 	nvme_print(4, "Namespace Features", -1, NULL);
615 	nvme_print_bit(6, "Thin Provisioning",
616 	    idns->id_nsfeat.f_thin, NULL, NULL);
617 	nvme_print_uint64(4, "Number of LBA Formats",
618 	    (uint16_t)idns->id_nlbaf + 1, NULL, NULL);
619 	nvme_print(4, "Formatted LBA Size", -1, NULL);
620 	nvme_print_uint64(6, "LBA Format",
621 	    (uint16_t)idns->id_flbas.lba_format, NULL, NULL);
622 	nvme_print_bit(6, "Extended Data LBA",
623 	    idns->id_flbas.lba_extlba, "yes", "no");
624 	nvme_print(4, "Metadata Capabilities", -1, NULL);
625 	nvme_print_bit(6, "Extended Data LBA",
626 	    idns->id_mc.mc_extlba, NULL, NULL);
627 	nvme_print_bit(6, "Separate Metadata",
628 	    idns->id_mc.mc_separate, NULL, NULL);
629 	nvme_print(4, "End-to-End Data Protection Capabilities", -1, NULL);
630 	nvme_print_bit(6, "Protection Information Type 1",
631 	    idns->id_dpc.dp_type1, NULL, NULL);
632 	nvme_print_bit(6, "Protection Information Type 2",
633 	    idns->id_dpc.dp_type2, NULL, NULL);
634 	nvme_print_bit(6, "Protection Information Type 3",
635 	    idns->id_dpc.dp_type3, NULL, NULL);
636 	nvme_print_bit(6, "Protection Information first",
637 	    idns->id_dpc.dp_first, NULL, NULL);
638 	nvme_print_bit(6, "Protection Information last",
639 	    idns->id_dpc.dp_last, NULL, NULL);
640 	nvme_print(4, "End-to-End Data Protection Settings", -1, NULL);
641 	if (idns->id_dps.dp_pinfo == 0)
642 		nvme_print_str(6, "Protection Information", -1,
643 		    "disabled", 0);
644 	else
645 		nvme_print_uint64(6, "Protection Information Type",
646 		    idns->id_dps.dp_pinfo, NULL, NULL);
647 	nvme_print_bit(6, "Protection Information in Metadata",
648 	    idns->id_dps.dp_first, "first 8 bytes", "last 8 bytes");
649 
650 	if (NVME_VERSION_ATLEAST(version, 1, 1)) {
651 		nvme_print(4, "Namespace Multi-Path I/O and Namespace Sharing "
652 		    "Capabilities", -1, NULL);
653 		nvme_print_bit(6, "Namespace is shared",
654 		    idns->id_nmic.nm_shared, "yes", "no");
655 		nvme_print(2, "Reservation Capabilities", -1, NULL);
656 		nvme_print_bit(6, "Persist Through Power Loss",
657 		    idns->id_rescap.rc_persist, NULL, NULL);
658 		nvme_print_bit(6, "Write Exclusive",
659 		    idns->id_rescap.rc_wr_excl, NULL, NULL);
660 		nvme_print_bit(6, "Exclusive Access",
661 		    idns->id_rescap.rc_excl, NULL, NULL);
662 		nvme_print_bit(6, "Write Exclusive - Registrants Only",
663 		    idns->id_rescap.rc_wr_excl_r, NULL, NULL);
664 		nvme_print_bit(6, "Exclusive Access - Registrants Only",
665 		    idns->id_rescap.rc_excl_r, NULL, NULL);
666 		nvme_print_bit(6, "Write Exclusive - All Registrants",
667 		    idns->id_rescap.rc_wr_excl_a, NULL, NULL);
668 		nvme_print_bit(6, "Exclusive Access - All Registrants",
669 		    idns->id_rescap.rc_excl_a, NULL, NULL);
670 
671 		nvme_print(4, "IEEE Extended Unique Identifier", -1,
672 		    "%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X",
673 		    idns->id_eui64[0], idns->id_eui64[1],
674 		    idns->id_eui64[2], idns->id_eui64[3],
675 		    idns->id_eui64[4], idns->id_eui64[5],
676 		    idns->id_eui64[6], idns->id_eui64[7]);
677 	}
678 
679 	for (i = 0; i <= idns->id_nlbaf; i++) {
680 		if (verbose == 0 && idns->id_lbaf[i].lbaf_ms != 0)
681 			continue;
682 
683 		nvme_print(4, "LBA Format", i, NULL);
684 		nvme_print_uint64(6, "Metadata Size",
685 		    idns->id_lbaf[i].lbaf_ms, NULL, " bytes");
686 		nvme_print_uint64(6, "LBA Data Size",
687 		    1 << idns->id_lbaf[i].lbaf_lbads, NULL, " bytes");
688 		nvme_print_str(6, "Relative Performance", -1,
689 		    lbaf_relative_performance[idns->id_lbaf[i].lbaf_rp], 0);
690 	}
691 }
692 
693 /*
694  * nvme_print_error_log
695  *
696  * This function pretty-prints all non-zero error log entries, or all entries
697  * if verbose is set.
698  */
699 void
700 nvme_print_error_log(int nlog, nvme_error_log_entry_t *elog)
701 {
702 	int i;
703 
704 	nvme_print(0, "Error Log", -1, NULL);
705 	for (i = 0; i != nlog; i++)
706 		if (elog[i].el_count == 0)
707 			break;
708 	nvme_print_uint64(2, "Number of Error Log Entries", i, NULL, NULL);
709 
710 	for (i = 0; i != nlog; i++) {
711 		int sc = elog[i].el_sf.sf_sc;
712 		const char *sc_str = "";
713 
714 		if (elog[i].el_count == 0 && verbose == 0)
715 			break;
716 
717 		switch (elog[i].el_sf.sf_sct) {
718 		case 0: /* Generic Command Status */
719 			if (sc < ARRAYSIZE(generic_status_codes))
720 				sc_str = generic_status_codes[sc];
721 			else if (sc >= 0x80 &&
722 			    sc - 0x80 < ARRAYSIZE(generic_nvm_status_codes))
723 				sc_str = generic_nvm_status_codes[sc - 0x80];
724 			break;
725 		case 1: /* Specific Command Status */
726 			if (sc < ARRAYSIZE(specific_status_codes))
727 				sc_str = specific_status_codes[sc];
728 			else if (sc >= 0x80 &&
729 			    sc - 0x80 < ARRAYSIZE(specific_nvm_status_codes))
730 				sc_str = specific_nvm_status_codes[sc - 0x80];
731 			break;
732 		case 2: /* Media Errors */
733 			if (sc >= 0x80 &&
734 			    sc - 0x80 < ARRAYSIZE(media_nvm_status_codes))
735 				sc_str = media_nvm_status_codes[sc - 0x80];
736 			break;
737 		case 7: /* Vendor Specific */
738 			sc_str = "Unknown Vendor Specific";
739 			break;
740 		default:
741 			sc_str = "Reserved";
742 			break;
743 		}
744 
745 		nvme_print(2, "Entry", i, NULL);
746 		nvme_print_uint64(4, "Error Count",
747 		    elog[i].el_count, NULL, NULL);
748 		nvme_print_uint64(4, "Submission Queue ID",
749 		    elog[i].el_sqid, NULL, NULL);
750 		nvme_print_uint64(4, "Command ID",
751 		    elog[i].el_cid, NULL, NULL);
752 		nvme_print(4, "Status Field", -1, NULL);
753 		nvme_print_uint64(6, "Phase Tag",
754 		    elog[i].el_sf.sf_p, NULL, NULL);
755 		nvme_print(6, "Status Code", -1, "0x%0.2x (%s)",
756 		    sc, sc_str);
757 		nvme_print(6, "Status Code Type", -1, "0x%x (%s)",
758 		    elog[i].el_sf.sf_sct,
759 		    status_code_types[elog[i].el_sf.sf_sct]);
760 		nvme_print_bit(6, "More",
761 		    elog[i].el_sf.sf_m, "yes", "no");
762 		nvme_print_bit(6, "Do Not Retry",
763 		    elog[i].el_sf.sf_m, "yes", "no");
764 		nvme_print_uint64(4, "Parameter Error Location byte",
765 		    elog[i].el_byte, "0x%0.2"PRIx64, NULL);
766 		nvme_print_uint64(4, "Parameter Error Location bit",
767 		    elog[i].el_bit, NULL, NULL);
768 		nvme_print_uint64(4, "Logical Block Address",
769 		    elog[i].el_lba, NULL, NULL);
770 		nvme_print(4, "Namespace ID", -1, "%d",
771 		    elog[i].el_nsid == 0xffffffff ?
772 		    0 : elog[i].el_nsid);
773 		nvme_print_uint64(4,
774 		    "Vendor Specifc Information Available",
775 		    elog[i].el_vendor, NULL, NULL);
776 	}
777 }
778 
779 /*
780  * nvme_print_health_log
781  *
782  * This function pretty-prints a summary of the SMART/Health log, or all
783  * of the log if verbose is set.
784  */
785 void
786 nvme_print_health_log(nvme_health_log_t *hlog, nvme_identify_ctrl_t *idctl)
787 {
788 	nvme_print(0, "SMART/Health Information", -1, NULL);
789 	nvme_print(2, "Critical Warnings", -1, NULL);
790 	nvme_print_bit(4, "Available Space",
791 	    hlog->hl_crit_warn.cw_avail, "low", "OK");
792 	nvme_print_bit(4, "Temperature",
793 	    hlog->hl_crit_warn.cw_temp, "too high", "OK");
794 	nvme_print_bit(4, "Device Reliability",
795 	    hlog->hl_crit_warn.cw_reliab, "degraded", "OK");
796 	nvme_print_bit(4, "Media",
797 	    hlog->hl_crit_warn.cw_readonly, "read-only", "OK");
798 	if (idctl->id_vwc.vwc_present != 0)
799 		nvme_print_bit(4, "Volatile Memory Backup",
800 		    hlog->hl_crit_warn.cw_volatile, "failed", "OK");
801 
802 	nvme_print_uint64(2, "Temperature",
803 	    hlog->hl_temp - 273, NULL, "C");
804 	nvme_print_uint64(2, "Available Spare Capacity",
805 	    hlog->hl_avail_spare, NULL, "%");
806 
807 	if (verbose != 0)
808 		nvme_print_uint64(2, "Available Spare Threshold",
809 		    hlog->hl_avail_spare_thr, NULL, "%");
810 
811 	nvme_print_uint64(2, "Device Life Used",
812 	    hlog->hl_used, NULL, "%");
813 
814 	if (verbose == 0)
815 		return;
816 
817 	/*
818 	 * The following two fields are in 1000 512 byte units. Convert that to
819 	 * GB by doing binary shifts (9 left and 30 right) and muliply by 10^3.
820 	 */
821 	nvme_print_uint128(2, "Data Read",
822 	    hlog->hl_data_read, "GB", 30 - 9, 3);
823 	nvme_print_uint128(2, "Data Written",
824 	    hlog->hl_data_write, "GB", 30 - 9, 3);
825 
826 	nvme_print_uint128(2, "Read Commands",
827 	    hlog->hl_host_read, NULL, 0, 0);
828 	nvme_print_uint128(2, "Write Commands",
829 	    hlog->hl_host_write, NULL, 0, 0);
830 	nvme_print_uint128(2, "Controller Busy",
831 	    hlog->hl_ctrl_busy, "min", 0, 0);
832 	nvme_print_uint128(2, "Power Cycles",
833 	    hlog->hl_power_cycles, NULL, 0, 0);
834 	nvme_print_uint128(2, "Power On",
835 	    hlog->hl_power_on_hours, "h", 0, 0);
836 	nvme_print_uint128(2, "Unsafe Shutdowns",
837 	    hlog->hl_unsafe_shutdn, NULL, 0, 0);
838 	nvme_print_uint128(2, "Uncorrectable Media Errors",
839 	    hlog->hl_media_errors, NULL, 0, 0);
840 	nvme_print_uint128(2, "Errors Logged",
841 	    hlog->hl_errors_logged, NULL, 0, 0);
842 }
843 
844 /*
845  * nvme_print_fwslot_log
846  *
847  * This function pretty-prints the firmware slot information.
848  */
849 void
850 nvme_print_fwslot_log(nvme_fwslot_log_t *fwlog)
851 {
852 	int i;
853 
854 	nvme_print(0, "Firmware Slot Information", -1, NULL);
855 	nvme_print_uint64(2, "Active Firmware Slot", fwlog->fw_afi, NULL, NULL);
856 
857 	for (i = 0; i != ARRAYSIZE(fwlog->fw_frs); i++) {
858 		if (fwlog->fw_frs[i][0] == '\0')
859 			break;
860 		nvme_print_str(2, "Firmware Revision for Slot", i + 1,
861 		    fwlog->fw_frs[i], sizeof (fwlog->fw_frs[i]));
862 	}
863 }
864 
865 /*
866  * nvme_print_feat_*
867  *
868  * These functions pretty-print the data structures returned by GET FEATURES.
869  */
870 void
871 nvme_print_feat_arbitration(uint64_t res, void *b, size_t s,
872     nvme_identify_ctrl_t *id)
873 {
874 	_NOTE(ARGUNUSED(b));
875 	_NOTE(ARGUNUSED(s));
876 	_NOTE(ARGUNUSED(id));
877 	nvme_arbitration_t arb;
878 
879 	arb.r = (uint32_t)res;
880 	if (arb.b.arb_ab != 7)
881 		nvme_print_uint64(4, "Arbitration Burst",
882 		    1 << arb.b.arb_ab, NULL, NULL);
883 	else
884 		nvme_print_str(4, "Arbitration Burst", 0,
885 		    "no limit", 0);
886 	nvme_print_uint64(4, "Low Priority Weight",
887 	    (uint16_t)arb.b.arb_lpw + 1, NULL, NULL);
888 	nvme_print_uint64(4, "Medium Priority Weight",
889 	    (uint16_t)arb.b.arb_mpw + 1, NULL, NULL);
890 	nvme_print_uint64(4, "High Priority Weight",
891 	    (uint16_t)arb.b.arb_hpw + 1, NULL, NULL);
892 }
893 
894 void
895 nvme_print_feat_power_mgmt(uint64_t res, void *b, size_t s,
896     nvme_identify_ctrl_t *id)
897 {
898 	_NOTE(ARGUNUSED(b));
899 	_NOTE(ARGUNUSED(s));
900 	_NOTE(ARGUNUSED(id));
901 	nvme_power_mgmt_t pm;
902 
903 	pm.r = (uint32_t)res;
904 	nvme_print_uint64(4, "Power State", (uint8_t)pm.b.pm_ps,
905 	    NULL, NULL);
906 }
907 
908 void
909 nvme_print_feat_lba_range(uint64_t res, void *buf, size_t bufsize,
910     nvme_identify_ctrl_t *id)
911 {
912 	_NOTE(ARGUNUSED(id));
913 
914 	nvme_lba_range_type_t lrt;
915 	nvme_lba_range_t *lr;
916 	size_t n_lr;
917 	int i;
918 
919 	if (buf == NULL)
920 		return;
921 
922 	lrt.r = res;
923 	lr = buf;
924 
925 	n_lr = bufsize / sizeof (nvme_lba_range_t);
926 	if (n_lr > lrt.b.lr_num + 1)
927 		n_lr = lrt.b.lr_num + 1;
928 
929 	nvme_print_uint64(4, "Number of LBA Ranges",
930 	    (uint8_t)lrt.b.lr_num + 1, NULL, NULL);
931 
932 	for (i = 0; i != n_lr; i++) {
933 		if (verbose == 0 && lr[i].lr_nlb == 0)
934 			continue;
935 
936 		nvme_print(4, "LBA Range", i, NULL);
937 		if (lr[i].lr_type < ARRAYSIZE(lba_range_types))
938 			nvme_print_str(6, "Type", -1,
939 			    lba_range_types[lr[i].lr_type], 0);
940 		else
941 			nvme_print_uint64(6, "Type",
942 			    lr[i].lr_type, NULL, NULL);
943 		nvme_print(6, "Attributes", -1, NULL);
944 		nvme_print_bit(8, "Writable",
945 		    lr[i].lr_attr.lr_write, "yes", "no");
946 		nvme_print_bit(8, "Hidden",
947 		    lr[i].lr_attr.lr_hidden, "yes", "no");
948 		nvme_print_uint64(6, "Starting LBA",
949 		    lr[i].lr_slba, NULL, NULL);
950 		nvme_print_uint64(6, "Number of Logical Blocks",
951 		    lr[i].lr_nlb, NULL, NULL);
952 		nvme_print(6, "Unique Identifier", -1,
953 		    "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
954 		    "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
955 		    lr[i].lr_guid[0], lr[i].lr_guid[1],
956 		    lr[i].lr_guid[2], lr[i].lr_guid[3],
957 		    lr[i].lr_guid[4], lr[i].lr_guid[5],
958 		    lr[i].lr_guid[6], lr[i].lr_guid[7],
959 		    lr[i].lr_guid[8], lr[i].lr_guid[9],
960 		    lr[i].lr_guid[10], lr[i].lr_guid[11],
961 		    lr[i].lr_guid[12], lr[i].lr_guid[13],
962 		    lr[i].lr_guid[14], lr[i].lr_guid[15]);
963 	}
964 }
965 
966 void
967 nvme_print_feat_temperature(uint64_t res, void *b, size_t s,
968     nvme_identify_ctrl_t *id)
969 {
970 	_NOTE(ARGUNUSED(b));
971 	_NOTE(ARGUNUSED(s));
972 	_NOTE(ARGUNUSED(id));
973 	nvme_temp_threshold_t tt;
974 
975 	tt.r = (uint32_t)res;
976 	nvme_print_uint64(4, "Temperature Threshold", tt.b.tt_tmpth - 273,
977 	    NULL, "C");
978 }
979 
980 void
981 nvme_print_feat_error(uint64_t res, void *b, size_t s,
982     nvme_identify_ctrl_t *id)
983 {
984 	_NOTE(ARGUNUSED(b));
985 	_NOTE(ARGUNUSED(s));
986 	_NOTE(ARGUNUSED(id));
987 	nvme_error_recovery_t er;
988 
989 	er.r = (uint32_t)res;
990 	if (er.b.er_tler > 0)
991 		nvme_print_uint64(4, "Time Limited Error Recovery",
992 		    (uint32_t)er.b.er_tler * 100, NULL, "ms");
993 	else
994 		nvme_print_str(4, "Time Limited Error Recovery", -1,
995 		    "no time limit", 0);
996 }
997 
998 void
999 nvme_print_feat_write_cache(uint64_t res, void *b, size_t s,
1000     nvme_identify_ctrl_t *id)
1001 {
1002 	_NOTE(ARGUNUSED(b));
1003 	_NOTE(ARGUNUSED(s));
1004 	_NOTE(ARGUNUSED(id));
1005 	nvme_write_cache_t wc;
1006 
1007 	wc.r = (uint32_t)res;
1008 	nvme_print_bit(4, "Volatile Write Cache",
1009 	    wc.b.wc_wce, "enabled", "disabled");
1010 }
1011 
1012 void
1013 nvme_print_feat_nqueues(uint64_t res, void *b, size_t s,
1014     nvme_identify_ctrl_t *id)
1015 {
1016 	_NOTE(ARGUNUSED(b));
1017 	_NOTE(ARGUNUSED(s));
1018 	_NOTE(ARGUNUSED(id));
1019 	nvme_nqueues_t nq;
1020 
1021 	nq.r = (uint32_t)res;
1022 	nvme_print_uint64(4, "Number of Submission Queues",
1023 	    nq.b.nq_nsq + 1, NULL, NULL);
1024 	nvme_print_uint64(4, "Number of Completion Queues",
1025 	    nq.b.nq_ncq + 1, NULL, NULL);
1026 }
1027 
1028 void
1029 nvme_print_feat_intr_coal(uint64_t res, void *b, size_t s,
1030     nvme_identify_ctrl_t *id)
1031 {
1032 	_NOTE(ARGUNUSED(b));
1033 	_NOTE(ARGUNUSED(s));
1034 	_NOTE(ARGUNUSED(id));
1035 	nvme_intr_coal_t ic;
1036 
1037 	ic.r = (uint32_t)res;
1038 	nvme_print_uint64(4, "Aggregation Threshold",
1039 	    ic.b.ic_thr + 1, NULL, NULL);
1040 	nvme_print_uint64(4, "Aggregation Time",
1041 	    (uint16_t)ic.b.ic_time * 100, NULL, "us");
1042 }
1043 void
1044 nvme_print_feat_intr_vect(uint64_t res, void *b, size_t s,
1045     nvme_identify_ctrl_t *id)
1046 {
1047 	_NOTE(ARGUNUSED(b));
1048 	_NOTE(ARGUNUSED(s));
1049 	_NOTE(ARGUNUSED(id));
1050 	nvme_intr_vect_t iv;
1051 	char *tmp;
1052 
1053 	iv.r = (uint32_t)res;
1054 	if (asprintf(&tmp, "Vector %d Coalescing Disable", iv.b.iv_iv) < 0)
1055 		err(-1, "nvme_print_feat_common()");
1056 
1057 	nvme_print_bit(4, tmp, iv.b.iv_cd, "yes", "no");
1058 }
1059 
1060 void
1061 nvme_print_feat_write_atom(uint64_t res, void *b, size_t s,
1062     nvme_identify_ctrl_t *id)
1063 {
1064 	_NOTE(ARGUNUSED(b));
1065 	_NOTE(ARGUNUSED(s));
1066 	_NOTE(ARGUNUSED(id));
1067 	nvme_write_atomicity_t wa;
1068 
1069 	wa.r = (uint32_t)res;
1070 	nvme_print_bit(4, "Disable Normal", wa.b.wa_dn, "yes", "no");
1071 }
1072 
1073 void
1074 nvme_print_feat_async_event(uint64_t res, void *b, size_t s,
1075     nvme_identify_ctrl_t *idctl)
1076 {
1077 	_NOTE(ARGUNUSED(b));
1078 	_NOTE(ARGUNUSED(s));
1079 	nvme_async_event_conf_t aec;
1080 
1081 	aec.r = (uint32_t)res;
1082 	nvme_print_bit(4, "Available Space below threshold",
1083 	    aec.b.aec_avail, "enabled", "disabled");
1084 	nvme_print_bit(4, "Temperature above threshold",
1085 	    aec.b.aec_temp, "enabled", "disabled");
1086 	nvme_print_bit(4, "Device Reliability compromised",
1087 	    aec.b.aec_reliab, "enabled", "disabled");
1088 	nvme_print_bit(4, "Media read-only",
1089 	    aec.b.aec_readonly, "enabled", "disabled");
1090 	if (idctl->id_vwc.vwc_present != 0)
1091 		nvme_print_bit(4, "Volatile Memory Backup failed",
1092 		    aec.b.aec_volatile, "enabled", "disabled");
1093 }
1094 
1095 void
1096 nvme_print_feat_auto_pst(uint64_t res, void *buf, size_t bufsize,
1097     nvme_identify_ctrl_t *id)
1098 {
1099 	_NOTE(ARGUNUSED(id));
1100 
1101 	nvme_auto_power_state_trans_t apst;
1102 	nvme_auto_power_state_t *aps;
1103 	int i;
1104 	int cnt = bufsize / sizeof (nvme_auto_power_state_t);
1105 
1106 	if (buf == NULL)
1107 		return;
1108 
1109 	apst.r = res;
1110 	aps = buf;
1111 
1112 	nvme_print_bit(4, "Autonomous Power State Transition",
1113 	    apst.b.apst_apste, "enabled", "disabled");
1114 	for (i = 0; i != cnt; i++) {
1115 		if (aps[i].apst_itps == 0 && aps[i].apst_itpt == 0)
1116 			break;
1117 
1118 		nvme_print(4, "Power State", i, NULL);
1119 		nvme_print_uint64(6, "Idle Transition Power State",
1120 		    (uint16_t)aps[i].apst_itps, NULL, NULL);
1121 		nvme_print_uint64(6, "Idle Time Prior to Transition",
1122 		    aps[i].apst_itpt, NULL, "ms");
1123 	}
1124 }
1125 
1126 void
1127 nvme_print_feat_progress(uint64_t res, void *b, size_t s,
1128     nvme_identify_ctrl_t *id)
1129 {
1130 	_NOTE(ARGUNUSED(b));
1131 	_NOTE(ARGUNUSED(s));
1132 	_NOTE(ARGUNUSED(id));
1133 	nvme_software_progress_marker_t spm;
1134 
1135 	spm.r = (uint32_t)res;
1136 	nvme_print_uint64(4, "Pre-Boot Software Load Count",
1137 	    spm.b.spm_pbslc, NULL, NULL);
1138 }
1139