• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "host/commands/assemble_cvd/disk_flags.h"
18 
19 #include <android-base/logging.h>
20 #include <android-base/strings.h>
21 #include <fruit/fruit.h>
22 #include <gflags/gflags.h>
23 #include <sys/statvfs.h>
24 
25 #include <fstream>
26 
27 #include "common/libs/fs/shared_buf.h"
28 #include "common/libs/utils/environment.h"
29 #include "common/libs/utils/files.h"
30 #include "common/libs/utils/size_utils.h"
31 #include "common/libs/utils/subprocess.h"
32 #include "host/commands/assemble_cvd/boot_config.h"
33 #include "host/commands/assemble_cvd/boot_image_utils.h"
34 #include "host/commands/assemble_cvd/disk_builder.h"
35 #include "host/commands/assemble_cvd/super_image_mixer.h"
36 #include "host/libs/config/bootconfig_args.h"
37 #include "host/libs/config/cuttlefish_config.h"
38 #include "host/libs/config/data_image.h"
39 #include "host/libs/vm_manager/crosvm_manager.h"
40 #include "host/libs/vm_manager/gem5_manager.h"
41 
42 // Taken from external/avb/libavb/avb_slot_verify.c; this define is not in the headers
43 #define VBMETA_MAX_SIZE 65536ul
44 // Taken from external/avb/avbtool.py; this define is not in the headers
45 #define MAX_AVB_METADATA_SIZE 69632ul
46 
47 DECLARE_string(system_image_dir);
48 
49 DEFINE_string(boot_image, "",
50               "Location of cuttlefish boot image. If empty it is assumed to be "
51               "boot.img in the directory specified by -system_image_dir.");
52 DEFINE_string(
53     init_boot_image, "",
54     "Location of cuttlefish init boot image. If empty it is assumed to "
55     "be init_boot.img in the directory specified by -system_image_dir.");
56 DEFINE_string(data_image, "", "Location of the data partition image.");
57 DEFINE_string(super_image, "", "Location of the super partition image.");
58 DEFINE_string(misc_image, "",
59               "Location of the misc partition image. If the image does not "
60               "exist, a blank new misc partition image is created.");
61 DEFINE_string(metadata_image, "", "Location of the metadata partition image "
62               "to be generated.");
63 DEFINE_string(vendor_boot_image, "",
64               "Location of cuttlefish vendor boot image. If empty it is assumed to "
65               "be vendor_boot.img in the directory specified by -system_image_dir.");
66 DEFINE_string(vbmeta_image, "",
67               "Location of cuttlefish vbmeta image. If empty it is assumed to "
68               "be vbmeta.img in the directory specified by -system_image_dir.");
69 DEFINE_string(vbmeta_system_image, "",
70               "Location of cuttlefish vbmeta_system image. If empty it is assumed to "
71               "be vbmeta_system.img in the directory specified by -system_image_dir.");
72 DEFINE_string(otheros_esp_image, "",
73               "Location of cuttlefish esp image. If the image does not exist, "
74               "and --otheros_root_image is specified, an esp partition image "
75               "is created with default bootloaders.");
76 DEFINE_string(otheros_kernel_path, "",
77               "Location of cuttlefish otheros kernel.");
78 DEFINE_string(otheros_initramfs_path, "",
79               "Location of cuttlefish otheros initramfs.img.");
80 DEFINE_string(otheros_root_image, "",
81               "Location of cuttlefish otheros root filesystem image.");
82 
83 DEFINE_int32(blank_metadata_image_mb, 16,
84              "The size of the blank metadata image to generate, MB.");
85 DEFINE_int32(blank_sdcard_image_mb, 2048,
86              "If enabled, the size of the blank sdcard image to generate, MB.");
87 
88 DECLARE_string(ap_rootfs_image);
89 DECLARE_string(bootloader);
90 DECLARE_bool(use_sdcard);
91 DECLARE_string(initramfs_path);
92 DECLARE_string(kernel_path);
93 DECLARE_bool(resume);
94 DECLARE_bool(protected_vm);
95 
96 namespace cuttlefish {
97 
98 using vm_manager::Gem5Manager;
99 
ResolveInstanceFiles()100 Result<void> ResolveInstanceFiles() {
101   CF_EXPECT(!FLAGS_system_image_dir.empty(),
102             "--system_image_dir must be specified.");
103 
104   // If user did not specify location of either of these files, expect them to
105   // be placed in --system_image_dir location.
106   std::string default_boot_image = FLAGS_system_image_dir + "/boot.img";
107   SetCommandLineOptionWithMode("boot_image", default_boot_image.c_str(),
108                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
109   std::string default_init_boot_image =
110       FLAGS_system_image_dir + "/init_boot.img";
111   SetCommandLineOptionWithMode("init_boot_image",
112                                default_init_boot_image.c_str(),
113                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
114   std::string default_data_image = FLAGS_system_image_dir + "/userdata.img";
115   SetCommandLineOptionWithMode("data_image", default_data_image.c_str(),
116                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
117   std::string default_metadata_image = FLAGS_system_image_dir + "/metadata.img";
118   SetCommandLineOptionWithMode("metadata_image", default_metadata_image.c_str(),
119                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
120   std::string default_super_image = FLAGS_system_image_dir + "/super.img";
121   SetCommandLineOptionWithMode("super_image", default_super_image.c_str(),
122                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
123   std::string default_misc_image = FLAGS_system_image_dir + "/misc.img";
124   SetCommandLineOptionWithMode("misc_image", default_misc_image.c_str(),
125                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
126   std::string default_esp_image = FLAGS_system_image_dir + "/esp.img";
127   SetCommandLineOptionWithMode("otheros_esp_image", default_esp_image.c_str(),
128                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
129   std::string default_vendor_boot_image = FLAGS_system_image_dir
130                                         + "/vendor_boot.img";
131   SetCommandLineOptionWithMode("vendor_boot_image",
132                                default_vendor_boot_image.c_str(),
133                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
134   std::string default_vbmeta_image = FLAGS_system_image_dir + "/vbmeta.img";
135   SetCommandLineOptionWithMode("vbmeta_image", default_vbmeta_image.c_str(),
136                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
137   std::string default_vbmeta_system_image = FLAGS_system_image_dir
138                                           + "/vbmeta_system.img";
139   SetCommandLineOptionWithMode("vbmeta_system_image",
140                                default_vbmeta_system_image.c_str(),
141                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
142 
143   return {};
144 }
145 
GetOsCompositeDiskConfig()146 std::vector<ImagePartition> GetOsCompositeDiskConfig() {
147   std::vector<ImagePartition> partitions;
148   partitions.push_back(ImagePartition{
149       .label = "misc",
150       .image_file_path = AbsolutePath(FLAGS_misc_image),
151       .read_only = true,
152   });
153   partitions.push_back(ImagePartition{
154       .label = "boot_a",
155       .image_file_path = AbsolutePath(FLAGS_boot_image),
156       .read_only = true,
157   });
158   partitions.push_back(ImagePartition{
159       .label = "boot_b",
160       .image_file_path = AbsolutePath(FLAGS_boot_image),
161       .read_only = true,
162   });
163   partitions.push_back(ImagePartition{
164       .label = "init_boot_a",
165       .image_file_path = AbsolutePath(FLAGS_init_boot_image),
166       .read_only = true,
167   });
168   partitions.push_back(ImagePartition{
169       .label = "init_boot_b",
170       .image_file_path = AbsolutePath(FLAGS_init_boot_image),
171       .read_only = true,
172   });
173   partitions.push_back(ImagePartition{
174       .label = "vendor_boot_a",
175       .image_file_path = AbsolutePath(FLAGS_vendor_boot_image),
176       .read_only = true,
177   });
178   partitions.push_back(ImagePartition{
179       .label = "vendor_boot_b",
180       .image_file_path = AbsolutePath(FLAGS_vendor_boot_image),
181       .read_only = true,
182   });
183   partitions.push_back(ImagePartition{
184       .label = "vbmeta_a",
185       .image_file_path = AbsolutePath(FLAGS_vbmeta_image),
186       .read_only = true,
187   });
188   partitions.push_back(ImagePartition{
189       .label = "vbmeta_b",
190       .image_file_path = AbsolutePath(FLAGS_vbmeta_image),
191       .read_only = true,
192   });
193   partitions.push_back(ImagePartition{
194       .label = "vbmeta_system_a",
195       .image_file_path = AbsolutePath(FLAGS_vbmeta_system_image),
196       .read_only = true,
197   });
198   partitions.push_back(ImagePartition{
199       .label = "vbmeta_system_b",
200       .image_file_path = AbsolutePath(FLAGS_vbmeta_system_image),
201       .read_only = true,
202   });
203   partitions.push_back(ImagePartition{
204       .label = "super",
205       .image_file_path = AbsolutePath(FLAGS_super_image),
206       .read_only = true,
207   });
208   partitions.push_back(ImagePartition{
209       .label = "userdata",
210       .image_file_path = AbsolutePath(FLAGS_data_image),
211       .read_only = true,
212   });
213   partitions.push_back(ImagePartition{
214       .label = "metadata",
215       .image_file_path = AbsolutePath(FLAGS_metadata_image),
216       .read_only = true,
217   });
218   if (!FLAGS_otheros_root_image.empty()) {
219     partitions.push_back(ImagePartition{
220         .label = "otheros_esp",
221         .image_file_path = AbsolutePath(FLAGS_otheros_esp_image),
222         .type = kEfiSystemPartition,
223         .read_only = true,
224     });
225     partitions.push_back(ImagePartition{
226         .label = "otheros_root",
227         .image_file_path = AbsolutePath(FLAGS_otheros_root_image),
228         .read_only = true,
229     });
230   }
231   if (!FLAGS_ap_rootfs_image.empty()) {
232     partitions.push_back(ImagePartition{
233         .label = "ap_rootfs",
234         .image_file_path = AbsolutePath(FLAGS_ap_rootfs_image),
235         .read_only = true,
236     });
237   }
238   return partitions;
239 }
240 
OsCompositeDiskBuilder(const CuttlefishConfig & config)241 DiskBuilder OsCompositeDiskBuilder(const CuttlefishConfig& config) {
242   return DiskBuilder()
243       .Partitions(GetOsCompositeDiskConfig())
244       .VmManager(config.vm_manager())
245       .CrosvmPath(config.crosvm_binary())
246       .ConfigPath(config.AssemblyPath("os_composite_disk_config.txt"))
247       .HeaderPath(config.AssemblyPath("os_composite_gpt_header.img"))
248       .FooterPath(config.AssemblyPath("os_composite_gpt_footer.img"))
249       .CompositeDiskPath(config.os_composite_disk_path())
250       .ResumeIfPossible(FLAGS_resume);
251 }
252 
persistent_composite_disk_config(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance)253 std::vector<ImagePartition> persistent_composite_disk_config(
254     const CuttlefishConfig& config,
255     const CuttlefishConfig::InstanceSpecific& instance) {
256   std::vector<ImagePartition> partitions;
257 
258   // Note that if the position of uboot_env changes, the environment for
259   // u-boot must be updated as well (see boot_config.cc and
260   // cuttlefish.fragment in external/u-boot).
261   partitions.push_back(ImagePartition{
262       .label = "uboot_env",
263       .image_file_path = AbsolutePath(instance.uboot_env_image_path()),
264   });
265   partitions.push_back(ImagePartition{
266       .label = "vbmeta",
267       .image_file_path = AbsolutePath(instance.vbmeta_path()),
268   });
269   if (!FLAGS_protected_vm) {
270     partitions.push_back(ImagePartition{
271         .label = "frp",
272         .image_file_path =
273             AbsolutePath(instance.factory_reset_protected_path()),
274     });
275   }
276   if (config.bootconfig_supported()) {
277     partitions.push_back(ImagePartition{
278         .label = "bootconfig",
279         .image_file_path = AbsolutePath(instance.persistent_bootconfig_path()),
280     });
281   }
282   return partitions;
283 }
284 
AvailableSpaceAtPath(const std::string & path)285 static uint64_t AvailableSpaceAtPath(const std::string& path) {
286   struct statvfs vfs;
287   if (statvfs(path.c_str(), &vfs) != 0) {
288     int error_num = errno;
289     LOG(ERROR) << "Could not find space available at " << path << ", error was "
290                << strerror(error_num);
291     return 0;
292   }
293   // f_frsize (block size) * f_bavail (free blocks) for unprivileged users.
294   return static_cast<uint64_t>(vfs.f_frsize) * vfs.f_bavail;
295 }
296 
297 class BootImageRepacker : public SetupFeature {
298  public:
INJECT(BootImageRepacker (const CuttlefishConfig & config))299   INJECT(BootImageRepacker(const CuttlefishConfig& config)) : config_(config) {}
300 
301   // SetupFeature
Name() const302   std::string Name() const override { return "BootImageRepacker"; }
Dependencies() const303   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
Enabled() const304   bool Enabled() const override {
305     // If we are booting a protected VM, for now, assume that image repacking
306     // isn't trusted. Repacking requires resigning the image and keys from an
307     // android host aren't trusted.
308     return !config_.protected_vm();
309   }
310 
311  protected:
Setup()312   bool Setup() override {
313     if (!FileHasContent(FLAGS_boot_image)) {
314       LOG(ERROR) << "File not found: " << FLAGS_boot_image;
315       return false;
316     }
317     // The init_boot partition is be optional for testing boot.img
318     // with the ramdisk inside.
319     if (!FileHasContent(FLAGS_init_boot_image)) {
320       LOG(WARNING) << "File not found: " << FLAGS_init_boot_image;
321     }
322 
323     if (!FileHasContent(FLAGS_vendor_boot_image)) {
324       LOG(ERROR) << "File not found: " << FLAGS_vendor_boot_image;
325       return false;
326     }
327 
328     // Repacking a boot.img doesn't work with Gem5 because the user must always
329     // specify a vmlinux instead of an arm64 Image, and that file can be too
330     // large to be repacked. Skip repack of boot.img on Gem5, as we need to be
331     // able to extract the ramdisk.img in a later stage and so this step must
332     // not fail (..and the repacked kernel wouldn't be used anyway).
333     if (FLAGS_kernel_path.size() &&
334         config_.vm_manager() != Gem5Manager::name()) {
335       const std::string new_boot_image_path =
336           config_.AssemblyPath("boot_repacked.img");
337       bool success =
338           RepackBootImage(FLAGS_kernel_path, FLAGS_boot_image,
339                           new_boot_image_path, config_.assembly_dir());
340       if (!success) {
341         LOG(ERROR) << "Failed to regenerate the boot image with the new kernel";
342         return false;
343       }
344       SetCommandLineOptionWithMode("boot_image", new_boot_image_path.c_str(),
345                                    google::FlagSettingMode::SET_FLAGS_DEFAULT);
346     }
347 
348     if (FLAGS_kernel_path.size() || FLAGS_initramfs_path.size()) {
349       const std::string new_vendor_boot_image_path =
350           config_.AssemblyPath("vendor_boot_repacked.img");
351       // Repack the vendor boot images if kernels and/or ramdisks are passed in.
352       if (FLAGS_initramfs_path.size()) {
353         bool success = RepackVendorBootImage(
354             FLAGS_initramfs_path, FLAGS_vendor_boot_image,
355             new_vendor_boot_image_path, config_.assembly_dir(),
356             config_.bootconfig_supported());
357         if (!success) {
358           LOG(ERROR) << "Failed to regenerate the vendor boot image with the "
359                         "new ramdisk";
360         } else {
361           // This control flow implies a kernel with all configs built in.
362           // If it's just the kernel, repack the vendor boot image without a
363           // ramdisk.
364           bool success = RepackVendorBootImageWithEmptyRamdisk(
365               FLAGS_vendor_boot_image, new_vendor_boot_image_path,
366               config_.assembly_dir(), config_.bootconfig_supported());
367           if (!success) {
368             LOG(ERROR) << "Failed to regenerate the vendor boot image without "
369                           "a ramdisk";
370             return false;
371           }
372         }
373         SetCommandLineOptionWithMode(
374             "vendor_boot_image", new_vendor_boot_image_path.c_str(),
375             google::FlagSettingMode::SET_FLAGS_DEFAULT);
376       }
377     }
378     return true;
379   }
380 
381  private:
382   const CuttlefishConfig& config_;
383 };
384 
385 class Gem5ImageUnpacker : public SetupFeature {
386  public:
INJECT(Gem5ImageUnpacker (const CuttlefishConfig & config,BootImageRepacker & bir))387   INJECT(Gem5ImageUnpacker(
388       const CuttlefishConfig& config,
389       BootImageRepacker& bir))
390       : config_(config),
391         bir_(bir) {}
392 
393   // SetupFeature
Name() const394   std::string Name() const override { return "Gem5ImageUnpacker"; }
395 
Dependencies() const396   std::unordered_set<SetupFeature*> Dependencies() const override {
397     return {
398         static_cast<SetupFeature*>(&bir_),
399     };
400   }
401 
Enabled() const402   bool Enabled() const override {
403     // Everything has a bootloader except gem5, so only run this for gem5
404     return config_.vm_manager() == Gem5Manager::name();
405   }
406 
407  protected:
Setup()408   bool Setup() override {
409     /* Unpack the original or repacked boot and vendor boot ramdisks, so that
410      * we have access to the baked bootconfig and raw compressed ramdisks.
411      * This allows us to emulate what a bootloader would normally do, which
412      * Gem5 can't support itself. This code also copies the kernel again
413      * (because Gem5 only supports raw vmlinux) and handles the bootloader
414      * binaries specially. This code is just part of the solution; it only
415      * does the parts which are instance agnostic.
416      */
417 
418     if (!FileHasContent(FLAGS_boot_image)) {
419       LOG(ERROR) << "File not found: " << FLAGS_boot_image;
420       return false;
421     }
422     // The init_boot partition is be optional for testing boot.img
423     // with the ramdisk inside.
424     if (!FileHasContent(FLAGS_init_boot_image)) {
425       LOG(WARNING) << "File not found: " << FLAGS_init_boot_image;
426     }
427 
428     if (!FileHasContent(FLAGS_vendor_boot_image)) {
429       LOG(ERROR) << "File not found: " << FLAGS_vendor_boot_image;
430       return false;
431     }
432 
433     const std::string unpack_dir = config_.assembly_dir();
434 
435     bool success = UnpackBootImage(FLAGS_init_boot_image, unpack_dir);
436     if (!success) {
437       LOG(ERROR) << "Failed to extract the init boot image";
438       return false;
439     }
440 
441     success = UnpackVendorBootImageIfNotUnpacked(FLAGS_vendor_boot_image,
442                                                  unpack_dir);
443     if (!success) {
444       LOG(ERROR) << "Failed to extract the vendor boot image";
445       return false;
446     }
447 
448     // Assume the user specified a kernel manually which is a vmlinux
449     std::ofstream kernel(unpack_dir + "/kernel", std::ios_base::binary |
450                                                  std::ios_base::trunc);
451     std::ifstream vmlinux(FLAGS_kernel_path, std::ios_base::binary);
452     kernel << vmlinux.rdbuf();
453     kernel.close();
454 
455     // Gem5 needs the bootloader binary to be a specific directory structure
456     // to find it. Create a 'binaries' directory and copy it into there
457     const std::string binaries_dir = unpack_dir + "/binaries";
458     if (mkdir(binaries_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
459         && errno != EEXIST) {
460       PLOG(ERROR) << "Failed to create dir: \"" << binaries_dir << "\" ";
461       return false;
462     }
463     std::ofstream bootloader(binaries_dir + "/" +
464                              cpp_basename(FLAGS_bootloader),
465                              std::ios_base::binary | std::ios_base::trunc);
466     std::ifstream src_bootloader(FLAGS_bootloader, std::ios_base::binary);
467     bootloader << src_bootloader.rdbuf();
468     bootloader.close();
469 
470     // Gem5 also needs the ARM version of the bootloader, even though it
471     // doesn't use it. It'll even open it to check it's a valid ELF file.
472     // Work around this by copying such a named file from the same directory
473     std::ofstream boot_arm(binaries_dir + "/boot.arm",
474                            std::ios_base::binary | std::ios_base::trunc);
475     std::ifstream src_boot_arm(cpp_dirname(FLAGS_bootloader) + "/boot.arm",
476                                std::ios_base::binary);
477     boot_arm << src_boot_arm.rdbuf();
478     boot_arm.close();
479 
480     return true;
481   }
482 
483  private:
484   const CuttlefishConfig& config_;
485   BootImageRepacker& bir_;
486 };
487 
488 class GeneratePersistentBootconfig : public SetupFeature {
489  public:
INJECT(GeneratePersistentBootconfig (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))490   INJECT(GeneratePersistentBootconfig(
491       const CuttlefishConfig& config,
492       const CuttlefishConfig::InstanceSpecific& instance))
493       : config_(config), instance_(instance) {}
494 
495   // SetupFeature
Name() const496   std::string Name() const override {
497     return "GeneratePersistentBootconfig";
498   }
Enabled() const499   bool Enabled() const override {
500     return (!config_.protected_vm());
501   }
502 
503  private:
Dependencies() const504   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
Setup()505   bool Setup() override {
506     //  Cuttlefish for the time being won't be able to support OTA from a
507     //  non-bootconfig kernel to a bootconfig-kernel (or vice versa) IF the
508     //  device is stopped (via stop_cvd). This is rarely an issue since OTA
509     //  testing run on cuttlefish is done within one launch cycle of the device.
510     //  If this ever becomes an issue, this code will have to be rewritten.
511     if(!config_.bootconfig_supported()) {
512       return true;
513     }
514 
515     const auto bootconfig_path = instance_.persistent_bootconfig_path();
516     if (!FileExists(bootconfig_path)) {
517       if (!CreateBlankImage(bootconfig_path, 1 /* mb */, "none")) {
518         LOG(ERROR) << "Failed to create image at " << bootconfig_path;
519         return false;
520       }
521     }
522 
523     auto bootconfig_fd = SharedFD::Open(bootconfig_path, O_RDWR);
524     if (!bootconfig_fd->IsOpen()) {
525       LOG(ERROR) << "Unable to open bootconfig file: "
526                  << bootconfig_fd->StrError();
527       return false;
528     }
529 
530     const std::string bootconfig =
531         android::base::Join(BootconfigArgsFromConfig(config_, instance_),
532                             "\n") +
533         "\n";
534     ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig);
535     LOG(DEBUG) << "bootconfig size is " << bytesWritten;
536     if (bytesWritten != bootconfig.size()) {
537       LOG(ERROR) << "Failed to write contents of bootconfig to \""
538                  << bootconfig_path << "\"";
539       return false;
540     }
541     LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are "
542                << ReadFile(bootconfig_path);
543 
544     if (bootconfig_fd->Truncate(bytesWritten) != 0) {
545       LOG(ERROR) << "`truncate --size=" << bytesWritten << " bytes "
546                  << bootconfig_path << "` failed:" << bootconfig_fd->StrError();
547       return false;
548     }
549 
550     if (config_.vm_manager() != Gem5Manager::name()) {
551       bootconfig_fd->Close();
552       const off_t bootconfig_size_bytes = AlignToPowerOf2(
553           MAX_AVB_METADATA_SIZE + bytesWritten, PARTITION_SIZE_SHIFT);
554 
555       auto avbtool_path = HostBinaryPath("avbtool");
556       Command bootconfig_hash_footer_cmd(avbtool_path);
557       bootconfig_hash_footer_cmd.AddParameter("add_hash_footer");
558       bootconfig_hash_footer_cmd.AddParameter("--image");
559       bootconfig_hash_footer_cmd.AddParameter(bootconfig_path);
560       bootconfig_hash_footer_cmd.AddParameter("--partition_size");
561       bootconfig_hash_footer_cmd.AddParameter(bootconfig_size_bytes);
562       bootconfig_hash_footer_cmd.AddParameter("--partition_name");
563       bootconfig_hash_footer_cmd.AddParameter("bootconfig");
564       bootconfig_hash_footer_cmd.AddParameter("--key");
565       bootconfig_hash_footer_cmd.AddParameter(
566           DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem"));
567       bootconfig_hash_footer_cmd.AddParameter("--algorithm");
568       bootconfig_hash_footer_cmd.AddParameter("SHA256_RSA4096");
569       int success = bootconfig_hash_footer_cmd.Start().Wait();
570       if (success != 0) {
571         LOG(ERROR) << "Unable to run append hash footer. Exited with status "
572                    << success;
573         return false;
574       }
575     } else {
576       const off_t bootconfig_size_bytes_gem5 = AlignToPowerOf2(
577           bytesWritten, PARTITION_SIZE_SHIFT);
578       bootconfig_fd->Truncate(bootconfig_size_bytes_gem5);
579       bootconfig_fd->Close();
580     }
581     return true;
582   }
583 
584   const CuttlefishConfig& config_;
585   const CuttlefishConfig::InstanceSpecific& instance_;
586 };
587 
588 class GeneratePersistentVbmeta : public SetupFeature {
589  public:
INJECT(GeneratePersistentVbmeta (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,InitBootloaderEnvPartition & bootloader_env,GeneratePersistentBootconfig & bootconfig))590   INJECT(GeneratePersistentVbmeta(
591       const CuttlefishConfig& config,
592       const CuttlefishConfig::InstanceSpecific& instance,
593       InitBootloaderEnvPartition& bootloader_env,
594       GeneratePersistentBootconfig& bootconfig))
595       : config_(config),
596         instance_(instance),
597         bootloader_env_(bootloader_env),
598         bootconfig_(bootconfig) {}
599 
600   // SetupFeature
Name() const601   std::string Name() const override {
602     return "GeneratePersistentVbmeta";
603   }
Enabled() const604   bool Enabled() const override {
605     return (!config_.protected_vm());
606   }
607 
608  private:
Dependencies() const609   std::unordered_set<SetupFeature*> Dependencies() const override {
610     return {
611         static_cast<SetupFeature*>(&bootloader_env_),
612         static_cast<SetupFeature*>(&bootconfig_),
613     };
614   }
615 
Setup()616   bool Setup() override {
617     auto avbtool_path = HostBinaryPath("avbtool");
618     Command vbmeta_cmd(avbtool_path);
619     vbmeta_cmd.AddParameter("make_vbmeta_image");
620     vbmeta_cmd.AddParameter("--output");
621     vbmeta_cmd.AddParameter(instance_.vbmeta_path());
622     vbmeta_cmd.AddParameter("--algorithm");
623     vbmeta_cmd.AddParameter("SHA256_RSA4096");
624     vbmeta_cmd.AddParameter("--key");
625     vbmeta_cmd.AddParameter(
626         DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem"));
627 
628     vbmeta_cmd.AddParameter("--chain_partition");
629     vbmeta_cmd.AddParameter("uboot_env:1:" +
630                             DefaultHostArtifactsPath("etc/cvd.avbpubkey"));
631 
632     if (config_.bootconfig_supported()) {
633         vbmeta_cmd.AddParameter("--chain_partition");
634         vbmeta_cmd.AddParameter("bootconfig:2:" +
635                                 DefaultHostArtifactsPath("etc/cvd.avbpubkey"));
636     }
637 
638     bool success = vbmeta_cmd.Start().Wait();
639     if (success != 0) {
640       LOG(ERROR) << "Unable to create persistent vbmeta. Exited with status "
641                  << success;
642       return false;
643     }
644 
645     if (FileSize(instance_.vbmeta_path()) > VBMETA_MAX_SIZE) {
646       LOG(ERROR) << "Generated vbmeta - " << instance_.vbmeta_path()
647                  << " is larger than the expected " << VBMETA_MAX_SIZE
648                  << ". Stopping.";
649       return false;
650     }
651     if (FileSize(instance_.vbmeta_path()) != VBMETA_MAX_SIZE) {
652       auto fd = SharedFD::Open(instance_.vbmeta_path(), O_RDWR);
653       if (!fd->IsOpen() || fd->Truncate(VBMETA_MAX_SIZE) != 0) {
654         LOG(ERROR) << "`truncate --size=" << VBMETA_MAX_SIZE << " "
655                    << instance_.vbmeta_path() << "` "
656                    << "failed: " << fd->StrError();
657         return false;
658       }
659     }
660     return true;
661   }
662 
663   const CuttlefishConfig& config_;
664   const CuttlefishConfig::InstanceSpecific& instance_;
665   InitBootloaderEnvPartition& bootloader_env_;
666   GeneratePersistentBootconfig& bootconfig_;
667 };
668 
669 class InitializeMetadataImage : public SetupFeature {
670  public:
INJECT(InitializeMetadataImage ())671   INJECT(InitializeMetadataImage()) {}
672 
673   // SetupFeature
Name() const674   std::string Name() const override { return "InitializeMetadataImage"; }
Enabled() const675   bool Enabled() const override { return true; }
676 
677  private:
Dependencies() const678   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()679   Result<void> ResultSetup() override {
680     if (FileExists(FLAGS_metadata_image)) {
681       return {};
682     }
683 
684     CF_EXPECT(CreateBlankImage(FLAGS_metadata_image,
685                                FLAGS_blank_metadata_image_mb, "none"),
686               "Failed to create \"" << FLAGS_metadata_image << "\" with size "
687                                     << FLAGS_blank_metadata_image_mb);
688     return {};
689   }
690 };
691 
692 class InitializeAccessKregistryImage : public SetupFeature {
693  public:
INJECT(InitializeAccessKregistryImage (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))694   INJECT(InitializeAccessKregistryImage(
695       const CuttlefishConfig& config,
696       const CuttlefishConfig::InstanceSpecific& instance))
697       : config_(config), instance_(instance) {}
698 
699   // SetupFeature
Name() const700   std::string Name() const override { return "InitializeAccessKregistryImage"; }
Enabled() const701   bool Enabled() const override { return !config_.protected_vm(); }
702 
703  private:
Dependencies() const704   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()705   Result<void> ResultSetup() override {
706     auto access_kregistry = instance_.access_kregistry_path();
707     if (FileExists(access_kregistry)) {
708       return {};
709     }
710     CF_EXPECT(CreateBlankImage(access_kregistry, 2 /* mb */, "none"),
711               "Failed to create \"" << access_kregistry << "\"");
712     return {};
713   }
714 
715   const CuttlefishConfig& config_;
716   const CuttlefishConfig::InstanceSpecific& instance_;
717 };
718 
719 class InitializeHwcomposerPmemImage : public SetupFeature {
720  public:
INJECT(InitializeHwcomposerPmemImage (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))721   INJECT(InitializeHwcomposerPmemImage(
722       const CuttlefishConfig& config,
723       const CuttlefishConfig::InstanceSpecific& instance))
724       : config_(config), instance_(instance) {}
725 
726   // SetupFeature
Name() const727   std::string Name() const override { return "InitializeHwcomposerPmemImage"; }
Enabled() const728   bool Enabled() const override { return !config_.protected_vm(); }
729 
730  private:
Dependencies() const731   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()732   Result<void> ResultSetup() override {
733     if (FileExists(instance_.hwcomposer_pmem_path())) {
734       return {};
735     }
736     CF_EXPECT(
737         CreateBlankImage(instance_.hwcomposer_pmem_path(), 2 /* mb */, "none"),
738         "Failed creating \"" << instance_.hwcomposer_pmem_path() << "\"");
739     return {};
740   }
741 
742   const CuttlefishConfig& config_;
743   const CuttlefishConfig::InstanceSpecific& instance_;
744 };
745 
746 class InitializePstore : public SetupFeature {
747  public:
INJECT(InitializePstore (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))748   INJECT(InitializePstore(const CuttlefishConfig& config,
749                           const CuttlefishConfig::InstanceSpecific& instance))
750       : config_(config), instance_(instance) {}
751 
752   // SetupFeature
Name() const753   std::string Name() const override { return "InitializePstore"; }
Enabled() const754   bool Enabled() const override { return !config_.protected_vm(); }
755 
756  private:
Dependencies() const757   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()758   Result<void> ResultSetup() override {
759     if (FileExists(instance_.pstore_path())) {
760       return {};
761     }
762 
763     CF_EXPECT(CreateBlankImage(instance_.pstore_path(), 2 /* mb */, "none"),
764               "Failed to create \"" << instance_.pstore_path() << "\"");
765     return {};
766   }
767 
768   const CuttlefishConfig& config_;
769   const CuttlefishConfig::InstanceSpecific& instance_;
770 };
771 
772 class InitializeSdCard : public SetupFeature {
773  public:
INJECT(InitializeSdCard (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))774   INJECT(InitializeSdCard(const CuttlefishConfig& config,
775                           const CuttlefishConfig::InstanceSpecific& instance))
776       : config_(config), instance_(instance) {}
777 
778   // SetupFeature
Name() const779   std::string Name() const override { return "InitializeSdCard"; }
Enabled() const780   bool Enabled() const override {
781     return FLAGS_use_sdcard && !config_.protected_vm();
782   }
783 
784  private:
Dependencies() const785   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()786   Result<void> ResultSetup() override {
787     if (FileExists(instance_.sdcard_path())) {
788       return {};
789     }
790     CF_EXPECT(CreateBlankImage(instance_.sdcard_path(),
791                                FLAGS_blank_sdcard_image_mb, "sdcard"),
792               "Failed to create \"" << instance_.sdcard_path() << "\"");
793     return {};
794   }
795 
796   const CuttlefishConfig& config_;
797   const CuttlefishConfig::InstanceSpecific& instance_;
798 };
799 
800 class InitializeFactoryResetProtected : public SetupFeature {
801  public:
INJECT(InitializeFactoryResetProtected (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))802   INJECT(InitializeFactoryResetProtected(
803       const CuttlefishConfig& config,
804       const CuttlefishConfig::InstanceSpecific& instance))
805       : config_(config), instance_(instance) {}
806 
807   // SetupFeature
Name() const808   std::string Name() const override { return "InitializeSdCard"; }
Enabled() const809   bool Enabled() const override { return !config_.protected_vm(); }
810 
811  private:
Dependencies() const812   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()813   Result<void> ResultSetup() override {
814     auto frp = instance_.factory_reset_protected_path();
815     if (FileExists(frp)) {
816       return {};
817     }
818     CF_EXPECT(CreateBlankImage(frp, 1 /* mb */, "none"),
819               "Failed to create \"" << frp << "\"");
820     return {};
821   }
822 
823   const CuttlefishConfig& config_;
824   const CuttlefishConfig::InstanceSpecific& instance_;
825 };
826 
827 class InitializeInstanceCompositeDisk : public SetupFeature {
828  public:
INJECT(InitializeInstanceCompositeDisk (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,InitializeFactoryResetProtected & frp,GeneratePersistentVbmeta & vbmeta))829   INJECT(InitializeInstanceCompositeDisk(
830       const CuttlefishConfig& config,
831       const CuttlefishConfig::InstanceSpecific& instance,
832       InitializeFactoryResetProtected& frp,
833       GeneratePersistentVbmeta& vbmeta))
834       : config_(config),
835         instance_(instance),
836         frp_(frp),
837         vbmeta_(vbmeta) {}
838 
Name() const839   std::string Name() const override {
840     return "InitializeInstanceCompositeDisk";
841   }
Enabled() const842   bool Enabled() const override { return true; }
843 
844  private:
Dependencies() const845   std::unordered_set<SetupFeature*> Dependencies() const override {
846     return {
847         static_cast<SetupFeature*>(&frp_),
848         static_cast<SetupFeature*>(&vbmeta_),
849     };
850   }
ResultSetup()851   Result<void> ResultSetup() override {
852     auto ipath = [this](const std::string& path) -> std::string {
853       return instance_.PerInstancePath(path.c_str());
854     };
855     auto persistent_disk_builder =
856         DiskBuilder()
857             .Partitions(persistent_composite_disk_config(config_, instance_))
858             .VmManager(config_.vm_manager())
859             .CrosvmPath(config_.crosvm_binary())
860             .ConfigPath(ipath("persistent_composite_disk_config.txt"))
861             .HeaderPath(ipath("persistent_composite_gpt_header.img"))
862             .FooterPath(ipath("persistent_composite_gpt_footer.img"))
863             .CompositeDiskPath(instance_.persistent_composite_disk_path())
864             .ResumeIfPossible(FLAGS_resume);
865 
866     CF_EXPECT(persistent_disk_builder.BuildCompositeDiskIfNecessary());
867     return {};
868   }
869 
870   const CuttlefishConfig& config_;
871   const CuttlefishConfig::InstanceSpecific& instance_;
872   InitializeFactoryResetProtected& frp_;
873   GeneratePersistentVbmeta& vbmeta_;
874 };
875 
876 class VbmetaEnforceMinimumSize : public SetupFeature {
877  public:
INJECT(VbmetaEnforceMinimumSize ())878   INJECT(VbmetaEnforceMinimumSize()) {}
879 
Name() const880   std::string Name() const override { return "VbmetaEnforceMinimumSize"; }
Enabled() const881   bool Enabled() const override { return true; }
882 
883  private:
Dependencies() const884   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()885   Result<void> ResultSetup() override {
886     // libavb expects to be able to read the maximum vbmeta size, so we must
887     // provide a partition which matches this or the read will fail
888     for (const auto& vbmeta_image :
889          {FLAGS_vbmeta_image, FLAGS_vbmeta_system_image}) {
890       if (FileSize(vbmeta_image) != VBMETA_MAX_SIZE) {
891         auto fd = SharedFD::Open(vbmeta_image, O_RDWR);
892         CF_EXPECT(fd->IsOpen(), "Could not open \"" << vbmeta_image << "\": "
893                                                     << fd->StrError());
894         CF_EXPECT(fd->Truncate(VBMETA_MAX_SIZE) == 0,
895                   "`truncate --size=" << VBMETA_MAX_SIZE << " " << vbmeta_image
896                                       << "` failed: " << fd->StrError());
897       }
898     }
899     return {};
900   }
901 };
902 
903 class BootloaderPresentCheck : public SetupFeature {
904  public:
INJECT(BootloaderPresentCheck ())905   INJECT(BootloaderPresentCheck()) {}
906 
Name() const907   std::string Name() const override { return "BootloaderPresentCheck"; }
Enabled() const908   bool Enabled() const override { return true; }
909 
910  private:
Dependencies() const911   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()912   Result<void> ResultSetup() override {
913     CF_EXPECT(FileHasContent(FLAGS_bootloader),
914               "File not found: " << FLAGS_bootloader);
915     return {};
916   }
917 };
918 
DiskChangesComponent(const FetcherConfig * fetcher,const CuttlefishConfig * config)919 static fruit::Component<> DiskChangesComponent(const FetcherConfig* fetcher,
920                                                const CuttlefishConfig* config) {
921   return fruit::createComponent()
922       .bindInstance(*fetcher)
923       .bindInstance(*config)
924       .addMultibinding<SetupFeature, InitializeMetadataImage>()
925       .addMultibinding<SetupFeature, BootImageRepacker>()
926       .addMultibinding<SetupFeature, VbmetaEnforceMinimumSize>()
927       .addMultibinding<SetupFeature, BootloaderPresentCheck>()
928       .addMultibinding<SetupFeature, Gem5ImageUnpacker>()
929       .install(FixedMiscImagePathComponent, &FLAGS_misc_image)
930       .install(InitializeMiscImageComponent)
931       .install(FixedDataImagePathComponent, &FLAGS_data_image)
932       .install(InitializeDataImageComponent)
933       // Create esp if necessary
934       .install(InitializeEspImageComponent, &FLAGS_otheros_esp_image,
935                &FLAGS_otheros_kernel_path, &FLAGS_otheros_initramfs_path,
936                &FLAGS_otheros_root_image, config)
937       .install(SuperImageRebuilderComponent, &FLAGS_super_image);
938 }
939 
DiskChangesPerInstanceComponent(const FetcherConfig * fetcher,const CuttlefishConfig * config,const CuttlefishConfig::InstanceSpecific * instance)940 static fruit::Component<> DiskChangesPerInstanceComponent(
941     const FetcherConfig* fetcher, const CuttlefishConfig* config,
942     const CuttlefishConfig::InstanceSpecific* instance) {
943   return fruit::createComponent()
944       .bindInstance(*fetcher)
945       .bindInstance(*config)
946       .bindInstance(*instance)
947       .addMultibinding<SetupFeature, InitializeAccessKregistryImage>()
948       .addMultibinding<SetupFeature, InitializeHwcomposerPmemImage>()
949       .addMultibinding<SetupFeature, InitializePstore>()
950       .addMultibinding<SetupFeature, InitializeSdCard>()
951       .addMultibinding<SetupFeature, InitializeFactoryResetProtected>()
952       .addMultibinding<SetupFeature, GeneratePersistentBootconfig>()
953       .addMultibinding<SetupFeature, GeneratePersistentVbmeta>()
954       .addMultibinding<SetupFeature, InitializeInstanceCompositeDisk>()
955       .install(InitBootloaderEnvPartitionComponent);
956 }
957 
CreateDynamicDiskFiles(const FetcherConfig & fetcher_config,const CuttlefishConfig & config)958 Result<void> CreateDynamicDiskFiles(const FetcherConfig& fetcher_config,
959                                     const CuttlefishConfig& config) {
960   // TODO(schuffelen): Unify this with the other injector created in
961   // assemble_cvd.cpp
962   fruit::Injector<> injector(DiskChangesComponent, &fetcher_config, &config);
963 
964   const auto& features = injector.getMultibindings<SetupFeature>();
965   CF_EXPECT(SetupFeature::RunSetup(features));
966 
967   for (const auto& instance : config.Instances()) {
968     fruit::Injector<> instance_injector(DiskChangesPerInstanceComponent,
969                                         &fetcher_config, &config, &instance);
970     const auto& instance_features =
971         instance_injector.getMultibindings<SetupFeature>();
972     CF_EXPECT(SetupFeature::RunSetup(instance_features),
973               "instance = \"" << instance.instance_name() << "\"");
974   }
975 
976   // Check if filling in the sparse image would run out of disk space.
977   auto existing_sizes = SparseFileSizes(FLAGS_data_image);
978   CF_EXPECT(existing_sizes.sparse_size > 0 || existing_sizes.disk_size > 0,
979             "Unable to determine size of \"" << FLAGS_data_image
980                                              << "\". Does this file exist?");
981   auto available_space = AvailableSpaceAtPath(FLAGS_data_image);
982   if (available_space < existing_sizes.sparse_size - existing_sizes.disk_size) {
983     // TODO(schuffelen): Duplicate this check in run_cvd when it can run on a
984     // separate machine
985     return CF_ERR("Not enough space remaining in fs containing \""
986                   << FLAGS_data_image << "\", wanted "
987                   << (existing_sizes.sparse_size - existing_sizes.disk_size)
988                   << ", got " << available_space);
989   } else {
990     LOG(DEBUG) << "Available space: " << available_space;
991     LOG(DEBUG) << "Sparse size of \"" << FLAGS_data_image
992                << "\": " << existing_sizes.sparse_size;
993     LOG(DEBUG) << "Disk size of \"" << FLAGS_data_image
994                << "\": " << existing_sizes.disk_size;
995   }
996 
997   auto os_disk_builder = OsCompositeDiskBuilder(config);
998   auto built_composite =
999       CF_EXPECT(os_disk_builder.BuildCompositeDiskIfNecessary());
1000   if (built_composite) {
1001     for (auto instance : config.Instances()) {
1002       if (FileExists(instance.access_kregistry_path())) {
1003         CF_EXPECT(CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */,
1004                                    "none"),
1005                   "Failed for \"" << instance.access_kregistry_path() << "\"");
1006       }
1007       if (FileExists(instance.hwcomposer_pmem_path())) {
1008         CF_EXPECT(CreateBlankImage(instance.hwcomposer_pmem_path(), 2 /* mb */,
1009                                    "none"),
1010                   "Failed for \"" << instance.hwcomposer_pmem_path() << "\"");
1011       }
1012       if (FileExists(instance.pstore_path())) {
1013         CF_EXPECT(CreateBlankImage(instance.pstore_path(), 2 /* mb */, "none"),
1014                   "Failed for\"" << instance.pstore_path() << "\"");
1015       }
1016     }
1017   }
1018 
1019   if (!FLAGS_protected_vm) {
1020     for (auto instance : config.Instances()) {
1021       os_disk_builder.OverlayPath(instance.PerInstancePath("overlay.img"));
1022       CF_EXPECT(os_disk_builder.BuildOverlayIfNecessary());
1023       if (instance.start_ap()) {
1024         os_disk_builder.OverlayPath(instance.PerInstancePath("ap_overlay.img"));
1025         CF_EXPECT(os_disk_builder.BuildOverlayIfNecessary());
1026       }
1027     }
1028   }
1029 
1030   for (auto instance : config.Instances()) {
1031     // Check that the files exist
1032     for (const auto& file : instance.virtual_disk_paths()) {
1033       if (!file.empty()) {
1034         CF_EXPECT(FileHasContent(file), "File not found: \"" << file << "\"");
1035       }
1036     }
1037     // Gem5 Simulate per-instance what the bootloader would usually do
1038     // Since on other devices this runs every time, just do it here every time
1039     if (config.vm_manager() == Gem5Manager::name()) {
1040       RepackGem5BootImage(
1041           instance.PerInstancePath("initrd.img"),
1042           instance.persistent_bootconfig_path(),
1043           config.assembly_dir());
1044     }
1045   }
1046 
1047   return {};
1048 }
1049 
1050 } // namespace cuttlefish
1051