1*a3f161aeSRobert Mustacchi /*
2*a3f161aeSRobert Mustacchi * This file and its contents are supplied under the terms of the
3*a3f161aeSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*a3f161aeSRobert Mustacchi * You may only use this file in accordance with the terms of version
5*a3f161aeSRobert Mustacchi * 1.0 of the CDDL.
6*a3f161aeSRobert Mustacchi *
7*a3f161aeSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*a3f161aeSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*a3f161aeSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*a3f161aeSRobert Mustacchi */
11*a3f161aeSRobert Mustacchi
12*a3f161aeSRobert Mustacchi /*
13*a3f161aeSRobert Mustacchi * Copyright 2026 Oxide Computer Company
14*a3f161aeSRobert Mustacchi */
15*a3f161aeSRobert Mustacchi
16*a3f161aeSRobert Mustacchi /*
17*a3f161aeSRobert Mustacchi * Deal with Micron-specific logs.
18*a3f161aeSRobert Mustacchi */
19*a3f161aeSRobert Mustacchi
20*a3f161aeSRobert Mustacchi #include <err.h>
21*a3f161aeSRobert Mustacchi #include <string.h>
22*a3f161aeSRobert Mustacchi #include <sys/stddef.h>
23*a3f161aeSRobert Mustacchi #include <sys/sysmacros.h>
24*a3f161aeSRobert Mustacchi #include <sys/nvme/micron.h>
25*a3f161aeSRobert Mustacchi
26*a3f161aeSRobert Mustacchi #include "nvmeadm.h"
27*a3f161aeSRobert Mustacchi
28*a3f161aeSRobert Mustacchi /*
29*a3f161aeSRobert Mustacchi * Synthetic identifiers for these logs to deal with the changes that we have
30*a3f161aeSRobert Mustacchi * found in here over time. See micron_vul_ext_smart_getvers() for more.
31*a3f161aeSRobert Mustacchi */
32*a3f161aeSRobert Mustacchi #define MICRON_GEN_73XX 1
33*a3f161aeSRobert Mustacchi #define MICRON_GEN_74XX 2
34*a3f161aeSRobert Mustacchi
35*a3f161aeSRobert Mustacchi #define MICRON_F_SMART(f) \
36*a3f161aeSRobert Mustacchi .nf_off = offsetof(micron_vul_ext_smart_t, mes_##f), \
37*a3f161aeSRobert Mustacchi .nf_len = sizeof (((micron_vul_ext_smart_t *)NULL)->mes_##f)
38*a3f161aeSRobert Mustacchi
39*a3f161aeSRobert Mustacchi static const nvmeadm_field_bit_t micron_vul_ext_smart_wpr_bits[] = { {
40*a3f161aeSRobert Mustacchi .nfb_lowbit = 0, .nfb_hibit = 0,
41*a3f161aeSRobert Mustacchi .nfb_short = "dramue",
42*a3f161aeSRobert Mustacchi .nfb_desc = "DRAM Double Bit Error",
43*a3f161aeSRobert Mustacchi .nfb_type = NVMEADM_FT_STRMAP,
44*a3f161aeSRobert Mustacchi .nfb_strs = { "did not occur", "occurred" }
45*a3f161aeSRobert Mustacchi }, {
46*a3f161aeSRobert Mustacchi .nfb_lowbit = 1, .nfb_hibit = 1,
47*a3f161aeSRobert Mustacchi .nfb_short = "spare",
48*a3f161aeSRobert Mustacchi .nfb_desc = "Low Remaining Spare Block Count",
49*a3f161aeSRobert Mustacchi .nfb_type = NVMEADM_FT_STRMAP,
50*a3f161aeSRobert Mustacchi .nfb_strs = { "did not occur", "occurred" }
51*a3f161aeSRobert Mustacchi }, {
52*a3f161aeSRobert Mustacchi .nfb_lowbit = 2, .nfb_hibit = 2,
53*a3f161aeSRobert Mustacchi .nfb_short = "cap",
54*a3f161aeSRobert Mustacchi .nfb_desc = "Power Holdup Capacitor Failure",
55*a3f161aeSRobert Mustacchi .nfb_type = NVMEADM_FT_STRMAP,
56*a3f161aeSRobert Mustacchi .nfb_strs = { "did not occur", "occurred" }
57*a3f161aeSRobert Mustacchi }, {
58*a3f161aeSRobert Mustacchi .nfb_lowbit = 3, .nfb_hibit = 3,
59*a3f161aeSRobert Mustacchi .nfb_short = "nvram",
60*a3f161aeSRobert Mustacchi .nfb_desc = "NVRAM Checksum Failure",
61*a3f161aeSRobert Mustacchi .nfb_type = NVMEADM_FT_STRMAP,
62*a3f161aeSRobert Mustacchi .nfb_strs = { "did not occur", "occurred" }
63*a3f161aeSRobert Mustacchi }, {
64*a3f161aeSRobert Mustacchi .nfb_lowbit = 4, .nfb_hibit = 4,
65*a3f161aeSRobert Mustacchi .nfb_short = "daor",
66*a3f161aeSRobert Mustacchi .nfb_desc = "DRAM Address Out of Range",
67*a3f161aeSRobert Mustacchi .nfb_type = NVMEADM_FT_STRMAP,
68*a3f161aeSRobert Mustacchi .nfb_strs = { "did not occur", "occurred" }
69*a3f161aeSRobert Mustacchi }, {
70*a3f161aeSRobert Mustacchi .nfb_lowbit = 5, .nfb_hibit = 5,
71*a3f161aeSRobert Mustacchi .nfb_short = "temp",
72*a3f161aeSRobert Mustacchi .nfb_desc = "Overtemp Shutdown",
73*a3f161aeSRobert Mustacchi .nfb_type = NVMEADM_FT_STRMAP,
74*a3f161aeSRobert Mustacchi .nfb_strs = { "did not occur", "occurred" }
75*a3f161aeSRobert Mustacchi } };
76*a3f161aeSRobert Mustacchi
77*a3f161aeSRobert Mustacchi static const nvmeadm_field_t micron_vul_ext_smart_fields[] = { {
78*a3f161aeSRobert Mustacchi MICRON_F_SMART(gbb),
79*a3f161aeSRobert Mustacchi .nf_short = "gbb",
80*a3f161aeSRobert Mustacchi .nf_desc = "Grown Bad Block Count",
81*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
82*a3f161aeSRobert Mustacchi }, {
83*a3f161aeSRobert Mustacchi MICRON_F_SMART(max_erase),
84*a3f161aeSRobert Mustacchi .nf_short = "mec",
85*a3f161aeSRobert Mustacchi .nf_desc = "Per-Block Max Erase Count",
86*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
87*a3f161aeSRobert Mustacchi }, {
88*a3f161aeSRobert Mustacchi MICRON_F_SMART(power_on),
89*a3f161aeSRobert Mustacchi .nf_short = "pon",
90*a3f161aeSRobert Mustacchi .nf_desc = "Power-on",
91*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_UNIT,
92*a3f161aeSRobert Mustacchi .nf_addend = { .nfa_unit = "min" }
93*a3f161aeSRobert Mustacchi }, {
94*a3f161aeSRobert Mustacchi MICRON_F_SMART(wp_reason),
95*a3f161aeSRobert Mustacchi .nf_short = "wpr",
96*a3f161aeSRobert Mustacchi .nf_desc = "Write Protect Reason",
97*a3f161aeSRobert Mustacchi NVMEADM_F_BITS(micron_vul_ext_smart_wpr_bits)
98*a3f161aeSRobert Mustacchi }, {
99*a3f161aeSRobert Mustacchi MICRON_F_SMART(cap),
100*a3f161aeSRobert Mustacchi .nf_short = "cap",
101*a3f161aeSRobert Mustacchi .nf_desc = "Device Capacity",
102*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_BYTES
103*a3f161aeSRobert Mustacchi }, {
104*a3f161aeSRobert Mustacchi MICRON_F_SMART(erase_count),
105*a3f161aeSRobert Mustacchi .nf_short = "tec",
106*a3f161aeSRobert Mustacchi .nf_desc = "Total Erase Count",
107*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
108*a3f161aeSRobert Mustacchi }, {
109*a3f161aeSRobert Mustacchi MICRON_F_SMART(use_rate),
110*a3f161aeSRobert Mustacchi .nf_short = "use",
111*a3f161aeSRobert Mustacchi .nf_desc = "Lifetime Use Rate",
112*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
113*a3f161aeSRobert Mustacchi }, {
114*a3f161aeSRobert Mustacchi MICRON_F_SMART(erase_fail),
115*a3f161aeSRobert Mustacchi .nf_short = "efc",
116*a3f161aeSRobert Mustacchi .nf_desc = "Erase Fail Count",
117*a3f161aeSRobert Mustacchi .nf_rev = MICRON_GEN_74XX,
118*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
119*a3f161aeSRobert Mustacchi }, {
120*a3f161aeSRobert Mustacchi MICRON_F_SMART(uecc),
121*a3f161aeSRobert Mustacchi .nf_short = "uecc",
122*a3f161aeSRobert Mustacchi .nf_desc = "Reported Uncorrectable ECC Errors",
123*a3f161aeSRobert Mustacchi .nf_rev = MICRON_GEN_74XX,
124*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
125*a3f161aeSRobert Mustacchi }, {
126*a3f161aeSRobert Mustacchi MICRON_F_SMART(prog_fail),
127*a3f161aeSRobert Mustacchi .nf_short = "pfc",
128*a3f161aeSRobert Mustacchi .nf_desc = "Program Fail Count",
129*a3f161aeSRobert Mustacchi .nf_rev = MICRON_GEN_74XX,
130*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
131*a3f161aeSRobert Mustacchi }, {
132*a3f161aeSRobert Mustacchi MICRON_F_SMART(read_bytes),
133*a3f161aeSRobert Mustacchi .nf_short = "read",
134*a3f161aeSRobert Mustacchi .nf_desc = "Total Bytes Read",
135*a3f161aeSRobert Mustacchi .nf_rev = MICRON_GEN_74XX,
136*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_BYTES
137*a3f161aeSRobert Mustacchi }, {
138*a3f161aeSRobert Mustacchi MICRON_F_SMART(write_bytes),
139*a3f161aeSRobert Mustacchi .nf_short = "write",
140*a3f161aeSRobert Mustacchi .nf_desc = "Total Bytes Written",
141*a3f161aeSRobert Mustacchi .nf_rev = MICRON_GEN_74XX,
142*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_BYTES
143*a3f161aeSRobert Mustacchi }, {
144*a3f161aeSRobert Mustacchi MICRON_F_SMART(trans_size),
145*a3f161aeSRobert Mustacchi .nf_short = "tus",
146*a3f161aeSRobert Mustacchi .nf_desc = "Translation Unit Size",
147*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_BYTES
148*a3f161aeSRobert Mustacchi }, {
149*a3f161aeSRobert Mustacchi MICRON_F_SMART(bs_total),
150*a3f161aeSRobert Mustacchi .nf_short = "tbs",
151*a3f161aeSRobert Mustacchi .nf_desc = "Total Block Stripe Count for User Data",
152*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
153*a3f161aeSRobert Mustacchi }, {
154*a3f161aeSRobert Mustacchi MICRON_F_SMART(bs_free),
155*a3f161aeSRobert Mustacchi .nf_short = "fbs",
156*a3f161aeSRobert Mustacchi .nf_desc = "Free Block Stripe Count for User Data",
157*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
158*a3f161aeSRobert Mustacchi }, {
159*a3f161aeSRobert Mustacchi MICRON_F_SMART(bs_cap),
160*a3f161aeSRobert Mustacchi .nf_short = "bss",
161*a3f161aeSRobert Mustacchi .nf_desc = "Block Stripe Size",
162*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_BYTES
163*a3f161aeSRobert Mustacchi }, {
164*a3f161aeSRobert Mustacchi MICRON_F_SMART(user_erase_min),
165*a3f161aeSRobert Mustacchi .nf_short = "ubemin",
166*a3f161aeSRobert Mustacchi .nf_desc = "Minimum User Block Erase Count",
167*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
168*a3f161aeSRobert Mustacchi }, {
169*a3f161aeSRobert Mustacchi MICRON_F_SMART(user_erase_avg),
170*a3f161aeSRobert Mustacchi .nf_short = "ubeavg",
171*a3f161aeSRobert Mustacchi .nf_desc = "Average User Block Erase Count",
172*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
173*a3f161aeSRobert Mustacchi }, {
174*a3f161aeSRobert Mustacchi MICRON_F_SMART(user_erase_max),
175*a3f161aeSRobert Mustacchi .nf_short = "ubemax",
176*a3f161aeSRobert Mustacchi .nf_desc = "Maximum User Block Erase Count",
177*a3f161aeSRobert Mustacchi .nf_type = NVMEADM_FT_HEX
178*a3f161aeSRobert Mustacchi } };
179*a3f161aeSRobert Mustacchi
180*a3f161aeSRobert Mustacchi /*
181*a3f161aeSRobert Mustacchi * The 73xx series and 74xx series have some different entries in these log
182*a3f161aeSRobert Mustacchi * pages. There is no good way to determine this in the log. Instead we use a
183*a3f161aeSRobert Mustacchi * crude but reasonable heuristic. The 74xx series added a pair of counters for
184*a3f161aeSRobert Mustacchi * total bytes read and written. If these are zero, then we know we're on the
185*a3f161aeSRobert Mustacchi * 73xx assuming it's playing by its reserved rules. We've also seen some cases
186*a3f161aeSRobert Mustacchi * where the 73xx parts will return all 1s for the reserved fields, so we check
187*a3f161aeSRobert Mustacchi * that too.
188*a3f161aeSRobert Mustacchi */
189*a3f161aeSRobert Mustacchi static uint32_t
micron_vul_ext_smart_getvers(const void * data,size_t len)190*a3f161aeSRobert Mustacchi micron_vul_ext_smart_getvers(const void *data, size_t len)
191*a3f161aeSRobert Mustacchi {
192*a3f161aeSRobert Mustacchi const uint8_t zero[16] = { 0 };
193*a3f161aeSRobert Mustacchi uint8_t ones[16];
194*a3f161aeSRobert Mustacchi
195*a3f161aeSRobert Mustacchi if (len < sizeof (micron_vul_ext_smart_t)) {
196*a3f161aeSRobert Mustacchi errx(-1, "cannot parse revision information, found 0x%zx "
197*a3f161aeSRobert Mustacchi "bytes, need at least 0x%zx", len,
198*a3f161aeSRobert Mustacchi sizeof (micron_vul_ext_smart_t));
199*a3f161aeSRobert Mustacchi }
200*a3f161aeSRobert Mustacchi
201*a3f161aeSRobert Mustacchi (void) memset(ones, 0xff, sizeof (ones));
202*a3f161aeSRobert Mustacchi const micron_vul_ext_smart_t *log = data;
203*a3f161aeSRobert Mustacchi if (memcmp(zero, log->mes_read_bytes, sizeof (zero)) == 0 &&
204*a3f161aeSRobert Mustacchi memcmp(zero, log->mes_write_bytes, sizeof (zero)) == 0) {
205*a3f161aeSRobert Mustacchi return (MICRON_GEN_73XX);
206*a3f161aeSRobert Mustacchi }
207*a3f161aeSRobert Mustacchi
208*a3f161aeSRobert Mustacchi if (memcmp(ones, log->mes_read_bytes, sizeof (ones)) == 0 &&
209*a3f161aeSRobert Mustacchi memcmp(ones, log->mes_write_bytes, sizeof (ones)) == 0) {
210*a3f161aeSRobert Mustacchi return (MICRON_GEN_73XX);
211*a3f161aeSRobert Mustacchi }
212*a3f161aeSRobert Mustacchi
213*a3f161aeSRobert Mustacchi return (MICRON_GEN_74XX);
214*a3f161aeSRobert Mustacchi }
215*a3f161aeSRobert Mustacchi
216*a3f161aeSRobert Mustacchi const nvmeadm_log_field_info_t micron_vul_extsmart_field_info = {
217*a3f161aeSRobert Mustacchi .nlfi_log = "micron/extsmart",
218*a3f161aeSRobert Mustacchi .nlfi_fields = micron_vul_ext_smart_fields,
219*a3f161aeSRobert Mustacchi .nlfi_nfields = ARRAY_SIZE(micron_vul_ext_smart_fields),
220*a3f161aeSRobert Mustacchi .nlfi_min = sizeof (micron_vul_ext_smart_t),
221*a3f161aeSRobert Mustacchi .nlfi_getrev = micron_vul_ext_smart_getvers
222*a3f161aeSRobert Mustacchi };
223