vm_device/bus/
address.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;
5use std::convert::TryFrom;
6use std::ops::{Add, Sub};
7
8/// This trait defines the operations we expect to apply to bus address values.
9pub trait BusAddress:
10    Add<<Self as BusAddress>::V, Output = Self>
11    + Copy
12    + Eq
13    + Ord
14    + Sub<Output = <Self as BusAddress>::V>
15{
16    /// Defines the underlying value type of the `BusAddress`.
17    type V: Add<Output = Self::V>
18        + Copy
19        + From<u8>
20        + PartialEq
21        + Ord
22        + Sub<Output = Self::V>
23        + TryFrom<usize>;
24
25    /// Return the inner value.
26    fn value(&self) -> Self::V;
27
28    /// Return the bus address computed by offsetting `self` by the specified value, if no
29    /// overflow occurs.
30    fn checked_add(&self, value: Self::V) -> Option<Self>;
31}
32
33/// Represents a MMIO address offset.
34pub type MmioAddressOffset = u64;
35
36/// Represents a MMIO address.
37#[derive(Clone, Copy, Debug)]
38pub struct MmioAddress(pub MmioAddressOffset);
39
40/// Represents a PIO address offset.
41pub type PioAddressOffset = u16;
42
43/// Represents a PIO address.
44#[derive(Clone, Copy, Debug)]
45pub struct PioAddress(pub PioAddressOffset);
46
47// Implementing `BusAddress` and its prerequisites for `MmioAddress`.
48
49impl PartialEq for MmioAddress {
50    fn eq(&self, other: &Self) -> bool {
51        self.0 == other.0
52    }
53}
54
55impl Eq for MmioAddress {}
56
57impl PartialOrd for MmioAddress {
58    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
59        self.0.partial_cmp(&other.0)
60    }
61}
62
63impl Ord for MmioAddress {
64    fn cmp(&self, other: &Self) -> Ordering {
65        self.0.cmp(&other.0)
66    }
67}
68
69impl Add<MmioAddressOffset> for MmioAddress {
70    type Output = Self;
71
72    fn add(self, rhs: MmioAddressOffset) -> Self::Output {
73        MmioAddress(self.0 + rhs)
74    }
75}
76
77impl Sub for MmioAddress {
78    type Output = MmioAddressOffset;
79
80    fn sub(self, rhs: Self) -> Self::Output {
81        self.0 - rhs.0
82    }
83}
84
85impl BusAddress for MmioAddress {
86    type V = MmioAddressOffset;
87
88    fn value(&self) -> Self::V {
89        self.0
90    }
91
92    fn checked_add(&self, value: Self::V) -> Option<Self> {
93        self.0.checked_add(value).map(MmioAddress)
94    }
95}
96
97// Implementing `BusAddress` and its prerequisites for `PioAddress`.
98
99impl PartialEq for PioAddress {
100    fn eq(&self, other: &Self) -> bool {
101        self.0 == other.0
102    }
103}
104
105impl Eq for PioAddress {}
106
107impl PartialOrd for PioAddress {
108    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
109        self.0.partial_cmp(&other.0)
110    }
111}
112
113impl Ord for PioAddress {
114    fn cmp(&self, other: &Self) -> Ordering {
115        self.0.cmp(&other.0)
116    }
117}
118
119impl Add<PioAddressOffset> for PioAddress {
120    type Output = Self;
121
122    fn add(self, rhs: PioAddressOffset) -> Self::Output {
123        PioAddress(self.0 + rhs)
124    }
125}
126
127impl Sub for PioAddress {
128    type Output = PioAddressOffset;
129
130    fn sub(self, rhs: Self) -> Self::Output {
131        self.0 - rhs.0
132    }
133}
134
135impl BusAddress for PioAddress {
136    type V = PioAddressOffset;
137
138    fn value(&self) -> Self::V {
139        self.0
140    }
141
142    fn checked_add(&self, value: Self::V) -> Option<Self> {
143        self.0.checked_add(value).map(PioAddress)
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    use std::fmt::Debug;
152
153    // `addr_zero` should be an address equivalent to 0, while `max_value` should contain the
154    // maximum possible address value.
155    fn check_bus_address_ops<A>(addr_zero: A, max_value: A::V)
156    where
157        A: BusAddress + Debug,
158        A::V: Debug,
159    {
160        let value = A::V::from(5);
161        let addr = addr_zero + value;
162
163        assert!(addr_zero < addr);
164        assert_eq!(addr - addr_zero, value);
165
166        assert_eq!(addr.value(), value);
167        assert_eq!(addr_zero.checked_add(value).unwrap(), addr);
168
169        let addr_max = addr_zero.checked_add(max_value).unwrap();
170        assert!(addr_max.checked_add(A::V::from(1)).is_none());
171    }
172
173    #[test]
174    fn test_address_ops() {
175        check_bus_address_ops(MmioAddress(0), std::u64::MAX);
176        check_bus_address_ops(PioAddress(0), std::u16::MAX);
177    }
178}