1 /*crosvm
2  * Copyright (C) 2019 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/libs/vm_manager/crosvm_manager.h"
18 
19 #include <poll.h>
20 #include <signal.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 
24 #include <cassert>
25 #include <map>
26 #include <string>
27 #include <unordered_map>
28 #include <utility>
29 #include <vector>
30 
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33 #include <android-base/strings.h>
34 #include <json/json.h>
35 #include <vulkan/vulkan.h>
36 
37 #include "common/libs/utils/environment.h"
38 #include "common/libs/utils/files.h"
39 #include "common/libs/utils/json.h"
40 #include "common/libs/utils/network.h"
41 #include "common/libs/utils/result.h"
42 #include "common/libs/utils/subprocess.h"
43 #include "host/libs/command_util/snapshot_utils.h"
44 #include "host/libs/config/cuttlefish_config.h"
45 #include "host/libs/config/known_paths.h"
46 #include "host/libs/vm_manager/crosvm_builder.h"
47 #include "host/libs/vm_manager/qemu_manager.h"
48 #include "host/libs/vm_manager/vhost_user.h"
49 
50 namespace cuttlefish {
51 namespace vm_manager {
52 
53 constexpr auto kTouchpadDefaultPrefix = "Crosvm_Virtio_Multitouch_Touchpad_";
54 
IsSupported()55 bool CrosvmManager::IsSupported() {
56 #ifdef __ANDROID__
57   return true;
58 #else
59   return HostSupportsQemuCli();
60 #endif
61 }
62 
63 Result<std::unordered_map<std::string, std::string>>
ConfigureGraphics(const CuttlefishConfig::InstanceSpecific & instance)64 CrosvmManager::ConfigureGraphics(
65     const CuttlefishConfig::InstanceSpecific& instance) {
66   // Override the default HAL search paths in all cases. We do this because
67   // the HAL search path allows for fallbacks, and fallbacks in conjunction
68   // with properties lead to non-deterministic behavior while loading the
69   // HALs.
70 
71   std::unordered_map<std::string, std::string> bootconfig_args;
72 
73   if (instance.gpu_mode() == kGpuModeGuestSwiftshader) {
74     bootconfig_args = {
75         {"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_3)},
76         {"androidboot.hardware.gralloc", "minigbm"},
77         {"androidboot.hardware.hwcomposer", instance.hwcomposer()},
78         {"androidboot.hardware.hwcomposer.display_finder_mode", "drm"},
79         {"androidboot.hardware.hwcomposer.display_framebuffer_format",
80          instance.guest_uses_bgra_framebuffers() ? "bgra" : "rgba"},
81         {"androidboot.hardware.egl", "angle"},
82         {"androidboot.hardware.vulkan", "pastel"},
83         {"androidboot.opengles.version", "196609"},  // OpenGL ES 3.1
84     };
85   } else if (instance.gpu_mode() == kGpuModeDrmVirgl) {
86     bootconfig_args = {
87         {"androidboot.cpuvulkan.version", "0"},
88         {"androidboot.hardware.gralloc", "minigbm"},
89         {"androidboot.hardware.hwcomposer", "ranchu"},
90         {"androidboot.hardware.hwcomposer.mode", "client"},
91         {"androidboot.hardware.hwcomposer.display_finder_mode", "drm"},
92         {"androidboot.hardware.hwcomposer.display_framebuffer_format",
93          instance.guest_uses_bgra_framebuffers() ? "bgra" : "rgba"},
94         {"androidboot.hardware.egl", "mesa"},
95         // No "hardware" Vulkan support, yet
96         {"androidboot.opengles.version", "196608"},  // OpenGL ES 3.0
97     };
98   } else if (instance.gpu_mode() == kGpuModeGfxstream ||
99              instance.gpu_mode() == kGpuModeGfxstreamGuestAngle ||
100              instance.gpu_mode() ==
101                  kGpuModeGfxstreamGuestAngleHostSwiftShader) {
102     const bool uses_angle =
103         instance.gpu_mode() == kGpuModeGfxstreamGuestAngle ||
104         instance.gpu_mode() == kGpuModeGfxstreamGuestAngleHostSwiftShader;
105 
106     const std::string gles_impl = uses_angle ? "angle" : "emulation";
107 
108     const std::string gfxstream_transport = instance.gpu_gfxstream_transport();
109     CF_EXPECT(gfxstream_transport == "virtio-gpu-asg" ||
110                   gfxstream_transport == "virtio-gpu-pipe",
111               "Invalid Gfxstream transport option: \"" << gfxstream_transport
112                                                        << "\"");
113 
114     bootconfig_args = {
115         {"androidboot.cpuvulkan.version", "0"},
116         {"androidboot.hardware.gralloc", "minigbm"},
117         {"androidboot.hardware.hwcomposer", instance.hwcomposer()},
118         {"androidboot.hardware.hwcomposer.display_finder_mode", "drm"},
119         {"androidboot.hardware.hwcomposer.display_framebuffer_format",
120          instance.guest_uses_bgra_framebuffers() ? "bgra" : "rgba"},
121         {"androidboot.hardware.egl", gles_impl},
122         {"androidboot.hardware.vulkan", "ranchu"},
123         {"androidboot.hardware.gltransport", gfxstream_transport},
124         {"androidboot.opengles.version", "196609"},  // OpenGL ES 3.1
125     };
126   } else if (instance.gpu_mode() == kGpuModeCustom) {
127     bootconfig_args = {
128         {"androidboot.cpuvulkan.version", "0"},
129         {"androidboot.hardware.gralloc", "minigbm"},
130         {"androidboot.hardware.hwcomposer", instance.hwcomposer()},
131         {"androidboot.hardware.hwcomposer.display_finder_mode", "drm"},
132         {"androidboot.hardware.hwcomposer.display_framebuffer_format",
133          instance.guest_uses_bgra_framebuffers() ? "bgra" : "rgba"},
134         {"androidboot.hardware.egl", "angle"},
135         {"androidboot.hardware.vulkan", instance.guest_vulkan_driver()},
136         {"androidboot.hardware.gltransport", "virtio-gpu-asg"},
137         {"androidboot.opengles.version", "196609"},  // OpenGL ES 3.1
138     };
139   } else if (instance.gpu_mode() == kGpuModeNone) {
140     return {};
141   } else {
142     return CF_ERR("Unknown GPU mode " << instance.gpu_mode());
143   }
144 
145   if (!instance.gpu_angle_feature_overrides_enabled().empty()) {
146     bootconfig_args["androidboot.hardware.angle_feature_overrides_enabled"] =
147         instance.gpu_angle_feature_overrides_enabled();
148   }
149   if (!instance.gpu_angle_feature_overrides_disabled().empty()) {
150     bootconfig_args["androidboot.hardware.angle_feature_overrides_disabled"] =
151         instance.gpu_angle_feature_overrides_disabled();
152   }
153 
154   return bootconfig_args;
155 }
156 
157 Result<std::unordered_map<std::string, std::string>>
ConfigureBootDevices(const CuttlefishConfig::InstanceSpecific & instance)158 CrosvmManager::ConfigureBootDevices(
159     const CuttlefishConfig::InstanceSpecific& instance) {
160   const int num_disks = instance.virtual_disk_paths().size();
161   const bool has_gpu = instance.hwcomposer() != kHwComposerNone;
162   // TODO There is no way to control this assignment with crosvm (yet)
163   if (HostArch() == Arch::X86_64) {
164     int num_gpu_pcis = has_gpu ? 1 : 0;
165     if (instance.gpu_mode() != kGpuModeNone &&
166         !instance.enable_gpu_vhost_user()) {
167       // crosvm has an additional PCI device for an ISA bridge when running
168       // with a gpu and without vhost user gpu.
169       num_gpu_pcis += 1;
170     }
171     // virtio_gpu and virtio_wl precedes the first console or disk
172     return ConfigureMultipleBootDevices("pci0000:00/0000:00:", 1 + num_gpu_pcis,
173                                         num_disks);
174   } else {
175     // On ARM64 crosvm, block devices are on their own bridge, so we don't
176     // need to calculate it, and the path is always the same
177     return {{{"androidboot.boot_devices", "10000.pci"}}};
178   }
179 }
180 
ToSingleLineString(const Json::Value & value)181 std::string ToSingleLineString(const Json::Value& value) {
182   Json::StreamWriterBuilder builder;
183   builder["indentation"] = "";
184   return Json::writeString(builder, value);
185 }
186 
HostSwiftShaderIcdPathForArch()187 Result<std::string> HostSwiftShaderIcdPathForArch() {
188   switch (HostArch()) {
189     case Arch::Arm64:
190       return HostBinaryPath("aarch64-linux-gnu/vk_swiftshader_icd.json");
191     case Arch::X86:
192     case Arch::X86_64:
193       return HostUsrSharePath("vulkan/icd.d/vk_swiftshader_icd.json");
194     default:
195       break;
196   }
197   return CF_ERR("Unhandled host arch " << HostArchStr()
198                                        << " for finding SwiftShader ICD.");
199 }
200 
MaybeConfigureVulkanIcd(const CuttlefishConfig & config,Command * command)201 Result<void> MaybeConfigureVulkanIcd(const CuttlefishConfig& config,
202                                      Command* command) {
203   const auto& gpu_mode = config.ForDefaultInstance().gpu_mode();
204   if (gpu_mode == kGpuModeGfxstreamGuestAngleHostSwiftShader) {
205     const std::string swiftshader_icd_json_path =
206         CF_EXPECT(HostSwiftShaderIcdPathForArch());
207 
208     // See https://github.com/KhronosGroup/Vulkan-Loader.
209     command->AddEnvironmentVariable("VK_DRIVER_FILES",
210                                     swiftshader_icd_json_path);
211     command->AddEnvironmentVariable("VK_ICD_FILENAMES",
212                                     swiftshader_icd_json_path);
213   }
214 
215   return {};
216 }
217 
CrosvmPathForVhostUserGpu(const CuttlefishConfig & config)218 Result<std::string> CrosvmPathForVhostUserGpu(const CuttlefishConfig& config) {
219   const auto& instance = config.ForDefaultInstance();
220   switch (HostArch()) {
221     case Arch::Arm64:
222       return HostBinaryPath("aarch64-linux-gnu/crosvm");
223     case Arch::X86:
224     case Arch::X86_64:
225       return instance.crosvm_binary();
226     default:
227       break;
228   }
229   return CF_ERR("Unhandled host arch " << HostArchStr()
230                                        << " for vhost user gpu crosvm");
231 }
232 
BuildVhostUserGpu(const CuttlefishConfig & config,Command * main_crosvm_cmd)233 Result<VhostUserDeviceCommands> BuildVhostUserGpu(
234     const CuttlefishConfig& config, Command* main_crosvm_cmd) {
235   const auto& instance = config.ForDefaultInstance();
236   if (!instance.enable_gpu_vhost_user()) {
237     return CF_ERR("Attempting to build vhost user gpu when not enabled?");
238   }
239 
240   auto gpu_device_socket_path =
241       instance.PerInstanceInternalUdsPath("vhost-user-gpu-socket");
242   auto gpu_device_logs_path =
243       instance.PerInstanceInternalPath("crosvm_vhost_user_gpu.fifo");
244   auto gpu_device_logs = CF_EXPECT(SharedFD::Fifo(gpu_device_logs_path, 0666));
245 
246   Command gpu_device_logs_cmd(HostBinaryPath("log_tee"));
247   gpu_device_logs_cmd.AddParameter("--process_name=crosvm_gpu");
248   gpu_device_logs_cmd.AddParameter("--log_fd_in=", gpu_device_logs);
249   gpu_device_logs_cmd.SetStopper(KillSubprocessFallback([](Subprocess* proc) {
250     // Ask nicely so that log_tee gets a chance to process all the logs.
251     // TODO: b/335934714 - Make sure the process actually exits
252     bool res = kill(proc->pid(), SIGINT) == 0;
253     return res ? StopperResult::kStopSuccess : StopperResult::kStopFailure;
254   }));
255 
256   const std::string crosvm_path = CF_EXPECT(CrosvmPathForVhostUserGpu(config));
257 
258   CrosvmBuilder gpu_device_cmd;
259 
260   // NOTE: The "main" crosvm process returns a kCrosvmVmResetExitCode when the
261   // guest exits but the "gpu" crosvm just exits cleanly with 0 after the "main"
262   // crosvm disconnects.
263   gpu_device_cmd.ApplyProcessRestarter(crosvm_path,
264                                        /*first_time_argument=*/"",
265                                        /*exit_code=*/0);
266 
267   gpu_device_cmd.Cmd().AddParameter("device");
268   gpu_device_cmd.Cmd().AddParameter("gpu");
269 
270   const auto& gpu_mode = instance.gpu_mode();
271   CF_EXPECT(
272       gpu_mode == kGpuModeGfxstream ||
273           gpu_mode == kGpuModeGfxstreamGuestAngle ||
274           gpu_mode == kGpuModeGfxstreamGuestAngleHostSwiftShader,
275       "GPU mode " << gpu_mode << " not yet supported with vhost user gpu.");
276 
277   const std::string gpu_pci_address =
278       fmt::format("00:{:0>2x}.0", VmManager::kGpuPciSlotNum);
279 
280   // Why does this need JSON instead of just following the normal flags style...
281   Json::Value gpu_params_json;
282   gpu_params_json["pci-address"] = gpu_pci_address;
283   if (gpu_mode == kGpuModeGfxstream) {
284     gpu_params_json["context-types"] = "gfxstream-gles:gfxstream-vulkan";
285     gpu_params_json["egl"] = true;
286     gpu_params_json["gles"] = true;
287   } else if (gpu_mode == kGpuModeGfxstreamGuestAngle ||
288              gpu_mode == kGpuModeGfxstreamGuestAngleHostSwiftShader) {
289     gpu_params_json["context-types"] = "gfxstream-vulkan";
290     gpu_params_json["egl"] = false;
291     gpu_params_json["gles"] = false;
292   }
293   gpu_params_json["glx"] = false;
294   gpu_params_json["surfaceless"] = true;
295   gpu_params_json["external-blob"] = instance.enable_gpu_external_blob();
296   gpu_params_json["system-blob"] = instance.enable_gpu_system_blob();
297   if (!instance.gpu_renderer_features().empty()) {
298     gpu_params_json["renderer-features"] = instance.gpu_renderer_features();
299   }
300 
301   if (instance.hwcomposer() != kHwComposerNone) {
302     // "displays": [
303     //   {
304     //    "mode": {
305     //      "windowed": [
306     //        720,
307     //        1280
308     //      ]
309     //    },
310     //    "dpi": [
311     //      320,
312     //      320
313     //    ],
314     //    "refresh-rate": 60
315     //   }
316     // ]
317     Json::Value displays(Json::arrayValue);
318     for (const auto& display_config : instance.display_configs()) {
319       Json::Value display_mode_windowed(Json::arrayValue);
320       display_mode_windowed[0] = display_config.width;
321       display_mode_windowed[1] = display_config.height;
322 
323       Json::Value display_mode;
324       display_mode["windowed"] = display_mode_windowed;
325 
326       Json::Value display_dpi(Json::arrayValue);
327       display_dpi[0] = display_config.dpi;
328       display_dpi[1] = display_config.dpi;
329 
330       Json::Value display;
331       display["mode"] = display_mode;
332       display["dpi"] = display_dpi;
333       display["refresh-rate"] = display_config.refresh_rate_hz;
334 
335       displays.append(display);
336     }
337     gpu_params_json["displays"] = displays;
338 
339     gpu_device_cmd.Cmd().AddParameter("--wayland-sock=",
340                                       instance.frames_socket_path());
341   }
342 
343   // Connect device to main crosvm:
344   gpu_device_cmd.Cmd().AddParameter("--socket=", gpu_device_socket_path);
345 
346   main_crosvm_cmd->AddParameter(
347       "--vhost-user=gpu,pci-address=", gpu_pci_address,
348       ",socket=", gpu_device_socket_path);
349 
350   gpu_device_cmd.Cmd().AddParameter("--params");
351   gpu_device_cmd.Cmd().AddParameter(ToSingleLineString(gpu_params_json));
352 
353   CF_EXPECT(MaybeConfigureVulkanIcd(config, &gpu_device_cmd.Cmd()));
354 
355   gpu_device_cmd.Cmd().RedirectStdIO(Subprocess::StdIOChannel::kStdOut,
356                                      gpu_device_logs);
357   gpu_device_cmd.Cmd().RedirectStdIO(Subprocess::StdIOChannel::kStdErr,
358                                      gpu_device_logs);
359 
360   return VhostUserDeviceCommands{
361       .device_cmd = std::move(gpu_device_cmd.Cmd()),
362       .device_logs_cmd = std::move(gpu_device_logs_cmd),
363       .socket_path = gpu_device_socket_path,
364   };
365 }
366 
ConfigureGpu(const CuttlefishConfig & config,Command * crosvm_cmd)367 Result<void> ConfigureGpu(const CuttlefishConfig& config, Command* crosvm_cmd) {
368   const auto& instance = config.ForDefaultInstance();
369   const auto& gpu_mode = instance.gpu_mode();
370 
371   const std::string gles_string =
372       gpu_mode == kGpuModeGfxstreamGuestAngle ||
373               gpu_mode == kGpuModeGfxstreamGuestAngleHostSwiftShader
374           ? ",gles=false"
375           : ",gles=true";
376 
377   // 256MB so it is small enough for a 32-bit kernel.
378   const bool target_is_32bit = instance.target_arch() == Arch::Arm ||
379                                instance.target_arch() == Arch::X86;
380   const std::string gpu_pci_bar_size =
381       target_is_32bit ? ",pci-bar-size=268435456" : "";
382 
383   const std::string gpu_udmabuf_string =
384       instance.enable_gpu_udmabuf() ? ",udmabuf=true" : "";
385 
386   const std::string gpu_renderer_features = instance.gpu_renderer_features();
387   const std::string gpu_renderer_features_param =
388       !gpu_renderer_features.empty()
389           ? ",renderer-features=\"" + gpu_renderer_features + "\""
390           : "";
391 
392   const std::string gpu_common_string =
393       fmt::format(",pci-address=00:{:0>2x}.0", VmManager::kGpuPciSlotNum) +
394       gpu_udmabuf_string + gpu_pci_bar_size;
395   const std::string gpu_common_3d_string =
396       gpu_common_string + ",egl=true,surfaceless=true,glx=false" + gles_string +
397       gpu_renderer_features_param;
398 
399   std::string gpu_displays_string = "";
400   if (instance.hwcomposer() != kHwComposerNone) {
401     std::vector<std::string> gpu_displays_strings;
402     for (const auto& display_config : instance.display_configs()) {
403       const auto display_w = std::to_string(display_config.width);
404       const auto display_h = std::to_string(display_config.height);
405       const auto display_dpi = std::to_string(display_config.dpi);
406       const auto display_rr = std::to_string(display_config.refresh_rate_hz);
407       gpu_displays_strings.push_back(android::base::Join(
408           std::vector<std::string>{
409               "mode=windowed[" + display_w + "," + display_h + "]",
410               "dpi=[" + display_dpi + "," + display_dpi + "]",
411               "refresh-rate=" + display_rr,
412           },
413           ","));
414     }
415     gpu_displays_string =
416         "displays=[[" + android::base::Join(gpu_displays_strings, "],[") + "]],";
417 
418     crosvm_cmd->AddParameter("--wayland-sock=", instance.frames_socket_path());
419   }
420 
421   if (gpu_mode == kGpuModeGuestSwiftshader) {
422     crosvm_cmd->AddParameter("--gpu=", gpu_displays_string, "backend=2D",
423                              gpu_common_string);
424   } else if (gpu_mode == kGpuModeDrmVirgl) {
425     crosvm_cmd->AddParameter("--gpu=", gpu_displays_string,
426                              "backend=virglrenderer,context-types=virgl2",
427                              gpu_common_3d_string);
428   } else if (gpu_mode == kGpuModeGfxstream) {
429     crosvm_cmd->AddParameter(
430         "--gpu=", gpu_displays_string,
431         "context-types=gfxstream-gles:gfxstream-vulkan:gfxstream-composer",
432         gpu_common_3d_string);
433   } else if (gpu_mode == kGpuModeGfxstreamGuestAngle ||
434              gpu_mode == kGpuModeGfxstreamGuestAngleHostSwiftShader) {
435     crosvm_cmd->AddParameter(
436         "--gpu=", gpu_displays_string,
437         "context-types=gfxstream-vulkan:gfxstream-composer",
438         gpu_common_3d_string);
439   } else if (gpu_mode == kGpuModeCustom) {
440     crosvm_cmd->AddParameter("--gpu=", gpu_displays_string,
441                              "context-types=" + instance.gpu_context_types(),
442                              gpu_common_string);
443   }
444 
445   CF_EXPECT(MaybeConfigureVulkanIcd(config, crosvm_cmd));
446 
447   return {};
448 }
449 
StartCommands(const CuttlefishConfig & config,std::vector<VmmDependencyCommand * > & dependencyCommands)450 Result<std::vector<MonitorCommand>> CrosvmManager::StartCommands(
451     const CuttlefishConfig& config,
452     std::vector<VmmDependencyCommand*>& dependencyCommands) {
453   auto instance = config.ForDefaultInstance();
454   auto environment = config.ForDefaultEnvironment();
455 
456   std::vector<MonitorCommand> commands;
457 
458   CrosvmBuilder crosvm_cmd;
459   crosvm_cmd.Cmd().AddPrerequisite([&dependencyCommands]() -> Result<void> {
460     for (auto dependencyCommand : dependencyCommands) {
461       CF_EXPECT(dependencyCommand->WaitForAvailability());
462     }
463 
464     return {};
465   });
466 
467   // Add "--restore_path=<guest snapshot directory>" if there is a snapshot
468   // path supplied.
469   //
470   // Use the process_restarter "-first_time_argument" flag to only do this for
471   // the first invocation. If the guest requests a restart, we don't want crosvm
472   // to restore again. It should reboot normally.
473   std::string first_time_argument;
474   if (IsRestoring(config)) {
475     const std::string snapshot_dir_path = config.snapshot_path();
476     auto meta_info_json = CF_EXPECT(LoadMetaJson(snapshot_dir_path));
477     const std::vector<std::string> selectors{kGuestSnapshotField,
478                                              instance.id()};
479     const auto guest_snapshot_dir_suffix =
480         CF_EXPECT(GetValue<std::string>(meta_info_json, selectors));
481     // guest_snapshot_dir_suffix is a relative to
482     // the snapshot_path
483     const auto restore_path = snapshot_dir_path + "/" +
484                               guest_snapshot_dir_suffix + "/" +
485                               kGuestSnapshotBase;
486     first_time_argument = "--restore=" + restore_path;
487   }
488 
489   crosvm_cmd.ApplyProcessRestarter(instance.crosvm_binary(),
490                                    first_time_argument, kCrosvmVmResetExitCode);
491   crosvm_cmd.Cmd().AddParameter("run");
492   crosvm_cmd.AddControlSocket(instance.CrosvmSocketPath(),
493                               instance.crosvm_binary());
494 
495   if (!instance.smt()) {
496     crosvm_cmd.Cmd().AddParameter("--no-smt");
497   }
498 
499   // Disable USB passthrough. It isn't needed for any key use cases and it is
500   // not compatible with crosvm suspend-resume support yet (b/266622743).
501   // TODO: Allow it to be turned back on using a flag.
502   if (!instance.enable_usb()) {
503     crosvm_cmd.Cmd().AddParameter("--no-usb");
504   }
505 
506   crosvm_cmd.Cmd().AddParameter("--core-scheduling=false");
507 
508   crosvm_cmd.Cmd().AddParameter("--vhost-user-connect-timeout-ms=", 30 * 1000);
509 
510   if (instance.vhost_net()) {
511     crosvm_cmd.Cmd().AddParameter("--vhost-net");
512   }
513 
514   if (config.virtio_mac80211_hwsim() &&
515       !environment.vhost_user_mac80211_hwsim().empty()) {
516     crosvm_cmd.Cmd().AddParameter("--vhost-user=mac80211-hwsim,socket=",
517                                   environment.vhost_user_mac80211_hwsim());
518   }
519 
520   if (instance.protected_vm()) {
521     crosvm_cmd.Cmd().AddParameter("--protected-vm");
522   }
523 
524   if (!instance.crosvm_use_balloon()) {
525     crosvm_cmd.Cmd().AddParameter("--no-balloon");
526   }
527 
528   if (!instance.crosvm_use_rng()) {
529     crosvm_cmd.Cmd().AddParameter("--no-rng");
530   }
531 
532   if (instance.gdb_port() > 0) {
533     CF_EXPECT(instance.cpus() == 1, "CPUs must be 1 for crosvm gdb mode");
534     crosvm_cmd.Cmd().AddParameter("--gdb=", instance.gdb_port());
535   }
536 
537   std::optional<VhostUserDeviceCommands> vhost_user_gpu;
538   if (instance.enable_gpu_vhost_user()) {
539     vhost_user_gpu.emplace(
540         CF_EXPECT(BuildVhostUserGpu(config, &crosvm_cmd.Cmd())));
541   } else {
542     CF_EXPECT(ConfigureGpu(config, &crosvm_cmd.Cmd()));
543   }
544 
545   if (instance.hwcomposer() != kHwComposerNone) {
546     const bool pmem_disabled = instance.mte() || !instance.use_pmem();
547     if (!pmem_disabled && FileExists(instance.hwcomposer_pmem_path())) {
548       crosvm_cmd.Cmd().AddParameter("--pmem=path=",
549                                     instance.hwcomposer_pmem_path());
550     }
551   }
552 
553   const auto gpu_capture_enabled = !instance.gpu_capture_binary().empty();
554 
555   // crosvm_cmd.Cmd().AddParameter("--null-audio");
556   crosvm_cmd.Cmd().AddParameter("--mem=", instance.memory_mb());
557   if (instance.mte()) {
558     crosvm_cmd.Cmd().AddParameter("--mte");
559   }
560 
561   CF_EXPECT(crosvm_cmd.AddCpus(instance.cpus(), instance.vcpu_config_path()));
562 
563   auto disk_num = instance.virtual_disk_paths().size();
564   CF_EXPECT(VmManager::kMaxDisks >= disk_num,
565             "Provided too many disks (" << disk_num << "), maximum "
566                                         << VmManager::kMaxDisks << "supported");
567   size_t disk_i = 0;
568   for (const auto& disk : instance.virtual_disk_paths()) {
569     if (instance.protected_vm()) {
570       crosvm_cmd.AddReadOnlyDisk(disk);
571     } else if (instance.vhost_user_block() && disk_i == 2) {
572       // TODO: b/346855591 - Run on all devices
573       auto block = CF_EXPECT(VhostUserBlockDevice(config, disk_i, disk));
574       commands.emplace_back(std::move(block.device_cmd));
575       commands.emplace_back(std::move(block.device_logs_cmd));
576       auto socket_path = std::move(block.socket_path);
577       crosvm_cmd.Cmd().AddPrerequisite([socket_path]() -> Result<void> {
578 #ifdef __linux__
579         return WaitForUnixSocketListeningWithoutConnect(socket_path,
580                                                         /*timeoutSec=*/30);
581 #else
582         return CF_ERR("Unhandled check if vhost user block ready.");
583 #endif
584       });
585       auto pci_addr = fmt::format("00:{:0>2x}.0", 0x13 + disk_i);
586       crosvm_cmd.Cmd().AddParameter("--vhost-user=block,socket=", socket_path,
587                                     ",pci-address=", pci_addr);
588     } else {
589       crosvm_cmd.AddReadWriteDisk(disk);
590     }
591     disk_i++;
592   }
593 
594   if (instance.enable_webrtc()) {
595     bool is_chromeos =
596         instance.boot_flow() ==
597             CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOs ||
598         instance.boot_flow() ==
599             CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOsDisk;
600     auto touch_type_parameter =
601         is_chromeos ? "single-touch" : "multi-touch";
602 
603     auto display_configs = instance.display_configs();
604     CF_EXPECT(display_configs.size() >= 1);
605 
606     int touch_idx = 0;
607     for (auto& display_config : display_configs) {
608       crosvm_cmd.Cmd().AddParameter(
609           "--input=", touch_type_parameter, "[path=",
610           instance.touch_socket_path(touch_idx++),
611           ",width=", display_config.width,
612           ",height=", display_config.height, "]");
613     }
614     auto touchpad_configs = instance.touchpad_configs();
615     for (int i = 0; i < touchpad_configs.size(); ++i) {
616       auto touchpad_config = touchpad_configs[i];
617       crosvm_cmd.Cmd().AddParameter(
618           "--input=", touch_type_parameter, "[path=",
619           instance.touch_socket_path(touch_idx++),
620           ",width=", touchpad_config.width,
621           ",height=", touchpad_config.height,
622           ",name=", kTouchpadDefaultPrefix, i, "]");
623     }
624     if (instance.enable_mouse()) {
625       crosvm_cmd.Cmd().AddParameter(
626           "--input=mouse[path=", instance.mouse_socket_path(), "]");
627     }
628     crosvm_cmd.Cmd().AddParameter("--input=rotary[path=",
629                                   instance.rotary_socket_path(), "]");
630     crosvm_cmd.Cmd().AddParameter("--input=keyboard[path=",
631                                   instance.keyboard_socket_path(), "]");
632     crosvm_cmd.Cmd().AddParameter("--input=switches[path=",
633                                   instance.switches_socket_path(), "]");
634   }
635 
636   // GPU capture can only support named files and not file descriptors due to
637   // having to pass arguments to crosvm via a wrapper script.
638 #ifdef __linux__
639   if (!gpu_capture_enabled) {
640     // The PCI ordering of tap devices is important. Make sure any change here
641     // is reflected in ethprime u-boot variable.
642     // TODO(b/218364216, b/322862402): Crosvm occupies 32 PCI devices first and only then uses PCI
643     // functions which may break order. The final solution is going to be a PCI allocation strategy
644     // that will guarantee the ordering. For now, hardcode PCI network devices to unoccupied
645     // functions.
646     const pci::Address mobile_pci = pci::Address(0, VmManager::kNetPciDeviceNum, 1);
647     const pci::Address ethernet_pci = pci::Address(0, VmManager::kNetPciDeviceNum, 2);
648     crosvm_cmd.AddTap(instance.mobile_tap_name(), instance.mobile_mac(), mobile_pci);
649     crosvm_cmd.AddTap(instance.ethernet_tap_name(), instance.ethernet_mac(), ethernet_pci);
650 
651     if (!config.virtio_mac80211_hwsim() && environment.enable_wifi()) {
652       crosvm_cmd.AddTap(instance.wifi_tap_name());
653     }
654   }
655 #endif
656 
657   const bool pmem_disabled = instance.mte() || !instance.use_pmem();
658   if (!pmem_disabled && FileExists(instance.access_kregistry_path())) {
659     crosvm_cmd.Cmd().AddParameter("--pmem=path=",
660                                   instance.access_kregistry_path());
661   }
662 
663   if (!pmem_disabled && FileExists(instance.pstore_path())) {
664     crosvm_cmd.Cmd().AddParameter("--pstore=path=", instance.pstore_path(),
665                                   ",size=", FileSize(instance.pstore_path()));
666   }
667 
668   if (instance.enable_sandbox()) {
669     const bool seccomp_exists = DirectoryExists(instance.seccomp_policy_dir());
670     const std::string& var_empty_dir = kCrosvmVarEmptyDir;
671     const bool var_empty_available = DirectoryExists(var_empty_dir);
672     CF_EXPECT(var_empty_available && seccomp_exists,
673               var_empty_dir << " is not an existing, empty directory."
674                             << "seccomp-policy-dir, "
675                             << instance.seccomp_policy_dir()
676                             << " does not exist");
677     crosvm_cmd.Cmd().AddParameter("--seccomp-policy-dir=",
678                                   instance.seccomp_policy_dir());
679   } else {
680     crosvm_cmd.Cmd().AddParameter("--disable-sandbox");
681   }
682 
683   if (instance.vsock_guest_cid() >= 2) {
684     if (instance.vhost_user_vsock()) {
685       auto param =
686           fmt::format("/tmp/vsock_{}_{}/vhost.socket,max-queue-size=256",
687                       instance.vsock_guest_cid(), std::to_string(getuid()));
688       crosvm_cmd.Cmd().AddParameter("--vhost-user=vsock,socket=", param);
689     } else {
690       crosvm_cmd.Cmd().AddParameter("--cid=", instance.vsock_guest_cid());
691     }
692   }
693 
694   // /dev/hvc0 = kernel console
695   // If kernel log is enabled, the virtio-console port will be specified as
696   // a true console for Linux, and kernel messages will be printed there.
697   // Otherwise, the port will still be set up for bootloader and userspace
698   // messages, but the kernel will not print anything here. This keeps our
699   // kernel log event features working. If an alternative "earlycon" boot
700   // console is configured below on a legacy serial port, it will control
701   // the main log until the virtio-console takes over.
702   crosvm_cmd.AddHvcReadOnly(instance.kernel_log_pipe_name(),
703                             instance.enable_kernel_log());
704 
705   // /dev/hvc1 = serial console
706   if (instance.console()) {
707     // stdin is the only currently supported way to write data to a serial port
708     // in crosvm. A file (named pipe) is used here instead of stdout to ensure
709     // only the serial port output is received by the console forwarder as
710     // crosvm may print other messages to stdout.
711     if (instance.kgdb() || instance.use_bootloader()) {
712       crosvm_cmd.AddSerialConsoleReadWrite(instance.console_out_pipe_name(),
713                                            instance.console_in_pipe_name(),
714                                            instance.enable_kernel_log());
715       // In kgdb mode, we have the interactive console on ttyS0 (both Android's
716       // console and kdb), so we can disable the virtio-console port usually
717       // allocated to Android's serial console, and redirect it to a sink. This
718       // ensures that that the PCI device assignments (and thus sepolicy) don't
719       // have to change
720       crosvm_cmd.AddHvcSink();
721     } else {
722       crosvm_cmd.AddSerialSink();
723       crosvm_cmd.AddHvcReadWrite(instance.console_out_pipe_name(),
724                                  instance.console_in_pipe_name());
725     }
726   } else {
727     // Use an 8250 UART (ISA or platform device) for earlycon, as the
728     // virtio-console driver may not be available for early messages
729     // In kgdb mode, earlycon is an interactive console, and so early
730     // dmesg will go there instead of the kernel.log
731     if (instance.enable_kernel_log() &&
732         (instance.kgdb() || instance.use_bootloader())) {
733       crosvm_cmd.AddSerialConsoleReadOnly(instance.kernel_log_pipe_name());
734     }
735 
736     // as above, create a fake virtio-console 'sink' port when the serial
737     // console is disabled, so the PCI device ID assignments don't move
738     // around
739     crosvm_cmd.AddHvcSink();
740   }
741 
742   auto crosvm_logs_path = instance.PerInstanceInternalPath("crosvm.fifo");
743   auto crosvm_logs = CF_EXPECT(SharedFD::Fifo(crosvm_logs_path, 0666));
744 
745   Command crosvm_log_tee_cmd(HostBinaryPath("log_tee"));
746   crosvm_log_tee_cmd.AddParameter("--process_name=crosvm");
747   crosvm_log_tee_cmd.AddParameter("--log_fd_in=", crosvm_logs);
748   crosvm_log_tee_cmd.SetStopper(KillSubprocessFallback([](Subprocess* proc) {
749     // Ask nicely so that log_tee gets a chance to process all the logs.
750     bool res = kill(proc->pid(), SIGINT) == 0;
751     // TODO: b/335934714 - Make sure the process actually exits
752     return res ? StopperResult::kStopSuccess : StopperResult::kStopFailure;
753   }));
754 
755   // /dev/hvc2 = serial logging
756   // Serial port for logcat, redirected to a pipe
757   crosvm_cmd.AddHvcReadOnly(instance.logcat_pipe_name());
758 
759   // /dev/hvc3 = keymaster (C++ implementation)
760   crosvm_cmd.AddHvcReadWrite(
761       instance.PerInstanceInternalPath("keymaster_fifo_vm.out"),
762       instance.PerInstanceInternalPath("keymaster_fifo_vm.in"));
763   // /dev/hvc4 = gatekeeper
764   crosvm_cmd.AddHvcReadWrite(
765       instance.PerInstanceInternalPath("gatekeeper_fifo_vm.out"),
766       instance.PerInstanceInternalPath("gatekeeper_fifo_vm.in"));
767 
768   // /dev/hvc5 = bt
769   if (config.enable_host_bluetooth()) {
770     crosvm_cmd.AddHvcReadWrite(
771         instance.PerInstanceInternalPath("bt_fifo_vm.out"),
772         instance.PerInstanceInternalPath("bt_fifo_vm.in"));
773   } else {
774     crosvm_cmd.AddHvcSink();
775   }
776 
777   // /dev/hvc6 = gnss
778   // /dev/hvc7 = location
779   if (instance.enable_gnss_grpc_proxy()) {
780     crosvm_cmd.AddHvcReadWrite(
781         instance.PerInstanceInternalPath("gnsshvc_fifo_vm.out"),
782         instance.PerInstanceInternalPath("gnsshvc_fifo_vm.in"));
783     crosvm_cmd.AddHvcReadWrite(
784         instance.PerInstanceInternalPath("locationhvc_fifo_vm.out"),
785         instance.PerInstanceInternalPath("locationhvc_fifo_vm.in"));
786   } else {
787     for (auto i = 0; i < 2; i++) {
788       crosvm_cmd.AddHvcSink();
789     }
790   }
791 
792   // /dev/hvc8 = confirmationui
793   crosvm_cmd.AddHvcReadWrite(
794       instance.PerInstanceInternalPath("confui_fifo_vm.out"),
795       instance.PerInstanceInternalPath("confui_fifo_vm.in"));
796 
797   // /dev/hvc9 = uwb
798   if (config.enable_host_uwb()) {
799     crosvm_cmd.AddHvcReadWrite(
800         instance.PerInstanceInternalPath("uwb_fifo_vm.out"),
801         instance.PerInstanceInternalPath("uwb_fifo_vm.in"));
802   } else {
803     crosvm_cmd.AddHvcSink();
804   }
805 
806   // /dev/hvc10 = oemlock
807   crosvm_cmd.AddHvcReadWrite(
808       instance.PerInstanceInternalPath("oemlock_fifo_vm.out"),
809       instance.PerInstanceInternalPath("oemlock_fifo_vm.in"));
810 
811   // /dev/hvc11 = keymint (Rust implementation)
812   crosvm_cmd.AddHvcReadWrite(
813       instance.PerInstanceInternalPath("keymint_fifo_vm.out"),
814       instance.PerInstanceInternalPath("keymint_fifo_vm.in"));
815 
816   // /dev/hvc12 = NFC
817   if (config.enable_host_nfc()) {
818     crosvm_cmd.AddHvcReadWrite(
819         instance.PerInstanceInternalPath("nfc_fifo_vm.out"),
820         instance.PerInstanceInternalPath("nfc_fifo_vm.in"));
821   } else {
822     crosvm_cmd.AddHvcSink();
823   }
824 
825 
826   // /dev/hvc13 = sensors
827   crosvm_cmd.AddHvcReadWrite(
828       instance.PerInstanceInternalPath("sensors_fifo_vm.out"),
829       instance.PerInstanceInternalPath("sensors_fifo_vm.in"));
830 
831   // /dev/hvc14 = MCU CONTROL
832   if (instance.mcu()["control"]["type"].asString() == "serial") {
833     auto path = instance.PerInstanceInternalPath("mcu");
834     path += "/" + instance.mcu()["control"]["path"].asString();
835     crosvm_cmd.AddHvcReadWrite(path, path);
836   } else {
837     crosvm_cmd.AddHvcSink();
838   }
839 
840   // /dev/hvc15 = MCU UART
841   if (instance.mcu()["uart0"]["type"].asString() == "serial") {
842     auto path = instance.PerInstanceInternalPath("mcu");
843     path += "/" + instance.mcu()["uart0"]["path"].asString();
844     crosvm_cmd.AddHvcReadWrite(path, path);
845   } else {
846     crosvm_cmd.AddHvcSink();
847   }
848 
849   for (auto i = 0; i < VmManager::kMaxDisks - disk_num; i++) {
850     crosvm_cmd.AddHvcSink();
851   }
852   CF_EXPECT(crosvm_cmd.HvcNum() + disk_num ==
853                 VmManager::kMaxDisks + VmManager::kDefaultNumHvcs,
854             "HVC count (" << crosvm_cmd.HvcNum() << ") + disk count ("
855                           << disk_num << ") is not the expected total of "
856                           << VmManager::kMaxDisks + VmManager::kDefaultNumHvcs
857                           << " devices");
858 
859   if (instance.enable_audio()) {
860     crosvm_cmd.Cmd().AddParameter("--sound=", instance.audio_server_path());
861   }
862 
863   // TODO(b/162071003): virtiofs crashes without sandboxing, this should be
864   // fixed
865   if (instance.enable_virtiofs()) {
866     CF_EXPECT(instance.enable_sandbox(),
867               "virtiofs is currently not supported without sandboxing");
868     // Set up directory shared with virtiofs, setting security_ctx option to
869     // false prevents host error when unable to write data in the
870     // /proc/thread-self/attr/fscreate file.
871     crosvm_cmd.Cmd().AddParameter(
872         "--shared-dir=", instance.PerInstancePath(kSharedDirName),
873         ":shared:type=fs:security_ctx=false");
874   }
875 
876   if (instance.target_arch() == Arch::X86_64) {
877     crosvm_cmd.Cmd().AddParameter("--pflash=", instance.pflash_path());
878   }
879 
880   // This needs to be the last parameter
881   crosvm_cmd.Cmd().AddParameter("--bios=", instance.bootloader());
882 
883   if (vhost_user_gpu) {
884     // The vhost user gpu crosvm command should be added before the main
885     // crosvm command so that the main crosvm command can use a prerequisite
886     // to wait for the communication socket to be ready.
887     commands.emplace_back(std::move(vhost_user_gpu->device_cmd));
888     commands.emplace_back(std::move(vhost_user_gpu->device_logs_cmd));
889   }
890 
891   // log_tee must be added before crosvm_cmd to ensure all of crosvm's logs are
892   // captured during shutdown. Processes are stopped in reverse order.
893   commands.emplace_back(std::move(crosvm_log_tee_cmd));
894 
895   if (gpu_capture_enabled) {
896     const std::string gpu_capture_basename =
897         android::base::Basename(instance.gpu_capture_binary());
898 
899     auto gpu_capture_logs_path =
900         instance.PerInstanceInternalPath("gpu_capture.fifo");
901     auto gpu_capture_logs =
902         CF_EXPECT(SharedFD::Fifo(gpu_capture_logs_path, 0666));
903 
904     Command gpu_capture_log_tee_cmd(HostBinaryPath("log_tee"));
905     gpu_capture_log_tee_cmd.AddParameter("--process_name=",
906                                          gpu_capture_basename);
907     gpu_capture_log_tee_cmd.AddParameter("--log_fd_in=", gpu_capture_logs);
908 
909     Command gpu_capture_command(instance.gpu_capture_binary());
910     if (gpu_capture_basename == "ngfx") {
911       // Crosvm depends on command line arguments being passed as multiple
912       // arguments but ngfx only allows a single `--args`. To work around this,
913       // create a wrapper script that launches crosvm with all of the arguments
914       // and pass this wrapper script to ngfx.
915       const std::string crosvm_wrapper_path =
916           instance.PerInstanceInternalPath("crosvm_wrapper.sh");
917       const std::string crosvm_wrapper_content =
918           crosvm_cmd.Cmd().AsBashScript(crosvm_logs_path);
919 
920       CF_EXPECT(android::base::WriteStringToFile(crosvm_wrapper_content,
921                                                  crosvm_wrapper_path));
922       CF_EXPECT(MakeFileExecutable(crosvm_wrapper_path));
923 
924       gpu_capture_command.AddParameter("--exe=", crosvm_wrapper_path);
925       gpu_capture_command.AddParameter("--launch-detached");
926       gpu_capture_command.AddParameter("--verbose");
927       gpu_capture_command.AddParameter("--activity=Frame Debugger");
928     } else {
929       // TODO(natsu): renderdoc
930       return CF_ERR(
931           "Unhandled GPU capture binary: " << instance.gpu_capture_binary());
932     }
933 
934     gpu_capture_command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut,
935                                       gpu_capture_logs);
936     gpu_capture_command.RedirectStdIO(Subprocess::StdIOChannel::kStdErr,
937                                       gpu_capture_logs);
938 
939     commands.emplace_back(std::move(gpu_capture_log_tee_cmd));
940     commands.emplace_back(std::move(gpu_capture_command));
941   } else {
942     crosvm_cmd.Cmd().RedirectStdIO(Subprocess::StdIOChannel::kStdOut,
943                                    crosvm_logs);
944     crosvm_cmd.Cmd().RedirectStdIO(Subprocess::StdIOChannel::kStdErr,
945                                    crosvm_logs);
946     commands.emplace_back(std::move(crosvm_cmd.Cmd()), true);
947   }
948 
949   return commands;
950 }
951 
WaitForRestoreComplete(SharedFD stop_fd) const952 Result<bool> CrosvmManager::WaitForRestoreComplete(SharedFD stop_fd) const {
953   auto instance = CF_EXPECT(CuttlefishConfig::Get())->ForDefaultInstance();
954 
955   // Wait for the control socket to exist. It is created early in crosvm's
956   // startup sequence, but the process may not even have been exec'd by CF at
957   // this point.
958   while (!FileExists(instance.CrosvmSocketPath())) {
959     std::vector<PollSharedFd> poll = {{.fd = stop_fd, .events = POLLIN}};
960     const int result = SharedFD::Poll(poll, 50 /* ms */);
961     // Check for errors.
962     CF_EXPECT(result >= 0, "failed to wait on stop_fd: " << strerror(errno));
963     // Check if pipe became readable or closed.
964     if (result > 0) {
965       return false;
966     }
967   }
968 
969   // Ask crosvm to resume the VM. crosvm promises to not complete this command
970   // until the vCPUs are started (even if it was never suspended to begin
971   // with).
972   auto infop = CF_EXPECT(Execute(
973       std::vector<std::string>{
974           instance.crosvm_binary(),
975           "resume",
976           instance.CrosvmSocketPath(),
977           "--full",
978       },
979       SubprocessOptions(), WEXITED));
980   CF_EXPECT_EQ(infop.si_code, CLD_EXITED);
981   CF_EXPECTF(infop.si_status == 0, "crosvm resume returns non zero code {}",
982              infop.si_status);
983   return true;
984 }
985 
986 }  // namespace vm_manager
987 }  // namespace cuttlefish
988