1#![allow(dead_code)]
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12
13use crate::{
14 error::KernelError,
15 mm::{PageFlags, VirtualAddress, PAGE_SIZE},
16};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum PageFaultReason {
21 NotPresent,
23 ProtectionViolation,
25 WriteToReadOnly,
27 ExecuteNoExecute,
29 UserModeKernelAccess,
31}
32
33#[derive(Debug, Clone, Copy)]
35pub struct PageFaultInfo {
36 pub faulting_address: u64,
38 pub reason: PageFaultReason,
40 pub was_write: bool,
42 pub was_user_mode: bool,
44 pub instruction_pointer: u64,
46}
47
48const _STACK_GUARD_SIZE: usize = PAGE_SIZE;
50
51const MAX_STACK_GROWTH: usize = 8 * 1024 * 1024;
54
55pub fn handle_page_fault(info: PageFaultInfo) -> Result<(), KernelError> {
66 if let Ok(()) = try_demand_page(&info) {
68 return Ok(());
69 }
70
71 if info.was_write {
73 if let Ok(()) = try_copy_on_write(&info) {
74 return Ok(());
75 }
76 }
77
78 if let Ok(()) = try_stack_growth(&info) {
80 return Ok(());
81 }
82
83 signal_segv(&info)
85}
86
87fn try_demand_page(info: &PageFaultInfo) -> Result<(), KernelError> {
97 let process = crate::process::current_process().ok_or(KernelError::NotInitialized {
98 subsystem: "process",
99 })?;
100
101 let vaddr = VirtualAddress::new(info.faulting_address);
102
103 let memory_space = process.memory_space.lock();
105
106 #[cfg(feature = "alloc")]
107 {
108 let mapping = memory_space.find_mapping(vaddr);
109 match mapping {
110 Some(m) => {
111 let offset = info.faulting_address - m.start.as_u64();
114 let page_index = (offset / PAGE_SIZE as u64) as usize;
115
116 if page_index < m.physical_frames.len() {
117 return Err(KernelError::InvalidAddress {
119 addr: info.faulting_address as usize,
120 });
121 }
122
123 if info.was_write && !m.flags.contains(PageFlags::WRITABLE) {
125 return Err(KernelError::PermissionDenied {
126 operation: "write to read-only mapping",
127 });
128 }
129
130 if info.was_user_mode && !m.flags.contains(PageFlags::USER) {
131 return Err(KernelError::PermissionDenied {
132 operation: "user access to kernel mapping",
133 });
134 }
135
136 drop(memory_space);
139
140 let page_addr = (info.faulting_address & !(PAGE_SIZE as u64 - 1)) as usize;
141 let mut memory_space_mut = process.memory_space.lock();
142 memory_space_mut.map_page(page_addr, m.flags)?;
143
144 Ok(())
145 }
146 None => Err(KernelError::UnmappedMemory {
147 addr: info.faulting_address as usize,
148 }),
149 }
150 }
151
152 #[cfg(not(feature = "alloc"))]
153 {
154 let _ = vaddr;
155 Err(KernelError::NotImplemented {
156 feature: "demand paging (requires alloc)",
157 })
158 }
159}
160
161fn try_copy_on_write(info: &PageFaultInfo) -> Result<(), KernelError> {
166 let _ = info;
175 Err(KernelError::NotImplemented {
176 feature: "copy-on-write page handling",
177 })
178}
179
180fn try_stack_growth(info: &PageFaultInfo) -> Result<(), KernelError> {
186 if !info.was_user_mode {
188 return Err(KernelError::PermissionDenied {
189 operation: "kernel stack growth not supported",
190 });
191 }
192
193 let process = crate::process::current_process().ok_or(KernelError::NotInitialized {
194 subsystem: "process",
195 })?;
196
197 let memory_space = process.memory_space.lock();
198 let stack_top = memory_space.stack_top() as u64;
199 let stack_size = memory_space.user_stack_size() as u64;
200 let stack_bottom = stack_top - stack_size;
201
202 let fault = info.faulting_address;
204 if fault >= stack_bottom {
205 return Err(KernelError::InvalidAddress {
207 addr: fault as usize,
208 });
209 }
210
211 let max_total_stack = stack_size as usize + MAX_STACK_GROWTH;
213 let absolute_bottom = stack_top.saturating_sub(max_total_stack as u64);
214 if fault < absolute_bottom {
215 return Err(KernelError::InvalidAddress {
217 addr: fault as usize,
218 });
219 }
220
221 let fault_page = fault & !(PAGE_SIZE as u64 - 1);
223 let pages_needed = ((stack_bottom - fault_page) / PAGE_SIZE as u64) as usize;
224
225 if pages_needed == 0 {
226 return Err(KernelError::InvalidAddress {
227 addr: fault as usize,
228 });
229 }
230
231 drop(memory_space);
233
234 let flags = PageFlags::PRESENT | PageFlags::WRITABLE | PageFlags::USER | PageFlags::NO_EXECUTE;
235
236 let mut memory_space_mut = process.memory_space.lock();
237 for i in 0..pages_needed {
238 let page_addr = (stack_bottom - ((i + 1) as u64 * PAGE_SIZE as u64)) as usize;
239 memory_space_mut.map_page(page_addr, flags)?;
240 }
241
242 let new_size = stack_size as usize + (pages_needed * PAGE_SIZE);
244 memory_space_mut.set_stack_size(new_size);
245
246 Ok(())
247}
248
249fn signal_segv(info: &PageFaultInfo) -> Result<(), KernelError> {
252 if info.was_user_mode {
253 if let Some(process) = crate::process::current_process() {
255 use crate::process::exit::signals::SIGSEGV;
256 if let Err(_e) = crate::process::exit::kill_process(process.pid, SIGSEGV) {
257 kprintln!(
258 "[MM] Warning: Failed to deliver SIGSEGV to pid {}: {:?}",
259 process.pid,
260 _e
261 );
262 }
263 }
264 }
265
266 Err(KernelError::InvalidAddress {
267 addr: info.faulting_address as usize,
268 })
269}
270
271#[cfg(target_arch = "x86_64")]
283pub fn from_x86_64(error_code: u64, cr2: u64, rip: u64) -> PageFaultInfo {
284 let not_present = (error_code & 1) == 0;
285 let was_write = (error_code & 2) != 0;
286 let was_user = (error_code & 4) != 0;
287 let was_fetch = (error_code & 16) != 0;
288
289 let reason = if not_present {
290 PageFaultReason::NotPresent
291 } else if was_fetch {
292 PageFaultReason::ExecuteNoExecute
293 } else if was_write {
294 PageFaultReason::WriteToReadOnly
295 } else if was_user {
296 PageFaultReason::UserModeKernelAccess
297 } else {
298 PageFaultReason::ProtectionViolation
299 };
300
301 PageFaultInfo {
302 faulting_address: cr2,
303 reason,
304 was_write,
305 was_user_mode: was_user,
306 instruction_pointer: rip,
307 }
308}
309
310#[cfg(target_arch = "aarch64")]
317pub fn from_aarch64(esr_el1: u64, far_el1: u64, elr_el1: u64) -> PageFaultInfo {
318 let dfsc = (esr_el1 & 0x3F) as u8;
319 let was_write = (esr_el1 & (1 << 6)) != 0;
320 let ec = ((esr_el1 >> 26) & 0x3F) as u8;
322 let was_user = ec == 0b100100;
324
325 let reason = match dfsc & 0x0F {
326 0x04..=0x07 => PageFaultReason::NotPresent,
328 0x0C..=0x0F => {
330 if was_write {
331 PageFaultReason::WriteToReadOnly
332 } else {
333 PageFaultReason::ProtectionViolation
334 }
335 }
336 _ => PageFaultReason::ProtectionViolation,
337 };
338
339 PageFaultInfo {
340 faulting_address: far_el1,
341 reason,
342 was_write,
343 was_user_mode: was_user,
344 instruction_pointer: elr_el1,
345 }
346}
347
348#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
353pub fn from_riscv(cause: u64, stval: u64, sepc: u64) -> PageFaultInfo {
354 let was_write = cause == 15; let was_fetch = cause == 12; let reason = if was_fetch {
362 PageFaultReason::ExecuteNoExecute
363 } else {
364 PageFaultReason::NotPresent
365 };
366
367 PageFaultInfo {
371 faulting_address: stval,
372 reason,
373 was_write,
374 was_user_mode: true,
375 instruction_pointer: sepc,
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
384 fn test_page_fault_reason_equality() {
385 assert_eq!(PageFaultReason::NotPresent, PageFaultReason::NotPresent);
386 assert_ne!(
387 PageFaultReason::NotPresent,
388 PageFaultReason::WriteToReadOnly
389 );
390 }
391
392 #[test]
393 fn test_page_fault_info_construction() {
394 let info = PageFaultInfo {
395 faulting_address: 0xDEAD_BEEF,
396 reason: PageFaultReason::NotPresent,
397 was_write: false,
398 was_user_mode: true,
399 instruction_pointer: 0x4010_0000,
400 };
401 assert_eq!(info.faulting_address, 0xDEAD_BEEF);
402 assert!(!info.was_write);
403 assert!(info.was_user_mode);
404 }
405
406 #[test]
407 fn test_page_fault_info_write_fault() {
408 let info = PageFaultInfo {
409 faulting_address: 0x1000,
410 reason: PageFaultReason::WriteToReadOnly,
411 was_write: true,
412 was_user_mode: true,
413 instruction_pointer: 0x2000,
414 };
415 assert!(info.was_write);
416 assert_eq!(info.reason, PageFaultReason::WriteToReadOnly);
417 }
418
419 #[test]
420 fn test_page_fault_info_kernel_fault() {
421 let info = PageFaultInfo {
422 faulting_address: 0xFFFF_8000_0000_1000,
423 reason: PageFaultReason::ProtectionViolation,
424 was_write: false,
425 was_user_mode: false,
426 instruction_pointer: 0xFFFF_8000_0010_0000,
427 };
428 assert!(!info.was_user_mode);
429 }
430}