• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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