blob: ed0c5d0eed644cc5c21ec903a671051a6f7891fa [file] [log] [blame]
# 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
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 with various modes.
If custom images are supplied, the DUT is left running that firmware, so the
test can be used to apply updates. Otherwise, it modifies the FWIDs of the
current firmware before flashing, and restores the firmware after the test.
Accepted --args names:
mode=[recovery|factory]
Run test with the given mode (default 'recovery')
new_bios=
new_ec=
new_pd=
apply the given image(s) instead of generating an update with fake fwids
"""
def initialize(self, host, cmdline_args):
self.images_specified = False
self.flashed = False
dict_args = utils.args_to_dict(cmdline_args)
super(firmware_FWupdate, self).initialize(host, cmdline_args)
self.new_bios = dict_args.get('new_bios', None)
self.new_ec = dict_args.get('new_ec', None)
self.new_pd = dict_args.get('new_pd', None)
if self.new_bios:
self.images_specified = True
if not os.path.isfile(self.new_bios):
raise error.TestError('Specified BIOS file does not exist: %s'
% self.new_bios)
logging.info('new_bios=%s', self.new_bios)
if self.new_ec:
self.images_specified = True
if not os.path.isfile(self.new_ec):
raise error.TestError('Specified EC file does not exist: %s'
% self.new_ec)
logging.info('new_ec=%s', self.new_ec)
if self.new_pd:
self.images_specified = True
if not os.path.isfile(self.new_pd):
raise error.TestError('Specified PD file does not exist: %s'
% self.new_pd)
logging.info('new_pd=%s', self.new_pd)
if not self.images_specified:
# TODO(dgoyette): move this into the general FirmwareTest init?
stripped_bios = self.faft_client.Bios.StripModifiedFwids()
if stripped_bios:
logging.warn(
"Fixed the previously modified BIOS FWID(s): %s",
stripped_bios)
if self.faft_config.chrome_ec:
stripped_ec = self.faft_client.Ec.StripModifiedFwids()
if stripped_ec:
logging.warn(
"Fixed the previously modified EC FWID(s): %s",
stripped_ec)
self.backup_firmware()
self.set_hardware_write_protect(False)
self.mode = dict_args.get('mode', 'recovery')
if self.mode not in ('factory', 'recovery'):
raise error.TestError('Unhandled mode: %s' % self.mode)
def get_installed_versions(self):
"""Get the installed versions of BIOS and EC firmware.
@return: A nested dict keyed by target ('bios' or 'ec') and then section
@rtype: dict
"""
versions = dict()
versions['bios'] = self.faft_client.Updater.GetAllInstalledFwids('bios')
if self.faft_config.chrome_ec:
versions['ec'] = self.faft_client.Updater.GetAllInstalledFwids('ec')
return versions
def copy_cmdline_images(self, hostname):
"""Copy the specified command line images into the extracted shellball.
@param hostname: hostname (not the Host object) to copy to
"""
if self.new_bios or self.new_ec or self.new_pd:
extract_dir = self.faft_client.Updater.GetWorkPath()
dut_access = remote_access.RemoteDevice(hostname, username='root')
# Replace bin files.
if self.new_bios:
bios_rel = self.faft_client.Updater.GetBiosRelativePath()
bios_path = os.path.join(extract_dir, bios_rel)
dut_access.CopyToDevice(self.new_bios, bios_path, mode='scp')
if self.new_ec:
ec_rel = self.faft_client.Updater.GetEcRelativePath()
ec_path = os.path.join(extract_dir, ec_rel)
dut_access.CopyToDevice(self.new_ec, ec_path, mode='scp')
if self.new_pd:
# note: pd.bin might likewise need special path logic
pd_path = os.path.join(extract_dir, 'pd.bin')
dut_access.CopyToDevice(self.new_pd, pd_path, mode='scp')
def run_once(self, host):
"""Run chromeos-firmwareupdate with recovery or factory mode.
@param host: host to run on
"""
mode = self.mode
append = 'new'
have_ec = bool(self.faft_config.chrome_ec)
self.faft_client.Updater.ExtractShellball()
before_fwids = self.get_installed_versions()
# Repack shellball with modded fwids
if self.images_specified:
# Use new images as-is
logging.info(
"Using specified image(s):"
"new_bios=%s, new_ec=%s, new_pd=%s",
self.new_bios, self.new_ec, self.new_pd)
self.copy_cmdline_images(host.hostname)
self.faft_client.Updater.ReloadImages()
self.faft_client.Updater.RepackShellball(append)
modded_fwids = self.identify_shellball(include_ec=have_ec)
else:
# Modify the stock image
logging.info(
"Using the currently running firmware, with modified fwids")
self.setup_firmwareupdate_shellball()
self.faft_client.Updater.ReloadImages()
self.modify_shellball(append, modify_ro=True, modify_ec=have_ec)
modded_fwids = self.identify_shellball(include_ec=have_ec)
case_desc = 'chromeos-firmwareupdate --mode=%s --wp=0' % mode
logging.info("Run %s", case_desc)
# make sure we restore firmware after the test, if it tried to flash.
self.flashed = True
self.faft_client.Updater.RunFirmwareupdate(mode, append, ['--wp=0'])
after_fwids = self.get_installed_versions()
errors = self.check_fwids_written(
before_fwids, modded_fwids, after_fwids,
{'bios': ['ro', 'a', 'b'], 'ec': ['ro', 'rw']})
if not errors:
logging.debug('versions correct: %s', after_fwids)
if len(errors) == 1:
raise error.TestFail(errors[0])
elif errors:
errors.insert(0, "%s: %s problems" % (case_desc, len(errors)))
raise error.TestFail('\n'.join(errors))
def cleanup(self):
"""
If test was given custom images to apply, reboot the EC to apply them.
Otherwise, restore firmware from the backup taken before flashing.
No EC reboot is needed in that case, because the test didn't actually
reboot the EC with the new firmware.
"""
if self.flashed:
if self.images_specified:
self.sync_and_ec_reboot('hard')
else:
logging.info("Restoring firmware")
self.restore_firmware()
super(firmware_FWupdate, self).cleanup()