| // Copyright © 2019 Intel Corporation. All Rights Reserved. |
| // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause |
| |
| #![deny(missing_docs)] |
| |
| //! This crate provides: |
| //! * device traits defining read and write operations on specialized buses |
| //! * device manager (bus-specific traits and a concrete implementation) for |
| //! operating devices and dispatching I/O |
| //! * abstractions for defining resources and their constraints (e.g. a specific bus |
| //! address range, IRQ number, etc) |
| //! |
| //! [`MutDevicePio`] and [`MutDeviceMmio`] traits help with composite inner mutability |
| //! (i.e. if we have a `Mutex` that holds a `T` which implements [`MutDevicePio`], |
| //! then the `Mutex` can implement [`DevicePio`] based on its inner |
| //! mutability properties). |
| //! |
| //! # Example |
| //! |
| //! Implement a simple log PIO device, register it with |
| //! [`IoManager`](device_manager/struct.IoManager.html) |
| //! and dispatch a write operation to the device. |
| //!``` |
| //! use std::sync::{Arc, Mutex}; |
| //! use vm_device::bus::{PioAddress, PioAddressOffset, PioRange}; |
| //! use vm_device::device_manager::{IoManager, PioManager}; |
| //! use vm_device::MutDevicePio; |
| //! |
| //! struct LogDevice {} |
| //! |
| //! impl MutDevicePio for LogDevice { |
| //! fn pio_read(&mut self, base: PioAddress, offset: PioAddressOffset, _data: &mut [u8]) { |
| //! println!("mut pio_read: base {:?}, offset {}", base, offset); |
| //! } |
| //! fn pio_write(&mut self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) { |
| //! println!( |
| //! "mut pio_write: base {:?}, offset {}, data {:?}", |
| //! base, offset, data |
| //! ); |
| //! } |
| //! } |
| //! |
| //! // IoManager implements PioManager trait. |
| //! let mut manager = IoManager::new(); |
| //! let device = LogDevice {}; |
| //! let bus_range = PioRange::new(PioAddress(0), 10).unwrap(); |
| //! manager |
| //! .register_pio(bus_range, Arc::new(Mutex::new(device))) |
| //! .unwrap(); |
| //! manager.pio_write(PioAddress(0), &vec![b'o', b'k']).unwrap(); |
| //! ``` |
| |
| pub mod bus; |
| pub mod device_manager; |
| pub mod resources; |
| |
| use std::ops::Deref; |
| use std::sync::{Arc, Mutex}; |
| |
| use bus::{MmioAddress, MmioAddressOffset, PioAddress, PioAddressOffset}; |
| |
| /// Allows a device to be attached to a |
| /// [PIO](https://en.wikipedia.org/wiki/Programmed_input%E2%80%93output) bus. |
| /// |
| /// # Example |
| /// ``` |
| /// # use std::sync::Mutex; |
| /// # use vm_device::{DevicePio, bus::{PioAddress, PioAddressOffset}}; |
| /// struct DummyDevice { |
| /// config: Mutex<u32>, |
| /// } |
| /// |
| /// impl DevicePio for DummyDevice { |
| /// fn pio_read(&self, _base: PioAddress, _offset: PioAddressOffset, data: &mut [u8]) { |
| /// if data.len() > 4 { |
| /// return; |
| /// } |
| /// for (idx, iter) in data.iter_mut().enumerate() { |
| /// let config = self.config.lock().expect("failed to acquire lock"); |
| /// *iter = (*config >> (idx * 8) & 0xff) as u8; |
| /// } |
| /// } |
| /// |
| /// fn pio_write(&self, _base: PioAddress, _offset: PioAddressOffset, data: &[u8]) { |
| /// let mut config = self.config.lock().expect("failed to acquire lock"); |
| /// *config = u32::from(data[0]) & 0xff; |
| /// } |
| /// } |
| /// ``` |
| pub trait DevicePio { |
| /// Handle a read operation on the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a PIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller to store the read data |
| fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]); |
| |
| /// Handle a write operation to the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a PIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller holding the data to write |
| fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]); |
| } |
| |
| /// Allows a device to be attached to a |
| /// [MMIO](https://en.wikipedia.org/wiki/Memory-mapped_I/O) bus. |
| /// |
| /// # Example |
| /// ``` |
| /// # use std::sync::Mutex; |
| /// # use vm_device::{DeviceMmio, bus::{MmioAddress, MmioAddressOffset}}; |
| /// struct DummyDevice { |
| /// config: Mutex<u32>, |
| /// } |
| /// |
| /// impl DeviceMmio for DummyDevice { |
| /// fn mmio_read(&self, _base: MmioAddress, _offset: MmioAddressOffset, data: &mut [u8]) { |
| /// if data.len() > 4 { |
| /// return; |
| /// } |
| /// for (idx, iter) in data.iter_mut().enumerate() { |
| /// let config = self.config.lock().expect("failed to acquire lock"); |
| /// *iter = (*config >> (idx * 8) & 0xff) as u8; |
| /// } |
| /// } |
| /// |
| /// fn mmio_write(&self, _base: MmioAddress, _offset: MmioAddressOffset, data: &[u8]) { |
| /// let mut config = self.config.lock().expect("failed to acquire lock"); |
| /// *config = u32::from(data[0]) & 0xff; |
| /// } |
| /// } |
| /// ``` |
| pub trait DeviceMmio { |
| /// Handle a read operation on the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a MMIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller to store the read data |
| fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]); |
| |
| /// Handle a write operation to the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a MMIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller holding the data to write |
| fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]); |
| } |
| |
| /// Same as [DevicePio] but the methods are invoked with a mutable self borrow. |
| /// |
| /// # Example |
| /// ``` |
| /// # use vm_device::{MutDevicePio, bus::{PioAddress, PioAddressOffset}}; |
| /// struct DummyDevice { |
| /// config: u32, |
| /// } |
| /// |
| /// impl MutDevicePio for DummyDevice { |
| /// fn pio_read(&mut self, _base: PioAddress, _offset: PioAddressOffset, data: &mut [u8]) { |
| /// if data.len() > 4 { |
| /// return; |
| /// } |
| /// for (idx, iter) in data.iter_mut().enumerate() { |
| /// *iter = (self.config >> (idx * 8) & 0xff) as u8; |
| /// } |
| /// } |
| /// |
| /// fn pio_write(&mut self, _base: PioAddress, _offset: PioAddressOffset, data: &[u8]) { |
| /// self.config = u32::from(data[0]) & 0xff; |
| /// } |
| /// } |
| /// ``` |
| pub trait MutDevicePio { |
| /// Handle a read operation on the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a PIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller to store the read data |
| fn pio_read(&mut self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]); |
| |
| /// Handle a write operation to the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a PIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller holding the data to write |
| fn pio_write(&mut self, base: PioAddress, offset: PioAddressOffset, data: &[u8]); |
| } |
| |
| /// Same as [DeviceMmio] but the methods are invoked with a mutable self borrow. |
| /// # Example |
| /// ``` |
| /// # use vm_device::{MutDeviceMmio, bus::{MmioAddress, MmioAddressOffset}}; |
| /// struct DummyDevice { |
| /// config: u32, |
| /// } |
| /// |
| /// impl MutDeviceMmio for DummyDevice { |
| /// fn mmio_read(&mut self, _base: MmioAddress, _offset: MmioAddressOffset, data: &mut [u8]) { |
| /// if data.len() > 4 { |
| /// return; |
| /// } |
| /// for (idx, iter) in data.iter_mut().enumerate() { |
| /// *iter = (self.config >> (idx * 8) & 0xff) as u8; |
| /// } |
| /// } |
| /// |
| /// fn mmio_write(&mut self, _base: MmioAddress, _offset: MmioAddressOffset, data: &[u8]) { |
| /// self.config = u32::from(data[0]) & 0xff; |
| /// } |
| /// } |
| /// ``` |
| pub trait MutDeviceMmio { |
| /// Handle a read operation on the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a MMIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller to store the read data |
| fn mmio_read(&mut self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]); |
| |
| /// Handle a write operation to the device. |
| /// |
| /// # Arguments |
| /// |
| /// * `base`: base address on a MMIO bus |
| /// * `offset`: base address' offset |
| /// * `data`: a buffer provided by the caller holding the data to write |
| fn mmio_write(&mut self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]); |
| } |
| |
| // Blanket implementations for Arc<T>. |
| |
| impl<T: DeviceMmio + ?Sized> DeviceMmio for Arc<T> { |
| fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]) { |
| self.deref().mmio_read(base, offset, data); |
| } |
| |
| fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]) { |
| self.deref().mmio_write(base, offset, data); |
| } |
| } |
| |
| impl<T: DevicePio + ?Sized> DevicePio for Arc<T> { |
| fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]) { |
| self.deref().pio_read(base, offset, data); |
| } |
| |
| fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) { |
| self.deref().pio_write(base, offset, data); |
| } |
| } |
| |
| // Blanket implementations for Mutex<T>. |
| |
| impl<T: MutDeviceMmio + ?Sized> DeviceMmio for Mutex<T> { |
| fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]) { |
| self.lock().unwrap().mmio_read(base, offset, data) |
| } |
| |
| fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]) { |
| self.lock().unwrap().mmio_write(base, offset, data) |
| } |
| } |
| |
| impl<T: MutDevicePio + ?Sized> DevicePio for Mutex<T> { |
| fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]) { |
| self.lock().unwrap().pio_read(base, offset, data) |
| } |
| |
| fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) { |
| self.lock().unwrap().pio_write(base, offset, data) |
| } |
| } |