blob: d153b693568b618b7167f44499a638f2cb0da971 [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
Simon Glassceff3ff2012-04-04 11:23:45 -070019import glob
Simon Glass89b86b82011-07-17 23:49:49 -070020import os
21import re
22
23import cros_output
24from fdt import Fdt
25from pack_firmware import PackFirmware
26import shutil
Simon Glass7c2d5572011-11-15 14:47:08 -080027import struct
Simon Glass89b86b82011-07-17 23:49:49 -070028import tempfile
Simon Glass439fe7a2012-03-09 16:19:34 -080029from tools import CmdError
Simon Glass89b86b82011-07-17 23:49:49 -070030from tools import Tools
31from write_firmware import WriteFirmware
32
33# This data is required by bmpblk_utility. Does it ever change?
34# It was stored with the chromeos-bootimage ebuild, but we want
35# this utility to work outside the chroot.
36yaml_data = '''
37bmpblock: 1.0
38
39images:
40 devmode: DeveloperBmp/DeveloperBmp.bmp
41 recovery: RecoveryBmp/RecoveryBmp.bmp
42 rec_yuck: RecoveryNoOSBmp/RecoveryNoOSBmp.bmp
43 rec_insert: RecoveryMissingOSBmp/RecoveryMissingOSBmp.bmp
44
45screens:
46 dev_en:
47 - [0, 0, devmode]
48 rec_en:
49 - [0, 0, recovery]
50 yuck_en:
51 - [0, 0, rec_yuck]
52 ins_en:
53 - [0, 0, rec_insert]
54
55localizations:
56 - [ dev_en, rec_en, yuck_en, ins_en ]
57'''
58
59class Bundle:
Simon Glass290a1802011-07-17 13:54:32 -070060 """This class encapsulates the entire bundle firmware logic.
Simon Glass89b86b82011-07-17 23:49:49 -070061
Simon Glass290a1802011-07-17 13:54:32 -070062 Sequence of events:
63 bundle = Bundle(tools.Tools(), cros_output.Output())
64 bundle.SetDirs(...)
65 bundle.SetFiles(...)
66 bundle.SetOptions(...)
67 bundle.SelectFdt(fdt.Fdt('filename.dtb')
Simon Glassa4934b72012-05-09 13:35:02 -070068 .. can call bundle.AddConfigList(), AddEnableList() if required
Simon Glass290a1802011-07-17 13:54:32 -070069 bundle.Start(...)
Simon Glass89b86b82011-07-17 23:49:49 -070070
Simon Glass290a1802011-07-17 13:54:32 -070071 Public properties:
72 fdt: The fdt object that we use for building our image. This wil be the
73 one specified by the user, except that we might add config options
74 to it. This is set up by SelectFdt() which must be called before
75 bundling starts.
76 uboot_fname: Full filename of the U-Boot binary we use.
77 bct_fname: Full filename of the BCT file we use.
78 """
Simon Glass89b86b82011-07-17 23:49:49 -070079
Simon Glass290a1802011-07-17 13:54:32 -070080 def __init__(self, tools, output):
81 """Set up a new Bundle object.
Simon Glass89b86b82011-07-17 23:49:49 -070082
Simon Glass290a1802011-07-17 13:54:32 -070083 Args:
84 tools: A tools.Tools object to use for external tools.
85 output: A cros_output.Output object to use for program output.
Simon Glass89b86b82011-07-17 23:49:49 -070086 """
Simon Glass290a1802011-07-17 13:54:32 -070087 self._tools = tools
88 self._out = output
89
90 # Set up the things we need to know in order to operate.
91 self._board = None # Board name, e.g. tegra2_seaboard.
92 self._fdt_fname = None # Filename of our FDT.
93 self.uboot_fname = None # Filename of our U-Boot binary.
94 self.bct_fname = None # Filename of our BCT file.
95 self.fdt = None # Our Fdt object.
Hung-Te Lin5b649382011-08-03 15:01:16 +080096 self.bmpblk_fname = None # Filename of our Bitmap Block
Stefan Reinauer8d79d362011-08-16 14:20:43 -070097 self.coreboot_fname = None # Filename of our coreboot binary.
Vincent Palatinf7286772011-10-12 14:31:53 -070098 self.seabios_fname = None # Filename of our SeaBIOS payload.
Simon Glass7e199222012-03-13 15:51:18 -070099 self.exynos_bl1 = None # Filename of Exynos BL1 (pre-boot)
100 self.exynos_bl2 = None # Filename of Exynos BL2 (SPL)
Simon Glass290a1802011-07-17 13:54:32 -0700101
102 def SetDirs(self, keydir):
103 """Set up directories required for Bundle.
104
105 Args:
106 keydir: Directory containing keys to use for signing firmware.
107 """
108 self._keydir = keydir
109
Simon Glass6dcc2f22011-07-28 15:26:49 +1200110 def SetFiles(self, board, bct, uboot=None, bmpblk=None, coreboot=None,
Simon Glass7e199222012-03-13 15:51:18 -0700111 postload=None, seabios=None, exynos_bl1=None, exynos_bl2=None):
Simon Glass290a1802011-07-17 13:54:32 -0700112 """Set up files required for Bundle.
113
114 Args:
115 board: The name of the board to target (e.g. tegra2_seaboard).
116 uboot: The filename of the u-boot.bin image to use.
117 bct: The filename of the binary BCT file to use.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800118 bmpblk: The filename of bitmap block file to use.
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700119 coreboot: The filename of the coreboot image to use (on x86)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200120 postload: The filename of the u-boot-post.bin image to use.
Vincent Palatinf7286772011-10-12 14:31:53 -0700121 seabios: The filename of the SeaBIOS payload to use if any.
Simon Glass290a1802011-07-17 13:54:32 -0700122 """
123 self._board = board
124 self.uboot_fname = uboot
125 self.bct_fname = bct
Hung-Te Lin5b649382011-08-03 15:01:16 +0800126 self.bmpblk_fname = bmpblk
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700127 self.coreboot_fname = coreboot
Simon Glass6dcc2f22011-07-28 15:26:49 +1200128 self.postload_fname = postload
Vincent Palatinf7286772011-10-12 14:31:53 -0700129 self.seabios_fname = seabios
Simon Glass7e199222012-03-13 15:51:18 -0700130 self.exynos_bl1 = exynos_bl1
131 self.exynos_bl2 = exynos_bl2
Simon Glass290a1802011-07-17 13:54:32 -0700132
133 def SetOptions(self, small):
134 """Set up options supported by Bundle.
135
136 Args:
137 small: Only create a signed U-Boot - don't produce the full packed
138 firmware image. This is useful for devs who want to replace just the
139 U-Boot part while keeping the keys, gbb, etc. the same.
140 """
141 self._small = small
142
143 def CheckOptions(self):
144 """Check provided options and select defaults."""
145 if not self._board:
146 raise ValueError('No board defined - please define a board to use')
Simon Glass493163b2011-09-14 11:19:57 -0700147 build_root = os.path.join('##', 'build', self._board, 'firmware')
Simon Glass881964d2012-04-04 11:34:09 -0700148 dir_name = os.path.join(build_root, 'dts')
Simon Glass290a1802011-07-17 13:54:32 -0700149 if not self._fdt_fname:
Simon Glassceff3ff2012-04-04 11:23:45 -0700150 # Figure out where the file should be, and the name we expect.
Simon Glassceff3ff2012-04-04 11:23:45 -0700151 base_name = re.sub('_', '-', self._board)
152
153 # In case the name exists with a prefix or suffix, find it.
154 wildcard = os.path.join(dir_name, '*%s*.dts' % base_name)
155 found_list = glob.glob(self._tools.Filename(wildcard))
156 if len(found_list) == 1:
157 self._fdt_fname = found_list[0]
158 else:
159 # We didn't find anything definite, so set up our expected name.
160 self._fdt_fname = os.path.join(dir_name, '%s.dts' % base_name)
161
Simon Glass881964d2012-04-04 11:34:09 -0700162 # Convert things like 'exynos5250-daisy' into a full path.
163 root, ext = os.path.splitext(self._fdt_fname)
164 if not ext and not os.path.dirname(root):
165 self._fdt_fname = os.path.join(dir_name, '%s.dts' % root)
166
Simon Glass290a1802011-07-17 13:54:32 -0700167 if not self.uboot_fname:
168 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
169 if not self.bct_fname:
170 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass2a7f0b32011-08-26 11:25:17 -0700171 if not self.bmpblk_fname:
172 self.bmpblk_fname = os.path.join(build_root, 'default.bmpblk')
Simon Glass1d376832012-03-15 20:50:54 -0700173 if not self.exynos_bl1:
174 self.exynos_bl1 = os.path.join(build_root, 'E5250.nbl1.bin')
175 if not self.exynos_bl2:
176 self.exynos_bl2 = os.path.join(build_root, 'smdk5250-spl.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700177
Simon Glass75759302012-03-15 20:26:53 -0700178 def GetFiles(self):
179 """Get a list of files that we know about.
180
181 This is the opposite of SetFiles except that we may have put in some
182 default names. It returns a dictionary containing the filename for
183 each of a number of pre-defined files.
184
185 Returns:
186 Dictionary, with one entry for each file.
187 """
188 file_list = {
189 'bct' : self.bct_fname,
190 'exynos-bl1' : self.exynos_bl1,
191 'exynos-bl2' : self.exynos_bl2,
192 }
193 return file_list
194
Simon Glass56577572011-07-19 11:08:06 +1200195 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700196 """Create a GBB for the image.
197
Simon Glass56577572011-07-19 11:08:06 +1200198 Args:
199 hardware_id: Hardware ID to use for this board. If None, then the
200 default from the Fdt will be used
201
Simon Glass89b86b82011-07-17 23:49:49 -0700202 Returns:
203 Path of the created GBB file.
204
205 Raises:
206 CmdError if a command fails.
207 """
Simon Glass56577572011-07-19 11:08:06 +1200208 if not hardware_id:
Simon Glass02d124a2012-03-02 14:47:20 -0800209 hardware_id = self.fdt.GetString('/config', 'hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700210 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700211 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700212
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800213 chromeos_config = self.fdt.GetProps("/chromeos-config")
214 if 'fast-developer-mode' not in chromeos_config:
215 gbb_flags = 0
216 else:
217 self._out.Notice("Enabling fast-developer-mode.")
218 gbb_flags = 1
219
Simon Glass89b86b82011-07-17 23:49:49 -0700220 self._out.Progress('Creating GBB')
221 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
222 sizes = ['%#x' % size for size in sizes]
223 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700224 keydir = self._tools.Filename(self._keydir)
225 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700226 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200227 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700228 '--rootkey=%s/root_key.vbpubk' % keydir,
229 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Simon Glass2a7f0b32011-08-26 11:25:17 -0700230 '--bmpfv=%s' % self._tools.Filename(self.bmpblk_fname),
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800231 '--flags=%d' % gbb_flags,
Simon Glass89b86b82011-07-17 23:49:49 -0700232 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700233 cwd=odir)
234 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700235
Simon Glasse13ee2c2011-07-28 08:12:28 +1200236 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700237 """Sign an image so that the Tegra SOC will boot it.
238
239 Args:
240 bct: BCT file to use.
241 bootstub: Boot stub (U-Boot + fdt) file to sign.
242 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700243
244 Returns:
245 filename of signed image.
246
247 Raises:
248 CmdError if a command fails.
249 """
250 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200251 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700252 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200253 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700254 fd = open(config, 'w')
255 fd.write('Version = 1;\n')
256 fd.write('Redundancy = 1;\n')
257 fd.write('Bctfile = %s;\n' % bct)
Doug Anderson0eeb0742011-09-15 18:11:40 -0700258
259 # TODO(dianders): Right now, we don't have enough space in our flash map
260 # for two copies of the BCT when we're using NAND, so hack it to 1. Not
261 # sure what this does for reliability, but at least things will fit...
262 is_nand = "NvBootDevType_Nand" in self._tools.Run('bct_dump', [bct])
263 if is_nand:
264 fd.write('Bctcopy = 1;\n')
265
Simon Glass89b86b82011-07-17 23:49:49 -0700266 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
267 text_base))
Doug Anderson0eeb0742011-09-15 18:11:40 -0700268
Simon Glass89b86b82011-07-17 23:49:49 -0700269 fd.close()
270
271 self._tools.Run('cbootimage', [config, signed])
272 self._tools.OutputSize('BCT', bct)
273 self._tools.OutputSize('Signed image', signed)
274 return signed
275
Doug Anderson86ce5f42011-07-27 10:40:18 -0700276 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700277 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700278
279 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700280 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700281 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700282 """
Simon Glass290a1802011-07-17 13:54:32 -0700283 if bootcmd:
Simon Glass02d124a2012-03-02 14:47:20 -0800284 self.fdt.PutString('/config', 'bootcmd', bootcmd)
285 self.fdt.PutInteger('/config', 'bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700286 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700287
Simon Glassa4934b72012-05-09 13:35:02 -0700288 def SetNodeEnabled(self, node_name, enabled):
289 """Set whether an node is enabled or disabled.
290
291 This simply sets the 'status' property of a node to "ok", or "disabled".
292
293 The node should either be a full path to the node (like '/uart@10200000')
294 or an alias property.
295
296 Aliases are supported like this:
297
298 aliases {
299 console = "/uart@10200000";
300 };
301
302 pointing to a node:
303
304 uart@10200000 {
305 status = "ok";
306 };
307
308 In this case, this function takes the name of the alias ('console' in
309 this case) and updates the status of the node that is pointed to, to
310 either ok or disabled. If the alias does not exist, a warning is
311 displayed.
312
313 Args:
314 node_name: Name of node (e.g. '/uart@10200000') or alias alias
315 (e.g. 'console') to adjust
316 enabled: True to enable, False to disable
317 """
318 # Look up the alias if this is an alias reference
319 if not node_name.startswith('/'):
320 lookup = self.fdt.GetString('/aliases', node_name, '')
321 if not lookup:
322 self._out.Warning("Cannot find alias '%s' - ignoring" % node_name)
323 return
324 node_name = lookup
325 if enabled:
326 status = 'ok'
327 else:
328 status = 'disabled'
329 self.fdt.PutString(node_name, 'status', status)
330
331 def AddEnableList(self, enable_list):
332 """Process a list of nodes to enable/disable.
333
334 Args:
335 config_list: List of (node, value) tuples to add to the fdt. For each
336 tuple:
337 node: The fdt node to write to will be <node> or pointed to by
338 /aliases/<node>. We can tell which
339 value: 0 to disable the node, 1 to enable it
340 """
341 if enable_list:
342 for node_name, enabled in enable_list:
343 try:
344 enabled = int(enabled)
345 if enabled not in (0, 1):
346 raise ValueError
347 except ValueError as str:
348 raise CmdError("Invalid enable option value '%s' "
349 "(should be 0 or 1)" % enabled)
350 self.SetNodeEnabled(node_name, enabled)
351
Simon Glass290a1802011-07-17 13:54:32 -0700352 def AddConfigList(self, config_list, use_int=False):
353 """Add a list of config items to the fdt.
354
355 Normally these values are written to the fdt as strings, but integers
356 are also supported, in which case the values will be converted to integers
357 (if necessary) before being stored.
358
359 Args:
360 config_list: List of (config, value) tuples to add to the fdt. For each
361 tuple:
362 config: The fdt node to write to will be /config/<config>.
363 value: An integer or string value to write.
364 use_int: True to only write integer values.
365
366 Raises:
367 CmdError: if a value is required to be converted to integer but can't be.
368 """
369 if config_list:
370 for config in config_list:
371 value = config[1]
372 if use_int:
373 try:
374 value = int(value)
375 except ValueError as str:
376 raise CmdError("Cannot convert config option '%s' to integer" %
377 value)
378 if type(value) == type(1):
Simon Glass02d124a2012-03-02 14:47:20 -0800379 self.fdt.PutInteger('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700380 else:
Simon Glass02d124a2012-03-02 14:47:20 -0800381 self.fdt.PutString('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700382
Simon Glass7c2d5572011-11-15 14:47:08 -0800383 def DecodeTextBase(self, data):
384 """Look at a U-Boot image and try to decode its TEXT_BASE.
385
386 This works because U-Boot has a header with the value 0x12345678
387 immediately followed by the TEXT_BASE value. We can therefore read this
388 from the image with some certainty. We check only the first 40 words
389 since the header should be within that region.
390
391 Args:
392 data: U-Boot binary data
393
394 Returns:
395 Text base (integer) or None if none was found
396 """
397 found = False
398 for i in range(0, 160, 4):
399 word = data[i:i + 4]
400
401 # TODO(sjg): This does not cope with a big-endian target
402 value = struct.unpack('<I', word)[0]
403 if found:
404 return value
405 if value == 0x12345678:
406 found = True
407
408 return None
409
410 def CalcTextBase(self, name, fdt, fname):
411 """Calculate the TEXT_BASE to use for U-Boot.
412
413 Normally this value is in the fdt, so we just read it from there. But as
414 a second check we look at the image itself in case this is different, and
415 switch to that if it is.
416
417 This allows us to flash any U-Boot even if its TEXT_BASE is different.
418 This is particularly useful with upstream U-Boot which uses a different
419 value (which we will move to).
420 """
421 data = self._tools.ReadFile(fname)
Simon Glass02d124a2012-03-02 14:47:20 -0800422 fdt_text_base = fdt.GetInt('/chromeos-config', 'textbase')
Simon Glass7c2d5572011-11-15 14:47:08 -0800423 text_base = self.DecodeTextBase(data)
424
425 # If they are different, issue a warning and switch over.
426 if text_base and text_base != fdt_text_base:
427 self._out.Warning("TEXT_BASE %x in %sU-Boot doesn't match "
428 "fdt value of %x. Using %x" % (text_base, name,
429 fdt_text_base, text_base))
430 fdt_text_base = text_base
431 return fdt_text_base
432
Simon Glass6dcc2f22011-07-28 15:26:49 +1200433 def _CreateBootStub(self, uboot, base_fdt, postload):
Simon Glass89b86b82011-07-17 23:49:49 -0700434 """Create a boot stub and a signed boot stub.
435
Simon Glass6dcc2f22011-07-28 15:26:49 +1200436 For postload:
437 We add a /config/postload-text-offset entry to the signed bootstub's
438 fdt so that U-Boot can find the postload code.
439
440 The raw (unsigned) bootstub will have a value of -1 for this since we will
441 simply append the postload code to the bootstub and it can find it there.
442 This will be used for RW A/B firmware.
443
444 For the signed case this value will specify where in the flash to find
445 the postload code. This will be used for RO firmware.
446
Simon Glass89b86b82011-07-17 23:49:49 -0700447 Args:
448 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass29b96ad2012-03-09 15:34:33 -0800449 base_fdt: Fdt object containing the flat device tree.
Simon Glass6dcc2f22011-07-28 15:26:49 +1200450 postload: Path to u-boot-post.bin, or None if none.
Simon Glass89b86b82011-07-17 23:49:49 -0700451
452 Returns:
453 Tuple containing:
Simon Glass6dcc2f22011-07-28 15:26:49 +1200454 Full path to bootstub (uboot + fdt(-1) + postload).
455 Full path to signed (uboot + fdt(flash pos) + bct) + postload.
Simon Glass89b86b82011-07-17 23:49:49 -0700456
457 Raises:
458 CmdError if a command fails.
459 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200460 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass7c2d5572011-11-15 14:47:08 -0800461 text_base = self.CalcTextBase('', self.fdt, uboot)
Simon Glass89b86b82011-07-17 23:49:49 -0700462 uboot_data = self._tools.ReadFile(uboot)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200463
464 # Make a copy of the fdt for the bootstub
465 fdt = base_fdt.Copy(os.path.join(self._tools.outdir, 'bootstub.dtb'))
Simon Glass02d124a2012-03-02 14:47:20 -0800466 fdt.PutInteger('/config', 'postload-text-offset', 0xffffffff);
Simon Glass290a1802011-07-17 13:54:32 -0700467 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200468
Simon Glass89b86b82011-07-17 23:49:49 -0700469 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700470 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
471 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700472 self._tools.OutputSize('Combined binary', bootstub)
473
Simon Glasse13ee2c2011-07-28 08:12:28 +1200474 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700475 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700476 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200477 bootstub, text_base)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200478
479 signed_postload = os.path.join(self._tools.outdir, 'signed-postload.bin')
480 data = self._tools.ReadFile(signed)
481
482 if postload:
483 # We must add postload to the bootstub since A and B will need to
484 # be able to find it without the /config/postload-text-offset mechanism.
485 bs_data = self._tools.ReadFile(bootstub)
486 bs_data += self._tools.ReadFile(postload)
487 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt-postload.bin')
488 self._tools.WriteFile(bootstub, bs_data)
489 self._tools.OutputSize('Combined binary with postload', bootstub)
490
491 # Now that we know the file size, adjust the fdt and re-sign
492 postload_bootstub = os.path.join(self._tools.outdir, 'postload.bin')
Simon Glass02d124a2012-03-02 14:47:20 -0800493 fdt.PutInteger('/config', 'postload-text-offset', len(data))
Simon Glass6dcc2f22011-07-28 15:26:49 +1200494 fdt_data = self._tools.ReadFile(fdt.fname)
495 self._tools.WriteFile(postload_bootstub, uboot_data + fdt_data)
496 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
497 postload_bootstub, text_base)
498 if len(data) != os.path.getsize(signed):
499 raise CmdError('Signed file size changed from %d to %d after updating '
500 'fdt' % (len(data), os.path.getsize(signed)))
501
502 # Re-read the signed image, and add the post-load binary.
503 data = self._tools.ReadFile(signed)
504 data += self._tools.ReadFile(postload)
505 self._tools.OutputSize('Post-load binary', postload)
506
507 self._tools.WriteFile(signed_postload, data)
508 self._tools.OutputSize('Final bootstub with postload', signed_postload)
509
510 return bootstub, signed_postload
Simon Glass89b86b82011-07-17 23:49:49 -0700511
Vincent Palatinf7286772011-10-12 14:31:53 -0700512 def _CreateCorebootStub(self, uboot, coreboot, fdt, seabios):
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700513 """Create a coreboot boot stub.
514
515 Args:
516 uboot: Path to u-boot.bin (may be chroot-relative)
517 coreboot: Path to coreboot.rom
518 fdt: Device Tree
Vincent Palatinf7286772011-10-12 14:31:53 -0700519 seabios: Path to SeaBIOS payload binary or None
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700520
521 Returns:
522 Full path to bootstub (coreboot + uboot + fdt).
523
524 Raises:
525 CmdError if a command fails.
526 """
527 bootstub = os.path.join(self._tools.outdir, 'coreboot-full.rom')
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700528 uboot_elf = uboot.replace(".bin", ".elf")
529 shutil.copyfile(coreboot, bootstub)
Vincent Palatinf7286772011-10-12 14:31:53 -0700530 if seabios:
Simon Glass146cb8e2012-03-09 15:51:24 -0800531 self._tools.Run('cbfstool', [bootstub, 'add-payload', seabios,
Vincent Palatinf7286772011-10-12 14:31:53 -0700532 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800533 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700534 'img/U-Boot', 'lzma'])
535 else:
Simon Glass146cb8e2012-03-09 15:51:24 -0800536 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700537 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800538 self._tools.Run('cbfstool', [bootstub, 'add', fdt.fname, 'u-boot.dtb',
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700539 '0xac'])
540 return bootstub
541
Simon Glass8e1fdb22012-03-15 21:02:10 -0700542 def _UpdateBl2Parameters(self, fdt, pack, data, pos):
Simon Glassdf95dd22012-03-13 15:46:16 -0700543 """Update the parameters in a BL2 blob.
544
545 We look at the list in the parameter block, extract the value of each
546 from the device tree, and write that value to the parameter block.
547
548 Args:
549 fdt: Device tree containing the parameter values.
Simon Glass8e1fdb22012-03-15 21:02:10 -0700550 pack: The firmware packer object
Simon Glassdf95dd22012-03-13 15:46:16 -0700551 data: The BL2 data.
552 pos: The position of the start of the parameter block.
553
554 Returns:
555 The new contents of the parameter block, after updating.
556 """
Simon Glassdf95dd22012-03-13 15:46:16 -0700557 version, size = struct.unpack('<2L', data[pos + 4:pos + 12])
558 if version != 1:
559 raise CmdError("Cannot update machine parameter block version '%d'" %
560 version)
561 if size < 0 or pos + size > len(data):
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700562 raise CmdError("Machine parameter block size %d is invalid: "
563 "pos=%d, size=%d, space=%d, len=%d" %
564 (size, pos, size, len(data) - pos, len(data)))
Simon Glassdf95dd22012-03-13 15:46:16 -0700565
566 # Move past the header and read the parameter list, which is terminated
567 # with \0.
568 pos += 12
569 param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0]
570 param_len = param_list.find('\0')
571 param_list = param_list[:param_len]
Simon Glass66c1a9f2012-03-22 19:15:53 -0700572 pos += (param_len + 4) & ~3
Simon Glassdf95dd22012-03-13 15:46:16 -0700573
574 # Work through the parameters one at a time, adding each value
575 new_data = ''
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700576 upto = 0
Simon Glassdf95dd22012-03-13 15:46:16 -0700577 for param in param_list:
Simon Glass2c48ddf2012-03-23 16:55:22 -0700578 value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
Simon Glassdf95dd22012-03-13 15:46:16 -0700579 if param == 'm' :
Simon Glass1a77ded2012-03-15 21:00:49 -0700580 mem_type = fdt.GetString('/dmc', 'mem-type')
Simon Glassdf95dd22012-03-13 15:46:16 -0700581 mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3']
582 if not mem_type in mem_types:
583 raise CmdError("Unknown memory type '%s'" % mem_type)
584 value = mem_types.index(mem_type)
585 self._out.Info(' Memory type: %s (%d)' % (mem_type, value))
Doug Andersonee46cfe2012-05-18 09:53:08 -0700586 elif param == 'M' :
587 mem_manuf = fdt.GetString('/dmc', 'mem-manuf')
588 mem_manufs = ['autodetect', 'elpida', 'samsung']
589 if not mem_manuf in mem_manufs:
590 raise CmdError("Unknown memory manufacturer: '%s'" % mem_manuf)
591 value = mem_manufs.index(mem_manuf)
592 self._out.Info(' Memory manufacturer: %s (%d)' % (mem_manuf, value))
Simon Glassdf95dd22012-03-13 15:46:16 -0700593 elif param == 'v':
594 value = 31
595 self._out.Info(' Memory interleave: %#0x' % value)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700596 elif param == 'u':
Simon Glass2c48ddf2012-03-23 16:55:22 -0700597 # TODO(sjg): Seems to not work unless set to the same value as in the
598 # existing image. Need to find root cause.
599 #value = os.stat(pack.GetProperty('boot+dtb')).st_size
600 #value = (value + 0xfff) & ~0xfff
601 self._out.Warning("Leaving U-Boot size unchanged")
Simon Glass8e1fdb22012-03-15 21:02:10 -0700602 self._out.Info(' U-Boot size: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700603 else:
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700604 self._out.Warning("Unknown machine parameter type '%s'" % param)
Simon Glass2c48ddf2012-03-23 16:55:22 -0700605 self._out.Info(' Unknown value: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700606 new_data += struct.pack('<L', value)
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700607 upto += 4
Simon Glassdf95dd22012-03-13 15:46:16 -0700608
609 # Put the data into our block.
610 data = data[:pos] + new_data + data[pos + len(new_data):]
611 self._out.Info('BL2 configuration complete')
612 return data
613
Simon Glass8e1fdb22012-03-15 21:02:10 -0700614 def _ConfigureExynosBl2(self, fdt, pack, orig_bl2):
Simon Glass7e199222012-03-13 15:51:18 -0700615 """Configure an Exynos BL2 binary for our needs.
616
617 We create a new modified BL2 and return its filename.
618
619 Args:
620 fdt: Device tree containing the parameter values.
Simon Glass8e1fdb22012-03-15 21:02:10 -0700621 pack: The firmware packer object
Simon Glass7e199222012-03-13 15:51:18 -0700622 orig_bl2: Filename of original BL2 file to modify.
623 """
Simon Glass66c1a9f2012-03-22 19:15:53 -0700624 self._out.Info('Configuring BL2')
Simon Glass7e199222012-03-13 15:51:18 -0700625 bl2 = os.path.join(self._tools.outdir, 'updated-spl.bin')
Simon Glass66c1a9f2012-03-22 19:15:53 -0700626 data = self._tools.ReadFile(orig_bl2)
627 self._tools.WriteFile(bl2, data)
Simon Glass7e199222012-03-13 15:51:18 -0700628
629 # Locate the parameter block
630 data = self._tools.ReadFile(bl2)
631 marker = struct.pack('<L', 0xdeadbeef)
632 pos = data.rfind(marker)
633 if not pos:
634 raise CmdError("Could not find machine parameter block in '%s'" %
635 orig_bl2)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700636 data = self._UpdateBl2Parameters(fdt, pack, data, pos)
Simon Glass7e199222012-03-13 15:51:18 -0700637 self._tools.WriteFile(bl2, data)
638 return bl2
639
Simon Glass89b86b82011-07-17 23:49:49 -0700640 def _PackOutput(self, msg):
641 """Helper function to write output from PackFirmware (verbose level 2).
642
643 This is passed to PackFirmware for it to use to write output.
644
645 Args:
646 msg: Message to display.
647 """
648 self._out.Notice(msg)
649
Simon Glass439fe7a2012-03-09 16:19:34 -0800650 def _BuildBlob(self, pack, fdt, blob_type):
651 """Build the blob data for a particular blob type.
652
653 Args:
654 blob_type: The type of blob to create data for. Supported types are:
655 coreboot A coreboot image (ROM plus U-boot and .dtb payloads).
656 signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb).
657 """
658 if blob_type == 'coreboot':
659 coreboot = self._CreateCorebootStub(self.uboot_fname,
660 self.coreboot_fname, fdt, self.seabios_fname)
661 pack.AddProperty('coreboot', coreboot)
662 pack.AddProperty('image', coreboot)
663 elif blob_type == 'signed':
664 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt,
665 self.postload_fname)
666 pack.AddProperty('bootstub', bootstub)
667 pack.AddProperty('signed', signed)
668 pack.AddProperty('image', signed)
Simon Glass7e199222012-03-13 15:51:18 -0700669 elif blob_type == 'exynos-bl1':
670 pack.AddProperty(blob_type, self.exynos_bl1)
671 elif blob_type == 'exynos-bl2':
Simon Glass8e1fdb22012-03-15 21:02:10 -0700672 bl2 = self._ConfigureExynosBl2(fdt, pack, self.exynos_bl2)
Simon Glass7e199222012-03-13 15:51:18 -0700673 pack.AddProperty(blob_type, bl2)
Simon Glass439fe7a2012-03-09 16:19:34 -0800674 elif pack.GetProperty(blob_type):
675 pass
676 else:
677 raise CmdError("Unknown blob type '%s' required in flash map" %
678 blob_type)
679
Simon Glass290a1802011-07-17 13:54:32 -0700680 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700681 """Create a full firmware image, along with various by-products.
682
683 This uses the provided u-boot.bin, fdt and bct to create a firmware
684 image containing all the required parts. If the GBB is not supplied
685 then this will just return a signed U-Boot as the image.
686
687 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200688 gbb: Full path to the GBB file, or empty if a GBB is not required.
689 fdt: Fdt object containing required information.
690
691 Returns:
692 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700693
694 Raises:
695 CmdError if a command fails.
696 """
Simon Glass02d124a2012-03-02 14:47:20 -0800697 self._out.Notice("Model: %s" % fdt.GetString('/', 'model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700698
Simon Glass439fe7a2012-03-09 16:19:34 -0800699 # Get the flashmap so we know what to build
700 pack = PackFirmware(self._tools, self._out)
701 pack.SelectFdt(fdt)
702
703 # Get all our blobs ready
704 pack.AddProperty('boot', self.uboot_fname)
Simon Glass50f74602012-03-15 21:04:25 -0700705
706 # Make a copy of the fdt for the bootstub
707 fdt_data = self._tools.ReadFile(fdt.fname)
708 uboot_data = self._tools.ReadFile(self.uboot_fname)
709 uboot_copy = os.path.join(self._tools.outdir, 'u-boot.bin')
710 self._tools.WriteFile(uboot_copy, uboot_data)
711
712 bootstub = os.path.join(self._tools.outdir, 'u-boot-dtb.bin')
713 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
714 pack.AddProperty('boot+dtb', bootstub)
715
Simon Glass439fe7a2012-03-09 16:19:34 -0800716 pack.AddProperty('gbb', self.uboot_fname)
717 for blob_type in pack.GetBlobList(self.coreboot_fname is not None):
718 self._BuildBlob(pack, fdt, blob_type)
Simon Glass89b86b82011-07-17 23:49:49 -0700719
720 if gbb:
Simon Glasse76bf7b2012-03-13 15:34:41 -0700721 pack.RequireAllEntries()
Hung-Te Lina7462e72011-07-27 19:17:10 +0800722 fwid = '.'.join([
Simon Glass02d124a2012-03-02 14:47:20 -0800723 re.sub('[ ,]+', '_', fdt.GetString('/', 'model')),
Hung-Te Lina7462e72011-07-27 19:17:10 +0800724 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700725 self._out.Notice('Firmware ID: %s' % fwid)
Simon Glass439fe7a2012-03-09 16:19:34 -0800726 pack.AddProperty('fwid', fwid)
727 pack.AddProperty('gbb', gbb)
728 pack.AddProperty('keydir', self._keydir)
Simon Glassc90cf582012-03-13 15:40:47 -0700729
730 pack.CheckProperties()
731 image = os.path.join(self._tools.outdir, 'image.bin')
732 pack.PackImage(self._tools.outdir, image)
733 pack.AddProperty('image', image)
Simon Glass89b86b82011-07-17 23:49:49 -0700734
Simon Glass439fe7a2012-03-09 16:19:34 -0800735 image = pack.GetProperty('image')
Simon Glass89b86b82011-07-17 23:49:49 -0700736 self._tools.OutputSize('Final image', image)
Simon Glassc90cf582012-03-13 15:40:47 -0700737 return image, pack
Simon Glass89b86b82011-07-17 23:49:49 -0700738
Simon Glass290a1802011-07-17 13:54:32 -0700739 def SelectFdt(self, fdt_fname):
740 """Select an FDT to control the firmware bundling
741
742 Args:
743 fdt_fname: The filename of the fdt to use.
744
Simon Glassc0f3dc62011-08-09 14:19:05 -0700745 Returns:
746 The Fdt object of the original fdt file, which we will not modify.
747
Simon Glass290a1802011-07-17 13:54:32 -0700748 We make a copy of this which will include any on-the-fly changes we want
749 to make.
750 """
751 self._fdt_fname = fdt_fname
752 self.CheckOptions()
753 fdt = Fdt(self._tools, self._fdt_fname)
Simon Glass29b96ad2012-03-09 15:34:33 -0800754 fdt.Compile()
Simon Glass290a1802011-07-17 13:54:32 -0700755 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700756 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700757
Simon Glassc90cf582012-03-13 15:40:47 -0700758 def Start(self, hardware_id, output_fname, show_map):
Simon Glass290a1802011-07-17 13:54:32 -0700759 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700760
761 - Checks options, tools, output directory, fdt.
762 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700763
764 Args:
Simon Glass56577572011-07-19 11:08:06 +1200765 hardware_id: Hardware ID to use for this board. If None, then the
766 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700767 output_fname: Output filename for the image. If this is not None, then
768 the final image will be copied here.
Simon Glassc90cf582012-03-13 15:40:47 -0700769 show_map: Show a flash map, with each area's name and position
Simon Glass290a1802011-07-17 13:54:32 -0700770
771 Returns:
772 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700773 """
Simon Glass89b86b82011-07-17 23:49:49 -0700774 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700775 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200776 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700777
778 # This creates the actual image.
Simon Glassc90cf582012-03-13 15:40:47 -0700779 image, pack = self._CreateImage(gbb, self.fdt)
780 if show_map:
781 pack.ShowMap()
Simon Glass290a1802011-07-17 13:54:32 -0700782 if output_fname:
783 shutil.copyfile(image, output_fname)
784 self._out.Notice("Output image '%s'" % output_fname)
785 return image