(Sent to this list under Jean Delvare's suggestion.) Dear list readers,Attached are three patches to the decode-dimms tool that I've found to be rather useful.
First, there are several places where the printl_cond() function should be used to display some manufacturing data. With existing code, if only some DIMMs have this data, the side-by-side feature will be disabled. This patch completes the implementation of printl_cond().
Second, when decoding more than two DIMMs, it might well be useful to have the side-by-side merge-cells work even if only some of the DIMMs have equal values. This improves the display when, for example, you have two pairs of DIMMs. Even though the DIMMs in each pair are the same, the existing code will not merge cells unless all four DIMMs are the same. This patch allows merging of the partial rows.
The third patch is useful to expand the OS base where decode-dimms is useful. This patch "teaches" decode-dimms how to retrieve SPD EEPROM data from a NetBSD sysctl(8) node. Without this, the only way to use decode-dimms on NetBSD is to manually extract the sysctl(8) entries, run them through hexdump, supply hexdump's output file(s) as input to decode-dimms with -x, and then clean up (delete) the hexdump output!
(I'd love to figure out a way to reliably detect that the script is being run on a NetBSD system, and then default to using sysctl rather than sysfs, but even then there's no current way to "scan" the sysctl tree for spdmem nodes.)
All three of these patches are relative to the most recent version of decode-dimms that I can find:
http://lm-sensors.org/browser/i2c-tools/trunk/eeprom/decode-dimms?rev=5714 ------------------------------------------------------------------------- | Paul Goyette | PGP DSS Key fingerprint: | E-mail addresses: | | Customer Service | FA29 0E3B 35AF E8AE 6651 | paul at whooppee.com | | Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette at juniper.net | | Kernel Developer | | pgoyette at netbsd.org | -------------------------------------------------------------------------
--- decode-dimms-5714 2009-05-23 14:02:10.000000000 -0700 +++ my-decode-dimms 2009-05-24 07:02:48.000000000 -0700 @@ -1373,26 +1388,20 @@ printl("Module Manufacturer", manufacturer_ddr3($bytes->[117], $bytes->[118])); - if (spd_written(@{$bytes}[148..149])) { - printl("DRAM Manufacturer", - manufacturer_ddr3($bytes->[148], $bytes->[149])); - } + printl_cond(spd_written(@{$bytes}[148..149]), "DRAM Manufacturer", + manufacturer_ddr3($bytes->[148], $bytes->[149])); printl_mfg_location_code($bytes->[119]); - if (spd_written(@{$bytes}[120..121])) { - printl("Manufacturing Date", - manufacture_date($bytes->[120], $bytes->[121])); - } + printl_cond(spd_written(@{$bytes}[120..121]), "Manufacturing Date", + manufacture_date($bytes->[120], $bytes->[121])); printl_mfg_assembly_serial(@{$bytes}[122..125]); printl("Part Number", part_number(@{$bytes}[128..145])); - if (spd_written(@{$bytes}[146..147])) { - printl("Revision Code", - sprintf("0x%02X%02X", $bytes->[146], $bytes->[147])); - } + printl_cond(spd_written(@{$bytes}[146..147]), "Revision Code", + sprintf("0x%02X%02X", $bytes->[146], $bytes->[147])); } # Parameter: EEPROM bytes 0-127 (using 64-98)
--- decode-dimms-5714 2009-05-23 14:02:10.000000000 -0700 +++ my-decode-dimms 2009-05-24 07:02:48.000000000 -0700 @@ -381,10 +381,25 @@ $label = html_encode($label); @values = map { html_encode($_) } @values; print "<tr><td valign=top>$label</td>"; - if ($opt_merge && $same_values) { + if (!$opt_merge) { + print "<td>$_</td>" foreach @values; + } elsif ($same_values) { print "<td colspan=".(scalar @values).">$values[0]</td>"; } else { - print "<td>$_</td>" foreach @values; + # For HTML output, merge adjacent cells even if + # the whole line cannot be merged. + my $colcnt = 0; + my $value; + while (@values) { + $colcnt++; + $value = shift @values; + next if (@values && $value eq $values[0]); + print "<td colspan=" . $colcnt . ">$value</td>"; + $colcnt = 0; + } + if ($colcnt) { + print "<td colspan=" . $colcnt . ">$value</td>"; + } } print "</tr>\n"; } else {
--- decode-dimms-5714.orig 2009-05-23 12:49:26.000000000 -0700 +++ decode-dimms-5714.sysctl 2009-05-23 14:02:10.000000000 -0700 @@ -41,8 +41,9 @@ use POSIX qw(ceil); use Fcntl qw(:DEFAULT :seek); use vars qw($opt_html $opt_bodyonly $opt_side_by_side $opt_merge - $opt_igncheck $use_sysfs $use_hexdump $sbs_col_width - @vendors %decode_callback $revision @dimm $current %hexdump_cache); + $opt_igncheck $use_sysfs $use_hexdump $use_sysctl $sbs_col_width + @vendors %decode_callback $revision @dimm $current %hexdump_cache + %sysctl_cache); use constant LITTLEENDIAN => "little-endian"; use constant BIGENDIAN => "big-endian"; @@ -253,6 +254,7 @@ "SiliconBlue Technologies", "Rambus Inc."]); $use_sysfs = -d '/sys/bus'; +$use_sysctl = 0; # We consider that no data was written to this area of the SPD EEPROM if # all bytes read 0x00 or all bytes read 0xff @@ -1543,6 +1545,26 @@ } } +# read data from a NetBSD (or equivalent) sysctl variable + +sub read_sysctl($) +{ + + # Look in the cache first + return @{$sysctl_cache{$_[0]}} if exists $sysctl_cache{$_[0]}; + + my $sysctl_var = sprintf("hw.%s.spd_data", $_[0]); + open(PIPE, "-|", "sysctl -r $sysctl_var") + or die "Cannot read sysctl variable $sysctl_var"; + sysread(PIPE, my $eeprom, 512); # XXX Assumed maximum size! XXX + close PIPE or die "sysctl returned $?"; + my @bytes = unpack("C*", $eeprom); + + # Cache the data for later use + $hexdump_cache{$_[0]} = \@bytes; + return @bytes; +} + # Read bytes from SPD-EEPROM # Note: offset must be a multiple of 16! sub readspd($$$) @@ -1552,6 +1574,9 @@ if ($use_hexdump) { @bytes = read_hexdump($dimm_i); return @bytes[$offset..($offset + $size - 1)]; + } elsif ($use_sysctl) { + @bytes = read_sysctl($dimm_i); + return @bytes[$offset..($offset + $size - 1)]; } elsif ($use_sysfs) { # Kernel 2.6 with sysfs sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY) @@ -1621,7 +1646,7 @@ # Parse command-line foreach (@ARGV) { if ($_ eq '-h' || $_ eq '--help') { - print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n", + print "Usage: $0 [-c] [-f [-b]] [-x|-X|-s file [files..]]\n", " $0 -h\n\n", " -f, --format Print nice html output\n", " -b, --bodyonly Don't print html header\n", @@ -1633,6 +1658,8 @@ " -x, Read data from hexdump files\n", " -X, Same as -x except treat multibyte hex\n", " data as little endian\n", + " -s, Use NetBSD-compatible sysctl(8) to obtain\n", + " EEPROM data\n", " -h, --help Display this usage summary\n"; print <<"EOF"; @@ -1673,13 +1700,20 @@ $use_hexdump = LITTLEENDIAN; next; } + if ($_ eq '-s') { + if (-x "/sbin/sysctl") { + $use_sysctl = 1; + } else { die "No /sbin/sysctl available for -s"; } + next; + } if (m/^-/) { print STDERR "Unrecognized option $_\n"; exit; } - push @dimm, { eeprom => $_, file => $_ } if $use_hexdump; + push @dimm, { eeprom => $_, file => $_ } + if ($use_sysctl || $use_hexdump); } if ($opt_html && !$opt_bodyonly) { @@ -1732,7 +1766,7 @@ # * chk_spd: The checksum or CRC value found in the EEPROM # * chk_calc: The checksum or CRC computed from the EEPROM data # Keys are added over time. -@dimm = get_dimm_list() unless $use_hexdump; +@dimm = get_dimm_list() unless ($use_sysctl || $use_hexdump); for my $i (0 .. $#dimm) { my @bytes = readspd(0, 128, $dimm[$i]->{file}); @@ -1768,7 +1802,7 @@ printl("Decoding EEPROM", $dimm[$current]->{eeprom}); } - if (!$use_hexdump) { + if (!$use_hexdump && !$use_sysctl) { if ($dimm[$current]->{file} =~ /-([\da-f]+)$/i) { my $dimm_num = hex($1) - 0x50 + 1; if ($dimm_num >= 1 && $dimm_num <= 8) {