blob: 3ce8c73d865386d3c2e8d849339f6c4b79404213 [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.
95
96 def SetDirs(self, keydir):
97 """Set up directories required for Bundle.
98
99 Args:
100 keydir: Directory containing keys to use for signing firmware.
101 """
102 self._keydir = keydir
103
104 def SetFiles(self, board, uboot, bct):
105 """Set up files required for Bundle.
106
107 Args:
108 board: The name of the board to target (e.g. tegra2_seaboard).
109 uboot: The filename of the u-boot.bin image to use.
110 bct: The filename of the binary BCT file to use.
111 """
112 self._board = board
113 self.uboot_fname = uboot
114 self.bct_fname = bct
115
116 def SetOptions(self, small):
117 """Set up options supported by Bundle.
118
119 Args:
120 small: Only create a signed U-Boot - don't produce the full packed
121 firmware image. This is useful for devs who want to replace just the
122 U-Boot part while keeping the keys, gbb, etc. the same.
123 """
124 self._small = small
125
126 def CheckOptions(self):
127 """Check provided options and select defaults."""
128 if not self._board:
129 raise ValueError('No board defined - please define a board to use')
130 build_root = os.path.join('##', 'build', self._board, 'u-boot')
131 if not self._fdt_fname:
132 self._fdt_fname = os.path.join(build_root, 'dtb', '%s.dtb' %
133 re.sub('_', '-', self._board))
134 if not self.uboot_fname:
135 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
136 if not self.bct_fname:
137 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass89b86b82011-07-17 23:49:49 -0700138
Simon Glass56577572011-07-19 11:08:06 +1200139 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700140 """Create a GBB for the image.
141
Simon Glass56577572011-07-19 11:08:06 +1200142 Args:
143 hardware_id: Hardware ID to use for this board. If None, then the
144 default from the Fdt will be used
145
Simon Glass89b86b82011-07-17 23:49:49 -0700146 Returns:
147 Path of the created GBB file.
148
149 Raises:
150 CmdError if a command fails.
151 """
Simon Glass56577572011-07-19 11:08:06 +1200152 if not hardware_id:
153 hardware_id = self.fdt.GetString('/config/hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700154 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700155 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700156
157 # Get LCD dimensions from the device tree.
158 screen_geometry = '%sx%s' % (self.fdt.GetInt('/lcd/width'),
159 self.fdt.GetInt('/lcd/height'))
160
161 # This is the magic directory that make_bmp_image writes to!
Simon Glass56577572011-07-19 11:08:06 +1200162 out_dir = 'out_%s' % re.sub(' ', '_', hardware_id)
Simon Glass290a1802011-07-17 13:54:32 -0700163 bmp_dir = os.path.join(odir, out_dir)
Simon Glass89b86b82011-07-17 23:49:49 -0700164 self._out.Progress('Creating bitmaps')
Simon Glass56577572011-07-19 11:08:06 +1200165 self._tools.Run('make_bmp_image', [hardware_id, screen_geometry, 'arm'],
166 cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700167
168 self._out.Progress('Creating bitmap block')
169 yaml = 'config.yaml'
170 self._tools.WriteFile(os.path.join(bmp_dir, yaml), yaml_data)
171 self._tools.Run('bmpblk_utility', ['-z', '2', '-c', yaml, 'bmpblk.bin'],
172 cwd=bmp_dir)
173
174 self._out.Progress('Creating GBB')
175 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
176 sizes = ['%#x' % size for size in sizes]
177 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700178 keydir = self._tools.Filename(self._keydir)
179 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700180 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200181 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700182 '--rootkey=%s/root_key.vbpubk' % keydir,
183 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
184 '--bmpfv=%s' % os.path.join(out_dir, 'bmpblk.bin'),
185 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700186 cwd=odir)
187 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700188
Simon Glasse13ee2c2011-07-28 08:12:28 +1200189 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700190 """Sign an image so that the Tegra SOC will boot it.
191
192 Args:
193 bct: BCT file to use.
194 bootstub: Boot stub (U-Boot + fdt) file to sign.
195 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700196
197 Returns:
198 filename of signed image.
199
200 Raises:
201 CmdError if a command fails.
202 """
203 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200204 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700205 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200206 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700207 fd = open(config, 'w')
208 fd.write('Version = 1;\n')
209 fd.write('Redundancy = 1;\n')
210 fd.write('Bctfile = %s;\n' % bct)
211 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
212 text_base))
213 fd.close()
214
215 self._tools.Run('cbootimage', [config, signed])
216 self._tools.OutputSize('BCT', bct)
217 self._tools.OutputSize('Signed image', signed)
218 return signed
219
Doug Anderson86ce5f42011-07-27 10:40:18 -0700220 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700221 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700222
223 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700224 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700225 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700226 """
Simon Glass290a1802011-07-17 13:54:32 -0700227 if bootcmd:
Simon Glassb4447fd2011-07-26 11:18:25 +1200228 self.fdt.PutString('/config/bootcmd', bootcmd)
Doug Anderson86ce5f42011-07-27 10:40:18 -0700229 self.fdt.PutInteger('/config/bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700230 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700231
Simon Glass290a1802011-07-17 13:54:32 -0700232 def AddConfigList(self, config_list, use_int=False):
233 """Add a list of config items to the fdt.
234
235 Normally these values are written to the fdt as strings, but integers
236 are also supported, in which case the values will be converted to integers
237 (if necessary) before being stored.
238
239 Args:
240 config_list: List of (config, value) tuples to add to the fdt. For each
241 tuple:
242 config: The fdt node to write to will be /config/<config>.
243 value: An integer or string value to write.
244 use_int: True to only write integer values.
245
246 Raises:
247 CmdError: if a value is required to be converted to integer but can't be.
248 """
249 if config_list:
250 for config in config_list:
251 value = config[1]
252 if use_int:
253 try:
254 value = int(value)
255 except ValueError as str:
256 raise CmdError("Cannot convert config option '%s' to integer" %
257 value)
258 if type(value) == type(1):
259 self.fdt.PutInteger('/config/%s' % config[0], value)
260 else:
261 self.fdt.PutString('/config/%s' % config[0], value)
262
263 def _CreateBootStub(self, uboot, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700264 """Create a boot stub and a signed boot stub.
265
266 Args:
267 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass89b86b82011-07-17 23:49:49 -0700268 text_base: Address of text base for image.
269
270 Returns:
271 Tuple containing:
Simon Glass56577572011-07-19 11:08:06 +1200272 Full path to bootstub (uboot + fdt).
273 Full path to signed blob (uboot + fdt + bct).
Simon Glass89b86b82011-07-17 23:49:49 -0700274
275 Raises:
276 CmdError if a command fails.
277 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200278 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass290a1802011-07-17 13:54:32 -0700279 text_base = self.fdt.GetInt('/chromeos-config/textbase');
Simon Glass89b86b82011-07-17 23:49:49 -0700280 uboot_data = self._tools.ReadFile(uboot)
Simon Glass290a1802011-07-17 13:54:32 -0700281 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200282
Simon Glass89b86b82011-07-17 23:49:49 -0700283 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700284 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
285 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700286 self._tools.OutputSize('Combined binary', bootstub)
287
Simon Glasse13ee2c2011-07-28 08:12:28 +1200288 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700289 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700290 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200291 bootstub, text_base)
292 return bootstub, signed
Simon Glass89b86b82011-07-17 23:49:49 -0700293
294 def _PackOutput(self, msg):
295 """Helper function to write output from PackFirmware (verbose level 2).
296
297 This is passed to PackFirmware for it to use to write output.
298
299 Args:
300 msg: Message to display.
301 """
302 self._out.Notice(msg)
303
Simon Glass290a1802011-07-17 13:54:32 -0700304 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700305 """Create a full firmware image, along with various by-products.
306
307 This uses the provided u-boot.bin, fdt and bct to create a firmware
308 image containing all the required parts. If the GBB is not supplied
309 then this will just return a signed U-Boot as the image.
310
311 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200312 gbb: Full path to the GBB file, or empty if a GBB is not required.
313 fdt: Fdt object containing required information.
314
315 Returns:
316 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700317
318 Raises:
319 CmdError if a command fails.
320 """
Simon Glass290a1802011-07-17 13:54:32 -0700321 self._out.Notice("Model: %s" % fdt.GetString('/model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700322
323 # Create the boot stub, which is U-Boot plus an fdt and bct
Simon Glasse13ee2c2011-07-28 08:12:28 +1200324 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700325
326 if gbb:
327 pack = PackFirmware(self._tools, self._out)
328 image = os.path.join(self._tools.outdir, 'image.bin')
Hung-Te Lina7462e72011-07-27 19:17:10 +0800329 fwid = '.'.join([
330 re.sub('[ ,]+', '_', fdt.GetString('/model')),
331 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700332 self._out.Notice('Firmware ID: %s' % fwid)
333 pack.SetupFiles(boot=bootstub, signed=signed, gbb=gbb,
Simon Glass290a1802011-07-17 13:54:32 -0700334 fwid=fwid, keydir=self._keydir)
335 pack.SelectFdt(fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700336 pack.PackImage(self._tools.outdir, image)
337 else:
338 image = signed
339
340 self._tools.OutputSize('Final image', image)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200341 return image
Simon Glass89b86b82011-07-17 23:49:49 -0700342
Simon Glass290a1802011-07-17 13:54:32 -0700343 def SelectFdt(self, fdt_fname):
344 """Select an FDT to control the firmware bundling
345
346 Args:
347 fdt_fname: The filename of the fdt to use.
348
349 We make a copy of this which will include any on-the-fly changes we want
350 to make.
351 """
352 self._fdt_fname = fdt_fname
353 self.CheckOptions()
354 fdt = Fdt(self._tools, self._fdt_fname)
355 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
356
Simon Glass56577572011-07-19 11:08:06 +1200357 def Start(self, hardware_id, output_fname):
Simon Glass290a1802011-07-17 13:54:32 -0700358 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700359
360 - Checks options, tools, output directory, fdt.
361 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700362
363 Args:
Simon Glass56577572011-07-19 11:08:06 +1200364 hardware_id: Hardware ID to use for this board. If None, then the
365 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700366 output_fname: Output filename for the image. If this is not None, then
367 the final image will be copied here.
368
369 Returns:
370 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700371 """
Simon Glass89b86b82011-07-17 23:49:49 -0700372 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700373 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200374 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700375
376 # This creates the actual image.
Simon Glasse13ee2c2011-07-28 08:12:28 +1200377 image = self._CreateImage(gbb, self.fdt)
Simon Glass290a1802011-07-17 13:54:32 -0700378 if output_fname:
379 shutil.copyfile(image, output_fname)
380 self._out.Notice("Output image '%s'" % output_fname)
381 return image