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}