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