• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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