1 /*
2 * Copyright (C) 2018 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/boot_config.h"
18
19 #include <fstream>
20 #include <sstream>
21 #include <string>
22
23 #include <sys/stat.h>
24
25 #include <android-base/logging.h>
26 #include <android-base/strings.h>
27 #include <gflags/gflags.h>
28
29 #include "common/libs/utils/environment.h"
30 #include "common/libs/utils/files.h"
31 #include "common/libs/utils/result.h"
32 #include "common/libs/utils/size_utils.h"
33 #include "common/libs/utils/subprocess.h"
34 #include "host/commands/assemble_cvd/bootconfig_args.h"
35 #include "host/libs/avb/avb.h"
36 #include "host/libs/config/cuttlefish_config.h"
37 #include "host/libs/config/kernel_args.h"
38 #include "host/libs/vm_manager/crosvm_manager.h"
39 #include "host/libs/vm_manager/vm_manager.h"
40
41 using cuttlefish::vm_manager::CrosvmManager;
42
43 DECLARE_string(vm_manager);
44
45 namespace cuttlefish {
46 namespace {
47
48 // The ordering of tap devices we're passing to crosvm / qemu is important
49 // Ethernet tap device is the second one (eth1) we're passing ATM
50 static constexpr char kUbootPrimaryEth[] = "eth1";
51
WritePausedEntrypoint(const std::string & entrypoint,const CuttlefishConfig::InstanceSpecific & instance,std::ostream & env)52 void WritePausedEntrypoint(const std::string& entrypoint,
53 const CuttlefishConfig::InstanceSpecific& instance,
54 std::ostream& env) {
55 if (instance.pause_in_bootloader()) {
56 env << "if test $paused -ne 1; then paused=1; else " << entrypoint << "; fi";
57 } else {
58 env << entrypoint;
59 }
60
61 env << '\0';
62 }
63
WriteAndroidEnvironment(std::ostream & env,const CuttlefishConfig::InstanceSpecific & instance)64 void WriteAndroidEnvironment(
65 std::ostream& env,
66 const CuttlefishConfig::InstanceSpecific& instance) {
67 WritePausedEntrypoint("run bootcmd_android", instance, env);
68
69 if (!instance.boot_slot().empty()) {
70 env << "android_slot_suffix=_" << instance.boot_slot() << '\0';
71 }
72 env << '\0';
73 }
74
WriteEFIEnvironment(const CuttlefishConfig::InstanceSpecific & instance,std::optional<std::uint16_t> partition_num,std::ostream & env)75 void WriteEFIEnvironment(const CuttlefishConfig::InstanceSpecific& instance,
76 std::optional<std::uint16_t> partition_num,
77 std::ostream& env) {
78 std::string partition_str =
79 partition_num ? fmt::format("setenv devplist {:x};", *partition_num) : "";
80 WritePausedEntrypoint(
81 partition_str +
82 "load virtio 0:${devplist} ${loadaddr} efi/boot/bootaa64.efi "
83 "&& bootefi ${loadaddr} ${fdtcontroladdr}; "
84 "load virtio 0:${devplist} ${loadaddr} efi/boot/bootia32.efi && "
85 "bootefi ${loadaddr} ${fdtcontroladdr};"
86 "load virtio 0:${devplist} ${loadaddr} efi/boot/bootriscv64.efi && "
87 "bootefi ${loadaddr} ${fdtcontroladdr}",
88 instance, env);
89 }
90
WriteEnvironment(const CuttlefishConfig::InstanceSpecific & instance,const CuttlefishConfig::InstanceSpecific::BootFlow & flow,const std::string & kernel_args,const std::string & env_path)91 size_t WriteEnvironment(const CuttlefishConfig::InstanceSpecific& instance,
92 const CuttlefishConfig::InstanceSpecific::BootFlow& flow,
93 const std::string& kernel_args,
94 const std::string& env_path) {
95 std::ostringstream env;
96
97 env << "ethprime=" << kUbootPrimaryEth << '\0';
98 if (!kernel_args.empty()) {
99 env << "uenvcmd=setenv bootargs \"$cbootargs " << kernel_args << "\" && ";
100 } else {
101 env << "uenvcmd=setenv bootargs \"$cbootargs\" && ";
102 }
103
104 switch (flow) {
105 case CuttlefishConfig::InstanceSpecific::BootFlow::Android:
106 WriteAndroidEnvironment(env, instance);
107 break;
108 case CuttlefishConfig::InstanceSpecific::BootFlow::AndroidEfiLoader:
109 WriteEFIEnvironment(instance, 1, env);
110 break;
111 case CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOs:
112 WriteEFIEnvironment(instance, 2, env);
113 break;
114 case CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOsDisk:
115 WriteEFIEnvironment(instance, 12, env);
116 break;
117 case CuttlefishConfig::InstanceSpecific::BootFlow::Fuchsia:
118 case CuttlefishConfig::InstanceSpecific::BootFlow::Linux:
119 WriteEFIEnvironment(instance, {}, env);
120 break;
121 }
122
123 std::string env_str = env.str();
124 std::ofstream file_out(env_path.c_str(), std::ios::binary);
125 file_out << env_str;
126
127 if (!file_out.good()) {
128 return 0;
129 }
130
131 return env_str.length();
132 }
133
ReplaceKernelBootArgs(const std::unordered_map<std::string,std::string> & args)134 std::unordered_map<std::string, std::string> ReplaceKernelBootArgs(
135 const std::unordered_map<std::string, std::string>& args) {
136 std::unordered_map<std::string, std::string> ret;
137 std::transform(std::begin(args), std::end(args),
138 std::inserter(ret, ret.end()), [](const auto& kv) {
139 const auto& k = kv.first;
140 const auto& v = kv.second;
141 return std::make_pair(
142 android::base::StringReplace(k, " kernel.", " ", true),
143 v);
144 });
145 return ret;
146 }
147
PrepareBootEnvImage(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,const std::string & image_path,const CuttlefishConfig::InstanceSpecific::BootFlow & flow)148 Result<void> PrepareBootEnvImage(
149 const CuttlefishConfig& config,
150 const CuttlefishConfig::InstanceSpecific& instance,
151 const std::string& image_path,
152 const CuttlefishConfig::InstanceSpecific::BootFlow& flow) {
153 if (instance.protected_vm()) {
154 return {};
155 }
156 auto tmp_boot_env_image_path = image_path + ".tmp";
157 auto uboot_env_path = instance.PerInstancePath("mkenvimg_input");
158 auto kernel_cmdline =
159 android::base::Join(KernelCommandLineFromConfig(config, instance), " ");
160 // If the bootconfig isn't supported in the guest kernel, the bootconfig
161 // args need to be passed in via the uboot env. This won't be an issue for
162 // protect kvm which is running a kernel with bootconfig support.
163 if (!instance.bootconfig_supported()) {
164 auto bootconfig_args =
165 CF_EXPECT(BootconfigArgsFromConfig(config, instance));
166
167 // "androidboot.hardware" kernel parameter has changed to "hardware" in
168 // bootconfig and needs to be replaced before being used in the kernel
169 // cmdline.
170 auto bootconfig_hardware_it = bootconfig_args.find("hardware");
171 if (bootconfig_hardware_it != bootconfig_args.end()) {
172 bootconfig_args["androidboot.hardware"] = bootconfig_hardware_it->second;
173 bootconfig_args.erase(bootconfig_hardware_it);
174 }
175
176 // TODO(b/182417593): Until we pass the module parameters through
177 // modules.options, we pass them through bootconfig using
178 // 'kernel.<key>=<value>' But if we don't support bootconfig, we need to
179 // rename them back to the old cmdline version
180 bootconfig_args = ReplaceKernelBootArgs(bootconfig_args);
181
182 kernel_cmdline +=
183 " " + CF_EXPECT(BootconfigArgsString(bootconfig_args, " "));
184 }
185
186 CF_EXPECTF(WriteEnvironment(instance, flow, kernel_cmdline, uboot_env_path),
187 "Unable to write out plaintext env '{}'", uboot_env_path);
188
189 auto mkimage_path = HostBinaryPath("mkenvimage_slim");
190 Command cmd(mkimage_path);
191 cmd.AddParameter("-output_path");
192 cmd.AddParameter(tmp_boot_env_image_path);
193 cmd.AddParameter("-input_path");
194 cmd.AddParameter(uboot_env_path);
195 int success = cmd.Start().Wait();
196 CF_EXPECTF(success == 0,
197 "Unable to run mkenvimage_slim. Exited with status {}", success);
198
199 const off_t boot_env_size_bytes =
200 AlignToPowerOf2(kMaxAvbMetadataSize + 4096, PARTITION_SIZE_SHIFT);
201
202 std::unique_ptr<Avb> avbtool = GetDefaultAvb();
203 CF_EXPECT(avbtool->AddHashFooter(tmp_boot_env_image_path, "uboot_env",
204 boot_env_size_bytes));
205
206 if (!FileExists(image_path) ||
207 ReadFile(image_path) != ReadFile(tmp_boot_env_image_path)) {
208 CF_EXPECT(RenameFile(tmp_boot_env_image_path, image_path),
209 "Unable to delete the old env image");
210 LOG(DEBUG) << "Updated bootloader environment image.";
211 } else {
212 RemoveFile(tmp_boot_env_image_path);
213 }
214
215 return {};
216 }
217
218 } // namespace
219
InitBootloaderEnvPartition(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance)220 Result<void> InitBootloaderEnvPartition(
221 const CuttlefishConfig& config,
222 const CuttlefishConfig::InstanceSpecific& instance) {
223 if (instance.ap_boot_flow() ==
224 CuttlefishConfig::InstanceSpecific::APBootFlow::Grub) {
225 CF_EXPECT(PrepareBootEnvImage(
226 config, instance, instance.ap_uboot_env_image_path(),
227 CuttlefishConfig::InstanceSpecific::BootFlow::Linux));
228 }
229 CF_EXPECT(PrepareBootEnvImage(
230 config, instance, instance.uboot_env_image_path(), instance.boot_flow()));
231 return {};
232 }
233
234 } // namespace cuttlefish
235