@@ -2656,6 +2656,108 @@ sub decode_ddr5_mfg_data($)
}
}
+# Parameter: EEPROM bytes 0-1023 (using 640-1023)
+sub decode_ddr5_error_data($)
+{
+ my $bytes = shift;
+
+ # Zero or more error logs may appear anywhere in any End User Programmable blocks of the SPD,
+ # including over SPD Block boundaries. They may be found by searching for a four byte anchor string.
+
+ my $errors = [];
+
+ my $size = scalar @{$bytes} < 1023 ? scalar @{$bytes} : 1023;
+
+ for (my $ii = 0; $ii < $size - 640 - 23; $ii++) {
+ if (join('', @{$bytes}[640 + $ii .. 640 + $ii + 3]) eq "95707695") {
+ push @{$errors}, [@{$bytes}[640 + $ii .. 640 + $ii + 23]];
+ $ii += 23;
+ }
+ }
+
+ if (@{$errors} == 0) {
+ # No error logs found
+ return;
+ }
+
+ prints("Error Log");
+
+ printl("Error Log Count", scalar @{$errors});
+
+ for (my $ii = 0; $ii < scalar @{$errors}; $ii++) {
+ my $error = @{$errors}[$ii];
+
+ # error location
+ printl_cond($error->[4] & (1 << 0), "Error $ii Type", "DRAM Uncorrectable Error");
+ printl_cond($error->[4] & (1 << 1), "Error $ii Type", "DRAM Correctable Error");
+ printl_cond($error->[4] & (1 << 2), "Error $ii Type", "DRAM ECS Error");
+ printl_cond($error->[4] & (1 << 3), "Error $ii Type", "hPPR Was Required");
+ printl_cond($error->[4] & (1 << 4), "Error $ii Type", "hPPR Resource Error");
+
+ printl("Error $ii Location CPU", ($error->[5] >> 3) & 0x07);
+ printl("Error $ii Location CPUMC", (($error->[5] & 3) << 2) | ($error->[6] >> 6));
+ printl("Error $ii Location DIMM", ($error->[6] >> 4) & 0x01);
+
+ # these are active low
+ printl_cond(~$error->[6] & (1 << 3), "Error $ii Location Rank", "0 (sub-channel A)");
+ printl_cond(~$error->[6] & (1 << 2), "Error $ii Location Rank", "1 (sub-channel A)");
+ printl_cond(~$error->[6] & (1 << 1), "Error $ii Location Rank", "0 (sub-channel B)");
+ printl_cond(~$error->[6] & (1 << 0), "Error $ii Location Rank", "1 (sub-channel B)");
+
+ printl("Error $ii Location Parity", ($error->[7] >> 6) & 0x01);
+ if (($error->[7] >> 5) & 1) {
+ # chip identifier?
+ printl("Error $ii Location Chip", ($error->[7] >> 2) & 0x07);
+ } else {
+ # row address?
+ printl("Error $ii Location Bank Group", (($error->[7] & 0x03) << 1) | (($error->[8] & 0x80) >> 7));
+ printl("Error $ii Location Bank Address", ($error->[8] >> 5) & 0x03);
+ printl("Error $ii Location Row Address", (($error->[8] & 0x1f) << 12) | ($error->[9] << 4) | ($error->[10] >> 4));
+ printl("Error $ii Location Column Address", (($error->[10] & 0x0f) << 7) | (($error->[11] & 0xf0) >> 1));
+ }
+
+ # also active low
+ printl_cond(~$error->[11] & (1 << 0), "Error $ii Location Device", "DQS6A");
+ printl_cond(~$error->[11] & (1 << 1), "Error $ii Location Device", "DQS7A");
+ printl_cond(~$error->[11] & (1 << 2), "Error $ii Location Device", "DQS8A");
+ printl_cond(~$error->[11] & (1 << 3), "Error $ii Location Device", "DQS9A");
+
+ printl_cond(~$error->[12] & (1 << 0), "Error $ii Location Device", "DQS8B");
+ printl_cond(~$error->[12] & (1 << 1), "Error $ii Location Device", "DQS9B");
+ printl_cond(~$error->[12] & (1 << 2), "Error $ii Location Device", "DQS0A");
+ printl_cond(~$error->[12] & (1 << 3), "Error $ii Location Device", "DQS1A");
+ printl_cond(~$error->[12] & (1 << 4), "Error $ii Location Device", "DQS2A");
+ printl_cond(~$error->[12] & (1 << 5), "Error $ii Location Device", "DQS3A");
+ printl_cond(~$error->[12] & (1 << 6), "Error $ii Location Device", "DQS4A");
+ printl_cond(~$error->[12] & (1 << 7), "Error $ii Location Device", "DQS5A");
+
+ printl_cond(~$error->[12] & (1 << 0), "Error $ii Location Device", "DQS0B");
+ printl_cond(~$error->[12] & (1 << 1), "Error $ii Location Device", "DQS1B");
+ printl_cond(~$error->[12] & (1 << 2), "Error $ii Location Device", "DQS2B");
+ printl_cond(~$error->[12] & (1 << 3), "Error $ii Location Device", "DQS3B");
+ printl_cond(~$error->[12] & (1 << 4), "Error $ii Location Device", "DQS4B");
+ printl_cond(~$error->[12] & (1 << 5), "Error $ii Location Device", "DQS5B");
+ printl_cond(~$error->[12] & (1 << 6), "Error $ii Location Device", "DQS6B");
+ printl_cond(~$error->[12] & (1 << 7), "Error $ii Location Device", "DQS7B");
+
+ # timestamp
+ my $year = ($error->[14] >> 2) + 2020;
+ my $month = (($error->[14] & 0x03) << 2) | ($error->[15] >> 6);
+ my $day = ($error->[15] & 0x3e) >> 1;
+ my $hour = (($error->[15] & 0x01) << 4) | ($error->[16] >> 4);
+ my $minute = (($error->[16] & 0x0f) << 2) | ($error->[17] >> 6);
+ my $second = $error->[17] & 0x3f;
+ printl("Error $ii Timestamp", sprintf("%04d-%02d-%02d %02d:%02d:%02d",
+ $year, $month, $day, $hour, $minute, $second));
+
+ # DRAM refresh settings
+ # TODO
+
+ # measured temperature
+ # TODO
+ }
+}
+
# Parameter: EEPROM bytes 0-127 (using 64-98)
sub decode_manufacturing_information($)
{
@@ -3215,6 +3317,9 @@ for $current (0 .. $#dimm) {
# Decode DDR5-specific manufacturing data in bytes
# 512-639
decode_ddr5_mfg_data(\@bytes);
+ # Decode DDR5-specific error log
+ # 640-1023 (max)
+ decode_ddr5_error_data(\@bytes);
}
} else {
# Decode next 35 bytes (64-98, common to most