1# Copyright (c) 2017 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"""Auto test for SiS firmware updater functionality and udev rule.""" 5 6from __future__ import print_function 7import logging 8import os 9import re 10import time 11 12from autotest_lib.client.common_lib.cros import power_cycle_usb_util 13 14from autotest_lib.client.common_lib import error 15from autotest_lib.server import test 16 17POWER_CYCLE_WAIT_TIME = 1 # seconds 18UPDATER_WAIT_TIME = 80 # seconds 19# This is the GPIO on guado. 20FRONT_LEFT_USB_GPIO = 218 21 22 23class enterprise_CFM_SiSFwUpdater(test.test): 24 """ 25 SiS firmware updater functionality test in Chrome Box. 26 27 The procedure of the test is: 28 1. flash old version FW to device, 29 2. power cycle usb port to simulate unplug and replug of device, which 30 should be able to trigger udev rule and run the updater, 31 3. wait for the updater to finish, 32 4. run fw updater again and verify that the FW in device is consistent with 33 latest FW within system by checking the output. 34 """ 35 36 version = 1 37 38 _LOG_FILE_PATH = '/tmp/sis-updater.log' 39 _FW_PATH = '/lib/firmware/sis/' 40 _OLD_FW_NAME = 'FW_Watchdog_0110.bin' 41 _NEW_FW_NAME = 'WYD_101_WYD_9255_A353_V04.bin' 42 _DUT_BOARD = 'guado' 43 _SIS_VID = '266e' 44 _SIS_PID = '0110' 45 46 def initialize(self, host): 47 self.host = host 48 self.log_file = self._LOG_FILE_PATH 49 self.old_fw_path = os.path.join(self._FW_PATH, self._OLD_FW_NAME) 50 self.new_fw_path = os.path.join(self._FW_PATH, self._NEW_FW_NAME) 51 self.usb_port_gpio_number = FRONT_LEFT_USB_GPIO 52 self.board = self._DUT_BOARD 53 self.vid = self._SIS_VID 54 self.pid = self._SIS_PID 55 # Open log file object. 56 self.log_file_obj = open(self.log_file, 'w') 57 58 def cleanup(self): 59 self.log_file_obj.close() 60 test.test.cleanup(self) 61 cmd = 'rm -f {}'.format(self.old_fw_path) 62 self._run_cmd(cmd) 63 64 def _run_cmd(self, command, str_compare='', print_output=False): 65 """ 66 Run command line on DUT. 67 68 Run commands on DUT. Wait for command to complete, then check the 69 output for expected string. 70 71 @param command: command line to run in dut. 72 @param str_compare: a piece of string we want to see in the output of 73 running the command. 74 @param print_output: if true, print command output in log. 75 76 @returns the command output and a bool value. If str_compare is in 77 command output, return true. Otherwise return false. 78 79 """ 80 81 logging.info('Execute: %s', command) 82 result = self.host.run(command, ignore_status=True) 83 if result.stderr: 84 output = result.stderr 85 else: 86 output = result.stdout 87 if print_output: 88 logging.info('Output: %s', ''.join(output)) 89 if str_compare and str_compare not in ''.join(output): 90 return output, False 91 else: 92 return output, True 93 94 def convert_rootfs_writable(self): 95 """Remove rootfs verification on DUT, reboot, 96 and remount the filesystem read-writable""" 97 98 logging.info('Disabling rootfs verification...') 99 self.remove_rootfs_verification() 100 101 logging.info('Rebooting...') 102 self.reboot() 103 104 logging.info('Remounting..') 105 cmd = 'mount -o remount,rw /' 106 self._run_cmd(cmd) 107 108 def remove_rootfs_verification(self): 109 """Remove rootfs verification.""" 110 111 # 2 & 4 are default partitions, and the system boots from one of them. 112 # Code from chromite/scripts/deploy_chrome.py 113 KERNEL_A_PARTITION = 2 114 KERNEL_B_PARTITION = 4 115 116 cmd_template = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d ' 117 '--remove_rootfs_verification --force') 118 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION): 119 cmd = cmd_template % partition 120 self._run_cmd(cmd) 121 122 def reboot(self): 123 """Reboots the DUT.""" 124 125 self.host.reboot() 126 127 def is_filesystem_readwrite(self): 128 """Check if the root file system is read-writable. 129 130 Query the DUT's filesystem /dev/root, often manifested as /dev/dm-0 131 or is mounted as read-only or not. 132 133 @returns True if the /dev/root is read-writable. False otherwise. 134 """ 135 136 cmd = 'cat /proc/mounts | grep "/dev/root"' 137 result, _ = self._run_cmd(cmd) 138 fields = re.split(' |,', result) 139 return True if fields.__len__() >= 4 and fields[3] == 'rw' else False 140 141 def copy_firmware(self): 142 """Copy test firmware from server to DUT.""" 143 144 current_dir = os.path.dirname(os.path.realpath(__file__)) 145 src_firmware_path = os.path.join(current_dir, self._OLD_FW_NAME) 146 dst_firmware_path = self._FW_PATH 147 logging.info('Copy firmware from {} to {}.'.format(src_firmware_path, 148 dst_firmware_path)) 149 self.host.send_file(src_firmware_path, dst_firmware_path, 150 delete_dest=True) 151 152 def triger_updater(self): 153 """Triger udev rule to run fw updater.""" 154 155 try: 156 power_cycle_usb_util.power_cycle_usb_vidpid(self.host, self.board, 157 self.vid, self.pid) 158 except KeyError: 159 raise error.TestFail('Counld\'t find target device: ' 160 'vid:pid {}:{}'.format(self.vid, self.pid)) 161 162 def flash_fw(self, fw_path, str_compare='', print_output=False): 163 """ 164 Flash certain firmware to device. 165 166 Run SiS firmware updater on DUT to flash the firmware given 167 by fw_path to target device (Mimo). 168 169 @param fw_path: the path to the firmware to flash. 170 @param str_compare, print_output: the same as function _run_cmd. 171 172 """ 173 cmd_run_updater = ('/usr/sbin/sis-updater ' 174 '-ba -log_to=stdout {}'.format(fw_path)) 175 output, succeed = self._run_cmd( 176 cmd_run_updater, str_compare=str_compare, print_output=print_output) 177 return output, succeed 178 179 def run_once(self): 180 """Main test procedure.""" 181 182 # Make the DUT filesystem writable. 183 if not self.is_filesystem_readwrite(): 184 logging.info('DUT root file system is not read-writable. ' 185 'Converting it read-writable...') 186 self.convert_rootfs_writable() 187 else: 188 logging.info('DUT is read-writable.') 189 190 # Copy old FW to device. 191 self.copy_firmware() 192 193 # Flash old FW to device. 194 expect_output = 'update firmware complete' 195 output, succeed = self.flash_fw(self.old_fw_path, 196 str_compare=expect_output) 197 self.log_file_obj.write('{}Log info for writing ' 198 'old firmware{}\n'.format('-'*8, '-'*8)) 199 self.log_file_obj.write(output) 200 if not succeed: 201 raise error.TestFail('Expect \'{}\' in output, ' 202 'but didn\'t find it.'.format(expect_output)) 203 204 # No need to manually triger udev to run FW updater here. 205 # Previous FW updating process will reset SiS after it finish. 206 207 # Wait for fw updater to finish. 208 time.sleep(UPDATER_WAIT_TIME) 209 210 # Try flash the new firmware, should detect same fw version. 211 expect_output = 'The device has the same FW as system' 212 output, succeed = self.flash_fw(self.new_fw_path, 213 str_compare=expect_output) 214 self.log_file_obj.write('{}Log info for writing ' 215 'new firmware{}\n'.format('-'*8, '-'*8)) 216 self.log_file_obj.write(output) 217 if not succeed: 218 raise error.TestFail('Expect {} in output ' 219 'but didn\'t find it.'.format(expect_output)) 220 221