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, info};
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        info!("pfm: Initializing page frame array");
127
128        unsafe {
129            let slice = core::slice::from_raw_parts_mut::<'static, spin::Mutex<PageFrame>>(addr, num_frames);
130            for frame in slice.iter_mut() {
131                *frame = spin::Mutex::<PageFrame>::new(PageFrame::new());
132            }
133            FRAME_ARRAY = slice;
134        }
135
136        info!("pfm: Obtaining memory map from bootloader");
137
138        let memmap = MMAP.response().expect("Failed to obtain memory map").entries();
139
140        info!("pfm: Reserving whole array");
141
142        for idx in 0..num_frames {
143            self.set_flags(to(idx), PageFlags::RESERVED);
144        }
145
146        info!("pfm: Searching usable memory");
147
148        for entry in memmap {
149            if entry.type_ == limine::memmap::MEMMAP_USABLE {
150                let start = (entry.base / 4096) as usize;
151                let end = ((entry.base + entry.length + 4095) / 4096) as usize;
152                for idx in start..end.min(num_frames) {
153                    self.clear_flags(to(idx), PageFlags::RESERVED);
154                }
155            }
156        }
157
158        info!("pfm: Reserving system memory areas");
159
160        let array_start = to(phys_addr) >> 12;
161        let array_end = (array_start + size + 4095) >> 12;
162        for idx in array_start..array_end.min(num_frames) {
163            self.set_flags(to(idx), PageFlags::RESERVED);
164        }
165    }
166}
167
168/// Type alias for a physical frame number (PFN) as defined by `faces`.
169type PFN = faces::PageFrameNumber;
170
171impl AbsPageFrameManager for PageFrameManager {
172    type Flags = PageFlags;
173    type Access = spin::MutexGuard<'static, PageFrame, spin::Spin>;
174
175    /// Checks whether a given flag is set on the specified page frame.
176    fn check_flags(&self, pfn: PFN, flag: PageFlags) -> bool {
177        frame![pfn].flags & flag != PageFlags::empty()
178    }
179
180    /// Clears the specified flags on the given page frame.
181    fn clear_flags(&self, pfn: PFN, flag: PageFlags) {
182        frame![pfn].flags &= !flag
183    }
184
185    /// Returns the maximum possible page frame number.
186    ///
187    /// # TODO
188    /// self currently returns `PFN::MAX`, which may be larger than the actual
189    /// number of frames. A proper implementation should return the highest
190    /// valid PFN based on the detected memory size.
191    fn max(&self) -> PFN {
192        to(usize::MAX)
193    }
194
195    /// Returns the minimum possible page frame number (always 0).
196    fn min(&self) -> PFN {
197        to(0)
198    }
199
200    /// Returns whether a page frame is physically present.
201    ///
202    /// # TODO
203    /// self always returns `true`. A real implementation should validate
204    /// the PFN against the number of available frames.
205    fn present(&self, _pfn: PFN) -> bool {
206        true
207    }
208
209    /// Sets the specified flags on the given page frame.
210    fn set_flags(&self, pfn: PFN, flag: PageFlags) {
211        frame!(pfn).flags |= flag
212    }
213
214    /// Returns a locked guard that provides mutable access to the page frame metadata.
215    fn get(&self, pfn: PFN) -> Self::Access {
216        frame!(pfn)
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223    use crate::PageFrame;
224    use spin::Mutex;
225
226    /// Create a mock frame array of given length and leak it to a 'static mut slice.
227    /// Then assign it to FRAME_ARRAY for testing.
228    unsafe fn setup_mock_frames(num_frames: usize) {
229        // Build a vector of Mutex<PageFrame> initialised with default frames.
230        let mut vec = Vec::with_capacity(num_frames);
231        for _ in 0..num_frames {
232            vec.push(Mutex::new(PageFrame::default()));
233        }
234        // Leak the vector to obtain a 'static mut slice.
235        let slice: &'static mut [Mutex<PageFrame>] = Box::leak(vec.into_boxed_slice());
236        unsafe {
237            FRAME_ARRAY = slice;
238        }
239    }
240
241    /// Reset FRAME_ARRAY to an empty slice after each test to avoid cross-test interference.
242    unsafe fn teardown_mock_frames() {
243        unsafe {
244            FRAME_ARRAY = Fa::default();
245        }
246    }
247
248    #[test]
249    fn test_flag_operations() {
250        unsafe {
251            setup_mock_frames(10);
252        }
253
254        let pfm = PageFrameManager::new();
255        let pfn = faces::to(5);
256
257        // Initially no flags should be set.
258        assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
259        assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
260
261        // Set LOCKED flag.
262        pfm.set_flags(pfn, PageFlags::LOCKED);
263        assert!(pfm.check_flags(pfn, PageFlags::LOCKED));
264        assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
265
266        // Set DIRTY flag (should keep LOCKED).
267        pfm.set_flags(pfn, PageFlags::DIRTY);
268        assert!(pfm.check_flags(pfn, PageFlags::LOCKED));
269        assert!(pfm.check_flags(pfn, PageFlags::DIRTY));
270
271        // Clear LOCKED flag.
272        pfm.clear_flags(pfn, PageFlags::LOCKED);
273        assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
274        assert!(pfm.check_flags(pfn, PageFlags::DIRTY));
275
276        // Clear DIRTY flag.
277        pfm.clear_flags(pfn, PageFlags::DIRTY);
278        assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
279        assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
280
281        unsafe {
282            teardown_mock_frames();
283        }
284    }
285
286    #[test]
287    fn test_get_frame_mut() {
288        unsafe {
289            setup_mock_frames(3);
290        }
291
292        let pfm = PageFrameManager::new();
293        let pfn = faces::to(1);
294
295        // Obtain a guard and modify the frame directly.
296        {
297            let mut guard = pfm.get(pfn);
298            guard.flags = PageFlags::SWAPCACHE;
299            guard.order = 42;
300            guard.rc = 100;
301        }
302
303        // Verify changes via flag checks.
304        assert!(pfm.check_flags(pfn, PageFlags::SWAPCACHE));
305        // Also verify using get and reading back.
306        let guard = pfm.get(pfn);
307        assert_eq!(guard.order, 42);
308        assert_eq!(guard.rc, 100);
309
310        unsafe {
311            teardown_mock_frames();
312        }
313    }
314
315    #[test]
316    fn test_min_max_present() {
317        let pfm = PageFrameManager::new();
318        // present currently always returns true
319        assert!(pfm.present(faces::to(0)));
320        assert!(pfm.present(faces::to(12345)));
321    }
322
323    #[test]
324    fn test_frame_macro() {
325        unsafe {
326            setup_mock_frames(5);
327        }
328
329        let pfn: faces::PageFrameNumber = faces::to(2);
330        // The macro frame! should give a locked guard.
331        {
332            let mut guard = frame!(pfn);
333            guard.flags = PageFlags::COMPOUND;
334        }
335        assert!(PFM.check_flags(pfn, PageFlags::COMPOUND));
336
337        unsafe {
338            teardown_mock_frames();
339        }
340    }
341}