237 lines
9.5 KiB
Python
237 lines
9.5 KiB
Python
# Copyright 2020 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 xmlrpclib
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
|
|
from autotest_lib.server.cros.power import utils as PowerUtils
|
|
|
|
|
|
class firmware_FWupdateThenSleep(FirmwareTest):
|
|
"""Firmware update using chromeos-firmwareupdate --mode=recovery, then
|
|
sleep and wake, then make sure the host responds and the flash contents
|
|
don't change.
|
|
"""
|
|
MODE = 'recovery'
|
|
MIN_BATTERY_LEVEL = 20
|
|
|
|
def initialize(self, host, cmdline_args, battery_only=False):
|
|
|
|
self.flashed = False
|
|
self._want_restore = None
|
|
self._original_sw_wp = {}
|
|
self._original_hw_wp = None
|
|
|
|
super(firmware_FWupdateThenSleep, self).initialize(host, cmdline_args)
|
|
|
|
ac_online = self._client.is_ac_connected()
|
|
battery_percentage = self._client.get_battery_display_percentage()
|
|
self.have_power_control = False
|
|
if battery_only:
|
|
if not self._client.has_battery():
|
|
raise error.TestNAError("DUT type does not have a battery.")
|
|
|
|
if self.servo.supports_built_in_pd_control():
|
|
self.have_power_control = True
|
|
PowerUtils.put_host_battery_in_range(self._client,
|
|
min_level=25,
|
|
max_level=100,
|
|
timeout=600)
|
|
elif ac_online:
|
|
raise error.TestNAError(
|
|
"For this version of the test, the DUT power supply must"
|
|
" be unplugged, or connected through Servo V4 Type-C.")
|
|
elif battery_percentage < self.MIN_BATTERY_LEVEL:
|
|
raise error.TestError(
|
|
"Battery level is too low (%s%%). Please charge the DUT "
|
|
"to at least %s%%, then try again."
|
|
% (battery_percentage, self.MIN_BATTERY_LEVEL))
|
|
else:
|
|
logging.info("On battery: %s%% charge", battery_percentage)
|
|
|
|
self._original_sw_wp = self.faft_client.bios.get_write_protect_status()
|
|
self._original_hw_wp = 'on' in self.servo.get('fw_wp_state')
|
|
|
|
self.backup_firmware()
|
|
self.setup_firmwareupdate_shellball()
|
|
|
|
if self.faft_config.ap_access_ec_flash:
|
|
self._setup_ec_write_protect(False)
|
|
self.set_ap_write_protect_and_reboot(False)
|
|
self.faft_client.bios.set_write_protect_range(0, 0, False)
|
|
|
|
if battery_only and self.have_power_control:
|
|
self.set_servo_v4_role_to_snk()
|
|
|
|
def cleanup(self):
|
|
"""Restore the original firmware and original write-protect."""
|
|
self._restore_servo_v4_role()
|
|
|
|
self.set_ap_write_protect_and_reboot(False)
|
|
try:
|
|
if self.flashed and self.is_firmware_saved():
|
|
self.restore_firmware()
|
|
except (EnvironmentError, xmlrpclib.Fault,
|
|
error.AutoservError, error.TestBaseException):
|
|
logging.error("Problem restoring firmware:", exc_info=True)
|
|
|
|
try:
|
|
# Restore the old write-protection value at the end of the test.
|
|
if self._original_sw_wp:
|
|
self.faft_client.bios.set_write_protect_range(
|
|
self._original_sw_wp['start'],
|
|
self._original_sw_wp['length'],
|
|
self._original_sw_wp['enabled'])
|
|
except (EnvironmentError, xmlrpclib.Fault,
|
|
error.AutoservError, error.TestBaseException):
|
|
logging.error("Problem restoring SW write-protect:", exc_info=True)
|
|
|
|
if self._original_hw_wp is not None:
|
|
self.set_ap_write_protect_and_reboot(self._original_hw_wp)
|
|
|
|
super(firmware_FWupdateThenSleep, self).cleanup()
|
|
|
|
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.get_device_fwids('bios')
|
|
if self.faft_config.chrome_ec:
|
|
versions['ec'] = self.faft_client.updater.get_device_fwids('ec')
|
|
return versions
|
|
|
|
def apply_update(self, append, before_fwids, image_fwids, wp=0):
|
|
"""Run chromeos-firmwareupdate with given sub-case
|
|
|
|
@param append: additional piece to add to shellball name
|
|
@param wp: is the flash write protected (--wp)?
|
|
@return: a list of failure messages for the case
|
|
"""
|
|
options = ['--wp=%s' % wp, '--quirks=ec_partial_recovery=0']
|
|
|
|
if append:
|
|
cmd_desc = ['chromeos-firmwareupdate-%s' % append]
|
|
else:
|
|
cmd_desc = ['chromeos-firmwareupdate']
|
|
cmd_desc += ['--mode=%s' % self.MODE]
|
|
cmd_desc += options
|
|
cmd_desc = ' '.join(cmd_desc)
|
|
|
|
expected_written = {}
|
|
|
|
if wp:
|
|
bios_written = ['a', 'b']
|
|
ec_written = [] # EC write is all-or-nothing
|
|
else:
|
|
bios_written = ['ro', 'a', 'b']
|
|
ec_written = ['ro', 'rw']
|
|
|
|
expected_written['bios'] = bios_written
|
|
|
|
if self.faft_config.chrome_ec and ec_written:
|
|
expected_written['ec'] = ec_written
|
|
|
|
# remove quotes and braces: bios: [a, b], ec: [ro, rw]
|
|
written_desc = repr(expected_written).replace("'", "")[1:-1]
|
|
logging.debug('Before(%s): %s', append or '', before_fwids)
|
|
logging.debug('Image(%s): %s', append or '', image_fwids)
|
|
logging.info("Run %s (should write %s)", cmd_desc, written_desc)
|
|
|
|
# make sure we restore firmware after the test, if it tried to flash.
|
|
self.flashed = True
|
|
|
|
errors = []
|
|
result = self.run_chromeos_firmwareupdate(
|
|
self.MODE, append, options, ignore_status=True)
|
|
|
|
if result.exit_status == 255:
|
|
logging.warn("DUT network dropped during update.")
|
|
elif result.exit_status != 0:
|
|
if (image_fwids == before_fwids and
|
|
'Good. It seems nothing was changed.' in result.stdout):
|
|
logging.info("DUT already matched the image; updater aborted.")
|
|
else:
|
|
errors.append('...updater: unexpectedly failed (rc=%s)' %
|
|
result.exit_status)
|
|
|
|
after_fwids = self.get_installed_versions()
|
|
logging.debug('After(%s): %s', append or '', after_fwids)
|
|
|
|
errors += self.check_fwids_written(
|
|
before_fwids, image_fwids, after_fwids, expected_written)
|
|
|
|
if errors:
|
|
logging.debug('%s', '\n'.join(errors))
|
|
return ["%s (should write %s)\n%s"
|
|
% (cmd_desc, written_desc, '\n'.join(errors))]
|
|
return []
|
|
|
|
def run_once(self, wp=0, battery_only=False):
|
|
"""Try a firmware update, then put the machine in sleep and wake it."""
|
|
|
|
errors = []
|
|
original_boot_id = self._client.get_boot_id()
|
|
|
|
have_ec = bool(self.faft_config.chrome_ec)
|
|
before_fwids = self.get_installed_versions()
|
|
|
|
self.modify_shellball('mod', modify_ro=True, modify_ec=have_ec)
|
|
modded_fwids = self.identify_shellball(include_ec=have_ec)
|
|
|
|
# Check power again, in case it got reconnected during setup.
|
|
if battery_only and self._client.is_ac_connected():
|
|
if self.have_power_control:
|
|
raise error.TestError(
|
|
"DUT is on AC power, even though Servo role was set to"
|
|
" sink. Is a second power supply connected?")
|
|
else:
|
|
raise error.TestError(
|
|
"DUT is back on AC power, when it should be on battery."
|
|
" Did it get plugged back in?")
|
|
|
|
errors += self.apply_update('mod', before_fwids, modded_fwids, wp=wp)
|
|
if errors:
|
|
fail_msg = "Problem(s) occurred during flash, before sleep:"
|
|
errors.insert(0, fail_msg)
|
|
raise error.TestFail('\n'.join(errors))
|
|
|
|
logging.info("Firmware flashed successfully; trying sleep.")
|
|
before_sleep_fwids = self.get_installed_versions()
|
|
self.suspend()
|
|
if self.faft_config.chrome_ec:
|
|
if not self.wait_power_state(
|
|
self.POWER_STATE_SUSPEND, self.DEFAULT_PWR_RETRIES):
|
|
raise error.TestFail('Platform failed to reach a suspend state'
|
|
' after flashing firmware.')
|
|
else:
|
|
self.switcher.wait_for_client_offline(orig_boot_id=original_boot_id)
|
|
logging.info("System should now be in sleep; trying to wake.")
|
|
|
|
self.servo.power_normal_press()
|
|
self.switcher.wait_for_client()
|
|
|
|
after_sleep_fwids = self.get_installed_versions()
|
|
logging.debug('After sleep+wake: %s', after_sleep_fwids)
|
|
|
|
if have_ec:
|
|
expected_changes = {'bios': None, 'ec': None}
|
|
else:
|
|
expected_changes = {'bios': None}
|
|
|
|
after_sleep_errors = self.check_fwids_written(
|
|
before_sleep_fwids, None, after_sleep_fwids, expected_changes)
|
|
if after_sleep_errors:
|
|
errors += ['After sleep, FWIDs unexpectedly differ from'
|
|
' what was written:'] + after_sleep_errors
|
|
|
|
if errors:
|
|
fail_msg = "Problem(s) occurred during flash + sleep + wake:"
|
|
errors.insert(0, fail_msg)
|
|
raise error.TestFail('\n'.join(errors))
|