vm_device/bus/
range.rs

1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3
4use std::cmp::Ordering;
5
6use crate::bus::{BusAddress, Error, MmioAddress, PioAddress};
7
8/// An interval in the address space of a bus.
9#[derive(Copy, Clone, Debug)]
10pub struct BusRange<A: BusAddress> {
11    base: A,
12    size: A::V,
13}
14
15impl<A: BusAddress> BusRange<A> {
16    /// Create a new range while checking for overflow.
17    pub fn new(base: A, size: A::V) -> Result<Self, Error> {
18        // A zero-length range is not valid.
19        if size == 0.into() {
20            return Err(Error::InvalidRange);
21        }
22
23        // Subtracting one, because a range that ends at the very edge of the address space
24        // is still valid.
25        base.checked_add(size - 1.into())
26            .ok_or(Error::InvalidRange)?;
27
28        Ok(BusRange { base, size })
29    }
30
31    /// Create a new unit range (its size equals `1`).
32    pub fn unit(base: A) -> Self {
33        BusRange {
34            base,
35            size: 1.into(),
36        }
37    }
38
39    /// Return the base address of this range.
40    pub fn base(&self) -> A {
41        self.base
42    }
43
44    /// Return the size of the range.
45    pub fn size(&self) -> A::V {
46        self.size
47    }
48
49    /// Return the last bus address that's still part of the range.
50    pub fn last(&self) -> A {
51        self.base + (self.size - 1.into())
52    }
53
54    /// Check whether `self` and `other` overlap as intervals.
55    pub fn overlaps(&self, other: &BusRange<A>) -> bool {
56        !(self.base > other.last() || self.last() < other.base)
57    }
58}
59
60// We need to implement the following traits so we can use `BusRange` values with `BTreeMap`s.
61// This usage scenario requires treating ranges as if they supported a total order, but that's
62// not really possible with intervals, so we write the implementations as if `BusRange`s were
63// solely determined by their base addresses, and apply extra checks in the `Bus` logic.
64
65impl<A: BusAddress> PartialEq for BusRange<A> {
66    fn eq(&self, other: &BusRange<A>) -> bool {
67        self.base == other.base
68    }
69}
70
71impl<A: BusAddress> Eq for BusRange<A> {}
72
73impl<A: BusAddress> PartialOrd for BusRange<A> {
74    fn partial_cmp(&self, other: &BusRange<A>) -> Option<Ordering> {
75        self.base.partial_cmp(&other.base)
76    }
77}
78
79impl<A: BusAddress> Ord for BusRange<A> {
80    fn cmp(&self, other: &BusRange<A>) -> Ordering {
81        self.base.cmp(&other.base)
82    }
83}
84
85/// Represents an MMIO bus range.
86pub type MmioRange = BusRange<MmioAddress>;
87/// Represents a PIO bus range.
88pub type PioRange = BusRange<PioAddress>;
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_bus_range() {
96        let base_zero = MmioAddress(0);
97        let value = 5;
98
99        assert_eq!(BusRange::new(base_zero, 0), Err(Error::InvalidRange));
100
101        assert!(BusRange::new(base_zero, std::u64::MAX).is_ok());
102        assert!(BusRange::new(MmioAddress(1), std::u64::MAX).is_ok());
103        assert_eq!(
104            BusRange::new(MmioAddress(2), std::u64::MAX),
105            Err(Error::InvalidRange)
106        );
107
108        {
109            let range = BusRange::new(base_zero, value).unwrap();
110            assert_eq!(range.base(), base_zero);
111            assert_eq!(range.size(), value);
112            assert_eq!(range.last(), MmioAddress(value - 1));
113            assert!(range.base() < range.last());
114        }
115
116        {
117            let range = BusRange::unit(base_zero);
118            assert_eq!(range.base(), base_zero);
119            assert_eq!(range.last(), range.base());
120        }
121
122        // Let's test `BusRange::overlaps`.
123        {
124            let range = BusRange::new(MmioAddress(10), 10).unwrap();
125
126            let overlaps = |base_value, len_value| {
127                range.overlaps(&BusRange::new(MmioAddress(base_value), len_value).unwrap())
128            };
129
130            assert!(!overlaps(0, 5));
131            assert!(!overlaps(0, 10));
132            assert!(!overlaps(5, 5));
133
134            assert!(overlaps(0, 11));
135            assert!(overlaps(5, 6));
136            assert!(overlaps(5, 10));
137            assert!(overlaps(11, 15));
138            assert!(overlaps(5, 35));
139            assert!(overlaps(19, 1));
140            assert!(overlaps(19, 10));
141
142            assert!(!overlaps(20, 1));
143            assert!(!overlaps(30, 10));
144        }
145
146        // Finally, let's test the `BusRange` trait implementations that we added.
147        {
148            let base = MmioAddress(10);
149            let len = 10;
150
151            let range = BusRange::new(base, len).unwrap();
152
153            assert_eq!(range.cmp(&range), range.partial_cmp(&range).unwrap());
154            assert_eq!(range.cmp(&range), Ordering::Equal);
155
156            {
157                let other = BusRange::new(base, len + 1).unwrap();
158
159                // Still equal becase we're only comparing `base` values as part
160                // of the `eq` implementation.
161                assert_eq!(range, other);
162
163                assert_eq!(range.cmp(&other), range.partial_cmp(&other).unwrap());
164                assert_eq!(range.cmp(&other), Ordering::Equal);
165            }
166
167            {
168                let other = BusRange::unit(base.checked_add(1).unwrap());
169
170                // Different due to different base addresses.
171                assert_ne!(range, other);
172
173                assert_eq!(range.cmp(&other), range.partial_cmp(&other).unwrap());
174                assert_eq!(range.cmp(&other), Ordering::Less);
175            }
176        }
177    }
178}