// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. pub mod android; pub mod fdt; use std::collections::BTreeMap; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::fs::File; use std::io::{self, Read, Seek, SeekFrom}; use std::os::unix::io::AsRawFd; use std::sync::Arc; use devices::virtio::VirtioDevice; use devices::{ Bus, BusDevice, BusError, PciDevice, PciDeviceError, PciInterruptPin, PciRoot, ProxyDevice, Serial, SerialParameters, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR, }; use io_jail::Minijail; use kvm::{IoeventAddress, Kvm, Vcpu, Vm}; use resources::SystemAllocator; use sync::Mutex; use sys_util::{syslog, EventFd, GuestAddress, GuestMemory, GuestMemoryError}; pub enum VmImage { Kernel(File), Bios(File), } /// Holds the pieces needed to build a VM. Passed to `build_vm` in the `LinuxArch` trait below to /// create a `RunnableLinuxVm`. pub struct VmComponents { pub memory_size: u64, pub vcpu_count: u32, pub vcpu_affinity: Vec, pub vm_image: VmImage, pub android_fstab: Option, pub initrd_image: Option, pub extra_kernel_params: Vec, pub wayland_dmabuf: bool, } /// Holds the elements needed to run a Linux VM. Created by `build_vm`. pub struct RunnableLinuxVm { pub vm: Vm, pub kvm: Kvm, pub resources: SystemAllocator, pub stdio_serial: Option>>, pub exit_evt: EventFd, pub vcpus: Vec, pub vcpu_affinity: Vec, pub irq_chip: Option, pub io_bus: Bus, pub mmio_bus: Bus, pub pid_debug_label_map: BTreeMap, } /// The device and optional jail. pub struct VirtioDeviceStub { pub dev: Box, pub jail: Option, } /// Trait which is implemented for each Linux Architecture in order to /// set up the memory, cpus, and system devices and to boot the kernel. pub trait LinuxArch { type Error: StdError; /// Takes `VmComponents` and generates a `RunnableLinuxVm`. /// /// # Arguments /// /// * `components` - Parts to use to build the VM. /// * `split_irqchip` - whether to use a split IRQ chip (i.e. userspace PIT/PIC/IOAPIC) /// * `serial_parameters` - definitions for how the serial devices should be configured. /// * `create_devices` - Function to generate a list of devices. fn build_vm( components: VmComponents, split_irqchip: bool, serial_parameters: &BTreeMap, create_devices: F, ) -> Result where F: FnOnce( &GuestMemory, &mut Vm, &mut SystemAllocator, &EventFd, ) -> Result, Option)>, E>, E: StdError + 'static; } /// Errors for device manager. #[derive(Debug)] pub enum DeviceRegistrationError { /// Could not allocate IO space for the device. AllocateIoAddrs(PciDeviceError), /// Could not allocate device address space for the device. AllocateDeviceAddrs(PciDeviceError), /// Could not allocate an IRQ number. AllocateIrq, /// Could not create the mmio device to wrap a VirtioDevice. CreateMmioDevice(sys_util::Error), // Unable to create serial device from serial parameters CreateSerialDevice(devices::SerialError), /// Could not create an event fd. EventFdCreate(sys_util::Error), /// Could not add a device to the mmio bus. MmioInsert(BusError), /// Failed to register ioevent with VM. RegisterIoevent(sys_util::Error), /// Failed to register irq eventfd with VM. RegisterIrqfd(sys_util::Error), /// Failed to initialize proxy device for jailed device. ProxyDeviceCreation(devices::ProxyError), /// Appending to kernel command line failed. Cmdline(kernel_cmdline::Error), /// No more IRQs are available. IrqsExhausted, /// No more MMIO space available. AddrsExhausted, /// Could not register PCI device capabilities. RegisterDeviceCapabilities(PciDeviceError), } impl Display for DeviceRegistrationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::DeviceRegistrationError::*; match self { AllocateIoAddrs(e) => write!(f, "Allocating IO addresses: {}", e), AllocateDeviceAddrs(e) => write!(f, "Allocating device addresses: {}", e), AllocateIrq => write!(f, "Allocating IRQ number"), CreateMmioDevice(e) => write!(f, "failed to create mmio device: {}", e), CreateSerialDevice(e) => write!(f, "failed to create serial device: {}", e), Cmdline(e) => write!(f, "unable to add device to kernel command line: {}", e), EventFdCreate(e) => write!(f, "failed to create eventfd: {}", e), MmioInsert(e) => write!(f, "failed to add to mmio bus: {}", e), RegisterIoevent(e) => write!(f, "failed to register ioevent to VM: {}", e), RegisterIrqfd(e) => write!(f, "failed to register irq eventfd to VM: {}", e), ProxyDeviceCreation(e) => write!(f, "failed to create proxy device: {}", e), IrqsExhausted => write!(f, "no more IRQs are available"), AddrsExhausted => write!(f, "no more addresses are available"), RegisterDeviceCapabilities(e) => { write!(f, "could not register PCI device capabilities: {}", e) } } } } /// Creates a root PCI device for use by this Vm. pub fn generate_pci_root( devices: Vec<(Box, Option)>, mmio_bus: &mut Bus, resources: &mut SystemAllocator, vm: &mut Vm, ) -> Result<(PciRoot, Vec<(u32, PciInterruptPin)>, BTreeMap), DeviceRegistrationError> { let mut root = PciRoot::new(); let mut pci_irqs = Vec::new(); let mut pid_labels = BTreeMap::new(); for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() { // Only support one bus. device.assign_bus_dev(0, dev_idx as u8); let mut keep_fds = device.keep_fds(); syslog::push_fds(&mut keep_fds); let irqfd = EventFd::new().map_err(DeviceRegistrationError::EventFdCreate)?; let irq_resample_fd = EventFd::new().map_err(DeviceRegistrationError::EventFdCreate)?; let irq_num = resources .allocate_irq() .ok_or(DeviceRegistrationError::AllocateIrq)? as u32; let pci_irq_pin = match dev_idx % 4 { 0 => PciInterruptPin::IntA, 1 => PciInterruptPin::IntB, 2 => PciInterruptPin::IntC, 3 => PciInterruptPin::IntD, _ => panic!(""), // Obviously not possible, but the compiler is not smart enough. }; vm.register_irqfd_resample(&irqfd, &irq_resample_fd, irq_num) .map_err(DeviceRegistrationError::RegisterIrqfd)?; keep_fds.push(irqfd.as_raw_fd()); keep_fds.push(irq_resample_fd.as_raw_fd()); device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin); pci_irqs.push((dev_idx as u32, pci_irq_pin)); let ranges = device .allocate_io_bars(resources) .map_err(DeviceRegistrationError::AllocateIoAddrs)?; let device_ranges = device .allocate_device_bars(resources) .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?; device .register_device_capabilities() .map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?; for (event, addr, datamatch) in device.ioeventfds() { let io_addr = IoeventAddress::Mmio(addr); vm.register_ioevent(&event, io_addr, datamatch) .map_err(DeviceRegistrationError::RegisterIoevent)?; keep_fds.push(event.as_raw_fd()); } let arced_dev: Arc> = if let Some(jail) = jail { let proxy = ProxyDevice::new(device, &jail, keep_fds) .map_err(DeviceRegistrationError::ProxyDeviceCreation)?; pid_labels.insert(proxy.pid() as u32, proxy.debug_label()); Arc::new(Mutex::new(proxy)) } else { device.on_sandboxed(); Arc::new(Mutex::new(device)) }; root.add_device(arced_dev.clone()); for range in &ranges { mmio_bus .insert(arced_dev.clone(), range.0, range.1, true) .map_err(DeviceRegistrationError::MmioInsert)?; } for range in &device_ranges { mmio_bus .insert(arced_dev.clone(), range.0, range.1, true) .map_err(DeviceRegistrationError::MmioInsert)?; } } Ok((root, pci_irqs, pid_labels)) } /// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial /// port number and serial device to be used for stdout if defined. /// /// # Arguments /// /// * `io_bus` - Bus to add the devices to /// * `com_evt_1_3` - eventfd for com1 and com3 /// * `com_evt_1_4` - eventfd for com2 and com4 /// * `io_bus` - Bus to add the devices to /// * `serial_parameters` - definitions of serial parameter configuationis. If a setting is not /// provided for a port, then it will use the default configuation. pub fn add_serial_devices( io_bus: &mut Bus, com_evt_1_3: &EventFd, com_evt_2_4: &EventFd, serial_parameters: &BTreeMap, ) -> Result<(Option, Option>>), DeviceRegistrationError> { let mut stdio_serial_num = None; let mut stdio_serial = None; for x in 0..=3 { let com_evt = match x { 0 => com_evt_1_3, 1 => com_evt_2_4, 2 => com_evt_1_3, 3 => com_evt_2_4, _ => com_evt_1_3, }; let param = serial_parameters .get(&(x + 1)) .unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]); let com = Arc::new(Mutex::new( param .create_serial_device(&com_evt) .map_err(DeviceRegistrationError::CreateSerialDevice)?, )); io_bus .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false) .unwrap(); if param.console { stdio_serial_num = Some(x + 1); } if param.stdin { stdio_serial = Some(com.clone()); } } Ok((stdio_serial_num, stdio_serial)) } /// Errors for image loading. #[derive(Debug)] pub enum LoadImageError { BadAlignment(u64), Seek(io::Error), ImageSizeTooLarge(u64), ReadToMemory(GuestMemoryError), } impl Display for LoadImageError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::LoadImageError::*; match self { BadAlignment(a) => write!(f, "Alignment not a power of two: {}", a), Seek(e) => write!(f, "Seek failed: {}", e), ImageSizeTooLarge(size) => write!(f, "Image size too large: {}", size), ReadToMemory(e) => write!(f, "Reading image into memory failed: {}", e), } } } /// Load an image from a file into guest memory. /// /// # Arguments /// /// * `guest_mem` - The memory to be used by the guest. /// * `guest_addr` - The starting address to load the image in the guest memory. /// * `max_size` - The amount of space in bytes available in the guest memory for the image. /// * `image` - The file containing the image to be loaded. /// /// The size in bytes of the loaded image is returned. pub fn load_image( guest_mem: &GuestMemory, image: &mut F, guest_addr: GuestAddress, max_size: u64, ) -> Result where F: Read + Seek + AsRawFd, { let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?; if size > usize::max_value() as u64 || size > max_size { return Err(LoadImageError::ImageSizeTooLarge(size)); } // This is safe due to the bounds check above. let size = size as usize; image .seek(SeekFrom::Start(0)) .map_err(LoadImageError::Seek)?; guest_mem .read_to_memory(guest_addr, image, size) .map_err(LoadImageError::ReadToMemory)?; Ok(size) } /// Load an image from a file into guest memory at the highest possible address. /// /// # Arguments /// /// * `guest_mem` - The memory to be used by the guest. /// * `image` - The file containing the image to be loaded. /// * `min_guest_addr` - The minimum address of the start of the image. /// * `max_guest_addr` - The address to load the last byte of the image. /// * `align` - The minimum alignment of the start address of the image in bytes /// (must be a power of two). /// /// The guest address and size in bytes of the loaded image are returned. pub fn load_image_high( guest_mem: &GuestMemory, image: &mut F, min_guest_addr: GuestAddress, max_guest_addr: GuestAddress, align: u64, ) -> Result<(GuestAddress, usize), LoadImageError> where F: Read + Seek + AsRawFd, { if !align.is_power_of_two() { return Err(LoadImageError::BadAlignment(align)); } let max_size = max_guest_addr.offset_from(min_guest_addr) & !(align - 1); let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?; if size > usize::max_value() as u64 || size > max_size { return Err(LoadImageError::ImageSizeTooLarge(size)); } image .seek(SeekFrom::Start(0)) .map_err(LoadImageError::Seek)?; // Load image at the maximum aligned address allowed. // The subtraction cannot underflow because of the size checks above. let guest_addr = GuestAddress((max_guest_addr.offset() - size) & !(align - 1)); // This is safe due to the bounds check above. let size = size as usize; guest_mem .read_to_memory(guest_addr, image, size) .map_err(LoadImageError::ReadToMemory)?; Ok((guest_addr, size)) }