kernel/object/capability/memory_mapping/
mod.rs

1//! Memory mapping capability module
2//!
3//! This module provides the MemoryMappingOps trait for objects that support
4//! memory mapping operations like mmap and munmap.
5
6pub mod syscall;
7
8pub use syscall::{sys_memory_map, sys_memory_unmap};
9
10/// Memory mapping operations capability
11///
12/// This trait represents the ability to provide memory mapping information
13/// and receive notifications about mapping lifecycle events.
14/// Objects that support memory mapping (like files and devices) should implement
15/// this trait to provide mmap/munmap functionality.
16pub trait MemoryMappingOps: Send + Sync {
17    /// Get mapping information for a region of the object
18    ///
19    /// Returns the physical address, permissions, and sharing information
20    /// for mapping a region of this object into virtual memory.
21    ///
22    /// # Arguments
23    /// * `offset` - Offset within the object to start mapping from
24    /// * `length` - Length of the mapping in bytes
25    ///
26    /// # Returns
27    /// * `Result<(usize, usize, bool), &'static str>` - (paddr, permissions, is_shared) on success
28    fn get_mapping_info(
29        &self,
30        offset: usize,
31        length: usize,
32    ) -> Result<(usize, usize, bool), &'static str>;
33
34    /// Get mapping information with sharing intent.
35    ///
36    /// Default implementation ignores `is_shared` and delegates to
37    /// `get_mapping_info` for backward compatibility.
38    fn get_mapping_info_with(
39        &self,
40        offset: usize,
41        length: usize,
42        _is_shared: bool,
43    ) -> Result<(usize, usize, bool), &'static str> {
44        self.get_mapping_info(offset, length)
45    }
46
47    /// Notification that a mapping has been created
48    ///
49    /// Called when a mapping of this object has been successfully created
50    /// in the virtual memory manager. The object can use this to track
51    /// its active mappings.
52    ///
53    /// # Arguments
54    /// * `vaddr` - Virtual address where the mapping was created
55    /// * `paddr` - Physical address that was mapped
56    /// * `length` - Length of the mapping in bytes
57    /// * `offset` - Offset within the object that was mapped
58    fn on_mapped(&self, vaddr: usize, paddr: usize, length: usize, offset: usize) {}
59
60    /// Notification that a mapping has been removed
61    ///
62    /// Called when a mapping of this object has been removed from
63    /// the virtual memory manager. The object should clean up any
64    /// tracking of this mapping.
65    ///
66    /// # Arguments
67    /// * `vaddr` - Virtual address where the mapping was removed
68    /// * `length` - Length of the mapping that was removed
69    fn on_unmapped(&self, vaddr: usize, length: usize) {}
70
71    /// Check if memory mapping is supported
72    ///
73    /// # Returns
74    /// * `bool` - true if this object supports memory mapping
75    fn supports_mmap(&self) -> bool {
76        true
77    }
78
79    /// Diagnostic helper: return a short owner name for logging
80    ///
81    /// Default implementation returns a generic "object" string. Implementers
82    /// (e.g. VfsFileObject) should override to provide more meaningful names
83    /// such as file paths.
84    fn mmap_owner_name(&self) -> alloc::string::String {
85        alloc::string::String::from("object")
86    }
87
88    // Resolve a page fault for a given access. Default: page-base paddr from mapping, not tail.
89    fn resolve_fault(
90        &self,
91        access: &crate::object::capability::memory_mapping::AccessKind,
92        map: &crate::vm::vmem::VirtualMemoryMap,
93    ) -> core::result::Result<
94        crate::object::capability::memory_mapping::ResolveFaultResult,
95        crate::object::capability::memory_mapping::ResolveFaultError,
96    > {
97        let page_vaddr = access.vaddr & !(crate::environment::PAGE_SIZE - 1);
98        let offset_in_mapping = page_vaddr - map.vmarea.start;
99        Ok(
100            crate::object::capability::memory_mapping::ResolveFaultResult {
101                paddr_page_base: map.pmarea.start + offset_in_mapping,
102                is_tail: false,
103            },
104        )
105    }
106}
107
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub enum AccessOp {
110    Instruction,
111    Load,
112    Store,
113}
114
115#[derive(Clone, Copy, Debug)]
116pub struct AccessKind {
117    pub op: AccessOp,
118    pub vaddr: usize,
119    pub size: Option<usize>,
120}
121
122#[derive(Clone, Copy, Debug)]
123pub struct ResolveFaultResult {
124    pub paddr_page_base: usize,
125    pub is_tail: bool,
126}
127
128#[derive(Clone, Copy, Debug)]
129pub enum ResolveFaultError {
130    Invalid,
131    Unmapped,
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    // Mock object that implements MemoryMappingOps for testing
139    struct MockMappableObject {
140        should_fail: bool,
141        mapped_regions: spin::RwLock<alloc::vec::Vec<(usize, usize)>>, // (vaddr, length)
142    }
143
144    impl MockMappableObject {
145        fn new(should_fail: bool) -> Self {
146            MockMappableObject {
147                should_fail,
148                mapped_regions: spin::RwLock::new(alloc::vec::Vec::new()),
149            }
150        }
151    }
152
153    impl MemoryMappingOps for MockMappableObject {
154        fn get_mapping_info(
155            &self,
156            offset: usize,
157            _length: usize,
158        ) -> Result<(usize, usize, bool), &'static str> {
159            if self.should_fail {
160                Err("Mock get_mapping_info failure")
161            } else {
162                // Return mock physical address, read/write permissions, not shared
163                Ok((0x80000000 + offset, 0x3, false))
164            }
165        }
166
167        fn on_mapped(&self, vaddr: usize, _paddr: usize, length: usize, _offset: usize) {
168            if !self.should_fail {
169                self.mapped_regions.write().push((vaddr, length));
170            }
171        }
172
173        fn on_unmapped(&self, vaddr: usize, length: usize) {
174            if !self.should_fail {
175                let mut regions = self.mapped_regions.write();
176                if let Some(pos) = regions
177                    .iter()
178                    .position(|(v, l)| *v == vaddr && *l == length)
179                {
180                    regions.remove(pos);
181                }
182            }
183        }
184
185        fn supports_mmap(&self) -> bool {
186            !self.should_fail
187        }
188
189        fn mmap_owner_name(&self) -> alloc::string::String {
190            alloc::string::String::from("mock_object")
191        }
192    }
193
194    #[test_case]
195    fn test_memory_mapping_ops_trait() {
196        // Test the MemoryMappingOps trait implementation
197        let mock_obj = MockMappableObject::new(false);
198
199        // Test supports_mmap
200        assert!(mock_obj.supports_mmap());
201
202        // Test successful get_mapping_info
203        let result = mock_obj.get_mapping_info(1024, 8192);
204        assert!(result.is_ok());
205        let (paddr, permissions, is_shared) = result.unwrap();
206        assert_eq!(paddr, 0x80000400); // 0x80000000 + 1024
207        assert_eq!(permissions, 0x3);
208        assert!(!is_shared);
209
210        // Test on_mapped notification
211        mock_obj.on_mapped(0x10000000, 0x80000400, 8192, 1024);
212        assert_eq!(mock_obj.mapped_regions.read().len(), 1);
213        assert_eq!(mock_obj.mapped_regions.read()[0], (0x10000000, 8192));
214
215        // Test on_unmapped notification
216        mock_obj.on_unmapped(0x10000000, 8192);
217        assert_eq!(mock_obj.mapped_regions.read().len(), 0);
218    }
219
220    #[test_case]
221    fn test_memory_mapping_failure_cases() {
222        // Test failure cases
223        let mock_fail_obj = MockMappableObject::new(true);
224
225        // Test supports_mmap returns false for failing object
226        assert!(!mock_fail_obj.supports_mmap());
227
228        // Test failed get_mapping_info
229        let result = mock_fail_obj.get_mapping_info(0, 4096);
230        assert!(result.is_err());
231        assert_eq!(result.unwrap_err(), "Mock get_mapping_info failure");
232
233        // Test that on_mapped/on_unmapped don't panic for failing object
234        mock_fail_obj.on_mapped(0x10000000, 0x80000000, 4096, 0);
235        mock_fail_obj.on_unmapped(0x10000000, 4096);
236        // Should not crash
237    }
238}