vm_device/bus/
mod.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
4//! Provides abstractions for modelling an I/O bus.
5//!
6//! A bus is seen here as a mapping between
7//! disjoint intervals (ranges) from an address space and objects (devices) associated with them.
8//! A single device can be registered with multiple ranges, but no two ranges can overlap,
9//! regardless with their device associations.
10
11mod address;
12mod range;
13
14use std::collections::BTreeMap;
15use std::convert::TryFrom;
16use std::fmt::{Display, Formatter};
17use std::result::Result;
18
19use address::BusAddress;
20
21pub use address::{MmioAddress, MmioAddressOffset, PioAddress, PioAddressOffset};
22pub use range::{BusRange, MmioRange, PioRange};
23
24/// Errors encountered during bus operations.
25#[derive(Debug, PartialEq)]
26pub enum Error {
27    /// No device is associated with the specified address or range.
28    DeviceNotFound,
29    /// Specified range overlaps an already registered range.
30    DeviceOverlap,
31    /// Access with invalid length attempted.
32    InvalidAccessLength(usize),
33    /// Invalid range provided (either zero-sized, or last address overflows).
34    InvalidRange,
35}
36
37impl Display for Error {
38    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Error::DeviceNotFound => write!(f, "device not found"),
41            Error::DeviceOverlap => write!(f, "range overlaps with existing device"),
42            Error::InvalidAccessLength(len) => write!(f, "invalid access length ({})", len),
43            Error::InvalidRange => write!(f, "invalid range provided"),
44        }
45    }
46}
47
48impl std::error::Error for Error {}
49
50/// A bus that's agnostic to the range address type and device type.
51pub struct Bus<A: BusAddress, D> {
52    devices: BTreeMap<BusRange<A>, D>,
53}
54
55impl<A: BusAddress, D> Default for Bus<A, D> {
56    fn default() -> Self {
57        Bus {
58            devices: BTreeMap::new(),
59        }
60    }
61}
62
63impl<A: BusAddress, D> Bus<A, D> {
64    /// Create an empty bus.
65    pub fn new() -> Self {
66        Self::default()
67    }
68
69    /// Return the registered range and device associated with `addr`.
70    pub fn device(&self, addr: A) -> Option<(&BusRange<A>, &D)> {
71        // The range is returned as an optimization because the caller
72        // might need both the device and its associated bus range.
73        // The same goes for the device_mut() method.
74        self.devices
75            .range(..=BusRange::unit(addr))
76            .nth_back(0)
77            .filter(|pair| pair.0.last() >= addr)
78    }
79
80    /// Return the registered range and a mutable reference to the device
81    /// associated with `addr`.
82    pub fn device_mut(&mut self, addr: A) -> Option<(&BusRange<A>, &mut D)> {
83        self.devices
84            .range_mut(..=BusRange::unit(addr))
85            .nth_back(0)
86            .filter(|pair| pair.0.last() >= addr)
87    }
88
89    /// Register a device with the provided range.
90    pub fn register(&mut self, range: BusRange<A>, device: D) -> Result<(), Error> {
91        for r in self.devices.keys() {
92            if range.overlaps(r) {
93                return Err(Error::DeviceOverlap);
94            }
95        }
96
97        self.devices.insert(range, device);
98
99        Ok(())
100    }
101
102    /// Deregister the device associated with `addr`.
103    pub fn deregister(&mut self, addr: A) -> Option<(BusRange<A>, D)> {
104        let range = self.device(addr).map(|(range, _)| *range)?;
105        self.devices.remove(&range).map(|device| (range, device))
106    }
107
108    /// Verify whether an access starting at `addr` with length `len` fits within any of
109    /// the registered ranges. Return the range and a handle to the device when present.
110    pub fn check_access(&self, addr: A, len: usize) -> Result<(&BusRange<A>, &D), Error> {
111        let access_range = BusRange::new(
112            addr,
113            A::V::try_from(len).map_err(|_| Error::InvalidAccessLength(len))?,
114        )
115        .map_err(|_| Error::InvalidRange)?;
116        self.device(addr)
117            .filter(|(range, _)| range.last() >= access_range.last())
118            .ok_or(Error::DeviceNotFound)
119    }
120}
121
122/// Represents an MMIO bus.
123pub type MmioBus<D> = Bus<MmioAddress, D>;
124/// Represents a PIO bus.
125pub type PioBus<D> = Bus<PioAddress, D>;
126
127/// Helper trait that can be implemented by types which hold one or more buses.
128pub trait BusManager<A: BusAddress> {
129    /// Type of the objects held by the bus.
130    type D;
131
132    /// Return a reference to the bus.
133    fn bus(&self) -> &Bus<A, Self::D>;
134
135    /// Return a mutable reference to the bus.
136    fn bus_mut(&mut self) -> &mut Bus<A, Self::D>;
137}
138
139#[cfg(test)]
140mod test {
141    use super::*;
142
143    #[test]
144    fn test_bus() {
145        let base = MmioAddress(10);
146        let base_prev = MmioAddress(base.value().checked_sub(1).unwrap());
147        let len = 10;
148        let range = MmioRange::new(base, len).unwrap();
149        let range_next = range.last().checked_add(1).unwrap();
150
151        let mut bus = Bus::new();
152        // The bus is agnostic to actual device types, so let's just use a numeric type here.
153        let device = 1u8;
154
155        assert_eq!(bus.devices.len(), 0);
156
157        bus.register(range, device).unwrap();
158        assert_eq!(bus.devices.len(), 1);
159
160        assert!(bus.device(base_prev).is_none());
161        assert!(bus.device_mut(base_prev).is_none());
162        assert!(bus.device(range_next).is_none());
163        assert!(bus.device_mut(range_next).is_none());
164
165        for offset in 0..len {
166            let addr = base.checked_add(offset).unwrap();
167
168            {
169                let (r, d) = bus.device(addr).unwrap();
170                assert_eq!(range, *r);
171                assert_eq!(device, *d);
172            }
173
174            {
175                let (r, d) = bus.device_mut(addr).unwrap();
176                assert_eq!(range, *r);
177                assert_eq!(device, *d);
178            }
179
180            // Let's also check invocations of `Bus::check_access`.
181            for start_offset in 0..offset {
182                let start_addr = base.checked_add(start_offset).unwrap();
183
184                let (r, d) = bus
185                    .check_access(start_addr, usize::try_from(offset - start_offset).unwrap())
186                    .unwrap();
187                assert_eq!(range, *r);
188                assert_eq!(device, *d);
189            }
190        }
191
192        // Detect double registration with the same range.
193        assert_eq!(bus.register(range, device), Err(Error::DeviceOverlap));
194
195        // We detect overlaps even if it's another range associated with the same device (we don't
196        // implicitly merge ranges). `check_access` fails if the specified range does not fully
197        // fit within a region associated with a particular device.
198
199        {
200            let range2 = MmioRange::new(MmioAddress(1), 10).unwrap();
201            assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
202            assert_eq!(
203                bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
204                Err(Error::DeviceNotFound)
205            );
206        }
207
208        {
209            let range2 = MmioRange::new(range.last(), 10).unwrap();
210            assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
211            assert_eq!(
212                bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
213                Err(Error::DeviceNotFound)
214            );
215        }
216
217        {
218            let range2 = MmioRange::new(MmioAddress(1), range.last().value() + 100).unwrap();
219            assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
220            assert_eq!(
221                bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
222                Err(Error::DeviceNotFound)
223            );
224        }
225
226        {
227            // For a completely empty range, `check_access` should still fail, but `insert`
228            // will succeed.
229
230            let range2 = MmioRange::new(range.last().checked_add(1).unwrap(), 5).unwrap();
231
232            assert_eq!(
233                bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
234                Err(Error::DeviceNotFound)
235            );
236
237            // Validate registration, and that `deregister` works for all addresses within a range.
238            for offset in 0..range2.size() {
239                let device2 = device + 1;
240                assert!(bus.register(range2, device2).is_ok());
241                assert_eq!(bus.devices.len(), 2);
242
243                let addr = range2.base().checked_add(offset).unwrap();
244                let (r, d) = bus.deregister(addr).unwrap();
245                assert_eq!(bus.devices.len(), 1);
246                assert_eq!(r, range2);
247                assert_eq!(d, device2);
248
249                // A second deregister should fail.
250                assert!(bus.deregister(addr).is_none());
251                assert_eq!(bus.devices.len(), 1);
252            }
253
254            // Register the previous `device` for `range2`.
255            assert!(bus.register(range2, device).is_ok());
256            assert_eq!(bus.devices.len(), 2);
257
258            // Even though the new range is associated with the same device, and right after the
259            // previous one, accesses across multiple ranges are not allowed for now.
260            // TODO: Do we want to support this in the future?
261            assert_eq!(
262                bus.check_access(range.base(), usize::try_from(range.size() + 1).unwrap()),
263                Err(Error::DeviceNotFound)
264            );
265        }
266
267        // Ensure that bus::check_access() fails when the len argument
268        // cannot be safely converted to PioAddressOffset which is u16.
269        let pio_base = PioAddress(10);
270        let pio_len = 10;
271        let pio_range = PioRange::new(pio_base, pio_len).unwrap();
272        let mut pio_bus = Bus::new();
273        let pio_device = 1u8;
274        pio_bus.register(pio_range, pio_device).unwrap();
275        assert_eq!(
276            pio_bus.check_access(pio_base, usize::MAX),
277            Err(Error::InvalidAccessLength(usize::MAX))
278        );
279    }
280}