|
@@ -1,18 +1,23 @@
|
|
|
use super::mm_list::EMPTY_PAGE;
|
|
use super::mm_list::EMPTY_PAGE;
|
|
|
use super::paging::AllocZeroed as _;
|
|
use super::paging::AllocZeroed as _;
|
|
|
use super::{AsMemoryBlock, Mapping, Page, Permission};
|
|
use super::{AsMemoryBlock, Mapping, Page, Permission};
|
|
|
-use crate::io::ByteBuffer;
|
|
|
|
|
|
|
+use crate::kernel::constants::EINVAL;
|
|
|
|
|
+use crate::kernel::mem::page_cache::PageCacheRawPage;
|
|
|
use crate::KResult;
|
|
use crate::KResult;
|
|
|
-use core::{borrow::Borrow, cell::UnsafeCell, cmp::Ordering};
|
|
|
|
|
|
|
+use core::sync::atomic;
|
|
|
|
|
+use core::{borrow::Borrow, cell::UnsafeCell, cmp};
|
|
|
|
|
+use eonix_hal::traits::fault::PageFaultErrorCode;
|
|
|
use eonix_mm::address::{AddrOps as _, VAddr, VRange};
|
|
use eonix_mm::address::{AddrOps as _, VAddr, VRange};
|
|
|
use eonix_mm::page_table::{PageAttribute, RawAttribute, PTE};
|
|
use eonix_mm::page_table::{PageAttribute, RawAttribute, PTE};
|
|
|
-use eonix_mm::paging::PFN;
|
|
|
|
|
|
|
+use eonix_mm::paging::{PAGE_SIZE, PFN};
|
|
|
|
|
+use eonix_runtime::task::Task;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
#[derive(Debug)]
|
|
|
pub struct MMArea {
|
|
pub struct MMArea {
|
|
|
range: UnsafeCell<VRange>,
|
|
range: UnsafeCell<VRange>,
|
|
|
pub(super) mapping: Mapping,
|
|
pub(super) mapping: Mapping,
|
|
|
pub(super) permission: Permission,
|
|
pub(super) permission: Permission,
|
|
|
|
|
+ pub is_shared: bool,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl Clone for MMArea {
|
|
impl Clone for MMArea {
|
|
@@ -21,16 +26,18 @@ impl Clone for MMArea {
|
|
|
range: UnsafeCell::new(self.range()),
|
|
range: UnsafeCell::new(self.range()),
|
|
|
mapping: self.mapping.clone(),
|
|
mapping: self.mapping.clone(),
|
|
|
permission: self.permission,
|
|
permission: self.permission,
|
|
|
|
|
+ is_shared: self.is_shared,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl MMArea {
|
|
impl MMArea {
|
|
|
- pub fn new(range: VRange, mapping: Mapping, permission: Permission) -> Self {
|
|
|
|
|
|
|
+ pub fn new(range: VRange, mapping: Mapping, permission: Permission, is_shared: bool) -> Self {
|
|
|
Self {
|
|
Self {
|
|
|
range: range.into(),
|
|
range: range.into(),
|
|
|
mapping,
|
|
mapping,
|
|
|
permission,
|
|
permission,
|
|
|
|
|
+ is_shared,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -56,9 +63,9 @@ impl MMArea {
|
|
|
assert!(at.is_page_aligned());
|
|
assert!(at.is_page_aligned());
|
|
|
|
|
|
|
|
match self.range_borrow().cmp(&VRange::from(at)) {
|
|
match self.range_borrow().cmp(&VRange::from(at)) {
|
|
|
- Ordering::Less => (Some(self), None),
|
|
|
|
|
- Ordering::Greater => (None, Some(self)),
|
|
|
|
|
- Ordering::Equal => {
|
|
|
|
|
|
|
+ cmp::Ordering::Less => (Some(self), None),
|
|
|
|
|
+ cmp::Ordering::Greater => (None, Some(self)),
|
|
|
|
|
+ cmp::Ordering::Equal => {
|
|
|
let diff = at - self.range_borrow().start();
|
|
let diff = at - self.range_borrow().start();
|
|
|
if diff == 0 {
|
|
if diff == 0 {
|
|
|
return (None, Some(self));
|
|
return (None, Some(self));
|
|
@@ -71,6 +78,7 @@ impl MMArea {
|
|
|
Mapping::Anonymous => Mapping::Anonymous,
|
|
Mapping::Anonymous => Mapping::Anonymous,
|
|
|
Mapping::File(mapping) => Mapping::File(mapping.offset(diff)),
|
|
Mapping::File(mapping) => Mapping::File(mapping.offset(diff)),
|
|
|
},
|
|
},
|
|
|
|
|
+ is_shared: self.is_shared,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let new_range = self.range_borrow().shrink(self.range_borrow().end() - at);
|
|
let new_range = self.range_borrow().shrink(self.range_borrow().end() - at);
|
|
@@ -119,35 +127,75 @@ impl MMArea {
|
|
|
|
|
|
|
|
/// # Arguments
|
|
/// # Arguments
|
|
|
/// * `offset`: The offset from the start of the mapping, aligned to 4KB boundary.
|
|
/// * `offset`: The offset from the start of the mapping, aligned to 4KB boundary.
|
|
|
- pub fn handle_mmap(
|
|
|
|
|
|
|
+ pub async fn handle_mmap(
|
|
|
&self,
|
|
&self,
|
|
|
pfn: &mut PFN,
|
|
pfn: &mut PFN,
|
|
|
attr: &mut PageAttribute,
|
|
attr: &mut PageAttribute,
|
|
|
offset: usize,
|
|
offset: usize,
|
|
|
|
|
+ error: PageFaultErrorCode,
|
|
|
) -> KResult<()> {
|
|
) -> KResult<()> {
|
|
|
- // TODO: Implement shared mapping
|
|
|
|
|
- let Mapping::File(mapping) = &self.mapping else {
|
|
|
|
|
|
|
+ let Mapping::File(file_mapping) = &self.mapping else {
|
|
|
panic!("Anonymous mapping should not be PA_MMAP");
|
|
panic!("Anonymous mapping should not be PA_MMAP");
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- assert!(offset < mapping.length, "Offset out of range");
|
|
|
|
|
- unsafe {
|
|
|
|
|
- Page::with_raw(*pfn, |page| {
|
|
|
|
|
- // SAFETY: `page` is marked as mapped, so others trying to read or write to
|
|
|
|
|
- // it will be blocked and enter the page fault handler, where they will
|
|
|
|
|
- // be blocked by the mutex held by us.
|
|
|
|
|
- let page_data = page.as_memblk().as_bytes_mut();
|
|
|
|
|
|
|
+ assert!(offset < file_mapping.length, "Offset out of range");
|
|
|
|
|
|
|
|
- let cnt_to_read = (mapping.length - offset).min(0x1000);
|
|
|
|
|
- let cnt_read = mapping.file.read(
|
|
|
|
|
- &mut ByteBuffer::new(&mut page_data[..cnt_to_read]),
|
|
|
|
|
- mapping.offset + offset,
|
|
|
|
|
- )?;
|
|
|
|
|
|
|
+ let Some(page_cache) = file_mapping.file.page_cache() else {
|
|
|
|
|
+ panic!("Mapping file should have pagecache");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let file_offset = file_mapping.offset + offset;
|
|
|
|
|
+ let cnt_to_read = (file_mapping.length - offset).min(0x1000);
|
|
|
|
|
+ let raw_page = page_cache.get_page(file_offset).await?.ok_or(EINVAL)?;
|
|
|
|
|
+
|
|
|
|
|
+ // Read or ifetch fault, we find page in pagecache and do mapping
|
|
|
|
|
+ // Write falut, we need to care about shared or private mapping.
|
|
|
|
|
+ if error.contains(PageFaultErrorCode::Read)
|
|
|
|
|
+ || error.contains(PageFaultErrorCode::InstructionFetch)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Bss is embarrassing in pagecache!
|
|
|
|
|
+ // We have to assume cnt_to_read < PAGE_SIZE all bss
|
|
|
|
|
+ if cnt_to_read < PAGE_SIZE {
|
|
|
|
|
+ let new_page = Page::zeroed();
|
|
|
|
|
+ unsafe {
|
|
|
|
|
+ let page_data = new_page.as_memblk().as_bytes_mut();
|
|
|
|
|
+ page_data[..cnt_to_read]
|
|
|
|
|
+ .copy_from_slice(&raw_page.as_memblk().as_bytes()[..cnt_to_read]);
|
|
|
|
|
+ }
|
|
|
|
|
+ *pfn = new_page.into_raw();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ raw_page.refcount().fetch_add(1, atomic::Ordering::Relaxed);
|
|
|
|
|
+ *pfn = Into::<PFN>::into(raw_page);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- page_data[cnt_read..].fill(0);
|
|
|
|
|
|
|
+ if self.permission.write {
|
|
|
|
|
+ if self.is_shared {
|
|
|
|
|
+ // The page may will not be written,
|
|
|
|
|
+ // But we simply assume page will be dirty
|
|
|
|
|
+ raw_page.set_dirty();
|
|
|
|
|
+ attr.insert(PageAttribute::WRITE);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ attr.insert(PageAttribute::COPY_ON_WRITE);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if error.contains(PageFaultErrorCode::Write) {
|
|
|
|
|
+ if self.is_shared {
|
|
|
|
|
+ raw_page.refcount().fetch_add(1, atomic::Ordering::Relaxed);
|
|
|
|
|
+ raw_page.set_dirty();
|
|
|
|
|
+ *pfn = Into::<PFN>::into(raw_page);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ let new_page = Page::zeroed();
|
|
|
|
|
+ unsafe {
|
|
|
|
|
+ let page_data = new_page.as_memblk().as_bytes_mut();
|
|
|
|
|
+ page_data[..cnt_to_read]
|
|
|
|
|
+ .copy_from_slice(&raw_page.as_memblk().as_bytes()[..cnt_to_read]);
|
|
|
|
|
+ }
|
|
|
|
|
+ *pfn = new_page.into_raw();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- KResult::Ok(())
|
|
|
|
|
- })?;
|
|
|
|
|
|
|
+ attr.insert(PageAttribute::WRITE);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ unreachable!("Unexpected page fault error code: {:?}", error);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
attr.insert(PageAttribute::PRESENT);
|
|
attr.insert(PageAttribute::PRESENT);
|
|
@@ -155,7 +203,12 @@ impl MMArea {
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub fn handle(&self, pte: &mut impl PTE, offset: usize) -> KResult<()> {
|
|
|
|
|
|
|
+ pub fn handle(
|
|
|
|
|
+ &self,
|
|
|
|
|
+ pte: &mut impl PTE,
|
|
|
|
|
+ offset: usize,
|
|
|
|
|
+ error: Option<PageFaultErrorCode>,
|
|
|
|
|
+ ) -> KResult<()> {
|
|
|
let mut attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
|
|
let mut attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
|
|
|
let mut pfn = pte.get_pfn();
|
|
let mut pfn = pte.get_pfn();
|
|
|
|
|
|
|
@@ -164,7 +217,9 @@ impl MMArea {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if attr.contains(PageAttribute::MAPPED) {
|
|
if attr.contains(PageAttribute::MAPPED) {
|
|
|
- self.handle_mmap(&mut pfn, &mut attr, offset)?;
|
|
|
|
|
|
|
+ let error =
|
|
|
|
|
+ error.expect("Mapped area should not be accessed without a page fault error code");
|
|
|
|
|
+ Task::block_on(self.handle_mmap(&mut pfn, &mut attr, offset, error))?;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
attr.set(PageAttribute::ACCESSED, true);
|
|
attr.set(PageAttribute::ACCESSED, true);
|