Home | History | Annotate | Download | only in firmware_CompareChipFwToShellBall
      1 # Copyright 2017 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """This is a FAFT test to check if TCPCs are up-to-date.
      6 
      7 This test figures out which TCPCs exist on a DUT and matches
      8 these up with corresponding firmware blobs in the system
      9 image shellball.  If mismatches are detected, the test fails.
     10 
     11 The test can optionally be invoked with --args bios=... to
     12 specify an alternate reference firmware image.
     13 """
     14 
     15 import logging
     16 import os
     17 
     18 from autotest_lib.client.common_lib import error
     19 from autotest_lib.client.common_lib import utils
     20 from autotest_lib.client.common_lib.cros import chip_utils
     21 from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
     22 
     23 
     24 class firmware_CompareChipFwToShellBall(FirmwareTest):
     25 
     26     """Compares the active DUT chip firmware with reference.
     27 
     28     FAFT test to verify that a DUT runs the expected chip
     29     firmware based on the system shellball or a specified
     30     reference image.
     31     """
     32     version = 1
     33 
     34     BIOS = 'bios.bin'
     35     MAXPORTS = 100
     36 
     37     def initialize(self, host, cmdline_args):
     38         super(firmware_CompareChipFwToShellBall,
     39               self).initialize(host, cmdline_args)
     40         dict_args = utils.args_to_dict(cmdline_args)
     41         self.new_bios_path = dict_args['bios'] if 'bios' in dict_args else None
     42         self.cbfs_work_dir = None
     43         self.dut_bios_path = None
     44 
     45     def cleanup(self):
     46         try:
     47             if self.cbfs_work_dir:
     48                 self.faft_client.system.remove_dir(self.cbfs_work_dir)
     49         except Exception as e:
     50             logging.error("Caught exception: %s", str(e))
     51         super(firmware_CompareChipFwToShellBall, self).cleanup()
     52 
     53     def dut_get_chip(self, port):
     54         """Gets the chip info for a port.
     55 
     56         Args:
     57             port: TCPC port number on DUT
     58 
     59         Returns:
     60             A chip object if available, else None.
     61         """
     62 
     63         cmd = 'mosys -s product_id pd chip %d' % port
     64         chip_id = self.faft_client.system.run_shell_command_get_output(cmd)
     65         if not chip_id:
     66             # chip probably does not exist
     67             return None
     68         chip_id = chip_id[0]
     69 
     70         if chip_id not in chip_utils.chip_id_map:
     71             logging.info('chip type %s not recognized', chip_id)
     72             return chip_utils.generic_chip()
     73         chip = chip_utils.chip_id_map[chip_id]()
     74 
     75         cmd = 'mosys -s fw_version pd chip %d' % port
     76         fw_rev = self.faft_client.system.run_shell_command_get_output(cmd)
     77         if not fw_rev:
     78             # chip probably does not exist
     79             return None
     80         fw_rev = fw_rev[0]
     81         chip.set_fw_ver_from_string(fw_rev)
     82         return chip
     83 
     84     def dut_scan_chips(self):
     85         """Scans for TCPC chips on DUT.
     86 
     87         Returns:
     88             A tuple (S, L) consisting of a set S of chip types and a list L
     89             of chips indexed by port number found on on the DUT.
     90 
     91         Raises:
     92             TestFail: DUT has >= MAXPORTS pd ports.
     93         """
     94 
     95         chip_types = set()
     96         port2chip = []
     97         for port in xrange(self.MAXPORTS):
     98             chip = self.dut_get_chip(port)
     99             if not chip:
    100                 return (chip_types, port2chip)
    101             port2chip.append(chip)
    102             chip_types.add(type(chip))
    103         logging.error('found at least %u TCPC ports '
    104                       '- please update test to handle more ports '
    105                       'if this is expected.', self.MAXPORTS)
    106         raise error.TestFail('MAXPORTS exceeded' % self.MAXPORTS)
    107 
    108     def dut_locate_bios_bin(self):
    109         """Finds bios.bin on DUT.
    110 
    111         Figures out where FAFT unpacked the shellball
    112         and return path to extracted bios.bin.
    113 
    114         Returns:
    115             Full path of bios.bin on DUT.
    116         """
    117 
    118         work_path = self.faft_client.updater.get_work_path()
    119         bios_relative_path = self.faft_client.updater.get_bios_relative_path()
    120         bios_bin = os.path.join(work_path, bios_relative_path)
    121         return bios_bin
    122 
    123     def dut_prep_cbfs(self):
    124         """Sets up cbfs on DUT.
    125 
    126         Finds bios.bin on the DUT and sets up a temp dir to operate on
    127         bios.bin.  If a bios.bin was specified, it is copied to the DUT
    128         and used instead of the native bios.bin.
    129         """
    130 
    131         cbfs_path = self.faft_client.updater.cbfs_setup_work_dir()
    132         bios_relative_path = self.faft_client.updater.get_bios_relative_path()
    133         self.cbfs_work_dir = cbfs_path
    134         self.dut_bios_path = os.path.join(cbfs_path, bios_relative_path)
    135 
    136     def dut_cbfs_extract_chips(self, chip_types):
    137         """Extracts firmware hash blobs from cbfs.
    138 
    139         Iterates over requested chip types and looks for corresponding
    140         firmware hash blobs in cbfs.  These firmware hash blobs are
    141         extracted into cbfs_work_dir.
    142 
    143         Args:
    144             chip_types:
    145                 A set of chip types for which the hash blobs will be
    146                 extracted.
    147 
    148         Returns:
    149             A dict mapping found chip names to chip instances.
    150         """
    151 
    152         cbfs_chip_info = {}
    153         for chip_type in chip_types:
    154             chip = chip_type()
    155             fw = chip.fw_name
    156             if not fw:
    157                 # must be an unfamiliar chip
    158                 continue
    159 
    160             if not self.faft_client.updater.cbfs_extract_chip(chip.fw_name):
    161                 logging.warning('%s firmware not bundled in %s',
    162                                 chip.chip_name, self.BIOS)
    163                 continue
    164 
    165             hashblob = self.faft_client.updater.cbfs_get_chip_hash(
    166                 chip.fw_name)
    167             if not hashblob:
    168                 logging.warning('%s firmware hash not extracted from %s',
    169                                 chip.chip_name, self.BIOS)
    170                 continue
    171 
    172             bundled_fw_ver = chip.fw_ver_from_hash(hashblob)
    173             if not bundled_fw_ver:
    174                 raise error.TestFail(
    175                     'could not decode %s firmware hash: %s' % (
    176                         chip.chip_name, hashblob))
    177 
    178             chip.set_fw_ver_from_string(bundled_fw_ver)
    179             cbfs_chip_info[chip.chip_name] = chip
    180             logging.info('%s bundled firmware for %s is version %s',
    181                          self.BIOS, chip.chip_name, bundled_fw_ver)
    182         return cbfs_chip_info
    183 
    184     def check_chip_versions(self, port2chip, ref_chip_info):
    185         """Verifies DUT chips have expected firmware.
    186 
    187         Iterates over found DUT chips and verifies their firmware version
    188         matches the chips found in the reference ref_chip_info map.
    189 
    190         Args:
    191             port2chip: A list of chips to verify against ref_chip_info.
    192             ref_chip_info: A dict of reference chip chip instances indexed
    193                 by chip name.
    194         """
    195 
    196         for p, pinfo in enumerate(port2chip):
    197             if not pinfo.fw_ver:
    198                 # must be an unknown chip
    199                 continue
    200             msg = 'DUT port %s is a %s running firmware 0x%02x' % (
    201                 p, pinfo.chip_name, pinfo.fw_ver)
    202             if pinfo.chip_name not in ref_chip_info:
    203                 logging.warning('%s but there is no reference version', msg)
    204                 continue
    205             expected_fw_ver = ref_chip_info[pinfo.chip_name].fw_ver
    206             logging.info('%s%s', msg,
    207                          ('' if pinfo.fw_ver == expected_fw_ver else
    208                           ' (expected 0x%02x)' % expected_fw_ver))
    209 
    210             if pinfo.fw_ver != expected_fw_ver:
    211                 msg = '%s firmware was not updated to 0x%02x' % (
    212                     pinfo.chip_name, expected_fw_ver)
    213                 raise error.TestFail(msg)
    214 
    215     def run_once(self, host):
    216         # Make sure the client library is on the device so that the proxy
    217         # code is there when we try to call it.
    218 
    219         (dut_chip_types, dut_chips) = self.dut_scan_chips()
    220         if not dut_chip_types:
    221             logging.info('mosys reported no chips on DUT, skipping test')
    222             return
    223 
    224         self.dut_prep_cbfs()
    225         if self.new_bios_path:
    226             host.send_file(self.new_bios_path, self.dut_bios_path)
    227 
    228         ref_chip_info = self.dut_cbfs_extract_chips(dut_chip_types)
    229         self.check_chip_versions(dut_chips, ref_chip_info)
    230