2017-06-21 22:04:16 +00:00
|
|
|
// Copyright 2017 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.
|
|
|
|
|
|
|
|
//! Manages jailing and connecting virtio devices to the system bus.
|
|
|
|
|
|
|
|
use std::fmt;
|
|
|
|
use std::os::unix::io::{AsRawFd, RawFd};
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
|
|
use io_jail::Minijail;
|
2017-12-13 04:19:25 +00:00
|
|
|
use kvm::{Vm, IoeventAddress};
|
2017-10-13 20:18:00 +00:00
|
|
|
use sys_util::{GuestMemory, syslog};
|
2017-08-04 22:12:58 +00:00
|
|
|
use sys_util;
|
2017-06-21 22:04:16 +00:00
|
|
|
|
2017-10-06 22:26:46 +00:00
|
|
|
use devices;
|
2017-06-21 22:04:16 +00:00
|
|
|
use kernel_cmdline;
|
|
|
|
|
|
|
|
/// Errors for device manager.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
/// Could not create the mmio device to wrap a VirtioDevice.
|
|
|
|
CreateMmioDevice(sys_util::Error),
|
2017-12-13 04:19:25 +00:00
|
|
|
/// Failed to register ioevent with VM.
|
|
|
|
RegisterIoevent(sys_util::Error),
|
|
|
|
/// Failed to register irq eventfd with VM.
|
|
|
|
RegisterIrqfd(sys_util::Error),
|
2017-06-21 22:04:16 +00:00
|
|
|
/// Failed to initialize proxy device for jailed device.
|
2017-10-13 20:18:00 +00:00
|
|
|
ProxyDeviceCreation(devices::ProxyError),
|
2017-06-21 22:04:16 +00:00
|
|
|
/// Appending to kernel command line failed.
|
|
|
|
Cmdline(kernel_cmdline::Error),
|
|
|
|
/// No more IRQs are available.
|
|
|
|
IrqsExhausted,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
&Error::CreateMmioDevice(ref e) => write!(f, "failed to create mmio device: {:?}", e),
|
2017-12-13 04:19:25 +00:00
|
|
|
&Error::RegisterIoevent(ref e) => {
|
|
|
|
write!(f, "failed to register ioevent to VM: {:?}", e)
|
|
|
|
}
|
|
|
|
&Error::RegisterIrqfd(ref e) => {
|
|
|
|
write!(f, "failed to register irq eventfd to VM: {:?}", e)
|
|
|
|
}
|
2017-06-21 22:04:16 +00:00
|
|
|
&Error::ProxyDeviceCreation(ref e) => write!(f, "failed to create proxy device: {}", e),
|
|
|
|
&Error::Cmdline(ref e) => {
|
|
|
|
write!(f, "unable to add device to kernel command line: {}", e)
|
|
|
|
}
|
2017-12-13 04:19:25 +00:00
|
|
|
&Error::IrqsExhausted => write!(f, "no more IRQs are available"),
|
2017-06-21 22:04:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Result<T> = ::std::result::Result<T, Error>;
|
|
|
|
|
|
|
|
const MAX_IRQ: u32 = 15;
|
|
|
|
|
|
|
|
/// Manages the complexities of adding a device.
|
2017-12-13 04:19:25 +00:00
|
|
|
pub struct DeviceManager<'a> {
|
2017-10-06 22:26:46 +00:00
|
|
|
pub bus: devices::Bus,
|
2017-12-13 04:19:25 +00:00
|
|
|
vm: &'a mut Vm,
|
2017-06-21 22:04:16 +00:00
|
|
|
guest_mem: GuestMemory,
|
|
|
|
mmio_len: u64,
|
|
|
|
mmio_base: u64,
|
|
|
|
irq: u32,
|
|
|
|
}
|
|
|
|
|
2017-12-13 04:19:25 +00:00
|
|
|
impl<'a> DeviceManager<'a> {
|
2017-06-21 22:04:16 +00:00
|
|
|
/// Create a new DeviceManager.
|
2017-12-13 04:19:25 +00:00
|
|
|
pub fn new(vm: &mut Vm,
|
|
|
|
guest_mem: GuestMemory,
|
|
|
|
mmio_len: u64,
|
|
|
|
mmio_base: u64,
|
|
|
|
irq_base: u32)
|
|
|
|
-> DeviceManager {
|
2017-06-21 22:04:16 +00:00
|
|
|
DeviceManager {
|
2017-10-06 22:26:46 +00:00
|
|
|
bus: devices::Bus::new(),
|
2017-12-13 04:19:25 +00:00
|
|
|
vm,
|
|
|
|
guest_mem,
|
|
|
|
mmio_len,
|
|
|
|
mmio_base,
|
|
|
|
irq: irq_base,
|
2017-06-21 22:04:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a device to be used via MMIO transport.
|
|
|
|
pub fn register_mmio(&mut self,
|
2017-10-06 22:26:46 +00:00
|
|
|
device: Box<devices::virtio::VirtioDevice>,
|
2017-06-21 22:04:16 +00:00
|
|
|
jail: Option<Minijail>,
|
|
|
|
cmdline: &mut kernel_cmdline::Cmdline)
|
|
|
|
-> Result<()> {
|
|
|
|
if self.irq > MAX_IRQ {
|
|
|
|
return Err(Error::IrqsExhausted);
|
|
|
|
}
|
|
|
|
|
|
|
|
// List of FDs to keep open in the child after it forks.
|
|
|
|
let mut keep_fds: Vec<RawFd> = device.keep_fds();
|
2017-09-04 22:59:08 +00:00
|
|
|
syslog::push_fds(&mut keep_fds);
|
2017-06-21 22:04:16 +00:00
|
|
|
|
2017-10-06 22:26:46 +00:00
|
|
|
let mmio_device = devices::virtio::MmioDevice::new(self.guest_mem.clone(), device)
|
2017-06-21 22:04:16 +00:00
|
|
|
.map_err(Error::CreateMmioDevice)?;
|
|
|
|
for (i, queue_evt) in mmio_device.queue_evts().iter().enumerate() {
|
|
|
|
let io_addr = IoeventAddress::Mmio(self.mmio_base +
|
2017-10-06 22:26:46 +00:00
|
|
|
devices::virtio::NOITFY_REG_OFFSET as u64);
|
2017-12-13 04:19:25 +00:00
|
|
|
self.vm
|
|
|
|
.register_ioevent(&queue_evt, io_addr, i as u32)
|
|
|
|
.map_err(Error::RegisterIoevent)?;
|
2017-06-21 22:04:16 +00:00
|
|
|
keep_fds.push(queue_evt.as_raw_fd());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(interrupt_evt) = mmio_device.interrupt_evt() {
|
2017-12-13 04:19:25 +00:00
|
|
|
self.vm
|
|
|
|
.register_irqfd(&interrupt_evt, self.irq)
|
|
|
|
.map_err(Error::RegisterIrqfd)?;
|
2017-06-21 22:04:16 +00:00
|
|
|
keep_fds.push(interrupt_evt.as_raw_fd());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(jail) = jail {
|
2017-10-13 20:18:00 +00:00
|
|
|
let proxy_dev = devices::ProxyDevice::new(mmio_device, &jail, keep_fds)
|
2017-12-13 04:19:25 +00:00
|
|
|
.map_err(Error::ProxyDeviceCreation)?;
|
2017-08-24 21:05:48 +00:00
|
|
|
|
2017-06-21 22:04:16 +00:00
|
|
|
self.bus
|
|
|
|
.insert(Arc::new(Mutex::new(proxy_dev)),
|
|
|
|
self.mmio_base,
|
|
|
|
self.mmio_len)
|
|
|
|
.unwrap();
|
|
|
|
} else {
|
|
|
|
self.bus
|
|
|
|
.insert(Arc::new(Mutex::new(mmio_device)),
|
|
|
|
self.mmio_base,
|
|
|
|
self.mmio_len)
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
cmdline
|
|
|
|
.insert("virtio_mmio.device",
|
|
|
|
&format!("4K@0x{:08x}:{}", self.mmio_base, self.irq))
|
|
|
|
.map_err(Error::Cmdline)?;
|
|
|
|
self.mmio_base += self.mmio_len;
|
|
|
|
self.irq += 1;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use std::sync::atomic::AtomicUsize;
|
|
|
|
use std::os::unix::io::RawFd;
|
2017-08-04 22:12:58 +00:00
|
|
|
use sys_util::{EventFd, GuestAddress, GuestMemory};
|
2017-12-13 04:19:25 +00:00
|
|
|
use kvm::*;
|
2017-06-21 22:04:16 +00:00
|
|
|
use device_manager;
|
|
|
|
use kernel_cmdline;
|
2017-10-06 22:26:46 +00:00
|
|
|
use devices;
|
2017-06-21 22:04:16 +00:00
|
|
|
|
|
|
|
const QUEUE_SIZES: &'static [u16] = &[64];
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
struct DummyDevice {
|
|
|
|
dummy: u32,
|
|
|
|
}
|
|
|
|
|
2017-10-06 22:26:46 +00:00
|
|
|
impl devices::virtio::VirtioDevice for DummyDevice {
|
2017-06-21 22:04:16 +00:00
|
|
|
fn keep_fds(&self) -> Vec<RawFd> {
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn device_type(&self) -> u32 {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
|
|
|
fn queue_max_sizes(&self) -> &[u16] {
|
|
|
|
QUEUE_SIZES
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(unused_variables)]
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
fn activate(&mut self,
|
|
|
|
mem: GuestMemory,
|
|
|
|
interrupt_evt: EventFd,
|
|
|
|
status: Arc<AtomicUsize>,
|
2017-10-06 22:26:46 +00:00
|
|
|
queues: Vec<devices::virtio::Queue>,
|
2017-06-21 22:04:16 +00:00
|
|
|
mut queue_evts: Vec<EventFd>) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2017-12-13 04:19:25 +00:00
|
|
|
#[ignore] // no access to /dev/kvm
|
2017-06-21 22:04:16 +00:00
|
|
|
fn register_device() {
|
|
|
|
let start_addr1 = GuestAddress(0x0);
|
|
|
|
let start_addr2 = GuestAddress(0x1000);
|
|
|
|
let guest_mem = GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x1000)])
|
|
|
|
.unwrap();
|
2017-12-13 04:19:25 +00:00
|
|
|
let mut vm = Vm::new(&Kvm::new().unwrap(), guest_mem.clone()).unwrap();
|
2017-06-21 22:04:16 +00:00
|
|
|
let mut device_manager =
|
2017-12-13 04:19:25 +00:00
|
|
|
device_manager::DeviceManager::new(&mut vm, guest_mem, 0x1000, 0xd0000000, 5);
|
2017-06-21 22:04:16 +00:00
|
|
|
|
|
|
|
let mut cmdline = kernel_cmdline::Cmdline::new(4096);
|
|
|
|
let dummy_box = Box::new(DummyDevice { dummy: 0 });
|
|
|
|
device_manager
|
|
|
|
.register_mmio(dummy_box, None, &mut cmdline)
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
}
|