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