| # Copyright 2015 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import os |
| import shutil |
| import tempfile |
| |
| from chromite.lib import remote_access |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib import utils |
| from autotest_lib.server.cros.faft.firmware_test import FirmwareTest |
| |
| |
| class firmware_FWupdate(FirmwareTest): |
| """RO+RW firmware update using chromeos-firmware --mode=[recovery|factory] |
| |
| Setup Steps: |
| 1. Check the device is in normal mode for recovery or |
| Check the device is in dev mode for factory |
| |
| Test Steps: |
| 2. extract shellball and repack with new bios.bin and ec.bin |
| 3. run --mode=recovery |
| 4. reboot |
| |
| Verification Steps: |
| 1. Step 3 should result into a success message |
| 2. Run crossystem and check fwid and ro_fwid should display the new bios |
| firmware version string. |
| 4. Run ectool version to check ec version. The RO version and RW version |
| strings should display new ec firmware strings. |
| """ |
| |
| version = 1 |
| |
| SHELLBALL_ORG = '/usr/sbin/chromeos-firmwareupdate' |
| SHELLBALL_COPY = '/home/root/chromeos-firmwareupdate' |
| |
| def initialize(self, host, cmdline_args): |
| dict_args = utils.args_to_dict(cmdline_args) |
| super(firmware_FWupdate, self).initialize(host, cmdline_args) |
| if not set(('new_ec', 'new_bios')).issubset(set(dict_args)): |
| raise error.TestError('Missing new_ec and/or new_bios argument') |
| self.new_ec = dict_args['new_ec'] |
| self.new_bios = dict_args['new_bios'] |
| if not os.path.isfile(self.new_ec) or not os.path.isfile(self.new_bios): |
| raise error.TestError('Failed to locate ec or bios file') |
| self.new_pd = '' |
| if 'new_pd' in dict_args: |
| self.new_pd = dict_args['new_pd'] |
| if not os.path.isfile(self.new_pd): |
| raise error.TestError('Failed to locate pd file') |
| logging.info('EC=%s BIOS=%s PD=%s', |
| self.new_ec, self.new_bios, self.new_pd) |
| self.mode = 'recovery' |
| if 'mode' in dict_args: |
| self.mode = dict_args['mode'] |
| if self.mode == 'recovery': |
| self.switcher.setup_mode('normal') # Set device to normal mode |
| elif self.mode == 'factory': |
| self.switcher.setup_mode('dev') # Set device to dev mode |
| else: |
| raise error.TestError('Unknown mode:%s' % self.mode) |
| |
| def local_run_cmd(self, command): |
| """Execute command on local system. |
| |
| @param command: shell command to be executed on local system. |
| @returns command output. |
| """ |
| logging.info('Execute %s', command) |
| output = utils.system_output(command) |
| logging.info('Output %s', output) |
| return output |
| |
| def dut_run_cmd(self, command): |
| """Execute command on DUT. |
| |
| @param command: shell command to be executed on DUT. |
| @returns command output. |
| """ |
| logging.info('Execute %s', command) |
| output = self.faft_client.system.run_shell_command_get_output(command) |
| logging.info('Output %s', output) |
| return output |
| |
| def get_pd_version(self): |
| """Get pd firmware version. |
| |
| @returns pd firmware version string if available. |
| """ |
| if self.new_pd: |
| return self.dut_run_cmd('mosys -k pd info')[0].split('"')[5] |
| return '' |
| |
| def get_system_setup(self): |
| """Get and return DUT system params. |
| |
| @returns DUT system params needed for this test. |
| """ |
| return { |
| 'pd_version': self.get_pd_version(), |
| 'ec_version': self.faft_client.ec.get_version(), |
| 'mainfw_type': |
| self.faft_client.system.get_crossystem_value('mainfw_type'), |
| 'ro_fwid': |
| self.faft_client.system.get_crossystem_value('ro_fwid'), |
| 'fwid': |
| self.faft_client.system.get_crossystem_value('fwid'), |
| } |
| |
| def repack_shellball(self, hostname): |
| """Repack DUT shellball and replace on DUT. |
| |
| @param hostname: hostname of DUT. |
| """ |
| extract_dir = tempfile.mkdtemp(prefix='extract', dir='/tmp') |
| |
| self.dut_run_cmd('mkdir %s' % extract_dir) |
| self.dut_run_cmd('cp %s %s' % (self.SHELLBALL_ORG, self.SHELLBALL_COPY)) |
| self.dut_run_cmd('%s --sb_extract %s' % (self.SHELLBALL_COPY, |
| extract_dir)) |
| |
| dut_access = remote_access.RemoteDevice(hostname, username='root') |
| self.dut_run_cmd('cp %s %s' % (self.SHELLBALL_ORG, self.SHELLBALL_COPY)) |
| |
| # Replace bin files. |
| target_file = '%s/%s' % (extract_dir, 'ec.bin') |
| dut_access.CopyToDevice(self.new_ec, target_file, mode='scp') |
| target_file = '%s/%s' % (extract_dir, 'bios.bin') |
| dut_access.CopyToDevice(self.new_bios, target_file, mode='scp') |
| |
| if self.new_pd: |
| target_file = '%s/%s' % (extract_dir, 'pd.bin') |
| dut_access.CopyToDevice(self.new_pd, target_file, mode='scp') |
| |
| self.dut_run_cmd('%s --sb_repack %s' % (self.SHELLBALL_COPY, |
| extract_dir)) |
| |
| # Call to "shar" in chromeos-firmwareupdate might fail and the repack |
| # ignore failure and exit with 0 status (http://crosbug.com/p/33719). |
| # Add additional check to ensure the repack is successful. |
| command = 'tail -1 %s' % self.SHELLBALL_COPY |
| output = self.dut_run_cmd(command) |
| if 'exit 0' not in output: |
| raise error.TestError('Failed to repack %s' % self.SHELLBALL_COPY) |
| |
| def get_fw_bin_version(self): |
| """Get firmwware version from binary file. |
| |
| @returns verions for bios, ec, pd |
| """ |
| bios_version = self.local_run_cmd('strings %s|grep Google_|head -1' |
| % self.new_bios) |
| ec_version = self.local_run_cmd('strings %s|head -1' % self.new_ec) |
| pd_version = '' |
| if self.new_pd: |
| pd_version = self.local_run_cmd('strings %s|head -1' % self.new_pd) |
| return (bios_version, ec_version, pd_version) |
| |
| def run_once(self, host): |
| """Run chromeos-firmwareupdate with recovery or factory mode. |
| |
| @param host: host to run on |
| """ |
| crossystem_before = self.get_system_setup() |
| (bios_version, ec_version, pd_version) = self.get_fw_bin_version() |
| |
| # Repack shellball with new ec and bios. |
| self.repack_shellball(host.hostname) |
| |
| # Flash DUT with new bios/ec. |
| command = '%s --mode=%s' % (self.SHELLBALL_COPY, self.mode) |
| self.dut_run_cmd(command) |
| host.reboot() |
| |
| # Extract and verify DUT state. |
| crossystem_after = self.get_system_setup() |
| logging.info('crossystem BEFORE: %s', crossystem_before) |
| logging.info('crossystem AFTER: %s', crossystem_after) |
| logging.info('Expects bios %s', bios_version) |
| logging.info('Expects ec %s', ec_version) |
| logging.info('Expects pd %s', pd_version) |
| assert bios_version == crossystem_after['fwid'] |
| assert bios_version == crossystem_after['ro_fwid'] |
| assert ec_version == crossystem_after['ec_version'] |
| assert pd_version == crossystem_after['pd_version'] |