blob: bfca292e791d47cfaef7f3df38d043d2252e237c [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
Stefan Reinauer8d79d362011-08-16 14:20:43 -070096 self.coreboot_fname = None # Filename of our coreboot binary.
Simon Glass290a1802011-07-17 13:54:32 -070097
98 def SetDirs(self, keydir):
99 """Set up directories required for Bundle.
100
101 Args:
102 keydir: Directory containing keys to use for signing firmware.
103 """
104 self._keydir = keydir
105
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700106 def SetFiles(self, board, bct, uboot=None, bmpblk=None, coreboot=None):
Simon Glass290a1802011-07-17 13:54:32 -0700107 """Set up files required for Bundle.
108
109 Args:
110 board: The name of the board to target (e.g. tegra2_seaboard).
111 uboot: The filename of the u-boot.bin image to use.
112 bct: The filename of the binary BCT file to use.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800113 bmpblk: The filename of bitmap block file to use.
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700114 coreboot: The filename of the coreboot image to use (on x86)
Simon Glass290a1802011-07-17 13:54:32 -0700115 """
116 self._board = board
117 self.uboot_fname = uboot
118 self.bct_fname = bct
Hung-Te Lin5b649382011-08-03 15:01:16 +0800119 self.bmpblk_fname = bmpblk
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700120 self.coreboot_fname = coreboot
Simon Glass290a1802011-07-17 13:54:32 -0700121
122 def SetOptions(self, small):
123 """Set up options supported by Bundle.
124
125 Args:
126 small: Only create a signed U-Boot - don't produce the full packed
127 firmware image. This is useful for devs who want to replace just the
128 U-Boot part while keeping the keys, gbb, etc. the same.
129 """
130 self._small = small
131
132 def CheckOptions(self):
133 """Check provided options and select defaults."""
134 if not self._board:
135 raise ValueError('No board defined - please define a board to use')
136 build_root = os.path.join('##', 'build', self._board, 'u-boot')
137 if not self._fdt_fname:
138 self._fdt_fname = os.path.join(build_root, 'dtb', '%s.dtb' %
139 re.sub('_', '-', self._board))
140 if not self.uboot_fname:
141 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
142 if not self.bct_fname:
143 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass89b86b82011-07-17 23:49:49 -0700144
Simon Glass56577572011-07-19 11:08:06 +1200145 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700146 """Create a GBB for the image.
147
Simon Glass56577572011-07-19 11:08:06 +1200148 Args:
149 hardware_id: Hardware ID to use for this board. If None, then the
150 default from the Fdt will be used
151
Simon Glass89b86b82011-07-17 23:49:49 -0700152 Returns:
153 Path of the created GBB file.
154
155 Raises:
156 CmdError if a command fails.
157 """
Simon Glass56577572011-07-19 11:08:06 +1200158 if not hardware_id:
159 hardware_id = self.fdt.GetString('/config/hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700160 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700161 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700162
Hung-Te Lin5b649382011-08-03 15:01:16 +0800163 if not self.bmpblk_fname:
164 # TODO(hungte) Remove this bitmap generation in future because we should
165 # always use pre-genereated bitmaps.
166 # Get LCD dimensions from the device tree.
167 screen_geometry = '%sx%s' % (self.fdt.GetInt('/lcd/width'),
168 self.fdt.GetInt('/lcd/height'))
Simon Glass89b86b82011-07-17 23:49:49 -0700169
Hung-Te Lin5b649382011-08-03 15:01:16 +0800170 # This is the magic directory that make_bmp_image writes to!
171 out_dir = 'out_%s' % re.sub(' ', '_', hardware_id)
172 bmp_dir = os.path.join(odir, out_dir)
173 self._out.Progress('Creating bitmaps')
174 self._tools.Run('make_bmp_image', [hardware_id, screen_geometry, 'arm'],
175 cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700176
Hung-Te Lin5b649382011-08-03 15:01:16 +0800177 self._out.Progress('Creating bitmap block')
178 yaml = 'config.yaml'
179 self._tools.WriteFile(os.path.join(bmp_dir, yaml), yaml_data)
180 self._tools.Run('bmpblk_utility', ['-z', '2', '-c', yaml, 'bmpblk.bin'],
181 cwd=bmp_dir)
182 self.bmpblk_fname = os.path.join(out_dir, 'bmpblk.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700183
184 self._out.Progress('Creating GBB')
185 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
186 sizes = ['%#x' % size for size in sizes]
187 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700188 keydir = self._tools.Filename(self._keydir)
189 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700190 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200191 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700192 '--rootkey=%s/root_key.vbpubk' % keydir,
193 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Hung-Te Lin5b649382011-08-03 15:01:16 +0800194 '--bmpfv=%s' % self.bmpblk_fname,
Simon Glass89b86b82011-07-17 23:49:49 -0700195 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700196 cwd=odir)
197 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700198
Simon Glasse13ee2c2011-07-28 08:12:28 +1200199 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700200 """Sign an image so that the Tegra SOC will boot it.
201
202 Args:
203 bct: BCT file to use.
204 bootstub: Boot stub (U-Boot + fdt) file to sign.
205 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700206
207 Returns:
208 filename of signed image.
209
210 Raises:
211 CmdError if a command fails.
212 """
213 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200214 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700215 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200216 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700217 fd = open(config, 'w')
218 fd.write('Version = 1;\n')
219 fd.write('Redundancy = 1;\n')
220 fd.write('Bctfile = %s;\n' % bct)
221 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
222 text_base))
223 fd.close()
224
225 self._tools.Run('cbootimage', [config, signed])
226 self._tools.OutputSize('BCT', bct)
227 self._tools.OutputSize('Signed image', signed)
228 return signed
229
Doug Anderson86ce5f42011-07-27 10:40:18 -0700230 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700231 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700232
233 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700234 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700235 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700236 """
Simon Glass290a1802011-07-17 13:54:32 -0700237 if bootcmd:
Simon Glassb4447fd2011-07-26 11:18:25 +1200238 self.fdt.PutString('/config/bootcmd', bootcmd)
Doug Anderson86ce5f42011-07-27 10:40:18 -0700239 self.fdt.PutInteger('/config/bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700240 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700241
Simon Glass290a1802011-07-17 13:54:32 -0700242 def AddConfigList(self, config_list, use_int=False):
243 """Add a list of config items to the fdt.
244
245 Normally these values are written to the fdt as strings, but integers
246 are also supported, in which case the values will be converted to integers
247 (if necessary) before being stored.
248
249 Args:
250 config_list: List of (config, value) tuples to add to the fdt. For each
251 tuple:
252 config: The fdt node to write to will be /config/<config>.
253 value: An integer or string value to write.
254 use_int: True to only write integer values.
255
256 Raises:
257 CmdError: if a value is required to be converted to integer but can't be.
258 """
259 if config_list:
260 for config in config_list:
261 value = config[1]
262 if use_int:
263 try:
264 value = int(value)
265 except ValueError as str:
266 raise CmdError("Cannot convert config option '%s' to integer" %
267 value)
268 if type(value) == type(1):
269 self.fdt.PutInteger('/config/%s' % config[0], value)
270 else:
271 self.fdt.PutString('/config/%s' % config[0], value)
272
273 def _CreateBootStub(self, uboot, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700274 """Create a boot stub and a signed boot stub.
275
276 Args:
277 uboot: Path to u-boot.bin (may be chroot-relative)
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700278 fdt: Device Tree
Simon Glass89b86b82011-07-17 23:49:49 -0700279
280 Returns:
281 Tuple containing:
Simon Glass56577572011-07-19 11:08:06 +1200282 Full path to bootstub (uboot + fdt).
283 Full path to signed blob (uboot + fdt + bct).
Simon Glass89b86b82011-07-17 23:49:49 -0700284
285 Raises:
286 CmdError if a command fails.
287 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200288 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass290a1802011-07-17 13:54:32 -0700289 text_base = self.fdt.GetInt('/chromeos-config/textbase');
Simon Glass89b86b82011-07-17 23:49:49 -0700290 uboot_data = self._tools.ReadFile(uboot)
Simon Glass290a1802011-07-17 13:54:32 -0700291 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200292
Simon Glass89b86b82011-07-17 23:49:49 -0700293 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700294 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
295 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700296 self._tools.OutputSize('Combined binary', bootstub)
297
Simon Glasse13ee2c2011-07-28 08:12:28 +1200298 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700299 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700300 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200301 bootstub, text_base)
302 return bootstub, signed
Simon Glass89b86b82011-07-17 23:49:49 -0700303
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700304 def _CreateCorebootStub(self, uboot, coreboot, fdt):
305 """Create a coreboot boot stub.
306
307 Args:
308 uboot: Path to u-boot.bin (may be chroot-relative)
309 coreboot: Path to coreboot.rom
310 fdt: Device Tree
311
312 Returns:
313 Full path to bootstub (coreboot + uboot + fdt).
314
315 Raises:
316 CmdError if a command fails.
317 """
318 bootstub = os.path.join(self._tools.outdir, 'coreboot-full.rom')
319 cbfstool = os.path.join(os.path.dirname(coreboot), "cbfstool")
320 uboot_elf = uboot.replace(".bin", ".elf")
321 shutil.copyfile(coreboot, bootstub)
322 self._tools.Run(cbfstool, [bootstub, 'add-payload', uboot_elf,
323 'fallback/payload', 'lzma'])
324 self._tools.Run(cbfstool, [bootstub, 'add', fdt.fname, 'u-boot.dtb',
325 '0xac'])
326 return bootstub
327
Simon Glass89b86b82011-07-17 23:49:49 -0700328 def _PackOutput(self, msg):
329 """Helper function to write output from PackFirmware (verbose level 2).
330
331 This is passed to PackFirmware for it to use to write output.
332
333 Args:
334 msg: Message to display.
335 """
336 self._out.Notice(msg)
337
Simon Glass290a1802011-07-17 13:54:32 -0700338 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700339 """Create a full firmware image, along with various by-products.
340
341 This uses the provided u-boot.bin, fdt and bct to create a firmware
342 image containing all the required parts. If the GBB is not supplied
343 then this will just return a signed U-Boot as the image.
344
345 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200346 gbb: Full path to the GBB file, or empty if a GBB is not required.
347 fdt: Fdt object containing required information.
348
349 Returns:
350 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700351
352 Raises:
353 CmdError if a command fails.
354 """
Simon Glass290a1802011-07-17 13:54:32 -0700355 self._out.Notice("Model: %s" % fdt.GetString('/model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700356
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700357 if self.coreboot_fname:
358 # FIXME(reinauer) the names are not too great choices.
359 # signed gets packed into the bootstub, and bootstub gets
360 # packed into the RW sections.
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700361 signed = self._CreateCorebootStub(self.uboot_fname,
362 self.coreboot_fname, fdt)
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700363 bootstub = self.uboot_fname
364 else:
365 # Create the boot stub, which is U-Boot plus an fdt and bct
366 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700367
368 if gbb:
369 pack = PackFirmware(self._tools, self._out)
370 image = os.path.join(self._tools.outdir, 'image.bin')
Hung-Te Lina7462e72011-07-27 19:17:10 +0800371 fwid = '.'.join([
372 re.sub('[ ,]+', '_', fdt.GetString('/model')),
373 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700374 self._out.Notice('Firmware ID: %s' % fwid)
375 pack.SetupFiles(boot=bootstub, signed=signed, gbb=gbb,
Simon Glass290a1802011-07-17 13:54:32 -0700376 fwid=fwid, keydir=self._keydir)
377 pack.SelectFdt(fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700378 pack.PackImage(self._tools.outdir, image)
379 else:
380 image = signed
381
382 self._tools.OutputSize('Final image', image)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200383 return image
Simon Glass89b86b82011-07-17 23:49:49 -0700384
Simon Glass290a1802011-07-17 13:54:32 -0700385 def SelectFdt(self, fdt_fname):
386 """Select an FDT to control the firmware bundling
387
388 Args:
389 fdt_fname: The filename of the fdt to use.
390
Simon Glassc0f3dc62011-08-09 14:19:05 -0700391 Returns:
392 The Fdt object of the original fdt file, which we will not modify.
393
Simon Glass290a1802011-07-17 13:54:32 -0700394 We make a copy of this which will include any on-the-fly changes we want
395 to make.
396 """
397 self._fdt_fname = fdt_fname
398 self.CheckOptions()
399 fdt = Fdt(self._tools, self._fdt_fname)
400 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700401 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700402
Simon Glass56577572011-07-19 11:08:06 +1200403 def Start(self, hardware_id, output_fname):
Simon Glass290a1802011-07-17 13:54:32 -0700404 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700405
406 - Checks options, tools, output directory, fdt.
407 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700408
409 Args:
Simon Glass56577572011-07-19 11:08:06 +1200410 hardware_id: Hardware ID to use for this board. If None, then the
411 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700412 output_fname: Output filename for the image. If this is not None, then
413 the final image will be copied here.
414
415 Returns:
416 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700417 """
Simon Glass89b86b82011-07-17 23:49:49 -0700418 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700419 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200420 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700421
422 # This creates the actual image.
Simon Glasse13ee2c2011-07-28 08:12:28 +1200423 image = self._CreateImage(gbb, self.fdt)
Simon Glass290a1802011-07-17 13:54:32 -0700424 if output_fname:
425 shutil.copyfile(image, output_fname)
426 self._out.Notice("Output image '%s'" % output_fname)
427 return image