| # Copyright 2017 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. |
| |
| |
| """ The autotest performing Cr50 update.""" |
| |
| |
| import os |
| import logging |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib.cros import cr50_utils |
| from autotest_lib.server.cros.faft.cr50_test import Cr50Test |
| |
| |
| class provision_Cr50Update(Cr50Test): |
| """A test that can provision a machine to the correct cr50 version. |
| |
| Take value and split it into image_rw_ver, image_bid, and chip bid if it |
| is specifying a prod signed image. If it requests a selfsigned image split |
| it into the dev server build info and chip board id. |
| |
| Update Cr50 so it is running the correct image with the right chip baord id. |
| |
| value=prodsigned-0.0.23(/ZZAF:ffffffff:7f00/ZZAF:7f80)? |
| value=selfsigned-reef-release/R61-9704.0.0(/ZZAF:7f80)? |
| |
| """ |
| |
| version = 1 |
| |
| def initialize(self, host, cmdline_args, value='', release_path='', |
| chip_bid_str='', dev_path=''): |
| """Initialize get the cr50 update version information""" |
| super(provision_Cr50Update, self).initialize(host, cmdline_args, |
| provision_update=True, cr50_dev_path=dev_path) |
| self.host = host |
| self.chip_bid_str = chip_bid_str |
| |
| image_info = None |
| |
| if os.path.isfile(release_path): |
| image_info = self.get_local_image(release_path) |
| else: |
| source, test_info = value.split('-', 1) |
| |
| # The chip board id is optional so value can be |
| # 'ver_part_1/ver_part_2/chip_bid' or 'ver_part_1/ver_part_2'. |
| # |
| # Get the chip bid from test_info if it was included. |
| if test_info.count('/') == 2: |
| test_info, self.chip_bid_str = test_info.rsplit('/', 1) |
| |
| if source == 'prodsigned': |
| image_info = self.get_prodsigned_image(test_info) |
| if source == 'selfsigned': |
| image_info = self.get_selfsigned_image(test_info) |
| |
| if not image_info: |
| raise error.TestError('Could not find new cr50 image') |
| |
| self.local_path, self.image_ver = image_info |
| self.image_rw = self.image_ver[1] |
| self.image_bid = self.image_ver[2] |
| |
| |
| def get_local_image(self, release_path): |
| """Get the version of the local image. |
| |
| Args: |
| release_path: The local path to the cr50 image |
| |
| Returns: |
| the local path, image version tuple |
| """ |
| ver = cr50_utils.InstallImage(self.host, release_path, |
| '/tmp/release.bin')[1] |
| return release_path, ver |
| |
| |
| def get_prodsigned_image(self, value): |
| """Find the release image. |
| |
| Args: |
| value: The Cr50 image version info rw_ver/image_bid. The image_bid |
| is optional |
| |
| Returns: |
| the local path, image version tuple |
| """ |
| release_bid_str = None |
| values = value.split('/') |
| release_ver = values[0] |
| |
| if len(values) > 1 and values[1]: |
| release_bid_str = values[1] |
| |
| return self.download_cr50_release_image(release_ver, |
| release_bid_str) |
| |
| |
| def get_selfsigned_image(self, value): |
| """Find the selfsigned image image. |
| |
| The value should be something like reef-release/R61-9704.0.0 |
| |
| Args: |
| value: A string with the build to extract the cr50 image from. |
| |
| Returns: |
| the local path, image version tuple |
| |
| Raises: |
| TestError because it is not yet implemented |
| """ |
| # TODO(mruthven): Add support for downloading the image from the |
| # devserver and extracting the cr50 image. |
| raise error.TestError('No support for finding %s', value) |
| |
| |
| def check_bid_settings(self, chip_bid_info, image_bid_str): |
| """Compare the chip and image board ids. |
| |
| Compare the image and chip board ids before changing the cr50 state. |
| Raise an error if the image will not be able to run with the requested |
| chip board id. |
| |
| Args: |
| chip_bid_info: The chip board id info tuple |
| image_bid_str: the image board_id:mask:flags. |
| |
| Raises: |
| TestFail if we will not be able to update to the image with the |
| given chip board id. |
| """ |
| # If the image isn't board id locked, it will run on all devices. |
| if chip_bid_info == cr50_utils.ERASED_CHIP_BID: |
| logging.info('Chip has no board id. It will run any image.') |
| return |
| if not image_bid_str: |
| logging.info('Image is not board id locked. It will run on all ' |
| 'devices.') |
| return |
| |
| chip_bid, chip_bid_inv, chip_flags = chip_bid_info |
| chip_bid_str = cr50_utils.GetBoardIdInfoString(chip_bid_info, |
| symbolic=True) |
| |
| image_bid, image_mask, image_flags = image_bid_str.split(':') |
| |
| # Convert the image board id to integers |
| image_mask = int(image_mask, 16) |
| image_bid = cr50_utils.GetIntBoardId(image_bid) |
| image_flags = int(image_flags, 16) |
| |
| errors = [] |
| # All bits in the image mask must match between the image and chip board |
| # ids. |
| image_bid = image_bid & image_mask |
| chip_bid = chip_bid & image_mask |
| if image_bid != chip_bid: |
| errors.append('board id') |
| # All 1s in the image flags must also be 1 in the chip flags |
| chip_flags = chip_flags & image_flags |
| if image_flags != chip_flags: |
| errors.append('flags') |
| if len(errors): |
| raise error.TestFail('Image will not be able to run with the ' |
| 'given %s: chip %s image %s' % (' and '.join(errors), |
| chip_bid_str, image_bid_str)) |
| |
| |
| def get_new_chip_bid(self): |
| """Get the new chip board id and flags. |
| |
| Returns: |
| a tuple chip bid info, a bool True if the chip board id needs to |
| change. |
| """ |
| chip_bid_info = self.chip_bid_str.split(':') |
| running_bid_info = cr50_utils.GetChipBoardId(self.host) |
| |
| # If no board id was specified, restore the original board id |
| if len(chip_bid_info) != 2 or not chip_bid_info[0]: |
| logging.info('No board id given. Using the current chip settings ' |
| '%s', running_bid_info) |
| return running_bid_info, False |
| |
| chip_bid = cr50_utils.GetIntBoardId(chip_bid_info[0]) |
| chip_flags = int(chip_bid_info[1], 16) |
| |
| chip_bid_info = (chip_bid, 0xffffffff ^ chip_bid, chip_flags) |
| set_bid = chip_bid_info != running_bid_info |
| return chip_bid_info, set_bid |
| |
| |
| def check_final_state(self, chip_bid_info): |
| """Verify the update checking the chip board id and running image |
| |
| Args: |
| chip_bid_info: A tuple of ints: chip_board_id, ~chip_board_id, |
| and flags. |
| |
| Raises: |
| TestFail if the device did not update to the correct state |
| """ |
| state = self.get_cr50_device_state() |
| image_bid = cr50_utils.GetBoardIdInfoString(self.image_bid) |
| |
| failed = [] |
| if chip_bid_info != state['chip_bid']: |
| failed.append('cr50 chip board id') |
| if image_bid != state['cr50_image_bid']: |
| failed.append('cr50 image board id') |
| if self.image_rw != state['running_ver'][1]: |
| failed.append('cr50 image version') |
| if self.image_ver != state['device_image_ver']: |
| failed.append('device image') |
| if len(failed): |
| raise error.TestFail('Update failures: %s', ', '.join(failed)) |
| |
| |
| def run_once(self): |
| """The method called by the control file to start the update.""" |
| chip_bid_info, set_bid = self.get_new_chip_bid() |
| |
| logging.info('Updating to image %s with chip board id %s', |
| self.image_ver, cr50_utils.GetBoardIdInfoString(chip_bid_info)) |
| |
| # Make sure the image will be able to run with the given chip board id. |
| self.check_bid_settings(chip_bid_info, self.image_bid) |
| |
| # If the release version is not newer than the running rw version, we |
| # have to do a rollback. |
| running_rw = cr50_utils.GetRunningVersion(self.host)[1] |
| rollback = (cr50_utils.GetNewestVersion(running_rw, self.image_rw) != |
| self.image_rw) |
| |
| # You can only update the board id or update to an old image by rolling |
| # back from a dev image. |
| need_rollback = rollback or set_bid |
| if need_rollback and not self.has_saved_cr50_dev_path(): |
| raise error.TestFail('Need a dev image to rollback to %s or update' |
| 'the board id') |
| # Copy the image onto the DUT. The image is stored in |
| # /opt/google/cr50/firmware/cr50.bin.prod, so rootfs verification has |
| # to be disabled before the copy. |
| self.rootfs_verification_disable() |
| cr50_utils.InstallImage(self.host, self.local_path) |
| |
| # Update to the dev image if there needs to be a rollback. |
| if need_rollback: |
| dev_path = self.get_saved_cr50_dev_path() |
| self.cr50_update(dev_path) |
| |
| # If we aren't changing the board id, don't pass any values into the bid |
| # args. |
| chip_bid = chip_bid_info[0] if need_rollback else None |
| chip_flags = chip_bid_info[2] if need_rollback else None |
| # Update to the new image, setting the chip board id and rolling back if |
| # necessary. |
| self.cr50_update(self.local_path, rollback=need_rollback, |
| chip_bid=chip_bid, chip_flags=chip_flags) |
| |
| cr50_utils.ClearUpdateStateAndReboot(self.host) |
| |
| # Verify everything updated correctly |
| self.check_final_state(chip_bid_info) |