Skip to main content

ax_memory_set/
area.rs

1use core::fmt;
2
3use ax_memory_addr::{AddrRange, MemoryAddr};
4
5use crate::{MappingBackend, MappingError, MappingResult};
6
7/// A memory area represents a continuous range of virtual memory with the same
8/// flags.
9///
10/// The target physical memory frames are determined by [`MappingBackend`] and
11/// may not be contiguous.
12pub struct MemoryArea<B: MappingBackend> {
13    va_range: AddrRange<B::Addr>,
14    flags: B::Flags,
15    reported_flags: B::Flags,
16    backend: B,
17}
18
19impl<B: MappingBackend> MemoryArea<B> {
20    /// Creates a new memory area.
21    ///
22    /// # Panics
23    ///
24    /// Panics if `start + size` overflows.
25    pub fn new(start: B::Addr, size: usize, flags: B::Flags, backend: B) -> Self {
26        Self::new_with_reported_flags(start, size, flags, flags, backend)
27    }
28
29    /// Creates a new memory area with separate backend and reported flags.
30    ///
31    /// `flags` are used for page-table/backend operations. `reported_flags`
32    /// are metadata exposed through introspection interfaces such as procfs.
33    ///
34    /// # Panics
35    ///
36    /// Panics if `start + size` overflows.
37    pub fn new_with_reported_flags(
38        start: B::Addr,
39        size: usize,
40        flags: B::Flags,
41        reported_flags: B::Flags,
42        backend: B,
43    ) -> Self {
44        Self {
45            va_range: AddrRange::from_start_size(start, size),
46            flags,
47            reported_flags,
48            backend,
49        }
50    }
51
52    /// Returns the virtual address range.
53    pub const fn va_range(&self) -> AddrRange<B::Addr> {
54        self.va_range
55    }
56
57    /// Returns the memory flags, e.g., the permission bits.
58    pub const fn flags(&self) -> B::Flags {
59        self.flags
60    }
61
62    /// Returns the permission flags reported to user-visible introspection.
63    pub const fn reported_flags(&self) -> B::Flags {
64        self.reported_flags
65    }
66
67    /// Returns the start address of the memory area.
68    pub const fn start(&self) -> B::Addr {
69        self.va_range.start
70    }
71
72    /// Returns the end address of the memory area.
73    pub const fn end(&self) -> B::Addr {
74        self.va_range.end
75    }
76
77    /// Returns the size of the memory area.
78    pub fn size(&self) -> usize {
79        self.va_range.size()
80    }
81
82    /// Returns the mapping backend of the memory area.
83    pub const fn backend(&self) -> &B {
84        &self.backend
85    }
86}
87
88impl<B: MappingBackend> MemoryArea<B> {
89    /// Changes backend/page-table flags and reported flags together.
90    pub(crate) fn set_flags_with_reported_flags(
91        &mut self,
92        new_flags: B::Flags,
93        new_reported_flags: B::Flags,
94    ) {
95        self.flags = new_flags;
96        self.reported_flags = new_reported_flags;
97    }
98
99    /// Maps the whole memory area in the page table.
100    pub(crate) fn map_area(&self, page_table: &mut B::PageTable) -> MappingResult {
101        self.backend
102            .map(self.start(), self.size(), self.flags, page_table)
103            .then_some(())
104            .ok_or(MappingError::BadState)
105    }
106
107    /// Unmaps the whole memory area in the page table.
108    pub(crate) fn unmap_area(&self, page_table: &mut B::PageTable) -> MappingResult {
109        self.backend
110            .unmap(self.start(), self.size(), page_table)
111            .then_some(())
112            .ok_or(MappingError::BadState)
113    }
114
115    /// Changes the flags in the page table.
116    pub(crate) fn protect_area(
117        &mut self,
118        new_flags: B::Flags,
119        page_table: &mut B::PageTable,
120    ) -> MappingResult {
121        self.backend
122            .protect(self.start(), self.size(), new_flags, page_table);
123        Ok(())
124    }
125
126    /// Shrinks the memory area at the left side.
127    ///
128    /// The memory area is shrunk to `new_size`, and the left-side part is
129    /// unmapped.
130    ///
131    /// The start address is increased by `old_size - new_size`.
132    ///
133    /// `new_size` must be greater than 0 and less than the current size.
134    pub(crate) fn shrink_left(
135        &mut self,
136        new_size: usize,
137        page_table: &mut B::PageTable,
138    ) -> MappingResult {
139        assert!(new_size > 0 && new_size < self.size());
140
141        let old_size = self.size();
142        let unmap_size = old_size - new_size;
143
144        if !self.backend.unmap(self.start(), unmap_size, page_table) {
145            return Err(MappingError::BadState);
146        }
147        // Use wrapping_add to avoid overflow check.
148        // Safety: `unmap_size` is less than the current size, so it will never
149        // overflow.
150        self.va_range.start = self.va_range.start.wrapping_add(unmap_size);
151        self.backend.shrink_left(unmap_size);
152        Ok(())
153    }
154
155    /// Shrinks the memory area at the left side without touching the page
156    /// table.
157    pub(crate) fn shrink_left_metadata(&mut self, new_size: usize) {
158        assert!(new_size > 0 && new_size < self.size());
159
160        let old_size = self.size();
161        let unmap_size = old_size - new_size;
162        self.va_range.start = self.va_range.start.wrapping_add(unmap_size);
163        self.backend.shrink_left(unmap_size);
164    }
165
166    /// Shrinks the memory area at the right side.
167    ///
168    /// The memory area is shrunk to `new_size`, and the right-side part is
169    /// unmapped.
170    ///
171    /// The end address is decreased by `old_size - new_size`.
172    ///
173    /// `new_size` must be greater than 0 and less than the current size.
174    pub(crate) fn shrink_right(
175        &mut self,
176        new_size: usize,
177        page_table: &mut B::PageTable,
178    ) -> MappingResult {
179        assert!(new_size > 0 && new_size < self.size());
180        let old_size = self.size();
181        let unmap_size = old_size - new_size;
182
183        // Use wrapping_add to avoid overflow check.
184        // Safety: `new_size` is less than the current size, so it will never overflow.
185        let unmap_start = self.start().wrapping_add(new_size);
186
187        if !self.backend.unmap(unmap_start, unmap_size, page_table) {
188            return Err(MappingError::BadState);
189        }
190
191        // Use wrapping_sub to avoid overflow check, same as above.
192        self.va_range.end = self.va_range.end.wrapping_sub(unmap_size);
193        self.backend.shrink_right(unmap_size);
194        Ok(())
195    }
196
197    /// Shrinks the memory area at the right side without touching the page
198    /// table.
199    pub(crate) fn shrink_right_metadata(&mut self, new_size: usize) {
200        assert!(new_size > 0 && new_size < self.size());
201        let old_size = self.size();
202        let unmap_size = old_size - new_size;
203
204        self.va_range.end = self.va_range.end.wrapping_sub(unmap_size);
205        self.backend.shrink_right(unmap_size);
206    }
207
208    /// Inverse of [`shrink_right`]: extends the end by `additional_size`
209    /// and maps the new region via the backend.
210    pub(crate) fn grow_right(
211        &mut self,
212        additional_size: usize,
213        page_table: &mut B::PageTable,
214    ) -> MappingResult {
215        assert!(additional_size > 0);
216        assert!(
217            self.end().is_aligned_4k()
218                && additional_size.is_multiple_of(ax_memory_addr::PAGE_SIZE_4K),
219            "grow_right: end and additional_size must be page-aligned"
220        );
221        let map_start = self.end();
222        let new_end = self
223            .va_range
224            .end
225            .checked_add(additional_size)
226            .ok_or(MappingError::InvalidParam)?;
227        if !self
228            .backend
229            .map(map_start, additional_size, self.flags, page_table)
230        {
231            return Err(MappingError::BadState);
232        }
233        self.va_range.end = new_end;
234        Ok(())
235    }
236
237    /// Splits the memory area at the given position.
238    ///
239    /// The original memory area is shrunk to the left part, and the right part
240    /// is returned.
241    ///
242    /// Returns `None` if the given position is not in the memory area, or one
243    /// of the parts is empty after splitting.
244    pub(crate) fn split(&mut self, pos: B::Addr) -> Option<Self> {
245        if self.start() < pos && pos < self.end() {
246            let align_diff = pos.sub_addr(self.start());
247
248            let right = self
249                .backend
250                .split(align_diff)
251                .expect("backend should be splittable");
252
253            let new_area = Self::new_with_reported_flags(
254                pos,
255                // Use wrapping_sub_addr to avoid overflow check. It is safe because
256                // `pos` is within the memory area.
257                self.end().wrapping_sub_addr(pos),
258                self.flags,
259                self.reported_flags,
260                right,
261            );
262            self.va_range.end = pos;
263            Some(new_area)
264        } else {
265            None
266        }
267    }
268}
269
270impl<B: MappingBackend> fmt::Debug for MemoryArea<B>
271where
272    B::Addr: fmt::Debug,
273    B::Flags: fmt::Debug + Copy,
274{
275    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
276        f.debug_struct("MemoryArea")
277            .field("va_range", &self.va_range)
278            .field("flags", &self.flags)
279            .field("reported_flags", &self.reported_flags)
280            .finish()
281    }
282}