Add revision table to the SPL blob

It is becoming necessary to communicate the revision table to the SPL
blob so that it can decide what memory size to expect on the target.

The machine parameters structure allows only fixed size (32 bits)
elements. Let's add to the structure the offset of the array of bytes
representing the revision table.

The offset of the table is based on the address of the machine
parameter structure. The size of the table implicitly is defined as (3
^ NUM_GPIOS * 2) bytes, as each GPIO is a tertiary number, and for
each revision read from GPIOs the table includes two byte values:
revision and subrevision.

The size field of the variable size SPL blob header needs to be
updated to reflect the addition.

Instead of adding another elif case to the huge statement handling the
machine parameter names, this change introduces a dictionary for
routing processing based on the machine parameter name.

The fact that the blob could be resized by parameter processing
functions posed a challenge, as the blob is represented by a binary
string, and changing it in the function does not affect the caller.
The solution is to make the blob on of the ExynosBL2 class'
properties. This results in a larger change, but simplifies footprints
of many methods of this class.

For the purposes of checksum verification the BL1 expects the SPL size
to be divisible by 4, adding padding as necessary.

Conflicts:
	host/lib/exynos.py
...branch is missing a significant number of changes, so had to apply
a bunch manually.

BRANCH=4482
BUG=chrome-os-partner:23024
TEST=With all other CLs, rev5.0 board now reports 5.0 in the SPL.
TEST=Added SPL code to print map and saw values that matched dts:
  02028154: 0e 00 0e 01 0e 02 0e 03 0e 04 0e 05 0e 06 0e 07
  02028164: 03 00 03 01 03 02 03 03 04 00 04 01 04 02 04 03
  02028174: 05 00 05 01 05 02 05 03 06 00 06 01 06 02 06 03
  ...
  020281e4: 0d 00 0d 01 0d 02 0d 03 0d 04 0d 05 0d 06 0d 07
  020281f4: f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Change-Id: I4f0b005f53d4bc437780432fb59fb3c9088f4ec7
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Signed-off-by: Doug Anderson <dianders@chromium.org>
Previously-Reviewed-on: https://chromium-review.googlesource.com/171772
Reviewed-on: https://chromium-review.googlesource.com/173243
diff --git a/host/lib/exynos.py b/host/lib/exynos.py
index 5623246..340f029 100644
--- a/host/lib/exynos.py
+++ b/host/lib/exynos.py
@@ -52,6 +52,11 @@
             the caller
     _spl_type: an enum defined below, describing type (fixed of variable size)
             of the SPL being handled
+    _spl_data: a binary blob representing the SPL data being handled. It comes
+            as the value read from the u-boot makefile generated image and
+            goes as an enhanced image including the updated machine parameters
+            structure and possibly concatenated with the hash and revision
+            table.
   """
 
   VAR_SPL = 1
@@ -63,8 +68,29 @@
     self._out = output
     self._spl_type = None
     self.spl_source = 'straps'  # SPL boot according to board settings
+    self._spl_data = None  # SPL blob to configure
 
-  def _UpdateParameters(self, fdt, spl_load_size, data, pos):
+  def _MpRevMap(self, _unused1, _unused2, fdt, pos):
+    """Append revision map to the SPL blob.
+
+    Read the revison map table from the device tree, concatenate it with the
+    SPL data blob and return the offset of the added table from the machine
+    parameter structure.
+
+    Args:
+      _unused1 - a single character string, machine parameter name
+      _unused2 - a 32 bit int read from the blob
+      fdt - the Fdt object representing the target device tree
+      pos - an int, offset of the machine parameter structure into data
+    """
+    # offset of the revision table from machine param table
+    offset = len(self._spl_data) - pos
+    rev_table =  fdt.GetIntList('/board-rev', 'google,board-rev-map')
+    extra = struct.pack('%dB' % len(rev_table), *rev_table)
+    self._spl_data += extra
+    return offset
+
+  def _UpdateParameters(self, fdt, spl_load_size, pos):
     """Update the parameters in a BL2 blob.
 
     We look at the list in the parameter block, extract the value of each
@@ -73,38 +99,66 @@
     Args:
       fdt: Device tree containing the parameter values.
       spl_load_size: Size of U-Boot image that SPL must load
-      data: The BL2 data.
       pos: The position of the start of the parameter block.
