$NetBSD: patch-decode-dimms,v 1.19 2021/10/28 20:18:21 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	2021-07-22 04:10:34.000000000 -0700
+++ eeprom/decode-dimms	2021-10-28 13:01:32.530284359 -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
@@ -2437,6 +2438,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($$$)
@@ -2450,20 +2471,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) {
@@ -2529,7 +2543,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",
@@ -2543,6 +2557,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";
 
@@ -2587,85 +2603,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 = <FILE>;
-	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
@@ -2678,7 +2634,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});
@@ -2730,7 +2685,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) {
