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}