blob: a605f15478791b0afb160751b66e2a19c2d95418 [file] [log] [blame]
Simon Glass89b86b82011-07-17 23:49:49 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This module builds a firmware image for a tegra-based board.
6
7This modules uses a few rudimentary other libraries for its activity.
8
9Here are the names we give to the various files we deal with. It is important
10to keep these consistent!
11
12 uboot u-boot.bin (with no device tree)
13 fdt the fdt blob
14 bct the BCT file
15 bootstub uboot + fdt
16 signed (uboot + fdt + bct) signed blob
17"""
18
Simon Glassceff3ff2012-04-04 11:23:45 -070019import glob
Simon Glass89b86b82011-07-17 23:49:49 -070020import os
21import re
22
23import cros_output
24from fdt import Fdt
25from pack_firmware import PackFirmware
26import shutil
Simon Glass7c2d5572011-11-15 14:47:08 -080027import struct
Simon Glass89b86b82011-07-17 23:49:49 -070028import tempfile
Simon Glass439fe7a2012-03-09 16:19:34 -080029from tools import CmdError
Simon Glass89b86b82011-07-17 23:49:49 -070030from tools import Tools
31from write_firmware import WriteFirmware
32
33# This data is required by bmpblk_utility. Does it ever change?
34# It was stored with the chromeos-bootimage ebuild, but we want
35# this utility to work outside the chroot.
36yaml_data = '''
37bmpblock: 1.0
38
39images:
40 devmode: DeveloperBmp/DeveloperBmp.bmp
41 recovery: RecoveryBmp/RecoveryBmp.bmp
42 rec_yuck: RecoveryNoOSBmp/RecoveryNoOSBmp.bmp
43 rec_insert: RecoveryMissingOSBmp/RecoveryMissingOSBmp.bmp
44
45screens:
46 dev_en:
47 - [0, 0, devmode]
48 rec_en:
49 - [0, 0, recovery]
50 yuck_en:
51 - [0, 0, rec_yuck]
52 ins_en:
53 - [0, 0, rec_insert]
54
55localizations:
56 - [ dev_en, rec_en, yuck_en, ins_en ]
57'''
58
59class Bundle:
Simon Glass290a1802011-07-17 13:54:32 -070060 """This class encapsulates the entire bundle firmware logic.
Simon Glass89b86b82011-07-17 23:49:49 -070061
Simon Glass290a1802011-07-17 13:54:32 -070062 Sequence of events:
63 bundle = Bundle(tools.Tools(), cros_output.Output())
64 bundle.SetDirs(...)
65 bundle.SetFiles(...)
66 bundle.SetOptions(...)
67 bundle.SelectFdt(fdt.Fdt('filename.dtb')
68 .. can call bundle.AddConfigList() if required
69 bundle.Start(...)
Simon Glass89b86b82011-07-17 23:49:49 -070070
Simon Glass290a1802011-07-17 13:54:32 -070071 Public properties:
72 fdt: The fdt object that we use for building our image. This wil be the
73 one specified by the user, except that we might add config options
74 to it. This is set up by SelectFdt() which must be called before
75 bundling starts.
76 uboot_fname: Full filename of the U-Boot binary we use.
77 bct_fname: Full filename of the BCT file we use.
78 """
Simon Glass89b86b82011-07-17 23:49:49 -070079
Simon Glass290a1802011-07-17 13:54:32 -070080 def __init__(self, tools, output):
81 """Set up a new Bundle object.
Simon Glass89b86b82011-07-17 23:49:49 -070082
Simon Glass290a1802011-07-17 13:54:32 -070083 Args:
84 tools: A tools.Tools object to use for external tools.
85 output: A cros_output.Output object to use for program output.
Simon Glass89b86b82011-07-17 23:49:49 -070086 """
Simon Glass290a1802011-07-17 13:54:32 -070087 self._tools = tools
88 self._out = output
89
90 # Set up the things we need to know in order to operate.
91 self._board = None # Board name, e.g. tegra2_seaboard.
92 self._fdt_fname = None # Filename of our FDT.
93 self.uboot_fname = None # Filename of our U-Boot binary.
94 self.bct_fname = None # Filename of our BCT file.
95 self.fdt = None # Our Fdt object.
Hung-Te Lin5b649382011-08-03 15:01:16 +080096 self.bmpblk_fname = None # Filename of our Bitmap Block
Stefan Reinauer8d79d362011-08-16 14:20:43 -070097 self.coreboot_fname = None # Filename of our coreboot binary.
Vincent Palatinf7286772011-10-12 14:31:53 -070098 self.seabios_fname = None # Filename of our SeaBIOS payload.
Simon Glass7e199222012-03-13 15:51:18 -070099 self.exynos_bl1 = None # Filename of Exynos BL1 (pre-boot)
100 self.exynos_bl2 = None # Filename of Exynos BL2 (SPL)
Simon Glass290a1802011-07-17 13:54:32 -0700101
102 def SetDirs(self, keydir):
103 """Set up directories required for Bundle.
104
105 Args:
106 keydir: Directory containing keys to use for signing firmware.
107 """
108 self._keydir = keydir
109
Simon Glass6dcc2f22011-07-28 15:26:49 +1200110 def SetFiles(self, board, bct, uboot=None, bmpblk=None, coreboot=None,
Simon Glass7e199222012-03-13 15:51:18 -0700111 postload=None, seabios=None, exynos_bl1=None, exynos_bl2=None):
Simon Glass290a1802011-07-17 13:54:32 -0700112 """Set up files required for Bundle.
113
114 Args:
115 board: The name of the board to target (e.g. tegra2_seaboard).
116 uboot: The filename of the u-boot.bin image to use.
117 bct: The filename of the binary BCT file to use.
Hung-Te Lin5b649382011-08-03 15:01:16 +0800118 bmpblk: The filename of bitmap block file to use.
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700119 coreboot: The filename of the coreboot image to use (on x86)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200120 postload: The filename of the u-boot-post.bin image to use.
Vincent Palatinf7286772011-10-12 14:31:53 -0700121 seabios: The filename of the SeaBIOS payload to use if any.
Simon Glass290a1802011-07-17 13:54:32 -0700122 """
123 self._board = board
124 self.uboot_fname = uboot
125 self.bct_fname = bct
Hung-Te Lin5b649382011-08-03 15:01:16 +0800126 self.bmpblk_fname = bmpblk
Stefan Reinauer8d79d362011-08-16 14:20:43 -0700127 self.coreboot_fname = coreboot
Simon Glass6dcc2f22011-07-28 15:26:49 +1200128 self.postload_fname = postload
Vincent Palatinf7286772011-10-12 14:31:53 -0700129 self.seabios_fname = seabios
Simon Glass7e199222012-03-13 15:51:18 -0700130 self.exynos_bl1 = exynos_bl1
131 self.exynos_bl2 = exynos_bl2
Simon Glass290a1802011-07-17 13:54:32 -0700132
133 def SetOptions(self, small):
134 """Set up options supported by Bundle.
135
136 Args:
137 small: Only create a signed U-Boot - don't produce the full packed
138 firmware image. This is useful for devs who want to replace just the
139 U-Boot part while keeping the keys, gbb, etc. the same.
140 """
141 self._small = small
142
143 def CheckOptions(self):
144 """Check provided options and select defaults."""
145 if not self._board:
146 raise ValueError('No board defined - please define a board to use')
Simon Glass493163b2011-09-14 11:19:57 -0700147 build_root = os.path.join('##', 'build', self._board, 'firmware')
Simon Glass290a1802011-07-17 13:54:32 -0700148 if not self._fdt_fname:
Simon Glassceff3ff2012-04-04 11:23:45 -0700149 # Figure out where the file should be, and the name we expect.
150 dir_name = os.path.join(build_root, 'dts')
151 base_name = re.sub('_', '-', self._board)
152
153 # In case the name exists with a prefix or suffix, find it.
154 wildcard = os.path.join(dir_name, '*%s*.dts' % base_name)
155 found_list = glob.glob(self._tools.Filename(wildcard))
156 if len(found_list) == 1:
157 self._fdt_fname = found_list[0]
158 else:
159 # We didn't find anything definite, so set up our expected name.
160 self._fdt_fname = os.path.join(dir_name, '%s.dts' % base_name)
161
Simon Glass290a1802011-07-17 13:54:32 -0700162 if not self.uboot_fname:
163 self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
164 if not self.bct_fname:
165 self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
Simon Glass2a7f0b32011-08-26 11:25:17 -0700166 if not self.bmpblk_fname:
167 self.bmpblk_fname = os.path.join(build_root, 'default.bmpblk')
Simon Glass1d376832012-03-15 20:50:54 -0700168 if not self.exynos_bl1:
169 self.exynos_bl1 = os.path.join(build_root, 'E5250.nbl1.bin')
170 if not self.exynos_bl2:
171 self.exynos_bl2 = os.path.join(build_root, 'smdk5250-spl.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700172
Simon Glass75759302012-03-15 20:26:53 -0700173 def GetFiles(self):
174 """Get a list of files that we know about.
175
176 This is the opposite of SetFiles except that we may have put in some
177 default names. It returns a dictionary containing the filename for
178 each of a number of pre-defined files.
179
180 Returns:
181 Dictionary, with one entry for each file.
182 """
183 file_list = {
184 'bct' : self.bct_fname,
185 'exynos-bl1' : self.exynos_bl1,
186 'exynos-bl2' : self.exynos_bl2,
187 }
188 return file_list
189
Simon Glass56577572011-07-19 11:08:06 +1200190 def _CreateGoogleBinaryBlock(self, hardware_id):
Simon Glass89b86b82011-07-17 23:49:49 -0700191 """Create a GBB for the image.
192
Simon Glass56577572011-07-19 11:08:06 +1200193 Args:
194 hardware_id: Hardware ID to use for this board. If None, then the
195 default from the Fdt will be used
196
Simon Glass89b86b82011-07-17 23:49:49 -0700197 Returns:
198 Path of the created GBB file.
199
200 Raises:
201 CmdError if a command fails.
202 """
Simon Glass56577572011-07-19 11:08:06 +1200203 if not hardware_id:
Simon Glass02d124a2012-03-02 14:47:20 -0800204 hardware_id = self.fdt.GetString('/config', 'hwid')
Simon Glass89b86b82011-07-17 23:49:49 -0700205 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
Simon Glass290a1802011-07-17 13:54:32 -0700206 odir = self._tools.outdir
Simon Glass89b86b82011-07-17 23:49:49 -0700207
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800208 chromeos_config = self.fdt.GetProps("/chromeos-config")
209 if 'fast-developer-mode' not in chromeos_config:
210 gbb_flags = 0
211 else:
212 self._out.Notice("Enabling fast-developer-mode.")
213 gbb_flags = 1
214
Simon Glass89b86b82011-07-17 23:49:49 -0700215 self._out.Progress('Creating GBB')
216 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
217 sizes = ['%#x' % size for size in sizes]
218 gbb = 'gbb.bin'
Simon Glass290a1802011-07-17 13:54:32 -0700219 keydir = self._tools.Filename(self._keydir)
220 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
Simon Glass89b86b82011-07-17 23:49:49 -0700221 self._tools.Run('gbb_utility', ['-s',
Simon Glass56577572011-07-19 11:08:06 +1200222 '--hwid=%s' % hardware_id,
Simon Glass89b86b82011-07-17 23:49:49 -0700223 '--rootkey=%s/root_key.vbpubk' % keydir,
224 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
Simon Glass2a7f0b32011-08-26 11:25:17 -0700225 '--bmpfv=%s' % self._tools.Filename(self.bmpblk_fname),
Stefan Reinauer975e68f2012-02-27 13:27:08 -0800226 '--flags=%d' % gbb_flags,
Simon Glass89b86b82011-07-17 23:49:49 -0700227 gbb],
Simon Glass290a1802011-07-17 13:54:32 -0700228 cwd=odir)
229 return os.path.join(odir, gbb)
Simon Glass89b86b82011-07-17 23:49:49 -0700230
Simon Glasse13ee2c2011-07-28 08:12:28 +1200231 def _SignBootstub(self, bct, bootstub, text_base):
Simon Glass89b86b82011-07-17 23:49:49 -0700232 """Sign an image so that the Tegra SOC will boot it.
233
234 Args:
235 bct: BCT file to use.
236 bootstub: Boot stub (U-Boot + fdt) file to sign.
237 text_base: Address of text base for image.
Simon Glass89b86b82011-07-17 23:49:49 -0700238
239 Returns:
240 filename of signed image.
241
242 Raises:
243 CmdError if a command fails.
244 """
245 # First create a config file - this is how we instruct cbootimage
Simon Glasse13ee2c2011-07-28 08:12:28 +1200246 signed = os.path.join(self._tools.outdir, 'signed.bin')
Simon Glass89b86b82011-07-17 23:49:49 -0700247 self._out.Progress('Signing Bootstub')
Simon Glasse13ee2c2011-07-28 08:12:28 +1200248 config = os.path.join(self._tools.outdir, 'boot.cfg')
Simon Glass89b86b82011-07-17 23:49:49 -0700249 fd = open(config, 'w')
250 fd.write('Version = 1;\n')
251 fd.write('Redundancy = 1;\n')
252 fd.write('Bctfile = %s;\n' % bct)
Doug Anderson0eeb0742011-09-15 18:11:40 -0700253
254 # TODO(dianders): Right now, we don't have enough space in our flash map
255 # for two copies of the BCT when we're using NAND, so hack it to 1. Not
256 # sure what this does for reliability, but at least things will fit...
257 is_nand = "NvBootDevType_Nand" in self._tools.Run('bct_dump', [bct])
258 if is_nand:
259 fd.write('Bctcopy = 1;\n')
260
Simon Glass89b86b82011-07-17 23:49:49 -0700261 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
262 text_base))
Doug Anderson0eeb0742011-09-15 18:11:40 -0700263
Simon Glass89b86b82011-07-17 23:49:49 -0700264 fd.close()
265
266 self._tools.Run('cbootimage', [config, signed])
267 self._tools.OutputSize('BCT', bct)
268 self._tools.OutputSize('Signed image', signed)
269 return signed
270
Doug Anderson86ce5f42011-07-27 10:40:18 -0700271 def SetBootcmd(self, bootcmd, bootsecure):
Simon Glass290a1802011-07-17 13:54:32 -0700272 """Set the boot command for U-Boot.
Simon Glass89b86b82011-07-17 23:49:49 -0700273
274 Args:
Simon Glass290a1802011-07-17 13:54:32 -0700275 bootcmd: Boot command to use, as a string (if None this this is a nop).
Doug Anderson86ce5f42011-07-27 10:40:18 -0700276 bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
Simon Glass89b86b82011-07-17 23:49:49 -0700277 """
Simon Glass290a1802011-07-17 13:54:32 -0700278 if bootcmd:
Simon Glass02d124a2012-03-02 14:47:20 -0800279 self.fdt.PutString('/config', 'bootcmd', bootcmd)
280 self.fdt.PutInteger('/config', 'bootsecure', int(bootsecure))
Simon Glass290a1802011-07-17 13:54:32 -0700281 self._out.Info('Boot command: %s' % bootcmd)
Simon Glass89b86b82011-07-17 23:49:49 -0700282
Simon Glass290a1802011-07-17 13:54:32 -0700283 def AddConfigList(self, config_list, use_int=False):
284 """Add a list of config items to the fdt.
285
286 Normally these values are written to the fdt as strings, but integers
287 are also supported, in which case the values will be converted to integers
288 (if necessary) before being stored.
289
290 Args:
291 config_list: List of (config, value) tuples to add to the fdt. For each
292 tuple:
293 config: The fdt node to write to will be /config/<config>.
294 value: An integer or string value to write.
295 use_int: True to only write integer values.
296
297 Raises:
298 CmdError: if a value is required to be converted to integer but can't be.
299 """
300 if config_list:
301 for config in config_list:
302 value = config[1]
303 if use_int:
304 try:
305 value = int(value)
306 except ValueError as str:
307 raise CmdError("Cannot convert config option '%s' to integer" %
308 value)
309 if type(value) == type(1):
Simon Glass02d124a2012-03-02 14:47:20 -0800310 self.fdt.PutInteger('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700311 else:
Simon Glass02d124a2012-03-02 14:47:20 -0800312 self.fdt.PutString('/config', '%s' % config[0], value)
Simon Glass290a1802011-07-17 13:54:32 -0700313
Simon Glass7c2d5572011-11-15 14:47:08 -0800314 def DecodeTextBase(self, data):
315 """Look at a U-Boot image and try to decode its TEXT_BASE.
316
317 This works because U-Boot has a header with the value 0x12345678
318 immediately followed by the TEXT_BASE value. We can therefore read this
319 from the image with some certainty. We check only the first 40 words
320 since the header should be within that region.
321
322 Args:
323 data: U-Boot binary data
324
325 Returns:
326 Text base (integer) or None if none was found
327 """
328 found = False
329 for i in range(0, 160, 4):
330 word = data[i:i + 4]
331
332 # TODO(sjg): This does not cope with a big-endian target
333 value = struct.unpack('<I', word)[0]
334 if found:
335 return value
336 if value == 0x12345678:
337 found = True
338
339 return None
340
341 def CalcTextBase(self, name, fdt, fname):
342 """Calculate the TEXT_BASE to use for U-Boot.
343
344 Normally this value is in the fdt, so we just read it from there. But as
345 a second check we look at the image itself in case this is different, and
346 switch to that if it is.
347
348 This allows us to flash any U-Boot even if its TEXT_BASE is different.
349 This is particularly useful with upstream U-Boot which uses a different
350 value (which we will move to).
351 """
352 data = self._tools.ReadFile(fname)
Simon Glass02d124a2012-03-02 14:47:20 -0800353 fdt_text_base = fdt.GetInt('/chromeos-config', 'textbase')
Simon Glass7c2d5572011-11-15 14:47:08 -0800354 text_base = self.DecodeTextBase(data)
355
356 # If they are different, issue a warning and switch over.
357 if text_base and text_base != fdt_text_base:
358 self._out.Warning("TEXT_BASE %x in %sU-Boot doesn't match "
359 "fdt value of %x. Using %x" % (text_base, name,
360 fdt_text_base, text_base))
361 fdt_text_base = text_base
362 return fdt_text_base
363
Simon Glass6dcc2f22011-07-28 15:26:49 +1200364 def _CreateBootStub(self, uboot, base_fdt, postload):
Simon Glass89b86b82011-07-17 23:49:49 -0700365 """Create a boot stub and a signed boot stub.
366
Simon Glass6dcc2f22011-07-28 15:26:49 +1200367 For postload:
368 We add a /config/postload-text-offset entry to the signed bootstub's
369 fdt so that U-Boot can find the postload code.
370
371 The raw (unsigned) bootstub will have a value of -1 for this since we will
372 simply append the postload code to the bootstub and it can find it there.
373 This will be used for RW A/B firmware.
374
375 For the signed case this value will specify where in the flash to find
376 the postload code. This will be used for RO firmware.
377
Simon Glass89b86b82011-07-17 23:49:49 -0700378 Args:
379 uboot: Path to u-boot.bin (may be chroot-relative)
Simon Glass29b96ad2012-03-09 15:34:33 -0800380 base_fdt: Fdt object containing the flat device tree.
Simon Glass6dcc2f22011-07-28 15:26:49 +1200381 postload: Path to u-boot-post.bin, or None if none.
Simon Glass89b86b82011-07-17 23:49:49 -0700382
383 Returns:
384 Tuple containing:
Simon Glass6dcc2f22011-07-28 15:26:49 +1200385 Full path to bootstub (uboot + fdt(-1) + postload).
386 Full path to signed (uboot + fdt(flash pos) + bct) + postload.
Simon Glass89b86b82011-07-17 23:49:49 -0700387
388 Raises:
389 CmdError if a command fails.
390 """
Simon Glasse13ee2c2011-07-28 08:12:28 +1200391 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
Simon Glass7c2d5572011-11-15 14:47:08 -0800392 text_base = self.CalcTextBase('', self.fdt, uboot)
Simon Glass89b86b82011-07-17 23:49:49 -0700393 uboot_data = self._tools.ReadFile(uboot)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200394
395 # Make a copy of the fdt for the bootstub
396 fdt = base_fdt.Copy(os.path.join(self._tools.outdir, 'bootstub.dtb'))
Simon Glass02d124a2012-03-02 14:47:20 -0800397 fdt.PutInteger('/config', 'postload-text-offset', 0xffffffff);
Simon Glass290a1802011-07-17 13:54:32 -0700398 fdt_data = self._tools.ReadFile(fdt.fname)
Simon Glasse13ee2c2011-07-28 08:12:28 +1200399
Simon Glass89b86b82011-07-17 23:49:49 -0700400 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
Simon Glass290a1802011-07-17 13:54:32 -0700401 self._tools.OutputSize('U-Boot binary', self.uboot_fname)
402 self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
Simon Glass89b86b82011-07-17 23:49:49 -0700403 self._tools.OutputSize('Combined binary', bootstub)
404
Simon Glasse13ee2c2011-07-28 08:12:28 +1200405 # Sign the bootstub; this is a combination of the board specific
Simon Glass89b86b82011-07-17 23:49:49 -0700406 # bct and the stub u-boot image.
Simon Glass290a1802011-07-17 13:54:32 -0700407 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
Simon Glasse13ee2c2011-07-28 08:12:28 +1200408 bootstub, text_base)
Simon Glass6dcc2f22011-07-28 15:26:49 +1200409
410 signed_postload = os.path.join(self._tools.outdir, 'signed-postload.bin')
411 data = self._tools.ReadFile(signed)
412
413 if postload:
414 # We must add postload to the bootstub since A and B will need to
415 # be able to find it without the /config/postload-text-offset mechanism.
416 bs_data = self._tools.ReadFile(bootstub)
417 bs_data += self._tools.ReadFile(postload)
418 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt-postload.bin')
419 self._tools.WriteFile(bootstub, bs_data)
420 self._tools.OutputSize('Combined binary with postload', bootstub)
421
422 # Now that we know the file size, adjust the fdt and re-sign
423 postload_bootstub = os.path.join(self._tools.outdir, 'postload.bin')
Simon Glass02d124a2012-03-02 14:47:20 -0800424 fdt.PutInteger('/config', 'postload-text-offset', len(data))
Simon Glass6dcc2f22011-07-28 15:26:49 +1200425 fdt_data = self._tools.ReadFile(fdt.fname)
426 self._tools.WriteFile(postload_bootstub, uboot_data + fdt_data)
427 signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
428 postload_bootstub, text_base)
429 if len(data) != os.path.getsize(signed):
430 raise CmdError('Signed file size changed from %d to %d after updating '
431 'fdt' % (len(data), os.path.getsize(signed)))
432
433 # Re-read the signed image, and add the post-load binary.
434 data = self._tools.ReadFile(signed)
435 data += self._tools.ReadFile(postload)
436 self._tools.OutputSize('Post-load binary', postload)
437
438 self._tools.WriteFile(signed_postload, data)
439 self._tools.OutputSize('Final bootstub with postload', signed_postload)
440
441 return bootstub, signed_postload
Simon Glass89b86b82011-07-17 23:49:49 -0700442
Vincent Palatinf7286772011-10-12 14:31:53 -0700443 def _CreateCorebootStub(self, uboot, coreboot, fdt, seabios):
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700444 """Create a coreboot boot stub.
445
446 Args:
447 uboot: Path to u-boot.bin (may be chroot-relative)
448 coreboot: Path to coreboot.rom
449 fdt: Device Tree
Vincent Palatinf7286772011-10-12 14:31:53 -0700450 seabios: Path to SeaBIOS payload binary or None
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700451
452 Returns:
453 Full path to bootstub (coreboot + uboot + fdt).
454
455 Raises:
456 CmdError if a command fails.
457 """
458 bootstub = os.path.join(self._tools.outdir, 'coreboot-full.rom')
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700459 uboot_elf = uboot.replace(".bin", ".elf")
460 shutil.copyfile(coreboot, bootstub)
Vincent Palatinf7286772011-10-12 14:31:53 -0700461 if seabios:
Simon Glass146cb8e2012-03-09 15:51:24 -0800462 self._tools.Run('cbfstool', [bootstub, 'add-payload', seabios,
Vincent Palatinf7286772011-10-12 14:31:53 -0700463 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800464 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700465 'img/U-Boot', 'lzma'])
466 else:
Simon Glass146cb8e2012-03-09 15:51:24 -0800467 self._tools.Run('cbfstool', [bootstub, 'add-payload', uboot_elf,
Vincent Palatinf7286772011-10-12 14:31:53 -0700468 'fallback/payload', 'lzma'])
Simon Glass146cb8e2012-03-09 15:51:24 -0800469 self._tools.Run('cbfstool', [bootstub, 'add', fdt.fname, 'u-boot.dtb',
Stefan Reinauerc2e1e4d2011-08-23 14:50:59 -0700470 '0xac'])
471 return bootstub
472
Simon Glass8e1fdb22012-03-15 21:02:10 -0700473 def _UpdateBl2Parameters(self, fdt, pack, data, pos):
Simon Glassdf95dd22012-03-13 15:46:16 -0700474 """Update the parameters in a BL2 blob.
475
476 We look at the list in the parameter block, extract the value of each
477 from the device tree, and write that value to the parameter block.
478
479 Args:
480 fdt: Device tree containing the parameter values.
Simon Glass8e1fdb22012-03-15 21:02:10 -0700481 pack: The firmware packer object
Simon Glassdf95dd22012-03-13 15:46:16 -0700482 data: The BL2 data.
483 pos: The position of the start of the parameter block.
484
485 Returns:
486 The new contents of the parameter block, after updating.
487 """
Simon Glassdf95dd22012-03-13 15:46:16 -0700488 version, size = struct.unpack('<2L', data[pos + 4:pos + 12])
489 if version != 1:
490 raise CmdError("Cannot update machine parameter block version '%d'" %
491 version)
492 if size < 0 or pos + size > len(data):
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700493 raise CmdError("Machine parameter block size %d is invalid: "
494 "pos=%d, size=%d, space=%d, len=%d" %
495 (size, pos, size, len(data) - pos, len(data)))
Simon Glassdf95dd22012-03-13 15:46:16 -0700496
497 # Move past the header and read the parameter list, which is terminated
498 # with \0.
499 pos += 12
500 param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0]
501 param_len = param_list.find('\0')
502 param_list = param_list[:param_len]
Simon Glass66c1a9f2012-03-22 19:15:53 -0700503 pos += (param_len + 4) & ~3
Simon Glassdf95dd22012-03-13 15:46:16 -0700504
505 # Work through the parameters one at a time, adding each value
506 new_data = ''
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700507 upto = 0
Simon Glassdf95dd22012-03-13 15:46:16 -0700508 for param in param_list:
Simon Glass2c48ddf2012-03-23 16:55:22 -0700509 value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0]
Simon Glassdf95dd22012-03-13 15:46:16 -0700510 if param == 'm' :
Simon Glass1a77ded2012-03-15 21:00:49 -0700511 mem_type = fdt.GetString('/dmc', 'mem-type')
Simon Glassdf95dd22012-03-13 15:46:16 -0700512 mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3']
513 if not mem_type in mem_types:
514 raise CmdError("Unknown memory type '%s'" % mem_type)
515 value = mem_types.index(mem_type)
516 self._out.Info(' Memory type: %s (%d)' % (mem_type, value))
517 elif param == 'v':
518 value = 31
519 self._out.Info(' Memory interleave: %#0x' % value)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700520 elif param == 'u':
Simon Glass2c48ddf2012-03-23 16:55:22 -0700521 # TODO(sjg): Seems to not work unless set to the same value as in the
522 # existing image. Need to find root cause.
523 #value = os.stat(pack.GetProperty('boot+dtb')).st_size
524 #value = (value + 0xfff) & ~0xfff
525 self._out.Warning("Leaving U-Boot size unchanged")
Simon Glass8e1fdb22012-03-15 21:02:10 -0700526 self._out.Info(' U-Boot size: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700527 else:
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700528 self._out.Warning("Unknown machine parameter type '%s'" % param)
Simon Glass2c48ddf2012-03-23 16:55:22 -0700529 self._out.Info(' Unknown value: %#0x' % value)
Simon Glassdf95dd22012-03-13 15:46:16 -0700530 new_data += struct.pack('<L', value)
Simon Glass7dbdf5b2012-03-15 20:58:04 -0700531 upto += 4
Simon Glassdf95dd22012-03-13 15:46:16 -0700532
533 # Put the data into our block.
534 data = data[:pos] + new_data + data[pos + len(new_data):]
535 self._out.Info('BL2 configuration complete')
536 return data
537
Simon Glass8e1fdb22012-03-15 21:02:10 -0700538 def _ConfigureExynosBl2(self, fdt, pack, orig_bl2):
Simon Glass7e199222012-03-13 15:51:18 -0700539 """Configure an Exynos BL2 binary for our needs.
540
541 We create a new modified BL2 and return its filename.
542
543 Args:
544 fdt: Device tree containing the parameter values.
Simon Glass8e1fdb22012-03-15 21:02:10 -0700545 pack: The firmware packer object
Simon Glass7e199222012-03-13 15:51:18 -0700546 orig_bl2: Filename of original BL2 file to modify.
547 """
Simon Glass66c1a9f2012-03-22 19:15:53 -0700548 self._out.Info('Configuring BL2')
Simon Glass7e199222012-03-13 15:51:18 -0700549 bl2 = os.path.join(self._tools.outdir, 'updated-spl.bin')
Simon Glass66c1a9f2012-03-22 19:15:53 -0700550 data = self._tools.ReadFile(orig_bl2)
551 self._tools.WriteFile(bl2, data)
Simon Glass7e199222012-03-13 15:51:18 -0700552
553 # Locate the parameter block
554 data = self._tools.ReadFile(bl2)
555 marker = struct.pack('<L', 0xdeadbeef)
556 pos = data.rfind(marker)
557 if not pos:
558 raise CmdError("Could not find machine parameter block in '%s'" %
559 orig_bl2)
Simon Glass8e1fdb22012-03-15 21:02:10 -0700560 data = self._UpdateBl2Parameters(fdt, pack, data, pos)
Simon Glass7e199222012-03-13 15:51:18 -0700561 self._tools.WriteFile(bl2, data)
562 return bl2
563
Simon Glass89b86b82011-07-17 23:49:49 -0700564 def _PackOutput(self, msg):
565 """Helper function to write output from PackFirmware (verbose level 2).
566
567 This is passed to PackFirmware for it to use to write output.
568
569 Args:
570 msg: Message to display.
571 """
572 self._out.Notice(msg)
573
Simon Glass439fe7a2012-03-09 16:19:34 -0800574 def _BuildBlob(self, pack, fdt, blob_type):
575 """Build the blob data for a particular blob type.
576
577 Args:
578 blob_type: The type of blob to create data for. Supported types are:
579 coreboot A coreboot image (ROM plus U-boot and .dtb payloads).
580 signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb).
581 """
582 if blob_type == 'coreboot':
583 coreboot = self._CreateCorebootStub(self.uboot_fname,
584 self.coreboot_fname, fdt, self.seabios_fname)
585 pack.AddProperty('coreboot', coreboot)
586 pack.AddProperty('image', coreboot)
587 elif blob_type == 'signed':
588 bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt,
589 self.postload_fname)
590 pack.AddProperty('bootstub', bootstub)
591 pack.AddProperty('signed', signed)
592 pack.AddProperty('image', signed)
Simon Glass7e199222012-03-13 15:51:18 -0700593 elif blob_type == 'exynos-bl1':
594 pack.AddProperty(blob_type, self.exynos_bl1)
595 elif blob_type == 'exynos-bl2':
Simon Glass8e1fdb22012-03-15 21:02:10 -0700596 bl2 = self._ConfigureExynosBl2(fdt, pack, self.exynos_bl2)
Simon Glass7e199222012-03-13 15:51:18 -0700597 pack.AddProperty(blob_type, bl2)
Simon Glass439fe7a2012-03-09 16:19:34 -0800598 elif pack.GetProperty(blob_type):
599 pass
600 else:
601 raise CmdError("Unknown blob type '%s' required in flash map" %
602 blob_type)
603
Simon Glass290a1802011-07-17 13:54:32 -0700604 def _CreateImage(self, gbb, fdt):
Simon Glass89b86b82011-07-17 23:49:49 -0700605 """Create a full firmware image, along with various by-products.
606
607 This uses the provided u-boot.bin, fdt and bct to create a firmware
608 image containing all the required parts. If the GBB is not supplied
609 then this will just return a signed U-Boot as the image.
610
611 Args:
Simon Glasse13ee2c2011-07-28 08:12:28 +1200612 gbb: Full path to the GBB file, or empty if a GBB is not required.
613 fdt: Fdt object containing required information.
614
615 Returns:
616 Path to image file
Simon Glass89b86b82011-07-17 23:49:49 -0700617
618 Raises:
619 CmdError if a command fails.
620 """
Simon Glass02d124a2012-03-02 14:47:20 -0800621 self._out.Notice("Model: %s" % fdt.GetString('/', 'model'))
Simon Glass89b86b82011-07-17 23:49:49 -0700622
Simon Glass439fe7a2012-03-09 16:19:34 -0800623 # Get the flashmap so we know what to build
624 pack = PackFirmware(self._tools, self._out)
625 pack.SelectFdt(fdt)
626
627 # Get all our blobs ready
628 pack.AddProperty('boot', self.uboot_fname)
Simon Glass50f74602012-03-15 21:04:25 -0700629
630 # Make a copy of the fdt for the bootstub
631 fdt_data = self._tools.ReadFile(fdt.fname)
632 uboot_data = self._tools.ReadFile(self.uboot_fname)
633 uboot_copy = os.path.join(self._tools.outdir, 'u-boot.bin')
634 self._tools.WriteFile(uboot_copy, uboot_data)
635
636 bootstub = os.path.join(self._tools.outdir, 'u-boot-dtb.bin')
637 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
638 pack.AddProperty('boot+dtb', bootstub)
639
Simon Glass439fe7a2012-03-09 16:19:34 -0800640 pack.AddProperty('gbb', self.uboot_fname)
641 for blob_type in pack.GetBlobList(self.coreboot_fname is not None):
642 self._BuildBlob(pack, fdt, blob_type)
Simon Glass89b86b82011-07-17 23:49:49 -0700643
644 if gbb:
Simon Glasse76bf7b2012-03-13 15:34:41 -0700645 pack.RequireAllEntries()
Hung-Te Lina7462e72011-07-27 19:17:10 +0800646 fwid = '.'.join([
Simon Glass02d124a2012-03-02 14:47:20 -0800647 re.sub('[ ,]+', '_', fdt.GetString('/', 'model')),
Hung-Te Lina7462e72011-07-27 19:17:10 +0800648 self._tools.GetChromeosVersion()])
Simon Glass89b86b82011-07-17 23:49:49 -0700649 self._out.Notice('Firmware ID: %s' % fwid)
Simon Glass439fe7a2012-03-09 16:19:34 -0800650 pack.AddProperty('fwid', fwid)
651 pack.AddProperty('gbb', gbb)
652 pack.AddProperty('keydir', self._keydir)
Simon Glassc90cf582012-03-13 15:40:47 -0700653
654 pack.CheckProperties()
655 image = os.path.join(self._tools.outdir, 'image.bin')
656 pack.PackImage(self._tools.outdir, image)
657 pack.AddProperty('image', image)
Simon Glass89b86b82011-07-17 23:49:49 -0700658
Simon Glass439fe7a2012-03-09 16:19:34 -0800659 image = pack.GetProperty('image')
Simon Glass89b86b82011-07-17 23:49:49 -0700660 self._tools.OutputSize('Final image', image)
Simon Glassc90cf582012-03-13 15:40:47 -0700661 return image, pack
Simon Glass89b86b82011-07-17 23:49:49 -0700662
Simon Glass290a1802011-07-17 13:54:32 -0700663 def SelectFdt(self, fdt_fname):
664 """Select an FDT to control the firmware bundling
665
666 Args:
667 fdt_fname: The filename of the fdt to use.
668
Simon Glassc0f3dc62011-08-09 14:19:05 -0700669 Returns:
670 The Fdt object of the original fdt file, which we will not modify.
671
Simon Glass290a1802011-07-17 13:54:32 -0700672 We make a copy of this which will include any on-the-fly changes we want
673 to make.
674 """
675 self._fdt_fname = fdt_fname
676 self.CheckOptions()
677 fdt = Fdt(self._tools, self._fdt_fname)
Simon Glass29b96ad2012-03-09 15:34:33 -0800678 fdt.Compile()
Simon Glass290a1802011-07-17 13:54:32 -0700679 self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
Simon Glassc0f3dc62011-08-09 14:19:05 -0700680 return fdt
Simon Glass290a1802011-07-17 13:54:32 -0700681
Simon Glassc90cf582012-03-13 15:40:47 -0700682 def Start(self, hardware_id, output_fname, show_map):
Simon Glass290a1802011-07-17 13:54:32 -0700683 """This creates a firmware bundle according to settings provided.
Simon Glass89b86b82011-07-17 23:49:49 -0700684
685 - Checks options, tools, output directory, fdt.
686 - Creates GBB and image.
Simon Glass290a1802011-07-17 13:54:32 -0700687
688 Args:
Simon Glass56577572011-07-19 11:08:06 +1200689 hardware_id: Hardware ID to use for this board. If None, then the
690 default from the Fdt will be used
Simon Glass290a1802011-07-17 13:54:32 -0700691 output_fname: Output filename for the image. If this is not None, then
692 the final image will be copied here.
Simon Glassc90cf582012-03-13 15:40:47 -0700693 show_map: Show a flash map, with each area's name and position
Simon Glass290a1802011-07-17 13:54:32 -0700694
695 Returns:
696 Filename of the resulting image (not the output_fname copy).
Simon Glass89b86b82011-07-17 23:49:49 -0700697 """
Simon Glass89b86b82011-07-17 23:49:49 -0700698 gbb = ''
Simon Glass290a1802011-07-17 13:54:32 -0700699 if not self._small:
Simon Glass56577572011-07-19 11:08:06 +1200700 gbb = self._CreateGoogleBinaryBlock(hardware_id)
Simon Glass89b86b82011-07-17 23:49:49 -0700701
702 # This creates the actual image.
Simon Glassc90cf582012-03-13 15:40:47 -0700703 image, pack = self._CreateImage(gbb, self.fdt)
704 if show_map:
705 pack.ShowMap()
Simon Glass290a1802011-07-17 13:54:32 -0700706 if output_fname:
707 shutil.copyfile(image, output_fname)
708 self._out.Notice("Output image '%s'" % output_fname)
709 return image