1use 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#[split_enum_storage::container] #[must_use]
39pub struct Mapping {
40 backing: MappingBacking,
42
43 flags: MappingFlags,
45
46 #[split_enum_storage::decomposed]
59 name: MappingName,
60}
61
62#[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 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 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 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 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 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 pub fn may_exec(&self) -> bool {
203 self.max_access().contains(Access::EXEC)
204 }
205
206 pub fn is_private_file(&self) -> bool {
215 !self.flags.intersects(MappingFlags::SHARED | MappingFlags::ANONYMOUS)
216 && matches!(self.name(), MappingNameRef::File(_))
217 }
218
219 pub fn is_unmodified_private_file(&self) -> bool {
222 self.is_private_file() && !self.is_known_cow_modified()
223 }
224
225 pub fn has_cow_modifications(&self) -> bool {
232 self.is_private_file() && self.is_known_cow_modified()
233 }
234
235 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 fn is_known_cow_modified(&self) -> bool {
247 matches!(&self.backing, MappingBacking::Memory(backing) if backing.is_cow_modified())
248 }
249
250 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 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 if self.flags.contains(MappingFlags::READ) {
290 push(b"rd ");
291 }
292 if self.flags.contains(MappingFlags::WRITE) {
294 push(b"wr ");
295 }
296 if self.flags.contains(MappingFlags::EXEC) {
298 push(b"ex ");
299 }
300 if self.flags.contains(MappingFlags::SHARED) && max_access.contains(Access::WRITE) {
302 push(b"sh ");
303 }
304 if max_access.contains(Access::READ) {
306 push(b"mr ");
307 }
308 if max_access.contains(Access::WRITE) {
310 push(b"mw ");
311 }
312 if max_access.contains(Access::EXEC) {
314 push(b"me ");
315 }
316 if self.flags.contains(MappingFlags::SHARED) {
318 push(b"ms ");
319 }
320 if self.flags.contains(MappingFlags::GROWSDOWN) {
322 push(b"gd ");
323 }
324 if self.flags.contains(MappingFlags::LOCKED) {
328 push(b"lo ");
329 }
330 if self.flags.contains(MappingFlags::DONTFORK) {
335 push(b"dc ");
336 }
337 if self.flags.contains(MappingFlags::DONT_EXPAND) {
339 push(b"de ");
340 }
341 push(b"ac ");
343 if self.flags.contains(MappingFlags::WIPEONFORK) {
350 push(b"wf ");
351 }
352 if self.flags.contains(MappingFlags::UFFD_MISSING) {
360 push(b"um ");
361 }
362 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 None,
388
389 Stack,
391
392 Heap,
394
395 Vdso,
397
398 Vvar,
400
401 File(Arc<FileMapping>),
403
404 Vma(FlyByteStr),
409
410 Ashmem(FlyByteStr),
413
414 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 memory: Arc<MemoryObject>,
431
432 address_to_offset_delta_and_flags: u64,
436}
437
438static_assertions::assert_eq_size!(MappingBackingMemory, [u8; 16]);
440
441const COW_MODIFIED: u64 = 1;
447
448const 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 pub fn set_cow_modified(&mut self) {
464 self.address_to_offset_delta_and_flags |= COW_MODIFIED;
465 }
466
467 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 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 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 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] pub struct MappingFlags: u32 {
516 const READ = 1 << 0; const WRITE = 1 << 1; const EXEC = 1 << 2; 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 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
540const_assert_eq!(MappingFlags::READ.bits(), PROT_READ);
542const_assert_eq!(MappingFlags::WRITE.bits(), PROT_WRITE);
543const_assert_eq!(MappingFlags::EXEC.bits(), PROT_EXEC);
544
545const_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
560const CREATION_ONLY_OPTIONS: MappingOptions = MappingOptions::POPULATE;
563
564const_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 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}