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 * This is the common file or parsing out SPD data of different generations. Our
18 * general goal is to create a single nvlist_t that has a few different sections
19 * present in it:
20 *
21 * o Metadata (e.g. DRAM type, Revision, overlay type, etc.)
22 * o Manufacturing Information
23 * o Common parameters: these are ultimately specific to a DDR type.
24 * o Overlay parameters: these are specific to both the DDR type and the
25 * module type.
26 *
27 * We try to only fail top-level parsing if we really can't understand anything
28 * or don't have enough information. We assume that we'll get relatively
29 * complete data. Errors are listed as keys for a given entry and will be
30 * skipped otherwise. For an overview of the actual fields and structures, see
31 * libjedec.h.
32 *
33 * Currently we support all of DDR4, DDD5, and LPDDR5/x based SPD information
34 * with the exception of some NVDIMM properties.
35 */
36
37 #include <string.h>
38 #include <sys/debug.h>
39 #include <sys/sysmacros.h>
40 #include <ctype.h>
41 #include <stdarg.h>
42 #include <errno.h>
43 #include <stdbool.h>
44
45 #include "libjedec_spd.h"
46
47 void
spd_nvl_err(spd_info_t * si,const char * key,spd_error_kind_t err,const char * fmt,...)48 spd_nvl_err(spd_info_t *si, const char *key, spd_error_kind_t err,
49 const char *fmt, ...)
50 {
51 int ret;
52 nvlist_t *nvl;
53 char msg[1024];
54 va_list ap;
55
56 if (si->si_error != LIBJEDEC_SPD_OK)
57 return;
58
59 ret = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
60 if (ret != 0) {
61 VERIFY3S(ret, ==, ENOMEM);
62 si->si_error = LIBJEDEC_SPD_NOMEM;
63 return;
64 }
65
66 ret = nvlist_add_uint32(nvl, SPD_KEY_ERRS_CODE, err);
67 if (ret != 0) {
68 VERIFY3S(ret, ==, ENOMEM);
69 nvlist_free(nvl);
70 si->si_error = LIBJEDEC_SPD_NOMEM;
71 return;
72 }
73
74 /*
75 * We cast this snprintf to void so we can try to get someone something
76 * at least in the face of it somehow being too large.
77 */
78 va_start(ap, fmt);
79 (void) vsnprintf(msg, sizeof (msg), fmt, ap);
80 va_end(ap);
81
82 ret = nvlist_add_string(nvl, SPD_KEY_ERRS_MSG, msg);
83 if (ret != 0) {
84 VERIFY3S(ret, ==, ENOMEM);
85 nvlist_free(nvl);
86 si->si_error = LIBJEDEC_SPD_NOMEM;
87 return;
88 }
89
90 ret = nvlist_add_nvlist(si->si_errs, key, nvl);
91 if (ret != 0) {
92 VERIFY3S(ret, ==, ENOMEM);
93 nvlist_free(nvl);
94 si->si_error = LIBJEDEC_SPD_NOMEM;
95 return;
96 }
97
98 nvlist_free(nvl);
99 }
100
101 void
spd_nvl_insert_str(spd_info_t * si,const char * key,const char * data)102 spd_nvl_insert_str(spd_info_t *si, const char *key, const char *data)
103 {
104 int ret;
105
106 if (si->si_error != LIBJEDEC_SPD_OK)
107 return;
108
109 ret = nvlist_add_string(si->si_nvl, key, data);
110 if (ret != 0) {
111 VERIFY3S(ret, ==, ENOMEM);
112 si->si_error = LIBJEDEC_SPD_NOMEM;
113 return;
114 }
115 }
116
117 void
spd_nvl_insert_u32(spd_info_t * si,const char * key,uint32_t data)118 spd_nvl_insert_u32(spd_info_t *si, const char *key, uint32_t data)
119 {
120 int ret;
121
122 if (si->si_error != LIBJEDEC_SPD_OK)
123 return;
124
125 ret = nvlist_add_uint32(si->si_nvl, key, data);
126 if (ret != 0) {
127 VERIFY3S(ret, ==, ENOMEM);
128 si->si_error = LIBJEDEC_SPD_NOMEM;
129 return;
130 }
131 }
132
133 void
spd_nvl_insert_u64(spd_info_t * si,const char * key,uint64_t data)134 spd_nvl_insert_u64(spd_info_t *si, const char *key, uint64_t data)
135 {
136 int ret;
137
138 if (si->si_error != LIBJEDEC_SPD_OK)
139 return;
140
141 ret = nvlist_add_uint64(si->si_nvl, key, data);
142 if (ret != 0) {
143 VERIFY3S(ret, ==, ENOMEM);
144 si->si_error = LIBJEDEC_SPD_NOMEM;
145 return;
146 }
147 }
148
149 void
spd_nvl_insert_u8_array(spd_info_t * si,const char * key,uint8_t * data,uint_t nent)150 spd_nvl_insert_u8_array(spd_info_t *si, const char *key,
151 uint8_t *data, uint_t nent)
152 {
153 int ret;
154
155 if (si->si_error != LIBJEDEC_SPD_OK)
156 return;
157
158 ret = nvlist_add_uint8_array(si->si_nvl, key, data, nent);
159 if (ret != 0) {
160 VERIFY3S(ret, ==, ENOMEM);
161 si->si_error = LIBJEDEC_SPD_NOMEM;
162 return;
163 }
164 }
165
166 void
spd_nvl_insert_u32_array(spd_info_t * si,const char * key,uint32_t * data,uint_t nent)167 spd_nvl_insert_u32_array(spd_info_t *si, const char *key,
168 uint32_t *data, uint_t nent)
169 {
170 int ret;
171
172 if (si->si_error != LIBJEDEC_SPD_OK)
173 return;
174
175 ret = nvlist_add_uint32_array(si->si_nvl, key, data, nent);
176 if (ret != 0) {
177 VERIFY3S(ret, ==, ENOMEM);
178 si->si_error = LIBJEDEC_SPD_NOMEM;
179 return;
180 }
181 }
182
183 void
spd_nvl_insert_u64_array(spd_info_t * si,const char * key,uint64_t * data,uint_t nent)184 spd_nvl_insert_u64_array(spd_info_t *si, const char *key,
185 uint64_t *data, uint_t nent)
186 {
187 int ret;
188
189 if (si->si_error != LIBJEDEC_SPD_OK)
190 return;
191
192 ret = nvlist_add_uint64_array(si->si_nvl, key, data, nent);
193 if (ret != 0) {
194 VERIFY3S(ret, ==, ENOMEM);
195 si->si_error = LIBJEDEC_SPD_NOMEM;
196 return;
197 }
198 }
199
200 void
spd_nvl_insert_boolean_array(spd_info_t * si,const char * key,boolean_t * data,uint_t nent)201 spd_nvl_insert_boolean_array(spd_info_t *si, const char *key,
202 boolean_t *data, uint_t nent)
203 {
204 int ret;
205
206 if (si->si_error != LIBJEDEC_SPD_OK)
207 return;
208
209 ret = nvlist_add_boolean_array(si->si_nvl, key, data, nent);
210 if (ret != 0) {
211 VERIFY3S(ret, ==, ENOMEM);
212 si->si_error = LIBJEDEC_SPD_NOMEM;
213 return;
214 }
215 }
216
217 void
spd_nvl_insert_key(spd_info_t * si,const char * key)218 spd_nvl_insert_key(spd_info_t *si, const char *key)
219 {
220 int ret;
221
222 if (si->si_error != LIBJEDEC_SPD_OK)
223 return;
224
225 ret = nvlist_add_boolean(si->si_nvl, key);
226 if (ret != 0) {
227 VERIFY3S(ret, ==, ENOMEM);
228 si->si_error = LIBJEDEC_SPD_NOMEM;
229 return;
230 }
231 }
232
233 void
spd_insert_map(spd_info_t * si,const char * key,uint8_t spd_val,const spd_value_map_t * maps,size_t nmaps)234 spd_insert_map(spd_info_t *si, const char *key, uint8_t spd_val,
235 const spd_value_map_t *maps, size_t nmaps)
236 {
237 for (size_t i = 0; i < nmaps; i++) {
238 if (maps[i].svm_spd != spd_val)
239 continue;
240 if (maps[i].svm_skip)
241 return;
242
243 spd_nvl_insert_u32(si, key, maps[i].svm_use);
244 return;
245 }
246
247 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered unknown "
248 "value: 0x%x", spd_val);
249 }
250
251 void
spd_insert_map64(spd_info_t * si,const char * key,uint8_t spd_val,const spd_value_map64_t * maps,size_t nmaps)252 spd_insert_map64(spd_info_t *si, const char *key, uint8_t spd_val,
253 const spd_value_map64_t *maps, size_t nmaps)
254 {
255 for (size_t i = 0; i < nmaps; i++) {
256 if (maps[i].svm_spd != spd_val)
257 continue;
258 if (maps[i].svm_skip)
259 return;
260
261 spd_nvl_insert_u64(si, key, maps[i].svm_use);
262 return;
263 }
264
265 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered unknown "
266 "value: 0x%x", spd_val);
267 }
268
269 void
spd_insert_str_map(spd_info_t * si,const char * key,uint8_t spd_val,const spd_str_map_t * maps,size_t nmaps)270 spd_insert_str_map(spd_info_t *si, const char *key, uint8_t spd_val,
271 const spd_str_map_t *maps, size_t nmaps)
272 {
273 for (size_t i = 0; i < nmaps; i++) {
274 if (maps[i].ssm_spd != spd_val)
275 continue;
276 if (maps[i].ssm_skip)
277 return;
278
279 spd_nvl_insert_str(si, key, maps[i].ssm_str);
280 return;
281 }
282
283 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered unknown "
284 "value: 0x%x", spd_val);
285 }
286
287 /*
288 * Map an array in its entirety to a corresponding set of values. If any one
289 * value cannot be translated, then we fail the whole item.
290 */
291 void
spd_insert_map_array(spd_info_t * si,const char * key,const uint8_t * raw,size_t nraw,const spd_value_map_t * maps,size_t nmaps)292 spd_insert_map_array(spd_info_t *si, const char *key, const uint8_t *raw,
293 size_t nraw, const spd_value_map_t *maps, size_t nmaps)
294 {
295 uint32_t *trans;
296
297 trans = calloc(nraw, sizeof (uint32_t));
298 if (trans == NULL) {
299 si->si_error = LIBJEDEC_SPD_NOMEM;
300 return;
301 }
302
303 for (size_t i = 0; i < nraw; i++) {
304 bool found = false;
305 for (size_t map = 0; map < nmaps; map++) {
306 if (maps[map].svm_spd != raw[i])
307 continue;
308 ASSERT3U(maps[map].svm_skip, ==, false);
309 found = true;
310 trans[i] = maps[map].svm_use;
311 break;
312 }
313
314 if (!found) {
315 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered "
316 "unknown array value: [%zu]=0x%x", i, raw[i]);
317 goto done;
318 }
319 }
320
321 spd_nvl_insert_u32_array(si, key, trans, nraw);
322 done:
323 free(trans);
324 }
325
326 /*
327 * We've been given a value which attempts to fit within a range. This range has
328 * an optional upper and lower bound. The value can be transformed in one of
329 * three ways which are honored in the following order:
330 *
331 * 1) If there is a multiple, we apply that to the raw value first.
332 * 2) There can be a base value which we then add to any adjusted value.
333 * 3) The final value can be treated as an exponent resulting in a bit-shift.
334 *
335 * After this is done we can check against the minimum and maximum values. A
336 * specified min or max of zero is ignored.
337 */
338 void
spd_insert_range(spd_info_t * si,const char * key,uint8_t raw_val,const spd_value_range_t * range)339 spd_insert_range(spd_info_t *si, const char *key, uint8_t raw_val,
340 const spd_value_range_t *range)
341 {
342 uint32_t min = 0, max = UINT32_MAX;
343 uint32_t act = raw_val;
344
345 if (range->svr_mult != 0) {
346 act *= range->svr_mult;
347 }
348
349 act += range->svr_base;
350
351 if (range->svr_exp) {
352 act = 1 << act;
353 }
354
355 if (range->svr_max != 0) {
356 max = range->svr_max;
357 }
358
359 if (range->svr_min != 0) {
360 min = range->svr_min;
361 } else if (range->svr_base != 0) {
362 min = range->svr_base;
363 }
364
365 if (act > max || act < min) {
366 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "found value "
367 "0x%x (raw 0x%x) outside range [0x%x, 0x%x]", act, raw_val,
368 min, max);
369 } else {
370 spd_nvl_insert_u32(si, key, act);
371 }
372 }
373
374 /*
375 * Either insert the given flag for a key or OR it in if it already exists.
376 */
377 void
spd_upsert_flag(spd_info_t * si,const char * key,uint32_t flag)378 spd_upsert_flag(spd_info_t *si, const char *key, uint32_t flag)
379 {
380 int ret;
381 uint32_t val;
382
383 ret = nvlist_lookup_uint32(si->si_nvl, key, &val);
384 if (ret != 0) {
385 VERIFY3S(ret, ==, ENOENT);
386 spd_nvl_insert_u32(si, key, flag);
387 return;
388 }
389
390 VERIFY0(val & flag);
391 val |= flag;
392 spd_nvl_insert_u32(si, key, val);
393 }
394
395 void
spd_parse_rev(spd_info_t * si,uint32_t off,uint32_t len,const char * key)396 spd_parse_rev(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
397 {
398 const uint8_t data = si->si_data[off];
399 const uint8_t enc = SPD_DDR4_SPD_REV_ENC(data);
400 const uint8_t add = SPD_DDR4_SPD_REV_ADD(data);
401
402 spd_nvl_insert_u32(si, SPD_KEY_REV_ENC, enc);
403 spd_nvl_insert_u32(si, SPD_KEY_REV_ADD, add);
404 }
405
406 void
spd_parse_jedec_id(spd_info_t * si,uint32_t off,uint32_t len,const char * key)407 spd_parse_jedec_id(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
408 {
409 uint32_t id[2];
410
411 VERIFY3U(len, ==, 2);
412 id[0] = SPD_MFG_ID0_CONT(si->si_data[off]);
413 id[1] = si->si_data[off + 1];
414
415 spd_nvl_insert_u32_array(si, key, id, ARRAY_SIZE(id));
416 }
417
418 void
spd_parse_jedec_id_str(spd_info_t * si,uint32_t off,uint32_t len,const char * key)419 spd_parse_jedec_id_str(spd_info_t *si, uint32_t off, uint32_t len,
420 const char *key)
421 {
422 uint8_t cont = SPD_MFG_ID0_CONT(si->si_data[off]);
423 const char *str;
424
425 VERIFY3U(len, ==, 2);
426 str = libjedec_vendor_string(cont, si->si_data[off + 1]);
427 if (str != NULL) {
428 spd_nvl_insert_str(si, key, str);
429 } else {
430 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "no matching "
431 "libjedec vendor string for 0x%x,0x%x", cont,
432 si->si_data[off + 1]);
433 }
434 }
435
436 /*
437 * Parse a string that is at most len bytes wide and is padded with spaces. If
438 * the string contains an unprintable, then we will not pull this off and set an
439 * error for the string's key. 128 bytes should be larger than any ascii string
440 * that we encounter as that is the size of most regions in SPD data.
441 */
442 void
spd_parse_string(spd_info_t * si,uint32_t off,uint32_t len,const char * key)443 spd_parse_string(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
444 {
445 uint32_t nbytes = len;
446 char buf[128];
447
448 /*
449 * First trim trailing spaces then go back and see that everything is
450 * printable.
451 */
452 VERIFY3U(sizeof (buf), >, len);
453 while (nbytes > 0) {
454 if (si->si_data[off + nbytes - 1] != ' ')
455 break;
456 nbytes--;
457 }
458
459 for (uint32_t i = 0; i < nbytes; i++) {
460 if (isascii(si->si_data[off + i]) == 0 ||
461 isprint(si->si_data[off + i]) == 0) {
462 spd_nvl_err(si, key, SPD_ERROR_UNPRINT,
463 "byte %u for key %s (off: 0x%x, val: 0x%x) is not "
464 "printable", i, key, off + 1,
465 si->si_data[off + i]);
466 return;
467 }
468 }
469
470 if (nbytes == 0) {
471 spd_nvl_err(si, key, SPD_ERROR_NO_DATA, "key %s has "
472 "no valid bytes in the string", key);
473 return;
474 }
475
476 (void) memcpy(buf, &si->si_data[off], nbytes);
477 buf[nbytes] = '\0';
478 spd_nvl_insert_str(si, key, buf);
479 }
480
481 /*
482 * Turn an array of bytes into a hex string. We need to allocate up to two bytes
483 * per length that we have. We always zero pad such strings. We statically size
484 * our buffer because the largest such string we have right now is a 4-byte
485 * serial number. With the 128 byte buffer below, we could deal with a length up
486 * to 63 (far beyond what we expect to ever see).
487 */
488 void
spd_parse_hex_string(spd_info_t * si,uint32_t off,uint32_t len,const char * key)489 spd_parse_hex_string(spd_info_t *si, uint32_t off, uint32_t len,
490 const char *key)
491 {
492 char buf[128];
493 size_t nwrite = 0;
494
495 VERIFY3U(sizeof (buf), >=, len * 2 + 1);
496
497 for (uint32_t i = 0; i < len; i++) {
498 int ret = snprintf(buf + nwrite, sizeof (buf) - nwrite,
499 "%02X", si->si_data[off + i]);
500 if (ret < 0) {
501 spd_nvl_err(si, key, SPD_ERROR_INTERNAL,
502 "snprintf failed unexpectedly for key %s: %s",
503 key, strerror(errno));
504 return;
505 }
506
507 VERIFY3U(ret, ==, 2);
508 nwrite += ret;
509 }
510
511 spd_nvl_insert_str(si, key, buf);
512 }
513
514 /*
515 * Several SPD keys are explicit BCD major and minor versions in a given nibble.
516 * This is most common in DDR5, but otherwise one should probably use
517 * spd_parse_hex_string().
518 */
519 void
spd_parse_hex_vers(spd_info_t * si,uint32_t off,uint32_t len,const char * key)520 spd_parse_hex_vers(spd_info_t *si, uint32_t off, uint32_t len,
521 const char *key)
522 {
523 const uint8_t data = si->si_data[off];
524 const uint8_t maj = bitx8(data, 7, 4);
525 const uint8_t min = bitx8(data, 3, 0);
526 char buf[128];
527
528 VERIFY3U(len, ==, 1);
529
530 int ret = snprintf(buf, sizeof (buf), "%X.%X", maj, min);
531 if (ret < 0) {
532 spd_nvl_err(si, key, SPD_ERROR_INTERNAL,
533 "snprintf failed unexpectedly for key %s: %s",
534 key, strerror(errno));
535 return;
536 }
537
538 spd_nvl_insert_str(si, key, buf);
539 }
540
541 void
spd_parse_raw_u8(spd_info_t * si,uint32_t off,uint32_t len,const char * key)542 spd_parse_raw_u8(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
543 {
544 VERIFY3U(len, ==, 1);
545 spd_nvl_insert_u32(si, key, si->si_data[off]);
546 }
547
548 void
spd_parse_u8_array(spd_info_t * si,uint32_t off,uint32_t len,const char * key)549 spd_parse_u8_array(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
550 {
551 uint8_t *data = (uint8_t *)si->si_data + off;
552
553 spd_nvl_insert_u8_array(si, key, data, len);
554 }
555
556 void
spd_parse_dram_step(spd_info_t * si,uint32_t off,uint32_t len,const char * key)557 spd_parse_dram_step(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
558 {
559 VERIFY3U(len, ==, 1);
560
561 if (si->si_data[off] == SPD_DRAM_STEP_NOINFO)
562 return;
563
564 spd_parse_hex_string(si, off, len, key);
565 }
566
567 /*
568 * Height and thickness have the same meaning across DDR3-DDR5.
569 */
570 static const spd_value_range_t spd_height_range = {
571 .svr_base = SPD_DDR5_COM_HEIGHT_BASE
572 };
573
574 static const spd_value_range_t spd_thick_range = {
575 .svr_base = SPD_DDR5_COM_THICK_BASE
576 };
577
578 void
spd_parse_height(spd_info_t * si,uint32_t off,uint32_t len,const char * key)579 spd_parse_height(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
580 {
581 const uint8_t data = si->si_data[off];
582 const uint8_t height = SPD_DDR5_COM_HEIGHT_MM(data);
583 spd_insert_range(si, key, height, &spd_height_range);
584 }
585
586 void
spd_parse_thickness(spd_info_t * si,uint32_t off,uint32_t len,const char * key)587 spd_parse_thickness(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
588 {
589 const uint8_t data = si->si_data[off];
590 const uint8_t front = SPD_DDR5_COM_THICK_FRONT(data);
591 const uint8_t back = SPD_DDR5_COM_THICK_BACK(data);
592
593 spd_insert_range(si, SPD_KEY_MOD_FRONT_THICK, front, &spd_thick_range);
594 spd_insert_range(si, SPD_KEY_MOD_BACK_THICK, back, &spd_thick_range);
595 }
596
597 /*
598 * Common timestamp calculation logic for DDR3-4, LPDDR3-5 that assumes 1 ps FT
599 * and 125ps MTB. The MTB may either be an 8-bit, 12-bit, or 16-bit value. The
600 * FTB value is actually a signed two's complement value that we use to adjust
601 * things. We need to check for two illegal values:
602 *
603 * 1. That the value as a whole after adjustment is non-zero.
604 * 2. That the fine adjustment does not cause us to underflow (i.e. unit values
605 * for the MTB of 1 and the FTB of -126).
606 */
607 void
spd_parse_ddr_time(spd_info_t * si,const char * key,uint8_t upper_mtb,uint8_t mtb,uint8_t ftb)608 spd_parse_ddr_time(spd_info_t *si, const char *key, uint8_t upper_mtb,
609 uint8_t mtb, uint8_t ftb)
610 {
611 uint64_t ps = ((upper_mtb << 8) | mtb) * SPD_DDR4_MTB_PS;
612 int8_t adj = (int8_t)ftb * SPD_DDR4_FTB_PS;
613
614 if (ps == 125 && adj <= -125) {
615 spd_nvl_err(si, key, SPD_ERROR_BAD_DATA,
616 "MTB (%" PRIu64 "ps) and FTB (%dps) would cause underflow",
617 ps, adj);
618 return;
619 }
620
621 ps += adj;
622 if (ps == 0) {
623 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
624 "encountered unexpected zero time value");
625 return;
626 }
627 spd_nvl_insert_u64(si, key, ps);
628 }
629
630 /*
631 * Combine two values into a picosecond value that is split between the MTB and
632 * FTB. The MTB and FTB are split amongst a large number of bytes and are not
633 * contiguous. The MTB is at data[off], and the FTB is at data[off + len - 1].
634 *
635 * This is shared by LPDDR3-5 which all use the same time base parameters. DDR3
636 * also uses it for a number of items based on our assumptions.
637 */
638 void
spd_parse_mtb_ftb_time_pair(spd_info_t * si,uint32_t off,uint32_t len,const char * key)639 spd_parse_mtb_ftb_time_pair(spd_info_t *si, uint32_t off, uint32_t len,
640 const char *key)
641 {
642 const uint8_t mtb = si->si_data[off];
643 const uint8_t ftb = si->si_data[off + len - 1];
644
645 return (spd_parse_ddr_time(si, key, 0, mtb, ftb));
646 }
647
648 /*
649 * Parse a pair of values where the MTB is split across two uint8_t's. The LSB
650 * is in off and the MSB is in off+1.
651 */
652 void
spd_parse_mtb_pair(spd_info_t * si,uint32_t off,uint32_t len,const char * key)653 spd_parse_mtb_pair(spd_info_t *si, uint32_t off, uint32_t len,
654 const char *key)
655 {
656 ASSERT3U(len, ==, 2);
657 return (spd_parse_ddr_time(si, key, si->si_data[off + 1],
658 si->si_data[off], 0));
659 }
660
661 static const spd_str_map_t spd_ddr_design_map0[32] = {
662 { 0, "A", false },
663 { 1, "B", false },
664 { 2, "C", false },
665 { 3, "D", false },
666 { 4, "E", false },
667 { 5, "F", false },
668 { 6, "G", false },
669 { 7, "H", false },
670 { 8, "J", false },
671 { 9, "K", false },
672 { 10, "L", false },
673 { 11, "M", false },
674 { 12, "N", false },
675 { 13, "P", false },
676 { 14, "R", false },
677 { 15, "T", false },
678 { 16, "U", false },
679 { 17, "V", false },
680 { 18, "W", false },
681 { 19, "Y", false },
682 { 20, "AA", false },
683 { 21, "AB", false },
684 { 22, "AC", false },
685 { 23, "AD", false },
686 { 24, "AE", false },
687 { 25, "AF", false },
688 { 26, "AG", false },
689 { 27, "AH", false },
690 { 28, "AJ", false },
691 { 29, "AK", false },
692 { 30, "AL", false },
693 { 31, "ZZ", false }
694 };
695
696 static const spd_str_map_t spd_ddr_design_map1[32] = {
697 { 0, "AM", false },
698 { 1, "AN", false },
699 { 2, "AP", false },
700 { 3, "AR", false },
701 { 4, "AT", false },
702 { 5, "AU", false },
703 { 6, "AV", false },
704 { 7, "AW", false },
705 { 8, "AY", false },
706 { 9, "BA", false },
707 { 10, "BB", false },
708 { 11, "BC", false },
709 { 12, "BD", false },
710 { 13, "BE", false },
711 { 14, "BF", false },
712 { 15, "BG", false },
713 { 16, "BH", false },
714 { 17, "BJ", false },
715 { 18, "BK", false },
716 { 19, "BL", false },
717 { 20, "BM", false },
718 { 21, "BN", false },
719 { 22, "BP", false },
720 { 23, "BR", false },
721 { 24, "BT", false },
722 { 25, "BU", false },
723 { 26, "BV", false },
724 { 27, "BW", false },
725 { 28, "BY", false },
726 { 29, "CA", false },
727 { 30, "CB", false },
728 { 31, "ZZ", false }
729 };
730
731 /*
732 * In DDR3/4 and LPDDR3-5 the design information contains both a reference raw
733 * card and a revision of the card. The card revision is split between two
734 * bytes, the design and the height field. This is common logic that'll check
735 * both. We use the DDR4 constants for the fields, but they are the same across
736 * all versions.
737 */
738 void
spd_parse_design(spd_info_t * si,uint32_t design,uint32_t height)739 spd_parse_design(spd_info_t *si, uint32_t design, uint32_t height)
740 {
741 const uint8_t data = si->si_data[design];
742 const uint8_t rev = SPD_DDR4_RDIMM_REF_REV(data);
743 const uint8_t card = SPD_DDR4_RDIMM_REF_CARD(data);
744
745 if (SPD_DDR4_RDIMM_REF_EXT(data) != 0) {
746 spd_insert_str_map(si, SPD_KEY_MOD_REF_DESIGN, card,
747 spd_ddr_design_map1, ARRAY_SIZE(spd_ddr_design_map1));
748 } else {
749 spd_insert_str_map(si, SPD_KEY_MOD_REF_DESIGN, card,
750 spd_ddr_design_map0, ARRAY_SIZE(spd_ddr_design_map0));
751 }
752
753 /*
754 * The design rev is split between here and the height field. If we
755 * have the value of three, then we must also add in the height's value
756 * to this.
757 */
758 if (rev == SPD_DDR4_RDIMM_REV_USE_HEIGHT) {
759 const uint8_t hdata = si->si_data[height];
760 const uint8_t hrev = SPD_DDR4_RDIMM_HEIGHT_REV(hdata);
761 spd_nvl_insert_u32(si, SPD_KEY_MOD_DESIGN_REV, rev + hrev);
762 } else {
763 spd_nvl_insert_u32(si, SPD_KEY_MOD_DESIGN_REV, rev);
764 }
765 }
766
767 /*
768 * Calculate the DRAM CRC16. The crc calculation covers [ off, off + len ). The
769 * expected CRC is in expect. The JEDEC specs describe the algorithm (e.g. 21-C
770 * Annex L, 8.1.53).
771 */
772 void
spd_parse_crc_expect(spd_info_t * si,uint32_t off,uint32_t len,uint16_t expect,const char * key)773 spd_parse_crc_expect(spd_info_t *si, uint32_t off, uint32_t len,
774 uint16_t expect, const char *key)
775 {
776 uint32_t crc = 0;
777
778 for (uint32_t i = 0; i < len; i++) {
779 crc = crc ^ (uint32_t)si->si_data[off + i] << 8;
780 for (uint32_t c = 0; c < 8; c++) {
781 if (crc & 0x8000) {
782 crc = crc << 1 ^ 0x1021;
783 } else {
784 crc = crc << 1;
785 }
786 }
787 }
788
789 crc &= 0xffff;
790 if (crc == expect) {
791 spd_nvl_insert_u32(si, key, crc);
792 } else {
793 spd_nvl_err(si, key, SPD_ERROR_BAD_DATA, "crc mismatch: "
794 "expected 0x%x, found 0x%x", expect, crc);
795 }
796 }
797
798 /*
799 * Calculate the DRAM CRC16. The crc ranges over [ off, off + len - 2). The crc
800 * lsb is at off + len - 2, and the msb is at off + len - 1.
801 */
802 void
spd_parse_crc(spd_info_t * si,uint32_t off,uint32_t len,const char * key)803 spd_parse_crc(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
804 {
805 const uint16_t expect = si->si_data[off + len - 2] |
806 (si->si_data[off + len - 1] << 8);
807
808 spd_parse_crc_expect(si, off, len - 2, expect, key);
809 }
810
811 void
spd_parse(spd_info_t * sip,const spd_parse_t * parse,size_t nparse)812 spd_parse(spd_info_t *sip, const spd_parse_t *parse, size_t nparse)
813 {
814 for (size_t i = 0; i < nparse; i++) {
815 uint32_t len;
816
817 if (parse[i].sp_len != 0) {
818 len = parse[i].sp_len;
819 } else {
820 len = 1;
821 }
822
823 if (len + parse[i].sp_off >= sip->si_nbytes) {
824 if ((sip->si_flags & SPD_INFO_F_INCOMPLETE) != 0)
825 continue;
826 sip->si_flags |= SPD_INFO_F_INCOMPLETE;
827 ASSERT3U(parse[i].sp_off, <, UINT32_MAX);
828 spd_nvl_insert_u32(sip, SPD_KEY_INCOMPLETE,
829 (uint32_t)parse[i].sp_off);
830 } else {
831 parse[i].sp_parse(sip, parse[i].sp_off, len,
832 parse[i].sp_key);
833 }
834
835 if (sip->si_error != LIBJEDEC_SPD_OK) {
836 return;
837 }
838 }
839 }
840
841 static spd_error_t
spd_init_info(spd_info_t * sip)842 spd_init_info(spd_info_t *sip)
843 {
844 int ret;
845
846 if ((ret = nvlist_alloc(&sip->si_nvl, NV_UNIQUE_NAME, 0)) != 0) {
847 VERIFY3S(ret, ==, ENOMEM);
848 return (LIBJEDEC_SPD_NOMEM);
849 }
850
851 if ((ret = nvlist_alloc(&sip->si_errs, NV_UNIQUE_NAME, 0)) != 0) {
852 VERIFY3S(ret, ==, ENOMEM);
853 return (LIBJEDEC_SPD_NOMEM);
854 }
855
856 return (LIBJEDEC_SPD_OK);
857 }
858
859 static void
spd_fini_info(spd_info_t * sip)860 spd_fini_info(spd_info_t *sip)
861 {
862 nvlist_free(sip->si_nvl);
863 nvlist_free(sip->si_errs);
864 }
865
866 nvlist_t *
libjedec_spd(const uint8_t * buf,size_t nbytes,spd_error_t * err)867 libjedec_spd(const uint8_t *buf, size_t nbytes, spd_error_t *err)
868 {
869 int ret;
870 spd_error_t set;
871 spd_info_t si;
872
873 if (err == NULL) {
874 err = &set;
875 }
876
877 (void) memset(&si, 0, sizeof (spd_info_t));
878 si.si_data = buf;
879 si.si_nbytes = nbytes;
880
881 *err = spd_init_info(&si);
882 if (si.si_error != LIBJEDEC_SPD_OK) {
883 goto fatal;
884 }
885
886 /*
887 * To begin parsing the SPD, we must first look at byte 2, which appears
888 * to almost always be the Key Byte / Host Bus Command Protocol Type
889 * which then tells us how the rest of the data is formatted.
890 */
891 if (si.si_nbytes <= SPD_DRAM_TYPE) {
892 *err = LIBJEDEC_SPD_TOOSHORT;
893 goto fatal;
894 }
895
896 si.si_error = LIBJEDEC_SPD_OK;
897 si.si_dram = buf[SPD_DRAM_TYPE];
898 switch (si.si_dram) {
899 case SPD_DT_DDR3_SDRAM:
900 spd_parse_ddr3(&si);
901 break;
902 case SPD_DT_DDR4_SDRAM:
903 spd_parse_ddr4(&si);
904 break;
905 case SPD_DT_LPDDR3_SDRAM:
906 case SPD_DT_LPDDR4_SDRAM:
907 case SPD_DT_LPDDR4X_SDRAM:
908 spd_parse_lp4(&si);
909 break;
910 case SPD_DT_DDR5_SDRAM:
911 spd_parse_ddr5(&si);
912 break;
913 case SPD_DT_LPDDR5_SDRAM:
914 case SPD_DT_LPDDR5X_SDRAM:
915 spd_parse_lp5(&si);
916 break;
917 default:
918 *err = LIBJEDEC_SPD_UNSUP_TYPE;
919 goto fatal;
920 }
921
922 /*
923 * We got everything, at this point add the error nvlist here.
924 */
925 if (si.si_error == LIBJEDEC_SPD_OK) {
926 if (!nvlist_empty(si.si_errs) &&
927 (ret = nvlist_add_nvlist(si.si_nvl, "errors",
928 si.si_errs)) != 0) {
929 VERIFY3S(ret, ==, ENOMEM);
930 *err = LIBJEDEC_SPD_NOMEM;
931 goto fatal;
932 }
933 nvlist_free(si.si_errs);
934 return (si.si_nvl);
935 }
936
937 *err = si.si_error;
938 fatal:
939 spd_fini_info(&si);
940 return (NULL);
941 }
942