1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright 2016 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Li""" 7*9c5db199SXin LiRepair actions and verifiers relating to CrOS firmware. 8*9c5db199SXin Li 9*9c5db199SXin LiThis contains the repair actions and verifiers need to find problems 10*9c5db199SXin Liwith the firmware installed on ChromeOS DUTs, and when necessary, to 11*9c5db199SXin Lifix problems by updating or re-installing the firmware. 12*9c5db199SXin Li 13*9c5db199SXin LiThe operations in the module support two distinct use cases: 14*9c5db199SXin Li * DUTs used for FAFT tests can in some cases have problems with 15*9c5db199SXin Li corrupted firmware. The module supplies `FirmwareStatusVerifier` 16*9c5db199SXin Li to check for corruption, and supplies `FaftFirmwareRepair` to 17*9c5db199SXin Li re-install firmware of current faft stable_version via servo 18*9c5db199SXin Li when needed. 19*9c5db199SXin Li * DUTs used for general testing normally should be running a 20*9c5db199SXin Li designated "stable" firmware version. This module supplies 21*9c5db199SXin Li `FirmwareVersionVerifier` to detect and automatically update 22*9c5db199SXin Li firmware that is out-of-date from the designated version. This model 23*9c5db199SXin Li also supplys `GeneralFirmwareRepair` to re-install firmware that 24*9c5db199SXin Li tied with current stable_version image via servo when needed. 25*9c5db199SXin Li 26*9c5db199SXin LiFor purposes of the operations in the module, we distinguish three kinds 27*9c5db199SXin Liof DUT, based on pool assignments: 28*9c5db199SXin Li * DUTs used for general testing. These DUTs automatically check for 29*9c5db199SXin Li and install the stable firmware using `FirmwareVersionVerifier`. 30*9c5db199SXin Li * DUTs in pools used for FAFT testing. These check for bad firmware 31*9c5db199SXin Li builds with `FirmwareStatusVerifier`, and will fix problems using 32*9c5db199SXin Li `FirmwareRepair`. These DUTs don't check for or install the 33*9c5db199SXin Li stable firmware. 34*9c5db199SXin Li * DUTs not in general pools, and not used for FAFT. These DUTs 35*9c5db199SXin Li are expected to be managed by separate processes and are excluded 36*9c5db199SXin Li from all of the verification and repair code in this module. 37*9c5db199SXin Li""" 38*9c5db199SXin Li 39*9c5db199SXin Li# pylint: disable=missing-docstring 40*9c5db199SXin Li 41*9c5db199SXin Lifrom __future__ import absolute_import 42*9c5db199SXin Lifrom __future__ import division 43*9c5db199SXin Lifrom __future__ import print_function 44*9c5db199SXin Li 45*9c5db199SXin Liimport json 46*9c5db199SXin Liimport logging 47*9c5db199SXin Li 48*9c5db199SXin Liimport common 49*9c5db199SXin Lifrom autotest_lib.client.common_lib import global_config 50*9c5db199SXin Lifrom autotest_lib.client.common_lib import hosts 51*9c5db199SXin Lifrom autotest_lib.server import afe_utils 52*9c5db199SXin Lifrom autotest_lib.server.hosts import repair_utils 53*9c5db199SXin Lifrom autotest_lib.server.hosts import cros_constants 54*9c5db199SXin Li 55*9c5db199SXin Lifrom autotest_lib.utils.frozen_chromite.lib import timeout_util 56*9c5db199SXin Liimport six 57*9c5db199SXin Li 58*9c5db199SXin Li 59*9c5db199SXin Li# _FIRMWARE_REPAIR_POOLS - The set of pools that should be 60*9c5db199SXin Li# managed by `FirmwareStatusVerifier` and `FirmwareRepair`. 61*9c5db199SXin Li# 62*9c5db199SXin Li_FIRMWARE_REPAIR_POOLS = set( 63*9c5db199SXin Li global_config.global_config.get_config_value( 64*9c5db199SXin Li 'CROS', 65*9c5db199SXin Li 'pools_support_firmware_repair', 66*9c5db199SXin Li type=str).split(',')) 67*9c5db199SXin Li 68*9c5db199SXin Li 69*9c5db199SXin Lidef _is_firmware_testing_device(host): 70*9c5db199SXin Li """ 71*9c5db199SXin Li check if a host is dedicated for firmware testing. 72*9c5db199SXin Li 73*9c5db199SXin Li When this function returns true, the DUT should be managed by 74*9c5db199SXin Li `FirmwareStatusVerifier` and `FaftFirmwareRepair`, but not 75*9c5db199SXin Li `FirmwareVersionVerifier` and `GeneralFirmwareRepair. 76*9c5db199SXin Li 77*9c5db199SXin Li @return A true value if the host should use `FirmwareStatusVerifier` 78*9c5db199SXin Li and `FaftFirmwareRepair`; a false value otherwise. 79*9c5db199SXin Li """ 80*9c5db199SXin Li info = host.host_info_store.get() 81*9c5db199SXin Li return bool(info.pools & _FIRMWARE_REPAIR_POOLS) 82*9c5db199SXin Li 83*9c5db199SXin Li 84*9c5db199SXin Lidef _is_firmware_update_supported(host): 85*9c5db199SXin Li """ 86*9c5db199SXin Li Return whether a DUT should be running the standard firmware. 87*9c5db199SXin Li 88*9c5db199SXin Li In the test lab, DUTs used for general testing, (e.g. the `bvt` 89*9c5db199SXin Li pool) need their firmware kept up-to-date with 90*9c5db199SXin Li `FirmwareVersionVerifier`. However, some pools have alternative 91*9c5db199SXin Li policies for firmware management. This returns whether a given DUT 92*9c5db199SXin Li should be updated via the standard stable version update, or 93*9c5db199SXin Li managed by some other procedure. 94*9c5db199SXin Li 95*9c5db199SXin Li @param host The host to be checked for update policy. 96*9c5db199SXin Li @return A true value if the host should use 97*9c5db199SXin Li `FirmwareVersionVerifier`; a false value otherwise. 98*9c5db199SXin Li """ 99*9c5db199SXin Li return not _is_firmware_testing_device(host) 100*9c5db199SXin Li 101*9c5db199SXin Li 102*9c5db199SXin Lidef _get_available_firmware(host, model): 103*9c5db199SXin Li """Get the available RW firmware version given the model. 104*9c5db199SXin Li 105*9c5db199SXin Li @param host The host to get available firmware for. 106*9c5db199SXin Li @param model The model name to get corresponding firmware version. 107*9c5db199SXin Li @return The available RW firmware version if found, else, None. 108*9c5db199SXin Li """ 109*9c5db199SXin Li result = host.run('chromeos-firmwareupdate --manifest', ignore_status=True) 110*9c5db199SXin Li 111*9c5db199SXin Li if result.exit_status != 0: 112*9c5db199SXin Li return None 113*9c5db199SXin Li 114*9c5db199SXin Li # The manifest is a JSON in .model.host.versions.rw 115*9c5db199SXin Li data = json.loads(result.stdout) or {} 116*9c5db199SXin Li key = model if len(data) > 1 else next(six.iterkeys(data), '') 117*9c5db199SXin Li key += '.host.versions.rw' 118*9c5db199SXin Li for k in key.split('.'): 119*9c5db199SXin Li data = data.get(k, {}) 120*9c5db199SXin Li return data or None 121*9c5db199SXin Li 122*9c5db199SXin Li 123*9c5db199SXin Liclass FirmwareStatusVerifier(hosts.Verifier): 124*9c5db199SXin Li """ 125*9c5db199SXin Li Verify that a host's firmware is in a good state. 126*9c5db199SXin Li 127*9c5db199SXin Li For DUTs that run firmware tests, it's possible that the firmware 128*9c5db199SXin Li on the DUT can get corrupted. This verifier checks whether it 129*9c5db199SXin Li appears that firmware should be re-flashed using servo. 130*9c5db199SXin Li """ 131*9c5db199SXin Li 132*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 133*9c5db199SXin Li def verify(self, host): 134*9c5db199SXin Li if not _is_firmware_testing_device(host): 135*9c5db199SXin Li return 136*9c5db199SXin Li try: 137*9c5db199SXin Li # Read the AP firmware and dump the sections that we're 138*9c5db199SXin Li # interested in. 139*9c5db199SXin Li cmd = ('mkdir /tmp/verify_firmware; ' 140*9c5db199SXin Li 'cd /tmp/verify_firmware; ' 141*9c5db199SXin Li 'for section in VBLOCK_A VBLOCK_B FW_MAIN_A FW_MAIN_B; ' 142*9c5db199SXin Li 'do flashrom -p host -r -i $section:$section; ' 143*9c5db199SXin Li 'done') 144*9c5db199SXin Li host.run(cmd) 145*9c5db199SXin Li 146*9c5db199SXin Li # Verify the firmware blocks A and B. 147*9c5db199SXin Li cmd = ('vbutil_firmware --verify /tmp/verify_firmware/VBLOCK_%c' 148*9c5db199SXin Li ' --signpubkey /usr/share/vboot/devkeys/root_key.vbpubk' 149*9c5db199SXin Li ' --fv /tmp/verify_firmware/FW_MAIN_%c') 150*9c5db199SXin Li for c in ('A', 'B'): 151*9c5db199SXin Li rv = host.run(cmd % (c, c), ignore_status=True) 152*9c5db199SXin Li if rv.exit_status: 153*9c5db199SXin Li raise hosts.AutoservVerifyError( 154*9c5db199SXin Li 'Firmware %c is in a bad state.' % c) 155*9c5db199SXin Li finally: 156*9c5db199SXin Li # Remove the temporary files. 157*9c5db199SXin Li host.run('rm -rf /tmp/verify_firmware') 158*9c5db199SXin Li 159*9c5db199SXin Li @property 160*9c5db199SXin Li def description(self): 161*9c5db199SXin Li return 'Firmware on this DUT is clean' 162*9c5db199SXin Li 163*9c5db199SXin Li 164*9c5db199SXin Liclass FirmwareRepair(hosts.RepairAction): 165*9c5db199SXin Li """ 166*9c5db199SXin Li Reinstall the firmware image using servo. 167*9c5db199SXin Li 168*9c5db199SXin Li This repair function attempts to use servo to install the DUT's 169*9c5db199SXin Li designated "stable firmware version". 170*9c5db199SXin Li 171*9c5db199SXin Li This repair method only applies to DUTs used for FAFT. 172*9c5db199SXin Li """ 173*9c5db199SXin Li 174*9c5db199SXin Li def _get_faft_stable_build(self, host): 175*9c5db199SXin Li info = host.host_info_store.get() 176*9c5db199SXin Li return afe_utils.get_stable_faft_version_v2(info) 177*9c5db199SXin Li 178*9c5db199SXin Li def _get_os_stable_build(self, host): 179*9c5db199SXin Li # Use firmware in current stable os build. 180*9c5db199SXin Li return host.get_cros_repair_image_name() 181*9c5db199SXin Li 182*9c5db199SXin Li def _run_faft_repair(self, host, build): 183*9c5db199SXin Li host.firmware_install(build) 184*9c5db199SXin Li 185*9c5db199SXin Li def _run_general_repair(self, host, build): 186*9c5db199SXin Li # As GeneralFirmwareRepair is the last repair action, we expect 187*9c5db199SXin Li # stable_version os image is loaded on usbkey during other repair 188*9c5db199SXin Li # action runs. And there is also no point to repeat and waste time if 189*9c5db199SXin Li # download image to usbkey failed in other repair actions. 190*9c5db199SXin Li if host._servo_host.validate_image_usbkey() != build: 191*9c5db199SXin Li raise hosts.AutoservRepairError('%s is expected to be preloaded,' 192*9c5db199SXin Li 'however it\'s not found on the usbkey' % build, 193*9c5db199SXin Li 'image not loaded on usbkey') 194*9c5db199SXin Li ec_image, bios_image = host._servo_host.prepare_repair_firmware_image() 195*9c5db199SXin Li 196*9c5db199SXin Li # For EVT device with signed variant exists we skip this repair 197*9c5db199SXin Li # as it's hard to decide which image to use if DUT do not boot. 198*9c5db199SXin Li info = host.host_info_store.get() 199*9c5db199SXin Li phase = info.get_label_value('phase') 200*9c5db199SXin Li if 'signed' in bios_image and phase.lower() in ('evt', 'dvt', ''): 201*9c5db199SXin Li raise hosts.AutoservRepairError( 202*9c5db199SXin Li 'Could not determine which firmware image to use' 203*9c5db199SXin Li ' due to signed firmware image variant exists but' 204*9c5db199SXin Li ' DUT phase is earlier than PVT or missing; Phase' 205*9c5db199SXin Li ' from inventory: %s' % phase, 206*9c5db199SXin Li 'Can not determine variant for EVT device') 207*9c5db199SXin Li 208*9c5db199SXin Li # Before flash firmware we want update the build into health profile. 209*9c5db199SXin Li if host.health_profile: 210*9c5db199SXin Li host.health_profile.set_firmware_stable_version(build) 211*9c5db199SXin Li 212*9c5db199SXin Li if ec_image: 213*9c5db199SXin Li logging.info('Attempting to flash ec firmware...') 214*9c5db199SXin Li host.servo.program_ec(ec_image, copy_image=False) 215*9c5db199SXin Li if bios_image: 216*9c5db199SXin Li logging.info('Attempting to flash bios firmware...') 217*9c5db199SXin Li host._servo_host.flash_ap_firmware_via_servo(bios_image) 218*9c5db199SXin Li 219*9c5db199SXin Li logging.info('Cold resetting DUT through servo...') 220*9c5db199SXin Li host.servo.get_power_state_controller().reset() 221*9c5db199SXin Li host.wait_up(timeout=host.BOOT_TIMEOUT) 222*9c5db199SXin Li # flash firmware via servo will turn DUT into dev mode, so disable 223*9c5db199SXin Li # dev mode and reset gbb flag here. 224*9c5db199SXin Li host.run('/usr/share/vboot/bin/set_gbb_flags.sh 0', ignore_status=True) 225*9c5db199SXin Li host.run('crossystem disable_dev_request=1', ignore_status=True) 226*9c5db199SXin Li host.reboot() 227*9c5db199SXin Li 228*9c5db199SXin Li 229*9c5db199SXin Liclass FaftFirmwareRepair(FirmwareRepair): 230*9c5db199SXin Li """ 231*9c5db199SXin Li Reinstall the firmware for DUTs in faft related pool. 232*9c5db199SXin Li """ 233*9c5db199SXin Li 234*9c5db199SXin Li def repair(self, host): 235*9c5db199SXin Li repair_utils.require_servo(host, ignore_state=True) 236*9c5db199SXin Li build = self._get_faft_stable_build(host) 237*9c5db199SXin Li if build: 238*9c5db199SXin Li self._run_faft_repair(host, build) 239*9c5db199SXin Li else: 240*9c5db199SXin Li logging.info('Cannot find faft stable_version, falling back to' 241*9c5db199SXin Li ' use firmware on OS stable_version.') 242*9c5db199SXin Li build = self._get_os_stable_build(host) 243*9c5db199SXin Li if not build: 244*9c5db199SXin Li raise hosts.AutoservRepairError( 245*9c5db199SXin Li 'Failed to find stable_version from host_info.', 246*9c5db199SXin Li 'cannot find stable_version') 247*9c5db199SXin Li self._run_general_repair(host, build) 248*9c5db199SXin Li 249*9c5db199SXin Li def _is_applicable(self, host): 250*9c5db199SXin Li return _is_firmware_testing_device(host) 251*9c5db199SXin Li 252*9c5db199SXin Li @property 253*9c5db199SXin Li def description(self): 254*9c5db199SXin Li return 'Re-install the stable firmware(faft) via servo' 255*9c5db199SXin Li 256*9c5db199SXin Li 257*9c5db199SXin Liclass GeneralFirmwareRepair(FirmwareRepair): 258*9c5db199SXin Li """Reinstall the firmware for non-faft DUTs. 259*9c5db199SXin Li We need different RepairAction for non firmware testing DUT because 260*9c5db199SXin Li we want only try re-install firmware if all other RepairAction could 261*9c5db199SXin Li not restore ssh capability to the DUT. 262*9c5db199SXin Li """ 263*9c5db199SXin Li 264*9c5db199SXin Li def repair(self, host): 265*9c5db199SXin Li repair_utils.require_servo(host, ignore_state=True) 266*9c5db199SXin Li build = self._get_os_stable_build(host) 267*9c5db199SXin Li if not build: 268*9c5db199SXin Li raise hosts.AutoservRepairError( 269*9c5db199SXin Li 'Failed to find stable_version from host_info.', 270*9c5db199SXin Li 'cannot find stable_version') 271*9c5db199SXin Li self._run_general_repair(host, build) 272*9c5db199SXin Li 273*9c5db199SXin Li def _is_applicable(self, host): 274*9c5db199SXin Li if _is_firmware_testing_device(host): 275*9c5db199SXin Li return False 276*9c5db199SXin Li if not host.servo: 277*9c5db199SXin Li logging.info( 278*9c5db199SXin Li 'The current servo state of %s is not met the' 279*9c5db199SXin Li ' minimum requirement to flash firmware.', host.hostname) 280*9c5db199SXin Li # Flash firmware via servo is consider an expansive opertation, so we 281*9c5db199SXin Li # want to check repair data from previous repairs to determine if 282*9c5db199SXin Li # firmware repair is need. 283*9c5db199SXin Li dhp = host.health_profile 284*9c5db199SXin Li if not dhp: 285*9c5db199SXin Li logging.info('Device health profile is not available, cannot' 286*9c5db199SXin Li ' determine if firmware repair is needed.') 287*9c5db199SXin Li return False 288*9c5db199SXin Li repair_fail_count = dhp.get_repair_fail_count() 289*9c5db199SXin Li if repair_fail_count < 2: 290*9c5db199SXin Li # We want to start with a more conservative strategy, so only try 291*9c5db199SXin Li # this action on DUTs that failed repair at least twice. 292*9c5db199SXin Li # @TODO(xianuowang@) adjust or remove this threshold. 293*9c5db199SXin Li logging.info( 294*9c5db199SXin Li 'Firmware repair will only applies to DUT that' 295*9c5db199SXin Li ' failed at least two AdminRepair, current fail' 296*9c5db199SXin Li ' count: %s', repair_fail_count) 297*9c5db199SXin Li return False 298*9c5db199SXin Li flashed_build = dhp.get_firmware_stable_version() 299*9c5db199SXin Li candidate_build = self._get_os_stable_build(host) 300*9c5db199SXin Li # If we had an success firmware flash in this repair loop, 301*9c5db199SXin Li # there is no need to retry flash the same firmware build. 302*9c5db199SXin Li if (dhp.get_succeed_repair_action(self.tag) > 0 303*9c5db199SXin Li and flashed_build == candidate_build): 304*9c5db199SXin Li logging.info( 305*9c5db199SXin Li 'Firmware from %s has been already installed on %s,' 306*9c5db199SXin Li ' no need to retry.', flashed_build, host.hostname) 307*9c5db199SXin Li return False 308*9c5db199SXin Li if (dhp.get_failed_repair_action(self.tag) > 2 309*9c5db199SXin Li and flashed_build == candidate_build): 310*9c5db199SXin Li logging.info( 311*9c5db199SXin Li 'Firmware from %s has been attempted and failed 3 ' 312*9c5db199SXin Li 'times, no need to retry.', flashed_build) 313*9c5db199SXin Li return False 314*9c5db199SXin Li return True 315*9c5db199SXin Li 316*9c5db199SXin Li @property 317*9c5db199SXin Li def description(self): 318*9c5db199SXin Li return 'Re-install the stable firmware(non-faft) via servo' 319*9c5db199SXin Li 320*9c5db199SXin Li 321*9c5db199SXin Liclass FirmwareVersionVerifier(hosts.Verifier): 322*9c5db199SXin Li """ 323*9c5db199SXin Li Check for a firmware update, and apply it if appropriate. 324*9c5db199SXin Li 325*9c5db199SXin Li This verifier checks to ensure that either the firmware on the DUT 326*9c5db199SXin Li is up-to-date, or that the target firmware can be installed from the 327*9c5db199SXin Li currently running build. 328*9c5db199SXin Li 329*9c5db199SXin Li Failure occurs when all of the following apply: 330*9c5db199SXin Li 1. The DUT is not excluded from updates. For example, DUTs used 331*9c5db199SXin Li for FAFT testing use `FirmwareRepair` instead. 332*9c5db199SXin Li 2. The DUT's board has an assigned stable firmware version. 333*9c5db199SXin Li 3. The DUT is not running the assigned stable firmware. 334*9c5db199SXin Li 4. The firmware supplied in the running OS build is not the 335*9c5db199SXin Li assigned stable firmware. 336*9c5db199SXin Li 337*9c5db199SXin Li If the DUT needs an upgrade and the currently running OS build 338*9c5db199SXin Li supplies the necessary firmware, the verifier installs the new 339*9c5db199SXin Li firmware using `chromeos-firmwareupdate`. Failure to install will 340*9c5db199SXin Li cause the verifier to fail. 341*9c5db199SXin Li 342*9c5db199SXin Li This verifier nominally breaks the rule that "verifiers must succeed 343*9c5db199SXin Li quickly", since it can invoke `reboot()` during the success code 344*9c5db199SXin Li path. We're doing it anyway for two reasons: 345*9c5db199SXin Li * The time between updates will typically be measured in months, 346*9c5db199SXin Li so the amortized cost is low. 347*9c5db199SXin Li * The reason we distinguish repair from verify is to allow 348*9c5db199SXin Li rescheduling work immediately while the expensive repair happens 349*9c5db199SXin Li out-of-band. But a firmware update will likely hit all DUTs at 350*9c5db199SXin Li once, so it's pointless to pass the buck to repair. 351*9c5db199SXin Li 352*9c5db199SXin Li N.B. This verifier is a trigger for all repair actions that install 353*9c5db199SXin Li the stable repair image. If the firmware is out-of-date, but the 354*9c5db199SXin Li stable repair image does *not* contain the proper firmware version, 355*9c5db199SXin Li _the target DUT will fail repair, and will be unable to fix itself_. 356*9c5db199SXin Li """ 357*9c5db199SXin Li 358*9c5db199SXin Li @staticmethod 359*9c5db199SXin Li def _get_rw_firmware(host): 360*9c5db199SXin Li result = host.run('crossystem fwid', ignore_status=True) 361*9c5db199SXin Li if result.exit_status == 0: 362*9c5db199SXin Li return result.stdout 363*9c5db199SXin Li else: 364*9c5db199SXin Li return None 365*9c5db199SXin Li 366*9c5db199SXin Li @staticmethod 367*9c5db199SXin Li def _check_hardware_match(version_a, version_b): 368*9c5db199SXin Li """ 369*9c5db199SXin Li Check that two firmware versions identify the same hardware. 370*9c5db199SXin Li 371*9c5db199SXin Li Firmware version strings look like this: 372*9c5db199SXin Li Google_Gnawty.5216.239.34 373*9c5db199SXin Li The part before the numbers identifies the hardware for which 374*9c5db199SXin Li the firmware was built. This function checks that the hardware 375*9c5db199SXin Li identified by `version_a` and `version_b` is the same. 376*9c5db199SXin Li 377*9c5db199SXin Li This is a confidence check to protect us from installing the wrong 378*9c5db199SXin Li firmware on a DUT when a board label has somehow gone astray. 379*9c5db199SXin Li 380*9c5db199SXin Li @param version_a First firmware version for the comparison. 381*9c5db199SXin Li @param version_b Second firmware version for the comparison. 382*9c5db199SXin Li """ 383*9c5db199SXin Li hardware_a = version_a.split('.')[0] 384*9c5db199SXin Li hardware_b = version_b.split('.')[0] 385*9c5db199SXin Li if hardware_a != hardware_b: 386*9c5db199SXin Li message = 'Hardware/Firmware mismatch updating %s to %s' 387*9c5db199SXin Li raise hosts.AutoservVerifyError( 388*9c5db199SXin Li message % (version_a, version_b)) 389*9c5db199SXin Li 390*9c5db199SXin Li def _is_stable_image_installed(self, host): 391*9c5db199SXin Li """Verify that ChromeOS image on host is a stable version. 392*9c5db199SXin Li 393*9c5db199SXin Li This check verify that device booted from stable image to protect us 394*9c5db199SXin Li from installing the firmware from bad/broken/no-tested image. Bad 395*9c5db199SXin Li image can have broken updater or corrupted firmware. 396*9c5db199SXin Li 397*9c5db199SXin Li The representation version looks like: 398*9c5db199SXin Li nocturne-release/R89-13728.0.0 399*9c5db199SXin Li Check compare version from host to version provide as stable image 400*9c5db199SXin Li from host-info file. 401*9c5db199SXin Li 402*9c5db199SXin Li @param host CrosHost instance. 403*9c5db199SXin Li """ 404*9c5db199SXin Li os_from_host = host.get_release_builder_path() 405*9c5db199SXin Li os_from_host_info = host.get_cros_repair_image_name() 406*9c5db199SXin Li if os_from_host != os_from_host_info: 407*9c5db199SXin Li raise hosts.AutoservNonCriticalVerifyError( 408*9c5db199SXin Li 'Firmware update can be run only from stable image.' 409*9c5db199SXin Li ' Expected version:"%s", actually: "%s"' % 410*9c5db199SXin Li (os_from_host_info, os_from_host)) 411*9c5db199SXin Li 412*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 413*9c5db199SXin Li def verify(self, host): 414*9c5db199SXin Li # Test 1 - The DUT is not excluded from updates. 415*9c5db199SXin Li if not _is_firmware_update_supported(host): 416*9c5db199SXin Li return 417*9c5db199SXin Li # Test 2 - The DUT has an assigned stable firmware version. 418*9c5db199SXin Li info = host.host_info_store.get() 419*9c5db199SXin Li if info.model is None: 420*9c5db199SXin Li raise hosts.AutoservVerifyError( 421*9c5db199SXin Li 'Can not verify firmware version. ' 422*9c5db199SXin Li 'No model label value found') 423*9c5db199SXin Li 424*9c5db199SXin Li stable_firmware = None 425*9c5db199SXin Li try: 426*9c5db199SXin Li stable_firmware = afe_utils.get_stable_firmware_version_v2(info) 427*9c5db199SXin Li except Exception as e: 428*9c5db199SXin Li logging.exception('Failed lookup to AFE for stable fw version ' 429*9c5db199SXin Li ' with exception: %s', e) 430*9c5db199SXin Li 431*9c5db199SXin Li if stable_firmware is None: 432*9c5db199SXin Li logging.debug('Expected FW version not found') 433*9c5db199SXin Li # This DUT doesn't have a firmware update target 434*9c5db199SXin Li return 435*9c5db199SXin Li logging.debug('Expected FW version: %s', stable_firmware) 436*9c5db199SXin Li # For tests 3 and 4: If the output from `crossystem` or 437*9c5db199SXin Li # `chromeos-firmwareupdate` isn't what we expect, we log an 438*9c5db199SXin Li # error, but don't fail: We don't want DUTs unable to test a 439*9c5db199SXin Li # build merely because of a bug or change in either of those 440*9c5db199SXin Li # commands. 441*9c5db199SXin Li 442*9c5db199SXin Li # Test 3 - The DUT is not running the target stable firmware. 443*9c5db199SXin Li current_firmware = self._get_rw_firmware(host) 444*9c5db199SXin Li if current_firmware is None: 445*9c5db199SXin Li logging.error('DUT firmware version can\'t be determined.') 446*9c5db199SXin Li return 447*9c5db199SXin Li logging.debug('Current FW version: %s', current_firmware) 448*9c5db199SXin Li if current_firmware == stable_firmware: 449*9c5db199SXin Li return 450*9c5db199SXin Li # Test 4 - The firmware supplied in the running OS build is not 451*9c5db199SXin Li # the assigned stable firmware. 452*9c5db199SXin Li available_firmware = _get_available_firmware(host, info.model) 453*9c5db199SXin Li if available_firmware is None: 454*9c5db199SXin Li logging.error('Supplied firmware version in OS can\'t be ' 455*9c5db199SXin Li 'determined.') 456*9c5db199SXin Li return 457*9c5db199SXin Li self._is_stable_image_installed(host) 458*9c5db199SXin Li if available_firmware != stable_firmware: 459*9c5db199SXin Li raise hosts.AutoservVerifyError( 460*9c5db199SXin Li 'DUT firmware requires update from %s to %s' % 461*9c5db199SXin Li (current_firmware, stable_firmware)) 462*9c5db199SXin Li # Time to update the firmware. 463*9c5db199SXin Li logging.info('Updating firmware from %s to %s', 464*9c5db199SXin Li current_firmware, stable_firmware) 465*9c5db199SXin Li self._check_hardware_match(current_firmware, stable_firmware) 466*9c5db199SXin Li try: 467*9c5db199SXin Li host.run('chromeos-firmwareupdate --mode=autoupdate') 468*9c5db199SXin Li host.reboot() 469*9c5db199SXin Li except Exception as e: 470*9c5db199SXin Li message = ('chromeos-firmwareupdate failed: from ' 471*9c5db199SXin Li '%s to %s') 472*9c5db199SXin Li logging.exception(message, current_firmware, stable_firmware) 473*9c5db199SXin Li raise hosts.AutoservVerifyError( 474*9c5db199SXin Li message % (current_firmware, stable_firmware)) 475*9c5db199SXin Li final_firmware = self._get_rw_firmware(host) 476*9c5db199SXin Li if final_firmware != stable_firmware: 477*9c5db199SXin Li message = ('chromeos-firmwareupdate failed: tried upgrade ' 478*9c5db199SXin Li 'to %s, now running %s instead') 479*9c5db199SXin Li raise hosts.AutoservVerifyError( 480*9c5db199SXin Li message % (stable_firmware, final_firmware)) 481*9c5db199SXin Li 482*9c5db199SXin Li @property 483*9c5db199SXin Li def description(self): 484*9c5db199SXin Li return 'The firmware on this DUT is up-to-date' 485