Skip to main content

axplat/
mem.rs

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