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
+2 -1
View File
@@ -5,6 +5,7 @@ use alloc::collections::BTreeMap;
use core::{slice, str};
use core::sync::atomic::{AtomicUsize, Ordering};
use spin::RwLock;
use rmm::Flusher;
use syscall::data::Stat;
use syscall::error::*;
@@ -55,7 +56,7 @@ impl DiskScheme {
let virt = phys + crate::PHYS_OFFSET;
unsafe {
let mut active_table = ActivePageTable::new(TableKind::Kernel);
let flush_all = PageFlushAll::new();
let mut flush_all = PageFlushAll::new();
let start_page = Page::containing_address(VirtualAddress::new(virt));
let end_page = Page::containing_address(VirtualAddress::new(virt + size - 1));
for page in Page::range_inclusive(start_page, end_page) {
+4 -18
View File
@@ -1,7 +1,7 @@
use crate::context;
use crate::context::memory::{page_flags, Grant};
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
use crate::paging::{ActivePageTable, VirtualAddress};
use crate::paging::{ActivePageTable, mapper::PageFlushAll, Page, VirtualAddress};
use crate::syscall::data::{Map, OldMap, StatVfs};
use crate::syscall::error::*;
use crate::syscall::flag::MapFlags;
@@ -23,25 +23,11 @@ impl MemoryScheme {
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
let context = context_lock.read();
let mut grants = context.grants.write();
let mut addr_space = context.addr_space()?.write();
let region = grants.find_free_at(VirtualAddress::new(map.address), map.size, map.flags)?.round();
let region = addr_space.grants.find_free_at(VirtualAddress::new(map.address), map.size, map.flags)?.round();
{
// Make sure it's *absolutely* not mapped already
// TODO: Keep track of all allocated memory so this isn't necessary
let active_table = unsafe { ActivePageTable::new(rmm::TableKind::User) };
for page in region.pages() {
if let Some(flags) = active_table.translate_page_flags(page).filter(|flags| flags.has_present()) {
println!("page at {:#x} was already mapped, flags: {:?}", page.start_address().data(), flags);
return Err(Error::new(EEXIST))
}
}
}
grants.insert(Grant::map(region.start_address(), region.size(), page_flags(map.flags)));
addr_space.grants.insert(Grant::zeroed(Page::containing_address(region.start_address()), map.size / PAGE_SIZE, page_flags(map.flags), &mut *unsafe { ActivePageTable::new(rmm::TableKind::User) }, PageFlushAll::new())?);
Ok(region.start_address().data())
}
+1 -1
View File
@@ -301,7 +301,7 @@ pub fn schemes_mut() -> RwLockWriteGuard<'static, SchemeList> {
}
pub trait KernelScheme: Scheme + Send + Sync + 'static {
#[allow(unused_arguments)]
#[allow(unused_variables)]
fn kfmap(&self, number: usize, map: &syscall::data::Map, target_context: &Arc<RwLock<Context>>) -> Result<usize> {
log::error!("Returning ENOSYS since kfmap can only be called on UserScheme schemes");
Err(Error::new(ENOSYS))
+102 -60
View File
@@ -1,6 +1,6 @@
use crate::{
arch::paging::{ActivePageTable, InactivePageTable, mapper::{Mapper, PageFlushAll}, Page, VirtualAddress},
context::{self, Context, ContextId, Status, memory::{Grant, page_flags, Region}},
arch::paging::{ActivePageTable, Flusher, InactivePageTable, mapper::{InactiveFlusher, Mapper, PageFlushAll}, Page, RmmA, VirtualAddress},
context::{self, Context, ContextId, Status, memory::{addrspace, Grant, new_addrspace, PtId, page_flags, Region}},
memory::PAGE_SIZE,
ptrace,
scheme::{AtomicSchemeId, SchemeId},
@@ -32,7 +32,7 @@ use core::{
str,
sync::atomic::{AtomicUsize, Ordering},
};
use spin::RwLock;
use spin::{Once, RwLock};
fn read_from(dst: &mut [u8], src: &[u8], offset: &mut usize) -> Result<usize> {
let byte_count = cmp::min(dst.len(), src.len().saturating_sub(*offset));
@@ -68,7 +68,7 @@ where
}
fn try_stop_context<F, T>(pid: ContextId, mut callback: F) -> Result<T>
where
F: FnMut(&mut Context) -> Result<T>,
F: FnOnce(&mut Context) -> Result<T>,
{
if pid == context::context_id() {
return Err(Error::new(EBADF));
@@ -118,6 +118,8 @@ enum Operation {
Sigstack,
Attr(Attr),
Files,
AddrSpace { id: PtId },
CurrentAddrSpace,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Attr {
@@ -216,7 +218,7 @@ impl Handle {
}
}
pub static PROC_SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default();
pub static PROC_SCHEME_ID: Once<SchemeId> = Once::new();
pub struct ProcScheme {
next_id: AtomicUsize,
@@ -231,7 +233,7 @@ pub enum Access {
impl ProcScheme {
pub fn new(scheme_id: SchemeId) -> Self {
PROC_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
PROC_SCHEME_ID.call_once(|| scheme_id);
Self {
next_id: AtomicUsize::new(0),
@@ -246,6 +248,11 @@ impl ProcScheme {
access: Access::Restricted,
}
}
fn new_handle(&self, handle: Handle) -> Result<usize> {
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
let _ = self.handles.write().insert(id, handle);
Ok(id)
}
}
impl Scheme for ProcScheme {
@@ -264,7 +271,8 @@ impl Scheme for ProcScheme {
let operation = match parts.next() {
Some("mem") => Operation::Memory,
Some("grants") => Operation::Grants,
Some("addrspace") => Operation::AddrSpace { id: context::contexts().current().ok_or(Error::new(ESRCH))?.read().addr_space()?.read().id },
Some("current-addrspace") => Operation::CurrentAddrSpace,
Some("regs/float") => Operation::Regs(RegsKind::Float),
Some("regs/int") => Operation::Regs(RegsKind::Int),
Some("regs/env") => Operation::Regs(RegsKind::Env),
@@ -340,9 +348,16 @@ impl Scheme for ProcScheme {
}
};
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
let id = self.new_handle(Handle {
info: Info {
flags,
pid,
operation,
},
data,
})?;
if let Operation::Trace { .. } = operation {
if let Operation::Trace = operation {
if !ptrace::try_new_session(pid, id) {
// There is no good way to handle id being occupied for nothing
// here, is there?
@@ -355,44 +370,41 @@ impl Scheme for ProcScheme {
}
}
self.handles.write().insert(id, Handle {
info: Info {
flags,
pid,
operation,
},
data,
});
Ok(id)
}
/// Using dup for `proc:` simply opens another operation on the same PID
/// ```rust,ignore
/// let trace = syscall::open("proc:1234/trace")?;
///
/// // let regs = syscall::open("proc:1234/regs/int")?;
/// let regs = syscall::dup(trace, "regs/int")?;
/// ```
/// Dup is currently used to implement clone() and execve().
fn dup(&self, old_id: usize, buf: &[u8]) -> Result<usize> {
let info = {
let handles = self.handles.read();
let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?;
handle.info
};
let buf_str = str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?;
self.new_handle(match info.operation {
Operation::AddrSpace { id } => {
let new_ptid = match buf {
// TODO: Better way to obtain new empty address spaces, perhaps using SYS_OPEN. But
// in that case, what scheme?
b"empty" => new_addrspace()?.0,
// Reuse same ID.
b"shared" => id,
b"exclusive" => addrspace(id).ok_or(Error::new(EBADFD))?.read().try_clone()?.0,
let mut path = format!("{}/", info.pid.into());
path.push_str(buf_str);
let (uid, gid) = {
let contexts = context::contexts();
let context = contexts.current().ok_or(Error::new(ESRCH))?;
let context = context.read();
(context.euid, context.egid)
};
self.open(&path, info.flags, uid, gid)
_ => return Err(Error::new(EINVAL)),
};
Handle {
info: Info {
flags: 0,
pid: info.pid,
operation: Operation::AddrSpace { id: new_ptid },
},
data: OperationData::Other,
}
}
_ => return Err(Error::new(EINVAL)),
})
}
fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> {
@@ -421,6 +433,7 @@ impl Scheme for ProcScheme {
};
match info.operation {
Operation::Grants => return Err(Error::new(ENOSYS)),
Operation::Static(_) => {
let mut handles = self.handles.write();
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
@@ -455,8 +468,7 @@ impl Scheme for ProcScheme {
data.offset = VirtualAddress::new(data.offset.data() + bytes_read);
Ok(bytes_read)
},
// TODO: Allow reading process mappings?
Operation::Grants => return Err(Error::new(EBADF)),
Operation::AddrSpace { .. } => return Err(Error::new(EBADF)),
Operation::Regs(kind) => {
union Output {
@@ -586,6 +598,14 @@ impl Scheme for ProcScheme {
read_from(buf, &data.buf, &mut data.offset)
}
// TODO: Replace write() with SYS_DUP_FORWARD.
// TODO: Find a better way to switch address spaces, since they also require switching
// the instruction and stack pointer. Maybe remove `<pid>/regs` altogether and replace it
// with `<pid>/ctx`
Operation::CurrentAddrSpace => {
//read_from(buf, &usize::to_ne_bytes(id.into()), &mut 0)
Ok(0)
}
}
}
@@ -606,6 +626,7 @@ impl Scheme for ProcScheme {
};
match info.operation {
Operation::Grants => Err(Error::new(ENOSYS)),
Operation::Static(_) => Err(Error::new(EBADF)),
Operation::Memory => {
// Won't context switch, don't worry about the locks
@@ -631,7 +652,7 @@ impl Scheme for ProcScheme {
data.offset = VirtualAddress::new(data.offset.data() + bytes_written);
Ok(bytes_written)
},
Operation::Grants => {
Operation::AddrSpace { .. } => {
// FIXME: Forbid upgrading external mappings.
let pid = self.handles.read()
@@ -649,51 +670,52 @@ impl Scheme for ProcScheme {
return Err(Error::new(EINVAL));
}
let is_inactive = pid != context::context_id();
let is_active = pid == context::context_id();
let callback = |context: &mut Context| {
let mut inactive = is_inactive.then(|| unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) });
let (mut inactive, mut active);
let mut grants = context.grants.write();
let mut addr_space = context.addr_space()?.write();
let conflicting = grants.conflicts(region).map(|g| *g.region()).collect::<Vec<_>>();
let (mut mapper, mut flusher) = if is_active {
active = (unsafe { ActivePageTable::new(rmm::TableKind::User) }, PageFlushAll::new());
(active.0.mapper(), &mut active.1 as &mut dyn Flusher<RmmA>)
} else {
inactive = (unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) }, InactiveFlusher::new());
(inactive.0.mapper(), &mut inactive.1 as &mut dyn Flusher<RmmA>)
};
let conflicting = addr_space.grants.conflicts(region).map(|g| *g.region()).collect::<Vec<_>>();
for conflicting_region in conflicting {
let whole_grant = grants.take(&conflicting_region).ok_or(Error::new(EBADFD))?;
let whole_grant = addr_space.grants.take(&conflicting_region).ok_or(Error::new(EBADFD))?;
let (before_opt, current, after_opt) = whole_grant.extract(region.intersect(conflicting_region)).ok_or(Error::new(EBADFD))?;
if let Some(before) = before_opt {
grants.insert(before);
addr_space.grants.insert(before);
}
if let Some(after) = after_opt {
grants.insert(after);
addr_space.grants.insert(after);
}
let res = if let Some(ref mut inactive) = inactive {
current.unmap_inactive(inactive)
} else {
current.unmap()
};
let res = current.unmap(&mut mapper, &mut flusher);
if res.file_desc.is_some() {
drop(grants);
return Err(Error::new(EBUSY));
}
// TODO: Partial free if grant is mapped externally.
// TODO: Partial free if grant is mapped externally, or fail and force
// userspace to do it.
}
if flags.intersects(MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::PROT_WRITE) {
let base = VirtualAddress::new(base);
let base = Page::containing_address(VirtualAddress::new(base));
if let Some(ref mut inactive) = inactive {
grants.insert(Grant::zeroed_inactive(Page::containing_address(base), size / PAGE_SIZE, page_flags(flags), inactive).unwrap());
} else {
grants.insert(Grant::map(base, size, page_flags(flags)));
}
addr_space.grants.insert(Grant::zeroed(base, size / PAGE_SIZE, page_flags(flags), &mut mapper, flusher)?);
}
Ok(())
};
if is_inactive {
if is_active {
with_context_mut(pid, callback)?;
} else {
try_stop_context(pid, callback)?;
@@ -868,6 +890,24 @@ impl Scheme for ProcScheme {
Ok(buf.len())
}
Operation::Files => return Err(Error::new(EBADF)),
Operation::CurrentAddrSpace { .. } => {
let mut iter = buf.array_chunks::<{mem::size_of::<usize>()}>().copied().map(usize::from_ne_bytes);
let id = iter.next().ok_or(Error::new(EINVAL))?;
let sp = iter.next().ok_or(Error::new(EINVAL))?;
let ip = iter.next().ok_or(Error::new(EINVAL))?;
let space = addrspace(PtId::from(id)).ok_or(Error::new(EINVAL))?;
try_stop_context(info.pid, |context| unsafe {
let regs = &mut ptrace::regs_for_mut(context).ok_or(Error::new(ESRCH))?.iret;
regs.rip = ip;
regs.rsp = sp;
context.set_addr_space(space);
Ok(())
})?;
Ok(3 * mem::size_of::<usize>())
}
}
}
@@ -911,6 +951,8 @@ impl Scheme for ProcScheme {
Operation::Attr(Attr::Uid) => "uid",
Operation::Attr(Attr::Gid) => "gid",
Operation::Files => "files",
Operation::AddrSpace { .. } => "addrspace",
Operation::CurrentAddrSpace => "current-addrspace",
});
read_from(buf, &path.as_bytes(), &mut 0)
+13 -7
View File
@@ -28,11 +28,15 @@ pub fn resource() -> Result<Vec<u8>> {
let mut stat_string = String::new();
// TODO: All user programs must have some grant in order for executable memory to even
// exist, but is this a good indicator of whether it is user or kernel?
if context.grants.read().is_empty() {
stat_string.push('K');
stat_string.push(if let Ok(addr_space) = context.addr_space() {
if addr_space.read().grants.is_empty() {
'K'
} else {
'U'
}
} else {
stat_string.push('U');
}
'R'
});
match context.status {
context::Status::Runnable => {
stat_string.push('R');
@@ -79,9 +83,11 @@ pub fn resource() -> Result<Vec<u8>> {
if let Some(ref kstack) = context.kstack {
memory += kstack.len();
}
for grant in context.grants.read().iter() {
if grant.is_owned() {
memory += grant.size();
if let Ok(addr_space) = context.addr_space() {
for grant in addr_space.read().grants.iter() {
if grant.is_owned() {
memory += grant.size();
}
}
}
+11 -12
View File
@@ -13,7 +13,7 @@ use crate::event;
use crate::paging::{PAGE_SIZE, InactivePageTable, VirtualAddress};
use crate::scheme::{AtomicSchemeId, SchemeId};
use crate::sync::{WaitQueue, WaitMap};
use crate::syscall::data::{Map, OldMap, Packet, Stat, StatVfs, TimeSpec};
use crate::syscall::data::{Map, Packet, Stat, StatVfs, TimeSpec};
use crate::syscall::error::*;
use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, MapFlags, PROT_READ, PROT_WRITE};
use crate::syscall::number::*;
@@ -145,15 +145,15 @@ impl UserInner {
let mut new_table = unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) };
let mut grants = context.grants.write();
let mut addr_space = context.addr_space()?.write();
let src_address = round_down_pages(address);
let offset = address - src_address;
let src_region = Region::new(VirtualAddress::new(src_address), offset + size).round();
let dst_region = grants.find_free_at(VirtualAddress::new(dst_address), src_region.size(), flags)?;
let dst_region = addr_space.grants.find_free_at(VirtualAddress::new(dst_address), src_region.size(), flags)?;
//TODO: Use syscall_head and syscall_tail to avoid leaking data
grants.insert(Grant::map_inactive(
addr_space.grants.insert(Grant::map_inactive(
src_region.start_address(),
dst_region.start_address(),
src_region.size(),
@@ -166,7 +166,6 @@ impl UserInner {
}
pub fn release(&self, address: usize) -> Result<()> {
//dbg!(address);
if address == DANGLING {
return Ok(());
}
@@ -174,13 +173,13 @@ impl UserInner {
let mut context = context_lock.write();
let mut other_table = unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) };
let mut grants = context.grants.write();
let mut addr_space = context.addr_space()?.write();
let region = match grants.contains(VirtualAddress::new(address)).map(Region::from) {
let region = match addr_space.grants.contains(VirtualAddress::new(address)).map(Region::from) {
Some(region) => region,
None => return Err(Error::new(EFAULT)),
};
grants.take(&region).unwrap().unmap_inactive(&mut other_table);
addr_space.grants.take(&region).unwrap().unmap(&mut other_table.mapper(), crate::paging::mapper::InactiveFlusher::new());
Ok(())
}
@@ -242,8 +241,8 @@ impl UserInner {
if let Ok(grant_address) = res {
if let Some(context_lock) = context_weak.upgrade() {
let context = context_lock.read();
let mut grants = context.grants.write();
grants.funmap.insert(
let mut addr_space = context.addr_space()?.write();
addr_space.grants.funmap.insert(
Region::new(grant_address, map.size),
VirtualAddress::new(address)
);
@@ -437,8 +436,8 @@ impl Scheme for UserScheme {
let contexts = context::contexts();
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
let context = context_lock.read();
let mut grants = context.grants.write();
let funmap = &mut grants.funmap;
let mut addr_space = context.addr_space()?.write();
let funmap = &mut addr_space.grants.funmap;
let entry = funmap.range(..=Region::byte(VirtualAddress::new(grant_address))).next_back();
let grant_address = VirtualAddress::new(grant_address);