1# Copyright (c) 2012 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 5""" 6A module to support automatic firmware update. 7 8See FirmwareUpdater object below. 9""" 10 11import os 12 13class FirmwareUpdater(object): 14 """ 15 An object to support firmware update. 16 17 This object will create a temporary directory in /var/tmp/faft/autest with 18 two subdirectory keys/ and work/. You can modify the keys in keys/ 19 directory. If you want to provide a given shellball to do firmware update, 20 put shellball under /var/tmp/faft/autest with name chromeos-firmwareupdate. 21 """ 22 23 def __init__(self, os_if): 24 self.os_if = os_if 25 self._temp_path = '/var/tmp/faft/autest' 26 self._keys_path = os.path.join(self._temp_path, 'keys') 27 self._work_path = os.path.join(self._temp_path, 'work') 28 29 if not self.os_if.is_dir(self._temp_path): 30 self._setup_temp_dir() 31 32 33 def _setup_temp_dir(self): 34 """Setup temporary directory. 35 36 Devkeys are copied to _key_path. Then, shellball (default: 37 /usr/sbin/chromeos-firmwareupdate) is extracted to _work_path. 38 """ 39 self.cleanup_temp_dir() 40 41 self.os_if.create_dir(self._temp_path) 42 self.os_if.create_dir(self._work_path) 43 self.os_if.copy_dir('/usr/share/vboot/devkeys', self._keys_path) 44 45 original_shellball = '/usr/sbin/chromeos-firmwareupdate' 46 working_shellball = os.path.join(self._temp_path, 47 'chromeos-firmwareupdate') 48 self.os_if.copy_file(original_shellball, working_shellball) 49 self.extract_shellball() 50 51 52 def cleanup_temp_dir(self): 53 """Cleanup temporary directory.""" 54 if self.os_if.is_dir(self._temp_path): 55 self.os_if.remove_dir(self._temp_path) 56 57 58 def retrieve_fwid(self): 59 """Retrieve shellball's fwid. 60 61 This method should be called after setup_firmwareupdate_temp_dir. 62 63 Returns: 64 Shellball's fwid. 65 """ 66 self.os_if.run_shell_command('dump_fmap -x %s %s' % 67 (os.path.join(self._work_path, 'bios.bin'), 'RW_FWID_A')) 68 69 [fwid] = self.os_if.run_shell_command_get_output( 70 "cat RW_FWID_A | tr '\\0' '\\t' | cut -f1") 71 return fwid 72 73 74 def resign_firmware(self, version): 75 """Resign firmware with version. 76 77 Args: 78 version: new firmware version number. 79 """ 80 ro_normal = 0 81 self.os_if.run_shell_command( 82 '/usr/share/vboot/bin/resign_firmwarefd.sh ' 83 '%s %s %s %s %s %s %s %d %d' % ( 84 os.path.join(self._work_path, 'bios.bin'), 85 os.path.join(self._temp_path, 'output.bin'), 86 os.path.join(self._keys_path, 'firmware_data_key.vbprivk'), 87 os.path.join(self._keys_path, 'firmware.keyblock'), 88 os.path.join(self._keys_path, 'dev_firmware_data_key.vbprivk'), 89 os.path.join(self._keys_path, 'dev_firmware.keyblock'), 90 os.path.join(self._keys_path, 'kernel_subkey.vbpubk'), 91 version, 92 ro_normal)) 93 self.os_if.copy_file('%s' % os.path.join(self._temp_path, 'output.bin'), 94 '%s' % os.path.join(self._work_path, 'bios.bin')) 95 96 97 def extract_shellball(self, append=None): 98 """Extract the working shellball. 99 100 Args: 101 append: decide which shellball to use with format 102 chromeos-firmwareupdate-[append]. Use 'chromeos-firmwareupdate' 103 if append is None. 104 """ 105 working_shellball = os.path.join(self._temp_path, 106 'chromeos-firmwareupdate') 107 if append: 108 working_shellball = working_shellball + '-%s' % append 109 110 self.os_if.run_shell_command('sh %s --sb_extract %s' % ( 111 working_shellball, self._work_path)) 112 113 114 def repack_shellball(self, append=None): 115 """Repack shellball with new fwid. 116 117 New fwid follows the rule: [orignal_fwid]-[append]. 118 119 Args: 120 append: save the new shellball with a suffix, for example, 121 chromeos-firmwareupdate-[append]. Use 'chromeos-firmwareupdate' 122 if append is None. 123 """ 124 working_shellball = os.path.join(self._temp_path, 125 'chromeos-firmwareupdate') 126 if append: 127 self.os_if.copy_file(working_shellball, 128 working_shellball + '-%s' % append) 129 working_shellball = working_shellball + '-%s' % append 130 131 self.os_if.run_shell_command('sh %s --sb_repack %s' % ( 132 working_shellball, self._work_path)) 133 134 if append: 135 args = ['-i'] 136 args.append( 137 '"s/TARGET_FWID=\\"\\(.*\\)\\"/TARGET_FWID=\\"\\1.%s\\"/g"' 138 % append) 139 args.append(working_shellball) 140 cmd = 'sed %s' % ' '.join(args) 141 self.os_if.run_shell_command(cmd) 142 143 args = ['-i'] 144 args.append('"s/TARGET_UNSTABLE=\\".*\\"/TARGET_UNSTABLE=\\"\\"/g"') 145 args.append(working_shellball) 146 cmd = 'sed %s' % ' '.join(args) 147 self.os_if.run_shell_command(cmd) 148 149 150 def run_firmwareupdate(self, mode, updater_append=None, options=[]): 151 """Do firmwareupdate with updater in temp_dir. 152 153 Args: 154 updater_append: decide which shellball to use with format 155 chromeos-firmwareupdate-[append]. Use'chromeos-firmwareupdate' 156 if updater_append is None. 157 mode: ex.'autoupdate', 'recovery', 'bootok', 'factory_install'... 158 options: ex. ['--noupdate_ec', '--nocheck_rw_compatible'] or [] for 159 no option. 160 """ 161 if updater_append: 162 updater = os.path.join( 163 self._temp_path, 'chromeos-firmwareupdate-%s' % updater_append) 164 else: 165 updater = os.path.join(self._temp_path, 'chromeos-firmwareupdate') 166 command = '/bin/sh %s --mode %s %s' % (updater, mode, ' '.join(options)) 167 168 if mode == 'bootok': 169 # Since CL:459837, bootok is moved to chromeos-setgoodfirmware. 170 new_command = '/usr/sbin/chromeos-setgoodfirmware' 171 command = 'if [ -e %s ]; then %s; else %s; fi' % ( 172 new_command, new_command, command) 173 174 self.os_if.run_shell_command(command) 175 176 177 def get_temp_path(self): 178 """Get temp directory path.""" 179 return self._temp_path 180 181 182 def get_keys_path(self): 183 """Get keys directory path.""" 184 return self._keys_path 185 186 187 def get_work_path(self): 188 """Get work directory path.""" 189 return self._work_path 190