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 self.host = host 28 self.fast_open(enable_testlab=True) 29 # Run factory mode disable to make sure everything is reset. 30 self.host.run('gsctool -a -F disable', ignore_status=True) 31 32 # If we can set wp to off and on, then we can control write protect 33 try: 34 self.set_wp(True) 35 self.set_wp(False) 36 except: 37 raise error.TestNAError('Cannot fully test factory mode vendor ' 38 'command without control of write protect') 39 40 41 def wp_enabled(self): 42 """Returns True if write protect is enabled.""" 43 rv = self.cr50.send_command_get_output('gpioget', 44 ['(0|1)..BATT_PRES_L']) 45 logging.info(rv) 46 return not int(rv[0][1]) 47 48 49 def set_wp(self, enable): 50 """Deassert BATT_PRES signal, so cr50 will think wp is off.""" 51 self.cr50.send_command('ccd testlab open') 52 # TODO(mruthven): come up with servo rework, so we can control batt_pres 53 # directly. 54 # 55 # for now build a dbg image and connect BATT_PRES_L to DOIM4 and set it 56 # as an output 57 self.cr50.send_command('gpioset BATT_PRES_L %d' % (0 if enable else 1)) 58 if (not self.wp_enabled()) != (not enable): 59 raise error.TestError('Could not %s write protect' % 60 ('set' if enable else 'clear')) 61 self.cr50.set_ccd_level('lock') 62 63 64 def fwmp_ccd_lockout(self): 65 """Returns True if FWMP is locking out CCD.""" 66 return 'fwmp_lock' in self.cr50.get_ccd_info()['TPM'] 67 68 69 def set_fwmp_lockout(self, enable): 70 """Change the FWMP to enable or disable ccd. 71 72 Args: 73 enable: True if FWMP flags should lock out ccd. 74 """ 75 logging.info('%sing FWMP ccd lockout', 'enabl' if enable else 'clear') 76 if enable: 77 flags = hex(self.FWMP_DEV_DISABLE_CCD_UNLOCK) 78 logging.info('Setting FWMP flags to %s', flags) 79 autotest.Autotest(self.host).run_test('firmware_SetFWMP', 80 flags=flags, fwmp_cleared=True, check_client_result=True) 81 82 if (not self.fwmp_ccd_lockout()) != (not enable): 83 raise error.TestError('Could not %s fwmp lockout' % 84 ('set' if enable else 'clear')) 85 86 87 def has_ccd_password(self): 88 """Returns True if the ccd password is set.""" 89 return 'set' in self.cr50.get_ccd_info()['Password'] 90 91 92 def setup_ccd_password(self, set_password): 93 """Set the Cr50 CCD password. 94 95 Args: 96 set_password: if True set the password. The password is already 97 cleared, so if False just check the password is cleared 98 """ 99 if set_password: 100 self.cr50.send_command('ccd testlab open') 101 # Set the ccd password 102 self.set_ccd_password('ccd_dummy_pw') 103 if self.has_ccd_password() != set_password: 104 raise error.TestError('Could not %s password' % 105 ('set' if set_password else 'clear')) 106 107 108 def factory_mode_enabled(self): 109 """Returns True if factory mode is enabled.""" 110 caps = self.cr50.get_cap_dict() 111 caps.pop('GscFullConsole') 112 return self.cr50.get_cap_overview(caps)[0] 113 114 115 def get_relevant_state(self): 116 """Returns cr50 factory mode check state. 117 118 If any item in state is True, that means ccd factory mode should be 119 locked out. 120 """ 121 state = [] 122 state.append(self.fwmp_ccd_lockout()) 123 state.append(self.wp_enabled()) 124 state.append(self.has_ccd_password()) 125 return state 126 127 def get_state_message(self): 128 """Convert relevant state into a useful log message.""" 129 fwmp, wp, password = self.get_relevant_state() 130 return 'fwmp %s wp %s password %s' % ('set' if fwmp else 'cleared', 131 'enabled' if wp else 'disabled', 132 'set' if password else 'cleared') 133 134 def factory_locked_out(self): 135 """Returns True if any state preventing factory mode is True.""" 136 return True in self.get_relevant_state() 137 138 139 def set_factory_mode(self, enable): 140 """Use the vendor command to control factory mode. 141 142 Args: 143 enable: Enable factory mode if True. Disable it if False. 144 """ 145 enable_fail = self.factory_locked_out() and enable 146 time.sleep(self.SLEEP) 147 logging.info('%sABLING FACTORY MODE', 'EN' if enable else 'DIS') 148 if enable: 149 logging.info('EXPECT: %s', 'failure' if enable_fail else 'success') 150 cmd = 'enable' if enable else 'disable' 151 152 result = self.host.run('gsctool -a -F %s' % cmd, 153 ignore_status=(enable_fail or not enable)) 154 logging.debug(result) 155 expect_enabled = enable and not enable_fail 156 157 time.sleep(self.SLEEP) 158 if self.factory_mode_enabled() != expect_enabled: 159 raise error.TestFail('Unexpected factory mode %s result' % cmd) 160 161 162 def run_once(self): 163 """Verify FWMP disable with different flag values.""" 164 errors = [] 165 # Try enabling factory mode in each valid state. Cr50 checks write 166 # protect, password, and fwmp before allowing fwmp to be enabled. 167 for lockout_ccd_with_fwmp in self.BOOL_VALUES: 168 for set_password in self.BOOL_VALUES: 169 for enable_wp in self.BOOL_VALUES: 170 # make sure all of the ccd stuff is reset 171 self.cr50.send_command('ccd testlab open') 172 # Run ccd reset to make sure all ccd state is cleared 173 self.cr50.send_command('ccd reset') 174 # Clear the TPM owner, so we can set the ccd password and 175 # create the FWMP 176 tpm_utils.ClearTPMOwnerRequest(self.host, 177 wait_for_ready=True) 178 self.setup_ccd_password(set_password) 179 self.set_wp(enable_wp) 180 self.set_fwmp_lockout(lockout_ccd_with_fwmp) 181 self.cr50.set_ccd_level('lock') 182 logging.info('RUN: %s', self.get_state_message()) 183 184 try: 185 self.set_factory_mode(True) 186 self.set_factory_mode(False) 187 except Exception, e: 188 message = 'FAILURE %r %r' % (self.get_state_message(), 189 e) 190 logging.info(message) 191 errors.append(message) 192 if errors: 193 raise error.TestFail(errors) 194