| # 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. |
| |
| """Update Cr50 firmware. |
| |
| Description |
| ----------- |
| This test calls `gsctool` on DUT to update Cr50 firmware. The Cr50 firmware |
| image to update is either from a given path in station or DUT, or from the |
| release partition on DUT. |
| |
| To prepare Cr50 firmware image on station, download the release image with |
| desired Cr50 firmware image and find the image in DEFAULT_FIRMWARE_PATH below. |
| |
| Test Procedure |
| -------------- |
| This is an automatic test that doesn't need any user interaction. |
| |
| 1. Firstly, this test will create a DUT link. |
| 2. If Cr50 firmware image source is from station, the image would be sent to |
| DUT. |
| 3. DUT runs `gsctool` to update Cr50 firmware using the specified cr50 image. |
| If the cr50 image is in release partition, the test will also mount the |
| release partition. |
| |
| Dependency |
| ---------- |
| - DUT link must be ready before running this test. |
| - `gsctool` on DUT. |
| - Cr50 firmware image must be prepared. |
| |
| Examples |
| -------- |
| To update Cr50 firmware with the Cr50 firmware image in DUT release partition, |
| add this in test list:: |
| |
| { |
| "pytest_name": "update_cr50_firmware" |
| } |
| |
| To update Cr50 firmware with the Cr50 firmware image in station:: |
| |
| { |
| "pytest_name": "update_cr50_firmware", |
| "args": { |
| "firmware_file": "/path/on/station/to/cr50.bin.prod", |
| "from_release": false |
| } |
| } |
| """ |
| |
| import logging |
| import os |
| import re |
| |
| import factory_common # pylint: disable=unused-import |
| from cros.factory.device import device_utils |
| from cros.factory.test import test_case |
| from cros.factory.test import test_ui |
| from cros.factory.testlog import testlog |
| from cros.factory.utils.arg_utils import Arg |
| from cros.factory.utils import sys_utils |
| |
| |
| GSCTOOL = '/usr/sbin/gsctool' |
| DEFAULT_FIRMWARE_PATH = '/opt/google/cr50/firmware/cr50.bin.prod' |
| BOARD_ID_FLAG_RE = re.compile(r'^IMAGE_BID_FLAGS=([0-9a-f]*)', re.MULTILINE) |
| PREPVT_FLAG_MASK = 0x7F |
| |
| |
| class UpdateCr50FirmwareTest(test_case.TestCase): |
| ARGS = [ |
| Arg('firmware_file', str, 'The full path of the firmware.', |
| default=None), |
| Arg('from_release', bool, 'Find the firmware from release rootfs.', |
| default=True), |
| Arg('skip_prepvt_flag_check', |
| bool, 'Skip prepvt flag check. For non-dogfood devcies, ' |
| 'we should always use prod firmware, rather than prepvt one. ' |
| 'A dogfood device can use prod firmware, as long as the board id' |
| 'setting is correct. The dogfood device will update to the prepvt ' |
| 'firmware when first time boot to recovery image. ' |
| 'http://crbug.com/802235', |
| default=False) |
| ] |
| |
| ui_class = test_ui.ScrollableLogUI |
| |
| def setUp(self): |
| self.dut = device_utils.CreateDUTInterface() |
| def runTest(self): |
| """Update Cr50 firmware.""" |
| if self.args.firmware_file is None: |
| self.assertEqual( |
| self.args.from_release, True, |
| 'Must set "from_release" to True if not specifiying firmware_file') |
| self.args.firmware_file = DEFAULT_FIRMWARE_PATH |
| |
| self.assertEqual(self.args.firmware_file[0], '/', |
| 'firmware_file should be a full path') |
| |
| self._LogCr50Info() |
| |
| if self.args.from_release: |
| with sys_utils.MountPartition( |
| self.dut.partitions.RELEASE_ROOTFS.path, dut=self.dut) as root: |
| self._UpdateCr50Firmware( |
| os.path.join(root, self.args.firmware_file[1:])) |
| else: |
| if self.dut.link.IsLocal(): |
| self._UpdateCr50Firmware(self.args.firmware_file) |
| else: |
| with self.dut.temp.TempFile() as dut_temp_file: |
| self.dut.SendFile(self.args.firmware_file, dut_temp_file) |
| self._UpdateCr50Firmware(dut_temp_file) |
| |
| def _LogCr50Info(self): |
| # Report running Cr50 firmware versions |
| self.ui.PipeProcessOutputToUI([GSCTOOL, '-a', '-f']) |
| # Get Info1 board ID fields |
| self.ui.PipeProcessOutputToUI([GSCTOOL, '-a', '-i']) |
| |
| def _IsPrePVTFirmware(self, firmware_file): |
| p = self.dut.CheckOutput([GSCTOOL, '-b', '-M', firmware_file]).strip() |
| board_id_flag = int(BOARD_ID_FLAG_RE.search(p).group(1), 16) |
| logging.info('Cr50 firmware board ID flag: %s', hex(board_id_flag)) |
| testlog.LogParam('cr50_firmware_file_info', p) |
| testlog.UpdateParam('cr50_firmware_file_info', |
| description='Output of gsctool -b -M.') |
| return board_id_flag & PREPVT_FLAG_MASK |
| |
| def _UpdateCr50Firmware(self, firmware_file): |
| if not self.args.skip_prepvt_flag_check: |
| if self._IsPrePVTFirmware(firmware_file): |
| raise ValueError('Cr50 firmware board ID flag is PrePVT.') |
| # Get current version, PipeProcessOutputToUI will log the output. |
| self.ui.PipeProcessOutputToUI([GSCTOOL, '-a', '-f']) |
| cmd = [GSCTOOL, '-a', '-u', firmware_file] |
| returncode = self.ui.PipeProcessOutputToUI(cmd) |
| # `gsctool -a -u` return values: |
| # 0: noop. 1: all_updated, 2: rw_updated, 3: update_error |
| # See platform/ec/extra/usb_updater/gsctool.h for more detail. |
| self.assertTrue(0 <= returncode <= 2, |
| 'Cr50 firmware update failed: %d.' % returncode) |