| # Copyright 2019 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import re |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server.cros.faft.firmware_test import FirmwareTest |
| |
| class firmware_ECCbiEeprom(FirmwareTest): |
| """Servo-based EC test for Cros Board Info EEPROM""" |
| version = 1 |
| |
| EEPROM_LOCATE_TYPE = 0 |
| EEPROM_LOCATE_INDEX = 0 # Only one EEPROM ever |
| |
| # Test data to attempt to write to EEPROM |
| TEST_EEPROM_DATA = ('0xaa ' * 16).strip() |
| TEST_EEPROM_DATA_2 = ('0x55 ' * 16).strip() |
| |
| # Size of read and write |
| PAGE_SIZE = 16 |
| NO_READ = 0 |
| |
| # The number of bytes we verify are both writable and write protectable |
| MAX_BYTES = 64 |
| |
| def initialize(self, host, cmdline_args): |
| super(firmware_ECCbiEeprom, self).initialize(host, cmdline_args) |
| # Don't bother if CBI isn't on this device. |
| if not self.check_ec_capability(['cbi']): |
| raise error.TestNAError("Nothing needs to be tested on this device") |
| cmd = 'ectool locatechip %d %d' % (self.EEPROM_LOCATE_TYPE, |
| self.EEPROM_LOCATE_INDEX) |
| cmd_out = self.faft_client.System.RunShellCommandGetOutput(cmd, True) |
| logging.debug('Ran %s on DUT, output was: %s', cmd, cmd_out) |
| |
| if len(cmd_out) > 0 and cmd_out[0].startswith('Usage'): |
| raise error.TestNAError("I2C lookup not supported yet.") |
| |
| if len(cmd_out) < 1: |
| cmd_out = [''] |
| |
| match = re.search('Bus: I2C; Port: (\w+); Address: (\w+)', cmd_out[0]) |
| if match is None: |
| raise error.TestFail("I2C lookup for EEPROM CBI Failed. Check " |
| "debug log for output.") |
| |
| # Allow hex value parsing (i.e. base set to 0) |
| self.i2c_port = int(match.group(1), 0) |
| self.i2c_addr = int(match.group(2), 0) |
| |
| # Ensure that the i2c mux is disabled on the servo as the CBI EEPROM |
| # i2c lines are shared with the servo lines on some HW designs. We do |
| # not want the servo to artificially drive the i2c lines during this |
| # test. This only applies to non CCD servo connections |
| if not self.servo.get_servo_version().endswith('ccd_cr50'): |
| self.servo.set('i2c_mux_en', 'off') |
| |
| def _gen_write_command(self, offset, data): |
| return ('ectool i2cxfer %d %d %d %d %s' % |
| (self.i2c_port, self.i2c_addr, self.NO_READ, offset, data)) |
| |
| def _read_eeprom(self, offset): |
| cmd_out = self.faft_client.System.RunShellCommandGetOutput( |
| 'ectool i2cxfer %d %d %d %d' % |
| (self.i2c_port, self.i2c_addr, self.PAGE_SIZE, offset)) |
| if len(cmd_out) < 1: |
| raise error.TestFail( |
| "Could not read EEPROM data at offset %d" % (offset)) |
| data = re.search('Read bytes: (.+)', cmd_out[0]).group(1) |
| if data == '': |
| raise error.TestFail( |
| "Empty EEPROM read at offset %d" % (offset)) |
| return data |
| |
| def _write_eeprom(self, offset, data): |
| # Note we expect this call to fail in certain scenarios, so ignore |
| # results |
| self.faft_client.System.RunShellCommandGetOutput( |
| self._gen_write_command(offset, data)) |
| |
| def _read_write_data(self, offset): |
| before = self._read_eeprom(offset) |
| logging.info("To reset CBI that's in a bad state, run w/ WP off:\n%s", |
| self._gen_write_command(offset, before)) |
| |
| if before == self.TEST_EEPROM_DATA: |
| write_data = self.TEST_EEPROM_DATA_2 |
| else: |
| write_data = self.TEST_EEPROM_DATA |
| |
| self._write_eeprom(offset, write_data) |
| |
| after = self._read_eeprom(offset) |
| |
| return before, write_data, after |
| |
| def check_eeprom_write_protected(self): |
| """Checks that CBI EEPROM cannot be written to when WP is asserted""" |
| self.set_hardware_write_protect(True) |
| offset = 0 |
| |
| for offset in range(0, self.MAX_BYTES, self.PAGE_SIZE): |
| before, write_data, after = self._read_write_data(offset) |
| |
| if before != after: |
| # Set the data back to the original value before failing |
| self._write_eeprom(offset, before) |
| |
| raise error.TestFail('EEPROM data changed with write protect ' |
| 'enabled. Offset %d' % (offset)) |
| |
| return True |
| |
| def check_eeprom_without_write_protected(self): |
| """Checks that CBI EEPROM can be written to when WP is de-asserted""" |
| self.set_hardware_write_protect(False) |
| offset = 0 |
| |
| for offset in range(0, self.MAX_BYTES, self.PAGE_SIZE): |
| before, write_data, after = self._read_write_data(offset) |
| |
| if write_data != after: |
| raise error.TestFail('EEPROM did not update with write protect ' |
| 'disabled. Offset %d' % (offset)) |
| |
| # Set the data back to the original value |
| self._write_eeprom(offset, before) |
| |
| return True |
| |
| def run_once(self): |
| """Execute the main body of the test.""" |
| |
| logging.info("Checking CBI EEPROM with write protect off...") |
| self.check_eeprom_without_write_protected() |
| |
| logging.info("Checking CBI EEPROM with write protect on...") |
| self.check_eeprom_write_protected() |