blob: f79289fcdd6b53617f1428062c95f22514ccb8ab [file] [log] [blame]
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This module builds a firmware image for a tegra-based board.
This modules uses a few rudimentary other libraries for its activity.
Here are the names we give to the various files we deal with. It is important
to keep these consistent!
uboot u-boot.bin (with no device tree)
fdt the fdt blob
bct the BCT file
bootstub uboot + fdt
signed (uboot + fdt + bct) signed blob
import os
import re
import cros_output
from fdt import Fdt
from pack_firmware import PackFirmware
import shutil
import tempfile
from tools import Tools
from write_firmware import WriteFirmware
# This data is required by bmpblk_utility. Does it ever change?
# It was stored with the chromeos-bootimage ebuild, but we want
# this utility to work outside the chroot.
yaml_data = '''
bmpblock: 1.0
devmode: DeveloperBmp/DeveloperBmp.bmp
recovery: RecoveryBmp/RecoveryBmp.bmp
rec_yuck: RecoveryNoOSBmp/RecoveryNoOSBmp.bmp
rec_insert: RecoveryMissingOSBmp/RecoveryMissingOSBmp.bmp
- [0, 0, devmode]
- [0, 0, recovery]
- [0, 0, rec_yuck]
- [0, 0, rec_insert]
- [ dev_en, rec_en, yuck_en, ins_en ]
class Bundle:
"""This class encapsulates the entire bundle firmware logic.
Sequence of events:
bundle = Bundle(tools.Tools(), cros_output.Output())
.. can call bundle.AddConfigList() if required
Public properties:
fdt: The fdt object that we use for building our image. This wil be the
one specified by the user, except that we might add config options
to it. This is set up by SelectFdt() which must be called before
bundling starts.
uboot_fname: Full filename of the U-Boot binary we use.
bct_fname: Full filename of the BCT file we use.
def __init__(self, tools, output):
"""Set up a new Bundle object.
tools: A tools.Tools object to use for external tools.
output: A cros_output.Output object to use for program output.
self.text_base = None # Base of U-Boot image in memory
self._tools = tools
self._out = output
# Set up the things we need to know in order to operate.
self._board = None # Board name, e.g. tegra2_seaboard.
self._fdt_fname = None # Filename of our FDT.
self.uboot_fname = None # Filename of our U-Boot binary.
self.bct_fname = None # Filename of our BCT file.
self.fdt = None # Our Fdt object.
self.bmpblk_fname = None # Filename of our Bitmap Block
def SetDirs(self, keydir):
"""Set up directories required for Bundle.
keydir: Directory containing keys to use for signing firmware.
self._keydir = keydir
def SetFiles(self, board, uboot, bct, bmpblk):
"""Set up files required for Bundle.
board: The name of the board to target (e.g. tegra2_seaboard).
uboot: The filename of the u-boot.bin image to use.
bct: The filename of the binary BCT file to use.
bmpblk: The filename of bitmap block file to use.
self._board = board
self.uboot_fname = uboot
self.bct_fname = bct
self.bmpblk_fname = bmpblk
def SetOptions(self, small):
"""Set up options supported by Bundle.
small: Only create a signed U-Boot - don't produce the full packed
firmware image. This is useful for devs who want to replace just the
U-Boot part while keeping the keys, gbb, etc. the same.
self._small = small
def CheckOptions(self):
"""Check provided options and select defaults."""
if not self._board:
raise ValueError('No board defined - please define a board to use')
build_root = os.path.join('##', 'build', self._board, 'u-boot')
if not self._fdt_fname:
self._fdt_fname = os.path.join(build_root, 'dtb', '%s.dtb' %
re.sub('_', '-', self._board))
if not self.uboot_fname:
self.uboot_fname = os.path.join(build_root, 'u-boot.bin')
if not self.bct_fname:
self.bct_fname = os.path.join(build_root, 'bct', 'board.bct')
def _CreateGoogleBinaryBlock(self, hardware_id):
"""Create a GBB for the image.
hardware_id: Hardware ID to use for this board. If None, then the
default from the Fdt will be used
Path of the created GBB file.
CmdError if a command fails.
if not hardware_id:
hardware_id = self.fdt.GetString('/config/hwid')
gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
odir = self._tools.outdir
if not self.bmpblk_fname:
# TODO(hungte) Remove this bitmap generation in future because we should
# always use pre-genereated bitmaps.
# Get LCD dimensions from the device tree.
screen_geometry = '%sx%s' % (self.fdt.GetInt('/lcd/width'),
# This is the magic directory that make_bmp_image writes to!
out_dir = 'out_%s' % re.sub(' ', '_', hardware_id)
bmp_dir = os.path.join(odir, out_dir)
self._out.Progress('Creating bitmaps')
self._tools.Run('make_bmp_image', [hardware_id, screen_geometry, 'arm'],
self._out.Progress('Creating bitmap block')
yaml = 'config.yaml'
self._tools.WriteFile(os.path.join(bmp_dir, yaml), yaml_data)
self._tools.Run('bmpblk_utility', ['-z', '2', '-c', yaml, 'bmpblk.bin'],
self.bmpblk_fname = os.path.join(out_dir, 'bmpblk.bin')
self._out.Progress('Creating GBB')
sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
sizes = ['%#x' % size for size in sizes]
gbb = 'gbb.bin'
keydir = self._tools.Filename(self._keydir)
self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=odir)
self._tools.Run('gbb_utility', ['-s',
'--hwid=%s' % hardware_id,
'--rootkey=%s/root_key.vbpubk' % keydir,
'--recoverykey=%s/recovery_key.vbpubk' % keydir,
'--bmpfv=%s' % self.bmpblk_fname,
return os.path.join(odir, gbb)
def _SignBootstub(self, bct, bootstub, text_base):
"""Sign an image so that the Tegra SOC will boot it.
bct: BCT file to use.
bootstub: Boot stub (U-Boot + fdt) file to sign.
text_base: Address of text base for image.
filename of signed image.
CmdError if a command fails.
# First create a config file - this is how we instruct cbootimage
signed = os.path.join(self._tools.outdir, 'signed.bin')
self._out.Progress('Signing Bootstub')
config = os.path.join(self._tools.outdir, 'boot.cfg')
fd = open(config, 'w')
fd.write('Version = 1;\n')
fd.write('Redundancy = 1;\n')
fd.write('Bctfile = %s;\n' % bct)
fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
self._tools.Run('cbootimage', [config, signed])
self._tools.OutputSize('BCT', bct)
self._tools.OutputSize('Signed image', signed)
return signed
def SetBootcmd(self, bootcmd, bootsecure):
"""Set the boot command for U-Boot.
bootcmd: Boot command to use, as a string (if None this this is a nop).
bootsecure: We'll set '/config/bootsecure' to 1 if True and 0 if False.
if bootcmd:
self.fdt.PutString('/config/bootcmd', bootcmd)
self.fdt.PutInteger('/config/bootsecure', int(bootsecure))
self._out.Info('Boot command: %s' % bootcmd)
def AddConfigList(self, config_list, use_int=False):
"""Add a list of config items to the fdt.
Normally these values are written to the fdt as strings, but integers
are also supported, in which case the values will be converted to integers
(if necessary) before being stored.
config_list: List of (config, value) tuples to add to the fdt. For each
config: The fdt node to write to will be /config/<config>.
value: An integer or string value to write.
use_int: True to only write integer values.
CmdError: if a value is required to be converted to integer but can't be.
if config_list:
for config in config_list:
value = config[1]
if use_int:
value = int(value)
except ValueError as str:
raise CmdError("Cannot convert config option '%s' to integer" %
if type(value) == type(1):
self.fdt.PutInteger('/config/%s' % config[0], value)
self.fdt.PutString('/config/%s' % config[0], value)
def _CreateBootStub(self, uboot, fdt):
"""Create a boot stub and a signed boot stub.
uboot: Path to u-boot.bin (may be chroot-relative)
text_base: Address of text base for image.
Tuple containing:
Full path to bootstub (uboot + fdt).
Full path to signed blob (uboot + fdt + bct).
CmdError if a command fails.
bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
text_base = self.fdt.GetInt('/chromeos-config/textbase');
uboot_data = self._tools.ReadFile(uboot)
fdt_data = self._tools.ReadFile(fdt.fname)
self._tools.WriteFile(bootstub, uboot_data + fdt_data)
self._tools.OutputSize('U-Boot binary', self.uboot_fname)
self._tools.OutputSize('U-Boot fdt', self._fdt_fname)
self._tools.OutputSize('Combined binary', bootstub)
# Sign the bootstub; this is a combination of the board specific
# bct and the stub u-boot image.
signed = self._SignBootstub(self._tools.Filename(self.bct_fname),
bootstub, text_base)
return bootstub, signed
def _PackOutput(self, msg):
"""Helper function to write output from PackFirmware (verbose level 2).
This is passed to PackFirmware for it to use to write output.
msg: Message to display.
def _CreateImage(self, gbb, fdt):
"""Create a full firmware image, along with various by-products.
This uses the provided u-boot.bin, fdt and bct to create a firmware
image containing all the required parts. If the GBB is not supplied
then this will just return a signed U-Boot as the image.
gbb: Full path to the GBB file, or empty if a GBB is not required.
fdt: Fdt object containing required information.
Path to image file
CmdError if a command fails.
self._out.Notice("Model: %s" % fdt.GetString('/model'))
# Create the boot stub, which is U-Boot plus an fdt and bct
bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt)
if gbb:
pack = PackFirmware(self._tools, self._out)
image = os.path.join(self._tools.outdir, 'image.bin')
fwid = '.'.join([
re.sub('[ ,]+', '_', fdt.GetString('/model')),
self._out.Notice('Firmware ID: %s' % fwid)
pack.SetupFiles(boot=bootstub, signed=signed, gbb=gbb,
fwid=fwid, keydir=self._keydir)
pack.PackImage(self._tools.outdir, image)
image = signed
self._tools.OutputSize('Final image', image)
return image
def SelectFdt(self, fdt_fname):
"""Select an FDT to control the firmware bundling
fdt_fname: The filename of the fdt to use.
We make a copy of this which will include any on-the-fly changes we want
to make.
self._fdt_fname = fdt_fname
fdt = Fdt(self._tools, self._fdt_fname)
self.fdt = fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
def Start(self, hardware_id, output_fname):
"""This creates a firmware bundle according to settings provided.
- Checks options, tools, output directory, fdt.
- Creates GBB and image.
hardware_id: Hardware ID to use for this board. If None, then the
default from the Fdt will be used
output_fname: Output filename for the image. If this is not None, then
the final image will be copied here.
Filename of the resulting image (not the output_fname copy).
gbb = ''
if not self._small:
gbb = self._CreateGoogleBinaryBlock(hardware_id)
# This creates the actual image.
image = self._CreateImage(gbb, self.fdt)
if output_fname:
shutil.copyfile(image, output_fname)
self._out.Notice("Output image '%s'" % output_fname)
return image