blob: c7dc40865f11b938e50b410c610c0bb2e1e484de [file] [log] [blame]
# 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.