| # Copyright 2019 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import re |
| import time |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server.cros.faft.cr50_test import Cr50Test |
| |
| |
| class firmware_Cr50WPG3(Cr50Test): |
| """Verify WP in G3.""" |
| version = 1 |
| |
| WAIT_FOR_STATE = 20 |
| CMD_FIND_WP_GPIO = 'ectool gpioget | grep -i wp | grep -vi cbi' |
| WP_REGEX = r'WP status: ((en|dis)abled)' |
| STATUS_CMD = '--wp-status --ignore-hw' |
| WP_ENABLE_CMD = '--wp-enable' |
| WP_DISABLE_CMD = '--wp-disable' |
| DISABLED = 'disabled' |
| ENABLED = 'enabled' |
| CUSTOM_RST = ',custom_rst=true' |
| |
| def cleanup(self): |
| """Reenable servo wp.""" |
| self.gsc.send_command('ccd testlab open') |
| try: |
| # Reset the WP state. |
| if hasattr(self, '_start_fw_wp_vref'): |
| self.set_gsc_hw_wp('disable atboot') |
| self.ccd_set_sw_wp(self.WP_DISABLE_CMD) |
| self.set_gsc_hw_wp('follow_batt_pres atboot') |
| self.servo.set_nocheck('fw_wp_state', self._start_fw_wp_state) |
| if self._start_fw_wp_vref: |
| self.servo.set_nocheck('fw_wp_vref', self._start_fw_wp_vref) |
| self.gsc.send_command('rddkeepalive disable') |
| self.enable_servo_ec_uart() |
| finally: |
| super(firmware_Cr50WPG3, self).cleanup() |
| |
| def generate_futility_wp_cmd(self): |
| """Use the cr50 serialname to generate the futility command.""" |
| self._futility_cmd = ( |
| 'sudo futility flash -p raiden_debug_spi:target=AP,serial=%s%s ' |
| % (self.gsc.get_serial(), |
| self.CUSTOM_RST if self._use_custom_rst else '')) |
| |
| def ccd_get_sw_wp(self): |
| """Returns 'on' if write protect is enabled. 'off' if it's disabled.""" |
| output = self.servo.system_output(self._futility_cmd + self.STATUS_CMD) |
| m = re.search(self.WP_REGEX, output) |
| logging.info('SW WP is %s', m.group(1) if m else 'UKNOWN') |
| logging.info('futility output\n%s', output) |
| if not m: |
| raise error.TestError( |
| 'Unable to find WP status in futility output') |
| return m.group(1) |
| |
| def ccd_set_sw_wp(self, cmd): |
| """Set SW WP.""" |
| self.enable_ccd_spi() |
| time.sleep(self.gsc.SHORT_WAIT) |
| output = self.servo.system_output(self._futility_cmd + cmd, |
| ignore_status=True) |
| logging.debug('SW WP output: %r', output) |
| time.sleep(self.gsc.SHORT_WAIT) |
| sw_wp = self.ccd_get_sw_wp() |
| # Done with ccd spi access. Reenable servo ec uart access. |
| self.enable_servo_ec_uart() |
| return sw_wp |
| |
| def find_ec_wp_gpio_name(self): |
| """Find the EC WP gpio name from ectool gpioget.""" |
| self.wp_gpio = None |
| result = self.host.run(self.CMD_FIND_WP_GPIO).stdout.strip() |
| logging.info('WP gpio output: %s', result) |
| if len(result.splitlines()) > 1: |
| logging.info('Too many wp lines.') |
| return |
| self.wp_gpio = result.split()[1] |
| logging.info('WP GPIO: %s', self.wp_gpio) |
| |
| def get_ec_hw_wp(self): |
| """Check the WP GPIO from the EC console.""" |
| if not self.wp_gpio: |
| return |
| for i in range(3): |
| try: |
| rv = self.ec.send_command_get_output( |
| 'gpioget %s' % self.wp_gpio, |
| ['gpioget.*([01]).*%s.*>' % self.wp_gpio])[0][1] |
| logging.info('EC %s: %s', self.wp_gpio, rv) |
| return |
| except Exception as e: |
| logging.info('Issue getting ec wp: %s', e) |
| |
| def set_ccd_cpu_fw_spi(self, state): |
| """Set ccd_cpu_fw_spi if the board uses custom reset.""" |
| if not self._use_custom_rst: |
| return |
| logging.info('Setting ccd_cpu_fw_spi %s', state) |
| logging.info('%s is active', self.servo.get('active_dut_controller')) |
| self.servo.set_nocheck('ccd_cpu_fw_spi', state) |
| time.sleep(2) |
| logging.info('Set ccd_cpu_fw_spi %s', state) |
| logging.info('%s is active', self.servo.get('active_dut_controller')) |
| self.gsc.get_ccdstate() |
| |
| def enable_ccd_spi(self): |
| """Enable ccd spi access. |
| |
| Make GSC ignore servo, so it can enable CCD SPI access. |
| """ |
| # There's no need to switch servo control if the main device is ccd. |
| if self.servo.main_device_is_ccd(): |
| self.set_ccd_cpu_fw_spi('on') |
| return |
| |
| self.gsc.send_command('rddkeepalive enable') |
| # Setup the AP flash access. This could require EC uart access. It |
| # has to be done before making ccd active. |
| self.servo.enable_main_servo_device() |
| self.set_ccd_cpu_fw_spi('on') |
| |
| # Enable the CCD servo device. |
| self.servo.enable_ccd_servo_device() |
| time.sleep(2) |
| |
| def enable_servo_ec_uart(self): |
| """Enable servo control of ec uart.""" |
| # There's no need to switch servo control if the main device is ccd. |
| if self.servo.main_device_is_ccd(): |
| self.set_ccd_cpu_fw_spi('off') |
| return |
| self.servo.set('ec_uart_en', 'on') |
| self.servo.enable_main_servo_device() |
| self.set_ccd_cpu_fw_spi('off') |
| time.sleep(2) |
| self.gsc.get_ccdstate() |
| |
| def check_state_from_consoles(self): |
| """Log device state from EC and GSC consoles.""" |
| self.gsc.get_ccdstate() |
| logging.info('EC Power State: %s', self.get_power_state()) |
| self.get_ec_hw_wp() |
| self.gsc.get_wp_state() |
| |
| def set_gsc_hw_wp(self, state): |
| """Open GSC. Set GSC wp.""" |
| logging.info('Set GSC WP: %s', state) |
| self.gsc.set_wp_state(state) |
| |
| def run_once(self): |
| """Verify WP in G3.""" |
| self._use_custom_rst = self.servo.has_control('ccd_cpu_fw_spi') |
| if not self.servo.get_ccd_servo_device(): |
| raise error.TestNAError('Only supported with dual-v4') |
| if not self.check_ec_capability(suppress_warning=True): |
| raise error.TestNAError('Only supported on boards with ECs') |
| if self.check_cr50_capability(['wp_on_in_g3'], suppress_warning=True): |
| raise error.TestNAError('config: WP not pulled up in G3') |
| try: |
| self.gsc.ccd_enable(True) |
| except: |
| raise error.TestNAError('CCD required to check wp.') |
| self.find_ec_wp_gpio_name() |
| self.generate_futility_wp_cmd() |
| self.fast_ccd_open(enable_testlab=True) |
| self.gsc.send_command('ccd set OverrideWP Always') |
| self.gsc.send_command('ccd set FlashAP Always') |
| |
| if self.servo.has_control('cold_reset_select'): |
| self.servo.set_nocheck('cold_reset_select', 'gsc_ec_reset') |
| |
| if self.servo.main_device_is_flex(): |
| self._start_fw_wp_state = self.servo.get('fw_wp_state') |
| # If nothing is forcing write protect, then the initial state is |
| # "reset". |
| if "force" not in self._start_fw_wp_state: |
| self._start_fw_wp_state = "reset" |
| self._start_fw_wp_vref = (self.servo.get('fw_wp_vref') if |
| self.servo.has_control('fw_wp_vref') else None) |
| # Stop forcing wp using servo, so we can set it with ccd. |
| self.servo.set_nocheck('fw_wp_state', 'reset') |
| if self._start_fw_wp_vref: |
| self.servo.set_nocheck('fw_wp_vref', 'off') |
| |
| # Disable HW WP. |
| self.set_gsc_hw_wp('disable atboot') |
| |
| # Disable SW WP. |
| wp_state = self.ccd_set_sw_wp(self.WP_DISABLE_CMD) |
| # Bring the DUT up since futility commands put the EC in reset. |
| self._try_to_bring_dut_up() |
| if wp_state != self.DISABLED: |
| raise error.TestFail('Unable to disable SW WP with HW WP enabled') |
| |
| # Verify we can see it's disabled. This should always work. If it |
| # doesn't, it may be a setup issue. |
| logging.info('Checking WP from DUT') |
| self.check_state_from_consoles() |
| if not self.checkers.crossystem_checker({'wpsw_cur': '0'}): |
| raise error.TestError("HW WP isn't disabled in S0") |
| |
| # Enable HW WP. |
| self.set_gsc_hw_wp('enable atboot') |
| self.check_state_from_consoles() |
| if not self.checkers.crossystem_checker({'wpsw_cur': '1'}): |
| raise error.TestError("HW WP isn't enabled in S0") |
| |
| self.faft_client.system.run_shell_command('poweroff') |
| time.sleep(self.WAIT_FOR_STATE) |
| self.check_state_from_consoles() |
| |
| # SW WP can be enabled at any time. |
| logging.info('Check enabling SW WP with HW WP enabled') |
| if self.ccd_set_sw_wp(self.WP_ENABLE_CMD) != self.ENABLED: |
| self.gsc.reboot() |
| self._try_to_bring_dut_up() |
| raise error.TestFail('Unable to enable SW WP in G3') |
| |
| # It shouldn't be possible to disable SW WP with HW WP enabled. |
| logging.info('Check disabling SW WP with HW WP enabled') |
| self.check_state_from_consoles() |
| if self.ccd_set_sw_wp(self.WP_DISABLE_CMD) == self.DISABLED: |
| self.gsc.reboot() |
| self._try_to_bring_dut_up() |
| raise error.TestFail('Disabled SW WP with HW WP enabled') |
| |
| # It should be possible to disable SW WP after HW WP is disabled. |
| logging.info('Check disabling SW WP with HW WP disabled') |
| # Disable HW WP. |
| self.set_gsc_hw_wp('disable atboot') |
| self.check_state_from_consoles() |
| # Verify setting SW WP. |
| if self.ccd_set_sw_wp(self.WP_DISABLE_CMD) != self.DISABLED: |
| self.gsc.reboot() |
| self._try_to_bring_dut_up() |
| raise error.TestFail('Disabled SW WP with HW WP enabled') |
| self._try_to_bring_dut_up() |