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