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 2023 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 and DDD5 based SPD information with the 34 * 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_u32_array(spd_info_t *si, const char *key, 151 uint32_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_uint32_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_key(spd_info_t *si, const char *key) 168 { 169 int ret; 170 171 if (si->si_error != LIBJEDEC_SPD_OK) 172 return; 173 174 ret = nvlist_add_boolean(si->si_nvl, key); 175 if (ret != 0) { 176 VERIFY3S(ret, ==, ENOMEM); 177 si->si_error = LIBJEDEC_SPD_NOMEM; 178 return; 179 } 180 } 181 182 void 183 spd_insert_map(spd_info_t *si, const char *key, uint8_t spd_val, 184 const spd_value_map_t *maps, size_t nmaps) 185 { 186 for (size_t i = 0; i < nmaps; i++) { 187 if (maps[i].svm_spd != spd_val) 188 continue; 189 if (maps[i].svm_skip) 190 return; 191 192 spd_nvl_insert_u32(si, key, maps[i].svm_use); 193 return; 194 } 195 196 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered unknown " 197 "value: 0x%x", spd_val); 198 } 199 200 void 201 spd_insert_map64(spd_info_t *si, const char *key, uint8_t spd_val, 202 const spd_value_map64_t *maps, size_t nmaps) 203 { 204 for (size_t i = 0; i < nmaps; i++) { 205 if (maps[i].svm_spd != spd_val) 206 continue; 207 if (maps[i].svm_skip) 208 return; 209 210 spd_nvl_insert_u64(si, key, maps[i].svm_use); 211 return; 212 } 213 214 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered unknown " 215 "value: 0x%x", spd_val); 216 } 217 218 void 219 spd_insert_str_map(spd_info_t *si, const char *key, uint8_t spd_val, 220 const spd_str_map_t *maps, size_t nmaps) 221 { 222 for (size_t i = 0; i < nmaps; i++) { 223 if (maps[i].ssm_spd != spd_val) 224 continue; 225 if (maps[i].ssm_skip) 226 return; 227 228 spd_nvl_insert_str(si, key, maps[i].ssm_str); 229 return; 230 } 231 232 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered unknown " 233 "value: 0x%x", spd_val); 234 } 235 236 /* 237 * Map an array in its entirety to a corresponding set of values. If any one 238 * value cannot be translated, then we fail the whole item. 239 */ 240 void 241 spd_insert_map_array(spd_info_t *si, const char *key, const uint8_t *raw, 242 size_t nraw, const spd_value_map_t *maps, size_t nmaps) 243 { 244 uint32_t *trans; 245 246 trans = calloc(nraw, sizeof (uint32_t)); 247 if (trans == NULL) { 248 si->si_error = LIBJEDEC_SPD_NOMEM; 249 return; 250 } 251 252 for (size_t i = 0; i < nraw; i++) { 253 bool found = false; 254 for (size_t map = 0; map < nmaps; map++) { 255 if (maps[map].svm_spd != raw[i]) 256 continue; 257 ASSERT3U(maps[map].svm_skip, ==, false); 258 found = true; 259 trans[i] = maps[map].svm_use; 260 break; 261 } 262 263 if (!found) { 264 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "encountered " 265 "unknown array value: [%zu]=0x%x", i, raw[i]); 266 goto done; 267 } 268 } 269 270 spd_nvl_insert_u32_array(si, key, trans, nraw); 271 done: 272 free(trans); 273 } 274 275 void 276 spd_insert_range(spd_info_t *si, const char *key, uint8_t raw_val, 277 const spd_value_range_t *range) 278 { 279 /* 280 * Apply any base or multiple to the value. If the min or max are zero, 281 * then we ignore them. We apply the base before a multiple. 282 */ 283 uint32_t min = 0, max = UINT32_MAX; 284 uint32_t act = raw_val + range->svr_base; 285 286 if (range->svr_mult != 0) { 287 act *= range->svr_mult; 288 } 289 290 if (range->svr_max != 0) { 291 max = range->svr_max; 292 } 293 294 if (range->svr_min != 0) { 295 min = range->svr_min; 296 } else if (range->svr_base != 0) { 297 min = range->svr_base; 298 } 299 300 if (act > max || act < min) { 301 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "found value " 302 "0x%x (raw 0x%x) outside range [0x%x, 0x%x]", act, raw_val, 303 min, max); 304 } else { 305 spd_nvl_insert_u32(si, key, act); 306 } 307 } 308 309 /* 310 * Either insert the given flag for a key or OR it in if it already exists. 311 */ 312 void 313 spd_upsert_flag(spd_info_t *si, const char *key, uint32_t flag) 314 { 315 int ret; 316 uint32_t val; 317 318 ret = nvlist_lookup_uint32(si->si_nvl, key, &val); 319 if (ret != 0) { 320 VERIFY3S(ret, ==, ENOENT); 321 spd_nvl_insert_u32(si, key, flag); 322 return; 323 } 324 325 VERIFY0(val & flag); 326 val |= flag; 327 spd_nvl_insert_u32(si, key, val); 328 } 329 330 void 331 spd_parse_rev(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 332 { 333 const uint8_t data = si->si_data[off]; 334 const uint8_t enc = SPD_DDR4_SPD_REV_ENC(data); 335 const uint8_t add = SPD_DDR4_SPD_REV_ENC(data); 336 337 spd_nvl_insert_u32(si, SPD_KEY_REV_ENC, enc); 338 spd_nvl_insert_u32(si, SPD_KEY_REV_ADD, add); 339 } 340 341 void 342 spd_parse_jedec_id(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 343 { 344 uint32_t id[2]; 345 346 VERIFY3U(len, ==, 2); 347 id[0] = SPD_MFG_ID0_CONT(si->si_data[off]); 348 id[1] = si->si_data[off + 1]; 349 350 spd_nvl_insert_u32_array(si, key, id, ARRAY_SIZE(id)); 351 } 352 353 void 354 spd_parse_jedec_id_str(spd_info_t *si, uint32_t off, uint32_t len, 355 const char *key) 356 { 357 uint8_t cont = SPD_MFG_ID0_CONT(si->si_data[off]); 358 const char *str; 359 360 VERIFY3U(len, ==, 2); 361 str = libjedec_vendor_string(cont, si->si_data[off + 1]); 362 if (str != NULL) { 363 spd_nvl_insert_str(si, key, str); 364 } else { 365 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "no matching " 366 "libjedec vendor string for 0x%x,0x%x", cont, 367 si->si_data[off + 1]); 368 } 369 } 370 371 /* 372 * Parse a string that is at most len bytes wide and is padded with spaces. If 373 * the string contains an unprintable, then we will not pull this off and set an 374 * error for the string's key. 128 bytes should be larger than any ascii string 375 * that we encounter as that is the size of most regions in SPD data. 376 */ 377 void 378 spd_parse_string(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 379 { 380 uint32_t nbytes = 0; 381 char buf[128]; 382 383 VERIFY3U(sizeof (buf), >, len); 384 for (uint32_t i = 0; i < len; i++) { 385 if (si->si_data[off + i] == ' ') { 386 nbytes = i; 387 break; 388 } 389 390 if (isascii(si->si_data[off + i]) == 0 || 391 isprint(si->si_data[off + i]) == 0) { 392 spd_nvl_err(si, key, SPD_ERROR_UNPRINT, 393 "byte %u for key %s (off: 0x%x, val: 0x%x) is not " 394 "printable", i, key, off + 1, 395 si->si_data[off + i]); 396 return; 397 } 398 } 399 400 if (nbytes == 0) { 401 spd_nvl_err(si, key, SPD_ERROR_NO_DATA, "key %s has " 402 "no valid bytes in the string", key); 403 return; 404 } 405 406 (void) memcpy(buf, &si->si_data[off], nbytes); 407 buf[nbytes] = '\0'; 408 spd_nvl_insert_str(si, key, buf); 409 } 410 411 /* 412 * Turn an array of bytes into a hex string. We need to allocate up to two bytes 413 * per length that we have. We always zero pad such strings. We statically size 414 * our buffer because the largest such string we have right now is a 4-byte 415 * serial number. With the 128 byte buffer below, we could deal with a length up 416 * to 63 (far beyond what we expect to ever see). 417 */ 418 void 419 spd_parse_hex_string(spd_info_t *si, uint32_t off, uint32_t len, 420 const char *key) 421 { 422 char buf[128]; 423 size_t nwrite = 0; 424 425 VERIFY3U(sizeof (buf), >=, len * 2 + 1); 426 427 for (uint32_t i = 0; i < len; i++) { 428 int ret = snprintf(buf + nwrite, sizeof (buf) - nwrite, 429 "%02X", si->si_data[off + i]); 430 if (ret < 0) { 431 spd_nvl_err(si, key, SPD_ERROR_INTERNAL, 432 "snprintf failed unexpectedly for key %s: %s", 433 key, strerror(errno)); 434 return; 435 } 436 437 VERIFY3U(ret, ==, 2); 438 nwrite += ret; 439 } 440 441 spd_nvl_insert_str(si, key, buf); 442 } 443 444 /* 445 * Several SPD keys are explicit BCD major and minor versions in a given nibble. 446 * This is most common in DDR5, but otherwise one should probably use 447 * spd_parse_hex_string(). 448 */ 449 void 450 spd_parse_hex_vers(spd_info_t *si, uint32_t off, uint32_t len, 451 const char *key) 452 { 453 const uint8_t data = si->si_data[off]; 454 const uint8_t maj = bitx8(data, 7, 4); 455 const uint8_t min = bitx8(data, 3, 0); 456 char buf[128]; 457 458 VERIFY3U(len, ==, 1); 459 460 int ret = snprintf(buf, sizeof (buf), "%x.%x", maj, min); 461 if (ret < 0) { 462 spd_nvl_err(si, key, SPD_ERROR_INTERNAL, 463 "snprintf failed unexpectedly for key %s: %s", 464 key, strerror(errno)); 465 return; 466 } 467 468 spd_nvl_insert_str(si, key, buf); 469 } 470 471 void 472 spd_parse_raw_u8(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 473 { 474 VERIFY3U(len, ==, 1); 475 spd_nvl_insert_u32(si, key, si->si_data[off]); 476 } 477 478 void 479 spd_parse_dram_step(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 480 { 481 VERIFY3U(len, ==, 1); 482 483 if (si->si_data[off] == SPD_DRAM_STEP_NOINFO) 484 return; 485 486 spd_parse_hex_string(si, off, len, key); 487 } 488 489 /* 490 * Height and thickness have the same meaning across DDR3-DDR5. 491 */ 492 static const spd_value_range_t spd_height_range = { 493 .svr_base = SPD_DDR5_COM_HEIGHT_BASE 494 }; 495 496 static const spd_value_range_t spd_thick_range = { 497 .svr_base = SPD_DDR5_COM_THICK_BASE 498 }; 499 500 void 501 spd_parse_height(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 502 { 503 const uint8_t data = si->si_data[off]; 504 const uint8_t height = SPD_DDR5_COM_HEIGHT_MM(data); 505 spd_insert_range(si, key, height, &spd_height_range); 506 } 507 508 void 509 spd_parse_thickness(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 510 { 511 const uint8_t data = si->si_data[off]; 512 const uint8_t front = SPD_DDR5_COM_THICK_FRONT(data); 513 const uint8_t back = SPD_DDR5_COM_THICK_FRONT(data); 514 515 spd_insert_range(si, SPD_KEY_MOD_FRONT_THICK, front, &spd_thick_range); 516 spd_insert_range(si, SPD_KEY_MOD_BACK_THICK, back, &spd_thick_range); 517 } 518 519 /* 520 * Calculate the DRAM CRC16. The crc ranges over [ off, off + len - 2). The crc 521 * lsb is at off + len - 2, and the msb is at off + len - 1. The JEDEC specs 522 * describe the algorithm (e.g. 21-C Annex L, 8.1.53). 523 */ 524 void 525 spd_parse_crc(spd_info_t *si, uint32_t off, uint32_t len, const char *key) 526 { 527 uint32_t crc = 0; 528 const uint16_t expect = si->si_data[off + len - 2] | 529 (si->si_data[off + len - 1] << 8); 530 531 for (uint32_t i = 0; i < len - 2; i++) { 532 crc = crc ^ (uint32_t)si->si_data[off + i] << 8; 533 for (uint32_t c = 0; c < 8; c++) { 534 if (crc & 0x8000) { 535 crc = crc << 1 ^ 0x1021; 536 } else { 537 crc = crc << 1; 538 } 539 } 540 } 541 542 crc &= 0xffff; 543 if (crc == expect) { 544 spd_nvl_insert_u32(si, key, crc); 545 } else { 546 spd_nvl_err(si, key, SPD_ERROR_BAD_DATA, "crc mismatch: " 547 "expected 0x%x, found 0x%x", expect, crc); 548 } 549 } 550 551 void 552 spd_parse(spd_info_t *sip, const spd_parse_t *parse, size_t nparse) 553 { 554 for (size_t i = 0; i < nparse; i++) { 555 uint32_t len; 556 557 if (parse[i].sp_len != 0) { 558 len = parse[i].sp_len; 559 } else { 560 len = 1; 561 } 562 563 if (len + parse[i].sp_off >= sip->si_nbytes) { 564 if ((sip->si_flags & SPD_INFO_F_INCOMPLETE) != 0) 565 continue; 566 sip->si_flags |= SPD_INFO_F_INCOMPLETE; 567 ASSERT3U(parse[i].sp_off, <, UINT32_MAX); 568 spd_nvl_insert_u32(sip, SPD_KEY_INCOMPLETE, 569 (uint32_t)parse[i].sp_off); 570 } else { 571 parse[i].sp_parse(sip, parse[i].sp_off, len, 572 parse[i].sp_key); 573 } 574 575 if (sip->si_error != LIBJEDEC_SPD_OK) { 576 return; 577 } 578 } 579 } 580 581 static spd_error_t 582 spd_init_info(spd_info_t *sip) 583 { 584 int ret; 585 586 if ((ret = nvlist_alloc(&sip->si_nvl, NV_UNIQUE_NAME, 0)) != 0) { 587 VERIFY3S(ret, ==, ENOMEM); 588 return (LIBJEDEC_SPD_NOMEM); 589 } 590 591 if ((ret = nvlist_alloc(&sip->si_errs, NV_UNIQUE_NAME, 0)) != 0) { 592 VERIFY3S(ret, ==, ENOMEM); 593 return (LIBJEDEC_SPD_NOMEM); 594 } 595 596 return (LIBJEDEC_SPD_OK); 597 } 598 599 static void 600 spd_fini_info(spd_info_t *sip) 601 { 602 nvlist_free(sip->si_nvl); 603 nvlist_free(sip->si_errs); 604 } 605 606 nvlist_t * 607 libjedec_spd(const uint8_t *buf, size_t nbytes, spd_error_t *err) 608 { 609 int ret; 610 spd_error_t set; 611 spd_info_t si; 612 613 if (err == NULL) { 614 err = &set; 615 } 616 617 (void) memset(&si, 0, sizeof (spd_info_t)); 618 si.si_data = buf; 619 si.si_nbytes = nbytes; 620 621 *err = spd_init_info(&si); 622 if (si.si_error != LIBJEDEC_SPD_OK) { 623 goto fatal; 624 } 625 626 /* 627 * To begin parsing the SPD, we must first look at byte 2, which appears 628 * to almost always be the Key Byte / Host Bus Command Protocol Type 629 * which then tells us how the rest of the data is formatted. 630 */ 631 if (si.si_nbytes <= SPD_DRAM_TYPE) { 632 *err = LIBJEDEC_SPD_TOOSHORT; 633 goto fatal; 634 } 635 636 si.si_error = LIBJEDEC_SPD_OK; 637 si.si_dram = buf[SPD_DRAM_TYPE]; 638 switch (si.si_dram) { 639 case SPD_DT_DDR4_SDRAM: 640 spd_parse_ddr4(&si); 641 break; 642 case SPD_DT_DDR5_SDRAM: 643 spd_parse_ddr5(&si); 644 break; 645 default: 646 *err = LIBJEDEC_SPD_UNSUP_TYPE; 647 goto fatal; 648 } 649 650 /* 651 * We got everything, at this point add the error nvlist here. 652 */ 653 if (si.si_error == LIBJEDEC_SPD_OK) { 654 if (!nvlist_empty(si.si_errs) && 655 (ret = nvlist_add_nvlist(si.si_nvl, "errors", 656 si.si_errs)) != 0) { 657 VERIFY3S(ret, ==, ENOMEM); 658 *err = LIBJEDEC_SPD_NOMEM; 659 goto fatal; 660 } 661 nvlist_free(si.si_errs); 662 return (si.si_nvl); 663 } 664 665 *err = si.si_error; 666 fatal: 667 spd_fini_info(&si); 668 return (NULL); 669 } 670