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