1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright 2018 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Liimport logging 7*9c5db199SXin Liimport os 8*9c5db199SXin Liimport time 9*9c5db199SXin Li 10*9c5db199SXin Liimport six 11*9c5db199SXin Li 12*9c5db199SXin Lifrom autotest_lib.server import test 13*9c5db199SXin Lifrom autotest_lib.server.cros import filesystem_util 14*9c5db199SXin Lifrom autotest_lib.client.common_lib import error, utils 15*9c5db199SXin Li 16*9c5db199SXin Li 17*9c5db199SXin Liclass FingerprintTest(test.test): 18*9c5db199SXin Li """Base class that sets up helpers for fingerprint tests.""" 19*9c5db199SXin Li version = 1 20*9c5db199SXin Li 21*9c5db199SXin Li # Location of firmware from the build on the DUT 22*9c5db199SXin Li _FINGERPRINT_BUILD_FW_DIR = '/opt/google/biod/fw' 23*9c5db199SXin Li 24*9c5db199SXin Li _DISABLE_FP_UPDATER_FILE = '.disable_fp_updater' 25*9c5db199SXin Li 26*9c5db199SXin Li _UPSTART_DIR = '/etc/init' 27*9c5db199SXin Li _BIOD_UPSTART_JOB_FILE = 'biod.conf' 28*9c5db199SXin Li _STATEFUL_PARTITION_DIR = '/mnt/stateful_partition' 29*9c5db199SXin Li 30*9c5db199SXin Li _GENIMAGES_SCRIPT_NAME = 'gen_test_images.sh' 31*9c5db199SXin Li _GENIMAGES_OUTPUT_DIR_NAME = 'images' 32*9c5db199SXin Li 33*9c5db199SXin Li _TEST_IMAGE_FORMAT_MAP = { 34*9c5db199SXin Li 'TEST_IMAGE_ORIGINAL': '%s.bin', 35*9c5db199SXin Li 'TEST_IMAGE_DEV': '%s.dev', 36*9c5db199SXin Li 'TEST_IMAGE_CORRUPT_FIRST_BYTE': '%s_corrupt_first_byte.bin', 37*9c5db199SXin Li 'TEST_IMAGE_CORRUPT_LAST_BYTE': '%s_corrupt_last_byte.bin', 38*9c5db199SXin Li 'TEST_IMAGE_DEV_RB_ZERO': '%s.dev.rb0', 39*9c5db199SXin Li 'TEST_IMAGE_DEV_RB_ONE': '%s.dev.rb1', 40*9c5db199SXin Li 'TEST_IMAGE_DEV_RB_NINE': '%s.dev.rb9' 41*9c5db199SXin Li } 42*9c5db199SXin Li 43*9c5db199SXin Li _ROLLBACK_ZERO_BLOCK_ID = '0' 44*9c5db199SXin Li _ROLLBACK_INITIAL_BLOCK_ID = '1' 45*9c5db199SXin Li _ROLLBACK_INITIAL_MIN_VERSION = '0' 46*9c5db199SXin Li _ROLLBACK_INITIAL_RW_VERSION = '0' 47*9c5db199SXin Li 48*9c5db199SXin Li _SERVER_GENERATED_FW_DIR_NAME = 'generated_fw' 49*9c5db199SXin Li 50*9c5db199SXin Li _DUT_TMP_PATH_BASE = '/tmp/fp_test' 51*9c5db199SXin Li 52*9c5db199SXin Li # Name of key in "futility show" output corresponds to the signing key ID 53*9c5db199SXin Li _FUTILITY_KEY_ID_KEY_NAME = 'ID' 54*9c5db199SXin Li 55*9c5db199SXin Li # Types of firmware 56*9c5db199SXin Li _FIRMWARE_TYPE_RO = 'RO' 57*9c5db199SXin Li _FIRMWARE_TYPE_RW = 'RW' 58*9c5db199SXin Li 59*9c5db199SXin Li # Types of signing keys 60*9c5db199SXin Li _KEY_TYPE_DEV = 'dev' 61*9c5db199SXin Li _KEY_TYPE_PRE_MP = 'premp' 62*9c5db199SXin Li _KEY_TYPE_MP = 'mp' 63*9c5db199SXin Li 64*9c5db199SXin Li # EC board names for FPMCUs 65*9c5db199SXin Li _FP_BOARD_NAME_BLOONCHIPPER = 'bloonchipper' 66*9c5db199SXin Li _FP_BOARD_NAME_DARTMONKEY = 'dartmonkey' 67*9c5db199SXin Li _FP_BOARD_NAME_NOCTURNE = 'nocturne_fp' 68*9c5db199SXin Li _FP_BOARD_NAME_NAMI = 'nami_fp' 69*9c5db199SXin Li 70*9c5db199SXin Li # Map from signing key ID to type of signing key 71*9c5db199SXin Li _KEY_ID_MAP_ = { 72*9c5db199SXin Li # bloonchipper 73*9c5db199SXin Li '61382804da86b4156d666cc9a976088f8b647d44': _KEY_TYPE_DEV, 74*9c5db199SXin Li '07b1af57220c196e363e68d73a5966047c77011e': _KEY_TYPE_PRE_MP, 75*9c5db199SXin Li '1c590ef36399f6a2b2ef87079c135b69ef89eb60': _KEY_TYPE_MP, 76*9c5db199SXin Li 77*9c5db199SXin Li # dartmonkey 78*9c5db199SXin Li '257a0aa3ac9e81aa4bc3aabdb6d3d079117c5799': _KEY_TYPE_MP, 79*9c5db199SXin Li 80*9c5db199SXin Li # nocturne 81*9c5db199SXin Li '8a8fc039a9463271995392f079b83ce33832d07d': _KEY_TYPE_DEV, 82*9c5db199SXin Li '6f38c866182bd9bf7a4462c06ac04fa6a0074351': _KEY_TYPE_MP, 83*9c5db199SXin Li 'f6f7d96c48bd154dbae7e3fe3a3b4c6268a10934': _KEY_TYPE_PRE_MP, 84*9c5db199SXin Li 85*9c5db199SXin Li # nami 86*9c5db199SXin Li '754aea623d69975a22998f7b97315dd53115d723': _KEY_TYPE_PRE_MP, 87*9c5db199SXin Li '35486c0090ca390408f1fbbf2a182966084fe2f8': _KEY_TYPE_MP 88*9c5db199SXin Li 89*9c5db199SXin Li } 90*9c5db199SXin Li 91*9c5db199SXin Li # RO versions that are flashed in the factory 92*9c5db199SXin Li # (for eternity for a given board) 93*9c5db199SXin Li _GOLDEN_RO_FIRMWARE_VERSION_MAP = { 94*9c5db199SXin Li _FP_BOARD_NAME_BLOONCHIPPER: { 95*9c5db199SXin Li 'hatch': 'bloonchipper_v2.0.4277-9f652bb3', 96*9c5db199SXin Li 'zork': 'bloonchipper_v2.0.5938-197506c1', 97*9c5db199SXin Li 'volteer': 'bloonchipper_v2.0.5938-197506c1', 98*9c5db199SXin Li 'brya': 'bloonchipper_v2.0.5938-197506c1', 99*9c5db199SXin Li 'guybrush': 'bloonchipper_v2.0.5938-197506c1', 100*9c5db199SXin Li }, 101*9c5db199SXin Li _FP_BOARD_NAME_DARTMONKEY: 'dartmonkey_v2.0.2887-311310808', 102*9c5db199SXin Li _FP_BOARD_NAME_NOCTURNE: 'nocturne_fp_v2.2.64-58cf5974e', 103*9c5db199SXin Li _FP_BOARD_NAME_NAMI: 'nami_fp_v2.2.144-7a08e07eb', 104*9c5db199SXin Li } 105*9c5db199SXin Li 106*9c5db199SXin Li _FIRMWARE_VERSION_SHA256SUM = 'sha256sum' 107*9c5db199SXin Li _FIRMWARE_VERSION_RO_VERSION = 'ro_version' 108*9c5db199SXin Li _FIRMWARE_VERSION_RW_VERSION = 'rw_version' 109*9c5db199SXin Li _FIRMWARE_VERSION_KEY_ID = 'key_id' 110*9c5db199SXin Li 111*9c5db199SXin Li # Map of attributes for a given board's various firmware file releases 112*9c5db199SXin Li # 113*9c5db199SXin Li # Two purposes: 114*9c5db199SXin Li # 1) Documents the exact versions and keys used for a given firmware file. 115*9c5db199SXin Li # 2) Used to verify that files that end up in the build (and therefore 116*9c5db199SXin Li # what we release) is exactly what we expect. 117*9c5db199SXin Li _FIRMWARE_VERSION_MAP = { 118*9c5db199SXin Li _FP_BOARD_NAME_BLOONCHIPPER: { 119*9c5db199SXin Li 'bloonchipper_v2.0.4277-9f652bb3-RO_v2.0.13589-727a419-RW.bin': { 120*9c5db199SXin Li _FIRMWARE_VERSION_SHA256SUM: 'b500a08d1c4f49ac1455214f1957f178288a2f4b36b40e7cd49acad1d0896ccc', 121*9c5db199SXin Li _FIRMWARE_VERSION_RO_VERSION: 'bloonchipper_v2.0.4277-9f652bb3', 122*9c5db199SXin Li _FIRMWARE_VERSION_RW_VERSION: 'bloonchipper_v2.0.13589-727a419', 123*9c5db199SXin Li _FIRMWARE_VERSION_KEY_ID: '1c590ef36399f6a2b2ef87079c135b69ef89eb60', 124*9c5db199SXin Li }, 125*9c5db199SXin Li 'bloonchipper_v2.0.5938-197506c1-RO_v2.0.13589-727a419-RW.bin': { 126*9c5db199SXin Li _FIRMWARE_VERSION_SHA256SUM: 'dfa1a9e409893441c990edde86dbe6d0e301c03b7a9e604ec6af5fc1691ef1be', 127*9c5db199SXin Li _FIRMWARE_VERSION_RO_VERSION: 'bloonchipper_v2.0.5938-197506c1', 128*9c5db199SXin Li _FIRMWARE_VERSION_RW_VERSION: 'bloonchipper_v2.0.13589-727a419', 129*9c5db199SXin Li _FIRMWARE_VERSION_KEY_ID: '1c590ef36399f6a2b2ef87079c135b69ef89eb60', 130*9c5db199SXin Li }, 131*9c5db199SXin Li }, 132*9c5db199SXin Li _FP_BOARD_NAME_NOCTURNE: { 133*9c5db199SXin Li 'nocturne_fp_v2.2.64-58cf5974e-RO_v2.0.13584-6fcfe697-RW.bin': { 134*9c5db199SXin Li _FIRMWARE_VERSION_SHA256SUM: '8ebc978bf18fc1629a8ab9b33ac91817d850ce5ca9c55dc69c99b0acfb540948', 135*9c5db199SXin Li _FIRMWARE_VERSION_RO_VERSION: 'nocturne_fp_v2.2.64-58cf5974e', 136*9c5db199SXin Li _FIRMWARE_VERSION_RW_VERSION: 'nocturne_fp_v2.0.13584-6fcfe697', 137*9c5db199SXin Li _FIRMWARE_VERSION_KEY_ID: '6f38c866182bd9bf7a4462c06ac04fa6a0074351', 138*9c5db199SXin Li }, 139*9c5db199SXin Li }, 140*9c5db199SXin Li _FP_BOARD_NAME_NAMI: { 141*9c5db199SXin Li 'nami_fp_v2.2.144-7a08e07eb-RO_v2.0.13584-6fcfe69780-RW.bin': { 142*9c5db199SXin Li _FIRMWARE_VERSION_SHA256SUM: 'e198db08020ac71a11a53d641d6ada750061fb3f3faa2728aab7835266ed9e7b', 143*9c5db199SXin Li _FIRMWARE_VERSION_RO_VERSION: 'nami_fp_v2.2.144-7a08e07eb', 144*9c5db199SXin Li _FIRMWARE_VERSION_RW_VERSION: 'nami_fp_v2.0.13584-6fcfe69780', 145*9c5db199SXin Li _FIRMWARE_VERSION_KEY_ID: '35486c0090ca390408f1fbbf2a182966084fe2f8', 146*9c5db199SXin Li }, 147*9c5db199SXin Li }, 148*9c5db199SXin Li _FP_BOARD_NAME_DARTMONKEY: { 149*9c5db199SXin Li 'dartmonkey_v2.0.2887-311310808-RO_v2.0.13584-6fcfe6978-RW.bin': { 150*9c5db199SXin Li _FIRMWARE_VERSION_SHA256SUM: '8fa168c19d886b5fe8e852bba7d3b04cd0cd2344d377d9b3d278a45d76b206a1', 151*9c5db199SXin Li _FIRMWARE_VERSION_RO_VERSION: 'dartmonkey_v2.0.2887-311310808', 152*9c5db199SXin Li _FIRMWARE_VERSION_RW_VERSION: 'dartmonkey_v2.0.13584-6fcfe6978', 153*9c5db199SXin Li _FIRMWARE_VERSION_KEY_ID: '257a0aa3ac9e81aa4bc3aabdb6d3d079117c5799', 154*9c5db199SXin Li } 155*9c5db199SXin Li } 156*9c5db199SXin Li } 157*9c5db199SXin Li 158*9c5db199SXin Li _BIOD_UPSTART_JOB_NAME = 'biod' 159*9c5db199SXin Li _POWERD_UPSTART_JOB_NAME = 'powerd' 160*9c5db199SXin Li # TODO(crbug.com/925545) 161*9c5db199SXin Li _TIMBERSLIDE_UPSTART_JOB_NAME = \ 162*9c5db199SXin Li 'timberslide LOG_PATH=/sys/kernel/debug/cros_fp/console_log' 163*9c5db199SXin Li 164*9c5db199SXin Li _INIT_ENTROPY_CMD = 'bio_wash --factory_init' 165*9c5db199SXin Li 166*9c5db199SXin Li _CROS_FP_ARG = '--name=cros_fp' 167*9c5db199SXin Li _CROS_CONFIG_FINGERPRINT_PATH = '/fingerprint' 168*9c5db199SXin Li _ECTOOL_RO_VERSION = 'RO version' 169*9c5db199SXin Li _ECTOOL_RW_VERSION = 'RW version' 170*9c5db199SXin Li _ECTOOL_FIRMWARE_COPY = 'Firmware copy' 171*9c5db199SXin Li _ECTOOL_ROLLBACK_BLOCK_ID = 'Rollback block id' 172*9c5db199SXin Li _ECTOOL_ROLLBACK_MIN_VERSION = 'Rollback min version' 173*9c5db199SXin Li _ECTOOL_ROLLBACK_RW_VERSION = 'RW rollback version' 174*9c5db199SXin Li 175*9c5db199SXin Li @staticmethod 176*9c5db199SXin Li def _parse_colon_delimited_output(ectool_output): 177*9c5db199SXin Li """ 178*9c5db199SXin Li Converts ectool's (or any other tool with similar output) colon 179*9c5db199SXin Li delimited output into python dict. Ignores any lines that do not have 180*9c5db199SXin Li colons. 181*9c5db199SXin Li 182*9c5db199SXin Li Example: 183*9c5db199SXin Li RO version: nocturne_fp_v2.2.64-58cf5974e 184*9c5db199SXin Li RW version: nocturne_fp_v2.2.110-b936c0a3c 185*9c5db199SXin Li 186*9c5db199SXin Li becomes: 187*9c5db199SXin Li { 188*9c5db199SXin Li 'RO version': 'nocturne_fp_v2.2.64-58cf5974e', 189*9c5db199SXin Li 'RW version': 'nocturne_fp_v2.2.110-b936c0a3c' 190*9c5db199SXin Li } 191*9c5db199SXin Li """ 192*9c5db199SXin Li ret = {} 193*9c5db199SXin Li try: 194*9c5db199SXin Li for line in ectool_output.strip().split('\n'): 195*9c5db199SXin Li splits = line.split(':', 1) 196*9c5db199SXin Li if len(splits) != 2: 197*9c5db199SXin Li continue 198*9c5db199SXin Li key = splits[0].strip() 199*9c5db199SXin Li val = splits[1].strip() 200*9c5db199SXin Li ret[key] = val 201*9c5db199SXin Li except: 202*9c5db199SXin Li raise error.TestFail('Unable to parse ectool output: %s' 203*9c5db199SXin Li % ectool_output) 204*9c5db199SXin Li return ret 205*9c5db199SXin Li 206*9c5db199SXin Li def initialize(self, host): 207*9c5db199SXin Li """Perform minimal initialization, to avoid AttributeError in cleanup""" 208*9c5db199SXin Li self.host = host 209*9c5db199SXin Li self.servo = host.servo 210*9c5db199SXin Li 211*9c5db199SXin Li self._validate_compatible_servo_version() 212*9c5db199SXin Li 213*9c5db199SXin Li self.servo.initialize_dut() 214*9c5db199SXin Li 215*9c5db199SXin Li self.fp_board = self.get_fp_board() 216*9c5db199SXin Li self._build_fw_file = self.get_build_fw_file() 217*9c5db199SXin Li 218*9c5db199SXin Li def setup_test(self, test_dir, use_dev_signed_fw=False, 219*9c5db199SXin Li enable_hardware_write_protect=True, 220*9c5db199SXin Li enable_software_write_protect=True, 221*9c5db199SXin Li force_firmware_flashing=False, init_entropy=True): 222*9c5db199SXin Li """Perform more complete initialization, including copying test files""" 223*9c5db199SXin Li logging.info('HW write protect enabled: %s', 224*9c5db199SXin Li self.is_hardware_write_protect_enabled()) 225*9c5db199SXin Li 226*9c5db199SXin Li # TODO(crbug.com/925545): stop timberslide so /var/log/cros_fp.log 227*9c5db199SXin Li # continues to update after flashing. 228*9c5db199SXin Li self._timberslide_running = self.host.upstart_status( 229*9c5db199SXin Li self._TIMBERSLIDE_UPSTART_JOB_NAME) 230*9c5db199SXin Li if self._timberslide_running: 231*9c5db199SXin Li logging.info('Stopping %s', self._TIMBERSLIDE_UPSTART_JOB_NAME) 232*9c5db199SXin Li self.host.upstart_stop(self._TIMBERSLIDE_UPSTART_JOB_NAME) 233*9c5db199SXin Li 234*9c5db199SXin Li self._biod_running = self.host.upstart_status( 235*9c5db199SXin Li self._BIOD_UPSTART_JOB_NAME) 236*9c5db199SXin Li if self._biod_running: 237*9c5db199SXin Li logging.info('Stopping %s', self._BIOD_UPSTART_JOB_NAME) 238*9c5db199SXin Li self.host.upstart_stop(self._BIOD_UPSTART_JOB_NAME) 239*9c5db199SXin Li 240*9c5db199SXin Li # TODO(b/183123775): Remove when bug is fixed. 241*9c5db199SXin Li # Disabling powerd to prevent the display from turning off, which kills 242*9c5db199SXin Li # USB on some platforms. 243*9c5db199SXin Li self._powerd_running = self.host.upstart_status( 244*9c5db199SXin Li self._POWERD_UPSTART_JOB_NAME) 245*9c5db199SXin Li if self._powerd_running: 246*9c5db199SXin Li logging.info('Stopping %s', self._POWERD_UPSTART_JOB_NAME) 247*9c5db199SXin Li self.host.upstart_stop(self._POWERD_UPSTART_JOB_NAME) 248*9c5db199SXin Li 249*9c5db199SXin Li # On some platforms an AP reboot is needed after flashing firmware to 250*9c5db199SXin Li # rebind the driver. 251*9c5db199SXin Li self._dut_needs_reboot = self.is_uart_device() 252*9c5db199SXin Li 253*9c5db199SXin Li if filesystem_util.is_rootfs_writable(self.host): 254*9c5db199SXin Li if self._dut_needs_reboot: 255*9c5db199SXin Li logging.warning('rootfs is writable') 256*9c5db199SXin Li else: 257*9c5db199SXin Li raise error.TestFail('rootfs is writable') 258*9c5db199SXin Li 259*9c5db199SXin Li if not self.biod_upstart_job_enabled(): 260*9c5db199SXin Li raise error.TestFail( 261*9c5db199SXin Li 'Biod upstart job is disabled at the beginning of test') 262*9c5db199SXin Li if not self.fp_updater_is_enabled(): 263*9c5db199SXin Li raise error.TestFail( 264*9c5db199SXin Li 'Fingerprint firmware updater is disabled at the beginning of test' 265*9c5db199SXin Li ) 266*9c5db199SXin Li 267*9c5db199SXin Li # Disable biod and updater so that they won't interfere after reboot. 268*9c5db199SXin Li if self._dut_needs_reboot: 269*9c5db199SXin Li self.disable_biod_upstart_job() 270*9c5db199SXin Li self.disable_fp_updater() 271*9c5db199SXin Li 272*9c5db199SXin Li # create tmp working directory on device (automatically cleaned up) 273*9c5db199SXin Li self._dut_working_dir = self.host.get_tmp_dir( 274*9c5db199SXin Li parent=self._DUT_TMP_PATH_BASE) 275*9c5db199SXin Li logging.info('Created dut_working_dir: %s', self._dut_working_dir) 276*9c5db199SXin Li self.copy_files_to_dut(test_dir, self._dut_working_dir) 277*9c5db199SXin Li 278*9c5db199SXin Li self.validate_build_fw_file() 279*9c5db199SXin Li 280*9c5db199SXin Li gen_script = os.path.abspath(os.path.join(self.autodir, 281*9c5db199SXin Li 'server', 'cros', 'faft', 282*9c5db199SXin Li self._GENIMAGES_SCRIPT_NAME)) 283*9c5db199SXin Li self._dut_firmware_test_images_dir = \ 284*9c5db199SXin Li self._generate_test_firmware_images(gen_script, 285*9c5db199SXin Li self._build_fw_file, 286*9c5db199SXin Li self._dut_working_dir) 287*9c5db199SXin Li logging.info('dut_firmware_test_images_dir: %s', 288*9c5db199SXin Li self._dut_firmware_test_images_dir) 289*9c5db199SXin Li 290*9c5db199SXin Li self._initialize_test_firmware_image_attrs( 291*9c5db199SXin Li self._dut_firmware_test_images_dir) 292*9c5db199SXin Li 293*9c5db199SXin Li self._initialize_running_fw_version(use_dev_signed_fw, 294*9c5db199SXin Li force_firmware_flashing) 295*9c5db199SXin Li 296*9c5db199SXin Li if init_entropy: 297*9c5db199SXin Li self._initialize_fw_entropy() 298*9c5db199SXin Li 299*9c5db199SXin Li self._initialize_hw_and_sw_write_protect(enable_hardware_write_protect, 300*9c5db199SXin Li enable_software_write_protect) 301*9c5db199SXin Li 302*9c5db199SXin Li def cleanup(self): 303*9c5db199SXin Li """Restores original state.""" 304*9c5db199SXin Li if hasattr(self, '_need_fw_restore') and self._need_fw_restore: 305*9c5db199SXin Li # Once the tests complete we need to make sure we're running the 306*9c5db199SXin Li # original firmware (not dev version) and potentially reset rollback. 307*9c5db199SXin Li self._initialize_running_fw_version(use_dev_signed_fw=False, 308*9c5db199SXin Li force_firmware_flashing=False) 309*9c5db199SXin Li self._initialize_fw_entropy() 310*9c5db199SXin Li 311*9c5db199SXin Li # Re-enable biod and updater after flashing and initializing entropy so 312*9c5db199SXin Li # that they don't interfere if there was a reboot. 313*9c5db199SXin Li if hasattr(self, '_dut_needs_reboot') and self._dut_needs_reboot: 314*9c5db199SXin Li if not self.biod_upstart_job_enabled(): 315*9c5db199SXin Li self.enable_biod_upstart_job() 316*9c5db199SXin Li if not self.fp_updater_is_enabled(): 317*9c5db199SXin Li self.enable_fp_updater() 318*9c5db199SXin Li self._initialize_hw_and_sw_write_protect( 319*9c5db199SXin Li enable_hardware_write_protect=True, 320*9c5db199SXin Li enable_software_write_protect=True) 321*9c5db199SXin Li # TODO(b/183123775) 322*9c5db199SXin Li if hasattr(self, '_powerd_running') and self._powerd_running: 323*9c5db199SXin Li logging.info('Restarting powerd') 324*9c5db199SXin Li self.host.upstart_restart(self._POWERD_UPSTART_JOB_NAME) 325*9c5db199SXin Li if hasattr(self, '_biod_running') and self._biod_running: 326*9c5db199SXin Li logging.info('Restarting biod') 327*9c5db199SXin Li self.host.upstart_restart(self._BIOD_UPSTART_JOB_NAME) 328*9c5db199SXin Li # TODO(crbug.com/925545) 329*9c5db199SXin Li if hasattr(self, '_timberslide_running') and self._timberslide_running: 330*9c5db199SXin Li logging.info('Restarting timberslide') 331*9c5db199SXin Li self.host.upstart_restart(self._TIMBERSLIDE_UPSTART_JOB_NAME) 332*9c5db199SXin Li 333*9c5db199SXin Li super(FingerprintTest, self).cleanup() 334*9c5db199SXin Li 335*9c5db199SXin Li def after_run_once(self): 336*9c5db199SXin Li """Logs which iteration just ran.""" 337*9c5db199SXin Li logging.info('successfully ran iteration %d', self.iteration) 338*9c5db199SXin Li 339*9c5db199SXin Li def _validate_compatible_servo_version(self): 340*9c5db199SXin Li """Asserts if a compatible servo version is not attached.""" 341*9c5db199SXin Li servo_version = self.servo.get_servo_version() 342*9c5db199SXin Li logging.info('servo version: %s', servo_version) 343*9c5db199SXin Li 344*9c5db199SXin Li def _generate_test_firmware_images(self, gen_script, build_fw_file, 345*9c5db199SXin Li dut_working_dir): 346*9c5db199SXin Li """ 347*9c5db199SXin Li Copies the fingerprint firmware from the DUT to the server running 348*9c5db199SXin Li the tests, which runs a script to generate various test versions of 349*9c5db199SXin Li the firmware. 350*9c5db199SXin Li 351*9c5db199SXin Li @return full path to location of test images on DUT 352*9c5db199SXin Li """ 353*9c5db199SXin Li # create subdirectory under existing tmp dir 354*9c5db199SXin Li server_tmp_dir = os.path.join(self.tmpdir, 355*9c5db199SXin Li self._SERVER_GENERATED_FW_DIR_NAME) 356*9c5db199SXin Li os.mkdir(server_tmp_dir) 357*9c5db199SXin Li logging.info('server_tmp_dir: %s', server_tmp_dir) 358*9c5db199SXin Li 359*9c5db199SXin Li # Copy firmware from device to server 360*9c5db199SXin Li self.get_files_from_dut(build_fw_file, server_tmp_dir) 361*9c5db199SXin Li 362*9c5db199SXin Li # Run the test image generation script on server 363*9c5db199SXin Li pushd = os.getcwd() 364*9c5db199SXin Li os.chdir(server_tmp_dir) 365*9c5db199SXin Li cmd = ' '.join([gen_script, 366*9c5db199SXin Li self.get_fp_board(), 367*9c5db199SXin Li os.path.basename(build_fw_file)]) 368*9c5db199SXin Li result = self.run_server_cmd(cmd) 369*9c5db199SXin Li if result.exit_status != 0: 370*9c5db199SXin Li raise error.TestFail('Failed to run test image generation script') 371*9c5db199SXin Li 372*9c5db199SXin Li os.chdir(pushd) 373*9c5db199SXin Li 374*9c5db199SXin Li # Copy resulting files to DUT tmp dir 375*9c5db199SXin Li server_generated_images_dir = \ 376*9c5db199SXin Li os.path.join(server_tmp_dir, self._GENIMAGES_OUTPUT_DIR_NAME) 377*9c5db199SXin Li self.copy_files_to_dut(server_generated_images_dir, dut_working_dir) 378*9c5db199SXin Li 379*9c5db199SXin Li return os.path.join(dut_working_dir, self._GENIMAGES_OUTPUT_DIR_NAME) 380*9c5db199SXin Li 381*9c5db199SXin Li def _initialize_test_firmware_image_attrs(self, dut_fw_test_images_dir): 382*9c5db199SXin Li """Sets attributes with full path to test images on DUT. 383*9c5db199SXin Li 384*9c5db199SXin Li Example: self.TEST_IMAGE_DEV = /some/path/images/nocturne_fp.dev 385*9c5db199SXin Li """ 386*9c5db199SXin Li for key, val in six.iteritems(self._TEST_IMAGE_FORMAT_MAP): 387*9c5db199SXin Li full_path = os.path.join(dut_fw_test_images_dir, 388*9c5db199SXin Li val % self.get_fp_board()) 389*9c5db199SXin Li setattr(self, key, full_path) 390*9c5db199SXin Li 391*9c5db199SXin Li def _initialize_running_fw_version(self, use_dev_signed_fw, 392*9c5db199SXin Li force_firmware_flashing): 393*9c5db199SXin Li """ 394*9c5db199SXin Li Ensures that the running firmware version matches build version 395*9c5db199SXin Li and factory rollback settings; flashes to correct version if either 396*9c5db199SXin Li fails to match is requested to force flashing. 397*9c5db199SXin Li 398*9c5db199SXin Li RO firmware: original version released at factory 399*9c5db199SXin Li RW firmware: firmware from current build 400*9c5db199SXin Li """ 401*9c5db199SXin Li build_rw_firmware_version = \ 402*9c5db199SXin Li self.get_build_rw_firmware_version(use_dev_signed_fw) 403*9c5db199SXin Li golden_ro_firmware_version = \ 404*9c5db199SXin Li self.get_golden_ro_firmware_version(use_dev_signed_fw) 405*9c5db199SXin Li logging.info('Build RW firmware version: %s', build_rw_firmware_version) 406*9c5db199SXin Li logging.info('Golden RO firmware version: %s', 407*9c5db199SXin Li golden_ro_firmware_version) 408*9c5db199SXin Li 409*9c5db199SXin Li running_rw_firmware = self.ensure_running_rw_firmware() 410*9c5db199SXin Li 411*9c5db199SXin Li fw_versions_match = self.running_fw_version_matches_given_version( 412*9c5db199SXin Li build_rw_firmware_version, golden_ro_firmware_version) 413*9c5db199SXin Li 414*9c5db199SXin Li if not running_rw_firmware or not fw_versions_match \ 415*9c5db199SXin Li or not self.is_rollback_set_to_initial_val() \ 416*9c5db199SXin Li or force_firmware_flashing: 417*9c5db199SXin Li fw_file = self._build_fw_file 418*9c5db199SXin Li if use_dev_signed_fw: 419*9c5db199SXin Li fw_file = self.TEST_IMAGE_DEV 420*9c5db199SXin Li self.flash_rw_ro_firmware(fw_file) 421*9c5db199SXin Li if not self.running_fw_version_matches_given_version( 422*9c5db199SXin Li build_rw_firmware_version, golden_ro_firmware_version): 423*9c5db199SXin Li raise error.TestFail( 424*9c5db199SXin Li 'Running firmware version does not match expected version') 425*9c5db199SXin Li 426*9c5db199SXin Li def _initialize_fw_entropy(self): 427*9c5db199SXin Li """Sets the entropy (key) in FPMCU flash (if not set).""" 428*9c5db199SXin Li result = self.run_cmd(self._INIT_ENTROPY_CMD) 429*9c5db199SXin Li if result.exit_status != 0: 430*9c5db199SXin Li raise error.TestFail('Unable to initialize entropy') 431*9c5db199SXin Li 432*9c5db199SXin Li def _initialize_hw_and_sw_write_protect(self, enable_hardware_write_protect, 433*9c5db199SXin Li enable_software_write_protect): 434*9c5db199SXin Li """Enables/disables hardware/software write protect.""" 435*9c5db199SXin Li # sw: 0, hw: 0 => initial_hw(0) -> sw(0) -> hw(0) 436*9c5db199SXin Li # sw: 0, hw: 1 => initial_hw(0) -> sw(0) -> hw(1) 437*9c5db199SXin Li # sw: 1, hw: 0 => initial_hw(1) -> sw(1) -> hw(0) 438*9c5db199SXin Li # sw: 1, hw: 1 => initial_hw(1) -> sw(1) -> hw(1) 439*9c5db199SXin Li hardware_write_protect_initial_enabled = True 440*9c5db199SXin Li if not enable_software_write_protect: 441*9c5db199SXin Li hardware_write_protect_initial_enabled = False 442*9c5db199SXin Li 443*9c5db199SXin Li self.set_hardware_write_protect(hardware_write_protect_initial_enabled) 444*9c5db199SXin Li 445*9c5db199SXin Li self.set_software_write_protect(enable_software_write_protect) 446*9c5db199SXin Li self.set_hardware_write_protect(enable_hardware_write_protect) 447*9c5db199SXin Li 448*9c5db199SXin Li def get_fp_board(self): 449*9c5db199SXin Li """Returns name of fingerprint EC. 450*9c5db199SXin Li 451*9c5db199SXin Li nocturne and nami are special cases and have "_fp" appended. Newer 452*9c5db199SXin Li FPMCUs have unique names. 453*9c5db199SXin Li See go/cros-fingerprint-firmware-branching-and-signing. 454*9c5db199SXin Li """ 455*9c5db199SXin Li # Use cros_config to get fingerprint board. 456*9c5db199SXin Li # Due to b/160271883, we will try running the cmd via cat instead. 457*9c5db199SXin Li result = self._run_cros_config_cmd_cat('fingerprint/board') 458*9c5db199SXin Li if result.exit_status != 0: 459*9c5db199SXin Li raise error.TestFail( 460*9c5db199SXin Li 'Unable to get fingerprint board with cros_config') 461*9c5db199SXin Li return result.stdout.rstrip() 462*9c5db199SXin Li 463*9c5db199SXin Li def is_uart_device(self) -> bool: 464*9c5db199SXin Li """Returns True if the boards transpot device is UART""" 465*9c5db199SXin Li uart_devices = ['zork', 'guybrush'] 466*9c5db199SXin Li return self.get_host_board() in uart_devices 467*9c5db199SXin Li 468*9c5db199SXin Li def get_host_board(self): 469*9c5db199SXin Li """Returns name of the host board.""" 470*9c5db199SXin Li return self.host.get_board().split(':')[-1] 471*9c5db199SXin Li 472*9c5db199SXin Li def get_build_fw_file(self): 473*9c5db199SXin Li """Returns full path to build FW file on DUT.""" 474*9c5db199SXin Li ls_cmd = 'ls %s/%s*.bin' % ( 475*9c5db199SXin Li self._FINGERPRINT_BUILD_FW_DIR, self.fp_board) 476*9c5db199SXin Li result = self.run_cmd(ls_cmd) 477*9c5db199SXin Li if result.exit_status != 0: 478*9c5db199SXin Li raise error.TestFail( 479*9c5db199SXin Li 'Unable to find firmware file on device:' 480*9c5db199SXin Li ' command failed (rc=%s): %s' 481*9c5db199SXin Li % (result.exit_status, result.stderr.strip() or ls_cmd)) 482*9c5db199SXin Li ret = result.stdout.rstrip() 483*9c5db199SXin Li logging.info('Build firmware file: %s', ret) 484*9c5db199SXin Li return ret 485*9c5db199SXin Li 486*9c5db199SXin Li def check_equal(self, a, b): 487*9c5db199SXin Li """Raises exception if "a" does not equal "b".""" 488*9c5db199SXin Li if a != b: 489*9c5db199SXin Li raise error.TestFail('"%s" does not match expected "%s" for board ' 490*9c5db199SXin Li '%s' % (a, b, self.get_fp_board())) 491*9c5db199SXin Li 492*9c5db199SXin Li def validate_build_fw_file(self, 493*9c5db199SXin Li allowed_types=(_KEY_TYPE_PRE_MP, _KEY_TYPE_MP)): 494*9c5db199SXin Li """ 495*9c5db199SXin Li Checks that all attributes in the given firmware file match their 496*9c5db199SXin Li expected values. 497*9c5db199SXin Li 498*9c5db199SXin Li @param allowed_types: If key type is something else, raise TestFail. 499*9c5db199SXin Li Default: pre-MP or MP. 500*9c5db199SXin Li @type allowed_types: tuple | list 501*9c5db199SXin Li """ 502*9c5db199SXin Li build_fw_file = self._build_fw_file 503*9c5db199SXin Li # check hash 504*9c5db199SXin Li actual_hash = self._calculate_sha256sum(build_fw_file) 505*9c5db199SXin Li expected_hash = self._get_expected_firmware_hash(build_fw_file) 506*9c5db199SXin Li self.check_equal(actual_hash, expected_hash) 507*9c5db199SXin Li 508*9c5db199SXin Li # check signing key_id 509*9c5db199SXin Li actual_key_id = self._read_firmware_key_id(build_fw_file) 510*9c5db199SXin Li expected_key_id = self._get_expected_firmware_key_id(build_fw_file) 511*9c5db199SXin Li self.check_equal(actual_key_id, expected_key_id) 512*9c5db199SXin Li 513*9c5db199SXin Li # check that the signing key for firmware in the build 514*9c5db199SXin Li # is "pre mass production" (pre-mp) or "mass production" (MP) 515*9c5db199SXin Li key_type = self._get_key_type(actual_key_id) 516*9c5db199SXin Li if key_type not in allowed_types: 517*9c5db199SXin Li raise error.TestFail( 518*9c5db199SXin Li 'Firmware key type must be %s for board %s; got %s (%s)' % 519*9c5db199SXin Li (' or '.join(allowed_types), self.fp_board, key_type, 520*9c5db199SXin Li actual_key_id)) 521*9c5db199SXin Li 522*9c5db199SXin Li # check ro_version 523*9c5db199SXin Li actual_ro_version = self._read_firmware_ro_version(build_fw_file) 524*9c5db199SXin Li expected_ro_version = \ 525*9c5db199SXin Li self._get_expected_firmware_ro_version(build_fw_file) 526*9c5db199SXin Li self.check_equal(actual_ro_version, expected_ro_version) 527*9c5db199SXin Li 528*9c5db199SXin Li # check rw_version 529*9c5db199SXin Li actual_rw_version = self._read_firmware_rw_version(build_fw_file) 530*9c5db199SXin Li expected_rw_version = \ 531*9c5db199SXin Li self._get_expected_firmware_rw_version(build_fw_file) 532*9c5db199SXin Li self.check_equal(actual_rw_version, expected_rw_version) 533*9c5db199SXin Li 534*9c5db199SXin Li logging.info("Validated build firmware metadata.") 535*9c5db199SXin Li 536*9c5db199SXin Li def _get_key_type(self, key_id): 537*9c5db199SXin Li """Returns the key "type" for a given "key id".""" 538*9c5db199SXin Li key_type = self._KEY_ID_MAP_.get(key_id) 539*9c5db199SXin Li if key_type is None: 540*9c5db199SXin Li raise error.TestFail('Unable to get key type for key id: %s' 541*9c5db199SXin Li % key_id) 542*9c5db199SXin Li return key_type 543*9c5db199SXin Li 544*9c5db199SXin Li def _get_expected_firmware_info(self, build_fw_file, info_type): 545*9c5db199SXin Li """ 546*9c5db199SXin Li Returns expected firmware info for a given firmware file name. 547*9c5db199SXin Li """ 548*9c5db199SXin Li build_fw_file_name = os.path.basename(build_fw_file) 549*9c5db199SXin Li 550*9c5db199SXin Li board = self.get_fp_board() 551*9c5db199SXin Li board_expected_fw_info = self._FIRMWARE_VERSION_MAP.get(board) 552*9c5db199SXin Li if board_expected_fw_info is None: 553*9c5db199SXin Li raise error.TestFail('Unable to get firmware info for board: %s' 554*9c5db199SXin Li % board) 555*9c5db199SXin Li 556*9c5db199SXin Li expected_fw_info = board_expected_fw_info.get(build_fw_file_name) 557*9c5db199SXin Li if expected_fw_info is None: 558*9c5db199SXin Li raise error.TestFail('Unable to get firmware info for file: %s' 559*9c5db199SXin Li % build_fw_file_name) 560*9c5db199SXin Li 561*9c5db199SXin Li ret = expected_fw_info.get(info_type) 562*9c5db199SXin Li if ret is None: 563*9c5db199SXin Li raise error.TestFail('Unable to get firmware info type: %s' 564*9c5db199SXin Li % info_type) 565*9c5db199SXin Li 566*9c5db199SXin Li return ret 567*9c5db199SXin Li 568*9c5db199SXin Li def _get_expected_firmware_hash(self, build_fw_file): 569*9c5db199SXin Li """Returns expected hash of firmware file.""" 570*9c5db199SXin Li return self._get_expected_firmware_info( 571*9c5db199SXin Li build_fw_file, self._FIRMWARE_VERSION_SHA256SUM) 572*9c5db199SXin Li 573*9c5db199SXin Li def _get_expected_firmware_key_id(self, build_fw_file): 574*9c5db199SXin Li """Returns expected "key id" for firmware file.""" 575*9c5db199SXin Li return self._get_expected_firmware_info( 576*9c5db199SXin Li build_fw_file, self._FIRMWARE_VERSION_KEY_ID) 577*9c5db199SXin Li 578*9c5db199SXin Li def _get_expected_firmware_ro_version(self, build_fw_file): 579*9c5db199SXin Li """Returns expected RO version for firmware file.""" 580*9c5db199SXin Li return self._get_expected_firmware_info( 581*9c5db199SXin Li build_fw_file, self._FIRMWARE_VERSION_RO_VERSION) 582*9c5db199SXin Li 583*9c5db199SXin Li def _get_expected_firmware_rw_version(self, build_fw_file): 584*9c5db199SXin Li """Returns expected RW version for firmware file.""" 585*9c5db199SXin Li return self._get_expected_firmware_info( 586*9c5db199SXin Li build_fw_file, self._FIRMWARE_VERSION_RW_VERSION) 587*9c5db199SXin Li 588*9c5db199SXin Li def _read_firmware_key_id(self, file_name): 589*9c5db199SXin Li """Returns "key id" as read from the given file.""" 590*9c5db199SXin Li result = self._run_futility_show_cmd(file_name) 591*9c5db199SXin Li parsed = self._parse_colon_delimited_output(result) 592*9c5db199SXin Li key_id = parsed.get(self._FUTILITY_KEY_ID_KEY_NAME) 593*9c5db199SXin Li if key_id is None: 594*9c5db199SXin Li raise error.TestFail('Failed to get key ID for file: %s' 595*9c5db199SXin Li % file_name) 596*9c5db199SXin Li return key_id 597*9c5db199SXin Li 598*9c5db199SXin Li def _read_firmware_ro_version(self, file_name): 599*9c5db199SXin Li """Returns RO firmware version as read from the given file.""" 600*9c5db199SXin Li return self._run_dump_fmap_cmd(file_name, 'RO_FRID') 601*9c5db199SXin Li 602*9c5db199SXin Li def _read_firmware_rw_version(self, file_name): 603*9c5db199SXin Li """Returns RW firmware version as read from the given file.""" 604*9c5db199SXin Li return self._run_dump_fmap_cmd(file_name, 'RW_FWID') 605*9c5db199SXin Li 606*9c5db199SXin Li def _calculate_sha256sum(self, file_name): 607*9c5db199SXin Li """Returns SHA256 hash of the given file contents.""" 608*9c5db199SXin Li result = self._run_sha256sum_cmd(file_name) 609*9c5db199SXin Li return result.stdout.split()[0] 610*9c5db199SXin Li 611*9c5db199SXin Li def _get_running_firmware_info(self, key): 612*9c5db199SXin Li """ 613*9c5db199SXin Li Returns requested firmware info (RW version, RO version, or firmware 614*9c5db199SXin Li type). 615*9c5db199SXin Li """ 616*9c5db199SXin Li result = self._run_ectool_cmd('version') 617*9c5db199SXin Li parsed = self._parse_colon_delimited_output(result.stdout) 618*9c5db199SXin Li if result.exit_status != 0: 619*9c5db199SXin Li raise error.TestFail('Failed to get running firmware info') 620*9c5db199SXin Li info = parsed.get(key) 621*9c5db199SXin Li if info is None: 622*9c5db199SXin Li raise error.TestFail( 623*9c5db199SXin Li 'Failed to get running firmware info: %s' % key) 624*9c5db199SXin Li return info 625*9c5db199SXin Li 626*9c5db199SXin Li def get_running_rw_firmware_version(self): 627*9c5db199SXin Li """Returns running RW firmware version.""" 628*9c5db199SXin Li return self._get_running_firmware_info(self._ECTOOL_RW_VERSION) 629*9c5db199SXin Li 630*9c5db199SXin Li def get_running_ro_firmware_version(self): 631*9c5db199SXin Li """Returns running RO firmware version.""" 632*9c5db199SXin Li return self._get_running_firmware_info(self._ECTOOL_RO_VERSION) 633*9c5db199SXin Li 634*9c5db199SXin Li def get_running_firmware_type(self): 635*9c5db199SXin Li """Returns type of firmware we are running (RW or RO).""" 636*9c5db199SXin Li return self._get_running_firmware_info(self._ECTOOL_FIRMWARE_COPY) 637*9c5db199SXin Li 638*9c5db199SXin Li def _get_rollback_info(self, info_type): 639*9c5db199SXin Li """Returns requested type of rollback info.""" 640*9c5db199SXin Li result = self._run_ectool_cmd('rollbackinfo') 641*9c5db199SXin Li parsed = self._parse_colon_delimited_output(result.stdout) 642*9c5db199SXin Li if result.exit_status != 0: 643*9c5db199SXin Li raise error.TestFail('Failed to get rollback info') 644*9c5db199SXin Li info = parsed.get(info_type) 645*9c5db199SXin Li if info is None: 646*9c5db199SXin Li raise error.TestFail('Failed to get rollback info: %s' % info_type) 647*9c5db199SXin Li return info 648*9c5db199SXin Li 649*9c5db199SXin Li def get_rollback_id(self): 650*9c5db199SXin Li """Returns rollback ID.""" 651*9c5db199SXin Li return self._get_rollback_info(self._ECTOOL_ROLLBACK_BLOCK_ID) 652*9c5db199SXin Li 653*9c5db199SXin Li def get_rollback_min_version(self): 654*9c5db199SXin Li """Returns rollback min version.""" 655*9c5db199SXin Li return self._get_rollback_info(self._ECTOOL_ROLLBACK_MIN_VERSION) 656*9c5db199SXin Li 657*9c5db199SXin Li def get_rollback_rw_version(self): 658*9c5db199SXin Li """Returns RW rollback version.""" 659*9c5db199SXin Li return self._get_rollback_info(self._ECTOOL_ROLLBACK_RW_VERSION) 660*9c5db199SXin Li 661*9c5db199SXin Li def _construct_dev_version(self, orig_version): 662*9c5db199SXin Li """ 663*9c5db199SXin Li Given a "regular" version string from a signed build, returns the 664*9c5db199SXin Li special "dev" version that we use when creating the test images. 665*9c5db199SXin Li """ 666*9c5db199SXin Li fw_version = orig_version 667*9c5db199SXin Li if len(fw_version) + len('.dev') > 31: 668*9c5db199SXin Li fw_version = fw_version[:27] 669*9c5db199SXin Li fw_version = fw_version + '.dev' 670*9c5db199SXin Li return fw_version 671*9c5db199SXin Li 672*9c5db199SXin Li def get_golden_ro_firmware_version(self, use_dev_signed_fw): 673*9c5db199SXin Li """Returns RO firmware version used in factory.""" 674*9c5db199SXin Li board = self.get_fp_board() 675*9c5db199SXin Li golden_version = self._GOLDEN_RO_FIRMWARE_VERSION_MAP.get(board) 676*9c5db199SXin Li if isinstance(golden_version, dict): 677*9c5db199SXin Li golden_version = golden_version.get(self.get_host_board()) 678*9c5db199SXin Li if golden_version is None: 679*9c5db199SXin Li raise error.TestFail('Unable to get golden RO version for board: %s' 680*9c5db199SXin Li % board) 681*9c5db199SXin Li if use_dev_signed_fw: 682*9c5db199SXin Li golden_version = self._construct_dev_version(golden_version) 683*9c5db199SXin Li return golden_version 684*9c5db199SXin Li 685*9c5db199SXin Li def get_build_rw_firmware_version(self, use_dev_signed_fw): 686*9c5db199SXin Li """Returns RW firmware version from build.""" 687*9c5db199SXin Li fw_version = self._read_firmware_rw_version(self._build_fw_file) 688*9c5db199SXin Li if use_dev_signed_fw: 689*9c5db199SXin Li fw_version = self._construct_dev_version(fw_version) 690*9c5db199SXin Li return fw_version 691*9c5db199SXin Li 692*9c5db199SXin Li def ensure_running_rw_firmware(self): 693*9c5db199SXin Li """ 694*9c5db199SXin Li Check whether the device is running RW firmware. If not, try rebooting 695*9c5db199SXin Li to RW. 696*9c5db199SXin Li 697*9c5db199SXin Li @return true if successfully verified running RW firmware, false 698*9c5db199SXin Li otherwise. 699*9c5db199SXin Li """ 700*9c5db199SXin Li try: 701*9c5db199SXin Li if self.get_running_firmware_type() != self._FIRMWARE_TYPE_RW: 702*9c5db199SXin Li self._reboot_ec() 703*9c5db199SXin Li if self.get_running_firmware_type() != self._FIRMWARE_TYPE_RW: 704*9c5db199SXin Li # RW may be corrupted. 705*9c5db199SXin Li return False 706*9c5db199SXin Li except: 707*9c5db199SXin Li # We may not always be able to read the firmware version. 708*9c5db199SXin Li # For example, if the firmware is erased due to RDP1, running any 709*9c5db199SXin Li # commands (such as getting the version) won't work. 710*9c5db199SXin Li return False 711*9c5db199SXin Li return True 712*9c5db199SXin Li 713*9c5db199SXin Li def running_fw_version_matches_given_version(self, rw_version, ro_version): 714*9c5db199SXin Li """ 715*9c5db199SXin Li Returns True if the running RO and RW firmware versions match the 716*9c5db199SXin Li provided versions. 717*9c5db199SXin Li """ 718*9c5db199SXin Li try: 719*9c5db199SXin Li running_rw_firmware_version = self.get_running_rw_firmware_version() 720*9c5db199SXin Li running_ro_firmware_version = self.get_running_ro_firmware_version() 721*9c5db199SXin Li 722*9c5db199SXin Li logging.info('RW firmware, running: %s, expected: %s', 723*9c5db199SXin Li running_rw_firmware_version, rw_version) 724*9c5db199SXin Li logging.info('RO firmware, running: %s, expected: %s', 725*9c5db199SXin Li running_ro_firmware_version, ro_version) 726*9c5db199SXin Li 727*9c5db199SXin Li return (running_rw_firmware_version == rw_version and 728*9c5db199SXin Li running_ro_firmware_version == ro_version) 729*9c5db199SXin Li except: 730*9c5db199SXin Li # We may not always be able to read the firmware version. 731*9c5db199SXin Li # For example, if the firmware is erased due to RDP1, running any 732*9c5db199SXin Li # commands (such as getting the version) won't work. 733*9c5db199SXin Li return False 734*9c5db199SXin Li 735*9c5db199SXin Li def is_rollback_set_to_initial_val(self): 736*9c5db199SXin Li """ 737*9c5db199SXin Li Returns True if rollbackinfo matches the initial value that it 738*9c5db199SXin Li should have coming from the factory. 739*9c5db199SXin Li """ 740*9c5db199SXin Li return (self.get_rollback_id() == 741*9c5db199SXin Li self._ROLLBACK_INITIAL_BLOCK_ID 742*9c5db199SXin Li and 743*9c5db199SXin Li self.get_rollback_min_version() == 744*9c5db199SXin Li self._ROLLBACK_INITIAL_MIN_VERSION 745*9c5db199SXin Li and 746*9c5db199SXin Li self.get_rollback_rw_version() == 747*9c5db199SXin Li self._ROLLBACK_INITIAL_RW_VERSION) 748*9c5db199SXin Li 749*9c5db199SXin Li def is_rollback_unset(self): 750*9c5db199SXin Li """ 751*9c5db199SXin Li Returns True if rollbackinfo matches the uninitialized value that it 752*9c5db199SXin Li should have after flashing the entire flash. 753*9c5db199SXin Li """ 754*9c5db199SXin Li return (self.get_rollback_id() == self._ROLLBACK_ZERO_BLOCK_ID 755*9c5db199SXin Li and self.get_rollback_min_version() == 756*9c5db199SXin Li self._ROLLBACK_INITIAL_MIN_VERSION 757*9c5db199SXin Li and self.get_rollback_rw_version() == 758*9c5db199SXin Li self._ROLLBACK_INITIAL_RW_VERSION) 759*9c5db199SXin Li 760*9c5db199SXin Li def biod_upstart_job_enabled(self): 761*9c5db199SXin Li """Returns whether biod's upstart job file is at original location.""" 762*9c5db199SXin Li return self.host.is_file_exists( 763*9c5db199SXin Li os.path.join(self._UPSTART_DIR, self._BIOD_UPSTART_JOB_FILE)) 764*9c5db199SXin Li 765*9c5db199SXin Li def disable_biod_upstart_job(self): 766*9c5db199SXin Li """ 767*9c5db199SXin Li Disable biod's upstart job so that biod will not run after a reboot. 768*9c5db199SXin Li """ 769*9c5db199SXin Li logging.info('Disabling biod\'s upstart job') 770*9c5db199SXin Li filesystem_util.make_rootfs_writable(self.host) 771*9c5db199SXin Li cmd = 'mv %s %s' % (os.path.join( 772*9c5db199SXin Li self._UPSTART_DIR, 773*9c5db199SXin Li self._BIOD_UPSTART_JOB_FILE), self._STATEFUL_PARTITION_DIR) 774*9c5db199SXin Li result = self.run_cmd(cmd) 775*9c5db199SXin Li if result.exit_status != 0: 776*9c5db199SXin Li raise error.TestFail('Unable to disable biod upstart job: %s' % 777*9c5db199SXin Li result.stderr.strip()) 778*9c5db199SXin Li 779*9c5db199SXin Li def enable_biod_upstart_job(self): 780*9c5db199SXin Li """ 781*9c5db199SXin Li Enable biod's upstart job so that biod will run after a reboot. 782*9c5db199SXin Li """ 783*9c5db199SXin Li logging.info('Enabling biod\'s upstart job') 784*9c5db199SXin Li filesystem_util.make_rootfs_writable(self.host) 785*9c5db199SXin Li cmd = 'mv %s %s' % (os.path.join( 786*9c5db199SXin Li self._STATEFUL_PARTITION_DIR, 787*9c5db199SXin Li self._BIOD_UPSTART_JOB_FILE), self._UPSTART_DIR) 788*9c5db199SXin Li result = self.run_cmd(cmd) 789*9c5db199SXin Li if result.exit_status != 0: 790*9c5db199SXin Li raise error.TestFail('Unable to enable biod upstart job: %s' % 791*9c5db199SXin Li result.stderr.strip()) 792*9c5db199SXin Li 793*9c5db199SXin Li def fp_updater_is_enabled(self): 794*9c5db199SXin Li """Returns whether the fingerprint firmware updater is disabled.""" 795*9c5db199SXin Li return not self.host.is_file_exists( 796*9c5db199SXin Li os.path.join(self._FINGERPRINT_BUILD_FW_DIR, 797*9c5db199SXin Li self._DISABLE_FP_UPDATER_FILE)) 798*9c5db199SXin Li 799*9c5db199SXin Li def disable_fp_updater(self): 800*9c5db199SXin Li """Disable the fingerprint firmware updater.""" 801*9c5db199SXin Li filesystem_util.make_rootfs_writable(self.host) 802*9c5db199SXin Li touch_cmd = 'touch %s' % os.path.join(self._FINGERPRINT_BUILD_FW_DIR, 803*9c5db199SXin Li self._DISABLE_FP_UPDATER_FILE) 804*9c5db199SXin Li logging.info('Disabling fp firmware updater') 805*9c5db199SXin Li result = self.run_cmd(touch_cmd) 806*9c5db199SXin Li if result.exit_status != 0: 807*9c5db199SXin Li raise error.TestFail( 808*9c5db199SXin Li 'Unable to write file to disable fp updater:' 809*9c5db199SXin Li ' command failed (rc=%s): %s' % 810*9c5db199SXin Li (result.exit_status, result.stderr.strip() or touch_cmd)) 811*9c5db199SXin Li self.run_cmd('sync') 812*9c5db199SXin Li 813*9c5db199SXin Li def enable_fp_updater(self): 814*9c5db199SXin Li """ 815*9c5db199SXin Li Enable the fingerprint firmware updater. Must be called only after 816*9c5db199SXin Li disable_fp_updater(). 817*9c5db199SXin Li """ 818*9c5db199SXin Li filesystem_util.make_rootfs_writable(self.host) 819*9c5db199SXin Li rm_cmd = 'rm %s' % os.path.join(self._FINGERPRINT_BUILD_FW_DIR, 820*9c5db199SXin Li self._DISABLE_FP_UPDATER_FILE) 821*9c5db199SXin Li logging.info('Enabling fp firmware updater') 822*9c5db199SXin Li result = self.run_cmd(rm_cmd) 823*9c5db199SXin Li if result.exit_status != 0: 824*9c5db199SXin Li raise error.TestFail( 825*9c5db199SXin Li 'Unable to rm .disable_fp_updater:' 826*9c5db199SXin Li ' command failed (rc=%s): %s' % 827*9c5db199SXin Li (result.exit_status, result.stderr.strip() or rm_cmd)) 828*9c5db199SXin Li self.run_cmd('sync') 829*9c5db199SXin Li 830*9c5db199SXin Li def flash_rw_ro_firmware(self, fw_path): 831*9c5db199SXin Li """Flashes *all* firmware (both RO and RW).""" 832*9c5db199SXin Li # Check if FPMCU firmware needs to be re-flashed during cleanup 833*9c5db199SXin Li self._need_fw_restore = True 834*9c5db199SXin Li self.set_hardware_write_protect(False) 835*9c5db199SXin Li flash_cmd = 'flash_fp_mcu' + ' --noservices ' + fw_path 836*9c5db199SXin Li logging.info('Running flash cmd: %s', flash_cmd) 837*9c5db199SXin Li flash_result = self.run_cmd(flash_cmd) 838*9c5db199SXin Li self.set_hardware_write_protect(True) 839*9c5db199SXin Li 840*9c5db199SXin Li # Zork cannot rebind cros-ec-uart after flashing, so an AP reboot is 841*9c5db199SXin Li # needed to talk to FPMCU. See b/170213489. 842*9c5db199SXin Li # We have to do this even if flashing failed. 843*9c5db199SXin Li if hasattr(self, '_dut_needs_reboot') and self._dut_needs_reboot: 844*9c5db199SXin Li self.host.reboot() 845*9c5db199SXin Li if self.fp_updater_is_enabled(): 846*9c5db199SXin Li raise error.TestFail( 847*9c5db199SXin Li 'Fp updater was not disabled when firmware is flashed') 848*9c5db199SXin Li # If we just re-enable fp updater, it can still update (race 849*9c5db199SXin Li # condition), so do it later in cleanup. 850*9c5db199SXin Li 851*9c5db199SXin Li if flash_result.exit_status != 0: 852*9c5db199SXin Li raise error.TestFail('Flashing RW/RO firmware failed') 853*9c5db199SXin Li 854*9c5db199SXin Li def is_hardware_write_protect_enabled(self): 855*9c5db199SXin Li """Returns state of hardware write protect.""" 856*9c5db199SXin Li fw_wp_state = self.servo.get('fw_wp_state') 857*9c5db199SXin Li return fw_wp_state == 'on' or fw_wp_state == 'force_on' 858*9c5db199SXin Li 859*9c5db199SXin Li def set_hardware_write_protect(self, enable): 860*9c5db199SXin Li """Enables or disables hardware write protect.""" 861*9c5db199SXin Li self.servo.set('fw_wp_state', 'force_on' if enable else 'force_off') 862*9c5db199SXin Li 863*9c5db199SXin Li def set_software_write_protect(self, enable): 864*9c5db199SXin Li """Enables or disables software write protect.""" 865*9c5db199SXin Li arg = 'enable' if enable else 'disable' 866*9c5db199SXin Li self._run_ectool_cmd('flashprotect ' + arg) 867*9c5db199SXin Li # TODO(b/116396469): The flashprotect command returns an error even on 868*9c5db199SXin Li # success. 869*9c5db199SXin Li # if result.exit_status != 0: 870*9c5db199SXin Li # raise error.TestFail('Failed to modify software write protect') 871*9c5db199SXin Li 872*9c5db199SXin Li # TODO(b/116396469): "flashprotect enable" command is slow, so wait for 873*9c5db199SXin Li # it to complete before attempting to reboot. 874*9c5db199SXin Li time.sleep(2) 875*9c5db199SXin Li self._reboot_ec() 876*9c5db199SXin Li 877*9c5db199SXin Li def _reboot_ec(self): 878*9c5db199SXin Li """Reboots the fingerprint MCU (FPMCU).""" 879*9c5db199SXin Li self._run_ectool_cmd('reboot_ec') 880*9c5db199SXin Li # TODO(b/116396469): The reboot_ec command returns an error even on 881*9c5db199SXin Li # success. 882*9c5db199SXin Li # if result.exit_status != 0: 883*9c5db199SXin Li # raise error.TestFail('Failed to reboot ec') 884*9c5db199SXin Li time.sleep(2) 885*9c5db199SXin Li 886*9c5db199SXin Li def get_files_from_dut(self, src, dst): 887*9c5db199SXin Li """Copes files from DUT to server.""" 888*9c5db199SXin Li logging.info('Copying files from (%s) to (%s).', src, dst) 889*9c5db199SXin Li self.host.get_file(src, dst, delete_dest=True) 890*9c5db199SXin Li 891*9c5db199SXin Li def copy_files_to_dut(self, src_dir, dst_dir): 892*9c5db199SXin Li """Copies files from server to DUT.""" 893*9c5db199SXin Li logging.info('Copying files from (%s) to (%s).', src_dir, dst_dir) 894*9c5db199SXin Li self.host.send_file(src_dir, dst_dir, delete_dest=True) 895*9c5db199SXin Li # Sync the filesystem in case we need to reboot the AP soon. 896*9c5db199SXin Li self.run_cmd('sync') 897*9c5db199SXin Li 898*9c5db199SXin Li def run_server_cmd(self, command, timeout=65): 899*9c5db199SXin Li """Runs command on server; return result with output and exit code.""" 900*9c5db199SXin Li logging.info('Server execute: %s', command) 901*9c5db199SXin Li result = utils.run(command, timeout=timeout, ignore_status=True) 902*9c5db199SXin Li logging.info('exit_code: %d', result.exit_status) 903*9c5db199SXin Li logging.info('stdout:\n%s', result.stdout) 904*9c5db199SXin Li logging.info('stderr:\n%s', result.stderr) 905*9c5db199SXin Li return result 906*9c5db199SXin Li 907*9c5db199SXin Li def run_cmd(self, command, timeout=300): 908*9c5db199SXin Li """Runs command on the DUT; return result with output and exit code.""" 909*9c5db199SXin Li logging.debug('DUT Execute: %s', command) 910*9c5db199SXin Li result = self.host.run(command, timeout=timeout, ignore_status=True) 911*9c5db199SXin Li logging.info('exit_code: %d', result.exit_status) 912*9c5db199SXin Li logging.info('stdout:\n%s', result.stdout) 913*9c5db199SXin Li logging.info('stderr:\n%s', result.stderr) 914*9c5db199SXin Li return result 915*9c5db199SXin Li 916*9c5db199SXin Li def _run_ectool_cmd(self, command): 917*9c5db199SXin Li """Runs ectool on DUT; return result with output and exit code.""" 918*9c5db199SXin Li cmd = 'ectool ' + self._CROS_FP_ARG + ' ' + command 919*9c5db199SXin Li result = self.run_cmd(cmd) 920*9c5db199SXin Li return result 921*9c5db199SXin Li 922*9c5db199SXin Li def _run_cros_config_cmd(self, command): 923*9c5db199SXin Li """Runs cros_config on DUT; return result with output and exit code.""" 924*9c5db199SXin Li cmd = 'cros_config ' + self._CROS_CONFIG_FINGERPRINT_PATH + ' ' \ 925*9c5db199SXin Li + command 926*9c5db199SXin Li result = self.run_cmd(cmd) 927*9c5db199SXin Li return result 928*9c5db199SXin Li 929*9c5db199SXin Li def _run_cros_config_cmd_cat(self, command): 930*9c5db199SXin Li """Runs cat /run/chromeos-config/v1 on DUT; return result.""" 931*9c5db199SXin Li cmd = "cat /run/chromeos-config/v1/{}".format(command) 932*9c5db199SXin Li return self.run_cmd(cmd) 933*9c5db199SXin Li 934*9c5db199SXin Li def _run_dump_fmap_cmd(self, fw_file, section): 935*9c5db199SXin Li """ 936*9c5db199SXin Li Runs "dump_fmap" on DUT for given file. 937*9c5db199SXin Li Returns value of given section. 938*9c5db199SXin Li """ 939*9c5db199SXin Li # Write result to stderr while redirecting stderr to stdout 940*9c5db199SXin Li # and dropping stdout. This is done because dump_map only writes the 941*9c5db199SXin Li # value read from a section to a file (will not just print it to 942*9c5db199SXin Li # stdout). 943*9c5db199SXin Li cmd = 'dump_fmap -x ' + fw_file + ' ' + section +\ 944*9c5db199SXin Li ':/dev/stderr /dev/stderr >& /dev/stdout > /dev/null' 945*9c5db199SXin Li result = self.run_cmd(cmd) 946*9c5db199SXin Li if result.exit_status != 0: 947*9c5db199SXin Li raise error.TestFail('Failed to read section: %s' % section) 948*9c5db199SXin Li return result.stdout.rstrip('\0') 949*9c5db199SXin Li 950*9c5db199SXin Li def _run_futility_show_cmd(self, fw_file): 951*9c5db199SXin Li """ 952*9c5db199SXin Li Runs "futility show" on DUT for given file. 953*9c5db199SXin Li Returns stdout on success. 954*9c5db199SXin Li """ 955*9c5db199SXin Li futility_cmd = 'futility show ' + fw_file 956*9c5db199SXin Li result = self.run_cmd(futility_cmd) 957*9c5db199SXin Li if result.exit_status != 0: 958*9c5db199SXin Li raise error.TestFail('Unable to run futility on device') 959*9c5db199SXin Li return result.stdout 960*9c5db199SXin Li 961*9c5db199SXin Li def _run_sha256sum_cmd(self, file_name): 962*9c5db199SXin Li """ 963*9c5db199SXin Li Runs "sha256sum" on DUT for given file. 964*9c5db199SXin Li Returns stdout on success. 965*9c5db199SXin Li """ 966*9c5db199SXin Li sha_cmd = 'sha256sum ' + file_name 967*9c5db199SXin Li result = self.run_cmd(sha_cmd) 968*9c5db199SXin Li if result.exit_status != 0: 969*9c5db199SXin Li raise error.TestFail('Unable to calculate sha256sum on device') 970*9c5db199SXin Li return result 971*9c5db199SXin Li 972*9c5db199SXin Li def run_test(self, test_name, *args): 973*9c5db199SXin Li """Runs test on DUT.""" 974*9c5db199SXin Li logging.info('Running %s', test_name) 975*9c5db199SXin Li # Redirecting stderr to stdout since some commands intentionally fail 976*9c5db199SXin Li # and it's easier to read when everything ordered in the same output 977*9c5db199SXin Li test_cmd = ' '.join([os.path.join(self._dut_working_dir, test_name)] + 978*9c5db199SXin Li list(args) + ['2>&1']) 979*9c5db199SXin Li # Change the working dir so we can write files from within the test 980*9c5db199SXin Li # (otherwise defaults to $HOME (/root), which is not usually writable) 981*9c5db199SXin Li # Note that dut_working_dir is automatically cleaned up so tests don't 982*9c5db199SXin Li # need to worry about files from previous invocations or other tests. 983*9c5db199SXin Li test_cmd = '(cd ' + self._dut_working_dir + ' && ' + test_cmd + ')' 984*9c5db199SXin Li logging.info('Test command: %s', test_cmd) 985*9c5db199SXin Li result = self.run_cmd(test_cmd) 986*9c5db199SXin Li if result.exit_status != 0: 987*9c5db199SXin Li raise error.TestFail(test_name + ' failed') 988