1 /*
2 * Copyright (C) 2021 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/bootconfig_args.h"
18
19 #include <array>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23
24 #include <android-base/parseint.h>
25
26 #include "common/libs/utils/environment.h"
27 #include "common/libs/utils/files.h"
28 #include "common/libs/utils/json.h"
29 #include "host/libs/config/cuttlefish_config.h"
30 #include "host/libs/config/known_paths.h"
31 #include "host/libs/vm_manager/crosvm_manager.h"
32 #include "host/libs/vm_manager/qemu_manager.h"
33 #include "host/libs/vm_manager/vm_manager.h"
34
35 namespace cuttlefish {
36
37 using vm_manager::CrosvmManager;
38 using vm_manager::QemuManager;
39
40 namespace {
41
42 template <typename T>
AppendMapWithReplacement(T * destination,const T & source)43 void AppendMapWithReplacement(T* destination, const T& source) {
44 for (const auto& [k, v] : source) {
45 (*destination)[k] = v;
46 }
47 }
48
ConsoleBootconfig(const CuttlefishConfig::InstanceSpecific & instance)49 Result<std::unordered_map<std::string, std::string>> ConsoleBootconfig(
50 const CuttlefishConfig::InstanceSpecific& instance) {
51 std::unordered_map<std::string, std::string> bootconfig_args;
52 if (instance.console()) {
53 bootconfig_args["androidboot.console"] = instance.console_dev();
54 bootconfig_args["androidboot.serialconsole"] = "1";
55 } else {
56 // Specify an invalid path under /dev, so the init process will disable the
57 // console service due to the console not being found. On physical devices,
58 // *and on older kernels* it is enough to not specify androidboot.console=
59 // *and* not specify the console= kernel command line parameter, because
60 // the console and kernel dmesg are muxed. However, on cuttlefish, we don't
61 // need to mux, and would prefer to retain the kernel dmesg logging, so we
62 // must work around init falling back to the check for /dev/console (which
63 // we'll always have).
64 // bootconfig_args["androidboot.console"] = "invalid";
65 // The bug above has been fixed in Android 14 and later so we can just
66 // specify androidboot.serialconsole=0 instead.
67 bootconfig_args["androidboot.serialconsole"] = "0";
68 }
69 return bootconfig_args;
70 }
71
72 } // namespace
73
BootconfigArgsFromConfig(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance)74 Result<std::unordered_map<std::string, std::string>> BootconfigArgsFromConfig(
75 const CuttlefishConfig& config,
76 const CuttlefishConfig::InstanceSpecific& instance) {
77 std::unordered_map<std::string, std::string> bootconfig_args;
78
79 AppendMapWithReplacement(&bootconfig_args,
80 CF_EXPECT(ConsoleBootconfig(instance)));
81
82 auto vmm =
83 vm_manager::GetVmManager(config.vm_manager(), instance.target_arch());
84 AppendMapWithReplacement(&bootconfig_args,
85 CF_EXPECT(vmm->ConfigureBootDevices(instance)));
86
87 AppendMapWithReplacement(&bootconfig_args,
88 CF_EXPECT(vmm->ConfigureGraphics(instance)));
89
90 bootconfig_args["androidboot.serialno"] = instance.serial_number();
91 bootconfig_args["androidboot.ddr_size"] =
92 std::to_string(instance.ddr_mem_mb()) + "MB";
93
94 // TODO(b/131884992): update to specify multiple once supported.
95 const auto display_configs = instance.display_configs();
96 if (!display_configs.empty()) {
97 bootconfig_args["androidboot.lcd_density"] =
98 std::to_string(display_configs[0].dpi);
99 }
100
101 bootconfig_args["androidboot.setupwizard_mode"] = instance.setupwizard_mode();
102
103 bootconfig_args["androidboot.enable_bootanimation"] =
104 std::to_string(instance.enable_bootanimation());
105
106 if (!instance.guest_enforce_security()) {
107 bootconfig_args["androidboot.selinux"] = "permissive";
108 }
109
110 if (instance.tombstone_receiver_port()) {
111 bootconfig_args["androidboot.vsock_tombstone_port"] =
112 std::to_string(instance.tombstone_receiver_port());
113 }
114
115 if (instance.openthread_node_id()) {
116 bootconfig_args["androidboot.openthread_node_id"] =
117 std::to_string(instance.openthread_node_id());
118 }
119
120 const auto enable_confui = (config.vm_manager() == VmmMode::kQemu ? 0 : 1);
121 bootconfig_args["androidboot.enable_confirmationui"] =
122 std::to_string(enable_confui);
123
124 if (instance.audiocontrol_server_port()) {
125 bootconfig_args["androidboot.vendor.audiocontrol.server.cid"] =
126 std::to_string(instance.vsock_guest_cid());
127 bootconfig_args["androidboot.vendor.audiocontrol.server.port"] =
128 std::to_string(instance.audiocontrol_server_port());
129 }
130
131 if (!instance.enable_audio()) {
132 bootconfig_args["androidboot.audio.tinyalsa.ignore_output"] = "true";
133 bootconfig_args["androidboot.audio.tinyalsa.simulate_input"] = "true";
134 }
135
136 if (instance.camera_server_port()) {
137 bootconfig_args["androidboot.vsock_camera_port"] =
138 std::to_string(instance.camera_server_port());
139 bootconfig_args["androidboot.vsock_camera_cid"] =
140 std::to_string(instance.vsock_guest_cid());
141 }
142
143 if (instance.lights_server_port()) {
144 bootconfig_args["androidboot.vsock_lights_port"] =
145 std::to_string(instance.lights_server_port());
146 bootconfig_args["androidboot.vsock_lights_cid"] =
147 std::to_string(instance.vsock_guest_cid());
148 }
149
150 if (instance.enable_modem_simulator() &&
151 instance.modem_simulator_ports() != "") {
152 bootconfig_args["androidboot.modem_simulator_ports"] =
153 instance.modem_simulator_ports();
154 }
155
156 // Once all Cuttlefish kernel versions are at least 5.15, filename encryption
157 // will not need to be set conditionally. HCTR2 will always be available.
158 // At that point fstab.cf.f2fs.cts and fstab.cf.ext4.cts can be removed.
159 std::string fstab_suffix = fmt::format("cf.{}.{}", instance.userdata_format(),
160 instance.filename_encryption_mode());
161
162 bootconfig_args["androidboot.fstab_suffix"] = fstab_suffix;
163
164 bootconfig_args["androidboot.wifi_mac_prefix"] =
165 std::to_string(instance.wifi_mac_prefix());
166
167 // Non-native architecture implies a significantly slower execution speed, so
168 // set a large timeout multiplier.
169 if (!IsHostCompatible(instance.target_arch())) {
170 bootconfig_args["androidboot.hw_timeout_multiplier"] = "50";
171 } else {
172 // Even on native architecture, Cuttlefish is still slower than physical
173 // devices in CI environments, so add a small timeout multiplier.
174 bootconfig_args["androidboot.hw_timeout_multiplier"] = "3";
175 }
176
177 // TODO(b/217564326): improve this checks for a hypervisor in the VM.
178 if (instance.target_arch() == Arch::X86 ||
179 instance.target_arch() == Arch::X86_64) {
180 bootconfig_args["androidboot.hypervisor.version"] =
181 "cf-" + ToString(config.vm_manager());
182 bootconfig_args["androidboot.hypervisor.vm.supported"] = "1";
183 } else {
184 bootconfig_args["androidboot.hypervisor.vm.supported"] = "0";
185 }
186 bootconfig_args["androidboot.hypervisor.protected_vm.supported"] = "0";
187 if (!instance.kernel_path().empty()) {
188 bootconfig_args["androidboot.kernel_hotswapped"] = "1";
189 }
190 if (!instance.initramfs_path().empty()) {
191 bootconfig_args["androidboot.ramdisk_hotswapped"] = "1";
192 }
193
194 const auto& secure_hals = CF_EXPECT(config.secure_hals());
195 if (secure_hals.count(SecureHal::kGuestKeymintInsecure)) {
196 bootconfig_args["androidboot.vendor.apex.com.android.hardware.keymint"] =
197 "com.android.hardware.keymint.rust_nonsecure";
198 } else if (secure_hals.count(SecureHal::kGuestKeymintTrustyInsecure)) {
199 bootconfig_args["androidboot.vendor.apex.com.android.hardware.keymint"] =
200 "com.android.hardware.keymint.rust_cf_guest_trusty_nonsecure";
201 } else {
202 bootconfig_args["androidboot.vendor.apex.com.android.hardware.keymint"] =
203 "com.android.hardware.keymint.rust_cf_remote";
204 }
205
206 // Preemptive for when we set up the HAL to be runtime selectable
207 bootconfig_args["androidboot.vendor.apex.com.android.hardware.gatekeeper"] =
208 secure_hals.count(SecureHal::kGuestGatekeeperInsecure)
209 ? "com.android.hardware.gatekeeper.nonsecure"
210 : "com.android.hardware.gatekeeper.cf_remote";
211
212 bootconfig_args
213 ["androidboot.vendor.apex.com.android.hardware.graphics.composer"] =
214 instance.hwcomposer() == kHwComposerDrm
215 ? "com.android.hardware.graphics.composer.drm_hwcomposer"
216 : "com.android.hardware.graphics.composer.ranchu";
217
218 if (config.vhal_proxy_server_port()) {
219 bootconfig_args["androidboot.vhal_proxy_server_port"] =
220 std::to_string(config.vhal_proxy_server_port());
221 int32_t instance_id;
222 CF_EXPECT(android::base::ParseInt(instance.id(), &instance_id),
223 "instance id: " << instance.id() << " is not a valid int");
224 // The static ethernet IP address assigned for the guest.
225 bootconfig_args["androidboot.auto_eth_guest_addr"] =
226 fmt::format("192.168.98.{}", instance_id + 2);
227 }
228
229 if (!instance.vcpu_config_path().empty()) {
230 auto vcpu_config_json =
231 CF_EXPECT(LoadFromFile(instance.vcpu_config_path()));
232
233 const auto guest_soc =
234 CF_EXPECT(GetValue<std::string>(vcpu_config_json, {"guest_soc"}));
235
236 bootconfig_args["androidboot.guest_soc.model"] = guest_soc;
237 }
238
239 std::vector<std::string> args = instance.extra_bootconfig_args();
240
241 LOG(DEBUG) << "Parsing extra_bootconfig_args of size:" << args.size()
242 << "; Contents: " << android::base::Join(args, "\n");
243
244 for (const std::string& kv : args) {
245 if (kv.empty()) {
246 continue;
247 }
248 const auto& parts = android::base::Split(kv, "=");
249 CF_EXPECT_EQ(parts.size(), 2,
250 "Failed to parse --extra_bootconfig_args: \"" << kv << "\"");
251 bootconfig_args[parts[0]] = parts[1];
252 }
253
254 return bootconfig_args;
255 }
256
BootconfigArgsString(const std::unordered_map<std::string,std::string> & args,const std::string & separator)257 Result<std::string> BootconfigArgsString(
258 const std::unordered_map<std::string, std::string>& args,
259 const std::string& separator) {
260 std::vector<std::string> combined_args;
261 for (const auto& [k, v] : args) {
262 CF_EXPECT(!v.empty(), "Found empty bootconfig value for " << k);
263 combined_args.push_back(k + "=" + v);
264 }
265 return android::base::Join(combined_args, separator);
266 }
267
268 } // namespace cuttlefish
269