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
nvme_print(int indent,char * name,int index,const char * fmt,...)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
nvme_strlen(const char * str,int len)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
nvme_print_str(int indent,char * name,int index,const char * value,int len)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
nvme_print_double(int indent,char * name,double value,int places,char * unit)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
nvme_print_uint64(int indent,char * name,uint64_t value,const char * fmt,char * unit)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
nvme_print_uint128(int indent,char * name,nvme_uint128_t value,char * unit,int scale_bits,int scale_tens)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
nvme_print_bit(int indent,char * name,int value,char * s_true,char * s_false)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
nvme_print_ctrl_summary(nvme_identify_ctrl_t * idctl,nvme_version_t * version)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
nvme_print_nsid_summary(nvme_identify_nsid_t * idns)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
nvme_print_identify_ctrl(nvme_identify_ctrl_t * idctl,nvme_capabilities_t * cap,nvme_version_t * version)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
nvme_print_identify_nsid(nvme_identify_nsid_t * idns,nvme_version_t * version)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
nvme_print_error_log(int nlog,nvme_error_log_entry_t * elog)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
nvme_print_health_log(nvme_health_log_t * hlog,nvme_identify_ctrl_t * idctl)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
nvme_print_fwslot_log(nvme_fwslot_log_t * fwlog)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
nvme_print_feat_arbitration(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_power_mgmt(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_lba_range(uint64_t res,void * buf,size_t bufsize,nvme_identify_ctrl_t * id)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
nvme_print_feat_temperature(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_error(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_write_cache(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_nqueues(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_intr_coal(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_intr_vect(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_write_atom(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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
nvme_print_feat_async_event(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * idctl)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
nvme_print_feat_auto_pst(uint64_t res,void * buf,size_t bufsize,nvme_identify_ctrl_t * id)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
nvme_print_feat_progress(uint64_t res,void * b,size_t s,nvme_identify_ctrl_t * id)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