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