axplat/
mem.rs

1//! Physical memory information.
2
3use core::{fmt, ops::Range};
4
5use memory_addr::PhysAddr;
6
7bitflags::bitflags! {
8    /// The flags of a physical memory region.
9    #[derive(Clone, Copy)]
10    pub struct MemRegionFlags: usize {
11        /// Readable.
12        const READ          = 1 << 0;
13        /// Writable.
14        const WRITE         = 1 << 1;
15        /// Executable.
16        const EXECUTE       = 1 << 2;
17        /// Device memory. (e.g., MMIO regions)
18        const DEVICE        = 1 << 4;
19        /// Uncachable memory. (e.g., framebuffer)
20        const UNCACHED      = 1 << 5;
21        /// Reserved memory, do not use for allocation.
22        const RESERVED      = 1 << 6;
23        /// Free memory for allocation.
24        const FREE          = 1 << 7;
25    }
26}
27
28impl fmt::Debug for MemRegionFlags {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        fmt::Debug::fmt(&self.0, f)
31    }
32}
33
34/// The default flags for a normal memory region (readable, writable and allocatable).
35pub const DEFAULT_RAM_FLAGS: MemRegionFlags = MemRegionFlags::READ
36    .union(MemRegionFlags::WRITE)
37    .union(MemRegionFlags::FREE);
38
39/// The default flags for a reserved memory region (readable, writable, and reserved).
40pub const DEFAULT_RESERVED_FLAGS: MemRegionFlags = MemRegionFlags::READ
41    .union(MemRegionFlags::WRITE)
42    .union(MemRegionFlags::RESERVED);
43
44/// The default flags for a MMIO region (readable, writable, device, and reserved).
45pub const DEFAULT_MMIO_FLAGS: MemRegionFlags = MemRegionFlags::READ
46    .union(MemRegionFlags::WRITE)
47    .union(MemRegionFlags::DEVICE)
48    .union(MemRegionFlags::RESERVED);
49
50/// The raw memory range with start and size.
51pub type RawRange = (usize, usize);
52
53/// A physical memory region.
54#[derive(Debug, Clone, Copy)]
55pub struct PhysMemRegion {
56    /// The start physical address of the region.
57    pub paddr: PhysAddr,
58    /// The size in bytes of the region.
59    pub size: usize,
60    /// The region flags, see [`MemRegionFlags`].
61    pub flags: MemRegionFlags,
62    /// The region name, used for identification.
63    pub name: &'static str,
64}
65
66impl PhysMemRegion {
67    /// Creates a RAM region with default flags (readable, writable, and allocatable).
68    pub const fn new_ram(start: usize, size: usize, name: &'static str) -> Self {
69        Self {
70            paddr: PhysAddr::from_usize(start),
71            size,
72            flags: DEFAULT_RAM_FLAGS,
73            name,
74        }
75    }
76
77    /// Creates a MMIO region with default flags (readable, writable, and device).
78    pub const fn new_mmio(start: usize, size: usize, name: &'static str) -> Self {
79        Self {
80            paddr: PhysAddr::from_usize(start),
81            size,
82            flags: DEFAULT_MMIO_FLAGS,
83            name,
84        }
85    }
86
87    /// Creates a reserved memory region with default flags (readable, writable, and reserved).
88    pub const fn new_reserved(start: usize, size: usize, name: &'static str) -> Self {
89        Self {
90            paddr: PhysAddr::from_usize(start),
91            size,
92            flags: DEFAULT_RESERVED_FLAGS,
93            name,
94        }
95    }
96}
97
98/// Physical memory interface.
99#[def_plat_interface]
100pub trait MemIf {
101    /// Returns all physical memory (RAM) ranges on the platform.
102    ///
103    /// All memory ranges except reserved ranges (including the kernel loaded
104    /// range) are free for allocation.
105    fn phys_ram_ranges() -> &'static [RawRange];
106
107    /// Returns all reserved physical memory ranges on the platform.
108    ///
109    /// Reserved memory can be contained in [`phys_ram_ranges`], they are not
110    /// allocatable but should be mapped to kernel's address space.
111    ///
112    /// Note that the ranges returned should not include the range where the
113    /// kernel is loaded.
114    fn reserved_phys_ram_ranges() -> &'static [RawRange];
115
116    /// Returns all device memory (MMIO) ranges on the platform.
117    fn mmio_ranges() -> &'static [RawRange];
118}
119
120/// Returns the total size of physical memory (RAM) on the platform.
121///
122/// It should be equal to the sum of sizes of all physical memory ranges (returned
123/// by [`phys_ram_ranges`]).
124pub fn total_ram_size() -> usize {
125    phys_ram_ranges().iter().map(|range| range.1).sum()
126}
127
128/// The error type for overlapping check.
129///
130/// It contains the overlapping range pair.
131pub type OverlapErr = (Range<usize>, Range<usize>);
132
133/// Checks if the given ranges are overlapping.
134///
135/// Returns `Err` with one of the overlapping range pair if they are overlapping.
136///
137/// The given ranges should be sorted by the start, otherwise it always returns
138/// `Err`.
139///
140/// # Example
141///
142/// ```rust
143/// # use axplat::mem::check_sorted_ranges_overlap;
144/// assert!(check_sorted_ranges_overlap([(0, 10), (10, 10)].into_iter()).is_ok());
145/// assert_eq!(
146///     check_sorted_ranges_overlap([(0, 10), (5, 10)].into_iter()),
147///     Err((0..10, 5..15))
148/// );
149/// ```
150pub fn check_sorted_ranges_overlap(
151    ranges: impl Iterator<Item = RawRange>,
152) -> Result<(), OverlapErr> {
153    let mut prev = Range::default();
154    for (start, size) in ranges {
155        if prev.end > start {
156            return Err((prev, start..start + size));
157        }
158        prev = start..start + size;
159    }
160    Ok(())
161}
162
163/// Removes a portion of ranges from the given ranges.
164///
165/// `from` is a list of ranges to be operated on, and `exclude` is a list of
166/// ranges to be removed. `exclude` should have been sorted by the start, and
167/// have non-overlapping ranges. If not, an error will be returned.
168///
169/// The result is also a list of ranges with each range contained in `from` but
170/// not in `exclude`. `result_op` is a closure that will be called for each range
171/// in the result.
172///
173/// # Example
174///
175/// ```rust
176/// # use axplat::mem::ranges_difference;
177/// let mut res = Vec::new();
178/// // 0..10, 20..30 - 5..15, 15..25 = 0..5, 25..30
179/// ranges_difference(&[(0, 10), (20, 10)], &[(5, 10), (15, 10)], |r| res.push(r)).unwrap();
180/// assert_eq!(res, &[(0, 5), (25, 5)]);
181/// ```
182pub fn ranges_difference<F>(
183    from: &[RawRange],
184    exclude: &[RawRange],
185    mut result_op: F,
186) -> Result<(), OverlapErr>
187where
188    F: FnMut(RawRange),
189{
190    check_sorted_ranges_overlap(from.iter().cloned())?;
191
192    for &(start, size) in from {
193        let mut start = start;
194        let end = start + size;
195
196        for &(exclude_start, exclude_size) in exclude {
197            let exclude_end = exclude_start + exclude_size;
198            if exclude_end <= start {
199                continue;
200            } else if exclude_start >= end {
201                break;
202            } else if exclude_start > start {
203                result_op((start, exclude_start - start));
204            }
205            start = exclude_end;
206        }
207        if start < end {
208            result_op((start, end - start));
209        }
210    }
211    Ok(())
212}
213
214#[cfg(test)]
215mod tests {
216    #[test]
217    fn check_sorted_ranges_overlap() {
218        use super::check_sorted_ranges_overlap as f;
219
220        assert!(f([(0, 10), (10, 10), (20, 10)].into_iter()).is_ok());
221        assert!(f([(0, 10), (20, 10), (40, 10)].into_iter()).is_ok());
222        assert_eq!(f([(0, 1), (0, 2)].into_iter()), Err((0..1, 0..2)));
223        assert_eq!(
224            f([(0, 11), (10, 10), (20, 10)].into_iter()),
225            Err((0..11, 10..20)),
226        );
227        assert_eq!(
228            f([(0, 10), (20, 10), (10, 10)].into_iter()),
229            Err((20..30, 10..20)), // not sorted
230        );
231    }
232
233    #[test]
234    fn ranges_difference() {
235        let f = |from, exclude| {
236            let mut res = Vec::new();
237            super::ranges_difference(from, exclude, |r| res.push(r)).unwrap();
238            res
239        };
240
241        // 0..10, 20..30
242        assert_eq!(
243            f(&[(0, 10), (20, 10)], &[(5, 5), (25, 5)]), // - 5..10, 25..30
244            &[(0, 5), (20, 5)]                           // = 0..5, 20..25
245        );
246        assert_eq!(
247            f(&[(0, 10), (20, 10)], &[(5, 10), (15, 5)]), // - 5..15, 15..20
248            &[(0, 5), (20, 10)]                           // = 0..5, 20..30
249        );
250        assert_eq!(
251            f(&[(0, 10), (20, 10)], &[(5, 1), (25, 1), (30, 1)]), // - 5..6, 25..26, 30..31
252            &[(0, 5), (6, 4), (20, 5), (26, 4)]                   // = 0..5, 6..10, 20..25, 26..30
253        );
254
255        // 0..10, 20..30
256        assert_eq!(f(&[(0, 10), (20, 10)], &[(5, 20)]), &[(0, 5), (25, 5)]); // - 5..25 = 0..5, 25..30
257        assert_eq!(f(&[(0, 10), (20, 10)], &[(0, 30)]), &[]); // - 0..30 = []
258
259        // 0..30
260        assert_eq!(
261            f(&[(0, 30)], &[(0, 5), (10, 5), (20, 5)]), // - 0..5, 10..15, 20..25
262            &[(5, 5), (15, 5), (25, 5)]                 // = 5..10, 15..20, 25..30
263        );
264        assert_eq!(
265            f(
266                &[(0, 30)],
267                &[(0, 5), (5, 5), (10, 5), (15, 5), (20, 5), (25, 5)] // - 0..5, 5..10, 10..15, 15..20, 20..25, 25..30
268            ),
269            &[] // = []
270        );
271
272        // 10..20
273        assert_eq!(f(&[(10, 10)], &[(0, 30)]), &[]); // - 0..30 = []
274    }
275}