blob: 56e0c51b1813b1f7f3c142dcb1f6c20130f2dc68 [file] [log] [blame]
# Copyright 2016 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.
import logging
from autotest_lib.client.common_lib import error
from autotest_lib.server import autotest
from autotest_lib.server import test
from autotest_lib.server.cros.faft.rpc_proxy import RPCProxy
class platform_LabFirmwareUpdate(test.test):
"""For test or lab devices. Test will fail if Software write protection
is enabled. Test will compare the installed firmware to those in
the shellball. If differ, execute chromeos-firmwareupdate
--mode=recovery to reset RO and RW firmware. Basic procedure are:
- check software write protect, if enable, attemp reset.
- fail test if software write protect is enabled.
- check if ec is available on DUT.
- get RO, RW versions of firmware, if RO != RW, update=True
- get shellball versions of firmware
- compare shellball version to DUT, update=True if shellball != DUT.
- run chromeos-firwmareupdate --mode=recovery if update==True
- reboot
version = 1
# TODO(kmshelton): Move most of the logic in this test to a unit tested
# library.
def initialize(self, host): = host
# Make sure the client library is on the device so that the proxy
# code is there when we try to call it.
client_at = autotest.Autotest(
self.faft_client = RPCProxy(
# Check if EC, PD is available.
# Check if DUT software write protect is disabled, failed otherwise.
self._run_cmd('flashrom -p host --wp-status', checkfor='is disabled')
self.has_ec = False
mosys_output = self._run_cmd('mosys')
if 'EC information' in mosys_output:
self.has_ec = True
self._run_cmd('flashrom -p ec --wp-status', checkfor='is disabled')
def _run_cmd(self, command, checkfor=''):
"""Run command on dut and return output.
Optionally check output contain string 'checkfor'.
"""'Execute: %s', command)
output =, ignore_status=True).stdout'Output: %s', output.split('\n'))
if checkfor and checkfor not in ''.join(output):
raise error.TestFail('Expect %s in output of %s' %
(checkfor, ' '.join(output)))
return output
def _get_version(self):
"""Retrive RO, RW EC/PD version."""
ro = None
rw = None
lines = self._run_cmd('ectool version', checkfor='version')
for line in lines.splitlines():
if line.startswith('RO version:'):
parts = line.split(':')
ro = parts[1].strip()
if line.startswith('RW version:'):
parts = line.split(':')
rw = parts[1].strip()
return (ro, rw)
def _bios_version(self):
"""Retrive RO, RW BIOS version."""
ro = self.faft_client.system.get_crossystem_value('ro_fwid')
rw = self.faft_client.system.get_crossystem_value('fwid')
return (ro, rw)
def _construct_fw_version(self, fw_ro, fw_rw):
"""Construct a firmware version string in a consistent manner.
@param fw_ro: A string representing the version of a read-only
@param fw_rw: A string representing the version of a read-write
@returns a string constructed from fw_ro and fw_rw
if fw_ro == fw_rw:
return fw_rw
return '%s,%s' % (fw_ro, fw_rw)
def _get_version_all(self):
"""Retrive BIOS, EC, and PD firmware version.
@return firmware version tuple (bios, ec)
bios_version = None
ec_version = None
if self.has_ec:
(ec_ro, ec_rw) = self._get_version()
ec_version = self._construct_fw_version(ec_ro, ec_rw)'Installed EC version: %s', ec_version)
(bios_ro, bios_rw) = self._bios_version()
bios_version = self._construct_fw_version(bios_ro, bios_rw)'Installed BIOS version: %s', bios_version)
return (bios_version, ec_version)
def _get_shellball_version(self):
"""Get shellball firmware version.
@return shellball firmware version tuple (bios, ec)
bios = None
ec = None
bios_ro = None
bios_rw = None
ec_ro = None
ec_rw = None
shellball = self._run_cmd('/usr/sbin/chromeos-firmwareupdate -V')
# TODO(kmshelton): Add a structured output option (likely a protobuf)
# to chromeos-firmwareupdate so the below can become less fragile.
for line in shellball.splitlines():
if line.startswith('BIOS version:'):
parts = line.split(':')
bios_ro = parts[1].strip()'shellball ro bios %s', bios_ro)
if line.startswith('BIOS (RW) version:'):
parts = line.split(':')
bios_rw = parts[1].strip()'shellball rw bios %s', bios_rw)
if line.startswith('EC version:'):
parts = line.split(':')
ec_ro = parts[1].strip()'shellball ro ec %s', ec_ro)
elif line.startswith('EC (RW) version:'):
parts = line.split(':')
ec_rw = parts[1].strip()'shellball rw ec %s', ec_rw)
# Shellballs do not always contain a RW version.
if bios_rw is not None:
bios = self._construct_fw_version(bios_ro, bios_rw)
bios = bios_ro
if ec_rw is not None:
ec = self._construct_fw_version(ec_ro, ec_rw)
ec = ec_ro
return (bios, ec)
def run_once(self, replace=True):
# Get DUT installed firmware versions.
(installed_bios, installed_ec) = self._get_version_all()
# Get shellball firmware versions.
(shball_bios, shball_ec) = self._get_shellball_version()
# Figure out if update is needed.
need_update = False
if installed_bios != shball_bios:
need_update = True'BIOS mismatch %s, will update to %s',
installed_bios, shball_bios)
if installed_ec and installed_ec != shball_ec:
need_update = True'EC mismatch %s, will update to %s',
installed_ec, shball_ec)
# Update and reboot if needed.
if need_update:
output = self._run_cmd('/usr/sbin/chromeos-firmwareupdate '
' --mode=recovery', '(recovery) completed.')
# Check that installed firmware match the shellball.
(bios, ec) = self._get_version_all()
# TODO(kmshelton): Refactor this test to use named tuples so that
# the comparison is eaiser to grok.
if (bios != shball_bios or ec != shball_ec):'shball bios/ec: %s/%s',
shball_bios, shball_ec)'installed bios/ec: %s/%s', bios, ec)
raise error.TestFail('Version mismatch after firmware update')'*** Done firmware updated to match shellball. ***')
else:'*** No firmware update is needed. ***')