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