123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- use bindings::kernel::mem::paging::pfn_to_page;
- use bindings::{PA_A, PA_ANON, PA_COW, PA_MMAP, PA_P, PA_RW};
- use bitflags::bitflags;
- use crate::bindings::root::interrupt_stack;
- use crate::kernel::mem::paging::{Page, PageBuffer};
- use crate::kernel::mem::phys::{CachedPP, PhysPtr};
- 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 interrupt_stack,
- addr: VAddr,
- error: PageFaultError,
- ) -> Result<(), Signal> {
- let inner = self.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 = self
- .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(());
- }
- let mut pfn = pte.pfn();
- let mut attributes = pte.attributes();
- if attributes & PA_COW as usize != 0 {
- attributes &= !PA_COW as usize;
- if area.permission.write {
- attributes |= PA_RW as usize;
- } else {
- attributes &= !PA_RW as usize;
- }
- // TODO!!!: Change this.
- let page = unsafe { pfn_to_page(pfn).as_mut().unwrap() };
- if page.refcount == 1 {
- pte.set_attributes(attributes);
- return Ok(());
- }
- let new_page = Page::alloc_one();
- if attributes & PA_ANON as usize != 0 {
- new_page.zero();
- } else {
- new_page
- .as_cached()
- .as_mut_slice::<u8>(0x1000)
- .copy_from_slice(CachedPP::new(pfn).as_slice(0x1000));
- }
- attributes &= !(PA_A | PA_ANON) as usize;
- page.refcount -= 1;
- pfn = new_page.into_pfn();
- pte.set(pfn, attributes);
- }
- // TODO: shared mapping
- if attributes & PA_MMAP as usize != 0 {
- attributes |= PA_P as usize;
- if let Mapping::File(mapping) = &area.mapping {
- let load_offset = addr.floor() - area.range().start();
- if load_offset < mapping.length {
- // SAFETY: Since we are here, the `pfn` must refer to a valid buddy page.
- let page = unsafe { Page::from_pfn(pfn, 0) };
- let nread = mapping
- .file
- .read(
- &mut PageBuffer::new(page.clone()),
- mapping.offset + load_offset,
- )
- .map_err(|_| Signal::SIGBUS)?;
- if nread < page.len() {
- page.as_cached().as_mut_slice::<u8>(0x1000)[nread..].fill(0);
- }
- if mapping.length - load_offset < 0x1000 {
- let length_to_end = mapping.length - load_offset;
- page.as_cached().as_mut_slice::<u8>(0x1000)[length_to_end..].fill(0);
- }
- }
- // Otherwise, the page is kept zero emptied.
- attributes &= !PA_MMAP as usize;
- pte.set_attributes(attributes);
- } else {
- panic!("Anonymous mapping should not be PA_MMAP");
- }
- }
- Ok(())
- }
- }
- 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 interrupt_stack, addr: VAddr) {
- let ip = int_stack.v_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.v_rip = entry.jump_address as usize;
- 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 interrupt_stack) {
- let error = PageFaultError::from_bits_truncate(int_stack.error_code);
- let vaddr = VAddr(arch::x86_64::vm::get_cr2());
- 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.v_rip
- );
- ProcessList::kill_current(signal)
- }
- }
|