• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 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 re
7
8from autotest_lib.client.common_lib import error
9from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
10from autotest_lib.server.cros.servo import servo
11
12class firmware_ECCbiEeprom(FirmwareTest):
13    """Servo-based EC test for Cros Board Info EEPROM"""
14    version = 1
15
16    EEPROM_LOCATE_TYPE = 0
17    EEPROM_LOCATE_INDEX = 0   # Only one EEPROM ever
18
19    # Test data to attempt to write to EEPROM
20    TEST_EEPROM_DATA = ('0xaa ' * 8).strip()
21    TEST_EEPROM_DATA_2 = ('0x55 ' * 8).strip()
22
23    # Size of read and write. Use 8-bytes as this will work with EEPROMs with
24    # page size 8 or 16 bytes. We allow 8-bytes page size parts.
25    PAGE_SIZE = 8
26    NO_READ = 0
27
28    # The number of bytes we verify are both writable and write protectable
29    MAX_BYTES = 64
30
31    def initialize(self, host, cmdline_args):
32        super(firmware_ECCbiEeprom, self).initialize(host, cmdline_args)
33        # Don't bother if CBI isn't on this device.
34        if not self.check_ec_capability(['cbi']):
35            raise error.TestNAError("Nothing needs to be tested on this device")
36        cmd = 'ectool locatechip %d %d' % (self.EEPROM_LOCATE_TYPE,
37                                           self.EEPROM_LOCATE_INDEX)
38        cmd_out = self.faft_client.system.run_shell_command_get_output(
39                cmd, True)
40        logging.debug('Ran %s on DUT, output was: %s', cmd, cmd_out)
41
42        if len(cmd_out) > 0 and cmd_out[0].startswith('Usage'):
43            raise error.TestNAError("I2C lookup not supported yet.")
44
45        if len(cmd_out) < 1:
46            cmd_out = ['']
47
48        match = re.search('Bus: I2C; Port: (\w+); Address: (\w+)', cmd_out[0])
49        if match is None:
50            raise error.TestFail("I2C lookup for EEPROM CBI Failed.  Check "
51                                 "debug log for output.")
52
53        # Allow hex value parsing (i.e. base set to 0)
54        self.i2c_port = int(match.group(1), 0)
55        self.i2c_addr = int(match.group(2), 0)
56
57        # Ensure that the i2c mux is disabled on the servo as the CBI EEPROM
58        # i2c lines are shared with the servo lines on some HW designs. If the
59        # control does not exist, ignore error
60        try:
61            self.servo.set('i2c_mux_en', 'off')
62            logging.info("i2c_mux_en present and reset.")
63        except servo.ControlUnavailableError:
64            logging.info("i2c_mux_en does not exist. Ignoring.")
65
66    def _gen_write_command(self, offset, data):
67        return ('ectool i2cxfer %d %d %d %d %s' %
68               (self.i2c_port, self.i2c_addr, self.NO_READ, offset, data))
69
70    def _read_eeprom(self, offset):
71        cmd_out = self.faft_client.system.run_shell_command_get_output(
72                  'ectool i2cxfer %d %d %d %d' %
73                  (self.i2c_port, self.i2c_addr, self.PAGE_SIZE, offset))
74        if len(cmd_out) < 1:
75            raise error.TestFail(
76                "Could not read EEPROM data at offset %d" % (offset))
77        data = re.search('Read bytes: (.+)', cmd_out[0]).group(1)
78        if data == '':
79            raise error.TestFail(
80                "Empty EEPROM read at offset %d" % (offset))
81        return data
82
83    def _write_eeprom(self, offset, data):
84        # Note we expect this call to fail in certain scenarios, so ignore
85        # results
86        self.faft_client.system.run_shell_command_get_output(
87             self._gen_write_command(offset, data))
88
89    def _read_write_data(self, offset):
90        before = self._read_eeprom(offset)
91        logging.info("To reset CBI that's in a bad state, run w/ WP off:\n%s",
92                     self._gen_write_command(offset, before))
93
94        if before == self.TEST_EEPROM_DATA:
95            write_data = self.TEST_EEPROM_DATA_2
96        else:
97            write_data = self.TEST_EEPROM_DATA
98
99        self._write_eeprom(offset, write_data)
100
101        after = self._read_eeprom(offset)
102
103        return before, write_data, after
104
105    def check_eeprom_write_protected(self):
106        """Checks that CBI EEPROM cannot be written to when WP is asserted"""
107        self.set_hardware_write_protect(True)
108        offset = 0
109
110        for offset in range(0, self.MAX_BYTES, self.PAGE_SIZE):
111            before, write_data, after = self._read_write_data(offset)
112
113            if before != after:
114                # Set the data back to the original value before failing
115                self._write_eeprom(offset, before)
116
117                raise error.TestFail('EEPROM data changed with write protect '
118                                     'enabled. Offset %d' % (offset))
119
120        return True
121
122    def check_eeprom_without_write_protected(self):
123        """Checks that CBI EEPROM can be written to when WP is de-asserted"""
124        self.set_hardware_write_protect(False)
125        offset = 0
126
127        for offset in range(0, self.MAX_BYTES, self.PAGE_SIZE):
128            before, write_data, after = self._read_write_data(offset)
129
130            if write_data != after:
131                raise error.TestFail('EEPROM did not update with write protect '
132                                     'disabled. Offset %d' % (offset))
133
134            # Set the data back to the original value
135            self._write_eeprom(offset, before)
136
137        return True
138
139    def run_once(self):
140        """Execute the main body of the test."""
141
142        logging.info("Checking CBI EEPROM with write protect off...")
143        self.check_eeprom_without_write_protected()
144
145        logging.info("Checking CBI EEPROM with write protect on...")
146        self.check_eeprom_write_protected()
147