|
@@ -1,23 +1,25 @@
|
|
|
mod page_fault;
|
|
|
|
|
|
-use crate::prelude::*;
|
|
|
+use core::sync::atomic::{AtomicUsize, Ordering};
|
|
|
+
|
|
|
+use crate::{prelude::*, sync::ArcSwap};
|
|
|
|
|
|
use alloc::{collections::btree_set::BTreeSet, sync::Arc};
|
|
|
-use bindings::{EEXIST, EINVAL, ENOMEM};
|
|
|
+use bindings::{EEXIST, EFAULT, EINVAL, ENOMEM, KERNEL_PML4};
|
|
|
|
|
|
use crate::kernel::vfs::dentry::Dentry;
|
|
|
|
|
|
-use super::{MMArea, PageTable, VAddr, VRange};
|
|
|
+use super::{MMArea, Page, PageTable, VAddr, VRange};
|
|
|
|
|
|
pub use page_fault::{handle_page_fault, PageFaultError};
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct FileMapping {
|
|
|
- file: Arc<Dentry>,
|
|
|
+ pub file: Arc<Dentry>,
|
|
|
/// Offset in the file, aligned to 4KB boundary.
|
|
|
- offset: usize,
|
|
|
+ pub offset: usize,
|
|
|
/// Length of the mapping. Exceeding part will be zeroed.
|
|
|
- length: usize,
|
|
|
+ pub length: usize,
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
@@ -35,6 +37,7 @@ pub enum Mapping {
|
|
|
#[derive(Debug)]
|
|
|
struct MMListInner {
|
|
|
areas: BTreeSet<MMArea>,
|
|
|
+ page_table: PageTable,
|
|
|
break_start: Option<VRange>,
|
|
|
break_pos: Option<VAddr>,
|
|
|
}
|
|
@@ -43,9 +46,9 @@ struct MMListInner {
|
|
|
pub struct MMList {
|
|
|
/// # Safety
|
|
|
/// This field might be used in IRQ context, so it should be locked with `lock_irq()`.
|
|
|
- inner: Mutex<MMListInner>,
|
|
|
- /// Do not modify entries in the page table without acquiring the `inner` lock.
|
|
|
- page_table: PageTable,
|
|
|
+ inner: ArcSwap<Mutex<MMListInner>>,
|
|
|
+ /// Only used in kernel space to switch page tables on context switch.
|
|
|
+ root_page_table: AtomicUsize,
|
|
|
}
|
|
|
|
|
|
impl FileMapping {
|
|
@@ -110,7 +113,7 @@ impl MMListInner {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn unmap(&mut self, page_table: &PageTable, start: VAddr, len: usize) -> KResult<()> {
|
|
|
+ fn unmap(&mut self, start: VAddr, len: usize) -> KResult<()> {
|
|
|
assert_eq!(start.floor(), start);
|
|
|
let end = (start + len).ceil();
|
|
|
let range = VRange::new(start, end);
|
|
@@ -128,7 +131,7 @@ impl MMListInner {
|
|
|
}
|
|
|
if area.range() == range.start().into() {
|
|
|
let (left, right) = area.clone().split(range.start());
|
|
|
- page_table.unmap(&right.unwrap());
|
|
|
+ self.page_table.unmap(&right.unwrap());
|
|
|
|
|
|
if let Some(left) = left {
|
|
|
assert!(
|
|
@@ -138,7 +141,7 @@ impl MMListInner {
|
|
|
}
|
|
|
} else if area.range() == range.end().into() {
|
|
|
let (left, right) = area.clone().split(range.end());
|
|
|
- page_table.unmap(&left.unwrap());
|
|
|
+ self.page_table.unmap(&left.unwrap());
|
|
|
|
|
|
assert!(
|
|
|
back_remaining
|
|
@@ -147,7 +150,7 @@ impl MMListInner {
|
|
|
"There should be only one `back`."
|
|
|
);
|
|
|
} else {
|
|
|
- page_table.unmap(area);
|
|
|
+ self.page_table.unmap(area);
|
|
|
}
|
|
|
|
|
|
false
|
|
@@ -165,7 +168,6 @@ impl MMListInner {
|
|
|
|
|
|
fn mmap(
|
|
|
&mut self,
|
|
|
- page_table: &PageTable,
|
|
|
at: VAddr,
|
|
|
len: usize,
|
|
|
mapping: Mapping,
|
|
@@ -181,8 +183,8 @@ impl MMListInner {
|
|
|
}
|
|
|
|
|
|
match &mapping {
|
|
|
- Mapping::Anonymous => page_table.set_anonymous(range, permission),
|
|
|
- Mapping::File(_) => page_table.set_mmapped(range, permission),
|
|
|
+ Mapping::Anonymous => self.page_table.set_anonymous(range, permission),
|
|
|
+ Mapping::File(_) => self.page_table.set_mmapped(range, permission),
|
|
|
}
|
|
|
|
|
|
self.areas.insert(MMArea::new(range, mapping, permission));
|
|
@@ -191,36 +193,41 @@ impl MMListInner {
|
|
|
}
|
|
|
|
|
|
impl MMList {
|
|
|
- pub fn new() -> Arc<Self> {
|
|
|
- Arc::new(Self {
|
|
|
- inner: Mutex::new(MMListInner {
|
|
|
+ pub fn new() -> Self {
|
|
|
+ let page_table = PageTable::new();
|
|
|
+ Self {
|
|
|
+ root_page_table: AtomicUsize::from(page_table.root_page_table()),
|
|
|
+ inner: ArcSwap::new(Mutex::new(MMListInner {
|
|
|
areas: BTreeSet::new(),
|
|
|
+ page_table,
|
|
|
break_start: None,
|
|
|
break_pos: None,
|
|
|
- }),
|
|
|
- page_table: PageTable::new(),
|
|
|
- })
|
|
|
+ })),
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- pub fn new_cloned(&self) -> Arc<Self> {
|
|
|
- let inner = self.inner.lock_irq();
|
|
|
+ pub fn new_cloned(&self) -> Self {
|
|
|
+ let inner = self.inner.borrow();
|
|
|
+ let inner = inner.lock_irq();
|
|
|
|
|
|
- let list = Arc::new(Self {
|
|
|
- inner: Mutex::new(MMListInner {
|
|
|
+ let page_table = PageTable::new();
|
|
|
+ let list = Self {
|
|
|
+ root_page_table: AtomicUsize::from(page_table.root_page_table()),
|
|
|
+ inner: ArcSwap::new(Mutex::new(MMListInner {
|
|
|
areas: inner.areas.clone(),
|
|
|
+ page_table,
|
|
|
break_start: inner.break_start,
|
|
|
break_pos: inner.break_pos,
|
|
|
- }),
|
|
|
- page_table: PageTable::new(),
|
|
|
- });
|
|
|
+ })),
|
|
|
+ };
|
|
|
|
|
|
- // SAFETY: `self.inner` already locked with IRQ disabled.
|
|
|
{
|
|
|
- let list_inner = list.inner.lock();
|
|
|
+ let list_inner = list.inner.borrow();
|
|
|
+ let list_inner = list_inner.lock();
|
|
|
|
|
|
for area in list_inner.areas.iter() {
|
|
|
- let new_iter = list.page_table.iter_user(area.range()).unwrap();
|
|
|
- let old_iter = self.page_table.iter_user(area.range()).unwrap();
|
|
|
+ let new_iter = list_inner.page_table.iter_user(area.range()).unwrap();
|
|
|
+ let old_iter = inner.page_table.iter_user(area.range()).unwrap();
|
|
|
|
|
|
for (new, old) in new_iter.zip(old_iter) {
|
|
|
new.setup_cow(old);
|
|
@@ -229,29 +236,54 @@ impl MMList {
|
|
|
}
|
|
|
|
|
|
// We set some pages as COW, so we need to invalidate TLB.
|
|
|
- self.page_table.lazy_invalidate_tlb_all();
|
|
|
+ inner.page_table.lazy_invalidate_tlb_all();
|
|
|
|
|
|
list
|
|
|
}
|
|
|
|
|
|
- /// No need to do invalidation manually, `PageTable` already does it.
|
|
|
- pub fn clear_user(&self) {
|
|
|
- let mut inner = self.inner.lock_irq();
|
|
|
- inner.areas.retain(|area| {
|
|
|
- self.page_table.unmap(area);
|
|
|
- false
|
|
|
- });
|
|
|
- inner.break_start = None;
|
|
|
- inner.break_pos = None;
|
|
|
+ pub fn switch_page_table(&self) {
|
|
|
+ let root_page_table = self.root_page_table.load(Ordering::Relaxed);
|
|
|
+ assert_ne!(root_page_table, 0);
|
|
|
+ arch::set_root_page_table(root_page_table);
|
|
|
}
|
|
|
|
|
|
- pub fn switch_page_table(&self) {
|
|
|
- self.page_table.switch();
|
|
|
+ pub fn replace(&self, new: Self) {
|
|
|
+ // Switch to kernel page table in case we are using the page table to be swapped and released.
|
|
|
+ let mut switched = false;
|
|
|
+ if arch::get_root_page_table() == self.root_page_table.load(Ordering::Relaxed) {
|
|
|
+ arch::set_root_page_table(KERNEL_PML4 as usize);
|
|
|
+ switched = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsafe {
|
|
|
+ // SAFETY: Even if we're using the page table, we've switched to kernel page table.
|
|
|
+ // So it's safe to release the old memory list.
|
|
|
+ self.release();
|
|
|
+ }
|
|
|
+
|
|
|
+ // SAFETY: `self.inner` should be `None` after releasing.
|
|
|
+ self.inner.swap(Some(new.inner.borrow().clone()));
|
|
|
+ self.root_page_table.store(
|
|
|
+ new.root_page_table.load(Ordering::Relaxed),
|
|
|
+ Ordering::Relaxed,
|
|
|
+ );
|
|
|
+
|
|
|
+ if switched {
|
|
|
+ self.switch_page_table();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// # Safety
|
|
|
+ /// This function is unsafe because the caller should make sure that the `inner` is not currently used.
|
|
|
+ pub unsafe fn release(&self) {
|
|
|
+ // TODO: Check whether we should wake someone up if they've been put to sleep when calling `vfork`.
|
|
|
+ self.inner.swap(None);
|
|
|
+ self.root_page_table.store(0, Ordering::Relaxed);
|
|
|
}
|
|
|
|
|
|
/// No need to do invalidation manually, `PageTable` already does it.
|
|
|
pub fn unmap(&self, start: VAddr, len: usize) -> KResult<()> {
|
|
|
- self.inner.lock_irq().unmap(&self.page_table, start, len)
|
|
|
+ self.inner.borrow().lock_irq().unmap(start, len)
|
|
|
}
|
|
|
|
|
|
pub fn mmap_hint(
|
|
@@ -261,18 +293,20 @@ impl MMList {
|
|
|
mapping: Mapping,
|
|
|
permission: Permission,
|
|
|
) -> KResult<VAddr> {
|
|
|
- let mut inner = self.inner.lock_irq();
|
|
|
+ let inner = self.inner.borrow();
|
|
|
+ let mut inner = inner.lock_irq();
|
|
|
+
|
|
|
if hint == VAddr::NULL {
|
|
|
let at = inner.find_available(hint, len).ok_or(ENOMEM)?;
|
|
|
- inner.mmap(&self.page_table, at, len, mapping, permission)?;
|
|
|
+ inner.mmap(at, len, mapping, permission)?;
|
|
|
return Ok(at);
|
|
|
}
|
|
|
|
|
|
- match inner.mmap(&self.page_table, hint, len, mapping.clone(), permission) {
|
|
|
+ match inner.mmap(hint, len, mapping.clone(), permission) {
|
|
|
Ok(()) => Ok(hint),
|
|
|
Err(EEXIST) => {
|
|
|
let at = inner.find_available(hint, len).ok_or(ENOMEM)?;
|
|
|
- inner.mmap(&self.page_table, at, len, mapping, permission)?;
|
|
|
+ inner.mmap(at, len, mapping, permission)?;
|
|
|
Ok(at)
|
|
|
}
|
|
|
Err(err) => Err(err),
|
|
@@ -287,13 +321,15 @@ impl MMList {
|
|
|
permission: Permission,
|
|
|
) -> KResult<VAddr> {
|
|
|
self.inner
|
|
|
+ .borrow()
|
|
|
.lock_irq()
|
|
|
- .mmap(&self.page_table, at, len, mapping.clone(), permission)
|
|
|
+ .mmap(at, len, mapping.clone(), permission)
|
|
|
.map(|_| at)
|
|
|
}
|
|
|
|
|
|
pub fn set_break(&self, pos: Option<VAddr>) -> VAddr {
|
|
|
- let mut inner = self.inner.lock_irq();
|
|
|
+ let inner = self.inner.borrow();
|
|
|
+ let mut inner = inner.lock_irq();
|
|
|
|
|
|
// SAFETY: `set_break` is only called in syscalls, where program break should be valid.
|
|
|
assert!(inner.break_start.is_some() && inner.break_pos.is_some());
|
|
@@ -326,7 +362,7 @@ impl MMList {
|
|
|
.expect("Program break area should be valid");
|
|
|
|
|
|
let len = pos - current_break;
|
|
|
- self.page_table.set_anonymous(
|
|
|
+ inner.page_table.set_anonymous(
|
|
|
VRange::from(program_break.range().end()).grow(len),
|
|
|
Permission {
|
|
|
write: true,
|
|
@@ -342,19 +378,81 @@ impl MMList {
|
|
|
|
|
|
/// This should be called only **once** for every thread.
|
|
|
pub fn register_break(&self, start: VAddr) {
|
|
|
- let mut inner = self.inner.lock_irq();
|
|
|
+ let inner = self.inner.borrow();
|
|
|
+ let mut inner = inner.lock_irq();
|
|
|
assert!(inner.break_start.is_none() && inner.break_pos.is_none());
|
|
|
|
|
|
inner.break_start = Some(start.into());
|
|
|
inner.break_pos = Some(start);
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-impl Drop for MMList {
|
|
|
- fn drop(&mut self) {
|
|
|
- let inner = self.inner.get_mut();
|
|
|
- assert!(inner.areas.is_empty());
|
|
|
- assert_eq!(inner.break_start, None);
|
|
|
- assert_eq!(inner.break_pos, None);
|
|
|
+ /// Access the memory area with the given function.
|
|
|
+ /// The function will be called with the offset of the area and the slice of the area.
|
|
|
+ pub fn access_mut<F>(&self, start: VAddr, len: usize, func: F) -> KResult<()>
|
|
|
+ where
|
|
|
+ F: Fn(usize, &mut [u8]),
|
|
|
+ {
|
|
|
+ // First, validate the address range.
|
|
|
+ let end = start + len;
|
|
|
+ if !start.is_user() || !end.is_user() {
|
|
|
+ return Err(EINVAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ let inner = self.inner.borrow();
|
|
|
+ let inner = inner.lock_irq();
|
|
|
+
|
|
|
+ let mut offset = 0;
|
|
|
+ let mut remaining = len;
|
|
|
+ let mut current = start;
|
|
|
+
|
|
|
+ while remaining > 0 {
|
|
|
+ let area = inner.overlapping_addr(current).ok_or(EFAULT)?;
|
|
|
+
|
|
|
+ let area_start = area.range().start();
|
|
|
+ let area_end = area.range().end();
|
|
|
+ let area_remaining = area_end - current;
|
|
|
+
|
|
|
+ let access_len = remaining.min(area_remaining);
|
|
|
+ let access_end = current + access_len;
|
|
|
+
|
|
|
+ for (idx, pte) in inner
|
|
|
+ .page_table
|
|
|
+ .iter_user(VRange::new(current, access_end))?
|
|
|
+ .enumerate()
|
|
|
+ {
|
|
|
+ let page_start = current.floor() + idx * 0x1000;
|
|
|
+ let page_end = page_start + 0x1000;
|
|
|
+
|
|
|
+ area.handle(pte, page_start - area_start)?;
|
|
|
+
|
|
|
+ let start_offset;
|
|
|
+ if page_start < current {
|
|
|
+ start_offset = current - page_start;
|
|
|
+ } else {
|
|
|
+ start_offset = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ let end_offset;
|
|
|
+ if page_end > access_end {
|
|
|
+ end_offset = access_end - page_start;
|
|
|
+ } else {
|
|
|
+ end_offset = 0x1000;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsafe {
|
|
|
+ let page = Page::from_pfn(pte.pfn(), 0);
|
|
|
+ func(
|
|
|
+ offset + idx * 0x1000,
|
|
|
+ &mut page.as_mut_slice()[start_offset..end_offset],
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ offset += access_len;
|
|
|
+ remaining -= access_len;
|
|
|
+ current = access_end;
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(())
|
|
|
}
|
|
|
}
|