blob: 7fbd0bfe10a9c0adea8819ab68fda7331ff505b3 [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 Glass89b86b82011-07-17 23:49:49 -0700156
Simon Glass75759302012-03-15 20:26:53 -0700157 def GetFiles(self):
158 """Get a list of files that we know about.
159
160 This is the opposite of SetFiles except that we may have put in some
161 default names. It returns a dictionary containing the filename for
162 each of a number of pre-defined files.
163
164 Returns:
165 Dictionary, with one entry for each file.
166 """
167 file_list = {
168 'bct' : self.bct_fname,
169 'exynos-bl1' : self.exynos_bl1,
170 'exynos-bl2' : self.exynos_bl2,
171 }
172 return file_list
173
Simon Glass56577572011-07-19 11:08:06 +1200174 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700175 """Create a GBB for the image.
176
Simon Glass56577572011-07-19 11:08:06 +1200177 Args:
178 hardware_id: Hardware ID to use for this board. If None, then the
179 default from the Fdt will be used
180
Simon Glass89b86b82011-07-17 23:49:49 -0700181 Returns:
182 Path of the created GBB file.
183
184 Raises:
185 CmdError if a command fails.
186 """
Simon Glass56577572011-07-19 11:08:06 +1200187 if not hardware_id:
Simon Glass02d124a2012-03-02 14:47:20 -0800188 hardware_id = self.fdt.GetString('/config', 'hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700189 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700190 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700191
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800192 chromeos_config = self.fdt.GetProps("/chromeos-config")
193 if 'fast-developer-mode' not in chromeos_config:
194 gbb_flags = 0
195 else:
196 self._out.Notice("Enabling fast-developer-mode.")
197 gbb_flags = 1
198
Simon Glass89b86b82011-07-17 23:49:49 -0700199 self._out.Progress('Creating GBB')
200 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
201 sizes = ['%#x' % size for size in sizes]
202 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700203 keydir = self._tools.Filename(self._keydir)
204 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700205 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200206 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700207 '--rootkey=%s/root_key.vbpubk' % keydir,
208 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Simon Glass2a7f0b32011-08-26 11:25:17 -0700209 '--bmpfv=%s' % self._tools.Filename(self.bmpblk_fname),
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800210 '--flags=%d' % gbb_flags,
Simon Glass89b86b82011-07-17 23:49:49 -0700211 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700212 cwd=odir)
213 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700214
Simon Glasse13ee2c2011-07-28 08:12:28 +1200215 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700216 """Sign an image so that the Tegra SOC will boot it.
217
218 Args:
219 bct: BCT file to use.
220 bootstub: Boot stub (U-Boot + fdt) file to sign.
221 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700222
223 Returns:
224 filename of signed image.
225
226 Raises:
227 CmdError if a command fails.
228 """
229 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200230 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700231 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200232 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700233 fd = open(config, 'w')
234 fd.write('Version = 1;\n')
235 fd.write('Redundancy = 1;\n')
236 fd.write('Bctfile = %s;\n' % bct)
Doug Anderson0eeb0742011-09-15 18:11:40 -0700237
238 # TODO(dianders): Right now, we don't have enough space in our flash map
239 # for two copies of the BCT when we're using NAND, so hack it to 1. Not
240 # sure what this does for reliability, but at least things will fit...
241 is_nand = "NvBootDevType_Nand" in self._tools.Run('bct_dump', [bct])
242 if is_nand:
243 fd.write('Bctcopy = 1;\n')
244
Simon Glass89b86b82011-07-17 23:49:49 -0700245 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
246 text_base))
Doug Anderson0eeb0742011-09-15 18:11:40 -0700247
Simon Glass89b86b82011-07-17 23:49:49 -0700248 fd.close()
249
250 self._tools.Run('cbootimage', [config, signed])
251 self._tools.OutputSize('BCT', bct)
252 self._tools.OutputSize('Signed image', signed)
253 return signed
254
Doug Anderson86ce5f42011-07-27 10:40:18 -0700255 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700256 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700257
258 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700259 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700260 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700261 """
Simon Glass290a1802011-07-17 13:54:32 -0700262 if bootcmd:
Simon Glass02d124a2012-03-02 14:47:20 -0800263 self.fdt.PutString('/config', 'bootcmd', bootcmd)
264 self.fdt.PutInteger('/config', 'bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700265 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700266
Simon Glass290a1802011-07-17 13:54:32 -0700267 def AddConfigList(self, config_list, use_int=False):
268 """Add a list of config items to the fdt.
269
270 Normally these values are written to the fdt as strings, but integers
271 are also supported, in which case the values will be converted to integers
272 (if necessary) before being stored.
273
274 Args:
275 config_list: List of (config, value) tuples to add to the fdt. For each
276 tuple:
277 config: The fdt node to write to will be /config/<config>.
278 value: An integer or string value to write.
279 use_int: True to only write integer values.
280
281 Raises:
282 CmdError: if a value is required to be converted to integer but can't be.
283 """
284 if config_list:
285 for config in config_list:
286 value = config[1]
287 if use_int:
288 try:
289 value = int(value)
290 except ValueError as str:
291 raise CmdError("Cannot convert config option '%s' to integer" %
292 value)
293 if type(value) == type(1):
Simon Glass02d124a2012-03-02 14:47:20 -0800294 self.fdt.PutInteger('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700295 else:
Simon Glass02d124a2012-03-02 14:47:20 -0800296 self.fdt.PutString('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700297
Simon Glass7c2d5572011-11-15 14:47:08 -0800298 def DecodeTextBase(self, data):
299 """Look at a U-Boot image and try to decode its TEXT_BASE.
300
301 This works because U-Boot has a header with the value 0x12345678
302 immediately followed by the TEXT_BASE value. We can therefore read this
303 from the image with some certainty. We check only the first 40 words
304 since the header should be within that region.
305
306 Args:
307 data: U-Boot binary data
308
309 Returns:
310 Text base (integer) or None if none was found
311 """
312 found = False
313 for i in range(0, 160, 4):
314 word = data[i:i + 4]
315
316 # TODO(sjg): This does not cope with a big-endian target
317 value = struct.unpack('<I', word)[0]
318 if found:
319 return value
320 if value == 0x12345678:
321 found = True
322
323 return None
324
325 def CalcTextBase(self, name, fdt, fname):
326 """Calculate the TEXT_BASE to use for U-Boot.
327
328 Normally this value is in the fdt, so we just read it from there. But as
329 a second check we look at the image itself in case this is different, and
330 switch to that if it is.
331
332 This allows us to flash any U-Boot even if its TEXT_BASE is different.
333 This is particularly useful with upstream U-Boot which uses a different
334 value (which we will move to).
335 """
336 data = self._tools.ReadFile(fname)
Simon Glass02d124a2012-03-02 14:47:20 -0800337 fdt_text_base = fdt.GetInt('/chromeos-config', 'textbase')
Simon Glass7c2d5572011-11-15 14:47:08 -0800338 text_base = self.DecodeTextBase(data)
339
340 # If they are different, issue a warning and switch over.
341 if text_base and text_base != fdt_text_base:
342 self._out.Warning("TEXT_BASE %x in %sU-Boot doesn't match "
343 "fdt value of %x. Using %x" % (text_base, name,
344 fdt_text_base, text_base))
345 fdt_text_base = text_base
346 return fdt_text_base
347
Simon Glass6dcc2f22011-07-28 15:26:49 +1200348 def _CreateBootStub(self, uboot, base_fdt, postload):
Simon Glass89b86b82011-07-17 23:49:49 -0700349 """Create a boot stub and a signed boot stub.
350
Simon Glass6dcc2f22011-07-28 15:26:49 +1200351 For postload:
352 We add a /config/postload-text-offset entry to the signed bootstub's
353 fdt so that U-Boot can find the postload code.
354
355 The raw (unsigned) bootstub will have a value of -1 for this since we will
356 simply append the postload code to the bootstub and it can find it there.
357 This will be used for RW A/B firmware.
358
359 For the signed case this value will specify where in the flash to find
360 the postload code. This will be used for RO firmware.
361
Simon Glass89b86b82011-07-17 23:49:49 -0700362 Args:
363 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass29b96ad2012-03-09 15:34:33 -0800364 base_fdt: Fdt object containing the flat device tree.
Simon Glass6dcc2f22011-07-28 15:26:49 +1200365 postload: Path to u-boot-post.bin, or None if none.
Simon Glass89b86b82011-07-17 23:49:49 -0700366
367 Returns:
368 Tuple containing:
Simon Glass6dcc2f22011-07-28 15:26:49 +1200369 Full path to bootstub (uboot + fdt(-1) + postload).
370 Full path to signed (uboot + fdt(flash pos) + bct) + postload.
Simon Glass89b86b82011-07-17 23:49:49 -0700371
372 Raises:
373 CmdError if a command fails.
374 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200375 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass7c2d5572011-11-15 14:47:08 -0800376 text_base = self.CalcTextBase('', self.fdt, uboot)
Simon Glass89b86b82011-07-17 23:49:49 -0700377 uboot_data = self._tools.ReadFile(uboot)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200378
379 # Make a copy of the fdt for the bootstub
380 fdt = base_fdt.Copy(os.path.join(self._tools.outdir, 'bootstub.dtb'))
Simon Glass02d124a2012-03-02 14:47:20 -0800381 fdt.PutInteger('/config', 'postload-text-offset', 0xffffffff);
Simon Glass290a1802011-07-17 13:54:32 -0700382 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200383
Simon Glass89b86b82011-07-17 23:49:49 -0700384 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700385 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
386 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700387 self._tools.OutputSize('Combined binary', bootstub)
388
Simon Glasse13ee2c2011-07-28 08:12:28 +1200389 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700390 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700391 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200392 bootstub, text_base)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200393
394 signed_postload = os.path.join(self._tools.outdir, 'signed-postload.bin')
395 data = self._tools.ReadFile(signed)
396
397 if postload:
398 # We must add postload to the bootstub since A and B will need to
399 # be able to find it without the /config/postload-text-offset mechanism.
400 bs_data = self._tools.ReadFile(bootstub)
401 bs_data += self._tools.ReadFile(postload)
402 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt-postload.bin')
403 self._tools.WriteFile(bootstub, bs_data)
404 self._tools.OutputSize('Combined binary with postload', bootstub)
405
406 # Now that we know the file size, adjust the fdt and re-sign
407 postload_bootstub = os.path.join(self._tools.outdir, 'postload.bin')
Simon Glass02d124a2012-03-02 14:47:20 -0800408 fdt.PutInteger('/config', 'postload-text-offset', len(data))
Simon Glass6dcc2f22011-07-28 15:26:49 +1200409 fdt_data = self._tools.ReadFile(fdt.fname)
410 self._tools.WriteFile(postload_bootstub, uboot_data + fdt_data)
411 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
412 postload_bootstub, text_base)
413 if len(data) != os.path.getsize(signed):
414 raise CmdError('Signed file size changed from %d to %d after updating '
415 'fdt' % (len(data), os.path.getsize(signed)))
416
417 # Re-read the signed image, and add the post-load binary.
418 data = self._tools.ReadFile(signed)
419 data += self._tools.ReadFile(postload)
420 self._tools.OutputSize('Post-load binary', postload)
421
422 self._tools.WriteFile(signed_postload, data)
423 self._tools.OutputSize('Final bootstub with postload', signed_postload)
424
425 return bootstub, signed_postload
Simon Glass89b86b82011-07-17 23:49:49 -0700426
Vincent Palatinf7286772011-10-12 14:31:53 -0700427 def _CreateCorebootStub(self, uboot, coreboot, fdt, seabios):
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700428 """Create a coreboot boot stub.
429
430 Args:
431 uboot: Path to u-boot.bin (may be chroot-relative)
432 coreboot: Path to coreboot.rom
433 fdt: Device Tree
Vincent Palatinf7286772011-10-12 14:31:53 -0700434 seabios: Path to SeaBIOS payload binary or None
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700435
436 Returns:
437 Full path to bootstub (coreboot + uboot + fdt).
438
439 Raises:
440 CmdError if a command fails.
441 """
442 bootstub = os.path.join(self._tools.outdir, 'coreboot-full.rom')
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700443 uboot_elf = uboot.replace(".bin", ".elf")
444 shutil.copyfile(coreboot, bootstub)
Vincent Palatinf7286772011-10-12 14:31:53 -0700445 if seabios:
Simon Glass146cb8e2012-03-09 15:51:24 -0800446 self._tools.Run('cbfstool', [bootstub, 'add-payload', seabios,
Vincent Palatinf7286772011-10-12 14:31:53 -0700447 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800448 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700449 'img/U-Boot', 'lzma'])
450 else:
Simon Glass146cb8e2012-03-09 15:51:24 -0800451 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700452 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800453 self._tools.Run('cbfstool', [bootstub, 'add', fdt.fname, 'u-boot.dtb',
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700454 '0xac'])
455 return bootstub
456
Simon Glassdf95dd22012-03-13 15:46:16 -0700457 def _UpdateBl2Parameters(self, fdt, data, pos):
458 """Update the parameters in a BL2 blob.
459
460 We look at the list in the parameter block, extract the value of each
461 from the device tree, and write that value to the parameter block.
462
463 Args:
464 fdt: Device tree containing the parameter values.
465 data: The BL2 data.
466 pos: The position of the start of the parameter block.
467
468 Returns:
469 The new contents of the parameter block, after updating.
470 """
471 self._out.Info('Configuring BL2')
472 version, size = struct.unpack('<2L', data[pos + 4:pos + 12])
473 if version != 1:
474 raise CmdError("Cannot update machine parameter block version '%d'" %
475 version)
476 if size < 0 or pos + size > len(data):
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700477 raise CmdError("Machine parameter block size %d is invalid: "
478 "pos=%d, size=%d, space=%d, len=%d" %
479 (size, pos, size, len(data) - pos, len(data)))
Simon Glassdf95dd22012-03-13 15:46:16 -0700480
481 # Move past the header and read the parameter list, which is terminated
482 # with \0.
483 pos += 12
484 param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0]
485 param_len = param_list.find('\0')
486 param_list = param_list[:param_len]
487 pos += (param_len + 3) & ~3
488
489 # Work through the parameters one at a time, adding each value
490 new_data = ''
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700491 upto = 0
Simon Glassdf95dd22012-03-13 15:46:16 -0700492 for param in param_list:
493 if param == 'm' :
Simon Glass1a77ded2012-03-15 21:00:49 -0700494 mem_type = fdt.GetString('/dmc', 'mem-type')
Simon Glassdf95dd22012-03-13 15:46:16 -0700495 mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3']
496 if not mem_type in mem_types:
497 raise CmdError("Unknown memory type '%s'" % mem_type)
498 value = mem_types.index(mem_type)
499 self._out.Info(' Memory type: %s (%d)' % (mem_type, value))
500 elif param == 'v':
501 value = 31
502 self._out.Info(' Memory interleave: %#0x' % value)
503 else:
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700504 self._out.Warning("Unknown machine parameter type '%s'" % param)
505 value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
Simon Glassdf95dd22012-03-13 15:46:16 -0700506 new_data += struct.pack('<L', value)
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700507 upto += 4
Simon Glassdf95dd22012-03-13 15:46:16 -0700508
509 # Put the data into our block.
510 data = data[:pos] + new_data + data[pos + len(new_data):]
511 self._out.Info('BL2 configuration complete')
512 return data
513
Simon Glass7e199222012-03-13 15:51:18 -0700514 def _ConfigureExynosBl2(self, fdt, orig_bl2):
515 """Configure an Exynos BL2 binary for our needs.
516
517 We create a new modified BL2 and return its filename.
518
519 Args:
520 fdt: Device tree containing the parameter values.
521 orig_bl2: Filename of original BL2 file to modify.
522 """
523 bl2 = os.path.join(self._tools.outdir, 'updated-spl.bin')
524 shutil.copyfile(orig_bl2, bl2)
525
526 # Locate the parameter block
527 data = self._tools.ReadFile(bl2)
528 marker = struct.pack('<L', 0xdeadbeef)
529 pos = data.rfind(marker)
530 if not pos:
531 raise CmdError("Could not find machine parameter block in '%s'" %
532 orig_bl2)
Simon Glassdf95dd22012-03-13 15:46:16 -0700533 self._UpdateBl2Parameters(fdt, data, pos)
Simon Glass7e199222012-03-13 15:51:18 -0700534 self._tools.WriteFile(bl2, data)
535 return bl2
536
Simon Glass89b86b82011-07-17 23:49:49 -0700537 def _PackOutput(self, msg):
538 """Helper function to write output from PackFirmware (verbose level 2).
539
540 This is passed to PackFirmware for it to use to write output.
541
542 Args:
543 msg: Message to display.
544 """
545 self._out.Notice(msg)
546
Simon Glass439fe7a2012-03-09 16:19:34 -0800547 def _BuildBlob(self, pack, fdt, blob_type):
548 """Build the blob data for a particular blob type.
549
550 Args:
551 blob_type: The type of blob to create data for. Supported types are:
552 coreboot A coreboot image (ROM plus U-boot and .dtb payloads).
553 signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb).
554 """
555 if blob_type == 'coreboot':
556 coreboot = self._CreateCorebootStub(self.uboot_fname,
557 self.coreboot_fname, fdt, self.seabios_fname)
558 pack.AddProperty('coreboot', coreboot)
559 pack.AddProperty('image', coreboot)
560 elif blob_type == 'signed':
561 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt,
562 self.postload_fname)
563 pack.AddProperty('bootstub', bootstub)
564 pack.AddProperty('signed', signed)
565 pack.AddProperty('image', signed)
Simon Glass7e199222012-03-13 15:51:18 -0700566 elif blob_type == 'exynos-bl1':
567 pack.AddProperty(blob_type, self.exynos_bl1)
568 elif blob_type == 'exynos-bl2':
569 bl2 = self._ConfigureExynosBl2(fdt, self.exynos_bl2)
570 pack.AddProperty(blob_type, bl2)
Simon Glass439fe7a2012-03-09 16:19:34 -0800571 elif pack.GetProperty(blob_type):
572 pass
573 else:
574 raise CmdError("Unknown blob type '%s' required in flash map" %
575 blob_type)
576
Simon Glass290a1802011-07-17 13:54:32 -0700577 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700578 """Create a full firmware image, along with various by-products.
579
580 This uses the provided u-boot.bin, fdt and bct to create a firmware
581 image containing all the required parts. If the GBB is not supplied
582 then this will just return a signed U-Boot as the image.
583
584 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200585 gbb: Full path to the GBB file, or empty if a GBB is not required.
586 fdt: Fdt object containing required information.
587
588 Returns:
589 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700590
591 Raises:
592 CmdError if a command fails.
593 """
Simon Glass02d124a2012-03-02 14:47:20 -0800594 self._out.Notice("Model: %s" % fdt.GetString('/', 'model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700595
Simon Glass439fe7a2012-03-09 16:19:34 -0800596 # Get the flashmap so we know what to build
597 pack = PackFirmware(self._tools, self._out)
598 pack.SelectFdt(fdt)
599
600 # Get all our blobs ready
601 pack.AddProperty('boot', self.uboot_fname)
Simon Glass50f74602012-03-15 21:04:25 -0700602
603 # Make a copy of the fdt for the bootstub
604 fdt_data = self._tools.ReadFile(fdt.fname)
605 uboot_data = self._tools.ReadFile(self.uboot_fname)
606 uboot_copy = os.path.join(self._tools.outdir, 'u-boot.bin')
607 self._tools.WriteFile(uboot_copy, uboot_data)
608
609 bootstub = os.path.join(self._tools.outdir, 'u-boot-dtb.bin')
610 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
611 pack.AddProperty('boot+dtb', bootstub)
612
Simon Glass439fe7a2012-03-09 16:19:34 -0800613 pack.AddProperty('gbb', self.uboot_fname)
614 for blob_type in pack.GetBlobList(self.coreboot_fname is not None):
615 self._BuildBlob(pack, fdt, blob_type)
Simon Glass89b86b82011-07-17 23:49:49 -0700616
617 if gbb:
Simon Glasse76bf7b2012-03-13 15:34:41 -0700618 pack.RequireAllEntries()
Hung-Te Lina7462e72011-07-27 19:17:10 +0800619 fwid = '.'.join([
Simon Glass02d124a2012-03-02 14:47:20 -0800620 re.sub('[ ,]+', '_', fdt.GetString('/', 'model')),
Hung-Te Lina7462e72011-07-27 19:17:10 +0800621 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700622 self._out.Notice('Firmware ID: %s' % fwid)
Simon Glass439fe7a2012-03-09 16:19:34 -0800623 pack.AddProperty('fwid', fwid)
624 pack.AddProperty('gbb', gbb)
625 pack.AddProperty('keydir', self._keydir)
Simon Glassc90cf582012-03-13 15:40:47 -0700626
627 pack.CheckProperties()
628 image = os.path.join(self._tools.outdir, 'image.bin')
629 pack.PackImage(self._tools.outdir, image)
630 pack.AddProperty('image', image)
Simon Glass89b86b82011-07-17 23:49:49 -0700631
Simon Glass439fe7a2012-03-09 16:19:34 -0800632 image = pack.GetProperty('image')
Simon Glass89b86b82011-07-17 23:49:49 -0700633 self._tools.OutputSize('Final image', image)
Simon Glassc90cf582012-03-13 15:40:47 -0700634 return image, pack
Simon Glass89b86b82011-07-17 23:49:49 -0700635
Simon Glass290a1802011-07-17 13:54:32 -0700636 def SelectFdt(self, fdt_fname):
637 """Select an FDT to control the firmware bundling
638
639 Args:
640 fdt_fname: The filename of the fdt to use.
641
Simon Glassc0f3dc62011-08-09 14:19:05 -0700642 Returns:
643 The Fdt object of the original fdt file, which we will not modify.
644
Simon Glass290a1802011-07-17 13:54:32 -0700645 We make a copy of this which will include any on-the-fly changes we want
646 to make.
647 """
648 self._fdt_fname = fdt_fname
649 self.CheckOptions()
650 fdt = Fdt(self._tools, self._fdt_fname)
Simon Glass29b96ad2012-03-09 15:34:33 -0800651 fdt.Compile()
Simon Glass290a1802011-07-17 13:54:32 -0700652 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700653 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700654
Simon Glassc90cf582012-03-13 15:40:47 -0700655 def Start(self, hardware_id, output_fname, show_map):
Simon Glass290a1802011-07-17 13:54:32 -0700656 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700657
658 - Checks options, tools, output directory, fdt.
659 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700660
661 Args:
Simon Glass56577572011-07-19 11:08:06 +1200662 hardware_id: Hardware ID to use for this board. If None, then the
663 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700664 output_fname: Output filename for the image. If this is not None, then
665 the final image will be copied here.
Simon Glassc90cf582012-03-13 15:40:47 -0700666 show_map: Show a flash map, with each area's name and position
Simon Glass290a1802011-07-17 13:54:32 -0700667
668 Returns:
669 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700670 """
Simon Glass89b86b82011-07-17 23:49:49 -0700671 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700672 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200673 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700674
675 # This creates the actual image.
Simon Glassc90cf582012-03-13 15:40:47 -0700676 image, pack = self._CreateImage(gbb, self.fdt)
677 if show_map:
678 pack.ShowMap()
Simon Glass290a1802011-07-17 13:54:32 -0700679 if output_fname:
680 shutil.copyfile(image, output_fname)
681 self._out.Notice("Output image '%s'" % output_fname)
682 return image