mod address;
mod range;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::fmt::{Display, Formatter};
use std::result::Result;
use address::BusAddress;
pub use address::{MmioAddress, MmioAddressOffset, PioAddress, PioAddressOffset};
pub use range::{BusRange, MmioRange, PioRange};
#[derive(Debug, PartialEq)]
pub enum Error {
DeviceNotFound,
DeviceOverlap,
InvalidAccessLength(usize),
InvalidRange,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::DeviceNotFound => write!(f, "device not found"),
Error::DeviceOverlap => write!(f, "range overlaps with existing device"),
Error::InvalidAccessLength(len) => write!(f, "invalid access length ({})", len),
Error::InvalidRange => write!(f, "invalid range provided"),
}
}
}
impl std::error::Error for Error {}
pub struct Bus<A: BusAddress, D> {
devices: BTreeMap<BusRange<A>, D>,
}
impl<A: BusAddress, D> Default for Bus<A, D> {
fn default() -> Self {
Bus {
devices: BTreeMap::new(),
}
}
}
impl<A: BusAddress, D> Bus<A, D> {
pub fn new() -> Self {
Self::default()
}
pub fn device(&self, addr: A) -> Option<(&BusRange<A>, &D)> {
self.devices
.range(..=BusRange::unit(addr))
.nth_back(0)
.filter(|pair| pair.0.last() >= addr)
}
pub fn device_mut(&mut self, addr: A) -> Option<(&BusRange<A>, &mut D)> {
self.devices
.range_mut(..=BusRange::unit(addr))
.nth_back(0)
.filter(|pair| pair.0.last() >= addr)
}
pub fn register(&mut self, range: BusRange<A>, device: D) -> Result<(), Error> {
for r in self.devices.keys() {
if range.overlaps(r) {
return Err(Error::DeviceOverlap);
}
}
self.devices.insert(range, device);
Ok(())
}
pub fn deregister(&mut self, addr: A) -> Option<(BusRange<A>, D)> {
let range = self.device(addr).map(|(range, _)| *range)?;
self.devices.remove(&range).map(|device| (range, device))
}
pub fn check_access(&self, addr: A, len: usize) -> Result<(&BusRange<A>, &D), Error> {
let access_range = BusRange::new(
addr,
A::V::try_from(len).map_err(|_| Error::InvalidAccessLength(len))?,
)
.map_err(|_| Error::InvalidRange)?;
self.device(addr)
.filter(|(range, _)| range.last() >= access_range.last())
.ok_or(Error::DeviceNotFound)
}
}
pub type MmioBus<D> = Bus<MmioAddress, D>;
pub type PioBus<D> = Bus<PioAddress, D>;
pub trait BusManager<A: BusAddress> {
type D;
fn bus(&self) -> &Bus<A, Self::D>;
fn bus_mut(&mut self) -> &mut Bus<A, Self::D>;
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_bus() {
let base = MmioAddress(10);
let base_prev = MmioAddress(base.value().checked_sub(1).unwrap());
let len = 10;
let range = MmioRange::new(base, len).unwrap();
let range_next = range.last().checked_add(1).unwrap();
let mut bus = Bus::new();
let device = 1u8;
assert_eq!(bus.devices.len(), 0);
bus.register(range, device).unwrap();
assert_eq!(bus.devices.len(), 1);
assert!(bus.device(base_prev).is_none());
assert!(bus.device_mut(base_prev).is_none());
assert!(bus.device(range_next).is_none());
assert!(bus.device_mut(range_next).is_none());
for offset in 0..len {
let addr = base.checked_add(offset).unwrap();
{
let (r, d) = bus.device(addr).unwrap();
assert_eq!(range, *r);
assert_eq!(device, *d);
}
{
let (r, d) = bus.device_mut(addr).unwrap();
assert_eq!(range, *r);
assert_eq!(device, *d);
}
for start_offset in 0..offset {
let start_addr = base.checked_add(start_offset).unwrap();
let (r, d) = bus
.check_access(start_addr, usize::try_from(offset - start_offset).unwrap())
.unwrap();
assert_eq!(range, *r);
assert_eq!(device, *d);
}
}
assert_eq!(bus.register(range, device), Err(Error::DeviceOverlap));
{
let range2 = MmioRange::new(MmioAddress(1), 10).unwrap();
assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
assert_eq!(
bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
Err(Error::DeviceNotFound)
);
}
{
let range2 = MmioRange::new(range.last(), 10).unwrap();
assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
assert_eq!(
bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
Err(Error::DeviceNotFound)
);
}
{
let range2 = MmioRange::new(MmioAddress(1), range.last().value() + 100).unwrap();
assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
assert_eq!(
bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
Err(Error::DeviceNotFound)
);
}
{
let range2 = MmioRange::new(range.last().checked_add(1).unwrap(), 5).unwrap();
assert_eq!(
bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
Err(Error::DeviceNotFound)
);
for offset in 0..range2.size() {
let device2 = device + 1;
assert!(bus.register(range2, device2).is_ok());
assert_eq!(bus.devices.len(), 2);
let addr = range2.base().checked_add(offset).unwrap();
let (r, d) = bus.deregister(addr).unwrap();
assert_eq!(bus.devices.len(), 1);
assert_eq!(r, range2);
assert_eq!(d, device2);
assert!(bus.deregister(addr).is_none());
assert_eq!(bus.devices.len(), 1);
}
assert!(bus.register(range2, device).is_ok());
assert_eq!(bus.devices.len(), 2);
assert_eq!(
bus.check_access(range.base(), usize::try_from(range.size() + 1).unwrap()),
Err(Error::DeviceNotFound)
);
}
let pio_base = PioAddress(10);
let pio_len = 10;
let pio_range = PioRange::new(pio_base, pio_len).unwrap();
let mut pio_bus = Bus::new();
let pio_device = 1u8;
pio_bus.register(pio_range, pio_device).unwrap();
assert_eq!(
pio_bus.check_access(pio_base, usize::MAX),
Err(Error::InvalidAccessLength(usize::MAX))
);
}
}