blob: 209d188e8f8a745871ab6667d64534e1e27675fe [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
139 def _CreateGoogleBinaryBlock(self):
140 """Create a GBB for the image.
141
142 Returns:
143 Path of the created GBB file.
144
145 Raises:
146 CmdError if a command fails.
147 """
148 hwid = self.fdt.GetString('/config/hwid')
149 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700150 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700151
152 # Get LCD dimensions from the device tree.
153 screen_geometry = '%sx%s' % (self.fdt.GetInt('/lcd/width'),
154 self.fdt.GetInt('/lcd/height'))
155
156 # This is the magic directory that make_bmp_image writes to!
157 out_dir = 'out_%s' % re.sub(' ', '_', hwid)
Simon Glass290a1802011-07-17 13:54:32 -0700158 bmp_dir = os.path.join(odir, out_dir)
Simon Glass89b86b82011-07-17 23:49:49 -0700159 self._out.Progress('Creating bitmaps')
Simon Glass290a1802011-07-17 13:54:32 -0700160 self._tools.Run('make_bmp_image', [hwid, screen_geometry, 'arm'], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700161
162 self._out.Progress('Creating bitmap block')
163 yaml = 'config.yaml'
164 self._tools.WriteFile(os.path.join(bmp_dir, yaml), yaml_data)
165 self._tools.Run('bmpblk_utility', ['-z', '2', '-c', yaml, 'bmpblk.bin'],
166 cwd=bmp_dir)
167
168 self._out.Progress('Creating GBB')
169 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
170 sizes = ['%#x' % size for size in sizes]
171 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700172 keydir = self._tools.Filename(self._keydir)
173 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700174 self._tools.Run('gbb_utility', ['-s',
175 '--hwid=%s' % hwid,
176 '--rootkey=%s/root_key.vbpubk' % keydir,
177 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
178 '--bmpfv=%s' % os.path.join(out_dir, 'bmpblk.bin'),
179 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700180 cwd=odir)
181 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700182
183 def _SignBootstub(self, bct, bootstub, text_base, name):
184 """Sign an image so that the Tegra SOC will boot it.
185
186 Args:
187 bct: BCT file to use.
188 bootstub: Boot stub (U-Boot + fdt) file to sign.
189 text_base: Address of text base for image.
190 name: root of basename to use for signed image.
191
192 Returns:
193 filename of signed image.
194
195 Raises:
196 CmdError if a command fails.
197 """
198 # First create a config file - this is how we instruct cbootimage
199 signed = os.path.join(self._tools.outdir, 'signed%s.bin' % name)
200 self._out.Progress('Signing Bootstub')
201 config = os.path.join(self._tools.outdir, 'boot%s.cfg' % name)
202 fd = open(config, 'w')
203 fd.write('Version = 1;\n')
204 fd.write('Redundancy = 1;\n')
205 fd.write('Bctfile = %s;\n' % bct)
206 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
207 text_base))
208 fd.close()
209
210 self._tools.Run('cbootimage', [config, signed])
211 self._tools.OutputSize('BCT', bct)
212 self._tools.OutputSize('Signed image', signed)
213 return signed
214
Simon Glass290a1802011-07-17 13:54:32 -0700215 def SetBootcmd(self, bootcmd):
216 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700217
218 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700219 bootcmd: Boot command to use, as a string (if None this this is a nop).
Simon Glass89b86b82011-07-17 23:49:49 -0700220 """
Simon Glass290a1802011-07-17 13:54:32 -0700221 if bootcmd:
Simon Glassb4447fd2011-07-26 11:18:25 +1200222 self.fdt.PutString('/config/bootcmd', bootcmd)
Simon Glass290a1802011-07-17 13:54:32 -0700223 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700224
Simon Glass290a1802011-07-17 13:54:32 -0700225 def AddConfigList(self, config_list, use_int=False):
226 """Add a list of config items to the fdt.
227
228 Normally these values are written to the fdt as strings, but integers
229 are also supported, in which case the values will be converted to integers
230 (if necessary) before being stored.
231
232 Args:
233 config_list: List of (config, value) tuples to add to the fdt. For each
234 tuple:
235 config: The fdt node to write to will be /config/<config>.
236 value: An integer or string value to write.
237 use_int: True to only write integer values.
238
239 Raises:
240 CmdError: if a value is required to be converted to integer but can't be.
241 """
242 if config_list:
243 for config in config_list:
244 value = config[1]
245 if use_int:
246 try:
247 value = int(value)
248 except ValueError as str:
249 raise CmdError("Cannot convert config option '%s' to integer" %
250 value)
251 if type(value) == type(1):
252 self.fdt.PutInteger('/config/%s' % config[0], value)
253 else:
254 self.fdt.PutString('/config/%s' % config[0], value)
255
256 def _CreateBootStub(self, uboot, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700257 """Create a boot stub and a signed boot stub.
258
259 Args:
260 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass89b86b82011-07-17 23:49:49 -0700261 text_base: Address of text base for image.
262
263 Returns:
264 Tuple containing:
265 Full path to u-boot.bin.
266 Full path to bootstub.
267
268 Raises:
269 CmdError if a command fails.
270 """
Simon Glass290a1802011-07-17 13:54:32 -0700271 text_base = self.fdt.GetInt('/chromeos-config/textbase');
Simon Glass89b86b82011-07-17 23:49:49 -0700272 uboot_data = self._tools.ReadFile(uboot)
Simon Glass290a1802011-07-17 13:54:32 -0700273 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700274 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
275 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700276 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
277 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700278 self._tools.OutputSize('Combined binary', bootstub)
279
280 # sign the bootstub; this is a combination of the board specific
281 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700282 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
283 bootstub, text_base, '')
Simon Glass89b86b82011-07-17 23:49:49 -0700284 return self._tools.Filename(uboot), bootstub, signed
285
286 def _PackOutput(self, msg):
287 """Helper function to write output from PackFirmware (verbose level 2).
288
289 This is passed to PackFirmware for it to use to write output.
290
291 Args:
292 msg: Message to display.
293 """
294 self._out.Notice(msg)
295
Simon Glass290a1802011-07-17 13:54:32 -0700296 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700297 """Create a full firmware image, along with various by-products.
298
299 This uses the provided u-boot.bin, fdt and bct to create a firmware
300 image containing all the required parts. If the GBB is not supplied
301 then this will just return a signed U-Boot as the image.
302
303 Args:
304 gbb Full path to the GBB file, or empty if a GBB is not required.
Simon Glass89b86b82011-07-17 23:49:49 -0700305
306 Raises:
307 CmdError if a command fails.
308 """
Simon Glass290a1802011-07-17 13:54:32 -0700309 self._out.Notice("Model: %s" % fdt.GetString('/model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700310
311 # Create the boot stub, which is U-Boot plus an fdt and bct
Simon Glass290a1802011-07-17 13:54:32 -0700312 uboot, bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700313
314 if gbb:
315 pack = PackFirmware(self._tools, self._out)
316 image = os.path.join(self._tools.outdir, 'image.bin')
317 fwid = self._tools.GetChromeosVersion()
318 self._out.Notice('Firmware ID: %s' % fwid)
319 pack.SetupFiles(boot=bootstub, signed=signed, gbb=gbb,
Simon Glass290a1802011-07-17 13:54:32 -0700320 fwid=fwid, keydir=self._keydir)
321 pack.SelectFdt(fdt)
Simon Glass89b86b82011-07-17 23:49:49 -0700322 pack.PackImage(self._tools.outdir, image)
323 else:
324 image = signed
325
326 self._tools.OutputSize('Final image', image)
327 return uboot, image
328
Simon Glass290a1802011-07-17 13:54:32 -0700329 def SelectFdt(self, fdt_fname):
330 """Select an FDT to control the firmware bundling
331
332 Args:
333 fdt_fname: The filename of the fdt to use.
334
335 We make a copy of this which will include any on-the-fly changes we want
336 to make.
337 """
338 self._fdt_fname = fdt_fname
339 self.CheckOptions()
340 fdt = Fdt(self._tools, self._fdt_fname)
341 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
342
343 def Start(self, output_fname):
344 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700345
346 - Checks options, tools, output directory, fdt.
347 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700348
349 Args:
350 output_fname: Output filename for the image. If this is not None, then
351 the final image will be copied here.
352
353 Returns:
354 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700355 """
Simon Glass89b86b82011-07-17 23:49:49 -0700356 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700357 if not self._small:
Simon Glass89b86b82011-07-17 23:49:49 -0700358 gbb = self._CreateGoogleBinaryBlock()
359
360 # This creates the actual image.
Simon Glass290a1802011-07-17 13:54:32 -0700361 uboot, image = self._CreateImage(gbb, self.fdt)
362 if output_fname:
363 shutil.copyfile(image, output_fname)
364 self._out.Notice("Output image '%s'" % output_fname)
365 return image