$NetBSD: patch-decode-dimms,v 1.10 2019/01/17 05:41:10 pgoyette Exp $

Add NetBSD-specific ability to use spdmem(4)'s sysctl values as
input.

--- eeprom/decode-dimms.orig	2019-01-17 13:29:22.361611073 +0800
+++ eeprom/decode-dimms	2019-01-17 13:35:48.518437248 +0800
@@ -42,8 +42,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 $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";
@@ -417,6 +418,7 @@
 );
 
 $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
@@ -2335,6 +2337,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($$$)
@@ -2344,6 +2366,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)
@@ -2420,7 +2445,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",
@@ -2434,6 +2459,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";
 
@@ -2478,13 +2505,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 => basename($_), file => $_ } if $use_hexdump;
+	push @dimm, { eeprom => basename($_), file => $_ }
+		if ($use_sysctl || $use_hexdump);
 }
 
 # Default values
@@ -2562,7 +2596,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});
@@ -2614,7 +2648,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) {
