blob: 6c21a66db4be7184bffb2707bd9037bfed546778 [file] [log] [blame]
# 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)