xref: /aosp_15_r20/external/autotest/server/site_tests/firmware_Cr50CheckCap/firmware_Cr50CheckCap.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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 pprint
7
8from autotest_lib.client.common_lib import error
9from autotest_lib.server.cros.faft.cr50_test import Cr50Test
10
11
12class firmware_Cr50CheckCap(Cr50Test):
13    """Verify cr50 capabilities"""
14    version = 1
15
16    # The default requirements for these capabilities change between prod and
17    # prepvt images. Make sure they match the expected values.
18    SPECIAL_CAPS = ['OpenNoDevMode', 'OpenFromUSB']
19    EXPECTED_REQ_PREPVT = 'Always'
20    EXPECTED_REQ_PROD = 'IfOpened'
21
22    def check_cap_command(self, enable_factory, reset_caps):
23        """Verify the cr50 cap response after running the given command"""
24        if enable_factory:
25            self.cr50.ccd_reset_factory()
26        else:
27            # Testlab mode is enabled, so it's ok to reset ccd without enabling
28            # capabilities necessary for ccd.
29            self.cr50.ccd_reset(servo_en=False)
30        caps = self.cr50.get_cap_dict()
31        logging.info(caps)
32        in_factory_mode, is_reset = self.cr50.get_cap_overview(caps)
33        if reset_caps and not is_reset:
34            raise error.TestFail('did not reset capabilities')
35        if enable_factory and not in_factory_mode:
36            raise error.TestFail('did not enable factory mode')
37
38
39    def check_cap_req(self, cap_dict, cap, expected_req):
40        """Check the current cap requirement against the expected requirement"""
41        req = cap_dict[cap]
42        if req != expected_req:
43            raise error.TestFail('%r should be %r not %r' % (cap, expected_req,
44                                                             req))
45
46
47    def ccd_ec_uart_works(self):
48        """Returns True if the CCD ec uart works."""
49        logging.info('checking ec console')
50        try:
51            self.servo.get('ec_board', self._ec_prefix)
52            logging.info('ccd ec console is responsive')
53            return True
54        except:
55            logging.info('ccd ec console is unresponsive')
56            return False
57
58
59    def check_cap_accessiblity(self, ccd_level, cap_setting, expect_accessible):
60        """Check setting cap requirements restricts the capabilities correctly.
61
62        Set each ccd capability to cap_setting. Set the ccd state to ccd_level.
63        Then verify the capability accessiblity matches expect_accessible.
64
65        Args:
66            ccd_level: a ccd state level: 'lock', 'unlock', or 'open'.
67            cap_setting: A ccd cap setting: 'IfOpened', 'Always', or
68                         'UnlessLocked'.
69            expect_accessible: True if capabilities should be accessible
70
71        Raises:
72            TestFail if expect_accessible doesn't match the accessibility state.
73        """
74
75        if (ccd_level == 'unlock' or cap_setting == 'UnlessLocked') \
76            and not self.cr50.unlock_is_supported():
77            return
78
79        # Run testlab open, so we won't have to do physical presence stuff.
80        self.cr50.send_command('ccd testlab open')
81
82        # Set all capabilities to cap_setting
83        caps = self.cr50.get_cap_dict().keys()
84        cap_settings = {}
85        for cap in caps:
86            cap_settings[cap] = cap_setting
87        self.cr50.set_caps(cap_settings)
88
89        # Set the ccd state to ccd_level
90        self.cr50.set_ccd_level(ccd_level, self.CCD_PASSWORD)
91        cap_dict = self.cr50.get_cap_dict()
92        logging.info('Cap state with console %r req %r:\n%s', ccd_level,
93                     cap_setting, pprint.pformat(cap_dict))
94
95        # Check the accessiblity
96        for cap, cap_info in cap_dict.items():
97            if cap_info[self.cr50.CAP_IS_ACCESSIBLE] != expect_accessible:
98                raise error.TestFail('%r is %raccessible' % (cap,
99                                     'not ' if expect_accessible else ''))
100
101        if (self.check_ec_uart and
102            expect_accessible != self.ccd_ec_uart_works()):
103            raise error.TestFail('EC UART is %saccessible when it should%s be' %
104                                 ('not ' if expect_accessible else '',
105                                  '' if expect_accessible else "n't"))
106
107
108    def run_once(self, ccd_open_restricted=False):
109        """Check cr50 capabilities work correctly."""
110        self.fast_ccd_open(enable_testlab=True)
111
112
113        # Check servo monitoring before changing the active device. There's no
114        # need for servo detection if ccd is the only device.
115        servo_detect_ok = (self.servo.main_device_is_ccd()
116                           or self.cr50.check_servo_monitor())
117
118        set_ccd = self.servo.enable_ccd_servo_device()
119        self._ec_prefix = self.servo.get_active_device_prefix()
120        # Only check EC uart if the board has a working EC and cr50 can detect
121        # servo connect/disconnect.
122        self.check_ec_uart = (
123                set_ccd and servo_detect_ok
124                and self.check_ec_capability(suppress_warning=True)
125                and self.servo.active_device_is_ccd()
126                and self.servo.has_control('ec_board', self._ec_prefix))
127
128        # Make sure factory reset sets all capabilities to Always
129        self.check_cap_command(True, False)
130
131        # Make sure ccd reset sets all capabilities to Default
132        self.check_cap_command(False, True)
133
134        expected_req = (self.EXPECTED_REQ_PROD if ccd_open_restricted else
135                        self.EXPECTED_REQ_PREPVT)
136        cap_dict = self.cr50.get_cap_dict(info=self.cr50.CAP_REQ)
137        # Make sure the special ccd capabilities match ccd_open_restricted
138        for cap in self.SPECIAL_CAPS:
139            self.check_cap_req(cap_dict, cap, expected_req)
140
141        # Set the password so we can change the ccd level from the console
142        self.cr50.send_command('ccd testlab open')
143        # Testlab mode is enabled, so it's ok to reset ccd without enabling
144        # capabilities necessary for ccd.
145        self.cr50.ccd_reset(servo_en=False)
146        self.set_ccd_password(self.CCD_PASSWORD)
147
148        # Make sure ccd accessiblity behaves as expected based on the cap
149        # settings and the ccd state.
150        self.check_cap_accessiblity('open', 'IfOpened', True)
151        self.check_cap_accessiblity('open', 'UnlessLocked', True)
152        self.check_cap_accessiblity('open', 'Always', True)
153
154        self.check_cap_accessiblity('unlock', 'IfOpened', False)
155        self.check_cap_accessiblity('unlock', 'UnlessLocked', True)
156        self.check_cap_accessiblity('unlock', 'Always', True)
157
158        self.check_cap_accessiblity('lock', 'IfOpened', False)
159        self.check_cap_accessiblity('lock', 'UnlessLocked', False)
160        self.check_cap_accessiblity('lock', 'Always', True)
161