Skip to main content

ostd/mm/fault/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Page fault handling.
4//!
5//! This module manages a handler that can address page faults occurring in the kernel due to
6//! user-space addresses. For example, this occurs during [`FallibleVmRead::read_fallible`] and
7//! [`FallibleVmWrite::write_fallible`].
8//!
9//! If the page fault is handled successfully, the read/write process continues and the fault
10//! address is retried. Otherwise, those methods will return an error to indicate that the
11//! read/write process cannot be completed.
12//!
13//! [`FallibleVmRead::read_fallible`]: super::FallibleVmRead::read_fallible
14//! [`FallibleVmWrite::write_fallible`]: super::FallibleVmWrite::write_fallible
15
16mod ex_table;
17
18use spin::Once;
19
20#[cfg(not(target_arch = "loongarch64"))]
21use crate::arch::cpu::context::CpuException;
22#[cfg(target_arch = "loongarch64")]
23use crate::arch::cpu::context::CpuExceptionInfo as CpuException;
24use crate::{
25    arch::trap::TrapFrame,
26    mm::{MAX_USERSPACE_VADDR, Vaddr, fault::ex_table::ExTable},
27};
28
29/// A handler that handles page faults caused by user-space addresses.
30///
31/// The page fault is described in [`CpuException`]. If it can be resolved successfully,
32/// this method will return `Ok(())`. Otherwise, it should return `Err(())`.
33pub type UserPageFaultHandler = fn(&CpuException) -> Result<(), ()>;
34
35static USER_PAGE_FAULT_HANDLER: Once<UserPageFaultHandler> = Once::new();
36
37/// Injects a custom handler for page faults that occur in the kernel and
38/// are caused by user-space addresses.
39///
40/// The function may be called only once; subsequent calls take no effect.
41pub fn inject_user_page_fault_handler(handler: UserPageFaultHandler) {
42    USER_PAGE_FAULT_HANDLER.call_once(|| handler);
43}
44
45/// The common interface that every CPU architecture-specific [`TrapFrame`] implements.
46pub(crate) trait TrapFrameApi {
47    /// Sets the instruction pointer.
48    fn set_instruction_pointer(&mut self, ip: usize);
49
50    /// Gets the instruction pointer.
51    fn instruction_pointer(&self) -> usize;
52}
53
54/// Handles page fault from user space.
55pub(crate) fn handle_user_page_fault(
56    f: &mut TrapFrame,
57    exception: &CpuException,
58    fault_addr: Vaddr,
59) {
60    // The actual user space implementation should be responsible
61    // for providing mechanism to treat the 0 virtual address.
62    if !(0..MAX_USERSPACE_VADDR).contains(&fault_addr) {
63        panic!(
64            "Cannot handle kernel page fault: {:#x?}; trapframe: {:#x?}",
65            exception, f
66        );
67    }
68
69    let handler = USER_PAGE_FAULT_HANDLER
70        .get()
71        .expect("a page fault handler is missing");
72
73    let res = handler(exception);
74    // Copying bytes by bytes can recover directly
75    // if handling the page fault successfully.
76    if res.is_ok() {
77        return;
78    }
79
80    // Use the exception table to recover to normal execution.
81    let inst_addr = f.instruction_pointer();
82    if let Some(new_addr) = ExTable::find_recovery_inst_addr(inst_addr) {
83        f.set_instruction_pointer(new_addr);
84    } else {
85        panic!("Cannot handle user page fault; trapframe: {:#x?}", f);
86    }
87}