| # 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. |
| |
| from __future__ import print_function |
| |
| 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_ccd_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 as 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(self.CCD_PASSWORD) |
| 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 as e: |
| message = 'FAILURE %r %r' % (self.get_state_message(), |
| e) |
| logging.info(message) |
| errors.append(message) |
| if errors: |
| raise error.TestFail(errors) |