add page fault handler

This commit is contained in:
Jianfeng Jiang 2022-12-07 14:08:01 +08:00
parent 429341caa6
commit 52531c6cb6
11 changed files with 181 additions and 41 deletions

View File

@ -20,8 +20,9 @@ pub fn handle_exception(context: &mut CpuContext) {
fn handle_page_fault(trap_info: &TrapInformation) { fn handle_page_fault(trap_info: &TrapInformation) {
const PAGE_NOT_PRESENT_ERROR_MASK: u64 = 0x1 << 0; const PAGE_NOT_PRESENT_ERROR_MASK: u64 = 0x1 << 0;
const WRITE_ACCESS_MASK: u64 = 0x1 << 1;
if trap_info.err & PAGE_NOT_PRESENT_ERROR_MASK == 0 { if trap_info.err & PAGE_NOT_PRESENT_ERROR_MASK == 0 {
// If page is not present, we should ask the vmar try to commit this page // TODO: If page is not present, we should ask the vmar try to commit this page
generate_fault_signal(trap_info) generate_fault_signal(trap_info)
} else { } else {
// Otherwise, the page fault is caused by page protection error. // Otherwise, the page fault is caused by page protection error.

View File

@ -14,6 +14,7 @@
//! In Jinux, VMARs and VMOs, as well as other capabilities, are implemented //! In Jinux, VMARs and VMOs, as well as other capabilities, are implemented
//! as zero-cost capabilities. //! as zero-cost capabilities.
mod page_fault_handler;
mod perms; mod perms;
mod vmar; mod vmar;
mod vmo; mod vmo;

View File

@ -0,0 +1,12 @@
use jinux_frame::vm::Vaddr;
use jinux_frame::Result;
/// This trait is implemented by structs which can handle a user space page fault.
/// In current implementation, they are vmars and vmos.
pub trait PageFaultHandler {
/// Handle a page fault at a specific addr. if write is true, means the page fault is caused by a write access,
/// otherwise, the page fault is caused by a read access.
/// If the page fault can be handled successfully, this function will return Ok(()).
/// Otherwise, this function will return Err.
fn handle_page_fault(&self, page_fault_addr: Vaddr, write: bool) -> Result<()>;
}

View File

@ -1,8 +1,14 @@
use alloc::sync::Arc; use alloc::sync::Arc;
use core::ops::Range; use core::ops::Range;
use jinux_frame::{vm::VmIo, Error, Result}; use jinux_frame::{
vm::{Vaddr, VmIo},
Error, Result,
};
use crate::{rights::Rights, vm::vmo::Vmo}; use crate::{
rights::Rights,
vm::{page_fault_handler::PageFaultHandler, vmo::Vmo},
};
use super::{options::VmarChildOptions, vm_mapping::VmarMapOptions, VmPerms, Vmar, Vmar_}; use super::{options::VmarChildOptions, vm_mapping::VmarMapOptions, VmPerms, Vmar, Vmar_};
@ -158,3 +164,14 @@ impl VmIo for Vmar<Rights> {
self.0.write(offset, buf) self.0.write(offset, buf)
} }
} }
impl PageFaultHandler for Vmar<Rights> {
fn handle_page_fault(&self, page_fault_addr: Vaddr, write: bool) -> Result<()> {
if write {
self.check_rights(Rights::WRITE)?;
} else {
self.check_rights(Rights::READ)?;
}
self.0.handle_page_fault(page_fault_addr, write)
}
}

View File

@ -21,6 +21,8 @@ use spin::Mutex;
use self::vm_mapping::VmMapping; use self::vm_mapping::VmMapping;
use super::page_fault_handler::PageFaultHandler;
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages /// Virtual Memory Address Regions (VMARs) are a type of capability that manages
/// user address spaces. /// user address spaces.
/// ///
@ -51,6 +53,11 @@ use self::vm_mapping::VmMapping;
pub struct Vmar<R = Rights>(Arc<Vmar_>, R); pub struct Vmar<R = Rights>(Arc<Vmar_>, R);
// TODO: how page faults can be delivered to and handled by the current VMAR. // TODO: how page faults can be delivered to and handled by the current VMAR.
impl<R> PageFaultHandler for Vmar<R> {
default fn handle_page_fault(&self, page_fault_addr: Vaddr, write: bool) -> Result<()> {
unimplemented!()
}
}
pub(super) struct Vmar_ { pub(super) struct Vmar_ {
/// vmar inner /// vmar inner
@ -77,12 +84,16 @@ struct VmarInner {
free_regions: BTreeMap<Vaddr, FreeRegion>, free_regions: BTreeMap<Vaddr, FreeRegion>,
} }
// FIXME: How to set the correct root vmar range?
// We should not include addr 0 here(is this right?), since the 0 addr means the null pointer.
// We should include addr 0x0040_0000, since non-pie executables typically are put on 0x0040_0000.
pub const ROOT_VMAR_LOWEST_ADDR: Vaddr = 0x0010_0000;
pub const ROOT_VMAR_HIGHEST_ADDR: Vaddr = 0x1000_0000_0000; pub const ROOT_VMAR_HIGHEST_ADDR: Vaddr = 0x1000_0000_0000;
impl Vmar_ { impl Vmar_ {
pub fn new_root() -> Result<Self> { pub fn new_root() -> Result<Self> {
let mut free_regions = BTreeMap::new(); let mut free_regions = BTreeMap::new();
let root_region = FreeRegion::new(0..ROOT_VMAR_HIGHEST_ADDR); let root_region = FreeRegion::new(ROOT_VMAR_LOWEST_ADDR..ROOT_VMAR_HIGHEST_ADDR);
free_regions.insert(root_region.start(), root_region); free_regions.insert(root_region.start(), root_region);
let vmar_inner = VmarInner { let vmar_inner = VmarInner {
is_destroyed: false, is_destroyed: false,
@ -156,6 +167,34 @@ impl Vmar_ {
Ok(()) Ok(())
} }
/// Handle user space page fault, if the page fault is successfully handled ,return Ok(()).
pub fn handle_page_fault(&self, page_fault_addr: Vaddr, write: bool) -> Result<()> {
if page_fault_addr < self.base || page_fault_addr >= self.base + self.size {
return Err(Error::AccessDenied);
}
let inner = self.inner.lock();
for (child_vmar_base, child_vmar) in &inner.child_vmar_s {
if *child_vmar_base <= page_fault_addr
&& page_fault_addr < *child_vmar_base + child_vmar.size
{
return child_vmar.handle_page_fault(page_fault_addr, write);
}
}
// FIXME: If multiple vmos are mapped to the addr, should we allow all vmos to handle page fault?
for (vm_mapping_base, vm_mapping) in &inner.mapped_vmos {
if *vm_mapping_base <= page_fault_addr
&& page_fault_addr <= *vm_mapping_base + vm_mapping.size()
{
return vm_mapping.handle_page_fault(page_fault_addr, write);
}
}
return Err(Error::AccessDenied);
}
pub fn destroy_all(&self) -> Result<()> { pub fn destroy_all(&self) -> Result<()> {
todo!() todo!()
} }

View File

@ -1,12 +1,15 @@
use core::ops::Range; use core::ops::Range;
use alloc::sync::Arc; use alloc::sync::Arc;
use jinux_frame::{vm::VmIo, Error, Result}; use jinux_frame::{
vm::{Vaddr, VmIo},
Error, Result,
};
use jinux_rights_proc::require; use jinux_rights_proc::require;
use crate::{ use crate::{
rights::{Dup, Rights, TRights}, rights::{Dup, Rights, TRights},
vm::vmo::Vmo, vm::{page_fault_handler::PageFaultHandler, vmo::Vmo},
}; };
use super::{options::VmarChildOptions, vm_mapping::VmarMapOptions, VmPerms, Vmar, Vmar_}; use super::{options::VmarChildOptions, vm_mapping::VmarMapOptions, VmPerms, Vmar, Vmar_};
@ -171,3 +174,14 @@ impl<R: TRights> VmIo for Vmar<R> {
self.0.write(offset, buf) self.0.write(offset, buf)
} }
} }
impl<R: TRights> PageFaultHandler for Vmar<R> {
fn handle_page_fault(&self, page_fault_addr: Vaddr, write: bool) -> Result<()> {
if write {
self.check_rights(Rights::WRITE)?;
} else {
self.check_rights(Rights::READ)?;
}
self.0.handle_page_fault(page_fault_addr, write)
}
}

View File

@ -4,18 +4,18 @@ use alloc::{
}; };
use jinux_frame::{ use jinux_frame::{
config::PAGE_SIZE, config::PAGE_SIZE,
vm::{Vaddr, VmFrameVec, VmPerm}, vm::{Vaddr, VmFrameVec, VmIo, VmPerm},
Error, Error,
}; };
use jinux_frame::{vm::VmMapOptions, Result}; use jinux_frame::{vm::VmMapOptions, Result};
use spin::Mutex; use spin::Mutex;
use crate::vm::vmo::{Pager, Vmo, Vmo_}; use crate::vm::{page_fault_handler::PageFaultHandler, vmo::Vmo};
use super::{Vmar, Vmar_}; use super::{Vmar, Vmar_};
use crate::vm::perms::VmPerms; use crate::vm::perms::VmPerms;
use crate::vm::vmar::Rights; use crate::vm::vmar::Rights;
use crate::vm::vmo::VmoRights; use crate::vm::vmo::VmoRightsOp;
/// A VmMapping represents mapping a vmo into a vmar. /// A VmMapping represents mapping a vmo into a vmar.
/// A vmar can has multiple VmMappings, which means multiple vmos are mapped to a vmar. /// A vmar can has multiple VmMappings, which means multiple vmos are mapped to a vmar.
@ -24,8 +24,8 @@ use crate::vm::vmo::VmoRights;
pub struct VmMapping { pub struct VmMapping {
/// The parent vmar. The parent should always point to a valid vmar. /// The parent vmar. The parent should always point to a valid vmar.
parent: Weak<Vmar_>, parent: Weak<Vmar_>,
/// The mapped vmo. /// The mapped vmo. The mapped vmo is with dynamic capability.
vmo: Arc<Vmo_>, vmo: Vmo<Rights>,
/// The mao offset of the vmo, in bytes. /// The mao offset of the vmo, in bytes.
vmo_offset: usize, vmo_offset: usize,
/// The size of mapping, in bytes. The map size can even be larger than the size of backup vmo. /// The size of mapping, in bytes. The map size can even be larger than the size of backup vmo.
@ -54,8 +54,8 @@ impl VmMapping {
can_overwrite, can_overwrite,
} = option; } = option;
let Vmar(parent_vmar, _) = parent; let Vmar(parent_vmar, _) = parent;
let vmo_ = vmo.0; let vmo = vmo.to_dyn();
let vmo_size = vmo_.size(); let vmo_size = vmo.size();
let map_to_addr = parent_vmar.allocate_free_region_for_vmo( let map_to_addr = parent_vmar.allocate_free_region_for_vmo(
vmo_size, vmo_size,
size, size,
@ -79,8 +79,8 @@ impl VmMapping {
vm_map_options.perm(perm); vm_map_options.perm(perm);
vm_map_options.can_overwrite(can_overwrite); vm_map_options.can_overwrite(can_overwrite);
vm_map_options.align(align); vm_map_options.align(align);
if vmo_.page_commited(page_idx) { if vmo.page_commited(page_idx) {
vmo_.map_page(page_idx, &vm_space, vm_map_options)?; vmo.map_page(page_idx, &vm_space, vm_map_options)?;
mapped_pages.insert(page_idx); mapped_pages.insert(page_idx);
} else { } else {
// The page is not committed. We simple record the map options for further mapping. // The page is not committed. We simple record the map options for further mapping.
@ -89,7 +89,7 @@ impl VmMapping {
} }
Ok(Self { Ok(Self {
parent: Arc::downgrade(&parent_vmar), parent: Arc::downgrade(&parent_vmar),
vmo: vmo_, vmo,
vmo_offset, vmo_offset,
map_size: size, map_size: size,
map_to_addr, map_to_addr,
@ -137,6 +137,11 @@ impl VmMapping {
let vmo_write_offset = self.vmo_offset + offset; let vmo_write_offset = self.vmo_offset + offset;
self.vmo.write_bytes(vmo_write_offset, buf) self.vmo.write_bytes(vmo_write_offset, buf)
} }
pub fn handle_page_fault(&self, page_fault_addr: Vaddr, write: bool) -> Result<()> {
let vmo_offset = self.vmo_offset + page_fault_addr - self.map_to_addr;
self.vmo.handle_page_fault(vmo_offset, write)
}
} }
/// Options for creating a new mapping. The mapping is not allowed to overlap /// Options for creating a new mapping. The mapping is not allowed to overlap

View File

@ -5,7 +5,7 @@ use jinux_frame::{vm::VmIo, Error};
use crate::rights::{Rights, TRights}; use crate::rights::{Rights, TRights};
use super::VmoRights; use super::VmoRightsOp;
use super::{ use super::{
options::{VmoCowChild, VmoSliceChild}, options::{VmoCowChild, VmoSliceChild},
Vmo, VmoChildOptions, Vmo, VmoChildOptions,
@ -142,12 +142,6 @@ impl Vmo<Rights> {
} }
} }
impl VmoRights for Vmo<Rights> {
fn rights(&self) -> Rights {
self.1
}
}
impl VmIo for Vmo<Rights> { impl VmIo for Vmo<Rights> {
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> { fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
self.check_rights(Rights::READ)?; self.check_rights(Rights::READ)?;
@ -159,3 +153,15 @@ impl VmIo for Vmo<Rights> {
self.0.write_bytes(offset, buf) self.0.write_bytes(offset, buf)
} }
} }
impl VmoRightsOp for Vmo<Rights> {
fn rights(&self) -> Rights {
self.1
}
/// Converts to a dynamic capability.
fn to_dyn(self) -> Vmo<Rights> {
let rights = self.rights();
Vmo(self.0, rights)
}
}

View File

@ -22,7 +22,8 @@ pub use options::{VmoChildOptions, VmoOptions};
pub use pager::Pager; pub use pager::Pager;
use spin::Mutex; use spin::Mutex;
use super::vmar::vm_mapping::{self, VmMapping}; use super::page_fault_handler::PageFaultHandler;
use super::vmar::vm_mapping::VmMapping;
/// Virtual Memory Objects (VMOs) are a type of capability that represents a /// Virtual Memory Objects (VMOs) are a type of capability that represents a
/// range of memory pages. /// range of memory pages.
@ -82,7 +83,7 @@ use super::vmar::vm_mapping::{self, VmMapping};
pub struct Vmo<R = Rights>(pub(super) Arc<Vmo_>, R); pub struct Vmo<R = Rights>(pub(super) Arc<Vmo_>, R);
/// Functions exist both for static capbility and dynamic capibility /// Functions exist both for static capbility and dynamic capibility
pub trait VmoRights { pub trait VmoRightsOp {
/// Returns the access rights. /// Returns the access rights.
fn rights(&self) -> Rights; fn rights(&self) -> Rights;
@ -94,14 +95,37 @@ pub trait VmoRights {
Err(Error::AccessDenied) Err(Error::AccessDenied)
} }
} }
/// Converts to a dynamic capability.
fn to_dyn(self) -> Vmo<Rights>
where
Self: Sized;
} }
// We implement this trait for Vmo, so we can use functions on type like Vmo<R> without trait bounds. // We implement this trait for Vmo, so we can use functions on type like Vmo<R> without trait bounds.
// FIXME: This requires the imcomplete feature specialization, which should be fixed further. // FIXME: This requires the imcomplete feature specialization, which should be fixed further.
impl<R> VmoRights for Vmo<R> { impl<R> VmoRightsOp for Vmo<R> {
default fn rights(&self) -> Rights { default fn rights(&self) -> Rights {
unimplemented!() unimplemented!()
} }
default fn to_dyn(self) -> Vmo<Rights>
where
Self: Sized,
{
unimplemented!()
}
}
impl<R> PageFaultHandler for Vmo<R> {
default fn handle_page_fault(&self, page_fault_addr: Vaddr, write: bool) -> Result<()> {
if write {
self.check_rights(Rights::WRITE)?;
} else {
self.check_rights(Rights::READ)?;
}
self.0.handle_page_fault(page_fault_addr, write)
}
} }
bitflags! { bitflags! {
@ -155,10 +179,6 @@ struct VmoInner {
inherited_pages: InheritedPages, inherited_pages: InheritedPages,
/// The current mapping on this vmo. The vmo can be mapped to multiple vmars. /// The current mapping on this vmo. The vmo can be mapped to multiple vmars.
mappings: Vec<Weak<VmMapping>>, mappings: Vec<Weak<VmMapping>>,
// Pages should be filled with zeros when committed. When create COW child, the pages exceed the range of parent vmo
// should be in this set. According to the on demand requirement, when read or write these pages for the first time,
// we should commit these pages and zeroed these pages.
// pages_should_fill_zeros: BTreeSet<usize>,
} }
/// Pages inherited from parent /// Pages inherited from parent
@ -273,6 +293,16 @@ impl Vmo_ {
Ok(()) Ok(())
} }
/// Handle page fault.
pub fn handle_page_fault(&self, offset: usize, write: bool) -> Result<()> {
if offset >= self.size() {
return Err(Error::AccessDenied);
}
let page_idx = offset / PAGE_SIZE;
self.ensure_page_exists(page_idx, write)?;
Ok(())
}
/// determine whether a page is commited /// determine whether a page is commited
pub fn page_commited(&self, page_idx: usize) -> bool { pub fn page_commited(&self, page_idx: usize) -> bool {
self.inner.lock().committed_pages.contains_key(&page_idx) self.inner.lock().committed_pages.contains_key(&page_idx)
@ -283,7 +313,7 @@ impl Vmo_ {
&self, &self,
page_idx: usize, page_idx: usize,
vm_space: &VmSpace, vm_space: &VmSpace,
options: VmMapOptions, map_options: VmMapOptions,
) -> Result<Vaddr> { ) -> Result<Vaddr> {
debug_assert!(self.page_commited(page_idx)); debug_assert!(self.page_commited(page_idx));
if !self.page_commited(page_idx) { if !self.page_commited(page_idx) {
@ -296,7 +326,7 @@ impl Vmo_ {
.get(&page_idx) .get(&page_idx)
.unwrap() .unwrap()
.clone(); .clone();
vm_space.map(frames, &options) vm_space.map(frames, &map_options)
} }
pub fn add_mapping(&self, mapping: Weak<VmMapping>) { pub fn add_mapping(&self, mapping: Weak<VmMapping>) {
@ -440,4 +470,19 @@ impl<R> Vmo<R> {
pub fn flags(&self) -> VmoFlags { pub fn flags(&self) -> VmoFlags {
self.0.flags() self.0.flags()
} }
/// return whether a page is already committed
pub fn page_commited(&self, page_idx: usize) -> bool {
self.0.page_commited(page_idx)
}
/// map a committed page, returns the map address
pub fn map_page(
&self,
page_idx: usize,
vm_space: &VmSpace,
map_options: VmMapOptions,
) -> Result<Vaddr> {
self.0.map_page(page_idx, vm_space, map_options)
}
} }

View File

@ -19,7 +19,7 @@ use crate::vm::vmo::InheritedPages;
use crate::vm::vmo::VmoType; use crate::vm::vmo::VmoType;
use crate::vm::vmo::{VmoInner, Vmo_}; use crate::vm::vmo::{VmoInner, Vmo_};
use super::VmoRights; use super::VmoRightsOp;
use super::{Pager, Vmo, VmoFlags}; use super::{Pager, Vmo, VmoFlags};
/// Options for allocating a root VMO. /// Options for allocating a root VMO.

View File

@ -6,7 +6,7 @@ use jinux_rights_proc::require;
use crate::rights::*; use crate::rights::*;
use super::VmoRights; use super::VmoRightsOp;
use super::{ use super::{
options::{VmoCowChild, VmoSliceChild}, options::{VmoCowChild, VmoSliceChild},
Vmo, VmoChildOptions, Vmo, VmoChildOptions,
@ -134,12 +134,6 @@ impl<R: TRights> Vmo<R> {
pub fn restrict<R1: TRights>(self) -> Vmo<R1> { pub fn restrict<R1: TRights>(self) -> Vmo<R1> {
Vmo(self.0, R1::new()) Vmo(self.0, R1::new())
} }
/// Converts to a dynamic capability.
pub fn to_dyn(self) -> Vmo<Rights> {
let rights = self.rights();
Vmo(self.0, rights)
}
} }
impl<R: TRights> VmIo for Vmo<R> { impl<R: TRights> VmIo for Vmo<R> {
@ -154,8 +148,14 @@ impl<R: TRights> VmIo for Vmo<R> {
} }
} }
impl<R: TRights> VmoRights for Vmo<R> { impl<R: TRights> VmoRightsOp for Vmo<R> {
fn rights(&self) -> Rights { fn rights(&self) -> Rights {
Rights::from_bits(R::BITS).unwrap() Rights::from_bits(R::BITS).unwrap()
} }
/// Converts to a dynamic capability.
fn to_dyn(self) -> Vmo<Rights> {
let rights = self.rights();
Vmo(self.0, rights)
}
} }