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