vm-device 0.1.0

management for virtual devices and resources
Documentation
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause

use std::cmp::Ordering;
use std::convert::TryFrom;
use std::ops::{Add, Sub};

/// This trait defines the operations we expect to apply to bus address values.
pub trait BusAddress:
    Add<<Self as BusAddress>::V, Output = Self>
    + Copy
    + Eq
    + Ord
    + Sub<Output = <Self as BusAddress>::V>
{
    /// Defines the underlying value type of the `BusAddress`.
    type V: Add<Output = Self::V>
        + Copy
        + From<u8>
        + PartialEq
        + Ord
        + Sub<Output = Self::V>
        + TryFrom<usize>;

    /// Return the inner value.
    fn value(&self) -> Self::V;

    /// Return the bus address computed by offsetting `self` by the specified value, if no
    /// overflow occurs.
    fn checked_add(&self, value: Self::V) -> Option<Self>;
}

/// Represents a MMIO address offset.
pub type MmioAddressOffset = u64;

/// Represents a MMIO address.
#[derive(Clone, Copy, Debug)]
pub struct MmioAddress(pub MmioAddressOffset);

/// Represents a PIO address offset.
pub type PioAddressOffset = u16;

/// Represents a PIO address.
#[derive(Clone, Copy, Debug)]
pub struct PioAddress(pub PioAddressOffset);

// Implementing `BusAddress` and its prerequisites for `MmioAddress`.

impl PartialEq for MmioAddress {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl Eq for MmioAddress {}

impl PartialOrd for MmioAddress {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl Ord for MmioAddress {
    fn cmp(&self, other: &Self) -> Ordering {
        self.0.cmp(&other.0)
    }
}

impl Add<MmioAddressOffset> for MmioAddress {
    type Output = Self;

    fn add(self, rhs: MmioAddressOffset) -> Self::Output {
        MmioAddress(self.0 + rhs)
    }
}

impl Sub for MmioAddress {
    type Output = MmioAddressOffset;

    fn sub(self, rhs: Self) -> Self::Output {
        self.0 - rhs.0
    }
}

impl BusAddress for MmioAddress {
    type V = MmioAddressOffset;

    fn value(&self) -> Self::V {
        self.0
    }

    fn checked_add(&self, value: Self::V) -> Option<Self> {
        self.0.checked_add(value).map(MmioAddress)
    }
}

// Implementing `BusAddress` and its prerequisites for `PioAddress`.

impl PartialEq for PioAddress {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl Eq for PioAddress {}

impl PartialOrd for PioAddress {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl Ord for PioAddress {
    fn cmp(&self, other: &Self) -> Ordering {
        self.0.cmp(&other.0)
    }
}

impl Add<PioAddressOffset> for PioAddress {
    type Output = Self;

    fn add(self, rhs: PioAddressOffset) -> Self::Output {
        PioAddress(self.0 + rhs)
    }
}

impl Sub for PioAddress {
    type Output = PioAddressOffset;

    fn sub(self, rhs: Self) -> Self::Output {
        self.0 - rhs.0
    }
}

impl BusAddress for PioAddress {
    type V = PioAddressOffset;

    fn value(&self) -> Self::V {
        self.0
    }

    fn checked_add(&self, value: Self::V) -> Option<Self> {
        self.0.checked_add(value).map(PioAddress)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::fmt::Debug;

    // `addr_zero` should be an address equivalent to 0, while `max_value` should contain the
    // maximum possible address value.
    fn check_bus_address_ops<A>(addr_zero: A, max_value: A::V)
    where
        A: BusAddress + Debug,
        A::V: Debug,
    {
        let value = A::V::from(5);
        let addr = addr_zero + value;

        assert!(addr_zero < addr);
        assert_eq!(addr - addr_zero, value);

        assert_eq!(addr.value(), value);
        assert_eq!(addr_zero.checked_add(value).unwrap(), addr);

        let addr_max = addr_zero.checked_add(max_value).unwrap();
        assert!(addr_max.checked_add(A::V::from(1)).is_none());
    }

    #[test]
    fn test_address_ops() {
        check_bus_address_ops(MmioAddress(0), std::u64::MAX);
        check_bus_address_ops(PioAddress(0), std::u16::MAX);
    }
}