blob: 9dfc52df631b4d160a28bece0e4114b1009d6b6e [file] [log] [blame]
Simon Glass89b86b82011-07-17 23:49:49 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This module builds a firmware image for a tegra-based board.
6
7This modules uses a few rudimentary other libraries for its activity.
8
9Here are the names we give to the various files we deal with. It is important
10to keep these consistent!
11
12 uboot u-boot.bin (with no device tree)
13 fdt the fdt blob
14 bct the BCT file
15 bootstub uboot + fdt
16 signed (uboot + fdt + bct) signed blob
17"""
18
19import os
20import re
21
22import cros_output
23from fdt import Fdt
24from pack_firmware import PackFirmware
25import shutil
Simon Glass7c2d5572011-11-15 14:47:08 -080026import struct
Simon Glass89b86b82011-07-17 23:49:49 -070027import tempfile
Simon Glass439fe7a2012-03-09 16:19:34 -080028from tools import CmdError
Simon Glass89b86b82011-07-17 23:49:49 -070029from tools import Tools
30from write_firmware import WriteFirmware
31
32# This data is required by bmpblk_utility. Does it ever change?
33# It was stored with the chromeos-bootimage ebuild, but we want
34# this utility to work outside the chroot.
35yaml_data = '''
36bmpblock: 1.0
37
38images:
39 devmode: DeveloperBmp/DeveloperBmp.bmp
40 recovery: RecoveryBmp/RecoveryBmp.bmp
41 rec_yuck: RecoveryNoOSBmp/RecoveryNoOSBmp.bmp
42 rec_insert: RecoveryMissingOSBmp/RecoveryMissingOSBmp.bmp
43
44screens:
45 dev_en:
46 - [0, 0, devmode]
47 rec_en:
48 - [0, 0, recovery]
49 yuck_en:
50 - [0, 0, rec_yuck]
51 ins_en:
52 - [0, 0, rec_insert]
53
54localizations:
55 - [ dev_en, rec_en, yuck_en, ins_en ]
56'''
57
58class Bundle:
Simon Glass290a1802011-07-17 13:54:32 -070059 """This class encapsulates the entire bundle firmware logic.
Simon Glass89b86b82011-07-17 23:49:49 -070060
Simon Glass290a1802011-07-17 13:54:32 -070061 Sequence of events:
62 bundle = Bundle(tools.Tools(), cros_output.Output())
63 bundle.SetDirs(...)
64 bundle.SetFiles(...)
65 bundle.SetOptions(...)
66 bundle.SelectFdt(fdt.Fdt('filename.dtb')
67 .. can call bundle.AddConfigList() if required
68 bundle.Start(...)
Simon Glass89b86b82011-07-17 23:49:49 -070069
Simon Glass290a1802011-07-17 13:54:32 -070070 Public properties:
71 fdt: The fdt object that we use for building our image. This wil be the
72 one specified by the user, except that we might add config options
73 to it. This is set up by SelectFdt() which must be called before
74 bundling starts.
75 uboot_fname: Full filename of the U-Boot binary we use.
76 bct_fname: Full filename of the BCT file we use.
77 """
Simon Glass89b86b82011-07-17 23:49:49 -070078
Simon Glass290a1802011-07-17 13:54:32 -070079 def __init__(self, tools, output):
80 """Set up a new Bundle object.
Simon Glass89b86b82011-07-17 23:49:49 -070081
Simon Glass290a1802011-07-17 13:54:32 -070082 Args:
83 tools: A tools.Tools object to use for external tools.
84 output: A cros_output.Output object to use for program output.
Simon Glass89b86b82011-07-17 23:49:49 -070085 """
Simon Glass290a1802011-07-17 13:54:32 -070086 self._tools = tools
87 self._out = output
88
89 # Set up the things we need to know in order to operate.
90 self._board = None # Board name, e.g. tegra2_seaboard.
91 self._fdt_fname = None # Filename of our FDT.
92 self.uboot_fname = None # Filename of our U-Boot binary.
93 self.bct_fname = None # Filename of our BCT file.
94 self.fdt = None # Our Fdt object.
Hung-Te Lin5b649382011-08-03 15:01:16 +080095 self.bmpblk_fname = None # Filename of our Bitmap Block
Stefan Reinauer8d79d362011-08-16 14:20:43 -070096 self.coreboot_fname = None # Filename of our coreboot binary.
Vincent Palatinf7286772011-10-12 14:31:53 -070097 self.seabios_fname = None # Filename of our SeaBIOS payload.
Simon Glass7e199222012-03-13 15:51:18 -070098 self.exynos_bl1 = None # Filename of Exynos BL1 (pre-boot)
99 self.exynos_bl2 = None # Filename of Exynos BL2 (SPL)
Simon Glass290a1802011-07-17 13:54:32 -0700100
101 def SetDirs(self, keydir):
102 """Set up directories required for Bundle.
103
104 Args:
105 keydir: Directory containing keys to use for signing firmware.
106 """
107 self._keydir = keydir
108
Simon Glass6dcc2f22011-07-28 15:26:49 +1200109 def SetFiles(self, board, bct, uboot=None, bmpblk=None, coreboot=None,
Simon Glass7e199222012-03-13 15:51:18 -0700110 postload=None, seabios=None, exynos_bl1=None, exynos_bl2=None):
Simon Glass290a1802011-07-17 13:54:32 -0700111 """Set up files required for Bundle.
112
113 Args:
114 board: The name of the board to target (e.g. tegra2_seaboard).
115 uboot: The filename of the u-boot.bin image to use.
116 bct: The filename of the binary BCT file to use.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800117 bmpblk: The filename of bitmap block file to use.
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700118 coreboot: The filename of the coreboot image to use (on x86)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200119 postload: The filename of the u-boot-post.bin image to use.
Vincent Palatinf7286772011-10-12 14:31:53 -0700120 seabios: The filename of the SeaBIOS payload to use if any.
Simon Glass290a1802011-07-17 13:54:32 -0700121 """
122 self._board = board
123 self.uboot_fname = uboot
124 self.bct_fname = bct
Hung-Te Lin5b649382011-08-03 15:01:16 +0800125 self.bmpblk_fname = bmpblk
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700126 self.coreboot_fname = coreboot
Simon Glass6dcc2f22011-07-28 15:26:49 +1200127 self.postload_fname = postload
Vincent Palatinf7286772011-10-12 14:31:53 -0700128 self.seabios_fname = seabios
Simon Glass7e199222012-03-13 15:51:18 -0700129 self.exynos_bl1 = exynos_bl1
130 self.exynos_bl2 = exynos_bl2
Simon Glass290a1802011-07-17 13:54:32 -0700131
132 def SetOptions(self, small):
133 """Set up options supported by Bundle.
134
135 Args:
136 small: Only create a signed U-Boot - don't produce the full packed
137 firmware image. This is useful for devs who want to replace just the
138 U-Boot part while keeping the keys, gbb, etc. the same.
139 """
140 self._small = small
141
142 def CheckOptions(self):
143 """Check provided options and select defaults."""
144 if not self._board:
145 raise ValueError('No board defined - please define a board to use')
Simon Glass493163b2011-09-14 11:19:57 -0700146 build_root = os.path.join('##', 'build', self._board, 'firmware')
Simon Glass290a1802011-07-17 13:54:32 -0700147 if not self._fdt_fname:
Simon Glass29b96ad2012-03-09 15:34:33 -0800148 self._fdt_fname = os.path.join(build_root, 'dts', '%s.dts' %
Simon Glass290a1802011-07-17 13:54:32 -0700149 re.sub('_', '-', self._board))
150 if not self.uboot_fname:
151 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
152 if not self.bct_fname:
153 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass2a7f0b32011-08-26 11:25:17 -0700154 if not self.bmpblk_fname:
155 self.bmpblk_fname = os.path.join(build_root, 'default.bmpblk')
Simon Glass1d376832012-03-15 20:50:54 -0700156 if not self.exynos_bl1:
157 self.exynos_bl1 = os.path.join(build_root, 'E5250.nbl1.bin')
158 if not self.exynos_bl2:
159 self.exynos_bl2 = os.path.join(build_root, 'smdk5250-spl.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700160
Simon Glass75759302012-03-15 20:26:53 -0700161 def GetFiles(self):
162 """Get a list of files that we know about.
163
164 This is the opposite of SetFiles except that we may have put in some
165 default names. It returns a dictionary containing the filename for
166 each of a number of pre-defined files.
167
168 Returns:
169 Dictionary, with one entry for each file.
170 """
171 file_list = {
172 'bct' : self.bct_fname,
173 'exynos-bl1' : self.exynos_bl1,
174 'exynos-bl2' : self.exynos_bl2,
175 }
176 return file_list
177
Simon Glass56577572011-07-19 11:08:06 +1200178 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700179 """Create a GBB for the image.
180
Simon Glass56577572011-07-19 11:08:06 +1200181 Args:
182 hardware_id: Hardware ID to use for this board. If None, then the
183 default from the Fdt will be used
184
Simon Glass89b86b82011-07-17 23:49:49 -0700185 Returns:
186 Path of the created GBB file.
187
188 Raises:
189 CmdError if a command fails.
190 """
Simon Glass56577572011-07-19 11:08:06 +1200191 if not hardware_id:
Simon Glass02d124a2012-03-02 14:47:20 -0800192 hardware_id = self.fdt.GetString('/config', 'hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700193 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700194 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700195
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800196 chromeos_config = self.fdt.GetProps("/chromeos-config")
197 if 'fast-developer-mode' not in chromeos_config:
198 gbb_flags = 0
199 else:
200 self._out.Notice("Enabling fast-developer-mode.")
201 gbb_flags = 1
202
Simon Glass89b86b82011-07-17 23:49:49 -0700203 self._out.Progress('Creating GBB')
204 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
205 sizes = ['%#x' % size for size in sizes]
206 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700207 keydir = self._tools.Filename(self._keydir)
208 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700209 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200210 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700211 '--rootkey=%s/root_key.vbpubk' % keydir,
212 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Simon Glass2a7f0b32011-08-26 11:25:17 -0700213 '--bmpfv=%s' % self._tools.Filename(self.bmpblk_fname),
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800214 '--flags=%d' % gbb_flags,
Simon Glass89b86b82011-07-17 23:49:49 -0700215 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700216 cwd=odir)
217 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700218
Simon Glasse13ee2c2011-07-28 08:12:28 +1200219 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700220 """Sign an image so that the Tegra SOC will boot it.
221
222 Args:
223 bct: BCT file to use.
224 bootstub: Boot stub (U-Boot + fdt) file to sign.
225 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700226
227 Returns:
228 filename of signed image.
229
230 Raises:
231 CmdError if a command fails.
232 """
233 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200234 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700235 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200236 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700237 fd = open(config, 'w')
238 fd.write('Version = 1;\n')
239 fd.write('Redundancy = 1;\n')
240 fd.write('Bctfile = %s;\n' % bct)
Doug Anderson0eeb0742011-09-15 18:11:40 -0700241
242 # TODO(dianders): Right now, we don't have enough space in our flash map
243 # for two copies of the BCT when we're using NAND, so hack it to 1. Not
244 # sure what this does for reliability, but at least things will fit...
245 is_nand = "NvBootDevType_Nand" in self._tools.Run('bct_dump', [bct])
246 if is_nand:
247 fd.write('Bctcopy = 1;\n')
248
Simon Glass89b86b82011-07-17 23:49:49 -0700249 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
250 text_base))
Doug Anderson0eeb0742011-09-15 18:11:40 -0700251
Simon Glass89b86b82011-07-17 23:49:49 -0700252 fd.close()
253
254 self._tools.Run('cbootimage', [config, signed])
255 self._tools.OutputSize('BCT', bct)
256 self._tools.OutputSize('Signed image', signed)
257 return signed
258
Doug Anderson86ce5f42011-07-27 10:40:18 -0700259 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700260 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700261
262 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700263 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700264 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700265 """
Simon Glass290a1802011-07-17 13:54:32 -0700266 if bootcmd:
Simon Glass02d124a2012-03-02 14:47:20 -0800267 self.fdt.PutString('/config', 'bootcmd', bootcmd)
268 self.fdt.PutInteger('/config', 'bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700269 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700270
Simon Glass290a1802011-07-17 13:54:32 -0700271 def AddConfigList(self, config_list, use_int=False):
272 """Add a list of config items to the fdt.
273
274 Normally these values are written to the fdt as strings, but integers
275 are also supported, in which case the values will be converted to integers
276 (if necessary) before being stored.
277
278 Args:
279 config_list: List of (config, value) tuples to add to the fdt. For each
280 tuple:
281 config: The fdt node to write to will be /config/<config>.
282 value: An integer or string value to write.
283 use_int: True to only write integer values.
284
285 Raises:
286 CmdError: if a value is required to be converted to integer but can't be.
287 """
288 if config_list:
289 for config in config_list:
290 value = config[1]
291 if use_int:
292 try:
293 value = int(value)
294 except ValueError as str:
295 raise CmdError("Cannot convert config option '%s' to integer" %
296 value)
297 if type(value) == type(1):
Simon Glass02d124a2012-03-02 14:47:20 -0800298 self.fdt.PutInteger('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700299 else:
Simon Glass02d124a2012-03-02 14:47:20 -0800300 self.fdt.PutString('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700301
Simon Glass7c2d5572011-11-15 14:47:08 -0800302 def DecodeTextBase(self, data):
303 """Look at a U-Boot image and try to decode its TEXT_BASE.
304
305 This works because U-Boot has a header with the value 0x12345678
306 immediately followed by the TEXT_BASE value. We can therefore read this
307 from the image with some certainty. We check only the first 40 words
308 since the header should be within that region.
309
310 Args:
311 data: U-Boot binary data
312
313 Returns:
314 Text base (integer) or None if none was found
315 """
316 found = False
317 for i in range(0, 160, 4):
318 word = data[i:i + 4]
319
320 # TODO(sjg): This does not cope with a big-endian target
321 value = struct.unpack('<I', word)[0]
322 if found:
323 return value
324 if value == 0x12345678:
325 found = True
326
327 return None
328
329 def CalcTextBase(self, name, fdt, fname):
330 """Calculate the TEXT_BASE to use for U-Boot.
331
332 Normally this value is in the fdt, so we just read it from there. But as
333 a second check we look at the image itself in case this is different, and
334 switch to that if it is.
335
336 This allows us to flash any U-Boot even if its TEXT_BASE is different.
337 This is particularly useful with upstream U-Boot which uses a different
338 value (which we will move to).
339 """
340 data = self._tools.ReadFile(fname)
Simon Glass02d124a2012-03-02 14:47:20 -0800341 fdt_text_base = fdt.GetInt('/chromeos-config', 'textbase')
Simon Glass7c2d5572011-11-15 14:47:08 -0800342 text_base = self.DecodeTextBase(data)
343
344 # If they are different, issue a warning and switch over.
345 if text_base and text_base != fdt_text_base:
346 self._out.Warning("TEXT_BASE %x in %sU-Boot doesn't match "
347 "fdt value of %x. Using %x" % (text_base, name,
348 fdt_text_base, text_base))
349 fdt_text_base = text_base
350 return fdt_text_base
351
Simon Glass6dcc2f22011-07-28 15:26:49 +1200352 def _CreateBootStub(self, uboot, base_fdt, postload):
Simon Glass89b86b82011-07-17 23:49:49 -0700353 """Create a boot stub and a signed boot stub.
354
Simon Glass6dcc2f22011-07-28 15:26:49 +1200355 For postload:
356 We add a /config/postload-text-offset entry to the signed bootstub's
357 fdt so that U-Boot can find the postload code.
358
359 The raw (unsigned) bootstub will have a value of -1 for this since we will
360 simply append the postload code to the bootstub and it can find it there.
361 This will be used for RW A/B firmware.
362
363 For the signed case this value will specify where in the flash to find
364 the postload code. This will be used for RO firmware.
365
Simon Glass89b86b82011-07-17 23:49:49 -0700366 Args:
367 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass29b96ad2012-03-09 15:34:33 -0800368 base_fdt: Fdt object containing the flat device tree.
Simon Glass6dcc2f22011-07-28 15:26:49 +1200369 postload: Path to u-boot-post.bin, or None if none.
Simon Glass89b86b82011-07-17 23:49:49 -0700370
371 Returns:
372 Tuple containing:
Simon Glass6dcc2f22011-07-28 15:26:49 +1200373 Full path to bootstub (uboot + fdt(-1) + postload).
374 Full path to signed (uboot + fdt(flash pos) + bct) + postload.
Simon Glass89b86b82011-07-17 23:49:49 -0700375
376 Raises:
377 CmdError if a command fails.
378 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200379 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass7c2d5572011-11-15 14:47:08 -0800380 text_base = self.CalcTextBase('', self.fdt, uboot)
Simon Glass89b86b82011-07-17 23:49:49 -0700381 uboot_data = self._tools.ReadFile(uboot)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200382
383 # Make a copy of the fdt for the bootstub
384 fdt = base_fdt.Copy(os.path.join(self._tools.outdir, 'bootstub.dtb'))
Simon Glass02d124a2012-03-02 14:47:20 -0800385 fdt.PutInteger('/config', 'postload-text-offset', 0xffffffff);
Simon Glass290a1802011-07-17 13:54:32 -0700386 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200387
Simon Glass89b86b82011-07-17 23:49:49 -0700388 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700389 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
390 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700391 self._tools.OutputSize('Combined binary', bootstub)
392
Simon Glasse13ee2c2011-07-28 08:12:28 +1200393 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700394 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700395 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200396 bootstub, text_base)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200397
398 signed_postload = os.path.join(self._tools.outdir, 'signed-postload.bin')
399 data = self._tools.ReadFile(signed)
400
401 if postload:
402 # We must add postload to the bootstub since A and B will need to
403 # be able to find it without the /config/postload-text-offset mechanism.
404 bs_data = self._tools.ReadFile(bootstub)
405 bs_data += self._tools.ReadFile(postload)
406 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt-postload.bin')
407 self._tools.WriteFile(bootstub, bs_data)
408 self._tools.OutputSize('Combined binary with postload', bootstub)
409
410 # Now that we know the file size, adjust the fdt and re-sign
411 postload_bootstub = os.path.join(self._tools.outdir, 'postload.bin')
Simon Glass02d124a2012-03-02 14:47:20 -0800412 fdt.PutInteger('/config', 'postload-text-offset', len(data))
Simon Glass6dcc2f22011-07-28 15:26:49 +1200413 fdt_data = self._tools.ReadFile(fdt.fname)
414 self._tools.WriteFile(postload_bootstub, uboot_data + fdt_data)
415 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
416 postload_bootstub, text_base)
417 if len(data) != os.path.getsize(signed):
418 raise CmdError('Signed file size changed from %d to %d after updating '
419 'fdt' % (len(data), os.path.getsize(signed)))
420
421 # Re-read the signed image, and add the post-load binary.
422 data = self._tools.ReadFile(signed)
423 data += self._tools.ReadFile(postload)
424 self._tools.OutputSize('Post-load binary', postload)
425
426 self._tools.WriteFile(signed_postload, data)
427 self._tools.OutputSize('Final bootstub with postload', signed_postload)
428
429 return bootstub, signed_postload
Simon Glass89b86b82011-07-17 23:49:49 -0700430
Vincent Palatinf7286772011-10-12 14:31:53 -0700431 def _CreateCorebootStub(self, uboot, coreboot, fdt, seabios):
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700432 """Create a coreboot boot stub.
433
434 Args:
435 uboot: Path to u-boot.bin (may be chroot-relative)
436 coreboot: Path to coreboot.rom
437 fdt: Device Tree
Vincent Palatinf7286772011-10-12 14:31:53 -0700438 seabios: Path to SeaBIOS payload binary or None
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700439
440 Returns:
441 Full path to bootstub (coreboot + uboot + fdt).
442
443 Raises:
444 CmdError if a command fails.
445 """
446 bootstub = os.path.join(self._tools.outdir, 'coreboot-full.rom')
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700447 uboot_elf = uboot.replace(".bin", ".elf")
448 shutil.copyfile(coreboot, bootstub)
Vincent Palatinf7286772011-10-12 14:31:53 -0700449 if seabios:
Simon Glass146cb8e2012-03-09 15:51:24 -0800450 self._tools.Run('cbfstool', [bootstub, 'add-payload', seabios,
Vincent Palatinf7286772011-10-12 14:31:53 -0700451 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800452 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700453 'img/U-Boot', 'lzma'])
454 else:
Simon Glass146cb8e2012-03-09 15:51:24 -0800455 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700456 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800457 self._tools.Run('cbfstool', [bootstub, 'add', fdt.fname, 'u-boot.dtb',
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700458 '0xac'])
459 return bootstub
460
Simon Glass8e1fdb22012-03-15 21:02:10 -0700461 def _UpdateBl2Parameters(self, fdt, pack, data, pos):
Simon Glassdf95dd22012-03-13 15:46:16 -0700462 """Update the parameters in a BL2 blob.
463
464 We look at the list in the parameter block, extract the value of each
465 from the device tree, and write that value to the parameter block.
466
467 Args:
468 fdt: Device tree containing the parameter values.
Simon Glass8e1fdb22012-03-15 21:02:10 -0700469 pack: The firmware packer object
Simon Glassdf95dd22012-03-13 15:46:16 -0700470 data: The BL2 data.
471 pos: The position of the start of the parameter block.
472
473 Returns:
474 The new contents of the parameter block, after updating.
475 """
Simon Glassdf95dd22012-03-13 15:46:16 -0700476 version, size = struct.unpack('<2L', data[pos + 4:pos + 12])
477 if version != 1:
478 raise CmdError("Cannot update machine parameter block version '%d'" %
479 version)
480 if size < 0 or pos + size > len(data):
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700481 raise CmdError("Machine parameter block size %d is invalid: "
482 "pos=%d, size=%d, space=%d, len=%d" %
483 (size, pos, size, len(data) - pos, len(data)))
Simon Glassdf95dd22012-03-13 15:46:16 -0700484
485 # Move past the header and read the parameter list, which is terminated
486 # with \0.
487 pos += 12
488 param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0]
489 param_len = param_list.find('\0')
490 param_list = param_list[:param_len]
Simon Glass66c1a9f2012-03-22 19:15:53 -0700491 pos += (param_len + 4) & ~3
Simon Glassdf95dd22012-03-13 15:46:16 -0700492
493 # Work through the parameters one at a time, adding each value
494 new_data = ''
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700495 upto = 0
Simon Glassdf95dd22012-03-13 15:46:16 -0700496 for param in param_list:
Simon Glass2c48ddf2012-03-23 16:55:22 -0700497 value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
Simon Glassdf95dd22012-03-13 15:46:16 -0700498 if param == 'm' :
Simon Glass1a77ded2012-03-15 21:00:49 -0700499 mem_type = fdt.GetString('/dmc', 'mem-type')
Simon Glassdf95dd22012-03-13 15:46:16 -0700500 mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3']
501 if not mem_type in mem_types:
502 raise CmdError("Unknown memory type '%s'" % mem_type)
503 value = mem_types.index(mem_type)
504 self._out.Info(' Memory type: %s (%d)' % (mem_type, value))
505 elif param == 'v':
506 value = 31
507 self._out.Info(' Memory interleave: %#0x' % value)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700508 elif param == 'u':
Simon Glass2c48ddf2012-03-23 16:55:22 -0700509 # TODO(sjg): Seems to not work unless set to the same value as in the
510 # existing image. Need to find root cause.
511 #value = os.stat(pack.GetProperty('boot+dtb')).st_size
512 #value = (value + 0xfff) & ~0xfff
513 self._out.Warning("Leaving U-Boot size unchanged")
Simon Glass8e1fdb22012-03-15 21:02:10 -0700514 self._out.Info(' U-Boot size: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700515 else:
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700516 self._out.Warning("Unknown machine parameter type '%s'" % param)
Simon Glass2c48ddf2012-03-23 16:55:22 -0700517 self._out.Info(' Unknown value: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700518 new_data += struct.pack('<L', value)
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700519 upto += 4
Simon Glassdf95dd22012-03-13 15:46:16 -0700520
521 # Put the data into our block.
522 data = data[:pos] + new_data + data[pos + len(new_data):]
523 self._out.Info('BL2 configuration complete')
524 return data
525
Simon Glass8e1fdb22012-03-15 21:02:10 -0700526 def _ConfigureExynosBl2(self, fdt, pack, orig_bl2):
Simon Glass7e199222012-03-13 15:51:18 -0700527 """Configure an Exynos BL2 binary for our needs.
528
529 We create a new modified BL2 and return its filename.
530
531 Args:
532 fdt: Device tree containing the parameter values.
Simon Glass8e1fdb22012-03-15 21:02:10 -0700533 pack: The firmware packer object
Simon Glass7e199222012-03-13 15:51:18 -0700534 orig_bl2: Filename of original BL2 file to modify.
535 """
Simon Glass66c1a9f2012-03-22 19:15:53 -0700536 self._out.Info('Configuring BL2')
Simon Glass7e199222012-03-13 15:51:18 -0700537 bl2 = os.path.join(self._tools.outdir, 'updated-spl.bin')
Simon Glass66c1a9f2012-03-22 19:15:53 -0700538 data = self._tools.ReadFile(orig_bl2)
539 self._tools.WriteFile(bl2, data)
Simon Glass7e199222012-03-13 15:51:18 -0700540
541 # Locate the parameter block
542 data = self._tools.ReadFile(bl2)
543 marker = struct.pack('<L', 0xdeadbeef)
544 pos = data.rfind(marker)
545 if not pos:
546 raise CmdError("Could not find machine parameter block in '%s'" %
547 orig_bl2)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700548 data = self._UpdateBl2Parameters(fdt, pack, data, pos)
Simon Glass7e199222012-03-13 15:51:18 -0700549 self._tools.WriteFile(bl2, data)
550 return bl2
551
Simon Glass89b86b82011-07-17 23:49:49 -0700552 def _PackOutput(self, msg):
553 """Helper function to write output from PackFirmware (verbose level 2).
554
555 This is passed to PackFirmware for it to use to write output.
556
557 Args:
558 msg: Message to display.
559 """
560 self._out.Notice(msg)
561
Simon Glass439fe7a2012-03-09 16:19:34 -0800562 def _BuildBlob(self, pack, fdt, blob_type):
563 """Build the blob data for a particular blob type.
564
565 Args:
566 blob_type: The type of blob to create data for. Supported types are:
567 coreboot A coreboot image (ROM plus U-boot and .dtb payloads).
568 signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb).
569 """
570 if blob_type == 'coreboot':
571 coreboot = self._CreateCorebootStub(self.uboot_fname,
572 self.coreboot_fname, fdt, self.seabios_fname)
573 pack.AddProperty('coreboot', coreboot)
574 pack.AddProperty('image', coreboot)
575 elif blob_type == 'signed':
576 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt,
577 self.postload_fname)
578 pack.AddProperty('bootstub', bootstub)
579 pack.AddProperty('signed', signed)
580 pack.AddProperty('image', signed)
Simon Glass7e199222012-03-13 15:51:18 -0700581 elif blob_type == 'exynos-bl1':
582 pack.AddProperty(blob_type, self.exynos_bl1)
583 elif blob_type == 'exynos-bl2':
Simon Glass8e1fdb22012-03-15 21:02:10 -0700584 bl2 = self._ConfigureExynosBl2(fdt, pack, self.exynos_bl2)
Simon Glass7e199222012-03-13 15:51:18 -0700585 pack.AddProperty(blob_type, bl2)
Simon Glass439fe7a2012-03-09 16:19:34 -0800586 elif pack.GetProperty(blob_type):
587 pass
588 else:
589 raise CmdError("Unknown blob type '%s' required in flash map" %
590 blob_type)
591
Simon Glass290a1802011-07-17 13:54:32 -0700592 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700593 """Create a full firmware image, along with various by-products.
594
595 This uses the provided u-boot.bin, fdt and bct to create a firmware
596 image containing all the required parts. If the GBB is not supplied
597 then this will just return a signed U-Boot as the image.
598
599 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200600 gbb: Full path to the GBB file, or empty if a GBB is not required.
601 fdt: Fdt object containing required information.
602
603 Returns:
604 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700605
606 Raises:
607 CmdError if a command fails.
608 """
Simon Glass02d124a2012-03-02 14:47:20 -0800609 self._out.Notice("Model: %s" % fdt.GetString('/', 'model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700610
Simon Glass439fe7a2012-03-09 16:19:34 -0800611 # Get the flashmap so we know what to build
612 pack = PackFirmware(self._tools, self._out)
613 pack.SelectFdt(fdt)
614
615 # Get all our blobs ready
616 pack.AddProperty('boot', self.uboot_fname)
Simon Glass50f74602012-03-15 21:04:25 -0700617
618 # Make a copy of the fdt for the bootstub
619 fdt_data = self._tools.ReadFile(fdt.fname)
620 uboot_data = self._tools.ReadFile(self.uboot_fname)
621 uboot_copy = os.path.join(self._tools.outdir, 'u-boot.bin')
622 self._tools.WriteFile(uboot_copy, uboot_data)
623
624 bootstub = os.path.join(self._tools.outdir, 'u-boot-dtb.bin')
625 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
626 pack.AddProperty('boot+dtb', bootstub)
627
Simon Glass439fe7a2012-03-09 16:19:34 -0800628 pack.AddProperty('gbb', self.uboot_fname)
629 for blob_type in pack.GetBlobList(self.coreboot_fname is not None):
630 self._BuildBlob(pack, fdt, blob_type)
Simon Glass89b86b82011-07-17 23:49:49 -0700631
632 if gbb:
Simon Glasse76bf7b2012-03-13 15:34:41 -0700633 pack.RequireAllEntries()
Hung-Te Lina7462e72011-07-27 19:17:10 +0800634 fwid = '.'.join([
Simon Glass02d124a2012-03-02 14:47:20 -0800635 re.sub('[ ,]+', '_', fdt.GetString('/', 'model')),
Hung-Te Lina7462e72011-07-27 19:17:10 +0800636 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700637 self._out.Notice('Firmware ID: %s' % fwid)
Simon Glass439fe7a2012-03-09 16:19:34 -0800638 pack.AddProperty('fwid', fwid)
639 pack.AddProperty('gbb', gbb)
640 pack.AddProperty('keydir', self._keydir)
Simon Glassc90cf582012-03-13 15:40:47 -0700641
642 pack.CheckProperties()
643 image = os.path.join(self._tools.outdir, 'image.bin')
644 pack.PackImage(self._tools.outdir, image)
645 pack.AddProperty('image', image)
Simon Glass89b86b82011-07-17 23:49:49 -0700646
Simon Glass439fe7a2012-03-09 16:19:34 -0800647 image = pack.GetProperty('image')
Simon Glass89b86b82011-07-17 23:49:49 -0700648 self._tools.OutputSize('Final image', image)
Simon Glassc90cf582012-03-13 15:40:47 -0700649 return image, pack
Simon Glass89b86b82011-07-17 23:49:49 -0700650
Simon Glass290a1802011-07-17 13:54:32 -0700651 def SelectFdt(self, fdt_fname):
652 """Select an FDT to control the firmware bundling
653
654 Args:
655 fdt_fname: The filename of the fdt to use.
656
Simon Glassc0f3dc62011-08-09 14:19:05 -0700657 Returns:
658 The Fdt object of the original fdt file, which we will not modify.
659
Simon Glass290a1802011-07-17 13:54:32 -0700660 We make a copy of this which will include any on-the-fly changes we want
661 to make.
662 """
663 self._fdt_fname = fdt_fname
664 self.CheckOptions()
665 fdt = Fdt(self._tools, self._fdt_fname)
Simon Glass29b96ad2012-03-09 15:34:33 -0800666 fdt.Compile()
Simon Glass290a1802011-07-17 13:54:32 -0700667 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700668 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700669
Simon Glassc90cf582012-03-13 15:40:47 -0700670 def Start(self, hardware_id, output_fname, show_map):
Simon Glass290a1802011-07-17 13:54:32 -0700671 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700672
673 - Checks options, tools, output directory, fdt.
674 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700675
676 Args:
Simon Glass56577572011-07-19 11:08:06 +1200677 hardware_id: Hardware ID to use for this board. If None, then the
678 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700679 output_fname: Output filename for the image. If this is not None, then
680 the final image will be copied here.
Simon Glassc90cf582012-03-13 15:40:47 -0700681 show_map: Show a flash map, with each area's name and position
Simon Glass290a1802011-07-17 13:54:32 -0700682
683 Returns:
684 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700685 """
Simon Glass89b86b82011-07-17 23:49:49 -0700686 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700687 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200688 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700689
690 # This creates the actual image.
Simon Glassc90cf582012-03-13 15:40:47 -0700691 image, pack = self._CreateImage(gbb, self.fdt)
692 if show_map:
693 pack.ShowMap()
Simon Glass290a1802011-07-17 13:54:32 -0700694 if output_fname:
695 shutil.copyfile(image, output_fname)
696 self._out.Notice("Output image '%s'" % output_fname)
697 return image