WIP: Remove SYS_CLONE (to be done in userspace).

This commit is contained in:
4lDO2
2022-07-04 10:42:04 +02:00
parent 563121596d
commit 283ada82a0
20 changed files with 380 additions and 909 deletions
+19 -7
View File
@@ -16,13 +16,13 @@ use crate::arch::{interrupt::InterruptStack, paging::PAGE_SIZE};
use crate::common::unique::Unique;
use crate::context::arch;
use crate::context::file::{FileDescriptor, FileDescription};
use crate::context::memory::UserGrants;
use crate::context::memory::{AddrSpace, new_addrspace, UserGrants};
use crate::ipi::{ipi, IpiKind, IpiTarget};
use crate::scheme::{SchemeNamespace, FileHandle};
use crate::sync::WaitMap;
use crate::syscall::data::SigAction;
use crate::syscall::error::{Result, Error, ENOMEM};
use crate::syscall::error::{Result, Error, ENOMEM, ESRCH};
use crate::syscall::flag::{SIG_DFL, SigActionFlags};
/// Unique identifier for a context (i.e. `pid`).
@@ -226,8 +226,9 @@ pub struct Context {
pub ksig: Option<(arch::Context, Option<Box<[u8]>>, Option<Box<[u8]>>, u8)>,
/// Restore ksig context on next switch
pub ksig_restore: bool,
/// User grants
pub grants: Arc<RwLock<UserGrants>>,
/// Address space containing a page table lock, and grants. Normally this will have a value,
/// but can be None while the context is being reaped.
pub addr_space: Option<Arc<RwLock<AddrSpace>>>,
/// The name of the context
pub name: Arc<RwLock<Box<str>>>,
/// The current working directory
@@ -307,7 +308,7 @@ impl Context {
let syscall_head = AlignedBox::try_zeroed()?;
let syscall_tail = AlignedBox::try_zeroed()?;
Ok(Context {
let mut this = Context {
id,
pgid: id,
ppid: ContextId::from(0),
@@ -336,7 +337,7 @@ impl Context {
kstack: None,
ksig: None,
ksig_restore: false,
grants: Arc::new(RwLock::new(UserGrants::default())),
addr_space: None,
name: Arc::new(RwLock::new(String::new().into_boxed_str())),
cwd: Arc::new(RwLock::new(String::new())),
files: Arc::new(RwLock::new(Vec::new())),
@@ -351,7 +352,9 @@ impl Context {
regs: None,
ptrace_stop: false,
sigstack: None,
})
};
this.set_addr_space(new_addrspace()?.1);
Ok(this)
}
/// Make a relative path absolute
@@ -520,4 +523,13 @@ impl Context {
None
}
}
pub fn addr_space(&self) -> Result<&Arc<RwLock<AddrSpace>>> {
self.addr_space.as_ref().ok_or(Error::new(ESRCH))
}
pub fn set_addr_space(&mut self, addr_space: Arc<RwLock<AddrSpace>>) {
assert!(!self.running);
self.arch.set_page_utable(addr_space.read().frame.utable.start_address().data());
self.addr_space = Some(addr_space);
}
}
+6 -9
View File
@@ -7,7 +7,7 @@ use core::sync::atomic::Ordering;
use crate::paging::{ActivePageTable, TableKind};
use spin::RwLock;
use crate::syscall::error::{Result, Error, EAGAIN};
use crate::syscall::error::{Result, Error, EAGAIN, ENOMEM};
use super::context::{Context, ContextId};
/// Context list type
@@ -79,7 +79,11 @@ impl ContextList {
let context_lock = self.new_context()?;
{
let mut context = context_lock.write();
let mut fx = unsafe { Box::from_raw(crate::ALLOCATOR.alloc(Layout::from_size_align_unchecked(1024, 16)) as *mut [u8; 1024]) };
let mut fx = unsafe {
let ptr = crate::ALLOCATOR.alloc(Layout::from_size_align_unchecked(1024, 16)) as *mut [u8; 1024];
if ptr.is_null() { return Err(Error::new(ENOMEM)); }
Box::from_raw(ptr)
};
for b in fx.iter_mut() {
*b = 0;
}
@@ -100,13 +104,6 @@ impl ContextList {
context.arch.set_context_handle();
}
let mut new_tables = super::memory::setup_new_utable()?;
new_tables.take();
context.arch.set_page_utable(unsafe { new_tables.new_utable.address() });
#[cfg(target_arch = "aarch64")]
context.arch.set_page_ktable(unsafe { new_tables.new_ktable.address() });
context.arch.set_fx(fx.as_ptr() as usize);
context.arch.set_stack(stack.as_ptr() as usize + offset);
context.kfx = Some(fx);
+121 -197
View File
@@ -5,7 +5,8 @@ use core::cmp::{self, Eq, Ordering, PartialEq, PartialOrd};
use core::fmt::{self, Debug};
use core::intrinsics;
use core::ops::Deref;
use spin::Mutex;
use core::sync::atomic;
use spin::{Mutex, RwLock};
use syscall::{
flag::MapFlags,
error::*,
@@ -14,9 +15,8 @@ use rmm::Arch as _;
use crate::arch::paging::PAGE_SIZE;
use crate::context::file::FileDescriptor;
use crate::ipi::{ipi, IpiKind, IpiTarget};
use crate::memory::Frame;
use crate::paging::mapper::PageFlushAll;
use crate::paging::mapper::{Flusher, InactiveFlusher, Mapper, PageFlushAll};
use crate::paging::{ActivePageTable, InactivePageTable, Page, PageFlags, PageIter, PhysicalAddress, RmmA, TableKind, VirtualAddress};
/// Round down to the nearest multiple of page size
@@ -47,6 +47,76 @@ impl Drop for UnmapResult {
}
}
int_like!(PtId, usize);
static ADDRSPACES: RwLock<BTreeMap<PtId, Arc<RwLock<AddrSpace>>>> = RwLock::new(BTreeMap::new());
static NEXT_PTID: atomic::AtomicUsize = atomic::AtomicUsize::new(1);
pub fn new_addrspace() -> Result<(PtId, Arc<RwLock<AddrSpace>>)> {
let id = PtId::from(NEXT_PTID.fetch_add(1, atomic::Ordering::Relaxed));
let arc = Arc::try_new(RwLock::new(AddrSpace::new(id)?)).map_err(|_| Error::new(ENOMEM))?;
ADDRSPACES.write().insert(id, Arc::clone(&arc));
Ok((id, arc))
}
pub fn addrspace(id: PtId) -> Option<Arc<RwLock<AddrSpace>>> {
ADDRSPACES.read().get(&id).map(Arc::clone)
}
#[derive(Debug)]
pub struct AddrSpace {
pub frame: Tables,
pub grants: UserGrants,
pub id: PtId,
}
impl AddrSpace {
/// Attempt to clone an existing address space so that all mappings are copied (CoW).
// TODO: Actually use CoW!
pub fn try_clone(&self) -> Result<(PtId, Arc<RwLock<Self>>)> {
let (id, mut new) = new_addrspace()?;
// TODO: Abstract away this.
let (mut inactive, mut active);
// TODO: aarch64
let mut this_mapper = if self.frame.utable.start_address().data() == unsafe { x86::controlregs::cr3() } as usize {
active = unsafe { ActivePageTable::new(rmm::TableKind::User) };
active.mapper()
} else {
inactive = unsafe { InactivePageTable::from_address(self.frame.utable.start_address().data()) };
inactive.mapper()
};
let mut new_mapper = unsafe { InactivePageTable::from_address(new.read().frame.utable.start_address().data()) };
for grant in self.grants.iter() {
// TODO: Fail if there are borrowed grants, rather than simply ignoring them?
if !grant.is_owned() { continue; }
let new_grant = Grant::zeroed(Page::containing_address(grant.start_address()), grant.size() / PAGE_SIZE, grant.flags(), &mut new_mapper.mapper(), ())?;
for page in new_grant.pages() {
// FIXME: ENOMEM is wrong here, it cannot fail.
let current_frame = this_mapper.translate_page(page).ok_or(Error::new(ENOMEM))?.start_address().data() as *const u8;
let new_frame = new_mapper.mapper().translate_page(page).ok_or(Error::new(ENOMEM))?.start_address().data() as *mut u8;
// TODO: Replace this with CoW
unsafe {
new_frame.copy_from_nonoverlapping(current_frame, PAGE_SIZE);
}
}
new.write().grants.insert(new_grant);
}
Ok((id, new))
}
pub fn new(id: PtId) -> Result<Self> {
Ok(Self {
grants: UserGrants::new(),
frame: setup_new_utable()?,
id,
})
}
}
#[derive(Debug)]
pub struct UserGrants {
inner: BTreeSet<Grant>,
@@ -406,7 +476,7 @@ impl Grant {
pub fn physmap(from: PhysicalAddress, to: VirtualAddress, size: usize, flags: PageFlags<RmmA>) -> Grant {
let mut active_table = unsafe { ActivePageTable::new(to.kind()) };
let flush_all = PageFlushAll::new();
let mut flush_all = PageFlushAll::new();
let start_page = Page::containing_address(to);
let end_page = Page::containing_address(VirtualAddress::new(to.data() + size - 1));
@@ -429,40 +499,10 @@ impl Grant {
desc_opt: None,
}
}
pub fn map(to: VirtualAddress, size: usize, flags: PageFlags<RmmA>) -> Grant {
let mut active_table = unsafe { ActivePageTable::new(to.kind()) };
let flush_all = PageFlushAll::new();
let start_page = Page::containing_address(to);
let end_page = Page::containing_address(VirtualAddress::new(to.data() + size - 1));
for page in Page::range_inclusive(start_page, end_page) {
let result = active_table
.map(page, flags)
.expect("TODO: handle ENOMEM in Grant::map");
flush_all.consume(result);
}
flush_all.flush();
Grant {
region: Region {
start: to,
size,
},
flags,
mapped: true,
owned: true,
desc_opt: None,
}
}
pub fn zeroed_inactive(dst: Page, page_count: usize, flags: PageFlags<RmmA>, table: &mut InactivePageTable) -> Result<Grant> {
let mut inactive_mapper = table.mapper();
pub fn zeroed(dst: Page, page_count: usize, flags: PageFlags<RmmA>, mapper: &mut Mapper, mut flusher: impl Flusher<RmmA>) -> Result<Grant> {
for page in Page::range_exclusive(dst, dst.next_by(page_count)) {
let flush = inactive_mapper.map(page, flags).map_err(|_| Error::new(ENOMEM))?;
unsafe { flush.ignore(); }
let flush = mapper.map(page, flags).map_err(|_| Error::new(ENOMEM))?;
flusher.consume(flush);
}
Ok(Grant { region: Region { start: dst.start_address(), size: page_count * PAGE_SIZE }, flags, mapped: true, owned: true, desc_opt: None })
}
@@ -487,8 +527,6 @@ impl Grant {
unsafe { inactive_flush.ignore(); }
}
ipi(IpiKind::Tlb, IpiTarget::Other);
Grant {
region: Region {
start: dst,
@@ -501,97 +539,22 @@ impl Grant {
}
}
/// This function should only be used in clone!
pub(crate) fn secret_clone(&self, inactive_table: &mut InactivePageTable) -> Grant {
assert!(self.mapped);
let active_table = unsafe { ActivePageTable::new(TableKind::User) };
let mut inactive_mapper = inactive_table.mapper();
for page in self.pages() {
//TODO: One function to do both?
let flags = active_table.translate_page_flags(page).expect("grant references unmapped memory");
let old_frame = active_table.translate_page(page).expect("grant references unmapped memory");
let frame = if self.owned {
// TODO: CoW paging
let new_frame = crate::memory::allocate_frames(1)
.expect("TODO: handle ENOMEM in Grant::secret_clone");
unsafe {
// We might as well use self.start_address() directly, but if we were to
// introduce SMAP it would help to only move to/from kernel memory, and we are
// copying physical frames anyway.
let src_pointer = RmmA::phys_to_virt(old_frame.start_address()).data() as *const u8;
let dst_pointer = RmmA::phys_to_virt(new_frame.start_address()).data() as *mut u8;
dst_pointer.copy_from_nonoverlapping(src_pointer, PAGE_SIZE);
}
new_frame
} else {
old_frame
};
let flush = inactive_mapper.map_to(page, frame, flags);
// SAFETY: This happens within an inactive table.
unsafe { flush.ignore() }
}
Grant {
region: Region {
start: self.region.start,
size: self.region.size,
},
flags: self.flags,
mapped: true,
owned: self.owned,
desc_opt: self.desc_opt.clone()
}
}
pub fn flags(&self) -> PageFlags<RmmA> {
self.flags
}
pub fn unmap(mut self) -> UnmapResult {
pub fn unmap(mut self, mapper: &mut Mapper, mut flusher: impl Flusher<RmmA>) -> UnmapResult {
assert!(self.mapped);
let mut active_table = unsafe { ActivePageTable::new(self.start_address().kind()) };
let flush_all = PageFlushAll::new();
for page in self.pages() {
let (result, frame) = active_table.unmap_return(page, false);
let (result, frame) = mapper.unmap_return(page, false);
if self.owned {
//TODO: make sure this frame can be safely freed, physical use counter
crate::memory::deallocate_frames(frame, 1);
}
flush_all.consume(result);
flusher.consume(result);
}
flush_all.flush();
self.mapped = false;
// TODO: This imposes a large cost on unmapping, but that cost cannot be avoided without modifying fmap and funmap
UnmapResult { file_desc: self.desc_opt.take() }
}
pub fn unmap_inactive(mut self, other_table: &mut InactivePageTable) -> UnmapResult {
assert!(self.mapped);
for page in self.pages() {
let (result, frame) = other_table.mapper().unmap_return(page, false);
if self.owned {
//TODO: make sure this frame can be safely freed, physical use counter
crate::memory::deallocate_frames(frame, 1);
}
// This is not the active table, so the flush can be ignored
unsafe { result.ignore(); }
}
ipi(IpiKind::Tlb, IpiTarget::Other);
self.mapped = false;
// TODO: This imposes a large cost on unmapping, but that cost cannot be avoided without modifying fmap and funmap
@@ -636,34 +599,6 @@ impl Grant {
Some((before_grant, self, after_grant))
}
pub fn move_to_address_space(&mut self, new_start: Page, new_page_table: &mut InactivePageTable, flags: PageFlags<RmmA>, flush_all: &mut PageFlushAll<RmmA>) -> Grant {
assert!(self.mapped);
let mut active_table = unsafe { ActivePageTable::new(TableKind::User) };
let mut new_mapper = new_page_table.mapper();
let keep_parents = false;
for (i, page) in self.pages().enumerate() {
unsafe {
let (flush, frame) = active_table.unmap_return(page, keep_parents);
flush_all.consume(flush);
let flush = new_mapper.map_to(new_start.next_by(i), frame, flags);
flush.ignore();
}
}
let was_owned = core::mem::replace(&mut self.owned, false);
self.mapped = false;
Self {
region: Region::new(new_start.start_address(), self.region.size),
flags,
mapped: true,
owned: was_owned,
desc_opt: self.desc_opt.clone(),
}
}
}
impl Deref for Grant {
@@ -704,79 +639,68 @@ impl Drop for Grant {
pub const DANGLING: usize = 1 << (usize::BITS - 2);
pub struct NewTables {
#[derive(Debug)]
pub struct Tables {
#[cfg(target_arch = "aarch64")]
pub new_ktable: InactivePageTable,
pub new_utable: InactivePageTable,
pub ktable: Frame,
taken: bool,
}
impl NewTables {
pub fn take(&mut self) {
self.taken = true;
}
pub utable: Frame,
}
impl Drop for NewTables {
impl Drop for Tables {
fn drop(&mut self) {
if self.taken { return }
use crate::memory::deallocate_frames;
deallocate_frames(Frame::containing_address(PhysicalAddress::new(self.utable.start_address().data())), 1);
unsafe {
use crate::memory::deallocate_frames;
deallocate_frames(Frame::containing_address(PhysicalAddress::new(self.new_utable.address())), 1);
#[cfg(target_arch = "aarch64")]
deallocate_frames(Frame::containing_address(PhysicalAddress::new(self.new_ktable.address())), 1);
}
#[cfg(target_arch = "aarch64")]
deallocate_frames(Frame::containing_address(PhysicalAddress::new(self.ktable.start_address().data())), 1);
}
}
/// Allocates a new identically mapped ktable and empty utable (same memory on x86_64).
pub fn setup_new_utable() -> Result<NewTables> {
let mut new_utable = unsafe { InactivePageTable::new(crate::memory::allocate_frames(1).ok_or(Error::new(ENOMEM))?) };
pub fn setup_new_utable() -> Result<Tables> {
let mut new_utable = crate::memory::allocate_frames(1).ok_or(Error::new(ENOMEM))?;
let mut new_ktable = if cfg!(target_arch = "aarch64") {
unsafe { InactivePageTable::new(crate::memory::allocate_frames(1).ok_or(Error::new(ENOMEM))?) }
} else {
unsafe { InactivePageTable::from_address(new_utable.address()) }
};
// TODO: There is only supposed to be one ktable, right? Use a global variable to store the
// ktable (or access it from a control register) on architectures which have ktables, or obtain
// it from *any* utable on architectures which do not.
#[cfg(target_arch = "aarch64")]
let new_ktable = crate::memory::allocate_frames(1).ok_or(Error::new(ENOMEM))?;
let active_ktable = unsafe { ActivePageTable::new(TableKind::Kernel) };
// Copy kernel image mapping
{
let frame = active_ktable.p4()[crate::KERNEL_PML4].pointed_frame().expect("kernel image not mapped");
let flags = active_ktable.p4()[crate::KERNEL_PML4].flags();
#[cfg(target_arch = "aarch64")]
let ktable = &new_ktable;
new_ktable.mapper().p4_mut()[crate::KERNEL_PML4].set(frame, flags);
}
#[cfg(not(target_arch = "aarch64"))]
let ktable = &new_utable;
let mut new_mapper = unsafe { InactivePageTable::from_address(ktable.start_address().data()) };
let mut copy_mapping = |p4_no| {
let frame = active_ktable.p4()[p4_no].pointed_frame().expect("kernel image not mapped");
let flags = active_ktable.p4()[p4_no].flags();
new_mapper.mapper().p4_mut()[p4_no].set(frame, flags);
};
// TODO: Just copy all 256 mappings?
// Copy kernel image mapping
copy_mapping(crate::KERNEL_PML4);
// Copy kernel heap mapping
{
let frame = active_ktable.p4()[crate::KERNEL_HEAP_PML4].pointed_frame().expect("kernel heap not mapped");
let flags = active_ktable.p4()[crate::KERNEL_HEAP_PML4].flags();
new_ktable.mapper().p4_mut()[crate::KERNEL_HEAP_PML4].set(frame, flags);
}
copy_mapping(crate::KERNEL_HEAP_PML4);
// Copy physmap mapping
{
let frame = active_ktable.p4()[crate::PHYS_PML4].pointed_frame().expect("physmap not mapped");
let flags = active_ktable.p4()[crate::PHYS_PML4].flags();
new_ktable.mapper().p4_mut()[crate::PHYS_PML4].set(frame, flags);
}
// Copy kernel percpu (similar to TLS) mapping.
{
let frame = active_ktable.p4()[crate::KERNEL_PERCPU_PML4].pointed_frame().expect("kernel TLS not mapped");
let flags = active_ktable.p4()[crate::KERNEL_PERCPU_PML4].flags();
new_ktable.mapper().p4_mut()[crate::KERNEL_PERCPU_PML4].set(frame, flags);
}
copy_mapping(crate::PHYS_PML4);
Ok(NewTables {
taken: false,
new_utable,
// Copy kernel percpu (similar to TLS) mapping.
copy_mapping(crate::KERNEL_PERCPU_PML4);
Ok(Tables {
utable: new_utable,
#[cfg(target_arch = "aarch64")]
new_ktable,
ktable: new_ktable,
})
}