Skip to main content

faces_pfm/
mgr.rs

1//! Page frame manager implementation for `faces`.
2//!
3//! self module provides a concrete singleton manager (`PFM`) that implements
4//! `faces::AbsPageFrameManager`. It uses a static array of `spin::Mutex<PageFrame>`
5//! to store per‑frame metadata, initialised from the memory map provided by the
6//! Limine boot protocol.
7
8use crate::{PageFlags, PageFrame};
9use faces::{AbsPageFrameManager, Convertable as _, PhysicalAddress, to};
10use log::debug;
11
12/// The page frame manager singleton.
13///
14/// self type implements `AbsPageFrameManager` and is the central point for
15/// manipulating page frame flags and accessing per‑frame metadata.
16#[derive(Debug, Copy, Clone, Default)]
17pub struct PageFrameManager;
18
19/// Global page frame manager instance.
20///
21/// self is the singleton instance used throughout the system. It is safe to
22/// access because all methods are either read‑only or use internal locking.
23pub static PFM: PageFrameManager = PageFrameManager::new();
24
25type Fa = &'static mut [spin::Mutex<PageFrame>];
26
27/// Static slice of locked page frame metadata.
28///
29/// Each entry is a `spin::Mutex<PageFrame>` protecting the per‑frame data.
30/// The slice is initialised during `PageFrameManager::init()` and must be
31/// considered immutable afterwards.
32static mut FRAME_ARRAY: &mut [spin::Mutex<PageFrame>] = Fa::default();
33
34/// Helper macro to obtain a locked guard for a given physical frame number.
35///
36/// # Safety
37/// self macro dereferences `FRAME_ARRAY` mutably and must only be used after
38/// the array has been initialised by `PageFrameManager::init()`.
39macro_rules! frame { ($pfn:expr) => {unsafe{FRAME_ARRAY[to($pfn)].lock()}} }
40
41/// Limine memory map request structure.
42///
43/// self forces the bootloader to provide a complete memory map, which is used
44/// to determine usable RAM and to allocate the frame metadata array.
45#[used]
46#[unsafe(link_section = ".requests")]
47static MMAP: limine::request::MemmapRequest =  limine::request::MemmapRequest::new();
48
49impl PageFrameManager {
50    /// Creates a new uninitialised page frame manager.
51    ///
52    /// The manager is not usable until `init()` is called.
53    pub const fn new() -> Self { Self {} }
54
55    /// Detects the total amount of physical memory from the Limine memory map.
56    ///
57    /// Sums the lengths of all memory map entries. self is used to calculate
58    /// the total number of 4 KiB page frames.
59    fn detect_total_physical_memory() -> usize {
60        let memmap = MMAP.response().expect("Failed to obtain memory map").entries();
61        let mut total = 0;
62        for entry in memmap {
63            total += entry.length as usize;
64        }
65        total
66    }
67
68    /// Finds a suitable region of physical memory to place the frame metadata array.
69    ///
70    /// # Arguments
71    /// * `num_frames` - Number of `PageFrame` entries to allocate.
72    ///
73    /// # Returns
74    /// A tuple `(base_physical_address, size_in_bytes)` of the chosen region.
75    ///
76    /// # Panics
77    /// Panics if no usable memory region of sufficient size is found.
78    fn find_better_place(num_frames: usize) -> (PhysicalAddress, usize) {
79        let memmap = MMAP.response().expect("Failed to obtain memory map").entries();
80        let required_size = (num_frames * core::mem::size_of::<PageFrame>()).next_multiple_of(4096);
81        let mut best_start = 0;
82        let mut best_len = 0;
83
84        for entry in memmap {
85            if entry.type_ != limine::memmap::MEMMAP_USABLE {
86                continue;
87            }
88            let len = entry.length as usize;
89            if len >= required_size && len > best_len {
90                best_len = len;
91                best_start = entry.base;
92            }
93        }
94        if best_len == 0 {
95            panic!("No suitable memory region for PageFrameInfo array (need {} bytes)", required_size);
96        }
97        (to(best_start as usize), required_size)
98    }
99
100    /// Initialises the global page frame manager.
101    ///
102    /// self function:
103    /// 1. Detects total physical memory and computes the number of frames.
104    /// 2. Finds a physical memory region large enough to hold the `PageFrame` array.
105    /// 3. Maps that region into the kernel’s virtual address space.
106    /// 4. Initialises each `PageFrame` with default values.
107    /// 5. Sets the `RESERVED` flag for all frames, then clears it for usable RAM entries.
108    /// 6. Finally reserves the frames that contain the metadata array itself.
109    ///
110    /// # Panics
111    /// Panics if the memory map is unavailable or if no suitable region is found.
112    pub fn init(&self) {
113        let total_memory = Self::detect_total_physical_memory();
114        let num_frames = total_memory >> 12;
115        let (phys_addr, size) = Self::find_better_place(num_frames);
116        let virt_addr = phys_addr.to() << 12;
117        let addr: &mut spin::Mutex<PageFrame>;
118        unsafe {
119            addr = faces::unsafe_to(to::<faces::VirtualAddress, _>(virt_addr));
120        }
121
122        debug!("Total mem: {:#x}", total_memory);
123        debug!("Total frames: {}", num_frames);
124        debug!("PFI array address: {:#x}", virt_addr);
125
126        unsafe {
127            let slice = core::slice::from_raw_parts_mut::<'static, spin::Mutex<PageFrame>>(addr, num_frames);
128            for frame in slice.iter_mut() {
129                *frame = spin::Mutex::<PageFrame>::new(PageFrame::default());
130            }
131            FRAME_ARRAY = slice;
132        }
133
134        let memmap = MMAP.response().expect("Failed to obtain memory map").entries();
135
136        for idx in 0..num_frames {
137            self.set_flags(to(idx), PageFlags::RESERVED);
138        }
139
140        for entry in memmap {
141            if entry.type_ == limine::memmap::MEMMAP_USABLE {
142                let start = (entry.base / 4096) as usize;
143                let end = ((entry.base + entry.length + 4095) / 4096) as usize;
144                for idx in start..end.min(num_frames) {
145                    self.clear_flags(to(idx), PageFlags::RESERVED);
146                }
147            }
148        }
149
150        let array_start = to(phys_addr) >> 12;
151        let array_end = (array_start + size + 4095) >> 12;
152        for idx in array_start..array_end.min(num_frames) {
153            self.set_flags(to(idx), PageFlags::RESERVED);
154        }
155    }
156}
157
158/// Type alias for a physical frame number (PFN) as defined by `faces`.
159type PFN = faces::PageFrameNumber;
160
161impl AbsPageFrameManager for PageFrameManager {
162    type Flags = PageFlags;
163    type Access = spin::MutexGuard<'static, PageFrame, spin::Spin>;
164
165    /// Checks whether a given flag is set on the specified page frame.
166    fn check_flags(&self, pfn: PFN, flag: PageFlags) -> bool {
167        frame![pfn].flags & flag != PageFlags::empty()
168    }
169
170    /// Clears the specified flags on the given page frame.
171    fn clear_flags(&self, pfn: PFN, flag: PageFlags) {
172        frame![pfn].flags &= !flag
173    }
174
175    /// Returns the maximum possible page frame number.
176    ///
177    /// # TODO
178    /// self currently returns `PFN::MAX`, which may be larger than the actual
179    /// number of frames. A proper implementation should return the highest
180    /// valid PFN based on the detected memory size.
181    fn max(&self) -> PFN {
182        to(usize::MAX)
183    }
184
185    /// Returns the minimum possible page frame number (always 0).
186    fn min(&self) -> PFN {
187        to(0)
188    }
189
190    /// Returns whether a page frame is physically present.
191    ///
192    /// # TODO
193    /// self always returns `true`. A real implementation should validate
194    /// the PFN against the number of available frames.
195    fn present(&self, _pfn: PFN) -> bool {
196        true
197    }
198
199    /// Sets the specified flags on the given page frame.
200    fn set_flags(&self, pfn: PFN, flag: PageFlags) {
201        frame!(pfn).flags |= flag
202    }
203
204    /// Returns a locked guard that provides mutable access to the page frame metadata.
205    fn get(&self, pfn: PFN) -> Self::Access {
206        frame!(pfn)
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use crate::PageFrame;
214    use spin::Mutex;
215
216    /// Create a mock frame array of given length and leak it to a 'static mut slice.
217    /// Then assign it to FRAME_ARRAY for testing.
218    unsafe fn setup_mock_frames(num_frames: usize) {
219        // Build a vector of Mutex<PageFrame> initialised with default frames.
220        let mut vec = Vec::with_capacity(num_frames);
221        for _ in 0..num_frames {
222            vec.push(Mutex::new(PageFrame::default()));
223        }
224        // Leak the vector to obtain a 'static mut slice.
225        let slice: &'static mut [Mutex<PageFrame>] = Box::leak(vec.into_boxed_slice());
226        unsafe {
227            FRAME_ARRAY = slice;
228        }
229    }
230
231    /// Reset FRAME_ARRAY to an empty slice after each test to avoid cross-test interference.
232    unsafe fn teardown_mock_frames() {
233        unsafe {
234            FRAME_ARRAY = Fa::default();
235        }
236    }
237
238    #[test]
239    fn test_flag_operations() {
240        unsafe {
241            setup_mock_frames(10);
242        }
243
244        let pfm = PageFrameManager::new();
245        let pfn = faces::to(5);
246
247        // Initially no flags should be set.
248        assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
249        assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
250
251        // Set LOCKED flag.
252        pfm.set_flags(pfn, PageFlags::LOCKED);
253        assert!(pfm.check_flags(pfn, PageFlags::LOCKED));
254        assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
255
256        // Set DIRTY flag (should keep LOCKED).
257        pfm.set_flags(pfn, PageFlags::DIRTY);
258        assert!(pfm.check_flags(pfn, PageFlags::LOCKED));
259        assert!(pfm.check_flags(pfn, PageFlags::DIRTY));
260
261        // Clear LOCKED flag.
262        pfm.clear_flags(pfn, PageFlags::LOCKED);
263        assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
264        assert!(pfm.check_flags(pfn, PageFlags::DIRTY));
265
266        // Clear DIRTY flag.
267        pfm.clear_flags(pfn, PageFlags::DIRTY);
268        assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
269        assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
270
271        unsafe {
272            teardown_mock_frames();
273        }
274    }
275
276    #[test]
277    fn test_get_frame_mut() {
278        unsafe {
279            setup_mock_frames(3);
280        }
281
282        let pfm = PageFrameManager::new();
283        let pfn = faces::to(1);
284
285        // Obtain a guard and modify the frame directly.
286        {
287            let mut guard = pfm.get(pfn);
288            guard.flags = PageFlags::SWAPCACHE;
289            guard.order = 42;
290            guard.rc = 100;
291        }
292
293        // Verify changes via flag checks.
294        assert!(pfm.check_flags(pfn, PageFlags::SWAPCACHE));
295        // Also verify using get and reading back.
296        let guard = pfm.get(pfn);
297        assert_eq!(guard.order, 42);
298        assert_eq!(guard.rc, 100);
299
300        unsafe {
301            teardown_mock_frames();
302        }
303    }
304
305    #[test]
306    fn test_min_max_present() {
307        let pfm = PageFrameManager::new();
308        // present currently always returns true
309        assert!(pfm.present(faces::to(0)));
310        assert!(pfm.present(faces::to(12345)));
311    }
312
313    #[test]
314    fn test_frame_macro() {
315        unsafe {
316            setup_mock_frames(5);
317        }
318
319        let pfn: faces::PageFrameNumber = faces::to(2);
320        // The macro frame! should give a locked guard.
321        {
322            let mut guard = frame!(pfn);
323            guard.flags = PageFlags::COMPOUND;
324        }
325        assert!(PFM.check_flags(pfn, PageFlags::COMPOUND));
326
327        unsafe {
328            teardown_mock_frames();
329        }
330    }
331}