diff --git a/core/Makefile b/core/Makefile index 03bd5f08e..17a0317b8 100644 --- a/core/Makefile +++ b/core/Makefile @@ -507,8 +507,28 @@ INTERNAL_BOOTIMAGE_ARGS := \ $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \ --kernel $(INSTALLED_KERNEL_TARGET) +INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS := \ + --kernel $(INSTALLED_KERNEL_TARGET) \ + --rootfs_with_hashes $(PRODUCT_OUT)/system.img + +ifdef BOARD_BVB_ROLLBACK_INDEX +INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS += \ + --rollback_index $(BOARD_BVB_ROLLBACK_INDEX) +endif + +ifndef BOARD_BVB_KEY_PATH +# If key path isn't specified, use the 4096-bit test key. +INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS := --algorithm SHA256_RSA4096 \ + --key system/bvb/test/testkey_rsa4096.pem +else +INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS := \ + --algorithm $(BOARD_BVB_ALGORITHM) --key $(BOARD_BVB_KEY_PATH) +endif + + ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET) +INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS += --initrd $(INSTALLED_RAMDISK_TARGET) endif INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS)) @@ -516,6 +536,7 @@ INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS)) BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE)) ifdef BOARD_KERNEL_CMDLINE INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)" + INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS += --kernel_cmdline "$(BOARD_KERNEL_CMDLINE)" endif BOARD_KERNEL_BASE := $(strip $(BOARD_KERNEL_BASE)) @@ -541,6 +562,23 @@ ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) endif endif +ifeq ($(BOARD_BVB_ENABLE),true) + +$(INSTALLED_BOOTIMAGE_TARGET): $(BVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(PRODUCT_OUT)/system.img + $(call pretty,"Target boot image: $@") + $(hide) $(BVBTOOL) make_boot_image $(INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS) $(BOARD_BVB_MAKE_BOOT_IMAGE_ARGS) --output $@ + $(hide) $(BVBTOOL) sign_boot_image $(INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS) $(BOARD_BVB_SIGN_BOOT_IMAGE_ARGS) --image $@ + $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE)) + +.PHONY: bootimage-nodeps +bootimage-nodeps: $(BVBTOOL) + @echo "make $@: ignoring dependencies" + $(hide) $(BVBTOOL) make_boot_image $(INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS) $(BOARD_BVB_MAKE_BOOT_IMAGE_ARGS) --output $(INSTALLED_BOOTIMAGE_TARGET) + $(hide) $(BVBTOOL) sign_boot_image $(INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS) $(BOARD_BVB_SIGN_BOOT_IMAGE_ARGS) --image $(INSTALLED_BOOTIMAGE_TARGET) + $(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE)) + +else # BOARD_BVB_ENABLE + # We build recovery as boot image if BOARD_USES_RECOVERY_AS_BOOT is true. ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true) ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true) @@ -590,6 +628,7 @@ bootimage-nodeps: $(MKBOOTIMG) endif # TARGET_BOOTIMAGE_USE_EXT2 endif # BOARD_USES_RECOVERY_AS_BOOT +endif # BOARD_BVB_ENABLE else # TARGET_NO_KERNEL # HACK: The top-level targets depend on the bootimage. Not all targets @@ -1128,8 +1167,13 @@ define build-systemimage-target fi; \ mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \ exit 1 ) + $(if $(BOARD_BVB_ENABLE), $(hide) $(BVBTOOL) add_image_hashes $(BOARD_BVB_ADD_IMAGE_HASHES_ARGS) --image $(1)) endef +ifeq ($(BOARD_BVB_ENABLE),true) +FULL_SYSTEMIMAGE_DEPS += $(BVBTOOL) +endif + $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(call build-systemimage-target,$@) @@ -1772,6 +1816,15 @@ ifneq ($(strip $(SANITIZE_TARGET)),) endif ifeq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true) $(hide) echo "full_recovery_image=true" >> $(zip_root)/META/misc_info.txt +endif +ifeq ($(BOARD_BVB_ENABLE),true) + $(hide) echo "board_bvb_enable=true" >> $(zip_root)/META/misc_info.txt + $(hide) echo "board_bvb_make_boot_image_args=$(BOARD_BVB_MAKE_BOOT_IMAGE_ARGS)" >> $(zip_root)/META/misc_info.txt + $(hide) echo "board_bvb_sign_boot_image_args=$(BOARD_BVB_SIGN_BOOT_IMAGE_ARGS)" >> $(zip_root)/META/misc_info.txt + $(hide) echo "board_bvb_algorithm=$(BOARD_BVB_ALGORITHM)" >> $(zip_root)/META/misc_info.txt + $(hide) echo "board_bvb_key_path=$(BOARD_BVB_KEY_PATH)" >> $(zip_root)/META/misc_info.txt + $(hide) echo "board_bvb_rollback_index=$(BOARD_BVB_ROLLBACK_INDEX)" >> $(zip_root)/META/misc_info.txt + $(hide) echo "board_bvb_add_image_hashes_args=$(BOARD_BVB_ADD_IMAGE_HASHES_ARGS)" >> $(zip_root)/META/misc_info.txt endif $(call generate-userimage-prop-dictionary, $(zip_root)/META/misc_info.txt) ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) diff --git a/core/config.mk b/core/config.mk index c772c53e6..4562ebed4 100644 --- a/core/config.mk +++ b/core/config.mk @@ -528,6 +528,11 @@ MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX) else MKBOOTIMG := $(BOARD_CUSTOM_MKBOOTIMG) endif +ifeq (,$(strip $(BOARD_CUSTOM_BVBTOOL))) +BVBTOOL := $(HOST_OUT_EXECUTABLES)/bvbtool$(HOST_EXECUTABLE_SUFFIX) +else +BVBTOOL := $(BOARD_CUSTOM_BVBTOOL) +endif APICHECK := $(HOST_OUT_EXECUTABLES)/apicheck$(HOST_EXECUTABLE_SUFFIX) FS_GET_STATS := $(HOST_OUT_EXECUTABLES)/fs_get_stats$(HOST_EXECUTABLE_SUFFIX) MAKE_EXT4FS := $(HOST_OUT_EXECUTABLES)/make_ext4fs$(HOST_EXECUTABLE_SUFFIX) diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py index 7cb907260..d0027dc71 100755 --- a/tools/releasetools/add_img_to_target_files.py +++ b/tools/releasetools/add_img_to_target_files.py @@ -31,7 +31,9 @@ if sys.hexversion < 0x02070000: import datetime import errno import os +import shlex import shutil +import subprocess import tempfile import zipfile @@ -48,12 +50,12 @@ OPTIONS.verity_signer_path = None 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 @@ def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None): 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 @@ def AddImagesToTargetFiles(filename): 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 @@ def AddImagesToTargetFiles(filename): 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 563ce31a1..62ccc452a 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -473,8 +473,114 @@ def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None, 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 @@ def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir, 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