147 lines
5.6 KiB
Python
147 lines
5.6 KiB
Python
# 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
|
|
from autotest_lib.server.cros.servo import servo
|
|
|
|
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 ' * 8).strip()
|
|
TEST_EEPROM_DATA_2 = ('0x55 ' * 8).strip()
|
|
|
|
# Size of read and write. Use 8-bytes as this will work with EEPROMs with
|
|
# page size 8 or 16 bytes. We allow 8-bytes page size parts.
|
|
PAGE_SIZE = 8
|
|
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.run_shell_command_get_output(
|
|
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. If the
|
|
# control does not exist, ignore error
|
|
try:
|
|
self.servo.set('i2c_mux_en', 'off')
|
|
logging.info("i2c_mux_en present and reset.")
|
|
except servo.ControlUnavailableError:
|
|
logging.info("i2c_mux_en does not exist. Ignoring.")
|
|
|
|
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.run_shell_command_get_output(
|
|
'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.run_shell_command_get_output(
|
|
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()
|