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.os_if.run_shell_command(
50            'sh %s --sb_extract %s' % (working_shellball, self._work_path))
51
52
53    def cleanup_temp_dir(self):
54        """Cleanup temporary directory."""
55        if self.os_if.is_dir(self._temp_path):
56            self.os_if.remove_dir(self._temp_path)
57
58
59    def retrieve_fwid(self):
60        """Retrieve shellball's fwid.
61
62        This method should be called after setup_firmwareupdate_temp_dir.
63
64        Returns:
65            Shellball's fwid.
66        """
67        self.os_if.run_shell_command('dump_fmap -x %s %s' %
68            (os.path.join(self._work_path, 'bios.bin'), 'RW_FWID_A'))
69
70        [fwid] = self.os_if.run_shell_command_get_output(
71            "cat RW_FWID_A | tr '\\0' '\\t' | cut -f1")
72        return fwid
73
74
75    def resign_firmware(self, version):
76        """Resign firmware with version.
77
78        Args:
79            version: new firmware version number.
80        """
81        ro_normal = 0
82        self.os_if.run_shell_command(
83                '/usr/share/vboot/bin/resign_firmwarefd.sh '
84                '%s %s %s %s %s %s %s %d %d' % (
85                    os.path.join(self._work_path, 'bios.bin'),
86                    os.path.join(self._temp_path, 'output.bin'),
87                    os.path.join(self._keys_path, 'firmware_data_key.vbprivk'),
88                    os.path.join(self._keys_path, 'firmware.keyblock'),
89                    os.path.join(self._keys_path, 'dev_firmware_data_key.vbprivk'),
90                    os.path.join(self._keys_path, 'dev_firmware.keyblock'),
91                    os.path.join(self._keys_path, 'kernel_subkey.vbpubk'),
92                    version,
93                    ro_normal))
94        self.os_if.copy_file('%s' % os.path.join(self._temp_path, 'output.bin'),
95                             '%s' % os.path.join(self._work_path, 'bios.bin'))
96
97
98    def repack_shellball(self, append):
99        """Repack shellball with new fwid.
100
101        New fwid follows the rule: [orignal_fwid]-[append].
102
103        Args:
104            append: use for new fwid naming.
105        """
106        self.os_if.copy_file(
107                '/usr/sbin/chromeos-firmwareupdate',
108                os.path.join(self._temp_path,
109                             'chromeos-firmwareupdate-%s' % append))
110
111        self.os_if.run_shell_command('sh %s --sb_repack %s' % (
112            os.path.join(self._temp_path,
113                         'chromeos-firmwareupdate-%s' % append),
114            self._work_path))
115
116        args = ['-i']
117        args.append('"s/TARGET_FWID=\\"\\(.*\\)\\"/TARGET_FWID=\\"\\1.%s\\"/g"'
118                    % append)
119        args.append(os.path.join(self._temp_path,
120                                 'chromeos-firmwareupdate-%s' % append))
121        cmd = 'sed %s' % ' '.join(args)
122        self.os_if.run_shell_command(cmd)
123
124        args = ['-i']
125        args.append('"s/TARGET_UNSTABLE=\\".*\\"/TARGET_UNSTABLE=\\"\\"/g"')
126        args.append(os.path.join(self._temp_path,
127                                 'chromeos-firmwareupdate-%s' % append))
128        cmd = 'sed %s' % ' '.join(args)
129        self.os_if.run_shell_command(cmd)
130
131
132    def run_firmwareupdate(self, mode, updater_append=None, options=[]):
133        """Do firmwareupdate with updater in temp_dir.
134
135        Args:
136            updater_append: decide which shellball to use with format
137                chromeos-firmwareupdate-[append]. Use'chromeos-firmwareupdate'
138                if updater_append is None.
139            mode: ex.'autoupdate', 'recovery', 'bootok', 'factory_install'...
140            options: ex. ['--noupdate_ec', '--nocheck_rw_compatible'] or [] for
141                no option.
142        """
143        if updater_append:
144            updater = os.path.join(
145                self._temp_path, 'chromeos-firmwareupdate-%s' % updater_append)
146        else:
147            updater = os.path.join(self._temp_path, 'chromeos-firmwareupdate')
148
149        self.os_if.run_shell_command(
150            '/bin/sh %s --mode %s %s' % (updater, mode, ' '.join(options)))
151
152
153    def get_temp_path(self):
154        """Get temp directory path."""
155        return self._temp_path
156
157
158    def get_keys_path(self):
159        """Get keys directory path."""
160        return self._keys_path
161
162
163    def get_work_path(self):
164        """Get work directory path."""
165        return self._work_path
166