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 time 7import os 8 9from autotest_lib.client.common_lib import error 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11 12 13class firmware_WilcoDiagnosticsMode(FirmwareTest): 14 """Corrupt the Wilco diagnostics image and then reinstall it. 15 16 Wilco supports entry into a diagnostics image from recovery mode. The image 17 is stored in the RW_LEGACY firmware section and updated during AP firmware 18 updates. Entry into the image should fail if the image is corrupted. 19 Updating the firmware should restore the diagnostics image. 20 """ 21 version = 1 22 23 # The delay between pressing <F12> to enter diagnostics mode and reaching 24 # the confirmation screen; typically about 10 seconds; overshoot to be safe. 25 DIAGNOSTICS_CONFIRM_SCREEN_DELAY_SECONDS = 20 26 # The delay between pressing <Power> to confirm entry to diagnostics mode 27 # and rebooting into diagnostics mode. 28 DIAGNOSTICS_CONFIRM_REBOOT_DELAY_SECONDS = 8 29 # The delay between rebooting to enter diagnostics mode and rebooting again 30 # if that fails. 31 DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS = 8 32 # The name of the diagnostics image file in CBFS. 33 DIAG_CBFS_NAME = 'altfw/diag' 34 35 def initialize(self, host, cmdline_args): 36 super(firmware_WilcoDiagnosticsMode, self).initialize( 37 host, cmdline_args) 38 39 if not self.faft_config.has_diagnostics_image: 40 raise error.TestNAError('No diagnostics image for this board.') 41 42 self.setup_firmwareupdate_shellball(shellball=None) 43 # Make sure that the shellball is retained over subsequent power cycles. 44 self.blocking_sync() 45 self.switcher.setup_mode('normal') 46 47 def cleanup(self): 48 self._client.reset_via_servo() 49 50 super(firmware_WilcoDiagnosticsMode, self).cleanup() 51 52 def _corrupt_diagnostics_image(self): 53 # Extract the diagnostics image from the firmware image, corrupt the 54 # image, and write a new firmware image with that corrupt diagnostics 55 # image. 56 local_filename = 'diag.bin' 57 cbfs_work_dir = self.faft_client.updater.cbfs_setup_work_dir() 58 bios_cbfs_path = os.path.join(cbfs_work_dir, 59 self.faft_client.updater.get_bios_relative_path()) 60 diag_cbfs_path = os.path.join(cbfs_work_dir, local_filename) 61 62 logging.info('Extracting diagnostics') 63 self.faft_client.updater.cbfs_extract_diagnostics(self.DIAG_CBFS_NAME, 64 diag_cbfs_path) 65 66 logging.info('Corrupting diagnostics') 67 self.faft_client.updater.corrupt_diagnostics_image(diag_cbfs_path) 68 69 logging.info('Replacing diagnostics') 70 self.faft_client.updater.cbfs_replace_diagnostics(self.DIAG_CBFS_NAME, 71 diag_cbfs_path) 72 73 logging.info('Writing back BIOS') 74 self.faft_client.bios.write_whole(bios_cbfs_path) 75 self.switcher.mode_aware_reboot() 76 77 def _press_f12(self): 78 self.servo.set_nocheck('arb_key_config', '<f12>') 79 self.servo.set_nocheck('arb_key', 'tab') 80 81 def _enter_diagnostics_mode(self): 82 # Reboot to the recovery screen, press <F12>, and press power to 83 # confirm. 84 self.servo.switch_usbkey('host') 85 psc = self.servo.get_power_state_controller() 86 logging.info('Powering off') 87 if self.cr50.ap_is_on(): 88 self.servo.power_key(self.faft_config.hold_pwr_button_poweroff) 89 logging.info('Waiting for power off') 90 time.sleep(1) 91 self._client.close_main_ssh() 92 logging.info('Booting to recovery screen') 93 psc.power_on(psc.REC_ON) 94 95 logging.info('Sleeping %s seconds (firmware_screen)', 96 self.faft_config.firmware_screen) 97 time.sleep(self.faft_config.firmware_screen) 98 if not self.cr50.ap_is_on(): 99 raise error.TestFail('Expected AP on when booting to recovery') 100 logging.info('Pressing <F12>') 101 self._press_f12() 102 logging.info( 103 'Sleeping %s seconds (DIAGNOSTICS_CONFIRM_SCREEN_DELAY_SECONDS)', 104 self.DIAGNOSTICS_CONFIRM_SCREEN_DELAY_SECONDS) 105 time.sleep(self.DIAGNOSTICS_CONFIRM_SCREEN_DELAY_SECONDS) 106 logging.info('Pressing <Power> to confirm') 107 self.servo.power_short_press() 108 # At this point, the DUT will try to reboot into diagnostics mode. 109 110 def _verify_diagnostics_mode(self): 111 """Checks that the AP is on and ssh fails. 112 113 This is not certain that we are in the diagnostic mode, but gives some 114 confidence. 115 """ 116 # Wait long enough that DUT would have rebooted to normal mode if 117 # diagnostics mode failed. 118 logging.info( 119 'Sleeping %s seconds (DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS)', 120 self.DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS) 121 time.sleep(self.DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS) 122 if not self.cr50.ap_is_on(): 123 raise error.TestFail( 124 'AP is off, expected diagnostics mode. Is diagnostic ' 125 'corrupted? Run chromeos-firmwareupdate --mode=recovery') 126 logging.info('Sleeping %s seconds (delay_reboot_to_ping)', 127 self.faft_config.delay_reboot_to_ping) 128 time.sleep(self.faft_config.delay_reboot_to_ping) 129 self.switcher.wait_for_client_offline(timeout=5) 130 logging.info('DUT offline after entering diagnostics mode') 131 132 def run_once(self): 133 """Run the body of the test.""" 134 logging.info('Attempting to enter diagnostics mode') 135 self._enter_diagnostics_mode() 136 self._verify_diagnostics_mode() 137 self._client.reset_via_servo() 138 self.switcher.wait_for_client() 139 140 # Corrupt the diagnostics image, try to reboot into diagnostics mode, 141 # and verify that the DUT ends up in normal mode (indicating failure to 142 # enter diagnostics mode). 143 self._corrupt_diagnostics_image() 144 self._enter_diagnostics_mode() 145 logging.info( 146 'Sleeping %s seconds (DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS)', 147 self.DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS) 148 time.sleep(self.DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS) 149 # If the diagnostic mode fails, it might just power off 150 if not self.cr50.ap_is_on(): 151 logging.info('AP off, pressing <Power> to boot to normal mode') 152 self.servo.power_short_press() 153 self.switcher.wait_for_client() 154 self.check_state((self.checkers.mode_checker, 'normal')) 155 156 # Update the firmware to restore the diagnostics image, reboot into 157 # diagnostics mode, and verify that the DUT goes down (indicating 158 # success). 159 logging.info('Updating firmware') 160 self.faft_client.updater.run_autoupdate(None) 161 logging.info('Rebooting to apply firmware update') 162 self.switcher.mode_aware_reboot() 163 164 logging.info('Attempting to enter diagnostics mode') 165 self._enter_diagnostics_mode() 166 self._verify_diagnostics_mode() 167