diff options
-rw-r--r-- | arch/src/lib.rs | 3 | ||||
-rw-r--r-- | devices/src/pci/ac97.rs | 30 | ||||
-rw-r--r-- | devices/src/pci/pci_configuration.rs | 4 | ||||
-rw-r--r-- | devices/src/pci/pci_device.rs | 17 | ||||
-rw-r--r-- | devices/src/usb/xhci/xhci_controller.rs | 20 | ||||
-rw-r--r-- | devices/src/virtio/virtio_pci_device.rs | 44 | ||||
-rw-r--r-- | resources/src/address_allocator.rs | 122 | ||||
-rw-r--r-- | resources/src/gpu_allocator.rs | 12 | ||||
-rw-r--r-- | resources/src/lib.rs | 63 | ||||
-rw-r--r-- | resources/src/system_allocator.rs | 62 | ||||
-rw-r--r-- | src/linux.rs | 11 | ||||
-rw-r--r-- | vm_control/src/lib.rs | 11 |
12 files changed, 325 insertions, 74 deletions
diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 01c16a0..032170f 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -146,6 +146,9 @@ pub fn generate_pci_root( 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); diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs index d883894..aeab1f1 100644 --- a/devices/src/pci/ac97.rs +++ b/devices/src/pci/ac97.rs @@ -5,7 +5,7 @@ use std::os::unix::io::RawFd; use audio_streams::StreamSource; -use resources::SystemAllocator; +use resources::{Alloc, SystemAllocator}; use sys_util::{error, EventFd, GuestMemory}; use crate::pci::ac97_bus_master::Ac97BusMaster; @@ -27,6 +27,7 @@ const PCI_DEVICE_ID_INTEL_82801AA_5: u16 = 0x2415; /// the audio backend. pub struct Ac97Dev { config_regs: PciConfiguration, + pci_bus_dev: Option<(u8, u8)>, // The irq events are temporarily saved here. They need to be passed to the device after the // jail forks. This happens when the bus is first written. irq_evt: Option<EventFd>, @@ -52,6 +53,7 @@ impl Ac97Dev { Ac97Dev { config_regs, + pci_bus_dev: None, irq_evt: None, irq_resample_evt: None, bus_master: Ac97BusMaster::new(mem, audio_server), @@ -125,6 +127,10 @@ impl PciDevice for Ac97Dev { "AC97".to_owned() } + fn assign_bus_dev(&mut self, bus: u8, device: u8) { + self.pci_bus_dev = Some((bus, device)); + } + fn assign_irq( &mut self, irq_evt: EventFd, @@ -138,10 +144,18 @@ impl PciDevice for Ac97Dev { } fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> { + let (bus, dev) = self + .pci_bus_dev + .expect("assign_bus_dev must be called prior to allocate_io_bars"); let mut ranges = Vec::new(); let mixer_regs_addr = resources - .allocate_mmio_addresses(MIXER_REGS_SIZE) - .ok_or(pci_device::Error::IoAllocationFailed(MIXER_REGS_SIZE))?; + .mmio_allocator() + .allocate( + MIXER_REGS_SIZE, + Alloc::PciBar { bus, dev, bar: 0 }, + "ac97-mixer_regs".to_string(), + ) + .map_err(|e| pci_device::Error::IoAllocationFailed(MIXER_REGS_SIZE, e))?; let mixer_config = PciBarConfiguration::default() .set_register_index(0) .set_address(mixer_regs_addr) @@ -152,8 +166,13 @@ impl PciDevice for Ac97Dev { ranges.push((mixer_regs_addr, MIXER_REGS_SIZE)); let master_regs_addr = resources - .allocate_mmio_addresses(MASTER_REGS_SIZE) - .ok_or_else(|| pci_device::Error::IoAllocationFailed(MASTER_REGS_SIZE))?; + .mmio_allocator() + .allocate( + MASTER_REGS_SIZE, + Alloc::PciBar { bus, dev, bar: 1 }, + "ac97-master_regs".to_string(), + ) + .map_err(|e| pci_device::Error::IoAllocationFailed(MASTER_REGS_SIZE, e))?; let master_config = PciBarConfiguration::default() .set_register_index(1) .set_address(master_regs_addr) @@ -228,6 +247,7 @@ mod tests { .add_device_addresses(0x3000_0000, 0x1000_0000) .create_allocator(5, false) .unwrap(); + ac97_dev.assign_bus_dev(0, 0); assert!(ac97_dev.allocate_io_bars(&mut allocator).is_ok()); } } diff --git a/devices/src/pci/pci_configuration.rs b/devices/src/pci/pci_configuration.rs index 1cfa6bb..ab969f7 100644 --- a/devices/src/pci/pci_configuration.rs +++ b/devices/src/pci/pci_configuration.rs @@ -490,6 +490,10 @@ impl PciBarConfiguration { self } + pub fn get_register_index(&self) -> usize { + self.reg_idx + } + pub fn set_address(mut self, addr: u64) -> Self { self.addr = addr; self diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs index f8f249b..8ea3548 100644 --- a/devices/src/pci/pci_device.rs +++ b/devices/src/pci/pci_device.rs @@ -9,7 +9,7 @@ use std::fmt::{self, Display}; use std::os::unix::io::RawFd; use kvm::Datamatch; -use resources::SystemAllocator; +use resources::{Error as SystemAllocatorFaliure, SystemAllocator}; use sys_util::EventFd; use crate::pci::pci_configuration::{self, PciConfiguration}; @@ -21,7 +21,7 @@ pub enum Error { /// Setup of the device capabilities failed. CapabilitiesSetup(pci_configuration::Error), /// Allocating space for an IO BAR failed. - IoAllocationFailed(u64), + IoAllocationFailed(u64, SystemAllocatorFaliure), /// Registering an IO BAR failed. IoRegistrationFailed(u64, pci_configuration::Error), } @@ -33,9 +33,11 @@ impl Display for Error { match self { CapabilitiesSetup(e) => write!(f, "failed to add capability {}", e), - IoAllocationFailed(size) => { - write!(f, "failed to allocate space for an IO BAR, size={}", size) - } + IoAllocationFailed(size, e) => write!( + f, + "failed to allocate space for an IO BAR, size={}: {}", + size, e + ), IoRegistrationFailed(addr, e) => { write!(f, "failed to register an IO BAR, addr={} err={}", addr, e) } @@ -46,6 +48,8 @@ impl Display for Error { pub trait PciDevice: Send { /// Returns a label suitable for debug output. fn debug_label(&self) -> String; + /// Assign a unique bus and device number to this device. + fn assign_bus_dev(&mut self, _bus: u8, _device: u8 /*u5*/) {} /// A vector of device-specific file descriptors that must be kept open /// after jailing. Must be called before the process is jailed. fn keep_fds(&self) -> Vec<RawFd>; @@ -147,6 +151,9 @@ impl<T: PciDevice + ?Sized> PciDevice for Box<T> { fn debug_label(&self) -> String { (**self).debug_label() } + fn assign_bus_dev(&mut self, bus: u8, device: u8 /*u5*/) { + (**self).assign_bus_dev(bus, device) + } fn keep_fds(&self) -> Vec<RawFd> { (**self).keep_fds() } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 1f58a5d..76e1d4a 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -12,7 +12,7 @@ use crate::usb::xhci::xhci::Xhci; use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider; use crate::usb::xhci::xhci_regs::{init_xhci_mmio_space_and_regs, XhciRegs}; use crate::utils::FailHandle; -use resources::SystemAllocator; +use resources::{Alloc, SystemAllocator}; use std::mem; use std::os::unix::io::RawFd; use std::sync::atomic::{AtomicBool, Ordering}; @@ -94,6 +94,7 @@ enum XhciControllerState { /// xHCI PCI interface implementation. pub struct XhciController { config_regs: PciConfiguration, + pci_bus_dev: Option<(u8, u8)>, mem: GuestMemory, bar0: u64, // bar0 in config_regs will be changed by guest. Not sure why. state: XhciControllerState, @@ -114,6 +115,7 @@ impl XhciController { ); XhciController { config_regs, + pci_bus_dev: None, mem, bar0: 0, state: XhciControllerState::Created { @@ -167,6 +169,10 @@ impl PciDevice for XhciController { "xhci controller".to_owned() } + fn assign_bus_dev(&mut self, bus: u8, device: u8) { + self.pci_bus_dev = Some((bus, device)); + } + fn keep_fds(&self) -> Vec<RawFd> { match &self.state { XhciControllerState::Created { device_provider } => device_provider.keep_fds(), @@ -204,10 +210,18 @@ impl PciDevice for XhciController { &mut self, resources: &mut SystemAllocator, ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { + let (bus, dev) = self + .pci_bus_dev + .expect("assign_bus_dev must be called prior to allocate_io_bars"); // xHCI spec 5.2.1. let bar0_addr = resources - .allocate_mmio_addresses(XHCI_BAR0_SIZE) - .ok_or(PciDeviceError::IoAllocationFailed(XHCI_BAR0_SIZE))?; + .mmio_allocator() + .allocate( + XHCI_BAR0_SIZE, + Alloc::PciBar { bus, dev, bar: 0 }, + "xhci_bar0".to_string(), + ) + .map_err(|e| PciDeviceError::IoAllocationFailed(XHCI_BAR0_SIZE, e))?; let bar0_config = PciBarConfiguration::default() .set_register_index(0) .set_address(bar0_addr) diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs index 5b54de5..45bd423 100644 --- a/devices/src/virtio/virtio_pci_device.rs +++ b/devices/src/virtio/virtio_pci_device.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use data_model::{DataInit, Le32}; use kvm::Datamatch; -use resources::SystemAllocator; +use resources::{Alloc, SystemAllocator}; use sys_util::{EventFd, GuestMemory, Result}; use super::*; @@ -148,6 +148,7 @@ const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; // Add to device type to get devi /// transport for virtio devices. pub struct VirtioPciDevice { config_regs: PciConfiguration, + pci_bus_dev: Option<(u8, u8)>, device: Box<dyn VirtioDevice>, device_activated: bool, @@ -191,6 +192,7 @@ impl VirtioPciDevice { Ok(VirtioPciDevice { config_regs, + pci_bus_dev: None, device, device_activated: false, interrupt_status: Arc::new(AtomicUsize::new(0)), @@ -300,6 +302,10 @@ impl PciDevice for VirtioPciDevice { format!("virtio-pci ({})", self.device.debug_label()) } + fn assign_bus_dev(&mut self, bus: u8, device: u8) { + self.pci_bus_dev = Some((bus, device)); + } + fn keep_fds(&self) -> Vec<RawFd> { let mut fds = self.device.keep_fds(); if let Some(interrupt_evt) = &self.interrupt_evt { @@ -327,11 +333,22 @@ impl PciDevice for VirtioPciDevice { &mut self, resources: &mut SystemAllocator, ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { + let (bus, dev) = self + .pci_bus_dev + .expect("assign_bus_dev must be called prior to allocate_io_bars"); // Allocate one bar for the structures pointed to by the capability structures. let mut ranges = Vec::new(); let settings_config_addr = resources - .allocate_mmio_addresses(CAPABILITY_BAR_SIZE) - .ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?; + .mmio_allocator() + .allocate( + CAPABILITY_BAR_SIZE, + Alloc::PciBar { bus, dev, bar: 0 }, + format!( + "virtio-{}-cap_bar", + type_to_str(self.device.device_type()).unwrap_or("?") + ), + ) + .map_err(|e| PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE, e))?; let config = PciBarConfiguration::default() .set_register_index(0) .set_address(settings_config_addr) @@ -353,12 +370,27 @@ impl PciDevice for VirtioPciDevice { &mut self, resources: &mut SystemAllocator, ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { + let (bus, dev) = self + .pci_bus_dev + .expect("assign_bus_dev must be called prior to allocate_device_bars"); let mut ranges = Vec::new(); for config in self.device.get_device_bars() { let device_addr = resources - .allocate_device_addresses(config.get_size()) - .ok_or(PciDeviceError::IoAllocationFailed(config.get_size()))?; - config.set_address(device_addr); + .device_allocator() + .allocate( + config.get_size(), + Alloc::PciBar { + bus, + dev, + bar: config.get_register_index() as u8, + }, + format!( + "virtio-{}-custom_bar", + type_to_str(self.device.device_type()).unwrap_or("?") + ), + ) + .map_err(|e| PciDeviceError::IoAllocationFailed(config.get_size(), e))?; + let config = config.set_address(device_addr); let _device_bar = self .config_regs .add_pci_bar(&config) diff --git a/resources/src/address_allocator.rs b/resources/src/address_allocator.rs index 6c8513d..45927e0 100644 --- a/resources/src/address_allocator.rs +++ b/resources/src/address_allocator.rs @@ -2,16 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::collections::HashMap; + +use crate::{Alloc, Error, Result}; + /// Manages allocating address ranges. /// Use `AddressAllocator` whenever an address range needs to be allocated to different users. +/// Allocations must be uniquely tagged with an Alloc enum, which can be used for lookup. +/// An human-readable tag String must also be provided for debugging / reference. /// /// # Examples /// /// ``` -/// # use resources::AddressAllocator; +/// // Anon is used for brevity. Don't manually instantiate Anon allocs! +/// # use resources::{Alloc, AddressAllocator}; /// AddressAllocator::new(0x1000, 0x10000, Some(0x100)).map(|mut pool| { -/// assert_eq!(pool.allocate(0x110), Some(0x1000)); -/// assert_eq!(pool.allocate(0x100), Some(0x1200)); +/// assert_eq!(pool.allocate(0x110, Alloc::Anon(0), "caps".to_string()), Ok(0x1000)); +/// assert_eq!(pool.allocate(0x100, Alloc::Anon(1), "cache".to_string()), Ok(0x1200)); +/// assert_eq!(pool.allocate(0x100, Alloc::Anon(2), "etc".to_string()), Ok(0x1300)); +/// assert_eq!(pool.get(&Alloc::Anon(1)), Some(&(0x1200, 0x100, "cache".to_string()))); /// }); /// ``` #[derive(Debug, Eq, PartialEq)] @@ -20,6 +29,7 @@ pub struct AddressAllocator { pool_end: u64, alignment: u64, next_addr: u64, + allocs: HashMap<Alloc, (u64, u64, String)>, } impl AddressAllocator { @@ -30,43 +40,64 @@ impl AddressAllocator { /// * `pool_base` - The starting address of the range to manage. /// * `pool_size` - The size of the address range in bytes. /// * `align_size` - The minimum size of an address region to align to, defaults to four. - pub fn new(pool_base: u64, pool_size: u64, align_size: Option<u64>) -> Option<Self> { + pub fn new(pool_base: u64, pool_size: u64, align_size: Option<u64>) -> Result<Self> { if pool_size == 0 { - return None; + return Err(Error::PoolSizeZero); } - let pool_end = pool_base.checked_add(pool_size - 1)?; + let pool_end = pool_base + .checked_add(pool_size - 1) + .ok_or(Error::PoolOverflow { + base: pool_base, + size: pool_size, + })?; let alignment = align_size.unwrap_or(4); if !alignment.is_power_of_two() || alignment == 0 { - return None; + return Err(Error::BadAlignment); } - Some(AddressAllocator { + Ok(AddressAllocator { pool_base, pool_end, alignment, next_addr: pool_base, + allocs: HashMap::new(), }) } - /// Allocates a range of addresses from the managed region. Returns `Some(allocated_address)` - /// when successful, or `None` if an area of `size` can't be allocated. - pub fn allocate(&mut self, size: u64) -> Option<u64> { + /// Allocates a range of addresses from the managed region with an optional tag. + /// Returns allocated_address. (allocated_address, size, tag) can be retrieved + /// through the `get` method. + pub fn allocate(&mut self, size: u64, alloc: Alloc, tag: String) -> Result<u64> { + if self.allocs.contains_key(&alloc) { + return Err(Error::ExistingAlloc(alloc)); + } if size == 0 { - return None; + return Err(Error::AllocSizeZero); } let align_adjust = if self.next_addr % self.alignment != 0 { self.alignment - (self.next_addr % self.alignment) } else { 0 }; - let addr = self.next_addr.checked_add(align_adjust)?; - let end_addr = addr.checked_add(size - 1)?; + let addr = self + .next_addr + .checked_add(align_adjust) + .ok_or(Error::OutOfSpace)?; + let end_addr = addr.checked_add(size - 1).ok_or(Error::OutOfSpace)?; if end_addr > self.pool_end { - return None; + return Err(Error::OutOfSpace); } + // TODO(dgreid): Use a smarter allocation strategy. The current strategy is just // bumping this pointer, meaning it will eventually exhaust available addresses. self.next_addr = end_addr.saturating_add(1); - Some(addr) + + self.allocs.insert(alloc, (addr, size, tag)); + Ok(addr) + } + + /// Returns allocation associated with `alloc`, or None if no such allocation exists. + pub fn get(&self, alloc: &Alloc) -> Option<&(u64, u64, String)> { + self.allocs.get(alloc) } } @@ -76,36 +107,77 @@ mod tests { #[test] fn new_fails_overflow() { - assert_eq!(AddressAllocator::new(u64::max_value(), 0x100, None), None); + assert!(AddressAllocator::new(u64::max_value(), 0x100, None).is_err()); } #[test] fn new_fails_size_zero() { - assert_eq!(AddressAllocator::new(0x1000, 0, None), None); + assert!(AddressAllocator::new(0x1000, 0, None).is_err()); } #[test] fn new_fails_alignment_zero() { - assert_eq!(AddressAllocator::new(0x1000, 0x10000, Some(0)), None); + assert!(AddressAllocator::new(0x1000, 0x10000, Some(0)).is_err()); } #[test] fn new_fails_alignment_non_power_of_two() { - assert_eq!(AddressAllocator::new(0x1000, 0x10000, Some(200)), None); + assert!(AddressAllocator::new(0x1000, 0x10000, Some(200)).is_err()); + } + + #[test] + fn allocate_fails_exising_alloc() { + let mut pool = AddressAllocator::new(0x1000, 0x1000, Some(0x100)).unwrap(); + assert_eq!( + pool.allocate(0x800, Alloc::Anon(0), String::from("bar0")), + Ok(0x1000) + ); + assert_eq!( + pool.allocate(0x800, Alloc::Anon(0), String::from("bar0")), + Err(Error::ExistingAlloc(Alloc::Anon(0))) + ); } #[test] fn allocate_fails_not_enough_space() { let mut pool = AddressAllocator::new(0x1000, 0x1000, Some(0x100)).unwrap(); - assert_eq!(pool.allocate(0x800), Some(0x1000)); - assert_eq!(pool.allocate(0x900), None); - assert_eq!(pool.allocate(0x800), Some(0x1800)); + assert_eq!( + pool.allocate(0x800, Alloc::Anon(0), String::from("bar0")), + Ok(0x1000) + ); + assert_eq!( + pool.allocate(0x900, Alloc::Anon(1), String::from("bar1")), + Err(Error::OutOfSpace) + ); + assert_eq!( + pool.allocate(0x800, Alloc::Anon(2), String::from("bar2")), + Ok(0x1800) + ); } #[test] fn allocate_alignment() { let mut pool = AddressAllocator::new(0x1000, 0x10000, Some(0x100)).unwrap(); - assert_eq!(pool.allocate(0x110), Some(0x1000)); - assert_eq!(pool.allocate(0x100), Some(0x1200)); + assert_eq!( + pool.allocate(0x110, Alloc::Anon(0), String::from("bar0")), + Ok(0x1000) + ); + assert_eq!( + pool.allocate(0x100, Alloc::Anon(1), String::from("bar1")), + Ok(0x1200) + ); + } + + #[test] + fn allocate_retrieve_alloc() { + let mut pool = AddressAllocator::new(0x1000, 0x10000, Some(0x100)).unwrap(); + assert_eq!( + pool.allocate(0x110, Alloc::Anon(0), String::from("bar0")), + Ok(0x1000) + ); + assert_eq!( + pool.get(&Alloc::Anon(0)), + Some(&(0x1000, 0x110, String::from("bar0"))) + ); } } diff --git a/resources/src/gpu_allocator.rs b/resources/src/gpu_allocator.rs index 7b40a22..a44d3c8 100644 --- a/resources/src/gpu_allocator.rs +++ b/resources/src/gpu_allocator.rs @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::fmt::Debug; use std::fs::File; #[cfg(feature = "wl-dmabuf")] @@ -13,7 +14,7 @@ use msg_socket::MsgOnSocket; use sys_util; #[allow(dead_code)] -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub enum GpuAllocatorError { OpenGpuBufferDevice, CreateGpuBufferDevice, @@ -35,7 +36,7 @@ pub struct GpuMemoryDesc { /// Trait that needs to be implemented in order to service GPU memory allocation /// requests. Implementations are expected to support some set of buffer sizes and /// formats but every possible combination is not required. -pub trait GpuMemoryAllocator { +pub trait GpuMemoryAllocator: Debug { /// Allocates GPU memory for a buffer of a specific size and format. The memory /// layout for the returned buffer must be linear. A file handle and the /// description of the planes for the buffer are returned on success. @@ -58,6 +59,13 @@ pub struct GpuBufferDevice { } #[cfg(feature = "wl-dmabuf")] +impl std::fmt::Debug for GpuBufferDevice { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "GpuBufferDevice {{opaque}}") + } +} + +#[cfg(feature = "wl-dmabuf")] impl GpuMemoryAllocator for GpuBufferDevice { fn allocate( &self, diff --git a/resources/src/lib.rs b/resources/src/lib.rs index b16b2da..a49f998 100644 --- a/resources/src/lib.rs +++ b/resources/src/lib.rs @@ -4,10 +4,67 @@ //! Manages system resources that can be allocated to VMs and their devices. +#[cfg(feature = "wl-dmabuf")] +extern crate gpu_buffer; +extern crate libc; +extern crate msg_socket; +extern crate sys_util; + +use std::fmt::Display; + +pub use crate::address_allocator::AddressAllocator; +pub use crate::gpu_allocator::{ + GpuAllocatorError, GpuMemoryAllocator, GpuMemoryDesc, GpuMemoryPlaneDesc, +}; +pub use crate::system_allocator::SystemAllocator; + mod address_allocator; mod gpu_allocator; mod system_allocator; -pub use crate::address_allocator::AddressAllocator; -pub use crate::gpu_allocator::{GpuMemoryAllocator, GpuMemoryDesc, GpuMemoryPlaneDesc}; -pub use crate::system_allocator::SystemAllocator; +/// Used to tag SystemAllocator allocations. +#[derive(Debug, Eq, PartialEq, Hash)] +pub enum Alloc { + /// An anonymous resource allocation. + /// Should only be instantiated through `SystemAllocator::get_anon_alloc()`. + /// Avoid using these. Instead, use / create a more descriptive Alloc variant. + Anon(usize), + /// A PCI BAR region with associated bus, device, and bar numbers. + PciBar { bus: u8, dev: u8, bar: u8 }, + /// GPU render node region. + GpuRenderNode, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum Error { + AllocSizeZero, + BadAlignment, + CreateGpuAllocator(GpuAllocatorError), + ExistingAlloc(Alloc), + MissingDeviceAddresses, + MissingMMIOAddresses, + NoIoAllocator, + OutOfSpace, + PoolOverflow { base: u64, size: u64 }, + PoolSizeZero, +} + +pub type Result<T> = std::result::Result<T, Error>; + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use self::Error::*; + match self { + AllocSizeZero => write!(f, "Allocation cannot have size of 0"), + BadAlignment => write!(f, "Pool alignment must be a power of 2"), + CreateGpuAllocator(e) => write!(f, "Failed to create GPU allocator: {:?}", e), + ExistingAlloc(tag) => write!(f, "Alloc already exists: {:?}", tag), + MissingDeviceAddresses => write!(f, "Device address range not specified"), + MissingMMIOAddresses => write!(f, "MMIO address range not specified"), + NoIoAllocator => write!(f, "No IO address range specified"), + OutOfSpace => write!(f, "Out of space"), + PoolOverflow { base, size } => write!(f, "base={} + size={} overflows", base, size), + PoolSizeZero => write!(f, "Pool cannot have size of 0"), + } + } +} diff --git a/resources/src/system_allocator.rs b/resources/src/system_allocator.rs index 3cc564d..6cdb70e 100644 --- a/resources/src/system_allocator.rs +++ b/resources/src/system_allocator.rs @@ -6,29 +6,44 @@ use sys_util::pagesize; use crate::address_allocator::AddressAllocator; use crate::gpu_allocator::{self, GpuMemoryAllocator}; +use crate::{Alloc, Error, Result}; /// Manages allocating system resources such as address space and interrupt numbers. /// /// # Example - Use the `SystemAddress` builder. /// /// ``` -/// # use resources::SystemAllocator; -/// if let Some(mut a) = SystemAllocator::builder() +/// # use resources::{Alloc, SystemAllocator}; +/// if let Ok(mut a) = SystemAllocator::builder() /// .add_io_addresses(0x1000, 0x10000) /// .add_device_addresses(0x10000000, 0x10000000) /// .add_mmio_addresses(0x30000000, 0x10000) /// .create_allocator(5, false) { /// assert_eq!(a.allocate_irq(), Some(5)); /// assert_eq!(a.allocate_irq(), Some(6)); -/// assert_eq!(a.allocate_device_addresses(0x100), Some(0x10000000)); +/// assert_eq!( +/// a.device_allocator() +/// .allocate( +/// 0x100, +/// Alloc::PciBar { bus: 0, dev: 0, bar: 0 }, +/// "bar0".to_string() +/// ), +/// Ok(0x10000000) +/// ); +/// assert_eq!( +/// a.device_allocator().get(&Alloc::PciBar { bus: 0, dev: 0, bar: 0 }), +/// Some(&(0x10000000, 0x100, "bar0".to_string())) +/// ); /// } /// ``` +#[derive(Debug)] pub struct SystemAllocator { io_address_space: Option<AddressAllocator>, device_address_space: AddressAllocator, mmio_address_space: AddressAllocator, gpu_allocator: Option<Box<dyn GpuMemoryAllocator>>, next_irq: u32, + next_anon_id: usize, } impl SystemAllocator { @@ -53,9 +68,9 @@ impl SystemAllocator { mmio_size: u64, create_gpu_allocator: bool, first_irq: u32, - ) -> Option<Self> { + ) -> Result<Self> { let page_size = pagesize() as u64; - Some(SystemAllocator { + Ok(SystemAllocator { io_address_space: if let (Some(b), Some(s)) = (io_base, io_size) { Some(AddressAllocator::new(b, s, Some(0x400))?) } else { @@ -64,11 +79,12 @@ impl SystemAllocator { device_address_space: AddressAllocator::new(dev_base, dev_size, Some(page_size))?, mmio_address_space: AddressAllocator::new(mmio_base, mmio_size, Some(page_size))?, gpu_allocator: if create_gpu_allocator { - gpu_allocator::create_gpu_memory_allocator().ok()? + gpu_allocator::create_gpu_memory_allocator().map_err(Error::CreateGpuAllocator)? } else { None }, next_irq: first_irq, + next_anon_id: 0, }) } @@ -87,25 +103,31 @@ impl SystemAllocator { } } - /// Reserves a section of `size` bytes of IO address space. - pub fn allocate_io_addresses(&mut self, size: u64) -> Option<u64> { - self.io_address_space.as_mut()?.allocate(size) + /// Gets an allocator to be used for IO memory. + pub fn io_allocator(&mut self) -> Option<&mut AddressAllocator> { + self.io_address_space.as_mut() } - /// Reserves a section of `size` bytes of device address space. - pub fn allocate_device_addresses(&mut self, size: u64) -> Option<u64> { - self.device_address_space.allocate(size) + /// Gets an allocator to be used for device memory. + pub fn device_allocator(&mut self) -> &mut AddressAllocator { + &mut self.device_address_space } - /// Reserves a section of `size` bytes of device address space. - pub fn allocate_mmio_addresses(&mut self, size: u64) -> Option<u64> { - self.mmio_address_space.allocate(size) + /// Gets an allocator to be used for MMIO memory. + pub fn mmio_allocator(&mut self) -> &mut AddressAllocator { + &mut self.mmio_address_space } /// Gets an allocator to be used for GPU memory. pub fn gpu_memory_allocator(&self) -> Option<&dyn GpuMemoryAllocator> { self.gpu_allocator.as_ref().map(|v| v.as_ref()) } + + /// Gets a unique anonymous allocation + pub fn get_anon_alloc(&mut self) -> Alloc { + self.next_anon_id += 1; + Alloc::Anon(self.next_anon_id) + } } /// Used to build a system address map for use in creating a `SystemAllocator`. @@ -152,14 +174,14 @@ impl SystemAllocatorBuilder { &self, first_irq: u32, gpu_allocation: bool, - ) -> Option<SystemAllocator> { + ) -> Result<SystemAllocator> { SystemAllocator::new( self.io_base, self.io_size, - self.device_base?, - self.device_size?, - self.mmio_base?, - self.mmio_size?, + self.device_base.ok_or(Error::MissingDeviceAddresses)?, + self.device_size.ok_or(Error::MissingDeviceAddresses)?, + self.mmio_base.ok_or(Error::MissingMMIOAddresses)?, + self.mmio_size.ok_or(Error::MissingMMIOAddresses)?, gpu_allocation, first_irq, ) diff --git a/src/linux.rs b/src/linux.rs index 19cdf09..6e2c2c9 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -34,6 +34,8 @@ use net_util::{Error as NetError, MacAddress, Tap}; use qcow::{self, ImageType, QcowFile}; use rand_ish::SimpleRng; use remain::sorted; +#[cfg(feature = "gpu-forward")] +use resources::Alloc; use sync::{Condvar, Mutex}; use sys_util::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener}; use sys_util::{ @@ -1173,8 +1175,13 @@ pub fn run_config(cfg: Config) -> Result<()> { // guest address space. let gpu_addr = linux .resources - .allocate_device_addresses(RENDER_NODE_HOST_SIZE) - .ok_or(Error::AllocateGpuDeviceAddress)?; + .device_allocator() + .allocate( + RENDER_NODE_HOST_SIZE, + Alloc::GpuRenderNode, + "gpu_render_node".to_string(), + ) + .map_err(|_| Error::AllocateGpuDeviceAddress)?; let host = RenderNodeHost::start(&gpu_mmap, gpu_addr, linux.vm.get_memory().clone()); diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs index b197e9b..474380c 100644 --- a/vm_control/src/lib.rs +++ b/vm_control/src/lib.rs @@ -209,9 +209,14 @@ fn register_memory( Err(MmapError::SystemCallFailed(e)) => return Err(e), _ => return Err(SysError::new(EINVAL)), }; - let addr = match allocator.allocate_device_addresses(size as u64) { - Some(a) => a, - None => return Err(SysError::new(EINVAL)), + let alloc = allocator.get_anon_alloc(); + let addr = match allocator.device_allocator().allocate( + size as u64, + alloc, + "vmcontrol_register_memory".to_string(), + ) { + Ok(a) => a, + Err(_) => return Err(SysError::new(EINVAL)), }; let slot = match vm.add_device_memory(GuestAddress(addr), mmap, false, false) { Ok(v) => v, |