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