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