Add support for Brillo Verified Boot.
The following variables are introduced
BOARD_BVB_ENABLE: can be set to true to build boot.img and system.img
files compatible with Brillo Verfied Boot.
BOARD_BVB_ROLLBACK_INDEX: can be set to an integer to use for the
rollback index.
BOARD_BVB_KEY_PATH, BOARD_BVB_ALGORITHM: If set, the former must be a
path to the private key used to sign the boot image and the latter must
be the algorithm to use. If unset, a test-key stored in the tree will
be used.
BOARD_BVB_MAKE_BOOT_IMAGE_ARGS: Extra options to pass to 'bvbtool
make_boot_image'.
BOARD_BVB_SIGN_BOOT_IMAGE_ARGS: Extra options to pass to 'bvbtool
sign_boot_image'.
BOARD_BVB_ADD_IMAGE_HASHES_ARGS: Extra options to pass to 'bvbtool
add_image_hashes'.
BOARD_CUSTOM_BVBTOOL: Can be set to specify what bvbtool program to
use.
The existing BOARD_KERNEL_CMDLINE variable is also used, as are existing
kernel and initrd-related variables. Therefore, simply adding
BOARD_BVB_ENABLE := true
to an existing Makefile should do the trick.
Bug: 26185038
TEST=Added 'BOARD_BVB_ENABLE := true' to hardware/bsp/intel/soc/edison/soc.mk
and built an image and then ran bvbtool's info_boot_image and
info_image_hashes commands on the resulting boot.img and system.img
files and verified that the information was correct. Also ran 'm dist'
and verified that the boot.img and system.img files in the resulting
target_files.zip file had similar information.
Change-Id: I08045ed8b0cbddc7c3acdd3a6f2c4bb75cb44bbc
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 7cb9072..d0027dc 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -31,7 +31,9 @@
import datetime
import errno
import os
+import shlex
import shutil
+import subprocess
import tempfile
import zipfile
@@ -48,12 +50,12 @@
def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
"""Turn the contents of SYSTEM into a system image and store it in
- output_zip."""
+ output_zip. Returns the name of the system image file."""
prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img")
if os.path.exists(prebuilt_path):
print "system.img already exists in %s, no need to rebuild..." % (prefix,)
- return
+ return prebuilt_path
def output_sink(fn, data):
ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
@@ -68,8 +70,23 @@
block_list = common.MakeTempFile(prefix="system-blocklist-", suffix=".map")
imgname = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict,
block_list=block_list)
+
+ # If requested, calculate and add dm-verity integrity hashes and
+ # metadata to system.img.
+ if OPTIONS.info_dict.get("board_bvb_enable", None) == "true":
+ bvbtool = os.getenv('BVBTOOL') or "bvbtool"
+ cmd = [bvbtool, "add_image_hashes", "--image", imgname]
+ args = OPTIONS.info_dict.get("board_bvb_add_image_hashes_args", None)
+ if args and args.strip():
+ cmd.extend(shlex.split(args))
+ p = common.Run(cmd, stdout=subprocess.PIPE)
+ p.communicate()
+ assert p.returncode == 0, "bvbtool add_image_hashes of %s image failed" % (
+ os.path.basename(OPTIONS.input_tmp),)
+
common.ZipWrite(output_zip, imgname, prefix + "system.img")
common.ZipWrite(output_zip, block_list, prefix + "system.map")
+ return imgname
def BuildSystem(input_dir, info_dict, block_list=None):
@@ -275,23 +292,40 @@
compression=zipfile.ZIP_DEFLATED)
has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
+ system_root_image = (OPTIONS.info_dict.get("system_root_image", None) == "true")
+ board_bvb_enable = (OPTIONS.info_dict.get("board_bvb_enable", None) == "true")
+
+ # Brillo Verified Boot is incompatible with certain
+ # configurations. Explicitly check for these.
+ if board_bvb_enable:
+ assert not has_recovery, "has_recovery incompatible with bvb"
+ assert not system_root_image, "system_root_image incompatible with bvb"
+ assert not OPTIONS.rebuild_recovery, "rebuild_recovery incompatible with bvb"
+ assert not has_vendor, "VENDOR images currently incompatible with bvb"
def banner(s):
print "\n\n++++ " + s + " ++++\n\n"
- banner("boot")
prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
boot_image = None
if os.path.exists(prebuilt_path):
+ banner("boot")
print "boot.img already exists in IMAGES/, no need to rebuild..."
if OPTIONS.rebuild_recovery:
boot_image = common.GetBootableImage(
"IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
else:
- boot_image = common.GetBootableImage(
+ if board_bvb_enable:
+ # With Brillo Verified Boot, we need to build system.img before
+ # boot.img since the latter includes the dm-verity root hash and
+ # salt for the former.
+ pass
+ else:
+ banner("boot")
+ boot_image = common.GetBootableImage(
"IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
- if boot_image:
- boot_image.AddToZip(output_zip)
+ if boot_image:
+ boot_image.AddToZip(output_zip)
recovery_image = None
if has_recovery:
@@ -310,7 +344,17 @@
recovery_image.AddToZip(output_zip)
banner("system")
- AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image)
+ system_img_path = AddSystem(
+ output_zip, recovery_img=recovery_image, boot_img=boot_image)
+ if OPTIONS.info_dict.get("board_bvb_enable", None) == "true":
+ # If we're using Brillo Verified Boot, we can now build boot.img
+ # given that we have system.img.
+ banner("boot")
+ boot_image = common.GetBootableImage(
+ "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT",
+ system_img_path=system_img_path)
+ if boot_image:
+ boot_image.AddToZip(output_zip)
if has_vendor:
banner("vendor")
AddVendor(output_zip)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 563ce31..62ccc45 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -473,8 +473,114 @@
return data
+def _BuildBvbBootableImage(sourcedir, fs_config_file, system_img_path,
+ info_dict=None, has_ramdisk=False):
+ """Build a bootable image compatible with Brillo Verified Boot from the
+ specified sourcedir.
+
+ Take a kernel, cmdline, system image path, and optionally a ramdisk
+ directory from the input (in 'sourcedir'), and turn them into a boot
+ image. Return the image data, or None if sourcedir does not appear
+ to contains files for building the requested image.
+ """
+
+ def make_ramdisk():
+ ramdisk_img = tempfile.NamedTemporaryFile()
+
+ if os.access(fs_config_file, os.F_OK):
+ cmd = ["mkbootfs", "-f", fs_config_file,
+ os.path.join(sourcedir, "RAMDISK")]
+ else:
+ cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
+ p1 = Run(cmd, stdout=subprocess.PIPE)
+ p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
+
+ p2.wait()
+ p1.wait()
+ assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
+ assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
+
+ return ramdisk_img
+
+ if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
+ return None
+
+ if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
+ return None
+
+ if info_dict is None:
+ info_dict = OPTIONS.info_dict
+
+ img = tempfile.NamedTemporaryFile()
+
+ if has_ramdisk:
+ ramdisk_img = make_ramdisk()
+
+ # use BVBTOOL from environ, or "bvbtool" if empty or not set
+ bvbtool = os.getenv('BVBTOOL') or "bvbtool"
+
+ # First, create boot.img.
+ cmd = [bvbtool, "make_boot_image"]
+
+ fn = os.path.join(sourcedir, "cmdline")
+ if os.access(fn, os.F_OK):
+ cmd.append("--kernel_cmdline")
+ cmd.append(open(fn).read().rstrip("\n"))
+
+ cmd.extend(["--kernel", os.path.join(sourcedir, "kernel")])
+
+ if has_ramdisk:
+ cmd.extend(["--initrd", ramdisk_img.name])
+
+ cmd.extend(["--rootfs_with_hashes", system_img_path])
+
+ args = info_dict.get("board_bvb_make_boot_image_args", None)
+ if args and args.strip():
+ cmd.extend(shlex.split(args))
+
+ rollback_index = info_dict.get("board_bvb_rollback_index", None)
+ if rollback_index and rollback_index.strip():
+ cmd.extend(["--rollback_index", rollback_index.strip()])
+
+ cmd.extend(["--output", img.name])
+
+ p = Run(cmd, stdout=subprocess.PIPE)
+ p.communicate()
+ assert p.returncode == 0, "bvbtool make_boot_image of %s image failed" % (
+ os.path.basename(sourcedir),)
+
+ # Then, sign boot.img.
+ cmd = [bvbtool, "sign_boot_image", "--image", img.name]
+
+ algorithm = info_dict.get("board_bvb_algorithm", None)
+ key_path = info_dict.get("board_bvb_key_path", None)
+ if algorithm and algorithm.strip() and key_path and key_path.strip():
+ cmd.extend(["--algorithm", algorithm, "--key", key_path])
+ else:
+ cmd.extend(["--algorithm", "SHA256_RSA4096"])
+ cmd.extend(["--key", "system/bvb/test/testkey_rsa4096.pem"])
+
+ args = info_dict.get("board_bvb_sign_boot_image_args", None)
+ if args and args.strip():
+ cmd.extend(shlex.split(args))
+
+ p = Run(cmd, stdout=subprocess.PIPE)
+ p.communicate()
+ assert p.returncode == 0, "bvbtool sign_boot_image of %s image failed" % (
+ os.path.basename(sourcedir),)
+
+ img.seek(os.SEEK_SET, 0)
+ data = img.read()
+
+ if has_ramdisk:
+ ramdisk_img.close()
+ img.close()
+
+ return data
+
+
def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
- info_dict=None):
+ info_dict=None, system_img_path=None):
"""Return a File object with the desired bootable image.
Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
@@ -504,9 +610,14 @@
info_dict.get("recovery_as_boot") == "true")
fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
- data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
- os.path.join(unpack_dir, fs_config),
- info_dict, has_ramdisk)
+ if info_dict.get("board_bvb_enable", None) == "true":
+ data = _BuildBvbBootableImage(os.path.join(unpack_dir, tree_subdir),
+ os.path.join(unpack_dir, fs_config),
+ system_img_path, info_dict, has_ramdisk)
+ else:
+ data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
+ os.path.join(unpack_dir, fs_config),
+ info_dict, has_ramdisk)
if data:
return File(name, data)
return None