2018-02-14 06:09:43 +00:00
|
|
|
// 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.
|
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
extern crate io_jail;
|
2018-02-14 06:09:43 +00:00
|
|
|
extern crate sys_util;
|
2018-05-18 01:47:11 +00:00
|
|
|
extern crate resources;
|
2018-02-14 06:09:43 +00:00
|
|
|
extern crate kernel_cmdline;
|
|
|
|
extern crate kvm;
|
|
|
|
extern crate libc;
|
|
|
|
extern crate devices;
|
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
use std::fmt;
|
2018-02-14 06:09:43 +00:00
|
|
|
use std::fs::File;
|
2018-07-24 00:58:09 +00:00
|
|
|
use std::os::unix::io::{AsRawFd, RawFd};
|
2018-02-14 06:09:43 +00:00
|
|
|
use std::result;
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
use devices::{Bus, PciDeviceList, Serial};
|
|
|
|
use devices::virtio::VirtioDevice;
|
|
|
|
use io_jail::Minijail;
|
|
|
|
use kvm::{IoeventAddress, Kvm, Vm, Vcpu};
|
|
|
|
use sys_util::{EventFd, GuestMemory, syslog};
|
2018-05-18 01:47:11 +00:00
|
|
|
use resources::SystemAllocator;
|
2018-02-14 06:09:43 +00:00
|
|
|
|
|
|
|
pub type Result<T> = result::Result<T, Box<std::error::Error>>;
|
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
/// 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 pci_devices: PciDeviceList,
|
|
|
|
pub memory_mb: u64,
|
|
|
|
pub vcpu_count: u32,
|
|
|
|
pub kernel_image: File,
|
|
|
|
pub extra_kernel_params: Vec<String>,
|
|
|
|
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: Arc<Mutex<Serial>>,
|
|
|
|
pub exit_evt: EventFd,
|
|
|
|
pub vcpus: Vec<Vcpu>,
|
|
|
|
pub irq_chip: Option<File>,
|
|
|
|
pub io_bus: Bus,
|
|
|
|
pub mmio_bus: Bus,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The device and optional jail.
|
|
|
|
pub struct VirtioDeviceStub {
|
|
|
|
pub dev: Box<VirtioDevice>,
|
|
|
|
pub jail: Option<Minijail>,
|
|
|
|
}
|
|
|
|
|
2018-02-14 06:09:43 +00:00
|
|
|
/// 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 {
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Takes `VmComponents` and generates a `RunnableLinuxVm`.
|
2018-02-14 06:09:43 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2018-07-24 00:58:09 +00:00
|
|
|
/// * `components` - Parts to use to build the VM.
|
|
|
|
/// * `virtio_devs` - Function to generate a list of virtio devices.
|
|
|
|
fn build_vm<F>(components: VmComponents, virtio_devs: F) -> Result<RunnableLinuxVm>
|
|
|
|
where
|
|
|
|
F: FnOnce(&GuestMemory, &EventFd) -> Result<Vec<VirtioDeviceStub>>;
|
|
|
|
}
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Errors for device manager.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum MmioRegisterError {
|
|
|
|
/// Could not create the mmio device to wrap a VirtioDevice.
|
|
|
|
CreateMmioDevice(sys_util::Error),
|
|
|
|
/// 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,
|
|
|
|
}
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
impl fmt::Display for MmioRegisterError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
&MmioRegisterError::CreateMmioDevice(ref e) => write!(f, "failed to create mmio device: {:?}", e),
|
|
|
|
&MmioRegisterError::Cmdline(ref e) => {
|
|
|
|
write!(f, "unable to add device to kernel command line: {}", e)
|
|
|
|
}
|
|
|
|
&MmioRegisterError::RegisterIoevent(ref e) => {
|
|
|
|
write!(f, "failed to register ioevent to VM: {:?}", e)
|
|
|
|
}
|
|
|
|
&MmioRegisterError::RegisterIrqfd(ref e) => {
|
|
|
|
write!(f, "failed to register irq eventfd to VM: {:?}", e)
|
|
|
|
}
|
|
|
|
&MmioRegisterError::ProxyDeviceCreation(ref e) => write!(f, "failed to create proxy device: {}", e),
|
|
|
|
&MmioRegisterError::IrqsExhausted => write!(f, "no more IRQs are available"),
|
|
|
|
&MmioRegisterError::AddrsExhausted => write!(f, "no more addresses are available"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-18 01:47:11 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Register a device to be used via MMIO transport.
|
|
|
|
pub fn register_mmio(bus: &mut devices::Bus,
|
|
|
|
vm: &mut Vm,
|
|
|
|
device: Box<devices::virtio::VirtioDevice>,
|
|
|
|
jail: Option<Minijail>,
|
|
|
|
resources: &mut SystemAllocator,
|
|
|
|
cmdline: &mut kernel_cmdline::Cmdline)
|
|
|
|
-> std::result::Result<(), MmioRegisterError> {
|
|
|
|
let irq = match resources.allocate_irq() {
|
|
|
|
None => return Err(MmioRegisterError::IrqsExhausted),
|
|
|
|
Some(i) => i,
|
|
|
|
};
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
// List of FDs to keep open in the child after it forks.
|
|
|
|
let mut keep_fds: Vec<RawFd> = device.keep_fds();
|
|
|
|
syslog::push_fds(&mut keep_fds);
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
let mmio_device = devices::virtio::MmioDevice::new((*vm.get_memory()).clone(),
|
|
|
|
device)
|
|
|
|
.map_err(MmioRegisterError::CreateMmioDevice)?;
|
|
|
|
let mmio_len = 0x1000; // TODO(dgreid) - configurable per arch?
|
|
|
|
let mmio_base = resources.allocate_mmio_addresses(mmio_len)
|
|
|
|
.ok_or(MmioRegisterError::AddrsExhausted)?;
|
|
|
|
for (i, queue_evt) in mmio_device.queue_evts().iter().enumerate() {
|
|
|
|
let io_addr = IoeventAddress::Mmio(mmio_base +
|
|
|
|
devices::virtio::NOTIFY_REG_OFFSET as u64);
|
|
|
|
vm.register_ioevent(&queue_evt, io_addr, i as u32)
|
|
|
|
.map_err(MmioRegisterError::RegisterIoevent)?;
|
|
|
|
keep_fds.push(queue_evt.as_raw_fd());
|
|
|
|
}
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
if let Some(interrupt_evt) = mmio_device.interrupt_evt() {
|
|
|
|
vm.register_irqfd(&interrupt_evt, irq)
|
|
|
|
.map_err(MmioRegisterError::RegisterIrqfd)?;
|
|
|
|
keep_fds.push(interrupt_evt.as_raw_fd());
|
|
|
|
}
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
if let Some(jail) = jail {
|
|
|
|
let proxy_dev = devices::ProxyDevice::new(mmio_device, &jail, keep_fds)
|
|
|
|
.map_err(MmioRegisterError::ProxyDeviceCreation)?;
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
bus.insert(Arc::new(Mutex::new(proxy_dev)), mmio_base, mmio_len, false).unwrap();
|
|
|
|
} else {
|
|
|
|
bus.insert(Arc::new(Mutex::new(mmio_device)), mmio_base, mmio_len, false).unwrap();
|
|
|
|
}
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
cmdline
|
|
|
|
.insert("virtio_mmio.device",
|
|
|
|
&format!("4K@0x{:08x}:{}", mmio_base, irq))
|
|
|
|
.map_err(MmioRegisterError::Cmdline)?;
|
|
|
|
|
|
|
|
Ok(())
|
2018-02-14 06:09:43 +00:00
|
|
|
}
|