hardware_SAT: add failure address to memory chip mapping for Link

This change upgrades the included stressapptest tarball in hardware_SAT
to the new version that supports virtual-to-physical address translation
and configurable memory channel/chip mapping for failure addresses. It
also adapts the Python wrapper to supply the necessary parameters for
Link, dynamically detecting the memory channel ordering and the
interleave hash by reading memory controller registers.

BUG=chrome-os-partner:16577
TEST=Run hardware_SAT on Link, make sure it works. Run it again while
holding a soldering iron close to a memory chip, make sure that reported
failures correctly identify it.

Change-Id: I877b00af84bc74eb2f7ed9171f2ba2651f3bb6cb
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/40947
Reviewed-by: Nick Sanders <nsanders@chromium.org>
Commit-Queue: Jon Salz <jsalz@chromium.org>
diff --git a/client/site_tests/hardware_SAT/hardware_SAT.py b/client/site_tests/hardware_SAT/hardware_SAT.py
index 761510e..8790799 100644
--- a/client/site_tests/hardware_SAT/hardware_SAT.py
+++ b/client/site_tests/hardware_SAT/hardware_SAT.py
@@ -2,16 +2,54 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import logging, os, re, sys
+import logging, os, re, struct, sys
 
 from autotest_lib.client.bin import test, utils
 from autotest_lib.client.common_lib import error
 
+def memory_channel_args_sandybridge(channel_modules):
+    with open('/proc/bus/pci/00/00.0', 'r', 0) as fd:
+        fd.seek(0x48)
+        mchbar = struct.unpack('=I', fd.read(4))[0]
+    if not mchbar & 1:
+        raise error.TestError('Host Memory Mapped Register Range not enabled.')
+    mchbar &= ~1
+
+    with open('/dev/mem', 'r', 0) as fd:
+        fd.seek(mchbar + 0x5000)
+        mad_chnl = struct.unpack('=I', fd.read(4))[0]
+        fd.seek(mchbar + 0x5024)
+        channel_hash = struct.unpack('=I', fd.read(4))[0]
+
+    if (mad_chnl >> 4) & 3 != 2:
+        raise error.TestError('This test does not support triple-channel mode.')
+    if mad_chnl & 3 == 0 and (mad_chnl >> 2) & 3 == 1:
+        channel_order = [0, 1]
+    elif mad_chnl & 3 == 1 and (mad_chnl >> 2) & 3 == 0:
+        logging.warn('Non-default memory channel configuration... please '
+                     'double-check that this is correct and intended.')
+        channel_order = [1, 0]
+    else:
+        raise error.TestError('Invalid channel configuration: %x' % mad_chnl)
+
+    if not channel_hash & (1 << 23):
+        logging.warn('Memory channel_hash deactivated... going with cache-line '
+                     'sized ping-pong as a wild guess.')
+        channel_hash = 1
+    channel_hash = (channel_hash & 0x3FFF) << 6
+
+    return (' --memory_channel %s --memory_channel %s --channel_hash 0x%x'
+            ' --channel_width 64' % (
+                    ','.join(channel_modules[channel_order[0]]),
+                    ','.join(channel_modules[channel_order[1]]),
+                    channel_hash))
+
+
 class hardware_SAT(test.test):
     version = 1
 
-    # http://code.google.com/p/stressapptest/ 
-    def setup(self, tarball='stressapptest-1.0.3_autoconf.tar.gz'):
+    # http://code.google.com/p/stressapptest/
+    def setup(self, tarball='stressapptest-1.0.6_autoconf.tar.gz'):
         # clean
         if os.path.exists(self.srcdir):
             utils.system('rm -rf %s' % self.srcdir)
@@ -24,12 +62,8 @@
         cflags = '-I' + self.autodir + '/deps/libaio/include'
         # Add paths to libaio files.
         var_flags = 'LDFLAGS="' + ldflags + '"'
-        if utils.target_is_pie():
-            var_flags += ' CXXFLAGS="-nopie ' + cflags + '"'
-            var_flags += ' CFLAGS="-nopie ' + cflags + '"'
-        else:
-            var_flags += ' CXXFLAGS="' + cflags + '"'
-            var_flags += ' CFLAGS="' + cflags + '"'
+        var_flags += ' CXXFLAGS="' + cflags + '"'
+        var_flags += ' CFLAGS="' + cflags + '"'
         var_flags += ' LIBS="-static -laio"'
 
         os.chdir(self.srcdir)
@@ -57,7 +91,7 @@
         # Even though shared memory allows us to go past the 1.4G
         # limit, ftruncate still limits us to 2G max on 32 bit systems.
         if sys.maxsize < 2**32 and mbytes > 2047:
-          mbytes = 2047
+            mbytes = 2047
         # SAT should use as much memory as possible, while still
         # avoiding OOMs and allowing the kernel to run, so that
         # the maximum amoun tof memory can be tested.
@@ -81,6 +115,11 @@
         args += ' -f sat.diskthread.a'  # disk thread
         args += ' -f sat.diskthread.b'
 
+        if utils.get_board() == 'LINK':
+            args += memory_channel_args_sandybridge([
+                    ['U1', 'U2', 'U3', 'U4'],
+                    ['U6', 'U5', 'U7', 'U8']])  # yes, U6 is actually before U5
+
         os.chdir(os.path.join(self.srcdir, 'src'))
         sat = utils.run('./stressapptest' + args)
         logging.debug(sat.stdout)
diff --git a/client/site_tests/hardware_SAT/stressapptest-1.0.3_autoconf.tar.gz b/client/site_tests/hardware_SAT/stressapptest-1.0.3_autoconf.tar.gz
deleted file mode 100644
index 8a5d088..0000000
--- a/client/site_tests/hardware_SAT/stressapptest-1.0.3_autoconf.tar.gz
+++ /dev/null
Binary files differ
diff --git a/client/site_tests/hardware_SAT/stressapptest-1.0.6_autoconf.tar.gz b/client/site_tests/hardware_SAT/stressapptest-1.0.6_autoconf.tar.gz
new file mode 100644
index 0000000..63d7445
--- /dev/null
+++ b/client/site_tests/hardware_SAT/stressapptest-1.0.6_autoconf.tar.gz
Binary files differ