Re: Preliminary support for DDR3 in decode-dimms (fwd)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jean et al,

Attached please find a new set of diffs for decode-dimms. This is a beginning at addressing the earlier comments.

This set of diffs implements the following changes:

1. Instead of reading the spd data 64 bytes at a time, we calculate
   the actual sizes (total and written) of the spd and read the entire
   thing, all at once.

1a.This also involved changing all the offsets at the end of the main
   loop which were pointing to Manufacturing data, etc.

1b.Also, the main loop is modified to use the returned, calulated sizes
   rather than making its own calculations.

2. Remove the checksum calculation from the main loop into a separate
   function which returns the actual and calculated checksums as well
   as a True/False match status.

3. Create a CRC routine with the same arguments and returns as for
   the checksum routine.

4. In the mainline, call the checksum() or CRC() routine as appropriate
   for the specific memory type.

5. The cascading if .. elsif .. for fundamental memory types has been
   replaced with an indexable list.  The list has been updated to
   include FB-DIMM, FB-DIMM Probe, and DDR3 SDRAM types.


The following is not yet done; I'll leave these for the next round, assuming this set of changes comes closer to meeting your approval.

6. Add the routine to actually decode DDR3 data

7. Move the now not-so-common manufacturing data into a separate
   function.


Thanks!


----------------------------------------------------------------------
|   Paul Goyette   | PGP DSS Key fingerprint: |  E-mail addresses:   |
| Customer Service | FA29 0E3B 35AF E8AE 6651 |  paul@xxxxxxxxxxxx   |
| Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette@xxxxxxxxxxx |
----------------------------------------------------------------------
--- decode-dimms.orig	2008-11-18 09:15:09.000000000 -0800
+++ decode-dimms.cleanup	2008-12-08 09:44:53.000000000 -0800
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/pkg/bin/perl -w
 #
 # EEPROM data decoder for SDRAM DIMM modules
 #
@@ -1156,30 +1156,70 @@ sub read_hexdump($)
 	return @bytes;
 }
 
-sub readspd64($$) # reads 64 bytes from SPD-EEPROM
+sub spd_sizes(@) {
+	my @bytes = @_;
+
+	if ($bytes[2] >= 9) {
+		# For FB-DIMM and newer, decode number of bytes written
+		my $spd_len = ($bytes[0] >> 4) & 7;
+		my $size = 64 << ($bytes[0] & 15);
+		if ($spd_len == 0) {
+			return ($size, 128);
+		} elsif ($spd_len == 1) {
+			return ($size, 176);
+		} elsif ($spd_len == 2) {
+			return ($size, 256);
+		} else {
+			return (64, 64);
+		}
+        } else {
+		my $size;
+		if ($bytes[1] <= 14) {
+			$size = 1 << $bytes[1];
+		} elsif ($bytes[1] == 0) {
+			$size = "RFU";
+		} else { $size = "ERROR!" }
+
+		return ($size, ($bytes[0] < 64) ? 64 : $bytes[0]);
+        }
+}
+
+sub readspd($) # reads entire SPD-EEPROM
 {
-	my ($offset, $dimm_i) = @_;
+	my $dimm_i = $_;
 	my @bytes;
+	my $written;
+	my $size;
 	if ($use_hexdump) {
 		@bytes = read_hexdump($dimm_i);
-		return @bytes[$offset..($offset+63)];
+		($size, $written) = spd_sizes(@bytes);
+		return ($size, $written, @bytes[0..($written - 1)]);
 	} 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";
 		binmode HANDLE;
-		sysseek(HANDLE, $offset, SEEK_SET);
-		sysread(HANDLE, my $eeprom, 64);
+
+		# Read header bytes to determine spd size
+		sysread(HANDLE, my $eeprom, 3);
+		@bytes = unpack("C64", $eeprom);
+		($size, $written) = spd_sizes(@bytes);
+
+		# Now (re)read the entire spd
+		sysseek(HANDLE, 0, SEEK_SET);
+		sysread(HANDLE, $eeprom, $written);
 		close HANDLE;
 		@bytes = unpack("C64", $eeprom);
 	} else {
 		# Kernel 2.4 with procfs
-		for my $i (0 .. 3) {
-			my $hexoff = sprintf('%02x', $offset + $i * 16);
+		@bytes = split(" ", `cat /proc/sys/dev/sensors/$dimm_i/00`);
+		($size, $written) = spd_sizes(@bytes);
+		for my $i (1 .. (($written + 15) / 16)) {
+			my $hexoff = sprintf('%02x', $i * 16);
 			push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
 		}
 	}
-	return @bytes;
+	return ($size, $written, @bytes);
 }
 
 # Parse command-line
@@ -1243,6 +1283,48 @@ if ($opt_html && !$opt_bodyonly) {
 		  "</head><body>\n";
 }
 
