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 * Many vendors have followed the same layout for their Extended SMART data 18 * which usually is a 1 byte id, 1 byte normalized value, and 6ish bytes of 19 * data. This file contains the field data for printing those and the 20 * corresponding logs. 21 */ 22 23 #include <sys/stddef.h> 24 #include <sys/sysmacros.h> 25 #include <sys/nvme/kioxia.h> 26 #include <sys/nvme/solidigm.h> 27 #include <sys/nvme/wdc.h> 28 29 #include "nvmeadm.h" 30 31 #define EXTSMART_F(f) \ 32 .nf_off = offsetof(solidigm_smart_ent_t, sse_##f), \ 33 .nf_len = sizeof (((solidigm_smart_ent_t *)NULL)->sse_##f) 34 35 static const nvmeadm_field_t extsmart_percent_fields[] = { { 36 EXTSMART_F(type), 37 .nf_short = "id", 38 .nf_desc = "Identifier", 39 .nf_type = NVMEADM_FT_HEX 40 }, { 41 EXTSMART_F(norm), 42 .nf_short = "norm", 43 .nf_desc = "Normalized", 44 .nf_type = NVMEADM_FT_PERCENT 45 }, { 46 EXTSMART_F(raw), 47 .nf_short = "raw", 48 .nf_desc = "Raw", 49 .nf_type = NVMEADM_FT_HEX 50 } }; 51 52 /* 53 * All fields are just printed in hex. 54 */ 55 static const nvmeadm_field_t extsmart_hex_fields[] = { { 56 EXTSMART_F(type), 57 .nf_short = "id", 58 .nf_desc = "Identifier", 59 .nf_type = NVMEADM_FT_HEX 60 }, { 61 EXTSMART_F(norm), 62 .nf_short = "norm", 63 .nf_desc = "Normalized", 64 .nf_type = NVMEADM_FT_HEX 65 }, { 66 EXTSMART_F(raw), 67 .nf_short = "raw", 68 .nf_desc = "Raw", 69 .nf_type = NVMEADM_FT_HEX 70 } }; 71 72 static const nvmeadm_field_t extsmart_wl_fields[] = { { 73 EXTSMART_F(type), 74 .nf_short = "id", 75 .nf_desc = "Identifier", 76 .nf_type = NVMEADM_FT_HEX 77 }, { 78 EXTSMART_F(norm), 79 .nf_short = "norm", 80 .nf_desc = "Normalized", 81 .nf_type = NVMEADM_FT_PERCENT 82 }, { 83 .nf_off = offsetof(solidigm_smart_ent_t, sse_raw[0]), 84 .nf_len = 2, 85 .nf_short = "min", 86 .nf_desc = "Minimum Erase Cycles", 87 .nf_type = NVMEADM_FT_HEX 88 }, { 89 .nf_off = offsetof(solidigm_smart_ent_t, sse_raw[2]), 90 .nf_len = 2, 91 .nf_short = "max", 92 .nf_desc = "Maximum Erase Cycles", 93 .nf_type = NVMEADM_FT_HEX 94 }, { 95 .nf_off = offsetof(solidigm_smart_ent_t, sse_raw[4]), 96 .nf_len = 2, 97 .nf_short = "avg", 98 .nf_desc = "Average Erase Cycles", 99 .nf_type = NVMEADM_FT_HEX 100 } }; 101 102 static const nvmeadm_field_t extsmart_32mio_fields[] = { { 103 EXTSMART_F(type), 104 .nf_short = "id", 105 .nf_desc = "Identifier", 106 .nf_type = NVMEADM_FT_HEX 107 }, { 108 EXTSMART_F(norm), 109 .nf_short = "norm", 110 .nf_desc = "Normalized", 111 .nf_type = NVMEADM_FT_HEX 112 }, { 113 EXTSMART_F(raw), 114 .nf_short = "raw", 115 .nf_desc = "Raw", 116 .nf_type = NVMEADM_FT_BYTES, 117 .nf_addend = { .nfa_shift = 25 } 118 } }; 119 120 static const nvmeadm_field_t extsmart_rawpct_fields[] = { { 121 EXTSMART_F(type), 122 .nf_short = "id", 123 .nf_desc = "Identifier", 124 .nf_type = NVMEADM_FT_HEX 125 }, { 126 EXTSMART_F(norm), 127 .nf_short = "norm", 128 .nf_desc = "Normalized", 129 .nf_type = NVMEADM_FT_HEX 130 }, { 131 EXTSMART_F(raw), 132 .nf_short = "raw", 133 .nf_desc = "Raw", 134 .nf_type = NVMEADM_FT_PERCENT, 135 } }; 136 137 static const nvmeadm_field_t extsmart_rawmin_fields[] = { { 138 EXTSMART_F(type), 139 .nf_short = "id", 140 .nf_desc = "Identifier", 141 .nf_type = NVMEADM_FT_HEX 142 }, { 143 EXTSMART_F(norm), 144 .nf_short = "norm", 145 .nf_desc = "Normalized", 146 .nf_type = NVMEADM_FT_HEX 147 }, { 148 EXTSMART_F(raw), 149 .nf_short = "raw", 150 .nf_desc = "Raw", 151 .nf_type = NVMEADM_FT_UNIT, 152 .nf_addend = { .nfa_unit = "min" } 153 } }; 154 155 static const nvmeadm_field_t extsmart_rawhour_fields[] = { { 156 EXTSMART_F(type), 157 .nf_short = "id", 158 .nf_desc = "Identifier", 159 .nf_type = NVMEADM_FT_HEX 160 }, { 161 EXTSMART_F(norm), 162 .nf_short = "norm", 163 .nf_desc = "Normalized", 164 .nf_type = NVMEADM_FT_HEX 165 }, { 166 EXTSMART_F(raw), 167 .nf_short = "raw", 168 .nf_desc = "Raw", 169 .nf_type = NVMEADM_FT_UNIT, 170 .nf_addend = { .nfa_unit = "hours" } 171 } }; 172 173 static const nvmeadm_field_t extsmart_therm_fields[] = { { 174 EXTSMART_F(type), 175 .nf_short = "id", 176 .nf_desc = "Identifier", 177 .nf_type = NVMEADM_FT_HEX 178 }, { 179 EXTSMART_F(norm), 180 .nf_short = "norm", 181 .nf_desc = "Normalized", 182 .nf_type = NVMEADM_FT_HEX 183 }, { 184 .nf_off = offsetof(solidigm_smart_ent_t, sse_raw[0]), 185 .nf_len = 1, 186 .nf_short = "status", 187 .nf_desc = "Throttle Status", 188 .nf_type = NVMEADM_FT_PERCENT 189 }, { 190 .nf_off = offsetof(solidigm_smart_ent_t, sse_raw[1]), 191 .nf_len = 4, 192 .nf_short = "count", 193 .nf_desc = "Throttle Count", 194 .nf_type = NVMEADM_FT_HEX 195 } }; 196 197 #define WDC_F_SMART(f) \ 198 .nf_off = offsetof(wdc_vul_sn65x_smart_t, sm_##f), \ 199 .nf_len = sizeof (((wdc_vul_sn65x_smart_t *)NULL)->sm_##f) 200 201 static const nvmeadm_field_t wdc_vul_cusmart_fields[] = { { 202 WDC_F_SMART(prog_fail), 203 .nf_short = "pfc", 204 .nf_desc = "Program Fail Count", 205 NVMEADM_F_FIELDS(extsmart_percent_fields) 206 }, { 207 WDC_F_SMART(erase_fail), 208 .nf_short = "efc", 209 .nf_desc = "Erase Fail Count", 210 NVMEADM_F_FIELDS(extsmart_percent_fields) 211 }, { 212 WDC_F_SMART(wear_level), 213 .nf_short = "wl", 214 .nf_desc = "Wear Leveling", 215 NVMEADM_F_FIELDS(extsmart_wl_fields) 216 }, { 217 WDC_F_SMART(e2e_edet), 218 .nf_short = "e2e", 219 .nf_desc = "End-to-End Error Detection Count", 220 NVMEADM_F_FIELDS(extsmart_hex_fields) 221 }, { 222 WDC_F_SMART(crc_err), 223 .nf_short = "crc", 224 .nf_desc = "CRC Error Count", 225 NVMEADM_F_FIELDS(extsmart_hex_fields) 226 }, { 227 WDC_F_SMART(timed_wear), 228 .nf_short = "twmw", 229 .nf_desc = "Timed Workload Media Wear", 230 NVMEADM_F_FIELDS(extsmart_hex_fields) 231 }, { 232 WDC_F_SMART(timed_read), 233 .nf_short = "twhr", 234 .nf_desc = "Timed Workload Host Reads", 235 NVMEADM_F_FIELDS(extsmart_rawpct_fields) 236 }, { 237 WDC_F_SMART(timed_timer), 238 .nf_short = "twt", 239 .nf_desc = "Timed Workload Timer", 240 NVMEADM_F_FIELDS(extsmart_rawmin_fields) 241 }, { 242 WDC_F_SMART(therm_throt), 243 .nf_short = "tthrot", 244 .nf_desc = "Thermal Throttle", 245 NVMEADM_F_FIELDS(extsmart_therm_fields) 246 }, { 247 WDC_F_SMART(retry_buf_over), 248 .nf_short = "rboc", 249 .nf_desc = "Retry Buffer Overflow Count", 250 NVMEADM_F_FIELDS(extsmart_hex_fields) 251 }, { 252 WDC_F_SMART(pll_lock_loss), 253 .nf_short = "pllll", 254 .nf_desc = "PLL Lock Loss Count", 255 NVMEADM_F_FIELDS(extsmart_hex_fields) 256 }, { 257 WDC_F_SMART(nand_write), 258 .nf_short = "nbw", 259 .nf_desc = "NAND Bytes Written", 260 NVMEADM_F_FIELDS(extsmart_32mio_fields) 261 }, { 262 WDC_F_SMART(host_write), 263 .nf_short = "hbw", 264 .nf_desc = "Host Bytes Written", 265 NVMEADM_F_FIELDS(extsmart_32mio_fields) 266 } }; 267 268 const nvmeadm_log_field_info_t wdc_vul_cusmart_field_info = { 269 .nlfi_log = "wdc/cusmart", 270 .nlfi_fields = wdc_vul_cusmart_fields, 271 .nlfi_nfields = ARRAY_SIZE(wdc_vul_cusmart_fields), 272 .nlfi_min = sizeof (wdc_vul_sn65x_smart_t) 273 }; 274 275 276 #define KIOXIA_F_SMART(f) \ 277 .nf_off = offsetof(kioxia_vul_cd8_smart_t, cds_##f), \ 278 .nf_len = sizeof (((kioxia_vul_cd8_smart_t *)NULL)->cds_##f) 279 280 static const nvmeadm_field_t kioxia_vul_extsmart_fields[] = { { 281 KIOXIA_F_SMART(prog_fail), 282 .nf_short = "pfc", 283 .nf_desc = "Program Fail Count", 284 NVMEADM_F_FIELDS(extsmart_percent_fields) 285 }, { 286 KIOXIA_F_SMART(erase_fail), 287 .nf_short = "efc", 288 .nf_desc = "Erase Fail Count", 289 NVMEADM_F_FIELDS(extsmart_percent_fields) 290 }, { 291 KIOXIA_F_SMART(wear_level), 292 .nf_short = "wl", 293 .nf_desc = "Wear Leveling", 294 NVMEADM_F_FIELDS(extsmart_wl_fields) 295 }, { 296 KIOXIA_F_SMART(e2e_det), 297 .nf_short = "e2e", 298 .nf_desc = "End-to-End Error Detection Count", 299 NVMEADM_F_FIELDS(extsmart_hex_fields) 300 }, { 301 KIOXIA_F_SMART(crc_error), 302 .nf_short = "crc", 303 .nf_desc = "CRC Error Count", 304 NVMEADM_F_FIELDS(extsmart_hex_fields) 305 }, { 306 KIOXIA_F_SMART(nand_write), 307 .nf_short = "nbw", 308 .nf_desc = "NAND Bytes Written", 309 NVMEADM_F_FIELDS(extsmart_32mio_fields) 310 }, { 311 KIOXIA_F_SMART(host_write), 312 .nf_short = "hbw", 313 .nf_desc = "Host Bytes Written", 314 NVMEADM_F_FIELDS(extsmart_32mio_fields) 315 }, { 316 /* 317 * The remaining fields (other than host bytes read) are duplicates from 318 * the normal NVMe Health log so we use the standard's name and short 319 * values. 320 */ 321 KIOXIA_F_SMART(crit_warn), 322 .nf_short = "cw", 323 .nf_desc = "Device Critical Warning", 324 NVMEADM_F_FIELDS(extsmart_hex_fields) 325 }, { 326 KIOXIA_F_SMART(host_read), 327 .nf_short = "hbr", 328 .nf_desc = "Host Bytes Read", 329 NVMEADM_F_FIELDS(extsmart_32mio_fields) 330 }, { 331 KIOXIA_F_SMART(comp_temp), 332 .nf_short = "ctemp", 333 .nf_desc = "Composite Temperature", 334 NVMEADM_F_FIELDS(extsmart_hex_fields) 335 }, { 336 KIOXIA_F_SMART(life_used), 337 .nf_short = "pused", 338 .nf_desc = "Percentage Used", 339 NVMEADM_F_FIELDS(extsmart_percent_fields) 340 }, { 341 KIOXIA_F_SMART(power_cycles), 342 .nf_short = "pwrc", 343 .nf_desc = "Power Cycles", 344 NVMEADM_F_FIELDS(extsmart_hex_fields) 345 }, { 346 KIOXIA_F_SMART(power_hours), 347 .nf_short = "poh", 348 .nf_desc = "Power On Hours", 349 NVMEADM_F_FIELDS(extsmart_rawhour_fields) 350 }, { 351 KIOXIA_F_SMART(unsafe_shut), 352 .nf_short = "upl", 353 .nf_desc = "Unexpected Power Losses", 354 NVMEADM_F_FIELDS(extsmart_hex_fields) 355 } }; 356 357 const nvmeadm_log_field_info_t kioxia_vul_extsmart_field_info = { 358 .nlfi_log = "kioxia/extsmart", 359 .nlfi_fields = kioxia_vul_extsmart_fields, 360 .nlfi_nfields = ARRAY_SIZE(kioxia_vul_extsmart_fields), 361 .nlfi_min = sizeof (kioxia_vul_cd8_smart_t) 362 }; 363