blob: 89e462f0ca017b58755045169bd90e48b9220a58 [file] [log] [blame]
/*
* 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 <string>
#include <vector>
#include <android-base/strings.h>
#include <android-base/logging.h>
#include "common/libs/utils/environment.h"
#include "common/libs/utils/network.h"
#include "common/libs/utils/subprocess.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/vm_manager/qemu_manager.h"
namespace vm_manager {
namespace {
std::string GetControlSocketPath(const vsoc::CuttlefishConfig* config) {
return config->ForDefaultInstance()
.PerInstanceInternalPath("crosvm_control.sock");
}
void AddTapFdParameter(cvd::Command* crosvm_cmd, const std::string& tap_name) {
auto tap_fd = cvd::OpenTapInterface(tap_name);
if (tap_fd->IsOpen()) {
crosvm_cmd->AddParameter("--tap-fd=", tap_fd);
} else {
LOG(ERROR) << "Unable to connect to " << tap_name << ": "
<< tap_fd->StrError();
}
}
bool Stop() {
auto config = vsoc::CuttlefishConfig::Get();
cvd::Command command(config->crosvm_binary());
command.AddParameter("stop");
command.AddParameter(GetControlSocketPath(config));
auto process = command.Start();
return process.Wait() == 0;
}
} // namespace
const std::string CrosvmManager::name() { return "crosvm"; }
std::vector<std::string> CrosvmManager::ConfigureGpu(const std::string& gpu_mode) {
// 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.
if (gpu_mode == vsoc::kGpuModeGuestSwiftshader) {
return {
"androidboot.hardware.gralloc=minigbm",
"androidboot.hardware.hwcomposer=cutf_hwc2",
"androidboot.hardware.egl=swiftshader",
"androidboot.hardware.vulkan=pastel",
};
}
// Try to load the Nvidia modeset kernel module. Running Crosvm with Nvidia's EGL library on a
// fresh machine after a boot will fail because the Nvidia EGL library will fork to run the
// nvidia-modprobe command and the main Crosvm process will abort after receiving the exit signal
// of the forked child which is interpreted as a failure.
cvd::Command modprobe_cmd("/usr/bin/nvidia-modprobe");
modprobe_cmd.AddParameter("--modeset");
modprobe_cmd.Start().Wait();
if (gpu_mode == vsoc::kGpuModeDrmVirgl) {
return {
"androidboot.hardware.gralloc=minigbm",
"androidboot.hardware.hwcomposer=drm_minigbm",
"androidboot.hardware.egl=mesa",
};
}
if (gpu_mode == vsoc::kGpuModeGfxStream) {
return {
"androidboot.hardware.gralloc=minigbm",
"androidboot.hardware.hwcomposer=drm_minigbm",
"androidboot.hardware.egl=emulation",
"androidboot.hardware.vulkan=ranchu",
"androidboot.hardware.gltransport=virtio-gpu-pipe",
};
}
return {};
}
std::vector<std::string> CrosvmManager::ConfigureBootDevices() {
// PCI domain 0, bus 0, device 1, function 0
// TODO There is no way to control this assignment with crosvm (yet)
if (cvd::HostArch() == "x86_64") {
return { "androidboot.boot_devices=pci0000:00/0000:00:01.0" };
} else {
return { "androidboot.boot_devices=10000.pci" };
}
}
CrosvmManager::CrosvmManager(const vsoc::CuttlefishConfig* config)
: VmManager(config) {}
std::vector<cvd::Command> CrosvmManager::StartCommands() {
auto instance = config_->ForDefaultInstance();
cvd::Command crosvm_cmd(config_->crosvm_binary(), [](cvd::Subprocess* proc) {
auto stopped = Stop();
if (stopped) {
return true;
}
LOG(WARNING) << "Failed to stop VMM nicely, attempting to KILL";
return KillSubprocess(proc);
});
crosvm_cmd.AddParameter("run");
auto gpu_mode = config_->gpu_mode();
if (gpu_mode == vsoc::kGpuModeGuestSwiftshader) {
crosvm_cmd.AddParameter("--gpu=2D,",
"width=", config_->x_res(), ",",
"height=", config_->y_res());
} else if (gpu_mode == vsoc::kGpuModeDrmVirgl ||
gpu_mode == vsoc::kGpuModeGfxStream) {
crosvm_cmd.AddParameter(gpu_mode == vsoc::kGpuModeGfxStream ?
"--gpu=gfxstream," : "--gpu=",
"width=", config_->x_res(), ",",
"height=", config_->y_res(), ",",
"egl=true,surfaceless=true,glx=false,gles=true");
crosvm_cmd.AddParameter("--wayland-sock=", instance.frames_socket_path());
}
if (!config_->final_ramdisk_path().empty()) {
crosvm_cmd.AddParameter("--initrd=", config_->final_ramdisk_path());
}
crosvm_cmd.AddParameter("--mem=", config_->memory_mb());
crosvm_cmd.AddParameter("--cpus=", config_->cpus());
crosvm_cmd.AddParameter("--params=", kernel_cmdline_);
for (const auto& disk : instance.virtual_disk_paths()) {
crosvm_cmd.AddParameter("--rwdisk=", disk);
}
crosvm_cmd.AddParameter("--socket=", GetControlSocketPath(config_));
if (frontend_enabled_) {
crosvm_cmd.AddParameter("--single-touch=", instance.touch_socket_path(),
":", config_->x_res(), ":", config_->y_res());
crosvm_cmd.AddParameter("--keyboard=", instance.keyboard_socket_path());
}
AddTapFdParameter(&crosvm_cmd, instance.wifi_tap_name());
AddTapFdParameter(&crosvm_cmd, instance.mobile_tap_name());
crosvm_cmd.AddParameter("--rw-pmem-device=", instance.access_kregistry_path());
// TODO remove this (use crosvm's seccomp files)
crosvm_cmd.AddParameter("--disable-sandbox");
if (instance.vsock_guest_cid() >= 2) {
crosvm_cmd.AddParameter("--cid=", instance.vsock_guest_cid());
}
// Redirect the first serial port with the kernel logs to the appropriate file
crosvm_cmd.AddParameter("--serial=num=1,type=file,path=",
instance.kernel_log_pipe_name(), ",console=true");
// Redirect standard input to a pipe for the console forwarder host process
// to handle.
cvd::SharedFD console_in_rd, console_in_wr;
if (!cvd::SharedFD::Pipe(&console_in_rd, &console_in_wr)) {
LOG(ERROR) << "Failed to create console pipe for crosvm's stdin: "
<< console_in_rd->StrError();
return {};
}
auto console_pipe_name = instance.console_pipe_name();
if (mkfifo(console_pipe_name.c_str(), 0660) != 0) {
auto error = errno;
LOG(ERROR) << "Failed to create console fifo for crosvm: "
<< strerror(error);
return {};
}
// This fd will only be read from, but it's open with write access as well to
// keep the pipe open in case the subprocesses exit.
cvd::SharedFD console_out_rd =
cvd::SharedFD::Open(console_pipe_name.c_str(), O_RDWR);
if (!console_out_rd->IsOpen()) {
LOG(ERROR) << "Failed to open console fifo for reads: "
<< console_out_rd->StrError();
return {};
}
// 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.
crosvm_cmd.AddParameter("--serial=num=2,type=file,path=", console_pipe_name,
",stdin=true");
crosvm_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdIn,
console_in_rd);
cvd::Command console_cmd(config_->console_forwarder_binary());
console_cmd.AddParameter("--console_in_fd=", console_in_wr);
console_cmd.AddParameter("--console_out_fd=", console_out_rd);
// This needs to be the last parameter
crosvm_cmd.AddParameter(config_->GetKernelImageToUse());
std::vector<cvd::Command> ret;
ret.push_back(std::move(crosvm_cmd));
ret.push_back(std::move(console_cmd));
return ret;
}
} // namespace vm_manager