518 lines
21 KiB
C++
518 lines
21 KiB
C++
|
|
/*
|
||
|
|
* Copyright (C) 2019 The Android Open Source Project
|
||
|
|
*
|
||
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
* you may not use this file except in compliance with the License.
|
||
|
|
* You may obtain a copy of the License at
|
||
|
|
*
|
||
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
*
|
||
|
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
* See the License for the specific language governing permissions and
|
||
|
|
* limitations under the License.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "host/libs/vm_manager/crosvm_manager.h"
|
||
|
|
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <sys/types.h>
|
||
|
|
|
||
|
|
#include <cassert>
|
||
|
|
#include <string>
|
||
|
|
#include <unordered_map>
|
||
|
|
#include <utility>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
#include <android-base/file.h>
|
||
|
|
#include <android-base/logging.h>
|
||
|
|
#include <android-base/strings.h>
|
||
|
|
#include <vulkan/vulkan.h>
|
||
|
|
|
||
|
|
#include "common/libs/utils/environment.h"
|
||
|
|
#include "common/libs/utils/files.h"
|
||
|
|
#include "common/libs/utils/network.h"
|
||
|
|
#include "common/libs/utils/result.h"
|
||
|
|
#include "common/libs/utils/subprocess.h"
|
||
|
|
#include "host/libs/config/cuttlefish_config.h"
|
||
|
|
#include "host/libs/config/known_paths.h"
|
||
|
|
#include "host/libs/vm_manager/crosvm_builder.h"
|
||
|
|
#include "host/libs/vm_manager/qemu_manager.h"
|
||
|
|
|
||
|
|
namespace cuttlefish {
|
||
|
|
namespace vm_manager {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
std::string GetControlSocketPath(
|
||
|
|
const CuttlefishConfig::InstanceSpecific& instance,
|
||
|
|
const std::string& socket_name) {
|
||
|
|
return instance.PerInstanceInternalUdsPath(socket_name.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
bool CrosvmManager::IsSupported() {
|
||
|
|
#ifdef __ANDROID__
|
||
|
|
return true;
|
||
|
|
#else
|
||
|
|
return HostSupportsQemuCli();
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<std::unordered_map<std::string, std::string>>
|
||
|
|
CrosvmManager::ConfigureGraphics(
|
||
|
|
const CuttlefishConfig::InstanceSpecific& instance) {
|
||
|
|
// Override the default HAL search paths in all cases. We do this because
|
||
|
|
// the HAL search path allows for fallbacks, and fallbacks in conjunction
|
||
|
|
// with properities lead to non-deterministic behavior while loading the
|
||
|
|
// HALs.
|
||
|
|
|
||
|
|
std::unordered_map<std::string, std::string> bootconfig_args;
|
||
|
|
|
||
|
|
if (instance.gpu_mode() == kGpuModeGuestSwiftshader) {
|
||
|
|
bootconfig_args = {
|
||
|
|
{"androidboot.cpuvulkan.version", std::to_string(VK_API_VERSION_1_2)},
|
||
|
|
{"androidboot.hardware.gralloc", "minigbm"},
|
||
|
|
{"androidboot.hardware.hwcomposer", instance.hwcomposer()},
|
||
|
|
{"androidboot.hardware.hwcomposer.display_finder_mode", "drm"},
|
||
|
|
{"androidboot.hardware.egl", "angle"},
|
||
|
|
{"androidboot.hardware.vulkan", "pastel"},
|
||
|
|
{"androidboot.opengles.version", "196609"}, // OpenGL ES 3.1
|
||
|
|
};
|
||
|
|
} else if (instance.gpu_mode() == kGpuModeDrmVirgl) {
|
||
|
|
bootconfig_args = {
|
||
|
|
{"androidboot.cpuvulkan.version", "0"},
|
||
|
|
{"androidboot.hardware.gralloc", "minigbm"},
|
||
|
|
{"androidboot.hardware.hwcomposer", "ranchu"},
|
||
|
|
{"androidboot.hardware.hwcomposer.mode", "client"},
|
||
|
|
{"androidboot.hardware.hwcomposer.display_finder_mode", "drm"},
|
||
|
|
{"androidboot.hardware.egl", "mesa"},
|
||
|
|
// No "hardware" Vulkan support, yet
|
||
|
|
{"androidboot.opengles.version", "196608"}, // OpenGL ES 3.0
|
||
|
|
};
|
||
|
|
} else if (instance.gpu_mode() == kGpuModeGfxstream ||
|
||
|
|
instance.gpu_mode() == kGpuModeGfxstreamGuestAngle) {
|
||
|
|
const bool uses_angle = instance.gpu_mode() == kGpuModeGfxstreamGuestAngle;
|
||
|
|
const std::string gles_impl = uses_angle ? "angle" : "emulation";
|
||
|
|
const std::string gltransport =
|
||
|
|
(instance.guest_android_version() == "11.0.0") ? "virtio-gpu-pipe"
|
||
|
|
: "virtio-gpu-asg";
|
||
|
|
bootconfig_args = {
|
||
|
|
{"androidboot.cpuvulkan.version", "0"},
|
||
|
|
{"androidboot.hardware.gralloc", "minigbm"},
|
||
|
|
{"androidboot.hardware.hwcomposer", instance.hwcomposer()},
|
||
|
|
{"androidboot.hardware.hwcomposer.display_finder_mode", "drm"},
|
||
|
|
{"androidboot.hardware.egl", gles_impl},
|
||
|
|
{"androidboot.hardware.vulkan", "ranchu"},
|
||
|
|
{"androidboot.hardware.gltransport", gltransport},
|
||
|
|
{"androidboot.opengles.version", "196609"}, // OpenGL ES 3.1
|
||
|
|
};
|
||
|
|
} else if (instance.gpu_mode() == kGpuModeNone) {
|
||
|
|
return {};
|
||
|
|
} else {
|
||
|
|
return CF_ERR("Unknown GPU mode " << instance.gpu_mode());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!instance.gpu_angle_feature_overrides_enabled().empty()) {
|
||
|
|
bootconfig_args["androidboot.hardware.angle_feature_overrides_enabled"] =
|
||
|
|
instance.gpu_angle_feature_overrides_enabled();
|
||
|
|
}
|
||
|
|
if (!instance.gpu_angle_feature_overrides_disabled().empty()) {
|
||
|
|
bootconfig_args["androidboot.hardware.angle_feature_overrides_disabled"] =
|
||
|
|
instance.gpu_angle_feature_overrides_disabled();
|
||
|
|
}
|
||
|
|
|
||
|
|
return bootconfig_args;
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<std::unordered_map<std::string, std::string>>
|
||
|
|
CrosvmManager::ConfigureBootDevices(int num_disks, bool have_gpu) {
|
||
|
|
// TODO There is no way to control this assignment with crosvm (yet)
|
||
|
|
if (HostArch() == Arch::X86_64) {
|
||
|
|
// crosvm has an additional PCI device for an ISA bridge
|
||
|
|
// virtio_gpu and virtio_wl precedes the first console or disk
|
||
|
|
return ConfigureMultipleBootDevices("pci0000:00/0000:00:",
|
||
|
|
1 + (have_gpu ? 2 : 0), num_disks);
|
||
|
|
} else {
|
||
|
|
// On ARM64 crosvm, block devices are on their own bridge, so we don't
|
||
|
|
// need to calculate it, and the path is always the same
|
||
|
|
return {{{"androidboot.boot_devices", "10000.pci"}}};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto crosvm_socket = "crosvm_control.sock";
|
||
|
|
|
||
|
|
Result<std::vector<MonitorCommand>> CrosvmManager::StartCommands(
|
||
|
|
const CuttlefishConfig& config) {
|
||
|
|
auto instance = config.ForDefaultInstance();
|
||
|
|
|
||
|
|
CrosvmBuilder crosvm_cmd;
|
||
|
|
|
||
|
|
crosvm_cmd.ApplyProcessRestarter(instance.crosvm_binary(),
|
||
|
|
kCrosvmVmResetExitCode);
|
||
|
|
crosvm_cmd.Cmd().AddParameter("run");
|
||
|
|
crosvm_cmd.AddControlSocket(GetControlSocketPath(instance, crosvm_socket),
|
||
|
|
instance.crosvm_binary());
|
||
|
|
if (!instance.smt()) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--no-smt");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (instance.vhost_net()) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--vhost-net");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (config.virtio_mac80211_hwsim() &&
|
||
|
|
!config.vhost_user_mac80211_hwsim().empty()) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--vhost-user-mac80211-hwsim=",
|
||
|
|
config.vhost_user_mac80211_hwsim());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (instance.protected_vm()) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--protected-vm");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (instance.gdb_port() > 0) {
|
||
|
|
CF_EXPECT(instance.cpus() == 1, "CPUs must be 1 for crosvm gdb mode");
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--gdb=", instance.gdb_port());
|
||
|
|
}
|
||
|
|
|
||
|
|
const auto gpu_capture_enabled = !instance.gpu_capture_binary().empty();
|
||
|
|
const auto gpu_mode = instance.gpu_mode();
|
||
|
|
|
||
|
|
const std::string gpu_angle_string =
|
||
|
|
gpu_mode == kGpuModeGfxstreamGuestAngle ? ",angle=true" : "";
|
||
|
|
// 256MB so it is small enough for a 32-bit kernel.
|
||
|
|
const std::string gpu_pci_bar_size = ",pci-bar-size=268435456";
|
||
|
|
const std::string gpu_udmabuf_string =
|
||
|
|
instance.enable_gpu_udmabuf() ? ",udmabuf=true" : "";
|
||
|
|
|
||
|
|
const std::string gpu_common_string = gpu_udmabuf_string + gpu_pci_bar_size;
|
||
|
|
const std::string gpu_common_3d_string =
|
||
|
|
gpu_common_string + ",egl=true,surfaceless=true,glx=false,gles=true";
|
||
|
|
|
||
|
|
if (gpu_mode == kGpuModeGuestSwiftshader) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--gpu=backend=2D", gpu_common_string);
|
||
|
|
} else if (gpu_mode == kGpuModeDrmVirgl) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--gpu=backend=virglrenderer",
|
||
|
|
gpu_common_3d_string);
|
||
|
|
} else if (gpu_mode == kGpuModeGfxstream ||
|
||
|
|
gpu_mode == kGpuModeGfxstreamGuestAngle) {
|
||
|
|
const std::string capset_names = ",context-types=gfxstream";
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--gpu=backend=gfxstream,gles31=true",
|
||
|
|
gpu_common_3d_string, gpu_angle_string,
|
||
|
|
capset_names);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (instance.hwcomposer() != kHwComposerNone) {
|
||
|
|
if (!instance.mte() && FileExists(instance.hwcomposer_pmem_path())) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--rw-pmem-device=",
|
||
|
|
instance.hwcomposer_pmem_path());
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const auto& display_config : instance.display_configs()) {
|
||
|
|
const auto display_w = std::to_string(display_config.width);
|
||
|
|
const auto display_h = std::to_string(display_config.height);
|
||
|
|
const auto display_dpi = std::to_string(display_config.dpi);
|
||
|
|
const auto display_rr = std::to_string(display_config.refresh_rate_hz);
|
||
|
|
const auto display_params = android::base::Join(
|
||
|
|
std::vector<std::string>{
|
||
|
|
"mode=windowed[" + display_w + "," + display_h + "]",
|
||
|
|
"dpi=[" + display_dpi + "," + display_dpi + "]",
|
||
|
|
"refresh-rate=" + display_rr,
|
||
|
|
},
|
||
|
|
",");
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--gpu-display=", display_params);
|
||
|
|
}
|
||
|
|
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--wayland-sock=",
|
||
|
|
instance.frames_socket_path());
|
||
|
|
}
|
||
|
|
|
||
|
|
// crosvm_cmd.Cmd().AddParameter("--null-audio");
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--mem=", instance.memory_mb());
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--cpus=", instance.cpus());
|
||
|
|
if (instance.mte()) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--mte");
|
||
|
|
}
|
||
|
|
|
||
|
|
auto disk_num = instance.virtual_disk_paths().size();
|
||
|
|
CF_EXPECT(VmManager::kMaxDisks >= disk_num,
|
||
|
|
"Provided too many disks (" << disk_num << "), maximum "
|
||
|
|
<< VmManager::kMaxDisks << "supported");
|
||
|
|
for (const auto& disk : instance.virtual_disk_paths()) {
|
||
|
|
if (instance.protected_vm()) {
|
||
|
|
crosvm_cmd.AddReadOnlyDisk(disk);
|
||
|
|
} else {
|
||
|
|
crosvm_cmd.AddReadWriteDisk(disk);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (instance.enable_webrtc()) {
|
||
|
|
auto touch_type_parameter =
|
||
|
|
instance.enable_webrtc() ? "--multi-touch=" : "--single-touch=";
|
||
|
|
|
||
|
|
auto display_configs = instance.display_configs();
|
||
|
|
CF_EXPECT(display_configs.size() >= 1);
|
||
|
|
|
||
|
|
for (int i = 0; i < display_configs.size(); ++i) {
|
||
|
|
auto display_config = display_configs[i];
|
||
|
|
|
||
|
|
crosvm_cmd.Cmd().AddParameter(
|
||
|
|
touch_type_parameter, instance.touch_socket_path(i), ":",
|
||
|
|
display_config.width, ":", display_config.height);
|
||
|
|
}
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--keyboard=",
|
||
|
|
instance.keyboard_socket_path());
|
||
|
|
}
|
||
|
|
if (instance.enable_webrtc()) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--switches=",
|
||
|
|
instance.switches_socket_path());
|
||
|
|
}
|
||
|
|
|
||
|
|
SharedFD wifi_tap;
|
||
|
|
// GPU capture can only support named files and not file descriptors due to
|
||
|
|
// having to pass arguments to crosvm via a wrapper script.
|
||
|
|
if (!gpu_capture_enabled) {
|
||
|
|
// The ordering of tap devices is important. Make sure any change here
|
||
|
|
// is reflected in ethprime u-boot variable
|
||
|
|
crosvm_cmd.AddTap(instance.mobile_tap_name(), instance.mobile_mac());
|
||
|
|
crosvm_cmd.AddTap(instance.ethernet_tap_name(), instance.ethernet_mac());
|
||
|
|
|
||
|
|
if (!config.virtio_mac80211_hwsim()) {
|
||
|
|
wifi_tap = crosvm_cmd.AddTap(instance.wifi_tap_name());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!instance.mte() && FileExists(instance.access_kregistry_path())) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--rw-pmem-device=",
|
||
|
|
instance.access_kregistry_path());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!instance.mte() && FileExists(instance.pstore_path())) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--pstore=path=", instance.pstore_path(),
|
||
|
|
",size=", FileSize(instance.pstore_path()));
|
||
|
|
}
|
||
|
|
|
||
|
|
if (instance.enable_sandbox()) {
|
||
|
|
const bool seccomp_exists = DirectoryExists(instance.seccomp_policy_dir());
|
||
|
|
const std::string& var_empty_dir = kCrosvmVarEmptyDir;
|
||
|
|
const bool var_empty_available = DirectoryExists(var_empty_dir);
|
||
|
|
CF_EXPECT(var_empty_available && seccomp_exists,
|
||
|
|
var_empty_dir << " is not an existing, empty directory."
|
||
|
|
<< "seccomp-policy-dir, "
|
||
|
|
<< instance.seccomp_policy_dir()
|
||
|
|
<< " does not exist");
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--seccomp-policy-dir=",
|
||
|
|
instance.seccomp_policy_dir());
|
||
|
|
} else {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--disable-sandbox");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (instance.vsock_guest_cid() >= 2) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--cid=", instance.vsock_guest_cid());
|
||
|
|
}
|
||
|
|
|
||
|
|
// If kernel log is enabled, the virtio-console port will be specified as
|
||
|
|
// a true console for Linux, and kernel messages will be printed there.
|
||
|
|
// Otherwise, the port will still be set up for bootloader and userspace
|
||
|
|
// messages, but the kernel will not print anything here. This keeps our
|
||
|
|
// kernel log event features working. If an alternative "earlycon" boot
|
||
|
|
// console is configured below on a legacy serial port, it will control
|
||
|
|
// the main log until the virtio-console takes over.
|
||
|
|
crosvm_cmd.AddHvcReadOnly(instance.kernel_log_pipe_name(),
|
||
|
|
instance.enable_kernel_log());
|
||
|
|
|
||
|
|
if (instance.console()) {
|
||
|
|
// stdin is the only currently supported way to write data to a serial port
|
||
|
|
// in crosvm. A file (named pipe) is used here instead of stdout to ensure
|
||
|
|
// only the serial port output is received by the console forwarder as
|
||
|
|
// crosvm may print other messages to stdout.
|
||
|
|
if (instance.kgdb() || instance.use_bootloader()) {
|
||
|
|
crosvm_cmd.AddSerialConsoleReadWrite(instance.console_out_pipe_name(),
|
||
|
|
instance.console_in_pipe_name(),
|
||
|
|
instance.enable_kernel_log());
|
||
|
|
// In kgdb mode, we have the interactive console on ttyS0 (both Android's
|
||
|
|
// console and kdb), so we can disable the virtio-console port usually
|
||
|
|
// allocated to Android's serial console, and redirect it to a sink. This
|
||
|
|
// ensures that that the PCI device assignments (and thus sepolicy) don't
|
||
|
|
// have to change
|
||
|
|
crosvm_cmd.AddHvcSink();
|
||
|
|
} else {
|
||
|
|
crosvm_cmd.AddSerialSink();
|
||
|
|
crosvm_cmd.AddHvcReadWrite(instance.console_out_pipe_name(),
|
||
|
|
instance.console_in_pipe_name());
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Use an 8250 UART (ISA or platform device) for earlycon, as the
|
||
|
|
// virtio-console driver may not be available for early messages
|
||
|
|
// In kgdb mode, earlycon is an interactive console, and so early
|
||
|
|
// dmesg will go there instead of the kernel.log
|
||
|
|
if (instance.enable_kernel_log() &&
|
||
|
|
(instance.kgdb() || instance.use_bootloader())) {
|
||
|
|
crosvm_cmd.AddSerialConsoleReadOnly(instance.kernel_log_pipe_name());
|
||
|
|
}
|
||
|
|
|
||
|
|
// as above, create a fake virtio-console 'sink' port when the serial
|
||
|
|
// console is disabled, so the PCI device ID assignments don't move
|
||
|
|
// around
|
||
|
|
crosvm_cmd.AddHvcSink();
|
||
|
|
}
|
||
|
|
|
||
|
|
auto crosvm_logs_path = instance.PerInstanceInternalPath("crosvm.fifo");
|
||
|
|
auto crosvm_logs = SharedFD::Fifo(crosvm_logs_path, 0666);
|
||
|
|
CF_EXPECT(crosvm_logs->IsOpen(),
|
||
|
|
"Failed to create log fifo for crosvm's stdout/stderr: "
|
||
|
|
<< crosvm_logs->StrError());
|
||
|
|
|
||
|
|
Command crosvm_log_tee_cmd(HostBinaryPath("log_tee"));
|
||
|
|
crosvm_log_tee_cmd.AddParameter("--process_name=crosvm");
|
||
|
|
crosvm_log_tee_cmd.AddParameter("--log_fd_in=", crosvm_logs);
|
||
|
|
crosvm_log_tee_cmd.SetStopper([](Subprocess* proc) {
|
||
|
|
// Ask nicely so that log_tee gets a chance to process all the logs.
|
||
|
|
int rval = kill(proc->pid(), SIGINT);
|
||
|
|
if (rval != 0) {
|
||
|
|
LOG(ERROR) << "Failed to stop log_tee nicely, attempting to KILL";
|
||
|
|
return KillSubprocess(proc) == StopperResult::kStopSuccess
|
||
|
|
? StopperResult::kStopCrash
|
||
|
|
: StopperResult::kStopFailure;
|
||
|
|
}
|
||
|
|
return StopperResult::kStopSuccess;
|
||
|
|
});
|
||
|
|
|
||
|
|
// Serial port for logcat, redirected to a pipe
|
||
|
|
crosvm_cmd.AddHvcReadOnly(instance.logcat_pipe_name());
|
||
|
|
|
||
|
|
crosvm_cmd.AddHvcReadWrite(
|
||
|
|
instance.PerInstanceInternalPath("keymaster_fifo_vm.out"),
|
||
|
|
instance.PerInstanceInternalPath("keymaster_fifo_vm.in"));
|
||
|
|
crosvm_cmd.AddHvcReadWrite(
|
||
|
|
instance.PerInstanceInternalPath("gatekeeper_fifo_vm.out"),
|
||
|
|
instance.PerInstanceInternalPath("gatekeeper_fifo_vm.in"));
|
||
|
|
|
||
|
|
if (config.enable_host_bluetooth()) {
|
||
|
|
crosvm_cmd.AddHvcReadWrite(
|
||
|
|
instance.PerInstanceInternalPath("bt_fifo_vm.out"),
|
||
|
|
instance.PerInstanceInternalPath("bt_fifo_vm.in"));
|
||
|
|
} else {
|
||
|
|
crosvm_cmd.AddHvcSink();
|
||
|
|
}
|
||
|
|
if (instance.enable_gnss_grpc_proxy()) {
|
||
|
|
crosvm_cmd.AddHvcReadWrite(
|
||
|
|
instance.PerInstanceInternalPath("gnsshvc_fifo_vm.out"),
|
||
|
|
instance.PerInstanceInternalPath("gnsshvc_fifo_vm.in"));
|
||
|
|
crosvm_cmd.AddHvcReadWrite(
|
||
|
|
instance.PerInstanceInternalPath("locationhvc_fifo_vm.out"),
|
||
|
|
instance.PerInstanceInternalPath("locationhvc_fifo_vm.in"));
|
||
|
|
} else {
|
||
|
|
for (auto i = 0; i < 2; i++) {
|
||
|
|
crosvm_cmd.AddHvcSink();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
crosvm_cmd.AddHvcReadWrite(
|
||
|
|
instance.PerInstanceInternalPath("confui_fifo_vm.out"),
|
||
|
|
instance.PerInstanceInternalPath("confui_fifo_vm.in"));
|
||
|
|
|
||
|
|
if (config.enable_host_uwb()) {
|
||
|
|
crosvm_cmd.AddHvcReadWrite(
|
||
|
|
instance.PerInstanceInternalPath("uwb_fifo_vm.out"),
|
||
|
|
instance.PerInstanceInternalPath("uwb_fifo_vm.in"));
|
||
|
|
} else {
|
||
|
|
crosvm_cmd.AddHvcSink();
|
||
|
|
}
|
||
|
|
|
||
|
|
for (auto i = 0; i < VmManager::kMaxDisks - disk_num; i++) {
|
||
|
|
crosvm_cmd.AddHvcSink();
|
||
|
|
}
|
||
|
|
CF_EXPECT(crosvm_cmd.HvcNum() + disk_num ==
|
||
|
|
VmManager::kMaxDisks + VmManager::kDefaultNumHvcs,
|
||
|
|
"HVC count (" << crosvm_cmd.HvcNum() << ") + disk count ("
|
||
|
|
<< disk_num << ") is not the expected total of "
|
||
|
|
<< VmManager::kMaxDisks + VmManager::kDefaultNumHvcs
|
||
|
|
<< " devices");
|
||
|
|
|
||
|
|
if (instance.enable_audio()) {
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--sound=", instance.audio_server_path());
|
||
|
|
}
|
||
|
|
|
||
|
|
// TODO(b/162071003): virtiofs crashes without sandboxing, this should be
|
||
|
|
// fixed
|
||
|
|
if (instance.enable_sandbox()) {
|
||
|
|
// Set up directory shared with virtiofs
|
||
|
|
crosvm_cmd.Cmd().AddParameter(
|
||
|
|
"--shared-dir=", instance.PerInstancePath(kSharedDirName),
|
||
|
|
":shared:type=fs");
|
||
|
|
}
|
||
|
|
|
||
|
|
// This needs to be the last parameter
|
||
|
|
crosvm_cmd.Cmd().AddParameter("--bios=", instance.bootloader());
|
||
|
|
|
||
|
|
// log_tee must be added before crosvm_cmd to ensure all of crosvm's logs are
|
||
|
|
// captured during shutdown. Processes are stopped in reverse order.
|
||
|
|
std::vector<MonitorCommand> commands;
|
||
|
|
commands.emplace_back(std::move(crosvm_log_tee_cmd));
|
||
|
|
|
||
|
|
if (gpu_capture_enabled) {
|
||
|
|
const std::string gpu_capture_basename =
|
||
|
|
cpp_basename(instance.gpu_capture_binary());
|
||
|
|
|
||
|
|
auto gpu_capture_logs_path =
|
||
|
|
instance.PerInstanceInternalPath("gpu_capture.fifo");
|
||
|
|
auto gpu_capture_logs = SharedFD::Fifo(gpu_capture_logs_path, 0666);
|
||
|
|
CF_EXPECT(gpu_capture_logs->IsOpen(),
|
||
|
|
"Failed to create log fifo for gpu capture's stdout/stderr: "
|
||
|
|
<< gpu_capture_logs->StrError());
|
||
|
|
|
||
|
|
Command gpu_capture_log_tee_cmd(HostBinaryPath("log_tee"));
|
||
|
|
gpu_capture_log_tee_cmd.AddParameter("--process_name=",
|
||
|
|
gpu_capture_basename);
|
||
|
|
gpu_capture_log_tee_cmd.AddParameter("--log_fd_in=", gpu_capture_logs);
|
||
|
|
|
||
|
|
Command gpu_capture_command(instance.gpu_capture_binary());
|
||
|
|
if (gpu_capture_basename == "ngfx") {
|
||
|
|
// Crosvm depends on command line arguments being passed as multiple
|
||
|
|
// arguments but ngfx only allows a single `--args`. To work around this,
|
||
|
|
// create a wrapper script that launches crosvm with all of the arguments
|
||
|
|
// and pass this wrapper script to ngfx.
|
||
|
|
const std::string crosvm_wrapper_path =
|
||
|
|
instance.PerInstanceInternalPath("crosvm_wrapper.sh");
|
||
|
|
const std::string crosvm_wrapper_content =
|
||
|
|
crosvm_cmd.Cmd().AsBashScript(crosvm_logs_path);
|
||
|
|
|
||
|
|
CF_EXPECT(android::base::WriteStringToFile(crosvm_wrapper_content,
|
||
|
|
crosvm_wrapper_path));
|
||
|
|
CF_EXPECT(MakeFileExecutable(crosvm_wrapper_path));
|
||
|
|
|
||
|
|
gpu_capture_command.AddParameter("--exe=", crosvm_wrapper_path);
|
||
|
|
gpu_capture_command.AddParameter("--launch-detached");
|
||
|
|
gpu_capture_command.AddParameter("--verbose");
|
||
|
|
gpu_capture_command.AddParameter("--activity=Frame Debugger");
|
||
|
|
} else {
|
||
|
|
// TODO(natsu): renderdoc
|
||
|
|
return CF_ERR(
|
||
|
|
"Unhandled GPU capture binary: " << instance.gpu_capture_binary());
|
||
|
|
}
|
||
|
|
|
||
|
|
gpu_capture_command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut,
|
||
|
|
gpu_capture_logs);
|
||
|
|
gpu_capture_command.RedirectStdIO(Subprocess::StdIOChannel::kStdErr,
|
||
|
|
gpu_capture_logs);
|
||
|
|
|
||
|
|
commands.emplace_back(std::move(gpu_capture_log_tee_cmd));
|
||
|
|
commands.emplace_back(std::move(gpu_capture_command));
|
||
|
|
} else {
|
||
|
|
crosvm_cmd.Cmd().RedirectStdIO(Subprocess::StdIOChannel::kStdOut,
|
||
|
|
crosvm_logs);
|
||
|
|
crosvm_cmd.Cmd().RedirectStdIO(Subprocess::StdIOChannel::kStdErr,
|
||
|
|
crosvm_logs);
|
||
|
|
commands.emplace_back(std::move(crosvm_cmd.Cmd()), true);
|
||
|
|
}
|
||
|
|
|
||
|
|
return commands;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace vm_manager
|
||
|
|
} // namespace cuttlefish
|