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