blob: 064546b379d72a960e807a1bf8dd2be38e93a3b4 [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 Glass290a1802011-07-17 13:54:32 -0700106
107 def SetDirs(self, keydir):
108 """Set up directories required for Bundle.
109
110 Args:
111 keydir: Directory containing keys to use for signing firmware.
112 """
113 self._keydir = keydir
114
Simon Glass6dcc2f22011-07-28 15:26:49 +1200115 def SetFiles(self, board, bct, uboot=None, bmpblk=None, coreboot=None,
Simon Glass775b5f92012-06-08 15:21:55 -0700116 postload=None, seabios=None, exynos_bl1=None, exynos_bl2=None):
Simon Glass290a1802011-07-17 13:54:32 -0700117 """Set up files required for Bundle.
118
119 Args:
120 board: The name of the board to target (e.g. tegra2_seaboard).
121 uboot: The filename of the u-boot.bin image to use.
122 bct: The filename of the binary BCT file to use.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800123 bmpblk: The filename of bitmap block file to use.
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700124 coreboot: The filename of the coreboot image to use (on x86)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200125 postload: The filename of the u-boot-post.bin image to use.
Vincent Palatinf7286772011-10-12 14:31:53 -0700126 seabios: The filename of the SeaBIOS payload to use if any.
Simon Glass290a1802011-07-17 13:54:32 -0700127 """
128 self._board = board
129 self.uboot_fname = uboot
130 self.bct_fname = bct
Hung-Te Lin5b649382011-08-03 15:01:16 +0800131 self.bmpblk_fname = bmpblk
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700132 self.coreboot_fname = coreboot
Simon Glass6dcc2f22011-07-28 15:26:49 +1200133 self.postload_fname = postload
Vincent Palatinf7286772011-10-12 14:31:53 -0700134 self.seabios_fname = seabios
Simon Glass7e199222012-03-13 15:51:18 -0700135 self.exynos_bl1 = exynos_bl1
136 self.exynos_bl2 = exynos_bl2
Simon Glass290a1802011-07-17 13:54:32 -0700137
138 def SetOptions(self, small):
139 """Set up options supported by Bundle.
140
141 Args:
142 small: Only create a signed U-Boot - don't produce the full packed
143 firmware image. This is useful for devs who want to replace just the
144 U-Boot part while keeping the keys, gbb, etc. the same.
145 """
146 self._small = small
147
148 def CheckOptions(self):
149 """Check provided options and select defaults."""
150 if not self._board:
151 raise ValueError('No board defined - please define a board to use')
Simon Glass493163b2011-09-14 11:19:57 -0700152 build_root = os.path.join('##', 'build', self._board, 'firmware')
Simon Glass881964d2012-04-04 11:34:09 -0700153 dir_name = os.path.join(build_root, 'dts')
Simon Glass290a1802011-07-17 13:54:32 -0700154 if not self._fdt_fname:
Simon Glassceff3ff2012-04-04 11:23:45 -0700155 # Figure out where the file should be, and the name we expect.
Simon Glassceff3ff2012-04-04 11:23:45 -0700156 base_name = re.sub('_', '-', self._board)
157
158 # In case the name exists with a prefix or suffix, find it.
159 wildcard = os.path.join(dir_name, '*%s*.dts' % base_name)
160 found_list = glob.glob(self._tools.Filename(wildcard))
161 if len(found_list) == 1:
162 self._fdt_fname = found_list[0]
163 else:
164 # We didn't find anything definite, so set up our expected name.
165 self._fdt_fname = os.path.join(dir_name, '%s.dts' % base_name)
166
Simon Glass881964d2012-04-04 11:34:09 -0700167 # Convert things like 'exynos5250-daisy' into a full path.
168 root, ext = os.path.splitext(self._fdt_fname)
169 if not ext and not os.path.dirname(root):
170 self._fdt_fname = os.path.join(dir_name, '%s.dts' % root)
171
Simon Glass290a1802011-07-17 13:54:32 -0700172 if not self.uboot_fname:
173 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
174 if not self.bct_fname:
175 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass2a7f0b32011-08-26 11:25:17 -0700176 if not self.bmpblk_fname:
177 self.bmpblk_fname = os.path.join(build_root, 'default.bmpblk')
Simon Glass1d376832012-03-15 20:50:54 -0700178 if not self.exynos_bl1:
179 self.exynos_bl1 = os.path.join(build_root, 'E5250.nbl1.bin')
180 if not self.exynos_bl2:
181 self.exynos_bl2 = os.path.join(build_root, 'smdk5250-spl.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700182
Simon Glass75759302012-03-15 20:26:53 -0700183 def GetFiles(self):
184 """Get a list of files that we know about.
185
186 This is the opposite of SetFiles except that we may have put in some
187 default names. It returns a dictionary containing the filename for
188 each of a number of pre-defined files.
189
190 Returns:
191 Dictionary, with one entry for each file.
192 """
193 file_list = {
194 'bct' : self.bct_fname,
195 'exynos-bl1' : self.exynos_bl1,
196 'exynos-bl2' : self.exynos_bl2,
197 }
198 return file_list
199
Simon Glass56577572011-07-19 11:08:06 +1200200 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700201 """Create a GBB for the image.
202
Simon Glass56577572011-07-19 11:08:06 +1200203 Args:
204 hardware_id: Hardware ID to use for this board. If None, then the
205 default from the Fdt will be used
206
Simon Glass89b86b82011-07-17 23:49:49 -0700207 Returns:
208 Path of the created GBB file.
209
210 Raises:
211 CmdError if a command fails.
212 """
Simon Glass56577572011-07-19 11:08:06 +1200213 if not hardware_id:
Simon Glass02d124a2012-03-02 14:47:20 -0800214 hardware_id = self.fdt.GetString('/config', 'hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700215 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700216 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700217
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800218 chromeos_config = self.fdt.GetProps("/chromeos-config")
219 if 'fast-developer-mode' not in chromeos_config:
220 gbb_flags = 0
221 else:
222 self._out.Notice("Enabling fast-developer-mode.")
223 gbb_flags = 1
224
Simon Glass89b86b82011-07-17 23:49:49 -0700225 self._out.Progress('Creating GBB')
226 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
227 sizes = ['%#x' % size for size in sizes]
228 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700229 keydir = self._tools.Filename(self._keydir)
230 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700231 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200232 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700233 '--rootkey=%s/root_key.vbpubk' % keydir,
234 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Simon Glass2a7f0b32011-08-26 11:25:17 -0700235 '--bmpfv=%s' % self._tools.Filename(self.bmpblk_fname),
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800236 '--flags=%d' % gbb_flags,
Simon Glass89b86b82011-07-17 23:49:49 -0700237 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700238 cwd=odir)
239 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700240
Simon Glasse13ee2c2011-07-28 08:12:28 +1200241 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700242 """Sign an image so that the Tegra SOC will boot it.
243
244 Args:
245 bct: BCT file to use.
246 bootstub: Boot stub (U-Boot + fdt) file to sign.
247 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700248
249 Returns:
250 filename of signed image.
251
252 Raises:
253 CmdError if a command fails.
254 """
255 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200256 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700257 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200258 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700259 fd = open(config, 'w')
260 fd.write('Version = 1;\n')
261 fd.write('Redundancy = 1;\n')
262 fd.write('Bctfile = %s;\n' % bct)
Doug Anderson0eeb0742011-09-15 18:11:40 -0700263
264 # TODO(dianders): Right now, we don't have enough space in our flash map
265 # for two copies of the BCT when we're using NAND, so hack it to 1. Not
266 # sure what this does for reliability, but at least things will fit...
267 is_nand = "NvBootDevType_Nand" in self._tools.Run('bct_dump', [bct])
268 if is_nand:
269 fd.write('Bctcopy = 1;\n')
270
Simon Glass89b86b82011-07-17 23:49:49 -0700271 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
272 text_base))
Doug Anderson0eeb0742011-09-15 18:11:40 -0700273
Simon Glass89b86b82011-07-17 23:49:49 -0700274 fd.close()
275
276 self._tools.Run('cbootimage', [config, signed])
277 self._tools.OutputSize('BCT', bct)
278 self._tools.OutputSize('Signed image', signed)
279 return signed
280
Doug Anderson86ce5f42011-07-27 10:40:18 -0700281 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700282 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700283
284 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700285 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700286 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700287 """
Simon Glass290a1802011-07-17 13:54:32 -0700288 if bootcmd:
Simon Glass02d124a2012-03-02 14:47:20 -0800289 self.fdt.PutString('/config', 'bootcmd', bootcmd)
290 self.fdt.PutInteger('/config', 'bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700291 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700292
Simon Glassa4934b72012-05-09 13:35:02 -0700293 def SetNodeEnabled(self, node_name, enabled):
294 """Set whether an node is enabled or disabled.
295
296 This simply sets the 'status' property of a node to "ok", or "disabled".
297
298 The node should either be a full path to the node (like '/uart@10200000')
299 or an alias property.
300
301 Aliases are supported like this:
302
303 aliases {
304 console = "/uart@10200000";
305 };
306
307 pointing to a node:
308
309 uart@10200000 {
310 status = "ok";
311 };
312
313 In this case, this function takes the name of the alias ('console' in
314 this case) and updates the status of the node that is pointed to, to
315 either ok or disabled. If the alias does not exist, a warning is
316 displayed.
317
318 Args:
319 node_name: Name of node (e.g. '/uart@10200000') or alias alias
320 (e.g. 'console') to adjust
321 enabled: True to enable, False to disable
322 """
323 # Look up the alias if this is an alias reference
324 if not node_name.startswith('/'):
325 lookup = self.fdt.GetString('/aliases', node_name, '')
326 if not lookup:
327 self._out.Warning("Cannot find alias '%s' - ignoring" % node_name)
328 return
329 node_name = lookup
330 if enabled:
331 status = 'ok'
332 else:
333 status = 'disabled'
334 self.fdt.PutString(node_name, 'status', status)
335
336 def AddEnableList(self, enable_list):
337 """Process a list of nodes to enable/disable.
338
339 Args:
340 config_list: List of (node, value) tuples to add to the fdt. For each
341 tuple:
342 node: The fdt node to write to will be <node> or pointed to by
343 /aliases/<node>. We can tell which
344 value: 0 to disable the node, 1 to enable it
345 """
346 if enable_list:
347 for node_name, enabled in enable_list:
348 try:
349 enabled = int(enabled)
350 if enabled not in (0, 1):
351 raise ValueError
352 except ValueError as str:
353 raise CmdError("Invalid enable option value '%s' "
354 "(should be 0 or 1)" % enabled)
355 self.SetNodeEnabled(node_name, enabled)
356
Simon Glass290a1802011-07-17 13:54:32 -0700357 def AddConfigList(self, config_list, use_int=False):
358 """Add a list of config items to the fdt.
359
360 Normally these values are written to the fdt as strings, but integers
361 are also supported, in which case the values will be converted to integers
362 (if necessary) before being stored.
363
364 Args:
365 config_list: List of (config, value) tuples to add to the fdt. For each
366 tuple:
367 config: The fdt node to write to will be /config/<config>.
368 value: An integer or string value to write.
369 use_int: True to only write integer values.
370
371 Raises:
372 CmdError: if a value is required to be converted to integer but can't be.
373 """
374 if config_list:
375 for config in config_list:
376 value = config[1]
377 if use_int:
378 try:
379 value = int(value)
380 except ValueError as str:
381 raise CmdError("Cannot convert config option '%s' to integer" %
382 value)
383 if type(value) == type(1):
Simon Glass02d124a2012-03-02 14:47:20 -0800384 self.fdt.PutInteger('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700385 else:
Simon Glass02d124a2012-03-02 14:47:20 -0800386 self.fdt.PutString('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700387
Simon Glass7c2d5572011-11-15 14:47:08 -0800388 def DecodeTextBase(self, data):
389 """Look at a U-Boot image and try to decode its TEXT_BASE.
390
391 This works because U-Boot has a header with the value 0x12345678
392 immediately followed by the TEXT_BASE value. We can therefore read this
393 from the image with some certainty. We check only the first 40 words
394 since the header should be within that region.
395
396 Args:
397 data: U-Boot binary data
398
399 Returns:
400 Text base (integer) or None if none was found
401 """
402 found = False
403 for i in range(0, 160, 4):
404 word = data[i:i + 4]
405
406 # TODO(sjg): This does not cope with a big-endian target
407 value = struct.unpack('<I', word)[0]
408 if found:
409 return value
410 if value == 0x12345678:
411 found = True
412
413 return None
414
415 def CalcTextBase(self, name, fdt, fname):
416 """Calculate the TEXT_BASE to use for U-Boot.
417
418 Normally this value is in the fdt, so we just read it from there. But as
419 a second check we look at the image itself in case this is different, and
420 switch to that if it is.
421
422 This allows us to flash any U-Boot even if its TEXT_BASE is different.
423 This is particularly useful with upstream U-Boot which uses a different
424 value (which we will move to).
425 """
426 data = self._tools.ReadFile(fname)
Simon Glass02d124a2012-03-02 14:47:20 -0800427 fdt_text_base = fdt.GetInt('/chromeos-config', 'textbase')
Simon Glass7c2d5572011-11-15 14:47:08 -0800428 text_base = self.DecodeTextBase(data)
Simon Glass60a40af2012-06-07 11:54:17 -0700429 self._out.Info('TEXT_BASE: fdt says %#x, %s says %#x' % (fdt_text_base,
430 fname, text_base))
Simon Glass7c2d5572011-11-15 14:47:08 -0800431
432 # If they are different, issue a warning and switch over.
433 if text_base and text_base != fdt_text_base:
434 self._out.Warning("TEXT_BASE %x in %sU-Boot doesn't match "
435 "fdt value of %x. Using %x" % (text_base, name,
436 fdt_text_base, text_base))
437 fdt_text_base = text_base
438 return fdt_text_base
439
Simon Glass6dcc2f22011-07-28 15:26:49 +1200440 def _CreateBootStub(self, uboot, base_fdt, postload):
Simon Glass89b86b82011-07-17 23:49:49 -0700441 """Create a boot stub and a signed boot stub.
442
Simon Glass6dcc2f22011-07-28 15:26:49 +1200443 For postload:
444 We add a /config/postload-text-offset entry to the signed bootstub's
445 fdt so that U-Boot can find the postload code.
446
447 The raw (unsigned) bootstub will have a value of -1 for this since we will
448 simply append the postload code to the bootstub and it can find it there.
449 This will be used for RW A/B firmware.
450
451 For the signed case this value will specify where in the flash to find
452 the postload code. This will be used for RO firmware.
453
Simon Glass89b86b82011-07-17 23:49:49 -0700454 Args:
455 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass29b96ad2012-03-09 15:34:33 -0800456 base_fdt: Fdt object containing the flat device tree.
Simon Glass6dcc2f22011-07-28 15:26:49 +1200457 postload: Path to u-boot-post.bin, or None if none.
Simon Glass89b86b82011-07-17 23:49:49 -0700458
459 Returns:
460 Tuple containing:
Simon Glass6dcc2f22011-07-28 15:26:49 +1200461 Full path to bootstub (uboot + fdt(-1) + postload).
462 Full path to signed (uboot + fdt(flash pos) + bct) + postload.
Simon Glass89b86b82011-07-17 23:49:49 -0700463
464 Raises:
465 CmdError if a command fails.
466 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200467 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass7c2d5572011-11-15 14:47:08 -0800468 text_base = self.CalcTextBase('', self.fdt, uboot)
Simon Glass89b86b82011-07-17 23:49:49 -0700469 uboot_data = self._tools.ReadFile(uboot)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200470
471 # Make a copy of the fdt for the bootstub
472 fdt = base_fdt.Copy(os.path.join(self._tools.outdir, 'bootstub.dtb'))
Simon Glass02d124a2012-03-02 14:47:20 -0800473 fdt.PutInteger('/config', 'postload-text-offset', 0xffffffff);
Simon Glass290a1802011-07-17 13:54:32 -0700474 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200475
Simon Glass89b86b82011-07-17 23:49:49 -0700476 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700477 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
478 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700479 self._tools.OutputSize('Combined binary', bootstub)
480
Simon Glasse13ee2c2011-07-28 08:12:28 +1200481 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700482 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700483 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200484 bootstub, text_base)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200485
486 signed_postload = os.path.join(self._tools.outdir, 'signed-postload.bin')
487 data = self._tools.ReadFile(signed)
488
489 if postload:
490 # We must add postload to the bootstub since A and B will need to
491 # be able to find it without the /config/postload-text-offset mechanism.
492 bs_data = self._tools.ReadFile(bootstub)
493 bs_data += self._tools.ReadFile(postload)
494 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt-postload.bin')
495 self._tools.WriteFile(bootstub, bs_data)
496 self._tools.OutputSize('Combined binary with postload', bootstub)
497
498 # Now that we know the file size, adjust the fdt and re-sign
499 postload_bootstub = os.path.join(self._tools.outdir, 'postload.bin')
Simon Glass02d124a2012-03-02 14:47:20 -0800500 fdt.PutInteger('/config', 'postload-text-offset', len(data))
Simon Glass6dcc2f22011-07-28 15:26:49 +1200501 fdt_data = self._tools.ReadFile(fdt.fname)
502 self._tools.WriteFile(postload_bootstub, uboot_data + fdt_data)
503 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
504 postload_bootstub, text_base)
505 if len(data) != os.path.getsize(signed):
506 raise CmdError('Signed file size changed from %d to %d after updating '
507 'fdt' % (len(data), os.path.getsize(signed)))
508
509 # Re-read the signed image, and add the post-load binary.
510 data = self._tools.ReadFile(signed)
511 data += self._tools.ReadFile(postload)
512 self._tools.OutputSize('Post-load binary', postload)
513
514 self._tools.WriteFile(signed_postload, data)
515 self._tools.OutputSize('Final bootstub with postload', signed_postload)
516
517 return bootstub, signed_postload
Simon Glass89b86b82011-07-17 23:49:49 -0700518
Vincent Palatinf7286772011-10-12 14:31:53 -0700519 def _CreateCorebootStub(self, uboot, coreboot, fdt, seabios):
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700520 """Create a coreboot boot stub.
521
522 Args:
523 uboot: Path to u-boot.bin (may be chroot-relative)
524 coreboot: Path to coreboot.rom
525 fdt: Device Tree
Vincent Palatinf7286772011-10-12 14:31:53 -0700526 seabios: Path to SeaBIOS payload binary or None
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700527
528 Returns:
529 Full path to bootstub (coreboot + uboot + fdt).
530
531 Raises:
532 CmdError if a command fails.
533 """
534 bootstub = os.path.join(self._tools.outdir, 'coreboot-full.rom')
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700535 uboot_elf = uboot.replace(".bin", ".elf")
536 shutil.copyfile(coreboot, bootstub)
Vincent Palatinf7286772011-10-12 14:31:53 -0700537 if seabios:
Simon Glass146cb8e2012-03-09 15:51:24 -0800538 self._tools.Run('cbfstool', [bootstub, 'add-payload', seabios,
Vincent Palatinf7286772011-10-12 14:31:53 -0700539 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800540 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700541 'img/U-Boot', 'lzma'])
542 else:
Simon Glass146cb8e2012-03-09 15:51:24 -0800543 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700544 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800545 self._tools.Run('cbfstool', [bootstub, 'add', fdt.fname, 'u-boot.dtb',
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700546 '0xac'])
547 return bootstub
548
Simon Glass3b404092012-05-23 13:10:36 -0700549 def _UpdateBl2Parameters(self, fdt, spl_load_size, data, pos):
Simon Glassdf95dd22012-03-13 15:46:16 -0700550 """Update the parameters in a BL2 blob.
551
552 We look at the list in the parameter block, extract the value of each
553 from the device tree, and write that value to the parameter block.
554
555 Args:
556 fdt: Device tree containing the parameter values.
Simon Glass3b404092012-05-23 13:10:36 -0700557 spl_load_size: Size of U-Boot image that SPL must load
Simon Glassdf95dd22012-03-13 15:46:16 -0700558 data: The BL2 data.
559 pos: The position of the start of the parameter block.
560
561 Returns:
562 The new contents of the parameter block, after updating.
563 """
Simon Glassdf95dd22012-03-13 15:46:16 -0700564 version, size = struct.unpack('<2L', data[pos + 4:pos + 12])
565 if version != 1:
566 raise CmdError("Cannot update machine parameter block version '%d'" %
567 version)
568 if size < 0 or pos + size > len(data):
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700569 raise CmdError("Machine parameter block size %d is invalid: "
570 "pos=%d, size=%d, space=%d, len=%d" %
571 (size, pos, size, len(data) - pos, len(data)))
Simon Glassdf95dd22012-03-13 15:46:16 -0700572
573 # Move past the header and read the parameter list, which is terminated
574 # with \0.
575 pos += 12
576 param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0]
577 param_len = param_list.find('\0')
578 param_list = param_list[:param_len]
Simon Glass66c1a9f2012-03-22 19:15:53 -0700579 pos += (param_len + 4) & ~3
Simon Glassdf95dd22012-03-13 15:46:16 -0700580
581 # Work through the parameters one at a time, adding each value
582 new_data = ''
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700583 upto = 0
Simon Glassdf95dd22012-03-13 15:46:16 -0700584 for param in param_list:
Simon Glass2c48ddf2012-03-23 16:55:22 -0700585 value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
Simon Glassdf95dd22012-03-13 15:46:16 -0700586 if param == 'm' :
Simon Glass1a77ded2012-03-15 21:00:49 -0700587 mem_type = fdt.GetString('/dmc', 'mem-type')
Simon Glassdf95dd22012-03-13 15:46:16 -0700588 mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3']
589 if not mem_type in mem_types:
590 raise CmdError("Unknown memory type '%s'" % mem_type)
591 value = mem_types.index(mem_type)
592 self._out.Info(' Memory type: %s (%d)' % (mem_type, value))
Doug Andersonee46cfe2012-05-18 09:53:08 -0700593 elif param == 'M' :
594 mem_manuf = fdt.GetString('/dmc', 'mem-manuf')
595 mem_manufs = ['autodetect', 'elpida', 'samsung']
596 if not mem_manuf in mem_manufs:
597 raise CmdError("Unknown memory manufacturer: '%s'" % mem_manuf)
598 value = mem_manufs.index(mem_manuf)
599 self._out.Info(' Memory manufacturer: %s (%d)' % (mem_manuf, value))
Simon Glassdf95dd22012-03-13 15:46:16 -0700600 elif param == 'v':
601 value = 31
602 self._out.Info(' Memory interleave: %#0x' % value)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700603 elif param == 'u':
Simon Glass3b404092012-05-23 13:10:36 -0700604 value = (spl_load_size + 0xfff) & ~0xfff
605 self._out.Info(' U-Boot size: %#0x (rounded up from %#0x)' %
606 (value, spl_load_size))
Simon Glass559b6612012-05-23 13:28:45 -0700607 elif param == 'b':
608 # These values come from enum boot_mode in U-Boot's cpu.h
609 if self.spl_source == 'straps':
610 value = 32
611 elif self.spl_source == 'emmc':
612 value = 4
613 elif self.spl_source == 'spi':
614 value = 20
615 elif self.spl_source == 'usb':
616 value = 33
617 else:
618 raise CmdError("Invalid boot source '%s'" % self.spl_source)
619 self._out.Info(' Boot source: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700620 else:
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700621 self._out.Warning("Unknown machine parameter type '%s'" % param)
Simon Glass2c48ddf2012-03-23 16:55:22 -0700622 self._out.Info(' Unknown value: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700623 new_data += struct.pack('<L', value)
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700624 upto += 4
Simon Glassdf95dd22012-03-13 15:46:16 -0700625
626 # Put the data into our block.
627 data = data[:pos] + new_data + data[pos + len(new_data):]
628 self._out.Info('BL2 configuration complete')
629 return data
630
Simon Glasse5e8afb2012-05-23 11:19:23 -0700631 def _UpdateChecksum(self, data):
632 """Update the BL2 checksum.
633
634 The checksum is a 4 byte sum of all the bytes in the image before the
635 last 4 bytes (which hold the checksum).
636
637 Args:
638 data: The BL2 data to update.
639
640 Returns:
641 The new contents of the BL2 data, after updating the checksum.
642 """
643 checksum = 0
644 for ch in data[:-4]:
645 checksum += ord(ch)
646 return data[:-4] + struct.pack('<L', checksum & 0xffffffff)
647
Simon Glass559b6612012-05-23 13:28:45 -0700648 def ConfigureExynosBl2(self, fdt, spl_load_size, orig_bl2, name=''):
Simon Glass7e199222012-03-13 15:51:18 -0700649 """Configure an Exynos BL2 binary for our needs.
650
651 We create a new modified BL2 and return its filename.
652
653 Args:
654 fdt: Device tree containing the parameter values.
Simon Glass3b404092012-05-23 13:10:36 -0700655 spl_load_size: Size of U-Boot image that SPL must load
Simon Glass7e199222012-03-13 15:51:18 -0700656 orig_bl2: Filename of original BL2 file to modify.
657 """
Simon Glass66c1a9f2012-03-22 19:15:53 -0700658 self._out.Info('Configuring BL2')
Simon Glass559b6612012-05-23 13:28:45 -0700659 bl2 = os.path.join(self._tools.outdir, 'updated-spl%s.bin' % name)
Simon Glass66c1a9f2012-03-22 19:15:53 -0700660 data = self._tools.ReadFile(orig_bl2)
661 self._tools.WriteFile(bl2, data)
Simon Glass7e199222012-03-13 15:51:18 -0700662
663 # Locate the parameter block
664 data = self._tools.ReadFile(bl2)
665 marker = struct.pack('<L', 0xdeadbeef)
666 pos = data.rfind(marker)
667 if not pos:
668 raise CmdError("Could not find machine parameter block in '%s'" %
669 orig_bl2)
Simon Glass3b404092012-05-23 13:10:36 -0700670 data = self._UpdateBl2Parameters(fdt, spl_load_size, data, pos)
Simon Glasse5e8afb2012-05-23 11:19:23 -0700671 data = self._UpdateChecksum(data)
Simon Glass7e199222012-03-13 15:51:18 -0700672 self._tools.WriteFile(bl2, data)
673 return bl2
674
Simon Glass89b86b82011-07-17 23:49:49 -0700675 def _PackOutput(self, msg):
676 """Helper function to write output from PackFirmware (verbose level 2).
677
678 This is passed to PackFirmware for it to use to write output.
679
680 Args:
681 msg: Message to display.
682 """
683 self._out.Notice(msg)
684
Simon Glass439fe7a2012-03-09 16:19:34 -0800685 def _BuildBlob(self, pack, fdt, blob_type):
686 """Build the blob data for a particular blob type.
687
688 Args:
689 blob_type: The type of blob to create data for. Supported types are:
690 coreboot A coreboot image (ROM plus U-boot and .dtb payloads).
691 signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb).
692 """
693 if blob_type == 'coreboot':
694 coreboot = self._CreateCorebootStub(self.uboot_fname,
695 self.coreboot_fname, fdt, self.seabios_fname)
696 pack.AddProperty('coreboot', coreboot)
697 pack.AddProperty('image', coreboot)
698 elif blob_type == 'signed':
699 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt,
700 self.postload_fname)
701 pack.AddProperty('bootstub', bootstub)
702 pack.AddProperty('signed', signed)
703 pack.AddProperty('image', signed)
Simon Glass7e199222012-03-13 15:51:18 -0700704 elif blob_type == 'exynos-bl1':
705 pack.AddProperty(blob_type, self.exynos_bl1)
706 elif blob_type == 'exynos-bl2':
Simon Glass3b404092012-05-23 13:10:36 -0700707 spl_load_size = os.stat(pack.GetProperty('boot+dtb')).st_size
708 bl2 = self.ConfigureExynosBl2(fdt, spl_load_size, self.exynos_bl2)
Simon Glass7e199222012-03-13 15:51:18 -0700709 pack.AddProperty(blob_type, bl2)
Simon Glass439fe7a2012-03-09 16:19:34 -0800710 elif pack.GetProperty(blob_type):
711 pass
712 else:
713 raise CmdError("Unknown blob type '%s' required in flash map" %
714 blob_type)
715
Simon Glass290a1802011-07-17 13:54:32 -0700716 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700717 """Create a full firmware image, along with various by-products.
718
719 This uses the provided u-boot.bin, fdt and bct to create a firmware
720 image containing all the required parts. If the GBB is not supplied
721 then this will just return a signed U-Boot as the image.
722
723 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200724 gbb: Full path to the GBB file, or empty if a GBB is not required.
725 fdt: Fdt object containing required information.
726
727 Returns:
728 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700729
730 Raises:
731 CmdError if a command fails.
732 """
Simon Glass02d124a2012-03-02 14:47:20 -0800733 self._out.Notice("Model: %s" % fdt.GetString('/', 'model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700734
Simon Glass439fe7a2012-03-09 16:19:34 -0800735 # Get the flashmap so we know what to build
736 pack = PackFirmware(self._tools, self._out)
737 pack.SelectFdt(fdt)
738
739 # Get all our blobs ready
740 pack.AddProperty('boot', self.uboot_fname)
Simon Glass50f74602012-03-15 21:04:25 -0700741
742 # Make a copy of the fdt for the bootstub
743 fdt_data = self._tools.ReadFile(fdt.fname)
744 uboot_data = self._tools.ReadFile(self.uboot_fname)
745 uboot_copy = os.path.join(self._tools.outdir, 'u-boot.bin')
746 self._tools.WriteFile(uboot_copy, uboot_data)
747
748 bootstub = os.path.join(self._tools.outdir, 'u-boot-dtb.bin')
749 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
750 pack.AddProperty('boot+dtb', bootstub)
751
Simon Glass439fe7a2012-03-09 16:19:34 -0800752 pack.AddProperty('gbb', self.uboot_fname)
753 for blob_type in pack.GetBlobList(self.coreboot_fname is not None):
754 self._BuildBlob(pack, fdt, blob_type)
Simon Glass89b86b82011-07-17 23:49:49 -0700755
756 if gbb:
Simon Glasse76bf7b2012-03-13 15:34:41 -0700757 pack.RequireAllEntries()
Hung-Te Lina7462e72011-07-27 19:17:10 +0800758 fwid = '.'.join([
Simon Glass02d124a2012-03-02 14:47:20 -0800759 re.sub('[ ,]+', '_', fdt.GetString('/', 'model')),
Hung-Te Lina7462e72011-07-27 19:17:10 +0800760 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700761 self._out.Notice('Firmware ID: %s' % fwid)
Simon Glass439fe7a2012-03-09 16:19:34 -0800762 pack.AddProperty('fwid', fwid)
763 pack.AddProperty('gbb', gbb)
764 pack.AddProperty('keydir', self._keydir)
Simon Glassc90cf582012-03-13 15:40:47 -0700765
766 pack.CheckProperties()
767 image = os.path.join(self._tools.outdir, 'image.bin')
768 pack.PackImage(self._tools.outdir, image)
769 pack.AddProperty('image', image)
Simon Glass89b86b82011-07-17 23:49:49 -0700770
Simon Glass439fe7a2012-03-09 16:19:34 -0800771 image = pack.GetProperty('image')
Simon Glass89b86b82011-07-17 23:49:49 -0700772 self._tools.OutputSize('Final image', image)
Simon Glassc90cf582012-03-13 15:40:47 -0700773 return image, pack
Simon Glass89b86b82011-07-17 23:49:49 -0700774
Simon Glass290a1802011-07-17 13:54:32 -0700775 def SelectFdt(self, fdt_fname):
776 """Select an FDT to control the firmware bundling
777
778 Args:
779 fdt_fname: The filename of the fdt to use.
780
Simon Glassc0f3dc62011-08-09 14:19:05 -0700781 Returns:
782 The Fdt object of the original fdt file, which we will not modify.
783
Simon Glass290a1802011-07-17 13:54:32 -0700784 We make a copy of this which will include any on-the-fly changes we want
785 to make.
786 """
787 self._fdt_fname = fdt_fname
788 self.CheckOptions()
789 fdt = Fdt(self._tools, self._fdt_fname)
Simon Glass29b96ad2012-03-09 15:34:33 -0800790 fdt.Compile()
Simon Glass290a1802011-07-17 13:54:32 -0700791 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700792 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700793
Simon Glassc90cf582012-03-13 15:40:47 -0700794 def Start(self, hardware_id, output_fname, show_map):
Simon Glass290a1802011-07-17 13:54:32 -0700795 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700796
797 - Checks options, tools, output directory, fdt.
798 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700799
800 Args:
Simon Glass56577572011-07-19 11:08:06 +1200801 hardware_id: Hardware ID to use for this board. If None, then the
802 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700803 output_fname: Output filename for the image. If this is not None, then
804 the final image will be copied here.
Simon Glassc90cf582012-03-13 15:40:47 -0700805 show_map: Show a flash map, with each area's name and position
Simon Glass290a1802011-07-17 13:54:32 -0700806
807 Returns:
808 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700809 """
Simon Glass89b86b82011-07-17 23:49:49 -0700810 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700811 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200812 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700813
814 # This creates the actual image.
Simon Glassc90cf582012-03-13 15:40:47 -0700815 image, pack = self._CreateImage(gbb, self.fdt)
816 if show_map:
817 pack.ShowMap()
Simon Glass290a1802011-07-17 13:54:32 -0700818 if output_fname:
819 shutil.copyfile(image, output_fname)
820 self._out.Notice("Output image '%s'" % output_fname)
Simon Glass794217e2012-06-07 11:40:37 -0700821 return image, pack.props