kernel/arch/x86_64/interrupts/exceptions/
_14_page_fault.rs1use 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::layout::USER_MAX;
12use crate::mm::pmm::FRAME_SIZE;
13use crate::mm::vmm::{Vmm, VmmError};
14use crate::mm::vmm::traits::VmmPaging;
15use crate::mm::vmm::vma::VmaFlags;
16use crate::state::kstate::KSTATE;
17use core::fmt::Write;
18use x86_64::registers::control::Cr2;
19use x86_64::structures::idt::{InterruptStackFrame, PageFaultErrorCode};
20use crate::arch::x86_64::exception_table::search_exception_table;
21
22pub extern "x86-interrupt" fn handler(
24 mut isf: InterruptStackFrame,
25 error_code: PageFaultErrorCode
26) {
27 let faulting_virt = VirtAddr::new(Cr2::read_raw());
28
29 let is_ring3 = error_code.contains(PageFaultErrorCode::USER_MODE);
30 let is_user_addr = faulting_virt.as_u64() < USER_MAX;
31
32 match (is_ring3, is_user_addr) {
33 (true, true) => {
35 handle_user_address_pf(
36 false,
37 &mut isf,
38 error_code,
39 faulting_virt
40 );
41 }
42
43 (true, false) => {
45 kernel_panic(
47 PanicCode::Unknown,
48 "Attempted to access kernel address from userspace, EXSEGV not implemented yet"
49 );
50 }
51
52 (false, true) => {
54 handle_user_address_pf(
55 true,
56 &mut isf,
57 error_code,
58 faulting_virt
59 );
60 }
61
62 (false, false) => {
64 handle_kernel_address_pf(
65 &isf,
66 error_code,
67 faulting_virt
68 );
69 }
70 }
71
72
73}
74
75#[inline(always)]
77fn handle_kernel_address_pf(
78 isf: &InterruptStackFrame,
79 error_code: PageFaultErrorCode,
80 faulting_virt: VirtAddr
81) {
82 let addr_space = unsafe { KSTATE.mm.kernel_addr_space().lock() };
85 let vma = addr_space.find_vma(faulting_virt).unwrap_or_else(
86 || kernel_pf_panic(
87 "Could not find VMA to map a page for",
88 isf, error_code, faulting_virt
89 )
90 );
91
92 let flags = vma.flags;
93 let page_ptr = addr_space.page_ptr();
94 drop(addr_space);
95
96 let mut pmm = unsafe { KSTATE.mm.pmm().lock() };
99 let frame = pmm.alloc_frame().unwrap_or_else(
100 || kernel_pf_panic(
101 "Could not map a new page, out of memory",
102 isf, error_code, faulting_virt
103 )
104 );
105
106 unsafe {
109 if let Err(error) = Vmm::map_page(
110 &mut pmm,
111 page_ptr,
112 faulting_virt.align_down(FRAME_SIZE),
113 frame,
114 PageType::Normal,
115 Vmm::vma_flags_to_page_flags(flags)
116 ) {
117 kernel_panic(
120 PanicCode::OutOfMemory,
121 "Could not lazy-map a page for the kernel, out of memory"
122 );
123 }
124 }
125}
126
127#[inline(always)]
130fn handle_user_address_pf(
131 from_ring0: bool,
132 isf: &mut InterruptStackFrame,
133 error_code: PageFaultErrorCode,
134 faulting_virt: VirtAddr
135) {
136 let active_pid = KSTATE.sched.active_pid();
137
138 let (vma_flags, page_ptr) = unsafe {
140 KSTATE.sched.with_process(active_pid, |proc| {
141 (proc.addr_space.find_vma(faulting_virt).map(|vma| vma.flags), proc.addr_space.page_ptr())
142 }).unwrap_or_else(
143 || kernel_pf_panic(
144 "Attempted to fetch process data of non-existent process in page fault handler (this is likely a kernel bug)",
145 isf, error_code, faulting_virt
146 )
147 )
148 };
149
150 let Some(vma_flags) = vma_flags else {
151 user_pf_cant_map_handler(
152 from_ring0,
153 isf,
154 error_code,
155 faulting_virt
156 );
157 return;
158 };
159
160 let mapping_allowed =
161 if error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION) { false }
162 else if error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE) && !vma_flags.contains(VmaFlags::WRITE) { false }
163 else if error_code.contains(PageFaultErrorCode::INSTRUCTION_FETCH) && !vma_flags.contains(VmaFlags::EXEC) { false }
164 else { true };
165
166 if !mapping_allowed {
167 user_pf_cant_map_handler(
168 from_ring0,
169 isf,
170 error_code,
171 faulting_virt
172 );
173 return;
174 }
175
176 let mut pmm = unsafe { KSTATE.mm.pmm().lock() };
179 let frame = pmm.alloc_frame_zeroed().unwrap_or_else(
180 || kernel_pf_panic(
183 "Could not lazy-map a page for a user process, out of memory (this is a kernel bug)",
184 isf, error_code, faulting_virt
185 )
186 );
187
188 unsafe {
191 if let Err(error) = Vmm::map_page(
192 &mut pmm,
193 page_ptr,
194 faulting_virt.align_down(FRAME_SIZE),
195 frame,
196 PageType::Normal,
197 Vmm::vma_flags_to_page_flags(vma_flags)
198 ) {
199 kernel_panic(
204 PanicCode::OutOfMemory,
205 "Could not lazy-map a page for a user process, out of memory (this is a kernel bug)"
206 );
207 }
208 }
209}
210
211#[cold]
213fn user_pf_cant_map_handler(
214 from_ring0: bool,
215 isf: &mut InterruptStackFrame,
216 error_code: PageFaultErrorCode,
217 faulting_virt: VirtAddr
218) {
219 if from_ring0 {
220 match search_exception_table(isf.instruction_pointer.as_u64()) {
221 Some(fixup) => {
222 unsafe {
226 isf.as_mut().update(
227 |frame| frame.instruction_pointer = x86_64::VirtAddr::new(fixup)
228 );
229 }
230 }
231 None => {
232 kernel_pf_panic(
233 "Kernel accessed unmapped user memory outside of the exception table",
234 isf, error_code, faulting_virt
235 )
236 }
237 }
238 } else {
239 kernel_pf_panic(
241 "Process tried accessing unmapped memory (from userspace); TODO: send EXSEGV",
242 isf, error_code, faulting_virt
243 )
244 }
245}
246
247#[cold]
250#[inline(never)]
251fn kernel_pf_panic(
252 panic_message: &str,
253 isf: &InterruptStackFrame,
254 error_code: PageFaultErrorCode,
255 faulting_virt: VirtAddr
256) -> ! {
257 let mut fmt_buffer = FmtBuffer::<1024>::new();
258 let _ = write!(
259 &mut fmt_buffer,
260 "{}\n\nCR2: {:x}\nError Code: {:?}\n\n{:#?}",
261 panic_message, faulting_virt, error_code, isf
262 );
263 kernel_panic(
264 PanicCode::PageFault,
265 fmt_buffer.as_str(),
266 );
267}