blob: f79289fcdd6b53617f1428062c95f22514ccb8ab [file] [log] [blame]
Simon Glass89b86b82011-07-17 23:49:49 -07001# Copyright (c) 2011 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"""This module builds a firmware image for a tegra-based board.
6
7This modules uses a few rudimentary other libraries for its activity.
8
9Here are the names we give to the various files we deal with. It is important
10to keep these consistent!
11
12 uboot u-boot.bin (with no device tree)
13 fdt the fdt blob
14 bct the BCT file
15 bootstub uboot + fdt
16 signed (uboot + fdt + bct) signed blob
17"""
18
19import os
20import re
21
22import cros_output
23from fdt import Fdt
24from pack_firmware import PackFirmware
25import shutil
26import tempfile
27from tools import Tools
28from write_firmware import WriteFirmware
29
30# This data is required by bmpblk_utility. Does it ever change?
31# It was stored with the chromeos-bootimage ebuild, but we want
32# this utility to work outside the chroot.
33yaml_data = '''
34bmpblock: 1.0
35
36images:
37 devmode: DeveloperBmp/DeveloperBmp.bmp
38 recovery: RecoveryBmp/RecoveryBmp.bmp
39 rec_yuck: RecoveryNoOSBmp/RecoveryNoOSBmp.bmp
40 rec_insert: RecoveryMissingOSBmp/RecoveryMissingOSBmp.bmp
41
42screens:
43 dev_en:
44 - [0, 0, devmode]
45 rec_en:
46 - [0, 0, recovery]
47 yuck_en:
48 - [0, 0, rec_yuck]
49 ins_en:
50 - [0, 0, rec_insert]
51
52localizations:
53 - [ dev_en, rec_en, yuck_en, ins_en ]
54'''
55
56class Bundle:
Simon Glass290a1802011-07-17 13:54:32 -070057 """This class encapsulates the entire bundle firmware logic.
Simon Glass89b86b82011-07-17 23:49:49 -070058
Simon Glass290a1802011-07-17 13:54:32 -070059 Sequence of events:
60 bundle = Bundle(tools.Tools(), cros_output.Output())
61 bundle.SetDirs(...)
62 bundle.SetFiles(...)
63 bundle.SetOptions(...)
64 bundle.SelectFdt(fdt.Fdt('filename.dtb')
65 .. can call bundle.AddConfigList() if required
66 bundle.Start(...)
Simon Glass89b86b82011-07-17 23:49:49 -070067
Simon Glass290a1802011-07-17 13:54:32 -070068 Public properties:
69 fdt: The fdt object that we use for building our image. This wil be the
70 one specified by the user, except that we might add config options
71 to it. This is set up by SelectFdt() which must be called before
72 bundling starts.
73 uboot_fname: Full filename of the U-Boot binary we use.
74 bct_fname: Full filename of the BCT file we use.
75 """
Simon Glass89b86b82011-07-17 23:49:49 -070076
Simon Glass290a1802011-07-17 13:54:32 -070077 def __init__(self, tools, output):
78 """Set up a new Bundle object.
Simon Glass89b86b82011-07-17 23:49:49 -070079
Simon Glass290a1802011-07-17 13:54:32 -070080 Args:
81 tools: A tools.Tools object to use for external tools.
82 output: A cros_output.Output object to use for program output.
Simon Glass89b86b82011-07-17 23:49:49 -070083 """
Simon Glass290a1802011-07-17 13:54:32 -070084 self.text_base = None # Base of U-Boot image in memory
85
86 self._tools = tools
87 self._out = output
88
89 # Set up the things we need to know in order to operate.
90 self._board = None # Board name, e.g. tegra2_seaboard.
91 self._fdt_fname = None # Filename of our FDT.
92 self.uboot_fname = None # Filename of our U-Boot binary.
93 self.bct_fname = None # Filename of our BCT file.
94 self.fdt = None # Our Fdt object.
Hung-Te Lin5b649382011-08-03 15:01:16 +080095 self.bmpblk_fname = None # Filename of our Bitmap Block
Simon Glass290a1802011-07-17 13:54:32 -070096
97 def SetDirs(self, keydir):
98 """Set up directories required for Bundle.
99
100 Args:
101 keydir: Directory containing keys to use for signing firmware.
102 """
103 self._keydir = keydir
104
Hung-Te Lin5b649382011-08-03 15:01:16 +0800105 def SetFiles(self, board, uboot, bct, bmpblk):
Simon Glass290a1802011-07-17 13:54:32 -0700106 """Set up files required for Bundle.
107
108 Args:
109 board: The name of the board to target (e.g. tegra2_seaboard).
110 uboot: The filename of the u-boot.bin image to use.
111 bct: The filename of the binary BCT file to use.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800112 bmpblk: The filename of bitmap block file to use.
Simon Glass290a1802011-07-17 13:54:32 -0700113 """
114 self._board = board
115 self.uboot_fname = uboot
116 self.bct_fname = bct
Hung-Te Lin5b649382011-08-03 15:01:16 +0800117 self.bmpblk_fname = bmpblk
Simon Glass290a1802011-07-17 13:54:32 -0700118
119 def SetOptions(self, small):
120 """Set up options supported by Bundle.
121
122 Args:
123 small: Only create a signed U-Boot - don't produce the full packed
124 firmware image. This is useful for devs who want to replace just the
125 U-Boot part while keeping the keys, gbb, etc. the same.
126 """
127 self._small = small
128
129 def CheckOptions(self):
130 """Check provided options and select defaults."""
131 if not self._board:
132 raise ValueError('No board defined - please define a board to use')
133 build_root = os.path.join('##', 'build', self._board, 'u-boot')
134 if not self._fdt_fname:
135 self._fdt_fname = os.path.join(build_root, 'dtb', '%s.dtb' %
136 re.sub('_', '-', self._board))
137 if not self.uboot_fname:
138 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
139 if not self.bct_fname:
140 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass89b86b82011-07-17 23:49:49 -0700141
Simon Glass56577572011-07-19 11:08:06 +1200142 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700143 """Create a GBB for the image.
144
Simon Glass56577572011-07-19 11:08:06 +1200145 Args:
146 hardware_id: Hardware ID to use for this board. If None, then the
147 default from the Fdt will be used
148
Simon Glass89b86b82011-07-17 23:49:49 -0700149 Returns:
150 Path of the created GBB file.
151
152 Raises:
153 CmdError if a command fails.
154 """
Simon Glass56577572011-07-19 11:08:06 +1200155 if not hardware_id:
156 hardware_id = self.fdt.GetString('/config/hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700157 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700158 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700159
Hung-Te Lin5b649382011-08-03 15:01:16 +0800160 if not self.bmpblk_fname:
161 # TODO(hungte) Remove this bitmap generation in future because we should
162 # always use pre-genereated bitmaps.
163 # Get LCD dimensions from the device tree.
164 screen_geometry = '%sx%s' % (self.fdt.GetInt('/lcd/width'),
165 self.fdt.GetInt('/lcd/height'))
Simon Glass89b86b82011-07-17 23:49:49 -0700166
Hung-Te Lin5b649382011-08-03 15:01:16 +0800167 # This is the magic directory that make_bmp_image writes to!
168 out_dir = 'out_%s' % re.sub(' ', '_', hardware_id)
169 bmp_dir = os.path.join(odir, out_dir)
170 self._out.Progress('Creating bitmaps')
171 self._tools.Run('make_bmp_image', [hardware_id, screen_geometry, 'arm'],
172 cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700173
Hung-Te Lin5b649382011-08-03 15:01:16 +0800174 self._out.Progress('Creating bitmap block')
175 yaml = 'config.yaml'
176 self._tools.WriteFile(os.path.join(bmp_dir, yaml), yaml_data)
177 self._tools.Run('bmpblk_utility', ['-z', '2', '-c', yaml, 'bmpblk.bin'],
178 cwd=bmp_dir)
179 self.bmpblk_fname = os.path.join(out_dir, 'bmpblk.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700180
181 self._out.Progress('Creating GBB')
182 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
183 sizes = ['%#x' % size for size in sizes]
184 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700185 keydir = self._tools.Filename(self._keydir)
186 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700187 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200188 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700189 '--rootkey=%s/root_key.vbpubk' % keydir,
190 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Hung-Te Lin5b649382011-08-03 15:01:16 +0800191 '--bmpfv=%s' % self.bmpblk_fname,
Simon Glass89b86b82011-07-17 23:49:49 -0700192 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700193 cwd=odir)
194 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700195
Simon Glasse13ee2c2011-07-28 08:12:28 +1200196 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700197 """Sign an image so that the Tegra SOC will boot it.
198
199 Args:
200 bct: BCT file to use.
201 bootstub: Boot stub (U-Boot + fdt) file to sign.
202 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700203
204 Returns:
205 filename of signed image.
206
207 Raises:
208 CmdError if a command fails.
209 """
210 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200211 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700212 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200213 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700214 fd = open(config, 'w')
215 fd.write('Version = 1;\n')
216 fd.write('Redundancy = 1;\n')
217 fd.write('Bctfile = %s;\n' % bct)
218 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
219 text_base))
220 fd.close()
221
222 self._tools.Run('cbootimage', [config, signed])
223 self._tools.OutputSize('BCT', bct)
224 self._tools.OutputSize('Signed image', signed)
225 return signed
226
Doug Anderson86ce5f42011-07-27 10:40:18 -0700227 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700228 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700229
230 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700231 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700232 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700233 """
Simon Glass290a1802011-07-17 13:54:32 -0700234 if bootcmd:
Simon Glassb4447fd2011-07-26 11:18:25 +1200235 self.fdt.PutString('/config/bootcmd', bootcmd)
Doug Anderson86ce5f42011-07-27 10:40:18 -0700236 self.fdt.PutInteger('/config/bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700237 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700238
Simon Glass290a1802011-07-17 13:54:32 -0700239 def AddConfigList(self, config_list, use_int=False):
240 """Add a list of config items to the fdt.
241
242 Normally these values are written to the fdt as strings, but integers
243 are also supported, in which case the values will be converted to integers
244 (if necessary) before being stored.
245
246 Args:
247 config_list: List of (config, value) tuples to add to the fdt. For each
248 tuple:
249 config: The fdt node to write to will be /config/<config>.
250 value: An integer or string value to write.
251 use_int: True to only write integer values.
252
253 Raises:
254 CmdError: if a value is required to be converted to integer but can't be.
255 """
256 if config_list:
257 for config in config_list:
258 value = config[1]
259 if use_int:
260 try:
261 value = int(value)
262 except ValueError as str:
263 raise CmdError("Cannot convert config option '%s' to integer" %
264 value)
265 if type(value) == type(1):
266 self.fdt.PutInteger('/config/%s' % config[0], value)
267 else:
268 self.fdt.PutString('/config/%s' % config[0], value)
269
270 def _CreateBootStub(self, uboot, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700271 """Create a boot stub and a signed boot stub.
272
273 Args:
274 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass89b86b82011-07-17 23:49:49 -0700275 text_base: Address of text base for image.
276
277 Returns:
278 Tuple containing:
Simon Glass56577572011-07-19 11:08:06 +1200279 Full path to bootstub (uboot + fdt).
280 Full path to signed blob (uboot + fdt + bct).
Simon Glass89b86b82011-07-17 23:49:49 -0700281
282 Raises:
283 CmdError if a command fails.
284 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200285 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass290a1802011-07-17 13:54:32 -0700286 text_base = self.fdt.GetInt('/chromeos-config/textbase');
Simon Glass89b86b82011-07-17 23:49:49 -0700287 uboot_data = self._tools.ReadFile(uboot)
Simon Glass290a1802011-07-17 13:54:32 -0700288 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200289
Simon Glass89b86b82011-07-17 23:49:49 -0700290 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700291 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
292 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700293 self._tools.OutputSize('Combined binary', bootstub)
294
Simon Glasse13ee2c2011-07-28 08:12:28 +1200295 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700296 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700297 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200298 bootstub, text_base)
299 return bootstub, signed
Simon Glass89b86b82011-07-17 23:49:49 -0700300
301 def _PackOutput(self, msg):
302 """Helper function to write output from PackFirmware (verbose level 2).
303
304 This is passed to PackFirmware for it to use to write output.
305
306 Args:
307 msg: Message to display.
308 """
309 self._out.Notice(msg)
310
Simon Glass290a1802011-07-17 13:54:32 -0700311 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700312 """Create a full firmware image, along with various by-products.
313
314 This uses the provided u-boot.bin, fdt and bct to create a firmware
315 image containing all the required parts. If the GBB is not supplied
316 then this will just return a signed U-Boot as the image.
317
318 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200319 gbb: Full path to the GBB file, or empty if a GBB is not required.
320 fdt: Fdt object containing required information.
321
322 Returns:
323 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700324
325 Raises:
326 CmdError if a command fails.
327 """
Simon Glass290a1802011-07-17 13:54:32 -0700328 self._out.Notice("Model: %s" % fdt.GetString('/model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700329
330 # Create the boot stub, which is U-Boot plus an fdt and bct
Simon Glasse13ee2c2011-07-28 08:12:28 +1200331 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700332
333 if gbb:
334 pack = PackFirmware(self._tools, self._out)
335 image = os.path.join(self._tools.outdir, 'image.bin')
Hung-Te Lina7462e72011-07-27 19:17:10 +0800336 fwid = '.'.join([
337 re.sub('[ ,]+', '_', fdt.GetString('/model')),
338 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700339 self._out.Notice('Firmware ID: %s' % fwid)
340 pack.SetupFiles(boot=bootstub, signed=signed, gbb=gbb,
Simon Glass290a1802011-07-17 13:54:32 -0700341 fwid=fwid, keydir=self._keydir)
342 pack.SelectFdt(fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700343 pack.PackImage(self._tools.outdir, image)
344 else:
345 image = signed
346
347 self._tools.OutputSize('Final image', image)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200348 return image
Simon Glass89b86b82011-07-17 23:49:49 -0700349
Simon Glass290a1802011-07-17 13:54:32 -0700350 def SelectFdt(self, fdt_fname):
351 """Select an FDT to control the firmware bundling
352
353 Args:
354 fdt_fname: The filename of the fdt to use.
355
356 We make a copy of this which will include any on-the-fly changes we want
357 to make.
358 """
359 self._fdt_fname = fdt_fname
360 self.CheckOptions()
361 fdt = Fdt(self._tools, self._fdt_fname)
362 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
363
Simon Glass56577572011-07-19 11:08:06 +1200364 def Start(self, hardware_id, output_fname):
Simon Glass290a1802011-07-17 13:54:32 -0700365 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700366
367 - Checks options, tools, output directory, fdt.
368 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700369
370 Args:
Simon Glass56577572011-07-19 11:08:06 +1200371 hardware_id: Hardware ID to use for this board. If None, then the
372 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700373 output_fname: Output filename for the image. If this is not None, then
374 the final image will be copied here.
375
376 Returns:
377 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700378 """
Simon Glass89b86b82011-07-17 23:49:49 -0700379 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700380 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200381 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700382
383 # This creates the actual image.
Simon Glasse13ee2c2011-07-28 08:12:28 +1200384 image = self._CreateImage(gbb, self.fdt)
Simon Glass290a1802011-07-17 13:54:32 -0700385 if output_fname:
386 shutil.copyfile(image, output_fname)
387 self._out.Notice("Output image '%s'" % output_fname)
388 return image