1 /*
2  * Copyright (C) 2023 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/disk.h"
18 
19 #include <string>
20 
21 #include "common/libs/utils/files.h"
22 #include "host/commands/assemble_cvd/boot_image_utils.h"
23 #include "host/commands/assemble_cvd/vendor_dlkm_utils.h"
24 #include "host/libs/avb/avb.h"
25 #include "host/libs/config/cuttlefish_config.h"
26 
27 namespace cuttlefish {
28 namespace {
29 
RebuildDlkmAndVbmeta(const std::string & build_dir,const std::string & partition_name,const std::string & output_image,const std::string & vbmeta_image)30 Result<void> RebuildDlkmAndVbmeta(const std::string& build_dir,
31                                   const std::string& partition_name,
32                                   const std::string& output_image,
33                                   const std::string& vbmeta_image) {
34   // TODO(b/149866755) For now, we assume that vendor_dlkm is ext4. Add
35   // logic to handle EROFS once the feature stabilizes.
36   const auto tmp_output_image = output_image + ".tmp";
37   CF_EXPECTF(BuildDlkmImage(build_dir, false, partition_name, tmp_output_image),
38              "Failed to build `{}' image from '{}'", partition_name, build_dir);
39 
40   CF_EXPECT(MoveIfChanged(tmp_output_image, output_image));
41 
42   CF_EXPECT(BuildVbmetaImage(output_image, vbmeta_image),
43             "Failed to rebuild vbmeta vendor.");
44 
45   return {};
46 }
47 
RepackSuperAndVbmeta(const CuttlefishConfig::InstanceSpecific & instance,const std::string & superimg_build_dir,const std::string & vendor_dlkm_build_dir,const std::string & system_dlkm_build_dir,const std::string & ramdisk_path)48 Result<void> RepackSuperAndVbmeta(
49     const CuttlefishConfig::InstanceSpecific& instance,
50     const std::string& superimg_build_dir,
51     const std::string& vendor_dlkm_build_dir,
52     const std::string& system_dlkm_build_dir, const std::string& ramdisk_path) {
53   const auto ramdisk_stage_dir = instance.instance_dir() + "/ramdisk_staged";
54   CF_EXPECT(SplitRamdiskModules(ramdisk_path, ramdisk_stage_dir,
55                                 vendor_dlkm_build_dir, system_dlkm_build_dir),
56             "Failed to move ramdisk modules to vendor_dlkm");
57 
58   const auto new_vendor_dlkm_img =
59       superimg_build_dir + "/vendor_dlkm_repacked.img";
60   CF_EXPECTF(RebuildDlkmAndVbmeta(vendor_dlkm_build_dir, "vendor_dlkm",
61                                   new_vendor_dlkm_img,
62                                   instance.new_vbmeta_vendor_dlkm_image()),
63              "Failed to build vendor_dlkm image from '{}'",
64              vendor_dlkm_build_dir);
65 
66   const auto new_system_dlkm_img =
67       superimg_build_dir + "/system_dlkm_repacked.img";
68   CF_EXPECTF(RebuildDlkmAndVbmeta(system_dlkm_build_dir, "system_dlkm",
69                                   new_system_dlkm_img,
70                                   instance.new_vbmeta_system_dlkm_image()),
71              "Failed to build system_dlkm image from '{}'",
72              system_dlkm_build_dir);
73 
74   const auto new_super_img = instance.new_super_image();
75   CF_EXPECTF(Copy(instance.super_image(), new_super_img),
76              "Failed to copy super image '{}' to '{}': '{}'",
77              instance.super_image(), new_super_img, strerror(errno));
78 
79   CF_EXPECT(RepackSuperWithPartition(new_super_img, new_vendor_dlkm_img,
80                                      "vendor_dlkm"),
81             "Failed to repack super image with new vendor dlkm image.");
82 
83   CF_EXPECT(RepackSuperWithPartition(new_super_img, new_system_dlkm_img,
84                                      "system_dlkm"),
85             "Failed to repack super image with new system dlkm image.");
86 
87   return {};
88 }
89 
90 }  // namespace
91 
RepackKernelRamdisk(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,const Avb & avb)92 Result<void> RepackKernelRamdisk(
93     const CuttlefishConfig& config,
94     const CuttlefishConfig::InstanceSpecific& instance, const Avb& avb) {
95   if (instance.protected_vm()) {
96     // If we are booting a protected VM, for now, assume that image repacking
97     // isn't trusted. Repacking requires resigning the image and keys from an
98     // android host aren't trusted.
99     return {};
100   }
101 
102   CF_EXPECTF(FileHasContent(instance.boot_image()), "File not found: {}",
103              instance.boot_image());
104   // The init_boot partition is be optional for testing boot.img
105   // with the ramdisk inside.
106   if (!FileHasContent(instance.init_boot_image())) {
107     LOG(WARNING) << "File not found: " << instance.init_boot_image();
108   }
109 
110   CF_EXPECTF(FileHasContent(instance.vendor_boot_image()), "File not found: {}",
111              instance.vendor_boot_image());
112 
113   // Repacking a boot.img doesn't work with Gem5 because the user must always
114   // specify a vmlinux instead of an arm64 Image, and that file can be too
115   // large to be repacked. Skip repack of boot.img on Gem5, as we need to be
116   // able to extract the ramdisk.img in a later stage and so this step must
117   // not fail (..and the repacked kernel wouldn't be used anyway).
118   if (instance.kernel_path().size() && config.vm_manager() != VmmMode::kGem5) {
119     CF_EXPECT(
120         RepackBootImage(avb, instance.kernel_path(), instance.boot_image(),
121                         instance.new_boot_image(), instance.instance_dir()),
122         "Failed to regenerate the boot image with the new kernel");
123   }
124 
125   if (instance.kernel_path().size() || instance.initramfs_path().size()) {
126     const std::string new_vendor_boot_image_path =
127         instance.new_vendor_boot_image();
128     // Repack the vendor boot images if kernels and/or ramdisks are passed in.
129     if (instance.initramfs_path().size()) {
130       const auto superimg_build_dir = instance.instance_dir() + "/superimg";
131       const auto ramdisk_repacked =
132           instance.instance_dir() + "/ramdisk_repacked";
133       CF_EXPECTF(Copy(instance.initramfs_path(), ramdisk_repacked),
134                  "Failed to copy {} to {}", instance.initramfs_path(),
135                  ramdisk_repacked);
136       const auto vendor_dlkm_build_dir = superimg_build_dir + "/vendor_dlkm";
137       const auto system_dlkm_build_dir = superimg_build_dir + "/system_dlkm";
138       CF_EXPECT(RepackSuperAndVbmeta(instance, superimg_build_dir,
139                                      vendor_dlkm_build_dir,
140                                      system_dlkm_build_dir, ramdisk_repacked));
141       bool success = RepackVendorBootImage(
142           ramdisk_repacked, instance.vendor_boot_image(),
143           new_vendor_boot_image_path, config.assembly_dir(),
144           instance.bootconfig_supported());
145       if (!success) {
146         LOG(ERROR) << "Failed to regenerate the vendor boot image with the "
147                       "new ramdisk";
148       } else {
149         // This control flow implies a kernel with all configs built in.
150         // If it's just the kernel, repack the vendor boot image without a
151         // ramdisk.
152         CF_EXPECT(
153             RepackVendorBootImageWithEmptyRamdisk(
154                 instance.vendor_boot_image(), new_vendor_boot_image_path,
155                 config.assembly_dir(), instance.bootconfig_supported()),
156             "Failed to regenerate the vendor boot image without a ramdisk");
157       }
158     }
159   }
160   return {};
161 }
162 
163 }  // namespace cuttlefish
164