1 // Copyright 2021, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Functions for running instances of `crosvm`. 16 17 use crate::aidl::VirtualMachineCallbacks; 18 use crate::config::VmConfig; 19 use crate::Cid; 20 use anyhow::Error; 21 use log::{error, info}; 22 use shared_child::SharedChild; 23 use std::fs::File; 24 use std::process::Command; 25 use std::sync::atomic::{AtomicBool, Ordering}; 26 use std::sync::Arc; 27 use std::thread; 28 29 const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm"; 30 31 /// Information about a particular instance of a VM which is running. 32 #[derive(Debug)] 33 pub struct VmInstance { 34 /// The crosvm child process. 35 child: SharedChild, 36 /// The CID assigned to the VM for vsock communication. 37 pub cid: Cid, 38 /// The UID of the process which requested the VM. 39 pub requester_uid: u32, 40 /// The SID of the process which requested the VM. 41 pub requester_sid: String, 42 /// The PID of the process which requested the VM. Note that this process may no longer exist 43 /// and the PID may have been reused for a different process, so this should not be trusted. 44 pub requester_debug_pid: i32, 45 /// Whether the VM is still running. 46 running: AtomicBool, 47 /// Callbacks to clients of the VM. 48 pub callbacks: VirtualMachineCallbacks, 49 } 50 51 impl VmInstance { 52 /// Create a new `VmInstance` for the given process. 53 fn new( 54 child: SharedChild, 55 cid: Cid, 56 requester_uid: u32, 57 requester_sid: String, 58 requester_debug_pid: i32, 59 ) -> VmInstance { 60 VmInstance { 61 child, 62 cid, 63 requester_uid, 64 requester_sid, 65 requester_debug_pid, 66 running: AtomicBool::new(true), 67 callbacks: Default::default(), 68 } 69 } 70 71 /// Start an instance of `crosvm` to manage a new VM. The `crosvm` instance will be killed when 72 /// the `VmInstance` is dropped. 73 pub fn start( 74 config: &VmConfig, 75 cid: Cid, 76 log_fd: Option<File>, 77 requester_uid: u32, 78 requester_sid: String, 79 requester_debug_pid: i32, 80 ) -> Result<Arc<VmInstance>, Error> { 81 let child = run_vm(config, cid, log_fd)?; 82 let instance = Arc::new(VmInstance::new( 83 child, 84 cid, 85 requester_uid, 86 requester_sid, 87 requester_debug_pid, 88 )); 89 90 let instance_clone = instance.clone(); 91 thread::spawn(move || { 92 instance_clone.monitor(); 93 }); 94 95 Ok(instance) 96 } 97 98 /// Wait for the crosvm child process to finish, then mark the VM as no longer running and call 99 /// any callbacks. 100 fn monitor(&self) { 101 match self.child.wait() { 102 Err(e) => error!("Error waiting for crosvm instance to die: {}", e), 103 Ok(status) => info!("crosvm exited with status {}", status), 104 } 105 self.running.store(false, Ordering::Release); 106 self.callbacks.callback_on_died(self.cid); 107 } 108 109 /// Return whether `crosvm` is still running the VM. 110 pub fn running(&self) -> bool { 111 self.running.load(Ordering::Acquire) 112 } 113 114 /// Kill the crosvm instance. 115 pub fn kill(&self) { 116 // TODO: Talk to crosvm to shutdown cleanly. 117 if let Err(e) = self.child.kill() { 118 error!("Error killing crosvm instance: {}", e); 119 } 120 } 121 } 122 123 /// Start an instance of `crosvm` to manage a new VM. 124 fn run_vm(config: &VmConfig, cid: Cid, log_fd: Option<File>) -> Result<SharedChild, Error> { 125 config.validate()?; 126 127 let mut command = Command::new(CROSVM_PATH); 128 // TODO(qwandor): Remove --disable-sandbox. 129 command.arg("run").arg("--disable-sandbox").arg("--cid").arg(cid.to_string()); 130 if let Some(log_fd) = log_fd { 131 command.stdout(log_fd); 132 } else { 133 // Ignore console output. 134 command.arg("--serial=type=sink"); 135 } 136 if let Some(bootloader) = &config.bootloader { 137 command.arg("--bios").arg(bootloader); 138 } 139 if let Some(initrd) = &config.initrd { 140 command.arg("--initrd").arg(initrd); 141 } 142 if let Some(params) = &config.params { 143 command.arg("--params").arg(params); 144 } 145 for disk in &config.disks { 146 command.arg(if disk.writable { "--rwdisk" } else { "--disk" }).arg(&disk.image); 147 } 148 if let Some(kernel) = &config.kernel { 149 command.arg(kernel); 150 } 151 info!("Running {:?}", command); 152 Ok(SharedChild::spawn(&mut command)?) 153 } 154