• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import logging
6
7from autotest_lib.client.common_lib import error
8from autotest_lib.server import autotest
9from autotest_lib.server import test
10from autotest_lib.server.cros.faft.rpc_proxy import RPCProxy
11
12class 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 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        mosys_output = self._run_cmd('mosys')
42        if 'EC information' in mosys_output:
43            self.has_ec = True
44            self._run_cmd('flashrom -p ec --wp-status', checkfor='is disabled')
45
46    def _run_cmd(self, command, checkfor=''):
47        """Run command on dut and return output.
48           Optionally check output contain string 'checkfor'.
49        """
50        logging.info('Execute: %s', command)
51        output = self.host.run(command, ignore_status=True).stdout
52        logging.info('Output: %s', output.split('\n'))
53        if checkfor and checkfor not in ''.join(output):
54            raise error.TestFail('Expect %s in output of %s' %
55                                 (checkfor, ' '.join(output)))
56        return output
57
58    def _get_version(self):
59        """Retrive RO, RW EC/PD version."""
60        ro = None
61        rw = None
62        lines = self._run_cmd('ectool version', checkfor='version')
63        for line in lines.splitlines():
64            if line.startswith('RO version:'):
65                parts = line.split(':')
66                ro = parts[1].strip()
67            if line.startswith('RW version:'):
68                parts = line.split(':')
69                rw = parts[1].strip()
70        return (ro, rw)
71
72    def _bios_version(self):
73        """Retrive RO, RW BIOS version."""
74        ro = self.faft_client.system.get_crossystem_value('ro_fwid')
75        rw = self.faft_client.system.get_crossystem_value('fwid')
76        return (ro, rw)
77
78    def _construct_fw_version(self, fw_ro, fw_rw):
79        """Construct a firmware version string in a consistent manner.
80
81        @param fw_ro: A string representing the version of a read-only
82                      firmware.
83        @param fw_rw: A string representing the version of a read-write
84                      firmware.
85
86        @returns a string constructed from fw_ro and fw_rw
87
88        """
89        if fw_ro == fw_rw:
90            return fw_rw
91        else:
92            return '%s,%s' % (fw_ro, fw_rw)
93
94    def _get_version_all(self):
95        """Retrive BIOS, EC, and PD firmware version.
96
97        @return firmware version tuple (bios, ec)
98        """
99        bios_version = None
100        ec_version = None
101        if self.has_ec:
102            (ec_ro, ec_rw) = self._get_version()
103            ec_version = self._construct_fw_version(ec_ro, ec_rw)
104            logging.info('Installed EC version: %s', ec_version)
105        (bios_ro, bios_rw) = self._bios_version()
106        bios_version = self._construct_fw_version(bios_ro, bios_rw)
107        logging.info('Installed BIOS version: %s', bios_version)
108        return (bios_version, ec_version)
109
110    def _get_shellball_version(self):
111        """Get shellball firmware version.
112
113        @return shellball firmware version tuple (bios, ec)
114        """
115        ec = None
116        bios = None
117        bios_ro = None
118        bios_rw = None
119        shellball = self._run_cmd('/usr/sbin/chromeos-firmwareupdate -V')
120        for line in shellball.splitlines():
121            if line.startswith('BIOS version:'):
122                parts = line.split(':')
123                bios_ro = parts[1].strip()
124                logging.info('shellball ro bios %s', bios_ro)
125            if line.startswith('BIOS (RW) version:'):
126                parts = line.split(':')
127                bios_rw = parts[1].strip()
128                logging.info('shellball rw bios %s', bios_rw)
129            elif line.startswith('EC version:'):
130                parts = line.split(':')
131                ec = parts[1].strip()
132                logging.info('shellball ec %s', ec)
133        # Shellballs do not always contain a RW version.
134        if bios_rw is not None:
135          bios = self._construct_fw_version(bios_ro, bios_rw)
136        else:
137          bios = bios_ro
138        return (bios, ec)
139
140    def run_once(self, replace=True):
141        # Get DUT installed firmware versions.
142        (installed_bios, installed_ec) = self._get_version_all()
143
144        # Get shellball firmware versions.
145        (shball_bios, shball_ec) = self._get_shellball_version()
146
147        # Figure out if update is needed.
148        need_update = False
149        if installed_bios != shball_bios:
150            need_update = True
151            logging.info('BIOS mismatch %s, will update to %s',
152                         installed_bios, shball_bios)
153        if installed_ec and installed_ec != shball_ec:
154            need_update = True
155            logging.info('EC mismatch %s, will update to %s',
156                         installed_ec, shball_ec)
157
158        # Update and reboot if needed.
159        if need_update:
160            output = self._run_cmd('/usr/sbin/chromeos-firmwareupdate '
161                                   ' --mode=recovery', '(recovery) completed.')
162            self.host.reboot()
163            # Check that installed firmware match the shellball.
164            (bios, ec) = self._get_version_all()
165            # TODO(kmshelton): Refactor this test to use named tuples so that
166            # the comparison is eaiser to grok.
167            if (bios != shball_bios or ec != shball_ec):
168                logging.info('shball bios/ec: %s/%s',
169                             shball_bios, shball_ec)
170                logging.info('installed bios/ec: %s/%s', bios, ec)
171                raise error.TestFail('Version mismatch after firmware update')
172            logging.info('*** Done firmware updated to match shellball. ***')
173        else:
174            logging.info('*** No firmware update is needed. ***')
175
176