• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 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 re
7import time
8
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib.cros import cr50_utils
11from autotest_lib.server.cros.faft.cr50_test import Cr50Test
12
13
14class firmware_GSCAPROV1Trigger(Cr50Test):
15    """Verify GSC response after triggering AP RO V1 verification."""
16    version = 1
17
18    # This only verifies V1 output right now.
19    TEST_AP_RO_VER = 1
20
21    # DBG image has to be able to set the AP RO hash with the board id set.
22    MIN_DBG_VER = '1.6.100'
23
24    VERIFICATION_PASSED = 1
25    VERIFICATION_FAILED = 2
26
27    DIGEST_RE = r' digest ([0-9a-f]{64})'
28    CALCULATED_DIGEST_RE = 'Calculated' + DIGEST_RE
29    STORED_DIGEST_RE = 'Stored' + DIGEST_RE
30
31    def initialize(self, host, cmdline_args, full_args={}):
32        """Initialize servo"""
33        self.ran_test = False
34        super(firmware_GSCAPROV1Trigger,
35              self).initialize(host,
36                               cmdline_args,
37                               full_args,
38                               restore_cr50_image=True)
39        if not self.cr50.ap_ro_version_is_supported(self.TEST_AP_RO_VER):
40            raise error.TestNAError('GSC does not support AP RO v%s' %
41                                    self.TEST_AP_RO_VER)
42
43        dbg_ver = cr50_utils.InstallImage(self.host,
44                                          self.get_saved_dbg_image_path(),
45                                          '/tmp/cr50.bin')[1][1]
46        if cr50_utils.GetNewestVersion(dbg_ver,
47                                       self.MIN_DBG_VER) == self.MIN_DBG_VER:
48            raise error.TestNAError('Update DBG image to 6.100 or newer.')
49
50    def update_to_dbg_and_clear_hash(self):
51        """Clear the Hash."""
52        # Make sure the AP is up before trying to update.
53        self.recover_dut()
54        self._retry_cr50_update(self._dbg_image_path, 3, False)
55        self.cr50.send_command('ap_ro_info erase')
56        time.sleep(3)
57        ap_ro_info = self.cr50.get_ap_ro_info()
58        logging.info(ap_ro_info)
59        if ap_ro_info['hash']:
60            raise error.TestError('Could not erase hash')
61
62    def after_run_once(self):
63        """Reboot cr50 to recover the dut."""
64        try:
65            self.recover_dut()
66        finally:
67            super(firmware_GSCAPROV1Trigger, self).after_run_once()
68
69    def set_hash(self):
70        """Set the Hash."""
71        self.recover_dut()
72        result = self.host.run('ap_ro_hash.py -v True GBB')
73        logging.info(result)
74        time.sleep(3)
75        ap_ro_info = self.cr50.get_ap_ro_info()
76        logging.info(ap_ro_info)
77        if not ap_ro_info['hash']:
78            raise error.TestError('Could not set hash %r' % result)
79
80    def rollback_to_release_image(self):
81        """Update to the release image."""
82        self._retry_cr50_update(self.get_saved_cr50_original_path(),
83                                3,
84                                rollback=True)
85        logging.info(self.cr50.get_ap_ro_info())
86
87    def cleanup(self):
88        """Clear the AP RO hash."""
89        try:
90            if not self.ran_test:
91                return
92            logging.info('Cleanup')
93            self.recover_dut()
94            self.update_to_dbg_and_clear_hash()
95            self.rollback_to_release_image()
96        finally:
97            super(firmware_GSCAPROV1Trigger, self).cleanup()
98
99    def recover_dut(self):
100        """Reboot gsc to recover the dut."""
101        logging.info('Recover DUT')
102        ap_ro_info = self.cr50.get_ap_ro_info()
103        logging.info(ap_ro_info)
104        if ap_ro_info['result'] != self.VERIFICATION_FAILED:
105            self._try_to_bring_dut_up()
106            return
107        time.sleep(3)
108        self.cr50.send_command('ccd testlab open')
109        time.sleep(3)
110        self.cr50.reboot()
111        time.sleep(self.faft_config.delay_reboot_to_ping)
112        logging.info(self.cr50.get_ap_ro_info())
113        self._try_to_bring_dut_up()
114        self.cr50.send_command('ccd testlab open')
115
116    def trigger_verification(self):
117        """Trigger verification."""
118        try:
119            self.recover_dut()
120            result = self.host.run('gsctool -aB start',
121                                   ignore_timeout=True,
122                                   ignore_status=True,
123                                   timeout=20)
124            logging.info(result)
125        finally:
126            time.sleep(5)
127            ap_ro_info = self.cr50.get_ap_ro_info()
128            logging.info(ap_ro_info)
129            self.hash_results.append(ap_ro_info['result'])
130            self.servo.record_uart_capture()
131
132    def run_once(self):
133        """Save hash and trigger verification"""
134        self.ran_test = True
135        self.hash_results = []
136        # The DBG image can set the hash when the board id is saved. The release
137        # image can't. Set the hash with the DBG image, so the test doesn't need
138        # to erase the board id. This test verifies triggering AP RO
139        # verification. It's not about saving the hash.
140        self.update_to_dbg_and_clear_hash()
141        self.set_hash()
142        self.rollback_to_release_image()
143        # CCD has to be open to trigger verification.
144        self.fast_ccd_open(True)
145
146        # Trigger verification multiple times. Make sure it doesn't fail or
147        # change.
148        self.trigger_verification()
149        self.trigger_verification()
150        self.trigger_verification()
151        self.trigger_verification()
152
153        self.servo.record_uart_capture()
154        cr50_uart_file = self.servo.get_uart_logfile('cr50')
155        if not cr50_uart_file:
156            logging.info('No cr50 uart file')
157            return
158        with open(cr50_uart_file, 'r') as f:
159            contents = f.read()
160
161        self.recover_dut()
162
163        # GSC only prints calculated and stored hashes after AP RO verificaiton
164        # fails. These sets will be empty if verification passed every time.
165        calculated = set(re.findall(self.CALCULATED_DIGEST_RE, contents))
166        stored = set(re.findall(self.STORED_DIGEST_RE, contents))
167        logging.info('Stored: %r', stored)
168        logging.info('Calculated: %r', calculated)
169        logging.info('Results: %r', self.hash_results)
170
171        if self.VERIFICATION_FAILED in self.hash_results:
172            raise error.TestFail(
173                    'Verification failed -- stored: %r calculated: %r' %
174                    (stored, calculated))
175        if len(calculated) > 1:
176            raise error.TestFail('Multiple calculated digests %r' % calculated)
177        # This shouldn't happen. Raise TestNA, so it's easy to see.
178        if self.VERIFICATION_PASSED not in self.hash_results:
179            raise error.TestNAError(
180                    'Verification Not Run -- stored: %r calculated: %r' %
181                    (stored, calculated))
182
183        # TODO(b/218705748): change the hash and verify verification fails.
184