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. 4import logging 5 6from autotest_lib.client.common_lib import error 7from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 8from autotest_lib.server.cros.faft.firmware_test import ConnectionError 9 10 11class firmware_UpdaterModes(FirmwareTest): 12 """RO+RW firmware update using chromeos-firmwareupdate with various modes. 13 14 This test uses --emulate, to avoid writing repeatedly to the flash. 15 """ 16 17 version = 1 18 19 SHELLBALL = '/usr/sbin/chromeos-firmwareupdate' 20 21 def initialize(self, host, cmdline_args, ec_wp=None): 22 """ 23 During initialization, back up the firmware, in case --emulate ever 24 breaks in a way that causes real writes. 25 """ 26 super(firmware_UpdaterModes, self).initialize(host, cmdline_args, ec_wp) 27 self.backup_firmware() 28 29 def cleanup(self): 30 """Restore the original firmware, if it was somehow overwritten.""" 31 try: 32 if self.is_firmware_saved(): 33 self.restore_firmware() 34 except ConnectionError: 35 logging.error("ERROR: DUT did not come up after firmware restore!") 36 finally: 37 super(firmware_UpdaterModes, self).cleanup() 38 39 def get_bios_fwids(self, path): 40 """Return the BIOS fwids for the given file""" 41 return self.faft_client.updater.get_image_fwids('bios', path) 42 43 def run_case(self, mode, write_protected, written, modify_ro=True, 44 should_abort=False, writes_gbb=False): 45 """Run chromeos-firmwareupdate with given sub-case 46 47 @param mode: factory or recovery or autoupdate 48 @param write_protected: is the flash write protected (--wp)? 49 @param modify_ro: should ro fwid be modified? 50 @param written: list of bios areas expected to change 51 @param should_abort: if True, the updater should abort with no changes 52 @param writes_gbb: if True, the updater should rewrite gbb flags. 53 @return: a list of failure messages for the case 54 """ 55 self.faft_client.updater.reset_shellball() 56 57 fake_bios_path = self.faft_client.updater.copy_bios('fake-bios.bin') 58 self.faft_client.updater.set_image_gbb_flags(0, fake_bios_path) 59 60 before_fwids = {'bios': self.get_bios_fwids(fake_bios_path)} 61 before_gbb = self.faft_client.updater.get_image_gbb_flags( 62 fake_bios_path) 63 64 case_desc = ('chromeos-firmwareupdate --mode=%s --wp=%s' 65 % (mode, write_protected)) 66 67 if modify_ro: 68 append = 'ro+rw' 69 else: 70 case_desc += ' [rw-only]' 71 append = 'rw' 72 73 # Repack the shellball with modded fwids 74 self.modify_shellball(append, modify_ro) 75 modded_fwids = self.identify_shellball() 76 image_gbb = self.faft_client.updater.get_image_gbb_flags() 77 78 options = ['--emulate', fake_bios_path, '--wp=%s' % write_protected] 79 80 logging.info("%s (should write %s)", case_desc, 81 ', '.join(written).upper() or 'nothing') 82 83 errors = [] 84 result = self.run_chromeos_firmwareupdate(mode, append, options, 85 ignore_status=True) 86 if result.exit_status == 0: 87 if should_abort: 88 errors.append( 89 "...updater: with current mode and write-protect value," 90 " should abort (rc!=0) and not modify anything") 91 else: 92 if should_abort: 93 logging.debug('updater aborted as expected') 94 else: 95 errors.append('...updater: unexpectedly failed (rc!=0)') 96 97 after_fwids = {'bios': self.get_bios_fwids(fake_bios_path)} 98 after_gbb = self.faft_client.updater.get_image_gbb_flags(fake_bios_path) 99 expected_written = {'bios': written or []} 100 101 errors += self.check_fwids_written( 102 before_fwids, modded_fwids, after_fwids, expected_written) 103 104 if not errors: 105 logging.debug('...bios versions correct: %s', after_fwids['bios']) 106 107 if writes_gbb: 108 if after_gbb != image_gbb: 109 # Expect rewritten, but it might not be different from before 110 errors.append( 111 "...GBB flags weren't rewritten to match the image: " 112 "before=0x%x, image=0x%x, after=0x%x." 113 % (before_gbb, image_gbb, after_gbb)) 114 else: 115 if after_gbb != before_gbb: 116 errors.append( 117 "...GBB flags were unexpectedly rewritten: " 118 "before=0x%x, image=0x%x, after=0x%x." 119 % (before_gbb, image_gbb, after_gbb)) 120 121 if self.restore_firmware(): 122 # If real writes happen, fail immediately to avoid flash wear. 123 raise error.TestFail( 124 'With chromeos-firmwareupdate --emulate, real flash device ' 125 'was unexpectedly modified.') 126 127 if errors: 128 case_message = '%s:\n%s' % (case_desc, '\n'.join(errors)) 129 logging.error('%s', case_message) 130 return [case_message] 131 return [] 132 133 def run_once(self, host): 134 """Run test, iterating through combinations of mode and write-protect""" 135 errors = [] 136 137 # factory: update A, B, and RO; reset gbb flags. If WP=1, abort. 138 errors += self.run_case('factory', 0, ['ro', 'a', 'b'], writes_gbb=True) 139 errors += self.run_case('factory', 1, [], should_abort=True) 140 141 # recovery: update A and B, and RO if WP=0. 142 errors += self.run_case('recovery', 0, ['ro', 'a', 'b']) 143 errors += self.run_case('recovery', 1, ['a', 'b']) 144 145 # autoupdate with changed ro: same as recovery (modify ro only if WP=0) 146 errors += self.run_case('autoupdate', 0, ['ro', 'a', 'b']) 147 errors += self.run_case('autoupdate', 1, ['b']) 148 149 # autoupdate with unchanged ro: update inactive slot 150 errors += self.run_case('autoupdate', 0, ['b'], modify_ro=False) 151 errors += self.run_case('autoupdate', 1, ['b'], modify_ro=False) 152 153 if len(errors) == 1: 154 raise error.TestFail(errors[0]) 155 elif errors: 156 raise error.TestFail( 157 '%s combinations of mode and write-protect failed:\n%s' % 158 (len(errors), '\n'.join(errors))) 159