Skip to main content

kernel/arch/x86_64/interrupts/exceptions/
_14_page_fault.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Page-fault exception handler (vector 14, `#PF`).
3//!
4//! Authors: MarioS271
5
6use crate::arch::x86_64::mm::vmm::page_type::PageType;
7use crate::lib::addr::VirtAddr;
8use crate::lib::panic::kernel_panic;
9use crate::lib::panic_codes::PanicCode;
10use crate::lib::types::fmt_buffer::FmtBuffer;
11use crate::mm::pmm::FRAME_SIZE;
12use crate::mm::vmm::traits::VmmPaging;
13use crate::mm::vmm::Vmm;
14use crate::state::kstate::KSTATE;
15use core::fmt::Write;
16use x86_64::registers::control::Cr2;
17use x86_64::structures::idt::{InterruptStackFrame, PageFaultErrorCode};
18
19/// Panic with the faulting address (from CR2), the error code, and the interrupt stack frame.
20pub extern "x86-interrupt" fn handler(
21    isf: InterruptStackFrame,
22    error_code: PageFaultErrorCode
23) {
24    let faulting_virt = VirtAddr::new(Cr2::read_raw());
25
26    if error_code.contains(PageFaultErrorCode::USER_MODE) {
27        handle_user_pf(faulting_virt);
28    } else {
29        handle_kernel_pf(isf, error_code, faulting_virt);
30    }
31}
32
33/// Handle a kernel page fault; maps a new page if VMA is found, otherwise panics
34#[inline(always)]
35fn handle_kernel_pf(
36    isf: InterruptStackFrame,
37    error_code: PageFaultErrorCode,
38    faulting_virt: VirtAddr
39) {
40    // Safety: interrupts only get enabled after full mm kernel boot, which guarantees that
41    // kernel_addr_space is already initialized
42    let addr_space = unsafe { KSTATE.mm.kernel_addr_space().lock() };
43    let vma = addr_space.find_vma(faulting_virt).unwrap_or_else(
44        || kernel_pf_panic(
45            "Could not find VMA to map a page for",
46            &isf, error_code, faulting_virt
47        )
48    );
49
50    let flags = vma.flags;
51    let page_ptr = addr_space.page_ptr();
52    drop(addr_space);
53
54    // Safety: interrupts only get enabled after full mm kernel boot, which guarantees that
55    // pmm is already initialized
56    let mut pmm = unsafe { KSTATE.mm.pmm().lock() };
57    let frame = pmm.alloc_frame().unwrap_or_else(
58        || kernel_pf_panic(
59            "Could not map a new page, out of memory",
60            &isf, error_code, faulting_virt
61        )
62    );
63
64    // Safety: page_ptr is the correct virtual address of the kernel PML4 and frame is a valid
65    // PMM-allocated memory frame
66    unsafe {
67        if Vmm::map_page(
68            &mut pmm,
69            page_ptr,
70            faulting_virt.align_down(FRAME_SIZE),
71            frame,
72            PageType::Normal,
73            Vmm::vma_flags_to_page_flags(flags)
74        ).is_err() {
75            kernel_panic(
76                PanicCode::OutOfMemory,
77                "Could not lazy-map a page, out of memory"
78            );
79        }
80    }
81}
82
83#[inline(always)]
84fn handle_user_pf(
85    faulting_virt: VirtAddr
86) {
87    todo!();
88}
89
90
91#[inline(always)]
92fn kernel_pf_panic(
93    panic_message: &str,
94    isf: &InterruptStackFrame,
95    error_code: PageFaultErrorCode,
96    faulting_virt: VirtAddr
97) -> ! {
98    let mut fmt_buffer = FmtBuffer::<512>::new();
99    let _ = write!(
100        &mut fmt_buffer,
101        "{}\n\nCR2: {:x}\nError Code: {:?}\n\n{:#?}",
102        panic_message, faulting_virt, error_code, isf
103    );
104    kernel_panic(
105        PanicCode::PageFault,
106        fmt_buffer.as_str(),
107    );
108}