blob: dd25a333d37df2c5947dae692b5dcbd6a0ef9089 [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
def initialize(self, host):
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.host)
client_at.install()
self.faft_client = RPCProxy(self.host)
# 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'.
"""
logging.info('Execute: %s', command)
output = self.host.run(command, ignore_status=True).stdout
logging.info('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
firmware.
@param fw_rw: A string representing the version of a read-write
firmware.
@returns a string constructed from fw_ro and fw_rw
"""
if fw_ro == fw_rw:
return fw_rw
else:
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)
logging.info('Installed EC version: %s', ec_version)
(bios_ro, bios_rw) = self._bios_version()
bios_version = self._construct_fw_version(bios_ro, bios_rw)
logging.info('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)
"""
ec = None
bios = None
bios_ro = None
bios_rw = None
shellball = self._run_cmd('/usr/sbin/chromeos-firmwareupdate -V')
for line in shellball.splitlines():
if line.startswith('BIOS version:'):
parts = line.split(':')
bios_ro = parts[1].strip()
logging.info('shellball ro bios %s', bios_ro)
if line.startswith('BIOS (RW) version:'):
parts = line.split(':')
bios_rw = parts[1].strip()
logging.info('shellball rw bios %s', bios_rw)
elif line.startswith('EC version:'):
parts = line.split(':')
ec = parts[1].strip()
logging.info('shellball ec %s', ec)
# Shellballs do not always contain a RW version.
if bios_rw is not None:
bios = self._construct_fw_version(bios_ro, bios_rw)
else:
bios = bios_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
logging.info('BIOS mismatch %s, will update to %s',
installed_bios, shball_bios)
if installed_ec and installed_ec != shball_ec:
need_update = True
logging.info('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.')
self.host.reboot()
# 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):
logging.info('shball bios/ec: %s/%s',
shball_bios, shball_ec)
logging.info('installed bios/ec: %s/%s', bios, ec)
raise error.TestFail('Version mismatch after firmware update')
logging.info('*** Done firmware updated to match shellball. ***')
else:
logging.info('*** No firmware update is needed. ***')