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 <android-base/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 cuttlefish {
43 namespace vm_manager {
44 namespace {
45 
GetMonitorPath(const cuttlefish::CuttlefishConfig * config)46 std::string GetMonitorPath(const cuttlefish::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 = cuttlefish::CuttlefishConfig::Get();
58   auto monitor_path = GetMonitorPath(config);
59   auto monitor_sock = cuttlefish::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 != cuttlefish::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 5, function 0
108   return { "androidboot.boot_devices=pci0000:00/0000:00:05.0" };
109 }
110 
QemuManager(const cuttlefish::CuttlefishConfig * config)111 QemuManager::QemuManager(const cuttlefish::CuttlefishConfig* config)
112   : VmManager(config) {}
113 
StartCommands()114 std::vector<cuttlefish::Command> QemuManager::StartCommands() {
115   auto instance = config_->ForDefaultInstance();
116 
117   auto stop = [](cuttlefish::Subprocess* proc) {
118     auto stopped = Stop();
119     if (stopped) {
120       return true;
121     }
122     LOG(WARNING) << "Failed to stop VMM nicely, "
123                   << "attempting to KILL";
124     return KillSubprocess(proc);
125   };
126 
127   bool is_arm = android::base::EndsWith(config_->qemu_binary(), "system-aarch64");
128 
129   auto access_kregistry_size_bytes = cuttlefish::FileSize(instance.access_kregistry_path());
130   if (access_kregistry_size_bytes & (1024 * 1024 - 1)) {
131       LOG(FATAL) << instance.access_kregistry_path() <<  " file size ("
132                  << access_kregistry_size_bytes << ") not a multiple of 1MB";
133       return {};
134   }
135 
136   auto pstore_size_bytes = cuttlefish::FileSize(instance.pstore_path());
137   if (pstore_size_bytes & (1024 * 1024 - 1)) {
138       LOG(FATAL) << instance.pstore_path() <<  " file size ("
139                  << pstore_size_bytes << ") not a multiple of 1MB";
140       return {};
141   }
142 
143   cuttlefish::Command qemu_cmd(config_->qemu_binary(), stop);
144   qemu_cmd.AddParameter("-name");
145   qemu_cmd.AddParameter("guest=", instance.instance_name(), ",debug-threads=on");
146 
147   qemu_cmd.AddParameter("-machine");
148   auto machine = is_arm ? "virt,gic-version=2" : "pc-i440fx-2.8,accel=kvm,nvdimm=on";
149   qemu_cmd.AddParameter(machine, ",usb=off,dump-guest-core=off");
150 
151   qemu_cmd.AddParameter("-m");
152   auto maxmem = config_->memory_mb() +
153                 access_kregistry_size_bytes / 1024 / 1024 +
154                 (is_arm ? 0 : pstore_size_bytes / 1024 / 1024);
155   auto slots = is_arm ? "" : ",slots=2";
156   qemu_cmd.AddParameter("size=", config_->memory_mb(), "M",
157                         ",maxmem=", maxmem, "M", slots);
158 
159   qemu_cmd.AddParameter("-overcommit");
160   qemu_cmd.AddParameter("mem-lock=off");
161 
162   qemu_cmd.AddParameter("-smp");
163   qemu_cmd.AddParameter(config_->cpus(), ",sockets=", config_->cpus(),
164                         ",cores=1,threads=1");
165 
166   qemu_cmd.AddParameter("-uuid");
167   qemu_cmd.AddParameter(instance.uuid());
168 
169   qemu_cmd.AddParameter("-display");
170   qemu_cmd.AddParameter("none");
171 
172   qemu_cmd.AddParameter("-no-user-config");
173   qemu_cmd.AddParameter("-nodefaults");
174   qemu_cmd.AddParameter("-no-shutdown");
175 
176   qemu_cmd.AddParameter("-rtc");
177   qemu_cmd.AddParameter("base=utc");
178 
179   qemu_cmd.AddParameter("-boot");
180   qemu_cmd.AddParameter("strict=on");
181 
182   qemu_cmd.AddParameter("-kernel");
183   qemu_cmd.AddParameter(config_->GetKernelImageToUse());
184 
185   qemu_cmd.AddParameter("-append");
186   qemu_cmd.AddParameter(kernel_cmdline_);
187 
188   qemu_cmd.AddParameter("-chardev");
189   qemu_cmd.AddParameter("socket,id=charmonitor,path=", GetMonitorPath(config_),
190                         ",server,nowait");
191 
192   qemu_cmd.AddParameter("-mon");
193   qemu_cmd.AddParameter("chardev=charmonitor,id=monitor,mode=control");
194 
195   // In kgdb mode, earlycon is an interactive console, and so early
196   // dmesg will go there instead of the kernel.log
197   if (config_->kgdb() || config_->use_bootloader()) {
198     qemu_cmd.AddParameter("-chardev");
199     qemu_cmd.AddParameter("socket,id=earlycon,path=",
200                           instance.console_path(), ",server,nowait");
201   } else {
202     qemu_cmd.AddParameter("-chardev");
203     qemu_cmd.AddParameter("file,id=earlycon,path=",
204                           instance.kernel_log_pipe_name(), ",append=on");
205   }
206 
207   // On ARM, -serial will imply an AMBA pl011 serial port. On x86, -serial
208   // will imply an ISA serial port. We have set up earlycon for each of these
209   // port types, so the setting here should match
210   qemu_cmd.AddParameter("-serial");
211   qemu_cmd.AddParameter("chardev:earlycon");
212 
213   // This sets up the HVC (virtio-serial / virtio-console) port for the kernel
214   // logging. This will take over the earlycon logging when the module is
215   // loaded. Give it the first nr, so it gets /dev/hvc0.
216   qemu_cmd.AddParameter("-chardev");
217   qemu_cmd.AddParameter("file,id=hvc0,path=",
218                         instance.kernel_log_pipe_name(), ",append=on");
219 
220   qemu_cmd.AddParameter("-device");
221   qemu_cmd.AddParameter("virtio-serial-pci-non-transitional,max_ports=1,id=virtio-serial0");
222 
223   qemu_cmd.AddParameter("-device");
224   qemu_cmd.AddParameter("virtconsole,bus=virtio-serial0.0,chardev=hvc0");
225 
226   // This handles the Android interactive serial console - /dev/hvc1
227 
228   // In kgdb mode, we have the interactive console on ttyS0 (both Android's
229   // console and kdb), so we can disable the virtio-console port usually
230   // allocated to Android's serial console, and redirect it to a sink. This
231   // ensures that that the PCI device assignments (and thus sepolicy) don't
232   // have to change
233   if (config_->kgdb() || config_->use_bootloader()) {
234     qemu_cmd.AddParameter("-chardev");
235     qemu_cmd.AddParameter("null,id=hvc1");
236   } else {
237     qemu_cmd.AddParameter("-chardev");
238     qemu_cmd.AddParameter("socket,id=hvc1,path=", instance.console_path(),
239                           ",server,nowait");
240   }
241 
242   qemu_cmd.AddParameter("-device");
243   qemu_cmd.AddParameter("virtio-serial-pci-non-transitional,max_ports=1,id=virtio-serial1");
244 
245   qemu_cmd.AddParameter("-device");
246   qemu_cmd.AddParameter("virtconsole,bus=virtio-serial1.0,chardev=hvc1");
247 
248   // If configured, this handles logcat forwarding to the host via serial
249   // (instead of vsocket) - /dev/hvc2
250 
251   qemu_cmd.AddParameter("-chardev");
252   qemu_cmd.AddParameter("file,id=hvc2,path=",
253                         instance.logcat_pipe_name(), ",append=on");
254 
255   qemu_cmd.AddParameter("-device");
256   qemu_cmd.AddParameter("virtio-serial-pci-non-transitional,max_ports=1,id=virtio-serial2");
257 
258   qemu_cmd.AddParameter("-device");
259   qemu_cmd.AddParameter("virtconsole,bus=virtio-serial2.0,chardev=hvc2");
260 
261   for (size_t i = 0; i < instance.virtual_disk_paths().size(); i++) {
262     auto bootindex = i == 0 ? ",bootindex=1" : "";
263     auto format = i == 0 ? "" : ",format=raw";
264     auto disk = instance.virtual_disk_paths()[i];
265     qemu_cmd.AddParameter("-drive");
266     qemu_cmd.AddParameter("file=", disk, ",if=none,id=drive-virtio-disk", i,
267                           ",aio=threads", format);
268     qemu_cmd.AddParameter("-device");
269     qemu_cmd.AddParameter("virtio-blk-pci-non-transitional,scsi=off,drive=drive-virtio-disk", i,
270                           ",id=virtio-disk", i, bootindex);
271   }
272 
273   if (!is_arm) {
274     // QEMU will assign the NVDIMM (ramoops pstore region) 100000000-1001fffff
275     // As we will pass this to ramoops, define this region first so it is always
276     // located at this address. This is currently x86 only.
277     qemu_cmd.AddParameter("-object");
278     qemu_cmd.AddParameter("memory-backend-file,id=objpmem0,share,mem-path=",
279                           instance.pstore_path(), ",size=", pstore_size_bytes);
280 
281     qemu_cmd.AddParameter("-device");
282     qemu_cmd.AddParameter("nvdimm,memdev=objpmem0,id=ramoops");
283   }
284 
285   qemu_cmd.AddParameter("-object");
286   qemu_cmd.AddParameter("memory-backend-file,id=objpmem1,share,mem-path=",
287                         instance.access_kregistry_path(), ",size=",
288                         access_kregistry_size_bytes);
289 
290   qemu_cmd.AddParameter("-device");
291   qemu_cmd.AddParameter("virtio-pmem-pci,disable-legacy=on,memdev=objpmem1,id=pmem0");
292 
293   qemu_cmd.AddParameter("-object");
294   qemu_cmd.AddParameter("rng-random,id=objrng0,filename=/dev/urandom");
295 
296   qemu_cmd.AddParameter("-device");
297   qemu_cmd.AddParameter("virtio-rng-pci-non-transitional,rng=objrng0,id=rng0,",
298                         "max-bytes=1024,period=2000");
299 
300   qemu_cmd.AddParameter("-device");
301   qemu_cmd.AddParameter("virtio-balloon-pci-non-transitional,id=balloon0");
302 
303   qemu_cmd.AddParameter("-netdev");
304   qemu_cmd.AddParameter("tap,id=hostnet0,ifname=", instance.wifi_tap_name(),
305                         ",script=no,downscript=no");
306 
307   qemu_cmd.AddParameter("-device");
308   qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet0,id=net0");
309 
310   qemu_cmd.AddParameter("-netdev");
311   qemu_cmd.AddParameter("tap,id=hostnet1,ifname=", instance.mobile_tap_name(),
312                         ",script=no,downscript=no");
313 
314   qemu_cmd.AddParameter("-device");
315   qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet1,id=net1");
316 
317   qemu_cmd.AddParameter("-device");
318   qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0");
319 
320   qemu_cmd.AddParameter("-cpu");
321   qemu_cmd.AddParameter(is_arm ? "cortex-a53" : "host");
322 
323   qemu_cmd.AddParameter("-msg");
324   qemu_cmd.AddParameter("timestamp=on");
325 
326   qemu_cmd.AddParameter("-device");
327   qemu_cmd.AddParameter("vhost-vsock-pci-non-transitional,guest-cid=",
328                         instance.vsock_guest_cid());
329 
330   qemu_cmd.AddParameter("-device");
331   qemu_cmd.AddParameter("AC97");
332 
333   if (config_->use_bootloader()) {
334     qemu_cmd.AddParameter("-bios");
335     qemu_cmd.AddParameter(config_->bootloader());
336   }
337 
338   if (config_->gdb_flag().size() > 0) {
339     qemu_cmd.AddParameter("-gdb");
340     qemu_cmd.AddParameter(config_->gdb_flag());
341   }
342 
343   qemu_cmd.AddParameter("-initrd");
344   qemu_cmd.AddParameter(config_->final_ramdisk_path());
345 
346   LogAndSetEnv("QEMU_AUDIO_DRV", "none");
347 
348   std::vector<cuttlefish::Command> ret;
349   ret.push_back(std::move(qemu_cmd));
350   return ret;
351 }
352 
353 } // namespace vm_manager
354 } // namespace cuttlefish
355 
356