• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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