1# Copyright 2018 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import time 7 8from autotest_lib.client.common_lib import error 9from autotest_lib.client.common_lib.cros import tpm_utils 10from autotest_lib.server import autotest 11from autotest_lib.server.cros.faft.cr50_test import Cr50Test 12 13 14class firmware_Cr50FactoryResetVC(Cr50Test): 15 """A test verifying factory mode vendor command.""" 16 version = 1 17 18 FWMP_DEV_DISABLE_CCD_UNLOCK = (1 << 6) 19 # Short wait to make sure cr50 has had enough time to update the ccd state 20 SLEEP = 2 21 BOOL_VALUES = (True, False) 22 23 def initialize(self, host, cmdline_args, full_args): 24 """Initialize servo check if cr50 exists.""" 25 super(firmware_Cr50FactoryResetVC, self).initialize(host, cmdline_args, 26 full_args) 27 if not self.cr50.has_command('bpforce'): 28 raise error.TestNAError('Cannot run test without bpforce') 29 self.fast_open(enable_testlab=True) 30 # Reset ccd completely. 31 self.cr50.send_command('ccd reset') 32 33 # If we can fake battery connect/disconnect, then we can test the vendor 34 # command. 35 try: 36 self.bp_override(True) 37 self.bp_override(False) 38 except Exception, e: 39 logging.info(e) 40 raise error.TestNAError('Cannot fully test factory mode vendor ' 41 'command without the ability to fake battery presence') 42 43 def cleanup(self): 44 """Clear the FWMP and ccd state""" 45 try: 46 self.clear_state() 47 finally: 48 super(firmware_Cr50FactoryResetVC, self).cleanup() 49 50 51 def bp_override(self, connect): 52 """Deassert BATT_PRES signal, so cr50 will think wp is off.""" 53 self.cr50.send_command('ccd testlab open') 54 self.cr50.set_batt_pres_state('connect' if connect else 'disconnect', 55 False) 56 if self.cr50.get_batt_pres_state()[1] != connect: 57 raise error.TestError('Could not fake battery %sconnect' % 58 ('' if connect else 'dis')) 59 self.cr50.set_ccd_level('lock') 60 61 62 def fwmp_ccd_lockout(self): 63 """Returns True if FWMP is locking out CCD.""" 64 return 'fwmp_lock' in self.cr50.get_ccd_info()['TPM'] 65 66 67 def set_fwmp_lockout(self, enable): 68 """Change the FWMP to enable or disable ccd. 69 70 Args: 71 enable: True if FWMP flags should lock out ccd. 72 """ 73 logging.info('%sing FWMP ccd lockout', 'enabl' if enable else 'clear') 74 if enable: 75 flags = hex(self.FWMP_DEV_DISABLE_CCD_UNLOCK) 76 logging.info('Setting FWMP flags to %s', flags) 77 autotest.Autotest(self.host).run_test('firmware_SetFWMP', 78 flags=flags, fwmp_cleared=True, check_client_result=True) 79 80 if (not self.fwmp_ccd_lockout()) != (not enable): 81 raise error.TestError('Could not %s fwmp lockout' % 82 ('set' if enable else 'clear')) 83 84 85 def setup_ccd_password(self, set_password): 86 """Set the Cr50 CCD password. 87 88 Args: 89 set_password: if True set the password. The password is already 90 cleared, so if False just check the password is cleared 91 """ 92 if set_password: 93 self.cr50.send_command('ccd testlab open') 94 # Set the ccd password 95 self.set_ccd_password('ccd_dummy_pw') 96 if self.cr50.password_is_reset() == set_password: 97 raise error.TestError('Could not %s password' % 98 ('set' if set_password else 'clear')) 99 100 101 def factory_mode_enabled(self): 102 """Returns True if factory mode is enabled.""" 103 caps = self.cr50.get_cap_dict() 104 caps.pop('GscFullConsole') 105 return self.cr50.get_cap_overview(caps)[0] 106 107 108 def get_relevant_state(self): 109 """Returns cr50 state that can lock out factory mode. 110 111 FWMP, battery presence, or a password can all lock out enabling factory 112 mode using the vendor command. If any item in state is True, factory 113 mode should be locked out. 114 """ 115 state = [] 116 state.append(self.fwmp_ccd_lockout()) 117 state.append(self.cr50.get_batt_pres_state()[1]) 118 state.append(not self.cr50.password_is_reset()) 119 return state 120 121 122 def get_state_message(self): 123 """Convert relevant state into a useful log message.""" 124 fwmp, bp, password = self.get_relevant_state() 125 return ('fwmp %s bp %sconnected password %s' % 126 ('set' if fwmp else 'cleared', 127 '' if bp else 'dis', 128 'set' if password else 'cleared')) 129 130 131 def factory_locked_out(self): 132 """Returns True if any state preventing factory mode is True.""" 133 return True in self.get_relevant_state() 134 135 136 def set_factory_mode(self, enable): 137 """Use the vendor command to control factory mode. 138 139 Args: 140 enable: Enable factory mode if True. Disable it if False. 141 """ 142 enable_fail = self.factory_locked_out() and enable 143 time.sleep(self.SLEEP) 144 logging.info('%sABLING FACTORY MODE', 'EN' if enable else 'DIS') 145 if enable: 146 logging.info('EXPECT: %s', 'failure' if enable_fail else 'success') 147 cmd = 'enable' if enable else 'disable' 148 149 result = self.host.run('gsctool -a -F %s' % cmd, 150 ignore_status=(enable_fail or not enable)) 151 logging.debug(result) 152 expect_enabled = enable and not enable_fail 153 154 if expect_enabled: 155 # Cr50 will reboot after it enables factory mode. 156 self.cr50.wait_for_reboot(timeout=10) 157 else: 158 # Wait long enoug for cr50 to udpate the ccd state. 159 time.sleep(self.SLEEP) 160 if self.factory_mode_enabled() != expect_enabled: 161 raise error.TestFail('Unexpected factory mode %s result' % cmd) 162 163 164 def clear_state(self): 165 """Clear the FWMP and reset CCD""" 166 # Clear the FWMP 167 self.clear_fwmp() 168 # make sure all of the ccd stuff is reset 169 self.cr50.send_command('ccd testlab open') 170 # Run ccd reset to make sure all ccd state is cleared 171 self.cr50.send_command('ccd reset') 172 # Clear the TPM owner, so we can set the ccd password and 173 # create the FWMP 174 tpm_utils.ClearTPMOwnerRequest(self.host, wait_for_ready=True) 175 176 177 def run_once(self): 178 """Verify FWMP disable with different flag values.""" 179 errors = [] 180 # Try enabling factory mode in each valid state. Cr50 checks write 181 # protect, password, and fwmp before allowing fwmp to be enabled. 182 for lockout_ccd_with_fwmp in self.BOOL_VALUES: 183 for set_password in self.BOOL_VALUES: 184 for connect in self.BOOL_VALUES: 185 # Clear relevant state, so we can set the fwmp and password 186 self.clear_state() 187 188 # Setup the cr50 state 189 self.setup_ccd_password(set_password) 190 self.bp_override(connect) 191 self.set_fwmp_lockout(lockout_ccd_with_fwmp) 192 self.cr50.set_ccd_level('lock') 193 194 logging.info('RUN: %s', self.get_state_message()) 195 196 try: 197 self.set_factory_mode(True) 198 self.set_factory_mode(False) 199 except Exception, e: 200 message = 'FAILURE %r %r' % (self.get_state_message(), 201 e) 202 logging.info(message) 203 errors.append(message) 204 if errors: 205 raise error.TestFail(errors) 206