/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "host/commands/assemble_cvd/disk_flags.h" #include #include #include #include #include #include #include #include #include "common/libs/fs/shared_buf.h" #include "common/libs/utils/files.h" #include "common/libs/utils/size_utils.h" #include "common/libs/utils/subprocess.h" #include "host/commands/assemble_cvd/boot_config.h" #include "host/commands/assemble_cvd/boot_image_utils.h" #include "host/commands/assemble_cvd/disk_builder.h" #include "host/commands/assemble_cvd/flags_defaults.h" #include "host/commands/assemble_cvd/super_image_mixer.h" #include "host/commands/assemble_cvd/vendor_dlkm_utils.h" #include "host/libs/config/bootconfig_args.h" #include "host/libs/config/cuttlefish_config.h" #include "host/libs/config/data_image.h" #include "host/libs/config/inject.h" #include "host/libs/config/instance_nums.h" #include "host/libs/vm_manager/gem5_manager.h" // Taken from external/avb/avbtool.py; this define is not in the headers #define MAX_AVB_METADATA_SIZE 69632ul DECLARE_string(system_image_dir); DEFINE_string(boot_image, CF_DEFAULTS_BOOT_IMAGE, "Location of cuttlefish boot image. If empty it is assumed to be " "boot.img in the directory specified by -system_image_dir."); DEFINE_string( init_boot_image, CF_DEFAULTS_INIT_BOOT_IMAGE, "Location of cuttlefish init boot image. If empty it is assumed to " "be init_boot.img in the directory specified by -system_image_dir."); DEFINE_string(data_image, CF_DEFAULTS_DATA_IMAGE, "Location of the data partition image."); DEFINE_string(super_image, CF_DEFAULTS_SUPER_IMAGE, "Location of the super partition image."); DEFINE_string(misc_image, CF_DEFAULTS_MISC_IMAGE, "Location of the misc partition image. If the image does not " "exist, a blank new misc partition image is created."); DEFINE_string(misc_info_txt, "", "Location of the misc_info.txt file."); DEFINE_string(metadata_image, CF_DEFAULTS_METADATA_IMAGE, "Location of the metadata partition image " "to be generated."); DEFINE_string( vendor_boot_image, CF_DEFAULTS_VENDOR_BOOT_IMAGE, "Location of cuttlefish vendor boot image. If empty it is assumed to " "be vendor_boot.img in the directory specified by -system_image_dir."); DEFINE_string(vbmeta_image, CF_DEFAULTS_VBMETA_IMAGE, "Location of cuttlefish vbmeta image. If empty it is assumed to " "be vbmeta.img in the directory specified by -system_image_dir."); DEFINE_string( vbmeta_system_image, CF_DEFAULTS_VBMETA_SYSTEM_IMAGE, "Location of cuttlefish vbmeta_system image. If empty it is assumed to " "be vbmeta_system.img in the directory specified by -system_image_dir."); DEFINE_string( vbmeta_vendor_dlkm_image, CF_DEFAULTS_VBMETA_VENDOR_DLKM_IMAGE, "Location of cuttlefish vbmeta_vendor_dlkm image. If empty it is assumed " "to " "be vbmeta_vendor_dlkm.img in the directory specified by " "-system_image_dir."); DEFINE_string(linux_kernel_path, CF_DEFAULTS_LINUX_KERNEL_PATH, "Location of linux kernel for cuttlefish otheros flow."); DEFINE_string(linux_initramfs_path, CF_DEFAULTS_LINUX_INITRAMFS_PATH, "Location of linux initramfs.img for cuttlefish otheros flow."); DEFINE_string(linux_root_image, CF_DEFAULTS_LINUX_ROOT_IMAGE, "Location of linux root filesystem image for cuttlefish otheros flow."); DEFINE_string(fuchsia_zedboot_path, CF_DEFAULTS_FUCHSIA_ZEDBOOT_PATH, "Location of fuchsia zedboot path for cuttlefish otheros flow."); DEFINE_string(fuchsia_multiboot_bin_path, CF_DEFAULTS_FUCHSIA_MULTIBOOT_BIN_PATH, "Location of fuchsia multiboot bin path for cuttlefish otheros flow."); DEFINE_string(fuchsia_root_image, CF_DEFAULTS_FUCHSIA_ROOT_IMAGE, "Location of fuchsia root filesystem image for cuttlefish otheros flow."); DEFINE_string(custom_partition_path, CF_DEFAULTS_CUSTOM_PARTITION_PATH, "Location of custom image that will be passed as a \"custom\" partition" "to rootfs and can be used by /dev/block/by-name/custom"); DEFINE_string(blank_metadata_image_mb, CF_DEFAULTS_BLANK_METADATA_IMAGE_MB, "The size of the blank metadata image to generate, MB."); DEFINE_string( blank_sdcard_image_mb, CF_DEFAULTS_BLANK_SDCARD_IMAGE_MB, "If enabled, the size of the blank sdcard image to generate, MB."); DECLARE_string(ap_rootfs_image); DECLARE_string(bootloader); DECLARE_string(initramfs_path); DECLARE_string(kernel_path); DECLARE_bool(resume); DECLARE_bool(use_overlay); namespace cuttlefish { using APBootFlow = CuttlefishConfig::InstanceSpecific::APBootFlow; using vm_manager::Gem5Manager; Result ResolveInstanceFiles() { CF_EXPECT(!FLAGS_system_image_dir.empty(), "--system_image_dir must be specified."); std::vector system_image_dir = android::base::Split(FLAGS_system_image_dir, ","); std::string default_boot_image = ""; std::string default_init_boot_image = ""; std::string default_data_image = ""; std::string default_metadata_image = ""; std::string default_super_image = ""; std::string default_misc_image = ""; std::string default_misc_info_txt = ""; std::string default_vendor_boot_image = ""; std::string default_vbmeta_image = ""; std::string default_vbmeta_system_image = ""; std::string default_vbmeta_vendor_dlkm_image = ""; std::string cur_system_image_dir; std::string comma_str = ""; auto instance_nums = CF_EXPECT(InstanceNumsCalculator().FromGlobalGflags().Calculate()); for (int instance_index = 0; instance_index < instance_nums.size(); instance_index++) { if (instance_index < system_image_dir.size()) { cur_system_image_dir = system_image_dir[instance_index]; } else { // legacy variable or out of boundary. Vectorize by copy [0] to all instances cur_system_image_dir = system_image_dir[0]; } if (instance_index > 0) { comma_str = ","; } // If user did not specify location of either of these files, expect them to // be placed in --system_image_dir location. default_boot_image += comma_str + cur_system_image_dir + "/boot.img"; default_init_boot_image += comma_str + cur_system_image_dir + "/init_boot.img"; default_data_image += comma_str + cur_system_image_dir + "/userdata.img"; default_metadata_image += comma_str + cur_system_image_dir + "/metadata.img"; default_super_image += comma_str + cur_system_image_dir + "/super.img"; default_misc_image += comma_str + cur_system_image_dir + "/misc.img"; default_misc_info_txt += comma_str + cur_system_image_dir + "/misc_info.txt"; default_vendor_boot_image += comma_str + cur_system_image_dir + "/vendor_boot.img"; default_vbmeta_image += comma_str + cur_system_image_dir + "/vbmeta.img"; default_vbmeta_system_image += comma_str + cur_system_image_dir + "/vbmeta_system.img"; default_vbmeta_vendor_dlkm_image += comma_str + cur_system_image_dir + "/vbmeta_vendor_dlkm.img"; } SetCommandLineOptionWithMode("boot_image", default_boot_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("init_boot_image", default_init_boot_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("data_image", default_data_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("metadata_image", default_metadata_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("super_image", default_super_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("misc_image", default_misc_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("misc_info_txt", default_misc_info_txt.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("vendor_boot_image", default_vendor_boot_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("vbmeta_image", default_vbmeta_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("vbmeta_system_image", default_vbmeta_system_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("vbmeta_vendor_dlkm_image", default_vbmeta_vendor_dlkm_image.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); return {}; } std::vector linux_composite_disk_config( const CuttlefishConfig::InstanceSpecific& instance) { std::vector partitions; partitions.push_back(ImagePartition{ .label = "linux_esp", .image_file_path = AbsolutePath(instance.otheros_esp_image_path()), .type = kEfiSystemPartition, .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "linux_root", .image_file_path = AbsolutePath(instance.linux_root_image()), .read_only = FLAGS_use_overlay, }); return partitions; } std::vector fuchsia_composite_disk_config( const CuttlefishConfig::InstanceSpecific& instance) { std::vector partitions; partitions.push_back(ImagePartition{ .label = "fuchsia_esp", .image_file_path = AbsolutePath(instance.otheros_esp_image_path()), .type = kEfiSystemPartition, .read_only = FLAGS_use_overlay, }); return partitions; } std::vector android_composite_disk_config( const CuttlefishConfig::InstanceSpecific& instance) { std::vector partitions; partitions.push_back(ImagePartition{ .label = "misc", .image_file_path = AbsolutePath(instance.new_misc_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "boot_a", .image_file_path = AbsolutePath(instance.new_boot_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "boot_b", .image_file_path = AbsolutePath(instance.new_boot_image()), .read_only = FLAGS_use_overlay, }); const auto init_boot_path = instance.init_boot_image(); if (FileExists(init_boot_path)) { partitions.push_back(ImagePartition{ .label = "init_boot_a", .image_file_path = AbsolutePath(init_boot_path), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "init_boot_b", .image_file_path = AbsolutePath(init_boot_path), .read_only = FLAGS_use_overlay, }); } partitions.push_back(ImagePartition{ .label = "vendor_boot_a", .image_file_path = AbsolutePath(instance.new_vendor_boot_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "vendor_boot_b", .image_file_path = AbsolutePath(instance.new_vendor_boot_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "vbmeta_a", .image_file_path = AbsolutePath(instance.vbmeta_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "vbmeta_b", .image_file_path = AbsolutePath(instance.vbmeta_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "vbmeta_system_a", .image_file_path = AbsolutePath(instance.vbmeta_system_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "vbmeta_system_b", .image_file_path = AbsolutePath(instance.vbmeta_system_image()), .read_only = FLAGS_use_overlay, }); auto vbmeta_vendor_dlkm_img = instance.new_vbmeta_vendor_dlkm_image(); if (!FileExists(vbmeta_vendor_dlkm_img)) { vbmeta_vendor_dlkm_img = instance.vbmeta_vendor_dlkm_image(); } if (FileExists(vbmeta_vendor_dlkm_img)) { partitions.push_back(ImagePartition{ .label = "vbmeta_vendor_dlkm_a", .image_file_path = AbsolutePath(vbmeta_vendor_dlkm_img), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "vbmeta_vendor_dlkm_b", .image_file_path = AbsolutePath(vbmeta_vendor_dlkm_img), .read_only = FLAGS_use_overlay, }); } auto super_image = instance.new_super_image(); if (!FileExists(super_image)) { super_image = instance.super_image(); } partitions.push_back(ImagePartition{ .label = "super", .image_file_path = AbsolutePath(super_image), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "userdata", .image_file_path = AbsolutePath(instance.data_image()), .read_only = FLAGS_use_overlay, }); partitions.push_back(ImagePartition{ .label = "metadata", .image_file_path = AbsolutePath(instance.new_metadata_image()), .read_only = FLAGS_use_overlay, }); const auto custom_partition_path = instance.custom_partition_path(); if (!custom_partition_path.empty()) { partitions.push_back(ImagePartition{ .label = "custom", .image_file_path = AbsolutePath(custom_partition_path), .read_only = FLAGS_use_overlay, }); } return partitions; } std::vector GetApCompositeDiskConfig(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance) { std::vector partitions; if (instance.ap_boot_flow() == APBootFlow::Grub) { partitions.push_back(ImagePartition{ .label = "ap_esp", .image_file_path = AbsolutePath(instance.ap_esp_image_path()), .read_only = FLAGS_use_overlay, }); } partitions.push_back(ImagePartition{ .label = "ap_rootfs", .image_file_path = AbsolutePath(config.ap_rootfs_image()), .read_only = FLAGS_use_overlay, }); return partitions; } std::vector GetOsCompositeDiskConfig( const CuttlefishConfig::InstanceSpecific& instance) { switch (instance.boot_flow()) { case CuttlefishConfig::InstanceSpecific::BootFlow::Android: return android_composite_disk_config(instance); break; case CuttlefishConfig::InstanceSpecific::BootFlow::Linux: return linux_composite_disk_config(instance); break; case CuttlefishConfig::InstanceSpecific::BootFlow::Fuchsia: return fuchsia_composite_disk_config(instance); break; } } DiskBuilder OsCompositeDiskBuilder(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance) { return DiskBuilder() .Partitions(GetOsCompositeDiskConfig(instance)) .VmManager(config.vm_manager()) .CrosvmPath(instance.crosvm_binary()) .ConfigPath(instance.PerInstancePath("os_composite_disk_config.txt")) .HeaderPath(instance.PerInstancePath("os_composite_gpt_header.img")) .FooterPath(instance.PerInstancePath("os_composite_gpt_footer.img")) .CompositeDiskPath(instance.os_composite_disk_path()) .ResumeIfPossible(FLAGS_resume); } DiskBuilder ApCompositeDiskBuilder(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance) { return DiskBuilder() .Partitions(GetApCompositeDiskConfig(config, instance)) .VmManager(config.vm_manager()) .CrosvmPath(instance.crosvm_binary()) .ConfigPath(instance.PerInstancePath("ap_composite_disk_config.txt")) .HeaderPath(instance.PerInstancePath("ap_composite_gpt_header.img")) .FooterPath(instance.PerInstancePath("ap_composite_gpt_footer.img")) .CompositeDiskPath(instance.ap_composite_disk_path()) .ResumeIfPossible(FLAGS_resume); } std::vector persistent_composite_disk_config( const CuttlefishConfig::InstanceSpecific& instance) { std::vector partitions; // Note that if the position of uboot_env changes, the environment for // u-boot must be updated as well (see boot_config.cc and // cuttlefish.fragment in external/u-boot). partitions.push_back(ImagePartition{ .label = "uboot_env", .image_file_path = AbsolutePath(instance.uboot_env_image_path()), }); partitions.push_back(ImagePartition{ .label = "vbmeta", .image_file_path = AbsolutePath(instance.vbmeta_path()), }); if (!instance.protected_vm()) { partitions.push_back(ImagePartition{ .label = "frp", .image_file_path = AbsolutePath(instance.factory_reset_protected_path()), }); } if (instance.bootconfig_supported()) { partitions.push_back(ImagePartition{ .label = "bootconfig", .image_file_path = AbsolutePath(instance.persistent_bootconfig_path()), }); } return partitions; } std::vector persistent_ap_composite_disk_config( const CuttlefishConfig::InstanceSpecific& instance) { std::vector partitions; // Note that if the position of uboot_env changes, the environment for // u-boot must be updated as well (see boot_config.cc and // cuttlefish.fragment in external/u-boot). partitions.push_back(ImagePartition{ .label = "uboot_env", .image_file_path = AbsolutePath(instance.ap_uboot_env_image_path()), }); partitions.push_back(ImagePartition{ .label = "vbmeta", .image_file_path = AbsolutePath(instance.ap_vbmeta_path()), }); return partitions; } static uint64_t AvailableSpaceAtPath(const std::string& path) { struct statvfs vfs {}; if (statvfs(path.c_str(), &vfs) != 0) { int error_num = errno; LOG(ERROR) << "Could not find space available at " << path << ", error was " << strerror(error_num); return 0; } // f_frsize (block size) * f_bavail (free blocks) for unprivileged users. return static_cast(vfs.f_frsize) * vfs.f_bavail; } class KernelRamdiskRepacker : public SetupFeature { public: INJECT( KernelRamdiskRepacker(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance)) : config_(config), instance_(instance) {} // SetupFeature std::string Name() const override { return "KernelRamdiskRepacker"; } std::unordered_set Dependencies() const override { return {}; } bool Enabled() const override { // If we are booting a protected VM, for now, assume that image repacking // isn't trusted. Repacking requires resigning the image and keys from an // android host aren't trusted. return !instance_.protected_vm(); } protected: bool RepackVendorDLKM(const std::string& superimg_build_dir, const std::string& vendor_dlkm_build_dir, const std::string& ramdisk_path) { const auto new_vendor_dlkm_img = superimg_build_dir + "/vendor_dlkm_repacked.img"; const auto tmp_vendor_dlkm_img = new_vendor_dlkm_img + ".tmp"; if (!EnsureDirectoryExists(vendor_dlkm_build_dir).ok()) { LOG(ERROR) << "Failed to create directory " << vendor_dlkm_build_dir; return false; } const auto ramdisk_stage_dir = instance_.instance_dir() + "/ramdisk_staged"; if (!SplitRamdiskModules(ramdisk_path, ramdisk_stage_dir, vendor_dlkm_build_dir)) { LOG(ERROR) << "Failed to move ramdisk modules to vendor_dlkm"; return false; } // TODO(b/149866755) For now, we assume that vendor_dlkm is ext4. Add // logic to handle EROFS once the feature stablizes. if (!BuildVendorDLKM(vendor_dlkm_build_dir, false, tmp_vendor_dlkm_img)) { LOG(ERROR) << "Failed to build vendor_dlkm image from " << vendor_dlkm_build_dir; return false; } if (ReadFile(tmp_vendor_dlkm_img) == ReadFile(new_vendor_dlkm_img)) { LOG(INFO) << "vendor_dlkm unchanged, skip super image rebuilding."; return true; } if (!RenameFile(tmp_vendor_dlkm_img, new_vendor_dlkm_img).ok()) { return false; } const auto new_super_img = instance_.new_super_image(); if (!Copy(instance_.super_image(), new_super_img)) { PLOG(ERROR) << "Failed to copy super image " << instance_.super_image() << " to " << new_super_img; return false; } if (!RepackSuperWithVendorDLKM(new_super_img, new_vendor_dlkm_img)) { LOG(ERROR) << "Failed to repack super image with new vendor dlkm image."; return false; } if (!RebuildVbmetaVendor(new_vendor_dlkm_img, instance_.new_vbmeta_vendor_dlkm_image())) { LOG(ERROR) << "Failed to rebuild vbmeta vendor."; return false; } SetCommandLineOptionWithMode("super_image", new_super_img.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode( "vbmeta_vendor_dlkm_image", instance_.new_vbmeta_vendor_dlkm_image().c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); return true; } bool Setup() override { if (!FileHasContent(instance_.boot_image())) { LOG(ERROR) << "File not found: " << instance_.boot_image(); return false; } // The init_boot partition is be optional for testing boot.img // with the ramdisk inside. if (!FileHasContent(instance_.init_boot_image())) { LOG(WARNING) << "File not found: " << instance_.init_boot_image(); } if (!FileHasContent(instance_.vendor_boot_image())) { LOG(ERROR) << "File not found: " << instance_.vendor_boot_image(); return false; } // Repacking a boot.img doesn't work with Gem5 because the user must always // specify a vmlinux instead of an arm64 Image, and that file can be too // large to be repacked. Skip repack of boot.img on Gem5, as we need to be // able to extract the ramdisk.img in a later stage and so this step must // not fail (..and the repacked kernel wouldn't be used anyway). if (instance_.kernel_path().size() && config_.vm_manager() != Gem5Manager::name()) { const std::string new_boot_image_path = instance_.new_boot_image(); bool success = RepackBootImage(instance_.kernel_path(), instance_.boot_image(), new_boot_image_path, instance_.instance_dir()); if (!success) { LOG(ERROR) << "Failed to regenerate the boot image with the new kernel"; return false; } SetCommandLineOptionWithMode("boot_image", new_boot_image_path.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); } if (instance_.kernel_path().size() || instance_.initramfs_path().size()) { const std::string new_vendor_boot_image_path = instance_.new_vendor_boot_image(); // Repack the vendor boot images if kernels and/or ramdisks are passed in. if (instance_.initramfs_path().size()) { const auto superimg_build_dir = instance_.instance_dir() + "/superimg"; const auto ramdisk_repacked = instance_.instance_dir() + "/ramdisk_repacked"; if (!Copy(instance_.initramfs_path(), ramdisk_repacked)) { LOG(ERROR) << "Failed to copy " << instance_.initramfs_path() << " to " << ramdisk_repacked; return false; } const auto vendor_dlkm_build_dir = superimg_build_dir + "/vendor_dlkm"; if (!RepackVendorDLKM(superimg_build_dir, vendor_dlkm_build_dir, ramdisk_repacked)) { return false; } bool success = RepackVendorBootImage( ramdisk_repacked, instance_.vendor_boot_image(), new_vendor_boot_image_path, config_.assembly_dir(), instance_.bootconfig_supported()); if (!success) { LOG(ERROR) << "Failed to regenerate the vendor boot image with the " "new ramdisk"; } else { // This control flow implies a kernel with all configs built in. // If it's just the kernel, repack the vendor boot image without a // ramdisk. bool success = RepackVendorBootImageWithEmptyRamdisk( instance_.vendor_boot_image(), new_vendor_boot_image_path, config_.assembly_dir(), instance_.bootconfig_supported()); if (!success) { LOG(ERROR) << "Failed to regenerate the vendor boot image without " "a ramdisk"; return false; } } SetCommandLineOptionWithMode( "vendor_boot_image", new_vendor_boot_image_path.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); } } return true; } private: const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; }; class Gem5ImageUnpacker : public SetupFeature { public: INJECT(Gem5ImageUnpacker(const CuttlefishConfig& config, KernelRamdiskRepacker& bir)) : config_(config), bir_(bir) {} // SetupFeature std::string Name() const override { return "Gem5ImageUnpacker"; } std::unordered_set Dependencies() const override { return { static_cast(&bir_), }; } bool Enabled() const override { // Everything has a bootloader except gem5, so only run this for gem5 return config_.vm_manager() == Gem5Manager::name(); } protected: Result ResultSetup() override { const CuttlefishConfig::InstanceSpecific& instance_ = config_.ForDefaultInstance(); /* Unpack the original or repacked boot and vendor boot ramdisks, so that * we have access to the baked bootconfig and raw compressed ramdisks. * This allows us to emulate what a bootloader would normally do, which * Gem5 can't support itself. This code also copies the kernel again * (because Gem5 only supports raw vmlinux) and handles the bootloader * binaries specially. This code is just part of the solution; it only * does the parts which are instance agnostic. */ CF_EXPECT(FileHasContent(instance_.boot_image()), instance_.boot_image()); const std::string unpack_dir = config_.assembly_dir(); // The init_boot partition is be optional for testing boot.img // with the ramdisk inside. if (!FileHasContent(instance_.init_boot_image())) { LOG(WARNING) << "File not found: " << instance_.init_boot_image(); } else { CF_EXPECT(UnpackBootImage(instance_.init_boot_image(), unpack_dir), "Failed to extract the init boot image"); } CF_EXPECT(FileHasContent(instance_.vendor_boot_image()), instance_.vendor_boot_image()); CF_EXPECT(UnpackVendorBootImageIfNotUnpacked(instance_.vendor_boot_image(), unpack_dir), "Failed to extract the vendor boot image"); // Assume the user specified a kernel manually which is a vmlinux CF_EXPECT(cuttlefish::Copy(instance_.kernel_path(), unpack_dir + "/kernel")); // Gem5 needs the bootloader binary to be a specific directory structure // to find it. Create a 'binaries' directory and copy it into there const std::string binaries_dir = unpack_dir + "/binaries"; CF_EXPECT(mkdir(binaries_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0 || errno == EEXIST, "\"" << binaries_dir << "\": " << strerror(errno)); CF_EXPECT(cuttlefish::Copy(instance_.bootloader(), binaries_dir + "/" + cpp_basename(instance_.bootloader()))); // Gem5 also needs the ARM version of the bootloader, even though it // doesn't use it. It'll even open it to check it's a valid ELF file. // Work around this by copying such a named file from the same directory CF_EXPECT(cuttlefish::Copy( cpp_dirname(instance_.bootloader()) + "/boot.arm", binaries_dir + "/boot.arm")); return {}; } private: const CuttlefishConfig& config_; KernelRamdiskRepacker& bir_; }; class GeneratePersistentBootconfig : public SetupFeature { public: INJECT(GeneratePersistentBootconfig( const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance)) : config_(config), instance_(instance) {} // SetupFeature std::string Name() const override { return "GeneratePersistentBootconfig"; } bool Enabled() const override { return (!instance_.protected_vm()); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { // Cuttlefish for the time being won't be able to support OTA from a // non-bootconfig kernel to a bootconfig-kernel (or vice versa) IF the // device is stopped (via stop_cvd). This is rarely an issue since OTA // testing run on cuttlefish is done within one launch cycle of the device. // If this ever becomes an issue, this code will have to be rewritten. if(!instance_.bootconfig_supported()) { return {}; } const auto bootconfig_path = instance_.persistent_bootconfig_path(); if (!FileExists(bootconfig_path)) { CF_EXPECT(CreateBlankImage(bootconfig_path, 1 /* mb */, "none"), "Failed to create image at " << bootconfig_path); } auto bootconfig_fd = SharedFD::Open(bootconfig_path, O_RDWR); CF_EXPECT(bootconfig_fd->IsOpen(), "Unable to open bootconfig file: " << bootconfig_fd->StrError()); const auto bootconfig_args = CF_EXPECT(BootconfigArgsFromConfig(config_, instance_)); const auto bootconfig = CF_EXPECT(BootconfigArgsString(bootconfig_args, "\n")) + "\n"; LOG(DEBUG) << "bootconfig size is " << bootconfig.size(); ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig); CF_EXPECT(WriteAll(bootconfig_fd, bootconfig) == bootconfig.size(), "Failed to write bootconfig to \"" << bootconfig_path << "\""); LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are " << ReadFile(bootconfig_path); CF_EXPECT(bootconfig_fd->Truncate(bootconfig.size()) == 0, "`truncate --size=" << bootconfig.size() << " bytes " << bootconfig_path << "` failed:" << bootconfig_fd->StrError()); if (config_.vm_manager() == Gem5Manager::name()) { const off_t bootconfig_size_bytes_gem5 = AlignToPowerOf2(bytesWritten, PARTITION_SIZE_SHIFT); CF_EXPECT(bootconfig_fd->Truncate(bootconfig_size_bytes_gem5) == 0); bootconfig_fd->Close(); } else { bootconfig_fd->Close(); const off_t bootconfig_size_bytes = AlignToPowerOf2( MAX_AVB_METADATA_SIZE + bootconfig.size(), PARTITION_SIZE_SHIFT); auto avbtool_path = HostBinaryPath("avbtool"); Command bootconfig_hash_footer_cmd(avbtool_path); bootconfig_hash_footer_cmd.AddParameter("add_hash_footer"); bootconfig_hash_footer_cmd.AddParameter("--image"); bootconfig_hash_footer_cmd.AddParameter(bootconfig_path); bootconfig_hash_footer_cmd.AddParameter("--partition_size"); bootconfig_hash_footer_cmd.AddParameter(bootconfig_size_bytes); bootconfig_hash_footer_cmd.AddParameter("--partition_name"); bootconfig_hash_footer_cmd.AddParameter("bootconfig"); bootconfig_hash_footer_cmd.AddParameter("--key"); bootconfig_hash_footer_cmd.AddParameter( DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem")); bootconfig_hash_footer_cmd.AddParameter("--algorithm"); bootconfig_hash_footer_cmd.AddParameter("SHA256_RSA4096"); int success = bootconfig_hash_footer_cmd.Start().Wait(); CF_EXPECT( success == 0, "Unable to run append hash footer. Exited with status " << success); } return {}; } const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; }; class GeneratePersistentVbmeta : public SetupFeature { public: INJECT(GeneratePersistentVbmeta( const CuttlefishConfig::InstanceSpecific& instance, InitBootloaderEnvPartition& bootloader_env, GeneratePersistentBootconfig& bootconfig)) : instance_(instance), bootloader_env_(bootloader_env), bootconfig_(bootconfig) {} // SetupFeature std::string Name() const override { return "GeneratePersistentVbmeta"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return { static_cast(&bootloader_env_), static_cast(&bootconfig_), }; } bool Setup() override { if (!instance_.protected_vm()) { if (!PrepareVBMetaImage(instance_.vbmeta_path(), instance_.bootconfig_supported())) { return false; } } if (instance_.ap_boot_flow() == APBootFlow::Grub) { if (!PrepareVBMetaImage(instance_.ap_vbmeta_path(), false)) { return false; } } return true; } bool PrepareVBMetaImage(const std::string& path, bool has_boot_config) { auto avbtool_path = HostBinaryPath("avbtool"); Command vbmeta_cmd(avbtool_path); vbmeta_cmd.AddParameter("make_vbmeta_image"); vbmeta_cmd.AddParameter("--output"); vbmeta_cmd.AddParameter(path); vbmeta_cmd.AddParameter("--algorithm"); vbmeta_cmd.AddParameter("SHA256_RSA4096"); vbmeta_cmd.AddParameter("--key"); vbmeta_cmd.AddParameter( DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem")); vbmeta_cmd.AddParameter("--chain_partition"); vbmeta_cmd.AddParameter("uboot_env:1:" + DefaultHostArtifactsPath("etc/cvd.avbpubkey")); if (has_boot_config) { vbmeta_cmd.AddParameter("--chain_partition"); vbmeta_cmd.AddParameter("bootconfig:2:" + DefaultHostArtifactsPath("etc/cvd.avbpubkey")); } bool success = vbmeta_cmd.Start().Wait(); if (success != 0) { LOG(ERROR) << "Unable to create persistent vbmeta. Exited with status " << success; return false; } const auto vbmeta_size = FileSize(path); if (vbmeta_size > VBMETA_MAX_SIZE) { LOG(ERROR) << "Generated vbmeta - " << path << " is larger than the expected " << VBMETA_MAX_SIZE << ". Stopping."; return false; } if (vbmeta_size != VBMETA_MAX_SIZE) { auto fd = SharedFD::Open(path, O_RDWR); if (!fd->IsOpen() || fd->Truncate(VBMETA_MAX_SIZE) != 0) { LOG(ERROR) << "`truncate --size=" << VBMETA_MAX_SIZE << " " << path << "` failed: " << fd->StrError(); return false; } } return true; } const CuttlefishConfig::InstanceSpecific& instance_; InitBootloaderEnvPartition& bootloader_env_; GeneratePersistentBootconfig& bootconfig_; }; class InitializeMetadataImage : public SetupFeature { public: INJECT(InitializeMetadataImage( const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // SetupFeature std::string Name() const override { return "InitializeMetadataImage"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { if (FileExists(instance_.metadata_image()) && FileSize(instance_.metadata_image()) == instance_.blank_metadata_image_mb() << 20) { return {}; } CF_EXPECT(CreateBlankImage(instance_.new_metadata_image(), instance_.blank_metadata_image_mb(), "none"), "Failed to create \"" << instance_.new_metadata_image() << "\" with size " << instance_.blank_metadata_image_mb()); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; class InitializeAccessKregistryImage : public SetupFeature { public: INJECT(InitializeAccessKregistryImage( const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // SetupFeature std::string Name() const override { return "InitializeAccessKregistryImage"; } bool Enabled() const override { return !instance_.protected_vm(); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { auto access_kregistry = instance_.access_kregistry_path(); if (FileExists(access_kregistry)) { return {}; } CF_EXPECT(CreateBlankImage(access_kregistry, 2 /* mb */, "none"), "Failed to create \"" << access_kregistry << "\""); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; class InitializeHwcomposerPmemImage : public SetupFeature { public: INJECT(InitializeHwcomposerPmemImage( const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // SetupFeature std::string Name() const override { return "InitializeHwcomposerPmemImage"; } bool Enabled() const override { return instance_.hwcomposer() != kHwComposerNone && !instance_.protected_vm(); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { if (FileExists(instance_.hwcomposer_pmem_path())) { return {}; } CF_EXPECT( CreateBlankImage(instance_.hwcomposer_pmem_path(), 2 /* mb */, "none"), "Failed creating \"" << instance_.hwcomposer_pmem_path() << "\""); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; class InitializePstore : public SetupFeature { public: INJECT(InitializePstore(const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // SetupFeature std::string Name() const override { return "InitializePstore"; } bool Enabled() const override { return !instance_.protected_vm(); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { if (FileExists(instance_.pstore_path())) { return {}; } CF_EXPECT(CreateBlankImage(instance_.pstore_path(), 2 /* mb */, "none"), "Failed to create \"" << instance_.pstore_path() << "\""); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; class InitializeSdCard : public SetupFeature { public: INJECT(InitializeSdCard(const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // SetupFeature std::string Name() const override { return "InitializeSdCard"; } bool Enabled() const override { return instance_.use_sdcard() && !instance_.protected_vm(); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { if (FileExists(instance_.sdcard_path())) { return {}; } CF_EXPECT(CreateBlankImage(instance_.sdcard_path(), instance_.blank_sdcard_image_mb(), "sdcard"), "Failed to create \"" << instance_.sdcard_path() << "\""); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; class InitializeFactoryResetProtected : public SetupFeature { public: INJECT(InitializeFactoryResetProtected( const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // SetupFeature std::string Name() const override { return "InitializeSdCard"; } bool Enabled() const override { return !instance_.protected_vm(); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { auto frp = instance_.factory_reset_protected_path(); if (FileExists(frp)) { return {}; } CF_EXPECT(CreateBlankImage(frp, 1 /* mb */, "none"), "Failed to create \"" << frp << "\""); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; class InitializeInstanceCompositeDisk : public SetupFeature { public: INJECT(InitializeInstanceCompositeDisk( const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance, InitializeFactoryResetProtected& frp, GeneratePersistentVbmeta& vbmeta)) : config_(config), instance_(instance), frp_(frp), vbmeta_(vbmeta) {} std::string Name() const override { return "InitializeInstanceCompositeDisk"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return { static_cast(&frp_), static_cast(&vbmeta_), }; } Result ResultSetup() override { const auto ipath = [this](const std::string& path) -> std::string { return instance_.PerInstancePath(path.c_str()); }; auto persistent_disk_builder = DiskBuilder() .Partitions(persistent_composite_disk_config(instance_)) .VmManager(config_.vm_manager()) .CrosvmPath(instance_.crosvm_binary()) .ConfigPath(ipath("persistent_composite_disk_config.txt")) .HeaderPath(ipath("persistent_composite_gpt_header.img")) .FooterPath(ipath("persistent_composite_gpt_footer.img")) .CompositeDiskPath(instance_.persistent_composite_disk_path()) .ResumeIfPossible(FLAGS_resume); CF_EXPECT(persistent_disk_builder.BuildCompositeDiskIfNecessary()); if (instance_.ap_boot_flow() == APBootFlow::Grub) { auto persistent_ap_disk_builder = DiskBuilder() .Partitions(persistent_ap_composite_disk_config(instance_)) .VmManager(config_.vm_manager()) .CrosvmPath(instance_.crosvm_binary()) .ConfigPath(ipath("ap_persistent_composite_disk_config.txt")) .HeaderPath(ipath("ap_persistent_composite_gpt_header.img")) .FooterPath(ipath("ap_persistent_composite_gpt_footer.img")) .CompositeDiskPath(instance_.persistent_ap_composite_disk_path()) .ResumeIfPossible(FLAGS_resume); CF_EXPECT(persistent_ap_disk_builder.BuildCompositeDiskIfNecessary()); } return {}; } const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; InitializeFactoryResetProtected& frp_; GeneratePersistentVbmeta& vbmeta_; }; class VbmetaEnforceMinimumSize : public SetupFeature { public: INJECT(VbmetaEnforceMinimumSize( const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} std::string Name() const override { return "VbmetaEnforceMinimumSize"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { // libavb expects to be able to read the maximum vbmeta size, so we must // provide a partition which matches this or the read will fail for (const auto& vbmeta_image : {instance_.vbmeta_image(), instance_.vbmeta_system_image(), instance_.vbmeta_vendor_dlkm_image()}) { // In some configurations of cuttlefish, the vendor dlkm vbmeta image does // not exist if (FileExists(vbmeta_image) && FileSize(vbmeta_image) != VBMETA_MAX_SIZE) { auto fd = SharedFD::Open(vbmeta_image, O_RDWR); CF_EXPECT(fd->IsOpen(), "Could not open \"" << vbmeta_image << "\": " << fd->StrError()); CF_EXPECT(fd->Truncate(VBMETA_MAX_SIZE) == 0, "`truncate --size=" << VBMETA_MAX_SIZE << " " << vbmeta_image << "` failed: " << fd->StrError()); } } return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; class BootloaderPresentCheck : public SetupFeature { public: INJECT(BootloaderPresentCheck( const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} std::string Name() const override { return "BootloaderPresentCheck"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { CF_EXPECT(FileHasContent(instance_.bootloader()), "File not found: " << instance_.bootloader()); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; }; static fruit::Component<> DiskChangesComponent( const FetcherConfig* fetcher, const CuttlefishConfig* config, const CuttlefishConfig::InstanceSpecific* instance) { return fruit::createComponent() .bindInstance(*fetcher) .bindInstance(*config) .bindInstance(*instance) .addMultibinding() .addMultibinding() .addMultibinding() .addMultibinding() .addMultibinding() .install(InitializeMiscImageComponent) // Create esp if necessary .install(InitializeEspImageComponent) .install(SuperImageRebuilderComponent); } static fruit::Component<> DiskChangesPerInstanceComponent( const FetcherConfig* fetcher, const CuttlefishConfig* config, const CuttlefishConfig::InstanceSpecific* instance) { return fruit::createComponent() .bindInstance(*fetcher) .bindInstance(*config) .bindInstance(*instance) .addMultibinding() .addMultibinding() .addMultibinding() .addMultibinding() .addMultibinding() .addMultibinding() .addMultibinding() .addMultibinding() .install(InitializeDataImageComponent) .install(InitBootloaderEnvPartitionComponent); } Result DiskImageFlagsVectorization(CuttlefishConfig& config, const FetcherConfig& fetcher_config) { std::vector boot_image = android::base::Split(FLAGS_boot_image, ","); std::vector init_boot_image = android::base::Split(FLAGS_init_boot_image, ","); std::vector data_image = android::base::Split(FLAGS_data_image, ","); std::vector super_image = android::base::Split(FLAGS_super_image, ","); std::vector misc_image = android::base::Split(FLAGS_misc_image, ","); std::vector misc_info = android::base::Split(FLAGS_misc_info_txt, ","); std::vector metadata_image = android::base::Split(FLAGS_metadata_image, ","); std::vector vendor_boot_image = android::base::Split(FLAGS_vendor_boot_image, ","); std::vector vbmeta_image = android::base::Split(FLAGS_vbmeta_image, ","); std::vector vbmeta_system_image = android::base::Split(FLAGS_vbmeta_system_image, ","); auto vbmeta_vendor_dlkm_image = android::base::Split(FLAGS_vbmeta_vendor_dlkm_image, ","); std::vector linux_kernel_path = android::base::Split(FLAGS_linux_kernel_path, ","); std::vector linux_initramfs_path = android::base::Split(FLAGS_linux_initramfs_path, ","); std::vector linux_root_image = android::base::Split(FLAGS_linux_root_image, ","); std::vector fuchsia_zedboot_path = android::base::Split(FLAGS_fuchsia_zedboot_path, ","); std::vector fuchsia_multiboot_bin_path = android::base::Split(FLAGS_fuchsia_multiboot_bin_path, ","); std::vector fuchsia_root_image = android::base::Split(FLAGS_fuchsia_root_image, ","); std::vector custom_partition_path = android::base::Split(FLAGS_custom_partition_path, ","); std::vector bootloader = android::base::Split(FLAGS_bootloader, ","); std::vector initramfs_path = android::base::Split(FLAGS_initramfs_path, ","); std::vector kernel_path = android::base::Split(FLAGS_kernel_path, ","); std::vector blank_metadata_image_mb = android::base::Split(FLAGS_blank_metadata_image_mb, ","); std::vector blank_sdcard_image_mb = android::base::Split(FLAGS_blank_sdcard_image_mb, ","); std::string cur_kernel_path; std::string cur_initramfs_path; std::string cur_boot_image; std::string cur_vendor_boot_image; std::string cur_super_image; std::string cur_metadata_image; std::string cur_misc_image; int cur_blank_metadata_image_mb{}; int value{}; int instance_index = 0; auto instance_nums = CF_EXPECT(InstanceNumsCalculator().FromGlobalGflags().Calculate()); for (const auto& num : instance_nums) { auto instance = config.ForInstance(num); if (instance_index >= misc_image.size()) { // legacy variable. Vectorize by copy [0] to all instances cur_misc_image = misc_image[0]; } else { cur_misc_image = misc_image[instance_index]; } instance.set_misc_image(cur_misc_image); if (instance_index >= misc_info.size()) { instance.set_misc_info_txt(misc_info[0]); } else { instance.set_misc_info_txt(misc_info[instance_index]); } if (instance_index >= boot_image.size()) { cur_boot_image = boot_image[0]; } else { cur_boot_image = boot_image[instance_index]; } instance.set_boot_image(cur_boot_image); instance.set_new_boot_image(cur_boot_image); if (instance_index >= init_boot_image.size()) { instance.set_init_boot_image(init_boot_image[0]); } else { instance.set_init_boot_image(init_boot_image[instance_index]); } if (instance_index >= vendor_boot_image.size()) { cur_vendor_boot_image = vendor_boot_image[0]; } else { cur_vendor_boot_image = vendor_boot_image[instance_index]; } instance.set_vendor_boot_image(cur_vendor_boot_image); instance.set_new_vendor_boot_image(cur_vendor_boot_image); if (instance_index >= vbmeta_image.size()) { instance.set_vbmeta_image(vbmeta_image[0]); } else { instance.set_vbmeta_image(vbmeta_image[instance_index]); } if (instance_index >= vbmeta_system_image.size()) { instance.set_vbmeta_system_image(vbmeta_system_image[0]); } else { instance.set_vbmeta_system_image(vbmeta_system_image[instance_index]); } if (instance_index >= vbmeta_system_image.size()) { instance.set_vbmeta_vendor_dlkm_image(vbmeta_vendor_dlkm_image[0]); } else { instance.set_vbmeta_vendor_dlkm_image( vbmeta_vendor_dlkm_image[instance_index]); } if (instance_index >= super_image.size()) { cur_super_image = super_image[0]; } else { cur_super_image = super_image[instance_index]; } instance.set_super_image(cur_super_image); if (instance_index >= data_image.size()) { instance.set_data_image(data_image[0]); } else { instance.set_data_image(data_image[instance_index]); } if (instance_index >= metadata_image.size()) { cur_metadata_image = metadata_image[0]; } else { cur_metadata_image = metadata_image[instance_index]; } instance.set_metadata_image(cur_metadata_image); if (instance_index >= linux_kernel_path.size()) { instance.set_linux_kernel_path(linux_kernel_path[0]); } else { instance.set_linux_kernel_path(linux_kernel_path[instance_index]); } if (instance_index >= linux_initramfs_path.size()) { instance.set_linux_initramfs_path(linux_initramfs_path[0]); } else { instance.set_linux_initramfs_path(linux_initramfs_path[instance_index]); } if (instance_index >= linux_root_image.size()) { instance.set_linux_root_image(linux_root_image[0]); } else { instance.set_linux_root_image(linux_root_image[instance_index]); } if (instance_index >= fuchsia_zedboot_path.size()) { instance.set_fuchsia_zedboot_path(fuchsia_zedboot_path[0]); } else { instance.set_fuchsia_zedboot_path(fuchsia_zedboot_path[instance_index]); } if (instance_index >= fuchsia_multiboot_bin_path.size()) { instance.set_fuchsia_multiboot_bin_path(fuchsia_multiboot_bin_path[0]); } else { instance.set_fuchsia_multiboot_bin_path(fuchsia_multiboot_bin_path[instance_index]); } if (instance_index >= fuchsia_root_image.size()) { instance.set_fuchsia_root_image(fuchsia_root_image[0]); } else { instance.set_fuchsia_root_image(fuchsia_root_image[instance_index]); } if (instance_index >= custom_partition_path.size()) { instance.set_custom_partition_path(custom_partition_path[0]); } else { instance.set_custom_partition_path(custom_partition_path[instance_index]); } if (instance_index >= bootloader.size()) { instance.set_bootloader(bootloader[0]); } else { instance.set_bootloader(bootloader[instance_index]); } if (instance_index >= kernel_path.size()) { cur_kernel_path = kernel_path[0]; } else { cur_kernel_path = kernel_path[instance_index]; } instance.set_kernel_path(cur_kernel_path); if (instance_index >= initramfs_path.size()) { cur_initramfs_path = initramfs_path[0]; } else { cur_initramfs_path = initramfs_path[instance_index]; } instance.set_initramfs_path(cur_initramfs_path); if (instance_index >= blank_metadata_image_mb.size()) { CHECK(android::base::ParseInt(blank_metadata_image_mb[0], &value)) << "Invalid 'blank_metadata_image_mb' " << blank_metadata_image_mb[0]; } else { CHECK(android::base::ParseInt(blank_metadata_image_mb[instance_index], &value)) << "Invalid 'blank_metadata_image_mb' " << blank_metadata_image_mb[instance_index]; } instance.set_blank_metadata_image_mb(value); cur_blank_metadata_image_mb = value; if (instance_index >= blank_sdcard_image_mb.size()) { CHECK(android::base::ParseInt(blank_sdcard_image_mb[0], &value)) << "Invalid 'blank_sdcard_image_mb' " << blank_sdcard_image_mb[0]; } else { CHECK(android::base::ParseInt(blank_sdcard_image_mb[instance_index], &value)) << "Invalid 'blank_sdcard_image_mb' " << blank_sdcard_image_mb[instance_index]; } instance.set_blank_sdcard_image_mb(value); // Repacking a boot.img changes boot_image and vendor_boot_image paths const CuttlefishConfig& const_config = const_cast(config); const CuttlefishConfig::InstanceSpecific const_instance = const_config.ForInstance(num); if (cur_kernel_path.size() && config.vm_manager() != Gem5Manager::name()) { const std::string new_boot_image_path = const_instance.PerInstancePath("boot_repacked.img"); // change the new flag value to corresponding instance instance.set_new_boot_image(new_boot_image_path.c_str()); } if (cur_kernel_path.size() || cur_initramfs_path.size()) { const std::string new_vendor_boot_image_path = const_instance.PerInstancePath("vendor_boot_repacked.img"); // Repack the vendor boot images if kernels and/or ramdisks are passed in. if (cur_initramfs_path.size()) { // change the new flag value to corresponding instance instance.set_new_vendor_boot_image(new_vendor_boot_image_path.c_str()); } } // We will need to rebuild vendor_dlkm if custom ramdisk is specified, as a // result super image would need to be rebuilt as well. if (SuperImageNeedsRebuilding(fetcher_config) || cur_initramfs_path.size()) { const std::string new_super_image_path = const_instance.PerInstancePath("super.img"); instance.set_new_super_image(new_super_image_path); } if (FileExists(cur_metadata_image) && FileSize(cur_metadata_image) == cur_blank_metadata_image_mb << 20) { instance.set_new_metadata_image(cur_metadata_image); } else { const std::string new_metadata_image_path = const_instance.PerInstancePath("metadata.img"); instance.set_new_metadata_image(new_metadata_image_path); } instance.set_new_vbmeta_vendor_dlkm_image( const_instance.PerInstancePath("vbmeta_vendor_dlkm_repacked.img")); if (FileHasContent(cur_misc_image)) { instance.set_new_misc_image(cur_misc_image); } else { const std::string new_misc_image_path = const_instance.PerInstancePath("misc.img"); instance.set_new_misc_image(new_misc_image_path); } instance_index++; } return {}; } Result CreateDynamicDiskFiles(const FetcherConfig& fetcher_config, const CuttlefishConfig& config) { for (const auto& instance : config.Instances()) { // TODO(schuffelen): Unify this with the other injector created in // assemble_cvd.cpp fruit::Injector<> injector(DiskChangesComponent, &fetcher_config, &config, &instance); for (auto& late_injected : injector.getMultibindings()) { CF_EXPECT(late_injected->LateInject(injector)); } const auto& features = injector.getMultibindings(); CF_EXPECT(SetupFeature::RunSetup(features)); fruit::Injector<> instance_injector(DiskChangesPerInstanceComponent, &fetcher_config, &config, &instance); for (auto& late_injected : instance_injector.getMultibindings()) { CF_EXPECT(late_injected->LateInject(instance_injector)); } const auto& instance_features = instance_injector.getMultibindings(); CF_EXPECT(SetupFeature::RunSetup(instance_features), "instance = \"" << instance.instance_name() << "\""); // Check if filling in the sparse image would run out of disk space. auto existing_sizes = SparseFileSizes(instance.data_image()); CF_EXPECT(existing_sizes.sparse_size > 0 || existing_sizes.disk_size > 0, "Unable to determine size of \"" << instance.data_image() << "\". Does this file exist?"); auto available_space = AvailableSpaceAtPath(instance.data_image()); if (available_space < existing_sizes.sparse_size - existing_sizes.disk_size) { // TODO(schuffelen): Duplicate this check in run_cvd when it can run on a // separate machine return CF_ERR("Not enough space remaining in fs containing \"" << instance.data_image() << "\", wanted " << (existing_sizes.sparse_size - existing_sizes.disk_size) << ", got " << available_space); } else { LOG(DEBUG) << "Available space: " << available_space; LOG(DEBUG) << "Sparse size of \"" << instance.data_image() << "\": " << existing_sizes.sparse_size; LOG(DEBUG) << "Disk size of \"" << instance.data_image() << "\": " << existing_sizes.disk_size; } auto os_disk_builder = OsCompositeDiskBuilder(config, instance); const auto os_built_composite = CF_EXPECT(os_disk_builder.BuildCompositeDiskIfNecessary()); auto ap_disk_builder = ApCompositeDiskBuilder(config, instance); if (instance.ap_boot_flow() != APBootFlow::None) { CF_EXPECT(ap_disk_builder.BuildCompositeDiskIfNecessary()); } if (os_built_composite) { if (FileExists(instance.access_kregistry_path())) { CF_EXPECT(CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none"), "Failed for \"" << instance.access_kregistry_path() << "\""); } if (FileExists(instance.hwcomposer_pmem_path())) { CF_EXPECT(CreateBlankImage(instance.hwcomposer_pmem_path(), 2 /* mb */, "none"), "Failed for \"" << instance.hwcomposer_pmem_path() << "\""); } if (FileExists(instance.pstore_path())) { CF_EXPECT(CreateBlankImage(instance.pstore_path(), 2 /* mb */, "none"), "Failed for\"" << instance.pstore_path() << "\""); } } if (!instance.protected_vm()) { os_disk_builder.OverlayPath(instance.PerInstancePath("overlay.img")); CF_EXPECT(os_disk_builder.BuildOverlayIfNecessary()); if (instance.ap_boot_flow() != APBootFlow::None) { ap_disk_builder.OverlayPath(instance.PerInstancePath("ap_overlay.img")); CF_EXPECT(ap_disk_builder.BuildOverlayIfNecessary()); } } } for (auto instance : config.Instances()) { // Check that the files exist for (const auto& file : instance.virtual_disk_paths()) { if (!file.empty()) { CF_EXPECT(FileHasContent(file), "File not found: \"" << file << "\""); } } // Gem5 Simulate per-instance what the bootloader would usually do // Since on other devices this runs every time, just do it here every time if (config.vm_manager() == Gem5Manager::name()) { RepackGem5BootImage(instance.PerInstancePath("initrd.img"), instance.persistent_bootconfig_path(), config.assembly_dir(), instance.initramfs_path()); } } return {}; } } // namespace cuttlefish