diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index cb39897582..e78ee55c8a 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -107,8 +107,6 @@ const AARCH64_IRQ_BASE: u32 = 1; #[derive(Debug)] pub enum Error { - /// Error Adding a PCI device. - AddPciDev(devices::PciRootError), /// Unable to clone an EventFd CloneEventFd(sys_util::Error), /// Error creating kernel command line. @@ -118,7 +116,7 @@ pub enum Error { /// Unable to create Kvm. CreateKvm(sys_util::Error), /// Unable to create a PciRoot hub. - CreatePciRoot(devices::PciRootError), + CreatePciRoot(arch::DeviceRegistrationError), /// Unable to create socket. CreateSocket(io::Error), /// Unable to create Vcpu. @@ -130,7 +128,7 @@ pub enum Error { /// Failure to Create GIC CreateGICFailure(sys_util::Error), /// Couldn't register virtio socket. - RegisterVsock(arch::MmioRegisterError), + RegisterVsock(arch::DeviceRegistrationError), /// VCPU Init failed VCPUInitFailure, /// VCPU Set one reg failed @@ -140,7 +138,6 @@ pub enum Error { impl error::Error for Error { fn description(&self) -> &str { match self { - &Error::AddPciDev(_) => "Failed to add device to PCI", &Error::CloneEventFd(_) => "Unable to clone an EventFd", &Error::Cmdline(_) => "the given kernel command line was invalid", &Error::CreateEventFd(_) => "Unable to make an EventFd", @@ -210,7 +207,9 @@ impl arch::LinuxArch for AArch64 { let mut mmio_bus = devices::Bus::new(); - let (pci, pci_irqs) = components.pci_devices.generate_root(&mut mmio_bus, &mut resources) + let (pci, pci_irqs) = arch::generate_pci_root(components.pci_devices, + &mut mmio_bus, + &mut resources) .map_err(Error::CreatePciRoot)?; let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?; diff --git a/arch/src/lib.rs b/arch/src/lib.rs index d7a1a635e3..6cd4dde8d4 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -16,7 +16,8 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::result; use std::sync::{Arc, Mutex}; -use devices::{Bus, PciDeviceList, Serial}; +use devices::{Bus, BusError, PciDevice, PciDeviceError, PciInterruptPin, + PciRoot, ProxyDevice, Serial}; use devices::virtio::VirtioDevice; use io_jail::Minijail; use kvm::{IoeventAddress, Kvm, Vm, Vcpu}; @@ -28,7 +29,7 @@ pub type Result = result::Result>; /// 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 pci_devices: Vec<(Box, Minijail)>, pub memory_mb: u64, pub vcpu_count: u32, pub kernel_image: File, @@ -71,9 +72,17 @@ pub trait LinuxArch { /// Errors for device manager. #[derive(Debug)] -pub enum MmioRegisterError { +pub enum DeviceRegistrationError { + /// Could not allocate IO space for the device. + AllocateIoAddrs(PciDeviceError), + /// Could not allocate an IRQ number. + AllocateIrq, /// Could not create the mmio device to wrap a VirtioDevice. CreateMmioDevice(sys_util::Error), + /// 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. @@ -88,26 +97,81 @@ pub enum MmioRegisterError { AddrsExhausted, } -impl fmt::Display for MmioRegisterError { +impl fmt::Display for DeviceRegistrationError { 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) => { + &DeviceRegistrationError::AllocateIoAddrs(ref e) => { + write!(f, "Allocating IO addresses: {:?}", e) + } + &DeviceRegistrationError::AllocateIrq => { + write!(f, "Allocating IRQ number") + } + &DeviceRegistrationError::CreateMmioDevice(ref e) => { + write!(f, "failed to create mmio device: {:?}", e) + } + &DeviceRegistrationError::Cmdline(ref e) => { write!(f, "unable to add device to kernel command line: {}", e) } - &MmioRegisterError::RegisterIoevent(ref e) => { + &DeviceRegistrationError::EventFdCreate(ref e) => { + write!(f, "failed to create eventfd: {:?}", e) + } + &DeviceRegistrationError::MmioInsert(ref e) => { + write!(f, "failed to add to mmio bus: {:?}", e) + } + &DeviceRegistrationError::RegisterIoevent(ref e) => { write!(f, "failed to register ioevent to VM: {:?}", e) } - &MmioRegisterError::RegisterIrqfd(ref e) => { + &DeviceRegistrationError::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"), + &DeviceRegistrationError::ProxyDeviceCreation(ref e) => { + write!(f, "failed to create proxy device: {}", e) + } + &DeviceRegistrationError::IrqsExhausted => write!(f, "no more IRQs are available"), + &DeviceRegistrationError::AddrsExhausted => write!(f, "no more addresses are available"), } } } +/// Creates a root PCI device for use by this Vm. +pub fn generate_pci_root(devices: Vec<(Box, Minijail)>, + mmio_bus: &mut Bus, + resources: &mut SystemAllocator) + -> std::result::Result<(PciRoot, Vec<(u32, PciInterruptPin)>), DeviceRegistrationError> +{ + let mut root = PciRoot::new(); + let mut pci_irqs = Vec::new(); + for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() { + let mut keep_fds = Vec::new(); + syslog::push_fds(&mut keep_fds); + + let irqfd = 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. + }; + device.assign_irq(irqfd, irq_num, pci_irq_pin); + pci_irqs.push((irq_num, pci_irq_pin)); + + let ranges = device + .allocate_io_bars(resources) + .map_err(DeviceRegistrationError::AllocateIoAddrs)?; + let proxy = ProxyDevice::new(device, &jail, keep_fds) + .map_err(DeviceRegistrationError::ProxyDeviceCreation)?; + let arced_dev = Arc::new(Mutex::new(proxy)); + 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)?; + } + } + Ok((root, pci_irqs)) +} + /// Register a device to be used via MMIO transport. pub fn register_mmio(bus: &mut devices::Bus, vm: &mut Vm, @@ -115,9 +179,9 @@ pub fn register_mmio(bus: &mut devices::Bus, jail: Option, resources: &mut SystemAllocator, cmdline: &mut kernel_cmdline::Cmdline) - -> std::result::Result<(), MmioRegisterError> { + -> std::result::Result<(), DeviceRegistrationError> { let irq = match resources.allocate_irq() { - None => return Err(MmioRegisterError::IrqsExhausted), + None => return Err(DeviceRegistrationError::IrqsExhausted), Some(i) => i, }; @@ -125,29 +189,28 @@ pub fn register_mmio(bus: &mut devices::Bus, let mut keep_fds: Vec = device.keep_fds(); syslog::push_fds(&mut keep_fds); - let mmio_device = devices::virtio::MmioDevice::new((*vm.get_memory()).clone(), - device) - .map_err(MmioRegisterError::CreateMmioDevice)?; + let mmio_device = devices::virtio::MmioDevice::new((*vm.get_memory()).clone(), device) + .map_err(DeviceRegistrationError::CreateMmioDevice)?; let mmio_len = 0x1000; // TODO(dgreid) - configurable per arch? let mmio_base = resources.allocate_mmio_addresses(mmio_len) - .ok_or(MmioRegisterError::AddrsExhausted)?; + .ok_or(DeviceRegistrationError::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)?; + .map_err(DeviceRegistrationError::RegisterIoevent)?; keep_fds.push(queue_evt.as_raw_fd()); } if let Some(interrupt_evt) = mmio_device.interrupt_evt() { vm.register_irqfd(&interrupt_evt, irq) - .map_err(MmioRegisterError::RegisterIrqfd)?; + .map_err(DeviceRegistrationError::RegisterIrqfd)?; keep_fds.push(interrupt_evt.as_raw_fd()); } if let Some(jail) = jail { let proxy_dev = devices::ProxyDevice::new(mmio_device, &jail, keep_fds) - .map_err(MmioRegisterError::ProxyDeviceCreation)?; + .map_err(DeviceRegistrationError::ProxyDeviceCreation)?; bus.insert(Arc::new(Mutex::new(proxy_dev)), mmio_base, mmio_len, false).unwrap(); } else { @@ -157,7 +220,7 @@ pub fn register_mmio(bus: &mut devices::Bus, cmdline .insert("virtio_mmio.device", &format!("4K@0x{:08x}:{}", mmio_base, irq)) - .map_err(MmioRegisterError::Cmdline)?; + .map_err(DeviceRegistrationError::Cmdline)?; Ok(()) } diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 1478849454..46664fc2cf 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -28,11 +28,11 @@ pub mod pl030; pub mod virtio; pub use self::bus::{Bus, BusDevice, BusRange}; +pub use self::bus::Error as BusError; pub use self::cmos::Cmos; pub use self::pl030::Pl030; pub use self::i8042::I8042Device; -pub use self::pci::{PciDevice, PciDeviceList, PciInterruptPin, PciRoot}; -pub use self::pci::PciRootError as PciRootError; +pub use self::pci::{PciDevice, PciDeviceError, PciInterruptPin, PciRoot}; pub use self::proxy::ProxyDevice; pub use self::proxy::Error as ProxyError; pub use self::serial::Serial; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index f5e5d1f06f..a1d211d798 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -8,9 +8,10 @@ mod pci_configuration; mod pci_device; mod pci_root; +pub use self::pci_configuration::{PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciHeaderType, PciSubclass}; +pub use self::pci_device::Error as PciDeviceError; pub use self::pci_device::PciDevice; -pub use self::pci_root::Error as PciRootError; -pub use self::pci_root::{PciDeviceList, PciRoot}; +pub use self::pci_root::PciRoot; /// PCI has four interrupt pins A->D. #[derive(Copy, Clone)] diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs index e0487438fb..7988a9f58f 100644 --- a/devices/src/pci/pci_root.rs +++ b/devices/src/pci/pci_root.rs @@ -2,73 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std; use std::sync::{Arc, Mutex}; use byteorder::{ByteOrder, LittleEndian}; -use io_jail::Minijail; -use sys_util::{self, EventFd}; -use resources::SystemAllocator; - -use Bus; use BusDevice; -use bus::Error as BusError; -use proxy::Error as ProxyError; use ProxyDevice; use pci::pci_configuration::{PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType}; -use pci::pci_device::{self, PciDevice}; -use pci::PciInterruptPin; - -#[derive(Debug)] -pub enum Error { - CreateEventFd(sys_util::Error), - MmioRegistration(BusError), - ProxyCreation(ProxyError), - DeviceIoSpaceAllocation(pci_device::Error), -} -pub type Result = std::result::Result; - -/// Contains the devices that will be on a PCI bus. Used to configure a PCI bus before adding it to -/// a VM. Use `generate_hub` to produce a PciRoot for use in a Vm. -pub struct PciDeviceList { - devices: Vec<(Box, Minijail)>, -} - -impl PciDeviceList { - pub fn new() -> Self { - PciDeviceList { - devices: Vec::new(), - } - } - - pub fn add_device(&mut self, device: Box, jail: Minijail) { - self.devices.push((device, jail)); - } - - pub fn generate_root(self, mmio_bus: &mut Bus, resources: &mut SystemAllocator) - -> Result<(PciRoot, Vec<(u32, PciInterruptPin)>)> { - let mut root = PciRoot::new(); - let mut pci_irqs = Vec::new(); - for (dev_idx, (mut device, jail)) in self.devices.into_iter().enumerate() { - let irqfd = EventFd::new().map_err(Error::CreateEventFd)?; - let irq_num = resources.allocate_irq().unwrap() 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. - }; - device.assign_irq(irqfd, irq_num, pci_irq_pin); - pci_irqs.push((irq_num, pci_irq_pin)); - root.add_device(device, &jail, mmio_bus, resources)?; - } - Ok((root, pci_irqs)) - } -} +use pci::pci_device::PciDevice; // A PciDevice that holds the root hub's configuration. struct PciRootConfiguration { @@ -101,7 +44,7 @@ pub struct PciRoot { impl PciRoot { /// Create an empty PCI root bus. - fn new() -> Self { + pub fn new() -> Self { PciRoot { root_configuration: PciRootConfiguration { config: PciConfiguration::new( @@ -120,21 +63,8 @@ impl PciRoot { } /// Add a `device` to this root PCI bus. - pub fn add_device(&mut self, mut device: D, jail: &Minijail, - mmio_bus: &mut Bus, // TODO - move to resources or something. - resources: &mut SystemAllocator) -> Result<()> { - let ranges = device - .allocate_io_bars(resources) - .map_err(Error::DeviceIoSpaceAllocation)?; - let proxy = ProxyDevice::new(device, &jail, Vec::new()) - .map_err(Error::ProxyCreation)?; - let arced_dev = Arc::new(Mutex::new(proxy)); - for range in &ranges { - mmio_bus.insert(arced_dev.clone(), range.0, range.1, true) - .map_err(Error::MmioRegistration)?; - } - self.devices.push(arced_dev); - Ok(()) + pub fn add_device(&mut self, device: Arc>) { + self.devices.push(device); } fn config_space_read(&self) -> u32 { diff --git a/src/linux.rs b/src/linux.rs index 5b466630e0..1496998ca8 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -25,7 +25,7 @@ use rand::thread_rng; use rand::distributions::{IndependentSample, Range}; use byteorder::{ByteOrder, LittleEndian}; -use devices; +use devices::{self, PciDevice}; use io_jail::{self, Minijail}; use kvm::*; use net_util::Tap; @@ -75,14 +75,14 @@ pub enum Error { QcowDeviceCreate(qcow::Error), ReadLowmemAvailable(io::Error), ReadLowmemMargin(io::Error), - RegisterBalloon(arch::MmioRegisterError), - RegisterBlock(arch::MmioRegisterError), - RegisterGpu(arch::MmioRegisterError), - RegisterNet(arch::MmioRegisterError), - RegisterP9(arch::MmioRegisterError), - RegisterRng(arch::MmioRegisterError), + RegisterBalloon(arch::DeviceRegistrationError), + RegisterBlock(arch::DeviceRegistrationError), + RegisterGpu(arch::DeviceRegistrationError), + RegisterNet(arch::DeviceRegistrationError), + RegisterP9(arch::DeviceRegistrationError), + RegisterRng(arch::DeviceRegistrationError), RegisterSignalHandler(sys_util::Error), - RegisterWayland(arch::MmioRegisterError), + RegisterWayland(arch::DeviceRegistrationError), ResetTimerFd(sys_util::Error), RngDeviceNew(devices::virtio::RngError), SettingGidMap(io_jail::Error), @@ -695,7 +695,7 @@ pub fn run_config(cfg: Config) -> Result<()> { info!("crosvm entering multiprocess mode"); } - let pci_devices = devices::PciDeviceList::new(); + let pci_devices: Vec<(Box, Minijail)> = Vec::new(); // Masking signals is inherently dangerous, since this can persist across clones/execs. Do this // before any jailed devices have been spawned, so that we can catch any of them that fail very diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index fe7de3b3af..bee8bff623 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -77,8 +77,6 @@ use kvm::*; #[derive(Debug)] pub enum Error { - /// Error Adding a PCI device. - AddPciDev(devices::PciRootError), /// Error configuring the system ConfigureSystem, /// Unable to clone an EventFd @@ -90,7 +88,7 @@ pub enum Error { /// Unable to create Kvm. CreateKvm(sys_util::Error), /// Unable to create a PciRoot hub. - CreatePciRoot(devices::PciRootError), + CreatePciRoot(arch::DeviceRegistrationError), /// Unable to create socket. CreateSocket(io::Error), /// Unable to create Vcpu. @@ -100,7 +98,7 @@ pub enum Error { /// Error registering an IrqFd RegisterIrqfd(sys_util::Error), /// Couldn't register virtio socket. - RegisterVsock(arch::MmioRegisterError), + RegisterVsock(arch::DeviceRegistrationError), LoadCmdline(kernel_loader::Error), LoadKernel(kernel_loader::Error), /// Error writing the zero page of guest memory. @@ -114,7 +112,6 @@ pub enum Error { impl error::Error for Error { fn description(&self) -> &str { match self { - &Error::AddPciDev(_) => "Failed to add device to PCI", &Error::ConfigureSystem => "Error configuring the system", &Error::CloneEventFd(_) => "Unable to clone an EventFd", &Error::Cmdline(_) => "the given kernel command line was invalid", @@ -278,7 +275,9 @@ impl arch::LinuxArch for X8664arch { let mut mmio_bus = devices::Bus::new(); - let (pci, pci_irqs) = components.pci_devices.generate_root(&mut mmio_bus, &mut resources) + let (pci, pci_irqs) = arch::generate_pci_root(components.pci_devices, + &mut mmio_bus, + &mut resources) .map_err(Error::CreatePciRoot)?; let pci_bus = Arc::new(Mutex::new(pci));