mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 13:26:48 +00:00
add page fault handler
This commit is contained in:
parent
429341caa6
commit
52531c6cb6
@ -20,8 +20,9 @@ pub fn handle_exception(context: &mut CpuContext) {
|
||||
|
||||
fn handle_page_fault(trap_info: &TrapInformation) {
|
||||
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 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)
|
||||
} else {
|
||||
// Otherwise, the page fault is caused by page protection error.
|
||||
|
@ -14,6 +14,7 @@
|
||||
//! In Jinux, VMARs and VMOs, as well as other capabilities, are implemented
|
||||
//! as zero-cost capabilities.
|
||||
|
||||
mod page_fault_handler;
|
||||
mod perms;
|
||||
mod vmar;
|
||||
mod vmo;
|
||||
|
12
src/services/libs/jinux-std/src/vm/page_fault_handler.rs
Normal file
12
src/services/libs/jinux-std/src/vm/page_fault_handler.rs
Normal 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<()>;
|
||||
}
|
@ -1,8 +1,14 @@
|
||||
use alloc::sync::Arc;
|
||||
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_};
|
||||
|
||||
@ -158,3 +164,14 @@ impl VmIo for Vmar<Rights> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ use spin::Mutex;
|
||||
|
||||
use self::vm_mapping::VmMapping;
|
||||
|
||||
use super::page_fault_handler::PageFaultHandler;
|
||||
|
||||
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
|
||||
/// user address spaces.
|
||||
///
|
||||
@ -51,6 +53,11 @@ use self::vm_mapping::VmMapping;
|
||||
pub struct Vmar<R = Rights>(Arc<Vmar_>, R);
|
||||
|
||||
// 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_ {
|
||||
/// vmar inner
|
||||
@ -77,12 +84,16 @@ struct VmarInner {
|
||||
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;
|
||||
|
||||
impl Vmar_ {
|
||||
pub fn new_root() -> Result<Self> {
|
||||
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);
|
||||
let vmar_inner = VmarInner {
|
||||
is_destroyed: false,
|
||||
@ -156,6 +167,34 @@ impl Vmar_ {
|
||||
|
||||
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<()> {
|
||||
todo!()
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
use core::ops::Range;
|
||||
|
||||
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 crate::{
|
||||
rights::{Dup, Rights, TRights},
|
||||
vm::vmo::Vmo,
|
||||
vm::{page_fault_handler::PageFaultHandler, vmo::Vmo},
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,18 @@ use alloc::{
|
||||
};
|
||||
use jinux_frame::{
|
||||
config::PAGE_SIZE,
|
||||
vm::{Vaddr, VmFrameVec, VmPerm},
|
||||
vm::{Vaddr, VmFrameVec, VmIo, VmPerm},
|
||||
Error,
|
||||
};
|
||||
use jinux_frame::{vm::VmMapOptions, Result};
|
||||
use spin::Mutex;
|
||||
|
||||
use crate::vm::vmo::{Pager, Vmo, Vmo_};
|
||||
use crate::vm::{page_fault_handler::PageFaultHandler, vmo::Vmo};
|
||||
|
||||
use super::{Vmar, Vmar_};
|
||||
use crate::vm::perms::VmPerms;
|
||||
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 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 {
|
||||
/// The parent vmar. The parent should always point to a valid vmar.
|
||||
parent: Weak<Vmar_>,
|
||||
/// The mapped vmo.
|
||||
vmo: Arc<Vmo_>,
|
||||
/// The mapped vmo. The mapped vmo is with dynamic capability.
|
||||
vmo: Vmo<Rights>,
|
||||
/// The mao offset of the vmo, in bytes.
|
||||
vmo_offset: usize,
|
||||
/// 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,
|
||||
} = option;
|
||||
let Vmar(parent_vmar, _) = parent;
|
||||
let vmo_ = vmo.0;
|
||||
let vmo_size = vmo_.size();
|
||||
let vmo = vmo.to_dyn();
|
||||
let vmo_size = vmo.size();
|
||||
let map_to_addr = parent_vmar.allocate_free_region_for_vmo(
|
||||
vmo_size,
|
||||
size,
|
||||
@ -79,8 +79,8 @@ impl VmMapping {
|
||||
vm_map_options.perm(perm);
|
||||
vm_map_options.can_overwrite(can_overwrite);
|
||||
vm_map_options.align(align);
|
||||
if vmo_.page_commited(page_idx) {
|
||||
vmo_.map_page(page_idx, &vm_space, vm_map_options)?;
|
||||
if vmo.page_commited(page_idx) {
|
||||
vmo.map_page(page_idx, &vm_space, vm_map_options)?;
|
||||
mapped_pages.insert(page_idx);
|
||||
} else {
|
||||
// The page is not committed. We simple record the map options for further mapping.
|
||||
@ -89,7 +89,7 @@ impl VmMapping {
|
||||
}
|
||||
Ok(Self {
|
||||
parent: Arc::downgrade(&parent_vmar),
|
||||
vmo: vmo_,
|
||||
vmo,
|
||||
vmo_offset,
|
||||
map_size: size,
|
||||
map_to_addr,
|
||||
@ -137,6 +137,11 @@ impl VmMapping {
|
||||
let vmo_write_offset = self.vmo_offset + offset;
|
||||
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
|
||||
|
@ -5,7 +5,7 @@ use jinux_frame::{vm::VmIo, Error};
|
||||
|
||||
use crate::rights::{Rights, TRights};
|
||||
|
||||
use super::VmoRights;
|
||||
use super::VmoRightsOp;
|
||||
use super::{
|
||||
options::{VmoCowChild, VmoSliceChild},
|
||||
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> {
|
||||
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||
self.check_rights(Rights::READ)?;
|
||||
@ -159,3 +153,15 @@ impl VmIo for Vmo<Rights> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,8 @@ pub use options::{VmoChildOptions, VmoOptions};
|
||||
pub use pager::Pager;
|
||||
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
|
||||
/// 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);
|
||||
|
||||
/// Functions exist both for static capbility and dynamic capibility
|
||||
pub trait VmoRights {
|
||||
pub trait VmoRightsOp {
|
||||
/// Returns the access rights.
|
||||
fn rights(&self) -> Rights;
|
||||
|
||||
@ -94,14 +95,37 @@ pub trait VmoRights {
|
||||
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.
|
||||
// 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 {
|
||||
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! {
|
||||
@ -155,10 +179,6 @@ struct VmoInner {
|
||||
inherited_pages: InheritedPages,
|
||||
/// The current mapping on this vmo. The vmo can be mapped to multiple vmars.
|
||||
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
|
||||
@ -273,6 +293,16 @@ impl Vmo_ {
|
||||
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
|
||||
pub fn page_commited(&self, page_idx: usize) -> bool {
|
||||
self.inner.lock().committed_pages.contains_key(&page_idx)
|
||||
@ -283,7 +313,7 @@ impl Vmo_ {
|
||||
&self,
|
||||
page_idx: usize,
|
||||
vm_space: &VmSpace,
|
||||
options: VmMapOptions,
|
||||
map_options: VmMapOptions,
|
||||
) -> Result<Vaddr> {
|
||||
debug_assert!(self.page_commited(page_idx));
|
||||
if !self.page_commited(page_idx) {
|
||||
@ -296,7 +326,7 @@ impl Vmo_ {
|
||||
.get(&page_idx)
|
||||
.unwrap()
|
||||
.clone();
|
||||
vm_space.map(frames, &options)
|
||||
vm_space.map(frames, &map_options)
|
||||
}
|
||||
|
||||
pub fn add_mapping(&self, mapping: Weak<VmMapping>) {
|
||||
@ -440,4 +470,19 @@ impl<R> Vmo<R> {
|
||||
pub fn flags(&self) -> VmoFlags {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use crate::vm::vmo::InheritedPages;
|
||||
use crate::vm::vmo::VmoType;
|
||||
use crate::vm::vmo::{VmoInner, Vmo_};
|
||||
|
||||
use super::VmoRights;
|
||||
use super::VmoRightsOp;
|
||||
use super::{Pager, Vmo, VmoFlags};
|
||||
|
||||
/// Options for allocating a root VMO.
|
||||
|
@ -6,7 +6,7 @@ use jinux_rights_proc::require;
|
||||
|
||||
use crate::rights::*;
|
||||
|
||||
use super::VmoRights;
|
||||
use super::VmoRightsOp;
|
||||
use super::{
|
||||
options::{VmoCowChild, VmoSliceChild},
|
||||
Vmo, VmoChildOptions,
|
||||
@ -134,12 +134,6 @@ impl<R: TRights> Vmo<R> {
|
||||
pub fn restrict<R1: TRights>(self) -> Vmo<R1> {
|
||||
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> {
|
||||
@ -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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user