1# Copyright (c) 2012 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 time 7 8from autotest_lib.client.common_lib import error 9from autotest_lib.client.common_lib.cros import cr50_utils 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11from autotest_lib.server.cros import vboot_constants as vboot 12 13 14class firmware_SoftwareSync(FirmwareTest): 15 """ 16 Servo based EC software sync test. 17 """ 18 version = 1 19 20 def initialize(self, host, cmdline_args, dev_mode=False): 21 # This test tries to corrupt EC firmware. Should disable EC WP. 22 super(firmware_SoftwareSync, self).initialize(host, cmdline_args, 23 ec_wp=False) 24 25 # Don't bother if there is no Chrome EC. 26 if not self.check_ec_capability(): 27 raise error.TestNAError("Nothing needs to be tested on this device") 28 29 if self._no_ec_sync: 30 raise error.TestNAError( 31 "User selected to disable EC software sync") 32 33 # In order to test software sync, it must be enabled. 34 self.clear_set_gbb_flags(vboot.GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC, 0) 35 self.backup_firmware() 36 self.switcher.setup_mode('dev' if dev_mode else 'normal') 37 self.setup_usbkey(usbkey=False) 38 self.setup_rw_boot() 39 self.dev_mode = dev_mode 40 41 if self.ec.check_feature('EC_FEATURE_EFS2'): 42 self.original_ccd_level = self.cr50.get_ccd_level() 43 44 # CCD needs to be open for 'ec_comm corrupt' run. 45 self.fast_ccd_open(reset_ccd=False, dev_mode=dev_mode) 46 logging.info("CCD opened.") 47 else: 48 self.original_ccd_level = None 49 50 # Check for cr50 support on a device and get the boot mode. It should 51 # be NORMAL at this point, even if EFS2 is not supported. 52 if hasattr(self, 'cr50'): 53 res = cr50_utils.GSCTool(host, ['-a', '-g']).stdout.strip() 54 if 'NORMAL' in res: 55 pass 56 elif 'error' in res.lower(): 57 raise error.TestFail('TPM Vendor command GET_BOOT_MODE failed:' 58 ' %r' % res) 59 else: 60 raise error.TestFail('GET_BOOT_MODE did not return NORMAL:' 61 ' %r' % res) 62 63 def cleanup(self): 64 try: 65 if self.is_firmware_saved(): 66 self.restore_firmware() 67 except Exception as e: 68 logging.error("Caught exception: %s", str(e)) 69 70 try: 71 if self.original_ccd_level: 72 self.cr50.set_ccd_level(self.original_ccd_level) 73 except Exception as e: 74 logging.error("Failed to restore ccd to %r: %s", 75 self.original_ccd_level, str(e)) 76 77 super(firmware_SoftwareSync, self).cleanup() 78 79 def record_hash(self): 80 """Record current EC hash.""" 81 self._ec_hash = self.faft_client.ec.get_active_hash() 82 logging.info("Stored EC hash: %s", self._ec_hash) 83 84 def corrupt_active_rw(self): 85 """Corrupt the active RW portion.""" 86 section = 'rw' 87 try: 88 if self.servo.get_ec_active_copy() == 'RW_B': 89 section = 'rw_b' 90 except error.TestFail: 91 # Skip the failure, as ec_active_copy is new. 92 # TODO(waihong): Remove this except clause. 93 pass 94 logging.info("Corrupt the EC section: %s", section) 95 self.faft_client.ec.corrupt_body(section) 96 97 def software_sync_checker(self): 98 """Check EC firmware is restored by software sync.""" 99 ec_hash = self.faft_client.ec.get_active_hash() 100 logging.info("Current EC hash: %s", ec_hash) 101 if self._ec_hash != ec_hash: 102 return False 103 return self.checkers.ec_act_copy_checker('RW') 104 105 def wait_software_sync_and_boot(self): 106 """Wait for software sync to update EC.""" 107 if self.dev_mode: 108 time.sleep(self.faft_config.software_sync_update + 109 self.faft_config.firmware_screen) 110 self.servo.ctrl_d() 111 else: 112 time.sleep(self.faft_config.software_sync_update) 113 114 def run_test_corrupt_ec_rw(self): 115 """Runs a single iteration of the test.""" 116 logging.info("Corrupt EC firmware RW body.") 117 self.check_state((self.checkers.ec_act_copy_checker, 'RW')) 118 self.record_hash() 119 self.corrupt_active_rw() 120 logging.info("Reboot AP, check EC hash, and software sync it.") 121 self.switcher.simple_reboot(reboot_type='warm') 122 self.wait_software_sync_and_boot() 123 self.switcher.wait_for_client() 124 125 logging.info("Expect EC in RW and RW is restored.") 126 self.check_state(self.software_sync_checker) 127 128 def run_test_corrupt_hash_in_cr50(self): 129 """Run the test corrupting ECRW hash in CR50.""" 130 131 self.check_state((self.checkers.ec_act_copy_checker, 'RW')) 132 self.record_hash() 133 134 logging.info('Corrupt ECRW hashcode in TPM kernel NV index.') 135 ec_corrupt_cmd = 'ec_comm corrupt' 136 self.cr50.send_command(ec_corrupt_cmd) 137 138 try: 139 # Reboot EC but keep AP off, so that it can earn a time to check 140 # if boot_mode is NO_BOOT and EC is in RO. 141 logging.info('Reset EC with AP off.') 142 self.ec.reboot('ap-off') 143 time.sleep(5) 144 145 # The boot mode should be "NO_BOOT". 146 logging.info('Check the boot mode is NO_BOOT mode.') 147 if not self.cr50.check_boot_mode('NO_BOOT'): 148 raise error.TestFail('Boot mode is not NO_BOOT.') 149 150 # Check if the current EC image is RO. 151 logging.info('Check EC is in RO.') 152 if not self.ec.check_ro_rw('RO'): 153 raise error.TestFail('EC is not in RO mode.') 154 finally: 155 # Wake up AP by tapping power button. 156 logging.info('Wake up AP.') 157 self.servo.power_short_press() 158 159 # Wait for software sync is done. 160 self.wait_software_sync_and_boot() 161 self.switcher.wait_for_client() 162 163 # The boot mode should be "NORMAL". 164 logging.info('Check the boot mode is NORMAL mode.') 165 if not self.cr50.check_boot_mode('NORMAL'): 166 logging.warning('You may want to run %r in cr50 console to uncorrupt' 167 ' EC hash.', ec_corrupt_cmd) 168 raise error.TestFail('Boot mode is not NORMAL.') 169 170 logging.info('Expect EC in RW and RW is restored.') 171 self.check_state(self.software_sync_checker) 172 173 def run_once(self): 174 """Entry point of this test.""" 175 self.run_test_corrupt_ec_rw() 176 177 if self.ec.check_feature('EC_FEATURE_EFS2'): 178 self.run_test_corrupt_hash_in_cr50() 179