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