+# Calculate and verify checksum of first 63 bytes
+
+sub checksum(@) {
+	my @bytes = @_;
+	my $dimm_checksum = 0;
+	$dimm_checksum += $bytes[$_] foreach (0 .. 62);
+	$dimm_checksum &= 0xff;
+
+	return ("EEPROM Checksum of bytes 0-62",
+		($bytes[63] == $dimm_checksum) ? 1 : 0,
+		sprintf('0x%02x', $bytes[63]),
+		sprintf('0x%02x', $dimm_checksum));
+}
+
+# Calculate and verify CRC
+
+sub check_crc(@) {
+	my @bytes = @_;
+	my $crc = 0;
+	my $crc_cover = $bytes[0] & 0x80 ? 116 : 125;
+	my $crc_ptr = 0;
+	my $crc_bit;
+	while ($crc_ptr <= $crc_cover) {
+		$crc = $crc ^ ($bytes[$crc_ptr] << 8);
+		for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
+			if ($crc & 0x8000) {
+				$crc = ($crc << 1) ^ 0x1021;
+			} else {
+				$crc = $crc << 1
+			}
+		}
+		$crc_ptr++;
+	}
+	$crc = $crc & 0xffff;
+
+	my $dimm_crc = ($bytes[127] << 8) | $bytes[126];
+	return ("EEPROM CRC of bytes 0-" . sprintf("%d", $crc_cover),
+		($dimm_crc == $crc) ? 1 : 0,
+		sprintf("0x%04x", $dimm_crc),
+		sprintf("0x%04x", $crc));
+}
+
 printc "decode-dimms version $revision";
 printh 'Memory Serial Presence Detect Decoder',
 'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
