1# Copyright 2017 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5 6""" The autotest performing Cr50 update.""" 7 8 9import os 10import logging 11 12from autotest_lib.client.common_lib import error 13from autotest_lib.client.common_lib.cros import cr50_utils 14from autotest_lib.server.cros.faft.cr50_test import Cr50Test 15 16 17class provision_Cr50Update(Cr50Test): 18 """A test that can provision a machine to the correct cr50 version. 19 20 Take value and split it into image_rw_ver, image_bid, and chip bid if it 21 is specifying a prod signed image. If it requests a selfsigned image split 22 it into the dev server build info and chip board id. 23 24 Update Cr50 so it is running the correct image with the right chip baord id. 25 26 value=prodsigned-0.0.23(/ZZAF:ffffffff:7f00/ZZAF:7f80)? 27 value=selfsigned-reef-release/R61-9704.0.0(/ZZAF:7f80)? 28 29 """ 30 31 version = 1 32 33 def initialize(self, host, cmdline_args, value='', release_path='', 34 chip_bid_str='', dev_path=''): 35 """Initialize get the cr50 update version information""" 36 super(provision_Cr50Update, self).initialize(host, cmdline_args, 37 provision_update=True, cr50_dev_path=dev_path) 38 self.host = host 39 self.chip_bid_str = chip_bid_str 40 41 image_info = None 42 43 if os.path.isfile(release_path): 44 image_info = self.get_local_image(release_path) 45 else: 46 source, test_info = value.split('-', 1) 47 48 # The chip board id is optional so value can be 49 # 'ver_part_1/ver_part_2/chip_bid' or 'ver_part_1/ver_part_2'. 50 # 51 # Get the chip bid from test_info if it was included. 52 if test_info.count('/') == 2: 53 test_info, self.chip_bid_str = test_info.rsplit('/', 1) 54 55 if source == 'prodsigned': 56 image_info = self.get_prodsigned_image(test_info) 57 if source == 'selfsigned': 58 image_info = self.get_selfsigned_image(test_info) 59 60 if not image_info: 61 raise error.TestError('Could not find new cr50 image') 62 63 self.local_path, self.image_ver = image_info 64 self.image_rw = self.image_ver[1] 65 self.image_bid = self.image_ver[2] 66 67 68 def get_local_image(self, release_path): 69 """Get the version of the local image. 70 71 Args: 72 release_path: The local path to the cr50 image 73 74 Returns: 75 the local path, image version tuple 76 """ 77 ver = cr50_utils.InstallImage(self.host, release_path, 78 '/tmp/release.bin')[1] 79 return release_path, ver 80 81 82 def get_prodsigned_image(self, value): 83 """Find the release image. 84 85 Args: 86 value: The Cr50 image version info rw_ver/image_bid. The image_bid 87 is optional 88 89 Returns: 90 the local path, image version tuple 91 """ 92 release_bid_str = None 93 values = value.split('/') 94 release_ver = values[0] 95 96 if len(values) > 1 and values[1]: 97 release_bid_str = values[1] 98 99 return self.download_cr50_release_image(release_ver, 100 release_bid_str) 101 102 103 def get_selfsigned_image(self, value): 104 """Find the selfsigned image image. 105 106 The value should be something like reef-release/R61-9704.0.0 107 108 Args: 109 value: A string with the build to extract the cr50 image from. 110 111 Returns: 112 the local path, image version tuple 113 114 Raises: 115 TestError because it is not yet implemented 116 """ 117 # TODO(mruthven): Add support for downloading the image from the 118 # devserver and extracting the cr50 image. 119 raise error.TestError('No support for finding %s', value) 120 121 122 def check_bid_settings(self, chip_bid_info, image_bid_str): 123 """Compare the chip and image board ids. 124 125 Compare the image and chip board ids before changing the cr50 state. 126 Raise an error if the image will not be able to run with the requested 127 chip board id. 128 129 Args: 130 chip_bid_info: The chip board id info tuple 131 image_bid_str: the image board_id:mask:flags. 132 133 Raises: 134 TestFail if we will not be able to update to the image with the 135 given chip board id. 136 """ 137 # If the image isn't board id locked, it will run on all devices. 138 if chip_bid_info == cr50_utils.ERASED_CHIP_BID: 139 logging.info('Chip has no board id. It will run any image.') 140 return 141 if not image_bid_str: 142 logging.info('Image is not board id locked. It will run on all ' 143 'devices.') 144 return 145 146 chip_bid, chip_bid_inv, chip_flags = chip_bid_info 147 chip_bid_str = cr50_utils.GetBoardIdInfoString(chip_bid_info, 148 symbolic=True) 149 150 image_bid, image_mask, image_flags = image_bid_str.split(':') 151 152 # Convert the image board id to integers 153 image_mask = int(image_mask, 16) 154 image_bid = cr50_utils.GetIntBoardId(image_bid) 155 image_flags = int(image_flags, 16) 156 157 errors = [] 158 # All bits in the image mask must match between the image and chip board 159 # ids. 160 image_bid = image_bid & image_mask 161 chip_bid = chip_bid & image_mask 162 if image_bid != chip_bid: 163 errors.append('board id') 164 # All 1s in the image flags must also be 1 in the chip flags 165 chip_flags = chip_flags & image_flags 166 if image_flags != chip_flags: 167 errors.append('flags') 168 if len(errors): 169 raise error.TestFail('Image will not be able to run with the ' 170 'given %s: chip %s image %s' % (' and '.join(errors), 171 chip_bid_str, image_bid_str)) 172 173 174 def get_new_chip_bid(self): 175 """Get the new chip board id and flags. 176 177 Returns: 178 a tuple chip bid info, a bool True if the chip board id needs to 179 change. 180 """ 181 chip_bid_info = self.chip_bid_str.split(':') 182 running_bid_info = cr50_utils.GetChipBoardId(self.host) 183 184 # If no board id was specified, restore the original board id 185 if len(chip_bid_info) != 2 or not chip_bid_info[0]: 186 logging.info('No board id given. Using the current chip settings ' 187 '%s', running_bid_info) 188 return running_bid_info, False 189 190 chip_bid = cr50_utils.GetIntBoardId(chip_bid_info[0]) 191 chip_flags = int(chip_bid_info[1], 16) 192 193 chip_bid_info = (chip_bid, 0xffffffff ^ chip_bid, chip_flags) 194 set_bid = chip_bid_info != running_bid_info 195 return chip_bid_info, set_bid 196 197 198 def check_final_state(self, chip_bid_info): 199 """Verify the update checking the chip board id and running image 200 201 Args: 202 chip_bid_info: A tuple of ints: chip_board_id, ~chip_board_id, 203 and flags. 204 205 Raises: 206 TestFail if the device did not update to the correct state 207 """ 208 state = self.get_cr50_device_state() 209 image_bid = cr50_utils.GetBoardIdInfoString(self.image_bid) 210 211 failed = [] 212 if chip_bid_info != state['chip_bid']: 213 failed.append('cr50 chip board id') 214 if image_bid != state['cr50_image_bid']: 215 failed.append('cr50 image board id') 216 if self.image_rw != state['running_ver'][1]: 217 failed.append('cr50 image version') 218 if self.image_ver != state['device_prod_ver']: 219 failed.append('device prod image') 220 if self.image_ver != state['device_prepvt_ver']: 221 failed.append('device prepvt image') 222 if len(failed): 223 raise error.TestFail('Update failures: %s', ', '.join(failed)) 224 225 226 def run_once(self): 227 """The method called by the control file to start the update.""" 228 chip_bid_info, set_bid = self.get_new_chip_bid() 229 230 logging.info('Updating to image %s with chip board id %s', 231 self.image_ver, cr50_utils.GetBoardIdInfoString(chip_bid_info)) 232 233 # Make sure the image will be able to run with the given chip board id. 234 self.check_bid_settings(chip_bid_info, self.image_bid) 235 236 # If the release version is not newer than the running rw version, we 237 # have to do a rollback. 238 running_rw = cr50_utils.GetRunningVersion(self.host)[1] 239 rollback = (cr50_utils.GetNewestVersion(running_rw, self.image_rw) != 240 self.image_rw) 241 242 # You can only update the board id or update to an old image by rolling 243 # back from a dev image. 244 need_rollback = rollback or set_bid 245 if need_rollback and not self.has_saved_cr50_dev_path(): 246 raise error.TestFail('Need a dev image to rollback to %s or update' 247 'the board id') 248 # Copy the image onto the DUT. cr50-update uses both cr50.bin.prod and 249 # cr50.bin.prepvt in /opt/google/cr50/firmware/, so copy it to both 250 # places. Rootfs verification has to be disabled to do the copy. 251 self.rootfs_verification_disable() 252 cr50_utils.InstallImage(self.host, self.local_path, 253 cr50_utils.CR50_PREPVT) 254 cr50_utils.InstallImage(self.host, self.local_path, 255 cr50_utils.CR50_PROD) 256 257 # Update to the dev image if there needs to be a rollback. 258 if need_rollback: 259 dev_path = self.get_saved_cr50_dev_path() 260 self.cr50_update(dev_path) 261 262 # If we aren't changing the board id, don't pass any values into the bid 263 # args. 264 chip_bid = chip_bid_info[0] if need_rollback else None 265 chip_flags = chip_bid_info[2] if need_rollback else None 266 # Update to the new image, setting the chip board id and rolling back if 267 # necessary. 268 self.cr50_update(self.local_path, rollback=need_rollback, 269 chip_bid=chip_bid, chip_flags=chip_flags) 270 271 cr50_utils.ClearUpdateStateAndReboot(self.host) 272 273 # Verify everything updated correctly 274 self.check_final_state(chip_bid_info) 275