-
-    Returns:
-      The new contents of the parameter block, after updating.
     """
-    version, size = struct.unpack('<2L', data[pos + 4:pos + 12])
+    version, size = struct.unpack('<2L', self._spl_data[pos + 4:pos + 12])
     if version != 1:
       raise CmdError("Cannot update machine parameter block version '%d'" %
                      version)
-    if size < 0 or pos + size > len(data):
+    if size < 0 or pos + size > len(self._spl_data):
       raise CmdError('Machine parameter block size %d is invalid: '
                      'pos=%d, size=%d, space=%d, len=%d' %
-                     (size, pos, size, len(data) - pos, len(data)))
+                     (size, pos, size, len(self._spl_data)
+                      - pos, len(self._spl_data)))
 
     # Move past the header and read the parameter list, which is terminated
     # with \0.
     pos += 12
-    param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0]
+    param_list = struct.unpack('<%ds' % (len(self._spl_data) - pos),
+                               self._spl_data[pos:])[0]
     param_len = param_list.find('\0')
     param_list = param_list[:param_len]
     pos += (param_len + 4) & ~3
 
+    #
+    # A dictionary of functions processing machine parameters. This is being
+    # introduced after more than 20 parameters have been already defined and
+    # are handled by the ugly if/elif/... construct below. It will be
+    # refactored eventually (one should hope), no new parameters' processing
+    # should be added there.
+    #
+    # The key of the dictionary is the parameter name, the value is a list.
+    # the first element of the list is the function to call to process the
+    # parameter, the rest of the elements are parameters to pass to the
+    # function.
+    #
+    # The first three parameters passed to the function are always prepended
+    # to those obtained from the list and are as follows:
+    #
+    # - the machine parameter name (one character string)
+    # - value read from the appropriate spot of the machine param structure,
+    #   as generated by the u-boot makefile, a - 32 bit int
+    # - fdt object representing the target FDT
+    #
+    # The function is expected to return a 32 bit value to plug into the
+    # machine parameters structure.
+    #
+    mp_router = {
+      't': [self._MpRevMap, pos]
+      }
+
+    # Use this to detect a missing value from the fdt.
+    not_given = 'not-given-invalid-value'
+
     # Work through the parameters one at a time, adding each value
     new_data = ''
     upto = 0
     for param in param_list:
-      value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
+      value = struct.unpack('<1L', self._spl_data[pos + upto:pos + upto + 4])[0]
 
-      # Use this to detect a missing value from the fdt.
-      not_given = 'not-given-invalid-value'
-      if param == 'm':
+      if param in mp_router:
+        value = mp_router[param][0](param, value, fdt, *mp_router[param][1:])
+      elif param == 'm':
         mem_type = fdt.GetString('/dmc', 'mem-type', not_given)
         if mem_type == not_given:
           mem_type = 'ddr3'
@@ -248,39 +302,43 @@
       upto += 4
 
     # Put the data into our block.
-    data = data[:pos] + new_data + data[pos + len(new_data):]
+    self._spl_data = self._spl_data[:pos] + new_data + self._spl_data[
+      pos + len(new_data):]
     self._out.Info('BL2 configuration complete')
-    return data
 
-  def _UpdateChecksum(self, data):
-    """Update the BL2 checksum.
+  def _UpdateChecksum(self):
+    """Update the BL2 size and checksum.
 
-    The checksum is a 4 byte sum of all the bytes in the image before the
-    last 4 bytes (which hold the checksum).
+    For the fixed size spl the checksum is a 4 byte sum of all the bytes in
+    the image before the last 4 bytes (which hold the checksum).
 
-    Args:
-      data: The BL2 data to update.
-
-    Returns:
-      The new contents of the BL2 data, after updating the checksum.
+    For the variable size SPL the first four bytes of the blob is the size of
+    the blob and the second four bytes is the sum of bytes in the rest of the
+    blob.
 
     Raises:
       CmdError if spl type is not set properly.
     """
 
     if self._spl_type == self.FIXED_SPL:
-      checksum = sum(ord(x) for x in data[:-4])
-      checksum_offset = len(data) - 4
+      checksum = sum(ord(x) for x in self._spl_data[:-4])
+      checksum_offset = len(self._spl_data) - 4
     elif self._spl_type == self.VAR_SPL:
-      checksum = sum(ord(x) for x in data[8:])
+      # Data size could have changed (the rev table could have been added).
+      if len(self._spl_data) % 4:
+        # Bl1 expects data size to be divisible by 4
+        self._spl_data += '\0' * (4 - len(self._spl_data) % 4)
+      self._spl_data = struct.pack('<L',
+                                   len(self._spl_data)) + self._spl_data[4:]
+      checksum = sum(ord(x) for x in self._spl_data[8:])
       checksum_offset = 4
     else:
       raise CmdError('SPL type not set')
 
-    return data[:checksum_offset]+ struct.pack(
-      '<L', checksum) + data[checksum_offset + 4:]
+    self._spl_data = self._spl_data[:checksum_offset] + struct.pack(
+      '<L', checksum) + self._spl_data[checksum_offset+4:]
 
-  def _VerifyBl2(self, data, loose_check):
+  def _VerifyBl2(self, loose_check):
     """Verify BL2 integrity.
 
     Fixed size and variable size SPL have different formats. Determine format,
@@ -288,13 +346,13 @@
     reference.
 
     Args:
-      data: The BL2 data to update.
       loose_check: a Boolean, if true - the variable size SPL blob could be
                    larger than the size value in the header
     Raises:
       CmdError if SPL blob is of unrecognizable format.
     """
 
+    data = self._spl_data  # Cache it to improve readability.
     # Variable size format is more sophisticated, check it first.
     try:
       size = struct.unpack('<I', data[:4])[0]
@@ -350,18 +408,18 @@
       CmdError if machine parameter block could not be found.
     """
     self._out.Info('Configuring BL2')
-    data = self._tools.ReadFile(orig_bl2)
-    self._VerifyBl2(data, loose_check)
+    self._spl_data = self._tools.ReadFile(orig_bl2)
+    self._VerifyBl2(loose_check)
 
     # Locate the parameter block
     marker = struct.pack('<L', 0xdeadbeef)
-    pos = data.rfind(marker)
+    pos = self._spl_data.rfind(marker)
     if not pos:
       raise CmdError("Could not find machine parameter block in '%s'" %
                      orig_bl2)
-    data = self._UpdateParameters(fdt, spl_load_size, data, pos)
-    data = self._UpdateChecksum(data)
+    self._UpdateParameters(fdt, spl_load_size, pos)
+    self._UpdateChecksum()
 
     bl2 = os.path.join(self._tools.outdir, 'updated-spl%s.bin' % name)
-    self._tools.WriteFile(bl2, data)
+    self._tools.WriteFile(bl2, self._spl_data)
     return bl2