# Copyright 2018 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 tpm_utils from autotest_lib.server import autotest from autotest_lib.server.cros.faft.cr50_test import Cr50Test class firmware_Cr50FactoryResetVC(Cr50Test): """A test verifying factory mode vendor command.""" version = 1 FWMP_DEV_DISABLE_CCD_UNLOCK = (1 << 6) # Short wait to make sure cr50 has had enough time to update the ccd state SLEEP = 2 BOOL_VALUES = (True, False) def initialize(self, host, cmdline_args, full_args): """Initialize servo check if cr50 exists.""" super(firmware_Cr50FactoryResetVC, self).initialize(host, cmdline_args, full_args) if not self.cr50.has_command('bpforce'): raise error.TestNAError('Cannot run test without bpforce') self.fast_open(enable_testlab=True) # Reset ccd completely. self.cr50.send_command('ccd reset') # If we can fake battery connect/disconnect, then we can test the vendor # command. try: self.bp_override(True) self.bp_override(False) except Exception, e: logging.info(e) raise error.TestNAError('Cannot fully test factory mode vendor ' 'command without the ability to fake battery presence') def cleanup(self): """Clear the FWMP and ccd state""" try: self.clear_state() finally: super(firmware_Cr50FactoryResetVC, self).cleanup() def bp_override(self, connect): """Deassert BATT_PRES signal, so cr50 will think wp is off.""" self.cr50.send_command('ccd testlab open') self.cr50.set_batt_pres_state('connect' if connect else 'disconnect', False) if self.cr50.get_batt_pres_state()[1] != connect: raise error.TestError('Could not fake battery %sconnect' % ('' if connect else 'dis')) self.cr50.set_ccd_level('lock') def fwmp_ccd_lockout(self): """Returns True if FWMP is locking out CCD.""" return 'fwmp_lock' in self.cr50.get_ccd_info()['TPM'] def set_fwmp_lockout(self, enable): """Change the FWMP to enable or disable ccd. Args: enable: True if FWMP flags should lock out ccd. """ logging.info('%sing FWMP ccd lockout', 'enabl' if enable else 'clear') if enable: flags = hex(self.FWMP_DEV_DISABLE_CCD_UNLOCK) logging.info('Setting FWMP flags to %s', flags) autotest.Autotest(self.host).run_test('firmware_SetFWMP', flags=flags, fwmp_cleared=True, check_client_result=True) if (not self.fwmp_ccd_lockout()) != (not enable): raise error.TestError('Could not %s fwmp lockout' % ('set' if enable else 'clear')) def setup_ccd_password(self, set_password): """Set the Cr50 CCD password. Args: set_password: if True set the password. The password is already cleared, so if False just check the password is cleared """ if set_password: self.cr50.send_command('ccd testlab open') # Set the ccd password self.set_ccd_password('ccd_dummy_pw') if self.cr50.password_is_reset() == set_password: raise error.TestError('Could not %s password' % ('set' if set_password else 'clear')) def factory_mode_enabled(self): """Returns True if factory mode is enabled.""" caps = self.cr50.get_cap_dict() caps.pop('GscFullConsole') return self.cr50.get_cap_overview(caps)[0] def get_relevant_state(self): """Returns cr50 state that can lock out factory mode. FWMP, battery presence, or a password can all lock out enabling factory mode using the vendor command. If any item in state is True, factory mode should be locked out. """ state = [] state.append(self.fwmp_ccd_lockout()) state.append(self.cr50.get_batt_pres_state()[1]) state.append(not self.cr50.password_is_reset()) return state def get_state_message(self): """Convert relevant state into a useful log message.""" fwmp, bp, password = self.get_relevant_state() return ('fwmp %s bp %sconnected password %s' % ('set' if fwmp else 'cleared', '' if bp else 'dis', 'set' if password else 'cleared')) def factory_locked_out(self): """Returns True if any state preventing factory mode is True.""" return True in self.get_relevant_state() def set_factory_mode(self, enable): """Use the vendor command to control factory mode. Args: enable: Enable factory mode if True. Disable it if False. """ enable_fail = self.factory_locked_out() and enable time.sleep(self.SLEEP) logging.info('%sABLING FACTORY MODE', 'EN' if enable else 'DIS') if enable: logging.info('EXPECT: %s', 'failure' if enable_fail else 'success') cmd = 'enable' if enable else 'disable' result = self.host.run('gsctool -a -F %s' % cmd, ignore_status=(enable_fail or not enable)) logging.debug(result) expect_enabled = enable and not enable_fail if expect_enabled: # Cr50 will reboot after it enables factory mode. self.cr50.wait_for_reboot(timeout=10) else: # Wait long enoug for cr50 to udpate the ccd state. time.sleep(self.SLEEP) if self.factory_mode_enabled() != expect_enabled: raise error.TestFail('Unexpected factory mode %s result' % cmd) def clear_state(self): """Clear the FWMP and reset CCD""" # Clear the FWMP self.clear_fwmp() # make sure all of the ccd stuff is reset self.cr50.send_command('ccd testlab open') # Run ccd reset to make sure all ccd state is cleared self.cr50.send_command('ccd reset') # Clear the TPM owner, so we can set the ccd password and # create the FWMP tpm_utils.ClearTPMOwnerRequest(self.host, wait_for_ready=True) def run_once(self): """Verify FWMP disable with different flag values.""" errors = [] # Try enabling factory mode in each valid state. Cr50 checks write # protect, password, and fwmp before allowing fwmp to be enabled. for lockout_ccd_with_fwmp in self.BOOL_VALUES: for set_password in self.BOOL_VALUES: for connect in self.BOOL_VALUES: # Clear relevant state, so we can set the fwmp and password self.clear_state() # Setup the cr50 state self.setup_ccd_password(set_password) self.bp_override(connect) self.set_fwmp_lockout(lockout_ccd_with_fwmp) self.cr50.set_ccd_level('lock') logging.info('RUN: %s', self.get_state_message()) try: self.set_factory_mode(True) self.set_factory_mode(False) except Exception, e: message = 'FAILURE %r %r' % (self.get_state_message(), e) logging.info(message) errors.append(message) if errors: raise error.TestFail(errors)