xref: /illumos-gate/usr/src/lib/libjedec/common/libjedec_spd.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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 2024 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 	VERIFY3U(sizeof (buf), >, len);
449 	for (uint32_t i = 0; i < len; i++) {
450 		if (si->si_data[off + i] == ' ') {
451 			nbytes = i;
452 			break;
453 		}
454 
455 		if (isascii(si->si_data[off + i]) == 0 ||
456 		    isprint(si->si_data[off + i]) == 0) {
457 			spd_nvl_err(si, key, SPD_ERROR_UNPRINT,
458 			    "byte %u for key %s (off: 0x%x, val: 0x%x) is not "
459 			    "printable", i, key, off + 1,
460 			    si->si_data[off + i]);
461 			return;
462 		}
463 	}
464 
465 	if (nbytes == 0) {
466 		spd_nvl_err(si, key, SPD_ERROR_NO_DATA, "key %s has "
467 		    "no valid bytes in the string", key);
468 		return;
469 	}
470 
471 	(void) memcpy(buf, &si->si_data[off], nbytes);
472 	buf[nbytes] = '\0';
473 	spd_nvl_insert_str(si, key, buf);
474 }
475 
476 /*
477  * Turn an array of bytes into a hex string. We need to allocate up to two bytes
478  * per length that we have. We always zero pad such strings. We statically size
479  * our buffer because the largest such string we have right now is a 4-byte
480  * serial number. With the 128 byte buffer below, we could deal with a length up
481  * to 63 (far beyond what we expect to ever see).
482  */
483 void
spd_parse_hex_string(spd_info_t * si,uint32_t off,uint32_t len,const char * key)484 spd_parse_hex_string(spd_info_t *si, uint32_t off, uint32_t len,
485     const char *key)
486 {
487 	char buf[128];
488 	size_t nwrite = 0;
489 
490 	VERIFY3U(sizeof (buf), >=, len * 2 + 1);
491 
492 	for (uint32_t i = 0; i < len; i++) {
493 		int ret = snprintf(buf + nwrite, sizeof (buf) - nwrite,
494 		    "%02X", si->si_data[off + i]);
495 		if (ret < 0) {
496 			spd_nvl_err(si, key, SPD_ERROR_INTERNAL,
497 			    "snprintf failed unexpectedly for key %s: %s",
498 			    key, strerror(errno));
499 			return;
500 		}
501 
502 		VERIFY3U(ret, ==, 2);
503 		nwrite += ret;
504 	}
505 
506 	spd_nvl_insert_str(si, key, buf);
507 }
508 
509 /*
510  * Several SPD keys are explicit BCD major and minor versions in a given nibble.
511  * This is most common in DDR5, but otherwise one should probably use
512  * spd_parse_hex_string().
513  */
514 void
spd_parse_hex_vers(spd_info_t * si,uint32_t off,uint32_t len,const char * key)515 spd_parse_hex_vers(spd_info_t *si, uint32_t off, uint32_t len,
516     const char *key)
517 {
518 	const uint8_t data = si->si_data[off];
519 	const uint8_t maj = bitx8(data, 7, 4);
520 	const uint8_t min = bitx8(data, 3, 0);
521 	char buf[128];
522 
523 	VERIFY3U(len, ==, 1);
524 
525 	int ret = snprintf(buf, sizeof (buf), "%X.%X", maj, min);
526 	if (ret < 0) {
527 		spd_nvl_err(si, key, SPD_ERROR_INTERNAL,
528 		    "snprintf failed unexpectedly for key %s: %s",
529 		    key, strerror(errno));
530 		return;
531 	}
532 
533 	spd_nvl_insert_str(si, key, buf);
534 }
535 
536 void
spd_parse_raw_u8(spd_info_t * si,uint32_t off,uint32_t len,const char * key)537 spd_parse_raw_u8(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
538 {
539 	VERIFY3U(len, ==, 1);
540 	spd_nvl_insert_u32(si, key, si->si_data[off]);
541 }
542 
543 void
spd_parse_u8_array(spd_info_t * si,uint32_t off,uint32_t len,const char * key)544 spd_parse_u8_array(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
545 {
546 	uint8_t *data = (uint8_t *)si->si_data + off;
547 
548 	spd_nvl_insert_u8_array(si, key, data, len);
549 }
550 
551 void
spd_parse_dram_step(spd_info_t * si,uint32_t off,uint32_t len,const char * key)552 spd_parse_dram_step(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
553 {
554 	VERIFY3U(len, ==, 1);
555 
556 	if (si->si_data[off] == SPD_DRAM_STEP_NOINFO)
557 		return;
558 
559 	spd_parse_hex_string(si, off, len, key);
560 }
561 
562 /*
563  * Height and thickness have the same meaning across DDR3-DDR5.
564  */
565 static const spd_value_range_t spd_height_range = {
566 	.svr_base = SPD_DDR5_COM_HEIGHT_BASE
567 };
568 
569 static const spd_value_range_t spd_thick_range = {
570 	.svr_base = SPD_DDR5_COM_THICK_BASE
571 };
572 
573 void
spd_parse_height(spd_info_t * si,uint32_t off,uint32_t len,const char * key)574 spd_parse_height(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
575 {
576 	const uint8_t data = si->si_data[off];
577 	const uint8_t height = SPD_DDR5_COM_HEIGHT_MM(data);
578 	spd_insert_range(si, key, height, &spd_height_range);
579 }
580 
581 void
spd_parse_thickness(spd_info_t * si,uint32_t off,uint32_t len,const char * key)582 spd_parse_thickness(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
583 {
584 	const uint8_t data = si->si_data[off];
585 	const uint8_t front = SPD_DDR5_COM_THICK_FRONT(data);
586 	const uint8_t back = SPD_DDR5_COM_THICK_BACK(data);
587 
588 	spd_insert_range(si, SPD_KEY_MOD_FRONT_THICK, front, &spd_thick_range);
589 	spd_insert_range(si, SPD_KEY_MOD_BACK_THICK, back, &spd_thick_range);
590 }
591 
592 /*
593  * Common timestamp calculation logic for DDR3-4, LPDDR3-5 that assumes 1 ps FT
594  * and 125ps MTB. The MTB may either be an 8-bit, 12-bit, or 16-bit value. The
595  * FTB value is actually a signed two's complement value that we use to adjust
596  * things. We need to check for two illegal values:
597  *
598  * 1. That the value as a whole after adjustment is non-zero.
599  * 2. That the fine adjustment does not cause us to underflow (i.e. unit values
600  *    for the MTB of 1 and the FTB of -126).
601  */
602 void
spd_parse_ddr_time(spd_info_t * si,const char * key,uint8_t upper_mtb,uint8_t mtb,uint8_t ftb)603 spd_parse_ddr_time(spd_info_t *si, const char *key, uint8_t upper_mtb,
604     uint8_t mtb, uint8_t ftb)
605 {
606 	uint64_t ps = ((upper_mtb << 8) | mtb) * SPD_DDR4_MTB_PS;
607 	int8_t adj = (int8_t)ftb * SPD_DDR4_FTB_PS;
608 
609 	if (ps == 125 && adj <= -125) {
610 		spd_nvl_err(si, key, SPD_ERROR_BAD_DATA,
611 		    "MTB (%" PRIu64 "ps) and FTB (%dps) would cause underflow",
612 		    ps, adj);
613 		return;
614 	}
615 
616 	ps += adj;
617 	if (ps == 0) {
618 		spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
619 		    "encountered unexpected zero time value");
620 		return;
621 	}
622 	spd_nvl_insert_u64(si, key, ps);
623 }
624 
625 /*
626  * Combine two values into a picosecond value that is split between the MTB and
627  * FTB. The MTB and FTB are split amongst a large number of bytes and are not
628  * contiguous. The MTB is at data[off], and the FTB is at data[off + len - 1].
629  *
630  * This is shared by LPDDR3-5 which all use the same time base parameters. DDR3
631  * also uses it for a number of items based on our assumptions.
632  */
633 void
spd_parse_mtb_ftb_time_pair(spd_info_t * si,uint32_t off,uint32_t len,const char * key)634 spd_parse_mtb_ftb_time_pair(spd_info_t *si, uint32_t off, uint32_t len,
635     const char *key)
636 {
637 	const uint8_t mtb = si->si_data[off];
638 	const uint8_t ftb = si->si_data[off + len - 1];
639 
640 	return (spd_parse_ddr_time(si, key, 0, mtb, ftb));
641 }
642 
643 /*
644  * Parse a pair of values where the MTB is split across two uint8_t's. The LSB
645  * is in off and the MSB is in off+1.
646  */
647 void
spd_parse_mtb_pair(spd_info_t * si,uint32_t off,uint32_t len,const char * key)648 spd_parse_mtb_pair(spd_info_t *si, uint32_t off, uint32_t len,
649     const char *key)
650 {
651 	ASSERT3U(len, ==, 2);
652 	return (spd_parse_ddr_time(si, key, si->si_data[off + 1],
653 	    si->si_data[off], 0));
654 }
655 
656 static const spd_str_map_t spd_ddr_design_map0[32] = {
657 	{ 0, "A", false },
658 	{ 1, "B", false },
659 	{ 2, "C", false },
660 	{ 3, "D", false },
661 	{ 4, "E", false },
662 	{ 5, "F", false },
663 	{ 6, "G", false },
664 	{ 7, "H", false },
665 	{ 8, "J", false },
666 	{ 9, "K", false },
667 	{ 10, "L", false },
668 	{ 11, "M", false },
669 	{ 12, "N", false },
670 	{ 13, "P", false },
671 	{ 14, "R", false },
672 	{ 15, "T", false },
673 	{ 16, "U", false },
674 	{ 17, "V", false },
675 	{ 18, "W", false },
676 	{ 19, "Y", false },
677 	{ 20, "AA", false },
678 	{ 21, "AB", false },
679 	{ 22, "AC", false },
680 	{ 23, "AD", false },
681 	{ 24, "AE", false },
682 	{ 25, "AF", false },
683 	{ 26, "AG", false },
684 	{ 27, "AH", false },
685 	{ 28, "AJ", false },
686 	{ 29, "AK", false },
687 	{ 30, "AL", false },
688 	{ 31, "ZZ", false }
689 };
690 
691 static const spd_str_map_t spd_ddr_design_map1[32] = {
692 	{ 0, "AM", false },
693 	{ 1, "AN", false },
694 	{ 2, "AP", false },
695 	{ 3, "AR", false },
696 	{ 4, "AT", false },
697 	{ 5, "AU", false },
698 	{ 6, "AV", false },
699 	{ 7, "AW", false },
700 	{ 8, "AY", false },
701 	{ 9, "BA", false },
702 	{ 10, "BB", false },
703 	{ 11, "BC", false },
704 	{ 12, "BD", false },
705 	{ 13, "BE", false },
706 	{ 14, "BF", false },
707 	{ 15, "BG", false },
708 	{ 16, "BH", false },
709 	{ 17, "BJ", false },
710 	{ 18, "BK", false },
711 	{ 19, "BL", false },
712 	{ 20, "BM", false },
713 	{ 21, "BN", false },
714 	{ 22, "BP", false },
715 	{ 23, "BR", false },
716 	{ 24, "BT", false },
717 	{ 25, "BU", false },
718 	{ 26, "BV", false },
719 	{ 27, "BW", false },
720 	{ 28, "BY", false },
721 	{ 29, "CA", false },
722 	{ 30, "CB", false },
723 	{ 31, "ZZ", false }
724 };
725 
726 /*
727  * In DDR3/4 and LPDDR3-5 the design information contains both a reference raw
728  * card and a revision of the card. The card revision is split between two
729  * bytes, the design and the height field. This is common logic that'll check
730  * both. We use the DDR4 constants for the fields, but they are the same across
731  * all versions.
732  */
733 void
spd_parse_design(spd_info_t * si,uint32_t design,uint32_t height)734 spd_parse_design(spd_info_t *si, uint32_t design, uint32_t height)
735 {
736 	const uint8_t data = si->si_data[design];
737 	const uint8_t rev = SPD_DDR4_RDIMM_REF_REV(data);
738 	const uint8_t card = SPD_DDR4_RDIMM_REF_CARD(data);
739 
740 	if (SPD_DDR4_RDIMM_REF_EXT(data) != 0) {
741 		spd_insert_str_map(si, SPD_KEY_MOD_REF_DESIGN, card,
742 		    spd_ddr_design_map1, ARRAY_SIZE(spd_ddr_design_map1));
743 	} else {
744 		spd_insert_str_map(si, SPD_KEY_MOD_REF_DESIGN, card,
745 		    spd_ddr_design_map0, ARRAY_SIZE(spd_ddr_design_map0));
746 	}
747 
748 	/*
749 	 * The design rev is split between here and the height field. If we
750 	 * have the value of three, then we must also add in the height's value
751 	 * to this.
752 	 */
753 	if (rev == SPD_DDR4_RDIMM_REV_USE_HEIGHT) {
754 		const uint8_t hdata = si->si_data[height];
755 		const uint8_t hrev = SPD_DDR4_RDIMM_HEIGHT_REV(hdata);
756 		spd_nvl_insert_u32(si, SPD_KEY_MOD_DESIGN_REV, rev + hrev);
757 	} else {
758 		spd_nvl_insert_u32(si, SPD_KEY_MOD_DESIGN_REV, rev);
759 	}
760 }
761 
762 /*
763  * Calculate the DRAM CRC16. The crc calculation covers [ off, off + len ). The
764  * expected CRC is in expect. The JEDEC specs describe the algorithm (e.g. 21-C
765  * Annex L, 8.1.53).
766  */
767 void
spd_parse_crc_expect(spd_info_t * si,uint32_t off,uint32_t len,uint16_t expect,const char * key)768 spd_parse_crc_expect(spd_info_t *si, uint32_t off, uint32_t len,
769     uint16_t expect, const char *key)
770 {
771 	uint32_t crc = 0;
772 
773 	for (uint32_t i = 0; i < len; i++) {
774 		crc = crc ^ (uint32_t)si->si_data[off + i] << 8;
775 		for (uint32_t c = 0; c < 8; c++) {
776 			if (crc & 0x8000) {
777 				crc = crc << 1 ^ 0x1021;
778 			} else {
779 				crc = crc << 1;
780 			}
781 		}
782 	}
783 
784 	crc &= 0xffff;
785 	if (crc == expect) {
786 		spd_nvl_insert_u32(si, key, crc);
787 	} else {
788 		spd_nvl_err(si, key, SPD_ERROR_BAD_DATA, "crc mismatch: "
789 		    "expected 0x%x, found 0x%x", expect, crc);
790 	}
791 }
792 
793 /*
794  * Calculate the DRAM CRC16. The crc ranges over [ off, off + len - 2). The crc
795  * lsb is at off + len - 2, and the msb is at off + len - 1.
796  */
797 void
spd_parse_crc(spd_info_t * si,uint32_t off,uint32_t len,const char * key)798 spd_parse_crc(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
799 {
800 	const uint16_t expect = si->si_data[off + len - 2] |
801 	    (si->si_data[off + len - 1] << 8);
802 
803 	spd_parse_crc_expect(si, off, len - 2, expect, key);
804 }
805 
806 void
spd_parse(spd_info_t * sip,const spd_parse_t * parse,size_t nparse)807 spd_parse(spd_info_t *sip, const spd_parse_t *parse, size_t nparse)
808 {
809 	for (size_t i = 0; i < nparse; i++) {
810 		uint32_t len;
811 
812 		if (parse[i].sp_len != 0) {
813 			len = parse[i].sp_len;
814 		} else {
815 			len = 1;
816 		}
817 
818 		if (len + parse[i].sp_off >= sip->si_nbytes) {
819 			if ((sip->si_flags & SPD_INFO_F_INCOMPLETE) != 0)
820 				continue;
821 			sip->si_flags |= SPD_INFO_F_INCOMPLETE;
822 			ASSERT3U(parse[i].sp_off, <, UINT32_MAX);
823 			spd_nvl_insert_u32(sip, SPD_KEY_INCOMPLETE,
824 			    (uint32_t)parse[i].sp_off);
825 		} else {
826 			parse[i].sp_parse(sip, parse[i].sp_off, len,
827 			    parse[i].sp_key);
828 		}
829 
830 		if (sip->si_error != LIBJEDEC_SPD_OK) {
831 			return;
832 		}
833 	}
834 }
835 
836 static spd_error_t
spd_init_info(spd_info_t * sip)837 spd_init_info(spd_info_t *sip)
838 {
839 	int ret;
840 
841 	if ((ret = nvlist_alloc(&sip->si_nvl, NV_UNIQUE_NAME, 0)) != 0) {
842 		VERIFY3S(ret, ==, ENOMEM);
843 		return (LIBJEDEC_SPD_NOMEM);
844 	}
845 
846 	if ((ret = nvlist_alloc(&sip->si_errs, NV_UNIQUE_NAME, 0)) != 0) {
847 		VERIFY3S(ret, ==, ENOMEM);
848 		return (LIBJEDEC_SPD_NOMEM);
849 	}
850 
851 	return (LIBJEDEC_SPD_OK);
852 }
853 
854 static void
spd_fini_info(spd_info_t * sip)855 spd_fini_info(spd_info_t *sip)
856 {
857 	nvlist_free(sip->si_nvl);
858 	nvlist_free(sip->si_errs);
859 }
860 
861 nvlist_t *
libjedec_spd(const uint8_t * buf,size_t nbytes,spd_error_t * err)862 libjedec_spd(const uint8_t *buf, size_t nbytes, spd_error_t *err)
863 {
864 	int ret;
865 	spd_error_t set;
866 	spd_info_t si;
867 
868 	if (err == NULL) {
869 		err = &set;
870 	}
871 
872 	(void) memset(&si, 0, sizeof (spd_info_t));
873 	si.si_data = buf;
874 	si.si_nbytes = nbytes;
875 
876 	*err = spd_init_info(&si);
877 	if (si.si_error != LIBJEDEC_SPD_OK) {
878 		goto fatal;
879 	}
880 
881 	/*
882 	 * To begin parsing the SPD, we must first look at byte 2, which appears
883 	 * to almost always be the Key Byte / Host Bus Command Protocol Type
884 	 * which then tells us how the rest of the data is formatted.
885 	 */
886 	if (si.si_nbytes <= SPD_DRAM_TYPE) {
887 		*err = LIBJEDEC_SPD_TOOSHORT;
888 		goto fatal;
889 	}
890 
891 	si.si_error = LIBJEDEC_SPD_OK;
892 	si.si_dram = buf[SPD_DRAM_TYPE];
893 	switch (si.si_dram) {
894 	case SPD_DT_DDR3_SDRAM:
895 		spd_parse_ddr3(&si);
896 		break;
897 	case SPD_DT_DDR4_SDRAM:
898 		spd_parse_ddr4(&si);
899 		break;
900 	case SPD_DT_LPDDR3_SDRAM:
901 	case SPD_DT_LPDDR4_SDRAM:
902 	case SPD_DT_LPDDR4X_SDRAM:
903 		spd_parse_lp4(&si);
904 		break;
905 	case SPD_DT_DDR5_SDRAM:
906 		spd_parse_ddr5(&si);
907 		break;
908 	case SPD_DT_LPDDR5_SDRAM:
909 	case SPD_DT_LPDDR5X_SDRAM:
910 		spd_parse_lp5(&si);
911 		break;
912 	default:
913 		*err = LIBJEDEC_SPD_UNSUP_TYPE;
914 		goto fatal;
915 	}
916 
917 	/*
918 	 * We got everything, at this point add the error nvlist here.
919 	 */
920 	if (si.si_error == LIBJEDEC_SPD_OK) {
921 		if (!nvlist_empty(si.si_errs) &&
922 		    (ret = nvlist_add_nvlist(si.si_nvl, "errors",
923 		    si.si_errs)) != 0) {
924 			VERIFY3S(ret, ==, ENOMEM);
925 			*err = LIBJEDEC_SPD_NOMEM;
926 			goto fatal;
927 		}
928 		nvlist_free(si.si_errs);
929 		return (si.si_nvl);
930 	}
931 
932 	*err = si.si_error;
933 fatal:
934 	spd_fini_info(&si);
935 	return (NULL);
936 }
937