@@ -1267,12 +1349,19 @@ for my $i ( 0 .. $#dimm_list ) {
 	if (($use_sysfs && /^\d+-\d+$/)
 	 || (!$use_sysfs && /^eeprom-/)
 	 || $use_hexdump) {
-		my @bytes = readspd64(0, $dimm_list[$i]);
-		my $dimm_checksum = 0;
-		$dimm_checksum += $bytes[$_] foreach (0 .. 62);
-		$dimm_checksum &= 0xff;
+		my @bytes = readspd($dimm_list[$i]);
+		my $spd_size = shift(@bytes);
+		my $spd_used = shift(@bytes);
+		my ($chk_type, $chk_valid, $chk_spd, $chk_calc);
+		if ($bytes[2] < 9) {
+			($chk_type, $chk_valid, $chk_spd, $chk_calc) =
+				checksum(@bytes);
+		} else {
+			($chk_type, $chk_valid, $chk_spd, $chk_calc) =
+				check_crc(@bytes);
+		}
 
-		next unless $bytes[63] == $dimm_checksum || $opt_igncheck;
+		next unless $chk_valid || $opt_igncheck;
 		$dimm_count++;
 
 		print "<b><u>" if $opt_html;
@@ -1293,11 +1382,10 @@ for my $i ( 0 .. $#dimm_list ) {
 # Decode first 3 bytes (0-2)
 		prints "SPD EEPROM Information";
 
-		my $l = "EEPROM Checksum of bytes 0-62";
-		printl $l, ($bytes[63] == $dimm_checksum ?
-			sprintf("OK (0x%.2X)", $bytes[63]):
-			sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
-				$bytes[63], $dimm_checksum));
+		printl $chk_type, ($chk_valid ?
+			sprintf("OK (%s)", $chk_calc) :
+			sprintf("Bad\n(found %s, calculated %s)\n",
+				$chk_spd, $chk_calc));
 
 		# Simple heuristic to detect Rambus
 		my $is_rambus = $bytes[0] < 4;
@@ -1309,31 +1397,29 @@ for my $i ( 0 .. $#dimm_list ) {
 			else { $temp = "Reserved"; }
 			printl "SPD Revision", $temp;
 		} else {
-			printl "# of bytes written to SDRAM EEPROM",
-			       $bytes[0];
+			printl "# of bytes written to SDRAM EEPROM", $spd_used;
 		}
 
-		$l = "Total number of bytes in EEPROM";
-		if ($bytes[1] <= 14) {
-			printl $l, 2**$bytes[1];
-		} elsif ($bytes[1] == 0) {
-			printl $l, "RFU";
-		} else { printl $l, "ERROR!"; }
+		my $l = "Total number of bytes in EEPROM";
+		printl $l, $spd_size;
 
 		$l = "Fundamental Memory type";
-		my $type = "Unknown";
+		my $type;
 		if ($is_rambus) {
 			if ($bytes[2] == 1) { $type = "Direct Rambus"; }
 			elsif ($bytes[2] == 17) { $type = "Rambus"; }
+			else { $type = "Unknown"; }
 		} else {
-			if ($bytes[2] == 1) { $type = "FPM DRAM"; }
-			elsif ($bytes[2] == 2) { $type = "EDO"; }
-			elsif ($bytes[2] == 3) { $type = "Pipelined Nibble"; }
-			elsif ($bytes[2] == 4) { $type = "SDR SDRAM"; }
-			elsif ($bytes[2] == 5) { $type = "Multiplexed ROM"; }
-			elsif ($bytes[2] == 6) { $type = "DDR SGRAM"; }
-			elsif ($bytes[2] == 7) { $type = "DDR SDRAM"; }
-			elsif ($bytes[2] == 8) { $type = "DDR2 SDRAM"; }
+			my @type_list = ("Reserved", "FPM DRAM", "EDO",
+				"Pipelined Nibble", "SDR SDRAM",
+				"Multiplexed ROM", "DDR SGRAM", "DDR SDRAM",
+				"DDR2 SDRAM", "FB-DIMM", "FB-DIMM Probe",
+				"DDR3 SDRAM");
+			if ($bytes[2] <= $#type_list) {
+				$type = $type_list[$bytes[2]];
+			} else {
+				$type = sprintf("Unknown (0x%02x)", $bytes[2]);
+			}
 		}
 		printl $l, $type;
 
@@ -1344,61 +1430,59 @@ for my $i ( 0 .. $#dimm_list ) {
 # Decode next 35 bytes (64-98, common to all memory types)
 		prints "Manufacturing Information";
 
-		@bytes = readspd64(64, $dimm_list[$i]);
-
 		$l = "Manufacturer";
 		# $extra is a reference to an array containing up to
 		# 7 extra bytes from the Manufacturer field. Sometimes
 		# these bytes are filled with interesting data.
-		($temp, my $extra) = manufacturer(@bytes[0..7]);
+		($temp, my $extra) = manufacturer(@bytes[64..71]);
 		printl $l, $temp;
 		$l = "Custom Manufacturer Data";
 		$temp = manufacturer_data(@{$extra});
 		printl $l, $temp if defined $temp;
 
-		if (spd_written($bytes[8])) {
+		if (spd_written($bytes[72])) {
 			# Try the location code as ASCII first, as earlier specifications
 			# suggested this. As newer specifications don't mention it anymore,
 			# we still fall back to binary.
 			$l = "Manufacturing Location Code";
-			$temp = (chr($bytes[8]) =~ m/^[\w\d]$/) ? chr($bytes[8])
-			      : sprintf("0x%.2X", $bytes[8]);
+			$temp = (chr($bytes[72]) =~ m/^[\w\d]$/) ? chr($bytes[72])
+			      : sprintf("0x%.2X", $bytes[72]);
 			printl $l, $temp;
 		}
 
 		$l = "Part Number";
-		$temp = part_number(@bytes[9..26]);
+		$temp = part_number(@bytes[73..90]);
 		printl $l, $temp;
 
-		if (spd_written(@bytes[27..28])) {
+		if (spd_written(@bytes[91..92])) {
 			$l = "Revision Code";
-			$temp = sprintf("0x%02X%02X\n", @bytes[27..28]);
+			$temp = sprintf("0x%02X%02X\n", @bytes[91..92]);
 			printl $l, $temp;
 		}
 
-		if (spd_written(@bytes[29..30])) {
+		if (spd_written(@bytes[93..94])) {
 			$l = "Manufacturing Date";
 			# In theory the year and week are in BCD format, but
 			# this is not always true in practice :(
-			if (($bytes[29] & 0xf0) <= 0x90
-			 && ($bytes[29] & 0x0f) <= 0x09
-			 && ($bytes[30] & 0xf0) <= 0x90
-			 && ($bytes[30] & 0x0f) <= 0x09) {
+			if (($bytes[93] & 0xf0) <= 0x90
+			 && ($bytes[93] & 0x0f) <= 0x09
+			 && ($bytes[94] & 0xf0) <= 0x90
+			 && ($bytes[94] & 0x0f) <= 0x09) {
 				# Note that this heuristic will break in year 2080
 				$temp = sprintf("%d%02X-W%02X\n",
-						$bytes[29] >= 0x80 ? 19 : 20,
-						@bytes[29..30]);
+						$bytes[93] >= 0x80 ? 19 : 20,
+						@bytes[93..94]);
 			} else {
 				$temp = sprintf("0x%02X%02X\n",
-						@bytes[29..30]);
+						@bytes[93..94]);
 			}
 			printl $l, $temp;
 		}
 
-		if (spd_written(@bytes[31..34])) {
+		if (spd_written(@bytes[95..98])) {
 			$l = "Assembly Serial Number";
 			$temp = sprintf("0x%02X%02X%02X%02X\n",
-					@bytes[31..34]);
+					@bytes[95..98]);
 			printl $l, $temp;
 		}
 

[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux