1# Copyright 2015 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 6import os 7import shutil 8import tempfile 9 10from chromite.lib import remote_access 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib import utils 13from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 14 15 16class firmware_FWupdate(FirmwareTest): 17 """RO+RW firmware update using chromeos-firmware --mode=[recovery|factory] 18 19 Setup Steps: 20 1. Check the device is in normal mode for recovery or 21 Check the device is in dev mode for factory 22 23 Test Steps: 24 2. extract shellball and repack with new bios.bin and ec.bin 25 3. run --mode=recovery 26 4. reboot 27 28 Verification Steps: 29 1. Step 3 should result into a success message 30 2. Run crossystem and check fwid and ro_fwid should display the new bios 31 firmware version string. 32 4. Run ectool version to check ec version. The RO version and RW version 33 strings should display new ec firmware strings. 34 """ 35 36 version = 1 37 38 SHELLBALL_ORG = '/usr/sbin/chromeos-firmwareupdate' 39 SHELLBALL_COPY = '/home/root/chromeos-firmwareupdate' 40 41 def initialize(self, host, cmdline_args): 42 dict_args = utils.args_to_dict(cmdline_args) 43 super(firmware_FWupdate, self).initialize(host, cmdline_args) 44 if not set(('new_ec', 'new_bios')).issubset(set(dict_args)): 45 raise error.TestError('Missing new_ec and/or new_bios argument') 46 self.new_ec = dict_args['new_ec'] 47 self.new_bios = dict_args['new_bios'] 48 if not os.path.isfile(self.new_ec) or not os.path.isfile(self.new_bios): 49 raise error.TestError('Failed to locate ec or bios file') 50 self.new_pd = '' 51 if 'new_pd' in dict_args: 52 self.new_pd = dict_args['new_pd'] 53 if not os.path.isfile(self.new_pd): 54 raise error.TestError('Failed to locate pd file') 55 logging.info('EC=%s BIOS=%s PD=%s', 56 self.new_ec, self.new_bios, self.new_pd) 57 self.mode = 'recovery' 58 if 'mode' in dict_args: 59 self.mode = dict_args['mode'] 60 if self.mode == 'recovery': 61 self.switcher.setup_mode('normal') # Set device to normal mode 62 elif self.mode == 'factory': 63 self.switcher.setup_mode('dev') # Set device to dev mode 64 else: 65 raise error.TestError('Unknown mode:%s' % self.mode) 66 67 def local_run_cmd(self, command): 68 """Execute command on local system. 69 70 @param command: shell command to be executed on local system. 71 @returns command output. 72 """ 73 logging.info('Execute %s', command) 74 output = utils.system_output(command) 75 logging.info('Output %s', output) 76 return output 77 78 def dut_run_cmd(self, command): 79 """Execute command on DUT. 80 81 @param command: shell command to be executed on DUT. 82 @returns command output. 83 """ 84 logging.info('Execute %s', command) 85 output = self.faft_client.system.run_shell_command_get_output(command) 86 logging.info('Output %s', output) 87 return output 88 89 def get_pd_version(self): 90 """Get pd firmware version. 91 92 @returns pd firmware version string if available. 93 """ 94 if self.new_pd: 95 return self.dut_run_cmd('mosys -k pd info')[0].split('"')[5] 96 return '' 97 98 def get_system_setup(self): 99 """Get and return DUT system params. 100 101 @returns DUT system params needed for this test. 102 """ 103 return { 104 'pd_version': self.get_pd_version(), 105 'ec_version': self.faft_client.ec.get_version(), 106 'mainfw_type': 107 self.faft_client.system.get_crossystem_value('mainfw_type'), 108 'ro_fwid': 109 self.faft_client.system.get_crossystem_value('ro_fwid'), 110 'fwid': 111 self.faft_client.system.get_crossystem_value('fwid'), 112 } 113 114 def repack_shellball(self, hostname): 115 """Repack DUT shellball and replace on DUT. 116 117 @param hostname: hostname of DUT. 118 """ 119 extract_dir = tempfile.mkdtemp(prefix='extract', dir='/tmp') 120 121 self.dut_run_cmd('mkdir %s' % extract_dir) 122 self.dut_run_cmd('cp %s %s' % (self.SHELLBALL_ORG, self.SHELLBALL_COPY)) 123 self.dut_run_cmd('%s --sb_extract %s' % (self.SHELLBALL_COPY, 124 extract_dir)) 125 126 dut_access = remote_access.RemoteDevice(hostname, username='root') 127 self.dut_run_cmd('cp %s %s' % (self.SHELLBALL_ORG, self.SHELLBALL_COPY)) 128 129 # Replace bin files. 130 target_file = '%s/%s' % (extract_dir, 'ec.bin') 131 dut_access.CopyToDevice(self.new_ec, target_file, mode='scp') 132 target_file = '%s/%s' % (extract_dir, 'bios.bin') 133 dut_access.CopyToDevice(self.new_bios, target_file, mode='scp') 134 135 if self.new_pd: 136 target_file = '%s/%s' % (extract_dir, 'pd.bin') 137 dut_access.CopyToDevice(self.new_pd, target_file, mode='scp') 138 139 self.dut_run_cmd('%s --sb_repack %s' % (self.SHELLBALL_COPY, 140 extract_dir)) 141 142 # Call to "shar" in chromeos-firmwareupdate might fail and the repack 143 # ignore failure and exit with 0 status (http://crosbug.com/p/33719). 144 # Add additional check to ensure the repack is successful. 145 command = 'tail -1 %s' % self.SHELLBALL_COPY 146 output = self.dut_run_cmd(command) 147 if 'exit 0' not in output: 148 raise error.TestError('Failed to repack %s' % self.SHELLBALL_COPY) 149 150 def get_fw_bin_version(self): 151 """Get firmwware version from binary file. 152 153 @returns verions for bios, ec, pd 154 """ 155 bios_version = self.local_run_cmd('strings %s|grep Google_|head -1' 156 % self.new_bios) 157 ec_version = self.local_run_cmd('strings %s|head -1' % self.new_ec) 158 pd_version = '' 159 if self.new_pd: 160 pd_version = self.local_run_cmd('strings %s|head -1' % self.new_pd) 161 return (bios_version, ec_version, pd_version) 162 163 def run_once(self, host): 164 """Run chromeos-firmwareupdate with recovery or factory mode. 165 166 @param host: host to run on 167 """ 168 crossystem_before = self.get_system_setup() 169 (bios_version, ec_version, pd_version) = self.get_fw_bin_version() 170 171 # Repack shellball with new ec and bios. 172 self.repack_shellball(host.hostname) 173 174 # Flash DUT with new bios/ec. 175 command = '%s --mode=%s' % (self.SHELLBALL_COPY, self.mode) 176 self.dut_run_cmd(command) 177 host.reboot() 178 179 # Extract and verify DUT state. 180 crossystem_after = self.get_system_setup() 181 logging.info('crossystem BEFORE: %s', crossystem_before) 182 logging.info('crossystem AFTER: %s', crossystem_after) 183 logging.info('Expects bios %s', bios_version) 184 logging.info('Expects ec %s', ec_version) 185 logging.info('Expects pd %s', pd_version) 186 assert bios_version == crossystem_after['fwid'] 187 assert bios_version == crossystem_after['ro_fwid'] 188 assert ec_version == crossystem_after['ec_version'] 189 assert pd_version == crossystem_after['pd_version'] 190