/* * Copyright (C) 2018 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/qemu_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/libs/fs/shared_select.h" #include "common/libs/utils/files.h" #include "common/libs/utils/subprocess.h" #include "common/libs/utils/users.h" #include "host/libs/config/cuttlefish_config.h" namespace vm_manager { namespace { std::string GetMonitorPath(const vsoc::CuttlefishConfig* config) { return config->ForDefaultInstance() .PerInstanceInternalPath("qemu_monitor.sock"); } void LogAndSetEnv(const char* key, const std::string& value) { setenv(key, value.c_str(), 1); LOG(INFO) << key << "=" << value; } bool Stop() { auto config = vsoc::CuttlefishConfig::Get(); auto monitor_path = GetMonitorPath(config); auto monitor_sock = cvd::SharedFD::SocketLocalClient( monitor_path.c_str(), false, SOCK_STREAM); if (!monitor_sock->IsOpen()) { LOG(ERROR) << "The connection to qemu is closed, is it still running?"; return false; } char msg[] = "{\"execute\":\"qmp_capabilities\"}{\"execute\":\"quit\"}"; ssize_t len = sizeof(msg) - 1; while (len > 0) { int tmp = monitor_sock->Write(msg, len); if (tmp < 0) { LOG(ERROR) << "Error writing to socket: " << monitor_sock->StrError(); return false; } len -= tmp; } // Log the reply char buff[1000]; while ((len = monitor_sock->Read(buff, sizeof(buff) - 1)) > 0) { buff[len] = '\0'; LOG(INFO) << "From qemu monitor: " << buff; } return true; } } // namespace const std::string QemuManager::name() { return "qemu_cli"; } std::vector QemuManager::ConfigureGpu(const std::string& gpu_mode) { if (gpu_mode != vsoc::kGpuModeGuestSwiftshader) { return {}; } // 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. return { "androidboot.hardware.gralloc=minigbm", "androidboot.hardware.hwcomposer=cutf_cvm_ashmem", "androidboot.hardware.egl=swiftshader", "androidboot.hardware.vulkan=pastel", }; } std::vector QemuManager::ConfigureBootDevices() { // PCI domain 0, bus 0, device 3, function 0 // This is controlled with 'addr=0x3' in cf_qemu.sh return { "androidboot.boot_devices=pci0000:00/0000:00:03.0" }; } QemuManager::QemuManager(const vsoc::CuttlefishConfig* config) : VmManager(config) {} std::vector QemuManager::StartCommands() { auto instance = config_->ForDefaultInstance(); auto stop = [](cvd::Subprocess* proc) { auto stopped = Stop(); if (stopped) { return true; } LOG(WARNING) << "Failed to stop VMM nicely, " << "attempting to KILL"; return KillSubprocess(proc); }; bool is_arm = android::base::EndsWith(config_->qemu_binary(), "system-aarch64"); cvd::Command qemu_cmd(config_->qemu_binary(), stop); qemu_cmd.AddParameter("-name"); qemu_cmd.AddParameter("guest=", instance.instance_name(), ",debug-threads=on"); qemu_cmd.AddParameter("-machine"); auto machine = is_arm ? "virt,gic_version=2" : "pc-i440fx-2.8,accel=kvm"; qemu_cmd.AddParameter(machine, ",usb=off,dump-guest-core=off"); qemu_cmd.AddParameter("-m"); qemu_cmd.AddParameter(config_->memory_mb()); qemu_cmd.AddParameter("-realtime"); qemu_cmd.AddParameter("mlock=off"); qemu_cmd.AddParameter("-smp"); qemu_cmd.AddParameter(config_->cpus(), ",sockets=", config_->cpus(), ",cores=1,threads=1"); qemu_cmd.AddParameter("-uuid"); qemu_cmd.AddParameter(instance.uuid()); qemu_cmd.AddParameter("-display"); qemu_cmd.AddParameter("none"); qemu_cmd.AddParameter("-no-user-config"); qemu_cmd.AddParameter("-nodefaults"); qemu_cmd.AddParameter("-no-shutdown"); qemu_cmd.AddParameter("-rtc"); qemu_cmd.AddParameter("base=utc"); qemu_cmd.AddParameter("-boot"); qemu_cmd.AddParameter("strict=on"); qemu_cmd.AddParameter("-kernel"); qemu_cmd.AddParameter(config_->GetKernelImageToUse()); qemu_cmd.AddParameter("-append"); qemu_cmd.AddParameter(kernel_cmdline_); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtio-serial-pci,id=virtio-serial0"); for (size_t i = 0; i < instance.virtual_disk_paths().size(); i++) { auto bootindex = i == 0 ? ",bootindex=1" : ""; auto disk = instance.virtual_disk_paths()[i]; qemu_cmd.AddParameter("-drive"); qemu_cmd.AddParameter("file=", disk, ",if=none,id=drive-virtio-disk", i, ",aio=threads"); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtio-blk-pci,scsi=off,drive=drive-virtio-disk", i, ",id=virtio-disk", i, bootindex); } qemu_cmd.AddParameter("-netdev"); qemu_cmd.AddParameter("tap,id=hostnet0,ifname=", instance.wifi_tap_name(), ",script=no,downscript=no"); auto romfile = is_arm ? ",romfile" : ""; qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet0,id=net0", romfile); qemu_cmd.AddParameter("-netdev"); qemu_cmd.AddParameter("tap,id=hostnet1,ifname=", instance.mobile_tap_name(), ",script=no,downscript=no"); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet1,id=net1", romfile); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtio-balloon-pci,id=balloon0"); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0"); qemu_cmd.AddParameter("-object"); qemu_cmd.AddParameter("rng-random,id=objrng0,filename=/dev/urandom"); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtio-rng-pci,rng=objrng0,id=rng0,", "max-bytes=1024,period=2000"); qemu_cmd.AddParameter("-cpu"); qemu_cmd.AddParameter(is_arm ? "cortex-a53" : "host"); qemu_cmd.AddParameter("-msg"); qemu_cmd.AddParameter("timestamp=on"); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("AC97"); if (config_->use_bootloader()) { qemu_cmd.AddParameter("-bios"); qemu_cmd.AddParameter(config_->bootloader()); } qemu_cmd.AddParameter("-chardev"); qemu_cmd.AddParameter("socket,id=charmonitor,path=", GetMonitorPath(config_), ",server,nowait"); qemu_cmd.AddParameter("-mon"); qemu_cmd.AddParameter("chardev=charmonitor,id=monitor,mode=control"); qemu_cmd.AddParameter("-chardev"); qemu_cmd.AddParameter("file,id=charserial0,path=", instance.kernel_log_pipe_name(), ",append=on"); qemu_cmd.AddParameter("-device"); // On ARM, the early console can be PCI, and ISA is not supported // On x86, the early console must be ISA, not PCI, so we start to get kernel // messages as soon as possible. ISA devices do not have 'addr' assignments. auto kernel_console_serial = is_arm ? "pci-serial" : "isa-serial"; qemu_cmd.AddParameter(kernel_console_serial, ",chardev=charserial0,id=serial0"); qemu_cmd.AddParameter("-chardev"); qemu_cmd.AddParameter("socket,id=charserial1,path=", instance.console_path(), ",server,nowait"); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter(kernel_console_serial, ",chardev=charserial1,id=serial1"); if (config_->logcat_mode() == "serial") { qemu_cmd.AddParameter("-chardev"); qemu_cmd.AddParameter("file,id=charchannel0,path=", instance.logcat_path(), ",append=on"); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("virtserialport,bus=virtio-serial0.0,nr=1,", "chardev=charchannel0,id=channel0,name=cf-logcat"); } if (config_->gdb_flag().size() > 0) { qemu_cmd.AddParameter("-gdb"); qemu_cmd.AddParameter(config_->gdb_flag()); } qemu_cmd.AddParameter("-initrd"); qemu_cmd.AddParameter(config_->final_ramdisk_path()); qemu_cmd.AddParameter("-device"); qemu_cmd.AddParameter("vhost-vsock-pci,guest-cid=", instance.vsock_guest_cid()); LogAndSetEnv("QEMU_AUDIO_DRV", "none"); std::vector ret; ret.push_back(std::move(qemu_cmd)); return ret; } } // namespace vm_manager