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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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