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.\" Copyright 2024 Oxide Computer Company 13.\" 14.Dd September 2, 2024 15.Dt LIBJEDEC_SPD 3JEDEC 16.Os 17.Sh NAME 18.Nm libjedec_spd 19.Nd parse DIMM SPD data 20.Sh LIBRARY 21.Lb libjedec 22.Sh SYNOPSIS 23.In libjedec.h 24.Ft "nvlist_t *" 25.Fo libjedec_spd 26.Fa "const uint8_t *buf" 27.Fa "size_t buflen" 28.Fa "spd_error_t *errp 29.Fc 30.Sh DESCRIPTION 31The 32.Fn libjedec_spd 33function parses a binary payload of SPD 34.Pq serial presence detect 35data and transforms it into a 36.Vt nvlist_t 37that can be used by callers to inspect the data. 38.Pp 39SPD data is defined by JEDEC and used in various DDR DRAM standards. 40This information describes everything from the size and organization of 41the DIMM and its dies, 42timing information, electrical properties, manufacturing data, and more. 43A DRAM module 44.Pq the thing we plug into a computer 45is made up of a number of different components. 46For example, a DDR5 module 47.Pq which is plugged into a slot 48contains not only a number of different DRAM dies, but also power 49controllers, registers, clock drivers, and more. 50The SPD data describes all of this information. 51.Pp 52There are two major properties of SPD information that determine which 53keys are present in it: 54.Bl -enum -width Ds 55.It 56The DDR version that the device implements. 57Examples of the version information include DDR4, LPDDR5, DDR3, and many 58others. 59.It 60The module's type. 61Examples of this include whether it is an RDIMM 62.Pq registered DIMM , 63UDIMM 64.Pq unregistered DIMM , 65soldered down, etc. 66.El 67.Pp 68While each DDR version has different SPD information and the module's 69type may further modify parts of it, the 70.Vt nvlist_t 71that is returned attempts to normalize this information as much as 72possible. 73Each key that is returned is defined in 74.In libjedec.h 75as a macro. 76The key space is dot 77.Pq Sq \&. 78delineated. 79The following key spaces are currently used: 80.Bl -tag -width Ds 81.It Dq meta 82This contains information about the SPD ROM itself, the encoding 83revision, the DRAM standard in use, the type of module, etc. 84This section also contains all of the CRC values and information that 85affects how the rest of data is parsed 86.Pq such as whether or not the package is monolithic . 87.It Dq dram 88This section contains information that is specific to the SDRAM dies 89that are present. 90This includes addressing information such as the number of rows, 91columns, banks, and bank groups that are on each die. 92It also includes information such as the supported voltages of the dies 93themselves and then describes a lot of the different timing properties 94such as the minimum times that a controller must wait between certain 95actions. 96.It Dq ddr4 , Dq ddr5 97This section contains information that is specific to a given DDR 98standard and cannot otherwise share a common definition. 99This section has a few additional subdivisions of information to cover 100items related to the module's type or are otherwise logically grouped 101together such as refresh management. 102For example, the library uses namespaces such as 103.Dq ddr4.rdimm 104and 105.Dq ddr5.lrdimm 106for properties that would be specific to DDR4 RDIMMs and DDR5 LRDIMMs 107respectively. 108.Pp 109Similarly, there are subdivisions based on the device in question. 110For example, 111.Dq ddr3.mb 112and 113.Dq ddr4.rcd 114refer to properties of the DDR3 memory buffer and the DDR4 registering 115clock driver respectively. 116.It Dq lp 117This section describes information that is specific to low power 118devices that come from the LPDDR standards. 119.It Dq channel 120This section describes properties that are specific to the 121implementation of a channel. 122DDR3 and DDR4 DIMMs have a single channel. 123DDR5 DIMMs have a single channel that is broken into two sub-channels. 124A channel represents a specific data stream to the host. 125Most DDR channels are 64-bit channels with optional ECC. 126LPDDR devices have rather different channel widths and their soldered 127down form factor may have more than one channel present 128.Pq regardless of sub-channels . 129.It Dq module 130This section relates to information about the physical module which 131includes information such as its physical dimensions, the types of 132components that are present on it, signal mapping, and related. 133.It Dq mfg 134This section contains information about the module manufacturing. 135This is where things like the module's serial number, the manufacturer, 136the part number, and related can be found. 137Generally this information is found in a different part of the SPD ROM 138and therefore may not always be present. 139.It Dq errors 140This section is an embedded 141.Vt nvlist_t 142that contains keys for each value that couldn't be parsed. 143For more information, see the 144.Sx Parsing Errors 145section for more information. 146.El 147.Ss Data Types 148The library attempts to use a fixed number of data types when performing 149conversions and follows the following guidelines: 150.Bl -bullet 151.It 152By default, integer values use a 153.Vt uint32_t 154to try and make things more uniform where possible and to allow for 155future changes. 156A 157.Vt uint64_t 158is used when the types contain data that could naturally overflow a 159.Vt uint32_t 160such as when counting the number of bits, bytes, or measuring time 161.Pq which is normalized to picoseconds . 162.Pp 163All integers are denoted as such explicitly with their size: either 164.Sq uint32_t 165or 166.Sq uint64_t . 167Some banks of keys all have the same type and are denoted as such in the 168introductory block comment. 169Use 170.Xr nvlist_lookup_uint32 3NVPAIR 171or 172.Xr nvlist_lookup_uint64 3NVPAIR 173to retrieve these keys. 174.It 175Strings, which are stored as traditional 176.Vt char * 177values should only contain printable characters. 178.Pp 179All strings are denoted as such by using the comment 180.Sq string . 181Use 182.Xr nvlist_lookup_string 3NVPAIR 183to retrieve these keys. 184.It 185There are many values which represent enumerations. 186The raw type is stored as a 187.Vt uint32_t . 188In general, these enumerations, unless otherwise indicated should not be 189assumed to have the identical values of a given specification. 190This is done because the values are not always uniform from 191specification to specification, except in rare cases. 192Not all DDR specifications can result in the same set of enumeration 193values. 194.Pp 195For example, consider the 196.Vt spd_temp_type_t 197which is an enumeration that describes the kind of temperature 198monitoring device present. 199The enumeration contains values from DDR3, DDR4, and DDR5; however, each 200standard has its own defined type of SPD temperature sensor. 201.Pp 202All enumerations are denoted as such by using the comment 203.Sq uint32_t Pq enum . 204Use 205.Xr nvlist_lookup_uint32 3NVPAIR 206to retrieve these keys. 207.It 208Several items are used to indicate a fact, but do not have any actual 209data associated with them. 210Their mere presence is sufficient to indicate a unique property of the 211DIMM. 212Examples of this include if the package is non-monolithic, the DIMM has 213asymmetrical ranks, etc. 214.Pp 215All such items are denoted as such by using the comment 216.Sq key . 217Use 218.Xr nvlist_lookup_boolean 3NVPAIR 219to retrieve these keys. 220.It 221A few types use arrays of 222.Vt uint32_t 223values to denote information. 224Some of the keys have a fixed number of entries that are expected, while 225others are variable and will depend on the parsed data. 226For example, a JEDEC ID is always encoded as two 227.Vt uint32_t 228values, the continuation and the underlying ID itself. 229Other values, such as the number of supported voltages or CAS latencies 230will vary. 231.Pp 232All fixed size arrays denote their size by using the comment 233.Sq uint32_t [4] , 234where the number four is replaced with the actual size. 235All variable sized arrays elide the number and are denoted by the 236comment 237.Sq uint32_t [] . 238Use 239.Xr nvlist_lookup_uint32_array 3NVPAIR 240to retrieve these keys. 241.Pp 242A few keys will use different sized integer arrays. 243For example, arrays that relate to timing parameters use arrays of 244.Vt uint64_t 245to match the broader pattern of using the 246.Vt uint64_t 247for timing. 248.El 249.Ss Parsing Errors 250There are a number of different issues that can arise when trying to 251parse the SPD data that a device returns. 252The library attempts to parse as much as it can and breaks errors into 253two large categories: 254.Bl -enum -width Ds 255.It 256Errors which prevent us from doing anything at all, which are noted in 257the 258.Sx ERRORS 259section. 260The main items that relate to data 261.Pq as opposed to issues like running out of memory 262include when we encounter a DDR specification that we don't support or 263that the data has an encoding version we don't understand. 264This can also occur if there's not enough data for us to figure out what 265kind of SPD information is encoded. 266.It 267Errors that mean we cannot parse a specific piece of SPD data. 268This is by far the most common form of error that we encounter and these 269are the entries that make up the 270.Dq errors 271section of the returned 272.Vt nvlist_t 273that was mentioned earlier. 274.El 275.Pp 276The 277.Dq errors 278section is a nested 279.Vt nvlist_t 280that can be retrieved by using the 281.Dv SPD_KEY_ERRS 282key. 283This keys in this nvlist use the same name as the normal data keys. 284For example, if we were unable to properly parse the manufacturer's 285JEDEC ID, then the key 286.Dv SPD_KEY_MFG_MOD_MFG_ID 287.Pq Dq mfg.module-mfg-id 288would not be present in the top-level 289.Vt nvlist_t 290and would instead be in the error 291.Vt nvlist_t . 292.Pp 293Each item present in the error 294.Vt nvlist_t 295is itself a 296.Vt nvlist_t 297which contains the following keys: 298.Bl -tag -width Ds 299.It Dv SPD_KEY_ERRS_CODE 300The error code, a 301.Vt uint32_t, 302indicates a class of issue that occurred and its values are defined by 303the 304.Vt spd_error_kind_t 305enumeration. 306The following errors are defined: 307.Bl -tag -width Ds 308.It SPD_ERROR_NO_XLATE 309This indicates that the library did not know how to interpret a specific 310binary value. 311For example, if a given particular field was using what we believed to 312be a reserved value then we would set this error. 313.It SPD_ERROR_UNPRINT 314This indicates that we encountered a non-ASCII or otherwise unprintable 315character that was not allowed in the SPD string character set. 316.It SPD_ERROR_NO_DATA 317This indicates that there was no data for a given key. 318For example, this would be a string that consisted solely of space 319characters which are used to represent padding. 320.It SPD_ERROR_INTERNAL 321This indicates that something went wrong inside the library that was 322unexpected. 323.It SPD_ERROR_BAD_DATA 324This indicates that we've encountered something strange about the data 325in question. 326For example, this would be used for an invalid CRC or if the combination 327of values would cause an underflow in a calculation. 328.El 329.It Dv SPD_KEY_ERRS_MSG 330This is an error message that is intended for a person to understand and 331contains more specific information than the code above. 332.El 333.Pp 334Finally, there is one last top-level key that we will set if we find 335that the set of data that we had was incomplete. 336Rather than tag every single item in the 337.Dq errors 338.Vt nvlist_t , 339we instead insert the key 340.Dv SPD_KEY_INCOMPLETE 341on the top-level nvlist. 342The key is a 343.Vt uint32_t 344that contains the starting offset of the 345.Sy key 346that we were unable to parse, which may be less than the total amount of 347data that was provided. 348For example, if we had 100 bytes of data, but there was a 20 byte key 349starting at byte 90, the this key would have a value of 90 as that's 350what we were unable to parse. 351.Sh RETURN VALUES 352Upon successful completion, the 353.Fn libjedec_spd 354function returns an allocated 355.Vt nvlist_t 356that contains the parsed SPD data. 357There may be individual SPD fields that were unparsable which will be 358found in the 359.Dv SPD_KEY_ERRS 360field of the nvlist still. 361Otherwise 362.Dv NULL 363is returned 364and 365.Fa errp 366is set with a value that indicates why the function was unable to parse 367any data. 368.Sh EXAMPLES 369.Sy Example 1 370Printing SPD DRAM and Module Type 371.Pp 372The following example shows how to parse the SPD information and print 373out the type of DRAM and module's type. 374This example assumes that one has already obtained the raw SPD file from 375somewhere and just prints the raw values. 376.Bd -literal -offset 2 377#include <stdio.h> 378#include <stdint.h> 379#include <libjedec.h> 380 381void 382dump_dram_module_type(const uint8_t *buf, size_t len) 383{ 384 nvlist_t *nvl; 385 spd_error_t err; 386 uint32_t ddr, mod; 387 388 nvl = libjedec_spd(buf, len, &err); 389 if (nvl == NULL) { 390 (void) fprintf(stderr, "failed to parse SPD data: 0x%x\en", 391 err); 392 return; 393 } 394 395 if (nvlist_lookup_pairs(nvl, 0, 396 SPD_KEY_DRAM_TYPE, DATA_TYPE_UINT32, &ddr, 397 SPD_KEY_MOD_TYPE, DATA_TYPE_UINT32, &mod, 398 NULL) != 0) { 399 nvlist_free(nvl); 400 (void) fprintf(stderr, "failed to look up keys\en"); 401 return; 402 } 403 404 nvlist_free(nvl); 405 (void) printf("Found DDR type 0x%x, module type 0x%x\en", ddr, 406 mod); 407} 408.Ed 409.Sh ERRORS 410Upon returning from the 411.Fn libjedec_spd 412function, the 413.Fa errp 414pointer will be set to one of the following values: 415.Bl -tag -width Dv 416.It Er LIBJEDEC_SPD_OK 417This indicates that the system was able to parse SPD data into a valid 418.Vt nvlist_t 419and return it to the caller. 420This code should never appear when the function fails and returns 421.Dv NULL . 422Callers must still cehck the contents of the 423.Dv SPD_KEY_ERRS 424nvlist. 425.It Dv LIBJEDEC_SPD_NOMEM 426This indicates that the system ran out of memory while trying to 427construct the 428.Vt nvlist_t 429to return. 430.It Dv LIBJEDEC_SPD_TOOSHORT 431This indicates that not enough SPD data was present as indicated by 432.Fa buf 433and 434.Fa buflen 435for the given type of SPD information and therefore we were unable to 436successfully parse basic information. 437.It Dv LIBJEDEC_SPD_UNSUP_TYPE 438This indicates that the type of SPD data present, e.g. DDR4 SDRAM, 439LPDDR3, etc., that was found is not currently supported by the library. 440.It Dv LIBJEDEC_SPD_UNSUP_REV 441This indicates that while the library is familiar with the specific type 442of SPD data present, the encoding level of the data 443.Pq similar to a major version 444is unsupported by the library. 445.El 446.Sh INTERFACE STABILITY 447.Sy Uncommitted 448.Sh MT-LEVEL 449.Sy MT-Safe 450.Sh SEE ALSO 451.Xr libjedec 3LIB , 452.Xr nvlist_lookup_boolean 3NVPAIR , 453.Xr nvlist_lookup_string 3NVPAIR , 454.Xr nvlist_lookup_uint32 3NVPAIR , 455.Xr nvlist_lookup_uint32_array 3NVPAIR , 456.Xr nvlist_lookup_uint64 3NVPAIR 457.Pp 458.Rs 459.%Q JEDEC Solid State Technology Association 460.%T Serial Presence Detect (SPD), General Standard 461.%N 21-C 462.Re 463.Rs 464.%Q JEDEC Solid State Technology Association 465.%T DDR5 Serial Presence Detect (SPD) Contents 466.%N JESD400-5B Document Release 1.2 467.%D October 2023 468.Re 469.Rs 470.%Q JEDEC Solid State Technology Association 471.%T LPDDR5/5X Serial Presence Detect (SPD) Contents 472.%N JESD406-5 Document Release 1.0 473.%D June 2024 474.Re 475