Skip to main content

ax_plat/
mem.rs

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