blob: cf900b6ba327c24ec8c7628e2d8c26b4eb711b22 [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 Glassdf95dd22012-03-13 15:46:16 -0700461 def _UpdateBl2Parameters(self, fdt, data, pos):
462 """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.
469 data: The BL2 data.
470 pos: The position of the start of the parameter block.
471
472 Returns:
473 The new contents of the parameter block, after updating.
474 """
475 self._out.Info('Configuring BL2')
476 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]
491 pos += (param_len + 3) & ~3
492
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:
497 if param == 'm' :
Simon Glass1a77ded2012-03-15 21:00:49 -0700498 mem_type = fdt.GetString('/dmc', 'mem-type')
Simon Glassdf95dd22012-03-13 15:46:16 -0700499 mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3']
500 if not mem_type in mem_types:
501 raise CmdError("Unknown memory type '%s'" % mem_type)
502 value = mem_types.index(mem_type)
503 self._out.Info(' Memory type: %s (%d)' % (mem_type, value))
504 elif param == 'v':
505 value = 31
506 self._out.Info(' Memory interleave: %#0x' % value)
507 else:
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700508 self._out.Warning("Unknown machine parameter type '%s'" % param)
509 value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
Simon Glassdf95dd22012-03-13 15:46:16 -0700510 new_data += struct.pack('<L', value)
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700511 upto += 4
Simon Glassdf95dd22012-03-13 15:46:16 -0700512
513 # Put the data into our block.
514 data = data[:pos] + new_data + data[pos + len(new_data):]
515 self._out.Info('BL2 configuration complete')
516 return data
517
Simon Glass7e199222012-03-13 15:51:18 -0700518 def _ConfigureExynosBl2(self, fdt, orig_bl2):
519 """Configure an Exynos BL2 binary for our needs.
520
521 We create a new modified BL2 and return its filename.
522
523 Args:
524 fdt: Device tree containing the parameter values.
525 orig_bl2: Filename of original BL2 file to modify.
526 """
527 bl2 = os.path.join(self._tools.outdir, 'updated-spl.bin')
528 shutil.copyfile(orig_bl2, bl2)
529
530 # Locate the parameter block
531 data = self._tools.ReadFile(bl2)
532 marker = struct.pack('<L', 0xdeadbeef)
533 pos = data.rfind(marker)
534 if not pos:
535 raise CmdError("Could not find machine parameter block in '%s'" %
536 orig_bl2)
Simon Glassdf95dd22012-03-13 15:46:16 -0700537 self._UpdateBl2Parameters(fdt, data, pos)
Simon Glass7e199222012-03-13 15:51:18 -0700538 self._tools.WriteFile(bl2, data)
539 return bl2
540
Simon Glass89b86b82011-07-17 23:49:49 -0700541 def _PackOutput(self, msg):
542 """Helper function to write output from PackFirmware (verbose level 2).
543
544 This is passed to PackFirmware for it to use to write output.
545
546 Args:
547 msg: Message to display.
548 """
549 self._out.Notice(msg)
550
Simon Glass439fe7a2012-03-09 16:19:34 -0800551 def _BuildBlob(self, pack, fdt, blob_type):
552 """Build the blob data for a particular blob type.
553
554 Args:
555 blob_type: The type of blob to create data for. Supported types are:
556 coreboot A coreboot image (ROM plus U-boot and .dtb payloads).
557 signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb).
558 """
559 if blob_type == 'coreboot':
560 coreboot = self._CreateCorebootStub(self.uboot_fname,
561 self.coreboot_fname, fdt, self.seabios_fname)
562 pack.AddProperty('coreboot', coreboot)
563 pack.AddProperty('image', coreboot)
564 elif blob_type == 'signed':
565 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt,
566 self.postload_fname)
567 pack.AddProperty('bootstub', bootstub)
568 pack.AddProperty('signed', signed)
569 pack.AddProperty('image', signed)
Simon Glass7e199222012-03-13 15:51:18 -0700570 elif blob_type == 'exynos-bl1':
571 pack.AddProperty(blob_type, self.exynos_bl1)
572 elif blob_type == 'exynos-bl2':
573 bl2 = self._ConfigureExynosBl2(fdt, self.exynos_bl2)
574 pack.AddProperty(blob_type, bl2)
Simon Glass439fe7a2012-03-09 16:19:34 -0800575 elif pack.GetProperty(blob_type):
576 pass
577 else:
578 raise CmdError("Unknown blob type '%s' required in flash map" %
579 blob_type)
580
Simon Glass290a1802011-07-17 13:54:32 -0700581 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700582 """Create a full firmware image, along with various by-products.
583
584 This uses the provided u-boot.bin, fdt and bct to create a firmware
585 image containing all the required parts. If the GBB is not supplied
586 then this will just return a signed U-Boot as the image.
587
588 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200589 gbb: Full path to the GBB file, or empty if a GBB is not required.
590 fdt: Fdt object containing required information.
591
592 Returns:
593 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700594
595 Raises:
596 CmdError if a command fails.
597 """
Simon Glass02d124a2012-03-02 14:47:20 -0800598 self._out.Notice("Model: %s" % fdt.GetString('/', 'model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700599
Simon Glass439fe7a2012-03-09 16:19:34 -0800600 # Get the flashmap so we know what to build
601 pack = PackFirmware(self._tools, self._out)
602 pack.SelectFdt(fdt)
603
604 # Get all our blobs ready
605 pack.AddProperty('boot', self.uboot_fname)
Simon Glass50f74602012-03-15 21:04:25 -0700606
607 # Make a copy of the fdt for the bootstub
608 fdt_data = self._tools.ReadFile(fdt.fname)
609 uboot_data = self._tools.ReadFile(self.uboot_fname)
610 uboot_copy = os.path.join(self._tools.outdir, 'u-boot.bin')
611 self._tools.WriteFile(uboot_copy, uboot_data)
612
613 bootstub = os.path.join(self._tools.outdir, 'u-boot-dtb.bin')
614 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
615 pack.AddProperty('boot+dtb', bootstub)
616
Simon Glass439fe7a2012-03-09 16:19:34 -0800617 pack.AddProperty('gbb', self.uboot_fname)
618 for blob_type in pack.GetBlobList(self.coreboot_fname is not None):
619 self._BuildBlob(pack, fdt, blob_type)
Simon Glass89b86b82011-07-17 23:49:49 -0700620
621 if gbb:
Simon Glasse76bf7b2012-03-13 15:34:41 -0700622 pack.RequireAllEntries()
Hung-Te Lina7462e72011-07-27 19:17:10 +0800623 fwid = '.'.join([
Simon Glass02d124a2012-03-02 14:47:20 -0800624 re.sub('[ ,]+', '_', fdt.GetString('/', 'model')),
Hung-Te Lina7462e72011-07-27 19:17:10 +0800625 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700626 self._out.Notice('Firmware ID: %s' % fwid)
Simon Glass439fe7a2012-03-09 16:19:34 -0800627 pack.AddProperty('fwid', fwid)
628 pack.AddProperty('gbb', gbb)
629 pack.AddProperty('keydir', self._keydir)
Simon Glassc90cf582012-03-13 15:40:47 -0700630
631 pack.CheckProperties()
632 image = os.path.join(self._tools.outdir, 'image.bin')
633 pack.PackImage(self._tools.outdir, image)
634 pack.AddProperty('image', image)
Simon Glass89b86b82011-07-17 23:49:49 -0700635
Simon Glass439fe7a2012-03-09 16:19:34 -0800636 image = pack.GetProperty('image')
Simon Glass89b86b82011-07-17 23:49:49 -0700637 self._tools.OutputSize('Final image', image)
Simon Glassc90cf582012-03-13 15:40:47 -0700638 return image, pack
Simon Glass89b86b82011-07-17 23:49:49 -0700639
Simon Glass290a1802011-07-17 13:54:32 -0700640 def SelectFdt(self, fdt_fname):
641 """Select an FDT to control the firmware bundling
642
643 Args:
644 fdt_fname: The filename of the fdt to use.
645
Simon Glassc0f3dc62011-08-09 14:19:05 -0700646 Returns:
647 The Fdt object of the original fdt file, which we will not modify.
648
Simon Glass290a1802011-07-17 13:54:32 -0700649 We make a copy of this which will include any on-the-fly changes we want
650 to make.
651 """
652 self._fdt_fname = fdt_fname
653 self.CheckOptions()
654 fdt = Fdt(self._tools, self._fdt_fname)
Simon Glass29b96ad2012-03-09 15:34:33 -0800655 fdt.Compile()
Simon Glass290a1802011-07-17 13:54:32 -0700656 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700657 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700658
Simon Glassc90cf582012-03-13 15:40:47 -0700659 def Start(self, hardware_id, output_fname, show_map):
Simon Glass290a1802011-07-17 13:54:32 -0700660 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700661
662 - Checks options, tools, output directory, fdt.
663 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700664
665 Args:
Simon Glass56577572011-07-19 11:08:06 +1200666 hardware_id: Hardware ID to use for this board. If None, then the
667 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700668 output_fname: Output filename for the image. If this is not None, then
669 the final image will be copied here.
Simon Glassc90cf582012-03-13 15:40:47 -0700670 show_map: Show a flash map, with each area's name and position
Simon Glass290a1802011-07-17 13:54:32 -0700671
672 Returns:
673 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700674 """
Simon Glass89b86b82011-07-17 23:49:49 -0700675 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700676 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200677 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700678
679 # This creates the actual image.
Simon Glassc90cf582012-03-13 15:40:47 -0700680 image, pack = self._CreateImage(gbb, self.fdt)
681 if show_map:
682 pack.ShowMap()
Simon Glass290a1802011-07-17 13:54:32 -0700683 if output_fname:
684 shutil.copyfile(image, output_fname)
685 self._out.Notice("Output image '%s'" % output_fname)
686 return image