175 lines
6.6 KiB
Python
175 lines
6.6 KiB
Python
# Copyright (c) 2012 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
|
|
import time
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.common_lib.cros import cr50_utils
|
|
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
|
|
from autotest_lib.server.cros import vboot_constants as vboot
|
|
|
|
|
|
class firmware_SoftwareSync(FirmwareTest):
|
|
"""
|
|
Servo based EC software sync test.
|
|
"""
|
|
version = 1
|
|
|
|
def initialize(self, host, cmdline_args, dev_mode=False):
|
|
# This test tries to corrupt EC firmware. Should disable EC WP.
|
|
super(firmware_SoftwareSync, self).initialize(host, cmdline_args,
|
|
ec_wp=False)
|
|
|
|
# Don't bother if there is no Chrome EC.
|
|
if not self.check_ec_capability():
|
|
raise error.TestNAError("Nothing needs to be tested on this device")
|
|
|
|
# In order to test software sync, it must be enabled.
|
|
self.clear_set_gbb_flags(vboot.GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC, 0)
|
|
self.backup_firmware()
|
|
self.switcher.setup_mode('dev' if dev_mode else 'normal')
|
|
self.setup_usbkey(usbkey=False)
|
|
self.setup_rw_boot()
|
|
self.dev_mode = dev_mode
|
|
|
|
if self.ec.check_feature('EC_FEATURE_EFS2'):
|
|
self.original_ccd_level = self.cr50.get_ccd_level()
|
|
|
|
# CCD needs to be open for 'ec_comm corrupt' run.
|
|
self.fast_ccd_open(reset_ccd=False, dev_mode=dev_mode)
|
|
logging.info("CCD opened.")
|
|
else:
|
|
self.original_ccd_level = None
|
|
|
|
# Check for cr50 support on a device and get the boot mode. It should
|
|
# be NORMAL at this point, even if EFS2 is not supported.
|
|
if hasattr(self, 'cr50'):
|
|
res = cr50_utils.GSCTool(host, ['-a', '-g']).stdout.strip()
|
|
if 'NORMAL' in res:
|
|
pass
|
|
elif 'error' in res.lower():
|
|
raise error.TestFail('TPM Vendor command GET_BOOT_MODE failed:'
|
|
' %r' % res)
|
|
else:
|
|
raise error.TestFail('GET_BOOT_MODE did not return NORMAL:'
|
|
' %r' % res)
|
|
|
|
def cleanup(self):
|
|
try:
|
|
if self.is_firmware_saved():
|
|
self.restore_firmware()
|
|
except Exception as e:
|
|
logging.error("Caught exception: %s", str(e))
|
|
|
|
try:
|
|
if self.original_ccd_level:
|
|
self.cr50.set_ccd_level(self.original_ccd_level)
|
|
except Exception as e:
|
|
logging.error("Failed to restore ccd to %r: %s",
|
|
self.original_ccd_level, str(e))
|
|
|
|
super(firmware_SoftwareSync, self).cleanup()
|
|
|
|
def record_hash(self):
|
|
"""Record current EC hash."""
|
|
self._ec_hash = self.faft_client.ec.get_active_hash()
|
|
logging.info("Stored EC hash: %s", self._ec_hash)
|
|
|
|
def corrupt_active_rw(self):
|
|
"""Corrupt the active RW portion."""
|
|
section = 'rw'
|
|
try:
|
|
if self.servo.get_ec_active_copy() == 'RW_B':
|
|
section = 'rw_b'
|
|
except error.TestFail:
|
|
# Skip the failure, as ec_active_copy is new.
|
|
# TODO(waihong): Remove this except clause.
|
|
pass
|
|
logging.info("Corrupt the EC section: %s", section)
|
|
self.faft_client.ec.corrupt_body(section)
|
|
|
|
def software_sync_checker(self):
|
|
"""Check EC firmware is restored by software sync."""
|
|
ec_hash = self.faft_client.ec.get_active_hash()
|
|
logging.info("Current EC hash: %s", ec_hash)
|
|
if self._ec_hash != ec_hash:
|
|
return False
|
|
return self.checkers.ec_act_copy_checker('RW')
|
|
|
|
def wait_software_sync_and_boot(self):
|
|
"""Wait for software sync to update EC."""
|
|
if self.dev_mode:
|
|
time.sleep(self.faft_config.software_sync_update +
|
|
self.faft_config.firmware_screen)
|
|
self.servo.ctrl_d()
|
|
else:
|
|
time.sleep(self.faft_config.software_sync_update)
|
|
|
|
def run_test_corrupt_ec_rw(self):
|
|
"""Runs a single iteration of the test."""
|
|
logging.info("Corrupt EC firmware RW body.")
|
|
self.check_state((self.checkers.ec_act_copy_checker, 'RW'))
|
|
self.record_hash()
|
|
self.corrupt_active_rw()
|
|
logging.info("Reboot AP, check EC hash, and software sync it.")
|
|
self.switcher.simple_reboot(reboot_type='warm')
|
|
self.wait_software_sync_and_boot()
|
|
self.switcher.wait_for_client()
|
|
|
|
logging.info("Expect EC in RW and RW is restored.")
|
|
self.check_state(self.software_sync_checker)
|
|
|
|
def run_test_corrupt_hash_in_cr50(self):
|
|
"""Run the test corrupting ECRW hash in CR50."""
|
|
|
|
self.check_state((self.checkers.ec_act_copy_checker, 'RW'))
|
|
self.record_hash()
|
|
|
|
logging.info('Corrupt ECRW hashcode in TPM kernel NV index.')
|
|
ec_corrupt_cmd = 'ec_comm corrupt'
|
|
self.cr50.send_command(ec_corrupt_cmd)
|
|
|
|
try:
|
|
# Reboot EC but keep AP off, so that it can earn a time to check
|
|
# if boot_mode is NO_BOOT and EC is in RO.
|
|
logging.info('Reset EC with AP off.')
|
|
self.ec.reboot('ap-off')
|
|
time.sleep(5)
|
|
|
|
# The boot mode should be "NO_BOOT".
|
|
logging.info('Check the boot mode is NO_BOOT mode.')
|
|
if not self.cr50.check_boot_mode('NO_BOOT'):
|
|
raise error.TestFail('Boot mode is not NO_BOOT.')
|
|
|
|
# Check if the current EC image is RO.
|
|
logging.info('Check EC is in RO.')
|
|
if not self.ec.check_ro_rw('RO'):
|
|
raise error.TestFail('EC is not in RO mode.')
|
|
finally:
|
|
# Wake up AP by tapping power button.
|
|
logging.info('Wake up AP.')
|
|
self.servo.power_short_press()
|
|
|
|
# Wait for software sync is done.
|
|
self.wait_software_sync_and_boot()
|
|
self.switcher.wait_for_client()
|
|
|
|
# The boot mode should be "NORMAL".
|
|
logging.info('Check the boot mode is NORMAL mode.')
|
|
if not self.cr50.check_boot_mode('NORMAL'):
|
|
logging.warn('You may want to run %r in cr50 console to uncorrupt'
|
|
' EC hash.', ec_corrupt_cmd)
|
|
raise error.TestFail('Boot mode is not NORMAL.')
|
|
|
|
logging.info('Expect EC in RW and RW is restored.')
|
|
self.check_state(self.software_sync_checker)
|
|
|
|
def run_once(self):
|
|
"""Entry point of this test."""
|
|
self.run_test_corrupt_ec_rw()
|
|
|
|
if self.ec.check_feature('EC_FEATURE_EFS2'):
|
|
self.run_test_corrupt_hash_in_cr50()
|