blob: 08a6a636d1f7685adde1e42d1d56c9f01f71b775 [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.
Simon Glass559b6612012-05-23 13:28:45 -070078 spl_source: Source device to load U-Boot from, in SPL:
79 straps: Select device according to CPU strap pins
80 spi: Boot from SPI
81 emmc: Boot from eMMC
Simon Glass290a1802011-07-17 13:54:32 -070082 """
Simon Glass89b86b82011-07-17 23:49:49 -070083
Simon Glass290a1802011-07-17 13:54:32 -070084 def __init__(self, tools, output):
85 """Set up a new Bundle object.
Simon Glass89b86b82011-07-17 23:49:49 -070086
Simon Glass290a1802011-07-17 13:54:32 -070087 Args:
88 tools: A tools.Tools object to use for external tools.
89 output: A cros_output.Output object to use for program output.
Simon Glass89b86b82011-07-17 23:49:49 -070090 """
Simon Glass290a1802011-07-17 13:54:32 -070091 self._tools = tools
92 self._out = output
93
94 # Set up the things we need to know in order to operate.
95 self._board = None # Board name, e.g. tegra2_seaboard.
96 self._fdt_fname = None # Filename of our FDT.
97 self.uboot_fname = None # Filename of our U-Boot binary.
98 self.bct_fname = None # Filename of our BCT file.
99 self.fdt = None # Our Fdt object.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800100 self.bmpblk_fname = None # Filename of our Bitmap Block
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700101 self.coreboot_fname = None # Filename of our coreboot binary.
Vincent Palatinf7286772011-10-12 14:31:53 -0700102 self.seabios_fname = None # Filename of our SeaBIOS payload.
Simon Glass7e199222012-03-13 15:51:18 -0700103 self.exynos_bl1 = None # Filename of Exynos BL1 (pre-boot)
104 self.exynos_bl2 = None # Filename of Exynos BL2 (SPL)
Simon Glass559b6612012-05-23 13:28:45 -0700105 self.spl_source = 'straps' # SPL boot according to board settings
Simon Glass07267952012-06-08 12:45:13 -0700106 self.skeleton_fname = None # Filename of Coreboot skeleton file
Simon Glass290a1802011-07-17 13:54:32 -0700107
108 def SetDirs(self, keydir):
109 """Set up directories required for Bundle.
110
111 Args:
112 keydir: Directory containing keys to use for signing firmware.
113 """
114 self._keydir = keydir
115
Simon Glass6dcc2f22011-07-28 15:26:49 +1200116 def SetFiles(self, board, bct, uboot=None, bmpblk=None, coreboot=None,
Simon Glass07267952012-06-08 12:45:13 -0700117 postload=None, seabios=None, exynos_bl1=None, exynos_bl2=None,
118 skeleton=None):
Simon Glass290a1802011-07-17 13:54:32 -0700119 """Set up files required for Bundle.
120
121 Args:
122 board: The name of the board to target (e.g. tegra2_seaboard).
123 uboot: The filename of the u-boot.bin image to use.
124 bct: The filename of the binary BCT file to use.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800125 bmpblk: The filename of bitmap block file to use.
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700126 coreboot: The filename of the coreboot image to use (on x86)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200127 postload: The filename of the u-boot-post.bin image to use.
Vincent Palatinf7286772011-10-12 14:31:53 -0700128 seabios: The filename of the SeaBIOS payload to use if any.
Simon Glass07267952012-06-08 12:45:13 -0700129 exynos_bl1: The filename of the exynos BL1 file
130 exynos_bl2: The filename of the exynos BL2 file (U-Boot spl)
131 skeleton: The filename of the coreboot skeleton file.
Simon Glass290a1802011-07-17 13:54:32 -0700132 """
133 self._board = board
134 self.uboot_fname = uboot
135 self.bct_fname = bct
Hung-Te Lin5b649382011-08-03 15:01:16 +0800136 self.bmpblk_fname = bmpblk
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700137 self.coreboot_fname = coreboot
Simon Glass6dcc2f22011-07-28 15:26:49 +1200138 self.postload_fname = postload
Vincent Palatinf7286772011-10-12 14:31:53 -0700139 self.seabios_fname = seabios
Simon Glass7e199222012-03-13 15:51:18 -0700140 self.exynos_bl1 = exynos_bl1
141 self.exynos_bl2 = exynos_bl2
Simon Glass07267952012-06-08 12:45:13 -0700142 self.skeleton_fname = skeleton
Simon Glass290a1802011-07-17 13:54:32 -0700143
144 def SetOptions(self, small):
145 """Set up options supported by Bundle.
146
147 Args:
148 small: Only create a signed U-Boot - don't produce the full packed
149 firmware image. This is useful for devs who want to replace just the
150 U-Boot part while keeping the keys, gbb, etc. the same.
151 """
152 self._small = small
153
154 def CheckOptions(self):
155 """Check provided options and select defaults."""
156 if not self._board:
157 raise ValueError('No board defined - please define a board to use')
Simon Glass493163b2011-09-14 11:19:57 -0700158 build_root = os.path.join('##', 'build', self._board, 'firmware')
Simon Glass881964d2012-04-04 11:34:09 -0700159 dir_name = os.path.join(build_root, 'dts')
Simon Glass290a1802011-07-17 13:54:32 -0700160 if not self._fdt_fname:
Simon Glassceff3ff2012-04-04 11:23:45 -0700161 # Figure out where the file should be, and the name we expect.
Simon Glassceff3ff2012-04-04 11:23:45 -0700162 base_name = re.sub('_', '-', self._board)
163
164 # In case the name exists with a prefix or suffix, find it.
165 wildcard = os.path.join(dir_name, '*%s*.dts' % base_name)
166 found_list = glob.glob(self._tools.Filename(wildcard))
167 if len(found_list) == 1:
168 self._fdt_fname = found_list[0]
169 else:
170 # We didn't find anything definite, so set up our expected name.
171 self._fdt_fname = os.path.join(dir_name, '%s.dts' % base_name)
172
Simon Glass881964d2012-04-04 11:34:09 -0700173 # Convert things like 'exynos5250-daisy' into a full path.
174 root, ext = os.path.splitext(self._fdt_fname)
175 if not ext and not os.path.dirname(root):
176 self._fdt_fname = os.path.join(dir_name, '%s.dts' % root)
177
Simon Glass290a1802011-07-17 13:54:32 -0700178 if not self.uboot_fname:
179 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
180 if not self.bct_fname:
181 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass2a7f0b32011-08-26 11:25:17 -0700182 if not self.bmpblk_fname:
183 self.bmpblk_fname = os.path.join(build_root, 'default.bmpblk')
Simon Glass1d376832012-03-15 20:50:54 -0700184 if not self.exynos_bl1:
185 self.exynos_bl1 = os.path.join(build_root, 'E5250.nbl1.bin')
186 if not self.exynos_bl2:
187 self.exynos_bl2 = os.path.join(build_root, 'smdk5250-spl.bin')
Simon Glass07267952012-06-08 12:45:13 -0700188 if not self.coreboot_fname:
189 self.coreboot_fname = os.path.join(build_root, 'coreboot.rom')
190 if not self.skeleton_fname:
191 self.skeleton_fname = os.path.join(build_root, 'skeleton.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700192
Simon Glass75759302012-03-15 20:26:53 -0700193 def GetFiles(self):
194 """Get a list of files that we know about.
195
196 This is the opposite of SetFiles except that we may have put in some
197 default names. It returns a dictionary containing the filename for
198 each of a number of pre-defined files.
199
200 Returns:
201 Dictionary, with one entry for each file.
202 """
203 file_list = {
204 'bct' : self.bct_fname,
205 'exynos-bl1' : self.exynos_bl1,
206 'exynos-bl2' : self.exynos_bl2,
207 }
208 return file_list
209
Simon Glass56577572011-07-19 11:08:06 +1200210 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700211 """Create a GBB for the image.
212
Simon Glass56577572011-07-19 11:08:06 +1200213 Args:
214 hardware_id: Hardware ID to use for this board. If None, then the
215 default from the Fdt will be used
216
Simon Glass89b86b82011-07-17 23:49:49 -0700217 Returns:
218 Path of the created GBB file.
219
220 Raises:
221 CmdError if a command fails.
222 """
Simon Glass56577572011-07-19 11:08:06 +1200223 if not hardware_id:
Simon Glass02d124a2012-03-02 14:47:20 -0800224 hardware_id = self.fdt.GetString('/config', 'hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700225 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700226 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700227
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800228 chromeos_config = self.fdt.GetProps("/chromeos-config")
229 if 'fast-developer-mode' not in chromeos_config:
230 gbb_flags = 0
231 else:
232 self._out.Notice("Enabling fast-developer-mode.")
233 gbb_flags = 1
234
Simon Glass89b86b82011-07-17 23:49:49 -0700235 self._out.Progress('Creating GBB')
236 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
237 sizes = ['%#x' % size for size in sizes]
238 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700239 keydir = self._tools.Filename(self._keydir)
240 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700241 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200242 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700243 '--rootkey=%s/root_key.vbpubk' % keydir,
244 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Simon Glass2a7f0b32011-08-26 11:25:17 -0700245 '--bmpfv=%s' % self._tools.Filename(self.bmpblk_fname),
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800246 '--flags=%d' % gbb_flags,
Simon Glass89b86b82011-07-17 23:49:49 -0700247 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700248 cwd=odir)
249 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700250
Simon Glasse13ee2c2011-07-28 08:12:28 +1200251 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700252 """Sign an image so that the Tegra SOC will boot it.
253
254 Args:
255 bct: BCT file to use.
256 bootstub: Boot stub (U-Boot + fdt) file to sign.
257 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700258
259 Returns:
260 filename of signed image.
261
262 Raises:
263 CmdError if a command fails.
264 """
265 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200266 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700267 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200268 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700269 fd = open(config, 'w')
270 fd.write('Version = 1;\n')
271 fd.write('Redundancy = 1;\n')
272 fd.write('Bctfile = %s;\n' % bct)
Doug Anderson0eeb0742011-09-15 18:11:40 -0700273
274 # TODO(dianders): Right now, we don't have enough space in our flash map
275 # for two copies of the BCT when we're using NAND, so hack it to 1. Not
276 # sure what this does for reliability, but at least things will fit...
277 is_nand = "NvBootDevType_Nand" in self._tools.Run('bct_dump', [bct])
278 if is_nand:
279 fd.write('Bctcopy = 1;\n')
280
Simon Glass89b86b82011-07-17 23:49:49 -0700281 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
282 text_base))
Doug Anderson0eeb0742011-09-15 18:11:40 -0700283
Simon Glass89b86b82011-07-17 23:49:49 -0700284 fd.close()
285
286 self._tools.Run('cbootimage', [config, signed])
287 self._tools.OutputSize('BCT', bct)
288 self._tools.OutputSize('Signed image', signed)
289 return signed
290
Doug Anderson86ce5f42011-07-27 10:40:18 -0700291 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700292 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700293
294 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700295 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700296 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700297 """
Simon Glass290a1802011-07-17 13:54:32 -0700298 if bootcmd:
Simon Glass02d124a2012-03-02 14:47:20 -0800299 self.fdt.PutString('/config', 'bootcmd', bootcmd)
300 self.fdt.PutInteger('/config', 'bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700301 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700302
Simon Glassa4934b72012-05-09 13:35:02 -0700303 def SetNodeEnabled(self, node_name, enabled):
304 """Set whether an node is enabled or disabled.
305
306 This simply sets the 'status' property of a node to "ok", or "disabled".
307
308 The node should either be a full path to the node (like '/uart@10200000')
309 or an alias property.
310
311 Aliases are supported like this:
312
313 aliases {
314 console = "/uart@10200000";
315 };
316
317 pointing to a node:
318
319 uart@10200000 {
320 status = "ok";
321 };
322
323 In this case, this function takes the name of the alias ('console' in
324 this case) and updates the status of the node that is pointed to, to
325 either ok or disabled. If the alias does not exist, a warning is
326 displayed.
327
328 Args:
329 node_name: Name of node (e.g. '/uart@10200000') or alias alias
330 (e.g. 'console') to adjust
331 enabled: True to enable, False to disable
332 """
333 # Look up the alias if this is an alias reference
334 if not node_name.startswith('/'):
335 lookup = self.fdt.GetString('/aliases', node_name, '')
336 if not lookup:
337 self._out.Warning("Cannot find alias '%s' - ignoring" % node_name)
338 return
339 node_name = lookup
340 if enabled:
341 status = 'ok'
342 else:
343 status = 'disabled'
344 self.fdt.PutString(node_name, 'status', status)
345
346 def AddEnableList(self, enable_list):
347 """Process a list of nodes to enable/disable.
348
349 Args:
350 config_list: List of (node, value) tuples to add to the fdt. For each
351 tuple:
352 node: The fdt node to write to will be <node> or pointed to by
353 /aliases/<node>. We can tell which
354 value: 0 to disable the node, 1 to enable it
355 """
356 if enable_list:
357 for node_name, enabled in enable_list:
358 try:
359 enabled = int(enabled)
360 if enabled not in (0, 1):
361 raise ValueError
362 except ValueError as str:
363 raise CmdError("Invalid enable option value '%s' "
364 "(should be 0 or 1)" % enabled)
365 self.SetNodeEnabled(node_name, enabled)
366
Simon Glass290a1802011-07-17 13:54:32 -0700367 def AddConfigList(self, config_list, use_int=False):
368 """Add a list of config items to the fdt.
369
370 Normally these values are written to the fdt as strings, but integers
371 are also supported, in which case the values will be converted to integers
372 (if necessary) before being stored.
373
374 Args:
375 config_list: List of (config, value) tuples to add to the fdt. For each
376 tuple:
377 config: The fdt node to write to will be /config/<config>.
378 value: An integer or string value to write.
379 use_int: True to only write integer values.
380
381 Raises:
382 CmdError: if a value is required to be converted to integer but can't be.
383 """
384 if config_list:
385 for config in config_list:
386 value = config[1]
387 if use_int:
388 try:
389 value = int(value)
390 except ValueError as str:
391 raise CmdError("Cannot convert config option '%s' to integer" %
392 value)
393 if type(value) == type(1):
Simon Glass02d124a2012-03-02 14:47:20 -0800394 self.fdt.PutInteger('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700395 else:
Simon Glass02d124a2012-03-02 14:47:20 -0800396 self.fdt.PutString('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700397
Simon Glass7c2d5572011-11-15 14:47:08 -0800398 def DecodeTextBase(self, data):
399 """Look at a U-Boot image and try to decode its TEXT_BASE.
400
401 This works because U-Boot has a header with the value 0x12345678
402 immediately followed by the TEXT_BASE value. We can therefore read this
403 from the image with some certainty. We check only the first 40 words
404 since the header should be within that region.
405
406 Args:
407 data: U-Boot binary data
408
409 Returns:
410 Text base (integer) or None if none was found
411 """
412 found = False
413 for i in range(0, 160, 4):
414 word = data[i:i + 4]
415
416 # TODO(sjg): This does not cope with a big-endian target
417 value = struct.unpack('<I', word)[0]
418 if found:
419 return value
420 if value == 0x12345678:
421 found = True
422
423 return None
424
425 def CalcTextBase(self, name, fdt, fname):
426 """Calculate the TEXT_BASE to use for U-Boot.
427
428 Normally this value is in the fdt, so we just read it from there. But as
429 a second check we look at the image itself in case this is different, and
430 switch to that if it is.
431
432 This allows us to flash any U-Boot even if its TEXT_BASE is different.
433 This is particularly useful with upstream U-Boot which uses a different
434 value (which we will move to).
435 """
436 data = self._tools.ReadFile(fname)
Simon Glass02d124a2012-03-02 14:47:20 -0800437 fdt_text_base = fdt.GetInt('/chromeos-config', 'textbase')
Simon Glass7c2d5572011-11-15 14:47:08 -0800438 text_base = self.DecodeTextBase(data)
Simon Glass60a40af2012-06-07 11:54:17 -0700439 self._out.Info('TEXT_BASE: fdt says %#x, %s says %#x' % (fdt_text_base,
440 fname, text_base))
Simon Glass7c2d5572011-11-15 14:47:08 -0800441
442 # If they are different, issue a warning and switch over.
443 if text_base and text_base != fdt_text_base:
444 self._out.Warning("TEXT_BASE %x in %sU-Boot doesn't match "
445 "fdt value of %x. Using %x" % (text_base, name,
446 fdt_text_base, text_base))
447 fdt_text_base = text_base
448 return fdt_text_base
449
Simon Glass6dcc2f22011-07-28 15:26:49 +1200450 def _CreateBootStub(self, uboot, base_fdt, postload):
Simon Glass89b86b82011-07-17 23:49:49 -0700451 """Create a boot stub and a signed boot stub.
452
Simon Glass6dcc2f22011-07-28 15:26:49 +1200453 For postload:
454 We add a /config/postload-text-offset entry to the signed bootstub's
455 fdt so that U-Boot can find the postload code.
456
457 The raw (unsigned) bootstub will have a value of -1 for this since we will
458 simply append the postload code to the bootstub and it can find it there.
459 This will be used for RW A/B firmware.
460
461 For the signed case this value will specify where in the flash to find
462 the postload code. This will be used for RO firmware.
463
Simon Glass89b86b82011-07-17 23:49:49 -0700464 Args:
465 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass29b96ad2012-03-09 15:34:33 -0800466 base_fdt: Fdt object containing the flat device tree.
Simon Glass6dcc2f22011-07-28 15:26:49 +1200467 postload: Path to u-boot-post.bin, or None if none.
Simon Glass89b86b82011-07-17 23:49:49 -0700468
469 Returns:
470 Tuple containing:
Simon Glass6dcc2f22011-07-28 15:26:49 +1200471 Full path to bootstub (uboot + fdt(-1) + postload).
472 Full path to signed (uboot + fdt(flash pos) + bct) + postload.
Simon Glass89b86b82011-07-17 23:49:49 -0700473
474 Raises:
475 CmdError if a command fails.
476 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200477 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass7c2d5572011-11-15 14:47:08 -0800478 text_base = self.CalcTextBase('', self.fdt, uboot)
Simon Glass89b86b82011-07-17 23:49:49 -0700479 uboot_data = self._tools.ReadFile(uboot)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200480
481 # Make a copy of the fdt for the bootstub
482 fdt = base_fdt.Copy(os.path.join(self._tools.outdir, 'bootstub.dtb'))
Simon Glass02d124a2012-03-02 14:47:20 -0800483 fdt.PutInteger('/config', 'postload-text-offset', 0xffffffff);
Simon Glass290a1802011-07-17 13:54:32 -0700484 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200485
Simon Glass89b86b82011-07-17 23:49:49 -0700486 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700487 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
488 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700489 self._tools.OutputSize('Combined binary', bootstub)
490
Simon Glasse13ee2c2011-07-28 08:12:28 +1200491 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700492 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700493 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200494 bootstub, text_base)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200495
496 signed_postload = os.path.join(self._tools.outdir, 'signed-postload.bin')
497 data = self._tools.ReadFile(signed)
498
499 if postload:
500 # We must add postload to the bootstub since A and B will need to
501 # be able to find it without the /config/postload-text-offset mechanism.
502 bs_data = self._tools.ReadFile(bootstub)
503 bs_data += self._tools.ReadFile(postload)
504 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt-postload.bin')
505 self._tools.WriteFile(bootstub, bs_data)
506 self._tools.OutputSize('Combined binary with postload', bootstub)
507
508 # Now that we know the file size, adjust the fdt and re-sign
509 postload_bootstub = os.path.join(self._tools.outdir, 'postload.bin')
Simon Glass02d124a2012-03-02 14:47:20 -0800510 fdt.PutInteger('/config', 'postload-text-offset', len(data))
Simon Glass6dcc2f22011-07-28 15:26:49 +1200511 fdt_data = self._tools.ReadFile(fdt.fname)
512 self._tools.WriteFile(postload_bootstub, uboot_data + fdt_data)
513 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
514 postload_bootstub, text_base)
515 if len(data) != os.path.getsize(signed):
516 raise CmdError('Signed file size changed from %d to %d after updating '
517 'fdt' % (len(data), os.path.getsize(signed)))
518
519 # Re-read the signed image, and add the post-load binary.
520 data = self._tools.ReadFile(signed)
521 data += self._tools.ReadFile(postload)
522 self._tools.OutputSize('Post-load binary', postload)
523
524 self._tools.WriteFile(signed_postload, data)
525 self._tools.OutputSize('Final bootstub with postload', signed_postload)
526
527 return bootstub, signed_postload
Simon Glass89b86b82011-07-17 23:49:49 -0700528
Vincent Palatinf7286772011-10-12 14:31:53 -0700529 def _CreateCorebootStub(self, uboot, coreboot, fdt, seabios):
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700530 """Create a coreboot boot stub.
531
532 Args:
533 uboot: Path to u-boot.bin (may be chroot-relative)
534 coreboot: Path to coreboot.rom
535 fdt: Device Tree
Vincent Palatinf7286772011-10-12 14:31:53 -0700536 seabios: Path to SeaBIOS payload binary or None
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700537
538 Returns:
539 Full path to bootstub (coreboot + uboot + fdt).
540
541 Raises:
542 CmdError if a command fails.
543 """
544 bootstub = os.path.join(self._tools.outdir, 'coreboot-full.rom')
Simon Glassf2b3a5c2012-06-07 14:02:36 -0700545
546 # U-Boot itself does not put a .elf extension on the elf file.
547 # The U-Boot ebuild does, but we shouldn't actually require it since
548 # devs may want to just use what U-Boot creates.
549 uboot_elf = uboot.replace('.bin', '')
550 if not os.path.exists(self._tools.Filename(uboot_elf)):
551 uboot_elf = uboot.replace('.bin', '.elf')
552 shutil.copyfile(self._tools.Filename(coreboot), bootstub)
Vincent Palatinf7286772011-10-12 14:31:53 -0700553 if seabios:
Simon Glass146cb8e2012-03-09 15:51:24 -0800554 self._tools.Run('cbfstool', [bootstub, 'add-payload', seabios,
Vincent Palatinf7286772011-10-12 14:31:53 -0700555 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800556 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700557 'img/U-Boot', 'lzma'])
558 else:
Simon Glass146cb8e2012-03-09 15:51:24 -0800559 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700560 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800561 self._tools.Run('cbfstool', [bootstub, 'add', fdt.fname, 'u-boot.dtb',
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700562 '0xac'])
563 return bootstub
564
Simon Glass3b404092012-05-23 13:10:36 -0700565 def _UpdateBl2Parameters(self, fdt, spl_load_size, data, pos):
Simon Glassdf95dd22012-03-13 15:46:16 -0700566 """Update the parameters in a BL2 blob.
567
568 We look at the list in the parameter block, extract the value of each
569 from the device tree, and write that value to the parameter block.
570
571 Args:
572 fdt: Device tree containing the parameter values.
Simon Glass3b404092012-05-23 13:10:36 -0700573 spl_load_size: Size of U-Boot image that SPL must load
Simon Glassdf95dd22012-03-13 15:46:16 -0700574 data: The BL2 data.
575 pos: The position of the start of the parameter block.
576
577 Returns:
578 The new contents of the parameter block, after updating.
579 """
Simon Glassdf95dd22012-03-13 15:46:16 -0700580 version, size = struct.unpack('<2L', data[pos + 4:pos + 12])
581 if version != 1:
582 raise CmdError("Cannot update machine parameter block version '%d'" %
583 version)
584 if size < 0 or pos + size > len(data):
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700585 raise CmdError("Machine parameter block size %d is invalid: "
586 "pos=%d, size=%d, space=%d, len=%d" %
587 (size, pos, size, len(data) - pos, len(data)))
Simon Glassdf95dd22012-03-13 15:46:16 -0700588
589 # Move past the header and read the parameter list, which is terminated
590 # with \0.
591 pos += 12
592 param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0]
593 param_len = param_list.find('\0')
594 param_list = param_list[:param_len]
Simon Glass66c1a9f2012-03-22 19:15:53 -0700595 pos += (param_len + 4) & ~3
Simon Glassdf95dd22012-03-13 15:46:16 -0700596
597 # Work through the parameters one at a time, adding each value
598 new_data = ''
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700599 upto = 0
Simon Glassdf95dd22012-03-13 15:46:16 -0700600 for param in param_list:
Simon Glass2c48ddf2012-03-23 16:55:22 -0700601 value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
Simon Glassdf95dd22012-03-13 15:46:16 -0700602 if param == 'm' :
Simon Glass1a77ded2012-03-15 21:00:49 -0700603 mem_type = fdt.GetString('/dmc', 'mem-type')
Simon Glassdf95dd22012-03-13 15:46:16 -0700604 mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3']
605 if not mem_type in mem_types:
606 raise CmdError("Unknown memory type '%s'" % mem_type)
607 value = mem_types.index(mem_type)
608 self._out.Info(' Memory type: %s (%d)' % (mem_type, value))
Doug Andersonee46cfe2012-05-18 09:53:08 -0700609 elif param == 'M' :
610 mem_manuf = fdt.GetString('/dmc', 'mem-manuf')
611 mem_manufs = ['autodetect', 'elpida', 'samsung']
612 if not mem_manuf in mem_manufs:
613 raise CmdError("Unknown memory manufacturer: '%s'" % mem_manuf)
614 value = mem_manufs.index(mem_manuf)
615 self._out.Info(' Memory manufacturer: %s (%d)' % (mem_manuf, value))
Simon Glassdf95dd22012-03-13 15:46:16 -0700616 elif param == 'v':
617 value = 31
618 self._out.Info(' Memory interleave: %#0x' % value)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700619 elif param == 'u':
Simon Glass3b404092012-05-23 13:10:36 -0700620 value = (spl_load_size + 0xfff) & ~0xfff
621 self._out.Info(' U-Boot size: %#0x (rounded up from %#0x)' %
622 (value, spl_load_size))
Simon Glass559b6612012-05-23 13:28:45 -0700623 elif param == 'b':
624 # These values come from enum boot_mode in U-Boot's cpu.h
625 if self.spl_source == 'straps':
626 value = 32
627 elif self.spl_source == 'emmc':
628 value = 4
629 elif self.spl_source == 'spi':
630 value = 20
631 elif self.spl_source == 'usb':
632 value = 33
633 else:
634 raise CmdError("Invalid boot source '%s'" % self.spl_source)
635 self._out.Info(' Boot source: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700636 else:
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700637 self._out.Warning("Unknown machine parameter type '%s'" % param)
Simon Glass2c48ddf2012-03-23 16:55:22 -0700638 self._out.Info(' Unknown value: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700639 new_data += struct.pack('<L', value)
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700640 upto += 4
Simon Glassdf95dd22012-03-13 15:46:16 -0700641
642 # Put the data into our block.
643 data = data[:pos] + new_data + data[pos + len(new_data):]
644 self._out.Info('BL2 configuration complete')
645 return data
646
Simon Glasse5e8afb2012-05-23 11:19:23 -0700647 def _UpdateChecksum(self, data):
648 """Update the BL2 checksum.
649
650 The checksum is a 4 byte sum of all the bytes in the image before the
651 last 4 bytes (which hold the checksum).
652
653 Args:
654 data: The BL2 data to update.
655
656 Returns:
657 The new contents of the BL2 data, after updating the checksum.
658 """
659 checksum = 0
660 for ch in data[:-4]:
661 checksum += ord(ch)
662 return data[:-4] + struct.pack('<L', checksum & 0xffffffff)
663
Simon Glass559b6612012-05-23 13:28:45 -0700664 def ConfigureExynosBl2(self, fdt, spl_load_size, orig_bl2, name=''):
Simon Glass7e199222012-03-13 15:51:18 -0700665 """Configure an Exynos BL2 binary for our needs.
666
667 We create a new modified BL2 and return its filename.
668
669 Args:
670 fdt: Device tree containing the parameter values.
Simon Glass3b404092012-05-23 13:10:36 -0700671 spl_load_size: Size of U-Boot image that SPL must load
Simon Glass7e199222012-03-13 15:51:18 -0700672 orig_bl2: Filename of original BL2 file to modify.
673 """
Simon Glass66c1a9f2012-03-22 19:15:53 -0700674 self._out.Info('Configuring BL2')
Simon Glass559b6612012-05-23 13:28:45 -0700675 bl2 = os.path.join(self._tools.outdir, 'updated-spl%s.bin' % name)
Simon Glass66c1a9f2012-03-22 19:15:53 -0700676 data = self._tools.ReadFile(orig_bl2)
677 self._tools.WriteFile(bl2, data)
Simon Glass7e199222012-03-13 15:51:18 -0700678
679 # Locate the parameter block
680 data = self._tools.ReadFile(bl2)
681 marker = struct.pack('<L', 0xdeadbeef)
682 pos = data.rfind(marker)
683 if not pos:
684 raise CmdError("Could not find machine parameter block in '%s'" %
685 orig_bl2)
Simon Glass3b404092012-05-23 13:10:36 -0700686 data = self._UpdateBl2Parameters(fdt, spl_load_size, data, pos)
Simon Glasse5e8afb2012-05-23 11:19:23 -0700687 data = self._UpdateChecksum(data)
Simon Glass7e199222012-03-13 15:51:18 -0700688 self._tools.WriteFile(bl2, data)
689 return bl2
690
Simon Glass89b86b82011-07-17 23:49:49 -0700691 def _PackOutput(self, msg):
692 """Helper function to write output from PackFirmware (verbose level 2).
693
694 This is passed to PackFirmware for it to use to write output.
695
696 Args:
697 msg: Message to display.
698 """
699 self._out.Notice(msg)
700
Simon Glass439fe7a2012-03-09 16:19:34 -0800701 def _BuildBlob(self, pack, fdt, blob_type):
702 """Build the blob data for a particular blob type.
703
704 Args:
705 blob_type: The type of blob to create data for. Supported types are:
706 coreboot A coreboot image (ROM plus U-boot and .dtb payloads).
707 signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb).
708 """
709 if blob_type == 'coreboot':
710 coreboot = self._CreateCorebootStub(self.uboot_fname,
711 self.coreboot_fname, fdt, self.seabios_fname)
712 pack.AddProperty('coreboot', coreboot)
713 pack.AddProperty('image', coreboot)
714 elif blob_type == 'signed':
715 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt,
716 self.postload_fname)
717 pack.AddProperty('bootstub', bootstub)
718 pack.AddProperty('signed', signed)
719 pack.AddProperty('image', signed)
Simon Glass7e199222012-03-13 15:51:18 -0700720 elif blob_type == 'exynos-bl1':
721 pack.AddProperty(blob_type, self.exynos_bl1)
722 elif blob_type == 'exynos-bl2':
Simon Glass3b404092012-05-23 13:10:36 -0700723 spl_load_size = os.stat(pack.GetProperty('boot+dtb')).st_size
724 bl2 = self.ConfigureExynosBl2(fdt, spl_load_size, self.exynos_bl2)
Simon Glass7e199222012-03-13 15:51:18 -0700725 pack.AddProperty(blob_type, bl2)
Simon Glass439fe7a2012-03-09 16:19:34 -0800726 elif pack.GetProperty(blob_type):
727 pass
728 else:
729 raise CmdError("Unknown blob type '%s' required in flash map" %
730 blob_type)
731
Simon Glass290a1802011-07-17 13:54:32 -0700732 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700733 """Create a full firmware image, along with various by-products.
734
735 This uses the provided u-boot.bin, fdt and bct to create a firmware
736 image containing all the required parts. If the GBB is not supplied
737 then this will just return a signed U-Boot as the image.
738
739 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200740 gbb: Full path to the GBB file, or empty if a GBB is not required.
741 fdt: Fdt object containing required information.
742
743 Returns:
744 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700745
746 Raises:
747 CmdError if a command fails.
748 """
Simon Glass02d124a2012-03-02 14:47:20 -0800749 self._out.Notice("Model: %s" % fdt.GetString('/', 'model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700750
Simon Glass439fe7a2012-03-09 16:19:34 -0800751 # Get the flashmap so we know what to build
752 pack = PackFirmware(self._tools, self._out)
753 pack.SelectFdt(fdt)
754
755 # Get all our blobs ready
756 pack.AddProperty('boot', self.uboot_fname)
Simon Glass07267952012-06-08 12:45:13 -0700757 pack.AddProperty('skeleton', self.skeleton_fname)
Simon Glass50f74602012-03-15 21:04:25 -0700758
759 # Make a copy of the fdt for the bootstub
760 fdt_data = self._tools.ReadFile(fdt.fname)
761 uboot_data = self._tools.ReadFile(self.uboot_fname)
762 uboot_copy = os.path.join(self._tools.outdir, 'u-boot.bin')
763 self._tools.WriteFile(uboot_copy, uboot_data)
764
765 bootstub = os.path.join(self._tools.outdir, 'u-boot-dtb.bin')
766 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
767 pack.AddProperty('boot+dtb', bootstub)
768
Simon Glass439fe7a2012-03-09 16:19:34 -0800769 pack.AddProperty('gbb', self.uboot_fname)
Simon Glass07267952012-06-08 12:45:13 -0700770 for blob_type in pack.GetBlobList():
Simon Glass439fe7a2012-03-09 16:19:34 -0800771 self._BuildBlob(pack, fdt, blob_type)
Simon Glass89b86b82011-07-17 23:49:49 -0700772
773 if gbb:
Simon Glasse76bf7b2012-03-13 15:34:41 -0700774 pack.RequireAllEntries()
Hung-Te Lina7462e72011-07-27 19:17:10 +0800775 fwid = '.'.join([
Simon Glass02d124a2012-03-02 14:47:20 -0800776 re.sub('[ ,]+', '_', fdt.GetString('/', 'model')),
Hung-Te Lina7462e72011-07-27 19:17:10 +0800777 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700778 self._out.Notice('Firmware ID: %s' % fwid)
Simon Glass439fe7a2012-03-09 16:19:34 -0800779 pack.AddProperty('fwid', fwid)
780 pack.AddProperty('gbb', gbb)
781 pack.AddProperty('keydir', self._keydir)
Simon Glassc90cf582012-03-13 15:40:47 -0700782
783 pack.CheckProperties()
784 image = os.path.join(self._tools.outdir, 'image.bin')
785 pack.PackImage(self._tools.outdir, image)
786 pack.AddProperty('image', image)
Simon Glass89b86b82011-07-17 23:49:49 -0700787
Simon Glass439fe7a2012-03-09 16:19:34 -0800788 image = pack.GetProperty('image')
Simon Glass89b86b82011-07-17 23:49:49 -0700789 self._tools.OutputSize('Final image', image)
Simon Glassc90cf582012-03-13 15:40:47 -0700790 return image, pack
Simon Glass89b86b82011-07-17 23:49:49 -0700791
Simon Glass290a1802011-07-17 13:54:32 -0700792 def SelectFdt(self, fdt_fname):
793 """Select an FDT to control the firmware bundling
794
795 Args:
796 fdt_fname: The filename of the fdt to use.
797
Simon Glassc0f3dc62011-08-09 14:19:05 -0700798 Returns:
799 The Fdt object of the original fdt file, which we will not modify.
800
Simon Glass290a1802011-07-17 13:54:32 -0700801 We make a copy of this which will include any on-the-fly changes we want
802 to make.
803 """
804 self._fdt_fname = fdt_fname
805 self.CheckOptions()
806 fdt = Fdt(self._tools, self._fdt_fname)
Simon Glass29b96ad2012-03-09 15:34:33 -0800807 fdt.Compile()
Simon Glass290a1802011-07-17 13:54:32 -0700808 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700809 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700810
Simon Glassc90cf582012-03-13 15:40:47 -0700811 def Start(self, hardware_id, output_fname, show_map):
Simon Glass290a1802011-07-17 13:54:32 -0700812 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700813
814 - Checks options, tools, output directory, fdt.
815 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700816
817 Args:
Simon Glass56577572011-07-19 11:08:06 +1200818 hardware_id: Hardware ID to use for this board. If None, then the
819 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700820 output_fname: Output filename for the image. If this is not None, then
821 the final image will be copied here.
Simon Glassc90cf582012-03-13 15:40:47 -0700822 show_map: Show a flash map, with each area's name and position
Simon Glass290a1802011-07-17 13:54:32 -0700823
824 Returns:
825 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700826 """
Simon Glass89b86b82011-07-17 23:49:49 -0700827 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700828 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200829 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700830
831 # This creates the actual image.
Simon Glassc90cf582012-03-13 15:40:47 -0700832 image, pack = self._CreateImage(gbb, self.fdt)
833 if show_map:
834 pack.ShowMap()
Simon Glass290a1802011-07-17 13:54:32 -0700835 if output_fname:
836 shutil.copyfile(image, output_fname)
837 self._out.Notice("Output image '%s'" % output_fname)
Simon Glass794217e2012-06-07 11:40:37 -0700838 return image, pack.props