diff options
Diffstat (limited to 'tools/releasetools/add_img_to_target_files.py')
-rw-r--r-- | tools/releasetools/add_img_to_target_files.py | 302 |
1 files changed, 160 insertions, 142 deletions
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py index f58b6978a8..c583d013b3 100644 --- a/tools/releasetools/add_img_to_target_files.py +++ b/tools/releasetools/add_img_to_target_files.py @@ -51,6 +51,7 @@ import logging import os import shlex import shutil +import stat import sys import uuid import zipfile @@ -58,9 +59,11 @@ import zipfile import build_image import build_super_image import common -import rangelib -import sparse_img import verity_utils +import ota_metadata_pb2 + +from apex_utils import GetApexInfoFromTargetFiles +from common import AddCareMapForAbOta if sys.hexversion < 0x02070000: print("Python 2.7 or newer is required.", file=sys.stderr) @@ -93,58 +96,19 @@ class OutputFile(object): name: The name of the output file, regardless of the final destination. """ - def __init__(self, output_zip, input_dir, prefix, name): + def __init__(self, output_zip, input_dir, *args): # We write the intermediate output file under the given input_dir, even if # the final destination is a zip archive. - self.name = os.path.join(input_dir, prefix, name) + self.name = os.path.join(input_dir, *args) self._output_zip = output_zip if self._output_zip: - self._zip_name = os.path.join(prefix, name) + self._zip_name = os.path.join(*args) def Write(self): if self._output_zip: common.ZipWrite(self._output_zip, self.name, self._zip_name) -def GetCareMap(which, imgname): - """Returns the care_map string for the given partition. - - Args: - which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP. - imgname: The filename of the image. - - Returns: - (which, care_map_ranges): care_map_ranges is the raw string of the care_map - RangeSet; or None. - """ - assert which in common.PARTITIONS_WITH_CARE_MAP - - # which + "_image_size" contains the size that the actual filesystem image - # resides in, which is all that needs to be verified. The additional blocks in - # the image file contain verity metadata, by reading which would trigger - # invalid reads. - image_size = OPTIONS.info_dict.get(which + "_image_size") - if not image_size: - return None - - image_blocks = int(image_size) // 4096 - 1 - assert image_blocks > 0, "blocks for {} must be positive".format(which) - - # For sparse images, we will only check the blocks that are listed in the care - # map, i.e. the ones with meaningful data. - if "extfs_sparse_flag" in OPTIONS.info_dict: - simg = sparse_img.SparseImage(imgname) - care_map_ranges = simg.care_map.intersect( - rangelib.RangeSet("0-{}".format(image_blocks))) - - # Otherwise for non-sparse images, we read all the blocks in the filesystem - # image. - else: - care_map_ranges = rangelib.RangeSet("0-{}".format(image_blocks)) - - return [which, care_map_ranges.to_string_raw()] - - def AddSystem(output_zip, recovery_img=None, boot_img=None): """Turn the contents of SYSTEM into a system image and store it in output_zip. Returns the name of the system image file.""" @@ -178,7 +142,6 @@ def AddSystem(output_zip, recovery_img=None, boot_img=None): block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.map") CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img, block_list=block_list) - return img.name @@ -281,6 +244,38 @@ def AddOdm(output_zip): return img.name +def AddVendorDlkm(output_zip): + """Turn the contents of VENDOR_DLKM into an vendor_dlkm image and store it in output_zip.""" + + img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.img") + if os.path.exists(img.name): + logger.info("vendor_dlkm.img already exists; no need to rebuild...") + return img.name + + block_list = OutputFile( + output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.map") + CreateImage( + OPTIONS.input_tmp, OPTIONS.info_dict, "vendor_dlkm", img, + block_list=block_list) + return img.name + + +def AddOdmDlkm(output_zip): + """Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip.""" + + img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.img") + if os.path.exists(img.name): + logger.info("odm_dlkm.img already exists; no need to rebuild...") + return img.name + + block_list = OutputFile( + output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.map") + CreateImage( + OPTIONS.input_tmp, OPTIONS.info_dict, "odm_dlkm", img, + block_list=block_list) + return img.name + + def AddDtbo(output_zip): """Adds the DTBO image. @@ -299,6 +294,9 @@ def AddDtbo(output_zip): # AVB-sign the image as needed. if OPTIONS.info_dict.get("avb_enable") == "true": + # Signing requires +w + os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR) + avbtool = OPTIONS.info_dict["avb_avbtool"] part_size = OPTIONS.info_dict["dtbo_size"] # The AVB hash footer will be replaced if already present. @@ -313,6 +311,43 @@ def AddDtbo(output_zip): img.Write() return img.name + +def AddPvmfw(output_zip): + """Adds the pvmfw image. + + Uses the image under IMAGES/ if it already exists. Otherwise looks for the + image under PREBUILT_IMAGES/, signs it as needed, and returns the image name. + """ + img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "pvmfw.img") + if os.path.exists(img.name): + logger.info("pvmfw.img already exists; no need to rebuild...") + return img.name + + pvmfw_prebuilt_path = os.path.join( + OPTIONS.input_tmp, "PREBUILT_IMAGES", "pvmfw.img") + assert os.path.exists(pvmfw_prebuilt_path) + shutil.copy(pvmfw_prebuilt_path, img.name) + + # AVB-sign the image as needed. + if OPTIONS.info_dict.get("avb_enable") == "true": + # Signing requires +w + os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR) + + avbtool = OPTIONS.info_dict["avb_avbtool"] + part_size = OPTIONS.info_dict["pvmfw_size"] + # The AVB hash footer will be replaced if already present. + cmd = [avbtool, "add_hash_footer", "--image", img.name, + "--partition_size", str(part_size), "--partition_name", "pvmfw"] + common.AppendAVBSigningArgs(cmd, "pvmfw") + args = OPTIONS.info_dict.get("avb_pvmfw_add_hash_footer_args") + if args and args.strip(): + cmd.extend(shlex.split(args)) + common.RunAndCheckOutput(cmd) + + img.Write() + return img.name + + def AddCustomImages(output_zip, partition_name): """Adds and signs custom images in IMAGES/. @@ -327,8 +362,6 @@ def AddCustomImages(output_zip, partition_name): AssertionError: If image can't be found. """ - partition_size = OPTIONS.info_dict.get( - "avb_{}_partition_size".format(partition_name)) key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name)) algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name)) extra_args = OPTIONS.info_dict.get( @@ -387,8 +420,9 @@ def CreateImage(input_dir, info_dict, what, output_file, block_list=None): image_props["block_list"] = block_list.name # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and - # build fingerprint). - build_info = common.BuildInfo(info_dict) + # build fingerprint). Also use the legacy build id, because the vbmeta digest + # isn't available at this point. + build_info = common.BuildInfo(info_dict, use_legacy_id=True) uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what) image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed)) hash_seed = "hash_seed-" + uuid_seed @@ -572,72 +606,6 @@ def CheckAbOtaImages(output_zip, ab_partitions): assert available, "Failed to find " + img_name -def AddCareMapForAbOta(output_zip, ab_partitions, image_paths): - """Generates and adds care_map.pb for a/b partition that has care_map. - - Args: - output_zip: The output zip file (needs to be already open), or None to - write care_map.pb to OPTIONS.input_tmp/. - ab_partitions: The list of A/B partitions. - image_paths: A map from the partition name to the image path. - """ - care_map_list = [] - for partition in ab_partitions: - partition = partition.strip() - if partition not in common.PARTITIONS_WITH_CARE_MAP: - continue - - verity_block_device = "{}_verity_block_device".format(partition) - avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition) - if (verity_block_device in OPTIONS.info_dict or - OPTIONS.info_dict.get(avb_hashtree_enable) == "true"): - image_path = image_paths[partition] - assert os.path.exists(image_path) - - care_map = GetCareMap(partition, image_path) - if not care_map: - continue - care_map_list += care_map - - # adds fingerprint field to the care_map - # TODO(xunchang) revisit the fingerprint calculation for care_map. - partition_props = OPTIONS.info_dict.get(partition + ".build.prop") - prop_name_list = ["ro.{}.build.fingerprint".format(partition), - "ro.{}.build.thumbprint".format(partition)] - - present_props = [x for x in prop_name_list if - partition_props and partition_props.GetProp(x)] - if not present_props: - logger.warning("fingerprint is not present for partition %s", partition) - property_id, fingerprint = "unknown", "unknown" - else: - property_id = present_props[0] - fingerprint = partition_props.GetProp(property_id) - care_map_list += [property_id, fingerprint] - - if not care_map_list: - return - - # Converts the list into proto buf message by calling care_map_generator; and - # writes the result to a temp file. - temp_care_map_text = common.MakeTempFile(prefix="caremap_text-", - suffix=".txt") - with open(temp_care_map_text, 'w') as text_file: - text_file.write('\n'.join(care_map_list)) - - temp_care_map = common.MakeTempFile(prefix="caremap-", suffix=".pb") - care_map_gen_cmd = ["care_map_generator", temp_care_map_text, temp_care_map] - common.RunAndCheckOutput(care_map_gen_cmd) - - care_map_path = "META/care_map.pb" - if output_zip and care_map_path not in output_zip.namelist(): - common.ZipWrite(output_zip, temp_care_map, arcname=care_map_path) - else: - shutil.copy(temp_care_map, os.path.join(OPTIONS.input_tmp, care_map_path)) - if output_zip: - OPTIONS.replace_updated_files_list.append(care_map_path) - - def AddPackRadioImages(output_zip, images): """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/. @@ -708,6 +676,34 @@ def ReplaceUpdatedFiles(zip_filename, files_list): common.ZipClose(output_zip) +def HasPartition(partition_name): + """Determines if the target files archive should build a given partition.""" + + return ((os.path.isdir( + os.path.join(OPTIONS.input_tmp, partition_name.upper())) and + OPTIONS.info_dict.get( + "building_{}_image".format(partition_name)) == "true") or + os.path.exists( + os.path.join(OPTIONS.input_tmp, "IMAGES", + "{}.img".format(partition_name)))) + +def AddApexInfo(output_zip): + apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system') + apex_metadata_proto = ota_metadata_pb2.ApexMetadata() + apex_metadata_proto.apex_info.extend(apex_infos) + apex_info_bytes = apex_metadata_proto.SerializeToString() + + output_file = os.path.join(OPTIONS.input_tmp, "META", "apex_info.pb") + with open(output_file, "wb") as ofile: + ofile.write(apex_info_bytes) + if output_zip: + arc_name = "META/apex_info.pb" + if arc_name in output_zip.namelist(): + OPTIONS.replace_updated_files_list.append(arc_name) + else: + common.ZipWrite(output_zip, output_file, arc_name) + + def AddImagesToTargetFiles(filename): """Creates and adds images (boot/recovery/system/...) to a target_files.zip. @@ -736,28 +732,18 @@ def AddImagesToTargetFiles(filename): has_boot = OPTIONS.info_dict.get("no_boot") != "true" has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true" - # {vendor,odm,product,system_ext}.img are unlike system.img or - # system_other.img. Because it could be built from source, or dropped into - # target_files.zip as a prebuilt blob. We consider either of them as - # {vendor,product,system_ext}.img being available, which could be - # used when generating vbmeta.img for AVB. - has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or - os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES", - "vendor.img"))) - has_odm = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "ODM")) or - os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES", - "odm.img"))) - has_product = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) or - os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES", - "product.img"))) - has_system_ext = (os.path.isdir(os.path.join(OPTIONS.input_tmp, - "SYSTEM_EXT")) or - os.path.exists(os.path.join(OPTIONS.input_tmp, - "IMAGES", - "system_ext.img"))) - has_system = os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM")) - has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp, - "SYSTEM_OTHER")) + # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system, system_other}.img + # can be built from source, or dropped into target_files.zip as a prebuilt blob. + has_vendor = HasPartition("vendor") + has_odm = HasPartition("odm") + has_vendor_dlkm = HasPartition("vendor_dlkm") + has_odm_dlkm = HasPartition("odm_dlkm") + has_product = HasPartition("product") + has_system_ext = HasPartition("system_ext") + has_system = HasPartition("system") + has_system_other = HasPartition("system_other") + has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true" + has_cache = OPTIONS.info_dict.get("building_cache_image") == "true" # Set up the output destination. It writes to the given directory for dir # mode; otherwise appends to the given ZIP. @@ -865,10 +851,20 @@ def AddImagesToTargetFiles(filename): banner("odm") partitions['odm'] = AddOdm(output_zip) + if has_vendor_dlkm: + banner("vendor_dlkm") + partitions['vendor_dlkm'] = AddVendorDlkm(output_zip) + + if has_odm_dlkm: + banner("odm_dlkm") + partitions['odm_dlkm'] = AddOdmDlkm(output_zip) + if has_system_other: banner("system_other") AddSystemOther(output_zip) + AddApexInfo(output_zip) + if not OPTIONS.is_signing: banner("userdata") AddUserdata(output_zip) @@ -883,6 +879,10 @@ def AddImagesToTargetFiles(filename): banner("dtbo") partitions['dtbo'] = AddDtbo(output_zip) + if OPTIONS.info_dict.get("has_pvmfw") == "true": + banner("pvmfw") + partitions['pvmfw'] = AddPvmfw(output_zip) + # Custom images. custom_partitions = OPTIONS.info_dict.get( "avb_custom_images_partition_list", "").strip().split() @@ -918,12 +918,14 @@ def AddImagesToTargetFiles(filename): if item not in vbmeta_vendor.split()] vbmeta_partitions.append("vbmeta_vendor") - banner("vbmeta") - AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions) + if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true": + banner("vbmeta") + AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions) if OPTIONS.info_dict.get("use_dynamic_partitions") == "true": - banner("super_empty") - AddSuperEmpty(output_zip) + if OPTIONS.info_dict.get("build_super_empty_partition") == "true": + banner("super_empty") + AddSuperEmpty(output_zip) if OPTIONS.info_dict.get("build_super_partition") == "true": if OPTIONS.info_dict.get( @@ -944,7 +946,9 @@ def AddImagesToTargetFiles(filename): # Generate care_map.pb for ab_partitions, then write this file to # target_files package. - AddCareMapForAbOta(output_zip, ab_partitions, partitions) + output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb") + AddCareMapForAbOta(output_zip if output_zip else output_care_map, + ab_partitions, partitions) # Radio images that need to be packed into IMAGES/, and product-img.zip. pack_radioimages_txt = os.path.join( @@ -953,6 +957,20 @@ def AddImagesToTargetFiles(filename): with open(pack_radioimages_txt) as f: AddPackRadioImages(output_zip, f.readlines()) + # Calculate the vbmeta digest and put the result in to META/ + boot_images = OPTIONS.info_dict.get("boot_images") + # Disable the digest calculation if the target_file is used as a container + # for boot images. + boot_container = boot_images and len(boot_images.split()) >= 2 + if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and + OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"): + avbtool = OPTIONS.info_dict["avb_avbtool"] + digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool) + vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META", + "vbmeta_digest.txt") + with open(vbmeta_digest_txt, 'w') as f: + f.write(digest) + if output_zip: common.ZipClose(output_zip) if OPTIONS.replace_updated_files_list: |