Home | History | Annotate | Download | only in firmware_Cr50Unlock
      1 # Copyright 2017 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 
      5 import logging
      6 
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.common_lib.cros import tpm_utils
      9 from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
     10 
     11 
     12 class firmware_Cr50Unlock(FirmwareTest):
     13     """Verify cr50 unlock using the console and gsctool.
     14 
     15     Enable the lock on cr50, run the different forms of unlock, making sure
     16     cr50 can or cannot be unlocked.
     17 
     18     This does not verify unlock with password.
     19     """
     20     version = 1
     21 
     22     def initialize(self, host, cmdline_args):
     23         """Initialize servo and check that it has access to cr50 with ccd"""
     24         super(firmware_Cr50Unlock, self).initialize(host, cmdline_args)
     25 
     26         if not hasattr(self, 'cr50'):
     27             raise error.TestNAError('Test can only be run on devices with '
     28                                     'access to the Cr50 console')
     29         if self.cr50.using_ccd():
     30             raise error.TestNAError('Use a flex cable instead of CCD cable.')
     31 
     32         self.host = host
     33 
     34 
     35     def gsctool_unlock(self, unlock_allowed):
     36         """Unlock cr50 using the gsctool command"""
     37         result = self.host.run('gsctool -a -U',
     38                 ignore_status=not unlock_allowed)
     39         if not unlock_allowed and (result.exit_status != 3 or
     40             'Error: rv 7, response 7' not in result.stderr):
     41             raise error.TestFail('unexpected lockout result %r', result)
     42         self.check_unlock(unlock_allowed)
     43 
     44 
     45     def console_unlock(self, unlock_allowed):
     46         """Unlock cr50 using the console command"""
     47         try:
     48             self.cr50.set_ccd_level('unlock')
     49         except error.TestFail, e:
     50             # The console cannot be used to unlock cr50 unless a password is
     51             # is set.
     52             if 'Unlock only allowed after password is set' in e.message:
     53                 logging.info('Unlock unsupported without password')
     54             else:
     55                 raise
     56         self.check_unlock(unlock_allowed)
     57 
     58 
     59     def check_unlock(self, unlock_allowed):
     60         """Check that cr50 is unlocked or locked
     61 
     62         Args:
     63             unlock_allowed: True or False on whether Cr50 should be able to
     64                             be unlocked.
     65         Raises:
     66             TestFail if the cr50 ccd state does not match unlock_allowed
     67         """
     68         unlocked = self.cr50.get_ccd_level() == 'unlock'
     69         if unlocked and not unlock_allowed:
     70             raise error.TestFail("Cr50 was unlocked when it shouldn't have "
     71                     "been")
     72         elif not unlocked and unlock_allowed:
     73             raise error.TestFail('Cr50 was not unlocked when it should have '
     74                     'been')
     75 
     76 
     77     def unlock_test(self, unlock_func, unlock_allowed):
     78         """Verify cr50 can or cannot be unlocked with the given unlock_func"""
     79         self.cr50.set_ccd_level('lock')
     80 
     81         # Clear the TPM owner. The login state can affect unlock abilities
     82         tpm_utils.ClearTPMOwnerRequest(self.host)
     83 
     84         unlock_func(unlock_allowed)
     85 
     86         # TODO: set password and try to unlock again. Make sure it succeeds.
     87         # no matter how it is being set.
     88 
     89 
     90     def run_once(self, ccd_lockout):
     91         """Verify cr50 lock behavior on v1 images and v0 images"""
     92         logging.info('ccd should %sbe locked out',
     93                 '' if ccd_lockout else 'not ')
     94         if self.cr50.has_command('ccdstate'):
     95             self.unlock_test(self.gsctool_unlock, not ccd_lockout)
     96             self.unlock_test(self.console_unlock, False)
     97             logging.info('ccd unlock is %s', 'locked out' if ccd_lockout else
     98                     'accessible')
     99         else:
    100             # pre-v1, cr50 cannot be unlocked. Make sure that's true
    101             logging.info(self.cr50.send_command_get_output('lock disable',
    102                     ['Access Denied\s+Usage: lock']))
    103             logging.info('Cr50 cannot be unlocked with ccd v0')
    104