| # 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 |
| |
| |
| class firmware_FWupdateThenSleep(FirmwareTest): |
| """RO+RW 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' |
| |
| def initialize(self, host, cmdline_args): |
| |
| self.flashed = False |
| self._want_restore = None |
| self._original_sw_wp = {} |
| self._original_hw_wp = None |
| |
| super(firmware_FWupdateThenSleep, self).initialize(host, cmdline_args) |
| |
| 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_hardware_write_protect(False) |
| self.faft_client.bios.set_write_protect_range(0, 0, False) |
| |
| def cleanup(self): |
| """Restore the original firmware and original write-protect.""" |
| |
| self.set_hardware_write_protect(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_hardware_write_protect(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): |
| """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) |
| |
| 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: |
| # TODO(b/154846910): use an enum instead of a string |
| if not self.wait_power_state('(S0ix|S3)', self.DEFAULT_PWR_RETRIES): |
| raise error.TestFail('Platform failed to reach S0ix or S3 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)) |