$NetBSD: patch-decode-dimms,v 1.18 2020/09/07 14:50:25 pgoyette Exp $ Add NetBSD-specific ability to use spdmem(4)'s sysctl values as input. Remove attempts to use linux-specific data sources. --- eeprom/decode-dimms.orig 2020-09-07 07:41:27.782730720 -0700 +++ eeprom/decode-dimms 2020-09-07 07:42:58.255264706 -0700 @@ -45,8 +45,9 @@ use Fcntl qw(:DEFAULT :seek); use File::Basename; 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 @dimm $current %hexdump_cache); + $opt_igncheck $use_sysctl $use_hexdump $sbs_col_width + @vendors %decode_callback @dimm $current %hexdump_cache + %sysctl_cache); use constant LITTLEENDIAN => "little-endian"; use constant BIGENDIAN => "big-endian"; @@ -497,7 +498,7 @@ die "Unexpected number of vendor names in page $page" unless @{$vendors[$page]} == 126; } -$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 @@ -2436,6 +2437,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 + $sysctl_cache{$_[0]} = \@bytes; + return @bytes; +} + # Read bytes from SPD-EEPROM # Note: offset must be a multiple of 16! sub readspd($$$) @@ -2449,20 +2470,13 @@ $size = @bytes - $offset; } return @bytes[$offset..($offset + $size - 1)]; - } elsif ($use_sysfs) { - # Kernel 2.6 with sysfs - sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY) - or die "Cannot open $dimm_i/eeprom"; - binmode HANDLE; - sysseek(HANDLE, $offset, SEEK_SET) - or die "Cannot seek $dimm_i/eeprom"; - $read = sysread(HANDLE, my $eeprom, $size) - or die "Cannot read $dimm_i/eeprom"; - close HANDLE; - if ($read < $size) { - print STDERR "WARNING: $dimm_i/eeprom is smaller than expected\n"; + } elsif ($use_sysctl) { + @bytes = read_sysctl($dimm_i); + if (@bytes < $offset + $size) { + print STDERR "WARNING: sysctl for $dimm_i is truncated\n"; + $size = @bytes - $offset; } - @bytes = unpack("C*", $eeprom); + return return @bytes[$offset..($offset + $size - 1)]; } else { # Kernel 2.4 with procfs for my $i (0 .. ($size-1)/16) { @@ -2528,7 +2542,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", @@ -2542,6 +2556,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"; @@ -2586,85 +2602,25 @@ $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 => basename($_), file => $_ } if $use_hexdump; + push @dimm, { eeprom => basename($_), file => $_ } + if ($use_sysctl || $use_hexdump); } # Default values $opt_merge = 1 unless defined $opt_merge; -# From a sysfs device path and an attribute name, return the attribute -# value, or undef (stolen from sensors-detect) -sub sysfs_device_attribute -{ - my ($device, $attr) = @_; - my $value; - - open(local *FILE, "$device/$attr") or return ""; - $value = ; - close(FILE); - return unless defined $value; - - chomp($value); - return $value; -} - -sub get_dimm_list -{ - my (@drivers, $driver, $dir, $opened, $file, @files); - - if ($use_sysfs) { - @drivers = ('eeprom', - 'at24', - 'ee1004'); # DDR4 - } else { - @drivers = ('eeprom'); - $dir = '/proc/sys/dev/sensors'; - } - - foreach $driver (@drivers) { - if ($use_sysfs) { - $dir = "/sys/bus/i2c/drivers/$driver"; - } - - next unless opendir(local *DIR, $dir); - $opened++; - while (defined($file = readdir(DIR))) { - if ($use_sysfs) { - # We look for I2C devices like 0-0050 or 2-0051 - next unless $file =~ /^\d+-[\da-f]+$/i; - next unless -d "$dir/$file"; - - # Device name must be eeprom (driver eeprom) - # spd (driver at24) or ee1004 (driver ee1004) - my $attr = sysfs_device_attribute("$dir/$file", "name"); - next unless defined $attr && - ($attr eq "eeprom" || - $attr eq "spd" || - $attr eq "ee1004"); # DDR4 - } else { - next unless $file =~ /^eeprom-/; - } - push @files, { eeprom => "$file", - file => "$dir/$file", - driver => "$driver" }; - } - close(DIR); - } - - if (!$opened) { - print STDERR "No EEPROM found, try loading the eeprom, at24 or ee1004 module\n"; - exit; - } - - return sort { $a->{file} cmp $b->{file} } @files; -} - # @dimm is a list of hashes. There's one hash for each EEPROM we found. # Each hash has the following keys: # * eeprom: Name of the eeprom data file @@ -2677,7 +2633,6 @@ # * 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; for my $i (0 .. $#dimm) { my @bytes = readspd(0, 128, $dimm[$i]->{file}); @@ -2729,7 +2684,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) {