blob: 5034a0c0d0dc8ab45e77f93087fb0a7a2de2e9e7 [file] [log] [blame]
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import time
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros import tpm_utils
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
class firmware_GSCUpdatePCR(FirmwareTest):
"""Verify GSC Update PCR."""
version = 1
INDEX = 5
# 7 is pretty random. It doesn't really matter. For testing purposes just
# verify the extended digest doesn't start with '36'
EXTEND_VALUE = 7
CMD_READ_PCR = 'trunks_client --read_pcr --index=%d' % INDEX
CMD_EXTEND_PCR = 'trunks_client --extend_pcr --index=%d --value=' % INDEX
ZERO_DIGEST = '0000000000000000000000000000000000000000000000000000000000000000'
def initialize(self, host, cmdline_args):
"""Initialize GSC."""
self.host = host
super(firmware_GSCUpdatePCR, self).initialize(host, cmdline_args)
if 'ccd' in self.servo.get_servo_version():
self.servo.disable_ccd_watchdog_for_test()
if self.faft_config.ec_forwards_short_pp_press:
raise error.TestNAError(
'Not supported on devices with ec_forwards_short_pp_press')
self.fast_ccd_open(True)
self.gsc.ccd_reset_and_wipe_tpm()
self.gsc.send_command('rddkeepalive disable')
self.gsc.ccd_disable()
def cleanup(self):
"""Open ccd to clear the tpm."""
try:
self.gsc.ccd_reset_and_wipe_tpm()
finally:
super(firmware_GSCUpdatePCR, self).cleanup()
def resume(self, enter_ds):
"""Resume from suspend."""
ds_disabled = self.gsc.ccdstate_ds_disabled()
if enter_ds:
self.gsc.clear_deep_sleep_count()
time.sleep(self.gsc.DEEP_SLEEP_DELAY)
else:
time.sleep(10)
ap_off = not self.gsc.ap_is_on()
logging.info('Pressing power button to resume')
self._power_state = self.get_power_state()
self.servo.power_normal_press()
entered_ds = not not self.gsc.get_deep_sleep_count()
logging.info('Entered DS: %s', entered_ds)
if enter_ds:
if not ap_off:
logging.info('AP did not turn off during suspend')
elif ds_disabled:
logging.info('AP disabled deep sleep')
elif not entered_ds:
raise error.TestFail(
'Did not enter deep sleep when ap was off')
return entered_ds
def suspend_and_resume(self, enter_ds):
"""Suspend and resume."""
if enter_ds:
self.gsc.ccd_disable()
elif self.gsc.NAME == 'cr50':
# Send command to disable deep sleep in S3.
self.host.run('trunks_send --raw 80010000000c20000000003b')
else:
self.gsc.ccd_enable()
self.suspend()
return self.resume(enter_ds)
def send_shutdown(self):
"""Send the tpm shutdown command to update the pcr value."""
tpm_utils.SendTPMShutdownState(self.host)
def read_pcr(self):
"""Get the PCR 5 gigest."""
result = self.host.run(self.CMD_READ_PCR)
logging.info('Read PCR: %s', result.stdout)
return result.stdout.split(':')[-1].strip()
def extend_pcr(self, value):
"""Extend PCR 5 with the given value."""
result = self.host.run('%s%s' % (self.CMD_EXTEND_PCR, value))
logging.info('Extend PCR: %s', result.stdout)
return result.stdout
def assert_digest_is_non_zero(self, desc):
"""Verify the PCR digest is some non-zero value."""
digest = self.read_pcr()
if digest == self.ZERO_DIGEST:
raise error.TestFail('Digest is %s zero %s' % (digest, desc))
return digest
def assert_digest_is_zero(self, desc):
"""Verify the digest is all zeroes."""
digest = self.read_pcr()
if digest != self.ZERO_DIGEST:
raise error.TestFail('Digest is %s not zero %s' % (digest, desc))
def run_once(self):
"""Check cr50 can see dev mode correctly."""
# After reboot, the pcr 5 digest should be 0.
self.host.reboot()
self.assert_digest_is_zero('after first reboot')
# Extend it with some value. The value doesn't really matter.
self.extend_pcr(self.EXTEND_VALUE)
self.assert_digest_is_non_zero('after first extend')
# Send tpm shutdown
self.send_shutdown()
# Extend it again.
self.extend_pcr(self.EXTEND_VALUE)
updated_digest = self.assert_digest_is_non_zero('after second extend')
# Enter S3 and resume without entering deep sleep. This should send shutdown
# and trigger update pcr should be equal to updated_digest.
self.suspend_and_resume(False)
digest_after_resume = self.read_pcr()
logging.info('Before %s suspend: %s', self._power_state,
updated_digest)
logging.info('After %s resume: %s', self._power_state,
digest_after_resume)
if digest_after_resume != updated_digest:
raise error.TestFail('Digest changed after suspend/resume')
# Reboot device
self.host.reboot()
# pcr 5 should be back to 0
self.assert_digest_is_zero('after second reboot')
# Suspend and resume
entered_ds = self.suspend_and_resume(True)
# The loaded pcr 5 digest should still be 0
self.assert_digest_is_zero(
'after %s resume (%s ds)' %
(self._power_state, 'enetered' if entered_ds else 'no'))