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