• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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