• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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/qemu_manager.h"
18 
19 #include <string.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <sys/un.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 
27 #include <cstdlib>
28 #include <sstream>
29 #include <string>
30 #include <thread>
31 #include <vector>
32 
33 #include <android-base/strings.h>
34 #include <glog/logging.h>
35 
36 #include "common/libs/fs/shared_select.h"
37 #include "common/libs/utils/files.h"
38 #include "common/libs/utils/subprocess.h"
39 #include "common/libs/utils/users.h"
40 #include "host/libs/config/cuttlefish_config.h"
41 
42 namespace vm_manager {
43 
44 namespace {
45 
GetMonitorPath(const vsoc::CuttlefishConfig * config)46 std::string GetMonitorPath(const vsoc::CuttlefishConfig* config) {
47   return config->ForDefaultInstance()
48       .PerInstanceInternalPath("qemu_monitor.sock");
49 }
50 
LogAndSetEnv(const char * key,const std::string & value)51 void LogAndSetEnv(const char* key, const std::string& value) {
52   setenv(key, value.c_str(), 1);
53   LOG(INFO) << key << "=" << value;
54 }
55 
Stop()56 bool Stop() {
57   auto config = vsoc::CuttlefishConfig::Get();
58   auto monitor_path = GetMonitorPath(config);
59   auto monitor_sock = cvd::SharedFD::SocketLocalClient(
60       monitor_path.c_str(), false, SOCK_STREAM);
61 
62   if (!monitor_sock->IsOpen()) {
63     LOG(ERROR) << "The connection to qemu is closed, is it still running?";
64     return false;
65   }
66   char msg[] = "{\"execute\":\"qmp_capabilities\"}{\"execute\":\"quit\"}";
67   ssize_t len = sizeof(msg) - 1;
68   while (len > 0) {
69     int tmp = monitor_sock->Write(msg, len);
70     if (tmp < 0) {
71       LOG(ERROR) << "Error writing to socket: " << monitor_sock->StrError();
72       return false;
73     }
74     len -= tmp;
75   }
76   // Log the reply
77   char buff[1000];
78   while ((len = monitor_sock->Read(buff, sizeof(buff) - 1)) > 0) {
79     buff[len] = '\0';
80     LOG(INFO) << "From qemu monitor: " << buff;
81   }
82 
83   return true;
84 }
85 
86 }  // namespace
87 
name()88 const std::string QemuManager::name() { return "qemu_cli"; }
89 
ConfigureGpu(const std::string & gpu_mode)90 std::vector<std::string> QemuManager::ConfigureGpu(const std::string& gpu_mode) {
91   if (gpu_mode != vsoc::kGpuModeGuestSwiftshader) {
92     return {};
93   }
94   // Override the default HAL search paths in all cases. We do this because
95   // the HAL search path allows for fallbacks, and fallbacks in conjunction
96   // with properities lead to non-deterministic behavior while loading the
97   // HALs.
98   return {
99       "androidboot.hardware.gralloc=minigbm",
100       "androidboot.hardware.hwcomposer=cutf_cvm_ashmem",
101       "androidboot.hardware.egl=swiftshader",
102       "androidboot.hardware.vulkan=pastel",
103   };
104 }
105 
ConfigureBootDevices()106 std::vector<std::string> QemuManager::ConfigureBootDevices() {
107   // PCI domain 0, bus 0, device 3, function 0
108   // This is controlled with 'addr=0x3' in cf_qemu.sh
109   return { "androidboot.boot_devices=pci0000:00/0000:00:03.0" };
110 }
111 
QemuManager(const vsoc::CuttlefishConfig * config)112 QemuManager::QemuManager(const vsoc::CuttlefishConfig* config)
113   : VmManager(config) {}
114 
StartCommands()115 std::vector<cvd::Command> QemuManager::StartCommands() {
116   auto instance = config_->ForDefaultInstance();
117 
118   auto stop = [](cvd::Subprocess* proc) {
119     auto stopped = Stop();
120     if (stopped) {
121       return true;
122     }
123     LOG(WARNING) << "Failed to stop VMM nicely, "
124                   << "attempting to KILL";
125     return KillSubprocess(proc);
126   };
127 
128   bool is_arm = android::base::EndsWith(config_->qemu_binary(), "system-aarch64");
129 
130   cvd::Command qemu_cmd(config_->qemu_binary(), stop);
131   qemu_cmd.AddParameter("-name");
132   qemu_cmd.AddParameter("guest=", instance.instance_name(), ",debug-threads=on");
133 
134   qemu_cmd.AddParameter("-machine");
135   auto machine = is_arm ? "virt,gic_version=2" : "pc-i440fx-2.8,accel=kvm";
136   qemu_cmd.AddParameter(machine, ",usb=off,dump-guest-core=off");
137 
138   qemu_cmd.AddParameter("-m");
139   qemu_cmd.AddParameter(config_->memory_mb());
140 
141   qemu_cmd.AddParameter("-realtime");
142   qemu_cmd.AddParameter("mlock=off");
143 
144   qemu_cmd.AddParameter("-smp");
145   qemu_cmd.AddParameter(config_->cpus(), ",sockets=", config_->cpus(),
146                         ",cores=1,threads=1");
147 
148   qemu_cmd.AddParameter("-uuid");
149   qemu_cmd.AddParameter(instance.uuid());
150 
151   qemu_cmd.AddParameter("-display");
152   qemu_cmd.AddParameter("none");
153 
154   qemu_cmd.AddParameter("-no-user-config");
155   qemu_cmd.AddParameter("-nodefaults");
156   qemu_cmd.AddParameter("-no-shutdown");
157 
158   qemu_cmd.AddParameter("-rtc");
159   qemu_cmd.AddParameter("base=utc");
160 
161   qemu_cmd.AddParameter("-boot");
162   qemu_cmd.AddParameter("strict=on");
163 
164   qemu_cmd.AddParameter("-kernel");
165   qemu_cmd.AddParameter(config_->GetKernelImageToUse());
166 
167   qemu_cmd.AddParameter("-append");
168   qemu_cmd.AddParameter(kernel_cmdline_);
169 
170   qemu_cmd.AddParameter("-device");
171   qemu_cmd.AddParameter("virtio-serial-pci,id=virtio-serial0");
172 
173   for (size_t i = 0; i < instance.virtual_disk_paths().size(); i++) {
174     auto bootindex = i == 0 ? ",bootindex=1" : "";
175     auto disk = instance.virtual_disk_paths()[i];
176     qemu_cmd.AddParameter("-drive");
177     qemu_cmd.AddParameter("file=", disk, ",if=none,id=drive-virtio-disk", i,
178                           ",aio=threads");
179     qemu_cmd.AddParameter("-device");
180     qemu_cmd.AddParameter("virtio-blk-pci,scsi=off,drive=drive-virtio-disk", i,
181                           ",id=virtio-disk", i, bootindex);
182   }
183 
184   qemu_cmd.AddParameter("-netdev");
185   qemu_cmd.AddParameter("tap,id=hostnet0,ifname=", instance.wifi_tap_name(),
186                         ",script=no,downscript=no");
187 
188   auto romfile = is_arm ? ",romfile" : "";
189   qemu_cmd.AddParameter("-device");
190   qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet0,id=net0", romfile);
191 
192   qemu_cmd.AddParameter("-netdev");
193   qemu_cmd.AddParameter("tap,id=hostnet1,ifname=", instance.mobile_tap_name(),
194                         ",script=no,downscript=no");
195 
196   qemu_cmd.AddParameter("-device");
197   qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet1,id=net1", romfile);
198 
199   qemu_cmd.AddParameter("-device");
200   qemu_cmd.AddParameter("virtio-balloon-pci,id=balloon0");
201 
202   qemu_cmd.AddParameter("-device");
203   qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0");
204 
205   qemu_cmd.AddParameter("-object");
206   qemu_cmd.AddParameter("rng-random,id=objrng0,filename=/dev/urandom");
207 
208   qemu_cmd.AddParameter("-device");
209   qemu_cmd.AddParameter("virtio-rng-pci,rng=objrng0,id=rng0,",
210                         "max-bytes=1024,period=2000");
211 
212   qemu_cmd.AddParameter("-cpu");
213   qemu_cmd.AddParameter(is_arm ? "cortex-a53" : "host");
214 
215   qemu_cmd.AddParameter("-msg");
216   qemu_cmd.AddParameter("timestamp=on");
217 
218   qemu_cmd.AddParameter("-device");
219   qemu_cmd.AddParameter("AC97");
220 
221   if (config_->use_bootloader()) {
222     qemu_cmd.AddParameter("-bios");
223     qemu_cmd.AddParameter(config_->bootloader());
224   }
225 
226   qemu_cmd.AddParameter("-chardev");
227   qemu_cmd.AddParameter("socket,id=charmonitor,path=", GetMonitorPath(config_),
228                         ",server,nowait");
229 
230   qemu_cmd.AddParameter("-mon");
231   qemu_cmd.AddParameter("chardev=charmonitor,id=monitor,mode=control");
232 
233   qemu_cmd.AddParameter("-chardev");
234   qemu_cmd.AddParameter("file,id=charserial0,path=",
235                         instance.kernel_log_pipe_name(), ",append=on");
236 
237   qemu_cmd.AddParameter("-device");
238   // On ARM, the early console can be PCI, and ISA is not supported
239   // On x86, the early console must be ISA, not PCI, so we start to get kernel
240   // messages as soon as possible. ISA devices do not have 'addr' assignments.
241   auto kernel_console_serial = is_arm ? "pci-serial" : "isa-serial";
242   qemu_cmd.AddParameter(kernel_console_serial, ",chardev=charserial0,id=serial0");
243 
244   qemu_cmd.AddParameter("-chardev");
245   qemu_cmd.AddParameter("socket,id=charserial1,path=", instance.console_path(),
246                         ",server,nowait");
247 
248   qemu_cmd.AddParameter("-device");
249   qemu_cmd.AddParameter(kernel_console_serial, ",chardev=charserial1,id=serial1");
250 
251   if (config_->logcat_mode() == "serial") {
252     qemu_cmd.AddParameter("-chardev");
253     qemu_cmd.AddParameter("file,id=charchannel0,path=", instance.logcat_path(),
254                           ",append=on");
255 
256     qemu_cmd.AddParameter("-device");
257     qemu_cmd.AddParameter("virtserialport,bus=virtio-serial0.0,nr=1,",
258                           "chardev=charchannel0,id=channel0,name=cf-logcat");
259   }
260 
261   if (config_->gdb_flag().size() > 0) {
262     qemu_cmd.AddParameter("-gdb");
263     qemu_cmd.AddParameter(config_->gdb_flag());
264   }
265 
266   qemu_cmd.AddParameter("-initrd");
267   qemu_cmd.AddParameter(config_->final_ramdisk_path());
268 
269   qemu_cmd.AddParameter("-device");
270   qemu_cmd.AddParameter("vhost-vsock-pci,guest-cid=", instance.vsock_guest_cid());
271 
272   LogAndSetEnv("QEMU_AUDIO_DRV", "none");
273 
274   std::vector<cvd::Command> ret;
275   ret.push_back(std::move(qemu_cmd));
276   return ret;
277 }
278 
279 }  // namespace vm_manager
280