1#[macro_use]
14extern crate log;
15
16pub mod builder;
18pub(crate) mod device_manager;
19pub mod resources;
21#[cfg(target_os = "linux")]
23pub mod signal_handler;
24pub mod vmm_config;
26
27#[cfg(target_os = "linux")]
28mod linux;
29#[cfg(target_os = "linux")]
30use crate::linux::vstate;
31#[cfg(target_os = "macos")]
32mod macos;
33mod terminal;
34pub mod worker;
35
36#[cfg(target_os = "macos")]
37use macos::vstate;
38
39use std::fmt::{Display, Formatter};
40use std::io;
41use std::os::unix::io::AsRawFd;
42use std::sync::atomic::{AtomicI32, Ordering};
43use std::sync::{Arc, Mutex};
44#[cfg(target_os = "linux")]
45use std::time::Duration;
46
47#[cfg(target_arch = "x86_64")]
48use crate::device_manager::legacy::PortIODeviceManager;
49use crate::device_manager::mmio::MMIODeviceManager;
50#[cfg(target_os = "linux")]
51use crate::vstate::VcpuEvent;
52use crate::vstate::{Vcpu, VcpuHandle, VcpuResponse, Vm};
53
54use arch::{ArchMemoryInfo, InitrdConfig};
55#[cfg(target_os = "macos")]
56use crossbeam_channel::Sender;
57#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
58use devices::fdt;
59use devices::legacy::IrqChip;
60use devices::virtio::VmmExitObserver;
61use devices::{BusDevice, DeviceType};
62use kernel::cmdline::Cmdline as KernelCmdline;
63use polly::event_manager::{self, EventManager, Subscriber};
64use utils::epoll::{EpollEvent, EventSet};
65use utils::eventfd::EventFd;
66use vm_memory::GuestMemoryMmap;
67
68pub const FC_EXIT_CODE_OK: u8 = 0;
70pub const FC_EXIT_CODE_GENERIC_ERROR: u8 = 1;
72pub const FC_EXIT_CODE_UNEXPECTED_ERROR: u8 = 2;
74pub const FC_EXIT_CODE_BAD_SYSCALL: u8 = 148;
76pub const FC_EXIT_CODE_SIGBUS: u8 = 149;
78pub const FC_EXIT_CODE_SIGSEGV: u8 = 150;
80pub const FC_EXIT_CODE_BAD_CONFIGURATION: u8 = 152;
82pub const FC_EXIT_CODE_ARG_PARSING: u8 = 153;
84
85#[derive(Debug)]
89pub enum Error {
90 ConfigureSystem(arch::Error),
92 #[cfg(target_arch = "x86_64")]
95 CreateLegacyDevice(device_manager::legacy::Error),
96 EventFd(io::Error),
98 EventManager(event_manager::Error),
100 #[cfg(target_arch = "x86_64")]
102 I8042Error(devices::legacy::I8042DeviceError),
103 KernelFile(io::Error),
105 KvmContext(vstate::Error),
108 #[cfg(target_arch = "x86_64")]
109 LegacyIOBus(device_manager::legacy::Error),
111 LoadCommandline(kernel::cmdline::Error),
113 RegisterMMIODevice(device_manager::mmio::Error),
115 Serial(io::Error),
117 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
118 SetupFDT(devices::fdt::Error),
120 TimerFd(io::Error),
122 Vcpu(vstate::Error),
124 VcpuEvent(vstate::Error),
126 VcpuHandle(vstate::Error),
128 VcpuResume,
130 VcpuSpawn(std::io::Error),
132 Vm(vstate::Error),
134 VmmObserverInit(utils::errno::Error),
136 VmmObserverTeardown(utils::errno::Error),
138}
139
140impl Display for Error {
141 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
142 use self::Error::*;
143
144 match self {
145 ConfigureSystem(e) => write!(f, "System configuration error: {e:?}"),
146 #[cfg(target_arch = "x86_64")]
147 CreateLegacyDevice(e) => write!(f, "Error creating legacy device: {e:?}"),
148 EventFd(e) => write!(f, "Event fd error: {e}"),
149 EventManager(e) => write!(f, "Event manager error: {e:?}"),
150 #[cfg(target_arch = "x86_64")]
151 I8042Error(e) => write!(f, "I8042 error: {e}"),
152 KernelFile(e) => write!(f, "Cannot access kernel file: {e}"),
153 KvmContext(e) => write!(f, "Failed to validate KVM support: {e:?}"),
154 #[cfg(target_arch = "x86_64")]
155 LegacyIOBus(e) => write!(f, "Cannot add devices to the legacy I/O Bus. {e}"),
156 LoadCommandline(e) => write!(f, "Cannot load command line: {e}"),
157 RegisterMMIODevice(e) => write!(f, "Cannot add a device to the MMIO Bus. {e}"),
158 Serial(e) => write!(f, "Error writing to the serial console: {e:?}"),
159 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
160 SetupFDT(e) => write!(f, "Error generating or writing FDT: {e:?}"),
161 TimerFd(e) => write!(f, "Error creating timer fd: {e}"),
162 Vcpu(e) => write!(f, "Vcpu error: {e}"),
163 VcpuEvent(e) => write!(f, "Cannot send event to vCPU. {e:?}"),
164 VcpuHandle(e) => write!(f, "Cannot create a vCPU handle. {e}"),
165 VcpuResume => write!(f, "vCPUs resume failed."),
166 VcpuSpawn(e) => write!(f, "Cannot spawn Vcpu thread: {e}"),
167 Vm(e) => write!(f, "Vm error: {e}"),
168 VmmObserverInit(e) => write!(
169 f,
170 "Error thrown by observer object on Vmm initialization: {e}"
171 ),
172 VmmObserverTeardown(e) => {
173 write!(f, "Error thrown by observer object on Vmm teardown: {e}")
174 }
175 }
176 }
177}
178
179pub trait VmmEventsObserver {
181 fn on_vmm_boot(&mut self) -> std::result::Result<(), utils::errno::Error> {
183 Ok(())
184 }
185 fn on_vmm_stop(&mut self) -> std::result::Result<(), utils::errno::Error> {
187 Ok(())
188 }
189}
190
191pub type Result<T> = std::result::Result<T, Error>;
193
194pub struct Vmm {
196 guest_memory: GuestMemoryMmap,
198 arch_memory_info: ArchMemoryInfo,
199
200 kernel_cmdline: KernelCmdline,
201
202 vcpus_handles: Vec<VcpuHandle>,
203 exit_evt: EventFd,
204 vm: Vm,
205 exit_observers: Vec<Arc<Mutex<dyn VmmExitObserver>>>,
206 exit_code: Arc<AtomicI32>,
207
208 mmio_device_manager: MMIODeviceManager,
210 #[cfg(target_arch = "x86_64")]
211 pio_device_manager: PortIODeviceManager,
212}
213
214impl Vmm {
215 pub fn get_bus_device(
217 &self,
218 device_type: DeviceType,
219 device_id: &str,
220 ) -> Option<&Mutex<dyn BusDevice>> {
221 self.mmio_device_manager.get_device(device_type, device_id)
222 }
223
224 pub fn start_vcpus(&mut self, mut vcpus: Vec<Vcpu>) -> Result<()> {
226 let vcpu_count = vcpus.len();
227
228 Vcpu::register_kick_signal_handler();
229
230 self.vcpus_handles.reserve(vcpu_count);
231
232 for mut vcpu in vcpus.drain(..) {
233 vcpu.set_mmio_bus(self.mmio_device_manager.bus.clone());
234
235 self.vcpus_handles
236 .push(vcpu.start_threaded().map_err(Error::VcpuHandle)?);
237 }
238
239 self.resume_vcpus()?;
241
242 Ok(())
243 }
244
245 #[cfg(target_os = "linux")]
247 pub fn resume_vcpus(&mut self) -> Result<()> {
248 for handle in self.vcpus_handles.iter() {
249 handle
250 .send_event(VcpuEvent::Resume)
251 .map_err(Error::VcpuEvent)?;
252 }
253 for handle in self.vcpus_handles.iter() {
254 match handle
255 .response_receiver()
256 .recv_timeout(Duration::from_millis(1000))
257 {
258 Ok(VcpuResponse::Resumed) => (),
259 _ => return Err(Error::VcpuResume),
260 }
261 }
262 Ok(())
263 }
264
265 #[cfg(target_os = "macos")]
266 pub fn resume_vcpus(&mut self) -> Result<()> {
267 Ok(())
268 }
269
270 pub fn configure_system(
272 &self,
273 vcpus: &[Vcpu],
274 _intc: &IrqChip,
275 initrd: &Option<InitrdConfig>,
276 _smbios_oem_strings: &Option<Vec<String>>,
277 ) -> Result<()> {
278 #[cfg(target_arch = "x86_64")]
279 {
280 let cmdline_len = if cfg!(feature = "tee") {
281 arch::x86_64::layout::CMDLINE_SEV_SIZE
282 } else {
283 self.kernel_cmdline.len() + 1
284 };
285
286 arch::x86_64::configure_system(
287 &self.guest_memory,
288 &self.arch_memory_info,
289 vm_memory::GuestAddress(arch::x86_64::layout::CMDLINE_START),
290 cmdline_len,
291 initrd,
292 vcpus.len() as u8,
293 )
294 .map_err(Error::ConfigureSystem)?;
295 }
296
297 #[cfg(target_arch = "aarch64")]
298 {
299 let vcpu_mpidr = vcpus.iter().map(|cpu| cpu.get_mpidr()).collect();
300 fdt::create_fdt(
301 &self.guest_memory,
302 &self.arch_memory_info,
303 vcpu_mpidr,
304 self.kernel_cmdline.as_str(),
305 self.mmio_device_manager.get_device_info(),
306 _intc,
307 initrd,
308 )
309 .map_err(Error::SetupFDT)?;
310 }
311
312 #[cfg(target_arch = "aarch64")]
313 {
314 arch::aarch64::configure_system(
315 &self.guest_memory,
316 &self.arch_memory_info,
317 _smbios_oem_strings,
318 )
319 .map_err(Error::ConfigureSystem)?;
320 }
321
322 #[cfg(target_arch = "riscv64")]
323 {
324 fdt::create_fdt(
325 &self.guest_memory,
326 &self.arch_memory_info,
327 vcpus.len() as u32,
328 self.kernel_cmdline.as_str(),
329 self.mmio_device_manager.get_device_info(),
330 _intc,
331 initrd,
332 )
333 .map_err(Error::SetupFDT)?;
334
335 arch::riscv64::configure_system(&self.guest_memory, _smbios_oem_strings)
336 .map_err(Error::ConfigureSystem)?;
337 }
338
339 Ok(())
340 }
341
342 pub fn guest_memory(&self) -> &GuestMemoryMmap {
344 &self.guest_memory
345 }
346
347 #[cfg(target_arch = "x86_64")]
349 pub fn send_ctrl_alt_del(&mut self) -> Result<()> {
350 self.pio_device_manager
351 .i8042
352 .lock()
353 .expect("i8042 lock was poisoned")
354 .trigger_ctrl_alt_del()
355 .map_err(Error::I8042Error)
356 }
357
358 pub fn stop(&mut self, exit_code: i32) {
360 info!("Vmm is stopping.");
361
362 for observer in &self.exit_observers {
363 observer
364 .lock()
365 .expect("Poisoned mutex for exit observer")
366 .on_vmm_exit();
367 }
368
369 unsafe {
372 libc::_exit(exit_code);
373 }
374 }
375
376 pub fn kvm_vm(&self) -> &Vm {
378 &self.vm
379 }
380
381 #[cfg(target_os = "macos")]
382 pub fn add_mapping(
383 &self,
384 reply_sender: Sender<bool>,
385 host_addr: u64,
386 guest_addr: u64,
387 len: u64,
388 ) {
389 self.vm
390 .add_mapping(reply_sender, host_addr, guest_addr, len);
391 }
392
393 #[cfg(target_os = "macos")]
394 pub fn remove_mapping(&self, reply_sender: Sender<bool>, guest_addr: u64, len: u64) {
395 self.vm.remove_mapping(reply_sender, guest_addr, len);
396 }
397}
398
399impl Subscriber for Vmm {
400 fn process(&mut self, event: &EpollEvent, _: &mut EventManager) {
402 let source = event.fd();
403 let event_set = event.event_set();
404
405 if source == self.exit_evt.as_raw_fd() && event_set == EventSet::IN {
406 let _ = self.exit_evt.read();
407 let vcpu_exit_code = self
415 .vcpus_handles
416 .iter()
417 .find_map(|handle| match handle.response_receiver().try_recv() {
418 Ok(VcpuResponse::Exited(exit_code)) => Some(exit_code),
419 _ => None,
420 })
421 .unwrap_or(FC_EXIT_CODE_OK);
422 let vmm_exit_code = self.exit_code.load(Ordering::SeqCst);
423 let exit_code = if vmm_exit_code != i32::MAX {
424 debug!("using vmm exit code: {vmm_exit_code}");
425 vmm_exit_code
426 } else {
427 debug!("using vcpu exit code: {vcpu_exit_code}");
428 vcpu_exit_code as i32
429 };
430 self.stop(exit_code);
431 } else {
432 error!("Spurious EventManager event for handler: Vmm");
433 }
434 }
435
436 fn interest_list(&self) -> Vec<EpollEvent> {
437 vec![EpollEvent::new(
438 EventSet::IN,
439 self.exit_evt.as_raw_fd() as u64,
440 )]
441 }
442}