diff options
-rw-r--r-- | server/site_tests/firmware_GSCAPROV1Trigger/control | 33 | ||||
-rw-r--r-- | server/site_tests/firmware_GSCAPROV1Trigger/firmware_GSCAPROV1Trigger.py | 183 |
2 files changed, 216 insertions, 0 deletions
diff --git a/server/site_tests/firmware_GSCAPROV1Trigger/control b/server/site_tests/firmware_GSCAPROV1Trigger/control new file mode 100644 index 0000000000..b38b6065c1 --- /dev/null +++ b/server/site_tests/firmware_GSCAPROV1Trigger/control @@ -0,0 +1,33 @@ +# Copyright 2022 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +AUTHOR = "mruthven" +NAME = "firmware_GSCAPROV1Trigger" +PURPOSE = "Test triggering AP RO verification." +ATTRIBUTES = "suite:faft_cr50_experimental" +TIME = "SHORT" +TEST_TYPE = "server" +DEPENDENCIES = "servo_state:WORKING" +PY_VERSION = 3 + +DOC = """ +Verify triggering AP RO verification on GSC. +""" + +from autotest_lib.client.common_lib import error +from autotest_lib.server import utils + +if 'args_dict' not in locals(): + args_dict = {} + +args_dict.update(utils.args_to_dict(args)) +servo_args = hosts.CrosHost.get_servo_arguments(args_dict) + +def run(machine): + host = hosts.create_host(machine, servo_args=servo_args) + + job.run_test("firmware_GSCAPROV1Trigger", host=host, cmdline_args=args, + full_args=args_dict) + +parallel_simple(run, machines) diff --git a/server/site_tests/firmware_GSCAPROV1Trigger/firmware_GSCAPROV1Trigger.py b/server/site_tests/firmware_GSCAPROV1Trigger/firmware_GSCAPROV1Trigger.py new file mode 100644 index 0000000000..c7dc40865f --- /dev/null +++ b/server/site_tests/firmware_GSCAPROV1Trigger/firmware_GSCAPROV1Trigger.py @@ -0,0 +1,183 @@ +# Copyright 2022 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import logging +import re +import time + +from autotest_lib.client.common_lib import error +from autotest_lib.client.common_lib.cros import cr50_utils +from autotest_lib.server.cros.faft.cr50_test import Cr50Test + + +class firmware_GSCAPROV1Trigger(Cr50Test): + """Verify GSC response after triggering AP RO V1 verification.""" + version = 1 + + # This only verifies V1 output right now. + TEST_AP_RO_VER = 1 + + # DBG image has to be able to set the AP RO hash with the board id set. + MIN_DBG_VER = '1.6.100' + + VERIFICATION_PASSED = 1 + VERIFICATION_FAILED = 2 + + DIGEST_RE = r' digest ([0-9a-f]{64})' + CALCULATED_DIGEST_RE = 'Calculated' + DIGEST_RE + STORED_DIGEST_RE = 'Stored' + DIGEST_RE + + def initialize(self, host, cmdline_args, full_args={}): + """Initialize servo""" + self.ran_test = False + super(firmware_GSCAPROV1Trigger, + self).initialize(host, + cmdline_args, + full_args, + restore_cr50_image=True) + if not self.cr50.ap_ro_version_is_supported(self.TEST_AP_RO_VER): + raise error.TestNAError('GSC does not support AP RO v%s' % + self.TEST_AP_RO_VER) + + dbg_ver = cr50_utils.InstallImage(self.host, + self.get_saved_dbg_image_path(), + '/tmp/cr50.bin')[1][1] + if cr50_utils.GetNewestVersion(dbg_ver, + self.MIN_DBG_VER) == self.MIN_DBG_VER: + raise error.TestNAError('Update DBG image to 6.100 or newer.') + + def update_to_dbg_and_clear_hash(self): + """Clear the Hash.""" + # Make sure the AP is up before trying to update. + self.recover_dut() + self._retry_cr50_update(self._dbg_image_path, 3, False) + self.cr50.send_command('ap_ro_info erase') + time.sleep(3) + ap_ro_info = self.cr50.get_ap_ro_info() + logging.info(ap_ro_info) + if ap_ro_info['hash']: + raise error.TestError('Could not erase hash') + + def after_run_once(self): + """Reboot cr50 to recover the dut.""" + try: + self.recover_dut() + finally: + super(firmware_GSCAPROV1Trigger, self).after_run_once() + + def set_hash(self): + """Set the Hash.""" + self.recover_dut() + result = self.host.run('ap_ro_hash.py -v True GBB') + logging.info(result) + time.sleep(3) + ap_ro_info = self.cr50.get_ap_ro_info() + logging.info(ap_ro_info) + if not ap_ro_info['hash']: + raise error.TestError('Could not set hash %r' % result) + + def rollback_to_release_image(self): + """Update to the release image.""" + self._retry_cr50_update(self.get_saved_cr50_original_path(), + 3, + rollback=True) + logging.info(self.cr50.get_ap_ro_info()) + + def cleanup(self): + """Clear the AP RO hash.""" + try: + if not self.ran_test: + return + logging.info('Cleanup') + self.recover_dut() + self.update_to_dbg_and_clear_hash() + self.rollback_to_release_image() + finally: + super(firmware_GSCAPROV1Trigger, self).cleanup() + + def recover_dut(self): + """Reboot gsc to recover the dut.""" + logging.info('Recover DUT') + ap_ro_info = self.cr50.get_ap_ro_info() + logging.info(ap_ro_info) + if ap_ro_info['result'] != self.VERIFICATION_FAILED: + self._try_to_bring_dut_up() + return + time.sleep(3) + self.cr50.send_command('ccd testlab open') + time.sleep(3) + self.cr50.reboot() + time.sleep(self.faft_config.delay_reboot_to_ping) + logging.info(self.cr50.get_ap_ro_info()) + self._try_to_bring_dut_up() + self.cr50.send_command('ccd testlab open') + + def trigger_verification(self): + """Trigger verification.""" + try: + self.recover_dut() + result = self.host.run('gsctool -aB start', + ignore_timeout=True, + ignore_status=True, + timeout=20) + logging.info(result) + finally: + time.sleep(5) + ap_ro_info = self.cr50.get_ap_ro_info() + logging.info(ap_ro_info) + self.hash_results.append(ap_ro_info['result']) + self.servo.record_uart_capture() + + def run_once(self): + """Save hash and trigger verification""" + self.ran_test = True + self.hash_results = [] + # The DBG image can set the hash when the board id is saved. The release + # image can't. Set the hash with the DBG image, so the test doesn't need + # to erase the board id. This test verifies triggering AP RO + # verification. It's not about saving the hash. + self.update_to_dbg_and_clear_hash() + self.set_hash() + self.rollback_to_release_image() + # CCD has to be open to trigger verification. + self.fast_ccd_open(True) + + # Trigger verification multiple times. Make sure it doesn't fail or + # change. + self.trigger_verification() + self.trigger_verification() + self.trigger_verification() + self.trigger_verification() + + self.servo.record_uart_capture() + cr50_uart_file = self.servo.get_uart_logfile('cr50') + if not cr50_uart_file: + logging.info('No cr50 uart file') + return + with open(cr50_uart_file, 'r') as f: + contents = f.read() + + self.recover_dut() + + # GSC only prints calculated and stored hashes after AP RO verificaiton + # fails. These sets will be empty if verification passed every time. + calculated = set(re.findall(self.CALCULATED_DIGEST_RE, contents)) + stored = set(re.findall(self.STORED_DIGEST_RE, contents)) + logging.info('Stored: %r', stored) + logging.info('Calculated: %r', calculated) + logging.info('Results: %r', self.hash_results) + + if self.VERIFICATION_FAILED in self.hash_results: + raise error.TestFail( + 'Verification failed -- stored: %r calculated: %r' % + (stored, calculated)) + if len(calculated) > 1: + raise error.TestFail('Multiple calculated digests %r' % calculated) + # This shouldn't happen. Raise TestNA, so it's easy to see. + if self.VERIFICATION_PASSED not in self.hash_results: + raise error.TestNAError( + 'Verification Not Run -- stored: %r calculated: %r' % + (stored, calculated)) + + # TODO(b/218705748): change the hash and verify verification fails. |