Skip to main content

starnix_core/mm/
mapping.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::mm::memory::MemoryObject;
6use crate::mm::memory_manager::MemoryManagerState;
7use crate::mm::{
8    FaultRegisterMode, GUARD_PAGE_COUNT_FOR_GROWSDOWN_MAPPINGS, MappingOptions, PAGE_SIZE,
9    ProtectionFlags,
10};
11use crate::vfs::FileMapping;
12use crate::vfs::aio::AioContext;
13use crate::vfs::pseudo::dynamic_file::DynamicFileBuf;
14use bitflags::bitflags;
15use flyweights::FlyByteStr;
16use fuchsia_inspect::HistogramProperty;
17use starnix_uapi::errors::Errno;
18use starnix_uapi::file_mode::Access;
19use starnix_uapi::user_address::UserAddress;
20use starnix_uapi::{PROT_EXEC, PROT_READ, PROT_WRITE, errno};
21use static_assertions::const_assert_eq;
22use std::mem::MaybeUninit;
23use std::ops::Range;
24use std::sync::Arc;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum MappingMode {
28    Eager,
29    Lazy,
30}
31
32/// Describes a single memory mapping entry.
33///
34/// The size of this type *heavily* influences Starnix's heap usage in common scenarios, please
35/// think twice about increasing its size. Compiling in "release" mode includes a compile
36/// time check for size increase.
37#[split_enum_storage::container] // this also derives Clone, Debug, PartialEq, Eq
38#[must_use]
39pub struct Mapping {
40    /// Object backing this mapping.
41    backing: MappingBacking,
42
43    /// The flags used by the mapping, including protection.
44    flags: MappingFlags,
45
46    /// The name for this mapping.
47    ///
48    /// This may be a reference to the filesystem node backing this mapping or a userspace-assigned
49    /// name. The value of the name is orthogonal to whether this mapping is anonymous -
50    /// mappings of the file '/dev/zero' are treated as anonymous mappings and anonymous mappings
51    /// may have a name assigned.
52    ///
53    /// Because of this exception, avoid using this to check if a mapping is anonymous.
54    /// use [private_anonymous] method instead.
55    ///
56    /// NOTE: this *must* be accessed through `name()`/`set_name()` which are generated by below
57    /// macro.
58    #[split_enum_storage::decomposed]
59    name: MappingName,
60}
61
62// The size of this type *heavily* influences Starnix's heap usage in common scenarios, please
63// think twice about increasing the size here.
64#[cfg(not(any(test, debug_assertions)))]
65static_assertions::assert_eq_size!(Mapping, [u8; 24]);
66
67impl Mapping {
68    pub fn new(backing: MappingBacking, flags: MappingFlags, mode: MappingMode) -> Mapping {
69        Self::with_name(backing, flags, MappingName::None, mode)
70    }
71
72    pub fn with_name(
73        backing: MappingBacking,
74        mut flags: MappingFlags,
75        name: MappingName,
76        mode: MappingMode,
77    ) -> Mapping {
78        flags.set(MappingFlags::MAPPED_IN_VMAR, mode == MappingMode::Eager);
79        MappingUnsplit { backing, flags, name }.decompose()
80    }
81
82    pub fn flags(&self) -> MappingFlags {
83        self.flags
84    }
85
86    pub fn mapping_mode(&self) -> MappingMode {
87        if self.flags.contains(MappingFlags::MAPPED_IN_VMAR) {
88            MappingMode::Eager
89        } else {
90            MappingMode::Lazy
91        }
92    }
93
94    pub fn set_mapping_mode(&mut self, mode: MappingMode) {
95        self.flags.set(MappingFlags::MAPPED_IN_VMAR, mode == MappingMode::Eager);
96    }
97
98    pub fn set_flags(&mut self, new_flags: MappingFlags) {
99        self.flags = new_flags;
100    }
101
102    /// Maximum [`Access`] permissions allowed for this mapping, used by [`Mapping::write_vm_flags`] to
103    /// report `mr`, `mw`, and `me` in `/proc/[pid]/smaps` (see `proc_pid_smaps(5)`).
104    fn max_access(&self) -> Access {
105        match self.name() {
106            MappingNameRef::None
107            | MappingNameRef::Stack
108            | MappingNameRef::Heap
109            | MappingNameRef::Vma(_)
110            | MappingNameRef::Ashmem(_)
111            | MappingNameRef::AioContext(_) => Access::rwx(),
112            // The vDSO and vvar mappings are backed by read-only kernel VMOs (with execute rights
113            // for the vDSO) and cannot be made writable by userspace via `mprotect`.
114            MappingNameRef::Vdso => Access::READ | Access::EXEC,
115            MappingNameRef::Vvar => Access::READ,
116            MappingNameRef::File(file) => {
117                let mut access = file.file().max_access_for_memory_mapping();
118                // Private file mappings use copy-on-write, so they may always be made writable
119                // (e.g. via `mprotect(PROT_WRITE)`) even if the underlying file was opened
120                // read-only.
121                if !self.flags.contains(MappingFlags::SHARED) {
122                    access |= Access::WRITE;
123                }
124                access
125            }
126        }
127    }
128
129    pub fn get_backing_internal(&self) -> &MappingBacking {
130        &self.backing
131    }
132
133    pub fn set_backing_internal(&mut self, backing: MappingBacking) {
134        self.backing = backing;
135    }
136
137    pub fn set_uffd(&mut self, mode: FaultRegisterMode) {
138        self.flags |= MappingFlags::UFFD;
139        if mode == FaultRegisterMode::MISSING {
140            self.flags |= MappingFlags::UFFD_MISSING;
141        }
142    }
143
144    pub fn clear_uffd(&mut self) {
145        self.flags = self.flags.difference(MappingFlags::UFFD | MappingFlags::UFFD_MISSING);
146    }
147
148    pub fn set_mlock(&mut self) {
149        self.flags |= MappingFlags::LOCKED;
150    }
151
152    pub fn clear_mlock(&mut self) {
153        self.flags = self.flags.difference(MappingFlags::LOCKED);
154    }
155
156    pub fn new_private_anonymous(
157        flags: MappingFlags,
158        name: MappingName,
159        mode: MappingMode,
160    ) -> Mapping {
161        Self::with_name(MappingBacking::PrivateAnonymous, flags, name, mode)
162    }
163
164    pub fn inflate_to_include_guard_pages(&self, range: &Range<UserAddress>) -> Range<UserAddress> {
165        let start = if self.flags.contains(MappingFlags::GROWSDOWN) {
166            range
167                .start
168                .saturating_sub(*PAGE_SIZE as usize * GUARD_PAGE_COUNT_FOR_GROWSDOWN_MAPPINGS)
169        } else {
170            range.start
171        };
172        start..range.end
173    }
174
175    /// Converts a `UserAddress` to an offset in this mapping's memory object.
176    pub fn address_to_offset(&self, addr: UserAddress) -> u64 {
177        match &self.backing {
178            MappingBacking::Memory(backing) => backing.address_to_offset(addr),
179            MappingBacking::PrivateAnonymous => {
180                // For private, anonymous allocations the virtual address is the offset in the backing memory object.
181                addr.ptr() as u64
182            }
183        }
184    }
185
186    pub fn can_read(&self) -> bool {
187        self.flags.contains(MappingFlags::READ)
188    }
189
190    pub fn can_write(&self) -> bool {
191        self.flags.contains(MappingFlags::WRITE)
192    }
193
194    pub fn can_exec(&self) -> bool {
195        self.flags.contains(MappingFlags::EXEC)
196    }
197
198    /// Returns whether this mapping is allowed to be executable, as reported by `me` in
199    /// `/proc/[pid]/smaps`. For file mappings, this is false if the file is on a `noexec` mount
200    /// or is sealed against execution. Security checks (see `security::file_mprotect()`) may still
201    /// deny making the mapping executable.
202    pub fn may_exec(&self) -> bool {
203        self.max_access().contains(Access::EXEC)
204    }
205
206    /// Returns whether this is a private mapping of a file (as opposed to a `MAP_SHARED` mapping
207    /// or an anonymous mapping).
208    ///
209    /// Unlike `MAP_SHARED` and anonymous mappings (which access their backing pages directly),
210    /// writes to a private file mapping perform copy-on-write against the underlying file VMO.
211    /// `MappingFlags::ANONYMOUS` must also be checked because some anonymous mappings (such as
212    /// `mmap` of `/dev/zero`) carry `MappingName::File` for `/proc/<pid>/maps` display while
213    /// setting `MappingFlags::ANONYMOUS`.
214    pub fn is_private_file(&self) -> bool {
215        !self.flags.intersects(MappingFlags::SHARED | MappingFlags::ANONYMOUS)
216            && matches!(self.name(), MappingNameRef::File(_))
217    }
218
219    /// Returns whether this is a private file mapping that is not yet recorded as holding pages
220    /// modified via copy-on-write.
221    pub fn is_unmodified_private_file(&self) -> bool {
222        self.is_private_file() && !self.is_known_cow_modified()
223    }
224
225    /// Returns whether this is a private file mapping with pages that were modified via
226    /// copy-on-write, and are therefore no longer shared with the mapped file.
227    ///
228    /// Copy-on-write modifications are tracked at the granularity of the backing `MemoryObject`
229    /// (and synchronized across all mappings sharing that backing memory when write access is
230    /// enabled), rather than per-page.
231    pub fn has_cow_modifications(&self) -> bool {
232        self.is_private_file() && self.is_known_cow_modified()
233    }
234
235    /// Records that the memory of this mapping holds pages modified via copy-on-write. See
236    /// `Mapping::has_cow_modifications`.
237    pub fn set_cow_modified(&mut self) {
238        debug_assert!(self.is_private_file());
239        if let MappingBacking::Memory(backing) = &mut self.backing {
240            backing.set_cow_modified();
241        }
242    }
243
244    /// Returns whether the memory of this mapping is already known to hold pages modified via
245    /// copy-on-write.
246    fn is_known_cow_modified(&self) -> bool {
247        matches!(&self.backing, MappingBacking::Memory(backing) if backing.is_cow_modified())
248    }
249
250    /// Returns the [`MappingFlags`] to apply in the user VMAR when mapping or restoring
251    /// permissions on populated pages of this mapping.
252    ///
253    /// For writable private file mappings, `MappingFlags::WRITE` is omitted while the mapping is
254    /// unmodified (both when initially mapped and across any subsequent VMAR remaps or protection
255    /// updates) so that the first write faults. Once a write is recorded with
256    /// [`Mapping::set_cow_modified`], `vmar_flags()` includes `MappingFlags::WRITE`.
257    ///
258    /// Note that `userfaultfd` (`MappingFlags::UFFD`) strips VMAR permissions separately on
259    /// unpopulated pages via `UserFault`, and uses `vmar_flags()` when restoring permissions on
260    /// populated or unregistered ranges.
261    pub fn vmar_flags(&self) -> MappingFlags {
262        if self.is_unmodified_private_file() {
263            self.flags - MappingFlags::WRITE
264        } else {
265            self.flags
266        }
267    }
268
269    pub fn private_anonymous(&self) -> bool {
270        if let MappingBacking::PrivateAnonymous = &self.backing {
271            return true;
272        }
273        !self.flags.contains(MappingFlags::SHARED) && self.flags.contains(MappingFlags::ANONYMOUS)
274    }
275
276    pub fn write_vm_flags(&self, sink: &mut DynamicFileBuf) {
277        // Linux defines ~38 three-byte VmFlags mnemonics ("xx ", <= 114 bytes), of which Starnix
278        // currently emits at most 15 (45 bytes).
279        let mut buf = [0u8; 128];
280        let mut len = 0;
281        let mut push = |flag: &[u8]| {
282            buf[len..len + flag.len()].copy_from_slice(flag);
283            len += flag.len();
284        };
285        let max_access = self.max_access();
286        // From <https://man7.org/linux/man-pages/man5/proc_pid_smaps.5.html>:
287        //
288        // rd   -   readable
289        if self.flags.contains(MappingFlags::READ) {
290            push(b"rd ");
291        }
292        // wr   -   writable
293        if self.flags.contains(MappingFlags::WRITE) {
294            push(b"wr ");
295        }
296        // ex   -   executable
297        if self.flags.contains(MappingFlags::EXEC) {
298            push(b"ex ");
299        }
300        // sh   -   shared
301        if self.flags.contains(MappingFlags::SHARED) && max_access.contains(Access::WRITE) {
302            push(b"sh ");
303        }
304        // mr   -   may read
305        if max_access.contains(Access::READ) {
306            push(b"mr ");
307        }
308        // mw   -   may write
309        if max_access.contains(Access::WRITE) {
310            push(b"mw ");
311        }
312        // me   -   may execute
313        if max_access.contains(Access::EXEC) {
314            push(b"me ");
315        }
316        // ms   -   may share
317        if self.flags.contains(MappingFlags::SHARED) {
318            push(b"ms ");
319        }
320        // gd   -   stack segment grows down
321        if self.flags.contains(MappingFlags::GROWSDOWN) {
322            push(b"gd ");
323        }
324        // pf   -   pure PFN range
325        // dw   -   disabled write to the mapped file
326        // lo   -   pages are locked in memory
327        if self.flags.contains(MappingFlags::LOCKED) {
328            push(b"lo ");
329        }
330        // io   -   memory mapped I/O area
331        // sr   -   sequential read advise provided
332        // rr   -   random read advise provided
333        // dc   -   do not copy area on fork
334        if self.flags.contains(MappingFlags::DONTFORK) {
335            push(b"dc ");
336        }
337        // de   -   do not expand area on remapping
338        if self.flags.contains(MappingFlags::DONT_EXPAND) {
339            push(b"de ");
340        }
341        // ac   -   area is accountable
342        push(b"ac ");
343        // nr   -   swap space is not reserved for the area
344        // ht   -   area uses huge tlb pages
345        // sf   -   perform synchronous page faults (since Linux 4.15)
346        // nl   -   non-linear mapping (removed in Linux 4.0)
347        // ar   -   architecture specific flag
348        // wf   -   wipe on fork (since Linux 4.14)
349        if self.flags.contains(MappingFlags::WIPEONFORK) {
350            push(b"wf ");
351        }
352        // dd   -   do not include area into core dump
353        // sd   -   soft-dirty flag (since Linux 3.13)
354        // mm   -   mixed map area
355        // hg   -   huge page advise flag
356        // nh   -   no-huge page advise flag
357        // mg   -   mergeable advise flag
358        // um   -   userfaultfd missing pages tracking (since Linux 4.3)
359        if self.flags.contains(MappingFlags::UFFD_MISSING) {
360            push(b"um ");
361        }
362        // uw   -   userfaultfd wprotect pages tracking (since Linux 4.3)
363        // ui   -   userfaultfd minor fault pages tracking (since Linux 5.13)
364        sink.write(&buf[..len]);
365    }
366}
367
368#[derive(Debug, Eq, PartialEq, Clone)]
369pub enum MappingBacking {
370    Memory(Box<MappingBackingMemory>),
371
372    PrivateAnonymous,
373}
374
375impl MappingBacking {
376    pub fn as_memory(&self) -> Option<&MappingBackingMemory> {
377        match self {
378            Self::Memory(memory) => Some(memory),
379            Self::PrivateAnonymous => None,
380        }
381    }
382}
383
384#[derive(Debug, Eq, PartialEq, Clone, split_enum_storage::SplitStorage)]
385pub enum MappingName {
386    /// No name.
387    None,
388
389    /// This mapping is the initial stack.
390    Stack,
391
392    /// This mapping is the heap.
393    Heap,
394
395    /// This mapping is the vdso.
396    Vdso,
397
398    /// This mapping is the vvar.
399    Vvar,
400
401    /// The file backing this mapping.
402    File(Arc<FileMapping>),
403
404    /// The name associated with the mapping. Set by prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ...).
405    /// An empty name is distinct from an unnamed mapping. Mappings are initially created with no
406    /// name and can be reset to the unnamed state by passing NULL to
407    /// prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ...).
408    Vma(FlyByteStr),
409
410    /// The name associated with the mapping of an ashmem region.  Set by ioctl(fd, ASHMEM_SET_NAME, ...).
411    /// By default "dev/ashmem".
412    Ashmem(FlyByteStr),
413
414    /// This mapping is a context for asynchronous I/O.
415    AioContext(Arc<AioContext>),
416}
417
418impl<'a> MappingNameRef<'a> {
419    pub fn as_file(&self) -> Option<&'a Arc<FileMapping>> {
420        match self {
421            Self::File(file) => Some(file),
422            _ => None,
423        }
424    }
425}
426
427#[derive(Debug, Eq, PartialEq, Clone)]
428pub struct MappingBackingMemory {
429    /// The memory object that contains the memory used in this mapping.
430    memory: Arc<MemoryObject>,
431
432    /// The delta to convert from a user address to an offset in the memory object, and flags in
433    /// its lowest bits (see `ADDRESS_TO_OFFSET_DELTA_FLAGS`). Both the base address and the offset
434    /// of a mapping are page-aligned, so these bits of the delta are otherwise always zero.
435    address_to_offset_delta_and_flags: u64,
436}
437
438// Keep the size of the backing of memory mappings, which are allocated for each mapping.
439static_assertions::assert_eq_size!(MappingBackingMemory, [u8; 16]);
440
441/// Set in `MappingBackingMemory::address_to_offset_delta_and_flags` when the backing memory is
442/// known to hold pages modified via copy-on-write (tracked at the granularity of the backing
443/// `MemoryObject` and synchronized across all mappings sharing that backing memory when write
444/// access is enabled on a private file mapping) that are no longer shared with the mapped file.
445/// See `Mapping::has_cow_modifications`.
446const COW_MODIFIED: u64 = 1;
447
448/// The flags stored in `MappingBackingMemory::address_to_offset_delta_and_flags`.
449const ADDRESS_TO_OFFSET_DELTA_FLAGS: u64 = COW_MODIFIED;
450
451impl MappingBackingMemory {
452    pub fn new(base: UserAddress, memory: Arc<MemoryObject>, memory_offset: u64) -> Self {
453        let address_to_offset_delta = memory_offset.wrapping_sub(base.ptr() as u64);
454        debug_assert_eq!(
455            address_to_offset_delta & ADDRESS_TO_OFFSET_DELTA_FLAGS,
456            0,
457            "mapping base {base:?} and memory offset {memory_offset:#x} must be page-aligned"
458        );
459        Self { memory, address_to_offset_delta_and_flags: address_to_offset_delta }
460    }
461
462    /// Records that the memory holds pages modified via copy-on-write.
463    pub fn set_cow_modified(&mut self) {
464        self.address_to_offset_delta_and_flags |= COW_MODIFIED;
465    }
466
467    /// Returns whether the memory is known to hold pages modified via copy-on-write.
468    fn is_cow_modified(&self) -> bool {
469        self.address_to_offset_delta_and_flags & COW_MODIFIED != 0
470    }
471
472    pub fn memory(&self) -> &Arc<MemoryObject> {
473        &self.memory
474    }
475
476    /// Reads exactly `bytes.len()` bytes of memory from `addr`.
477    ///
478    /// # Parameters
479    /// - `addr`: The address to read data from.
480    /// - `bytes`: The byte array to read into.
481    pub fn read_memory<'a>(
482        &self,
483        addr: UserAddress,
484        bytes: &'a mut [MaybeUninit<u8>],
485    ) -> Result<&'a mut [u8], Errno> {
486        self.memory.read_uninit(bytes, self.address_to_offset(addr)).map_err(|_| errno!(EFAULT))
487    }
488
489    /// Writes the provided bytes to `addr`.
490    ///
491    /// # Parameters
492    /// - `addr`: The address to write to.
493    /// - `bytes`: The bytes to write to the memory object.
494    pub fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<(), Errno> {
495        self.memory.write(bytes, self.address_to_offset(addr)).map_err(|_| errno!(EFAULT))
496    }
497
498    pub fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno> {
499        self.memory
500            .op_range(zx::VmoOp::ZERO, self.address_to_offset(addr), length as u64)
501            .map_err(|_| errno!(EFAULT))?;
502        Ok(length)
503    }
504
505    /// Converts a `UserAddress` to an offset in this mapping's memory object.
506    pub fn address_to_offset(&self, addr: UserAddress) -> u64 {
507        (addr.ptr() as u64)
508            .wrapping_add(self.address_to_offset_delta_and_flags & !ADDRESS_TO_OFFSET_DELTA_FLAGS)
509    }
510}
511
512bitflags! {
513    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
514    #[rustfmt::skip]  // Preserve column alignment.
515    pub struct MappingFlags: u32 {
516        const READ              = 1 <<  0;  // PROT_READ
517        const WRITE             = 1 <<  1;  // PROT_WRITE
518        const EXEC              = 1 <<  2;  // PROT_EXEC
519        const SHARED            = 1 <<  3;
520        const ANONYMOUS         = 1 <<  4;
521        const LOWER_32BIT       = 1 <<  5;
522        const GROWSDOWN         = 1 <<  6;
523        const ELF_BINARY        = 1 <<  7;
524        const DONTFORK          = 1 <<  8;
525        const WIPEONFORK        = 1 <<  9;
526        const DONT_SPLIT        = 1 << 10;
527        const DONT_EXPAND       = 1 << 11;
528        /// A read-only `MAP_PRIVATE` file mapping that is still backed directly by the file's
529        /// VMO, through a handle without `WRITE` rights. It must be promoted to a copy-on-write
530        /// child VMO before it can become writable (see
531        /// `MemoryManagerState::promote_to_private_child`).
532        const LAZY_PRIVATE_FILE = 1 << 12;
533        const LOCKED            = 1 << 13;
534        const UFFD              = 1 << 14;
535        const UFFD_MISSING      = 1 << 15;
536        const MAPPED_IN_VMAR    = 1 << 16;
537    }
538}
539
540// The low three bits of MappingFlags match ProtectionFlags.
541const_assert_eq!(MappingFlags::READ.bits(), PROT_READ);
542const_assert_eq!(MappingFlags::WRITE.bits(), PROT_WRITE);
543const_assert_eq!(MappingFlags::EXEC.bits(), PROT_EXEC);
544
545// The next bits of MappingFlags match MappingOptions, shifted up.
546const_assert_eq!(MappingFlags::SHARED.bits(), MappingOptions::SHARED.bits() << 3);
547const_assert_eq!(MappingFlags::ANONYMOUS.bits(), MappingOptions::ANONYMOUS.bits() << 3);
548const_assert_eq!(MappingFlags::LOWER_32BIT.bits(), MappingOptions::LOWER_32BIT.bits() << 3);
549const_assert_eq!(MappingFlags::GROWSDOWN.bits(), MappingOptions::GROWSDOWN.bits() << 3);
550const_assert_eq!(MappingFlags::ELF_BINARY.bits(), MappingOptions::ELF_BINARY.bits() << 3);
551const_assert_eq!(MappingFlags::DONTFORK.bits(), MappingOptions::DONTFORK.bits() << 3);
552const_assert_eq!(MappingFlags::WIPEONFORK.bits(), MappingOptions::WIPEONFORK.bits() << 3);
553const_assert_eq!(MappingFlags::DONT_SPLIT.bits(), MappingOptions::DONT_SPLIT.bits() << 3);
554const_assert_eq!(MappingFlags::DONT_EXPAND.bits(), MappingOptions::DONT_EXPAND.bits() << 3);
555const_assert_eq!(
556    MappingFlags::LAZY_PRIVATE_FILE.bits(),
557    MappingOptions::LAZY_PRIVATE_FILE.bits() << 3
558);
559
560/// `MappingOptions` that only affect how a mapping is created. They have no corresponding
561/// `MappingFlags` bit and are dropped when converting between the two.
562const CREATION_ONLY_OPTIONS: MappingOptions = MappingOptions::POPULATE;
563
564// Every other `MappingOptions` bit must land exactly on one of the `MappingFlags` bits asserted
565// above. Otherwise shifting the options up would silently set an unrelated flag.
566const_assert_eq!(
567    MappingOptions::all().difference(CREATION_ONLY_OPTIONS).bits() << 3,
568    MappingFlags::SHARED.bits()
569        | MappingFlags::ANONYMOUS.bits()
570        | MappingFlags::LOWER_32BIT.bits()
571        | MappingFlags::GROWSDOWN.bits()
572        | MappingFlags::ELF_BINARY.bits()
573        | MappingFlags::DONTFORK.bits()
574        | MappingFlags::WIPEONFORK.bits()
575        | MappingFlags::DONT_SPLIT.bits()
576        | MappingFlags::DONT_EXPAND.bits()
577        | MappingFlags::LAZY_PRIVATE_FILE.bits()
578);
579
580impl MappingFlags {
581    pub fn access_flags(&self) -> ProtectionFlags {
582        ProtectionFlags::from_bits_truncate(self.bits() & ProtectionFlags::ACCESS_FLAGS.bits())
583    }
584
585    pub fn with_access_flags(&self, prot_flags: ProtectionFlags) -> Self {
586        let mapping_flags =
587            *self & (MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXEC).complement();
588        mapping_flags | Self::from_bits_truncate(prot_flags.access_flags().bits())
589    }
590
591    pub fn options(&self) -> MappingOptions {
592        MappingOptions::from_bits_truncate(self.bits() >> 3).difference(CREATION_ONLY_OPTIONS)
593    }
594
595    pub fn from_access_flags_and_options(
596        prot_flags: ProtectionFlags,
597        options: MappingOptions,
598    ) -> Self {
599        Self::from_bits_truncate(prot_flags.access_flags().bits())
600            | Self::from_bits_truncate(options.difference(CREATION_ONLY_OPTIONS).bits() << 3)
601    }
602}
603
604#[derive(Debug, Default)]
605pub struct MappingSummary {
606    no_kind: MappingKindSummary,
607    stack: MappingKindSummary,
608    heap: MappingKindSummary,
609    vdso: MappingKindSummary,
610    vvar: MappingKindSummary,
611    file: MappingKindSummary,
612    vma: MappingKindSummary,
613    ashmem: MappingKindSummary,
614    aiocontext: MappingKindSummary,
615
616    name_lengths: Vec<usize>,
617}
618
619impl MappingSummary {
620    pub fn add(&mut self, mm_state: &MemoryManagerState, mapping: &Mapping) {
621        let kind_summary = match mapping.name() {
622            MappingNameRef::None => &mut self.no_kind,
623            MappingNameRef::Stack => &mut self.stack,
624            MappingNameRef::Heap => &mut self.heap,
625            MappingNameRef::Vdso => &mut self.vdso,
626            MappingNameRef::Vvar => &mut self.vvar,
627            MappingNameRef::File(_) => &mut self.file,
628            MappingNameRef::Vma(name) => {
629                self.name_lengths.push(name.len());
630                &mut self.vma
631            }
632            MappingNameRef::Ashmem(name) => {
633                self.name_lengths.push(name.len());
634                &mut self.ashmem
635            }
636            MappingNameRef::AioContext(_) => &mut self.aiocontext,
637        };
638
639        kind_summary.count += 1;
640        if mapping.flags.contains(MappingFlags::SHARED) {
641            kind_summary.num_shared += 1;
642        } else {
643            kind_summary.num_private += 1;
644        }
645        match mm_state.get_mapping_backing(mapping) {
646            MappingBacking::Memory(_) => {
647                kind_summary.num_memory_objects += 1;
648            }
649            MappingBacking::PrivateAnonymous => kind_summary.num_private_anon += 1,
650        }
651    }
652
653    pub fn record(self, node: &fuchsia_inspect::Node) {
654        node.record_child("no_kind", |node| self.no_kind.record(node));
655        node.record_child("stack", |node| self.stack.record(node));
656        node.record_child("heap", |node| self.heap.record(node));
657        node.record_child("vdso", |node| self.vdso.record(node));
658        node.record_child("vvar", |node| self.vvar.record(node));
659        node.record_child("file", |node| self.file.record(node));
660        node.record_child("vma", |node| self.vma.record(node));
661        node.record_child("ashmem", |node| self.ashmem.record(node));
662        node.record_child("aiocontext", |node| self.aiocontext.record(node));
663
664        let name_lengths = node.create_uint_linear_histogram(
665            "name_lengths",
666            fuchsia_inspect::LinearHistogramParams { floor: 0, step_size: 8, buckets: 4 },
667        );
668        for l in self.name_lengths {
669            name_lengths.insert(l as u64);
670        }
671        node.record(name_lengths);
672    }
673}
674
675#[derive(Debug, Default)]
676struct MappingKindSummary {
677    count: u64,
678    num_private: u64,
679    num_shared: u64,
680    num_memory_objects: u64,
681    num_private_anon: u64,
682}
683
684impl MappingKindSummary {
685    fn record(&self, node: &fuchsia_inspect::Node) {
686        node.record_uint("count", self.count);
687        node.record_uint("num_private", self.num_private);
688        node.record_uint("num_shared", self.num_shared);
689        node.record_uint("num_memory_objects", self.num_memory_objects);
690        node.record_uint("num_private_anon", self.num_private_anon);
691    }
692}
693
694#[cfg(test)]
695mod tests {
696    use super::*;
697
698    #[::fuchsia::test]
699    fn cow_modified_flag_preserves_address_to_offset() {
700        let memory = Arc::new(MemoryObject::from(zx::Vmo::create(0x10000).expect("create VMO")));
701        let base = UserAddress::from(0x200000u64);
702        let addr = UserAddress::from(0x201000u64);
703        // The memory offset may be larger or smaller than the base address.
704        for (memory_offset, expected_offset) in [(0x3000, 0x4000), (0, 0x1000)] {
705            let mut backing = MappingBackingMemory::new(base, memory.clone(), memory_offset);
706            assert!(!backing.is_cow_modified());
707            assert_eq!(backing.address_to_offset(addr), expected_offset);
708            backing.set_cow_modified();
709            assert!(backing.is_cow_modified());
710            assert_eq!(backing.address_to_offset(addr), expected_offset);
711        }
712    }
713}