From a56e6baf477b7f8aaf248e2f764e95e4eec77ea3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 24 Mar 2008 10:22:48 +0000 Subject: [PATCH] Patch from Trent Piepho: This adds a "-x" option to decode-dimms.pl, which lets one supply a list of file names to read SPD data from. It can parse various hexdump formats, such as the output from i2cdump, eeprog, and the util-linux and Busybox hexdump progams run on a sysfs eeprom file. Useful for decoding SPD data that you cut and pasted from a manufacturer's website or from a DIMM installed on an embedded system that does not have perl/etc, but does have a serial console with busybox. git-svn-id: http://lm-sensors.org/svn/i2c-tools/trunk@5157 7894878c-1315-0410-8ee3-d5d059ff63e0 --- CHANGES | 1 + eeprom/decode-dimms.pl | 111 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 90 insertions(+), 22 deletions(-) diff --git a/CHANGES b/CHANGES index 67d3c61..a5d5460 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,7 @@ SVN Update manufacturer IDs Don't print anything by default if checksum fails Decode all DDR2 SDRAM timing information + Add support for reading SPD data from hexdumps decode-vaio: Private data might not be readable by non-root users Print the asset tag Fix the timestamp decoding diff --git a/eeprom/decode-dimms.pl b/eeprom/decode-dimms.pl index a0a2269..ccd3ea4 100755 --- a/eeprom/decode-dimms.pl +++ b/eeprom/decode-dimms.pl @@ -46,8 +46,8 @@ require 5.004; use strict; use POSIX; use Fcntl qw(:DEFAULT :seek); -use vars qw($opt_html $opt_body $opt_bodyonly $opt_igncheck $use_sysfs - @vendors %decode_callback $revision); +use vars qw($opt_html $opt_body $opt_bodyonly $opt_igncheck $use_sysfs $use_hexdump + @vendors %decode_callback $revision @dimm_list); $revision = '$Revision$ ($Date$)'; $revision =~ s/\$\w+: (.*?) \$/$1/g; @@ -1100,10 +1100,61 @@ sub decode_intel_spec_freq($) printl $l, $temp; } +# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog +# note that normal 'hexdump' format on a little-endian system byte-swaps +# words, using hexdump -C is better. +sub read_hexdump ($) +{ + my $addr = 0; + my $repstart = 0; + my @bytes; + my $header = 1; + my $word = 0; + + open F, '<', $_[0] or die "Unable to open: $_[0]"; + while () { + chomp; + if (/^\*$/) { + $repstart = $addr; + next; + } + /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i || + /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i; + next if (!defined $1 && $header); # skip leading unparsed lines + + defined $1 or die "Unable to parse input"; + $header = 0; + + $addr = hex $1; + if ($repstart) { + @bytes[$repstart .. ($addr-1)] = + (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16); + $repstart = 0; + } + last unless defined $2; + foreach (split(/\s+/, $2)) { + if (/^(..)(..)$/) { + $word |= 1; + $bytes[$addr++] = hex($1); + $bytes[$addr++] = hex($2); + } else { + $bytes[$addr++] = hex($_); + } + } + } + close F; + $header and die "Unable to parse any data from hexdump '$_[0]'"; + $word and printc "Warning: Assuming big-endian order 16-bit hex dump"; + return @bytes; +} + sub readspd64 ($$) { # reads 64 bytes from SPD-EEPROM my ($offset, $dimm_i) = @_; my @bytes; - if ($use_sysfs) { + if ($use_hexdump) { + @bytes = read_hexdump($dimm_i); + return @bytes[$offset..($offset+63)]; + } 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"; @@ -1123,19 +1174,30 @@ sub readspd64 ($$) { # reads 64 bytes from SPD-EEPROM } for (@ARGV) { - if (/-h/) { - print "Usage: $0 [-c] [-f [-b]]\n", + if (/^-?-h/) { + print "Usage: $0 [-c] [-f [-b]] [-x file [files..]]\n", " $0 -h\n\n", " -f, --format print nice html output\n", " -b, --bodyonly don't print html header\n", " (useful for postprocessing the output)\n", " -c, --checksum decode completely even if checksum fails\n", + " -x, Read data from hexdump files\n", " -h, --help display this usage summary\n"; + print <<"EOF"; + +Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and +likely many other progams producing hex dumps of one kind or another. Note +that the default output of "hexdump" will be byte-swapped on little-endian +systems and will therefore not be parsed correctly. It is better to use +"hexdump -C", which is not ambiguous. +EOF exit; } - $opt_html = 1 if (/-f/); - $opt_bodyonly = 1 if (/-b/); - $opt_igncheck = 1 if (/-c/); + $opt_html = 1 if (/^-?-f/); + $opt_bodyonly = 1 if (/^-?-b/); + $opt_igncheck = 1 if (/^-?-c/); + $use_hexdump = 1 if (/^-x/); + push @dimm_list, $_ if ($use_hexdump && !/^-/); } $opt_body = $opt_html && ! $opt_bodyonly; @@ -1155,21 +1217,23 @@ Jean Delvare and others'; my $dimm_count = 0; -my @dimm_list; my $dir; -if ($use_sysfs) { $dir = '/sys/bus/i2c/drivers/eeprom'; } -else { $dir = '/proc/sys/dev/sensors'; } -if (-d $dir) { - @dimm_list = split(/\s+/, `ls $dir`); -} elsif (! -d '/sys/module/eeprom') { - print "No EEPROM found, are you sure the eeprom module is loaded?\n"; - exit; +if (!$use_hexdump) { + if ($use_sysfs) { $dir = '/sys/bus/i2c/drivers/eeprom'; } + else { $dir = '/proc/sys/dev/sensors'; } + if (-d $dir) { + @dimm_list = split(/\s+/, `ls $dir`); + } elsif (! -d '/sys/module/eeprom') { + print "No EEPROM found, are you sure the eeprom module is loaded?\n"; + exit; + } } for my $i ( 0 .. $#dimm_list ) { $_ = $dimm_list[$i]; if (($use_sysfs && /^\d+-\d+$/) - || (!$use_sysfs && /^eeprom-/)) { + || (!$use_sysfs && /^eeprom-/) + || $use_hexdump) { my @bytes = readspd64(0, $dimm_list[$i]); my $dimm_checksum = 0; $dimm_checksum += $bytes[$_] foreach (0 .. 62); @@ -1179,15 +1243,18 @@ for my $i ( 0 .. $#dimm_list ) { $dimm_count++; print "" if $opt_html; - printl2 "\n\nDecoding EEPROM", ($use_sysfs ? + printl2 "\n\nDecoding EEPROM", + $use_hexdump ? $dimm_list[$i] : ($use_sysfs ? "/sys/bus/i2c/drivers/eeprom/$dimm_list[$i]" : "/proc/sys/dev/sensors/$dimm_list[$i]"); print "" if $opt_html; print "\n" if $opt_html; - if (($use_sysfs && /^[^-]+-([^-]+)$/) - || (!$use_sysfs && /^[^-]+-[^-]+-[^-]+-([^-]+)$/)) { - my $dimm_num = $1 - 49; - printl "Guessing DIMM is in", "bank $dimm_num"; + if (!$use_hexdump) { + if (($use_sysfs && /^[^-]+-([^-]+)$/) + || (!$use_sysfs && /^[^-]+-[^-]+-[^-]+-([^-]+)$/)) { + my $dimm_num = $1 - 49; + printl "Guessing DIMM is in", "bank $dimm_num"; + } } # Decode first 3 bytes (0-2)