Home | History | Annotate | Download | only in platform_LabFirmwareUpdate
      1 # Copyright 2016 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 import logging
      6 
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.server import autotest
      9 from autotest_lib.server import test
     10 from autotest_lib.server.cros.faft.rpc_proxy import RPCProxy
     11 
     12 class platform_LabFirmwareUpdate(test.test):
     13     """For test or lab devices.  Test will fail if Software write protection
     14        is enabled.  Test will compare the installed firmware to those in
     15        the shellball.  If differ, execute chromeos-firmwareupdate
     16        --mode=recovery to reset RO and RW firmware. Basic procedure are:
     17 
     18        - check software write protect, if enable, attemp reset.
     19        - fail test if software write protect is enabled.
     20        - check if [ec, pd] is available on DUT.
     21        - get RO, RW versions of firmware, if RO != RW, update=True
     22        - get shellball versions of firmware
     23        - compare shellball version to DUT, update=True if shellball != DUT.
     24        - run chromeos-firwmareupdate --mode=recovery if update==True
     25        - reboot
     26     """
     27     version = 1
     28 
     29     def initialize(self, host):
     30         self.host = host
     31         # Make sure the client library is on the device so that the proxy
     32         # code is there when we try to call it.
     33         client_at = autotest.Autotest(self.host)
     34         client_at.install()
     35         self.faft_client = RPCProxy(self.host)
     36 
     37         # Check if EC, PD is available.
     38         # Check if DUT software write protect is disabled, failed otherwise.
     39         self._run_cmd('flashrom -p host --wp-status', checkfor='is disabled')
     40         self.has_ec = False
     41         self.has_pd = False
     42         mosys_output = self._run_cmd('mosys')
     43         if 'EC information' in mosys_output:
     44             self.has_ec = True
     45             self._run_cmd('flashrom -p ec --wp-status', checkfor='is disabled')
     46         if 'PD information' in mosys_output:
     47             self.has_pd = True
     48             self._run_cmd('flashrom -p ec:dev=1 --wp-status',
     49                          checkfor='is disabled')
     50 
     51     def _run_cmd(self, command, checkfor=''):
     52         """Run command on dut and return output.
     53            Optionally check output contain string 'checkfor'.
     54         """
     55         logging.info('Execute: %s', command)
     56         output = self.host.run(command, ignore_status=True).stdout
     57         logging.info('Output: %s', output.split('\n'))
     58         if checkfor and checkfor not in ''.join(output):
     59             raise error.TestFail('Expect %s in output of %s' %
     60                                  (checkfor, ' '.join(output)))
     61         return output
     62 
     63     def _get_version(self, pd=False):
     64         """Retrive RO, RW EC/PD version."""
     65         ro = None
     66         rw = None
     67         opt_arg = ''
     68         if pd: opt_arg = '--dev=1'
     69         lines = self._run_cmd('/usr/sbin/ectool %s version' % opt_arg)
     70         for line in lines.splitlines():
     71             if line.startswith('RO version:'):
     72                 parts = line.split()
     73                 ro = parts[2].strip()
     74             if line.startswith('RW version:'):
     75                 parts = line.split()
     76                 rw = parts[2].strip()
     77         return (ro, rw)
     78 
     79     def _bios_version(self):
     80         """Retrive RO, RW BIOS version."""
     81         ro = self.faft_client.system.get_crossystem_value('ro_fwid')
     82         rw = self.faft_client.system.get_crossystem_value('fwid')
     83         return (ro, rw)
     84 
     85     def _get_version_all(self):
     86         """Retrive BIOS, EC, and PD firmware version.
     87 
     88         @return firmware version tuple (bios, ec, pd)
     89         """
     90         pd_version = None
     91         ec_version = None
     92         bios_version = None
     93         if self.has_ec:
     94             (ec_ro, ec_rw) = self._get_version()
     95             if ec_ro == ec_rw:
     96                 ec_version = ec_rw
     97             else:
     98                 ec_version = '%s,%s' % (ec_ro, ec_rw)
     99             logging.info('Installed EC version: %s', ec_version)
    100         if self.has_pd:
    101             (pd_ro, pd_rw) = self._get_version(pd=True)
    102             if pd_ro == pd_rw:
    103                 pd_version = pd_rw
    104             else:
    105                 pd_version = '%s,%s' % (pd_ro, pd_rw)
    106             logging.info('Installed PD version: %s', pd_version)
    107         (bios_ro, bios_rw) = self._bios_version()
    108         if bios_ro == bios_rw:
    109             bios_version = bios_rw
    110         else:
    111             bios_version = '%s,%s' % (bios_ro, bios_rw)
    112         logging.info('Installed BIOS version: %s', bios_version)
    113         return (bios_version, ec_version, pd_version)
    114 
    115     def _get_shellball_version(self):
    116         """Get shellball firmware version.
    117 
    118         @return shellball firmware version tuple (bios, ec, pd)
    119         """
    120         ec = None
    121         bios = None
    122         pd = None
    123         shellball = self._run_cmd('/usr/sbin/chromeos-firmwareupdate -V')
    124         for line in shellball.splitlines():
    125             if line.startswith('BIOS version:'):
    126                 parts = line.split()
    127                 bios = parts[2].strip()
    128                 logging.info('shellball bios %s', bios)
    129             elif line.startswith('EC version:'):
    130                 parts = line.split()
    131                 ec = parts[2].strip()
    132                 logging.info('shellball ec %s', ec)
    133             elif line.startswith('PD version:'):
    134                 parts = line.split()
    135                 pd = parts[2].strip()
    136                 logging.info('shellball pd %s', pd)
    137         return (bios, ec, pd)
    138 
    139     def run_once(self, replace=True):
    140         # Get DUT installed firmware versions.
    141         (installed_bios, installed_ec, installed_pd) = self._get_version_all()
    142 
    143         # Get shellball firmware versions.
    144         (shball_bios, shball_ec, shball_pd) = self._get_shellball_version()
    145 
    146         # Figure out if update is needed.
    147         need_update = False
    148         if installed_bios != shball_bios:
    149             need_update = True
    150             logging.info('BIOS mismatch %s, will update to %s',
    151                          installed_bios, shball_bios)
    152         if installed_ec and installed_ec != shball_ec:
    153             need_update = True
    154             logging.info('EC mismatch %s, will update to %s',
    155                          installed_ec, shball_ec)
    156         if installed_pd != shball_pd:
    157             need_update = True
    158             logging.info('PD mismatch %s, will update to %s',
    159                          installed_pd, shball_pd)
    160 
    161         # Update and reboot if needed.
    162         if need_update:
    163             output = self._run_cmd('/usr/sbin/chromeos-firmwareupdate '
    164                                    ' --mode=recovery', '(recovery) completed.')
    165             self.host.reboot()
    166             # Check that installed firmware match the shellball.
    167             (bios, ec, pd) = self._get_version_all()
    168             if (bios != shball_bios or ec != shball_ec or pd != shball_pd):
    169                 logging.info('shball bios/ec/pd: %s/%s/%s',
    170                              shball_bios, shball_ec, shball_pd)
    171                 logging.info('installed bios/ec/pd: %s/%s/%s', bios, ec, pd)
    172                 raise error.TestFail('Version mismatch after firmware update')
    173             logging.info('*** Done firmware updated to match shellball. ***')
    174         else:
    175             logging.info('*** No firmware update is needed. ***')
    176 
    177