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