keos_project3/
lazy_pager.rs

1//! # Lazy Paging
2//!
3//! The lazy paging or demand paging is an another policy for the
4//! paging, which used by modern operating systems. Unlike the [`EagerPager`]
5//! that you implemented in project 2, the [`LazyPager`] defers physical page
6//! allocation until a page fault occurs. This method optimizes memory usage by
7//! mapping memory pages **on demand**, rather than preallocating them.
8//!
9//! Instead of allocating physical memory during the `mmap` call, the OS records
10//! **metadata** about the mapping and waits to allocate physical memory until
11//! the first **page fault** on that region. When a page fault occurs, the
12//! kernel allocates and maps the required physical page.
13//! In other words, **page table entries are created only when accessed**.
14//!
15//! ## Page Fault in KeOS
16//!
17//! The main function responsible for handling page faults lies in
18//! [`Task::page_fault`]. This resolves the page fault reason into
19//! [`PageFaultReason`] by reading the `cr2`, which contains faulting address,
20//! and decoding the error code on the interrupt stack.
21//!
22//! It then delegates the page fault handling into the
23//! [`LazyPager::handle_page_fault`]. This method is responsible to look up the
24//! lazy mapping metadata recorded during the `mmap` and determine whether the
25//! fault is bogus fault or not. If the address is valid, it should allocate a
26//! new physical page and maps the page into page table. Otherwise, killing the
27//! current process by returning the [`KernelError`].
28//!
29//! ## [`VmAreaStruct`]
30//!
31//! The [`VmAreaStruct`] represents a range of virtual addresses that share the
32//! same memory permissions, similar to the Linux kernel's `struct
33//! vm_area_struct`. It serves as the core metadata structure for memory-mapped
34//! regions created via `mmap`, capturing the virtual range and the method
35//! for populating that region's contents on access.
36//!
37//! Each [`VmAreaStruct`] is associated with an implementation of the
38//! [`MmLoader`] trait, which defines how the contents of a page should be
39//! supplied when the region is accessed. This trait-based abstraction
40//! enables the kernel to support multiple types of memory mappings in a uniform
41//! way. For instance, file-backed mappings use a [`FileBackedLoader`], which
42//! reads contents from a file, while anonymous mappings use an [`AnonLoader`],
43//! which typically supplies zero-filled pages. Each loader implementation can
44//! maintain its own internal state, supporting extensibility and encapsulates
45//! the complexity of mapping behavior within each loader.
46//!
47//! The [`MmLoader`] trait provides a single method, `load`, which is called
48//! during demand paging when a page fault occurs at an address within the
49//! associated [`VmAreaStruct`]. The method must return a fully initialized
50//! [`Page`] object corresponding to that virtual address. The returned page is
51//! then mapped into the page table by the pager.
52//!
53//! This loader-based architecture provides a clean separation of concerns:
54//! [`VmAreaStruct`] tracks regions and permissions, while [`MmLoader`]
55//! encapsulates how pages are provisioned. This allows KeOS to support flexible
56//! and efficient memory models while maintaining clean abstractions.
57//!
58//! ## Implementation Requirements
59//! You need to implement the followings:
60//! - [`LazyPager`]
61//! - [`LazyPager::new`]
62//! - [`LazyPager::mmap`]
63//! - [`LazyPager::munmap`]
64//! - [`LazyPager::get_user_page`]
65//! - [`LazyPager::access_ok`]
66//! - [`PageFaultReason::is_demand_paging_fault`]
67//! - [`LazyPager::do_lazy_load`]
68//! - [`VmAreaStruct`]
69//! - [`FileBackedLoader`]
70//! - [`FileBackedLoader::load`]
71//!
72//! After implement the functionalities, move on to the next [`section`].
73//!
74//! [`section`]: mod@crate::fork
75//! [`EagerPager`]: ../../keos_project2/mmap/struct.EagerPager.html
76
77use alloc::sync::Arc;
78#[cfg(doc)]
79use keos::task::Task;
80use keos::{
81    KernelError,
82    addressing::Va,
83    fs::RegularFile,
84    mm::{Page, PageRef, page_table::Permission},
85    task::PFErrorCode,
86};
87use keos_project2::{page_table::PageTable, pager::Pager};
88
89/// A trait for loading the contents of a virtual memory page on demand.
90///
91/// This trait abstracts the mechanism for supplying the contents of a page
92/// during **demand paging**. It is used by a lazy pager when handling a
93/// page fault for a region that has not yet been populated.
94///
95/// Implementors of this trait can define custom behaviors, such as reading
96/// from a file, or zero-filling anonymous pages.
97pub trait MmLoader
98where
99    Self: Send + Sync,
100{
101    /// Loads and returns the content for the page at the given virtual address.
102    ///
103    /// The pager will call this function when a page fault occurs at `addr`
104    /// within the corresponding [`VmAreaStruct`]. This method must return a
105    /// fully initialized [`Page`] containing the data for that virtual page.
106    ///
107    /// # Parameters
108    /// - `addr`: The virtual address of the page to be loaded. This address is
109    ///   guaranteed to lie within the virtual memory area associated with this
110    ///   loader.
111    ///
112    /// # Returns
113    /// - A newly allocated [`Page`] containing the initialized data for the
114    ///   page.
115    fn load(&self, addr: Va) -> Page;
116}
117
118/// A loader for anonymous memory regions.
119///
120/// [`AnonLoader`] is used for memory mappings that are not backed by any file.
121/// When a page fault occurs, this loader simply returns a newly allocated
122/// zero-filled [`Page`].
123pub struct AnonLoader {}
124impl MmLoader for AnonLoader {
125    /// Returns a zero-filled page for the given virtual address.
126    ///
127    /// Since anonymous memory is not backed by any persistent source, this
128    /// implementation always returns a freshly zero-initialized [`Page`].
129    fn load(&self, _addr: Va) -> Page {
130        Page::new()
131    }
132}
133
134/// A loader for file-backed memory regions.
135///
136/// [`FileBackedLoader`] is used for memory mappings backed by files, such as
137/// when `mmap` is called with a regular file. This loader reads data from
138/// the underlying file starting at a specific offset and returns it in a
139/// newly allocated [`Page`].
140///
141/// The offset within the file is determined based on the virtual address
142/// passed to `load`, relative to the mapping’s start address and offset.
143///
144/// This loader handles partial page reads and fills any unread bytes with
145/// zeroes.
146pub struct FileBackedLoader {
147    // TODO: Define any member you need.
148}
149
150impl MmLoader for FileBackedLoader {
151    /// Loads a page from the file based on the given virtual address.
152    ///
153    /// This implementation calculates the offset within the file and reads
154    /// up to one page of data into memory. If the read returns fewer than
155    /// `PAGE_SIZE` bytes, the remainder of the page is zero-filled.
156    fn load(&self, addr: Va) -> Page {
157        todo!()
158    }
159}
160
161/// Represents a memory-mapped region within a process's virtual address space,
162/// corresponding to the Linux kernel's `struct vm_area_struct`.
163///
164/// Each [`VmAreaStruct`] corresponds to a contiguous region with a specific
165/// mapping behavior—e.g., anonymous memory or file-backed memory.
166/// This abstraction allows the pager to defer the actual page population
167/// until the memory is accessed, using demand paging.
168///
169/// The key component is the [`MmLoader`], which defines how to load
170/// the contents of a page when it is accessed (e.g., reading from a file
171/// or zero-filling the page).
172#[derive(Clone)]
173pub struct VmAreaStruct {
174    /// A handle to the memory loader for this region.
175    ///
176    /// The [`MmLoader`] defines how to populate pages in this VMA during
177    /// lazy loading. The loader must be thread-safe and cloneable.
178    pub loader: Arc<dyn MmLoader>,
179    // TODO: Define any member you need.
180}
181
182/// The [`LazyPager`] structure implements lazy paging, where memory pages are
183/// mapped only when accessed (on page fault), instead of during `mmap` calls.
184#[derive(Clone)]
185pub struct LazyPager {
186    // TODO: Define any member you need.
187}
188
189impl Pager for LazyPager {
190    /// Creates a new instance of [`LazyPager`].
191    ///
192    /// This constructor initializes an empty [`LazyPager`] struct.
193    fn new() -> Self {
194        LazyPager {
195            // TODO: Initialize any member you need.
196        }
197    }
198
199    /// Memory map function (`mmap`) for lazy paging.
200    ///
201    /// This function creates the metadata for memory mappings, and delegate the
202    /// real mappings on page fault.
203    ///
204    /// Returns an address for the mapped area.
205    fn mmap(
206        &mut self,
207        _page_table: &mut PageTable,
208        addr: Va,
209        size: usize,
210        prot: Permission,
211        file: Option<&RegularFile>,
212        offset: usize,
213    ) -> Result<usize, KernelError> {
214        todo!()
215    }
216
217    /// Memory unmap function (`munmap`) for lazy paging.
218    ///
219    /// This function would unmap a previously mapped memory region, releasing
220    /// any associated resources.
221    ///
222    /// # Returns
223    /// - Zero (if succeed) or an error ([`KernelError`]).
224    fn munmap(&mut self, page_table: &mut PageTable, addr: Va) -> Result<usize, KernelError> {
225        todo!()
226    }
227
228    /// Find a mapped page at the given virtual address. If the page for addr is
229    /// not loaded, load it and then returns.
230    ///
231    /// This function searches for a memory page mapped at `addr` and, if found,
232    /// returns a tuple of [`PageRef`] to the page and its corresponding
233    /// [`Permission`] flags.
234    ///
235    /// # Parameters
236    /// - `addr`: The virtual address ([`Va`]) of the page to find.
237    ///
238    /// # Returns
239    /// - `Some(([`PageRef`], [`Permission`]))`: If the page is found.
240    /// - `None`: If no mapped page is found at `addr`.
241    fn get_user_page(
242        &mut self,
243        page_table: &mut PageTable,
244        addr: Va,
245    ) -> Option<(PageRef<'_>, Permission)> {
246        todo!()
247    }
248
249    /// Checks whether access to the given virtual address is permitted.
250    ///
251    /// This function verifies that a virtual address `va` is part of a valid
252    /// memory mapping and that the requested access type (read or write) is
253    /// allowed by the page's protection flags. Note that this does not trigger
254    /// the demand paging.
255    fn access_ok(&self, va: Va, is_write: bool) -> bool {
256        todo!()
257    }
258}
259
260/// Represents the reason for a page fault in a virtual memory system.
261///
262/// This struct is used to capture various details about a page fault, including
263/// the faulting address, the type of access that caused the fault (read or
264/// write).
265#[derive(Debug)]
266pub struct PageFaultReason {
267    /// The address that caused the page fault.
268    ///
269    /// This is the virtual address that the program attempted to access when
270    /// the page fault occurred. It can be useful for debugging and
271    /// identifying the location in memory where the fault happened.
272    pub fault_addr: Va,
273
274    /// Indicates whether the fault was due to a write access violation.
275    ///
276    /// A value of `true` means that the program attempted to write to a page
277    /// that was marked as read-only or otherwise restricted from write
278    /// access. A value of `false` indicates that the access was a read
279    /// or the page allowed write access.
280    pub is_write_access: bool,
281
282    /// Indicates whether the page that caused the fault is present in memory.
283    ///
284    /// A value of `true` means that the page is currently loaded into memory,
285    /// and the fault may have occurred due to other conditions, such as
286    /// protection violations. A value of `false` means the page is not present
287    /// in memory (e.g., the page might have been swapped out or mapped as a
288    /// non-resident page).
289    pub is_present: bool,
290}
291
292impl PageFaultReason {
293    /// Probe the cause of page fault into a [`PageFaultReason`].
294    ///
295    /// This function decodes a hardware-provided [`PFErrorCode`],
296    /// generated by the CPU when a page fault occurs, into a structured
297    /// [`PageFaultReason`] that the kernel can interpret.
298    ///
299    /// The decoded information includes:
300    /// - The type of access (`is_write_access`),
301    /// - Whether the faulting page is currently mapped (`is_present`),
302    /// - The faulting virtual address (`fault_addr`).
303    pub fn new(ec: PFErrorCode, cr2: Va) -> Self {
304        PageFaultReason {
305            fault_addr: cr2,
306            is_write_access: ec.contains(PFErrorCode::WRITE_ACCESS),
307            is_present: ec.contains(PFErrorCode::PRESENT),
308        }
309    }
310
311    /// Returns `true` if the fault is due to **demand paging**.
312    ///
313    /// # Returns
314    /// - `true` if this fault was caused by an demand paging.
315    /// - `false` otherwise.
316    #[inline]
317    pub fn is_demand_paging_fault(&self) -> bool {
318        todo!()
319    }
320}
321
322impl LazyPager {
323    /// Handles a page fault by performing **lazy loading** of the faulting
324    /// page.
325    ///
326    /// This method is invoked when a page fault occurs due to **demand
327    /// paging**— that is, when a program accesses a virtual address that is
328    /// validly mapped but not yet backed by a physical page. This function
329    /// allocates and installs the corresponding page into the page table on
330    /// demand.
331    ///
332    /// The kernel may initialize the page from a file (if the mapping was
333    /// file-backed) or zero-fill it (if anonymous). The newly loaded page
334    /// must also be mapped with the correct permissions, as defined at the
335    /// time of the original `mmap`.
336    ///
337    /// # Parameters
338    /// - `page_table`: The page table of the faulting process.
339    /// - `reason`: The [`PageFaultReason`] that describes the faulting reason.
340    ///
341    /// This must indicate a **demand paging fault**.
342    ///
343    /// # Returns
344    /// - `Ok(())` if the page was successfully loaded and mapped.
345    /// - `Err(KernelError)`: If the faulting address is invalid, out of bounds,
346    ///   or if page allocation fails.
347    pub fn do_lazy_load(
348        &mut self,
349        page_table: &mut PageTable,
350        reason: &PageFaultReason,
351    ) -> Result<(), KernelError> {
352        todo!()
353    }
354
355    /// Handles a **page fault** by allocating a physical page and updating the
356    /// page table.
357    ///
358    /// This function is called when a process accesses a lazily mapped page
359    /// that has not been allocated yet. The function must:
360    /// 1. Identify the faulting virtual address from [`PageFaultReason`].
361    /// 2. Check if the address was previously recorded in `mmap` metadata.
362    /// 3. Allocate a new physical page.
363    /// 4. Update the page table with the new mapping.
364    /// 5. Invalidate the TLB entry to ensure memory consistency.
365    ///
366    /// # Arguments
367    /// - `page_table`: Mutable reference to the page table.
368    /// - `reason`: The cause of the page fault, including the faulting address.
369    ///
370    /// If the faulting address was not mapped via `mmap`, the system should
371    /// trigger a **segmentation fault**, resulting process exit.
372    pub fn handle_page_fault(
373        &mut self,
374        page_table: &mut PageTable,
375        reason: &PageFaultReason,
376    ) -> Result<(), KernelError> {
377        if reason.is_demand_paging_fault() {
378            self.do_lazy_load(page_table, reason)
379        } else if reason.is_cow_fault() {
380            self.do_copy_on_write(page_table, reason)
381        } else {
382            Err(KernelError::InvalidAccess)
383        }
384    }
385}