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.
12#[derive(Clone)]
13pub struct MemoryArea<B: MappingBackend> {
14    va_range: AddrRange<B::Addr>,
15    flags: B::Flags,
16    reported_flags: B::Flags,
17    max_flags: B::Flags,
18    backend: B,
19}
20
21impl<B: MappingBackend> MemoryArea<B> {
22    /// Fallible counterpart of [`Self::new`].  New code that receives
23    /// untrusted address/length pairs should use this constructor so an
24    /// overflow is represented as a mapping error instead of a panic.
25    pub fn try_new(
26        start: B::Addr,
27        size: usize,
28        flags: B::Flags,
29        backend: B,
30    ) -> MappingResult<Self> {
31        Self::try_new_with_reported_flags(start, size, flags, flags, backend)
32    }
33
34    /// Fallible constructor with separate operational and reported flags.
35    pub fn try_new_with_reported_flags(
36        start: B::Addr,
37        size: usize,
38        flags: B::Flags,
39        reported_flags: B::Flags,
40        backend: B,
41    ) -> MappingResult<Self> {
42        let va_range = ax_memory_addr::AddrRange::try_from_start_size(start, size)
43            .ok_or(MappingError::InvalidParam)?;
44        if va_range.is_empty() {
45            return Err(MappingError::InvalidParam);
46        }
47        Ok(Self {
48            va_range,
49            flags,
50            reported_flags,
51            max_flags: flags,
52            backend,
53        })
54    }
55
56    /// Creates a new memory area.
57    ///
58    /// # Panics
59    ///
60    /// Panics if `start + size` overflows.
61    pub fn new(start: B::Addr, size: usize, flags: B::Flags, backend: B) -> Self {
62        Self::new_with_reported_flags(start, size, flags, flags, backend)
63    }
64
65    /// Creates a new memory area with separate backend and reported flags.
66    ///
67    /// `flags` are used for page-table/backend operations. `reported_flags`
68    /// are metadata exposed through introspection interfaces such as procfs.
69    ///
70    /// # Panics
71    ///
72    /// Panics if `start + size` overflows.
73    pub fn new_with_reported_flags(
74        start: B::Addr,
75        size: usize,
76        flags: B::Flags,
77        reported_flags: B::Flags,
78        backend: B,
79    ) -> Self {
80        Self {
81            va_range: AddrRange::from_start_size(start, size),
82            flags,
83            reported_flags,
84            max_flags: flags,
85            backend,
86        }
87    }
88
89    /// Creates an area with an explicit maximum permission envelope.
90    pub fn new_with_permissions(
91        start: B::Addr,
92        size: usize,
93        flags: B::Flags,
94        reported_flags: B::Flags,
95        max_flags: B::Flags,
96        backend: B,
97    ) -> Self {
98        Self {
99            va_range: AddrRange::from_start_size(start, size),
100            flags,
101            reported_flags,
102            max_flags,
103            backend,
104        }
105    }
106
107    /// Fallible constructor with an explicit maximum permission envelope.
108    ///
109    /// This is the constructor used at syscall boundaries.  The older
110    /// infallible constructors remain available for trusted boot-time
111    /// mappings, but user supplied `start + size` pairs must not be allowed to
112    /// wrap through `AddrRange::from_start_size`.
113    pub fn try_new_with_permissions(
114        start: B::Addr,
115        size: usize,
116        flags: B::Flags,
117        reported_flags: B::Flags,
118        max_flags: B::Flags,
119        backend: B,
120    ) -> MappingResult<Self> {
121        let va_range =
122            AddrRange::try_from_start_size(start, size).ok_or(MappingError::InvalidParam)?;
123        if va_range.is_empty() {
124            return Err(MappingError::InvalidParam);
125        }
126        Ok(Self {
127            va_range,
128            flags,
129            reported_flags,
130            max_flags,
131            backend,
132        })
133    }
134
135    /// Returns the virtual address range.
136    pub const fn va_range(&self) -> AddrRange<B::Addr> {
137        self.va_range
138    }
139
140    /// Returns the memory flags, e.g., the permission bits.
141    pub const fn flags(&self) -> B::Flags {
142        self.flags
143    }
144
145    /// Returns the permission flags reported to user-visible introspection.
146    pub const fn reported_flags(&self) -> B::Flags {
147        self.reported_flags
148    }
149
150    /// Returns the maximum permissions retained for this mapping.
151    pub const fn max_flags(&self) -> B::Flags {
152        self.max_flags
153    }
154
155    /// Returns the start address of the memory area.
156    pub const fn start(&self) -> B::Addr {
157        self.va_range.start
158    }
159
160    /// Returns the end address of the memory area.
161    pub const fn end(&self) -> B::Addr {
162        self.va_range.end
163    }
164
165    /// Returns the size of the memory area.
166    pub fn size(&self) -> usize {
167        self.va_range.size()
168    }
169
170    /// Returns the mapping backend of the memory area.
171    pub const fn backend(&self) -> &B {
172        &self.backend
173    }
174}
175
176impl<B: MappingBackend> MemoryArea<B> {
177    pub(crate) fn replace_backend(&mut self, backend: B) -> B {
178        core::mem::replace(&mut self.backend, backend)
179    }
180
181    /// Changes backend/page-table flags and reported flags together.
182    pub(crate) fn set_flags_with_reported_flags(
183        &mut self,
184        new_flags: B::Flags,
185        new_reported_flags: B::Flags,
186    ) {
187        self.flags = new_flags;
188        self.reported_flags = new_reported_flags;
189    }
190
191    /// Maps the whole memory area in the page table.
192    pub(crate) fn map_area(
193        &self,
194        context: &mut B::MutationContext,
195        page_table: &mut B::PageTable,
196    ) -> MappingResult {
197        self.backend
198            .map(self.start(), self.size(), self.flags, context, page_table)
199            .then_some(())
200            .ok_or(MappingError::BadState)
201    }
202
203    pub(crate) fn validate_map(&self, page_table: &B::PageTable) -> MappingResult {
204        self.backend
205            .validate_map(self.start(), self.size(), self.flags, page_table)
206            .then_some(())
207            .ok_or(MappingError::BadState)
208    }
209
210    /// Unmaps the whole memory area in the page table.
211    pub(crate) fn unmap_area(
212        &self,
213        context: &mut B::MutationContext,
214        page_table: &mut B::PageTable,
215    ) -> MappingResult {
216        self.unmap_range(self.start(), self.size(), context, page_table)
217    }
218
219    /// Unmaps a sub-range without changing this area's metadata.
220    ///
221    /// Callers use this to complete the fallible backend transition before
222    /// committing a split or key change in the containing memory set.
223    pub(crate) fn unmap_range(
224        &self,
225        start: B::Addr,
226        size: usize,
227        context: &mut B::MutationContext,
228        page_table: &mut B::PageTable,
229    ) -> MappingResult {
230        debug_assert!(
231            self.va_range
232                .contains_range(AddrRange::from_start_size(start, size))
233        );
234        self.backend
235            .unmap(start, size, context, page_table)
236            .then_some(())
237            .ok_or(MappingError::BadState)
238    }
239
240    /// Preflights an unmap sub-range without changing page-table or metadata.
241    pub(crate) fn validate_unmap_range(
242        &self,
243        start: B::Addr,
244        size: usize,
245        page_table: &B::PageTable,
246    ) -> MappingResult {
247        debug_assert!(
248            self.va_range
249                .contains_range(AddrRange::from_start_size(start, size))
250        );
251        self.backend
252            .validate_unmap(start, size, page_table)
253            .then_some(())
254            .ok_or(MappingError::BadState)
255    }
256
257    /// Changes page-table flags for a sub-range without changing metadata.
258    pub(crate) fn protect_range(
259        &self,
260        start: B::Addr,
261        size: usize,
262        new_flags: B::Flags,
263        context: &mut B::MutationContext,
264        page_table: &mut B::PageTable,
265    ) -> MappingResult {
266        debug_assert!(
267            self.va_range
268                .contains_range(AddrRange::from_start_size(start, size))
269        );
270        self.backend
271            .protect(start, size, new_flags, context, page_table)
272            .then_some(())
273            .ok_or(MappingError::BadState)
274    }
275
276    /// Shrinks the memory area at the left side without touching the page
277    /// table.
278    pub(crate) fn shrink_left_metadata(&mut self, new_size: usize) -> MappingResult {
279        if new_size == 0 || new_size >= self.size() {
280            return Err(MappingError::InvalidParam);
281        }
282
283        let old_size = self.size();
284        let unmap_size = old_size - new_size;
285        let new_start = self
286            .va_range
287            .start
288            .checked_add(unmap_size)
289            .ok_or(MappingError::InvalidParam)?;
290        if !self.backend.shrink_left(unmap_size) {
291            return Err(MappingError::BadState);
292        }
293        self.va_range.start = new_start;
294        Ok(())
295    }
296
297    /// Shrinks the memory area at the right side without touching the page
298    /// table.
299    pub(crate) fn shrink_right_metadata(&mut self, new_size: usize) -> MappingResult {
300        if new_size == 0 || new_size >= self.size() {
301            return Err(MappingError::InvalidParam);
302        }
303        let old_size = self.size();
304        let unmap_size = old_size - new_size;
305
306        let new_end = self
307            .va_range
308            .end
309            .checked_sub(unmap_size)
310            .ok_or(MappingError::InvalidParam)?;
311        if !self.backend.shrink_right(unmap_size) {
312            return Err(MappingError::BadState);
313        }
314        self.va_range.end = new_end;
315        Ok(())
316    }
317
318    /// Inverse of [`shrink_right`]: extends the end by `additional_size`
319    /// and maps the new region via the backend.
320    pub(crate) fn grow_right(
321        &mut self,
322        additional_size: usize,
323        context: &mut B::MutationContext,
324        page_table: &mut B::PageTable,
325    ) -> MappingResult {
326        if additional_size == 0
327            || !self.end().is_aligned_4k()
328            || !additional_size.is_multiple_of(ax_memory_addr::PAGE_SIZE_4K)
329        {
330            return Err(MappingError::InvalidParam);
331        }
332        let map_start = self.end();
333        let new_end = self
334            .va_range
335            .end
336            .checked_add(additional_size)
337            .ok_or(MappingError::InvalidParam)?;
338        if !self
339            .backend
340            .validate_map(map_start, additional_size, self.flags, page_table)
341        {
342            return Err(MappingError::BadState);
343        }
344        if !self
345            .backend
346            .map(map_start, additional_size, self.flags, context, page_table)
347        {
348            // A backend is allowed to materialize a prefix before reporting
349            // failure.  Use its inverse while the original metadata is still
350            // intact; if that inverse cannot prove a full cleanup, expose the
351            // indeterminate state instead of returning a recoverable error.
352            return Err(
353                if self
354                    .backend
355                    .unmap(map_start, additional_size, context, page_table)
356                {
357                    MappingError::BadState
358                } else {
359                    MappingError::NeedsRepair
360                },
361            );
362        }
363        self.va_range.end = new_end;
364        Ok(())
365    }
366
367    /// Splits the memory area at the given position.
368    ///
369    /// The original memory area is shrunk to the left part, and the right part
370    /// is returned.
371    ///
372    /// Returns `None` if the given position is not in the memory area, or one
373    /// of the parts is empty after splitting.
374    pub(crate) fn split(&mut self, pos: B::Addr) -> MappingResult<Option<Self>> {
375        if self.start() < pos && pos < self.end() {
376            let align_diff = pos.sub_addr(self.start());
377
378            let right = self
379                .backend
380                .split(align_diff)
381                .ok_or(MappingError::BadState)?;
382
383            let mut new_area = Self::new_with_reported_flags(
384                pos,
385                self.end()
386                    .checked_sub_addr(pos)
387                    .ok_or(MappingError::InvalidParam)?,
388                self.flags,
389                self.reported_flags,
390                right,
391            );
392            new_area.max_flags = self.max_flags;
393            self.va_range.end = pos;
394            Ok(Some(new_area))
395        } else {
396            Ok(None)
397        }
398    }
399}
400
401impl<B: MappingBackend> fmt::Debug for MemoryArea<B>
402where
403    B::Addr: fmt::Debug,
404    B::Flags: fmt::Debug + Copy,
405{
406    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407        f.debug_struct("MemoryArea")
408            .field("va_range", &self.va_range)
409            .field("flags", &self.flags)
410            .field("reported_flags", &self.reported_flags)
411            .field("max_flags", &self.max_flags)
412            .finish()
413    }
414}