123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- use arch::InterruptContext;
- use bindings::{PA_A, PA_ANON, PA_COW, PA_MMAP, PA_P, PA_RW};
- use bitflags::bitflags;
- use crate::kernel::mem::paging::{Page, PageBuffer};
- use crate::kernel::mem::{Mapping, VRange};
- use crate::kernel::task::{ProcessList, Signal, Thread};
- use crate::prelude::*;
- use super::{MMList, VAddr};
- bitflags! {
- pub struct PageFaultError: u64 {
- const Present = 0x0001;
- const Write = 0x0002;
- const User = 0x0004;
- const ReservedSet = 0x0008;
- const InstructionFetch = 0x0010;
- const ProtectionKey = 0x0020;
- const SGX = 0x8000;
- }
- }
- #[repr(C)]
- struct FixEntry {
- start: u64,
- length: u64,
- jump_address: u64,
- op_type: u64,
- }
- impl MMList {
- fn handle_page_fault(
- &self,
- int_stack: &mut InterruptContext,
- addr: VAddr,
- error: PageFaultError,
- ) -> Result<(), Signal> {
- let inner = self.inner.borrow();
- let inner = inner.lock();
- let area = match inner.areas.get(&VRange::from(addr)) {
- Some(area) => area,
- None => {
- if error.contains(PageFaultError::User) {
- return Err(Signal::SIGBUS);
- } else {
- try_page_fault_fix(int_stack, addr);
- return Ok(());
- }
- }
- };
- // User access permission violation, check user access permission.
- if error.contains(PageFaultError::User | PageFaultError::Present) {
- if error.contains(PageFaultError::Write) && !area.permission.write {
- ProcessList::kill_current(Signal::SIGSEGV)
- }
- if error.contains(PageFaultError::InstructionFetch) && !area.permission.execute {
- ProcessList::kill_current(Signal::SIGSEGV)
- }
- }
- let pte = inner
- .page_table
- .iter_user(VRange::new(addr.floor(), addr.floor() + 0x1000))
- .unwrap()
- .next()
- .expect("If we can find the mapped area, we should be able to find the PTE");
- let is_mapped = matches!(&area.mapping, Mapping::File(_));
- if !is_mapped && !error.contains(PageFaultError::Present) {
- try_page_fault_fix(int_stack, addr);
- return Ok(());
- }
- area.handle(pte, addr.floor() - area.range().start())
- .map_err(|_| Signal::SIGBUS)
- }
- }
- extern "C" {
- static FIX_START: *const FixEntry;
- static FIX_END: *const FixEntry;
- }
- /// Try to fix the page fault by jumping to the `error` address.
- ///
- /// Panic if we can't find the `ip` in the fix list.
- fn try_page_fault_fix(int_stack: &mut InterruptContext, addr: VAddr) {
- let ip = int_stack.rip as u64;
- // TODO: Use `op_type` to fix.
- // SAFETY: `FIX_START` and `FIX_END` are defined in the linker script in `.rodata` section.
- let entries = unsafe {
- core::slice::from_raw_parts(
- FIX_START,
- (FIX_END as usize - FIX_START as usize) / size_of::<FixEntry>(),
- )
- };
- for entry in entries.iter() {
- if ip >= entry.start && ip < entry.start + entry.length {
- int_stack.rip = entry.jump_address as u64;
- return;
- }
- }
- kernel_page_fault_die(addr, ip as usize)
- }
- fn kernel_page_fault_die(vaddr: VAddr, ip: usize) -> ! {
- panic!(
- "Invalid kernel mode memory access to {:#8x} while executing the instruction at {:#8x}",
- vaddr.0, ip
- )
- }
- pub fn handle_page_fault(int_stack: &mut InterruptContext) {
- let error = PageFaultError::from_bits_truncate(int_stack.error_code);
- let vaddr = VAddr(arch::get_page_fault_address());
- let result = Thread::current()
- .process
- .mm_list
- .handle_page_fault(int_stack, vaddr, error);
- if let Err(signal) = result {
- println_debug!(
- "Page fault on {:#x} in user space at {:#x}",
- vaddr.0,
- int_stack.rip
- );
- ProcessList::kill_current(signal)
- }
- }
|