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