xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_ocp.c (revision 56566d3ad46b71a5a32fad910c82ced1b1343e32)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2026 Oxide Computer Company
14  */
15 
16 /*
17  * Field information for OCP logs.
18  */
19 
20 #include <err.h>
21 #include <string.h>
22 #include <sys/stddef.h>
23 #include <sys/sysmacros.h>
24 #include <sys/nvme/ocp.h>
25 
26 #include "nvmeadm.h"
27 
28 #define	OCP_F_SMART(f)	.nf_off = offsetof(ocp_vul_smart_t, osh_##f), \
29 	.nf_len = sizeof (((ocp_vul_smart_t *)NULL)->osh_##f)
30 
31 static const nvmeadm_field_bit_t ocp_vul_smart_block_bits[] = { {
32 	.nfb_lowbit = 0, .nfb_hibit = 47,
33 	.nfb_short = "raw",
34 	.nfb_desc = "Raw Count",
35 	.nfb_type = NVMEADM_FT_HEX,
36 }, {
37 	.nfb_lowbit = 48, .nfb_hibit = 63,
38 	.nfb_short = "norm",
39 	.nfb_desc = "Normalized Value",
40 	.nfb_type = NVMEADM_FT_PERCENT,
41 } };
42 
43 static const nvmeadm_field_bit_t ocp_vul_smart_e2e_bits[] = { {
44 	.nfb_lowbit = 0, .nfb_hibit = 31,
45 	.nfb_short = "det",
46 	.nfb_desc = "Detected Errors",
47 	.nfb_type = NVMEADM_FT_HEX,
48 }, {
49 	.nfb_lowbit = 32, .nfb_hibit = 63,
50 	.nfb_short = "cor",
51 	.nfb_desc = "Corrected Errors",
52 	.nfb_type = NVMEADM_FT_HEX
53 } };
54 
55 static const nvmeadm_field_bit_t ocp_vul_smart_udec_bits[] = { {
56 	.nfb_lowbit = 0, .nfb_hibit = 31,
57 	.nfb_short = "max",
58 	.nfb_desc = "Maximum Count",
59 	.nfb_type = NVMEADM_FT_HEX,
60 }, {
61 	.nfb_lowbit = 32, .nfb_hibit = 63,
62 	.nfb_short = "min",
63 	.nfb_desc = "Minimum Count",
64 	.nfb_type = NVMEADM_FT_HEX
65 } };
66 
67 static const nvmeadm_field_bit_t ocp_vul_smart_therm_bits[] = { {
68 	.nfb_lowbit = 0, .nfb_hibit = 7,
69 	.nfb_short = "events",
70 	.nfb_desc = "Throttling Events",
71 	.nfb_type = NVMEADM_FT_HEX,
72 }, {
73 	.nfb_lowbit = 8, .nfb_hibit = 15,
74 	.nfb_short = "status",
75 	.nfb_desc = "Current Throttling Status",
76 	.nfb_type = NVMEADM_FT_STRMAP,
77 	.nfb_strs = { "unthrottled", "first level", "second level",
78 	    "third level" }
79 } };
80 
81 static const nvmeadm_field_bit_t ocp_vul_smart_dssd_bits[] = { {
82 	.nfb_lowbit = 0, .nfb_hibit = 7,
83 	.nfb_short = "errata",
84 	.nfb_desc = "Errata Version",
85 	.nfb_type = NVMEADM_FT_HEX,
86 }, {
87 	.nfb_lowbit = 8, .nfb_hibit = 23,
88 	.nfb_short = "point",
89 	.nfb_desc = "Point Version",
90 	.nfb_type = NVMEADM_FT_HEX,
91 }, {
92 	.nfb_lowbit = 24, .nfb_hibit = 39,
93 	.nfb_short = "minor",
94 	.nfb_desc = "Minor Version",
95 	.nfb_type = NVMEADM_FT_HEX,
96 }, {
97 	.nfb_lowbit = 40, .nfb_hibit = 47,
98 	.nfb_short = "major",
99 	.nfb_desc = "Major Version",
100 	.nfb_type = NVMEADM_FT_HEX,
101 } };
102 
103 static const nvmeadm_field_t ocp_vul_smart_fields[] = { {
104 	OCP_F_SMART(pmed_write),
105 	.nf_short = "pmuw",
106 	.nf_desc = "Physical Media Units Written",
107 	.nf_type = NVMEADM_FT_BYTES
108 }, {
109 	OCP_F_SMART(pmed_read),
110 	.nf_short = "pmur",
111 	.nf_desc = "Physical Media Units Read",
112 	.nf_type = NVMEADM_FT_BYTES
113 }, {
114 	OCP_F_SMART(bunb),
115 	.nf_short = "bunb",
116 	.nf_desc = "Bad User NAND Blocks",
117 	NVMEADM_F_BITS(ocp_vul_smart_block_bits)
118 }, {
119 	OCP_F_SMART(bsnb),
120 	.nf_short = "bsnb",
121 	.nf_desc = "Bad System NAND Blocks",
122 	NVMEADM_F_BITS(ocp_vul_smart_block_bits)
123 }, {
124 	OCP_F_SMART(xor_rec),
125 	.nf_short = "xrc",
126 	.nf_desc = "XOR Recovery Count",
127 	.nf_type = NVMEADM_FT_HEX
128 }, {
129 	OCP_F_SMART(read_unrec),
130 	.nf_short = "urec",
131 	.nf_desc = "Uncorrectable Read Error Count",
132 	.nf_type = NVMEADM_FT_HEX
133 }, {
134 	OCP_F_SMART(soft_ecc_err),
135 	.nf_short = "seec",
136 	.nf_desc = "Soft ECC Error Count",
137 	.nf_type = NVMEADM_FT_HEX
138 }, {
139 	OCP_F_SMART(e2e),
140 	.nf_short = "e2e",
141 	.nf_desc = "End to End Correction Counts",
142 	NVMEADM_F_BITS(ocp_vul_smart_e2e_bits)
143 }, {
144 	OCP_F_SMART(sys_used),
145 	.nf_short = "sdu",
146 	.nf_desc = "System Data Percent Used",
147 	.nf_type = NVMEADM_FT_PERCENT
148 }, {
149 	OCP_F_SMART(refresh),
150 	.nf_short = "refresh",
151 	.nf_desc = "Refresh Counts",
152 	.nf_type = NVMEADM_FT_HEX
153 }, {
154 	OCP_F_SMART(udec),
155 	.nf_short = "udec",
156 	.nf_desc = "User Data Erase Counts",
157 	NVMEADM_F_BITS(ocp_vul_smart_udec_bits)
158 }, {
159 	OCP_F_SMART(therm),
160 	.nf_short = "therm",
161 	.nf_desc = "Thermal Throttling Status and Count",
162 	NVMEADM_F_BITS(ocp_vul_smart_therm_bits)
163 }, {
164 	OCP_F_SMART(dssd),
165 	.nf_short = "dssd",
166 	.nf_desc = "DSSD Specification Version",
167 	.nf_rev = 3,
168 	NVMEADM_F_BITS(ocp_vul_smart_dssd_bits)
169 }, {
170 	OCP_F_SMART(pcie_errcor),
171 	.nf_short = "pcicor",
172 	.nf_desc = "PCIe Correctable Error Count",
173 	.nf_type = NVMEADM_FT_HEX
174 }, {
175 	OCP_F_SMART(inc_shut),
176 	.nf_short = "incshut",
177 	.nf_desc = "Incomplete Shutdowns",
178 	.nf_type = NVMEADM_FT_HEX
179 }, {
180 	OCP_F_SMART(free),
181 	.nf_short = "freeblk",
182 	.nf_desc = "Percent Free Blocks",
183 	.nf_type = NVMEADM_FT_PERCENT
184 }, {
185 	OCP_F_SMART(cap_health),
186 	.nf_short = "cap",
187 	.nf_desc = "Capacitor Health",
188 	.nf_type = NVMEADM_FT_PERCENT
189 }, {
190 	OCP_F_SMART(nvme_base_errata),
191 	.nf_short = "baseev",
192 	.nf_desc = "NVMe Base Errata Version",
193 	.nf_rev = 3,
194 	.nf_type = NVMEADM_FT_ASCII
195 }, {
196 	OCP_F_SMART(nvme_cmd_errata),
197 	.nf_short = "cmdev",
198 	.nf_desc = "NVMe Command Set Errata Version",
199 	.nf_rev = 4,
200 	.nf_type = NVMEADM_FT_ASCII
201 }, {
202 	OCP_F_SMART(unaligned),
203 	.nf_short = "unalign",
204 	.nf_desc = "Unaligned I/O",
205 	.nf_type = NVMEADM_FT_HEX
206 }, {
207 	OCP_F_SMART(sec_vers),
208 	.nf_short = "secvers",
209 	.nf_desc = "Security Version Number",
210 	.nf_type = NVMEADM_FT_HEX
211 }, {
212 	OCP_F_SMART(nuse),
213 	.nf_short = "nuse",
214 	.nf_desc = "Total NUSE",
215 	.nf_type = NVMEADM_FT_HEX
216 }, {
217 	OCP_F_SMART(plp_start),
218 	.nf_short = "plp",
219 	.nf_desc = "PLP Start Count",
220 	.nf_type = NVMEADM_FT_HEX
221 }, {
222 	OCP_F_SMART(endurance),
223 	.nf_short = "endest",
224 	.nf_desc = "Endurance Estimate",
225 	.nf_type = NVMEADM_FT_BYTES
226 }, {
227 	OCP_F_SMART(pcie_retrain),
228 	.nf_short = "retrain",
229 	.nf_desc = "PCIe Link Retraining Count",
230 	.nf_type = NVMEADM_FT_HEX
231 }, {
232 	OCP_F_SMART(ps_change),
233 	.nf_short = "pstate",
234 	.nf_desc = "Power State Change Count",
235 	.nf_type = NVMEADM_FT_HEX
236 }, {
237 	OCP_F_SMART(min_fwrev),
238 	.nf_short = "minfw",
239 	.nf_desc = "Lowest Permitted Firmware Revision",
240 	.nf_type = NVMEADM_FT_ASCII
241 }, {
242 	OCP_F_SMART(vers),
243 	.nf_short = "lpv",
244 	.nf_desc = "Log Page Version",
245 	.nf_type = NVMEADM_FT_HEX
246 }, {
247 	OCP_F_SMART(guid),
248 	.nf_short = "lpg",
249 	.nf_desc = "Log Page GUID",
250 	.nf_type = NVMEADM_FT_GUID
251 } };
252 
253 static uint32_t
ocp_vul_smart_getvers(const void * data,size_t len)254 ocp_vul_smart_getvers(const void *data, size_t len)
255 {
256 	if (len < sizeof (ocp_vul_smart_t)) {
257 		errx(-1, "cannot parse revision information, found 0x%zx "
258 		    "bytes, need at least 0x%zx", len,
259 		    sizeof (ocp_vul_smart_t));
260 	}
261 
262 	const ocp_vul_smart_t *log = data;
263 	return (log->osh_vers);
264 }
265 
266 const nvmeadm_log_field_info_t ocp_vul_smart_field_info = {
267 	.nlfi_log = "ocp/smart",
268 	.nlfi_fields = ocp_vul_smart_fields,
269 	.nlfi_nfields = ARRAY_SIZE(ocp_vul_smart_fields),
270 	.nlfi_min = sizeof (ocp_vul_smart_t),
271 	.nlfi_getrev = ocp_vul_smart_getvers
272 };
273 
274 #define	OCP_F_ERRREC(f)	.nf_off = offsetof(ocp_vul_errrec_t, oer_##f), \
275 	.nf_len = sizeof (((ocp_vul_errrec_t *)NULL)->oer_##f)
276 
277 static const nvmeadm_field_bit_t ocp_vul_errrec_pra_bits[] = { {
278 	.nfb_lowbit = 0, .nfb_hibit = 0,
279 	.nfb_short = "ctrl",
280 	.nfb_desc = "NVMe Controller Reset",
281 	.nfb_type = NVMEADM_FT_STRMAP,
282 	.nfb_strs = { "not required", "required" }
283 }, {
284 	.nfb_lowbit = 1, .nfb_hibit = 1,
285 	.nfb_short = "subsys",
286 	.nfb_desc = "NVMe Subsystem Reset",
287 	.nfb_type = NVMEADM_FT_STRMAP,
288 	.nfb_strs = { "not required", "required" }
289 }, {
290 	.nfb_lowbit = 2, .nfb_hibit = 2,
291 	.nfb_short = "flr",
292 	.nfb_desc = "PCIe Function Level Reset",
293 	.nfb_type = NVMEADM_FT_STRMAP,
294 	.nfb_strs = { "not required", "required" }
295 }, {
296 	.nfb_lowbit = 3, .nfb_hibit = 3,
297 	.nfb_short = "perst",
298 	.nfb_desc = "PERST#",
299 	.nfb_type = NVMEADM_FT_STRMAP,
300 	.nfb_strs = { "not required", "required" }
301 }, {
302 	.nfb_lowbit = 4, .nfb_hibit = 4,
303 	.nfb_short = "power",
304 	.nfb_desc = "Main Power Cycle",
305 	.nfb_type = NVMEADM_FT_STRMAP,
306 	.nfb_strs = { "not required", "required" }
307 }, {
308 	.nfb_lowbit = 5, .nfb_hibit = 5,
309 	.nfb_short = "hotrst",
310 	.nfb_desc = "PCIe Conventional Hot Reset",
311 	.nfb_type = NVMEADM_FT_STRMAP,
312 	.nfb_strs = { "not required", "required" }
313 } };
314 
315 static const nvmeadm_field_bit_t ocp_vul_errrec_dra_bits[] = { {
316 	.nfb_lowbit = 0, .nfb_hibit = 0,
317 	.nfb_short = "none",
318 	.nfb_desc = "No Action",
319 	.nfb_type = NVMEADM_FT_STRMAP,
320 	.nfb_strs = { "not required", "required" }
321 }, {
322 	.nfb_lowbit = 1, .nfb_hibit = 1,
323 	.nfb_short = "fmt",
324 	.nfb_desc = "Format NVM",
325 	.nfb_type = NVMEADM_FT_STRMAP,
326 	.nfb_strs = { "not required", "required" }
327 }, {
328 	.nfb_lowbit = 2, .nfb_hibit = 2,
329 	.nfb_short = "vsc",
330 	.nfb_desc = "Vendor Specific Command",
331 	.nfb_type = NVMEADM_FT_STRMAP,
332 	.nfb_strs = { "not required", "required" }
333 }, {
334 	.nfb_lowbit = 3, .nfb_hibit = 3,
335 	.nfb_short = "valys",
336 	.nfb_desc = "Vendor Analysis",
337 	.nfb_type = NVMEADM_FT_STRMAP,
338 	.nfb_strs = { "not required", "required" }
339 }, {
340 	.nfb_lowbit = 4, .nfb_hibit = 4,
341 	.nfb_short = "rep",
342 	.nfb_desc = "Device Replacement",
343 	.nfb_type = NVMEADM_FT_STRMAP,
344 	.nfb_strs = { "not required", "required" }
345 }, {
346 	.nfb_lowbit = 5, .nfb_hibit = 5,
347 	.nfb_short = "san",
348 	.nfb_desc = "Sanitize",
349 	.nfb_type = NVMEADM_FT_STRMAP,
350 	.nfb_strs = { "not required", "required" }
351 }, {
352 	.nfb_lowbit = 6, .nfb_hibit = 6,
353 	.nfb_short = "udl",
354 	.nfb_desc = "User Data Loss",
355 	.nfb_type = NVMEADM_FT_STRMAP,
356 	.nfb_strs = { "not required", "required" }
357 } };
358 
359 static const nvmeadm_field_bit_t ocp_vul_errrec_devcap_bits[] = { {
360 	.nfb_lowbit = 0, .nfb_hibit = 0,
361 	.nfb_short = "aen",
362 	.nfb_desc = "Panic AEN",
363 	.nfb_type = NVMEADM_FT_STRMAP,
364 	.nfb_strs = { "unsupported", "supported" }
365 }, {
366 	.nfb_lowbit = 1, .nfb_hibit = 1,
367 	.nfb_short = "cfs",
368 	.nfb_desc = "Panic CFS",
369 	.nfb_type = NVMEADM_FT_STRMAP,
370 	.nfb_strs = { "unsupported", "supported" }
371 } };
372 
373 static const nvmeadm_field_t ocp_vul_errrec_fields[] = { {
374 	OCP_F_ERRREC(prwt),
375 	.nf_short = "prwt",
376 	.nf_desc = "Panic Reset Wait Time",
377 	.nf_type = NVMEADM_FT_HEX
378 }, {
379 	OCP_F_ERRREC(pra),
380 	.nf_short = "pra",
381 	.nf_desc = "Panic Reset Action",
382 	NVMEADM_F_BITS(ocp_vul_errrec_pra_bits)
383 }, {
384 	OCP_F_ERRREC(dra),
385 	.nf_short = "dra",
386 	.nf_desc = "Device Recovery Action 1",
387 	NVMEADM_F_BITS(ocp_vul_errrec_dra_bits)
388 }, {
389 	OCP_F_ERRREC(panic_id),
390 	.nf_short = "id",
391 	.nf_desc = "Panic ID",
392 	.nf_type = NVMEADM_FT_HEX
393 }, {
394 	OCP_F_ERRREC(devcap),
395 	.nf_short = "devcap",
396 	.nf_desc = "Device Capabilities",
397 	NVMEADM_F_BITS(ocp_vul_errrec_devcap_bits)
398 }, {
399 	OCP_F_ERRREC(vsr_opcode),
400 	.nf_short = "vsro",
401 	.nf_desc = "Vendor Specific Recovery Opcode",
402 	.nf_type = NVMEADM_FT_HEX
403 }, {
404 	OCP_F_ERRREC(vsr_cdw12),
405 	.nf_short = "vcdw12",
406 	.nf_desc = "Vendor Specific Command CDW12",
407 	.nf_type = NVMEADM_FT_HEX
408 }, {
409 	OCP_F_ERRREC(vsr_cdw13),
410 	.nf_short = "vcdw13",
411 	.nf_desc = "Vendor Specific Command CDW13",
412 	.nf_type = NVMEADM_FT_HEX
413 }, {
414 	OCP_F_ERRREC(vsr_to),
415 	.nf_short = "vsct",
416 	.nf_desc = "Vendor Specific Command Timeout",
417 	.nf_rev = 2,
418 	.nf_type = NVMEADM_FT_HEX
419 }, {
420 	OCP_F_ERRREC(dra2),
421 	.nf_short = "dra2",
422 	.nf_desc = "Device Recovery Action 2",
423 	.nf_rev = 3,
424 	NVMEADM_F_BITS(ocp_vul_errrec_dra_bits)
425 }, {
426 	OCP_F_ERRREC(dra2_to),
427 	.nf_short = "dra2to",
428 	.nf_desc = "Device Recovery Action 2 Timeout",
429 	.nf_rev = 3,
430 	.nf_type = NVMEADM_FT_HEX
431 }, {
432 	OCP_F_ERRREC(npanic),
433 	.nf_short = "npanic",
434 	.nf_desc = "Panic Count",
435 	.nf_rev = 3,
436 	.nf_type = NVMEADM_FT_HEX
437 }, {
438 	OCP_F_ERRREC(old_panics[0]),
439 	.nf_short = "ppanic1",
440 	.nf_desc = "Previous Panic N-1",
441 	.nf_rev = 3,
442 	.nf_type = NVMEADM_FT_HEX
443 }, {
444 	OCP_F_ERRREC(old_panics[1]),
445 	.nf_short = "ppanic2",
446 	.nf_desc = "Previous Panic N-2",
447 	.nf_rev = 3,
448 	.nf_type = NVMEADM_FT_HEX
449 }, {
450 	OCP_F_ERRREC(old_panics[2]),
451 	.nf_short = "ppanic3",
452 	.nf_desc = "Previous Panic N-3",
453 	.nf_rev = 3,
454 	.nf_type = NVMEADM_FT_HEX
455 }, {
456 	OCP_F_ERRREC(old_panics[3]),
457 	.nf_short = "ppanic4",
458 	.nf_desc = "Previous Panic N-4",
459 	.nf_rev = 3,
460 	.nf_type = NVMEADM_FT_HEX
461 }, {
462 	OCP_F_ERRREC(vers),
463 	.nf_short = "lpv",
464 	.nf_desc = "Log Page Version",
465 	.nf_type = NVMEADM_FT_HEX
466 }, {
467 	OCP_F_ERRREC(guid),
468 	.nf_short = "lpg",
469 	.nf_desc = "Log Page GUID",
470 	.nf_type = NVMEADM_FT_GUID
471 } };
472 
473 static uint32_t
ocp_vul_errrec_getvers(const void * data,size_t len)474 ocp_vul_errrec_getvers(const void *data, size_t len)
475 {
476 	if (len < sizeof (ocp_vul_errrec_t)) {
477 		errx(-1, "cannot parse revision information, found 0x%zx "
478 		    "bytes, need at least 0x%zx", len,
479 		    sizeof (ocp_vul_errrec_t));
480 	}
481 
482 	const ocp_vul_errrec_t *log = data;
483 	return (log->oer_vers);
484 }
485 
486 const nvmeadm_log_field_info_t ocp_vul_errrec_field_info = {
487 	.nlfi_log = "ocp/errrec",
488 	.nlfi_fields = ocp_vul_errrec_fields,
489 	.nlfi_nfields = ARRAY_SIZE(ocp_vul_errrec_fields),
490 	.nlfi_min = sizeof (ocp_vul_errrec_t),
491 	.nlfi_getrev = ocp_vul_errrec_getvers
492 };
493 
494 #define	OCP_F_DEVCAP(f)	.nf_off = offsetof(ocp_vul_devcap_t, odc_##f), \
495 	.nf_len = sizeof (((ocp_vul_devcap_t *)NULL)->odc_##f)
496 
497 #define	OCP_F_DEVCAP_PSD(f)	{ .nf_off = offsetof(ocp_vul_devcap_t, \
498 	odc_dssd[f]), \
499 	.nf_len = sizeof (((ocp_vul_devcap_t *)NULL)->odc_dssd[f]), \
500 	.nf_short = "psd" #f, .nf_desc = "DSSD Power State Descriptor " #f, \
501 	NVMEADM_F_BITS(ocp_vul_devcap_psd_bits) }
502 
503 static const nvmeadm_field_bit_t ocp_vul_devcap_oob_bits[] = { {
504 	.nfb_lowbit = 0, .nfb_hibit = 0,
505 	.nfb_short = "smbus",
506 	.nfb_desc = "MCTP over SMBus",
507 	.nfb_type = NVMEADM_FT_STRMAP,
508 	.nfb_strs = { "unsupported", "supported" }
509 }, {
510 	.nfb_lowbit = 1, .nfb_hibit = 1,
511 	.nfb_short = "vdm",
512 	.nfb_desc = "MCTP over PCIe VDM",
513 	.nfb_type = NVMEADM_FT_STRMAP,
514 	.nfb_strs = { "unsupported", "supported" }
515 }, {
516 	.nfb_lowbit = 2, .nfb_hibit = 2,
517 	.nfb_short = "bmc",
518 	.nfb_desc = "NVMe Basic Management Command",
519 	.nfb_type = NVMEADM_FT_STRMAP,
520 	.nfb_strs = { "unsupported", "supported" }
521 }, {
522 	.nfb_lowbit = 15, .nfb_hibit = 15,
523 	.nfb_short = "pass",
524 	.nfb_desc = "Meets OOB Management Requirements",
525 	.nfb_type = NVMEADM_FT_STRMAP,
526 	.nfb_strs = { "no", "yes" }
527 } };
528 
529 static const nvmeadm_field_bit_t ocp_vul_devcap_wz_bits[] = { {
530 	.nfb_lowbit = 0, .nfb_hibit = 0,
531 	.nfb_short = "wz",
532 	.nfb_desc = "Write Zeros Command",
533 	.nfb_type = NVMEADM_FT_STRMAP,
534 	.nfb_strs = { "unsupported", "supported" }
535 }, {
536 	.nfb_lowbit = 1, .nfb_hibit = 1,
537 	.nfb_short = "deac",
538 	.nfb_desc = "Setting DEAC Bit",
539 	.nfb_type = NVMEADM_FT_STRMAP,
540 	.nfb_strs = { "unsupported", "supported" }
541 }, {
542 	.nfb_lowbit = 2, .nfb_hibit = 2,
543 	.nfb_short = "fua",
544 	.nfb_desc = "Setting FUA Bit",
545 	.nfb_type = NVMEADM_FT_STRMAP,
546 	.nfb_strs = { "unsupported", "supported" }
547 }, {
548 	.nfb_lowbit = 3, .nfb_hibit = 3,
549 	.nfb_short = "io5",
550 	.nfb_desc = "NVMe-IO-5 Requirements",
551 	.nfb_type = NVMEADM_FT_STRMAP,
552 	.nfb_strs = { "unsupported", "supported" }
553 }, {
554 	.nfb_lowbit = 4, .nfb_hibit = 4,
555 	.nfb_short = "io6",
556 	.nfb_desc = "NVMe-IO-6 Requirements",
557 	.nfb_type = NVMEADM_FT_STRMAP,
558 	.nfb_strs = { "unsupported", "supported" }
559 }, {
560 	.nfb_lowbit = 15, .nfb_hibit = 15,
561 	.nfb_short = "pass",
562 	.nfb_desc = "Meets Write Zeros Requirements",
563 	.nfb_type = NVMEADM_FT_STRMAP,
564 	.nfb_strs = { "no", "yes" }
565 } };
566 
567 static const nvmeadm_field_bit_t ocp_vul_devcap_san_bits[] = { {
568 	.nfb_lowbit = 0, .nfb_hibit = 0,
569 	.nfb_short = "san",
570 	.nfb_desc = "Sanitize Command",
571 	.nfb_type = NVMEADM_FT_STRMAP,
572 	.nfb_strs = { "unsupported", "supported" }
573 }, {
574 	.nfb_lowbit = 1, .nfb_hibit = 1,
575 	.nfb_short = "crypto",
576 	.nfb_desc = "Crypto-Erase",
577 	.nfb_type = NVMEADM_FT_STRMAP,
578 	.nfb_strs = { "unsupported", "supported" }
579 }, {
580 	.nfb_lowbit = 2, .nfb_hibit = 2,
581 	.nfb_short = "block",
582 	.nfb_desc = "Block Erase",
583 	.nfb_type = NVMEADM_FT_STRMAP,
584 	.nfb_strs = { "unsupported", "supported" }
585 }, {
586 	.nfb_lowbit = 3, .nfb_hibit = 3,
587 	.nfb_short = "ovr",
588 	.nfb_desc = "Overwrite",
589 	.nfb_type = NVMEADM_FT_STRMAP,
590 	.nfb_strs = { "unsupported", "supported" }
591 }, {
592 	.nfb_lowbit = 4, .nfb_hibit = 4,
593 	.nfb_short = "dea",
594 	.nfb_desc = "Deallocate LBAs",
595 	.nfb_type = NVMEADM_FT_STRMAP,
596 	.nfb_strs = { "unsupported", "supported" }
597 }, {
598 	.nfb_lowbit = 15, .nfb_hibit = 15,
599 	.nfb_short = "pass",
600 	.nfb_desc = "Meets Sanitize Requirements",
601 	.nfb_type = NVMEADM_FT_STRMAP,
602 	.nfb_strs = { "no", "yes" }
603 } };
604 
605 static const nvmeadm_field_bit_t ocp_vul_devcap_ds_bits[] = { {
606 	.nfb_lowbit = 0, .nfb_hibit = 0,
607 	.nfb_short = "dsmgmt",
608 	.nfb_desc = "Dataset Management Command",
609 	.nfb_type = NVMEADM_FT_STRMAP,
610 	.nfb_strs = { "unsupported", "supported" }
611 }, {
612 	.nfb_lowbit = 1, .nfb_hibit = 1,
613 	.nfb_short = "ad",
614 	.nfb_desc = "Attribute Deallocate",
615 	.nfb_type = NVMEADM_FT_STRMAP,
616 	.nfb_strs = { "unsupported", "supported" }
617 }, {
618 	.nfb_lowbit = 15, .nfb_hibit = 15,
619 	.nfb_short = "pass",
620 	.nfb_desc = "Meets Dataset Management Requirements",
621 	.nfb_type = NVMEADM_FT_STRMAP,
622 	.nfb_strs = { "no", "yes" }
623 } };
624 
625 static const nvmeadm_field_bit_t ocp_vul_devcap_wu_bits[] = { {
626 	.nfb_lowbit = 0, .nfb_hibit = 0,
627 	.nfb_short = "wu",
628 	.nfb_desc = "Write Uncorrectable Command",
629 	.nfb_type = NVMEADM_FT_STRMAP,
630 	.nfb_strs = { "unsupported", "supported" }
631 }, {
632 	.nfb_lowbit = 1, .nfb_hibit = 1,
633 	.nfb_short = "slba",
634 	.nfb_desc = "Single LBA",
635 	.nfb_type = NVMEADM_FT_STRMAP,
636 	.nfb_strs = { "unsupported", "supported" }
637 }, {
638 	.nfb_lowbit = 2, .nfb_hibit = 2,
639 	.nfb_short = "maxlba",
640 	.nfb_desc = "Maximum Number of LBAs",
641 	.nfb_type = NVMEADM_FT_STRMAP,
642 	.nfb_strs = { "unsupported", "supported" }
643 }, {
644 	.nfb_lowbit = 3, .nfb_hibit = 3,
645 	.nfb_short = "io14",
646 	.nfb_desc = "NVMe-IO-14",
647 	.nfb_type = NVMEADM_FT_STRMAP,
648 	.nfb_strs = { "unsupported", "supported" }
649 }, {
650 	.nfb_lowbit = 15, .nfb_hibit = 15,
651 	.nfb_short = "pass",
652 	.nfb_desc = "Meets Write Uncorrectable Requirements",
653 	.nfb_type = NVMEADM_FT_STRMAP,
654 	.nfb_strs = { "no", "yes" }
655 } };
656 
657 static const nvmeadm_field_bit_t ocp_vul_devcap_fuse_bits[] = { {
658 	.nfb_lowbit = 0, .nfb_hibit = 0,
659 	.nfb_short = "cmpwr",
660 	.nfb_desc = "Compare and Write Fused Command",
661 	.nfb_type = NVMEADM_FT_STRMAP,
662 	.nfb_strs = { "unsupported", "supported" }
663 }, {
664 	.nfb_lowbit = 15, .nfb_hibit = 15,
665 	.nfb_short = "pass",
666 	.nfb_desc = "Meets Fused Command Requirements",
667 	.nfb_type = NVMEADM_FT_STRMAP,
668 	.nfb_strs = { "no", "yes" }
669 } };
670 
671 static const nvmeadm_field_bit_t ocp_vul_devcap_psd_bits[] = {  {
672 	.nfb_lowbit = 0, .nfb_hibit = 4,
673 	.nfb_short = "ps",
674 	.nfb_desc = "NVMe Power State",
675 	.nfb_type = NVMEADM_FT_HEX
676 }, {
677 	.nfb_lowbit = 7, .nfb_hibit = 7,
678 	.nfb_short = "valid",
679 	.nfb_desc = "Valid DSSD Power State",
680 	.nfb_type = NVMEADM_FT_STRMAP,
681 	.nfb_strs = { "no", "yes" }
682 } };
683 
684 static const nvmeadm_field_t ocp_vul_devcap_fields[] = { {
685 	OCP_F_DEVCAP(nports),
686 	.nf_short = "nports",
687 	.nf_desc = "PCI Express Ports",
688 	.nf_type = NVMEADM_FT_HEX
689 }, {
690 	OCP_F_DEVCAP(oob_sup),
691 	.nf_short = "oob",
692 	.nf_desc = "OOB Management Support",
693 	NVMEADM_F_BITS(ocp_vul_devcap_oob_bits)
694 }, {
695 	OCP_F_DEVCAP(wz_sup),
696 	.nf_short = "wz",
697 	.nf_desc = "Write Zeroes Command Support",
698 	NVMEADM_F_BITS(ocp_vul_devcap_wz_bits)
699 }, {
700 	OCP_F_DEVCAP(san_sup),
701 	.nf_short = "san",
702 	.nf_desc = "Sanitize Command Support",
703 	NVMEADM_F_BITS(ocp_vul_devcap_san_bits)
704 }, {
705 	OCP_F_DEVCAP(dsmgmt_sup),
706 	.nf_short = "ds",
707 	.nf_desc = "Dataset Management Support",
708 	NVMEADM_F_BITS(ocp_vul_devcap_ds_bits)
709 }, {
710 	OCP_F_DEVCAP(wunc_sup),
711 	.nf_short = "wu",
712 	.nf_desc = "Write Uncorrectable Command Support",
713 	NVMEADM_F_BITS(ocp_vul_devcap_wu_bits)
714 }, {
715 	OCP_F_DEVCAP(fuse_sup),
716 	.nf_short = "fuse",
717 	.nf_desc = "Fused Operations Support",
718 	NVMEADM_F_BITS(ocp_vul_devcap_fuse_bits)
719 }, {
720 	OCP_F_DEVCAP(dssd_min_valid),
721 	.nf_short = "minps",
722 	.nf_desc = "Minimum Valid DSSD Power State",
723 	.nf_type = NVMEADM_FT_HEX
724 },
725 	OCP_F_DEVCAP_PSD(1), OCP_F_DEVCAP_PSD(2), OCP_F_DEVCAP_PSD(3),
726 	OCP_F_DEVCAP_PSD(4), OCP_F_DEVCAP_PSD(5), OCP_F_DEVCAP_PSD(6),
727 	OCP_F_DEVCAP_PSD(7), OCP_F_DEVCAP_PSD(8), OCP_F_DEVCAP_PSD(9),
728 	OCP_F_DEVCAP_PSD(10), OCP_F_DEVCAP_PSD(11), OCP_F_DEVCAP_PSD(12),
729 	OCP_F_DEVCAP_PSD(13), OCP_F_DEVCAP_PSD(14), OCP_F_DEVCAP_PSD(15),
730 	OCP_F_DEVCAP_PSD(16), OCP_F_DEVCAP_PSD(17), OCP_F_DEVCAP_PSD(18),
731 	OCP_F_DEVCAP_PSD(19), OCP_F_DEVCAP_PSD(20), OCP_F_DEVCAP_PSD(21),
732 	OCP_F_DEVCAP_PSD(22), OCP_F_DEVCAP_PSD(23), OCP_F_DEVCAP_PSD(24),
733 	OCP_F_DEVCAP_PSD(25), OCP_F_DEVCAP_PSD(26), OCP_F_DEVCAP_PSD(27),
734 	OCP_F_DEVCAP_PSD(28), OCP_F_DEVCAP_PSD(29), OCP_F_DEVCAP_PSD(30),
735 	OCP_F_DEVCAP_PSD(31), OCP_F_DEVCAP_PSD(32), OCP_F_DEVCAP_PSD(33),
736 	OCP_F_DEVCAP_PSD(34), OCP_F_DEVCAP_PSD(35), OCP_F_DEVCAP_PSD(36),
737 	OCP_F_DEVCAP_PSD(37), OCP_F_DEVCAP_PSD(38), OCP_F_DEVCAP_PSD(39),
738 	OCP_F_DEVCAP_PSD(40), OCP_F_DEVCAP_PSD(41), OCP_F_DEVCAP_PSD(42),
739 	OCP_F_DEVCAP_PSD(43), OCP_F_DEVCAP_PSD(44), OCP_F_DEVCAP_PSD(45),
740 	OCP_F_DEVCAP_PSD(46), OCP_F_DEVCAP_PSD(47), OCP_F_DEVCAP_PSD(48),
741 	OCP_F_DEVCAP_PSD(49), OCP_F_DEVCAP_PSD(50), OCP_F_DEVCAP_PSD(51),
742 	OCP_F_DEVCAP_PSD(52), OCP_F_DEVCAP_PSD(53), OCP_F_DEVCAP_PSD(54),
743 	OCP_F_DEVCAP_PSD(55), OCP_F_DEVCAP_PSD(56), OCP_F_DEVCAP_PSD(57),
744 	OCP_F_DEVCAP_PSD(58), OCP_F_DEVCAP_PSD(59), OCP_F_DEVCAP_PSD(60),
745 	OCP_F_DEVCAP_PSD(61), OCP_F_DEVCAP_PSD(62), OCP_F_DEVCAP_PSD(63),
746 	OCP_F_DEVCAP_PSD(64), OCP_F_DEVCAP_PSD(65), OCP_F_DEVCAP_PSD(66),
747 	OCP_F_DEVCAP_PSD(67), OCP_F_DEVCAP_PSD(68), OCP_F_DEVCAP_PSD(69),
748 	OCP_F_DEVCAP_PSD(70), OCP_F_DEVCAP_PSD(71), OCP_F_DEVCAP_PSD(72),
749 	OCP_F_DEVCAP_PSD(73), OCP_F_DEVCAP_PSD(74), OCP_F_DEVCAP_PSD(75),
750 	OCP_F_DEVCAP_PSD(76), OCP_F_DEVCAP_PSD(77), OCP_F_DEVCAP_PSD(78),
751 	OCP_F_DEVCAP_PSD(79), OCP_F_DEVCAP_PSD(80), OCP_F_DEVCAP_PSD(81),
752 	OCP_F_DEVCAP_PSD(82), OCP_F_DEVCAP_PSD(83), OCP_F_DEVCAP_PSD(84),
753 	OCP_F_DEVCAP_PSD(85), OCP_F_DEVCAP_PSD(86), OCP_F_DEVCAP_PSD(87),
754 	OCP_F_DEVCAP_PSD(88), OCP_F_DEVCAP_PSD(89), OCP_F_DEVCAP_PSD(90),
755 	OCP_F_DEVCAP_PSD(91), OCP_F_DEVCAP_PSD(92), OCP_F_DEVCAP_PSD(93),
756 	OCP_F_DEVCAP_PSD(94), OCP_F_DEVCAP_PSD(95), OCP_F_DEVCAP_PSD(96),
757 	OCP_F_DEVCAP_PSD(97), OCP_F_DEVCAP_PSD(98), OCP_F_DEVCAP_PSD(99),
758 	OCP_F_DEVCAP_PSD(100), OCP_F_DEVCAP_PSD(101), OCP_F_DEVCAP_PSD(102),
759 	OCP_F_DEVCAP_PSD(103), OCP_F_DEVCAP_PSD(104), OCP_F_DEVCAP_PSD(105),
760 	OCP_F_DEVCAP_PSD(106), OCP_F_DEVCAP_PSD(107), OCP_F_DEVCAP_PSD(108),
761 	OCP_F_DEVCAP_PSD(109), OCP_F_DEVCAP_PSD(110), OCP_F_DEVCAP_PSD(111),
762 	OCP_F_DEVCAP_PSD(112), OCP_F_DEVCAP_PSD(113), OCP_F_DEVCAP_PSD(114),
763 	OCP_F_DEVCAP_PSD(115), OCP_F_DEVCAP_PSD(116), OCP_F_DEVCAP_PSD(117),
764 	OCP_F_DEVCAP_PSD(118), OCP_F_DEVCAP_PSD(119), OCP_F_DEVCAP_PSD(120),
765 	OCP_F_DEVCAP_PSD(121), OCP_F_DEVCAP_PSD(122), OCP_F_DEVCAP_PSD(123),
766 	OCP_F_DEVCAP_PSD(124), OCP_F_DEVCAP_PSD(125), OCP_F_DEVCAP_PSD(126),
767 	OCP_F_DEVCAP_PSD(127),
768 {
769 	OCP_F_DEVCAP(vers),
770 	.nf_short = "lpv",
771 	.nf_desc = "Log Page Version",
772 	.nf_type = NVMEADM_FT_HEX
773 }, {
774 	OCP_F_DEVCAP(guid),
775 	.nf_short = "lpg",
776 	.nf_desc = "Log Page GUID",
777 	.nf_type = NVMEADM_FT_GUID
778 } };
779 
780 static uint32_t
ocp_vul_devcap_getvers(const void * data,size_t len)781 ocp_vul_devcap_getvers(const void *data, size_t len)
782 {
783 	if (len < sizeof (ocp_vul_devcap_t)) {
784 		errx(-1, "cannot parse revision information, found 0x%zx "
785 		    "bytes, need at least 0x%zx", len,
786 		    sizeof (ocp_vul_devcap_t));
787 	}
788 
789 	const ocp_vul_devcap_t *log = data;
790 	return (log->odc_vers);
791 }
792 
793 const nvmeadm_log_field_info_t ocp_vul_devcap_field_info = {
794 	.nlfi_log = "ocp/devcap",
795 	.nlfi_fields = ocp_vul_devcap_fields,
796 	.nlfi_nfields = ARRAY_SIZE(ocp_vul_devcap_fields),
797 	.nlfi_min = sizeof (ocp_vul_devcap_t),
798 	.nlfi_getrev = ocp_vul_devcap_getvers
799 };
800 
801 #define	OCP_F_UNSUP(f)	.nf_off = offsetof(ocp_vul_unsup_req_t, our_##f), \
802 	.nf_len = sizeof (((ocp_vul_unsup_req_t *)NULL)->our_##f)
803 
804 static const nvmeadm_field_t ocp_vul_unsup_fields_head[] = { {
805 	OCP_F_UNSUP(nunsup),
806 	.nf_short = "count",
807 	.nf_desc = "Unsupported Count",
808 	.nf_type = NVMEADM_FT_HEX
809 } };
810 
811 static const nvmeadm_field_t ocp_vul_unsup_fields_tail[] = { {
812 	OCP_F_UNSUP(vers),
813 	.nf_short = "lpv",
814 	.nf_desc = "Log Page Version",
815 	.nf_type = NVMEADM_FT_HEX
816 }, {
817 	OCP_F_UNSUP(guid),
818 	.nf_short = "lpg",
819 	.nf_desc = "Log Page GUID",
820 	.nf_type = NVMEADM_FT_GUID
821 } };
822 
823 static uint32_t
ocp_vul_unsup_getvers(const void * data,size_t len)824 ocp_vul_unsup_getvers(const void *data, size_t len)
825 {
826 	if (len < sizeof (ocp_vul_unsup_req_t)) {
827 		errx(-1, "cannot parse revision information, found 0x%zx "
828 		    "bytes, need at least 0x%zx", len,
829 		    sizeof (ocp_vul_unsup_req_t));
830 	}
831 
832 	const ocp_vul_unsup_req_t *log = data;
833 	return (log->our_vers);
834 }
835 
836 /*
837  * We manually drive this so we can create the appropriate number of entries for
838  * the string table as there are a variable number of these.
839  */
840 static bool
ocp_vul_unsup_drive(nvmeadm_field_print_t * print,const void * data,size_t len)841 ocp_vul_unsup_drive(nvmeadm_field_print_t *print, const void *data, size_t len)
842 {
843 	print->fp_header = NULL;
844 	print->fp_fields = ocp_vul_unsup_fields_head;
845 	print->fp_nfields = ARRAY_SIZE(ocp_vul_unsup_fields_head);
846 	print->fp_base = NULL;
847 	print->fp_data = data;
848 	print->fp_dlen = len;
849 	print->fp_off = 0;
850 	nvmeadm_field_print(print);
851 
852 	/*
853 	 * Look at the data and make sure we have an appropriate number of
854 	 * entries specified. While there is a uint16_t worth of entries the
855 	 * specification indicates there can be a maximum of 253.
856 	 */
857 	const ocp_vul_unsup_req_t *log = data;
858 	if (log->our_nunsup > 253) {
859 		warnx("log page has questionable data: log page count of "
860 		    "unsupported requirements %u exceeds spec max of 253",
861 		    log->our_nunsup);
862 	}
863 	size_t nlogs = MIN(log->our_nunsup, 253);
864 	for (size_t i = 0; i < nlogs; i++) {
865 		nvmeadm_field_t field;
866 		char shrt[32];
867 		char desc[128];
868 
869 		(void) snprintf(shrt, sizeof (shrt), "ureq%zu", i);
870 		(void) snprintf(desc, sizeof (desc), "Unsupported Requirement "
871 		    "%zu", i);
872 		(void) memset(&field, 0, sizeof (nvmeadm_field_t));
873 		field.nf_off = offsetof(ocp_vul_unsup_req_t, our_reqs[i]);
874 		field.nf_len = sizeof (ocp_req_str_t);
875 		field.nf_short = shrt;
876 		field.nf_desc = desc;
877 		field.nf_type = NVMEADM_FT_ASCIIZ;
878 
879 		print->fp_fields = &field;
880 		print->fp_nfields = 1;
881 		nvmeadm_field_print(print);
882 	}
883 
884 	print->fp_fields = ocp_vul_unsup_fields_tail;
885 	print->fp_nfields = ARRAY_SIZE(ocp_vul_unsup_fields_tail);
886 	nvmeadm_field_print(print);
887 	return (true);
888 }
889 
890 const nvmeadm_log_field_info_t ocp_vul_unsup_field_info = {
891 	.nlfi_log = "ocp/unsup",
892 	.nlfi_min = sizeof (ocp_vul_unsup_req_t),
893 	.nlfi_getrev = ocp_vul_unsup_getvers,
894 	.nlfi_drive = ocp_vul_unsup_drive
895 };
896 
897 #define	OCP_F_TELSTR(f)	.nf_off = offsetof(ocp_vul_telstr_t, ots_##f), \
898 	.nf_len = sizeof (((ocp_vul_telstr_t *)NULL)->ots_##f)
899 
900 static const nvmeadm_field_t ocp_vul_telstr_fields[] = { {
901 	OCP_F_TELSTR(vers),
902 	.nf_short = "lpv",
903 	.nf_desc = "Log Page Version",
904 	.nf_type = NVMEADM_FT_HEX
905 }, {
906 	OCP_F_TELSTR(guid),
907 	.nf_short = "lpg",
908 	.nf_desc = "Log Page GUID",
909 	.nf_type = NVMEADM_FT_GUID
910 }, {
911 	OCP_F_TELSTR(sls),
912 	.nf_short = "sls",
913 	.nf_desc = "Telemetry String Log Size",
914 	.nf_type = NVMEADM_FT_HEX,
915 	.nf_addend = { .nfa_shift = 2 }
916 }, {
917 	OCP_F_TELSTR(sits),
918 	.nf_short = "sits",
919 	.nf_desc = "Statistics Identifier String Table Start",
920 	.nf_type = NVMEADM_FT_HEX,
921 	.nf_addend = { .nfa_shift = 2 }
922 }, {
923 	OCP_F_TELSTR(sitz),
924 	.nf_short = "sitz",
925 	.nf_desc = "Statistics Identifier String Table Size",
926 	.nf_type = NVMEADM_FT_HEX,
927 	.nf_addend = { .nfa_shift = 2 }
928 }, {
929 	OCP_F_TELSTR(ests),
930 	.nf_short = "ests",
931 	.nf_desc = "Event String Table Start",
932 	.nf_type = NVMEADM_FT_HEX,
933 	.nf_addend = { .nfa_shift = 2 }
934 }, {
935 	OCP_F_TELSTR(estz),
936 	.nf_short = "estz",
937 	.nf_desc = "Event String Table Size",
938 	.nf_type = NVMEADM_FT_HEX,
939 	.nf_addend = { .nfa_shift = 2 }
940 }, {
941 	OCP_F_TELSTR(vuests),
942 	.nf_short = "vuests",
943 	.nf_desc = "VU Event String Table Start",
944 	.nf_type = NVMEADM_FT_HEX,
945 	.nf_addend = { .nfa_shift = 2 }
946 }, {
947 	OCP_F_TELSTR(vuestz),
948 	.nf_short = "vuestz",
949 	.nf_desc = "VU Event String Table Size",
950 	.nf_type = NVMEADM_FT_HEX,
951 	.nf_addend = { .nfa_shift = 2 }
952 }, {
953 	OCP_F_TELSTR(ascts),
954 	.nf_short = "asctss",
955 	.nf_desc = "ASCII Table Start",
956 	.nf_type = NVMEADM_FT_HEX,
957 	.nf_addend = { .nfa_shift = 2 }
958 }, {
959 	OCP_F_TELSTR(asctz),
960 	.nf_short = "asctsz",
961 	.nf_desc = "ASCII Table Size",
962 	.nf_type = NVMEADM_FT_HEX,
963 	.nf_addend = { .nfa_shift = 2 }
964 } };
965 
966 #define	OCP_F_TELSTR_SIT(f)	.nf_off = offsetof(ocp_vul_telstr_sit_t, \
967 	ocp_sit_##f), \
968 	.nf_len = sizeof (((ocp_vul_telstr_sit_t *)NULL)->ocp_sit_##f)
969 
970 static const nvmeadm_field_t ocp_vul_telstr_sit_fields[] = { {
971 	OCP_F_TELSTR_SIT(id),
972 	.nf_short = "id",
973 	.nf_desc = "Vendor Unique Statistic Identifier",
974 	.nf_type = NVMEADM_FT_HEX,
975 }, {
976 	OCP_F_TELSTR_SIT(len),
977 	.nf_short = "len",
978 	.nf_desc = "ASCII ID Length",
979 	.nf_type = NVMEADM_FT_HEX,
980 	.nf_addend = { .nfa_addend = 1 }
981 }, {
982 	OCP_F_TELSTR_SIT(off),
983 	.nf_short = "off",
984 	.nf_desc = "ASCII ID Offset",
985 	.nf_type = NVMEADM_FT_HEX,
986 	.nf_addend = { .nfa_shift = 2 }
987 } };
988 
989 #define	OCP_F_TELSTR_EST(f)	.nf_off = offsetof(ocp_vul_telstr_est_t, \
990 	ocp_est_##f), \
991 	.nf_len = sizeof (((ocp_vul_telstr_est_t *)NULL)->ocp_est_##f)
992 
993 /*
994  * This is the same currently for both the vendor unique and regular events so
995  * we use the same structure for the time being.
996  */
997 static const nvmeadm_field_t ocp_vul_telstr_est_fields[] = { {
998 	OCP_F_TELSTR_EST(class),
999 	.nf_short = "class",
1000 	.nf_desc = "Debug Event Class",
1001 	.nf_type = NVMEADM_FT_HEX,
1002 }, {
1003 	OCP_F_TELSTR_EST(eid),
1004 	.nf_short = "id",
1005 	.nf_desc = "Event Identifier",
1006 	.nf_type = NVMEADM_FT_HEX,
1007 }, {
1008 	OCP_F_TELSTR_EST(len),
1009 	.nf_short = "len",
1010 	.nf_desc = "ASCII ID Length",
1011 	.nf_type = NVMEADM_FT_HEX,
1012 	.nf_addend = { .nfa_addend = 1 }
1013 }, {
1014 	OCP_F_TELSTR_EST(off),
1015 	.nf_short = "off",
1016 	.nf_desc = "ASCII ID Offset",
1017 	.nf_type = NVMEADM_FT_HEX,
1018 	.nf_addend = { .nfa_shift = 2 }
1019 } };
1020 
1021 static uint32_t
ocp_vul_telstr_getvers(const void * data,size_t len)1022 ocp_vul_telstr_getvers(const void *data, size_t len)
1023 {
1024 	if (len < sizeof (ocp_vul_telstr_t)) {
1025 		errx(-1, "cannot parse revision information, found 0x%zx "
1026 		    "bytes, need at least 0x%zx", len,
1027 		    sizeof (ocp_vul_telstr_t));
1028 	}
1029 
1030 	const ocp_vul_telstr_t *log = data;
1031 	return (log->ots_vers);
1032 }
1033 
1034 static bool
ocp_vul_telstr_sanity(const char * name,uint64_t off_dw,uint64_t len_dw,size_t flen)1035 ocp_vul_telstr_sanity(const char *name, uint64_t off_dw, uint64_t len_dw,
1036     size_t flen)
1037 {
1038 	const uint64_t max_dw = UINT64_MAX / sizeof (uint32_t);
1039 
1040 	/*
1041 	 * These values are in units of uint32_t's. Make sure we can represent
1042 	 * them.
1043 	 */
1044 	if (off_dw > max_dw) {
1045 		warnx("telemetry log %s offset does not fit in a 64-bit "
1046 		    "quantity", name);
1047 		return (false);
1048 	}
1049 
1050 	if (len_dw > max_dw) {
1051 		warnx("telemetry log %s length does not fit in a 64-bit "
1052 		    "quantity", name);
1053 		return (false);
1054 	}
1055 
1056 	const uint64_t off_bytes = off_dw << 2;
1057 	const uint64_t len_bytes = len_dw << 2;
1058 
1059 	if (len_bytes > UINT64_MAX - off_bytes) {
1060 		warnx("telemetry log %s final offset would overflow a 64-bit "
1061 		    "quantity", name);
1062 		return (false);
1063 	}
1064 
1065 	const uint64_t end = off_bytes + len_bytes;
1066 	if (end > flen) {
1067 		warnx("telemetry log %s exceeds beyond the end of the file",
1068 		    name);
1069 		return (false);
1070 	}
1071 
1072 	return (true);
1073 }
1074 
1075 /*
1076  * Set up a field to print an ASCII string and error if the embedded information
1077  * is not useful.
1078  */
1079 static bool
ocp_vul_telstr_field_str(nvmeadm_field_t * field,uint16_t len0,uint64_t off_dw,uint64_t ascii_start,uint64_t ascii_len)1080 ocp_vul_telstr_field_str(nvmeadm_field_t *field, uint16_t len0, uint64_t off_dw,
1081     uint64_t ascii_start, uint64_t ascii_len)
1082 {
1083 	const uint64_t max_dw = UINT64_MAX / sizeof (uint32_t);
1084 
1085 	(void) memset(field, 0, sizeof (nvmeadm_field_t));
1086 
1087 	if (off_dw > max_dw) {
1088 		warnx("telemetry log ASCII string offset 0x%" PRIx64 " is "
1089 		    "not representable in a 64-bit quantity", off_dw);
1090 		return (false);
1091 	}
1092 
1093 	const uint64_t off = off_dw << 2;
1094 	const uint64_t len = len0 + 1;
1095 	if (len > UINT64_MAX - off) {
1096 		warnx("telemetry log ASCII string would overflow a 64-bit "
1097 		    "quantity: offset 0x%" PRIx64 ", length: %" PRIu64,
1098 		    off, len);
1099 		return (false);
1100 	}
1101 
1102 	if (off + len > ascii_start + ascii_len) {
1103 		warnx("telemetry log ASCII string exceeds ASCII table");
1104 		return (false);
1105 	}
1106 
1107 	field->nf_len = len;
1108 	field->nf_off = off + ascii_start;
1109 	field->nf_short = "str";
1110 	field->nf_desc = "String";
1111 	/*
1112 	 * Vendors are inconsistent as to whether the string table is padded
1113 	 * with zeros or spaces. Use ASCIIZ here to account for both.
1114 	 */
1115 	field->nf_type = NVMEADM_FT_ASCIIZ;
1116 
1117 	return (true);
1118 }
1119 
1120 /*
1121  * The telemetry string table is comprised of a fixed section and then a number
1122  * of variable sections that point into the ASCII table, somewhat analogous to
1123  * an ELF string table. There is no good way to see where the various strings
1124  * begin and end in the ASCII table. There is no strict separator between
1125  * entries. Entries are space padded to the next u32 aligned point generally;
1126  * however, the presence or lack of spaces doesn't tell us where something
1127  * begins or ends.
1128  *
1129  * As such, we manually drive this and relate the ASCII strings to the
1130  * corresponding other tables that we encounter. This isn't the most eloquent;
1131  * however, there's no other good way to do display this programmatically.
1132  */
1133 static bool
ocp_vul_telstr_drive(nvmeadm_field_print_t * print,const void * data,size_t len)1134 ocp_vul_telstr_drive(nvmeadm_field_print_t *print, const void *data, size_t len)
1135 {
1136 	const ocp_vul_telstr_t *telstr = data;
1137 	bool ret = true;
1138 
1139 	print->fp_header = "Telemetry String Header";
1140 	print->fp_fields = ocp_vul_telstr_fields;
1141 	print->fp_nfields = ARRAY_SIZE(ocp_vul_telstr_fields);
1142 	print->fp_base = "tsh";
1143 	print->fp_data = data;
1144 	print->fp_dlen = len;
1145 	print->fp_off = 0;
1146 	nvmeadm_field_print(print);
1147 
1148 	/*
1149 	 * First take care of the 16 FIFOs. If a FIFO has a totally zero string,
1150 	 * then we should ignore it. This is the last data entry that we're
1151 	 * guaranteed we have space for. Everything else after this needs to be
1152 	 * checked for paranoia and consistency.
1153 	 */
1154 	for (size_t i = 0; i < 16; i++) {
1155 		char shrt[32], desc[128];
1156 		nvmeadm_field_t field;
1157 		const uint8_t empty[16] = { 0 };
1158 
1159 		(void) snprintf(shrt, sizeof (shrt), "fifo%zu", i);
1160 		(void) snprintf(desc, sizeof (desc), "FIFO %zu", i);
1161 		(void) memset(&field, 0, sizeof (nvmeadm_field_t));
1162 		field.nf_len = sizeof (((ocp_vul_telstr_t *)NULL)->ots_fifo0);
1163 		field.nf_off = offsetof(ocp_vul_telstr_t, ots_fifo0) +
1164 		    i * field.nf_len;
1165 		field.nf_short = shrt;
1166 		field.nf_desc = desc;
1167 		field.nf_type = NVMEADM_FT_ASCIIZ;
1168 
1169 		if (memcmp(data + field.nf_off, empty, sizeof (empty)) == 0) {
1170 			continue;
1171 		}
1172 
1173 		print->fp_header = NULL;
1174 		print->fp_fields = &field;
1175 		print->fp_nfields = 1;
1176 		nvmeadm_field_print(print);
1177 	}
1178 
1179 	/*
1180 	 * Sanity check that the rest of this makes sense. In particular, this
1181 	 * is supposed to be ordered SITS, ESTS, VUETS, ASCTS. Make sure these
1182 	 * don't overlap, that the offsets don't cause an overflow when we
1183 	 * expand them, etc.
1184 	 */
1185 	if (!ocp_vul_telstr_sanity("sit", telstr->ots_sits, telstr->ots_sitz,
1186 	    len) ||
1187 	    !ocp_vul_telstr_sanity("est", telstr->ots_ests, telstr->ots_estz,
1188 	    len) ||
1189 	    !ocp_vul_telstr_sanity("vuest", telstr->ots_vuests,
1190 	    telstr->ots_vuestz, len) ||
1191 	    !ocp_vul_telstr_sanity("asct", telstr->ots_ascts, telstr->ots_asctz,
1192 	    len)) {
1193 		return (false);
1194 	}
1195 
1196 	const uint64_t sit_start = telstr->ots_sits << 2;
1197 	const uint64_t sit_len = telstr->ots_sitz << 2;
1198 	const uint64_t est_start = telstr->ots_ests << 2;
1199 	const uint64_t est_len = telstr->ots_estz << 2;
1200 	const uint64_t vu_start = telstr->ots_vuests << 2;
1201 	const uint64_t vu_len = telstr->ots_vuestz << 2;
1202 	const uint64_t ascii_start = telstr->ots_ascts << 2;
1203 	const uint64_t ascii_len = telstr->ots_asctz << 2;
1204 
1205 	if (sit_start != offsetof(ocp_vul_telstr_t, ots_data)) {
1206 		warnx("invalid telemetry string table: SIT table starts at "
1207 		    "unexpected offset 0x%" PRIx64, sit_start);
1208 		return (false);
1209 	}
1210 
1211 	if (est_start < sit_start + sit_len) {
1212 		warnx("invalid telemetry string table: EST table starts before "
1213 		    "SIT table ends");
1214 		return (false);
1215 	}
1216 
1217 	if (vu_start < est_start + est_len) {
1218 		warnx("invalid telemetry string table: VUEST table starts "
1219 		    "before EST table ends");
1220 		return (false);
1221 	}
1222 
1223 	if (ascii_start < vu_start + vu_len) {
1224 		warnx("invalid telemetry string table: ASCT table starts "
1225 		    "before VUEST table ends");
1226 		return (false);
1227 	}
1228 
1229 	print->fp_header = "Statistic Identifier Table";
1230 	print->fp_base = "sit";
1231 	const uint64_t sit_nents = sit_len / sizeof (ocp_vul_telstr_sit_t);
1232 	for (uint64_t i = 0; i < sit_nents; i++) {
1233 		char shrt[32], desc[128];
1234 		const size_t off = sit_start + i *
1235 		    sizeof (ocp_vul_telstr_sit_t);
1236 		const ocp_vul_telstr_sit_t *sit = data + off;
1237 		nvmeadm_field_t cont;
1238 		nvmeadm_field_t fields[ARRAY_SIZE(ocp_vul_telstr_sit_fields) +
1239 		    1];
1240 
1241 		(void) memcpy(fields, ocp_vul_telstr_sit_fields,
1242 		    sizeof (ocp_vul_telstr_sit_fields));
1243 		if (!ocp_vul_telstr_field_str(&fields[ARRAY_SIZE(fields) - 1],
1244 		    sit->ocp_sit_len, sit->ocp_sit_off, ascii_start,
1245 		    ascii_len)) {
1246 			ret = false;
1247 			continue;
1248 		}
1249 
1250 		for (size_t f = 0; f < ARRAY_SIZE(fields) - 1; f++) {
1251 			fields[f].nf_off += off;
1252 		}
1253 
1254 		(void) snprintf(shrt, sizeof (shrt), "%" PRIu64, i);
1255 		(void) snprintf(desc, sizeof (desc), "SIT Entry %" PRIu64, i);
1256 		(void) memset(&cont, 0, sizeof (nvmeadm_field_t));
1257 		cont.nf_off = 0;
1258 		cont.nf_len = sizeof (ocp_vul_telstr_sit_t);
1259 		cont.nf_short = shrt;
1260 		cont.nf_desc = desc;
1261 		cont.nf_type = NVMEADM_FT_CONTAINER;
1262 		cont.nf_fields = fields;
1263 		cont.nf_nfields = ARRAY_SIZE(fields);
1264 
1265 		if (i > 0) {
1266 			print->fp_header = NULL;
1267 		}
1268 		print->fp_fields = &cont;
1269 		print->fp_nfields = 1;
1270 
1271 		nvmeadm_field_print(print);
1272 
1273 	}
1274 
1275 	print->fp_header = "Event Identifier Table";
1276 	print->fp_base = "est";
1277 	const uint64_t est_nents = est_len / sizeof (ocp_vul_telstr_est_t);
1278 	for (uint64_t i = 0; i < est_nents; i++) {
1279 		char shrt[32], desc[128];
1280 		const size_t off = est_start + i *
1281 		    sizeof (ocp_vul_telstr_est_t);
1282 		const ocp_vul_telstr_est_t *est = data + off;
1283 		nvmeadm_field_t cont;
1284 		nvmeadm_field_t fields[ARRAY_SIZE(ocp_vul_telstr_est_fields) +
1285 		    1];
1286 
1287 		(void) memcpy(fields, ocp_vul_telstr_est_fields,
1288 		    sizeof (ocp_vul_telstr_est_fields));
1289 		if (!ocp_vul_telstr_field_str(&fields[ARRAY_SIZE(fields) - 1],
1290 		    est->ocp_est_len, est->ocp_est_off, ascii_start,
1291 		    ascii_len)) {
1292 			ret = false;
1293 			continue;
1294 		}
1295 
1296 		for (size_t f = 0; f < ARRAY_SIZE(fields) - 1; f++) {
1297 			fields[f].nf_off += off;
1298 		}
1299 
1300 		(void) snprintf(shrt, sizeof (shrt), "%" PRIu64, i);
1301 		(void) snprintf(desc, sizeof (desc), "EST Entry %" PRIu64, i);
1302 		(void) memset(&cont, 0, sizeof (nvmeadm_field_t));
1303 		cont.nf_off = 0;
1304 		cont.nf_len = sizeof (ocp_vul_telstr_est_t);
1305 		cont.nf_short = shrt;
1306 		cont.nf_desc = desc;
1307 		cont.nf_type = NVMEADM_FT_CONTAINER;
1308 		cont.nf_fields = fields;
1309 		cont.nf_nfields = ARRAY_SIZE(fields);
1310 
1311 		if (i > 0) {
1312 			print->fp_header = NULL;
1313 		}
1314 		print->fp_fields = &cont;
1315 		print->fp_nfields = 1;
1316 
1317 		nvmeadm_field_print(print);
1318 	}
1319 
1320 	print->fp_header = "Vendor Unique Event Identifier Table";
1321 	print->fp_base = "vuest";
1322 	const uint64_t vuest_nents = vu_len / sizeof (ocp_vul_telstr_vuest_t);
1323 	for (uint64_t i = 0; i < vuest_nents; i++) {
1324 		char shrt[32], desc[128];
1325 		const size_t off = vu_start + i *
1326 		    sizeof (ocp_vul_telstr_vuest_t);
1327 		const ocp_vul_telstr_vuest_t *vuest = data + off;
1328 		nvmeadm_field_t cont;
1329 		nvmeadm_field_t fields[ARRAY_SIZE(ocp_vul_telstr_est_fields) +
1330 		    1];
1331 
1332 		(void) memcpy(fields, ocp_vul_telstr_est_fields,
1333 		    sizeof (ocp_vul_telstr_est_fields));
1334 		if (!ocp_vul_telstr_field_str(&fields[ARRAY_SIZE(fields) - 1],
1335 		    vuest->ocp_vuest_len, vuest->ocp_vuest_off, ascii_start,
1336 		    ascii_len)) {
1337 			ret = false;
1338 			continue;
1339 		}
1340 
1341 		for (size_t f = 0; f < ARRAY_SIZE(fields) - 1; f++) {
1342 			fields[f].nf_off += off;
1343 		}
1344 
1345 		(void) snprintf(shrt, sizeof (shrt), "%" PRIu64, i);
1346 		(void) snprintf(desc, sizeof (desc), "VUEST Entry %" PRIu64, i);
1347 		(void) memset(&cont, 0, sizeof (nvmeadm_field_t));
1348 		cont.nf_off = 0;
1349 		cont.nf_len = sizeof (ocp_vul_telstr_vuest_t);
1350 		cont.nf_short = shrt;
1351 		cont.nf_desc = desc;
1352 		cont.nf_type = NVMEADM_FT_CONTAINER;
1353 		cont.nf_fields = fields;
1354 		cont.nf_nfields = ARRAY_SIZE(fields);
1355 
1356 		if (i > 0) {
1357 			print->fp_header = NULL;
1358 		}
1359 		print->fp_fields = &cont;
1360 		print->fp_nfields = 1;
1361 
1362 		nvmeadm_field_print(print);
1363 	}
1364 
1365 	return (ret);
1366 }
1367 
1368 const nvmeadm_log_field_info_t ocp_vul_telstr_field_info = {
1369 	.nlfi_log = "ocp/telstr",
1370 	.nlfi_min = sizeof (ocp_vul_telstr_t),
1371 	.nlfi_getrev = ocp_vul_telstr_getvers,
1372 	.nlfi_drive = ocp_vul_telstr_drive
1373 };
1374