LIBJEDEC_SPD(3JEDEC) JEDEC Support Library Functions LIBJEDEC_SPD(3JEDEC)

NAME


libjedec_spd - parse DIMM SPD data

LIBRARY


JEDEC Support Library Functions (libjedec, -ljedec)

SYNOPSIS


#include <libjedec.h>

nvlist_t *
libjedec_spd(const uint8_t *buf, size_t buflen, spd_error_t *errp);

DESCRIPTION


The libjedec_spd() function parses a binary payload of SPD (serial
presence detect) data and transforms it into a nvlist_t that can be
used by callers to inspect the data.

SPD data is defined by JEDEC and used in various DDR DRAM standards.
This information describes everything from the size and organization of
the DIMM and its dies, timing information, electrical properties,
manufacturing data, and more. A DRAM module (the thing we plug into a
computer) is made up of a number of different components. For example,
a DDR5 module (which is plugged into a slot) contains not only a number
of different DRAM dies, but also power controllers, registers, clock
drivers, and more. The SPD data describes all of this information.

There are two major properties of SPD information that determine which
keys are present in it:

1. The DDR version that the device implements. Examples of the
version information include DDR4, LPDDR5, DDR3, and many
others.

2. The module's type. Examples of this include whether it is an
RDIMM (registered DIMM), UDIMM (unregistered DIMM), soldered
down, etc.

