Jean et al,
Attached please find a new set of diffs for decode-dimms. This is a
beginning at addressing the earlier comments.
This set of diffs implements the following changes:
1. Instead of reading the spd data 64 bytes at a time, we calculate
the actual sizes (total and written) of the spd and read the entire
thing, all at once.
1a.This also involved changing all the offsets at the end of the main
loop which were pointing to Manufacturing data, etc.
1b.Also, the main loop is modified to use the returned, calulated sizes
rather than making its own calculations.
2. Remove the checksum calculation from the main loop into a separate
function which returns the actual and calculated checksums as well
as a True/False match status.
3. Create a CRC routine with the same arguments and returns as for
the checksum routine.
4. In the mainline, call the checksum() or CRC() routine as appropriate
for the specific memory type.
5. The cascading if .. elsif .. for fundamental memory types has been
replaced with an indexable list. The list has been updated to
include FB-DIMM, FB-DIMM Probe, and DDR3 SDRAM types.
The following is not yet done; I'll leave these for the next round,
assuming this set of changes comes closer to meeting your approval.
6. Add the routine to actually decode DDR3 data
7. Move the now not-so-common manufacturing data into a separate
function.
Thanks!
----------------------------------------------------------------------
| Paul Goyette | PGP DSS Key fingerprint: | E-mail addresses: |
| Customer Service | FA29 0E3B 35AF E8AE 6651 | paul@xxxxxxxxxxxx |
| Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette@xxxxxxxxxxx |
----------------------------------------------------------------------
--- decode-dimms.orig 2008-11-18 09:15:09.000000000 -0800
+++ decode-dimms.cleanup 2008-12-08 09:44:53.000000000 -0800
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/pkg/bin/perl -w
#
# EEPROM data decoder for SDRAM DIMM modules
#
@@ -1156,30 +1156,70 @@ sub read_hexdump($)
return @bytes;
}
-sub readspd64($$) # reads 64 bytes from SPD-EEPROM
+sub spd_sizes(@) {
+ my @bytes = @_;
+
+ if ($bytes[2] >= 9) {
+ # For FB-DIMM and newer, decode number of bytes written
+ my $spd_len = ($bytes[0] >> 4) & 7;
+ my $size = 64 << ($bytes[0] & 15);
+ if ($spd_len == 0) {
+ return ($size, 128);
+ } elsif ($spd_len == 1) {
+ return ($size, 176);
+ } elsif ($spd_len == 2) {
+ return ($size, 256);
+ } else {
+ return (64, 64);
+ }
+ } else {
+ my $size;
+ if ($bytes[1] <= 14) {
+ $size = 1 << $bytes[1];
+ } elsif ($bytes[1] == 0) {
+ $size = "RFU";
+ } else { $size = "ERROR!" }
+
+ return ($size, ($bytes[0] < 64) ? 64 : $bytes[0]);
+ }
+}
+
+sub readspd($) # reads entire SPD-EEPROM
{
- my ($offset, $dimm_i) = @_;
+ my $dimm_i = $_;
my @bytes;
+ my $written;
+ my $size;
if ($use_hexdump) {
@bytes = read_hexdump($dimm_i);
- return @bytes[$offset..($offset+63)];
+ ($size, $written) = spd_sizes(@bytes);
+ return ($size, $written, @bytes[0..($written - 1)]);
} elsif ($use_sysfs) {
# Kernel 2.6 with sysfs
sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", O_RDONLY)
or die "Cannot open /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
binmode HANDLE;
- sysseek(HANDLE, $offset, SEEK_SET);
- sysread(HANDLE, my $eeprom, 64);
+
+ # Read header bytes to determine spd size
+ sysread(HANDLE, my $eeprom, 3);
+ @bytes = unpack("C64", $eeprom);
+ ($size, $written) = spd_sizes(@bytes);
+
+ # Now (re)read the entire spd
+ sysseek(HANDLE, 0, SEEK_SET);
+ sysread(HANDLE, $eeprom, $written);
close HANDLE;
@bytes = unpack("C64", $eeprom);
} else {
# Kernel 2.4 with procfs
- for my $i (0 .. 3) {
- my $hexoff = sprintf('%02x', $offset + $i * 16);
+ @bytes = split(" ", `cat /proc/sys/dev/sensors/$dimm_i/00`);
+ ($size, $written) = spd_sizes(@bytes);
+ for my $i (1 .. (($written + 15) / 16)) {
+ my $hexoff = sprintf('%02x', $i * 16);
push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
}
}
- return @bytes;
+ return ($size, $written, @bytes);
}
# Parse command-line
@@ -1243,6 +1283,48 @@ if ($opt_html && !$opt_bodyonly) {
"</head><body>\n";
}
+# Calculate and verify checksum of first 63 bytes
+
+sub checksum(@) {
+ my @bytes = @_;
+ my $dimm_checksum = 0;
+ $dimm_checksum += $bytes[$_] foreach (0 .. 62);
+ $dimm_checksum &= 0xff;
+
+ return ("EEPROM Checksum of bytes 0-62",
+ ($bytes[63] == $dimm_checksum) ? 1 : 0,
+ sprintf('0x%02x', $bytes[63]),
+ sprintf('0x%02x', $dimm_checksum));
+}
+
+# Calculate and verify CRC
+
+sub check_crc(@) {
+ my @bytes = @_;
+ my $crc = 0;
+ my $crc_cover = $bytes[0] & 0x80 ? 116 : 125;
+ my $crc_ptr = 0;
+ my $crc_bit;
+ while ($crc_ptr <= $crc_cover) {
+ $crc = $crc ^ ($bytes[$crc_ptr] << 8);
+ for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
+ if ($crc & 0x8000) {
+ $crc = ($crc << 1) ^ 0x1021;
+ } else {
+ $crc = $crc << 1
+ }
+ }
+ $crc_ptr++;
+ }
+ $crc = $crc & 0xffff;
+
+ my $dimm_crc = ($bytes[127] << 8) | $bytes[126];
+ return ("EEPROM CRC of bytes 0-" . sprintf("%d", $crc_cover),
+ ($dimm_crc == $crc) ? 1 : 0,
+ sprintf("0x%04x", $dimm_crc),
+ sprintf("0x%04x", $crc));
+}
+
printc "decode-dimms version $revision";
printh 'Memory Serial Presence Detect Decoder',
'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
@@ -1267,12 +1349,19 @@ for my $i ( 0 .. $#dimm_list ) {
if (($use_sysfs && /^\d+-\d+$/)
|| (!$use_sysfs && /^eeprom-/)
|| $use_hexdump) {
- my @bytes = readspd64(0, $dimm_list[$i]);
- my $dimm_checksum = 0;
- $dimm_checksum += $bytes[$_] foreach (0 .. 62);
- $dimm_checksum &= 0xff;
+ my @bytes = readspd($dimm_list[$i]);
+ my $spd_size = shift(@bytes);
+ my $spd_used = shift(@bytes);
+ my ($chk_type, $chk_valid, $chk_spd, $chk_calc);
+ if ($bytes[2] < 9) {
+ ($chk_type, $chk_valid, $chk_spd, $chk_calc) =
+ checksum(@bytes);
+ } else {
+ ($chk_type, $chk_valid, $chk_spd, $chk_calc) =
+ check_crc(@bytes);
+ }
- next unless $bytes[63] == $dimm_checksum || $opt_igncheck;
+ next unless $chk_valid || $opt_igncheck;
$dimm_count++;
print "<b><u>" if $opt_html;
@@ -1293,11 +1382,10 @@ for my $i ( 0 .. $#dimm_list ) {
# Decode first 3 bytes (0-2)
prints "SPD EEPROM Information";
- my $l = "EEPROM Checksum of bytes 0-62";
- printl $l, ($bytes[63] == $dimm_checksum ?
- sprintf("OK (0x%.2X)", $bytes[63]):
- sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
- $bytes[63], $dimm_checksum));
+ printl $chk_type, ($chk_valid ?
+ sprintf("OK (%s)", $chk_calc) :
+ sprintf("Bad\n(found %s, calculated %s)\n",
+ $chk_spd, $chk_calc));
# Simple heuristic to detect Rambus
my $is_rambus = $bytes[0] < 4;
@@ -1309,31 +1397,29 @@ for my $i ( 0 .. $#dimm_list ) {
else { $temp = "Reserved"; }
printl "SPD Revision", $temp;
} else {
- printl "# of bytes written to SDRAM EEPROM",
- $bytes[0];
+ printl "# of bytes written to SDRAM EEPROM", $spd_used;
}
- $l = "Total number of bytes in EEPROM";
- if ($bytes[1] <= 14) {
- printl $l, 2**$bytes[1];
- } elsif ($bytes[1] == 0) {
- printl $l, "RFU";
- } else { printl $l, "ERROR!"; }
+ my $l = "Total number of bytes in EEPROM";
+ printl $l, $spd_size;
$l = "Fundamental Memory type";
- my $type = "Unknown";
+ my $type;
if ($is_rambus) {
if ($bytes[2] == 1) { $type = "Direct Rambus"; }
elsif ($bytes[2] == 17) { $type = "Rambus"; }
+ else { $type = "Unknown"; }
} else {
- if ($bytes[2] == 1) { $type = "FPM DRAM"; }
- elsif ($bytes[2] == 2) { $type = "EDO"; }
- elsif ($bytes[2] == 3) { $type = "Pipelined Nibble"; }
- elsif ($bytes[2] == 4) { $type = "SDR SDRAM"; }
- elsif ($bytes[2] == 5) { $type = "Multiplexed ROM"; }
- elsif ($bytes[2] == 6) { $type = "DDR SGRAM"; }
- elsif ($bytes[2] == 7) { $type = "DDR SDRAM"; }
- elsif ($bytes[2] == 8) { $type = "DDR2 SDRAM"; }
+ my @type_list = ("Reserved", "FPM DRAM", "EDO",
+ "Pipelined Nibble", "SDR SDRAM",
+ "Multiplexed ROM", "DDR SGRAM", "DDR SDRAM",
+ "DDR2 SDRAM", "FB-DIMM", "FB-DIMM Probe",
+ "DDR3 SDRAM");
+ if ($bytes[2] <= $#type_list) {
+ $type = $type_list[$bytes[2]];
+ } else {
+ $type = sprintf("Unknown (0x%02x)", $bytes[2]);
+ }
}
printl $l, $type;
@@ -1344,61 +1430,59 @@ for my $i ( 0 .. $#dimm_list ) {
# Decode next 35 bytes (64-98, common to all memory types)
prints "Manufacturing Information";
- @bytes = readspd64(64, $dimm_list[$i]);
-
$l = "Manufacturer";
# $extra is a reference to an array containing up to
# 7 extra bytes from the Manufacturer field. Sometimes
# these bytes are filled with interesting data.
- ($temp, my $extra) = manufacturer(@bytes[0..7]);
+ ($temp, my $extra) = manufacturer(@bytes[64..71]);
printl $l, $temp;
$l = "Custom Manufacturer Data";
$temp = manufacturer_data(@{$extra});
printl $l, $temp if defined $temp;
- if (spd_written($bytes[8])) {
+ if (spd_written($bytes[72])) {
# Try the location code as ASCII first, as earlier specifications
# suggested this. As newer specifications don't mention it anymore,
# we still fall back to binary.
$l = "Manufacturing Location Code";
- $temp = (chr($bytes[8]) =~ m/^[\w\d]$/) ? chr($bytes[8])
- : sprintf("0x%.2X", $bytes[8]);
+ $temp = (chr($bytes[72]) =~ m/^[\w\d]$/) ? chr($bytes[72])
+ : sprintf("0x%.2X", $bytes[72]);
printl $l, $temp;
}
$l = "Part Number";
- $temp = part_number(@bytes[9..26]);
+ $temp = part_number(@bytes[73..90]);
printl $l, $temp;
- if (spd_written(@bytes[27..28])) {
+ if (spd_written(@bytes[91..92])) {
$l = "Revision Code";
- $temp = sprintf("0x%02X%02X\n", @bytes[27..28]);
+ $temp = sprintf("0x%02X%02X\n", @bytes[91..92]);
printl $l, $temp;
}
- if (spd_written(@bytes[29..30])) {
+ if (spd_written(@bytes[93..94])) {
$l = "Manufacturing Date";
# In theory the year and week are in BCD format, but
# this is not always true in practice :(
- if (($bytes[29] & 0xf0) <= 0x90
- && ($bytes[29] & 0x0f) <= 0x09
- && ($bytes[30] & 0xf0) <= 0x90
- && ($bytes[30] & 0x0f) <= 0x09) {
+ if (($bytes[93] & 0xf0) <= 0x90
+ && ($bytes[93] & 0x0f) <= 0x09
+ && ($bytes[94] & 0xf0) <= 0x90
+ && ($bytes[94] & 0x0f) <= 0x09) {
# Note that this heuristic will break in year 2080
$temp = sprintf("%d%02X-W%02X\n",
- $bytes[29] >= 0x80 ? 19 : 20,
- @bytes[29..30]);
+ $bytes[93] >= 0x80 ? 19 : 20,
+ @bytes[93..94]);
} else {
$temp = sprintf("0x%02X%02X\n",
- @bytes[29..30]);
+ @bytes[93..94]);
}
printl $l, $temp;
}
- if (spd_written(@bytes[31..34])) {
+ if (spd_written(@bytes[95..98])) {
$l = "Assembly Serial Number";
$temp = sprintf("0x%02X%02X%02X%02X\n",
- @bytes[31..34]);
+ @bytes[95..98]);
printl $l, $temp;
}