While each DDR version has different SPD information and the module's
type may further modify parts of it, the nvlist_t that is returned
attempts to normalize this information as much as possible. Each key
that is returned is defined in <libjedec.h> as a macro. The key space
is dot (`.') delineated. The following key spaces are currently used:

"meta" This contains information about the SPD ROM itself, the
encoding revision, the DRAM standard in use, the type of
module, etc. This section also contains all of the CRC values
and information that affects how the rest of data is parsed
(such as whether or not the package is monolithic).

"dram" This section contains information that is specific to the SDRAM
dies that are present. This includes addressing information
such as the number of rows, columns, banks, and bank groups
that are on each die. It also includes information such as the
supported voltages of the dies themselves and then describes a
lot of the different timing properties such as the minimum
times that a controller must wait between certain actions.

"ddr4, "ddr5""
This section contains information that is specific to a given
DDR standard and cannot otherwise share a common definition.
This section has a few additional subdivisions of information
to cover items related to the module's type or are otherwise
logically grouped together such as refresh management. For
example, the library uses namespaces such as "ddr4.rdimm" and
"ddr5.lrdimm" for properties that would be specific to DDR4
RDIMMs and DDR5 LRDIMMs respectively.

Similarly, there are subdivisions based on the device in
question. For example, "ddr3.mb" and "ddr4.rcd" refer to
properties of the DDR3 memory buffer and the DDR4 registering
clock driver respectively.

"lp" This section describes information that is specific to low
power devices that come from the LPDDR standards.

"channel"
This section describes properties that are specific to the
implementation of a channel. DDR3 and DDR4 DIMMs have a single
channel. DDR5 DIMMs have a single channel that is broken into
two sub-channels. A channel represents a specific data stream
to the host. Most DDR channels are 64-bit channels with
optional ECC. LPDDR devices have rather different channel
widths and their soldered down form factor may have more than
one channel present (regardless of sub-channels).

"module"
This section relates to information about the physical module
which includes information such as its physical dimensions, the
types of components that are present on it, signal mapping, and
related.

"mfg" This section contains information about the module
manufacturing. This is where things like the module's serial
number, the manufacturer, the part number, and related can be
found. Generally this information is found in a different part
of the SPD ROM and therefore may not always be present.

"errors"
This section is an embedded nvlist_t that contains keys for
each value that couldn't be parsed. For more information, see
the Parsing Errors section for more information.

Data Types


The library attempts to use a fixed number of data types when
performing conversions and follows the following guidelines:

+o By default, integer values use a uint32_t to try and make things
more uniform where possible and to allow for future changes. A
uint64_t is used when the types contain data that could naturally
overflow a uint32_t such as when counting the number of bits,
bytes, or measuring time (which is normalized to picoseconds).

All integers are denoted as such explicitly with their size: either
`uint32_t' or `uint64_t'. Some banks of keys all have the same
type and are denoted as such in the introductory block comment.
Use nvlist_lookup_uint32(3NVPAIR) or nvlist_lookup_uint64(3NVPAIR)
to retrieve these keys.

+o Strings, which are stored as traditional char * values should only
contain printable characters.

All strings are denoted as such by using the comment `string'. Use
nvlist_lookup_string(3NVPAIR) to retrieve these keys.

+o There are many values which represent enumerations. The raw type
is stored as a uint32_t. In general, these enumerations, unless
otherwise indicated should not be assumed to have the identical
values of a given specification. This is done because the values
are not always uniform from specification to specification, except
in rare cases. Not all DDR specifications can result in the same
set of enumeration values.

For example, consider the spd_temp_type_t which is an enumeration
that describes the kind of temperature monitoring device present.
The enumeration contains values from DDR3, DDR4, and DDR5; however,
each standard has its own defined type of SPD temperature sensor.

All enumerations are denoted as such by using the comment `uint32_t
(enum)'. Use nvlist_lookup_uint32(3NVPAIR) to retrieve these keys.

+o Several items are used to indicate a fact, but do not have any
actual data associated with them. Their mere presence is
sufficient to indicate a unique property of the DIMM. Examples of
this include if the package is non-monolithic, the DIMM has
asymmetrical ranks, etc.

All such items are denoted as such by using the comment `key'. Use
nvlist_lookup_boolean(3NVPAIR) to retrieve these keys.

+o A few types use arrays of uint32_t values to denote information.
Some of the keys have a fixed number of entries that are expected,
while others are variable and will depend on the parsed data. For
example, a JEDEC ID is always encoded as two uint32_t values, the
continuation and the underlying ID itself. Other values, such as
the number of supported voltages or CAS latencies will vary.

All fixed size arrays denote their size by using the comment
`uint32_t [4]', where the number four is replaced with the actual
size. All variable sized arrays elide the number and are denoted
by the comment `uint32_t []'. Use
nvlist_lookup_uint32_array(3NVPAIR) to retrieve these keys.

A few keys will use different sized integer arrays. For example,
arrays that relate to timing parameters use arrays of uint64_t to
match the broader pattern of using the uint64_t for timing.

Parsing Errors


There are a number of different issues that can arise when trying to
parse the SPD data that a device returns. The library attempts to
parse as much as it can and breaks errors into two large categories:

1. Errors which prevent us from doing anything at all, which are
noted in the ERRORS section. The main items that relate to
data (as opposed to issues like running out of memory) include
when we encounter a DDR specification that we don't support or
that the data has an encoding version we don't understand.
This can also occur if there's not enough data for us to figure
out what kind of SPD information is encoded.

2. Errors that mean we cannot parse a specific piece of SPD data.
This is by far the most common form of error that we encounter
and these are the entries that make up the "errors" section of
the returned nvlist_t that was mentioned earlier.

The "errors" section is a nested nvlist_t that can be retrieved by
using the SPD_KEY_ERRS key. This keys in this nvlist use the same name
as the normal data keys. For example, if we were unable to properly
parse the manufacturer's JEDEC ID, then the key SPD_KEY_MFG_MOD_MFG_ID
("mfg.module-mfg-id") would not be present in the top-level nvlist_t
and would instead be in the error nvlist_t.

Each item present in the error nvlist_t is itself a nvlist_t which
contains the following keys:

SPD_KEY_ERRS_CODE
The error code, a uint32_t, indicates a class of issue that
occurred and its values are defined by the spd_error_kind_t
enumeration. The following errors are defined:

SPD_ERROR_NO_XLATE
This indicates that the library did not know how to
interpret a specific binary value. For example, if a
given particular field was using what we believed to be
a reserved value then we would set this error.

SPD_ERROR_UNPRINT
This indicates that we encountered a non-ASCII or
otherwise unprintable character that was not allowed in
the SPD string character set.

SPD_ERROR_NO_DATA
This indicates that there was no data for a given key.
For example, this would be a string that consisted
solely of space characters which are used to represent
padding.

SPD_ERROR_INTERNAL
This indicates that something went wrong inside the
library that was unexpected.

SPD_ERROR_BAD_DATA
This indicates that we've encountered something strange
about the data in question. For example, this would be
used for an invalid CRC or if the combination of values
would cause an underflow in a calculation.

SPD_KEY_ERRS_MSG
This is an error message that is intended for a person to
understand and contains more specific information than the code
above.

Finally, there is one last top-level key that we will set if we find
that the set of data that we had was incomplete. Rather than tag every
single item in the "errors" nvlist_t, we instead insert the key
SPD_KEY_INCOMPLETE on the top-level nvlist. The key is a uint32_t that
contains the starting offset of the key that we were unable to parse,
which may be less than the total amount of data that was provided. For
example, if we had 100 bytes of data, but there was a 20 byte key
starting at byte 90, the this key would have a value of 90 as that's
what we were unable to parse.

RETURN VALUES


Upon successful completion, the libjedec_spd() function returns an
allocated nvlist_t that contains the parsed SPD data. There may be
individual SPD fields that were unparsable which will be found in the
SPD_KEY_ERRS field of the nvlist still. Otherwise NULL is returned and
errp is set with a value that indicates why the function was unable to
parse any data.

EXAMPLES


Example 1 Printing SPD DRAM and Module Type

The following example shows how to parse the SPD information and print
out the type of DRAM and module's type. This example assumes that one
has already obtained the raw SPD file from somewhere and just prints
the raw values.

#include <stdio.h>
#include <stdint.h>
#include <libjedec.h>

void
dump_dram_module_type(const uint8_t *buf, size_t len)
{
nvlist_t *nvl;
spd_error_t err;
uint32_t ddr, mod;

nvl = libjedec_spd(buf, len, &err);
if (nvl == NULL) {
(void) fprintf(stderr, "failed to parse SPD data: 0x%x\n",
err);
return;
}

if (nvlist_lookup_pairs(nvl, 0,
SPD_KEY_DRAM_TYPE, DATA_TYPE_UINT32, &ddr,
SPD_KEY_MOD_TYPE, DATA_TYPE_UINT32, &mod,
NULL) != 0) {
nvlist_free(nvl);
(void) fprintf(stderr, "failed to look up keys\n");
return;
}

nvlist_free(nvl);
(void) printf("Found DDR type 0x%x, module type 0x%x\n", ddr,
mod);
}

ERRORS


Upon returning from the libjedec_spd() function, the errp pointer will
be set to one of the following values:

LIBJEDEC_SPD_OK
This indicates that the system was able to parse SPD data
into a valid nvlist_t and return it to the caller. This
code should never appear when the function fails and
returns NULL. Callers must still cehck the contents of
the SPD_KEY_ERRS nvlist.

LIBJEDEC_SPD_NOMEM
This indicates that the system ran out of memory while
trying to construct the nvlist_t to return.

LIBJEDEC_SPD_TOOSHORT
This indicates that not enough SPD data was present as
indicated by buf and buflen for the given type of SPD
information and therefore we were unable to successfully
parse basic information.

LIBJEDEC_SPD_UNSUP_TYPE
This indicates that the type of SPD data present, e.g.
DDR4 SDRAM, LPDDR3, etc., that was found is not currently
supported by the library.

LIBJEDEC_SPD_UNSUP_REV
This indicates that while the library is familiar with
the specific type of SPD data present, the encoding level
of the data (similar to a major version) is unsupported
by the library.

INTERFACE STABILITY


Uncommitted

MT-LEVEL
MT-Safe

SEE ALSO


libjedec(3LIB), nvlist_lookup_boolean(3NVPAIR),
nvlist_lookup_string(3NVPAIR), nvlist_lookup_uint32(3NVPAIR),
nvlist_lookup_uint32_array(3NVPAIR), nvlist_lookup_uint64(3NVPAIR)


Serial Presence Detect (SPD), General Standard, 21-C, JEDEC Solid State
Technology Association.

DDR5 Serial Presence Detect (SPD) Contents, JESD400-5B Document Release
1.2, JEDEC Solid State Technology Association, October 2023.

LPDDR5/5X Serial Presence Detect (SPD) Contents, JESD406-5 Document
Release 1.0, JEDEC Solid State Technology Association, June 2024.

illumos September 2, 2024 illumos

tribblix@gmail.com :: GitHub :: Privacy